diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..ae62d97 --- /dev/null +++ b/COPYING @@ -0,0 +1,61 @@ +GPLv3 for all my stuff (everything but ocs and cdb.ml). + +------------------------------------------------------------------------ + +ocs was downloaded from and came with +the following COPYING file. + + +Copyright (c) 2003-2004 Ville-Pertti Keinonen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +------------------------------------------------------------------------ + +cdb.ml was downloaded from +. It came with the +following text in the COPYING file. Dustin, if you're reading this, +OCaml is not Irish. + + +Copyright (c) 2004 by Dustin Sallings + +The package "Dustin's O'Caml lib" is copyright by Dustin Sallings. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +the "Dustin's O'Caml lib" software (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software is provided ``as is'', without warranty of any kind, expressed or +implied, including but not limited to the warranties of merchantability, +fitness for a particular purpose and noninfringement. In no event shall Dustin +Sallings be liable for any claim, damages or other liability, whether in an +action of contract, tort or otherwise, arising from, out of or in connection +with the Software or the use or other dealings in the software. + diff --git a/ocs-1.0.3/CHANGES b/ocs-1.0.3/CHANGES new file mode 100644 index 0000000..d13f09c --- /dev/null +++ b/ocs-1.0.3/CHANGES @@ -0,0 +1,76 @@ +1.0.3 + + - The various let forms now create new frames. This fixes + behavior for situations where the initializers for the bound + variables return multiple times due to captured continuations. + + - Change define-syntax to return the unspecified value. + + - Fix (lambda ...) forms where is env-tagged by + macro expansion. + +1.0.2 + + - Try to find a smaller invariant precision when converting from + floating point values to strings. + + - Add missing function vector-fill!. + + - Add an unspecified value that isn't printed by the repl. + + - Add a value and functor that can be used to safely wrap arbitrary + OCaml values in Scheme values. + + - Fix internal definitions inside (begin ...) forms. + + - Consider literals in the literal list of syntax-rules locally + bound while parsing (but not while matching) patterns. R5RS is not + clear on this, but it is necessary to avoid breaking hygiene when + some expansions of an outer macro could change the interpretation of + pattern variables to literals within the patterns of inner macros. + This seems consistent with the behavior of other implementations. + + - Fix namespace lookup for syntax-rules literals to allow changes + in global bindings. + + - Fix namespace handling for nested macros. + + - Fix copy-paste error in log. + + - Fix the behavior of eval and arguments. + + - Fix inexact->exact for negative numbers that don't fit into an + integer. + + - The reader now also accepts square brackets [ and ] as list + delimiters. + + - Fix remainder to handle differing signs correctly. + +1.0.1 + + - Fix sorting of byte code objects in Makefile. + + - Add missing functions numerator and denominator. + + - Keep rationals normalized. + +1.0 (changes from pre-releases) + + - Rearrange build to generate bytecode and native libraries and a + native interpreter usable from the command line. + + - Remove CVS Id's (the project is now being stored in a GNU Arch + repository) from all files. + + - Fix Ocs_port.string_input_port to actually initialize the + port with the string length. + + - Fix internal definitions of the form (define (fun args ...) ...). + Previously the first item of the body would be skipped. + + - Fix the order of arguments to atan when called with two arguments. + + - When invoking continuations with multiple arguments, the + arguments are now wrapped with Svalues as if (values ...) were used. + diff --git a/ocs-1.0.3/COPYING b/ocs-1.0.3/COPYING new file mode 100644 index 0000000..a54d963 --- /dev/null +++ b/ocs-1.0.3/COPYING @@ -0,0 +1,25 @@ + +Copyright (c) 2003-2004 Ville-Pertti Keinonen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + diff --git a/ocs-1.0.3/README b/ocs-1.0.3/README new file mode 100644 index 0000000..768d180 --- /dev/null +++ b/ocs-1.0.3/README @@ -0,0 +1,136 @@ +1. General + +Ocs is an implementation of Scheme, as defined by R5RS. It is +written entirely in OCaml and can be trivially embedded in any +OCaml program. + +Known deviations from R5RS: + + - transcript-on and transcript-off are currently not implemented + - scheme-report-environment and null-environment ignore their + argument + +Anything else that does not work as specified in R5RS is a bug. + + +2. Installation + +Requirements: + + - GNU make or pmake (BSD make) + - OCaml 3.x (versions 3.06 and newer tested) + +Type make or gmake in the src directory. This should produce the +following: + + - A bytecode library (ocs.cma) + - A native library (ocs.cmxa, ocs.a) + - A stand-alone, native interpreter (ocscm) + +2.1 The 'ocscm' command + +If invoked without arguments, the interpreter will run in interactive +mode. + +If invoked with arguments, the interpreter will read and evaluate +the files listed as arguments and exit. The evaluation results are +not printed. + + +3. Implementation Details + +Implementing Scheme in OCaml is so straightforward that it hardly +needs any documentation. The following mappings between languages +are done: + + - Scheme is dynamically typed. Scheme values are represented by +the OCaml type Ocs_types.sval. + + - In Scheme, top-level bindings are global and all variables are +mutable. Variables references are bound through environments +(Ocs_types.env) to global slots (Ocs_types.gvar) or frame indices +(the actual frames are visible at evaluation-time through +Ocs_types.thread). + + - Scheme has capturable, first-class continuations. Most of the +evaluator is written in continuation-passing style in order to allow +this. + +Where discussing types, the rest of this section assumes that the +types defined in the module Ocs_types are visible. + +3.1 Evaluation + +Scheme values (S-expressions) are of the type sval. + +Before evaluation Scheme values are compiled to internal representations +of the type code. This is done by the function + + Ocs_compile.compile : env -> sval -> code + +The env type is used during compilation for variable bindings. A +new env is created for each new scope and frame. The base +environment with the basic language bindings can be created using + + Ocs_top.make_env : unit -> env + +Evaluation is done by + + Ocs_eval.eval : thread -> (sval -> unit) -> code -> unit + +where the second argument is a continuation to pass the result to. + +The thread type is used during evaluation for storing the current +frame and display for local variables, the input/output ports and +the current dynamic extent. It does not represent a thread in the +concurrent sense, but rather the evaluation state, and is copied and +changed rather than modified in place. The initial thread to be +passed to the evaluator can be created using +Ocs_top.make_thread : unit -> thread. + +3.2 Continuations and I/O + +Any continuations captured are associated with the thread at the +time of capture, so if a continuation is used to escape a +with-input-from-file or with-output-to-file, the input/output port +is restored to those of the time of capture. + +If a continuation is used to return to a with-input-from-file or +with-output-to-file, the port is once again set to the one +opened by the with-...-file call. However, if the thunk has +already exited once, the port will be closed and no longer be +valid for I/O calls. + +3.3 Numbers + +The full R5RS numeric tower is implemented, with the following +internal representations: + +Exact numbers are + - 31- or 63-bit integers (OCaml int) + - Big_int objects from the Num library when unboxed integers are + too small + - Ratio objects from the Num library for rationals + +Inexact numbers are + - 64-bit IEEE floats for reals (OCaml float) + - Pairs of 64-bit IEEE floats for complex numbers (OCaml Complex.t) + +Since inexact numbers are represented internally as binary floating +point, conversions to exact numbers are most precise for fractions of +powers of two + + (inexact->exact 2.125) ==> 17/8 + +compared to + + (inexact->exact 0.3) ==> 5404319552844595/18014398509481984 + +And in fact many rationals will not satisfy + + (= (inexact->exact (exact->inexact r)) r) + +However + + (rationalize (inexact->exact 0.3) (expt 2 -54)) ==> 3/10 + diff --git a/ocs-1.0.3/src/.depend b/ocs-1.0.3/src/.depend new file mode 100644 index 0000000..2ce83d6 --- /dev/null +++ b/ocs-1.0.3/src/.depend @@ -0,0 +1,107 @@ +ocs_char.cmo: ocs_types.cmi ocs_error.cmo ocs_env.cmi ocs_char.cmi +ocs_char.cmx: ocs_types.cmi ocs_error.cmx ocs_env.cmx ocs_char.cmi +ocs_compile.cmo: ocs_vartable.cmi ocs_types.cmi ocs_sym.cmi ocs_misc.cmi \ + ocs_error.cmo ocs_env.cmi ocs_compile.cmi +ocs_compile.cmx: ocs_vartable.cmx ocs_types.cmi ocs_sym.cmx ocs_misc.cmx \ + ocs_error.cmx ocs_env.cmx ocs_compile.cmi +ocs_complex.cmo: ocs_complex.cmi +ocs_complex.cmx: ocs_complex.cmi +ocs_contin.cmo: ocs_types.cmi ocs_misc.cmi ocs_eval.cmi ocs_error.cmo \ + ocs_env.cmi ocs_contin.cmi +ocs_contin.cmx: ocs_types.cmi ocs_misc.cmx ocs_eval.cmx ocs_error.cmx \ + ocs_env.cmx ocs_contin.cmi +ocs_env.cmo: ocs_vartable.cmi ocs_types.cmi ocs_sym.cmi ocs_error.cmo \ + ocs_env.cmi +ocs_env.cmx: ocs_vartable.cmx ocs_types.cmi ocs_sym.cmx ocs_error.cmx \ + ocs_env.cmi +ocs_eval.cmo: ocs_types.cmi ocs_sym.cmi ocs_misc.cmi ocs_error.cmo \ + ocs_eval.cmi +ocs_eval.cmx: ocs_types.cmi ocs_sym.cmx ocs_misc.cmx ocs_error.cmx \ + ocs_eval.cmi +ocs_io.cmo: ocs_types.cmi ocs_read.cmi ocs_print.cmi ocs_port.cmi \ + ocs_eval.cmi ocs_error.cmo ocs_env.cmi ocs_io.cmi +ocs_io.cmx: ocs_types.cmi ocs_read.cmx ocs_print.cmx ocs_port.cmx \ + ocs_eval.cmx ocs_error.cmx ocs_env.cmx ocs_io.cmi +ocs_lex.cmo: ocs_types.cmi ocs_port.cmi ocs_numstr.cmi ocs_error.cmo \ + ocs_char.cmi ocs_lex.cmi +ocs_lex.cmx: ocs_types.cmi ocs_port.cmx ocs_numstr.cmx ocs_error.cmx \ + ocs_char.cmx ocs_lex.cmi +ocs_list.cmo: ocs_types.cmi ocs_misc.cmi ocs_error.cmo ocs_env.cmi \ + ocs_list.cmi +ocs_list.cmx: ocs_types.cmi ocs_misc.cmx ocs_error.cmx ocs_env.cmx \ + ocs_list.cmi +ocs_macro.cmo: ocs_types.cmi ocs_sym.cmi ocs_misc.cmi ocs_error.cmo \ + ocs_env.cmi ocs_compile.cmi ocs_macro.cmi +ocs_macro.cmx: ocs_types.cmi ocs_sym.cmx ocs_misc.cmx ocs_error.cmx \ + ocs_env.cmx ocs_compile.cmx ocs_macro.cmi +ocs_main.cmo: ocs_types.cmi ocs_top.cmi ocs_prim.cmi ocs_error.cmo +ocs_main.cmx: ocs_types.cmi ocs_top.cmx ocs_prim.cmx ocs_error.cmx +ocs_misc.cmo: ocs_types.cmi ocs_error.cmo ocs_misc.cmi +ocs_misc.cmx: ocs_types.cmi ocs_error.cmx ocs_misc.cmi +ocs_num.cmo: ocs_types.cmi ocs_numaux.cmi ocs_error.cmo ocs_env.cmi \ + ocs_complex.cmi ocs_num.cmi +ocs_num.cmx: ocs_types.cmi ocs_numaux.cmx ocs_error.cmx ocs_env.cmx \ + ocs_complex.cmx ocs_num.cmi +ocs_numaux.cmo: ocs_types.cmi ocs_error.cmo ocs_numaux.cmi +ocs_numaux.cmx: ocs_types.cmi ocs_error.cmx ocs_numaux.cmi +ocs_numstr.cmo: ocs_types.cmi ocs_numaux.cmi ocs_num.cmi ocs_error.cmo \ + ocs_env.cmi ocs_numstr.cmi +ocs_numstr.cmx: ocs_types.cmi ocs_numaux.cmx ocs_num.cmx ocs_error.cmx \ + ocs_env.cmx ocs_numstr.cmi +ocs_port.cmo: ocs_error.cmo ocs_port.cmi +ocs_port.cmx: ocs_error.cmx ocs_port.cmi +ocs_prim.cmo: ocs_types.cmi ocs_sym.cmi ocs_read.cmi ocs_port.cmi \ + ocs_misc.cmi ocs_macro.cmi ocs_lex.cmi ocs_io.cmi ocs_eval.cmi \ + ocs_error.cmo ocs_env.cmi ocs_compile.cmi ocs_prim.cmi +ocs_prim.cmx: ocs_types.cmi ocs_sym.cmx ocs_read.cmx ocs_port.cmx \ + ocs_misc.cmx ocs_macro.cmx ocs_lex.cmx ocs_io.cmx ocs_eval.cmx \ + ocs_error.cmx ocs_env.cmx ocs_compile.cmx ocs_prim.cmi +ocs_print.cmo: ocs_types.cmi ocs_sym.cmi ocs_port.cmi ocs_numstr.cmi \ + ocs_char.cmi ocs_print.cmi +ocs_print.cmx: ocs_types.cmi ocs_sym.cmx ocs_port.cmx ocs_numstr.cmx \ + ocs_char.cmx ocs_print.cmi +ocs_read.cmo: ocs_types.cmi ocs_sym.cmi ocs_port.cmi ocs_misc.cmi ocs_lex.cmi \ + ocs_error.cmo ocs_read.cmi +ocs_read.cmx: ocs_types.cmi ocs_sym.cmx ocs_port.cmx ocs_misc.cmx ocs_lex.cmx \ + ocs_error.cmx ocs_read.cmi +ocs_string.cmo: ocs_types.cmi ocs_error.cmo ocs_env.cmi ocs_string.cmi +ocs_string.cmx: ocs_types.cmi ocs_error.cmx ocs_env.cmx ocs_string.cmi +ocs_sym.cmo: ocs_types.cmi ocs_error.cmo ocs_sym.cmi +ocs_sym.cmx: ocs_types.cmi ocs_error.cmx ocs_sym.cmi +ocs_top.cmo: ocs_vector.cmi ocs_types.cmi ocs_string.cmi ocs_read.cmi \ + ocs_print.cmi ocs_prim.cmi ocs_port.cmi ocs_numstr.cmi ocs_num.cmi \ + ocs_macro.cmi ocs_list.cmi ocs_lex.cmi ocs_io.cmi ocs_eval.cmi \ + ocs_error.cmo ocs_env.cmi ocs_contin.cmi ocs_compile.cmi ocs_char.cmi \ + ocs_top.cmi +ocs_top.cmx: ocs_vector.cmx ocs_types.cmi ocs_string.cmx ocs_read.cmx \ + ocs_print.cmx ocs_prim.cmx ocs_port.cmx ocs_numstr.cmx ocs_num.cmx \ + ocs_macro.cmx ocs_list.cmx ocs_lex.cmx ocs_io.cmx ocs_eval.cmx \ + ocs_error.cmx ocs_env.cmx ocs_contin.cmx ocs_compile.cmx ocs_char.cmx \ + ocs_top.cmi +ocs_vartable.cmo: ocs_vartable.cmi +ocs_vartable.cmx: ocs_vartable.cmi +ocs_vector.cmo: ocs_types.cmi ocs_error.cmo ocs_env.cmi ocs_vector.cmi +ocs_vector.cmx: ocs_types.cmi ocs_error.cmx ocs_env.cmx ocs_vector.cmi +ocs_wrap.cmo: ocs_types.cmi ocs_error.cmo +ocs_wrap.cmx: ocs_types.cmi ocs_error.cmx +ocs_char.cmi: ocs_types.cmi +ocs_compile.cmi: ocs_types.cmi +ocs_contin.cmi: ocs_types.cmi +ocs_env.cmi: ocs_types.cmi ocs_sym.cmi +ocs_eval.cmi: ocs_types.cmi +ocs_io.cmi: ocs_types.cmi +ocs_lex.cmi: ocs_types.cmi ocs_port.cmi ocs_error.cmo +ocs_list.cmi: ocs_types.cmi +ocs_macro.cmi: ocs_types.cmi +ocs_misc.cmi: ocs_types.cmi +ocs_num.cmi: ocs_types.cmi +ocs_numaux.cmi: ocs_types.cmi +ocs_numstr.cmi: ocs_types.cmi +ocs_prim.cmi: ocs_types.cmi +ocs_print.cmi: ocs_types.cmi ocs_port.cmi +ocs_read.cmi: ocs_types.cmi ocs_port.cmi ocs_lex.cmi +ocs_string.cmi: ocs_types.cmi +ocs_sym.cmi: ocs_types.cmi +ocs_top.cmi: ocs_types.cmi +ocs_types.cmi: ocs_vartable.cmi ocs_port.cmi +ocs_vector.cmi: ocs_types.cmi diff --git a/ocs-1.0.3/src/Makefile b/ocs-1.0.3/src/Makefile new file mode 100644 index 0000000..67b19b0 --- /dev/null +++ b/ocs-1.0.3/src/Makefile @@ -0,0 +1,57 @@ +# +# Build system for ocs library and interpreter +# + +include Makefile.common + +BC_LIB = ocs.cma +N_LIB = ocs.cmxa +C_LIB = ocs.a +INTERP = ocscm + +BC_OBJS = ocs_error.cmo ocs_port.cmo ocs_vartable.cmo ocs_sym.cmo \ + ocs_env.cmo ocs_char.cmo ocs_complex.cmo ocs_numaux.cmo \ + ocs_num.cmo ocs_numstr.cmo ocs_lex.cmo ocs_misc.cmo ocs_read.cmo \ + ocs_eval.cmo ocs_compile.cmo ocs_contin.cmo ocs_print.cmo \ + ocs_io.cmo ocs_list.cmo ocs_macro.cmo ocs_prim.cmo ocs_string.cmo \ + ocs_vector.cmo ocs_wrap.cmo ocs_top.cmo + +N_OBJS = ocs_error.cmx ocs_sym.cmx ocs_vartable.cmx ocs_env.cmx \ + ocs_char.cmx ocs_misc.cmx ocs_compile.cmx ocs_eval.cmx \ + ocs_contin.cmx ocs_port.cmx ocs_complex.cmx ocs_numaux.cmx \ + ocs_num.cmx ocs_numstr.cmx ocs_print.cmx ocs_lex.cmx ocs_read.cmx \ + ocs_io.cmx ocs_list.cmx ocs_macro.cmx ocs_prim.cmx ocs_string.cmx \ + ocs_vector.cmx ocs_wrap.cmx ocs_top.cmx + +INTERP_OBJS = ocs_main.cmx + +BCI_OBJS = ocs_main.cmo +BCI = ocscm-bc + +all: $(BC_LIB) $(N_LIB) $(INTERP) + +native: $(N_LIB) + +bytecode: $(BC_LIB) + +$(N_LIB): $(N_OBJS) + $(OCAMLOPT) -a -o $(N_LIB) $(N_OBJS) + +$(BC_LIB): $(BC_OBJS) + $(OCAMLC) -a -o $(BC_LIB) $(BC_OBJS) + +$(INTERP): $(N_LIB) $(INTERP_OBJS) + $(OCAMLOPT) -o $(INTERP) nums.cmxa unix.cmxa $(N_LIB) $(INTERP_OBJS) + +$(BCI): $(BC_LIB) $(BCI_OBJS) + $(OCAMLC) $(OCAMLFLAGS) -o $(BCI) nums.cma unix.cma $(BC_LIB) $(BCI_OBJS) + +clean: + -rm -f $(N_LIB) $(BC_LIB) $(C_LIB) $(INTERP) *.cm* *.o + -rm -f $(BCI) + +depend: + $(OCAMLDEP) *.ml *.mli > .depend + +include .depend + diff --git a/ocs-1.0.3/src/Makefile.common b/ocs-1.0.3/src/Makefile.common new file mode 100644 index 0000000..9ac5f71 --- /dev/null +++ b/ocs-1.0.3/src/Makefile.common @@ -0,0 +1,21 @@ +# +# General OCaml build settings +# + +OCAMLOPT = ocamlopt +OCAMLC = ocamlc +OCAMLDEP = ocamldep + +OCAMLFLAGS = + +.SUFFIXES: .o .c .h .cc .cpp .y .l .a .ml .mli .cmx .cmi .cmo + +.mli.cmi: + $(OCAMLC) $(OCAMLFLAGS) -c $< + +.ml.cmx: + $(OCAMLOPT) $(OCAMLFLAGS) -c $< + +.ml.cmo: + $(OCAMLC) $(OCAMLFLAGS) -c $< + diff --git a/ocs-1.0.3/src/ocs_char.ml b/ocs-1.0.3/src/ocs_char.ml new file mode 100644 index 0000000..26434be --- /dev/null +++ b/ocs-1.0.3/src/ocs_char.ml @@ -0,0 +1,153 @@ +(* Character primitives *) + +open Ocs_types +open Ocs_error +open Ocs_env + +(* Character name equivalents (long). *) +let char_long_names = + [| + "space", ' '; + "newline", '\n'; + + (* The rest are extensions. *) + "return", '\r'; + "tab", '\t'; + "backspace", '\008'; + "escape", '\027'; + "backslash", '\\'; + "alarm", '\007'; + "vtab", '\011'; + "del", '\127' |] +;; + +(* Table of short names for 0 .. 31 *) +let char_short_names = + [| "nul"; "soh"; "stx"; "etx"; "eot"; "enq"; "ack"; "bel"; + "bs"; "ht"; "nl"; "vt"; "np"; "cr"; "so"; "si"; + "dle"; "dc1"; "dc2"; "dc3"; "dc4"; "nak"; "syn"; "etb"; + "can"; "em"; "sub"; "esc"; "fs"; "gs"; "rs"; "us" |] +;; + +let name_to_char name = + let name = (String.lowercase name) + and ln = Array.length char_long_names + and sn = Array.length char_short_names in + let rec lloop i = + if i = ln then sloop 0 + else + match char_long_names.(i) with + (n, c) -> if n = name then Some c else lloop (i + 1) + and sloop i = + if i = sn then None + else if char_short_names.(i) = name then Some (char_of_int i) + else sloop (i + 1) + in + lloop 0 +;; + +(* Generate the preferred printed literal form of a character. *) +let char_to_name = + function + '\n' -> "newline" + | ' ' -> "space" + | '\127' -> "del" + | '\000' .. '\031' as c -> char_short_names.(int_of_char c) + | '\033' .. '\126' as c -> String.make 1 c + | c -> Printf.sprintf "x%02x" (int_of_char c) +;; + +let char_cmp op c1 c2 = + match (c1, c2) with + (Schar c1, Schar c2) -> if op c1 c2 then Strue else Sfalse + | _ -> raise (Error "args not characters") +;; + +let char_eq = char_cmp (=);; +let char_lt = char_cmp (<);; +let char_gt = char_cmp (>);; +let char_le = char_cmp (<=);; +let char_ge = char_cmp (>=);; + +let char_ci_cmp op c1 c2 = + match (c1, c2) with + (Schar c1, Schar c2) -> + if op (Char.lowercase c1) (Char.lowercase c2) then Strue else Sfalse + | _ -> raise (Error "args not characters") +;; + +let char_ci_eq = char_ci_cmp (=);; +let char_ci_lt = char_ci_cmp (<);; +let char_ci_gt = char_ci_cmp (>);; +let char_ci_le = char_ci_cmp (<=);; +let char_ci_ge = char_ci_cmp (>=);; + +let char_unop op = + function + Schar c -> op c + | _ -> raise (Error "arg not character") +;; + +let char_alphabetic = + char_unop (function 'A' .. 'Z' | 'a' ..'z' -> Strue | _ -> Sfalse) +;; + +let char_numeric = + char_unop (function '0' ..'9' -> Strue | _ -> Sfalse) +;; + +let char_whitespace = + char_unop (function ' ' | '\t' | '\r' | '\n' | '\012' -> Strue | _ -> Sfalse) +;; + +let char_uppercase = + char_unop (function 'A' .. 'Z' -> Strue | _ -> Sfalse) +;; + +let char_lowercase = + char_unop (function 'a' .. 'z' -> Strue | _ -> Sfalse) +;; + +let char_integer = + char_unop (fun c -> Sint (int_of_char c)) +;; + +let integer_char = + function + Sint i -> Schar (char_of_int i) + | _ -> raise (Error "arg not int") +;; + +let char_upcase = + char_unop (fun c -> Schar (Char.uppercase c)) +;; + +let char_downcase = + char_unop (fun c -> Schar (Char.lowercase c)) +;; + +let init e = + set_pf2 e char_eq "char=?"; + set_pf2 e char_lt "char?"; + set_pf2 e char_le "char<=?"; + set_pf2 e char_ge "char>=?"; + + set_pf2 e char_ci_eq "char-ci=?"; + set_pf2 e char_ci_lt "char-ci?"; + set_pf2 e char_ci_le "char-ci<=?"; + set_pf2 e char_ci_ge "char-ci>=?"; + + set_pf1 e char_alphabetic "char-alphabetic?"; + set_pf1 e char_numeric "char-numeric?"; + set_pf1 e char_whitespace "char-whitespace?"; + set_pf1 e char_uppercase "char-upper-case?"; + set_pf1 e char_lowercase "char-lower-case?"; + + set_pf1 e char_integer "char->integer"; + set_pf1 e integer_char "integer->char"; + + set_pf1 e char_upcase "char-upcase"; + set_pf1 e char_downcase "char-downcase"; +;; diff --git a/ocs-1.0.3/src/ocs_char.mli b/ocs-1.0.3/src/ocs_char.mli new file mode 100644 index 0000000..40b350b --- /dev/null +++ b/ocs-1.0.3/src/ocs_char.mli @@ -0,0 +1,9 @@ +(* Character primitives. *) + +open Ocs_types + +val name_to_char : string -> char option +val char_to_name : char -> string + +val init : env -> unit + diff --git a/ocs-1.0.3/src/ocs_compile.ml b/ocs-1.0.3/src/ocs_compile.ml new file mode 100644 index 0000000..411ea4f --- /dev/null +++ b/ocs-1.0.3/src/ocs_compile.ml @@ -0,0 +1,487 @@ +(* Compile Scheme expressions into a form that can be evaluated efficiently. *) + +open Ocs_types +open Ocs_error +open Ocs_sym +open Ocs_misc +open Ocs_env +open Ocs_vartable + +(* Split the variables that are arguments to let/let*/letrec *) +let letsplit f = + function + Spair { car = (Ssymbol _ | Sesym (_, _)) as s; + cdr = Spair { car = v; cdr = Snull }} -> f s v + | _ -> raise (Error "invalid let arglist") +;; + +(* Split the variables that are arguments to do *) +let dosplit f = + function + Spair { car = (Ssymbol _ | Sesym (_, _)) as sym; + cdr = Spair { car = init; cdr = t }} -> + begin + match t with + Snull -> f sym init sym + | Spair { car = step; cdr = Snull } -> f sym init step + | _ -> raise (Error "invalid do arglist") + end + | _ -> raise (Error "invalid do arglist") +;; + +let genset b v = + match b with + Vglob g -> Csetg (g, v) + | Vloc (d, i) -> Csetl (d, i, v) + | _ -> raise (Error "cannot change syntactic keywords") +;; + +let gendef b v = + match b with + Vglob g -> Cdefine (g, v) + | Vloc (d, i) -> Csetl (d, i, v) + | _ -> raise (Error "cannot change syntactic keywords") +;; + +let genref = + function + Vglob g -> Cgetg g + | Vloc (d, i) -> Cgetl (d, i) + | Vsyntax _ -> Cval Sunspec + | Vmacro _ -> Cval Sunspec + | Vkeyword _ -> Cval Sunbound +;; + +let mkseq s = + match Array.length s with + 0 -> Cval Sunspec + | 1 -> s.(0) + | 2 -> Cseq2 (s.(0), s.(1)) + | 3 -> Cseq3 (s.(0), s.(1), s.(2)) + | _ -> Cseqn s +;; + +let mkand s = + match Array.length s with + 0 -> Cval Strue + | 1 -> s.(0) + | 2 -> Cand2 (s.(0), s.(1)) + | 3 -> Cand3 (s.(0), s.(1), s.(2)) + | _ -> Candn s +;; + +let mkor s = + match Array.length s with + 0 -> Cval Sfalse + | 1 -> s.(0) + | 2 -> Cor2 (s.(0), s.(1)) + | 3 -> Cor3 (s.(0), s.(1), s.(2)) + | _ -> Corn s +;; + +let make_proc c n hr fs = + { proc_body = c; + proc_nargs = n; + proc_has_rest = hr; + proc_frame_size = fs; + proc_name = "##" } +;; + +let chksplice a = + let n = Array.length a in + let rec loop i = + if i < n then + begin + match a.(i) with + Cqqspl _ -> true + | _ -> loop (i + 1) + end + else + false + in + loop 0 +;; + +(* Scan quoted sections, eliminate environment-specific symbols *) +let rec scanquoted = + function + Sesym (_, sym) -> sym + | Spair { car = h; cdr = t } -> + Spair { car = scanquoted h; cdr = scanquoted t } + | Svector v -> + Svector (Array.map (fun x -> scanquoted x) v) + | x -> x +;; + +let is_uglobal e = + vt_global e.env_vartable +;; + +let is_global e = + e.env_depth < 0 +;; + +let rec mkdefine e args = + let narg = Array.length args in + if narg < 1 then + raise (Error "define: not enough args"); + match args.(0) with + Spair { car = (Ssymbol _ | Sesym (_, _)) as s; cdr = al } when narg > 1 -> + begin + match mklambda e al args with + Clambda p as l -> + p.proc_name <- sym_name s; + gendef (get_var e s) l + | _ -> assert false + end + | (Ssymbol _ | Sesym (_, _)) as s when narg = 2 -> + gendef (get_var e s) (compile e args.(1)) + | (Ssymbol _ | Sesym (_, _)) as s when narg = 1 -> + gendef (get_var e s) (Cval Sunspec) + | _ -> raise (Error "define: invalid syntax") + +(* The following functions up to mkbody are used to compile the body + of a lambda, let etc., with possible internal definitions. The + internal definitions may be created by macro expansion, so we need + to do that here, too...and we might end up expanding a macro more + than once (so there must be no side-effects to expansion). *) +and idpp = + function + Spair { car = (Ssymbol _ | Sesym (_, _)) as s; + cdr = Spair { car = v; cdr = Snull }} -> (s, s, v) + | Spair { car = Spair { car = (Ssymbol _ | Sesym (_, _)) as s; + cdr = _ } as x; + cdr = _ } as v -> (s, x, v) + | _ -> raise (Error "invalid internal definition") + +and getidef e = + function + Spair { car = (Ssymbol _ | Sesym (_, _)) as s; cdr = t } -> + begin + match find_var e s with + Some (Vsyntax f) when f == mkdefine -> Some (idpp t) + | Some (Vmacro f) -> getidef e (f e (Array.of_list (list_to_caml t))) + | _ -> None + end + | _ -> None + +and mkid e x v = + match x with + Spair { car = _; cdr = al } -> + mklambda e al (Array.of_list (list_to_caml v)) + | _ -> compile e v + +and expand_begin e = + function + (Spair { car = (Ssymbol _ | Sesym (_, _)) as s; cdr = t }) as x -> + begin + match find_var e s with + Some (Vsyntax f) when f == mkbegin -> + Array.concat (List.map (expand_begin e) (list_to_caml t)) + | Some (Vmacro f) -> + expand_begin e (f e (Array.of_list (list_to_caml t))) + | _ -> [| x |] + end + | x -> [| x |] + +and mkbody e args = + let args = Array.concat (List.map (expand_begin e) (Array.to_list args)) in + let n = Array.length args in + let rec loop i r = + if i < n then + begin + match getidef e args.(i) with + Some d -> loop (i + 1) (d::r) + | None -> r + end + else + r + in + let ids = Array.map (fun (s, x, v) -> let r = bind_var e s in (r, x, v)) + (Array.of_list (List.rev (loop 0 []))) in + let sets = Array.map (fun (r, x, v) -> gendef r (mkid e x v)) ids in + let nid = Array.length sets in + let rest = Array.map (fun x -> compile e x) + (Array.sub args nid (n - nid)) + in + Array.append sets rest + +and mkset e args = + if Array.length args != 2 then + raise (Error "set!: requires exactly two args"); + match args.(0) with + (Ssymbol _ | Sesym (_, _)) as s -> + let v = compile e args.(1) in + genset (get_var e s) v + | _ -> raise (Error "set!: not a symbol") + +(* Note that the first item of the "body" array is ignored, it + corresponds to the argument list but may be in the form expected + by either define or lambda. *) +and mklambda e args body = + let ne = new_frame e + and nargs = ref 0 + and has_rest = ref false in + let rec scanargs = + function + Spair { car = (Ssymbol _ | Sesym (_, _)) as s; cdr = tl } -> + let _ = bind_var ne s in + incr nargs; + scanargs tl + | (Ssymbol _ | Sesym (_, _)) as s -> + let _ = bind_var ne s in + incr nargs; + has_rest := true; + () + | Snull -> () + | _ -> raise (Error "lambda: bad arg list") + in + scanargs args; + let body = + mkseq (mkbody ne (Array.sub body 1 (Array.length body - 1))) + in + Clambda (make_proc body !nargs !has_rest !(ne.env_frame_size)) + +and mkif e args = + match Array.length args with + 2 -> Cif (compile e args.(0), compile e args.(1), Cval Sunspec) + | 3 -> Cif (compile e args.(0), compile e args.(1), compile e args.(2)) + | _ -> raise (Error "if: needs two or three args") + +and mknamedlet e s args = + let argv = + Array.map + (letsplit (fun s v -> s, compile e v)) + (Array.of_list (list_to_caml args.(1))) in + let ar = new_var e in + let ne = new_frame e in + bind_name ne s ar; + let av = + Array.map (fun (s, v) -> let _ = bind_var ne s in v) argv in + let body = mkseq (mkbody ne (Array.sub args 2 (Array.length args - 2))) in + let proc = + Clambda (make_proc body (Array.length av) false !(ne.env_frame_size)) + in + Cseq2 (gendef ar proc, mkapply (genref ar) av) + +and mklet e args = + if Array.length args < 2 then + raise (Error "let: too few args"); + match args.(0) with + (Ssymbol _ | Sesym (_, _)) as s -> mknamedlet e s args + | Snull -> mkseq (mkbody e (Array.sub args 1 (Array.length args - 1))) + | Spair _ as al -> + let argv = + Array.map + (letsplit (fun s v -> s, compile e v)) + (Array.of_list (list_to_caml al)) in + let ne = new_frame e in + let av = Array.map (fun (s, v) -> let _ = bind_var ne s in v) argv in + let body = mkseq (mkbody ne (Array.sub args 1 (Array.length args - 1))) in + let proc = + Clambda (make_proc body (Array.length av) false !(ne.env_frame_size)) + in + mkapply proc av + | _ -> raise (Error "let: missing argument list") + +and mkletstar e args = + if Array.length args < 2 then + raise (Error "let*: too few args"); + let rec build e = + function + x::t -> + let (s, v) = letsplit (fun s v -> s, compile e v) x in + let ne = new_frame e in + let _ = bind_var ne s in + let body = build ne t in + let proc = Clambda (make_proc body 1 false !(ne.env_frame_size)) in + mkapply proc [| v |] + | [] -> mkseq (mkbody e (Array.sub args 1 (Array.length args - 1))) + in + build e (list_to_caml args.(0)) + +and mkletrec e args = + if Array.length args < 2 then + raise (Error "letrec: too few args"); + let ne = new_frame e in + let av = + Array.map (letsplit (fun s v -> let r = bind_var ne s in (r, v))) + (Array.of_list (list_to_caml args.(0))) in + let avi = Array.map (fun (r, v) -> compile ne v) av in + let ne' = new_frame ne in + let sets = Array.map (fun (r, v) -> gendef r (genref (new_var ne'))) av in + let body = mkseq (Array.append sets + (mkbody ne' (Array.sub args 1 (Array.length args - 1)))) in + let proc = + Clambda (make_proc body (Array.length av) false !(ne'.env_frame_size)) in + let proc = + Clambda (make_proc (mkapply proc avi) + (Array.length av) false !(ne.env_frame_size)) + in + mkapply proc (Array.map (fun _ -> Cval Sunspec) av) + +and compileseq e s = + mkseq (Array.map (fun x -> compile e x) + (Array.of_list (list_to_caml s))) + +and mkcond e args = + Ccond + (Array.map + (function + Spair { car = test; + cdr = Spair { car = (Ssymbol _ | Sesym (_, _)) as s; + cdr = Spair { car = x; cdr = Snull }}} + when is_keyword e s "=>" -> + (Ccondspec (compile e test), (compile e x)) + | Spair { car = (Ssymbol _ | Sesym (_, _)) as s; cdr = body } + when is_keyword e s "else" -> + (Cval Strue, compileseq e body) + | Spair { car = test; cdr = body } -> + (compile e test, compileseq e body) + | _ -> raise (Error "cond: syntax error")) + args) + +and mkcase e args = + Ccase + (compile e args.(0), + Array.map + (function + Spair { car = (Ssymbol _ | Sesym (_, _)) as s; cdr = body } + when is_keyword e s "else" -> + ([| |], compileseq e body) + | Spair { car = Spair _ as c; cdr = body } -> + (Array.of_list (list_to_caml c), compileseq e body) + | _ -> raise (Error "case: syntax error")) + (Array.sub args 1 (Array.length args - 1))) + +and mkdo e args = + if Array.length args < 2 then + raise (Error "do: bad args"); + let vv = + Array.map + (dosplit (fun sym init step -> sym, compile e init, step)) + (Array.of_list (list_to_caml args.(0))) + and (test, result) = + match args.(1) with + Spair { car = t; cdr = r } -> t, r + | _ -> raise (Error "do: bad args") + and anonvar = new_var e + and ne = new_frame e in + let av = Array.map (fun (sym, init, _) -> + let _ = bind_var ne sym in init) vv in + let body = + Cif (compile ne test, compileseq ne result, + mkseq + (Array.append + (Array.map (fun x -> compile ne x) + (Array.sub args 2 (Array.length args - 2))) + [| mkapply (genref anonvar) + (Array.map (fun (_, _, step) -> compile ne step) vv) |])) + in + let proc = + Clambda (make_proc body (Array.length av) false !(ne.env_frame_size)) + in + Cseq2 (gendef anonvar proc, mkapply (genref anonvar) av) + +and mkdelay e = + function + [| expr |] -> Cdelay (compile e expr) + | _ -> raise (Error "delay: bad args") + +and mkqq e args = + if Array.length args <> 1 then + raise (Error "quasiquote: need exactly one arg") + else + let rec qq depth = + function + Spair { car = (Ssymbol _ | Sesym (_, _)) as s; + cdr = Spair { car = x; cdr = Snull }} -> + if is_syntax e s mkqq then + Cqqp (Cval s, Cqqp (qq (depth + 1) x, Cval Snull)) + else if is_keyword e s "unquote" then + begin + if depth > 0 then + Cqqp (Cval s, Cqqp (qq (depth - 1) x, Cval Snull)) + else + compile e x + end + else if is_keyword e s "unquote-splicing" then + begin + if depth > 0 then + Cqqp (Cval s, Cqqp (qq (depth - 1) x, Cval Snull)) + else + Cqqspl (compile e x) + end + else + Cqqp (Cval s, Cqqp (qq depth x, Cval Snull)) + | Spair { car = h; cdr = t } -> Cqqp (qq depth h, qq depth t) + | Svector v -> + let qv = Array.map (fun x -> qq depth x) v in + if chksplice qv then + Cqqvs (Array.to_list qv) + else + Cqqv qv + | x -> Cval (scanquoted x) + in + qq 0 args.(0) + +and applysym e s args = + match get_var e s with + Vsyntax f -> f e args + | Vmacro f -> compile e (f e args) + | r -> mkapply (genref r) (Array.map (fun x -> compile e x) args) + +and compile e = + function + (Ssymbol _ | Sesym (_, _)) as s -> genref (get_var e s) + | Spair p -> + let args = Array.of_list (list_to_caml p.cdr) in + begin + match p.car with + (Ssymbol _ | Sesym (_, _)) as s -> applysym e s args + | x -> + mkapply (compile e x) (Array.map (fun x -> compile e x) args) + end + | x -> Cval (scanquoted x) + +and mkbegin e args = + mkseq (Array.map (fun x -> compile e x) args) +;; + +let bind_lang e = + let spec = + [ sym_define, mkdefine; + sym_set, mkset; + sym_let, mklet; + sym_letstar, mkletstar; + sym_letrec, mkletrec; + sym_if, mkif; + sym_cond, mkcond; + sym_case, mkcase; + sym_do, mkdo; + sym_begin, mkbegin; + sym_and, (fun e args -> mkand (Array.map (fun x -> compile e x) args)); + sym_or, (fun e args -> mkor (Array.map (fun x -> compile e x) args)); + sym_lambda, + (fun e args -> + if Array.length args >= 1 then + mklambda e args.(0) args + else + raise (Error "lambda: needs at least one arg")); + sym_delay, mkdelay; + sym_quote, + (fun e args -> + if Array.length args = 1 then + Cval (scanquoted args.(0)) + else + raise (Error "quote: need exactly one arg")); + sym_quasiquote, mkqq ] + in + List.iter (fun (s, f) -> bind_name e s (Vsyntax f)) spec; + bind_name e sym_else (Vkeyword "else"); + bind_name e sym_arrow (Vkeyword "=>"); + bind_name e sym_unquote (Vkeyword "unquote"); + bind_name e sym_unquote_splicing (Vkeyword "unquote-splicing"); + bind_name e sym_syntax_rules (Vkeyword "syntax-rules"); +;; + diff --git a/ocs-1.0.3/src/ocs_compile.mli b/ocs-1.0.3/src/ocs_compile.mli new file mode 100644 index 0000000..1333149 --- /dev/null +++ b/ocs-1.0.3/src/ocs_compile.mli @@ -0,0 +1,13 @@ +(* Compile expressions *) + +open Ocs_types + +val compile : env -> sval -> code + +val bind_lang : env -> unit + +(* Internal, used by ocs_macro *) +val letsplit : (sval -> sval -> 'a) -> sval -> 'a +val mkseq : code array -> code +val mkbody : env -> sval array -> code array + diff --git a/ocs-1.0.3/src/ocs_complex.ml b/ocs-1.0.3/src/ocs_complex.ml new file mode 100644 index 0000000..8572a50 --- /dev/null +++ b/ocs-1.0.3/src/ocs_complex.ml @@ -0,0 +1,44 @@ +(* Implement functions missing from Complex. *) + +open Complex + +let sin_cplx z = + { re = sin z.re *. cosh z.im; + im = cos z.re *. sinh z.im } +;; + +let cos_cplx z = + { re = cos z.re *. cosh z.im; + im = -.(sin z.re) *. sinh z.im } +;; + +let tan_cplx z = + div (sin_cplx z) (cos_cplx z) +;; + +(* asin z = -i Ln (iz + sqrt (1 - z^2)) *) + +let asin_cplx = + function { re = x; im = y } -> + let t = sqrt { re = 1.0 +. y *. y -. x *. x; im = -2.0 *. x *. y } in + let z = log { re = t.re -. y; im = t.im +. x } in + { re = z.im; im = -.z.re } +;; + +(* acos z = pi/2 - asin z *) + +let acos_cplx z = + match asin_cplx z with + { re = x; im = y } -> { re = 1.57079632679489661923 -. x; im = -.y } +;; + +(* atan z = [Ln (1 + iz) - Ln (1 - iz)] / 2i *) + +let atan_cplx = + function { re = x; im = y } -> + let t1 = log { re = 1.0 -. y; im = x } + and t2 = log { re = 1.0 +. y; im = -.x } in + { re = (t1.im -. t2.im) *. 0.5; + im = -.(t1.re -. t2.re) *. 0.5 } +;; + diff --git a/ocs-1.0.3/src/ocs_complex.mli b/ocs-1.0.3/src/ocs_complex.mli new file mode 100644 index 0000000..b8f48bd --- /dev/null +++ b/ocs-1.0.3/src/ocs_complex.mli @@ -0,0 +1,11 @@ +(* Functions missing from Complex. *) + +open Complex + +val sin_cplx : t -> t +val cos_cplx : t -> t +val tan_cplx : t -> t +val asin_cplx : t -> t +val acos_cplx : t -> t +val atan_cplx : t -> t + diff --git a/ocs-1.0.3/src/ocs_contin.ml b/ocs-1.0.3/src/ocs_contin.ml new file mode 100644 index 0000000..118b36e --- /dev/null +++ b/ocs-1.0.3/src/ocs_contin.ml @@ -0,0 +1,114 @@ +(* Continuations *) + +open Ocs_types +open Ocs_error +open Ocs_eval +open Ocs_env +open Ocs_misc + +let rec find_depth fdx tdx al bl = + match (fdx, tdx) with + (Some f, Some t) -> + if f.dynext_parent == t.dynext_parent then + (List.rev (f.dynext_after::al), t.dynext_before::bl) + else if f.dynext_depth > t.dynext_depth then + find_depth f.dynext_parent tdx (f.dynext_after::al) bl + else if f.dynext_depth < t.dynext_depth then + find_depth fdx t.dynext_parent al (t.dynext_before::bl) + else + find_depth f.dynext_parent t.dynext_parent + (f.dynext_after::al) (t.dynext_before::bl) + | (Some f, None) -> + find_depth f.dynext_parent tdx (f.dynext_after::al) bl + | (None, Some t) -> + find_depth fdx t.dynext_parent al (t.dynext_before::bl) + | _ -> (List.rev al, bl) +;; + +(* Change from the dynamic extent fdx to the dynamic extent tdx *) +let dxswitch fdx tdx cont = + if fdx == tdx then + cont () + else + let (al, bl) = find_depth fdx tdx [] [] in + let rec bloop = + function + [] -> cont () + | h::t -> eval (fst h) (fun _ -> bloop t) (snd h) + in + let rec aloop = + function + [] -> bloop bl + | h::t -> eval (fst h) (fun _ -> aloop t) (snd h) + in + aloop al +;; + +let continuation dx cc th _ = + function + [| x |] -> dxswitch th.th_dynext dx (fun () -> cc x) + | av -> dxswitch th.th_dynext dx (fun () -> cc (Svalues av)) +;; + +let call_cc th cc = + function + [| proc |] -> + let cont = + Sprim { prim_fun = Pfcn (continuation th.th_dynext cc); + prim_name = "" } + in + eval th cc (Capply1 (Cval proc, Cval cont)) + | _ -> raise (Error "call/cc: bad args") +;; + +let values = + function + [| x |] -> x + | av -> Svalues av +;; + +let call_values th cc = + function + [| producer; consumer |] -> + eval th + (function + Svalues av -> + eval th cc (mkapply (Cval consumer) + (Array.map (fun x -> Cval x) av)) + | x -> eval th cc (Capply1 (Cval consumer, Cval x))) + (Capply0 (Cval producer)) + | _ -> raise (Error "call-with-values: bad args") +;; + +let dynamic_wind th cc = + function + [| before; thunk; after |] -> + let before = Capply0 (Cval before) + and after = Capply0 (Cval after) in + let ndx = { + dynext_parent = th.th_dynext; + dynext_depth = + (match th.th_dynext with + None -> 0 + | Some dx -> dx.dynext_depth + 1); + dynext_before = (th, before); + dynext_after = (th, after) + } in + eval th + (fun _ -> + eval { th with th_dynext = Some ndx } + (fun r -> + eval th (fun _ -> cc r) after) (Capply0 (Cval thunk))) before + | _ -> raise (Error "dynamic-wind: bad args") +;; + +let init e = + set_pfcn e call_cc "call-with-current-continuation"; + set_pfcn e call_cc "call/cc"; + + set_pfn e values "values"; + + set_pfcn e call_values "call-with-values"; + set_pfcn e dynamic_wind "dynamic-wind"; +;; + diff --git a/ocs-1.0.3/src/ocs_contin.mli b/ocs-1.0.3/src/ocs_contin.mli new file mode 100644 index 0000000..6f25bd3 --- /dev/null +++ b/ocs-1.0.3/src/ocs_contin.mli @@ -0,0 +1,6 @@ +(* Continuations *) + +open Ocs_types + +val init : env -> unit + diff --git a/ocs-1.0.3/src/ocs_env.ml b/ocs-1.0.3/src/ocs_env.ml new file mode 100644 index 0000000..0e7eae4 --- /dev/null +++ b/ocs-1.0.3/src/ocs_env.ml @@ -0,0 +1,143 @@ +(* Compilation environment, variable bindings. *) + +open Ocs_types +open Ocs_error +open Ocs_sym +open Ocs_vartable + +let top_env () = + { env_depth = -1; + env_vartable = vt_create (); + env_frame_size = ref 0; + env_tagged = [] } +;; + +let new_scope e = + { env_depth = e.env_depth; + env_vartable = vt_inherit e.env_vartable; + env_frame_size = e.env_frame_size; + env_tagged = e.env_tagged } +;; + +let new_frame e = + { env_depth = e.env_depth + 1; + env_vartable = vt_inherit e.env_vartable; + env_frame_size = ref 0; + env_tagged = e.env_tagged } +;; + +let new_var e = + if e.env_depth < 0 then + Vglob { g_sym = Snull; g_val = Sunbound } + else + let v = Vloc (e.env_depth, !(e.env_frame_size)) in + incr e.env_frame_size; + v +;; + +let bind_name e sym v = + match sym with + Sesym (te, sym) -> + e.env_tagged <- (te, sym, v)::e.env_tagged + | _ -> + begin + match v with + Vglob g -> g.g_sym <- sym + | _ -> () + end; + var_insert e.env_vartable (sym_name sym) v +;; + +let bind_var e sym = + let r = new_var e in + bind_name e sym r; + r +;; + +let find_tagged te ts l = + let rec loop = + function + (e, s, v)::t -> + if e == te && s == ts then + Some v + else + loop t + | [] -> None + in + loop l +;; + +let rec find_var e sym = + match sym with + Sesym (te, sym) -> + begin + match find_tagged te sym e.env_tagged with + Some _ as v -> v + | None -> find_var te sym + end + | _ -> var_find e.env_vartable (sym_name sym) +;; + +let rec get_var e sym = + match sym with + Sesym (te, sym) -> + begin + match find_tagged te sym e.env_tagged with + Some v -> v + | None -> get_var te sym + end + | _ -> var_get e.env_vartable (sym_name sym) + (fun () -> Vglob { g_sym = sym; g_val = Sunbound }) +;; + +let set_glob e sym v = + match get_var e sym with + Vglob g -> g.g_val <- v + | _ -> raise (Error "set_glob: not a global") +;; + +let vb_copy = + function + Vglob g -> Vglob { g with g_val = g.g_val } + | x -> x + +let env_copy e = + { e with + env_vartable = vt_copy e.env_vartable vb_copy; + env_tagged = [] } + +let is_a_keyword e sym = + match find_var e sym with + Some (Vkeyword _) -> true + | _ -> false +;; + +let is_keyword e sym name = + match find_var e sym with + Some (Vkeyword kw) -> kw = name + | _ -> false +;; + +let safe_is_keyword e sym name = + match sym with + Ssymbol _ | Sesym (_, _) -> is_keyword e sym name + | _ -> false +;; + +let is_syntax e sym sf = + match find_var e sym with + Some (Vsyntax f) -> f == sf + | _ -> false +;; + +let set_pfg e f n = + set_glob e (get_symbol n) (Sprim { prim_fun = f; prim_name = n }) +;; + +let set_pf0 e f n = set_pfg e (Pf0 f) n +let set_pf1 e f n = set_pfg e (Pf1 f) n +let set_pf2 e f n = set_pfg e (Pf2 f) n +let set_pf3 e f n = set_pfg e (Pf3 f) n +let set_pfn e f n = set_pfg e (Pfn f) n +let set_pfcn e f n = set_pfg e (Pfcn f) n + diff --git a/ocs-1.0.3/src/ocs_env.mli b/ocs-1.0.3/src/ocs_env.mli new file mode 100644 index 0000000..e84c55b --- /dev/null +++ b/ocs-1.0.3/src/ocs_env.mli @@ -0,0 +1,30 @@ +(* Compilation environment, variable bindings. *) + +open Ocs_types +open Ocs_sym + +val top_env : unit -> env +val new_scope : env -> env +val new_frame : env -> env +val new_var : env -> vbind +val bind_name : env -> sval -> vbind -> unit +val bind_var : env -> sval -> vbind +val find_var : env -> sval -> vbind option +val get_var : env -> sval -> vbind +val set_glob : env -> sval -> sval -> unit +val env_copy : env -> env + +val is_a_keyword : env -> sval -> bool +val is_keyword : env -> sval -> string -> bool +val safe_is_keyword : env -> sval -> string -> bool + +val is_syntax : env -> sval -> (env -> sval array -> code) -> bool + +val set_pf0 : env -> (unit -> sval) -> string -> unit +val set_pf1 : env -> (sval -> sval) -> string -> unit +val set_pf2 : env -> (sval -> sval -> sval) -> string -> unit +val set_pf3 : env -> (sval -> sval -> sval -> sval) -> string -> unit +val set_pfn : env -> (sval array -> sval) -> string -> unit +val set_pfcn : + env -> (thread -> (sval -> unit) -> sval array -> unit) -> string -> unit + diff --git a/ocs-1.0.3/src/ocs_error.ml b/ocs-1.0.3/src/ocs_error.ml new file mode 100644 index 0000000..bbed9a8 --- /dev/null +++ b/ocs-1.0.3/src/ocs_error.ml @@ -0,0 +1,11 @@ +(* Errors (exceptions) generated by the library. *) + +(* Source location (file, line) *) +type location = + string * int + +(* These errors indicate non-fatal run-time errors that should be + reported, generally interactively. *) +exception Error of string +exception ErrorL of location * string + diff --git a/ocs-1.0.3/src/ocs_eval.ml b/ocs-1.0.3/src/ocs_eval.ml new file mode 100644 index 0000000..2681dd0 --- /dev/null +++ b/ocs-1.0.3/src/ocs_eval.ml @@ -0,0 +1,339 @@ +(* Actual evaluator for the semi-compiled form. *) + +open Ocs_types +open Ocs_error +open Ocs_sym +open Ocs_misc + +(* Local variables are stored either in th_frame or th_display. + th_frame is the deepest frame, not yet part of the display. *) + +let getl th d i = + if d >= Array.length th.th_display then + th.th_frame.(i) + else + th.th_display.(d).(i) +;; + +let setl th d i v = + if d >= Array.length th.th_display then + th.th_frame.(i) <- v + else + th.th_display.(d).(i) <- v +;; + +let args_err p n = + if p.proc_has_rest then + Printf.sprintf "procedure %s expected %d or more args got %d" + p.proc_name (p.proc_nargs - 1) n + else + Printf.sprintf "procedure %s expected %d args got %d" + p.proc_name p.proc_nargs n + +let chkargs p n = + match p with + Sproc (p, _) -> + if n <> p.proc_nargs && (not p.proc_has_rest || n < p.proc_nargs - 1) then + raise (Error (args_err p n)) + else + () + | Sprim p -> + if + begin + match p.prim_fun with + Pf0 _ -> n = 0 + | Pf1 _ -> n = 1 + | Pf2 _ -> n = 2 + | Pf3 _ -> n = 3 + | Pfn _ | Pfcn _ -> true + end + then + () + else + raise (Error (p.prim_name ^ ": wrong number of arguments")) + | _ -> raise (Error "apply: not a procedure or primitive") +;; + +let rec doapply th cc p disp av = + let th = { + th with + th_frame = Array.make p.proc_frame_size Seof; + th_display = disp; + th_depth = Array.length disp } + in + if p.proc_has_rest then + begin + let nfixed = p.proc_nargs - 1 + and n = Array.length av in + if nfixed > 0 then + Array.blit av 0 th.th_frame 0 nfixed; + let rec mkrest i r = + if i < nfixed then r + else mkrest (i - 1) (Spair { car = av.(i); cdr = r }) + in + th.th_frame.(nfixed) <- mkrest (n - 1) Snull + end + else + Array.blit av 0 th.th_frame 0 p.proc_nargs; + eval th cc p.proc_body + +and eval th cc = + function + Cval v -> cc v + | Cseq2 (s1, s2) -> + eval th (fun _ -> eval th cc s2) s1 + | Cseq3 (s1, s2, s3) -> + eval th (fun _ -> eval th (fun _ -> eval th cc s3) s2) s1 + | Cseqn s -> + let n = Array.length s in + let rec loop i = + if i = n - 1 then + eval th cc s.(i) + else + eval th (fun _ -> loop (i + 1)) s.(i) + in + loop 0 + | Cand2 (s1, s2) -> + eval th (function Sfalse -> cc Sfalse | _ -> eval th cc s2) s1 + | Cand3 (s1, s2, s3) -> + eval th + (function + Sfalse -> cc Sfalse + | _ -> + eval th + (function + Sfalse -> cc Sfalse + | _ -> eval th cc s3) s2) s1 + | Candn s -> + let n = Array.length s in + let rec loop i = + begin + if i = n - 1 then + eval th cc s.(i) + else + eval th (function Sfalse -> cc Sfalse | _ -> loop (i + 1)) s.(i) + end + in + loop 0 + | Cor2 (s1, s2) -> + eval th (function Sfalse -> eval th cc s2 | x -> cc x) s1 + | Cor3 (s1, s2, s3) -> + eval th + (function + Sfalse -> eval th + (function + Sfalse -> eval th cc s3 + | x -> cc x) s2 + | x -> cc x) s1 + | Corn s -> + let n = Array.length s in + let rec loop i = + if i = n - 1 then + eval th cc s.(i) + else + eval th (function Sfalse -> loop (i + 1) | x -> cc x) s.(i) + in + loop 0 + | Cif (c, tx, fx) -> + eval th + (function Sfalse -> eval th cc fx | _ -> eval th cc tx) + c + | Csetg (g, c) -> + eval th (fun v -> + if g.g_val == Sunbound then + raise (Error ("set!: unbound variable: " ^ (sym_name g.g_sym))) + else + g.g_val <- v; cc Sunspec) c + | Csetl (d, i, c) -> + eval th (fun v -> setl th d i v; cc Sunspec) c + | Cdefine (g, c) -> + eval th (fun v -> g.g_val <- v; cc Sunspec) c + | Cgetg g -> + if g.g_val == Sunbound then + raise (Error ("unbound variable: " ^ (sym_name g.g_sym))) + else + cc g.g_val + | Cgetl (d, i) -> cc (getl th d i) + | Capply0 c -> + eval th (fun cv -> + chkargs cv 0; + match cv with + Sproc (p, d) -> doapply th cc p d [| |] + | Sprim p -> + begin + match p.prim_fun with + Pf0 f -> cc (f ()) + | Pfn f -> cc (f [| |]) + | Pfcn f -> f th cc [| |] + | _ -> assert false + end + | _ -> assert false) c + | Capply1 (c, a1) -> + eval th (fun cv -> eval th (fun a1v -> + chkargs cv 1; + match cv with + Sproc (p, d) -> doapply th cc p d [| a1v |] + | Sprim p -> + begin + match p.prim_fun with + Pf1 f -> cc (f a1v) + | Pfn f -> cc (f [| a1v |]) + | Pfcn f -> f th cc [| a1v |] + | _ -> assert false + end + | _ -> assert false) a1) c + | Capply2 (c, a1, a2) -> + eval th (fun cv -> eval th (fun a1v -> eval th (fun a2v -> + chkargs cv 2; + match cv with + Sproc (p, d) -> doapply th cc p d [| a1v; a2v |] + | Sprim p -> + begin + match p.prim_fun with + Pf2 f -> cc (f a1v a2v) + | Pfn f -> cc (f [| a1v; a2v |]) + | Pfcn f -> f th cc [| a1v; a2v |] + | _ -> assert false + end + | _ -> assert false) a2) a1) c + | Capply3 (c, a1, a2, a3) -> + eval th (fun cv -> eval th (fun a1v -> eval th (fun a2v -> + eval th (fun a3v -> + chkargs cv 3; + match cv with + Sproc (p, d) -> doapply th cc p d [| a1v; a2v; a3v |] + | Sprim p -> + begin + match p.prim_fun with + Pf3 f -> cc (f a1v a2v a3v) + | Pfn f -> cc (f [| a1v; a2v; a3v |]) + | Pfcn f -> f th cc [| a1v; a2v; a3v |] + | _ -> assert false + end + | _ -> assert false) a3) a2) a1) c + | Capplyn (c, a) -> + eval th (fun cv -> + let n = Array.length a in + let av = Array.make n Snull in + let rec loop i = + if i = n then + begin + chkargs cv n; + match cv with + Sproc (p, d) -> doapply th cc p d av + | Sprim p -> + begin + match p.prim_fun with + Pfn f -> cc (f av) + | Pfcn f -> f th cc av + | _ -> assert false + end + | _ -> assert false + end + else + eval th (fun x -> av.(i) <- x; loop (i + 1)) a.(i) + in + loop 0) c + | Clambda p -> + let n = th.th_depth + 1 in + let nd = Array.init n + (fun i -> if i < n - 1 then th.th_display.(i) + else th.th_frame) + in + cc (Sproc (p, nd)) + | Cqqp (h, t) -> + begin + match h with + Cqqspl x -> + eval th (fun usl -> eval th (fun t -> + let rec findtl = + function + Spair ({ car = _; cdr = Snull } as p) -> + p.cdr <- t; usl + | Spair { car = _; cdr = nt } -> findtl nt + | Snull -> t + | _ -> raise (Error "unquote-splicing: not a list") + in + cc (findtl usl)) t) x + | _ -> + eval th (fun h -> eval th (fun t -> + cc (Spair { car = h; cdr = t })) t) h + end + | Cqqv v -> + let n = Array.length v in + let qv = Array.make n Snull in + let rec loop i = + if i = n then + cc (Svector qv) + else + eval th (fun x -> qv.(i) <- x; loop (i + 1)) v.(i) + in + loop 0 + | Cqqvs l -> + begin + let rec loop r = + function + [] -> cc (Svector (Array.of_list r)) + | (Cqqspl x)::t -> + eval th (fun l -> loop ((list_to_caml l) @ r) t) x + | h::t -> + eval th (fun x -> loop (x::r) t) h + in + loop [] (List.rev l) + end + | Cqqspl x -> raise (Error "unquote-splicing: not valid here") + | Ccond av -> + begin + let n = Array.length av in + let rec loop i = + if i < n then + begin + match av.(i) with + (Ccondspec c, b) -> + eval th (fun v -> + if v <> Sfalse then eval th cc (Capply1 (b, Cval v)) + else loop (i + 1)) c + | (c, b) -> + eval th (fun v -> + if v <> Sfalse then eval th cc b + else loop (i + 1)) c + end + else + cc Sunspec + in + loop 0 + end + | Ccase (c, m) -> + eval th (fun v -> + let n = Array.length m in + let rec loop i = + if i < n then + begin + match m.(i) with + ([| |], b) -> eval th cc b + | (mv, b) -> + let n = Array.length mv in + let rec has i = + if i < n then + begin + let mvv = mv.(i) in + if mvv == v || test_eqv mvv v then true + else has (i + 1) + end + else + false + in + if has 0 then eval th cc b + else loop (i + 1) + end + else + cc Sunspec + in + loop 0) c + | Cdelay c -> + cc (Spromise { promise_code = c; + promise_val = None; + promise_th = Some { th with th_frame = th.th_frame } }) + | _ -> raise (Error "eval: internal error") +;; + diff --git a/ocs-1.0.3/src/ocs_eval.mli b/ocs-1.0.3/src/ocs_eval.mli new file mode 100644 index 0000000..10f9259 --- /dev/null +++ b/ocs-1.0.3/src/ocs_eval.mli @@ -0,0 +1,6 @@ +(* Evaluation *) + +open Ocs_types + +val eval : thread -> (sval -> unit) -> code -> unit + diff --git a/ocs-1.0.3/src/ocs_io.ml b/ocs-1.0.3/src/ocs_io.ml new file mode 100644 index 0000000..06fc612 --- /dev/null +++ b/ocs-1.0.3/src/ocs_io.ml @@ -0,0 +1,224 @@ +(* I/O primitives. *) + +open Ocs_types +open Ocs_error +open Ocs_env +open Ocs_eval +open Ocs_print + +let get_stdin th = + match th.th_stdin with + Sport p -> p + | _ -> assert false +;; + +let get_stdout th = + match th.th_stdout with + Sport p -> p + | _ -> assert false +;; + +let read th cc = + function + [| |] -> + cc (Ocs_read.read_from_port (get_stdin th)) + | [| Sport port |] -> cc (Ocs_read.read_from_port port) + | _ -> raise (Error "read: bad args") +;; + +let rdchr p cc = + match Ocs_port.getc p with + Some c -> cc (Schar c) + | None -> cc Seof +;; + +let read_char th cc = + function + [| |] -> rdchr (get_stdin th) cc + | [| Sport port |] -> rdchr port cc + | _ -> raise (Error "read-char: bad args") +;; + +let peekchr p cc = + match Ocs_port.getc p with + Some c -> + Ocs_port.ungetc p c; + cc (Schar c) + | None -> cc Seof +;; + +let peek_char th cc = + function + [| |] -> peekchr (get_stdin th) cc + | [| Sport port |] -> peekchr port cc + | _ -> raise (Error "peek-char: bad args") +;; + +let eof_object = + function + Seof -> Strue + | _ -> Sfalse +;; + +let chrdy p cc = + cc (if Ocs_port.char_ready p then Strue else Sfalse) +;; + +let char_ready th cc = + function + [| |] -> chrdy (get_stdin th) cc + | [| Sport port |] -> chrdy port cc + | _ -> raise (Error "char-ready?: bad args") +;; + +let display th cc = + function + [| obj |] -> + let p = get_stdout th in print p true obj; Ocs_port.flush p; cc Sunspec + | [| obj; Sport p |] -> print p true obj; Ocs_port.flush p; cc Sunspec + | _ -> raise (Error "display: bad args") +;; + +let write th cc = + function + [| obj |] -> + let p = get_stdout th in print p false obj; Ocs_port.flush p; cc Sunspec + | [| obj; Sport p |] -> print p false obj; Ocs_port.flush p; cc Sunspec + | _ -> raise (Error "write: bad args") +;; + +let write_char th cc = + function + [| Schar c |] -> + let p = get_stdout th in Ocs_port.putc p c; Ocs_port.flush p; cc Sunspec + | [| Schar c; Sport p |] -> Ocs_port.putc p c; Ocs_port.flush p; cc Sunspec + | _ -> raise (Error "write-char: bad args") +;; + +let newline th cc = + function + [| |] -> + let p = get_stdout th in Ocs_port.putc p '\n'; Ocs_port.flush p; cc Sunspec + | [| Sport p |] -> Ocs_port.putc p '\n'; Ocs_port.flush p; cc Sunspec + | _ -> raise (Error "newline: bad args") +;; + +let current_input th cc = + function + [| |] -> cc th.th_stdin + | _ -> raise (Error "current-input-port: bad args") +;; + +let current_output th cc = + function + [| |] -> cc th.th_stdout + | _ -> raise (Error "current-output-port: bad args") +;; + +let is_input = + function + Sport p -> if Ocs_port.is_input p then Strue else Sfalse + | _ -> Sfalse +;; + +let is_output = + function + Sport p -> if Ocs_port.is_output p then Strue else Sfalse + | _ -> Sfalse +;; + +let open_input_file = + function + Sstring s -> Sport (Ocs_port.open_input_port s) + | _ -> raise (Error "expected string as input file name") +;; + +let open_output_file = + function + Sstring s -> Sport (Ocs_port.open_output_port s) + | _ -> raise (Error "expected string as output file name") +;; + +let close_port = + function + Sport p -> Ocs_port.close p + | _ -> raise (Error "close-port: invalid argument") +;; + +let scm_close_port p = + close_port p; Sunspec +;; + +(* Note that the call-with-*-file functions close the port if the + procedure exits, so they must not be re-called using a captured + continuation after they exit once. Dynamic extents can't be used + for this because closing and reopening the file would be an even + bigger problem. *) + +let call_w_in th cc = + function + [| name; proc |] -> + let p = open_input_file name in + eval th (fun x -> close_port p; cc x) (Capply1 (Cval proc, Cval p)) + | _ -> raise (Error "call-with-input-file: bad args") +;; + +let call_w_out th cc = + function + [| name; proc |] -> + let p = open_output_file name in + eval th (fun x -> close_port p; cc x) (Capply1 (Cval proc, Cval p)) + | _ -> raise (Error "call-with-output-file: bad args") +;; + +let w_in th cc = + function + [| name; thunk |] -> + let p = open_input_file name in + eval { th with th_stdin = p } + (fun x -> close_port p; cc x) + (Capply0 (Cval thunk)) + | _ -> raise (Error "with-input-from-file: bad args") +;; + +let w_out th cc = + function + [| name; thunk |] -> + let p = open_output_file name in + eval { th with th_stdout = p } + (fun x -> close_port p; cc x) + (Capply0 (Cval thunk)) + | _ -> raise (Error "with-output-to-file: bad args") +;; + +let init e = + set_pfcn e read "read"; + set_pfcn e read_char "read-char"; + set_pfcn e peek_char "peek-char"; + set_pfcn e char_ready "char-ready?"; + + set_pf1 e eof_object "eof-object?"; + + set_pfcn e display "display"; + set_pfcn e newline "newline"; + set_pfcn e write "write"; + set_pfcn e write_char "write-char"; + + set_pfcn e current_input "current-input-port"; + set_pfcn e current_output "current-output-port"; + + set_pf1 e is_input "input-port?"; + set_pf1 e is_output "output-port?"; + + set_pf1 e open_input_file "open-input-file"; + set_pf1 e open_output_file "open-output-file"; + + set_pf1 e scm_close_port "close-input-port"; + set_pf1 e scm_close_port "close-output-port"; + + set_pfcn e call_w_in "call-with-input-file"; + set_pfcn e call_w_out "call-with-output-file"; + + set_pfcn e w_in "with-input-from-file"; + set_pfcn e w_out "with-output-to-file"; +;; diff --git a/ocs-1.0.3/src/ocs_io.mli b/ocs-1.0.3/src/ocs_io.mli new file mode 100644 index 0000000..0398a99 --- /dev/null +++ b/ocs-1.0.3/src/ocs_io.mli @@ -0,0 +1,11 @@ +(* I/O primitives *) + +open Ocs_types + +val read : thread -> (sval -> unit) -> sval array -> unit + +val open_input_file : sval -> sval +val open_output_file : sval -> sval + +val init : env -> unit + diff --git a/ocs-1.0.3/src/ocs_lex.ml b/ocs-1.0.3/src/ocs_lex.ml new file mode 100644 index 0000000..d4796ec --- /dev/null +++ b/ocs-1.0.3/src/ocs_lex.ml @@ -0,0 +1,279 @@ +(* Lexer for Scheme. *) + +open Ocs_types +open Ocs_error + +type token = + Leof + | Lopenv (* #( *) + | Lunqsplice (* ,@ *) + | Lident of string + | Lstring of string + | Lnumber of sval + | Lbool of sval + | Lchar of sval + | Ltoken of char + +type lexer = { + l_port : Ocs_port.port; + l_buf : Buffer.t; + l_name : string; + mutable l_line : int +} + +let make_lexer port name = + { l_port = port; + l_buf = Buffer.create 512; + l_name = name; + l_line = 0 } +;; + +let get_loc lex = + (lex.l_name, lex.l_line) +;; + +let lex_error lex err = + if String.length lex.l_name = 0 then + Error err + else + ErrorL (get_loc lex, err) +;; + +let num_w_base lex s = + let base = + match s.[0] with + 'B' | 'b' -> 2 + | 'D' | 'd' -> 10 + | 'O' | 'o' -> 8 + | 'X' | 'x' -> 16 + | _ -> raise (lex_error lex "invalid character literal") + and n = String.length s + in + let rec scn v i = + if i >= n then v + else + match s.[i] with + '0' .. '9' as c when (int_of_char c) - (int_of_char '0') < base -> + scn (v * base + (int_of_char c) - (int_of_char '0')) (i + 1) + | 'a' .. 'f' as c when base = 16 -> + scn (v * base + (int_of_char c) - (int_of_char 'a') + 10) (i + 1) + | 'A' .. 'F' as c when base = 16 -> + scn (v * base + (int_of_char c) - (int_of_char 'A') + 10) (i + 1) + | _ -> v (* Ignore trailing junk *) + in + scn 0 1 +;; + +let string_num_esc lex base n = + let rec scn v i = + if i >= n then char_of_int v + else + match Ocs_port.getc lex.l_port with + Some ('0' .. '9' as c) when + (int_of_char c) - (int_of_char '0') < base -> + scn (v * base + (int_of_char c) - (int_of_char '0')) (i + 1) + | Some ('a' .. 'f' as c) when base = 16 -> + scn (v * base + (int_of_char c) - (int_of_char 'a') + 10) (i + 1) + | Some ('A' .. 'F' as c) when base = 16 -> + scn (v * base + (int_of_char c) - (int_of_char 'A') + 10) (i + 1) + | Some c -> + Ocs_port.ungetc lex.l_port c; + char_of_int v + | None -> raise (lex_error lex "unexpected eof in string literal") + in + scn 0 0 +;; + +let read_char lex = + begin + match Ocs_port.getc lex.l_port with + Some c -> Buffer.add_char lex.l_buf c + | None -> raise (lex_error lex "unexpected eof") + end; + let rec loop () = + match Ocs_port.getc lex.l_port with + Some (('a' .. 'z' | 'A' .. 'Z' | '0' .. '9') as c) -> + Buffer.add_char lex.l_buf c; loop () + | Some c -> Ocs_port.ungetc lex.l_port c + | None -> () + in + loop (); + let s = Buffer.contents lex.l_buf in + if String.length s = 1 then + Lchar (Schar s.[0]) + else + match Ocs_char.name_to_char s with + Some c -> Lchar (Schar c) + | None -> Lchar (Schar (char_of_int (num_w_base lex s))) +;; + +let rec read_string lex = + match Ocs_port.getc lex.l_port with + Some '\"' -> Lstring (Buffer.contents lex.l_buf) + | Some '\\' -> + begin + match Ocs_port.getc lex.l_port with + Some ('N' | 'n') -> + Buffer.add_char lex.l_buf '\n'; + read_string lex + | Some ('R' | 'r') -> + Buffer.add_char lex.l_buf '\r'; + read_string lex + | Some ('T' | 't') -> + Buffer.add_char lex.l_buf '\t'; + read_string lex + | Some ('B' | 'b') -> + Buffer.add_char lex.l_buf (string_num_esc lex 2 8); + read_string lex + | Some ('D' | 'd') -> + Buffer.add_char lex.l_buf (string_num_esc lex 10 3); + read_string lex + | Some ('O' | 'o') -> + Buffer.add_char lex.l_buf (string_num_esc lex 8 3); + read_string lex + | Some ('X' | 'x') -> + Buffer.add_char lex.l_buf (string_num_esc lex 16 2); + read_string lex + | Some ('0' .. '9' as c) -> + Ocs_port.ungetc lex.l_port c; + Buffer.add_char lex.l_buf (string_num_esc lex 10 3); + read_string lex + | Some c -> + Buffer.add_char lex.l_buf c; + read_string lex + | None -> + raise (lex_error lex "unexpected eof in string literal") + end + | Some '\n' -> + lex.l_line <- lex.l_line + 1; + Buffer.add_char lex.l_buf '\n'; + read_string lex + | Some c -> + Buffer.add_char lex.l_buf c; + read_string lex + | None -> raise (lex_error lex "unexpected eof in string literal") +;; + +let rec read_ident lex = + match Ocs_port.getc lex.l_port with + Some (('a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '!' | '$' | '%' | '&' | + '*' | '/' | ':' | '<' | '=' | '>' | '?' | '^' | '_' | '~' | + '+' | '-' | '.' | '@') as c) -> + Buffer.add_char lex.l_buf c; + read_ident lex + | Some c -> + Ocs_port.ungetc lex.l_port c; + Lident (Buffer.contents lex.l_buf) + | None -> + Lident (Buffer.contents lex.l_buf) +;; + +let parse_number lex = + try + Lnumber (Ocs_numstr.string_to_num (Buffer.contents lex.l_buf) 0) + with + Error err -> raise (lex_error lex err) +;; + +(* When reading numbers, accept almost any characters that look + like they may be part of a number. Some extremely obfuscated + inputs may be misinterpreted. *) +let rec read_number lex = + match Ocs_port.getc lex.l_port with + Some (('0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' | '-' | '+' | '.' | + '#' | '/' | '@') as c) -> + Buffer.add_char lex.l_buf c; + read_number lex + | Some c -> + Ocs_port.ungetc lex.l_port c; + parse_number lex + | None -> + parse_number lex +;; + +let rec tok lex = + match Ocs_port.getc lex.l_port with + Some c -> + begin + match c with + '\n' -> lex.l_line <- lex.l_line + 1; tok lex + | ' ' | '\t' | '\r' | '\012' -> tok lex + | ';' -> + begin + let rec loop () = + match Ocs_port.getc lex.l_port with + Some '\n' -> lex.l_line <- lex.l_line + 1; tok lex + | Some _ -> loop () + | None -> Leof + in + loop () + end + | ',' -> + begin + match Ocs_port.getc lex.l_port with + Some '@' -> Lunqsplice + | Some c -> Ocs_port.ungetc lex.l_port c; Ltoken ',' + | None -> Ltoken ',' + end + | '#' -> + begin + match Ocs_port.getc lex.l_port with + Some ('f' | 'F') -> Lbool Sfalse + | Some ('t' | 'T') -> Lbool Strue + | Some (('B' | 'b' | 'D' | 'd' | 'O' | 'o' | 'X' | 'x' | + 'E' | 'e' | 'I' | 'i') as c) -> + Buffer.add_char lex.l_buf '#'; + Buffer.add_char lex.l_buf c; + read_number lex + | Some '\\' -> read_char lex + | Some '(' -> Lopenv + | Some c -> Ocs_port.ungetc lex.l_port c; Ltoken '#' + | None -> Ltoken '#' + end + | '\"' -> read_string lex + | '+' | '-' -> + begin + match Ocs_port.getc lex.l_port with + Some (('0' .. '9' | 'i' | 'I' | '.') as x) -> + Buffer.add_char lex.l_buf c; + Buffer.add_char lex.l_buf x; + read_number lex + | Some x -> + Ocs_port.ungetc lex.l_port x; + Lident (String.make 1 c) + | None -> Lident (String.make 1 c) + end + | '.' -> + begin + match Ocs_port.getc lex.l_port with + Some '.' -> + Buffer.add_string lex.l_buf ".."; + read_ident lex + | Some ('0' .. '9' as c) -> + Buffer.add_char lex.l_buf '.'; + Buffer.add_char lex.l_buf c; + read_number lex + | Some c -> + Ocs_port.ungetc lex.l_port c; + Ltoken '.' + | None -> Ltoken '.' + end + | '0' .. '9' -> + Buffer.add_char lex.l_buf c; + read_number lex + | 'a' .. 'z' | 'A' .. 'Z' | '!' | '$' | '%' | '&' | '*' | '/' + | ':' | '<' | '=' | '>' | '?' | '^' | '_' | '~' -> + begin + Buffer.add_char lex.l_buf c; + read_ident lex + end + | _ -> Ltoken c + end + | None -> Leof +;; + +let get_tok lex = + Buffer.clear lex.l_buf; + tok lex +;; + diff --git a/ocs-1.0.3/src/ocs_lex.mli b/ocs-1.0.3/src/ocs_lex.mli new file mode 100644 index 0000000..55892ac --- /dev/null +++ b/ocs-1.0.3/src/ocs_lex.mli @@ -0,0 +1,22 @@ +(* Lexer for Scheme. *) + +open Ocs_types +open Ocs_error + +type token = + Leof + | Lopenv (* #( *) + | Lunqsplice (* ,@ *) + | Lident of string + | Lstring of string + | Lnumber of sval + | Lbool of sval + | Lchar of sval + | Ltoken of char + +type lexer + +val make_lexer : Ocs_port.port -> string -> lexer +val get_loc : lexer -> location +val get_tok : lexer -> token + diff --git a/ocs-1.0.3/src/ocs_list.ml b/ocs-1.0.3/src/ocs_list.ml new file mode 100644 index 0000000..781d948 --- /dev/null +++ b/ocs-1.0.3/src/ocs_list.ml @@ -0,0 +1,302 @@ +(* List functionality. *) + +open Ocs_types +open Ocs_error +open Ocs_env +open Ocs_misc + +(* Primitives *) + +let make_list av = + let rec loop i r = + if i < 0 then r + else loop (i - 1) (Spair { car = Array.unsafe_get av i; cdr = r }) + in + loop (Array.length av - 1) Snull +;; + +let cons h t = + Spair { car = h; cdr = t } +;; + +let gcar = + function + Spair { car = r; cdr = _ } -> r + | _ -> raise (Error "car: bad args") +;; + +let gcdr = + function + Spair { car = _; cdr = r } -> r + | _ -> raise (Error "cdr: bad args") +;; + +let caar x = gcar (gcar x);; +let cadr x = gcar (gcdr x);; +let cdar x = gcdr (gcar x);; +let cddr x = gcdr (gcdr x);; + +let gcxr seq = + List.fold_left (fun f g -> fun x -> f (g x)) (fun x -> x) seq +;; + +let set_car l v = + match l with + Spair p -> p.car <- v; Sunspec + | _ -> raise (Error "set-car!: bad args") +;; + +let set_cdr l v = + match l with + Spair p -> p.cdr <- v; Sunspec + | _ -> raise (Error "set-cdr!: bad args") +;; + +let safe_length l = + let next = + function + Spair { car = _; cdr = t } -> t + | _ -> raise (Error "length: invalid list") in + let rec loop l r n = + if l == Snull then + n + else if n land 1 = 0 then + loop (next l) r (n + 1) + else if l == r then + raise (Error "length: loop detected") + else + loop (next l) (next r) (n + 1) + in + loop l l 0 +;; + +let is_list l = + try + let _ = safe_length l in Strue + with + _ -> Sfalse +;; + +let length l = + Sint (safe_length l) +;; + +let reverse l = + let rec loop nl = + function + Snull -> nl + | Spair { car = h; cdr = t } -> loop (Spair { car = h; cdr = nl }) t + | _ -> raise (Error "reverse: invalid list") + in + loop Snull l +;; + +(* Copy list and set tail, used by append *) +let cptl tl = + function + Snull -> tl + | Spair { car = h; cdr = t } -> + let nl = Spair { car = h; cdr = Snull } in + let rec loop = + function + Spair p -> + begin + function + Spair { car = h; cdr = t } -> + let n = Spair { car = h; cdr = Snull } in + p.cdr <- n; loop n t + | Snull -> p.cdr <- tl + | _ -> raise (Error "append: bad list") + end + | _ -> assert false + in + loop nl t; nl + | _ -> raise (Error "append: bad list") +;; + +let append = + function + [| |] -> Snull + | av -> + let n = Array.length av in + let rec loop i tl = + if i >= 0 then + loop (i - 1) (cptl tl av.(i)) + else + tl + in + loop (n - 2) av.(n - 1) +;; + +let list_tail l = + function + Sint k -> + begin + let rec tail i x = + if i = 0 then x + else + match x with + Spair { car = _; cdr = t } -> tail (i - 1) t + | _ -> raise (Error "list-tail: bad list") + in + tail k l + end + | _ -> raise (Error "list-tail: bad args") +;; + +let list_ref l k = + match list_tail l k with + Spair { car = x; cdr = _ } -> x + | _ -> raise (Error "list-ref: bad args") +;; + +let rec memq o = + function + Snull -> Sfalse + | Spair { car = x; cdr = t } as p -> if o == x then p else memq o t + | _ -> raise (Error "memq: bad list") +;; + +let rec memv o = + function + Snull -> Sfalse + | Spair { car = x; cdr = t } as p -> + if o == x || test_eqv o x then p else memv o t + | _ -> raise (Error "memv: bad list") +;; + +let rec member o = + function + Snull -> Sfalse + | Spair { car = x; cdr = t } as p -> + if o == x || test_equal o x then p else member o t + | _ -> raise (Error "member: bad list") +;; + +let rec assq o = + function + Snull -> Sfalse + | Spair { car = Spair { car = x; cdr = _ } as p; cdr = t } -> + if o == x then p else assq o t + | _ -> raise (Error "assq: bad list") +;; + +let rec assv o = + function + Snull -> Sfalse + | Spair { car = Spair { car = x; cdr = _ } as p; cdr = t } -> + if o == x || test_eqv o x then p else assv o t + | _ -> raise (Error "assv: bad list") +;; + +let rec assoc o = + function + Snull -> Sfalse + | Spair { car = Spair { car = x; cdr = _ } as p; cdr = t } -> + if o == x || test_equal o x then p else assv o t + | _ -> raise (Error "assoc: bad list") +;; + +let list_to_vector = + function + Snull -> Svector [| |] + | Spair _ as l -> + let n = safe_length l in + let v = Array.make n Snull in + let rec loop i l = + if i < n then + begin + match l with + Spair { car = h; cdr = t } -> v.(i) <- h; loop (i + 1) t + | _ -> assert false (* length was wrong? *) + end + else + () + in + loop 0 l; + Svector v + | _ -> raise (Error "list->vector: bad args") +;; + +let list_to_string = + function + Snull -> Sstring "" + | Spair _ as l -> + let n = safe_length l in + let s = String.create n in + let rec loop i l = + if i < n then + begin + match l with + Spair { car = Schar c; cdr = t } -> s.[i] <- c; loop (i + 1) t + | _ -> raise (Error "list->string: non-characters in list") + end + else + () + in + loop 0 l; + Sstring s + | _ -> raise (Error "list->string: bad args") +;; + +let init e = + set_pf1 e is_list "list?"; + set_pfn e make_list "list"; + set_pf2 e cons "cons"; + + set_pf1 e gcar "car"; + set_pf1 e gcdr "cdr"; + + set_pf1 e caar "caar"; + set_pf1 e cadr "cadr"; + set_pf1 e cdar "cdar"; + set_pf1 e cddr "cddr"; + + set_pf1 e (gcxr [ gcar; gcar; gcar ]) "caaar"; + set_pf1 e (gcxr [ gcar; gcar; gcdr ]) "caadr"; + set_pf1 e (gcxr [ gcar; gcdr; gcar ]) "cadar"; + set_pf1 e (gcxr [ gcar; gcdr; gcdr ]) "caddr"; + set_pf1 e (gcxr [ gcdr; gcar; gcar ]) "cdaar"; + set_pf1 e (gcxr [ gcdr; gcar; gcdr ]) "cdadr"; + set_pf1 e (gcxr [ gcdr; gcdr; gcar ]) "cddar"; + set_pf1 e (gcxr [ gcdr; gcdr; gcdr ]) "cdddr"; + + set_pf1 e (gcxr [ gcar; gcar; gcar; gcar ]) "caaaar"; + set_pf1 e (gcxr [ gcar; gcar; gcar; gcdr ]) "caaadr"; + set_pf1 e (gcxr [ gcar; gcar; gcdr; gcar ]) "caadar"; + set_pf1 e (gcxr [ gcar; gcar; gcdr; gcdr ]) "caaddr"; + set_pf1 e (gcxr [ gcar; gcdr; gcar; gcar ]) "cadaar"; + set_pf1 e (gcxr [ gcar; gcdr; gcar; gcdr ]) "cadadr"; + set_pf1 e (gcxr [ gcar; gcdr; gcdr; gcar ]) "caddar"; + set_pf1 e (gcxr [ gcar; gcdr; gcdr; gcdr ]) "cadddr"; + set_pf1 e (gcxr [ gcdr; gcar; gcar; gcar ]) "cdaaar"; + set_pf1 e (gcxr [ gcdr; gcar; gcar; gcdr ]) "cdaadr"; + set_pf1 e (gcxr [ gcdr; gcar; gcdr; gcar ]) "cdadar"; + set_pf1 e (gcxr [ gcdr; gcar; gcdr; gcdr ]) "cdaddr"; + set_pf1 e (gcxr [ gcdr; gcdr; gcar; gcar ]) "cddaar"; + set_pf1 e (gcxr [ gcdr; gcdr; gcar; gcdr ]) "cddadr"; + set_pf1 e (gcxr [ gcdr; gcdr; gcdr; gcar ]) "cdddar"; + set_pf1 e (gcxr [ gcdr; gcdr; gcdr; gcdr ]) "cddddr"; + + set_pf2 e set_car "set-car!"; + set_pf2 e set_cdr "set-cdr!"; + + set_pf1 e length "length"; + set_pf1 e reverse "reverse"; + + set_pfn e append "append"; + + set_pf2 e list_tail "list-tail"; + set_pf2 e list_ref "list-ref"; + + set_pf2 e memq "memq"; + set_pf2 e memv "memv"; + set_pf2 e member "member"; + set_pf2 e assq "assq"; + set_pf2 e assv "assv"; + set_pf2 e assoc "assoc"; + + set_pf1 e list_to_vector "list->vector"; + set_pf1 e list_to_string "list->string"; +;; + diff --git a/ocs-1.0.3/src/ocs_list.mli b/ocs-1.0.3/src/ocs_list.mli new file mode 100644 index 0000000..1b9c1da --- /dev/null +++ b/ocs-1.0.3/src/ocs_list.mli @@ -0,0 +1,6 @@ +(* List functionality. *) + +open Ocs_types + +val init : env -> unit + diff --git a/ocs-1.0.3/src/ocs_macro.ml b/ocs-1.0.3/src/ocs_macro.ml new file mode 100644 index 0000000..e9afbc6 --- /dev/null +++ b/ocs-1.0.3/src/ocs_macro.ml @@ -0,0 +1,438 @@ +(* Macro definitions and expanders. *) + +open Ocs_types +open Ocs_error +open Ocs_sym +open Ocs_env +open Ocs_compile +open Ocs_misc + +(* Pattern/template type in syntax rules. *) +type pattern = + Pkeyword of env * sval + | Pvar of sval + | Pvalue of sval + | Ppair of pattern * pattern + | Pvector of pattern array + | Pmulti of pattern * pattern (* pattern ... in a list *) + | Pvmulti of pattern (* pattern ... in a vector *) + | Pmvector of pattern list (* Vector with Pvmulti patterns *) + +type ruleset = { + mutable r_rules : (pattern * pattern) list +} +(* Values of pattern variables *) +type pattvar = + Vitem of sval + | Vmulti of pattvar list + +let rec var_name = + function + Sesym (_, s) -> var_name s + | Ssymbol _ as s -> sym_name s + | _ -> assert false +;; + +let kw_name = + function + Vkeyword s -> s + | _ -> assert false +;; + +(* Parse a pattern or template *) +let parseptt e patt dovar = + let rec parse = + function + Spair { car = h; cdr = t } -> + let x = parse h in + begin + match t with (* Special case, ... *) + Spair { car = (Ssymbol _ | Sesym (_, _)) as s; cdr = t } + when is_keyword e s "..." -> Pmulti (x, parse t) + | _ -> Ppair (x, parse t) + end + | (Ssymbol _ | Sesym (_, _)) as s -> dovar s + | Svector v -> + let n = Array.length v + and has_multi = ref false in + let rec loop r i = + if i < 0 then + r + else if safe_is_keyword e v.(i) "..." && i > 0 then + begin + has_multi := true; + loop (Pvmulti (parse v.(i - 1))::r) (i - 2) + end + else + loop (parse v.(i)::r) (i - 1) + in + let nl = loop [] (n - 1) in + if !has_multi then + Pmvector nl + else + Pvector (Array.of_list nl) + | x -> Pvalue x + in + parse patt +;; + +let rec match_sym s s' = + match (s, s') with + Sesym (e, s), Sesym (e', s') -> e == e' && match_sym s s' + | _, _ -> s == s' +;; + +let parsepatt e litlist patt = + let vars = ref [] in + let p = parseptt e patt + (fun s -> + try + let _ = List.find (fun s' -> match_sym s s') litlist in + Pkeyword (e, s) + with Not_found -> + vars := s::!vars; + Pvar s) + in + (!vars, p) +;; + +let parsetmpl e varlist tmpl = + let assocvar = + function + Sesym (e, s) as sym -> + begin + try + Pvar (List.find (function + Sesym (e', s') -> e' == e && s' == s + | _ -> false) varlist) + with Not_found -> + Pvalue sym + end + | s -> if List.memq s varlist then Pvar s else Pvalue s + in + parseptt e tmpl assocvar +;; + +let parserule e ll = + function + Spair { car = Spair { car = _; cdr = patt }; + cdr = Spair { car = tmpl; cdr = Snull }} -> + let (vars, patt) = parsepatt e ll patt in + let tmpl = parsetmpl e vars tmpl in + (patt, tmpl) + | _ -> raise (Error "syntax definition: invalid syntax rule") +;; + +let parsetspec e sym = + function + Spair { car = (Ssymbol _ | Sesym (_, _)) as s; + cdr = Spair { car = literals; cdr = rules }} + when is_keyword e s "syntax-rules" -> + let litlist = list_to_caml literals in + List.map (parserule e litlist) (list_to_caml rules) + | _ -> raise (Error "syntax definition: invalid transformer spec") +;; + +let rebuild a = + let rec loop i r = + if i < 0 then + r + else + loop (i - 1) (Spair { car = a.(i); cdr = r }) + in + loop (Array.length a - 1) Snull +;; + +(* Given a pattern, return an association list of pattern variables + with empty multiple value lists. *) +let rec empty_vars = + function + Pvar v -> [ v, Vmulti [] ] + | Ppair (h, t) -> (empty_vars h) @ (empty_vars t) + | Pmulti (p, t) -> (empty_vars p) @ (empty_vars t) + | Pvmulti p -> empty_vars p + | Pvector v -> + Array.fold_left (@) [] (Array.map empty_vars v) + | Pmvector l -> + List.fold_left (@) [] (List.map empty_vars l) + | _ -> [] +;; + +(* Merge an association list of multi-vars and a set of values *) +let merge_vars mv vs = + let rec merge = + function + (v, Vmulti m)::t -> (v, Vmulti (m @ [ List.assq v vs ]))::(merge t) + | [] -> [] + | _ -> assert false + in + merge mv +;; + +let rec normalize_name = + function + Ssymbol s -> s + | Sesym (_, s) -> normalize_name s + | _ -> "" +;; + +let rec match_patt e patt expr = + let vars = ref [] in + let rec match_sub p x = + match p with + Pkeyword (e', s') -> + begin + match x with + Ssymbol _ | Sesym (_, _) -> (get_var e x) == (get_var e' s') + | _ -> false + end + | Pvar v -> vars := (v, Vitem x)::!vars; true + | Pvalue v -> test_equal v x + | Ppair (h, t) -> + begin + match x with + Spair { car = xh; cdr = xt } -> match_sub h xh && match_sub t xt + | _ -> false + end + | Pvector v -> + begin + match x with + Svector sv -> + let n = Array.length v in + if Array.length sv <> n then + false + else + let rec loop i = + if i = n then + true + else if match_sub v.(i) sv.(i) then + loop (i + 1) + else + false + in + loop 0 + | _ -> false + end + | Pmulti (p, t) -> + if t <> (Pvalue Snull) then + raise (Error "invalid pattern"); + begin + let rec loop r = + function + Spair { car = h; cdr = t } -> + begin + match match_patt e p h with + Some v -> loop (merge_vars r v) t + | None -> None + end + | Snull -> Some r + | _ -> None + in + match loop (empty_vars p) x with + Some vl -> vars := vl @ !vars; true + | None -> false + end + | Pmvector l -> + begin + match x with + Svector v -> + begin + let n = Array.length v in + let rec loop i = + function + Pvmulti p::t -> + if i >= n then + begin + vars := (empty_vars p) @ !vars; + loop i t + end + else + begin + let rec mloop r i = + if i >= n then Some r + else + match match_patt e p v.(i) with + Some v -> mloop (merge_vars r v) (i + 1) + | None -> None + in + match mloop (empty_vars p) i with + Some vl -> vars := vl @ !vars; true + | None -> false + end + | h::t -> + if i >= n then false + else if match_sub h v.(i) then loop (i + 1) t + else false + | [] -> i >= n + in + loop 0 l + end + | _ -> false + end + | _ -> assert false + in + if match_sub patt expr then + Some !vars + else + None +;; + +(* Test whether a variable occurs in a pattern *) +let var_in_patt p (v, _) = + let rec is_in = + function + Pvar pv -> pv == v + | Ppair (h, t) -> is_in h || is_in t + | Pmulti (p, t) -> is_in p || is_in t + | Pvmulti p -> is_in p + | Pvector v -> + let rec loop i = + if i < 0 then false + else if is_in v.(i) then true + else loop (i - 1) + in + loop (Array.length v - 1) + | Pmvector l -> List.exists is_in l + | _ -> false + in + is_in p +;; + +(* Select variables that are applicable to this pattern *) +let subvars vl p = + List.filter (var_in_patt p) vl +;; + +(* Get the current values of variables, if available. *) +let varvals vl = + try + let l = List.map + (function + (v, Vmulti (x::_)) -> (v, x) + | (v, Vmulti []) -> raise Not_found + | x -> x) vl + in + if l = [] then + raise (Error "bad template") + else + (true, l) + with Not_found -> (false, []) +;; + +(* Get the next position in the variable list. *) +let varnext vl = + List.map (function (v, Vmulti (_::t)) -> (v, Vmulti t) | x -> x) vl +;; + +let rec expand_var = + function + Vitem x -> x + | Vmulti m -> make_slist Snull (List.rev_map expand_var m) +;; + +let rec expand_tmpl e tmpl vars = + let rec fix_syms = + function + Ssymbol _ as s -> Sesym (e, s) + | Spair { car = h; cdr = t } -> + Spair { car = fix_syms h; cdr = fix_syms t } + | Svector v -> + Svector (Array.map (fun x -> fix_syms x) v) + | x -> x in + let rec expand_sub = + function + Pvar v -> expand_var (List.assq v vars) + | Pvalue v -> fix_syms v + | Ppair (h, t) -> + Spair { car = expand_sub h; cdr = expand_sub t } + | Pvector v -> + Svector (Array.map (fun x -> expand_sub x) v) + | Pmulti (p, t) -> + let rec loop r v = + let (ok, vv) = varvals v in + if ok then loop ((expand_tmpl e p vv)::r) (varnext v) + else r + in + make_slist (expand_sub t) (loop [] (subvars vars p)) + | Pmvector l -> + begin + let rec loop r = + function + Pvmulti p::t -> + let rec mloop r v = + let (ok, vv) = varvals v in + if ok then mloop ((expand_tmpl e p vv)::r) (varnext v) + else r + in + loop (mloop [] (subvars vars p) @ r) t + | h::t -> loop (expand_sub h::r) t + | [] -> r + in + Svector (Array.of_list (List.rev (loop [] l))) + end + | _ -> assert false + in + expand_sub tmpl +;; + +let expand name me rs e av = + let me = new_scope me in + let al = rebuild av in + let rec try_rule = + function + (patt, tmpl)::t -> + begin + match match_patt e patt al with + Some vars -> expand_tmpl me tmpl vars + | None -> try_rule t + end + | [] -> raise (Error (name ^ ": no matching syntax rule")) + in + try_rule rs.r_rules +;; + +let mkdefine_syntax e = + function + [| (Ssymbol _ | Sesym (_, _)) as sym; tspec |] -> + let rules = parsetspec (new_scope e) sym tspec in + bind_name e sym (Vmacro (expand (normalize_name sym) e + { r_rules = rules })); + Cval Sunspec + | _ -> raise (Error "define-syntax: bad args") +;; + +let mklet_syntax e args = + if Array.length args < 2 then + raise (Error "let-syntax: too few args"); + let av = + Array.map (letsplit (fun s v -> s, + Vmacro (expand (normalize_name s) e { r_rules = parsetspec e s v }))) + (Array.of_list (list_to_caml args.(0))) in + let ne = new_scope e in + Array.iter (fun (s, r) -> bind_name ne s r) av; + mkseq (mkbody ne (Array.sub args 1 (Array.length args - 1))) +;; + +let mkletrec_syntax e args = + if Array.length args < 2 then + raise (Error "letrec-syntax: too few args"); + let ne = new_scope e in + let t = + Array.map (letsplit + (fun s v -> let r = { r_rules = [] } + and name = normalize_name s in + bind_name ne s (Vmacro (expand name ne r)); + r, s, v)) + (Array.of_list (list_to_caml args.(0))) + in + Array.iter (fun (r, s, v) -> r.r_rules <- parsetspec (new_scope e) s v) t; + mkseq (mkbody ne (Array.sub args 1 (Array.length args - 1))) +;; + +let bind_macro e = + bind_name e sym_define_syntax (Vsyntax mkdefine_syntax); + bind_name e sym_let_syntax (Vsyntax mklet_syntax); + bind_name e sym_letrec_syntax (Vsyntax mkletrec_syntax); + bind_name e sym_ellipsis (Vkeyword "...") +;; + diff --git a/ocs-1.0.3/src/ocs_macro.mli b/ocs-1.0.3/src/ocs_macro.mli new file mode 100644 index 0000000..80b6b20 --- /dev/null +++ b/ocs-1.0.3/src/ocs_macro.mli @@ -0,0 +1,6 @@ +(* Syntax definitions and expansions. *) + +open Ocs_types + +val bind_macro : env -> unit + diff --git a/ocs-1.0.3/src/ocs_main.ml b/ocs-1.0.3/src/ocs_main.ml new file mode 100644 index 0000000..36a4bb7 --- /dev/null +++ b/ocs-1.0.3/src/ocs_main.ml @@ -0,0 +1,28 @@ +(* Main program entry point. *) + +open Ocs_types +open Ocs_error + +let main () = + let loadf = ref [] in + let addf x = loadf := !loadf @ [x] in + let argspec = [ + ("file", Arg.Rest addf, "Files to run in batch mode") + ] in + Arg.parse argspec addf "Usage: ocscm [ file ... ]"; + if !loadf = [] then + Ocs_top.interactive () + else + let e = Ocs_top.make_env () + and th = Ocs_top.make_thread () in + try + List.iter (fun x -> Ocs_prim.load_file e th x) !loadf + with + Error err -> + Printf.eprintf "Error: %s\n" err + | ErrorL ((file, line), err) -> + Printf.eprintf "%s:%d: %s\n" file line err +;; + +main ();; + diff --git a/ocs-1.0.3/src/ocs_misc.ml b/ocs-1.0.3/src/ocs_misc.ml new file mode 100644 index 0000000..d72ebf6 --- /dev/null +++ b/ocs-1.0.3/src/ocs_misc.ml @@ -0,0 +1,77 @@ +(* Miscellaneous utility functions *) + +open Ocs_types +open Ocs_error + +let list_to_caml l = + let rec loop r = + function + Snull -> List.rev r + | Spair p -> loop (p.car::r) p.cdr + | _ -> raise (Error "not a valid list") + in + loop [] l +;; + +(* Create a Scheme list from a reversed native list. *) +let make_slist tl l = + let rec loop r = + function + h::t -> loop (Spair { car = h; cdr = r }) t + | [] -> r + in + loop tl l +;; + +(* Create one of Capply[0123n] depending on the number of arguments. *) +let mkapply f av = + match av with + [| |] -> Capply0 (f) + | [| a1 |] -> Capply1 (f, a1) + | [| a1; a2 |] -> Capply2 (f, a1, a2) + | [| a1; a2; a3 |] -> Capply3 (f, a1, a2, a3) + | av -> Capplyn (f, av) +;; + +(* Test equivalence (as in eqv?) *) + +let test_eqv a b = + if a == b then true + else + match (a, b) with + (Sint i1, Sint i2) -> i1 = i2 + | (Schar c1, Schar c2) -> c1 = c2 + | (Sreal r1, Sreal r2) -> r1 = r2 + | (Sbigint bi1, Sbigint bi2) -> Big_int.compare_big_int bi1 bi2 = 0 + | (Srational r1, Srational r2) -> Ratio.compare_ratio r1 r2 = 0 + | (Scomplex z1, Scomplex z2) -> z1 = z2 + | (Sstring s1, Sstring s2) -> s1 = s2 + | (Svector v1, Svector v2) -> v1 == v2 + | _ -> false +;; + +(* Test equality (as in equal?) *) + +let rec test_equal a b = + if a == b then true + else + match (a, b) with + (Svector v1, Svector v2) -> + let n = Array.length v1 in + if Array.length v2 <> n then + false + else + let rec loop i = + if i >= n then + true + else + if test_equal v1.(i) v2.(i) then + loop (i + 1) + else + false + in + loop 0 + | (Spair p1, Spair p2) -> + test_equal p1.car p2.car && test_equal p1.cdr p2.cdr + | _ -> test_eqv a b +;; diff --git a/ocs-1.0.3/src/ocs_misc.mli b/ocs-1.0.3/src/ocs_misc.mli new file mode 100644 index 0000000..229ec35 --- /dev/null +++ b/ocs-1.0.3/src/ocs_misc.mli @@ -0,0 +1,12 @@ +(* Miscellaneous utility functions *) + +open Ocs_types + +val list_to_caml : sval -> sval list + +val make_slist : sval -> sval list -> sval + +val mkapply : code -> code array -> code + +val test_eqv : sval -> sval -> bool +val test_equal : sval -> sval -> bool diff --git a/ocs-1.0.3/src/ocs_num.ml b/ocs-1.0.3/src/ocs_num.ml new file mode 100644 index 0000000..5de1fc9 --- /dev/null +++ b/ocs-1.0.3/src/ocs_num.ml @@ -0,0 +1,633 @@ +(* Handle number types (Sint, Sreal, Sbigint, Srational, Scomplex) *) + +open Ocs_types +open Ocs_error +open Ocs_env +open Ocs_numaux +open Ocs_complex + +open Num +open Ratio +open Big_int + +let rec negate = + function + (Sint i) as s -> + if i >= min_int then Sint (-i) + else negate (promote_bigint s) + | Sbigint bi -> Sbigint (minus_big_int bi) + | Srational r -> Srational (Ratio.minus_ratio r) + | Sreal r -> Sreal (-.r) + | Scomplex z -> Scomplex (Complex.neg z) + | _ -> raise (Error "bad number type") +;; + +let add2 a b = + match (a, b) with + (Sint i1, Sint i2) -> + let r = i1 + i2 in + if (i1 lxor i2) lor (i1 lxor (r lxor (-1))) < 0 + then Sint r + else Sbigint (add_big_int (big_int_of_int i1) (big_int_of_int i2)) + | (Scomplex z1, Scomplex z2) -> Scomplex (Complex.add z1 z2) + | (Scomplex z, o) | (o, Scomplex z) -> + Scomplex (Complex.add z { Complex.re = float_of_snum o; + Complex.im = 0.0 }) + | (Sreal r, o) | (o, Sreal r) -> + Sreal ((float_of_snum o) +. r) + | (Srational r, o) | (o, Srational r) -> + Srational (add_ratio (rational_of_snum o) r) + | (Sbigint bi, o) | (o, Sbigint bi) -> + bigint_res (add_big_int (bigint_of_snum o) bi) + | _ -> raise (Error "add: bad types") +;; + +let sub2 a b = + add2 a (negate b) +;; + +let mul2 a b = + match snum_fixtypes a b with + (Scomplex z1, Scomplex z2) -> Scomplex (Complex.mul z1 z2) + | (Sreal r1, Sreal r2) -> Sreal (r1 *. r2) + | (Srational r1, Srational r2) -> + snum_of_num (mult_num (Ratio r1) (Ratio r2)) + | (Sbigint bi1, Sbigint bi2) -> + snum_of_num (mult_num (Big_int bi1) (Big_int bi2)) + | (Sint i1, Sint i2) -> + snum_of_num (mult_num (Int i1) (Int i2)) + | _ -> raise (Error "mul: invalid args") +;; + +let div2 a b = + match snum_fixtypes a b with + (Scomplex n, Scomplex d) -> Scomplex (Complex.div n d) + | (Sreal n, Sreal d) -> Sreal (n /. d) + | (Srational n, Srational d) -> + snum_of_num (div_num (Ratio n) (Ratio d)) + | (Sbigint n, Sbigint d) -> + snum_of_num (div_num (Big_int n) (Big_int d)) + | (Sint n, Sint d) -> + if d = 0 then + raise (Error "division by zero") + else + snum_of_num (div_num (Int n) (Int d)) + | _ -> raise (Error "div: invalid args") +;; + +let cmp2 eq_only a b = + match (a, b) with + (Sint i1, Sint i2) -> + if i1 > i2 then 1 else if i1 < i2 then -1 else 0 + | _ -> + begin + match snum_fixtypes a b with + (Sreal r1, Sreal r2) -> + let r = r1 -. r2 in + if r > 0.0 then 1 else if r < 0.0 then -1 else 0 + | (Scomplex z1, Scomplex z2) -> + if eq_only then + if z1 = z2 then 0 else 1 + else + if z1.Complex.im = 0.0 && z2.Complex.im = 0.0 then + begin + let r = z1.Complex.re -. z2.Complex.re in + if r > 0.0 then 1 else if r < 0.0 then -1 else 0 + end + else + raise (Error "complex numbers compared") + | (Srational r1, Srational r2) -> + compare_ratio r1 r2 + | (Sbigint bi1, Sbigint bi2) -> + compare_big_int bi1 bi2 + | _ -> raise (Error "cmp: invalid args") + end +;; + +let is_exact = + function + (Sint _ | Sbigint _ | Srational _) -> Strue + | _ -> Sfalse +;; + +let is_inexact = + function + (Sreal _ | Scomplex _) -> Strue + | _ -> Sfalse +;; + +let to_exact = + function + Sreal r -> float_to_exact r + | Scomplex z -> + if z.Complex.im = 0.0 then + float_to_exact z.Complex.re + else + raise (Error "inexact->exact: no exact complex representation") + | (Sint _ | Sbigint _ | Srational _) as n -> n + | _ -> raise (Error "inexact->exact: not a number") +;; + +let to_inexact = + function + Sreal _ as r -> r + | Scomplex _ as z -> z + | x -> Sreal (float_of_snum x) +;; + +let mkbool b = if b then Strue else Sfalse;; + +let is_zero = + function + Sint i -> mkbool (i = 0) + | (Sbigint _ | Srational _) -> Sfalse + | Sreal r -> mkbool (r = 0.0) + | Scomplex z -> mkbool (z.Complex.re = 0.0 && z.Complex.im = 0.0) + | _ -> Sfalse +;; + +let is_positive = + function + Sint i -> mkbool (i > 0) + | Sbigint bi -> mkbool (sign_big_int bi > 0) + | Srational r -> mkbool (sign_ratio r > 0) + | Sreal r -> mkbool (r > 0.0) + | _ -> raise (Error "positive?: bad arg type") +;; + +let is_negative = + function + Sint i -> mkbool (i < 0) + | Sbigint bi -> mkbool (sign_big_int bi < 0) + | Srational r -> mkbool (sign_ratio r < 0) + | Sreal r -> mkbool (r < 0.0) + | _ -> raise (Error "positive?: bad arg type") +;; + +let is_number = + function + Sint _ | Sbigint _ | Srational _ | Sreal _ | Scomplex _ -> Strue + | _ -> Sfalse +;; + +let is_real = + function + Sint _ | Sbigint _ | Srational _ | Sreal _ -> Strue + | Scomplex { Complex.re = _; Complex.im = i } -> mkbool (i = 0.0) + | _ -> Sfalse +;; + +let is_rational = + is_real +;; + +let is_integer = + function + Sint _ | Sbigint _ -> Strue + | Sreal r -> mkbool (float_is_int r) + | Scomplex z -> mkbool (z.Complex.im = 0.0 && float_is_int z.Complex.re) + | _ -> Sfalse +;; + +let bi_modi bi i = + int_of_big_int (mod_big_int bi (big_int_of_int i)) +;; + +let is_even = + function + Sint i -> mkbool (i land 1 = 0) + | Sbigint bi -> mkbool (bi_modi bi 2 = 0) + | _ -> raise (Error "even?: bad arg type") +;; + +let is_odd = + function + Sint i -> mkbool (i land 1 <> 0) + | Sbigint bi -> mkbool (bi_modi bi 2 <> 0) + | _ -> raise (Error "odd?: bad arg type") +;; + +let do_ops op av n = + let rec oploop v i = + if i < n then + let r = op v av.(i) in + oploop r (i + 1) + else + v + in + oploop av.(0) 1 +;; + +let snum_add av = + let n = Array.length av in + if n = 0 then + Sint 0 + else + do_ops add2 av n +;; + +let snum_sub av = + match Array.length av with + 0 -> raise (Error "-: need args") + | 1 -> negate av.(0) + | n -> do_ops sub2 av n +;; + +let snum_mul av = + match Array.length av with + 0 -> Sint 1 + | n -> do_ops mul2 av n +;; + +let snum_div av = + match Array.length av with + 0 -> raise (Error "/: need args") + | 1 -> div2 (Sint 1) av.(0) + | n -> do_ops div2 av n +;; + +let snum_eq av = + match Array.length av with + 0 | 1 -> Strue + | n -> + let a0 = av.(0) in + let rec loop i = + if i < n then + begin + if cmp2 true a0 av.(i) <> 0 then Sfalse + else loop (i + 1) + end + else + Strue + in + loop 1 +;; + +let snum_rel op av = + match Array.length av with + 0 | 1 -> Strue + | n -> + let rec loop v i = + if i < n then + begin + if op (cmp2 false v av.(i)) 0 then loop av.(i) (i + 1) + else Sfalse + end + else + Strue + in + loop av.(0) 1 +;; + +let snum_minormax op av = + match Array.length av with + 0 -> raise (Error "args required") + | 1 -> av.(0) + | n -> + let inex = ref false in + let r = do_ops (fun a b -> + if is_inexact a = Strue || is_inexact b = Strue then + inex := true; + if op (cmp2 false a b) 0 then a else b) av n + in + if !inex then to_inexact r else r +;; + +let snum_abs = + function + Sint i -> Sint (abs i) + | Sbigint bi -> Sbigint (abs_big_int bi) + | Sreal r -> Sreal (abs_float r) + | Srational r -> Srational (abs_ratio r) + | Scomplex _ -> raise (Error "abs: number is complex") + | _ -> raise (Error "abs: not a number") +;; + +let snum_floor = + function + (Sint _ | Sbigint _) as x -> x + | Srational r -> bigint_res (floor_ratio r) + | Sreal r -> Sreal (floor r) + | _ -> raise (Error "floor: bad arg type") +;; + +let snum_ceiling = + function + (Sint _ | Sbigint _) as x -> x + | Srational r -> bigint_res (ceiling_ratio r) + | Sreal r -> Sreal (ceil r) + | _ -> raise (Error "ceiling: bad arg type") +;; + +let snum_truncate = + function + (Sint _ | Sbigint _) as x -> x + | Srational r -> bigint_res (integer_ratio r) + | Sreal r -> + if r < 0.0 then + Sreal (ceil r) + else + Sreal (floor r) + | _ -> raise (Error "truncate: bad arg type") +;; + +let snum_round = + function + (Sint _ | Sbigint _) as x -> x + | Srational r -> bigint_res (round_ratio r) + | Sreal r -> Sreal (round_float r) + | _ -> raise (Error "round: bad arg type") +;; + +let rcsw rfun cfun = + function + Scomplex z -> Scomplex (cfun z) + | x -> Sreal (rfun (float_of_snum x)) +;; + +let snum_exp = rcsw exp Complex.exp;; +let snum_log = rcsw log Complex.log;; + +let snum_sin = rcsw sin sin_cplx;; +let snum_cos = rcsw cos cos_cplx;; +let snum_tan = rcsw tan tan_cplx;; +let snum_asin = rcsw asin asin_cplx;; +let snum_acos = rcsw acos acos_cplx;; + +let snum_atan = + function + [| x |] -> rcsw atan atan_cplx x + | [| y; x |] -> + Sreal (Complex.arg { Complex.re = float_of_snum x; + Complex.im = float_of_snum y }) + | _ -> raise (Error "atan: bad args") +;; + +let snum_sqrt = + function + Scomplex z -> Scomplex (Complex.sqrt z) + | x -> + let r = float_of_snum x in + if r < 0.0 then + Scomplex (Complex.sqrt { Complex.re = r; Complex.im = 0.0 }) + else + let sq = sqrt r in + if is_exact x <> Sfalse && float_is_int sq then + float_to_exact sq + else + Sreal sq +;; + +(* Optimize the simplest cases, leave the rest to Num. *) +let snum_expt x y = + match (x, y) with + (_, Sint n) when n = 0 -> Sint 1 + | ((Sint _ | Sbigint _), Sint n) when n > 0 -> + bigint_res (power_big_int_positive_int (bigint_of_snum x) n) + | ((Sint _ | Sbigint _ | Srational _), (Sint _ | Sbigint _)) -> + snum_of_num (power_num (num_of_snum x) (num_of_snum y)) + | (Scomplex _, _) | (_, Scomplex _) -> + Scomplex (Complex.pow (complex_of_snum x) (complex_of_snum y)) + | _ -> + Sreal (float_of_snum x ** float_of_snum y) +;; + +let make_rectangular x y = + Scomplex { Complex.re = float_of_snum x; Complex.im = float_of_snum y } +;; + +let make_polar x y = + Scomplex (Complex.polar (float_of_snum x) (float_of_snum y)) +;; + +let real_part x = + Sreal (complex_of_snum x).Complex.re +;; + +let imag_part x = + Sreal (complex_of_snum x).Complex.im +;; + +let magnitude x = + Sreal (Complex.norm (complex_of_snum x)) +;; + +let angle x = + Sreal (Complex.arg (complex_of_snum x)) +;; + +let quotient n d = + match (n, d) with + ((Sint _ | Sbigint _ | Srational _), + (Sint _ | Sbigint _ | Srational _)) -> + snum_of_num (integer_num (div_num (num_of_snum n) (num_of_snum d))) + | _ -> + let n = float_of_snum n + and d = float_of_snum d in + if not (float_is_int n && float_is_int d) then + raise (Error "quotient: non-integer arguments") + else + Sreal (n /. d -. (mod_float n d) /. d) +;; + +let remainder n d = + match (n, d) with + ((Sint _ | Sbigint _ | Srational _), + (Sint _ | Sbigint _ | Srational _)) -> + let n = num_of_snum n + and d = num_of_snum d in + let m = mod_num n d in + if sign_num n + sign_num d = 0 then + snum_of_num (sub_num m d) + else + snum_of_num m + | _ -> + let n = float_of_snum n + and d = float_of_snum d in + if not (float_is_int n && float_is_int d) then + raise (Error "quotient: non-integer arguments") + else + Sreal (mod_float n d) +;; + +let modulo n d = + match (n, d) with + ((Sint _ | Sbigint _ | Srational _), + (Sint _ | Sbigint _ | Srational _)) -> + snum_of_num (mod_num (num_of_snum n) (num_of_snum d)) + | _ -> + let n = float_of_snum n + and d = float_of_snum d in + if not (float_is_int n && float_is_int d) then + raise (Error "quotient: non-integer arguments") + else + let m = mod_float n d in + if (n < 0.0 && d > 0.0) || (n > 0.0 && d < 0.0) then + Sreal (d +. m) + else + Sreal m +;; + +(* Compute the gcd of two numbers *) +let rec gcd2 a b = + match (a, b) with + ((Sint _ | Sbigint _), (Sint _ | Sbigint _)) -> + bigint_res (gcd_big_int (bigint_of_snum (snum_abs a)) + (bigint_of_snum (snum_abs b))) + | _ -> + if is_integer a <> Sfalse && is_integer b <> Sfalse then + to_inexact (gcd2 (to_exact a) (to_exact b)) + else + raise (Error "gcd: non-integer arguments") +;; + +let snum_gcd av = + let n = Array.length av in + if n = 0 then + Sint 0 + else + do_ops gcd2 av n +;; + +let lcm2 a b = + let g = gcd2 a b in + snum_abs (mul2 (div2 a g) b) +;; + +let snum_lcm av = + let n = Array.length av in + if n = 0 then + Sint 1 + else + do_ops lcm2 av n +;; + +(* The algorithm for calculating the simplest rational form of a number + is translated from the appendix in the IEEE draft. It implicitly + preserves exactness. *) + +let simplest_rational x y = + let one = Sint 1 in + let rec sri x y = + let fx = snum_floor x + and fy = snum_floor y in + if cmp2 false fx x >= 0 then + fx + else if cmp2 true fx fy = 0 then + add2 fx + (div2 one (sri (div2 one (sub2 y fy)) (div2 one (sub2 x fx)))) + else + add2 fx one + in + if cmp2 false y x < 0 then + sri y x + else if cmp2 false x y >= 0 then + begin + if is_rational x <> Sfalse then + x + else + raise (Error "rationalize: not a rational") + end + else if is_positive x <> Sfalse then + sri x y + else if is_negative y <> Sfalse then + negate (sri (negate y) (negate x)) + else if is_exact x <> Sfalse && is_exact y <> Sfalse then + Sint 0 + else + Sreal 0.0 +;; + +let snum_rationalize x e = + simplest_rational (sub2 x e) (add2 x e) +;; + +let rec snum_numerator = + function + (Sint _ | Sbigint _) as x -> x + | Srational q -> bigint_res (numerator_ratio q) + | Sreal _ as x -> to_inexact (snum_numerator (to_exact x)) + | Scomplex _ -> raise (Error "numerator: not defined for complex numbers") + | _ -> raise (Error "numerator: not a numeric type") +;; + +let rec snum_denominator = + function + Sint _ | Sbigint _ -> Sint 1 + | Srational q -> bigint_res (denominator_ratio q) + | Sreal r as x -> + if r = 0.0 then Sreal 1.0 else + to_inexact (snum_denominator (to_exact x)) + | Scomplex _ -> raise (Error "denominator: not defined for complex numbers") + | _ -> raise (Error "denominator: not a numeric type") +;; + +let init e = + set_pf1 e is_exact "exact?"; + set_pf1 e is_inexact "inexact?"; + set_pf1 e is_zero "zero?"; + set_pf1 e is_positive "positive?"; + set_pf1 e is_negative "negative?"; + set_pf1 e is_number "number?"; + set_pf1 e is_number "complex?"; + set_pf1 e is_real "real?"; + set_pf1 e is_rational "rational?"; + set_pf1 e is_integer "integer?"; + set_pf1 e is_even "even?"; + set_pf1 e is_odd "odd?"; + + set_pfn e snum_add "+"; + set_pfn e snum_sub "-"; + set_pfn e snum_mul "*"; + set_pfn e snum_div "/"; + set_pfn e snum_eq "="; + set_pfn e (snum_rel (>)) ">"; + set_pfn e (snum_rel (<)) "<"; + set_pfn e (snum_rel (>=)) ">="; + set_pfn e (snum_rel (<=)) "<="; + + set_pfn e (snum_minormax (>)) "max"; + set_pfn e (snum_minormax (<)) "min"; + + set_pf1 e snum_abs "abs"; + + set_pf1 e snum_floor "floor"; + set_pf1 e snum_ceiling "ceiling"; + set_pf1 e snum_truncate "truncate"; + set_pf1 e snum_round "round"; + + set_pf1 e snum_exp "exp"; + set_pf1 e snum_log "log"; + + set_pf1 e snum_sin "sin"; + set_pf1 e snum_cos "cos"; + set_pf1 e snum_tan "tan"; + set_pf1 e snum_asin "asin"; + set_pf1 e snum_acos "acos"; + set_pfn e snum_atan "atan"; + + set_pf1 e snum_sqrt "sqrt"; + set_pf2 e snum_expt "expt"; + + set_pf2 e make_rectangular "make-rectangular"; + set_pf2 e make_polar "make-polar"; + set_pf1 e real_part "real-part"; + set_pf1 e imag_part "imag-part"; + set_pf1 e magnitude "magnitude"; + set_pf1 e angle "angle"; + + set_pf2 e quotient "quotient"; + set_pf2 e remainder "remainder"; + set_pf2 e modulo "modulo"; + + set_pf1 e to_exact "inexact->exact"; + set_pf1 e to_inexact "exact->inexact"; + + set_pfn e snum_gcd "gcd"; + set_pfn e snum_lcm "lcm"; + + set_pf2 e snum_rationalize "rationalize"; + + set_pf1 e snum_numerator "numerator"; + set_pf1 e snum_denominator "denominator"; +;; diff --git a/ocs-1.0.3/src/ocs_num.mli b/ocs-1.0.3/src/ocs_num.mli new file mode 100644 index 0000000..6840dd6 --- /dev/null +++ b/ocs-1.0.3/src/ocs_num.mli @@ -0,0 +1,13 @@ +(* Operations on number types. *) + +open Ocs_types + +val negate : sval -> sval + +val add2 : sval -> sval -> sval +val sub2 : sval -> sval -> sval +val mul2 : sval -> sval -> sval +val div2 : sval -> sval -> sval + +val init : env -> unit + diff --git a/ocs-1.0.3/src/ocs_numaux.ml b/ocs-1.0.3/src/ocs_numaux.ml new file mode 100644 index 0000000..eb7fa7a --- /dev/null +++ b/ocs-1.0.3/src/ocs_numaux.ml @@ -0,0 +1,201 @@ +(* Numeric utility functions. *) + +open Ocs_types +open Ocs_error + +open Num +open Ratio +open Big_int + +let fix_floating_precision () = + let n = Arith_status.get_floating_precision () in + if n < 25 then + Arith_status.set_floating_precision 25 +;; + +fix_floating_precision ();; + +Arith_status.set_normalize_ratio true;; + +let promote_real = + function + Sint i -> Sreal (float_of_int i) + | (Sreal _) as r -> r + | (Scomplex _) as z -> z + | Sbigint bi -> Sreal (float_of_big_int bi) + | Srational r -> Sreal (float_of_ratio r) + | _ -> raise (Error "bad number type") +;; + +let float_of_snum = + function + Sint i -> float_of_int i + | Sreal r -> r + (* Note - the imaginary part is discarded! *) + | Scomplex { Complex.re = r; Complex.im = _ } -> r + | Sbigint bi -> float_of_big_int bi + | Srational r -> float_of_ratio r + | _ -> raise (Error "bad number type") +;; + +let promote_complex = + function + (Scomplex _) as z -> z + | x -> Scomplex { Complex.re = float_of_snum x; Complex.im = 0.0 } +;; + +let complex_of_snum = + function + Scomplex z -> z + | x -> { Complex.re = float_of_snum x; Complex.im = 0.0 } +;; + +let rational_of_snum = + function + Sbigint bi -> ratio_of_big_int bi + | Sint i -> ratio_of_int i + | Srational r -> r + | _ -> raise (Error "bad number type") +;; + +let promote_rational s = + Srational (rational_of_snum s) +;; + +let bigint_of_snum = + function + Sint i -> big_int_of_int i + | Sbigint bi -> bi + | _ -> raise (Error "bad number type") +;; + +let promote_bigint = + function + Sint i -> Sbigint (big_int_of_int i) + | (Sbigint _) as bi -> bi + | _ -> raise (Error "bad number type") +;; + +let snum_fixtypes a b = + match (a, b) with + (Sint _, Sint _) -> a, b + | (Scomplex _, _) -> a, promote_complex b + | (_, Scomplex _) -> (promote_complex a), b + | (Sreal _, _) -> a, promote_real b + | (_, Sreal _) -> (promote_real a), b + | (Srational _, _) -> a, promote_rational b + | (_, Srational _) -> (promote_rational a), b + | (Sbigint _, _) -> a, promote_bigint b + | (_, Sbigint _) -> (promote_bigint a), b + | _ -> raise (Error "snum_fixtypes: not numeric types") +;; + +let snum_of_num = + function + Int x -> Sint x + | Big_int x -> Sbigint x + | Ratio x -> Srational x +;; + +let num_of_snum = + function + Sint x -> Int x + | Sbigint x -> Big_int x + | Srational x -> Ratio x + | _ -> raise (Error "bad number type") +;; + +(* Return a result as the simplest representation of a given bigint *) +let bigint_res bi = + if is_int_big_int bi then + Sint (int_of_big_int bi) + else + Sbigint bi +;; + +let round_float r = + let d = floor (r +. 0.5) + and e = ceil (r -. 0.5) in + if d <> e && (mod_float e 2.0) = 0.0 then e + else d +;; + +let float_is_int f = + let (x, _) = modf f in + x = 0.0 +;; + +let max_f_int = 2.0 ** (float_of_int (Sys.word_size - 2)) -. 1.0;; +let min_f_int = -.max_f_int -. 1.0;; + +(* We need to deconstruct IEEE floats to convert them. *) +let fe_bits = Int64.of_string "0x7ff0000000000000";; +let fm_bits = Int64.of_string "0x000fffffffffffff";; +let fi_bit = Int64.of_string "0x0010000000000000";; +let fs_bit = Int64.of_string "0x8000000000000000";; + +let fb_get_dm fb = + Int64.logand fb fm_bits +;; + +let fb_get_m fb = + Int64.logor (fb_get_dm fb) fi_bit +;; + +let fb_get_e fb = + Int64.to_int (Int64.shift_right (Int64.logand fb fe_bits) 52) - 1023 +;; + +let fb_get_s fb = + Int64.compare (Int64.logand fb fs_bit) Int64.zero <> 0; +;; + +let f_is_int m e = + if e < 0 then false + else if e >= 52 then true + else Int64.compare (Int64.logand fm_bits + (Int64.shift_left m e)) Int64.zero = 0 +;; + +(* Convert an int64 into a bigint, possibly ignoring the most + significant 4 bits (on 32-bit machines). This is good enough + for the 52 + 1 -bit mantissa of 64-bit IEEE floats. *) +let big_int_of_int64 i = + if Sys.word_size = 64 then + (big_int_of_int (Int64.to_int i)) + else (* Assume 32 *) + let lo = Int64.to_int i land 0x3fffffff + and hi = Int64.to_int (Int64.shift_right i 30) in + add_big_int (big_int_of_int lo) + (mult_big_int (big_int_of_int hi) (power_int_positive_int 2 30)) +;; + +let float_to_exact f = + if float_is_int f && f >= min_f_int && f <= max_f_int then + Sint (int_of_float f) + else if f = infinity || f = neg_infinity || f = nan then + raise (Error "invalid float") + else + let fb = Int64.bits_of_float f in + let m = fb_get_m fb + and e = fb_get_e fb + and is_neg = fb_get_s fb in + let bm = big_int_of_int64 m in + if f_is_int m e then + let wrap = if is_neg then minus_big_int else fun x -> x in + if e = 52 then + Sbigint (wrap bm) + else if e > 52 then + Sbigint (wrap (mult_big_int bm (power_int_positive_int 2 (e - 52)))) + else + Sbigint (wrap (div_big_int bm (power_int_positive_int 2 (52 - e)))) + else + let wrap = if is_neg then minus_ratio else fun x -> x in + if e < -1022 then (* not normalized, no implied mantissa bit *) + Srational (wrap (create_ratio (big_int_of_int64 (fb_get_dm fb)) + (power_int_positive_int 2 (51 - e)))) + else + Srational (wrap (create_ratio bm + (power_int_positive_int 2 (52 - e)))) +;; + diff --git a/ocs-1.0.3/src/ocs_numaux.mli b/ocs-1.0.3/src/ocs_numaux.mli new file mode 100644 index 0000000..3527676 --- /dev/null +++ b/ocs-1.0.3/src/ocs_numaux.mli @@ -0,0 +1,29 @@ +(* Numeric utility functions. *) + +open Ocs_types + +open Num +open Ratio +open Big_int + +val promote_real : sval -> sval +val promote_complex : sval -> sval +val promote_rational : sval -> sval +val promote_bigint : sval -> sval + +val complex_of_snum : sval -> Complex.t +val float_of_snum : sval -> float +val rational_of_snum : sval -> ratio +val bigint_of_snum : sval -> big_int + +val snum_fixtypes : sval -> sval -> sval * sval + +val snum_of_num : num -> sval +val num_of_snum : sval -> num + +val bigint_res : big_int -> sval + +val round_float : float -> float +val float_is_int : float -> bool +val float_to_exact : float -> sval + diff --git a/ocs-1.0.3/src/ocs_numstr.ml b/ocs-1.0.3/src/ocs_numstr.ml new file mode 100644 index 0000000..fdf4825 --- /dev/null +++ b/ocs-1.0.3/src/ocs_numstr.ml @@ -0,0 +1,403 @@ +(* Conversions between numbers and strings. *) + +open Ocs_types +open Ocs_error +open Ocs_numaux +open Ocs_num +open Ocs_env + +open Num +open Big_int +open Ratio + +(* We need to scan strings and keep track of our position. *) + +type sbuf = { + s_str : string; + mutable s_pos : int +} + +let speek s = + if s.s_pos < String.length s.s_str then + Some s.s_str.[s.s_pos] + else + None +;; + +let skip s = + s.s_pos <- s.s_pos + 1 +;; + +let sget s = + match speek s with + (Some _) as c -> skip s; c + | _ -> None +;; + +let ssleft s = + (String.length s.s_str) - s.s_pos +;; + +(* Converting strings to numbers is fairly complex. We have a lot of + cases to consider. *) + +type exactness = + Exact + | Inexact + | Undef + +let parse_prefix s = + let rec nextp b e = + match speek s with + Some '#' -> + begin + skip s; + match sget s with + Some ('E' | 'e') -> + if e <> Undef then raise (Error "invalid #e") else nextp b Exact + | Some ('I' | 'i') -> + if e <> Undef then raise (Error "invalid #i") else nextp b Inexact + | Some ('B' | 'b') -> + if b <> 0 then raise (Error "invalid #b") else nextp 2 e + | Some ('O' | 'o') -> + if b <> 0 then raise (Error "invalid #o") else nextp 8 e + | Some ('D' | 'd') -> + if b <> 0 then raise (Error "invalid #d") else nextp 10 e + | Some ('X' | 'x') -> + if b <> 0 then raise (Error "invalid #x") else nextp 16 e + | _ -> raise (Error "invalid prefix") + end + | _ -> (b, e) + in + nextp 0 Undef +;; + +let strtobi s base = + let n = String.length s + and am v i = add_int_big_int i (mult_int_big_int base v) in + let rec loop i v = + if i >= n then + v + else + loop (i + 1) (am v + (match s.[i] with + '0' .. '9' as c -> int_of_char c - int_of_char '0' + | 'a' .. 'f' as c -> int_of_char c - int_of_char 'a' + 10 + | 'A' .. 'F' as c -> int_of_char c - int_of_char 'A' + 10 + | _ -> raise (Error "invalid number"))) + in + loop 0 (big_int_of_int 0) +;; + +let read_bigint s base = + if base = 10 then + big_int_of_string s + else + strtobi s base +;; + +(* The largest integer is at *least* this big (bigger on 64-bit machines). *) +let max_int = 0x3fffffff +let min_int = -max_int - 1 + +let parse_num s base = + let fixsign = + match speek s with + Some '-' -> skip s; negate + | Some '+' -> skip s; fun x -> x + | _ -> fun x -> x + and maxv = max_int / base + and maxi = max_int mod base + in + let rec scann v o = + match speek s with + Some ('0' .. '9' as c) + when (int_of_char c) - (int_of_char '0') < base -> + addo v ((int_of_char c) - (int_of_char '0')) o + | Some ('a' .. 'f' as c) when base = 16 -> + addo v ((int_of_char c) - (int_of_char 'a') + 10) o + | Some ('A' .. 'F' as c) when base = 16 -> + addo v ((int_of_char c) - (int_of_char 'A') + 10) o + | _ -> (v, o) + and addo v i o = + skip s; + if o || v > maxv || (v = maxv && i > maxi) then + scann 0 true + else + scann (v * base + i) o + and readn () = + let sp = s.s_pos in + match scann 0 false with + (i, false) -> + if s.s_pos = sp then + raise (Error "invalid number") + else + Sint i + | (_, true) -> + Sbigint (read_bigint (String.sub s.s_str sp (s.s_pos - sp)) base) + in + let num = readn () in + match speek s with + Some '/' -> + skip s; + fixsign (div2 (Sbigint (bigint_of_snum num)) + (Sbigint (bigint_of_snum (readn ())))) + | Some ('+' | '-' | '@') | None -> fixsign num + | _ -> raise (Error "invalid rational") +;; + +let parse_flo10 s = + let sp = s.s_pos in + let rec skipd isfirst = + match speek s with + Some '0' .. '9' | Some '#' -> skip s; skipd false + | Some ('+' | '-') when isfirst -> skip s; skipd false + | _ -> () + in + skipd true; + if speek s = Some '.' then + begin + skip s; + skipd false + end; + begin + match speek s with + Some ('E' | 'e' | 'F' | 'f' | 'D' | 'd' | 'S' | 's' | 'L' | 'l') -> + skip s; skipd true + | _ -> () + end; + let t = String.sub s.s_str sp (s.s_pos - sp) in + for i = 0 to String.length t - 1 do + match t.[i] with + '#' -> t.[i] <- '0' + | 'F' | 'f' | 'D' | 'd' | 'S' | 's' | 'L' | 'l' -> t.[i] <- 'e' + | _ -> () + done; + try + Sreal (float_of_string t) + with + Failure _ -> raise (Error "invalid float") +;; + +let string_to_num str ub = + (* Special cases for [-+][iI] *) + if str = "+i" || str = "+I" then + Scomplex { Complex.re = 0.0; Complex.im = 1.0; } + else if str = "-i" || str = "-I" then + Scomplex { Complex.re = 0.0; Complex.im = -1.0; } + else + let s = { s_str = str; s_pos = 0 } in + let (base, ex) = + match parse_prefix s with + 0, x -> if ub = 0 then (10, x) else (ub, x) + | (b, x) as r -> + if ub <> 0 && ub <> b then + raise (Error "Base mismatch") + else r + in + let getn () = + if base = 10 && ex <> Exact then + begin + let sp = s.s_pos in + try + parse_num s 10 + with _ -> + s.s_pos <- sp; + parse_flo10 s + end + else + parse_num s base + and fixex n = + match (ex, n) with + (Inexact, (Sint _ | Sbigint _ | Srational _)) -> promote_real n + | (Exact, (Sreal _ | Scomplex _)) -> raise (Error "Not exact") + | _ -> n + in + let a = fixex (getn ()) in + match speek s with + Some ('+' | '-' as c) -> + if ex = Exact then + raise (Error "Complex not exact") + else + if ssleft s = 2 && s.s_str.[s.s_pos + 1] = 'i' then + Scomplex { Complex.re = float_of_snum a; + Complex.im = (if c = '-' then -1.0 else 1.0) } + else + let b = getn () in + if ssleft s <> 1 || speek s <> Some 'i' then + raise (Error "invalid number") + else + Scomplex { Complex.re = float_of_snum a; + Complex.im = float_of_snum b } + | Some '@' -> + skip s; + if ex = Exact then + raise (Error "Complex not exact") + else + let b = getn () in + if ssleft s <> 0 then + raise (Error "invalid number") + else + let r = float_of_snum a + and t = float_of_snum b in + Scomplex (Complex.polar r t) + | Some c -> raise (Error "invalid number") + | None -> a +;; + +let snum_strtonum av = + match Array.length av with + (1 | 2) as n -> + let r = + if n = 2 then + begin + match av.(1) with + Sint i -> i + | _ -> raise (Error "string->number: invalid radix") + end + else + 0 + in + begin + match av.(0) with + Sstring s -> + begin + try + if s = "" then + Sfalse + else + string_to_num s r + with + _ -> Sfalse + end + | _ -> raise (Error "string->number: not a string") + end + | _ -> raise (Error "string->number: wrong number of args") +;; + +let string_of_real_s r = + let rec loop n = + let s = Printf.sprintf "%.*g" n r in + if n >= 25 || r = float_of_string s then s + else loop (n + 1) + in + loop 14 +;; + +let string_of_real r = + let s = string_of_real_s r in + let n = String.length s in + let rec loop i = + if i >= n then s ^ ".0" + else if s.[i] = '.' || s.[i] = 'e' then s + else loop (i + 1) + in + loop 0 +;; + +let string_of_complex = + function { Complex.re = r; Complex.im = i } -> + (string_of_real_s r) ^ + (if i < 0.0 then + begin + if i = -1.0 then + "-i" + else + (string_of_real_s i) ^ "i" + end + else + if i = 1.0 then + "+i" + else + "+" ^ (string_of_real_s i) ^ "i") +;; + +let ichr i = + if i < 10 then + char_of_int (int_of_char '0' + i) + else + char_of_int (int_of_char 'a' + i - 10) +;; + +let string_of_list l = + let n = List.length l in + let s = String.create n in + let rec loop i l = + if i < n then + begin + match l with + c::t -> s.[i] <- c; loop (i + 1) t + | _ -> assert false + end + else + () + in + loop 0 l; s +;; + +let itostr base i = + if i = 0 then + "0" + else + let pf = if i < 0 then "-" else "" in + let rec loop i r = + if i = 0 then + r + else + loop (i / base) ((ichr (i mod base))::r) + in + pf ^ string_of_list (loop (abs i) []) +;; + +let biqmi bi i = + let (q, r) = quomod_big_int bi (big_int_of_int i) in + (q, int_of_big_int r) +;; + +let bitostr base bi = + let pf = if sign_big_int bi < 0 then "-" else "" in + let rec loop bi r = + if sign_big_int bi = 0 then + begin + match r with + [] -> [ '0' ] + | _ -> r + end + else + let (q, m) = biqmi bi base in + loop q ((ichr m)::r) + in + pf ^ string_of_list (loop (abs_big_int bi) []) +;; + +let ntostr base = + function + Sint i -> itostr base i + | Sbigint bi -> bitostr base bi + | Srational r -> bitostr base (numerator_ratio r) ^ "/" ^ + bitostr base (denominator_ratio r) + | _ -> raise (Error "number->string: invalid radix for inexact number") +;; + +let rec snum_numtostr = + function + [| Sint i |] -> Sstring (string_of_int i) + | [| Sbigint bi |] -> Sstring (string_of_big_int bi) + | [| Srational r |] -> Sstring (string_of_ratio r) + | [| Sreal r |] -> Sstring (string_of_real r) + | [| Scomplex z |] -> Sstring (string_of_complex z) + | [| snum; Sint radix |] -> + if radix = 10 then + snum_numtostr [| snum |] + else if radix = 2 || radix = 8 || radix = 16 then + Sstring (ntostr radix snum) + else + raise (Error "number->string: invalid radix") + | _ -> raise (Error "number->string: bad args") +;; + +let init e = + set_pfn e snum_strtonum "string->number"; + set_pfn e snum_numtostr "number->string"; +;; + diff --git a/ocs-1.0.3/src/ocs_numstr.mli b/ocs-1.0.3/src/ocs_numstr.mli new file mode 100644 index 0000000..287fe4d --- /dev/null +++ b/ocs-1.0.3/src/ocs_numstr.mli @@ -0,0 +1,11 @@ +(* Conversions between numbers and strings. *) + +open Ocs_types + +val string_of_real : float -> string +val string_of_complex : Complex.t -> string + +val string_to_num : string -> int -> sval + +val init : env -> unit + diff --git a/ocs-1.0.3/src/ocs_port.ml b/ocs-1.0.3/src/ocs_port.ml new file mode 100644 index 0000000..801c519 --- /dev/null +++ b/ocs-1.0.3/src/ocs_port.ml @@ -0,0 +1,223 @@ +(* Buffered I/O, Scheme ports. *) + +open Ocs_error + +(* Ports can either be file descriptors or string buffers. File + descriptors may be valid for both input and output, but when + switching between the two modes, the file offset may not work + as expected. + + For unbuffered or asynchronous I/O, from Scheme or otherwise, + the port can simply be used as a reference to the file + descriptor. *) + +type port = { + mutable p_buf : string; + mutable p_pos : int; + mutable p_wend : int; + mutable p_rend : int; + mutable p_ugc : char option; + mutable p_fd : Unix.file_descr option; + mutable p_input : bool; + mutable p_output : bool; + p_close : bool +} + +type port_flag = + Pf_input + | Pf_output + | Pf_close + +let mkbuf () = + String.create 1024 +;; + +let mkport buf fd inf outf cl = + { p_buf = buf; + p_pos = 0; + p_wend = 0; + p_rend = 0; + p_ugc = None; + p_fd = fd; + p_input = inf; + p_output = outf; + p_close = cl } +;; + +let is_input p = + p.p_input +;; + +let is_output p = + p.p_output +;; + +let wrflush p = + if not p.p_output then + raise (Error "not a valid output port"); + match p.p_fd with + Some fd -> + if p.p_wend > 0 && p.p_pos > 0 then + begin + try + let n = Unix.write fd p.p_buf 0 p.p_pos in + if n <> p.p_pos then + raise (Error "write error: incomplete write") + with + Unix.Unix_error (e, _, _) -> + raise (Error ("write error: " ^ Unix.error_message e)) + end; + p.p_pos <- 0; + p.p_wend <- String.length p.p_buf + | None -> + if p.p_pos = p.p_wend then + let n = String.length p.p_buf in + let nbuf = String.create (n * 2) in + String.blit p.p_buf 0 nbuf 0 n; + p.p_buf <- nbuf; + p.p_wend <- String.length p.p_buf +;; + +let rdfill p = + if not p.p_input then + raise (Error "not a valid input port"); + if p.p_wend > 0 then + wrflush p; + p.p_pos <- 0; + p.p_rend <- 0; + p.p_wend <- 0; + match p.p_fd with + Some fd -> + begin + try + p.p_rend <- Unix.read fd p.p_buf 0 (String.length p.p_buf) + with + Unix.Unix_error (e, _, _) -> + raise (Error ("read error: " ^ Unix.error_message e)) + end + | None -> () +;; + +let getc p = + match p.p_ugc with + Some _ as c -> p.p_ugc <- None; c + | None -> + if p.p_rend = 0 || p.p_pos >= p.p_rend then rdfill p; + if p.p_rend = 0 then None + else + begin + assert (p.p_pos < p.p_rend); + let c = p.p_buf.[p.p_pos] in + p.p_pos <- p.p_pos + 1; + Some c + end +;; + +let get_fd p = + p.p_fd +;; + +let flush p = + if p.p_wend > 0 then + wrflush p +;; + +let close p = + if p.p_input || p.p_output then + begin + flush p; + p.p_input <- false; + p.p_output <- false; + match p.p_fd with + Some fd -> + if p.p_close then Unix.close fd; + p.p_fd <- None + | None -> () + end +;; + +let ungetc p c = + p.p_ugc <- Some c +;; + +let char_ready p = + if p.p_ugc <> None || p.p_pos < p.p_rend then true + else if not p.p_input then false + else + match p.p_fd with + Some fd -> + let (r, _, _) = Unix.select [ fd ] [] [] 0.0 in + List.length r > 0 + | None -> false +;; + +let putc p c = + if p.p_wend = 0 || p.p_pos >= p.p_wend then + wrflush p; + assert (p.p_pos < p.p_wend); + p.p_buf.[p.p_pos] <- c; + p.p_pos <- p.p_pos + 1 +;; + +let puts p s = + let n = String.length s in + if n > 0 && p.p_rend - p.p_pos >= n then + begin + String.blit s 0 p.p_buf p.p_pos n; + p.p_pos <- p.p_pos + n + end + else + String.iter (fun c -> putc p c) s +;; + +let fd_port fd flags = + let inf = ref false + and outf = ref false + and clf = ref false in + List.iter (function + Pf_input -> inf := true + | Pf_output -> outf := true + | Pf_close -> clf := true) flags; + let p = mkport (mkbuf ()) (Some fd) !inf !outf !clf in + if !clf then Gc.finalise close p; + p +;; + +let input_port ch = + fd_port (Unix.descr_of_in_channel ch) [ Pf_input ] +;; + +let output_port ch = + fd_port (Unix.descr_of_out_channel ch) [ Pf_output ] +;; + +let open_input_port name = + try + let fd = Unix.openfile name [ Unix.O_RDONLY ] 0 in + fd_port fd [ Pf_input; Pf_close ] + with + Unix.Unix_error (e, _, _) -> + let err = Unix.error_message e in + raise (Error ("unable to open '" ^ name ^ "' for input: " ^ err)) +;; + +let open_output_port name = + try + let fd = Unix.openfile name [ Unix.O_WRONLY; Unix.O_APPEND; + Unix.O_CREAT; Unix.O_TRUNC ] 0o666 in + fd_port fd [ Pf_output; Pf_close ] + with + Unix.Unix_error (e, _, _) -> + let err = Unix.error_message e in + raise (Error ("unable to open '" ^ name ^ "' for output: " ^ err)) +;; + +let string_input_port s = + let p = mkport s None true false false in + p.p_rend <- String.length s; + p +;; + +let string_output_port () = + mkport (mkbuf ()) None false true false +;; diff --git a/ocs-1.0.3/src/ocs_port.mli b/ocs-1.0.3/src/ocs_port.mli new file mode 100644 index 0000000..3294e81 --- /dev/null +++ b/ocs-1.0.3/src/ocs_port.mli @@ -0,0 +1,33 @@ +(* Buffered I/O, Scheme ports. *) + +type port + +type port_flag = + Pf_input + | Pf_output + | Pf_close + +val fd_port : Unix.file_descr -> port_flag list -> port +val input_port : in_channel -> port +val output_port : out_channel -> port +val open_input_port : string -> port +val open_output_port : string -> port +val string_input_port : string -> port +val string_output_port : unit -> port + +val is_input : port -> bool +val is_output : port -> bool + +val getc : port -> char option +val ungetc : port -> char -> unit +val char_ready : port -> bool + +val putc : port -> char -> unit +val puts : port -> string -> unit + +val get_fd : port -> Unix.file_descr option + +val flush : port -> unit + +val close : port -> unit + diff --git a/ocs-1.0.3/src/ocs_prim.ml b/ocs-1.0.3/src/ocs_prim.ml new file mode 100644 index 0000000..8ec6eec --- /dev/null +++ b/ocs-1.0.3/src/ocs_prim.ml @@ -0,0 +1,269 @@ +(* Various primitives *) + +open Ocs_types +open Ocs_error +open Ocs_env +open Ocs_eval +open Ocs_misc +open Ocs_sym +open Ocs_io +open Ocs_compile +open Ocs_macro + +let logical_not = + function + Sfalse -> Strue + | _ -> Sfalse +;; + +(* Type predicates *) + +let is_boolean = + function + Strue | Sfalse -> Strue + | _ -> Sfalse +;; + +let is_string = + function + Sstring _ -> Strue + | _ -> Sfalse +;; + +let is_char = + function + Schar _ -> Strue + | _ -> Sfalse +;; + +let is_pair = + function + Spair _ -> Strue + | _ -> Sfalse +;; + +let is_null = + function + Snull -> Strue + | _ -> Sfalse +;; + +let is_vector = + function + Svector _ -> Strue + | _ -> Sfalse +;; + +let is_proc = + function + Sproc (_, _) | Sprim _ -> Strue + | _ -> Sfalse +;; + +let is_port = + function + Sport _ -> Strue + | _ -> Sfalse +;; + +let is_symbol = + function + Ssymbol _ -> Strue + | _ -> Sfalse +;; + +let symbol_to_string = + function + Ssymbol s -> Sstring s + | _ -> raise (Error "symbol->string: not a symbol") +;; + +let string_to_symbol = + function + Sstring s -> get_symbol (String.copy s) + | _ -> raise (Error "string->symbol: not a string") +;; + +let is_eq a b = + if a == b then Strue else Sfalse +;; + +let is_eqv a b = + if test_eqv a b then Strue else Sfalse +;; + +let is_equal a b = + if test_equal a b then Strue else Sfalse +;; + +let do_apply th cc av = + let n = Array.length av in + if n < 1 then + raise (Error "apply: bad args") + else + let f = av.(0) in + let rec loop i r = + if i = 0 then + r + else if i = n - 1 then (* r must be [] *) + loop (i - 1) (list_to_caml av.(i)) + else + loop (i - 1) (av.(i)::r) + in + let args = Array.map (fun x -> Cval x) + (Array.of_list (loop (n - 1) [])) + in + eval th cc (mkapply (Cval f) args) +;; + +let force _ cc = + function + [| Spromise ({ promise_code = c; + promise_val = None; + promise_th = Some th } as p) |] -> + eval th + (fun v -> + match p.promise_val with (* Computed before returning? *) + Some v -> cc v + | None -> + p.promise_val <- Some v; + p.promise_th <- None; (* Release reference for gc *) + cc v) c + | [| Spromise { promise_code = _; + promise_val = Some v; + promise_th = _ } |] -> + cc v + | _ -> raise (Error "force: bad args") +;; + +let map_for_each th cc av is_map = + let my_name = if is_map then "map" else "for-each" + and na = Array.length av - 1 in + if na <= 0 then + raise (Error (my_name ^ ": bad args")); + let proc = av.(0) + and get_cdr = + function + Spair { car = _; cdr = t } -> t + | _ -> raise (Error (my_name ^ ": list lengths don't match")) + and get_carc = + function + Spair { car = h; cdr = _ } -> Cval h + | _ -> raise (Error (my_name ^ ": list lengths don't match")) + and result = ref (if is_map then Snull else Sunspec) + and rtail = ref Snull in + let append v = + if !rtail == Snull then + begin + result := Spair { car = v; cdr = Snull }; + rtail := !result; + end + else + begin + match !rtail with + Spair p -> + p.cdr <- Spair { car = v; cdr = Snull }; + rtail := p.cdr + | _ -> assert false + end + in + let rec loop args = + match args.(0) with + Snull -> cc !result + | Spair _ -> + eval th + (fun v -> + if is_map then append v; + loop (Array.map get_cdr args)) + (mkapply (Cval proc) (Array.map get_carc args)) + | _ -> raise (Error (my_name ^ ": invalid argument lists")) + in + loop (Array.sub av 1 na) +;; + +let map th cc av = + map_for_each th cc av true +;; + +let for_each th cc av = + map_for_each th cc av false +;; + +let load_file e th name = + let th = { th with th_display = [| |]; th_depth = -1 } + and inp = Ocs_port.open_input_port name in + let lex = Ocs_lex.make_lexer inp name in + let rec loop () = + match Ocs_read.read_expr lex with + Seof -> () + | v -> + let c = compile e v in + eval th (fun _ -> ()) c; + loop () + in + loop () +;; + +let load_prim e th cc = + function + [| Sstring name |] -> load_file e th name; cc Sunspec + | _ -> raise (Error "load: invalid name argument") +;; + +let eval_prim th cc = + function + [| expr; Sesym (e, _) |] -> + eval { th with th_display = [| |]; th_depth = -1 } cc + (compile e expr) + | _ -> raise (Error "eval: invalid args") +;; + +let report_env e _ = + Sesym (env_copy e, Ssymbol "") +;; + +let null_env _ = + let e = top_env () in + bind_lang e; + bind_macro e; + Sesym (e, Ssymbol "") +;; + +let interact_env e = + fun () -> Sesym (e, Ssymbol "") +;; + +let init e = + set_pf1 e logical_not "not"; + set_pf1 e is_boolean "boolean?"; + set_pf1 e is_string "string?"; + set_pf1 e is_char "char?"; + set_pf1 e is_vector "vector?"; + set_pf1 e is_pair "pair?"; + set_pf1 e is_null "null?"; + set_pf1 e is_proc "procedure?"; + set_pf1 e is_port "port?"; + set_pf1 e is_symbol "symbol?"; + + set_pf1 e symbol_to_string "symbol->string"; + set_pf1 e string_to_symbol "string->symbol"; + + set_pf2 e is_eq "eq?"; + set_pf2 e is_eqv "eqv?"; + set_pf2 e is_equal "equal?"; + + set_pfcn e do_apply "apply"; + + set_pfcn e force "force"; + + set_pfcn e map "map"; + set_pfcn e for_each "for-each"; + + set_pfcn e (load_prim e) "load"; + set_pfcn e eval_prim "eval"; + + set_pf1 e (report_env (env_copy e)) "scheme-report-environment"; + set_pf1 e null_env "null-environment"; + set_pf0 e (interact_env e) "interaction-environment"; +;; + diff --git a/ocs-1.0.3/src/ocs_prim.mli b/ocs-1.0.3/src/ocs_prim.mli new file mode 100644 index 0000000..b8f4a6d --- /dev/null +++ b/ocs-1.0.3/src/ocs_prim.mli @@ -0,0 +1,8 @@ +(* Miscellaneous primitives. *) + +open Ocs_types + +val load_file : env -> thread -> string -> unit + +val init : env -> unit + diff --git a/ocs-1.0.3/src/ocs_print.ml b/ocs-1.0.3/src/ocs_print.ml new file mode 100644 index 0000000..e2f5671 --- /dev/null +++ b/ocs-1.0.3/src/ocs_print.ml @@ -0,0 +1,75 @@ +(* Print Scheme values *) + +open Ocs_types +open Ocs_sym +open Ocs_numstr + +let write_string p s = + Ocs_port.putc p '\"'; + for i = 0 to String.length s - 1 do + match s.[i] with + '\n' -> Ocs_port.puts p "\\n" + | '\r' -> Ocs_port.puts p "\\r" + | '\t' -> Ocs_port.puts p "\\t" + | '\\' -> Ocs_port.puts p "\\\\" + | '\"' -> Ocs_port.puts p "\\\"" + | '\032' .. '\126' as c -> Ocs_port.putc p c + | c -> Ocs_port.puts p (Printf.sprintf "\\x%02x" (int_of_char c)) + done; + Ocs_port.putc p '\"' +;; + +let write_char p c = + Ocs_port.puts p "#\\"; + match c with + '\033' .. '\126' -> Ocs_port.putc p c + | _ -> Ocs_port.puts p (Ocs_char.char_to_name c) +;; + +let rec write_vector p disp v = + Ocs_port.puts p "#("; + for i = 0 to Array.length v - 1 do + if i <> 0 then Ocs_port.putc p ' '; + print p disp v.(i) + done; + Ocs_port.putc p ')' + +and write_list p disp l = + Ocs_port.putc p '('; + let rec pit l = + print p disp l.car; + match l.cdr with + Snull -> () + | Spair t -> Ocs_port.putc p ' '; pit t + | x -> Ocs_port.puts p " . "; print p disp x + in + pit l; + Ocs_port.putc p ')' + +and print p disp = + function + Snull -> Ocs_port.puts p "()" + | Seof -> Ocs_port.puts p "#" + | Strue -> Ocs_port.puts p "#t" + | Sfalse -> Ocs_port.puts p "#f" + | Sstring s -> if disp then Ocs_port.puts p s else write_string p s + | Ssymbol s -> Ocs_port.puts p s + | Sint i -> Ocs_port.puts p (string_of_int i) + | Sreal r -> Ocs_port.puts p (string_of_real r) + | Scomplex z -> Ocs_port.puts p (string_of_complex z) + | Sbigint b -> Ocs_port.puts p (Big_int.string_of_big_int b) + | Srational r -> Ocs_port.puts p (Ratio.string_of_ratio r) + | Schar c -> if disp then Ocs_port.putc p c else write_char p c + | Spair l -> write_list p disp l + | Svector v -> write_vector p disp v + | Sport _ -> Ocs_port.puts p "#" + | Sproc _ -> Ocs_port.puts p "#" + | Sprim { prim_fun = _; prim_name = n } -> + Ocs_port.puts p "#' + | Spromise _ -> Ocs_port.puts p "#" + | Sesym (_, s) -> print p disp s + | Swrapped _ -> Ocs_port.puts p "#" + | Sunspec -> Ocs_port.puts p "#" + | _ -> Ocs_port.puts p "#" +;; + diff --git a/ocs-1.0.3/src/ocs_print.mli b/ocs-1.0.3/src/ocs_print.mli new file mode 100644 index 0000000..15e52ed --- /dev/null +++ b/ocs-1.0.3/src/ocs_print.mli @@ -0,0 +1,9 @@ +(* Print Scheme values *) + +open Ocs_types + +val write_string : Ocs_port.port -> string -> unit +val write_char : Ocs_port.port -> char -> unit + +val print : Ocs_port.port -> bool -> sval -> unit + diff --git a/ocs-1.0.3/src/ocs_read.ml b/ocs-1.0.3/src/ocs_read.ml new file mode 100644 index 0000000..5f10beb --- /dev/null +++ b/ocs-1.0.3/src/ocs_read.ml @@ -0,0 +1,87 @@ +(* Reader of Scheme expressions. *) + +open Ocs_types +open Ocs_error +open Ocs_lex +open Ocs_sym +open Ocs_misc + +let read_error lex err = + let (file, name) = get_loc lex in + if String.length file = 0 then + Error err + else + ErrorL ((file, name), err) +;; + +let show_token = + function + '"' -> "'\"'" + | '\032' .. '\126' as c -> Printf.sprintf "\"%c\"" c + | c -> Printf.sprintf "ascii %d" (int_of_char c) +;; + +let rec read_item lex = + function + Leof -> Seof + | Lident s -> get_symbol (String.lowercase s) + | Lstring s -> Sstring s + | Lnumber s | Lbool s | Lchar s -> s + | Ltoken '(' -> read_list lex ')' + | Ltoken '[' -> read_list lex ']' + | Lopenv -> read_vector lex + | Ltoken '\'' -> read_quoted lex sym_quote + | Ltoken '`' -> read_quoted lex sym_quasiquote + | Ltoken ',' -> read_quoted lex sym_unquote + | Lunqsplice -> read_quoted lex sym_unquote_splicing + | Ltoken c -> raise (read_error lex ("unexpected " ^ show_token c)) + +and read_list lex term = + let rec loop r = + match get_tok lex with + Leof -> raise (read_error lex "unexpected eof in list") + | Ltoken c when c = term -> make_slist Snull r + | Ltoken '.' -> + begin + let tl = + match get_tok lex with + Leof -> raise (read_error lex "unexpected eof in dotted tail") + | x -> read_item lex x + in + match get_tok lex with + Ltoken c when c = term -> make_slist tl r + | _ -> raise (read_error lex + "expected close paren after dotted tail") + end + | x -> loop ((read_item lex x)::r) + in + loop [] + +and read_vector lex = + let rec loop r = + match get_tok lex with + Leof -> raise (read_error lex "unexpected eof in vector") + | Ltoken ')' -> r + | x -> loop ((read_item lex x)::r) + in + Svector (Array.of_list (List.rev (loop []))) + +and read_quoted lex sym = + match get_tok lex with + Leof -> raise (read_error lex "unexpected eof") + | x -> + let x = read_item lex x in + Spair { car = sym; cdr = Spair { car = x; cdr = Snull }} +;; + +let read_expr lex = + read_item lex (get_tok lex) +;; + +let read_from_port p = + read_expr (make_lexer p "") +;; + +let read_from_string s = + read_from_port (Ocs_port.string_input_port s) +;; diff --git a/ocs-1.0.3/src/ocs_read.mli b/ocs-1.0.3/src/ocs_read.mli new file mode 100644 index 0000000..e65bdac --- /dev/null +++ b/ocs-1.0.3/src/ocs_read.mli @@ -0,0 +1,10 @@ +(* Reader of Scheme expressions. *) + +open Ocs_types +open Ocs_lex + +val read_expr : lexer -> sval + +val read_from_port : Ocs_port.port -> sval +val read_from_string : string -> sval + diff --git a/ocs-1.0.3/src/ocs_string.ml b/ocs-1.0.3/src/ocs_string.ml new file mode 100644 index 0000000..2330900 --- /dev/null +++ b/ocs-1.0.3/src/ocs_string.ml @@ -0,0 +1,153 @@ +(* String primitives *) + +open Ocs_types +open Ocs_error +open Ocs_env + +let make_string = + function + [| Sint k |] -> Sstring (String.create k) + | [| Sint k; Schar c |] -> Sstring (String.make k c) + | _ -> raise (Error "make-string: bad args") +;; + +let string_of av = + let n = Array.length av in + let s = String.create n in + for i = 0 to n - 1 do + match av.(i) with + Schar c -> s.[i] <- c + | _ -> raise (Error "string: bad args") + done; + Sstring s +;; + +let string_length = + function + Sstring s -> Sint (String.length s) + | _ -> raise (Error "string-length: not a string") +;; + +let string_ref s k = + match (s, k) with + (Sstring s, Sint k) -> + if k >= 0 && k < String.length s then + Schar s.[k] + else + raise (Error "string-ref: out of bounds") + | _ -> raise (Error "string-ref: bad args") +;; + +let string_set s k c = + match (s, k, c) with + (Sstring s, Sint k, Schar c) -> + if k >= 0 && k < String.length s then + begin + s.[k] <- c; Sunspec + end + else + raise (Error "string-set!: out of bounds") + | _ -> raise (Error "string-set!: bad args") +;; + +let string_cmp op s1 s2 = + match (s1, s2) with + (Sstring s1, Sstring s2) -> if op s1 s2 then Strue else Sfalse + | _ -> raise (Error "bad args") +;; + +let string_eq = string_cmp (=);; +let string_lt = string_cmp (<);; +let string_gt = string_cmp (>);; +let string_le = string_cmp (<=);; +let string_ge = string_cmp (>=);; + +let string_ci_cmp op s1 s2 = + match (s1, s2) with + (Sstring s1, Sstring s2) -> + if op (String.lowercase s1) (String.lowercase s2) then Strue else Sfalse + | _ -> raise (Error "bad args") +;; + +let string_ci_eq = string_ci_cmp (=);; +let string_ci_lt = string_ci_cmp (<);; +let string_ci_gt = string_ci_cmp (>);; +let string_ci_le = string_ci_cmp (<=);; +let string_ci_ge = string_ci_cmp (>=);; + +let string_append av = + Sstring + (Array.fold_left (^) "" + (Array.map (function + Sstring s -> s + | _ -> raise (Error "string-append: bad args")) av)) +;; + +let substring s sp ep = + match (s, sp, ep) with + (Sstring s, Sint sp, Sint ep) -> + let n = String.length s in + if sp >= 0 && sp <= ep && ep <= n then + Sstring (String.sub s sp (ep - sp)) + else + raise (Error "substring: out of bounds") + | _ -> raise (Error "substring: bad args") +;; + +let string_to_list = + function + Sstring s -> + begin + let rec loop i r = + if i < 0 then r + else loop (i - 1) (Spair { car = Schar s.[i]; cdr = r }) + in + loop (String.length s - 1) Snull + end + | _ -> raise (Error "string->list: not a string") +;; + +let string_copy = + function + Sstring s -> Sstring (String.copy s) + | _ -> raise (Error "string-copy: not a string") +;; + +let string_fill s c = + match (s, c) with + (Sstring s, Schar c) -> + String.fill s 0 (String.length s) c; Sunspec + | _ -> raise (Error "string-fill!: bad args") +;; + +let init e = + set_pfn e make_string "make-string"; + set_pfn e string_of "string"; + + set_pf1 e string_length "string-length"; + + set_pf2 e string_ref "string-ref"; + set_pf3 e string_set "string-set!"; + + set_pf2 e string_eq "string=?"; + set_pf2 e string_lt "string?"; + set_pf2 e string_le "string<=?"; + set_pf2 e string_ge "string>=?"; + + set_pf2 e string_ci_eq "string-ci=?"; + set_pf2 e string_ci_lt "string-ci?"; + set_pf2 e string_ci_le "string-ci<=?"; + set_pf2 e string_ci_ge "string-ci>=?"; + + set_pf3 e substring "substring"; + + set_pfn e string_append "string-append"; + + set_pf1 e string_to_list "string->list"; + + set_pf1 e string_copy "string-copy"; + + set_pf2 e string_fill "string-fill!"; +;; diff --git a/ocs-1.0.3/src/ocs_string.mli b/ocs-1.0.3/src/ocs_string.mli new file mode 100644 index 0000000..7c7287a --- /dev/null +++ b/ocs-1.0.3/src/ocs_string.mli @@ -0,0 +1,6 @@ +(* String primitives *) + +open Ocs_types + +val init : env -> unit + diff --git a/ocs-1.0.3/src/ocs_sym.ml b/ocs-1.0.3/src/ocs_sym.ml new file mode 100644 index 0000000..62139ef --- /dev/null +++ b/ocs-1.0.3/src/ocs_sym.ml @@ -0,0 +1,59 @@ +(* Symbol table implementation. *) + +open Ocs_types +open Ocs_error + +(* Symbols are stored in a hash table of weak references. This + guarantees that they are unique, but they needn't be permanent. *) + +module HashSymbol = + struct + type t = sval + let equal a b = + match (a, b) with + (Ssymbol s1, Ssymbol s2) -> s1 = s2 + | _ -> false + let hash = Hashtbl.hash + end + +module SymTable = Weak.Make (HashSymbol) + +let symt = SymTable.create 307 + +let get_symbol s = + SymTable.merge symt (Ssymbol s) +;; + +let sym_name = + function + Ssymbol s -> s + | _ -> raise (Error "sym_name: not a symbol") +;; + +let sym_quote = get_symbol "quote" +let sym_lambda = get_symbol "lambda" +let sym_if = get_symbol "if" +let sym_set = get_symbol "set!" +let sym_begin = get_symbol "begin" +let sym_cond = get_symbol "cond" +let sym_and = get_symbol "and" +let sym_or = get_symbol "or" +let sym_case = get_symbol "case" +let sym_let = get_symbol "let" +let sym_letstar = get_symbol "let*" +let sym_letrec = get_symbol "letrec" +let sym_do = get_symbol "do" +let sym_delay = get_symbol "delay" +let sym_quasiquote = get_symbol "quasiquote" +let sym_else = get_symbol "else" +let sym_arrow = get_symbol "=>" +let sym_define = get_symbol "define" +let sym_unquote = get_symbol "unquote" +let sym_unquote_splicing = get_symbol "unquote-splicing" + +let sym_define_syntax = get_symbol "define-syntax" +let sym_let_syntax = get_symbol "let-syntax" +let sym_letrec_syntax = get_symbol "letrec-syntax" +let sym_syntax_rules = get_symbol "syntax-rules" +let sym_ellipsis = get_symbol "..." + diff --git a/ocs-1.0.3/src/ocs_sym.mli b/ocs-1.0.3/src/ocs_sym.mli new file mode 100644 index 0000000..f9a0d2f --- /dev/null +++ b/ocs-1.0.3/src/ocs_sym.mli @@ -0,0 +1,36 @@ +(* Symbol table interface. *) + +open Ocs_types + +(* get_symbol returns a symbol corresponding to a string. It is + created if it doesn't exist. *) +val get_symbol : string -> sval +val sym_name : sval -> string + +(* Keywords are globally defined for convenience. *) +val sym_quote : sval +val sym_lambda : sval +val sym_if : sval +val sym_set : sval +val sym_begin : sval +val sym_cond : sval +val sym_and : sval +val sym_or : sval +val sym_case : sval +val sym_let : sval +val sym_letstar : sval +val sym_letrec : sval +val sym_do : sval +val sym_delay : sval +val sym_quasiquote : sval +val sym_else : sval +val sym_arrow : sval +val sym_define : sval +val sym_unquote : sval +val sym_unquote_splicing : sval + +val sym_define_syntax : sval +val sym_let_syntax : sval +val sym_letrec_syntax : sval +val sym_syntax_rules : sval +val sym_ellipsis : sval diff --git a/ocs-1.0.3/src/ocs_top.ml b/ocs-1.0.3/src/ocs_top.ml new file mode 100644 index 0000000..14fc07c --- /dev/null +++ b/ocs-1.0.3/src/ocs_top.ml @@ -0,0 +1,75 @@ +(* Top level, create and initialize the environment. *) + +open Ocs_types +open Ocs_error +open Ocs_env +open Ocs_compile +open Ocs_eval +open Ocs_print +open Ocs_macro + +(* Create a top-level environment and bind standard primitives. *) +let make_env () = + let e = top_env () in + bind_lang e; + bind_macro e; + Ocs_num.init e; + Ocs_numstr.init e; + Ocs_prim.init e; + Ocs_vector.init e; + Ocs_list.init e; + Ocs_char.init e; + Ocs_string.init e; + Ocs_contin.init e; + Ocs_io.init e; + e +;; + +(* Create a top-level thread. *) +let make_thread () = + { th_display = [| |]; + th_frame = [| |]; + th_depth = -1; + th_stdin = Sport (Ocs_port.input_port stdin); + th_stdout = Sport (Ocs_port.output_port stdout); + th_dynext = None } +;; + +let get_port = + function + Sport p -> p + | _ -> failwith "expected port" +;; + +(* Top-level loop for interaction. *) +let top_loop env th = + let inp = get_port th.th_stdin + and outp = get_port th.th_stdout + and errp = Ocs_port.output_port stderr in + let lex = Ocs_lex.make_lexer inp "" in + let rec loop () = + Ocs_port.puts outp "> "; + Ocs_port.flush outp; + try + match Ocs_read.read_expr lex with + Seof -> () + | v -> + let c = compile env v in + eval th (function Sunspec -> () + | r -> + print outp false r; + Ocs_port.putc outp '\n') c; + loop () + with Error err | ErrorL (_, err) -> + Ocs_port.puts errp ("Error: " ^ err ^ "\n"); + Ocs_port.flush errp; + loop () + in + loop () +;; + +(* Simple interface to invoke the interactive Scheme environment. *) +let interactive () = + top_loop (make_env ()) (make_thread ()) +;; + diff --git a/ocs-1.0.3/src/ocs_top.mli b/ocs-1.0.3/src/ocs_top.mli new file mode 100644 index 0000000..4280cb0 --- /dev/null +++ b/ocs-1.0.3/src/ocs_top.mli @@ -0,0 +1,10 @@ +(* Create and initialize top level environment. *) + +open Ocs_types + +val make_env : unit -> env +val make_thread : unit -> thread +val top_loop : env -> thread -> unit + +val interactive : unit -> unit + diff --git a/ocs-1.0.3/src/ocs_types.mli b/ocs-1.0.3/src/ocs_types.mli new file mode 100644 index 0000000..4203dff --- /dev/null +++ b/ocs-1.0.3/src/ocs_types.mli @@ -0,0 +1,200 @@ +(* Main types used *) + +open Ocs_vartable + +(* We have to declare most types here to avoid cross-dependencies between + compilation units. *) + +type sval = + (* Global variables are set to Sunbound when referenced but not assigned. *) + Sunbound + + (* List terminator. *) + | Snull + + (* End-of-file indicator. *) + | Seof + + (* Boolean values. This is more compact than Sbool of bool. *) + | Strue + | Sfalse + + (* String object. *) + | Sstring of string + + (* Symbol type. Symbols should not be created directly, but using + Ocs_sym.get_symbol, which ensures that they are unique and can be + compared using ==. *) + | Ssymbol of string + + (* Numeric types. *) + | Sint of int + | Sreal of float + | Scomplex of Complex.t + | Sbigint of Big_int.big_int + | Srational of Ratio.ratio + + (* Character. *) + | Schar of char + + (* A pair (list element). *) + | Spair of spair + + (* Vector. *) + | Svector of sval array + + (* Port object. *) + | Sport of Ocs_port.port + + (* A closure created by combining the process reference with the + local environment at that point of execution. *) + | Sproc of sproc * sval array array + + (* Primitive function. *) + | Sprim of sprim + + (* Delayed expression. *) + | Spromise of spromise + + (* A set of values returned by the 'values' primitive, + deconstructed into multiple parameters by call-with-values. *) + | Svalues of sval array + + (* A symbol explicitly tied to an environment that defines its scope. + These symbols are generated by macro expansions and eliminated + prior to evaluation. *) + | Sesym of env * sval + + (* Wrapped values are stub functions that encapsulate external values + of unknown types. *) + | Swrapped of (unit -> unit) + + (* An unspecified value. *) + | Sunspec + + (* The actual type of a pair (cons cell). *) +and spair = + { + mutable car : sval; + mutable cdr : sval + } + + (* Primitive structure. *) +and sprim = + { + prim_fun : primf; + prim_name : string + } + + (* Primitive function types. *) +and primf = + (* Simple functional interface to primitives with a small, constant + number of arguments. *) + Pf0 of (unit -> sval) + | Pf1 of (sval -> sval) + | Pf2 of (sval -> sval -> sval) + | Pf3 of (sval -> sval -> sval -> sval) + + (* Functional interface to primitives with a variable number of arguments. *) + | Pfn of (sval array -> sval) + + (* Continuation-based interface to primitives, also includes the thread + and supports a variable number of arguments. *) + | Pfcn of (thread -> (sval -> unit) -> sval array -> unit) + + (* Procedure structure. *) +and sproc = + { + proc_body : code; + proc_nargs : int; + proc_has_rest : bool; + proc_frame_size : int; + mutable proc_name : string + } + + (* Delayed expression. *) +and spromise = + { + promise_code : code; + mutable promise_val : sval option; + mutable promise_th : thread option (* Copy of the thread state *) + } + + (* Code types are used to represent analyzed expressions prepared for + evaluation. *) +and code = + Cval of sval + | Cseq2 of code * code + | Cseq3 of code * code * code + | Cseqn of code array + | Cand2 of code * code + | Cand3 of code * code * code + | Candn of code array + | Cor2 of code * code + | Cor3 of code * code * code + | Corn of code array + | Cif of code * code * code + | Csetg of gvar * code + | Csetl of int * int * code + | Cdefine of gvar * code + | Cgetg of gvar + | Cgetl of int * int + | Capply0 of code + | Capply1 of code * code + | Capply2 of code * code * code + | Capply3 of code * code * code * code + | Capplyn of code * code array + | Clambda of sproc + | Cqqp of code * code + | Cqqv of code array + | Cqqvs of code list + | Cqqspl of code + | Ccond of (code * code) array + | Ccondspec of code + | Ccase of code * (sval array * code) array + | Cdelay of code + + (* Global variable slot. *) +and gvar = + { + mutable g_sym : sval; + mutable g_val : sval + } + + (* Thread state, used during evaluation. *) +and thread = + { + th_frame : sval array; (* Current local frame. *) + th_display : sval array array; (* Current display. *) + th_depth : int; (* Display depth. *) + th_stdin : sval; (* Default input port. *) + th_stdout : sval; (* Default output port. *) + th_dynext : dynext option (* Current dynamic extent. *) + } + + (* Bindings, used during analysis. *) +and vbind = + Vglob of gvar + | Vloc of int * int + | Vsyntax of (env -> sval array -> code) + | Vmacro of (env -> sval array -> sval) + | Vkeyword of string + + (* Environment, used during analysis. *) +and env = + { + env_depth : int; + env_vartable : vbind vartable; + env_frame_size : int ref; + mutable env_tagged : (env * sval * vbind) list + } + + (* Dynamic extents are associated with threads and continuations. *) +and dynext = + { + dynext_parent : dynext option; + dynext_depth : int; + dynext_before : thread * code; + dynext_after : thread * code + } + diff --git a/ocs-1.0.3/src/ocs_vartable.ml b/ocs-1.0.3/src/ocs_vartable.ml new file mode 100644 index 0000000..85f1943 --- /dev/null +++ b/ocs-1.0.3/src/ocs_vartable.ml @@ -0,0 +1,65 @@ +(* Utility type for variable binding tables. *) + +module VarTable = Map.Make (String) + +type 'a vartable = { + vt_global : 'a vartable option; + mutable vt_bindings : 'a VarTable.t +} + +let vt_create () = + { vt_global = None; + vt_bindings = VarTable.empty } +;; + +let vt_inherit vt = + { vt_global = + if vt.vt_global = None then Some vt + else vt.vt_global; + vt_bindings = vt.vt_bindings } +;; + +let vt_global vt = + vt.vt_global = None +;; + +let rec vt_copy vt vc = + { vt_global = + (match vt.vt_global with + Some t -> Some (vt_copy t vc) + | _ -> None); + vt_bindings = + VarTable.map vc vt.vt_bindings } +;; + +let var_insert vt key r = + vt.vt_bindings <- VarTable.add key r vt.vt_bindings +;; + +let rec var_find vt key = + try + Some (VarTable.find key vt.vt_bindings) + with + Not_found -> + begin + match vt.vt_global with + Some x -> var_find x key + | _ -> None + end +;; + +let rec var_get vt key mkvar = + try + VarTable.find key vt.vt_bindings + with + Not_found -> + begin + match vt.vt_global with + Some x -> var_get x key mkvar + | _ -> + let r = mkvar () in + var_insert vt key r; + r + end +;; + diff --git a/ocs-1.0.3/src/ocs_vartable.mli b/ocs-1.0.3/src/ocs_vartable.mli new file mode 100644 index 0000000..2c5b946 --- /dev/null +++ b/ocs-1.0.3/src/ocs_vartable.mli @@ -0,0 +1,13 @@ +(* Utility type for variable binding tables. *) + +type 'a vartable + +val vt_create : unit -> 'a vartable +val vt_inherit : 'a vartable -> 'a vartable +val vt_global : 'a vartable -> bool +val vt_copy : 'a vartable -> ('a -> 'a) -> 'a vartable + +val var_insert : 'a vartable -> string -> 'a -> unit +val var_find : 'a vartable -> string -> 'a option +val var_get : 'a vartable -> string -> (unit -> 'a) -> 'a + diff --git a/ocs-1.0.3/src/ocs_vector.ml b/ocs-1.0.3/src/ocs_vector.ml new file mode 100644 index 0000000..5916f46 --- /dev/null +++ b/ocs-1.0.3/src/ocs_vector.ml @@ -0,0 +1,79 @@ +(* Vector primitives. *) + +open Ocs_types +open Ocs_error +open Ocs_env + +let get_int = + function + Sint i -> i + | _ -> raise (Error "bad arg types") + +let make_vector av = + match Array.length av with + (1 | 2) as n -> + let size = get_int av.(0) + and fill = if n = 2 then av.(1) else Snull in + Svector (Array.make size fill) + | _ -> raise (Error "make-vector: wrong number of args") + +let vector_of v = + Svector v +;; + +let vector_length = + function + Svector v -> Sint (Array.length v) + | _ -> raise (Error "vector-length: not a vector") +;; + +let vector_ref sv i = + match (sv, i) with + (Svector vec, Sint i) -> vec.(i) + | _ -> raise (Error "vector-ref: bad arg types") +;; + +let vector_set sv i v = + match (sv, i) with + (Svector vec, Sint i) -> vec.(i) <- v; Sunspec + | _ -> raise (Error "vector-set!: bad arg types") +;; + +let vector_to_list = + function + Svector v -> + begin + let rec loop i r = + if i < 0 then r + else loop (i - 1) (Spair { car = v.(i); cdr = r }) + in + loop (Array.length v - 1) Snull + end + | _ -> raise (Error "vector->list: bad args") +;; + +let vector_fill sv v = + match sv with + Svector vec -> + for i = 0 to Array.length vec - 1 do + vec.(i) <- v + done; + Sunspec + | _ -> raise (Error "vector-fill!: bad args") +;; + +let init e = + set_pfn e make_vector "make-vector"; + + set_pfn e vector_of "vector"; + + set_pf1 e vector_length "vector-length"; + + set_pf2 e vector_ref "vector-ref"; + set_pf3 e vector_set "vector-set!"; + + set_pf1 e vector_to_list "vector->list"; + + set_pf2 e vector_fill "vector-fill!"; +;; + diff --git a/ocs-1.0.3/src/ocs_vector.mli b/ocs-1.0.3/src/ocs_vector.mli new file mode 100644 index 0000000..4282981 --- /dev/null +++ b/ocs-1.0.3/src/ocs_vector.mli @@ -0,0 +1,6 @@ +(* Vector primitives *) + +open Ocs_types + +val init : env -> unit + diff --git a/ocs-1.0.3/src/ocs_wrap.ml b/ocs-1.0.3/src/ocs_wrap.ml new file mode 100644 index 0000000..1680cd5 --- /dev/null +++ b/ocs-1.0.3/src/ocs_wrap.ml @@ -0,0 +1,35 @@ +(* Functor for creating wrap/unwrap functions *) + +open Ocs_types +open Ocs_error + +module Make(T: sig type t end) = + struct + type t = T.t + + exception E of t + + let wrap v = + Swrapped (fun () -> raise (E v)) + + let unwrap = + function + Swrapped f -> + (try + f (); + raise (Error "unwrap: internal error") + with E v -> v + | _ -> raise (Error "unwrap: wrong wrapped type")) + | _ -> raise (Error "unwrap: not a wrapped type") + + let try_unwrap = + function + Swrapped f -> + (try + f (); + None + with E v -> Some v + | _ -> None) + | _ -> None + end +