Initial commit

Import existing source tree; original VCS history is no longer available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ville Lindholm
2026-05-28 16:35:31 +03:00
co-authored by Claude
commit dbc223eb84
308 changed files with 81555 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
noinst_LIBRARIES = libeel.a
INCLUDES = @LOCALCFLAGS@
libeel_a_SOURCES = eel.c e_script.c e_symtab.c e_path.c e_util.c \
e_lexer.c e_register.c e_builtin.c e_getargs.c
EXTRA_DIST = *.h *.txt
+434
View File
@@ -0,0 +1,434 @@
# Makefile.in generated by automake 1.9.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004, 2005 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = ..
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
INSTALL = @INSTALL@
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
target_triplet = @target@
subdir = eel
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.in
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = $(top_builddir)/aconfig.h
CONFIG_CLEAN_FILES =
LIBRARIES = $(noinst_LIBRARIES)
AR = ar
ARFLAGS = cru
libeel_a_AR = $(AR) $(ARFLAGS)
libeel_a_LIBADD =
am_libeel_a_OBJECTS = eel.$(OBJEXT) e_script.$(OBJEXT) \
e_symtab.$(OBJEXT) e_path.$(OBJEXT) e_util.$(OBJEXT) \
e_lexer.$(OBJEXT) e_register.$(OBJEXT) e_builtin.$(OBJEXT) \
e_getargs.$(OBJEXT)
libeel_a_OBJECTS = $(am_libeel_a_OBJECTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(libeel_a_SOURCES)
DIST_SOURCES = $(libeel_a_SOURCES)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMDEP_FALSE = @AMDEP_FALSE@
AMDEP_TRUE = @AMDEP_TRUE@
AMTAR = @AMTAR@
AUDIO_LIBS = @AUDIO_LIBS@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
BUILD_MACOSX_BUNDLE_FALSE = @BUILD_MACOSX_BUNDLE_FALSE@
BUILD_MACOSX_BUNDLE_TRUE = @BUILD_MACOSX_BUNDLE_TRUE@
BUILD_SIMPLE_BUNDLE_FALSE = @BUILD_SIMPLE_BUNDLE_FALSE@
BUILD_SIMPLE_BUNDLE_TRUE = @BUILD_SIMPLE_BUNDLE_TRUE@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CONFIGDIR = @CONFIGDIR@
CONFIGFILE = @CONFIGFILE@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
DATADIR = @DATADIR@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
EXEFILE = @EXEFILE@
GREP = @GREP@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LOCALCFLAGS = @LOCALCFLAGS@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MATHLIB = @MATHLIB@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
POW_LIB = @POW_LIB@
RANLIB = @RANLIB@
SCOREDIR = @SCOREDIR@
SDL_CFLAGS = @SDL_CFLAGS@
SDL_CONFIG = @SDL_CONFIG@
SDL_LIBS = @SDL_LIBS@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
UNIX_SCOREDIR_FALSE = @UNIX_SCOREDIR_FALSE@
UNIX_SCOREDIR_TRUE = @UNIX_SCOREDIR_TRUE@
VERSION = @VERSION@
XMKMF = @XMKMF@
X_CFLAGS = @X_CFLAGS@
X_EXTRA_LIBS = @X_EXTRA_LIBS@
X_LIBS = @X_LIBS@
X_PRE_LIBS = @X_PRE_LIBS@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
sysconfdir = @sysconfdir@
target = @target@
target_alias = @target_alias@
target_cpu = @target_cpu@
target_os = @target_os@
target_vendor = @target_vendor@
noinst_LIBRARIES = libeel.a
INCLUDES = @LOCALCFLAGS@
libeel_a_SOURCES = eel.c e_script.c e_symtab.c e_path.c e_util.c \
e_lexer.c e_register.c e_builtin.c e_getargs.c
EXTRA_DIST = *.h *.txt
all: all-am
.SUFFIXES:
.SUFFIXES: .c .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
&& exit 0; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign eel/Makefile'; \
cd $(top_srcdir) && \
$(AUTOMAKE) --foreign eel/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
clean-noinstLIBRARIES:
-test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
libeel.a: $(libeel_a_OBJECTS) $(libeel_a_DEPENDENCIES)
-rm -f libeel.a
$(libeel_a_AR) libeel.a $(libeel_a_OBJECTS) $(libeel_a_LIBADD)
$(RANLIB) libeel.a
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/e_builtin.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/e_getargs.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/e_lexer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/e_path.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/e_register.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/e_script.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/e_symtab.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/e_util.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eel.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
uninstall-info-am:
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
mkid -fID $$unique
tags: TAGS
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique; \
fi
ctags: CTAGS
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(CTAGS_ARGS)$$tags$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$tags $$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& cd $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) $$here
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
case $$file in \
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
esac; \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
dir="/$$dir"; \
$(mkdir_p) "$(distdir)$$dir"; \
else \
dir=''; \
fi; \
if test -d $$d/$$file; then \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
fi; \
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
else \
test -f $(distdir)/$$file \
|| cp -p $$d/$$file $(distdir)/$$file \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(LIBRARIES)
installdirs:
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
`test -z '$(STRIP)' || \
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
info: info-am
info-am:
install-data-am:
install-exec-am:
install-info: install-info-am
install-man:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-info-am
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
clean-noinstLIBRARIES ctags distclean distclean-compile \
distclean-generic distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-exec install-exec-am install-info \
install-info-am install-man install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
uninstall-am uninstall-info-am
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+40
View File
@@ -0,0 +1,40 @@
/*(LGPL)
---------------------------------------------------------------------------
_e_script.h - EEL source code management (Private)
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _EEL__SCRIPT_H_
#define _EEL__SCRIPT_H_
#include "eel.h"
#include "e_script.h"
#define MAX_SCRIPTS 256
typedef struct script_t
{
char *name;
unsigned char *data;
unsigned len;
} script_t;
extern script_t eel_scripttab[MAX_SCRIPTS];
#endif /*_EEL__SCRIPT_H_*/
+462
View File
@@ -0,0 +1,462 @@
/*(LGPL)
---------------------------------------------------------------------------
e_builtin.c - EEL built-in commands and directives
---------------------------------------------------------------------------
* Copyright (C) 2002, 2003, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define DBG(x)
#include "kobolog.h"
#include "math.h"
#include "e_builtin.h"
#include "e_util.h"
#include "e_lexer.h"
/*----------------------------------------------------------
Command Callbacks
----------------------------------------------------------*/
int eel_cmd_end(int argc, struct eel_data_t *argv)
{
DBG(log_printf(DLOG, "end;\n");)
return 0;
}
int eel_cmd_set(int argc, struct eel_data_t *argv)
{
eel_symbol_t *to;
eel_data_t *val;
if(eel_get_args("nV(irsce)", &to, &val) != 2)
return -1;
DBG(log_printf(DLOG, "set %s, ", eel_s_stringrep(to));)
DBG(log_printf(DLOG, "%s;\n", eel_d_stringrep(val));)
eel_s_set(to, val);
return 1;
}
int eel_cmd_print(int argc, struct eel_data_t *argv)
{
int i;
for(i = 0; i < argc; ++i)
{
if(EDT_SYMREF == argv[i].type)
switch(argv[i].value.sym->type)
{
case EST_CONSTANT:
case EST_VARIABLE:
/* Print the *value* of these... */
log_printf(ULOG, "%s", eel_d_stringrep(
&argv[i].value.sym->data));
continue;
default:
break;
}
log_printf(ULOG, "%s", eel_d_stringrep(argv + i));
}
return 1;
}
/*----------------------------------------------------------
Operator Callbacks
----------------------------------------------------------*/
static inline eel_datatypes_t promote(struct eel_data_t *left,
struct eel_data_t *right)
{
if(left->type == right->type)
{
if(left->type == EDT_INTEGER || left->type == EDT_REAL)
return left->type;
else
{
eel_error("Operands of unsupported type!");
return -1;
}
}
if(left->type == EDT_INTEGER)
{
left->value.r = (double)left->value.i;
left->type = EDT_REAL;
}
else if(left->type != EDT_REAL)
{
eel_error("Left hand operand of unsupported type!");
return -1;
}
if(right->type == EDT_INTEGER)
{
right->value.r = (double)right->value.i;
right->type = EDT_REAL;
}
else if(right->type != EDT_REAL)
{
eel_error("Right hand operand of unsupported type!");
return -1;
}
return EDT_REAL;
}
int eel_op_add(int argc, struct eel_data_t *argv)
{
eel_data_t *left = argv, *right = argv + 1;
DBG(log_printf(DLOG, "add ");)
if(argc != 2)
{
eel_error("Operator needs exactly two arguments!");
return -1;
}
switch(promote(left, right))
{
case EDT_REAL:
DBG(log_printf(DLOG, "%f, %f\n", left->value.r, right->value.r);)
left->value.r += right->value.r;
break;
case EDT_INTEGER:
DBG(log_printf(DLOG, "%d, %d\n", left->value.i, right->value.i);)
left->value.i += right->value.i;
break;
default:
return -1;
}
return 0;
}
int eel_op_sub(int argc, struct eel_data_t *argv)
{
eel_data_t *left = argv, *right = argv + 1;
DBG(log_printf(DLOG, "sub ");)
if(argc != 2)
{
eel_error("Operator needs exactly two arguments!");
return -1;
}
switch(promote(left, right))
{
case EDT_REAL:
DBG(log_printf(DLOG, "%f, %f\n", left->value.r, right->value.r);)
left->value.r -= right->value.r;
break;
case EDT_INTEGER:
DBG(log_printf(DLOG, "%d, %d\n", left->value.i, right->value.i);)
left->value.i -= right->value.i;
break;
default:
return -1;
}
return 0;
}
int eel_op_mul(int argc, struct eel_data_t *argv)
{
eel_data_t *left = argv, *right = argv + 1;
DBG(log_printf(DLOG, "mul ");)
if(argc != 2)
{
eel_error("Operator needs exactly two arguments!");
return -1;
}
switch(promote(left, right))
{
case EDT_REAL:
DBG(log_printf(DLOG, "%f, %f\n", left->value.r, right->value.r);)
left->value.r *= right->value.r;
break;
case EDT_INTEGER:
DBG(log_printf(DLOG, "%d, %d\n", left->value.i, right->value.i);)
left->value.i *= right->value.i;
break;
default:
return -1;
}
return 0;
}
int eel_op_div(int argc, struct eel_data_t *argv)
{
eel_data_t *left = argv, *right = argv + 1;
DBG(log_printf(DLOG, "div ");)
if(argc != 2)
{
eel_error("Operator needs exactly two arguments!");
return -1;
}
switch(promote(left, right))
{
case EDT_REAL:
if(!right->value.r)
{
eel_error("Division by zero!");
return -1;
}
DBG(log_printf(DLOG, "%f, %f\n", left->value.r, right->value.r);)
left->value.r /= right->value.r;
break;
case EDT_INTEGER:
if(!right->value.i)
{
eel_error("Division by zero!");
return -1;
}
DBG(log_printf(DLOG, "%d, %d\n", left->value.i, right->value.i);)
left->value.i /= right->value.i;
break;
default:
return -1;
}
return 0;
}
int eel_op_mod(int argc, struct eel_data_t *argv)
{
eel_data_t *left = argv, *right = argv + 1;
DBG(log_printf(DLOG, "mod ");)
if(argc != 2)
{
eel_error("Operator needs exactly two arguments!");
return -1;
}
switch(promote(left, right))
{
case EDT_REAL:
if(!right->value.r)
{
eel_error("Zero modulus!");
return -1;
}
DBG(log_printf(DLOG, "%f, %f\n", left->value.r, right->value.r);)
left->value.r -= right->value.r *
floor(left->value.r / right->value.r);
break;
case EDT_INTEGER:
if(!right->value.i)
{
eel_error("Zero modulus!");
return -1;
}
DBG(log_printf(DLOG, "%d, %d\n", left->value.i, right->value.i);)
left->value.i %= right->value.i;
break;
default:
return -1;
}
return 0;
}
int eel_op_pow(int argc, struct eel_data_t *argv)
{
eel_data_t *left = argv, *right = argv + 1;
DBG(log_printf(DLOG, "pow ");)
if(argc != 2)
{
eel_error("Operator needs exactly two arguments!");
return -1;
}
switch(promote(left, right))
{
case EDT_REAL:
DBG(log_printf(DLOG, "%f, %f\n", left->value.r, right->value.r);)
left->value.r = pow(left->value.r, right->value.r);
break;
case EDT_INTEGER:
DBG(log_printf(DLOG, "%d, %d\n", left->value.i, right->value.i);)
left->value.i = pow(left->value.i, right->value.i);
break;
default:
return -1;
}
return 0;
}
/*----------------------------------------------------------
Directive Callbacks
----------------------------------------------------------*/
int eel_dir_include(void)
{
int h, res;
const char *fn;
if(eel_parse_args(" ", '\n') < 0)
return -1;
if(eel_get_args("s", &fn) < 0)
return -1;
h = eel_load(fn);
if(h < 0)
{
eel_error("Couldn't include file \"%s\"!", fn);
return -1;
}
res = eel_run(h);
eel_free(h);
if(res < 0)
return res;
else
return 1;
}
int eel_dir_define(void)
{
eel_symbol_t *to;
eel_data_t *val;
int res;
if(eel_parse_args(" ", '\n') < 0)
return -1;
res = eel_get_args("n[V(irsce)]", &to, &val);
if(res < 0)
return -1;
switch(res)
{
case 1:
DBG(log_printf(DLOG, "#define\t%s\n", eel_s_stringrep(to));)
to->data.type = EDT_ILLEGAL;
to->data.value.i = 0;
to->type = EST_CONSTANT;
break;
case 2:
DBG(log_printf(DLOG, "#define\t%s\t%s\n",
eel_s_stringrep(to), eel_d_stringrep(val));)
eel_s_set(to, val);
to->type = EST_CONSTANT;
break;
default:
eel_error("Expected either one or two arguments!");
return -1;
}
return res;
}
/*----------------------------------------------------------
Special Callbacks
----------------------------------------------------------*/
static int _proc_func(void)
{
const char *funcname;
eel_symbol_t *s;
int tk, res;
/* Get function name */
tk = eel_lex(0);
if(tk != TK_NEWSYM)
{
eel_error("Function/procedure name must be unique!");
return -1;
}
funcname = eel_current.lval->value.s;
/* Create function symbol */
s = eel_s_new(funcname, EST_FUNCTION);
if(!s)
{
eel_error("INTERNAL ERROR: Failed to create "
"function/procedure symbol!");
return -1;
}
/* Check argument list */
if(eel_lex(0) != '(')
{
eel_error("Expected '('!");
return -1;
}
/* Store the start of the *argument list*... :-) */
s->data.value.a = bio_tell(eel_current.bio);
s->token = eel_current.script;
eel_push_scope();
res = eel_parse_args(",", ')');
eel_pop_scope();
if(res < 0)
return -1;
DBG(log_printf(DLOG, "Function/procedure '%s' defined; starting at %d.\n",
s->name, s->data.value.a);)
/* Find start of body ("the code") */
if(eel_lex(0) != '{')
{
eel_error("Expected '{'!");
return -1;
}
return 1;
}
int eel_spec_function(void)
{
int c;
if(_proc_func() < 0)
return -1;
/* QUICK HACK: Skip function body */
while((c = bio_getchar(eel_current.bio)) != '}')
if(EOF == c)
{
eel_error("EOF inside function body!");
return -1;
}
return 1;
}
int eel_spec_procedure(void)
{
int c;
if(_proc_func() < 0)
return -1;
/* QUICK HACK: Skip procedure body */
while((c = bio_getchar(eel_current.bio)) != '}')
if(EOF == c)
{
eel_error("EOF inside procedure body!");
return -1;
}
return 1;
}
+59
View File
@@ -0,0 +1,59 @@
/*(LGPL)
---------------------------------------------------------------------------
e_builtin.c - EEL built-in commands and directives
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _EEL_BUILTIN_H_
#define _EEL_BUILTIN_H_
#include "eel.h"
/*----------------------------------------------------------
Command Callbacks
----------------------------------------------------------*/
int eel_cmd_end(int argc, struct eel_data_t *argv);
int eel_cmd_set(int argc, struct eel_data_t *argv);
int eel_cmd_print(int argc, struct eel_data_t *argv);
/*----------------------------------------------------------
Operator Callbacks
----------------------------------------------------------*/
int eel_op_add(int argc, struct eel_data_t *argv);
int eel_op_sub(int argc, struct eel_data_t *argv);
int eel_op_mul(int argc, struct eel_data_t *argv);
int eel_op_div(int argc, struct eel_data_t *argv);
int eel_op_mod(int argc, struct eel_data_t *argv);
int eel_op_pow(int argc, struct eel_data_t *argv);
/*----------------------------------------------------------
Directive Callbacks
----------------------------------------------------------*/
int eel_dir_include(void);
int eel_dir_define(void);
/*----------------------------------------------------------
Special Callbacks
----------------------------------------------------------*/
int eel_spec_function(void);
int eel_spec_procedure(void);
#endif /*_EEL_BUILTIN_H_*/
+1023
View File
File diff suppressed because it is too large Load Diff
+419
View File
@@ -0,0 +1,419 @@
/*(LGPL)
---------------------------------------------------------------------------
e_lexer.c - EEL engine lexer
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "e_lexer.h"
#include "e_util.h"
int eel_get_unique_token()
{
static int last = TK_USER;
return last++;
}
static char *lexbuf = NULL;
static int lexbuf_length = 0;
static int lexbuf_realloc(int len)
{
char *nsb;
lexbuf_length = len * 5 / 4;
nsb = (char *)realloc(lexbuf, lexbuf_length + 1);
if(!nsb)
{
eel_error("EEL lexer out of memory!");
return -1;
}
lexbuf = nsb;
return 0;
}
static inline int lexbuf_bump(int len)
{
if(len >= lexbuf_length)
return lexbuf_realloc(len);
return 0;
}
void eel_lexer_cleanup(void)
{
if(eel_current.lval)
{
eel_d_freestring(eel_current.lval);
free(eel_current.lval);
eel_current.lval = NULL;
}
free(lexbuf);
lexbuf = NULL;
lexbuf_length = 0;
}
static int skipwhite(int report_eoln)
{
int c;
/* Ignore whitespace and get first nonwhite character. */
while((c = bio_getchar(eel_current.bio)) == ' ' || c == '\t' ||
c == '\r' || c == '\n')
if((c == '\n') && report_eoln)
return c;
return c;
}
static eel_data_t *newdata(eel_datatypes_t type)
{
eel_data_t *d = (eel_data_t *)calloc(1, sizeof(eel_data_t));
if(!d)
return NULL;
d->type = type;
return d;
}
static int get_figure(bio_file_t *h, int base)
{
int n = bio_getchar(h);
if(n >= '0' && n <= '9')
n -= '0';
else if(n >= 'a' && n <= 'z')
n -= 'a';
else if(n >= 'A' && n <= 'Z')
n -= 'A';
else
return -1;
if(n >= base)
return -1;
return n;
}
static int get_num(bio_file_t *h, int base, int figures)
{
int value = 0;
while(figures--)
{
int n = get_figure(h, base);
if(n < 0)
return n;
value *= base;
value += n;
}
return value;
}
/*
* Parse quoted string.
* C printf "backslash codes" are supported,
* as well as "\dXXX", for decimal codes.
*/
static int parse_string(bio_file_t *h)
{
int c, len = 0;
while((c = bio_getchar(h)) != '"')
{
switch(c)
{
case EOF:
eel_error("EOF inside string literal!");
return TK_ERROR;
case '\\':
c = bio_getchar(h);
switch(c)
{
case EOF:
eel_error("EOF inside string escape sequence!");
return -1;
case '0':
c = get_num(h, 8, 3);
if(c < 0)
{
eel_error("Illegal octal figure!");
return TK_ERROR;
}
break;
case 'a':
c = '\a';
break;
case 'b':
c = '\b';
break;
case 'c':
c = '\0';
break;
case 'd':
c = get_num(h, 10, 2);
if(c < 0)
{
eel_error("Illegal decimal figure!");
return TK_ERROR;
}
break;
case 'f':
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'v':
c = '\v';
break;
case 'x':
c = get_num(h, 16, 2);
if(c < 0)
{
eel_error("Illegal hex figure!");
return TK_ERROR;
}
break;
default:
break;
}
break;
default:
break;
}
lexbuf_bump(len + 1);
lexbuf[len++] = c;
}
lexbuf[len] = 0;
eel_current.lval = newdata(EDT_STRING);
if(!eel_current.lval)
return TK_ERROR;
eel_d_setstring(eel_current.lval, lexbuf);
return TK_STRN;
}
static inline int token(int tk)
{
eel_current.token = tk;
return tk;
}
/*
* Pass 1 for 'report_eoln' to get '\n' back whenever a
* newline occurs in the source.
*
* Note that newlines inside comments are *always* treated
* as white space!
*/
int eel_lex(int report_eoln)
{
int c, prevc;
int negative = 0, directive = 0;
bio_file_t *h = eel_current.bio;
eel_symbol_t *s;
/* Handle eel_unlex() pushbacks */
if(eel_current.unlexed)
{
eel_current.unlexed = 0;
return eel_current.token;
}
/* In case someone should ignore an lval... */
if(eel_current.lval)
{
eel_d_freestring(eel_current.lval);
free(eel_current.lval);
eel_current.lval = NULL;
}
while(1)
{
c = skipwhite(report_eoln);
if(c == EOF)
return token(0);
/* /X comment tokens */
if(c == '/')
{
switch ((c = bio_getchar(h)))
{
case '/': /* C++ style single line comment */
while(EOF != (c = bio_getchar(h)))
if('\n' == c)
break;
continue;
case '*': /* C style comment */
prevc = 0;
while(EOF != (c = bio_getchar(h)))
{
if(('/' == c) && ('*' == prevc))
break;
else
prevc = c;
}
continue;
default:
bio_ungetc(h);
c = '/';
break;
}
}
break;
}
/*
* Char starts a number => parse the number.
* Note: This never returns an EDT_INTEGER!
*/
if(c == '.' || isdigit(c))
{
bio_ungetc(h);
eel_current.lval = newdata(EDT_REAL);
if(!eel_current.lval)
return token(TK_ERROR);
eel_current.lval->value.r = bio_strtod(h);
if(negative)
eel_current.lval->value.r = -eel_current.lval->value.r;
return token(TK_RNUM);
}
negative = 0;
/*
* (Multi)character literal (cast to integer)
* Note that this is always little endian, so
* it's safe to use for "FourCC" identifiers
* and the like.
*/
if('\'' == c)
{
eel_current.lval = newdata(EDT_INTEGER);
if(!eel_current.lval)
return token(TK_ERROR);
eel_current.lval->value.i = 0;
while((c = bio_getchar(h)) != '\'')
{
if(EOF == c)
{
eel_error("EOF inside character literal!");
return token(TK_ERROR);
}
eel_current.lval->value.i <<= 8;
eel_current.lval->value.i |= c;
}
return token(TK_INUM);
}
/* String literal */
if('"' == c)
return token(parse_string(h));
/* Check for "preprocessor" directives */
if('#' == c)
{
directive = 1;
c = bio_getchar(h);
}
/* Char starts an identifier => read the name. */
if(isalpha(c) || ('_' == c))
{
int len = 1;
int start = bio_tell(h) - 1;
while((isalnum(c = bio_getchar(h)) || ('_' == c)) && (EOF != c))
++len;
if(lexbuf_bump(len) < 0)
return token(TK_ERROR);
bio_seek(h, start, SEEK_SET);
bio_read(h, lexbuf, len);
lexbuf[len] = '\0';
if(directive)
{
directive = 0;
s = eel_s_find_n_t(NULL, lexbuf, EST_DIRECTIVE);
if(!s)
{
eel_error("Unknown directive '%s'!",
lexbuf);
return token(TK_ERROR);
}
if(s->type != EST_DIRECTIVE)
{
eel_error("Preprocessor directive expected!");
return token(TK_ERROR);
}
eel_current.lval = newdata(EDT_SYMREF);
eel_current.lval->value.sym = s;
return token(TK_SYMREF);
}
else
{
s = eel_s_find(NULL, lexbuf);
if(s)
{
eel_current.lval = newdata(EDT_SYMREF);
eel_current.lval->value.sym = s;
return token(TK_SYMREF);
}
else
{
eel_current.lval = newdata(EDT_SYMNAME);
eel_d_setstring(eel_current.lval, lexbuf);
return token(TK_NEWSYM);
}
}
}
/* Look for valid operator characters */
if(strchr("+-*/^<>~&%@!|$", (char)c))
{
lexbuf[0] = c;
lexbuf[1] = 0;
s = eel_s_find_n_t(NULL, lexbuf, EST_OPERATOR);
if(!s)
{
eel_error("Unknown operator '%s'!", lexbuf);
return token(TK_ERROR);
}
eel_current.lval = newdata(EDT_SYMREF);
eel_current.lval->value.sym = s;
return token(TK_SYMREF);
}
return token(c);
}
void eel_unlex(void)
{
eel_current.unlexed = 1;
}
+65
View File
@@ -0,0 +1,65 @@
/*(LGPL)
---------------------------------------------------------------------------
e_lexer.h - EEL engine lexer
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _EEL_LEXER_H_
#define _EEL_LEXER_H_
#include "eel.h"
/*----------------------------------------------------------
Tokens and Unique User Token management
----------------------------------------------------------*/
/* Errors... */
#define TK_ERROR -1 /* Lexer complains about something */
/* Immediate arguments */
#define TK_RNUM 300 /* Real number */
#define TK_INUM 301 /* Integer number */
#define TK_STRN 302 /* String literal */
/* Symbol reference */
#define TK_SYMREF 303
/* New symbol */
#define TK_NEWSYM 304
#define TK_USER 1024
int eel_get_unique_token();
/*----------------------------------------------------------
The Lexer
----------------------------------------------------------*/
/* Get next "token" */
int eel_lex(int report_eoln);
/*
* Push back last token + lval.
* Works only for *one* token per context!
*/
void eel_unlex(void);
void eel_lexer_cleanup(void);
#endif /*_EEL_LEXER_H_*/
+60
View File
@@ -0,0 +1,60 @@
/*(LGPL)
---------------------------------------------------------------------------
e_path.c - EEL file
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <string.h>
#include "eel.h"
/*----------------------------------------------------------
File System Interface (Hah! :-)
----------------------------------------------------------*/
static char *_eel_path = NULL;
void eel_set_path(const char *path)
{
if(_eel_path)
free(_eel_path);
if(path)
_eel_path = strdup(path);
else
_eel_path = NULL;
}
const char *eel_path(void)
{
if(!_eel_path)
{
#if defined(WIN32)
eel_set_path("");
#elif defined(MACOS)
eel_set_path("");
#else
eel_set_path("./");
#endif
}
return _eel_path;
}
+89
View File
@@ -0,0 +1,89 @@
/*(LGPL)
---------------------------------------------------------------------------
e_register.c - EEL extension registry
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "e_register.h"
#include "e_symtab.h"
#include "e_lexer.h"
eel_symbol_t *_register(const char *name, eel_symtypes_t stype,
eel_datatypes_t type)
{
eel_symbol_t *s = eel_s_new(name, stype);
if(!s)
return (eel_symbol_t *)0;
s->data.type = type;
return s;
}
int eel_register_operator(const char *name, eel_operator_cb_t handler,
int priority, int retargs, int preargs)
{
eel_symbol_t *s = _register(name, EST_OPERATOR, EDT_OPERATOR);
if(!s)
return -1;
s->token = eel_get_unique_token();
s->data.value.op.priority = priority;
s->data.value.op.retargs = retargs;
s->data.value.op.preargs = preargs;
s->data.value.op.cb = handler;
return s->token;
}
int eel_register_enum_class(void)
{
return eel_get_unique_token();
}
int eel_register_enum(int eclass, const char *name, int value)
{
eel_symbol_t *s = _register(name, EST_ENUM, EDT_INTEGER);
if(!s)
return -1;
s->token = eclass;
s->data.value.i = value;
return 0;
}
int eel_register_directive(const char *name, eel_parser_cb_t parser)
{
eel_symbol_t *s = _register(name, EST_DIRECTIVE, EDT_DIRECTIVE);
if(!s)
return -1;
s->token = eel_get_unique_token();
s->data.value.parse = parser;
return 0;
}
int eel_register_special(const char *name, eel_parser_cb_t parser)
{
eel_symbol_t *s = _register(name, EST_SPECIAL, EDT_SPECIAL);
if(!s)
return -1;
s->token = eel_get_unique_token();
s->data.value.parse = parser;
return 0;
}
+96
View File
@@ -0,0 +1,96 @@
/*(LGPL)
---------------------------------------------------------------------------
e_register.h - EEL extension registry
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _EEL_REGISTER_H_
#define _EEL_REGISTER_H_
#include "eel.h"
/*----------------------------------------------------------
Language Extension API
----------------------------------------------------------*/
/*
* Register an EEL operator. ("The real thing")
*
* (This is the "direct to the metal" interface; you may want
* to use eel_register_cmd_operator(), eel_register_func_operator()
* or eel_register_math_operator() instead most of the time.)
*
* The callback should check the types and number of
* arguments (easily done with eel_get_args()), and return
* 1 upon success, -1 in case of an error, or 0 if the script
* should be terminated.
*
* Operator precendence in expressions is controlled by means
* of operator priorities specified when operators are
* registered. EEL built-in operators also have priorities,
* and these are of course put in relation to the ones
* specified for extension operators;
* '=' (assignment) 0 (always lowest)
* '+' '-' (add, sub) 10
* '*' '/' '%' (mul, div, mod) 20
* '^' (power) 30
* (command and function operators) 100
*
* 'retargs' is the number of return arguments, and 'preargs'
* is the number of arguments that must be placed *before* the
* operator token. (Only normal, "post operator token" argument
* lists can be variable length!)
*
* The return value is the command's unique token, or a
* negative value upon failure.
*/
int eel_register_operator(const char *name, eel_operator_cb_t handler,
int priority, int retargs, int preargs);
/* Register a new enum class. The unique token is returned. */
int eel_register_enum_class(void);
/*
* Add enum constant to specified enum class.
*
* A negative value is returned if the operation fails.
*/
int eel_register_enum(int eclass, const char *name, int value);
/*
* Register a directive.
*
* A directive is similar to a command, but can optionally be
* invoked with a '#' before it's name. It does argument
* parsing itself, which provides more detailed control.
*
* A negative value is returned if the operation fails.
*/
int eel_register_directive(const char *name, int (*parser)(void));
/*
* Register a special.
*
* A special does argument parsing itself, like a directive,
* but the #<name> syntax is not valid.
*
* A negative value is returned if the operation fails.
*/
int eel_register_special(const char *name, int (*parser)(void));
#endif /*_EEL_REGISTER_H_*/
+159
View File
@@ -0,0 +1,159 @@
/*(LGPL)
---------------------------------------------------------------------------
e_script.c - EEL source code management
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
* Copyright (C) 2002, Florian Schulze <fs@crowproductions.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "_e_script.h"
#include "e_util.h"
#define DBG(x)
/*----------------------------------------------------------
Script source management
----------------------------------------------------------*/
script_t eel_scripttab[MAX_SCRIPTS];
int eel_script_alloc()
{
int h = 0;
while(h < MAX_SCRIPTS)
{
if(!eel_scripttab[h].data)
return h;
++h;
}
return -1;
}
int eel_load(const char *filename)
{
FILE *f;
int h = eel_script_alloc();
if(h < 0)
return h;
/* Construct full name with path */
eel_scripttab[h].name = malloc(strlen(filename) +
strlen(eel_path()) + 2);
if(!eel_scripttab[h].name)
return -1;
strcpy(eel_scripttab[h].name, eel_path());
#ifdef WIN32
strcat(eel_scripttab[h].name, "\\");
#elif defined MACOS
strcat(eel_scripttab[h].name, ":");
#else
strcat(eel_scripttab[h].name, "/");
#endif
strcat(eel_scripttab[h].name, filename);
f = fopen(eel_scripttab[h].name, "rb");
if(!f)
return -1;
if(fseek(f, 0, SEEK_END) == 0)
{
eel_scripttab[h].len = ftell(f);
if(fseek(f, 0, SEEK_SET) == 0)
{
eel_scripttab[h].data = (unsigned char *)malloc(
eel_scripttab[h].len + 1);
if(eel_scripttab[h].data)
{
if(fread(eel_scripttab[h].data,
eel_scripttab[h].len,
1, f) == 1)
{
fclose(f);
DBG(log_printf(DLOG, "Loaded script \"%s\"."
" (Handle %d)\n",
eel_scripttab[h].name,
h);)
return h;
}
DBG(printf("eel_load(): Read error!\n");)
eel_free(h);
}
DBG(printf("eel_load(): Memory allocation error!\n");)
}
DBG(printf("eel_load(): Rewind error!\n");)
}
DBG(printf("eel_load(): Seek-to-end error!\n");)
fclose(f);
return -2;
}
int eel_load_from_mem(const char *script, unsigned len)
{
char name_buffer[128];
int h = eel_script_alloc();
if(h < 0)
return h;
/* Construct name */
snprintf(name_buffer, 127, "mem_script (%p, %u)", script, len);
name_buffer[127] = '\0';
eel_scripttab[h].name = (char *)malloc(strlen(name_buffer) + 1);
if(!eel_scripttab[h].name)
return -1;
strcpy(eel_scripttab[h].name, name_buffer);
eel_scripttab[h].len = len;
eel_scripttab[h].data =
(unsigned char *)malloc(eel_scripttab[h].len + 1);
if(!eel_scripttab[h].data)
{
DBG(printf("eel_load_from_mem(): Memory allocation error!\n");)
return -2;
}
memcpy(eel_scripttab[h].data, script, len);
DBG(printf("Loaded script \"%s\" (Handle %d)\n",
eel_scripttab[h].name, h);)
return h;
}
void eel_free(int handle)
{
if(handle < 0)
return;
if(handle > MAX_SCRIPTS)
return;
if(!eel_scripttab[handle].data)
return;
DBG(printf("Freeing script \"%s\". (Handle %d)\n",
eel_scripttab[handle].name, handle);)
free(eel_scripttab[handle].data);
eel_scripttab[handle].data = NULL;
free(eel_scripttab[handle].name);
eel_scripttab[handle].name = NULL;
eel_scripttab[handle].len = 0;
}
+29
View File
@@ -0,0 +1,29 @@
/*(LGPL)
---------------------------------------------------------------------------
e_script.h - EEL source code management (API)
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _EEL_SCRIPT_H_
#define _EEL_SCRIPT_H_
int eel_load(const char *filename);
int eel_load_from_mem(const char *script, unsigned len);
void eel_free(int handle);
#endif /*_EEL_SCRIPT_H_*/
+444
View File
@@ -0,0 +1,444 @@
/*(LGPL)
---------------------------------------------------------------------------
symtab.c - Simple symbol table for EEL
---------------------------------------------------------------------------
* Copyright (C) 2000-2003, 2007 David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kobolog.h"
#include "e_symtab.h"
#define DBG(x)
#define STRINGREP_SIZE 128
#define MAX_SCOPES 32
/*------------------------------------------------
Data container
------------------------------------------------*/
int eel_d_copy(eel_data_t *data, const eel_data_t *from)
{
eel_d_freestring(data);
switch (from->type)
{
case EDT_ILLEGAL:
case EDT_REAL:
case EDT_INTEGER:
case EDT_CADDR:
case EDT_SYMREF:
case EDT_SYMTAB:
case EDT_OPERATOR:
case EDT_DIRECTIVE:
case EDT_SPECIAL:
*data = *from;
return 1;
case EDT_STRING:
case EDT_SYMNAME:
data->type = from->type;
return eel_d_setstring(data, from->value.s);
}
return -1;
}
int eel_d_setstring(eel_data_t *data, const char *s)
{
if((EDT_STRING == data->type) || (EDT_SYMNAME == data->type))
free(data->value.s);
else
data->type = EDT_STRING;
data->value.s = (char *)malloc(strlen(s) + 1);
if(data->value.s < 0)
return -1;
strcpy(data->value.s, s);
return 0;
}
void eel_d_grabstring(eel_data_t *data, char *s)
{
if((EDT_STRING == data->type) || (EDT_SYMNAME == data->type))
free(data->value.s);
else
data->type = EDT_STRING;
data->value.s = s;
}
void eel_d_freestring(eel_data_t *data)
{
if((data->type != EDT_STRING) && (data->type != EDT_SYMNAME))
return;
free(data->value.s);
data->value.s = 0;
}
/* NOTE: Output string only valid until next call. */
const char *eel_d_stringrep(eel_data_t * data)
{
static char buf[STRINGREP_SIZE];
switch (data->type)
{
case EDT_ILLEGAL:
return "<illegal data>";
case EDT_REAL:
snprintf(buf, STRINGREP_SIZE, "%.10g", data->value.r);
return buf;
case EDT_INTEGER:
snprintf(buf, STRINGREP_SIZE, "%d", data->value.i);
return buf;
case EDT_STRING:
case EDT_SYMNAME:
if(data->value.s)
{
strncpy(buf, data->value.s, STRINGREP_SIZE);
return buf;
}
else
return "<undef string>";
case EDT_SYMTAB:
return "<symbol table reference>";
case EDT_CADDR:
snprintf(buf, STRINGREP_SIZE, "%x", data->value.a);
return buf;
case EDT_SYMREF:
return eel_s_stringrep(data->value.sym);
case EDT_OPERATOR:
if(data->value.s)
{
snprintf(buf, STRINGREP_SIZE, "(%s)",
data->value.s);
return buf;
}
else
return "<unnamed operator>";
case EDT_DIRECTIVE:
if(data->value.s)
{
snprintf(buf, STRINGREP_SIZE, "(#%s)",
data->value.s);
return buf;
}
else
return "<unnamed directive>";
case EDT_SPECIAL:
if(data->value.s)
{
snprintf(buf, STRINGREP_SIZE, "(%s)",
data->value.s);
return buf;
}
else
return "<unnamed special>";
default:
return "<internal error: undef data type>";
}
}
/*------------------------------------------------
Symbol table
------------------------------------------------*/
/* The symbol table: a chain of `struct symrec'. */
static eel_symbol_t *eel_s_table[MAX_SCOPES];
static int _current_scope = 0;
static int _deepest_scope = 0;
int eel_s_open(void)
{
memset(eel_s_table, 0, sizeof(eel_s_table));
_current_scope = 0;
_deepest_scope = 0;
return 0;
}
void eel_s_close(void)
{
eel_s_freeall();
}
eel_symbol_t *eel_s_new(const char *name, eel_symtypes_t type)
{
eel_symbol_t *sym;
sym = (eel_symbol_t *) calloc(1, sizeof(eel_symbol_t));
if(!sym)
{
log_printf(ELOG, "eel_s_new: Out of memory!\n");
return 0;
}
sym->name = strdup(name);
sym->type = type;
switch (type)
{
case EST_UNDEFINED:
break;
case EST_CONSTANT:
case EST_VARIABLE:
sym->data.type = EDT_REAL; /* Default type */
break;
case EST_LABEL:
case EST_FUNCTION:
sym->data.type = EDT_CADDR;
break;
case EST_OPERATOR:
sym->data.type = EDT_OPERATOR;
break;
case EST_DIRECTIVE:
sym->data.type = EDT_DIRECTIVE;
break;
case EST_SPECIAL:
sym->data.type = EDT_SPECIAL;
break;
case EST_ENUM:
sym->data.type = EDT_INTEGER; /* For the value */
break;
case EST_NAMESPACE:
sym->data.type = EDT_SYMTAB;
default:
log_printf(ELOG, "internal error: created"
" symbol of undef type\n");
}
sym->next = (struct eel_symbol_t *)eel_s_table[_current_scope];
eel_s_table[_current_scope] = sym;
return sym;
}
static void _free_scope(int scope)
{
eel_symbol_t *sym, *nextptr;
for(sym = eel_s_table[scope]; sym; sym = nextptr)
{
nextptr = (eel_symbol_t *) sym->next;
eel_d_freestring(&sym->data);
free(sym->name);
free(sym);
}
eel_s_table[scope] = (eel_symbol_t *)0;
}
void eel_s_freeall(void)
{
int scope;
for(scope = 0; scope < MAX_SCOPES; ++scope)
_free_scope(scope);
}
eel_symbol_t *eel_s_find(eel_symbol_t * sym, const char *eel_s_name)
{
int scope = _current_scope;
while(scope >= 0)
{
if(!sym)
sym = eel_s_table[scope];
for(; sym; sym = (eel_symbol_t *) sym->next)
if(strcmp(sym->name, eel_s_name) == 0)
return sym;
sym = NULL;
--scope;
}
return NULL;
}
eel_symbol_t *eel_s_find_n_tk(eel_symbol_t * sym, const char *eel_s_name, int token)
{
int scope = _current_scope;
while(scope >= 0)
{
if(!sym)
sym = eel_s_table[scope];
for(; sym; sym = (eel_symbol_t *) sym->next)
if((sym->token == token) &&
(strcmp(sym->name, eel_s_name) == 0))
return sym;
sym = NULL;
--scope;
}
return NULL;
}
eel_symbol_t *eel_s_find_tk(eel_symbol_t * sym, int token)
{
int scope = _current_scope;
while(scope >= 0)
{
if(!sym)
sym = eel_s_table[scope];
for(; sym; sym = (eel_symbol_t *) sym->next)
if(sym->token == token)
return sym;
sym = NULL;
--scope;
}
return NULL;
}
eel_symbol_t *eel_s_find_n_t(eel_symbol_t * sym, const char *eel_s_name,
eel_symtypes_t type)
{
int scope = _current_scope;
while(scope >= 0)
{
if(!sym)
sym = eel_s_table[scope];
for(; sym; sym = (eel_symbol_t *) sym->next)
if((sym->type == type) &&
(strcmp(sym->name, eel_s_name) == 0))
return sym;
--scope;
}
return NULL;
}
eel_symbol_t *eel_s_find_t(eel_symbol_t * sym, eel_symtypes_t type)
{
int scope = _current_scope;
while(scope >= 0)
{
if(!sym)
sym = eel_s_table[scope];
for(; sym; sym = (eel_symbol_t *) sym->next)
if(sym->type == type)
return sym;
--scope;
}
return NULL;
}
/* NOTE: Output string only valid until next call. */
const char *eel_s_stringrep(eel_symbol_t * sym)
{
static char buf[STRINGREP_SIZE];
switch (sym->type)
{
case EST_UNDEFINED:
snprintf(buf, STRINGREP_SIZE, "<undef symbol '%s'>",
sym->name);
return buf;
case EST_CONSTANT:
strncpy(buf, eel_d_stringrep(&sym->data), STRINGREP_SIZE);
return buf;
case EST_VARIABLE:
case EST_LABEL:
case EST_FUNCTION:
case EST_OPERATOR:
case EST_DIRECTIVE:
case EST_SPECIAL:
case EST_ENUM:
case EST_NAMESPACE:
strncpy(buf, sym->name, STRINGREP_SIZE);
return buf;
default:
return "<internal error: undef symbol type>";
}
}
/*------------------------------------------------
Scope/Namespace Management
------------------------------------------------*/
int eel_push_scope(void)
{
if(_deepest_scope >= MAX_SCOPES-1)
{
log_printf(ELOG, "EEL ERROR: Too deep scope nesting!\n");
return -1;
}
++_deepest_scope;
_current_scope = _deepest_scope;
DBG(log_printf(DLOG, "eel_push_scope(): current scope = %d\n", _current_scope);)
return 0;
}
int eel_pop_scope(void)
{
if(!_deepest_scope)
{
log_printf(ELOG, "EEL ERROR: Tried to pop root scope!\n");
return -1;
}
_free_scope(_deepest_scope);
--_deepest_scope;
_current_scope = _deepest_scope;
DBG(log_printf(DLOG, "eel_pop_scope(): current scope = %d\n", _current_scope);)
return 0;
}
int eel_scope(void)
{
return _current_scope;
}
int eel_set_scope(int ns)
{
if(ns < 0 || ns > _deepest_scope)
{
log_printf(ELOG, "EEL ERROR: Tried to select illegal scope!\n");
return -1;
}
_current_scope = ns;
return 0;
}
void eel_restore_scope(void)
{
_current_scope = _deepest_scope;
}
/*------------------------------------------------
Symbol table tools
------------------------------------------------*/
eel_symbol_t *eel_s_get(const char *name, eel_symtypes_t type)
{
eel_symbol_t *sym = eel_s_find(NULL, name);
if(sym)
{
if(sym->type == type)
return sym;
else
{
log_printf(ELOG, "eel_s_integer: '%s' is of"
" the wrong type!\n", name);
return NULL;
}
}
else
return eel_s_new(name, type);
}
int eel_s_set(eel_symbol_t *sym, const eel_data_t *dat)
{
return eel_d_copy(&sym->data, dat);
}
+239
View File
@@ -0,0 +1,239 @@
/*(LGPL)
---------------------------------------------------------------------------
symtab.h - Simple symbol table for EEL
---------------------------------------------------------------------------
* Copyright (C) 2000, 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _EEL_SYMTAB_H_
#define _EEL_SYMTAB_H_
/*------------------------------------------------
Data container
------------------------------------------------*/
struct eel_symbol_table_t;
struct eel_symbol_t;
struct eel_data_t;
typedef int (*eel_operator_cb_t)(int argc, struct eel_data_t *argv);
typedef int (*eel_parser_cb_t)(void);
/* Native datatypes */
typedef enum
{
EDT_ILLEGAL = 0,
EDT_REAL, /* Real */
EDT_INTEGER, /* Integer */
EDT_STRING, /* String */
EDT_CADDR, /* Code address */
EDT_SYMREF, /* Symbol reference */
EDT_SYMTAB, /* Symbol Table Reference (for namespaces) */
EDT_SYMNAME, /* Name of a new symbol (String) */
EDT_OPERATOR, /* Operator callback */
EDT_DIRECTIVE, /* Directive parser callback */
EDT_SPECIAL /* Special parser callback */
} eel_datatypes_t;
#define EDTM_ILLEGAL (1<<EDT_ILLEGAL)
#define EDTM_REAL (1<<EDT_REAL)
#define EDTM_INTEGER (1<<EDT_INTEGER)
#define EDTM_STRING (1<<EDT_STRING)
#define EDTM_CADDR (1<<EDT_CADDR)
#define EDTM_SYMREF (1<<EDT_SYMREF)
#define EDTM_SYMNAME (1<<EDT_SYMNAME)
#define EDTM_OPERATOR (1<<EDT_OPERATOR)
#define EDTM_DIRECTIVE (1<<EDT_DIRECTIVE)
#define EDTM_SPECIAL (1<<EDT_SPECIAL)
/* Data container */
typedef struct eel_data_t
{
eel_datatypes_t type;
union {
double r; /* real constant */
int i; /* integer constant; token */
int a; /* code address */
char *s; /* string constant; symbol name */
struct eel_symbol_t *sym; /* Symbol ref */
struct eel_symbol_table_t *st; /* Symbol table */
struct
{
eel_operator_cb_t cb;
char priority;
char retargs;
char preargs;
} op;
eel_parser_cb_t parse;
} value;
} eel_data_t;
int eel_d_copy(eel_data_t *data, const eel_data_t *from);
/* Copy string */
int eel_d_setstring(eel_data_t *data, const char *s);
/*
* Take over string. (No copying)
*
* The source string pointer *must* have been returned
* by malloc(), calloc() or realloc(), and the string
* MUST NOT be freed after this call. (The string will
* be owned by the data_t.)
*/
void eel_d_grabstring(eel_data_t *data, char *s);
void eel_d_freestring(eel_data_t *data);
/* NOTE: Output string only valid until next call. */
const char *eel_d_stringrep(eel_data_t *data);
/*------------------------------------------------
Symbol table
------------------------------------------------*/
typedef enum eel_symtypes_t
{
EST_UNDEFINED,
EST_CONSTANT,
EST_VARIABLE,
EST_LABEL,
EST_FUNCTION, /* token = function index; data.a = start "address" */
EST_OPERATOR, /* data -> callback; token = priority */
EST_DIRECTIVE, /* data -> callback */
EST_SPECIAL, /* data -> callback */
EST_ENUM, /* token = enum type; data.i = enum value */
EST_NAMESPACE /* data -> namespace symbol table */
} eel_symtypes_t;
#define ESTM_UNDEFINED (1<<EST_UNDEFINED)
#define ESTM_CONSTANT (1<<EST_CONSTANT)
#define ESTM_VARIABLE (1<<EST_VARIABLE)
#define ESTM_LABEL (1<<EST_LABEL)
#define ESTM_FUNCTION (1<<EST_FUNCTION)
#define ESTM_OPERATOR (1<<EST_OPERATOR)
#define ESTM_DIRECTIVE (1<<EST_DIRECTIVE)
#define ESTM_SPECIAL (1<<EST_SPECIAL)
#define ESTM_ENUM (1<<EST_ENUM)
#define ESTM_NAMESPACE (1<<EST_NAMESPACE)
/* symbol list node */
typedef struct eel_symbol_t
{
struct eel_symbol_t *next;
char *name;
eel_symtypes_t type;
eel_data_t data;
int token;
} eel_symbol_t;
/* Open/close symbol table */
int eel_s_open(void);
void eel_s_close(void);
eel_symbol_t *eel_s_new(const char *name, eel_symtypes_t type);
void eel_s_freeall(void);
eel_symbol_t *eel_s_find(eel_symbol_t *sym, const char *sym_name);
eel_symbol_t *eel_s_find_n_tk(eel_symbol_t *sym, const char *sym_name,
int token);
eel_symbol_t *eel_s_find_tk(eel_symbol_t *sym, int token);
eel_symbol_t *eel_s_find_n_t(eel_symbol_t *sym, const char *sym_name,
eel_symtypes_t type);
eel_symbol_t *eel_s_find_t(eel_symbol_t *sym, eel_symtypes_t type);
/* NOTE: Output string only valid until next call. */
const char *eel_s_stringrep(eel_symbol_t *sym);
/*------------------------------------------------
Scope Management
--------------------------------------------------
* The EEL symbol table manages namespace
* separation using a stack of separate symbol
* tables. A "scope" consists of a namespace and
* all namespaces above it.
*
* In the normal case, the "current scope" includes
* all namespaces currently defined, but it's
* possible to temporarily move the scope up to an
* arbitrary namespace level.
*
* Symbol lookups start at the namespace level set
* by the "current scope", and moves up from there
* until the symbol is found, or it is concluded
* that the symbol does not exist.
*
* New symbols are added to the namespace set by
* the "current scope". Higher level namespaces are
* never considered.
*/
/*
* Create a new, local scope inside the deepest
* level of the current scope, and make it current.
*/
int eel_push_scope(void);
/*
* Destroy the deepest level of the scope, and move
* up to the next higher level.
*/
int eel_pop_scope(void);
/*
* Returns the current scope level.
*/
int eel_scope(void);
/*
* Directly set scope level for subsequent eel_s_*()
* operations.
*/
int eel_set_scope(int ns);
/*
* Return to the current deepest level scope.
*/
void eel_restore_scope(void);
/*------------------------------------------------
Symbol table tools
------------------------------------------------*/
/*
* Find symbol 'name' and verify that it is of of type 'type',
* or if it doesn't exist, create new symbol 'name' of type
* 'type'.
*
* Returns the found/new symbol, or NULL in the case of
* failure.
*/
eel_symbol_t *eel_s_get(const char *name, eel_symtypes_t type);
/*
* Load 'dat' into symbol 'sym'.
* Returns 0 upon success, or a negative value if the operation
* fails.
*/
int eel_s_set(eel_symbol_t *sym, const eel_data_t *dat);
#endif /* _EEL_SYMTAB_H_ */
+236
View File
@@ -0,0 +1,236 @@
/*(LGPL)
---------------------------------------------------------------------------
e_util.c - EEL engine utilities
---------------------------------------------------------------------------
* Copyright (C) 2002, 2003, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "kobolog.h"
#include "e_util.h"
#include "eel.h"
#include "_e_script.h"
/*----------------------------------------------------------
"stdio" style API for raw memory buffers
----------------------------------------------------------*/
bio_file_t *bio_open(void *data, int len)
{
bio_file_t *bio = malloc(sizeof(bio_file_t));
if(!bio)
return NULL;
bio->data = (unsigned char *)data;
bio->len = len;
bio->pos = 0;
return bio;
}
bio_file_t *bio_clone(bio_file_t *bio)
{
bio_file_t *c = malloc(sizeof(bio_file_t));
if(!c)
return NULL;
*c = *bio;
return c;
}
void bio_close(bio_file_t *bio)
{
free(bio);
}
int bio_seek(bio_file_t *bio, int offset, int whence)
{
switch(whence)
{
case SEEK_SET:
bio->pos = offset;
break;
case SEEK_CUR:
bio->pos += offset;
break;
case SEEK_END:
bio->pos = bio->len - offset;
break;
}
if(bio->pos < 0)
bio->pos = 0;
else if(bio->pos > bio->len)
bio->pos = bio->len;
return 0;
}
int bio_read(bio_file_t *bio, char *buf, int count)
{
if(bio->pos + count > bio->len)
count = bio->len - bio->pos;
memcpy(buf, bio->data + bio->pos, count);
bio->pos += count;
return count;
}
double bio_strtod(bio_file_t *bio)
{
char *p = (char *)bio->data + bio->pos;
double val;
val = strtod(p, &p);
bio->pos = p - (char *)bio->data;
return val;
}
/*----------------------------------------------------------
Context management ("VM stack")
----------------------------------------------------------*/
eel_context_t eel_current = {
NULL,
NULL,
-1,
0,
0,
EOF,
NULL
};
static eel_context_t *context_stack = NULL;
void eel_push_context(void)
{
eel_context_t *c = malloc(sizeof(eel_context_t));
if(!c)
{
eel_error("INTERNAL ERROR: Failed to push context!");
return;
}
memcpy(c, &eel_current, sizeof(eel_context_t));
c->previous = context_stack;
context_stack = c;
}
void eel_pop_context(void)
{
eel_context_t *c = context_stack;
if(!c)
{
eel_error("INTERNAL ERROR: No context to pop!");
return;
}
memcpy(&eel_current, c, sizeof(eel_context_t));
context_stack = c->previous;
if(c->lval)
{
eel_d_freestring(c->lval);
free(c->lval);
}
free(c);
}
/*----------------------------------------------------------
Error handling tools
----------------------------------------------------------*/
static int last_script = -1;
int eel_error(const char *format, ...)
{
int count = 0;
va_list args;
int i, pos, line = 1;
/* Figure out which line we're at... */
pos = bio_tell(eel_current.bio);
bio_seek(eel_current.bio, 0, SEEK_SET);
for(i = 0; i < pos; ++i)
if('\n' == bio_getchar(eel_current.bio))
++line;
if((eel_current.script >= 0) && (eel_current.script != last_script))
{
count = log_printf(ELOG, "(EEL) in file \"%s\":\n",
eel_scripttab[eel_current.script].name);
last_script = eel_current.script;
}
if(eel_current.arg)
count += log_printf(ELOG, "(EEL) line %d, arg %d: ",
line, eel_current.arg);
else
count += log_printf(ELOG, "(EEL) line %d: ", line);
va_start(args, format);
count += vfprintf(stderr, format, args);
va_end(args);
count += log_printf(ELOG, "\n");
return count;
}
const char *eel_symbol_is(eel_symbol_t *s)
{
switch(s->type)
{
case EST_UNDEFINED: return "an undedined symbol";
case EST_CONSTANT: return "a constant";
case EST_VARIABLE: return "a variable";
case EST_LABEL: return "a label";
case EST_FUNCTION: return "a function";
case EST_OPERATOR: return "an operator";
case EST_DIRECTIVE: return "a directive";
case EST_SPECIAL: return "a special";
case EST_ENUM: return "an enum constant";
case EST_NAMESPACE: return "a namespace";
}
/*
* Should be here, and not under default:, to get
* a warning if we forget to add new types. :-)
*/
return "a broken symbol! (illegal type)";
}
const char *eel_data_is(eel_data_t *d)
{
switch(d->type)
{
case EDT_ILLEGAL: return "an illegal value";
case EDT_REAL: return "a real value";
case EDT_INTEGER: return "an integer value";
case EDT_STRING: return "a string";
case EDT_CADDR: return "a code address";
case EDT_SYMREF: return "a symbol reference";
case EDT_SYMTAB: return "a symbol table pointer";
case EDT_SYMNAME: return "a (new) symbol name";
case EDT_OPERATOR: return "an operator callback pointer";
case EDT_DIRECTIVE: return "a directive callback pointer";
case EDT_SPECIAL: return "a special callback pointer";
}
return "a broken value! (illegal type)";
}
+107
View File
@@ -0,0 +1,107 @@
/*(LGPL)
---------------------------------------------------------------------------
e_util.h - EEL engine utilities
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _EEL_UTIL_H_
#define _EEL_UTIL_H_
#include <stdio.h>
#include "eel.h"
/*----------------------------------------------------------
"stdio" style API for raw memory buffers
----------------------------------------------------------*/
typedef struct bio_file_t
{
unsigned char *data;
int len;
int pos;
} bio_file_t;
bio_file_t *bio_open(void *data, int len);
bio_file_t *bio_clone(bio_file_t *bio);
void bio_close(bio_file_t *bio);
/* Get the next character from the buffer */
static inline int bio_getchar(bio_file_t *bio)
{
if(bio->pos < bio->len)
return bio->data[bio->pos++];
else
return EOF;
}
/* Push last character back */
static inline void bio_ungetc(bio_file_t *bio)
{
if(bio->pos > 0)
--bio->pos;
}
/*
* Get the current source buffer position
*/
static inline int bio_tell(bio_file_t *bio)
{
return bio->pos;
}
/*
* Set the current source buffer position
*/
int bio_seek(bio_file_t *bio, int offset, int whence);
/*
* Read 'count' bytes from the source code,
* starting at the current position, into
* 'buf'. Returns the number of bytes
* actually read.
*/
int bio_read(bio_file_t *bio, char *buf, int count);
double bio_strtod(bio_file_t *bio);
/*----------------------------------------------------------
Context management ("VM stack")
------------------------------------------------------------
* This supports skipping between scripts, and also keeps
* track of the arg count for error messages.
*/
typedef struct eel_context_t
{
struct eel_context_t *previous;
bio_file_t *bio;
int script;
int arg;
int unlexed;
int token;
eel_data_t *lval;
} eel_context_t;
extern eel_context_t eel_current;
void eel_push_context(void);
void eel_pop_context(void);
#endif /*_EEL_UTIL_H_*/
+89
View File
@@ -0,0 +1,89 @@
The "EEL Compressed Source" file format
---------------------------------------
EEL Compressed Source files are EEL source files stripped of
all comments and formatting information. They are tokenized
using an external dictionary, which contains all keywords of
EEL and extensions, and any project names. Immediate values
are encoded using an extended "variable length number" BCD
format.
* EEL Compressed Source files should have the extension
".eec" instead of the normal ".eel", for reliable
identification, and to avoid accidentally overwriting
source files with compressed source files. (That would
be nasty, as the compression is destructive WRT comments,
symbol names and code formatting...)
* The files are based on 4 bit words. Wider words (8 or 16
bits) are *not* aligned to 8 or 16 bits, but start
directly at the current 4 bit word position.
* There are two modes: XASCII and XBCD. Decoding of a file
starts in the XASCII mode. XASCII codes are 8 bits, and
XBCD codes are 4 bits.
* The file starts with an 8 bit version code for the EEL
dictionary to use. Then follows a list of 4 byte EEL
extension IDs, terminated by a 0 byte. The EEL extension
IDs are of the form "XXXv", where "XXX" is an acronym
identifying the extension, and "v" is the version of the
extension dictionary to use.
Any extension ID that doesn't math any known EEL
extension is assumed to be a Local Dictionary name.
".eed" will be appended to the full 4 byte string (no
version code), and then EEL will try to find a file by
that name and load it as a Local Dictionary.
* A Local Dictionary file consists of a 32 bit little
endian integer that holds the number of tokens needed
by the dictionary, *optionally* followed by a list of
null terminated ASCII strings, holding the human
readable names of all tokens. (The token name list is
useful when mixing compressed source with ASCII source
during development.)
* Token values are allocated from 0 and up as
dictionaries are loaded. This is why there are
extension version numbers in the dictionary lists -
and it should also make it obvious that EEL extensions
must only *add* to their dictionaries if compatibility
with old compressed sources is to be maintained.
* XASCII code:
0-14 XBCD code + enter XBCD mode
15 Local token dictionary follows
16 8 bit character follows
17 Null terminated 8 bit quoted string
18 Quoted 8 bit Pascal string follows
19 Quoted 8 bit Pascal string continued
20 <unused>
21 <unused>
22 <unused>
23 <unused>
24 <unused>
25 <unused>
26 <unused>
27 <unused>
28 <unused>
29 <unused>
30 <unused>
31 <unused>
32-127 Standard 7 bit ASCII (copied as is)
128-191 Short Tokens
192-254 Extended Tokens (8 extra bits follow)
255 Wide Tokens (16 extra bits follow)
* XBCD code:
0-9 Decimal numbers 0-9
10 Decimal point (.)
11 Unary minus (-)
12 Argument separator/white space
13 Statement terminator (;)
14 Single XASCII character follows
15 Switch to XASCII mode
+161
View File
@@ -0,0 +1,161 @@
The Extensible Embeddable Language
----------------------------------
Well, here's a simple extensible scripting language
for ya' - all in a few thousand lines of (hopefully)
portable C code! No external tools needed to build
this version, although future versions might have the
parsing functions replaced with a Bison parser. (You
still won't really need Bison, unless you want to
change the language syntax.)
Extending the language is done by adding commands,
directives, operators, enums and "specials". It's pretty
much as simple as it gets, without using a custom source
preprocessor. A single scanf() style function call does
argument checking and casting for commands, so you
usually get away with a single call to get all your
arguments.
The Language:
EEL is CaSe SEnSitIve!
An EEL file consists of zero or more statements.
There are (so far) two kinds of statements: Command
Statements and Assignments.
A Command Statement starts with a command name,
followed by zero or more coma (,) separated arguments,
and is terminated with a semicolon (;). Some commands
accept variable numbers of arguments.
A command argument may be of any type, which means
that symbol references, command references and other
weird stuff can be passed.
"Functions" can be implemented as commands that
require one or more arguments to be passed by
reference. (See "set" / '=' command, which is an
example of this.)
Assignments can be written in two forms; either using
the 'set' command, which takes two arguments; the
target and the source - or the more compact '='
operator method, is which the target is on the left
side of the '=' operator, and the source is on the
right.
TODO:
"Function commands" can alternatively be called using
the traditional "a = function ...;" style. The
command's first argument should be an output argument,
and preferably the only output argument. (Note that
the *real* set command/'=' operator is not involved!)
/TODO
Variables are created implicitly as a result of the
first Assignment. (This will probably change...)
Variables are dynamically typed. Both value and type
may be changed after a variable has been declared.
EEL strives to use "late casting", which means that
in the normal case, assignments change type as well
as value.
TODO:
The above is somewhat confusing, and will most probably
change. We need a clear distinction between "passing
by value" and "passing by reference", as this becomes
very important in algorithmic code.)
/TODO
Data types that can be stored in a variable:
Real (64 bit float)
Integer (32 bit signed)
String (Null terminated)
Code Address (Source or bytecode position)
Symbol Reference (Can refer to any symbol)
Symbol reference variables are quite interesting, as
they can theoretically reference *anything*, including
commands - and all type info is available as an extra
bonus. (Note that most of this is rather useless right
now, as the language doesn't have constructs that can
make use of it!)
Built-in EEL commands, functions and directives:
end;
End of script. (Same effect as EOF.)
set <name>, <value>;
<name> = <value>;
Set variable <name> to <value>. If <name>
is not yet defined, it will be created
automatically. (This is likely to change.)
print arg[, arg[,...]];
Print the arguments in the "standard console".
procedure <procname>([arg[, arg[,...]]]) { <body> }
function <funcname>([arg[, arg[,...]]]) { <body> }
Define procedure 'procname' or function
'funcname', respectively. The function will
take (as in "require") the specified
(dynamically typed) arguments, and when it's
invoked, the code in between the curly
brackets ('{' and '}') will be executed.
The function *must* return a value, while
the procedure must *not* - that's the only
difference between them.
When a function or procedure is running, a
local "partition" of the symbol table is used.
Initially, this table contains only the
arguments, but any new variables declared as
the body code executes are added to *this*
table, instead of the global table, as in the
normal case. The "partition" (actually just a
local namespace) is removed as the function
or procedure returns, which obviously means
that the argument variables, as well as any
local variables, are deleted.
<funcname> [arg[, arg[,...]]];
Function invocation. Works just like a
command invocation, although EEL will just
add the arguments to the Local Symbol Table -
it will not perform any type checking, as
argument types aren't supported as of now.
#include <filename>
Run the file 'filename' in the current scope,
and then continue executing the current
script.
Note that as of now, this is not 100%
compatible with the way a C/C++ preprocessor
works! It's more like loading and executing
a "subroutine script".
#define <identifier> <value>
C header compatibility hack for defining
constants. It's mostly there to allow EEL
scripts to share headers with other languages
using C preprocessors, like C or C++. We want
to do that *without* actually using cpp, as
EEL scripts are supposed to load and run from
source without "special tools".
TODO:
<name>:
Label declaration. Creates a symbol <name>
that contains the Code Address of the
statement following the colon.
TODO:
i = 0;
for i < 10
{
...
} i = i + 1;
+573
View File
@@ -0,0 +1,573 @@
/*(LGPL)
---------------------------------------------------------------------------
eel.c - The "Extensible Embeddable Language"
---------------------------------------------------------------------------
* Copyright (C) 2002, 2007 David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
TODO:
* Eliminate the parser and use the expression
evaluator instead. "Everything is an expression."
The evaluator will need a few extra features, but
lots of messy code will be eliminated, and the
design will be much cleaner and more flexible.
* Make all commands, functions and stuff operators.
Binary commands and operators should use the same
callback prototype. A way of describing return,
reference and input arguments will be needed. (Not
sure about multiple return arguments...)
* Split the command and operator callbacks up
into one "parser" callback, and one or more
run-time callbacks. Leave argument grabbing/casting
and callback selection to the parser callbacks, for
simplicity and flexibility. (They can still use
tools like the EEL argument grabber.) Let them
generate byte code or similar (using some EEL API
calls) for one or more "operations", as required to
perform the desired actions.
Interpreting mode could still be implemented,
with some overhead, by compiling and executing one
statement at a time - although IMHO, one might as
well make a complete switch to compiling + VM.
That would eliminate issues with distinguishing
between compiled and interpreted functions and the
like.
* To make the above work, we'll need a distinction
between compile time and run-time symbols. (In
fact, we don't really need actual symbols at run
time, other than for globals. Local variables
should be allocated from some VM stack.) For
example, some features of the current argument
grabber won't work for compiling, as they rely on
the actual contents of a symbol, to extract data.
Such features will have to be implemented though
run-time callbacks that execute the corresponding
operations.
* Remove EDT_INTEGER? The lexer currently never
returns TK_INUM except for character literals,
and I'm starting to think it's a good idea to
keep it that way, for various reasons...
* Functions should return values! Make sure
that errors are issued if a function returns
without an explicit return statement.
* Add "procedure" keyword for "functions"
without return values. Issue an error if a
"return with value" statement is executed
within a procedure.
* Implement conditional constructs.
* Implement '&' ("address" of function) operator.
* Add a procedure extension(name) to EEL, to allow
scripts to check for extension availability and
version. Should return version #, or 0 if the
extension is not available. There should probably
also be a uses(name), to avoid implicitly stuffing
all available extensions into the name space of
every script.
* Eliminate all fixed size arrays and limits...
* Implement the Compressed Source file format.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "eel.h"
#include "e_lexer.h"
#include "e_util.h"
#include "e_builtin.h"
#include "_e_script.h"
/*----------------------------------------------------------
Parser/Interpreter
----------------------------------------------------------*/
static int command(void)
{
eel_symbol_t *sym = eel_current.lval->value.sym;
if(!sym->data.value.op.cb)
{
eel_error("INTERNAL ERROR: Command callback missing!");
return -1;
}
if(sym->data.value.op.retargs)
{
eel_error("INTERNAL ERROR: Return value ignored!");
return -1;
}
if(eel_parse_args(",", ';') < 0)
return -1;
return sym->data.value.op.cb(eel_arg_count, eel_args);
}
static int assignment(int token)
{
int res;
/* Get target */
switch(token)
{
case TK_SYMREF:
switch (eel_current.lval->value.sym->type)
{
case EST_UNDEFINED:
case EST_VARIABLE:
break;
default:
eel_error("Cannot change the value of %s!",
eel_symbol_is(eel_current.lval->value.sym));
return -1;
}
case TK_NEWSYM:
if(eel_grab_arg() < 0)
{
eel_error("Argument overflow!");
return -1;
}
break;
#if 0
case 0:
eel_error("Unexpected end of file!");
return -1;
default:
eel_error("Unexpected token!");
return -1;
#endif
}
/* Get operator */
if(eel_lex(0) != '=')
{
eel_error("Expected '='!");
return -1;
}
/* Get source */
if(eel_parse_args(",", ';') < 0)
return -1;
/* End of declaration; execute! */
res = eel_cmd_set(eel_arg_count, eel_args);
return res;
}
static int directive(void)
{
int res;
eel_symbol_t *sym = eel_current.lval->value.sym;
if(!sym->data.value.parse)
{
eel_error("INTERNAL ERROR: Directive callback missing!");
return -1;
}
res = sym->data.value.parse();
return res;
}
static int function(void)
{
int arg, res;
eel_symbol_t *func = eel_current.lval->value.sym;
eel_push_scope();
/* Grab arguments */
res = eel_parse_args(",", ';');
/* Enter function */
eel_push_context();
if(eel_current.script == func->token)
eel_current.bio = bio_clone(eel_current.bio);
else
eel_current.bio = bio_open(eel_scripttab[func->token].data,
eel_scripttab[func->token].len);
if(!eel_current.bio)
{
eel_error("INTERNAL ERROR: Failed to set up script"
" reading at start of function!");
eel_pop_context();
eel_pop_scope();
return -1;
}
res = bio_seek(eel_current.bio, func->data.value.a, SEEK_SET);
/*
* Find out where to stuff the arguments.
*/
for(arg = 0; (arg < eel_arg_count) && (res >= 0); )
switch(eel_lex(0))
{
case ',':
/* Already verified, so we just skip them. */
break;
case TK_NEWSYM:
{
eel_symbol_t *sym;
sym = eel_set_data(eel_current.lval->value.s,
eel_args + arg);
if(sym)
{
sym->type = EST_VARIABLE;
++arg;
}
else
{
eel_error("INTERNAL ERROR: Failed to "
"create argument variable!");
res = -1;
}
break;
}
case TK_SYMREF:
eel_error("Function argument clashes with "
"previously defined symbol!");
res = -1;
break;
default:
eel_error("Syntax error in argument list!");
res = -1;
break;
}
/* Find start of body ("the code") */
if(res >= 0)
{
if(eel_lex(0) != ')')
{
eel_error("Expected ')'!");
res = -1;
}
else if(eel_lex(0) != '{')
{
eel_error("Expected '{'!");
res = -1;
}
}
if(res >= 0)
{
/*
* Execute function
*
* Note that this is a simple, inefficient
* RECURSIVE implementation!
*
* Alternatively, we could set up a "trigger"
* to run "the rest of this function" when
* the function ends - and then just return.
*/
res = eel_call(eel_current.script, bio_tell(eel_current.bio));
}
/* Leave function */
bio_close(eel_current.bio);
eel_pop_context();
eel_pop_scope();
if(res >= 0)
return 1;
else
return res;
}
int eel_call(int handle, int pos)
{
int res = 1;
int depth = 0;
eel_push_context();
eel_current.script = handle;
eel_current.bio = bio_open(eel_scripttab[handle].data,
eel_scripttab[handle].len);
if(!eel_current.bio)
{
eel_error("INTERNAL ERROR: Couldn't read script!");
eel_pop_context();
return -1;
}
bio_seek(eel_current.bio, pos, SEEK_SET);
eel_current.lval = NULL;
while(res > 0)
{
/* Start of statement */
eel_clear_args(0);
eel_current.arg = 0;
switch(eel_lex(0))
{
case 0:
/* End of script */
res = 0;
break;
case ';':
/* Empty statement */
res = 1;
break;
case '{':
/* Begin scope */
eel_push_scope();
++depth;
res = 1;
break;
case '}':
/* End scope */
if(depth)
{
eel_pop_scope();
--depth;
}
else
res = 0;
break;
case TK_NEWSYM:
res = assignment(TK_NEWSYM);
break;
case TK_SYMREF:
switch(eel_current.lval->value.sym->type)
{
case EST_CONSTANT: /* Just to get a more
* sensible error message...
*/
case EST_UNDEFINED:
case EST_VARIABLE:
res = assignment(TK_SYMREF);
break;
/* case EST_COMMAND:
res = command();
break;*/
case EST_FUNCTION:
res = function();
break;
case EST_DIRECTIVE:
case EST_SPECIAL:
res = directive();
break;
case EST_ENUM:
case EST_LABEL:
eel_error("Syntax error!");
res = -1;
break;
case EST_OPERATOR:
res = command();
break;
default:
eel_error("INTERNAL ERROR: Lexer returned"
" symbol of illegal type!");
res = -1;
break;
}
break;
default:
eel_error("Unexpected character!");
res = -1;
break;
}
}
eel_current.arg = 0;
bio_close(eel_current.bio);
eel_pop_context();
return res;
}
int eel_run(int handle)
{
return eel_call(handle, 0);
}
static int _eel_users = 0;
int eel_open(void)
{
if(_eel_users++)
return 0;
memset(eel_scripttab, 0, sizeof(eel_scripttab));
if(eel_s_open() < 0)
{
_eel_users = 0;
return -1;
}
eel_register_operator("end", eel_cmd_end, 100, 0, 0);
eel_register_operator("set", eel_cmd_set, 100, 0, 0);
eel_register_operator("print", eel_cmd_print, 100, 0, 0);
eel_register_operator("+", eel_op_add, 10, 0, 1);
eel_register_operator("-", eel_op_sub, 10, 0, 1);
eel_register_operator("*", eel_op_mul, 20, 0, 1);
eel_register_operator("/", eel_op_div, 20, 0, 1);
eel_register_operator("%", eel_op_mod, 20, 0, 1);
eel_register_operator("^", eel_op_pow, 30, 0, 1);
eel_register_directive("include", eel_dir_include);
eel_register_directive("define", eel_dir_define);
eel_register_special("function", eel_spec_function);
eel_register_special("procedure", eel_spec_procedure);
/* eel_register_special("global", eel_spec_global);*/
return 0;
}
void eel_close()
{
int h;
if(!_eel_users)
return;
for(h = 0; h < MAX_SCRIPTS; ++h)
eel_free(h);
eel_s_freeall();
eel_set_path(NULL);
eel_s_close();
eel_lexer_cleanup();
--_eel_users;
}
/*----------------------------------------------------------
EEL variable access
----------------------------------------------------------*/
eel_symbol_t *eel_set_integer(const char *name, int value)
{
eel_symbol_t *sym = eel_s_get(name, EST_VARIABLE);
if(!sym)
return NULL;
eel_d_freestring(&sym->data);
sym->data.type = EDT_INTEGER;
sym->data.value.i = value;
return sym;
}
eel_symbol_t *eel_set_real(const char *name, double value)
{
eel_symbol_t *sym = eel_s_get(name, EST_VARIABLE);
if(!sym)
return NULL;
eel_d_freestring(&sym->data);
sym->data.type = EDT_REAL;
sym->data.value.r = value;
return sym;
}
eel_symbol_t *eel_set_string(const char *name, const char *s)
{
eel_symbol_t *sym = eel_s_get(name, EST_VARIABLE);
if(!sym)
return NULL;
eel_d_setstring(&sym->data, s);
return sym;
}
eel_symbol_t *eel_set_data(const char *name, const eel_data_t *dat)
{
eel_symbol_t *sym = eel_s_get(name, EST_VARIABLE);
if(!sym)
return NULL;
eel_d_freestring(&sym->data);
switch (dat->type)
{
case EDT_ILLEGAL:
case EDT_REAL:
case EDT_INTEGER:
case EDT_CADDR:
case EDT_SYMREF:
case EDT_SYMTAB:
case EDT_OPERATOR:
case EDT_DIRECTIVE:
case EDT_SPECIAL:
sym->data = *dat;
break;
case EDT_STRING:
case EDT_SYMNAME:
sym->data.type = dat->type;
if(eel_d_setstring(&sym->data, dat->value.s) < 0)
return NULL;
break;
}
return sym;
}
eel_symbol_t *eel_clone_symbol(const char *name, const eel_symbol_t *orig)
{
eel_symbol_t *sym = eel_s_new(name, orig->type);
if(!sym)
return NULL;
eel_d_freestring(&sym->data);
switch (orig->data.type)
{
case EDT_ILLEGAL:
case EDT_REAL:
case EDT_INTEGER:
case EDT_CADDR:
case EDT_SYMREF:
case EDT_SYMTAB:
case EDT_OPERATOR:
case EDT_DIRECTIVE:
case EDT_SPECIAL:
sym->data = orig->data;
break;
case EDT_STRING:
case EDT_SYMNAME:
sym->data.type = orig->data.type;
if(eel_d_setstring(&sym->data, orig->data.value.s) < 0)
return NULL;
break;
}
return sym;
}
+376
View File
@@ -0,0 +1,376 @@
/*(LGPL)
---------------------------------------------------------------------------
eel.c - The "Extensible Embeddable Language"
---------------------------------------------------------------------------
* Copyright (C) 2002, David Olofson
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
TODO:
...or at least think about: Extend the dynamic typing to
include functions. Ex:
set some_var, some_function;
will connect 'some_function' to 'some_var', so that the
function is called to calculate the value whenever the
variable is read. Obviously, such a variable becomes
read-only! This could be worked around with the following
syntax:
set some_var, read_function, write_function;
A handy syntax extension:
set some_var, some_function(...);
or
set some_other_var,
{
...
some code
...
};
which would be interpreted as a nameless, single statement
function being connected to 'some_var', and a nameless
function with a normal body being connected to
'some_other_var'.
The whole point with this is that it allows the way a
variable gets written to change at run time, and that it's
even applicable to code that isn't aware of this feature.
BTW, this is pretty similar to Delphi 'properties' -
although you cannot turn variables into properties or vice
versa, at run time, as you could do with this feature.
----------------------------------------------------------------------
Scope/Namespace management sucks. Namespaces probably need
to be arranged in a tree rather than a stack.
This becomes obvious when considering what happens when you
call a function... The function executes in a scope that's
the calling scope + a local namespace!
Indeed, this could be considered an interesting "feature",
but I'm not sure it's as useful as it is confusing...
As a quick hack solution, one could reserve the first few
namespaces (ie highest levels) for language extensions, to
make it easy to install and remove extensions without them
interfering with each other.
One could possible change the "scope level" into a "scope
handle", and make a scope a "list of namespaces" rather
than the current "lowest namespace index". That would allow
multiple totally independent scopes under the same scripting
engine.
*/
#ifndef _EEL_H_
#define _EEL_H_
#include "e_script.h"
#include "e_symtab.h"
#ifdef __cplusplus
extern "C" {
#endif
#define EEL_MAX_ARGS 128
#define _EEL_API_
#include "e_register.h"
/*----------------------------------------------------------
Toolkit for directive callbacks
----------------------------------------------------------*/
/*
* The internal argument list. Avoid using this directly!
*/
extern int eel_arg_count;
extern eel_data_t *eel_args;
/*
* Add the last lval returned from the lexer to the internal
* argument list.
*/
int eel_grab_arg(void);
/*
* Clear the internal argument list starting at argument
* 'first'.
*/
void eel_clear_args(int first);
/*
* Parse arguments separated by one of the characters in
* 'separators', until the character 'terminator' is
* matched, or an error occurs.
*
* The arguments that are "grabbed" are added to the
* internal argument list using eel_grab_arg(), and can
* be retrieved, decoded and typechecked with eel_get_args()
* if desired.
*
* Returns a negative value in case of an error.
*/
int eel_parse_args(const char *separators, char terminator);
/*----------------------------------------------------------
Toolkit for command callbacks
----------------------------------------------------------*/
/*
* Grab arguments from the internal argument list, verifying
* that their types and count are in accordance with 'fmt'.
* Results will be stored in the variables passed by
* reference after the 'fmt' argument.
*
* Normally, the argument count not matching the number of
* arguments specified in the format string will be reported
* as an error. However, this can be changed by means of
* argument tipples and optional arguments, denoted by the
* '<', '>' and '[', ']' characters respectively.
*
* The return value is the number of arguments correctly
* retrieved, or a negative value if something goes wrong.
*
* Format string syntax:
*
* Char Meaning Type
*--------------------------------------------------------------------
* * Skip argument N/A
*
* , Next argument (optional) N/A
*
* ? Grab any argument as is eel_data_t *
*
* < > Argument tipple. The argument spec in N/A
* between the < and > will be repeated
* until all arguments have been grabbed,
* or an error occurs. The target args
* should be ARRAYS and *must* be large
* enough to fit all arguments retrieved,
* or bad things will happen. Argument
* tipples cannot be nested. There must
* be no argument specifiers after the
* '>', as an argument tipple repeats
* "forever". Normally, the arguments
* must match an *integer* number of
* tipple repetitions - but this can be
* changed using the Optional Argument
* format codes.
*
* [ ] Optional arguments. Arguments beyond N/A
* the '[' character after the 'argv'
* array has been exhausted will not
* generate an error, as this situation
* normally would.
*
* Optional arguments inside argument
* tipples apply only to the *last*
* tipple repetition.
*
* Note that no argument specifiers must
* follow the ']' character - or; optional
* arguments must be the last ones in the
* list.
*
* E Get any enum symbol. Any enum class eel_symbol_t *
* is accepted. (Do check, if you care!)
*
* V Get value of any allowed type. This eel_data_t *
* code can optionally be followed by a
* list of allowed types, enclosed within
* parentheses. If no list is specified,
* all *value* data types are accepted,
* including EDT_CADDR. Available types:
* i integer (EDT_INTEGER)
* r real (EDT_REAL)
* s string (EDT_STRING)
* c code address (EDT_CADDR)
* e enum constant (EDT_SYMREF)
* All but 'e' are set by default, if no
* explicit list is provided.
*
* Note that symrefs are followed, to get
* the values of the symbols they refer to,
* except that when 'e' (enum constant) is
* listed, symrefs to enum constants will
* stay symrefs.
*
* e Get enum value. The argument should be int
* INITIALIZED with the enum class. Only
* enum constants from the right class
* are accepted.
*
* i Get integer value int
*
* n Get or create variable. In the case of eel_symbol_t *
* a new variable being created, the
* argument has to be a string holding the
* name of the new variable. The type of
* a new variable is undefined.
*
* r Get real value double
*
* s Get string value char *
* NOTE: Currently it's NOT possible to
* get non-string values casted to strings.
* (We need a fast, reference counting
* string package...)
*
* v Get variable (must be defined) eel_symbol_t *
*
* f Get function "address". The returned eel_symbol_t *
* symbol will be of type EST_FUNCTION.
*
* Any other character in the format string in matched with that
* respective character in the input.
*/
int eel_get_args(const char *fmt, ...);
/*
* Like eel_get_args(), but reads from the specified eel_data_t
* array instead of the internal argument list.
*/
int eel_get_args_from(int argc, struct eel_data_t *argv, const char *fmt, ...);
/*
* Print a nice error "prompt" with file name, line #
* and arg #, followed by your formatted error message,
* followed by a newline ('\n') where appropriate. (Don't
* add this yourself, unless you need multiple lines.)
*
* Note that syntax checking and most type
* and error checking is done automatically
* by EEL (mostly in eel_get_args()). This
* function is only for errors that EEL
* can't detect.
*
* Returns total number of characters printed.
*/
int eel_error(const char *format, ...);
/*
* Returns a string describing the specified symbol
* in a way that would complete the sentences:
* "This is ..."
* "Expected ..."
*/
const char *eel_symbol_is(eel_symbol_t *s);
/*
* As eel_symbol_is(), but for data containers.
*/
const char *eel_data_is(eel_data_t *d);
/*----------------------------------------------------------
Runtime API
----------------------------------------------------------*/
/*
* Set the EEL path for scripts and includes.
*
* This must be a valid path, terminated by a
* '/', '\', ':' or whatever "directory separator"
* the current platform is using, and will be
* prepended to file names passed to the #include
* directive.
*/
void eel_set_path(const char *path);
/* Returns the current EEL path. */
const char *eel_path(void);
/* Open/Close EEL. (Keep track of nested calls!) */
int eel_open(void);
void eel_close();
/*
* Execute code inside script 'handle', starting at 'pos'.
*
* Execution will take place as a "sub procedure", with it's
* own context¨, but *not* automatically it's own *scope*.
* This is because most real applications will require
* scripts to be passed some "arguments", which is actually
* done by creating a few variables before the script is
* called. Obviously, the code that sets up the "argument
* variables" and calls eel_run() will also have to do any
* scope pushing and popping *around* these calls - which is
* why it can't be done inside eel_run().
*
* The following events will end execution:
* - A statement gives a "stop execution" return code.
* - The top level scope is terminated; ie an unmatched
* curly brace ('}') is found.
* - EOF is reached.
* - An error occurs.
*
* Returns the result, which will be negative in the case
* of an error.
*/
int eel_call(int handle, int pos);
/*
* Run a script from the start.
*
* Currently 100% equivalent to eel_call('handle', 0);
*/
int eel_run(int handle);
/*
* Create or set variable 'name' to the specified value.
* Existing symbols *must* be of type ST_VARIABLE, or
* these calls will fail.
*
* Returns the changed/created symbol, or NULL if the
* operation failed.
*/
eel_symbol_t *eel_set_integer(const char *name, int value);
eel_symbol_t *eel_set_real(const char *name, double value);
eel_symbol_t *eel_set_string(const char *name, const char *s);
eel_symbol_t *eel_set_data(const char *name, const eel_data_t *dat);
/*
* Make an exact copy of symbol 'orig', except for the name,
* which will be 'name'. Symbol 'name' must not exist before
* this call.
*
* Returns the created symbol, or NULL if the operation failed.
*/
eel_symbol_t *eel_clone_symbol(const char *name, const eel_symbol_t *orig);
#undef _EEL_API_
#ifdef __cplusplus
};
#endif
#endif /* _EEL_H_ */