diff options
Diffstat (limited to 'src')
125 files changed, 29287 insertions, 0 deletions
diff --git a/src/Grammar b/src/Grammar new file mode 100644 index 0000000..574acd6 --- /dev/null +++ b/src/Grammar @@ -0,0 +1,72 @@ +# Grammar for Python, version 4 + +# Changes compared to version 3: +# Removed 'dir' statement. +# Function call argument is a testlist instead of exprlist. + +# Changes compared to version 2: +# The syntax of Boolean operations is changed to use more +# conventional priorities: or < and < not. + +# Changes compared to version 1: +# modules and scripts are unified; +# 'quit' is gone (use ^D); +# empty_stmt is gone, replaced by explicit NEWLINE where appropriate; +# 'import' and 'def' aren't special any more; +# added 'from' NAME option on import clause, and '*' to import all; +# added class definition. + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# expr_input is the input for the input() function; +# eval_input is the input for the eval() function. + +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: (NEWLINE | stmt)* ENDMARKER +expr_input: testlist NEWLINE +eval_input: testlist ENDMARKER + +funcdef: 'def' NAME parameters ':' suite +parameters: '(' [fplist] ')' +fplist: fpdef (',' fpdef)* +fpdef: NAME | '(' fplist ')' + +stmt: simple_stmt | compound_stmt +simple_stmt: expr_stmt | print_stmt | pass_stmt | del_stmt | flow_stmt | import_stmt +expr_stmt: (exprlist '=')* exprlist NEWLINE +# For assignments, additional restrictions enforced by the interpreter +print_stmt: 'print' (test ',')* [test] NEWLINE +del_stmt: 'del' exprlist NEWLINE +pass_stmt: 'pass' NEWLINE +flow_stmt: break_stmt | return_stmt | raise_stmt +break_stmt: 'break' NEWLINE +return_stmt: 'return' [testlist] NEWLINE +raise_stmt: 'raise' expr [',' expr] NEWLINE +import_stmt: 'import' NAME (',' NAME)* NEWLINE | 'from' NAME 'import' ('*' | NAME (',' NAME)*) NEWLINE +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef +if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] +while_stmt: 'while' test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] +try_stmt: 'try' ':' suite (except_clause ':' suite)* ['finally' ':' suite] +except_clause: 'except' [expr [',' expr]] +suite: simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT + +test: and_test ('or' and_test)* +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +comp_op: '<'|'>'|'='|'>' '='|'<' '='|'<' '>'|'in'|'not' 'in'|'is'|'is' 'not' +expr: term (('+'|'-') term)* +term: factor (('*'|'/'|'%') factor)* +factor: ('+'|'-') factor | atom trailer* +atom: '(' [testlist] ')' | '[' [testlist] ']' | '{' '}' | '`' testlist '`' | NAME | NUMBER | STRING +trailer: '(' [testlist] ')' | '[' subscript ']' | '.' NAME +subscript: expr | [expr] ':' [expr] +exprlist: expr (',' expr)* [','] +testlist: test (',' test)* [','] + +classdef: 'class' NAME parameters ['=' baselist] ':' suite +baselist: atom arguments (',' atom arguments)* +arguments: '(' [testlist] ')' diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..d47eceb --- /dev/null +++ b/src/Makefile @@ -0,0 +1,508 @@ +# /*********************************************************** +# Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +# Netherlands. +# +# All Rights Reserved +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the names of Stichting Mathematisch +# Centrum or CWI not be used in advertising or publicity pertaining to +# distribution of the software without specific, written prior permission. +# +# STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# ******************************************************************/ + +# Makefile for Python +# =================== +# +# If you are in a hurry, you can just edit this Makefile to choose the +# correct settings for SYSV and RANLIB below, and type "make" in this +# directory. If you are using a recent version of SunOS (or Ultrix?) +# you don't even have to edit: the Makefile comes pre-configured for +# such systems with all configurable options turned off, building the +# minimal portable version of the Python interpreter. +# +# If have more time, read the section on configurable options below. +# It may still be wise to begin building the minimal portable Python, +# to see if it works at all, and select options later. You don't have +# to rebuild all objects when you turn on options; all dependencies +# are concentrated in the file "config.c" which is rebuilt whenever +# the Makefile changes. (Except if you turn on the GNU Readline option +# you may have to toss out the tokenizer.o object.) + + +# Operating System Defines (ALWAYS READ THIS) +# =========================================== + +# Uncomment the following line if you are using a System V derivative. +# This must be used, for instance, on an SGI IRIS. Don't use it for +# SunOS. (This is only needed by posixmodule.c...) + +#SYSVDEF= -DSYSV + +# Choose one of the following two lines depending on whether your system +# requires the use of 'ranlib' after creating a library, or not. + +#RANLIB = true # For System V +RANLIB = ranlib # For BSD + +# If your system doesn't have symbolic links, uncomment the following +# line. + +#NOSYMLINKDEF= -DNO_LSTAT + + +# Installation Options +# ==================== + +# You may want to change DEFPYTHONPATH to reflect where you install the +# Python module library. The default contains "../lib" so running +# the interpreter from the source/build directory as distributed will +# find the library (admittedly a hack). + +DEFPYTHONPATH= .:/usr/local/lib/python:/ufs/guido/lib/python:../lib + + +# For "Pure" BSD Systems +# ====================== +# +# "Pure" BSD systems (as opposed to enhanced BSD derivatives like SunOS) +# often miss certain standard library functions. Source for +# these is provided, you just have to turn it on. This may work for +# other systems as well, where these things are needed. + +# If your system does not have a strerror() function in the library, +# uncomment the following two lines to use one I wrote. (Actually, this +# is missing in most systems I have encountered, so it is turned on +# in the Makefile. Turn it off if your system doesn't have sys_errlist.) + +STRERROR_SRC= strerror.c +STRERROR_OBJ= strerror.o + +# If your BSD system does not have a fmod() function in the library, +# uncomment the following two lines to use one I wrote. + +#FMOD_SRC= fmod.c +#FMOD_OBJ= fmod.o + +# If your BSD system does not have a strtol() function in the library, +# uncomment the following two lines to use one I wrote. + +#STRTOL_SRC= strtol.c +#STRTOL_OBJ= strtol.o + +# If your BSD system does not have a getcwd() function in the library, +# but it does have a getwd() function, uncomment the following two lines +# to use one I wrote. (If you don't have getwd() either, turn on the +# NO_GETWD #define in getcwd.c.) + +#GETCWD_SRC= getcwd.c +#GETCWD_OBJ= getcwd.o + +# If your signal() function believes signal handlers return int, +# uncomment the following line. + +#SIGTYPEDEF= -DSIGTYPE=int + + +# Further porting hints +# ===================== +# +# If you don't have the header file <string.h>, but you do have +# <strings.h>, create a file "string.h" in this directory which contains +# the single line "#include <strings.h>", and add "-I." to CFLAGS. +# If you don't have the functions strchr and strrchr, add definitions +# "-Dstrchr=index -Dstrrchr=rindex" to CFLAGS. (NB: CFLAGS is not +# defined in this Makefile.) + + +# Configurable Options +# ==================== +# +# Python can be configured to interface to various system libraries that +# are not available on all systems. It is also possible to configure +# the input module to use the GNU Readline library for interactive +# input. For each configuration choice you must uncomment the relevant +# section of the Makefile below. Note: you may also have to change a +# pathname and/or an architecture identifier that is hardcoded in the +# Makefile. +# +# Read the comments to determine if you can use the option. (You can +# always leave all options off and build a minimal portable version of +# Python.) + + +# BSD Time Option +# =============== +# +# This option does not add a new module but adds two functions to +# an existing module. +# +# It implements time.millisleep() and time.millitimer() +# using the BSD system calls select() and gettimeofday(). +# +# Uncomment the following line to select this option. + +#BSDTIMEDEF= -DBSD_TIME + + +# GNU Readline Option +# =================== +# +# If you have the sources of the GNU Readline library you can have +# full interactive command line editing and history in Python. +# The GNU Readline library is distributed with the BASH shell +# (I only know of version 1.05). You must build the GNU Readline +# library and the alloca routine it needs in their own source +# directories (which are subdirectories of the basg source directory), +# and plant a pointer to the BASH source directory in this Makefile. +# +# Uncomment and edit the following block to use the GNU Readline option. +# - Edit the definition of BASHDIR to point to the bash source tree. +# You may have to fix the definition of LIBTERMCAP; leave the LIBALLOCA +# definition commented if alloca() is in your C library. + +#BASHDIR= ../../bash-1.05 +#LIBREADLINE= $(BASHDIR)/readline/libreadline.a +#LIBALLOCA= $(BASHDIR)/alloc-files/alloca.o +#LIBTERMCAP= -ltermcap +#RL_USE = -DUSE_READLINE +#RL_LIBS= $(LIBREADLINE) $(LIBALLOCA) $(LIBTERMCAP) +#RL_LIBDEPS= $(LIBREADLINE) $(LIBALLOCA) + + +# STDWIN Option +# ============= +# +# If you have the sources of STDWIN (by the same author) you can +# configure Python to incorporate the built-in module 'stdwin'. +# This requires a fairly recent version of STDWIN (dated late 1990). +# +# Uncomment and edit the following block to use the STDWIN option. +# - Edit the STDWINDIR defition to reflect the top of the STDWIN source +# tree. +# - Edit the ARCH definition to reflect your system's architecture +# (usually the program 'arch' or 'machine' returns this). +# You may have to edit the LIBX11 defition to reflect the location of +# the X11 runtime library if it is non-standard. + +#STDWINDIR= ../../stdwin +#ARCH= sgi +#LIBSTDWIN= $(STDWINDIR)/Build/$(ARCH)/x11/lib/lib.a +#LIBX11 = -lX11 +#STDW_INCL= -I$(STDWINDIR)/H +#STDW_USE= -DUSE_STDWIN +#STDW_LIBS= $(LIBSTDWIN) $(LIBX11) +#STDW_LIBDEPS= $(LIBSTDWIN) +#STDW_SRC= stdwinmodule.c +#STDW_OBJ= stdwinmodule.o + + +# Amoeba Option +# ============= +# +# If you have the Amoeba 4.0 distribution (Beta or otherwise) you can +# configure Python to incorporate the built-in module 'amoeba'. +# (Python can also be built for native Amoeba, but it requires more +# work and thought. Contact the author.) +# +# Uncomment and edit the following block to use the Amoeba option. +# - Edit the AMOEBADIR defition to reflect the top of the Amoeba source +# tree. +# - Edit the AM_CONF definition to reflect the machine/operating system +# configuration needed by Amoeba (this is the name of a subdirectory +# of $(AMOEBADIR)/conf/unix, e.g., vax.ultrix). + +#AMOEBADIR= /usr/amoeba +#AM_CONF= mipseb.irix +#LIBAMUNIX= $(AMOEBADIR)/conf/unix/$(AM_CONF)/lib/amunix/libamunix.a +#AM_INCL= -I$(AMOEBADIR)/src/h +#AM_USE = -DUSE_AMOEBA +#AM_LIBDEPS= $(LIBAMUNIX) +#AM_LIBS= $(LIBAMUNIX) +#AM_SRC = amoebamodule.c sc_interpr.c sc_errors.c +#AM_OBJ = amoebamodule.o sc_interpr.o sc_errors.o + + +# Silicon Graphics IRIS Options +# ============================= +# +# The following three options are only relevant if you are using a +# Silicon Graphics IRIS machine. These have been tested with IRIX 3.3.1 +# on a 4D/25. + + +# GL Option +# ========= +# +# This option incorporates the built-in module 'gl', which provides a +# complete interface to the Silicon Graphics GL library. It adds +# about 70K to the Python text size and about 260K to the unstripped +# binary size. +# +# NOTE WHEN BUILDING FOR THE FIRST TIME: +# There is a circular dependency in the build process: you need to have +# a working Python interpreter before you can build a Python interpreter +# that incorporates the 'gl' module -- the source file 'glmodule.c' is +# not distributed (it's about 140K!) and a Python script is used to +# create it. Thus, you first have to build python without the the GL +# and Panel options, then edit the Makefile to turn them (or at least GL) +# on and rebuild. You may also have to set PYTHONPATH to point to +# the place where the module library is for the generation script to +# work. +# +# Uncomment the following block to use the GL option. + +#GL_USE = -DUSE_GL +#GL_LIBDEPS= +#GL_LIBS= -lgl_s +#GL_SRC = glmodule.c cgensupport.c +#GL_OBJ = glmodule.o cgensupport.o + + +# Panel Option +# ============ +# +# If you have source to the NASA Ames Panel Library, you can configure +# Python to incorporate the built-in module 'pnl', which is used byu +# the standard module 'panel' to provide an interface to most features +# of the Panel Library. This option requires that you also turn on the +# GL option. It adds about 100K to the Python text size and about 160K +# to the unstripped binary size. This requires Panel Library version 9.7 +# (for lower versions you may have to remove some functionality -- send +# me the patches if you bothered to do this). +# +# Uncomment and edit the following block to use the Panel option. +# - Edit the PANELDIR definition to point to the top-level directory +# of the Panel distribution tree. + +#PANELDIR= /usr/people/guido/src/pl +#PANELLIBDIR= $(PANELDIR)/library +#LIBPANEL= $(PANELLIBDIR)/lib/libpanel.a +#PANEL_USE= -DUSE_PANEL +#PANEL_INCL= -I$(PANELLIBDIR)/include +#PANEL_LIBDEPS= $(LIBPANEL) +#PANEL_LIBS= $(LIBPANEL) +#PANEL_SRC= panelmodule.c +#PANEL_OBJ= panelmodule.o + + +# Audio Option +# ============ +# +# This option lets you play with /dev/audio on the IRIS 4D/25. +# It incorporates the built-in module 'audio'. +# Warning: using the asynchronous I/O facilities of this module can +# create a second 'thread', which looks in the listings of 'ps' like a +# forked child. However, it shares its address space with the parent. +# +# Uncomment the following block to use the Audio option. + +#AUDIO_USE= -DUSE_AUDIO +#AUDIO_SRC= audiomodule.c asa.c +#AUDIO_OBJ= audiomodule.o asa.o + + +# Major Definitions +# ================= + +STANDARD_OBJ= acceler.o bltinmodule.o ceval.o classobject.o \ + compile.o dictobject.o errors.o fgetsintr.o \ + fileobject.o floatobject.o $(FMOD_OBJ) frameobject.o \ + funcobject.o $(GETCWD_OBJ) \ + graminit.o grammar1.o import.o \ + intobject.o intrcheck.o listnode.o listobject.o \ + mathmodule.o methodobject.o modsupport.o \ + moduleobject.o node.o object.o parser.o \ + parsetok.o posixmodule.o regexp.o regexpmodule.o \ + strdup.o $(STRERROR_OBJ) \ + stringobject.o $(STRTOL_OBJ) structmember.o \ + sysmodule.o timemodule.o tokenizer.o traceback.o \ + tupleobject.o typeobject.o + +STANDARD_SRC= acceler.c bltinmodule.c ceval.c classobject.c \ + compile.c dictobject.c errors.c fgetsintr.c \ + fileobject.c floatobject.c $(FMOD_SRC) frameobject.c \ + funcobject.c $(GETCWD_SRC) \ + graminit.c grammar1.c import.c \ + intobject.c intrcheck.c listnode.c listobject.c \ + mathmodule.c methodobject.c modsupport.c \ + moduleobject.c node.c object.c parser.c \ + parsetok.c posixmodule.c regexp.c regexpmodule.c \ + strdup.c $(STRERROR_SRC) \ + stringobject.c $(STRTOL_SRC) structmember.c \ + sysmodule.c timemodule.c tokenizer.c traceback.c \ + tupleobject.c typeobject.c + +CONFIGDEFS= $(STDW_USE) $(AM_USE) $(AUDIO_USE) $(GL_USE) $(PANEL_USE) \ + '-DPYTHONPATH="$(DEFPYTHONPATH)"' + +CONFIGINCLS= $(STDW_INCL) + +LIBDEPS= libpython.a $(STDW_LIBDEPS) $(AM_LIBDEPS) \ + $(GL_LIBDEPS) $(PANEL_LIBSDEP) $(RL_LIBDEPS) + +# NB: the ordering of items in LIBS is significant! +LIBS= libpython.a $(STDW_LIBS) $(AM_LIBS) \ + $(PANEL_LIBS) $(GL_LIBS) $(RL_LIBS) -lm + +LIBOBJECTS= $(STANDARD_OBJ) $(STDW_OBJ) $(AM_OBJ) $(AUDIO_OBJ) \ + $(GL_OBJ) $(PANEL_OBJ) + +LIBSOURCES= $(STANDARD_SRC) $(STDW_SRC) $(AM_SRC) $(AUDIO_SRC) \ + $(GL_SRC) $(PANEL_SRC) + +OBJECTS= pythonmain.o config.o + +SOURCES= $(LIBSOURCES) pythonmain.c config.c + +GENOBJECTS= acceler.o fgetsintr.o grammar1.o \ + intrcheck.o listnode.o node.o parser.o \ + parsetok.o strdup.o tokenizer.o bitset.o \ + firstsets.o grammar.o metagrammar.o pgen.o \ + pgenmain.o printgrammar.o + +GENSOURCES= acceler.c fgetsintr.c grammar1.c \ + intrcheck.c listnode.c node.c parser.c \ + parsetok.c strdup.c tokenizer.c bitset.c \ + firstsets.c grammar.c metagrammar.c pgen.c \ + pgenmain.c printgrammar.c + + +# Main Targets +# ============ + +python: libpython.a $(OBJECTS) $(LIBDEPS) Makefile + $(CC) $(CFLAGS) $(OBJECTS) $(LIBS) -o @python + mv @python python + +libpython.a: $(LIBOBJECTS) + -rm -f @lib + ar cr @lib $(LIBOBJECTS) + $(RANLIB) @lib + mv @lib libpython.a + +python_gen: $(GENOBJECTS) $(RL_LIBDEPS) + $(CC) $(CFLAGS) $(GENOBJECTS) $(RL_LIBS) -o python_gen + + +# Utility Targets +# =============== + +# Don't take the output from lint too seriously. I have not attempted +# to make Python lint-free. But I use function prototypes. + +LINTFLAGS= -h + +LINTCPPFLAGS= $(CONFIGDEFS) $(CONFIGINCLS) $(SYSVDEF) \ + $(AM_INCL) $(PANEL_INCL) + +LINT= lint + +lint:: $(SOURCES) + $(LINT) $(LINTFLAGS) $(LINTCPPFLAGS) $(SOURCES) + +lint:: $(GENSOURCES) + $(LINT) $(LINTFLAGS) $(GENSOURCES) + +# Generating dependencies is only necessary if you intend to hack Python. +# You may change $(MKDEP) to your favorite dependency generator (it should +# edit the Makefile in place). + +MKDEP= mkdep + +depend:: + $(MKDEP) $(LINTCPPFLAGS) $(SOURCES) $(GENSOURCES) + +# You may change $(CTAGS) to suit your taste... + +CTAGS= ctags -t -w + +HEADERS= *.h + +tags: $(SOURCES) $(GENSOURCES) $(HEADERS) + $(CTAGS) $(SOURCES) $(GENSOURCES) $(HEADERS) + +clean:: + -rm -f *.o core [,#@]* + +clobber:: clean + -rm -f python python_gen libpython.a tags + + +# Build Special Objects +# ===================== + +# You may change $(COMPILE) to reflect the default .c.o rule... + +COMPILE= $(CC) -c $(CFLAGS) + +amoebamodule.o: amoebamodule.c + $(COMPILE) $(AM_INCL) $*.c + +config.o: config.c Makefile + $(COMPILE) $(CONFIGDEFS) $(CONFIGINCLS) $*.c + +fgetsintr.o: fgetsintr.c + $(COMPILE) $(SIGTYPEDEF) $*.c + +intrcheck.o: intrcheck.c + $(COMPILE) $(SIGTYPEDEF) $*.c + +panelmodule.o: panelmodule.c + $(COMPILE) $(PANEL_INCL) $*.c + +posixmodule.o: posixmodule.c + $(COMPILE) $(SYSVDEF) $(NOSYMLINKDEF) $*.c + +sc_interpr.o: sc_interpr.c + $(COMPILE) $(AM_INCL) $*.c + +sc_error.o: sc_error.c + $(COMPILE) $(AM_INCL) $*.c + +stdwinmodule.o: stdwinmodule.c + $(COMPILE) $(STDW_INCL) $*.c + +timemodule.o: timemodule.c + $(COMPILE) $(SIGTYPEDEF) $(BSDTIMEDEF) $*.c + +tokenizer.o: tokenizer.c + $(COMPILE) $(RL_USE) $*.c + +.PRECIOUS: python libpython.a glmodule.c graminit.c graminit.h + + +# Generated Sources +# ================= +# +# Some source files are (or may be) generated. +# The rules for doing so are given here. + +# Build "glmodule.c", the GL interface. +# See important note at "GL Option" above. +# You may have to set and export PYTHONPATH for this to work. +# Ignore the messages emitted by the cgen script as long as its exit +# status is zero. +# Also ignore the warnings emitted while compiling glmodule.c; it works. + +glmodule.c: cstubs cgen + python cgen <cstubs >@glmodule.c + mv @glmodule.c glmodule.c + +# The dependencies for graminit.[ch] are not turned on in the +# distributed Makefile because the files themselves are distributed. +# Turn them on if you want to hack the grammar. + +#graminit.c graminit.h: Grammar python_gen +# python_gen Grammar diff --git a/src/PROTO.h b/src/PROTO.h new file mode 100644 index 0000000..88b94fe --- /dev/null +++ b/src/PROTO.h @@ -0,0 +1,79 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* +The macro PROTO(x) is used to put function prototypes in the source. +This is defined differently for compilers that support prototypes than +for compilers that don't. It should be used as follows: + int some_function PROTO((int arg1, char *arg2)); +A variant FPROTO(x) is used for cases where Standard C allows prototypes +but Think C doesn't (mostly function pointers). + +This file also defines the macro HAVE_PROTOTYPES if and only if +the PROTO() macro expands the prototype. It is also allowed to predefine +HAVE_PROTOTYPES to force prototypes on. +*/ + +#ifndef PROTO + +#ifdef __STDC__ +#define HAVE_PROTOTYPES +#endif + +#ifdef THINK_C +#undef HAVE_PROTOTYPES +#define HAVE_PROTOTYPES +#endif + +#ifdef sgi +#ifdef mips +#define HAVE_PROTOTYPES +#endif +#endif + +#ifdef HAVE_PROTOTYPES +#define PROTO(x) x +#else +#define PROTO(x) () +#endif + +#endif /* PROTO */ + + +/* FPROTO() is for cases where Think C doesn't like prototypes */ + +#ifdef THINK_C +#define FPROTO(arglist) () +#else /* !THINK_C */ +#define FPROTO(arglist) PROTO(arglist) +#endif /* !THINK_C */ + +#ifndef HAVE_PROTOTYPES +#define const /*empty*/ +#else /* HAVE_PROTOTYPES */ +#ifdef THINK_C +#undef const +#define const /*empty*/ +#endif /* THINK_C */ +#endif /* HAVE_PROTOTYPES */ diff --git a/src/README b/src/README new file mode 100644 index 0000000..714cd5b --- /dev/null +++ b/src/README @@ -0,0 +1,11 @@ +This directory contains the source for the Python interpreter. + +To build the interpreter, edit the Makefile, follow the instructions +there, and type "make python". + +To use the interpreter, you must set the environment variable PYTHONPATH +to point to the directory containing the standard modules. These are +distributed as a sister directory called 'lib' of this source directory. +Try importing the module 'testall' to see if everything works. + +Good Luck! diff --git a/src/To.do b/src/To.do new file mode 100644 index 0000000..893e96d --- /dev/null +++ b/src/To.do @@ -0,0 +1,11 @@ +- return better errors for file objects (also check read/write allowed, etc.) + +- introduce more specific exceptions (e.g., zero divide, index failure, ...) + +- why do reads from stdin fail when I suspend the process? + +- introduce macros to set/inspect errno for syscalls, to support things + like getoserr() + +- fix interrupt handling (interruptable system calls should call + intrcheck() to clear the interrupt status) diff --git a/src/acceler.c b/src/acceler.c new file mode 100644 index 0000000..5ed37d3 --- /dev/null +++ b/src/acceler.c @@ -0,0 +1,136 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Parser accelerator module */ + +/* The parser as originally conceived had disappointing performance. + This module does some precomputation that speeds up the selection + of a DFA based upon a token, turning a search through an array + into a simple indexing operation. The parser now cannot work + without the accelerators installed. Note that the accelerators + are installed dynamically when the parser is initialized, they + are not part of the static data structure written on graminit.[ch] + by the parser generator. */ + +#include "pgenheaders.h" +#include "grammar.h" +#include "token.h" +#include "parser.h" + +/* Forward references */ +static void fixdfa PROTO((grammar *, dfa *)); +static void fixstate PROTO((grammar *, dfa *, state *)); + +void +addaccelerators(g) + grammar *g; +{ + dfa *d; + int i; +#ifdef DEBUG + printf("Adding parser accellerators ...\n"); +#endif + d = g->g_dfa; + for (i = g->g_ndfas; --i >= 0; d++) + fixdfa(g, d); + g->g_accel = 1; +#ifdef DEBUG + printf("Done.\n"); +#endif +} + +static void +fixdfa(g, d) + grammar *g; + dfa *d; +{ + state *s; + int j; + s = d->d_state; + for (j = 0; j < d->d_nstates; j++, s++) + fixstate(g, d, s); +} + +static void +fixstate(g, d, s) + grammar *g; + dfa *d; + state *s; +{ + arc *a; + int k; + int *accel; + int nl = g->g_ll.ll_nlabels; + s->s_accept = 0; + accel = NEW(int, nl); + for (k = 0; k < nl; k++) + accel[k] = -1; + a = s->s_arc; + for (k = s->s_narcs; --k >= 0; a++) { + int lbl = a->a_lbl; + label *l = &g->g_ll.ll_label[lbl]; + int type = l->lb_type; + if (a->a_arrow >= (1 << 7)) { + printf("XXX too many states!\n"); + continue; + } + if (ISNONTERMINAL(type)) { + dfa *d1 = finddfa(g, type); + int ibit; + if (type - NT_OFFSET >= (1 << 7)) { + printf("XXX too high nonterminal number!\n"); + continue; + } + for (ibit = 0; ibit < g->g_ll.ll_nlabels; ibit++) { + if (testbit(d1->d_first, ibit)) { + if (accel[ibit] != -1) + printf("XXX ambiguity!\n"); + accel[ibit] = a->a_arrow | (1 << 7) | + ((type - NT_OFFSET) << 8); + } + } + } + else if (lbl == EMPTY) + s->s_accept = 1; + else if (lbl >= 0 && lbl < nl) + accel[lbl] = a->a_arrow; + } + while (nl > 0 && accel[nl-1] == -1) + nl--; + for (k = 0; k < nl && accel[k] == -1;) + k++; + if (k < nl) { + int i; + s->s_accel = NEW(int, nl-k); + if (s->s_accel == NULL) { + fprintf(stderr, "no mem to add parser accelerators\n"); + exit(1); + } + s->s_lower = k; + s->s_upper = nl; + for (i = 0; k < nl; i++, k++) + s->s_accel[i] = accel[k]; + } + DEL(accel); +} diff --git a/src/allobjects.h b/src/allobjects.h new file mode 100644 index 0000000..b6b487b --- /dev/null +++ b/src/allobjects.h @@ -0,0 +1,50 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* "allobjects.c" -- Source for precompiled header "allobjects.h" */ + +#include <stdio.h> +#include <string.h> + +#include "PROTO.h" + +#include "object.h" +#include "objimpl.h" + +#include "intobject.h" +#include "floatobject.h" +#include "stringobject.h" +#include "tupleobject.h" +#include "listobject.h" +#include "dictobject.h" +#include "methodobject.h" +#include "moduleobject.h" +#include "funcobject.h" +#include "classobject.h" +#include "fileobject.h" + +#include "errors.h" +#include "malloc.h" + +extern char *strdup PROTO((const char *)); diff --git a/src/amoebamodule.c b/src/amoebamodule.c new file mode 100644 index 0000000..c9131b7 --- /dev/null +++ b/src/amoebamodule.c @@ -0,0 +1,774 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Amoeba module implementation */ + +/* Amoeba includes */ +#include <amoeba.h> +#include <cmdreg.h> +#include <stdcom.h> +#include <stderr.h> +#include <caplist.h> +#include <server/bullet/bullet.h> +#include <server/tod/tod.h> +#include <module/name.h> +#include <module/direct.h> +#include <module/mutex.h> +#include <module/prv.h> +#include <module/stdcmd.h> + +/* C includes */ +#include <stdlib.h> +#include <ctype.h> + +/* POSIX includes */ +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +/* Python includes */ +#include "allobjects.h" +#include "modsupport.h" +#include "sc_global.h" +#include "stubcode.h" + +extern char *err_why(); +extern char *ar_cap(); + +#define STUBCODE "+stubcode" + +static object *AmoebaError; +object *StubcodeError; + +static object *sc_dict; + +/* Module initialization */ + +extern struct methodlist amoeba_methods[]; /* Forward */ +extern object *convertcapv(); /* Forward */ + +static void +ins(d, name, v) + object *d; + char *name; + object *v; +{ + if (v == NULL || dictinsert(d, name, v) != 0) + fatal("can't initialize amoeba module"); +} + +void +initamoeba() +{ + object *m, *d, *v; + + m = initmodule("amoeba", amoeba_methods); + d = getmoduledict(m); + + /* Define capv */ + v = convertcapv(); + ins(d, "capv", v); + DECREF(v); + + /* Set timeout */ + timeout((interval)2000); + + /* Initialize amoeba.error exception */ + AmoebaError = newstringobject("amoeba.error"); + ins(d, "error", AmoebaError); + StubcodeError = newstringobject("amoeba.stubcode_error"); + ins(d, "stubcode_error", StubcodeError); + sc_dict = newdictobject(); +} + + +/* Set an Amoeba-specific error, and return NULL */ + +object * +amoeba_error(err) + errstat err; +{ + object *v = newtupleobject(2); + if (v != NULL) { + settupleitem(v, 0, newintobject((long)err)); + settupleitem(v, 1, newstringobject(err_why(err))); + } + err_setval(AmoebaError, v); + if (v != NULL) + DECREF(v); + return NULL; +} + + +/* Capability object implementation */ + +extern typeobject Captype; /* Forward */ + +#define is_capobject(v) ((v)->ob_type == &Captype) + +typedef struct { + OB_HEAD + capability ob_cap; +} capobject; + +object * +newcapobject(cap) + capability *cap; +{ + capobject *v = NEWOBJ(capobject, &Captype); + if (v == NULL) + return NULL; + v->ob_cap = *cap; + return (object *)v; +} + +getcapability(v, cap) + object *v; + capability *cap; +{ + + if (!is_capobject(v)) + return err_badarg(); + *cap = ((capobject *)v)->ob_cap; + return 0; +} + +/* + * is_capobj exports the is_capobject macro to the stubcode modules + */ + +int +is_capobj(v) + object *v; +{ + + return is_capobject(v); +} + +/* Methods */ + +static void +capprint(v, fp, flags) + capobject *v; + FILE *fp; + int flags; +{ + /* XXX needs lock when multi-threading */ + fputs(ar_cap(&v->ob_cap), fp); +} + +static object * +caprepr(v) + capobject *v; +{ + /* XXX needs lock when multi-threading */ + return newstringobject(ar_cap(&v->ob_cap)); +} + +extern object *sc_interpreter(); + +extern struct methodlist cap_methods[]; /* Forward */ + +object * +sc_makeself(cap, stubcode, name) + object *cap, *stubcode; + char *name; +{ + object *sc_name, *sc_self; + + sc_name = newstringobject(name); + if (sc_name == NULL) + return NULL; + sc_self = newtupleobject(3); + if (sc_self == NULL) { + DECREF(sc_name); + return NULL; + } + if (settupleitem(sc_self, NAME, sc_name) != 0) { + DECREF(sc_self); + return NULL; + } + INCREF(cap); + if (settupleitem(sc_self, CAP, cap) != 0) { + DECREF(sc_self); + return NULL; + } + INCREF(stubcode); + if (settupleitem(sc_self, STUBC, stubcode) != 0) { + DECREF(sc_self); + return NULL; + } + return sc_self; +} + + +static void +swapcode(code, len) + char *code; + int len; +{ + int i = sizeof(TscOperand); + TscOpcode opcode; + TscOperand operand; + + while (i < len) { + memcpy(&opcode, &code[i], sizeof(TscOpcode)); + SwapOpcode(opcode); + memcpy(&code[i], &opcode, sizeof(TscOpcode)); + i += sizeof(TscOpcode); + if (opcode & OPERAND) { + memcpy(&operand, &code[i], sizeof(TscOperand)); + SwapOperand(operand); + memcpy(&code[i], &operand, sizeof(TscOperand)); + i += sizeof(TscOperand); + } + } +} + +object * +sc_findstubcode(v, name) + object *v; + char *name; +{ + int fd, fsize; + char *fname, *buffer; + struct stat statbuf; + object *sc_stubcode, *ret; + TscOperand sc_magic; + + /* + * Only look in the current directory for now. + */ + fname = malloc(strlen(name) + 4); + if (fname == NULL) { + return err_nomem(); + } + sprintf(fname, "%s.sc", name); + if ((fd = open(fname, O_RDONLY)) == -1) { + extern int errno; + + if (errno == 2) { + /* + ** errno == 2 is file not found. + */ + err_setstr(NameError, fname); + return NULL; + } + free(fname); + return err_errno(newstringobject(name)); + } + fstat(fd, &statbuf); + fsize = (int)statbuf.st_size; + buffer = malloc(fsize); + if (buffer == NULL) { + free(fname); + close(fd); + return err_nomem(); + } + if (read(fd, buffer, fsize) != fsize) { + close(fd); + free(fname); + return err_errno(newstringobject(name)); + } + close(fd); + free(fname); + memcpy(&sc_magic, buffer, sizeof(TscOperand)); + if (sc_magic != SC_MAGIC) { + SwapOperand(sc_magic); + if (sc_magic != SC_MAGIC) { + free(buffer); + return NULL; + } else { + swapcode(buffer, fsize); + } + } + sc_stubcode = newsizedstringobject( &buffer[sizeof(TscOperand)], + fsize - sizeof(TscOperand)); + free(buffer); + if (sc_stubcode == NULL) { + return NULL; + } + if (dictinsert(sc_dict, name, sc_stubcode) != 0) { + DECREF(sc_stubcode); + return NULL; + } + DECREF(sc_stubcode); /* XXXX */ + sc_stubcode = sc_makeself(v, sc_stubcode, name); + if (sc_stubcode == NULL) { + return NULL; + } + return sc_stubcode; +} + +object * +capgetattr(v, name) + capobject *v; + char *name; +{ + object *sc_method, *sc_stubcodemethod; + + if (sc_dict == NULL) { + /* + ** For some reason the dictionary has not been + ** initialized. Try to find one of the built in + ** methods. + */ + return findmethod(cap_methods, (object *)v, name); + } + sc_stubcodemethod = dictlookup(sc_dict, name); + if (sc_stubcodemethod != NULL) { + /* + ** There is a stubcode method in the dictionary. + ** Execute the stubcode interpreter with the right + ** arguments. + */ + object *self, *ret; + + self = sc_makeself((object *)v, sc_stubcodemethod, name); + if (self == NULL) { + return NULL; + } + ret = findmethod(cap_methods, self, STUBCODE); + DECREF(self); + return ret; + } + err_clear(); + sc_method = findmethod(cap_methods, (object *)v, name); + if (sc_method == NULL) { + /* + ** The method is not built in and not in the + ** dictionary. Try to find it as a stubcode file. + */ + object *self, *ret; + + err_clear(); + self = sc_findstubcode((object *)v, name); + if (self == NULL) { + return NULL; + } + ret = findmethod(cap_methods, self, STUBCODE); + DECREF(self); + return ret; + } + return sc_method; +} + +int +capcompare(v, w) + capobject *v, *w; +{ + int cmp = bcmp((char *)&v->ob_cap.cap_port, + (char *)&w->ob_cap, PORTSIZE); + if (cmp != 0) + return cmp; + return prv_number(&v->ob_cap.cap_priv) - + prv_number(&w->ob_cap.cap_priv); +} + +static typeobject Captype = { + OB_HEAD_INIT(&Typetype) + 0, + "capability", + sizeof(capobject), + 0, + free, /*tp_dealloc*/ + capprint, /*tp_print*/ + capgetattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + capcompare, /*tp_comp +are*/ + caprepr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ +}; + + +/* Return a dictionary corresponding to capv */ + +extern struct caplist *capv; + +static object * +convertcapv() +{ + object *d; + struct caplist *c; + d = newdictobject(); + if (d == NULL) + return NULL; + if (capv == NULL) + return d; + for (c = capv; c->cl_name != NULL; c++) { + object *v = newcapobject(c->cl_cap); + if (v == NULL || dictinsert(d, c->cl_name, v) != 0) { + DECREF(d); + return NULL; + } + DECREF(v); + } + return d; +} + + +/* Strongly Amoeba-specific argument handlers */ + +static int +getcaparg(v, a) + object *v; + capability *a; +{ + if (v == NULL || !is_capobject(v)) + return err_badarg(); + *a = ((capobject *)v) -> ob_cap; + return 1; +} + +static int +getstrcapargs(v, a, b) + object *v; + object **a; + capability *b; +{ + if (v == NULL || !is_tupleobject(v) || gettuplesize(v) != 2) + return err_badarg(); + return getstrarg(gettupleitem(v, 0), a) && + getcaparg(gettupleitem(v, 1), b); +} + + +/* Amoeba methods */ + +static object * +amoeba_name_lookup(self, args) + object *self; + object *args; +{ + object *name; + capability cap; + errstat err; + if (!getstrarg(args, &name)) + return NULL; + err = name_lookup(getstringvalue(name), &cap); + if (err != STD_OK) + return amoeba_error(err); + return newcapobject(&cap); +} + +static object * +amoeba_name_append(self, args) + object *self; + object *args; +{ + object *name; + capability cap; + errstat err; + if (!getstrcapargs(args, &name, &cap)) + return NULL; + err = name_append(getstringvalue(name), &cap); + if (err != STD_OK) + return amoeba_error(err); + INCREF(None); + return None; +} + +static object * +amoeba_name_replace(self, args) + object *self; + object *args; +{ + object *name; + capability cap; + errstat err; + if (!getstrcapargs(args, &name, &cap)) + return NULL; + err = name_replace(getstringvalue(name), &cap); + if (err != STD_OK) + return amoeba_error(err); + INCREF(None); + return None; +} + +static object * +amoeba_name_delete(self, args) + object *self; + object *args; +{ + object *name; + errstat err; + if (!getstrarg(args, &name)) + return NULL; + err = name_delete(getstringvalue(name)); + if (err != STD_OK) + return amoeba_error(err); + INCREF(None); + return None; +} + +static object * +amoeba_timeout(self, args) + object *self; + object *args; +{ + int i; + object *v; + interval tout; + if (!getintarg(args, &i)) + return NULL; + tout = timeout((interval)i); + v = newintobject((long)tout); + if (v == NULL) + timeout(tout); + return v; +} + +static struct methodlist amoeba_methods[] = { + {"name_append", amoeba_name_append}, + {"name_delete", amoeba_name_delete}, + {"name_lookup", amoeba_name_lookup}, + {"name_replace", amoeba_name_replace}, + {"timeout", amoeba_timeout}, + {NULL, NULL} /* Sentinel */ +}; + +/* Capability methods */ + +static object * +cap_b_size(self, args) + capobject *self; + object *args; +{ + errstat err; + b_fsize size; + if (!getnoarg(args)) + return NULL; + err = b_size(&self->ob_cap, &size); + if (err != STD_OK) + return amoeba_error(err); + return newintobject((long)size); +} + +static object * +cap_b_read(self, args) + capobject *self; + object *args; +{ + errstat err; + char *buf; + object *v; + long offset, size; + b_fsize nread; + if (!getlonglongargs(args, &offset, &size)) + return NULL; + buf = malloc((unsigned int)size); + if (buf == NULL) { + return err_nomem(); + } + err = b_read(&self->ob_cap, (b_fsize)offset, buf, (b_fsize)size, + &nread); + if (err != STD_OK) { + free(buf); + return amoeba_error(err); + } + v = newsizedstringobject(buf, (int)nread); + free(buf); + return v; +} + +static object * +cap_dir_lookup(self, args) + capobject *self; + object *args; +{ + object *name; + capability cap; + errstat err; + if (!getstrarg(args, &name)) + return NULL; + err = dir_lookup(&self->ob_cap, getstringvalue(name), &cap); + if (err != STD_OK) + return amoeba_error(err); + return newcapobject(&cap); +} + +static object * +cap_dir_append(self, args) + capobject *self; + object *args; +{ + object *name; + capability cap; + errstat err; + if (!getstrcapargs(args, &name, &cap)) + return NULL; + err = dir_append(&self->ob_cap, getstringvalue(name), &cap); + if (err != STD_OK) + return amoeba_error(err); + INCREF(None); + return None; +} + +static object * +cap_dir_delete(self, args) + capobject *self; + object *args; +{ + object *name; + errstat err; + if (!getstrarg(args, &name)) + return NULL; + err = dir_delete(&self->ob_cap, getstringvalue(name)); + if (err != STD_OK) + return amoeba_error(err); + INCREF(None); + return None; +} + +static object * +cap_dir_replace(self, args) + capobject *self; + object *args; +{ + object *name; + capability cap; + errstat err; + if (!getstrcapargs(args, &name, &cap)) + return NULL; + err = dir_replace(&self->ob_cap, getstringvalue(name), &cap); + if (err != STD_OK) + return amoeba_error(err); + INCREF(None); + return None; +} + +static object * +cap_dir_list(self, args) + capobject *self; + object *args; +{ + errstat err; + struct dir_open *dd; + object *d; + char *name; + if (!getnoarg(args)) + return NULL; + if ((dd = dir_open(&self->ob_cap)) == NULL) + return amoeba_error(STD_COMBAD); + if ((d = newlistobject(0)) == NULL) { + dir_close(dd); + return NULL; + } + while ((name = dir_next(dd)) != NULL) { + object *v; + v = newstringobject(name); + if (v == NULL) { + DECREF(d); + d = NULL; + break; + } + if (addlistitem(d, v) != 0) { + DECREF(v); + DECREF(d); + d = NULL; + break; + } + DECREF(v); + } + dir_close(dd); + return d; +} + +object * +cap_std_info(self, args) + capobject *self; + object *args; +{ + char buf[256]; + errstat err; + int n; + if (!getnoarg(args)) + return NULL; + err = std_info(&self->ob_cap, buf, sizeof buf, &n); + if (err != STD_OK) + return amoeba_error(err); + return newsizedstringobject(buf, n); +} + +object * +cap_tod_gettime(self, args) + capobject *self; + object *args; +{ + header h; + errstat err; + bufsize n; + long sec; + int msec, tz, dst; + if (!getnoarg(args)) + return NULL; + h.h_port = self->ob_cap.cap_port; + h.h_priv = self->ob_cap.cap_priv; + h.h_command = TOD_GETTIME; + n = trans(&h, NILBUF, 0, &h, NILBUF, 0); + if (ERR_STATUS(n)) + return amoeba_error(ERR_CONVERT(n)); + tod_decode(&h, &sec, &msec, &tz, &dst); + return newintobject(sec); +} + +object * +cap_tod_settime(self, args) + capobject *self; + object *args; +{ + header h; + errstat err; + bufsize n; + long sec; + if (!getlongarg(args, &sec)) + return NULL; + h.h_port = self->ob_cap.cap_port; + h.h_priv = self->ob_cap.cap_priv; + h.h_command = TOD_SETTIME; + tod_encode(&h, sec, 0, 0, 0); + n = trans(&h, NILBUF, 0, &h, NILBUF, 0); + if (ERR_STATUS(n)) + return amoeba_error(ERR_CONVERT(n)); + INCREF(None); + return None; +} + +static struct methodlist cap_methods[] = { + { STUBCODE, sc_interpreter}, + {"b_read", cap_b_read}, + {"b_size", cap_b_size}, + {"dir_append", cap_dir_append}, + {"dir_delete", cap_dir_delete}, + {"dir_list", cap_dir_list}, + {"dir_lookup", cap_dir_lookup}, + {"dir_replace", cap_dir_replace}, + {"std_info", cap_std_info}, + {"tod_gettime", cap_tod_gettime}, + {"tod_settime", cap_tod_settime}, + {NULL, NULL} /* Sentinel */ +}; diff --git a/src/asa.c b/src/asa.c new file mode 100644 index 0000000..f7f7378 --- /dev/null +++ b/src/asa.c @@ -0,0 +1,494 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Asynchronous audio module for Silicon Graphics 4D/20 under IRIX 3.3 + Copyright 1990 Stichting Mathematisch Centrum, Amsterdam + Author: Guido van Rossum, <[email protected]> + Last modified: [email protected], Oct 14, 1990 + + Callers should #include "asa.h". + + This code is strongly IRIX 3.3 dependent. (Or are sproc() and + friends standard SYSV now?) + + Caution: if you put printf's in the slave for debugging, use "-lmpc" + to get the semaphore version of stdio! + + + This file contains two library layers and a test program: + + + The lower layer implements a simple asynchronous execution facility, + built directly on the system calls sproc() and [un]blockproc(). + + A slave thread sits in an infinite loop waiting for work assigned to + it by the master thread. Work is represented by a function pointer + and an argument of type void*. The function returns a void* pointer + which is transferred back to the master when it submits the next bit + of work. Submitting a NULL function pointer can be used by the + master to wait for completion of the previous work. This lower + layer could be generally useful, but is currently implemented by + static functions, for exclusive by the asynchronous audio layer. + + + The higher layer implements an asynchronous interface to the + /dev/audio device on the Silicon Graphics 4D/20. It defines the + following functions: + + int asa_init() + Required initialization function. The other functions will call + abort() when they are called before asa_init(). It creates the + slave process and returns a file descriptor for the audio + device which can be used to set the sampling rate and output + gain, etc. It prints a message to stderr and returns -1 if the + initialization failed. Calling this function more than onece is + harmless. + + void asa_start_read(char *buf, int len) + Starts an asynchronous read call on the audio device. This + waits for completion of the previous request, if any. + + void asa_start_write(char *buf, int len) + Starts an asynchronous write call on the audio device. This + waits for completion of the previous request, if any. + + int asa_poll() + Polls whether the last asynchronous read or write request is + finished. Returns -1 if no request was queued, 0 if the request + is not yet finished, and 1 if it is finished. + + int asa_wait() + Waits for completion if the last asynchronous read or write + request. It returns the result of the read or write request, + and sets the error code to the error code set by the request if + the result is -1. If no request was queued, this also returns + -1 but leaves the error code unchanged. Note: to get the error + code, don't inspect the global variable errno but call the + function oserror(). + + int asa_cancel() + Cancels the last asynchronous read or write request (by sending + the slave thread a signal for which it has a handler) then + returns its result and error code as asa_wait(). + + void asa_done() + Kills the slave process and closes the audio device. After + this, if further use of the module is required, asa_init() + should be called again. Calling this function when asa_init() + has not been called is harmless. + + + Finally, this file contains a simple test program that is compiled if + MAIN is defined (e.g., compile with cc -DMAIN). It makes a recording + and plays it back. The user must indicate begin and end of recording + and play-back by pressing the Return key. +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/prctl.h> + +#include "asa.h" + + +/* Asynchronous execution facility (lower layer) */ + + +/* Signal used to cancel requests in progress */ +#define MYSIG SIGUSR1 + +/* Respective process IDs */ +static pid_t master_pid = -1; +static pid_t slave_pid = -1; + +/* Work and result "queue" (1 element) */ +static void * (*work_func)(); +static void *work_arg; +static void *work_result; + +/* Signal handler for MYSIG -- interrupts read or write system call */ + +/*ARGSUSED*/ +static void +handler(sig) + int sig; +{ + /* Reinstate the handler (non-BSD signal semantics) */ + signal(sig, handler); +} + +/* Subroutine to fiddle signals */ + +static void +dosig(sig) + int sig; +{ + if (signal(sig, SIG_IGN) != SIG_IGN) + signal(sig, SIG_DFL); +} + +/* Slave control flow */ + +/*ARGSUSED*/ +static void +slave(arg) + void *arg; +{ + void * (*func)(); + void *arg; + void *result; + + /* Reset signal handlers that interactive programs often catch. + The assumption is that if the master has a handler for these + signals, it will be a cleanup function. The slave must die + from these. */ + dosig(SIGHUP); + dosig(SIGQUIT); + dosig(SIGTERM); + dosig(SIGPIPE); + + /* Ignore SIGINT if caught or ignored */ + if (signal(SIGINT, SIG_IGN) == SIG_DFL) + signal(SIGINT, SIG_DFL); + + /* Let the handler install itself */ + handler(MYSIG); + + /* Set slave_pid. This is also done in the master thread, but + there is a race condition whereby the slave begins execution + before the master has assigned the result of sproc() to + slave_pid. So we set it here as well -- since this sets the + same value it should be OK. */ + slave_pid = getpid(); + + /* Set the dummy result returned by the first rendezvous */ + result = NULL; + + /* Loop forever, waiting for and executing work */ + for (;;) { + /* First rendezvous: store previous result */ + if (blockproc(slave_pid) < 0) + perror("slave: [result] blockproc(slave_pid)"); + work_result = result; + if (unblockproc(master_pid) < 0) + perror("slave: [result] unblockproc(master_pid)"); + + /* Second rendezvous: fetch work */ + if (blockproc(slave_pid) < 0) + perror("slave: [func,arg] blockproc(slave_pid)"); + func = work_func; + arg = work_arg; + if (unblockproc(master_pid) < 0) + perror("slave: [func,arg] unblockproc(master_pid)"); + + /* Execute work, computing new result */ + if (func == NULL) { + result = arg; + } + else { + result = (*func)(arg); + } + } +} + +static int +slave_init() +{ + if (slave_pid > 0) + return slave_pid; + master_pid = getpid(); + + /* Reset the queue, in case this is a re-init after asa_done() */ + work_result = NULL; + work_func = NULL; + work_arg = NULL; + + /* Create the slave process, sharing all segments and properties */ + slave_pid = sproc(slave, PR_SALL, (char *)NULL); + if (slave_pid < 0) + perror("slave_init: sproc(slave, PR_SALL, NULL)"); + + /* Set up initial conditions---tricky! + Both the master and the slave start with one credit, since + both the result slot and the work/func slots are initially + free. + Note that we use setblockproccnt() for the master so a + possible indeterminate semaphore value caused by a previous + asa_done() at an unfortunate moment doesn't harm us. + */ + setblockproccnt(master_pid, 1); + unblockproc(slave_pid); + + return slave_pid; +} + +static void +slave_done() +{ + if (slave_pid > 0) { + if (kill(slave_pid, SIGKILL) < 0) + perror("slave_done: kill(slave_pid, SIGKILL)"); + } + slave_pid = -1; +} + +/* Queue new work and return result of previous work */ + +static void * +rendezvous(func, arg) + void * (*func)(); + void *arg; +{ + void *result; + + if (slave_pid <= 0) + abort(); /* Illegal call: not initialized properly */ + + /* First rendezvous: store new work */ + if (blockproc(master_pid) < 0) + perror("rendezvous: [func,arg] blockproc(master_pid)"); + work_func = func; + work_arg = arg; + if (unblockproc(slave_pid) < 0) + perror("rendezvous: [func,arg] unblockproc(slave_pid)"); + + /* Second rendezvous: fetch previous result */ + if (blockproc(master_pid) < 0) + perror("rendezvous: [result] blockproc(master_pid)"); + result = work_result; + if (unblockproc(slave_pid) < 0) + perror("rendezvous: [result] unblockproc(slave_pid)"); + + return result; +} + + +/* Asynchronous audio interface (higher layer) */ + + +int audio_fd = -1; /* File descriptor -- not initialized yet */ + +static struct queue { + int func; /* 0 = read, 1 = write */ + char *buf; + int len; + int result; + int error; +} queue[2]; + +static int qindex = 0; + +int +asa_init() +{ + int fd; + char *p; + + if (audio_fd >= 0) + return audio_fd; + fd = open("/dev/audio", 2); + if (fd < 0) { + perror("asa_init: Can't open /dev/audio"); + return -1; + } + if (slave_init() < 0) { + perror("asa_init: Can't create slave process"); + close(fd); + return -1; + } + audio_fd = fd; + return fd; +} + +void +asa_done() +{ + slave_done(); + if (audio_fd >= 0) { + if (close(audio_fd) < 0) + perror("asa_done: close(audio_fd)"); + } + audio_fd = -1; +} + +static void * +runjob(arg) + void *arg; +{ + extern int errno; + struct queue *q = (struct queue *)arg; + char *buf = q->buf; + int len = q->len; + int n = 0; + + if (q->func == 0) + n = read(audio_fd, buf, len); + else + n = write(audio_fd, buf, len); + if (q->func == 0 && n >= 0) { + while (--len >= n && buf[len] == '\0') + ; + n = len+1; + } + q->result = n; + q->error = oserror(); + return arg; +} + +static void +startjob(func, buf, len) + int func; + char *buf; + int len; +{ + struct queue *q; + + q = &queue[qindex]; + qindex = (qindex+1) & 1; + q->func = func; + q->buf = buf; + q->len = len; + (void) rendezvous(runjob, (void *)q); +} + +void +asa_start_read(buf, len) + char *buf; + int len; +{ + memset(buf, '\0', len); + startjob(0, buf, len); +} + +void +asa_start_write(buf, len) + char *buf; + int len; +{ + startjob(1, buf, len); +} + +int +asa_wait() +{ + struct queue *q; + + q = (struct queue *) rendezvous((void*(*)())NULL, (void *)NULL); + if (q == NULL) { + setoserror(0); + return -1; /* No work was queued */ + } + setoserror(q->error); + return q->result; +} + +int +asa_poll() +{ + int err; + + err = prctl(PR_ISBLOCKED, slave_pid); + if (err < 0) { + perror("prctl(PR_ISBLOCKED, slave_pid)"); + return -1; + } + else if (err == 0) + return 0; + else if (work_result == NULL) { + setoserror(0); + return -1; + } + else + return 1; +} + +int +asa_cancel() +{ + int result; + + kill(slave_pid, MYSIG); + result = asa_wait(); + return result; +} + + +#ifdef MAIN + +/* Test program */ + +#include <sys/audio.h> + +main() +{ + static char buf[10*16*1024]; /* 10 seconds of sound at 16K/sec */ + int n; + int afd; + + if ((afd = asa_init()) < 0) + exit(1); + ioctl(afd, AUDIOCSETRATE, 3); + ioctl(afd, AUDIOCSETOUTGAIN, 0); + printf("Poll returns %d\n", asa_poll()); + go("Hit enter to start recording:\n"); + asa_start_read(buf, sizeof buf); + go("Hit enter to stop recording:\n"); + /*printf("Poll returns %d\n", asa_poll());*/ + n = asa_cancel(); + if (n < 0) + perror("Read failed"); + else { + printf("Got %d bytes\n", n); + printf("Poll returns %d\n", asa_poll()); + go("Hit enter to start playing:\n"); + ioctl(afd, AUDIOCSETOUTGAIN, 50); + asa_start_write(buf, n); + go("Hit enter to stop playing:\n"); + printf("Poll returns %d\n", asa_poll()); + n = asa_cancel(); + if (n < 0) + perror("Write failed"); + else + printf("Stopped at %d bytes\n", n); + } + ioctl(afd, AUDIOCSETOUTGAIN, 0); + asa_done(); + exit(n < 0 ? 1 : 0); +} + +go(str) + char *str; +{ + char line[100]; + + sleep(1); + fputs(str, stdout); + fflush(stdout); + fgets(line, sizeof line, stdin); +} + +#endif /* MAIN */ diff --git a/src/asa.h b/src/asa.h new file mode 100644 index 0000000..d9a7d10 --- /dev/null +++ b/src/asa.h @@ -0,0 +1,33 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Interface for asynchronous audio module */ + +extern int asa_init(void); +extern void asa_done(void); +extern void asa_start_write(char *, int); +extern void asa_start_read(char *, int); +extern int asa_poll(void); +extern int asa_wait(void); +extern int asa_cancel(void); diff --git a/src/assert.h b/src/assert.h new file mode 100644 index 0000000..f21225e --- /dev/null +++ b/src/assert.h @@ -0,0 +1,25 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +#define assert(e) { if (!(e)) { printf("Assertion failed\n"); abort(); } } diff --git a/src/audiomodule.c b/src/audiomodule.c new file mode 100644 index 0000000..cca0102 --- /dev/null +++ b/src/audiomodule.c @@ -0,0 +1,615 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Silicon Graphics audio module implementation */ +/* For SGI Personal IRIS 4D/20 under IRIX 3.3; <sys/audio.h> mentions "IP6" */ +/* Note: The set-in-gain ioctl exists but is non-functional */ + +#include <errno.h> +#include <sys/audio.h> +#include "asa.h" + +#include "allobjects.h" +#include "modsupport.h" + +static int audio_fd = -1; + +static int +init() +{ + if (audio_fd >= 0) + return 1; + if ((audio_fd = asa_init()) >= 0) + return 1; + err_setstr(RuntimeError, "can't initialize async audio"); + return 0; +} + + +/* POSIX methods */ + +static object * +audio_get_ioctl(self, args, code) + object *self; + object *args; + long code; +{ + long x; + if (!getnoarg(args)) + return NULL; + if (!init()) + return NULL; + if ((x = ioctl(audio_fd, code, (char *) NULL)) < 0) { + return NULL; + } + return newintobject(x); +} + +static object * +audio_set_ioctl(self, args, code) + object *self; + object *args; + long code; +{ + long x; + if (!getlongarg(args, &x)) + return NULL; + if (!init()) + return NULL; + if (ioctl(audio_fd, code, (char *) x) != 0) + return NULL; + INCREF(None); + return None; +} + +static object * +audio_getingain(self, args) + object *self; + object *args; +{ + return audio_get_ioctl(self, args, AUDIOCGETINGAIN); +} + +static object * +audio_getoutgain(self, args) + object *self; + object *args; +{ + return audio_get_ioctl(self, args, AUDIOCGETOUTGAIN); +} + +static object * +audio_setingain(self, args) + object *self; + object *args; +{ + return audio_set_ioctl(self, args, AUDIOCSETINGAIN); +} + +static object * +audio_setoutgain(self, args) + object *self; + object *args; +{ + return audio_set_ioctl(self, args, AUDIOCSETOUTGAIN); +} + +static object * +audio_setrate(self, args) + object *self; + object *args; +{ + return audio_set_ioctl(self, args, AUDIOCSETRATE); +} + +static object * +audio_setduration(self, args) + object *self; + object *args; +{ + return audio_set_ioctl(self, args, AUDIOCDURATION); +} + +/* Compute average bias, and remove it */ + +static void +unbias(buf, len) + char *buf; + int len; +{ + register int i; + register int c; + register long bias; + if (len == 0) + return; + bias = 0; + for (i = 0; i < len; i++) { + c = buf[i]; + if (c > 127) + c -= 256; + bias += c; + } + bias = (bias + len/2) / len; /* Rounded average */ + if (bias != 0) { + for (i = 0; i < len; i++) { + buf[i] -= bias; + } + } +} + +static object * +audio_read(self, args) + object *self; + object *args; +{ + int c, i, n; + object *v; + char *s; + if (!getintarg(args, &n)) + return NULL; + if (n <= 0) { + err_setstr(RuntimeError, "audio.read: arg <= 0"); + return NULL; + } + if (!init()) + return NULL; + v = newsizedstringobject((char *)NULL, n); + if (v == NULL) + return err_nomem(); + s = getstringvalue(v); + n = read(audio_fd, s, n); + if (intrcheck()) { + DECREF(v); + err_set(KeyboardInterrupt); + return NULL; + } + /* Check for errors */ + if (n < 0) { + DECREF(v); + return NULL; + } + /* But EOF is reported as an empty string */ + + unbias(s, n); + resizestring(&v, n); + return v; +} + +static object * +audio_write(self, args) + object *self; + object *args; +{ + int n, n2; + object *v; + if (!getstrarg(args, &v)) + return NULL; + if (!init()) + return NULL; + errno = 0; + n2 = write(audio_fd, getstringvalue(v), n = getstringsize(v)); + if (intrcheck()) { + err_set(KeyboardInterrupt); + return NULL; + } + /* Check for other errors */ + if (n2 != n) { + if (errno == 0) + errno = EIO; + return NULL; + } + INCREF(None); + return None; +} + +/* audio.amplify(sample, f1, f2). + Amplify a sample by a factor changing from f1/256 to (almost) f2/256. + Negative factors are allowed. Sound values that are to large + to fit in a byte are clipped. */ + +static object * +audio_amplify(self, args) + object *self; + object *args; +{ + object *v; + char *s, *t; + int f1, f2; + int i, n; + int c; + if (!getstrintintarg(args, &v, &f1, &f2)) + return NULL; + n = getstringsize(v); + s = getstringvalue(v); + v = newsizedstringobject((char *)NULL, n); + if (v == NULL) + return err_nomem(); + t = getstringvalue(v); + for (i = 0; i < n; i++) { + c = s[i]; + if (c > 127) c -= 256; /* If chars are unsigned */ + c = c * ( f1*(n-i) + f2*i ) / ( n*256 ); + if (c > 127) c = 127; + else if (c < -128) c = -128; + t[i] = c; + } + return v; +} + +/* audio.reverse(s): return a sample backwards */ + +static object * +audio_reverse(self, args) + object *self; + object *args; +{ + object *v; + char *s, *t; + int i, n; + if (!getstrarg(args, &v)) + return NULL; + n = getstringsize(v); + s = getstringvalue(v); + v = newsizedstringobject((char *)NULL, n); + if (v == NULL) + return err_nomem(); + t = getstringvalue(v); + for (i = 0; i < n; i++) { + t[n-1-i] = s[i]; + } + return v; +} + +/* audio.add(a, b): add two samples. + Bytes that exceed the range are clipped. + If one is shorter, the rest of the longer sample is returned unchanged. */ + +static object * +audio_add(self, args) + object *self; + object *args; +{ + object *a, *b, *v; + char *sa, *sb, *t; + int i, n, na, nb, c, ca, cb; + if (!getstrstrarg(args, &a, &b)) + return NULL; + na = getstringsize(a); + sa = getstringvalue(a); + nb = getstringsize(b); + sb = getstringvalue(b); + n = (na > nb) ? na : nb; + v = newsizedstringobject((char *)NULL, n); + if (v == NULL) + return err_nomem(); + t = getstringvalue(v); + for (i = 0; i < n; i++) { + c = 0; + if (i < na) { + ca = sa[i]; + if (ca > 127) ca = ca - 256; + c = c + ca; + } + if (i < nb) { + cb = sb[i]; + if (cb > 127) cb = cb - 256; + c = c + cb; + } + if (c > 127) c = 127; + else if (c < -128) c = -128; + t[i] = c; + } + return v; +} + +/* audio.chr2num(s) returns a list containing the numeric values + of the samples. */ + +static object * +audio_chr2num(self, args) + object *self; + object *args; +{ + object *v, *w; + char *s; + int c, i, n; + static object *ints[256]; + + /* To avoid filling memory with all those int objects, we create + integer objects for all the desired values and reference these. */ + if (ints[255] == NULL) { + for (i = 0; i < 256; i++) { + if (ints[i] != NULL) + continue; + c = i; + if (c > 127) c -= 256; + ints[i] = newintobject((long)c); + if (ints[i] == NULL) + return NULL; + } + } + + if (!getstrarg(args, &v)) + return NULL; + n = getstringsize(v); + s = getstringvalue(v); + v = newlistobject(n); + if (v == NULL) + return err_nomem(); + for (i = 0; i < n; i++) { + c = s[i] & 0xff; + w = ints[c]; + INCREF(w); + if (setlistitem(v, i, w) != 0) { + DECREF(v); + return NULL; + } + } + return v; +} + +/* audio.num2chr is the inverse of audio.chr2num. + Excess values are clipped. */ + +static object * +audio_num2chr(self, args) + object *self; + object *args; +{ + object *v, *w; + char *s; + int c, i, n; + if (!is_listobject(args)) { + err_badarg(); + return NULL; + } + n = getlistsize(args); + v = newsizedstringobject((char *)NULL, n); + if (v == NULL) + return NULL; + s = getstringvalue(v); + for (i = 0; i < n; i++) { + w = getlistitem(args, i); + if (!is_intobject(w)) { + DECREF(v); + err_badarg(); + return NULL; + } + s[i] = getintvalue(w); + } + return v; +} + +static object *stdaudio_buffer = NULL; + +static object * +audio_start_recording(self, args) + object *self; + object *args; +{ + int n; + object *v; + char *s; + if (!getintarg(args, &n)) + return NULL; + if (stdaudio_buffer != NULL) { + err_setstr(RuntimeError, "audio.start_recording: device busy"); + return NULL; + } + if (n <= 0) { + err_setstr(TypeError, "audio.start_recording: arg <= 0"); + return NULL; + } + if (!init()) + return NULL; + v = newsizedstringobject((char *)NULL, n); + if (v == NULL) + return err_nomem(); + s = getstringvalue(v); + asa_start_read(s, n); + stdaudio_buffer = v; + INCREF(None); + return None; +} + +static object * +audio_poll(self, args) + object *self; + object *args; +{ + int n; + if (!getnoarg(args)) + return NULL; + if (stdaudio_buffer == NULL) { + err_setstr(RuntimeError, "audio.poll: not busy"); + return NULL; + } + if (!init()) + return NULL; + if ((n = asa_poll()) < 0) + return NULL; + return newintobject(n); +} + +static object * +audio_wait_recording(self, args) + object *self; + object *args; +{ + object *v; + int n; + if (!getnoarg(args)) + return NULL; + if (stdaudio_buffer == NULL) { + err_setstr(RuntimeError, "audio.wait_recording: not busy"); + return NULL; + } + if (!init()) + return NULL; + if ((n = asa_wait()) < 0) + return NULL; + v = stdaudio_buffer; + stdaudio_buffer = NULL; + unbias(getstringvalue(v), n); + resizestring(&v, n); + return v; +} + +static object * +audio_stop_recording(self, args) + object *self; + object *args; +{ + int n; + object *v; + char *s; + if (!getnoarg(args)) + return NULL; + if (stdaudio_buffer == NULL) { + err_setstr(RuntimeError, "audio.stop_recording: not busy"); + return NULL; + } + if ((n = asa_cancel()) < 0) + return NULL; + v = stdaudio_buffer; + stdaudio_buffer = NULL; + s = getstringvalue(v); + unbias(s, n); + resizestring(&v, n); + return v; +} + +static object * +audio_start_playing(self, args) + object *self; + object *args; +{ + object *v; + if (!getstrarg(args, &v)) + return NULL; + if (stdaudio_buffer != NULL) { + err_setstr(RuntimeError, "audio.start_recording: device rbusy"); + return NULL; + } + asa_start_write(getstringvalue(v), (int)getstringsize(v)); + INCREF(v); + stdaudio_buffer = v; + INCREF(None); + return None; +} + +static object * +audio_wait_playing(self, args) + object *self; + object *args; +{ + int n; + if (!getnoarg(args)) + return NULL; + if (stdaudio_buffer == NULL) { + err_setstr(RuntimeError, "audio.wait_playing: not busy"); + return NULL; + } + if ((n = asa_wait()) < 0) + return NULL; + DECREF(stdaudio_buffer); + stdaudio_buffer = NULL; + /* XXX return newintobject((long)n); ??? */ + INCREF(None); + return None; +} + +static object * +audio_stop_playing(self, args) + object *self; + object *args; +{ + int n; + if (!getnoarg(args)) + return NULL; + if (stdaudio_buffer == NULL) { + err_setstr(RuntimeError, "audio.stop_playing: not busy"); + return NULL; + } + if ((n = asa_cancel()) < 0) + return NULL; + DECREF(stdaudio_buffer); + stdaudio_buffer = NULL; + return newintobject((long)n); +} + +static object * +audio_audio_done(self, args) + object *self; + object *args; +{ + if (!getnoarg(args)) + return NULL; + asa_done(); + if (stdaudio_buffer != NULL) + DECREF(stdaudio_buffer); + stdaudio_buffer = NULL; + audio_fd = -1; + INCREF(None); + return None; +} + + +static struct methodlist audio_methods[] = { + {"getingain", audio_getingain}, + {"getoutgain", audio_getoutgain}, + {"setingain", audio_setingain}, + {"setoutgain", audio_setoutgain}, + {"setrate", audio_setrate}, + {"setduration", audio_setduration}, + {"read", audio_read}, + {"write", audio_write}, + {"amplify", audio_amplify}, + {"reverse", audio_reverse}, + {"add", audio_add}, + {"chr2num", audio_chr2num}, + {"num2chr", audio_num2chr}, + + /* "asa" interface: */ + + {"start_recording", audio_start_recording}, + {"poll_recording", audio_poll}, + {"wait_recording", audio_wait_recording}, + {"stop_recording", audio_stop_recording}, + + {"start_playing", audio_start_playing}, + {"poll_playing", audio_poll}, + {"wait_playing", audio_wait_playing}, + {"stop_playing", audio_stop_playing}, + + {"done", audio_audio_done}, + + {NULL, NULL} /* Sentinel */ +}; + +void +initaudio() +{ + initmodule("audio", audio_methods); +} diff --git a/src/bitset.c b/src/bitset.c new file mode 100644 index 0000000..c08dcc7 --- /dev/null +++ b/src/bitset.c @@ -0,0 +1,99 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Bitset primitives used by the parser generator */ + +#include "pgenheaders.h" +#include "bitset.h" + +bitset +newbitset(nbits) + int nbits; +{ + int nbytes = NBYTES(nbits); + bitset ss = NEW(BYTE, nbytes); + + if (ss == NULL) + fatal("no mem for bitset"); + + ss += nbytes; + while (--nbytes >= 0) + *--ss = 0; + return ss; +} + +void +delbitset(ss) + bitset ss; +{ + DEL(ss); +} + +int +addbit(ss, ibit) + bitset ss; + int ibit; +{ + int ibyte = BIT2BYTE(ibit); + BYTE mask = BIT2MASK(ibit); + + if (ss[ibyte] & mask) + return 0; /* Bit already set */ + ss[ibyte] |= mask; + return 1; +} + +#if 0 /* Now a macro */ +int +testbit(ss, ibit) + bitset ss; + int ibit; +{ + return (ss[BIT2BYTE(ibit)] & BIT2MASK(ibit)) != 0; +} +#endif + +int +samebitset(ss1, ss2, nbits) + bitset ss1, ss2; + int nbits; +{ + int i; + + for (i = NBYTES(nbits); --i >= 0; ) + if (*ss1++ != *ss2++) + return 0; + return 1; +} + +void +mergebitset(ss1, ss2, nbits) + bitset ss1, ss2; + int nbits; +{ + int i; + + for (i = NBYTES(nbits); --i >= 0; ) + *ss1++ |= *ss2++; +} diff --git a/src/bitset.h b/src/bitset.h new file mode 100644 index 0000000..5ec6882 --- /dev/null +++ b/src/bitset.h @@ -0,0 +1,46 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Bitset interface */ + +#define BYTE char + +typedef BYTE *bitset; + +bitset newbitset PROTO((int nbits)); +void delbitset PROTO((bitset bs)); +/* int testbit PROTO((bitset bs, int ibit)); /* Now a macro, see below */ +int addbit PROTO((bitset bs, int ibit)); /* Returns 0 if already set */ +int samebitset PROTO((bitset bs1, bitset bs2, int nbits)); +void mergebitset PROTO((bitset bs1, bitset bs2, int nbits)); + +#define BITSPERBYTE (8*sizeof(BYTE)) +#define NBYTES(nbits) (((nbits) + BITSPERBYTE - 1) / BITSPERBYTE) + +#define BIT2BYTE(ibit) ((ibit) / BITSPERBYTE) +#define BIT2SHIFT(ibit) ((ibit) % BITSPERBYTE) +#define BIT2MASK(ibit) (1 << BIT2SHIFT(ibit)) +#define BYTE2BIT(ibyte) ((ibyte) * BITSPERBYTE) + +#define testbit(ss, ibit) (((ss)[BIT2BYTE(ibit)] & BIT2MASK(ibit)) != 0) diff --git a/src/bltinmodule.c b/src/bltinmodule.c new file mode 100644 index 0000000..151ba86 --- /dev/null +++ b/src/bltinmodule.c @@ -0,0 +1,559 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Built-in functions */ + +#include "allobjects.h" + +#include "node.h" +#include "graminit.h" +#include "errcode.h" +#include "sysmodule.h" +#include "bltinmodule.h" +#include "import.h" +#include "pythonrun.h" +#include "compile.h" /* For ceval.h */ +#include "ceval.h" +#include "modsupport.h" + +static object * +builtin_abs(self, v) + object *self; + object *v; +{ + /* XXX This should be a method in the as_number struct in the type */ + if (v == NULL) { + /* */ + } + else if (is_intobject(v)) { + long x = getintvalue(v); + if (x < 0) + x = -x; + return newintobject(x); + } + else if (is_floatobject(v)) { + double x = getfloatvalue(v); + if (x < 0) + x = -x; + return newfloatobject(x); + } + err_setstr(TypeError, "abs() argument must be float or int"); + return NULL; +} + +static object * +builtin_chr(self, v) + object *self; + object *v; +{ + long x; + char s[1]; + if (v == NULL || !is_intobject(v)) { + err_setstr(TypeError, "chr() must have int argument"); + return NULL; + } + x = getintvalue(v); + if (x < 0 || x >= 256) { + err_setstr(RuntimeError, "chr() arg not in range(256)"); + return NULL; + } + s[0] = x; + return newsizedstringobject(s, 1); +} + +static object * +builtin_dir(self, v) + object *self; + object *v; +{ + object *d; + if (v == NULL) { + d = getlocals(); + } + else { + if (!is_moduleobject(v)) { + err_setstr(TypeError, + "dir() argument, must be module or absent"); + return NULL; + } + d = getmoduledict(v); + } + v = getdictkeys(d); + if (sortlist(v) != 0) { + DECREF(v); + v = NULL; + } + return v; +} + +static object * +builtin_divmod(self, v) + object *self; + object *v; +{ + object *x, *y; + long xi, yi, xdivy, xmody; + if (v == NULL || !is_tupleobject(v) || gettuplesize(v) != 2) { + err_setstr(TypeError, "divmod() requires 2 int arguments"); + return NULL; + } + x = gettupleitem(v, 0); + y = gettupleitem(v, 1); + if (!is_intobject(x) || !is_intobject(y)) { + err_setstr(TypeError, "divmod() requires 2 int arguments"); + return NULL; + } + xi = getintvalue(x); + yi = getintvalue(y); + if (yi == 0) { + err_setstr(TypeError, "divmod() division by zero"); + return NULL; + } + if (yi < 0) { + xdivy = -xi / -yi; + } + else { + xdivy = xi / yi; + } + xmody = xi - xdivy*yi; + if (xmody < 0 && yi > 0 || xmody > 0 && yi < 0) { + xmody += yi; + xdivy -= 1; + } + v = newtupleobject(2); + x = newintobject(xdivy); + y = newintobject(xmody); + if (v == NULL || x == NULL || y == NULL || + settupleitem(v, 0, x) != 0 || + settupleitem(v, 1, y) != 0) { + XDECREF(v); + XDECREF(x); + XDECREF(y); + return NULL; + } + return v; +} + +static object * +exec_eval(v, start) + object *v; + int start; +{ + object *str = NULL, *globals = NULL, *locals = NULL; + int n; + if (v != NULL) { + if (is_stringobject(v)) + str = v; + else if (is_tupleobject(v) && + ((n = gettuplesize(v)) == 2 || n == 3)) { + str = gettupleitem(v, 0); + globals = gettupleitem(v, 1); + if (n == 3) + locals = gettupleitem(v, 2); + } + } + if (str == NULL || !is_stringobject(str) || + globals != NULL && !is_dictobject(globals) || + locals != NULL && !is_dictobject(locals)) { + err_setstr(TypeError, + "exec/eval arguments must be string[,dict[,dict]]"); + return NULL; + } + return run_string(getstringvalue(str), start, globals, locals); +} + +static object * +builtin_eval(self, v) + object *self; + object *v; +{ + return exec_eval(v, eval_input); +} + +static object * +builtin_exec(self, v) + object *self; + object *v; +{ + return exec_eval(v, file_input); +} + +static object * +builtin_float(self, v) + object *self; + object *v; +{ + if (v == NULL) { + /* */ + } + else if (is_floatobject(v)) { + INCREF(v); + return v; + } + else if (is_intobject(v)) { + long x = getintvalue(v); + return newfloatobject((double)x); + } + err_setstr(TypeError, "float() argument must be float or int"); + return NULL; +} + +static object * +builtin_input(self, v) + object *self; + object *v; +{ + FILE *in = sysgetfile("stdin", stdin); + FILE *out = sysgetfile("stdout", stdout); + node *n; + int err; + object *m, *d; + flushline(); + if (v != NULL) + printobject(v, out, PRINT_RAW); + m = add_module("__main__"); + d = getmoduledict(m); + return run_file(in, "<stdin>", expr_input, d, d); +} + +static object * +builtin_int(self, v) + object *self; + object *v; +{ + if (v == NULL) { + /* */ + } + else if (is_intobject(v)) { + INCREF(v); + return v; + } + else if (is_floatobject(v)) { + double x = getfloatvalue(v); + return newintobject((long)x); + } + err_setstr(TypeError, "int() argument must be float or int"); + return NULL; +} + +static object * +builtin_len(self, v) + object *self; + object *v; +{ + long len; + typeobject *tp; + if (v == NULL) { + err_setstr(TypeError, "len() without argument"); + return NULL; + } + tp = v->ob_type; + if (tp->tp_as_sequence != NULL) { + len = (*tp->tp_as_sequence->sq_length)(v); + } + else if (tp->tp_as_mapping != NULL) { + len = (*tp->tp_as_mapping->mp_length)(v); + } + else { + err_setstr(TypeError, "len() of unsized object"); + return NULL; + } + return newintobject(len); +} + +static object * +min_max(v, sign) + object *v; + int sign; +{ + int i, n, cmp; + object *w, *x; + sequence_methods *sq; + if (v == NULL) { + err_setstr(TypeError, "min() or max() without argument"); + return NULL; + } + sq = v->ob_type->tp_as_sequence; + if (sq == NULL) { + err_setstr(TypeError, "min() or max() of non-sequence"); + return NULL; + } + n = (*sq->sq_length)(v); + if (n == 0) { + err_setstr(RuntimeError, "min() or max() of empty sequence"); + return NULL; + } + w = (*sq->sq_item)(v, 0); /* Implies INCREF */ + for (i = 1; i < n; i++) { + x = (*sq->sq_item)(v, i); /* Implies INCREF */ + cmp = cmpobject(x, w); + if (cmp * sign > 0) { + DECREF(w); + w = x; + } + else + DECREF(x); + } + return w; +} + +static object * +builtin_min(self, v) + object *self; + object *v; +{ + return min_max(v, -1); +} + +static object * +builtin_max(self, v) + object *self; + object *v; +{ + return min_max(v, 1); +} + +static object * +builtin_open(self, v) + object *self; + object *v; +{ + object *name, *mode; + if (v == NULL || !is_tupleobject(v) || gettuplesize(v) != 2 || + !is_stringobject(name = gettupleitem(v, 0)) || + !is_stringobject(mode = gettupleitem(v, 1))) { + err_setstr(TypeError, "open() requires 2 string arguments"); + return NULL; + } + v = newfileobject(getstringvalue(name), getstringvalue(mode)); + return v; +} + +static object * +builtin_ord(self, v) + object *self; + object *v; +{ + if (v == NULL || !is_stringobject(v)) { + err_setstr(TypeError, "ord() must have string argument"); + return NULL; + } + if (getstringsize(v) != 1) { + err_setstr(RuntimeError, "ord() arg must have length 1"); + return NULL; + } + return newintobject((long)(getstringvalue(v)[0] & 0xff)); +} + +static object * +builtin_range(self, v) + object *self; + object *v; +{ + static char *errmsg = "range() requires 1-3 int arguments"; + int i, n; + long ilow, ihigh, istep; + if (v != NULL && is_intobject(v)) { + ilow = 0; ihigh = getintvalue(v); istep = 1; + } + else if (v == NULL || !is_tupleobject(v)) { + err_setstr(TypeError, errmsg); + return NULL; + } + else { + n = gettuplesize(v); + if (n < 1 || n > 3) { + err_setstr(TypeError, errmsg); + return NULL; + } + for (i = 0; i < n; i++) { + if (!is_intobject(gettupleitem(v, i))) { + err_setstr(TypeError, errmsg); + return NULL; + } + } + if (n == 3) { + istep = getintvalue(gettupleitem(v, 2)); + --n; + } + else + istep = 1; + ihigh = getintvalue(gettupleitem(v, --n)); + if (n > 0) + ilow = getintvalue(gettupleitem(v, 0)); + else + ilow = 0; + } + if (istep == 0) { + err_setstr(RuntimeError, "zero step for range()"); + return NULL; + } + /* XXX ought to check overflow of subtraction */ + if (istep > 0) + n = (ihigh - ilow + istep - 1) / istep; + else + n = (ihigh - ilow + istep + 1) / istep; + if (n < 0) + n = 0; + v = newlistobject(n); + if (v == NULL) + return NULL; + for (i = 0; i < n; i++) { + object *w = newintobject(ilow); + if (w == NULL) { + DECREF(v); + return NULL; + } + setlistitem(v, i, w); + ilow += istep; + } + return v; +} + +static object * +builtin_raw_input(self, v) + object *self; + object *v; +{ + FILE *in = sysgetfile("stdin", stdin); + FILE *out = sysgetfile("stdout", stdout); + char *p; + int err; + int n = 1000; + flushline(); + if (v != NULL) + printobject(v, out, PRINT_RAW); + v = newsizedstringobject((char *)NULL, n); + if (v != NULL) { + if ((err = fgets_intr(getstringvalue(v), n+1, in)) != E_OK) { + err_input(err); + DECREF(v); + return NULL; + } + else { + n = strlen(getstringvalue(v)); + if (n > 0 && getstringvalue(v)[n-1] == '\n') + n--; + resizestring(&v, n); + } + } + return v; +} + +static object * +builtin_reload(self, v) + object *self; + object *v; +{ + return reload_module(v); +} + +static object * +builtin_type(self, v) + object *self; + object *v; +{ + if (v == NULL) { + err_setstr(TypeError, "type() requres an argument"); + return NULL; + } + v = (object *)v->ob_type; + INCREF(v); + return v; +} + +static struct methodlist builtin_methods[] = { + {"abs", builtin_abs}, + {"chr", builtin_chr}, + {"dir", builtin_dir}, + {"divmod", builtin_divmod}, + {"eval", builtin_eval}, + {"exec", builtin_exec}, + {"float", builtin_float}, + {"input", builtin_input}, + {"int", builtin_int}, + {"len", builtin_len}, + {"max", builtin_max}, + {"min", builtin_min}, + {"open", builtin_open}, /* XXX move to OS module */ + {"ord", builtin_ord}, + {"range", builtin_range}, + {"raw_input", builtin_raw_input}, + {"reload", builtin_reload}, + {"type", builtin_type}, + {NULL, NULL}, +}; + +static object *builtin_dict; + +object * +getbuiltin(name) + char *name; +{ + return dictlookup(builtin_dict, name); +} + +/* Predefined exceptions */ + +object *RuntimeError; +object *EOFError; +object *TypeError; +object *MemoryError; +object *NameError; +object *SystemError; +object *KeyboardInterrupt; + +static object * +newstdexception(name, message) + char *name, *message; +{ + object *v = newstringobject(message); + if (v == NULL || dictinsert(builtin_dict, name, v) != 0) + fatal("no mem for new standard exception"); + return v; +} + +static void +initerrors() +{ + RuntimeError = newstdexception("RuntimeError", "run-time error"); + EOFError = newstdexception("EOFError", "end-of-file read"); + TypeError = newstdexception("TypeError", "type error"); + MemoryError = newstdexception("MemoryError", "out of memory"); + NameError = newstdexception("NameError", "undefined name"); + SystemError = newstdexception("SystemError", "system error"); + KeyboardInterrupt = + newstdexception("KeyboardInterrupt", "keyboard interrupt"); +} + +void +initbuiltin() +{ + object *m; + m = initmodule("builtin", builtin_methods); + builtin_dict = getmoduledict(m); + INCREF(builtin_dict); + initerrors(); + (void) dictinsert(builtin_dict, "None", None); +} diff --git a/src/bltinmodule.h b/src/bltinmodule.h new file mode 100644 index 0000000..fe16907 --- /dev/null +++ b/src/bltinmodule.h @@ -0,0 +1,27 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Built-in module interface */ + +extern object *getbuiltin PROTO((char *)); diff --git a/src/ceval.c b/src/ceval.c new file mode 100644 index 0000000..6876208 --- /dev/null +++ b/src/ceval.c @@ -0,0 +1,1436 @@ +/*********************************************************** +Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Execute compiled code */ + +#include "allobjects.h" + +#include "import.h" +#include "sysmodule.h" +#include "compile.h" +#include "frameobject.h" +#include "ceval.h" +#include "opcode.h" +#include "bltinmodule.h" +#include "traceback.h" + +#ifndef NDEBUG +#define TRACE +#endif + +#ifdef TRACE +static int +prtrace(v, str) + object *v; + char *str; +{ + printf("%s ", str); + printobject(v, stdout, 0); + printf("\n"); +} +#endif + +static frameobject *current_frame; + +object * +getlocals() +{ + if (current_frame == NULL) + return NULL; + else + return current_frame->f_locals; +} + +object * +getglobals() +{ + if (current_frame == NULL) + return NULL; + else + return current_frame->f_globals; +} + +void +printtraceback(fp) + FILE *fp; +{ + object *v = tb_fetch(); + if (v != NULL) { + fprintf(fp, "Stack backtrace (innermost last):\n"); + tb_print(v, fp); + DECREF(v); + } +} + + +/* XXX Mixing "print ...," and direct file I/O on stdin/stdout + XXX has some bad consequences. The needspace flag should + XXX really be part of the file object. */ + +static int needspace; + +void +flushline() +{ + FILE *fp = sysgetfile("stdout", stdout); + if (needspace) { + fprintf(fp, "\n"); + needspace = 0; + } +} + + +/* Test a value used as condition, e.g., in a for or if statement */ + +static int +testbool(v) + object *v; +{ + if (is_intobject(v)) + return getintvalue(v) != 0; + if (is_floatobject(v)) + return getfloatvalue(v) != 0.0; + if (v->ob_type->tp_as_sequence != NULL) + return (*v->ob_type->tp_as_sequence->sq_length)(v) != 0; + if (v->ob_type->tp_as_mapping != NULL) + return (*v->ob_type->tp_as_mapping->mp_length)(v) != 0; + if (v == None) + return 0; + /* All other objects are 'true' */ + return 1; +} + +static object * +add(v, w) + object *v, *w; +{ + if (v->ob_type->tp_as_number != NULL) + v = (*v->ob_type->tp_as_number->nb_add)(v, w); + else if (v->ob_type->tp_as_sequence != NULL) + v = (*v->ob_type->tp_as_sequence->sq_concat)(v, w); + else { + err_setstr(TypeError, "+ not supported by operands"); + return NULL; + } + return v; +} + +static object * +sub(v, w) + object *v, *w; +{ + if (v->ob_type->tp_as_number != NULL) + return (*v->ob_type->tp_as_number->nb_subtract)(v, w); + err_setstr(TypeError, "bad operand type(s) for -"); + return NULL; +} + +static object * +mul(v, w) + object *v, *w; +{ + typeobject *tp; + if (is_intobject(v) && w->ob_type->tp_as_sequence != NULL) { + /* int*sequence -- swap v and w */ + object *tmp = v; + v = w; + w = tmp; + } + tp = v->ob_type; + if (tp->tp_as_number != NULL) + return (*tp->tp_as_number->nb_multiply)(v, w); + if (tp->tp_as_sequence != NULL) { + if (!is_intobject(w)) { + err_setstr(TypeError, + "can't multiply sequence with non-int"); + return NULL; + } + if (tp->tp_as_sequence->sq_repeat == NULL) { + err_setstr(TypeError, "sequence does not support *"); + return NULL; + } + return (*tp->tp_as_sequence->sq_repeat) + (v, (int)getintvalue(w)); + } + err_setstr(TypeError, "bad operand type(s) for *"); + return NULL; +} + +static object * +divide(v, w) + object *v, *w; +{ + if (v->ob_type->tp_as_number != NULL) + return (*v->ob_type->tp_as_number->nb_divide)(v, w); + err_setstr(TypeError, "bad operand type(s) for /"); + return NULL; +} + +static object * +rem(v, w) + object *v, *w; +{ + if (v->ob_type->tp_as_number != NULL) + return (*v->ob_type->tp_as_number->nb_remainder)(v, w); + err_setstr(TypeError, "bad operand type(s) for %"); + return NULL; +} + +static object * +neg(v) + object *v; +{ + if (v->ob_type->tp_as_number != NULL) + return (*v->ob_type->tp_as_number->nb_negative)(v); + err_setstr(TypeError, "bad operand type(s) for unary -"); + return NULL; +} + +static object * +pos(v) + object *v; +{ + if (v->ob_type->tp_as_number != NULL) + return (*v->ob_type->tp_as_number->nb_positive)(v); + err_setstr(TypeError, "bad operand type(s) for unary +"); + return NULL; +} + +static object * +not(v) + object *v; +{ + int outcome = testbool(v); + object *w = outcome == 0 ? True : False; + INCREF(w); + return w; +} + +static object * +call_builtin(func, arg) + object *func; + object *arg; +{ + if (is_methodobject(func)) { + method meth = getmethod(func); + object *self = getself(func); + return (*meth)(self, arg); + } + if (is_classobject(func)) { + if (arg != NULL) { + err_setstr(TypeError, + "classobject() allows no arguments"); + return NULL; + } + return newclassmemberobject(func); + } + err_setstr(TypeError, "call of non-function"); + return NULL; +} + +static object * +call_function(func, arg) + object *func; + object *arg; +{ + object *newarg = NULL; + object *newlocals, *newglobals; + object *co, *v; + + if (is_classmethodobject(func)) { + object *self = classmethodgetself(func); + func = classmethodgetfunc(func); + if (arg == NULL) { + arg = self; + } + else { + newarg = newtupleobject(2); + if (newarg == NULL) + return NULL; + INCREF(self); + INCREF(arg); + settupleitem(newarg, 0, self); + settupleitem(newarg, 1, arg); + arg = newarg; + } + } + else { + if (!is_funcobject(func)) { + err_setstr(TypeError, "call of non-function"); + return NULL; + } + } + + co = getfunccode(func); + if (co == NULL) { + XDECREF(newarg); + return NULL; + } + if (!is_codeobject(co)) { + fprintf(stderr, "XXX Bad code\n"); + abort(); + } + newlocals = newdictobject(); + if (newlocals == NULL) { + XDECREF(newarg); + return NULL; + } + + newglobals = getfuncglobals(func); + INCREF(newglobals); + + v = eval_code((codeobject *)co, newglobals, newlocals, arg); + + DECREF(newlocals); + DECREF(newglobals); + + XDECREF(newarg); + + return v; +} + +static object * +apply_subscript(v, w) + object *v, *w; +{ + typeobject *tp = v->ob_type; + if (tp->tp_as_sequence == NULL && tp->tp_as_mapping == NULL) { + err_setstr(TypeError, "unsubscriptable object"); + return NULL; + } + if (tp->tp_as_sequence != NULL) { + int i; + if (!is_intobject(w)) { + err_setstr(TypeError, "sequence subscript not int"); + return NULL; + } + i = getintvalue(w); + return (*tp->tp_as_sequence->sq_item)(v, i); + } + return (*tp->tp_as_mapping->mp_subscript)(v, w); +} + +static object * +loop_subscript(v, w) + object *v, *w; +{ + sequence_methods *sq = v->ob_type->tp_as_sequence; + int i, n; + if (sq == NULL) { + err_setstr(TypeError, "loop over non-sequence"); + return NULL; + } + i = getintvalue(w); + n = (*sq->sq_length)(v); + if (i >= n) + return NULL; /* End of loop */ + return (*sq->sq_item)(v, i); +} + +static int +slice_index(v, isize, pi) + object *v; + int isize; + int *pi; +{ + if (v != NULL) { + if (!is_intobject(v)) { + err_setstr(TypeError, "slice index must be int"); + return -1; + } + *pi = getintvalue(v); + if (*pi < 0) + *pi += isize; + } + return 0; +} + +static object * +apply_slice(u, v, w) /* return u[v:w] */ + object *u, *v, *w; +{ + typeobject *tp = u->ob_type; + int ilow, ihigh, isize; + if (tp->tp_as_sequence == NULL) { + err_setstr(TypeError, "only sequences can be sliced"); + return NULL; + } + ilow = 0; + isize = ihigh = (*tp->tp_as_sequence->sq_length)(u); + if (slice_index(v, isize, &ilow) != 0) + return NULL; + if (slice_index(w, isize, &ihigh) != 0) + return NULL; + return (*tp->tp_as_sequence->sq_slice)(u, ilow, ihigh); +} + +static int +assign_subscript(w, key, v) /* w[key] = v */ + object *w; + object *key; + object *v; +{ + typeobject *tp = w->ob_type; + sequence_methods *sq; + mapping_methods *mp; + int (*func)(); + if ((sq = tp->tp_as_sequence) != NULL && + (func = sq->sq_ass_item) != NULL) { + if (!is_intobject(key)) { + err_setstr(TypeError, + "sequence subscript must be integer"); + return -1; + } + else + return (*func)(w, (int)getintvalue(key), v); + } + else if ((mp = tp->tp_as_mapping) != NULL && + (func = mp->mp_ass_subscript) != NULL) { + return (*func)(w, key, v); + } + else { + err_setstr(TypeError, + "can't assign to this subscripted object"); + return -1; + } +} + +static int +assign_slice(u, v, w, x) /* u[v:w] = x */ + object *u, *v, *w, *x; +{ + sequence_methods *sq = u->ob_type->tp_as_sequence; + int ilow, ihigh, isize; + if (sq == NULL) { + err_setstr(TypeError, "assign to slice of non-sequence"); + return -1; + } + if (sq == NULL || sq->sq_ass_slice == NULL) { + err_setstr(TypeError, "unassignable slice"); + return -1; + } + ilow = 0; + isize = ihigh = (*sq->sq_length)(u); + if (slice_index(v, isize, &ilow) != 0) + return -1; + if (slice_index(w, isize, &ihigh) != 0) + return -1; + return (*sq->sq_ass_slice)(u, ilow, ihigh, x); +} + +static int +cmp_exception(err, v) + object *err, *v; +{ + if (is_tupleobject(v)) { + int i, n; + n = gettuplesize(v); + for (i = 0; i < n; i++) { + if (err == gettupleitem(v, i)) + return 1; + } + return 0; + } + return err == v; +} + +static int +cmp_member(v, w) + object *v, *w; +{ + int i, n, cmp; + object *x; + sequence_methods *sq; + /* Special case for char in string */ + if (is_stringobject(w)) { + register char *s, *end; + register char c; + if (!is_stringobject(v) || getstringsize(v) != 1) { + err_setstr(TypeError, + "string member test needs char left operand"); + return -1; + } + c = getstringvalue(v)[0]; + s = getstringvalue(w); + end = s + getstringsize(w); + while (s < end) { + if (c == *s++) + return 1; + } + return 0; + } + sq = w->ob_type->tp_as_sequence; + if (sq == NULL) { + err_setstr(TypeError, + "'in' or 'not in' needs sequence right argument"); + return -1; + } + n = (*sq->sq_length)(w); + for (i = 0; i < n; i++) { + x = (*sq->sq_item)(w, i); + cmp = cmpobject(v, x); + XDECREF(x); + if (cmp == 0) + return 1; + } + return 0; +} + +static object * +cmp_outcome(op, v, w) + enum cmp_op op; + register object *v; + register object *w; +{ + register int cmp; + register int res = 0; + switch (op) { + case IS: + case IS_NOT: + res = (v == w); + if (op == IS_NOT) + res = !res; + break; + case IN: + case NOT_IN: + res = cmp_member(v, w); + if (res < 0) + return NULL; + if (op == NOT_IN) + res = !res; + break; + case EXC_MATCH: + res = cmp_exception(v, w); + break; + default: + cmp = cmpobject(v, w); + switch (op) { + case LT: res = cmp < 0; break; + case LE: res = cmp <= 0; break; + case EQ: res = cmp == 0; break; + case NE: res = cmp != 0; break; + case GT: res = cmp > 0; break; + case GE: res = cmp >= 0; break; + /* XXX no default? (res is initialized to 0 though) */ + } + } + v = res ? True : False; + INCREF(v); + return v; +} + +static int +import_from(locals, v, name) + object *locals; + object *v; + char *name; +{ + object *w, *x; + w = getmoduledict(v); + if (name[0] == '*') { + int i; + int n = getdictsize(w); + for (i = 0; i < n; i++) { + name = getdictkey(w, i); + if (name == NULL || name[0] == '_') + continue; + x = dictlookup(w, name); + if (x == NULL) { + /* XXX can't happen? */ + err_setstr(NameError, name); + return -1; + } + if (dictinsert(locals, name, x) != 0) + return -1; + } + return 0; + } + else { + x = dictlookup(w, name); + if (x == NULL) { + err_setstr(NameError, name); + return -1; + } + else + return dictinsert(locals, name, x); + } +} + +static object * +build_class(v, w) + object *v; /* None or tuple containing base classes */ + object *w; /* dictionary */ +{ + if (is_tupleobject(v)) { + int i; + for (i = gettuplesize(v); --i >= 0; ) { + object *x = gettupleitem(v, i); + if (!is_classobject(x)) { + err_setstr(TypeError, + "base is not a class object"); + return NULL; + } + } + } + else { + v = NULL; + } + if (!is_dictobject(w)) { + err_setstr(SystemError, "build_class with non-dictionary"); + return NULL; + } + return newclassobject(v, w); +} + + +/* Status code for main loop (reason for stack unwind) */ + +enum why_code { + WHY_NOT, /* No error */ + WHY_EXCEPTION, /* Exception occurred */ + WHY_RERAISE, /* Exception re-raised by 'finally' */ + WHY_RETURN, /* 'return' statement */ + WHY_BREAK /* 'break' statement */ +}; + +/* Interpreter main loop */ + +object * +eval_code(co, globals, locals, arg) + codeobject *co; + object *globals; + object *locals; + object *arg; +{ + register unsigned char *next_instr; + register int opcode; /* Current opcode */ + register int oparg; /* Current opcode argument, if any */ + register object **stack_pointer; + register enum why_code why; /* Reason for block stack unwind */ + register int err; /* Error status -- nonzero if error */ + register object *x; /* Result object -- NULL if error */ + register object *v; /* Temporary objects popped off stack */ + register object *w; + register object *u; + register object *t; + register frameobject *f; /* Current frame */ + int lineno; /* Current line number */ + object *retval; /* Return value iff why == WHY_RETURN */ + char *name; /* Name used by some instructions */ + FILE *fp; /* Used by print operations */ +#ifdef TRACE + int trace = dictlookup(globals, "__trace__") != NULL; +#endif + +/* Code access macros */ + +#define GETCONST(i) Getconst(f, i) +#define GETNAME(i) Getname(f, i) +#define FIRST_INSTR() (GETUSTRINGVALUE(f->f_code->co_code)) +#define INSTR_OFFSET() (next_instr - FIRST_INSTR()) +#define NEXTOP() (*next_instr++) +#define NEXTARG() (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2]) +#define JUMPTO(x) (next_instr = FIRST_INSTR() + (x)) +#define JUMPBY(x) (next_instr += (x)) + +/* Stack manipulation macros */ + +#define STACK_LEVEL() (stack_pointer - f->f_valuestack) +#define EMPTY() (STACK_LEVEL() == 0) +#define TOP() (stack_pointer[-1]) +#define BASIC_PUSH(v) (*stack_pointer++ = (v)) +#define BASIC_POP() (*--stack_pointer) + +#ifdef TRACE +#define PUSH(v) (BASIC_PUSH(v), trace && prtrace(TOP(), "push")) +#define POP() (trace && prtrace(TOP(), "pop"), BASIC_POP()) +#else +#define PUSH(v) BASIC_PUSH(v) +#define POP() BASIC_POP() +#endif + + f = newframeobject( + current_frame, /*back*/ + co, /*code*/ + globals, /*globals*/ + locals, /*locals*/ + 50, /*nvalues*/ + 20); /*nblocks*/ + if (f == NULL) + return NULL; + + current_frame = f; + + next_instr = GETUSTRINGVALUE(f->f_code->co_code); + + stack_pointer = f->f_valuestack; + + if (arg != NULL) { + INCREF(arg); + PUSH(arg); + } + + why = WHY_NOT; + err = 0; + x = None; /* Not a reference, just anything non-NULL */ + lineno = -1; + + for (;;) { + static ticker; + + /* Do periodic things */ + + if (--ticker < 0) { + ticker = 10 |
