summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
authorHanhui <sylixos@gmail.com>2015-05-26 05:54:27 (GMT)
committer Hanhui <sylixos@gmail.com>2015-05-26 05:54:27 (GMT)
commitad5ea8afbdf96f971945d4d7f1499d4134f340ad (patch)
tree5b8ec8388e7ef7b7d7724637430efa7a352624ed
downloadMuJS-ad5ea8afbdf96f971945d4d7f1499d4134f340ad.zip
MuJS-ad5ea8afbdf96f971945d4d7f1499d4134f340ad.tar.gz
MuJS-ad5ea8afbdf96f971945d4d7f1499d4134f340ad.tar.bz2
MuJS first push.
-rw-r--r--.cproject59
-rw-r--r--.project32
-rw-r--r--.rcsetting16
-rw-r--r--Makefile242
-rw-r--r--config.mk34
-rw-r--r--mujs/.gitattributes7
-rw-r--r--mujs/.gitignore4
-rw-r--r--mujs/AUTHORS1
-rw-r--r--mujs/COPYING661
-rw-r--r--mujs/Makefile80
-rw-r--r--mujs/README55
-rw-r--r--mujs/astnames.h92
-rw-r--r--mujs/jsarray.c718
-rw-r--r--mujs/jsboolean.c40
-rw-r--r--mujs/jsbuiltin.c229
-rw-r--r--mujs/jsbuiltin.h50
-rw-r--r--mujs/jscompile.c1319
-rw-r--r--mujs/jscompile.h155
-rw-r--r--mujs/jsdate.c804
-rw-r--r--mujs/jsdtoa.c849
-rw-r--r--mujs/jsdump.c852
-rw-r--r--mujs/jserror.c121
-rw-r--r--mujs/jsfunction.c211
-rw-r--r--mujs/jsgc.c239
-rw-r--r--mujs/jsi.h213
-rw-r--r--mujs/jsintern.c106
-rw-r--r--mujs/jslex.c770
-rw-r--r--mujs/jslex.h81
-rw-r--r--mujs/jsmath.c166
-rw-r--r--mujs/jsnumber.c101
-rw-r--r--mujs/jsobject.c446
-rw-r--r--mujs/json.c302
-rw-r--r--mujs/jsparse.c959
-rw-r--r--mujs/jsparse.h146
-rw-r--r--mujs/jsproperty.c339
-rw-r--r--mujs/jsregexp.c194
-rw-r--r--mujs/jsrun.c1645
-rw-r--r--mujs/jsrun.h15
-rw-r--r--mujs/jsstate.c221
-rw-r--r--mujs/jsstring.c687
-rw-r--r--mujs/jsvalue.c586
-rw-r--r--mujs/jsvalue.h184
-rw-r--r--mujs/main.c173
-rw-r--r--mujs/mujs.h179
-rw-r--r--mujs/one.c26
-rw-r--r--mujs/opnames.h91
-rw-r--r--mujs/regex.c1143
-rw-r--r--mujs/regex.h35
-rw-r--r--mujs/utf.c164
-rw-r--r--mujs/utf.h42
-rw-r--r--mujs/utftype.c1138
51 files changed, 17022 insertions, 0 deletions
diff --git a/.cproject b/.cproject
new file mode 100644
index 0000000..30ecaa9
--- /dev/null
+++ b/.cproject
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+ <storageModule moduleId="org.eclipse.cdt.core.settings">
+ <cconfiguration id="com.sylixos.realcoder.toolChain.armsylixeabi.779999512">
+ <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.sylixos.realcoder.toolChain.armsylixeabi.779999512" moduleId="org.eclipse.cdt.core.settings" name="Default">
+ <macros>
+ <stringMacro name="SYLIXOS_TOOLCHAIN_VER" type="VALUE_TEXT" value="4.9.3"/>
+ <stringMacro name="SYLIXOS_TOOLCHAIN_PATH" type="VALUE_PATH_DIR" value="F:\ZTHSOFT\arm-sylixos-toolchain"/>
+ <stringMacro name="SYLIXOS_TOOLCHAIN_PREFIX" type="VALUE_TEXT" value="arm-sylixos-eabi"/>
+ <stringMacro name="SYLIXOS_BASE_PATH" type="VALUE_PATH_DIR" value="E:/SylixOS/gcc/base"/>
+ </macros>
+ <externalSettings/>
+ <extensions>
+ <extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+ <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.VCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ </extensions>
+ </storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <configuration artifactName="${ProjName}" buildProperties="" description="" id="com.sylixos.realcoder.toolChain.armsylixeabi.779999512" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
+ <folderInfo id="com.sylixos.realcoder.toolChain.armsylixeabi.779999512.642479514" name="/" resourcePath="">
+ <toolChain id="com.sylixos.realcoder.toolChain.armsylixeabi.829731389" name="arm-sylixos-toolchain" superClass="com.sylixos.realcoder.toolChain.armsylixeabi">
+ <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.GNU_ELF" id="com.sylixos.realcoder.targetPlatform.armsylixoseabi.409142224" isAbstract="false" name="SylixOS TagetPlagform" osList="win32" superClass="com.sylixos.realcoder.targetPlatform.armsylixoseabi"/>
+ <builder id="com.sylixos.realcoder.builder.armsylixeabi.768503082" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="SylixOS Builder" superClass="com.sylixos.realcoder.builder.armsylixeabi"/>
+ <tool id="com.sylixos.realcoder.tool.armsylixeabi.518962144" name="arm-sylixos-eabi-gcc" superClass="com.sylixos.realcoder.tool.armsylixeabi">
+ <option id="com.sylixos.realcoder.option.armsylixoseabi.include.1175388743" name="Include paths (-I)" superClass="com.sylixos.realcoder.option.armsylixoseabi.include" valueType="includePath">
+ <listOptionValue builtIn="false" value="&quot;${SYLIXOS_BASE_PATH}/libsylixos/SylixOS&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SYLIXOS_BASE_PATH}/libsylixos/SylixOS/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SYLIXOS_BASE_PATH}/libsylixos/SylixOS/include/inet&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SYLIXOS_TOOLCHAIN_PATH}/${SYLIXOS_TOOLCHAIN_PREFIX}/include&quot;"/>
+ <listOptionValue builtIn="false" value="&quot;${SYLIXOS_TOOLCHAIN_PATH}/lib/gcc/${SYLIXOS_TOOLCHAIN_PREFIX}/${SYLIXOS_TOOLCHAIN_VER}/include&quot;"/>
+ </option>
+ <option id="com.sylixos.realcoder.option.armsylixoseabi.DefinedSymbols.119053663" name="Defined symbols(-D)" superClass="com.sylixos.realcoder.option.armsylixoseabi.DefinedSymbols" valueType="definedSymbols">
+ <listOptionValue builtIn="false" value="__GNUC__=4"/>
+ <listOptionValue builtIn="false" value="SYLIXOS"/>
+ </option>
+ <inputType id="com.sylixos.realcoder.inputType.armnsylixeabic.438935783" name="SylixOS Input C" superClass="com.sylixos.realcoder.inputType.armnsylixeabic"/>
+ <inputType id="com.sylixos.realcoder.inputType.armnsylixeabicpp.1615912638" name="SylixOS Input CPP" superClass="com.sylixos.realcoder.inputType.armnsylixeabicpp"/>
+ </tool>
+ </toolChain>
+ </folderInfo>
+ </configuration>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+ </cconfiguration>
+ </storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <project id="MuJS.null.428772566" name="MuJS"/>
+ </storageModule>
+ <storageModule moduleId="scannerConfiguration">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+</cproject>
diff --git a/.project b/.project
new file mode 100644
index 0000000..b885903
--- /dev/null
+++ b/.project
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>MuJS</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+ <triggers>clean,full,incremental,</triggers>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+ <triggers>full,incremental,</triggers>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.sylixos.realcoder.SylixOSBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.cdt.core.cnature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+ <nature>com.sylixos.realcoder.SylixOSProject</nature>
+ </natures>
+</projectDescription>
diff --git a/.rcsetting b/.rcsetting
new file mode 100644
index 0000000..a2c655d
--- /dev/null
+++ b/.rcsetting
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="GB2312" standalone="no"?>
+<SylixOSSetting>
+<UploadSetting HostName="192.168.7.30" Password="root" UserName="root">
+<PathPair LocalPath="E:\SylixOS\gcc\mid\MuJS\Debug\libmujs.so" RemotePath="/lib/libmujs.so"/>
+<PathPair LocalPath="E:\SylixOS\gcc\mid\MuJS\Debug\mujs" RemotePath="/bin/mujs"/>
+</UploadSetting>
+<BaseSetting ProjectType="DynamicLibProject">
+<BaseCfgPair Macro="SylixOS_Base_Path" Value="E:/SylixOS/gcc/base"/>
+<BaseCfgPair Macro="SylixOS_Toolchain" Value="arm-sylixos-eabi"/>
+<BaseCfgPair Macro="SylixOS_Toolchain_Path" Value="F:\ZTHSOFT\arm-sylixos-toolchain"/>
+<BaseCfgPair Macro="SylixOS_Toolchain_Ver" Value="4.9.3"/>
+<BaseCfgPair Macro="SylixOS_Debug_Level" Value="debug"/>
+<BaseCfgPair Macro="Create_Date" Value="2015 年05 月26 日"/>
+</BaseSetting>
+<BuildConfigration/>
+</SylixOSSetting>
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4818737
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,242 @@
+#*********************************************************************************************************
+# MuJS Makefile
+# target -> libmujs.a
+# libmujs.so
+#*********************************************************************************************************
+
+#*********************************************************************************************************
+# include config.mk
+#*********************************************************************************************************
+CONFIG_MK_EXIST = $(shell if [ -f ../config.mk ]; then echo exist; else echo notexist; fi;)
+ifeq ($(CONFIG_MK_EXIST), exist)
+include ../config.mk
+else
+CONFIG_MK_EXIST = $(shell if [ -f config.mk ]; then echo exist; else echo notexist; fi;)
+ifeq ($(CONFIG_MK_EXIST), exist)
+include config.mk
+else
+CONFIG_MK_EXIST =
+endif
+endif
+
+#*********************************************************************************************************
+# check configure
+#*********************************************************************************************************
+check_defined = \
+ $(foreach 1,$1,$(__check_defined))
+__check_defined = \
+ $(if $(value $1),, \
+ $(error Undefined $1$(if $(value 2), ($(strip $2)))))
+
+$(call check_defined, CONFIG_MK_EXIST, Please configure this project in RealCoder or \
+create a config.mk file!)
+$(call check_defined, SYLIXOS_BASE_PATH, SylixOS base project path)
+$(call check_defined, TOOLCHAIN_PREFIX, the prefix name of toolchain)
+$(call check_defined, DEBUG_LEVEL, debug level(debug or release))
+
+#*********************************************************************************************************
+# configure area you can set the following config to you own system
+# FPUFLAGS (-mfloat-abi=softfp -mfpu=vfpv3 ...)
+# CPUFLAGS (-mcpu=arm920t ...)
+# NOTICE: libsylixos, BSP and other kernel modules projects CAN NOT use vfp!
+#*********************************************************************************************************
+FPUFLAGS =
+CPUFLAGS = -mcpu=arm920t $(FPUFLAGS)
+
+#*********************************************************************************************************
+# toolchain select
+#*********************************************************************************************************
+CC = $(TOOLCHAIN_PREFIX)gcc
+CXX = $(TOOLCHAIN_PREFIX)g++
+AS = $(TOOLCHAIN_PREFIX)gcc
+AR = $(TOOLCHAIN_PREFIX)ar
+LD = $(TOOLCHAIN_PREFIX)g++
+
+#*********************************************************************************************************
+# do not change the following code
+# buildin internal application source
+#*********************************************************************************************************
+#*********************************************************************************************************
+# src(s) file
+#*********************************************************************************************************
+LIB_SRCS = \
+mujs/one.c
+
+EXE_SRCS = \
+mujs/main.c
+
+#*********************************************************************************************************
+# build path
+#*********************************************************************************************************
+ifeq ($(DEBUG_LEVEL), debug)
+OUTDIR = Debug
+else
+OUTDIR = Release
+endif
+
+OUTPATH = ./$(OUTDIR)
+OBJPATH = $(OUTPATH)/obj
+DEPPATH = $(OUTPATH)/dep
+
+#*********************************************************************************************************
+# target
+#*********************************************************************************************************
+LIB = $(OUTPATH)/libmujs.a
+DLL = $(OUTPATH)/libmujs.so
+EXE = $(OUTPATH)/mujs
+
+#*********************************************************************************************************
+# objects
+#*********************************************************************************************************
+LIB_OBJS = $(addprefix $(OBJPATH)/, $(addsuffix .o, $(basename $(LIB_SRCS))))
+LIB_DEPS = $(addprefix $(DEPPATH)/, $(addsuffix .d, $(basename $(LIB_SRCS))))
+
+EXE_OBJS = $(addprefix $(OBJPATH)/, $(addsuffix .o, $(basename $(EXE_SRCS))))
+EXE_DEPS = $(addprefix $(DEPPATH)/, $(addsuffix .d, $(basename $(EXE_SRCS))))
+
+#*********************************************************************************************************
+# include path
+#*********************************************************************************************************
+INCDIR = -I"$(SYLIXOS_BASE_PATH)/libsylixos/SylixOS"
+INCDIR += -I"$(SYLIXOS_BASE_PATH)/libsylixos/SylixOS/include"
+INCDIR += -I"$(SYLIXOS_BASE_PATH)/libsylixos/SylixOS/include/inet"
+
+#*********************************************************************************************************
+# compiler preprocess
+#*********************************************************************************************************
+DSYMBOL = -DSYLIXOS
+DSYMBOL += -DSYLIXOS_LIB
+
+#*********************************************************************************************************
+# depend dynamic library
+#*********************************************************************************************************
+DEPEND_DLL =
+
+#*********************************************************************************************************
+# depend dynamic library search path
+#*********************************************************************************************************
+DEPEND_DLL_PATH =
+
+#*********************************************************************************************************
+# compiler optimize
+#*********************************************************************************************************
+ifeq ($(DEBUG_LEVEL), debug)
+OPTIMIZE = -O0 -g3 -gdwarf-2
+else
+OPTIMIZE = -O2 -g1 -gdwarf-2 # Do NOT use -O3 and -Os
+endif # -Os is not align for function
+ # loop and jump.
+#*********************************************************************************************************
+# depends and compiler parameter (cplusplus in kernel MUST NOT use exceptions and rtti)
+#*********************************************************************************************************
+DEPENDFLAG = -MM
+CXX_EXCEPT = -fno-exceptions -fno-rtti
+COMMONFLAGS = $(CPUFLAGS) $(OPTIMIZE) -Wall -fmessage-length=0 -fsigned-char -fno-short-enums
+ASFLAGS = -x assembler-with-cpp $(DSYMBOL) $(INCDIR) $(COMMONFLAGS) -c
+CFLAGS = $(DSYMBOL) $(INCDIR) $(COMMONFLAGS) -fPIC -c
+CXXFLAGS = $(DSYMBOL) $(INCDIR) $(CXX_EXCEPT) $(COMMONFLAGS) -fPIC -c
+ARFLAGS = -r
+
+#*********************************************************************************************************
+# define some useful variable
+#*********************************************************************************************************
+DEPEND = $(CC) $(DEPENDFLAG) $(CFLAGS)
+DEPEND.d = $(subst -g ,,$(DEPEND))
+COMPILE.S = $(AS) $(ASFLAGS)
+COMPILE_VFP.S = $(AS) $(ASFLAGS)
+COMPILE.c = $(CC) $(CFLAGS)
+COMPILE.cxx = $(CXX) $(CXXFLAGS)
+
+#*********************************************************************************************************
+# target
+#*********************************************************************************************************
+all: $(LIB) $(DLL) $(EXE)
+ @echo create "$(LIB) $(DLL) $(EXE)" success.
+
+#*********************************************************************************************************
+# include depends
+#*********************************************************************************************************
+ifneq ($(MAKECMDGOALS), clean)
+ifneq ($(MAKECMDGOALS), clean_project)
+sinclude $(LIB_DEPS) $(EXE_DEPS)
+endif
+endif
+
+#*********************************************************************************************************
+# create depends files
+#*********************************************************************************************************
+$(DEPPATH)/%.d: %.c
+ @echo creating $@
+ @if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi
+ @rm -f $@; \
+ echo -n '$@ $(addprefix $(OBJPATH)/, $(dir $<))' > $@; \
+ $(DEPEND.d) $< >> $@ || rm -f $@; exit;
+
+$(DEPPATH)/%.d: %.cpp
+ @echo creating $@
+ @if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi
+ @rm -f $@; \
+ echo -n '$@ $(addprefix $(OBJPATH)/, $(dir $<))' > $@; \
+ $(DEPEND.d) $< >> $@ || rm -f $@; exit;
+
+#*********************************************************************************************************
+# compile source files
+#*********************************************************************************************************
+$(OBJPATH)/%.o: %.S
+ @if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi
+ $(COMPILE.S) $< -o $@
+
+$(OBJPATH)/%.o: %.c
+ @if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi
+ $(COMPILE.c) $< -o $@
+
+$(OBJPATH)/%.o: %.cpp
+ @if [ ! -d "$(dir $@)" ]; then mkdir -p "$(dir $@)"; fi
+ $(COMPILE.cxx) $< -o $@
+
+#*********************************************************************************************************
+# link libmujs.a object files
+#*********************************************************************************************************
+$(LIB): $(LIB_OBJS)
+ $(AR) $(ARFLAGS) $(LIB) $(LIB_OBJS)
+
+#*********************************************************************************************************
+# link libmujs.so object files
+#*********************************************************************************************************
+$(DLL): $(LIB_OBJS)
+ $(LD) $(CPUFLAGS) -nostdlib -fPIC -shared -o $(DLL) $(LIB_OBJS) \
+ $(DEPEND_DLL_PATH) $(DEPEND_DLL) -lm -lgcc
+
+#*********************************************************************************************************
+# link mujs object files
+#*********************************************************************************************************
+$(EXE): $(EXE_OBJS)
+ $(LD) $(CPUFLAGS) -nostdlib -fPIC -shared -o $(EXE) $(EXE_OBJS) \
+ -L"./Debug" -L"./Release" -L"$(SYLIXOS_BASE_PATH)/libsylixos/$(OUTDIR)" \
+ -lmujs -lvpmpdm -lm -lgcc
+
+#*********************************************************************************************************
+# clean
+#*********************************************************************************************************
+.PHONY: clean
+.PHONY: clean_project
+
+#*********************************************************************************************************
+# clean objects
+#*********************************************************************************************************
+clean:
+ -rm -rf $(LIB)
+ -rm -rf $(DLL)
+ -rm -rf $(OBJS)
+ -rm -rf $(OBJPATH)
+ -rm -rf $(DEPPATH)
+
+#*********************************************************************************************************
+# clean project
+#*********************************************************************************************************
+clean_project:
+ -rm -rf $(OUTPATH)
+
+#*********************************************************************************************************
+# END
+#*********************************************************************************************************
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..c75472e
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,34 @@
+#*********************************************************************************************************
+#
+# 中国软件开源组织
+#
+# 嵌入式实时操作系统
+#
+# SylixOS(TM) LW : long wing
+#
+# Copyright All Rights Reserved
+#
+#--------------文件信息--------------------------------------------------------------------------------
+#
+# 文 件 名: config.mk
+#
+# 创 建 人: RealCoder
+#
+# 文件创建日期: 2015 年05 月26 日
+#
+# 描 述: 本文件由RealCoder生成,用于配置Makefile功能,请勿手动修改
+#*********************************************************************************************************
+#*********************************************************************************************************
+# SylixOS Base Project path
+#*********************************************************************************************************
+SYLIXOS_BASE_PATH = E:/SylixOS/gcc/base
+
+#*********************************************************************************************************
+# Toolchain prefix
+#*********************************************************************************************************
+TOOLCHAIN_PREFIX = arm-sylixos-eabi-
+
+#*********************************************************************************************************
+# Debug options (debug or release)
+#*********************************************************************************************************
+DEBUG_LEVEL = debug \ No newline at end of file
diff --git a/mujs/.gitattributes b/mujs/.gitattributes
new file mode 100644
index 0000000..46fe0b3
--- /dev/null
+++ b/mujs/.gitattributes
@@ -0,0 +1,7 @@
+# Define macro for whitespace settings:
+[attr]tabs whitespace=trailing-space,space-before-tab,indent-with-non-tab
+
+* text=auto
+Makefile tabs
+*.[ch] tabs
+*.js tabs
diff --git a/mujs/.gitignore b/mujs/.gitignore
new file mode 100644
index 0000000..fa636cb
--- /dev/null
+++ b/mujs/.gitignore
@@ -0,0 +1,4 @@
+build
+tags
+tests
+specs
diff --git a/mujs/AUTHORS b/mujs/AUTHORS
new file mode 100644
index 0000000..593c93f
--- /dev/null
+++ b/mujs/AUTHORS
@@ -0,0 +1 @@
+Tor Andersson <tor.andersson@artifex.com>
diff --git a/mujs/COPYING b/mujs/COPYING
new file mode 100644
index 0000000..dba13ed
--- /dev/null
+++ b/mujs/COPYING
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/mujs/Makefile b/mujs/Makefile
new file mode 100644
index 0000000..7802bd4
--- /dev/null
+++ b/mujs/Makefile
@@ -0,0 +1,80 @@
+SRCS := $(wildcard js*.c utf*.c regex.c)
+HDRS := $(wildcard js*.h mujs.h utf.h regex.h)
+OBJS := $(SRCS:%.c=build/%.o)
+
+prefix ?= /usr/local
+bindir ?= $(prefix)/bin
+incdir ?= $(prefix)/include
+libdir ?= $(prefix)/lib
+
+CFLAGS := -std=c99 -pedantic -Wall -Wextra -Wno-unused-parameter
+
+ifeq "$(CC)" "clang"
+CFLAGS += -Wunreachable-code
+endif
+
+ifeq "$(build)" "debug"
+CFLAGS += -g
+else
+CFLAGS += -O2
+endif
+
+default: build build/mujs build/mujsone
+
+debug:
+ $(MAKE) build=debug
+
+release:
+ $(MAKE) build=release
+
+astnames.h: jsparse.h
+ grep -E '(AST|EXP|STM)_' jsparse.h | sed 's/^[^A-Z]*\(AST_\)*/"/;s/,.*/",/' | tr A-Z a-z > $@
+
+opnames.h: jscompile.h
+ grep -E 'OP_' jscompile.h | sed 's/^[^A-Z]*OP_/"/;s/,.*/",/' | tr A-Z a-z > $@
+
+one.c: $(SRCS)
+ ls $(SRCS) | awk '{print "#include \""$$1"\""}' > $@
+
+jsdump.c: astnames.h opnames.h
+
+build:
+ mkdir -p build
+
+build/%.o: %.c $(HDRS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+build/libmujs.a: $(OBJS)
+ $(AR) cru $@ $^
+
+build/mujs: build/main.o build/libmujs.a
+ $(CC) $(LDFLAGS) -o $@ $^ -lm
+
+build/mujsone: build/main.o build/one.o
+ $(CC) $(LDFLAGS) -o $@ $^ -lm
+
+install: release
+ install -d $(DESTDIR)$(incdir)
+ install -d $(DESTDIR)$(libdir)
+ install -d $(DESTDIR)$(bindir)
+ install mujs.h $(DESTDIR)$(incdir)
+ install build/libmujs.a $(DESTDIR)$(libdir)
+ install build/mujs $(DESTDIR)$(bindir)
+
+VERSION = $(shell git describe --tags --always)
+
+tarball:
+ git archive --format=zip --prefix=mujs-$(VERSION)/ HEAD > mujs-$(VERSION).zip
+ git archive --format=tar --prefix=mujs-$(VERSION)/ HEAD | gzip > mujs-$(VERSION).tar.gz
+ git archive --format=tar --prefix=mujs-$(VERSION)/ HEAD | xz > mujs-$(VERSION).tar.xz
+
+tags: $(SRCS) main.c $(HDRS)
+ ctags $^
+
+test: build/mujs
+ python tests/sputniktests/tools/sputnik.py --tests=tests/sputniktests --command ./build/mujs --summary
+
+clean:
+ rm -f astnames.h opnames.h one.c build/*
+
+.PHONY: default test clean install debug release
diff --git a/mujs/README b/mujs/README
new file mode 100644
index 0000000..48f3f2f
--- /dev/null
+++ b/mujs/README
@@ -0,0 +1,55 @@
+MuJS: an embeddable Javascript interpreter in C.
+
+ABOUT
+
+MuJS is a lightweight Javascript interpreter designed for embedding in
+other software to extend them with scripting capabilities.
+
+LICENSE
+
+MuJS is Copyright 2014 Artifex Software, Inc.
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU Affero General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License along
+with this program. If not, see <http://www.gnu.org/licenses/>.
+
+For commercial licensing please contact sales@artifex.com.
+
+COMPILING
+
+If you are building from source you can either use the provided Unix Makefile:
+
+ make release
+
+Or compile the source with your preferred compiler:
+
+ cc -O3 -c one.c -o libmujs.o
+
+INSTALLING
+
+To install the MuJS command line interpreter, static library and header file:
+
+ make prefix=/usr/local install
+
+DOWNLOAD
+
+The latest development source is available directly from the git repository:
+
+ git clone http://git.ghostscript.com/mujs.git
+
+REPORTING BUGS AND PROBLEMS
+
+Report bugs on the ghostscript bugzilla, with MuJS as the selected component.
+
+ http://bugs.ghostscript.com/
+
+The MuJS developers hang out on IRC in the #ghostscript channel on
+irc.freenode.net.
diff --git a/mujs/astnames.h b/mujs/astnames.h
new file mode 100644
index 0000000..ab7620f
--- /dev/null
+++ b/mujs/astnames.h
@@ -0,0 +1,92 @@
+"list",
+"fundec",
+"identifier",
+"exp_identifier",
+"exp_number",
+"exp_string",
+"exp_regexp",
+"exp_undef",
+"exp_null",
+"exp_true",
+"exp_false",
+"exp_this",
+"exp_array",
+"exp_object",
+"exp_prop_val",
+"exp_prop_get",
+"exp_prop_set",
+"exp_fun",
+"exp_index",
+"exp_member",
+"exp_call",
+"exp_new",
+"exp_postinc",
+"exp_postdec",
+"exp_delete",
+"exp_void",
+"exp_typeof",
+"exp_preinc",
+"exp_predec",
+"exp_pos",
+"exp_neg",
+"exp_bitnot",
+"exp_lognot",
+"exp_mod",
+"exp_div",
+"exp_mul",
+"exp_sub",
+"exp_add",
+"exp_ushr",
+"exp_shr",
+"exp_shl",
+"exp_in",
+"exp_instanceof",
+"exp_ge",
+"exp_le",
+"exp_gt",
+"exp_lt",
+"exp_strictne",
+"exp_stricteq",
+"exp_ne",
+"exp_eq",
+"exp_bitand",
+"exp_bitxor",
+"exp_bitor",
+"exp_logand",
+"exp_logor",
+"exp_cond",
+"exp_ass",
+"exp_ass_mul",
+"exp_ass_div",
+"exp_ass_mod",
+"exp_ass_add",
+"exp_ass_sub",
+"exp_ass_shl",
+"exp_ass_shr",
+"exp_ass_ushr",
+"exp_ass_bitand",
+"exp_ass_bitxor",
+"exp_ass_bitor",
+"exp_comma",
+"exp_var",
+"stm_block",
+"stm_empty",
+"stm_var",
+"stm_if",
+"stm_do",
+"stm_while",
+"stm_for",
+"stm_for_var",
+"stm_for_in",
+"stm_for_in_var",
+"stm_continue",
+"stm_break",
+"stm_return",
+"stm_with",
+"stm_switch",
+"stm_throw",
+"stm_try",
+"stm_debugger",
+"stm_label",
+"stm_case",
+"stm_default",
diff --git a/mujs/jsarray.c b/mujs/jsarray.c
new file mode 100644
index 0000000..3535f8e
--- /dev/null
+++ b/mujs/jsarray.c
@@ -0,0 +1,718 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+unsigned int js_getlength(js_State *J, int idx)
+{
+ unsigned int len;
+ js_getproperty(J, idx, "length");
+ len = js_touint32(J, -1);
+ js_pop(J, 1);
+ return len;
+}
+
+void js_setlength(js_State *J, int idx, unsigned int len)
+{
+ js_pushnumber(J, len);
+ js_setproperty(J, idx < 0 ? idx - 1: idx, "length");
+}
+
+int js_hasindex(js_State *J, int idx, unsigned int i)
+{
+ char buf[32];
+ return js_hasproperty(J, idx, js_itoa(buf, i));
+}
+
+void js_getindex(js_State *J, int idx, unsigned int i)
+{
+ char buf[32];
+ js_getproperty(J, idx, js_itoa(buf, i));
+}
+
+void js_setindex(js_State *J, int idx, unsigned int i)
+{
+ char buf[32];
+ js_setproperty(J, idx, js_itoa(buf, i));
+}
+
+void js_delindex(js_State *J, int idx, unsigned int i)
+{
+ char buf[32];
+ js_delproperty(J, idx, js_itoa(buf, i));
+}
+
+static void jsB_new_Array(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+
+ js_newarray(J);
+
+ if (top == 2) {
+ if (js_isnumber(J, 1)) {
+ js_copy(J, 1);
+ js_setproperty(J, -2, "length");
+ } else {
+ js_copy(J, 1);
+ js_setindex(J, -2, 0);
+ }
+ } else {
+ for (i = 1; i < top; ++i) {
+ js_copy(J, i);
+ js_setindex(J, -2, i - 1);
+ }
+ }
+}
+
+static void Ap_concat(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+ unsigned int n, k, len;
+
+ js_newarray(J);
+ n = 0;
+
+ for (i = 0; i < top; ++i) {
+ js_copy(J, i);
+ if (js_isarray(J, -1)) {
+ len = js_getlength(J, -1);
+ for (k = 0; k < len; ++k)
+ if (js_hasindex(J, -1, k))
+ js_setindex(J, -3, n++);
+ js_pop(J, 1);
+ } else {
+ js_setindex(J, -2, n++);
+ }
+ }
+}
+
+static void Ap_join(js_State *J)
+{
+ char * volatile out = NULL;
+ const char *sep;
+ const char *r;
+ unsigned int seplen;
+ unsigned int k, n, len;
+
+ len = js_getlength(J, 0);
+
+ if (js_isdefined(J, 1)) {
+ sep = js_tostring(J, 1);
+ seplen = strlen(sep);
+ } else {
+ sep = ",";
+ seplen = 1;
+ }
+
+ if (len == 0) {
+ js_pushliteral(J, "");
+ return;
+ }
+
+ if (js_try(J)) {
+ js_free(J, out);
+ js_throw(J);
+ }
+
+ n = 1;
+ for (k = 0; k < len; ++k) {
+ js_getindex(J, 0, k);
+ if (js_isundefined(J, -1) || js_isnull(J, -1))
+ r = "";
+ else
+ r = js_tostring(J, -1);
+ n += strlen(r);
+
+ if (k == 0) {
+ out = js_malloc(J, n);
+ strcpy(out, r);
+ } else {
+ n += seplen;
+ out = realloc(out, n);
+ strcat(out, sep);
+ strcat(out, r);
+ }
+
+ js_pop(J, 1);
+ }
+
+ js_pushstring(J, out);
+ js_endtry(J);
+ js_free(J, out);
+}
+
+static void Ap_pop(js_State *J)
+{
+ unsigned int n;
+
+ n = js_getlength(J, 0);
+
+ if (n > 0) {
+ js_getindex(J, 0, n - 1);
+ js_delindex(J, 0, n - 1);
+ js_setlength(J, 0, n - 1);
+ } else {
+ js_setlength(J, 0, 0);
+ js_pushundefined(J);
+ }
+}
+
+static void Ap_push(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+ unsigned int n;
+
+ n = js_getlength(J, 0);
+
+ for (i = 1; i < top; ++i, ++n) {
+ js_copy(J, i);
+ js_setindex(J, 0, n);
+ }
+
+ js_setlength(J, 0, n);
+
+ js_pushnumber(J, n);
+}
+
+static void Ap_reverse(js_State *J)
+{
+ unsigned int len, middle, lower;
+
+ len = js_getlength(J, 0);
+ middle = len / 2;
+ lower = 0;
+
+ while (lower != middle) {
+ unsigned int upper = len - lower - 1;
+ int haslower = js_hasindex(J, 0, lower);
+ int hasupper = js_hasindex(J, 0, upper);
+ if (haslower && hasupper) {
+ js_setindex(J, 0, lower);
+ js_setindex(J, 0, upper);
+ } else if (hasupper) {
+ js_setindex(J, 0, lower);
+ js_delindex(J, 0, upper);
+ } else if (haslower) {
+ js_setindex(J, 0, upper);
+ js_delindex(J, 0, lower);
+ }
+ ++lower;
+ }
+
+ js_copy(J, 0);
+}
+
+static void Ap_shift(js_State *J)
+{
+ unsigned int k, len;
+
+ len = js_getlength(J, 0);
+
+ if (len == 0) {
+ js_setlength(J, 0, 0);
+ js_pushundefined(J);
+ return;
+ }
+
+ js_getindex(J, 0, 0);
+
+ for (k = 1; k < len; ++k) {
+ if (js_hasindex(J, 0, k))
+ js_setindex(J, 0, k - 1);
+ else
+ js_delindex(J, 0, k - 1);
+ }
+
+ js_delindex(J, 0, len - 1);
+ js_setlength(J, 0, len - 1);
+}
+
+static void Ap_slice(js_State *J)
+{
+ unsigned int len, s, e, n;
+ double sv, ev;
+
+ js_newarray(J);
+
+ len = js_getlength(J, 0);
+ sv = js_tointeger(J, 1);
+ ev = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
+
+ if (sv < 0) sv = sv + len;
+ if (ev < 0) ev = ev + len;
+
+ s = sv < 0 ? 0 : sv > len ? len : sv;
+ e = ev < 0 ? 0 : ev > len ? len : ev;
+
+ for (n = 0; s < e; ++s, ++n)
+ if (js_hasindex(J, 0, s))
+ js_setindex(J, -2, n);
+}
+
+static int compare(js_State *J, unsigned int x, unsigned int y, int *hasx, int *hasy, int hasfn)
+{
+ const char *sx, *sy;
+ int c;
+
+ *hasx = js_hasindex(J, 0, x);
+ *hasy = js_hasindex(J, 0, y);
+
+ if (*hasx && *hasy) {
+ int unx = js_isundefined(J, -2);
+ int uny = js_isundefined(J, -1);
+ if (unx && uny) return 0;
+ if (unx) return 1;
+ if (uny) return -1;
+
+ if (hasfn) {
+ js_copy(J, 1); /* copy function */
+ js_pushundefinedthis(J); /* set this object */
+ js_copy(J, -4); /* copy x */
+ js_copy(J, -4); /* copy y */
+ js_call(J, 2);
+ c = js_tonumber(J, -1);
+ js_pop(J, 1);
+ return c;
+ }
+
+ sx = js_tostring(J, -2);
+ sy = js_tostring(J, -1);
+ return strcmp(sx, sy);
+ }
+
+ if (*hasx) return -1;
+ if (*hasy) return 1;
+ return 0;
+}
+
+static void Ap_sort(js_State *J)
+{
+ unsigned int len, i, k;
+ int hasx, hasy, hasfn;
+
+ len = js_getlength(J, 0);
+
+ hasfn = js_iscallable(J, 1);
+
+ for (i = 1; i < len; ++i) {
+ k = i;
+ while (k > 0 && compare(J, k - 1, k, &hasx, &hasy, hasfn) > 0) {
+ if (hasx && hasy) {
+ js_setindex(J, 0, k - 1);
+ js_setindex(J, 0, k);
+ } else if (hasx) {
+ js_delindex(J, 0, k - 1);
+ js_setindex(J, 0, k);
+ } else if (hasy) {
+ js_setindex(J, 0, k - 1);
+ js_delindex(J, 0, k);
+ }
+ --k;
+ }
+ }
+
+ js_copy(J, 0);
+}
+
+static void Ap_splice(js_State *J)
+{
+ unsigned int top = js_gettop(J);
+ unsigned int len, start, del, add, k;
+ double f;
+
+ js_newarray(J);
+
+ len = js_getlength(J, 0);
+
+ f = js_tointeger(J, 1);
+ if (f < 0) f = f + len;
+ start = f < 0 ? 0 : f > len ? len : f;
+
+ f = js_tointeger(J, 2);
+ del = f < 0 ? 0 : f > len - start ? len - start : f;
+
+ /* copy deleted items to return array */
+ for (k = 0; k < del; ++k)
+ if (js_hasindex(J, 0, start + k))
+ js_setindex(J, -2, k);
+ js_setlength(J, -1, del);
+
+ /* shift the tail to resize the hole left by deleted items */
+ add = top - 3;
+ if (add < del) {
+ for (k = start; k < len - del; ++k) {
+ if (js_hasindex(J, 0, k + del))
+ js_setindex(J, 0, k + add);
+ else
+ js_delindex(J, 0, k + add);
+ }
+ for (k = len; k > len - del + add; --k)
+ js_delindex(J, 0, k - 1);
+ } else if (add > del) {
+ for (k = len - del; k > start; --k) {
+ if (js_hasindex(J, 0, k + del - 1))
+ js_setindex(J, 0, k + add - 1);
+ else
+ js_delindex(J, 0, k + add - 1);
+ }
+ }
+
+ /* copy new items into the hole */
+ for (k = 0; k < add; ++k) {
+ js_copy(J, 3 + k);
+ js_setindex(J, 0, start + k);
+ }
+
+ js_setlength(J, 0, len - del + add);
+}
+
+static void Ap_unshift(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+ unsigned int k, len;
+
+ len = js_getlength(J, 0);
+
+ for (k = len; k > 0; --k) {
+ int from = k - 1;
+ int to = k + top - 2;
+ if (js_hasindex(J, 0, from))
+ js_setindex(J, 0, to);
+ else
+ js_delindex(J, 0, to);
+ }
+
+ for (i = 1; i < top; ++i) {
+ js_copy(J, i);
+ js_setindex(J, 0, i - 1);
+ }
+
+ js_setlength(J, 0, len + top - 1);
+
+ js_pushnumber(J, len + top - 1);
+}
+
+static void Ap_toString(js_State *J)
+{
+ unsigned int top = js_gettop(J);
+ js_pop(J, top - 1);
+ Ap_join(J);
+}
+
+static void Ap_indexOf(js_State *J)
+{
+ int k, len, from;
+
+ len = js_getlength(J, 0);
+ from = js_isdefined(J, 2) ? js_tointeger(J, 2) : 0;
+ if (from < 0) from = len + from;
+ if (from < 0) from = 0;
+
+ js_copy(J, 1);
+ for (k = from; k < len; ++k) {
+ if (js_hasindex(J, 0, k)) {
+ if (js_strictequal(J)) {
+ js_pushnumber(J, k);
+ return;
+ }
+ js_pop(J, 1);
+ }
+ }
+
+ js_pushnumber(J, -1);
+}
+
+static void Ap_lastIndexOf(js_State *J)
+{
+ int k, len, from;
+
+ len = js_getlength(J, 0);
+ from = js_isdefined(J, 2) ? js_tointeger(J, 2) : len - 1;
+ if (from > len - 1) from = len - 1;
+ if (from < 0) from = len + from;
+
+ js_copy(J, 1);
+ for (k = from; k >= 0; --k) {
+ if (js_hasindex(J, 0, k)) {
+ if (js_strictequal(J)) {
+ js_pushnumber(J, k);
+ return;
+ }
+ js_pop(J, 1);
+ }
+ }
+
+ js_pushnumber(J, -1);
+}
+
+static void Ap_every(js_State *J)
+{
+ int hasthis = js_gettop(J) >= 3;
+ int k, len;
+
+ if (!js_iscallable(J, 1))
+ js_typeerror(J, "callback is not a function");
+
+ len = js_getlength(J, 0);
+ for (k = 0; k < len; ++k) {
+ if (js_hasindex(J, 0, k)) {
+ js_copy(J, 1);
+ if (hasthis)
+ js_copy(J, 2);
+ else
+ js_pushundefined(J);
+ js_copy(J, -3);
+ js_pushnumber(J, k);
+ js_copy(J, 0);
+ js_call(J, 3);
+ if (!js_toboolean(J, -1))
+ return;
+ js_pop(J, 2);
+ }
+ }
+
+ js_pushboolean(J, 1);
+}
+
+static void Ap_some(js_State *J)
+{
+ int hasthis = js_gettop(J) >= 3;
+ int k, len;
+
+ if (!js_iscallable(J, 1))
+ js_typeerror(J, "callback is not a function");
+
+ len = js_getlength(J, 0);
+ for (k = 0; k < len; ++k) {
+ if (js_hasindex(J, 0, k)) {
+ js_copy(J, 1);
+ if (hasthis)
+ js_copy(J, 2);
+ else
+ js_pushundefined(J);
+ js_copy(J, -3);
+ js_pushnumber(J, k);
+ js_copy(J, 0);
+ js_call(J, 3);
+ if (js_toboolean(J, -1))
+ return;
+ js_pop(J, 2);
+ }
+ }
+
+ js_pushboolean(J, 0);
+}
+
+static void Ap_forEach(js_State *J)
+{
+ int hasthis = js_gettop(J) >= 3;
+ int k, len;
+
+ if (!js_iscallable(J, 1))
+ js_typeerror(J, "callback is not a function");
+
+ len = js_getlength(J, 0);
+ for (k = 0; k < len; ++k) {
+ if (js_hasindex(J, 0, k)) {
+ js_copy(J, 1);
+ if (hasthis)
+ js_copy(J, 2);
+ else
+ js_pushundefined(J);
+ js_copy(J, -3);
+ js_pushnumber(J, k);
+ js_copy(J, 0);
+ js_call(J, 3);
+ js_pop(J, 2);
+ }
+ }
+
+ js_pushundefined(J);
+}
+
+static void Ap_map(js_State *J)
+{
+ int hasthis = js_gettop(J) >= 3;
+ int k, len;
+
+ if (!js_iscallable(J, 1))
+ js_typeerror(J, "callback is not a function");
+
+ js_newarray(J);
+
+ len = js_getlength(J, 0);
+ for (k = 0; k < len; ++k) {
+ if (js_hasindex(J, 0, k)) {
+ js_copy(J, 1);
+ if (hasthis)
+ js_copy(J, 2);
+ else
+ js_pushundefined(J);
+ js_copy(J, -3);
+ js_pushnumber(J, k);
+ js_copy(J, 0);
+ js_call(J, 3);
+ js_setindex(J, -3, k);
+ js_pop(J, 1);
+ }
+ }
+}
+
+static void Ap_filter(js_State *J)
+{
+ int hasthis = js_gettop(J) >= 3;
+ int k, to, len;
+
+ if (!js_iscallable(J, 1))
+ js_typeerror(J, "callback is not a function");
+
+ js_newarray(J);
+ to = 0;
+
+ len = js_getlength(J, 0);
+ for (k = 0; k < len; ++k) {
+ if (js_hasindex(J, 0, k)) {
+ js_copy(J, 1);
+ if (hasthis)
+ js_copy(J, 2);
+ else
+ js_pushundefined(J);
+ js_copy(J, -3);
+ js_pushnumber(J, k);
+ js_copy(J, 0);
+ js_call(J, 3);
+ if (js_toboolean(J, -1)) {
+ js_pop(J, 1);
+ js_setindex(J, -2, to++);
+ } else {
+ js_pop(J, 2);
+ }
+ }
+ }
+}
+
+static void Ap_reduce(js_State *J)
+{
+ int hasinitial = js_gettop(J) >= 3;
+ int k, len;
+
+ if (!js_iscallable(J, 1))
+ js_typeerror(J, "callback is not a function");
+
+ len = js_getlength(J, 0);
+ k = 0;
+
+ if (len == 0 && !hasinitial)
+ js_typeerror(J, "no initial value");
+
+ /* initial value of accumulator */
+ if (hasinitial)
+ js_copy(J, 2);
+ else {
+ while (k < len)
+ if (js_hasindex(J, 0, k++))
+ break;
+ if (k == len)
+ js_typeerror(J, "no initial value");
+ }
+
+ while (k < len) {
+ if (js_hasindex(J, 0, k)) {
+ js_copy(J, 1);
+ js_pushundefined(J);
+ js_rot(J, 4); /* accumulator on top */
+ js_rot(J, 4); /* property on top */
+ js_pushnumber(J, k);
+ js_copy(J, 0);
+ js_call(J, 4); /* calculate new accumulator */
+ }
+ ++k;
+ }
+
+ /* return accumulator */
+}
+
+static void Ap_reduceRight(js_State *J)
+{
+ int hasinitial = js_gettop(J) >= 3;
+ int k, len;
+
+ if (!js_iscallable(J, 1))
+ js_typeerror(J, "callback is not a function");
+
+ len = js_getlength(J, 0);
+ k = len - 1;
+
+ if (len == 0 && !hasinitial)
+ js_typeerror(J, "no initial value");
+
+ /* initial value of accumulator */
+ if (hasinitial)
+ js_copy(J, 2);
+ else {
+ while (k >= 0)
+ if (js_hasindex(J, 0, k--))
+ break;
+ if (k < 0)
+ js_typeerror(J, "no initial value");
+ }
+
+ while (k >= 0) {
+ if (js_hasindex(J, 0, k)) {
+ js_copy(J, 1);
+ js_pushundefined(J);
+ js_rot(J, 4); /* accumulator on top */
+ js_rot(J, 4); /* property on top */
+ js_pushnumber(J, k);
+ js_copy(J, 0);
+ js_call(J, 4); /* calculate new accumulator */
+ }
+ --k;
+ }
+
+ /* return accumulator */
+}
+
+static void A_isArray(js_State *J)
+{
+ if (js_isobject(J, 1)) {
+ js_Object *T = js_toobject(J, 1);
+ js_pushboolean(J, T->type == JS_CARRAY);
+ } else {
+ js_pushboolean(J, 0);
+ }
+}
+
+void jsB_initarray(js_State *J)
+{
+ js_pushobject(J, J->Array_prototype);
+ {
+ jsB_propf(J, "toString", Ap_toString, 0);
+ jsB_propf(J, "concat", Ap_concat, 1);
+ jsB_propf(J, "join", Ap_join, 1);
+ jsB_propf(J, "pop", Ap_pop, 0);
+ jsB_propf(J, "push", Ap_push, 1);
+ jsB_propf(J, "reverse", Ap_reverse, 0);
+ jsB_propf(J, "shift", Ap_shift, 0);
+ jsB_propf(J, "slice", Ap_slice, 2);
+ jsB_propf(J, "sort", Ap_sort, 1);
+ jsB_propf(J, "splice", Ap_splice, 2);
+ jsB_propf(J, "unshift", Ap_unshift, 1);
+
+ /* ES5 */
+ jsB_propf(J, "indexOf", Ap_indexOf, 1);
+ jsB_propf(J, "lastIndexOf", Ap_lastIndexOf, 1);
+ jsB_propf(J, "every", Ap_every, 1);
+ jsB_propf(J, "some", Ap_some, 1);
+ jsB_propf(J, "forEach", Ap_forEach, 1);
+ jsB_propf(J, "map", Ap_map, 1);
+ jsB_propf(J, "filter", Ap_filter, 1);
+ jsB_propf(J, "reduce", Ap_reduce, 1);
+ jsB_propf(J, "reduceRight", Ap_reduceRight, 1);
+ }
+ js_newcconstructor(J, jsB_new_Array, jsB_new_Array, "Array", 1);
+ {
+ /* ES5 */
+ jsB_propf(J, "isArray", A_isArray, 1);
+ }
+ js_defglobal(J, "Array", JS_DONTENUM);
+}
diff --git a/mujs/jsboolean.c b/mujs/jsboolean.c
new file mode 100644
index 0000000..56f8ddd
--- /dev/null
+++ b/mujs/jsboolean.c
@@ -0,0 +1,40 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+static void jsB_new_Boolean(js_State *J)
+{
+ js_newboolean(J, js_toboolean(J, 1));
+}
+
+static void jsB_Boolean(js_State *J)
+{
+ js_pushboolean(J, js_toboolean(J, 1));
+}
+
+static void Bp_toString(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ if (self->type != JS_CBOOLEAN) js_typeerror(J, "not a boolean");
+ js_pushliteral(J, self->u.boolean ? "true" : "false");
+}
+
+static void Bp_valueOf(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ if (self->type != JS_CBOOLEAN) js_typeerror(J, "not a boolean");
+ js_pushboolean(J, self->u.boolean);
+}
+
+void jsB_initboolean(js_State *J)
+{
+ J->Boolean_prototype->u.boolean = 0;
+
+ js_pushobject(J, J->Boolean_prototype);
+ {
+ jsB_propf(J, "toString", Bp_toString, 0);
+ jsB_propf(J, "valueOf", Bp_valueOf, 0);
+ }
+ js_newcconstructor(J, jsB_Boolean, jsB_new_Boolean, "Boolean", 1);
+ js_defglobal(J, "Boolean", JS_DONTENUM);
+}
diff --git a/mujs/jsbuiltin.c b/mujs/jsbuiltin.c
new file mode 100644
index 0000000..9a743ee
--- /dev/null
+++ b/mujs/jsbuiltin.c
@@ -0,0 +1,229 @@
+#include "jsi.h"
+#include "jslex.h"
+#include "jscompile.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+static void jsB_globalf(js_State *J, const char *name, js_CFunction cfun, int n)
+{
+ js_newcfunction(J, cfun, name, n);
+ js_defglobal(J, name, JS_DONTENUM);
+}
+
+void jsB_propf(js_State *J, const char *name, js_CFunction cfun, int n)
+{
+ js_newcfunction(J, cfun, name, n);
+ js_defproperty(J, -2, name, JS_DONTENUM);
+}
+
+void jsB_propn(js_State *J, const char *name, double number)
+{
+ js_pushnumber(J, number);
+ js_defproperty(J, -2, name, JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+}
+
+void jsB_props(js_State *J, const char *name, const char *string)
+{
+ js_pushliteral(J, string);
+ js_defproperty(J, -2, name, JS_DONTENUM);
+}
+
+static void jsB_parseInt(js_State *J)
+{
+ const char *s = js_tostring(J, 1);
+ double radix = js_isdefined(J, 2) ? js_tonumber(J, 2) : 10;
+ char *e;
+ double n;
+
+ while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s;
+ if (radix == 0)
+ radix = 10;
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ s += 2;
+ radix = 16;
+ }
+ n = strtol(s, &e, radix);
+ if (s == e)
+ js_pushnumber(J, NAN);
+ else
+ js_pushnumber(J, n);
+}
+
+static void jsB_parseFloat(js_State *J)
+{
+ const char *s = js_tostring(J, 1);
+ char *e;
+ double n;
+
+ while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s;
+ if (!strncmp(s, "Infinity", 8))
+ js_pushnumber(J, INFINITY);
+ else if (!strncmp(s, "+Infinity", 9))
+ js_pushnumber(J, INFINITY);
+ else if (!strncmp(s, "-Infinity", 9))
+ js_pushnumber(J, -INFINITY);
+ else {
+ n = js_stringtofloat(s, &e);
+ if (e == s)
+ js_pushnumber(J, NAN);
+ else
+ js_pushnumber(J, n);
+ }
+}
+
+static void jsB_isNaN(js_State *J)
+{
+ double n = js_tonumber(J, 1);
+ js_pushboolean(J, isnan(n));
+}
+
+static void jsB_isFinite(js_State *J)
+{
+ double n = js_tonumber(J, 1);
+ js_pushboolean(J, isfinite(n));
+}
+
+static void Encode(js_State *J, const char *str, const char *unescaped)
+{
+ js_Buffer *sb = NULL;
+
+ static const char *HEX = "0123456789ABCDEF";
+
+ while (*str) {
+ int c = (unsigned char) *str++;
+ if (strchr(unescaped, c))
+ js_putc(J, &sb, c);
+ else {
+ js_putc(J, &sb, '%');
+ js_putc(J, &sb, HEX[(c >> 4) & 0xf]);
+ js_putc(J, &sb, HEX[c & 0xf]);
+ }
+ }
+ js_putc(J, &sb, 0);
+
+ if (js_try(J)) {
+ js_free(J, sb);
+ js_throw(J);
+ }
+ js_pushstring(J, sb ? sb->s : "");
+ js_endtry(J);
+ js_free(J, sb);
+}
+
+static void Decode(js_State *J, const char *str, const char *reserved)
+{
+ js_Buffer *sb = NULL;
+ int a, b;
+
+ while (*str) {
+ int c = (unsigned char) *str++;
+ if (c != '%')
+ js_putc(J, &sb, c);
+ else {
+ if (!str[0] || !str[1])
+ js_urierror(J, "truncated escape sequence");
+ a = *str++;
+ b = *str++;
+ if (!jsY_ishex(a) || !jsY_ishex(b))
+ js_urierror(J, "invalid escape sequence");
+ c = jsY_tohex(a) << 4 | jsY_tohex(b);
+ if (!strchr(reserved, c))
+ js_putc(J, &sb, c);
+ else {
+ js_putc(J, &sb, '%');
+ js_putc(J, &sb, a);
+ js_putc(J, &sb, b);
+ }
+ }
+ }
+ js_putc(J, &sb, 0);
+
+ if (js_try(J)) {
+ js_free(J, sb);
+ js_throw(J);
+ }
+ js_pushstring(J, sb ? sb->s : "");
+ js_endtry(J);
+ js_free(J, sb);
+}
+
+#define URIRESERVED ";/?:@&=+$,"
+#define URIALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define URIDIGIT "0123456789"
+#define URIMARK "-_.!~*`()"
+#define URIUNESCAPED URIALPHA URIDIGIT URIMARK
+
+static void jsB_decodeURI(js_State *J)
+{
+ Decode(J, js_tostring(J, 1), URIRESERVED "#");
+}
+
+static void jsB_decodeURIComponent(js_State *J)
+{
+ Decode(J, js_tostring(J, 1), "");
+}
+
+static void jsB_encodeURI(js_State *J)
+{
+ Encode(J, js_tostring(J, 1), URIUNESCAPED URIRESERVED "#");
+}
+
+static void jsB_encodeURIComponent(js_State *J)
+{
+ Encode(J, js_tostring(J, 1), URIUNESCAPED);
+}
+
+void jsB_init(js_State *J)
+{
+ /* Create the prototype objects here, before the constructors */
+ J->Object_prototype = jsV_newobject(J, JS_COBJECT, NULL);
+ J->Array_prototype = jsV_newobject(J, JS_CARRAY, J->Object_prototype);
+ J->Function_prototype = jsV_newobject(J, JS_CCFUNCTION, J->Object_prototype);
+ J->Boolean_prototype = jsV_newobject(J, JS_CBOOLEAN, J->Object_prototype);
+ J->Number_prototype = jsV_newobject(J, JS_CNUMBER, J->Object_prototype);
+ J->String_prototype = jsV_newobject(J, JS_CSTRING, J->Object_prototype);
+ J->RegExp_prototype = jsV_newobject(J, JS_COBJECT, J->Object_prototype);
+ J->Date_prototype = jsV_newobject(J, JS_CDATE, J->Object_prototype);
+
+ /* All the native error types */
+ J->Error_prototype = jsV_newobject(J, JS_CERROR, J->Object_prototype);
+ J->EvalError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+ J->RangeError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+ J->ReferenceError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+ J->SyntaxError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+ J->TypeError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+ J->URIError_prototype = jsV_newobject(J, JS_CERROR, J->Error_prototype);
+
+ /* Create the constructors and fill out the prototype objects */
+ jsB_initobject(J);
+ jsB_initarray(J);
+ jsB_initfunction(J);
+ jsB_initboolean(J);
+ jsB_initnumber(J);
+ jsB_initstring(J);
+ jsB_initregexp(J);
+ jsB_initdate(J);
+ jsB_initerror(J);
+ jsB_initmath(J);
+ jsB_initjson(J);
+
+ /* Initialize the global object */
+ js_pushnumber(J, NAN);
+ js_defglobal(J, "NaN", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+
+ js_pushnumber(J, INFINITY);
+ js_defglobal(J, "Infinity", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+
+ js_pushundefined(J);
+ js_defglobal(J, "undefined", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+
+ jsB_globalf(J, "parseInt", jsB_parseInt, 1);
+ jsB_globalf(J, "parseFloat", jsB_parseFloat, 1);
+ jsB_globalf(J, "isNaN", jsB_isNaN, 1);
+ jsB_globalf(J, "isFinite", jsB_isFinite, 1);
+
+ jsB_globalf(J, "decodeURI", jsB_decodeURI, 1);
+ jsB_globalf(J, "decodeURIComponent", jsB_decodeURIComponent, 1);
+ jsB_globalf(J, "encodeURI", jsB_encodeURI, 1);
+ jsB_globalf(J, "encodeURIComponent", jsB_encodeURIComponent, 1);
+}
diff --git a/mujs/jsbuiltin.h b/mujs/jsbuiltin.h
new file mode 100644
index 0000000..7b71d28
--- /dev/null
+++ b/mujs/jsbuiltin.h
@@ -0,0 +1,50 @@
+#ifndef js_builtin_h
+#define js_builtin_h
+
+void jsB_init(js_State *J);
+void jsB_initobject(js_State *J);
+void jsB_initarray(js_State *J);
+void jsB_initfunction(js_State *J);
+void jsB_initboolean(js_State *J);
+void jsB_initnumber(js_State *J);
+void jsB_initstring(js_State *J);
+void jsB_initregexp(js_State *J);
+void jsB_initerror(js_State *J);
+void jsB_initmath(js_State *J);
+void jsB_initjson(js_State *J);
+void jsB_initdate(js_State *J);
+
+void jsB_propf(js_State *J, const char *name, js_CFunction cfun, int n);
+void jsB_propn(js_State *J, const char *name, double number);
+void jsB_props(js_State *J, const char *name, const char *string);
+
+typedef struct js_Buffer { unsigned int n, m; char s[64]; } js_Buffer;
+
+static void js_putc(js_State *J, js_Buffer **sbp, int c)
+{
+ js_Buffer *sb = *sbp;
+ if (!sb) {
+ sb = js_malloc(J, sizeof *sb);
+ sb->n = 0;
+ sb->m = sizeof sb->s;
+ *sbp = sb;
+ } else if (sb->n == sb->m) {
+ sb = js_realloc(J, sb, (sb->m *= 2) + offsetof(js_Buffer, s));
+ *sbp = sb;
+ }
+ sb->s[sb->n++] = c;
+}
+
+static inline void js_puts(js_State *J, js_Buffer **sb, const char *s)
+{
+ while (*s)
+ js_putc(J, sb, *s++);
+}
+
+static inline void js_putm(js_State *J, js_Buffer **sb, const char *s, const char *e)
+{
+ while (s < e)
+ js_putc(J, sb, *s++);
+}
+
+#endif
diff --git a/mujs/jscompile.c b/mujs/jscompile.c
new file mode 100644
index 0000000..64ac953
--- /dev/null
+++ b/mujs/jscompile.c
@@ -0,0 +1,1319 @@
+#include "jsi.h"
+#include "jsparse.h"
+#include "jscompile.h"
+#include "jsvalue.h" /* for jsV_numbertostring */
+
+#define cexp jsC_cexp /* collision with math.h */
+
+#define JF js_State *J, js_Function *F
+
+JS_NORETURN void jsC_error(js_State *J, js_Ast *node, const char *fmt, ...) JS_PRINTFLIKE(3,4);
+
+static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body);
+static void cexp(JF, js_Ast *exp);
+static void cstmlist(JF, js_Ast *list);
+static void cstm(JF, js_Ast *stm);
+
+void jsC_error(js_State *J, js_Ast *node, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[512];
+ char msgbuf[256];
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, 256, fmt, ap);
+ va_end(ap);
+
+ snprintf(buf, 256, "%s:%d: ", J->filename, node->line);
+ strcat(buf, msgbuf);
+
+ js_newsyntaxerror(J, buf);
+ js_throw(J);
+}
+
+static js_Function *newfun(js_State *J, js_Ast *name, js_Ast *params, js_Ast *body, int script)
+{
+ js_Function *F = js_malloc(J, sizeof *F);
+ memset(F, 0, sizeof *F);
+ F->gcmark = 0;
+ F->gcnext = J->gcfun;
+ J->gcfun = F;
+ ++J->gccounter;
+
+ F->filename = js_intern(J, J->filename);
+ F->line = name ? name->line : params ? params->line : body ? body->line : 1;
+ F->script = script;
+ F->name = name ? name->string : "";
+
+ cfunbody(J, F, name, params, body);
+
+ return F;
+}
+
+/* Emit opcodes, constants and jumps */
+
+static void emitraw(JF, int value)
+{
+ if (value != (js_Instruction)value)
+ js_syntaxerror(J, "integer overflow in instruction coding");
+ if (F->codelen >= F->codecap) {
+ F->codecap = F->codecap ? F->codecap * 2 : 64;
+ F->code = js_realloc(J, F->code, F->codecap * sizeof *F->code);
+ }
+ F->code[F->codelen++] = value;
+}
+
+static void emit(JF, int value)
+{
+ emitraw(J, F, value);
+}
+
+static void emitline(JF, js_Ast *node)
+{
+ if (F->lastline != node->line) {
+ F->lastline = node->line;
+ emit(J, F, OP_LINE);
+ emitraw(J, F, node->line);
+ }
+}
+
+static int addfunction(JF, js_Function *value)
+{
+ if (F->funlen >= F->funcap) {
+ F->funcap = F->funcap ? F->funcap * 2 : 16;
+ F->funtab = js_realloc(J, F->funtab, F->funcap * sizeof *F->funtab);
+ }
+ F->funtab[F->funlen] = value;
+ return F->funlen++;
+}
+
+static int addnumber(JF, double value)
+{
+ unsigned int i;
+ for (i = 0; i < F->numlen; ++i)
+ if (F->numtab[i] == value)
+ return i;
+ if (F->numlen >= F->numcap) {
+ F->numcap = F->numcap ? F->numcap * 2 : 16;
+ F->numtab = js_realloc(J, F->numtab, F->numcap * sizeof *F->numtab);
+ }
+ F->numtab[F->numlen] = value;
+ return F->numlen++;
+}
+
+static int addstring(JF, const char *value)
+{
+ unsigned int i;
+ for (i = 0; i < F->strlen; ++i)
+ if (!strcmp(F->strtab[i], value))
+ return i;
+ if (F->strlen >= F->strcap) {
+ F->strcap = F->strcap ? F->strcap * 2 : 16;
+ F->strtab = js_realloc(J, F->strtab, F->strcap * sizeof *F->strtab);
+ }
+ F->strtab[F->strlen] = value;
+ return F->strlen++;
+}
+
+static void addlocal(JF, js_Ast *ident, int reuse)
+{
+ const char *name = ident->string;
+ if (J->strict) {
+ if (!strcmp(name, "arguments"))
+ jsC_error(J, ident, "redefining 'arguments' is not allowed in strict mode");
+ if (!strcmp(name, "eval"))
+ jsC_error(J, ident, "redefining 'eval' is not allowed in strict mode");
+ }
+ if (reuse || J->strict) {
+ unsigned int i;
+ for (i = 0; i < F->varlen; ++i) {
+ if (!strcmp(F->vartab[i], name)) {
+ if (reuse)
+ return;
+ if (J->strict)
+ jsC_error(J, ident, "duplicate formal parameter '%s'", name);
+ }
+ }
+ }
+ if (F->varlen >= F->varcap) {
+ F->varcap = F->varcap ? F->varcap * 2 : 16;
+ F->vartab = js_realloc(J, F->vartab, F->varcap * sizeof *F->vartab);
+ }
+ F->vartab[F->varlen++] = name;
+}
+
+static int findlocal(JF, const char *name)
+{
+ unsigned int i;
+ for (i = F->varlen; i > 0; --i)
+ if (!strcmp(F->vartab[i-1], name))
+ return i;
+ return -1;
+}
+
+static void emitfunction(JF, js_Function *fun)
+{
+ emit(J, F, OP_CLOSURE);
+ emitraw(J, F, addfunction(J, F, fun));
+}
+
+static void emitnumber(JF, double num)
+{
+ if (num == 0) {
+ emit(J, F, OP_NUMBER_0);
+ if (signbit(num))
+ emit(J, F, OP_NEG);
+ } else if (num == 1) {
+ emit(J, F, OP_NUMBER_1);
+ } else if (num == (js_Instruction)num) {
+ emit(J, F, OP_NUMBER_POS);
+ emitraw(J, F, (js_Instruction)num);
+ } else if (num < 0 && -num == (js_Instruction)(-num)) {
+ emit(J, F, OP_NUMBER_NEG);
+ emitraw(J, F, (js_Instruction)(-num));
+ } else {
+ emit(J, F, OP_NUMBER);
+ emitraw(J, F, addnumber(J, F, num));
+ }
+}
+
+static void emitstring(JF, int opcode, const char *str)
+{
+ emit(J, F, opcode);
+ emitraw(J, F, addstring(J, F, str));
+}
+
+static void emitlocal(JF, int oploc, int opvar, js_Ast *ident)
+{
+ int i;
+ if (J->strict && oploc == OP_SETLOCAL) {
+ if (!strcmp(ident->string, "arguments"))
+ jsC_error(J, ident, "'arguments' is read-only in strict mode");
+ if (!strcmp(ident->string, "eval"))
+ jsC_error(J, ident, "'eval' is read-only in strict mode");
+ }
+ if (F->lightweight) {
+ i = findlocal(J, F, ident->string);
+ if (i >= 0) {
+ emit(J, F, oploc);
+ emitraw(J, F, i);
+ return;
+ }
+ }
+ emitstring(J, F, opvar, ident->string);
+}
+
+static int here(JF)
+{
+ return F->codelen;
+}
+
+static int emitjump(JF, int opcode)
+{
+ int inst = F->codelen + 1;
+ emit(J, F, opcode);
+ emitraw(J, F, 0);
+ return inst;
+}
+
+static void emitjumpto(JF, int opcode, int dest)
+{
+ emit(J, F, opcode);
+ if (dest != (js_Instruction)dest)
+ js_syntaxerror(J, "jump address integer overflow");
+ emitraw(J, F, dest);
+}
+
+static void labelto(JF, int inst, int addr)
+{
+ if (addr != (js_Instruction)addr)
+ js_syntaxerror(J, "jump address integer overflow");
+ F->code[inst] = addr;
+}
+
+static void label(JF, int inst)
+{
+ labelto(J, F, inst, F->codelen);
+}
+
+/* Expressions */
+
+static void ctypeof(JF, js_Ast *exp)
+{
+ if (exp->type == EXP_IDENTIFIER)
+ emitlocal(J, F, OP_GETLOCAL, OP_HASVAR, exp);
+ else
+ cexp(J, F, exp);
+ emit(J, F, OP_TYPEOF);
+}
+
+static void cunary(JF, js_Ast *exp, int opcode)
+{
+ cexp(J, F, exp->a);
+ emit(J, F, opcode);
+}
+
+static void cbinary(JF, js_Ast *exp, int opcode)
+{
+ cexp(J, F, exp->a);
+ cexp(J, F, exp->b);
+ emit(J, F, opcode);
+}
+
+static void carray(JF, js_Ast *list)
+{
+ int i = 0;
+ while (list) {
+ if (list->a->type != EXP_UNDEF) {
+ emitnumber(J, F, i++);
+ cexp(J, F, list->a);
+ emit(J, F, OP_INITPROP);
+ } else {
+ ++i;
+ }
+ list = list->b;
+ }
+}
+
+static void checkdup(JF, js_Ast *list, js_Ast *end)
+{
+ char nbuf[32], sbuf[32];
+ const char *needle, *straw;
+
+ if (end->a->type == EXP_NUMBER)
+ needle = jsV_numbertostring(J, nbuf, end->a->number);
+ else
+ needle = end->a->string;
+
+ while (list->a != end) {
+ if (list->a->type == end->type) {
+ js_Ast *prop = list->a->a;
+ if (prop->type == EXP_NUMBER)
+ straw = jsV_numbertostring(J, sbuf, prop->number);
+ else
+ straw = prop->string;
+ if (!strcmp(needle, straw))
+ jsC_error(J, list, "duplicate property '%s' in object literal", needle);
+ }
+ list = list->b;
+ }
+}
+
+static void cobject(JF, js_Ast *list)
+{
+ js_Ast *head = list;
+
+ while (list) {
+ js_Ast *kv = list->a;
+ js_Ast *prop = kv->a;
+
+ if (prop->type == AST_IDENTIFIER || prop->type == EXP_STRING)
+ emitstring(J, F, OP_STRING, prop->string);
+ else if (prop->type == EXP_NUMBER)
+ emitnumber(J, F, prop->number);
+ else
+ jsC_error(J, prop, "invalid property name in object initializer");
+
+ if (J->strict)
+ checkdup(J, F, head, kv);
+
+ switch (kv->type) {
+ case EXP_PROP_VAL:
+ cexp(J, F, kv->b);
+ emit(J, F, OP_INITPROP);
+ break;
+ case EXP_PROP_GET:
+ emitfunction(J, F, newfun(J, NULL, kv->b, kv->c, 0));
+ emit(J, F, OP_INITGETTER);
+ break;
+ case EXP_PROP_SET:
+ emitfunction(J, F, newfun(J, NULL, kv->b, kv->c, 0));
+ emit(J, F, OP_INITSETTER);
+ break;
+ }
+
+ list = list->b;
+ }
+}
+
+static int cargs(JF, js_Ast *list)
+{
+ int n = 0;
+ while (list) {
+ cexp(J, F, list->a);
+ list = list->b;
+ ++n;
+ }
+ return n;
+}
+
+static void cassign(JF, js_Ast *exp)
+{
+ js_Ast *lhs = exp->a;
+ js_Ast *rhs = exp->b;
+ switch (lhs->type) {
+ case EXP_IDENTIFIER:
+ cexp(J, F, rhs);
+ emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs);
+ break;
+ case EXP_INDEX:
+ cexp(J, F, lhs->a);
+ cexp(J, F, lhs->b);
+ cexp(J, F, rhs);
+ emit(J, F, OP_SETPROP);
+ break;
+ case EXP_MEMBER:
+ cexp(J, F, lhs->a);
+ cexp(J, F, rhs);
+ emitstring(J, F, OP_SETPROP_S, lhs->b->string);
+ break;
+ default:
+ jsC_error(J, lhs, "invalid l-value in assignment");
+ }
+}
+
+static void cassignforin(JF, js_Ast *stm)
+{
+ js_Ast *lhs = stm->a;
+
+ if (stm->type == STM_FOR_IN_VAR) {
+ if (lhs->b)
+ jsC_error(J, lhs->b, "more than one loop variable in for-in statement");
+ emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->a->a); /* list(var-init(ident)) */
+ emit(J, F, OP_POP);
+ return;
+ }
+
+ switch (lhs->type) {
+ case EXP_IDENTIFIER:
+ emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs);
+ emit(J, F, OP_POP);
+ break;
+ case EXP_INDEX:
+ cexp(J, F, lhs->a);
+ cexp(J, F, lhs->b);
+ emit(J, F, OP_ROT3);
+ emit(J, F, OP_SETPROP);
+ emit(J, F, OP_POP);
+ break;
+ case EXP_MEMBER:
+ cexp(J, F, lhs->a);
+ emit(J, F, OP_ROT2);
+ emitstring(J, F, OP_SETPROP_S, lhs->b->string);
+ emit(J, F, OP_POP);
+ break;
+ default:
+ jsC_error(J, lhs, "invalid l-value in for-in loop assignment");
+ }
+}
+
+static void cassignop1(JF, js_Ast *lhs)
+{
+ switch (lhs->type) {
+ case EXP_IDENTIFIER:
+ emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, lhs);
+ break;
+ case EXP_INDEX:
+ cexp(J, F, lhs->a);
+ cexp(J, F, lhs->b);
+ emit(J, F, OP_DUP2);
+ emit(J, F, OP_GETPROP);
+ break;
+ case EXP_MEMBER:
+ cexp(J, F, lhs->a);
+ emit(J, F, OP_DUP);
+ emitstring(J, F, OP_GETPROP_S, lhs->b->string);
+ break;
+ default:
+ jsC_error(J, lhs, "invalid l-value in assignment");
+ }
+}
+
+static void cassignop2(JF, js_Ast *lhs, int postfix)
+{
+ switch (lhs->type) {
+ case EXP_IDENTIFIER:
+ if (postfix) emit(J, F, OP_ROT2);
+ emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs);
+ break;
+ case EXP_INDEX:
+ if (postfix) emit(J, F, OP_ROT4);
+ emit(J, F, OP_SETPROP);
+ break;
+ case EXP_MEMBER:
+ if (postfix) emit(J, F, OP_ROT3);
+ emitstring(J, F, OP_SETPROP_S, lhs->b->string);
+ break;
+ default:
+ jsC_error(J, lhs, "invalid l-value in assignment");
+ }
+}
+
+static void cassignop(JF, js_Ast *exp, int opcode)
+{
+ js_Ast *lhs = exp->a;
+ js_Ast *rhs = exp->b;
+ cassignop1(J, F, lhs);
+ cexp(J, F, rhs);
+ emit(J, F, opcode);
+ cassignop2(J, F, lhs, 0);
+}
+
+static void cdelete(JF, js_Ast *exp)
+{
+ switch (exp->type) {
+ case EXP_IDENTIFIER:
+ if (J->strict)
+ jsC_error(J, exp, "delete on an unqualified name is not allowed in strict mode");
+ emitlocal(J, F, OP_DELLOCAL, OP_DELVAR, exp);
+ break;
+ case EXP_INDEX:
+ cexp(J, F, exp->a);
+ cexp(J, F, exp->b);
+ emit(J, F, OP_DELPROP);
+ break;
+ case EXP_MEMBER:
+ cexp(J, F, exp->a);
+ emitstring(J, F, OP_DELPROP_S, exp->b->string);
+ break;
+ default:
+ jsC_error(J, exp, "invalid l-value in delete expression");
+ }
+}
+
+static void ceval(JF, js_Ast *fun, js_Ast *args)
+{
+ int n = cargs(J, F, args);
+ if (n == 0)
+ emit(J, F, OP_UNDEF);
+ else while (n-- > 1)
+ emit(J, F, OP_POP);
+ emit(J, F, OP_EVAL);
+}
+
+static void ccall(JF, js_Ast *fun, js_Ast *args)
+{
+ int n;
+ switch (fun->type) {
+ case EXP_INDEX:
+ cexp(J, F, fun->a);
+ emit(J, F, OP_DUP);
+ cexp(J, F, fun->b);
+ emit(J, F, OP_GETPROP);
+ emit(J, F, OP_ROT2);
+ break;
+ case EXP_MEMBER:
+ cexp(J, F, fun->a);
+ emit(J, F, OP_DUP);
+ emitstring(J, F, OP_GETPROP_S, fun->b->string);
+ emit(J, F, OP_ROT2);
+ break;
+ case EXP_IDENTIFIER:
+ if (!strcmp(fun->string, "eval")) {
+ ceval(J, F, fun, args);
+ return;
+ }
+ /* fall through */
+ default:
+ cexp(J, F, fun);
+ emit(J, F, J->strict ? OP_UNDEF : OP_GLOBAL);
+ break;
+ }
+ n = cargs(J, F, args);
+ emit(J, F, OP_CALL);
+ emitraw(J, F, n);
+}
+
+static void cexp(JF, js_Ast *exp)
+{
+ int then, end;
+ int n;
+
+ switch (exp->type) {
+ case EXP_STRING: emitstring(J, F, OP_STRING, exp->string); break;
+ case EXP_NUMBER: emitnumber(J, F, exp->number); break;
+ case EXP_UNDEF: emit(J, F, OP_UNDEF); break;
+ case EXP_NULL: emit(J, F, OP_NULL); break;
+ case EXP_TRUE: emit(J, F, OP_TRUE); break;
+ case EXP_FALSE: emit(J, F, OP_FALSE); break;
+ case EXP_THIS: emit(J, F, OP_THIS); break;
+
+ case EXP_REGEXP:
+ emit(J, F, OP_NEWREGEXP);
+ emitraw(J, F, addstring(J, F, exp->string));
+ emitraw(J, F, exp->number);
+ break;
+
+ case EXP_OBJECT:
+ emit(J, F, OP_NEWOBJECT);
+ cobject(J, F, exp->a);
+ break;
+
+ case EXP_ARRAY:
+ emit(J, F, OP_NEWARRAY);
+ carray(J, F, exp->a);
+ break;
+
+ case EXP_FUN:
+ emitfunction(J, F, newfun(J, exp->a, exp->b, exp->c, 0));
+ break;
+
+ case EXP_IDENTIFIER:
+ emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, exp);
+ break;
+
+ case EXP_INDEX:
+ cexp(J, F, exp->a);
+ cexp(J, F, exp->b);
+ emit(J, F, OP_GETPROP);
+ break;
+
+ case EXP_MEMBER:
+ cexp(J, F, exp->a);
+ emitstring(J, F, OP_GETPROP_S, exp->b->string);
+ break;
+
+ case EXP_CALL:
+ ccall(J, F, exp->a, exp->b);
+ break;
+
+ case EXP_NEW:
+ cexp(J, F, exp->a);
+ n = cargs(J, F, exp->b);
+ emit(J, F, OP_NEW);
+ emitraw(J, F, n);
+ break;
+
+ case EXP_DELETE:
+ cdelete(J, F, exp->a);
+ break;
+
+ case EXP_PREINC:
+ cassignop1(J, F, exp->a);
+ emit(J, F, OP_INC);
+ cassignop2(J, F, exp->a, 0);
+ break;
+
+ case EXP_PREDEC:
+ cassignop1(J, F, exp->a);
+ emit(J, F, OP_DEC);
+ cassignop2(J, F, exp->a, 0);
+ break;
+
+ case EXP_POSTINC:
+ cassignop1(J, F, exp->a);
+ emit(J, F, OP_POSTINC);
+ cassignop2(J, F, exp->a, 1);
+ emit(J, F, OP_POP);
+ break;
+
+ case EXP_POSTDEC:
+ cassignop1(J, F, exp->a);
+ emit(J, F, OP_POSTDEC);
+ cassignop2(J, F, exp->a, 1);
+ emit(J, F, OP_POP);
+ break;
+
+ case EXP_VOID:
+ cexp(J, F, exp->a);
+ emit(J, F, OP_POP);
+ emit(J, F, OP_UNDEF);
+ break;
+
+ case EXP_TYPEOF: ctypeof(J, F, exp->a); break;
+ case EXP_POS: cunary(J, F, exp, OP_POS); break;
+ case EXP_NEG: cunary(J, F, exp, OP_NEG); break;
+ case EXP_BITNOT: cunary(J, F, exp, OP_BITNOT); break;
+ case EXP_LOGNOT: cunary(J, F, exp, OP_LOGNOT); break;
+
+ case EXP_BITOR: cbinary(J, F, exp, OP_BITOR); break;
+ case EXP_BITXOR: cbinary(J, F, exp, OP_BITXOR); break;
+ case EXP_BITAND: cbinary(J, F, exp, OP_BITAND); break;
+ case EXP_EQ: cbinary(J, F, exp, OP_EQ); break;
+ case EXP_NE: cbinary(J, F, exp, OP_NE); break;
+ case EXP_STRICTEQ: cbinary(J, F, exp, OP_STRICTEQ); break;
+ case EXP_STRICTNE: cbinary(J, F, exp, OP_STRICTNE); break;
+ case EXP_LT: cbinary(J, F, exp, OP_LT); break;
+ case EXP_GT: cbinary(J, F, exp, OP_GT); break;
+ case EXP_LE: cbinary(J, F, exp, OP_LE); break;
+ case EXP_GE: cbinary(J, F, exp, OP_GE); break;
+ case EXP_INSTANCEOF: cbinary(J, F, exp, OP_INSTANCEOF); break;
+ case EXP_IN: cbinary(J, F, exp, OP_IN); break;
+ case EXP_SHL: cbinary(J, F, exp, OP_SHL); break;
+ case EXP_SHR: cbinary(J, F, exp, OP_SHR); break;
+ case EXP_USHR: cbinary(J, F, exp, OP_USHR); break;
+ case EXP_ADD: cbinary(J, F, exp, OP_ADD); break;
+ case EXP_SUB: cbinary(J, F, exp, OP_SUB); break;
+ case EXP_MUL: cbinary(J, F, exp, OP_MUL); break;
+ case EXP_DIV: cbinary(J, F, exp, OP_DIV); break;
+ case EXP_MOD: cbinary(J, F, exp, OP_MOD); break;
+
+ case EXP_ASS: cassign(J, F, exp); break;
+ case EXP_ASS_MUL: cassignop(J, F, exp, OP_MUL); break;
+ case EXP_ASS_DIV: cassignop(J, F, exp, OP_DIV); break;
+ case EXP_ASS_MOD: cassignop(J, F, exp, OP_MOD); break;
+ case EXP_ASS_ADD: cassignop(J, F, exp, OP_ADD); break;
+ case EXP_ASS_SUB: cassignop(J, F, exp, OP_SUB); break;
+ case EXP_ASS_SHL: cassignop(J, F, exp, OP_SHL); break;
+ case EXP_ASS_SHR: cassignop(J, F, exp, OP_SHR); break;
+ case EXP_ASS_USHR: cassignop(J, F, exp, OP_USHR); break;
+ case EXP_ASS_BITAND: cassignop(J, F, exp, OP_BITAND); break;
+ case EXP_ASS_BITXOR: cassignop(J, F, exp, OP_BITXOR); break;
+ case EXP_ASS_BITOR: cassignop(J, F, exp, OP_BITOR); break;
+
+ case EXP_COMMA:
+ cexp(J, F, exp->a);
+ emit(J, F, OP_POP);
+ cexp(J, F, exp->b);
+ break;
+
+ case EXP_LOGOR:
+ cexp(J, F, exp->a);
+ emit(J, F, OP_DUP);
+ end = emitjump(J, F, OP_JTRUE);
+ emit(J, F, OP_POP);
+ cexp(J, F, exp->b);
+ label(J, F, end);
+ break;
+
+ case EXP_LOGAND:
+ cexp(J, F, exp->a);
+ emit(J, F, OP_DUP);
+ end = emitjump(J, F, OP_JFALSE);
+ emit(J, F, OP_POP);
+ cexp(J, F, exp->b);
+ label(J, F, end);
+ break;
+
+ case EXP_COND:
+ cexp(J, F, exp->a);
+ then = emitjump(J, F, OP_JTRUE);
+ cexp(J, F, exp->c);
+ end = emitjump(J, F, OP_JUMP);
+ label(J, F, then);
+ cexp(J, F, exp->b);
+ label(J, F, end);
+ break;
+
+ default:
+ jsC_error(J, exp, "unknown expression: (%s)", jsP_aststring(exp->type));
+ }
+}
+
+/* Patch break and continue statements */
+
+static void addjump(JF, enum js_AstType type, js_Ast *target, int inst)
+{
+ js_JumpList *jump = js_malloc(J, sizeof *jump);
+ jump->type = type;
+ jump->inst = inst;
+ jump->next = target->jumps;
+ target->jumps = jump;
+}
+
+static void labeljumps(JF, js_JumpList *jump, int baddr, int caddr)
+{
+ while (jump) {
+ if (jump->type == STM_BREAK)
+ labelto(J, F, jump->inst, baddr);
+ if (jump->type == STM_CONTINUE)
+ labelto(J, F, jump->inst, caddr);
+ jump = jump->next;
+ }
+}
+
+static int isloop(enum js_AstType T)
+{
+ return T == STM_DO || T == STM_WHILE ||
+ T == STM_FOR || T == STM_FOR_VAR ||
+ T == STM_FOR_IN || T == STM_FOR_IN_VAR;
+}
+
+static int isfun(enum js_AstType T)
+{
+ return T == AST_FUNDEC || T == EXP_FUN || T == EXP_PROP_GET || T == EXP_PROP_SET;
+}
+
+static int matchlabel(js_Ast *node, const char *label)
+{
+ while (node && node->type == STM_LABEL) {
+ if (!strcmp(node->a->string, label))
+ return 1;
+ node = node->parent;
+ }
+ return 0;
+}
+
+static js_Ast *breaktarget(JF, js_Ast *node, const char *label)
+{
+ while (node) {
+ if (isfun(node->type))
+ break;
+ if (!label) {
+ if (isloop(node->type) || node->type == STM_SWITCH)
+ return node;
+ } else {
+ if (matchlabel(node->parent, label))
+ return node;
+ }
+ node = node->parent;
+ }
+ return NULL;
+}
+
+static js_Ast *continuetarget(JF, js_Ast *node, const char *label)
+{
+ while (node) {
+ if (isfun(node->type))
+ break;
+ if (isloop(node->type)) {
+ if (!label)
+ return node;
+ else if (matchlabel(node->parent, label))
+ return node;
+ }
+ node = node->parent;
+ }
+ return NULL;
+}
+
+static js_Ast *returntarget(JF, js_Ast *node)
+{
+ while (node) {
+ if (isfun(node->type))
+ return node;
+ node = node->parent;
+ }
+ return NULL;
+}
+
+/* Emit code to rebalance stack and scopes during an abrupt exit */
+
+static void cexit(JF, enum js_AstType T, js_Ast *node, js_Ast *target)
+{
+ js_Ast *prev;
+ do {
+ prev = node, node = node->parent;
+ switch (node->type) {
+ case STM_WITH:
+ emit(J, F, OP_ENDWITH);
+ break;
+ case STM_FOR_IN:
+ case STM_FOR_IN_VAR:
+ /* pop the iterator if leaving the loop */
+ if (F->script) {
+ if (T == STM_RETURN || T == STM_BREAK || (T == STM_CONTINUE && target != node)) {
+ /* pop the iterator, save the return or exp value */
+ emit(J, F, OP_ROT2);
+ emit(J, F, OP_POP);
+ }
+ if (T == STM_CONTINUE)
+ emit(J, F, OP_ROT2); /* put the iterator back on top */
+ } else {
+ if (T == STM_RETURN) {
+ /* pop the iterator, save the return value */
+ emit(J, F, OP_ROT2);
+ emit(J, F, OP_POP);
+ }
+ if (T == STM_BREAK || (T == STM_CONTINUE && target != node))
+ emit(J, F, OP_POP); /* pop the iterator */
+ }
+ break;
+ case STM_TRY:
+ /* came from try block */
+ if (prev == node->a) {
+ emit(J, F, OP_ENDTRY);
+ if (node->d) cstm(J, F, node->d); /* finally */
+ }
+ /* came from catch block */
+ if (prev == node->c) {
+ /* ... with finally */
+ if (node->d) {
+ emit(J, F, OP_ENDCATCH);
+ emit(J, F, OP_ENDTRY);
+ cstm(J, F, node->d); /* finally */
+ } else {
+ emit(J, F, OP_ENDCATCH);
+ }
+ }
+ break;
+ }
+ } while (node != target);
+}
+
+/* Try/catch/finally */
+
+static void ctryfinally(JF, js_Ast *trystm, js_Ast *finallystm)
+{
+ int L1;
+ L1 = emitjump(J, F, OP_TRY);
+ {
+ /* if we get here, we have caught an exception in the try block */
+ cstm(J, F, finallystm); /* inline finally block */
+ emit(J, F, OP_THROW); /* rethrow exception */
+ }
+ label(J, F, L1);
+ cstm(J, F, trystm);
+ emit(J, F, OP_ENDTRY);
+ cstm(J, F, finallystm);
+}
+
+static void ctrycatch(JF, js_Ast *trystm, js_Ast *catchvar, js_Ast *catchstm)
+{
+ int L1, L2;
+ L1 = emitjump(J, F, OP_TRY);
+ {
+ /* if we get here, we have caught an exception in the try block */
+ if (J->strict) {
+ if (!strcmp(catchvar->string, "arguments"))
+ jsC_error(J, catchvar, "redefining 'arguments' is not allowed in strict mode");
+ if (!strcmp(catchvar->string, "eval"))
+ jsC_error(J, catchvar, "redefining 'eval' is not allowed in strict mode");
+ }
+ emitstring(J, F, OP_CATCH, catchvar->string);
+ cstm(J, F, catchstm);
+ emit(J, F, OP_ENDCATCH);
+ L2 = emitjump(J, F, OP_JUMP); /* skip past the try block */
+ }
+ label(J, F, L1);
+ cstm(J, F, trystm);
+ emit(J, F, OP_ENDTRY);
+ label(J, F, L2);
+}
+
+static void ctrycatchfinally(JF, js_Ast *trystm, js_Ast *catchvar, js_Ast *catchstm, js_Ast *finallystm)
+{
+ int L1, L2, L3;
+ L1 = emitjump(J, F, OP_TRY);
+ {
+ /* if we get here, we have caught an exception in the try block */
+ L2 = emitjump(J, F, OP_TRY);
+ {
+ /* if we get here, we have caught an exception in the catch block */
+ cstm(J, F, finallystm); /* inline finally block */
+ emit(J, F, OP_THROW); /* rethrow exception */
+ }
+ label(J, F, L2);
+ if (J->strict) {
+ if (!strcmp(catchvar->string, "arguments"))
+ jsC_error(J, catchvar, "redefining 'arguments' is not allowed in strict mode");
+ if (!strcmp(catchvar->string, "eval"))
+ jsC_error(J, catchvar, "redefining 'eval' is not allowed in strict mode");
+ }
+ emitstring(J, F, OP_CATCH, catchvar->string);
+ cstm(J, F, catchstm);
+ emit(J, F, OP_ENDCATCH);
+ L3 = emitjump(J, F, OP_JUMP); /* skip past the try block to the finally block */
+ }
+ label(J, F, L1);
+ cstm(J, F, trystm);
+ emit(J, F, OP_ENDTRY);
+ label(J, F, L3);
+ cstm(J, F, finallystm);
+}
+
+/* Switch */
+
+static void cswitch(JF, js_Ast *ref, js_Ast *head)
+{
+ js_Ast *node, *clause, *def = NULL;
+ int end;
+
+ cexp(J, F, ref);
+
+ /* emit an if-else chain of tests for the case clause expressions */
+ for (node = head; node; node = node->b) {
+ clause = node->a;
+ if (clause->type == STM_DEFAULT) {
+ if (def)
+ jsC_error(J, clause, "more than one default label in switch");
+ def = clause;
+ } else {
+ cexp(J, F, clause->a);
+ clause->casejump = emitjump(J, F, OP_JCASE);
+ }
+ }
+ emit(J, F, OP_POP);
+ if (def) {
+ def->casejump = emitjump(J, F, OP_JUMP);
+ end = 0;
+ } else {
+ end = emitjump(J, F, OP_JUMP);
+ }
+
+ /* emit the casue clause bodies */
+ for (node = head; node; node = node->b) {
+ clause = node->a;
+ label(J, F, clause->casejump);
+ if (clause->type == STM_DEFAULT)
+ cstmlist(J, F, clause->a);
+ else
+ cstmlist(J, F, clause->b);
+ }
+
+ if (end)
+ label(J, F, end);
+}
+
+/* Statements */
+
+static void cvarinit(JF, js_Ast *list)
+{
+ while (list) {
+ js_Ast *var = list->a;
+ if (var->b) {
+ cexp(J, F, var->b);
+ emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, var->a);
+ emit(J, F, OP_POP);
+ }
+ list = list->b;
+ }
+}
+
+static void cstm(JF, js_Ast *stm)
+{
+ js_Ast *target;
+ int loop, cont, then, end;
+
+ emitline(J, F, stm);
+
+ switch (stm->type) {
+ case AST_FUNDEC:
+ break;
+
+ case STM_BLOCK:
+ cstmlist(J, F, stm->a);
+ break;
+
+ case STM_EMPTY:
+ if (F->script) {
+ emit(J, F, OP_POP);
+ emit(J, F, OP_UNDEF);
+ }
+ break;
+
+ case STM_VAR:
+ cvarinit(J, F, stm->a);
+ break;
+
+ case STM_IF:
+ if (stm->c) {
+ cexp(J, F, stm->a);
+ then = emitjump(J, F, OP_JTRUE);
+ cstm(J, F, stm->c);
+ end = emitjump(J, F, OP_JUMP);
+ label(J, F, then);
+ cstm(J, F, stm->b);
+ label(J, F, end);
+ } else {
+ cexp(J, F, stm->a);
+ end = emitjump(J, F, OP_JFALSE);
+ cstm(J, F, stm->b);
+ label(J, F, end);
+ }
+ break;
+
+ case STM_DO:
+ loop = here(J, F);
+ cstm(J, F, stm->a);
+ cont = here(J, F);
+ cexp(J, F, stm->b);
+ emitjumpto(J, F, OP_JTRUE, loop);
+ labeljumps(J, F, stm->jumps, here(J,F), cont);
+ break;
+
+ case STM_WHILE:
+ loop = here(J, F);
+ cexp(J, F, stm->a);
+ end = emitjump(J, F, OP_JFALSE);
+ cstm(J, F, stm->b);
+ emitjumpto(J, F, OP_JUMP, loop);
+ label(J, F, end);
+ labeljumps(J, F, stm->jumps, here(J,F), loop);
+ break;
+
+ case STM_FOR:
+ case STM_FOR_VAR:
+ if (stm->type == STM_FOR_VAR) {
+ cvarinit(J, F, stm->a);
+ } else {
+ if (stm->a) {
+ cexp(J, F, stm->a);
+ emit(J, F, OP_POP);
+ }
+ }
+ loop = here(J, F);
+ if (stm->b) {
+ cexp(J, F, stm->b);
+ end = emitjump(J, F, OP_JFALSE);
+ } else {
+ end = 0;
+ }
+ cstm(J, F, stm->d);
+ cont = here(J, F);
+ if (stm->c) {
+ cexp(J, F, stm->c);
+ emit(J, F, OP_POP);
+ }
+ emitjumpto(J, F, OP_JUMP, loop);
+ if (end)
+ label(J, F, end);
+ labeljumps(J, F, stm->jumps, here(J,F), cont);
+ break;
+
+ case STM_FOR_IN:
+ case STM_FOR_IN_VAR:
+ cexp(J, F, stm->b);
+ emit(J, F, OP_ITERATOR);
+ loop = here(J, F);
+ {
+ emit(J, F, OP_NEXTITER);
+ end = emitjump(J, F, OP_JFALSE);
+ cassignforin(J, F, stm);
+ if (F->script) {
+ emit(J, F, OP_ROT2);
+ cstm(J, F, stm->c);
+ emit(J, F, OP_ROT2);
+ } else {
+ cstm(J, F, stm->c);
+ }
+ emitjumpto(J, F, OP_JUMP, loop);
+ }
+ label(J, F, end);
+ labeljumps(J, F, stm->jumps, here(J,F), loop);
+ break;
+
+ case STM_SWITCH:
+ cswitch(J, F, stm->a, stm->b);
+ labeljumps(J, F, stm->jumps, here(J,F), 0);
+ break;
+
+ case STM_LABEL:
+ cstm(J, F, stm->b);
+ /* skip consecutive labels */
+ while (stm->type == STM_LABEL)
+ stm = stm->b;
+ /* loops and switches have already been labelled */
+ if (!isloop(stm->type) && stm->type != STM_SWITCH)
+ labeljumps(J, F, stm->jumps, here(J,F), 0);
+ break;
+
+ case STM_BREAK:
+ if (stm->a) {
+ target = breaktarget(J, F, stm, stm->a->string);
+ if (!target)
+ jsC_error(J, stm, "break label '%s' not found", stm->a->string);
+ } else {
+ target = breaktarget(J, F, stm, NULL);
+ if (!target)
+ jsC_error(J, stm, "unlabelled break must be inside loop or switch");
+ }
+ cexit(J, F, STM_BREAK, stm, target);
+ addjump(J, F, STM_BREAK, target, emitjump(J, F, OP_JUMP));
+ break;
+
+ case STM_CONTINUE:
+ if (stm->a) {
+ target = continuetarget(J, F, stm, stm->a->string);
+ if (!target)
+ jsC_error(J, stm, "continue label '%s' not found", stm->a->string);
+ } else {
+ target = continuetarget(J, F, stm, NULL);
+ if (!target)
+ jsC_error(J, stm, "continue must be inside loop");
+ }
+ cexit(J, F, STM_CONTINUE, stm, target);
+ addjump(J, F, STM_CONTINUE, target, emitjump(J, F, OP_JUMP));
+ break;
+
+ case STM_RETURN:
+ if (stm->a)
+ cexp(J, F, stm->a);
+ else
+ emit(J, F, OP_UNDEF);
+ target = returntarget(J, F, stm);
+ if (!target)
+ jsC_error(J, stm, "return not in function");
+ cexit(J, F, STM_RETURN, stm, target);
+ emit(J, F, OP_RETURN);
+ break;
+
+ case STM_THROW:
+ cexp(J, F, stm->a);
+ emit(J, F, OP_THROW);
+ break;
+
+ case STM_WITH:
+ cexp(J, F, stm->a);
+ emit(J, F, OP_WITH);
+ cstm(J, F, stm->b);
+ emit(J, F, OP_ENDWITH);
+ break;
+
+ case STM_TRY:
+ if (stm->b && stm->c) {
+ if (stm->d)
+ ctrycatchfinally(J, F, stm->a, stm->b, stm->c, stm->d);
+ else
+ ctrycatch(J, F, stm->a, stm->b, stm->c);
+ } else {
+ ctryfinally(J, F, stm->a, stm->d);
+ }
+ break;
+
+ case STM_DEBUGGER:
+ emit(J, F, OP_DEBUGGER);
+ break;
+
+ default:
+ if (F->script) {
+ emit(J, F, OP_POP);
+ cexp(J, F, stm);
+ } else {
+ cexp(J, F, stm);
+ emit(J, F, OP_POP);
+ }
+ break;
+ }
+}
+
+static void cstmlist(JF, js_Ast *list)
+{
+ while (list) {
+ cstm(J, F, list->a);
+ list = list->b;
+ }
+}
+
+/* Analyze */
+
+static void analyze(JF, js_Ast *node)
+{
+ if (isfun(node->type)) {
+ F->lightweight = 0;
+ return; /* don't scan inner functions */
+ }
+
+ if (node->type == STM_WITH) {
+ F->lightweight = 0;
+ }
+
+ if (node->type == STM_TRY && node->c) {
+ F->lightweight = 0;
+ }
+
+ if (node->type == EXP_IDENTIFIER) {
+ if (!strcmp(node->string, "arguments")) {
+ F->lightweight = 0;
+ F->arguments = 1;
+ } else if (!strcmp(node->string, "eval")) {
+ /* eval may only be used as a direct function call */
+ if (!node->parent || node->parent->type != EXP_CALL || node->parent->a != node)
+ js_evalerror(J, "%s:%d: invalid use of 'eval'", J->filename, node->line);
+ F->lightweight = 0;
+ }
+ }
+
+ if (node->a) analyze(J, F, node->a);
+ if (node->b) analyze(J, F, node->b);
+ if (node->c) analyze(J, F, node->c);
+ if (node->d) analyze(J, F, node->d);
+}
+
+/* Declarations and programs */
+
+static int listlength(js_Ast *list)
+{
+ int n = 0;
+ while (list) ++n, list = list->b;
+ return n;
+}
+
+static void cparams(JF, js_Ast *list)
+{
+ F->numparams = listlength(list);
+ while (list) {
+ addlocal(J, F, list->a, 0);
+ list = list->b;
+ }
+}
+
+static void cvardecs(JF, js_Ast *node)
+{
+ if (isfun(node->type))
+ return; /* stop at inner functions */
+
+ if (node->type == EXP_VAR) {
+ if (F->lightweight)
+ addlocal(J, F, node->a, 1);
+ else
+ emitstring(J, F, OP_DEFVAR, node->a->string);
+ }
+
+ if (node->a) cvardecs(J, F, node->a);
+ if (node->b) cvardecs(J, F, node->b);
+ if (node->c) cvardecs(J, F, node->c);
+ if (node->d) cvardecs(J, F, node->d);
+}
+
+static void cfundecs(JF, js_Ast *list)
+{
+ while (list) {
+ js_Ast *stm = list->a;
+ if (stm->type == AST_FUNDEC) {
+ emitfunction(J, F, newfun(J, stm->a, stm->b, stm->c, 0));
+ emitstring(J, F, OP_INITVAR, stm->a->string);
+ }
+ list = list->b;
+ }
+}
+
+static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body)
+{
+ F->lightweight = 1;
+ F->arguments = 0;
+
+ if (F->script)
+ F->lightweight = 0;
+
+ if (body)
+ analyze(J, F, body);
+
+ cparams(J, F, params);
+
+ if (name) {
+ emit(J, F, OP_CURRENT);
+ if (F->lightweight) {
+ addlocal(J, F, name, 0);
+ emit(J, F, OP_INITLOCAL);
+ emitraw(J, F, findlocal(J, F, name->string));
+ } else {
+ emitstring(J, F, OP_INITVAR, name->string);
+ }
+ }
+
+ if (body) {
+ cvardecs(J, F, body);
+ cfundecs(J, F, body);
+ }
+
+ if (F->script) {
+ emit(J, F, OP_UNDEF);
+ cstmlist(J, F, body);
+ emit(J, F, OP_RETURN);
+ } else {
+ cstmlist(J, F, body);
+ emit(J, F, OP_UNDEF);
+ emit(J, F, OP_RETURN);
+ }
+}
+
+js_Function *jsC_compilefunction(js_State *J, js_Ast *prog)
+{
+ return newfun(J, prog->a, prog->b, prog->c, 0);
+}
+
+js_Function *jsC_compile(js_State *J, js_Ast *prog)
+{
+ return newfun(J, NULL, NULL, prog, 1);
+}
diff --git a/mujs/jscompile.h b/mujs/jscompile.h
new file mode 100644
index 0000000..a545c03
--- /dev/null
+++ b/mujs/jscompile.h
@@ -0,0 +1,155 @@
+#ifndef js_compile_h
+#define js_compile_h
+
+enum js_OpCode
+{
+ OP_POP, /* A -- */
+ OP_DUP, /* A -- A A */
+ OP_DUP2, /* A B -- A B A B */
+ OP_ROT2, /* A B -- B A */
+ OP_ROT3, /* A B C -- C A B */
+ OP_ROT4, /* A B C D -- D A B C */
+
+ OP_NUMBER_0, /* -- 0 */
+ OP_NUMBER_1, /* -- 1 */
+ OP_NUMBER_POS, /* -K- K */
+ OP_NUMBER_NEG, /* -K- -K */
+
+ OP_NUMBER, /* -N- <number> */
+ OP_STRING, /* -S- <string> */
+ OP_CLOSURE, /* -F- <closure> */
+
+ OP_NEWARRAY,
+ OP_NEWOBJECT,
+ OP_NEWREGEXP,
+
+ OP_UNDEF,
+ OP_NULL,
+ OP_TRUE,
+ OP_FALSE,
+
+ OP_THIS,
+ OP_GLOBAL,
+ OP_CURRENT, /* currently executing function object */
+
+ OP_INITLOCAL, /* <value> -K- */
+ OP_GETLOCAL, /* -K- <value> */
+ OP_SETLOCAL, /* <value> -K- <value> */
+ OP_DELLOCAL, /* -K- false */
+
+ OP_INITVAR, /* <value> -S- */
+ OP_DEFVAR, /* -S- */
+ OP_HASVAR, /* -S- ( <value> | undefined ) */
+ OP_GETVAR, /* -S- <value> */
+ OP_SETVAR, /* <value> -S- <value> */
+ OP_DELVAR, /* -S- <success> */
+
+ OP_IN, /* <name> <obj> -- <exists?> */
+
+ OP_INITPROP, /* <obj> <key> <val> -- <obj> */
+ OP_INITGETTER, /* <obj> <key> <closure> -- <obj> */
+ OP_INITSETTER, /* <obj> <key> <closure> -- <obj> */
+
+ OP_GETPROP, /* <obj> <name> -- <value> */
+ OP_GETPROP_S, /* <obj> -S- <value> */
+ OP_SETPROP, /* <obj> <name> <value> -- <value> */
+ OP_SETPROP_S, /* <obj> <value> -S- <value> */
+ OP_DELPROP, /* <obj> <name> -- <success> */
+ OP_DELPROP_S, /* <obj> -S- <success> */
+
+ OP_ITERATOR, /* <obj> -- <iobj> */
+ OP_NEXTITER, /* <iobj> -- ( <iobj> <name> true | false ) */
+
+ OP_EVAL, /* <args...> -(numargs)- <returnvalue> */
+ OP_CALL, /* <closure> <this> <args...> -(numargs)- <returnvalue> */
+ OP_NEW, /* <closure> <args...> -(numargs)- <returnvalue> */
+
+ OP_TYPEOF,
+ OP_POS,
+ OP_NEG,
+ OP_BITNOT,
+ OP_LOGNOT,
+ OP_INC, /* <x> -- ToNumber(x)+1 */
+ OP_DEC, /* <x> -- ToNumber(x)-1 */
+ OP_POSTINC, /* <x> -- ToNumber(x)+1 ToNumber(x) */
+ OP_POSTDEC, /* <x> -- ToNumber(x)-1 ToNumber(x) */
+
+ OP_MUL,
+ OP_DIV,
+ OP_MOD,
+ OP_ADD,
+ OP_SUB,
+ OP_SHL,
+ OP_SHR,
+ OP_USHR,
+ OP_LT,
+ OP_GT,
+ OP_LE,
+ OP_GE,
+ OP_EQ,
+ OP_NE,
+ OP_STRICTEQ,
+ OP_STRICTNE,
+ OP_JCASE,
+ OP_BITAND,
+ OP_BITXOR,
+ OP_BITOR,
+
+ OP_INSTANCEOF,
+
+ OP_THROW,
+
+ OP_TRY, /* -ADDR- /jump/ or -ADDR- <exception> */
+ OP_ENDTRY,
+
+ OP_CATCH, /* push scope chain with exception variable */
+ OP_ENDCATCH,
+
+ OP_WITH,
+ OP_ENDWITH,
+
+ OP_DEBUGGER,
+ OP_JUMP,
+ OP_JTRUE,
+ OP_JFALSE,
+ OP_RETURN,
+
+ OP_LINE, /* -K- */
+};
+
+struct js_Function
+{
+ const char *name;
+ int script;
+ int lightweight;
+ unsigned int arguments;
+ unsigned int numparams;
+
+ js_Instruction *code;
+ unsigned int codecap, codelen;
+
+ js_Function **funtab;
+ unsigned int funcap, funlen;
+
+ double *numtab;
+ unsigned int numcap, numlen;
+
+ const char **strtab;
+ unsigned int strcap, strlen;
+
+ const char **vartab;
+ unsigned int varcap, varlen;
+
+ const char *filename;
+ int line, lastline;
+
+ js_Function *gcnext;
+ int gcmark;
+};
+
+js_Function *jsC_compilefunction(js_State *J, js_Ast *prog);
+js_Function *jsC_compile(js_State *J, js_Ast *prog);
+const char *jsC_opcodestring(enum js_OpCode opcode);
+void jsC_dumpfunction(js_State *J, js_Function *fun);
+
+#endif
diff --git a/mujs/jsdate.c b/mujs/jsdate.c
new file mode 100644
index 0000000..cc8d9ea
--- /dev/null
+++ b/mujs/jsdate.c
@@ -0,0 +1,804 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+#include <time.h>
+
+#if defined(__unix__)
+#include <sys/time.h>
+#elif defined(_WIN32)
+#include <sys/timeb.h>
+#endif
+
+#define js_optnumber(J,I,V) (js_gettop(J) > I ? js_tonumber(J,I) : V)
+
+static double Now(void)
+{
+#if defined(__unix__)
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return floor(tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0);
+#elif defined(_WIN32)
+ struct _timeb tv;
+ _ftime(&tv);
+ return tv.time * 1000.0 + tv.millitm;
+#else
+ return time(NULL) * 1000.0;
+#endif
+}
+
+static double LocalTZA(void)
+{
+ static int once = 1;
+ static double tza = 0;
+ if (once) {
+ time_t now = time(NULL);
+ time_t utc = mktime(gmtime(&now));
+ time_t loc = mktime(localtime(&now));
+ tza = (loc - utc) * 1000;
+ once = 0;
+ }
+ return tza;
+}
+
+static double DaylightSavingTA(double t)
+{
+ return 0; // TODO
+}
+
+/* Helpers from the ECMA 262 specification */
+
+#define HoursPerDay 24.0
+#define MinutesPerDay (HoursPerDay * MinutesPerHour)
+#define MinutesPerHour 60.0
+#define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
+#define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
+#define SecondsPerMinute 60.0
+
+#define msPerDay (SecondsPerDay * msPerSecond)
+#define msPerHour (SecondsPerHour * msPerSecond)
+#define msPerMinute (SecondsPerMinute * msPerSecond)
+#define msPerSecond 1000.0
+
+static double pmod(double x, double y)
+{
+ x = fmod(x, y);
+ if (x < 0)
+ x += y;
+ return x;
+}
+
+static int Day(double t)
+{
+ return floor(t / msPerDay);
+}
+
+static double TimeWithinDay(double t)
+{
+ return pmod(t, msPerDay);
+}
+
+static int DaysInYear(int y)
+{
+ return y % 4 == 0 && (y % 100 || (y % 400 == 0)) ? 366 : 365;
+}
+
+static int DayFromYear(int y)
+{
+ return 365 * (y - 1970) +
+ floor((y - 1969) / 4.0) -
+ floor((y - 1901) / 100.0) +
+ floor((y - 1601) / 400.0);
+}
+
+static double TimeFromYear(int y)
+{
+ return DayFromYear(y) * msPerDay;
+}
+
+static int YearFromTime(double t)
+{
+ int y = floor(t / (msPerDay * 365.2425)) + 1970;
+ double t2 = TimeFromYear(y);
+ if (t2 > t)
+ --y;
+ else if (t2 + msPerDay * DaysInYear(y) <= t)
+ ++y;
+ return y;
+}
+
+static int InLeapYear(int t)
+{
+ return DaysInYear(YearFromTime(t)) == 366;
+}
+
+static int DayWithinYear(double t)
+{
+ return Day(t) - DayFromYear(YearFromTime(t));
+}
+
+static int MonthFromTime(double t)
+{
+ int day = DayWithinYear(t);
+ int leap = InLeapYear(t);
+ if (day < 31) return 0;
+ if (day < 59 + leap) return 1;
+ if (day < 90 + leap) return 2;
+ if (day < 120 + leap) return 3;
+ if (day < 151 + leap) return 4;
+ if (day < 181 + leap) return 5;
+ if (day < 212 + leap) return 6;
+ if (day < 243 + leap) return 7;
+ if (day < 273 + leap) return 8;
+ if (day < 304 + leap) return 9;
+ if (day < 334 + leap) return 10;
+ return 11;
+}
+
+static int DateFromTime(double t)
+{
+ int day = DayWithinYear(t);
+ int leap = InLeapYear(t);
+ switch (MonthFromTime(t)) {
+ case 0: return day + 1;
+ case 1: return day - 30;
+ case 2: return day - 58 - leap;
+ case 3: return day - 89 - leap;
+ case 4: return day - 119 - leap;
+ case 5: return day - 150 - leap;
+ case 6: return day - 180 - leap;
+ case 7: return day - 211 - leap;
+ case 8: return day - 242 - leap;
+ case 9: return day - 272 - leap;
+ case 10: return day - 303 - leap;
+ default : return day - 333 - leap;
+ }
+}
+
+static int WeekDay(double t)
+{
+ return pmod(Day(t) + 4, 7);
+}
+
+static double LocalTime(double utc)
+{
+ return utc + LocalTZA() + DaylightSavingTA(utc);
+}
+
+static double UTC(double loc)
+{
+ return loc - LocalTZA() - DaylightSavingTA(loc - LocalTZA());
+}
+
+static int HourFromTime(double t)
+{
+ return pmod(floor(t / msPerHour), HoursPerDay);
+}
+
+static int MinFromTime(double t)
+{
+ return pmod(floor(t / msPerMinute), MinutesPerHour);
+}
+
+static int SecFromTime(double t)
+{
+ return pmod(floor(t / msPerSecond), SecondsPerMinute);
+}
+
+static int msFromTime(double t)
+{
+ return pmod(t, msPerSecond);
+}
+
+static double MakeTime(double hour, double min, double sec, double ms)
+{
+ return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms;
+}
+
+static double MakeDay(double y, double m, double date)
+{
+ /*
+ * The following array contains the day of year for the first day of
+ * each month, where index 0 is January, and day 0 is January 1.
+ */
+ static const double firstDayOfMonth[2][12] = {
+ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
+ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
+ };
+
+ double yd, md;
+
+ y += floor(m / 12);
+ m = pmod(m, 12);
+
+ yd = floor(TimeFromYear(y) / msPerDay);
+ md = firstDayOfMonth[InLeapYear(y)][(int)m];
+
+ return yd + md + date - 1;
+}
+
+static double MakeDate(double day, double time)
+{
+ return day * msPerDay + time;
+}
+
+static double TimeClip(double t)
+{
+ if (!isfinite(t))
+ return NAN;
+ if (abs(t) > 8.64e15)
+ return NAN;
+ return t < 0 ? -floor(-t) : floor(t);
+}
+
+static int toint(const char **sp, int w, int *v)
+{
+ const char *s = *sp;
+ *v = 0;
+ while (w--) {
+ if (*s < '0' || *s > '9')
+ return 0;
+ *v = *v * 10 + (*s++ - '0');
+ }
+ *sp = s;
+ return 1;
+}
+
+static double parseDateTime(const char *s)
+{
+ int y = 1970, m = 1, d = 1, H = 0, M = 0, S = 0, ms = 0;
+ int tza = 0;
+ double t;
+
+ /* Parse ISO 8601 formatted date and time: */
+ /* YYYY("-"MM("-"DD)?)?("T"HH":"mm(":"ss("."sss)?)?("Z"|[+-]HH(":"mm)?)?)? */
+
+ if (!toint(&s, 4, &y)) return NAN;
+ if (*s == '-') {
+ s += 1;
+ if (!toint(&s, 2, &m)) return NAN;
+ if (*s == '-') {
+ s += 1;
+ if (!toint(&s, 2, &d)) return NAN;
+ }
+ }
+
+ if (*s == 'T') {
+ s += 1;
+ if (!toint(&s, 2, &H)) return NAN;
+ if (*s != ':') return NAN;
+ s += 1;
+ if (!toint(&s, 2, &M)) return NAN;
+ if (*s == ':') {
+ s += 1;
+ if (!toint(&s, 2, &S)) return NAN;
+ if (*s == '.') {
+ s += 1;
+ if (!toint(&s, 3, &ms)) return NAN;
+ }
+ }
+ if (*s == 'Z') {
+ s += 1;
+ tza = 0;
+ } else if (*s == '+' || *s == '-') {
+ int tzh = 0, tzm = 0;
+ int tzs = *s == '+' ? 1 : -1;
+ s += 1;
+ if (!toint(&s, 2, &tzh)) return NAN;
+ if (*s == ':') {
+ s += 1;
+ if (!toint(&s, 2, &tzm)) return NAN;
+ }
+ if (tzh > 23 || tzm > 59) return NAN;
+ tza = tzs * (tzh * msPerHour + tzm * msPerMinute);
+ } else {
+ tza = LocalTZA();
+ }
+ }
+
+ if (*s) return NAN;
+
+ if (m > 11) return NAN;
+ if (d < 1 || d > 31) return NAN;
+ if (H > 24 || M > 59 || S > 59) return NAN;
+ if (H == 24 && (M != 0 || S != 0 || ms != 0)) return NAN;
+
+ // TODO: DaylightSavingTA on local times
+ t = MakeDate(MakeDay(y, m-1, d), MakeTime(H, M, S, ms));
+ return t - tza;
+}
+
+/* date formatting */
+
+static char *fmtdate(char *buf, double t)
+{
+ int y = YearFromTime(t);
+ int m = MonthFromTime(t);
+ int d = DateFromTime(t);
+ if (!isfinite(t))
+ return "Invalid Date";
+ sprintf(buf, "%04d-%02d-%02d", y, m+1, d);
+ return buf;
+}
+
+static char *fmttime(char *buf, double t, double tza)
+{
+ int H = HourFromTime(t);
+ int M = MinFromTime(t);
+ int S = SecFromTime(t);
+ int ms = msFromTime(t);
+ int tzh = HourFromTime(fabs(tza));
+ int tzm = MinFromTime(fabs(tza));
+ if (!isfinite(t))
+ return "Invalid Date";
+ if (tza == 0)
+ sprintf(buf, "%02d:%02d:%02d.%03dZ", H, M, S, ms);
+ else if (tza < 0)
+ sprintf(buf, "%02d:%02d:%02d.%03d-%02d:%02d", H, M, S, ms, tzh, tzm);
+ else
+ sprintf(buf, "%02d:%02d:%02d.%03d+%02d:%02d", H, M, S, ms, tzh, tzm);
+ return buf;
+}
+
+static char *fmtdatetime(char *buf, double t, double tza)
+{
+ char dbuf[20], tbuf[20];
+ if (!isfinite(t))
+ return "Invalid Date";
+ fmtdate(dbuf, t);
+ fmttime(tbuf, t, tza);
+ sprintf(buf, "%sT%s", dbuf, tbuf);
+ return buf;
+}
+
+/* Date functions */
+
+static double js_todate(js_State *J, int idx)
+{
+ js_Object *self = js_toobject(J, idx);
+ if (self->type != JS_CDATE)
+ js_typeerror(J, "not a date");
+ return self->u.number;
+}
+
+static void js_setdate(js_State *J, int idx, double t)
+{
+ js_Object *self = js_toobject(J, idx);
+ if (self->type != JS_CDATE)
+ js_typeerror(J, "not a date");
+ self->u.number = TimeClip(t);
+ js_pushnumber(J, self->u.number);
+}
+
+static void D_parse(js_State *J)
+{
+ double t = parseDateTime(js_tostring(J, 1));
+ js_pushnumber(J, t);
+}
+
+static void D_UTC(js_State *J)
+{
+ double y, m, d, H, M, S, ms, t;
+ y = js_tonumber(J, 1);
+ if (y < 100) y += 1900;
+ m = js_tonumber(J, 2);
+ d = js_optnumber(J, 3, 1);
+ H = js_optnumber(J, 4, 0);
+ M = js_optnumber(J, 5, 0);
+ S = js_optnumber(J, 6, 0);
+ ms = js_optnumber(J, 7, 0);
+ t = MakeDate(MakeDay(y, m, d), MakeTime(H, M, S, ms));
+ t = TimeClip(t);
+ js_pushnumber(J, t);
+}
+
+static void D_now(js_State *J)
+{
+ js_pushnumber(J, Now());
+}
+
+static void jsB_Date(js_State *J)
+{
+ char buf[64];
+ js_pushstring(J, fmtdatetime(buf, LocalTime(Now()), LocalTZA()));
+}
+
+static void jsB_new_Date(js_State *J)
+{
+ unsigned int top = js_gettop(J);
+ js_Object *obj;
+ double t;
+
+ if (top == 1)
+ t = Now();
+ else if (top == 2) {
+ js_toprimitive(J, 1, JS_HNONE);
+ if (js_isstring(J, 1))
+ t = parseDateTime(js_tostring(J, 1));
+ else
+ t = TimeClip(js_tonumber(J, 1));
+ } else {
+ double y, m, d, H, M, S, ms;
+ y = js_tonumber(J, 1);
+ if (y < 100) y += 1900;
+ m = js_tonumber(J, 2);
+ d = js_optnumber(J, 3, 1);
+ H = js_optnumber(J, 4, 0);
+ M = js_optnumber(J, 5, 0);
+ S = js_optnumber(J, 6, 0);
+ ms = js_optnumber(J, 7, 0);
+ t = MakeDate(MakeDay(y, m, d), MakeTime(H, M, S, ms));
+ t = TimeClip(UTC(t));
+ }
+
+ obj = jsV_newobject(J, JS_CDATE, J->Date_prototype);
+ obj->u.number = t;
+
+ js_pushobject(J, obj);
+}
+
+static void Dp_valueOf(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, t);
+}
+
+static void Dp_toString(js_State *J)
+{
+ char buf[64];
+ double t = js_todate(J, 0);
+ js_pushstring(J, fmtdatetime(buf, LocalTime(t), LocalTZA()));
+}
+
+static void Dp_toDateString(js_State *J)
+{
+ char buf[64];
+ double t = js_todate(J, 0);
+ js_pushstring(J, fmtdate(buf, LocalTime(t)));
+}
+
+static void Dp_toTimeString(js_State *J)
+{
+ char buf[64];
+ double t = js_todate(J, 0);
+ js_pushstring(J, fmttime(buf, LocalTime(t), LocalTZA()));
+}
+
+static void Dp_toUTCString(js_State *J)
+{
+ char buf[64];
+ double t = js_todate(J, 0);
+ js_pushstring(J, fmtdatetime(buf, t, 0));
+}
+
+static void Dp_toISOString(js_State *J)
+{
+ char buf[64];
+ double t = js_todate(J, 0);
+ if (!isfinite(t))
+ js_rangeerror(J, "invalid date");
+ js_pushstring(J, fmtdatetime(buf, t, 0));
+}
+
+static void Dp_getFullYear(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, YearFromTime(LocalTime(t)));
+}
+
+static void Dp_getMonth(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, MonthFromTime(LocalTime(t)));
+}
+
+static void Dp_getDate(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, DateFromTime(LocalTime(t)));
+}
+
+static void Dp_getDay(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, WeekDay(LocalTime(t)));
+}
+
+static void Dp_getHours(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, HourFromTime(LocalTime(t)));
+}
+
+static void Dp_getMinutes(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, MinFromTime(LocalTime(t)));
+}
+
+static void Dp_getSeconds(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, SecFromTime(LocalTime(t)));
+}
+
+static void Dp_getMilliseconds(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, msFromTime(LocalTime(t)));
+}
+
+static void Dp_getUTCFullYear(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, YearFromTime(t));
+}
+
+static void Dp_getUTCMonth(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, MonthFromTime(t));
+}
+
+static void Dp_getUTCDate(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, DateFromTime(t));
+}
+
+static void Dp_getUTCDay(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, WeekDay(t));
+}
+
+static void Dp_getUTCHours(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, HourFromTime(t));
+}
+
+static void Dp_getUTCMinutes(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, MinFromTime(t));
+}
+
+static void Dp_getUTCSeconds(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, SecFromTime(t));
+}
+
+static void Dp_getUTCMilliseconds(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, msFromTime(t));
+}
+
+static void Dp_getTimezoneOffset(js_State *J)
+{
+ double t = js_todate(J, 0);
+ js_pushnumber(J, (t - LocalTime(t)) / msPerMinute);
+}
+
+static void Dp_setTime(js_State *J)
+{
+ js_setdate(J, 0, js_tonumber(J, 1));
+}
+
+static void Dp_setMilliseconds(js_State *J)
+{
+ double t = LocalTime(js_todate(J, 0));
+ double h = HourFromTime(t);
+ double m = MinFromTime(t);
+ double s = SecFromTime(t);
+ double ms = js_tonumber(J, 1);
+ js_setdate(J, 0, UTC(MakeDate(Day(t), MakeTime(h, m, s, ms))));
+}
+
+static void Dp_setSeconds(js_State *J)
+{
+ double t = LocalTime(js_todate(J, 0));
+ double h = HourFromTime(t);
+ double m = MinFromTime(t);
+ double s = js_tonumber(J, 1);
+ double ms = js_optnumber(J, 2, msFromTime(t));
+ js_setdate(J, 0, UTC(MakeDate(Day(t), MakeTime(h, m, s, ms))));
+}
+
+static void Dp_setMinutes(js_State *J)
+{
+ double t = LocalTime(js_todate(J, 0));
+ double h = HourFromTime(t);
+ double m = js_tonumber(J, 1);
+ double s = js_optnumber(J, 2, SecFromTime(t));
+ double ms = js_optnumber(J, 3, msFromTime(t));
+ js_setdate(J, 0, UTC(MakeDate(Day(t), MakeTime(h, m, s, ms))));
+}
+
+static void Dp_setHours(js_State *J)
+{
+ double t = LocalTime(js_todate(J, 0));
+ double h = js_tonumber(J, 1);
+ double m = js_optnumber(J, 2, HourFromTime(t));
+ double s = js_optnumber(J, 3, SecFromTime(t));
+ double ms = js_optnumber(J, 4, msFromTime(t));
+ js_setdate(J, 0, UTC(MakeDate(Day(t), MakeTime(h, m, s, ms))));
+}
+
+static void Dp_setDate(js_State *J)
+{
+ double t = LocalTime(js_todate(J, 0));
+ double y = YearFromTime(t);
+ double m = MonthFromTime(t);
+ double d = js_tonumber(J, 1);
+ js_setdate(J, 0, UTC(MakeDate(MakeDay(y, m, d), TimeWithinDay(t))));
+}
+
+static void Dp_setMonth(js_State *J)
+{
+ double t = LocalTime(js_todate(J, 0));
+ double y = YearFromTime(t);
+ double m = js_tonumber(J, 1);
+ double d = js_optnumber(J, 3, DateFromTime(t));
+ js_setdate(J, 0, UTC(MakeDate(MakeDay(y, m, d), TimeWithinDay(t))));
+}
+
+static void Dp_setFullYear(js_State *J)
+{
+ double t = LocalTime(js_todate(J, 0));
+ double y = js_tonumber(J, 1);
+ double m = js_optnumber(J, 2, MonthFromTime(t));
+ double d = js_optnumber(J, 3, DateFromTime(t));
+ js_setdate(J, 0, UTC(MakeDate(MakeDay(y, m, d), TimeWithinDay(t))));
+}
+
+static void Dp_setUTCMilliseconds(js_State *J)
+{
+ double t = js_todate(J, 0);
+ double h = HourFromTime(t);
+ double m = MinFromTime(t);
+ double s = SecFromTime(t);
+ double ms = js_tonumber(J, 1);
+ js_setdate(J, 0, MakeDate(Day(t), MakeTime(h, m, s, ms)));
+}
+
+static void Dp_setUTCSeconds(js_State *J)
+{
+ double t = js_todate(J, 0);
+ double h = HourFromTime(t);
+ double m = MinFromTime(t);
+ double s = js_tonumber(J, 1);
+ double ms = js_optnumber(J, 2, msFromTime(t));
+ js_setdate(J, 0, MakeDate(Day(t), MakeTime(h, m, s, ms)));
+}
+
+static void Dp_setUTCMinutes(js_State *J)
+{
+ double t = js_todate(J, 0);
+ double h = HourFromTime(t);
+ double m = js_tonumber(J, 1);
+ double s = js_optnumber(J, 2, SecFromTime(t));
+ double ms = js_optnumber(J, 3, msFromTime(t));
+ js_setdate(J, 0, MakeDate(Day(t), MakeTime(h, m, s, ms)));
+}
+
+static void Dp_setUTCHours(js_State *J)
+{
+ double t = js_todate(J, 0);
+ double h = js_tonumber(J, 1);
+ double m = js_optnumber(J, 2, HourFromTime(t));
+ double s = js_optnumber(J, 3, SecFromTime(t));
+ double ms = js_optnumber(J, 4, msFromTime(t));
+ js_setdate(J, 0, MakeDate(Day(t), MakeTime(h, m, s, ms)));
+}
+
+static void Dp_setUTCDate(js_State *J)
+{
+ double t = js_todate(J, 0);
+ double y = YearFromTime(t);
+ double m = MonthFromTime(t);
+ double d = js_tonumber(J, 1);
+ js_setdate(J, 0, MakeDate(MakeDay(y, m, d), TimeWithinDay(t)));
+}
+
+static void Dp_setUTCMonth(js_State *J)
+{
+ double t = js_todate(J, 0);
+ double y = YearFromTime(t);
+ double m = js_tonumber(J, 1);
+ double d = js_optnumber(J, 3, DateFromTime(t));
+ js_setdate(J, 0, MakeDate(MakeDay(y, m, d), TimeWithinDay(t)));
+}
+
+static void Dp_setUTCFullYear(js_State *J)
+{
+ double t = js_todate(J, 0);
+ double y = js_tonumber(J, 1);
+ double m = js_optnumber(J, 2, MonthFromTime(t));
+ double d = js_optnumber(J, 3, DateFromTime(t));
+ js_setdate(J, 0, MakeDate(MakeDay(y, m, d), TimeWithinDay(t)));
+}
+
+static void Dp_toJSON(js_State *J)
+{
+ js_copy(J, 0);
+ js_toprimitive(J, -1, JS_HNUMBER);
+ if (js_isnumber(J, -1) && !isfinite(js_tonumber(J, -1))) {
+ js_pushnull(J);
+ return;
+ }
+ js_pop(J, 1);
+
+ js_getproperty(J, 0, "toISOString");
+ if (!js_iscallable(J, -1))
+ js_typeerror(J, "Date.prototype.toJSON: this.toISOString not a function");
+ js_copy(J, 0);
+ js_call(J, 0);
+}
+
+void jsB_initdate(js_State *J)
+{
+ J->Date_prototype->u.number = 0;
+
+ js_pushobject(J, J->Date_prototype);
+ {
+ jsB_propf(J, "valueOf", Dp_valueOf, 0);
+ jsB_propf(J, "toString", Dp_toString, 0);
+ jsB_propf(J, "toDateString", Dp_toDateString, 0);
+ jsB_propf(J, "toTimeString", Dp_toTimeString, 0);
+ jsB_propf(J, "toLocaleString", Dp_toString, 0);
+ jsB_propf(J, "toLocaleDateString", Dp_toDateString, 0);
+ jsB_propf(J, "toLocaleTimeString", Dp_toTimeString, 0);
+ jsB_propf(J, "toUTCString", Dp_toUTCString, 0);
+
+ jsB_propf(J, "getTime", Dp_valueOf, 0);
+ jsB_propf(J, "getFullYear", Dp_getFullYear, 0);
+ jsB_propf(J, "getUTCFullYear", Dp_getUTCFullYear, 0);
+ jsB_propf(J, "getMonth", Dp_getMonth, 0);
+ jsB_propf(J, "getUTCMonth", Dp_getUTCMonth, 0);
+ jsB_propf(J, "getDate", Dp_getDate, 0);
+ jsB_propf(J, "getUTCDate", Dp_getUTCDate, 0);
+ jsB_propf(J, "getDay", Dp_getDay, 0);
+ jsB_propf(J, "getUTCDay", Dp_getUTCDay, 0);
+ jsB_propf(J, "getHours", Dp_getHours, 0);
+ jsB_propf(J, "getUTCHours", Dp_getUTCHours, 0);
+ jsB_propf(J, "getMinutes", Dp_getMinutes, 0);
+ jsB_propf(J, "getUTCMinutes", Dp_getUTCMinutes, 0);
+ jsB_propf(J, "getSeconds", Dp_getSeconds, 0);
+ jsB_propf(J, "getUTCSeconds", Dp_getUTCSeconds, 0);
+ jsB_propf(J, "getMilliseconds", Dp_getMilliseconds, 0);
+ jsB_propf(J, "getUTCMilliseconds", Dp_getUTCMilliseconds, 0);
+ jsB_propf(J, "getTimezoneOffset", Dp_getTimezoneOffset, 0);
+
+ jsB_propf(J, "setTime", Dp_setTime, 1);
+ jsB_propf(J, "setMilliseconds", Dp_setMilliseconds, 1);
+ jsB_propf(J, "setUTCMilliseconds", Dp_setUTCMilliseconds, 1);
+ jsB_propf(J, "setSeconds", Dp_setSeconds, 2);
+ jsB_propf(J, "setUTCSeconds", Dp_setUTCSeconds, 2);
+ jsB_propf(J, "setMinutes", Dp_setMinutes, 3);
+ jsB_propf(J, "setUTCMinutes", Dp_setUTCMinutes, 3);
+ jsB_propf(J, "setHours", Dp_setHours, 4);
+ jsB_propf(J, "setUTCHours", Dp_setUTCHours, 4);
+ jsB_propf(J, "setDate", Dp_setDate, 1);
+ jsB_propf(J, "setUTCDate", Dp_setUTCDate, 1);
+ jsB_propf(J, "setMonth", Dp_setMonth, 2);
+ jsB_propf(J, "setUTCMonth", Dp_setUTCMonth, 2);
+ jsB_propf(J, "setFullYear", Dp_setFullYear, 3);
+ jsB_propf(J, "setUTCFullYear", Dp_setUTCFullYear, 3);
+
+ /* ES5 */
+ jsB_propf(J, "toISOString", Dp_toISOString, 0);
+ jsB_propf(J, "toJSON", Dp_toJSON, 1);
+ }
+ js_newcconstructor(J, jsB_Date, jsB_new_Date, "Date", 1);
+ {
+ jsB_propf(J, "parse", D_parse, 1);
+ jsB_propf(J, "UTC", D_UTC, 7);
+
+ /* ES5 */
+ jsB_propf(J, "now", D_now, 0);
+ }
+ js_defglobal(J, "Date", JS_DONTENUM);
+}
diff --git a/mujs/jsdtoa.c b/mujs/jsdtoa.c
new file mode 100644
index 0000000..3bd9fe0
--- /dev/null
+++ b/mujs/jsdtoa.c
@@ -0,0 +1,849 @@
+/* The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "jsi.h"
+
+typedef unsigned long ulong;
+
+enum { NSIGNIF = 17 };
+
+/*
+ * first few powers of 10, enough for about 1/2 of the
+ * total space for doubles.
+ */
+static double pows10[] =
+{
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
+ 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
+ 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49,
+ 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59,
+ 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69,
+ 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79,
+ 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89,
+ 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99,
+ 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
+ 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
+ 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
+ 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
+ 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
+ 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
+};
+#define npows10 ((int)(sizeof(pows10)/sizeof(pows10[0])))
+#define pow10(x) fmtpow10(x)
+
+static double
+pow10(int n)
+{
+ double d;
+ int neg;
+
+ neg = 0;
+ if(n < 0){
+ neg = 1;
+ n = -n;
+ }
+
+ if(n < npows10)
+ d = pows10[n];
+ else{
+ d = pows10[npows10-1];
+ for(;;){
+ n -= npows10 - 1;
+ if(n < npows10){
+ d *= pows10[n];
+ break;
+ }
+ d *= pows10[npows10 - 1];
+ }
+ }
+ if(neg)
+ return 1./d;
+ return d;
+}
+
+/*
+ * add 1 to the decimal integer string a of length n.
+ * if 99999 overflows into 10000, return 1 to tell caller
+ * to move the virtual decimal point.
+ */
+static int
+xadd1(char *a, int n)
+{
+ char *b;
+ int c;
+
+ if(n < 0 || n > NSIGNIF)
+ return 0;
+ for(b = a+n-1; b >= a; b--) {
+ c = *b + 1;
+ if(c <= '9') {
+ *b = c;
+ return 0;
+ }
+ *b = '0';
+ }
+ /*
+ * need to overflow adding digit.
+ * shift number down and insert 1 at beginning.
+ * decimal is known to be 0s or we wouldn't
+ * have gotten this far. (e.g., 99999+1 => 00000)
+ */
+ a[0] = '1';
+ return 1;
+}
+
+/*
+ * subtract 1 from the decimal integer string a.
+ * if 10000 underflows into 09999, make it 99999
+ * and return 1 to tell caller to move the virtual
+ * decimal point. this way, xsub1 is inverse of xadd1.
+ */
+static int
+xsub1(char *a, int n)
+{
+ char *b;
+ int c;
+
+ if(n < 0 || n > NSIGNIF)
+ return 0;
+ for(b = a+n-1; b >= a; b--) {
+ c = *b - 1;
+ if(c >= '0') {
+ if(c == '0' && b == a) {
+ /*
+ * just zeroed the top digit; shift everyone up.
+ * decimal is known to be 9s or we wouldn't
+ * have gotten this far. (e.g., 10000-1 => 09999)
+ */
+ *b = '9';
+ return 1;
+ }
+ *b = c;
+ return 0;
+ }
+ *b = '9';
+ }
+ /*
+ * can't get here. the number a is always normalized
+ * so that it has a nonzero first digit.
+ */
+ return 0;
+}
+
+/*
+ * format exponent like sprintf(p, "e%+d", e)
+ */
+void
+js_fmtexp(char *p, int e)
+{
+ char se[9];
+ int i;
+
+ *p++ = 'e';
+ if(e < 0) {
+ *p++ = '-';
+ e = -e;
+ } else
+ *p++ = '+';
+ i = 0;
+ while(e) {
+ se[i++] = e % 10 + '0';
+ e /= 10;
+ }
+ while(i < 1)
+ se[i++] = '0';
+ while(i > 0)
+ *p++ = se[--i];
+ *p++ = '\0';
+}
+
+/*
+ * compute decimal integer m, exp such that:
+ * f = m*10^exp
+ * m is as short as possible with losing exactness
+ * assumes special cases (NaN, +Inf, -Inf) have been handled.
+ */
+void
+js_dtoa(double f, char *s, int *exp, int *neg, int *ns)
+{
+ int c, d, e2, e, ee, i, ndigit, oerrno;
+ char tmp[NSIGNIF+10];
+ double g;
+
+ oerrno = errno; /* in case strtod smashes errno */
+
+ /*
+ * make f non-negative.
+ */
+ *neg = 0;
+ if(f < 0) {
+ f = -f;
+ *neg = 1;
+ }
+
+ /*
+ * must handle zero specially.
+ */
+ if(f == 0){
+ *exp = 0;
+ s[0] = '0';
+ s[1] = '\0';
+ *ns = 1;
+ return;
+ }
+
+ /*
+ * find g,e such that f = g*10^e.
+ * guess 10-exponent using 2-exponent, then fine tune.
+ */
+ frexp(f, &e2);
+ e = (int)(e2 * .301029995664);
+ g = f * pow10(-e);
+ while(g < 1) {
+ e--;
+ g = f * pow10(-e);
+ }
+ while(g >= 10) {
+ e++;
+ g = f * pow10(-e);
+ }
+
+ /*
+ * convert NSIGNIF digits as a first approximation.
+ */
+ for(i=0; i<NSIGNIF; i++) {
+ d = (int)g;
+ s[i] = d+'0';
+ g = (g-d) * 10;
+ }
+ s[i] = 0;
+
+ /*
+ * adjust e because s is 314159... not 3.14159...
+ */
+ e -= NSIGNIF-1;
+ js_fmtexp(s+NSIGNIF, e);
+
+ /*
+ * adjust conversion until strtod(s) == f exactly.
+ */
+ for(i=0; i<10; i++) {
+ g = js_strtod(s, NULL);
+ if(f > g) {
+ if(xadd1(s, NSIGNIF)) {
+ /* gained a digit */
+ e--;
+ js_fmtexp(s+NSIGNIF, e);
+ }
+ continue;
+ }
+ if(f < g) {
+ if(xsub1(s, NSIGNIF)) {
+ /* lost a digit */
+ e++;
+ js_fmtexp(s+NSIGNIF, e);
+ }
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * play with the decimal to try to simplify.
+ */
+
+ /*
+ * bump last few digits up to 9 if we can
+ */
+ for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
+ c = s[i];
+ if(c != '9') {
+ s[i] = '9';
+ g = js_strtod(s, NULL);
+ if(g != f) {
+ s[i] = c;
+ break;
+ }
+ }
+ }
+
+ /*
+ * add 1 in hopes of turning 9s to 0s
+ */
+ if(s[NSIGNIF-1] == '9') {
+ strcpy(tmp, s);
+ ee = e;
+ if(xadd1(tmp, NSIGNIF)) {
+ ee--;
+ js_fmtexp(tmp+NSIGNIF, ee);
+ }
+ g = js_strtod(tmp, NULL);
+ if(g == f) {
+ strcpy(s, tmp);
+ e = ee;
+ }
+ }
+
+ /*
+ * bump last few digits down to 0 as we can.
+ */
+ for(i=NSIGNIF-1; i>=NSIGNIF-3; i--) {
+ c = s[i];
+ if(c != '0') {
+ s[i] = '0';
+ g = js_strtod(s, NULL);
+ if(g != f) {
+ s[i] = c;
+ break;
+ }
+ }
+ }
+
+ /*
+ * remove trailing zeros.
+ */
+ ndigit = NSIGNIF;
+ while(ndigit > 1 && s[ndigit-1] == '0'){
+ e++;
+ --ndigit;
+ }
+ s[ndigit] = 0;
+ *exp = e;
+ *ns = ndigit;
+ errno = oerrno;
+}
+
+static ulong
+umuldiv(ulong a, ulong b, ulong c)
+{
+ double d;
+
+ d = ((double)a * (double)b) / (double)c;
+ if(d >= 4294967295.)
+ d = 4294967295.;
+ return (ulong)d;
+}
+
+/*
+ * This routine will convert to arbitrary precision
+ * floating point entirely in multi-precision fixed.
+ * The answer is the closest floating point number to
+ * the given decimal number. Exactly half way are
+ * rounded ala ieee rules.
+ * Method is to scale input decimal between .500 and .999...
+ * with external power of 2, then binary search for the
+ * closest mantissa to this decimal number.
+ * Nmant is is the required precision. (53 for ieee dp)
+ * Nbits is the max number of bits/word. (must be <= 28)
+ * Prec is calculated - the number of words of fixed mantissa.
+ */
+enum
+{
+ Nbits = 28, /* bits safely represented in a ulong */
+ Nmant = 53, /* bits of precision required */
+ Prec = (Nmant+Nbits+1)/Nbits, /* words of Nbits each to represent mantissa */
+ Sigbit = 1<<(Prec*Nbits-Nmant), /* first significant bit of Prec-th word */
+ Ndig = 1500,
+ One = (ulong)(1<<Nbits),
+ Half = (ulong)(One>>1),
+ Maxe = 310,
+
+ Fsign = 1<<0, /* found - */
+ Fesign = 1<<1, /* found e- */
+ Fdpoint = 1<<2, /* found . */
+
+ S0 = 0, /* _ _S0 +S1 #S2 .S3 */
+ S1, /* _+ #S2 .S3 */
+ S2, /* _+# #S2 .S4 eS5 */
+ S3, /* _+. #S4 */
+ S4, /* _+#.# #S4 eS5 */
+ S5, /* _+#.#e +S6 #S7 */
+ S6, /* _+#.#e+ #S7 */
+ S7 /* _+#.#e+# #S7 */
+};
+
+static int xcmp(char*, char*);
+static int fpcmp(char*, ulong*);
+static void frnorm(ulong*);
+static void divascii(char*, int*, int*, int*);
+static void mulascii(char*, int*, int*, int*);
+
+typedef struct Tab Tab;
+struct Tab
+{
+ int bp;
+ int siz;
+ char* cmp;
+};
+
+double
+js_strtod(const char *as, char **aas)
+{
+ int na, ex, dp, bp, c, i, flag, state;
+ ulong low[Prec], hig[Prec], mid[Prec];
+ double d;
+ char *s, a[Ndig];
+
+ flag = 0; /* Fsign, Fesign, Fdpoint */
+ na = 0; /* number of digits of a[] */
+ dp = 0; /* na of decimal point */
+ ex = 0; /* exonent */
+
+ state = S0;
+ for(s=(char*)as;; s++) {
+ c = *s;
+ if(c >= '0' && c <= '9') {
+ switch(state) {
+ case S0:
+ case S1:
+ case S2:
+ state = S2;
+ break;
+ case S3:
+ case S4:
+ state = S4;
+ break;
+
+ case S5:
+ case S6:
+ case S7:
+ state = S7;
+ ex = ex*10 + (c-'0');
+ continue;
+ }
+ if(na == 0 && c == '0') {
+ dp--;
+ continue;
+ }
+ if(na < Ndig-50)
+ a[na++] = c;
+ continue;
+ }
+ switch(c) {
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ if(state == S0)
+ continue;
+ break;
+ case '-':
+ if(state == S0)
+ flag |= Fsign;
+ else
+ flag |= Fesign;
+ /* fall through */
+ case '+':
+ if(state == S0)
+ state = S1;
+ else
+ if(state == S5)
+ state = S6;
+ else
+ break; /* syntax */
+ continue;
+ case '.':
+ flag |= Fdpoint;
+ dp = na;
+ if(state == S0 || state == S1) {
+ state = S3;
+ continue;
+ }
+ if(state == S2) {
+ state = S4;
+ continue;
+ }
+ break;
+ case 'e':
+ case 'E':
+ if(state == S2 || state == S4) {
+ state = S5;
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+
+ /*
+ * clean up return char-pointer
+ */
+ switch(state) {
+ case S0:
+ if(xcmp(s, "nan") == 0) {
+ if(aas != NULL)
+ *aas = s+3;
+ goto retnan;
+ }
+ /* fall through */
+ case S1:
+ if(xcmp(s, "infinity") == 0) {
+ if(aas != NULL)
+ *aas = s+8;
+ goto retinf;
+ }
+ if(xcmp(s, "inf") == 0) {
+ if(aas != NULL)
+ *aas = s+3;
+ goto retinf;
+ }
+ /* fall through */
+ case S3:
+ if(aas != NULL)
+ *aas = (char*)as;
+ goto ret0; /* no digits found */
+ case S6:
+ s--; /* back over +- */
+ /* fall through */
+ case S5:
+ s--; /* back over e */
+ break;
+ }
+ if(aas != NULL)
+ *aas = s;
+
+ if(flag & Fdpoint)
+ while(na > 0 && a[na-1] == '0')
+ na--;
+ if(na == 0)
+ goto ret0; /* zero */
+ a[na] = 0;
+ if(!(flag & Fdpoint))
+ dp = na;
+ if(flag & Fesign)
+ ex = -ex;
+ dp += ex;
+ if(dp < -Maxe){
+ errno = ERANGE;
+ goto ret0; /* underflow by exp */
+ } else
+ if(dp > +Maxe)
+ goto retinf; /* overflow by exp */
+
+ /*
+ * normalize the decimal ascii number
+ * to range .[5-9][0-9]* e0
+ */
+ bp = 0; /* binary exponent */
+ while(dp > 0)
+ divascii(a, &na, &dp, &bp);
+ while(dp < 0 || a[0] < '5')
+ mulascii(a, &na, &dp, &bp);
+
+ /* close approx by naive conversion */
+ mid[0] = 0;
+ mid[1] = 1;
+ for(i=0; (c=a[i]) != '\0'; i++) {
+ mid[0] = mid[0]*10 + (c-'0');
+ mid[1] = mid[1]*10;
+ if(i >= 8)
+ break;
+ }
+ low[0] = umuldiv(mid[0], One, mid[1]);
+ hig[0] = umuldiv(mid[0]+1, One, mid[1]);
+ for(i=1; i<Prec; i++) {
+ low[i] = 0;
+ hig[i] = One-1;
+ }
+
+ /* binary search for closest mantissa */
+ for(;;) {
+ /* mid = (hig + low) / 2 */
+ c = 0;
+ for(i=0; i<Prec; i++) {
+ mid[i] = hig[i] + low[i];
+ if(c)
+ mid[i] += One;
+ c = mid[i] & 1;
+ mid[i] >>= 1;
+ }
+ frnorm(mid);
+
+ /* compare */
+ c = fpcmp(a, mid);
+ if(c > 0) {
+ c = 1;
+ for(i=0; i<Prec; i++)
+ if(low[i] != mid[i]) {
+ c = 0;
+ low[i] = mid[i];
+ }
+ if(c)
+ break; /* between mid and hig */
+ continue;
+ }
+ if(c < 0) {
+ for(i=0; i<Prec; i++)
+ hig[i] = mid[i];
+ continue;
+ }
+
+ /* only hard part is if even/odd roundings wants to go up */
+ c = mid[Prec-1] & (Sigbit-1);
+ if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0)
+ mid[Prec-1] -= c;
+ break; /* exactly mid */
+ }
+
+ /* normal rounding applies */
+ c = mid[Prec-1] & (Sigbit-1);
+ mid[Prec-1] -= c;
+ if(c >= Sigbit/2) {
+ mid[Prec-1] += Sigbit;
+ frnorm(mid);
+ }
+ goto out;
+
+ret0:
+ if(flag & Fsign)
+ return -0.0;
+ return 0;
+
+retnan:
+ return NAN;
+
+retinf:
+ /*
+ * Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */
+ errno = ERANGE;
+ if(flag & Fsign)
+ return -HUGE_VAL;
+ return HUGE_VAL;
+
+out:
+ d = 0;
+ for(i=0; i<Prec; i++)
+ d = d*One + mid[i];
+ if(flag & Fsign)
+ d = -d;
+ d = ldexp(d, bp - Prec*Nbits);
+ if(d == 0){ /* underflow */
+ errno = ERANGE;
+ }
+ return d;
+}
+
+static void
+frnorm(ulong *f)
+{
+ int i, c;
+
+ c = 0;
+ for(i=Prec-1; i>0; i--) {
+ f[i] += c;
+ c = f[i] >> Nbits;
+ f[i] &= One-1;
+ }
+ f[0] += c;
+}
+
+static int
+fpcmp(char *a, ulong* f)
+{
+ ulong tf[Prec];
+ int i, d, c;
+
+ for(i=0; i<Prec; i++)
+ tf[i] = f[i];
+
+ for(;;) {
+ /* tf *= 10 */
+ for(i=0; i<Prec; i++)
+ tf[i] = tf[i]*10;
+ frnorm(tf);
+ d = (tf[0] >> Nbits) + '0';
+ tf[0] &= One-1;
+
+ /* compare next digit */
+ c = *a;
+ if(c == 0) {
+ if('0' < d)
+ return -1;
+ if(tf[0] != 0)
+ goto cont;
+ for(i=1; i<Prec; i++)
+ if(tf[i] != 0)
+ goto cont;
+ return 0;
+ }
+ if(c > d)
+ return +1;
+ if(c < d)
+ return -1;
+ a++;
+ cont:;
+ }
+}
+
+static void
+divby(char *a, int *na, int b)
+{
+ int n, c;
+ char *p;
+
+ p = a;
+ n = 0;
+ while(n>>b == 0) {
+ c = *a++;
+ if(c == 0) {
+ while(n) {
+ c = n*10;
+ if(c>>b)
+ break;
+ n = c;
+ }
+ goto xx;
+ }
+ n = n*10 + c-'0';
+ (*na)--;
+ }
+ for(;;) {
+ c = n>>b;
+ n -= c<<b;
+ *p++ = c + '0';
+ c = *a++;
+ if(c == 0)
+ break;
+ n = n*10 + c-'0';
+ }
+ (*na)++;
+xx:
+ while(n) {
+ n = n*10;
+ c = n>>b;
+ n -= c<<b;
+ *p++ = c + '0';
+ (*na)++;
+ }
+ *p = 0;
+}
+
+static Tab tab1[] =
+{
+ { 1, 0, "" },
+ { 3, 1, "7" },
+ { 6, 2, "63" },
+ { 9, 3, "511" },
+ { 13, 4, "8191" },
+ { 16, 5, "65535" },
+ { 19, 6, "524287" },
+ { 23, 7, "8388607" },
+ { 26, 8, "67108863" },
+ { 27, 9, "134217727" },
+};
+
+static void
+divascii(char *a, int *na, int *dp, int *bp)
+{
+ int b, d;
+ Tab *t;
+
+ d = *dp;
+ if(d >= (int)(nelem(tab1)))
+ d = (int)(nelem(tab1))-1;
+ t = tab1 + d;
+ b = t->bp;
+ if(memcmp(a, t->cmp, t->siz) > 0)
+ d--;
+ *dp -= d;
+ *bp += b;
+ divby(a, na, b);
+}
+
+static void
+mulby(char *a, char *p, char *q, int b)
+{
+ int n, c;
+
+ n = 0;
+ *p = 0;
+ for(;;) {
+ q--;
+ if(q < a)
+ break;
+ c = *q - '0';
+ c = (c<<b) + n;
+ n = c/10;
+ c -= n*10;
+ p--;
+ *p = c + '0';
+ }
+ while(n) {
+ c = n;
+ n = c/10;
+ c -= n*10;
+ p--;
+ *p = c + '0';
+ }
+}
+
+static Tab tab2[] =
+{
+ { 1, 1, "" }, /* dp = 0-0 */
+ { 3, 3, "125" },
+ { 6, 5, "15625" },
+ { 9, 7, "1953125" },
+ { 13, 10, "1220703125" },
+ { 16, 12, "152587890625" },
+ { 19, 14, "19073486328125" },
+ { 23, 17, "11920928955078125" },
+ { 26, 19, "1490116119384765625" },
+ { 27, 19, "7450580596923828125" }, /* dp 8-9 */
+};
+
+static void
+mulascii(char *a, int *na, int *dp, int *bp)
+{
+ char *p;
+ int d, b;
+ Tab *t;
+
+ d = -*dp;
+ if(d >= (int)(nelem(tab2)))
+ d = (int)(nelem(tab2))-1;
+ t = tab2 + d;
+ b = t->bp;
+ if(memcmp(a, t->cmp, t->siz) < 0)
+ d--;
+ p = a + *na;
+ *bp -= b;
+ *dp += d;
+ *na += d;
+ mulby(a, p+d, p, b);
+}
+
+static int
+xcmp(char *a, char *b)
+{
+ int c1, c2;
+
+ while((c1 = *b++) != '\0') {
+ c2 = *a++;
+ if(c2 >= 'A' && c2 <= 'Z')
+ c2 = c2 - 'A' + 'a';
+ if(c1 != c2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/mujs/jsdump.c b/mujs/jsdump.c
new file mode 100644
index 0000000..7c0ec24
--- /dev/null
+++ b/mujs/jsdump.c
@@ -0,0 +1,852 @@
+#include "jsi.h"
+#include "jsparse.h"
+#include "jscompile.h"
+#include "jsvalue.h"
+
+#include "utf.h"
+
+#include <assert.h>
+
+static const char *astname[] = {
+#include "astnames.h"
+};
+
+static const char *opname[] = {
+#include "opnames.h"
+};
+
+const char *jsP_aststring(enum js_AstType type)
+{
+ if (type < nelem(astname))
+ return astname[type];
+ return "<unknown>";
+}
+
+const char *jsC_opcodestring(enum js_OpCode opcode)
+{
+ if (opcode < nelem(opname))
+ return opname[opcode];
+ return "<unknown>";
+}
+
+static int prec(enum js_AstType type)
+{
+ switch (type) {
+ case EXP_IDENTIFIER:
+ case EXP_NUMBER:
+ case EXP_STRING:
+ case EXP_REGEXP:
+ case EXP_UNDEF:
+ case EXP_NULL:
+ case EXP_TRUE:
+ case EXP_FALSE:
+ case EXP_THIS:
+ case EXP_ARRAY:
+ case EXP_OBJECT:
+ return 170;
+
+ case EXP_FUN:
+ case EXP_INDEX:
+ case EXP_MEMBER:
+ case EXP_CALL:
+ case EXP_NEW:
+ return 160;
+
+ case EXP_POSTINC:
+ case EXP_POSTDEC:
+ return 150;
+
+ case EXP_DELETE:
+ case EXP_VOID:
+ case EXP_TYPEOF:
+ case EXP_PREINC:
+ case EXP_PREDEC:
+ case EXP_POS:
+ case EXP_NEG:
+ case EXP_BITNOT:
+ case EXP_LOGNOT:
+ return 140;
+
+ case EXP_MOD:
+ case EXP_DIV:
+ case EXP_MUL:
+ return 130;
+
+ case EXP_SUB:
+ case EXP_ADD:
+ return 120;
+
+ case EXP_USHR:
+ case EXP_SHR:
+ case EXP_SHL:
+ return 110;
+
+ case EXP_IN:
+ case EXP_INSTANCEOF:
+ case EXP_GE:
+ case EXP_LE:
+ case EXP_GT:
+ case EXP_LT:
+ return 100;
+
+ case EXP_STRICTNE:
+ case EXP_STRICTEQ:
+ case EXP_NE:
+ case EXP_EQ:
+ return 90;
+
+ case EXP_BITAND: return 80;
+ case EXP_BITXOR: return 70;
+ case EXP_BITOR: return 60;
+ case EXP_LOGAND: return 50;
+ case EXP_LOGOR: return 40;
+
+ case EXP_COND:
+ return 30;
+
+ case EXP_ASS:
+ case EXP_ASS_MUL:
+ case EXP_ASS_DIV:
+ case EXP_ASS_MOD:
+ case EXP_ASS_ADD:
+ case EXP_ASS_SUB:
+ case EXP_ASS_SHL:
+ case EXP_ASS_SHR:
+ case EXP_ASS_USHR:
+ case EXP_ASS_BITAND:
+ case EXP_ASS_BITXOR:
+ case EXP_ASS_BITOR:
+ return 20;
+
+#define COMMA 15
+
+ case EXP_COMMA:
+ return 10;
+
+ default:
+ return 0;
+ }
+}
+
+static void pc(int c)
+{
+ putchar(c);
+}
+
+static void ps(const char *s)
+{
+ fputs(s, stdout);
+}
+
+static void in(int d)
+{
+ while (d-- > 0)
+ putchar('\t');
+}
+
+static void nl(void)
+{
+ putchar('\n');
+}
+
+/* Pretty-printed Javascript syntax */
+
+static void pstmlist(int d, js_Ast *list);
+static void pexpi(int d, int i, js_Ast *exp);
+static void pstm(int d, js_Ast *stm);
+static void slist(int d, js_Ast *list);
+static void sblock(int d, js_Ast *list);
+
+static void pargs(int d, js_Ast *list)
+{
+ while (list) {
+ assert(list->type == AST_LIST);
+ pexpi(d, COMMA, list->a);
+ list = list->b;
+ if (list)
+ ps(", ");
+ }
+}
+
+static void parray(int d, js_Ast *list)
+{
+ ps("[");
+ while (list) {
+ assert(list->type == AST_LIST);
+ pexpi(d, COMMA, list->a);
+ list = list->b;
+ if (list)
+ ps(", ");
+ }
+ ps("]");
+}
+
+static void pobject(int d, js_Ast *list)
+{
+ ps("{");
+ while (list) {
+ js_Ast *kv = list->a;
+ assert(list->type == AST_LIST);
+ switch (kv->type) {
+ case EXP_PROP_VAL:
+ pexpi(d, COMMA, kv->a);
+ ps(": ");
+ pexpi(d, COMMA, kv->b);
+ break;
+ case EXP_PROP_GET:
+ ps("get ");
+ pexpi(d, COMMA, kv->a);
+ ps("() {\n");
+ pstmlist(d, kv->c);
+ in(d); ps("}");
+ break;
+ case EXP_PROP_SET:
+ ps("set ");
+ pexpi(d, COMMA, kv->a);
+ ps("(");
+ pargs(d, kv->b);
+ ps(") {\n");
+ pstmlist(d, kv->c);
+ in(d); ps("}");
+ break;
+ }
+ list = list->b;
+ if (list)
+ ps(", ");
+ }
+ ps("}");
+}
+
+static void pstr(const char *s)
+{
+ static const char *HEX = "0123456789ABCDEF";
+ Rune c;
+ pc('"');
+ while (*s) {
+ s += chartorune(&c, s);
+ switch (c) {
+ case '"': ps("\\\""); break;
+ case '\\': ps("\\\\"); break;
+ case '\b': ps("\\b"); break;
+ case '\f': ps("\\f"); break;
+ case '\n': ps("\\n"); break;
+ case '\r': ps("\\r"); break;
+ case '\t': ps("\\t"); break;
+ default:
+ if (c < ' ' || c > 127) {
+ ps("\\u");
+ pc(HEX[(c>>12)&15]);
+ pc(HEX[(c>>8)&15]);
+ pc(HEX[(c>>4)&15]);
+ pc(HEX[c&15]);
+ } else {
+ pc(c); break;
+ }
+ }
+ }
+ pc('"');
+}
+
+static void pregexp(const char *prog, int flags)
+{
+ pc('/');
+ ps(prog);
+ pc('/');
+ if (flags & JS_REGEXP_G) pc('g');
+ if (flags & JS_REGEXP_I) pc('i');
+ if (flags & JS_REGEXP_M) pc('m');
+}
+
+static void pbin(int d, int p, js_Ast *exp, const char *op)
+{
+ pexpi(d, p, exp->a);
+ ps(op);
+ pexpi(d, p, exp->b);
+}
+
+static void puna(int d, int p, js_Ast *exp, const char *pre, const char *suf)
+{
+ ps(pre);
+ pexpi(d, p, exp->a);
+ ps(suf);
+}
+
+static void pexpi(int d, int p, js_Ast *exp)
+{
+ int tp = prec(exp->type);
+ int paren = 0;
+ if (tp < p) {
+ pc('(');
+ paren = 1;
+ }
+ p = tp;
+
+ switch (exp->type) {
+ case AST_IDENTIFIER: ps(exp->string); break;
+ case EXP_IDENTIFIER: ps(exp->string); break;
+ case EXP_NUMBER: printf("%.9g", exp->number); break;
+ case EXP_STRING: pstr(exp->string); break;
+ case EXP_REGEXP: pregexp(exp->string, exp->number); break;
+
+ case EXP_UNDEF: break;
+ case EXP_NULL: ps("null"); break;
+ case EXP_TRUE: ps("true"); break;
+ case EXP_FALSE: ps("false"); break;
+ case EXP_THIS: ps("this"); break;
+
+ case EXP_OBJECT: pobject(d, exp->a); break;
+ case EXP_ARRAY: parray(d, exp->a); break;
+
+ case EXP_DELETE: puna(d, p, exp, "delete ", ""); break;
+ case EXP_VOID: puna(d, p, exp, "void ", ""); break;
+ case EXP_TYPEOF: puna(d, p, exp, "typeof ", ""); break;
+ case EXP_PREINC: puna(d, p, exp, "++", ""); break;
+ case EXP_PREDEC: puna(d, p, exp, "--", ""); break;
+ case EXP_POSTINC: puna(d, p, exp, "", "++"); break;
+ case EXP_POSTDEC: puna(d, p, exp, "", "--"); break;
+ case EXP_POS: puna(d, p, exp, "+", ""); break;
+ case EXP_NEG: puna(d, p, exp, "-", ""); break;
+ case EXP_BITNOT: puna(d, p, exp, "~", ""); break;
+ case EXP_LOGNOT: puna(d, p, exp, "!", ""); break;
+
+ case EXP_LOGOR: pbin(d, p, exp, " || "); break;
+ case EXP_LOGAND: pbin(d, p, exp, " && "); break;
+ case EXP_BITOR: pbin(d, p, exp, " | "); break;
+ case EXP_BITXOR: pbin(d, p, exp, " ^ "); break;
+ case EXP_BITAND: pbin(d, p, exp, " & "); break;
+ case EXP_EQ: pbin(d, p, exp, " == "); break;
+ case EXP_NE: pbin(d, p, exp, " != "); break;
+ case EXP_STRICTEQ: pbin(d, p, exp, " === "); break;
+ case EXP_STRICTNE: pbin(d, p, exp, " !== "); break;
+ case EXP_LT: pbin(d, p, exp, " < "); break;
+ case EXP_GT: pbin(d, p, exp, " > "); break;
+ case EXP_LE: pbin(d, p, exp, " <= "); break;
+ case EXP_GE: pbin(d, p, exp, " >= "); break;
+ case EXP_INSTANCEOF: pbin(d, p, exp, " instanceof "); break;
+ case EXP_IN: pbin(d, p, exp, " in "); break;
+ case EXP_SHL: pbin(d, p, exp, " << "); break;
+ case EXP_SHR: pbin(d, p, exp, " >> "); break;
+ case EXP_USHR: pbin(d, p, exp, " >>> "); break;
+ case EXP_ADD: pbin(d, p, exp, " + "); break;
+ case EXP_SUB: pbin(d, p, exp, " - "); break;
+ case EXP_MUL: pbin(d, p, exp, " * "); break;
+ case EXP_DIV: pbin(d, p, exp, " / "); break;
+ case EXP_MOD: pbin(d, p, exp, " % "); break;
+ case EXP_ASS: pbin(d, p, exp, " = "); break;
+ case EXP_ASS_MUL: pbin(d, p, exp, " *= "); break;
+ case EXP_ASS_DIV: pbin(d, p, exp, " /= "); break;
+ case EXP_ASS_MOD: pbin(d, p, exp, " %= "); break;
+ case EXP_ASS_ADD: pbin(d, p, exp, " += "); break;
+ case EXP_ASS_SUB: pbin(d, p, exp, " -= "); break;
+ case EXP_ASS_SHL: pbin(d, p, exp, " <<= "); break;
+ case EXP_ASS_SHR: pbin(d, p, exp, " >>= "); break;
+ case EXP_ASS_USHR: pbin(d, p, exp, " >>>= "); break;
+ case EXP_ASS_BITAND: pbin(d, p, exp, " &= "); break;
+ case EXP_ASS_BITXOR: pbin(d, p, exp, " ^= "); break;
+ case EXP_ASS_BITOR: pbin(d, p, exp, " |= "); break;
+
+ case EXP_COMMA: pbin(d, p, exp, ", "); break;
+
+ case EXP_COND:
+ pexpi(d, p, exp->a);
+ ps(" ? ");
+ pexpi(d, p, exp->b);
+ ps(" : ");
+ pexpi(d, p, exp->c);
+ break;
+
+ case EXP_INDEX:
+ pexpi(d, p, exp->a);
+ pc('[');
+ pexpi(d, 0, exp->b);
+ pc(']');
+ break;
+
+ case EXP_MEMBER:
+ pexpi(d, p, exp->a);
+ pc('.');
+ pexpi(d, 0, exp->b);
+ break;
+
+ case EXP_CALL:
+ pexpi(d, p, exp->a);
+ pc('(');
+ pargs(d, exp->b);
+ pc(')');
+ break;
+
+ case EXP_NEW:
+ ps("new ");
+ pexpi(d, p, exp->a);
+ pc('(');
+ pargs(d, exp->b);
+ pc(')');
+ break;
+
+ case EXP_FUN:
+ if (p == 0) pc('(');
+ ps("function ");
+ if (exp->a) pexpi(d, 0, exp->a);
+ pc('(');
+ pargs(d, exp->b);
+ ps(") {\n");
+ pstmlist(d, exp->c);
+ in(d); pc('}');
+ if (p == 0) pc(')');
+ break;
+
+ default:
+ ps("<UNKNOWN>");
+ break;
+ }
+
+ if (paren) pc(')');
+}
+
+static void pexp(int d, js_Ast *exp)
+{
+ pexpi(d, 0, exp);
+}
+
+static void pvar(int d, js_Ast *var)
+{
+ assert(var->type == EXP_VAR);
+ pexp(d, var->a);
+ if (var->b) {
+ ps(" = ");
+ pexp(d, var->b);
+ }
+}
+
+static void pvarlist(int d, js_Ast *list)
+{
+ while (list) {
+ assert(list->type == AST_LIST);
+ pvar(d, list->a);
+ list = list->b;
+ if (list)
+ ps(", ");
+ }
+}
+
+static void pblock(int d, js_Ast *block)
+{
+ assert(block->type == STM_BLOCK);
+ ps(" {\n");
+ pstmlist(d, block->a);
+ in(d); pc('}');
+}
+
+static void pstmh(int d, js_Ast *stm)
+{
+ if (stm->type == STM_BLOCK)
+ pblock(d, stm);
+ else {
+ nl();
+ pstm(d+1, stm);
+ }
+}
+
+static void pcaselist(int d, js_Ast *list)
+{
+ while (list) {
+ js_Ast *stm = list->a;
+ if (stm->type == STM_CASE) {
+ in(d); ps("case "); pexp(d, stm->a); ps(":\n");
+ pstmlist(d, stm->b);
+ }
+ if (stm->type == STM_DEFAULT) {
+ in(d); ps("default:\n");
+ pstmlist(d, stm->a);
+ }
+ list = list->b;
+ }
+}
+
+static void pstm(int d, js_Ast *stm)
+{
+ if (stm->type == STM_BLOCK) {
+ pblock(d, stm);
+ return;
+ }
+
+ in(d);
+
+ switch (stm->type) {
+ case AST_FUNDEC:
+ ps("function ");
+ pexp(d, stm->a);
+ pc('(');
+ pargs(d, stm->b);
+ ps(") {\n");
+ pstmlist(d, stm->c);
+ in(d); ps("}");
+ break;
+
+ case STM_EMPTY:
+ pc(';');
+ break;
+
+ case STM_VAR:
+ ps("var ");
+ pvarlist(d, stm->a);
+ ps(";");
+ break;
+
+ case STM_IF:
+ ps("if ("); pexp(d, stm->a); ps(")");
+ pstmh(d, stm->b);
+ if (stm->c) {
+ nl(); in(d); ps("else");
+ pstmh(d, stm->c);
+ }
+ break;
+
+ case STM_DO:
+ ps("do");
+ pstmh(d, stm->a);
+ nl();
+ in(d); ps("while ("); pexp(d, stm->b); ps(");");
+ break;
+
+ case STM_WHILE:
+ ps("while ("); pexp(d, stm->a); ps(")");
+ pstmh(d, stm->b);
+ break;
+
+ case STM_FOR:
+ ps("for (");
+ pexp(d, stm->a); ps("; ");
+ pexp(d, stm->b); ps("; ");
+ pexp(d, stm->c); ps(")");
+ pstmh(d, stm->d);
+ break;
+ case STM_FOR_VAR:
+ ps("for (var ");
+ pvarlist(d, stm->a); ps("; ");
+ pexp(d, stm->b); ps("; ");
+ pexp(d, stm->c); ps(")");
+ pstmh(d, stm->d);
+ break;
+ case STM_FOR_IN:
+ ps("for (");
+ pexp(d, stm->a); ps(" in ");
+ pexp(d, stm->b); ps(")");
+ pstmh(d, stm->c);
+ break;
+ case STM_FOR_IN_VAR:
+ ps("for (var ");
+ pvarlist(d, stm->a); ps(" in ");
+ pexp(d, stm->b); ps(")");
+ pstmh(d, stm->c);
+ break;
+
+ case STM_CONTINUE:
+ if (stm->a) {
+ ps("continue "); pexp(d, stm->a); ps(";");
+ } else {
+ ps("continue;");
+ }
+ break;
+
+ case STM_BREAK:
+ if (stm->a) {
+ ps("break "); pexp(d, stm->a); ps(";");
+ } else {
+ ps("break;");
+ }
+ break;
+
+ case STM_RETURN:
+ if (stm->a) {
+ ps("return "); pexp(d, stm->a); ps(";");
+ } else {
+ ps("return;");
+ }
+ break;
+
+ case STM_WITH:
+ ps("with ("); pexp(d, stm->a); ps(")");
+ pstmh(d, stm->b);
+ break;
+
+ case STM_SWITCH:
+ ps("switch (");
+ pexp(d, stm->a);
+ ps(") {\n");
+ pcaselist(d, stm->b);
+ in(d); ps("}");
+ break;
+
+ case STM_THROW:
+ ps("throw "); pexp(d, stm->a); ps(";");
+ break;
+
+ case STM_TRY:
+ ps("try");
+ pstmh(d, stm->a);
+ if (stm->b && stm->c) {
+ nl(); in(d); ps("catch ("); pexp(d, stm->b); ps(")");
+ pstmh(d, stm->c);
+ }
+ if (stm->d) {
+ nl(); in(d); ps("finally");
+ pstmh(d, stm->d);
+ }
+ break;
+
+ case STM_LABEL:
+ pexp(d, stm->a); ps(": "); pstm(d, stm->b);
+ break;
+
+ case STM_DEBUGGER:
+ ps("debugger;");
+ break;
+
+ default:
+ pexp(d, stm); pc(';');
+ }
+}
+
+static void pstmlist(int d, js_Ast *list)
+{
+ while (list) {
+ assert(list->type == AST_LIST);
+ pstm(d+1, list->a);
+ nl();
+ list = list->b;
+ }
+}
+
+void jsP_dumpsyntax(js_State *J, js_Ast *prog)
+{
+ if (prog->type == AST_LIST)
+ pstmlist(-1, prog);
+ else {
+ pstm(0, prog);
+ nl();
+ }
+}
+
+/* S-expression list representation */
+
+static void snode(int d, js_Ast *node)
+{
+ void (*afun)(int,js_Ast*) = snode;
+ void (*bfun)(int,js_Ast*) = snode;
+ void (*cfun)(int,js_Ast*) = snode;
+ void (*dfun)(int,js_Ast*) = snode;
+
+ if (!node) {
+ return;
+ }
+
+ if (node->type == AST_LIST) {
+ slist(d, node);
+ return;
+ }
+
+ pc('(');
+ ps(astname[node->type]);
+ switch (node->type) {
+ case AST_IDENTIFIER: pc(' '); ps(node->string); break;
+ case EXP_IDENTIFIER: pc(' '); ps(node->string); break;
+ case EXP_STRING: pc(' '); pstr(node->string); break;
+ case EXP_REGEXP: pc(' '); pregexp(node->string, node->number); break;
+ case EXP_NUMBER: printf(" %.9g", node->number); break;
+ case STM_BLOCK: afun = sblock; break;
+ case AST_FUNDEC: case EXP_FUN: cfun = sblock; break;
+ case EXP_PROP_GET: cfun = sblock; break;
+ case EXP_PROP_SET: cfun = sblock; break;
+ case STM_SWITCH: bfun = sblock; break;
+ case STM_CASE: bfun = sblock; break;
+ case STM_DEFAULT: afun = sblock; break;
+ }
+ if (node->a) { pc(' '); afun(d, node->a); }
+ if (node->b) { pc(' '); bfun(d, node->b); }
+ if (node->c) { pc(' '); cfun(d, node->c); }
+ if (node->d) { pc(' '); dfun(d, node->d); }
+ pc(')');
+}
+
+static void slist(int d, js_Ast *list)
+{
+ pc('[');
+ while (list) {
+ assert(list->type == AST_LIST);
+ snode(d, list->a);
+ list = list->b;
+ if (list)
+ pc(' ');
+ }
+ pc(']');
+}
+
+static void sblock(int d, js_Ast *list)
+{
+ ps("[\n");
+ in(d+1);
+ while (list) {
+ assert(list->type == AST_LIST);
+ snode(d+1, list->a);
+ list = list->b;
+ if (list) {
+ nl();
+ in(d+1);
+ }
+ }
+ nl(); in(d); pc(']');
+}
+
+void jsP_dumplist(js_State *J, js_Ast *prog)
+{
+ if (prog->type == AST_LIST)
+ sblock(0, prog);
+ else
+ snode(0, prog);
+ nl();
+}
+
+/* Compiled code */
+
+void jsC_dumpfunction(js_State *J, js_Function *F)
+{
+ js_Instruction *p = F->code;
+ js_Instruction *end = F->code + F->codelen;
+ unsigned int i;
+
+ printf("%s(%d)\n", F->name, F->numparams);
+ if (F->lightweight) printf("\tlightweight\n");
+ if (F->arguments) printf("\targuments\n");
+ printf("\tsource %s:%d\n", F->filename, F->line);
+ for (i = 0; i < F->funlen; ++i)
+ printf("\tfunction %d %s\n", i, F->funtab[i]->name);
+ for (i = 0; i < F->varlen; ++i)
+ printf("\tlocal %d %s\n", i + 1, F->vartab[i]);
+
+ printf("{\n");
+ while (p < end) {
+ int c = *p++;
+
+ printf("% 5d: ", (int)(p - F->code) - 1);
+ ps(opname[c]);
+
+ switch (c) {
+ case OP_NUMBER:
+ printf(" %.9g", F->numtab[*p++]);
+ break;
+ case OP_STRING:
+ pc(' ');
+ pstr(F->strtab[*p++]);
+ break;
+ case OP_NEWREGEXP:
+ pc(' ');
+ pregexp(F->strtab[p[0]], p[1]);
+ p += 2;
+ break;
+
+ case OP_INITVAR:
+ case OP_DEFVAR:
+ case OP_GETVAR:
+ case OP_SETVAR:
+ case OP_DELVAR:
+ case OP_GETPROP_S:
+ case OP_SETPROP_S:
+ case OP_DELPROP_S:
+ case OP_CATCH:
+ pc(' ');
+ ps(F->strtab[*p++]);
+ break;
+
+ case OP_LINE:
+ case OP_CLOSURE:
+ case OP_INITLOCAL:
+ case OP_GETLOCAL:
+ case OP_SETLOCAL:
+ case OP_DELLOCAL:
+ case OP_NUMBER_POS:
+ case OP_NUMBER_NEG:
+ case OP_CALL:
+ case OP_NEW:
+ case OP_JUMP:
+ case OP_JTRUE:
+ case OP_JFALSE:
+ case OP_JCASE:
+ case OP_TRY:
+ printf(" %d", *p++);
+ break;
+ }
+
+ nl();
+ }
+ printf("}\n");
+
+ for (i = 0; i < F->funlen; ++i) {
+ if (F->funtab[i] != F) {
+ printf("function %d ", i);
+ jsC_dumpfunction(J, F->funtab[i]);
+ }
+ }
+}
+
+/* Runtime values */
+
+void js_dumpvalue(js_State *J, js_Value v)
+{
+ switch (v.type) {
+ case JS_TUNDEFINED: printf("undefined"); break;
+ case JS_TNULL: printf("null"); break;
+ case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break;
+ case JS_TNUMBER: printf("%.9g", v.u.number); break;
+ case JS_TSHRSTR: printf("'%s'", v.u.shrstr); break;
+ case JS_TLITSTR: printf("'%s'", v.u.litstr); break;
+ case JS_TMEMSTR: printf("'%s'", v.u.memstr->p); break;
+ case JS_TOBJECT:
+ if (v.u.object == J->G) {
+ printf("[Global]");
+ break;
+ }
+ switch (v.u.object->type) {
+ case JS_COBJECT: printf("[Object %p]", v.u.object); break;
+ case JS_CARRAY: printf("[Array %p]", v.u.object); break;
+ case JS_CFUNCTION:
+ printf("[Function %p, %s, %s:%d]",
+ v.u.object,
+ v.u.object->u.f.function->name,
+ v.u.object->u.f.function->filename,
+ v.u.object->u.f.function->line);
+ break;
+ case JS_CSCRIPT: printf("[Script %s]", v.u.object->u.f.function->filename); break;
+ case JS_CCFUNCTION: printf("[CFunction %p]", v.u.object->u.c.function); break;
+ case JS_CBOOLEAN: printf("[Boolean %d]", v.u.object->u.boolean); break;
+ case JS_CNUMBER: printf("[Number %g]", v.u.object->u.number); break;
+ case JS_CSTRING: printf("[String'%s']", v.u.object->u.s.string); break;
+ case JS_CERROR: printf("[Error %s]", v.u.object->u.s.string); break;
+ case JS_CITERATOR: printf("[Iterator %p]", v.u.object); break;
+ case JS_CUSERDATA:
+ printf("[Userdata %s %p]", v.u.object->u.user.tag, v.u.object->u.user.data);
+ break;
+ default: printf("[Object %p]", v.u.object); break;
+ }
+ break;
+ }
+}
+
+static void js_dumpproperty(js_State *J, js_Property *node)
+{
+ if (node->left->level)
+ js_dumpproperty(J, node->left);
+ printf("\t%s: ", node->name);
+ js_dumpvalue(J, node->value);
+ printf(",\n");
+ if (node->right->level)
+ js_dumpproperty(J, node->right);
+}
+
+void js_dumpobject(js_State *J, js_Object *obj)
+{
+ printf("{\n");
+ if (obj->properties->level)
+ js_dumpproperty(J, obj->properties);
+ printf("}\n");
+}
diff --git a/mujs/jserror.c b/mujs/jserror.c
new file mode 100644
index 0000000..9eda238
--- /dev/null
+++ b/mujs/jserror.c
@@ -0,0 +1,121 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+#define QQ(X) #X
+#define Q(X) QQ(X)
+
+static void jsB_stacktrace(js_State *J, int skip)
+{
+ int n;
+ char buf[256];
+ for (n = J->tracetop - skip; n >= 0; --n) {
+ const char *name = J->trace[n].name;
+ const char *file = J->trace[n].file;
+ int line = J->trace[n].line;
+ if (line > 0)
+ snprintf(buf, sizeof buf, "\n\t%s:%d: in function '%s'", file, line, name);
+ else
+ snprintf(buf, sizeof buf, "\n\t%s: in function '%s'", file, name);
+ js_pushstring(J, buf);
+ if (n < J->tracetop - skip)
+ js_concat(J);
+ }
+}
+
+static void Ep_toString(js_State *J)
+{
+ char buf[256];
+ const char *name = "Error";
+ const char *message = "";
+
+ if (!js_isobject(J, -1))
+ js_typeerror(J, "not an object");
+
+ if (js_hasproperty(J, 0, "name"))
+ name = js_tostring(J, -1);
+ if (js_hasproperty(J, 0, "message"))
+ message = js_tostring(J, -1);
+
+ snprintf(buf, sizeof buf, "%s: %s", name, message);
+ js_pushstring(J, buf);
+
+ if (js_hasproperty(J, 0, "stackTrace"))
+ js_concat(J);
+}
+
+static int jsB_ErrorX(js_State *J, js_Object *prototype)
+{
+ unsigned int top = js_gettop(J);
+ js_pushobject(J, jsV_newobject(J, JS_CERROR, prototype));
+ if (top > 1) {
+ js_pushstring(J, js_tostring(J, 1));
+ js_setproperty(J, -2, "message");
+ }
+ jsB_stacktrace(J, 1);
+ js_setproperty(J, -2, "stackTrace");
+ return 1;
+}
+
+static void js_newerrorx(js_State *J, const char *message, js_Object *prototype)
+{
+ js_pushobject(J, jsV_newobject(J, JS_CERROR, prototype));
+ js_pushstring(J, message);
+ js_setproperty(J, -2, "message");
+ jsB_stacktrace(J, 0);
+ js_setproperty(J, -2, "stackTrace");
+}
+
+#define DERROR(name, Name) \
+ static void jsB_##Name(js_State *J) { \
+ jsB_ErrorX(J, J->Name##_prototype); \
+ } \
+ void js_new##name(js_State *J, const char *s) { \
+ js_newerrorx(J, s, J->Name##_prototype); \
+ } \
+ void js_##name(js_State *J, const char *fmt, ...) { \
+ va_list ap; \
+ char buf[256]; \
+ va_start(ap, fmt); \
+ vsnprintf(buf, sizeof buf, fmt, ap); \
+ va_end(ap); \
+ js_newerrorx(J, buf, J->Name##_prototype); \
+ js_throw(J); \
+ }
+
+DERROR(error, Error)
+DERROR(evalerror, EvalError)
+DERROR(rangeerror, RangeError)
+DERROR(referenceerror, ReferenceError)
+DERROR(syntaxerror, SyntaxError)
+DERROR(typeerror, TypeError)
+DERROR(urierror, URIError)
+
+#undef DERROR
+
+void jsB_initerror(js_State *J)
+{
+ js_pushobject(J, J->Error_prototype);
+ {
+ jsB_props(J, "name", "Error");
+ jsB_props(J, "message", "an error has occurred");
+ jsB_propf(J, "toString", Ep_toString, 0);
+ }
+ js_newcconstructor(J, jsB_Error, jsB_Error, "Error", 1);
+ js_defglobal(J, "Error", JS_DONTENUM);
+
+ #define IERROR(NAME) \
+ js_pushobject(J, J->NAME##_prototype); \
+ jsB_props(J, "name", Q(NAME)); \
+ js_newcconstructor(J, jsB_##NAME, jsB_##NAME, Q(NAME), 1); \
+ js_defglobal(J, Q(NAME), JS_DONTENUM);
+
+ IERROR(EvalError);
+ IERROR(RangeError);
+ IERROR(ReferenceError);
+ IERROR(SyntaxError);
+ IERROR(TypeError);
+ IERROR(URIError);
+
+ #undef IERROR
+}
diff --git a/mujs/jsfunction.c b/mujs/jsfunction.c
new file mode 100644
index 0000000..403e63c
--- /dev/null
+++ b/mujs/jsfunction.c
@@ -0,0 +1,211 @@
+#include "jsi.h"
+#include "jsparse.h"
+#include "jscompile.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+static void jsB_Function(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+ js_Buffer *sb = NULL;
+ const char *body;
+ js_Ast *parse;
+ js_Function *fun;
+
+ /* p1, p2, ..., pn */
+ if (top > 2) {
+ for (i = 1; i < top - 1; ++i) {
+ if (i > 1)
+ js_putc(J, &sb, ',');
+ js_puts(J, &sb, js_tostring(J, i));
+ }
+ js_putc(J, &sb, ')');
+ }
+
+ /* body */
+ body = js_isdefined(J, top - 1) ? js_tostring(J, top - 1) : "";
+
+ if (js_try(J)) {
+ js_free(J, sb);
+ jsP_freeparse(J);
+ js_throw(J);
+ }
+
+ parse = jsP_parsefunction(J, "[string]", sb ? sb->s : NULL, body);
+ fun = jsC_compilefunction(J, parse);
+
+ js_endtry(J);
+ js_free(J, sb);
+ jsP_freeparse(J);
+
+ js_newfunction(J, fun, J->GE);
+}
+
+static void jsB_Function_prototype(js_State *J)
+{
+ js_pushundefined(J);
+}
+
+static void Fp_toString(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ char *s;
+ unsigned int i, n;
+
+ if (!js_iscallable(J, 0))
+ js_typeerror(J, "not a function");
+
+ if (self->type == JS_CFUNCTION || self->type == JS_CSCRIPT) {
+ js_Function *F = self->u.f.function;
+ n = strlen("function () { ... }");
+ n += strlen(F->name);
+ for (i = 0; i < F->numparams; ++i)
+ n += strlen(F->vartab[i]) + 1;
+ s = js_malloc(J, n);
+ strcpy(s, "function ");
+ strcat(s, F->name);
+ strcat(s, "(");
+ for (i = 0; i < F->numparams; ++i) {
+ if (i > 0) strcat(s, ",");
+ strcat(s, F->vartab[i]);
+ }
+ strcat(s, ") { ... }");
+ if (js_try(J)) {
+ js_free(J, s);
+ js_throw(J);
+ }
+ js_pushstring(J, s);
+ js_free(J, s);
+ js_endtry(J);
+ } else {
+ js_pushliteral(J, "function () { ... }");
+ }
+}
+
+static void Fp_apply(js_State *J)
+{
+ int i, n;
+
+ if (!js_iscallable(J, 0))
+ js_typeerror(J, "not a function");
+
+ js_copy(J, 0);
+ js_copy(J, 1);
+
+ n = js_getlength(J, 2);
+ for (i = 0; i < n; ++i)
+ js_getindex(J, 2, i);
+
+ js_call(J, n);
+}
+
+static void Fp_call(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+
+ if (!js_iscallable(J, 0))
+ js_typeerror(J, "not a function");
+
+ for (i = 0; i < top; ++i)
+ js_copy(J, i);
+
+ js_call(J, top - 2);
+}
+
+static void callbound(js_State *J)
+{
+ unsigned int top = js_gettop(J);
+ unsigned int i, fun, args, n;
+
+ fun = js_gettop(J);
+ js_currentfunction(J);
+ js_getproperty(J, fun, "__TargetFunction__");
+ js_getproperty(J, fun, "__BoundThis__");
+
+ args = js_gettop(J);
+ js_getproperty(J, fun, "__BoundArguments__");
+ n = js_getlength(J, args);
+ for (i = 0; i < n; ++i)
+ js_getindex(J, args, i);
+ js_remove(J, args);
+
+ for (i = 1; i < top; ++i)
+ js_copy(J, i);
+
+ js_call(J, n + top - 1);
+}
+
+static void constructbound(js_State *J)
+{
+ unsigned int top = js_gettop(J);
+ unsigned int i, fun, args, n;
+
+ fun = js_gettop(J);
+ js_currentfunction(J);
+ js_getproperty(J, fun, "__TargetFunction__");
+
+ args = js_gettop(J);
+ js_getproperty(J, fun, "__BoundArguments__");
+ n = js_getlength(J, args);
+ for (i = 0; i < n; ++i)
+ js_getindex(J, args, i);
+ js_remove(J, args);
+
+ for (i = 1; i < top; ++i)
+ js_copy(J, i);
+
+ js_construct(J, n + top - 1);
+}
+
+static void Fp_bind(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+ unsigned int n;
+
+ if (!js_iscallable(J, 0))
+ js_typeerror(J, "not a function");
+
+ n = js_getlength(J, 0);
+ if (n > top - 2)
+ n -= top - 2;
+ else
+ n = 0;
+
+ js_newcconstructor(J, callbound, constructbound, "[bind]", n);
+
+ /* Reuse target function's prototype for HasInstance check. */
+ js_getproperty(J, 0, "prototype");
+ js_defproperty(J, -2, "prototype", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+
+ /* target function */
+ js_copy(J, 0);
+ js_defproperty(J, -2, "__TargetFunction__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+
+ /* bound this */
+ js_copy(J, 1);
+ js_defproperty(J, -2, "__BoundThis__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+
+ /* bound arguments */
+ js_newarray(J);
+ for (i = 2; i < top; ++i) {
+ js_copy(J, i);
+ js_setindex(J, -2, i - 2);
+ }
+ js_defproperty(J, -2, "__BoundArguments__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+}
+
+void jsB_initfunction(js_State *J)
+{
+ J->Function_prototype->u.c.function = jsB_Function_prototype;
+ J->Function_prototype->u.c.constructor = NULL;
+
+ js_pushobject(J, J->Function_prototype);
+ {
+ jsB_propf(J, "toString", Fp_toString, 2);
+ jsB_propf(J, "apply", Fp_apply, 2);
+ jsB_propf(J, "call", Fp_call, 1);
+ jsB_propf(J, "bind", Fp_bind, 1);
+ }
+ js_newcconstructor(J, jsB_Function, jsB_Function, "Function", 1);
+ js_defglobal(J, "Function", JS_DONTENUM);
+}
diff --git a/mujs/jsgc.c b/mujs/jsgc.c
new file mode 100644
index 0000000..cd65b03
--- /dev/null
+++ b/mujs/jsgc.c
@@ -0,0 +1,239 @@
+#include "jsi.h"
+#include "jscompile.h"
+#include "jsvalue.h"
+#include "jsrun.h"
+
+#include "regex.h"
+
+static void jsG_markobject(js_State *J, int mark, js_Object *obj);
+
+static void jsG_freeenvironment(js_State *J, js_Environment *env)
+{
+ js_free(J, env);
+}
+
+static void jsG_freefunction(js_State *J, js_Function *fun)
+{
+ js_free(J, fun->funtab);
+ js_free(J, fun->numtab);
+ js_free(J, fun->strtab);
+ js_free(J, fun->vartab);
+ js_free(J, fun->code);
+ js_free(J, fun);
+}
+
+static void jsG_freeproperty(js_State *J, js_Property *node)
+{
+ while (node) {
+ js_Property *next = node->next;
+ js_free(J, node);
+ node = next;
+ }
+}
+
+static void jsG_freeiterator(js_State *J, js_Iterator *node)
+{
+ while (node) {
+ js_Iterator *next = node->next;
+ js_free(J, node);
+ node = next;
+ }
+}
+
+static void jsG_freeobject(js_State *J, js_Object *obj)
+{
+ if (obj->head)
+ jsG_freeproperty(J, obj->head);
+ if (obj->type == JS_CREGEXP)
+ js_regfree(obj->u.r.prog);
+ if (obj->type == JS_CITERATOR)
+ jsG_freeiterator(J, obj->u.iter.head);
+ if (obj->type == JS_CUSERDATA && obj->u.user.finalize)
+ obj->u.user.finalize(J, obj->u.user.data);
+ js_free(J, obj);
+}
+
+static void jsG_markfunction(js_State *J, int mark, js_Function *fun)
+{
+ unsigned int i;
+ fun->gcmark = mark;
+ for (i = 0; i < fun->funlen; ++i)
+ if (fun->funtab[i]->gcmark != mark)
+ jsG_markfunction(J, mark, fun->funtab[i]);
+}
+
+static void jsG_markenvironment(js_State *J, int mark, js_Environment *env)
+{
+ do {
+ env->gcmark = mark;
+ if (env->variables->gcmark != mark)
+ jsG_markobject(J, mark, env->variables);
+ env = env->outer;
+ } while (env && env->gcmark != mark);
+}
+
+static void jsG_markproperty(js_State *J, int mark, js_Property *node)
+{
+ while (node) {
+ if (node->value.type == JS_TMEMSTR && node->value.u.memstr->gcmark != mark)
+ node->value.u.memstr->gcmark = mark;
+ if (node->value.type == JS_TOBJECT && node->value.u.object->gcmark != mark)
+ jsG_markobject(J, mark, node->value.u.object);
+ if (node->getter && node->getter->gcmark != mark)
+ jsG_markobject(J, mark, node->getter);
+ if (node->setter && node->setter->gcmark != mark)
+ jsG_markobject(J, mark, node->setter);
+ node = node->next;
+ }
+}
+
+static void jsG_markobject(js_State *J, int mark, js_Object *obj)
+{
+ obj->gcmark = mark;
+ if (obj->head)
+ jsG_markproperty(J, mark, obj->head);
+ if (obj->prototype && obj->prototype->gcmark != mark)
+ jsG_markobject(J, mark, obj->prototype);
+ if (obj->type == JS_CITERATOR) {
+ jsG_markobject(J, mark, obj->u.iter.target);
+ }
+ if (obj->type == JS_CFUNCTION || obj->type == JS_CSCRIPT) {
+ if (obj->u.f.scope && obj->u.f.scope->gcmark != mark)
+ jsG_markenvironment(J, mark, obj->u.f.scope);
+ if (obj->u.f.function && obj->u.f.function->gcmark != mark)
+ jsG_markfunction(J, mark, obj->u.f.function);
+ }
+}
+
+static void jsG_markstack(js_State *J, int mark)
+{
+ js_Value *v = J->stack;
+ int n = J->top;
+ while (n--) {
+ if (v->type == JS_TMEMSTR && v->u.memstr->gcmark != mark)
+ v->u.memstr->gcmark = mark;
+ if (v->type == JS_TOBJECT && v->u.object->gcmark != mark)
+ jsG_markobject(J, mark, v->u.object);
+ ++v;
+ }
+}
+
+void js_gc(js_State *J, int report)
+{
+ js_Function *fun, *nextfun, **prevnextfun;
+ js_Object *obj, *nextobj, **prevnextobj;
+ js_String *str, *nextstr, **prevnextstr;
+ js_Environment *env, *nextenv, **prevnextenv;
+ int nenv = 0, nfun = 0, nobj = 0, nstr = 0;
+ int genv = 0, gfun = 0, gobj = 0, gstr = 0;
+ int mark;
+ int i;
+
+ mark = J->gcmark = J->gcmark == 1 ? 2 : 1;
+
+ jsG_markobject(J, mark, J->Object_prototype);
+ jsG_markobject(J, mark, J->Array_prototype);
+ jsG_markobject(J, mark, J->Function_prototype);
+ jsG_markobject(J, mark, J->Boolean_prototype);
+ jsG_markobject(J, mark, J->Number_prototype);
+ jsG_markobject(J, mark, J->String_prototype);
+ jsG_markobject(J, mark, J->RegExp_prototype);
+ jsG_markobject(J, mark, J->Date_prototype);
+
+ jsG_markobject(J, mark, J->Error_prototype);
+ jsG_markobject(J, mark, J->EvalError_prototype);
+ jsG_markobject(J, mark, J->RangeError_prototype);
+ jsG_markobject(J, mark, J->ReferenceError_prototype);
+ jsG_markobject(J, mark, J->SyntaxError_prototype);
+ jsG_markobject(J, mark, J->TypeError_prototype);
+ jsG_markobject(J, mark, J->URIError_prototype);
+
+ jsG_markobject(J, mark, J->R);
+ jsG_markobject(J, mark, J->G);
+
+ jsG_markstack(J, mark);
+
+ jsG_markenvironment(J, mark, J->E);
+ jsG_markenvironment(J, mark, J->GE);
+ for (i = 0; i < J->envtop; ++i)
+ jsG_markenvironment(J, mark, J->envstack[i]);
+
+ prevnextenv = &J->gcenv;
+ for (env = J->gcenv; env; env = nextenv) {
+ nextenv = env->gcnext;
+ if (env->gcmark != mark) {
+ *prevnextenv = nextenv;
+ jsG_freeenvironment(J, env);
+ ++genv;
+ } else {
+ prevnextenv = &env->gcnext;
+ }
+ ++nenv;
+ }
+
+ prevnextfun = &J->gcfun;
+ for (fun = J->gcfun; fun; fun = nextfun) {
+ nextfun = fun->gcnext;
+ if (fun->gcmark != mark) {
+ *prevnextfun = nextfun;
+ jsG_freefunction(J, fun);
+ ++gfun;
+ } else {
+ prevnextfun = &fun->gcnext;
+ }
+ ++nfun;
+ }
+
+ prevnextobj = &J->gcobj;
+ for (obj = J->gcobj; obj; obj = nextobj) {
+ nextobj = obj->gcnext;
+ if (obj->gcmark != mark) {
+ *prevnextobj = nextobj;
+ jsG_freeobject(J, obj);
+ ++gobj;
+ } else {
+ prevnextobj = &obj->gcnext;
+ }
+ ++nobj;
+ }
+
+ prevnextstr = &J->gcstr;
+ for (str = J->gcstr; str; str = nextstr) {
+ nextstr = str->gcnext;
+ if (str->gcmark != mark) {
+ *prevnextstr = nextstr;
+ js_free(J, str);
+ ++gstr;
+ } else {
+ prevnextstr = &str->gcnext;
+ }
+ ++nstr;
+ }
+
+ if (report)
+ printf("garbage collected: %d/%d envs, %d/%d funs, %d/%d objs, %d/%d strs\n",
+ genv, nenv, gfun, nfun, gobj, nobj, gstr, nstr);
+}
+
+void js_freestate(js_State *J)
+{
+ js_Function *fun, *nextfun;
+ js_Object *obj, *nextobj;
+ js_Environment *env, *nextenv;
+ js_String *str, *nextstr;
+
+ for (env = J->gcenv; env; env = nextenv)
+ nextenv = env->gcnext, jsG_freeenvironment(J, env);
+ for (fun = J->gcfun; fun; fun = nextfun)
+ nextfun = fun->gcnext, jsG_freefunction(J, fun);
+ for (obj = J->gcobj; obj; obj = nextobj)
+ nextobj = obj->gcnext, jsG_freeobject(J, obj);
+ for (str = J->gcstr; str; str = nextstr)
+ nextstr = str->gcnext, js_free(J, str);
+
+ jsS_freestrings(J);
+
+ js_free(J, J->lexbuf.text);
+ J->alloc(J->actx, J->stack, 0);
+ J->alloc(J->actx, J, 0);
+}
diff --git a/mujs/jsi.h b/mujs/jsi.h
new file mode 100644
index 0000000..85b4377
--- /dev/null
+++ b/mujs/jsi.h
@@ -0,0 +1,213 @@
+#ifndef jsi_h
+#define jsi_h
+
+#include "mujs.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <setjmp.h>
+#include <math.h>
+#include <float.h>
+
+/* Microsoft Visual C */
+#ifdef _MSC_VER
+#pragma warning(disable:4996) /* _CRT_SECURE_NO_WARNINGS */
+#pragma warning(disable:4244) /* implicit conversion from double to int */
+#pragma warning(disable:4267) /* implicit conversion of int to smaller int */
+#define inline __inline
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#if _MSC_VER < 1800
+#define round(x) floor((x) < 0 ? (x) - 0.5 : (x) + 0.5)
+#define isnan(x) _isnan(x)
+#define isinf(x) (!_finite(x))
+#define isfinite(x) _finite(x)
+static __inline int signbit(double x) {union{double d;__int64 i;}u;u.d=x;return u.i>>63;}
+#define INFINITY (DBL_MAX+DBL_MAX)
+#define NAN (INFINITY-INFINITY)
+#endif /* old MSVC */
+#endif
+
+#define nelem(a) (sizeof (a) / sizeof (a)[0])
+
+void *js_malloc(js_State *J, unsigned int size);
+void *js_realloc(js_State *J, void *ptr, unsigned int size);
+void js_free(js_State *J, void *ptr);
+
+typedef struct js_Regexp js_Regexp;
+typedef struct js_Value js_Value;
+typedef struct js_Object js_Object;
+typedef struct js_String js_String;
+typedef struct js_Ast js_Ast;
+typedef struct js_Function js_Function;
+typedef struct js_Environment js_Environment;
+typedef struct js_StringNode js_StringNode;
+typedef struct js_Jumpbuf js_Jumpbuf;
+typedef struct js_StackTrace js_StackTrace;
+
+/* Limits */
+
+#define JS_STACKSIZE 256 /* value stack size */
+#define JS_ENVLIMIT 64 /* environment stack size */
+#define JS_TRYLIMIT 64 /* exception stack size */
+#define JS_GCLIMIT 10000 /* run gc cycle every N allocations */
+
+/* instruction size -- change to unsigned int if you get integer overflow syntax errors */
+typedef unsigned short js_Instruction;
+
+/* String interning */
+
+const char *js_intern(js_State *J, const char *s);
+void jsS_dumpstrings(js_State *J);
+void jsS_freestrings(js_State *J);
+
+/* Portable strtod and printf float formatting */
+
+void js_fmtexp(char *p, int e);
+void js_dtoa(double f, char *digits, int *exp, int *neg, int *ndigits);
+double js_strtod(const char *as, char **aas);
+
+/* Private stack functions */
+
+void js_newfunction(js_State *J, js_Function *function, js_Environment *scope);
+void js_newscript(js_State *J, js_Function *function, js_Environment *scope);
+void js_loadeval(js_State *J, const char *filename, const char *source);
+
+js_Regexp *js_toregexp(js_State *J, int idx);
+int js_isarrayindex(js_State *J, const char *str, unsigned int *idx);
+int js_runeat(js_State *J, const char *s, int i);
+int js_utfptrtoidx(const char *s, const char *p);
+const char *js_utfidxtoptr(const char *s, int i);
+
+void js_dup(js_State *J);
+void js_dup2(js_State *J);
+void js_rot2(js_State *J);
+void js_rot3(js_State *J);
+void js_rot4(js_State *J);
+void js_rot2pop1(js_State *J);
+void js_rot3pop2(js_State *J);
+void js_dup1rot3(js_State *J);
+void js_dup1rot4(js_State *J);
+
+void js_pushundefinedthis(js_State *J); /* push 'global' if non-strict, undefined if strict */
+
+void js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text);
+
+void js_trap(js_State *J, int pc); /* dump stack and environment to stdout */
+
+struct js_StackTrace
+{
+ const char *name;
+ const char *file;
+ int line;
+};
+
+/* Exception handling */
+
+struct js_Jumpbuf
+{
+ jmp_buf buf;
+ js_Environment *E;
+ int envtop;
+ int tracetop;
+ int top, bot;
+ js_Instruction *pc;
+};
+
+void js_savetry(js_State *J, js_Instruction *pc);
+
+#define js_trypc(J, PC) \
+ (js_savetry(J, PC), setjmp(J->trybuf[J->trytop++].buf))
+
+#define js_try(J) \
+ (js_savetry(J, NULL), setjmp(J->trybuf[J->trytop++].buf))
+
+#define js_endtry(J) \
+ (--J->trytop)
+
+/* State struct */
+
+struct js_State
+{
+ void *actx;
+ void *uctx;
+ js_Alloc alloc;
+ js_Panic panic;
+
+ js_StringNode *strings;
+
+ int strict;
+
+ /* parser input source */
+ const char *filename;
+ const char *source;
+ int line;
+
+ /* lexer state */
+ struct { char *text; unsigned int len, cap; } lexbuf;
+ int lexline;
+ int lexchar;
+ int lasttoken;
+ int newline;
+
+ /* parser state */
+ int astline;
+ int lookahead;
+ const char *text;
+ double number;
+ js_Ast *gcast; /* list of allocated nodes to free after parsing */
+
+ /* runtime environment */
+ js_Object *Object_prototype;
+ js_Object *Array_prototype;
+ js_Object *Function_prototype;
+ js_Object *Boolean_prototype;
+ js_Object *Number_prototype;
+ js_Object *String_prototype;
+ js_Object *RegExp_prototype;
+ js_Object *Date_prototype;
+
+ js_Object *Error_prototype;
+ js_Object *EvalError_prototype;
+ js_Object *RangeError_prototype;
+ js_Object *ReferenceError_prototype;
+ js_Object *SyntaxError_prototype;
+ js_Object *TypeError_prototype;
+ js_Object *URIError_prototype;
+
+ int nextref; /* for js_ref use */
+ js_Object *R; /* registry of hidden values */
+ js_Object *G; /* the global object */
+ js_Environment *E; /* current environment scope */
+ js_Environment *GE; /* global environment scope (at the root) */
+
+ /* execution stack */
+ int top, bot;
+ js_Value *stack;
+
+ /* garbage collector list */
+ int gcmark;
+ int gccounter;
+ js_Environment *gcenv;
+ js_Function *gcfun;
+ js_Object *gcobj;
+ js_String *gcstr;
+
+
+ /* environments on the call stack but currently not in scope */
+ int envtop;
+ js_Environment *envstack[JS_ENVLIMIT];
+
+ /* debug info stack trace */
+ int tracetop;
+ js_StackTrace trace[JS_ENVLIMIT];
+
+ /* exception stack */
+ int trytop;
+ js_Jumpbuf trybuf[JS_TRYLIMIT];
+};
+
+#endif
diff --git a/mujs/jsintern.c b/mujs/jsintern.c
new file mode 100644
index 0000000..19c61a5
--- /dev/null
+++ b/mujs/jsintern.c
@@ -0,0 +1,106 @@
+#include "jsi.h"
+
+/* Use an AA-tree to quickly look up interned strings. */
+
+struct js_StringNode
+{
+ js_StringNode *left, *right;
+ int level;
+ char string[1];
+};
+
+static js_StringNode jsS_sentinel = { &jsS_sentinel, &jsS_sentinel, 0, ""};
+
+static js_StringNode *jsS_newstringnode(js_State *J, const char *string, const char **result)
+{
+ int n = strlen(string);
+ js_StringNode *node = js_malloc(J, offsetof(js_StringNode, string) + n + 1);
+ node->left = node->right = &jsS_sentinel;
+ node->level = 1;
+ memcpy(node->string, string, n + 1);
+ return *result = node->string, node;
+}
+
+static js_StringNode *jsS_skew(js_StringNode *node)
+{
+ if (node->left->level == node->level) {
+ js_StringNode *temp = node;
+ node = node->left;
+ temp->left = node->right;
+ node->right = temp;
+ }
+ return node;
+}
+
+static js_StringNode *jsS_split(js_StringNode *node)
+{
+ if (node->right->right->level == node->level) {
+ js_StringNode *temp = node;
+ node = node->right;
+ temp->right = node->left;
+ node->left = temp;
+ ++node->level;
+ }
+ return node;
+}
+
+static js_StringNode *jsS_insert(js_State *J, js_StringNode *node, const char *string, const char **result)
+{
+ if (node != &jsS_sentinel) {
+ int c = strcmp(string, node->string);
+ if (c < 0)
+ node->left = jsS_insert(J, node->left, string, result);
+ else if (c > 0)
+ node->right = jsS_insert(J, node->right, string, result);
+ else
+ return *result = node->string, node;
+ node = jsS_skew(node);
+ node = jsS_split(node);
+ return node;
+ }
+ return jsS_newstringnode(J, string, result);
+}
+
+static void dumpstringnode(js_StringNode *node, int level)
+{
+ int i;
+ if (node->left != &jsS_sentinel)
+ dumpstringnode(node->left, level + 1);
+ printf("%d: ", node->level);
+ for (i = 0; i < level; ++i)
+ putchar('\t');
+ printf("'%s'\n", node->string);
+ if (node->right != &jsS_sentinel)
+ dumpstringnode(node->right, level + 1);
+}
+
+void jsS_dumpstrings(js_State *J)
+{
+ js_StringNode *root = J->strings;
+ printf("interned strings {\n");
+ if (root && root != &jsS_sentinel)
+ dumpstringnode(root, 1);
+ printf("}\n");
+}
+
+static void jsS_freestringnode(js_State *J, js_StringNode *node)
+{
+ if (node->left != &jsS_sentinel) jsS_freestringnode(J, node->left);
+ if (node->right != &jsS_sentinel) jsS_freestringnode(J, node->right);
+ js_free(J, node);
+}
+
+void jsS_freestrings(js_State *J)
+{
+ if (J->strings && J->strings != &jsS_sentinel)
+ jsS_freestringnode(J, J->strings);
+}
+
+const char *js_intern(js_State *J, const char *s)
+{
+ const char *result;
+ if (!J->strings)
+ J->strings = &jsS_sentinel;
+ J->strings = jsS_insert(J, J->strings, s, &result);
+ return result;
+}
diff --git a/mujs/jslex.c b/mujs/jslex.c
new file mode 100644
index 0000000..7b80800
--- /dev/null
+++ b/mujs/jslex.c
@@ -0,0 +1,770 @@
+#include "jsi.h"
+#include "jslex.h"
+#include "utf.h"
+
+JS_NORETURN static void jsY_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+
+static void jsY_error(js_State *J, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[512];
+ char msgbuf[256];
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, 256, fmt, ap);
+ va_end(ap);
+
+ snprintf(buf, 256, "%s:%d: ", J->filename, J->lexline);
+ strcat(buf, msgbuf);
+
+ js_newsyntaxerror(J, buf);
+ js_throw(J);
+}
+
+static const char *tokenstring[] = {
+ "(end-of-file)",
+ "'\\x01'", "'\\x02'", "'\\x03'", "'\\x04'", "'\\x05'", "'\\x06'", "'\\x07'",
+ "'\\x08'", "'\\x09'", "'\\x0A'", "'\\x0B'", "'\\x0C'", "'\\x0D'", "'\\x0E'", "'\\x0F'",
+ "'\\x10'", "'\\x11'", "'\\x12'", "'\\x13'", "'\\x14'", "'\\x15'", "'\\x16'", "'\\x17'",
+ "'\\x18'", "'\\x19'", "'\\x1A'", "'\\x1B'", "'\\x1C'", "'\\x1D'", "'\\x1E'", "'\\x1F'",
+ "' '", "'!'", "'\"'", "'#'", "'$'", "'%'", "'&'", "'\\''",
+ "'('", "')'", "'*'", "'+'", "','", "'-'", "'.'", "'/'",
+ "'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'", "'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'", "'{'", "'|'", "'}'", "'~'", "'\\x7F'",
+
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+
+ "(identifier)", "(number)", "(string)", "(regexp)",
+
+ "'<='", "'>='", "'=='", "'!='", "'==='", "'!=='",
+ "'<<'", "'>>'", "'>>>'", "'&&'", "'||'",
+ "'+='", "'-='", "'*='", "'/='", "'%='",
+ "'<<='", "'>>='", "'>>>='", "'&='", "'|='", "'^='",
+ "'++'", "'--'",
+
+ "'break'", "'case'", "'catch'", "'continue'", "'debugger'",
+ "'default'", "'delete'", "'do'", "'else'", "'false'", "'finally'", "'for'",
+ "'function'", "'if'", "'in'", "'instanceof'", "'new'", "'null'", "'return'",
+ "'switch'", "'this'", "'throw'", "'true'", "'try'", "'typeof'", "'var'",
+ "'void'", "'while'", "'with'",
+};
+
+const char *jsY_tokenstring(int token)
+{
+ if (token >= 0 && token < (int)nelem(tokenstring))
+ if (tokenstring[token])
+ return tokenstring[token];
+ return "<unknown>";
+}
+
+static const char *keywords[] = {
+ "break", "case", "catch", "continue", "debugger", "default", "delete",
+ "do", "else", "false", "finally", "for", "function", "if", "in",
+ "instanceof", "new", "null", "return", "switch", "this", "throw",
+ "true", "try", "typeof", "var", "void", "while", "with",
+};
+
+int jsY_findword(const char *s, const char **list, int num)
+{
+ int l = 0;
+ int r = num - 1;
+ while (l <= r) {
+ int m = (l + r) >> 1;
+ int c = strcmp(s, list[m]);
+ if (c < 0)
+ r = m - 1;
+ else if (c > 0)
+ l = m + 1;
+ else
+ return m;
+ }
+ return -1;
+}
+
+static int jsY_findkeyword(js_State *J, const char *s)
+{
+ int i = jsY_findword(s, keywords, nelem(keywords));
+ if (i >= 0) {
+ J->text = keywords[i];
+ return TK_BREAK + i; /* first keyword + i */
+ }
+ J->text = js_intern(J, s);
+ return TK_IDENTIFIER;
+}
+
+int jsY_iswhite(int c)
+{
+ return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF;
+}
+
+int jsY_isnewline(int c)
+{
+ return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
+}
+
+#define isalpha(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+#define isdigit(c) (c >= '0' && c <= '9')
+#define ishex(c) ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
+
+static int jsY_isidentifierstart(int c)
+{
+ return isalpha(c) || c == '$' || c == '_' || isalpharune(c);
+}
+
+static int jsY_isidentifierpart(int c)
+{
+ return isdigit(c) || isalpha(c) || c == '$' || c == '_' || isalpharune(c);
+}
+
+static int jsY_isdec(int c)
+{
+ return isdigit(c);
+}
+
+int jsY_ishex(int c)
+{
+ return isdigit(c) || ishex(c);
+}
+
+int jsY_tohex(int c)
+{
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return c - 'a' + 0xA;
+ if (c >= 'A' && c <= 'F') return c - 'A' + 0xA;
+ return 0;
+}
+
+static void jsY_next(js_State *J)
+{
+ Rune c;
+ J->source += chartorune(&c, J->source);
+ /* consume CR LF as one unit */
+ if (c == '\r' && *J->source == '\n')
+ ++J->source;
+ if (jsY_isnewline(c)) {
+ J->line++;
+ c = '\n';
+ }
+ J->lexchar = c;
+}
+
+#define jsY_accept(J, x) (J->lexchar == x ? (jsY_next(J), 1) : 0)
+
+#define jsY_expect(J, x) if (!jsY_accept(J, x)) jsY_error(J, "expected '%c'", x)
+
+static void jsY_unescape(js_State *J)
+{
+ if (jsY_accept(J, '\\')) {
+ if (jsY_accept(J, 'u')) {
+ int x = 0;
+ if (!jsY_ishex(J->lexchar)) goto error; x |= jsY_tohex(J->lexchar) << 12; jsY_next(J);
+ if (!jsY_ishex(J->lexchar)) goto error; x |= jsY_tohex(J->lexchar) << 8; jsY_next(J);
+ if (!jsY_ishex(J->lexchar)) goto error; x |= jsY_tohex(J->lexchar) << 4; jsY_next(J);
+ if (!jsY_ishex(J->lexchar)) goto error; x |= jsY_tohex(J->lexchar);
+ J->lexchar = x;
+ return;
+ }
+error:
+ jsY_error(J, "unexpected escape sequence");
+ }
+}
+
+static void textinit(js_State *J)
+{
+ if (!J->lexbuf.text) {
+ J->lexbuf.cap = 4096;
+ J->lexbuf.text = js_malloc(J, J->lexbuf.cap);
+ }
+ J->lexbuf.len = 0;
+}
+
+static void textpush(js_State *J, Rune c)
+{
+ int n = runelen(c);
+ if (J->lexbuf.len + n > J->lexbuf.cap) {
+ J->lexbuf.cap = J->lexbuf.cap * 2;
+ J->lexbuf.text = js_realloc(J, J->lexbuf.text, J->lexbuf.cap);
+ }
+ J->lexbuf.len += runetochar(J->lexbuf.text + J->lexbuf.len, &c);
+}
+
+static char *textend(js_State *J)
+{
+ textpush(J, 0);
+ return J->lexbuf.text;
+}
+
+static void lexlinecomment(js_State *J)
+{
+ while (J->lexchar && J->lexchar != '\n')
+ jsY_next(J);
+}
+
+static int lexcomment(js_State *J)
+{
+ /* already consumed initial '/' '*' sequence */
+ while (J->lexchar != 0) {
+ if (jsY_accept(J, '*')) {
+ while (J->lexchar == '*')
+ jsY_next(J);
+ if (jsY_accept(J, '/'))
+ return 0;
+ }
+ jsY_next(J);
+ }
+ return -1;
+}
+
+static double lexhex(js_State *J)
+{
+ double n = 0;
+ if (!jsY_ishex(J->lexchar))
+ jsY_error(J, "malformed hexadecimal number");
+ while (jsY_ishex(J->lexchar)) {
+ n = n * 16 + jsY_tohex(J->lexchar);
+ jsY_next(J);
+ }
+ return n;
+}
+
+#if 0
+
+static double lexinteger(js_State *J)
+{
+ double n = 0;
+ if (!jsY_isdec(J->lexchar))
+ jsY_error(J, "malformed number");
+ while (jsY_isdec(J->lexchar)) {
+ n = n * 10 + (J->lexchar - '0');
+ jsY_next(J);
+ }
+ return n;
+}
+
+static double lexfraction(js_State *J)
+{
+ double n = 0;
+ double d = 1;
+ while (jsY_isdec(J->lexchar)) {
+ n = n * 10 + (J->lexchar - '0');
+ d = d * 10;
+ jsY_next(J);
+ }
+ return n / d;
+}
+
+static double lexexponent(js_State *J)
+{
+ double sign;
+ if (jsY_accept(J, 'e') || jsY_accept(J, 'E')) {
+ if (jsY_accept(J, '-')) sign = -1;
+ else if (jsY_accept(J, '+')) sign = 1;
+ else sign = 1;
+ return sign * lexinteger(J);
+ }
+ return 0;
+}
+
+static int lexnumber(js_State *J)
+{
+ double n;
+ double e;
+
+ if (jsY_accept(J, '0')) {
+ if (jsY_accept(J, 'x') || jsY_accept(J, 'X')) {
+ J->number = lexhex(J);
+ return TK_NUMBER;
+ }
+ if (jsY_isdec(J->lexchar))
+ jsY_error(J, "number with leading zero");
+ n = 0;
+ if (jsY_accept(J, '.'))
+ n += lexfraction(J);
+ } else if (jsY_accept(J, '.')) {
+ if (!jsY_isdec(J->lexchar))
+ return '.';
+ n = lexfraction(J);
+ } else {
+ n = lexinteger(J);
+ if (jsY_accept(J, '.'))
+ n += lexfraction(J);
+ }
+
+ e = lexexponent(J);
+ if (e < 0)
+ n /= pow(10, -e);
+ else if (e > 0)
+ n *= pow(10, e);
+
+ if (jsY_isidentifierstart(J->lexchar))
+ jsY_error(J, "number with letter suffix");
+
+ J->number = n;
+ return TK_NUMBER;
+}
+
+#else
+
+static int lexnumber(js_State *J)
+{
+ const char *s = J->source - 1;
+
+ if (jsY_accept(J, '0')) {
+ if (jsY_accept(J, 'x') || jsY_accept(J, 'X')) {
+ J->number = lexhex(J);
+ return TK_NUMBER;
+ }
+ if (jsY_isdec(J->lexchar))
+ jsY_error(J, "number with leading zero");
+ if (jsY_accept(J, '.')) {
+ while (jsY_isdec(J->lexchar))
+ jsY_next(J);
+ }
+ } else if (jsY_accept(J, '.')) {
+ if (!jsY_isdec(J->lexchar))
+ return '.';
+ while (jsY_isdec(J->lexchar))
+ jsY_next(J);
+ } else {
+ while (jsY_isdec(J->lexchar))
+ jsY_next(J);
+ if (jsY_accept(J, '.')) {
+ while (jsY_isdec(J->lexchar))
+ jsY_next(J);
+ }
+ }
+
+ if (jsY_accept(J, 'e') || jsY_accept(J, 'E')) {
+ if (J->lexchar == '-' || J->lexchar == '+')
+ jsY_next(J);
+ while (jsY_isdec(J->lexchar))
+ jsY_next(J);
+ }
+
+ if (jsY_isidentifierstart(J->lexchar))
+ jsY_error(J, "number with letter suffix");
+
+ J->number = js_strtod(s, NULL);
+ return TK_NUMBER;
+
+}
+
+#endif
+
+static int lexescape(js_State *J)
+{
+ int x = 0;
+
+ /* already consumed '\' */
+
+ if (jsY_accept(J, '\n'))
+ return 0;
+
+ switch (J->lexchar) {
+ case 'u':
+ jsY_next(J);
+ if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 12; jsY_next(J); }
+ if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 8; jsY_next(J); }
+ if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 4; jsY_next(J); }
+ if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar); jsY_next(J); }
+ textpush(J, x);
+ break;
+ case 'x':
+ jsY_next(J);
+ if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar) << 4; jsY_next(J); }
+ if (!jsY_ishex(J->lexchar)) return 1; else { x |= jsY_tohex(J->lexchar); jsY_next(J); }
+ textpush(J, x);
+ break;
+ case '0': textpush(J, 0); jsY_next(J); break;
+ case '\\': textpush(J, '\\'); jsY_next(J); break;
+ case '\'': textpush(J, '\''); jsY_next(J); break;
+ case '"': textpush(J, '"'); jsY_next(J); break;
+ case 'b': textpush(J, '\b'); jsY_next(J); break;
+ case 'f': textpush(J, '\f'); jsY_next(J); break;
+ case 'n': textpush(J, '\n'); jsY_next(J); break;
+ case 'r': textpush(J, '\r'); jsY_next(J); break;
+ case 't': textpush(J, '\t'); jsY_next(J); break;
+ case 'v': textpush(J, '\v'); jsY_next(J); break;
+ default: textpush(J, J->lexchar); jsY_next(J); break;
+ }
+ return 0;
+}
+
+static int lexstring(js_State *J)
+{
+ const char *s;
+
+ int q = J->lexchar;
+ jsY_next(J);
+
+ textinit(J);
+
+ while (J->lexchar != q) {
+ if (J->lexchar == 0 || J->lexchar == '\n')
+ jsY_error(J, "string not terminated");
+ if (jsY_accept(J, '\\')) {
+ if (lexescape(J))
+ jsY_error(J, "malformed escape sequence");
+ } else {
+ textpush(J, J->lexchar);
+ jsY_next(J);
+ }
+ }
+ jsY_expect(J, q);
+
+ s = textend(J);
+
+ J->text = js_intern(J, s);
+ return TK_STRING;
+}
+
+/* the ugliest language wart ever... */
+static int isregexpcontext(int last)
+{
+ switch (last) {
+ case ']':
+ case ')':
+ case '}':
+ case TK_IDENTIFIER:
+ case TK_NUMBER:
+ case TK_STRING:
+ case TK_FALSE:
+ case TK_NULL:
+ case TK_THIS:
+ case TK_TRUE:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static int lexregexp(js_State *J)
+{
+ const char *s;
+ int g, m, i;
+ int inclass = 0;
+
+ /* already consumed initial '/' */
+
+ textinit(J);
+
+ /* regexp body */
+ while (J->lexchar != '/' || inclass) {
+ if (J->lexchar == 0 || J->lexchar == '\n') {
+ jsY_error(J, "regular expression not terminated");
+ } else if (jsY_accept(J, '\\')) {
+ if (jsY_accept(J, '/')) {
+ textpush(J, '/');
+ } else {
+ textpush(J, '\\');
+ if (J->lexchar == 0 || J->lexchar == '\n')
+ jsY_error(J, "regular expression not terminated");
+ textpush(J, J->lexchar);
+ jsY_next(J);
+ }
+ } else {
+ if (J->lexchar == '[' && !inclass)
+ inclass = 1;
+ if (J->lexchar == ']' && inclass)
+ inclass = 0;
+ textpush(J, J->lexchar);
+ jsY_next(J);
+ }
+ }
+ jsY_expect(J, '/');
+
+ s = textend(J);
+
+ /* regexp flags */
+ g = i = m = 0;
+
+ while (jsY_isidentifierpart(J->lexchar)) {
+ if (jsY_accept(J, 'g')) ++g;
+ else if (jsY_accept(J, 'i')) ++i;
+ else if (jsY_accept(J, 'm')) ++m;
+ else jsY_error(J, "illegal flag in regular expression: %c", J->lexchar);
+ }
+
+ if (g > 1 || i > 1 || m > 1)
+ jsY_error(J, "duplicated flag in regular expression");
+
+ J->text = js_intern(J, s);
+ J->number = 0;
+ if (g) J->number += JS_REGEXP_G;
+ if (i) J->number += JS_REGEXP_I;
+ if (m) J->number += JS_REGEXP_M;
+ return TK_REGEXP;
+}
+
+/* simple "return [no Line Terminator here] ..." contexts */
+static int isnlthcontext(int last)
+{
+ switch (last) {
+ case TK_BREAK:
+ case TK_CONTINUE:
+ case TK_RETURN:
+ case TK_THROW:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int jsY_lexx(js_State *J)
+{
+ J->newline = 0;
+
+ while (1) {
+ J->lexline = J->line; /* save location of beginning of token */
+
+ while (jsY_iswhite(J->lexchar))
+ jsY_next(J);
+
+ if (jsY_accept(J, '\n')) {
+ J->newline = 1;
+ if (isnlthcontext(J->lasttoken))
+ return ';';
+ continue;
+ }
+
+ if (jsY_accept(J, '/')) {
+ if (jsY_accept(J, '/')) {
+ lexlinecomment(J);
+ continue;
+ } else if (jsY_accept(J, '*')) {
+ if (lexcomment(J))
+ jsY_error(J, "multi-line comment not terminated");
+ continue;
+ } else if (isregexpcontext(J->lasttoken)) {
+ return lexregexp(J);
+ } else if (jsY_accept(J, '=')) {
+ return TK_DIV_ASS;
+ } else {
+ return '/';
+ }
+ }
+
+ if (J->lexchar >= '0' && J->lexchar <= '9') {
+ return lexnumber(J);
+ }
+
+ switch (J->lexchar) {
+ case '(': jsY_next(J); return '(';
+ case ')': jsY_next(J); return ')';
+ case ',': jsY_next(J); return ',';
+ case ':': jsY_next(J); return ':';
+ case ';': jsY_next(J); return ';';
+ case '?': jsY_next(J); return '?';
+ case '[': jsY_next(J); return '[';
+ case ']': jsY_next(J); return ']';
+ case '{': jsY_next(J); return '{';
+ case '}': jsY_next(J); return '}';
+ case '~': jsY_next(J); return '~';
+
+ case '\'':
+ case '"':
+ return lexstring(J);
+
+ case '.':
+ return lexnumber(J);
+
+ case '<':
+ jsY_next(J);
+ if (jsY_accept(J, '<')) {
+ if (jsY_accept(J, '='))
+ return TK_SHL_ASS;
+ return TK_SHL;
+ }
+ if (jsY_accept(J, '='))
+ return TK_LE;
+ return '<';
+
+ case '>':
+ jsY_next(J);
+ if (jsY_accept(J, '>')) {
+ if (jsY_accept(J, '>')) {
+ if (jsY_accept(J, '='))
+ return TK_USHR_ASS;
+ return TK_USHR;
+ }
+ if (jsY_accept(J, '='))
+ return TK_SHR_ASS;
+ return TK_SHR;
+ }
+ if (jsY_accept(J, '='))
+ return TK_GE;
+ return '>';
+
+ case '=':
+ jsY_next(J);
+ if (jsY_accept(J, '=')) {
+ if (jsY_accept(J, '='))
+ return TK_STRICTEQ;
+ return TK_EQ;
+ }
+ return '=';
+
+ case '!':
+ jsY_next(J);
+ if (jsY_accept(J, '=')) {
+ if (jsY_accept(J, '='))
+ return TK_STRICTNE;
+ return TK_NE;
+ }
+ return '!';
+
+ case '+':
+ jsY_next(J);
+ if (jsY_accept(J, '+'))
+ return TK_INC;
+ if (jsY_accept(J, '='))
+ return TK_ADD_ASS;
+ return '+';
+
+ case '-':
+ jsY_next(J);
+ if (jsY_accept(J, '-'))
+ return TK_DEC;
+ if (jsY_accept(J, '='))
+ return TK_SUB_ASS;
+ return '-';
+
+ case '*':
+ jsY_next(J);
+ if (jsY_accept(J, '='))
+ return TK_MUL_ASS;
+ return '*';
+
+ case '%':
+ jsY_next(J);
+ if (jsY_accept(J, '='))
+ return TK_MOD_ASS;
+ return '%';
+
+ case '&':
+ jsY_next(J);
+ if (jsY_accept(J, '&'))
+ return TK_AND;
+ if (jsY_accept(J, '='))
+ return TK_AND_ASS;
+ return '&';
+
+ case '|':
+ jsY_next(J);
+ if (jsY_accept(J, '|'))
+ return TK_OR;
+ if (jsY_accept(J, '='))
+ return TK_OR_ASS;
+ return '|';
+
+ case '^':
+ jsY_next(J);
+ if (jsY_accept(J, '='))
+ return TK_XOR_ASS;
+ return '^';
+
+ case 0:
+ return 0; /* EOF */
+ }
+
+ /* Handle \uXXXX escapes in identifiers */
+ jsY_unescape(J);
+ if (jsY_isidentifierstart(J->lexchar)) {
+ textinit(J);
+ textpush(J, J->lexchar);
+
+ jsY_next(J);
+ jsY_unescape(J);
+ while (jsY_isidentifierpart(J->lexchar)) {
+ textpush(J, J->lexchar);
+ jsY_next(J);
+ jsY_unescape(J);
+ }
+
+ textend(J);
+
+ return jsY_findkeyword(J, J->lexbuf.text);
+ }
+
+ if (J->lexchar >= 0x20 && J->lexchar <= 0x7E)
+ jsY_error(J, "unexpected character: '%c'", J->lexchar);
+ jsY_error(J, "unexpected character: \\u%04X", J->lexchar);
+ }
+}
+
+void jsY_initlex(js_State *J, const char *filename, const char *source)
+{
+ J->filename = filename;
+ J->source = source;
+ J->line = 1;
+ J->lasttoken = 0;
+ jsY_next(J); /* load first lookahead character */
+}
+
+int jsY_lex(js_State *J)
+{
+ return J->lasttoken = jsY_lexx(J);
+}
+
+int jsY_lexjson(js_State *J)
+{
+ while (1) {
+ J->lexline = J->line; /* save location of beginning of token */
+
+ while (jsY_iswhite(J->lexchar) || J->lexchar == '\n')
+ jsY_next(J);
+
+ if (J->lexchar >= '0' && J->lexchar <= '9') {
+ return lexnumber(J);
+ }
+
+ switch (J->lexchar) {
+ case ',': jsY_next(J); return ',';
+ case ':': jsY_next(J); return ':';
+ case '[': jsY_next(J); return '[';
+ case ']': jsY_next(J); return ']';
+ case '{': jsY_next(J); return '{';
+ case '}': jsY_next(J); return '}';
+
+ case '"':
+ return lexstring(J);
+
+ case '.':
+ return lexnumber(J);
+
+ case 'f':
+ jsY_next(J); jsY_expect(J, 'a'); jsY_expect(J, 'l'); jsY_expect(J, 's'); jsY_expect(J, 'e');
+ return TK_FALSE;
+
+ case 'n':
+ jsY_next(J); jsY_expect(J, 'u'); jsY_expect(J, 'l'); jsY_expect(J, 'l');
+ return TK_NULL;
+
+ case 't':
+ jsY_next(J); jsY_expect(J, 'r'); jsY_expect(J, 'u'); jsY_expect(J, 'e');
+ return TK_TRUE;
+
+ case 0:
+ return 0; /* EOF */
+ }
+
+ if (J->lexchar >= 0x20 && J->lexchar <= 0x7E)
+ jsY_error(J, "unexpected character: '%c'", J->lexchar);
+ jsY_error(J, "unexpected character: \\u%04X", J->lexchar);
+ }
+}
diff --git a/mujs/jslex.h b/mujs/jslex.h
new file mode 100644
index 0000000..e050576
--- /dev/null
+++ b/mujs/jslex.h
@@ -0,0 +1,81 @@
+#ifndef js_lex_h
+#define js_lex_h
+
+enum
+{
+ TK_IDENTIFIER = 256,
+ TK_NUMBER,
+ TK_STRING,
+ TK_REGEXP,
+
+ /* multi-character punctuators */
+ TK_LE,
+ TK_GE,
+ TK_EQ,
+ TK_NE,
+ TK_STRICTEQ,
+ TK_STRICTNE,
+ TK_SHL,
+ TK_SHR,
+ TK_USHR,
+ TK_AND,
+ TK_OR,
+ TK_ADD_ASS,
+ TK_SUB_ASS,
+ TK_MUL_ASS,
+ TK_DIV_ASS,
+ TK_MOD_ASS,
+ TK_SHL_ASS,
+ TK_SHR_ASS,
+ TK_USHR_ASS,
+ TK_AND_ASS,
+ TK_OR_ASS,
+ TK_XOR_ASS,
+ TK_INC,
+ TK_DEC,
+
+ /* keywords */
+ TK_BREAK,
+ TK_CASE,
+ TK_CATCH,
+ TK_CONTINUE,
+ TK_DEBUGGER,
+ TK_DEFAULT,
+ TK_DELETE,
+ TK_DO,
+ TK_ELSE,
+ TK_FALSE,
+ TK_FINALLY,
+ TK_FOR,
+ TK_FUNCTION,
+ TK_IF,
+ TK_IN,
+ TK_INSTANCEOF,
+ TK_NEW,
+ TK_NULL,
+ TK_RETURN,
+ TK_SWITCH,
+ TK_THIS,
+ TK_THROW,
+ TK_TRUE,
+ TK_TRY,
+ TK_TYPEOF,
+ TK_VAR,
+ TK_VOID,
+ TK_WHILE,
+ TK_WITH,
+};
+
+int jsY_iswhite(int c);
+int jsY_isnewline(int c);
+int jsY_ishex(int c);
+int jsY_tohex(int c);
+
+const char *jsY_tokenstring(int token);
+int jsY_findword(const char *s, const char **list, int num);
+
+void jsY_initlex(js_State *J, const char *filename, const char *source);
+int jsY_lex(js_State *J);
+int jsY_lexjson(js_State *J);
+
+#endif
diff --git a/mujs/jsmath.c b/mujs/jsmath.c
new file mode 100644
index 0000000..7ee07a2
--- /dev/null
+++ b/mujs/jsmath.c
@@ -0,0 +1,166 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+static void Math_abs(js_State *J)
+{
+ js_pushnumber(J, fabs(js_tonumber(J, 1)));
+}
+
+static void Math_acos(js_State *J)
+{
+ js_pushnumber(J, acos(js_tonumber(J, 1)));
+}
+
+static void Math_asin(js_State *J)
+{
+ js_pushnumber(J, asin(js_tonumber(J, 1)));
+}
+
+static void Math_atan(js_State *J)
+{
+ js_pushnumber(J, atan(js_tonumber(J, 1)));
+}
+
+static void Math_atan2(js_State *J)
+{
+ double y = js_tonumber(J, 1);
+ double x = js_tonumber(J, 2);
+ js_pushnumber(J, atan2(y, x));
+}
+
+static void Math_ceil(js_State *J)
+{
+ js_pushnumber(J, ceil(js_tonumber(J, 1)));
+}
+
+static void Math_cos(js_State *J)
+{
+ js_pushnumber(J, cos(js_tonumber(J, 1)));
+}
+
+static void Math_exp(js_State *J)
+{
+ js_pushnumber(J, exp(js_tonumber(J, 1)));
+}
+
+static void Math_floor(js_State *J)
+{
+ js_pushnumber(J, floor(js_tonumber(J, 1)));
+}
+
+static void Math_log(js_State *J)
+{
+ js_pushnumber(J, log(js_tonumber(J, 1)));
+}
+
+static void Math_pow(js_State *J)
+{
+ double x = js_tonumber(J, 1);
+ double y = js_tonumber(J, 2);
+ if (!isfinite(y) && fabs(x) == 1)
+ js_pushnumber(J, NAN);
+ else
+ js_pushnumber(J, pow(x,y));
+}
+
+static void Math_random(js_State *J)
+{
+ js_pushnumber(J, rand() / (RAND_MAX + 1.0));
+}
+
+static void Math_round(js_State *J)
+{
+ double x = js_tonumber(J, 1);
+ double r = round(x);
+ if (r - x == -0.5)
+ js_pushnumber(J, x == -0.5 ? -0.0 : r + 1.0);
+ else
+ js_pushnumber(J, r);
+}
+
+static void Math_sin(js_State *J)
+{
+ js_pushnumber(J, sin(js_tonumber(J, 1)));
+}
+
+static void Math_sqrt(js_State *J)
+{
+ js_pushnumber(J, sqrt(js_tonumber(J, 1)));
+}
+
+static void Math_tan(js_State *J)
+{
+ js_pushnumber(J, tan(js_tonumber(J, 1)));
+}
+
+static void Math_max(js_State *J)
+{
+ unsigned int i, n = js_gettop(J);
+ double x = -INFINITY;
+ for (i = 1; i < n; ++i) {
+ double y = js_tonumber(J, i);
+ if (isnan(y)) {
+ x = y;
+ break;
+ }
+ if (signbit(x) == signbit(y))
+ x = x > y ? x : y;
+ else if (signbit(x))
+ x = y;
+ }
+ js_pushnumber(J, x);
+}
+
+static void Math_min(js_State *J)
+{
+ unsigned int i, n = js_gettop(J);
+ double x = INFINITY;
+ for (i = 1; i < n; ++i) {
+ double y = js_tonumber(J, i);
+ if (isnan(y)) {
+ x = y;
+ break;
+ }
+ if (signbit(x) == signbit(y))
+ x = x < y ? x : y;
+ else if (signbit(y))
+ x = y;
+ }
+ js_pushnumber(J, x);
+}
+
+void jsB_initmath(js_State *J)
+{
+ js_pushobject(J, jsV_newobject(J, JS_CMATH, J->Object_prototype));
+ {
+ jsB_propn(J, "E", 2.7182818284590452354);
+ jsB_propn(J, "LN10", 2.302585092994046);
+ jsB_propn(J, "LN2", 0.6931471805599453);
+ jsB_propn(J, "LOG2E", 1.4426950408889634);
+ jsB_propn(J, "LOG10E", 0.4342944819032518);
+ jsB_propn(J, "PI", 3.1415926535897932);
+ jsB_propn(J, "SQRT1_2", 0.7071067811865476);
+ jsB_propn(J, "SQRT2", 1.4142135623730951);
+
+ jsB_propf(J, "abs", Math_abs, 1);
+ jsB_propf(J, "acos", Math_acos, 1);
+ jsB_propf(J, "asin", Math_asin, 1);
+ jsB_propf(J, "atan", Math_atan, 1);
+ jsB_propf(J, "atan2", Math_atan2, 2);
+ jsB_propf(J, "ceil", Math_ceil, 1);
+ jsB_propf(J, "cos", Math_cos, 1);
+ jsB_propf(J, "exp", Math_exp, 1);
+ jsB_propf(J, "floor", Math_floor, 1);
+ jsB_propf(J, "log", Math_log, 1);
+ jsB_propf(J, "max", Math_max, 0);
+ jsB_propf(J, "min", Math_min, 0);
+ jsB_propf(J, "pow", Math_pow, 2);
+ jsB_propf(J, "random", Math_random, 0);
+ jsB_propf(J, "round", Math_round, 1);
+ jsB_propf(J, "sin", Math_sin, 1);
+ jsB_propf(J, "sqrt", Math_sqrt, 1);
+ jsB_propf(J, "tan", Math_tan, 1);
+ }
+ js_defglobal(J, "Math", JS_DONTENUM);
+}
diff --git a/mujs/jsnumber.c b/mujs/jsnumber.c
new file mode 100644
index 0000000..8f29f21
--- /dev/null
+++ b/mujs/jsnumber.c
@@ -0,0 +1,101 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+static void jsB_new_Number(js_State *J)
+{
+ js_newnumber(J, js_gettop(J) > 1 ? js_tonumber(J, 1) : 0);
+}
+
+static void jsB_Number(js_State *J)
+{
+ js_pushnumber(J, js_gettop(J) > 1 ? js_tonumber(J, 1) : 0);
+}
+
+static void Np_valueOf(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
+ js_pushnumber(J, self->u.number);
+}
+
+static void Np_toString(js_State *J)
+{
+ char buf[32];
+ js_Object *self = js_toobject(J, 0);
+ int radix = js_isundefined(J, 1) ? 10 : js_tointeger(J, 1);
+ if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
+ if (radix < 2 || radix > 36)
+ js_rangeerror(J, "invalid radix");
+ if (radix != 10)
+ js_rangeerror(J, "invalid radix");
+ js_pushstring(J, jsV_numbertostring(J, buf, self->u.number));
+}
+
+/* Customized ToString() on a number */
+static void numtostr(js_State *J, const char *fmt, int w, double n)
+{
+ char buf[32], *e;
+ if (isnan(n)) js_pushliteral(J, "NaN");
+ else if (isinf(n)) js_pushliteral(J, n < 0 ? "-Infinity" : "Infinity");
+ else if (n == 0) js_pushliteral(J, "0");
+ else {
+ if (w < 1) w = 1;
+ if (w > 17) w = 17;
+ sprintf(buf, fmt, w, n);
+ e = strchr(buf, 'e');
+ if (e) {
+ int exp = atoi(e+1);
+ sprintf(e, "e%+d", exp);
+ }
+ js_pushstring(J, buf);
+ }
+}
+
+static void Np_toFixed(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ int width = js_tointeger(J, 1);
+ if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
+ numtostr(J, "%.*f", width, self->u.number);
+}
+
+static void Np_toExponential(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ int width = js_tointeger(J, 1);
+ if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
+ numtostr(J, "%.*e", width, self->u.number);
+}
+
+static void Np_toPrecision(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ int width = js_tointeger(J, 1);
+ if (self->type != JS_CNUMBER) js_typeerror(J, "not a number");
+ numtostr(J, "%.*g", width, self->u.number);
+}
+
+void jsB_initnumber(js_State *J)
+{
+ J->Number_prototype->u.number = 0;
+
+ js_pushobject(J, J->Number_prototype);
+ {
+ jsB_propf(J, "valueOf", Np_valueOf, 0);
+ jsB_propf(J, "toString", Np_toString, 1);
+ jsB_propf(J, "toLocaleString", Np_toString, 0);
+ jsB_propf(J, "toFixed", Np_toFixed, 1);
+ jsB_propf(J, "toExponential", Np_toExponential, 1);
+ jsB_propf(J, "toPrecision", Np_toPrecision, 1);
+ }
+ js_newcconstructor(J, jsB_Number, jsB_new_Number, "Number", 1);
+ {
+ jsB_propn(J, "MAX_VALUE", 1.7976931348623157e+308);
+ jsB_propn(J, "MIN_VALUE", 5e-324);
+ jsB_propn(J, "NaN", NAN);
+ jsB_propn(J, "NEGATIVE_INFINITY", -INFINITY);
+ jsB_propn(J, "POSITIVE_INFINITY", INFINITY);
+ }
+ js_defglobal(J, "Number", JS_DONTENUM);
+}
diff --git a/mujs/jsobject.c b/mujs/jsobject.c
new file mode 100644
index 0000000..51ed86e
--- /dev/null
+++ b/mujs/jsobject.c
@@ -0,0 +1,446 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+static void jsB_new_Object(js_State *J)
+{
+ if (js_gettop(J) == 1 || js_isundefined(J, 1) || js_isnull(J, 1))
+ js_newobject(J);
+ else
+ js_pushobject(J, js_toobject(J, 1));
+}
+
+static void jsB_Object(js_State *J)
+{
+ if (js_gettop(J) == 1 || js_isundefined(J, 1) || js_isnull(J, 1))
+ js_newobject(J);
+ else
+ js_pushobject(J, js_toobject(J, 1));
+}
+
+static void Op_toString(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ switch (self->type) {
+ case JS_COBJECT: js_pushliteral(J, "[object Object]"); break;
+ case JS_CARRAY: js_pushliteral(J, "[object Array]"); break;
+ case JS_CFUNCTION: js_pushliteral(J, "[object Function]"); break;
+ case JS_CSCRIPT: js_pushliteral(J, "[object Function]"); break;
+ case JS_CCFUNCTION: js_pushliteral(J, "[object Function]"); break;
+ case JS_CERROR: js_pushliteral(J, "[object Error]"); break;
+ case JS_CBOOLEAN: js_pushliteral(J, "[object Boolean]"); break;
+ case JS_CNUMBER: js_pushliteral(J, "[object Number]"); break;
+ case JS_CSTRING: js_pushliteral(J, "[object String]"); break;
+ case JS_CREGEXP: js_pushliteral(J, "[object RegExp]"); break;
+ case JS_CDATE: js_pushliteral(J, "[object Date]"); break;
+ case JS_CMATH: js_pushliteral(J, "[object Math]"); break;
+ case JS_CJSON: js_pushliteral(J, "[object JSON]"); break;
+ case JS_CITERATOR: js_pushliteral(J, "[Iterator]"); break;
+ case JS_CUSERDATA:
+ js_pushliteral(J, "[object ");
+ js_pushliteral(J, self->u.user.tag);
+ js_concat(J);
+ js_pushliteral(J, "]");
+ js_concat(J);
+ break;
+ }
+}
+
+static void Op_valueOf(js_State *J)
+{
+ js_copy(J, 0);
+}
+
+static void Op_hasOwnProperty(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ const char *name = js_tostring(J, 1);
+ js_Property *ref = jsV_getownproperty(J, self, name);
+ js_pushboolean(J, ref != NULL);
+}
+
+static void Op_isPrototypeOf(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ if (js_isobject(J, 1)) {
+ js_Object *V = js_toobject(J, 1);
+ do {
+ V = V->prototype;
+ if (V == self) {
+ js_pushboolean(J, 1);
+ return;
+ }
+ } while (V);
+ }
+ js_pushboolean(J, 0);
+}
+
+static void Op_propertyIsEnumerable(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ const char *name = js_tostring(J, 1);
+ js_Property *ref = jsV_getownproperty(J, self, name);
+ js_pushboolean(J, ref && !(ref->atts & JS_DONTENUM));
+}
+
+static void O_getPrototypeOf(js_State *J)
+{
+ js_Object *obj;
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+ obj = js_toobject(J, 1);
+ if (obj->prototype)
+ js_pushobject(J, obj->prototype);
+ else
+ js_pushnull(J);
+}
+
+static void O_getOwnPropertyDescriptor(js_State *J)
+{
+ js_Object *obj;
+ js_Property *ref;
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+ obj = js_toobject(J, 1);
+ ref = jsV_getproperty(J, obj, js_tostring(J, 2));
+ if (!ref)
+ js_pushundefined(J);
+ else {
+ js_newobject(J);
+ if (!ref->getter && !ref->setter) {
+ js_pushvalue(J, ref->value);
+ js_setproperty(J, -2, "value");
+ js_pushboolean(J, !(ref->atts & JS_READONLY));
+ js_setproperty(J, -2, "writable");
+ } else {
+ if (ref->getter)
+ js_pushobject(J, ref->getter);
+ else
+ js_pushundefined(J);
+ js_setproperty(J, -2, "get");
+ if (ref->setter)
+ js_pushobject(J, ref->setter);
+ else
+ js_pushundefined(J);
+ js_setproperty(J, -2, "set");
+ }
+ js_pushboolean(J, !(ref->atts & JS_DONTENUM));
+ js_setproperty(J, -2, "enumerable");
+ js_pushboolean(J, !(ref->atts & JS_DONTCONF));
+ js_setproperty(J, -2, "configurable");
+ }
+}
+
+static void O_getOwnPropertyNames(js_State *J)
+{
+ js_Object *obj;
+ js_Property *ref;
+ unsigned int k;
+ int i;
+
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+ obj = js_toobject(J, 1);
+
+ js_newarray(J);
+
+ i = 0;
+ for (ref = obj->head; ref; ref = ref->next) {
+ js_pushliteral(J, ref->name);
+ js_setindex(J, -2, i++);
+ }
+
+ if (obj->type == JS_CARRAY) {
+ js_pushliteral(J, "length");
+ js_setindex(J, -2, i++);
+ }
+
+ if (obj->type == JS_CSTRING) {
+ js_pushliteral(J, "length");
+ js_setindex(J, -2, i++);
+ for (k = 0; k < obj->u.s.length; ++k) {
+ js_pushnumber(J, k);
+ js_setindex(J, -2, i++);
+ }
+ }
+
+ if (obj->type == JS_CREGEXP) {
+ js_pushliteral(J, "source");
+ js_setindex(J, -2, i++);
+ js_pushliteral(J, "global");
+ js_setindex(J, -2, i++);
+ js_pushliteral(J, "ignoreCase");
+ js_setindex(J, -2, i++);
+ js_pushliteral(J, "multiline");
+ js_setindex(J, -2, i++);
+ js_pushliteral(J, "lastIndex");
+ js_setindex(J, -2, i++);
+ }
+}
+
+static void ToPropertyDescriptor(js_State *J, js_Object *obj, const char *name, js_Object *desc)
+{
+ int haswritable = 0;
+ int hasvalue = 0;
+ int enumerable = 0;
+ int configurable = 0;
+ int writable = 0;
+ int atts = 0;
+
+ js_pushobject(J, obj);
+ js_pushobject(J, desc);
+
+ if (js_hasproperty(J, -1, "writable")) {
+ haswritable = 1;
+ writable = js_toboolean(J, -1);
+ js_pop(J, 1);
+ }
+ if (js_hasproperty(J, -1, "enumerable")) {
+ enumerable = js_toboolean(J, -1);
+ js_pop(J, 1);
+ }
+ if (js_hasproperty(J, -1, "configurable")) {
+ configurable = js_toboolean(J, -1);
+ js_pop(J, 1);
+ }
+ if (js_hasproperty(J, -1, "value")) {
+ hasvalue = 1;
+ js_setproperty(J, -3, name);
+ }
+
+ if (!writable) atts |= JS_READONLY;
+ if (!enumerable) atts |= JS_DONTENUM;
+ if (!configurable) atts |= JS_DONTCONF;
+
+ if (js_hasproperty(J, -1, "get")) {
+ if (haswritable || hasvalue)
+ js_typeerror(J, "value/writable and get/set attributes are exclusive");
+ } else {
+ js_pushundefined(J);
+ }
+
+ if (js_hasproperty(J, -2, "set")) {
+ if (haswritable || hasvalue)
+ js_typeerror(J, "value/writable and get/set attributes are exclusive");
+ } else {
+ js_pushundefined(J);
+ }
+
+ js_defaccessor(J, -4, name, atts);
+
+ js_pop(J, 2);
+}
+
+static void O_defineProperty(js_State *J)
+{
+ if (!js_isobject(J, 1)) js_typeerror(J, "not an object");
+ if (!js_isobject(J, 3)) js_typeerror(J, "not an object");
+ ToPropertyDescriptor(J, js_toobject(J, 1), js_tostring(J, 2), js_toobject(J, 3));
+ js_copy(J, 1);
+}
+
+static void O_defineProperties(js_State *J)
+{
+ js_Object *props;
+ js_Property *ref;
+
+ if (!js_isobject(J, 1)) js_typeerror(J, "not an object");
+ if (!js_isobject(J, 2)) js_typeerror(J, "not an object");
+
+ props = js_toobject(J, 2);
+ for (ref = props->head; ref; ref = ref->next) {
+ if (!(ref->atts & JS_DONTENUM)) {
+ js_pushvalue(J, ref->value);
+ ToPropertyDescriptor(J, js_toobject(J, 1), ref->name, js_toobject(J, -1));
+ js_pop(J, 1);
+ }
+ }
+
+ js_copy(J, 1);
+}
+
+static void O_create(js_State *J)
+{
+ js_Object *obj;
+ js_Object *proto;
+ js_Object *props;
+ js_Property *ref;
+
+ if (js_isobject(J, 1))
+ proto = js_toobject(J, 1);
+ else if (js_isnull(J, 1))
+ proto = NULL;
+ else
+ js_typeerror(J, "not an object or null");
+
+ obj = jsV_newobject(J, JS_COBJECT, proto);
+ js_pushobject(J, obj);
+
+ if (js_isdefined(J, 2)) {
+ if (!js_isobject(J, 2)) js_typeerror(J, "not an object");
+ props = js_toobject(J, 2);
+ for (ref = props->head; ref; ref = ref->next) {
+ if (!(ref->atts & JS_DONTENUM)) {
+ if (ref->value.type != JS_TOBJECT) js_typeerror(J, "not an object");
+ ToPropertyDescriptor(J, obj, ref->name, ref->value.u.object);
+ }
+ }
+ }
+}
+
+static void O_keys(js_State *J)
+{
+ js_Object *obj;
+ js_Property *ref;
+ unsigned int k;
+ int i;
+
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+ obj = js_toobject(J, 1);
+
+ js_newarray(J);
+
+ i = 0;
+ for (ref = obj->head; ref; ref = ref->next) {
+ if (!(ref->atts & JS_DONTENUM)) {
+ js_pushliteral(J, ref->name);
+ js_setindex(J, -2, i++);
+ }
+ }
+
+ if (obj->type == JS_CSTRING) {
+ for (k = 0; k < obj->u.s.length; ++k) {
+ js_pushnumber(J, k);
+ js_setindex(J, -2, i++);
+ }
+ }
+}
+
+static void O_preventExtensions(js_State *J)
+{
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+ js_toobject(J, 1)->extensible = 0;
+ js_copy(J, 1);
+}
+
+static void O_isExtensible(js_State *J)
+{
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+ js_pushboolean(J, js_toobject(J, 1)->extensible);
+}
+
+static void O_seal(js_State *J)
+{
+ js_Object *obj;
+ js_Property *ref;
+
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+
+ obj = js_toobject(J, 1);
+ obj->extensible = 0;
+
+ for (ref = obj->head; ref; ref = ref->next)
+ ref->atts |= JS_DONTCONF;
+
+ js_copy(J, 1);
+}
+
+static void O_isSealed(js_State *J)
+{
+ js_Object *obj;
+ js_Property *ref;
+
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+
+ obj = js_toobject(J, 1);
+ if (obj->extensible) {
+ js_pushboolean(J, 0);
+ return;
+ }
+
+ for (ref = obj->head; ref; ref = ref->next) {
+ if (!(ref->atts & JS_DONTCONF)) {
+ js_pushboolean(J, 0);
+ return;
+ }
+ }
+
+ js_pushboolean(J, 1);
+}
+
+static void O_freeze(js_State *J)
+{
+ js_Object *obj;
+ js_Property *ref;
+
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+
+ obj = js_toobject(J, 1);
+ obj->extensible = 0;
+
+ for (ref = obj->head; ref; ref = ref->next)
+ ref->atts |= JS_READONLY | JS_DONTCONF;
+
+ js_copy(J, 1);
+}
+
+static void O_isFrozen(js_State *J)
+{
+ js_Object *obj;
+ js_Property *ref;
+
+ if (!js_isobject(J, 1))
+ js_typeerror(J, "not an object");
+
+ obj = js_toobject(J, 1);
+ if (obj->extensible) {
+ js_pushboolean(J, 0);
+ return;
+ }
+
+ for (ref = obj->head; ref; ref = ref->next) {
+ if (!(ref->atts & (JS_READONLY | JS_DONTCONF))) {
+ js_pushboolean(J, 0);
+ return;
+ }
+ }
+
+ js_pushboolean(J, 1);
+}
+
+void jsB_initobject(js_State *J)
+{
+ js_pushobject(J, J->Object_prototype);
+ {
+ jsB_propf(J, "toString", Op_toString, 0);
+ jsB_propf(J, "toLocaleString", Op_toString, 0);
+ jsB_propf(J, "valueOf", Op_valueOf, 0);
+ jsB_propf(J, "hasOwnProperty", Op_hasOwnProperty, 1);
+ jsB_propf(J, "isPrototypeOf", Op_isPrototypeOf, 1);
+ jsB_propf(J, "propertyIsEnumerable", Op_propertyIsEnumerable, 1);
+ }
+ js_newcconstructor(J, jsB_Object, jsB_new_Object, "Object", 1);
+ {
+ /* ES5 */
+ jsB_propf(J, "getPrototypeOf", O_getPrototypeOf, 1);
+ jsB_propf(J, "getOwnPropertyDescriptor", O_getOwnPropertyDescriptor, 2);
+ jsB_propf(J, "getOwnPropertyNames", O_getOwnPropertyNames, 1);
+ jsB_propf(J, "create", O_create, 2);
+ jsB_propf(J, "defineProperty", O_defineProperty, 3);
+ jsB_propf(J, "defineProperties", O_defineProperties, 2);
+ jsB_propf(J, "seal", O_seal, 1);
+ jsB_propf(J, "freeze", O_freeze, 1);
+ jsB_propf(J, "preventExtensions", O_preventExtensions, 1);
+ jsB_propf(J, "isSealed", O_isSealed, 1);
+ jsB_propf(J, "isFrozen", O_isFrozen, 1);
+ jsB_propf(J, "isExtensible", O_isExtensible, 1);
+ jsB_propf(J, "keys", O_keys, 1);
+ }
+ js_defglobal(J, "Object", JS_DONTENUM);
+}
diff --git a/mujs/json.c b/mujs/json.c
new file mode 100644
index 0000000..69c6407
--- /dev/null
+++ b/mujs/json.c
@@ -0,0 +1,302 @@
+#include "jsi.h"
+#include "jslex.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+
+#include "utf.h"
+
+static void jsonnext(js_State *J)
+{
+ J->lookahead = jsY_lexjson(J);
+}
+
+static int jsonaccept(js_State *J, int t)
+{
+ if (J->lookahead == t) {
+ jsonnext(J);
+ return 1;
+ }
+ return 0;
+}
+
+static void jsonexpect(js_State *J, int t)
+{
+ if (!jsonaccept(J, t))
+ js_syntaxerror(J, "JSON: unexpected token: %s (expected %s)",
+ jsY_tokenstring(J->lookahead), jsY_tokenstring(t));
+}
+
+static void jsonvalue(js_State *J)
+{
+ int i;
+ const char *name;
+
+ switch (J->lookahead) {
+ case TK_STRING:
+ js_pushliteral(J, J->text);
+ jsonnext(J);
+ break;
+
+ case TK_NUMBER:
+ js_pushnumber(J, J->number);
+ jsonnext(J);
+ break;
+
+ case '{':
+ js_newobject(J);
+ jsonnext(J);
+ if (jsonaccept(J, '}'))
+ return;
+ do {
+ if (J->lookahead != TK_STRING)
+ js_syntaxerror(J, "JSON: unexpected token: %s (expected string)", jsY_tokenstring(J->lookahead));
+ name = J->text;
+ jsonnext(J);
+ jsonexpect(J, ':');
+ jsonvalue(J);
+ js_setproperty(J, -2, name);
+ } while (jsonaccept(J, ','));
+ jsonexpect(J, '}');
+ break;
+
+ case '[':
+ js_newarray(J);
+ jsonnext(J);
+ i = 0;
+ if (jsonaccept(J, ']'))
+ return;
+ do {
+ jsonvalue(J);
+ js_setindex(J, -2, i++);
+ } while (jsonaccept(J, ','));
+ jsonexpect(J, ']');
+ break;
+
+ case TK_TRUE:
+ js_pushboolean(J, 1);
+ jsonnext(J);
+ break;
+
+ case TK_FALSE:
+ js_pushboolean(J, 0);
+ jsonnext(J);
+ break;
+
+ case TK_NULL:
+ js_pushnull(J);
+ jsonnext(J);
+ break;
+
+ default:
+ js_syntaxerror(J, "JSON: unexpected token: %s", jsY_tokenstring(J->lookahead));
+ }
+}
+
+static void JSON_parse(js_State *J)
+{
+ const char *source = js_tostring(J, 1);
+ jsY_initlex(J, "JSON", source);
+ jsonnext(J);
+ jsonvalue(J);
+ // TODO: reviver Walk()
+}
+
+static void fmtnum(js_State *J, js_Buffer **sb, double n)
+{
+ if (isnan(n)) js_puts(J, sb, "null");
+ else if (isinf(n)) js_puts(J, sb, "null");
+ else if (n == 0) js_puts(J, sb, "0");
+ else {
+ char buf[40];
+ sprintf(buf, "%.17g", n);
+ js_puts(J, sb, buf);
+ }
+}
+
+static void fmtstr(js_State *J, js_Buffer **sb, const char *s)
+{
+ static const char *HEX = "0123456789ABCDEF";
+ Rune c;
+ js_putc(J, sb, '"');
+ while (*s) {
+ s += chartorune(&c, s);
+ switch (c) {
+ case '"': js_puts(J, sb, "\\\""); break;
+ case '\\': js_puts(J, sb, "\\\\"); break;
+ case '\b': js_puts(J, sb, "\\b"); break;
+ case '\f': js_puts(J, sb, "\\f"); break;
+ case '\n': js_puts(J, sb, "\\n"); break;
+ case '\r': js_puts(J, sb, "\\r"); break;
+ case '\t': js_puts(J, sb, "\\t"); break;
+ default:
+ if (c < ' ') {
+ js_puts(J, sb, "\\u");
+ js_putc(J, sb, HEX[(c>>12)&15]);
+ js_putc(J, sb, HEX[(c>>8)&15]);
+ js_putc(J, sb, HEX[(c>>4)&15]);
+ js_putc(J, sb, HEX[c&15]);
+ } else {
+ js_putc(J, sb, c); break;
+ }
+ }
+ }
+ js_putc(J, sb, '"');
+}
+
+static void fmtindent(js_State *J, js_Buffer **sb, const char *gap, int level)
+{
+ js_putc(J, sb, '\n');
+ while (level--)
+ js_puts(J, sb, gap);
+}
+
+static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level);
+
+static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj, const char *gap, int level)
+{
+ js_Property *ref;
+ int save;
+ int n = 0;
+
+ js_putc(J, sb, '{');
+ for (ref = obj->head; ref; ref = ref->next) {
+ if (ref->atts & JS_DONTENUM)
+ continue;
+ save = (*sb)->n;
+ if (n) js_putc(J, sb, ',');
+ if (gap) fmtindent(J, sb, gap, level + 1);
+ fmtstr(J, sb, ref->name);
+ js_putc(J, sb, ':');
+ if (gap)
+ js_putc(J, sb, ' ');
+ js_pushvalue(J, ref->value);
+ if (!fmtvalue(J, sb, ref->name, gap, level + 1))
+ (*sb)->n = save;
+ else
+ ++n;
+ js_pop(J, 1);
+ }
+ if (gap && n) fmtindent(J, sb, gap, level);
+ js_putc(J, sb, '}');
+}
+
+static void fmtarray(js_State *J, js_Buffer **sb, const char *gap, int level)
+{
+ unsigned int n, k;
+ char buf[32];
+
+ n = js_getlength(J, -1);
+
+ js_putc(J, sb, '[');
+ for (k = 0; k < n; ++k) {
+ if (k) js_putc(J, sb, ',');
+ if (gap) fmtindent(J, sb, gap, level + 1);
+ js_itoa(buf, k);
+ js_getproperty(J, -1, buf);
+ if (!fmtvalue(J, sb, js_intern(J, buf), gap, level + 1))
+ js_puts(J, sb, "null");
+ js_pop(J, 1);
+ }
+ if (gap && n) fmtindent(J, sb, gap, level);
+ js_putc(J, sb, ']');
+}
+
+static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level)
+{
+ if (js_try(J)) {
+ js_free(J, *sb);
+ js_throw(J);
+ }
+ if (js_isobject(J, -1)) {
+ if (js_hasproperty(J, -1, "toJSON")) {
+ if (js_iscallable(J, -1)) {
+ js_copy(J, -2);
+ js_pushliteral(J, key);
+ js_call(J, 1);
+ js_rot2pop1(J);
+ } else {
+ js_pop(J, 1);
+ }
+ }
+ }
+ js_endtry(J);
+
+ // TODO: replacer()
+
+ if (js_isobject(J, -1) && !js_iscallable(J, -1)) {
+ js_Object *obj = js_toobject(J, -1);
+ switch (obj->type) {
+ case JS_CNUMBER: fmtnum(J, sb, obj->u.number); break;
+ case JS_CSTRING: fmtstr(J, sb, obj->u.s.string); break;
+ case JS_CBOOLEAN: js_puts(J, sb, obj->u.boolean ? "true" : "false"); break;
+ case JS_CARRAY: fmtarray(J, sb, gap, level); break;
+ default: fmtobject(J, sb, obj, gap, level); break;
+ }
+ }
+ else if (js_isboolean(J, -1))
+ js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false");
+ else if (js_isnumber(J, -1))
+ fmtnum(J, sb, js_tonumber(J, -1));
+ else if (js_isstring(J, -1))
+ fmtstr(J, sb, js_tostring(J, -1));
+ else if (js_isnull(J, -1))
+ js_puts(J, sb, "null");
+ else
+ return 0;
+
+ return 1;
+}
+
+static void JSON_stringify(js_State *J)
+{
+ js_Buffer *sb = NULL;
+ char buf[12];
+ const char *s, *gap;
+ int n;
+
+ gap = NULL;
+
+ if (js_isnumber(J, 3)) {
+ n = js_tointeger(J, 3);
+ if (n < 0) n = 0;
+ if (n > 10) n = 10;
+ memset(buf, ' ', n);
+ buf[n] = 0;
+ if (n > 0) gap = buf;
+ } else if (js_isstring(J, 3)) {
+ s = js_tostring(J, 3);
+ n = strlen(s);
+ if (n > 10) n = 10;
+ memcpy(buf, s, n);
+ buf[n] = 0;
+ if (n > 0) gap = buf;
+ }
+
+ // TODO: replacer
+
+ if (js_isdefined(J, 1)) {
+ js_copy(J, 1);
+ if (fmtvalue(J, &sb, "", gap, 0)) {
+ js_putc(J, &sb, 0);
+ if (js_try(J)) {
+ js_free(J, sb);
+ js_throw(J);
+ }
+ js_pushstring(J, sb ? sb->s : "");
+ js_endtry(J);
+ js_free(J, sb);
+ }
+ } else {
+ js_pushundefined(J);
+ }
+}
+
+void jsB_initjson(js_State *J)
+{
+ js_pushobject(J, jsV_newobject(J, JS_CJSON, J->Object_prototype));
+ {
+ jsB_propf(J, "parse", JSON_parse, 2);
+ jsB_propf(J, "stringify", JSON_stringify, 3);
+ }
+ js_defglobal(J, "JSON", JS_DONTENUM);
+}
diff --git a/mujs/jsparse.c b/mujs/jsparse.c
new file mode 100644
index 0000000..31c2e20
--- /dev/null
+++ b/mujs/jsparse.c
@@ -0,0 +1,959 @@
+#include "jsi.h"
+#include "jslex.h"
+#include "jsparse.h"
+
+#define LIST(h) jsP_newnode(J, AST_LIST, h, 0, 0, 0)
+
+#define EXP0(x) jsP_newnode(J, EXP_ ## x, 0, 0, 0, 0)
+#define EXP1(x,a) jsP_newnode(J, EXP_ ## x, a, 0, 0, 0)
+#define EXP2(x,a,b) jsP_newnode(J, EXP_ ## x, a, b, 0, 0)
+#define EXP3(x,a,b,c) jsP_newnode(J, EXP_ ## x, a, b, c, 0)
+
+#define STM0(x) jsP_newnode(J, STM_ ## x, 0, 0, 0, 0)
+#define STM1(x,a) jsP_newnode(J, STM_ ## x, a, 0, 0, 0)
+#define STM2(x,a,b) jsP_newnode(J, STM_ ## x, a, b, 0, 0)
+#define STM3(x,a,b,c) jsP_newnode(J, STM_ ## x, a, b, c, 0)
+#define STM4(x,a,b,c,d) jsP_newnode(J, STM_ ## x, a, b, c, d)
+
+static js_Ast *expression(js_State *J, int notin);
+static js_Ast *assignment(js_State *J, int notin);
+static js_Ast *memberexp(js_State *J);
+static js_Ast *statement(js_State *J);
+static js_Ast *funbody(js_State *J);
+
+JS_NORETURN static void jsP_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+
+static void jsP_error(js_State *J, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[512];
+ char msgbuf[256];
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, 256, fmt, ap);
+ va_end(ap);
+
+ snprintf(buf, 256, "%s:%d: ", J->filename, J->lexline);
+ strcat(buf, msgbuf);
+
+ js_newsyntaxerror(J, buf);
+ js_throw(J);
+}
+
+static void jsP_warning(js_State *J, const char *fmt, ...)
+{
+ va_list ap;
+ fprintf(stderr, "%s:%d: warning: ", J->filename, J->lexline);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static js_Ast *jsP_newnode(js_State *J, int type, js_Ast *a, js_Ast *b, js_Ast *c, js_Ast *d)
+{
+ js_Ast *node = js_malloc(J, sizeof *node);
+
+ node->type = type;
+ node->line = J->astline;
+ node->a = a;
+ node->b = b;
+ node->c = c;
+ node->d = d;
+ node->number = 0;
+ node->string = NULL;
+ node->jumps = NULL;
+ node->casejump = 0;
+
+ node->parent = NULL;
+ if (a) a->parent = node;
+ if (b) b->parent = node;
+ if (c) c->parent = node;
+ if (d) d->parent = node;
+
+ node->gcnext = J->gcast;
+ J->gcast = node;
+
+ return node;
+}
+
+static js_Ast *jsP_list(js_Ast *head)
+{
+ /* set parent pointers in list nodes */
+ js_Ast *prev = head, *node = head->b;
+ while (node) {
+ node->parent = prev;
+ prev = node;
+ node = node->b;
+ }
+ return head;
+}
+
+static js_Ast *jsP_newstrnode(js_State *J, int type, const char *s)
+{
+ js_Ast *node = jsP_newnode(J, type, 0, 0, 0, 0);
+ node->string = s;
+ return node;
+}
+
+static js_Ast *jsP_newnumnode(js_State *J, int type, double n)
+{
+ js_Ast *node = jsP_newnode(J, type, 0, 0, 0, 0);
+ node->number = n;
+ return node;
+}
+
+static void jsP_freejumps(js_State *J, js_JumpList *node)
+{
+ while (node) {
+ js_JumpList *next = node->next;
+ js_free(J, node);
+ node = next;
+ }
+}
+
+void jsP_freeparse(js_State *J)
+{
+ js_Ast *node = J->gcast;
+ while (node) {
+ js_Ast *next = node->gcnext;
+ jsP_freejumps(J, node->jumps);
+ js_free(J, node);
+ node = next;
+ }
+ J->gcast = NULL;
+}
+
+/* Lookahead */
+
+static void jsP_next(js_State *J)
+{
+ J->astline = J->lexline;
+ J->lookahead = jsY_lex(J);
+}
+
+#define jsP_accept(J,x) (J->lookahead == x ? (jsP_next(J), 1) : 0)
+
+#define jsP_expect(J,x) if (!jsP_accept(J, x)) jsP_error(J, "unexpected token: %s (expected %s)", jsY_tokenstring(J->lookahead), jsY_tokenstring(x))
+
+static void semicolon(js_State *J)
+{
+ if (J->lookahead == ';') {
+ jsP_next(J);
+ return;
+ }
+ if (J->newline || J->lookahead == '}' || J->lookahead == 0)
+ return;
+ jsP_error(J, "unexpected token: %s (expected ';')", jsY_tokenstring(J->lookahead));
+}
+
+/* Literals */
+
+static const char *futurewords[] = {
+ "class", "const", "enum", "export", "extends", "import", "super",
+};
+
+static const char *strictfuturewords[] = {
+ "implements", "interface", "let", "package", "private", "protected",
+ "public", "static", "yield",
+};
+
+static void checkfutureword(js_State *J, const char *s)
+{
+ if (jsY_findword(s, futurewords, nelem(futurewords)) >= 0)
+ jsP_error(J, "'%s' is a future reserved word", s);
+ if (J->strict && jsY_findword(s, strictfuturewords, nelem(strictfuturewords)) >= 0)
+ jsP_error(J, "'%s' is a strict mode future reserved word", s);
+}
+
+static js_Ast *identifier(js_State *J)
+{
+ js_Ast *a;
+ if (J->lookahead == TK_IDENTIFIER) {
+ checkfutureword(J, J->text);
+ a = jsP_newstrnode(J, AST_IDENTIFIER, J->text);
+ jsP_next(J);
+ return a;
+ }
+ jsP_error(J, "unexpected token: %s (expected identifier)", jsY_tokenstring(J->lookahead));
+}
+
+static js_Ast *identifieropt(js_State *J)
+{
+ if (J->lookahead == TK_IDENTIFIER)
+ return identifier(J);
+ return NULL;
+}
+
+static js_Ast *identifiername(js_State *J)
+{
+ if (J->lookahead == TK_IDENTIFIER || J->lookahead >= TK_BREAK) {
+ js_Ast *a = jsP_newstrnode(J, AST_IDENTIFIER, J->text);
+ jsP_next(J);
+ return a;
+ }
+ jsP_error(J, "unexpected token: %s (expected identifier or keyword)", jsY_tokenstring(J->lookahead));
+}
+
+static js_Ast *arrayelement(js_State *J)
+{
+ if (J->lookahead == ',')
+ return EXP0(UNDEF);
+ return assignment(J, 0);
+}
+
+static js_Ast *arrayliteral(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == ']')
+ return NULL;
+ head = tail = LIST(arrayelement(J));
+ while (jsP_accept(J, ',')) {
+ if (J->lookahead != ']')
+ tail = tail->b = LIST(arrayelement(J));
+ }
+ return jsP_list(head);
+}
+
+static js_Ast *propname(js_State *J)
+{
+ js_Ast *name;
+ if (J->lookahead == TK_NUMBER) {
+ name = jsP_newnumnode(J, EXP_NUMBER, J->number);
+ jsP_next(J);
+ } else if (J->lookahead == TK_STRING) {
+ name = jsP_newstrnode(J, EXP_STRING, J->text);
+ jsP_next(J);
+ } else {
+ name = identifiername(J);
+ }
+ return name;
+}
+
+static js_Ast *propassign(js_State *J)
+{
+ js_Ast *name, *value, *arg, *body;
+
+ name = propname(J);
+
+ if (J->lookahead != ':' && name->type == AST_IDENTIFIER) {
+ if (!strcmp(name->string, "get")) {
+ name = propname(J);
+ jsP_expect(J, '(');
+ jsP_expect(J, ')');
+ body = funbody(J);
+ return EXP3(PROP_GET, name, NULL, body);
+ }
+ if (!strcmp(name->string, "set")) {
+ name = propname(J);
+ jsP_expect(J, '(');
+ arg = identifier(J);
+ jsP_expect(J, ')');
+ body = funbody(J);
+ return EXP3(PROP_SET, name, LIST(arg), body);
+ }
+ }
+
+ jsP_expect(J, ':');
+ value = assignment(J, 0);
+ return EXP2(PROP_VAL, name, value);
+}
+
+static js_Ast *objectliteral(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == '}')
+ return NULL;
+ head = tail = LIST(propassign(J));
+ while (jsP_accept(J, ',')) {
+ if (J->lookahead == '}')
+ break;
+ tail = tail->b = LIST(propassign(J));
+ }
+ return jsP_list(head);
+}
+
+/* Functions */
+
+static js_Ast *parameters(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == ')')
+ return NULL;
+ head = tail = LIST(identifier(J));
+ while (jsP_accept(J, ',')) {
+ tail = tail->b = LIST(identifier(J));
+ }
+ return jsP_list(head);
+}
+
+static js_Ast *fundec(js_State *J)
+{
+ js_Ast *a, *b, *c;
+ a = identifier(J);
+ jsP_expect(J, '(');
+ b = parameters(J);
+ jsP_expect(J, ')');
+ c = funbody(J);
+ return jsP_newnode(J, AST_FUNDEC, a, b, c, 0);
+}
+
+static js_Ast *funstm(js_State *J)
+{
+ js_Ast *a, *b, *c;
+ a = identifier(J);
+ jsP_expect(J, '(');
+ b = parameters(J);
+ jsP_expect(J, ')');
+ c = funbody(J);
+ /* rewrite function statement as "var X = function X() {}" */
+ return STM1(VAR, LIST(EXP2(VAR, a, EXP3(FUN, a, b, c))));
+}
+
+static js_Ast *funexp(js_State *J)
+{
+ js_Ast *a, *b, *c;
+ a = identifieropt(J);
+ jsP_expect(J, '(');
+ b = parameters(J);
+ jsP_expect(J, ')');
+ c = funbody(J);
+ return EXP3(FUN, a, b, c);
+}
+
+/* Expressions */
+
+static js_Ast *primary(js_State *J)
+{
+ js_Ast *a;
+
+ if (J->lookahead == TK_IDENTIFIER) {
+ checkfutureword(J, J->text);
+ a = jsP_newstrnode(J, EXP_IDENTIFIER, J->text);
+ jsP_next(J);
+ return a;
+ }
+ if (J->lookahead == TK_STRING) {
+ a = jsP_newstrnode(J, EXP_STRING, J->text);
+ jsP_next(J);
+ return a;
+ }
+ if (J->lookahead == TK_REGEXP) {
+ a = jsP_newstrnode(J, EXP_REGEXP, J->text);
+ a->number = J->number;
+ jsP_next(J);
+ return a;
+ }
+ if (J->lookahead == TK_NUMBER) {
+ a = jsP_newnumnode(J, EXP_NUMBER, J->number);
+ jsP_next(J);
+ return a;
+ }
+
+ if (jsP_accept(J, TK_THIS)) return EXP0(THIS);
+ if (jsP_accept(J, TK_NULL)) return EXP0(NULL);
+ if (jsP_accept(J, TK_TRUE)) return EXP0(TRUE);
+ if (jsP_accept(J, TK_FALSE)) return EXP0(FALSE);
+ if (jsP_accept(J, '{')) { a = EXP1(OBJECT, objectliteral(J)); jsP_expect(J, '}'); return a; }
+ if (jsP_accept(J, '[')) { a = EXP1(ARRAY, arrayliteral(J)); jsP_expect(J, ']'); return a; }
+ if (jsP_accept(J, '(')) { a = expression(J, 0); jsP_expect(J, ')'); return a; }
+
+ jsP_error(J, "unexpected token in expression: %s", jsY_tokenstring(J->lookahead));
+}
+
+static js_Ast *arguments(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == ')')
+ return NULL;
+ head = tail = LIST(assignment(J, 0));
+ while (jsP_accept(J, ',')) {
+ tail = tail->b = LIST(assignment(J, 0));
+ }
+ return jsP_list(head);
+}
+
+static js_Ast *newexp(js_State *J)
+{
+ js_Ast *a, *b;
+
+ if (jsP_accept(J, TK_NEW)) {
+ a = memberexp(J);
+ if (jsP_accept(J, '(')) {
+ b = arguments(J);
+ jsP_expect(J, ')');
+ return EXP2(NEW, a, b);
+ }
+ return EXP1(NEW, a);
+ }
+
+ if (jsP_accept(J, TK_FUNCTION))
+ return funexp(J);
+
+ return primary(J);
+}
+
+static js_Ast *memberexp(js_State *J)
+{
+ js_Ast *a = newexp(J);
+loop:
+ if (jsP_accept(J, '.')) { a = EXP2(MEMBER, a, identifiername(J)); goto loop; }
+ if (jsP_accept(J, '[')) { a = EXP2(INDEX, a, expression(J, 0)); jsP_expect(J, ']'); goto loop; }
+ return a;
+}
+
+static js_Ast *callexp(js_State *J)
+{
+ js_Ast *a = newexp(J);
+loop:
+ if (jsP_accept(J, '.')) { a = EXP2(MEMBER, a, identifiername(J)); goto loop; }
+ if (jsP_accept(J, '[')) { a = EXP2(INDEX, a, expression(J, 0)); jsP_expect(J, ']'); goto loop; }
+ if (jsP_accept(J, '(')) { a = EXP2(CALL, a, arguments(J)); jsP_expect(J, ')'); goto loop; }
+ return a;
+}
+
+static js_Ast *postfix(js_State *J)
+{
+ js_Ast *a = callexp(J);
+ if (!J->newline && jsP_accept(J, TK_INC)) return EXP1(POSTINC, a);
+ if (!J->newline && jsP_accept(J, TK_DEC)) return EXP1(POSTDEC, a);
+ return a;
+}
+
+static js_Ast *unary(js_State *J)
+{
+ if (jsP_accept(J, TK_DELETE)) return EXP1(DELETE, unary(J));
+ if (jsP_accept(J, TK_VOID)) return EXP1(VOID, unary(J));
+ if (jsP_accept(J, TK_TYPEOF)) return EXP1(TYPEOF, unary(J));
+ if (jsP_accept(J, TK_INC)) return EXP1(PREINC, unary(J));
+ if (jsP_accept(J, TK_DEC)) return EXP1(PREDEC, unary(J));
+ if (jsP_accept(J, '+')) return EXP1(POS, unary(J));
+ if (jsP_accept(J, '-')) return EXP1(NEG, unary(J));
+ if (jsP_accept(J, '~')) return EXP1(BITNOT, unary(J));
+ if (jsP_accept(J, '!')) return EXP1(LOGNOT, unary(J));
+ return postfix(J);
+}
+
+static js_Ast *multiplicative(js_State *J)
+{
+ js_Ast *a = unary(J);
+loop:
+ if (jsP_accept(J, '*')) { a = EXP2(MUL, a, unary(J)); goto loop; }
+ if (jsP_accept(J, '/')) { a = EXP2(DIV, a, unary(J)); goto loop; }
+ if (jsP_accept(J, '%')) { a = EXP2(MOD, a, unary(J)); goto loop; }
+ return a;
+}
+
+static js_Ast *additive(js_State *J)
+{
+ js_Ast *a = multiplicative(J);
+loop:
+ if (jsP_accept(J, '+')) { a = EXP2(ADD, a, multiplicative(J)); goto loop; }
+ if (jsP_accept(J, '-')) { a = EXP2(SUB, a, multiplicative(J)); goto loop; }
+ return a;
+}
+
+static js_Ast *shift(js_State *J)
+{
+ js_Ast *a = additive(J);
+loop:
+ if (jsP_accept(J, TK_SHL)) { a = EXP2(SHL, a, additive(J)); goto loop; }
+ if (jsP_accept(J, TK_SHR)) { a = EXP2(SHR, a, additive(J)); goto loop; }
+ if (jsP_accept(J, TK_USHR)) { a = EXP2(USHR, a, additive(J)); goto loop; }
+ return a;
+}
+
+static js_Ast *relational(js_State *J, int notin)
+{
+ js_Ast *a = shift(J);
+loop:
+ if (jsP_accept(J, '<')) { a = EXP2(LT, a, shift(J)); goto loop; }
+ if (jsP_accept(J, '>')) { a = EXP2(GT, a, shift(J)); goto loop; }
+ if (jsP_accept(J, TK_LE)) { a = EXP2(LE, a, shift(J)); goto loop; }
+ if (jsP_accept(J, TK_GE)) { a = EXP2(GE, a, shift(J)); goto loop; }
+ if (jsP_accept(J, TK_INSTANCEOF)) { a = EXP2(INSTANCEOF, a, shift(J)); goto loop; }
+ if (!notin && jsP_accept(J, TK_IN)) { a = EXP2(IN, a, shift(J)); goto loop; }
+ return a;
+}
+
+static js_Ast *equality(js_State *J, int notin)
+{
+ js_Ast *a = relational(J, notin);
+loop:
+ if (jsP_accept(J, TK_EQ)) { a = EXP2(EQ, a, relational(J, notin)); goto loop; }
+ if (jsP_accept(J, TK_NE)) { a = EXP2(NE, a, relational(J, notin)); goto loop; }
+ if (jsP_accept(J, TK_STRICTEQ)) { a = EXP2(STRICTEQ, a, relational(J, notin)); goto loop; }
+ if (jsP_accept(J, TK_STRICTNE)) { a = EXP2(STRICTNE, a, relational(J, notin)); goto loop; }
+ return a;
+}
+
+static js_Ast *bitand(js_State *J, int notin)
+{
+ js_Ast *a = equality(J, notin);
+ while (jsP_accept(J, '&'))
+ a = EXP2(BITAND, a, equality(J, notin));
+ return a;
+}
+
+static js_Ast *bitxor(js_State *J, int notin)
+{
+ js_Ast *a = bitand(J, notin);
+ while (jsP_accept(J, '^'))
+ a = EXP2(BITXOR, a, bitand(J, notin));
+ return a;
+}
+
+static js_Ast *bitor(js_State *J, int notin)
+{
+ js_Ast *a = bitxor(J, notin);
+ while (jsP_accept(J, '|'))
+ a = EXP2(BITOR, a, bitxor(J, notin));
+ return a;
+}
+
+static js_Ast *logand(js_State *J, int notin)
+{
+ js_Ast *a = bitor(J, notin);
+ if (jsP_accept(J, TK_AND))
+ a = EXP2(LOGAND, a, logand(J, notin));
+ return a;
+}
+
+static js_Ast *logor(js_State *J, int notin)
+{
+ js_Ast *a = logand(J, notin);
+ if (jsP_accept(J, TK_OR))
+ a = EXP2(LOGOR, a, logor(J, notin));
+ return a;
+}
+
+static js_Ast *conditional(js_State *J, int notin)
+{
+ js_Ast *a, *b, *c;
+ a = logor(J, notin);
+ if (jsP_accept(J, '?')) {
+ b = assignment(J, 0);
+ jsP_expect(J, ':');
+ c = assignment(J, notin);
+ return EXP3(COND, a, b, c);
+ }
+ return a;
+}
+
+static js_Ast *assignment(js_State *J, int notin)
+{
+ js_Ast *a = conditional(J, notin);
+ if (jsP_accept(J, '=')) return EXP2(ASS, a, assignment(J, notin));
+ if (jsP_accept(J, TK_MUL_ASS)) return EXP2(ASS_MUL, a, assignment(J, notin));
+ if (jsP_accept(J, TK_DIV_ASS)) return EXP2(ASS_DIV, a, assignment(J, notin));
+ if (jsP_accept(J, TK_MOD_ASS)) return EXP2(ASS_MOD, a, assignment(J, notin));
+ if (jsP_accept(J, TK_ADD_ASS)) return EXP2(ASS_ADD, a, assignment(J, notin));
+ if (jsP_accept(J, TK_SUB_ASS)) return EXP2(ASS_SUB, a, assignment(J, notin));
+ if (jsP_accept(J, TK_SHL_ASS)) return EXP2(ASS_SHL, a, assignment(J, notin));
+ if (jsP_accept(J, TK_SHR_ASS)) return EXP2(ASS_SHR, a, assignment(J, notin));
+ if (jsP_accept(J, TK_USHR_ASS)) return EXP2(ASS_USHR, a, assignment(J, notin));
+ if (jsP_accept(J, TK_AND_ASS)) return EXP2(ASS_BITAND, a, assignment(J, notin));
+ if (jsP_accept(J, TK_XOR_ASS)) return EXP2(ASS_BITXOR, a, assignment(J, notin));
+ if (jsP_accept(J, TK_OR_ASS)) return EXP2(ASS_BITOR, a, assignment(J, notin));
+ return a;
+}
+
+static js_Ast *expression(js_State *J, int notin)
+{
+ js_Ast *a = assignment(J, notin);
+ while (jsP_accept(J, ','))
+ a = EXP2(COMMA, a, assignment(J, notin));
+ return a;
+}
+
+/* Statements */
+
+static js_Ast *vardec(js_State *J, int notin)
+{
+ js_Ast *a = identifier(J);
+ if (jsP_accept(J, '='))
+ return EXP2(VAR, a, assignment(J, notin));
+ return EXP1(VAR, a);
+}
+
+static js_Ast *vardeclist(js_State *J, int notin)
+{
+ js_Ast *head, *tail;
+ head = tail = LIST(vardec(J, notin));
+ while (jsP_accept(J, ','))
+ tail = tail->b = LIST(vardec(J, notin));
+ return jsP_list(head);
+}
+
+static js_Ast *statementlist(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == '}' || J->lookahead == TK_CASE || J->lookahead == TK_DEFAULT)
+ return NULL;
+ head = tail = LIST(statement(J));
+ while (J->lookahead != '}' && J->lookahead != TK_CASE && J->lookahead != TK_DEFAULT)
+ tail = tail->b = LIST(statement(J));
+ return jsP_list(head);
+}
+
+static js_Ast *caseclause(js_State *J)
+{
+ js_Ast *a, *b;
+
+ if (jsP_accept(J, TK_CASE)) {
+ a = expression(J, 0);
+ jsP_expect(J, ':');
+ b = statementlist(J);
+ return STM2(CASE, a, b);
+ }
+
+ if (jsP_accept(J, TK_DEFAULT)) {
+ jsP_expect(J, ':');
+ a = statementlist(J);
+ return STM1(DEFAULT, a);
+ }
+
+ jsP_error(J, "unexpected token in switch: %s (expected 'case' or 'default')", jsY_tokenstring(J->lookahead));
+}
+
+static js_Ast *caselist(js_State *J)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == '}')
+ return NULL;
+ head = tail = LIST(caseclause(J));
+ while (J->lookahead != '}')
+ tail = tail->b = LIST(caseclause(J));
+ return jsP_list(head);
+}
+
+static js_Ast *block(js_State *J)
+{
+ js_Ast *a;
+ jsP_expect(J, '{');
+ a = statementlist(J);
+ jsP_expect(J, '}');
+ return STM1(BLOCK, a);
+}
+
+static js_Ast *forexpression(js_State *J, int end)
+{
+ js_Ast *a = NULL;
+ if (J->lookahead != end)
+ a = expression(J, 0);
+ jsP_expect(J, end);
+ return a;
+}
+
+static js_Ast *forstatement(js_State *J)
+{
+ js_Ast *a, *b, *c, *d;
+ jsP_expect(J, '(');
+ if (jsP_accept(J, TK_VAR)) {
+ a = vardeclist(J, 1);
+ if (jsP_accept(J, ';')) {
+ b = forexpression(J, ';');
+ c = forexpression(J, ')');
+ d = statement(J);
+ return STM4(FOR_VAR, a, b, c, d);
+ }
+ if (jsP_accept(J, TK_IN)) {
+ b = expression(J, 0);
+ jsP_expect(J, ')');
+ c = statement(J);
+ return STM3(FOR_IN_VAR, a, b, c);
+ }
+ jsP_error(J, "unexpected token in for-var-statement: %s", jsY_tokenstring(J->lookahead));
+ }
+
+ if (J->lookahead != ';')
+ a = expression(J, 1);
+ else
+ a = NULL;
+ if (jsP_accept(J, ';')) {
+ b = forexpression(J, ';');
+ c = forexpression(J, ')');
+ d = statement(J);
+ return STM4(FOR, a, b, c, d);
+ }
+ if (jsP_accept(J, TK_IN)) {
+ b = expression(J, 0);
+ jsP_expect(J, ')');
+ c = statement(J);
+ return STM3(FOR_IN, a, b, c);
+ }
+ jsP_error(J, "unexpected token in for-statement: %s", jsY_tokenstring(J->lookahead));
+}
+
+static js_Ast *statement(js_State *J)
+{
+ js_Ast *a, *b, *c, *d;
+
+ if (J->lookahead == '{') {
+ return block(J);
+ }
+
+ if (jsP_accept(J, TK_VAR)) {
+ a = vardeclist(J, 0);
+ semicolon(J);
+ return STM1(VAR, a);
+ }
+
+ /* empty statement */
+ if (jsP_accept(J, ';')) {
+ return STM0(EMPTY);
+ }
+
+ if (jsP_accept(J, TK_IF)) {
+ jsP_expect(J, '(');
+ a = expression(J, 0);
+ jsP_expect(J, ')');
+ b = statement(J);
+ if (jsP_accept(J, TK_ELSE))
+ c = statement(J);
+ else
+ c = NULL;
+ return STM3(IF, a, b, c);
+ }
+
+ if (jsP_accept(J, TK_DO)) {
+ a = statement(J);
+ jsP_expect(J, TK_WHILE);
+ jsP_expect(J, '(');
+ b = expression(J, 0);
+ jsP_expect(J, ')');
+ semicolon(J);
+ return STM2(DO, a, b);
+ }
+
+ if (jsP_accept(J, TK_WHILE)) {
+ jsP_expect(J, '(');
+ a = expression(J, 0);
+ jsP_expect(J, ')');
+ b = statement(J);
+ return STM2(WHILE, a, b);
+ }
+
+ if (jsP_accept(J, TK_FOR)) {
+ return forstatement(J);
+ }
+
+ if (jsP_accept(J, TK_CONTINUE)) {
+ a = identifieropt(J);
+ semicolon(J);
+ return STM1(CONTINUE, a);
+ }
+
+ if (jsP_accept(J, TK_BREAK)) {
+ a = identifieropt(J);
+ semicolon(J);
+ return STM1(BREAK, a);
+ }
+
+ if (jsP_accept(J, TK_RETURN)) {
+ if (J->lookahead != ';' && J->lookahead != '}' && J->lookahead != 0)
+ a = expression(J, 0);
+ else
+ a = NULL;
+ semicolon(J);
+ return STM1(RETURN, a);
+ }
+
+ if (jsP_accept(J, TK_WITH)) {
+ if (J->strict)
+ jsP_error(J, "'with' statements are not allowed in strict mode");
+ jsP_expect(J, '(');
+ a = expression(J, 0);
+ jsP_expect(J, ')');
+ b = statement(J);
+ return STM2(WITH, a, b);
+ }
+
+ if (jsP_accept(J, TK_SWITCH)) {
+ jsP_expect(J, '(');
+ a = expression(J, 0);
+ jsP_expect(J, ')');
+ jsP_expect(J, '{');
+ b = caselist(J);
+ jsP_expect(J, '}');
+ return STM2(SWITCH, a, b);
+ }
+
+ if (jsP_accept(J, TK_THROW)) {
+ a = expression(J, 0);
+ semicolon(J);
+ return STM1(THROW, a);
+ }
+
+ if (jsP_accept(J, TK_TRY)) {
+ a = block(J);
+ b = c = d = NULL;
+ if (jsP_accept(J, TK_CATCH)) {
+ jsP_expect(J, '(');
+ b = identifier(J);
+ jsP_expect(J, ')');
+ c = block(J);
+ }
+ if (jsP_accept(J, TK_FINALLY)) {
+ d = block(J);
+ }
+ if (!b && !d)
+ jsP_error(J, "unexpected token in try: %s (expected 'catch' or 'finally')", jsY_tokenstring(J->lookahead));
+ return STM4(TRY, a, b, c, d);
+ }
+
+ if (jsP_accept(J, TK_DEBUGGER)) {
+ semicolon(J);
+ return STM0(DEBUGGER);
+ }
+
+ if (jsP_accept(J, TK_FUNCTION)) {
+ jsP_warning(J, "function statements are not standard");
+ return funstm(J);
+ }
+
+ /* labelled statement or expression statement */
+ if (J->lookahead == TK_IDENTIFIER) {
+ a = expression(J, 0);
+ if (a->type == EXP_IDENTIFIER && jsP_accept(J, ':')) {
+ a->type = AST_IDENTIFIER;
+ b = statement(J);
+ return STM2(LABEL, a, b);
+ }
+ semicolon(J);
+ return a;
+ }
+
+ /* expression statement */
+ a = expression(J, 0);
+ semicolon(J);
+ return a;
+}
+
+/* Program */
+
+static js_Ast *scriptelement(js_State *J)
+{
+ if (jsP_accept(J, TK_FUNCTION))
+ return fundec(J);
+ return statement(J);
+}
+
+static js_Ast *script(js_State *J, int terminator)
+{
+ js_Ast *head, *tail;
+ if (J->lookahead == terminator)
+ return NULL;
+ head = tail = LIST(scriptelement(J));
+ while (J->lookahead != terminator)
+ tail = tail->b = LIST(scriptelement(J));
+ return jsP_list(head);
+}
+
+static js_Ast *funbody(js_State *J)
+{
+ js_Ast *a;
+ jsP_expect(J, '{');
+ a = script(J, '}');
+ jsP_expect(J, '}');
+ return a;
+}
+
+/* Constant folding */
+
+static int toint32(double d)
+{
+ double two32 = 4294967296.0;
+ double two31 = 2147483648.0;
+
+ if (!isfinite(d) || d == 0)
+ return 0;
+
+ d = fmod(d, two32);
+ d = d >= 0 ? floor(d) : ceil(d) + two32;
+ if (d >= two31)
+ return d - two32;
+ else
+ return d;
+}
+
+static unsigned int touint32(double d)
+{
+ return toint32(d);
+}
+
+static int jsP_setnumnode(js_Ast *node, double x)
+{
+ node->type = EXP_NUMBER;
+ node->number = x;
+ node->a = node->b = node->c = node->d = NULL;
+ return 1;
+}
+
+static int jsP_foldconst(js_Ast *node)
+{
+ double x, y;
+ int a, b;
+
+ if (node->type == EXP_NUMBER)
+ return 1;
+
+ a = node->a ? jsP_foldconst(node->a) : 0;
+ b = node->b ? jsP_foldconst(node->b) : 0;
+ if (node->c) jsP_foldconst(node->c);
+ if (node->d) jsP_foldconst(node->d);
+
+ if (a) {
+ x = node->a->number;
+ switch (node->type) {
+ case EXP_NEG: return jsP_setnumnode(node, -x);
+ case EXP_POS: return jsP_setnumnode(node, x);
+ case EXP_BITNOT: return jsP_setnumnode(node, ~toint32(x));
+ }
+
+ if (b) {
+ y = node->b->number;
+ switch (node->type) {
+ case EXP_MUL: return jsP_setnumnode(node, x * y);
+ case EXP_DIV: return jsP_setnumnode(node, x / y);
+ case EXP_MOD: return jsP_setnumnode(node, fmod(x, y));
+ case EXP_ADD: return jsP_setnumnode(node, x + y);
+ case EXP_SUB: return jsP_setnumnode(node, x - y);
+ case EXP_SHL: return jsP_setnumnode(node, toint32(x) << (touint32(y) & 0x1F));
+ case EXP_SHR: return jsP_setnumnode(node, toint32(x) >> (touint32(y) & 0x1F));
+ case EXP_USHR: return jsP_setnumnode(node, touint32(x) >> (touint32(y) & 0x1F));
+ case EXP_BITAND: return jsP_setnumnode(node, toint32(x) & toint32(y));
+ case EXP_BITXOR: return jsP_setnumnode(node, toint32(x) ^ toint32(y));
+ case EXP_BITOR: return jsP_setnumnode(node, toint32(x) | toint32(y));
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Main entry point */
+
+js_Ast *jsP_parse(js_State *J, const char *filename, const char *source)
+{
+ js_Ast *p;
+
+ jsY_initlex(J, filename, source);
+ jsP_next(J);
+ p = script(J, 0);
+ if (p)
+ jsP_foldconst(p);
+
+ return p;
+}
+
+js_Ast *jsP_parsefunction(js_State *J, const char *filename, const char *params, const char *body)
+{
+ js_Ast *p = NULL;
+ if (params) {
+ jsY_initlex(J, filename, params);
+ jsP_next(J);
+ p = parameters(J);
+ }
+ return EXP3(FUN, NULL, p, jsP_parse(J, filename, body));
+}
diff --git a/mujs/jsparse.h b/mujs/jsparse.h
new file mode 100644
index 0000000..db860de
--- /dev/null
+++ b/mujs/jsparse.h
@@ -0,0 +1,146 @@
+#ifndef js_parse_h
+#define js_parse_h
+
+enum js_AstType
+{
+ AST_LIST,
+ AST_FUNDEC,
+ AST_IDENTIFIER,
+
+ EXP_IDENTIFIER,
+ EXP_NUMBER,
+ EXP_STRING,
+ EXP_REGEXP,
+
+ /* literals */
+ EXP_UNDEF, /* for array elisions */
+ EXP_NULL,
+ EXP_TRUE,
+ EXP_FALSE,
+ EXP_THIS,
+
+ EXP_ARRAY,
+ EXP_OBJECT,
+ EXP_PROP_VAL,
+ EXP_PROP_GET,
+ EXP_PROP_SET,
+
+ EXP_FUN,
+
+ /* expressions */
+ EXP_INDEX,
+ EXP_MEMBER,
+ EXP_CALL,
+ EXP_NEW,
+
+ EXP_POSTINC,
+ EXP_POSTDEC,
+
+ EXP_DELETE,
+ EXP_VOID,
+ EXP_TYPEOF,
+ EXP_PREINC,
+ EXP_PREDEC,
+ EXP_POS,
+ EXP_NEG,
+ EXP_BITNOT,
+ EXP_LOGNOT,
+
+ EXP_MOD,
+ EXP_DIV,
+ EXP_MUL,
+ EXP_SUB,
+ EXP_ADD,
+ EXP_USHR,
+ EXP_SHR,
+ EXP_SHL,
+ EXP_IN,
+ EXP_INSTANCEOF,
+ EXP_GE,
+ EXP_LE,
+ EXP_GT,
+ EXP_LT,
+ EXP_STRICTNE,
+ EXP_STRICTEQ,
+ EXP_NE,
+ EXP_EQ,
+ EXP_BITAND,
+ EXP_BITXOR,
+ EXP_BITOR,
+ EXP_LOGAND,
+ EXP_LOGOR,
+
+ EXP_COND,
+
+ EXP_ASS,
+ EXP_ASS_MUL,
+ EXP_ASS_DIV,
+ EXP_ASS_MOD,
+ EXP_ASS_ADD,
+ EXP_ASS_SUB,
+ EXP_ASS_SHL,
+ EXP_ASS_SHR,
+ EXP_ASS_USHR,
+ EXP_ASS_BITAND,
+ EXP_ASS_BITXOR,
+ EXP_ASS_BITOR,
+
+ EXP_COMMA,
+
+ EXP_VAR, /* var initializer */
+
+ /* statements */
+ STM_BLOCK,
+ STM_EMPTY,
+ STM_VAR,
+ STM_IF,
+ STM_DO,
+ STM_WHILE,
+ STM_FOR,
+ STM_FOR_VAR,
+ STM_FOR_IN,
+ STM_FOR_IN_VAR,
+ STM_CONTINUE,
+ STM_BREAK,
+ STM_RETURN,
+ STM_WITH,
+ STM_SWITCH,
+ STM_THROW,
+ STM_TRY,
+ STM_DEBUGGER,
+
+ STM_LABEL,
+ STM_CASE,
+ STM_DEFAULT,
+};
+
+typedef struct js_JumpList js_JumpList;
+
+struct js_JumpList
+{
+ enum js_AstType type;
+ int inst;
+ js_JumpList *next;
+};
+
+struct js_Ast
+{
+ int type;
+ int line;
+ js_Ast *parent, *a, *b, *c, *d;
+ double number;
+ const char *string;
+ js_JumpList *jumps; /* list of break/continue jumps to patch */
+ int casejump; /* for switch case clauses */
+ js_Ast *gcnext; /* next in alloc list */
+};
+
+js_Ast *jsP_parsefunction(js_State *J, const char *filename, const char *params, const char *body);
+js_Ast *jsP_parse(js_State *J, const char *filename, const char *source);
+void jsP_freeparse(js_State *J);
+
+const char *jsP_aststring(enum js_AstType type);
+void jsP_dumpsyntax(js_State *J, js_Ast *prog);
+void jsP_dumplist(js_State *J, js_Ast *prog);
+
+#endif
diff --git a/mujs/jsproperty.c b/mujs/jsproperty.c
new file mode 100644
index 0000000..04e260e
--- /dev/null
+++ b/mujs/jsproperty.c
@@ -0,0 +1,339 @@
+#include "jsi.h"
+#include "jsvalue.h"
+
+/*
+ Use an AA-tree to quickly look up properties in objects:
+
+ The level of every leaf node is one.
+ The level of every left child is one less than its parent.
+ The level of every right child is equal or one less than its parent.
+ The level of every right grandchild is less than its grandparent.
+ Every node of level greater than one has two children.
+
+ A link where the child's level is equal to that of its parent is called a horizontal link.
+ Individual right horizontal links are allowed, but consecutive ones are forbidden.
+ Left horizontal links are forbidden.
+
+ skew() fixes left horizontal links.
+ split() fixes consecutive right horizontal links.
+*/
+
+static js_Property sentinel = {
+ "",
+ &sentinel, &sentinel,
+ NULL, NULL,
+ 0, 0,
+ { {0}, {0}, JS_TUNDEFINED },
+ NULL, NULL
+};
+
+static js_Property *newproperty(js_State *J, js_Object *obj, const char *name)
+{
+ js_Property *node = js_malloc(J, sizeof *node);
+ node->name = js_intern(J, name);
+ node->left = node->right = &sentinel;
+ node->prevp = NULL;
+ node->next = NULL;
+ node->level = 1;
+ node->atts = 0;
+ node->value.type = JS_TUNDEFINED;
+ node->value.u.number = 0;
+ node->getter = NULL;
+ node->setter = NULL;
+ ++obj->count;
+ return node;
+}
+
+static js_Property *lookup(js_Property *node, const char *name)
+{
+ while (node != &sentinel) {
+ int c = strcmp(name, node->name);
+ if (c == 0)
+ return node;
+ else if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ return NULL;
+}
+
+static js_Property *skew(js_Property *node)
+{
+ if (node->left->level == node->level) {
+ js_Property *temp = node;
+ node = node->left;
+ temp->left = node->right;
+ node->right = temp;
+ }
+ return node;
+}
+
+static js_Property *split(js_Property *node)
+{
+ if (node->right->right->level == node->level) {
+ js_Property *temp = node;
+ node = node->right;
+ temp->right = node->left;
+ node->left = temp;
+ ++node->level;
+ }
+ return node;
+}
+
+static js_Property *insert(js_State *J, js_Object *obj, js_Property *node, const char *name, js_Property **result)
+{
+ if (node != &sentinel) {
+ int c = strcmp(name, node->name);
+ if (c < 0)
+ node->left = insert(J, obj, node->left, name, result);
+ else if (c > 0)
+ node->right = insert(J, obj, node->right, name, result);
+ else
+ return *result = node;
+ node = skew(node);
+ node = split(node);
+ return node;
+ }
+ return *result = newproperty(J, obj, name);
+}
+
+static void freeproperty(js_State *J, js_Object *obj, js_Property *node)
+{
+ if (node->next)
+ node->next->prevp = node->prevp;
+ else
+ obj->tailp = node->prevp;
+ *node->prevp = node->next;
+ js_free(J, node);
+ --obj->count;
+}
+
+static js_Property *delete(js_State *J, js_Object *obj, js_Property *node, const char *name)
+{
+ js_Property *temp, *succ;
+
+ if (node != &sentinel) {
+ int c = strcmp(name, node->name);
+ if (c < 0) {
+ node->left = delete(J, obj, node->left, name);
+ } else if (c > 0) {
+ node->right = delete(J, obj, node->right, name);
+ } else {
+ if (node->left == &sentinel) {
+ temp = node;
+ node = node->right;
+ freeproperty(J, obj, temp);
+ } else if (node->right == &sentinel) {
+ temp = node;
+ node = node->left;
+ freeproperty(J, obj, temp);
+ } else {
+ succ = node->right;
+ while (succ->left != &sentinel)
+ succ = succ->left;
+ node->name = succ->name;
+ node->atts = succ->atts;
+ node->value = succ->value;
+ node->right = delete(J, obj, node->right, succ->name);
+ }
+ }
+
+ if (node->left->level < node->level - 1 ||
+ node->right->level < node->level - 1)
+ {
+ if (node->right->level > --node->level)
+ node->right->level = node->level;
+ node = skew(node);
+ node->right = skew(node->right);
+ node->right->right = skew(node->right->right);
+ node = split(node);
+ node->right = split(node->right);
+ }
+ }
+ return node;
+}
+
+
+js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype)
+{
+ js_Object *obj = js_malloc(J, sizeof *obj);
+ memset(obj, 0, sizeof *obj);
+ obj->gcmark = 0;
+ obj->gcnext = J->gcobj;
+ J->gcobj = obj;
+ ++J->gccounter;
+
+ obj->type = type;
+ obj->properties = &sentinel;
+ obj->head = NULL;
+ obj->tailp = &obj->head;
+ obj->prototype = prototype;
+ obj->extensible = 1;
+ return obj;
+}
+
+js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name)
+{
+ return lookup(obj->properties, name);
+}
+
+js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own)
+{
+ *own = 1;
+ do {
+ js_Property *ref = lookup(obj->properties, name);
+ if (ref)
+ return ref;
+ obj = obj->prototype;
+ *own = 0;
+ } while (obj);
+ return NULL;
+}
+
+js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name)
+{
+ do {
+ js_Property *ref = lookup(obj->properties, name);
+ if (ref)
+ return ref;
+ obj = obj->prototype;
+ } while (obj);
+ return NULL;
+}
+
+js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name)
+{
+ js_Property *result;
+
+ if (!obj->extensible) {
+ result = lookup(obj->properties, name);
+ if (J->strict && !result)
+ js_typeerror(J, "object is non-extensible");
+ return result;
+ }
+
+ obj->properties = insert(J, obj, obj->properties, name, &result);
+ if (!result->prevp) {
+ result->prevp = obj->tailp;
+ *obj->tailp = result;
+ obj->tailp = &result->next;
+ }
+ return result;
+}
+
+void jsV_delproperty(js_State *J, js_Object *obj, const char *name)
+{
+ obj->properties = delete(J, obj, obj->properties, name);
+}
+
+/* Flatten hierarchy of enumerable properties into an iterator object */
+
+static int itshadow(js_State *J, js_Object *top, js_Object *bot, const char *name)
+{
+ unsigned int k;
+ while (top != bot) {
+ js_Property *prop = lookup(top->properties, name);
+ if (prop && !(prop->atts & JS_DONTENUM))
+ return 1;
+ if (top->type == JS_CSTRING)
+ if (js_isarrayindex(J, name, &k) && k < top->u.s.length)
+ return 1;
+ top = top->prototype;
+ }
+ return 0;
+}
+
+static void itwalk(js_State *J, js_Object *io, js_Object *top, int own)
+{
+ js_Object *obj = top;
+ js_Iterator *tail = NULL;
+ char buf[32];
+ unsigned int k;
+
+#define ITADD(x) \
+ js_Iterator *node = js_malloc(J, sizeof *node); \
+ node->name = x; \
+ node->next = NULL; \
+ if (!tail) { \
+ io->u.iter.head = tail = node; \
+ } else { \
+ tail->next = node; \
+ tail = node; \
+ }
+
+ while (obj) {
+ js_Property *prop = obj->head;
+ while (prop) {
+ if (!(prop->atts & JS_DONTENUM) && !itshadow(J, top, obj, prop->name)) {
+ ITADD(prop->name);
+ }
+ prop = prop->next;
+ }
+
+ if (obj->type == JS_CSTRING) {
+ for (k = 0; k < obj->u.s.length; ++k) {
+ js_itoa(buf, k);
+ if (!itshadow(J, top, obj, buf)) {
+ ITADD(js_intern(J, buf));
+ }
+ }
+ }
+
+ if (own)
+ break;
+ obj = obj->prototype;
+ }
+}
+
+js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own)
+{
+ js_Object *io = jsV_newobject(J, JS_CITERATOR, NULL);
+ io->u.iter.target = obj;
+ io->u.iter.head = NULL;
+ itwalk(J, io, obj, own);
+ return io;
+}
+
+const char *jsV_nextiterator(js_State *J, js_Object *io)
+{
+ unsigned int k;
+ if (io->type != JS_CITERATOR)
+ js_typeerror(J, "not an iterator");
+ while (io->u.iter.head) {
+ js_Iterator *next = io->u.iter.head->next;
+ const char *name = io->u.iter.head->name;
+ js_free(J, io->u.iter.head);
+ io->u.iter.head = next;
+ if (jsV_getproperty(J, io->u.iter.target, name))
+ return name;
+ if (io->u.iter.target->type == JS_CSTRING)
+ if (js_isarrayindex(J, name, &k) && k < io->u.iter.target->u.s.length)
+ return name;
+ }
+ return NULL;
+}
+
+/* Walk all the properties and delete them one by one for arrays */
+
+void jsV_resizearray(js_State *J, js_Object *obj, unsigned int newlen)
+{
+ char buf[32];
+ const char *s;
+ unsigned int k;
+ if (newlen < obj->u.a.length) {
+ if (obj->u.a.length > obj->count * 2) {
+ js_Object *it = jsV_newiterator(J, obj, 1);
+ while ((s = jsV_nextiterator(J, it))) {
+ k = jsV_numbertouint32(jsV_stringtonumber(J, s));
+ if (k >= newlen && !strcmp(s, jsV_numbertostring(J, buf, k)))
+ jsV_delproperty(J, obj, s);
+ }
+ } else {
+ for (k = newlen; k < obj->u.a.length; ++k) {
+ jsV_delproperty(J, obj, js_itoa(buf, k));
+ }
+ }
+ }
+ obj->u.a.length = newlen;
+}
diff --git a/mujs/jsregexp.c b/mujs/jsregexp.c
new file mode 100644
index 0000000..199fdc2
--- /dev/null
+++ b/mujs/jsregexp.c
@@ -0,0 +1,194 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+#include "regex.h"
+
+void js_newregexp(js_State *J, const char *pattern, int flags)
+{
+ const char *error;
+ js_Object *obj;
+ Reprog *prog;
+ int opts;
+
+ obj = jsV_newobject(J, JS_CREGEXP, J->RegExp_prototype);
+
+ opts = 0;
+ if (flags & JS_REGEXP_I) opts |= REG_ICASE;
+ if (flags & JS_REGEXP_M) opts |= REG_NEWLINE;
+
+ prog = js_regcomp(pattern, opts, &error);
+ if (!prog)
+ js_syntaxerror(J, "regular expression: %s", error);
+
+ obj->u.r.prog = prog;
+ obj->u.r.source = pattern;
+ obj->u.r.flags = flags;
+ obj->u.r.last = 0;
+ js_pushobject(J, obj);
+}
+
+void js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text)
+{
+ unsigned int i;
+ int opts;
+ Resub m;
+
+ opts = 0;
+ if (re->flags & JS_REGEXP_G) {
+ if (re->last > strlen(text)) {
+ re->last = 0;
+ js_pushnull(J);
+ return;
+ }
+ if (re->last > 0) {
+ text += re->last;
+ opts |= REG_NOTBOL;
+ }
+ }
+
+ if (!js_regexec(re->prog, text, &m, opts)) {
+ js_newarray(J);
+ js_pushstring(J, text);
+ js_setproperty(J, -2, "input");
+ js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp));
+ js_setproperty(J, -2, "index");
+ for (i = 0; i < m.nsub; ++i) {
+ js_pushlstring(J, m.sub[i].sp, m.sub[i].ep - m.sub[i].sp);
+ js_setindex(J, -2, i);
+ }
+ if (re->flags & JS_REGEXP_G)
+ re->last = re->last + (m.sub[0].ep - text);
+ return;
+ }
+
+ if (re->flags & JS_REGEXP_G)
+ re->last = 0;
+
+ js_pushnull(J);
+}
+
+static void Rp_test(js_State *J)
+{
+ js_Regexp *re;
+ const char *text;
+ int opts;
+ Resub m;
+
+ re = js_toregexp(J, 0);
+ text = js_tostring(J, 1);
+
+ opts = 0;
+ if (re->flags & JS_REGEXP_G) {
+ if (re->last > strlen(text)) {
+ re->last = 0;
+ js_pushboolean(J, 0);
+ return;
+ }
+ if (re->last > 0) {
+ text += re->last;
+ opts |= REG_NOTBOL;
+ }
+ }
+
+ if (!js_regexec(re->prog, text, &m, opts)) {
+ if (re->flags & JS_REGEXP_G)
+ re->last = re->last + (m.sub[0].ep - text);
+ js_pushboolean(J, 1);
+ return;
+ }
+
+ if (re->flags & JS_REGEXP_G)
+ re->last = 0;
+
+ js_pushboolean(J, 0);
+}
+
+static void jsB_new_RegExp(js_State *J)
+{
+ js_Regexp *old;
+ const char *pattern;
+ int flags;
+
+ if (js_isregexp(J, 1)) {
+ if (js_isdefined(J, 2))
+ js_typeerror(J, "cannot supply flags when creating one RegExp from another");
+ old = js_toregexp(J, 1);
+ pattern = old->source;
+ flags = old->flags;
+ } else if (js_isundefined(J, 1)) {
+ pattern = "";
+ flags = 0;
+ } else {
+ pattern = js_tostring(J, 1);
+ flags = 0;
+ }
+
+ if (js_isdefined(J, 2)) {
+ const char *s = js_tostring(J, 2);
+ int g = 0, i = 0, m = 0;
+ while (*s) {
+ if (*s == 'g') ++g;
+ else if (*s == 'i') ++i;
+ else if (*s == 'm') ++m;
+ else js_syntaxerror(J, "invalid regular expression flag: '%c'", *s);
+ ++s;
+ }
+ if (g > 1) js_syntaxerror(J, "invalid regular expression flag: 'g'");
+ if (i > 1) js_syntaxerror(J, "invalid regular expression flag: 'i'");
+ if (m > 1) js_syntaxerror(J, "invalid regular expression flag: 'm'");
+ if (g) flags |= JS_REGEXP_G;
+ if (i) flags |= JS_REGEXP_I;
+ if (m) flags |= JS_REGEXP_M;
+ }
+
+ js_newregexp(J, pattern, flags);
+}
+
+static void jsB_RegExp(js_State *J)
+{
+ if (js_gettop(J) == 2 && js_isregexp(J, 1))
+ return;
+ jsB_new_RegExp(J);
+}
+
+static void Rp_toString(js_State *J)
+{
+ js_Regexp *re;
+ char *out;
+
+ re = js_toregexp(J, 0);
+
+ out = js_malloc(J, strlen(re->source) + 6); /* extra space for //gim */
+ strcpy(out, "/");
+ strcat(out, re->source);
+ strcat(out, "/");
+ if (re->flags & JS_REGEXP_G) strcat(out, "g");
+ if (re->flags & JS_REGEXP_I) strcat(out, "i");
+ if (re->flags & JS_REGEXP_M) strcat(out, "m");
+
+ if (js_try(J)) {
+ js_free(J, out);
+ js_throw(J);
+ }
+ js_pop(J, 0);
+ js_pushstring(J, out);
+ js_endtry(J);
+ js_free(J, out);
+}
+
+static void Rp_exec(js_State *J)
+{
+ js_RegExp_prototype_exec(J, js_toregexp(J, 0), js_tostring(J, 1));
+}
+
+void jsB_initregexp(js_State *J)
+{
+ js_pushobject(J, J->RegExp_prototype);
+ {
+ jsB_propf(J, "toString", Rp_toString, 0);
+ jsB_propf(J, "test", Rp_test, 0);
+ jsB_propf(J, "exec", Rp_exec, 0);
+ }
+ js_newcconstructor(J, jsB_RegExp, jsB_new_RegExp, "RegExp", 1);
+ js_defglobal(J, "RegExp", JS_DONTENUM);
+}
diff --git a/mujs/jsrun.c b/mujs/jsrun.c
new file mode 100644
index 0000000..d7b67fb
--- /dev/null
+++ b/mujs/jsrun.c
@@ -0,0 +1,1645 @@
+#include "jsi.h"
+#include "jscompile.h"
+#include "jsvalue.h"
+#include "jsrun.h"
+
+#include "utf.h"
+
+static void jsR_run(js_State *J, js_Function *F);
+
+/* Push values on stack */
+
+#define STACK (J->stack)
+#define TOP (J->top)
+#define BOT (J->bot)
+
+static void js_stackoverflow(js_State *J)
+{
+ STACK[TOP].type = JS_TLITSTR;
+ STACK[TOP].u.litstr = "stack overflow";
+ ++TOP;
+ js_throw(J);
+}
+
+static void js_outofmemory(js_State *J)
+{
+ STACK[TOP].type = JS_TLITSTR;
+ STACK[TOP].u.litstr = "out of memory";
+ ++TOP;
+ js_throw(J);
+}
+
+void *js_malloc(js_State *J, unsigned int size)
+{
+ void *ptr = J->alloc(J->actx, NULL, size);
+ if (!ptr)
+ js_outofmemory(J);
+ return ptr;
+}
+
+void *js_realloc(js_State *J, void *ptr, unsigned int size)
+{
+ ptr = J->alloc(J->actx, ptr, size);
+ if (!ptr)
+ js_outofmemory(J);
+ return ptr;
+}
+
+void js_free(js_State *J, void *ptr)
+{
+ J->alloc(J->actx, ptr, 0);
+}
+
+js_String *jsV_newmemstring(js_State *J, const char *s, int n)
+{
+ js_String *v = js_malloc(J, offsetof(js_String, p) + n + 1);
+ memcpy(v->p, s, n);
+ v->p[n] = 0;
+ v->gcmark = 0;
+ v->gcnext = J->gcstr;
+ J->gcstr = v;
+ ++J->gccounter;
+ return v;
+}
+
+#define CHECKSTACK(n) if (TOP + n >= JS_STACKSIZE) js_stackoverflow(J)
+
+void js_pushvalue(js_State *J, js_Value v)
+{
+ CHECKSTACK(1);
+ STACK[TOP] = v;
+ ++TOP;
+}
+
+void js_pushundefined(js_State *J)
+{
+ CHECKSTACK(1);
+ STACK[TOP].type = JS_TUNDEFINED;
+ ++TOP;
+}
+
+void js_pushnull(js_State *J)
+{
+ CHECKSTACK(1);
+ STACK[TOP].type = JS_TNULL;
+ ++TOP;
+}
+
+void js_pushboolean(js_State *J, int v)
+{
+ CHECKSTACK(1);
+ STACK[TOP].type = JS_TBOOLEAN;
+ STACK[TOP].u.boolean = !!v;
+ ++TOP;
+}
+
+void js_pushnumber(js_State *J, double v)
+{
+ CHECKSTACK(1);
+ STACK[TOP].type = JS_TNUMBER;
+ STACK[TOP].u.number = v;
+ ++TOP;
+}
+
+void js_pushstring(js_State *J, const char *v)
+{
+ unsigned int n = strlen(v);
+ CHECKSTACK(1);
+ if (n <= offsetof(js_Value, type)) {
+ char *s = STACK[TOP].u.shrstr;
+ while (n--) *s++ = *v++;
+ *s = 0;
+ STACK[TOP].type = JS_TSHRSTR;
+ } else {
+ STACK[TOP].type = JS_TMEMSTR;
+ STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
+ }
+ ++TOP;
+}
+
+void js_pushlstring(js_State *J, const char *v, unsigned int n)
+{
+ CHECKSTACK(1);
+ if (n <= offsetof(js_Value, type)) {
+ char *s = STACK[TOP].u.shrstr;
+ while (n--) *s++ = *v++;
+ *s = 0;
+ STACK[TOP].type = JS_TSHRSTR;
+ } else {
+ STACK[TOP].type = JS_TMEMSTR;
+ STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
+ }
+ ++TOP;
+}
+
+void js_pushliteral(js_State *J, const char *v)
+{
+ CHECKSTACK(1);
+ STACK[TOP].type = JS_TLITSTR;
+ STACK[TOP].u.litstr = v;
+ ++TOP;
+}
+
+void js_pushobject(js_State *J, js_Object *v)
+{
+ CHECKSTACK(1);
+ STACK[TOP].type = JS_TOBJECT;
+ STACK[TOP].u.object = v;
+ ++TOP;
+}
+
+void js_pushglobal(js_State *J)
+{
+ js_pushobject(J, J->G);
+}
+
+void js_pushundefinedthis(js_State *J)
+{
+ if (J->strict)
+ js_pushundefined(J);
+ else
+ js_pushobject(J, J->G);
+}
+
+void js_currentfunction(js_State *J)
+{
+ CHECKSTACK(1);
+ STACK[TOP] = STACK[BOT-1];
+ ++TOP;
+}
+
+/* Read values from stack */
+
+static js_Value *stackidx(js_State *J, int idx)
+{
+ static js_Value undefined = { {0}, {0}, JS_TUNDEFINED };
+ idx = idx < 0 ? TOP + idx : BOT + idx;
+ if (idx < 0 || idx >= TOP)
+ return &undefined;
+ return STACK + idx;
+}
+
+js_Value *js_tovalue(js_State *J, int idx)
+{
+ return stackidx(J, idx);
+}
+
+int js_isdefined(js_State *J, int idx) { return stackidx(J, idx)->type != JS_TUNDEFINED; }
+int js_isundefined(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TUNDEFINED; }
+int js_isnull(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TNULL; }
+int js_isboolean(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TBOOLEAN; }
+int js_isnumber(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TNUMBER; }
+int js_isstring(js_State *J, int idx) { enum js_Type t = stackidx(J, idx)->type; return t == JS_TSHRSTR || t == JS_TLITSTR || t == JS_TMEMSTR; }
+int js_isprimitive(js_State *J, int idx) { return stackidx(J, idx)->type != JS_TOBJECT; }
+int js_isobject(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TOBJECT; }
+
+int js_iscallable(js_State *J, int idx)
+{
+ js_Value *v = stackidx(J, idx);
+ if (v->type == JS_TOBJECT)
+ return v->u.object->type == JS_CFUNCTION ||
+ v->u.object->type == JS_CSCRIPT ||
+ v->u.object->type == JS_CCFUNCTION;
+ return 0;
+}
+
+int js_isarray(js_State *J, int idx)
+{
+ js_Value *v = stackidx(J, idx);
+ return v->type == JS_TOBJECT && v->u.object->type == JS_CARRAY;
+}
+
+int js_isregexp(js_State *J, int idx)
+{
+ js_Value *v = stackidx(J, idx);
+ return v->type == JS_TOBJECT && v->u.object->type == JS_CREGEXP;
+}
+
+int js_isuserdata(js_State *J, int idx, const char *tag)
+{
+ js_Value *v = stackidx(J, idx);
+ if (v->type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
+ return !strcmp(tag, v->u.object->u.user.tag);
+ return 0;
+}
+
+static const char *js_typeof(js_State *J, int idx)
+{
+ js_Value *v = stackidx(J, idx);
+ switch (v->type) {
+ default:
+ case JS_TSHRSTR: return "string";
+ case JS_TUNDEFINED: return "undefined";
+ case JS_TNULL: return "object";
+ case JS_TBOOLEAN: return "boolean";
+ case JS_TNUMBER: return "number";
+ case JS_TLITSTR: return "string";
+ case JS_TMEMSTR: return "string";
+ case JS_TOBJECT:
+ if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION)
+ return "function";
+ return "object";
+ }
+}
+
+int js_toboolean(js_State *J, int idx)
+{
+ return jsV_toboolean(J, stackidx(J, idx));
+}
+
+double js_tonumber(js_State *J, int idx)
+{
+ return jsV_tonumber(J, stackidx(J, idx));
+}
+
+double js_tointeger(js_State *J, int idx)
+{
+ return jsV_numbertointeger(jsV_tonumber(J, stackidx(J, idx)));
+}
+
+int js_toint32(js_State *J, int idx)
+{
+ return jsV_numbertoint32(jsV_tonumber(J, stackidx(J, idx)));
+}
+
+unsigned int js_touint32(js_State *J, int idx)
+{
+ return jsV_numbertouint32(jsV_tonumber(J, stackidx(J, idx)));
+}
+
+short js_toint16(js_State *J, int idx)
+{
+ return jsV_numbertoint16(jsV_tonumber(J, stackidx(J, idx)));
+}
+
+unsigned short js_touint16(js_State *J, int idx)
+{
+ return jsV_numbertouint16(jsV_tonumber(J, stackidx(J, idx)));
+}
+
+const char *js_tostring(js_State *J, int idx)
+{
+ return jsV_tostring(J, stackidx(J, idx));
+}
+
+js_Object *js_toobject(js_State *J, int idx)
+{
+ return jsV_toobject(J, stackidx(J, idx));
+}
+
+void js_toprimitive(js_State *J, int idx, int hint)
+{
+ jsV_toprimitive(J, stackidx(J, idx), hint);
+}
+
+js_Regexp *js_toregexp(js_State *J, int idx)
+{
+ js_Value *v = stackidx(J, idx);
+ if (v->type == JS_TOBJECT && v->u.object->type == JS_CREGEXP)
+ return &v->u.object->u.r;
+ js_typeerror(J, "not a regexp");
+}
+
+void *js_touserdata(js_State *J, int idx, const char *tag)
+{
+ js_Value *v = stackidx(J, idx);
+ if (v->type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
+ if (!strcmp(tag, v->u.object->u.user.tag))
+ return v->u.object->u.user.data;
+ js_typeerror(J, "not a %s", tag);
+}
+
+static js_Object *jsR_tofunction(js_State *J, int idx)
+{
+ js_Value *v = stackidx(J, idx);
+ if (v->type == JS_TUNDEFINED || v->type == JS_TNULL)
+ return NULL;
+ if (v->type == JS_TOBJECT)
+ if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION)
+ return v->u.object;
+ js_typeerror(J, "not a function");
+}
+
+/* Stack manipulation */
+
+int js_gettop(js_State *J)
+{
+ return TOP - BOT;
+}
+
+void js_pop(js_State *J, int n)
+{
+ TOP -= n;
+ if (TOP < BOT) {
+ TOP = BOT;
+ js_error(J, "stack underflow!");
+ }
+}
+
+void js_remove(js_State *J, int idx)
+{
+ idx = idx < 0 ? TOP + idx : BOT + idx;
+ if (idx < BOT || idx >= TOP)
+ js_error(J, "stack error!");
+ for (;idx < TOP - 1; ++idx)
+ STACK[idx] = STACK[idx+1];
+ --TOP;
+}
+
+void js_copy(js_State *J, int idx)
+{
+ CHECKSTACK(1);
+ STACK[TOP] = *stackidx(J, idx);
+ ++TOP;
+}
+
+void js_dup(js_State *J)
+{
+ CHECKSTACK(1);
+ STACK[TOP] = STACK[TOP-1];
+ ++TOP;
+}
+
+void js_dup2(js_State *J)
+{
+ CHECKSTACK(2);
+ STACK[TOP] = STACK[TOP-2];
+ STACK[TOP+1] = STACK[TOP-1];
+ TOP += 2;
+}
+
+void js_rot2(js_State *J)
+{
+ /* A B -> B A */
+ js_Value tmp = STACK[TOP-1]; /* A B (B) */
+ STACK[TOP-1] = STACK[TOP-2]; /* A A */
+ STACK[TOP-2] = tmp; /* B A */
+}
+
+void js_rot3(js_State *J)
+{
+ /* A B C -> C A B */
+ js_Value tmp = STACK[TOP-1]; /* A B C (C) */
+ STACK[TOP-1] = STACK[TOP-2]; /* A B B */
+ STACK[TOP-2] = STACK[TOP-3]; /* A A B */
+ STACK[TOP-3] = tmp; /* C A B */
+}
+
+void js_rot4(js_State *J)
+{
+ /* A B C D -> D A B C */
+ js_Value tmp = STACK[TOP-1]; /* A B C D (D) */
+ STACK[TOP-1] = STACK[TOP-2]; /* A B C C */
+ STACK[TOP-2] = STACK[TOP-3]; /* A B B C */
+ STACK[TOP-3] = STACK[TOP-4]; /* A A B C */
+ STACK[TOP-4] = tmp; /* D A B C */
+}
+
+void js_rot2pop1(js_State *J)
+{
+ /* A B -> B */
+ STACK[TOP-2] = STACK[TOP-1];
+ --TOP;
+}
+
+void js_rot3pop2(js_State *J)
+{
+ /* A B C -> C */
+ STACK[TOP-3] = STACK[TOP-1];
+ TOP -= 2;
+}
+
+void js_rot(js_State *J, int n)
+{
+ int i;
+ js_Value tmp = STACK[TOP-1];
+ for (i = 1; i < n; ++i)
+ STACK[TOP-i] = STACK[TOP-i-1];
+ STACK[TOP-i] = tmp;
+}
+
+/* Property access that takes care of attributes and getters/setters */
+
+int js_isarrayindex(js_State *J, const char *str, unsigned int *idx)
+{
+ char buf[32];
+ *idx = jsV_numbertouint32(jsV_stringtonumber(J, str));
+ sprintf(buf, "%u", *idx);
+ return !strcmp(buf, str);
+}
+
+static void js_pushrune(js_State *J, Rune rune)
+{
+ char buf[UTFmax + 1];
+ if (rune > 0) {
+ buf[runetochar(buf, &rune)] = 0;
+ js_pushstring(J, buf);
+ } else {
+ js_pushundefined(J);
+ }
+}
+
+
+static int jsR_hasproperty(js_State *J, js_Object *obj, const char *name)
+{
+ js_Property *ref;
+ unsigned int k;
+
+ if (obj->type == JS_CARRAY) {
+ if (!strcmp(name, "length")) {
+ js_pushnumber(J, obj->u.a.length);
+ return 1;
+ }
+ }
+
+ if (obj->type == JS_CSTRING) {
+ if (!strcmp(name, "length")) {
+ js_pushnumber(J, obj->u.s.length);
+ return 1;
+ }
+ if (js_isarrayindex(J, name, &k)) {
+ js_pushrune(J, js_runeat(J, obj->u.s.string, k));
+ return 1;
+ }
+ }
+
+ if (obj->type == JS_CREGEXP) {
+ if (!strcmp(name, "source")) {
+ js_pushliteral(J, obj->u.r.source);
+ return 1;
+ }
+ if (!strcmp(name, "global")) {
+ js_pushboolean(J, obj->u.r.flags & JS_REGEXP_G);
+ return 1;
+ }
+ if (!strcmp(name, "ignoreCase")) {
+ js_pushboolean(J, obj->u.r.flags & JS_REGEXP_I);
+ return 1;
+ }
+ if (!strcmp(name, "multiline")) {
+ js_pushboolean(J, obj->u.r.flags & JS_REGEXP_M);
+ return 1;
+ }
+ if (!strcmp(name, "lastIndex")) {
+ js_pushnumber(J, obj->u.r.last);
+ return 1;
+ }
+ }
+
+ ref = jsV_getproperty(J, obj, name);
+ if (ref) {
+ if (ref->getter) {
+ js_pushobject(J, ref->getter);
+ js_pushobject(J, obj);
+ js_call(J, 0);
+ } else {
+ js_pushvalue(J, ref->value);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+static void jsR_getproperty(js_State *J, js_Object *obj, const char *name)
+{
+ if (!jsR_hasproperty(J, obj, name))
+ js_pushundefined(J);
+}
+
+static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, js_Value *value)
+{
+ js_Property *ref;
+ unsigned int k;
+ int own;
+
+ if (obj->type == JS_CARRAY) {
+ if (!strcmp(name, "length")) {
+ double rawlen = jsV_tonumber(J, value);
+ unsigned int newlen = jsV_numbertouint32(rawlen);
+ if (newlen != rawlen)
+ js_rangeerror(J, "array length");
+ jsV_resizearray(J, obj, newlen);
+ return;
+ }
+ if (js_isarrayindex(J, name, &k))
+ if (k >= obj->u.a.length)
+ obj->u.a.length = k + 1;
+ }
+
+ if (obj->type == JS_CSTRING) {
+ if (!strcmp(name, "length"))
+ goto readonly;
+ if (js_isarrayindex(J, name, &k))
+ if (js_runeat(J, obj->u.s.string, k))
+ goto readonly;
+ }
+
+ if (obj->type == JS_CREGEXP) {
+ if (!strcmp(name, "source")) goto readonly;
+ if (!strcmp(name, "global")) goto readonly;
+ if (!strcmp(name, "ignoreCase")) goto readonly;
+ if (!strcmp(name, "multiline")) goto readonly;
+ if (!strcmp(name, "lastIndex")) {
+ obj->u.r.last = jsV_tointeger(J, value);
+ return;
+ }
+ }
+
+ /* First try to find a setter in prototype chain */
+ ref = jsV_getpropertyx(J, obj, name, &own);
+ if (ref && ref->setter) {
+ js_pushobject(J, ref->setter);
+ js_pushobject(J, obj);
+ js_pushvalue(J, *value);
+ js_call(J, 1);
+ js_pop(J, 1);
+ return;
+ }
+
+ /* Property not found on this object, so create one */
+ if (!ref || !own)
+ ref = jsV_setproperty(J, obj, name);
+
+ if (ref) {
+ if (!(ref->atts & JS_READONLY))
+ ref->value = *value;
+ else
+ goto readonly;
+ }
+
+ return;
+
+readonly:
+ if (J->strict)
+ js_typeerror(J, "'%s' is read-only", name);
+}
+
+static void jsR_defproperty(js_State *J, js_Object *obj, const char *name,
+ int atts, js_Value *value, js_Object *getter, js_Object *setter)
+{
+ js_Property *ref;
+ unsigned int k;
+
+ if (obj->type == JS_CARRAY)
+ if (!strcmp(name, "length"))
+ goto readonly;
+
+ if (obj->type == JS_CSTRING) {
+ if (!strcmp(name, "length"))
+ goto readonly;
+ if (js_isarrayindex(J, name, &k))
+ if (js_runeat(J, obj->u.s.string, k))
+ goto readonly;
+ }
+
+ if (obj->type == JS_CREGEXP) {
+ if (!strcmp(name, "source")) goto readonly;
+ if (!strcmp(name, "global")) goto readonly;
+ if (!strcmp(name, "ignoreCase")) goto readonly;
+ if (!strcmp(name, "multiline")) goto readonly;
+ if (!strcmp(name, "lastIndex")) goto readonly;
+ }
+
+ ref = jsV_setproperty(J, obj, name);
+ if (ref) {
+ if (value) {
+ if (!(ref->atts & JS_READONLY))
+ ref->value = *value;
+ else if (J->strict)
+ js_typeerror(J, "'%s' is read-only", name);
+ }
+ if (getter) {
+ if (!(ref->atts & JS_DONTCONF))
+ ref->getter = getter;
+ else if (J->strict)
+ js_typeerror(J, "'%s' is non-configurable", name);
+ }
+ if (setter) {
+ if (!(ref->atts & JS_DONTCONF))
+ ref->setter = setter;
+ else if (J->strict)
+ js_typeerror(J, "'%s' is non-configurable", name);
+ }
+ ref->atts |= atts;
+ }
+
+ return;
+
+readonly:
+ if (J->strict)
+ js_typeerror(J, "'%s' is read-only or non-configurable", name);
+}
+
+static int jsR_delproperty(js_State *J, js_Object *obj, const char *name)
+{
+ js_Property *ref;
+ unsigned int k;
+
+ if (obj->type == JS_CARRAY)
+ if (!strcmp(name, "length"))
+ goto dontconf;
+
+ if (obj->type == JS_CSTRING) {
+ if (!strcmp(name, "length"))
+ goto dontconf;
+ if (js_isarrayindex(J, name, &k))
+ if (js_runeat(J, obj->u.s.string, k))
+ goto dontconf;
+ }
+
+ if (obj->type == JS_CREGEXP) {
+ if (!strcmp(name, "source")) goto dontconf;
+ if (!strcmp(name, "global")) goto dontconf;
+ if (!strcmp(name, "ignoreCase")) goto dontconf;
+ if (!strcmp(name, "multiline")) goto dontconf;
+ if (!strcmp(name, "lastIndex")) goto dontconf;
+ }
+
+ ref = jsV_getownproperty(J, obj, name);
+ if (ref) {
+ if (ref->atts & JS_DONTCONF)
+ goto dontconf;
+ jsV_delproperty(J, obj, name);
+ }
+ return 1;
+
+dontconf:
+ if (J->strict)
+ js_typeerror(J, "'%s' is non-configurable", name);
+ return 0;
+}
+
+/* Registry, global and object property accessors */
+
+const char *js_ref(js_State *J)
+{
+ js_Value *v = stackidx(J, -1);
+ const char *s;
+ char buf[32];
+ switch (v->type) {
+ case JS_TUNDEFINED: s = "_Undefined"; break;
+ case JS_TNULL: s = "_Null"; break;
+ case JS_TBOOLEAN:
+ s = v->u.boolean ? "_True" : "_False";
+ break;
+ case JS_TOBJECT:
+ sprintf(buf, "%p", (void*)v->u.object);
+ s = js_intern(J, buf);
+ break;
+ default:
+ sprintf(buf, "%d", J->nextref++);
+ s = js_intern(J, buf);
+ break;
+ }
+ js_setregistry(J, s);
+ return s;
+}
+
+void js_unref(js_State *J, const char *ref)
+{
+ js_delregistry(J, ref);
+}
+
+void js_getregistry(js_State *J, const char *name)
+{
+ jsR_getproperty(J, J->R, name);
+}
+
+void js_setregistry(js_State *J, const char *name)
+{
+ jsR_setproperty(J, J->R, name, stackidx(J, -1));
+ js_pop(J, 1);
+}
+
+void js_delregistry(js_State *J, const char *name)
+{
+ jsR_delproperty(J, J->R, name);
+}
+
+void js_getglobal(js_State *J, const char *name)
+{
+ jsR_getproperty(J, J->G, name);
+}
+
+void js_setglobal(js_State *J, const char *name)
+{
+ jsR_setproperty(J, J->G, name, stackidx(J, -1));
+ js_pop(J, 1);
+}
+
+void js_defglobal(js_State *J, const char *name, int atts)
+{
+ jsR_defproperty(J, J->G, name, atts, stackidx(J, -1), NULL, NULL);
+ js_pop(J, 1);
+}
+
+void js_getproperty(js_State *J, int idx, const char *name)
+{
+ jsR_getproperty(J, js_toobject(J, idx), name);
+}
+
+void js_setproperty(js_State *J, int idx, const char *name)
+{
+ jsR_setproperty(J, js_toobject(J, idx), name, stackidx(J, -1));
+ js_pop(J, 1);
+}
+
+void js_defproperty(js_State *J, int idx, const char *name, int atts)
+{
+ jsR_defproperty(J, js_toobject(J, idx), name, atts, stackidx(J, -1), NULL, NULL);
+ js_pop(J, 1);
+}
+
+void js_delproperty(js_State *J, int idx, const char *name)
+{
+ jsR_delproperty(J, js_toobject(J, idx), name);
+}
+
+void js_defaccessor(js_State *J, int idx, const char *name, int atts)
+{
+ jsR_defproperty(J, js_toobject(J, idx), name, atts, NULL, jsR_tofunction(J, -2), jsR_tofunction(J, -1));
+ js_pop(J, 2);
+}
+
+int js_hasproperty(js_State *J, int idx, const char *name)
+{
+ return jsR_hasproperty(J, js_toobject(J, idx), name);
+}
+
+/* Iterator */
+
+void js_pushiterator(js_State *J, int idx, int own)
+{
+ js_pushobject(J, jsV_newiterator(J, js_toobject(J, idx), own));
+}
+
+const char *js_nextiterator(js_State *J, int idx)
+{
+ return jsV_nextiterator(J, js_toobject(J, idx));
+}
+
+/* Environment records */
+
+js_Environment *jsR_newenvironment(js_State *J, js_Object *vars, js_Environment *outer)
+{
+ js_Environment *E = js_malloc(J, sizeof *E);
+ E->gcmark = 0;
+ E->gcnext = J->gcenv;
+ J->gcenv = E;
+ ++J->gccounter;
+
+ E->outer = outer;
+ E->variables = vars;
+ return E;
+}
+
+static void js_initvar(js_State *J, const char *name, int idx)
+{
+ jsR_defproperty(J, J->E->variables, name, JS_DONTENUM | JS_DONTCONF, stackidx(J, idx), NULL, NULL);
+}
+
+static void js_defvar(js_State *J, const char *name)
+{
+ jsR_defproperty(J, J->E->variables, name, JS_DONTENUM | JS_DONTCONF, NULL, NULL, NULL);
+}
+
+static int js_hasvar(js_State *J, const char *name)
+{
+ js_Environment *E = J->E;
+ do {
+ js_Property *ref = jsV_getproperty(J, E->variables, name);
+ if (ref) {
+ if (ref->getter) {
+ js_pushobject(J, ref->getter);
+ js_pushobject(J, E->variables);
+ js_call(J, 0);
+ } else {
+ js_pushvalue(J, ref->value);
+ }
+ return 1;
+ }
+ E = E->outer;
+ } while (E);
+ return 0;
+}
+
+static void js_setvar(js_State *J, const char *name)
+{
+ js_Environment *E = J->E;
+ do {
+ js_Property *ref = jsV_getproperty(J, E->variables, name);
+ if (ref) {
+ if (ref->setter) {
+ js_pushobject(J, ref->setter);
+ js_pushobject(J, E->variables);
+ js_copy(J, -3);
+ js_call(J, 1);
+ js_pop(J, 1);
+ return;
+ }
+ if (!(ref->atts & JS_READONLY))
+ ref->value = *stackidx(J, -1);
+ else if (J->strict)
+ js_typeerror(J, "'%s' is read-only", name);
+ return;
+ }
+ E = E->outer;
+ } while (E);
+ if (J->strict)
+ js_referenceerror(J, "assignment to undeclared variable '%s'", name);
+ jsR_setproperty(J, J->G, name, stackidx(J, -1));
+}
+
+static int js_delvar(js_State *J, const char *name)
+{
+ js_Environment *E = J->E;
+ do {
+ js_Property *ref = jsV_getownproperty(J, E->variables, name);
+ if (ref) {
+ if (ref->atts & JS_DONTCONF) {
+ if (J->strict)
+ js_typeerror(J, "'%s' is non-configurable", name);
+ return 0;
+ }
+ jsV_delproperty(J, E->variables, name);
+ return 1;
+ }
+ E = E->outer;
+ } while (E);
+ return jsR_delproperty(J, J->G, name);
+}
+
+/* Function calls */
+
+static void jsR_savescope(js_State *J, js_Environment *newE)
+{
+ if (J->envtop + 1 >= JS_ENVLIMIT)
+ js_stackoverflow(J);
+ J->envstack[J->envtop++] = J->E;
+ J->E = newE;
+}
+
+static void jsR_restorescope(js_State *J)
+{
+ J->E = J->envstack[--J->envtop];
+}
+
+static void jsR_calllwfunction(js_State *J, unsigned int n, js_Function *F, js_Environment *scope)
+{
+ js_Value v;
+ unsigned int i;
+
+ jsR_savescope(J, scope);
+
+ if (n > F->numparams) {
+ js_pop(J, F->numparams - n);
+ n = F->numparams;
+ }
+ for (i = n; i < F->varlen; ++i)
+ js_pushundefined(J);
+
+ jsR_run(J, F);
+ v = *stackidx(J, -1);
+ TOP = --BOT; /* clear stack */
+ js_pushvalue(J, v);
+
+ jsR_restorescope(J);
+}
+
+static void jsR_callfunction(js_State *J, unsigned int n, js_Function *F, js_Environment *scope)
+{
+ js_Value v;
+ unsigned int i;
+
+ scope = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope);
+
+ jsR_savescope(J, scope);
+
+ if (F->arguments) {
+ js_newobject(J);
+ if (!J->strict) {
+ js_currentfunction(J);
+ js_defproperty(J, -2, "callee", JS_DONTENUM);
+ }
+ js_pushnumber(J, n);
+ js_defproperty(J, -2, "length", JS_DONTENUM);
+ for (i = 0; i < n; ++i) {
+ js_copy(J, i + 1);
+ js_setindex(J, -2, i);
+ }
+ js_initvar(J, "arguments", -1);
+ js_pop(J, 1);
+ }
+
+ for (i = 0; i < F->numparams; ++i) {
+ if (i < n)
+ js_initvar(J, F->vartab[i], i + 1);
+ else {
+ js_pushundefined(J);
+ js_initvar(J, F->vartab[i], -1);
+ js_pop(J, 1);
+ }
+ }
+ js_pop(J, n);
+
+ jsR_run(J, F);
+ v = *stackidx(J, -1);
+ TOP = --BOT; /* clear stack */
+ js_pushvalue(J, v);
+
+ jsR_restorescope(J);
+}
+
+static void jsR_callscript(js_State *J, unsigned int n, js_Function *F, js_Environment *scope)
+{
+ js_Value v;
+
+ if (scope)
+ jsR_savescope(J, scope);
+
+ js_pop(J, n);
+ jsR_run(J, F);
+ v = *stackidx(J, -1);
+ TOP = --BOT; /* clear stack */
+ js_pushvalue(J, v);
+
+ if (scope)
+ jsR_restorescope(J);
+}
+
+static void jsR_callcfunction(js_State *J, unsigned int n, unsigned int min, js_CFunction F)
+{
+ unsigned int i;
+ js_Value v;
+
+ for (i = n; i < min; ++i)
+ js_pushundefined(J);
+
+ F(J);
+ v = *stackidx(J, -1);
+ TOP = --BOT; /* clear stack */
+ js_pushvalue(J, v);
+}
+
+static void jsR_pushtrace(js_State *J, const char *name, const char *file, int line)
+{
+ if (++J->tracetop == JS_ENVLIMIT)
+ js_error(J, "call stack overflow");
+ J->trace[J->tracetop].name = name;
+ J->trace[J->tracetop].file = file;
+ J->trace[J->tracetop].line = line;
+}
+
+void js_call(js_State *J, int n)
+{
+ js_Object *obj;
+ int savebot;
+
+ if (!js_iscallable(J, -n-2))
+ js_typeerror(J, "called object is not a function");
+
+ obj = js_toobject(J, -n-2);
+
+ savebot = BOT;
+ BOT = TOP - n - 1;
+
+ if (obj->type == JS_CFUNCTION) {
+ jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line);
+ if (obj->u.f.function->lightweight)
+ jsR_calllwfunction(J, n, obj->u.f.function, obj->u.f.scope);
+ else
+ jsR_callfunction(J, n, obj->u.f.function, obj->u.f.scope);
+ --J->tracetop;
+ } else if (obj->type == JS_CSCRIPT) {
+ jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line);
+ jsR_callscript(J, n, obj->u.f.function, obj->u.f.scope);
+ --J->tracetop;
+ } else if (obj->type == JS_CCFUNCTION) {
+ jsR_pushtrace(J, obj->u.c.name, "[C]", 0);
+ jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.function);
+ --J->tracetop;
+ }
+
+ BOT = savebot;
+}
+
+void js_construct(js_State *J, int n)
+{
+ js_Object *obj;
+ js_Object *prototype;
+ js_Object *newobj;
+
+ if (!js_iscallable(J, -n-1))
+ js_typeerror(J, "called object is not a function");
+
+ obj = js_toobject(J, -n-1);
+
+ /* built-in constructors create their own objects, give them a 'null' this */
+ if (obj->type == JS_CCFUNCTION && obj->u.c.constructor) {
+ int savebot = BOT;
+ js_pushnull(J);
+ if (n > 0)
+ js_rot(J, n + 1);
+ BOT = TOP - n - 1;
+
+ jsR_pushtrace(J, obj->u.c.name, "[C]", 0);
+ jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.constructor);
+ --J->tracetop;
+
+ BOT = savebot;
+ return;
+ }
+
+ /* extract the function object's prototype property */
+ js_getproperty(J, -n - 1, "prototype");
+ if (js_isobject(J, -1))
+ prototype = js_toobject(J, -1);
+ else
+ prototype = J->Object_prototype;
+ js_pop(J, 1);
+
+ /* create a new object with above prototype, and shift it into the 'this' slot */
+ newobj = jsV_newobject(J, JS_COBJECT, prototype);
+ js_pushobject(J, newobj);
+ if (n > 0)
+ js_rot(J, n + 1);
+
+ /* call the function */
+ js_call(J, n);
+
+ /* if result is not an object, return the original object we created */
+ if (!js_isobject(J, -1)) {
+ js_pop(J, 1);
+ js_pushobject(J, newobj);
+ }
+}
+
+void js_eval(js_State *J)
+{
+ if (!js_isstring(J, -1))
+ return;
+ js_loadeval(J, "(eval)", js_tostring(J, -1));
+ js_rot2pop1(J);
+ js_copy(J, 0); /* copy 'this' */
+ js_call(J, 0);
+}
+
+int js_pconstruct(js_State *J, int n)
+{
+ if (js_try(J))
+ return 1;
+ js_construct(J, n);
+ js_endtry(J);
+ return 0;
+}
+
+int js_pcall(js_State *J, int n)
+{
+ if (js_try(J))
+ return 1;
+ js_call(J, n);
+ js_endtry(J);
+ return 0;
+}
+
+/* Exceptions */
+
+void js_savetry(js_State *J, js_Instruction *pc)
+{
+ if (J->trytop == JS_TRYLIMIT)
+ js_error(J, "try: exception stack overflow");
+ J->trybuf[J->trytop].E = J->E;
+ J->trybuf[J->trytop].envtop = J->envtop;
+ J->trybuf[J->trytop].tracetop = J->tracetop;
+ J->trybuf[J->trytop].top = J->top;
+ J->trybuf[J->trytop].bot = J->bot;
+ J->trybuf[J->trytop].pc = pc;
+}
+
+void js_throw(js_State *J)
+{
+ if (J->trytop > 0) {
+ js_Value v = *stackidx(J, -1);
+ --J->trytop;
+ J->E = J->trybuf[J->trytop].E;
+ J->envtop = J->trybuf[J->trytop].envtop;
+ J->tracetop = J->trybuf[J->trytop].tracetop;
+ J->top = J->trybuf[J->trytop].top;
+ J->bot = J->trybuf[J->trytop].bot;
+ js_pushvalue(J, v);
+ longjmp(J->trybuf[J->trytop].buf, 1);
+ }
+ if (J->panic)
+ J->panic(J);
+ abort();
+}
+
+/* Main interpreter loop */
+
+static void jsR_dumpstack(js_State *J)
+{
+ int i;
+ printf("stack {\n");
+ for (i = 0; i < TOP; ++i) {
+ putchar(i == BOT ? '>' : ' ');
+ printf("% 4d: ", i);
+ js_dumpvalue(J, STACK[i]);
+ putchar('\n');
+ }
+ printf("}\n");
+}
+
+static void jsR_dumpenvironment(js_State *J, js_Environment *E, int d)
+{
+ printf("scope %d ", d);
+ js_dumpobject(J, E->variables);
+ if (E->outer)
+ jsR_dumpenvironment(J, E->outer, d+1);
+}
+
+void js_stacktrace(js_State *J)
+{
+ int n;
+ printf("stack trace:\n");
+ for (n = J->tracetop; n >= 0; --n) {
+ const char *name = J->trace[n].name;
+ const char *file = J->trace[n].file;
+ int line = J->trace[n].line;
+ if (line > 0)
+ printf("\t%s:%d: in function '%s'\n", file, line, name);
+ else
+ printf("\t%s: in function '%s'\n", file, name);
+ }
+}
+
+void js_trap(js_State *J, int pc)
+{
+ if (pc > 0) {
+ js_Function *F = STACK[BOT-1].u.object->u.f.function;
+ printf("trap at %d in function ", pc);
+ jsC_dumpfunction(J, F);
+ }
+ jsR_dumpstack(J);
+ jsR_dumpenvironment(J, J->E, 0);
+ js_stacktrace(J);
+}
+
+static void jsR_run(js_State *J, js_Function *F)
+{
+ js_Function **FT = F->funtab;
+ double *NT = F->numtab;
+ const char **ST = F->strtab;
+ js_Instruction *pcstart = F->code;
+ js_Instruction *pc = F->code;
+ enum js_OpCode opcode;
+ int offset;
+
+ const char *str;
+ js_Object *obj;
+ double x, y;
+ unsigned int ux, uy;
+ int ix, iy, okay;
+ int b;
+
+ while (1) {
+ if (J->gccounter > JS_GCLIMIT) {
+ J->gccounter = 0;
+ js_gc(J, 0);
+ }
+
+ opcode = *pc++;
+ switch (opcode) {
+ case OP_POP: js_pop(J, 1); break;
+ case OP_DUP: js_dup(J); break;
+ case OP_DUP2: js_dup2(J); break;
+ case OP_ROT2: js_rot2(J); break;
+ case OP_ROT3: js_rot3(J); break;
+ case OP_ROT4: js_rot4(J); break;
+
+ case OP_NUMBER_0: js_pushnumber(J, 0); break;
+ case OP_NUMBER_1: js_pushnumber(J, 1); break;
+ case OP_NUMBER_POS: js_pushnumber(J, *pc++); break;
+ case OP_NUMBER_NEG: js_pushnumber(J, -(*pc++)); break;
+ case OP_NUMBER: js_pushnumber(J, NT[*pc++]); break;
+ case OP_STRING: js_pushliteral(J, ST[*pc++]); break;
+
+ case OP_CLOSURE: js_newfunction(J, FT[*pc++], J->E); break;
+ case OP_NEWOBJECT: js_newobject(J); break;
+ case OP_NEWARRAY: js_newarray(J); break;
+ case OP_NEWREGEXP: js_newregexp(J, ST[pc[0]], pc[1]); pc += 2; break;
+
+ case OP_UNDEF: js_pushundefined(J); break;
+ case OP_NULL: js_pushnull(J); break;
+ case OP_TRUE: js_pushboolean(J, 1); break;
+ case OP_FALSE: js_pushboolean(J, 0); break;
+
+ case OP_THIS: js_copy(J, 0); break;
+ case OP_GLOBAL: js_pushobject(J, J->G); break;
+ case OP_CURRENT: js_currentfunction(J); break;
+
+ case OP_INITLOCAL:
+ STACK[BOT + *pc++] = STACK[--TOP];
+ break;
+
+ case OP_GETLOCAL:
+ CHECKSTACK(1);
+ STACK[TOP++] = STACK[BOT + *pc++];
+ break;
+
+ case OP_SETLOCAL:
+ STACK[BOT + *pc++] = STACK[TOP-1];
+ break;
+
+ case OP_DELLOCAL:
+ ++pc;
+ js_pushboolean(J, 0);
+ break;
+
+ case OP_INITVAR:
+ js_initvar(J, ST[*pc++], -1);
+ js_pop(J, 1);
+ break;
+
+ case OP_DEFVAR:
+ js_defvar(J, ST[*pc++]);
+ break;
+
+ case OP_GETVAR:
+ str = ST[*pc++];
+ if (!js_hasvar(J, str))
+ js_referenceerror(J, "'%s' is not defined", str);
+ break;
+
+ case OP_HASVAR:
+ if (!js_hasvar(J, ST[*pc++]))
+ js_pushundefined(J);
+ break;
+
+ case OP_SETVAR:
+ js_setvar(J, ST[*pc++]);
+ break;
+
+ case OP_DELVAR:
+ b = js_delvar(J, ST[*pc++]);
+ js_pushboolean(J, b);
+ break;
+
+ case OP_IN:
+ str = js_tostring(J, -2);
+ if (!js_isobject(J, -1))
+ js_typeerror(J, "operand to 'in' is not an object");
+ b = js_hasproperty(J, -1, str);
+ js_pop(J, 2 + b);
+ js_pushboolean(J, b);
+ break;
+
+ case OP_INITPROP:
+ obj = js_toobject(J, -3);
+ str = js_tostring(J, -2);
+ jsR_setproperty(J, obj, str, stackidx(J, -1));
+ js_pop(J, 2);
+ break;
+
+ case OP_INITGETTER:
+ obj = js_toobject(J, -3);
+ str = js_tostring(J, -2);
+ jsR_defproperty(J, obj, str, 0, NULL, jsR_tofunction(J, -1), NULL);
+ js_pop(J, 2);
+ break;
+
+ case OP_INITSETTER:
+ obj = js_toobject(J, -3);
+ str = js_tostring(J, -2);
+ jsR_defproperty(J, obj, str, 0, NULL, NULL, jsR_tofunction(J, -1));
+ js_pop(J, 2);
+ break;
+
+ case OP_GETPROP:
+ str = js_tostring(J, -1);
+ obj = js_toobject(J, -2);
+ jsR_getproperty(J, obj, str);
+ js_rot3pop2(J);
+ break;
+
+ case OP_GETPROP_S:
+ str = ST[*pc++];
+ obj = js_toobject(J, -1);
+ jsR_getproperty(J, obj, str);
+ js_rot2pop1(J);
+ break;
+
+ case OP_SETPROP:
+ str = js_tostring(J, -2);
+ obj = js_toobject(J, -3);
+ jsR_setproperty(J, obj, str, stackidx(J, -1));
+ js_rot3pop2(J);
+ break;
+
+ case OP_SETPROP_S:
+ str = ST[*pc++];
+ obj = js_toobject(J, -2);
+ jsR_setproperty(J, obj, str, stackidx(J, -1));
+ js_rot2pop1(J);
+ break;
+
+ case OP_DELPROP:
+ str = js_tostring(J, -1);
+ obj = js_toobject(J, -2);
+ b = jsR_delproperty(J, obj, str);
+ js_pop(J, 2);
+ js_pushboolean(J, b);
+ break;
+
+ case OP_DELPROP_S:
+ str = ST[*pc++];
+ obj = js_toobject(J, -1);
+ b = jsR_delproperty(J, obj, str);
+ js_pop(J, 1);
+ js_pushboolean(J, b);
+ break;
+
+ case OP_ITERATOR:
+ if (!js_isundefined(J, -1) && !js_isnull(J, -1)) {
+ obj = jsV_newiterator(J, js_toobject(J, -1), 0);
+ js_pop(J, 1);
+ js_pushobject(J, obj);
+ }
+ break;
+
+ case OP_NEXTITER:
+ obj = js_toobject(J, -1);
+ str = jsV_nextiterator(J, obj);
+ if (str) {
+ js_pushliteral(J, str);
+ js_pushboolean(J, 1);
+ } else {
+ js_pop(J, 1);
+ js_pushboolean(J, 0);
+ }
+ break;
+
+ /* Function calls */
+
+ case OP_EVAL:
+ js_eval(J);
+ break;
+
+ case OP_CALL:
+ js_call(J, *pc++);
+ break;
+
+ case OP_NEW:
+ js_construct(J, *pc++);
+ break;
+
+ /* Unary operators */
+
+ case OP_TYPEOF:
+ str = js_typeof(J, -1);
+ js_pop(J, 1);
+ js_pushliteral(J, str);
+ break;
+
+ case OP_POS:
+ x = js_tonumber(J, -1);
+ js_pop(J, 1);
+ js_pushnumber(J, x);
+ break;
+
+ case OP_NEG:
+ x = js_tonumber(J, -1);
+ js_pop(J, 1);
+ js_pushnumber(J, -x);
+ break;
+
+ case OP_BITNOT:
+ ix = js_toint32(J, -1);
+ js_pop(J, 1);
+ js_pushnumber(J, ~ix);
+ break;
+
+ case OP_LOGNOT:
+ b = js_toboolean(J, -1);
+ js_pop(J, 1);
+ js_pushboolean(J, !b);
+ break;
+
+ case OP_INC:
+ x = js_tonumber(J, -1);
+ js_pop(J, 1);
+ js_pushnumber(J, x + 1);
+ break;
+
+ case OP_DEC:
+ x = js_tonumber(J, -1);
+ js_pop(J, 1);
+ js_pushnumber(J, x - 1);
+ break;
+
+ case OP_POSTINC:
+ x = js_tonumber(J, -1);
+ js_pop(J, 1);
+ js_pushnumber(J, x + 1);
+ js_pushnumber(J, x);
+ break;
+
+ case OP_POSTDEC:
+ x = js_tonumber(J, -1);
+ js_pop(J, 1);
+ js_pushnumber(J, x - 1);
+ js_pushnumber(J, x);
+ break;
+
+ /* Multiplicative operators */
+
+ case OP_MUL:
+ x = js_tonumber(J, -2);
+ y = js_tonumber(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, x * y);
+ break;
+
+ case OP_DIV:
+ x = js_tonumber(J, -2);
+ y = js_tonumber(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, x / y);
+ break;
+
+ case OP_MOD:
+ x = js_tonumber(J, -2);
+ y = js_tonumber(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, fmod(x, y));
+ break;
+
+ /* Additive operators */
+
+ case OP_ADD:
+ js_concat(J);
+ break;
+
+ case OP_SUB:
+ x = js_tonumber(J, -2);
+ y = js_tonumber(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, x - y);
+ break;
+
+ /* Shift operators */
+
+ case OP_SHL:
+ ix = js_toint32(J, -2);
+ uy = js_touint32(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, ix << (uy & 0x1F));
+ break;
+
+ case OP_SHR:
+ ix = js_toint32(J, -2);
+ uy = js_touint32(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, ix >> (uy & 0x1F));
+ break;
+
+ case OP_USHR:
+ ux = js_touint32(J, -2);
+ uy = js_touint32(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, ux >> (uy & 0x1F));
+ break;
+
+ /* Relational operators */
+
+ case OP_LT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b < 0); break;
+ case OP_GT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b > 0); break;
+ case OP_LE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b <= 0); break;
+ case OP_GE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b >= 0); break;
+
+ case OP_INSTANCEOF:
+ b = js_instanceof(J);
+ js_pop(J, 2);
+ js_pushboolean(J, b);
+ break;
+
+ /* Equality */
+
+ case OP_EQ: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, b); break;
+ case OP_NE: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, !b); break;
+ case OP_STRICTEQ: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, b); break;
+ case OP_STRICTNE: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, !b); break;
+
+ case OP_JCASE:
+ offset = *pc++;
+ b = js_strictequal(J);
+ if (b) {
+ js_pop(J, 2);
+ pc = pcstart + offset;
+ } else {
+ js_pop(J, 1);
+ }
+ break;
+
+ /* Binary bitwise operators */
+
+ case OP_BITAND:
+ ix = js_toint32(J, -2);
+ iy = js_toint32(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, ix & iy);
+ break;
+
+ case OP_BITXOR:
+ ix = js_toint32(J, -2);
+ iy = js_toint32(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, ix ^ iy);
+ break;
+
+ case OP_BITOR:
+ ix = js_toint32(J, -2);
+ iy = js_toint32(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, ix | iy);
+ break;
+
+ /* Try and Catch */
+
+ case OP_THROW:
+ js_throw(J);
+
+ case OP_TRY:
+ offset = *pc++;
+ if (js_trypc(J, pc)) {
+ pc = J->trybuf[J->trytop].pc;
+ } else {
+ pc = pcstart + offset;
+ }
+ break;
+
+ case OP_ENDTRY:
+ js_endtry(J);
+ break;
+
+ case OP_CATCH:
+ str = ST[*pc++];
+ obj = jsV_newobject(J, JS_COBJECT, NULL);
+ js_pushobject(J, obj);
+ js_rot2(J);
+ js_setproperty(J, -2, str);
+ J->E = jsR_newenvironment(J, obj, J->E);
+ js_pop(J, 1);
+ break;
+
+ case OP_ENDCATCH:
+ J->E = J->E->outer;
+ break;
+
+ /* With */
+
+ case OP_WITH:
+ obj = js_toobject(J, -1);
+ J->E = jsR_newenvironment(J, obj, J->E);
+ js_pop(J, 1);
+ break;
+
+ case OP_ENDWITH:
+ J->E = J->E->outer;
+ break;
+
+ /* Branching */
+
+ case OP_DEBUGGER:
+ js_trap(J, (int)(pc - pcstart) - 1);
+ break;
+
+ case OP_JUMP:
+ pc = pcstart + *pc;
+ break;
+
+ case OP_JTRUE:
+ offset = *pc++;
+ b = js_toboolean(J, -1);
+ js_pop(J, 1);
+ if (b)
+ pc = pcstart + offset;
+ break;
+
+ case OP_JFALSE:
+ offset = *pc++;
+ b = js_toboolean(J, -1);
+ js_pop(J, 1);
+ if (!b)
+ pc = pcstart + offset;
+ break;
+
+ case OP_RETURN:
+ return;
+
+ case OP_LINE:
+ J->trace[J->tracetop].line = *pc++;
+ break;
+ }
+ }
+}
diff --git a/mujs/jsrun.h b/mujs/jsrun.h
new file mode 100644
index 0000000..e1e04a7
--- /dev/null
+++ b/mujs/jsrun.h
@@ -0,0 +1,15 @@
+#ifndef js_run_h
+#define js_run_h
+
+js_Environment *jsR_newenvironment(js_State *J, js_Object *variables, js_Environment *outer);
+
+struct js_Environment
+{
+ js_Environment *outer;
+ js_Object *variables;
+
+ js_Environment *gcnext;
+ int gcmark;
+};
+
+#endif
diff --git a/mujs/jsstate.c b/mujs/jsstate.c
new file mode 100644
index 0000000..2d9b6b9
--- /dev/null
+++ b/mujs/jsstate.c
@@ -0,0 +1,221 @@
+#include "jsi.h"
+#include "jsparse.h"
+#include "jscompile.h"
+#include "jsvalue.h"
+#include "jsrun.h"
+#include "jsbuiltin.h"
+
+#include <assert.h>
+
+static void *js_defaultalloc(void *actx, void *ptr, unsigned int size)
+{
+ if (size == 0) {
+ free(ptr);
+ return NULL;
+ }
+ if (!ptr)
+ return malloc(size);
+ return realloc(ptr, size);
+}
+
+static void js_defaultpanic(js_State *J)
+{
+ fprintf(stderr, "uncaught exception: %s\n", js_tostring(J, -1));
+ /* return to javascript to abort */
+}
+
+int js_ploadstring(js_State *J, const char *filename, const char *source)
+{
+ if (js_try(J))
+ return 1;
+ js_loadstring(J, filename, source);
+ js_endtry(J);
+ return 0;
+}
+
+int js_ploadfile(js_State *J, const char *filename)
+{
+ if (js_try(J))
+ return 1;
+ js_loadfile(J, filename);
+ js_endtry(J);
+ return 0;
+}
+
+static void js_loadstringx(js_State *J, const char *filename, const char *source, int iseval)
+{
+ js_Ast *P;
+ js_Function *F;
+
+ if (js_try(J)) {
+ jsP_freeparse(J);
+ js_throw(J);
+ }
+
+ P = jsP_parse(J, filename, source);
+ F = jsC_compile(J, P);
+ jsP_freeparse(J);
+ js_newscript(J, F, iseval ? (J->strict ? J->E : NULL) : J->GE);
+
+ js_endtry(J);
+}
+
+void js_loadeval(js_State *J, const char *filename, const char *source)
+{
+ js_loadstringx(J, filename, source, 1);
+}
+
+void js_loadstring(js_State *J, const char *filename, const char *source)
+{
+ js_loadstringx(J, filename, source, 0);
+}
+
+void js_loadfile(js_State *J, const char *filename)
+{
+ FILE *f;
+ char *s;
+ int n, t;
+
+ f = fopen(filename, "rb");
+ if (!f) {
+ js_error(J, "cannot open file: '%s'", filename);
+ }
+
+ if (fseek(f, 0, SEEK_END) < 0) {
+ fclose(f);
+ js_error(J, "cannot seek in file: '%s'", filename);
+ }
+
+ n = ftell(f);
+ if (n < 0) {
+ fclose(f);
+ js_error(J, "cannot tell in file: '%s'", filename);
+ }
+
+ if (fseek(f, 0, SEEK_SET) < 0) {
+ fclose(f);
+ js_error(J, "cannot seek in file: '%s'", filename);
+ }
+
+ s = js_malloc(J, n + 1); /* add space for string terminator */
+ if (!s) {
+ fclose(f);
+ js_error(J, "cannot allocate storage for file contents: '%s'", filename);
+ }
+
+ t = fread(s, 1, n, f);
+ if (t != n) {
+ js_free(J, s);
+ fclose(f);
+ js_error(J, "cannot read data from file: '%s'", filename);
+ }
+
+ s[n] = 0; /* zero-terminate string containing file data */
+
+ if (js_try(J)) {
+ js_free(J, s);
+ fclose(f);
+ js_throw(J);
+ }
+
+ js_loadstring(J, filename, s);
+
+ js_free(J, s);
+ fclose(f);
+ js_endtry(J);
+}
+
+int js_dostring(js_State *J, const char *source, int report)
+{
+ if (js_try(J)) {
+ fprintf(stderr, "%s\n", js_tostring(J, -1));
+ js_pop(J, 1);
+ return 1;
+ }
+ js_loadstring(J, "[string]", source);
+ js_pushglobal(J);
+ js_call(J, 0);
+ if (report)
+ if (js_isdefined(J, -1))
+ printf("%s\n", js_tostring(J, -1));
+ js_pop(J, 1);
+ js_endtry(J);
+ return 0;
+}
+
+int js_dofile(js_State *J, const char *filename)
+{
+ if (js_try(J)) {
+ fprintf(stderr, "%s\n", js_tostring(J, -1));
+ js_pop(J, 1);
+ return 1;
+ }
+ js_loadfile(J, filename);
+ js_pushglobal(J);
+ js_call(J, 0);
+ js_pop(J, 1);
+ js_endtry(J);
+ return 0;
+}
+
+js_Panic js_atpanic(js_State *J, js_Panic panic)
+{
+ js_Panic old = J->panic;
+ J->panic = panic;
+ return old;
+}
+
+void js_setcontext(js_State *J, void *uctx)
+{
+ J->uctx = uctx;
+}
+
+void *js_getcontext(js_State *J)
+{
+ return J->uctx;
+}
+
+js_State *js_newstate(js_Alloc alloc, void *actx, int flags)
+{
+ js_State *J;
+
+ assert(sizeof(js_Value) == 16);
+ assert(offsetof(js_Value, type) == 15);
+
+ if (!alloc)
+ alloc = js_defaultalloc;
+
+ J = alloc(actx, NULL, sizeof *J);
+ if (!J)
+ return NULL;
+ memset(J, 0, sizeof(*J));
+ J->actx = actx;
+ J->alloc = alloc;
+
+ if (flags & JS_STRICT)
+ J->strict = 1;
+
+ J->trace[0].name = "?";
+ J->trace[0].file = "[C]";
+ J->trace[0].line = 0;
+
+ J->panic = js_defaultpanic;
+
+ J->stack = alloc(actx, NULL, JS_STACKSIZE * sizeof *J->stack);
+ if (!J->stack) {
+ alloc(actx, NULL, 0);
+ return NULL;
+ }
+
+ J->gcmark = 1;
+ J->nextref = 0;
+
+ J->R = jsV_newobject(J, JS_COBJECT, NULL);
+ J->G = jsV_newobject(J, JS_COBJECT, NULL);
+ J->E = jsR_newenvironment(J, J->G, NULL);
+ J->GE = J->E;
+
+ jsB_init(J);
+
+ return J;
+}
diff --git a/mujs/jsstring.c b/mujs/jsstring.c
new file mode 100644
index 0000000..a5dd7a0
--- /dev/null
+++ b/mujs/jsstring.c
@@ -0,0 +1,687 @@
+#include "jsi.h"
+#include "jsvalue.h"
+#include "jsbuiltin.h"
+#include "utf.h"
+#include "regex.h"
+
+int js_runeat(js_State *J, const char *s, int i)
+{
+ Rune rune = 0;
+ while (i-- >= 0) {
+ rune = *(unsigned char*)s;
+ if (rune < Runeself) {
+ if (rune == 0)
+ return 0;
+ ++s;
+ } else
+ s += chartorune(&rune, s);
+ }
+ return rune;
+}
+
+const char *js_utfidxtoptr(const char *s, int i)
+{
+ Rune rune;
+ while (i-- > 0) {
+ rune = *(unsigned char*)s;
+ if (rune < Runeself) {
+ if (rune == 0)
+ return NULL;
+ ++s;
+ } else
+ s += chartorune(&rune, s);
+ }
+ return s;
+}
+
+int js_utfptrtoidx(const char *s, const char *p)
+{
+ Rune rune;
+ int i = 0;
+ while (s < p) {
+ if (*(unsigned char *)s < Runeself)
+ ++s;
+ else
+ s += chartorune(&rune, s);
+ ++i;
+ }
+ return i;
+}
+
+static void jsB_new_String(js_State *J)
+{
+ js_newstring(J, js_gettop(J) > 1 ? js_tostring(J, 1) : "");
+}
+
+static void jsB_String(js_State *J)
+{
+ js_pushstring(J, js_gettop(J) > 1 ? js_tostring(J, 1) : "");
+}
+
+static void Sp_toString(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
+ js_pushliteral(J, self->u.s.string);
+}
+
+static void Sp_valueOf(js_State *J)
+{
+ js_Object *self = js_toobject(J, 0);
+ if (self->type != JS_CSTRING) js_typeerror(J, "not a string");
+ js_pushliteral(J, self->u.s.string);
+}
+
+static void Sp_charAt(js_State *J)
+{
+ char buf[UTFmax + 1];
+ const char *s = js_tostring(J, 0);
+ int pos = js_tointeger(J, 1);
+ Rune rune = js_runeat(J, s, pos);
+ if (rune > 0) {
+ buf[runetochar(buf, &rune)] = 0;
+ js_pushstring(J, buf);
+ } else {
+ js_pushliteral(J, "");
+ }
+}
+
+static void Sp_charCodeAt(js_State *J)
+{
+ const char *s = js_tostring(J, 0);
+ int pos = js_tointeger(J, 1);
+ Rune rune = js_runeat(J, s, pos);
+ if (rune > 0)
+ js_pushnumber(J, rune);
+ else
+ js_pushnumber(J, NAN);
+}
+
+static void Sp_concat(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+ unsigned int n;
+ char * volatile out;
+ const char *s;
+
+ if (top == 1)
+ return;
+
+ s = js_tostring(J, 0);
+ n = strlen(s);
+ out = js_malloc(J, n + 1);
+ strcpy(out, s);
+
+ if (js_try(J)) {
+ js_free(J, out);
+ js_throw(J);
+ }
+
+ for (i = 1; i < top; ++i) {
+ s = js_tostring(J, i);
+ n += strlen(s);
+ out = realloc(out, n + 1);
+ strcat(out, s);
+ }
+
+ js_pushstring(J, out);
+ js_endtry(J);
+ js_free(J, out);
+}
+
+static void Sp_indexOf(js_State *J)
+{
+ const char *haystack = js_tostring(J, 0);
+ const char *needle = js_tostring(J, 1);
+ int pos = js_tointeger(J, 2);
+ int len = strlen(needle);
+ int k = 0;
+ Rune rune;
+ while (*haystack) {
+ if (k >= pos && !strncmp(haystack, needle, len)) {
+ js_pushnumber(J, k);
+ return;
+ }
+ haystack += chartorune(&rune, haystack);
+ ++k;
+ }
+ js_pushnumber(J, -1);
+}
+
+static void Sp_lastIndexOf(js_State *J)
+{
+ const char *haystack = js_tostring(J, 0);
+ const char *needle = js_tostring(J, 1);
+ int pos = js_isdefined(J, 2) ? js_tointeger(J, 2) : strlen(haystack);
+ int len = strlen(needle);
+ int k = 0, last = -1;
+ Rune rune;
+ while (*haystack && k <= pos) {
+ if (!strncmp(haystack, needle, len))
+ last = k;
+ haystack += chartorune(&rune, haystack);
+ ++k;
+ }
+ js_pushnumber(J, last);
+}
+
+static void Sp_localeCompare(js_State *J)
+{
+ const char *a = js_tostring(J, 0);
+ const char *b = js_tostring(J, 1);
+ js_pushnumber(J, strcmp(a, b));
+}
+
+static void Sp_slice(js_State *J)
+{
+ const char *str = js_tostring(J, 0);
+ const char *ss, *ee;
+ int len = utflen(str);
+ int s = js_tointeger(J, 1);
+ int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
+
+ s = s < 0 ? s + len : s;
+ e = e < 0 ? e + len : e;
+
+ s = s < 0 ? 0 : s > len ? len : s;
+ e = e < 0 ? 0 : e > len ? len : e;
+
+ if (s < e) {
+ ss = js_utfidxtoptr(str, s);
+ ee = js_utfidxtoptr(ss, e - s);
+ } else {
+ ss = js_utfidxtoptr(str, e);
+ ee = js_utfidxtoptr(ss, s - e);
+ }
+
+ js_pushlstring(J, ss, ee - ss);
+}
+
+static void Sp_substring(js_State *J)
+{
+ const char *str = js_tostring(J, 0);
+ const char *ss, *ee;
+ int len = utflen(str);
+ int s = js_tointeger(J, 1);
+ int e = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
+
+ s = s < 0 ? 0 : s > len ? len : s;
+ e = e < 0 ? 0 : e > len ? len : e;
+
+ if (s < e) {
+ ss = js_utfidxtoptr(str, s);
+ ee = js_utfidxtoptr(ss, e - s);
+ } else {
+ ss = js_utfidxtoptr(str, e);
+ ee = js_utfidxtoptr(ss, s - e);
+ }
+
+ js_pushlstring(J, ss, ee - ss);
+}
+
+static void Sp_toLowerCase(js_State *J)
+{
+ const char *src = js_tostring(J, 0);
+ char *dst = js_malloc(J, UTFmax * strlen(src) + 1);
+ const char *s = src;
+ char *d = dst;
+ Rune rune;
+ while (*s) {
+ s += chartorune(&rune, s);
+ rune = tolowerrune(rune);
+ d += runetochar(d, &rune);
+ }
+ *d = 0;
+ if (js_try(J)) {
+ js_free(J, dst);
+ js_throw(J);
+ }
+ js_pushstring(J, dst);
+ js_endtry(J);
+ js_free(J, dst);
+}
+
+static void Sp_toUpperCase(js_State *J)
+{
+ const char *src = js_tostring(J, 0);
+ char *dst = js_malloc(J, UTFmax * strlen(src) + 1);
+ const char *s = src;
+ char *d = dst;
+ Rune rune;
+ while (*s) {
+ s += chartorune(&rune, s);
+ rune = toupperrune(rune);
+ d += runetochar(d, &rune);
+ }
+ *d = 0;
+ if (js_try(J)) {
+ js_free(J, dst);
+ js_throw(J);
+ }
+ js_pushstring(J, dst);
+ js_endtry(J);
+ js_free(J, dst);
+}
+
+static int istrim(int c)
+{
+ return c == 0x9 || c == 0xB || c == 0xC || c == 0x20 || c == 0xA0 || c == 0xFEFF ||
+ c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
+}
+
+static void Sp_trim(js_State *J)
+{
+ const char *s, *e;
+ s = js_tostring(J, 0);
+ while (istrim(*s))
+ ++s;
+ e = s + strlen(s);
+ while (e > s && istrim(e[-1]))
+ --e;
+ js_pushlstring(J, s, e - s);
+}
+
+static void S_fromCharCode(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+ Rune c;
+ char *s, *p;
+
+ s = p = js_malloc(J, (top-1) * UTFmax + 1);
+
+ if (js_try(J)) {
+ js_free(J, s);
+ js_throw(J);
+ }
+
+ for (i = 1; i < top; ++i) {
+ c = js_touint16(J, i);
+ p += runetochar(p, &c);
+ }
+ *p = 0;
+ js_pushstring(J, s);
+
+ js_endtry(J);
+ js_free(J, s);
+}
+
+static void Sp_match(js_State *J)
+{
+ js_Regexp *re;
+ const char *text;
+ unsigned int len;
+ const char *a, *b, *c, *e;
+ Resub m;
+
+ text = js_tostring(J, 0);
+
+ if (js_isregexp(J, 1))
+ js_copy(J, 1);
+ else if (js_isundefined(J, 1))
+ js_newregexp(J, "", 0);
+ else
+ js_newregexp(J, js_tostring(J, 1), 0);
+
+ re = js_toregexp(J, -1);
+ if (!(re->flags & JS_REGEXP_G)) {
+ js_RegExp_prototype_exec(J, re, text);
+ return;
+ }
+
+ re->last = 0;
+
+ js_newarray(J);
+
+ len = 0;
+ a = text;
+ e = text + strlen(text);
+ while (a <= e) {
+ if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0))
+ break;
+
+ b = m.sub[0].sp;
+ c = m.sub[0].ep;
+
+ js_pushlstring(J, b, c - b);
+ js_setindex(J, -2, len++);
+
+ a = c;
+ if (c - b == 0)
+ ++a;
+ }
+}
+
+static void Sp_search(js_State *J)
+{
+ js_Regexp *re;
+ const char *text;
+ Resub m;
+
+ text = js_tostring(J, 0);
+
+ if (js_isregexp(J, 1))
+ js_copy(J, 1);
+ else if (js_isundefined(J, 1))
+ js_newregexp(J, "", 0);
+ else
+ js_newregexp(J, js_tostring(J, 1), 0);
+
+ re = js_toregexp(J, -1);
+
+ if (!js_regexec(re->prog, text, &m, 0))
+ js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp));
+ else
+ js_pushnumber(J, -1);
+}
+
+static void Sp_replace_regexp(js_State *J)
+{
+ js_Regexp *re;
+ const char *source, *s, *r;
+ js_Buffer *sb = NULL;
+ unsigned int n, x;
+ Resub m;
+
+ source = js_tostring(J, 0);
+ re = js_toregexp(J, 1);
+
+ if (js_regexec(re->prog, source, &m, 0)) {
+ js_copy(J, 0);
+ return;
+ }
+
+ re->last = 0;
+
+loop:
+ s = m.sub[0].sp;
+ n = m.sub[0].ep - m.sub[0].sp;
+
+ if (js_iscallable(J, 2)) {
+ js_copy(J, 2);
+ js_pushundefinedthis(J);
+ for (x = 0; m.sub[x].sp; ++x) /* arg 0..x: substring and subexps that matched */
+ js_pushlstring(J, m.sub[x].sp, m.sub[x].ep - m.sub[x].sp);
+ js_pushnumber(J, s - source); /* arg x+2: offset within search string */
+ js_copy(J, 0); /* arg x+3: search string */
+ js_call(J, 2 + x);
+ r = js_tostring(J, -1);
+ js_putm(J, &sb, source, s);
+ js_puts(J, &sb, r);
+ js_pop(J, 1);
+ } else {
+ r = js_tostring(J, 2);
+ js_putm(J, &sb, source, s);
+ while (*r) {
+ if (*r == '$') {
+ switch (*(++r)) {
+ case '$': js_putc(J, &sb, '$'); break;
+ case '`': js_putm(J, &sb, source, s); break;
+ case '\'': js_puts(J, &sb, s + n); break;
+ case '&':
+ js_putm(J, &sb, s, s + n);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ x = *r - '0';
+ if (r[1] >= '0' && r[1] <= '9')
+ x = x * 10 + *(++r) - '0';
+ if (x > 0 && x < m.nsub) {
+ js_putm(J, &sb, m.sub[x].sp, m.sub[x].ep);
+ } else {
+ js_putc(J, &sb, '$');
+ if (x > 10) {
+ js_putc(J, &sb, '0' + x / 10);
+ js_putc(J, &sb, '0' + x % 10);
+ } else {
+ js_putc(J, &sb, '0' + x);
+ }
+ }
+ break;
+ default:
+ js_putc(J, &sb, '$');
+ js_putc(J, &sb, *r);
+ break;
+ }
+ ++r;
+ } else {
+ js_putc(J, &sb, *r++);
+ }
+ }
+ }
+
+ if (re->flags & JS_REGEXP_G) {
+ source = m.sub[0].ep;
+ if (n == 0) {
+ if (*source)
+ js_putc(J, &sb, *source++);
+ else
+ goto end;
+ }
+ if (!js_regexec(re->prog, source, &m, REG_NOTBOL))
+ goto loop;
+ }
+
+end:
+ js_puts(J, &sb, s + n);
+ js_putc(J, &sb, 0);
+
+ if (js_try(J)) {
+ js_free(J, sb);
+ js_throw(J);
+ }
+ js_pushstring(J, sb ? sb->s : "");
+ js_endtry(J);
+ js_free(J, sb);
+}
+
+static void Sp_replace_string(js_State *J)
+{
+ const char *source, *needle, *s, *r;
+ js_Buffer *sb = NULL;
+ int n;
+
+ source = js_tostring(J, 0);
+ needle = js_tostring(J, 1);
+
+ s = strstr(source, needle);
+ if (!s) {
+ js_copy(J, 0);
+ return;
+ }
+ n = strlen(needle);
+
+ if (js_iscallable(J, 2)) {
+ js_copy(J, 2);
+ js_pushundefinedthis(J);
+ js_pushlstring(J, s, n); /* arg 1: substring that matched */
+ js_pushnumber(J, s - source); /* arg 2: offset within search string */
+ js_copy(J, 0); /* arg 3: search string */
+ js_call(J, 3);
+ r = js_tostring(J, -1);
+ js_putm(J, &sb, source, s);
+ js_puts(J, &sb, r);
+ js_puts(J, &sb, s + n);
+ js_putc(J, &sb, 0);
+ js_pop(J, 1);
+ } else {
+ r = js_tostring(J, 2);
+ js_putm(J, &sb, source, s);
+ while (*r) {
+ if (*r == '$') {
+ switch (*(++r)) {
+ case '$': js_putc(J, &sb, '$'); break;
+ case '&': js_putm(J, &sb, s, s + n); break;
+ case '`': js_putm(J, &sb, source, s); break;
+ case '\'': js_puts(J, &sb, s + n); break;
+ default: js_putc(J, &sb, '$'); js_putc(J, &sb, *r); break;
+ }
+ ++r;
+ } else {
+ js_putc(J, &sb, *r++);
+ }
+ }
+ js_puts(J, &sb, s + n);
+ js_putc(J, &sb, 0);
+ }
+
+ if (js_try(J)) {
+ js_free(J, sb);
+ js_throw(J);
+ }
+ js_pushstring(J, sb ? sb->s : "");
+ js_endtry(J);
+ js_free(J, sb);
+}
+
+static void Sp_replace(js_State *J)
+{
+ if (js_isregexp(J, 1))
+ Sp_replace_regexp(J);
+ else
+ Sp_replace_string(J);
+}
+
+static void Sp_split_regexp(js_State *J)
+{
+ js_Regexp *re;
+ const char *text;
+ unsigned int limit, len, k;
+ const char *p, *a, *b, *c, *e;
+ Resub m;
+
+ text = js_tostring(J, 0);
+ re = js_toregexp(J, 1);
+ limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30;
+
+ js_newarray(J);
+ len = 0;
+
+ e = text + strlen(text);
+
+ /* splitting the empty string */
+ if (e == text) {
+ if (js_regexec(re->prog, text, &m, 0)) {
+ if (len == limit) return;
+ js_pushliteral(J, "");
+ js_setindex(J, -2, 0);
+ }
+ return;
+ }
+
+ p = a = text;
+ while (a < e) {
+ if (js_regexec(re->prog, a, &m, a > text ? REG_NOTBOL : 0))
+ break; /* no match */
+
+ b = m.sub[0].sp;
+ c = m.sub[0].ep;
+
+ /* empty string at end of last match */
+ if (b == p) {
+ ++a;
+ continue;
+ }
+
+ if (len == limit) return;
+ js_pushlstring(J, p, b - p);
+ js_setindex(J, -2, len++);
+
+ for (k = 1; k < m.nsub; ++k) {
+ if (len == limit) return;
+ js_pushlstring(J, m.sub[k].sp, m.sub[k].ep - m.sub[k].sp);
+ js_setindex(J, -2, len++);
+ }
+
+ a = p = c;
+ }
+
+ if (len == limit) return;
+ js_pushstring(J, p);
+ js_setindex(J, -2, len);
+}
+
+static void Sp_split_string(js_State *J)
+{
+ const char *str = js_tostring(J, 0);
+ const char *sep = js_tostring(J, 1);
+ unsigned int limit = js_isdefined(J, 2) ? js_touint32(J, 2) : 1 << 30;
+ unsigned int i, n;
+
+ js_newarray(J);
+
+ n = strlen(sep);
+
+ /* empty string */
+ if (n == 0) {
+ Rune rune;
+ for (i = 0; *str && i < limit; ++i) {
+ n = chartorune(&rune, str);
+ js_pushlstring(J, str, n);
+ js_setindex(J, -2, i);
+ str += n;
+ }
+ return;
+ }
+
+ for (i = 0; str && i < limit; ++i) {
+ const char *s = strstr(str, sep);
+ if (s) {
+ js_pushlstring(J, str, s-str);
+ js_setindex(J, -2, i);
+ str = s + n;
+ } else {
+ js_pushstring(J, str);
+ js_setindex(J, -2, i);
+ str = NULL;
+ }
+ }
+}
+
+static void Sp_split(js_State *J)
+{
+ if (js_isundefined(J, 1)) {
+ js_newarray(J);
+ js_copy(J, 0);
+ js_setindex(J, -2, 0);
+ } else if (js_isregexp(J, 1)) {
+ Sp_split_regexp(J);
+ } else {
+ Sp_split_string(J);
+ }
+}
+
+void jsB_initstring(js_State *J)
+{
+ J->String_prototype->u.s.string = "";
+ J->String_prototype->u.s.length = 0;
+
+ js_pushobject(J, J->String_prototype);
+ {
+ jsB_propf(J, "toString", Sp_toString, 0);
+ jsB_propf(J, "valueOf", Sp_valueOf, 0);
+ jsB_propf(J, "charAt", Sp_charAt, 1);
+ jsB_propf(J, "charCodeAt", Sp_charCodeAt, 1);
+ jsB_propf(J, "concat", Sp_concat, 1);
+ jsB_propf(J, "indexOf", Sp_indexOf, 1);
+ jsB_propf(J, "lastIndexOf", Sp_lastIndexOf, 1);
+ jsB_propf(J, "localeCompare", Sp_localeCompare, 1);
+ jsB_propf(J, "match", Sp_match, 1);
+ jsB_propf(J, "replace", Sp_replace, 2);
+ jsB_propf(J, "search", Sp_search, 1);
+ jsB_propf(J, "slice", Sp_slice, 2);
+ jsB_propf(J, "split", Sp_split, 2);
+ jsB_propf(J, "substring", Sp_substring, 2);
+ jsB_propf(J, "toLowerCase", Sp_toLowerCase, 0);
+ jsB_propf(J, "toLocaleLowerCase", Sp_toLowerCase, 0);
+ jsB_propf(J, "toUpperCase", Sp_toUpperCase, 0);
+ jsB_propf(J, "toLocaleUpperCase", Sp_toUpperCase, 0);
+
+ /* ES5 */
+ jsB_propf(J, "trim", Sp_trim, 0);
+ }
+ js_newcconstructor(J, jsB_String, jsB_new_String, "String", 1);
+ {
+ jsB_propf(J, "fromCharCode", S_fromCharCode, 1);
+ }
+ js_defglobal(J, "String", JS_DONTENUM);
+}
diff --git a/mujs/jsvalue.c b/mujs/jsvalue.c
new file mode 100644
index 0000000..ed93ded
--- /dev/null
+++ b/mujs/jsvalue.c
@@ -0,0 +1,586 @@
+#include "jsi.h"
+#include "jslex.h"
+#include "jscompile.h"
+#include "jsvalue.h"
+#include "utf.h"
+
+#define JSV_ISSTRING(v) (v->type==JS_TSHRSTR || v->type==JS_TMEMSTR || v->type==JS_TLITSTR)
+#define JSV_TOSTRING(v) (v->type==JS_TSHRSTR ? v->u.shrstr : v->type==JS_TLITSTR ? v->u.litstr : v->type==JS_TMEMSTR ? v->u.memstr->p : "")
+
+double jsV_numbertointeger(double n)
+{
+ double sign = n < 0 ? -1 : 1;
+ if (isnan(n)) return 0;
+ if (n == 0 || isinf(n)) return n;
+ return sign * floor(abs(n));
+}
+
+int jsV_numbertoint32(double n)
+{
+ double two32 = 4294967296.0;
+ double two31 = 2147483648.0;
+
+ if (!isfinite(n) || n == 0)
+ return 0;
+
+ n = fmod(n, two32);
+ n = n >= 0 ? floor(n) : ceil(n) + two32;
+ if (n >= two31)
+ return n - two32;
+ else
+ return n;
+}
+
+unsigned int jsV_numbertouint32(double n)
+{
+ return jsV_numbertoint32(n);
+}
+
+short jsV_numbertoint16(double n)
+{
+ return jsV_numbertoint32(n);
+}
+
+unsigned short jsV_numbertouint16(double n)
+{
+ return jsV_numbertoint32(n);
+}
+
+/* obj.toString() */
+static int jsV_toString(js_State *J, js_Object *obj)
+{
+ js_pushobject(J, obj);
+ js_getproperty(J, -1, "toString");
+ if (js_iscallable(J, -1)) {
+ js_rot2(J);
+ js_call(J, 0);
+ if (js_isprimitive(J, -1))
+ return 1;
+ js_pop(J, 1);
+ return 0;
+ }
+ js_pop(J, 2);
+ return 0;
+}
+
+/* obj.valueOf() */
+static int jsV_valueOf(js_State *J, js_Object *obj)
+{
+ js_pushobject(J, obj);
+ js_getproperty(J, -1, "valueOf");
+ if (js_iscallable(J, -1)) {
+ js_rot2(J);
+ js_call(J, 0);
+ if (js_isprimitive(J, -1))
+ return 1;
+ js_pop(J, 1);
+ return 0;
+ }
+ js_pop(J, 2);
+ return 0;
+}
+
+/* ToPrimitive() on a value */
+void jsV_toprimitive(js_State *J, js_Value *v, int preferred)
+{
+ js_Object *obj;
+
+ if (v->type != JS_TOBJECT)
+ return;
+
+ obj = v->u.object;
+
+ if (preferred == JS_HNONE)
+ preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER;
+
+ if (preferred == JS_HSTRING) {
+ if (jsV_toString(J, obj) || jsV_valueOf(J, obj)) {
+ *v = *js_tovalue(J, -1);
+ js_pop(J, 1);
+ return;
+ }
+ } else {
+ if (jsV_valueOf(J, obj) || jsV_toString(J, obj)) {
+ *v = *js_tovalue(J, -1);
+ js_pop(J, 1);
+ return;
+ }
+ }
+
+ v->type = JS_TLITSTR;
+ v->u.litstr = "[object]";
+ return;
+}
+
+/* ToBoolean() on a value */
+int jsV_toboolean(js_State *J, js_Value *v)
+{
+ switch (v->type) {
+ default:
+ case JS_TSHRSTR: return v->u.shrstr[0] != 0;
+ case JS_TUNDEFINED: return 0;
+ case JS_TNULL: return 0;
+ case JS_TBOOLEAN: return v->u.boolean;
+ case JS_TNUMBER: return v->u.number != 0 && !isnan(v->u.number);
+ case JS_TLITSTR: return v->u.litstr[0] != 0;
+ case JS_TMEMSTR: return v->u.memstr->p[0] != 0;
+ case JS_TOBJECT: return 1;
+ }
+}
+
+const char *js_itoa(char *out, unsigned int a)
+{
+ char buf[32], *s = out;
+ unsigned int i = 0;
+ while (a) {
+ buf[i++] = (a % 10) + '0';
+ a /= 10;
+ }
+ if (i == 0)
+ buf[i++] = '0';
+ while (i > 0)
+ *s++ = buf[--i];
+ *s = 0;
+ return out;
+}
+
+double js_stringtofloat(const char *s, char **ep)
+{
+ char *end;
+ double n;
+ const char *e = s;
+ int isflt = 0;
+ if (*e == '+' || *e == '-') ++e;
+ while (*e >= '0' && *e <= '9') ++e;
+ if (*e == '.') { ++e; isflt = 1; }
+ while (*e >= '0' && *e <= '9') ++e;
+ if (*e == 'e' || *e == 'E') {
+ ++e;
+ if (*e == '+' || *e == '-') ++e;
+ while (*e >= '0' && *e <= '9') ++e;
+ isflt = 1;
+ }
+ if (isflt || e - s > 9)
+ n = js_strtod(s, &end);
+ else
+ n = strtol(s, &end, 10);
+ if (end == e) {
+ *ep = (char*)e;
+ return n;
+ }
+ *ep = (char*)s;
+ return 0;
+}
+
+/* ToNumber() on a string */
+double jsV_stringtonumber(js_State *J, const char *s)
+{
+ char *e;
+ double n;
+ while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s;
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && s[2] != 0)
+ n = strtol(s + 2, &e, 16);
+ else if (!strncmp(s, "Infinity", 8))
+ n = INFINITY, e = (char*)s + 8;
+ else if (!strncmp(s, "+Infinity", 9))
+ n = INFINITY, e = (char*)s + 9;
+ else if (!strncmp(s, "-Infinity", 9))
+ n = -INFINITY, e = (char*)s + 9;
+ else
+ n = js_stringtofloat(s, &e);
+ while (jsY_iswhite(*e) || jsY_isnewline(*e)) ++e;
+ if (*e) return NAN;
+ return n;
+}
+
+/* ToNumber() on a value */
+double jsV_tonumber(js_State *J, js_Value *v)
+{
+ switch (v->type) {
+ default:
+ case JS_TSHRSTR: return jsV_stringtonumber(J, v->u.shrstr);
+ case JS_TUNDEFINED: return NAN;
+ case JS_TNULL: return 0;
+ case JS_TBOOLEAN: return v->u.boolean;
+ case JS_TNUMBER: return v->u.number;
+ case JS_TLITSTR: return jsV_stringtonumber(J, v->u.litstr);
+ case JS_TMEMSTR: return jsV_stringtonumber(J, v->u.memstr->p);
+ case JS_TOBJECT:
+ jsV_toprimitive(J, v, JS_HNUMBER);
+ return jsV_tonumber(J, v);
+ }
+}
+
+double jsV_tointeger(js_State *J, js_Value *v)
+{
+ return jsV_numbertointeger(jsV_tonumber(J, v));
+}
+
+/* ToString() on a number */
+const char *jsV_numbertostring(js_State *J, char buf[32], double f)
+{
+ char digits[32], *p = buf, *s = digits;
+ int exp, neg, ndigits, point;
+
+ if (isnan(f)) return "NaN";
+ if (isinf(f)) return f < 0 ? "-Infinity" : "Infinity";
+ if (f == 0) return "0";
+
+ js_dtoa(f, digits, &exp, &neg, &ndigits);
+ point = ndigits + exp;
+
+ if (neg)
+ *p++ = '-';
+
+ if (point < -5 || point > 21) {
+ *p++ = *s++;
+ if (ndigits > 1) {
+ int n = ndigits - 1;
+ *p++ = '.';
+ while (n--)
+ *p++ = *s++;
+ }
+ js_fmtexp(p, point - 1);
+ }
+
+ else if (point <= 0) {
+ *p++ = '0';
+ *p++ = '.';
+ while (point++ < 0)
+ *p++ = '0';
+ while (ndigits-- > 0)
+ *p++ = *s++;
+ *p = 0;
+ }
+
+ else {
+ while (ndigits-- > 0) {
+ *p++ = *s++;
+ if (--point == 0 && ndigits > 0)
+ *p++ = '.';
+ }
+ while (point-- > 0)
+ *p++ = '0';
+ *p = 0;
+ }
+
+ return buf;
+}
+
+/* ToString() on a value */
+const char *jsV_tostring(js_State *J, js_Value *v)
+{
+ char buf[32];
+ const char *p;
+ switch (v->type) {
+ default:
+ case JS_TSHRSTR: return v->u.shrstr;
+ case JS_TUNDEFINED: return "undefined";
+ case JS_TNULL: return "null";
+ case JS_TBOOLEAN: return v->u.boolean ? "true" : "false";
+ case JS_TLITSTR: return v->u.litstr;
+ case JS_TMEMSTR: return v->u.memstr->p;
+ case JS_TNUMBER:
+ p = jsV_numbertostring(J, buf, v->u.number);
+ if (p == buf) {
+ unsigned int n = strlen(p);
+ if (n <= offsetof(js_Value, type)) {
+ char *s = v->u.shrstr;
+ while (n--) *s++ = *p++;
+ *s = 0;
+ v->type = JS_TSHRSTR;
+ return v->u.shrstr;
+ } else {
+ v->type = JS_TMEMSTR;
+ v->u.memstr = jsV_newmemstring(J, p, n);
+ return v->u.memstr->p;
+ }
+ }
+ return p;
+ case JS_TOBJECT:
+ jsV_toprimitive(J, v, JS_HSTRING);
+ return jsV_tostring(J, v);
+ }
+}
+
+/* Objects */
+
+static js_Object *jsV_newboolean(js_State *J, int v)
+{
+ js_Object *obj = jsV_newobject(J, JS_CBOOLEAN, J->Boolean_prototype);
+ obj->u.boolean = v;
+ return obj;
+}
+
+static js_Object *jsV_newnumber(js_State *J, double v)
+{
+ js_Object *obj = jsV_newobject(J, JS_CNUMBER, J->Number_prototype);
+ obj->u.number = v;
+ return obj;
+}
+
+static js_Object *jsV_newstring(js_State *J, const char *v)
+{
+ js_Object *obj = jsV_newobject(J, JS_CSTRING, J->String_prototype);
+ obj->u.s.string = js_intern(J, v); /* TODO: js_String */
+ obj->u.s.length = utflen(v);
+ return obj;
+}
+
+/* ToObject() on a value */
+js_Object *jsV_toobject(js_State *J, js_Value *v)
+{
+ switch (v->type) {
+ default:
+ case JS_TSHRSTR: return jsV_newstring(J, v->u.shrstr);
+ case JS_TUNDEFINED: js_typeerror(J, "cannot convert undefined to object");
+ case JS_TNULL: js_typeerror(J, "cannot convert null to object");
+ case JS_TBOOLEAN: return jsV_newboolean(J, v->u.boolean);
+ case JS_TNUMBER: return jsV_newnumber(J, v->u.number);
+ case JS_TLITSTR: return jsV_newstring(J, v->u.litstr);
+ case JS_TMEMSTR: return jsV_newstring(J, v->u.memstr->p);
+ case JS_TOBJECT: return v->u.object;
+ }
+}
+
+void js_newobject(js_State *J)
+{
+ js_pushobject(J, jsV_newobject(J, JS_COBJECT, J->Object_prototype));
+}
+
+void js_newarray(js_State *J)
+{
+ js_pushobject(J, jsV_newobject(J, JS_CARRAY, J->Array_prototype));
+}
+
+void js_newboolean(js_State *J, int v)
+{
+ js_pushobject(J, jsV_newboolean(J, v));
+}
+
+void js_newnumber(js_State *J, double v)
+{
+ js_pushobject(J, jsV_newnumber(J, v));
+}
+
+void js_newstring(js_State *J, const char *v)
+{
+ js_pushobject(J, jsV_newstring(J, v));
+}
+
+void js_newfunction(js_State *J, js_Function *fun, js_Environment *scope)
+{
+ js_Object *obj = jsV_newobject(J, JS_CFUNCTION, J->Function_prototype);
+ obj->u.f.function = fun;
+ obj->u.f.scope = scope;
+ js_pushobject(J, obj);
+ {
+ js_pushnumber(J, fun->numparams);
+ js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+ js_newobject(J);
+ {
+ js_copy(J, -2);
+ js_defproperty(J, -2, "constructor", JS_DONTENUM);
+ }
+ js_defproperty(J, -2, "prototype", JS_DONTCONF);
+ }
+}
+
+void js_newscript(js_State *J, js_Function *fun, js_Environment *scope)
+{
+ js_Object *obj = jsV_newobject(J, JS_CSCRIPT, NULL);
+ obj->u.f.function = fun;
+ obj->u.f.scope = scope;
+ js_pushobject(J, obj);
+}
+
+void js_newcfunction(js_State *J, js_CFunction cfun, const char *name, unsigned int length)
+{
+ js_Object *obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype);
+ obj->u.c.name = name;
+ obj->u.c.function = cfun;
+ obj->u.c.constructor = NULL;
+ obj->u.c.length = length;
+ js_pushobject(J, obj);
+ {
+ js_pushnumber(J, length);
+ js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+ js_newobject(J);
+ {
+ js_copy(J, -2);
+ js_defproperty(J, -2, "constructor", JS_DONTENUM);
+ }
+ js_defproperty(J, -2, "prototype", JS_DONTCONF);
+ }
+}
+
+/* prototype -- constructor */
+void js_newcconstructor(js_State *J, js_CFunction cfun, js_CFunction ccon, const char *name, unsigned int length)
+{
+ js_Object *obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype);
+ obj->u.c.name = name;
+ obj->u.c.function = cfun;
+ obj->u.c.constructor = ccon;
+ js_pushobject(J, obj); /* proto obj */
+ {
+ js_pushnumber(J, length);
+ js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+ js_rot2(J); /* obj proto */
+ js_copy(J, -2); /* obj proto obj */
+ js_defproperty(J, -2, "constructor", JS_DONTENUM);
+ js_defproperty(J, -2, "prototype", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
+ }
+}
+
+void js_newuserdata(js_State *J, const char *tag, void *data, js_Finalize finalize)
+{
+ js_Object *prototype = NULL;
+ js_Object *obj;
+
+ if (js_isobject(J, -1))
+ prototype = js_toobject(J, -1);
+ js_pop(J, 1);
+
+ obj = jsV_newobject(J, JS_CUSERDATA, prototype);
+ obj->u.user.tag = tag;
+ obj->u.user.data = data;
+ obj->u.user.finalize = finalize;
+ js_pushobject(J, obj);
+}
+
+/* Non-trivial operations on values. These are implemented using the stack. */
+
+int js_instanceof(js_State *J)
+{
+ js_Object *O, *V;
+
+ if (!js_iscallable(J, -1))
+ js_typeerror(J, "instanceof: invalid operand");
+
+ if (!js_isobject(J, -2))
+ return 0;
+
+ js_getproperty(J, -1, "prototype");
+ if (!js_isobject(J, -1))
+ js_typeerror(J, "instanceof: 'prototype' property is not an object");
+ O = js_toobject(J, -1);
+ js_pop(J, 1);
+
+ V = js_toobject(J, -2);
+ while (V) {
+ V = V->prototype;
+ if (O == V)
+ return 1;
+ }
+
+ return 0;
+}
+
+void js_concat(js_State *J)
+{
+ js_toprimitive(J, -2, JS_HNONE);
+ js_toprimitive(J, -1, JS_HNONE);
+
+ if (js_isstring(J, -2) || js_isstring(J, -1)) {
+ const char *sa = js_tostring(J, -2);
+ const char *sb = js_tostring(J, -1);
+ /* TODO: create js_String directly */
+ char *sab = js_malloc(J, strlen(sa) + strlen(sb) + 1);
+ strcpy(sab, sa);
+ strcat(sab, sb);
+ if (js_try(J)) {
+ js_free(J, sab);
+ js_throw(J);
+ }
+ js_pop(J, 2);
+ js_pushstring(J, sab);
+ js_endtry(J);
+ js_free(J, sab);
+ } else {
+ double x = js_tonumber(J, -2);
+ double y = js_tonumber(J, -1);
+ js_pop(J, 2);
+ js_pushnumber(J, x + y);
+ }
+}
+
+int js_compare(js_State *J, int *okay)
+{
+ js_toprimitive(J, -2, JS_HNUMBER);
+ js_toprimitive(J, -1, JS_HNUMBER);
+
+ *okay = 1;
+ if (js_isstring(J, -2) && js_isstring(J, -1)) {
+ return strcmp(js_tostring(J, -2), js_tostring(J, -1));
+ } else {
+ double x = js_tonumber(J, -2);
+ double y = js_tonumber(J, -1);
+ if (isnan(x) || isnan(y))
+ *okay = 0;
+ return x < y ? -1 : x > y ? 1 : 0;
+ }
+}
+
+int js_equal(js_State *J)
+{
+ js_Value *x = js_tovalue(J, -2);
+ js_Value *y = js_tovalue(J, -1);
+
+retry:
+ if (JSV_ISSTRING(x) && JSV_ISSTRING(y))
+ return !strcmp(JSV_TOSTRING(x), JSV_TOSTRING(y));
+ if (x->type == y->type) {
+ if (x->type == JS_TUNDEFINED) return 1;
+ if (x->type == JS_TNULL) return 1;
+ if (x->type == JS_TNUMBER) return x->u.number == y->u.number;
+ if (x->type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
+ if (x->type == JS_TOBJECT) return x->u.object == y->u.object;
+ return 0;
+ }
+
+ if (x->type == JS_TNULL && y->type == JS_TUNDEFINED) return 1;
+ if (x->type == JS_TUNDEFINED && y->type == JS_TNULL) return 1;
+
+ if (x->type == JS_TNUMBER && JSV_ISSTRING(y))
+ return x->u.number == jsV_tonumber(J, y);
+ if (JSV_ISSTRING(x) && y->type == JS_TNUMBER)
+ return jsV_tonumber(J, x) == y->u.number;
+
+ if (x->type == JS_TBOOLEAN) {
+ x->type = JS_TNUMBER;
+ x->u.number = x->u.boolean;
+ goto retry;
+ }
+ if (y->type == JS_TBOOLEAN) {
+ y->type = JS_TNUMBER;
+ y->u.number = y->u.boolean;
+ goto retry;
+ }
+ if ((JSV_ISSTRING(x) || x->type == JS_TNUMBER) && y->type == JS_TOBJECT) {
+ jsV_toprimitive(J, y, JS_HNONE);
+ goto retry;
+ }
+ if (x->type == JS_TOBJECT && (JSV_ISSTRING(y) || y->type == JS_TNUMBER)) {
+ jsV_toprimitive(J, x, JS_HNONE);
+ goto retry;
+ }
+
+ return 0;
+}
+
+int js_strictequal(js_State *J)
+{
+ js_Value *x = js_tovalue(J, -2);
+ js_Value *y = js_tovalue(J, -1);
+
+ if (JSV_ISSTRING(x) && JSV_ISSTRING(y))
+ return !strcmp(JSV_TOSTRING(x), JSV_TOSTRING(y));
+
+ if (x->type != y->type) return 0;
+ if (x->type == JS_TUNDEFINED) return 1;
+ if (x->type == JS_TNULL) return 1;
+ if (x->type == JS_TNUMBER) return x->u.number == y->u.number;
+ if (x->type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
+ if (x->type == JS_TOBJECT) return x->u.object == y->u.object;
+ return 0;
+}
diff --git a/mujs/jsvalue.h b/mujs/jsvalue.h
new file mode 100644
index 0000000..1e77df7
--- /dev/null
+++ b/mujs/jsvalue.h
@@ -0,0 +1,184 @@
+#ifndef js_value_h
+#define js_value_h
+
+typedef struct js_Property js_Property;
+typedef struct js_Iterator js_Iterator;
+
+/* Hint to ToPrimitive() */
+enum {
+ JS_HNONE,
+ JS_HNUMBER,
+ JS_HSTRING
+};
+
+enum js_Type {
+ JS_TSHRSTR, /* type tag doubles as string zero-terminator */
+ JS_TUNDEFINED,
+ JS_TNULL,
+ JS_TBOOLEAN,
+ JS_TNUMBER,
+ JS_TLITSTR,
+ JS_TMEMSTR,
+ JS_TOBJECT,
+};
+
+enum js_Class {
+ JS_COBJECT,
+ JS_CARRAY,
+ JS_CFUNCTION,
+ JS_CSCRIPT, /* function created from global/eval code */
+ JS_CCFUNCTION, /* built-in function */
+ JS_CERROR,
+ JS_CBOOLEAN,
+ JS_CNUMBER,
+ JS_CSTRING,
+ JS_CREGEXP,
+ JS_CDATE,
+ JS_CMATH,
+ JS_CJSON,
+ JS_CITERATOR,
+ JS_CUSERDATA,
+};
+
+/*
+ Short strings abuse the js_Value struct. By putting the type tag in the
+ last byte, and using 0 as the tag for short strings, we can use the
+ entire js_Value as string storage by letting the type tag serve double
+ purpose as the string zero terminator.
+*/
+
+struct js_Value
+{
+ union {
+ int boolean;
+ double number;
+ char shrstr[8];
+ const char *litstr;
+ js_String *memstr;
+ js_Object *object;
+ } u;
+ char pad[7]; /* extra storage for shrstr */
+ char type; /* type tag and zero terminator for shrstr */
+};
+
+struct js_String
+{
+ js_String *gcnext;
+ char gcmark;
+ char p[1];
+};
+
+struct js_Regexp
+{
+ void *prog;
+ const char *source;
+ unsigned short flags;
+ unsigned short last;
+};
+
+struct js_Object
+{
+ enum js_Class type;
+ int extensible;
+ js_Property *properties;
+ js_Property *head, **tailp; /* for enumeration */
+ unsigned int count; /* number of properties, for array sparseness check */
+ js_Object *prototype;
+ union {
+ int boolean;
+ double number;
+ struct {
+ const char *string;
+ unsigned int length;
+ } s;
+ struct {
+ unsigned int length;
+ } a;
+ struct {
+ js_Function *function;
+ js_Environment *scope;
+ } f;
+ struct {
+ const char *name;
+ js_CFunction function;
+ js_CFunction constructor;
+ unsigned int length;
+ } c;
+ js_Regexp r;
+ struct {
+ js_Object *target;
+ js_Iterator *head;
+ } iter;
+ struct {
+ const char *tag;
+ void *data;
+ js_Finalize finalize;
+ } user;
+ } u;
+ js_Object *gcnext;
+ int gcmark;
+};
+
+struct js_Property
+{
+ const char *name;
+ js_Property *left, *right;
+ js_Property *next, **prevp; /* for enumeration */
+ int level;
+ int atts;
+ js_Value value;
+ js_Object *getter;
+ js_Object *setter;
+};
+
+struct js_Iterator
+{
+ const char *name;
+ js_Iterator *next;
+};
+
+/* jsrun.c */
+js_String *jsV_newmemstring(js_State *J, const char *s, int n);
+js_Value *js_tovalue(js_State *J, int idx);
+void js_toprimitive(js_State *J, int idx, int hint);
+js_Object *js_toobject(js_State *J, int idx);
+void js_pushvalue(js_State *J, js_Value v);
+void js_pushobject(js_State *J, js_Object *v);
+
+/* jsvalue.c */
+int jsV_toboolean(js_State *J, js_Value *v);
+double jsV_tonumber(js_State *J, js_Value *v);
+double jsV_tointeger(js_State *J, js_Value *v);
+const char *jsV_tostring(js_State *J, js_Value *v);
+js_Object *jsV_toobject(js_State *J, js_Value *v);
+void jsV_toprimitive(js_State *J, js_Value *v, int preferred);
+
+const char *js_itoa(char buf[32], unsigned int a);
+double js_stringtofloat(const char *s, char **ep);
+double jsV_numbertointeger(double n);
+int jsV_numbertoint32(double n);
+unsigned int jsV_numbertouint32(double n);
+short jsV_numbertoint16(double n);
+unsigned short jsV_numbertouint16(double n);
+const char *jsV_numbertostring(js_State *J, char buf[32], double number);
+double jsV_stringtonumber(js_State *J, const char *string);
+
+/* jsproperty.c */
+js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype);
+js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name);
+js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own);
+js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name);
+js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name);
+js_Property *jsV_nextproperty(js_State *J, js_Object *obj, const char *name);
+void jsV_delproperty(js_State *J, js_Object *obj, const char *name);
+
+js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own);
+const char *jsV_nextiterator(js_State *J, js_Object *iter);
+
+void jsV_resizearray(js_State *J, js_Object *obj, unsigned int newlen);
+
+/* jsdump.c */
+void js_dumpobject(js_State *J, js_Object *obj);
+void js_dumpvalue(js_State *J, js_Value v);
+
+#endif
diff --git a/mujs/main.c b/mujs/main.c
new file mode 100644
index 0000000..5c2abe6
--- /dev/null
+++ b/mujs/main.c
@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mujs.h"
+
+#define PS1 "> "
+
+static void jsB_gc(js_State *J)
+{
+ int report = js_toboolean(J, 1);
+ js_gc(J, report);
+ js_pushundefined(J);
+}
+
+static void jsB_load(js_State *J)
+{
+ const char *filename = js_tostring(J, 1);
+ int rv = js_dofile(J, filename);
+ js_pushboolean(J, !rv);
+}
+
+static void jsB_print(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+ for (i = 1; i < top; ++i) {
+ const char *s = js_tostring(J, i);
+ if (i > 1) putchar(' ');
+ fputs(s, stdout);
+ }
+ putchar('\n');
+ js_pushundefined(J);
+}
+
+static void jsB_write(js_State *J)
+{
+ unsigned int i, top = js_gettop(J);
+ for (i = 1; i < top; ++i) {
+ const char *s = js_tostring(J, i);
+ if (i > 1) putchar(' ');
+ fputs(s, stdout);
+ }
+ js_pushundefined(J);
+}
+
+static void jsB_read(js_State *J)
+{
+ const char *filename = js_tostring(J, 1);
+ FILE *f;
+ char *s;
+ int n, t;
+
+ f = fopen(filename, "rb");
+ if (!f) {
+ js_error(J, "cannot open file: '%s'", filename);
+ }
+
+ if (fseek(f, 0, SEEK_END) < 0) {
+ fclose(f);
+ js_error(J, "cannot seek in file: '%s'", filename);
+ }
+
+ n = ftell(f);
+ if (n < 0) {
+ fclose(f);
+ js_error(J, "cannot tell in file: '%s'", filename);
+ }
+
+ if (fseek(f, 0, SEEK_SET) < 0) {
+ fclose(f);
+ js_error(J, "cannot seek in file: '%s'", filename);
+ }
+
+ s = malloc(n + 1);
+ if (!s) {
+ fclose(f);
+ js_error(J, "cannot allocate storage for file contents: '%s'", filename);
+ }
+
+ t = fread(s, 1, n, f);
+ if (t != n) {
+ free(s);
+ fclose(f);
+ js_error(J, "cannot read data from file: '%s'", filename);
+ }
+ s[n] = 0;
+
+ js_pushstring(J, s);
+ free(s);
+ fclose(f);
+}
+
+static void jsB_readline(js_State *J)
+{
+ char line[256];
+ int n;
+ if (!fgets(line, sizeof line, stdin))
+ js_error(J, "cannot read line from stdin");
+ n = strlen(line);
+ if (n > 0 && line[n-1] == '\n')
+ line[n-1] = 0;
+ js_pushstring(J, line);
+}
+
+static void jsB_quit(js_State *J)
+{
+ exit(js_tonumber(J, 1));
+}
+
+static const char *require_js =
+ "function require(name) {\n"
+ "var cache = require.cache;\n"
+ "if (name in cache) return cache[name];\n"
+ "var exports = {};\n"
+ "cache[name] = exports;\n"
+ "Function('exports', read(name+'.js'))(exports);\n"
+ "return exports;\n"
+ "}\n"
+ "require.cache = Object.create(null);\n"
+;
+
+int
+main(int argc, char **argv)
+{
+ char line[256];
+ js_State *J;
+ int i;
+
+ J = js_newstate(NULL, NULL, JS_STRICT);
+
+ js_newcfunction(J, jsB_gc, "gc", 0);
+ js_setglobal(J, "gc");
+
+ js_newcfunction(J, jsB_load, "load", 1);
+ js_setglobal(J, "load");
+
+ js_newcfunction(J, jsB_print, "print", 1);
+ js_setglobal(J, "print");
+
+ js_newcfunction(J, jsB_write, "write", 0);
+ js_setglobal(J, "write");
+
+ js_newcfunction(J, jsB_read, "read", 1);
+ js_setglobal(J, "read");
+
+ js_newcfunction(J, jsB_readline, "readline", 0);
+ js_setglobal(J, "readline");
+
+ js_newcfunction(J, jsB_quit, "quit", 1);
+ js_setglobal(J, "quit");
+
+ js_dostring(J, require_js, 0);
+
+ if (argc > 1) {
+ for (i = 1; i < argc; ++i) {
+ if (js_dofile(J, argv[i]))
+ return 1;
+ js_gc(J, 0);
+ }
+ } else {
+ fputs(PS1, stdout);
+ while (fgets(line, sizeof line, stdin)) {
+ js_dostring(J, line, 1);
+ fputs(PS1, stdout);
+ }
+ putchar('\n');
+ js_gc(J, 1);
+ }
+
+ js_freestate(J);
+
+ return 0;
+}
diff --git a/mujs/mujs.h b/mujs/mujs.h
new file mode 100644
index 0000000..dd3da50
--- /dev/null
+++ b/mujs/mujs.h
@@ -0,0 +1,179 @@
+#ifndef mujs_h
+#define mujs_h
+
+/* noreturn is a GCC extension */
+#ifdef __GNUC__
+#define JS_NORETURN __attribute__((noreturn))
+#else
+#ifdef _MSC_VER
+#define JS_NORETURN __declspec(noreturn)
+#else
+#define JS_NORETURN
+#endif
+#endif
+
+/* GCC can do type checking of printf strings */
+#ifdef __printflike
+#define JS_PRINTFLIKE __printflike
+#else
+#if __GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7
+#define JS_PRINTFLIKE(fmtarg, firstvararg) \
+ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
+#else
+#define JS_PRINTFLIKE(fmtarg, firstvararg)
+#endif
+#endif
+
+typedef struct js_State js_State;
+
+typedef void *(*js_Alloc)(void *memctx, void *ptr, unsigned int size);
+typedef void (*js_Panic)(js_State *J);
+typedef void (*js_CFunction)(js_State *J);
+typedef void (*js_Finalize)(js_State *J, void *p);
+
+/* Basic functions */
+js_State *js_newstate(js_Alloc alloc, void *actx, int flags);
+void js_setcontext(js_State *J, void *uctx);
+void *js_getcontext(js_State *J);
+js_Panic js_atpanic(js_State *J, js_Panic panic);
+void js_freestate(js_State *J);
+void js_gc(js_State *J, int report);
+
+int js_dostring(js_State *J, const char *source, int report);
+int js_dofile(js_State *J, const char *filename);
+int js_ploadstring(js_State *J, const char *filename, const char *source);
+int js_ploadfile(js_State *J, const char *filename);
+int js_pcall(js_State *J, int n);
+int js_pconstruct(js_State *J, int n);
+
+/* State constructor flags */
+enum {
+ JS_STRICT = 1,
+};
+
+/* RegExp flags */
+enum {
+ JS_REGEXP_G = 1,
+ JS_REGEXP_I = 2,
+ JS_REGEXP_M = 4,
+};
+
+/* Property attribute flags */
+enum {
+ JS_READONLY = 1,
+ JS_DONTENUM = 2,
+ JS_DONTCONF = 4,
+};
+
+void js_newerror(js_State *J, const char *message);
+void js_newevalerror(js_State *J, const char *message);
+void js_newrangeerror(js_State *J, const char *message);
+void js_newreferenceerror(js_State *J, const char *message);
+void js_newsyntaxerror(js_State *J, const char *message);
+void js_newtypeerror(js_State *J, const char *message);
+void js_newurierror(js_State *J, const char *message);
+
+JS_NORETURN void js_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+JS_NORETURN void js_evalerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+JS_NORETURN void js_rangeerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+JS_NORETURN void js_referenceerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+JS_NORETURN void js_syntaxerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+JS_NORETURN void js_typeerror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+JS_NORETURN void js_urierror(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+JS_NORETURN void js_throw(js_State *J);
+
+void js_loadstring(js_State *J, const char *filename, const char *source);
+void js_loadfile(js_State *J, const char *filename);
+
+void js_eval(js_State *J);
+void js_call(js_State *J, int n);
+void js_construct(js_State *J, int n);
+
+const char *js_ref(js_State *J);
+void js_unref(js_State *J, const char *ref);
+
+void js_getregistry(js_State *J, const char *name);
+void js_setregistry(js_State *J, const char *name);
+void js_delregistry(js_State *J, const char *name);
+
+void js_getglobal(js_State *J, const char *name);
+void js_setglobal(js_State *J, const char *name);
+void js_defglobal(js_State *J, const char *name, int atts);
+
+int js_hasproperty(js_State *J, int idx, const char *name);
+void js_getproperty(js_State *J, int idx, const char *name);
+void js_setproperty(js_State *J, int idx, const char *name);
+void js_defproperty(js_State *J, int idx, const char *name, int atts);
+void js_delproperty(js_State *J, int idx, const char *name);
+void js_defaccessor(js_State *J, int idx, const char *name, int atts);
+
+unsigned int js_getlength(js_State *J, int idx);
+void js_setlength(js_State *J, int idx, unsigned int len);
+int js_hasindex(js_State *J, int idx, unsigned int i);
+void js_getindex(js_State *J, int idx, unsigned int i);
+void js_setindex(js_State *J, int idx, unsigned int i);
+void js_delindex(js_State *J, int idx, unsigned int i);
+
+void js_currentfunction(js_State *J);
+void js_pushglobal(js_State *J);
+void js_pushundefined(js_State *J);
+void js_pushnull(js_State *J);
+void js_pushboolean(js_State *J, int v);
+void js_pushnumber(js_State *J, double v);
+void js_pushstring(js_State *J, const char *v);
+void js_pushlstring(js_State *J, const char *v, unsigned int n);
+void js_pushliteral(js_State *J, const char *v);
+
+void js_newobject(js_State *J);
+void js_newarray(js_State *J);
+void js_newboolean(js_State *J, int v);
+void js_newnumber(js_State *J, double v);
+void js_newstring(js_State *J, const char *v);
+void js_newcfunction(js_State *J, js_CFunction fun, const char *name, unsigned int length);
+void js_newcconstructor(js_State *J, js_CFunction fun, js_CFunction con, const char *name, unsigned int length);
+void js_newuserdata(js_State *J, const char *tag, void *data, js_Finalize finalize);
+void js_newregexp(js_State *J, const char *pattern, int flags);
+
+void js_pushiterator(js_State *J, int idx, int own);
+const char *js_nextiterator(js_State *J, int idx);
+
+int js_isdefined(js_State *J, int idx);
+int js_isundefined(js_State *J, int idx);
+int js_isnull(js_State *J, int idx);
+int js_isboolean(js_State *J, int idx);
+int js_isnumber(js_State *J, int idx);
+int js_isstring(js_State *J, int idx);
+int js_isprimitive(js_State *J, int idx);
+int js_isobject(js_State *J, int idx);
+int js_isarray(js_State *J, int idx);
+int js_isregexp(js_State *J, int idx);
+int js_iscallable(js_State *J, int idx);
+int js_isuserdata(js_State *J, int idx, const char *tag);
+
+int js_toboolean(js_State *J, int idx);
+double js_tonumber(js_State *J, int idx);
+const char *js_tostring(js_State *J, int idx);
+void *js_touserdata(js_State *J, int idx, const char *tag);
+
+double js_tointeger(js_State *J, int idx);
+int js_toint32(js_State *J, int idx);
+unsigned int js_touint32(js_State *J, int idx);
+short js_toint16(js_State *J, int idx);
+unsigned short js_touint16(js_State *J, int idx);
+
+int js_gettop(js_State *J);
+void js_settop(js_State *J, int idx);
+void js_pop(js_State *J, int n);
+void js_rot(js_State *J, int n);
+void js_copy(js_State *J, int idx);
+void js_remove(js_State *J, int idx);
+void js_insert(js_State *J, int idx);
+void js_replace(js_State* J, int idx);
+
+void js_concat(js_State *J);
+int js_compare(js_State *J, int *okay);
+int js_equal(js_State *J);
+int js_strictequal(js_State *J);
+int js_instanceof(js_State *J);
+
+#endif
diff --git a/mujs/one.c b/mujs/one.c
new file mode 100644
index 0000000..2193194
--- /dev/null
+++ b/mujs/one.c
@@ -0,0 +1,26 @@
+#include "jsarray.c"
+#include "jsboolean.c"
+#include "jsbuiltin.c"
+#include "jscompile.c"
+#include "jsdate.c"
+#include "jsdtoa.c"
+#include "jsdump.c"
+#include "jserror.c"
+#include "jsfunction.c"
+#include "jsgc.c"
+#include "jsintern.c"
+#include "jslex.c"
+#include "jsmath.c"
+#include "jsnumber.c"
+#include "jsobject.c"
+#include "json.c"
+#include "jsparse.c"
+#include "jsproperty.c"
+#include "jsregexp.c"
+#include "jsrun.c"
+#include "jsstate.c"
+#include "jsstring.c"
+#include "jsvalue.c"
+#include "regex.c"
+#include "utf.c"
+#include "utftype.c"
diff --git a/mujs/opnames.h b/mujs/opnames.h
new file mode 100644
index 0000000..da0f214
--- /dev/null
+++ b/mujs/opnames.h
@@ -0,0 +1,91 @@
+"pop",
+"dup",
+"dup2",
+"rot2",
+"rot3",
+"rot4",
+"number_0",
+"number_1",
+"number_pos",
+"number_neg",
+"number",
+"string",
+"closure",
+"newarray",
+"newobject",
+"newregexp",
+"undef",
+"null",
+"true",
+"false",
+"this",
+"global",
+"current",
+"initlocal",
+"getlocal",
+"setlocal",
+"dellocal",
+"initvar",
+"defvar",
+"hasvar",
+"getvar",
+"setvar",
+"delvar",
+"in",
+"initprop",
+"initgetter",
+"initsetter",
+"getprop",
+"getprop_s",
+"setprop",
+"setprop_s",
+"delprop",
+"delprop_s",
+"iterator",
+"nextiter",
+"eval",
+"call",
+"new",
+"typeof",
+"pos",
+"neg",
+"bitnot",
+"lognot",
+"inc",
+"dec",
+"postinc",
+"postdec",
+"mul",
+"div",
+"mod",
+"add",
+"sub",
+"shl",
+"shr",
+"ushr",
+"lt",
+"gt",
+"le",
+"ge",
+"eq",
+"ne",
+"stricteq",
+"strictne",
+"jcase",
+"bitand",
+"bitxor",
+"bitor",
+"instanceof",
+"throw",
+"try",
+"endtry",
+"catch",
+"endcatch",
+"with",
+"endwith",
+"debugger",
+"jump",
+"jtrue",
+"jfalse",
+"return",
+"line",
diff --git a/mujs/regex.c b/mujs/regex.c
new file mode 100644
index 0000000..b35c248
--- /dev/null
+++ b/mujs/regex.c
@@ -0,0 +1,1143 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <limits.h>
+
+#include "regex.h"
+#include "utf.h"
+
+#define emit regemit
+#define next regnext
+#define accept regaccept
+
+#define nelem(a) (sizeof (a) / sizeof (a)[0])
+
+#define REPINF 255
+#define MAXTHREAD 1000
+#define MAXSUB REG_MAXSUB
+
+typedef struct Reclass Reclass;
+typedef struct Renode Renode;
+typedef struct Reinst Reinst;
+typedef struct Rethread Rethread;
+
+struct Reclass {
+ Rune *end;
+ Rune spans[64];
+};
+
+struct Reprog {
+ Reinst *start, *end;
+ int flags;
+ unsigned int nsub;
+ Reclass cclass[16];
+};
+
+struct cstate {
+ Reprog *prog;
+ Renode *pstart, *pend;
+
+ const char *source;
+ unsigned int ncclass;
+ unsigned int nsub;
+ Renode *sub[MAXSUB];
+
+ int lookahead;
+ Rune yychar;
+ Reclass *yycc;
+ int yymin, yymax;
+
+ const char *error;
+ jmp_buf kaboom;
+};
+
+static void die(struct cstate *g, const char *message)
+{
+ g->error = message;
+ longjmp(g->kaboom, 1);
+}
+
+static int canon(Rune c)
+{
+ Rune u = toupperrune(c);
+ if (c >= 128 && u < 128)
+ return c;
+ return u;
+}
+
+/* Scan */
+
+enum {
+ L_CHAR = 256,
+ L_CCLASS, /* character class */
+ L_NCCLASS, /* negative character class */
+ L_NC, /* "(?:" no capture */
+ L_PLA, /* "(?=" positive lookahead */
+ L_NLA, /* "(?!" negative lookahead */
+ L_WORD, /* "\b" word boundary */
+ L_NWORD, /* "\B" non-word boundary */
+ L_REF, /* "\1" back-reference */
+ L_COUNT, /* {M,N} */
+};
+
+static int hex(struct cstate *g, int c)
+{
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return c - 'a' + 0xA;
+ if (c >= 'A' && c <= 'F') return c - 'A' + 0xA;
+ die(g, "invalid escape sequence");
+ return 0;
+}
+
+static int dec(struct cstate *g, int c)
+{
+ if (c >= '0' && c <= '9') return c - '0';
+ die(g, "invalid quantifier");
+ return 0;
+}
+
+#define ESCAPES "BbDdSsWw^$\\.*+?()[]{}|0123456789"
+
+static int isunicodeletter(int c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || isalpharune(c);
+}
+
+static int nextrune(struct cstate *g)
+{
+ g->source += chartorune(&g->yychar, g->source);
+ if (g->yychar == '\\') {
+ g->source += chartorune(&g->yychar, g->source);
+ switch (g->yychar) {
+ case 0: die(g, "unterminated escape sequence");
+ case 'f': g->yychar = '\f'; return 0;
+ case 'n': g->yychar = '\n'; return 0;
+ case 'r': g->yychar = '\r'; return 0;
+ case 't': g->yychar = '\t'; return 0;
+ case 'v': g->yychar = '\v'; return 0;
+ case 'c':
+ g->yychar = (*g->source++) & 31;
+ return 0;
+ case 'x':
+ g->yychar = hex(g, *g->source++) << 4;
+ g->yychar += hex(g, *g->source++);
+ if (g->yychar == 0) {
+ g->yychar = '0';
+ return 1;
+ }
+ return 0;
+ case 'u':
+ g->yychar = hex(g, *g->source++) << 12;
+ g->yychar += hex(g, *g->source++) << 8;
+ g->yychar += hex(g, *g->source++) << 4;
+ g->yychar += hex(g, *g->source++);
+ if (g->yychar == 0) {
+ g->yychar = '0';
+ return 1;
+ }
+ return 0;
+ }
+ if (strchr(ESCAPES, g->yychar))
+ return 1;
+ if (isunicodeletter(g->yychar) || g->yychar == '_') /* check identity escape */
+ die(g, "invalid escape character");
+ return 0;
+ }
+ return 0;
+}
+
+static int lexcount(struct cstate *g)
+{
+ g->yychar = *g->source++;
+
+ g->yymin = dec(g, g->yychar);
+ g->yychar = *g->source++;
+ while (g->yychar != ',' && g->yychar != '}') {
+ g->yymin = g->yymin * 10 + dec(g, g->yychar);
+ g->yychar = *g->source++;
+ }
+ if (g->yymin >= REPINF)
+ die(g, "numeric overflow");
+
+ if (g->yychar == ',') {
+ g->yychar = *g->source++;
+ if (g->yychar == '}') {
+ g->yymax = REPINF;
+ } else {
+ g->yymax = dec(g, g->yychar);
+ g->yychar = *g->source++;
+ while (g->yychar != '}') {
+ g->yymax = g->yymax * 10 + dec(g, g->yychar);
+ g->yychar = *g->source++;
+ }
+ if (g->yymax >= REPINF)
+ die(g, "numeric overflow");
+ }
+ } else {
+ g->yymax = g->yymin;
+ }
+
+ return L_COUNT;
+}
+
+static void newcclass(struct cstate *g)
+{
+ if (g->ncclass >= nelem(g->prog->cclass))
+ die(g, "too many character classes");
+ g->yycc = g->prog->cclass + g->ncclass++;
+ g->yycc->end = g->yycc->spans;
+}
+
+static void addrange(struct cstate *g, Rune a, Rune b)
+{
+ if (a > b)
+ die(g, "invalid character class range");
+ if (g->yycc->end + 2 == g->yycc->spans + nelem(g->yycc->spans))
+ die(g, "too many character class ranges");
+ *g->yycc->end++ = a;
+ *g->yycc->end++ = b;
+}
+
+static void addranges_d(struct cstate *g)
+{
+ addrange(g, '0', '9');
+}
+
+static void addranges_D(struct cstate *g)
+{
+ addrange(g, 0, '0'-1);
+ addrange(g, '9'+1, 0xFFFF);
+}
+
+static void addranges_s(struct cstate *g)
+{
+ addrange(g, 0x9, 0x9);
+ addrange(g, 0xA, 0xD);
+ addrange(g, 0x20, 0x20);
+ addrange(g, 0xA0, 0xA0);
+ addrange(g, 0x2028, 0x2029);
+ addrange(g, 0xFEFF, 0xFEFF);
+}
+
+static void addranges_S(struct cstate *g)
+{
+ addrange(g, 0, 0x9-1);
+ addrange(g, 0x9+1, 0xA-1);
+ addrange(g, 0xD+1, 0x20-1);
+ addrange(g, 0x20+1, 0xA0-1);
+ addrange(g, 0xA0+1, 0x2028-1);
+ addrange(g, 0x2029+1, 0xFEFF-1);
+ addrange(g, 0xFEFF+1, 0xFFFF);
+}
+
+static void addranges_w(struct cstate *g)
+{
+ addrange(g, '0', '9');
+ addrange(g, 'A', 'Z');
+ addrange(g, '_', '_');
+ addrange(g, 'a', 'z');
+}
+
+static void addranges_W(struct cstate *g)
+{
+ addrange(g, 0, '0'-1);
+ addrange(g, '9'+1, 'A'-1);
+ addrange(g, 'Z'+1, '_'-1);
+ addrange(g, '_'+1, 'a'-1);
+ addrange(g, 'z'+1, 0xFFFF);
+}
+
+static int lexclass(struct cstate *g)
+{
+ int type = L_CCLASS;
+ int quoted, havesave, havedash;
+ Rune save;
+
+ newcclass(g);
+
+ quoted = nextrune(g);
+ if (!quoted && g->yychar == '^') {
+ type = L_NCCLASS;
+ quoted = nextrune(g);
+ }
+
+ havesave = havedash = 0;
+ for (;;) {
+ if (g->yychar == 0)
+ die(g, "unterminated character class");
+ if (!quoted && g->yychar == ']')
+ break;
+
+ if (!quoted && g->yychar == '-') {
+ if (havesave) {
+ if (havedash) {
+ addrange(g, save, '-');
+ havesave = havedash = 0;
+ } else {
+ havedash = 1;
+ }
+ } else {
+ save = '-';
+ havesave = 1;
+ }
+ } else if (quoted && strchr("DSWdsw", g->yychar)) {
+ if (havesave) {
+ addrange(g, save, save);
+ if (havedash)
+ addrange(g, '-', '-');
+ }
+ switch (g->yychar) {
+ case 'd': addranges_d(g); break;
+ case 's': addranges_s(g); break;
+ case 'w': addranges_w(g); break;
+ case 'D': addranges_D(g); break;
+ case 'S': addranges_S(g); break;
+ case 'W': addranges_W(g); break;
+ }
+ havesave = havedash = 0;
+ } else {
+ if (quoted) {
+ if (g->yychar == 'b')
+ g->yychar = '\b';
+ else if (g->yychar == '0')
+ g->yychar = 0;
+ /* else identity escape */
+ }
+ if (havesave) {
+ if (havedash) {
+ addrange(g, save, g->yychar);
+ havesave = havedash = 0;
+ } else {
+ addrange(g, save, save);
+ save = g->yychar;
+ }
+ } else {
+ save = g->yychar;
+ havesave = 1;
+ }
+ }
+
+ quoted = nextrune(g);
+ }
+
+ if (havesave) {
+ addrange(g, save, save);
+ if (havedash)
+ addrange(g, '-', '-');
+ }
+
+ return type;
+}
+
+static int lex(struct cstate *g)
+{
+ int quoted = nextrune(g);
+ if (quoted) {
+ switch (g->yychar) {
+ case 'b': return L_WORD;
+ case 'B': return L_NWORD;
+ case 'd': newcclass(g); addranges_d(g); return L_CCLASS;
+ case 's': newcclass(g); addranges_s(g); return L_CCLASS;
+ case 'w': newcclass(g); addranges_w(g); return L_CCLASS;
+ case 'D': newcclass(g); addranges_d(g); return L_NCCLASS;
+ case 'S': newcclass(g); addranges_s(g); return L_NCCLASS;
+ case 'W': newcclass(g); addranges_w(g); return L_NCCLASS;
+ case '0': g->yychar = 0; return L_CHAR;
+ }
+ if (g->yychar >= '0' && g->yychar <= '9') {
+ g->yychar -= '0';
+ if (*g->source >= '0' && *g->source <= '9')
+ g->yychar = g->yychar * 10 + *g->source++ - '0';
+ return L_REF;
+ }
+ return L_CHAR;
+ }
+
+ switch (g->yychar) {
+ case 0:
+ case '$': case ')': case '*': case '+':
+ case '.': case '?': case '^': case '|':
+ return g->yychar;
+ }
+
+ if (g->yychar == '{')
+ return lexcount(g);
+ if (g->yychar == '[')
+ return lexclass(g);
+ if (g->yychar == '(') {
+ if (g->source[0] == '?') {
+ if (g->source[1] == ':') {
+ g->source += 2;
+ return L_NC;
+ }
+ if (g->source[1] == '=') {
+ g->source += 2;
+ return L_PLA;
+ }
+ if (g->source[1] == '!') {
+ g->source += 2;
+ return L_NLA;
+ }
+ }
+ return '(';
+ }
+
+ return L_CHAR;
+}
+
+/* Parse */
+
+enum {
+ P_CAT, P_ALT, P_REP,
+ P_BOL, P_EOL, P_WORD, P_NWORD,
+ P_PAR, P_PLA, P_NLA,
+ P_ANY, P_CHAR, P_CCLASS, P_NCCLASS,
+ P_REF,
+};
+
+struct Renode {
+ unsigned char type;
+ unsigned char ng, m, n;
+ Rune c;
+ Reclass *cc;
+ Renode *x;
+ Renode *y;
+};
+
+static Renode *newnode(struct cstate *g, int type)
+{
+ Renode *node = g->pend++;
+ node->type = type;
+ node->cc = NULL;
+ node->c = 0;
+ node->ng = 0;
+ node->m = 0;
+ node->n = 0;
+ node->x = node->y = NULL;
+ return node;
+}
+
+static int empty(Renode *node)
+{
+ if (!node) return 1;
+ switch (node->type) {
+ default: return 1;
+ case P_CAT: return empty(node->x) && empty(node->y);
+ case P_ALT: return empty(node->x) || empty(node->y);
+ case P_REP: return empty(node->x) || node->m == 0;
+ case P_PAR: return empty(node->x);
+ case P_REF: return empty(node->x);
+ case P_ANY: case P_CHAR: case P_CCLASS: case P_NCCLASS: return 0;
+ }
+}
+
+static Renode *newrep(struct cstate *g, Renode *atom, int ng, int min, int max)
+{
+ Renode *rep = newnode(g, P_REP);
+ if (max == REPINF && empty(atom))
+ die(g, "infinite loop matching the empty string");
+ rep->ng = ng;
+ rep->m = min;
+ rep->n = max;
+ rep->x = atom;
+ return rep;
+}
+
+static void next(struct cstate *g)
+{
+ g->lookahead = lex(g);
+}
+
+static int accept(struct cstate *g, int t)
+{
+ if (g->lookahead == t) {
+ next(g);
+ return 1;
+ }
+ return 0;
+}
+
+static Renode *parsealt(struct cstate *g);
+
+static Renode *parseatom(struct cstate *g)
+{
+ Renode *atom;
+ if (g->lookahead == L_CHAR) {
+ atom = newnode(g, P_CHAR);
+ atom->c = g->yychar;
+ next(g);
+ return atom;
+ }
+ if (g->lookahead == L_CCLASS) {
+ atom = newnode(g, P_CCLASS);
+ atom->cc = g->yycc;
+ next(g);
+ return atom;
+ }
+ if (g->lookahead == L_NCCLASS) {
+ atom = newnode(g, P_NCCLASS);
+ atom->cc = g->yycc;
+ next(g);
+ return atom;
+ }
+ if (g->lookahead == L_REF) {
+ atom = newnode(g, P_REF);
+ if (g->yychar == 0 || g->yychar > g->nsub || !g->sub[g->yychar])
+ die(g, "invalid back-reference");
+ atom->n = g->yychar;
+ atom->x = g->sub[g->yychar];
+ next(g);
+ return atom;
+ }
+ if (accept(g, '.'))
+ return newnode(g, P_ANY);
+ if (accept(g, '(')) {
+ atom = newnode(g, P_PAR);
+ if (g->nsub == MAXSUB)
+ die(g, "too many captures");
+ atom->n = g->nsub++;
+ atom->x = parsealt(g);
+ g->sub[atom->n] = atom;
+ if (!accept(g, ')'))
+ die(g, "unmatched '('");
+ return atom;
+ }
+ if (accept(g, L_NC)) {
+ atom = parsealt(g);
+ if (!accept(g, ')'))
+ die(g, "unmatched '('");
+ return atom;
+ }
+ if (accept(g, L_PLA)) {
+ atom = newnode(g, P_PLA);
+ atom->x = parsealt(g);
+ if (!accept(g, ')'))
+ die(g, "unmatched '('");
+ return atom;
+ }
+ if (accept(g, L_NLA)) {
+ atom = newnode(g, P_NLA);
+ atom->x = parsealt(g);
+ if (!accept(g, ')'))
+ die(g, "unmatched '('");
+ return atom;
+ }
+ die(g, "syntax error");
+ return NULL;
+}
+
+static Renode *parserep(struct cstate *g)
+{
+ Renode *atom;
+
+ if (accept(g, '^')) return newnode(g, P_BOL);
+ if (accept(g, '$')) return newnode(g, P_EOL);
+ if (accept(g, L_WORD)) return newnode(g, P_WORD);
+ if (accept(g, L_NWORD)) return newnode(g, P_NWORD);
+
+ atom = parseatom(g);
+ if (g->lookahead == L_COUNT) {
+ int min = g->yymin, max = g->yymax;
+ next(g);
+ if (max < min)
+ die(g, "invalid quantifier");
+ return newrep(g, atom, accept(g, '?'), min, max);
+ }
+ if (accept(g, '*')) return newrep(g, atom, accept(g, '?'), 0, REPINF);
+ if (accept(g, '+')) return newrep(g, atom, accept(g, '?'), 1, REPINF);
+ if (accept(g, '?')) return newrep(g, atom, accept(g, '?'), 0, 1);
+ return atom;
+}
+
+static Renode *parsecat(struct cstate *g)
+{
+ Renode *cat, *x;
+ if (g->lookahead && g->lookahead != '|' && g->lookahead != ')') {
+ cat = parserep(g);
+ while (g->lookahead && g->lookahead != '|' && g->lookahead != ')') {
+ x = cat;
+ cat = newnode(g, P_CAT);
+ cat->x = x;
+ cat->y = parserep(g);
+ }
+ return cat;
+ }
+ return NULL;
+}
+
+static Renode *parsealt(struct cstate *g)
+{
+ Renode *alt, *x;
+ alt = parsecat(g);
+ while (accept(g, '|')) {
+ x = alt;
+ alt = newnode(g, P_ALT);
+ alt->x = x;
+ alt->y = parsecat(g);
+ }
+ return alt;
+}
+
+/* Compile */
+
+enum {
+ I_END, I_JUMP, I_SPLIT, I_PLA, I_NLA,
+ I_ANYNL, I_ANY, I_CHAR, I_CCLASS, I_NCCLASS, I_REF,
+ I_BOL, I_EOL, I_WORD, I_NWORD,
+ I_LPAR, I_RPAR
+};
+
+struct Reinst {
+ unsigned char opcode;
+ unsigned char n;
+ Rune c;
+ Reclass *cc;
+ Reinst *x;
+ Reinst *y;
+};
+
+static unsigned int count(Renode *node)
+{
+ unsigned int min, max;
+ if (!node) return 0;
+ switch (node->type) {
+ default: return 1;
+ case P_CAT: return count(node->x) + count(node->y);
+ case P_ALT: return count(node->x) + count(node->y) + 2;
+ case P_REP:
+ min = node->m;
+ max = node->n;
+ if (min == max) return count(node->x) * min;
+ if (max < REPINF) return count(node->x) * max + (max - min);
+ return count(node->x) * (min + 1) + 2;
+ case P_PAR: return count(node->x) + 2;
+ case P_PLA: return count(node->x) + 2;
+ case P_NLA: return count(node->x) + 2;
+ }
+}
+
+static Reinst *emit(Reprog *prog, int opcode)
+{
+ Reinst *inst = prog->end++;
+ inst->opcode = opcode;
+ inst->n = 0;
+ inst->c = 0;
+ inst->cc = NULL;
+ inst->x = inst->y = NULL;
+ return inst;
+}
+
+static void compile(Reprog *prog, Renode *node)
+{
+ Reinst *inst, *split, *jump;
+ unsigned int i;
+
+ if (!node)
+ return;
+
+ switch (node->type) {
+ case P_CAT:
+ compile(prog, node->x);
+ compile(prog, node->y);
+ break;
+
+ case P_ALT:
+ split = emit(prog, I_SPLIT);
+ compile(prog, node->x);
+ jump = emit(prog, I_JUMP);
+ compile(prog, node->y);
+ split->x = split + 1;
+ split->y = jump + 1;
+ jump->x = prog->end;
+ break;
+
+ case P_REP:
+ for (i = 0; i < node->m; ++i) {
+ inst = prog->end;
+ compile(prog, node->x);
+ }
+ if (node->m == node->n)
+ break;
+ if (node->n < REPINF) {
+ for (i = node->m; i < node->n; ++i) {
+ split = emit(prog, I_SPLIT);
+ compile(prog, node->x);
+ if (node->ng) {
+ split->y = split + 1;
+ split->x = prog->end;
+ } else {
+ split->x = split + 1;
+ split->y = prog->end;
+ }
+ }
+ } else if (node->m == 0) {
+ split = emit(prog, I_SPLIT);
+ compile(prog, node->x);
+ jump = emit(prog, I_JUMP);
+ if (node->ng) {
+ split->y = split + 1;
+ split->x = prog->end;
+ } else {
+ split->x = split + 1;
+ split->y = prog->end;
+ }
+ jump->x = split;
+ } else {
+ split = emit(prog, I_SPLIT);
+ if (node->ng) {
+ split->y = inst;
+ split->x = prog->end;
+ } else {
+ split->x = inst;
+ split->y = prog->end;
+ }
+ }
+ break;
+
+ case P_BOL: emit(prog, I_BOL); break;
+ case P_EOL: emit(prog, I_EOL); break;
+ case P_WORD: emit(prog, I_WORD); break;
+ case P_NWORD: emit(prog, I_NWORD); break;
+
+ case P_PAR:
+ inst = emit(prog, I_LPAR);
+ inst->n = node->n;
+ compile(prog, node->x);
+ inst = emit(prog, I_RPAR);
+ inst->n = node->n;
+ break;
+ case P_PLA:
+ split = emit(prog, I_PLA);
+ compile(prog, node->x);
+ emit(prog, I_END);
+ split->x = split + 1;
+ split->y = prog->end;
+ break;
+ case P_NLA:
+ split = emit(prog, I_NLA);
+ compile(prog, node->x);
+ emit(prog, I_END);
+ split->x = split + 1;
+ split->y = prog->end;
+ break;
+
+ case P_ANY:
+ emit(prog, I_ANY);
+ break;
+ case P_CHAR:
+ inst = emit(prog, I_CHAR);
+ inst->c = (prog->flags & REG_ICASE) ? canon(node->c) : node->c;
+ break;
+ case P_CCLASS:
+ inst = emit(prog, I_CCLASS);
+ inst->cc = node->cc;
+ break;
+ case P_NCCLASS:
+ inst = emit(prog, I_NCCLASS);
+ inst->cc = node->cc;
+ break;
+ case P_REF:
+ inst = emit(prog, I_REF);
+ inst->n = node->n;
+ break;
+ }
+}
+
+#ifdef TEST
+static void dumpnode(Renode *node)
+{
+ Rune *p;
+ if (!node) { printf("Empty"); return; }
+ switch (node->type) {
+ case P_CAT: printf("Cat("); dumpnode(node->x); printf(", "); dumpnode(node->y); printf(")"); break;
+ case P_ALT: printf("Alt("); dumpnode(node->x); printf(", "); dumpnode(node->y); printf(")"); break;
+ case P_REP:
+ printf(node->ng ? "NgRep(%d,%d," : "Rep(%d,%d,", node->m, node->n);
+ dumpnode(node->x);
+ printf(")");
+ break;
+ case P_BOL: printf("Bol"); break;
+ case P_EOL: printf("Eol"); break;
+ case P_WORD: printf("Word"); break;
+ case P_NWORD: printf("NotWord"); break;
+ case P_PAR: printf("Par(%d,", node->n); dumpnode(node->x); printf(")"); break;
+ case P_PLA: printf("PLA("); dumpnode(node->x); printf(")"); break;
+ case P_NLA: printf("NLA("); dumpnode(node->x); printf(")"); break;
+ case P_ANY: printf("Any"); break;
+ case P_CHAR: printf("Char(%c)", node->c); break;
+ case P_CCLASS:
+ printf("Class(");
+ for (p = node->cc->spans; p < node->cc->end; p += 2) printf("%02X-%02X,", p[0], p[1]);
+ printf(")");
+ break;
+ case P_NCCLASS:
+ printf("NotClass(");
+ for (p = node->cc->spans; p < node->cc->end; p += 2) printf("%02X-%02X,", p[0], p[1]);
+ printf(")");
+ break;
+ case P_REF: printf("Ref(%d)", node->n); break;
+ }
+}
+
+static void dumpprog(Reprog *prog)
+{
+ Reinst *inst;
+ int i;
+ for (i = 0, inst = prog->start; inst < prog->end; ++i, ++inst) {
+ printf("% 5d: ", i);
+ switch (inst->opcode) {
+ case I_END: puts("end"); break;
+ case I_JUMP: printf("jump %d\n", (int)(inst->x - prog->start)); break;
+ case I_SPLIT: printf("split %d %d\n", (int)(inst->x - prog->start), (int)(inst->y - prog->start)); break;
+ case I_PLA: printf("pla %d %d\n", (int)(inst->x - prog->start), (int)(inst->y - prog->start)); break;
+ case I_NLA: printf("nla %d %d\n", (int)(inst->x - prog->start), (int)(inst->y - prog->start)); break;
+ case I_ANY: puts("any"); break;
+ case I_ANYNL: puts("anynl"); break;
+ case I_CHAR: printf(inst->c >= 32 && inst->c < 127 ? "char '%c'\n" : "char U+%04X\n", inst->c); break;
+ case I_CCLASS: puts("cclass"); break;
+ case I_NCCLASS: puts("ncclass"); break;
+ case I_REF: printf("ref %d\n", inst->n); break;
+ case I_BOL: puts("bol"); break;
+ case I_EOL: puts("eol"); break;
+ case I_WORD: puts("word"); break;
+ case I_NWORD: puts("nword"); break;
+ case I_LPAR: printf("lpar %d\n", inst->n); break;
+ case I_RPAR: printf("rpar %d\n", inst->n); break;
+ }
+ }
+}
+#endif
+
+Reprog *regcomp(const char *pattern, int cflags, const char **errorp)
+{
+ struct cstate g;
+ Renode *node;
+ Reinst *split, *jump;
+ int i;
+
+ g.prog = malloc(sizeof (Reprog));
+ g.pstart = g.pend = malloc(sizeof (Renode) * strlen(pattern) * 2);
+
+ if (setjmp(g.kaboom)) {
+ if (errorp) *errorp = g.error;
+ free(g.pstart);
+ free(g.prog);
+ return NULL;
+ }
+
+ g.source = pattern;
+ g.ncclass = 0;
+ g.nsub = 1;
+ for (i = 0; i < MAXSUB; ++i)
+ g.sub[i] = 0;
+
+ g.prog->flags = cflags;
+
+ next(&g);
+ node = parsealt(&g);
+ if (g.lookahead == ')')
+ die(&g, "unmatched ')'");
+ if (g.lookahead != 0)
+ die(&g, "syntax error");
+
+ g.prog->nsub = g.nsub;
+ g.prog->start = g.prog->end = malloc((count(node) + 6) * sizeof (Reinst));
+
+ split = emit(g.prog, I_SPLIT);
+ split->x = split + 3;
+ split->y = split + 1;
+ emit(g.prog, I_ANYNL);
+ jump = emit(g.prog, I_JUMP);
+ jump->x = split;
+ emit(g.prog, I_LPAR);
+ compile(g.prog, node);
+ emit(g.prog, I_RPAR);
+ emit(g.prog, I_END);
+
+#ifdef TEST
+ dumpnode(node);
+ putchar('\n');
+ dumpprog(g.prog);
+#endif
+
+ free(g.pstart);
+
+ if (errorp) *errorp = NULL;
+ return g.prog;
+}
+
+void regfree(Reprog *prog)
+{
+ if (prog) {
+ free(prog->start);
+ free(prog);
+ }
+}
+
+/* Match */
+
+static int isnewline(int c)
+{
+ return c == 0xA || c == 0xD || c == 0x2028 || c == 0x2029;
+}
+
+static int iswordchar(int c)
+{
+ return c == '_' ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9');
+}
+
+static int incclass(Reclass *cc, Rune c)
+{
+ Rune *p;
+ for (p = cc->spans; p < cc->end; p += 2)
+ if (p[0] <= c && c <= p[1])
+ return 1;
+ return 0;
+}
+
+static int incclasscanon(Reclass *cc, Rune c)
+{
+ Rune *p, r;
+ for (p = cc->spans; p < cc->end; p += 2)
+ for (r = p[0]; r <= p[1]; ++r)
+ if (c == canon(r))
+ return 1;
+ return 0;
+}
+
+static int strncmpcanon(const char *a, const char *b, unsigned int n)
+{
+ Rune ra, rb;
+ int c;
+ while (n--) {
+ if (!*a) return -1;
+ if (!*b) return 1;
+ a += chartorune(&ra, a);
+ b += chartorune(&rb, b);
+ c = canon(ra) - canon(rb);
+ if (c)
+ return c;
+ }
+ return 0;
+}
+
+struct Rethread {
+ Reinst *pc;
+ const char *sp;
+ Resub sub;
+};
+
+static void spawn(Rethread *t, Reinst *pc, const char *sp, Resub *sub)
+{
+ t->pc = pc;
+ t->sp = sp;
+ memcpy(&t->sub, sub, sizeof t->sub);
+}
+
+static int match(Reinst *pc, const char *sp, const char *bol, int flags, Resub *out)
+{
+ Rethread ready[MAXTHREAD];
+ Resub scratch;
+ Resub sub;
+ Rune c;
+ unsigned int nready;
+ int i;
+
+ /* queue initial thread */
+ spawn(ready + 0, pc, sp, out);
+ nready = 1;
+
+ /* run threads in stack order */
+ while (nready > 0) {
+ --nready;
+ pc = ready[nready].pc;
+ sp = ready[nready].sp;
+ memcpy(&sub, &ready[nready].sub, sizeof sub);
+ for (;;) {
+ switch (pc->opcode) {
+ case I_END:
+ for (i = 0; i < MAXSUB; ++i) {
+ out->sub[i].sp = sub.sub[i].sp;
+ out->sub[i].ep = sub.sub[i].ep;
+ }
+ return 1;
+ case I_JUMP:
+ pc = pc->x;
+ continue;
+ case I_SPLIT:
+ if (nready >= MAXTHREAD) {
+ fprintf(stderr, "regexec: backtrack overflow!\n");
+ return 0;
+ }
+ spawn(&ready[nready++], pc->y, sp, &sub);
+ pc = pc->x;
+ continue;
+
+ case I_PLA:
+ if (!match(pc->x, sp, bol, flags, &sub))
+ goto dead;
+ pc = pc->y;
+ continue;
+ case I_NLA:
+ memcpy(&scratch, &sub, sizeof scratch);
+ if (match(pc->x, sp, bol, flags, &scratch))
+ goto dead;
+ pc = pc->y;
+ continue;
+
+ case I_ANYNL:
+ sp += chartorune(&c, sp);
+ if (c == 0)
+ goto dead;
+ break;
+ case I_ANY:
+ sp += chartorune(&c, sp);
+ if (c == 0)
+ goto dead;
+ if (isnewline(c))
+ goto dead;
+ break;
+ case I_CHAR:
+ sp += chartorune(&c, sp);
+ if (c == 0)
+ goto dead;
+ if (flags & REG_ICASE)
+ c = canon(c);
+ if (c != pc->c)
+ goto dead;
+ break;
+ case I_CCLASS:
+ sp += chartorune(&c, sp);
+ if (c == 0)
+ goto dead;
+ if (flags & REG_ICASE) {
+ if (!incclasscanon(pc->cc, canon(c)))
+ goto dead;
+ } else {
+ if (!incclass(pc->cc, c))
+ goto dead;
+ }
+ break;
+ case I_NCCLASS:
+ sp += chartorune(&c, sp);
+ if (c == 0)
+ goto dead;
+ if (flags & REG_ICASE) {
+ if (incclasscanon(pc->cc, canon(c)))
+ goto dead;
+ } else {
+ if (incclass(pc->cc, c))
+ goto dead;
+ }
+ break;
+ case I_REF:
+ i = sub.sub[pc->n].ep - sub.sub[pc->n].sp;
+ if (flags & REG_ICASE) {
+ if (strncmpcanon(sp, sub.sub[pc->n].sp, i))
+ goto dead;
+ } else {
+ if (strncmp(sp, sub.sub[pc->n].sp, i))
+ goto dead;
+ }
+ if (i > 0)
+ sp += i;
+ break;
+
+ case I_BOL:
+ if (sp == bol && !(flags & REG_NOTBOL))
+ break;
+ if (flags & REG_NEWLINE)
+ if (sp > bol && isnewline(sp[-1]))
+ break;
+ goto dead;
+ case I_EOL:
+ if (*sp == 0)
+ break;
+ if (flags & REG_NEWLINE)
+ if (isnewline(*sp))
+ break;
+ goto dead;
+ case I_WORD:
+ i = sp > bol && iswordchar(sp[-1]);
+ i ^= iswordchar(sp[0]);
+ if (i)
+ break;
+ goto dead;
+ case I_NWORD:
+ i = sp > bol && iswordchar(sp[-1]);
+ i ^= iswordchar(sp[0]);
+ if (!i)
+ break;
+ goto dead;
+
+ case I_LPAR:
+ sub.sub[pc->n].sp = sp;
+ break;
+ case I_RPAR:
+ sub.sub[pc->n].ep = sp;
+ break;
+ default:
+ goto dead;
+ }
+ pc = pc + 1;
+ }
+dead: ;
+ }
+ return 0;
+}
+
+int regexec(Reprog *prog, const char *sp, Resub *sub, int eflags)
+{
+ Resub scratch;
+ int i;
+
+ if (!sub)
+ sub = &scratch;
+
+ sub->nsub = prog->nsub;
+ for (i = 0; i < MAXSUB; ++i)
+ sub->sub[i].sp = sub->sub[i].ep = NULL;
+
+ return !match(prog->start, sp, sp, prog->flags | eflags, sub);
+}
+
+#ifdef TEST
+int main(int argc, char **argv)
+{
+ const char *error;
+ const char *s;
+ Reprog *p;
+ Resub m;
+ unsigned int i;
+
+ if (argc > 1) {
+ p = regcomp(argv[1], 0, &error);
+ if (!p) {
+ fprintf(stderr, "regcomp: %s\n", error);
+ return 1;
+ }
+
+ if (argc > 2) {
+ s = argv[2];
+ printf("nsub = %d\n", p->nsub);
+ if (!regexec(p, s, &m, 0)) {
+ for (i = 0; i < m.nsub; ++i) {
+ int n = m.sub[i].ep - m.sub[i].sp;
+ if (n > 0)
+ printf("match %d: s=%d e=%d n=%d '%.*s'\n", i, (int)(m.sub[i].sp - s), (int)(m.sub[i].ep - s), n, n, m.sub[i].sp);
+ else
+ printf("match %d: n=0 ''\n", i);
+ }
+ } else {
+ printf("no match\n");
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
diff --git a/mujs/regex.h b/mujs/regex.h
new file mode 100644
index 0000000..3164a46
--- /dev/null
+++ b/mujs/regex.h
@@ -0,0 +1,35 @@
+#ifndef regex_h
+#define regex_h
+
+#define regcomp js_regcomp
+#define regexec js_regexec
+#define regfree js_regfree
+
+typedef struct Reprog Reprog;
+typedef struct Resub Resub;
+
+Reprog *regcomp(const char *pattern, int cflags, const char **errorp);
+int regexec(Reprog *prog, const char *string, Resub *sub, int eflags);
+void regfree(Reprog *prog);
+
+enum {
+ /* regcomp flags */
+ REG_ICASE = 1,
+ REG_NEWLINE = 2,
+
+ /* regexec flags */
+ REG_NOTBOL = 4,
+
+ /* limits */
+ REG_MAXSUB = 16
+};
+
+struct Resub {
+ unsigned int nsub;
+ struct {
+ const char *sp;
+ const char *ep;
+ } sub[REG_MAXSUB];
+};
+
+#endif
diff --git a/mujs/utf.c b/mujs/utf.c
new file mode 100644
index 0000000..f959b68
--- /dev/null
+++ b/mujs/utf.c
@@ -0,0 +1,164 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include <stdlib.h>
+#include <string.h>
+
+#include "utf.h"
+
+typedef unsigned char uchar;
+
+enum
+{
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+
+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
+
+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */
+
+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror,
+};
+
+unsigned int
+chartorune(Rune *rune, const char *str)
+{
+ int c, c1, c2;
+ int l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(uchar*)str;
+ if(c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(uchar*)(str+1) ^ Tx;
+ if(c1 & Testx)
+ goto bad;
+ if(c < T3) {
+ if(c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if(l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(uchar*)(str+2) ^ Tx;
+ if(c2 & Testx)
+ goto bad;
+ if(c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if(l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+unsigned int
+runetochar(char *str, const Rune *rune)
+{
+ unsigned int c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if(c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ if(c <= Rune2) {
+ str[0] = T2 | (c >> 1*Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ str[0] = T3 | (c >> 2*Bitx);
+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+}
+
+unsigned int
+runelen(int c)
+{
+ Rune rune;
+ char str[10];
+
+ rune = c;
+ return runetochar(str, &rune);
+}
+
+unsigned int
+utflen(const char *s)
+{
+ unsigned int c;
+ unsigned int n;
+ Rune rune;
+
+ n = 0;
+ for(;;) {
+ c = *(uchar*)s;
+ if(c < Runeself) {
+ if(c == 0)
+ return n;
+ s++;
+ } else
+ s += chartorune(&rune, s);
+ n++;
+ }
+}
diff --git a/mujs/utf.h b/mujs/utf.h
new file mode 100644
index 0000000..4a10c01
--- /dev/null
+++ b/mujs/utf.h
@@ -0,0 +1,42 @@
+#ifndef js_utf_h
+#define js_utf_h
+
+typedef unsigned short Rune; /* 16 bits */
+
+#define chartorune jsU_chartorune
+#define runetochar jsU_runetochar
+#define runelen jsU_runelen
+#define utflen jsU_utflen
+
+#define isalpharune jsU_isalpharune
+#define islowerrune jsU_islowerrune
+#define isspacerune jsU_isspacerune
+#define istitlerune jsU_istitlerune
+#define isupperrune jsU_isupperrune
+#define tolowerrune jsU_tolowerrune
+#define totitlerune jsU_totitlerune
+#define toupperrune jsU_toupperrune
+
+enum
+{
+ UTFmax = 3, /* maximum bytes per rune */
+ Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
+ Runeself = 0x80, /* rune and UTF sequences are the same (<) */
+ Runeerror = 0xFFFD, /* decoding error in UTF */
+};
+
+unsigned int chartorune(Rune *rune, const char *str);
+unsigned int runetochar(char *str, const Rune *rune);
+unsigned int runelen(int c);
+unsigned int utflen(const char *s);
+
+int isalpharune(Rune c);
+int islowerrune(Rune c);
+int isspacerune(Rune c);
+int istitlerune(Rune c);
+int isupperrune(Rune c);
+Rune tolowerrune(Rune c);
+Rune totitlerune(Rune c);
+Rune toupperrune(Rune c);
+
+#endif
diff --git a/mujs/utftype.c b/mujs/utftype.c
new file mode 100644
index 0000000..3def578
--- /dev/null
+++ b/mujs/utftype.c
@@ -0,0 +1,1138 @@
+#include "utf.h"
+
+#define bsearch jsU_bsearch
+#define nelem(a) (sizeof (a) / sizeof (a)[0])
+
+/*
+ * alpha ranges -
+ * only covers ranges not in lower||upper
+ */
+static
+Rune __alpha2[] =
+{
+ 0x00d8, 0x00f6, /* 脴 - 枚 */
+ 0x00f8, 0x01f5, /* 酶 - 堑 */
+ 0x0250, 0x02a8, /* 蓯 - 狮 */
+ 0x038e, 0x03a1, /* 螏 - 巍 */
+ 0x03a3, 0x03ce, /* 危 - 蠋 */
+ 0x03d0, 0x03d6, /* 蠍 - 蠔 */
+ 0x03e2, 0x03f3, /* 息 - 铣 */
+ 0x0490, 0x04c4, /* 覑 - 觿 */
+ 0x0561, 0x0587, /* 铡 - 謬 */
+ 0x05d0, 0x05ea, /* 讗 - 转 */
+ 0x05f0, 0x05f2, /* 装 - 撞 */
+ 0x0621, 0x063a, /* 亍 - 睾 */
+ 0x0640, 0x064a, /* 賭 - 賷 */
+ 0x0671, 0x06b7, /* 俦 - 诜 */
+ 0x06ba, 0x06be, /* 诤 - 诰 */
+ 0x06c0, 0x06ce, /* 踿 - 蹘 */
+ 0x06d0, 0x06d3, /* 蹛 - 蹞 */
+ 0x0905, 0x0939, /* 啶 - 啶 */
+ 0x0958, 0x0961, /* 啷 - 啷 */
+ 0x0985, 0x098c, /* 唳 - 唳 */
+ 0x098f, 0x0990, /* 唳 - 唳 */
+ 0x0993, 0x09a8, /* 唳 - 唳 */
+ 0x09aa, 0x09b0, /* 唳 - 唳 */
+ 0x09b6, 0x09b9, /* 唳 - 唳 */
+ 0x09dc, 0x09dd, /* 唰 - 唰 */
+ 0x09df, 0x09e1, /* 唰 - 唰 */
+ 0x09f0, 0x09f1, /* 唰 - 唰 */
+ 0x0a05, 0x0a0a, /* 啜 - 啜 */
+ 0x0a0f, 0x0a10, /* 啜 - 啜 */
+ 0x0a13, 0x0a28, /* 啜 - 啜 */
+ 0x0a2a, 0x0a30, /* 啜 - 啜 */
+ 0x0a32, 0x0a33, /* 啜 - 啜 */
+ 0x0a35, 0x0a36, /* 啜 - 啜 */
+ 0x0a38, 0x0a39, /* 啜 - 啜 */
+ 0x0a59, 0x0a5c, /* 喋 - 喋 */
+ 0x0a85, 0x0a8b, /* 嗒 - 嗒 */
+ 0x0a8f, 0x0a91, /* 嗒 - 嗒 */
+ 0x0a93, 0x0aa8, /* 嗒 - 嗒 */
+ 0x0aaa, 0x0ab0, /* 嗒 - 嗒 */
+ 0x0ab2, 0x0ab3, /* 嗒 - 嗒 */
+ 0x0ab5, 0x0ab9, /* 嗒 - 嗒 */
+ 0x0b05, 0x0b0c, /* 喱 - 喱 */
+ 0x0b0f, 0x0b10, /* 喱 - 喱 */
+ 0x0b13, 0x0b28, /* 喱 - 喱 */
+ 0x0b2a, 0x0b30, /* 喱 - 喱 */
+ 0x0b32, 0x0b33, /* 喱 - 喱 */
+ 0x0b36, 0x0b39, /* 喱 - 喱 */
+ 0x0b5c, 0x0b5d, /* 喹 - 喹 */
+ 0x0b5f, 0x0b61, /* 喹 - 喹 */
+ 0x0b85, 0x0b8a, /* 喈 - 喈 */
+ 0x0b8e, 0x0b90, /* 喈 - 喈 */
+ 0x0b92, 0x0b95, /* 喈 - 喈 */
+ 0x0b99, 0x0b9a, /* 喈 - 喈 */
+ 0x0b9e, 0x0b9f, /* 喈 - 喈 */
+ 0x0ba3, 0x0ba4, /* 喈 - 喈 */
+ 0x0ba8, 0x0baa, /* 喈 - 喈 */
+ 0x0bae, 0x0bb5, /* 喈 - 喈 */
+ 0x0bb7, 0x0bb9, /* 喈 - 喈 */
+ 0x0c05, 0x0c0c, /* 喟 - 喟 */
+ 0x0c0e, 0x0c10, /* 喟 - 喟 */
+ 0x0c12, 0x0c28, /* 喟 - 喟 */
+ 0x0c2a, 0x0c33, /* 喟 - 喟 */
+ 0x0c35, 0x0c39, /* 喟 - 喟 */
+ 0x0c60, 0x0c61, /* 啾 - 啾 */
+ 0x0c85, 0x0c8c, /* 嗖 - 嗖 */
+ 0x0c8e, 0x0c90, /* 嗖 - 嗖 */
+ 0x0c92, 0x0ca8, /* 嗖 - 嗖 */
+ 0x0caa, 0x0cb3, /* 嗖 - 嗖 */
+ 0x0cb5, 0x0cb9, /* 嗖 - 嗖 */
+ 0x0ce0, 0x0ce1, /* 喑 - 喑 */
+ 0x0d05, 0x0d0c, /* 啻 - 啻 */
+ 0x0d0e, 0x0d10, /* 啻 - 啻 */
+ 0x0d12, 0x0d28, /* 啻 - 啻 */
+ 0x0d2a, 0x0d39, /* 啻 - 啻 */
+ 0x0d60, 0x0d61, /* 嗟 - 嗟 */
+ 0x0e01, 0x0e30, /* 喔 - 喔 */
+ 0x0e32, 0x0e33, /* 喔 - 喔 */
+ 0x0e40, 0x0e46, /* 喙 - 喙 */
+ 0x0e5a, 0x0e5b, /* 喙 - 喙 */
+ 0x0e81, 0x0e82, /* 嗪 - 嗪 */
+ 0x0e87, 0x0e88, /* 嗪 - 嗪 */
+ 0x0e94, 0x0e97, /* 嗪 - 嗪 */
+ 0x0e99, 0x0e9f, /* 嗪 - 嗪 */
+ 0x0ea1, 0x0ea3, /* 嗪 - 嗪 */
+ 0x0eaa, 0x0eab, /* 嗪 - 嗪 */
+ 0x0ead, 0x0eae, /* 嗪 - 嗪 */
+ 0x0eb2, 0x0eb3, /* 嗪 - 嗪 */
+ 0x0ec0, 0x0ec4, /* 嗷 - 嗷 */
+ 0x0edc, 0x0edd, /* 嗷 - 嗷 */
+ 0x0f18, 0x0f19, /* 嗉 - 嗉 */
+ 0x0f40, 0x0f47, /* 嘟 - 嘟 */
+ 0x0f49, 0x0f69, /* 嘟 - 嘟 */
+ 0x10d0, 0x10f6, /* 醿 - 醿 */
+ 0x1100, 0x1159, /* 釀 - 釁 */
+ 0x115f, 0x11a2, /* 釁 - 釂 */
+ 0x11a8, 0x11f9, /* 釂 - 釃 */
+ 0x1e00, 0x1e9b, /* 岣 - 岷 */
+ 0x1f50, 0x1f57, /* 峤 - 峤 */
+ 0x1f80, 0x1fb4, /* 峋 - 峋 */
+ 0x1fb6, 0x1fbc, /* 峋 - 峋 */
+ 0x1fc2, 0x1fc4, /* 峥 - 峥 */
+ 0x1fc6, 0x1fcc, /* 峥 - 峥 */
+ 0x1fd0, 0x1fd3, /* 峥 - 峥 */
+ 0x1fd6, 0x1fdb, /* 峥 - 峥 */
+ 0x1fe0, 0x1fec, /* 峥 - 峥 */
+ 0x1ff2, 0x1ff4, /* 峥 - 峥 */
+ 0x1ff6, 0x1ffc, /* 峥 - 峥 */
+ 0x210a, 0x2113, /* 鈩 - 鈩 */
+ 0x2115, 0x211d, /* 鈩 - 鈩 */
+ 0x2120, 0x2122, /* 鈩 - 鈩 */
+ 0x212a, 0x2131, /* 鈩 - 鈩 */
+ 0x2133, 0x2138, /* 鈩 - 鈩 */
+ 0x3041, 0x3094, /* 銇 - 銈 */
+ 0x30a1, 0x30fa, /* 銈 - 銉 */
+ 0x3105, 0x312c, /* 銊 - 銊 */
+ 0x3131, 0x318e, /* 銊 - 銌 */
+ 0x3192, 0x319f, /* 銌 - 銌 */
+ 0x3260, 0x327b, /* 銐 - 銐 */
+ 0x328a, 0x32b0, /* 銑 - 銑 */
+ 0x32d0, 0x32fe, /* 銒 - 銒 */
+ 0x3300, 0x3357, /* 銓 - 銔 */
+ 0x3371, 0x3376, /* 銔 - 銔 */
+ 0x337b, 0x3394, /* 銔 - 銕 */
+ 0x3399, 0x339e, /* 銕 - 銕 */
+ 0x33a9, 0x33ad, /* 銕 - 銕 */
+ 0x33b0, 0x33c1, /* 銕 - 銖 */
+ 0x33c3, 0x33c5, /* 銖 - 銖 */
+ 0x33c7, 0x33d7, /* 銖 - 銖 */
+ 0x33d9, 0x33dd, /* 銖 - 銖 */
+ 0x4e00, 0x9fff, /* 涓 - 榭 */
+ 0xac00, 0xd7a3, /* 臧 - 頌 */
+ 0xf900, 0xfb06, /* 铯 - 铿 */
+ 0xfb13, 0xfb17, /* 铿 - 铿 */
+ 0xfb1f, 0xfb28, /* 铿 - 铿 */
+ 0xfb2a, 0xfb36, /* 铿 - 铿 */
+ 0xfb38, 0xfb3c, /* 铿 - 铿 */
+ 0xfb40, 0xfb41, /* 锃 - 锃 */
+ 0xfb43, 0xfb44, /* 锃 - 锃 */
+ 0xfb46, 0xfbb1, /* 锃 - 锂 */
+ 0xfbd3, 0xfd3d, /* 锆 - 锎 */
+ 0xfd50, 0xfd8f, /* 锏 - 锒 */
+ 0xfd92, 0xfdc7, /* 锒 - 锓 */
+ 0xfdf0, 0xfdf9, /* 锓 - 锓 */
+ 0xfe70, 0xfe72, /* 锕 - 锕 */
+ 0xfe76, 0xfefc, /* 锕 - 锘 */
+ 0xff66, 0xff6f, /* 锝 - 锝 */
+ 0xff71, 0xff9d, /* 锝 - 锞 */
+ 0xffa0, 0xffbe, /* 锞 - 锞 */
+ 0xffc2, 0xffc7, /* 锟 - 锟 */
+ 0xffca, 0xffcf, /* 锟 - 锟 */
+ 0xffd2, 0xffd7, /* 锟 - 锟 */
+ 0xffda, 0xffdc, /* 锟 - 锟 */
+};
+
+/*
+ * alpha singlets -
+ * only covers ranges not in lower||upper
+ */
+static
+Rune __alpha1[] =
+{
+ 0x00aa, /* 陋 */
+ 0x00b5, /* 碌 */
+ 0x00ba, /* 潞 */
+ 0x03da, /* 蠚 */
+ 0x03dc, /* 蠝 */
+ 0x03de, /* 蠟 */
+ 0x03e0, /* 蠣 */
+ 0x06d5, /* 蹠 */
+ 0x09b2, /* 唳 */
+ 0x0a5e, /* 喋 */
+ 0x0a8d, /* 嗒 */
+ 0x0ae0, /* 喃 */
+ 0x0b9c, /* 喈 */
+ 0x0cde, /* 喑 */
+ 0x0e4f, /* 喙 */
+ 0x0e84, /* 嗪 */
+ 0x0e8a, /* 嗪 */
+ 0x0e8d, /* 嗪 */
+ 0x0ea5, /* 嗪 */
+ 0x0ea7, /* 嗪 */
+ 0x0eb0, /* 嗪 */
+ 0x0ebd, /* 嗪 */
+ 0x1fbe, /* 峋 */
+ 0x207f, /* 鈦 */
+ 0x20a8, /* 鈧 */
+ 0x2102, /* 鈩 */
+ 0x2107, /* 鈩 */
+ 0x2124, /* 鈩 */
+ 0x2126, /* 鈩 */
+ 0x2128, /* 鈩 */
+ 0xfb3e, /* 铿 */
+ 0xfe74, /* 锕 */
+};
+
+/*
+ * space ranges
+ */
+static
+Rune __space2[] =
+{
+ 0x0009, 0x000a, /* tab and newline */
+ 0x0020, 0x0020, /* space */
+ 0x00a0, 0x00a0, /* 聽 */
+ 0x2000, 0x200b, /* 鈥 - 鈥 */
+ 0x2028, 0x2029, /* 鈥 - 鈥 */
+ 0x3000, 0x3000, /* 銆 */
+ 0xfeff, 0xfeff, /* 锘 */
+};
+
+/*
+ * lower case ranges
+ * 3rd col is conversion excess 500
+ */
+static
+Rune __toupper2[] =
+{
+ 0x0061, 0x007a, 468, /* a-z A-Z */
+ 0x00e0, 0x00f6, 468, /* 脿-枚 脌-脰 */
+ 0x00f8, 0x00fe, 468, /* 酶-镁 脴-脼 */
+ 0x0256, 0x0257, 295, /* 蓶-蓷 茐-茒 */
+ 0x0258, 0x0259, 298, /* 蓸-蓹 茙-茝 */
+ 0x028a, 0x028b, 283, /* 蕣-蕥 票-撇 */
+ 0x03ad, 0x03af, 463, /* 苇-委 螆-螉 */
+ 0x03b1, 0x03c1, 468, /* 伪-蟻 螒-巍 */
+ 0x03c3, 0x03cb, 468, /* 蟽-蠇 危-潍 */
+ 0x03cd, 0x03ce, 437, /* 蠉-蠋 螏-螐 */
+ 0x0430, 0x044f, 468, /* 邪-褟 袗-携 */
+ 0x0451, 0x045c, 420, /* 褢-褱 衼-袑 */
+ 0x045e, 0x045f, 420, /* 褳-褵 袔-袕 */
+ 0x0561, 0x0586, 452, /* 铡-謫 员-諙 */
+ 0x1f00, 0x1f07, 508, /* 峒-峒 峒-峒 */
+ 0x1f10, 0x1f15, 508, /* 峒-峒 峒-峒 */
+ 0x1f20, 0x1f27, 508, /* 峒-峒 峒-峒 */
+ 0x1f30, 0x1f37, 508, /* 峒-峒 峒-峒 */
+ 0x1f40, 0x1f45, 508, /* 峤-峤 峤-峤 */
+ 0x1f60, 0x1f67, 508, /* 峤-峤 峤-峤 */
+ 0x1f70, 0x1f71, 574, /* 峤-峤 峋-峋 */
+ 0x1f72, 0x1f75, 586, /* 峤-峤 峥-峥 */
+ 0x1f76, 0x1f77, 600, /* 峤-峤 峥-峥 */
+ 0x1f78, 0x1f79, 628, /* 峤-峤 峥-峥 */
+ 0x1f7a, 0x1f7b, 612, /* 峤-峤 峥-峥 */
+ 0x1f7c, 0x1f7d, 626, /* 峤-峤 峥-峥 */
+ 0x1f80, 0x1f87, 508, /* 峋-峋 峋-峋 */
+ 0x1f90, 0x1f97, 508, /* 峋-峋 峋-峋 */
+ 0x1fa0, 0x1fa7, 508, /* 峋-峋 峋-峋 */
+ 0x1fb0, 0x1fb1, 508, /* 峋-峋 峋-峋 */
+ 0x1fd0, 0x1fd1, 508, /* 峥-峥 峥-峥 */
+ 0x1fe0, 0x1fe1, 508, /* 峥-峥 峥-峥 */
+ 0x2170, 0x217f, 484, /* 鈪-鈪 鈪-鈪 */
+ 0x24d0, 0x24e9, 474, /* 鈸-鈸 鈷-鈸 */
+ 0xff41, 0xff5a, 468, /* 锝-锝 锛-锛 */
+};
+
+/*
+ * lower case singlets
+ * 2nd col is conversion excess 500
+ */
+static
+Rune __toupper1[] =
+{
+ 0x00ff, 621, /* 每 鸥 */
+ 0x0101, 499, /* 膩 膧 */
+ 0x0103, 499, /* 膬 膫 */
+ 0x0105, 499, /* 膮 膭 */
+ 0x0107, 499, /* 膰 膯 */
+ 0x0109, 499, /* 膲 膱 */
+ 0x010b, 499, /* 膵 膴 */
+ 0x010d, 499, /* 膷 膶 */
+ 0x010f, 499, /* 膹 膸 */
+ 0x0111, 499, /* 膽 膼 */
+ 0x0113, 499, /* 膿 膾 */
+ 0x0115, 499, /* 臅 臄 */
+ 0x0117, 499, /* 臈 臇 */
+ 0x0119, 499, /* 臋 臉 */
+ 0x011b, 499, /* 臎 臍 */
+ 0x011d, 499, /* 臐 臏 */
+ 0x011f, 499, /* 臒 臑 */
+ 0x0121, 499, /* 摹 臓 */
+ 0x0123, 499, /* 模 蘑 */
+ 0x0125, 499, /* 磨 膜 */
+ 0x0127, 499, /* 魔 摩 */
+ 0x0129, 499, /* 末 抹 */
+ 0x012b, 499, /* 墨 莫 */
+ 0x012d, 499, /* 沫 默 */
+ 0x012f, 499, /* 寞 漠 */
+ 0x0131, 268, /* 谋 I */
+ 0x0133, 499, /* 某 牟 */
+ 0x0135, 499, /* 牡 拇 */
+ 0x0137, 499, /* 姆 亩 */
+ 0x013a, 499, /* 暮 墓 */
+ 0x013c, 499, /* 募 幕 */
+ 0x013e, 499, /* 木 慕 */
+ 0x0140, 499, /* 艀 目 */
+ 0x0142, 499, /* 艂 艁 */
+ 0x0144, 499, /* 艅 艃 */
+ 0x0146, 499, /* 艈 艆 */
+ 0x0148, 499, /* 艌 艊 */
+ 0x014b, 499, /* 艐 艎 */
+ 0x014d, 499, /* 艒 艑 */
+ 0x014f, 499, /* 艔 艓 */
+ 0x0151, 499, /* 艖 艕 */
+ 0x0153, 499, /* 艙 艗 */
+ 0x0155, 499, /* 艜 艛 */
+ 0x0157, 499, /* 艞 艝 */
+ 0x0159, 499, /* 艡 艠 */
+ 0x015b, 499, /* 艣 艢 */
+ 0x015d, 499, /* 艥 艤 */
+ 0x015f, 499, /* 艧 艦 */
+ 0x0161, 499, /* 拧 艩 */
+ 0x0163, 499, /* 牛 泞 */
+ 0x0165, 499, /* 钮 扭 */
+ 0x0167, 499, /* 脓 纽 */
+ 0x0169, 499, /* 农 浓 */
+ 0x016b, 499, /* 奴 弄 */
+ 0x016d, 499, /* 怒 努 */
+ 0x016f, 499, /* 暖 女 */
+ 0x0171, 499, /* 疟 虐 */
+ 0x0173, 499, /* 懦 挪 */
+ 0x0175, 499, /* 诺 糯 */
+ 0x0177, 499, /* 欧 哦 */
+ 0x017a, 499, /* 藕 殴 */
+ 0x017c, 499, /* 偶 呕 */
+ 0x017e, 499, /* 啪 沤 */
+ 0x017f, 200, /* 趴 S */
+ 0x0183, 499, /* 苾 苽 */
+ 0x0185, 499, /* 茀 苿 */
+ 0x0188, 499, /* 茍 茋 */
+ 0x018c, 499, /* 茖 茓 */
+ 0x0192, 499, /* 茠 茟 */
+ 0x0199, 499, /* 茩 茦 */
+ 0x01a1, 499, /* 啤 茽 */
+ 0x01a3, 499, /* 疲 脾 */
+ 0x01a5, 499, /* 匹 皮 */
+ 0x01a8, 499, /* 屁 僻 */
+ 0x01ad, 499, /* 骗 片 */
+ 0x01b0, 499, /* 瓢 漂 */
+ 0x01b4, 499, /* 拼 瞥 */
+ 0x01b6, 499, /* 贫 频 */
+ 0x01b9, 499, /* 乒 聘 */
+ 0x01bd, 499, /* 平 萍 */
+ 0x01c5, 499, /* 菂 莿 */
+ 0x01c6, 498, /* 菃 莿 */
+ 0x01c8, 499, /* 菆 菄 */
+ 0x01c9, 498, /* 菈 菄 */
+ 0x01cb, 499, /* 菋 菉 */
+ 0x01cc, 498, /* 菍 菉 */
+ 0x01ce, 499, /* 菐 菎 */
+ 0x01d0, 499, /* 菒 菑 */
+ 0x01d2, 499, /* 菕 菓 */
+ 0x01d4, 499, /* 菙 菗 */
+ 0x01d6, 499, /* 菛 菚 */
+ 0x01d8, 499, /* 菢 菞 */
+ 0x01da, 499, /* 菤 菣 */
+ 0x01dc, 499, /* 菧 菦 */
+ 0x01df, 499, /* 菬 菫 */
+ 0x01e1, 499, /* 恰 菭 */
+ 0x01e3, 499, /* 牵 洽 */
+ 0x01e5, 499, /* 钎 扦 */
+ 0x01e7, 499, /* 千 铅 */
+ 0x01e9, 499, /* 签 迁 */
+ 0x01eb, 499, /* 谦 仟 */
+ 0x01ed, 499, /* 黔 乾 */
+ 0x01ef, 499, /* 钳 钱 */
+ 0x01f2, 499, /* 遣 潜 */
+ 0x01f3, 498, /* 浅 潜 */
+ 0x01f5, 499, /* 堑 谴 */
+ 0x01fb, 499, /* 腔 呛 */
+ 0x01fd, 499, /* 墙 羌 */
+ 0x01ff, 499, /* 强 蔷 */
+ 0x0201, 499, /* 葋 葊 */
+ 0x0203, 499, /* 葍 葌 */
+ 0x0205, 499, /* 葏 葎 */
+ 0x0207, 499, /* 葒 葐 */
+ 0x0209, 499, /* 葔 葓 */
+ 0x020b, 499, /* 葖 葕 */
+ 0x020d, 499, /* 葝 葘 */
+ 0x020f, 499, /* 葟 葞 */
+ 0x0211, 499, /* 葢 葠 */
+ 0x0213, 499, /* 葥 葤 */
+ 0x0215, 499, /* 葧 葦 */
+ 0x0217, 499, /* 葪 葨 */
+ 0x0253, 290, /* 蓳 苼 */
+ 0x0254, 294, /* 蓴 茊 */
+ 0x025b, 297, /* 蓻 茞 */
+ 0x0260, 295, /* 蔂 茡 */
+ 0x0263, 293, /* 桑 茢 */
+ 0x0268, 291, /* 扫 茥 */
+ 0x0269, 289, /* 嫂 茤 */
+ 0x026f, 289, /* 莎 茰 */
+ 0x0272, 287, /* 刹 茲 */
+ 0x0283, 282, /* 蕛 譬 */
+ 0x0288, 282, /* 蕡 飘 */
+ 0x0292, 281, /* 蕭 品 */
+ 0x03ac, 462, /* 维 螁 */
+ 0x03cc, 436, /* 蠈 螌 */
+ 0x03d0, 438, /* 蠍 螔 */
+ 0x03d1, 443, /* 蠎 螛 */
+ 0x03d5, 453, /* 蠒 桅 */
+ 0x03d6, 446, /* 蠔 螤 */
+ 0x03e3, 499, /* 希 息 */
+ 0x03e5, 499, /* 膝 悉 */
+ 0x03e7, 499, /* 惜 夕 */
+ 0x03e9, 499, /* 烯 熄 */
+ 0x03eb, 499, /* 汐 溪 */
+ 0x03ed, 499, /* 檄 犀 */
+ 0x03ef, 499, /* 席 袭 */
+ 0x03f0, 414, /* 习 螝 */
+ 0x03f1, 420, /* 媳 巍 */
+ 0x0461, 499, /* 选 褷 */
+ 0x0463, 499, /* 眩 癣 */
+ 0x0465, 499, /* 靴 绚 */
+ 0x0467, 499, /* 学 薛 */
+ 0x0469, 499, /* 雪 穴 */
+ 0x046b, 499, /* 勋 血 */
+ 0x046d, 499, /* 循 熏 */
+ 0x046f, 499, /* 询 旬 */
+ 0x0471, 499, /* 驯 寻 */
+ 0x0473, 499, /* 殉 巡 */
+ 0x0475, 499, /* 训 汛 */
+ 0x0477, 499, /* 逊 讯 */
+ 0x0479, 499, /* 压 迅 */
+ 0x047b, 499, /* 鸦 押 */
+ 0x047d, 499, /* 呀 鸭 */
+ 0x047f, 499, /* 芽 丫 */
+ 0x0481, 499, /* 襾 襽 */
+ 0x0491, 499, /* 覒 覑 */
+ 0x0493, 499, /* 覔 覓 */
+ 0x0495, 499, /* 視 覕 */
+ 0x0497, 499, /* 覘 覗 */
+ 0x0499, 499, /* 覚 覙 */
+ 0x049b, 499, /* 覜 覛 */
+ 0x049d, 499, /* 覞 覝 */
+ 0x049f, 499, /* 覠 覟 */
+ 0x04a1, 499, /* 摇 覡 */
+ 0x04a3, 499, /* 遥 尧 */
+ 0x04a5, 499, /* 谣 窑 */
+ 0x04a7, 499, /* 咬 姚 */
+ 0x04a9, 499, /* 药 舀 */
+ 0x04ab, 499, /* 耀 要 */
+ 0x04ad, 499, /* 噎 椰 */
+ 0x04af, 499, /* 爷 耶 */
+ 0x04b1, 499, /* 冶 野 */
+ 0x04b3, 499, /* 页 也 */
+ 0x04b5, 499, /* 业 掖 */
+ 0x04b7, 499, /* 曳 叶 */
+ 0x04b9, 499, /* 夜 腋 */
+ 0x04bb, 499, /* 一 液 */
+ 0x04bd, 499, /* 医 壹 */
+ 0x04bf, 499, /* 铱 揖 */
+ 0x04c2, 499, /* 觽 觼 */
+ 0x04c4, 499, /* 觿 觾 */
+ 0x04c8, 499, /* 訄 訃 */
+ 0x04cc, 499, /* 訉 計 */
+ 0x04d1, 499, /* 討 訍 */
+ 0x04d3, 499, /* 訐 訏 */
+ 0x04d5, 499, /* 訒 訑 */
+ 0x04d7, 499, /* 訔 訓 */
+ 0x04d9, 499, /* 訖 訕 */
+ 0x04db, 499, /* 記 託 */
+ 0x04dd, 499, /* 訚 訙 */
+ 0x04df, 499, /* 訜 訛 */
+ 0x04e1, 499, /* 印 訝 */
+ 0x04e3, 499, /* 樱 英 */
+ 0x04e5, 499, /* 鹰 婴 */
+ 0x04e7, 499, /* 缨 应 */
+ 0x04e9, 499, /* 萤 莹 */
+ 0x04eb, 499, /* 荧 营 */
+ 0x04ef, 499, /* 盈 赢 */
+ 0x04f1, 499, /* 颖 影 */
+ 0x04f3, 499, /* 映 硬 */
+ 0x04f5, 499, /* 拥 哟 */
+ 0x04f9, 499, /* 庸 痈 */
+ 0x1e01, 499, /* 岣 岣 */
+ 0x1e03, 499, /* 岣 岣 */
+ 0x1e05, 499, /* 岣 岣 */
+ 0x1e07, 499, /* 岣 岣 */
+ 0x1e09, 499, /* 岣 岣 */
+ 0x1e0b, 499, /* 岣 岣 */
+ 0x1e0d, 499, /* 岣 岣 */
+ 0x1e0f, 499, /* 岣 岣 */
+ 0x1e11, 499, /* 岣 岣 */
+ 0x1e13, 499, /* 岣 岣 */
+ 0x1e15, 499, /* 岣 岣 */
+ 0x1e17, 499, /* 岣 岣 */
+ 0x1e19, 499, /* 岣 岣 */
+ 0x1e1b, 499, /* 岣 岣 */
+ 0x1e1d, 499, /* 岣 岣 */
+ 0x1e1f, 499, /* 岣 岣 */
+ 0x1e21, 499, /* 岣 岣 */
+ 0x1e23, 499, /* 岣 岣 */
+ 0x1e25, 499, /* 岣 岣 */
+ 0x1e27, 499, /* 岣 岣 */
+ 0x1e29, 499, /* 岣 岣 */
+ 0x1e2b, 499, /* 岣 岣 */
+ 0x1e2d, 499, /* 岣 岣 */
+ 0x1e2f, 499, /* 岣 岣 */
+ 0x1e31, 499, /* 岣 岣 */
+ 0x1e33, 499, /* 岣 岣 */
+ 0x1e35, 499, /* 岣 岣 */
+ 0x1e37, 499, /* 岣 岣 */
+ 0x1e39, 499, /* 岣 岣 */
+ 0x1e3b, 499, /* 岣 岣 */
+ 0x1e3d, 499, /* 岣 岣 */
+ 0x1e3f, 499, /* 岣 岣 */
+ 0x1e41, 499, /* 峁 峁 */
+ 0x1e43, 499, /* 峁 峁 */
+ 0x1e45, 499, /* 峁 峁 */
+ 0x1e47, 499, /* 峁 峁 */
+ 0x1e49, 499, /* 峁 峁 */
+ 0x1e4b, 499, /* 峁 峁 */
+ 0x1e4d, 499, /* 峁 峁 */
+ 0x1e4f, 499, /* 峁 峁 */
+ 0x1e51, 499, /* 峁 峁 */
+ 0x1e53, 499, /* 峁 峁 */
+ 0x1e55, 499, /* 峁 峁 */
+ 0x1e57, 499, /* 峁 峁 */
+ 0x1e59, 499, /* 峁 峁 */
+ 0x1e5b, 499, /* 峁 峁 */
+ 0x1e5d, 499, /* 峁 峁 */
+ 0x1e5f, 499, /* 峁 峁 */
+ 0x1e61, 499, /* 峁 峁 */
+ 0x1e63, 499, /* 峁 峁 */
+ 0x1e65, 499, /* 峁 峁 */
+ 0x1e67, 499, /* 峁 峁 */
+ 0x1e69, 499, /* 峁 峁 */
+ 0x1e6b, 499, /* 峁 峁 */
+ 0x1e6d, 499, /* 峁 峁 */
+ 0x1e6f, 499, /* 峁 峁 */
+ 0x1e71, 499, /* 峁 峁 */
+ 0x1e73, 499, /* 峁 峁 */
+ 0x1e75, 499, /* 峁 峁 */
+ 0x1e77, 499, /* 峁 峁 */
+ 0x1e79, 499, /* 峁 峁 */
+ 0x1e7b, 499, /* 峁 峁 */
+ 0x1e7d, 499, /* 峁 峁 */
+ 0x1e7f, 499, /* 峁 峁 */
+ 0x1e81, 499, /* 岷 岷 */
+ 0x1e83, 499, /* 岷 岷 */
+ 0x1e85, 499, /* 岷 岷 */
+ 0x1e87, 499, /* 岷 岷 */
+ 0x1e89, 499, /* 岷 岷 */
+ 0x1e8b, 499, /* 岷 岷 */
+ 0x1e8d, 499, /* 岷 岷 */
+ 0x1e8f, 499, /* 岷 岷 */
+ 0x1e91, 499, /* 岷 岷 */
+ 0x1e93, 499, /* 岷 岷 */
+ 0x1e95, 499, /* 岷 岷 */
+ 0x1ea1, 499, /* 岷 岷 */
+ 0x1ea3, 499, /* 岷 岷 */
+ 0x1ea5, 499, /* 岷 岷 */
+ 0x1ea7, 499, /* 岷 岷 */
+ 0x1ea9, 499, /* 岷 岷 */
+ 0x1eab, 499, /* 岷 岷 */
+ 0x1ead, 499, /* 岷 岷 */
+ 0x1eaf, 499, /* 岷 岷 */
+ 0x1eb1, 499, /* 岷 岷 */
+ 0x1eb3, 499, /* 岷 岷 */
+ 0x1eb5, 499, /* 岷 岷 */
+ 0x1eb7, 499, /* 岷 岷 */
+ 0x1eb9, 499, /* 岷 岷 */
+ 0x1ebb, 499, /* 岷 岷 */
+ 0x1ebd, 499, /* 岷 岷 */
+ 0x1ebf, 499, /* 岷 岷 */
+ 0x1ec1, 499, /* 峄 峄 */
+ 0x1ec3, 499, /* 峄 峄 */
+ 0x1ec5, 499, /* 峄 峄 */
+ 0x1ec7, 499, /* 峄 峄 */
+ 0x1ec9, 499, /* 峄 峄 */
+ 0x1ecb, 499, /* 峄 峄 */
+ 0x1ecd, 499, /* 峄 峄 */
+ 0x1ecf, 499, /* 峄 峄 */
+ 0x1ed1, 499, /* 峄 峄 */
+ 0x1ed3, 499, /* 峄 峄 */
+ 0x1ed5, 499, /* 峄 峄 */
+ 0x1ed7, 499, /* 峄 峄 */
+ 0x1ed9, 499, /* 峄 峄 */
+ 0x1edb, 499, /* 峄 峄 */
+ 0x1edd, 499, /* 峄 峄 */
+ 0x1edf, 499, /* 峄 峄 */
+ 0x1ee1, 499, /* 峄 峄 */
+ 0x1ee3, 499, /* 峄 峄 */
+ 0x1ee5, 499, /* 峄 峄 */
+ 0x1ee7, 499, /* 峄 峄 */
+ 0x1ee9, 499, /* 峄 峄 */
+ 0x1eeb, 499, /* 峄 峄 */
+ 0x1eed, 499, /* 峄 峄 */
+ 0x1eef, 499, /* 峄 峄 */
+ 0x1ef1, 499, /* 峄 峄 */
+ 0x1ef3, 499, /* 峄 峄 */
+ 0x1ef5, 499, /* 峄 峄 */
+ 0x1ef7, 499, /* 峄 峄 */
+ 0x1ef9, 499, /* 峄 峄 */
+ 0x1f51, 508, /* 峤 峤 */
+ 0x1f53, 508, /* 峤 峤 */
+ 0x1f55, 508, /* 峤 峤 */
+ 0x1f57, 508, /* 峤 峤 */
+ 0x1fb3, 509, /* 峋 峋 */
+ 0x1fc3, 509, /* 峥 峥 */
+ 0x1fe5, 507, /* 峥 峥 */
+ 0x1ff3, 509, /* 峥 峥 */
+};
+
+/*
+ * upper case ranges
+ * 3rd col is conversion excess 500
+ */
+static
+Rune __tolower2[] =
+{
+ 0x0041, 0x005a, 532, /* A-Z a-z */
+ 0x00c0, 0x00d6, 532, /* 脌-脰 脿-枚 */
+ 0x00d8, 0x00de, 532, /* 脴-脼 酶-镁 */
+ 0x0189, 0x018a, 705, /* 茐-茒 蓶-蓷 */
+ 0x018e, 0x018f, 702, /* 茙-茝 蓸-蓹 */
+ 0x01b1, 0x01b2, 717, /* 票-撇 蕣-蕥 */
+ 0x0388, 0x038a, 537, /* 螆-螉 苇-委 */
+ 0x038e, 0x038f, 563, /* 螏-螐 蠉-蠋 */
+ 0x0391, 0x03a1, 532, /* 螒-巍 伪-蟻 */
+ 0x03a3, 0x03ab, 532, /* 危-潍 蟽-蠇 */
+ 0x0401, 0x040c, 580, /* 衼-袑 褢-褱 */
+ 0x040e, 0x040f, 580, /* 袔-袕 褳-褵 */
+ 0x0410, 0x042f, 532, /* 袗-携 邪-褟 */
+ 0x0531, 0x0556, 548, /* 员-諙 铡-謫 */
+ 0x10a0, 0x10c5, 548, /* 醾-醿 醿-醿 */
+ 0x1f08, 0x1f0f, 492, /* 峒-峒 峒-峒 */
+ 0x1f18, 0x1f1d, 492, /* 峒-峒 峒-峒 */
+ 0x1f28, 0x1f2f, 492, /* 峒-峒 峒-峒 */
+ 0x1f38, 0x1f3f, 492, /* 峒-峒 峒-峒 */
+ 0x1f48, 0x1f4d, 492, /* 峤-峤 峤-峤 */
+ 0x1f68, 0x1f6f, 492, /* 峤-峤 峤-峤 */
+ 0x1f88, 0x1f8f, 492, /* 峋-峋 峋-峋 */
+ 0x1f98, 0x1f9f, 492, /* 峋-峋 峋-峋 */
+ 0x1fa8, 0x1faf, 492, /* 峋-峋 峋-峋 */
+ 0x1fb8, 0x1fb9, 492, /* 峋-峋 峋-峋 */
+ 0x1fba, 0x1fbb, 426, /* 峋-峋 峤-峤 */
+ 0x1fc8, 0x1fcb, 414, /* 峥-峥 峤-峤 */
+ 0x1fd8, 0x1fd9, 492, /* 峥-峥 峥-峥 */
+ 0x1fda, 0x1fdb, 400, /* 峥-峥 峤-峤 */
+ 0x1fe8, 0x1fe9, 492, /* 峥-峥 峥-峥 */
+ 0x1fea, 0x1feb, 388, /* 峥-峥 峤-峤 */
+ 0x1ff8, 0x1ff9, 372, /* 峥-峥 峤-峤 */
+ 0x1ffa, 0x1ffb, 374, /* 峥-峥 峤-峤 */
+ 0x2160, 0x216f, 516, /* 鈪-鈪 鈪-鈪 */
+ 0x24b6, 0x24cf, 526, /* 鈷-鈸 鈸-鈸 */
+ 0xff21, 0xff3a, 532, /* 锛-锛 锝-锝 */
+};
+
+/*
+ * upper case singlets
+ * 2nd col is conversion excess 500
+ */
+static
+Rune __tolower1[] =
+{
+ 0x0100, 501, /* 膧 膩 */
+ 0x0102, 501, /* 膫 膬 */
+ 0x0104, 501, /* 膭 膮 */
+ 0x0106, 501, /* 膯 膰 */
+ 0x0108, 501, /* 膱 膲 */
+ 0x010a, 501, /* 膴 膵 */
+ 0x010c, 501, /* 膶 膷 */
+ 0x010e, 501, /* 膸 膹 */
+ 0x0110, 501, /* 膼 膽 */
+ 0x0112, 501, /* 膾 膿 */
+ 0x0114, 501, /* 臄 臅 */
+ 0x0116, 501, /* 臇 臈 */
+ 0x0118, 501, /* 臉 臋 */
+ 0x011a, 501, /* 臍 臎 */
+ 0x011c, 501, /* 臏 臐 */
+ 0x011e, 501, /* 臑 臒 */
+ 0x0120, 501, /* 臓 摹 */
+ 0x0122, 501, /* 蘑 模 */
+ 0x0124, 501, /* 膜 磨 */
+ 0x0126, 501, /* 摩 魔 */
+ 0x0128, 501, /* 抹 末 */
+ 0x012a, 501, /* 莫 墨 */
+ 0x012c, 501, /* 默 沫 */
+ 0x012e, 501, /* 漠 寞 */
+ 0x0130, 301, /* 陌 i */
+ 0x0132, 501, /* 牟 某 */
+ 0x0134, 501, /* 拇 牡 */
+ 0x0136, 501, /* 亩 姆 */
+ 0x0139, 501, /* 墓 暮 */
+ 0x013b, 501, /* 幕 募 */
+ 0x013d, 501, /* 慕 木 */
+ 0x013f, 501, /* 目 艀 */
+ 0x0141, 501, /* 艁 艂 */
+ 0x0143, 501, /* 艃 艅 */
+ 0x0145, 501, /* 艆 艈 */
+ 0x0147, 501, /* 艊 艌 */
+ 0x014a, 501, /* 艎 艐 */
+ 0x014c, 501, /* 艑 艒 */
+ 0x014e, 501, /* 艓 艔 */
+ 0x0150, 501, /* 艕 艖 */
+ 0x0152, 501, /* 艗 艙 */
+ 0x0154, 501, /* 艛 艜 */
+ 0x0156, 501, /* 艝 艞 */
+ 0x0158, 501, /* 艠 艡 */
+ 0x015a, 501, /* 艢 艣 */
+ 0x015c, 501, /* 艤 艥 */
+ 0x015e, 501, /* 艦 艧 */
+ 0x0160, 501, /* 艩 拧 */
+ 0x0162, 501, /* 泞 牛 */
+ 0x0164, 501, /* 扭 钮 */
+ 0x0166, 501, /* 纽 脓 */
+ 0x0168, 501, /* 浓 农 */
+ 0x016a, 501, /* 弄 奴 */
+ 0x016c, 501, /* 努 怒 */
+ 0x016e, 501, /* 女 暖 */
+ 0x0170, 501, /* 虐 疟 */
+ 0x0172, 501, /* 挪 懦 */
+ 0x0174, 501, /* 糯 诺 */
+ 0x0176, 501, /* 哦 欧 */
+ 0x0178, 379, /* 鸥 每 */
+ 0x0179, 501, /* 殴 藕 */
+ 0x017b, 501, /* 呕 偶 */
+ 0x017d, 501, /* 沤 啪 */
+ 0x0181, 710, /* 苼 蓳 */
+ 0x0182, 501, /* 苽 苾 */
+ 0x0184, 501, /* 苿 茀 */
+ 0x0186, 706, /* 茊 蓴 */
+ 0x0187, 501, /* 茋 茍 */
+ 0x018b, 501, /* 茓 茖 */
+ 0x0190, 703, /* 茞 蓻 */
+ 0x0191, 501, /* 茟 茠 */
+ 0x0193, 705, /* 茡 蔂 */
+ 0x0194, 707, /* 茢 桑 */
+ 0x0196, 711, /* 茤 嫂 */
+ 0x0197, 709, /* 茥 扫 */
+ 0x0198, 501, /* 茦 茩 */
+ 0x019c, 711, /* 茰 莎 */
+ 0x019d, 713, /* 茲 刹 */
+ 0x01a0, 501, /* 茽 啤 */
+ 0x01a2, 501, /* 脾 疲 */
+ 0x01a4, 501, /* 皮 匹 */
+ 0x01a7, 501, /* 僻 屁 */
+ 0x01a9, 718, /* 譬 蕛 */
+ 0x01ac, 501, /* 片 骗 */
+ 0x01ae, 718, /* 飘 蕡 */
+ 0x01af, 501, /* 漂 瓢 */
+ 0x01b3, 501, /* 瞥 拼 */
+ 0x01b5, 501, /* 频 贫 */
+ 0x01b7, 719, /* 品 蕭 */
+ 0x01b8, 501, /* 聘 乒 */
+ 0x01bc, 501, /* 萍 平 */
+ 0x01c4, 502, /* 莿 菃 */
+ 0x01c5, 501, /* 菂 菃 */
+ 0x01c7, 502, /* 菄 菈 */
+ 0x01c8, 501, /* 菆 菈 */
+ 0x01ca, 502, /* 菉 菍 */
+ 0x01cb, 501, /* 菋 菍 */
+ 0x01cd, 501, /* 菎 菐 */
+ 0x01cf, 501, /* 菑 菒 */
+ 0x01d1, 501, /* 菓 菕 */
+ 0x01d3, 501, /* 菗 菙 */
+ 0x01d5, 501, /* 菚 菛 */
+ 0x01d7, 501, /* 菞 菢 */
+ 0x01d9, 501, /* 菣 菤 */
+ 0x01db, 501, /* 菦 菧 */
+ 0x01de, 501, /* 菫 菬 */
+ 0x01e0, 501, /* 菭 恰 */
+ 0x01e2, 501, /* 洽 牵 */
+ 0x01e4, 501, /* 扦 钎 */
+ 0x01e6, 501, /* 铅 千 */
+ 0x01e8, 501, /* 迁 签 */
+ 0x01ea, 501, /* 仟 谦 */
+ 0x01ec, 501, /* 乾 黔 */
+ 0x01ee, 501, /* 钱 钳 */
+ 0x01f1, 502, /* 潜 浅 */
+ 0x01f2, 501, /* 遣 浅 */
+ 0x01f4, 501, /* 谴 堑 */
+ 0x01fa, 501, /* 呛 腔 */
+ 0x01fc, 501, /* 羌 墙 */
+ 0x01fe, 501, /* 蔷 强 */
+ 0x0200, 501, /* 葊 葋 */
+ 0x0202, 501, /* 葌 葍 */
+ 0x0204, 501, /* 葎 葏 */
+ 0x0206, 501, /* 葐 葒 */
+ 0x0208, 501, /* 葓 葔 */
+ 0x020a, 501, /* 葕 葖 */
+ 0x020c, 501, /* 葘 葝 */
+ 0x020e, 501, /* 葞 葟 */
+ 0x0210, 501, /* 葠 葢 */
+ 0x0212, 501, /* 葤 葥 */
+ 0x0214, 501, /* 葦 葧 */
+ 0x0216, 501, /* 葨 葪 */
+ 0x0386, 538, /* 螁 维 */
+ 0x038c, 564, /* 螌 蠈 */
+ 0x03e2, 501, /* 息 希 */
+ 0x03e4, 501, /* 悉 膝 */
+ 0x03e6, 501, /* 夕 惜 */
+ 0x03e8, 501, /* 熄 烯 */
+ 0x03ea, 501, /* 溪 汐 */
+ 0x03ec, 501, /* 犀 檄 */
+ 0x03ee, 501, /* 袭 席 */
+ 0x0460, 501, /* 褷 选 */
+ 0x0462, 501, /* 癣 眩 */
+ 0x0464, 501, /* 绚 靴 */
+ 0x0466, 501, /* 薛 学 */
+ 0x0468, 501, /* 穴 雪 */
+ 0x046a, 501, /* 血 勋 */
+ 0x046c, 501, /* 熏 循 */
+ 0x046e, 501, /* 旬 询 */
+ 0x0470, 501, /* 寻 驯 */
+ 0x0472, 501, /* 巡 殉 */
+ 0x0474, 501, /* 汛 训 */
+ 0x0476, 501, /* 讯 逊 */
+ 0x0478, 501, /* 迅 压 */
+ 0x047a, 501, /* 押 鸦 */
+ 0x047c, 501, /* 鸭 呀 */
+ 0x047e, 501, /* 丫 芽 */
+ 0x0480, 501, /* 襽 襾 */
+ 0x0490, 501, /* 覑 覒 */
+ 0x0492, 501, /* 覓 覔 */
+ 0x0494, 501, /* 覕 視 */
+ 0x0496, 501, /* 覗 覘 */
+ 0x0498, 501, /* 覙 覚 */
+ 0x049a, 501, /* 覛 覜 */
+ 0x049c, 501, /* 覝 覞 */
+ 0x049e, 501, /* 覟 覠 */
+ 0x04a0, 501, /* 覡 摇 */
+ 0x04a2, 501, /* 尧 遥 */
+ 0x04a4, 501, /* 窑 谣 */
+ 0x04a6, 501, /* 姚 咬 */
+ 0x04a8, 501, /* 舀 药 */
+ 0x04aa, 501, /* 要 耀 */
+ 0x04ac, 501, /* 椰 噎 */
+ 0x04ae, 501, /* 耶 爷 */
+ 0x04b0, 501, /* 野 冶 */
+ 0x04b2, 501, /* 也 页 */
+ 0x04b4, 501, /* 掖 业 */
+ 0x04b6, 501, /* 叶 曳 */
+ 0x04b8, 501, /* 腋 夜 */
+ 0x04ba, 501, /* 液 一 */
+ 0x04bc, 501, /* 壹 医 */
+ 0x04be, 501, /* 揖 铱 */
+ 0x04c1, 501, /* 觼 觽 */
+ 0x04c3, 501, /* 觾 觿 */
+ 0x04c7, 501, /* 訃 訄 */
+ 0x04cb, 501, /* 計 訉 */
+ 0x04d0, 501, /* 訍 討 */
+ 0x04d2, 501, /* 訏 訐 */
+ 0x04d4, 501, /* 訑 訒 */
+ 0x04d6, 501, /* 訓 訔 */
+ 0x04d8, 501, /* 訕 訖 */
+ 0x04da, 501, /* 託 記 */
+ 0x04dc, 501, /* 訙 訚 */
+ 0x04de, 501, /* 訛 訜 */
+ 0x04e0, 501, /* 訝 印 */
+ 0x04e2, 501, /* 英 樱 */
+ 0x04e4, 501, /* 婴 鹰 */
+ 0x04e6, 501, /* 应 缨 */
+ 0x04e8, 501, /* 莹 萤 */
+ 0x04ea, 501, /* 营 荧 */
+ 0x04ee, 501, /* 赢 盈 */
+ 0x04f0, 501, /* 影 颖 */
+ 0x04f2, 501, /* 硬 映 */
+ 0x04f4, 501, /* 哟 拥 */
+ 0x04f8, 501, /* 痈 庸 */
+ 0x1e00, 501, /* 岣 岣 */
+ 0x1e02, 501, /* 岣 岣 */
+ 0x1e04, 501, /* 岣 岣 */
+ 0x1e06, 501, /* 岣 岣 */
+ 0x1e08, 501, /* 岣 岣 */
+ 0x1e0a, 501, /* 岣 岣 */
+ 0x1e0c, 501, /* 岣 岣 */
+ 0x1e0e, 501, /* 岣 岣 */
+ 0x1e10, 501, /* 岣 岣 */
+ 0x1e12, 501, /* 岣 岣 */
+ 0x1e14, 501, /* 岣 岣 */
+ 0x1e16, 501, /* 岣 岣 */
+ 0x1e18, 501, /* 岣 岣 */
+ 0x1e1a, 501, /* 岣 岣 */
+ 0x1e1c, 501, /* 岣 岣 */
+ 0x1e1e, 501, /* 岣 岣 */
+ 0x1e20, 501, /* 岣 岣 */
+ 0x1e22, 501, /* 岣 岣 */
+ 0x1e24, 501, /* 岣 岣 */
+ 0x1e26, 501, /* 岣 岣 */
+ 0x1e28, 501, /* 岣 岣 */
+ 0x1e2a, 501, /* 岣 岣 */
+ 0x1e2c, 501, /* 岣 岣 */
+ 0x1e2e, 501, /* 岣 岣 */
+ 0x1e30, 501, /* 岣 岣 */
+ 0x1e32, 501, /* 岣 岣 */
+ 0x1e34, 501, /* 岣 岣 */
+ 0x1e36, 501, /* 岣 岣 */
+ 0x1e38, 501, /* 岣 岣 */
+ 0x1e3a, 501, /* 岣 岣 */
+ 0x1e3c, 501, /* 岣 岣 */
+ 0x1e3e, 501, /* 岣 岣 */
+ 0x1e40, 501, /* 峁 峁 */
+ 0x1e42, 501, /* 峁 峁 */
+ 0x1e44, 501, /* 峁 峁 */
+ 0x1e46, 501, /* 峁 峁 */
+ 0x1e48, 501, /* 峁 峁 */
+ 0x1e4a, 501, /* 峁 峁 */
+ 0x1e4c, 501, /* 峁 峁 */
+ 0x1e4e, 501, /* 峁 峁 */
+ 0x1e50, 501, /* 峁 峁 */
+ 0x1e52, 501, /* 峁 峁 */
+ 0x1e54, 501, /* 峁 峁 */
+ 0x1e56, 501, /* 峁 峁 */
+ 0x1e58, 501, /* 峁 峁 */
+ 0x1e5a, 501, /* 峁 峁 */
+ 0x1e5c, 501, /* 峁 峁 */
+ 0x1e5e, 501, /* 峁 峁 */
+ 0x1e60, 501, /* 峁 峁 */
+ 0x1e62, 501, /* 峁 峁 */
+ 0x1e64, 501, /* 峁 峁 */
+ 0x1e66, 501, /* 峁 峁 */
+ 0x1e68, 501, /* 峁 峁 */
+ 0x1e6a, 501, /* 峁 峁 */
+ 0x1e6c, 501, /* 峁 峁 */
+ 0x1e6e, 501, /* 峁 峁 */
+ 0x1e70, 501, /* 峁 峁 */
+ 0x1e72, 501, /* 峁 峁 */
+ 0x1e74, 501, /* 峁 峁 */
+ 0x1e76, 501, /* 峁 峁 */
+ 0x1e78, 501, /* 峁 峁 */
+ 0x1e7a, 501, /* 峁 峁 */
+ 0x1e7c, 501, /* 峁 峁 */
+ 0x1e7e, 501, /* 峁 峁 */
+ 0x1e80, 501, /* 岷 岷 */
+ 0x1e82, 501, /* 岷 岷 */
+ 0x1e84, 501, /* 岷 岷 */
+ 0x1e86, 501, /* 岷 岷 */
+ 0x1e88, 501, /* 岷 岷 */
+ 0x1e8a, 501, /* 岷 岷 */
+ 0x1e8c, 501, /* 岷 岷 */
+ 0x1e8e, 501, /* 岷 岷 */
+ 0x1e90, 501, /* 岷 岷 */
+ 0x1e92, 501, /* 岷 岷 */
+ 0x1e94, 501, /* 岷 岷 */
+ 0x1ea0, 501, /* 岷 岷 */
+ 0x1ea2, 501, /* 岷 岷 */
+ 0x1ea4, 501, /* 岷 岷 */
+ 0x1ea6, 501, /* 岷 岷 */
+ 0x1ea8, 501, /* 岷 岷 */
+ 0x1eaa, 501, /* 岷 岷 */
+ 0x1eac, 501, /* 岷 岷 */
+ 0x1eae, 501, /* 岷 岷 */
+ 0x1eb0, 501, /* 岷 岷 */
+ 0x1eb2, 501, /* 岷 岷 */
+ 0x1eb4, 501, /* 岷 岷 */
+ 0x1eb6, 501, /* 岷 岷 */
+ 0x1eb8, 501, /* 岷 岷 */
+ 0x1eba, 501, /* 岷 岷 */
+ 0x1ebc, 501, /* 岷 岷 */
+ 0x1ebe, 501, /* 岷 岷 */
+ 0x1ec0, 501, /* 峄 峄 */
+ 0x1ec2, 501, /* 峄 峄 */
+ 0x1ec4, 501, /* 峄 峄 */
+ 0x1ec6, 501, /* 峄 峄 */
+ 0x1ec8, 501, /* 峄 峄 */
+ 0x1eca, 501, /* 峄 峄 */
+ 0x1ecc, 501, /* 峄 峄 */
+ 0x1ece, 501, /* 峄 峄 */
+ 0x1ed0, 501, /* 峄 峄 */
+ 0x1ed2, 501, /* 峄 峄 */
+ 0x1ed4, 501, /* 峄 峄 */
+ 0x1ed6, 501, /* 峄 峄 */
+ 0x1ed8, 501, /* 峄 峄 */
+ 0x1eda, 501, /* 峄 峄 */
+ 0x1edc, 501, /* 峄 峄 */
+ 0x1ede, 501, /* 峄 峄 */
+ 0x1ee0, 501, /* 峄 峄 */
+ 0x1ee2, 501, /* 峄 峄 */
+ 0x1ee4, 501, /* 峄 峄 */
+ 0x1ee6, 501, /* 峄 峄 */
+ 0x1ee8, 501, /* 峄 峄 */
+ 0x1eea, 501, /* 峄 峄 */
+ 0x1eec, 501, /* 峄 峄 */
+ 0x1eee, 501, /* 峄 峄 */
+ 0x1ef0, 501, /* 峄 峄 */
+ 0x1ef2, 501, /* 峄 峄 */
+ 0x1ef4, 501, /* 峄 峄 */
+ 0x1ef6, 501, /* 峄 峄 */
+ 0x1ef8, 501, /* 峄 峄 */
+ 0x1f59, 492, /* 峤 峤 */
+ 0x1f5b, 492, /* 峤 峤 */
+ 0x1f5d, 492, /* 峤 峤 */
+ 0x1f5f, 492, /* 峤 峤 */
+ 0x1fbc, 491, /* 峋 峋 */
+ 0x1fcc, 491, /* 峥 峥 */
+ 0x1fec, 493, /* 峥 峥 */
+ 0x1ffc, 491, /* 峥 峥 */
+};
+
+/*
+ * title characters are those between
+ * upper and lower case. ie DZ Dz dz
+ */
+static
+Rune __totitle1[] =
+{
+ 0x01c4, 501, /* 莿 菂 */
+ 0x01c6, 499, /* 菃 菂 */
+ 0x01c7, 501, /* 菄 菆 */
+ 0x01c9, 499, /* 菈 菆 */
+ 0x01ca, 501, /* 菉 菋 */
+ 0x01cc, 499, /* 菍 菋 */
+ 0x01f1, 501, /* 潜 遣 */
+ 0x01f3, 499, /* 浅 遣 */
+};
+
+static Rune*
+bsearch(Rune c, Rune *t, int n, int ne)
+{
+ Rune *p;
+ int m;
+
+ while(n > 1) {
+ m = n/2;
+ p = t + m*ne;
+ if(c >= p[0]) {
+ t = p;
+ n = n-m;
+ } else
+ n = m;
+ }
+ if(n && c >= t[0])
+ return t;
+ return 0;
+}
+
+Rune
+tolowerrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return c + p[2] - 500;
+ p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2);
+ if(p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+Rune
+toupperrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return c + p[2] - 500;
+ p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2);
+ if(p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+Rune
+totitlerune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __totitle1, nelem(__totitle1)/2, 2);
+ if(p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+int
+islowerrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2);
+ if(p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int
+isupperrune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2);
+ if(p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int
+isalpharune(Rune c)
+{
+ Rune *p;
+
+ if(isupperrune(c) || islowerrune(c))
+ return 1;
+ p = bsearch(c, __alpha2, nelem(__alpha2)/2, 2);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, __alpha1, nelem(__alpha1), 1);
+ if(p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int
+istitlerune(Rune c)
+{
+ return isupperrune(c) && islowerrune(c);
+}
+
+int
+isspacerune(Rune c)
+{
+ Rune *p;
+
+ p = bsearch(c, __space2, nelem(__space2)/2, 2);
+ if(p && c >= p[0] && c <= p[1])
+ return 1;
+ return 0;
+}