From 75897234bea58127dd00d961ebfe823dd8a253fb Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 29 May 2000 14:26:00 +0000 Subject: [PATCH] initial check-in of the new version (CVS 1) FossilOrigin-Name: 6f3655f79f9b6fc9fb7baaa10a7e0f2b6a512dfa --- Makefile.in | 105 ++ configure | 2006 +++++++++++++++++++++ configure.in | 513 ++++++ doc/lemon.html | 861 +++++++++ manifest | 35 +- manifest.uuid | 2 +- src/build.c | 1435 +++++++++++++++ src/dbbe.c | 510 ++++++ src/dbbe.h | 119 ++ src/main.c | 219 +++ src/shell.c | 384 ++++ src/sqlite.h | 118 ++ src/sqliteInt.h | 235 +++ src/tclsqlite.c | 241 +++ src/tokenize.c | 375 ++++ src/util.c | 445 +++++ src/vdbe.c | 1973 +++++++++++++++++++++ src/vdbe.h | 166 ++ src/where.c | 566 ++++++ tool/gdbmdump.c | 90 + tool/lemon.c | 4021 ++++++++++++++++++++++++++++++++++++++++++ tool/lempar.c | 606 +++++++ tool/opNames.awk | 23 + tool/opcodeDoc.awk | 23 + tool/renumberOps.awk | 12 + 25 files changed, 15075 insertions(+), 8 deletions(-) create mode 100644 Makefile.in create mode 100755 configure create mode 100644 configure.in create mode 100644 doc/lemon.html create mode 100644 src/build.c create mode 100644 src/dbbe.c create mode 100644 src/dbbe.h create mode 100644 src/main.c create mode 100644 src/shell.c create mode 100644 src/sqlite.h create mode 100644 src/sqliteInt.h create mode 100644 src/tclsqlite.c create mode 100644 src/tokenize.c create mode 100644 src/util.c create mode 100644 src/vdbe.c create mode 100644 src/vdbe.h create mode 100644 src/where.c create mode 100644 tool/gdbmdump.c create mode 100644 tool/lemon.c create mode 100644 tool/lempar.c create mode 100644 tool/opNames.awk create mode 100644 tool/opcodeDoc.awk create mode 100644 tool/renumberOps.awk diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000000..5e9db18184 --- /dev/null +++ b/Makefile.in @@ -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.* diff --git a/configure b/configure new file mode 100755 index 0000000000..dbf4986751 --- /dev/null +++ b/configure @@ -0,0 +1,2006 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --with-hints=FILE Read configuration options from FILE" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=src/sqlite.h + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +# 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. +# + +temp=`echo $srcdir | grep '[^./]'` + +if test "$temp" = ""; then + { echo "configure: 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. ** +**************************************************************************" 1>&2; exit 1; } +fi + +######### +# Set up an appropriate program prefix +# +if test "$program_prefix" = "NONE"; then + program_prefix="" +fi + + +######### +# 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. +# +# Check whether --with-hints or --without-hints was given. +if test "${with_hints+set}" = set; then + withval="$with_hints" + hints=$withval +fi + +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 + echo "$ac_t""reading hints from $hints" 1>&6 + . $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 + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:602: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:632: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:683: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:715: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 726 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:731: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:757: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:762: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:790: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + + if test "$cross_compiling" = "yes"; then + { echo "configure: error: unable to find a compiler for building build tools" 1>&2; exit 1; } + fi + BUILD_CC=$CC + default_build_cflags=$CFLAGS +else + BUILD_CC=$config_BUILD_CC + echo $ac_n "checking host compiler""... $ac_c" 1>&6 +echo "configure:829: checking host compiler" >&5 + CC=$BUILD_CC + echo "$ac_t""$BUILD_CC" 1>&6 +fi +echo $ac_n "checking switches for the host compiler""... $ac_c" 1>&6 +echo "configure:834: checking switches for the host compiler" >&5 +if test "$config_BUILD_CFLAGS" != ""; then + CFLAGS=$config_BUILD_CFLAGS + BUILD_CFLAGS=$config_BUILD_CFLAGS +else + BUILD_CFLAGS=$default_build_cflags +fi +echo "$ac_t""$BUILD_CFLAGS" 1>&6 +if test "$config_BUILD_LIBS" != ""; then + BUILD_LIBS=$config_BUILD_LIBS +fi + + + + +########## +# Locate a compiler that converts C code into *.o files that run on +# the target machine. +# +echo $ac_n "checking target compiler""... $ac_c" 1>&6 +echo "configure:854: checking target compiler" >&5 +if test "$config_TARGET_CC" != ""; then + TARGET_CC=$config_TARGET_CC +else + TARGET_CC=$BUILD_CC +fi +echo "$ac_t""$TARGET_CC" 1>&6 +echo $ac_n "checking switches on the target compiler""... $ac_c" 1>&6 +echo "configure:862: checking switches on the target compiler" >&5 +if test "$config_TARGET_CFLAGS" != ""; then + TARGET_CFLAGS=$config_TARGET_CFLAGS +else + TARGET_CFLAGS=$BUILD_CFLAGS +fi +echo "$ac_t""$TARGET_CFLAGS" 1>&6 +echo $ac_n "checking target linker""... $ac_c" 1>&6 +echo "configure:870: checking target linker" >&5 +if test "$config_TARGET_LINK" = ""; then + TARGET_LINK=$TARGET_CC +else + TARGET_LINK=$config_TARGET_LINK +fi +echo "$ac_t""$TARGET_LINK" 1>&6 +echo $ac_n "checking switches on the target compiler""... $ac_c" 1>&6 +echo "configure:878: checking switches on the target compiler" >&5 +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 + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:890: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + TARGET_RANLIB=$RANLIB +fi +if test "$config_TARGET_AR" != ""; then + TARGET_RANLIB=$config_TARGET_AR +else + TARGET_AR='ar cr' +fi +echo "$ac_t""$TARGET_TFLAGS" 1>&6 + + + + + + + +# Set the $cross variable if we are cross-compiling. Make +# it 0 if we are not. +# +echo $ac_n "checking if host and target compilers are the same""... $ac_c" 1>&6 +echo "configure:936: checking if host and target compilers are the same" >&5 +if test "$BUILD_CC" = "$TARGET_CC"; then + cross=0 + echo "$ac_t""yes" 1>&6 +else + cross=1 + echo "$ac_t""no" 1>&6 +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. +# +echo $ac_n "checking if executables have the .exe suffix""... $ac_c" 1>&6 +echo "configure:951: checking if executables have the .exe suffix" >&5 +if test "$config_BUILD_EXEEXT" = ".exe"; then + CYGWIN=yes + echo "$ac_t""yes" 1>&6 +else + echo "$ac_t""unknown" 1>&6 +fi +if test "$CYGWIN" != "yes"; then + echo $ac_n "checking for Cygwin environment""... $ac_c" 1>&6 +echo "configure:960: checking for Cygwin environment" >&5 +if eval "test \"`echo '$''{'ac_cv_cygwin'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_cygwin=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_cygwin=no +fi +rm -f conftest* +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_cygwin" 1>&6 +CYGWIN= +test "$ac_cv_cygwin" = yes && CYGWIN=yes +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" + + + + + + +########## +# Extract generic linker options from the environment. +# +if test "$config_TARGET_LIBS" != ""; then + TARGET_LIBS=$config_TARGET_LIBS +else + TARGET_LIBS="" +fi + + +########## +# 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 + echo $ac_n "checking for sin""... $ac_c" 1>&6 +echo "configure:1040: checking for sin" >&5 +if eval "test \"`echo '$''{'ac_cv_func_sin'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char sin(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_sin) || defined (__stub___sin) +choke me +#else +sin(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1068: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_sin=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_sin=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'sin`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="" +else + echo "$ac_t""no" 1>&6 +LIBS="-lm" +fi + + echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 +echo "configure:1089: checking for dlopen in -ldl" >&5 +ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo dl | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + + otherlibs=$LIBS + if test "$extra" != ""; then + LIBS=$extra + else + LIBS="" + +echo $ac_n "checking for library containing Tcl_Init""... $ac_c" 1>&6 +echo "configure:1142: checking for library containing Tcl_Init" >&5 +if eval "test \"`echo '$''{'ac_cv_search_Tcl_Init'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_func_search_save_LIBS="$LIBS" +ac_cv_search_Tcl_Init="no" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_search_Tcl_Init="none required" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +test "$ac_cv_search_Tcl_Init" = "no" && for i in tcl8.4 tcl8.3 tcl8.2 tcl8.1 tcl8.0 tcl80 tcl; do +LIBS="-l$i $otherlibs $ac_func_search_save_LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_search_Tcl_Init="-l$i" +break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +done +LIBS="$ac_func_search_save_LIBS" +fi + +echo "$ac_t""$ac_cv_search_Tcl_Init" 1>&6 +if test "$ac_cv_search_Tcl_Init" != "no"; then + test "$ac_cv_search_Tcl_Init" = "none required" || LIBS="$ac_cv_search_Tcl_Init $LIBS" + +else : + +fi + fi + TARGET_TCL_LIBS="$LIBS $otherlibs" +fi + + +########## +# Figure out where to get the TCL header files. +# +echo $ac_n "checking TCL header files""... $ac_c" 1>&6 +echo "configure:1211: checking TCL header files" >&5 +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 + echo "$ac_t""$TARGET_TCL_INC" 1>&6 +else + echo "$ac_t""not specified: still searching..." 1>&6 + echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:1230: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1251: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1268: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1285: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +ac_safe=`echo "tcl.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for tcl.h""... $ac_c" 1>&6 +echo "configure:1311: checking for tcl.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1321: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + found=yes +else + echo "$ac_t""no" 1>&6 +fi + +fi +if test "$found" = "no"; then + for dir in /usr/local /usr/X11* /usr/pkg /usr/contrib /usr; do + +ac_safe=`echo "$dir/include/tcl.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $dir/include/tcl.h""... $ac_c" 1>&6 +echo "configure:1348: checking for $dir/include/tcl.h" >&5 +if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; } +else + if test -r $dir/include/tcl.h; then + eval "ac_cv_file_$ac_safe=yes" + else + eval "ac_cv_file_$ac_safe=no" + fi +fi +fi +if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + found=yes +else + echo "$ac_t""no" 1>&6 + +fi + + if test "$found" = "yes"; then + TARGET_TCL_INC="-I$dir/include" + break + fi + done +fi + + +########## +# 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="" + +echo $ac_n "checking for library containing gdbm_open""... $ac_c" 1>&6 +echo "configure:1389: checking for library containing gdbm_open" >&5 +if eval "test \"`echo '$''{'ac_cv_search_gdbm_open'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_func_search_save_LIBS="$LIBS" +ac_cv_search_gdbm_open="no" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_search_gdbm_open="none required" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +test "$ac_cv_search_gdbm_open" = "no" && for i in gdbm; do +LIBS="-l$i $ac_func_search_save_LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_search_gdbm_open="-l$i" +break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +done +LIBS="$ac_func_search_save_LIBS" +fi + +echo "$ac_t""$ac_cv_search_gdbm_open" 1>&6 +if test "$ac_cv_search_gdbm_open" != "no"; then + test "$ac_cv_search_gdbm_open" = "none required" || LIBS="$ac_cv_search_gdbm_open $LIBS" + +else : + +fi + TARGET_GDBM_LIBS="$LIBS" +fi + + +########## +# Figure out where to get the GDBM header files. +# +echo $ac_n "checking GDBM header files""... $ac_c" 1>&6 +echo "configure:1457: checking GDBM header files" >&5 +found=no +if test "$config_TARGET_GDBM_INC" != ""; then + TARGET_GDBM_INC=$config_TARGET_GDBM_INC + found=yes +fi +if test "$found" = "yes"; then + echo "$ac_t""$TARGET_TCL_INC" 1>&6 +else + echo "$ac_t""not specified: still searching..." 1>&6 + ac_safe=`echo "gdbm.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for gdbm.h""... $ac_c" 1>&6 +echo "configure:1469: checking for gdbm.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1479: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + found=yes +else + echo "$ac_t""no" 1>&6 +fi + +fi +if test "$found" = "no"; then + for dir in /usr/local /usr/pkg /usr/contrib; do + +ac_safe=`echo "$dir/include/gdbm.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $dir/include/gdbm.h""... $ac_c" 1>&6 +echo "configure:1506: checking for $dir/include/gdbm.h" >&5 +if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; } +else + if test -r $dir/include/gdbm.h; then + eval "ac_cv_file_$ac_safe=yes" + else + eval "ac_cv_file_$ac_safe=no" + fi +fi +fi +if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + found=yes +else + echo "$ac_t""no" 1>&6 + +fi + + if test "$found" = "yes"; then + TARGET_GDBM_INC="-I$dir/include" + break + fi + done +fi + + +########## +# 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="" + +echo $ac_n "checking for library containing readline""... $ac_c" 1>&6 +echo "configure:1547: checking for library containing readline" >&5 +if eval "test \"`echo '$''{'ac_cv_search_readline'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_func_search_save_LIBS="$LIBS" +ac_cv_search_readline="no" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_search_readline="none required" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +test "$ac_cv_search_readline" = "no" && for i in readline; do +LIBS="-l$i $ac_func_search_save_LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + ac_cv_search_readline="-l$i" +break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +done +LIBS="$ac_func_search_save_LIBS" +fi + +echo "$ac_t""$ac_cv_search_readline" 1>&6 +if test "$ac_cv_search_readline" != "no"; then + test "$ac_cv_search_readline" = "none required" || LIBS="$ac_cv_search_readline $LIBS" + +else : + +fi + TARGET_READLINE_LIBS="$LIBS" +fi + + +########## +# Figure out where to get the READLINE header files. +# +echo $ac_n "checking readline header files""... $ac_c" 1>&6 +echo "configure:1615: checking readline header files" >&5 +found=no +if test "$config_TARGET_READLINE_INC" != ""; then + TARGET_READLINE_INC=$config_TARGET_READLINE_INC + found=yes +fi +if test "$found" = "yes"; then + echo "$ac_t""$TARGET_READLINE_INC" 1>&6 +else + echo "$ac_t""not specified: still searching..." 1>&6 + ac_safe=`echo "readline.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for readline.h""... $ac_c" 1>&6 +echo "configure:1627: checking for readline.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1637: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + found=yes +else + echo "$ac_t""no" 1>&6 +fi + +fi +if test "$found" = "no"; then + for dir in /usr /usr/local /usr/local/readline /usr/contrib; do + +ac_safe=`echo "$dir/include/readline.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $dir/include/readline.h""... $ac_c" 1>&6 +echo "configure:1664: checking for $dir/include/readline.h" >&5 +if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; } +else + if test -r $dir/include/readline.h; then + eval "ac_cv_file_$ac_safe=yes" + else + eval "ac_cv_file_$ac_safe=no" + fi +fi +fi +if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + found=yes +else + echo "$ac_t""no" 1>&6 + +fi + + if test "$found" = "yes"; then + TARGET_READLINE_INC="-I$dir/include" + break + fi + +ac_safe=`echo "$dir/include/readline/readline.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $dir/include/readline/readline.h""... $ac_c" 1>&6 +echo "configure:1693: checking for $dir/include/readline/readline.h" >&5 +if eval "test \"`echo '$''{'ac_cv_file_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: Cannot check for file existence when cross compiling" 1>&2; exit 1; } +else + if test -r $dir/include/readline/readline.h; then + eval "ac_cv_file_$ac_safe=yes" + else + eval "ac_cv_file_$ac_safe=no" + fi +fi +fi +if eval "test \"`echo '$ac_cv_file_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + found=yes +else + echo "$ac_t""no" 1>&6 + +fi + + 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 + + + +######### +# Generate the output files. +# +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir + +trap 'rm -fr `echo "Makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@program_prefix@%$program_prefix%g +s%@CC@%$CC%g +s%@BUILD_CC@%$BUILD_CC%g +s%@BUILD_CFLAGS@%$BUILD_CFLAGS%g +s%@BUILD_LIBS@%$BUILD_LIBS%g +s%@RANLIB@%$RANLIB%g +s%@TARGET_CC@%$TARGET_CC%g +s%@TARGET_CFLAGS@%$TARGET_CFLAGS%g +s%@TARGET_LINK@%$TARGET_LINK%g +s%@TARGET_LFLAGS@%$TARGET_LFLAGS%g +s%@TARGET_RANLIB@%$TARGET_RANLIB%g +s%@TARGET_AR@%$TARGET_AR%g +s%@BUILD_EXEEXT@%$BUILD_EXEEXT%g +s%@OS_UNIX@%$OS_UNIX%g +s%@OS_WIN@%$OS_WIN%g +s%@TARGET_EXEEXT@%$TARGET_EXEEXT%g +s%@TARGET_LIBS@%$TARGET_LIBS%g +s%@TARGET_TCL_LIBS@%$TARGET_TCL_LIBS%g +s%@CPP@%$CPP%g +s%@TARGET_TCL_INC@%$TARGET_TCL_INC%g +s%@TARGET_GDBM_LIBS@%$TARGET_GDBM_LIBS%g +s%@TARGET_GDBM_INC@%$TARGET_GDBM_INC%g +s%@TARGET_READLINE_LIBS@%$TARGET_READLINE_LIBS%g +s%@TARGET_READLINE_INC@%$TARGET_READLINE_INC%g +s%@TARGET_HAVE_READLINE@%$TARGET_HAVE_READLINE%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000000..a6f9a3147a --- /dev/null +++ b/configure.in @@ -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 +# 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 +# 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 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) diff --git a/doc/lemon.html b/doc/lemon.html new file mode 100644 index 0000000000..9b4648f46c --- /dev/null +++ b/doc/lemon.html @@ -0,0 +1,861 @@ + + +The Lemon Parser Generator + + +

The Lemon Parser Generator

+ +

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.

+ +

This document is an introduction to the Lemon +parser generator.

+ +

Theory of Operation

+ +

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: +

    +
  • The grammar specification. +
  • A parser template file. +
+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.

+ +

Depending on command-line options, Lemon will generate between +one and three files of outputs. +

    +
  • C code to implement the parser. +
  • A header file defining an integer ID for each terminal symbol. +
  • An information file that describes the states of the generated parser + automaton. +
+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.

+ +

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: +

+   lemon gram.y
+
+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.

+ +

Command Line Options

+ +

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 +

+   lemon -?
+
+As of this writing, the following command-line options are supported: +
    +
  • -b +
  • -c +
  • -g +
  • -m +
  • -q +
  • -s +
  • -x +
+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: +
+   Parser statistics: 74 terminals, 70 nonterminals, 179 rules
+                      340 states, 2026 parser table entries, 0 conflicts
+
+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.

+ +

The Parser Interface

+ +

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.

+ +

Before a program begins using a Lemon-generated parser, the program +must first create the parser. +A new parser is created as follows: +

+   void *pParser = ParseAlloc( malloc );
+
+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()''.

+ +

After a program is finished using a parser, it can reclaim all +memory allocated by that parser by calling +

+   ParseFree(pParser, free);
+
+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.

+ +

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: +

+   Parse(pParser, hTokenID, sTokenData, pArg);
+
+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.

+ +

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.

+ +

A typical use of a Lemon parser might look something like the +following: +

+   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 }
+
+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.

+ +

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.

+ +

The core of this example as it relates to Lemon is as follows: +

+   ParseFile(){
+      pParser = ParseAlloc( malloc );
+      while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
+         Parse(pParser, hTokenId, sToken);
+      }
+      Parse(pParser, 0, sToken);
+      ParseFree(pParser, free );
+   }
+
+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().

+ +

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: +

+   ParseTrace(FILE *stream, char *zPrefix);
+
+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).

+ +

Differences With YACC and BISON

+ +

Programmers who have previously used the yacc or bison parser +generator will notice several important differences between yacc and/or +bison and Lemon. +

    +
  • In yacc and bison, the parser calls the tokenizer. In Lemon, + the tokenizer calls the parser. +
  • Lemon uses no global variables. Yacc and bison use global variables + to pass information between the tokenizer and parser. +
  • Lemon allows multiple parsers to be running simultaneously. Yacc + and bison do not. +
+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.

+ +

Input File Syntax

+ +

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.

+ +

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++.

+ +

Terminals and Nonterminals

+ +

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.

+ +

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.

+ +

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.

+ +

Grammar Rules

+ +

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 %start directive described below.) +A typical sequence of grammar rules might look something like this: +

+  expr ::= expr PLUS expr.
+  expr ::= expr TIMES expr.
+  expr ::= LPAREN expr RPAREN.
+  expr ::= VALUE.
+
+

+ +

There is one non-terminal in this example, ``expr'', and five +terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'', +``RPAREN'' and ``VALUE''.

+ +

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 {...}) immediately after the +period that closes the rule. +For example: +

+  expr ::= expr PLUS expr.   { printf("Doing an addition...\n"); }
+
+

+ +

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''.

+ +

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: +

+  expr -> expr PLUS expr  { $$ = $1 + $3; };
+
+But in Lemon, the same rule becomes the following: +
+  expr(A) ::= expr(B) PLUS expr(C).  { A = B+C; }
+
+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.

+ +

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 +

+  expr(A) ::= expr(B) PLUS expr(C).  { A = B; }
+
+will generate an error because the linking symbol ``C'' is used +in the grammar rule but not in the reduce action.

+ +

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.

+ +

Precedence Rules

+ +

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.

+ +

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:

+ +

+   %left AND.
+   %left OR.
+   %nonassoc EQ NE GT GE LT LE.
+   %left PLUS MINUS.
+   %left TIMES DIVIDE MOD.
+   %right EXP NOT.
+

+ +

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 +

+     a AND b OR c
+
+like this +
+     a AND (b OR c).
+
+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 +
+     a AND b AND c
+
+is parsed like this +
+     (a AND b) AND c.
+
+The EXP operator is right-associative, though, so +
+     a EXP b EXP c
+
+is parsed like this +
+     a EXP (b EXP c).
+
+The nonassoc precedence is used for non-associative operators. +So +
+     a EQ b EQ c
+
+is an error.

+ +

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:

+ +

+   expr = MINUS expr.  [NOT]
+

+ +

This rule has a precedence equal to that of the NOT symbol, not the +MINUS symbol as would have been the case by default.

+ +

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: +

    +
  • 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. +
  • 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. +
  • 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. +
  • 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. +
  • 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. +
  • Otherwise, resolve the conflict by doing the shift and + report the parsing conflict. +
+Reduce-reduce conflicts are resolved this way: +
    +
  • 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. +
  • 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. +
  • Otherwise, resolve the conflict by reducing by the rule that + appears first in the grammar and report a parsing conflict. +
+ +

Special Directives

+ +

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.

+ +

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.

+ +

Lemon supports the following special directives: +

    +
  • %destructor +
  • %extra_argument +
  • %include +
  • %left +
  • %name +
  • %nonassoc +
  • %parse_accept +
  • %parse_failure +
  • %right +
  • %stack_overflow +
  • %stack_size +
  • %start_symbol +
  • %syntax_error +
  • %token_destructor +
  • %token_prefix +
  • %token_type +
  • %type +
+Each of these directives will be described separately in the +following sections:

+ +

The %destructor directive

+ +

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.)

+ +

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: +

    +
  • When a rule reduces and the value of a non-terminal on + the right-hand side is not linked to C code. +
  • When the stack is popped during error processing. +
  • When the ParseFree() function runs. +
+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.

+ +

Consider an example: +

+   %type nt {void*}
+   %destructor nt { free($$); }
+   nt(A) ::= ID NUM.   { A = malloc( 100 ); }
+
+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.)

+ +

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.

+ +

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.

+ +

The %extra_argument directive

+ +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:

+ +

+    %extra_argument { MyStruct *pAbc }
+

+ +

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().

+ +

The %include directive

+ +

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.

+ +

The %include directive is very handy for getting some extra #include +preprocessor statements at the beginning of the generated parser. +For example:

+ +

+   %include {#include <unistd.h>}
+

+ +

This might be needed, for example, if some of the C actions in the +grammar call functions that are prototyed in unistd.h.

+ +

The %left directive

+ +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:

+ +

+   %left AND.
+   %left OR.
+   %nonassoc EQ NE GT GE LT LE.
+   %left PLUS MINUS.
+   %left TIMES DIVIDE MOD.
+   %right EXP NOT.
+

+ +

Note the period that terminates each %left, %right or %nonassoc +directive.

+ +

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.

+ +

The %name directive

+ +

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:

+ +

+   %name Abcde
+

+ +

Putting this directive in the grammar file will cause Lemon to generate +functions named +

    +
  • AbcdeAlloc(), +
  • AbcdeFree(), +
  • AbcdeTrace(), and +
  • Abcde(). +
+The %name directive allows you to generator two or more different +parsers and link them all into the same executable. +

+ +

The %nonassoc directive

+ +

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.

+ +

The %parse_accept directive

+ +

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.

+ +

For example:

+ +

+   %parse_accept {
+      printf("parsing complete!\n");
+   }
+

+ + +

The %parse_failure directive

+ +

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.

+ +

+   %parse_failure {
+     fprintf(stderr,"Giving up.  Parser is hopelessly lost...\n");
+   }
+

+ +

The %right directive

+ +

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.

+ +

The %stack_overflow directive

+ +

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.

+ +

+   %stack_overflow {
+     fprintf(stderr,"Giving up.  Parser stack overflow\n");
+   }
+

+ +

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: +

+   list ::= list element.      // left-recursion.  Good!
+   list ::= .
+
+Not like this: +
+   list ::= element list.      // right-recursion.  Bad!
+   list ::= .
+
+ +

The %stack_size directive

+ +

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.

+ +

+   %stack_size 2000
+

+ +

The %start_symbol directive

+ +

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.

+ +

+   %start_symbol  prog
+

+ +

The %token_destructor directive

+ +

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.

+ +

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.

+ +

The %token_prefix directive

+ +

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: +

+    #define AND              1
+    #define MINUS            2
+    #define OR               3
+    #define PLUS             4
+
+You can insert a statement into the grammar like this: +
+    %token_prefix    TOKEN_
+
+to cause Lemon to produce these symbols instead: +
+    #define TOKEN_AND        1
+    #define TOKEN_MINUS      2
+    #define TOKEN_OR         3
+    #define TOKEN_PLUS       4
+
+ +

The %token_type and %type directives

+ +

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:

+ +

+   %token_type    {Token*}
+

+ +

If the data type of terminals is not specified, the default value +is ``int''.

+ +

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:

+ +

+   %type   expr  {Expr*}
+

+ +

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.

+ +

Error Processing

+ +

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.

+ +

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.

+ +

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.

+ + + diff --git a/manifest b/manifest index f96d70fa5a..a2d479053a 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,29 @@ -C initial\sempty\scheck-in -D 2000-05-29T14:16:00 -P -R d41d8cd98f00b204e9800998ecf8427e -T *branch * trunk -T *sym-trunk * +C initial\scheck-in\sof\sthe\snew\sversion\s(CVS\s1) +D 2000-05-29T14:26:00 +F Makefile.in 4bd5c67a3a2816e930df4b22df8c1631ee87ff0c +F configure 8faba4d0194321e5f61a64e842c65eab0f68e6d8 x +F configure.in 4fc2947d631037bd340b112d6193847d7ac827ae +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 -Z 8c6f780fffd15dac29a44b424067ccfc +Z a9e2b0f2d67c72179e4ea5172821c6d6 diff --git a/manifest.uuid b/manifest.uuid index 919e65c522..1fff0226d4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -704b122e5308587b60b47a5c2fff40c593d4bf8f \ No newline at end of file +6f3655f79f9b6fc9fb7baaa10a7e0f2b6a512dfa \ No newline at end of file diff --git a/src/build.c b/src/build.c new file mode 100644 index 0000000000..ba4eb170da --- /dev/null +++ b/src/build.c @@ -0,0 +1,1435 @@ +/* +** 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 C code routines that are called by the parser +** when syntax rules are reduced. +** +** $Id: build.c,v 1.1 2000/05/29 14:26:01 drh Exp $ +*/ +#include "sqliteInt.h" + +/* +** This routine is called after a single SQL statement has been +** parsed and we want to execute the code to implement +** the statement. Prior action routines should have already +** constructed VDBE code to do the work of the SQL statement. +** This routine just has to execute the VDBE code. +** +** Note that if an error occurred, it might be the case that +** no VDBE code was generated. +*/ +void sqliteExec(Parse *pParse){ + if( pParse->pVdbe ){ + if( pParse->explain ){ + sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg, + &pParse->zErrMsg); + }else{ + FILE *trace = (pParse->db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0; + sqliteVdbeTrace(pParse->pVdbe, trace); + sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg, + &pParse->zErrMsg); + } + sqliteVdbeDelete(pParse->pVdbe); + pParse->pVdbe = 0; + } +} + +/* +** Construct a new expression node and return a pointer to it. +*/ +Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){ + Expr *pNew; + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ) return 0; + pNew->op = op; + pNew->pLeft = pLeft; + pNew->pRight = pRight; + if( pToken ){ + pNew->token = *pToken; + }else{ + pNew->token.z = ""; + pNew->token.n = 0; + } + return pNew; +} + +/* +** Construct a new expression node for a function with multiple +** arguments. +*/ +Expr *sqliteExprFunction(ExprList *pList, Token *pToken){ + Expr *pNew; + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ) return 0; + pNew->op = TK_FUNCTION; + pNew->pList = pList; + if( pToken ){ + pNew->token = *pToken; + }else{ + pNew->token.z = ""; + pNew->token.n = 0; + } + return pNew; +} + +/* +** Recursively delete an expression tree. +*/ +void sqliteExprDelete(Expr *p){ + if( p==0 ) return; + if( p->pLeft ) sqliteExprDelete(p->pLeft); + if( p->pRight ) sqliteExprDelete(p->pRight); + sqliteFree(p); +} + +/* +** Locate the in-memory structure that describes the +** format of a particular database table given the name +** of that table. Return NULL if not found. +*/ +Table *sqliteFindTable(sqlite *db, char *zName){ + Table *pTable; + int h; + + h = sqliteHashNoCase(zName, 0) % N_HASH; + for(pTable=db->apTblHash[h]; pTable; pTable=pTable->pHash){ + if( sqliteStrICmp(pTable->zName, zName)==0 ) return pTable; + } + return 0; +} + +/* +** Locate the in-memory structure that describes the +** format of a particular index table given the name +** of that table. Return NULL if not found. +*/ +Index *sqliteFindIndex(sqlite *db, char *zName){ + Index *p; + int h; + + h = sqliteHashNoCase(zName, 0) % N_HASH; + for(p=db->apIdxHash[h]; p; p=p->pHash){ + if( sqliteStrICmp(p->zName, zName)==0 ) return p; + } + return 0; +} + +/* +** Remove the given index from the index hash table, and free +** its memory structures. +** +** The index is removed from the database hash table, but it is +** not unlinked from the table that is being indexed. Unlinking +** from the table must be done by the calling function. +*/ +static void sqliteDeleteIndex(sqlite *db, Index *pIndex){ + int h; + if( pIndex->zName ){ + h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH; + if( db->apIdxHash[h]==pIndex ){ + db->apIdxHash[h] = pIndex->pHash; + }else{ + Index *p; + for(p=db->apIdxHash[h]; p && p->pHash!=pIndex; p=p->pHash){} + if( p && p->pHash==pIndex ){ + p->pHash = pIndex->pHash; + } + } + } + sqliteFree(pIndex); +} + +/* +** Remove the memory data structures associated with the given +** table. No changes are made to disk by this routine. +** +** This routine just deletes the data structure. It does not unlink +** the table data structure from the hash table. But does it destroy +** memory structures of the indices associated with the table. +*/ +void sqliteDeleteTable(sqlite *db, Table *pTable){ + int i; + Index *pIndex, *pNext; + if( pTable==0 ) return; + for(i=0; inCol; i++){ + if( pTable->azCol[i] ) sqliteFree(pTable->azCol[i]); + } + for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ + pNext = pIndex->pNext; + sqliteDeleteIndex(db, pIndex); + } + sqliteFree(pTable->azCol); + sqliteFree(pTable); +} + +/* +** Construct the name of a user table from a token. +** +** Space to hold the name is obtained from sqliteMalloc() and must +** be freed by the calling function. +*/ +static char *sqliteTableNameFromToken(Token *pName){ + char *zName = 0; + sqliteSetNString(&zName, pName->z, pName->n, 0); + return zName; +} + +/* +** Begin constructing a new table representation in memory. This is +** the first of several action routines that get called in response +** to a CREATE TABLE statement. +*/ +void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ + Table *pTable; + char *zName; + + pParse->sFirstToken = *pStart; + zName = sqliteTableNameFromToken(pName); + pTable = sqliteFindTable(pParse->db, zName); + if( pTable!=0 ){ + sqliteSetNString(&pParse->zErrMsg, "table \"", 0, pName->z, pName->n, + "\" already exists", 0, 0); + sqliteFree(zName); + pParse->nErr++; + return; + } + if( sqliteFindIndex(pParse->db, zName) ){ + sqliteSetString(&pParse->zErrMsg, "there is already an index named \"", + zName, "\"", 0); + sqliteFree(zName); + pParse->nErr++; + return; + } + pTable = sqliteMalloc( sizeof(Table) ); + if( pTable==0 ){ + sqliteSetString(&pParse->zErrMsg, "out of memory", 0); + pParse->nErr++; + return; + } + pTable->zName = zName; + pTable->pHash = 0; + pTable->nCol = 0; + pTable->azCol = 0; + pTable->pIndex = 0; + if( pParse->pNewTable ) sqliteDeleteTable(pParse->db, pParse->pNewTable); + pParse->pNewTable = pTable; +} + +/* +** Add a new column to the table currently being constructed. +*/ +void sqliteAddColumn(Parse *pParse, Token *pName){ + Table *p; + char **pz; + if( (p = pParse->pNewTable)==0 ) return; + if( (p->nCol & 0x7)==0 ){ + p->azCol = sqliteRealloc( p->azCol, p->nCol+8); + } + if( p->azCol==0 ){ + p->nCol = 0; + return; + } + pz = &p->azCol[p->nCol++]; + *pz = 0; + sqliteSetNString(pz, pName->z, pName->n, 0); +} + +/* +** This routine is called to report the final ")" that terminates +** a CREATE TABLE statement. +** +** The table structure is added to the internal hash tables. +** +** An entry for the table is made in the master table, unless +** initFlag==1. When initFlag==1, it means we are reading the +** master table because we just connected to the database, so +** the entry for this table already exists in the master table. +** We do not want to create it again. +*/ +void sqliteEndTable(Parse *pParse, Token *pEnd){ + Table *p; + int h; + + if( pParse->nErr ) return; + + /* Add the table to the in-memory representation of the database + */ + if( (p = pParse->pNewTable)!=0 && pParse->explain==0 ){ + h = sqliteHashNoCase(p->zName, 0) % N_HASH; + p->pHash = pParse->db->apTblHash[h]; + pParse->db->apTblHash[h] = p; + pParse->pNewTable = 0; + } + + /* If not initializing, then create the table on disk. + */ + if( !pParse->initFlag ){ + static VdbeOp addTable[] = { + { OP_Open, 0, 0, MASTER_NAME }, + { OP_New, 0, 0, 0}, + { OP_String, 0, 0, "table" }, + { OP_String, 0, 0, 0}, /* 2 */ + { OP_String, 0, 0, 0}, /* 3 */ + { OP_String, 0, 0, 0}, /* 4 */ + { OP_MakeRecord, 4, 0, 0}, + { OP_Put, 0, 0, 0}, + { OP_Close, 0, 0, 0}, + }; + int n, base; + Vdbe *v = pParse->pVdbe; + + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) return; + n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1; + base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable); + sqliteVdbeChangeP3(v, base+2, p->zName, 0); + sqliteVdbeChangeP3(v, base+3, p->zName, 0); + sqliteVdbeChangeP3(v, base+4, pParse->sFirstToken.z, n); + } +} + +/* +** Given a token, look up a table with that name. If not found, leave +** an error for the parser to find and return NULL. +*/ +static Table *sqliteTableFromToken(Parse *pParse, Token *pTok){ + char *zName = sqliteTableNameFromToken(pTok); + Table *pTab = sqliteFindTable(pParse->db, zName); + sqliteFree(zName); + if( pTab==0 ){ + sqliteSetNString(&pParse->zErrMsg, "no such table: \"", 0, + pTok->z, pTok->n, "\"", 1, 0); + pParse->nErr++; + } + return pTab; +} + +/* +** This routine is called to do the work of a DROP TABLE statement. +*/ +void sqliteDropTable(Parse *pParse, Token *pName){ + Table *pTable; + int h; + Vdbe *v; + int base; + + pTable = sqliteTableFromToken(pParse, pName); + if( pTable==0 ) return; + if( pTable->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTable->zName, + "\" may not be dropped", 0); + pParse->nErr++; + return; + } + + /* Generate code to remove the table and its reference in sys_master */ + v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v ){ + static VdbeOp dropTable[] = { + { OP_Open, 0, 0, MASTER_NAME }, + { OP_ListOpen, 0, 0, 0}, + { OP_String, 0, 0, 0}, /* 2 */ + { OP_Next, 0, ADDR(10), 0}, /* 3 */ + { OP_Dup, 0, 0, 0}, + { OP_Field, 0, 2, 0}, + { OP_Ne, 0, ADDR(3), 0}, + { OP_Key, 0, 0, 0}, + { OP_ListWrite, 0, 0, 0}, + { OP_Goto, 0, ADDR(3), 0}, + { OP_ListRewind, 0, 0, 0}, /* 10 */ + { OP_ListRead, 0, ADDR(14), 0}, /* 11 */ + { OP_Delete, 0, 0, 0}, + { OP_Goto, 0, ADDR(11), 0}, + { OP_Destroy, 0, 0, 0}, /* 14 */ + { OP_Close, 0, 0, 0}, + }; + Index *pIdx; + base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); + sqliteVdbeChangeP3(v, base+2, pTable->zName, 0); + sqliteVdbeChangeP3(v, base+14, pTable->zName, 0); + for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){ + sqliteVdbeAddOp(v, OP_Destroy, 0, 0, pIdx->zName, 0); + } + } + + /* Remove the table structure and free its memory. + ** + ** Exception: if the SQL statement began with the EXPLAIN keyword, + ** then no changes are made. + */ + if( !pParse->explain ){ + h = sqliteHashNoCase(pTable->zName, 0) % N_HASH; + if( pParse->db->apTblHash[h]==pTable ){ + pParse->db->apTblHash[h] = pTable->pHash; + }else{ + Table *p; + for(p=pParse->db->apTblHash[h]; p && p->pHash!=pTable; p=p->pHash){} + if( p && p->pHash==pTable ){ + p->pHash = pTable->pHash; + } + } + sqliteDeleteTable(pParse->db, pTable); + } +} + +/* +** Create a new index for an SQL table. pIndex is the name of the index +** and pTable is the name of the table that is to be indexed. Both will +** be NULL for a primary key. In that case, use pParse->pNewTable as the +** table to be indexed. +** +** pList is a list of fields to be indexed. pList will be NULL if the +** most recently added field of the table is labeled as the primary key. +*/ +void sqliteCreateIndex( + Parse *pParse, /* All information about this parse */ + Token *pName, /* Name of the index. May be NULL */ + Token *pTable, /* Name of the table to index. Use pParse->pNewTable if 0 */ + IdList *pList, /* A list of fields to be indexed */ + Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */ + Token *pEnd /* The ")" that closes the CREATE INDEX statement */ +){ + Table *pTab; /* Table to be indexed */ + Index *pIndex; /* The index to be created */ + char *zName = 0; + int i, j, h; + Token nullId; /* Fake token for an empty ID list */ + + /* + ** Find the table that is to be indexed. Return early if not found. + */ + if( pTable!=0 ){ + pTab = sqliteTableFromToken(pParse, pTable); + }else{ + pTab = pParse->pNewTable; + } + if( pTab==0 || pParse->nErr ) goto exit_create_index; + if( pTab->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName, + "\" may not have new indices added", 0); + pParse->nErr++; + goto exit_create_index; + } + + /* + ** Find the name of the index. Make sure there is not already another + ** index or table with the same name. + */ + if( pName ){ + zName = sqliteTableNameFromToken(pName); + }else{ + zName = 0; + sqliteSetString(&zName, pTab->zName, "__primary_key", 0); + } + if( sqliteFindIndex(pParse->db, zName) ){ + sqliteSetString(&pParse->zErrMsg, "index \"", zName, + "\" already exists", 0); + pParse->nErr++; + goto exit_create_index; + } + if( sqliteFindTable(pParse->db, zName) ){ + sqliteSetString(&pParse->zErrMsg, "there is already a table named \"", + zName, "\"", 0); + pParse->nErr++; + goto exit_create_index; + } + + /* If pList==0, it means this routine was called to make a primary + ** key out of the last field added to the table under construction. + ** So create a fake list to simulate this. + */ + if( pList==0 ){ + nullId.z = pTab->azCol[pTab->nCol-1]; + nullId.n = strlen(nullId.z); + pList = sqliteIdListAppend(0, &nullId); + if( pList==0 ) goto exit_create_index; + } + + /* + ** Allocate the index structure. + */ + pIndex = sqliteMalloc( sizeof(Index) + strlen(zName) + + sizeof(int)*pList->nId ); + if( pIndex==0 ){ + sqliteSetString(&pParse->zErrMsg, "out of memory", 0); + pParse->nErr++; + goto exit_create_index; + } + pIndex->aiField = (int*)&pIndex[1]; + pIndex->zName = (char*)&pIndex->aiField[pList->nId]; + strcpy(pIndex->zName, zName); + pIndex->pTable = pTab; + pIndex->nField = pList->nId; + + /* Scan the names of the fields of the table to be indexed and + ** load the field indices into the Index structure. Report an error + ** if any field is not found. + */ + for(i=0; inId; i++){ + for(j=0; jnCol; j++){ + if( sqliteStrICmp(pList->a[i].zName, pTab->azCol[j])==0 ) break; + } + if( j>=pTab->nCol ){ + sqliteSetString(&pParse->zErrMsg, "table being indexed has no field " + "named \"", pList->a[i].zName, "\"", 0); + pParse->nErr++; + sqliteFree(pIndex); + goto exit_create_index; + } + pIndex->aiField[i] = j; + } + + /* Link the new Index structure to its table and to the other + ** in-memory database structures. + */ + if( pParse->explain==0 ){ + h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH; + pIndex->pHash = pParse->db->apIdxHash[h]; + pParse->db->apIdxHash[h] = pIndex; + pIndex->pNext = pTab->pIndex; + pTab->pIndex = pIndex; + } + + /* If the initFlag is 0 then create the index on disk. This + ** involves writing the index into the master table and filling in the + ** index with the current table contents. + ** + ** The initFlag is 0 when the user first enters a CREATE INDEX + ** command. The initFlag is 1 when a database is opened and + ** CREATE INDEX statements are read out of the master table. In + ** the latter case the index already exists on disk, which is why + ** we don't want to recreate it. + */ + if( pParse->initFlag==0 ){ + static VdbeOp addTable[] = { + { OP_Open, 0, 0, MASTER_NAME}, + { OP_New, 0, 0, 0}, + { OP_String, 0, 0, "index"}, + { OP_String, 0, 0, 0}, /* 2 */ + { OP_String, 0, 0, 0}, /* 3 */ + { OP_String, 0, 0, 0}, /* 4 */ + { OP_MakeRecord, 4, 0, 0}, + { OP_Put, 0, 0, 0}, + { OP_Close, 0, 0, 0}, + }; + int n; + Vdbe *v = pParse->pVdbe; + int lbl1, lbl2; + int i; + + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) goto exit_create_index; + if( pStart && pEnd ){ + int base; + n = (int)pEnd->z - (int)pStart->z + 1; + base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable); + sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0); + sqliteVdbeChangeP3(v, base+3, pTab->zName, 0); + sqliteVdbeChangeP3(v, base+4, pStart->z, n); + } + sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_Open, 1, 0, pIndex->zName, 0); + lbl1 = sqliteVdbeMakeLabel(v); + lbl2 = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_Next, 0, lbl2, 0, lbl1); + sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0); + for(i=0; inField; i++){ + sqliteVdbeAddOp(v, OP_Field, 0, pIndex->aiField[i], 0, 0); + } + sqliteVdbeAddOp(v, OP_MakeKey, pIndex->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, 1, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, lbl1, 0, 0); + sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, lbl2); + sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Close, 1, 0, 0, 0); + } + + /* Reclaim memory on an EXPLAIN call. + */ + if( pParse->explain ){ + sqliteFree(pIndex); + } + + /* Clean up before exiting */ +exit_create_index: + sqliteIdListDelete(pList); + sqliteFree(zName); + return; +} + +/* +** This routine will drop an existing named index. +*/ +void sqliteDropIndex(Parse *pParse, Token *pName){ + Index *pIndex; + char *zName; + Vdbe *v; + + zName = sqliteTableNameFromToken(pName); + pIndex = sqliteFindIndex(pParse->db, zName); + sqliteFree(zName); + if( pIndex==0 ){ + sqliteSetNString(&pParse->zErrMsg, "no such index: \"", 0, + pName->z, pName->n, "\"", 1, 0); + pParse->nErr++; + return; + } + + /* Generate code to remove the index and from the master table */ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + if( v ){ + static VdbeOp dropIndex[] = { + { OP_Open, 0, 0, MASTER_NAME}, + { OP_ListOpen, 0, 0, 0}, + { OP_String, 0, 0, 0}, /* 2 */ + { OP_Next, 0, ADDR(9), 0}, /* 3 */ + { OP_Dup, 0, 0, 0}, + { OP_Field, 0, 1, 0}, + { OP_Ne, 0, ADDR(3), 0}, + { OP_Key, 0, 0, 0}, + { OP_Delete, 0, 0, 0}, + { OP_Destroy, 0, 0, 0}, /* 9 */ + { OP_Close, 0, 0, 0}, + }; + int base; + + base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); + sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0); + sqliteVdbeChangeP3(v, base+9, pIndex->zName, 0); + } + + /* Remove the index structure and free its memory. Except if the + ** EXPLAIN keyword is present, no changes are made. + */ + if( !pParse->explain ){ + if( pIndex->pTable->pIndex==pIndex ){ + pIndex->pTable->pIndex = pIndex->pNext; + }else{ + Index *p; + for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){} + if( p && p->pNext==pIndex ){ + p->pNext = pIndex->pNext; + } + } + sqliteDeleteIndex(pParse->db, pIndex); + } +} + +/* +** Add a new element to the end of an expression list. If pList is +** initially NULL, then create a new expression list. +*/ +ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){ + int i; + if( pList==0 ){ + pList = sqliteMalloc( sizeof(ExprList) ); + } + if( pList==0 ) return 0; + if( (pList->nExpr & 7)==0 ){ + int n = pList->nExpr + 8; + pList->a = sqliteRealloc(pList->a, n*sizeof(pList->a[0])); + if( pList->a==0 ){ + pList->nExpr = 0; + return pList; + } + } + i = pList->nExpr++; + pList->a[i].pExpr = pExpr; + pList->a[i].zName = 0; + if( pName ){ + sqliteSetNString(&pList->a[i].zName, pName->z, pName->n, 0); + } + return pList; +} + +/* +** Delete an entire expression list. +*/ +void sqliteExprListDelete(ExprList *pList){ + int i; + if( pList==0 ) return; + for(i=0; inExpr; i++){ + sqliteExprDelete(pList->a[i].pExpr); + sqliteFree(pList->a[i].zName); + } + sqliteFree(pList->a); + sqliteFree(pList); +} + +/* +** Append a new element to the given IdList. Create a new IdList if +** need be. +*/ +IdList *sqliteIdListAppend(IdList *pList, Token *pToken){ + if( pList==0 ){ + pList = sqliteMalloc( sizeof(IdList) ); + if( pList==0 ) return 0; + } + if( (pList->nId & 7)==0 ){ + pList->a = sqliteRealloc(pList->a, (pList->nId+8)*sizeof(pList->a[0]) ); + if( pList->a==0 ){ + pList->nId = 0; + return pList; + } + } + memset(&pList->a[pList->nId], 0, sizeof(pList->a[0])); + if( pToken ){ + sqliteSetNString(&pList->a[pList->nId].zName, pToken->z, pToken->n, 0); + } + pList->nId++; + return pList; +} + +/* +** Add an alias to the last identifier on the given identifier list. +*/ +void sqliteIdListAddAlias(IdList *pList, Token *pToken){ + if( pList && pList->nId>0 ){ + int i = pList->nId - 1; + sqliteSetNString(&pList->a[i].zAlias, pToken->z, pToken->n, 0); + } +} + +/* +** Delete an entire IdList +*/ +void sqliteIdListDelete(IdList *pList){ + int i; + if( pList==0 ) return; + for(i=0; inId; i++){ + sqliteFree(pList->a[i].zName); + sqliteFree(pList->a[i].zAlias); + } + sqliteFree(pList->a); + sqliteFree(pList); +} + +/* +** This routine is call to handle SQL of the following form: +** +** insert into TABLE (IDLIST) values(EXPRLIST) +** +** The parameters are the table name and the expression list. +*/ +void sqliteInsert( + Parse *pParse, /* Parser context */ + Token *pTableName, /* Name of table into which we are inserting */ + ExprList *pList, /* List of values to be inserted */ + IdList *pField /* Field name corresponding to pList. Might be NULL */ +){ + Table *pTab; + char *zTab; + int i, j; + Vdbe *v; + + zTab = sqliteTableNameFromToken(pTableName); + pTab = sqliteFindTable(pParse->db, zTab); + sqliteFree(zTab); + if( pTab==0 ){ + sqliteSetNString(&pParse->zErrMsg, "no such table: \"", 0, + pTableName->z, pTableName->n, "\"", 1, 0); + pParse->nErr++; + goto insert_cleanup; + } + if( pTab->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName, + "\" may not be modified", 0); + pParse->nErr++; + goto insert_cleanup; + } + if( pField==0 && pList->nExpr!=pTab->nCol ){ + char zNum1[30]; + char zNum2[30]; + sprintf(zNum1,"%d", pList->nExpr); + sprintf(zNum2,"%d", pTab->nCol); + sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName, + " has ", zNum2, " columns but only ", + zNum1, " values were supplied", 0); + pParse->nErr++; + goto insert_cleanup; + } + if( pField!=0 && pList->nExpr!=pField->nId ){ + char zNum1[30]; + char zNum2[30]; + sprintf(zNum1,"%d", pList->nExpr); + sprintf(zNum2,"%d", pTab->nCol); + sqliteSetString(&pParse->zErrMsg, zNum1, " values for ", + zNum2, " columns", 0); + pParse->nErr++; + goto insert_cleanup; + } + if( pField ){ + for(i=0; inId; i++){ + pField->a[i].idx = -1; + } + for(i=0; inId; i++){ + for(j=0; jnCol; j++){ + if( sqliteStrICmp(pField->a[i].zName, pTab->azCol[j])==0 ){ + pField->a[i].idx = j; + break; + } + } + if( j>=pTab->nCol ){ + sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName, + " has no column named ", pField->a[i].zName, 0); + pParse->nErr++; + goto insert_cleanup; + } + } + } + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + if( v ){ + Index *pIdx; + sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_New, 0, 0, 0, 0); + if( pTab->pIndex ){ + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + } + for(i=0; inCol; i++){ + if( pField==0 ){ + j = i; + }else{ + for(j=0; jnId; j++){ + if( pField->a[j].idx==i ) break; + } + } + if( pField && j>=pField->nId ){ + sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0); + }else{ + sqliteExprCode(pParse, pList->a[j].pExpr); + } + } + sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->pNext ){ + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + } + sqliteVdbeAddOp(v, OP_Open, 0, 0, pIdx->zName, 0); + for(i=0; inField; i++){ + int idx = pIdx->aiField[i]; + if( pField==0 ){ + j = idx; + }else{ + for(j=0; jnId; j++){ + if( pField->a[j].idx==idx ) break; + } + } + if( pField && j>=pField->nId ){ + sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0); + }else{ + sqliteExprCode(pParse, pList->a[j].pExpr); + } + } + sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + } + } + +insert_cleanup: + sqliteExprListDelete(pList); + sqliteIdListDelete(pField); +} + +/* +** This routine walks an expression tree and resolves references to +** table fields. Nodes of the form ID.ID or ID resolve into an +** index to the table in the table list and a field offset. The opcode +** for such nodes is changed to TK_FIELD. The iTable value is changed +** to the index of the referenced table in pTabList, and the iField value +** is changed to the index of the field of the referenced table. +** +** Unknown fields or tables provoke an error. The function returns +** the number of errors seen and leaves an error message on pParse->zErrMsg. +*/ +int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ + if( pExpr==0 ) return 0; + switch( pExpr->op ){ + /* A lone identifier */ + case TK_ID: { + int cnt = 0; /* Number of matches */ + int i; /* Loop counter */ + char *z = pExpr->token.z; + int n = pExpr->token.n; + for(i=0; inId; i++){ + int j; + Table *pTab = pTabList->a[i].pTab; + if( pTab==0 ) continue; + for(j=0; jnCol; j++){ + if( sqliteStrNICmp(pTab->azCol[j], z, n)==0 ){ + cnt++; + pExpr->iTable = i; + pExpr->iField = j; + } + } + } + if( cnt==0 ){ + sqliteSetNString(&pParse->zErrMsg, "unknown field name: \"", -1, + pExpr->token.z, pExpr->token.n, "\"", -1, 0); + pParse->nErr++; + return 1; + }else if( cnt>1 ){ + sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: \"", -1, + pExpr->token.z, pExpr->token.n, "\"", -1, 0); + pParse->nErr++; + return 1; + } + pExpr->op = TK_FIELD; + break; + } + + /* A table name and field name: ID.ID */ + case TK_DOT: { + int cnt = 0; /* Number of matches */ + int i; /* Loop counter */ + Expr *pLeft, *pRight; /* Left and right subbranches of the expr */ + int n; /* Length of an identifier */ + char *z; /* Text of an identifier */ + + pLeft = pExpr->pLeft; + pRight = pExpr->pRight; + assert( pLeft && pLeft->op==TK_ID ); + assert( pRight && pRight->op==TK_ID ); + n = pRight->token.n; + z = pRight->token.z; + for(i=0; inId; i++){ + int j; + char *zTab; + Table *pTab = pTabList->a[i].pTab; + if( pTab==0 ) continue; + if( pTabList->a[i].zAlias ){ + zTab = pTabList->a[i].zAlias; + }else{ + zTab = pTab->zName; + } + if( sqliteStrNICmp(zTab, pLeft->token.z, pLeft->token.n)!=0 ) continue; + for(j=0; jnCol; j++){ + if( sqliteStrNICmp(pTab->azCol[j], z, n)==0 ){ + cnt++; + pExpr->iTable = i; + pExpr->iField = j; + } + } + } + if( cnt==0 ){ + sqliteSetNString(&pParse->zErrMsg, "unknown field name: \"", -1, + pLeft->token.z, pLeft->token.n, ".", 1, z, n, "\"", 1, 0); + pParse->nErr++; + return 1; + }else if( cnt>1 ){ + sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: \"", -1, + pExpr->token.z, pExpr->token.n, ".", 1, z, n, "\"", 1, 0); + pParse->nErr++; + return 1; + } + sqliteExprDelete(pLeft); + pExpr->pLeft = 0; + sqliteExprDelete(pRight); + pExpr->pRight = 0; + pExpr->op = TK_FIELD; + break; + } + + /* For all else, just recursively walk the tree */ + default: { + if( pExpr->pLeft + && sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){ + return 1; + } + if( pExpr->pRight + && sqliteExprResolveIds(pParse, pTabList, pExpr->pRight) ){ + return 1; + } + if( pExpr->pList ){ + int i; + ExprList *pList = pExpr->pList; + for(i=0; inExpr; i++){ + if( sqliteExprResolveIds(pParse, pTabList, pList->a[i].pExpr) ){ + return 1; + } + } + } + } + } + return 0; +} + +/* +** Process a SELECT statement. +*/ +void sqliteSelect( + Parse *pParse, /* The parser context */ + ExprList *pEList, /* List of fields to extract. NULL means "*" */ + IdList *pTabList, /* List of tables to select from */ + Expr *pWhere, /* The WHERE clause. May be NULL */ + ExprList *pOrderBy /* The ORDER BY clause. May be NULL */ +){ + int i, j; + WhereInfo *pWInfo; + Vdbe *v; + + if( pParse->nErr>0 ) goto select_cleanup; + + /* Look up every table in the table list. + */ + for(i=0; inId; i++){ + pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + if( pTabList->a[i].pTab==0 ){ + sqliteSetString(&pParse->zErrMsg, "unknown table \"", + pTabList->a[i].zName, "\"", 0); + pParse->nErr++; + goto select_cleanup; + } + } + + /* If the list of fields to retrieve is "*" then replace it with + ** a list of all fields from all tables. + */ + if( pEList==0 ){ + for(i=0; inId; i++){ + Table *pTab = pTabList->a[i].pTab; + for(j=0; jnCol; j++){ + Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0); + pExpr->iTable = i; + pExpr->iField = j; + pEList = sqliteExprListAppend(pEList, pExpr, 0); + } + } + } + + /* Resolve the field names in all the expressions. + */ + for(i=0; inExpr; i++){ + if( sqliteExprResolveIds(pParse, pTabList, pEList->a[i].pExpr) ){ + goto select_cleanup; + } + } + if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){ + goto select_cleanup; + } + if( pOrderBy ){ + for(i=0; inExpr; i++){ + if( sqliteExprResolveIds(pParse, pTabList, pOrderBy->a[i].pExpr) ){ + goto select_cleanup; + } + } + } + + /* Begin generating code. + */ + v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) goto select_cleanup; + if( pOrderBy ){ + sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0); + } + + + /* Identify column names + */ + sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0); + for(i=0; inExpr; i++){ + Expr *p; + if( pEList->a[i].zName ){ + char *zName = pEList->a[i].zName; + int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0); + if( zName[0]=='\'' || zName[0]=='"' ){ + sqliteVdbeDequoteP3(v, addr); + } + continue; + } + p = pEList->a[i].pExpr; + if( p->op!=TK_FIELD ){ + char zName[30]; + sprintf(zName, "field%d", i+1); + sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0); + }else{ + if( pTabList->nId>1 ){ + char *zName = 0; + Table *pTab = pTabList->a[p->iTable].pTab; + sqliteSetString(&zName, pTab->zName, ".", + pTab->azCol[p->iField], 0); + sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0); + sqliteFree(zName); + }else{ + Table *pTab = pTabList->a[0].pTab; + sqliteVdbeAddOp(v, OP_ColumnName, i, 0, pTab->azCol[p->iField], 0); + } + } + } + + /* Begin the database scan + */ + pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0); + if( pWInfo==0 ) goto select_cleanup; + + /* Pull the requested fields. + */ + for(i=0; inExpr; i++){ + sqliteExprCode(pParse, pEList->a[i].pExpr); + } + + /* If there is no ORDER BY clause, then we can invoke the callback + ** right away. If there is an ORDER BY, then we need to put the + ** data into an appropriate sorter record. + */ + if( pOrderBy==0 ){ + sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0); + }else{ + char *zSortOrder; + sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0); + zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 ); + if( zSortOrder==0 ) goto select_cleanup; + for(i=0; inExpr; i++){ + zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+'; + sqliteExprCode(pParse, pOrderBy->a[i].pExpr); + } + zSortOrder[pOrderBy->nExpr] = 0; + sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, 0); + sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0); + } + + /* End the database scan loop. + */ + sqliteWhereEnd(pWInfo); + + /* If there is an ORDER BY clause, then we need to sort the results + ** and send them to the callback one by one. + */ + if( pOrderBy ){ + int end = sqliteVdbeMakeLabel(v); + int addr; + sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0); + addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0); + sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); + sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, end); + } + + /* Always execute the following code before exiting, in order to + ** release resources. + */ +select_cleanup: + sqliteExprListDelete(pEList); + sqliteIdListDelete(pTabList); + sqliteExprDelete(pWhere); + sqliteExprListDelete(pOrderBy); + return; +} + +/* +** Process a DELETE FROM statement. +*/ +void sqliteDeleteFrom( + Parse *pParse, /* The parser context */ + Token *pTableName, /* The table from which we should delete things */ + Expr *pWhere /* The WHERE clause. May be null */ +){ + Vdbe *v; /* The virtual database engine */ + Table *pTab; /* The table from which records will be deleted */ + IdList *pTabList; /* An ID list holding pTab and nothing else */ + int end, addr; /* A couple addresses of generated code */ + int i; /* Loop counter */ + WhereInfo *pWInfo; /* Information about the WHERE clause */ + Index *pIdx; /* For looping over indices of the table */ + + /* Locate the table which we want to update. This table has to be + ** put in an IdList structure because some of the subroutines will + ** will be calling are designed to work with multiple tables and expect + ** an IdList* parameter instead of just a Table* parameger. + */ + pTabList = sqliteIdListAppend(0, pTableName); + for(i=0; inId; i++){ + pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + if( pTabList->a[i].pTab==0 ){ + sqliteSetString(&pParse->zErrMsg, "unknown table \"", + pTabList->a[i].zName, "\"", 0); + pParse->nErr++; + goto delete_from_cleanup; + } + if( pTabList->a[i].pTab->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTabList->a[i].zName, + "\" may not be modified", 0); + pParse->nErr++; + goto delete_from_cleanup; + } + } + pTab = pTabList->a[0].pTab; + + /* Resolve the field names in all the expressions. + */ + if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){ + goto delete_from_cleanup; + } + + /* Begin generating code. + */ + v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) goto delete_from_cleanup; + + /* Begin the database scan + */ + sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0); + pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1); + if( pWInfo==0 ) goto delete_from_cleanup; + + /* Remember the index of every item to be deleted. + */ + sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0); + + /* End the database scan loop. + */ + sqliteWhereEnd(pWInfo); + + /* Delete every item identified in the list. + */ + sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + sqliteVdbeAddOp(v, OP_Open, i, 0, pIdx->zName, 0); + } + end = sqliteVdbeMakeLabel(v); + addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); + if( pTab->pIndex ){ + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0); + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + int j; + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + for(j=0; jnField; j++){ + sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0); + } + sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_DeleteIdx, i, 0, 0, 0); + } + } + sqliteVdbeAddOp(v, OP_Delete, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); + sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end); + +delete_from_cleanup: + sqliteIdListDelete(pTabList); + sqliteExprDelete(pWhere); + return; +} + +/* +** Process an UPDATE statement. +*/ +void sqliteUpdate( + Parse *pParse, /* The parser context */ + Token *pTableName, /* The table in which we should change things */ + ExprList *pChanges, /* Things to be changed */ + Expr *pWhere /* The WHERE clause. May be null */ +){ + int i, j; /* Loop counters */ + Table *pTab; /* The table to be updated */ + IdList *pTabList = 0; /* List containing only pTab */ + int end, addr; /* A couple of addresses in the generated code */ + WhereInfo *pWInfo; /* Information about the WHERE clause */ + Vdbe *v; /* The virtual database engine */ + Index *pIdx; /* For looping over indices */ + int nIdx; /* Number of indices that need updating */ + Index **apIdx = 0; /* An array of indices that need updating too */ + int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the + ** an expression for the i-th field of the table. + ** aXRef[i]==-1 if the i-th field is not changed. */ + + /* Locate the table which we want to update. This table has to be + ** put in an IdList structure because some of the subroutines will + ** will be calling are designed to work with multiple tables and expect + ** an IdList* parameter instead of just a Table* parameger. + */ + pTabList = sqliteIdListAppend(0, pTableName); + for(i=0; inId; i++){ + pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + if( pTabList->a[i].pTab==0 ){ + sqliteSetString(&pParse->zErrMsg, "unknown table \"", + pTabList->a[i].zName, "\"", 0); + pParse->nErr++; + goto update_cleanup; + } + if( pTabList->a[i].pTab->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table \"", pTabList->a[i].zName, + "\" may not be modified", 0); + pParse->nErr++; + goto update_cleanup; + } + } + pTab = pTabList->a[0].pTab; + aXRef = sqliteMalloc( sizeof(int) * pTab->nCol ); + if( aXRef==0 ) goto update_cleanup; + for(i=0; inCol; i++) aXRef[i] = -1; + + /* Resolve the field names in all the expressions in both the + ** WHERE clause and in the new values. Also find the field index + ** for each field to be updated in the pChanges array. + */ + if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){ + goto update_cleanup; + } + for(i=0; inExpr; i++){ + if( sqliteExprResolveIds(pParse, pTabList, pChanges->a[i].pExpr) ){ + goto update_cleanup; + } + for(j=0; jnCol; j++){ + if( strcmp(pTab->azCol[j], pChanges->a[i].zName)==0 ){ + pChanges->a[i].idx = j; + aXRef[j] = i; + break; + } + } + if( j>=pTab->nCol ){ + sqliteSetString(&pParse->zErrMsg, "no such field: \"", + pChanges->a[i].zName, "\"", 0); + pParse->nErr++; + goto update_cleanup; + } + } + + /* Allocate memory for the array apIdx[] and fill it pointers to every + ** index that needs to be updated. Indices only need updating if their + ** key includes one of the fields named in pChanges. + */ + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + for(i=0; inField; i++){ + if( aXRef[pIdx->aiField[i]]>=0 ) break; + } + if( inField ) nIdx++; + } + apIdx = sqliteMalloc( sizeof(Index*) * nIdx ); + if( apIdx==0 ) goto update_cleanup; + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + for(i=0; inField; i++){ + if( aXRef[pIdx->aiField[i]]>=0 ) break; + } + if( inField ) apIdx[nIdx++] = pIdx; + } + + /* Begin generating code. + */ + v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + } + if( v==0 ) goto update_cleanup; + + /* Begin the database scan + */ + sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0); + pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1); + if( pWInfo==0 ) goto update_cleanup; + + /* Remember the index of every item to be updated. + */ + sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0); + + /* End the database scan loop. + */ + sqliteWhereEnd(pWInfo); + + /* Rewind the list of records that need to be updated and + ** open every index that needs updating. + */ + sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); + for(i=0; izName, 0); + } + + /* Loop over every record that needs updating. We have to load + ** the old data for each record to be updated because some fields + ** might not change and we will need to copy the old value, therefore. + ** Also, the old data is needed to delete the old index entires. + */ + end = sqliteVdbeMakeLabel(v); + addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0); + + /* Delete the old indices for the current record. + */ + for(i=0; inField; j++){ + sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0); + } + sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_DeleteIdx, i+1, 0, 0, 0); + } + + /* Compute a completely new data for this record. + */ + for(i=0; inCol; i++){ + j = aXRef[i]; + if( j<0 ){ + sqliteVdbeAddOp(v, OP_Field, 0, i, 0, 0); + }else{ + sqliteExprCode(pParse, pChanges->a[j].pExpr); + } + } + + /* Insert new index entries that correspond to the new data + */ + for(i=0; inCol, 0, 0, 0); /* The KEY */ + pIdx = apIdx[i]; + for(j=0; jnField; j++){ + sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiField[j], 0, 0, 0); + } + sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0); + sqliteVdbeAddOp(v, OP_PutIdx, i+1, 0, 0, 0); + } + + /* Write the new data back into the database. + */ + sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); + + /* Repeat the above with the next record to be updated, until + ** all record selected by the WHERE clause have been updated. + */ + sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); + sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end); + +update_cleanup: + sqliteFree(apIdx); + sqliteFree(aXRef); + sqliteIdListDelete(pTabList); + sqliteExprListDelete(pChanges); + sqliteExprDelete(pWhere); + return; +} diff --git a/src/dbbe.c b/src/dbbe.c new file mode 100644 index 0000000000..808d3429fd --- /dev/null +++ b/src/dbbe.c @@ -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 +#include +#include +#include +#include + +/* +** 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; inTemp; 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; inTemp; 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); +} diff --git a/src/dbbe.h b/src/dbbe.h new file mode 100644 index 0000000000..26e83a6485 --- /dev/null +++ b/src/dbbe.h @@ -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 + +/* +** 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_) */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000000..4bbc75cedd --- /dev/null +++ b/src/main.c @@ -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; iapTblHash[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; +} diff --git a/src/shell.c b/src/shell.c new file mode 100644 index 0000000000..0812747457 --- /dev/null +++ b/src/shell.c @@ -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 +#include +#include +#include "sqlite.h" +#include +#include + +#if !defined(NO_READLINE) +#include +#include +#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; iout,"%s = %s\n", azCol[i], azArg[i]); + } + break; + } + case MODE_Column: { + if( p->cnt++==0 && p->showHeader ){ + for(i=0; icolWidth) && 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; icolWidth) && 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; icolWidth) && 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; iout,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); + } + } + for(i=0; iout,"%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] && nArgmode = 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; jcolWidth); 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; +} diff --git a/src/sqlite.h b/src/sqlite.h new file mode 100644 index 0000000000..26b48746ad --- /dev/null +++ b/src/sqlite.h @@ -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_ */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h new file mode 100644 index 0000000000..b2bf129dee --- /dev/null +++ b/src/sqliteInt.h @@ -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 +#include +#include +#include +#include + +/* +** 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*); diff --git a/src/tclsqlite.c b/src/tclsqlite.c new file mode 100644 index 0000000000..55e9a85570 --- /dev/null +++ b/src/tclsqlite.c @@ -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 +#include +#include + +/* +** 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; iinterp, cbData->zArray, "*", azN[i], + TCL_LIST_ELEMENT|TCL_APPEND_VALUE); + } + } + for(i=0; iinterp, cbData->zArray, azN[i], azCol[i], 0); + } + }else{ + for(i=0; iinterp, 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); +} diff --git a/src/tokenize.c b/src/tokenize.c new file mode 100644 index 0000000000..f762b679d0 --- /dev/null +++ b/src/tokenize.c @@ -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 + +/* +** 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; ipNext){ + 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; +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000000..130634547e --- /dev/null +++ b/src/util.c @@ -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 +#include + +/* +** 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 +#include +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; ipBe = pBe; + return p; +} + +/* +** Turn tracing on or off +*/ +void sqliteVdbeTrace(Vdbe *p, FILE *trace){ + p->trace = trace; +} + +/* +** Add a new instruction to the list of instructions current in the +** VDBE. Return the address of the new instruction. +** +** Parameters: +** +** p Pointer to the VDBE +** +** op The opcode for this instruction +** +** p1, p2, p3 Three operands. +** +** lbl A symbolic label for this instruction. +** +** Symbolic labels are negative numbers that stand for the address +** of instructions that have yet to be coded. When the instruction +** is coded, its real address is substituted in the p2 field of +** prior and subsequent instructions that have the lbl value in +** their p2 fields. +*/ +int sqliteVdbeAddOp(Vdbe *p, int op, int p1, int p2, const char *p3, int lbl){ + int i, j; + + i = p->nOp; + p->nOp++; + if( i>=p->nOpAlloc ){ + int oldSize = p->nOpAlloc; + p->nOpAlloc = p->nOpAlloc*2 + 10; + p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op)); + if( p->aOp==0 ){ + p->nOp = 0; + p->nOpAlloc = 0; + return 0; + } + memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op)); + } + p->aOp[i].opcode = op; + p->aOp[i].p1 = p1; + if( p2<0 && (-1-p2)nLabel && p->aLabel[-1-p2]>=0 ){ + p2 = p->aLabel[-1-p2]; + } + p->aOp[i].p2 = p2; + if( p3 && p3[0] ){ + sqliteSetString(&p->aOp[i].p3, p3, 0); + }else{ + p->aOp[i].p3 = 0; + } + if( lbl<0 && (-lbl)<=p->nLabel ){ + p->aLabel[-1-lbl] = i; + for(j=0; jaOp[j].p2==lbl ) p->aOp[j].p2 = i; + } + } + return i; +} + +/* +** Resolve label "x" to be the address of the next instruction to +** be inserted. +*/ +void sqliteVdbeResolveLabel(Vdbe *p, int x){ + int j; + if( x<0 && (-x)<=p->nLabel ){ + p->aLabel[-1-x] = p->nOp; + for(j=0; jnOp; j++){ + if( p->aOp[j].p2==x ) p->aOp[j].p2 = p->nOp; + } + } +} + +/* +** Return the address of the next instruction to be inserted. +*/ +int sqliteVdbeCurrentAddr(Vdbe *p){ + return p->nOp; +} + +/* +** Add a whole list of operations to the operation stack. Return the +** address of the first operation added. +*/ +int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOp const *aOp){ + int addr; + if( p->nOp + nOp >= p->nOpAlloc ){ + int oldSize = p->nOpAlloc; + p->nOpAlloc = p->nOpAlloc*2 + nOp + 10; + p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op)); + if( p->aOp==0 ){ + p->nOp = 0; + p->nOpAlloc = 0; + return 0; + } + memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op)); + } + addr = p->nOp; + if( nOp>0 ){ + int i; + for(i=0; i=0 && p->nOp>addr && zP3 ){ + sqliteSetNString(&p->aOp[addr].p3, zP3, n, 0); + } +} + +/* +** If the P3 operand to the specified instruction appears +** to be a quoted string token, then this procedure removes +** the quotes. +** +** The quoting operator can be either a grave ascent (ASCII 0x27) +** or a double quote character (ASCII 0x22). Two quotes in a row +** resolve to be a single actual quote character within the string. +*/ +void sqliteVdbeDequoteP3(Vdbe *p, int addr){ + int quote; + int i, j; + char *z; + if( addr<0 || addr>=p->nOp ) return; + z = p->aOp[addr].p3; + quote = z[0]; + if( quote!='\'' && quote!='"' ) return; + for(i=1, j=0; z[i]; i++){ + if( z[i]==quote ){ + if( z[i+1]==quote ){ + z[j++] = quote; + i++; + }else{ + z[j++] = 0; + break; + } + }else{ + z[j++] = z[i]; + } + } +} + +/* +** Create a new symbolic label for an instruction that has yet to be +** coded. The symbolic label is really just a negative number. The +** label can be used as the P2 value of an operation. Later, when +** the label is resolved to a specific address, the VDBE will scan +** through its operation list and change all values of P2 which match +** the label into the resolved address. +** +** The VDBE knows that a P2 value is a label because labels are +** always negative and P2 values are suppose to be non-negative. +** Hence, a negative P2 value is a label that has yet to be resolved. +*/ +int sqliteVdbeMakeLabel(Vdbe *p){ + int i; + i = p->nLabel++; + if( i>=p->nLabelAlloc ){ + p->nLabelAlloc = p->nLabelAlloc*2 + 10; + p->aLabel = sqliteRealloc( p->aLabel, p->nLabelAlloc*sizeof(int)); + } + if( p->aLabel==0 ){ + p->nLabel = 0; + p->nLabelAlloc = 0; + return 0; + } + p->aLabel[i] = -1; + return -1-i; +} + +/* +** Pop the stack N times. Free any memory associated with the +** popped stack elements. +*/ +static void PopStack(Vdbe *p, int N){ + if( p->zStack==0 ) return; + while( p->tos>=0 && N-->0 ){ + int i = p->tos--; + sqliteFree(p->zStack[i]); + p->zStack[i] = 0; + } +} + +/* +** Clean up the VM after execution. +** +** This routine will automatically close any tables, list, and/or +** sorters that were left open. +*/ +static void Cleanup(Vdbe *p){ + int i; + PopStack(p, p->tos+1); + sqliteFree(p->azColName); + p->azColName = 0; + for(i=0; inTable; i++){ + if( p->aTab[i].pTable ){ + sqliteDbbeCloseTable(p->aTab[i].pTable); + p->aTab[i].pTable = 0; + } + } + sqliteFree(p->aTab); + p->aTab = 0; + p->nTable = 0; + for(i=0; inList; i++){ + if( p->apList[i] ){ + sqliteDbbeCloseTempFile(p->pBe, p->apList[i]); + p->apList[i] = 0; + } + } + sqliteFree(p->apList); + p->apList = 0; + p->nList = 0; + for(i=0; inSort; i++){ + Sorter *pSorter; + while( (pSorter = p->apSort[i])!=0 ){ + p->apSort[i] = pSorter->pNext; + sqliteFree(pSorter->zKey); + sqliteFree(pSorter->pData); + sqliteFree(pSorter); + } + } + sqliteFree(p->apSort); + p->apSort = 0; + p->nSort = 0; +} + +/* +** Delete an entire VDBE. +*/ +void sqliteVdbeDelete(Vdbe *p){ + int i; + if( p==0 ) return; + Cleanup(p); + if( p->nOpAlloc==0 ){ + p->aOp = 0; + p->nOp = 0; + } + for(i=0; inOp; i++){ + sqliteFree(p->aOp[i].p3); + } + sqliteFree(p->aOp); + sqliteFree(p->aLabel); + sqliteFree(p->iStack); + sqliteFree(p->zStack); + sqliteFree(p); +} + +/* +** A translation from opcode numbers to opcode names. Used for testing +** and debugging only. +** +** If any of the numeric OP_ values for opcodes defined in sqliteVdbe.h +** change, be sure to change this array to match. You can use the +** "opNames.awk" awk script which is part of the source tree to regenerate +** this array, then copy and paste it into this file, if you want. +*/ +static char *zOpName[] = { 0, + "Open", "Close", "Destroy", "Fetch", + "New", "Put", "Delete", "Field", + "Key", "Rewind", "Next", "ResetIdx", + "NextIdx", "PutIdx", "DeleteIdx", "ListOpen", + "ListWrite", "ListRewind", "ListRead", "ListClose", + "SortOpen", "SortPut", "SortMakeRec", "SortMakeKey", + "Sort", "SortNext", "SortKey", "SortCallback", + "SortClose", "MakeRecord", "MakeKey", "Goto", + "If", "Halt", "ColumnCount", "ColumnName", + "Callback", "Integer", "String", "Pop", + "Dup", "Pull", "Add", "AddImm", + "Subtract", "Multiply", "Divide", "Min", + "Max", "Eq", "Ne", "Lt", + "Le", "Gt", "Ge", "IsNull", + "NotNull", "Negative", "And", "Or", + "Not", "Concat", "Noop", +}; + +/* +** Given the name of an opcode, return its number. Return 0 if +** there is no match. +** +** This routine is used for testing and debugging. +*/ +int sqliteVdbeOpcode(const char *zName){ + int i; + for(i=1; i<=OP_MAX; i++){ + if( sqliteStrICmp(zName, zOpName[i])==0 ) return i; + } + return 0; +} + +/* +** Give a listing of the program in the virtual machine. +** +** The interface is the same as sqliteVdbeExec(). But instead of +** running the code, it invokes the callback once for each instruction. +** This feature is used to implement "EXPLAIN". +*/ +int sqliteVdbeList( + Vdbe *p, /* The VDBE */ + sqlite_callback xCallback, /* The callback */ + void *pArg, /* 1st argument to callback */ + char **pzErrMsg /* Error msg written here */ +){ + int i, rc; + char *azField[6]; + char zAddr[20]; + char zP1[20]; + char zP2[20]; + static char *azColumnNames[] = { + "addr", "opcode", "p1", "p2", "p3", 0 + }; + + if( xCallback==0 ) return 0; + azField[0] = zAddr; + azField[2] = zP1; + azField[3] = zP2; + azField[5] = 0; + rc = 0; + if( pzErrMsg ){ *pzErrMsg = 0; } + for(i=0; rc==0 && inOp; i++){ + sprintf(zAddr,"%d",i); + sprintf(zP1,"%d", p->aOp[i].p1); + sprintf(zP2,"%d", p->aOp[i].p2); + azField[4] = p->aOp[i].p3; + if( azField[4]==0 ) azField[4] = ""; + azField[1] = zOpName[p->aOp[i].opcode]; + rc = xCallback(pArg, 5, azField, azColumnNames); + } + return rc; +} + +/* +** Make sure space has been allocated to hold at least N +** stack elements. Allocate additional stack space if +** necessary. +** +** Return 0 on success and non-zero if there are memory +** allocation errors. +*/ +static int NeedStack(Vdbe *p, int N){ + int oldAlloc; + int i; + if( N>=p->nStackAlloc ){ + oldAlloc = p->nStackAlloc; + p->nStackAlloc = N + 20; + p->iStack = sqliteRealloc(p->iStack, p->nStackAlloc*sizeof(int)); + p->zStack = sqliteRealloc(p->zStack, p->nStackAlloc*sizeof(char*)); + if( p->iStack==0 || p->zStack==0 ){ + sqliteFree(p->iStack); + sqliteFree(p->zStack); + p->iStack = 0; + p->zStack = 0; + p->nStackAlloc = 0; + return 1; + } + for(i=oldAlloc; inStackAlloc; i++){ + p->zStack[i] = 0; + } + } + return 0; +} + +/* +** Convert the given stack entity into a string if it isn't one +** already. Return non-zero if we run out of memory. +*/ +static int Stringify(Vdbe *p, int i){ + if( p->zStack[i]==0 ){ + char zBuf[30]; + sprintf(zBuf,"%d",p->iStack[i]); + sqliteSetString(&p->zStack[i], zBuf, 0); + if( p->zStack[i]==0 ) return 1; + p->iStack[i] = strlen(p->zStack[i])+1; + } + return 0; +} + +/* +** Convert the given stack entity into a integer if it isn't one +** already. +*/ +static int Integerify(Vdbe *p, int i){ + if( p->zStack[i]!=0 ){ + p->iStack[i] = atoi(p->zStack[i]); + sqliteFree(p->zStack[i]); + p->zStack[i] = 0; + } + return p->iStack[i]; +} + +/* +** The parameters are pointers to the head of two sorted lists +** of Sorter structures. Merge these two lists together and return +** a single sorted list. This routine forms the core of the merge-sort +** algorithm. +** +** In the case of a tie, left sorts in front of right. +*/ +static Sorter *Merge(Sorter *pLeft, Sorter *pRight){ + Sorter sHead; + Sorter *pTail; + pTail = &sHead; + pTail->pNext = 0; + while( pLeft && pRight ){ + int c = sqliteSortCompare(pLeft->zKey, pRight->zKey); + if( c<=0 ){ + pTail->pNext = pLeft; + pLeft = pLeft->pNext; + }else{ + pTail->pNext = pRight; + pRight = pRight->pNext; + } + pTail = pTail->pNext; + } + if( pLeft ){ + pTail->pNext = pLeft; + }else if( pRight ){ + pTail->pNext = pRight; + } + return sHead.pNext; +} + + +/* +** Execute the program in the VDBE. +** +** If an error occurs, an error message is written to memory obtained +** from sqliteMalloc() and *pzErrMsg is made to point to that memory. +** The return parameter is the number of errors. +** +** If the callback every returns non-zero, then the program exits +** immediately. No error message is written but the return value +** from the callback because the return value of this routine. +*/ +int sqliteVdbeExec( + Vdbe *p, /* The VDBE */ + sqlite_callback xCallback, /* The callback */ + void *pArg, /* 1st argument to callback */ + char **pzErrMsg /* Error msg written here */ +){ + int pc; /* The program counter */ + Op *pOp; /* Current operation */ + int rc; /* Value to return */ + char zBuf[100]; /* Space to sprintf() and integer */ + + p->tos = -1; + rc = 0; + if( pzErrMsg ){ *pzErrMsg = 0; } + for(pc=0; rc==0 && pcnOp && pc>=0; pc++){ + pOp = &p->aOp[pc]; + if( p->trace ){ + fprintf(p->trace,"%4d %-12s %4d %4d %s\n", + pc, zOpName[pOp->opcode], pOp->p1, pOp->p2, + pOp->p3 ? pOp->p3 : ""); + } + switch( pOp->opcode ){ + /* Opcode: Goto P2 * * + ** + ** An unconditional jump to address P2. + ** The next instruction executed will be + ** the one at index P2 from the beginning of + ** the program. + */ + case OP_Goto: { + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + break; + } + + /* Opcode: Halt * * * + ** + ** Exit immediately. All open DBs, Lists, Sorts, etc are closed + ** automatically. + */ + case OP_Halt: { + pc = p->nOp-1; + break; + } + + /* Opcode: Integer P1 * * + ** + ** The integer value P1 is pushed onto the stack. + */ + case OP_Integer: { + int i = ++p->tos; + if( NeedStack(p, p->tos) ) goto no_mem; + p->iStack[i] = pOp->p1; + p->zStack[i] = 0; + break; + } + + /* Opcode: String * * P3 + ** + ** The string value P3 is pushed onto the stack. + */ + case OP_String: { + int i = ++p->tos; + char *z; + if( NeedStack(p, p->tos) ) goto no_mem; + z = pOp->p3; + if( z==0 ) z = ""; + p->iStack[i] = strlen(z) + 1; + sqliteSetString(&p->zStack[i], z, 0); + break; + } + + /* Opcode: Pop P1 * * + ** + ** P1 elements are popped off of the top of stack and discarded. + */ + case OP_Pop: { + PopStack(p, pOp->p1); + break; + } + + /* Opcode: Dup P1 * * + ** + ** A copy of the P1-th element of the stack + ** is made and pushed onto the top of the stack. + ** The top of the stack is element 0. So the + ** instruction "Dup 0 0 0" will make a copy of the + ** top of the stack. + */ + case OP_Dup: { + int i = p->tos - pOp->p1; + int j = ++p->tos; + if( i<0 ) goto not_enough_stack; + if( NeedStack(p, p->tos) ) goto no_mem; + p->iStack[j] = p->iStack[i]; + if( p->zStack[i] ){ + p->zStack[j] = sqliteMalloc( p->iStack[j] ); + if( p->zStack[j] ) memcpy(p->zStack[j], p->zStack[i], p->iStack[j]); + }else{ + p->zStack[j] = 0; + } + break; + } + + /* Opcode: Pull P1 * * + ** + ** The P1-th element is removed its current location on + ** the stack and pushed back on top of the stack. The + ** top of the stack is element 0, so "Pull 0 0 0" is + ** a no-op. + */ + case OP_Pull: { + int from = p->tos - pOp->p1; + int to = p->tos; + int i; + int ti; + char *tz; + if( from<0 ) goto not_enough_stack; + ti = p->iStack[from]; + tz = p->zStack[from]; + for(i=from; iiStack[i] = p->iStack[i+1]; + p->zStack[i] = p->zStack[i+1]; + } + p->iStack[to] = ti; + p->zStack[to] = tz; + break; + } + + /* Opcode: ColumnCount P1 * * + ** + ** Specify the number of column values that will appear in the + ** array passed as the 4th parameter to the callback. No checking + ** is done. If this value is wrong, a coredump can result. + */ + case OP_ColumnCount: { + p->azColName = sqliteRealloc(p->azColName, (pOp->p1+1)*sizeof(char*)); + if( p->azColName==0 ) goto no_mem; + p->azColName[pOp->p1] = 0; + break; + } + + /* Opcode: ColumnName P1 * P3 + ** + ** P3 becomes the P1-th column name (first is 0). An array of pointers + ** to all column names is passed as the 4th parameter to the callback. + ** The ColumnCount opcode must be executed first to allocate space to + ** hold the column names. Failure to do this will likely result in + ** a coredump. + */ + case OP_ColumnName: { + p->azColName[pOp->p1] = pOp->p3 ? pOp->p3 : ""; + break; + } + + /* Opcode: Callback P1 * * + ** + ** Pop P1 values off the stack and form them into an array. Then + ** invoke the callback function using the newly formed array as the + ** 3rd parameter. + */ + case OP_Callback: { + int i = p->tos - pOp->p1 + 1; + int j; + if( i<0 ) goto not_enough_stack; + if( NeedStack(p, p->tos+2) ) goto no_mem; + for(j=i; j<=p->tos; j++){ + if( Stringify(p, j) ) goto no_mem; + } + p->zStack[p->tos+1] = 0; + rc = xCallback(pArg, pOp->p1, &p->zStack[i], p->azColName); + PopStack(p, pOp->p1); + break; + } + + /* Opcode: Concat * * * + ** + ** Pop two elements from the stack. Append the first (what used + ** to be the top of stack) to the second (the next on stack) to + ** form a new string. Push the new string back onto the stack. + */ + case OP_Concat: { + int tos = p->tos; + int nos = tos - 1; + char *z; + if( nos<0 ) goto not_enough_stack; + Stringify(p, tos); + Stringify(p, nos); + z = 0; + sqliteSetString(&z, p->zStack[nos], p->zStack[tos], 0); + PopStack(p, 1); + sqliteFree(p->zStack[nos]); + p->zStack[nos] = z; + p->iStack[nos] = strlen(p->zStack[nos])+1; + break; + } + + /* Opcode: Add * * * + ** + ** Pop the top two elements from the stack, add them together, + ** and push the result back onto the stack. If either element + ** is a string then it is converted to a double using the atof() + ** function before the addition. + */ + /* Opcode: Multiply * * * + ** + ** Pop the top two elements from the stack, multiply them together, + ** and push the result back onto the stack. If either element + ** is a string then it is converted to a double using the atof() + ** function before the multiplication. + */ + /* Opcode: Subtract * * * + ** + ** Pop the top two elements from the stack, subtract the + ** first (what was on top of the stack) from the second (the + ** next on stack) + ** and push the result back onto the stack. If either element + ** is a string then it is converted to a double using the atof() + ** function before the subtraction. + */ + /* Opcode: Divide * * * + ** + ** Pop the top two elements from the stack, divide the + ** first (what was on top of the stack) from the second (the + ** next on stack) + ** and push the result back onto the stack. If either element + ** is a string then it is converted to a double using the atof() + ** function before the division. Division by zero causes the + ** program to abort with an error. + */ + case OP_Add: + case OP_Subtract: + case OP_Multiply: + case OP_Divide: { + int tos = p->tos; + int nos = tos - 1; + if( nos<0 ) goto not_enough_stack; + if( p->zStack[tos]==0 && p->zStack[nos]==0 ){ + int a, b; + a = p->iStack[tos]; + b = p->iStack[nos]; + switch( pOp->opcode ){ + case OP_Add: b += a; break; + case OP_Subtract: b -= a; break; + case OP_Multiply: b *= a; break; + default: { + if( a==0 ){ + sqliteSetString(pzErrMsg, "division by zero", 0); + rc = 1; + goto cleanup; + } + b /= a; + break; + } + } + PopStack(p, 1); + p->iStack[nos] = b; + }else{ + double a, b; + Stringify(p, tos); + Stringify(p, nos); + a = atof(p->zStack[tos]); + b = atof(p->zStack[nos]); + switch( pOp->opcode ){ + case OP_Add: b += a; break; + case OP_Subtract: b -= a; break; + case OP_Multiply: b *= a; break; + default: { + if( a==0.0 ){ + sqliteSetString(pzErrMsg, "division by zero", 0); + rc = 1; + goto cleanup; + } + b /= a; + break; + } + } + sprintf(zBuf,"%g",b); + PopStack(p, 1); + sqliteSetString(&p->zStack[nos], zBuf, 0); + if( p->zStack[nos]==0 ) goto no_mem; + p->iStack[nos] = strlen(p->zStack[nos]) + 1; + } + break; + } + + /* Opcode: Max * * * + ** + ** Pop the top two elements from the stack then push back the + ** largest of the two. + */ + case OP_Max: { + int tos = p->tos; + int nos = tos - 1; + if( nos<0 ) goto not_enough_stack; + if( p->zStack[tos]==0 && p->zStack[nos]==0 ){ + if( p->iStack[nos]iStack[tos] ){ + p->iStack[nos] = p->iStack[tos]; + } + }else{ + Stringify(p, tos); + Stringify(p, nos); + if( sqliteCompare(p->zStack[nos], p->zStack[tos])<0 ){ + sqliteFree(p->zStack[nos]); + p->zStack[nos] = p->zStack[tos]; + p->iStack[nos] = p->iStack[tos]; + } + } + p->tos--; + break; + } + + /* Opcode: Min * * * + ** + ** Pop the top two elements from the stack then push back the + ** smaller of the two. + */ + case OP_Min: { + int tos = p->tos; + int nos = tos - 1; + if( nos<0 ) goto not_enough_stack; + if( p->zStack[tos]==0 && p->zStack[nos]==0 ){ + if( p->iStack[nos]>p->iStack[tos] ){ + p->iStack[nos] = p->iStack[tos]; + } + }else{ + Stringify(p, tos); + Stringify(p, nos); + if( sqliteCompare(p->zStack[nos], p->zStack[tos])>0 ){ + sqliteFree(p->zStack[nos]); + p->zStack[nos] = p->zStack[tos]; + p->iStack[nos] = p->iStack[tos]; + } + } + p->tos--; + break; + } + + /* Opcode: AddImm P1 * * + ** + ** Add the value P1 to whatever is on top of the stack. + */ + case OP_AddImm: { + int tos = p->tos; + if( tos<0 ) goto not_enough_stack; + Integerify(p, tos); + p->iStack[tos] += pOp->p1; + break; + } + + /* Opcode: Eq * P2 * + ** + ** Pop the top two elements from the stack. If they are equal, then + ** jump to instruction P2. Otherwise, continue to the next instruction. + */ + /* Opcode: Ne * P2 * + ** + ** Pop the top two elements from the stack. If they are not equal, then + ** jump to instruction P2. Otherwise, continue to the next instruction. + */ + /* Opcode: Lt * P2 * + ** + ** Pop the top two elements from the stack. If second element (the + ** next on stack) is less than the first (the top of stack), then + ** jump to instruction P2. Otherwise, continue to the next instruction. + ** In other words, jump if NOSTOS. + */ + /* Opcode: Ge * P2 * + ** + ** Pop the top two elements from the stack. If second element (the next + ** on stack) is greater than or equal to the first (the top of stack), + ** then jump to instruction P2. In other words, jump if NOS>=TOS. + */ + case OP_Eq: + case OP_Ne: + case OP_Lt: + case OP_Le: + case OP_Gt: + case OP_Ge: { + int tos = p->tos; + int nos = tos - 1; + int c; + if( nos<0 ) goto not_enough_stack; + if( p->zStack[tos]==0 && p->zStack[nos]==0 ){ + int a, b; + a = p->iStack[tos]; + b = p->iStack[nos]; + switch( pOp->opcode ){ + case OP_Eq: c = b==a; break; + case OP_Ne: c = b!=a; break; + case OP_Lt: c = ba; break; + default: c = b>=a; break; + } + }else{ + Stringify(p, tos); + Stringify(p, nos); + c = sqliteCompare(p->zStack[nos], p->zStack[tos]); + switch( pOp->opcode ){ + case OP_Eq: c = c==0; break; + case OP_Ne: c = c!=0; break; + case OP_Lt: c = c<0; break; + case OP_Le: c = c<=0; break; + case OP_Gt: c = c>0; break; + default: c = c>=0; break; + } + } + PopStack(p, 2); + if( c ) pc = pOp->p2-1; + break; + } + + /* Opcode: And * * * + ** + ** Pop two values off the stack. Take the logical AND of the + ** two values and push the resulting boolean value back onto the + ** stack. Integers are considered false if zero and true otherwise. + ** Strings are considered false if their length is zero and true + ** otherwise. + */ + /* Opcode: Or * * * + ** + ** Pop two values off the stack. Take the logical OR of the + ** two values and push the resulting boolean value back onto the + ** stack. Integers are considered false if zero and true otherwise. + ** Strings are considered false if their length is zero and true + ** otherwise. + */ + case OP_And: + case OP_Or: { + int tos = p->tos; + int nos = tos - 1; + int x, y, c; + if( nos<0 ) goto not_enough_stack; + x = p->zStack[nos] ? p->zStack[nos][0] : p->iStack[nos]; + y = p->zStack[tos] ? p->zStack[tos][0] : p->iStack[tos]; + if( pOp->opcode==OP_And ){ + c = x && y; + }else{ + c = x || y; + } + PopStack(p, 2); + p->tos++; + p->iStack[nos] = c; + break; + } + + /* Opcode: Negative * * * + ** + ** Treat the top of the stack as a numeric quantity. Replace it + ** with its additive inverse. If the top of stack is a string, + ** then it is converted into a number using atof(). + */ + case OP_Negative: { + int tos; + if( (tos = p->tos)<0 ) goto not_enough_stack; + if( p->zStack[tos] ){ + double r = atof(p->zStack[tos]); + sprintf(zBuf, "%g", -r); + sqliteSetString(&p->zStack[tos], zBuf, 0); + p->iStack[tos] = strlen(zBuf) + 1; + }else{ + p->iStack[tos] = -p->iStack[tos]; + } + break; + } + + /* Opcode: Not * * * + ** + ** Treat the top of the stack as a boolean value. Replace it + ** with its complement. Integers are false if zero and true + ** otherwise. Strings are false if zero-length and true otherwise. + */ + case OP_Not: { + int c; + if( p->tos<0 ) goto not_enough_stack; + c = p->zStack[p->tos] ? p->zStack[p->tos][0] : p->iStack[p->tos]; + PopStack(p, 1); + p->tos++; + p->iStack[p->tos] = !c; + break; + } + + /* Opcode: Noop * * * + ** + ** Do nothing. This instruction is often useful as a jump + ** destination. + */ + case OP_Noop: { + break; + } + + /* Opcode: If * P2 * + ** + ** Pop a single boolean from the stack. If the boolean popped is + ** true, then jump to p2. Otherwise continue to the next instruction. + ** An integer is false if zero and true otherwise. A string is + ** false if it has zero length and true otherwise. + */ + case OP_If: { + int c; + if( p->tos<0 ) goto not_enough_stack; + c = p->zStack[p->tos] ? p->zStack[p->tos][0] : p->iStack[p->tos]; + PopStack(p, 1); + if( c ) pc = pOp->p2-1; + break; + } + + /* Opcode: IsNull * P2 * + ** + ** Pop a single value from the stack. If the value popped is the + ** empty string, then jump to p2. Otherwise continue to the next + ** instruction. + */ + case OP_IsNull: { + int c; + if( p->tos<0 ) goto not_enough_stack; + c = p->zStack[p->tos]!=0 && p->zStack[p->tos][0]==0; + PopStack(p, 1); + if( c ) pc = pOp->p2-1; + break; + } + + /* Opcode: NotNull * P2 * + ** + ** Pop a single value from the stack. If the value popped is not an + ** empty string, then jump to p2. Otherwise continue to the next + ** instruction. + */ + case OP_NotNull: { + int c; + if( p->tos<0 ) goto not_enough_stack; + c = p->zStack[p->tos]==0 || p->zStack[p->tos][0]!=0; + PopStack(p, 1); + if( c ) pc = pOp->p2-1; + break; + } + + /* Opcode: MakeRecord P1 * * + ** + ** Convert the top P1 entries of the stack into a single entry + ** suitable for use as a data record in the database. To do this + ** each entry is converted to a string and all the strings are + ** concatenated. The null-terminators are preserved by the concatation + ** and serve as a boundry marker between fields. The lowest entry + ** on the stack is the first in the concatenation and the top of + ** the stack is the last. After all fields are concatenated, an + ** index header is added. The index header consists of P1 integers + ** which hold the offset of the beginning of each field from the + ** beginning of the completed record including the header. + */ + case OP_MakeRecord: { + char *zNewRecord; + int nByte; + int nField; + int i, j; + int addr; + + nField = pOp->p1; + if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( Stringify(p, i) ) goto no_mem; + nByte += p->iStack[i]; + } + nByte += sizeof(int)*nField; + zNewRecord = sqliteMalloc( nByte ); + if( zNewRecord==0 ) goto no_mem; + j = 0; + addr = sizeof(int)*nField; + for(i=p->tos-nField+1; itos; i++){ + memcpy(&zNewRecord[j], (char*)&addr, sizeof(int)); + addr += p->iStack[i]; + j += sizeof(int); + } + memcpy(&zNewRecord[j], (char*)&addr, sizeof(int)); + j += sizeof(int); + for(i=p->tos-nField+1; i<=p->tos; i++){ + memcpy(&zNewRecord[j], p->zStack[i], p->iStack[i]); + j += p->iStack[i]; + } + PopStack(p, nField); + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = nByte; + p->zStack[p->tos] = zNewRecord; + break; + } + + /* Opcode: MakeKey P1 * * + ** + ** Convert the top P1 entries of the stack into a single entry suitable + ** for use as the key in an index or a sort. The top P1 records are + ** concatenated with a tab character (ASCII 0x09) used as a record + ** separator. The entire concatenation is null-terminated. The + ** lowest entry in the stack is the first field and the top of the + ** stack becomes the last. + ** + ** See also the SortMakeKey opcode. + */ + case OP_MakeKey: { + char *zNewKey; + int nByte; + int nField; + int i, j; + + nField = pOp->p1; + if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( Stringify(p, i) ) goto no_mem; + nByte += p->iStack[i]+1; + } + zNewKey = sqliteMalloc( nByte ); + if( zNewKey==0 ) goto no_mem; + j = 0; + for(i=p->tos-nField+1; i<=p->tos; i++){ + memcpy(&zNewKey[j], p->zStack[i], p->iStack[i]-1); + j += p->iStack[i]-1; + if( itos ) zNewKey[j++] = '\t'; + } + zNewKey[j] = 0; + PopStack(p, nField); + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = nByte; + p->zStack[p->tos] = zNewKey; + break; + } + + /* Open P1 P3 P2 + ** + ** Open a new database table named P3. Give it an identifier P1. + ** Open readonly if P2==0 and for reading and writing if P2!=0. + ** The table is created if it does not already exist and P2!=0. + ** If there is already another table opened on P1, then the old + ** table is closed first. All tables are automatically closed when + ** the VDBE finishes execution. The P1 values need not be + ** contiguous but all P1 values should be small integers. It is + ** an error for P1 to be negative. + */ + case OP_Open: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i>=p->nTable ){ + int j; + p->aTab = sqliteRealloc( p->aTab, (i+1)*sizeof(VdbeTable) ); + if( p->aTab==0 ){ p->nTable = 0; goto no_mem; } + for(j=p->nTable; j<=i; j++) p->aTab[j].pTable = 0; + p->nTable = i+1; + }else if( p->aTab[i].pTable ){ + sqliteDbbeCloseTable(p->aTab[i].pTable); + } + p->aTab[i].pTable = sqliteDbbeOpenTable(p->pBe, pOp->p3, pOp->p2); + p->aTab[i].index = 0; + break; + } + + /* Opcode: Close P1 * * + ** + ** Close a database table previously opened as P1. If P1 is not + ** currently open, this instruction is a no-op. + */ + case OP_Close: { + int i = pOp->p1; + if( i>=0 && inTable && p->aTab[i].pTable ){ + sqliteDbbeCloseTable(p->aTab[i].pTable); + p->aTab[i].pTable = 0; + } + break; + } + + /* Opcode: Fetch P1 * * + ** + ** Pop the top of the stack and use its value as a key to fetch + ** a record from database table or index P1. The data is held + ** in the P1 cursor until needed. The data is not pushed onto the + ** stack or anything like that. + */ + case OP_Fetch: { + int i = pOp->p1; + int tos = p->tos; + if( tos<0 ) goto not_enough_stack; + if( i>=0 && inTable && p->aTab[i].pTable ){ + if( p->zStack[tos]==0 ){ + sqliteDbbeFetch(p->aTab[i].pTable, sizeof(int), + (char*)&p->iStack[tos]); + }else{ + sqliteDbbeFetch(p->aTab[i].pTable, p->iStack[tos], p->zStack[tos]); + } + } + PopStack(p, 1); + break; + } + + /* Opcode: New P1 * * + ** + ** Get a new integer key not previous used by table P1 and + ** push it onto the stack. + */ + case OP_New: { + int i = pOp->p1; + int v; + if( i<0 || i>=p->nTable || p->aTab[i].pTable==0 ){ + v = 0; + }else{ + v = sqliteDbbeNew(p->aTab[i].pTable); + } + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = v; + break; + } + + /* Opcode: Put P1 * * + ** + ** Write an entry into the database table P1. A new entry is + ** created if it doesn't already exist, or the data for an existing + ** entry is overwritten. The data is the value on the top of the + ** stack. The key is the next value down on the stack. The stack + ** is popped twice by this instruction. + */ + case OP_Put: { + int tos = p->tos; + int nos = p->tos-1; + int i = pOp->p1; + if( nos<0 ) goto not_enough_stack; + if( i>=0 && inTable && p->aTab[i].pTable!=0 ){ + char *zKey; + int nKey; + Stringify(p, tos); + if( p->zStack[nos]!=0 ){ + nKey = p->iStack[nos]; + zKey = p->zStack[nos]; + }else{ + nKey = sizeof(int); + zKey = (char*)&p->iStack[nos]; + } + sqliteDbbePut(p->aTab[i].pTable, nKey, zKey, + p->iStack[tos], p->zStack[tos]); + } + PopStack(p, 2); + break; + } + + /* Opcode: Delete P1 * * + ** + ** The top of the stack is a key. Remove this key and its data + ** from database table P1. Then pop the stack to discard the key. + */ + case OP_Delete: { + int tos = p->tos; + int i = pOp->p1; + if( tos<0 ) goto not_enough_stack; + if( i>=0 && inTable && p->aTab[i].pTable!=0 ){ + char *zKey; + int nKey; + if( p->zStack[tos]!=0 ){ + nKey = p->iStack[tos]; + zKey = p->zStack[tos]; + }else{ + nKey = sizeof(int); + zKey = (char*)&p->iStack[tos]; + } + sqliteDbbeDelete(p->aTab[i].pTable, nKey, zKey); + } + PopStack(p, 1); + break; + } + + /* Opcode: Field P1 P2 * + ** + ** Push onto the stack the value of the P2-th field from the + ** most recent Fetch from table P1. + */ + case OP_Field: { + int *pAddr; + int amt; + int i = pOp->p1; + int p2 = pOp->p2; + int tos = ++p->tos; + DbbeTable *pTab; + char *z; + + if( NeedStack(p, p->tos) ) goto no_mem; + if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){ + amt = sqliteDbbeDataLength(pTab); + if( amt<=sizeof(int)*(p2+1) ){ + sqliteSetString(&p->zStack[tos], "", 0); + break; + } + pAddr = (int*)sqliteDbbeReadData(pTab, sizeof(int)*p2); + z = sqliteDbbeReadData(pTab, *pAddr); + sqliteSetString(&p->zStack[tos], z, 0); + p->iStack[tos] = strlen(z)+1; + } + break; + } + + /* Opcode: Key P1 * * + ** + ** Push onto the stack an integer which is the first 4 bytes of the + ** the key to the current entry in a sequential scan of the table P1. + ** A sequential scan is started using the Next opcode. + */ + case OP_Key: { + int i = pOp->p1; + int tos = ++p->tos; + DbbeTable *pTab; + + if( NeedStack(p, p->tos) ) goto no_mem; + if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){ + char *z = sqliteDbbeReadKey(pTab, 0); + memcpy(&p->iStack[tos], z, sizeof(int)); + p->zStack[tos] = 0; + } + break; + } + + /* Opcode: Rewind P1 * * + ** + ** The next use of the Key or Field or Next instruction for P1 + ** will refer to the first entry in the table. + */ + case OP_Rewind: { + int i = pOp->p1; + if( i>=0 && inTable && p->aTab[i].pTable!=0 ){ + sqliteDbbeRewind(p->aTab[i].pTable); + } + break; + } + + /* Opcode: Next P1 P2 * + ** + ** Advance P1 to the next entry in the table. Or, if there are no + ** more entries, rewind P1 and jump to location P2. + */ + case OP_Next: { + int i = pOp->p1; + if( i>=0 && inTable && p->aTab[i].pTable!=0 ){ + if( sqliteDbbeNextKey(p->aTab[i].pTable)==0 ){ + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + } + } + break; + } + + /* Opcode: ResetIdx P1 * * + ** + ** Begin treating the current row of table P1 as an index. The next + ** NextIdx instruction will refer to the first index in the table. + */ + case OP_ResetIdx: { + int i = pOp->p1; + if( i>=0 && inTable ){ + p->aTab[i].index = 0; + } + break; + } + + /* Opcode: NextIdx P1 P2 * + ** + ** Push the next index from the current entry of table P1 onto the + ** stack and advance the pointer. If there are no more indices, then + ** reset the table entry and jump to P2 + */ + case OP_NextIdx: { + int i = pOp->p1; + int tos = ++p->tos; + DbbeTable *pTab; + + if( NeedStack(p, p->tos) ) goto no_mem; + p->zStack[tos] = 0; + if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){ + int *aIdx; + int nIdx; + int j; + nIdx = sqliteDbbeDataLength(pTab)/sizeof(int); + aIdx = (int*)sqliteDbbeReadData(pTab, 0); + for(j=p->aTab[i].index; jiStack[tos] = aIdx[j]; + break; + } + } + if( j>=nIdx ){ + j = -1; + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + } + p->aTab[i].index = j+1; + } + break; + } + + /* Opcode: PutIdx P1 * * + ** + ** The top of the stack hold an index key (proably made using the + ** MakeKey instruction) and next on stack holds an index value for + ** a table. Locate the record in the index P1 that has the key + ** and insert the index value into its + ** data. Write the results back to the index. + ** If the key doesn't exist it is created. + */ + case OP_PutIdx: { + int i = pOp->p1; + int tos = p->tos; + int nos = tos - 1; + DbbeTable *pTab; + if( nos<0 ) goto not_enough_stack; + if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){ + int r; + int newVal = Integerify(p, nos); + Stringify(p, tos); + r = sqliteDbbeFetch(pTab, p->iStack[tos], p->zStack[tos]); + if( r==0 ){ + /* Create a new record for this index */ + sqliteDbbePut(pTab, p->iStack[tos], p->zStack[tos], + sizeof(int), (char*)&newVal); + }else{ + /* Extend the existing record */ + int nIdx; + int *aIdx; + nIdx = sqliteDbbeDataLength(pTab)/sizeof(int); + aIdx = sqliteMalloc( sizeof(int)*(nIdx+1) ); + if( aIdx==0 ) goto no_mem; + sqliteDbbeCopyData(pTab, 0, nIdx*sizeof(int), (char*)aIdx); + aIdx[nIdx] = newVal; + sqliteDbbePut(pTab, p->iStack[tos], p->zStack[tos], + sizeof(int)*(nIdx+1), (char*)aIdx); + sqliteFree(aIdx); + } + } + PopStack(p, 2); + break; + } + + /* Opcode: DeleteIdx P1 * * + ** + ** The top of the stack is a key and next on stack is an index value. + ** Locate the record + ** in index P1 that has the key and remove the index value from its + ** data. Write the results back to the table. If after removing + ** the index value no more indices remain in the record, then the + ** record is removed from the table. + */ + case OP_DeleteIdx: { + int i = pOp->p1; + int tos = p->tos; + int nos = tos - 1; + DbbeTable *pTab; + if( nos<0 ) goto not_enough_stack; + if( i>=0 && inTable && (pTab = p->aTab[i].pTable)!=0 ){ + int *aIdx; + int nIdx; + int j; + int r; + int oldVal = Integerify(p, nos); + Stringify(p, tos); + r = sqliteDbbeFetch(pTab, p->iStack[tos], p->zStack[tos]); + if( r==0 ) break; + nIdx = sqliteDbbeDataLength(pTab)/sizeof(int); + aIdx = (int*)sqliteDbbeReadData(pTab, 0); + for(j=0; j=nIdx ) break; + aIdx[j] = aIdx[nIdx-1]; + if( nIdx==1 ){ + sqliteDbbeDelete(pTab, p->iStack[tos], p->zStack[tos]); + }else{ + sqliteDbbePut(pTab, p->iStack[tos], p->zStack[tos], + sizeof(int)*(nIdx-1), (char*)aIdx); + } + } + PopStack(p, 2); + break; + } + + /* Opcode: Destroy * * P3 + ** + ** Drop the table whose name is P3. The file that holds this table + ** is removed from the disk drive. + */ + case OP_Destroy: { + sqliteDbbeDropTable(p->pBe, pOp->p3); + break; + } + + /* Opcode: ListOpen P1 * * + ** + ** Open a file used for temporary storage of index numbers. P1 + ** will server as a handle to this temporary file for future + ** interactions. If another temporary file with the P1 handle is + ** already opened, the prior file is closed and a new one opened + ** in its place. + */ + case OP_ListOpen: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i>=p->nList ){ + int j; + p->apList = sqliteRealloc( p->apList, (i+1)*sizeof(FILE*) ); + if( p->apList==0 ){ p->nList = 0; goto no_mem; } + for(j=p->nList; j<=i; j++) p->apList[j] = 0; + p->nList = i+1; + }else if( p->apList[i] ){ + sqliteDbbeCloseTempFile(p->pBe, p->apList[i]); + } + p->apList[i] = sqliteDbbeOpenTempFile(p->pBe); + break; + } + + /* Opcode: ListWrite P1 * * + ** + ** Write the integer on the top of the stack + ** into the temporary storage file P1. + */ + case OP_ListWrite: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( p->tos<0 ) goto not_enough_stack; + if( inList && p->apList[i]!=0 ){ + int val = Integerify(p, p->tos); + PopStack(p, 1); + fwrite(&val, sizeof(int), 1, p->apList[i]); + } + break; + } + + /* Opcode: ListRewind P1 * * + ** + ** Rewind the temporary buffer P1 back to the beginning. + */ + case OP_ListRewind: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( inList && p->apList[i]!=0 ){ + rewind(p->apList[i]); + } + break; + } + + /* Opcode: ListRead P1 P2 * + ** + ** Attempt to read an integer from temporary storage buffer P1 + ** and push it onto the stack. If the storage buffer is empty + ** push nothing but instead jump to P2. + */ + case OP_ListRead: { + int i = pOp->p1; + int val, amt; + if( i<0 || i>=p->nList || p->apList[i]==0 ) goto bad_instruction; + amt = fread(&val, sizeof(int), 1, p->apList[i]); + if( amt==1 ){ + p->tos++; + if( NeedStack(p, p->tos) ) goto no_mem; + p->iStack[p->tos] = val; + p->zStack[p->tos] = 0; + }else{ + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + } + break; + } + + /* Opcode: ListClose P1 * * + ** + ** Close the temporary storage buffer and discard its contents. + */ + case OP_ListClose: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( inList && p->apList[i]!=0 ){ + sqliteDbbeCloseTempFile(p->pBe, p->apList[i]); + p->apList[i] = 0; + } + break; + } + + /* Opcode: SortOpen P1 * * + ** + ** Create a new sorter with index P1 + */ + case OP_SortOpen: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( i>=p->nSort ){ + int j; + p->apSort = sqliteRealloc( p->apSort, (i+1)*sizeof(Sorter*) ); + if( p->apSort==0 ){ p->nSort = 0; goto no_mem; } + for(j=p->nSort; j<=i; j++) p->apSort[j] = 0; + p->nSort = i+1; + } + break; + } + + /* Opcode: SortPut P1 * * + ** + ** The TOS is the key and the NOS is the data. Pop both from the stack + ** and put them on the sorter. + */ + case OP_SortPut: { + int i = pOp->p1; + Sorter *pSorter; + if( i<0 || i>=p->nSort ) goto bad_instruction; + if( p->tos<1 ) goto not_enough_stack; + Stringify(p, p->tos); + Stringify(p, p->tos-1); + pSorter = sqliteMalloc( sizeof(Sorter) ); + if( pSorter==0 ) goto no_mem; + pSorter->pNext = p->apSort[i]; + p->apSort[i] = pSorter; + pSorter->nKey = p->iStack[p->tos]; + pSorter->zKey = p->zStack[p->tos]; + pSorter->nData = p->iStack[p->tos-1]; + pSorter->pData = p->zStack[p->tos-1]; + p->zStack[p->tos] = p->zStack[p->tos-1] = 0; + PopStack(p, 2); + break; + } + + /* Opcode: SortMakeRec P1 * * + ** + ** The top P1 elements are the arguments to a callback. Form these + ** elements into a single data entry that can be stored on a sorter + ** using SortPut and later fed to a callback using SortCallback. + */ + case OP_SortMakeRec: { + char *z; + char **azArg; + int nByte; + int nField; + int i, j; + + nField = pOp->p1; + if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( Stringify(p, i) ) goto no_mem; + nByte += p->iStack[i]; + } + nByte += sizeof(char*)*(nField+1); + azArg = sqliteMalloc( nByte ); + if( azArg==0 ) goto no_mem; + z = (char*)&azArg[nField+1]; + for(j=0, i=p->tos-nField+1; i<=p->tos; i++, j++){ + azArg[j] = z; + strcpy(z, p->zStack[i]); + z += p->iStack[i]; + } + PopStack(p, nField); + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = nByte; + p->zStack[p->tos] = (char*)azArg; + break; + } + + /* Opcode: SortMakeKey P1 * P3 + ** + ** Convert the top few entries of the stack into a sort key. The + ** number of stack entries consumed is the number of characters in + ** the string P3. One character from P3 is prepended to each entry. + ** The first character of P3 is prepended to the element lowest in + ** the stack and the last character of P3 is appended to the top of + ** the stack. All stack entries are separated by a \000 character + ** in the result. The whole key is terminated by two \000 characters + ** in a row. + ** + ** See also the MakeKey opcode. + */ + case OP_SortMakeKey: { + char *zNewKey; + int nByte; + int nField; + int i, j, k; + + nField = strlen(pOp->p3); + if( p->tos+1tos-nField+1; i<=p->tos; i++){ + if( Stringify(p, i) ) goto no_mem; + nByte += p->iStack[i]+2; + } + zNewKey = sqliteMalloc( nByte ); + if( zNewKey==0 ) goto no_mem; + j = 0; + k = nField-1; + for(i=p->tos-nField+1; i<=p->tos; i++){ + zNewKey[j++] = pOp->p3[k--]; + memcpy(&zNewKey[j], p->zStack[i], p->iStack[i]-1); + j += p->iStack[i]-1; + zNewKey[j++] = 0; + } + zNewKey[j] = 0; + PopStack(p, nField); + NeedStack(p, p->tos+1); + p->tos++; + p->iStack[p->tos] = nByte; + p->zStack[p->tos] = zNewKey; + break; + } + + /* Opcode: Sort P1 * * + ** + ** Sort all elements on the given sorter. The algorithm is a + ** mergesort. + */ + case OP_Sort: { + int j; + j = pOp->p1; + if( j<0 ) goto bad_instruction; + if( jnSort ){ + int i; + Sorter *pElem; + Sorter *apSorter[NSORT]; + for(i=0; iapSort[j] ){ + pElem = p->apSort[j]; + p->apSort[j] = pElem->pNext; + pElem->pNext = 0; + for(i=0; i=NSORT-1 ){ + apSorter[NSORT-1] = Merge(apSorter[NSORT-1],pElem); + } + } + pElem = 0; + for(i=0; iapSort[j] = pElem; + } + break; + } + + /* Opcode: SortNext P1 P2 * + ** + ** Push the data for the topmost element in the given sorter onto the + ** stack, then remove the element from the sorter. + */ + case OP_SortNext: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( inSort && p->apSort[i]!=0 ){ + Sorter *pSorter = p->apSort[i]; + p->apSort[i] = pSorter->pNext; + p->tos++; + NeedStack(p, p->tos); + p->zStack[p->tos] = pSorter->pData; + p->iStack[p->tos] = pSorter->nData; + sqliteFree(pSorter->zKey); + sqliteFree(pSorter); + }else{ + pc = pOp->p2; + if( pc<0 || pc>p->nOp ){ + sqliteSetString(pzErrMsg, "jump destination out of range", 0); + rc = 1; + } + pc--; + } + break; + } + + /* Opcode: SortKey P1 * * + ** + ** Push the key for the topmost element of the sorter onto the stack. + ** But don't change the sorter an any other way. + */ + case OP_SortKey: { + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( inSort && p->apSort[i]!=0 ){ + Sorter *pSorter = p->apSort[i]; + p->tos++; + NeedStack(p, p->tos); + sqliteSetString(&p->zStack[p->tos], pSorter->zKey, 0); + p->iStack[p->tos] = pSorter->nKey; + } + break; + } + + /* Opcode: SortCallback P1 P2 * + ** + ** The top of the stack contains a callback record built using + ** the SortMakeRec operation with the same P1 value as this + ** instruction. Pop this record from the stack and invoke the + ** callback on it. + */ + case OP_SortCallback: { + int i = p->tos; + if( i<0 ) goto not_enough_stack; + rc = xCallback(pArg, pOp->p1, (char**)p->zStack[i], p->azColName); + PopStack(p, 1); + break; + } + + /* Opcode: SortClose P1 * * + ** + ** Close the given sorter and remove all its elements. + */ + case OP_SortClose: { + Sorter *pSorter; + int i = pOp->p1; + if( i<0 ) goto bad_instruction; + if( inSort ){ + while( (pSorter = p->apSort[i])!=0 ){ + p->apSort[i] = pSorter->pNext; + sqliteFree(pSorter->zKey); + sqliteFree(pSorter->pData); + sqliteFree(pSorter); + } + } + break; + } + + /* An other opcode is illegal... + */ + default: { + sprintf(zBuf,"%d",pOp->opcode); + sqliteSetString(pzErrMsg, "unknown opcode ", zBuf, 0); + rc = 1; + break; + } + } + if( p->trace && p->tos>=0 ){ + int i; + fprintf(p->trace, "Stack:"); + for(i=p->tos; i>=0 && i>p->tos-5; i--){ + if( p->zStack[i] ){ + fprintf(p->trace, " [%.11s]", p->zStack[i]); + }else{ + fprintf(p->trace, " [%d]", p->iStack[i]); + } + } + fprintf(p->trace,"\n"); + } + } + +cleanup: + Cleanup(p); + return rc; + + /* Jump to here if a malloc() fails. It's hard to get a malloc() + ** to fail on a modern VM computer, so this code is untested. + */ +no_mem: + Cleanup(p); + sqliteSetString(pzErrMsg, "out or memory", 0); + return 1; + + /* Jump to here if a operator is encountered that requires more stack + ** operands than are currently available on the stack. + */ +not_enough_stack: + sprintf(zBuf,"%d",pc); + sqliteSetString(pzErrMsg, "too few operands on stack at ", zBuf, 0); + rc = 1; + goto cleanup; + + /* Jump here if an illegal or illformed instruction is executed. + */ +bad_instruction: + sprintf(zBuf,"%d",pc); + sqliteSetString(pzErrMsg, "illegal operation at ", zBuf, 0); + rc = 1; + goto cleanup; + +} diff --git a/src/vdbe.h b/src/vdbe.h new file mode 100644 index 0000000000..5bafa9943b --- /dev/null +++ b/src/vdbe.h @@ -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 + +/* +** 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 diff --git a/src/where.c b/src/where.c new file mode 100644 index 0000000000..ea15a5eeb3 --- /dev/null +++ b/src/where.c @@ -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<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; inId-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; inId; 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; inId && ia[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; jpLeft->iField; + int k; + for(k=0; knField; k++){ + if( pIdx->aiField[k]==iField ){ + fieldMask |= 1<pRight->iField; + int k; + for(k=0; knField; k++){ + if( pIdx->aiField[k]==iField ){ + fieldMask |= 1<nField) ){ + if( pBestIdx==0 || pBestIdx->nFieldnField ){ + pBestIdx = pIdx; + } + } + } + aIdx[i] = pBestIdx; + } + + /* Open all tables in the pTabList and all indices in aIdx[]. + */ + for(i=0; inId; i++){ + sqliteVdbeAddOp(v, OP_Open, i, 0, pTabList->a[i].pTab->zName, 0); + if( inId+i, 0, aIdx[i]->zName, 0); + } + } + + /* Generate the code to do the search + */ + pWInfo->iBreak = brk = sqliteVdbeMakeLabel(v); + loopMask = 0; + for(i=0; inId; i++){ + int j, k; + int idx = aOrder[i]; + Index *pIdx = inField; j++){ + for(k=0; kpLeft->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<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; + } + } +} diff --git a/tool/gdbmdump.c b/tool/gdbmdump.c new file mode 100644 index 0000000000..4385355ee5 --- /dev/null +++ b/tool/gdbmdump.c @@ -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 +#include +#include +#include + +static void print_data(char *zPrefix, datum p){ + int i, j; + + printf("%-5s: ", zPrefix); + for(i=0; i +#include +#include +#include + +extern void qsort(); +extern double strtod(); +extern long strtol(); +extern void free(); +extern int access(); +extern int atoi(); + +#ifndef __WIN32__ +# if defined(_WIN32) || defined(WIN32) +# define __WIN32__ +# endif +#endif + +/* #define PRIVATE static */ +#define PRIVATE + +#ifdef TEST +#define MAXRHS 5 /* Set low to exercise exception code */ +#else +#define MAXRHS 1000 +#endif + +char *msort(); +extern void *malloc(); + +/******** From the file "action.h" *************************************/ +struct action *Action_new(); +struct action *Action_sort(); +void Action_add(); + +/********* From the file "assert.h" ************************************/ +void myassert(); +#ifndef NDEBUG +# define assert(X) if(!(X))myassert(__FILE__,__LINE__) +#else +# define assert(X) +#endif + +/********** From the file "build.h" ************************************/ +void FindRulePrecedences(); +void FindFirstSets(); +void FindStates(); +void FindLinks(); +void FindFollowSets(); +void FindActions(); + +/********* From the file "configlist.h" *********************************/ +void Configlist_init(/* void */); +struct config *Configlist_add(/* struct rule *, int */); +struct config *Configlist_addbasis(/* struct rule *, int */); +void Configlist_closure(/* void */); +void Configlist_sort(/* void */); +void Configlist_sortbasis(/* void */); +struct config *Configlist_return(/* void */); +struct config *Configlist_basis(/* void */); +void Configlist_eat(/* struct config * */); +void Configlist_reset(/* void */); + +/********* From the file "error.h" ***************************************/ +void ErrorMsg( /* char *, int, char *, ... */ ); + +/****** From the file "option.h" ******************************************/ +struct s_options { + enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR, + OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type; + char *label; + char *arg; + char *message; +}; +int optinit(/* char**,struct s_options*,FILE* */); +int optnargs(/* void */); +char *optarg(/* int */); +void opterr(/* int */); +void optprint(/* void */); + +/******** From the file "parse.h" *****************************************/ +void Parse(/* struct lemon *lemp */); + +/********* From the file "plink.h" ***************************************/ +struct plink *Plink_new(/* void */); +void Plink_add(/* struct plink **, struct config * */); +void Plink_copy(/* struct plink **, struct plink * */); +void Plink_delete(/* struct plink * */); + +/********** From the file "report.h" *************************************/ +void Reprint(/* struct lemon * */); +void ReportOutput(/* struct lemon * */); +void ReportTable(/* struct lemon * */); +void ReportHeader(/* struct lemon * */); +void CompressTables(/* struct lemon * */); + +/********** From the file "set.h" ****************************************/ +void SetSize(/* int N */); /* All sets will be of size N */ +char *SetNew(/* void */); /* A new set for element 0..N */ +void SetFree(/* char* */); /* Deallocate a set */ + +int SetAdd(/* char*,int */); /* Add element to a set */ +int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */ + +#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */ + +/********** From the file "struct.h" *************************************/ +/* +** Principal data structures for the LEMON parser generator. +*/ + +typedef enum {FALSE=0, TRUE} Boolean; + +/* Symbols (terminals and nonterminals) of the grammar are stored +** in the following: */ +struct symbol { + char *name; /* Name of the symbol */ + int index; /* Index number for this symbol */ + enum { + TERMINAL, + NONTERMINAL + } type; /* Symbols are all either TERMINALS or NTs */ + struct rule *rule; /* Linked list of rules of this (if an NT) */ + int prec; /* Precedence if defined (-1 otherwise) */ + enum e_assoc { + LEFT, + RIGHT, + NONE, + UNK + } assoc; /* Associativity if predecence is defined */ + char *firstset; /* First-set for all rules of this symbol */ + Boolean lambda; /* True if NT and can generate an empty string */ + char *destructor; /* Code which executes whenever this symbol is + ** popped from the stack during error processing */ + int destructorln; /* Line number of destructor code */ + char *datatype; /* The data type of information held by this + ** object. Only used if type==NONTERMINAL */ + int dtnum; /* The data type number. In the parser, the value + ** stack is a union. The .yy%d element of this + ** union is the correct data type for this object */ +}; + +/* Each production rule in the grammar is stored in the following +** structure. */ +struct rule { + struct symbol *lhs; /* Left-hand side of the rule */ + char *lhsalias; /* Alias for the LHS (NULL if none) */ + int ruleline; /* Line number for the rule */ + int nrhs; /* Number of RHS symbols */ + struct symbol **rhs; /* The RHS symbols */ + char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ + int line; /* Line number at which code begins */ + char *code; /* The code executed when this rule is reduced */ + struct symbol *precsym; /* Precedence symbol for this rule */ + int index; /* An index number for this rule */ + Boolean canReduce; /* True if this rule is ever reduced */ + struct rule *nextlhs; /* Next rule with the same LHS */ + struct rule *next; /* Next rule in the global list */ +}; + +/* A configuration is a production rule of the grammar together with +** a mark (dot) showing how much of that rule has been processed so far. +** Configurations also contain a follow-set which is a list of terminal +** symbols which are allowed to immediately follow the end of the rule. +** Every configuration is recorded as an instance of the following: */ +struct config { + struct rule *rp; /* The rule upon which the configuration is based */ + int dot; /* The parse point */ + char *fws; /* Follow-set for this configuration only */ + struct plink *fplp; /* Follow-set forward propagation links */ + struct plink *bplp; /* Follow-set backwards propagation links */ + struct state *stp; /* Pointer to state which contains this */ + enum { + COMPLETE, /* The status is used during followset and */ + INCOMPLETE /* shift computations */ + } status; + struct config *next; /* Next configuration in the state */ + struct config *bp; /* The next basis configuration */ +}; + +/* Every shift or reduce operation is stored as one of the following */ +struct action { + struct symbol *sp; /* The look-ahead symbol */ + enum e_action { + SHIFT, + ACCEPT, + REDUCE, + ERROR, + CONFLICT, /* Was a reduce, but part of a conflict */ + SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ + RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ + NOT_USED /* Deleted by compression */ + } type; + union { + struct state *stp; /* The new state, if a shift */ + struct rule *rp; /* The rule, if a reduce */ + } x; + struct action *next; /* Next action for this state */ + struct action *collide; /* Next action with the same hash */ +}; + +/* Each state of the generated parser's finite state machine +** is encoded as an instance of the following structure. */ +struct state { + struct config *bp; /* The basis configurations for this state */ + struct config *cfp; /* All configurations in this set */ + int index; /* Sequencial number for this state */ + struct action *ap; /* Array of actions for this state */ + int naction; /* Number of actions for this state */ + int tabstart; /* First index of the action table */ + int tabdfltact; /* Default action */ +}; + +/* A followset propagation link indicates that the contents of one +** configuration followset should be propagated to another whenever +** the first changes. */ +struct plink { + struct config *cfp; /* The configuration to which linked */ + struct plink *next; /* The next propagate link */ +}; + +/* The state vector for the entire parser generator is recorded as +** follows. (LEMON uses no global variables and makes little use of +** static variables. Fields in the following structure can be thought +** of as begin global variables in the program.) */ +struct lemon { + struct state **sorted; /* Table of states sorted by state number */ + struct rule *rule; /* List of all rules */ + int nstate; /* Number of states */ + int nrule; /* Number of rules */ + int nsymbol; /* Number of terminal and nonterminal symbols */ + int nterminal; /* Number of terminal symbols */ + struct symbol **symbols; /* Sorted array of pointers to symbols */ + int errorcnt; /* Number of errors */ + struct symbol *errsym; /* The error symbol */ + char *name; /* Name of the generated parser */ + char *arg; /* Declaration of the 3th argument to parser */ + char *tokentype; /* Type of terminal symbols in the parser stack */ + char *start; /* Name of the start symbol for the grammar */ + char *stacksize; /* Size of the parser stack */ + char *include; /* Code to put at the start of the C file */ + int includeln; /* Line number for start of include code */ + char *error; /* Code to execute when an error is seen */ + int errorln; /* Line number for start of error code */ + char *overflow; /* Code to execute on a stack overflow */ + int overflowln; /* Line number for start of overflow code */ + char *failure; /* Code to execute on parser failure */ + int failureln; /* Line number for start of failure code */ + char *accept; /* Code to execute when the parser excepts */ + int acceptln; /* Line number for the start of accept code */ + char *extracode; /* Code appended to the generated file */ + int extracodeln; /* Line number for the start of the extra code */ + char *tokendest; /* Code to execute to destroy token data */ + int tokendestln; /* Line number for token destroyer code */ + char *filename; /* Name of the input file */ + char *outname; /* Name of the current output file */ + char *tokenprefix; /* A prefix added to token names in the .h file */ + int nconflict; /* Number of parsing conflicts */ + int tablesize; /* Size of the parse tables */ + int basisflag; /* Print only basis configurations */ + char *argv0; /* Name of the program */ +}; + +#define MemoryCheck(X) if((X)==0){ \ + extern void memory_error(); \ + memory_error(); \ +} + +/**************** From the file "table.h" *********************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ + +/* Routines for handling a strings */ + +char *Strsafe(); + +void Strsafe_init(/* void */); +int Strsafe_insert(/* char * */); +char *Strsafe_find(/* char * */); + +/* Routines for handling symbols of the grammar */ + +struct symbol *Symbol_new(); +int Symbolcmpp(/* struct symbol **, struct symbol ** */); +void Symbol_init(/* void */); +int Symbol_insert(/* struct symbol *, char * */); +struct symbol *Symbol_find(/* char * */); +struct symbol *Symbol_Nth(/* int */); +int Symbol_count(/* */); +struct symbol **Symbol_arrayof(/* */); + +/* Routines to manage the state table */ + +int Configcmp(/* struct config *, struct config * */); +struct state *State_new(); +void State_init(/* void */); +int State_insert(/* struct state *, struct config * */); +struct state *State_find(/* struct config * */); +struct state **State_arrayof(/* */); + +/* Routines used for efficiency in Configlist_add */ + +void Configtable_init(/* void */); +int Configtable_insert(/* struct config * */); +struct config *Configtable_find(/* struct config * */); +void Configtable_clear(/* int(*)(struct config *) */); +/****************** From the file "action.c" *******************************/ +/* +** Routines processing parser actions in the LEMON parser generator. +*/ + +/* Allocate a new parser action */ +struct action *Action_new(){ + static struct action *freelist = 0; + struct action *new; + + if( freelist==0 ){ + int i; + int amt = 100; + freelist = (struct action *)malloc( sizeof(struct action)*amt ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new parser action."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Compare two actions */ +static int actioncmp(ap1,ap2) +struct action *ap1; +struct action *ap2; +{ + int rc; + rc = ap1->sp->index - ap2->sp->index; + if( rc==0 ) rc = (int)ap1->type - (int)ap2->type; + if( rc==0 ){ + assert( ap1->type==REDUCE && ap2->type==REDUCE ); + rc = ap1->x.rp->index - ap2->x.rp->index; + } + return rc; +} + +/* Sort parser actions */ +struct action *Action_sort(ap) +struct action *ap; +{ + ap = (struct action *)msort(ap,&ap->next,actioncmp); + return ap; +} + +void Action_add(app,type,sp,arg) +struct action **app; +enum e_action type; +struct symbol *sp; +char *arg; +{ + struct action *new; + new = Action_new(); + new->next = *app; + *app = new; + new->type = type; + new->sp = sp; + if( type==SHIFT ){ + new->x.stp = (struct state *)arg; + }else{ + new->x.rp = (struct rule *)arg; + } +} +/********************** From the file "assert.c" ****************************/ +/* +** A more efficient way of handling assertions. +*/ +void myassert(file,line) +char *file; +int line; +{ + fprintf(stderr,"Assertion failed on line %d of file \"%s\"\n",line,file); + exit(1); +} +/********************** From the file "build.c" *****************************/ +/* +** Routines to construction the finite state machine for the LEMON +** parser generator. +*/ + +/* Find a precedence symbol of every rule in the grammar. +** +** Those rules which have a precedence symbol coded in the input +** grammar using the "[symbol]" construct will already have the +** rp->precsym field filled. Other rules take as their precedence +** symbol the first RHS symbol with a defined precedence. If there +** are not RHS symbols with a defined precedence, the precedence +** symbol field is left blank. +*/ +void FindRulePrecedences(xp) +struct lemon *xp; +{ + struct rule *rp; + for(rp=xp->rule; rp; rp=rp->next){ + if( rp->precsym==0 ){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]->prec>=0 ){ + rp->precsym = rp->rhs[i]; + break; + } + } + } + } + return; +} + +/* Find all nonterminals which will generate the empty string. +** Then go back and compute the first sets of every nonterminal. +** The first set is the set of all terminal symbols which can begin +** a string generated by that nonterminal. +*/ +void FindFirstSets(lemp) +struct lemon *lemp; +{ + int i; + struct rule *rp; + int progress; + + for(i=0; insymbol; i++){ + lemp->symbols[i]->lambda = FALSE; + } + for(i=lemp->nterminal; insymbol; i++){ + lemp->symbols[i]->firstset = SetNew(); + } + + /* First compute all lambdas */ + do{ + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->lhs->lambda ) continue; + for(i=0; inrhs; i++){ + if( rp->rhs[i]->lambda==FALSE ) break; + } + if( i==rp->nrhs ){ + rp->lhs->lambda = TRUE; + progress = 1; + } + } + }while( progress ); + + /* Now compute all first sets */ + do{ + struct symbol *s1, *s2; + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + s1 = rp->lhs; + for(i=0; inrhs; i++){ + s2 = rp->rhs[i]; + if( s2->type==TERMINAL ){ + progress += SetAdd(s1->firstset,s2->index); + break; + }else if( s1==s2 ){ + if( s1->lambda==FALSE ) break; + }else{ + progress += SetUnion(s1->firstset,s2->firstset); + if( s2->lambda==FALSE ) break; + } + } + } + }while( progress ); + return; +} + +/* Compute all LR(0) states for the grammar. Links +** are added to between some states so that the LR(1) follow sets +** can be computed later. +*/ +PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */ +void FindStates(lemp) +struct lemon *lemp; +{ + struct symbol *sp; + struct rule *rp; + + Configlist_init(); + + /* Find the start symbol */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ){ + ErrorMsg(lemp->filename,0, +"The specified start symbol \"%s\" is not \ +in a nonterminal of the grammar. \"%s\" will be used as the start \ +symbol instead.",lemp->start,lemp->rule->lhs->name); + lemp->errorcnt++; + sp = lemp->rule->lhs; + } + }else{ + sp = lemp->rule->lhs; + } + + /* Make sure the start symbol doesn't occur on the right-hand side of + ** any rule. Report an error if it does. (YACC would generate a new + ** start symbol in this case.) */ + for(rp=lemp->rule; rp; rp=rp->next){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]==sp ){ + ErrorMsg(lemp->filename,0, +"The start symbol \"%s\" occurs on the \ +right-hand side of a rule. This will result in a parser which \ +does not work properly.",sp->name); + lemp->errorcnt++; + } + } + } + + /* The basis configuration set for the first state + ** is all rules which have the start symbol as their + ** left-hand side */ + for(rp=sp->rule; rp; rp=rp->nextlhs){ + struct config *newcfp; + newcfp = Configlist_addbasis(rp,0); + SetAdd(newcfp->fws,0); + } + + /* Compute the first state. All other states will be + ** computed automatically during the computation of the first one. + ** The returned pointer to the first state is not used. */ + (void)getstate(lemp); + return; +} + +/* Return a pointer to a state which is described by the configuration +** list which has been built from calls to Configlist_add. +*/ +PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */ +PRIVATE struct state *getstate(lemp) +struct lemon *lemp; +{ + struct config *cfp, *bp; + struct state *stp; + + /* Extract the sorted basis of the new state. The basis was constructed + ** by prior calls to "Configlist_addbasis()". */ + Configlist_sortbasis(); + bp = Configlist_basis(); + + /* Get a state with the same basis */ + stp = State_find(bp); + if( stp ){ + /* A state with the same basis already exists! Copy all the follow-set + ** propagation links from the state under construction into the + ** preexisting state, then return a pointer to the preexisting state */ + struct config *x, *y; + for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){ + Plink_copy(&y->bplp,x->bplp); + Plink_delete(x->fplp); + x->fplp = x->bplp = 0; + } + cfp = Configlist_return(); + Configlist_eat(cfp); + }else{ + /* This really is a new state. Construct all the details */ + Configlist_closure(lemp); /* Compute the configuration closure */ + Configlist_sort(); /* Sort the configuration closure */ + cfp = Configlist_return(); /* Get a pointer to the config list */ + stp = State_new(); /* A new state structure */ + MemoryCheck(stp); + stp->bp = bp; /* Remember the configuration basis */ + stp->cfp = cfp; /* Remember the configuration closure */ + stp->index = lemp->nstate++; /* Every state gets a sequence number */ + stp->ap = 0; /* No actions, yet. */ + State_insert(stp,stp->bp); /* Add to the state table */ + buildshifts(lemp,stp); /* Recursively compute successor states */ + } + return stp; +} + +/* Construct all successor states to the given state. A "successor" +** state is any state which can be reached by a shift action. +*/ +PRIVATE void buildshifts(lemp,stp) +struct lemon *lemp; +struct state *stp; /* The state from which successors are computed */ +{ + struct config *cfp; /* For looping thru the config closure of "stp" */ + struct config *bcfp; /* For the inner loop on config closure of "stp" */ + struct config *new; /* */ + struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ + struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ + struct state *newstp; /* A pointer to a successor state */ + + /* Each configuration becomes complete after it contibutes to a successor + ** state. Initially, all configurations are incomplete */ + for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE; + + /* Loop through all configurations of the state "stp" */ + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */ + if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */ + Configlist_reset(); /* Reset the new config set */ + sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ + + /* For every configuration in the state "stp" which has the symbol "sp" + ** following its dot, add the same configuration to the basis set under + ** construction but with the dot shifted one symbol to the right. */ + for(bcfp=cfp; bcfp; bcfp=bcfp->next){ + if( bcfp->status==COMPLETE ) continue; /* Already used */ + if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ + bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ + if( bsp!=sp ) continue; /* Must be same as for "cfp" */ + bcfp->status = COMPLETE; /* Mark this config as used */ + new = Configlist_addbasis(bcfp->rp,bcfp->dot+1); + Plink_add(&new->bplp,bcfp); + } + + /* Get a pointer to the state described by the basis configuration set + ** constructed in the preceding loop */ + newstp = getstate(lemp); + + /* The state "newstp" is reached from the state "stp" by a shift action + ** on the symbol "sp" */ + Action_add(&stp->ap,SHIFT,sp,newstp); + } +} + +/* +** Construct the propagation links +*/ +void FindLinks(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp, *other; + struct state *stp; + struct plink *plp; + + /* Housekeeping detail: + ** Add to every propagate link a pointer back to the state to + ** which the link is attached. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + cfp->stp = stp; + } + } + + /* Convert all backlinks into forward links. Only the forward + ** links are used in the follow-set computation. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + for(plp=cfp->bplp; plp; plp=plp->next){ + other = plp->cfp; + Plink_add(&other->fplp,cfp); + } + } + } +} + +/* Compute all followsets. +** +** A followset is the set of all symbols which can come immediately +** after a configuration. +*/ +void FindFollowSets(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp; + struct plink *plp; + int progress; + int change; + + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + cfp->status = INCOMPLETE; + } + } + + do{ + progress = 0; + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; + for(plp=cfp->fplp; plp; plp=plp->next){ + change = SetUnion(plp->cfp->fws,cfp->fws); + if( change ){ + plp->cfp->status = INCOMPLETE; + progress = 1; + } + } + cfp->status = COMPLETE; + } + } + }while( progress ); +} + +static int resolve_conflict(); + +/* Compute the reduce actions, and resolve conflicts. +*/ +void FindActions(lemp) +struct lemon *lemp; +{ + int i,j; + struct config *cfp; + struct state *stp; + struct symbol *sp; + struct rule *rp; + + /* Add all of the reduce actions + ** A reduce action is added for each element of the followset of + ** a configuration which has its dot at the extreme right. + */ + for(i=0; instate; i++){ /* Loop over all states */ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ + if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ + for(j=0; jnterminal; j++){ + if( SetFind(cfp->fws,j) ){ + /* Add a reduce action to the state "stp" which will reduce by the + ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ + Action_add(&stp->ap,REDUCE,lemp->symbols[j],cfp->rp); + } + } + } + } + } + + /* Add the accepting token */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ) sp = lemp->rule->lhs; + }else{ + sp = lemp->rule->lhs; + } + /* Add to the first state (which is always the starting state of the + ** finite state machine) an action to ACCEPT if the lookahead is the + ** start nonterminal. */ + Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0); + + /* Resolve conflicts */ + for(i=0; instate; i++){ + struct action *ap, *nap; + struct state *stp; + stp = lemp->sorted[i]; + assert( stp->ap ); + stp->ap = Action_sort(stp->ap); + for(ap=stp->ap; ap && ap->next; ap=nap){ + for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ + /* The two actions "ap" and "nap" have the same lookahead. + ** Figure out which one should be used */ + lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym); + } + } + } + + /* Report an error for each rule that can never be reduced. */ + for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = FALSE; + for(i=0; instate; i++){ + struct action *ap; + for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ + if( ap->type==REDUCE ) ap->x.rp->canReduce = TRUE; + } + } + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->canReduce ) continue; + ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n"); + lemp->errorcnt++; + } +} + +/* Resolve a conflict between the two given actions. If the +** conflict can't be resolve, return non-zero. +** +** NO LONGER TRUE: +** To resolve a conflict, first look to see if either action +** is on an error rule. In that case, take the action which +** is not associated with the error rule. If neither or both +** actions are associated with an error rule, then try to +** use precedence to resolve the conflict. +** +** If either action is a SHIFT, then it must be apx. This +** function won't work if apx->type==REDUCE and apy->type==SHIFT. +*/ +static int resolve_conflict(apx,apy,errsym) +struct action *apx; +struct action *apy; +struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ +{ + struct symbol *spx, *spy; + int errcnt = 0; + assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ + if( apx->type==SHIFT && apy->type==REDUCE ){ + spx = apx->sp; + spy = apy->x.rp->precsym; + if( spy==0 || spx->prec<0 || spy->prec<0 ){ + /* Not enough precedence information. */ + apy->type = CONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ /* Lower precedence wins */ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = SH_RESOLVED; + }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ + apy->type = RD_RESOLVED; /* associativity */ + }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ + apx->type = SH_RESOLVED; + }else{ + assert( spx->prec==spy->prec && spx->assoc==NONE ); + apy->type = CONFLICT; + errcnt++; + } + }else if( apx->type==REDUCE && apy->type==REDUCE ){ + spx = apx->x.rp->precsym; + spy = apy->x.rp->precsym; + if( spx==0 || spy==0 || spx->prec<0 || + spy->prec<0 || spx->prec==spy->prec ){ + apy->type = CONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = RD_RESOLVED; + } + }else{ + /* Can't happen. Shifts have to come before Reduces on the + ** list because the reduces were added last. Hence, if apx->type==REDUCE + ** then it is impossible for apy->type==SHIFT */ + } + return errcnt; +} +/********************* From the file "configlist.c" *************************/ +/* +** Routines to processing a configuration list and building a state +** in the LEMON parser generator. +*/ + +static struct config *freelist = 0; /* List of free configurations */ +static struct config *current = 0; /* Top of list of configurations */ +static struct config **currentend = 0; /* Last on list of configs */ +static struct config *basis = 0; /* Top of list of basis configs */ +static struct config **basisend = 0; /* End of list of basis configs */ + +/* Return a pointer to a new configuration */ +PRIVATE struct config *newconfig(){ + struct config *new; + if( freelist==0 ){ + int i; + int amt = 3; + freelist = (struct config *)malloc( sizeof(struct config)*amt ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new configuration."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* The configuration "old" is no longer used */ +PRIVATE void deleteconfig(old) +struct config *old; +{ + old->next = freelist; + freelist = old; +} + +/* Initialized the configuration list builder */ +void Configlist_init(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_clear(0); + return; +} + +/* Add another configuration to the configuration list */ +struct config *Configlist_add(rp,dot) +struct rule *rp; /* The rule */ +int dot; /* Index into the RHS of the rule where the dot goes */ +{ + struct config *cfp, model; + + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + Configtable_insert(cfp); + } + return cfp; +} + +/* Add a basis configuration to the configuration list */ +struct config *Configlist_addbasis(rp,dot) +struct rule *rp; +int dot; +{ + struct config *cfp, model; + + assert( basisend!=0 ); + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + *basisend = cfp; + basisend = &cfp->bp; + Configtable_insert(cfp); + } + return cfp; +} + +/* Compute the closure of the configuration list */ +void Configlist_closure(lemp) +struct lemon *lemp; +{ + struct config *cfp, *newcfp; + struct rule *rp, *newrp; + struct symbol *sp, *xsp; + int i, dot; + + assert( currentend!=0 ); + for(cfp=current; cfp; cfp=cfp->next){ + rp = cfp->rp; + dot = cfp->dot; + if( dot>=rp->nrhs ) continue; + sp = rp->rhs[dot]; + if( sp->type==NONTERMINAL ){ + if( sp->rule==0 && sp!=lemp->errsym ){ + ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.", + sp->name); + lemp->errorcnt++; + } + for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){ + newcfp = Configlist_add(newrp,0); + for(i=dot+1; inrhs; i++){ + xsp = rp->rhs[i]; + if( xsp->type==TERMINAL ){ + SetAdd(newcfp->fws,xsp->index); + break; + }else{ + SetUnion(newcfp->fws,xsp->firstset); + if( xsp->lambda==FALSE ) break; + } + } + if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); + } + } + } + return; +} + +/* Sort the configuration list */ +void Configlist_sort(){ + current = (struct config *)msort(current,&(current->next),Configcmp); + currentend = 0; + return; +} + +/* Sort the basis configuration list */ +void Configlist_sortbasis(){ + basis = (struct config *)msort(current,&(current->bp),Configcmp); + basisend = 0; + return; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_return(){ + struct config *old; + old = current; + current = 0; + currentend = 0; + return old; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_basis(){ + struct config *old; + old = basis; + basis = 0; + basisend = 0; + return old; +} + +/* Free all elements of the given configuration list */ +void Configlist_eat(cfp) +struct config *cfp; +{ + struct config *nextcfp; + for(; cfp; cfp=nextcfp){ + nextcfp = cfp->next; + assert( cfp->fplp==0 ); + assert( cfp->bplp==0 ); + if( cfp->fws ) SetFree(cfp->fws); + deleteconfig(cfp); + } + return; +} +/***************** From the file "error.c" *********************************/ +/* +** Code for printing error message. +*/ + +/* Find a good place to break "msg" so that its length is at least "min" +** but no more than "max". Make the point as close to max as possible. +*/ +static int findbreak(msg,min,max) +char *msg; +int min; +int max; +{ + int i,spot; + char c; + for(i=spot=min; i<=max; i++){ + c = msg[i]; + if( c=='\t' ) msg[i] = ' '; + if( c=='\n' ){ msg[i] = ' '; spot = i; break; } + if( c==0 ){ spot = i; break; } + if( c=='-' && i0 ){ + sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno); + }else{ + sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename); + } + prefixsize = strlen(prefix); + availablewidth = LINEWIDTH - prefixsize; + + /* Generate the error message */ + vsprintf(errmsg,format,ap); + va_end(ap); + errmsgsize = strlen(errmsg); + /* Remove trailing '\n's from the error message. */ + while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){ + errmsg[--errmsgsize] = 0; + } + + /* Print the error message */ + base = 0; + while( errmsg[base]!=0 ){ + end = restart = findbreak(&errmsg[base],0,availablewidth); + restart += base; + while( errmsg[restart]==' ' ) restart++; + fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]); + base = restart; + } +} +/**************** From the file "main.c" ************************************/ +/* +** Main program file for the LEMON parser generator. +*/ + +/* Report an out-of-memory condition and abort. This function +** is used mostly by the "MemoryCheck" macro in struct.h +*/ +void memory_error(){ + fprintf(stderr,"Out of memory. Aborting...\n"); + exit(1); +} + + +/* The main program. Parse the command line and do it... */ +int main(argc,argv) +int argc; +char **argv; +{ + static int version = 0; + static int rpflag = 0; + static int basisflag = 0; + static int compress = 0; + static int quiet = 0; + static int statistics = 0; + static int mhflag = 0; + static struct s_options options[] = { + {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, + {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, + {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, + {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file"}, + {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, + {OPT_FLAG, "s", (char*)&statistics, "Print parser stats to standard output."}, + {OPT_FLAG, "x", (char*)&version, "Print the version number."}, + {OPT_FLAG,0,0,0} + }; + int i; + struct lemon lem; + + optinit(argv,options,stderr); + if( version ){ + printf("Lemon version 1.0\n" + "Copyright 1991-1997 by D. Richard Hipp\n" + "Freely distributable under the GNU Public License.\n" + ); + exit(0); + } + if( optnargs()!=1 ){ + fprintf(stderr,"Exactly one filename argument is required.\n"); + exit(1); + } + lem.errorcnt = 0; + + /* Initialize the machine */ + Strsafe_init(); + Symbol_init(); + State_init(); + lem.argv0 = argv[0]; + lem.filename = optarg(0); + lem.basisflag = basisflag; + lem.nconflict = 0; + lem.name = lem.include = lem.arg = lem.tokentype = lem.start = 0; + lem.stacksize = 0; + lem.error = lem.overflow = lem.failure = lem.accept = lem.tokendest = + lem.tokenprefix = lem.outname = lem.extracode = 0; + lem.tablesize = 0; + Symbol_new("$"); + lem.errsym = Symbol_new("error"); + + /* Parse the input file */ + Parse(&lem); + if( lem.errorcnt ) exit(lem.errorcnt); + if( lem.rule==0 ){ + fprintf(stderr,"Empty grammar.\n"); + exit(1); + } + + /* Count and index the symbols of the grammar */ + lem.nsymbol = Symbol_count(); + Symbol_new("{default}"); + lem.symbols = Symbol_arrayof(); + qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), + (int(*)())Symbolcmpp); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + for(i=1; isupper(lem.symbols[i]->name[0]); i++); + lem.nterminal = i; + + /* Generate a reprint of the grammar, if requested on the command line */ + if( rpflag ){ + Reprint(&lem); + }else{ + /* Initialize the size for all follow and first sets */ + SetSize(lem.nterminal); + + /* Find the precedence for every production rule (that has one) */ + FindRulePrecedences(&lem); + + /* Compute the lambda-nonterminals and the first-sets for every + ** nonterminal */ + FindFirstSets(&lem); + + /* Compute all LR(0) states. Also record follow-set propagation + ** links so that the follow-set can be computed later */ + lem.nstate = 0; + FindStates(&lem); + lem.sorted = State_arrayof(); + + /* Tie up loose ends on the propagation links */ + FindLinks(&lem); + + /* Compute the follow set of every reducible configuration */ + FindFollowSets(&lem); + + /* Compute the action tables */ + FindActions(&lem); + + /* Compress the action tables */ + if( compress==0 ) CompressTables(&lem); + + /* Generate a report of the parser generated. (the "y.output" file) */ + if( !quiet ) ReportOutput(&lem); + + /* Generate the source code for the parser */ + ReportTable(&lem, mhflag); + + /* Produce a header file for use by the scanner. (This step is + ** omitted if the "-m" option is used because makeheaders will + ** generate the file for us.) */ + if( !mhflag ) ReportHeader(&lem); + } + if( statistics ){ + printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n", + lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule); + printf(" %d states, %d parser table entries, %d conflicts\n", + lem.nstate, lem.tablesize, lem.nconflict); + } + if( lem.nconflict ){ + fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); + } + exit(lem.errorcnt + lem.nconflict); +} +/******************** From the file "msort.c" *******************************/ +/* +** A generic merge-sort program. +** +** USAGE: +** Let "ptr" be a pointer to some structure which is at the head of +** a null-terminated list. Then to sort the list call: +** +** ptr = msort(ptr,&(ptr->next),cmpfnc); +** +** In the above, "cmpfnc" is a pointer to a function which compares +** two instances of the structure and returns an integer, as in +** strcmp. The second argument is a pointer to the pointer to the +** second element of the linked list. This address is used to compute +** the offset to the "next" field within the structure. The offset to +** the "next" field must be constant for all structures in the list. +** +** The function returns a new pointer which is the head of the list +** after sorting. +** +** ALGORITHM: +** Merge-sort. +*/ + +/* +** Return a pointer to the next structure in the linked list. +*/ +#define NEXT(A) (*(char**)(((int)A)+offset)) + +/* +** Inputs: +** a: A sorted, null-terminated linked list. (May be null). +** b: A sorted, null-terminated linked list. (May be null). +** cmp: A pointer to the comparison function. +** offset: Offset in the structure to the "next" field. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** of both a and b. +** +** Side effects: +** The "next" pointers for elements in the lists a and b are +** changed. +*/ +static char *merge(a,b,cmp,offset) +char *a; +char *b; +int (*cmp)(); +int offset; +{ + char *ptr, *head; + + if( a==0 ){ + head = b; + }else if( b==0 ){ + head = a; + }else{ + if( (*cmp)(a,b)<0 ){ + ptr = a; + a = NEXT(a); + }else{ + ptr = b; + b = NEXT(b); + } + head = ptr; + while( a && b ){ + if( (*cmp)(a,b)<0 ){ + NEXT(ptr) = a; + ptr = a; + a = NEXT(a); + }else{ + NEXT(ptr) = b; + ptr = b; + b = NEXT(b); + } + } + if( a ) NEXT(ptr) = a; + else NEXT(ptr) = b; + } + return head; +} + +/* +** Inputs: +** list: Pointer to a singly-linked list of structures. +** next: Pointer to pointer to the second element of the list. +** cmp: A comparison function. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** orginally in list. +** +** Side effects: +** The "next" pointers for elements in list are changed. +*/ +#define LISTSIZE 30 +char *msort(list,next,cmp) +char *list; +char **next; +int (*cmp)(); +{ + int offset; + char *ep; + char *set[LISTSIZE]; + int i; + offset = (int)next - (int)list; + for(i=0; istate = WAITING_FOR_DECL_KEYWORD; + }else if( islower(x[0]) ){ + psp->lhs = Symbol_new(x); + psp->nrhs = 0; + psp->lhsalias = 0; + psp->state = WAITING_FOR_ARROW; + }else if( x[0]=='{' ){ + if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"There is not prior rule opon which to attach the code \ +fragment which begins on this line."); + psp->errorcnt++; + }else if( psp->prevrule->code!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Code fragment beginning on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->line = psp->tokenlineno; + psp->prevrule->code = &x[1]; + } + }else if( x[0]=='[' ){ + psp->state = PRECEDENCE_MARK_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Token \"%s\" should be either \"%%\" or a nonterminal name.", + x); + psp->errorcnt++; + } + break; + case PRECEDENCE_MARK_1: + if( !isupper(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The precedence symbol must be a terminal."); + psp->errorcnt++; + }else if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "There is no prior rule to assign precedence \"[%s]\".",x); + psp->errorcnt++; + }else if( psp->prevrule->precsym!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Precedence mark on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->precsym = Symbol_new(x); + } + psp->state = PRECEDENCE_MARK_2; + break; + case PRECEDENCE_MARK_2: + if( x[0]!=']' ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"]\" on precedence mark."); + psp->errorcnt++; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + break; + case WAITING_FOR_ARROW: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else if( x[0]=='(' ){ + psp->state = LHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Expected to see a \":\" following the LHS symbol \"%s\".", + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->lhsalias = x; + psp->state = LHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the LHS \"%s\"\n", + x,psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = LHS_ALIAS_3; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_3: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"->\" following: \"%s(%s)\".", + psp->lhs->name,psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case IN_RHS: + if( x[0]=='.' ){ + struct rule *rp; + rp = (struct rule *)malloc( sizeof(struct rule) + + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs ); + if( rp==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't allocate enough memory for this rule."); + psp->errorcnt++; + psp->prevrule = 0; + }else{ + int i; + rp->ruleline = psp->tokenlineno; + rp->rhs = (struct symbol**)&rp[1]; + rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]); + for(i=0; inrhs; i++){ + rp->rhs[i] = psp->rhs[i]; + rp->rhsalias[i] = psp->alias[i]; + } + rp->lhs = psp->lhs; + rp->lhsalias = psp->lhsalias; + rp->nrhs = psp->nrhs; + rp->code = 0; + rp->precsym = 0; + rp->index = psp->gp->nrule++; + rp->nextlhs = rp->lhs->rule; + rp->lhs->rule = rp; + rp->next = 0; + if( psp->firstrule==0 ){ + psp->firstrule = psp->lastrule = rp; + }else{ + psp->lastrule->next = rp; + psp->lastrule = rp; + } + psp->prevrule = rp; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isalpha(x[0]) ){ + if( psp->nrhs>=MAXRHS ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Too many symbol on RHS or rule beginning at \"%s\".", + x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + }else{ + psp->rhs[psp->nrhs] = Symbol_new(x); + psp->alias[psp->nrhs] = 0; + psp->nrhs++; + } + }else if( x[0]=='(' && psp->nrhs>0 ){ + psp->state = RHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal character on RHS of rule: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->alias[psp->nrhs-1] = x; + psp->state = RHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", + x,psp->rhs[psp->nrhs-1]->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case WAITING_FOR_DECL_KEYWORD: + if( isalpha(x[0]) ){ + psp->declkeyword = x; + psp->declargslot = 0; + psp->decllnslot = 0; + psp->state = WAITING_FOR_DECL_ARG; + if( strcmp(x,"name")==0 ){ + psp->declargslot = &(psp->gp->name); + }else if( strcmp(x,"include")==0 ){ + psp->declargslot = &(psp->gp->include); + psp->decllnslot = &psp->gp->includeln; + }else if( strcmp(x,"code")==0 ){ + psp->declargslot = &(psp->gp->extracode); + psp->decllnslot = &psp->gp->extracodeln; + }else if( strcmp(x,"token_destructor")==0 ){ + psp->declargslot = &psp->gp->tokendest; + psp->decllnslot = &psp->gp->tokendestln; + }else if( strcmp(x,"token_prefix")==0 ){ + psp->declargslot = &psp->gp->tokenprefix; + }else if( strcmp(x,"syntax_error")==0 ){ + psp->declargslot = &(psp->gp->error); + psp->decllnslot = &psp->gp->errorln; + }else if( strcmp(x,"parse_accept")==0 ){ + psp->declargslot = &(psp->gp->accept); + psp->decllnslot = &psp->gp->acceptln; + }else if( strcmp(x,"parse_failure")==0 ){ + psp->declargslot = &(psp->gp->failure); + psp->decllnslot = &psp->gp->failureln; + }else if( strcmp(x,"stack_overflow")==0 ){ + psp->declargslot = &(psp->gp->overflow); + psp->decllnslot = &psp->gp->overflowln; + }else if( strcmp(x,"extra_argument")==0 ){ + psp->declargslot = &(psp->gp->arg); + }else if( strcmp(x,"token_type")==0 ){ + psp->declargslot = &(psp->gp->tokentype); + }else if( strcmp(x,"stack_size")==0 ){ + psp->declargslot = &(psp->gp->stacksize); + }else if( strcmp(x,"start_symbol")==0 ){ + psp->declargslot = &(psp->gp->start); + }else if( strcmp(x,"left")==0 ){ + psp->preccounter++; + psp->declassoc = LEFT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"right")==0 ){ + psp->preccounter++; + psp->declassoc = RIGHT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"nonassoc")==0 ){ + psp->preccounter++; + psp->declassoc = NONE; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"destructor")==0 ){ + psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; + }else if( strcmp(x,"type")==0 ){ + psp->state = WAITING_FOR_DATATYPE_SYMBOL; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Unknown declaration keyword: \"%%%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal declaration keyword: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_DESTRUCTOR_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->destructor; + psp->decllnslot = &sp->destructorln; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_DATATYPE_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->datatype; + psp->decllnslot = 0; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_PRECEDENCE_SYMBOL: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isupper(x[0]) ){ + struct symbol *sp; + sp = Symbol_new(x); + if( sp->prec>=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol \"%s\" has already be given a precedence.",x); + psp->errorcnt++; + }else{ + sp->prec = psp->preccounter; + sp->assoc = psp->declassoc; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't assign a precedence to \"%s\".",x); + psp->errorcnt++; + } + break; + case WAITING_FOR_DECL_ARG: + if( (x[0]=='{' || x[0]=='\"' || isalnum(x[0])) ){ + if( *(psp->declargslot)!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The argument \"%s\" to declaration \"%%%s\" is not the first.", + x[0]=='\"' ? &x[1] : x,psp->declkeyword); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + *(psp->declargslot) = (x[0]=='\"' || x[0]=='{') ? &x[1] : x; + if( psp->decllnslot ) *psp->decllnslot = psp->tokenlineno; + psp->state = WAITING_FOR_DECL_OR_RULE; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal argument to %%%s: %s",psp->declkeyword,x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case RESYNC_AFTER_RULE_ERROR: +/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; +** break; */ + case RESYNC_AFTER_DECL_ERROR: + if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; + if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD; + break; + } +} + +/* In spite of its name, this function is really a scanner. It read +** in the entire input file (all at once) then tokenizes it. Each +** token is passed to the function "parseonetoken" which builds all +** the appropriate data structures in the global state vector "gp". +*/ +void Parse(gp) +struct lemon *gp; +{ + struct pstate ps; + FILE *fp; + char *filebuf; + int filesize; + int lineno; + int c; + char *cp, *nextcp; + int startline = 0; + + ps.gp = gp; + ps.filename = gp->filename; + ps.errorcnt = 0; + ps.state = INITIALIZE; + + /* Begin by reading the input file */ + fp = fopen(ps.filename,"rb"); + if( fp==0 ){ + ErrorMsg(ps.filename,0,"Can't open this file for reading."); + gp->errorcnt++; + return; + } + fseek(fp,0,2); + filesize = ftell(fp); + rewind(fp); + filebuf = (char *)malloc( filesize+1 ); + if( filebuf==0 ){ + ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", + filesize+1); + gp->errorcnt++; + return; + } + if( fread(filebuf,1,filesize,fp)!=filesize ){ + ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", + filesize); + free(filebuf); + gp->errorcnt++; + return; + } + fclose(fp); + filebuf[filesize] = 0; + + /* Now scan the text of the input file */ + lineno = 1; + for(cp=filebuf; (c= *cp)!=0; ){ + if( c=='\n' ) lineno++; /* Keep track of the line number */ + if( isspace(c) ){ cp++; continue; } /* Skip all white space */ + if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */ + cp+=2; + while( (c= *cp)!=0 && c!='\n' ) cp++; + continue; + } + if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */ + cp+=2; + while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c ) cp++; + continue; + } + ps.tokenstart = cp; /* Mark the beginning of the token */ + ps.tokenlineno = lineno; /* Linenumber on which token begins */ + if( c=='\"' ){ /* String literals */ + cp++; + while( (c= *cp)!=0 && c!='\"' ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c==0 ){ + ErrorMsg(ps.filename,startline, +"String starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( c=='{' ){ /* A block of C code */ + int level; + cp++; + for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){ + if( c=='\n' ) lineno++; + else if( c=='{' ) level++; + else if( c=='}' ) level--; + else if( c=='/' && cp[1]=='*' ){ /* Skip comments */ + int prevc; + cp = &cp[2]; + prevc = 0; + while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){ + if( c=='\n' ) lineno++; + prevc = c; + cp++; + } + }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */ + cp = &cp[2]; + while( (c= *cp)!=0 && c!='\n' ) cp++; + if( c ) lineno++; + }else if( c=='\'' || c=='\"' ){ /* String a character literals */ + int startchar, prevc; + startchar = c; + prevc = 0; + for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){ + if( c=='\n' ) lineno++; + if( prevc=='\\' ) prevc = 0; + else prevc = c; + } + } + } + if( c==0 ){ + ErrorMsg(ps.filename,startline, +"C code starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( isalnum(c) ){ /* Identifiers */ + while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ + cp += 3; + nextcp = cp; + }else{ /* All other (one character) operators */ + cp++; + nextcp = cp; + } + c = *cp; + *cp = 0; /* Null terminate the token */ + parseonetoken(&ps); /* Parse the token */ + *cp = c; /* Restore the buffer */ + cp = nextcp; + } + free(filebuf); /* Release the buffer after parsing */ + gp->rule = ps.firstrule; + gp->errorcnt = ps.errorcnt; +} +/*************************** From the file "plink.c" *********************/ +/* +** Routines processing configuration follow-set propagation links +** in the LEMON parser generator. +*/ +static struct plink *plink_freelist = 0; + +/* Allocate a new plink */ +struct plink *Plink_new(){ + struct plink *new; + + if( plink_freelist==0 ){ + int i; + int amt = 100; + plink_freelist = (struct plink *)malloc( sizeof(struct plink)*amt ); + if( plink_freelist==0 ){ + fprintf(stderr, + "Unable to allocate memory for a new follow-set propagation link.\n"); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Add a plink to a plink list */ +void Plink_add(plpp,cfp) +struct plink **plpp; +struct config *cfp; +{ + struct plink *new; + new = Plink_new(); + new->next = *plpp; + *plpp = new; + new->cfp = cfp; +} + +/* Transfer every plink on the list "from" to the list "to" */ +void Plink_copy(to,from) +struct plink **to; +struct plink *from; +{ + struct plink *nextpl; + while( from ){ + nextpl = from->next; + from->next = *to; + *to = from; + from = nextpl; + } +} + +/* Delete every plink on the list */ +void Plink_delete(plp) +struct plink *plp; +{ + struct plink *nextpl; + + while( plp ){ + nextpl = plp->next; + plp->next = plink_freelist; + plink_freelist = plp; + plp = nextpl; + } +} +/*********************** From the file "report.c" **************************/ +/* +** Procedures for generating reports and tables in the LEMON parser generator. +*/ + +/* Generate a filename with the given suffix. Space to hold the +** name comes from malloc() and must be freed by the calling +** function. +*/ +PRIVATE char *file_makename(lemp,suffix) +struct lemon *lemp; +char *suffix; +{ + char *name; + char *cp; + + name = malloc( strlen(lemp->filename) + strlen(suffix) + 5 ); + if( name==0 ){ + fprintf(stderr,"Can't allocate space for a filename.\n"); + exit(1); + } + strcpy(name,lemp->filename); + cp = strrchr(name,'.'); + if( cp ) *cp = 0; + strcat(name,suffix); + return name; +} + +/* Open a file with a name based on the name of the input file, +** but with a different (specified) suffix, and return a pointer +** to the stream */ +PRIVATE FILE *file_open(lemp,suffix,mode) +struct lemon *lemp; +char *suffix; +char *mode; +{ + FILE *fp; + + if( lemp->outname ) free(lemp->outname); + lemp->outname = file_makename(lemp, suffix); + fp = fopen(lemp->outname,mode); + if( fp==0 && *mode=='w' ){ + fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); + lemp->errorcnt++; + return 0; + } + return fp; +} + +/* Duplicate the input file without comments and without actions +** on rules */ +void Reprint(lemp) +struct lemon *lemp; +{ + struct rule *rp; + struct symbol *sp; + int i, j, maxlen, len, ncolumns, skip; + printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); + maxlen = 10; + for(i=0; insymbol; i++){ + sp = lemp->symbols[i]; + len = strlen(sp->name); + if( len>maxlen ) maxlen = len; + } + ncolumns = 76/(maxlen+5); + if( ncolumns<1 ) ncolumns = 1; + skip = (lemp->nsymbol + ncolumns - 1)/ncolumns; + for(i=0; insymbol; j+=skip){ + sp = lemp->symbols[j]; + assert( sp->index==j ); + printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name); + } + printf("\n"); + } + for(rp=lemp->rule; rp; rp=rp->next){ + printf("%s",rp->lhs->name); +/* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ + printf(" ::="); + for(i=0; inrhs; i++){ + printf(" %s",rp->rhs[i]->name); +/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ + } + printf("."); + if( rp->precsym ) printf(" [%s]",rp->precsym->name); +/* if( rp->code ) printf("\n %s",rp->code); */ + printf("\n"); + } +} + +void ConfigPrint(fp,cfp) +FILE *fp; +struct config *cfp; +{ + struct rule *rp; + int i; + rp = cfp->rp; + fprintf(fp,"%s ::=",rp->lhs->name); + for(i=0; i<=rp->nrhs; i++){ + if( i==cfp->dot ) fprintf(fp," *"); + if( i==rp->nrhs ) break; + fprintf(fp," %s",rp->rhs[i]->name); + } +} + +/* #define TEST */ +#ifdef TEST +/* Print a set */ +PRIVATE void SetPrint(out,set,lemp) +FILE *out; +char *set; +struct lemon *lemp; +{ + int i; + char *spacer; + spacer = ""; + fprintf(out,"%12s[",""); + for(i=0; interminal; i++){ + if( SetFind(set,i) ){ + fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); + spacer = " "; + } + } + fprintf(out,"]\n"); +} + +/* Print a plink chain */ +PRIVATE void PlinkPrint(out,plp,tag) +FILE *out; +struct plink *plp; +char *tag; +{ + while( plp ){ + fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->index); + ConfigPrint(out,plp->cfp); + fprintf(out,"\n"); + plp = plp->next; + } +} +#endif + +/* Print an action to the given file descriptor. Return FALSE if +** nothing was actually printed. +*/ +int PrintAction(struct action *ap, FILE *fp, int indent){ + int result = 1; + switch( ap->type ){ + case SHIFT: + fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->index); + break; + case REDUCE: + fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index); + break; + case ACCEPT: + fprintf(fp,"%*s accept",indent,ap->sp->name); + break; + case ERROR: + fprintf(fp,"%*s error",indent,ap->sp->name); + break; + case CONFLICT: + fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.rp->index); + break; + case SH_RESOLVED: + case RD_RESOLVED: + case NOT_USED: + result = 0; + break; + } + return result; +} + +/* Generate the "y.output" log file */ +void ReportOutput(lemp) +struct lemon *lemp; +{ + int i; + struct state *stp; + struct config *cfp; + struct action *ap; + FILE *fp; + + fp = file_open(lemp,".out","w"); + if( fp==0 ) return; + fprintf(fp," \b"); + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + fprintf(fp,"State %d:\n",stp->index); + if( lemp->basisflag ) cfp=stp->bp; + else cfp=stp->cfp; + while( cfp ){ + char buf[20]; + if( cfp->dot==cfp->rp->nrhs ){ + sprintf(buf,"(%d)",cfp->rp->index); + fprintf(fp," %5s ",buf); + }else{ + fprintf(fp," "); + } + ConfigPrint(fp,cfp); + fprintf(fp,"\n"); +#ifdef TEST + SetPrint(fp,cfp->fws,lemp); + PlinkPrint(fp,cfp->fplp,"To "); + PlinkPrint(fp,cfp->bplp,"From"); +#endif + if( lemp->basisflag ) cfp=cfp->bp; + else cfp=cfp->next; + } + fprintf(fp,"\n"); + for(ap=stp->ap; ap; ap=ap->next){ + if( PrintAction(ap,fp,30) ) fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + } + fclose(fp); + return; +} + +/* Search for the file "name" which is in the same directory as +** the exacutable */ +PRIVATE char *pathsearch(argv0,name,modemask) +char *argv0; +char *name; +int modemask; +{ + char *pathlist; + char *path,*cp; + char c; + extern int access(); + +#ifdef __WIN32__ + cp = strrchr(argv0,'\\'); +#else + cp = strrchr(argv0,'/'); +#endif + if( cp ){ + c = *cp; + *cp = 0; + path = (char *)malloc( strlen(argv0) + strlen(name) + 2 ); + if( path ) sprintf(path,"%s/%s",argv0,name); + *cp = c; + }else{ + extern char *getenv(); + pathlist = getenv("PATH"); + if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; + path = (char *)malloc( strlen(pathlist)+strlen(name)+2 ); + if( path!=0 ){ + while( *pathlist ){ + cp = strchr(pathlist,':'); + if( cp==0 ) cp = &pathlist[strlen(pathlist)]; + c = *cp; + *cp = 0; + sprintf(path,"%s/%s",pathlist,name); + *cp = c; + if( c==0 ) pathlist = ""; + else pathlist = &cp[1]; + if( access(path,modemask)==0 ) break; + } + } + } + return path; +} + +/* Given an action, compute the integer value for that action +** which is to be put in the action table of the generated machine. +** Return negative if no action should be generated. +*/ +PRIVATE int compute_action(lemp,ap) +struct lemon *lemp; +struct action *ap; +{ + int act; + switch( ap->type ){ + case SHIFT: act = ap->x.stp->index; break; + case REDUCE: act = ap->x.rp->index + lemp->nstate; break; + case ERROR: act = lemp->nstate + lemp->nrule; break; + case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break; + default: act = -1; break; + } + return act; +} + +#define LINESIZE 1000 +/* The next cluster of routines are for reading the template file +** and writing the results to the generated parser */ +/* The first function transfers data from "in" to "out" until +** a line is seen which begins with "%%". The line number is +** tracked. +** +** if name!=0, then any word that begin with "Parse" is changed to +** begin with *name instead. +*/ +PRIVATE void tplt_xfer(name,in,out,lineno) +char *name; +FILE *in; +FILE *out; +int *lineno; +{ + int i, iStart; + char line[LINESIZE]; + while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ + (*lineno)++; + iStart = 0; + if( name ){ + for(i=0; line[i]; i++){ + if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0 + && (i==0 || !isalpha(line[i-1])) + ){ + if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]); + fprintf(out,"%s",name); + i += 4; + iStart = i+1; + } + } + } + fprintf(out,"%s",&line[iStart]); + } +} + +/* The next function finds the template file and opens it, returning +** a pointer to the opened file. */ +PRIVATE FILE *tplt_open(lemp) +struct lemon *lemp; +{ + static char templatename[] = "lempar.c"; + char buf[1000]; + FILE *in; + char *tpltname; + char *cp; + + cp = strrchr(lemp->filename,'.'); + if( cp ){ + sprintf(buf,"%.*s.lt",(int)cp-(int)lemp->filename,lemp->filename); + }else{ + sprintf(buf,"%s.lt",lemp->filename); + } + if( access(buf,004)==0 ){ + tpltname = buf; + }else{ + tpltname = pathsearch(lemp->argv0,templatename,0); + } + if( tpltname==0 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(tpltname,"r"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",templatename); + lemp->errorcnt++; + return 0; + } + return in; +} + +/* Print a string to the file and keep the linenumber up to date */ +PRIVATE void tplt_print(out,lemp,str,strln,lineno) +FILE *out; +struct lemon *lemp; +char *str; +int strln; +int *lineno; +{ + if( str==0 ) return; + fprintf(out,"#line %d \"%s\"\n",strln,lemp->filename); (*lineno)++; + while( *str ){ + if( *str=='\n' ) (*lineno)++; + putc(*str,out); + str++; + } + fprintf(out,"\n#line %d \"%s\"\n",*lineno+2,lemp->outname); (*lineno)+=2; + return; +} + +/* +** The following routine emits code for the destructor for the +** symbol sp +*/ +void emit_destructor_code(out,sp,lemp,lineno) +FILE *out; +struct symbol *sp; +struct lemon *lemp; +int *lineno; +{ + char *cp; + + int linecnt = 0; + if( sp->type==TERMINAL ){ + cp = lemp->tokendest; + if( cp==0 ) return; + fprintf(out,"#line %d \"%s\"\n{",lemp->tokendestln,lemp->filename); + }else{ + cp = sp->destructor; + if( cp==0 ) return; + fprintf(out,"#line %d \"%s\"\n{",sp->destructorln,lemp->filename); + } + for(; *cp; cp++){ + if( *cp=='$' && cp[1]=='$' ){ + fprintf(out,"(yypminor->yy%d)",sp->dtnum); + cp++; + continue; + } + if( *cp=='\n' ) linecnt++; + fputc(*cp,out); + } + (*lineno) += 3 + linecnt; + fprintf(out,"}\n#line %d \"%s\"\n",*lineno,lemp->outname); + return; +} + +/* +** Return TRUE (non-zero) if the given symbol has a distructor. +*/ +int has_destructor(sp, lemp) +struct symbol *sp; +struct lemon *lemp; +{ + int ret; + if( sp->type==TERMINAL ){ + ret = lemp->tokendest!=0; + }else{ + ret = sp->destructor!=0; + } + return ret; +} + +/* +** Generate code which executes when the rule "rp" is reduced. Write +** the code to "out". Make sure lineno stays up-to-date. +*/ +PRIVATE void emit_code(out,rp,lemp,lineno) +FILE *out; +struct rule *rp; +struct lemon *lemp; +int *lineno; +{ + char *cp, *xp; + int linecnt = 0; + int i; + char lhsused = 0; /* True if the LHS element has been used */ + char used[MAXRHS]; /* True for each RHS element which is used */ + + for(i=0; inrhs; i++) used[i] = 0; + lhsused = 0; + + /* Generate code to do the reduce action */ + if( rp->code ){ + fprintf(out,"#line %d \"%s\"\n{",rp->line,lemp->filename); + for(cp=rp->code; *cp; cp++){ + if( isalpha(*cp) && (cp==rp->code || !isalnum(cp[-1])) ){ + char saved; + for(xp= &cp[1]; isalnum(*xp); xp++); + saved = *xp; + *xp = 0; + if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ + fprintf(out,"yygotominor.yy%d",rp->lhs->dtnum); + cp = xp; + lhsused = 1; + }else{ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ + fprintf(out,"yymsp[%d].minor.yy%d",i-rp->nrhs+1,rp->rhs[i]->dtnum); + cp = xp; + used[i] = 1; + break; + } + } + } + *xp = saved; + } + if( *cp=='\n' ) linecnt++; + fputc(*cp,out); + } /* End loop */ + (*lineno) += 3 + linecnt; + fprintf(out,"}\n#line %d \"%s\"\n",*lineno,lemp->outname); + } /* End if( rp->code ) */ + + /* Check to make sure the LHS has been used */ + if( rp->lhsalias && !lhsused ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label \"%s\" for \"%s(%s)\" is never used.", + rp->lhsalias,rp->lhs->name,rp->lhsalias); + lemp->errorcnt++; + } + + /* Generate destructor code for RHS symbols which are not used in the + ** reduce code */ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && !used[i] ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label $%s$ for \"%s(%s)\" is never used.", + rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); + lemp->errorcnt++; + }else if( rp->rhsalias[i]==0 ){ + if( has_destructor(rp->rhs[i],lemp) ){ + fprintf(out," yy_destructor(%d,&yymsp[%d].minor);\n", + rp->rhs[i]->index,i-rp->nrhs+1); (*lineno)++; + }else{ + fprintf(out," /* No destructor defined for %s */\n", + rp->rhs[i]->name); + (*lineno)++; + } + } + } + return; +} + +/* +** Print the definition of the union used for the parser's data stack. +** This union contains fields for every possible data type for tokens +** and nonterminals. In the process of computing and printing this +** union, also set the ".dtnum" field of every terminal and nonterminal +** symbol. +*/ +void print_stack_union(out,lemp,plineno,mhflag) +FILE *out; /* The output stream */ +struct lemon *lemp; /* The main info structure for this parser */ +int *plineno; /* Pointer to the line number */ +int mhflag; /* True if generating makeheaders output */ +{ + int lineno = *plineno; /* The line number of the output */ + char **types; /* A hash table of datatypes */ + int arraysize; /* Size of the "types" array */ + int maxdtlength; /* Maximum length of any ".datatype" field. */ + char *stddt; /* Standardized name for a datatype */ + int i,j; /* Loop counters */ + int hash; /* For hashing the name of a type */ + char *name; /* Name of the parser */ + + /* Allocate and initialize types[] and allocate stddt[] */ + arraysize = lemp->nsymbol * 2; + types = (char**)malloc( arraysize * sizeof(char*) ); + for(i=0; insymbol; i++){ + int len; + struct symbol *sp = lemp->symbols[i]; + if( sp->datatype==0 ) continue; + len = strlen(sp->datatype); + if( len>maxdtlength ) maxdtlength = len; + } + stddt = (char*)malloc( maxdtlength*2 + 1 ); + if( types==0 || stddt==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + + /* Build a hash table of datatypes. The ".dtnum" field of each symbol + ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is + ** used for terminal symbols and for nonterminals which don't specify + ** a datatype using the %type directive. */ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + char *cp; + if( sp==lemp->errsym ){ + sp->dtnum = arraysize+1; + continue; + } + if( sp->type!=NONTERMINAL || sp->datatype==0 ){ + sp->dtnum = 0; + continue; + } + cp = sp->datatype; + j = 0; + while( isspace(*cp) ) cp++; + while( *cp ) stddt[j++] = *cp++; + while( j>0 && isspace(stddt[j-1]) ) j--; + stddt[j] = 0; + hash = 0; + for(j=0; stddt[j]; j++){ + hash = hash*53 + stddt[j]; + } + if( hash<0 ) hash = -hash; + hash = hash%arraysize; + while( types[hash] ){ + if( strcmp(types[hash],stddt)==0 ){ + sp->dtnum = hash + 1; + break; + } + hash++; + if( hash>=arraysize ) hash = 0; + } + if( types[hash]==0 ){ + sp->dtnum = hash + 1; + types[hash] = (char*)malloc( strlen(stddt)+1 ); + if( types[hash]==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + strcpy(types[hash],stddt); + } + } + + /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ + name = lemp->name ? lemp->name : "Parse"; + lineno = *plineno; + if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; } + fprintf(out,"#define %sTOKENTYPE %s\n",name, + lemp->tokentype?lemp->tokentype:"void*"); lineno++; + if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } + fprintf(out,"typedef union {\n"); lineno++; + fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; + for(i=0; ierrsym->dtnum); lineno++; + free(stddt); + free(types); + fprintf(out,"} YYMINORTYPE;\n"); lineno++; + *plineno = lineno; +} + +/* Generate C source code for the parser */ +void ReportTable(lemp, mhflag) +struct lemon *lemp; +int mhflag; /* Output in makeheaders format if true */ +{ + FILE *out, *in; + char line[LINESIZE]; + int lineno; + struct state *stp; + struct action *ap; + struct rule *rp; + int i; + int tablecnt; + char *name; + + in = tplt_open(lemp); + if( in==0 ) return; + out = file_open(lemp,".c","w"); + if( out==0 ){ + fclose(in); + return; + } + lineno = 1; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the include code, if any */ + tplt_print(out,lemp,lemp->include,lemp->includeln,&lineno); + if( mhflag ){ + char *name = file_makename(lemp, ".h"); + fprintf(out,"#include \"%s\"\n", name); lineno++; + free(name); + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate #defines for all tokens */ + if( mhflag ){ + char *prefix; + fprintf(out,"#if INTERFACE\n"); lineno++; + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + lineno++; + } + fprintf(out,"#endif\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the defines */ + fprintf(out,"/* \001 */\n"); + fprintf(out,"#define YYCODETYPE %s\n", + lemp->nsymbol>250?"int":"unsigned char"); lineno++; + fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++; + fprintf(out,"#define YYACTIONTYPE %s\n", + lemp->nstate+lemp->nrule>250?"int":"unsigned char"); lineno++; + print_stack_union(out,lemp,&lineno,mhflag); + if( lemp->stacksize ){ + if( atoi(lemp->stacksize)<=0 ){ + ErrorMsg(lemp->filename,0, +"Illegal stack size: [%s]. The stack size should be an integer constant.", + lemp->stacksize); + lemp->errorcnt++; + lemp->stacksize = "100"; + } + fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++; + }else{ + fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++; + } + if( mhflag ){ + fprintf(out,"#if INTERFACE\n"); lineno++; + } + name = lemp->name ? lemp->name : "Parse"; + if( lemp->arg && lemp->arg[0] ){ + int i; + i = strlen(lemp->arg); + while( i>=1 && isalnum(lemp->arg[i-1]) ) i--; + fprintf(out,"#define %sARGDECL ,%s\n",name,&lemp->arg[i]); lineno++; + fprintf(out,"#define %sXARGDECL %s;\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sANSIARGDECL ,%s\n",name,lemp->arg); lineno++; + }else{ + fprintf(out,"#define %sARGDECL\n",name); lineno++; + fprintf(out,"#define %sXARGDECL\n",name); lineno++; + fprintf(out,"#define %sANSIARGDECL\n",name); lineno++; + } + if( mhflag ){ + fprintf(out,"#endif\n"); lineno++; + } + fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++; + fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++; + fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++; + fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the action table. + ** + ** Each entry in the action table is an element of the following + ** structure: + ** struct yyActionEntry { + ** YYCODETYPE lookahead; + ** YYACTIONTYPE action; + ** struct yyActionEntry *next; + ** } + ** + ** The entries are grouped into hash tables, one hash table for each + ** parser state. The hash table has a size which is the smallest + ** power of two needed to hold all entries. + */ + tablecnt = 0; + + /* Loop over parser states */ + for(i=0; instate; i++){ + int tablesize; /* size of the hash table */ + int j,k; /* Loop counter */ + int collide[2048]; /* The collision chain for the table */ + struct action *table[2048]; /* Build the hash table here */ + + /* Find the number of actions and initialize the hash table */ + stp = lemp->sorted[i]; + stp->tabstart = tablecnt; + stp->naction = 0; + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->sp->index!=lemp->nsymbol && compute_action(lemp,ap)>=0 ){ + stp->naction++; + } + } + tablesize = 1; + while( tablesizenaction ) tablesize += tablesize; + assert( tablesize<= sizeof(table)/sizeof(table[0]) ); + for(j=0; jtabdfltact = lemp->nstate + lemp->nrule; + for(ap=stp->ap; ap; ap=ap->next){ + int action = compute_action(lemp,ap); + int h; + if( ap->sp->index==lemp->nsymbol ){ + stp->tabdfltact = action; + }else if( action>=0 ){ + h = ap->sp->index & (tablesize-1); + ap->collide = table[h]; + table[h] = ap; + } + } + + /* Resolve collisions */ + for(j=k=0; jcollide ){ + while( table[k] ) k++; + table[k] = table[j]->collide; + collide[j] = k; + table[j]->collide = 0; + if( kindex); lineno++; + for(j=0; jsp->index, + compute_action(lemp,table[j])); + if( collide[j]>=0 ){ + fprintf(out,"&yyActionTable[%4d] }, /* ", + collide[j] + tablecnt); + }else{ + fprintf(out,"0 }, /* "); + } + PrintAction(table[j],out,22); + fprintf(out," */\n"); + } + lineno++; + } + + /* Update the table count */ + tablecnt += tablesize; + } + tplt_xfer(lemp->name,in,out,&lineno); + lemp->tablesize = tablecnt; + + /* Generate the state table + ** + ** Each entry is an element of the following structure: + ** struct yyStateEntry { + ** struct yyActionEntry *hashtbl; + ** int mask; + ** YYACTIONTYPE actionDefault; + ** } + */ + for(i=0; instate; i++){ + int tablesize; + stp = lemp->sorted[i]; + tablesize = 1; + while( tablesizenaction ) tablesize += tablesize; + fprintf(out," { &yyActionTable[%d], %d, %d},\n", + stp->tabstart, + tablesize - 1, + stp->tabdfltact); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate a table containing the symbolic name of every symbol */ + for(i=0; insymbol; i++){ + sprintf(line,"\"%s\",",lemp->symbols[i]->name); + fprintf(out," %-15s",line); + if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; } + } + if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes every time a symbol is popped from + ** the stack while processing errors or while destroying the parser. + ** (In other words, generate the %destructor actions) */ + if( lemp->tokendest ){ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type!=TERMINAL ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + } + for(i=0; insymbol && lemp->symbols[i]->type!=TERMINAL; i++); + if( insymbol ){ + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes whenever the parser stack overflows */ + tplt_print(out,lemp,lemp->overflow,lemp->overflowln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of rule information + ** + ** Note: This code depends on the fact that rules are number + ** sequentually beginning with 0. + */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which execution during each REDUCE action */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," case %d:\n",rp->index); lineno++; + fprintf(out," YYTRACE(\"%s ::=",rp->lhs->name); + for(i=0; inrhs; i++) fprintf(out," %s",rp->rhs[i]->name); + fprintf(out,"\")\n"); lineno++; + emit_code(out,rp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes if a parse fails */ + tplt_print(out,lemp,lemp->failure,lemp->failureln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when a syntax error occurs */ + tplt_print(out,lemp,lemp->error,lemp->errorln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when the parser accepts its input */ + tplt_print(out,lemp,lemp->accept,lemp->acceptln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Append any addition code the user desires */ + tplt_print(out,lemp,lemp->extracode,lemp->extracodeln,&lineno); + + fclose(in); + fclose(out); + return; +} + +/* Generate a header file for the parser */ +void ReportHeader(lemp) +struct lemon *lemp; +{ + FILE *out, *in; + char *prefix; + char line[LINESIZE]; + char pattern[LINESIZE]; + int i; + + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + in = file_open(lemp,".h","r"); + if( in ){ + for(i=1; interminal && fgets(line,LINESIZE,in); i++){ + sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + if( strcmp(line,pattern) ) break; + } + fclose(in); + if( i==lemp->nterminal ){ + /* No change in the file. Don't rewrite it. */ + return; + } + } + out = file_open(lemp,".h","w"); + if( out ){ + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + } + fclose(out); + } + return; +} + +/* Reduce the size of the action tables, if possible, by making use +** of defaults. +** +** In this version, if all REDUCE actions use the same rule, make +** them the default. Only default them if there are more than one. +*/ +void CompressTables(lemp) +struct lemon *lemp; +{ + struct state *stp; + struct action *ap; + struct rule *rp; + int i; + int cnt; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + + /* Find the first REDUCE action */ + for(ap=stp->ap; ap && ap->type!=REDUCE; ap=ap->next); + if( ap==0 ) continue; + + /* Remember the rule used */ + rp = ap->x.rp; + + /* See if all other REDUCE acitons use the same rule */ + cnt = 1; + for(ap=ap->next; ap; ap=ap->next){ + if( ap->type==REDUCE ){ + if( ap->x.rp!=rp ) break; + cnt++; + } + } + if( ap || cnt==1 ) continue; + + /* Combine all REDUCE actions into a single default */ + for(ap=stp->ap; ap && ap->type!=REDUCE; ap=ap->next); + assert( ap ); + ap->sp = Symbol_new("{default}"); + for(ap=ap->next; ap; ap=ap->next){ + if( ap->type==REDUCE ) ap->type = NOT_USED; + } + stp->ap = Action_sort(stp->ap); + } +} +/***************** From the file "set.c" ************************************/ +/* +** Set manipulation routines for the LEMON parser generator. +*/ + +static int size = 0; + +/* Set the set size */ +void SetSize(n) +int n; +{ + size = n+1; +} + +/* Allocate a new set */ +char *SetNew(){ + char *s; + int i; + s = (char*)malloc( size ); + if( s==0 ){ + extern void memory_error(); + memory_error(); + } + for(i=0; isize = 1024; + x1a->count = 0; + x1a->tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*1024 ); + if( x1a->tbl==0 ){ + free(x1a); + x1a = 0; + }else{ + int i; + x1a->ht = (x1node**)&(x1a->tbl[1024]); + for(i=0; i<1024; i++) x1a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Strsafe_insert(data) +char *data; +{ + x1node *np; + int h; + int ph; + + if( x1a==0 ) return 0; + ph = strhash(data); + h = ph & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x1a->count>=x1a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x1 array; + array.size = size = x1a->size*2; + array.count = x1a->count; + array.tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x1node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x1node *oldnp, *newnp; + oldnp = &(x1a->tbl[i]); + h = strhash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x1a->tbl); + *x1a = array; + } + /* Insert the new data */ + h = ph & (x1a->size-1); + np = &(x1a->tbl[x1a->count++]); + np->data = data; + if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next); + np->next = x1a->ht[h]; + x1a->ht[h] = np; + np->from = &(x1a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +char *Strsafe_find(key) +char *key; +{ + int h; + x1node *np; + + if( x1a==0 ) return 0; + h = strhash(key) & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return a pointer to the (terminal or nonterminal) symbol "x". +** Create a new symbol if this is the first time "x" has been seen. +*/ +struct symbol *Symbol_new(x) +char *x; +{ + struct symbol *sp; + + sp = Symbol_find(x); + if( sp==0 ){ + sp = (struct symbol *)malloc( sizeof(struct symbol) ); + MemoryCheck(sp); + sp->name = Strsafe(x); + sp->type = isupper(*x) ? TERMINAL : NONTERMINAL; + sp->rule = 0; + sp->prec = -1; + sp->assoc = UNK; + sp->firstset = 0; + sp->lambda = FALSE; + sp->destructor = 0; + sp->datatype = 0; + Symbol_insert(sp,sp->name); + } + return sp; +} + +/* Compare two symbols */ +int Symbolcmpp(a,b) +struct symbol **a; +struct symbol **b; +{ + return strcmp((**a).name,(**b).name); +} + +/* There is one instance of the following structure for each +** associative array of type "x2". +*/ +struct s_x2 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x2node *tbl; /* The data stored here */ + struct s_x2node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x2". +*/ +typedef struct s_x2node { + struct symbol *data; /* The data */ + char *key; /* The key */ + struct s_x2node *next; /* Next entry with the same hash */ + struct s_x2node **from; /* Previous link */ +} x2node; + +/* There is only one instance of the array, which is the following */ +static struct s_x2 *x2a; + +/* Allocate a new associative array */ +void Symbol_init(){ + if( x2a ) return; + x2a = (struct s_x2*)malloc( sizeof(struct s_x2) ); + if( x2a ){ + x2a->size = 128; + x2a->count = 0; + x2a->tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*128 ); + if( x2a->tbl==0 ){ + free(x2a); + x2a = 0; + }else{ + int i; + x2a->ht = (x2node**)&(x2a->tbl[128]); + for(i=0; i<128; i++) x2a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Symbol_insert(data,key) +struct symbol *data; +char *key; +{ + x2node *np; + int h; + int ph; + + if( x2a==0 ) return 0; + ph = strhash(key); + h = ph & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x2a->count>=x2a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x2 array; + array.size = size = x2a->size*2; + array.count = x2a->count; + array.tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x2node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x2node *oldnp, *newnp; + oldnp = &(x2a->tbl[i]); + h = strhash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x2a->tbl); + *x2a = array; + } + /* Insert the new data */ + h = ph & (x2a->size-1); + np = &(x2a->tbl[x2a->count++]); + np->key = key; + np->data = data; + if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next); + np->next = x2a->ht[h]; + x2a->ht[h] = np; + np->from = &(x2a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct symbol *Symbol_find(key) +char *key; +{ + int h; + x2node *np; + + if( x2a==0 ) return 0; + h = strhash(key) & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the n-th data. Return NULL if n is out of range. */ +struct symbol *Symbol_Nth(n) +int n; +{ + struct symbol *data; + if( x2a && n>0 && n<=x2a->count ){ + data = x2a->tbl[n-1].data; + }else{ + data = 0; + } + return data; +} + +/* Return the size of the array */ +int Symbol_count() +{ + return x2a ? x2a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct symbol **Symbol_arrayof() +{ + struct symbol **array; + int i,size; + if( x2a==0 ) return 0; + size = x2a->count; + array = (struct symbol **)malloc( sizeof(struct symbol *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Compare two configurations */ +int Configcmp(a,b) +struct config *a; +struct config *b; +{ + int x; + x = a->rp->index - b->rp->index; + if( x==0 ) x = a->dot - b->dot; + return x; +} + +/* Compare two states */ +PRIVATE int statecmp(a,b) +struct config *a; +struct config *b; +{ + int rc; + for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){ + rc = a->rp->index - b->rp->index; + if( rc==0 ) rc = a->dot - b->dot; + } + if( rc==0 ){ + if( a ) rc = 1; + if( b ) rc = -1; + } + return rc; +} + +/* Hash a state */ +PRIVATE int statehash(a) +struct config *a; +{ + int h=0; + while( a ){ + h = h*571 + a->rp->index*37 + a->dot; + a = a->bp; + } + return h; +} + +/* Allocate a new state structure */ +struct state *State_new() +{ + struct state *new; + new = (struct state *)malloc( sizeof(struct state) ); + MemoryCheck(new); + return new; +} + +/* There is one instance of the following structure for each +** associative array of type "x3". +*/ +struct s_x3 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x3node *tbl; /* The data stored here */ + struct s_x3node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x3". +*/ +typedef struct s_x3node { + struct state *data; /* The data */ + struct config *key; /* The key */ + struct s_x3node *next; /* Next entry with the same hash */ + struct s_x3node **from; /* Previous link */ +} x3node; + +/* There is only one instance of the array, which is the following */ +static struct s_x3 *x3a; + +/* Allocate a new associative array */ +void State_init(){ + if( x3a ) return; + x3a = (struct s_x3*)malloc( sizeof(struct s_x3) ); + if( x3a ){ + x3a->size = 128; + x3a->count = 0; + x3a->tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*128 ); + if( x3a->tbl==0 ){ + free(x3a); + x3a = 0; + }else{ + int i; + x3a->ht = (x3node**)&(x3a->tbl[128]); + for(i=0; i<128; i++) x3a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int State_insert(data,key) +struct state *data; +struct config *key; +{ + x3node *np; + int h; + int ph; + + if( x3a==0 ) return 0; + ph = statehash(key); + h = ph & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x3a->count>=x3a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x3 array; + array.size = size = x3a->size*2; + array.count = x3a->count; + array.tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x3node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x3node *oldnp, *newnp; + oldnp = &(x3a->tbl[i]); + h = statehash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x3a->tbl); + *x3a = array; + } + /* Insert the new data */ + h = ph & (x3a->size-1); + np = &(x3a->tbl[x3a->count++]); + np->key = key; + np->data = data; + if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next); + np->next = x3a->ht[h]; + x3a->ht[h] = np; + np->from = &(x3a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct state *State_find(key) +struct config *key; +{ + int h; + x3node *np; + + if( x3a==0 ) return 0; + h = statehash(key) & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct state **State_arrayof() +{ + struct state **array; + int i,size; + if( x3a==0 ) return 0; + size = x3a->count; + array = (struct state **)malloc( sizeof(struct state *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Hash a configuration */ +PRIVATE int confighash(a) +struct config *a; +{ + int h=0; + h = h*571 + a->rp->index*37 + a->dot; + return h; +} + +/* There is one instance of the following structure for each +** associative array of type "x4". +*/ +struct s_x4 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x4node *tbl; /* The data stored here */ + struct s_x4node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x4". +*/ +typedef struct s_x4node { + struct config *data; /* The data */ + struct s_x4node *next; /* Next entry with the same hash */ + struct s_x4node **from; /* Previous link */ +} x4node; + +/* There is only one instance of the array, which is the following */ +static struct s_x4 *x4a; + +/* Allocate a new associative array */ +void Configtable_init(){ + if( x4a ) return; + x4a = (struct s_x4*)malloc( sizeof(struct s_x4) ); + if( x4a ){ + x4a->size = 64; + x4a->count = 0; + x4a->tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*64 ); + if( x4a->tbl==0 ){ + free(x4a); + x4a = 0; + }else{ + int i; + x4a->ht = (x4node**)&(x4a->tbl[64]); + for(i=0; i<64; i++) x4a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Configtable_insert(data) +struct config *data; +{ + x4node *np; + int h; + int ph; + + if( x4a==0 ) return 0; + ph = confighash(data); + h = ph & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x4a->count>=x4a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x4 array; + array.size = size = x4a->size*2; + array.count = x4a->count; + array.tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x4node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x4node *oldnp, *newnp; + oldnp = &(x4a->tbl[i]); + h = confighash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x4a->tbl); + *x4a = array; + } + /* Insert the new data */ + h = ph & (x4a->size-1); + np = &(x4a->tbl[x4a->count++]); + np->data = data; + if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); + np->next = x4a->ht[h]; + x4a->ht[h] = np; + np->from = &(x4a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct config *Configtable_find(key) +struct config *key; +{ + int h; + x4node *np; + + if( x4a==0 ) return 0; + h = confighash(key) & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Remove all data from the table. Pass each data to the function "f" +** as it is removed. ("f" may be null to avoid this step.) */ +void Configtable_clear(f) +int(*f)(/* struct config * */); +{ + int i; + if( x4a==0 || x4a->count==0 ) return; + if( f ) for(i=0; icount; i++) (*f)(x4a->tbl[i].data); + for(i=0; isize; i++) x4a->ht[i] = 0; + x4a->count = 0; + return; +} diff --git a/tool/lempar.c b/tool/lempar.c new file mode 100644 index 0000000000..e5293b1de2 --- /dev/null +++ b/tool/lempar.c @@ -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 +%% +/* 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 +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: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** 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: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +/* 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(""); + ** #line + ** { ... } // User supplied code + ** #line + ** 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: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** 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( yyacterrcnt--; + 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; +} diff --git a/tool/opNames.awk b/tool/opNames.awk new file mode 100644 index 0000000000..1150c5aaee --- /dev/null +++ b/tool/opNames.awk @@ -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" +} diff --git a/tool/opcodeDoc.awk b/tool/opcodeDoc.awk new file mode 100644 index 0000000000..492010624f --- /dev/null +++ b/tool/opcodeDoc.awk @@ -0,0 +1,23 @@ +# +# Extract opcode documentation for sqliteVdbe.c and generate HTML +# +BEGIN { + print "" + print "

SQLite Virtual Database Engine Opcodes

" + print "" +} +/ Opcode: /,/\*\// { + if( $2=="Opcode:" ){ + printf "\n\n" + }else if( NF>1 ){ + sub(/^ *\*\* /,"") + gsub(/" +} diff --git a/tool/renumberOps.awk b/tool/renumberOps.awk new file mode 100644 index 0000000000..098c4d86d7 --- /dev/null +++ b/tool/renumberOps.awk @@ -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 }
%s %s %s %s\n", $3, $4, $5, $6 + }else if( $1=="*/" ){ + printf "