mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
initial check-in of the new version (CVS 1)
FossilOrigin-Name: 6f3655f79f9b6fc9fb7baaa10a7e0f2b6a512dfa
This commit is contained in:
105
Makefile.in
Normal file
105
Makefile.in
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/make
|
||||||
|
#
|
||||||
|
# Makefile for SQLITE
|
||||||
|
|
||||||
|
# The toplevel directory of the source tree
|
||||||
|
#
|
||||||
|
TOP = @srcdir@
|
||||||
|
|
||||||
|
# C Compiler and options for use in building executables that
|
||||||
|
# will run on the platform that is doing the build.
|
||||||
|
#
|
||||||
|
BCC = @BUILD_CC@ @BUILD_CFLAGS@
|
||||||
|
|
||||||
|
# C Compile and options for use in building executables that
|
||||||
|
# will run on the target platform.
|
||||||
|
#
|
||||||
|
TCC = @TARGET_CC@ @TARGET_CFLAGS@ -I. -I${TOP}/src
|
||||||
|
|
||||||
|
# Tools used to build a static library.
|
||||||
|
#
|
||||||
|
AR = @TARGET_AR@
|
||||||
|
RANLIB = @TARGET_RANLIB@
|
||||||
|
|
||||||
|
# Compiler options needed for programs that use the GDBM library.
|
||||||
|
#
|
||||||
|
GDBM_FLAGS = @TARGET_GDBM_INC@
|
||||||
|
|
||||||
|
# The library that programs using GDBM must link against.
|
||||||
|
#
|
||||||
|
LIBGDBM = @TARGET_GDBM_LIBS@
|
||||||
|
|
||||||
|
# Compiler options needed for programs that use the readline() library.
|
||||||
|
#
|
||||||
|
READLINE_FLAGS = -DHAVE_READLINE=@TARGET_HAVE_READLINE@ @TARGET_READLINE_INC@
|
||||||
|
|
||||||
|
# The library that programs using readline() must link against.
|
||||||
|
#
|
||||||
|
LIBREADLINE = @TARGET_READLINE_LIBS@
|
||||||
|
|
||||||
|
# Object files for the SQLite library.
|
||||||
|
#
|
||||||
|
LIBOBJ = build.o dbbe.o main.o parse.o tokenize.o util.o vdbe.o where.o
|
||||||
|
|
||||||
|
# This is the default Makefile target. The objects listed here
|
||||||
|
# are what get build when you type just "make" with no arguments.
|
||||||
|
#
|
||||||
|
all: libsqlite.a sqlite
|
||||||
|
# libtclsqlite.a tclsqlite
|
||||||
|
|
||||||
|
libsqlite.a: $(LIBOBJ)
|
||||||
|
$(AR) libsqlite.a $(LIBOBJ)
|
||||||
|
$(RANLIB) libsqlite.a
|
||||||
|
|
||||||
|
sqlite: $(TOP)/src/shell.c libsqlite.a $(TOP)/src/sqlite.h
|
||||||
|
$(TCC) $(READLINE_FLAGS) -o sqlite $(TOP)/src/shell.c \
|
||||||
|
libsqlite.a $(LIBGDBM) $(LIBREADLINE)
|
||||||
|
|
||||||
|
# Rules to build the LEMON compiler generator
|
||||||
|
#
|
||||||
|
lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
|
||||||
|
$(BCC) -o lemon $(TOP)/tool/lemon.c
|
||||||
|
cp $(TOP)/tool/lempar.c .
|
||||||
|
|
||||||
|
# Header files used by all library source files.
|
||||||
|
#
|
||||||
|
HDR = \
|
||||||
|
$(TOP)/src/sqlite.h \
|
||||||
|
$(TOP)/src/sqliteInt.h \
|
||||||
|
$(TOP)/src/dbbe.h \
|
||||||
|
$(TOP)/src/vdbe.h \
|
||||||
|
parse.h
|
||||||
|
|
||||||
|
build.o: $(TOP)/src/build.c $(HDR)
|
||||||
|
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/build.c
|
||||||
|
|
||||||
|
dbbe.o: $(TOP)/src/dbbe.c $(HDR)
|
||||||
|
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbe.c
|
||||||
|
|
||||||
|
main.o: $(TOP)/src/main.c $(HDR)
|
||||||
|
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/main.c
|
||||||
|
|
||||||
|
parse.o: parse.c $(HDR)
|
||||||
|
$(TCC) $(GDBM_FLAGS) -c parse.c
|
||||||
|
|
||||||
|
parse.h: parse.c
|
||||||
|
|
||||||
|
parse.c: $(TOP)/src/parse.y lemon
|
||||||
|
cp $(TOP)/src/parse.y .
|
||||||
|
./lemon parse.y
|
||||||
|
|
||||||
|
tokenize.o: $(TOP)/src/tokenize.c $(HDR)
|
||||||
|
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/tokenize.c
|
||||||
|
|
||||||
|
util.o: $(TOP)/src/util.c $(HDR)
|
||||||
|
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/util.c
|
||||||
|
|
||||||
|
vdbe.o: $(TOP)/src/vdbe.c $(HDR)
|
||||||
|
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/vdbe.c
|
||||||
|
|
||||||
|
where.o: $(TOP)/src/where.c $(HDR)
|
||||||
|
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/where.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o sqlite libsqlite.a
|
||||||
|
rm -f lemon lempar.c parse.*
|
513
configure.in
Normal file
513
configure.in
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
#
|
||||||
|
# This file describes a "configure" script that is used to build
|
||||||
|
# makefiles for a particular platform. Process this file using
|
||||||
|
# Autoconf version 1.13 in order to generate that script. All
|
||||||
|
# lines of this file up to the AC_INIT macro are ignored.
|
||||||
|
#
|
||||||
|
# The build process allows for using a cross-compiler. But the default
|
||||||
|
# action is to target the same platform that we are running on. The
|
||||||
|
# configure script needs to discover the following properties of the
|
||||||
|
# build and target systems:
|
||||||
|
#
|
||||||
|
# srcdir
|
||||||
|
#
|
||||||
|
# The is the name of the directory that contains the
|
||||||
|
# "configure" shell script. All source files are
|
||||||
|
# located relative to this directory.
|
||||||
|
#
|
||||||
|
# bindir
|
||||||
|
#
|
||||||
|
# The name of the directory where executables should be
|
||||||
|
# written by the "install" target of the makefile.
|
||||||
|
#
|
||||||
|
# program_prefix
|
||||||
|
#
|
||||||
|
# Add this prefix to the names of all executables that run
|
||||||
|
# on the target machine. Default: ""
|
||||||
|
#
|
||||||
|
# ENABLE_SHARED
|
||||||
|
#
|
||||||
|
# True if shared libraries should be generated.
|
||||||
|
#
|
||||||
|
# BUILD_CC
|
||||||
|
#
|
||||||
|
# The name of a command that is used to convert C
|
||||||
|
# source files into executables that run on the build
|
||||||
|
# platform.
|
||||||
|
#
|
||||||
|
# BUILD_CFLAGS
|
||||||
|
#
|
||||||
|
# Switches that the build compiler needs in order to construct
|
||||||
|
# command-line programs.
|
||||||
|
#
|
||||||
|
# BUILD_LIBS
|
||||||
|
#
|
||||||
|
# Libraries that the build compiler needs in order to construct
|
||||||
|
# command-line programs.
|
||||||
|
#
|
||||||
|
# BUILD_EXEEXT
|
||||||
|
#
|
||||||
|
# The filename extension for executables on the build
|
||||||
|
# platform. "" for Unix and ".exe" for Windows.
|
||||||
|
#
|
||||||
|
# TARGET_CC
|
||||||
|
#
|
||||||
|
# The name of a command that runs on the build platform
|
||||||
|
# and converts C source files into *.o files for the
|
||||||
|
# target platform. In other words, the cross-compiler.
|
||||||
|
#
|
||||||
|
# TARGET_CFLAGS
|
||||||
|
#
|
||||||
|
# Switches that the target compiler needs to turn C source files
|
||||||
|
# into *.o files. Do not include TARGET_TCL_INC in this list.
|
||||||
|
# Makefiles might add additional switches such as "-I.".
|
||||||
|
#
|
||||||
|
# TARGET_TCL_LIBS
|
||||||
|
#
|
||||||
|
# This is the library directives passed to the target linker
|
||||||
|
# that cause the executable to link against Tcl. This might
|
||||||
|
# be a switch like "-ltcl8.0" or pathnames of library file
|
||||||
|
# like "../../src/libtcl8.0.a".
|
||||||
|
#
|
||||||
|
# TARGET_TCL_INC
|
||||||
|
#
|
||||||
|
# This variables define the directory that contain header
|
||||||
|
# files for Tcl. If the compiler is able to find <tcl.h>
|
||||||
|
# on its own, then this can be blank.
|
||||||
|
#
|
||||||
|
# TARGET_GDBM_LIBS
|
||||||
|
#
|
||||||
|
# This is the library directives passed to the target linker
|
||||||
|
# that cause the executable to link against GDBM. This might
|
||||||
|
# be a switch like "-lgdbm" or pathnames of library file
|
||||||
|
# like "../../src/libgdbm.a".
|
||||||
|
#
|
||||||
|
# TARGET_GDBM_INC
|
||||||
|
#
|
||||||
|
# This variables define the directory that contain header
|
||||||
|
# files for GDBM. If the compiler is able to find <gdbm.h>
|
||||||
|
# on its own, then this can be blank.
|
||||||
|
#
|
||||||
|
# TARGET_READLINE_LIBS
|
||||||
|
#
|
||||||
|
# This is the library directives passed to the target linker
|
||||||
|
# that cause the executable to link against GDBM. This might
|
||||||
|
# be a switch like "-lreadline" or pathnames of library file
|
||||||
|
# like "../../src/libreadline.a".
|
||||||
|
#
|
||||||
|
# TARGET_READLINE_INC
|
||||||
|
#
|
||||||
|
# This variables define the directory that contain header
|
||||||
|
# files for the readline library. If the compiler is able
|
||||||
|
# to find <readline.h> on its own, then this can be blank.
|
||||||
|
#
|
||||||
|
# TARGET_LINK
|
||||||
|
#
|
||||||
|
# The name of the linker that combines *.o files generated
|
||||||
|
# by TARGET_CC into executables for the target platform.
|
||||||
|
#
|
||||||
|
# TARGET_LIBS
|
||||||
|
#
|
||||||
|
# Additional libraries or other switch that the target linker needs
|
||||||
|
# to build an executable on the target. Do not include
|
||||||
|
# on this list any libraries in TARGET_TCL_LIBS, TARGET_GDBM_LIBS,
|
||||||
|
# TARGET_READLINE_LIBS, etc.
|
||||||
|
#
|
||||||
|
# TARGET_EXEEXT
|
||||||
|
#
|
||||||
|
# The filename extension for executables on the
|
||||||
|
# target platform. "" for Unix and ".exe" for windows.
|
||||||
|
#
|
||||||
|
# The generated configure script will make an attempt to guess
|
||||||
|
# at all of the above parameters. You can override any of
|
||||||
|
# the guesses by setting the environment variable named
|
||||||
|
# "config_AAAA" where "AAAA" is the name of the parameter
|
||||||
|
# described above. (Exception: srcdir cannot be set this way.)
|
||||||
|
# If you have a file that sets one or more of these environment
|
||||||
|
# variables, you can invoke configure as follows:
|
||||||
|
#
|
||||||
|
# configure --with-hints=FILE
|
||||||
|
#
|
||||||
|
# where FILE is the name of the file that sets the environment
|
||||||
|
# variables. FILE should be an absolute pathname.
|
||||||
|
#
|
||||||
|
# If you have a Tcl/Tk/BLT source distribution available, then the
|
||||||
|
# files in that distribution will be used instead of any other
|
||||||
|
# Tcl/Tk/BLT files the script might discover if you tell the configure
|
||||||
|
# script about the source tree. Use commandline options:
|
||||||
|
#
|
||||||
|
# --with-tcl=PATH --with-tk=PATH --with-blt=PATH
|
||||||
|
#
|
||||||
|
# Or set environment variables config_WITH_TCL, config_WITH_TK, or
|
||||||
|
# config_WITH_BLT.
|
||||||
|
#
|
||||||
|
# This configure.in file is easy to reuse on other projects. Just
|
||||||
|
# change the argument to AC_INIT(). And disable any features that
|
||||||
|
# you don't need (for example BLT) by erasing or commenting out
|
||||||
|
# the corresponding code.
|
||||||
|
#
|
||||||
|
AC_INIT(src/sqlite.h)
|
||||||
|
|
||||||
|
dnl Put the RCS revision string after AC_INIT so that it will also
|
||||||
|
dnl show in in configure.
|
||||||
|
# The following RCS revision string applies to configure.in
|
||||||
|
# $Revision: 1.1 $
|
||||||
|
|
||||||
|
#########
|
||||||
|
# Make sure we are not building in a subdirectory of the source tree.
|
||||||
|
#
|
||||||
|
changequote(<<<,>>>)
|
||||||
|
temp=`echo $srcdir | grep '[^./]'`
|
||||||
|
changequote([,])
|
||||||
|
if test "$temp" = ""; then
|
||||||
|
AC_MSG_ERROR([
|
||||||
|
**************************************************************************
|
||||||
|
** This program may not be compiled in the same directory that contains **
|
||||||
|
** the configure script or any subdirectory of that directory. Rerun **
|
||||||
|
** the configure script from a directory that is separate from the **
|
||||||
|
** source tree. **
|
||||||
|
**************************************************************************])
|
||||||
|
fi
|
||||||
|
|
||||||
|
#########
|
||||||
|
# Set up an appropriate program prefix
|
||||||
|
#
|
||||||
|
if test "$program_prefix" = "NONE"; then
|
||||||
|
program_prefix=""
|
||||||
|
fi
|
||||||
|
AC_SUBST(program_prefix)
|
||||||
|
|
||||||
|
#########
|
||||||
|
# Check to see if the --with-hints=FILE option is used. If there is none,
|
||||||
|
# then check for a files named "$host.hints" and ../$hosts.hints where
|
||||||
|
# $host is the hostname of the build system. If still no hints are
|
||||||
|
# found, try looking in $system.hints and ../$system.hints where
|
||||||
|
# $system is the result of uname -s.
|
||||||
|
#
|
||||||
|
AC_ARG_WITH(hints,
|
||||||
|
[ --with-hints=FILE Read configuration options from FILE],
|
||||||
|
hints=$withval)
|
||||||
|
if test "$hints" = ""; then
|
||||||
|
host=`hostname | sed 's/\..*//'`
|
||||||
|
if test -r $host.hints; then
|
||||||
|
hints=$host.hints
|
||||||
|
else
|
||||||
|
if test -r ../$host.hints; then
|
||||||
|
hints=../$host.hints
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if test "$hints" = ""; then
|
||||||
|
sys=`uname -s`
|
||||||
|
if test -r $sys.hints; then
|
||||||
|
hints=$sys.hints
|
||||||
|
else
|
||||||
|
if test -r ../$sys.hints; then
|
||||||
|
hints=../$sys.hints
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if test "$hints" != ""; then
|
||||||
|
AC_MSG_RESULT(reading hints from $hints)
|
||||||
|
. $hints
|
||||||
|
fi
|
||||||
|
|
||||||
|
#########
|
||||||
|
# Locate a compiler for the build machine. This compiler should
|
||||||
|
# generate command-line programs that run on the build machine.
|
||||||
|
#
|
||||||
|
default_build_cflags="-g"
|
||||||
|
if test "$config_BUILD_CC" = ""; then
|
||||||
|
AC_PROG_CC
|
||||||
|
if test "$cross_compiling" = "yes"; then
|
||||||
|
AC_MSG_ERROR([unable to find a compiler for building build tools])
|
||||||
|
fi
|
||||||
|
BUILD_CC=$CC
|
||||||
|
default_build_cflags=$CFLAGS
|
||||||
|
else
|
||||||
|
BUILD_CC=$config_BUILD_CC
|
||||||
|
AC_MSG_CHECKING([host compiler])
|
||||||
|
CC=$BUILD_CC
|
||||||
|
AC_MSG_RESULT($BUILD_CC)
|
||||||
|
fi
|
||||||
|
AC_MSG_CHECKING([switches for the host compiler])
|
||||||
|
if test "$config_BUILD_CFLAGS" != ""; then
|
||||||
|
CFLAGS=$config_BUILD_CFLAGS
|
||||||
|
BUILD_CFLAGS=$config_BUILD_CFLAGS
|
||||||
|
else
|
||||||
|
BUILD_CFLAGS=$default_build_cflags
|
||||||
|
fi
|
||||||
|
AC_MSG_RESULT($BUILD_CFLAGS)
|
||||||
|
if test "$config_BUILD_LIBS" != ""; then
|
||||||
|
BUILD_LIBS=$config_BUILD_LIBS
|
||||||
|
fi
|
||||||
|
AC_SUBST(BUILD_CC)
|
||||||
|
AC_SUBST(BUILD_CFLAGS)
|
||||||
|
AC_SUBST(BUILD_LIBS)
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Locate a compiler that converts C code into *.o files that run on
|
||||||
|
# the target machine.
|
||||||
|
#
|
||||||
|
AC_MSG_CHECKING([target compiler])
|
||||||
|
if test "$config_TARGET_CC" != ""; then
|
||||||
|
TARGET_CC=$config_TARGET_CC
|
||||||
|
else
|
||||||
|
TARGET_CC=$BUILD_CC
|
||||||
|
fi
|
||||||
|
AC_MSG_RESULT($TARGET_CC)
|
||||||
|
AC_MSG_CHECKING([switches on the target compiler])
|
||||||
|
if test "$config_TARGET_CFLAGS" != ""; then
|
||||||
|
TARGET_CFLAGS=$config_TARGET_CFLAGS
|
||||||
|
else
|
||||||
|
TARGET_CFLAGS=$BUILD_CFLAGS
|
||||||
|
fi
|
||||||
|
AC_MSG_RESULT($TARGET_CFLAGS)
|
||||||
|
AC_MSG_CHECKING([target linker])
|
||||||
|
if test "$config_TARGET_LINK" = ""; then
|
||||||
|
TARGET_LINK=$TARGET_CC
|
||||||
|
else
|
||||||
|
TARGET_LINK=$config_TARGET_LINK
|
||||||
|
fi
|
||||||
|
AC_MSG_RESULT($TARGET_LINK)
|
||||||
|
AC_MSG_CHECKING([switches on the target compiler])
|
||||||
|
if test "$config_TARGET_TFLAGS" != ""; then
|
||||||
|
TARGET_TFLAGS=$config_TARGET_TFLAGS
|
||||||
|
else
|
||||||
|
TARGET_TFLAGS=$BUILD_CFLAGS
|
||||||
|
fi
|
||||||
|
if test "$config_TARGET_RANLIB" != ""; then
|
||||||
|
TARGET_RANLIB=$config_TARGET_RANLIB
|
||||||
|
else
|
||||||
|
AC_PROG_RANLIB
|
||||||
|
TARGET_RANLIB=$RANLIB
|
||||||
|
fi
|
||||||
|
if test "$config_TARGET_AR" != ""; then
|
||||||
|
TARGET_RANLIB=$config_TARGET_AR
|
||||||
|
else
|
||||||
|
TARGET_AR='ar cr'
|
||||||
|
fi
|
||||||
|
AC_MSG_RESULT($TARGET_TFLAGS)
|
||||||
|
AC_SUBST(TARGET_CC)
|
||||||
|
AC_SUBST(TARGET_CFLAGS)
|
||||||
|
AC_SUBST(TARGET_LINK)
|
||||||
|
AC_SUBST(TARGET_LFLAGS)
|
||||||
|
AC_SUBST(TARGET_RANLIB)
|
||||||
|
AC_SUBST(TARGET_AR)
|
||||||
|
|
||||||
|
# Set the $cross variable if we are cross-compiling. Make
|
||||||
|
# it 0 if we are not.
|
||||||
|
#
|
||||||
|
AC_MSG_CHECKING([if host and target compilers are the same])
|
||||||
|
if test "$BUILD_CC" = "$TARGET_CC"; then
|
||||||
|
cross=0
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
else
|
||||||
|
cross=1
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
fi
|
||||||
|
|
||||||
|
###########
|
||||||
|
# Lots of things are different if we are compiling for Windows using
|
||||||
|
# the CYGWIN environment. So check for that special case and handle
|
||||||
|
# things accordingly.
|
||||||
|
#
|
||||||
|
AC_MSG_CHECKING([if executables have the .exe suffix])
|
||||||
|
if test "$config_BUILD_EXEEXT" = ".exe"; then
|
||||||
|
CYGWIN=yes
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(unknown)
|
||||||
|
fi
|
||||||
|
if test "$CYGWIN" != "yes"; then
|
||||||
|
AC_CYGWIN
|
||||||
|
fi
|
||||||
|
if test "$CYGWIN" = "yes"; then
|
||||||
|
BUILD_EXEEXT=.exe
|
||||||
|
else
|
||||||
|
BUILD_EXEEXT=""
|
||||||
|
fi
|
||||||
|
if test "$cross" = "0"; then
|
||||||
|
TARGET_EXEEXT=$BUILD_EXEEXT
|
||||||
|
else
|
||||||
|
TARGET_EXEEXT=$config_TARGET_EXEEXT
|
||||||
|
fi
|
||||||
|
if test "$TARGET_EXEEXT" = ".exe"; then
|
||||||
|
OS_UNIX=0
|
||||||
|
OS_WIN=1
|
||||||
|
tclsubdir=win
|
||||||
|
else
|
||||||
|
OS_UNIX=1
|
||||||
|
OS_WIN=0
|
||||||
|
tclsubdir=unix
|
||||||
|
fi
|
||||||
|
TARGET_CFLAGS="$TARGET_CFLAGS -DOS_UNIX=$OS_UNIX -DOS_WIN=$OS_WIN"
|
||||||
|
|
||||||
|
AC_SUBST(BUILD_EXEEXT)
|
||||||
|
AC_SUBST(OS_UNIX)
|
||||||
|
AC_SUBST(OS_WIN)
|
||||||
|
AC_SUBST(TARGET_EXEEXT)
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Extract generic linker options from the environment.
|
||||||
|
#
|
||||||
|
if test "$config_TARGET_LIBS" != ""; then
|
||||||
|
TARGET_LIBS=$config_TARGET_LIBS
|
||||||
|
else
|
||||||
|
TARGET_LIBS=""
|
||||||
|
fi
|
||||||
|
AC_SUBST(TARGET_LIBS)
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Figure out what C libraries are required to compile Tcl programs.
|
||||||
|
#
|
||||||
|
if test "$config_TARGET_TCL_LIBS" != ""; then
|
||||||
|
TARGET_TCL_LIBS="$config_TARGET_TCL_LIBS"
|
||||||
|
else
|
||||||
|
if test "$with_tcl" != ""; then
|
||||||
|
extra=`echo $with_tcl/$tclsubdir/libtcl8*.a`
|
||||||
|
fi
|
||||||
|
CC=$TARGET_CC
|
||||||
|
AC_CHECK_FUNC(sin, LIBS="", LIBS="-lm")
|
||||||
|
AC_CHECK_LIB(dl, dlopen)
|
||||||
|
otherlibs=$LIBS
|
||||||
|
if test "$extra" != ""; then
|
||||||
|
LIBS=$extra
|
||||||
|
else
|
||||||
|
LIBS=""
|
||||||
|
AC_SEARCH_LIBS(Tcl_Init, dnl
|
||||||
|
tcl8.4 tcl8.3 tcl8.2 tcl8.1 tcl8.0 tcl80 tcl,,,$otherlibs)
|
||||||
|
fi
|
||||||
|
TARGET_TCL_LIBS="$LIBS $otherlibs"
|
||||||
|
fi
|
||||||
|
AC_SUBST(TARGET_TCL_LIBS)
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Figure out where to get the TCL header files.
|
||||||
|
#
|
||||||
|
AC_MSG_CHECKING([TCL header files])
|
||||||
|
found=no
|
||||||
|
if test "$config_TARGET_TCL_INC" != ""; then
|
||||||
|
TARGET_TCL_INC=$config_TARGET_TCL_INC
|
||||||
|
found=yes
|
||||||
|
else
|
||||||
|
if test "$with_tcl" != ""; then
|
||||||
|
TARGET_TCL_INC="-I$with_tcl/generic -I$with_tcl/$tclsubdir"
|
||||||
|
found=yes
|
||||||
|
else
|
||||||
|
TARGET_TCL_INC=""
|
||||||
|
found=no
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if test "$found" = "yes"; then
|
||||||
|
AC_MSG_RESULT($TARGET_TCL_INC)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(not specified: still searching...)
|
||||||
|
AC_CHECK_HEADER(tcl.h, [found=yes])
|
||||||
|
fi
|
||||||
|
if test "$found" = "no"; then
|
||||||
|
for dir in /usr/local /usr/X11* /usr/pkg /usr/contrib /usr; do
|
||||||
|
AC_CHECK_FILE($dir/include/tcl.h, found=yes)
|
||||||
|
if test "$found" = "yes"; then
|
||||||
|
TARGET_TCL_INC="-I$dir/include"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
AC_SUBST(TARGET_TCL_INC)
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Figure out what C libraries are required to compile programs
|
||||||
|
# that use GDBM.
|
||||||
|
#
|
||||||
|
if test "$config_TARGET_GDBM_LIBS" != ""; then
|
||||||
|
TARGET_GDBM_LIBS="$config_TARGET_GDBM_LIBS"
|
||||||
|
else
|
||||||
|
CC=$TARGET_CC
|
||||||
|
LIBS=""
|
||||||
|
AC_SEARCH_LIBS(gdbm_open, gdbm,,,)
|
||||||
|
TARGET_GDBM_LIBS="$LIBS"
|
||||||
|
fi
|
||||||
|
AC_SUBST(TARGET_GDBM_LIBS)
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Figure out where to get the GDBM header files.
|
||||||
|
#
|
||||||
|
AC_MSG_CHECKING([GDBM header files])
|
||||||
|
found=no
|
||||||
|
if test "$config_TARGET_GDBM_INC" != ""; then
|
||||||
|
TARGET_GDBM_INC=$config_TARGET_GDBM_INC
|
||||||
|
found=yes
|
||||||
|
fi
|
||||||
|
if test "$found" = "yes"; then
|
||||||
|
AC_MSG_RESULT($TARGET_TCL_INC)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(not specified: still searching...)
|
||||||
|
AC_CHECK_HEADER(gdbm.h, [found=yes])
|
||||||
|
fi
|
||||||
|
if test "$found" = "no"; then
|
||||||
|
for dir in /usr/local /usr/pkg /usr/contrib; do
|
||||||
|
AC_CHECK_FILE($dir/include/gdbm.h, found=yes)
|
||||||
|
if test "$found" = "yes"; then
|
||||||
|
TARGET_GDBM_INC="-I$dir/include"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
AC_SUBST(TARGET_GDBM_INC)
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Figure out what C libraries are required to compile programs
|
||||||
|
# that use "readline()" library.
|
||||||
|
#
|
||||||
|
if test "$config_TARGET_READLINE_LIBS" != ""; then
|
||||||
|
TARGET_READLINE_LIBS="$config_TARGET_READLINE_LIBS"
|
||||||
|
else
|
||||||
|
CC=$TARGET_CC
|
||||||
|
LIBS=""
|
||||||
|
AC_SEARCH_LIBS(readline, readline,,,)
|
||||||
|
TARGET_READLINE_LIBS="$LIBS"
|
||||||
|
fi
|
||||||
|
AC_SUBST(TARGET_READLINE_LIBS)
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Figure out where to get the READLINE header files.
|
||||||
|
#
|
||||||
|
AC_MSG_CHECKING([readline header files])
|
||||||
|
found=no
|
||||||
|
if test "$config_TARGET_READLINE_INC" != ""; then
|
||||||
|
TARGET_READLINE_INC=$config_TARGET_READLINE_INC
|
||||||
|
found=yes
|
||||||
|
fi
|
||||||
|
if test "$found" = "yes"; then
|
||||||
|
AC_MSG_RESULT($TARGET_READLINE_INC)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(not specified: still searching...)
|
||||||
|
AC_CHECK_HEADER(readline.h, [found=yes])
|
||||||
|
fi
|
||||||
|
if test "$found" = "no"; then
|
||||||
|
for dir in /usr /usr/local /usr/local/readline /usr/contrib; do
|
||||||
|
AC_CHECK_FILE($dir/include/readline.h, found=yes)
|
||||||
|
if test "$found" = "yes"; then
|
||||||
|
TARGET_READLINE_INC="-I$dir/include"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
AC_CHECK_FILE($dir/include/readline/readline.h, found=yes)
|
||||||
|
if test "$found" = "yes"; then
|
||||||
|
TARGET_READLINE_INC="-I$dir/include/readline"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if test "$found" = "yes"; then
|
||||||
|
TARGET_HAVE_READLINE=1
|
||||||
|
else
|
||||||
|
TARGET_HAVE_READLINE=0
|
||||||
|
fi
|
||||||
|
AC_SUBST(TARGET_READLINE_INC)
|
||||||
|
AC_SUBST(TARGET_HAVE_READLINE)
|
||||||
|
|
||||||
|
#########
|
||||||
|
# Generate the output files.
|
||||||
|
#
|
||||||
|
AC_OUTPUT(Makefile)
|
861
doc/lemon.html
Normal file
861
doc/lemon.html
Normal file
@ -0,0 +1,861 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>The Lemon Parser Generator</title>
|
||||||
|
</head>
|
||||||
|
<body bgcolor=white>
|
||||||
|
<h1 align=center>The Lemon Parser Generator</h1>
|
||||||
|
|
||||||
|
<p>Lemon is an LALR(1) parser generator for C or C++.
|
||||||
|
It does the same job as ``bison'' and ``yacc''.
|
||||||
|
But lemon is not another bison or yacc clone. It
|
||||||
|
uses a different grammar syntax which is designed to
|
||||||
|
reduce the number of coding errors. Lemon also uses a more
|
||||||
|
sophisticated parsing engine that is faster than yacc and
|
||||||
|
bison and which is both reentrant and thread-safe.
|
||||||
|
Furthermore, Lemon implements features that can be used
|
||||||
|
to eliminate resource leaks, making is suitable for use
|
||||||
|
in long-running programs such as graphical user interfaces
|
||||||
|
or embedded controllers.</p>
|
||||||
|
|
||||||
|
<p>This document is an introduction to the Lemon
|
||||||
|
parser generator.</p>
|
||||||
|
|
||||||
|
<h2>Theory of Operation</h2>
|
||||||
|
|
||||||
|
<p>The main goal of Lemon is to translate a context free grammar (CFG)
|
||||||
|
for a particular language into C code that implements a parser for
|
||||||
|
that language.
|
||||||
|
The program has two inputs:
|
||||||
|
<ul>
|
||||||
|
<li>The grammar specification.
|
||||||
|
<li>A parser template file.
|
||||||
|
</ul>
|
||||||
|
Typically, only the grammar specification is supplied by the programmer.
|
||||||
|
Lemon comes with a default parser template which works fine for most
|
||||||
|
applications. But the user is free to substitute a different parser
|
||||||
|
template if desired.</p>
|
||||||
|
|
||||||
|
<p>Depending on command-line options, Lemon will generate between
|
||||||
|
one and three files of outputs.
|
||||||
|
<ul>
|
||||||
|
<li>C code to implement the parser.
|
||||||
|
<li>A header file defining an integer ID for each terminal symbol.
|
||||||
|
<li>An information file that describes the states of the generated parser
|
||||||
|
automaton.
|
||||||
|
</ul>
|
||||||
|
By default, all three of these output files are generated.
|
||||||
|
The header file is suppressed if the ``-m'' command-line option is
|
||||||
|
used and the report file is omitted when ``-q'' is selected.</p>
|
||||||
|
|
||||||
|
<p>The grammar specification file uses a ``.y'' suffix, by convention.
|
||||||
|
In the examples used in this document, we'll assume the name of the
|
||||||
|
grammar file is ``gram.y''. A typical use of Lemon would be the
|
||||||
|
following command:
|
||||||
|
<pre>
|
||||||
|
lemon gram.y
|
||||||
|
</pre>
|
||||||
|
This command will generate three output files named ``gram.c'',
|
||||||
|
``gram.h'' and ``gram.out''.
|
||||||
|
The first is C code to implement the parser. The second
|
||||||
|
is the header file that defines numerical values for all
|
||||||
|
terminal symbols, and the last is the report that explains
|
||||||
|
the states used by the parser automaton.</p>
|
||||||
|
|
||||||
|
<h3>Command Line Options</h3>
|
||||||
|
|
||||||
|
<p>The behavior of Lemon can be modified using command-line options.
|
||||||
|
You can obtain a list of the available command-line options together
|
||||||
|
with a brief explanation of what each does by typing
|
||||||
|
<pre>
|
||||||
|
lemon -?
|
||||||
|
</pre>
|
||||||
|
As of this writing, the following command-line options are supported:
|
||||||
|
<ul>
|
||||||
|
<li><tt>-b</tt>
|
||||||
|
<li><tt>-c</tt>
|
||||||
|
<li><tt>-g</tt>
|
||||||
|
<li><tt>-m</tt>
|
||||||
|
<li><tt>-q</tt>
|
||||||
|
<li><tt>-s</tt>
|
||||||
|
<li><tt>-x</tt>
|
||||||
|
</ul>
|
||||||
|
The ``-b'' option reduces the amount of text in the report file by
|
||||||
|
printing only the basis of each parser state, rather than the full
|
||||||
|
configuration.
|
||||||
|
The ``-c'' option suppresses action table compression. Using -c
|
||||||
|
will make the parser a little larger and slower but it will detect
|
||||||
|
syntax errors sooner.
|
||||||
|
The ``-g'' option causes no output files to be generated at all.
|
||||||
|
Instead, the input grammar file is printed on standard output but
|
||||||
|
with all comments, actions and other extraneous text deleted. This
|
||||||
|
is a useful way to get a quick summary of a grammar.
|
||||||
|
The ``-m'' option causes the output C source file to be compatible
|
||||||
|
with the ``makeheaders'' program.
|
||||||
|
Makeheaders is a program that automatically generates header files
|
||||||
|
from C source code. When the ``-m'' option is used, the header
|
||||||
|
file is not output since the makeheaders program will take care
|
||||||
|
of generated all header files automatically.
|
||||||
|
The ``-q'' option suppresses the report file.
|
||||||
|
Using ``-s'' causes a brief summary of parser statistics to be
|
||||||
|
printed. Like this:
|
||||||
|
<pre>
|
||||||
|
Parser statistics: 74 terminals, 70 nonterminals, 179 rules
|
||||||
|
340 states, 2026 parser table entries, 0 conflicts
|
||||||
|
</pre>
|
||||||
|
Finally, the ``-x'' option causes Lemon to print its version number
|
||||||
|
and copyright information
|
||||||
|
and then stop without attempting to read the grammar or generate a parser.</p>
|
||||||
|
|
||||||
|
<h3>The Parser Interface</h3>
|
||||||
|
|
||||||
|
<p>Lemon doesn't generate a complete, working program. It only generates
|
||||||
|
a few subroutines that implement a parser. This section describes
|
||||||
|
the interface to those subroutines. It is up to the programmer to
|
||||||
|
call these subroutines in an appropriate way in order to produce a
|
||||||
|
complete system.</p>
|
||||||
|
|
||||||
|
<p>Before a program begins using a Lemon-generated parser, the program
|
||||||
|
must first create the parser.
|
||||||
|
A new parser is created as follows:
|
||||||
|
<pre>
|
||||||
|
void *pParser = ParseAlloc( malloc );
|
||||||
|
</pre>
|
||||||
|
The ParseAlloc() routine allocates and initializes a new parser and
|
||||||
|
returns a pointer to it.
|
||||||
|
The actual data structure used to represent a parser is opaque --
|
||||||
|
its internal structure is not visible or usable by the calling routine.
|
||||||
|
For this reason, the ParseAlloc() routine returns a pointer to void
|
||||||
|
rather than a pointer to some particular structure.
|
||||||
|
The sole argument to the ParseAlloc() routine is a pointer to the
|
||||||
|
subroutine used to allocate memory. Typically this means ``malloc()''.</p>
|
||||||
|
|
||||||
|
<p>After a program is finished using a parser, it can reclaim all
|
||||||
|
memory allocated by that parser by calling
|
||||||
|
<pre>
|
||||||
|
ParseFree(pParser, free);
|
||||||
|
</pre>
|
||||||
|
The first argument is the same pointer returned by ParseAlloc(). The
|
||||||
|
second argument is a pointer to the function used to release bulk
|
||||||
|
memory back to the system.</p>
|
||||||
|
|
||||||
|
<p>After a parser has been allocated using ParseAlloc(), the programmer
|
||||||
|
must supply the parser with a sequence of tokens (terminal symbols) to
|
||||||
|
be parsed. This is accomplished by calling the following function
|
||||||
|
once for each token:
|
||||||
|
<pre>
|
||||||
|
Parse(pParser, hTokenID, sTokenData, pArg);
|
||||||
|
</pre>
|
||||||
|
The first argument to the Parse() routine is the pointer returned by
|
||||||
|
ParseAlloc().
|
||||||
|
The second argument is a small positive integer that tells the parse the
|
||||||
|
type of the next token in the data stream.
|
||||||
|
There is one token type for each terminal symbol in the grammar.
|
||||||
|
The gram.h file generated by Lemon contains #define statements that
|
||||||
|
map symbolic terminal symbol names into appropriate integer values.
|
||||||
|
(A value of 0 for the second argument is a special flag to the
|
||||||
|
parser to indicate that the end of input has been reached.)
|
||||||
|
The third argument is the value of the given token. By default,
|
||||||
|
the type of the third argument is integer, but the grammar will
|
||||||
|
usually redefine this type to be some kind of structure.
|
||||||
|
Typically the second argument will be a broad category of tokens
|
||||||
|
such as ``identifier'' or ``number'' and the third argument will
|
||||||
|
be the name of the identifier or the value of the number.</p>
|
||||||
|
|
||||||
|
<p>The Parse() function may have either three or four arguments,
|
||||||
|
depending on the grammar. If the grammar specification file request
|
||||||
|
it, the Parse() function will have a fourth parameter that can be
|
||||||
|
of any type chosen by the programmer. The parser doesn't do anything
|
||||||
|
with this argument except to pass it through to action routines.
|
||||||
|
This is a convenient mechanism for passing state information down
|
||||||
|
to the action routines without having to use global variables.</p>
|
||||||
|
|
||||||
|
<p>A typical use of a Lemon parser might look something like the
|
||||||
|
following:
|
||||||
|
<pre>
|
||||||
|
01 ParseTree *ParseFile(const char *zFilename){
|
||||||
|
02 Tokenizer *pTokenizer;
|
||||||
|
03 void *pParser;
|
||||||
|
04 Token sToken;
|
||||||
|
05 int hTokenId;
|
||||||
|
06 ParserState sState;
|
||||||
|
07
|
||||||
|
08 pTokenizer = TokenizerCreate(zFilename);
|
||||||
|
09 pParser = ParseAlloc( malloc );
|
||||||
|
10 InitParserState(&sState);
|
||||||
|
11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
|
||||||
|
12 Parse(pParser, hTokenId, sToken, &sState);
|
||||||
|
13 }
|
||||||
|
14 Parse(pParser, 0, sToken, &sState);
|
||||||
|
15 ParseFree(pParser, free );
|
||||||
|
16 TokenizerFree(pTokenizer);
|
||||||
|
17 return sState.treeRoot;
|
||||||
|
18 }
|
||||||
|
</pre>
|
||||||
|
This example shows a user-written routine that parses a file of
|
||||||
|
text and returns a pointer to the parse tree.
|
||||||
|
(We've omitted all error-handling from this example to keep it
|
||||||
|
simple.)
|
||||||
|
We assume the existence of some kind of tokenizer which is created
|
||||||
|
using TokenizerCreate() on line 8 and deleted by TokenizerFree()
|
||||||
|
on line 16. The GetNextToken() function on line 11 retrieves the
|
||||||
|
next token from the input file and puts its type in the
|
||||||
|
integer variable hTokenId. The sToken variable is assumed to be
|
||||||
|
some kind of structure that contains details about each token,
|
||||||
|
such as its complete text, what line it occurs on, etc. </p>
|
||||||
|
|
||||||
|
<p>This example also assumes the existence of structure of type
|
||||||
|
ParserState that holds state information about a particular parse.
|
||||||
|
An instance of such a structure is created on line 6 and initialized
|
||||||
|
on line 10. A pointer to this structure is passed into the Parse()
|
||||||
|
routine as the optional 4th argument.
|
||||||
|
The action routine specified by the grammar for the parser can use
|
||||||
|
the ParserState structure to hold whatever information is useful and
|
||||||
|
appropriate. In the example, we note that the treeRoot field of
|
||||||
|
the ParserState structure is left pointing to the root of the parse
|
||||||
|
tree.</p>
|
||||||
|
|
||||||
|
<p>The core of this example as it relates to Lemon is as follows:
|
||||||
|
<pre>
|
||||||
|
ParseFile(){
|
||||||
|
pParser = ParseAlloc( malloc );
|
||||||
|
while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
|
||||||
|
Parse(pParser, hTokenId, sToken);
|
||||||
|
}
|
||||||
|
Parse(pParser, 0, sToken);
|
||||||
|
ParseFree(pParser, free );
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
Basically, what a program has to do to use a Lemon-generated parser
|
||||||
|
is first create the parser, then send it lots of tokens obtained by
|
||||||
|
tokenizing an input source. When the end of input is reached, the
|
||||||
|
Parse() routine should be called one last time with a token type
|
||||||
|
of 0. This step is necessary to inform the parser that the end of
|
||||||
|
input has been reached. Finally, we reclaim memory used by the
|
||||||
|
parser by calling ParseFree().</p>
|
||||||
|
|
||||||
|
<p>There is one other interface routine that should be mentioned
|
||||||
|
before we move on.
|
||||||
|
The ParseTrace() function can be used to generate debugging output
|
||||||
|
from the parser. A prototype for this routine is as follows:
|
||||||
|
<pre>
|
||||||
|
ParseTrace(FILE *stream, char *zPrefix);
|
||||||
|
</pre>
|
||||||
|
After this routine is called, a short (one-line) message is written
|
||||||
|
to the designated output stream every time the parser changes states
|
||||||
|
or calls an action routine. Each such message is prefaced using
|
||||||
|
the text given by zPrefix. This debugging output can be turned off
|
||||||
|
by calling ParseTrace() again with a first argument of NULL (0).</p>
|
||||||
|
|
||||||
|
<h3>Differences With YACC and BISON</h3>
|
||||||
|
|
||||||
|
<p>Programmers who have previously used the yacc or bison parser
|
||||||
|
generator will notice several important differences between yacc and/or
|
||||||
|
bison and Lemon.
|
||||||
|
<ul>
|
||||||
|
<li>In yacc and bison, the parser calls the tokenizer. In Lemon,
|
||||||
|
the tokenizer calls the parser.
|
||||||
|
<li>Lemon uses no global variables. Yacc and bison use global variables
|
||||||
|
to pass information between the tokenizer and parser.
|
||||||
|
<li>Lemon allows multiple parsers to be running simultaneously. Yacc
|
||||||
|
and bison do not.
|
||||||
|
</ul>
|
||||||
|
These differences may cause some initial confusion for programmers
|
||||||
|
with prior yacc and bison experience.
|
||||||
|
But after years of experience using Lemon, I firmly
|
||||||
|
believe that the Lemon way of doing things is better.</p>
|
||||||
|
|
||||||
|
<h2>Input File Syntax</h2>
|
||||||
|
|
||||||
|
<p>The main purpose of the grammar specification file for Lemon is
|
||||||
|
to define the grammar for the parser. But the input file also
|
||||||
|
specifies additional information Lemon requires to do its job.
|
||||||
|
Most of the work in using Lemon is in writing an appropriate
|
||||||
|
grammar file.</p>
|
||||||
|
|
||||||
|
<p>The grammar file for lemon is, for the most part, free format.
|
||||||
|
It does not have sections or divisions like yacc or bison. Any
|
||||||
|
declaration can occur at any point in the file.
|
||||||
|
Lemon ignores whitespace (except where it is needed to separate
|
||||||
|
tokens) and it honors the same commenting conventions as C and C++.</p>
|
||||||
|
|
||||||
|
<h3>Terminals and Nonterminals</h3>
|
||||||
|
|
||||||
|
<p>A terminal symbol (token) is any string of alphanumeric
|
||||||
|
and underscore characters
|
||||||
|
that begins with an upper case letter.
|
||||||
|
A terminal can contain lower class letters after the first character,
|
||||||
|
but the usual convention is to make terminals all upper case.
|
||||||
|
A nonterminal, on the other hand, is any string of alphanumeric
|
||||||
|
and underscore characters than begins with a lower case letter.
|
||||||
|
Again, the usual convention is to make nonterminals use all lower
|
||||||
|
case letters.</p>
|
||||||
|
|
||||||
|
<p>In Lemon, terminal and nonterminal symbols do not need to
|
||||||
|
be declared or identified in a separate section of the grammar file.
|
||||||
|
Lemon is able to generate a list of all terminals and nonterminals
|
||||||
|
by examining the grammar rules, and it can always distinguish a
|
||||||
|
terminal from a nonterminal by checking the case of the first
|
||||||
|
character of the name.</p>
|
||||||
|
|
||||||
|
<p>Yacc and bison allow terminal symbols to have either alphanumeric
|
||||||
|
names or to be individual characters included in single quotes, like
|
||||||
|
this: ')' or '$'. Lemon does not allow this alternative form for
|
||||||
|
terminal symbols. With Lemon, all symbols, terminals and nonterminals,
|
||||||
|
must have alphanumeric names.</p>
|
||||||
|
|
||||||
|
<h3>Grammar Rules</h3>
|
||||||
|
|
||||||
|
<p>The main component of a Lemon grammar file is a sequence of grammar
|
||||||
|
rules.
|
||||||
|
Each grammar rule consists of a nonterminal symbol followed by
|
||||||
|
the special symbol ``::='' and then a list of terminals and/or nonterminals.
|
||||||
|
The rule is terminated by a period.
|
||||||
|
The list of terminals and nonterminals on the right-hand side of the
|
||||||
|
rule can be empty.
|
||||||
|
Rules can occur in any order, except that the left-hand side of the
|
||||||
|
first rule is assumed to be the start symbol for the grammar (unless
|
||||||
|
specified otherwise using the <tt>%start</tt> directive described below.)
|
||||||
|
A typical sequence of grammar rules might look something like this:
|
||||||
|
<pre>
|
||||||
|
expr ::= expr PLUS expr.
|
||||||
|
expr ::= expr TIMES expr.
|
||||||
|
expr ::= LPAREN expr RPAREN.
|
||||||
|
expr ::= VALUE.
|
||||||
|
</pre>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>There is one non-terminal in this example, ``expr'', and five
|
||||||
|
terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'',
|
||||||
|
``RPAREN'' and ``VALUE''.</p>
|
||||||
|
|
||||||
|
<p>Like yacc and bison, Lemon allows the grammar to specify a block
|
||||||
|
of C code that will be executed whenever a grammar rule is reduced
|
||||||
|
by the parser.
|
||||||
|
In Lemon, this action is specified by putting the C code (contained
|
||||||
|
within curly braces <tt>{...}</tt>) immediately after the
|
||||||
|
period that closes the rule.
|
||||||
|
For example:
|
||||||
|
<pre>
|
||||||
|
expr ::= expr PLUS expr. { printf("Doing an addition...\n"); }
|
||||||
|
</pre>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>In order to be useful, grammar actions must normally be linked to
|
||||||
|
their associated grammar rules.
|
||||||
|
In yacc and bison, this is accomplished by embedding a ``$$'' in the
|
||||||
|
action to stand for the value of the left-hand side of the rule and
|
||||||
|
symbols ``$1'', ``$2'', and so forth to stand for the value of
|
||||||
|
the terminal or nonterminal at position 1, 2 and so forth on the
|
||||||
|
right-hand side of the rule.
|
||||||
|
This idea is very powerful, but it is also very error-prone. The
|
||||||
|
single most common source of errors in a yacc or bison grammar is
|
||||||
|
to miscount the number of symbols on the right-hand side of a grammar
|
||||||
|
rule and say ``$7'' when you really mean ``$8''.</p>
|
||||||
|
|
||||||
|
<p>Lemon avoids the need to count grammar symbols by assigning symbolic
|
||||||
|
names to each symbol in a grammar rule and then using those symbolic
|
||||||
|
names in the action.
|
||||||
|
In yacc or bison, one would write this:
|
||||||
|
<pre>
|
||||||
|
expr -> expr PLUS expr { $$ = $1 + $3; };
|
||||||
|
</pre>
|
||||||
|
But in Lemon, the same rule becomes the following:
|
||||||
|
<pre>
|
||||||
|
expr(A) ::= expr(B) PLUS expr(C). { A = B+C; }
|
||||||
|
</pre>
|
||||||
|
In the Lemon rule, any symbol in parentheses after a grammar rule
|
||||||
|
symbol becomes a place holder for that symbol in the grammar rule.
|
||||||
|
This place holder can then be used in the associated C action to
|
||||||
|
stand for the value of that symbol.<p>
|
||||||
|
|
||||||
|
<p>The Lemon notation for linking a grammar rule with its reduce
|
||||||
|
action is superior to yacc/bison on several counts.
|
||||||
|
First, as mentioned above, the Lemon method avoids the need to
|
||||||
|
count grammar symbols.
|
||||||
|
Secondly, if a terminal or nonterminal in a Lemon grammar rule
|
||||||
|
includes a linking symbol in parentheses but that linking symbol
|
||||||
|
is not actually used in the reduce action, then an error message
|
||||||
|
is generated.
|
||||||
|
For example, the rule
|
||||||
|
<pre>
|
||||||
|
expr(A) ::= expr(B) PLUS expr(C). { A = B; }
|
||||||
|
</pre>
|
||||||
|
will generate an error because the linking symbol ``C'' is used
|
||||||
|
in the grammar rule but not in the reduce action.</p>
|
||||||
|
|
||||||
|
<p>The Lemon notation for linking grammar rules to reduce actions
|
||||||
|
also facilitates the use of destructors for reclaiming memory
|
||||||
|
allocated by the values of terminals and nonterminals on the
|
||||||
|
right-hand side of a rule.</p>
|
||||||
|
|
||||||
|
<h3>Precedence Rules</h3>
|
||||||
|
|
||||||
|
<p>Lemon resolves parsing ambiguities in exactly the same way as
|
||||||
|
yacc and bison. A shift-reduce conflict is resolved in favor
|
||||||
|
of the shift, and a reduce-reduce conflict is resolved by reducing
|
||||||
|
whichever rule comes first in the grammar file.</p>
|
||||||
|
|
||||||
|
<p>Just like in
|
||||||
|
yacc and bison, Lemon allows a measure of control
|
||||||
|
over the resolution of paring conflicts using precedence rules.
|
||||||
|
A precedence value can be assigned to any terminal symbol
|
||||||
|
using the %left, %right or %nonassoc directives. Terminal symbols
|
||||||
|
mentioned in earlier directives have a lower precedence that
|
||||||
|
terminal symbols mentioned in later directives. For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%left AND.
|
||||||
|
%left OR.
|
||||||
|
%nonassoc EQ NE GT GE LT LE.
|
||||||
|
%left PLUS MINUS.
|
||||||
|
%left TIMES DIVIDE MOD.
|
||||||
|
%right EXP NOT.
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>In the preceding sequence of directives, the AND operator is
|
||||||
|
defined to have the lowest precedence. The OR operator is one
|
||||||
|
precedence level higher. And so forth. Hence, the grammar would
|
||||||
|
attempt to group the ambiguous expression
|
||||||
|
<pre>
|
||||||
|
a AND b OR c
|
||||||
|
</pre>
|
||||||
|
like this
|
||||||
|
<pre>
|
||||||
|
a AND (b OR c).
|
||||||
|
</pre>
|
||||||
|
The associativity (left, right or nonassoc) is used to determine
|
||||||
|
the grouping when the precedence is the same. AND is left-associative
|
||||||
|
in our example, so
|
||||||
|
<pre>
|
||||||
|
a AND b AND c
|
||||||
|
</pre>
|
||||||
|
is parsed like this
|
||||||
|
<pre>
|
||||||
|
(a AND b) AND c.
|
||||||
|
</pre>
|
||||||
|
The EXP operator is right-associative, though, so
|
||||||
|
<pre>
|
||||||
|
a EXP b EXP c
|
||||||
|
</pre>
|
||||||
|
is parsed like this
|
||||||
|
<pre>
|
||||||
|
a EXP (b EXP c).
|
||||||
|
</pre>
|
||||||
|
The nonassoc precedence is used for non-associative operators.
|
||||||
|
So
|
||||||
|
<pre>
|
||||||
|
a EQ b EQ c
|
||||||
|
</pre>
|
||||||
|
is an error.</p>
|
||||||
|
|
||||||
|
<p>The precedence of non-terminals is transferred to rules as follows:
|
||||||
|
The precedence of a grammar rule is equal to the precedence of the
|
||||||
|
left-most terminal symbol in the rule for which a precedence is
|
||||||
|
defined. This is normally what you want, but in those cases where
|
||||||
|
you want to precedence of a grammar rule to be something different,
|
||||||
|
you can specify an alternative precedence symbol by putting the
|
||||||
|
symbol in square braces after the period at the end of the rule and
|
||||||
|
before any C-code. For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
expr = MINUS expr. [NOT]
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>This rule has a precedence equal to that of the NOT symbol, not the
|
||||||
|
MINUS symbol as would have been the case by default.</p>
|
||||||
|
|
||||||
|
<p>With the knowledge of how precedence is assigned to terminal
|
||||||
|
symbols and individual
|
||||||
|
grammar rules, we can now explain precisely how parsing conflicts
|
||||||
|
are resolved in Lemon. Shift-reduce conflicts are resolved
|
||||||
|
as follows:
|
||||||
|
<ul>
|
||||||
|
<li> If either the token to be shifted or the rule to be reduced
|
||||||
|
lacks precedence information, then resolve in favor of the
|
||||||
|
shift, but report a parsing conflict.
|
||||||
|
<li> If the precedence of the token to be shifted is greater than
|
||||||
|
the precedence of the rule to reduce, then resolve in favor
|
||||||
|
of the shift. No parsing conflict is reported.
|
||||||
|
<li> If the precedence of the token it be shifted is less than the
|
||||||
|
precedence of the rule to reduce, then resolve in favor of the
|
||||||
|
reduce action. No parsing conflict is reported.
|
||||||
|
<li> If the precedences are the same and the shift token is
|
||||||
|
right-associative, then resolve in favor of the shift.
|
||||||
|
No parsing conflict is reported.
|
||||||
|
<li> If the precedences are the same the the shift token is
|
||||||
|
left-associative, then resolve in favor of the reduce.
|
||||||
|
No parsing conflict is reported.
|
||||||
|
<li> Otherwise, resolve the conflict by doing the shift and
|
||||||
|
report the parsing conflict.
|
||||||
|
</ul>
|
||||||
|
Reduce-reduce conflicts are resolved this way:
|
||||||
|
<ul>
|
||||||
|
<li> If either reduce rule
|
||||||
|
lacks precedence information, then resolve in favor of the
|
||||||
|
rule that appears first in the grammar and report a parsing
|
||||||
|
conflict.
|
||||||
|
<li> If both rules have precedence and the precedence is different
|
||||||
|
then resolve the dispute in favor of the rule with the highest
|
||||||
|
precedence and do not report a conflict.
|
||||||
|
<li> Otherwise, resolve the conflict by reducing by the rule that
|
||||||
|
appears first in the grammar and report a parsing conflict.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Special Directives</h3>
|
||||||
|
|
||||||
|
<p>The input grammar to Lemon consists of grammar rules and special
|
||||||
|
directives. We've described all the grammar rules, so now we'll
|
||||||
|
talk about the special directives.</p>
|
||||||
|
|
||||||
|
<p>Directives in lemon can occur in any order. You can put them before
|
||||||
|
the grammar rules, or after the grammar rules, or in the mist of the
|
||||||
|
grammar rules. It doesn't matter. The relative order of
|
||||||
|
directives used to assign precedence to terminals is important, but
|
||||||
|
other than that, the order of directives in Lemon is arbitrary.</p>
|
||||||
|
|
||||||
|
<p>Lemon supports the following special directives:
|
||||||
|
<ul>
|
||||||
|
<li><tt>%destructor</tt>
|
||||||
|
<li><tt>%extra_argument</tt>
|
||||||
|
<li><tt>%include</tt>
|
||||||
|
<li><tt>%left</tt>
|
||||||
|
<li><tt>%name</tt>
|
||||||
|
<li><tt>%nonassoc</tt>
|
||||||
|
<li><tt>%parse_accept</tt>
|
||||||
|
<li><tt>%parse_failure </tt>
|
||||||
|
<li><tt>%right</tt>
|
||||||
|
<li><tt>%stack_overflow</tt>
|
||||||
|
<li><tt>%stack_size</tt>
|
||||||
|
<li><tt>%start_symbol</tt>
|
||||||
|
<li><tt>%syntax_error</tt>
|
||||||
|
<li><tt>%token_destructor</tt>
|
||||||
|
<li><tt>%token_prefix</tt>
|
||||||
|
<li><tt>%token_type</tt>
|
||||||
|
<li><tt>%type</tt>
|
||||||
|
</ul>
|
||||||
|
Each of these directives will be described separately in the
|
||||||
|
following sections:</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%destructor</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %destructor directive is used to specify a destructor for
|
||||||
|
a non-terminal symbol.
|
||||||
|
(See also the %token_destructor directive which is used to
|
||||||
|
specify a destructor for terminal symbols.)</p>
|
||||||
|
|
||||||
|
<p>A non-terminal's destructor is called to dispose of the
|
||||||
|
non-terminal's value whenever the non-terminal is popped from
|
||||||
|
the stack. This includes all of the following circumstances:
|
||||||
|
<ul>
|
||||||
|
<li> When a rule reduces and the value of a non-terminal on
|
||||||
|
the right-hand side is not linked to C code.
|
||||||
|
<li> When the stack is popped during error processing.
|
||||||
|
<li> When the ParseFree() function runs.
|
||||||
|
</ul>
|
||||||
|
The destructor can do whatever it wants with the value of
|
||||||
|
the non-terminal, but its design is to deallocate memory
|
||||||
|
or other resources held by that non-terminal.</p>
|
||||||
|
|
||||||
|
<p>Consider an example:
|
||||||
|
<pre>
|
||||||
|
%type nt {void*}
|
||||||
|
%destructor nt { free($$); }
|
||||||
|
nt(A) ::= ID NUM. { A = malloc( 100 ); }
|
||||||
|
</pre>
|
||||||
|
This example is a bit contrived but it serves to illustrate how
|
||||||
|
destructors work. The example shows a non-terminal named
|
||||||
|
``nt'' that holds values of type ``void*''. When the rule for
|
||||||
|
an ``nt'' reduces, it sets the value of the non-terminal to
|
||||||
|
space obtained from malloc(). Later, when the nt non-terminal
|
||||||
|
is popped from the stack, the destructor will fire and call
|
||||||
|
free() on this malloced space, thus avoiding a memory leak.
|
||||||
|
(Note that the symbol ``$$'' in the destructor code is replaced
|
||||||
|
by the value of the non-terminal.)</p>
|
||||||
|
|
||||||
|
<p>It is important to note that the value of a non-terminal is passed
|
||||||
|
to the destructor whenever the non-terminal is removed from the
|
||||||
|
stack, unless the non-terminal is used in a C-code action. If
|
||||||
|
the non-terminal is used by C-code, then it is assumed that the
|
||||||
|
C-code will take care of destroying it if it should really
|
||||||
|
be destroyed. More commonly, the value is used to build some
|
||||||
|
larger structure and we don't want to destroy it, which is why
|
||||||
|
the destructor is not called in this circumstance.</p>
|
||||||
|
|
||||||
|
<p>By appropriate use of destructors, it is possible to
|
||||||
|
build a parser using Lemon that can be used within a long-running
|
||||||
|
program, such as a GUI, that will not leak memory or other resources.
|
||||||
|
To do the same using yacc or bison is much more difficult.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%extra_argument</tt> directive</h4>
|
||||||
|
|
||||||
|
The %extra_argument directive instructs Lemon to add a 4th parameter
|
||||||
|
to the parameter list of the Parse() function it generates. Lemon
|
||||||
|
doesn't do anything itself with this extra argument, but it does
|
||||||
|
make the argument available to C-code action routines, destructors,
|
||||||
|
and so forth. For example, if the grammar file contains:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%extra_argument { MyStruct *pAbc }
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>Then the Parse() function generated will have an 4th parameter
|
||||||
|
of type ``MyStruct*'' and all action routines will have access to
|
||||||
|
a variable named ``pAbc'' that is the value of the 4th parameter
|
||||||
|
in the most recent call to Parse().</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%include</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %include directive specifies C code that is included at the
|
||||||
|
top of the generated parser. You can include any text you want --
|
||||||
|
the Lemon parser generator copies to blindly. If you have multiple
|
||||||
|
%include directives in your grammar file, their values are concatenated
|
||||||
|
before being put at the beginning of the generated parser.</p>
|
||||||
|
|
||||||
|
<p>The %include directive is very handy for getting some extra #include
|
||||||
|
preprocessor statements at the beginning of the generated parser.
|
||||||
|
For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%include {#include <unistd.h>}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>This might be needed, for example, if some of the C actions in the
|
||||||
|
grammar call functions that are prototyed in unistd.h.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%left</tt> directive</h4>
|
||||||
|
|
||||||
|
The %left directive is used (along with the %right and
|
||||||
|
%nonassoc directives) to declare precedences of terminal
|
||||||
|
symbols. Every terminal symbol whose name appears after
|
||||||
|
a %left directive but before the next period (``.'') is
|
||||||
|
given the same left-associative precedence value. Subsequent
|
||||||
|
%left directives have higher precedence. For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%left AND.
|
||||||
|
%left OR.
|
||||||
|
%nonassoc EQ NE GT GE LT LE.
|
||||||
|
%left PLUS MINUS.
|
||||||
|
%left TIMES DIVIDE MOD.
|
||||||
|
%right EXP NOT.
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>Note the period that terminates each %left, %right or %nonassoc
|
||||||
|
directive.</p>
|
||||||
|
|
||||||
|
<p>LALR(1) grammars can get into a situation where they require
|
||||||
|
a large amount of stack space if you make heavy use or right-associative
|
||||||
|
operators. For this reason, it is recommended that you use %left
|
||||||
|
rather than %right whenever possible.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%name</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>By default, the functions generated by Lemon all begin with the
|
||||||
|
five-character string ``Parse''. You can change this string to something
|
||||||
|
different using the %name directive. For instance:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%name Abcde
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>Putting this directive in the grammar file will cause Lemon to generate
|
||||||
|
functions named
|
||||||
|
<ul>
|
||||||
|
<li> AbcdeAlloc(),
|
||||||
|
<li> AbcdeFree(),
|
||||||
|
<li> AbcdeTrace(), and
|
||||||
|
<li> Abcde().
|
||||||
|
</ul>
|
||||||
|
The %name directive allows you to generator two or more different
|
||||||
|
parsers and link them all into the same executable.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%nonassoc</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>This directive is used to assign non-associative precedence to
|
||||||
|
one or more terminal symbols. See the section on precedence rules
|
||||||
|
or on the %left directive for additional information.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%parse_accept</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %parse_accept directive specifies a block of C code that is
|
||||||
|
executed whenever the parser accepts its input string. To ``accept''
|
||||||
|
an input string means that the parser was able to process all tokens
|
||||||
|
without error.</p>
|
||||||
|
|
||||||
|
<p>For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%parse_accept {
|
||||||
|
printf("parsing complete!\n");
|
||||||
|
}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h4>The <tt>%parse_failure</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %parse_failure directive specifies a block of C code that
|
||||||
|
is executed whenever the parser fails complete. This code is not
|
||||||
|
executed until the parser has tried and failed to resolve an input
|
||||||
|
error using is usual error recovery strategy. The routine is
|
||||||
|
only invoked when parsing is unable to continue.</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%parse_failure {
|
||||||
|
fprintf(stderr,"Giving up. Parser is hopelessly lost...\n");
|
||||||
|
}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<h4>The <tt>%right</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>This directive is used to assign right-associative precedence to
|
||||||
|
one or more terminal symbols. See the section on precedence rules
|
||||||
|
or on the %left directive for additional information.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%stack_overflow</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %stack_overflow directive specifies a block of C code that
|
||||||
|
is executed if the parser's internal stack ever overflows. Typically
|
||||||
|
this just prints an error message. After a stack overflow, the parser
|
||||||
|
will be unable to continue and must be reset.</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%stack_overflow {
|
||||||
|
fprintf(stderr,"Giving up. Parser stack overflow\n");
|
||||||
|
}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>You can help prevent parser stack overflows by avoiding the use
|
||||||
|
of right recursion and right-precedence operators in your grammar.
|
||||||
|
Use left recursion and and left-precedence operators instead, to
|
||||||
|
encourage rules to reduce sooner and keep the stack size down.
|
||||||
|
For example, do rules like this:
|
||||||
|
<pre>
|
||||||
|
list ::= list element. // left-recursion. Good!
|
||||||
|
list ::= .
|
||||||
|
</pre>
|
||||||
|
Not like this:
|
||||||
|
<pre>
|
||||||
|
list ::= element list. // right-recursion. Bad!
|
||||||
|
list ::= .
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h4>The <tt>%stack_size</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>If stack overflow is a problem and you can't resolve the trouble
|
||||||
|
by using left-recursion, then you might want to increase the size
|
||||||
|
of the parser's stack using this directive. Put an positive integer
|
||||||
|
after the %stack_size directive and Lemon will generate a parse
|
||||||
|
with a stack of the requested size. The default value is 100.</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%stack_size 2000
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<h4>The <tt>%start_symbol</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>By default, the start-symbol for the grammar that Lemon generates
|
||||||
|
is the first non-terminal that appears in the grammar file. But you
|
||||||
|
can choose a different start-symbol using the %start_symbol directive.</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%start_symbol prog
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<h4>The <tt>%token_destructor</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %destructor directive assigns a destructor to a non-terminal
|
||||||
|
symbol. (See the description of the %destructor directive above.)
|
||||||
|
This directive does the same thing for all terminal symbols.</p>
|
||||||
|
|
||||||
|
<p>Unlike non-terminal symbols which may each have a different data type
|
||||||
|
for their values, terminals all use the same data type (defined by
|
||||||
|
the %token_type directive) and so they use a common destructor. Other
|
||||||
|
than that, the token destructor works just like the non-terminal
|
||||||
|
destructors.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%token_prefix</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>Lemon generates #defines that assign small integer constants
|
||||||
|
to each terminal symbol in the grammar. If desired, Lemon will
|
||||||
|
add a prefix specified by this directive
|
||||||
|
to each of the #defines it generates.
|
||||||
|
So if the default output of Lemon looked like this:
|
||||||
|
<pre>
|
||||||
|
#define AND 1
|
||||||
|
#define MINUS 2
|
||||||
|
#define OR 3
|
||||||
|
#define PLUS 4
|
||||||
|
</pre>
|
||||||
|
You can insert a statement into the grammar like this:
|
||||||
|
<pre>
|
||||||
|
%token_prefix TOKEN_
|
||||||
|
</pre>
|
||||||
|
to cause Lemon to produce these symbols instead:
|
||||||
|
<pre>
|
||||||
|
#define TOKEN_AND 1
|
||||||
|
#define TOKEN_MINUS 2
|
||||||
|
#define TOKEN_OR 3
|
||||||
|
#define TOKEN_PLUS 4
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4>
|
||||||
|
|
||||||
|
<p>These directives are used to specify the data types for values
|
||||||
|
on the parser's stack associated with terminal and non-terminal
|
||||||
|
symbols. The values of all terminal symbols must be of the same
|
||||||
|
type. This turns out to be the same data type as the 3rd parameter
|
||||||
|
to the Parse() function generated by Lemon. Typically, you will
|
||||||
|
make the value of a terminal symbol by a pointer to some kind of
|
||||||
|
token structure. Like this:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%token_type {Token*}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>If the data type of terminals is not specified, the default value
|
||||||
|
is ``int''.</p>
|
||||||
|
|
||||||
|
<p>Non-terminal symbols can each have their own data types. Typically
|
||||||
|
the data type of a non-terminal is a pointer to the root of a parse-tree
|
||||||
|
structure that contains all information about that non-terminal.
|
||||||
|
For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%type expr {Expr*}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>Each entry on the parser's stack is actually a union containing
|
||||||
|
instances of all data types for every non-terminal and terminal symbol.
|
||||||
|
Lemon will automatically use the correct element of this union depending
|
||||||
|
on what the corresponding non-terminal or terminal symbol is. But
|
||||||
|
the grammar designer should keep in mind that the size of the union
|
||||||
|
will be the size of its largest element. So if you have a single
|
||||||
|
non-terminal whose data type requires 1K of storage, then your 100
|
||||||
|
entry parser stack will require 100K of heap space. If you are willing
|
||||||
|
and able to pay that price, fine. You just need to know.</p>
|
||||||
|
|
||||||
|
<h3>Error Processing</h3>
|
||||||
|
|
||||||
|
<p>After extensive experimentation over several years, it has been
|
||||||
|
discovered that the error recovery strategy used by yacc is about
|
||||||
|
as good as it gets. And so that is what Lemon uses.</p>
|
||||||
|
|
||||||
|
<p>When a Lemon-generated parser encounters a syntax error, it
|
||||||
|
first invokes the code specified by the %syntax_error directive, if
|
||||||
|
any. It then enters its error recovery strategy. The error recovery
|
||||||
|
strategy is to begin popping the parsers stack until it enters a
|
||||||
|
state where it is permitted to shift a special non-terminal symbol
|
||||||
|
named ``error''. It then shifts this non-terminal and continues
|
||||||
|
parsing. But the %syntax_error routine will not be called again
|
||||||
|
until at least three new tokens have been successfully shifted.</p>
|
||||||
|
|
||||||
|
<p>If the parser pops its stack until the stack is empty, and it still
|
||||||
|
is unable to shift the error symbol, then the %parse_failed routine
|
||||||
|
is invoked and the parser resets itself to its start state, ready
|
||||||
|
to begin parsing a new file. This is what will happen at the very
|
||||||
|
first syntax error, of course, if there are no instances of the
|
||||||
|
``error'' non-terminal in your grammar.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
35
manifest
35
manifest
@ -1,8 +1,29 @@
|
|||||||
C initial\sempty\scheck-in
|
C initial\scheck-in\sof\sthe\snew\sversion\s(CVS\s1)
|
||||||
D 2000-05-29T14:16:00
|
D 2000-05-29T14:26:00
|
||||||
P
|
F Makefile.in 4bd5c67a3a2816e930df4b22df8c1631ee87ff0c
|
||||||
R d41d8cd98f00b204e9800998ecf8427e
|
F configure 8faba4d0194321e5f61a64e842c65eab0f68e6d8 x
|
||||||
T *branch * trunk
|
F configure.in 4fc2947d631037bd340b112d6193847d7ac827ae
|
||||||
T *sym-trunk *
|
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
|
||||||
|
F src/build.c 64016990ebbbcbc848165551732a1f9f397bd150
|
||||||
|
F src/dbbe.c ab05293e89525041eaab8b4aca10516db3648792
|
||||||
|
F src/dbbe.h bedeb3a0985bb584458e7849fb59927e99e751e6
|
||||||
|
F src/main.c 25cce7bce0eb3ba10bada7c05f4b38dc6dbbc86f
|
||||||
|
F src/shell.c de770d16aae1ea219e7a9ab5147a4437c23b4a6c
|
||||||
|
F src/sqlite.h 2397c17a8f4ca90c09acab0100dc7e2f8f441b69
|
||||||
|
F src/sqliteInt.h 0365970442441b5e9b74e1e828afdeac7b0662be
|
||||||
|
F src/tclsqlite.c 6eca1c1b8713048245d295ed18dca71c91d4931f
|
||||||
|
F src/tokenize.c ab578d90ec6ab117b7ade6e6cfbcb5b0f9cad500
|
||||||
|
F src/util.c 370c2339bb9ff82645804a4c62506149392fd032
|
||||||
|
F src/vdbe.c 80132b6bb9a744d1990a1c16666d54baaff2dbc3
|
||||||
|
F src/vdbe.h e721ad308f2e6ca805cebc4dd0a196ce4419d030
|
||||||
|
F src/where.c 67ffea57920e16b33c580e9a9b9855b3ec9dea7b
|
||||||
|
F tool/gdbmdump.c 529e67c78d920606ba196326ea55b57b75fcc82b
|
||||||
|
F tool/lemon.c cff35578b3c4d1491021b6418016639ebe21b1a5
|
||||||
|
F tool/lempar.c a1eec94d6eacc12332368660ec65f3b248853833
|
||||||
|
F tool/opNames.awk 2bd9071a138e4e2be13dc98fe066398a61219e1e
|
||||||
|
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
|
||||||
|
F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
|
||||||
|
P 704b122e5308587b60b47a5c2fff40c593d4bf8f
|
||||||
|
R 33c985d67f2f41286bc65b8529a1ae84
|
||||||
U drh
|
U drh
|
||||||
Z 8c6f780fffd15dac29a44b424067ccfc
|
Z a9e2b0f2d67c72179e4ea5172821c6d6
|
||||||
|
@ -1 +1 @@
|
|||||||
704b122e5308587b60b47a5c2fff40c593d4bf8f
|
6f3655f79f9b6fc9fb7baaa10a7e0f2b6a512dfa
|
1435
src/build.c
Normal file
1435
src/build.c
Normal file
File diff suppressed because it is too large
Load Diff
510
src/dbbe.c
Normal file
510
src/dbbe.c
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** This file contains code to implement the database baseend (DBBE)
|
||||||
|
** for sqlite. The database backend is the interface between
|
||||||
|
** sqlite and the code that does the actually reading and writing
|
||||||
|
** of information to the disk.
|
||||||
|
**
|
||||||
|
** This file uses GDBM as the database backend. It should be
|
||||||
|
** relatively simple to convert to a different database such
|
||||||
|
** as NDBM, SDBM, or BerkeleyDB.
|
||||||
|
**
|
||||||
|
** $Id: dbbe.c,v 1.1 2000/05/29 14:26:01 drh Exp $
|
||||||
|
*/
|
||||||
|
#include "sqliteInt.h"
|
||||||
|
#include <gdbm.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each open database file is an instance of this structure.
|
||||||
|
*/
|
||||||
|
typedef struct BeFile BeFile;
|
||||||
|
struct BeFile {
|
||||||
|
char *zName; /* Name of the file */
|
||||||
|
GDBM_FILE dbf; /* The file itself */
|
||||||
|
int nRef; /* Number of references */
|
||||||
|
BeFile *pNext, *pPrev; /* Next and previous on list of open files */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The complete database is an instance of the following structure.
|
||||||
|
*/
|
||||||
|
struct Dbbe {
|
||||||
|
char *zDir; /* The directory containing the database */
|
||||||
|
int write; /* True for write permission */
|
||||||
|
BeFile *pOpen; /* List of open files */
|
||||||
|
int nTemp; /* Number of temporary files created */
|
||||||
|
FILE **apTemp; /* Space to hold temporary file pointers */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each file within the database is an instance of this
|
||||||
|
** structure.
|
||||||
|
*/
|
||||||
|
struct DbbeTable {
|
||||||
|
Dbbe *pBe; /* The database of which this record is a part */
|
||||||
|
BeFile *pFile; /* The database file for this table */
|
||||||
|
datum key; /* Most recently used key */
|
||||||
|
datum data; /* Most recent data */
|
||||||
|
int needRewind; /* Next key should be the first */
|
||||||
|
int readPending; /* The fetch hasn't actually been done yet */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This routine opens a new database. For the current driver scheme,
|
||||||
|
** the database name is the name of the directory
|
||||||
|
** containing all the files of the database.
|
||||||
|
*/
|
||||||
|
Dbbe *sqliteDbbeOpen(
|
||||||
|
const char *zName, /* The name of the database */
|
||||||
|
int write, /* True if we will be writing to the database */
|
||||||
|
int create, /* True to create database if it doesn't exist */
|
||||||
|
char **pzErrMsg /* Write error messages (if any) here */
|
||||||
|
){
|
||||||
|
Dbbe *pNew;
|
||||||
|
struct stat statbuf;
|
||||||
|
|
||||||
|
if( stat(zName, &statbuf)!=0 ){
|
||||||
|
sqliteSetString(pzErrMsg, "can't find file \"", zName, "\"", 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if( !S_ISDIR(statbuf.st_mode) ){
|
||||||
|
sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pNew = sqliteMalloc(sizeof(Dbbe) + strlen(zName) + 1);
|
||||||
|
if( pNew==0 ){
|
||||||
|
sqliteSetString(pzErrMsg, "out of memory", 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pNew->zDir = (char*)&pNew[1];
|
||||||
|
strcpy(pNew->zDir, zName);
|
||||||
|
pNew->write = write;
|
||||||
|
pNew->pOpen = 0;
|
||||||
|
return pNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Completely shutdown the given database. Close all files. Free all memory.
|
||||||
|
*/
|
||||||
|
void sqliteDbbeClose(Dbbe *pBe){
|
||||||
|
BeFile *pFile, *pNext;
|
||||||
|
for(pFile=pBe->pOpen; pFile; pFile=pNext){
|
||||||
|
pNext = pFile->pNext;
|
||||||
|
gdbm_close(pFile->dbf);
|
||||||
|
memset(pFile, 0, sizeof(*pFile));
|
||||||
|
sqliteFree(pFile);
|
||||||
|
}
|
||||||
|
memset(pBe, 0, sizeof(*pBe));
|
||||||
|
sqliteFree(pBe);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Translate the name of a table into the name of a file that holds
|
||||||
|
** that table. Space to hold the filename is obtained from
|
||||||
|
** sqliteMalloc() and must be freed by the calling function.
|
||||||
|
*/
|
||||||
|
static char *sqliteFileOfTable(Dbbe *pBe, const char *zTable){
|
||||||
|
char *zFile = 0;
|
||||||
|
int i;
|
||||||
|
sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0);
|
||||||
|
if( zFile==0 ) return 0;
|
||||||
|
for(i=strlen(pBe->zDir)+1; zFile[i]; i++){
|
||||||
|
int c = zFile[i];
|
||||||
|
if( isupper(c) ){
|
||||||
|
zFile[i] = tolower(c);
|
||||||
|
}else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){
|
||||||
|
zFile[i] = '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Open a new table cursor
|
||||||
|
*/
|
||||||
|
DbbeTable *sqliteDbbeOpenTable(
|
||||||
|
Dbbe *pBe, /* The database the table belongs to */
|
||||||
|
const char *zTable, /* The name of the table */
|
||||||
|
int writeable /* True to open for writing */
|
||||||
|
){
|
||||||
|
char *zFile; /* Name of the table file */
|
||||||
|
DbbeTable *pTable; /* The new table cursor */
|
||||||
|
BeFile *pFile; /* The underlying data file for this table */
|
||||||
|
|
||||||
|
pTable = sqliteMalloc( sizeof(*pTable) );
|
||||||
|
if( pTable==0 ) return 0;
|
||||||
|
zFile = sqliteFileOfTable(pBe, zTable);
|
||||||
|
for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
|
||||||
|
if( strcmp(pFile->zName,zFile)==0 ) break;
|
||||||
|
}
|
||||||
|
if( pFile==0 ){
|
||||||
|
pFile = sqliteMalloc( sizeof(*pFile) );
|
||||||
|
if( pFile==0 ){
|
||||||
|
sqliteFree(zFile);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pFile->zName = zFile;
|
||||||
|
pFile->nRef = 1;
|
||||||
|
pFile->pPrev = 0;
|
||||||
|
if( pBe->pOpen ){
|
||||||
|
pBe->pOpen->pPrev = pFile;
|
||||||
|
}
|
||||||
|
pFile->pNext = pBe->pOpen;
|
||||||
|
pBe->pOpen = pFile;
|
||||||
|
pFile->dbf = gdbm_open(pFile->zName, 0, GDBM_WRCREAT, 0640, 0);
|
||||||
|
}else{
|
||||||
|
sqliteFree(zFile);
|
||||||
|
pFile->nRef++;
|
||||||
|
}
|
||||||
|
pTable->pBe = pBe;
|
||||||
|
pTable->pFile = pFile;
|
||||||
|
pTable->readPending = 0;
|
||||||
|
pTable->needRewind = 1;
|
||||||
|
return pTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Drop a table from the database.
|
||||||
|
*/
|
||||||
|
void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){
|
||||||
|
char *zFile; /* Name of the table file */
|
||||||
|
|
||||||
|
zFile = sqliteFileOfTable(pBe, zTable);
|
||||||
|
unlink(zFile);
|
||||||
|
sqliteFree(zFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Close a table previously opened by sqliteDbbeOpenTable().
|
||||||
|
*/
|
||||||
|
void sqliteDbbeCloseTable(DbbeTable *pTable){
|
||||||
|
BeFile *pFile;
|
||||||
|
Dbbe *pBe;
|
||||||
|
if( pTable==0 ) return;
|
||||||
|
pFile = pTable->pFile;
|
||||||
|
pBe = pTable->pBe;
|
||||||
|
pFile->nRef--;
|
||||||
|
if( pFile->nRef<=0 ){
|
||||||
|
if( pFile->dbf!=NULL ){
|
||||||
|
gdbm_close(pFile->dbf);
|
||||||
|
}
|
||||||
|
if( pFile->pPrev ){
|
||||||
|
pFile->pPrev->pNext = pFile->pNext;
|
||||||
|
}else{
|
||||||
|
pBe->pOpen = pFile->pNext;
|
||||||
|
}
|
||||||
|
if( pFile->pNext ){
|
||||||
|
pFile->pNext->pPrev = pFile->pPrev;
|
||||||
|
}
|
||||||
|
sqliteFree(pFile->zName);
|
||||||
|
memset(pFile, 0, sizeof(*pFile));
|
||||||
|
sqliteFree(pFile);
|
||||||
|
}
|
||||||
|
if( pTable->key.dptr ) free(pTable->key.dptr);
|
||||||
|
if( pTable->data.dptr ) free(pTable->data.dptr);
|
||||||
|
memset(pTable, 0, sizeof(*pTable));
|
||||||
|
sqliteFree(pTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Clear the given datum
|
||||||
|
*/
|
||||||
|
static void datumClear(datum *p){
|
||||||
|
if( p->dptr ) free(p->dptr);
|
||||||
|
p->dptr = 0;
|
||||||
|
p->dsize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Fetch a single record from an open table. Return 1 on success
|
||||||
|
** and 0 on failure.
|
||||||
|
*/
|
||||||
|
int sqliteDbbeFetch(DbbeTable *pTable, int nKey, char *pKey){
|
||||||
|
datum key;
|
||||||
|
key.dsize = nKey;
|
||||||
|
key.dptr = pKey;
|
||||||
|
datumClear(&pTable->key);
|
||||||
|
datumClear(&pTable->data);
|
||||||
|
if( pTable->pFile && pTable->pFile->dbf ){
|
||||||
|
pTable->data = gdbm_fetch(pTable->pFile->dbf, key);
|
||||||
|
}
|
||||||
|
return pTable->data.dptr!=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copy bytes from the current key or data into a buffer supplied by
|
||||||
|
** the calling function. Return the number of bytes copied.
|
||||||
|
*/
|
||||||
|
int sqliteDbbeCopyKey(DbbeTable *pTable, int offset, int size, char *zBuf){
|
||||||
|
int n;
|
||||||
|
if( offset>=pTable->key.dsize ) return 0;
|
||||||
|
if( offset+size>pTable->key.dsize ){
|
||||||
|
n = pTable->key.dsize - offset;
|
||||||
|
}else{
|
||||||
|
n = size;
|
||||||
|
}
|
||||||
|
memcpy(zBuf, &pTable->key.dptr[offset], n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
int sqliteDbbeCopyData(DbbeTable *pTable, int offset, int size, char *zBuf){
|
||||||
|
int n;
|
||||||
|
if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
|
||||||
|
pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
|
||||||
|
pTable->readPending = 0;
|
||||||
|
}
|
||||||
|
if( offset>=pTable->data.dsize ) return 0;
|
||||||
|
if( offset+size>pTable->data.dsize ){
|
||||||
|
n = pTable->data.dsize - offset;
|
||||||
|
}else{
|
||||||
|
n = size;
|
||||||
|
}
|
||||||
|
memcpy(zBuf, &pTable->data.dptr[offset], n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return a pointer to bytes from the key or data. The data returned
|
||||||
|
** is ephemeral.
|
||||||
|
*/
|
||||||
|
char *sqliteDbbeReadKey(DbbeTable *pTable, int offset){
|
||||||
|
if( offset<0 || offset>=pTable->key.dsize ) return "";
|
||||||
|
return &pTable->key.dptr[offset];
|
||||||
|
}
|
||||||
|
char *sqliteDbbeReadData(DbbeTable *pTable, int offset){
|
||||||
|
if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
|
||||||
|
pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
|
||||||
|
pTable->readPending = 0;
|
||||||
|
}
|
||||||
|
if( offset<0 || offset>=pTable->data.dsize ) return "";
|
||||||
|
return &pTable->data.dptr[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the total number of bytes in either data or key.
|
||||||
|
*/
|
||||||
|
int sqliteDbbeKeyLength(DbbeTable *pTable){
|
||||||
|
return pTable->key.dsize;
|
||||||
|
}
|
||||||
|
int sqliteDbbeDataLength(DbbeTable *pTable){
|
||||||
|
if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
|
||||||
|
pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
|
||||||
|
pTable->readPending = 0;
|
||||||
|
}
|
||||||
|
return pTable->data.dsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Make is so that the next call to sqliteNextKey() finds the first
|
||||||
|
** key of the table.
|
||||||
|
*/
|
||||||
|
int sqliteDbbeRewind(DbbeTable *pTable){
|
||||||
|
pTable->needRewind = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Read the next key from the table. Return 1 on success. Return
|
||||||
|
** 0 if there are no more keys.
|
||||||
|
*/
|
||||||
|
int sqliteDbbeNextKey(DbbeTable *pTable){
|
||||||
|
datum nextkey;
|
||||||
|
int rc;
|
||||||
|
if( pTable==0 || pTable->pFile==0 || pTable->pFile->dbf==0 ){
|
||||||
|
pTable->readPending = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if( pTable->needRewind ){
|
||||||
|
nextkey = gdbm_firstkey(pTable->pFile->dbf);
|
||||||
|
pTable->needRewind = 0;
|
||||||
|
}else{
|
||||||
|
nextkey = gdbm_nextkey(pTable->pFile->dbf, pTable->key);
|
||||||
|
}
|
||||||
|
datumClear(&pTable->key);
|
||||||
|
datumClear(&pTable->data);
|
||||||
|
pTable->key = nextkey;
|
||||||
|
if( pTable->key.dptr ){
|
||||||
|
pTable->readPending = 1;
|
||||||
|
rc = 1;
|
||||||
|
}else{
|
||||||
|
pTable->needRewind = 1;
|
||||||
|
pTable->readPending = 0;
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following are state variables for the RC4 algorithm. We
|
||||||
|
** use RC4 as a random number generator. Each call to RC4 gives
|
||||||
|
** a random 8-bit number.
|
||||||
|
*/
|
||||||
|
static struct {
|
||||||
|
int i, j;
|
||||||
|
int s[256];
|
||||||
|
} rc4;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Initialize the RC4 algorithm.
|
||||||
|
*/
|
||||||
|
static void rc4init(char *key, int keylen){
|
||||||
|
int i;
|
||||||
|
char k[256];
|
||||||
|
rc4.j = 0;
|
||||||
|
rc4.i = 0;
|
||||||
|
for(i=0; i<256; i++){
|
||||||
|
rc4.s[i] = i;
|
||||||
|
k[i] = key[i%keylen];
|
||||||
|
}
|
||||||
|
for(i=0; i<256; i++){
|
||||||
|
int t;
|
||||||
|
rc4.j = (rc4.j + rc4.s[i] + k[i]) & 0xff;
|
||||||
|
t = rc4.s[rc4.j];
|
||||||
|
rc4.s[rc4.j] = rc4.s[i];
|
||||||
|
rc4.s[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Get a single 8-bit random value from the RC4 algorithm.
|
||||||
|
*/
|
||||||
|
static int rc4byte(void){
|
||||||
|
int t;
|
||||||
|
rc4.i = (rc4.i + 1) & 0xff;
|
||||||
|
rc4.j = (rc4.j + rc4.s[rc4.i]) & 0xff;
|
||||||
|
t = rc4.s[rc4.i];
|
||||||
|
rc4.s[rc4.i] = rc4.s[rc4.j];
|
||||||
|
rc4.s[rc4.j] = t;
|
||||||
|
t = rc4.s[rc4.i] + rc4.s[rc4.j];
|
||||||
|
return t & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Get a new integer key.
|
||||||
|
*/
|
||||||
|
int sqliteDbbeNew(DbbeTable *pTable){
|
||||||
|
static int isInit = 0;
|
||||||
|
int iKey;
|
||||||
|
datum key;
|
||||||
|
int go = 1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if( !isInit ){
|
||||||
|
struct stat statbuf;
|
||||||
|
stat(pTable->pFile->zName, &statbuf);
|
||||||
|
time(&statbuf.st_ctime);
|
||||||
|
rc4init((char*)&statbuf, sizeof(statbuf));
|
||||||
|
isInit = 1;
|
||||||
|
}
|
||||||
|
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 1;
|
||||||
|
while( go ){
|
||||||
|
iKey = 0;
|
||||||
|
for(i=0; i<4; i++){
|
||||||
|
iKey = (iKey<<8) + rc4byte();
|
||||||
|
}
|
||||||
|
key.dptr = (char*)&iKey;
|
||||||
|
key.dsize = 4;
|
||||||
|
go = gdbm_exists(pTable->pFile->dbf, key);
|
||||||
|
}
|
||||||
|
return iKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Write an entry into the table. Overwrite any prior entry with the
|
||||||
|
** same key.
|
||||||
|
*/
|
||||||
|
int sqliteDbbePut(DbbeTable *pTable, int nKey,char *pKey,int nData,char *pData){
|
||||||
|
datum data, key;
|
||||||
|
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 0;
|
||||||
|
data.dsize = nData;
|
||||||
|
data.dptr = pData;
|
||||||
|
key.dsize = nKey;
|
||||||
|
key.dptr = pKey;
|
||||||
|
gdbm_store(pTable->pFile->dbf, key, data, GDBM_REPLACE);
|
||||||
|
datumClear(&pTable->key);
|
||||||
|
datumClear(&pTable->data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Remove an entry from a table, if the entry exists.
|
||||||
|
*/
|
||||||
|
int sqliteDbbeDelete(DbbeTable *pTable, int nKey, char *pKey){
|
||||||
|
datum key;
|
||||||
|
datumClear(&pTable->key);
|
||||||
|
datumClear(&pTable->data);
|
||||||
|
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 0;
|
||||||
|
key.dsize = nKey;
|
||||||
|
key.dptr = pKey;
|
||||||
|
gdbm_delete(pTable->pFile->dbf, key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Open a temporary file.
|
||||||
|
*/
|
||||||
|
FILE *sqliteDbbeOpenTempFile(Dbbe *pBe){
|
||||||
|
char *zFile;
|
||||||
|
char zBuf[30];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<pBe->nTemp; i++){
|
||||||
|
if( pBe->apTemp[i]==0 ) break;
|
||||||
|
}
|
||||||
|
if( i>=pBe->nTemp ){
|
||||||
|
pBe->nTemp++;
|
||||||
|
pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
|
||||||
|
}
|
||||||
|
if( pBe->apTemp==0 ) return 0;
|
||||||
|
sprintf(zBuf, "/_temp_%d~", i);
|
||||||
|
zFile = 0;
|
||||||
|
sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
|
||||||
|
pBe->apTemp[i] = fopen(zFile, "w+");
|
||||||
|
sqliteFree(zFile);
|
||||||
|
return pBe->apTemp[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Close a temporary file opened using sqliteDbbeOpenTempFile()
|
||||||
|
*/
|
||||||
|
void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){
|
||||||
|
int i;
|
||||||
|
for(i=0; i<pBe->nTemp; i++){
|
||||||
|
if( pBe->apTemp[i]==f ){
|
||||||
|
char *zFile;
|
||||||
|
char zBuf[30];
|
||||||
|
sprintf(zBuf, "/_temp_%d~", i);
|
||||||
|
zFile = 0;
|
||||||
|
sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
|
||||||
|
unlink(zFile);
|
||||||
|
sqliteFree(zFile);
|
||||||
|
pBe->apTemp[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
119
src/dbbe.h
Normal file
119
src/dbbe.h
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** This file defines the interface to the database backend (Dbbe).
|
||||||
|
**
|
||||||
|
** The database backend is designed to be as general as possible
|
||||||
|
** so that it can easily be replaced by a different backend.
|
||||||
|
** This library was originally designed to support the following
|
||||||
|
** backends: GDBM, NDBM, SDBM, Berkeley DB.
|
||||||
|
**
|
||||||
|
** $Id: dbbe.h,v 1.1 2000/05/29 14:26:01 drh Exp $
|
||||||
|
*/
|
||||||
|
#ifndef _SQLITE_DBBE_H_
|
||||||
|
#define _SQLITE_DBBE_H_
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The database backend supports two opaque structures. A Dbbe is
|
||||||
|
** a context for the entire set of tables forming a complete
|
||||||
|
** database. A DbbeTable is a single table.
|
||||||
|
**
|
||||||
|
** The DbbeTable structure holds some state information, such as
|
||||||
|
** the key and data from the last retrieval. For this reason,
|
||||||
|
** the backend must allow the creation of multiple independent
|
||||||
|
** DbbeTable structures for each table in the database.
|
||||||
|
*/
|
||||||
|
typedef struct Dbbe Dbbe;
|
||||||
|
typedef struct DbbeTable DbbeTable;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The 18 interface routines.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Open a complete database */
|
||||||
|
Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr);
|
||||||
|
|
||||||
|
/* Close the whole database. */
|
||||||
|
void sqliteDbbeClose(Dbbe*);
|
||||||
|
|
||||||
|
/* Open a particular table of a previously opened database.
|
||||||
|
** Create the table if it doesn't already exist and writeable!=0.
|
||||||
|
*/
|
||||||
|
DbbeTable *sqliteDbbeOpenTable(Dbbe*, const char *zTableName, int writeable);
|
||||||
|
|
||||||
|
/* Delete a table from the database */
|
||||||
|
void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
|
||||||
|
|
||||||
|
/* Close a table */
|
||||||
|
void sqliteDbbeCloseTable(DbbeTable*);
|
||||||
|
|
||||||
|
/* Fetch an entry from a table with the given key. Return 1 if
|
||||||
|
** successful and 0 if no such entry exists.
|
||||||
|
*/
|
||||||
|
int sqliteDbbeFetch(DbbeTable*, int nKey, char *pKey);
|
||||||
|
|
||||||
|
/* Retrieve the key or data used for the last fetch. Only size
|
||||||
|
** bytes are read beginning with the offset-th byte. The return
|
||||||
|
** value is the actual number of bytes read.
|
||||||
|
*/
|
||||||
|
int sqliteDbbeCopyKey(DbbeTable*, int offset, int size, char *zBuf);
|
||||||
|
int sqliteDbbeCopyData(DbbeTable*, int offset, int size, char *zBuf);
|
||||||
|
|
||||||
|
/* Retrieve the key or data. The result is ephemeral.
|
||||||
|
*/
|
||||||
|
char *sqliteDbbeReadKey(DbbeTable*, int offset);
|
||||||
|
char *sqliteDbbeReadData(DbbeTable*, int offset);
|
||||||
|
|
||||||
|
/* Return the length of the most recently fetched key or data. */
|
||||||
|
int sqliteDbbeKeyLength(DbbeTable*);
|
||||||
|
int sqliteDbbeDataLength(DbbeTable*);
|
||||||
|
|
||||||
|
/* Retrieve the next entry in the table. The first key is retrieved
|
||||||
|
** the first time this routine is called, or after a call to
|
||||||
|
** sqliteDbbeRewind(). The return value is 1 if there is another
|
||||||
|
** entry, or 0 if there are no more entries. */
|
||||||
|
int sqliteDbbeNextKey(DbbeTable*);
|
||||||
|
|
||||||
|
/* Make it so that the next call to sqliteDbbeNextKey() returns
|
||||||
|
** the first entry of the table. */
|
||||||
|
int sqliteDbbeRewind(DbbeTable*);
|
||||||
|
|
||||||
|
/* Get a new integer key for this table. */
|
||||||
|
int sqliteDbbeNew(DbbeTable*);
|
||||||
|
|
||||||
|
/* Write an entry into a table. If another entry already exists with
|
||||||
|
** the same key, the old entry is discarded first.
|
||||||
|
*/
|
||||||
|
int sqliteDbbePut(DbbeTable*, int nKey, char *pKey, int nData, char *pData);
|
||||||
|
|
||||||
|
/* Remove an entry from the table */
|
||||||
|
int sqliteDbbeDelete(DbbeTable*, int nKey, char *pKey);
|
||||||
|
|
||||||
|
/* Open a file suitable for temporary storage */
|
||||||
|
FILE *sqliteDbbeOpenTempFile(Dbbe*);
|
||||||
|
|
||||||
|
/* Close a temporary file */
|
||||||
|
void sqliteDbbeCloseTempFile(Dbbe *, FILE *);
|
||||||
|
|
||||||
|
#endif /* defined(_SQLITE_DBBE_H_) */
|
219
src/main.c
Normal file
219
src/main.c
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** Main file for the SQLite library. The routines in this file
|
||||||
|
** implement the programmer interface to the library. Routines in
|
||||||
|
** other files are for internal use by SQLite and should not be
|
||||||
|
** accessed by users of the library.
|
||||||
|
**
|
||||||
|
** $Id: main.c,v 1.1 2000/05/29 14:26:01 drh Exp $
|
||||||
|
*/
|
||||||
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This is the callback routine for the code that initializes the
|
||||||
|
** database. Each callback contains text of a CREATE TABLE or
|
||||||
|
** CREATE INDEX statement that must be parsed to yield the internal
|
||||||
|
** structures that describe the tables.
|
||||||
|
*/
|
||||||
|
static int sqliteOpenCb(void *pDb, int argc, char **argv, char **azColName){
|
||||||
|
sqlite *db = (sqlite*)pDb;
|
||||||
|
Parse sParse;
|
||||||
|
int nErr;
|
||||||
|
char *zErrMsg = 0;
|
||||||
|
|
||||||
|
if( argc!=1 ) return 0;
|
||||||
|
memset(&sParse, 0, sizeof(sParse));
|
||||||
|
sParse.db = db;
|
||||||
|
sParse.initFlag = 1;
|
||||||
|
nErr = sqliteRunParser(&sParse, argv[0], &zErrMsg);
|
||||||
|
return nErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Open a new SQLite database. Construct an "sqlite" structure to define
|
||||||
|
** the state of this database and return a pointer to that structure.
|
||||||
|
*/
|
||||||
|
sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
|
||||||
|
sqlite *db;
|
||||||
|
Vdbe *vdbe;
|
||||||
|
Table *pTab;
|
||||||
|
char *azArg[2];
|
||||||
|
static char master_schema[] =
|
||||||
|
"CREATE TABLE " MASTER_NAME " (\n"
|
||||||
|
" type text,\n"
|
||||||
|
" name text,\n"
|
||||||
|
" tbl_name text,\n"
|
||||||
|
" sql text\n"
|
||||||
|
")"
|
||||||
|
;
|
||||||
|
|
||||||
|
/* The following program is used to initialize the internal
|
||||||
|
** structure holding the tables and indexes of the database.
|
||||||
|
** The database contains a special table named "sqlite_master"
|
||||||
|
** defined as follows:
|
||||||
|
**
|
||||||
|
** CREATE TABLE sqlite_master (
|
||||||
|
** type text, -- Either "table" or "index"
|
||||||
|
** name text, -- Name of table or index
|
||||||
|
** tbl_name text, -- Associated table
|
||||||
|
** sql text -- The CREATE statement for this object
|
||||||
|
** );
|
||||||
|
**
|
||||||
|
** The sqlite_master table contains a single entry for each table
|
||||||
|
** and each index. The "type" field tells whether the entry is
|
||||||
|
** a table or index. The "name" field is the name of the object.
|
||||||
|
** The "tbl_name" is the name of the associated table. For tables,
|
||||||
|
** the tbl_name field is always the same as name. For indices, the
|
||||||
|
** tbl_name field contains the name of the table that the index
|
||||||
|
** indexes. Finally, the sql field contains the complete text of
|
||||||
|
** the CREATE TABLE or CREATE INDEX statement that originally created
|
||||||
|
** the table or index.
|
||||||
|
**
|
||||||
|
** The following program invokes its callback on the SQL for each
|
||||||
|
** table then goes back and invokes the callback on the
|
||||||
|
** SQL for each index. The callback will invoke the
|
||||||
|
** parser to build the internal representation of the
|
||||||
|
** database scheme.
|
||||||
|
*/
|
||||||
|
static VdbeOp initProg[] = {
|
||||||
|
{ OP_Open, 0, 0, MASTER_NAME},
|
||||||
|
{ OP_Next, 0, 8, 0}, /* 1 */
|
||||||
|
{ OP_Field, 0, 0, 0},
|
||||||
|
{ OP_String, 0, 0, "table"},
|
||||||
|
{ OP_Ne, 0, 1, 0},
|
||||||
|
{ OP_Field, 0, 3, 0},
|
||||||
|
{ OP_Callback, 1, 0, 0},
|
||||||
|
{ OP_Goto, 0, 1, 0},
|
||||||
|
{ OP_Rewind, 0, 0, 0}, /* 8 */
|
||||||
|
{ OP_Next, 0, 16, 0}, /* 9 */
|
||||||
|
{ OP_Field, 0, 0, 0},
|
||||||
|
{ OP_String, 0, 0, "index"},
|
||||||
|
{ OP_Ne, 0, 9, 0},
|
||||||
|
{ OP_Field, 0, 3, 0},
|
||||||
|
{ OP_Callback, 1, 0, 0},
|
||||||
|
{ OP_Goto, 0, 9, 0},
|
||||||
|
{ OP_Halt, 0, 0, 0}, /* 16 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Allocate space to hold the main database structure */
|
||||||
|
db = sqliteMalloc( sizeof(sqlite) );
|
||||||
|
if( pzErrMsg ) *pzErrMsg = 0;
|
||||||
|
if( db==0 ){
|
||||||
|
sqliteSetString(pzErrMsg, "out of memory", 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open the backend database driver */
|
||||||
|
db->pBe = sqliteDbbeOpen(zFilename, (mode&0222)!=0, mode!=0, pzErrMsg);
|
||||||
|
if( db->pBe==0 ){
|
||||||
|
sqliteFree(db);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a virtual machine to run the initialization program. Run
|
||||||
|
** the program. The delete the virtual machine.
|
||||||
|
*/
|
||||||
|
azArg[0] = master_schema;
|
||||||
|
azArg[1] = 0;
|
||||||
|
sqliteOpenCb(db, 1, azArg, 0);
|
||||||
|
pTab = sqliteFindTable(db, MASTER_NAME);
|
||||||
|
if( pTab ){
|
||||||
|
pTab->readOnly = 1;
|
||||||
|
}
|
||||||
|
vdbe = sqliteVdbeCreate(db->pBe);
|
||||||
|
sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg);
|
||||||
|
sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg);
|
||||||
|
sqliteVdbeDelete(vdbe);
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Close an existing SQLite database
|
||||||
|
*/
|
||||||
|
void sqlite_close(sqlite *db){
|
||||||
|
int i;
|
||||||
|
sqliteDbbeClose(db->pBe);
|
||||||
|
for(i=0; i<N_HASH; i++){
|
||||||
|
Table *pNext, *pList = db->apTblHash[i];
|
||||||
|
db->apTblHash[i] = 0;
|
||||||
|
while( pList ){
|
||||||
|
pNext = pList->pHash;
|
||||||
|
pList->pHash = 0;
|
||||||
|
sqliteDeleteTable(db, pList);
|
||||||
|
pList = pNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqliteFree(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return TRUE if the given SQL string ends in a semicolon.
|
||||||
|
*/
|
||||||
|
int sqlite_complete(const char *zSql){
|
||||||
|
int i;
|
||||||
|
int lastWasSemi = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while( i>=0 && zSql[i]!=0 ){
|
||||||
|
int tokenType;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = sqliteGetToken(&zSql[i], &tokenType);
|
||||||
|
switch( tokenType ){
|
||||||
|
case TK_SPACE:
|
||||||
|
case TK_COMMENT:
|
||||||
|
break;
|
||||||
|
case TK_SEMI:
|
||||||
|
lastWasSemi = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
lastWasSemi = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += n;
|
||||||
|
}
|
||||||
|
return lastWasSemi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Execute SQL code
|
||||||
|
*/
|
||||||
|
int sqlite_exec(
|
||||||
|
sqlite *db, /* The database on which the SQL executes */
|
||||||
|
char *zSql, /* The SQL to be executed */
|
||||||
|
sqlite_callback xCallback, /* Invoke this callback routine */
|
||||||
|
void *pArg, /* First argument to xCallback() */
|
||||||
|
char **pzErrMsg /* Write error messages here */
|
||||||
|
){
|
||||||
|
Parse sParse;
|
||||||
|
int nErr;
|
||||||
|
|
||||||
|
if( pzErrMsg ) *pzErrMsg = 0;
|
||||||
|
memset(&sParse, 0, sizeof(sParse));
|
||||||
|
sParse.db = db;
|
||||||
|
sParse.xCallback = xCallback;
|
||||||
|
sParse.pArg = pArg;
|
||||||
|
nErr = sqliteRunParser(&sParse, zSql, pzErrMsg);
|
||||||
|
return nErr;
|
||||||
|
}
|
384
src/shell.c
Normal file
384
src/shell.c
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** This file contains code to implement the "sqlite" command line
|
||||||
|
** utility for accessing SQLite databases.
|
||||||
|
**
|
||||||
|
** $Id: shell.c,v 1.1 2000/05/29 14:26:01 drh Exp $
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "sqlite.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#if !defined(NO_READLINE)
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** An pointer to an instance of this structure is passed from
|
||||||
|
** the main program to the callback. This is used to communicate
|
||||||
|
** state and mode information.
|
||||||
|
*/
|
||||||
|
struct callback_data {
|
||||||
|
int cnt; /* Number of records displayed so far */
|
||||||
|
FILE *out; /* Write results here */
|
||||||
|
int mode; /* An output mode setting */
|
||||||
|
int showHeader; /* True to show column names in List or Column mode */
|
||||||
|
char separator[20];/* Separator character for MODE_List */
|
||||||
|
int colWidth[30]; /* Width of each column when in column mode */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** These are the allowed modes.
|
||||||
|
*/
|
||||||
|
#define MODE_Line 0 /* One field per line. Blank line between records */
|
||||||
|
#define MODE_Column 1 /* One record per line in neat columns */
|
||||||
|
#define MODE_List 2 /* One record per line with a separator */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Number of elements in an array
|
||||||
|
*/
|
||||||
|
#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This is the callback routine that the SQLite library
|
||||||
|
** invokes for each row of a query result.
|
||||||
|
*/
|
||||||
|
static int callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||||
|
int i;
|
||||||
|
struct callback_data *p = (struct callback_data*)pArg;
|
||||||
|
switch( p->mode ){
|
||||||
|
case MODE_Line: {
|
||||||
|
if( p->cnt++>0 ) fprintf(p->out,"\n");
|
||||||
|
for(i=0; i<nArg; i++){
|
||||||
|
fprintf(p->out,"%s = %s\n", azCol[i], azArg[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MODE_Column: {
|
||||||
|
if( p->cnt++==0 && p->showHeader ){
|
||||||
|
for(i=0; i<nArg; i++){
|
||||||
|
int w;
|
||||||
|
if( i<ArraySize(p->colWidth) && p->colWidth[i]>0 ){
|
||||||
|
w = p->colWidth[i];
|
||||||
|
}else{
|
||||||
|
w = 10;
|
||||||
|
}
|
||||||
|
fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
|
||||||
|
}
|
||||||
|
for(i=0; i<nArg; i++){
|
||||||
|
int w;
|
||||||
|
if( i<ArraySize(p->colWidth) && p->colWidth[i]>0 ){
|
||||||
|
w = p->colWidth[i];
|
||||||
|
}else{
|
||||||
|
w = 10;
|
||||||
|
}
|
||||||
|
fprintf(p->out,"%-*.*s%s",w,w,"-------------------------------------",
|
||||||
|
i==nArg-1 ? "\n": " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i=0; i<nArg; i++){
|
||||||
|
int w;
|
||||||
|
if( i<ArraySize(p->colWidth) && p->colWidth[i]>0 ){
|
||||||
|
w = p->colWidth[i];
|
||||||
|
}else{
|
||||||
|
w = 10;
|
||||||
|
}
|
||||||
|
fprintf(p->out,"%-*.*s%s",w,w,azArg[i], i==nArg-1 ? "\n": " ");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MODE_List: {
|
||||||
|
if( p->cnt++==0 && p->showHeader ){
|
||||||
|
for(i=0; i<nArg; i++){
|
||||||
|
fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i=0; i<nArg; i++){
|
||||||
|
fprintf(p->out,"%s%s",azArg[i], i==nArg-1 ? "\n" : p->separator);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Text of a help message
|
||||||
|
*/
|
||||||
|
static char zHelp[] =
|
||||||
|
".exit Exit this program\n"
|
||||||
|
".explain Set output mode suitable for EXPLAIN\n"
|
||||||
|
".header ON|OFF Turn display of headers on or off\n"
|
||||||
|
".help Show this message\n"
|
||||||
|
".indices TABLE Show names of all indices on TABLE\n"
|
||||||
|
".mode MODE Set mode to one of \"line\", \"column\", or"
|
||||||
|
" \"list\"\n"
|
||||||
|
".output FILENAME Send output to FILENAME\n"
|
||||||
|
".output stdout Send output to the screen\n"
|
||||||
|
".schema ?TABLE? Show the CREATE statements\n"
|
||||||
|
".separator STRING Change separator string for \"list\" mode\n"
|
||||||
|
".tables List names all tables in the database\n"
|
||||||
|
".width NUM NUM ... Set column widths for \"column\" mode\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If an input line begins with "." then invoke this routine to
|
||||||
|
** process that line.
|
||||||
|
*/
|
||||||
|
static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
|
||||||
|
int i = 1;
|
||||||
|
int nArg = 0;
|
||||||
|
int n, c;
|
||||||
|
char *azArg[50];
|
||||||
|
|
||||||
|
/* Parse the input line into tokens.
|
||||||
|
*/
|
||||||
|
while( zLine[i] && nArg<ArraySize(azArg) ){
|
||||||
|
while( isspace(zLine[i]) ){ i++; }
|
||||||
|
if( zLine[i]=='\'' || zLine[i]=='"' ){
|
||||||
|
int delim = zLine[i++];
|
||||||
|
azArg[nArg++] = &zLine[i];
|
||||||
|
while( zLine[i] && zLine[i]!=delim ){ i++; }
|
||||||
|
if( zLine[i]==delim ){
|
||||||
|
zLine[i++] = 0;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
azArg[nArg++] = &zLine[i];
|
||||||
|
while( zLine[i] && !isspace(zLine[i]) ){ i++; }
|
||||||
|
if( zLine[i] ) zLine[i++] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process the input line.
|
||||||
|
*/
|
||||||
|
if( nArg==0 ) return;
|
||||||
|
n = strlen(azArg[0]);
|
||||||
|
c = azArg[0][0];
|
||||||
|
|
||||||
|
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
|
||||||
|
exit(0);
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
|
||||||
|
p->mode = MODE_Column;
|
||||||
|
p->showHeader = 1;
|
||||||
|
p->colWidth[0] = 4;
|
||||||
|
p->colWidth[1] = 12;
|
||||||
|
p->colWidth[2] = 5;
|
||||||
|
p->colWidth[3] = 5;
|
||||||
|
p->colWidth[4] = 40;
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='h' && strncmp(azArg[0], "header", n)==0 && nArg>1 ){
|
||||||
|
int j;
|
||||||
|
char *z = azArg[1];
|
||||||
|
int val = atoi(azArg[1]);
|
||||||
|
for(j=0; z[j]; j++){
|
||||||
|
if( isupper(z[j]) ) z[j] = tolower(z[j]);
|
||||||
|
}
|
||||||
|
if( strcmp(z,"on")==0 ){
|
||||||
|
val = 1;
|
||||||
|
}else if( strcmp(z,"yes")==0 ){
|
||||||
|
val = 1;
|
||||||
|
}
|
||||||
|
p->showHeader = val;
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
|
||||||
|
fprintf(stderr,zHelp);
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){
|
||||||
|
struct callback_data data;
|
||||||
|
char *zErrMsg = 0;
|
||||||
|
char zSql[1000];
|
||||||
|
memcpy(&data, p, sizeof(data));
|
||||||
|
data.showHeader = 0;
|
||||||
|
data.mode = MODE_List;
|
||||||
|
sprintf(zSql, "SELECT name FROM sqlite_master "
|
||||||
|
"WHERE type='index' AND tbl_name='%.900s'", azArg[1]);
|
||||||
|
sqlite_exec(db, zSql, callback, &data, &zErrMsg);
|
||||||
|
if( zErrMsg ){
|
||||||
|
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||||
|
free(zErrMsg);
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
|
||||||
|
int n2 = strlen(azArg[1]);
|
||||||
|
if( strncmp(azArg[1],"line",n2)==0 ){
|
||||||
|
p->mode = MODE_Line;
|
||||||
|
}else if( strncmp(azArg[1],"column",n2)==0 ){
|
||||||
|
p->mode = MODE_Column;
|
||||||
|
}else if( strncmp(azArg[1],"list",n2)==0 ){
|
||||||
|
p->mode = MODE_List;
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
|
||||||
|
if( p->out!=stdout ){
|
||||||
|
fclose(p->out);
|
||||||
|
}
|
||||||
|
if( strcmp(azArg[1],"stdout")==0 ){
|
||||||
|
p->out = stdout;
|
||||||
|
}else{
|
||||||
|
p->out = fopen(azArg[1], "w");
|
||||||
|
if( p->out==0 ){
|
||||||
|
fprintf(stderr,"can't write to \"%s\"\n", azArg[1]);
|
||||||
|
p->out = stdout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
|
||||||
|
struct callback_data data;
|
||||||
|
char *zErrMsg = 0;
|
||||||
|
char zSql[1000];
|
||||||
|
memcpy(&data, p, sizeof(data));
|
||||||
|
data.showHeader = 0;
|
||||||
|
data.mode = MODE_List;
|
||||||
|
if( nArg>1 ){
|
||||||
|
sprintf(zSql, "SELECT sql FROM sqlite_master WHERE name='%.900s'",
|
||||||
|
azArg[1]);
|
||||||
|
}else{
|
||||||
|
sprintf(zSql, "SELECT sql FROM sqlite_master "
|
||||||
|
"ORDER BY tbl_name, type DESC, name");
|
||||||
|
}
|
||||||
|
sqlite_exec(db, zSql, callback, &data, &zErrMsg);
|
||||||
|
if( zErrMsg ){
|
||||||
|
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||||
|
free(zErrMsg);
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
|
||||||
|
sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]);
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='t' && strncmp(azArg[0], "tables", n)==0 ){
|
||||||
|
struct callback_data data;
|
||||||
|
char *zErrMsg = 0;
|
||||||
|
static char zSql[] = "SELECT name FROM sqlite_master WHERE type='table'";
|
||||||
|
memcpy(&data, p, sizeof(data));
|
||||||
|
data.showHeader = 0;
|
||||||
|
data.mode = MODE_List;
|
||||||
|
sqlite_exec(db, zSql, callback, &data, &zErrMsg);
|
||||||
|
if( zErrMsg ){
|
||||||
|
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||||
|
free(zErrMsg);
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
|
||||||
|
if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
|
||||||
|
int j;
|
||||||
|
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
|
||||||
|
p->colWidth[j-1] = atoi(azArg[j]);
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
|
||||||
|
{
|
||||||
|
fprintf(stderr, "unknown command: \"%s\". Enter \".help\" for help\n",
|
||||||
|
azArg[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
sqlite *db;
|
||||||
|
char *zErrMsg = 0;
|
||||||
|
struct callback_data data;
|
||||||
|
|
||||||
|
if( argc!=2 && argc!=3 ){
|
||||||
|
fprintf(stderr,"Usage: %s FILENAME ?SQL?\n", *argv);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
db = sqlite_open(argv[1], 0666, &zErrMsg);
|
||||||
|
if( db==0 ){
|
||||||
|
fprintf(stderr,"Unable to open database \"%s\": %s\n", argv[1], zErrMsg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
memset(&data, 0, sizeof(data));
|
||||||
|
data.out = stdout;
|
||||||
|
if( argc==3 ){
|
||||||
|
data.mode = MODE_List;
|
||||||
|
strcpy(data.separator,"|");
|
||||||
|
if( sqlite_exec(db, argv[2], callback, &data, &zErrMsg)!=0 && zErrMsg!=0 ){
|
||||||
|
fprintf(stderr,"SQL error: %s\n", zErrMsg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
char *zLine;
|
||||||
|
char *zSql = 0;
|
||||||
|
int nSql = 0;
|
||||||
|
int istty = isatty(0);
|
||||||
|
data.mode = MODE_Line;
|
||||||
|
strcpy(data.separator,"|");
|
||||||
|
data.showHeader = 0;
|
||||||
|
if( istty ){
|
||||||
|
printf(
|
||||||
|
"Enter \".help\" for instructions\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
while( (zLine = readline(istty ? (zSql==0 ? "sql> " : ".... ") : 0))!=0 ){
|
||||||
|
if( zLine && zLine[0]=='.' ){
|
||||||
|
do_meta_command(zLine, db, &data);
|
||||||
|
free(zLine);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if( zSql==0 ){
|
||||||
|
nSql = strlen(zLine);
|
||||||
|
zSql = malloc( nSql+1 );
|
||||||
|
strcpy(zSql, zLine);
|
||||||
|
}else{
|
||||||
|
int len = strlen(zLine);
|
||||||
|
zSql = realloc( zSql, nSql + len + 2 );
|
||||||
|
if( zSql==0 ){
|
||||||
|
fprintf(stderr,"%s: out of memory!\n", *argv);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
strcpy(&zSql[nSql++], "\n");
|
||||||
|
strcpy(&zSql[nSql], zLine);
|
||||||
|
nSql += len;
|
||||||
|
}
|
||||||
|
free(zLine);
|
||||||
|
if( sqlite_complete(zSql) ){
|
||||||
|
data.cnt = 0;
|
||||||
|
if( sqlite_exec(db, zSql, callback, &data, &zErrMsg)!=0
|
||||||
|
&& zErrMsg!=0 ){
|
||||||
|
printf("SQL error: %s\n", zErrMsg);
|
||||||
|
free(zErrMsg);
|
||||||
|
zErrMsg = 0;
|
||||||
|
}
|
||||||
|
free(zSql);
|
||||||
|
zSql = 0;
|
||||||
|
nSql = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite_close(db);
|
||||||
|
return 0;
|
||||||
|
}
|
118
src/sqlite.h
Normal file
118
src/sqlite.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** This header file defines the interface that the sqlite library
|
||||||
|
** presents to client programs.
|
||||||
|
**
|
||||||
|
** @(#) $Id: sqlite.h,v 1.1 2000/05/29 14:26:01 drh Exp $
|
||||||
|
*/
|
||||||
|
#ifndef _SQLITE_H_
|
||||||
|
#define _SQLITE_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each open sqlite database is represented by an instance of the
|
||||||
|
** following opaque structure.
|
||||||
|
*/
|
||||||
|
typedef struct sqlite sqlite;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A function to open a new sqlite database.
|
||||||
|
**
|
||||||
|
** If the database does not exist and mode indicates write
|
||||||
|
** permission, then a new database is created. If the database
|
||||||
|
** does not exist and mode does not indicate write permission,
|
||||||
|
** then the open fails, an error message generated (if errmsg!=0)
|
||||||
|
** and the function returns 0.
|
||||||
|
**
|
||||||
|
** If mode does not indicates user write permission, then the
|
||||||
|
** database is opened read-only.
|
||||||
|
**
|
||||||
|
** The Truth: As currently implemented, all databases are opened
|
||||||
|
** for writing all the time. Maybe someday we will provide the
|
||||||
|
** ability to open a database readonly. The mode parameters is
|
||||||
|
** provide in anticipation of that enhancement.
|
||||||
|
*/
|
||||||
|
sqlite *sqlite_open(const char *filename, int mode, char **errmsg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A function to close the database.
|
||||||
|
**
|
||||||
|
** Call this function with a pointer to a structure that was previously
|
||||||
|
** returned from sqlite_open() and the corresponding database will by closed.
|
||||||
|
*/
|
||||||
|
void sqlite_close(sqlite *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The type for a callback function.
|
||||||
|
*/
|
||||||
|
typedef int (*sqlite_callback)(void*,int,char**, char**);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A function to executes one or more statements of SQL.
|
||||||
|
**
|
||||||
|
** If one or more of the SQL statements are queries, then
|
||||||
|
** the callback function specified by the 3rd parameter is
|
||||||
|
** invoked once for each row of the query result. This callback
|
||||||
|
** should normally return 0. If the callback returns a non-zero
|
||||||
|
** value then the query is aborted, all subsequent SQL statements
|
||||||
|
** are skipped and the sqlite_exec() function returns the same
|
||||||
|
** value that the callback returned.
|
||||||
|
**
|
||||||
|
** The 4th parameter is an arbitrary pointer that is passed
|
||||||
|
** to the callback function as its first parameter.
|
||||||
|
**
|
||||||
|
** The 2nd parameter to the callback function is the number of
|
||||||
|
** columns in the query result. The 3rd parameter is an array
|
||||||
|
** of string holding the values for each column. The 4th parameter
|
||||||
|
** is an array of strings holding the names of each column.
|
||||||
|
**
|
||||||
|
** The callback function may be NULL, even for queries. A NULL
|
||||||
|
** callback is not an error. It just means that no callback
|
||||||
|
** will be invoked.
|
||||||
|
**
|
||||||
|
** If an error occurs while parsing or evaluating the SQL (but
|
||||||
|
** not while executing the callback) then an appropriate error
|
||||||
|
** message is written into memory obtained from malloc() and
|
||||||
|
** *errmsg is made to point to that message. If errmsg==NULL,
|
||||||
|
** then no error message is ever written. The return value is
|
||||||
|
** non-zero if an error occurs.
|
||||||
|
*/
|
||||||
|
int sqlite_exec(
|
||||||
|
sqlite*, /* An open database */
|
||||||
|
char *sql, /* SQL to be executed */
|
||||||
|
sqlite_callback, /* Callback function */
|
||||||
|
void *, /* 1st argument to callback function */
|
||||||
|
char **errmsg /* Error msg written here */
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/* This function returns true if the given input string comprises
|
||||||
|
** one or more complete SQL statements.
|
||||||
|
**
|
||||||
|
** The algorithm is simple. If the last token other than spaces
|
||||||
|
** and comments is a semicolon, then return true. otherwise return
|
||||||
|
** false.
|
||||||
|
*/
|
||||||
|
int sqlite_complete(const char *sql);
|
||||||
|
|
||||||
|
#endif /* _SQLITE_H_ */
|
235
src/sqliteInt.h
Normal file
235
src/sqliteInt.h
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** Internal interface definitions for SQLite.
|
||||||
|
**
|
||||||
|
** @(#) $Id: sqliteInt.h,v 1.1 2000/05/29 14:26:01 drh Exp $
|
||||||
|
*/
|
||||||
|
#include "sqlite.h"
|
||||||
|
#include "dbbe.h"
|
||||||
|
#include "vdbe.h"
|
||||||
|
#include "parse.h"
|
||||||
|
#include <gdbm.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The number of entries in the in-memory hash table holding the
|
||||||
|
** schema.
|
||||||
|
*/
|
||||||
|
#define N_HASH 51
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Name of the master database table. The master database table
|
||||||
|
** is a special table that holds the names and attributes of all
|
||||||
|
** user tables and indices.
|
||||||
|
*/
|
||||||
|
#define MASTER_NAME "sqlite_master"
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A convenience macro that returns the number of elements in
|
||||||
|
** an array.
|
||||||
|
*/
|
||||||
|
#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Forward references to structures
|
||||||
|
*/
|
||||||
|
typedef struct Table Table;
|
||||||
|
typedef struct Index Index;
|
||||||
|
typedef struct Instruction Instruction;
|
||||||
|
typedef struct Expr Expr;
|
||||||
|
typedef struct ExprList ExprList;
|
||||||
|
typedef struct Parse Parse;
|
||||||
|
typedef struct Token Token;
|
||||||
|
typedef struct IdList IdList;
|
||||||
|
typedef struct WhereInfo WhereInfo;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each database is an instance of the following structure
|
||||||
|
*/
|
||||||
|
struct sqlite {
|
||||||
|
Dbbe *pBe; /* The backend driver */
|
||||||
|
int flags; /* Miscellanous flags */
|
||||||
|
Table *apTblHash[N_HASH]; /* All tables of the database */
|
||||||
|
Index *apIdxHash[N_HASH]; /* All indices of the database */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Possible values for the flags field of sqlite
|
||||||
|
*/
|
||||||
|
#define SQLITE_VdbeTrace 0x00000001
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each table is represented in memory by
|
||||||
|
** an instance of the following structure
|
||||||
|
*/
|
||||||
|
struct Table {
|
||||||
|
char *zName; /* Name of the table */
|
||||||
|
Table *pHash; /* Next table with same hash on zName */
|
||||||
|
int nCol; /* Number of columns in this table */
|
||||||
|
int readOnly; /* True if this table should not be written by the user */
|
||||||
|
char **azCol; /* Name of each column */
|
||||||
|
Index *pIndex; /* List of indices on this table. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each index is represented in memory by and
|
||||||
|
** instance of the following structure.
|
||||||
|
*/
|
||||||
|
struct Index {
|
||||||
|
char *zName; /* Name of this index */
|
||||||
|
Index *pHash; /* Next index with the same hash on zName */
|
||||||
|
int nField; /* Number of fields in the table indexed by this index */
|
||||||
|
int *aiField; /* Indices of fields used by this index. 1st is 0 */
|
||||||
|
Table *pTable; /* The table being indexed */
|
||||||
|
Index *pNext; /* The next index associated with the same table */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each token coming out of the lexer is an instance of
|
||||||
|
** this structure.
|
||||||
|
*/
|
||||||
|
struct Token {
|
||||||
|
char *z; /* Text of the token */
|
||||||
|
int n; /* Number of characters in this token */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each node of an expression in the parse tree is an instance
|
||||||
|
** of this structure
|
||||||
|
*/
|
||||||
|
struct Expr {
|
||||||
|
int op; /* Operation performed by this node */
|
||||||
|
Expr *pLeft, *pRight; /* Left and right subnodes */
|
||||||
|
ExprList *pList; /* A list of expressions used as a function argument */
|
||||||
|
Token token; /* An operand token */
|
||||||
|
int iTable, iField; /* When op==TK_FIELD, then this node means the
|
||||||
|
** iField-th field of the iTable-th table */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A list of expressions. Each expression may optionally have a
|
||||||
|
** name. An expr/name combination can be used in several ways, such
|
||||||
|
** as the list of "expr AS ID" fields following a "SELECT" or in the
|
||||||
|
** list of "ID = expr" items in an UPDATE. A list of expressions can
|
||||||
|
** also be used as the argument to a function, in which case the azName
|
||||||
|
** field is not used.
|
||||||
|
*/
|
||||||
|
struct ExprList {
|
||||||
|
int nExpr; /* Number of expressions on the list */
|
||||||
|
struct {
|
||||||
|
Expr *pExpr; /* The list of expressions */
|
||||||
|
char *zName; /* Token associated with this expression */
|
||||||
|
int idx; /* ... */
|
||||||
|
} *a; /* One entry for each expression */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A list of identifiers.
|
||||||
|
*/
|
||||||
|
struct IdList {
|
||||||
|
int nId; /* Number of identifiers on the list */
|
||||||
|
struct {
|
||||||
|
char *zName; /* Text of the identifier. */
|
||||||
|
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
|
||||||
|
Table *pTab; /* Table corresponding to zName */
|
||||||
|
int idx; /* Index of a field name in the table */
|
||||||
|
} *a; /* One entry for each identifier on the list */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The WHERE clause processing routine has two halves. The
|
||||||
|
** first part does the start of the WHERE loop and the second
|
||||||
|
** half does the tail of the WHERE loop. An instance of
|
||||||
|
** this structure is returned by the first half and passed
|
||||||
|
** into the second half to give some continuity.
|
||||||
|
*/
|
||||||
|
struct WhereInfo {
|
||||||
|
Parse *pParse;
|
||||||
|
IdList *pTabList;
|
||||||
|
int iContinue;
|
||||||
|
int iBreak;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** An SQL parser context
|
||||||
|
*/
|
||||||
|
struct Parse {
|
||||||
|
sqlite *db; /* The main database structure */
|
||||||
|
sqlite_callback xCallback; /* The callback function */
|
||||||
|
void *pArg; /* First argument to the callback function */
|
||||||
|
char *zErrMsg; /* An error message */
|
||||||
|
Token sErrToken; /* The token at which the error occurred */
|
||||||
|
Token sFirstToken; /* The first token parsed */
|
||||||
|
Token sLastToken; /* The last token parsed */
|
||||||
|
Table *pNewTable; /* A table being constructed by CREATE TABLE */
|
||||||
|
Vdbe *pVdbe; /* An engine for executing database bytecode */
|
||||||
|
int explain; /* True if the EXPLAIN flag is found on the query */
|
||||||
|
int initFlag; /* True if reparsing CREATE TABLEs */
|
||||||
|
int nErr; /* Number of errors seen */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Internal function prototypes
|
||||||
|
*/
|
||||||
|
int sqliteStrICmp(const char *, const char *);
|
||||||
|
int sqliteStrNICmp(const char *, const char *, int);
|
||||||
|
int sqliteHashNoCase(const char *, int);
|
||||||
|
int sqliteCompare(const char *, const char *);
|
||||||
|
int sqliteSortCompare(const char *, const char *);
|
||||||
|
void *sqliteMalloc(int);
|
||||||
|
void sqliteFree(void*);
|
||||||
|
void *sqliteRealloc(void*,int);
|
||||||
|
int sqliteGetToken(const char*, int *);
|
||||||
|
void sqliteSetString(char **, const char *, ...);
|
||||||
|
void sqliteSetNString(char **, ...);
|
||||||
|
int sqliteRunParser(Parse*, char*, char **);
|
||||||
|
void sqliteExec(Parse*);
|
||||||
|
Expr *sqliteExpr(int, Expr*, Expr*, Token*);
|
||||||
|
Expr *sqliteExprFunction(ExprList*, Token*);
|
||||||
|
void sqliteExprDelete(Expr*);
|
||||||
|
ExprList *sqliteExprListAppend(ExprList*,Expr*,Token*);
|
||||||
|
void sqliteExprListDelete(ExprList*);
|
||||||
|
void sqliteStartTable(Parse*,Token*,Token*);
|
||||||
|
void sqliteAddColumn(Parse*,Token*);
|
||||||
|
void sqliteEndTable(Parse*,Token*);
|
||||||
|
void sqliteDropTable(Parse*, Token*);
|
||||||
|
void sqliteDeleteTable(sqlite*, Table*);
|
||||||
|
void sqliteInsert(Parse*, Token*, ExprList*, IdList*);
|
||||||
|
IdList *sqliteIdListAppend(IdList*, Token*);
|
||||||
|
void sqliteIdListAddAlias(IdList*, Token*);
|
||||||
|
void sqliteIdListDelete(IdList*);
|
||||||
|
void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*);
|
||||||
|
void sqliteDropIndex(Parse*, Token*);
|
||||||
|
void sqliteSelect(Parse*, ExprList*, IdList*, Expr*, ExprList*);
|
||||||
|
void sqliteDeleteFrom(Parse*, Token*, Expr*);
|
||||||
|
void sqliteUpdate(Parse*, Token*, ExprList*, Expr*);
|
||||||
|
WhereInfo *sqliteWhereBegin(Parse*, IdList*, Expr*, int);
|
||||||
|
void sqliteWhereEnd(WhereInfo*);
|
||||||
|
void sqliteExprCode(Parse*, Expr*);
|
||||||
|
void sqliteExprIfTrue(Parse*, Expr*, int);
|
||||||
|
void sqliteExprIfFalse(Parse*, Expr*, int);
|
||||||
|
Table *sqliteFindTable(sqlite*,char*);
|
241
src/tclsqlite.c
Normal file
241
src/tclsqlite.c
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** A TCL Interface to SQLite
|
||||||
|
**
|
||||||
|
** $Id: tclsqlite.c,v 1.1 2000/05/29 14:26:01 drh Exp $
|
||||||
|
*/
|
||||||
|
#include "sqlite.h"
|
||||||
|
#include <tcl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** An instance of this structure passes information thru the sqlite
|
||||||
|
** logic from the original TCL command into the callback routine.
|
||||||
|
*/
|
||||||
|
typedef struct CallbackData CallbackData;
|
||||||
|
struct CallbackData {
|
||||||
|
Tcl_Interp *interp; /* The TCL interpreter */
|
||||||
|
char *zArray; /* The array into which data is written */
|
||||||
|
char *zCode; /* The code to execute for each row */
|
||||||
|
int once; /* Set only for the first invocation of callback */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Called for each row of the result.
|
||||||
|
*/
|
||||||
|
static int DbEvalCallback(
|
||||||
|
void *clientData, /* An instance of CallbackData */
|
||||||
|
int nCol, /* Number of columns in the result */
|
||||||
|
char ** azCol, /* Data for each column */
|
||||||
|
char ** azN /* Name for each column */
|
||||||
|
){
|
||||||
|
CallbackData *cbData = (CallbackData*)clientData;
|
||||||
|
int i, rc;
|
||||||
|
if( cbData->zArray[0] ){
|
||||||
|
if( cbData->once ){
|
||||||
|
for(i=0; i<nCol; i++){
|
||||||
|
Tcl_SetVar2(cbData->interp, cbData->zArray, "*", azN[i],
|
||||||
|
TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i=0; i<nCol; i++){
|
||||||
|
Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], azCol[i], 0);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
for(i=0; i<nCol; i++){
|
||||||
|
Tcl_SetVar(cbData->interp, azN[i], azCol[i], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cbData->once = 0;
|
||||||
|
rc = Tcl_Eval(cbData->interp, cbData->zCode);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Called when the command is deleted.
|
||||||
|
*/
|
||||||
|
static void DbDeleteCmd(void *db){
|
||||||
|
sqlite_close((sqlite*)db);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The "sqlite" command below creates a new Tcl command for each
|
||||||
|
** connection it opens to an SQLite database. This routine is invoked
|
||||||
|
** whenever one of those connection-specific commands is executed
|
||||||
|
** in Tcl. For example, if you run Tcl code like this:
|
||||||
|
**
|
||||||
|
** sqlite db1 "my_database"
|
||||||
|
** db1 close
|
||||||
|
**
|
||||||
|
** The first command opens a connection to the "my_database" database
|
||||||
|
** and calls that connection "db1". The second command causes this
|
||||||
|
** subroutine to be invoked.
|
||||||
|
*/
|
||||||
|
static int DbCmd(void *cd, Tcl_Interp *interp, int argc, char **argv){
|
||||||
|
char *z;
|
||||||
|
int n, c;
|
||||||
|
sqlite *db = cd;
|
||||||
|
if( argc<2 ){
|
||||||
|
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||||
|
" SUBCOMMAND ...\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
z = argv[1];
|
||||||
|
n = strlen(z);
|
||||||
|
c = z[0];
|
||||||
|
|
||||||
|
/* $db close
|
||||||
|
**
|
||||||
|
** Shutdown the database
|
||||||
|
*/
|
||||||
|
if( c=='c' && n>=2 && strncmp(z,"close",n)==0 ){
|
||||||
|
Tcl_DeleteCommand(interp, argv[0]);
|
||||||
|
}else
|
||||||
|
|
||||||
|
/* $db complete SQL
|
||||||
|
**
|
||||||
|
** Return TRUE if SQL is a complete SQL statement. Return FALSE if
|
||||||
|
** additional lines of input are needed. This is similar to the
|
||||||
|
** built-in "info complete" command of Tcl.
|
||||||
|
*/
|
||||||
|
if( c=='c' && n>=2 && strncmp(z,"complete",n)==0 ){
|
||||||
|
char *zRes;
|
||||||
|
if( argc!=3 ){
|
||||||
|
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||||
|
" complete SQL\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
zRes = sqlite_complete(argv[2]) ? "1" : "0";
|
||||||
|
Tcl_SetResult(interp, zRes, TCL_VOLATILE);
|
||||||
|
}else
|
||||||
|
|
||||||
|
/*
|
||||||
|
** $db eval $sql ?array { ...code... }?
|
||||||
|
**
|
||||||
|
** The SQL statement in $sql is evaluated. For each row, the values are
|
||||||
|
** placed in elements of the array named "array" and ...code.. is executed.
|
||||||
|
** If "array" and "code" are omitted, then no callback is every invoked.
|
||||||
|
** If "array" is an empty string, then the values are placed in variables
|
||||||
|
** that have the same name as the fields extracted by the query.
|
||||||
|
*/
|
||||||
|
if( c=='e' && strncmp(z,"eval",n)==0 ){
|
||||||
|
CallbackData cbData;
|
||||||
|
char *zErrMsg;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if( argc!=5 && argc!=3 ){
|
||||||
|
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||||
|
" eval SQL ?ARRAY-NAME CODE?", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
if( argc==5 ){
|
||||||
|
cbData.interp = interp;
|
||||||
|
cbData.zArray = argv[3];
|
||||||
|
cbData.zCode = argv[4];
|
||||||
|
zErrMsg = 0;
|
||||||
|
rc = sqlite_exec(db, argv[2], DbEvalCallback, &cbData, &zErrMsg);
|
||||||
|
}else{
|
||||||
|
rc = sqlite_exec(db, argv[2], 0, 0, &zErrMsg);
|
||||||
|
}
|
||||||
|
if( zErrMsg ){
|
||||||
|
Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
|
||||||
|
free(zErrMsg);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The default
|
||||||
|
*/
|
||||||
|
else{
|
||||||
|
Tcl_AppendResult(interp,"unknown subcommand \"", z,
|
||||||
|
"\" - should be one of: close complete eval", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** sqlite DBNAME FILENAME ?MODE?
|
||||||
|
**
|
||||||
|
** This is the main Tcl command. When the "sqlite" Tcl command is
|
||||||
|
** invoked, this routine runs to process that command.
|
||||||
|
**
|
||||||
|
** The first argument, DBNAME, is an arbitrary name for a new
|
||||||
|
** database connection. This command creates a new command named
|
||||||
|
** DBNAME that is used to control that connection. The database
|
||||||
|
** connection is deleted when the DBNAME command is deleted.
|
||||||
|
**
|
||||||
|
** The second argument is the name of the directory that contains
|
||||||
|
** the sqlite database that is to be accessed.
|
||||||
|
*/
|
||||||
|
static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){
|
||||||
|
int mode;
|
||||||
|
sqlite *p;
|
||||||
|
char *zErrMsg;
|
||||||
|
if( argc!=3 && argc!=4 ){
|
||||||
|
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||||
|
" HANDLE FILENAME ?MODE?\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
if( argc==3 ){
|
||||||
|
mode = 0;
|
||||||
|
}else if( Tcl_GetInt(interp, argv[3], &mode)!=TCL_OK ){
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
zErrMsg = 0;
|
||||||
|
p = sqlite_open(argv[2], mode, &zErrMsg);
|
||||||
|
if( p==0 ){
|
||||||
|
Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
|
||||||
|
free(zErrMsg);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
Tcl_CreateCommand(interp, argv[1], DbCmd, p, DbDeleteCmd);
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Initialize this module.
|
||||||
|
**
|
||||||
|
** This Tcl module contains only a single new Tcl command named "sqlite".
|
||||||
|
** (Hence there is no namespace. There is no point in using a namespace
|
||||||
|
** if the extension only supplies one new name!) The "sqlite" command is
|
||||||
|
** used to open a new SQLite database. See the DbMain() routine above
|
||||||
|
** for additional information.
|
||||||
|
*/
|
||||||
|
int Sqlite_Init(Tcl_Interp *interp){
|
||||||
|
Tcl_CreateCommand(interp, "sqlite", DbMain, 0, 0);
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
int Sqlite_SafeInit(Tcl_Interp *interp){
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If compiled using mktclapp, this routine runs to initialize
|
||||||
|
** everything.
|
||||||
|
*/
|
||||||
|
int Et_AppInit(Tcl_Interp *interp){
|
||||||
|
return Sqlite_Init(interp);
|
||||||
|
}
|
375
src/tokenize.c
Normal file
375
src/tokenize.c
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** An tokenizer for SQL
|
||||||
|
**
|
||||||
|
** This file contains C code that splits an SQL input string up into
|
||||||
|
** individual tokens and sends those tokens one-by-one over to the
|
||||||
|
** parser for analysis.
|
||||||
|
**
|
||||||
|
** $Id: tokenize.c,v 1.1 2000/05/29 14:26:02 drh Exp $
|
||||||
|
*/
|
||||||
|
#include "sqliteInt.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** All the keywords of the SQL language are stored as in a hash
|
||||||
|
** table composed of instances of the following structure.
|
||||||
|
*/
|
||||||
|
typedef struct Keyword Keyword;
|
||||||
|
struct Keyword {
|
||||||
|
char *zName; /* The keyword name */
|
||||||
|
int len; /* Number of characters in the keyword */
|
||||||
|
int tokenType; /* The token value for this keyword */
|
||||||
|
Keyword *pNext; /* Next keyword with the same hash */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** These are the keywords
|
||||||
|
*/
|
||||||
|
static Keyword aKeywordTable[] = {
|
||||||
|
{ "AND", 0, TK_AND, 0 },
|
||||||
|
{ "AS", 0, TK_AS, 0 },
|
||||||
|
{ "ASC", 0, TK_ASC, 0 },
|
||||||
|
{ "BY", 0, TK_BY, 0 },
|
||||||
|
{ "CHECK", 0, TK_CHECK, 0 },
|
||||||
|
{ "CONSTRAINT", 0, TK_CONSTRAINT, 0 },
|
||||||
|
{ "CREATE", 0, TK_CREATE, 0 },
|
||||||
|
{ "DEFAULT", 0, TK_DEFAULT, 0 },
|
||||||
|
{ "DELETE", 0, TK_DELETE, 0 },
|
||||||
|
{ "DESC", 0, TK_DESC, 0 },
|
||||||
|
{ "DROP", 0, TK_DROP, 0 },
|
||||||
|
{ "EXPLAIN", 0, TK_EXPLAIN, 0 },
|
||||||
|
{ "FROM", 0, TK_FROM, 0 },
|
||||||
|
{ "INDEX", 0, TK_INDEX, 0 },
|
||||||
|
{ "INSERT", 0, TK_INSERT, 0 },
|
||||||
|
{ "INTO", 0, TK_INTO, 0 },
|
||||||
|
{ "IS", 0, TK_IS, 0 },
|
||||||
|
{ "ISNULL", 0, TK_ISNULL, 0 },
|
||||||
|
{ "KEY", 0, TK_KEY, 0 },
|
||||||
|
{ "NOT", 0, TK_NOT, 0 },
|
||||||
|
{ "NOTNULL", 0, TK_NOTNULL, 0 },
|
||||||
|
{ "NULL", 0, TK_NULL, 0 },
|
||||||
|
{ "ON", 0, TK_ON, 0 },
|
||||||
|
{ "OR", 0, TK_OR, 0 },
|
||||||
|
{ "ORDER", 0, TK_ORDER, 0 },
|
||||||
|
{ "PRIMARY", 0, TK_PRIMARY, 0 },
|
||||||
|
{ "SELECT", 0, TK_SELECT, 0 },
|
||||||
|
{ "SET", 0, TK_SET, 0 },
|
||||||
|
{ "TABLE", 0, TK_TABLE, 0 },
|
||||||
|
{ "UNIQUE", 0, TK_UNIQUE, 0 },
|
||||||
|
{ "UPDATE", 0, TK_UPDATE, 0 },
|
||||||
|
{ "VALUES", 0, TK_VALUES, 0 },
|
||||||
|
{ "WHERE", 0, TK_WHERE, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This is the hash table
|
||||||
|
*/
|
||||||
|
#define KEY_HASH_SIZE 37
|
||||||
|
static Keyword *apHashTable[KEY_HASH_SIZE];
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function looks up an identifier to determine if it is a
|
||||||
|
** keyword. If it is a keyword, the token code of that keyword is
|
||||||
|
** returned. If the input is not a keyword, TK_ID is returned.
|
||||||
|
*/
|
||||||
|
static int sqliteKeywordCode(const char *z, int n){
|
||||||
|
int h;
|
||||||
|
Keyword *p;
|
||||||
|
if( aKeywordTable[0].len==0 ){
|
||||||
|
/* Initialize the keyword hash table */
|
||||||
|
int i;
|
||||||
|
int n;
|
||||||
|
n = sizeof(aKeywordTable)/sizeof(aKeywordTable[0]);
|
||||||
|
for(i=0; i<n; i++){
|
||||||
|
aKeywordTable[i].len = strlen(aKeywordTable[i].zName);
|
||||||
|
h = sqliteHashNoCase(aKeywordTable[i].zName, aKeywordTable[i].len);
|
||||||
|
h %= KEY_HASH_SIZE;
|
||||||
|
aKeywordTable[i].pNext = apHashTable[h];
|
||||||
|
apHashTable[h] = &aKeywordTable[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h = sqliteHashNoCase(z, n) % KEY_HASH_SIZE;
|
||||||
|
for(p=apHashTable[h]; p; p=p->pNext){
|
||||||
|
if( p->len==n && sqliteStrNICmp(p->zName, z, n)==0 ){
|
||||||
|
return p->tokenType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TK_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the length of the token that begins at z[0]. Return
|
||||||
|
** -1 if the token is (or might be) incomplete. Store the token
|
||||||
|
** type in *tokenType before returning.
|
||||||
|
*/
|
||||||
|
int sqliteGetToken(const char *z, int *tokenType){
|
||||||
|
int i;
|
||||||
|
switch( *z ){
|
||||||
|
case ' ': case '\t': case '\n': case '\f': {
|
||||||
|
for(i=1; z[i] && isspace(z[i]); i++){}
|
||||||
|
*tokenType = TK_SPACE;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
case '-': {
|
||||||
|
if( z[1]==0 ) return -1;
|
||||||
|
if( z[1]=='-' ){
|
||||||
|
for(i=2; z[i] && z[i]!='\n'; i++){}
|
||||||
|
*tokenType = TK_COMMENT;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
*tokenType = TK_MINUS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '(': {
|
||||||
|
*tokenType = TK_LP;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case ')': {
|
||||||
|
*tokenType = TK_RP;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case ';': {
|
||||||
|
*tokenType = TK_SEMI;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '+': {
|
||||||
|
*tokenType = TK_PLUS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '*': {
|
||||||
|
*tokenType = TK_STAR;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '/': {
|
||||||
|
*tokenType = TK_SLASH;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '=': {
|
||||||
|
*tokenType = TK_EQ;
|
||||||
|
return 1 + (z[1]=='=');
|
||||||
|
}
|
||||||
|
case '<': {
|
||||||
|
if( z[1]=='=' ){
|
||||||
|
*tokenType = TK_LE;
|
||||||
|
return 2;
|
||||||
|
}else if( z[1]=='>' ){
|
||||||
|
*tokenType = TK_NE;
|
||||||
|
return 2;
|
||||||
|
}else{
|
||||||
|
*tokenType = TK_LT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case '>': {
|
||||||
|
if( z[1]=='=' ){
|
||||||
|
*tokenType = TK_GE;
|
||||||
|
return 2;
|
||||||
|
}else{
|
||||||
|
*tokenType = TK_GT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case '!': {
|
||||||
|
if( z[1]!='=' ){
|
||||||
|
*tokenType = TK_ILLEGAL;
|
||||||
|
return 1;
|
||||||
|
}else{
|
||||||
|
*tokenType = TK_NE;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ',': {
|
||||||
|
*tokenType = TK_COMMA;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '\'': case '"': {
|
||||||
|
int delim = z[0];
|
||||||
|
for(i=1; z[i]; i++){
|
||||||
|
if( z[i]==delim ){
|
||||||
|
if( z[i+1]==delim ){
|
||||||
|
i++;
|
||||||
|
}else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( z[i] ) i++;
|
||||||
|
*tokenType = TK_STRING;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
case '.': {
|
||||||
|
if( !isdigit(z[1]) ){
|
||||||
|
*tokenType = TK_DOT;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* Fall thru into the next case */
|
||||||
|
}
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9': {
|
||||||
|
for(i=1; z[i] && isdigit(z[i]); i++){}
|
||||||
|
if( z[i]=='.' ){
|
||||||
|
i++;
|
||||||
|
while( z[i] && isdigit(z[i]) ){ i++; }
|
||||||
|
if( (z[i]=='e' || z[i]=='E') &&
|
||||||
|
( isdigit(z[i+1])
|
||||||
|
|| ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
|
||||||
|
)
|
||||||
|
){
|
||||||
|
i += 2;
|
||||||
|
while( z[i] && isdigit(z[i]) ){ i++; }
|
||||||
|
}
|
||||||
|
*tokenType = TK_FLOAT;
|
||||||
|
}else if( z[0]=='.' ){
|
||||||
|
*tokenType = TK_FLOAT;
|
||||||
|
}else{
|
||||||
|
*tokenType = TK_INTEGER;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||||
|
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
|
||||||
|
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
|
||||||
|
case 's': case 't': case 'u': case 'v': case 'w': case 'x':
|
||||||
|
case 'y': case 'z': case '_':
|
||||||
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||||
|
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
|
||||||
|
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
|
||||||
|
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
|
||||||
|
case 'Y': case 'Z': {
|
||||||
|
for(i=1; z[i] && (isalnum(z[i]) || z[i]=='_'); i++){}
|
||||||
|
*tokenType = sqliteKeywordCode(z, i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*tokenType = TK_ILLEGAL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Run the parser on the given SQL string. The parser structure is
|
||||||
|
** passed in. Return the number of errors.
|
||||||
|
*/
|
||||||
|
int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
|
||||||
|
int nErr = 0;
|
||||||
|
int i;
|
||||||
|
void *pEngine;
|
||||||
|
int once = 1;
|
||||||
|
static FILE *trace = 0;
|
||||||
|
extern void *sqliteParserAlloc(void*(*)(int));
|
||||||
|
extern void sqliteParserFree(void*, void(*)(void*));
|
||||||
|
extern int sqliteParser(void*, int, ...);
|
||||||
|
extern void sqliteParserTrace(FILE*, char *);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
pEngine = sqliteParserAlloc(sqliteMalloc);
|
||||||
|
if( pEngine==0 ){
|
||||||
|
sqliteSetString(pzErrMsg, "out of memory", 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
sqliteParserTrace(trace, "parser: ");
|
||||||
|
while( nErr==0 && i>=0 && zSql[i]!=0 ){
|
||||||
|
int tokenType;
|
||||||
|
|
||||||
|
pParse->sLastToken.z = &zSql[i];
|
||||||
|
pParse->sLastToken.n = sqliteGetToken(&zSql[i], &tokenType);
|
||||||
|
i += pParse->sLastToken.n;
|
||||||
|
if( once ){
|
||||||
|
pParse->sFirstToken = pParse->sLastToken;
|
||||||
|
once = 0;
|
||||||
|
}
|
||||||
|
switch( tokenType ){
|
||||||
|
case TK_SPACE:
|
||||||
|
break;
|
||||||
|
case TK_COMMENT: {
|
||||||
|
/* Various debugging modes can be turned on and off using
|
||||||
|
** special SQL comments. Check for the special comments
|
||||||
|
** here and take approriate action if found.
|
||||||
|
*/
|
||||||
|
char *z = pParse->sLastToken.z;
|
||||||
|
if( sqliteStrNICmp(z,"--parser-trace-on--",19)==0 ){
|
||||||
|
trace = stderr;
|
||||||
|
sqliteParserTrace(trace, "parser: ");
|
||||||
|
}else if( sqliteStrNICmp(z,"--parser-trace-off--", 20)==0 ){
|
||||||
|
trace = 0;
|
||||||
|
sqliteParserTrace(trace, "parser: ");
|
||||||
|
}else if( sqliteStrNICmp(z,"--vdbe-trace-on--",17)==0 ){
|
||||||
|
pParse->db->flags |= SQLITE_VdbeTrace;
|
||||||
|
}else if( sqliteStrNICmp(z,"--vdbe-trace-off--", 19)==0 ){
|
||||||
|
pParse->db->flags &= ~SQLITE_VdbeTrace;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_ILLEGAL:
|
||||||
|
sqliteSetNString(pzErrMsg, "illegal token: \"", -1,
|
||||||
|
pParse->sLastToken.z, pParse->sLastToken.n, 0);
|
||||||
|
nErr++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse);
|
||||||
|
if( pParse->zErrMsg ){
|
||||||
|
sqliteSetNString(pzErrMsg, "near \"", -1,
|
||||||
|
pParse->sErrToken.z, pParse->sErrToken.n,
|
||||||
|
"\": ", -1,
|
||||||
|
pParse->zErrMsg, -1,
|
||||||
|
0);
|
||||||
|
nErr++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( nErr==0 ){
|
||||||
|
sqliteParser(pEngine, 0, pParse->sLastToken, pParse);
|
||||||
|
if( pParse->zErrMsg ){
|
||||||
|
sqliteSetNString(pzErrMsg, "near \"", -1,
|
||||||
|
pParse->sErrToken.z, pParse->sErrToken.n,
|
||||||
|
"\": ", -1,
|
||||||
|
pParse->zErrMsg, -1,
|
||||||
|
0);
|
||||||
|
nErr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqliteParserFree(pEngine, sqliteFree);
|
||||||
|
if( pParse->zErrMsg ){
|
||||||
|
if( pzErrMsg ){
|
||||||
|
*pzErrMsg = pParse->zErrMsg;
|
||||||
|
}else{
|
||||||
|
sqliteFree(pParse->zErrMsg);
|
||||||
|
}
|
||||||
|
if( !nErr ) nErr++;
|
||||||
|
}
|
||||||
|
if( pParse->pVdbe ){
|
||||||
|
sqliteVdbeDelete(pParse->pVdbe);
|
||||||
|
pParse->pVdbe = 0;
|
||||||
|
}
|
||||||
|
if( pParse->pNewTable ){
|
||||||
|
sqliteDeleteTable(pParse->db, pParse->pNewTable);
|
||||||
|
pParse->pNewTable = 0;
|
||||||
|
}
|
||||||
|
return nErr;
|
||||||
|
}
|
445
src/util.c
Normal file
445
src/util.c
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** Utility functions used throughout sqlite.
|
||||||
|
**
|
||||||
|
** This file contains functions for allocating memory, comparing
|
||||||
|
** strings, and stuff like that.
|
||||||
|
**
|
||||||
|
** $Id: util.c,v 1.1 2000/05/29 14:26:02 drh Exp $
|
||||||
|
*/
|
||||||
|
#include "sqliteInt.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Allocate new memory and set it to zero. Return NULL if
|
||||||
|
** no memory is available.
|
||||||
|
*/
|
||||||
|
void *sqliteMalloc(int n){
|
||||||
|
void *p = malloc(n);
|
||||||
|
if( p==0 ) return 0;
|
||||||
|
memset(p, 0, n);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Free memory previously obtained from sqliteMalloc()
|
||||||
|
*/
|
||||||
|
void sqliteFree(void *p){
|
||||||
|
if( p ) free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Resize a prior allocation. If p==0, then this routine
|
||||||
|
** works just like sqliteMalloc(). If n==0, then this routine
|
||||||
|
** works just like sqliteFree().
|
||||||
|
*/
|
||||||
|
void *sqliteRealloc(void *p, int n){
|
||||||
|
if( p==0 ){
|
||||||
|
return sqliteMalloc(n);
|
||||||
|
}
|
||||||
|
if( n==0 ){
|
||||||
|
sqliteFree(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return realloc(p, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Create a string from the 2nd and subsequent arguments (up to the
|
||||||
|
** first NULL argument), store the string in memory obtained from
|
||||||
|
** sqliteMalloc() and make the pointer indicated by the 1st argument
|
||||||
|
** point to that string.
|
||||||
|
*/
|
||||||
|
void sqliteSetString(char **pz, const char *zFirst, ...){
|
||||||
|
va_list ap;
|
||||||
|
int nByte;
|
||||||
|
const char *z;
|
||||||
|
char *zResult;
|
||||||
|
|
||||||
|
if( pz==0 ) return;
|
||||||
|
nByte = strlen(zFirst) + 1;
|
||||||
|
va_start(ap, zFirst);
|
||||||
|
while( (z = va_arg(ap, const char*))!=0 ){
|
||||||
|
nByte += strlen(z);
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
sqliteFree(*pz);
|
||||||
|
*pz = zResult = sqliteMalloc( nByte );
|
||||||
|
if( zResult==0 ) return;
|
||||||
|
strcpy(zResult, zFirst);
|
||||||
|
zResult += strlen(zResult);
|
||||||
|
va_start(ap, zFirst);
|
||||||
|
while( (z = va_arg(ap, const char*))!=0 ){
|
||||||
|
strcpy(zResult, z);
|
||||||
|
zResult += strlen(zResult);
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Works like sqliteSetString, but each string is now followed by
|
||||||
|
** a length integer. -1 means use the whole string.
|
||||||
|
*/
|
||||||
|
void sqliteSetNString(char **pz, ...){
|
||||||
|
va_list ap;
|
||||||
|
int nByte;
|
||||||
|
const char *z;
|
||||||
|
char *zResult;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if( pz==0 ) return;
|
||||||
|
nByte = 0;
|
||||||
|
va_start(ap, pz);
|
||||||
|
while( (z = va_arg(ap, const char*))!=0 ){
|
||||||
|
n = va_arg(ap, int);
|
||||||
|
if( n<=0 ) n = strlen(z);
|
||||||
|
nByte += n;
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
sqliteFree(*pz);
|
||||||
|
*pz = zResult = sqliteMalloc( nByte + 1 );
|
||||||
|
if( zResult==0 ) return;
|
||||||
|
va_start(ap, pz);
|
||||||
|
while( (z = va_arg(ap, const char*))!=0 ){
|
||||||
|
n = va_arg(ap, int);
|
||||||
|
if( n<=0 ) n = strlen(z);
|
||||||
|
strncpy(zResult, z, n);
|
||||||
|
zResult += n;
|
||||||
|
}
|
||||||
|
*zResult = 0;
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An array to map all upper-case characters into their corresponding
|
||||||
|
** lower-case character.
|
||||||
|
*/
|
||||||
|
static unsigned char UpperToLower[] = {
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||||
|
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||||
|
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
|
||||||
|
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
|
||||||
|
104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
|
||||||
|
122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
|
||||||
|
108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
|
||||||
|
126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
|
||||||
|
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
|
||||||
|
162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
|
||||||
|
180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
|
||||||
|
198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
|
||||||
|
216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
|
||||||
|
234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
|
||||||
|
252,253,254,255
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function computes a hash on the name of a keyword.
|
||||||
|
** Case is not significant.
|
||||||
|
*/
|
||||||
|
int sqliteHashNoCase(const char *z, int n){
|
||||||
|
int h = 0;
|
||||||
|
int c;
|
||||||
|
if( n<=0 ) n = strlen(z);
|
||||||
|
while( n-- > 0 && (c = *z++)!=0 ){
|
||||||
|
h = h<<3 ^ h ^ UpperToLower[c];
|
||||||
|
}
|
||||||
|
if( h<0 ) h = -h;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Some system shave stricmp(). Others have strcasecmp(). Because
|
||||||
|
** there is no consistency, we will define our own.
|
||||||
|
*/
|
||||||
|
int sqliteStrICmp(const char *zLeft, const char *zRight){
|
||||||
|
register unsigned char *a, *b;
|
||||||
|
a = (unsigned char *)zLeft;
|
||||||
|
b = (unsigned char *)zRight;
|
||||||
|
while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
|
||||||
|
return *a - *b;
|
||||||
|
}
|
||||||
|
int sqliteStrNICmp(const char *zLeft, const char *zRight, int N){
|
||||||
|
register unsigned char *a, *b;
|
||||||
|
a = (unsigned char *)zLeft;
|
||||||
|
b = (unsigned char *)zRight;
|
||||||
|
while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
|
||||||
|
return N<=0 ? 0 : *a - *b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notes on string comparisions.
|
||||||
|
**
|
||||||
|
** We want the main string comparision function used for sorting to
|
||||||
|
** sort both numbers and alphanumeric words into the correct sequence.
|
||||||
|
** The same routine should do both without prior knowledge of which
|
||||||
|
** type of text the input represents. It should even work for strings
|
||||||
|
** which are a mixture of text and numbers.
|
||||||
|
**
|
||||||
|
** To accomplish this, we keep track of a state number while scanning
|
||||||
|
** the two strings. The states are as follows:
|
||||||
|
**
|
||||||
|
** 1 Beginning of word
|
||||||
|
** 2 Arbitrary text
|
||||||
|
** 3 Integer
|
||||||
|
** 4 Negative integer
|
||||||
|
** 5 Real number
|
||||||
|
** 6 Negative real
|
||||||
|
**
|
||||||
|
** The scan begins in state 1, beginning of word. Transitions to other
|
||||||
|
** states are determined by characters seen, as shown in the following
|
||||||
|
** chart:
|
||||||
|
**
|
||||||
|
** Current State Character Seen New State
|
||||||
|
** -------------------- -------------- -------------------
|
||||||
|
** 0 Beginning of word "-" 3 Negative integer
|
||||||
|
** digit 2 Integer
|
||||||
|
** space 0 Beginning of word
|
||||||
|
** otherwise 1 Arbitrary text
|
||||||
|
**
|
||||||
|
** 1 Arbitrary text space 0 Beginning of word
|
||||||
|
** digit 2 Integer
|
||||||
|
** otherwise 1 Arbitrary text
|
||||||
|
**
|
||||||
|
** 2 Integer space 0 Beginning of word
|
||||||
|
** "." 4 Real number
|
||||||
|
** digit 2 Integer
|
||||||
|
** otherwise 1 Arbitrary text
|
||||||
|
**
|
||||||
|
** 3 Negative integer space 0 Beginning of word
|
||||||
|
** "." 5 Negative Real num
|
||||||
|
** digit 3 Negative integer
|
||||||
|
** otherwise 1 Arbitrary text
|
||||||
|
**
|
||||||
|
** 4 Real number space 0 Beginning of word
|
||||||
|
** digit 4 Real number
|
||||||
|
** otherwise 1 Arbitrary text
|
||||||
|
**
|
||||||
|
** 5 Negative real num space 0 Beginning of word
|
||||||
|
** digit 5 Negative real num
|
||||||
|
** otherwise 1 Arbitrary text
|
||||||
|
**
|
||||||
|
** To implement this state machine, we first classify each character
|
||||||
|
** into on of the following categories:
|
||||||
|
**
|
||||||
|
** 0 Text
|
||||||
|
** 1 Space
|
||||||
|
** 2 Digit
|
||||||
|
** 3 "-"
|
||||||
|
** 4 "."
|
||||||
|
**
|
||||||
|
** Given an arbitrary character, the array charClass[] maps that character
|
||||||
|
** into one of the atove categories.
|
||||||
|
*/
|
||||||
|
static const unsigned char charClass[] = {
|
||||||
|
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||||
|
/* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
|
||||||
|
/* 1x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 2x */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0,
|
||||||
|
/* 3x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 4x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 5x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 6x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 7x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 8x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* 9x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* Ax */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* Bx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* Cx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* Dx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* Ex */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
/* Fx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
#define N_CHAR_CLASS 5
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Given the current state number (0 thru 5), this array figures
|
||||||
|
** the new state number given the character class.
|
||||||
|
*/
|
||||||
|
static const unsigned char stateMachine[] = {
|
||||||
|
/* Text, Space, Digit, "-", "." */
|
||||||
|
1, 0, 2, 3, 1, /* State 0: Beginning of word */
|
||||||
|
1, 0, 2, 1, 1, /* State 1: Arbitrary text */
|
||||||
|
1, 0, 2, 1, 4, /* State 2: Integer */
|
||||||
|
1, 0, 3, 1, 5, /* State 3: Negative integer */
|
||||||
|
1, 0, 4, 1, 1, /* State 4: Real number */
|
||||||
|
1, 0, 5, 1, 1, /* State 5: Negative real num */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* This routine does a comparison of two strings. Case is used only
|
||||||
|
** if useCase!=0. Numbers compare in numerical order.
|
||||||
|
*/
|
||||||
|
static int privateStrCmp(const char *atext, const char *btext, int useCase){
|
||||||
|
register unsigned char *a, *b, *map, ca, cb;
|
||||||
|
int result;
|
||||||
|
register int cclass = 0;
|
||||||
|
|
||||||
|
a = (unsigned char *)atext;
|
||||||
|
b = (unsigned char *)btext;
|
||||||
|
if( useCase ){
|
||||||
|
do{
|
||||||
|
if( (ca= *a++)!=(cb= *b++) ) break;
|
||||||
|
cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]];
|
||||||
|
}while( ca!=0 );
|
||||||
|
}else{
|
||||||
|
map = UpperToLower;
|
||||||
|
do{
|
||||||
|
if( (ca=map[*a++])!=(cb=map[*b++]) ) break;
|
||||||
|
cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]];
|
||||||
|
}while( ca!=0 );
|
||||||
|
}
|
||||||
|
switch( cclass ){
|
||||||
|
case 0:
|
||||||
|
case 1: {
|
||||||
|
if( isdigit(ca) && isdigit(cb) ){
|
||||||
|
cclass = 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch( cclass ){
|
||||||
|
case 2:
|
||||||
|
case 3: {
|
||||||
|
if( isdigit(ca) ){
|
||||||
|
if( isdigit(cb) ){
|
||||||
|
int acnt, bcnt;
|
||||||
|
acnt = bcnt = 0;
|
||||||
|
while( isdigit(*a++) ) acnt++;
|
||||||
|
while( isdigit(*b++) ) bcnt++;
|
||||||
|
result = acnt - bcnt;
|
||||||
|
if( result==0 ) result = ca-cb;
|
||||||
|
}else{
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
}else if( isdigit(cb) ){
|
||||||
|
result = -1;
|
||||||
|
}else if( ca=='.' ){
|
||||||
|
result = 1;
|
||||||
|
}else if( cb=='.' ){
|
||||||
|
result = -1;
|
||||||
|
}else{
|
||||||
|
result = ca - cb;
|
||||||
|
cclass = 2;
|
||||||
|
}
|
||||||
|
if( cclass==3 ) result = -result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 4: {
|
||||||
|
result = ca - cb;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
result = cb - ca;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This comparison routine is what we use for comparison operations
|
||||||
|
** in an SQL expression. (Ex: name<'Hello' or value<5). Compare two
|
||||||
|
** strings. Use case only as a tie-breaker. Numbers compare in
|
||||||
|
** numerical order.
|
||||||
|
*/
|
||||||
|
int sqliteCompare(const char *atext, const char *btext){
|
||||||
|
int result;
|
||||||
|
result = privateStrCmp(atext, btext, 0);
|
||||||
|
if( result==0 ) result = privateStrCmp(atext, btext, 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If you compile just this one file with the -DTEST_COMPARE=1 option,
|
||||||
|
** it generates a program to test the comparisons routines.
|
||||||
|
*/
|
||||||
|
#ifdef TEST_COMPARE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
int sortCmp(const char **a, const char **b){
|
||||||
|
return sqliteCompare(*a, *b);
|
||||||
|
}
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
int i, j, k, n;
|
||||||
|
static char *azStr[] = {
|
||||||
|
"abc", "aBc", "abcd", "aBcd",
|
||||||
|
"123", "124", "1234", "-123", "-124", "-1234",
|
||||||
|
"123.45", "123.456", "123.46", "-123.45", "-123.46", "-123.456",
|
||||||
|
"x9", "x10", "x-9", "x-10", "X9", "X10",
|
||||||
|
};
|
||||||
|
n = sizeof(azStr)/sizeof(azStr[0]);
|
||||||
|
qsort(azStr, n, sizeof(azStr[0]), sortCmp);
|
||||||
|
for(i=0; i<n; i++){
|
||||||
|
printf("%s\n", azStr[i]);
|
||||||
|
}
|
||||||
|
printf("Sanity1...");
|
||||||
|
fflush(stdout);
|
||||||
|
for(i=0; i<n-1; i++){
|
||||||
|
char *a = azStr[i];
|
||||||
|
for(j=i+1; j<n; j++){
|
||||||
|
char *b = azStr[j];
|
||||||
|
if( sqliteCompare(a,b) != -sqliteCompare(b,a) ){
|
||||||
|
printf("Failed! \"%s\" vs \"%s\"\n", a, b);
|
||||||
|
i = j = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( i<n ){
|
||||||
|
printf(" OK\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This routine is used for sorting. Each key is a list one or more
|
||||||
|
** null-terminated strings. The list is terminated by two null in
|
||||||
|
** a row. For example, the following text is strings:
|
||||||
|
**
|
||||||
|
** +one\000-two\000+three\000\000
|
||||||
|
**
|
||||||
|
** Both arguments will have the same number of strings. This routine
|
||||||
|
** returns negative, zero, or positive if the first argument is less
|
||||||
|
** than, equal to, or greater than the first. (Result is a-b).
|
||||||
|
**
|
||||||
|
** Every string begins with either a "+" or "-" character. If the
|
||||||
|
** character is "-" then the return value is negated. This is done
|
||||||
|
** to implement a sort in descending order.
|
||||||
|
*/
|
||||||
|
int sqliteSortCompare(const char *a, const char *b){
|
||||||
|
int len;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
while( res==0 && *a && *b ){
|
||||||
|
res = sqliteCompare(&a[1], &b[1]);
|
||||||
|
if( res==0 ){
|
||||||
|
len = strlen(a) + 1;
|
||||||
|
a += len;
|
||||||
|
b += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( *a=='-' ) res = -res;
|
||||||
|
return res;
|
||||||
|
}
|
1973
src/vdbe.c
Normal file
1973
src/vdbe.c
Normal file
File diff suppressed because it is too large
Load Diff
166
src/vdbe.h
Normal file
166
src/vdbe.h
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** Header file for the Virtual DataBase Engine (VDBE)
|
||||||
|
**
|
||||||
|
** This header defines the interface to the virtual database engine
|
||||||
|
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||||
|
** simple program to access and modify the underlying database.
|
||||||
|
**
|
||||||
|
** $Id: vdbe.h,v 1.1 2000/05/29 14:26:02 drh Exp $
|
||||||
|
*/
|
||||||
|
#ifndef _SQLITE_VDBE_H_
|
||||||
|
#define _SQLITE_VDBE_H_
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A single VDBE is an opaque structure named "Vdbe". Only routines
|
||||||
|
** in the source file sqliteVdbe.c are allowed to see the insides
|
||||||
|
** of this structure.
|
||||||
|
*/
|
||||||
|
typedef struct Vdbe Vdbe;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** A single instruction of the virtual machine has an opcode
|
||||||
|
** and as many as three operands. The instruction is recorded
|
||||||
|
** as an instance of the following structure:
|
||||||
|
*/
|
||||||
|
struct VdbeOp {
|
||||||
|
int opcode; /* What operation to perform */
|
||||||
|
int p1; /* First operand */
|
||||||
|
int p2; /* Second parameter (often the jump destination) */
|
||||||
|
char *p3; /* Third parameter */
|
||||||
|
};
|
||||||
|
typedef struct VdbeOp VdbeOp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following macro converts a relative address in the p2 field
|
||||||
|
** of a VdbeOp structure into a negative number so that
|
||||||
|
** sqliteVdbeAddOpList() knows that the address is relative. Calling
|
||||||
|
** the macro again restores the address.
|
||||||
|
*/
|
||||||
|
#define ADDR(X) (-1-(X))
|
||||||
|
|
||||||
|
/*
|
||||||
|
** These are the available opcodes.
|
||||||
|
**
|
||||||
|
** If any of the values changes or if opcodes are added or removed,
|
||||||
|
** be sure to also update the zOpName[] array in sqliteVdbe.c to
|
||||||
|
** mirror the change.
|
||||||
|
**
|
||||||
|
** The source tree contains an AWK script named renumberOps.awk that
|
||||||
|
** can be used to renumber these opcodes when new opcodes are inserted.
|
||||||
|
*/
|
||||||
|
#define OP_Open 1
|
||||||
|
#define OP_Close 2
|
||||||
|
#define OP_Destroy 3
|
||||||
|
#define OP_Fetch 4
|
||||||
|
#define OP_New 5
|
||||||
|
#define OP_Put 6
|
||||||
|
#define OP_Delete 7
|
||||||
|
#define OP_Field 8
|
||||||
|
#define OP_Key 9
|
||||||
|
#define OP_Rewind 10
|
||||||
|
#define OP_Next 11
|
||||||
|
#define OP_ResetIdx 12
|
||||||
|
#define OP_NextIdx 13
|
||||||
|
#define OP_PutIdx 14
|
||||||
|
#define OP_DeleteIdx 15
|
||||||
|
|
||||||
|
#define OP_ListOpen 16
|
||||||
|
#define OP_ListWrite 17
|
||||||
|
#define OP_ListRewind 18
|
||||||
|
#define OP_ListRead 19
|
||||||
|
#define OP_ListClose 20
|
||||||
|
|
||||||
|
#define OP_SortOpen 21
|
||||||
|
#define OP_SortPut 22
|
||||||
|
#define OP_SortMakeRec 23
|
||||||
|
#define OP_SortMakeKey 24
|
||||||
|
#define OP_Sort 25
|
||||||
|
#define OP_SortNext 26
|
||||||
|
#define OP_SortKey 27
|
||||||
|
#define OP_SortCallback 28
|
||||||
|
#define OP_SortClose 29
|
||||||
|
|
||||||
|
#define OP_MakeRecord 30
|
||||||
|
#define OP_MakeKey 31
|
||||||
|
|
||||||
|
#define OP_Goto 32
|
||||||
|
#define OP_If 33
|
||||||
|
#define OP_Halt 34
|
||||||
|
|
||||||
|
#define OP_ColumnCount 35
|
||||||
|
#define OP_ColumnName 36
|
||||||
|
#define OP_Callback 37
|
||||||
|
|
||||||
|
#define OP_Integer 38
|
||||||
|
#define OP_String 39
|
||||||
|
#define OP_Pop 40
|
||||||
|
#define OP_Dup 41
|
||||||
|
#define OP_Pull 42
|
||||||
|
|
||||||
|
#define OP_Add 43
|
||||||
|
#define OP_AddImm 44
|
||||||
|
#define OP_Subtract 45
|
||||||
|
#define OP_Multiply 46
|
||||||
|
#define OP_Divide 47
|
||||||
|
#define OP_Min 48
|
||||||
|
#define OP_Max 49
|
||||||
|
#define OP_Eq 50
|
||||||
|
#define OP_Ne 51
|
||||||
|
#define OP_Lt 52
|
||||||
|
#define OP_Le 53
|
||||||
|
#define OP_Gt 54
|
||||||
|
#define OP_Ge 55
|
||||||
|
#define OP_IsNull 56
|
||||||
|
#define OP_NotNull 57
|
||||||
|
#define OP_Negative 58
|
||||||
|
#define OP_And 59
|
||||||
|
#define OP_Or 60
|
||||||
|
#define OP_Not 61
|
||||||
|
#define OP_Concat 62
|
||||||
|
#define OP_Noop 63
|
||||||
|
|
||||||
|
#define OP_MAX 63
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Prototypes for the VDBE interface. See comments on the implementation
|
||||||
|
** for a description of what each of these routines does.
|
||||||
|
*/
|
||||||
|
Vdbe *sqliteVdbeCreate(Dbbe*);
|
||||||
|
int sqliteVdbeAddOp(Vdbe*,int,int,int,const char*,int);
|
||||||
|
int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp);
|
||||||
|
void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
|
||||||
|
void sqliteVdbeDequoteP3(Vdbe*, int addr);
|
||||||
|
int sqliteVdbeMakeLabel(Vdbe*);
|
||||||
|
void sqliteVdbeDelete(Vdbe*);
|
||||||
|
int sqliteVdbeOpcode(const char *zName);
|
||||||
|
int sqliteVdbeExec(Vdbe*,sqlite_callback,void*,char**);
|
||||||
|
int sqliteVdbeList(Vdbe*,sqlite_callback,void*,char**);
|
||||||
|
void sqliteVdbeResolveLabel(Vdbe*, int);
|
||||||
|
int sqliteVdbeCurrentAddr(Vdbe*);
|
||||||
|
void sqliteVdbeTrace(Vdbe*,FILE*);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
566
src/where.c
Normal file
566
src/where.c
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** This module contains C code that generates VDBE code used to process
|
||||||
|
** the WHERE clause of SQL statements. Also found here are subroutines
|
||||||
|
** to generate VDBE code to evaluate expressions.
|
||||||
|
**
|
||||||
|
** $Id: where.c,v 1.1 2000/05/29 14:26:02 drh Exp $
|
||||||
|
*/
|
||||||
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The query generator uses an array of instances of this structure to
|
||||||
|
** help it analyze the subexpressions of the WHERE clause. Each WHERE
|
||||||
|
** clause subexpression is separated from the others by an AND operator.
|
||||||
|
*/
|
||||||
|
typedef struct ExprInfo ExprInfo;
|
||||||
|
struct ExprInfo {
|
||||||
|
Expr *p; /* Pointer to the subexpression */
|
||||||
|
int indexable; /* True if this subexprssion is usable by an index */
|
||||||
|
int idxLeft; /* p->pLeft is a field in this table number. -1 if
|
||||||
|
** p->pLeft is not the field of any table */
|
||||||
|
int idxRight; /* p->pRight is a field in this table number. -1 if
|
||||||
|
** p->pRight is not the field of any table */
|
||||||
|
unsigned prereqLeft; /* Tables referenced by p->pLeft */
|
||||||
|
unsigned prereqRight; /* Tables referenced by p->pRight */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Determine the number of elements in an array.
|
||||||
|
*/
|
||||||
|
#define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This routine is used to divide the WHERE expression into subexpressions
|
||||||
|
** separated by the AND operator.
|
||||||
|
**
|
||||||
|
** aSlot[] is an array of subexpressions structures.
|
||||||
|
** There are nSlot spaces left in this array. This routine attempts to
|
||||||
|
** split pExpr into subexpressions and fills aSlot[] with those subexpressions.
|
||||||
|
** The return value is the number of slots filled.
|
||||||
|
*/
|
||||||
|
static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){
|
||||||
|
int cnt = 0;
|
||||||
|
if( pExpr==0 || nSlot<1 ) return 0;
|
||||||
|
if( nSlot==1 || pExpr->op!=TK_AND ){
|
||||||
|
aSlot[0].p = pExpr;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if( pExpr->pLeft->op!=TK_AND ){
|
||||||
|
aSlot[0].p = pExpr->pLeft;
|
||||||
|
cnt = 1 + exprSplit(nSlot-1, &aSlot[1], pExpr->pRight);
|
||||||
|
}else{
|
||||||
|
cnt = exprSplit(nSlot, aSlot, pExpr->pRight);
|
||||||
|
cnt += exprSplit(nSlot-cnt, &aSlot[cnt], pExpr->pLeft);
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This routine walks (recursively) an expression tree and generates
|
||||||
|
** a bitmask indicating which tables are used in that expression
|
||||||
|
** tree. Bit 0 of the mask is set if table 0 is used. But 1 is set
|
||||||
|
** if table 1 is used. And so forth.
|
||||||
|
**
|
||||||
|
** In order for this routine to work, the calling function must have
|
||||||
|
** previously invoked sqliteExprResolveIds() on the expression. See
|
||||||
|
** the header comment on that routine for additional information.
|
||||||
|
*/
|
||||||
|
static int exprTableUsage(Expr *p){
|
||||||
|
unsigned int mask = 0;
|
||||||
|
if( p==0 ) return 0;
|
||||||
|
if( p->op==TK_FIELD ){
|
||||||
|
return 1<<p->iTable;
|
||||||
|
}
|
||||||
|
if( p->pRight ){
|
||||||
|
mask = exprTableUsage(p->pRight);
|
||||||
|
}
|
||||||
|
if( p->pLeft ){
|
||||||
|
mask |= exprTableUsage(p->pLeft);
|
||||||
|
}
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The input to this routine is an ExprInfo structure with only the
|
||||||
|
** "p" field filled in. The job of this routine is to analyze the
|
||||||
|
** subexpression and populate all the other fields of the ExprInfo
|
||||||
|
** structure.
|
||||||
|
*/
|
||||||
|
static void exprAnalyze(ExprInfo *pInfo){
|
||||||
|
Expr *pExpr = pInfo->p;
|
||||||
|
pInfo->prereqLeft = exprTableUsage(pExpr->pLeft);
|
||||||
|
pInfo->prereqRight = exprTableUsage(pExpr->pRight);
|
||||||
|
pInfo->indexable = 0;
|
||||||
|
pInfo->idxLeft = -1;
|
||||||
|
pInfo->idxRight = -1;
|
||||||
|
if( pExpr->op==TK_EQ && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
|
||||||
|
if( pExpr->pRight->op==TK_FIELD ){
|
||||||
|
pInfo->idxRight = pExpr->pRight->iTable;
|
||||||
|
pInfo->indexable = 1;
|
||||||
|
}
|
||||||
|
if( pExpr->pLeft->op==TK_FIELD ){
|
||||||
|
pInfo->idxLeft = pExpr->pLeft->iTable;
|
||||||
|
pInfo->indexable = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Generating the beginning of the loop used for WHERE clause processing.
|
||||||
|
** The return value is a pointer to an (opaque) structure that contains
|
||||||
|
** information needed to terminate the loop. Later, the calling routine
|
||||||
|
** should invoke sqliteWhereEnd() with the return value of this function
|
||||||
|
** in order to complete the WHERE clause processing.
|
||||||
|
**
|
||||||
|
** If an error occurs, this routine returns NULL.
|
||||||
|
*/
|
||||||
|
WhereInfo *sqliteWhereBegin(
|
||||||
|
Parse *pParse, /* The parser context */
|
||||||
|
IdList *pTabList, /* A list of all tables */
|
||||||
|
Expr *pWhere, /* The WHERE clause */
|
||||||
|
int pushKey /* If TRUE, leave the table key on the stack */
|
||||||
|
){
|
||||||
|
int i; /* Loop counter */
|
||||||
|
WhereInfo *pWInfo; /* Will become the return value of this function */
|
||||||
|
Vdbe *v = pParse->pVdbe; /* The virtual database engine */
|
||||||
|
int brk, cont; /* Addresses used during code generation */
|
||||||
|
int *aOrder; /* Order in which pTabList entries are searched */
|
||||||
|
int nExpr; /* Number of subexpressions in the WHERE clause */
|
||||||
|
int loopMask; /* One bit set for each outer loop */
|
||||||
|
int haveKey; /* True if KEY is on the stack */
|
||||||
|
Index *aIdx[32]; /* Index to use on each nested loop. */
|
||||||
|
ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */
|
||||||
|
|
||||||
|
/* Allocate space for aOrder[]. */
|
||||||
|
aOrder = sqliteMalloc( sizeof(int) * pTabList->nId );
|
||||||
|
|
||||||
|
/* Allocate and initialize the WhereInfo structure that will become the
|
||||||
|
** return value.
|
||||||
|
*/
|
||||||
|
pWInfo = sqliteMalloc( sizeof(WhereInfo) );
|
||||||
|
if( pWInfo==0 ){
|
||||||
|
sqliteFree(aOrder);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pWInfo->pParse = pParse;
|
||||||
|
pWInfo->pTabList = pTabList;
|
||||||
|
|
||||||
|
/* Split the WHERE clause into as many as 32 separate subexpressions
|
||||||
|
** where each subexpression is separated by an AND operator. Any additional
|
||||||
|
** subexpressions are attached in the aExpr[32] and will not enter
|
||||||
|
** into the query optimizer computations. 32 is chosen as the cutoff
|
||||||
|
** since that is the number of bits in an integer that we use for an
|
||||||
|
** expression-used mask.
|
||||||
|
*/
|
||||||
|
memset(aExpr, 0, sizeof(aExpr));
|
||||||
|
nExpr = exprSplit(ARRAYSIZE(aExpr), aExpr, pWhere);
|
||||||
|
|
||||||
|
/* Analyze all of the subexpressions.
|
||||||
|
*/
|
||||||
|
for(i=0; i<nExpr; i++){
|
||||||
|
exprAnalyze(&aExpr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Figure out a good nesting order for the tables. aOrder[0] will
|
||||||
|
** be the index in pTabList of the outermost table. aOrder[1] will
|
||||||
|
** be the first nested loop and so on. aOrder[pTabList->nId-1] will
|
||||||
|
** be the innermost loop.
|
||||||
|
**
|
||||||
|
** Someday will put in a good algorithm here to reorder to the loops
|
||||||
|
** for an effiecient query. But for now, just use whatever order the
|
||||||
|
** tables appear in in the pTabList.
|
||||||
|
*/
|
||||||
|
for(i=0; i<pTabList->nId; i++){
|
||||||
|
aOrder[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Figure out what index to use (if any) for each nested loop.
|
||||||
|
** Make aIdx[i] point to the index to use for the i-th nested loop
|
||||||
|
** where i==0 is the outer loop and i==pTabList->nId-1 is the inner
|
||||||
|
** loop.
|
||||||
|
**
|
||||||
|
** Actually, if there are more than 32 tables in the join, only the
|
||||||
|
** first 32 tables are candidates for indices.
|
||||||
|
*/
|
||||||
|
loopMask = 0;
|
||||||
|
for(i=0; i<pTabList->nId && i<ARRAYSIZE(aIdx); i++){
|
||||||
|
int idx = aOrder[i];
|
||||||
|
Table *pTab = pTabList->a[idx].pTab;
|
||||||
|
Index *pIdx;
|
||||||
|
Index *pBestIdx = 0;
|
||||||
|
|
||||||
|
/* Do a search for usable indices. Leave pBestIdx pointing to
|
||||||
|
** most specific usable index.
|
||||||
|
**
|
||||||
|
** "Most specific" means that pBestIdx is the usable index that
|
||||||
|
** has the largest value for nField. A usable index is one for
|
||||||
|
** which there are subexpressions to compute every field of the
|
||||||
|
** index.
|
||||||
|
*/
|
||||||
|
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||||
|
int j;
|
||||||
|
int fieldMask = 0;
|
||||||
|
|
||||||
|
if( pIdx->nField>32 ) continue;
|
||||||
|
for(j=0; j<nExpr; j++){
|
||||||
|
if( aExpr[j].idxLeft==idx
|
||||||
|
&& (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
|
||||||
|
int iField = aExpr[j].p->pLeft->iField;
|
||||||
|
int k;
|
||||||
|
for(k=0; k<pIdx->nField; k++){
|
||||||
|
if( pIdx->aiField[k]==iField ){
|
||||||
|
fieldMask |= 1<<k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( aExpr[j].idxRight==idx
|
||||||
|
&& (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ){
|
||||||
|
int iField = aExpr[j].p->pRight->iField;
|
||||||
|
int k;
|
||||||
|
for(k=0; k<pIdx->nField; k++){
|
||||||
|
if( pIdx->aiField[k]==iField ){
|
||||||
|
fieldMask |= 1<<k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( fieldMask + 1 == (1<<pIdx->nField) ){
|
||||||
|
if( pBestIdx==0 || pBestIdx->nField<pIdx->nField ){
|
||||||
|
pBestIdx = pIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aIdx[i] = pBestIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open all tables in the pTabList and all indices in aIdx[].
|
||||||
|
*/
|
||||||
|
for(i=0; i<pTabList->nId; i++){
|
||||||
|
sqliteVdbeAddOp(v, OP_Open, i, 0, pTabList->a[i].pTab->zName, 0);
|
||||||
|
if( i<ARRAYSIZE(aIdx) && aIdx[i]!=0 ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Open, pTabList->nId+i, 0, aIdx[i]->zName, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate the code to do the search
|
||||||
|
*/
|
||||||
|
pWInfo->iBreak = brk = sqliteVdbeMakeLabel(v);
|
||||||
|
loopMask = 0;
|
||||||
|
for(i=0; i<pTabList->nId; i++){
|
||||||
|
int j, k;
|
||||||
|
int idx = aOrder[i];
|
||||||
|
Index *pIdx = i<ARRAYSIZE(aIdx) ? aIdx[i] : 0;
|
||||||
|
|
||||||
|
cont = sqliteVdbeMakeLabel(v);
|
||||||
|
if( pIdx==0 ){
|
||||||
|
/* Case 1: There was no usable index. We must do a complete
|
||||||
|
** scan of the table.
|
||||||
|
*/
|
||||||
|
sqliteVdbeAddOp(v, OP_Next, idx, brk, 0, cont);
|
||||||
|
haveKey = 0;
|
||||||
|
}else{
|
||||||
|
/* Case 2: We do have a usable index in pIdx.
|
||||||
|
*/
|
||||||
|
for(j=0; j<pIdx->nField; j++){
|
||||||
|
for(k=0; k<nExpr; k++){
|
||||||
|
if( aExpr[k].p==0 ) continue;
|
||||||
|
if( aExpr[k].idxLeft==idx
|
||||||
|
&& (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight
|
||||||
|
&& aExpr[k].p->pLeft->iField==pIdx->aiField[j]
|
||||||
|
){
|
||||||
|
sqliteExprCode(pParse, aExpr[k].p->pRight);
|
||||||
|
aExpr[k].p = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if( aExpr[k].idxRight==idx
|
||||||
|
&& (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
|
||||||
|
&& aExpr[k].p->pRight->iField==pIdx->aiField[j]
|
||||||
|
){
|
||||||
|
sqliteExprCode(pParse, aExpr[k].p->pLeft);
|
||||||
|
aExpr[k].p = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_Fetch, pTabList->nId+i, 0, 0, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_NextIdx, pTabList->nId+i, brk, 0, cont);
|
||||||
|
if( i==pTabList->nId-1 && pushKey ){
|
||||||
|
haveKey = 1;
|
||||||
|
}else{
|
||||||
|
sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0);
|
||||||
|
haveKey = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loopMask |= 1<<idx;
|
||||||
|
|
||||||
|
/* Insert code to test every subexpression that can be completely
|
||||||
|
** computed using the current set of tables.
|
||||||
|
*/
|
||||||
|
for(j=0; j<nExpr; j++){
|
||||||
|
if( aExpr[j].p==0 ) continue;
|
||||||
|
if( (aExpr[j].prereqRight & loopMask)!=aExpr[j].prereqRight ) continue;
|
||||||
|
if( (aExpr[j].prereqLeft & loopMask)!=aExpr[j].prereqLeft ) continue;
|
||||||
|
if( haveKey ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0);
|
||||||
|
haveKey = 0;
|
||||||
|
}
|
||||||
|
sqliteExprIfFalse(pParse, aExpr[j].p, cont);
|
||||||
|
aExpr[j].p = 0;
|
||||||
|
}
|
||||||
|
brk = cont;
|
||||||
|
}
|
||||||
|
pWInfo->iContinue = cont;
|
||||||
|
if( pushKey && !haveKey ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
sqliteFree(aOrder);
|
||||||
|
return pWInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Generate the end of the WHERE loop.
|
||||||
|
*/
|
||||||
|
void sqliteWhereEnd(WhereInfo *pWInfo){
|
||||||
|
Vdbe *v = pWInfo->pParse->pVdbe;
|
||||||
|
sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, pWInfo->iBreak);
|
||||||
|
sqliteFree(pWInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Generate code into the current Vdbe to evaluate the given
|
||||||
|
** expression and leave the result on the stack.
|
||||||
|
*/
|
||||||
|
void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||||
|
Vdbe *v = pParse->pVdbe;
|
||||||
|
int op;
|
||||||
|
switch( pExpr->op ){
|
||||||
|
case TK_PLUS: op = OP_Add; break;
|
||||||
|
case TK_MINUS: op = OP_Subtract; break;
|
||||||
|
case TK_STAR: op = OP_Multiply; break;
|
||||||
|
case TK_SLASH: op = OP_Divide; break;
|
||||||
|
case TK_AND: op = OP_And; break;
|
||||||
|
case TK_OR: op = OP_Or; break;
|
||||||
|
case TK_LT: op = OP_Lt; break;
|
||||||
|
case TK_LE: op = OP_Le; break;
|
||||||
|
case TK_GT: op = OP_Gt; break;
|
||||||
|
case TK_GE: op = OP_Ge; break;
|
||||||
|
case TK_NE: op = OP_Ne; break;
|
||||||
|
case TK_EQ: op = OP_Eq; break;
|
||||||
|
case TK_ISNULL: op = OP_IsNull; break;
|
||||||
|
case TK_NOTNULL: op = OP_NotNull; break;
|
||||||
|
case TK_NOT: op = OP_Not; break;
|
||||||
|
case TK_UMINUS: op = OP_Negative; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
switch( pExpr->op ){
|
||||||
|
case TK_FIELD: {
|
||||||
|
sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iField, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_INTEGER: {
|
||||||
|
int i = atoi(pExpr->token.z);
|
||||||
|
sqliteVdbeAddOp(v, OP_Integer, i, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_FLOAT: {
|
||||||
|
int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
|
||||||
|
sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_STRING: {
|
||||||
|
int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
|
||||||
|
sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
|
||||||
|
sqliteVdbeDequoteP3(v, addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_NULL: {
|
||||||
|
sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_AND:
|
||||||
|
case TK_OR:
|
||||||
|
case TK_PLUS:
|
||||||
|
case TK_STAR:
|
||||||
|
case TK_MINUS:
|
||||||
|
case TK_SLASH: {
|
||||||
|
sqliteExprCode(pParse, pExpr->pLeft);
|
||||||
|
sqliteExprCode(pParse, pExpr->pRight);
|
||||||
|
sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_LT:
|
||||||
|
case TK_LE:
|
||||||
|
case TK_GT:
|
||||||
|
case TK_GE:
|
||||||
|
case TK_NE:
|
||||||
|
case TK_EQ: {
|
||||||
|
int dest;
|
||||||
|
sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
|
||||||
|
sqliteExprCode(pParse, pExpr->pLeft);
|
||||||
|
sqliteExprCode(pParse, pExpr->pRight);
|
||||||
|
dest = sqliteVdbeCurrentAddr(v) + 2;
|
||||||
|
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_NOT:
|
||||||
|
case TK_UMINUS: {
|
||||||
|
sqliteExprCode(pParse, pExpr->pLeft);
|
||||||
|
sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_ISNULL:
|
||||||
|
case TK_NOTNULL: {
|
||||||
|
int dest;
|
||||||
|
sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
|
||||||
|
sqliteExprCode(pParse, pExpr->pLeft);
|
||||||
|
dest = sqliteVdbeCurrentAddr(v) + 2;
|
||||||
|
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Generate code for a boolean expression such that a jump is made
|
||||||
|
** to the label "dest" if the expression is true but execution
|
||||||
|
** continues straight thru if the expression is false.
|
||||||
|
*/
|
||||||
|
void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
|
||||||
|
Vdbe *v = pParse->pVdbe;
|
||||||
|
int op = 0;
|
||||||
|
switch( pExpr->op ){
|
||||||
|
case TK_LT: op = OP_Lt; break;
|
||||||
|
case TK_LE: op = OP_Le; break;
|
||||||
|
case TK_GT: op = OP_Gt; break;
|
||||||
|
case TK_GE: op = OP_Ge; break;
|
||||||
|
case TK_NE: op = OP_Ne; break;
|
||||||
|
case TK_EQ: op = OP_Eq; break;
|
||||||
|
case TK_ISNULL: op = OP_IsNull; break;
|
||||||
|
case TK_NOTNULL: op = OP_NotNull; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
switch( pExpr->op ){
|
||||||
|
case TK_AND: {
|
||||||
|
int d2 = sqliteVdbeMakeLabel(v);
|
||||||
|
sqliteExprIfFalse(pParse, pExpr->pLeft, d2);
|
||||||
|
sqliteExprIfTrue(pParse, pExpr->pRight, dest);
|
||||||
|
sqliteVdbeResolveLabel(v, d2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_OR: {
|
||||||
|
sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
|
||||||
|
sqliteExprIfTrue(pParse, pExpr->pRight, dest);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_LT:
|
||||||
|
case TK_LE:
|
||||||
|
case TK_GT:
|
||||||
|
case TK_GE:
|
||||||
|
case TK_NE:
|
||||||
|
case TK_EQ: {
|
||||||
|
sqliteExprCode(pParse, pExpr->pLeft);
|
||||||
|
sqliteExprCode(pParse, pExpr->pRight);
|
||||||
|
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_ISNULL:
|
||||||
|
case TK_NOTNULL: {
|
||||||
|
sqliteExprCode(pParse, pExpr->pLeft);
|
||||||
|
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
sqliteExprCode(pParse, pExpr);
|
||||||
|
sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Generate code for boolean expression such that a jump is made
|
||||||
|
** to the label "dest" if the expression is false but execution
|
||||||
|
** continues straight thru if the expression is true.
|
||||||
|
*/
|
||||||
|
void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
|
||||||
|
Vdbe *v = pParse->pVdbe;
|
||||||
|
int op = 0;
|
||||||
|
switch( pExpr->op ){
|
||||||
|
case TK_LT: op = OP_Ge; break;
|
||||||
|
case TK_LE: op = OP_Gt; break;
|
||||||
|
case TK_GT: op = OP_Le; break;
|
||||||
|
case TK_GE: op = OP_Lt; break;
|
||||||
|
case TK_NE: op = OP_Eq; break;
|
||||||
|
case TK_EQ: op = OP_Ne; break;
|
||||||
|
case TK_ISNULL: op = OP_NotNull; break;
|
||||||
|
case TK_NOTNULL: op = OP_IsNull; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
switch( pExpr->op ){
|
||||||
|
case TK_AND: {
|
||||||
|
sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
|
||||||
|
sqliteExprIfFalse(pParse, pExpr->pRight, dest);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_OR: {
|
||||||
|
int d2 = sqliteVdbeMakeLabel(v);
|
||||||
|
sqliteExprIfTrue(pParse, pExpr->pLeft, d2);
|
||||||
|
sqliteExprIfFalse(pParse, pExpr->pRight, dest);
|
||||||
|
sqliteVdbeResolveLabel(v, d2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_LT:
|
||||||
|
case TK_LE:
|
||||||
|
case TK_GT:
|
||||||
|
case TK_GE:
|
||||||
|
case TK_NE:
|
||||||
|
case TK_EQ: {
|
||||||
|
sqliteExprCode(pParse, pExpr->pLeft);
|
||||||
|
sqliteExprCode(pParse, pExpr->pRight);
|
||||||
|
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TK_ISNULL:
|
||||||
|
case TK_NOTNULL: {
|
||||||
|
sqliteExprCode(pParse, pExpr->pLeft);
|
||||||
|
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
sqliteExprCode(pParse, pExpr);
|
||||||
|
sqliteVdbeAddOp(v, OP_Not, 0, 0, 0, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
tool/gdbmdump.c
Normal file
90
tool/gdbmdump.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 1999, 2000 D. Richard Hipp
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 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 General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Author contact information:
|
||||||
|
** drh@hwaci.com
|
||||||
|
** http://www.hwaci.com/drh/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** A utility to dump the entire contents of a GDBM table in a
|
||||||
|
** readable format.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <gdbm.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static void print_data(char *zPrefix, datum p){
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
printf("%-5s: ", zPrefix);
|
||||||
|
for(i=0; i<p.dsize; i+=20){
|
||||||
|
for(j=i; j<p.dsize && j<i+20; j++){
|
||||||
|
printf("%02x", 0xff & p.dptr[j]);
|
||||||
|
if( (j&3)==3 ) printf(" ");
|
||||||
|
}
|
||||||
|
while( j<i+20 ){
|
||||||
|
printf(" ");
|
||||||
|
if( (j&3)==3 ) printf(" ");
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
printf(" ");
|
||||||
|
for(j=i; j<p.dsize && j<i+20; j++){
|
||||||
|
int c = p.dptr[j];
|
||||||
|
if( !isprint(c) ){ c = '.'; }
|
||||||
|
putchar(c);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
if( i+20<p.dsize ) printf(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gdbm_dump(char *zFilename){
|
||||||
|
GDBM_FILE p;
|
||||||
|
datum data, key, next;
|
||||||
|
|
||||||
|
p = gdbm_open(zFilename, 0, GDBM_READER, 0, 0);
|
||||||
|
if( p==0 ){
|
||||||
|
fprintf(stderr,"can't open file \"%s\"\n", zFilename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
key = gdbm_firstkey(p);
|
||||||
|
while( key.dptr ){
|
||||||
|
print_data("key",key);
|
||||||
|
data = gdbm_fetch(p, key);
|
||||||
|
if( data.dptr ){
|
||||||
|
print_data("data",data);
|
||||||
|
free( data.dptr );
|
||||||
|
}
|
||||||
|
next = gdbm_nextkey(p, key);
|
||||||
|
free( key.dptr );
|
||||||
|
key = next;
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
gdbm_close(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
int i;
|
||||||
|
int nErr = 0;
|
||||||
|
for(i=1; i<argc; i++){
|
||||||
|
nErr += gdbm_dump(argv[i]);
|
||||||
|
}
|
||||||
|
return nErr;
|
||||||
|
}
|
4021
tool/lemon.c
Normal file
4021
tool/lemon.c
Normal file
File diff suppressed because it is too large
Load Diff
606
tool/lempar.c
Normal file
606
tool/lempar.c
Normal file
@ -0,0 +1,606 @@
|
|||||||
|
/* Driver template for the LEMON parser generator.
|
||||||
|
** Copyright 1991-1995 by D. Richard Hipp.
|
||||||
|
*
|
||||||
|
* This version is specially modified for use with sqlite.
|
||||||
|
* @(#) $Id: lempar.c,v 1.1 2000/05/29 14:26:02 drh Exp $
|
||||||
|
*
|
||||||
|
**
|
||||||
|
** This library is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of the GNU Library General Public
|
||||||
|
** License as published by the Free Software Foundation; either
|
||||||
|
** version 2 of the License, or (at your option) any later version.
|
||||||
|
**
|
||||||
|
** This library 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
|
||||||
|
** Library General Public License for more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU Library General Public
|
||||||
|
** License along with this library; if not, write to the
|
||||||
|
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
** Boston, MA 02111-1307, USA.
|
||||||
|
**
|
||||||
|
** Modified 1997 to make it suitable for use with makeheaders.
|
||||||
|
*/
|
||||||
|
/* First off, code is include which follows the "include" declaration
|
||||||
|
** in the input file. */
|
||||||
|
#include <stdio.h>
|
||||||
|
%%
|
||||||
|
/* Next is all token values, in a form suitable for use by makeheaders.
|
||||||
|
** This section will be null unless lemon is run with the -m switch.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
** These constants (all generated automatically by the parser generator)
|
||||||
|
** specify the various kinds of tokens (terminals) that the parser
|
||||||
|
** understands.
|
||||||
|
**
|
||||||
|
** Each symbol here is a terminal symbol in the grammar.
|
||||||
|
*/
|
||||||
|
%%
|
||||||
|
/* Make sure the INTERFACE macro is defined.
|
||||||
|
*/
|
||||||
|
#ifndef INTERFACE
|
||||||
|
# define INTERFACE 1
|
||||||
|
#endif
|
||||||
|
/* The next thing included is series of defines which control
|
||||||
|
** various aspects of the generated parser.
|
||||||
|
** YYCODETYPE is the data type used for storing terminal
|
||||||
|
** and nonterminal numbers. "unsigned char" is
|
||||||
|
** used if there are fewer than 250 terminals
|
||||||
|
** and nonterminals. "int" is used otherwise.
|
||||||
|
** YYNOCODE is a number of type YYCODETYPE which corresponds
|
||||||
|
** to no legal terminal or nonterminal number. This
|
||||||
|
** number is used to fill in empty slots of the hash
|
||||||
|
** table.
|
||||||
|
** YYACTIONTYPE is the data type used for storing terminal
|
||||||
|
** and nonterminal numbers. "unsigned char" is
|
||||||
|
** used if there are fewer than 250 rules and
|
||||||
|
** states combined. "int" is used otherwise.
|
||||||
|
** ParseTOKENTYPE is the data type used for minor tokens given
|
||||||
|
** directly to the parser from the tokenizer.
|
||||||
|
** YYMINORTYPE is the data type used for all minor tokens.
|
||||||
|
** This is typically a union of many types, one of
|
||||||
|
** which is ParseTOKENTYPE. The entry in the union
|
||||||
|
** for base tokens is called "yy0".
|
||||||
|
** YYSTACKDEPTH is the maximum depth of the parser's stack.
|
||||||
|
** ParseARGDECL is a declaration of a 3rd argument to the
|
||||||
|
** parser, or null if there is no extra argument.
|
||||||
|
** ParseKRARGDECL A version of ParseARGDECL for K&R C.
|
||||||
|
** ParseANSIARGDECL A version of ParseARGDECL for ANSI C.
|
||||||
|
** YYNSTATE the combined number of states.
|
||||||
|
** YYNRULE the number of rules in the grammar
|
||||||
|
** YYERRORSYMBOL is the code number of the error symbol. If not
|
||||||
|
** defined, then do no error processing.
|
||||||
|
*/
|
||||||
|
%%
|
||||||
|
#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
|
||||||
|
#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
|
||||||
|
#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
|
||||||
|
/* Next is the action table. Each entry in this table contains
|
||||||
|
**
|
||||||
|
** + An integer which is the number representing the look-ahead
|
||||||
|
** token
|
||||||
|
**
|
||||||
|
** + An integer indicating what action to take. Number (N) between
|
||||||
|
** 0 and YYNSTATE-1 mean shift the look-ahead and go to state N.
|
||||||
|
** Numbers between YYNSTATE and YYNSTATE+YYNRULE-1 mean reduce by
|
||||||
|
** rule N-YYNSTATE. Number YYNSTATE+YYNRULE means that a syntax
|
||||||
|
** error has occurred. Number YYNSTATE+YYNRULE+1 means the parser
|
||||||
|
** accepts its input.
|
||||||
|
**
|
||||||
|
** + A pointer to the next entry with the same hash value.
|
||||||
|
**
|
||||||
|
** The action table is really a series of hash tables. Each hash
|
||||||
|
** table contains a number of entries which is a power of two. The
|
||||||
|
** "state" table (which follows) contains information about the starting
|
||||||
|
** point and size of each hash table.
|
||||||
|
*/
|
||||||
|
struct yyActionEntry {
|
||||||
|
YYCODETYPE lookahead; /* The value of the look-ahead token */
|
||||||
|
YYACTIONTYPE action; /* Action to take for this look-ahead */
|
||||||
|
struct yyActionEntry *next; /* Next look-ahead with the same hash, or NULL */
|
||||||
|
};
|
||||||
|
static struct yyActionEntry yyActionTable[] = {
|
||||||
|
%%
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The state table contains information needed to look up the correct
|
||||||
|
** action in the action table, given the current state of the parser.
|
||||||
|
** Information needed includes:
|
||||||
|
**
|
||||||
|
** + A pointer to the start of the action hash table in yyActionTable.
|
||||||
|
**
|
||||||
|
** + A mask used to hash the look-ahead token. The mask is an integer
|
||||||
|
** which is one less than the size of the hash table.
|
||||||
|
**
|
||||||
|
** + The default action. This is the action to take if no entry for
|
||||||
|
** the given look-ahead is found in the action hash table.
|
||||||
|
*/
|
||||||
|
struct yyStateEntry {
|
||||||
|
struct yyActionEntry *hashtbl; /* Start of the hash table in yyActionTable */
|
||||||
|
int mask; /* Mask used for hashing the look-ahead */
|
||||||
|
YYACTIONTYPE actionDefault; /* Default action if look-ahead not found */
|
||||||
|
};
|
||||||
|
static struct yyStateEntry yyStateTable[] = {
|
||||||
|
%%
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The following structure represents a single element of the
|
||||||
|
** parser's stack. Information stored includes:
|
||||||
|
**
|
||||||
|
** + The state number for the parser at this level of the stack.
|
||||||
|
**
|
||||||
|
** + The value of the token stored at this level of the stack.
|
||||||
|
** (In other words, the "major" token.)
|
||||||
|
**
|
||||||
|
** + The semantic value stored at this level of the stack. This is
|
||||||
|
** the information used by the action routines in the grammar.
|
||||||
|
** It is sometimes called the "minor" token.
|
||||||
|
*/
|
||||||
|
struct yyStackEntry {
|
||||||
|
int stateno; /* The state-number */
|
||||||
|
int major; /* The major token value. This is the code
|
||||||
|
** number for the token at this stack level */
|
||||||
|
YYMINORTYPE minor; /* The user-supplied minor token value. This
|
||||||
|
** is the value of the token */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The state of the parser is completely contained in an instance of
|
||||||
|
** the following structure */
|
||||||
|
struct yyParser {
|
||||||
|
int idx; /* Index of top element in stack */
|
||||||
|
int errcnt; /* Shifts left before out of the error */
|
||||||
|
struct yyStackEntry *top; /* Pointer to the top stack element */
|
||||||
|
struct yyStackEntry stack[YYSTACKDEPTH]; /* The parser's stack */
|
||||||
|
};
|
||||||
|
typedef struct yyParser yyParser;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#include <stdio.h>
|
||||||
|
static FILE *yyTraceFILE = 0;
|
||||||
|
static char *yyTracePrompt = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Turn parser tracing on by giving a stream to which to write the trace
|
||||||
|
** and a prompt to preface each trace message. Tracing is turned off
|
||||||
|
** by making either argument NULL
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** <ul>
|
||||||
|
** <li> A FILE* to which trace output should be written.
|
||||||
|
** If NULL, then tracing is turned off.
|
||||||
|
** <li> A prefix string written at the beginning of every
|
||||||
|
** line of trace output. If NULL, then tracing is
|
||||||
|
** turned off.
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** None.
|
||||||
|
*/
|
||||||
|
/* SQLITE MODIFICATION: Give the function file scope */
|
||||||
|
void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
|
||||||
|
yyTraceFILE = TraceFILE;
|
||||||
|
yyTracePrompt = zTracePrompt;
|
||||||
|
if( yyTraceFILE==0 ) yyTracePrompt = 0;
|
||||||
|
else if( yyTracePrompt==0 ) yyTraceFILE = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For tracing shifts, the names of all terminals and nonterminals
|
||||||
|
** are required. The following table supplies these names */
|
||||||
|
static char *yyTokenName[] = {
|
||||||
|
%%
|
||||||
|
};
|
||||||
|
#define YYTRACE(X) if( yyTraceFILE ) fprintf(yyTraceFILE,"%sReduce [%s].\n",yyTracePrompt,X);
|
||||||
|
#else
|
||||||
|
#define YYTRACE(X)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function allocates a new parser.
|
||||||
|
** The only argument is a pointer to a function which works like
|
||||||
|
** malloc.
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** A pointer to the function used to allocate memory.
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** A pointer to a parser. This pointer is used in subsequent calls
|
||||||
|
** to Parse and ParseFree.
|
||||||
|
*/
|
||||||
|
/* SQLITE MODIFICATION: Give the function file scope */
|
||||||
|
void *ParseAlloc(void *(*mallocProc)()){
|
||||||
|
yyParser *pParser;
|
||||||
|
pParser = (yyParser*)(*mallocProc)( sizeof(yyParser), __FILE__, __LINE__ );
|
||||||
|
if( pParser ){
|
||||||
|
pParser->idx = -1;
|
||||||
|
}
|
||||||
|
return pParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following function deletes the value associated with a
|
||||||
|
** symbol. The symbol can be either a terminal or nonterminal.
|
||||||
|
** "yymajor" is the symbol code, and "yypminor" is a pointer to
|
||||||
|
** the value.
|
||||||
|
*/
|
||||||
|
static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
|
||||||
|
switch( yymajor ){
|
||||||
|
/* Here is inserted the actions which take place when a
|
||||||
|
** terminal or non-terminal is destroyed. This can happen
|
||||||
|
** when the symbol is popped from the stack during a
|
||||||
|
** reduce or during error processing or when a parser is
|
||||||
|
** being destroyed before it is finished parsing.
|
||||||
|
**
|
||||||
|
** Note: during a reduce, the only symbols destroyed are those
|
||||||
|
** which appear on the RHS of the rule, but which are not used
|
||||||
|
** inside the C code.
|
||||||
|
*/
|
||||||
|
%%
|
||||||
|
default: break; /* If no destructor action specified: do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Pop the parser's stack once.
|
||||||
|
**
|
||||||
|
** If there is a destructor routine associated with the token which
|
||||||
|
** is popped from the stack, then call it.
|
||||||
|
**
|
||||||
|
** Return the major token number for the symbol popped.
|
||||||
|
*/
|
||||||
|
static int yy_pop_parser_stack(yyParser *pParser){
|
||||||
|
YYCODETYPE yymajor;
|
||||||
|
|
||||||
|
if( pParser->idx<0 ) return 0;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE && pParser->idx>=0 ){
|
||||||
|
fprintf(yyTraceFILE,"%sPopping %s\n",
|
||||||
|
yyTracePrompt,
|
||||||
|
yyTokenName[pParser->top->major]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
yymajor = pParser->top->major;
|
||||||
|
yy_destructor( yymajor, &pParser->top->minor);
|
||||||
|
pParser->idx--;
|
||||||
|
pParser->top--;
|
||||||
|
return yymajor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Deallocate and destroy a parser. Destructors are all called for
|
||||||
|
** all stack elements before shutting the parser down.
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** <ul>
|
||||||
|
** <li> A pointer to the parser. This should be a pointer
|
||||||
|
** obtained from ParseAlloc.
|
||||||
|
** <li> A pointer to a function used to reclaim memory obtained
|
||||||
|
** from malloc.
|
||||||
|
** </ul>
|
||||||
|
*/
|
||||||
|
/* SQLITE MODIFICATION: Give the function file scope */
|
||||||
|
void ParseFree(
|
||||||
|
void *p, /* The parser to be deleted */
|
||||||
|
void (*freeProc)() /* Function used to reclaim memory */
|
||||||
|
){
|
||||||
|
yyParser *pParser = (yyParser*)p;
|
||||||
|
if( pParser==0 ) return;
|
||||||
|
while( pParser->idx>=0 ) yy_pop_parser_stack(pParser);
|
||||||
|
(*freeProc)(pParser, __FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find the appropriate action for a parser given the look-ahead token.
|
||||||
|
**
|
||||||
|
** If the look-ahead token is YYNOCODE, then check to see if the action is
|
||||||
|
** independent of the look-ahead. If it is, return the action, otherwise
|
||||||
|
** return YY_NO_ACTION.
|
||||||
|
*/
|
||||||
|
static int yy_find_parser_action(
|
||||||
|
yyParser *pParser, /* The parser */
|
||||||
|
int iLookAhead /* The look-ahead token */
|
||||||
|
){
|
||||||
|
struct yyStateEntry *pState; /* Appropriate entry in the state table */
|
||||||
|
struct yyActionEntry *pAction; /* Action appropriate for the look-ahead */
|
||||||
|
|
||||||
|
/* if( pParser->idx<0 ) return YY_NO_ACTION; */
|
||||||
|
pState = &yyStateTable[pParser->top->stateno];
|
||||||
|
if( iLookAhead!=YYNOCODE ){
|
||||||
|
pAction = &pState->hashtbl[iLookAhead & pState->mask];
|
||||||
|
while( pAction ){
|
||||||
|
if( pAction->lookahead==iLookAhead ) return pAction->action;
|
||||||
|
pAction = pAction->next;
|
||||||
|
}
|
||||||
|
}else if( pState->mask!=0 || pState->hashtbl->lookahead!=YYNOCODE ){
|
||||||
|
return YY_NO_ACTION;
|
||||||
|
}
|
||||||
|
return pState->actionDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform a shift action.
|
||||||
|
*/
|
||||||
|
static void yy_shift(
|
||||||
|
yyParser *yypParser, /* The parser to be shifted */
|
||||||
|
int yyNewState, /* The new state to shift in */
|
||||||
|
int yyMajor, /* The major token to shift in */
|
||||||
|
YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */
|
||||||
|
){
|
||||||
|
yypParser->idx++;
|
||||||
|
yypParser->top++;
|
||||||
|
if( yypParser->idx>=YYSTACKDEPTH ){
|
||||||
|
yypParser->idx--;
|
||||||
|
yypParser->top--;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
|
||||||
|
/* Here code is inserted which will execute if the parser
|
||||||
|
** stack every overflows */
|
||||||
|
%%
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
yypParser->top->stateno = yyNewState;
|
||||||
|
yypParser->top->major = yyMajor;
|
||||||
|
yypParser->top->minor = *yypMinor;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE && yypParser->idx>0 ){
|
||||||
|
int i;
|
||||||
|
fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
|
||||||
|
fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
|
||||||
|
for(i=1; i<=yypParser->idx; i++)
|
||||||
|
fprintf(yyTraceFILE," %s",yyTokenName[yypParser->stack[i].major]);
|
||||||
|
fprintf(yyTraceFILE,"\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following table contains information about every rule that
|
||||||
|
** is used during the reduce.
|
||||||
|
*/
|
||||||
|
static struct {
|
||||||
|
YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
|
||||||
|
unsigned char nrhs; /* Number of right-hand side symbols in the rule */
|
||||||
|
} yyRuleInfo[] = {
|
||||||
|
%%
|
||||||
|
};
|
||||||
|
|
||||||
|
static void yy_accept(); /* Forward declaration */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform a reduce action and the shift that must immediately
|
||||||
|
** follow the reduce.
|
||||||
|
*/
|
||||||
|
static void yy_reduce(
|
||||||
|
yyParser *yypParser, /* The parser */
|
||||||
|
int yyruleno /* Number of the rule by which to reduce */
|
||||||
|
ParseANSIARGDECL
|
||||||
|
){
|
||||||
|
int yygoto; /* The next state */
|
||||||
|
int yyact; /* The next action */
|
||||||
|
YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
|
||||||
|
struct yyStackEntry *yymsp; /* The top of the parser's stack */
|
||||||
|
int yysize; /* Amount to pop the stack */
|
||||||
|
yymsp = yypParser->top;
|
||||||
|
switch( yyruleno ){
|
||||||
|
/* Beginning here are the reduction cases. A typical example
|
||||||
|
** follows:
|
||||||
|
** case 0:
|
||||||
|
** YYTRACE("<text of the rule>");
|
||||||
|
** #line <lineno> <grammarfile>
|
||||||
|
** { ... } // User supplied code
|
||||||
|
** #line <lineno> <thisfile>
|
||||||
|
** break;
|
||||||
|
*/
|
||||||
|
%%
|
||||||
|
};
|
||||||
|
yygoto = yyRuleInfo[yyruleno].lhs;
|
||||||
|
yysize = yyRuleInfo[yyruleno].nrhs;
|
||||||
|
yypParser->idx -= yysize;
|
||||||
|
yypParser->top -= yysize;
|
||||||
|
yyact = yy_find_parser_action(yypParser,yygoto);
|
||||||
|
if( yyact < YYNSTATE ){
|
||||||
|
yy_shift(yypParser,yyact,yygoto,&yygotominor);
|
||||||
|
}else if( yyact == YYNSTATE + YYNRULE + 1 ){
|
||||||
|
yy_accept(yypParser ParseARGDECL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following code executes when the parse fails
|
||||||
|
*/
|
||||||
|
static void yy_parse_failed(
|
||||||
|
yyParser *yypParser /* The parser */
|
||||||
|
ParseANSIARGDECL /* Extra arguments (if any) */
|
||||||
|
){
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
|
||||||
|
/* Here code is inserted which will be executed whenever the
|
||||||
|
** parser fails */
|
||||||
|
%%
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following code executes when a syntax error first occurs.
|
||||||
|
*/
|
||||||
|
static void yy_syntax_error(
|
||||||
|
yyParser *yypParser, /* The parser */
|
||||||
|
int yymajor, /* The major type of the error token */
|
||||||
|
YYMINORTYPE yyminor /* The minor type of the error token */
|
||||||
|
ParseANSIARGDECL /* Extra arguments (if any) */
|
||||||
|
){
|
||||||
|
#define TOKEN (yyminor.yy0)
|
||||||
|
%%
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following is executed when the parser accepts
|
||||||
|
*/
|
||||||
|
static void yy_accept(
|
||||||
|
yyParser *yypParser /* The parser */
|
||||||
|
ParseANSIARGDECL /* Extra arguments (if any) */
|
||||||
|
){
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
|
||||||
|
/* Here code is inserted which will be executed whenever the
|
||||||
|
** parser accepts */
|
||||||
|
%%
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The main parser program.
|
||||||
|
** The first argument is a pointer to a structure obtained from
|
||||||
|
** "ParseAlloc" which describes the current state of the parser.
|
||||||
|
** The second argument is the major token number. The third is
|
||||||
|
** the minor token. The fourth optional argument is whatever the
|
||||||
|
** user wants (and specified in the grammar) and is available for
|
||||||
|
** use by the action routines.
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** <ul>
|
||||||
|
** <li> A pointer to the parser (an opaque structure.)
|
||||||
|
** <li> The major token number.
|
||||||
|
** <li> The minor token number.
|
||||||
|
** <li> An option argument of a grammar-specified type.
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** None.
|
||||||
|
*/
|
||||||
|
/* SQLITE MODIFICATION: Give the function file scope */
|
||||||
|
void Parse(
|
||||||
|
void *yyp, /* The parser */
|
||||||
|
int yymajor, /* The major token code number */
|
||||||
|
ParseTOKENTYPE yyminor /* The value for the token */
|
||||||
|
ParseANSIARGDECL
|
||||||
|
){
|
||||||
|
YYMINORTYPE yyminorunion;
|
||||||
|
int yyact; /* The parser action. */
|
||||||
|
int yyendofinput; /* True if we are at the end of input */
|
||||||
|
int yyerrorhit = 0; /* True if yymajor has invoked an error */
|
||||||
|
yyParser *yypParser; /* The parser */
|
||||||
|
|
||||||
|
/* (re)initialize the parser, if necessary */
|
||||||
|
yypParser = (yyParser*)yyp;
|
||||||
|
if( yypParser->idx<0 ){
|
||||||
|
if( yymajor==0 ) return;
|
||||||
|
yypParser->idx = 0;
|
||||||
|
yypParser->errcnt = -1;
|
||||||
|
yypParser->top = &yypParser->stack[0];
|
||||||
|
yypParser->top->stateno = 0;
|
||||||
|
yypParser->top->major = 0;
|
||||||
|
}
|
||||||
|
yyminorunion.yy0 = yyminor;
|
||||||
|
yyendofinput = (yymajor==0);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
do{
|
||||||
|
yyact = yy_find_parser_action(yypParser,yymajor);
|
||||||
|
if( yyact<YYNSTATE ){
|
||||||
|
yy_shift(yypParser,yyact,yymajor,&yyminorunion);
|
||||||
|
yypParser->errcnt--;
|
||||||
|
if( yyendofinput && yypParser->idx>=0 ){
|
||||||
|
yymajor = 0;
|
||||||
|
}else{
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
}
|
||||||
|
}else if( yyact < YYNSTATE + YYNRULE ){
|
||||||
|
yy_reduce(yypParser,yyact-YYNSTATE ParseARGDECL);
|
||||||
|
}else if( yyact == YY_ERROR_ACTION ){
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef YYERRORSYMBOL
|
||||||
|
/* A syntax error has occurred.
|
||||||
|
** The response to an error depends upon whether or not the
|
||||||
|
** grammar defines an error token "ERROR".
|
||||||
|
**
|
||||||
|
** This is what we do if the grammar does define ERROR:
|
||||||
|
**
|
||||||
|
** * Call the %syntax_error function.
|
||||||
|
**
|
||||||
|
** * Begin popping the stack until we enter a state where
|
||||||
|
** it is legal to shift the error symbol, then shift
|
||||||
|
** the error symbol.
|
||||||
|
**
|
||||||
|
** * Set the error count to three.
|
||||||
|
**
|
||||||
|
** * Begin accepting and shifting new tokens. No new error
|
||||||
|
** processing will occur until three tokens have been
|
||||||
|
** shifted successfully.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
if( yypParser->errcnt<0 ){
|
||||||
|
yy_syntax_error(yypParser,yymajor,yyminorunion ParseARGDECL);
|
||||||
|
}
|
||||||
|
if( yypParser->top->major==YYERRORSYMBOL || yyerrorhit ){
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sDiscard input token %s\n",
|
||||||
|
yyTracePrompt,yyTokenName[yymajor]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
yy_destructor(yymajor,&yyminorunion);
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
}else{
|
||||||
|
while(
|
||||||
|
yypParser->idx >= 0 &&
|
||||||
|
yypParser->top->major != YYERRORSYMBOL &&
|
||||||
|
(yyact = yy_find_parser_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE
|
||||||
|
){
|
||||||
|
yy_pop_parser_stack(yypParser);
|
||||||
|
}
|
||||||
|
if( yypParser->idx < 0 || yymajor==0 ){
|
||||||
|
yy_destructor(yymajor,&yyminorunion);
|
||||||
|
yy_parse_failed(yypParser ParseARGDECL);
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
}else if( yypParser->top->major!=YYERRORSYMBOL ){
|
||||||
|
YYMINORTYPE u2;
|
||||||
|
u2.YYERRSYMDT = 0;
|
||||||
|
yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yypParser->errcnt = 3;
|
||||||
|
yyerrorhit = 1;
|
||||||
|
#else /* YYERRORSYMBOL is not defined */
|
||||||
|
/* This is what we do if the grammar does not define ERROR:
|
||||||
|
**
|
||||||
|
** * Report an error message, and throw away the input token.
|
||||||
|
**
|
||||||
|
** * If the input token is $, then fail the parse.
|
||||||
|
**
|
||||||
|
** As before, subsequent error messages are suppressed until
|
||||||
|
** three input tokens have been successfully shifted.
|
||||||
|
*/
|
||||||
|
if( yypParser->errcnt<=0 ){
|
||||||
|
yy_syntax_error(yypParser,yymajor,yyminorunion ParseARGDECL);
|
||||||
|
}
|
||||||
|
yypParser->errcnt = 3;
|
||||||
|
yy_destructor(yymajor,&yyminorunion);
|
||||||
|
if( yyendofinput ){
|
||||||
|
yy_parse_failed(yypParser ParseARGDECL);
|
||||||
|
}
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
#endif
|
||||||
|
}else{
|
||||||
|
yy_accept(yypParser ParseARGDECL);
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
}
|
||||||
|
}while( yymajor!=YYNOCODE && yypParser->idx>=0 );
|
||||||
|
return;
|
||||||
|
}
|
23
tool/opNames.awk
Normal file
23
tool/opNames.awk
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Read the sqliteVdbe.h file and generate a table of opcode names.
|
||||||
|
#
|
||||||
|
BEGIN {
|
||||||
|
printf "static char *zOpName[] = { 0,\n"
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
/^#define OP_MAX/ {
|
||||||
|
next
|
||||||
|
}
|
||||||
|
/^#define OP_/ {
|
||||||
|
name = "\"" substr($2,4) "\","
|
||||||
|
if( n<3 ){
|
||||||
|
printf " %-16s", name
|
||||||
|
n++
|
||||||
|
} else {
|
||||||
|
printf " %s\n", name
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
if( n ){ printf "\n" }
|
||||||
|
printf "};\n"
|
||||||
|
}
|
23
tool/opcodeDoc.awk
Normal file
23
tool/opcodeDoc.awk
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#
|
||||||
|
# Extract opcode documentation for sqliteVdbe.c and generate HTML
|
||||||
|
#
|
||||||
|
BEGIN {
|
||||||
|
print "<html><body bgcolor=white>"
|
||||||
|
print "<h1>SQLite Virtual Database Engine Opcodes</h1>"
|
||||||
|
print "<table>"
|
||||||
|
}
|
||||||
|
/ Opcode: /,/\*\// {
|
||||||
|
if( $2=="Opcode:" ){
|
||||||
|
printf "<tr><td>%s %s %s %s</td>\n<td>\n", $3, $4, $5, $6
|
||||||
|
}else if( $1=="*/" ){
|
||||||
|
printf "</td></tr>\n"
|
||||||
|
}else if( NF>1 ){
|
||||||
|
sub(/^ *\*\* /,"")
|
||||||
|
gsub(/</,"<")
|
||||||
|
gsub(/&/,"&")
|
||||||
|
print
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
print "</table></body></html>"
|
||||||
|
}
|
12
tool/renumberOps.awk
Normal file
12
tool/renumberOps.awk
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Run this script on sqliteVdbe.h to renumber the opcodes sequentially.
|
||||||
|
#
|
||||||
|
BEGIN { cnt = 1 }
|
||||||
|
/^#define OP_MAX/ {
|
||||||
|
printf "#define %-20s %3d\n",$2, cnt-1
|
||||||
|
next
|
||||||
|
}
|
||||||
|
/^#define OP_/ {
|
||||||
|
printf "#define %-20s %3d\n",$2, cnt++
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{ print }
|
Reference in New Issue
Block a user