Created
January 10, 2015 16:52
-
-
Save yallop/7d7e471ece10f5893642 to your computer and use it in GitHub Desktop.
BER n102 patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -Naur ocaml-4.02.1/.depend ocaml-ber-n102/.depend | |
--- ocaml-4.02.1/.depend 2015-01-10 16:27:54.270865651 +0000 | |
+++ ocaml-ber-n102/.depend 2015-01-10 16:27:06.936030975 +0000 | |
@@ -122,6 +122,9 @@ | |
typing/stypes.cmi : typing/typedtree.cmi parsing/location.cmi \ | |
typing/annot.cmi | |
typing/subst.cmi : typing/types.cmi typing/path.cmi typing/ident.cmi | |
+typing/trx.cmi : typing/types.cmi typing/typedtree.cmi parsing/parsetree.cmi \ | |
+ parsing/longident.cmi parsing/location.cmi typing/env.cmi \ | |
+ parsing/asttypes.cmi | |
typing/typeclass.cmi : typing/types.cmi typing/typedtree.cmi \ | |
parsing/parsetree.cmi parsing/longident.cmi parsing/location.cmi \ | |
typing/ident.cmi typing/env.cmi typing/ctype.cmi parsing/asttypes.cmi | |
@@ -280,6 +283,18 @@ | |
typing/subst.cmx : typing/types.cmx utils/tbl.cmx typing/path.cmx \ | |
utils/misc.cmx parsing/location.cmx typing/ident.cmx utils/clflags.cmx \ | |
typing/btype.cmx parsing/ast_mapper.cmx typing/subst.cmi | |
+typing/trx.cmo : utils/warnings.cmi typing/types.cmi typing/typedtree.cmi \ | |
+ typing/printtyp.cmi typing/predef.cmi typing/path.cmi \ | |
+ parsing/parsetree.cmi utils/misc.cmi parsing/longident.cmi \ | |
+ parsing/location.cmi typing/ident.cmi typing/env.cmi typing/ctype.cmi \ | |
+ typing/btype.cmi parsing/asttypes.cmi parsing/ast_helper.cmi \ | |
+ typing/trx.cmi | |
+typing/trx.cmx : utils/warnings.cmx typing/types.cmx typing/typedtree.cmx \ | |
+ typing/printtyp.cmx typing/predef.cmx typing/path.cmx \ | |
+ parsing/parsetree.cmi utils/misc.cmx parsing/longident.cmx \ | |
+ parsing/location.cmx typing/ident.cmx typing/env.cmx typing/ctype.cmx \ | |
+ typing/btype.cmx parsing/asttypes.cmi parsing/ast_helper.cmx \ | |
+ typing/trx.cmi | |
typing/typeclass.cmo : utils/warnings.cmi typing/typetexp.cmi \ | |
typing/types.cmi typing/typedtree.cmi typing/typedecl.cmi \ | |
typing/typecore.cmi parsing/syntaxerr.cmi typing/subst.cmi \ | |
@@ -299,23 +314,25 @@ | |
utils/clflags.cmx typing/btype.cmx parsing/asttypes.cmi \ | |
parsing/ast_helper.cmx typing/typeclass.cmi | |
typing/typecore.cmo : utils/warnings.cmi typing/typetexp.cmi \ | |
- typing/types.cmi typing/typedtree.cmi parsing/syntaxerr.cmi \ | |
- typing/subst.cmi typing/stypes.cmi typing/printtyp.cmi \ | |
- typing/primitive.cmi typing/predef.cmi typing/path.cmi \ | |
- parsing/parsetree.cmi typing/parmatch.cmi typing/oprint.cmi \ | |
- utils/misc.cmi parsing/longident.cmi parsing/location.cmi \ | |
- typing/ident.cmi typing/env.cmi typing/ctype.cmi typing/cmt_format.cmi \ | |
- utils/clflags.cmi typing/btype.cmi parsing/asttypes.cmi \ | |
- parsing/ast_helper.cmi typing/annot.cmi typing/typecore.cmi | |
+ typing/types.cmi typing/typedtree.cmi typing/trx.cmi \ | |
+ parsing/syntaxerr.cmi typing/subst.cmi typing/stypes.cmi \ | |
+ typing/printtyp.cmi typing/primitive.cmi typing/predef.cmi \ | |
+ typing/path.cmi parsing/parsetree.cmi typing/parmatch.cmi \ | |
+ typing/oprint.cmi utils/misc.cmi parsing/longident.cmi \ | |
+ parsing/location.cmi typing/ident.cmi typing/env.cmi typing/ctype.cmi \ | |
+ typing/cmt_format.cmi utils/clflags.cmi typing/btype.cmi \ | |
+ parsing/asttypes.cmi parsing/ast_helper.cmi typing/annot.cmi \ | |
+ typing/typecore.cmi | |
typing/typecore.cmx : utils/warnings.cmx typing/typetexp.cmx \ | |
- typing/types.cmx typing/typedtree.cmx parsing/syntaxerr.cmx \ | |
- typing/subst.cmx typing/stypes.cmx typing/printtyp.cmx \ | |
- typing/primitive.cmx typing/predef.cmx typing/path.cmx \ | |
- parsing/parsetree.cmi typing/parmatch.cmx typing/oprint.cmx \ | |
- utils/misc.cmx parsing/longident.cmx parsing/location.cmx \ | |
- typing/ident.cmx typing/env.cmx typing/ctype.cmx typing/cmt_format.cmx \ | |
- utils/clflags.cmx typing/btype.cmx parsing/asttypes.cmi \ | |
- parsing/ast_helper.cmx typing/annot.cmi typing/typecore.cmi | |
+ typing/types.cmx typing/typedtree.cmx typing/trx.cmx \ | |
+ parsing/syntaxerr.cmx typing/subst.cmx typing/stypes.cmx \ | |
+ typing/printtyp.cmx typing/primitive.cmx typing/predef.cmx \ | |
+ typing/path.cmx parsing/parsetree.cmi typing/parmatch.cmx \ | |
+ typing/oprint.cmx utils/misc.cmx parsing/longident.cmx \ | |
+ parsing/location.cmx typing/ident.cmx typing/env.cmx typing/ctype.cmx \ | |
+ typing/cmt_format.cmx utils/clflags.cmx typing/btype.cmx \ | |
+ parsing/asttypes.cmi parsing/ast_helper.cmx typing/annot.cmi \ | |
+ typing/typecore.cmi | |
typing/typedecl.cmo : utils/warnings.cmi typing/typetexp.cmi \ | |
typing/types.cmi typing/typedtree.cmi parsing/syntaxerr.cmi \ | |
typing/subst.cmi typing/printtyp.cmi typing/primitive.cmi \ | |
diff -Naur ocaml-4.02.1/Makefile ocaml-ber-n102/Makefile | |
--- ocaml-4.02.1/Makefile 2015-01-10 16:27:54.270865651 +0000 | |
+++ ocaml-ber-n102/Makefile 2015-01-10 16:27:06.936030975 +0000 | |
@@ -20,6 +20,9 @@ | |
COMPFLAGS=-strict-sequence -w +33..39+48 -warn-error A -bin-annot \ | |
-safe-string $(INCLUDES) | |
LINKFLAGS= | |
+# For debugging | |
+# COMPFLAGS=-warn-error A -g $(INCLUDES) # NNN | |
+# LINKFLAGS=-g #NNN | |
CAMLYACC=boot/ocamlyacc | |
YACCFLAGS=-v | |
@@ -49,6 +52,7 @@ | |
parsing/pprintast.cmo \ | |
parsing/ast_mapper.cmo | |
+# NNN (trx) | |
TYPING=typing/ident.cmo typing/path.cmo \ | |
typing/primitive.cmo typing/types.cmo \ | |
typing/btype.cmo typing/oprint.cmo \ | |
@@ -59,6 +63,7 @@ | |
typing/mtype.cmo typing/envaux.cmo typing/includecore.cmo \ | |
typing/typedtreeIter.cmo typing/typedtreeMap.cmo typing/cmt_format.cmo \ | |
typing/includemod.cmo typing/typetexp.cmo typing/parmatch.cmo \ | |
+ typing/trx.cmo \ | |
typing/stypes.cmo typing/typecore.cmo \ | |
typing/typedecl.cmo typing/typeclass.cmo \ | |
typing/typemod.cmo | |
@@ -130,6 +135,8 @@ | |
$(MAKE) otherlibraries $(OCAMLBUILDBYTE) $(WITH_DEBUGGER) \ | |
$(WITH_OCAMLDOC) | |
+# NNN make all && (cd metalib && make clean all) && (make install; cd metalib && make install) | |
+ | |
# Compile everything the first time | |
world: | |
$(MAKE) coldstart | |
@@ -325,6 +332,13 @@ | |
$(INSTALL_COMPLIBDIR) | |
cp expunge $(INSTALL_LIBDIR)/expunge$(EXE) | |
cp toplevel/topdirs.cmi $(INSTALL_LIBDIR) | |
+# NNN typing/trx.ml needs its own interface (since it looks up identifiers | |
+# in itself) | |
+# Although typing/trx.cmi is already copied, see above, it is copied | |
+# into $((COMPLIBDIR). We need trx.cmi in the standard .cmi search path. | |
+ cp typing/trx.cmi $(INSTALL_LIBDIR) | |
+# BTW, trx.cmo is part of ocamlcommon.cma | |
+# NNN end | |
cd tools; $(MAKE) install | |
-cd man; $(MAKE) install | |
for i in $(OTHERLIBRARIES); do \ | |
@@ -744,7 +758,7 @@ | |
partialclean:: | |
cd ocamldoc && $(MAKE) clean | |
-alldepend:: | |
+alldepen1d:: | |
cd ocamldoc && $(MAKE) depend | |
# The extra libraries | |
diff -Naur ocaml-4.02.1/metalib/berstart.ml ocaml-ber-n102/metalib/berstart.ml | |
--- ocaml-4.02.1/metalib/berstart.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/berstart.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,21 @@ | |
+(* | |
+ To `run' the code we use toplevel facilities. | |
+ If we invoke BER MetaOcaml top level, then Toplevel.topstart() will | |
+ initalialize the top level. | |
+ If we execute a byte-compiled executable, we link with | |
+ the top-level library. But we need initialize it first. | |
+ This is the job of the current file. | |
+ | |
+ This file must be linked in *before* the first user executable. | |
+ | |
+ The present code roughly do the same steps OCaml top level does | |
+ when executing a script. | |
+ See Toplevel.topmain | |
+*) | |
+ | |
+let () = | |
+ Toploop.set_paths (); | |
+ Compmisc.init_path true; | |
+ Toploop.initialize_toplevel_env () | |
+ (* toplevel_env := Compile.initial_env(); | |
+ *) | |
diff -Naur ocaml-4.02.1/metalib/bertop.ml ocaml-ber-n102/metalib/bertop.ml | |
--- ocaml-4.02.1/metalib/bertop.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/bertop.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,26 @@ | |
+(* `Plugin' for the OCaml top-level *) | |
+open Longident | |
+ | |
+(* Install printers for code values *) | |
+let install_printers () = | |
+ Topdirs.dir_install_printer Format.std_formatter | |
+ (Ldot(Lident "Print_code", "print_code")); | |
+ Topdirs.dir_install_printer Format.std_formatter | |
+ (Ldot(Lident "Print_code", "print_closed_code")) | |
+ | |
+(* Initialization function *) | |
+ | |
+let initialize () = | |
+ Printf.printf "BER MetaOCaml toplevel, version %s\n" Trx.meta_version; | |
+ install_printers () | |
+ | |
+(* Hook up to the top level *) | |
+let () = | |
+Toploop.toplevel_startup_hook := | |
+ let old_hook = !Toploop.toplevel_startup_hook in | |
+ fun () -> | |
+ begin | |
+ initialize (); | |
+ old_hook () | |
+ end | |
+ | |
diff -Naur ocaml-4.02.1/metalib/build_patch.sh ocaml-ber-n102/metalib/build_patch.sh | |
--- ocaml-4.02.1/metalib/build_patch.sh 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/build_patch.sh 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,21 @@ | |
+#!/bin/sh | |
+# Building the patch set | |
+# build_patch | |
+# This auxiliary file builds the patch set using the internal | |
+# GIT repository. The patch set is written into the standard output. | |
+ | |
+cd .. | |
+ | |
+#git diff 327f91b41f16b223c783070fa44058c5b1db8804 -- | |
+git diff 4.02 -- \ | |
+./.depend \ | |
+./Makefile \ | |
+./parsing/lexer.mll \ | |
+./parsing/parser.mly \ | |
+./parsing/pprintast.ml \ | |
+./typing/predef.ml \ | |
+./typing/predef.mli \ | |
+./typing/typecore.ml | |
+ | |
+# svn diff -r5522 \ | |
+# tools/addlabels.ml \ | |
diff -Naur ocaml-4.02.1/metalib/ChangeLog ocaml-ber-n102/metalib/ChangeLog | |
--- ocaml-4.02.1/metalib/ChangeLog 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/ChangeLog 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,97 @@ | |
+December 31, 2014 | |
+ Release N102 | |
+ - There is no longer a separate Typedtree traversal pass, after | |
+ the type checking, to translate brackets and escapes. The | |
+ translation is done right after the type-checking of the 0-level | |
+ bracket. That means that for staging-annotation-free code, | |
+ MetaOCaml has no substantial overhead. | |
+ - Use attributes for brackets, escapes and CSP (and internally | |
+ for levels and inexpansiveness). Much fewer OCaml files are | |
+ modified. | |
+ - New function Runcode.typecheck_code for the benefit of offshoring | |
+ - New function Runcode.add_search_path to add a directory to | |
+ the search path for .cmi/.cmo files -- report by Nicolas Ojeda Bar | |
+ - Better handling of CSP. For simple cases, there are no longer | |
+ any serialization problems. | |
+ - Overall better printing, of CSP, code and error messages. | |
+ - Overall, more uniform, better-factored code. | |
+ - metaocamlc is now a simple .c file; it adds compiler-libs only | |
+ in the case when the executable is built. Otherwise, we may end up | |
+ with the duplicate libraries. | |
+ | |
+April 24, 2014 | |
+ Fixed the problem noted by Jun Inoue in toplevel MetaOCaml. | |
+ If the first Runcode.run is used after a type declaration, | |
+ the defined type is considered locally scoped, which causes | |
+ type checking errors or assettion faliures in typechecking. | |
+ Preparing the environment for Runcode.run has a side-effect, | |
+ of corrupting Ident timestamps. The timestamps are preserved now. | |
+ | |
+November 26, 2013 | |
+ Release N101 | |
+ Removed environment classifiers. | |
+ Syntax .! is replaced with the ordinary prefix operation | |
+ !. (alias run) in the module Runcode. Runcode.run is an | |
+ ordinary function, and is not part of the MetaOCaml kernel. | |
+ New API for running code, encouraging the development of new ways | |
+ to execute code values (see metalib/runcode.mli). | |
+ BER N101 is binary compatible with OCaml 4.01. Building it | |
+ no longer involves bootstrapping. | |
+ Printing code is now part of OCaml (Pprintast, which was influenced by | |
+ MetaOCaml). | |
+ Scope-extrusion check works with delimited control. | |
+ Added a test for the well-formedness of recursive let. | |
+ Faster generation of code (especially for functions and nonbinding | |
+ functions). | |
+ More precise tracking of free variables and reporting of scope | |
+ extrusion. | |
+ | |
+February 20, 2013 | |
+ Re-enabled ocamlbuild.byte target in the Makefile, so to | |
+ to build ocamlbuild as part of the make all. | |
+ A small fix to permit compiling ocamlopt. Although at present, | |
+ ocamlopt can't compile code with brackets, it can compile | |
+ the regular OCaml code. In addition, it supports let!. | |
+ Thanks to Bob Zhang for suggestions. | |
+ | |
+January 30, 2013 | |
+ Rewritten trx.ml. Release N100. | |
+ | |
+January 12, 2013 | |
+ Importing the pretty-printing of code, greatly improved by Jacques | |
+ Carette. | |
+ | |
+January 7-12, 2013 | |
+ Beginning the port to OCaml 4.00.1. The basic changes to OCaml | |
+ are done. Started work on re-writing trx.ml | |
+ | |
+December 28, 2012 | |
+ Version N004 released | |
+ | |
+November 8, 2012 | |
+ Eliminating val_level from value_desc. Introducing Env map stage | |
+ to map an identifier to its stage, if not zero. Several | |
+ OCaml modules no longer have to be patched. | |
+ Simplified CSP code, removed a few patches from typecore. | |
+ SCP now carries strings, native_int, int32, int64 as | |
+ literal constants. Identifiers in List module are carried | |
+ by reference. | |
+ | |
+October 4, 2012 | |
+ Jacques Carette has committed Alain Frisch's patch implementing | |
+ the let! form proposed by Nicolas Pouillard for monadic | |
+ programming in Ocaml. | |
+ http://pauillac.inria.fr/~protzenk/let-bang.html | |
+ See metalib/test/pythagorian_triples.ml for an example. | |
+ | |
+January 3, 2011 | |
+ Added metaocamlmktop | |
+ | |
+December 2010 | |
+ Patch from Fabrice Le Fessant to remove functional values | |
+ from typing Env. See entry May 5, 2010 in Problems.txt. | |
+ Generic printing is now part of BER MetaOCaml. | |
+ | |
+March 1, 2010 | |
+ Version N002 released, based on OCaml 3.11 | |
+ | |
diff -Naur ocaml-4.02.1/metalib/cycle.h ocaml-ber-n102/metalib/cycle.h | |
--- ocaml-4.02.1/metalib/cycle.h 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/cycle.h 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,434 @@ | |
+/* | |
+ * Copyright (c) 2003 Matteo Frigo | |
+ * Copyright (c) 2003 Massachusetts Institute of Technology | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining | |
+ * a copy of this software and associated documentation files (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, | |
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ * | |
+ */ | |
+ | |
+/* $Id$ */ | |
+ | |
+/* machine-dependent cycle counters code. Needs to be inlined. */ | |
+ | |
+/***************************************************************************/ | |
+/* To use the cycle counters in your code, simply #include "cycle.h" (this | |
+ file), and then use the functions/macros: | |
+ | |
+ ticks getticks(void); | |
+ | |
+ ticks is an opaque typedef defined below, representing the current time. | |
+ You extract the elapsed time between two calls to gettick() via: | |
+ | |
+ double elapsed(ticks t1, ticks t0); | |
+ | |
+ which returns a double-precision variable in arbitrary units. You | |
+ are not expected to convert this into human units like seconds; it | |
+ is intended only for *comparisons* of time intervals. | |
+ | |
+ (In order to use some of the OS-dependent timer routines like | |
+ Solaris' gethrtime, you need to paste the autoconf snippet below | |
+ into your configure.ac file and #include "config.h" before cycle.h, | |
+ or define the relevant macros manually if you are not using autoconf.) | |
+*/ | |
+ | |
+/***************************************************************************/ | |
+/* This file uses macros like HAVE_GETHRTIME that are assumed to be | |
+ defined according to whether the corresponding function/type/header | |
+ is available on your system. The necessary macros are most | |
+ conveniently defined if you are using GNU autoconf, via the tests: | |
+ | |
+ dnl --------------------------------------------------------------------- | |
+ | |
+ AC_C_INLINE | |
+ AC_HEADER_TIME | |
+ AC_CHECK_HEADERS([sys/time.h c_asm.h intrinsics.h mach/mach_time.h]) | |
+ | |
+ AC_CHECK_TYPE([hrtime_t],[AC_DEFINE(HAVE_HRTIME_T, 1, [Define to 1 if hrtime_t is defined in <sys/time.h>])],,[#if HAVE_SYS_TIME_H | |
+#include <sys/time.h> | |
+#endif]) | |
+ | |
+ AC_CHECK_FUNCS([gethrtime read_real_time time_base_to_time clock_gettime mach_absolute_time]) | |
+ | |
+ dnl Cray UNICOS _rtc() (real-time clock) intrinsic | |
+ AC_MSG_CHECKING([for _rtc intrinsic]) | |
+ rtc_ok=yes | |
+ AC_TRY_LINK([#ifdef HAVE_INTRINSICS_H | |
+#include <intrinsics.h> | |
+#endif], [_rtc()], [AC_DEFINE(HAVE__RTC,1,[Define if you have the UNICOS _rtc() intrinsic.])], [rtc_ok=no]) | |
+ AC_MSG_RESULT($rtc_ok) | |
+ | |
+ dnl --------------------------------------------------------------------- | |
+*/ | |
+ | |
+/***************************************************************************/ | |
+ | |
+#if TIME_WITH_SYS_TIME | |
+# include <sys/time.h> | |
+# include <time.h> | |
+#else | |
+# if HAVE_SYS_TIME_H | |
+# include <sys/time.h> | |
+# else | |
+# include <time.h> | |
+# endif | |
+#endif | |
+ | |
+#define INLINE_ELAPSED(INL) static INL double elapsed(ticks t1, ticks t0) \ | |
+{ \ | |
+ return (double)(t1 - t0); \ | |
+} | |
+ | |
+/*----------------------------------------------------------------*/ | |
+/* Solaris */ | |
+#if (__sparc) | |
+// #if defined(HAVE_GETHRTIME) && defined(HAVE_HRTIME_T) && !defined(HAVE_TICK_COUNTER) | |
+typedef hrtime_t ticks; | |
+ | |
+#define getticks gethrtime | |
+ | |
+INLINE_ELAPSED(inline) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+/* AIX v. 4+ routines to read the real-time clock or time-base register */ | |
+#if defined(HAVE_READ_REAL_TIME) && defined(HAVE_TIME_BASE_TO_TIME) && !defined(HAVE_TICK_COUNTER) | |
+typedef timebasestruct_t ticks; | |
+ | |
+static inline ticks getticks(void) | |
+{ | |
+ ticks t; | |
+ read_real_time(&t, TIMEBASE_SZ); | |
+ return t; | |
+} | |
+ | |
+static inline double elapsed(ticks t1, ticks t0) /* time in nanoseconds */ | |
+{ | |
+ time_base_to_time(&t1, TIMEBASE_SZ); | |
+ time_base_to_time(&t0, TIMEBASE_SZ); | |
+ return ((t1.tb_high - t0.tb_high) * 1e9 + (t1.tb_low - t0.tb_low)); | |
+} | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+/* | |
+ * PowerPC ``cycle'' counter using the time base register. | |
+ */ | |
+#if ((defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))) || (defined(__MWERKS__) && defined(macintosh))) && !defined(HAVE_TICK_COUNTER) | |
+typedef unsigned long long ticks; | |
+ | |
+static __inline__ ticks getticks(void) | |
+{ | |
+ unsigned int tbl, tbu0, tbu1; | |
+ | |
+ do { | |
+ __asm__ __volatile__ ("mftbu %0" : "=r"(tbu0)); | |
+ __asm__ __volatile__ ("mftb %0" : "=r"(tbl)); | |
+ __asm__ __volatile__ ("mftbu %0" : "=r"(tbu1)); | |
+ } while (tbu0 != tbu1); | |
+ | |
+ return (((unsigned long long)tbu0) << 32) | tbl; | |
+} | |
+ | |
+INLINE_ELAPSED(__inline__) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/* MacOS/Mach (Darwin) time-base register interface (unlike UpTime, | |
+ from Carbon, requires no additional libraries to be linked). */ | |
+#if defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_MACH_TIME_H) && !defined(HAVE_TICK_COUNTER) | |
+#include <mach/mach_time.h> | |
+typedef uint64_t ticks; | |
+#define getticks mach_absolute_time | |
+INLINE_ELAPSED(__inline__) | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+/* | |
+ * Pentium cycle counter | |
+ */ | |
+#if (defined(__GNUC__) || defined(__ICC)) && defined(__i386__) && !defined(HAVE_TICK_COUNTER) | |
+typedef unsigned long long ticks; | |
+ | |
+static __inline__ ticks getticks(void) | |
+{ | |
+ ticks ret; | |
+ | |
+ __asm__ __volatile__("rdtsc": "=A" (ret)); | |
+ /* no input, nothing else clobbered */ | |
+ return ret; | |
+} | |
+ | |
+INLINE_ELAPSED(__inline__) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ | |
+#endif | |
+ | |
+/* Visual C++ -- thanks to Morten Nissov for his help with this */ | |
+#if _MSC_VER >= 1200 && _M_IX86 >= 500 && !defined(HAVE_TICK_COUNTER) | |
+#include <windows.h> | |
+typedef LARGE_INTEGER ticks; | |
+#define RDTSC __asm __emit 0fh __asm __emit 031h /* hack for VC++ 5.0 */ | |
+ | |
+static __inline ticks getticks(void) | |
+{ | |
+ ticks ret; | |
+ | |
+ __asm { | |
+ RDTSC | |
+ mov ret.HighPart, edx | |
+ mov ret.LowPart, eax | |
+ } | |
+ return ret; | |
+} | |
+ | |
+static __inline double elapsed(ticks t1, ticks t0) | |
+{ | |
+ return (double)(t1.QuadPart - t0.QuadPart); | |
+} | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+/* | |
+ * X86-64 cycle counter | |
+ */ | |
+#if defined(__GNUC__) && defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) | |
+typedef unsigned long long ticks; | |
+ | |
+static __inline__ ticks getticks(void) | |
+{ | |
+ unsigned a, d; | |
+ asm volatile("rdtsc" : "=a" (a), "=d" (d)); | |
+ return ((ticks)a) | (((ticks)d) << 32); | |
+} | |
+ | |
+INLINE_ELAPSED(__inline__) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/* Visual C++ (FIXME: how to detect compilation for x86-64?) */ | |
+#if _MSC_VER >= 1400 && !defined(HAVE_TICK_COUNTER) | |
+typedef ULONG64 ticks; | |
+ | |
+#define getticks __rdtsc | |
+ | |
+INLINE_ELAPSED(__inline) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+/* | |
+ * IA64 cycle counter | |
+ */ | |
+#if defined(__GNUC__) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER) | |
+typedef unsigned long ticks; | |
+ | |
+static __inline__ ticks getticks(void) | |
+{ | |
+ ticks ret; | |
+ | |
+ __asm__ __volatile__ ("mov %0=ar.itc" : "=r"(ret)); | |
+ return ret; | |
+} | |
+ | |
+INLINE_ELAPSED(__inline__) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/* HP/UX IA64 compiler, courtesy Teresa L. Johnson: */ | |
+#if defined(__hpux) && defined(__ia64) && !defined(HAVE_TICK_COUNTER) | |
+#include <machine/sys/inline.h> | |
+typedef unsigned long ticks; | |
+ | |
+static inline ticks getticks(void) | |
+{ | |
+ ticks ret; | |
+ | |
+ ret = _Asm_mov_from_ar (_AREG_ITC); | |
+ return ret; | |
+} | |
+ | |
+INLINE_ELAPSED(inline) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/* intel's ecc compiler */ | |
+#if defined(__ECC) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER) | |
+typedef unsigned long ticks; | |
+#include <ia64intrin.h> | |
+ | |
+static __inline__ ticks getticks(void) | |
+{ | |
+ return __getReg(_IA64_REG_AR_ITC); | |
+} | |
+ | |
+INLINE_ELAPSED(__inline__) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+/* | |
+ * PA-RISC cycle counter | |
+ */ | |
+#if defined(__hppa__) || defined(__hppa) && !defined(HAVE_TICK_COUNTER) | |
+typedef unsigned long ticks; | |
+ | |
+# ifdef __GNUC__ | |
+static __inline__ ticks getticks(void) | |
+{ | |
+ ticks ret; | |
+ | |
+ __asm__ __volatile__("mfctl 16, %0": "=r" (ret)); | |
+ /* no input, nothing else clobbered */ | |
+ return ret; | |
+} | |
+# else | |
+# include <machine/inline.h> | |
+static inline unsigned long getticks(void) | |
+{ | |
+ register ticks ret; | |
+ _MFCTL(16, ret); | |
+ return ret; | |
+} | |
+# endif | |
+ | |
+INLINE_ELAPSED(inline) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+/* S390, courtesy of James Treacy */ | |
+#if defined(__GNUC__) && defined(__s390__) && !defined(HAVE_TICK_COUNTER) | |
+typedef unsigned long long ticks; | |
+ | |
+static __inline__ ticks getticks(void) | |
+{ | |
+ ticks cycles; | |
+ __asm__("stck 0(%0)" : : "a" (&(cycles)) : "memory", "cc"); | |
+ return cycles; | |
+} | |
+ | |
+INLINE_ELAPSED(__inline__) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+/*----------------------------------------------------------------*/ | |
+#if defined(__GNUC__) && defined(__alpha__) && !defined(HAVE_TICK_COUNTER) | |
+/* | |
+ * The 32-bit cycle counter on alpha overflows pretty quickly, | |
+ * unfortunately. A 1GHz machine overflows in 4 seconds. | |
+ */ | |
+typedef unsigned int ticks; | |
+ | |
+static __inline__ ticks getticks(void) | |
+{ | |
+ unsigned long cc; | |
+ __asm__ __volatile__ ("rpcc %0" : "=r"(cc)); | |
+ return (cc & 0xFFFFFFFF); | |
+} | |
+ | |
+INLINE_ELAPSED(__inline__) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+#if defined(__GNUC__) && defined(__sparc_v9__) && !defined(HAVE_TICK_COUNTER) | |
+typedef unsigned long ticks; | |
+ | |
+static __inline__ ticks getticks(void) | |
+{ | |
+ ticks ret; | |
+ __asm__ __volatile__("rd %%tick, %0" : "=r" (ret)); | |
+ return ret; | |
+} | |
+ | |
+INLINE_ELAPSED(__inline__) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+#if defined(__DECC) && defined(__alpha) && defined(HAVE_C_ASM_H) && !defined(HAVE_TICK_COUNTER) | |
+# include <c_asm.h> | |
+typedef unsigned int ticks; | |
+ | |
+static __inline ticks getticks(void) | |
+{ | |
+ unsigned long cc; | |
+ cc = asm("rpcc %v0"); | |
+ return (cc & 0xFFFFFFFF); | |
+} | |
+ | |
+INLINE_ELAPSED(__inline) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+/*----------------------------------------------------------------*/ | |
+/* SGI/Irix */ | |
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_SGI_CYCLE) && !defined(HAVE_TICK_COUNTER) | |
+typedef struct timespec ticks; | |
+ | |
+static inline ticks getticks(void) | |
+{ | |
+ struct timespec t; | |
+ clock_gettime(CLOCK_SGI_CYCLE, &t); | |
+ return t; | |
+} | |
+ | |
+static inline double elapsed(ticks t1, ticks t0) | |
+{ | |
+ return (double)(t1.tv_sec - t0.tv_sec) * 1.0E9 + | |
+ (double)(t1.tv_nsec - t0.tv_nsec); | |
+} | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
+/*----------------------------------------------------------------*/ | |
+/* Cray UNICOS _rtc() intrinsic function */ | |
+#if defined(HAVE__RTC) && !defined(HAVE_TICK_COUNTER) | |
+#ifdef HAVE_INTRINSICS_H | |
+# include <intrinsics.h> | |
+#endif | |
+ | |
+typedef long long ticks; | |
+ | |
+#define getticks _rtc | |
+ | |
+INLINE_ELAPSED(inline) | |
+ | |
+#define HAVE_TICK_COUNTER | |
+#endif | |
+ | |
diff -Naur ocaml-4.02.1/metalib/.depend ocaml-ber-n102/metalib/.depend | |
--- ocaml-4.02.1/metalib/.depend 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/.depend 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,19 @@ | |
+metatop.cmi : | |
+print_code.cmi : runcode.cmi | |
+reify_type.cmi : | |
+runcode.cmi : | |
+trxtime.cmi : | |
+berstart.cmo : | |
+berstart.cmx : | |
+bertop.cmo : | |
+bertop.cmx : | |
+print_code.cmo : runcode.cmi print_code.cmi | |
+print_code.cmx : runcode.cmx print_code.cmi | |
+reify_type.cmo : reify_type.cmi | |
+reify_type.cmx : reify_type.cmi | |
+run_native.cmo : | |
+run_native.cmx : | |
+runcode.cmo : runcode.cmi | |
+runcode.cmx : runcode.cmi | |
+trxtime.cmo : trxtime.cmi | |
+trxtime.cmx : trxtime.cmi | |
diff -Naur ocaml-4.02.1/metalib/Files.txt ocaml-ber-n102/metalib/Files.txt | |
--- ocaml-4.02.1/metalib/Files.txt 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/Files.txt 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,68 @@ | |
+OCaml files affected by the BER MetaOCaml. | |
+Search for NNN in each files for concrete changes | |
+ | |
+./Makefile | |
+ | |
+done ./parsing/lexer.mll | |
+done ./parsing/parser.mly | |
+done ./parsing/pprintast.ml new in 4.01 | |
+ | |
+done ./typing/predef.ml | |
+done ./typing/predef.mli | |
+ | |
+ | |
+done ./typing/typecore.ml | |
+ | |
+ ./typing/trx.mli | |
+ ./typing/trx.ml | |
+ | |
+ | |
+No longer changed in N102 | |
+ | |
+./ocamldoc/Makefile | |
+ | |
+./parsing/parsetree.mli | |
+./parsing/printast.ml trivial | |
+./parsing/ast_mapper.ml new in 4.01, trivial | |
+ | |
+./typing/typedtree.mli | |
+./typing/typedtree.ml | |
+ | |
+./typing/printtyped.ml did not exist before, trivial changes | |
+./typing/typedtreeIter.ml in 4.01, moved from tools, trivial changes | |
+./typing/typedtreeMap.ml new in 4.01, trivial changes | |
+ | |
+./typing/env.mli | |
+./typing/env.ml | |
+./typing/envaux.ml new in 4.01, trivial changes | |
+ | |
+./typing/typecore.mli | |
+./typing/typemod.ml | |
+ | |
+./tools/depend.ml | |
+ | |
+./tools/ocamlprof.ml | |
+./tools/untypeast.ml did not exist before, trivial changes | |
+./tools/tast_iter.ml new in 4.01 | |
+ | |
+ | |
+No longer changed in N101 | |
+ ./typing/ident.ml No longer changed | |
+ ./tools/addlabels.ml not built any more? | |
+ ./typing/ident.mli No longer changed | |
+ ./typing/typeclass.ml No longer changed | |
+ ./typing/cmt_format.ml in 4.01, no longer changed | |
+ | |
+ ./typing/unused_var.ml No longer present in Ocaml 4 | |
+ | |
+ | |
+done ./asmcomp/cmmgen.ml Const_csp_value will cause fatal error | |
+ Const_csp_value must not appear in native code | |
+ | |
+./bytecomp/lambda.ml Only adding Const_csp_value | |
+./bytecomp/lambda.mli | |
+./bytecomp/printlambda.ml | |
+./bytecomp/translcore.ml | |
+./bytecomp/symtable.ml | |
+ | |
+./tools/dumpobj.ml (only printing Const_csp_value) | |
diff -Naur ocaml-4.02.1/metalib/Generalization-levels.txt ocaml-ber-n102/metalib/Generalization-levels.txt | |
--- ocaml-4.02.1/metalib/Generalization-levels.txt 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/Generalization-levels.txt 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,309 @@ | |
+Type checking: generalization and regions | |
+ | |
+OCaml implements type generalization in an efficient and interesting | |
+way, which also ensures that types defined within (nested) modules do | |
+not escape their scope. The technique hence enforces the region | |
+discipline for locally introduced type constructors. It is intriguing | |
+how generalization and regions are handled so uniformly. Truly they | |
+are deeply related. | |
+ | |
+Unfortunately, this technique does not seem to be documented at all, | |
+aside from a couple of brief puzzling comments in the OCaml source | |
+code (directory typing/ in the OCaml distribution). The present | |
+document is probably its only explanation. | |
+ | |
+To be certain, the type generalization in OCaml achieves the same | |
+result as the textbook algorithm: a free variable in the type of the | |
+expression 'e' that occurs in 'let x = e in body' is generalized if | |
+the variable does not occur in the type environment and 'e' is | |
+syntactically a value (more precisely, nonexpansive -- that is, has no | |
+visible effect). OCaml achieves this goal in an unexpected way. | |
+ | |
+Every type expression in OCaml -- type variable, type constant, a | |
+constructor expression etc. has a so-called _level_. (See type_expr in | |
+typing/types.mli.) The level is a positive integer, starting with 1 | |
+(see typing/ctype.ml). A very large positive integer -- which should | |
+be treated as the inaccessible ordinal omega -- is dedicated to a | |
+generic_level. [Aside: A rare code comment, in ctype.ml, claims that | |
+levels decrease from generic_level. It is a but misleading. Also, type | |
+levels, which are mutable, are overloaded for type tree traversal. I | |
+don't know how much efficiency it buys but it makes the code confusing | |
+and fragile: one cannot nest traversal functions.] A mutable global | |
+current_level (in ctype.ml) tracks the current level. A new type | |
+expression (created by newty () or newvar ()) receives the | |
+current_level. The current_level is increased by enter_def() and | |
+decreased by end_def(), which should be properly nested. | |
+ | |
+When two types are unified, their levels are updated to be the | |
+smallest of the two. Alas, this crucial property is documented nowhere | |
+and has to be deduced from the code for the unification algorithm, | |
+which takes 1634 lines of code, in ctype.ml. Yes, it is that long. | |
+ | |
+Let's take an example. The (very simplified) code for type-checking | |
+'let x = e in body' is as follows | |
+ | |
+ let e_typed = | |
+ enter_def (); | |
+ let r = type_check env e_source in | |
+ end_def (); | |
+ r | |
+ in | |
+ generalize e_typed.exp_type; | |
+ let new_env = bind env x e_typed.exp_type in | |
+ type_check new_env body_source | |
+ | |
+Here, e_source is the AST (Parsetree) for the expression 'e' and | |
+e_typed is the Typedtree version of the expression (with the attached | |
+inferred type, in the field exp_type). | |
+ | |
+Suppose at the beginning of the code, curr_level was 1 and e_source | |
+was the AST for the identity function "fun x -> x". The statement | |
+enter_def() increases the level by 1. When handling "fun x -> x" the | |
+type checker creates a new type-variable, call it ty_a, for the type | |
+of the argument. That variable receives the current level, which is | |
+now 2. The type-checked expression e_typed will have the type of | |
+Tarrow (ty_a,ty_a). Next comes generalize, defined in ctype.ml. It | |
+traverses the argument type expression and checks the level of every | |
+component. If that level is greater than the current_level and is not | |
+already generic_level, it is set to generic_level. In the code above, | |
+generalize is executed after end_def(), when the current_level is back | |
+to 1. When generalize traverses e_typed and comes across ty_a (whose | |
+level is 2), it sets the level of ty_a to generic_level. Hence ty_a | |
+becomes implicitly universally quantified. Type variables of | |
+generic_level are printed like "'a"; the type variables of other | |
+levels are printed as "'_a". | |
+ | |
+To see that the algorithm is sound, consider the type checking of | |
+ | |
+ fun x -> let f = fun z -> x in ... | |
+ | |
+When handling "fun z -> x", the type-checker will create a type | |
+variable for the argument, call it ty_z, and the type argument for the | |
+result, call it ty_r. Both will have the level 2 (assuming initially | |
+the current_level was 1). The variable ty_r is then unified with the | |
+type of x, which is ty_x. Technically, ty_x is modified to be Tlink | |
+(ty_r). Importantly, the level of ty_r is set to the smallest of ty_r | |
+and ty_x, which is 1, the level of ty_x. The type of f is hence | |
+Tarrow(ty_z,ty_r). When generalize traverses this type, it will | |
+generalize ty_z (since its level was 2, greater than the current 1), | |
+but not ty_r, since its level is 1. The difference is seen here: | |
+ | |
+ # fun x -> let f = fun z -> x in (f true, f false, f true = 1);; | |
+ - : int -> int * int * bool = <fun> | |
+ | |
+ # fun x -> let f = fun z -> x in (f true, f false, f true = 1, f false = false) | |
+ ^^^^^ | |
+ Error: This expression has type bool but an expression was expected of type | |
+ int | |
+ | |
+In summary: if we want to type check an expression and generalize its | |
+type, we should do | |
+ | |
+ let ty = | |
+ enter_def (); | |
+ let r = ... let tv = newvar() in ... (... tv ...) | |
+ end_def (); | |
+ r | |
+ generalize ty | |
+ | |
+If tv wasn't unified with something that existed in the environment | |
+before enter_def(), the variable will be generalized. Levels thus is | |
+a neat technique to avoid traversing the whole environment checking | |
+for free occurrence of a particular type variable. | |
+ | |
+Interestingly, levels have another use, enforcing the region | |
+discipline for local type declarations. Before we get to it, let's | |
+take a moment to appreciate the true complexity of generalization. | |
+The let-generalization in OCaml is far more complex than I have | |
+outlined. First, the let-statement has the general form | |
+ | |
+ let [rec] pattern = exp and pattern = exp ... in body | |
+ | |
+The type checker receives the list of pattern-expression pairs, | |
+and the recursion-flag. The complete code takes 160 lines. Here is the | |
+end of it | |
+ | |
+ begin_def (); | |
+ ... | |
+ let exp_list = | |
+ List.map2 | |
+ (fun (spat, sexp) (pat, slot) -> .... (* type checking of expressions *) | |
+ type_expect exp_env sexp pat.pat_type) | |
+ spat_sexp_list pat_slot_list in | |
+ ... | |
+ end_def(); | |
+ List.iter2 | |
+ (fun pat exp -> | |
+ if not (is_nonexpansive exp) then | |
+ iter_pattern (fun pat -> generalize_expansive env pat.pat_type) pat) | |
+ pat_list exp_list; | |
+ List.iter | |
+ (fun pat -> iter_pattern (fun pat -> generalize pat.pat_type) pat) | |
+ pat_list; | |
+ (List.combine pat_list exp_list, new_env, unpacks) | |
+ | |
+ | |
+We see the general template: | |
+ begin_def(); ... newvar () ... end_def(); generalize. | |
+But there is another traversal of the type, with | |
+generalize_expansive. That function is invoked only if the expression | |
+is expansive, that is, may have a visible effect -- for example, it is | |
+an application. The procedure generalize_expansive (in ctype.ml) | |
+traverses its argument type_expression; when it comes across a | |
+constructed type Tconstr(p,args) (such as list type, etc), its checks | |
+for the variance of the arguments. If an arg is covariant (to be | |
+precise, `weakly covariant'), arg's levels that are greater than the | |
+current level are set to generic_level. Otherwise, arg's levels that | |
+are greater than the current_level are set to the current_level. The | |
+subsequent generalize will leave those levels as they | |
+are. Incidentally, the argument of the type 'a ref is not weakly | |
+covariant. This is how a so-called relaxed value restriction is | |
+implemented, which is responsible for | |
+ | |
+ # let x = (fun y -> print_string "ok"; y) [];; | |
+ ok | |
+ val x : 'a list = [] | |
+ | |
+Here, x is bound to an application, which is not a value, which has | |
+effects, and which is expansive. And yet the type of x is generalized. | |
+SML would not. (BTW, all examples are checked with the MetaOCaml 4.00, | |
+which is a superset of OCaml 4.00). Without the relaxed value | |
+restriction, MetaOCaml will be hardly usable. For example, | |
+ let x = .<1 + .~(...)>. | |
+is an expression, so the classifier cannot be generalized and hence | |
+x cannot be run. | |
+ | |
+Finally we come to regions. ML has modules, which may define (local) | |
+types. The locally introduced types can be used within the scope of | |
+the module and in children modules. But the local type should not escape | |
+the scope of the module. OCaml type-checker thus implements Regions | |
+for type constructors. | |
+ | |
+Here is the example of an escaping type constructor (taken from a rare | |
+comment in OCaml type checker, ctype.ml) | |
+ | |
+ let x = ref [] | |
+ module M = struct type t let _ = (x : t list ref) end | |
+ | |
+The module M defines a local type t. The variable x has the | |
+non-generic type '_a list ref. The type attribution causes x to have | |
+the type (x : t list ref), so the local type constructor t escapes. | |
+ | |
+To see how levels help, consider type checking of local modules | |
+ | |
+ let module M = struct type t let foo = ... end in | |
+ M.foo | |
+ | |
+If 'foo' has the type that mentions M.t, then M.t would've escaped. | |
+Here is the code for the type-checking of local modules (excerpted | |
+from typecore.ml). | |
+ | |
+ | Pexp_letmodule(name, smodl, sbody) -> | |
+ let ty = newvar() in | |
+ (* remember original level *) | |
+ begin_def (); | |
+ Ident.set_current_time ty.level; | |
+ let context = Typetexp.narrow () in | |
+ let modl = !type_module env smodl in | |
+ let (id, new_env) = Env.enter_module name.txt modl.mod_type env in | |
+ Ctype.init_def(Ident.current_time()); | |
+ Typetexp.widen context; | |
+ let body = type_expect new_env sbody ty_expected in | |
+ (* go back to original level *) | |
+ end_def (); | |
+ (* Unification of body.exp_type with the fresh variable ty | |
+ fails if and only if the prefix condition is violated, | |
+ i.e. if generative types rooted at id show up in the | |
+ type body.exp_type. Thus, this unification enforces the | |
+ scoping condition on "let module". *) | |
+ begin try | |
+ Ctype.unify_var new_env ty body.exp_type | |
+ with Unify _ -> | |
+ raise(Error(loc, Scoping_let_module(name.txt, body.exp_type))) | |
+ end; | |
+ re { | |
+ exp_desc = Texp_letmodule(id, name, modl, body); | |
+ exp_loc = loc; exp_extra = []; | |
+ exp_type = ty; | |
+ exp_env = env } | |
+ | |
+To understand what is going on, we should examine | |
+ unify_var env tvar t2 | |
+which unifies the type variable tvar with the type t2. As usual, | |
+unify_var does the occurs check, and then makes tvar to be Tlink | |
+(t2). It also sets the level of t2 to be the min(t2.level, | |
+tvar.level). The function does something else: if t2.level > | |
+tvar.level, more checks are done. When the above condition is true, | |
+the type expression t2 was added at a higher level (or, at an inner | |
+level) compared to tvar. There is a danger than unification will leak | |
+the local type variables. | |
+ | |
+The extra check, in Ctype.update_level, checks if t2 is a | |
+TConstr, object, variant, record or package. If t2 is Tconstr(p,_,_) | |
+where p is the `identifier' of a type constructor, we check if | |
+the binding time of p is less or equal than tvar.level. If not, an | |
+error is reported. We should explain binding time: when an identifier | |
+is seen in a binding position and entered into the environment, the | |
+identifier is timestamped with the current `time', which is an | |
+increasing integer. Remember the line | |
+ Ident.set_current_time ty.level; | |
+in the above code? It sets the time stamp counter to the level of an | |
+external type variable ty plus one (see begin_def()). If the local | |
+module defines a type constructor, its binding time will be greater | |
+than the level of ty. If that type constructor is mentioned in the | |
+type of the body, Ctype.unify_var will encounter it while lowering the | |
+levels to ty.level, and reports the error. Thus the type checker | |
+enforces the invariant that the binding type of every type constructor | |
+is strictly less than its level of a type it forms. The invariant | |
+ensures the region discipline. | |
+ | |
+ | |
+ | |
+Appendix. How to create fresh type variables | |
+ | |
+There are two functions to create fresh type variables: | |
+ | |
+ newvar : ?name:string -> unit -> type_exp | |
+ newgenvar : ?name:string -> unit -> type_exp | |
+ | |
+Both take the optional argument ?name to give the name to the | |
+variable. The name will be chosen automatically otherwise. | |
+ | |
+The function newvar() creates a variable at the current_level whereas | |
+newgenvar() creates at the generic_level. In the code | |
+ | |
+ let ty1 = newvar () in | |
+ unify env ty1 some_type | |
+ | |
+ let ty2 = newgenvar () in | |
+ unify env ty2 some_type | |
+ | |
+both ty1 and ty2 behave similarly: the level of ty1 will be lowered to | |
+that of some_type (if some_type has level less than the current). The | |
+level of ty2 will most definitely will be lowered (unless some_type is | |
+also at generic_level) since generic_level is the highest. | |
+ | |
+The difference emerges in | |
+ | |
+ let ty1 = newvar () in | |
+ let list_type = newgenty (Tconstr(p_list, [ty1])) in | |
+ let texp = instance env list_type in | |
+ unify env texp some_type | |
+ | |
+ let ty2 = newgenvar () in | |
+ let list_type = newgenty (Tconstr(p_list, [ty2])) in | |
+ let texp = instance env list_type in | |
+ unify env texp some_type | |
+ | |
+The function instance copies the type (creates a Tsubst node, to be | |
+precise), if it is generic. That is, in | |
+ let ty = newvar () in instance env ty | |
+instance is the identity function. Nowever, in | |
+ let ty = newgenvar () in instance env ty | |
+instance copies the variable. Therefore, in the first snippet above, | |
+the final unification may affect list_type (instantiating ty1). It | |
+will not in the second snippet since unify will act on the copy of | |
+ty2. | |
+ | |
+ | |
+ | |
diff -Naur ocaml-4.02.1/metalib/gprint/gprint.ml ocaml-ber-n102/metalib/gprint/gprint.ml | |
--- ocaml-4.02.1/metalib/gprint/gprint.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/gprint/gprint.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,139 @@ | |
+(** Generic print | |
+ | |
+ | |
+If this ever gets integrated with MetaOCaml, the simplest implementation | |
+will be to change the AST printing: when is about to print CSP, | |
+check the type... | |
+ | |
+$Id: gprint.ml,v 1.3 2006/04/17 01:46:50 oleg Exp $ | |
+*) | |
+ | |
+ | |
+open Ident (* Just to make sure Ident is loaded first! *) | |
+ | |
+ | |
+(* Given an expression like .<something>., extract the parse tree | |
+ corresponding to something. | |
+ Recall that an object of type code is actually a parsetree | |
+*) | |
+ | |
+let get_elem_parsetree (x : ('a,'b) code) : Parsetree.expression = | |
+ Obj.magic x | |
+;; | |
+ | |
+(* Just a helper, for debugging purposes *) | |
+let type_to_str (x : Typedtree.expression) = | |
+ Printtyp.type_expr Format.str_formatter (x.Typedtree.exp_type); | |
+ Format.flush_str_formatter ();; | |
+ | |
+(* First, we do a consistency check: Env.initial is the same as | |
+ the initial environment that corresponds to the host system | |
+*) | |
+ | |
+(* | |
+let initial_env_ids = | |
+ Predef.build_initial_env | |
+ (fun iden _ lst -> Ident.name iden :: lst) | |
+ (fun iden _ lst -> Ident.name iden :: lst) | |
+ [] | |
+*) | |
+ | |
+ | |
+(* Detailed printing function (unlike Path.name), showing timestamps, etc.*) | |
+let string_of_ident i = | |
+ Ident.print Format.str_formatter i; | |
+ Format.flush_str_formatter () | |
+ | |
+ | |
+let rec string_of_path p = | |
+ let fmt = Format.str_formatter in | |
+ let rec loop = function | |
+ | Path.Pident i -> Ident.print fmt i | |
+ | Path.Pdot (p,s,fl) -> | |
+ loop p; Format.fprintf fmt ".%s(%d)" s fl | |
+ | Path.Papply _ -> Format.fprintf fmt "Papply" | |
+ in loop p; | |
+ Format.flush_str_formatter () | |
+;; | |
+ | |
+(* Get the env associated with the host compiler: true env *) | |
+(* This is somewhat silly; still, it's better to be safe than sorry. | |
+ The inconsistency did cause me a lot of trouble once. | |
+*) | |
+let true_env = | |
+ match get_elem_parsetree .<[None]>. with | |
+ {Parsetree.pexp_ext = Some tr} -> | |
+ let t : Typedtree.expression = Obj.obj tr in | |
+ t.Typedtree.exp_env | |
+ | _ -> assert false | |
+;; | |
+ | |
+ | |
+let () = | |
+ let itrue = (* what the type "int" is in the true env *) | |
+ string_of_path | |
+ (fst (Env.lookup_type (Longident.Lident "int") true_env)) in | |
+ let iint = (* what it is in the Env.initial *) | |
+ string_of_path | |
+ (fst (Env.lookup_type (Longident.Lident "int") Env.initial)) in | |
+ if itrue = iint then () | |
+ else failwith ("Inconsistent Env: " ^ itrue ^ " vs. " ^ iint ^ | |
+ "Ensure that the module Ident is loaded first!") | |
+;; | |
+ | |
+ | |
+(** The printing itself *) | |
+ | |
+(* EvalPath is used only to print exceptions. For now, we force | |
+ Genprintval to make a ``feeble attempt'' | |
+*) | |
+module EvalPath = struct | |
+ type value = Obj.t | |
+ exception Error | |
+ let eval_path p = raise Error | |
+ let same_value v1 v2 = (v1 == v2) | |
+end | |
+ | |
+module Printer = Genprintval.Make(Obj)(EvalPath) | |
+;; | |
+ | |
+let max_printer_depth = ref 100 | |
+let max_printer_steps = ref 300 | |
+ | |
+(* Before attempting to print the value, check to make sure | |
+ that Config.load_path is set. Otherwise, | |
+ the printer could not find .cmi files with the | |
+ needed type declarations. | |
+*) | |
+ | |
+let print_value env obj ty = | |
+ if !Config.load_path = [] then Compile.init_path(); | |
+ Printer.outval_of_value !max_printer_steps !max_printer_depth | |
+ (fun _ _ _ -> None) env obj ty | |
+;; | |
+ | |
+ | |
+(* Analyze the parse tree. If it describes a CSP value, extract the type | |
+ and use print_value. Otherwise, use printAST | |
+*) | |
+ | |
+let print_parsetree fmt = function | |
+ {Parsetree.pexp_desc = | |
+ Parsetree.Pexp_cspval (v, _); | |
+ Parsetree.pexp_ext = Some tr} -> | |
+ let t : Typedtree.expression = Obj.obj tr in | |
+ let () = !Oprint.out_value fmt | |
+ (print_value t.Typedtree.exp_env v t.Typedtree.exp_type) in | |
+ type_to_str t | |
+ | pt -> Print_code.inpc fmt pt; "<manifest>" | |
+;; | |
+ | |
+ | |
+let fprint fmt (x : ('a,'b) code) = | |
+ print_parsetree fmt (get_elem_parsetree x) | |
+;; | |
+ | |
+let print (x : ('a,'b) code) = | |
+ fprint Format.std_formatter x | |
+;; | |
+ | |
diff -Naur ocaml-4.02.1/metalib/gprint/gprint.mli ocaml-ber-n102/metalib/gprint/gprint.mli | |
--- ocaml-4.02.1/metalib/gprint/gprint.mli 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/gprint/gprint.mli 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,14 @@ | |
+(** Generic print | |
+ | |
+ The interface is similar to that in toplevel/printval.mli | |
+ | |
+$Id: gprint.mli,v 1.1 2006/04/15 11:27:37 oleg Exp $ | |
+*) | |
+ | |
+ | |
+(* The output is the type of the printed expression, as a string *) | |
+val fprint : Format.formatter -> ('a,'b) code -> string | |
+val print : ('a,'b) code -> string | |
+ | |
+val max_printer_depth: int ref | |
+val max_printer_steps: int ref | |
diff -Naur ocaml-4.02.1/metalib/gprint/gprint_toplevel.ml ocaml-ber-n102/metalib/gprint/gprint_toplevel.ml | |
--- ocaml-4.02.1/metalib/gprint/gprint_toplevel.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/gprint/gprint_toplevel.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,54 @@ | |
+(** Generic print | |
+ | |
+This file is for using the generic print facility with metaocaml: | |
+unmodified MetaOCaml toplevel. | |
+ | |
+First, correct the paths below (to point to the MetaOCaml installation) | |
+and then #use the file. | |
+ | |
+The file loads a few modules needed by the generic print facility. | |
+ | |
+THE ORDER OF LOADING IS IMPORTANT!!! | |
+ | |
+In particular, "ident.cmo" must be loaded first. It is a stateful module, | |
+whose state is the typestamp for identifiers. The module Predef uses | |
+Ident.create when building identifiers for pre-defined types such as | |
+int. It is imperative that predefined identifiers accessible via | |
+Predef here have the same timestamp as those that existed in the host | |
+code: so Meta-level and the host-level use the same predefined type | |
+identifiers. | |
+ | |
+ $Id: gprint_toplevel.ml,v 1.2 2006/04/15 11:27:37 oleg Exp $ | |
+*) | |
+ | |
+#directory "/home/oleg/Cache/ometa-cvs/parsing";; | |
+#directory "/home/oleg/Cache/ometa-cvs/typing";; | |
+#directory "/home/oleg/Cache/ometa-cvs/toplevel";; | |
+#directory "/home/oleg/Cache/ometa-cvs/utils";; | |
+ | |
+ | |
+(* This must be loaded first! It is stateful, and affects Predef *) | |
+#load "ident.cmo";; | |
+ | |
+#load "misc.cmo";; | |
+#load "path.cmo";; | |
+#load "types.cmo";; | |
+#load "btype.cmo";; | |
+#load "tbl.cmo";; | |
+#load "subst.cmo";; | |
+#load "predef.cmo";; | |
+#load "datarepr.cmo";; | |
+#load "config.cmo";; | |
+#load "consistbl.cmo";; | |
+#load "env.cmo";; | |
+#load "clflags.cmo";; | |
+#load "ctype.cmo";; | |
+#load "printast.cmo";; | |
+#load "oprint.cmo";; | |
+#load "primitive.cmo";; | |
+#load "printtyp.cmo";; | |
+#load "genprintval.cmo";; | |
+ | |
+ | |
+(* Finally, load ou gprint proper *) | |
+#use "gprint.ml";; | |
diff -Naur ocaml-4.02.1/metalib/gprint/Makefile ocaml-ber-n102/metalib/gprint/Makefile | |
--- ocaml-4.02.1/metalib/gprint/Makefile 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/gprint/Makefile 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,151 @@ | |
+# Building and testing the Generic Print facility | |
+# | |
+# make all | |
+# to build the library | |
+# make test_toponly | |
+# test with the top-level. No need to compile anything | |
+# make testb | |
+# test gprint in byte-code programs | |
+# make testo | |
+# test gprint in natively-compiled programs | |
+# | |
+# | |
+# $Id: Makefile,v 1.2 2006/04/17 01:46:50 oleg Exp $ | |
+ | |
+# Location of the OCaml distribution directory | |
+CAMLROOT=../.. | |
+ | |
+include $(CAMLROOT)/config/Makefile | |
+ | |
+RUNTIME=$(CAMLROOT)/boot/ocamlrun | |
+INCLUDES=-I $(CAMLROOT)/utils \ | |
+ -I $(CAMLROOT)/parsing -I $(CAMLROOT)/typing -I $(CAMLROOT)/bytecomp \ | |
+ -I $(CAMLROOT)/asmcomp -I $(CAMLROOT)/driver \ | |
+ -I $(CAMLROOT)/toplevel -I $(CAMLROOT)/boot \ | |
+ -I $(CAMLROOT)/metalib | |
+COMPILER=$(CAMLROOT)/ocamlc | |
+CAMLC=$(RUNTIME) $(COMPILER) $(INCLUDES) | |
+COMPFLAGS=-g -warn-error A -nostdlib | |
+OPTCOMPILER=$(CAMLROOT)/ocamlopt | |
+CAMLOPT=$(RUNTIME) $(OPTCOMPILER) | |
+OPTCOMPFLAGS=-warn-error A -nostdlib | |
+CAMLDEP=$(CAMLROOT)/boot/ocamlrun $(CAMLROOT)/tools/ocamldep | |
+MOCAMLTOP=$(BINDIR)/metaocaml | |
+MOCAMLC=$(BINDIR)/metaocamlc | |
+ | |
+ | |
+.SUFFIXES: .mli .ml .cmi .cmo .cmx | |
+ | |
+.mli.cmi: | |
+ $(CAMLC) $(COMPFLAGS) -c $< | |
+ | |
+.ml.cmi: | |
+ $(CAMLC) $(COMPFLAGS) -c $< | |
+ | |
+.ml.cmo: | |
+ $(CAMLC) $(COMPFLAGS) -c $< | |
+ | |
+.ml.cmx: | |
+ $(CAMLOPT) $(OPTCOMPFLAGS) -c $< | |
+ | |
+ | |
+all: gprint.cma | |
+ | |
+install: | |
+ cp gprint.cmi gprint.cma \ | |
+ $(LIBDIR) | |
+ | |
+ | |
+# MetaOCaml modules to build the library. | |
+# The order is mighty important! In particular, ident.cmo must be first! | |
+LIBLIBMOD= ident.cmo misc.cmo path.cmo types.cmo btype.cmo tbl.cmo \ | |
+ subst.cmo predef.cmo datarepr.cmo config.cmo consistbl.cmo \ | |
+ env.cmo clflags.cmo ctype.cmo printast.cmo oprint.cmo \ | |
+ primitive.cmo printtyp.cmo genprintval.cmo | |
+LIBINCLUDES=-I $(OCAMLROOT)/parsing \ | |
+ -I $(OCAMLROOT)/typing \ | |
+ -I $(OCAMLROOT)/toplevel \ | |
+ -I $(OCAMLROOT)/utils | |
+ | |
+# METANATIVE=ident.cmx utils/misc.cmx utils/tbl.cmx utils/config.cmx \ | |
+# utils/consistbl.cmx \ | |
+# utils/clflags.cmx utils/terminfo.cmx utils/ccomp.cmx utils/warnings.cmx \ | |
+# parsing/linenum.cmx parsing/location.cmx parsing/longident.cmx \ | |
+# parsing/syntaxerr.cmx parsing/parser.cmx \ | |
+# parsing/lexer.cmx parsing/parse.cmx parsing/printast.cmx \ | |
+# typing/path.cmx \ | |
+# typing/primitive.cmx typing/types.cmx \ | |
+# typing/btype.cmx \ | |
+# typing/subst.cmx typing/predef.cmx \ | |
+# typing/datarepr.cmx typing/env.cmx \ | |
+# typing/typedtree.cmx typing/ctype.cmx \ | |
+# typing/oprint.cmx typing/printtyp.cmx typing/includeclass.cmx \ | |
+# typing/mtype.cmx typing/includecore.cmx \ | |
+# typing/includemod.cmx typing/parmatch.cmx \ | |
+# typing/typetexp.cmx typing/stypes.cmx typing/typecore.cmx \ | |
+# typing/typedecl.cmx typing/typeclass.cmx \ | |
+# typing/typemod.cmx \ | |
+# bytecomp/lambda.cmx bytecomp/printlambda.cmx \ | |
+# bytecomp/typeopt.cmx bytecomp/switch.cmx bytecomp/matching.cmx \ | |
+# bytecomp/cprint.cmx bytecomp/f90abs.cmx bytecomp/cabs.cmx bytecomp/dl.cmx \ | |
+# bytecomp/trxtime.cmx bytecomp/trx.cmx \ | |
+# bytecomp/translobj.cmx bytecomp/translcore.cmx \ | |
+# bytecomp/translclass.cmx bytecomp/translmod.cmx \ | |
+# bytecomp/simplif.cmx bytecomp/runtimedef.cmx \ | |
+# asmcomp/arch.cmx asmcomp/cmm.cmx asmcomp/printcmm.cmx \ | |
+# asmcomp/reg.cmx asmcomp/mach.cmx asmcomp/proc.cmx \ | |
+# asmcomp/clambda.cmx asmcomp/compilenv.cmx \ | |
+# asmcomp/closure.cmx asmcomp/cmmgen.cmx \ | |
+# asmcomp/printmach.cmx asmcomp/selectgen.cmx asmcomp/selection.cmx \ | |
+# asmcomp/comballoc.cmx asmcomp/liveness.cmx \ | |
+# asmcomp/spill.cmx asmcomp/split.cmx \ | |
+# asmcomp/interf.cmx asmcomp/coloring.cmx \ | |
+# asmcomp/reloadgen.cmx asmcomp/reload.cmx \ | |
+# asmcomp/printlinear.cmx asmcomp/linearize.cmx \ | |
+# asmcomp/schedgen.cmx asmcomp/scheduling.cmx \ | |
+# asmcomp/emitaux.cmx asmcomp/emit.cmx asmcomp/asmgen.cmx \ | |
+# asmcomp/asmlink.cmx asmcomp/asmlibrarian.cmx asmcomp/asmpackager.cmx \ | |
+# unused_var.cmx \ | |
+# driver/pparse.cmx driver/opterrors.cmx driver/optcompile.cmx \ | |
+# natdyn/natdynlink.cmx\ | |
+# stdlib/hookdynlink.cmx | |
+ | |
+ | |
+gprint.cma: gprint.ml gprint.cmi | |
+ $(OCAMLC) $(LIBINCLUDES) -o $@ -a $(LIBLIBMOD) gprint.ml | |
+ | |
+# gprint.cmxa: gprint.ml gprint.cmi | |
+# $(MOCAMLOPTC) $(LIBINCLUDES) -I $(OCAMLROOT) -o $@ -a $(METANATIVE) \ | |
+# $(OCAMLROOT)/toplevel/genprintval.ml \ | |
+# gprint.ml | |
+ | |
+# .PHONY: test_toponly | |
+# test_toponly: | |
+# (sed "s?/home/oleg/Cache/ometa-cvs?$(OCAMLROOT)?g" gprint_toplevel.ml;\ | |
+# sed "s?open Gprint?open Format?g" vgprint.ml) | \ | |
+# $(OCAMLTOP) | |
+ | |
+# testb: gprint.cma gprint.cmi vgprint.ml | |
+# $(OCAMLC) -o $@ gprint.cma vgprint.ml | |
+# ./$@ | |
+ | |
+vgprint_top: vgprint_aux.cmi | |
+ $(MOCAMLTOP) vgprint.ml | |
+ | |
+vgprint: vgprint_aux.cmi | |
+ $(MOCAMLC) -o $@ vgprint.ml | |
+ ./vgprint | |
+ | |
+clean:: | |
+ rm -f testb vgprint | |
+ | |
+# testo: gprint.cmxa gprint.cmi vgprint.ml | |
+# $(MOCAMLOPTC) -o $@ gprint.cmxa \ | |
+# hookdynlink.cmx -ccopt -Wl,-E -cclib -lnatdyn vgprint.ml | |
+# ./$@ | |
+ | |
+clean:: | |
+ rm -f testo | |
+ | |
+clean:: | |
+ rm -f *.cm[io] *.[oa] *~ | |
diff -Naur ocaml-4.02.1/metalib/gprint/vgprint_aux.ml ocaml-ber-n102/metalib/gprint/vgprint_aux.ml | |
--- ocaml-4.02.1/metalib/gprint/vgprint_aux.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/gprint/vgprint_aux.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,13 @@ | |
+(* | |
+ Declaration of a simple variant type, to be used in the main | |
+ test file vgprint.ml | |
+ | |
+ The goal is to test gprint of values whose types are declared | |
+ in other files. To be precise, we test the proper setting of the | |
+ load_path, needed to locate .cmi files with declarations for | |
+ the types of the values being printed. | |
+ | |
+ This test has been proposed by Tran Minh Quang. | |
+*) | |
+ | |
+type ext = T1 of int | T2 of string option | |
diff -Naur ocaml-4.02.1/metalib/gprint/vgprint.ml ocaml-ber-n102/metalib/gprint/vgprint.ml | |
--- ocaml-4.02.1/metalib/gprint/vgprint.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/gprint/vgprint.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,89 @@ | |
+(** Generic print | |
+ | |
+Test code. | |
+ | |
+$Id: vgprint.ml,v 1.1 2006/04/15 11:27:37 oleg Exp $ | |
+*) | |
+ | |
+open Gprint;; | |
+ | |
+let pr_type et = Format.printf "\n%s@." et | |
+ | |
+let () = pr_type (print .<1>.) | |
+;; | |
+ | |
+ | |
+let () = pr_type (let x = 1 in print .<x>.);; | |
+ | |
+let () = pr_type (print .<10.0>.) | |
+;; | |
+ | |
+let () = pr_type (print .<"xxx">.) | |
+;; | |
+ | |
+let () = pr_type (let x = Some ([|(10,true);(11,false)|]) in print .<x>.);; | |
+ | |
+module C = struct | |
+ type 'a color = Blue | Green | Rgb of 'a | |
+end;; | |
+ | |
+type 'a image = {title : string; pixels : 'a C.color array};; | |
+type big = int image list;; | |
+ | |
+let v = [ | |
+ {title = "im1"; | |
+ pixels = [| C.Blue; C.Rgb 10 |]}; | |
+ {title = "im2"; | |
+ pixels = [| C.Green |]}; | |
+] ;; | |
+ | |
+let () = pr_type (print .<v>.) | |
+;; | |
+ | |
+(* current drawback due to the lack of integration, and the work-around*) | |
+let foo (x : int option) = print .<x>. in pr_type (foo None );; | |
+ | |
+let foo (x : int option) = print (let z = [x] in .<z>.) in pr_type (foo None);; | |
+ | |
+ | |
+(* Now we open C *) | |
+open C | |
+ | |
+let some_processing ims = | |
+ let brighten px = | |
+ let new_px = match px with | |
+ Blue -> Green | |
+ | Green -> Rgb 10 | |
+ | Rgb x -> Rgb (x+1) in | |
+ let () = Format.printf "@.pixel: %a -> %a @." | |
+ (fun ppf v -> ignore (fprint ppf v)) | |
+ (let x = [px] in .<x>.) | |
+ (fun ppf v -> ignore (fprint ppf v)) | |
+ (let x = [new_px] in .<x>.) in | |
+ new_px in | |
+ let process im = | |
+ let () = Format.printf "Processing: " in | |
+ let _ = print .<im>. in | |
+ {im with pixels = Array.map brighten im.pixels} in | |
+ let res = List.map process ims in | |
+ let _ = print .<res>. in | |
+ Format.printf "@." | |
+;; | |
+ | |
+let () = some_processing v;; | |
+ | |
+ | |
+(* This test gprint-ing of values whose type has been declared | |
+ in other files. In other words, we test locating .cmi files | |
+ at the type of gprint. | |
+ | |
+ This test has been proposed by Tran Minh Quang. | |
+*) | |
+ | |
+let extv = Vgprint_aux.T2 (Some "extv");; | |
+ | |
+let () = pr_type (print .<extv>.) | |
+;; | |
+ | |
+print_endline "\nAll done" | |
+;; | |
diff -Naur ocaml-4.02.1/metalib/INSTALL ocaml-ber-n102/metalib/INSTALL | |
--- ocaml-4.02.1/metalib/INSTALL 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/INSTALL 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,68 @@ | |
+INSTALLATION | |
+ | |
+You need the source OCaml 4.02.1 distribution, which you can download from | |
+ http://caml.inria.fr/download.en.html | |
+See also | |
+ http://ocaml.org/install.html | |
+ | |
+1. Download, un-tar the OCaml distribution and configure it as described | |
+ in its INSTALL file | |
+ | |
+ For example, in the OCaml distribution directory, do | |
+ | |
+ ./configure -prefix `pwd` -no-pthread -no-graph | |
+ | |
+ You may chose any other installation prefix. It is best however | |
+ to chose the prefix different from that of the standard OCaml installation, | |
+ so to keep the existing OCaml installation intact. | |
+ | |
+1a. Update the Makefile in the ber-metaocaml directory | |
+ | |
+ change CAMLROOT= at the beginning of the ber-metaocaml/Makefile | |
+ to point to the OCaml distribution directory | |
+ | |
+2. Patch the OCaml distribution | |
+ | |
+ In the ber-metaocaml directory, do | |
+ | |
+ make patch | |
+ | |
+ | |
+3. Compile and install OCaml as usual. For example, in the OCaml | |
+ distribution directory, do | |
+ | |
+ make world | |
+ make -i install | |
+ | |
+ | |
+4. Build the BER MetaOCaml library, the top-level, and the | |
+ compilation script | |
+ | |
+ In the ber-metaocaml directory, do | |
+ | |
+ make all | |
+ make install | |
+ | |
+ The BER MetaOCaml top-level can be started afterwards as | |
+ $prefix/bin/metaocaml | |
+ | |
+ The batch compiler can be run using the command file metaocamlc. | |
+ | |
+5. Optionally run sample tests | |
+ | |
+ In the ber-metaocaml directory, do | |
+ | |
+ make test | |
+ make test-compile | |
+ | |
+ | |
+Installation of BER MetaOCaml on Windows has not been tested. Original | |
+MetaOCaml contained the following note about the Windows platform: | |
+ | |
+ To use MetaOCaml under MS Windows, please do not use "Windows | |
+ OCaml". Rather, use the standard OCaml system under cygwin. If you | |
+ happened to install the "Windows OCaml" in the past, manually remove | |
+ some environment variables before you can install the standard system | |
+ successfully. To do that, goto START -> Control Panel -> System -> | |
+ Advanced -> Environment variables, and edit the OCAMLLIB variable and | |
+ remove any entries in that entry. | |
diff -Naur ocaml-4.02.1/metalib/Makefile ocaml-ber-n102/metalib/Makefile | |
--- ocaml-4.02.1/metalib/Makefile 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/Makefile 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,142 @@ | |
+# Build the BER MetaOCaml library, top-level, and the compiler driver | |
+# See INSTALL for more detail | |
+ | |
+# Location of the OCaml distribution directory | |
+CAMLROOT=.. | |
+ | |
+include $(CAMLROOT)/config/Makefile | |
+ | |
+RUNTIME=$(CAMLROOT)/boot/ocamlrun | |
+INCLUDES=-I $(CAMLROOT)/utils \ | |
+ -I $(CAMLROOT)/parsing -I $(CAMLROOT)/typing -I $(CAMLROOT)/bytecomp \ | |
+ -I $(CAMLROOT)/asmcomp -I $(CAMLROOT)/driver \ | |
+ -I $(CAMLROOT)/toplevel -I $(CAMLROOT)/stdlib | |
+COMPILER=$(CAMLROOT)/ocamlc | |
+CAMLC=$(RUNTIME) $(COMPILER) $(INCLUDES) | |
+COMPFLAGS=-strict-sequence -w +33..39 -warn-error A -nostdlib | |
+OPTCOMPILER=$(CAMLROOT)/ocamlopt | |
+CAMLOPT=$(RUNTIME) $(OPTCOMPILER) | |
+OPTCOMPFLAGS=-strict-sequence -w +33..39 -warn-error A -nostdlib | |
+CAMLDEP=$(CAMLROOT)/boot/ocamlrun $(CAMLROOT)/tools/ocamldep | |
+ | |
+METALIB=metalib.cma | |
+METALIBOBJS=runcode.cmo print_code.cmo | |
+METAOBJS=$(METALIB) bertop.cmo berstart.cmo | |
+ | |
+all: $(METAOBJS) metaocaml metaocamlc | |
+ | |
+.SUFFIXES: .mli .ml .cmi .cmo .cmx | |
+ | |
+.mli.cmi: | |
+ $(CAMLC) $(COMPFLAGS) -c $< | |
+ | |
+.ml.cmo: | |
+ $(CAMLC) $(COMPFLAGS) -c $< | |
+ | |
+.ml.cmx: | |
+ $(CAMLOPT) $(OPTCOMPFLAGS) -c $< | |
+ | |
+$(METALIB): $(METALIBOBJS) | |
+ $(CAMLC) $(COMPFLAGS) -o $@ -a $(METALIBOBJS) | |
+ | |
+ | |
+install: metaocaml metaocamlmktop metaocamlc | |
+ cp runcode.cmi print_code.cmi metalib.cma bertop.cmo berstart.cmo \ | |
+ $(LIBDIR) | |
+ cp metaocaml $(BINDIR)/metaocaml$(EXE) | |
+ cp metaocamlmktop $(BINDIR)/metaocamlmktop$(EXE) | |
+ cp metaocamlc $(BINDIR)/metaocamlc$(EXE) | |
+ | |
+metaocaml: $(METAOBJS) $(CAMLROOT)/compilerlibs/ocamlcommon.cma | |
+# $(CAMLROOT)/tools/ocamlmktop \ | |
+# -o $@ $(METALIB) bertop.cmo berstart.cmo | |
+ $(CAMLC) -nostdlib -linkall \ | |
+ $(CAMLROOT)/compilerlibs/ocamlcommon.cma \ | |
+ $(CAMLROOT)/compilerlibs/ocamlbytecomp.cma \ | |
+ $(CAMLROOT)/compilerlibs/ocamltoplevel.cma \ | |
+ -o $@ $(METALIB) bertop.cmo berstart.cmo \ | |
+ $(CAMLROOT)/toplevel/topstart.cmo | |
+ | |
+ | |
+ | |
+clean:: | |
+ rm -f metaocaml | |
+ | |
+metaocamlmktop: metaocamlmktop.tpl metaocaml | |
+ sed -e 's|%%BINDIR%%|$(BINDIR)|' metaocamlmktop.tpl > metaocamlmktop | |
+ chmod +x metaocamlmktop | |
+ | |
+clean:: | |
+ rm -f metaocamlmktop | |
+ | |
+metaocamlc: metaocamlc.c | |
+ $(CC) -Wall -DBINDIR=\"$(BINDIR)\" -o metaocamlc metaocamlc.c | |
+ | |
+clean:: | |
+ rm -f metaocamlc | |
+ | |
+clean:: | |
+ rm -f *.cm* *.o *.a | |
+ rm -f test/*.cm* test/*.o | |
+ rm -f *~ | |
+# cd gprint && $(MAKE) CAMLROOT=../$(CAMLROOT) clean | |
+ | |
+test: | |
+ TERM=dumb $(BINDIR)/metaocaml -w -8 < test/trivial.ml > trivial.out && \ | |
+ diff -u trivial.out trivial.ref | |
+ TERM=dumb $(BINDIR)/metaocaml -w -8 < test/simple.ml > simple.out && \ | |
+ diff -u simple.out simple.ref | |
+ $(BINDIR)/metaocaml -w -8 test/simple_true.ml | |
+ $(BINDIR)/metaocaml -w -8 test/quick_test.ml | |
+ $(BINDIR)/metaocaml -w -8 test/test21.ml | |
+ $(BINDIR)/metaocaml -w -8 test/pythagorian_triples.ml | |
+ $(BINDIR)/metaocaml -w -8 test/test_levels.ml | |
+# $(BINDIR)/metaocaml test/t4.ml | |
+# cd gprint && $(MAKE) CAMLROOT=../$(CAMLROOT) MOCAMLTOP="../metaocaml -I .." testv_top | |
+# cd gprint && $(MAKE) CAMLROOT=../$(CAMLROOT) vgprint_top | |
+ | |
+test-compile: | |
+ $(BINDIR)/metaocamlc -w -8 -o simple_true test/simple_true.ml | |
+ ./simple_true | |
+# $(BINDIR)/metaocamlc -o quick_test test/quick_test.ml | |
+# ./quick_test | |
+ $(BINDIR)/metaocamlc -dparsetree -drawlambda -dinstr -c test/test21.ml | |
+ $(BINDIR)/metaocamlc -c test/t4types.mli && \ | |
+ cp test/t4types.cmi . # .cmi file should be where the executable is | |
+ $(BINDIR)/metaocamlc -w -8 -o t4 -I test/ test/t4.ml | |
+ ./t4 | |
+ $(BINDIR)/metaocamlc -o pt test/pythagorian_triples.ml | |
+ ./pt | |
+# cd gprint && $(MAKE) CAMLROOT=../$(CAMLROOT) vgprint | |
+# Testing setting of the .cmo path | |
+ $(BINDIR)/metaocamlc test/test_path_a.ml | |
+ mv test/test_path_a.cm[io] /tmp | |
+ $(BINDIR)/metaocamlc -I /tmp -o tp test/test_path.ml | |
+ ./tp | |
+ | |
+clean:: | |
+ rm -f simple_true quick_test t4 pt trivial.out simple.out a.out tp | |
+ | |
+# Patch the OCaml distribution | |
+patch: | |
+ cp -p patches/trx.ml $(CAMLROOT)/typing/ | |
+ cp -p patches/trx.mli $(CAMLROOT)/typing/ | |
+# patch -p0 -d $(CAMLROOT) < patches/patch | |
+ patch -p1 -d $(CAMLROOT) < patches/patch | |
+ | |
+# Create the patch set | |
+makepatch: | |
+ cp -p $(CAMLROOT)/typing/trx.ml patches/ | |
+ cp -p $(CAMLROOT)/typing/trx.mli patches/ | |
+ sh ./build_patch.sh > patches/patch | |
+ | |
+# metaocamlopt: stdlib/metanative.cmxa tools/metaocamlopt.tpl | |
+# sed -e 's|%%BINDIR%%|$(BINDIR)|' tools/metaocamlopt.tpl > metaocamlopt | |
+# chmod +x metaocamlopt | |
+ | |
+include .depend | |
+ | |
+depend: | |
+ $(CAMLDEP) *.mli *.ml > .depend | |
+ | |
+.PHONY: all test clean patch makepatch depend test-compile install | |
diff -Naur ocaml-4.02.1/metalib/make_metalib.sh ocaml-ber-n102/metalib/make_metalib.sh | |
--- ocaml-4.02.1/metalib/make_metalib.sh 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/make_metalib.sh 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,39 @@ | |
+# Make the distribution directory | |
+ | |
+DEST=/tmp/ber-metaocaml-102 | |
+ | |
+mkdir $DEST | |
+ | |
+cp -p \ | |
+.depend \ | |
+ChangeLog \ | |
+Files.txt \ | |
+INSTALL \ | |
+Makefile \ | |
+NOTES.txt \ | |
+ORIGINAL-LICENSE-META \ | |
+Problems.txt \ | |
+README \ | |
+berstart.ml \ | |
+bertop.ml \ | |
+build_patch.sh \ | |
+metaocamlmktop.tpl \ | |
+metaocamlc.c \ | |
+print_code.ml \ | |
+print_code.mli \ | |
+runcode.ml \ | |
+runcode.mli \ | |
+simple.ref \ | |
+trivial.ref \ | |
+$DEST/ | |
+ | |
+mkdir $DEST/patches | |
+cp -p patches/* $DEST/patches | |
+ | |
+mkdir $DEST/test | |
+cp -p test/*.ml test/*.mli $DEST/test | |
+ | |
+# mkdir $DEST/gprint | |
+# cp -p gprint/Makefile $DEST/gprint | |
+# cp -p gprint/*.ml $DEST/gprint | |
+# cp -p gprint/*.mli $DEST/gprint | |
diff -Naur ocaml-4.02.1/metalib/metaocamlc.c ocaml-ber-n102/metalib/metaocamlc.c | |
--- ocaml-4.02.1/metalib/metaocamlc.c 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/metaocamlc.c 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,75 @@ | |
+/* A small glue file to compile MetaOCaml code | |
+ | |
+ All the compilation is done by the patched ocamlc, and this program | |
+ merely executes it. | |
+ | |
+ The purpose of this program is to set program options | |
+ to add compiler-libs/ and the compiler libraries -- but only when | |
+ we link the final executable. Otherwise, | |
+ the final executable ends up with two copies of the compiler libraries, | |
+ and this is the disaster, leading to sigfaults (because the the compiler | |
+ librtaries, esp. env, are stateful). | |
+ | |
+ This program expects the preprocessor flag -DBINDIR for the path to ocamlc. | |
+*/ | |
+ | |
+ | |
+#if !defined(BINDIR) | |
+#error "Define BINDIR, as -DBINDIR=\"path-to-ocaml-bin-directory\"" | |
+#endif | |
+ | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <assert.h> | |
+#include <unistd.h> | |
+#include <stdio.h> | |
+ | |
+static char path_ocamlc [] = BINDIR "/ocamlc"; | |
+ | |
+/* New flags that we prepend to the command line */ | |
+static char *const metaocaml_extra_flags [] = { | |
+ "-I", "+compiler-libs", | |
+ "ocamlcommon.cma", | |
+ "ocamlbytecomp.cma", | |
+ "ocamltoplevel.cma", | |
+ "metalib.cma", | |
+ "berstart.cmo" | |
+ }; | |
+ | |
+/* Flags that prevent building the executable */ | |
+static char *const nonlinking_flags [] = { | |
+ "-c", "-pack", "-a", "-output-obj" | |
+ }; | |
+ | |
+/* Check to see that we are invoked to build the executable */ | |
+static int is_linking(char * const argv[]) | |
+{ | |
+ int i,j; | |
+ for(i=1; argv[i]; i++) /* argv is NULL-terminated */ | |
+ for(j=0; j<sizeof(nonlinking_flags)/sizeof(nonlinking_flags[0]); j++) | |
+ if(strcmp(argv[i], nonlinking_flags[j]) == 0) | |
+ return 0; | |
+ return 1; | |
+} | |
+ | |
+int main(int argc, char *const argv[],char *const envp[]) | |
+{ | |
+ char *const * new_argv = argv; | |
+ | |
+ if( is_linking(argv) ) { | |
+ char **p; | |
+ const int argv_len = argc+1; /* counting the NULL at the end of argv */ | |
+ const int extra_flags_len = sizeof(metaocaml_extra_flags)/ | |
+ sizeof(metaocaml_extra_flags[0]); | |
+ assert(new_argv=calloc(argv_len+extra_flags_len,sizeof(argv[0]))); | |
+ p = (char **)new_argv; | |
+ *p++ = argv[0]; | |
+ memcpy(p,metaocaml_extra_flags,extra_flags_len*sizeof(argv[0])); | |
+ p += extra_flags_len; | |
+ memcpy(p,&argv[1],(argv_len-1)*sizeof(argv[0])); | |
+ } | |
+ | |
+ execve(path_ocamlc,new_argv,envp); /* Does not normally return */ | |
+ perror("execve failed, perhaps wrong path to ocamlc"); | |
+ exit(2); | |
+} | |
diff -Naur ocaml-4.02.1/metalib/metaocamlmktop.tpl ocaml-ber-n102/metalib/metaocamlmktop.tpl | |
--- ocaml-4.02.1/metalib/metaocamlmktop.tpl 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/metaocamlmktop.tpl 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,16 @@ | |
+#!/bin/sh | |
+######################################################################### | |
+# # | |
+# OCaml # | |
+# # | |
+# Damien Doligez, projet Para, INRIA Rocquencourt # | |
+# # | |
+# Copyright 1999 Institut National de Recherche en Informatique et # | |
+# en Automatique. All rights reserved. This file is distributed # | |
+# under the terms of the Q Public License version 1.0. # | |
+# # | |
+######################################################################### | |
+ | |
+# $Id$ | |
+ | |
+exec %%BINDIR%%/ocamlc -I +compiler-libs -linkall ocamlcommon.cma ocamlbytecomp.cma ocamltoplevel.cma metalib.cma bertop.cmo berstart.cmo "$@" topstart.cmo | |
diff -Naur ocaml-4.02.1/metalib/metatop.mli ocaml-ber-n102/metalib/metatop.mli | |
--- ocaml-4.02.1/metalib/metatop.mli 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/metatop.mli 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,3 @@ | |
+(* `Plugin' for the OCaml top-level *) | |
+ | |
+ | |
diff -Naur ocaml-4.02.1/metalib/NOTES.txt ocaml-ber-n102/metalib/NOTES.txt | |
--- ocaml-4.02.1/metalib/NOTES.txt 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/NOTES.txt 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,870 @@ | |
+ BER MetaOCaml | |
+ | |
+ | |
+* Main differences from the original MetaOCaml | |
+ | |
+ | |
+** Substantial changes, user-visible | |
+ -- New function Runcode.typecheck_code for the benefit of offshoring | |
+ | |
+ -- New function Runcode.add_search_path to add a directory to | |
+ the search path for .cmi/.cmo files -- report by Nicolas Ojeda Bar | |
+ | |
+ -- better printing of CSP; CSP become serializable in simpler cases | |
+ (see a special section on CSP below) | |
+ | |
+ [N101] | |
+ -- Constructor restriction: all data constructors and record labels | |
+ used within brackets must come from the types that are declared in | |
+ separately compiled modules. | |
+ | |
+ -- Scope extrusion check: attempting to build code values with | |
+ unbound or mistakenly bound variables (which is possible with | |
+ mutation or other effects) is caught early, raising an exception | |
+ with good diagnostics. | |
+ | |
+ -- The scope extrusion check made it possible to remove environment | |
+ classifiers while still preserving the static guarantee: if the generator | |
+ finishes successfully, the generated code is well-typed and | |
+ well-scoped. Environment classifiers ensured well-scopedness when | |
+ type-checking the generator -- but only for pure generators. The | |
+ scope extrusion check is executed when the generator is run; however | |
+ the check is now comprehensive. Scope extrusion is always caught, and | |
+ caught early, whether the generator is effectful or not. | |
+ | |
+ -- Syntax .! is replaced with the ordinary prefix operation | |
+ (!.) (alias run) defined in the module Runcode. Runcode.run is an | |
+ ordinary function, and is not part of the MetaOCaml kernel. | |
+ | |
+ | |
+** Substantial changes, internal | |
+ | |
+-- Use attributes for bracket and escape. | |
+ Why not extension nodes: the type checker ignores extension nodes | |
+ (or reports them as errors) at many places (for example, when | |
+ traversing the AST to find free variables; when checking for | |
+ non-expansiveness, etc). But nodes with attributes are treated | |
+ normally, which is exactly what we want. | |
+ | |
+So, .< exp >. is represented as | |
+ exp [@metaocaml.bracket] | |
+or | |
+ begin[@metaocaml.bracket] exp end | |
+Likewise, .~exp is represented as | |
+ exp [@metaocaml.escape] | |
+ | |
+Keep in mind that an expression can have several bracket and escape | |
+attributes, for example: .<.<1>.>. | |
+The order matters: .<.< .~ x >.>. vs .< .~.<x>. >. | |
+ | |
+-- Now metaocamlc a simple .c file that invokes ocamlc | |
+after setting adding up compiler libraries and berstart.cmo -- | |
+but only if we link the executable. If metaocamlc is invoked to | |
+compile only or to build a library, don't add compiler libraries and | |
+berstart. Otherwise, when we get to make the final executable, we end | |
+up with duplicate compiler libraries, and this is the disaster | |
+(segfault). | |
+ | |
+-- We now avoid the extra translation pass. The type-checker | |
+should invoke the trx_bracket after type-checking the bracket at level | |
+0. Therefore, if there are no staging constructs, the typedtree is | |
+not traversed at all after type checking. Typemod.ml no longer has to | |
+be modified. | |
+However, we pay the price: the check on non-epxansiveness is done | |
+on the type-checked expression. So, we have to arrange so that we | |
+check if an expression is non-expansive before the translation. | |
+We carry this information in the dedicated attribute. | |
+ | |
+-- Instead of the Level environment, carry the level as the attribute | |
+to the value, in value attributes. | |
+ | |
+-- Taking advantage of the extensible error-reporting mechanism | |
+provided by Location.register_error_of_exn. | |
+ | |
+[N101] | |
+ -- In OCaml 4.00.1 , the Core type checker was changed to use | |
+ type_expect. This generates better error messages. Therefore, the type | |
+ checking of brackets, escape and CSP was re-written accordingly, | |
+ to propagate expectations down. For example, <e> is expected to have | |
+ the type of ty code where e has is expected to have the | |
+ type ty. | |
+ | |
+ -- The main MetaOCaml kernel module, trx.ml, has been completely | |
+ re-written. Many algorithms were changed. For example, the traversal | |
+ of the Typedtree looking for brackets to replace is completely | |
+ re-written. Now we maintain sharing as much as possible. If the tree | |
+ has no brackets, it is returned as it was. Previously, it was copied. | |
+ | |
+ -- Faster generation of code (especially for functions and nonbinding | |
+ functions) | |
+ | |
+ -- Added a test for the well-formedness of recursive let (see below) | |
+ | |
+ -- Applications with labeled arguments are supported now. | |
+ | |
+ -- The problem with first-class polymorphism (records with polymorphic | |
+ fields inside brackets) has fixed itself. See the file | |
+ tests/simple.txt. | |
+ | |
+ | |
+ -- Precise type assignment when building Typedtree nodes (when | |
+ hand-compiling Parsetree generation) | |
+ | |
+ -- Processing of CSP has changed, split into two phases. At | |
+ compilation time, we know the type of the CSP and decide if values | |
+ of that type can be lifted. If not, we look at the CSP identifier | |
+ to decide if the CSP can be referred to by qualified name (that is, | |
+ the CSP identifier is part of a separately compiled module). | |
+ If all fails, we generate the call to a quotator that would build | |
+ the CSP at run-time, when the value to quote becomes known. Overall, | |
+ the procedure improves the printing of CSP. | |
+ | |
+ | |
+** Separation into `kernel' and `user-level' code | |
+ | |
+The kernel modifies (and is part of) the OCaml system. The user-level | |
+code is in a directory metalib outside the OCaml distribution. The | |
+user-level code can be changed and expanded without the need to hack | |
+and re-compile (Meta)OCaml. Changes introduced to future versions of | |
+OCaml will not generally affect the `user-level' code. | |
+ | |
+ -- Printing of code values -- AST pretty_printing by Ed Pizzi -- | |
+ is moved to user-level, a dedicated file metalib/print_code.ml. | |
+ The bulk of pretty-printing is now done by OCaml itself, module | |
+ Pprintast. | |
+ | |
+ -- New API for running code, encouraging the development of new ways | |
+ to execute code values (see metalib/runcode.mli). | |
+ The operation 'run' is a regular function now, and is user-level | |
+ rather than being part of the kernel. | |
+ | |
+ -- Now BER MetaOCaml is built as a custom top-level, using the standard tool | |
+ ocamlmktop. We register code printers as regular top-level printers | |
+ of user-defined data types. Therefore, topmain is no longer modified. | |
+ | |
+ -- Tag elimination is fully removed. | |
+ | |
+ -- trxtime is moved to metalib. | |
+ | |
+ -- Offshoring is temporarily removed; it should be re-introduced as | |
+ a module in metalib, a user-level code. | |
+ | |
+** Better handling of CSP in N102 | |
+ | |
+There is no longer any specific node for CSP. We do not need them: | |
+old CSP nodes are represented as an application (Obj.obj <csp-val>). | |
+Since the CSP nodes are assumed type-checked, we get around typing -- | |
+as we did before with the PExp_csp node. Obj.obj serves exactly the | |
+same purpose. | |
+A better design: if CSP is represented as an integer, we compile | |
+CSP v as if it were .<Obj.obj .~(Obj.repr v)>.. | |
+If Obj.repr is block (a heap value), presently we compile it as | |
+the same. However, in the future we should do | |
+.<Marshall.from_string .~(Marshall.to_string v)>. | |
+(perhaps with the flags to preserve sharing and permit marshaling of | |
+functional values). We attach the attribute carrying the name of the | |
+CSP variable, for the sake of pretty-printing functions | |
+(They may want to print that name in the comments). Finally we get the | |
+output format that is compilable even in the presence of CSP. | |
+To estimate when to switch to the new format, the dyn_quoter now | |
+checks the CSP to and prints a warning if the CSP is a closure or | |
+if it is ``too deep''. | |
+ | |
+Better printing of CSP; now in simple cases CSP are serializable | |
+and hence present no problems. | |
+let l x = .<x>.;; (* polymorphic *) | |
+l 1;; | |
+ - : int code = .<(* CSP x *) Obj.magic 1>. | |
+l 1.0;; (* better printing in N100 *) | |
+ - : float code = .<1.>. | |
+l [];; (* serializable code in N102 *) | |
+ - : 'a list code = .<(* CSP x *) Obj.magic 0>. | |
+ | |
+In any case, CSPs that are used by the MetaOCaml itself (for the | |
+Parsetree fragments) are already in the new format. Therefore, | |
+metaocaml (the compiler itself) can be natively compiled. | |
+Observation: most of the serialized values are of the size 50. It | |
+seems the minimal size is about 42, the size of the header for the | |
+serialized value. When dealing with functions, case matching, some | |
+serialized values (patterns, pattern lists) could be about 70 or even | |
+150. It seems I should write my own serializer, that avoids the | |
+overhead of Marshall. I should use it when the value is not too deep, | |
+or internally for MetaOCaml CSP. | |
+ | |
+** Engineering changes | |
+ | |
+ -- All modified lines in the OCaml code are marked by (* NNN *) | |
+ | |
+ -- There are comments now -- and lots of them | |
+ | |
+ -- Added very many regression tests (which also work as examples) to test | |
+ every feature: see test/trivial.ml and test/simple.ml | |
+ | |
+ -- The new BER N101 is not only source-compatible with OCaml 4.01 -- | |
+ it is also binary compatible. Any 4.01-built OCaml library and plugin | |
+ (including findlib) can be used with BER N101 in their binary form. | |
+ The building of BER N101 no longer involves bootstrapping | |
+ and is hence much faster. | |
+ | |
+ | |
+ | |
+* What part of MetaOCaml can be moved to the OCaml proper | |
+Suggestions for the OCaml HQ | |
+ | |
+ -- make all path_idents in predef.ml to be persistent. Currently, | |
+ to check if type int is in Pervasives, I have to search the initial | |
+ environment. | |
+ | |
+ -- There are two restrictions on let rec: patterns must be simple | |
+ variable patterns (either just 'var' or '_ as var') and the variable | |
+ bound by let rec must appear under lambda, lazy or constructor. | |
+ These conditions are checked very late (in bytecomp/transcore.ml), | |
+ after type checking. But these are syntactic conditions; in | |
+ particular, the first condition can be checked during the parsing. | |
+ Minimal suggestion: check the first condition at parsing and | |
+ the second at type checking. So, a code that type checks should | |
+ produce no further compilation errors. Extended suggestion: since | |
+ let rec patterns are so restricted (and since the processing of patterns | |
+ becomes more involved with the addition of GADTs), separate | |
+ Pexp_let/Texp_let into Pexp_let and Pexp_letrec (ditto for Texp). | |
+ Type checking will become more orthogonal since in type checking Pexp_letrec | |
+ we don't need to care about GADTs, the exhaustiveness check and | |
+ the type propagation from expression to patterns. | |
+[reported on Mantis, Dec 29, 2014] | |
+ | |
+ -- Unify printing functions for various trees and make them | |
+ prettier. The very same fmt_longident_aux occurs three times in | |
+ the OCaml code base (parsing/printast.ml, tools/pprintast.ml, | |
+ typing/printtyped.ml) | |
+ | |
+ | |
+ -- On one hand, it is tempting to eliminate Const_csp_value. There | |
+ will no longer be a need to modify the code generator (in bytecomp/). | |
+ On the other hand, Const_csp_value does make sense as a structured | |
+ constant. Consider a constant that refers to a big array, or even | |
+ array in ancient. Representing such constant in a Parsetree as a | |
+ string is ungainly. Syntactic extensions in particular could use | |
+ Const_csp_value. | |
+ Feature request: new constant type, structure constant (like block | |
+ constant in IR lambda). It must be used with a type coercion. | |
+ The code generator makes a cursory check that the structure agrees | |
+ with the defined type. To be precise, we want | |
+ Const_structural Obj.t (* or structural const from Lambda *) | |
+ * core_type | |
+ | |
+ | |
+* Further plans | |
+ | |
+** Optimize the construction of code values (Parsetree) | |
+ | |
+When the bracket expression is run, it produces the Parsetree that | |
+represents the code. The tree is constructed at run-time. In some | |
+cases the Parsetree can be constructed at compile time, in | |
+trx.ml. A constant like <1> is such a case, when we can immediately | |
+construct the Parsetree: Pexp_constant (Constant_int 1). After we | |
+construct the Parsetree at compile time, we use CSP to pass it over to | |
+run-time. When run, the program will use the compiled constant. This | |
+mechanism of building Parsetree at compile-time whenever possible is | |
+one of the large differences from the previous versions of MetaOCaml. | |
+ | |
+This approach can be extended, by constructing large Parsetree | |
+subtrees at compile time, and passing them as CSP. See ``TODO: an | |
+optimization idea. Consider <assert e> as a typical expression.'' in | |
+trx.ml | |
+If we can build functions at translation time, we don't even | |
+need to rename bound variables (let alone do the scope-extrusion | |
+check). If a code value has no escapes, like .<fun x -> x>., | |
+then no scope extrusion is possible. | |
+ | |
+** Improve processing CSP | |
+ | |
+When lifting int, bool, etc. values, we generate calls to run-time | |
+functions like lift_constant_int to do the Parsetree generation. In | |
+the future we should `inline' those functions -- that is, obtain the | |
+Typedtree for them and use the tree for building Texp_apply. | |
+ | |
+Lists, arrays, option types of liftable types are themselves | |
+liftable. We can lift many more types. For arrays, check their length. | |
+If the array is short, it should be lifted. For long arrays, building | |
+a CSP is better (although it make take a bit longer since we will have | |
+to invoke dyn_quote at run-time). | |
+ | |
+It seems I should write my own serializer, that avoids the | |
+overhead of Marshall. I should use it when the value is not too deep, | |
+or internally for MetaOCaml CSP. | |
+ | |
+** Minimize changes to OCaml | |
+ | |
+Strictly speaking, we don't need to change typedtree or parsetree. We | |
+can just add functions with distinguished names. And add printers and | |
+add to the type checker. It helps if OCaml added one node to parsetree | |
+Pexp_extension and a similar node to typed tree. The user may register | |
+extensions, invoked by the type checker, printer, etc. to type check | |
+the node. | |
+ | |
+ | |
+** Search for TODO in trx.ml | |
+ | |
+** Generating let rec with statically unknown number of bindings | |
+(suggestion by Jun Inoue) | |
+Use case: specialization of recursive functions, e.g., specializing | |
+KMP. | |
+ | |
+proposed interface | |
+module MakeLetRecs : sig | |
+ type letrec_id (* abstract *) | |
+ val make_letrec : (letrec_id -> ('cl,'w) code) -> ('cl,'w) code | |
+ | |
+ val add_fun_binding : letrec_id -> | |
+ (('cl,'a->'b) code -> ('cl,'a->'b) code) -> | |
+ (('cl,'a->'b) code -> ('cl,'w) code) -> ('cl,'w) code | |
+end | |
+ | |
+Perhaps there should also be | |
+ add_lazy_binding : letrec_id -> ('a lazy code -> 'b lazy code) -> ... | |
+ (* classifiers omitted *) | |
+i.e. one that generates bindings of the form | |
+ a = lazy (foo a) | |
+ | |
+Jun Inoue wrote: | |
+I wonder if it wouldn't be too limiting to restrict the interface | |
+slightly further, so as to ensure that we never get a "this kind of | |
+expression is not allowed on the right-hand side of a let rec" error? | |
+For instance, add_fun_binding could be restricted so that | |
+ add_fun_binding : letrec_id -> (('arg -> 'ret) code -> 'arg code -> | |
+'ret code) -> (('arg -> 'ret) code -> 'body code) -> 'body code | |
+and | |
+ add_fun_binding id (fun f x -> .<.~f .~x>.) (fun f -> .<.~f 0>.) | |
+produces | |
+ .<let rec f x = f x in f 0>. | |
+ | |
+ | |
+ | |
+* Installation notes | |
+ | |
+configuration line | |
+OLD (before N102) | |
+./configure -prefix /home/oleg/Cache/ncaml4/ -no-tk -no-pthread | |
+-no-camlp4 -no-graph | |
+ | |
+N102 | |
+./configure -prefix /home/oleg/Cache/ncaml4/ -no-debugger | |
+ -no-ocamlbuild -no-graph | |
+ | |
+Use -dtypedtree option on the top level to see the generated tree. | |
+ | |
+ | |
+* Old Future work | |
+ | |
+** Get rid of .! as a kernel form | |
+ | |
+The syntax .! may remain, but only as syntax. The parser would just | |
+expand .! x to {Trx.cde = x}. To really run the code, we would write | |
+ Run.run .! .<1>. | |
+Incidentally, we can have Run.run_bytecode and Run.run_native. | |
+Since it is possible now to dynamically load native code into | |
+bytecode, we can separate the mode of running the generated code | |
+(bytecode/native) from the mode of running the generator. | |
+ | |
+We don't have to add the record 'a cde to predef. We can add it to | |
+Trx. The only drawback is that pretty-printer may print {cde = x} with | |
+the Trx qualification. But it is easy to adjust the pretty-printer to | |
+omit the Trx qualification. | |
+ | |
+[BER N101 removed environment classifiers altogether. Therefore, run | |
+ became ordinary function and is moved out of the kernel.] | |
+ | |
+Then BER MetaOCaml becomes BE MetaOCaml? | |
+ | |
+** without environment classifiers, more programs type check | |
+let tr7 = .<fun x -> !. x>.;; | |
+(* | |
+val tr7 : ('a code -> 'a) code = .<fun x_63 -> Runcode.( !. ) x_63>. | |
+ | |
+Was: | |
+Characters 24-25: | |
+ let tr7 = .<fun x -> !. x>.;; | |
+ ^ | |
+Error: !. error: 'a not generalizable in ('a, 'b) code | |
+*) | |
+But the code was legitimate: | |
+let 10 = !. tr7 .<10>.;; | |
+ | |
+BER N101 accepts it and runs. | |
+ | |
+ | |
+** well-formedness check for let rec | |
+[This check is implemented in BER N101] | |
+ | |
+ let rec x = x in x;; | |
+ ^ | |
+Error: This kind of expression is not allowed as right-hand side of `let rec' | |
+ | |
+It is not well-formed. But it is well-typed: | |
+ | |
+let c1 = .<let rec x = x in x>.;; | |
+ val c1 : ('cl, 'a) code = .<let rec x_142 = x_142 in x_142>. | |
+ | |
+and it is accepted by all versions of MetaOCaml before N101. It is | |
+only when we try to compile (that is, run) the generated code, we see | |
+a problem | |
+ | |
+.! c1;; | |
+ Exception: Translcore.Error (_, 1). | |
+ | |
+ | |
+Once I knew where to look, I found another similar problem: | |
+ | |
+let c2 = .<let rec [] = [] in []>.;; | |
+ val c2 : ('cl, 'a list) code = .<let rec [] = [] in []>. | |
+ | |
+It is well-typed and accepted. Alas, | |
+ | |
+.! c2;; | |
+ Exception: Translcore.Error (_, 0). | |
+ | |
+This problem has existed in MetaOCaml from the very beginning, as far | |
+as I could see. | |
+ | |
+The first problem is most troubling. Consider | |
+ | |
+let t1 x = .<0:: .~x>.;; | |
+ val t1 : ('cl, int list) code -> ('cl, int list) code = <fun> | |
+let t2 x = .<if true then 0:: .~x else [1]>.;; | |
+ val t2 : ('cl, int list) code -> ('cl, int list) code = <fun> | |
+ | |
+Both t1 and t2 are well-typed and accepted. They have identical types. | |
+However, | |
+ | |
+let r = .<let rec x = .~(t1 .<x>.) in 2>. in .!r;; | |
+ - : int = 2 | |
+ | |
+let r = .<let rec x = .~(t2 .<x>.) in 2>. in .!r;; | |
+ Exception: Translcore.Error (_, 1). | |
+ | |
+I was thinking to check that all future-stage letrec are of the form | |
+ let rec f x1 ... xn = ... | |
+although this is certainly restrictive since it precludes lazy | |
+bindings and also the following legitimate code | |
+ | |
+ type stream = Stream of string * (unit -> stream);; | |
+ let rec f = Stream ("stream", fun () -> f) in f;; | |
+Perhaps it may be worth noting the issue and leave it for now? | |
+ | |
+** Installation notes | |
+When linking the first time, beware! | |
+Since we added to predef.ml[i], we have changed the timestamps of the | |
+pre-defined identifiers and exceptions, and so created inconsistency | |
+with respect to the bootstrap compiler. So, when compiling the system | |
+the first time, after applying the patches to OCaml, do | |
+ make core | |
+ make coreboot | |
+ make all | |
+[This is fixed in N101, which is now binary compatible with OCaml] | |
+ | |
+** Type-checking of run | |
+Type-checking of run (in typing/typecore.ml) can be done differently, | |
+using polymorphic/universally quantified variables (Tpoly/Tunivar) | |
+ | |
+One may think that we can pre-process the ParseTree before we hand it | |
+over to the type-checker, replacing Pexp_constant that occurs within | |
+the brackets with a node Pexp_apply "mk_constant", etc. However, we | |
+have problems with generalizing second-stage let. Since the let-form | |
+will be replaced with a function call, generalization won't be | |
+performed! | |
+[As of BER N101, this is no longer relevant since there are no | |
+environment classifiers any more.] | |
+ | |
+** The problem of the constructor environment (signature) | |
+ | |
+Why typecore.ml has so many changes. | |
+A bracket expression .< e >. is typechecked once, then it is turned | |
+into expression that builds, at run-time, the AST for e. When we run | |
+the code expression, MetaOCaml type-checks the AST -- essentially | |
+type-checking e the second time around, at a different level this | |
+time. The second type-checking certainly occurs in a different | |
+environment -- specifically, in a different constructor and label | |
+environment. The constructors and labels that have been in effect when | |
+e is first type-checked may be re-defined when e is type-checked the | |
+second time. | |
+ | |
+Therefore, we remember, in the fields pexp_ext and ppat_ext the typed | |
+tree of the expression resulting from the original type-checker | |
+run. When we type-check the expression the second time, we keep the | |
+constructor descriptors resolved from the first time around (yet we | |
+re-typecheck the arguments of the record and the constructed | |
+expression: for the sake of staging constructs and CSP, which are | |
+demoted. After all, the second type-checking occurs at a different | |
+level). | |
+ | |
+Currently, MetaOCaml adds pexp_ext and ppat_ext fields to ParseTree, | |
+to store ref to the type of the node. We only need this information | |
+for nodes of the variant and record types (see typecore). We don't | |
+need to store anything for literals, and other irrelevant nodes. | |
+Furthermore, we don't need to store anything for records and variants | |
+that are defined in Pervasives (or stdlib). The next approximation: | |
+we don't need to store any type information if the type is a | |
+variant/record defined in another module (that is, qualified with the | |
+name of another module) -- provided that the corresponding .cmi is | |
+available at run-time, to the run-time compiler. | |
+ | |
+What pexp_ext and ppat_ext really need to store? Can they just store | |
+constructor, labels, probably types and classes (but not values) maps | |
+from Env.t? (We need to force the maps: they have lazy components). | |
+We can bracket only expressions: therefore, staged code, when | |
+typechecked again, cannot modify the constructor, label etc maps | |
+from the environment. Well, there is always | |
+ <let module M = struct type foo = Foo ... end in ...> | |
+but it is not clear if we want to support this. | |
+ | |
+It would be great to find a way not not add fields to Parsetree such | |
+as pexp_ext and ppat_ext. The latter causes too many modifications, in | |
+all the places where such records are constructed. Could we store the | |
+_ext fields in a parallel map? How to garbage-collect them though? It | |
+would be great if the only cases where ppat_ext and pext_ext mattered | |
+where the cases of identifiers (so we can use Lident as a key). | |
+Incidentally, the type t in env.ml is a collection of various maps. | |
+Perhaps that's the place to store pexp_ext information for | |
+identifiers. Here is an idea: Think of replacing pexp_ext and ppat_ext | |
+with an extra field in location record. Location is almost always used | |
+as an abstract type. Thus when extending the concrete type of | |
+Location.t, little code needs to be patched. | |
+ | |
+Here is the idea how to simplify trx code. As we discussed earlier, | |
+the code expression produces Parsedtree, which contains only the names | |
+of the constructors. We need to know the constructor description. In | |
+the regular type-checking, all this information can be found in the | |
+environment, placed there by data type declarations. When we invoke | |
+the type checker at run time (as part of running the code), there are | |
+no data declarations available. After all, a code expression contains | |
+only expression rather than declarations. Currently, we stash away the | |
+whole environment inside the fake parsetree. What we can do: during | |
+the first type-checking, determine is a particular constructor name is | |
+pre-defined or user-defined. If a constructor name is not global, | |
+generate a long identifier of the form Lxxxx.real_name where Lxxxx is | |
+some random string. Maintain a new environment and store in it the | |
+association of that Lxxxx with the constructor description (which we | |
+can get from Env.t). Once we finish type-checking, attach the new env | |
+as part of the code value; perhaps each code value should have the | |
+field for the environment for constructor description, exception | |
+description, label and method description. When we run the code, add | |
+this associated env to the env of the type-checker. We don't need to | |
+care of any time-stamps. Since the parsed tree contains unique names | |
+anyway, like Lxxxxx, there is no chance of name clashes. Perhaps the | |
+synthesized constructor environment could be saved in the .cmo file | |
+(along with import and other such data)? | |
+ | |
+*** A new idea | |
+ | |
+In general, a code value should be a pair (ParseTree, CtorEnv). An | |
+escape (splice) should merge the CtorEnv parts. So a code value is a | |
+closure with respect to a signature for type and data constructors. In | |
+a sense, lambda-a gives us that, if we consider type declarations as | |
+let-expressions (lambda-a has big lambda). | |
+ | |
+Actually, OCaml 3.12 already has a similar facility: local open. | |
+So, a code value should be a parse tree expression of the form | |
+ let module M = struct | |
+ declaration of needed constructors, labels, | |
+ exceptions | |
+ let res = <expr> | |
+ in M.res | |
+Splice should merge such modules, performing renames in case | |
+of identically named constructors. | |
+ | |
+See env.ml (and mtype.ml) for functions to export Env.t as a signature | |
+(and to merge an old signature with the current Env.t -- open). | |
+There is code for renaming and qualifying all identifiers by a path. | |
+So, when we generate the code for bracket, maintain the list of | |
+constructors used in the code. Then build the declarations. | |
+ | |
+Since we won't store the env any more (as part of pexp_ext, ppat_ext), | |
+we don't need the lazy transformation of env.ml and we don't need | |
+to maintain extra time-stamps (which we currently do). The generated | |
+code becomes self-contained, with all needed constructors, which | |
+are explicit -- rather than hidden in *_ext fields. | |
+ | |
+As the initial approximation, to make porting to OCaml 4, require | |
+that all constructors to be in separate modules (the corresponding | |
+.cmi must be available at run-time, and, properly, we should record | |
+their CRC). Later on, save the needed .cmi as part of the code (for | |
+native compilation). Later, we eliminate that restriction by | |
+building proper declarations. [BER MetaOCaml N100 implements this | |
+idea.] | |
+ | |
+A code value should be a module, struct type t = ... let term = xxx | |
+end. That nodule defines all type constructors/types that are used | |
+within the code, except for the built-ins or pervasives. We should | |
+define these types along with equalities (sharing constraints) so that | |
+the code and the main program use consistent types.That solves the | |
+problem of redeclarations (we introduce a nested struct). Also, we | |
+automatically obtain the desired property that a code value is a | |
+closure with respect to the constructor env. | |
+ | |
+type foo = Foo | |
+let x = <Foo> | |
+type bar = Foo | |
+let y = <Foo> | |
+ | |
+type foo1 = Foo1 | |
+let x = quote (struct type t1 = foo1 = Foo | |
+ let res = Foo end) | |
+ | |
+It has to be a functor, from the env. We rely on contra-variance (env | |
+may contain many more types), Since the env should only contain | |
+types, its run-time representation is empty. But splicing is a bit | |
+more expensive since we have to apply env. All identifiers in the env | |
+are alpha-renamed to contain the explicit tstamp. The main benefit is | |
+that we don't need to modify the AST to contain the type env. | |
+ | |
+An idea for the constructor calculus | |
+ | |
+Datatype definitions can be represented in System Fw: | |
+ data T = Foo | Bar | |
+ | |
+is equivalent to the type T, functions foo :: T and bar :: T and | |
+the deconstructor T -> w -> w -> w. The body of the program in the scope of | |
+T can be represented as | |
+ Lam(t) lam(foo:t) lam(bar:t) lam(decon:forall w. t->w->w) ... | |
+ | |
+Assume that Lam and lam are special in that they bind `special | |
+identifiers' (constructors) and that we can evaluate under such | |
+lam. If c is a special identifier, then c v is a value. (check CRWL; I | |
+think we don't need to do anything about not-fully-applied | |
+constructors, which are values anyway.) We need Fw so we can bind | |
+types of the kind *->*, etc. needed for defining list-like types. The | |
+main advantage: we don't need to introduce constants, and we get the | |
+regular scoping, substitution rules. So, constants (constructors) and | |
+identifiers are pretty much the same, with respect to alpha renaming | |
+and substitution. That simplifies the calculus as we introduce | |
+staging. | |
+ | |
+ | |
+** Other | |
+Think about moving the predefined type ('a,'b) code from | |
+typing/predef.ml[i] into trx.mli Do we really need the code type | |
+predefined? Can we consider it pervasive instead? There would be no | |
+need to modify typecore to add a special rule to process the code | |
+type. | |
+ | |
+A conversation with Chung-chieh Shan brought up another issue: | |
+generalization is not at all clear staged languages. Consider | |
+ .<let f = fun x -> x in (f 1, f true)>. | |
+The code is OK. When we type-check it at level 0, we have to | |
+type-check the body of the bracket at level 1 | |
+ let f = fun x -> x in (f 1, f true) | |
+and it is certainly OK, since f is generalizable as being bound to a | |
+value. Now consider this: | |
+ .<let f = .~(.<fun x -> x>.) in (f 1, f true)>. | |
+Is this OK? MetaOCaml says yes. What about | |
+ .<let f = .~((fun y -> y) .<fun x -> x>.) in (f 1, f true)>. | |
+Now it does not generalize. | |
+For a good reason! The following, for example, generalizes | |
+let lift x = .<x>.;; | |
+let fff = | |
+ .<let foo = fun x -> let t = .~(lift (ref [])) in | |
+ (match !t with [] -> t := [x]; x | [y] -> t := [x]; y) in | |
+ (foo ("xxx"), foo(true), foo([1,2]))>.;; | |
+and gives the Bus error. See more description and proposed restriction | |
+in the file Problems.txt, the entry as of June 11, 2010. | |
+ | |
+Other interesting cases: | |
+let id y = y in .<let f = fun x -> id x in (f 1, f true)>.;; | |
+ | |
+Interesting case: | |
+let id y = y in .<let f = fun x -> id y in (f 1, f true)>.;; | |
+let id y = y in .<let f = fun x -> id x in (f 1, f true)>.;; | |
+ | |
+ | |
+ | |
+* Done in N102 (Dec 2014) | |
+ | |
+** What part of MetaOCaml can be moved to the OCaml proper | |
+Suggestions for the OCaml HQ | |
+ | |
+ - Better and modular error handling. Currently, to add a new | |
+ error, say, to typecore.ml one has to add the error to typecore.mli, | |
+ make the same addition to typecore.ml, augment the printing function | |
+ in typecore.ml. To add a new category, one has to augment | |
+ driver/errors.ml. I have tried a uniform | |
+ exception Error of (format -> unit) | |
+ The error carries the function that will print out the error | |
+ message on the supplied ppf. Handler becomes much more extensible! | |
+ This approach does seem to work out. It is used all throughout N101. | |
+ | |
+OCaml 4.02.1 introduces the modular error handling, by registering | |
+handlers for specific exceptions. See parsing/location.ml. BER N102 | |
+takes advantage of this facility. | |
+ | |
+* Done in N004 (Nov 7, 2012) | |
+Check to see if val_level field in the value_description structure can | |
+be eliminated. Quite a few patches become unnecessary. We should | |
+associate the staging level with identifiers rather than values. We | |
+should introduce a new map in Env.t that maps identifiers to levels. | |
+Global identifiers and identifiers appearing in structures and | |
+signatures are not in the domain of that map and are implicitly | |
+0-level. We don't support module expressions in the staged code. | |
+ | |
+We have done that. | |
+ | |
+Adding a new Texp_ident (see typecore etc) should be accompanied by | |
+Env.add_level ident !global_level But we don't support staging for | |
+objects. | |
+ | |
+Before | |
+ | |
+# let x = 1 in .<x>.;; | |
+- : ('a, int) code = .<1>. | |
+# let x = [] in .<x>.;; | |
+- : ('a, 'b list) code = .<(* cross-stage persistent value (as id: x) *)>. | |
+# let x = None in .<x>.;; | |
+- : ('a, 'b option) code = .<(* cross-stage persistent value (as id: x) *)>. | |
+# let x = "abc" in .<x>.;; | |
+- : ('a, string) code = .<(* cross-stage persistent value (as id: x) *)>. | |
+# let x = 123l in .<x>.;; | |
+- : ('a, int32) code = .<(* cross-stage persistent value (as id: x) *)>. | |
+ | |
+# .<Array.get>.;; | |
+- : ('a, 'b array -> int -> 'b) code = .<Array.get>. | |
+# .<List.nth>.;; | |
+- : ('a, 'b list -> int -> 'b) code = | |
+.<(* cross-stage persistent value (as id: List.nth) *)>. | |
+ | |
+Now | |
+# let x = 1 in .<x>.;; | |
+- : ('a, int) code = .<1>. | |
+# let x = [] in .<x>.;; | |
+- : ('a, 'b list) code = .<(* cross-stage persistent value (as id: x) *)>. | |
+# let x = None in .<x>.;; | |
+- : ('a, 'b option) code = .<(* cross-stage persistent value (as id: x) *)>. | |
+# let x = "abc" in .<x>.;; | |
+- : ('a, string) code = .<"abc">. | |
+# let x = 123l in .<x>.;; | |
+- : ('a, int32) code = .<123>. | |
+# .<Array.get>.;; | |
+X: Stage for var is set to implicit 0:Array.get | |
+- : ('a, 'b array -> int -> 'b) code = .<Array.get>. | |
+# .<List.nth>.;; | |
+X: Stage for var is set to implicit 0:List.nth | |
+- : ('a, 'b list -> int -> 'b) code = .<List.nth>. | |
+ | |
+More CSP are carried as literals. | |
+ | |
+When compiling code expressions, warnings should be disabled since | |
+they are not informative anyway. See runcode.ml, the function | |
+with_disabled_warnings. | |
+ | |
+ | |
+* Done in N100 (January 2013) | |
+ | |
+** trx.ml is re-written, and so most of the following has been taken | |
+care of | |
+ | |
+In [old] trx.ml, check to see if ident_can_be_quoted and path_to_lid | |
+can be merged to a single function, following the idea that any path | |
+made of global/persistent components can be safely converted to lident | |
+(the time stamps are all zero anyway). Any such path can be used to | |
+refer to a CSP by name, since that path persists and is stable. I | |
+guess it is also important that it is global: we can expect the same | |
+name available when we compile the code and when we invoke the | |
+type-checker again at run-time. | |
+ | |
+In trx.ml, there are notes on CSP, in the comments before mkcsp. If | |
+CSP id is long id, (global id), use it as a constant and generate the | |
+corresponding global ref code. Zero-arity constants such as [] or | |
+None, when used as CSP, can be included by value, and show as such | |
+when we print out the code values. OCaml 3.11 adds annotations for | |
+identifiers (see typing/annot.mli and search for Annot in | |
+env.mli). Annot are used for the sake of .annot files. That data could | |
+be useful to identify CSP that refer to external or global | |
+identifiers. | |
+ | |
+** Re-written trx.ml now does more efficient traversal, so most | |
+of the following has been taken care of | |
+ | |
+It seems that there is a lot of room for improvement. For example, | |
+run compiles and type-checks the code expression from scratch. | |
+Mainly, Trx.structure pre-processes the whole program, each and every | |
+expression and definition -- even if an expression contains no | |
+staging forms. That pre-processing re-builds the whole parse tree, | |
+which is wasteful, and slows down compilation for large programs. | |
+We should hook escape and bracket processing to the type-checker -- to | |
+typecore.ml, and be done. We pay for staging only when needed. | |
+ | |
+I have hooked Trx.trx_structure as a post-processor to | |
+Typemod.type_structure. Now, top-level drivers (toplevel/toploop.ml, | |
+driver/compile.ml) no longer have to be modified. The function | |
+Typemod.type_structure traverses the whole structure expression, | |
+invoking typecore.type_exp and other functions. But Trx.trx_structure | |
+does a very similar traversal! I have to think how to simplify | |
+unnecessary traversal and hook trx processing deeper, perhaps within | |
+typecore.type_exp and typecore.type_binding. These are the only cases | |
+that matter... | |
+ | |
+** Better quotation (which results in better printing of CSP *) | |
+# let l x = .<x>.;; | |
+val l : 'a -> ('cl, 'a) code = <fun> (* Polymorphic! *) | |
+# l 1;; | |
+- : ('a, int) code = .<(* cross-stage persistent value (as id: x) *)>. | |
+# l 1.0;; | |
+- : ('a, float) code = .<1.>. (* Now it prints as a constant *) | |
+ | |
+** Record with polymorphic fields in brackets: problem solved | |
+ | |
+A new way of running the code and its problems. Actually, the | |
+problems are old -- the Trx module cannot handle polymorphic | |
+values. It erroneously fails to generalize type variables. Here is | |
+the illustration of the bug. | |
+ | |
+# .< {Trx.cde = .<1>.} >.;; | |
+- : ('a, int Trx.cde) code = .<{Trx.cde = .<1>.}>. | |
+# .! .< {Trx.cde = .<1>.} >.;; | |
+# .! .< {Trx.cde = .<1>.} >.;; | |
+This expression [1 is highlighted] | |
+has type ('a, int) code but is here used with type | |
+ ('b, int) code | |
+ | |
+Exception: Trx.TypeCheckingError. | |
+ | |
+Since the new way of running relies on the polymorphic values, the | |
+problem becomes acute. | |
+Compare: | |
+# let a1 = .<fun x -> .! .<1>.>.;; | |
+val a1 : ('a, 'b -> int) code = .<fun x_2 -> .!.<1>.>. | |
+# let a2 = .! a1;; | |
+val a2 : 'a -> int = <fun> | |
+# a2 42;; | |
+- : int = 1 | |
+ | |
+# let b1 = .<fun x -> Runcode.run {Trx.cde = .<1>.}>.;; | |
+val b1 : ('a, 'b -> int) code = | |
+ .<fun x_3 -> | |
+ (((* cross-stage persistent value (as id: Runcode.run) *)) | |
+ {Trx.cde = .<1>.})>. | |
+# let b11 = {Trx.cde = b1 };; | |
+val b11 : ('a -> int) Trx.cde = | |
+ .<fun x_3 -> | |
+ (((* cross-stage persistent value (as id: Runcode.run) *)) | |
+ {Trx.cde = .<1>.})>. | |
+ | |
+# Runcode.run b11;; | |
+Warning X: this argument will not be used by the function. | |
+This expression has type ('a, int) code but is here used with type | |
+ ('b, int) code | |
+ | |
+Exception: Trx.TypeCheckingError. | |
+ | |
+Runcode re-typechecks the expression -- and here where the error comes | |
+in. | |
+ | |
+One workaround: when trx pre-processes the code first-time around, | |
+it should detect {Trx.cde = xxx} that appears within quotation, and | |
+replace it with something else (Parsetree.exp?). After all, the | |
+type-checking has already happened; when the generated code is run, no | |
+real check are needed; so we can use the untyped Parsetree at will. | |
+ | |
+I believe the problem is in the clause `Pexp_record(lid_sexp_list, | |
+opt_sexp)' of the function type_exp of the file typecore.ml. In the | |
+conditional branch of (is_type_exp_second_time sexp) being true, we | |
+may be missing generalization (or forget to introduce fresh type | |
+variables). | |
diff -Naur ocaml-4.02.1/metalib/ORIGINAL-LICENSE-META ocaml-ber-n102/metalib/ORIGINAL-LICENSE-META | |
--- ocaml-4.02.1/metalib/ORIGINAL-LICENSE-META 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/ORIGINAL-LICENSE-META 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,50 @@ | |
+ | |
+Copyright 2002-2006 | |
+Walid Taha's research group at Rice University and collaborators | |
+All Rights Reserved | |
+ | |
+MetaOCaml is licensed as a patch to the respective distribution of | |
+OCaml upon which it is based. Please see OCaml licence for status of | |
+a patch. | |
+ | |
+Permission is hereby granted, free of charge, to any person obtaining | |
+a copy of this software and associated documentation files (the | |
+"Software"), to deal with 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: | |
+ | |
+Redistributions of source code must retain the above copyright notice, | |
+this list of conditions and the following disclaimers. | |
+ | |
+ - Redistributions in binary form must reproduce the above copyright | |
+ notice, this list of conditions and the following disclaimers in | |
+ the documentation and/or other materials provided with the | |
+ distribution. | |
+ | |
+ - Neither the names of MetaOCaml, Concoqtion, Rice University, nor the | |
+ names of its contributors may be used to endorse or promote products | |
+ derived from this Software without specific prior written permission. | |
+ | |
+ - Products derived from this software may not be called "MetaOCaml", | |
+ "MetaOCaml Concoqtion", "Concoqtion", or an extension of these names | |
+ without prior written permission from the RAP group. | |
+ | |
+Commercial use is prohibited without prior written permission. | |
+ | |
+Permissions must be granted by | |
+ | |
+ Walid Taha (taha@rice.edu) | |
+ Department of Computer Science | |
+ Rice University, Houston, TX 77025. | |
+ | |
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
+NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT | |
+HOLDERS 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 WITH THE | |
+SOFTWARE. | |
+ | |
diff -Naur ocaml-4.02.1/metalib/patches/patch ocaml-ber-n102/metalib/patches/patch | |
--- ocaml-4.02.1/metalib/patches/patch 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/patches/patch 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,793 @@ | |
+diff --git a/.depend b/.depend | |
+index 5d95a9b..63a137f 100644 | |
+--- a/.depend | |
++++ b/.depend | |
+@@ -122,6 +122,9 @@ typing/printtyped.cmi : typing/typedtree.cmi | |
+ typing/stypes.cmi : typing/typedtree.cmi parsing/location.cmi \ | |
+ typing/annot.cmi | |
+ typing/subst.cmi : typing/types.cmi typing/path.cmi typing/ident.cmi | |
++typing/trx.cmi : typing/types.cmi typing/typedtree.cmi parsing/parsetree.cmi \ | |
++ parsing/longident.cmi parsing/location.cmi typing/env.cmi \ | |
++ parsing/asttypes.cmi | |
+ typing/typeclass.cmi : typing/types.cmi typing/typedtree.cmi \ | |
+ parsing/parsetree.cmi parsing/longident.cmi parsing/location.cmi \ | |
+ typing/ident.cmi typing/env.cmi typing/ctype.cmi parsing/asttypes.cmi | |
+@@ -280,6 +283,18 @@ typing/subst.cmo : typing/types.cmi utils/tbl.cmi typing/path.cmi \ | |
+ typing/subst.cmx : typing/types.cmx utils/tbl.cmx typing/path.cmx \ | |
+ utils/misc.cmx parsing/location.cmx typing/ident.cmx utils/clflags.cmx \ | |
+ typing/btype.cmx parsing/ast_mapper.cmx typing/subst.cmi | |
++typing/trx.cmo : utils/warnings.cmi typing/types.cmi typing/typedtree.cmi \ | |
++ typing/printtyp.cmi typing/predef.cmi typing/path.cmi \ | |
++ parsing/parsetree.cmi utils/misc.cmi parsing/longident.cmi \ | |
++ parsing/location.cmi typing/ident.cmi typing/env.cmi typing/ctype.cmi \ | |
++ typing/btype.cmi parsing/asttypes.cmi parsing/ast_helper.cmi \ | |
++ typing/trx.cmi | |
++typing/trx.cmx : utils/warnings.cmx typing/types.cmx typing/typedtree.cmx \ | |
++ typing/printtyp.cmx typing/predef.cmx typing/path.cmx \ | |
++ parsing/parsetree.cmi utils/misc.cmx parsing/longident.cmx \ | |
++ parsing/location.cmx typing/ident.cmx typing/env.cmx typing/ctype.cmx \ | |
++ typing/btype.cmx parsing/asttypes.cmi parsing/ast_helper.cmx \ | |
++ typing/trx.cmi | |
+ typing/typeclass.cmo : utils/warnings.cmi typing/typetexp.cmi \ | |
+ typing/types.cmi typing/typedtree.cmi typing/typedecl.cmi \ | |
+ typing/typecore.cmi parsing/syntaxerr.cmi typing/subst.cmi \ | |
+@@ -299,23 +314,25 @@ typing/typeclass.cmx : utils/warnings.cmx typing/typetexp.cmx \ | |
+ utils/clflags.cmx typing/btype.cmx parsing/asttypes.cmi \ | |
+ parsing/ast_helper.cmx typing/typeclass.cmi | |
+ typing/typecore.cmo : utils/warnings.cmi typing/typetexp.cmi \ | |
+- typing/types.cmi typing/typedtree.cmi parsing/syntaxerr.cmi \ | |
+- typing/subst.cmi typing/stypes.cmi typing/printtyp.cmi \ | |
+- typing/primitive.cmi typing/predef.cmi typing/path.cmi \ | |
+- parsing/parsetree.cmi typing/parmatch.cmi typing/oprint.cmi \ | |
+- utils/misc.cmi parsing/longident.cmi parsing/location.cmi \ | |
+- typing/ident.cmi typing/env.cmi typing/ctype.cmi typing/cmt_format.cmi \ | |
+- utils/clflags.cmi typing/btype.cmi parsing/asttypes.cmi \ | |
+- parsing/ast_helper.cmi typing/annot.cmi typing/typecore.cmi | |
++ typing/types.cmi typing/typedtree.cmi typing/trx.cmi \ | |
++ parsing/syntaxerr.cmi typing/subst.cmi typing/stypes.cmi \ | |
++ typing/printtyp.cmi typing/primitive.cmi typing/predef.cmi \ | |
++ typing/path.cmi parsing/parsetree.cmi typing/parmatch.cmi \ | |
++ typing/oprint.cmi utils/misc.cmi parsing/longident.cmi \ | |
++ parsing/location.cmi typing/ident.cmi typing/env.cmi typing/ctype.cmi \ | |
++ typing/cmt_format.cmi utils/clflags.cmi typing/btype.cmi \ | |
++ parsing/asttypes.cmi parsing/ast_helper.cmi typing/annot.cmi \ | |
++ typing/typecore.cmi | |
+ typing/typecore.cmx : utils/warnings.cmx typing/typetexp.cmx \ | |
+- typing/types.cmx typing/typedtree.cmx parsing/syntaxerr.cmx \ | |
+- typing/subst.cmx typing/stypes.cmx typing/printtyp.cmx \ | |
+- typing/primitive.cmx typing/predef.cmx typing/path.cmx \ | |
+- parsing/parsetree.cmi typing/parmatch.cmx typing/oprint.cmx \ | |
+- utils/misc.cmx parsing/longident.cmx parsing/location.cmx \ | |
+- typing/ident.cmx typing/env.cmx typing/ctype.cmx typing/cmt_format.cmx \ | |
+- utils/clflags.cmx typing/btype.cmx parsing/asttypes.cmi \ | |
+- parsing/ast_helper.cmx typing/annot.cmi typing/typecore.cmi | |
++ typing/types.cmx typing/typedtree.cmx typing/trx.cmx \ | |
++ parsing/syntaxerr.cmx typing/subst.cmx typing/stypes.cmx \ | |
++ typing/printtyp.cmx typing/primitive.cmx typing/predef.cmx \ | |
++ typing/path.cmx parsing/parsetree.cmi typing/parmatch.cmx \ | |
++ typing/oprint.cmx utils/misc.cmx parsing/longident.cmx \ | |
++ parsing/location.cmx typing/ident.cmx typing/env.cmx typing/ctype.cmx \ | |
++ typing/cmt_format.cmx utils/clflags.cmx typing/btype.cmx \ | |
++ parsing/asttypes.cmi parsing/ast_helper.cmx typing/annot.cmi \ | |
++ typing/typecore.cmi | |
+ typing/typedecl.cmo : utils/warnings.cmi typing/typetexp.cmi \ | |
+ typing/types.cmi typing/typedtree.cmi parsing/syntaxerr.cmi \ | |
+ typing/subst.cmi typing/printtyp.cmi typing/primitive.cmi \ | |
+diff --git a/Makefile b/Makefile | |
+index 6c0e7e6..287d183 100644 | |
+--- a/Makefile | |
++++ b/Makefile | |
+@@ -20,6 +20,9 @@ CAMLOPT=boot/ocamlrun ./ocamlopt -nostdlib -I stdlib -I otherlibs/dynlink | |
+ COMPFLAGS=-strict-sequence -w +33..39+48 -warn-error A -bin-annot \ | |
+ -safe-string $(INCLUDES) | |
+ LINKFLAGS= | |
++# For debugging | |
++# COMPFLAGS=-warn-error A -g $(INCLUDES) # NNN | |
++# LINKFLAGS=-g #NNN | |
+ | |
+ CAMLYACC=boot/ocamlyacc | |
+ YACCFLAGS=-v | |
+@@ -49,6 +52,7 @@ PARSING=parsing/location.cmo parsing/longident.cmo \ | |
+ parsing/pprintast.cmo \ | |
+ parsing/ast_mapper.cmo | |
+ | |
++# NNN (trx) | |
+ TYPING=typing/ident.cmo typing/path.cmo \ | |
+ typing/primitive.cmo typing/types.cmo \ | |
+ typing/btype.cmo typing/oprint.cmo \ | |
+@@ -59,6 +63,7 @@ TYPING=typing/ident.cmo typing/path.cmo \ | |
+ typing/mtype.cmo typing/envaux.cmo typing/includecore.cmo \ | |
+ typing/typedtreeIter.cmo typing/typedtreeMap.cmo typing/cmt_format.cmo \ | |
+ typing/includemod.cmo typing/typetexp.cmo typing/parmatch.cmo \ | |
++ typing/trx.cmo \ | |
+ typing/stypes.cmo typing/typecore.cmo \ | |
+ typing/typedecl.cmo typing/typeclass.cmo \ | |
+ typing/typemod.cmo | |
+@@ -130,6 +135,8 @@ all: | |
+ $(MAKE) otherlibraries $(OCAMLBUILDBYTE) $(WITH_DEBUGGER) \ | |
+ $(WITH_OCAMLDOC) | |
+ | |
++# NNN make all && (cd metalib && make clean all) && (make install; cd metalib && make install) | |
++ | |
+ # Compile everything the first time | |
+ world: | |
+ $(MAKE) coldstart | |
+@@ -325,6 +332,13 @@ install: | |
+ $(INSTALL_COMPLIBDIR) | |
+ cp expunge $(INSTALL_LIBDIR)/expunge$(EXE) | |
+ cp toplevel/topdirs.cmi $(INSTALL_LIBDIR) | |
++# NNN typing/trx.ml needs its own interface (since it looks up identifiers | |
++# in itself) | |
++# Although typing/trx.cmi is already copied, see above, it is copied | |
++# into $((COMPLIBDIR). We need trx.cmi in the standard .cmi search path. | |
++ cp typing/trx.cmi $(INSTALL_LIBDIR) | |
++# BTW, trx.cmo is part of ocamlcommon.cma | |
++# NNN end | |
+ cd tools; $(MAKE) install | |
+ -cd man; $(MAKE) install | |
+ for i in $(OTHERLIBRARIES); do \ | |
+@@ -744,7 +758,7 @@ html_doc: ocamldoc | |
+ partialclean:: | |
+ cd ocamldoc && $(MAKE) clean | |
+ | |
+-alldepend:: | |
++alldepen1d:: | |
+ cd ocamldoc && $(MAKE) depend | |
+ | |
+ # The extra libraries | |
+diff --git a/parsing/lexer.mll b/parsing/lexer.mll | |
+index 237b447..c009d8b 100644 | |
+--- a/parsing/lexer.mll | |
++++ b/parsing/lexer.mll | |
+@@ -270,6 +270,8 @@ let identchar_latin1 = | |
+ ['A'-'Z' 'a'-'z' '_' '\192'-'\214' '\216'-'\246' '\248'-'\255' '\'' '0'-'9'] | |
+ let symbolchar = | |
+ ['!' '$' '%' '&' '*' '+' '-' '.' '/' ':' '<' '=' '>' '?' '@' '^' '|' '~'] | |
++let symbolcharnodot = (* NNN *) | |
++ ['!' '$' '%' '&' '*' '+' '-' '/' ':' '<' '=' '>' '?' '@' '^' '|' '~'] (* NNN *) | |
+ let decimal_literal = | |
+ ['0'-'9'] ['0'-'9' '_']* | |
+ let hex_literal = | |
+@@ -302,6 +304,9 @@ rule token = parse | |
+ } | |
+ | blank + | |
+ { token lexbuf } | |
++ | ".<" { DOTLESS } (* NNN *) | |
++ | ">." { GREATERDOT } (* NNN *) | |
++ | ".~" { DOTTILDE } (* NNN *) | |
+ | "_" | |
+ { UNDERSCORE } | |
+ | "~" | |
+@@ -472,8 +477,10 @@ rule token = parse | |
+ { PREFIXOP(Lexing.lexeme lexbuf) } | |
+ | ['~' '?'] symbolchar + | |
+ { PREFIXOP(Lexing.lexeme lexbuf) } | |
+- | ['=' '<' '>' '|' '&' '$'] symbolchar * | |
++ | ['=' '<' '|' '&' '$'] symbolchar * (* NNN: ">." is not INFIXOP0 *) | |
+ { INFIXOP0(Lexing.lexeme lexbuf) } | |
++ | ['>'] symbolcharnodot symbolchar * (* NNN exclude ">." case *) | |
++ { INFIXOP0(Lexing.lexeme lexbuf) } (* NNN *) | |
+ | ['@' '^'] symbolchar * | |
+ { INFIXOP1(Lexing.lexeme lexbuf) } | |
+ | ['+' '-'] symbolchar * | |
+@@ -483,6 +490,8 @@ rule token = parse | |
+ | '%' { PERCENT } | |
+ | ['*' '/' '%'] symbolchar * | |
+ { INFIXOP3(Lexing.lexeme lexbuf) } | |
++ | "let" symbolchar* (* NNN *) | |
++ { LETOP(Lexing.lexeme lexbuf) } (* NNN *) | |
+ | eof { EOF } | |
+ | _ | |
+ { raise (Error(Illegal_character (Lexing.lexeme_char lexbuf 0), | |
+diff --git a/parsing/parser.mly b/parsing/parser.mly | |
+index ba8e98e..43330f1 100644 | |
+--- a/parsing/parser.mly | |
++++ b/parsing/parser.mly | |
+@@ -273,6 +273,21 @@ let wrap_type_annotation newtypes core_type body = | |
+ in | |
+ (exp, ghtyp(Ptyp_poly(newtypes,varify_constructors newtypes core_type))) | |
+ | |
++ (* NNN: the whole definition *) | |
++let let_operator op bindings cont = | |
++ let pat, expr = | |
++ match bindings with | |
++ | [] -> assert false | |
++ | [x] -> (x.pvb_pat,x.pvb_expr) | |
++ | l -> | |
++ let pats, exprs = | |
++ List.fold_right | |
++ (fun {pvb_pat=p;pvb_expr=e} (ps,es) -> (p::ps,e::es)) l ([],[]) in | |
++ ghpat (Ppat_tuple pats), ghexp (Pexp_tuple exprs) | |
++ in | |
++ mkexp(Pexp_apply(op, [("", expr); | |
++ ("", ghexp(Pexp_fun("", None, pat, cont)))])) | |
++ | |
+ let wrap_exp_attrs body (ext, attrs) = | |
+ (* todo: keep exact location for the entire attribute *) | |
+ let body = {body with pexp_attributes = attrs @ body.pexp_attributes} in | |
+@@ -293,6 +308,9 @@ let mkctf_attrs d attrs = | |
+ | |
+ /* Tokens */ | |
+ | |
++%token DOTLESS /* NNN */ | |
++%token GREATERDOT /* NNN */ | |
++%token DOTTILDE /* NNN */ | |
+ %token AMPERAMPER | |
+ %token AMPERSAND | |
+ %token AND | |
+@@ -358,6 +376,7 @@ let mkctf_attrs d attrs = | |
+ %token LESS | |
+ %token LESSMINUS | |
+ %token LET | |
++%token <string> LETOP /* NNN */ | |
+ %token <string> LIDENT | |
+ %token LPAREN | |
+ %token LBRACKETAT | |
+@@ -441,6 +460,7 @@ The precedences must be listed from low to high. | |
+ %nonassoc below_SEMI | |
+ %nonassoc SEMI /* below EQUAL ({lbl=...; lbl=...}) */ | |
+ %nonassoc LET /* above SEMI ( ...; let ... in ...) */ | |
++%nonassoc LETOP /* NNN */ | |
+ %nonassoc below_WITH | |
+ %nonassoc FUNCTION WITH /* below BAR (match ... with ...) */ | |
+ %nonassoc AND /* above WITH (module rec A: SIG with ... and ...) */ | |
+@@ -468,6 +488,7 @@ The precedences must be listed from low to high. | |
+ %nonassoc prec_unary_minus prec_unary_plus /* unary - */ | |
+ %nonassoc prec_constant_constructor /* cf. simple_expr (C versus C x) */ | |
+ %nonassoc prec_constr_appl /* above AS BAR COLONCOLON COMMA */ | |
++%left prec_escape /* NNN */ | |
+ %nonassoc below_SHARP | |
+ %nonassoc SHARP /* simple_expr/toplevel_directive */ | |
+ %nonassoc below_DOT | |
+@@ -1084,6 +1105,8 @@ expr: | |
+ { mkexp(Pexp_apply($1, List.rev $2)) } | |
+ | LET ext_attributes rec_flag let_bindings_no_attrs IN seq_expr | |
+ { mkexp_attrs (Pexp_let($3, List.rev $4, $6)) $2 } | |
++ | let_operator ext_attributes let_bindings IN seq_expr /* NNN */ | |
++ { wrap_exp_attrs (let_operator $1 $3 $5) $2 } /* NNN */ | |
+ | LET MODULE ext_attributes UIDENT module_binding_body IN seq_expr | |
+ { mkexp_attrs (Pexp_letmodule(mkrhs $4 4, $5, $7)) $3 } | |
+ | LET OPEN override_flag ext_attributes mod_longident IN seq_expr | |
+@@ -1200,6 +1223,12 @@ simple_expr: | |
+ { reloc_exp $2 } | |
+ | LPAREN seq_expr error | |
+ { unclosed "(" 1 ")" 3 } | |
++ | DOTLESS expr GREATERDOT /* NNN */ | |
++ { wrap_exp_attrs $2 | |
++ (None,[ghloc "metaocaml.bracket",PStr []]) } /* NNN */ | |
++ | DOTTILDE simple_expr %prec prec_escape /* NNN */ | |
++ { wrap_exp_attrs $2 | |
++ (None,[ghloc "metaocaml.escape",PStr []]) } /* NNN */ | |
+ | BEGIN ext_attributes seq_expr END | |
+ { wrap_exp_attrs (reloc_exp $3) $2 (* check location *) } | |
+ | BEGIN ext_attributes END | |
+@@ -1961,6 +1990,7 @@ operator: | |
+ | INFIXOP2 { $1 } | |
+ | INFIXOP3 { $1 } | |
+ | INFIXOP4 { $1 } | |
++ | LETOP { $1 } /* NNN */ | |
+ | BANG { "!" } | |
+ | PLUS { "+" } | |
+ | PLUSDOT { "+." } | |
+@@ -1978,6 +2008,16 @@ operator: | |
+ | PLUSEQ { "+=" } | |
+ | PERCENT { "%" } | |
+ ; | |
++ /* NNN: the whole definition */ | |
++let_operator: | |
++ LETOP { mkexp (Pexp_ident( | |
++ mkloc (Lident $1) | |
++ (symbol_rloc ()))) } | |
++ | mod_longident DOT LETOP { mkexp (Pexp_ident( | |
++ mkloc (Ldot($1,$3)) | |
++ (symbol_rloc ()))) } | |
++; | |
++ | |
+ constr_ident: | |
+ UIDENT { $1 } | |
+ /* | LBRACKET RBRACKET { "[]" } */ | |
+diff --git a/parsing/pprintast.ml b/parsing/pprintast.ml | |
+index 7dea70c..91ddb5e 100644 | |
+--- a/parsing/pprintast.ml | |
++++ b/parsing/pprintast.ml | |
+@@ -47,6 +47,8 @@ let fixity_of_string = function | |
+ | |
+ let view_fixity_of_exp = function | |
+ | {pexp_desc = Pexp_ident {txt=Lident l;_};_} -> fixity_of_string l | |
++ | {pexp_desc = Pexp_ident {txt=Ldot (Lident "Pervasives",l);_};_} -> (*NNN*) | |
++ fixity_of_string l (*NNN*) | |
+ | _ -> `Normal ;; | |
+ | |
+ let is_infix = function | `Infix _ -> true | _ -> false | |
+@@ -512,11 +514,44 @@ class printer ()= object(self:'self) | |
+ end | |
+ | _ -> false | |
+ method expression f x = | |
+- if x.pexp_attributes <> [] then begin | |
++ (* NNN begin *) | |
++ (* Keep in mind that there may be several metaocaml | |
++ attributes, and the order matters *) | |
++ (* Here we assume that all metaocaml attributes are at the front, | |
++ which is how they are generated. | |
++ *) | |
++ match x.pexp_attributes with | |
++ | ({txt="metaocaml.bracket"},_) :: t -> | |
++ pp f "@[<hov2>.<@ %a @ >.@]" self#expression {x with pexp_attributes=t} | |
++ | ({txt="metaocaml.escape"},_) :: t -> | |
++ begin | |
++ match x.pexp_desc with | |
++ | Pexp_ident li when t = [] -> pp f ".~%a" self#longident_loc li | |
++ | _ -> pp f ".~%a" (self#paren true self#expression) | |
++ {x with pexp_attributes=t} | |
++ end | |
++ | [({txt = "metaocaml.csp"},PStr [{pstr_desc = | |
++ Pstr_eval ({pexp_desc=Pexp_ident li},_)}])] -> | |
++ begin | |
++ (* This CSP is easy to print, so we print it *) | |
++ match x.pexp_desc with | |
++ | Pexp_apply (_,[("",{pexp_desc=Pexp_constant (Const_int _)})]) | |
++ -> | |
++ pp f "(* CSP %a *) %a" | |
++ self#longident_loc li | |
++ self#expression {x with pexp_attributes=[]} | |
++ | _ -> | |
++ pp f "(* CSP %a *)" | |
++ self#longident_loc li | |
++ end | |
++ (* if x.pexp_attributes <> [] then begin *) | |
++ | _::_ -> | |
+ pp f "((%a)%a)" self#expression {x with pexp_attributes=[]} | |
+ self#attributes x.pexp_attributes | |
+- end | |
+- else match x.pexp_desc with | |
++ (* end *) | |
++ | _ -> begin match x.pexp_desc with | |
++ (* else match x.pexp_desc with *) | |
++ (* NNN end *) | |
+ | Pexp_function _ | Pexp_fun _ | Pexp_match _ | Pexp_try _ | Pexp_sequence _ | |
+ when pipe || semi -> | |
+ self#paren true self#reset#expression f x | |
+@@ -620,6 +655,7 @@ class printer ()= object(self:'self) | |
+ pp f "@[<2>`%s@;%a@]" l self#simple_expr eo | |
+ | Pexp_extension e -> self#extension f e | |
+ | _ -> self#expression1 f x | |
++ end (* NNN *) | |
+ method expression1 f x = | |
+ if x.pexp_attributes <> [] then self#expression f x | |
+ else match x.pexp_desc with | |
+diff --git a/typing/predef.ml b/typing/predef.ml | |
+index e9b9f7e..9a08a86 100644 | |
+--- a/typing/predef.ml | |
++++ b/typing/predef.ml | |
+@@ -120,6 +120,17 @@ and ident_nil = ident_create "[]" | |
+ and ident_cons = ident_create "::" | |
+ and ident_none = ident_create "None" | |
+ and ident_some = ident_create "Some" | |
++ | |
++(* NNN ident_create "code" must be placed at the end of all other | |
++ ident creation expressions, to make sure that creating ident_code | |
++ does not shift the timestamps of other standard idents like | |
++ Eof, etc. Otherwise, binary compatibility with OCaml breaks, | |
++ and we have to do the expensive bootstrapping. | |
++*) | |
++let ident_code = ident_create "code" (* NNN *) | |
++let path_code = Pident ident_code (* NNN *) | |
++let type_code t = newgenty (Tconstr(path_code, [t], ref Mnil)) (* NNN *) | |
++ | |
+ let common_initial_env add_type add_extension empty_env = | |
+ let decl_bool = | |
+ {decl_abstr with | |
+@@ -157,6 +168,13 @@ let common_initial_env add_type add_extension empty_env = | |
+ type_params = [tvar]; | |
+ type_arity = 1; | |
+ type_variance = [Variance.covariant]} | |
++ (* NNN added decl_code *) | |
++ and decl_code = | |
++ let tvar = newgenvar() in | |
++ {decl_abstr with | |
++ type_params = [tvar]; | |
++ type_arity = 1; | |
++ type_variance = [Variance.covariant]} | |
+ in | |
+ | |
+ let add_extension id l = | |
+@@ -184,6 +202,7 @@ let common_initial_env add_type add_extension empty_env = | |
+ [newgenty (Ttuple[type_string; type_int; type_int])] ( | |
+ add_extension ident_undefined_recursive_module | |
+ [newgenty (Ttuple[type_string; type_int; type_int])] ( | |
++ add_type ident_code decl_code ( (* NNN *) | |
+ add_type ident_int64 decl_abstr ( | |
+ add_type ident_int32 decl_abstr ( | |
+ add_type ident_nativeint decl_abstr ( | |
+@@ -198,7 +217,8 @@ let common_initial_env add_type add_extension empty_env = | |
+ add_type ident_string decl_abstr ( | |
+ add_type ident_char decl_abstr ( | |
+ add_type ident_int decl_abstr ( | |
+- empty_env)))))))))))))))))))))))))) | |
++ empty_env)))))))))))))))))))))))))) ) (* NNN extra parenthesis *) | |
++ | |
+ | |
+ let build_initial_env add_type add_exception empty_env = | |
+ let common = common_initial_env add_type add_exception empty_env in | |
+diff --git a/typing/predef.mli b/typing/predef.mli | |
+index 9723671..29f79d6 100644 | |
+--- a/typing/predef.mli | |
++++ b/typing/predef.mli | |
+@@ -29,6 +29,7 @@ val type_nativeint: type_expr | |
+ val type_int32: type_expr | |
+ val type_int64: type_expr | |
+ val type_lazy_t: type_expr -> type_expr | |
++val type_code: type_expr -> type_expr (* NNN *) | |
+ | |
+ val path_int: Path.t | |
+ val path_char: Path.t | |
+@@ -45,6 +46,7 @@ val path_nativeint: Path.t | |
+ val path_int32: Path.t | |
+ val path_int64: Path.t | |
+ val path_lazy_t: Path.t | |
++val path_code: Path.t (* NNN *) | |
+ | |
+ val path_match_failure: Path.t | |
+ val path_assert_failure : Path.t | |
+diff --git a/typing/typecore.ml b/typing/typecore.ml | |
+index b173d99..bbbbeef 100644 | |
+--- a/typing/typecore.ml | |
++++ b/typing/typecore.ml | |
+@@ -93,6 +93,91 @@ let type_object = | |
+ Env.t -> Location.t -> Parsetree.class_structure -> | |
+ Typedtree.class_structure * Types.class_signature * string list) | |
+ | |
++(* NNN: begin | |
++ The current stage level. | |
++ Type-checking the body of a bracket increases the level, | |
++ type-checking of an escape decreases. | |
++ Be sure to reset upon any exception; | |
++ alternatively; reset when beginning a new type-level | |
++ expression or binding | |
++ (whenever you do Typetexp.reset_type_variables();) | |
++ | |
++ Check all instances of Env.add_value and make sure that | |
++ we record the stage of every identifier that is added to the | |
++ value env (unless the stage is 0). | |
++also check all val_attributes and Val_reg | |
++*) | |
++let global_stage : Trx.stage ref = ref 0 | |
++ | |
++(* Obsolete; kept for reference | |
++ | |
++ The list of active classifiers. The length of the list | |
++ is the level of an expression. | |
++ Type-checking the body of a bracket adds a type variable | |
++ to the list; type-checking of an escape removes the | |
++ top-most classifier. | |
++ Be sure to reset this list upon any exception; | |
++ alternatively; reset the list when beginning a new type-level | |
++ expression or binding | |
++ (whenever you do Typetexp.reset_type_variables();) | |
++ | |
++let global_stage : Env.stage ref = ref [] | |
++ | |
++ Unify classifier lists, *right-to-left* | |
++ See the bug Tue Jan 20 12:18:00 GMTST 2004 in XXCC-BUG-OPEN-FIXED | |
++ why we need this order. | |
++ The current classifier is left-most, and the lists don't have | |
++ to have the same length. | |
++ Example: | |
++ .<fun x -> .< x >. >. | |
++ When type-checking the innermost bracket, the global_stage | |
++ will contain ['b,'a] and the level of x will be ['a] | |
++ The unification will succeed, without changing anything, as expected. | |
++ | |
++let unify_stage env tl1 tl2 = | |
++ let rec loop = function | |
++ | (t1::tl1,t2::tl2) -> unify env t1 t2; loop (tl1,tl2) | |
++ | _ -> () | |
++ in loop (List.rev tl1, List.rev tl2) | |
++*) | |
++ | |
++(* This function does not take the env argument. Normally env affects | |
++ the printing of paths (search for raise_wrong_stage_error | |
++ in this file and printtyp.ml). | |
++ The particular error message we emit here does not use paths. | |
++*) | |
++let raise_wrong_stage_error loc n m = | |
++ raise @@ | |
++ Error_forward(Location.errorf ~loc | |
++ "Wrong level: variable bound at level %d and used at level %d" n m) | |
++ | |
++let raise_unsupported loc txt = | |
++ raise @@ | |
++ Error_forward(Location.errorf ~loc | |
++ "Not supported within brackets: %s" txt) | |
++ | |
++let with_stage_up body = | |
++ let old_stage = !global_stage in | |
++ let () = incr global_stage in | |
++ try | |
++ let r = body () in | |
++ global_stage := old_stage; r | |
++ with e -> | |
++ global_stage := old_stage; raise e | |
++ | |
++let with_stage_down loc env body = | |
++ let old_stage = !global_stage in | |
++ if !global_stage = 0 then | |
++ raise @@ Error_forward(Location.errorf ~loc | |
++ "Wrong level: escape at level 0"); | |
++ decr global_stage; | |
++ try | |
++ let r = body () in | |
++ global_stage := old_stage; r | |
++ with e -> | |
++ global_stage := old_stage; raise e | |
++(* NNN end *) | |
++ | |
+ (* | |
+ Saving and outputting type information. | |
+ We keep these function names short, because they have to be | |
+@@ -1258,7 +1343,7 @@ let add_pattern_variables ?check ?check_as env = | |
+ let check = if as_var then check_as else check in | |
+ Env.add_value ?check id | |
+ {val_type = ty; val_kind = Val_reg; Types.val_loc = loc; | |
+- val_attributes = []; | |
++ val_attributes = [Trx.attr_level !global_stage] (* NNN *) | |
+ } env | |
+ ) | |
+ pv env, | |
+@@ -1301,7 +1386,8 @@ let type_class_arg_pattern cl_num val_env met_env l spat = | |
+ ((id', name, id, ty)::pv, | |
+ Env.add_value id' {val_type = ty; | |
+ val_kind = Val_ivar (Immutable, cl_num); | |
+- val_attributes = []; | |
++ val_attributes = | |
++ [Trx.attr_level !global_stage]; (* NNN *) | |
+ Types.val_loc = loc; | |
+ } ~check | |
+ env)) | |
+@@ -1325,6 +1411,10 @@ let type_self_pattern cl_num privty val_env met_env par_env spat = | |
+ let pv = !pattern_variables in | |
+ pattern_variables := []; | |
+ let (val_env, met_env, par_env) = | |
++ (* NNN we don't record stage for | |
++ Env.add_value below | |
++ since we don't handle classes within brackets. | |
++ *) | |
+ List.fold_right | |
+ (fun (id, ty, name, loc, as_var) (val_env, met_env, par_env) -> | |
+ (Env.add_value id {val_type = ty; | |
+@@ -1377,6 +1467,7 @@ let rec final_subexpression sexp = | |
+ (* Generalization criterion for expressions *) | |
+ | |
+ let rec is_nonexpansive exp = | |
++ List.memq Trx.attr_nonexpansive exp.exp_attributes || (* NNN *) | |
+ match exp.exp_desc with | |
+ Texp_ident(_,_,_) -> true | |
+ | Texp_constant _ -> true | |
+@@ -1491,7 +1582,14 @@ let rec approx_type env sty = | |
+ approx_type env sty | |
+ | _ -> newvar () | |
+ | |
+-let rec type_approx env sexp = | |
++let rec type_approx env sexp = (* NNN the whole function *) | |
++ let open Trx in | |
++ match what_stage_attr sexp.pexp_attributes with | |
++ | Stage0 -> type_approx_orig env sexp | |
++ (* instance env @@ Predef.type_code @@ type_approx_orig env sexp *) | |
++ | _ -> newvar () | |
++and | |
++ type_approx_orig env sexp = (* NNN end *) | |
+ match sexp.pexp_desc with | |
+ Pexp_let (_, _, e) -> type_approx env e | |
+ | Pexp_fun (p, _, _, e) when is_optional p -> | |
+@@ -1702,6 +1800,11 @@ let duplicate_ident_types loc caselist env = | |
+ I don't think this is what we want *) | |
+ let (path, desc) = Env.lookup_value (Longident.Lident s) env in | |
+ match path with | |
++ (* NNN The code below uses the existing desc (value-descriptor) | |
++ as the template and simply changes val_type. | |
++ The attributes, specifically staging level attribute, | |
++ are preserved then. | |
++ *) | |
+ Path.Pident id -> | |
+ let desc = {desc with val_type = correct_levels desc.val_type} in | |
+ Env.add_value id desc env | |
+@@ -1736,8 +1839,66 @@ and type_expect ?in_function env sexp ty_expected = | |
+ (Cmt_format.Partial_expression exp :: previous_saved_types); | |
+ exp | |
+ | |
++(* NNN This whole function type_expect_ *) | |
++(* Type checking staging constructs *) | |
++(* If we are type-checking bracket at level 0, don't build the | |
++ bracket Texp node. Rather, invoke trx_bracket to translate | |
++ the bracket body and convert it to the c ode generator. | |
++*) | |
+ and type_expect_ ?in_function env sexp ty_expected = | |
+ let loc = sexp.pexp_loc in | |
++ let open Trx in | |
++ (* Keep in mind that there may be several metaocaml attributes, | |
++ and their order matters. | |
++ *) | |
++ match what_stage_attr sexp.pexp_attributes with | |
++ | Stage0 -> type_expect_orig ?in_function env sexp ty_expected | |
++ | Bracket(battr,attrs) -> | |
++ (* Typechecking bracket *) | |
++ (* follow Pexp_array or Pexp_lazy as a template *) | |
++ (* Expected type: ty code where ty is the type | |
++ of the expression within brackets. | |
++ *) | |
++ let ty = newgenvar() in (* expected type for the bracketed sexp *) | |
++ let to_unify = Predef.type_code ty in | |
++ unify_exp_types loc env to_unify ty_expected; | |
++ let exp = | |
++ with_stage_up (fun () -> | |
++ (* drop bracket attr *) | |
++ let sexp = {sexp with pexp_attributes = attrs} in | |
++ type_expect env sexp ty) in | |
++ re @@ | |
++ if !global_stage = 0 then | |
++ (* Check if the expression non-expansive before the translation *) | |
++ let nonexp = is_nonexpansive exp in | |
++ let exp = trx_bracket 1 exp in | |
++ {exp with exp_type = instance env ty_expected; | |
++ exp_attributes = | |
++ if nonexp then attr_nonexpansive :: exp.exp_attributes | |
++ else exp.exp_attributes} | |
++ else | |
++ texp_braesc battr exp env (instance env ty_expected) | |
++ | |
++ (* NNN: Typechecking escapes *) | |
++ (* If ~e is expected to have the type ty then | |
++ e is expected to have the type ty code | |
++ *) | |
++ | Escape(battr,attrs) -> | |
++ with_stage_down loc env (fun () -> | |
++ let sexp_ty_expected = Predef.type_code ty_expected in | |
++ let sexp = {sexp with pexp_attributes = attrs} in (* drop bracket attr *) | |
++ let exp = type_expect env sexp sexp_ty_expected in | |
++ re @@ | |
++ texp_braesc battr exp env (instance env ty_expected)) | |
++ | |
++ (* There is nothing special in type-checking CSPs. | |
++ After lifting, a CSP value becomes an ordinaru expression. | |
++ *) | |
++ | _ -> type_expect_orig ?in_function env sexp ty_expected | |
++ (* NNN end *) | |
++ | |
++and type_expect_orig ?in_function env sexp ty_expected = (* NNN *) | |
++ let loc = sexp.pexp_loc in | |
+ (* Record the expression type before unifying it with the expected type *) | |
+ let rue exp = | |
+ unify_exp env (re exp) (instance env ty_expected); | |
+@@ -1756,6 +1917,7 @@ and type_expect_ ?in_function env sexp ty_expected = | |
+ let name = Path.name ~paren:Oprint.parenthesized_ident path in | |
+ Stypes.record (Stypes.An_ident (loc, name, annot)) | |
+ end; | |
++ let stage = Trx.get_level desc.val_attributes in (* NNN *) | |
+ rue { | |
+ exp_desc = | |
+ begin match desc.val_kind with | |
+@@ -1779,9 +1941,13 @@ and type_expect_ ?in_function env sexp ty_expected = | |
+ Env.add_required_global (Path.head p); | |
+ Texp_ident(path, lid, desc)*) | |
+ | _ -> | |
++ if stage > !global_stage then (* NNN *) | |
++ raise_wrong_stage_error loc stage !global_stage (* NNN *) | |
++ else (* NNN *) | |
+ Texp_ident(path, lid, desc) | |
+ end; | |
+ exp_loc = loc; exp_extra = []; | |
++(* NNN: Instantiates type scheme to a type *) | |
+ exp_type = instance env desc.val_type; | |
+ exp_attributes = sexp.pexp_attributes; | |
+ exp_env = env } | |
+@@ -1875,9 +2041,9 @@ and type_expect_ ?in_function env sexp ty_expected = | |
+ in | |
+ type_expect ?in_function env sfun ty_expected | |
+ (* TODO: keep attributes, call type_function directly *) | |
+- | Pexp_fun (l, None, spat, sexp) -> | |
++ | Pexp_fun (l, None, spat, sexp_body) -> (* NNN fixing the bug: sexp_body *) | |
+ type_function ?in_function loc sexp.pexp_attributes env ty_expected | |
+- l [{pc_lhs=spat; pc_guard=None; pc_rhs=sexp}] | |
++ l [{pc_lhs=spat; pc_guard=None; pc_rhs=sexp_body}] | |
+ | Pexp_function caselist -> | |
+ type_function ?in_function | |
+ loc sexp.pexp_attributes env ty_expected "" caselist | |
+@@ -2195,7 +2361,8 @@ and type_expect_ ?in_function env sexp ty_expected = | |
+ | Ppat_any -> Ident.create "_for", env | |
+ | Ppat_var {txt} -> | |
+ Env.enter_value txt {val_type = instance_def Predef.type_int; | |
+- val_attributes = []; | |
++ val_attributes = (* NNN *) | |
++ [Trx.attr_level !global_stage]; | |
+ val_kind = Val_reg; Types.val_loc = loc; } env | |
+ ~check:(fun s -> Warnings.Unused_for_index s) | |
+ | _ -> | |
+@@ -2357,7 +2524,8 @@ and type_expect_ ?in_function env sexp ty_expected = | |
+ Texp_ident(Path.Pident method_id, lid, | |
+ {val_type = method_type; | |
+ val_kind = Val_reg; | |
+- val_attributes = []; | |
++ val_attributes = (* NNN *) | |
++ [Trx.attr_level !global_stage]; | |
+ Types.val_loc = Location.none}); | |
+ exp_loc = loc; exp_extra = []; | |
+ exp_type = method_type; | |
+@@ -2428,6 +2596,8 @@ and type_expect_ ?in_function env sexp ty_expected = | |
+ exp_env = env } | |
+ end | |
+ | Pexp_setinstvar (lab, snewval) -> | |
++ if !global_stage != 0 then (* NNN *) | |
++ raise_unsupported loc "setinstvar"; (* NNN *) | |
+ begin try | |
+ let (path, desc) = Env.lookup_value (Longident.Lident lab.txt) env in | |
+ match desc.val_kind with | |
+@@ -2452,6 +2622,8 @@ and type_expect_ ?in_function env sexp ty_expected = | |
+ raise(Error(loc, env, Unbound_instance_variable lab.txt)) | |
+ end | |
+ | Pexp_override lst -> | |
++ if !global_stage != 0 then (* NNN *) | |
++ raise_unsupported loc "override"; (* NNN *) | |
+ let _ = | |
+ List.fold_right | |
+ (fun (lab, _) l -> | |
+@@ -2490,6 +2662,8 @@ and type_expect_ ?in_function env sexp ty_expected = | |
+ assert false | |
+ end | |
+ | Pexp_letmodule(name, smodl, sbody) -> | |
++ if !global_stage != 0 then (* NNN *) | |
++ raise_unsupported loc "letmodule"; (* NNN *) | |
+ let ty = newvar() in | |
+ (* remember original level *) | |
+ begin_def (); | |
+@@ -3094,7 +3268,7 @@ and type_argument env sarg ty_expected' ty_expected = | |
+ exp_desc = | |
+ Texp_ident(Path.Pident id, mknoloc (Longident.Lident name), | |
+ {val_type = ty; val_kind = Val_reg; | |
+- val_attributes = []; | |
++ val_attributes = [Trx.attr_level !global_stage]; (* NNN *) | |
+ Types.val_loc = Location.none})} | |
+ in | |
+ let eta_pat, eta_var = var_pair "eta" ty_arg in | |
+@@ -3722,6 +3896,7 @@ and type_let ?(check = fun s -> Warnings.Unused_var s) | |
+ | |
+ let type_binding env rec_flag spat_sexp_list scope = | |
+ Typetexp.reset_type_variables(); | |
++ global_stage := 0; (* NNN *) | |
+ let (pat_exp_list, new_env, unpacks) = | |
+ type_let | |
+ ~check:(fun s -> Warnings.Unused_value_declaration s) | |
+@@ -3739,17 +3914,34 @@ let type_let env rec_flag spat_sexp_list scope = | |
+ | |
+ let type_expression env sexp = | |
+ Typetexp.reset_type_variables(); | |
++ global_stage := 0; (* NNN *) | |
+ begin_def(); | |
+ let exp = type_exp env sexp in | |
+ end_def(); | |
+ if is_nonexpansive exp then generalize exp.exp_type | |
+ else generalize_expansive env exp.exp_type; | |
++ (* NNN The original code | |
+ match sexp.pexp_desc with | |
+ Pexp_ident lid -> | |
+ (* Special case for keeping type variables when looking-up a variable *) | |
+ let (path, desc) = Env.lookup_value lid.txt env in | |
+ {exp with exp_type = desc.val_type} | |
+ | _ -> exp | |
++ We have to modify it since <x> is also Pexp_ident, with the additional | |
++ attribute though. So, either we have to check for metaocaml.bracket | |
++ attribute, or, better, check exp. After type-checking, <x> is no longer | |
++ Pexp_ident. For ordinary identifiers though, Pexp_ident li maps to | |
++ Texp_ident (..,li,..) -- with the exception of instance vars, which | |
++ don't matter at the ttoplevel anyway. | |
++*) | |
++ (* NNN new code *) | |
++ match exp.exp_desc with | |
++ Texp_ident (_,lid,_) -> | |
++ (* Special case for keeping type variables when looking-up a variable *) | |
++ let (path, desc) = Env.lookup_value lid.txt env in | |
++ {exp with exp_type = desc.val_type} | |
++ | _ -> exp | |
++ (* NNN end *) | |
+ | |
+ (* Error report *) | |
+ | |
diff -Naur ocaml-4.02.1/metalib/print_code.ml ocaml-ber-n102/metalib/print_code.ml | |
--- ocaml-4.02.1/metalib/print_code.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/print_code.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,43 @@ | |
+(* Printing code expressions *) | |
+ | |
+(* The original code was authored by Ed Pizzi | |
+ and simplified by Jacques Carette. | |
+ It is latter borrowed into the main OCaml tree, | |
+ as parsing/pprintast.ml. | |
+ It was extensively rewritten by Hongbo Zhang: University of Pennsylvania | |
+ and modified by Thomas Gazagnaire (OCamlPro) and | |
+ Fabrice Le Fessant (INRIA Saclay). | |
+ | |
+ We now rely on the OCaml's code. | |
+*) | |
+ | |
+open Format | |
+open Runcode | |
+ | |
+ | |
+(* print code as a parse tree. Useful for debugging *) | |
+let print_code_as_ast cde = | |
+ let cde = (cde : Trx.closed_code_repr :> Parsetree.expression) in | |
+ Printast.implementation Format.std_formatter | |
+ [Ast_helper.Str.eval cde] | |
+ | |
+let format_code : Format.formatter -> 'a closed_code -> unit = fun ppf cde -> | |
+ let cde = (cde : Trx.closed_code_repr :> Parsetree.expression) in | |
+ Pprintast.expression ppf cde | |
+ | |
+(* These functions are suitable for installing as printers | |
+ at the toplevel, using top-level directive install printer. | |
+ Don't rename these functions or change their types. | |
+ See bertop.ml, which refers to these functions by their external | |
+ symbolic name. | |
+*) | |
+ | |
+let print_closed_code : Format.formatter -> 'a closed_code -> unit = | |
+ fun ppf cde -> | |
+ Format.fprintf ppf ".<@,%a>.@ " format_code cde | |
+ | |
+let print_code ppf (cde : 'a code) = | |
+ let (cde, check) = close_code_delay_check cde in | |
+ print_closed_code ppf cde; | |
+ try check () | |
+ with e -> fprintf ppf "\n%s" (Printexc.to_string e) | |
diff -Naur ocaml-4.02.1/metalib/print_code.mli ocaml-ber-n102/metalib/print_code.mli | |
--- ocaml-4.02.1/metalib/print_code.mli 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/print_code.mli 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,18 @@ | |
+(* Printing code expressions *) | |
+ | |
+open Runcode | |
+ | |
+(* Print code values, useful as formatter. | |
+ The code is printed with outer brackets | |
+*) | |
+val print_code : Format.formatter -> 'a code -> unit | |
+val print_closed_code : Format.formatter -> 'a closed_code -> unit | |
+ | |
+(* Like print_closed_code, but omit the outer brackets. | |
+ This function is useful when saving the generated code into a file, | |
+ to compile later. | |
+*) | |
+val format_code : Format.formatter -> 'a closed_code -> unit | |
+ | |
+(* print code as a parse tree. Useful for debugging *) | |
+val print_code_as_ast : 'a closed_code -> unit | |
diff -Naur ocaml-4.02.1/metalib/Problems.txt ocaml-ber-n102/metalib/Problems.txt | |
--- ocaml-4.02.1/metalib/Problems.txt 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/Problems.txt 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,208 @@ | |
+ Open and closed problems | |
+ | |
+June 11, 2010. | |
+ | |
+While designing a counter-example, I was too | |
+successful. The counter-example worked, also for OCaml | |
+ | |
+let lift x = .<x>.;; | |
+let fff = | |
+ .<let foo = fun x -> let t = .~(lift (ref [])) in | |
+ (match !t with [] -> t := [x]; x | [y] -> t := [x]; y) in | |
+ (foo ("xxx"), foo(true), foo([1,2]))>.;; | |
+ | |
+So, the future-stage lambda should not be considered unexpansive by | |
+default. We should check the body for escapes. In principle, | |
+we should also check for CSP -- however MetaOCaml, unlike the | |
+calculus of PEPM03 paper, permits CSP of only values rather than | |
+expressions. And we know that we can't have a polymorphic reference | |
+cell bound to an identifier or as a constant. | |
+So, we should declare expansive those future-stage lambda whose | |
+bodies contain expansive escapes (that is, escapes that are not | |
+manifest code constants such as <0>). We should be careful of multiple-levels | |
+though; the problem above occurs because ``polymorphic'' reference cell can | |
+be a value (once created), and can be lifted to code. So, we should | |
+conservatively regard as potentially expansive a future-stage lambda | |
+that has expansive escapes to any level. | |
+ | |
+ | |
+ | |
+May 5, 2010 | |
+Loading test/test21.ml in top-level works fine. However, compiling | |
+that file gives | |
+ Fatal error: exception Invalid_argument("output_value: functional value") | |
+ | |
+If we pass the flags -drawlambda -dinstr to the compiler, we observe | |
+that the constants "" and Pervasive.^ are accompanied with | |
+environments (which are probably useless in that case anyway). | |
+If the code file has nested modules, it seems the environemnt has | |
+functional values. Therefore, attempting to serialize such an | |
+environment fails. | |
+ | |
+The permanent solution requires re-thinking of building parsetree at run-time | |
+(see quote_constant in trx.ml) | |
+We shouldn't probably attach environemnt to contants. Also, we should | |
+get rid of location information, which is not needed and can be | |
+confusing. | |
+ | |
+ | |
+It turns out that the environment indeed contains functional values. | |
+The patch from Fabrice Le Fessant (who faced a similar problem in | |
+JoCaml) helped. | |
+ | |
+ | |
+ | |
+-- Older | |
+ | |
+ | |
+ | |
+(* CSP issues *) | |
+let f = fun x -> .<x>.;; | |
+let a = .<fun y -> .~(f .<y>.)>.;; | |
+let b = .! a;; | |
+let c = b 56;; | |
+let d = .! c;; | |
+ | |
+let f = fun x -> .<x>.;; | |
+let a = .<fun y -> .~(f .<.<y>.>.)>.;; | |
+let b = .! a;; | |
+let c = b 56;; | |
+let d = .! c;; | |
+ | |
+let f = fun x -> .<x>.;; | |
+let a = .<fun y -> f y>.;; | |
+let f2 = .! a;; | |
+let a2 = .<fun y -> .~(f2 .<.<y>.>.)>.;; | |
+let b = .! a2;; | |
+let c = b 56;; | |
+let d = .! c;; | |
+ | |
+ | |
+(* Type synonyms/aliasing bug: *) | |
+-- Description: Pattern matching against a value whose type is a type | |
+synonym, while using the pattern alias (as s) results in a | |
+type-checking error when applying .! to a piece of code. | |
+ | |
+-- The smallest example of this bug is: | |
+type vect = int * int;; | |
+type state = State of vect;; | |
+let f state = .<let (State d) as s = .~state in | |
+ let (u,v) = d in 34>.;; | |
+let c = f .<State (2,3)>. in .! c;; | |
+ | |
+-- error: "This expression has type vect but is here used with type 'a * 'b" | |
+ | |
+-- The piece of code where this exception is raised is the call to | |
+(type_expect) in the function type_let in typecore.ml. | |
+ | |
+-- Suspected reasons: The type synonym 'vect' is not unified with 'a * | |
+'b, which it should be. The problem seems the way that we handle | |
+environments. When type-checking run, the (initial) environment in | |
+which it is type-checked does not seem to contain a binding for the | |
+type vect. | |
+ | |
+-- General note: This problem seems to be an artifact of how we | |
+ implement environments. Perhaps we need to deal with these things | |
+ more systematically. | |
+ | |
+ | |
+Oct 3, 2006 | |
+ printing of `record with expressions' | |
+ type recd = {f1 : int; f2 : string};; | |
+ | |
+ let foo = .<let x = {f1=1; f2="a"} in | |
+ let y = {x with f1 = 2} in | |
+ y>. | |
+ ;; | |
+ | |
+ produces the output | |
+ val foo : ('a, recd) code = | |
+ .<let x_3 = {f1 = 1; f2 = "a"} in let y_4 = {f1 = 2} (x_3) in y_4>. | |
+ | |
+ The expression "{f1 = 2} (x_3)" obviously can't be right: a record is | |
+ not a function and can't be applied. | |
+ | |
+ That is merely a printing issue: | |
+ .! foo;; | |
+ - : recd = {f1 = 2; f2 = "a"} | |
+ | |
+ which is correct. | |
+ | |
+ | |
+Tue Jan 18 14:08:52 GMTST 2005 | |
+ BUG: type aliases are not handled correctly in code, example: | |
+ # type a = int;; | |
+ # let f (x:a) = 1;; | |
+ # .! .<f 1>.;; | |
+ This expression has type int but is here used with type a | |
+ PROBLEM: the type of csp constants cannot resolve type aliases | |
+ FIX: when typing Pexp_cspval return a fresh variable so it will type | |
+ check in any situation | |
+ | |
+Tue Jan 11 11:19:23 GMTST 2005 | |
+ BUG: records, constructors etc are typed once and their type is reused, same | |
+ bug as on Jan 10 2005. | |
+ FIXED: when typing e.g. a record field access (Pexp_field) the stored type | |
+ is still used to get the label description (used by the compiler to compute | |
+ the offset for field access), but the toplevel type is not reused and a | |
+ fresh type variable is returned instead. In this way we avoid that several | |
+ runs of code coming from the same field access have their types unified. | |
+ | |
+Mon Jan 10 18:51:21 GMTST 2005 | |
+ BUG: csp constants in Pervasives (and similar) are type checked only once for | |
+ a given occurrence. | |
+ # let f x = .< ref .~ x>. | |
+ in (.! (f .<3>.), .! (f .<1.3>.));; | |
+ This expression has type int but is here used with type float | |
+ Exception: Trx.TypeCheckingError. | |
+ PROBLEM: "ref" is typechecked only once when f is declared, and the resulting | |
+ type is used twice in the second line | |
+ FIXED: typing/typecore.ml now re-createx the untyped parsetree for the | |
+ Pervasive identifier and type-checks it again. | |
+ | |
+Tue Jan 20 12:18:00 GMTST 2004 | |
+ BUG: typechecker broken for csp ids, e.g. we get the wrong type | |
+ We get the incorrect typing (inner and outer code forced to be both 'a) | |
+ # .<fun x -> .<x>.>.;; | |
+ - : ('a, 'b -> ('a, 'b) code) code = .<fun (x_2) -> .<(x_2)>.>. | |
+ PROBLEM: typechecker unifies the current level with the id's level left-to-right | |
+ but the latest classifier is the leftmost. | |
+ FIXED: levels are now unified right-to-left, and get correct type: | |
+ # .<fun x -> .<x>.>.;; | |
+ - : ('a, 'b -> ('c, 'b) code) code = .<fun (x_1) -> .<(x_1)>.>. | |
+ | |
+Fri May 16 14:54:22 BST 2003 | |
+ BUG: standalone bytecode compiling does not work with tag elimination | |
+ e.g. ./ocamlc toplevel/toplevellib.cma mex/evaletag.mk; ./a.out | |
+ PROBLEM: Tooploop.etag gets a typing error because timestamp of the code is in | |
+ the future (similar problem in the past with run) | |
+ FIXED: current timestamp bumped with the timestamp of the env passed to etag | |
+ | |
+Mon Nov 25 18:24:28 GMT 2002 | |
+ BUG: error typecore.ml: Texp_construct expected | |
+ PROBLEM: somehow typechecked made restrictive assuptions on the contents of the pexp_ext field for the case pPexp_construct | |
+ FIXED: assumptions relaxed | |
+ | |
+Mon Nov 25 10:10:32 GMT 2002 | |
+ BUG: csp of array ops gives internal errors | |
+ PROBLEM: arrays are treated in a special way after typechecking phase, so replacing an op with a csp val of the op changes the semantics. Use ocamlc -dlambda to see how array ops are treated internally. | |
+ FIXED: csp of array is rebuilt, like a Pervasives, so it's type-checked again, and the second phase of the compiler is happy. | |
+ | |
+Thu Oct 24 09:55:36 BST 2002 | |
+ BUG: csp at level n+2 gives Segmentation fault | |
+ PROBLEM: now Trx.mkcsp takes 2 args and not one, forgot to change cases | |
+ instvar and cspval at level n+1 accordingly | |
+ FIXED: cases instvar and cspvar at level n+1 now pass a second argument (None) | |
+ | |
+Wed Oct 2 08:39:04 BST 2002 | |
+ BUG: occurrences of a csp value share the same instantiated type | |
+ APPEARED: when treating Pervasives.* in a special way for performance. The | |
+ type of e.g. Pervasives.! is recorded in the extra field as an instantiated | |
+ type, so applications of the value cause unification. | |
+ BACKGROUND: values' types are closed schemas (all the identifiers are generic). | |
+ During type checking the types are instantiated. Instantiation is idempotent, | |
+ so one should always carry the schema somewhere. A typed expression exp such | |
+ that exp.exp_desc = Texp_ident (ident,value_description) has an instantiated | |
+ type in exp.exp_type and the type schema in value_description.val_type. | |
+ FIXED: Trx.trx creates a new instantiation for csp idents; Typecore.type_exp | |
+ re-instantiates the type schema when type-checking a cspval | |
diff -Naur ocaml-4.02.1/metalib/README ocaml-ber-n102/metalib/README | |
--- ocaml-4.02.1/metalib/README 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/README 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,152 @@ | |
+ BER MetaOCaml | |
+ N102 December 2014 | |
+ | |
+0. TABLE OF CONTENTS | |
+ | |
+ 1 Copyright | |
+ 2 Installation | |
+ 3 Staging constructs | |
+ 4 Restrictions and Common Problems | |
+ 5 Detailed Discussion | |
+ 6 Credits | |
+ | |
+1. COPYRIGHT | |
+ | |
+BER MetaOCaml is a complete re-implementation of MetaOCaml, | |
+which was licensed according to ORIGINAL-LICENSE-META | |
+ | |
+ | |
+2. INSTALLATION | |
+ | |
+ Please see the file INSTALL | |
+ | |
+ | |
+3. STAGING CONSTRUCTS | |
+ (This is a modified version of the section from the last | |
+ release of the original MetaOCaml, Feb 3, 2006) | |
+ | |
+ The three new constructs are | |
+ | |
+ bracket: .< e >. to delay computation (to the future stage) | |
+ escape: .~ e to perform a computation within brackets | |
+ and splice-in the result | |
+ run: !. e to run a future-stage computation, or code, now | |
+ | |
+ A special type constructor, called 'code' builds the type of | |
+ future-stage computations, or code expressions. For example, | |
+ | |
+ # .< 2 + 4 >.;; | |
+ - : int code = .<2 + 4>. | |
+ | |
+ The type constructor 'code' takes as its argument the type of the | |
+ future-stage expression. Future-stage expressions are executed later, | |
+ but are type-checked now. Therefore, the generated code is assuredly | |
+ well-typed. | |
+ | |
+ Code fragments can be spliced into larger code contexts by using the | |
+ escape construct: | |
+ | |
+ # let x = .< 2 + 4 >. in .< .~ x + .~ x >. ;; | |
+ - : int code = .<(2 + 4) + (2 + 4)>. | |
+ | |
+ The escape construct takes an expression of type (t code) and | |
+ produces an expression of type t, but only inside of a | |
+ code-constructing context (i.e., inside inside code brackets). | |
+ Attempting to escape code outside of a code-building context results | |
+ in the following type error: | |
+ | |
+ # .~ .< 2 + 3 >.;; | |
+ Characters 0-14: | |
+ .~ .< 2 + 3 >.;; | |
+ ^^^^^^^^^^^^^^ | |
+ Wrong level: escape at level 0 | |
+ | |
+ | |
+ The run construct takes a code value, executes it and returns its result. | |
+ It is actually an ordinary function Runcode.run, which is also bound | |
+ to the prefix operation (!.). These operations are in the module | |
+ Runcode (which is not opened by default). For example: | |
+ | |
+ # Runcode.run .< 2 + 3 >.;; | |
+ - : int = 5 | |
+ # open Runcode;; | |
+ # !. .<fun x y -> x + y >. 2 3;; | |
+ - : int = 5 | |
+ | |
+ The run construct only works on closed code values. Attempting to run | |
+ open code leads to an exception in the generator (which can be traced | |
+ as any other exception). | |
+ | |
+ # .< fun x -> .~ (let u = !. .< x >. in .<()>.) >.;; | |
+ Exception: | |
+ Failure | |
+ "The code built at Characters 7-8: | |
+ .< fun x -> .~ (let u = !. .< x >. in .<()>.) >.;; | |
+ ^ | |
+ is not closed: identifier x_91 bound at Characters 7-8: | |
+ .< fun x -> .~ (let u = !. .< x >. in .<()>.) >.;; | |
+ ^ | |
+ is free". | |
+ | |
+ Please see many more examples in test/trivial.ml and other files | |
+ in the test/ directory. | |
+ | |
+ | |
+4. RESTRICTIONS AND COMMON PROBLEMS | |
+ | |
+ 1. All data constructors and record labels used within brackets must | |
+ come from the types that are declared in separately compiled modules. | |
+ For example, the following works: | |
+ .<true>. (* data constructor is Pervasive *) | |
+ .<Some [1]>. (* ditto *) | |
+ .<{Complex.re = 1.0; im = 2.0}>. (* The Record Complex is defined *) | |
+ (* in the standard library. *) | |
+ open Complex (* which is separately complied *) | |
+ .<{re = 1.0; im = 2.0}>. | |
+ | |
+ But the following are not allowed and flagged as compile-time error: | |
+ | |
+ type foo = Bar | |
+ .<Bar>. | |
+ | |
+ module Foo = struct exception E end | |
+ .<raise Foo.E>. | |
+ | |
+ The type declaration foo or the module declaration Foo must be | |
+ moved into a separate file. The corresponding .cmi file must also be | |
+ available at run-time: either placed into the same directory as | |
+ the executable, or somewhere within the OCaml library search | |
+ path. | |
+ | |
+ 2. Escaped expressions at the same level evaluate in the same | |
+ order as arguments in an application. For byte-code OCaml, | |
+ this order is RIGHT to left. | |
+ | |
+ 3. Objects and modules are not supported within brackets | |
+ (but you can use brackets within modules or objects). | |
+ | |
+ | |
+5. DETAILED DISCUSSION | |
+ | |
+ For differences from the original MetaOCaml and further discussion, | |
+ see NOTES.txt. | |
+ | |
+ | |
+6. CREDITS | |
+ | |
+ BER MetaOCaml is a complete re-implementation of the original MetaOCaml. | |
+ | |
+ The original MetaOCaml was funded primarily by an NSF project titled: | |
+ | |
+ "ITR/SY(CISE): Putting Multi-Stage Annotations to Work" | |
+ | |
+ That project was led by Walid Taha. Most of the original development | |
+ and implementation of staging was done by Cristiano | |
+ Calcagno, then at Imperial College. | |
+ Edward Pizzi has implemented the pretty-printing of code -- which | |
+ since then has been extensively modified and maintained by | |
+ Jacques Carette. | |
+ Xavier Leroy, INRIA, helped with the compiler specifics. | |
+ | |
+ Many members of the metaocaml-users and metaocaml-hackers lists have | |
+ helped identify bugs and in some cases fixed them. | |
diff -Naur ocaml-4.02.1/metalib/reify_type.ml ocaml-ber-n102/metalib/reify_type.ml | |
--- ocaml-4.02.1/metalib/reify_type.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/reify_type.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,197 @@ | |
+(* Reifying (and printing) the type of a code expression *) | |
+(* This code illustrates type introspection facilities *) | |
+ | |
+#directory "../parsing";; | |
+#directory "../typing";; | |
+#directory "../toplevel";; | |
+#directory "../utils";; | |
+ | |
+(* This must be loaded first! It is stateful, and affects Predef *) | |
+#load "ident.cmo";; | |
+ | |
+(* Load the rest of the compiler *) | |
+#load "misc.cmo";; | |
+#load "path.cmo";; | |
+#load "types.cmo";; | |
+#load "btype.cmo";; | |
+#load "tbl.cmo";; | |
+#load "subst.cmo";; | |
+#load "predef.cmo";; | |
+#load "datarepr.cmo";; | |
+#load "config.cmo";; | |
+#load "consistbl.cmo";; | |
+#load "clflags.cmo";; | |
+#load "env.cmo";; | |
+#load "ctype.cmo";; | |
+#load "printast.cmo";; | |
+#load "oprint.cmo";; | |
+#load "primitive.cmo";; | |
+#load "printtyp.cmo";; | |
+ | |
+open Ident (* Just to make sure Ident is loaded first! *) | |
+open Types;; | |
+open Typedtree;; | |
+open Format | |
+;; | |
+ | |
+(* Obtain the information about the type and the typing environment *) | |
+let get_type_env = function | |
+ {Parsetree.pexp_ext = Some tr} -> | |
+ let t : Typedtree.expression = Obj.obj tr in | |
+ (t.Typedtree.exp_type, t.Typedtree.exp_env) | |
+ | _ -> failwith "get_type_env" | |
+;; | |
+ | |
+let describe_decl ppf env = function | |
+ | (Path.Pident id) as p -> | |
+ begin | |
+ try | |
+ let decl = Env.find_type p env in | |
+ Printtyp.type_declaration id ppf decl | |
+ with Not_found -> fprintf ppf "not found\n" | |
+ end | |
+ | _ -> fprintf ppf "not an ident" | |
+;; | |
+ | |
+let rec describe_type ppf env ty = | |
+ let ty = Btype.repr ty in | |
+ match ty.desc with | |
+ | Tvar -> fprintf ppf "Tvar\n" | |
+ | Tarrow(l, _, _, _) when not (l = "") -> | |
+ fprintf ppf "Labelled arrow\n" | |
+ | Tarrow(l, ty1, ty2, _) -> | |
+ fprintf ppf "Arrow:\n"; | |
+ describe_types ppf env [ty1;ty2] | |
+ | Ttuple tyl -> | |
+ fprintf ppf "Tuple:\n"; | |
+ describe_types ppf env tyl | |
+ | Tconstr(p, tyl, abbrev) -> | |
+ fprintf ppf "Constructor:\n"; | |
+ Printtyp.path ppf p; | |
+ describe_types ppf env tyl; | |
+ describe_decl ppf env p; | |
+ fprintf ppf "\n" | |
+ | Tvariant row -> | |
+ fprintf ppf "TVariant\n" | |
+ | Tobject (fi, nm) -> | |
+ fprintf ppf "Tobject\n" | |
+ | Tsubst ty -> | |
+ describe_type ppf env ty | |
+ | Tlink _ | Tnil | Tfield _ -> | |
+ failwith "describe_type" | |
+ | Tpoly (ty, []) -> | |
+ describe_type ppf env ty | |
+ | Tpoly (ty, tyl) -> | |
+ fprintf ppf "Tpoly\n" | |
+ | Tunivar -> | |
+ fprintf ppf "Tunivar\n" | |
+and describe_types ppf env tyl = | |
+ List.iter (describe_type ppf env) tyl; | |
+ fprintf ppf "\n" | |
+;; | |
+ | |
+let do_describe cde = | |
+ let (t,tenv) = get_type_env (Obj.magic cde) in | |
+ describe_type std_formatter tenv t;; | |
+ | |
+ | |
+(* tests *) | |
+ | |
+type t1 = A of int | B of bool list;; | |
+ | |
+let t1exp' () = (failwith "na" : t1);; | |
+ | |
+do_describe .<t1exp'>.;; | |
+ | |
+(* | |
+Arrow: | |
+Constructor: | |
+unit | |
+type unit = () | |
+Constructor: | |
+t1 | |
+type t1 = | |
+ A of int | |
+ | B of bool list | |
+*) | |
+ | |
+type 'a t2 = | A of t1 | B of 'a;; | |
+let t2exp' () = (failwith "na" : 'a t2);; | |
+ | |
+do_describe .<t2exp'>.;; | |
+ | |
+(* | |
+Arrow: | |
+Constructor: | |
+unit | |
+type unit = () | |
+Constructor: | |
+t2Tvar | |
+ | |
+type 'a t2 = | |
+ A of t1 | |
+ | B of 'a | |
+ | |
+*) | |
+ | |
+ | |
+(* | |
+module Units : sig | |
+ type 'a t | |
+ val to_feet : float -> [`Feet ] t | |
+ val to_meters : float -> [`Meters] t | |
+ val add : 'a t -> 'a t -> 'a t | |
+ val print : 'a t -> unit | |
+end = struct | |
+ type 'a t = float | |
+ let to_feet x=x | |
+ let to_meters x=x | |
+ let add x y = x +. y | |
+ let print (x : 'a t) = Printf.printf "%f (units)" x; | |
+ do_describe .<x>. | |
+end;; | |
+open Units;; | |
+ | |
+let test_unit = print (add (to_meters 1.) (to_meters 2.));; | |
+ | |
+module Units1 : sig | |
+ type 'a t | |
+ val to_feet : float -> [`Feet ] t | |
+ val to_meters : float -> [`Meters] t | |
+ val add : 'a t -> 'a t -> 'a t | |
+ val print : 'a t -> unit | |
+end = struct | |
+ type 'a t = float | |
+ let to_feet x=x | |
+ let to_meters x=x | |
+ let add x y = x +. y | |
+ let print (x : 'a t) = Printf.printf "%f (units)" x; | |
+ .! .<do_describe .<x>.>. | |
+end;; | |
+open Units1;; | |
+ | |
+let printx (x : 'a t) = .! .<do_describe .~(let v = .<x>. in .<v>.)>.;; | |
+let tt = printx (to_meters 1.);; | |
+ | |
+ | |
+let test_unit1 = print (add (to_meters 1.) (to_meters 2.));; | |
+ | |
+let x = to_meters 1. in do_describe .<x>.;; | |
+ | |
+type 'a ft = int;; | |
+type uf;; | |
+type um;; | |
+ | |
+ | |
+let printfx (x : 'a ft) = .! .<do_describe .~(let v = .<x>. in .<[v]>.)>.;; | |
+ | |
+let printfx (x : 'a ft) = let v = .<[x]>. in .! .<do_describe v>.;; | |
+let tt = printfx (1 : uf ft);; | |
+ | |
+let printfx (x : 'a ft) = let v1 = .<[x]>. in | |
+ do_describe ((.! v1));; | |
+ | |
+ | |
+let v = .<[x]>. in .! .<do_describe v>.;; | |
+ | |
+*) | |
diff -Naur ocaml-4.02.1/metalib/reify_type.mli ocaml-ber-n102/metalib/reify_type.mli | |
--- ocaml-4.02.1/metalib/reify_type.mli 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/reify_type.mli 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,5 @@ | |
+(* Reifying (and printing) the type of a code expression *) | |
+(* This code illustrates type introspection facilities *) | |
+ | |
+(* Print code values *) | |
+val describe_type_of : Format.formatter -> ('c,'a) code -> unit | |
diff -Naur ocaml-4.02.1/metalib/runcode.ml ocaml-ber-n102/metalib/runcode.ml | |
--- ocaml-4.02.1/metalib/runcode.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/runcode.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,161 @@ | |
+(* Run the closed code: byte-code and native code *) | |
+ | |
+open Format | |
+ | |
+type 'a closed_code = Trx.closed_code_repr | |
+ | |
+ | |
+(* Add a directory to search for .cmo/.cmi files, needed | |
+ for the sake of running the generated code . | |
+ The specified directory is prepended to the load_path. | |
+*) | |
+let add_search_path : string -> unit = fun dir -> | |
+ let dir = Misc.expand_directory Config.standard_library dir in | |
+ Config.load_path := dir :: !Config.load_path; | |
+ Dll.add_path [dir]; | |
+ Env.reset_cache () | |
+ | |
+ | |
+(* Check that the code is closed and return the closed code *) | |
+let close_code : 'a code -> 'a closed_code = fun cde -> | |
+ Trx.close_code_repr (Obj.magic cde) | |
+ | |
+(* The same as close_code but return the closedness check as a thunk | |
+ rather than performing it. | |
+ This is useful for debugging and for showing the code. | |
+*) | |
+let close_code_delay_check : 'a code -> 'a closed_code * (unit -> unit) = | |
+ fun cde -> Trx.close_code_delay_check (Obj.magic cde) | |
+ | |
+let open_code : 'a closed_code -> 'a code = fun ccde -> | |
+ Obj.magic (Trx.open_code ccde) | |
+ | |
+ | |
+(* Execute a thunk (which does compilation) while disabling certain | |
+ warnings. | |
+*) | |
+let warnings_descr = | |
+ [(Warnings.Partial_match "",("P","p")); | |
+ (Warnings.Unused_argument,("X","x")); | |
+ (Warnings.Unused_var "",("Y","y")); | |
+ (Warnings.Unused_var_strict "",("Z","z")) | |
+ ] | |
+ | |
+let with_disabled_warnings warnings thunk = | |
+ let disable_str = | |
+ String.concat "" | |
+ (List.map | |
+ (fun w -> snd (List.assoc w warnings_descr)) warnings) in | |
+(* | |
+ let curr_str = | |
+ String.concat "" | |
+ (List.map | |
+ (fun w -> | |
+ let state = Warnings.is_active w in | |
+ (if state then fst else snd) (List.assoc w warnings_descr)) | |
+ warnings) in | |
+*) | |
+ let warnings_old = Warnings.backup () in | |
+ let () = Warnings.parse_options false disable_str in | |
+ try | |
+ let r = thunk () in | |
+ Warnings.restore warnings_old; r | |
+ with e -> | |
+ Warnings.restore warnings_old; | |
+ raise e | |
+ | |
+ | |
+let initial_env = ref Env.empty | |
+ | |
+(* Load and execute bytecode: copied from toploop/toploop.ml *) | |
+let load_lambda ppf lam = | |
+ if !Clflags.dump_rawlambda then fprintf ppf "%a@." Printlambda.lambda lam; | |
+ let slam = Simplif.simplify_lambda lam in | |
+ if !Clflags.dump_lambda then fprintf ppf "%a@." Printlambda.lambda slam; | |
+ let (init_code, fun_code) = Bytegen.compile_phrase slam in | |
+ if !Clflags.dump_instr then | |
+ fprintf ppf "%a%a@." | |
+ Printinstr.instrlist init_code | |
+ Printinstr.instrlist fun_code; | |
+ let (code, code_size, reloc) = Emitcode.to_memory init_code fun_code in | |
+ let can_free = (fun_code = []) in | |
+ let initial_symtable = Symtable.current_state() in | |
+ Symtable.patch_object code reloc; | |
+ Symtable.check_global_initialized reloc; | |
+ Symtable.update_global_table(); | |
+ (* let initial_bindings = !toplevel_value_bindings in *) | |
+ try | |
+ Toploop.may_trace := true; | |
+ let retval = (Meta.reify_bytecode code code_size) () in | |
+ Toploop.may_trace := false; | |
+ if can_free then begin | |
+ Meta.static_release_bytecode code code_size; | |
+ Meta.static_free code; | |
+ end; | |
+ retval | |
+ with x -> | |
+ Toploop.may_trace := false; | |
+ if can_free then begin | |
+ Meta.static_release_bytecode code code_size; | |
+ Meta.static_free code; | |
+ end; | |
+ (* let initial_bindings = !toplevel_value_bindings in *) | |
+ Symtable.restore_state initial_symtable; | |
+ raise x | |
+ | |
+(* Patterned after toploop.ml:execute_phrase *) | |
+ | |
+let typecheck_code' : Parsetree.expression -> Typedtree.structure = fun exp -> | |
+ if !initial_env = Env.empty then begin | |
+ let old_time = Ident.current_time() in | |
+ (* does Ident.reinit() and may corrupt the timestamp if we | |
+ run in top-level. See Ident.reinit code | |
+ *) | |
+ initial_env := Compmisc.initial_env(); | |
+ Ident.set_current_time old_time | |
+ end; | |
+ (* Ctype.init_def(Ident.current_time()); *) | |
+ let ppf = std_formatter in | |
+ with_disabled_warnings [Warnings.Partial_match ""; | |
+ Warnings.Unused_argument; | |
+ Warnings.Unused_var ""; | |
+ Warnings.Unused_var_strict ""] | |
+ (fun () -> | |
+ let sstr = [Ast_helper.Str.eval exp] in | |
+ if !Clflags.dump_source then Pprintast.structure ppf sstr; | |
+ try | |
+ begin | |
+ Typecore.reset_delayed_checks (); | |
+ let (str, sg, newenv) = Typemod.type_toplevel_phrase !initial_env sstr in | |
+ if !Clflags.dump_typedtree then Printtyped.implementation ppf str; | |
+ let sg' = Typemod.simplify_signature sg in | |
+ if !Clflags.dump_typedtree then Printtyp.signature ppf sg'; | |
+ ignore (Includemod.signatures !initial_env sg sg'); | |
+ Typecore.force_delayed_checks (); str | |
+ end | |
+ with | |
+ x -> (Errors.report_error ppf x; | |
+ Format.pp_print_newline ppf (); | |
+ failwith | |
+ "Error type-checking generated code: scope extrusion?") | |
+ ) | |
+ | |
+(* For the benefit of offshoring, etc. *) | |
+let typecheck_code : 'a closed_code -> Typedtree.expression = fun cde -> | |
+ let str = typecheck_code' | |
+ (cde : Trx.closed_code_repr :> Parsetree.expression) in | |
+ match str.Typedtree.str_items with | |
+ | [{Typedtree.str_desc = Typedtree.Tstr_eval (texp,_)}] -> texp | |
+ | _ -> failwith "cannot happen: Parsetree was not an expression?" | |
+ | |
+let run_bytecode : 'a closed_code -> 'a = fun cde -> | |
+ let str = typecheck_code' | |
+ (cde : Trx.closed_code_repr :> Parsetree.expression) in | |
+ let lam = Translmod.transl_toplevel_definition str in | |
+ Warnings.check_fatal (); | |
+ Obj.obj @@ load_lambda Format.std_formatter lam | |
+ | |
+(* Abbreviations for backwards compatibility *) | |
+let run cde = run_bytecode (close_code cde) | |
+let (!.) cde = run cde | |
+ | |
diff -Naur ocaml-4.02.1/metalib/runcode.mli ocaml-ber-n102/metalib/runcode.mli | |
--- ocaml-4.02.1/metalib/runcode.mli 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/runcode.mli 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,42 @@ | |
+(* Given a closed code expression, compile and run it, returning | |
+ its result or propagating raised exceptions. | |
+*) | |
+ | |
+type 'a closed_code = Trx.closed_code_repr | |
+ | |
+(* Check that the code is closed and return the closed code *) | |
+val close_code : 'a code -> 'a closed_code | |
+ | |
+(* The same as close_code but return the closedness check as a thunk | |
+ rather than performing it. | |
+ This is useful for debugging and for showing the code: | |
+ If there is a scope extrusion error, it is still useful | |
+ to show the code with the extrusion before throwing the scope-extrusion | |
+ exception. | |
+*) | |
+val close_code_delay_check : 'a code -> 'a closed_code * (unit -> unit) | |
+ | |
+(* Total: a closed code can always be used in slices, etc. *) | |
+val open_code : 'a closed_code -> 'a code | |
+ | |
+(* Type-check the generated code and return the typed tree. | |
+ Offshoring takes it from here. | |
+*) | |
+val typecheck_code : 'a closed_code -> Typedtree.expression | |
+ | |
+(* Run closed code by bytecode compiling it and then executing *) | |
+val run_bytecode : 'a closed_code -> 'a | |
+ | |
+(* Other ways of running are equally possible *) | |
+ | |
+(* The following two synonyms are for backwards compatibility: | |
+ They are both compositions of close_code and run_bytecode *) | |
+val run : 'a code -> 'a | |
+val (!.) : 'a code -> 'a | |
+ | |
+(* Add a directory to search for .cmo/.cmi files, needed | |
+ for the sake of running the generated code. | |
+ The directory name may be given as +dir to refer to stdlib. | |
+ The specified directory is prepended to the load_path. | |
+*) | |
+val add_search_path : string -> unit | |
diff -Naur ocaml-4.02.1/metalib/run_native.ml ocaml-ber-n102/metalib/run_native.ml | |
--- ocaml-4.02.1/metalib/run_native.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/run_native.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,214 @@ | |
+(* Support for running natively compiled code *) | |
+ | |
+(* | |
+Here we collect various fragments, to be work on later. | |
+ | |
+The first fragment used to be in driver/optcompile.ml. It is definitely | |
+a hack! Rather than patch every implementation file with extra code to | |
+restore the array of CSP values, we should create a (perhaps C) | |
+code file that maintains the CSP array, storing and restoring it as needed, | |
+probably as part of initialization. | |
+That code file will be linked in with the whole project (forcibly linked, | |
+via the -linkall option). | |
+The Trx module will refer to the name of the CSP array, when processing | |
+CSPs in the native code. | |
+*) | |
+ | |
+open Parsetree | |
+let local_csp_arr_name = "local_csp_arr_hwrrekjhj" | |
+let local_csp_arr_str () = | |
+ let exp = {pexp_desc = Obj.magic (); | |
+ pexp_loc = Location.none; | |
+ pexp_ext = None } | |
+ and pat = {ppat_desc = Obj.magic (); | |
+ ppat_loc = Location.none; | |
+ ppat_ext = None } in | |
+ let exp1 = {exp with pexp_desc = Pexp_ident (Longident.Ldot (Longident.Lident "Marshal", "from_string"))} | |
+ and exp2 = {exp with pexp_desc = Pexp_constant (Asttypes.Const_string "blaaaaah!")} | |
+ and exp0 = {exp with pexp_desc = Pexp_constant (Asttypes.Const_int 0)} in | |
+ let exp3 = {exp with pexp_desc = Pexp_apply ( exp1, [("",exp2);("",exp0)])} in | |
+ let pat1 = {pat with ppat_desc = Ppat_var local_csp_arr_name} in | |
+ let str1 = {pstr_desc = Pstr_value (Asttypes.Nonrecursive,[pat1,exp3]); | |
+ pstr_loc = Location.none} in | |
+ let pat2 = {pat with ppat_desc = Ppat_any} in | |
+ let exp4 = {exp with pexp_desc = Pexp_ident (Longident.Lident local_csp_arr_name)} in | |
+ let str2 = {pstr_desc = Pstr_value (Asttypes.Nonrecursive,[pat2,exp4]); | |
+ pstr_loc = Location.none} in | |
+ let _ = Trx.reset_csp_array () | |
+ in [str1;str2] | |
+ | |
+let find_local_csp_arr_texp = | |
+ function | Tstr_value (_,[p,e]) -> e | |
+ | _ -> assert false | |
+ | |
+let hack_csp_arr_str = function | |
+ | Tstr_value (rf,[p,e]) -> | |
+ Tstr_value (rf,[p,{e with exp_desc = | |
+ begin | |
+ match e.exp_desc with | |
+ | Texp_apply (e,[(Some e1,o);a]) -> | |
+ Texp_apply (e,[(Some {e1 with exp_desc = | |
+ begin | |
+ match e1.exp_desc with | |
+ | Texp_constant (Asttypes.Const_string "blaaaaah!") -> | |
+ let s = Marshal.to_string !Trx.csp_array [] | |
+ in Texp_constant (Asttypes.Const_string s) | |
+ | _ -> assert false | |
+ end}, o);a]) | |
+ | _ -> assert false | |
+ end}]) | |
+ | _ -> assert false | |
+ | |
+let process_str str = | |
+ Trx.initial_native_compilation := true; | |
+ if !Clflags.plain_ocaml then str | |
+ else let str2 = List.hd(List.tl str) | |
+ in Trx.local_csp_arr_texp := (find_local_csp_arr_texp str2); | |
+ let str' = Trx.trx_structure str | |
+ in (hack_csp_arr_str (List.hd str'))::(List.tl str') | |
+ | |
+(*The local_csp_arr_str was hooked up in optcompile.ml before | |
+ Typemod.type_implementation: | |
+ ++ (fun str ->(if !Clflags.plain_ocaml then str else (local_csp_arr_str ()@str))) (* XXO *) | |
+and process_str was hooked up after Typemod.type_implementation | |
+*) | |
+ | |
+ | |
+let plugin_count = ref 0 | |
+let execute_expression_native exp = | |
+ init_path (); | |
+ begin (* Update the global ident timestamp *) | |
+ match exp.pexp_ext with | |
+ | Some v -> let t = Env.get_ident_timestamp (Obj.magic v).exp_env | |
+ in Ident.set_current_time t | |
+ | None -> () | |
+ end; | |
+ Ctype.init_def(Ident.current_time()); | |
+ let exp1 = { exp with pexp_desc = Pexp_ident (Longident.Ldot | |
+ (Longident.Lident "Trx", "execute_expression_result")) } in | |
+ let exp2 = { exp with pexp_desc = Pexp_ident (Longident.Lident ":=") } in | |
+ let exp3 = { exp with pexp_desc = Pexp_ident (Longident.Ldot | |
+ (Longident.Lident "Obj", "magic")) } in | |
+ let exp4 = { exp with pexp_desc = Pexp_apply ( exp3, [("",exp)]) } in | |
+ let exp5 = { exp with pexp_desc = Pexp_apply ( exp2, | |
+ [("",exp1);("",exp4)]) } in | |
+ let ppf = Format.std_formatter in | |
+ let sourcefile = begin | |
+ incr plugin_count; | |
+ "plugin" ^ (string_of_int !plugin_count) ^ ".ml" | |
+ end in | |
+ let prefixname = Misc.chop_extension_if_any sourcefile in | |
+ let modulename = String.capitalize(Filename.basename prefixname) in | |
+ let inputfile = Pparse.preprocess sourcefile in | |
+ let env = initial_env() in | |
+ Compilenv.reset modulename; | |
+ (* Clflags.keep_asm_file := true; *) | |
+ try | |
+ [{ Parsetree.pstr_desc = Parsetree.Pstr_eval exp5; | |
+ Parsetree.pstr_loc = Location.none }] | |
+ ++ Typemod.type_implementation sourcefile prefixname modulename env | |
+ (* The following should not be needed: we added Trx pre-processing | |
+ already in the type checker. | |
+ *) | |
+ ++ (fun (str,coe) -> (Trx.trx_structure str, coe)) (* XXO *) | |
+ ++ Translmod.transl_store_implementation modulename | |
+ +++ print_if ppf Clflags.dump_rawlambda Printlambda.lambda | |
+ +++ Simplif.simplify_lambda | |
+ +++ print_if ppf Clflags.dump_lambda Printlambda.lambda | |
+ ++ Asmgen.compile_implementation prefixname ppf; | |
+ Compilenv.save_unit_info (prefixname ^ ".cmx"); | |
+ Warnings.check_fatal (); | |
+ Pparse.remove_preprocessed inputfile; | |
+ !Trx.load_compiled_code_hook !plugin_count; | |
+ !Trx.execute_expression_result | |
+ with x -> | |
+ Pparse.remove_preprocessed_if_ast inputfile; | |
+ raise x | |
+ | |
+let _ = Trx.native_mode := true | |
+let _ = Trx.execute_expression_hook := execute_expression_native | |
+ | |
+ | |
+(* The following code is moved from trx.ml *) | |
+let pathval_trx_get_csp_value = lazy(find_value "Trx.get_csp_value") | |
+let pathval_array_get = lazy(find_value "Array.get") | |
+let trx_array_get exp = | |
+ let (p, v) = Lazy.force pathval_array_get in | |
+ { exp with exp_type = instance v.val_type; | |
+ exp_desc = Texp_ident(p, v) } | |
+ | |
+let trx_get_csp_value exp = | |
+ let (p, v) = Lazy.force pathval_trx_get_csp_value in | |
+ { exp with exp_type = instance v.val_type; | |
+ exp_desc = Texp_ident(p, v) } | |
+ | |
+let initial_native_compilation = ref false | |
+let execute_expression_hook = ref (fun _ -> assert false) | |
+let execute_expression_result = ref (Obj.repr 7) | |
+let execute_expression e = !execute_expression_hook e | |
+let load_compiled_code_hook = ref (fun n -> ()) | |
+ | |
+let empty_csp_array = Array.create 1000 (Obj.repr ()) | |
+let csp_array = ref (Array.copy empty_csp_array) | |
+let csp_index = ref 0 | |
+let reset_csp_array () = | |
+ csp_array := Array.copy empty_csp_array; | |
+ csp_index := 0 | |
+let csp_arr_filename = "saved_csp_array" | |
+ | |
+let save_csp_array () = if !csp_index <> 0 then | |
+ let outchan = open_out csp_arr_filename in | |
+ let v = (!csp_index, !csp_array) | |
+ in Marshal.to_channel outchan v []; close_out outchan | |
+ | |
+let load_csp_array () = | |
+ let inchan = open_in csp_arr_filename in | |
+ let (len,a) = Marshal.from_channel inchan | |
+ in csp_index := len; csp_array := a; | |
+ close_in inchan | |
+ | |
+let add_csp_value (v,l) = | |
+ begin | |
+ if (!csp_index >= (Array.length !csp_array)) | |
+ then csp_array := Array.append !csp_array empty_csp_array | |
+ end; | |
+ !csp_array.(!csp_index) <- v; | |
+ incr csp_index; | |
+ !csp_index - 1 | |
+ | |
+let get_csp_value n = !csp_array.(n) | |
+ | |
+ | |
+let local_csp_arr_texp = ref (Obj.magic ()) | |
+ | |
+ | |
+let remove_texp_cspval exp = | |
+ if !native_mode = false then exp else | |
+ failwith "native mode CSP are not impemented yet" | |
+(* | |
+ ZZZ | |
+ match exp.exp_desc with | |
+ | Texp_cspval (v,l) -> | |
+ let i = add_csp_value (v,l) in | |
+ let exp' = {exp with exp_desc = Texp_constant (Const_int i)} in | |
+ let desc = if !initial_native_compilation | |
+ then (Texp_apply (trx_array_get exp, [(Some !local_csp_arr_texp, Required);(Some exp', Required)])) | |
+ else (Texp_apply (trx_get_csp_value exp, [(Some exp', Required)])) in | |
+ {exp with exp_desc = desc} | |
+ | _ -> assert false | |
+*) | |
+ | |
+ | |
+(* | |
+ Moved from trx.mli | |
+val native_mode : bool ref | |
+val initial_native_compilation : bool ref | |
+val get_csp_value : int -> Obj.t | |
+val execute_expression_hook : (Parsetree.expression -> Obj.t) ref | |
+val load_compiled_code_hook : (int -> unit) ref | |
+val execute_expression_result : Obj.t ref | |
+val execute_expression : Parsetree.expression -> Obj.t | |
+val csp_array : Obj.t array ref | |
+val reset_csp_array : unit -> unit | |
+val local_csp_arr_texp : Typedtree.expression ref | |
+*) | |
diff -Naur ocaml-4.02.1/metalib/simple.ref ocaml-ber-n102/metalib/simple.ref | |
--- ocaml-4.02.1/metalib/simple.ref 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/simple.ref 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,40 @@ | |
+BER MetaOCaml toplevel, version N 102 | |
+ OCaml version 4.02.1 | |
+ | |
+# # val tr1 : ('a -> int) code = .<fun x_1 -> 1>. | |
+# * * val tr1' : ('a -> 'b -> 'b) code = .<fun x_2 -> fun x_3_4 -> x_3_4>. | |
+# * * val tr2 : ('a -> int) code = .<fun x_5 -> 1>. | |
+# * * Exception: | |
+Failure | |
+ "The code built at Characters 72-73:\n let tr3 = .<fun x -> .~(let x = !. .<x>. in .<x>.)>.;;\n ^\n is not closed: identifier x_6 bound at Characters 72-73:\n let tr3 = .<fun x -> .~(let x = !. .<x>. in .<x>.)>.;;\n ^\n is free". | |
+# * * * * * * * * * Error was expected | |
+- : unit = () | |
+# Exception: | |
+Failure | |
+ "The code built at Characters 8-9:\n .< fun x -> .~ (!. .< x >.) >.;;\n ^\n is not closed: identifier x_7 bound at Characters 8-9:\n .< fun x -> .~ (!. .< x >.) >.;;\n ^\n is free". | |
+# * * * * * * * * * * Error was expected | |
+- : unit = () | |
+# Characters 36-37: | |
+ let tr4 = .<fun x -> .~(let x = !. x in .<x>.)>.;; | |
+ ^ | |
+Wrong level: variable bound at level 1 and used at level 0 | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# val tr5 : ('a -> int) code = .<fun x_8 -> Runcode.(!.) .< 1 >.>. | |
+# * * # val tr6 : ('a -> 'a) code = .<fun x_9 -> Runcode.(!.) .< x_9 >.>. | |
+# * * # val tr7 : ('a code -> 'a) code = .<fun x_10 -> Runcode.(!.) x_10>. | |
+# * * * * * * * * * # val tr8 : ('a -> 'a code) code = .<fun x_11 -> (* CSP y *)>. | |
+# * * * * * * val tr8r : '_a -> '_a code = <fun> | |
+# * * * * * * * * * - : int code = .<x_11>. | |
+ | |
+Failure("The code built at Characters 36-37:\n val tr8r : '_a -> '_a code = <fun>\n ^\n is not closed: identifier x_11 bound at Characters 36-37:\n val tr8r : '_a -> '_a code = <fun>\n ^\n is free") | |
+# * * * * Error was expected | |
+- : unit = () | |
+# val tm1 : ('a -> 'a code) code = .<fun x_12 -> .< x_12 >.>. | |
+# * * - : int code = .<(* CSP x_12 *) Obj.magic 10>. | |
+# * * # val tg1 : '_a list ref = {contents = []} | |
+# * * * val tg2 : '_a list ref = {contents = []} | |
+# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
+All Done | |
+- : unit = () | |
+# | |
diff -Naur ocaml-4.02.1/metalib/test/bool2.ml ocaml-ber-n102/metalib/test/bool2.ml | |
--- ocaml-4.02.1/metalib/test/bool2.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/bool2.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,8 @@ | |
+type ('a,'b) staged = Now of 'b | Later of ('a, 'b) code | |
+ | |
+(* It is the *presence* of this module with a nested module which | |
+ * causes the failure, even though it is empty and not used! *) | |
+module XXX = struct | |
+ module TT = struct | |
+ end | |
+end | |
diff -Naur ocaml-4.02.1/metalib/test/letrec.ml ocaml-ber-n102/metalib/test/letrec.ml | |
--- ocaml-4.02.1/metalib/test/letrec.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/letrec.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,79 @@ | |
+(* Generation of more efficient letrec code, without nesting of letrec *) | |
+(* This is the product of the discussion with Jun Inoue in Oct 2013 *) | |
+ | |
+module type LETRECS = sig | |
+ type letrec_id (* abstract *) | |
+ val make_letrecs : (letrec_id -> 'w code) -> 'w code | |
+ | |
+ val add_letrec : letrec_id -> | |
+ (('a->'b) code -> ('a->'b) code * 'w) -> 'w | |
+end | |
+ | |
+ (* (('cl,'a->'b) code -> ('cl,'w) code) -> ('cl,'w) code *) | |
+ | |
+(* Example of using the interface | |
+ First, the standard even-odd example | |
+ *) | |
+ | |
+module Ex1(S:LETRECS) = struct | |
+ open S | |
+ | |
+ let r = | |
+ make_letrecs @@ fun lid -> | |
+ add_letrec lid (fun even' -> | |
+ add_letrec lid (fun odd' -> | |
+ let even = .<fun n -> n = 0 || .~odd' (n-1)>. | |
+ and odd = .<fun n -> not (n=0) && .~even' (n-1)>. | |
+ in (odd, (even, .<fun n -> (.~odd n, .~even n)>.)) | |
+ )) | |
+end | |
+ | |
+(* | |
+ Next example: | |
+ It is the variation of the even-odd example; the mutually | |
+ recursive functions are (artificially) made to have distinct types | |
+ | |
+ let rec even = fun n -> n=0 || odd even (n-1) | |
+ and odd even = not (n=0) && even (n-1) | |
+ in fun n -> [even n, odd n] | |
+ | |
+The next two examples are to generalize the above to compute residuals of k | |
+for example | |
+ | |
+let rec three0 n = n=0 || three2 (n-1) | |
+and three1 n = not (n=0) && three0 (n-1) | |
+and three2 n = not (n=0) && three1 (n-1) | |
+ | |
+ | |
+*) | |
+module Ex2(S:LETRECS) = struct | |
+ open S | |
+ | |
+ let r k = | |
+ make_letrecs @@ fun lid -> | |
+ let rec loop = function | |
+ | k when k <= 0 -> .<[| |]>. | |
+ | 1 -> .<[| true |]>. | |
+ | k -> | |
+ add_letrec lid (fun even' -> | |
+ add_letrec lid (fun odd' -> | |
+ let even = .<fun n -> n = 0 || .~odd' (n-1)>. | |
+ and odd = .<fun n -> not (n=0) && .~even' (n-1)>. | |
+ in (odd, (even, .<fun n -> (.~odd n, .~even n)>.)) | |
+ )) | |
+end | |
+ | |
+ | |
+(* One, naive implementation: a simultaneous letrec as nested letrecs *) | |
+module Nested : LETRECS = struct | |
+ type letrec_id = unit | |
+ | |
+ let add_letrec : letrec_id -> | |
+ (('cl,'a->'b) code -> ('cl,'a->'b) code) -> | |
+ (('cl,'a->'b) code -> ('cl,'w) code) -> ('cl,'w) code = | |
+ fun lid exp body -> .<let rec x = .~(exp .<x>.) in .~(body .<x>.)>. | |
+ | |
+ val make_letrecs : (letrec_id -> 'w code) -> 'w code | |
+ fun body -> body () | |
+end;; | |
+ | |
diff -Naur ocaml-4.02.1/metalib/test/pythagorian_triples.ml ocaml-ber-n102/metalib/test/pythagorian_triples.ml | |
--- ocaml-4.02.1/metalib/test/pythagorian_triples.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/pythagorian_triples.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,87 @@ | |
+(* Test of the let! notation: Fair monad for non-determinism *) | |
+ | |
+module NonDet : sig | |
+ type 'a stream_v | |
+ type 'a stream = unit -> 'a stream_v | |
+ | |
+ val ret : 'a -> 'a stream | |
+ val fail : 'a stream | |
+ | |
+ (* a.k.a bind or disjunction *) | |
+ val (let!) : 'a stream -> ('a -> 'b stream) -> 'b stream | |
+ (* a.k.a. fair disjunction *) | |
+ val mplus : 'a stream -> 'a stream -> 'a stream | |
+ | |
+ val guard : bool -> unit stream | |
+ val yield : 'a stream -> 'a stream | |
+ | |
+ val run : int -> (* upper bound on the number of solutions *) | |
+ 'a stream -> | |
+ 'a list | |
+end = struct | |
+ type 'a stream_v = | |
+ Nil | Cons of 'a * 'a stream | InC of 'a stream | |
+ and 'a stream = unit -> 'a stream_v | |
+ | |
+ let fail = fun () -> Nil | |
+ let ret a = fun () -> Cons (a,fail) | |
+ | |
+ (* actually, interleave: a fair disjunction with breadth-first search*) | |
+ let rec mplus a b = fun () -> | |
+ match a () with | |
+ | Nil -> InC b | |
+ | Cons (a1,a2) -> Cons (a1,(mplus b a2)) | |
+ | InC a -> | |
+ begin match b () with | |
+ | Nil -> InC a | |
+ | InC b -> InC (mplus a b) | |
+ | Cons (b1,b2) -> Cons (b1, (mplus a b2)) | |
+ end | |
+ | |
+ (* a fair conjunction *) | |
+ let rec (let!) m f = fun () -> | |
+ match m () with | |
+ | Nil -> fail () | |
+ | InC a -> InC ((let!) a f) | |
+ | Cons (a,b) -> mplus (f a) ((let!) b f) () | |
+ | |
+ let guard be = if be then ret () else fail | |
+ let yield m () = InC m | |
+ | |
+ let rec run n m = | |
+ if n = 0 then [] else | |
+ match m () with | |
+ | Nil -> [] | |
+ | InC a -> run n a | |
+ | Cons (a,b) -> (a::run (n-1) b) | |
+end;; | |
+ | |
+open NonDet;; | |
+ | |
+(* The example uses left recursion and truly infinite streams! *) | |
+(* Don't try this in Prolog or in Haskell's MonadPlus. *) | |
+ | |
+let rec numb () = (* infinite stream of integers *) | |
+ yield (mplus (let! n = numb in ret (n+1)) (* left recursion! *) | |
+ (ret 0)) () | |
+;; | |
+ | |
+let pyth : (int * int * int) NonDet.stream = | |
+ let! i = numb in | |
+ let! () = guard (i>0) in | |
+ let! j = numb in | |
+ let! () = guard (j>0) in | |
+ let! k = numb in | |
+ let! () = guard (k>0) in | |
+ (* Just to illustrate the `let' form within let! *) | |
+ let test x = x*x = j*j + k*k in | |
+ let! () = guard (test i) in | |
+ ret (i,j,k) | |
+;; | |
+ | |
+let [(5, 4, 3); (5, 3, 4); (10, 8, 6); (10, 6, 8); (13, 12, 5); (13, 5, 12); | |
+ (15, 12, 9); (15, 9, 12); (17, 15, 8); (17, 8, 15)] | |
+ = | |
+run 10 pyth;; | |
+ | |
+print_endline "\nAll done";; | |
diff -Naur ocaml-4.02.1/metalib/test/quick_test.ml ocaml-ber-n102/metalib/test/quick_test.ml | |
--- ocaml-4.02.1/metalib/test/quick_test.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/quick_test.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,615 @@ | |
+(* Quick test of BER MetaOCaml. From PEPM09 and PEPM08 papers: | |
+ | |
+ Closing the Stage: From staged code to typed closures. | |
+ Yukiyoshi Kameyama, Oleg Kiselyov, and Chung-chieh Shan | |
+ | |
+*) | |
+open Runcode;; | |
+ | |
+(* ---------------------------------------------------------------------- *) | |
+(* The power example, Sec 2 *) | |
+ | |
+let square x = x * x | |
+let rec power : int -> int code -> int code = | |
+ fun n -> fun x -> | |
+ if n = 0 then .<1>. | |
+ else if n mod 2 = 0 | |
+ then .< (*csp*)square .~(power (n/2) x)>. | |
+ else .<.~x * .~(power (n-1) x)>. | |
+;; | |
+let power7_cde = .<fun x -> .~(Printf.printf "power\n"; power 7 .<x>.)>.;; | |
+(* "power" printed once *) | |
+(* | |
+val power7_cde : (int -> int) code = .< | |
+ fun x_22 -> | |
+ x_22 * | |
+ (((* cross-stage persistent value (id: square) *)) | |
+ (x_22 * | |
+ (((* cross-stage persistent value (id: square) *)) (x_22 * 1))))>. | |
+*) | |
+let power7 : int -> int = !. power7_cde;; | |
+let (128, 2187) = (power7 2, power7 3);; | |
+(* nothing is printed... | |
+ val res : int * int = (128, 2187) | |
+*) | |
+ | |
+(* Before N101, the following didn't work *) | |
+let power7 : int -> int = | |
+ !. .<fun x -> .~(Printf.printf "power\n"; power 7 .<x>.)>.;; | |
+ | |
+(* But the following does. It is the explicit version of the above *) | |
+let power7 : int -> int = | |
+ Runcode.run_bytecode (Runcode.close_code | |
+ .<fun x -> .~(Printf.printf "power\n"; power 7 .<x>.)>.);; | |
+(* "power" printed once *) | |
+let (128, 2187) = (power7 2, power7 3);; | |
+(* nothing is printed... | |
+ val res : int * int = (128, 2187) | |
+*) | |
+ | |
+ | |
+(* ---------------------------------------------------------------------- *) | |
+(* The ef example. *) | |
+ | |
+(* The source code *) | |
+let ef = fun z -> .<fun x -> .~z + x>.;; | |
+let ef1 = .<fun y -> .~(ef .<y>.)>.;; | |
+(* | |
+val ef1 : (int -> int -> int) code = .<fun y_27 x_28 -> y_27 + x_28>. | |
+*) | |
+let 5 = (!. ef1) 2 3;; (* 5 *) | |
+ | |
+let ef2 = .<fun x -> fun y -> .~(ef .<x*y>.)>.;; | |
+(* | |
+val ef2 : (int -> int -> int -> int) code = .< | |
+ fun x_29 y_30 x_31 -> (x_29 * y_30) + x_31>. | |
+*) | |
+let 10 = (!. ef2) 2 3 4;; (* 10 *) | |
+ | |
+(* ---------------------------------------------------------------------- *) | |
+(* The eta example. *) | |
+ | |
+let (5,10,34) = | |
+ let eta f = .<fun x -> .~(f .<x>.)>. in | |
+ (!. | |
+ .< fun y -> | |
+ .~(eta (fun z -> .< .~z + y >.)) >.) | |
+ 2 3, | |
+ (!. | |
+ .< fun y -> fun w -> | |
+ .~(eta (fun z -> .< .~z + y*w >.)) >.) | |
+ 2 3 4, | |
+ (!. | |
+ .< fun x u -> | |
+ .~(eta (fun z -> .<fun y -> .~z + u*x*y >.)) >.) | |
+ 2 3 4 5 | |
+ ;; | |
+ | |
+ | |
+(* ---------------------------------------------------------------------- *) | |
+(* Cross-stage presistence *) | |
+ | |
+(* This example includes persistence of a code value, which we | |
+ specifically exclude in the paper. *) | |
+ | |
+let cspe = | |
+ .<fun x -> .~(let u = .<x>. in | |
+ (!. .<fun y -> .<.~u>.>.) ()) >.;; | |
+ | |
+(* | |
+ val cspe : ('a -> 'a) code = .<fun x_41 -> x_41>. | |
+*) | |
+ | |
+let 42 = (!. cspe) 42;; | |
+ | |
+(* This CSP example does fit our restriction *) | |
+ | |
+let rec scspe x = .<(fun y -> x) (scspe 1)>.;; | |
+(* | |
+val scspe : int -> int code = <fun> | |
+# scspe 10;; | |
+- : ('a, int) code = | |
+.<((fun y_1 -> 10) (((* cross-stage persistent value (as id: scspe) *)) 1))>. | |
+# !. (scspe 10);; | |
+- : int = 10 | |
+*) | |
+ | |
+let 10 = !. (scspe 10);; | |
+ | |
+(* ---------------------------------------------------------------------- *) | |
+(* Scope extrusion via mutable state *) | |
+ | |
+(* | |
+let extr = let x = ref .<1>. in | |
+ let _ = .<fun v -> .~(x := .<v>.; .<()>.)>. in | |
+ !x;; | |
+ | |
+(* It does type-check ... but printing it produces an error *) | |
+ | |
+ val extr : int code = .<v_45>. | |
+ | |
+Failure("The code built at Characters 50-51:\n let _ = .<fun v -> .~(x := .<v>.; .<()>.)>. in\n ^\n is not closed: identifier v_45 bound at Characters 50-51:\n let _ = .<fun v -> .~(x := .<v>.; .<()>.)>. in\n ^\n is free") | |
+*) | |
+ | |
+(* Previously: | |
+ val extr : ('a, int) code = .<v_1>. | |
+ | |
+ # !. extr ;; | |
+ Unbound value v_1 | |
+ Exception: Trx.TypeCheckingError. | |
+*) | |
+ | |
+(* The run-time error is reported on an attempt to run the code *) | |
+let true = | |
+ try !. (let x = ref .<1>. in | |
+ let _ = .<fun v -> .~(x := .<v>.; .<()>.)>. in | |
+ !x); false | |
+ with Failure e -> print_string e; true | |
+;; | |
+(* | |
+Scope extrusion at Characters 75-76: | |
+ let _ = .<fun v -> .~(x := .<v>.; .<()>.)>. in | |
+ ^ | |
+ for the identifier v_66 bound at Characters 60-61: | |
+ let _ = .<fun v -> .~(x := .<v>.; .<()>.)>. in | |
+ ^ | |
+*) | |
+ | |
+(* The run-time error is reported on an attempt to splice the code *) | |
+let true = | |
+ try let x = ref .<1>. in | |
+ let _ = .<fun v -> .~(x := .<v>.; .<()>.)>. in | |
+ .<1 + .~(!x)>.; false (* triggers an error with the message below *) | |
+ with Failure e -> print_string e; true | |
+;; | |
+(* | |
+Scope extrusion detected at Characters 97-107: | |
+ .<1 + .~(!x)>.; false (* triggers an error with the message below *) | |
+ ^^^^^^^^^^ | |
+ for code built at Characters 57-58: | |
+ let _ = .<fun v -> .~(x := .<v>.; .<()>.)>. in | |
+ ^ | |
+ for the identifier v_47 bound at Characters 57-58: | |
+ let _ = .<fun v -> .~(x := .<v>.; .<()>.)>. in | |
+ ^ | |
+*) | |
+ | |
+ | |
+(* | |
+ * In this example, we compute a staged power function while tracking how many | |
+ * multiplications the generated code performs. This example demonstrates the | |
+ * utility of our environment-passing translation, in two ways. First, it is | |
+ * easiest to write this example if we use a side effect such as mutable state | |
+ * in MetaOCaml, but such an extension (a piece of state of type int) has not | |
+ * been shown sound except through our translation. Second, we can write this | |
+ * example in pure MetaOCaml (more awkwardly) using our translation. | |
+ * | |
+ * Thanks to Olivier Danvy for suggesting this example. | |
+ *) | |
+ | |
+let rec powerc = function | |
+ | 0 -> (.<fun x -> 1>.,0) | |
+ | 1 -> (.<fun x -> x>.,1) | |
+ | n -> let (c,n1) = powerc (pred n) in | |
+ (.<fun x -> (.~c x) * x>.,succ n1) | |
+;; | |
+(* | |
+ val powerc : int -> (int -> int) code * int = <fun> | |
+*) | |
+ | |
+let test = powerc 5;; | |
+(* | |
+val test : (int -> int) code * int = | |
+ (.< | |
+ fun x_52 -> | |
+ ((fun x_51 -> | |
+ ((fun x_50 -> | |
+ ((fun x_49 -> ((fun x_48 -> x_48) x_49) * x_49) x_50) * x_50) | |
+ x_51) | |
+ * x_51) x_52) | |
+ * x_52>. | |
+ , 5) | |
+*) | |
+ | |
+let 32 = (!. (fst test)) 2;; | |
+(* | |
+val testc : int = 32 | |
+*) | |
+ | |
+let mul x y = .<.<.~.~x * .~.~y>.>.;; | |
+(* | |
+val mul : int code code -> int code code -> int code code = <fun> | |
+*) | |
+ | |
+let rec powerd = function | |
+ | 0 -> (.<fun x -> .<1>.>.,0) | |
+ | 1 -> (.<fun x -> x>.,1) | |
+ | n -> let (c,n1) = powerd (pred n) in | |
+ (.<fun x -> .~(mul .<.~c x>. .<x>.)>.,succ n1) | |
+;; | |
+(* | |
+val powerd : int -> (int code -> int code) code * int = <fun> | |
+*) | |
+ | |
+let test1 = powerd 5;; | |
+(* | |
+val test1 : (int code -> int code) code * int = | |
+ (.< | |
+ fun x_57 -> | |
+ .< | |
+ (.~(fun x_56 -> | |
+ .< | |
+ (.~(fun x_55 -> | |
+ .< | |
+ (.~(fun x_54 -> | |
+ .< (.~(fun x_53 -> x_53) x_54) * (.~(x_54)) >.) | |
+ x_55) | |
+ * (.~(x_55)) >.) x_56) | |
+ * (.~(x_56)) >.) x_57) | |
+ * (.~(x_57)) >.>. | |
+ , 5) | |
+*) | |
+ | |
+let testd = !. (fst (powerd 5));; | |
+let testdd = .<fun x -> .~(testd .<x>.)>.;; | |
+(* | |
+val testdd : (int -> int) code = .< | |
+ fun x_63 -> (((x_63 * x_63) * x_63) * x_63) * x_63>. | |
+*) | |
+ | |
+(* An attempt to write testd without overt use of multiple levels: | |
+ no nested brackets. | |
+*) | |
+let one = .<1>.;; | |
+let mul1 x y = .<.~x * .~y>.;; | |
+let mull x y = .<mul1 .~x .~y>.;; | |
+ | |
+let rec powerd1 = function | |
+ | 0 -> (.<fun x -> one>.,0) | |
+ | 1 -> (.<fun x -> x>.,1) | |
+ | n -> let (c,n1) = powerd1 (pred n) in | |
+ (.<fun x -> .~(mull .<.~c x>. .<x>.)>.,succ n1) | |
+;; | |
+(* | |
+val powerd1 : int -> (int code -> int code) code * int = <fun> | |
+*) | |
+ | |
+let test11 = powerd1 5;; | |
+(* | |
+val test11 : (int code -> int code) code * int = | |
+ (.< | |
+ fun x_68 -> | |
+ ((* cross-stage persistent value (id: mul1) *)) | |
+ ((fun x_67 -> | |
+ ((* cross-stage persistent value (id: mul1) *)) | |
+ ((fun x_66 -> | |
+ ((* cross-stage persistent value (id: mul1) *)) | |
+ ((fun x_65 -> | |
+ ((* cross-stage persistent value (id: mul1) *)) | |
+ ((fun x_64 -> x_64) x_65) x_65) x_66) x_66) x_67) | |
+ x_67) x_68) x_68>. | |
+ , 5) | |
+*) | |
+ | |
+let testd1 () = !. (fst (powerd1 5));; | |
+let testdd1 = .<fun x -> .~(testd1 () .<x>.)>.;; | |
+(* | |
+val testdd1 : (int -> int) code = .< | |
+ fun x_69 -> (((x_69 * x_69) * x_69) * x_69) * x_69>. | |
+*) | |
+let 32 = (Runcode.run .<fun x -> .~(testd1 () .<x>.)>.) 2;; | |
+ | |
+ | |
+(* Meta-programming with delimited continuations *) | |
+(* Writing an efficient specialized version of Gibonacci, | |
+ without using any fix-point combinators, etc. | |
+*) | |
+ | |
+open Printf;; | |
+ | |
+(* The original Gibonacci *) | |
+ | |
+let rec gib x y n = | |
+ if n = 0 then x else | |
+ if n = 1 then y else | |
+ gib x y (n-1) + gib x y (n-2) | |
+;; | |
+let 8 = gib 1 1 5;; | |
+ | |
+(* Naively staged Gibonacci, to the statically known value of n *) | |
+ | |
+let rec gibgen x y n = | |
+ if n = 0 then x else | |
+ if n = 1 then y else | |
+ .<.~(gibgen x y (n-1)) + .~(gibgen x y (n-2))>. | |
+;; | |
+(* | |
+ val gibgen : ('cl, int) code -> ('cl, int) code -> int -> ('cl, int) code | |
+*) | |
+let test_gibgen n = .<fun x y -> .~(gibgen .<x>. .<y>. n)>.;; | |
+(* val test_gibgen : int -> ('a, int -> int -> int) code = <fun> *) | |
+let test_gibgen5 = test_gibgen 5;; | |
+(* | |
+val test_gibgen5 : (int -> int -> int) code = .< | |
+ fun x_1 y_2 -> | |
+ (((y_2 + x_1) + y_2) + (y_2 + x_1)) + ((y_2 + x_1) + y_2)>. | |
+*) | |
+let 8 = (!. test_gibgen5) 1 1;; | |
+ | |
+(* Clearly, the naive Gibonacci is inefficient. | |
+ The specialized code test_gibgen5 shows why: | |
+ the computation (y_2 + x_1) is repeated thrice within such a short fragment | |
+*) | |
+ | |
+(* To improve Gibonacci, we have to add memoization *) | |
+ | |
+(* First we define the abstract data types of memoization table | |
+ with integer keys *) | |
+ | |
+(* For the sake of the closest correspondence with circle-shift.elf, | |
+ we use pairs to emulate 'a option data type. In the rest of the | |
+ code, 'a maybe is an abstract data type. | |
+*) | |
+module Maybe : | |
+ sig | |
+ type 'a maybe | |
+ val nothing : 'a maybe | |
+ val just : 'a -> 'a maybe | |
+ val ifnothing : 'a maybe -> bool | |
+ val fromjust : 'a maybe -> 'a | |
+ end = struct | |
+ type 'a maybe = bool * (unit -> 'a) | |
+ let nothing = (true, fun () -> failwith "nothing") | |
+ let just x = (false, fun () -> x) | |
+ let ifnothing = fst | |
+ let fromjust x = snd x () | |
+end;; | |
+open Maybe;; | |
+ | |
+module Memo : | |
+ sig | |
+ type 'a memo | |
+ val empty : 'a memo | |
+ val lookup : int -> 'a memo -> 'a maybe | |
+ val ext : 'a memo -> int -> 'a -> 'a memo | |
+ end = struct | |
+ (* The following implementation uses functions, for compatibility | |
+ with circle-shift.elf. The rest of the code does not depend | |
+ on the implementation and can't even know it. | |
+ *) | |
+ type 'a memo = int -> 'a maybe | |
+ let empty = fun key -> nothing | |
+ let lookup = fun n table -> table n | |
+ let ext = fun table n v -> | |
+ fun key -> if key = n then just v else table key | |
+end;; | |
+open Memo;; | |
+ | |
+(* we can write the standard, textbook memoizer *) | |
+(* It memoizes the result of the application of function f to the integer n. | |
+ *) | |
+ | |
+let new_memo () = | |
+ let table = ref empty in | |
+ fun f n -> | |
+ let r = lookup n !table in | |
+ if ifnothing r | |
+ then (* memo table miss *) | |
+ let v = f n in (* compute the value *) | |
+ table := ext !table n v; v | |
+ else fromjust r (* else return the memoized value *) | |
+;; | |
+ | |
+ | |
+(* Now we can memoize Gibonacci and obtain an improved version *) | |
+let gibo x y = | |
+ let memo = new_memo () in | |
+ let rec loop n = | |
+ if n = 0 then x else | |
+ if n = 1 then y else | |
+ memo loop (n-1) + memo loop (n-2) | |
+ in loop | |
+;; | |
+ | |
+let 8 = gibo 1 1 5;; (* 8 *) | |
+let 1346269 = gibo 1 1 30;; | |
+(* 1346269, without memoization it would've taken a while...*) | |
+ | |
+(* We may try to stage it, naively *) | |
+ | |
+let sgibo_naive x y = | |
+ let memo = new_memo () in | |
+ let rec loop n = | |
+ if n = 0 then x else | |
+ if n = 1 then y else | |
+ .<.~(memo loop (n-1)) + .~(memo loop (n-2))>. | |
+ in loop | |
+;; | |
+ | |
+let test_sgibo_naive5 = | |
+ .<fun x y -> .~(sgibo_naive .<x>. .<y>. 5)>.;; | |
+(* | |
+val test_sgibo_naive5 : (int -> int -> int) code = .< | |
+ fun x_3 y_4 -> | |
+ (((y_4 + x_3) + y_4) + (y_4 + x_3)) + ((y_4 + x_3) + y_4)>. | |
+*) | |
+ | |
+(* Alas, the result shows the duplication of computations. The result of | |
+ loop, in sgibo_naive, is a present-stage value but future-stage | |
+ computation. We saved effort at the present stage but we saved no | |
+ computation at the future stage. We need let insertion to save | |
+ future-stage computations. | |
+*) | |
+ | |
+(* But the let-insertion isn't that easy! The naive version *) | |
+ | |
+let sgibo1_naive x y = | |
+ let memo = new_memo () in | |
+ let rec loop n = | |
+ if n = 0 then x else | |
+ if n = 1 then y else | |
+ .<let t1 = .~(memo loop (n-1)) and t2 = .~(memo loop (n-2)) | |
+ in t1 + t2>. | |
+ in loop | |
+;; | |
+ | |
+let test_sgibo1_naive5 = | |
+ .<fun x y -> .~(sgibo1_naive .<x>. .<y>. 5)>. | |
+(* | |
+val test_sgibo1_naive5 : (int -> int -> int) code = .< | |
+ fun x_105 y_106 -> | |
+ let t1_113 = | |
+ let t1_111 = | |
+ let t1_109 = let t1_107 = y_106 and t2_108 = x_105 in t1_107 + t2_108 | |
+ and t2_110 = y_106 in t1_109 + t2_110 | |
+ and t2_112 = let t1_107 = y_106 and t2_108 = x_105 in t1_107 + t2_108 in | |
+ t1_111 + t2_112 | |
+ and t2_114 = | |
+ let t1_109 = let t1_107 = y_106 and t2_108 = x_105 in t1_107 + t2_108 | |
+ and t2_110 = y_106 in t1_109 + t2_110 in | |
+ t1_113 + t2_114>. | |
+*) | |
+ | |
+(* the naive version obviously doesn't do any good: It creates even bigger | |
+ duplicated computations *) | |
+ | |
+(* We have to change the memo table implementation. Our memo table should | |
+ contain only those future-stage computations that are future-stage | |
+ values. So, we need to do let-insertion after we detected a miss. | |
+ But for that, we have to re-write everything in CPS. We have to write | |
+ the memo-table implementation in CPS: | |
+*) | |
+ | |
+let new_memo_let_CPS () = | |
+ let table = ref empty in | |
+ fun f n k -> | |
+ let r = lookup n !table in | |
+ if ifnothing r | |
+ then (* memo table miss *) | |
+ f n (* compute the value *) | |
+ (fun v -> .<let t = .~v in | |
+ .~(table := ext !table n .<t>.; k .<t>.)>.) | |
+ else k (fromjust r) (* else return the memoized value *) | |
+;; | |
+ | |
+(* but we also must re-write sgibo in CPS! *) | |
+ | |
+let sgibo_CPS x y = | |
+ let memo = new_memo_let_CPS () in | |
+ let rec loop n k = | |
+ if n = 0 then k x else | |
+ if n = 1 then k y else | |
+ memo loop (n-1) (fun r1 -> | |
+ memo loop (n-2) (fun r2 -> | |
+ k .<.~r1 + .~r2>.)) | |
+ in loop | |
+;; | |
+ | |
+let test_sgibo_CPS5 = | |
+ .<fun x y -> .~(sgibo_CPS .<x>. .<y>. 5 (fun x ->x))>.;; | |
+ | |
+(* | |
+val test_sgibo_CPS5 : (int -> int -> int) code = .< | |
+ fun x_2 y_3 -> | |
+ let t_4 = y_3 in | |
+ let t_5 = x_2 in | |
+ let t_6 = t_4 + t_5 in | |
+ let t_7 = t_6 + t_4 in let t_8 = t_7 + t_6 in t_8 + t_7>. | |
+*) | |
+let 8 = (!. test_sgibo_CPS5) 1 1;; | |
+ | |
+(* Now we get the desired result: no duplicate computations. | |
+ At the cost of changing all of our code, even sgibo, in CPS. | |
+ Memoization is no longer easy -- it becomes very intrusive. | |
+*) | |
+ | |
+(* Not only this approach inconvenient, it is also unsafe. | |
+ The mutation in maintaining the table in new_memo_let_CPS | |
+ results in unsafety. We store in the `global' memo table code | |
+ values like .<t>. -- with variables bound in the scope | |
+ that is more narrow than the dynamic scope of the table. | |
+ *) | |
+ | |
+(* Let's make a simple `pessimization' of sgibo1_CPS. Let's suppose the | |
+ programmer didn't want to rewrite gib in CPS, and continued to use | |
+ memoization in `direct style'. | |
+*) | |
+ | |
+let sgibo1_bad x y = | |
+ let memo = new_memo_let_CPS () in | |
+ let rec loop n = | |
+ if n = 0 then x else | |
+ if n = 1 then y else | |
+ .<.~(memo (fun n k -> k (loop n)) (n-1) (fun x ->x)) + | |
+ .~(memo (fun n k -> k (loop n)) (n-2) (fun x ->x))>. | |
+ in loop | |
+;; | |
+ | |
+let true = | |
+ try | |
+ let test_sgibo1_bad = | |
+ .<fun x y -> .~(sgibo1_bad .<x>. .<y>. 5)>. in | |
+ false | |
+ with Failure e -> print_string e; true | |
+;; | |
+ | |
+(* Previously (before version N100) it worked: | |
+ val test_sgibo1_bad : ('a, int -> int -> int) code = | |
+ .<fun x_1 -> | |
+ fun y_2 -> | |
+ (let t_7 = (t_6 + t_5) in t_7 + | |
+ let t_6 = | |
+ (let t_5 = (t_3 + let t_4 = x_1 in t_4) in t_5 + let t_3 = y_2 in t_3) in | |
+ t_6)>. | |
+*) | |
+ | |
+(* Although the result appears efficient -- only four additions -- | |
+ it is incorrect! Please notice how variable t_6 is referenced before | |
+ it is bound. Attempting to run this code gives | |
+ | |
+!. test_sgibo1_bad;; | |
+ Unbound value t_6 | |
+ Exception: Trx.TypeCheckingError. | |
+*) | |
+ | |
+(* But now we get a scope extrusion error | |
+Scope extrusion detected at Characters 133-243: | |
+ .............................ng e; true;; | |
+ for code built at Characters 242-243: | |
+ for the identifier t_131 bound at Characters 242-243: | |
+*) | |
+ | |
+(* To rely on MetaOCaml's type soundness, we must not use any side effects | |
+ in our code generator. We could write our memoizing gib without state, | |
+ by including state-passing in our continuation-passing, as follows. | |
+*) | |
+ | |
+let new_memo_let_CPS_only f n k table = | |
+ let r = lookup n table in | |
+ if ifnothing r | |
+ then | |
+ f n | |
+ (fun v table -> .<let t = .~v in | |
+ .~(k .<t>. (ext table n .<t>.))>.) | |
+ table | |
+ else | |
+ k (fromjust r) table | |
+;; | |
+ | |
+let sgibo_CPS_only x y = | |
+ let memo = new_memo_let_CPS_only in | |
+ let rec loop n k = | |
+ if n = 0 then k x else | |
+ if n = 1 then k y else | |
+ memo loop (n-1) (fun r1 -> | |
+ memo loop (n-2) (fun r2 -> | |
+ k .<.~r1 + .~r2>.)) | |
+ in loop | |
+;; | |
+ | |
+let test_sgibo_CPS_only5 = | |
+ .<fun x y -> .~(sgibo_CPS_only .<x>. .<y>. 5 (fun r table -> r) empty)>.;; | |
+ | |
+let 8 = (!. test_sgibo_CPS_only5) 1 1;; | |
+ | |
+Printf.printf "\nAll Done\n";; | |
diff -Naur ocaml-4.02.1/metalib/test/simple.ml ocaml-ber-n102/metalib/test/simple.ml | |
--- ocaml-4.02.1/metalib/test/simple.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/simple.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,182 @@ | |
+(* Various simple (one-liner) examples and NON-examples *) | |
+open Runcode;; | |
+ | |
+(* Safety of run *) | |
+ | |
+let tr1 = .<fun x -> .~(!. .<.<1>.>.)>.;; | |
+(* | |
+val tr1 : ('a -> int) code = .<fun x_54 -> 1>. | |
+*) | |
+let tr1' = .<fun x -> .~(!. .<.<fun x -> x>.>.)>.;; | |
+(* | |
+val tr1' : ('a -> 'b -> 'b) code = .<fun x_55 x_56_57 -> x_56_57>. | |
+*) | |
+let tr2 = .<fun x -> .~(let x = !. .<1>. in .<x>.)>.;; | |
+(* | |
+val tr2 : ('a -> int) code = .<fun x_58 -> 1>. | |
+*) | |
+ | |
+let tr3 = .<fun x -> .~(let x = !. .<x>. in .<x>.)>.;; | |
+ | |
+(* | |
+Exception: | |
+Failure | |
+ "The code built at Characters 16-17:\n let tr3 = .<fun x -> .~(let x = !. .<x>. in .<x>.)>.;;\n ^\n is not closed: identifier x_59 bound at Characters 16-17:\n let tr3 = .<fun x -> .~(let x = !. .<x>. in .<x>.)>.;;\n ^\n is free". | |
+ | |
+Was: | |
+ let tr3 = .<fun x -> .~(let x = !. .<x>. in .<x>.)>.;; | |
+ ^^^^^ | |
+Error: !. error: 'a not generalizable in ('a, 'b) code | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+.< fun x -> .~ (!. .< x >.) >.;; | |
+(* | |
+Exception: | |
+Failure | |
+ "The code built at Characters 7-8:\n .< fun x -> .~ (!. .< x >.) >.;;\n ^\n is not closed: identifier x_60 bound at Characters 7-8:\n .< fun x -> .~ (!. .< x >.) >.;;\n ^\n is free". | |
+ | |
+Was: | |
+Characters 15-26: | |
+ .< fun x -> .~ (!..< x >.) >.;; | |
+ ^^^^^^^^^^^ | |
+Error: !. occurs check error: 'cl occurs in ('cl, ('cl, 'a) code) code | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+let tr4 = .<fun x -> .~(let x = !. x in .<x>.)>.;; | |
+(* | |
+Characters 35-36: | |
+ let tr4 = .<fun x -> .~(let x = !. x in .<x>.)>.;; | |
+ ^ | |
+Error: Wrong level: variable bound at level 1 and used at level 0 | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+let tr5 = .<fun x -> !. .<1>.>.;; | |
+(* | |
+val tr5 : ('a -> int) code = .<fun x_61 -> Runcode.( !. ) (.< 1 >.)>. | |
+*) | |
+let 1 = (!. tr5) true;; | |
+ | |
+let tr6 = .<fun x -> !. .<x>.>.;; | |
+(* | |
+val tr6 : ('a -> 'a) code = .<fun x_62 -> Runcode.( !. ) (.< x_62 >.)>. | |
+*) | |
+let 1 = (!. tr6) 1;; | |
+ | |
+let tr7 = .<fun x -> !. x>.;; | |
+(* | |
+val tr7 : ('a code -> 'a) code = .<fun x_63 -> Runcode.( !. ) x_63>. | |
+ | |
+Was: | |
+Characters 24-25: | |
+ let tr7 = .<fun x -> !. x>.;; | |
+ ^ | |
+Error: !. error: 'a not generalizable in ('a, 'b) code | |
+print_endline "Error was expected";; | |
+*) | |
+let 10 = !. tr7 .<10>.;; | |
+ | |
+ (* Bizzare CSP *) | |
+let tr8 = .<fun x -> .~(let y = .<x>. in .<y>.)>.;; | |
+(* | |
+Characters 43-44: | |
+ let tr8 = .<fun x -> .~(let y = .<x>. in .<y>.)>.;; | |
+ ^ | |
+Warning 22: The CSP value is a closure or too deep to serialize | |
+val tr8 : ('a -> 'a code) code = .<fun x_121 -> (* CSP y *)>. | |
+*) | |
+(* But it cannot be run! *) | |
+let tr8r = !. tr8;; | |
+(* | |
+val tr8r : '_a -> '_a code = <fun> | |
+ | |
+Was | |
+Characters 14-17: | |
+ let tr8r = !. tr8;; | |
+ ^^^ | |
+Error: !. occurs check error: 'a occurs in ('a, 'b -> ('a, 'b) code) code | |
+print_endline "Error was expected";; | |
+*) | |
+(* And it cannot be run indeed *) | |
+!. tr8 10;; | |
+(* | |
+- : int code = .<x_65>. | |
+ | |
+Failure("The code built at Characters 16-17:\n is not closed: identifier x_65 bound at Characters 16-17:\n is free") | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+let tm1 = .<fun x -> .< x >. >.;; | |
+(* | |
+val tm1 : ('a -> 'a code) code = .<fun x_66 -> .< x_66 >.>. | |
+*) | |
+!. tm1 10;; | |
+(* | |
+- : int code = .<(* CSP x_122 *) Obj.magic 10>. | |
+*) | |
+let 10 = !. (!. tm1 10);; | |
+ | |
+(* Generalization *) | |
+ | |
+let tg1 = !. ((fun x -> .<x>.) (ref []));; | |
+(* | |
+val tg1 : '_a list ref = {contents = []} | |
+ should not be polymorphic! | |
+*) | |
+let tg2 = !. .<ref []>.;; | |
+(* | |
+val tg2 : '_a list ref = {contents = []} | |
+ should not be polymorphic! | |
+*) | |
+ | |
+(* | |
+(* First-class polymorphism *) | |
+ | |
+(* Recall, in runcode.mli: | |
+ | |
+type 'a cde = {cde : 'c. ('c,'a) code} (* Type of the closed code *) | |
+ | |
+*) | |
+ | |
+(* In all previous versions of MetaOCaml, up to BER N004: | |
+ | |
+# Runcode.run;; | |
+- : 'a Runcode.cde -> 'a = <fun> | |
+# {Runcode.cde = .<1>.};; | |
+- : int Runcode.cde = .<1>. | |
+# Runcode.run {Runcode.cde = .<1>.};; | |
+- : int = 1 | |
+# .<{Runcode.cde = .<1>.}>.;; | |
+- : ('a, int Runcode.cde) code = .<{Runcode.cde = .<1>.}>. | |
+# !. .<{Runcode.cde = .<1>.}>.;; | |
+Characters 22-23: | |
+ !. .<{Runcode.cde = .<1>.}>.;; | |
+ ^ | |
+Error: This expression has type ('a, int) code | |
+ but an expression was expected of type ('b, int) code | |
+ | |
+Exception: Trx.TypeCheckingError. | |
+*) | |
+ | |
+(* Now *) | |
+let tfc1 = {Runcode.cde = .<1>.};; | |
+(* - : int Runcode.cde = .<1>. *) | |
+let 1 = Runcode.run {Runcode.cde = .<1>.};; | |
+ | |
+let tfc2 = .<{Runcode.cde = .<1>.}>.;; | |
+(* | |
+- : ('cl, int Runcode.cde) code = .<{Runcode.cde = .<1>.}>. | |
+*) | |
+let tfc3 = !. .<{Runcode.cde = .<1>.}>.;; | |
+(* - : int Runcode.cde = .<1>. *) | |
+let tfc4 = {Runcode.cde= .<{Runcode.cde = .<1>.}>.};; | |
+(* - : int Runcode.cde Runcode.cde = .<{Runcode.cde = .<1>.}>. *) | |
+let tfc5 = Runcode.run {Runcode.cde= .<{Runcode.cde = .<1>.}>.};; | |
+(* - : int Runcode.cde = .<1>. *) | |
+let 1 = Runcode.run (Runcode.run {Runcode.cde= .<{Runcode.cde = .<1>.}>.});; | |
+(* - : int = 1 *) | |
+*) | |
+ | |
+Printf.printf "\nAll Done\n";; | |
diff -Naur ocaml-4.02.1/metalib/test/simple_true.ml ocaml-ber-n102/metalib/test/simple_true.ml | |
--- ocaml-4.02.1/metalib/test/simple_true.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/simple_true.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,137 @@ | |
+(* Various simple tests, all should pass. Whenever an error is discovered, | |
+ please add here. | |
+*) | |
+open Runcode;; | |
+ | |
+(* From Problems.txt Wed Oct 2 08:39:04 BST 2002 | |
+ Occurrences of a csp value share the same instantiated type | |
+*) | |
+ | |
+let f1 x = x;; (* polymophic *) | |
+let t1 = .<(f1 1, f1 true)>.;; | |
+(* | |
+val t1 : (int * bool) code = .< | |
+ ((((* cross-stage persistent value (id: f1) *)) 1), | |
+ (((* cross-stage persistent value (id: f1) *)) true))>. | |
+*) | |
+let (1,true) = !. t1;; | |
+ | |
+(* From Problems.txt Thu Oct 24 09:55:36 BST 2002 | |
+ CSP at level n+2 gives Segmentation fault | |
+*) | |
+(* use f1 above *) | |
+let t2 = .<(.<f1 1>., .<f1 true>.)>.;; | |
+(* | |
+val t2 : (int code * bool code) code = .< | |
+ ((.< ((* cross-stage persistent value (id: f1) *)) 1 >.), | |
+ (.< ((* cross-stage persistent value (id: f1) *)) true >.))>. | |
+*) | |
+let 1 = !. (fst (!. t2));; | |
+let true = !. (snd (!. t2));; | |
+ | |
+(* From Problems.txt Mon Nov 25 10:10:32 GMT 2002 | |
+ CSP of array ops gives internal errors | |
+*) | |
+ | |
+let t3 = .<Array.get [|1|] 0>.;; | |
+(* | |
+val t3 : int code = .<[|1|].(0)>. | |
+*) | |
+let 1 = !. t3;; | |
+ | |
+(* From Problems.txt Tue Jan 20 12:18:00 GMTST 2004 | |
+ typechecker broken for csp ids, e.g. we get the wrong type | |
+ We get the incorrect typing (inner and outer code forced to be both 'a) | |
+ # .<fun x -> .<x>.>.;; | |
+*) | |
+ | |
+let t4 = .<fun x -> .<x>.>.;; | |
+(* | |
+val t4 : ('a -> 'a code) code = .<fun x_67 -> .< x_67 >.>. | |
+*) | |
+let true = !. ((!. t4) true);; | |
+ | |
+(* From Problems.txt Mon Jan 10 18:51:21 GMTST 2005 | |
+ CSP constants in Pervasives (and similar) are type checked only once for | |
+ a given occurrence. | |
+ # let f x = .< ref .~ x>. | |
+ in (!. (f .<3>.), !. (f .<1.3>.));; | |
+ This expression has type int but is here used with type float | |
+*) | |
+ | |
+let t5 = | |
+ let f x = .< ref .~ x>. | |
+ in (!. (f .<3>.), !. (f .<1.3>.));; | |
+ | |
+(* | |
+val t5 : int ref * float ref = ({contents = 3}, {contents = 1.3}) | |
+*) | |
+ | |
+(* From Problems.txt Tue Jan 18 14:08:52 GMTST 2005 | |
+ type aliases are not handled correctly in code, example: | |
+ # type a = int;; | |
+ # let f (x:a) = 1;; | |
+ # !. .<f 1>.;; | |
+*) | |
+type a = int;; | |
+let 1 = | |
+ let f (x:a) = 1 in | |
+ !. .<f 1>.;; | |
+ | |
+(* From Problems.txt Oct 3, 2006 Printing of records in brackets *) | |
+ | |
+let t7 = | |
+ let open Complex in | |
+ .<let x = {re=1.0; im=2.0} in | |
+ let y = {x with re = 2.0} in | |
+ y>. | |
+ ;; | |
+ | |
+(* | |
+val t7 : Complex.t code = .< | |
+ let x_14 = { Complex.re = 1.0; Complex.im = 2.0 } in | |
+ let y_15 = { x_14 with Complex.re = 2.0 } in y_15>. | |
+*) | |
+ | |
+let {Complex.re=2.0; im=2.0} = !. t7;; | |
+ | |
+(* First-class polymorphism *) | |
+(* | |
+let tfc1 = {Runcode.cde = .<1>.};; | |
+(* - : int Runcode.cde = .<1>. *) | |
+let 1 = Runcode.run {Runcode.cde = .<1>.};; | |
+ | |
+let tfc2 = .<{Runcode.cde = .<1>.}>.;; | |
+(* | |
+- : ('cl, int Runcode.cde) code = .<{Runcode.cde = .<1>.}>. | |
+*) | |
+let tfc3 = !. .<{Runcode.cde = .<1>.}>.;; | |
+(* - : int Runcode.cde = .<1>. *) | |
+let tfc4 = {Runcode.cde= .<{Runcode.cde = .<1>.}>.};; | |
+(* - : int Runcode.cde Runcode.cde = .<{Runcode.cde = .<1>.}>. *) | |
+let tfc5 = Runcode.run {Runcode.cde= .<{Runcode.cde = .<1>.}>.};; | |
+(* - : int Runcode.cde = .<1>. *) | |
+let 1 = Runcode.run (Runcode.run {Runcode.cde= .<{Runcode.cde = .<1>.}>.});; | |
+(* - : int = 1 *) | |
+*) | |
+ | |
+(* complex runs *) | |
+let tr1 = .<fun x -> .~(!. .<.<1>.>.)>.;; | |
+let 1 = (!. tr1) 42;; | |
+(* | |
+val tr1 : ('a -> int) code = .<fun x_17 -> 1>. | |
+*) | |
+ | |
+let tr1' = .<fun x -> .~(!. .<.<fun x -> x>.>.)>.;; | |
+(* | |
+val tr1' : ('a -> 'b -> 'b) code = .<fun x_70 x_71_72 -> x_71_72>. | |
+*) | |
+let 2 = (!. tr1') 1 2;; | |
+ | |
+let tr2 = .<fun x -> .~(let x = !. .<1>. in .<x>.)>.;; | |
+(* | |
+val tr2 : ('a -> int) code = .<fun x_73 -> 1>. | |
+*) | |
+let 1 = (!. tr2) 42;; | |
+ | |
+Printf.printf "\nAll Done\n";; | |
diff -Naur ocaml-4.02.1/metalib/test/t4.ml ocaml-ber-n102/metalib/test/t4.ml | |
--- ocaml-4.02.1/metalib/test/t4.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/t4.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,110 @@ | |
+ | |
+(* Author: Walid Taha and Cristiano Calcagno | |
+ Date: Fri Aug 31 03:03:11 EDT 2001 *) | |
+ | |
+open Runcode;; | |
+open T4types;; | |
+ | |
+let unJ(J i) = i | |
+let unF(F f) = f | |
+ | |
+let term0 = A(L("x",V"x"),I 7) | |
+ | |
+let term1 = A(R ("f","n", C(V "n",I 42,A (V "f",D (V "n")))),I 1000000) | |
+ | |
+exception Yiikes | |
+;; | |
+ | |
+let env0 = fun x -> raise Yiikes;; | |
+ | |
+let ext env x v = fun y -> if x=y then v else env y | |
+ | |
+let rec eval e env = | |
+match e with | |
+ I i -> J i | |
+| V s -> env s | |
+| A (e1,e2) -> (unF(eval e1 env)) (eval e2 env) | |
+| L (x,e) -> F (fun v -> eval e (ext env x v)) | |
+| D e -> J ((unJ (eval e env))-1) | |
+| C (e1,e2,e3) -> if (unJ(eval e1 env))=0 | |
+ then (eval e2 env) | |
+ else (eval e3 env) | |
+| R (f,x,e) -> F (let rec ff xx = eval e (ext (ext env x xx) | |
+ f (F ff)) | |
+ in ff) | |
+;; | |
+ | |
+let J 42 = eval term1 env0;; (* Unstaged *) | |
+ | |
+let rec eval' e env = | |
+match e with | |
+ I i -> .<J i>. | |
+| V s -> env s | |
+| A (e1,e2) -> .<(unF .~(eval' e1 env)) (.~(eval' e2 env))>. | |
+| L (x,e) -> .<F (fun v -> .~(eval' e (ext env x .<v>.)))>. | |
+| D e -> .<J (unJ .~(eval' e env) - 1)>. | |
+| C (e1,e2,e3) -> .< if (unJ .~(eval' e1 env)) = 0 | |
+ then .~(eval' e2 env) | |
+ else .~(eval' e3 env) >. | |
+| R (f,x,e) -> | |
+ .<F (let rec ff xx = .~(eval' e (ext (ext env x .<xx>.) f .<F ff>.)) in ff)>. | |
+;; | |
+ | |
+let stage1Running = eval' term1 env0;; | |
+ | |
+(* | |
+val stage1Running : ('a, dom) code = | |
+ .<((((* cross-stage persistent value (as id: unF) *)) | |
+ (F | |
+ (let rec ff_1 = | |
+ fun xx_2 -> | |
+ if ((((* cross-stage persistent value (as id: unJ) *)) xx_2) = 0) then | |
+ (J (42)) | |
+ else | |
+ ((((* cross-stage persistent value (as id: unF) *)) (F (ff_1))) | |
+ (J | |
+ ((((* cross-stage persistent value (as id: unJ) *)) xx_2) - 1))) in | |
+ ff_1))) (J (1000000)))>. | |
+ | |
+*) | |
+let compiling = !. .<fun () -> .~ stage1Running>.;; | |
+ | |
+let J 42 = compiling ();; | |
+ | |
+let unJ' = .<fun (J i) -> i>. | |
+let unF' = .<fun (F f) -> f>. | |
+ | |
+let rec eval'' e env = | |
+match e with | |
+ I i -> .<J i>. | |
+| V s -> env s | |
+| A (e1,e2) -> .<.~unF' .~(eval'' e1 env) .~(eval'' e2 env)>. | |
+| L (x,e) -> .<F (fun v -> .~(eval'' e (ext env x .<v>.)))>. | |
+| D e -> .<J (.~unJ' .~(eval'' e env) - 1)>. | |
+| C (e1,e2,e3) -> .<if .~unJ' .~(eval'' e1 env) = 0 | |
+ then .~(eval'' e2 env) | |
+ else .~(eval'' e3 env)>. | |
+| R (f,x,e) -> | |
+ .<F (let rec ff xx = .~(eval'' e (ext (ext env x .<xx>.) f (.<F ff>.))) in ff)>. | |
+;; | |
+ | |
+let stage1Running' = eval'' term1 env0;; | |
+ | |
+(* | |
+val stage1Running' : ('a, dom) code = | |
+ .<((fun F (f_2) -> f_2) | |
+ (F | |
+ (let rec ff_1 = | |
+ fun xx_2 -> | |
+ if (((fun J (i_1) -> i_1) xx_2) = 0) then (J (42)) | |
+ else | |
+ ((fun F (f_2) -> f_2) (F (ff_1)) | |
+ (J (((fun J (i_1) -> i_1) xx_2) - 1))) in | |
+ ff_1)) (J (1000000)))>. | |
+*) | |
+ | |
+let compiling' = !. .<fun () -> .~ stage1Running'>.;; | |
+ | |
+let J 42 = (compiling' ());; | |
+ | |
+Printf.printf "\n4.ml Done\n";; | |
diff -Naur ocaml-4.02.1/metalib/test/t4types.mli ocaml-ber-n102/metalib/test/t4types.mli | |
--- ocaml-4.02.1/metalib/test/t4types.mli 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/t4types.mli 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,17 @@ | |
+(* Data type declarations for the staged eval example by | |
+ Walid Taha and Cristiano Calcagno | |
+ | |
+In MetaOCaml N100, all data types used within brackets must be | |
+in separate .ml or .mli files | |
+*) | |
+ | |
+type exp = I of int | |
+ | V of string | |
+ | A of exp * exp | |
+ | L of string * exp | |
+ | D of exp | |
+ | C of exp * exp * exp | |
+ | R of string * string * exp | |
+ | |
+type dom = J of int | |
+ | F of (dom -> dom) | |
diff -Naur ocaml-4.02.1/metalib/test/test20.ml ocaml-ber-n102/metalib/test/test20.ml | |
--- ocaml-4.02.1/metalib/test/test20.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/test20.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,20 @@ | |
+open Bool2 | |
+ | |
+type ('a,'b,'c) unary = { | |
+ unow : 'b -> 'c ; | |
+ ulater : ('a,'b) code -> ('a, 'c) code | |
+} | |
+ | |
+module type SET = | |
+sig | |
+ type n | |
+ val to_string : ('a, n, string) unary | |
+end | |
+ | |
+module E2 (N : SET) = struct | |
+ let to_str p = match p with | |
+ | Now (x,y) -> Now ((N.to_string.unow x) ^ (N.to_string.unow y)) | |
+ | Later p -> Later (.< let (x, y) = .~p in | |
+ .~(N.to_string.ulater .<x>.) ^ | |
+ .~(N.to_string.ulater .<y>.) >. ) | |
+end | |
diff -Naur ocaml-4.02.1/metalib/test/test21.ml ocaml-ber-n102/metalib/test/test21.ml | |
--- ocaml-4.02.1/metalib/test/test21.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/test21.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,5 @@ | |
+(* this generates an error in BER-meta but not in meta-309-alpha-030 *) | |
+module Bool = struct module C = struct end end | |
+ | |
+let h = .<"" ^ "">. | |
+ | |
diff -Naur ocaml-4.02.1/metalib/test/test_levels.ml ocaml-ber-n102/metalib/test/test_levels.ml | |
--- ocaml-4.02.1/metalib/test/test_levels.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/test_levels.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,43 @@ | |
+(* Test for the maintenance of generalization levels. | |
+ The test submitted by Jun Inoue, Apr 24, 2014 | |
+ | |
+ Previously, when loaded in metaocaml top level, | |
+ it triggered assertion false in | |
+ typing/typecore.ml, in the local function lower_args | |
+ defined when type checking Pexp_apply. | |
+*) | |
+ | |
+let cde_eq c1 = () | |
+ | |
+type 'a cde = Cde of 'a | |
+ | |
+let get_cde x = | |
+ match x with | |
+ | Cde c -> () | |
+ | |
+type 'a glist = Cons of 'a | |
+ | |
+ | |
+(* If we move glist_eq below the let _ = ... part, we get: | |
+ | |
+ | Cons x -> true | |
+ ^^^^^^ | |
+Error: This pattern matches values of type 'a glist | |
+ but a pattern was expected which matches values of type 'a glist | |
+ The type constructor glist would escape its scope | |
+ | |
+ *) | |
+let glist_eq x = | |
+ match x with | |
+ | Cons x -> () | |
+ | |
+(* Invoking Runcode.run seems to be essential. Defining and using | |
+ let myrun : 'a code -> 'a = fun x -> failwith "" | |
+ doesn't provoke the bug. | |
+ *) | |
+let _ = Runcode.run .<()>.;; | |
+ | |
+(* This has to use glist_eq. Using get_cde instead won't cut it. *) | |
+let sumF env = glist_eq env;; | |
+ | |
+Printf.printf "\nAll Done\n";; | |
diff -Naur ocaml-4.02.1/metalib/test/test_path_a.ml ocaml-ber-n102/metalib/test/test_path_a.ml | |
--- ocaml-4.02.1/metalib/test/test_path_a.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/test_path_a.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,3 @@ | |
+(* Testing of setting of .cmo path | |
+ See test_path.ml for explanation *) | |
+type t = A | |
diff -Naur ocaml-4.02.1/metalib/test/test_path.ml ocaml-ber-n102/metalib/test/test_path.ml | |
--- ocaml-4.02.1/metalib/test/test_path.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/test_path.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,34 @@ | |
+(* Testing of setting of .cmo path *) | |
+(* The need for setting the path was pointed out by Nicolas Ojeda Bar. | |
+ The following is his simple example. | |
+ We assume that test_path_a.cmo and test_path_a.cmi are moved | |
+ to the tmp/ | |
+ | |
+ First of all, to even compile this file we need to pass the flag | |
+ -I /tmp to ocamlc or metaocamlc. Typechecker needs to know where to | |
+ find test_path.cmi. This is needed for compilation, before any code | |
+ is run. | |
+*) | |
+ | |
+ | |
+(* Now, if the code is type-checked and the executable is made, to run | |
+ it we still have to specify where to find test_path.cmi amd .cmo files. | |
+ Otherwise, we get the following *run-time* error. | |
+ | |
+Error: Unbound module Test_path_a | |
+ | |
+Fatal error: exception Failure("Error type-checking generated code: scope extrusion?") | |
+*) | |
+ | |
+(* | |
+let _ = | |
+ .< Test_path_a.A >. | |
+*) | |
+ | |
+(* This is needed to run the generated code *) | |
+let () = Runcode.add_search_path "/tmp" | |
+ | |
+let _ = | |
+ Runcode.run .< Test_path_a.A >. | |
+ | |
+let _ = Printf.printf "All Done\n" | |
diff -Naur ocaml-4.02.1/metalib/test/trivial.ml ocaml-ber-n102/metalib/test/trivial.ml | |
--- ocaml-4.02.1/metalib/test/trivial.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/test/trivial.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,1179 @@ | |
+(* Trivial tests of MetaOCaml, which are also regression tests *) | |
+open Runcode;; | |
+ | |
+3+2;; | |
+(* - : int = 5 *) | |
+let rec fact = function | 0 -> 1 | n -> n * fact (n-1);; | |
+(* val fact : int -> int = <fun> *) | |
+let 120 = fact 5;; | |
+ | |
+.<1>.;; | |
+(* - : int code = .<1>. *) | |
+.<"aaa">.;; | |
+(* - : string code = .<"aaa">. *) | |
+let 1 = !. .<1>.;; | |
+(* - : int = 1 *) | |
+ | |
+close_code .<1>.;; | |
+(* - : int Runcode.closed_code = .<1>. *) | |
+ | |
+(* Problem with special treatment of top-level identifiers by the | |
+ type checker | |
+*) | |
+List.rev;; | |
+(* - : 'a list -> 'a list = <fun> *) | |
+ | |
+(* Also check the generalization *) | |
+.<List.rev>.;; | |
+(* - : ('a list -> 'a list) code = .<List.rev>. *) | |
+ | |
+.<fun x -> .~(let y = x in y)>.;; | |
+(* | |
+Characters 22-23: | |
+ .<fun x -> .~(let y = x in y)>.;; | |
+ ^ | |
+Wrong level: variable bound at level 1 and used at level 0 | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+.<fun x -> 1 + .~(.<true>.)>.;; | |
+(* | |
+Characters 17-27: | |
+ .<fun x -> 1 + .~(.<true>.)>.;; | |
+ ^^^^^^^^^^ | |
+Error: This expression has type bool but an expression was expected of type | |
+ int | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+ | |
+(* CSP *) | |
+ | |
+let x = 1 in .<x>.;; | |
+(* | |
+- : int code = .<1>. | |
+*) | |
+let x = 1.0 in .<x>.;; | |
+(* | |
+- : float code = .<1.>. | |
+*) | |
+let x = true in .<x>.;; | |
+(* | |
+- : bool code = .<true>. | |
+*) | |
+let x = "aaa" in .<x>.;; | |
+(* | |
+- : string code = .<"aaa">. | |
+*) | |
+let x = 'a' in .<x>.;; | |
+(* | |
+- : char code = .<'a'>. | |
+*) | |
+let x = ['a'] in .<x>.;; | |
+(* | |
+- : char list code = .<(* CSP x *)>. | |
+*) | |
+ | |
+let l x = .<x>.;; (* polymorphic *) | |
+(* val l : 'a -> 'a code = <fun> *) | |
+l 1;; | |
+(* | |
+- : int code = .<(* CSP x *) Obj.magic 1>. | |
+*) | |
+let 1 = !. (l 1);; | |
+l 1.0;; (* better printing in N100 *) | |
+(* | |
+- : float code = .<1.>. | |
+*) | |
+let 1.0 = !. (l 1.0);; | |
+l [];; (* serializable code in N102 *) | |
+(* | |
+- : 'a list code = .<(* CSP x *) Obj.magic 0>. | |
+*) | |
+let [] = !. (l []);; | |
+ | |
+l (fun x -> x + 1);; | |
+(* | |
+Characters 12-13: | |
+ l (fun x -> x + 1);; | |
+ ^ | |
+Warning 22: The CSP value is a closure or too deep to serialize | |
+- : (int -> int) code = .<(* CSP x *)>. | |
+*) | |
+ | |
+.<List.rev>.;; | |
+(* | |
+- : ('a list -> 'a list) code = .<List.rev>. | |
+*) | |
+ | |
+.<Array.get>.;; | |
+(* | |
+- : ('a array -> int -> 'a) code = .<Array.get>. | |
+*) | |
+.<(+)>.;; | |
+(* | |
+- : (int -> int -> int) code = .<Pervasives.(+)>. | |
+*) | |
+ | |
+ | |
+let x = true in .<assert x>.;; | |
+(* | |
+- : unit code = .<assert true>. | |
+*) | |
+!. .<assert (2>1)>.;; | |
+ | |
+(* Applications and labels *) | |
+.<succ 1>.;; | |
+(* | |
+- : int code = .<Pervasives.succ 1>. | |
+*) | |
+ | |
+let 2 = !. .<succ 1>.;; | |
+ | |
+.<1 + 2>.;; | |
+(* | |
+- : int code = .<(1 + 2)>. | |
+*) | |
+let 3 = !. .<(1 + 2)>.;; | |
+ | |
+.<String.length "abc">.;; | |
+(* | |
+- : int code = .<String.length "abc">. | |
+*) | |
+let 3 = | |
+ !. .<String.length "abc">.;; | |
+ | |
+.<StringLabels.sub ~pos:1 ~len:2 "abc">.;; | |
+(* | |
+- : string code = .<(StringLabels.sub "abc" ~pos:1 ~len:2>. | |
+*) | |
+let "bc" = | |
+ !. .<StringLabels.sub ~pos:1 ~len:2 "abc">.;; | |
+ | |
+.<StringLabels.sub ~len:2 ~pos:1 "abc">.;; | |
+(* | |
+- : string code = .<(StringLabels.sub "abc" ~pos:1 ~len:2>. | |
+*) | |
+let "bc" = | |
+ !. .<StringLabels.sub ~len:2 ~pos:1 "abc">.;; | |
+ | |
+(* Nested brackets and escapes and run *) | |
+.<.<1>.>.;; | |
+(* - : int code code = .<.< 1 >.>. *) | |
+!. .<.<1>.>.;; | |
+(* - : int code = .<1>. *) | |
+let 1 = !. !. .<.<1>.>.;; | |
+(* - : int = 1 *) | |
+.<!. .<1>.>.;; | |
+(* | |
+- : int code = .<Runcode.(!.) (.< 1 >.)>. | |
+*) | |
+let 1 = !. .<!. .<1>.>.;; | |
+.<1 + .~(let x = 2 in .<x>.)>.;; | |
+(* | |
+- : int code = .<1 + 2>. | |
+*) | |
+let x = .< 2 + 4 >. in .< .~ x + .~ x >. ;; | |
+(* | |
+- : int code = .<(2 + 4) + (2 + 4)>. | |
+*) | |
+let 12 = !. (let x = .< 2 + 4 >. in .< .~ x + .~ x >. );; | |
+ | |
+.<1 + .~(let x = 2 in .<.<x>.>.)>.;; | |
+(* | |
+ .<1 + .~(let x = 2 in .<.<x>.>.)>.;; | |
+ ^ | |
+Error: This expression has type 'a code | |
+ but an expression was expected of type int | |
+*) | |
+print_endline "Error was expected";; | |
+.<1 + !. .~(let x = 2 in .<.<x>.>.)>.;; | |
+(* | |
+- : int code = .<1 + (Runcode.(!.) .< 2 >.)>. | |
+*) | |
+let 3 = !. .<1 + !. .~(let x = 2 in .<.<x>.>.)>.;; | |
+!. .<1 + .~ (.~(let x = 2 in .<.<x>.>.))>.;; | |
+(* | |
+Characters 12-40: | |
+ !. .<1 + .~ (.~(let x = 2 in .<.<x>.>.))>.;; | |
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
+Error: Wrong level: escape at level 0 | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+.<.<.~(.<1>.)>.>.;; | |
+(* | |
+- : int code code = .<.< .~(.< 1 >.) >.>. | |
+- : ('cl, ('cl0, int) code) code = .<.<.~(.<1>.)>.>. | |
+*) | |
+!. .<.<.~(.<1>.)>.>.;; | |
+(* | |
+- : int code = .<1>. | |
+*) | |
+let 1 = !. !. .<.<.~(.<1>.)>.>.;; | |
+ | |
+.<.<.~.~(.<.<1>.>.)>.>.;; | |
+(* | |
+- : int code code = .<.< .~(.< 1 >.) >.>. | |
+- : ('cl, ('cl0, int) code) code = .<.<.~(.<1>.)>.>. | |
+*) | |
+let 1 = !. !. .<.<.~.~(.<.<1>.>.)>.>.;; | |
+ | |
+(* Nested brackets and escapes on the same identifier *) | |
+let x = .<1>. in .<.~x>.;; | |
+(* | |
+- : int code = .<1>. | |
+*) | |
+let 1 = !. (let x = .<1>. in .<.~x>.);; | |
+ | |
+let x = .<1>. in .<.~.<x>.>.;; | |
+(* | |
+- : int code code = .<(* CSP x *)>. | |
+*) | |
+!. (let x = .<1>. in .<.~.<x>.>.);; | |
+(* | |
+- : int code = .<1>. | |
+*) | |
+let 1 = !. !. (let x = .<1>. in .<.~.<x>.>.);; | |
+ | |
+let x = .<1>. in .<.<.~x>.>.;; | |
+(* | |
+- : int code code = .<.< .~((* CSP x *)) >.>. | |
+*) | |
+let 1 = !. !. (let x = .<1>. in .<.<.~x>.>.);; | |
+ | |
+.<.<.~.<List.rev>.>.>.;; | |
+(* | |
+- : ('a list -> 'a list) code code = .<.< .~(.< List.rev >.) >.>. | |
+*) | |
+!. .<.<.~.<List.rev>.>.>.;; | |
+(* | |
+- : ('_a list -> '_a list) code = .<List.rev>. | |
+*) | |
+let [3;2;1] = !. !. .<.<.~.<List.rev>.>.>. [1;2;3];; | |
+ | |
+(* we use a sequence internally to represent brackets and escapes | |
+ in a Typedtree | |
+*) | |
+.<.<begin assert true; 1 end>.>.;; | |
+(* | |
+- : int code code = .<.< assert true; 1 >.>. | |
+*) | |
+let 1 = !. !. .<.<begin assert true; 1 end>.>.;; | |
+ | |
+.<.~(.<begin assert true; 1 end>.)>.;; | |
+(* | |
+- : int code = .<assert true; 1>. | |
+*) | |
+let x = .<begin assert true; 1 end>. in .<.~x>.;; | |
+(* | |
+- : int code = .<assert true; 1>. | |
+*) | |
+let x = .<begin assert true; 1 end>. in .<.~(assert true; x)>.;; | |
+(* | |
+- : int code = .<assert true; 1>. | |
+*) | |
+let 1 = | |
+ let x = .<begin assert true; 1 end>. in !. .<.~(assert true; x)>.;; | |
+ | |
+let x = .<begin assert true; 1 end>. in .<.~(ignore(!. x); x)>.;; | |
+(* | |
+- : int code = .<assert true; 1>. | |
+*) | |
+ | |
+ | |
+(* Lazy *) | |
+.<lazy 1>.;; | |
+(* | |
+- : int lazy_t code = .<lazy 1>. | |
+*) | |
+let 1 = Lazy.force (!. .<lazy 1>.);; | |
+ | |
+(* Tuples *) | |
+.<(1,"abc")>.;; | |
+(* | |
+- : (int * string) code = .<(1, "abc")>. | |
+*) | |
+.<(1,"abc",'d')>.;; | |
+(* | |
+- : (int * string * char) code = .<(1, "abc", 'd')>. | |
+*) | |
+let "abc" = | |
+ match !. .<(1,"abc",'d')>. with (_,x,_) -> x;; | |
+ | |
+(* Arrays *) | |
+.<[||]>.;; | |
+(* | |
+- : 'a array code = .<[||]>. | |
+*) | |
+let x = .<1+2>. in .<[|.~x;.~x|]>.;; | |
+(* | |
+- : int array code = .<[|(1 + 2);(1 + 2)|]>. | |
+*) | |
+ | |
+(* Constructors and enforcing externality *) | |
+.<raise Not_found>.;; | |
+(* | |
+- : 'a code = .<(raise (Not_found)>. | |
+*) | |
+.<raise (Scan_failure "")>.;; | |
+(* | |
+Characters 8-25: | |
+ .<raise (Scan_failure "")>.;; | |
+ ^^^^^^^^^^^^^^^^^ | |
+Error: Unbound constructor Scan_failure | |
+*) | |
+print_endline "Error was expected";; | |
+.<raise (Scanf.Scan_failure "")>.;; | |
+(* | |
+- : 'a code = .<(raise (Scanf.Scan_failure (""))>. | |
+*) | |
+open Scanf;; | |
+.<raise (Scan_failure "")>.;; | |
+(* | |
+- : 'a code = .<Pervasives.raise (Scanf.Scan_failure "")>. | |
+*) | |
+!. .<raise (Scan_failure "")>.;; | |
+(* | |
+Exception: Scanf.Scan_failure "". | |
+*) | |
+print_endline "Exception was expected";; | |
+ | |
+ | |
+.<true>.;; | |
+(* | |
+- : bool code = .<true>. | |
+*) | |
+.<Some 1>.;; | |
+(* | |
+- : int option code = .<Some 1>. | |
+*) | |
+.<Some [1]>.;; | |
+(* | |
+- : int list option code = .<Some [1]>. | |
+*) | |
+let Some [1] = !. .<Some [1]>.;; | |
+.<None>.;; | |
+(* | |
+- : 'a option code = .<None>. | |
+*) | |
+let None = !. .<None>.;; | |
+ | |
+.<Genlex.Int 1>.;; | |
+(* | |
+- : Genlex.token code = .<Genlex.Int 1>. | |
+*) | |
+open Genlex;; | |
+.<Int 1>.;; | |
+(* | |
+- : Genlex.token code = .<Genlex.Int 1>. | |
+*) | |
+let Int 1 = !. .<Int 1>.;; | |
+ | |
+module Foo = struct exception E end;; | |
+.<raise Foo.E>.;; | |
+(* | |
+Characters 8-13: | |
+ .<raise Foo.E>.;; | |
+ ^^^^^ | |
+Exception (extension) Foo.E cannot be used within brackets. Put into a separate file. | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+type foo = Bar;; | |
+.<Bar>.;; | |
+(* | |
+Characters 2-5: | |
+ .<Bar>.;; | |
+ ^^^ | |
+Unqualified constructor Bar cannot be used within brackets. Put into a separate file. | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+module Foo = struct type foo = Bar end;; | |
+.<Foo.Bar>.;; | |
+(* | |
+Characters 2-9: | |
+ .<Foo.Bar>.;; | |
+ ^^^^^^^ | |
+Constructor Bar cannot be used within brackets. Put into a separate file. | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+(* Records *) | |
+ | |
+.<{Complex.re = 1.0; im = 2.0}>.;; | |
+(* | |
+- : Complex.t code = .<{ Complex.re = 1.0; Complex.im = 2.0 }>. | |
+*) | |
+let {Complex.re = 1.; Complex.im = -2.} = | |
+ Complex.conj (!. .<{Complex.re = 1.0; im = 2.0}>.);; | |
+let x = {Complex.re = 1.0; im = 2.0} in .<x.re>.;; | |
+(* | |
+Characters 44-46: | |
+ let x = {Complex.re = 1.0; im = 2.0} in .<x.re>.;; | |
+ ^^ | |
+Warning 40: re was selected from type Complex.t. | |
+It is not visible in the current scope, and will not | |
+be selected if the type becomes unknown. | |
+- : float code = .<(* CSP x *).Complex.re>. | |
+*) | |
+ | |
+let x = {Complex.re = 1.0; im = 2.0} in .<x.Complex.re>.;; | |
+(* | |
+- : float code = | |
+.<((* cross-stage persistent value (as id: x) *)).Complex.re>. | |
+*) | |
+let 1.0 = !.(let x = {Complex.re = 1.0; im = 2.0} in .<x.Complex.re>.);; | |
+let x = ref 1 in .<x.contents>.;; (* Pervasive record *) | |
+(* | |
+- : int code = .<(* CSP x *).Pervasives.contents>. | |
+*) | |
+let 1 = !.(let x = ref 1 in .<x.contents>.);; | |
+let x = ref 1 in .<x.contents <- 2>.;; | |
+(* | |
+- : unit code = | |
+.<((* cross-stage persistent value (as id: x) *)).contents <- 2>. | |
+*) | |
+let x = ref 1 in (!. .<x.contents <- 2>.); x;; | |
+(* - : int ref = {contents = 2} *) | |
+ | |
+open Complex;; | |
+.<{re = 1.0; im = 2.0}>.;; | |
+(* | |
+- : Complex.t code = .<{Complex.re = 1.0; Complex.im = 2.0}>. | |
+*) | |
+let 5.0 = norm (!. .<{re = 3.0; im = 4.0}>.);; | |
+let x = {re = 1.0; im = 2.0} in .<x.re>.;; | |
+(* | |
+- : float code = | |
+.<((* cross-stage persistent value (as id: x) *)).Complex.re>. | |
+*) | |
+let 1.0 = !.(let x = {re = 1.0; im = 2.0} in .<x.re>.);; | |
+ | |
+type foo = {fool : int};; | |
+.<{fool = 1}>.;; | |
+(* | |
+Characters 3-7: | |
+ .<{fool = 1}>.;; | |
+ ^^^^ | |
+Unqualified label fool cannot be used within brackets. Put into a separate file. | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+(* Conditional *) | |
+ | |
+.<if true then 1 else 2>.;; | |
+(* - : int code = .<if true then 1 else 2>. *) | |
+.<if Some 1 = None then print_string "weird">.;; | |
+(* | |
+- : unit code = .<if (Some 1) = None then Pervasives.print_string "weird">. | |
+*) | |
+let () = !. .<if Some 1 = None then print_string "weird">.;; | |
+ | |
+(* Polymorphic variants *) | |
+.<`Foo>.;; | |
+(* | |
+- : [> `Foo ] code = .<`Foo>. | |
+*) | |
+.<`Bar 1>.;; | |
+(* | |
+- : [> `Bar of int ] code = .<`Bar 1>. | |
+*) | |
+let 1 = match !. .<`Bar 1>. with `Bar x -> x ;; | |
+ | |
+(* Some support for modules *) | |
+let f = fun x -> .<x # foo>.;; | |
+(* | |
+val f : < foo : 'a; .. > -> 'a code = <fun> | |
+*) | |
+let x = object method foo = 1 end;; | |
+(* | |
+val x : < foo : int > = <obj> | |
+*) | |
+f x;; | |
+(* | |
+- : int code = .<(* CSP x *)#foo>. | |
+*) | |
+let 1 = !. (f x);; | |
+ | |
+(* Local open *) | |
+.<Complex.(norm {re=3.0; im = 4.0})>.;; | |
+(* | |
+- : float code = .<Complex.norm { Complex.re = 3.0; Complex.im = 4.0 }>. | |
+*) | |
+ | |
+let 5.0 = !. .<Complex.(norm {re=3.0; im = 4.0})>.;; | |
+ | |
+.<let open Complex in norm {re=4.0; im = 3.0}>.;; | |
+(* | |
+- : float code = .<Complex.norm { Complex.re = 4.0; Complex.im = 3.0 }>. | |
+*) | |
+ | |
+let 5.0 = !. .<let open Complex in norm {re=4.0; im = 3.0}>.;; | |
+ | |
+(* For-loop *) | |
+ | |
+.<for i=1 to 5 do Printf.printf "ok %d %d\n" i (i+1) done>.;; | |
+(* | |
+- : unit code = .< | |
+for i_1 = 1 to 5 do Printf.printf "ok %d %d\n" i_1 (i_1 + 1) done>. | |
+*) | |
+!. .<for i=1 to 5 do Printf.printf "ok %d %d\n" i (i+1) done>.;; | |
+(* | |
+ok 1 2 | |
+ok 2 3 | |
+ok 3 4 | |
+ok 4 5 | |
+ok 5 6 | |
+*) | |
+ | |
+.<for i=5 downto 1 do Printf.printf "ok %d %d\n" i (i+1) done>.;; | |
+(* | |
+- : unit code = .< | |
+for i_3 = 5 downto 1 do Printf.printf "ok %d %d\n" i_3 (i_3 + 1) done>. | |
+*) | |
+!. .<for i=5 downto 1 do Printf.printf "ok %d %d\n" i (i+1) done>.;; | |
+(* | |
+ok 5 6 | |
+ok 4 5 | |
+ok 3 4 | |
+ok 2 3 | |
+ok 1 2 | |
+*) | |
+ | |
+.<for i=1 to 2 do for j=1 to 3 do Printf.printf "ok %d %d\n" i j done done>.;; | |
+(* | |
+- : unit code = .< | |
+for i_5 = 1 to 2 do | |
+ for j_6 = 1 to 3 do Printf.printf "ok %d %d\n" i_5 j_6 done | |
+done>. | |
+*) | |
+!. .<for i=1 to 2 do | |
+ for j=1 to 3 do Printf.printf "ok %d %d\n" i j done done>.;; | |
+(* | |
+ok 1 1 | |
+ok 1 2 | |
+ok 1 3 | |
+ok 2 1 | |
+ok 2 2 | |
+ok 2 3 | |
+*) | |
+ | |
+let c = .<for i=1 to 2 do .~(let x = .<i>. in | |
+ .<for i=1 to 3 do Printf.printf "ok %d %d\n" i .~x done>.) done>.;; | |
+(* | |
+val c : unit code = .< | |
+ for i_9 = 1 to 2 do | |
+ for i_10 = 1 to 3 do Printf.printf "ok %d %d\n" i_10 i_9 done | |
+ done>. | |
+*) | |
+!. c;; | |
+(* | |
+ok 1 1 | |
+ok 2 1 | |
+ok 3 1 | |
+ok 1 2 | |
+ok 2 2 | |
+ok 3 2 | |
+*) | |
+ | |
+(* Anonymous loop variable (new in 4.02?) *) | |
+.<for _ = 1 to 3 do print_string "ok" done>.;; | |
+(* | |
+- : unit code = .<for _for_11 = 1 to 3 do Pervasives.print_string "ok" done>. | |
+*) | |
+!. .<for _ = 1 to 3 do print_string "ok" done>.;; | |
+(* | |
+okokok- : unit = () | |
+*) | |
+ | |
+(* Scope extrusion test *) | |
+ | |
+.<for i=1 to 10 do .~(let _ = !. .<0>. in .<()>.) done>.;; | |
+(* | |
+- : unit code = .<for i_2 = 1 to 10 do () done>. | |
+*) | |
+.<for i=1 to 10 do .~(let _ = !. .<i>. in .<()>.) done>.;; | |
+(* | |
+Exception: | |
+Failure | |
+ "The code built at Characters 6-7:\n .<for i=1 to 10 do .~(let _ = !. .<i>. in .<()>.) done>.;;\n ^\n is not closed: identifier i_3 bound at Characters 6-7:\n .<for i=1 to 10 do .~(let _ = !. .<i>. in .<()>.) done>.;;\n ^\n is free". | |
+ | |
+Was: | |
+Characters 14-22: | |
+ .<fun x -> .~(!. .<x>.; .<1>.)>.;; | |
+ ^^^^^^^^ | |
+Error: !. error: 'cl not generalizable in ('cl, 'a) code | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<0>.; .<()>.) done>.; | |
+ .<for i=1 to 5 do ignore (.~(!r)) done>.;; | |
+(* | |
+- : unit code = .<for i_13 = 1 to 5 do Pervasives.ignore 0 done>. | |
+*) | |
+ | |
+let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i>.; .<()>.) done>.; | |
+ .<for i=1 to 5 do ignore (.~(!r)) done>.;; | |
+(* | |
+Exception: | |
+Failure | |
+ "Scope extrusion detected at Characters 110-125:\n .<for i=1 to 5 do ignore (.~(!r)) done>.;;\n ^^^^^^^^^^^^^^^\n for code built at Characters 27-28:\n let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i>.; .<()>.) done>.; \n ^\n for the identifier i_3 bound at Characters 27-28:\n let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i>.; .<()>.) done>.; \n ^\n". | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+(* Better error message *) | |
+let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i+1>.; .<()>.) done>.; | |
+ .<for i=1 to 5 do ignore (.~(!r)) done>.;; | |
+ | |
+(* | |
+Characters 21-70: | |
+ let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i+1>.; .<()>.) done>.; | |
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
+Warning 10: this expression should have type unit. | |
+Exception: | |
+Failure | |
+ "Scope extrusion detected at Characters 112-127:\n .<for i=1 to 5 do ignore (.~(!r)) done>.;;\n ^^^^^^^^^^^^^^^\n for code built at Characters 49-52:\n let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i+1>.; .<()>.) done>.; \n ^^^\n for the identifier i_16 bound at Characters 27-28:\n let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i+1>.; .<()>.) done>.; \n ^\n". | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+(* Simple functions *) | |
+.<fun x -> x>.;; | |
+(* | |
+- : ('a -> 'a) code = .<fun x_7 -> x_7>. | |
+*) | |
+let 42 = (!. .<fun x -> x>.) 42;; | |
+ | |
+.<fun x y -> x + y>.;; | |
+(* | |
+- : (int -> int -> int) code = .<fun x_9 y_10 -> x_9 + y_10>. | |
+*) | |
+let 5 = (!. .<fun x y -> x + y>.) 2 3;; | |
+ | |
+.<fun x -> fun x -> x + x >.;; | |
+(* | |
+- : ('a -> int -> int) code = .<fun x_13 x_14 -> x_14 + x_14>. | |
+*) | |
+let 8 = !. .<fun x -> fun x -> x + x >. 3 4;; | |
+ | |
+(* Testing hygiene *) | |
+let eta f = .<fun x -> .~(f .<x>.)>.;; | |
+(* | |
+val eta : ('a code -> 'b code) -> ('a -> 'b) code = <fun> | |
+*) | |
+eta (fun x -> .<1 + .~x>.);; | |
+(* | |
+- : (int -> int) code = .<fun x_17 -> 1 + x_17>. | |
+*) | |
+eta (fun y -> .<fun x -> x + .~y>.);; | |
+(* | |
+- : (int -> int -> int) code = .<fun x_18 x_19 -> x_19 + x_18>. | |
+*) | |
+let 5 = (!. (eta (fun y -> .<fun x -> x + .~y>.))) 2 3;; | |
+ | |
+(* new identifiers must be generated at run-time *) | |
+let rec fhyg = fun y -> function | |
+ | 0 -> y | |
+ | n -> .<(fun x -> .~(fhyg .<.~y + x>. (n-1))) n>.;; | |
+(* | |
+val fhyg : int -> int code = <fun> | |
+*) | |
+fhyg .<1>. 3;; | |
+(* | |
+- : int code = .< | |
+(fun x_7 -> (fun x_8 -> (fun x_9 -> ((1 + x_7) + x_8) + x_9) 1) 2) 3>. | |
+*) | |
+let 7 = !. (fhyg .<1>. 3);; | |
+ | |
+(* pattern-matching, general functions *) | |
+ | |
+.<fun () -> 1>.;; | |
+(* - : (unit -> int) code = .<fun () -> 1>. *) | |
+!. .<fun () -> 1>.;; | |
+(* - : unit -> int = <fun> *) | |
+let 1 = (!. .<fun () -> 1>.) ();; | |
+ | |
+.<function true -> 1 | false -> 0>.;; | |
+(* | |
+- : (bool -> int) code = .<function | true -> 1 | false -> 0>. | |
+*) | |
+let 1 = (!. .<function true -> 1 | false -> 0>.) true;; | |
+ | |
+.<fun (true,[]) -> 1>.;; | |
+(* | |
+- : (bool * 'a list -> int) code = .<fun (true, []) -> 1>. | |
+*) | |
+(!. .<fun (true,[]) -> 1>.) (true,[1]);; | |
+(* | |
+Exception: Match_failure ("//toplevel//", 1, 6). | |
+*) | |
+print_endline "Error was expected";; | |
+let 1 = (!. .<fun (true,[]) -> 1>.) (true,[]);; | |
+ | |
+.<fun [|true;false;false|] -> 1>.;; | |
+(* | |
+- : (bool array -> int) code = .<fun [|true; false; false|] -> 1>. | |
+*) | |
+let 1 = (!. .<fun [|true;false;false|] -> 1>.) [|true;false;false|];; | |
+ | |
+.<function `F 1 -> true | _ -> false>.;; | |
+(* | |
+- : ([> `F of int ] -> bool) code = .<function | `F 1 -> true | _ -> false>. | |
+*) | |
+let true = (!. .<function `F 1 -> true | _ -> false>.) (`F 1);; | |
+.<function `F 1 | `G 2 -> true | _ -> false>.;; | |
+(* | |
+- : ([> `F of int | `G of int ] -> bool) code = .< | |
+function | `F 1|`G 2 -> true | _ -> false>. | |
+*) | |
+ | |
+.<function (1,"str") -> 1 | (2,_) -> 2>.;; | |
+(* | |
+- : (int * string -> int) code = .<function | (1,"str") -> 1 | (2,_) -> 2>. | |
+*) | |
+let 1 = (!. .<function (1,"str") -> 1 | (2,_) -> 2>.) (1,"str");; | |
+let 2 = (!. .<function (1,"str") -> 1 | (2,_) -> 2>.) (2,"str");; | |
+let 1 = (!. .<fun [1;2] -> 1>.) [1;2];; | |
+ | |
+let 2 = (!. .<function None -> 1 | Some [1] -> 2>.) (Some [1]);; | |
+ | |
+let 2 = (!. .<function (Some (Some true)) -> 1 | _ -> 2>.) (Some None);; | |
+let 1 = (!. .<function (Some (Some true)) -> 1 | _ -> 2>.) (Some (Some true));; | |
+let 2 = (!. .<function (Some (Some true)) -> 1 | _ -> 2>.) (Some (Some false));; | |
+let 2 = (!. .<function (Some (Some true)) -> 1 | _ -> 2>.) None;; | |
+ | |
+open Complex;; | |
+.<function {re=1.0} -> 1 | {im=2.0; re = 2.0} -> 2 | {im=_} -> 3>.;; | |
+(* | |
+- : (Complex.t -> int) code = .< | |
+function | |
+| { Complex.re = 1.0 } -> 1 | |
+| { Complex.re = 2.0; Complex.im = 2.0 } -> 2 | |
+| { Complex.im = _ } -> 3>. | |
+*) | |
+ | |
+let 1 = (!. .<function {re=1.0} -> 1 | {im=2.0; re = 2.0} -> 2 | {im=_} -> 3>.) | |
+ {re=1.0; im=2.0};; | |
+let 2 = (!. .<function {re=1.0} -> 1 | {im=2.0; re = 2.0} -> 2 | {im=_} -> 3>.) | |
+ {re=2.0; im=2.0};; | |
+(* - : int = 2 *) | |
+let 3 = (!. .<function {re=1.0} -> 1 | {im=2.0; re = 2.0} -> 2 | {im=_} -> 3>.) | |
+ {re=2.0; im=3.0};; | |
+ | |
+(* General functions *) | |
+ | |
+(* Non-binding pattern *) | |
+.<fun () -> 1>.;; | |
+(* | |
+- : (unit -> int) code = .<fun () -> 1>. | |
+*) | |
+let 1 = !. .<fun () -> 1>. ();; | |
+ | |
+(* .<fun (type a) () -> 1>.;; *) | |
+ | |
+.<fun _ -> true>.;; | |
+(* - : ('a -> bool) code = .<fun _ -> true>. *) | |
+let true = !. .<fun _ -> true>. 1;; | |
+ | |
+.<fun (x,y) -> x + y>.;; | |
+(* | |
+- : (int * int -> int) code = .<fun (x_1,y_2) -> x_1 + y_2>. | |
+*) | |
+let 5 = (!. .<fun (x,y) -> x + y>.) (2,3);; | |
+.<function (Some x) as y -> x | _ -> 2>.;; | |
+(* | |
+- : (int option -> int) code = .<function | Some x_5 as y_6 -> x_5 | _ -> 2>. | |
+*) | |
+let 1 = (!. .<function (Some x) as y -> x | _ -> 2>.) (Some 1);; | |
+let 2 = (!. .<function (Some x) as y -> x | _ -> 2>.) None;; | |
+.<function [x;y;z] -> x - y + z | [x;y] -> x - y>.;; | |
+(* | |
+- : (int list -> int) code = .< | |
+function | |
+| x_11::y_12::z_13::[] -> (x_11 - y_12) + z_13 | |
+| x_14::y_15::[] -> x_14 - y_15>. | |
+*) | |
+let 2 = (!. .<function [x;y;z] -> x - y + z | [x;y] -> x - y>.) [1;2;3];; | |
+ | |
+ (* OR patterns *) | |
+.<function ([x;y] | [x;y;_]) -> x - y>.;; | |
+(* | |
+- : (int list -> int) code = .< | |
+fun (x_21::y_22::[]|x_21::y_22::_::[]) -> x_21 - y_22>. | |
+*) | |
+let -1 = (!. .<function ([x;y] | [x;y;_]) -> x - y>.) [1;2];; | |
+let -1 = (!. .<function ([x;y] | [x;y;_]) -> x - y>.) [1;2;3];; | |
+(!. .<function ([x;y] | [x;y;_]) -> x - y>.) [1;2;3;4];; | |
+(* Exception: Match_failure ("//toplevel//", 1, 6). *) | |
+print_endline "Error was expected";; | |
+ | |
+.<function ([x;y] | [x;y;_]| [y;x;_;_]) -> x - y>.;; | |
+(* | |
+- : (int list -> int) code = .< | |
+fun (x_29::y_30::[]|x_29::y_30::_::[]|y_30::x_29::_::_::[]) -> x_29 - y_30>. | |
+*) | |
+let -1 = (!. .<function ([x;y] | [x;y;_]| [y;x;_;_]) -> x - y>.) [1;2];; | |
+let -1 = (!. .<function ([x;y] | [x;y;_]| [y;x;_;_]) -> x - y>.) [1;2;3];; | |
+let 1 = (!. .<function ([x;y] | [x;y;_]| [y;x;_;_]) -> x - y>.) [1;2;3;4];; | |
+ | |
+.<function (`F x | `G x) -> x | `E x -> x>.;; | |
+(* | |
+- : ([< `E of 'a | `F of 'a | `G of 'a ] -> 'a) code = .< | |
+function | `F x_37|`G x_37 -> x_37 | `E x_38 -> x_38>. | |
+*) | |
+let 2 = (!. .<function (`F x | `G x) -> x | `E x -> x>.) (`F 2);; | |
+open Complex;; | |
+.<function {re=x} -> x | {im=x; re=y} -> x -. y>.;; | |
+(* | |
+- : (Complex.t -> float) code = .< | |
+function | |
+| { Complex.re = x_41 } -> x_41 | |
+| { Complex.re = y_42; Complex.im = x_43 } -> x_43 -. y_42>. | |
+*) | |
+.<function {re=x; im=2.0} -> x | {im=x; re=y} -> x -. y>.;; | |
+(* | |
+- : (Complex.t -> float) code = .< | |
+function | |
+| { Complex.re = x_44; Complex.im = 2.0 } -> x_44 | |
+| { Complex.re = y_45; Complex.im = x_46 } -> x_46 -. y_45>. | |
+*) | |
+let 0. = (!. .<function {re=x; im=2.0} -> x | {im=x; re=y} -> x -. y>.) | |
+ {re=1.0; im=1.0};; | |
+let 1. = (!. .<function {re=x; im=2.0} -> x | {im=x; re=y} -> x -. y>.) | |
+ {re=1.0; im=2.0};; | |
+.<function (Some x) as y when x > 0 -> y | _ -> None>.;; | |
+(* | |
+- : (int option -> int option) code = .< | |
+function | Some x_53 as y_54 when x_53 > 0 -> y_54 | _ -> None>. | |
+*) | |
+let Some 1 = (!. .<function (Some x) as y when x > 0 -> y | _ -> None>.) | |
+ (Some 1);; | |
+let None = (!. .<function (Some x) as y when x > 0 -> y | _ -> None>.) | |
+ (Some 0);; | |
+ | |
+(* pattern-matching *) | |
+.<match 1 with 1 -> true>.;; | |
+(* | |
+- : bool code = .<match 1 with | 1 -> true>. | |
+*) | |
+let true = !. .<match 1 with 1 -> true>.;; | |
+ | |
+.<match (1,2) with (1,x) -> true | x -> false>.;; | |
+(* | |
+- : bool code = .<match (1, 2) with | (1,x_5) -> true | x_6 -> false>. | |
+*) | |
+.<match [1;2] with [x] -> x | [x;y] -> x + y>.;; | |
+(* | |
+- : int code = .< | |
+match [1; 2] with | x_7::[] -> x_7 | x_8::y_9::[] -> x_8 + y_9>. | |
+*) | |
+let 3 = | |
+ !. .<match [1;2] with [x] -> x | [x;y] -> x + y>.;; | |
+ | |
+(* OR patterns *) | |
+.<match [1;2] with [x] -> x | [x;y] | [x;y;_] -> x + y>.;; | |
+(* | |
+- : int code = .< | |
+match [1; 2] with | |
+| x_13::[] -> x_13 | |
+| x_14::y_15::[]|x_14::y_15::_::[] -> x_14 + y_15>. | |
+*) | |
+let 3 = !. .<match [1;2] with [x] -> x | [x;y] | [x;y;_] -> x + y>.;; | |
+ | |
+.<match [1;2;3;4] with [x] -> x | [x;y] | [x;y;_] | [y;x;_;_] -> x - y>.;; | |
+(* | |
+- : int code = .< | |
+match [1; 2; 3; 4] with | |
+| x_19::[] -> x_19 | |
+| x_20::y_21::[]|x_20::y_21::_::[]|y_21::x_20::_::_::[] -> x_20 - y_21>. | |
+*) | |
+let 1 = | |
+ !. .<match [1;2;3;4] with [x] -> x | [x;y] | [x;y;_] | [y;x;_;_] -> x - y>.;; | |
+ | |
+.<fun x -> match x with (`F x | `G x) -> x | `E x -> x>.;; | |
+(* | |
+- : ([< `E of 'a | `F of 'a | `G of 'a ] -> 'a) code = .< | |
+fun x_25 -> match x_25 with | `F x_26|`G x_26 -> x_26 | `E x_27 -> x_27>. | |
+*) | |
+ | |
+let 1 = (!. .<fun x -> match x with (`F x | `G x) -> x | `E x -> x>.) (`G 1);; | |
+ | |
+open Complex;; | |
+.<fun x -> match x with {re=x; im=2.0} -> x | {im=x; re=y} -> x -. y>.;; | |
+(* | |
+- : (Complex.t -> float) code = .< | |
+fun x_31 -> | |
+ match x_31 with | |
+ | { Complex.re = x_32; Complex.im = 2.0 } -> x_32 | |
+ | { Complex.re = y_33; Complex.im = x_34 } -> x_34 -. y_33>. | |
+*) | |
+ | |
+let 1.0 = | |
+ (!. .<fun x -> match x with {re=x; im=2.0} -> x | {im=x; re=y} -> x -. y>.) | |
+ {im=2.0; re=1.0};; | |
+ | |
+(* exceptional cases *) | |
+.<match List.mem 1 [] with x -> x | exception Not_found -> false>.;; | |
+(* | |
+- : bool code = .< | |
+match List.mem 1 [] with | x_95 -> x_95 | exception Not_found -> false>. | |
+*) | |
+let false = | |
+ !. .<match List.mem 1 [] with x -> x | exception Not_found -> false>. | |
+ | |
+let f = .<fun x -> | |
+ match List.assoc 1 x with "1" as x -> x | x -> x | |
+ | exception Not_found -> "" | exception Invalid_argument x -> x>. | |
+(* | |
+val f : ((int * string) list -> string) code = .< | |
+ fun x_100 -> | |
+ match List.assoc 1 x_100 with | |
+ | "1" as x_101 -> x_101 | |
+ | x_102 -> x_102 | |
+ | exception Not_found -> "" | |
+ | exception Invalid_argument x_103 -> x_103>. | |
+*) | |
+let "" = !. f [] | |
+let "1" = !. f [(1,"1")] | |
+let "0" = !. f [(1,"0")];; | |
+ | |
+(* try *) | |
+.<fun x -> try Some (List.assoc x [(1,true); (2,false)]) with Not_found -> None>.;; | |
+(* | |
+- : (int -> bool option) code = .< | |
+fun x_39 -> | |
+ try Some (List.assoc x_39 [(1, true); (2, false)]) | |
+ with | Not_found -> None>. | |
+*) | |
+let Some true = | |
+ (!. .<fun x -> try Some (List.assoc x [(1,true); (2,false)]) with Not_found -> None>.) 1;; | |
+let Some false = | |
+ (!. .<fun x -> try Some (List.assoc x [(1,true); (2,false)]) with Not_found -> None>.) 2;; | |
+let None = | |
+ (!. .<fun x -> try Some (List.assoc x [(1,true); (2,false)]) with Not_found -> None>.) 3;; | |
+ | |
+.<fun x -> let open Scanf in try sscanf x "%d" (fun x -> string_of_int x) with Scan_failure x -> "fail " ^ x>.;; | |
+(* | |
+- : (string -> string) code = .< | |
+fun x_43 -> | |
+ let open Scanf in | |
+ try Scanf.sscanf x_43 "%d" (fun x_44 -> Pervasives.string_of_int x_44) | |
+ with | Scanf.Scan_failure x_45 -> "fail " ^ x_45>. | |
+*) | |
+ | |
+let "1" = | |
+ (!. .<fun x -> let open Scanf in try sscanf x "%d" (fun x -> string_of_int x) with Scan_failure x -> "fail " ^ x>.) "1";; | |
+let "fail scanf: bad input at char number 0: \"character 'x' is not a decimal digit\"" = | |
+ (!. .<fun x -> let open Scanf in try sscanf x "%d" (fun x -> string_of_int x) with Scan_failure x -> "fail " ^ x>.) "xxx";; | |
+ | |
+(* Simple let *) | |
+ | |
+.<let x = 1 in x>.;; | |
+(* | |
+- : int code = .<let x_1 = 1 in x_1>. | |
+*) | |
+let 1 = | |
+ !. .<let x = 1 in x>.;; | |
+.<let x = 1 in let x = x + 1 in x>.;; | |
+(* | |
+- : int code = .<let x_55 = 1 in let x_56 = x_55 + 1 in x_56>. | |
+*) | |
+let 2 = | |
+ !. .<let x = 1 in let x = x + 1 in x>.;; | |
+.<let rec f = fun n -> if n = 0 then 1 else n * f (n-1) in f 5>.;; | |
+(* | |
+- : int code = .< | |
+let rec f_7 n_8 = if n_8 = 0 then 1 else n_8 * (f_7 (n_8 - 1)) in f_7 5>. | |
+*) | |
+let 120 = | |
+ !. .<let rec f = fun n -> if n = 0 then 1 else n * f (n-1) in f 5>.;; | |
+ | |
+(* Recursive vs. non-recursive bindings *) | |
+.<let f = fun x -> x in | |
+ let rec f = fun n -> if n = 0 then 1 else n * f (n-1) in f 5>.;; | |
+(* | |
+ Characters 6-7: | |
+ .<let f = fun x -> x in | |
+ ^ | |
+Warning 26: unused variable f. | |
+- : int code = .< | |
+let f_12 x_11 = x_11 in | |
+let rec f_13 n_14 = if n_14 = 0 then 1 else n_14 * (f_13 (n_14 - 1)) in | |
+f_13 5>. | |
+*) | |
+ | |
+let 120 = !. .<let f = fun x -> x in | |
+ let rec f = fun n -> if n = 0 then 1 else n * f (n-1) in f 5>.;; | |
+ | |
+.<let f = fun x -> x in | |
+ let f = fun n -> if n = 0 then 1 else n * f (n-1) in f 5>.;; | |
+(* | |
+ - : int code = .< | |
+let f_20 x_19 = x_19 in | |
+let f_22 n_21 = if n_21 = 0 then 1 else n_21 * (f_20 (n_21 - 1)) in f_22 5>. | |
+*) | |
+let 20 = !. .<let f = fun x -> x in | |
+ let f = fun n -> if n = 0 then 1 else n * f (n-1) in f 5>.;; | |
+ | |
+.<let g = fun x -> x+10 in | |
+ let f = fun x -> g x + 20 | |
+ and g = fun n -> if n = 0 then 1 else n * g (n-1) in (f 5,g 5)>.;; | |
+ | |
+(* | |
+- : (int * int) code = .< | |
+let g_28 x_27 = x_27 + 10 in | |
+let f_29 x_32 = (g_28 x_32) + 20 | |
+and g_30 n_31 = if n_31 = 0 then 1 else n_31 * (g_28 (n_31 - 1)) in | |
+((f_29 5), (g_30 5))>. | |
+*) | |
+let (35,70) = !. .<let g = fun x -> x+10 in | |
+ let f = fun x -> g x + 20 | |
+ and g = fun n -> if n = 0 then 1 else n * g (n-1) in (f 5,g 5)>.;; | |
+ | |
+.<let g = fun x -> x+10 in | |
+ let rec f = fun x -> g x + 20 | |
+ and g = fun n -> if n = 0 then 1 else n * g (n-1) in (f 5,g 5)>.;; | |
+ | |
+(* | |
+Characters 6-7: | |
+ .<let g = fun x -> x+10 in | |
+ ^ | |
+Warning 26: unused variable g. | |
+- : (int * int) code = .< | |
+let g_40 x_39 = x_39 + 10 in | |
+let rec f_41 x_44 = (g_42 x_44) + 20 | |
+and g_42 n_43 = if n_43 = 0 then 1 else n_43 * (g_42 (n_43 - 1)) in | |
+((f_41 5), (g_42 5))>. | |
+*) | |
+ | |
+let (140,120) = | |
+ !. .<let g = fun x -> x+10 in | |
+ let rec f = fun x -> g x + 20 | |
+ and g = fun n -> if n = 0 then 1 else n * g (n-1) in (f 5,g 5)>.;; | |
+ | |
+.<let rec [] = [] in []>. | |
+(* | |
+Characters 10-12: | |
+ .<let rec [] = [] in []>.;; | |
+ ^^ | |
+Only variables are allowed as left-hand side of `let rec' | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+.<let rec f = f in f>. | |
+(* | |
+Characters 10-15: | |
+ .<let rec f = f in f>.;; | |
+ ^^^^^ | |
+Recursive let binding must be to a function | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+(* General let rec *) | |
+.<fun x -> let rec even = function 0 -> true | x -> odd (x-1) and | |
+ odd = function 0 -> false | x -> even (x-1) in even x>.;; | |
+(* | |
+ - : (int -> bool) code = .< | |
+fun x_80 -> | |
+ let rec even_81 = function | 0 -> true | x_84 -> odd_82 (x_84 - 1) | |
+ and odd_82 = function | 0 -> false | x_83 -> even_81 (x_83 - 1) in | |
+ even_81 x_80>. | |
+*) | |
+let true = (!. .<fun x -> let rec even = function 0 -> true | x -> odd (x-1) and odd = function 0 -> false | x -> even (x-1) in even x>.) 42;; | |
+let false = (!. .<fun x -> let rec even = function 0 -> true | x -> odd (x-1) and odd = function 0 -> false | x -> even (x-1) in even x>.) 43;; | |
+ | |
+ | |
+(* General let *) | |
+.<let x = 1 and y = 2 in x + y>.;; | |
+(* | |
+- : int code = .<let x_73 = 1 and y_74 = 2 in x_73 + y_74>. | |
+*) | |
+let 3 = !. .<let x = 1 and y = 2 in x + y>.;; | |
+ | |
+.<let x = 1 in let x = x+1 and y = x+1 in x + y>.;; | |
+(* | |
+- : int code = .< | |
+let x_77 = 1 in let x_78 = x_77 + 1 and y_79 = x_77 + 1 in x_78 + y_79>. | |
+*) | |
+let 4 = !. .<let x = 1 in let x = x+1 and y = x+1 in x + y>.;; | |
+.<fun x -> let (Some x) = x in x + 1>.;; | |
+(* | |
+Characters 15-23: | |
+ .<fun x -> let (Some x) = x in x + 1>.;; | |
+ ^^^^^^^^ | |
+Warning 8: this pattern-matching is not exhaustive. | |
+Here is an example of a value that is not matched: | |
+None | |
+- : (int option -> int) code = .< | |
+fun x_83 -> let Some x_84 = x_83 in x_84 + 1>. | |
+*) | |
+let 3 = (!. .<fun x -> let (Some x) = x in x + 1>.) (Some 2);; | |
+(!. .<fun x -> let (Some x) = x in x + 1>.) None;; | |
+(* | |
+Exception: Match_failure ("//toplevel//", 1, 19). | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+ | |
+ | |
+(* testing scope extrusion *) | |
+let r = ref .<0>. in let _ = .<fun x -> .~(r := .<1>.; .<0>.)>. in !r ;; | |
+(* - : int code = .<1>. *) | |
+let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in !r ;; | |
+(* | |
+- : int code = .<x_30>. | |
+ | |
+Failure("The code built at Characters 35-36:\n let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in !r ;;\n ^\n is not closed: identifier x_30 bound at Characters 35-36:\n let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in !r ;;\n ^\n is free") | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+let c = let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in (!r) in !. c;; | |
+(* | |
+Exception: | |
+Failure | |
+ "The code built at Characters 43-44:\n let c = let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in (!r) in !. c;;\n ^\n is not closed: identifier x_31 bound at Characters 43-44:\n let c = let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in (!r) in !. c;;\n ^\n is free". | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+let r = ref .<fun y->y>. in let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in !r ;; | |
+(* | |
+- : ('_a -> '_a) code = .<fun y_34 -> x_33>. | |
+ | |
+Failure("The code built at Characters 57-67:\n let r = ref .<fun y->y>. in let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in !r ;;\n ^^^^^^^^^^\n is not closed: identifier x_33 bound at Characters 42-43:\n let r = ref .<fun y->y>. in let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in !r ;;\n ^\n is free") | |
+ | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+(* Error message is reported on splice *) | |
+let r = ref .<fun y->y>. in | |
+ let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in .<fun x -> .~(!r) 1>. ;; | |
+(* | |
+Exception: | |
+Failure | |
+ "Scope extrusion detected at Characters 96-104:\n let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in .<fun x -> .~(!r) 1>. ;;\n ^^^^^^^^\n for code built at Characters 59-69:\n let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in .<fun x -> .~(!r) 1>. ;;\n ^^^^^^^^^^\n for the identifier x_95 bound at Characters 44-45:\n let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in .<fun x -> .~(!r) 1>. ;;\n ^\n". | |
+*) | |
+print_endline "Error was expected";; | |
+ | |
+(* Unlike BER N100, the test is exact. The following is accepted with BER N101 | |
+ (it wasn't with BER N100) | |
+*) | |
+let r = ref .<fun y->y>. in let _ = .<fun x -> .~(r := .<fun y -> y>.; .<0>.)>. in !r ;; | |
+(* | |
+- : ('_a -> '_a) code = .<fun y_41 -> y_41>. | |
+*) | |
+(* Was in N100: print_endline "Error was expected";; *) | |
+ | |
+(* The following are OK though *) | |
+let r = ref .<fun y->y>. in .<fun x -> .~(r := .<fun y -> y>.; !r)>.;; | |
+(* | |
+- : ('_a -> '_b -> '_b) code = .<fun x_43 y_44 -> y_44>. | |
+*) | |
+let r = ref .<fun y->y>. in .<fun x -> .~(r := .<fun y -> x>.; !r)>.;; | |
+(* | |
+- : ('_a -> '_a -> '_a) code = .<fun x_46 y_47 -> x_46>. | |
+*) | |
+let 3 = | |
+ let r = ref .<fun y->y>. in !. .<fun x -> .~(r := .<fun y -> x>.; !r)>. 3 4;; | |
+ | |
+print_endline "\nAll done\n";; | |
diff -Naur ocaml-4.02.1/metalib/trivial.ref ocaml-ber-n102/metalib/trivial.ref | |
--- ocaml-4.02.1/metalib/trivial.ref 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/trivial.ref 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,460 @@ | |
+BER MetaOCaml toplevel, version N 102 | |
+ OCaml version 4.02.1 | |
+ | |
+# # - : int = 5 | |
+# val fact : int -> int = <fun> | |
+# # - : int code = .<1>. | |
+# - : string code = .<"aaa">. | |
+# # - : int Runcode.closed_code = .<1>. | |
+# * * - : 'a list -> 'a list = <fun> | |
+# - : ('a list -> 'a list) code = .<List.rev>. | |
+# Characters 74-75: | |
+ .<fun x -> .~(let y = x in y)>.;; | |
+ ^ | |
+Wrong level: variable bound at level 1 and used at level 0 | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# Characters 18-28: | |
+ .<fun x -> 1 + .~(.<true>.)>.;; | |
+ ^^^^^^^^^^ | |
+Error: This expression has type bool but an expression was expected of type | |
+ int | |
+# * * * * * * Error was expected | |
+- : unit = () | |
+# - : int code = .<1>. | |
+# * * - : float code = .<1.>. | |
+# * * - : bool code = .<true>. | |
+# * * - : string code = .<"aaa">. | |
+# * * - : char code = .<'a'>. | |
+# * * - : char list code = .<(* CSP x *)>. | |
+# * * val l : 'a -> 'a code = <fun> | |
+# - : int code = .<(* CSP x *) Obj.magic 1>. | |
+# * * # - : float code = .<1.>. | |
+# * * # - : 'a list code = .<(* CSP x *) Obj.magic 0>. | |
+# * * # - : (int -> int) code = .<(* CSP x *)>. | |
+# * * * * * * - : ('a list -> 'a list) code = .<List.rev>. | |
+# * * - : ('a array -> int -> 'a) code = .<Array.get>. | |
+# * * - : (int -> int -> int) code = .<Pervasives.(+)>. | |
+# * * - : unit code = .<assert true>. | |
+# * * - : unit = () | |
+# - : int code = .<Pervasives.succ 1>. | |
+# * * # - : int code = .<1 + 2>. | |
+# * * # - : int code = .<String.length "abc">. | |
+# * * # - : string code = .<StringLabels.sub "abc" ~pos:1 ~len:2>. | |
+# * * # - : string code = .<StringLabels.sub "abc" ~pos:1 ~len:2>. | |
+# * * # - : int code code = .<.< 1 >.>. | |
+# - : int code = .<1>. | |
+# # - : int code = .<Runcode.(!.) .< 1 >.>. | |
+# * * # - : int code = .<1 + 2>. | |
+# * * - : int code = .<(2 + 4) + (2 + 4)>. | |
+# * * # Characters 27-28: | |
+ .<1 + .~(let x = 2 in .<.<x>.>.)>.;; | |
+ ^ | |
+Error: This expression has type 'a code | |
+ but an expression was expected of type int | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# - : int code = .<1 + (Runcode.(!.) .< 2 >.)>. | |
+# * * # Characters 12-40: | |
+ !. .<1 + .~ (.~(let x = 2 in .<.<x>.>.))>.;; | |
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
+Wrong level: escape at level 0 | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# - : int code code = .<.< .~(.< 1 >.) >.>. | |
+# * * * - : int code = .<1>. | |
+# * * # - : int code code = .<.< .~(.< 1 >.) >.>. | |
+# * * * # - : int code = .<1>. | |
+# * * # - : int code code = .<(* CSP x *)>. | |
+# * * - : int code = .<1>. | |
+# * * # - : int code code = .<.< .~((* CSP x *)) >.>. | |
+# * * # - : ('a list -> 'a list) code code = .<.< .~(.< List.rev >.) >.>. | |
+# * * - : ('_a list -> '_a list) code = .<List.rev>. | |
+# * * # * * - : int code code = .<.< assert true; 1 >.>. | |
+# * * # - : int code = .<assert true; 1>. | |
+# * * - : int code = .<assert true; 1>. | |
+# * * - : int code = .<assert true; 1>. | |
+# * * # - : int code = .<assert true; 1>. | |
+# * * - : int lazy_t code = .<lazy 1>. | |
+# * * # - : (int * string) code = .<(1, "abc")>. | |
+# * * - : (int * string * char) code = .<(1, "abc", 'd')>. | |
+# * * # - : 'a array code = .<[||]>. | |
+# * * - : int array code = .<[|(1 + 2);(1 + 2)|]>. | |
+# * * - : 'a code = .<Pervasives.raise Not_found>. | |
+# * * Characters 52-64: | |
+ .<raise (Scan_failure "")>.;; | |
+ ^^^^^^^^^^^^ | |
+Error: Unbound constructor Scan_failure | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# - : 'a code = .<Pervasives.raise (Scanf.Scan_failure "")>. | |
+# * * # - : 'a code = .<Pervasives.raise (Scanf.Scan_failure "")>. | |
+# * * Exception: Scanf.Scan_failure "". | |
+# * * Exception was expected | |
+- : unit = () | |
+# - : bool code = .<true>. | |
+# * * - : int option code = .<Some 1>. | |
+# * * - : int list option code = .<Some [1]>. | |
+# * * # - : 'a option code = .<None>. | |
+# * * # - : Genlex.token code = .<Genlex.Int 1>. | |
+# * * # - : Genlex.token code = .<Genlex.Int 1>. | |
+# * * # module Foo : sig exception E end | |
+# Characters 8-13: | |
+ .<raise Foo.E>.;; | |
+ ^^^^^ | |
+Exception (extension) Foo.E cannot be used within brackets. Put into a separate file. | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# type foo = Bar | |
+# Characters 2-5: | |
+ .<Bar>.;; | |
+ ^^^ | |
+Unqualified constructor Bar cannot be used within brackets. Put into a separate file. | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# module Foo : sig type foo = Bar end | |
+# Characters 2-9: | |
+ .<Foo.Bar>.;; | |
+ ^^^^^^^ | |
+Constructor Bar cannot be used within brackets. Put into a separate file. | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# - : Complex.t code = .<{ Complex.re = 1.0; Complex.im = 2.0 }>. | |
+# * * # - : float code = .<(* CSP x *).Complex.re>. | |
+# * * * * * * * * - : float code = .<(* CSP x *).Complex.re>. | |
+# * * * # - : int code = .<(* CSP x *).Pervasives.contents>. | |
+# * * # - : unit code = .<(* CSP x *).Pervasives.contents <- 2>. | |
+# * * * - : int ref = {contents = 2} | |
+# # - : Complex.t code = .<{ Complex.re = 1.0; Complex.im = 2.0 }>. | |
+# * * # - : float code = .<(* CSP x *).Complex.re>. | |
+# * * * # type foo = { fool : int; } | |
+# Characters 3-7: | |
+ .<{fool = 1}>.;; | |
+ ^^^^ | |
+Unqualified label fool cannot be used within brackets. Put into a separate file. | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# - : int code = .<if true then 1 else 2>. | |
+# - : unit code = .<if (Some 1) = None then Pervasives.print_string "weird">. | |
+# * * # - : [> `Foo ] code = .<`Foo>. | |
+# * * - : [> `Bar of int ] code = .<`Bar 1>. | |
+# * * # val f : < foo : 'a; .. > -> 'a code = <fun> | |
+# * * val x : < foo : int > = <obj> | |
+# * * - : int code = .<(* CSP x *)#foo>. | |
+# * * # - : float code = .<Complex.norm { Complex.re = 3.0; Complex.im = 4.0 }>. | |
+# * * # - : float code = .<Complex.norm { Complex.re = 4.0; Complex.im = 3.0 }>. | |
+# * * # - : unit code = .< | |
+for i_1 = 1 to 5 do | |
+ Printf.printf | |
+ (CamlinternalFormatBasics.Format | |
+ ((CamlinternalFormatBasics.String_literal | |
+ ("ok ", | |
+ (CamlinternalFormatBasics.Int | |
+ (CamlinternalFormatBasics.Int_d, | |
+ CamlinternalFormatBasics.No_padding, | |
+ CamlinternalFormatBasics.No_precision, | |
+ (CamlinternalFormatBasics.Char_literal | |
+ (' ', | |
+ (CamlinternalFormatBasics.Int | |
+ (CamlinternalFormatBasics.Int_d, | |
+ CamlinternalFormatBasics.No_padding, | |
+ CamlinternalFormatBasics.No_precision, | |
+ (CamlinternalFormatBasics.Char_literal | |
+ ('\n', CamlinternalFormatBasics.End_of_format)))))))))), | |
+ "ok %d %d\n")) i_1 (i_1 + 1) | |
+done>. | |
+# * * * ok 1 2 | |
+ok 2 3 | |
+ok 3 4 | |
+ok 4 5 | |
+ok 5 6 | |
+- : unit = () | |
+# * * * * * * - : unit code = .< | |
+for i_3 = 5 downto 1 do | |
+ Printf.printf | |
+ (CamlinternalFormatBasics.Format | |
+ ((CamlinternalFormatBasics.String_literal | |
+ ("ok ", | |
+ (CamlinternalFormatBasics.Int | |
+ (CamlinternalFormatBasics.Int_d, | |
+ CamlinternalFormatBasics.No_padding, | |
+ CamlinternalFormatBasics.No_precision, | |
+ (CamlinternalFormatBasics.Char_literal | |
+ (' ', | |
+ (CamlinternalFormatBasics.Int | |
+ (CamlinternalFormatBasics.Int_d, | |
+ CamlinternalFormatBasics.No_padding, | |
+ CamlinternalFormatBasics.No_precision, | |
+ (CamlinternalFormatBasics.Char_literal | |
+ ('\n', CamlinternalFormatBasics.End_of_format)))))))))), | |
+ "ok %d %d\n")) i_3 (i_3 + 1) | |
+done>. | |
+# * * * ok 5 6 | |
+ok 4 5 | |
+ok 3 4 | |
+ok 2 3 | |
+ok 1 2 | |
+- : unit = () | |
+# * * * * * * - : unit code = .< | |
+for i_5 = 1 to 2 do | |
+ for j_6 = 1 to 3 do | |
+ Printf.printf | |
+ (CamlinternalFormatBasics.Format | |
+ ((CamlinternalFormatBasics.String_literal | |
+ ("ok ", | |
+ (CamlinternalFormatBasics.Int | |
+ (CamlinternalFormatBasics.Int_d, | |
+ CamlinternalFormatBasics.No_padding, | |
+ CamlinternalFormatBasics.No_precision, | |
+ (CamlinternalFormatBasics.Char_literal | |
+ (' ', | |
+ (CamlinternalFormatBasics.Int | |
+ (CamlinternalFormatBasics.Int_d, | |
+ CamlinternalFormatBasics.No_padding, | |
+ CamlinternalFormatBasics.No_precision, | |
+ (CamlinternalFormatBasics.Char_literal | |
+ ('\n', | |
+ CamlinternalFormatBasics.End_of_format)))))))))), | |
+ "ok %d %d\n")) i_5 j_6 | |
+ done | |
+done>. | |
+# * * * * * ok 1 1 | |
+ok 1 2 | |
+ok 1 3 | |
+ok 2 1 | |
+ok 2 2 | |
+ok 2 3 | |
+- : unit = () | |
+# * * * * * * * val c : unit code = .< | |
+ for i_9 = 1 to 2 do | |
+ for i_10 = 1 to 3 do | |
+ Printf.printf | |
+ (CamlinternalFormatBasics.Format | |
+ ((CamlinternalFormatBasics.String_literal | |
+ ("ok ", | |
+ (CamlinternalFormatBasics.Int | |
+ (CamlinternalFormatBasics.Int_d, | |
+ CamlinternalFormatBasics.No_padding, | |
+ CamlinternalFormatBasics.No_precision, | |
+ (CamlinternalFormatBasics.Char_literal | |
+ (' ', | |
+ (CamlinternalFormatBasics.Int | |
+ (CamlinternalFormatBasics.Int_d, | |
+ CamlinternalFormatBasics.No_padding, | |
+ CamlinternalFormatBasics.No_precision, | |
+ (CamlinternalFormatBasics.Char_literal | |
+ ('\n', | |
+ CamlinternalFormatBasics.End_of_format)))))))))), | |
+ "ok %d %d\n")) i_10 i_9 | |
+ done | |
+ done>. | |
+# * * * * * ok 1 1 | |
+ok 2 1 | |
+ok 3 1 | |
+ok 1 2 | |
+ok 2 2 | |
+ok 3 2 | |
+- : unit = () | |
+# * * * * * * * - : unit code = .<for _for_11 = 1 to 3 do Pervasives.print_string "ok" done>. | |
+# * * okokok- : unit = () | |
+# * * - : unit code = .<for i_13 = 1 to 10 do () done>. | |
+# * * Exception: | |
+Failure | |
+ "The code built at Characters 62-63:\n .<for i=1 to 10 do .~(let _ = !. .<i>. in .<()>.) done>.;;\n ^\n is not closed: identifier i_14 bound at Characters 62-63:\n .<for i=1 to 10 do .~(let _ = !. .<i>. in .<()>.) done>.;;\n ^\n is free". | |
+# * * * * * * * * * * Error was expected | |
+- : unit = () | |
+# - : unit code = .<for i_16 = 1 to 5 do Pervasives.ignore 0 done>. | |
+# * * Exception: | |
+Failure | |
+ "Scope extrusion detected at Characters 184-199:\n .<for i=1 to 5 do ignore (.~(!r)) done>.;;\n ^^^^^^^^^^^^^^^\n for code built at Characters 101-102:\n let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i>.; .<()>.) done>.; \n ^\n for the identifier i_17 bound at Characters 101-102:\n let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i>.; .<()>.) done>.; \n ^\n". | |
+# * * * * Error was expected | |
+- : unit = () | |
+# Exception: | |
+Failure | |
+ "Scope extrusion detected at Characters 140-155:\n .<for i=1 to 5 do ignore (.~(!r)) done>.;;\n ^^^^^^^^^^^^^^^\n for code built at Characters 77-80:\n let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i+1>.; .<()>.) done>.; \n ^^^\n for the identifier i_19 bound at Characters 55-56:\n let r = ref .<0>. in .<for i=1 to 5 do .~(r := .<i+1>.; .<()>.) done>.; \n ^\n". | |
+# * * * * * * * * Error was expected | |
+- : unit = () | |
+# - : ('a -> 'a) code = .<fun x_21 -> x_21>. | |
+# * * # - : (int -> int -> int) code = .<fun x_23 -> fun y_24 -> x_23 + y_24>. | |
+# * * # - : ('a -> int -> int) code = .<fun x_27 -> fun x_28 -> x_28 + x_28>. | |
+# * * # val eta : ('a code -> 'b code) -> ('a -> 'b) code = <fun> | |
+# * * - : (int -> int) code = .<fun x_31 -> 1 + x_31>. | |
+# * * - : (int -> int -> int) code = .<fun x_32 -> fun x_33 -> x_33 + x_32>. | |
+# * * # val fhyg : int code -> int -> int code = <fun> | |
+# * * - : int code = .< | |
+(fun x_36 -> (fun x_37 -> (fun x_38 -> ((1 + x_36) + x_37) + x_38) 1) 2) 3>. | |
+ | |
+# * * * # - : (unit -> int) code = .<fun () -> 1>. | |
+# - : unit -> int = <fun> | |
+# # - : (bool -> int) code = .<function | true -> 1 | false -> 0>. | |
+# * * # - : (bool * 'a list -> int) code = .<fun (true ,[]) -> 1>. | |
+# * * Exception: Match_failure ("//toplevel//", 526, 6). | |
+# * * Error was expected | |
+- : unit = () | |
+# # - : (bool array -> int) code = .<fun [|true ;false ;false |] -> 1>. | |
+# * * # - : ([> `F of int ] -> bool) code = .<function | `F 1 -> true | _ -> false>. | |
+# * * # - : ([> `F of int | `G of int ] -> bool) code = .< | |
+function | `F 1|`G 2 -> true | _ -> false>. | |
+# * * * - : (int * string -> int) code = .<function | (1,"str") -> 1 | (2,_) -> 2>. | |
+# * * # # # # # # # # # - : (Complex.t -> int) code = .< | |
+function | |
+| { Complex.re = 1.0 } -> 1 | |
+| { Complex.re = 2.0; Complex.im = 2.0 } -> 2 | |
+| { Complex.im = _ } -> 3>. | |
+# * * * * * * # # # - : (unit -> int) code = .<fun () -> 1>. | |
+# * * # - : ('a -> bool) code = .<fun _ -> true>. | |
+# # - : (int * int -> int) code = .<fun (x_42,y_43) -> x_42 + y_43>. | |
+# * * # - : (int option -> int) code = .< | |
+function | Some x_46 as y_47 -> x_46 | _ -> 2>. | |
+# * * # # - : (int list -> int) code = .< | |
+function | |
+| x_52::y_53::z_54::[] -> (x_52 - y_53) + z_54 | |
+| x_55::y_56::[] -> x_55 - y_56>. | |
+# * * * * * # - : (int list -> int) code = .< | |
+fun (x_62::y_63::[]|x_62::y_63::_::[]) -> x_62 - y_63>. | |
+# * * * # # Exception: Match_failure ("//toplevel//", 590, -90). | |
+# Error was expected | |
+- : unit = () | |
+# - : (int list -> int) code = .< | |
+fun (x_70::y_71::[]|x_70::y_71::_::[]|y_71::x_70::_::_::[]) -> x_70 - y_71>. | |
+# * * * # # # - : ([< `E of 'a | `F of 'a | `G of 'a ] -> 'a) code = .< | |
+function | `F x_78|`G x_78 -> x_78 | `E x_79 -> x_79>. | |
+# * * * # # - : (Complex.t -> float) code = .< | |
+function | |
+| { Complex.re = x_82 } -> x_82 | |
+| { Complex.re = y_83; Complex.im = x_84 } -> x_84 -. y_83>. | |
+# * * * * * - : (Complex.t -> float) code = .< | |
+function | |
+| { Complex.re = x_85; Complex.im = 2.0 } -> x_85 | |
+| { Complex.re = y_86; Complex.im = x_87 } -> x_87 -. y_86>. | |
+# * * * * * # # - : (int option -> int option) code = .< | |
+function | Some x_94 as y_95 when x_94 > 0 -> y_95 | _ -> None>. | |
+# * * * # # - : bool code = .<match 1 with | 1 -> true>. | |
+# * * # - : bool code = .<match (1, 2) with | (1,x_100) -> true | x_101 -> false>. | |
+# * * - : int code = .< | |
+match [1; 2] with | x_102::[] -> x_102 | x_103::y_104::[] -> x_103 + y_104>. | |
+# * * * # - : int code = .< | |
+match [1; 2] with | |
+| x_108::[] -> x_108 | |
+| x_109::y_110::[]|x_109::y_110::_::[] -> x_109 + y_110>. | |
+# * * * * * # - : int code = .< | |
+match [1; 2; 3; 4] with | |
+| x_114::[] -> x_114 | |
+| x_115::y_116::[]|x_115::y_116::_::[]|y_116::x_115::_::_::[] -> | |
+ x_115 - y_116>. | |
+ | |
+# * * * * * # - : ([< `E of 'a | `F of 'a | `G of 'a ] -> 'a) code = .< | |
+fun x_120 -> | |
+ match x_120 with | `F x_121|`G x_121 -> x_121 | `E x_122 -> x_122>. | |
+ | |
+# * * * # # - : (Complex.t -> float) code = .< | |
+fun x_126 -> | |
+ match x_126 with | |
+ | { Complex.re = x_127; Complex.im = 2.0 } -> x_127 | |
+ | { Complex.re = y_128; Complex.im = x_129 } -> x_129 -. y_128>. | |
+ | |
+# * * * * * * # - : bool code = .< | |
+match List.mem 1 [] with | x_134 -> x_134 | exception Not_found -> false>. | |
+# * * * * * * * * * * * val f : ((int * string) list -> string) code = .< | |
+ fun x_136 -> | |
+ match List.assoc 1 x_136 with | |
+ | "1" as x_137 -> x_137 | |
+ | x_138 -> x_138 | |
+ | exception Not_found -> "" | |
+ | exception Invalid_argument x_139 -> x_139>. | |
+ | |
+# - : (int -> bool option) code = .< | |
+fun x_140 -> | |
+ try Some (List.assoc x_140 [(1, true); (2, false)]) | |
+ with | Not_found -> None>. | |
+ | |
+# * * * * * # # # - : (string -> string) code = .< | |
+fun x_144 -> | |
+ try | |
+ Scanf.sscanf x_144 | |
+ (CamlinternalFormatBasics.Format | |
+ ((CamlinternalFormatBasics.Int | |
+ (CamlinternalFormatBasics.Int_d, | |
+ CamlinternalFormatBasics.No_padding, | |
+ CamlinternalFormatBasics.No_precision, | |
+ CamlinternalFormatBasics.End_of_format)), "%d")) | |
+ (fun x_145 -> Pervasives.string_of_int x_145) | |
+ with | Scanf.Scan_failure x_146 -> "fail " ^ x_146>. | |
+ | |
+# * * * * * * # # - : int code = .<let x_153 = 1 in x_153>. | |
+# * * # - : int code = .<let x_155 = 1 in let x_156 = x_155 + 1 in x_156>. | |
+# * * # - : int code = .< | |
+let rec f_159 n_160 = if n_160 = 0 then 1 else n_160 * (f_159 (n_160 - 1)) in | |
+f_159 5>. | |
+# * * * # - : int code = .< | |
+let f_164 x_163 = x_163 in | |
+let rec f_165 n_166 = if n_166 = 0 then 1 else n_166 * (f_165 (n_166 - 1)) in | |
+f_165 5>. | |
+# * * * * * * * * * # - : int code = .< | |
+let f_172 x_171 = x_171 in | |
+let f_174 n_173 = if n_173 = 0 then 1 else n_173 * (f_172 (n_173 - 1)) in | |
+f_174 5>. | |
+# * * * * # - : (int * int) code = .< | |
+let g_180 x_179 = x_179 + 10 in | |
+let f_181 x_184 = (g_180 x_184) + 20 | |
+and g_182 n_183 = if n_183 = 0 then 1 else n_183 * (g_180 (n_183 - 1)) in | |
+((f_181 5), (g_182 5))>. | |
+# * * * * * * # - : (int * int) code = .< | |
+let g_192 x_191 = x_191 + 10 in | |
+let rec f_193 x_196 = (g_194 x_196) + 20 | |
+and g_194 n_195 = if n_195 = 0 then 1 else n_195 * (g_194 (n_195 - 1)) in | |
+((f_193 5), (g_194 5))>. | |
+# * * * * * * * * * * # * * * * * Characters 11-13: | |
+ .<let rec [] = [] in []>. | |
+ ^^ | |
+Only variables are allowed as left-hand side of `let rec' | |
+# * * * * * Characters 11-16: | |
+ .<let rec f = f in f>. | |
+ ^^^^^ | |
+Recursive let binding must be to a function | |
+# - : (int -> bool) code = .< | |
+fun x_203 -> | |
+ let rec even_204 = function | 0 -> true | x_207 -> odd_205 (x_207 - 1) | |
+ and odd_205 = function | 0 -> false | x_206 -> even_204 (x_206 - 1) in | |
+ even_204 x_203>. | |
+ | |
+# * * * * * * # # - : int code = .<let x_218 = 1 | |
+ and y_219 = 2 in x_218 + y_219>. | |
+# * * # - : int code = .< | |
+let x_222 = 1 in let x_223 = x_222 + 1 | |
+ and y_224 = x_222 + 1 in x_223 + y_224>. | |
+ | |
+# * * * # - : (int option -> int) code = .< | |
+fun x_228 -> let Some x_229 = x_228 in x_229 + 1>. | |
+# * * * * * * * * * # Exception: Match_failure ("//toplevel//", 831, -265). | |
+# * * Error was expected | |
+- : unit = () | |
+# - : int code = .<1>. | |
+# - : int code = .<x_235>. | |
+ | |
+Failure("The code built at Characters 63-64:\n let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in !r ;;\n ^\n is not closed: identifier x_235 bound at Characters 63-64:\n let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in !r ;;\n ^\n is free") | |
+# * * * * Error was expected | |
+- : unit = () | |
+# Exception: | |
+Failure | |
+ "The code built at Characters 44-45:\n let c = let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in (!r) in !. c;;\n ^\n is not closed: identifier x_236 bound at Characters 44-45:\n let c = let r = ref .<0>. in let _ = .<fun x -> .~(r := .<x>.; .<0>.)>. in (!r) in !. c;;\n ^\n is free". | |
+# * * * * Error was expected | |
+- : unit = () | |
+# - : ('_a -> '_a) code = .<fun y_239 -> x_238>. | |
+ | |
+Failure("The code built at Characters 58-68:\n let r = ref .<fun y->y>. in let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in !r ;;\n ^^^^^^^^^^\n is not closed: identifier x_238 bound at Characters 43-44:\n let r = ref .<fun y->y>. in let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in !r ;;\n ^\n is free") | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# Exception: | |
+Failure | |
+ "Scope extrusion detected at Characters 139-147:\n let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in .<fun x -> .~(!r) 1>. ;;\n ^^^^^^^^\n for code built at Characters 102-112:\n let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in .<fun x -> .~(!r) 1>. ;;\n ^^^^^^^^^^\n for the identifier x_241 bound at Characters 87-88:\n let _ = .<fun x -> .~(r := .<fun y -> x>.; .<0>.)>. in .<fun x -> .~(!r) 1>. ;;\n ^\n". | |
+# * * * * * Error was expected | |
+- : unit = () | |
+# * * - : ('_a -> '_a) code = .<fun y_246 -> y_246>. | |
+# * * - : ('_a -> '_b -> '_b) code = .<fun x_248 -> fun y_249 -> y_249>. | |
+# * * - : ('_a -> '_a -> '_a) code = .<fun x_251 -> fun y_252 -> x_251>. | |
+# * * # | |
+All done | |
+ | |
+- : unit = () | |
+# | |
diff -Naur ocaml-4.02.1/metalib/trxtime.c ocaml-ber-n102/metalib/trxtime.c | |
--- ocaml-4.02.1/metalib/trxtime.c 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/trxtime.c 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,67 @@ | |
+/* More precise timing functions and CPU counters */ | |
+ | |
+#include "cycle.h" /* Get cycle counter, FFTW project */ | |
+ /* http://www.fftw.org/download.html */ | |
+ | |
+/* The following is probablyu no longer needed: caml_sys_time | |
+ now uses rusage | |
+*/ | |
+ | |
+/* Return the structure of user and system process times, in whole | |
+ and fractional seconds. | |
+ sys_times:: unit -> double * double | |
+ Because we invoke caml_copy_double() function, which may cause gc(), | |
+ we must use CAMLparam/CAMLreturn() conventions. | |
+*/ | |
+CAMLprim value sys_times(value unit) | |
+{ | |
+ CAMLparam0 (); /* unit is unused */ | |
+ struct rusage ru; | |
+ CAMLlocal3 (result, utime, stime); | |
+ | |
+ memset(&ru,0,sizeof ru); | |
+ getrusage(0,&ru); | |
+ result = caml_alloc_small (2, 0); | |
+ utime = caml_copy_double((double)ru.ru_utime.tv_sec + | |
+ (double)ru.ru_utime.tv_usec/1000000.0); | |
+ stime = caml_copy_double((double)ru.ru_stime.tv_sec + | |
+ (double)ru.ru_stime.tv_usec/1000000.0); | |
+ Field(result, 0) = utime; /* Simple assignments don't cause GC */ | |
+ Field(result, 1) = stime; /* We're overriding freshly allocated fields */ | |
+ CAMLreturn (result); | |
+} | |
+ | |
+/* Ticks is an abstract data type. | |
+ It means different things on different systems. | |
+ See cycle.h for more details. | |
+ */ | |
+ | |
+/* | |
+ Get the current ticks counter. Return an abstract value | |
+*/ | |
+ | |
+CAMLprim value sys_get_ticks(value unit) | |
+{ | |
+ value res; | |
+ ticks t; | |
+ Assert(sizeof(t) == (sizeof t/sizeof res) * sizeof(res)); /* a constant op*/ | |
+#define Setup_for_gc | |
+#define Restore_after_gc | |
+ Alloc_small(res, (sizeof t/sizeof res), Abstract_tag); | |
+#undef Setup_for_gc | |
+#undef Restore_after_gc | |
+ t = getticks(); | |
+ memcpy(Bp_val(res),&t,sizeof t); | |
+ return res; | |
+} | |
+ | |
+/* | |
+ * Return the difference between the current tick counter and the passed one. | |
+ */ | |
+CAMLprim value sys_elapsed_ticks(value t0) | |
+{ | |
+ /* no CAML memory allocation, except for the very end */ | |
+ ticks tt0, tt1 = getticks(); | |
+ memcpy(&tt0,Bp_val(t0),sizeof tt0); | |
+ return caml_copy_double(elapsed(tt1,tt0)); | |
+} | |
diff -Naur ocaml-4.02.1/metalib/trxtime.ml ocaml-ber-n102/metalib/trxtime.ml | |
--- ocaml-4.02.1/metalib/trxtime.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/trxtime.ml 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,270 @@ | |
+let print_buffer = ref [] | |
+let numbers_buffer = ref [] | |
+ | |
+let init_times () = | |
+ let _ = print_buffer := [] in | |
+ numbers_buffer := [] | |
+ | |
+(* helper function to print times consisently *) | |
+ | |
+let pt s reps elapsed = | |
+ let t = String.sub (s^" ___________________") 0 20 in | |
+ let reps_string = "_________ "^(string_of_int reps) in | |
+ let len = String.length reps_string in | |
+ let reps_trimed = String.sub reps_string (len-10) 10 in | |
+ let _ = numbers_buffer := (s, reps, elapsed) :: !numbers_buffer in | |
+ print_buffer := (fun () -> (Format.fprintf Format.std_formatter | |
+ "__ %s_%sx avg= %E msec" | |
+ t reps_trimed elapsed; (Format.print_newline ()))) :: !print_buffer | |
+ | |
+(* do total time *) | |
+let time reps s f = | |
+ let initial = Sys.time() in | |
+ let _ = while initial = (Sys.time()) do () done in | |
+ let initial = Sys.time() in | |
+ let result = f () in | |
+ let final = | |
+ (for i=1 to reps-1 do f() done; | |
+ Sys.time ()) in | |
+ let elapsed = (final -. initial) *. 1000.0 /. (float reps) in | |
+ pt s reps elapsed; | |
+ result | |
+ | |
+let timenew s f = | |
+ let mx = ref 0 in (* Variables for fine timing construction *) | |
+ let n = ref 0 in | |
+ let m = ref 0 in | |
+ let s1 = ref 0.0 in | |
+ let s2 = ref 0.0 in | |
+ let s3 = ref 0.0 in | |
+ let _ = m:=0 in | |
+ let _ = mx:=0 in | |
+ let _ = s2:= (0.0) in | |
+ let _ = s3:= (0.0) in | |
+ | |
+ let reps = ref 1 in (* adaptive timing code *) | |
+ let tr = ref 0.0 in | |
+ let t1 = Sys.time() in | |
+ let t2 = begin while (Sys.time() = t1) do () done; Sys.time() end in | |
+ let r = f () in | |
+ let _ = (tr:=Sys.time()) in | |
+ let _ = while (!tr -. t2 < 1.0) | |
+ do for i=1 to !reps | |
+ do f () done; | |
+ reps := (!reps)*2; | |
+ tr := Sys.time () | |
+ done in (* end adaptive timing code *) | |
+ | |
+let _ = s1:= !tr in (* back to fine timing code *) | |
+let _ = for j=1 to 1 do | |
+ while (Sys.time () = (!s1)) do | |
+ for i=1 to 100 do | |
+ n := (!n) + 1 | |
+ done | |
+ done; s2:=(Sys.time ()) | |
+ done in | |
+let _ = s1:=!s2 in | |
+let _ = for j=1 to 10 do | |
+ while (Sys.time () = (!s2)) do | |
+ for i=1 to 100 do | |
+ m := (!m) + 1 | |
+ done | |
+ done; s2:=(Sys.time ()); | |
+ mx:= max !m !mx; | |
+ m:=0; | |
+ done; in | |
+let p = (float !n)/.(float !mx) in | |
+let te = (!tr*.(1.0-.p)) +. (!s1*.p) in | |
+ | |
+ let dt = (te -. t2) in | |
+ let n = !reps in | |
+ let at = dt *. 1000.0 /. (float n) in | |
+ pt s n at; | |
+ r | |
+ | |
+let print_times () = | |
+ List.iter (fun f -> f ()) (List.rev !print_buffer); | |
+ print_buffer := [] | |
+ | |
+let get_times () = | |
+ let nums = (List.rev !numbers_buffer) in | |
+ let _ = numbers_buffer := [] in | |
+ nums | |
+ | |
+(* To be activated later | |
+ | |
+(* More precise timing functions *) | |
+type ticks | |
+ | |
+external times: unit -> float * float = "sys_times" | |
+external get_ticks: unit -> ticks = "sys_get_ticks" | |
+external elapsed_ticks: ticks -> float = "sys_elapsed_ticks" | |
+ | |
+(** Timing based on CPU cycles. | |
+ CPU cycle counter is a real timer; it counts in CPU cycles, whose | |
+ period is the inverse of the rated frequency of the CPU. | |
+ The CPU cycle counter is most useful for shorter functions: for longer | |
+ functions, CPU cycle counter sees the effect of time interrupts and | |
+ preemption. Unlike the timing returned by Sys.time or | |
+ times, the cycle counter is real. That is, it counts even | |
+ when other process has the control of the CPU. CPU cycle counter | |
+ sees the effect of caching, etc. | |
+ | |
+ See | |
+ http://cedar.intel.com/software/idap/media/pdf/rdtscpm1.pdf | |
+ for more details on cycle counter. | |
+ | |
+ For consistency with the other timing interface, our function should | |
+ invoke 'pt s n at' and pass the number of invocations of the | |
+ function to time (n) and the elapsed time, in _milli_seconds. | |
+ Therefore, we have to deterimine the calibrartion coefficient from | |
+ cycles to seconds. | |
+ | |
+ To avoid the interference from time interrupts and scheduler | |
+ pre-emptions, we repeat the timing measurements to make the elapsed | |
+ time last up to 9 milliseconds, assuming a 100 Hz scheduling clock. | |
+ We accumulate the count of all the executions of the functions and | |
+ return the second smallest. We do a few dry runs of the function to | |
+ warm up the caches. | |
+*) | |
+ | |
+(* We target our cycle counters to last no more than the following number | |
+ of seconds. | |
+ The number is 90% of the scheduling interval (typically 10 ms). | |
+ It's quite likely that the current process will be preempted after | |
+ it consumes its quantum, and its priority lowered. | |
+*) | |
+let cycle_timing_target = 0.009 | |
+ | |
+ | |
+(* Cycles to seconds calibration code. | |
+ get_cycles_per_second: () -> float | |
+ Return the number of cycles per second. | |
+*) | |
+ | |
+let get_cycles_per_second () = | |
+ let calib_fn n = for i=1 to n do let _ = sin 0.8 in () done in | |
+ let repeat = 5 in (* How many times to repeat everything*) | |
+ let n_target = (* How many times to repeat calib_fn*) | |
+ let () = calib_fn 10 in (* warm up the cache *) | |
+ let rec loop n1 n2 = | |
+ let (tbeg,_) = times () in | |
+ let () = calib_fn n2 in | |
+ let exp = fst (times ()) -. tbeg in | |
+ if exp > cycle_timing_target then | |
+ if n1 = n2 then n1 else | |
+ if exp > 1.09 *. cycle_timing_target | |
+ then loop n1 ((n1 + n2)/2) else n2 | |
+ else loop n2 (2*n2) in | |
+ loop 1000 1000 | |
+ in | |
+ let do_cycles () = | |
+ let () = calib_fn 10 in (* warm up the cache *) | |
+ let cbeg = get_ticks () in | |
+ let () = calib_fn n_target in | |
+ elapsed_ticks cbeg | |
+ in | |
+ let do_times () = | |
+ let () = calib_fn 10 in (* warm up the cache *) | |
+ let (tbeg,_) = times () in | |
+ let () = calib_fn n_target in | |
+ fst (times ()) -. tbeg | |
+ in | |
+ let cycles_est = (* the second smallest datum *) | |
+ let d1 = do_cycles () in | |
+ let d2 = do_cycles () in | |
+ let rec loop d1 d2 n = | |
+ if n = 0 then d2 else | |
+ let d = do_cycles () in | |
+ if d < d1 then loop d d1 (n-1) else | |
+ if d < d2 then loop d1 d (n-1) else loop d1 d2 (n-1) in | |
+ loop (min d1 d2) (max d1 d2) repeat | |
+ in | |
+ let secs_est = (* the second smallest datum *) | |
+ let d1 = do_times () in | |
+ let d2 = do_times () in | |
+ let rec loop d1 d2 n = | |
+ if n = 0 then d2 else | |
+ let d = do_times () in | |
+ if d < d1 then loop d d1 (n-1) else | |
+ if d < d2 then loop d1 d (n-1) else loop d1 d2 (n-1) in | |
+ loop (min d1 d2) (max d1 d2) repeat | |
+ in | |
+ (* taking the second smallets datum for secs_est seems to give | |
+ more robust calibration coefficient, with smaller variance. Therefore, | |
+ the following averaging code is commented out. *) | |
+(* let secs_est1 = *) | |
+(* let rec loop accum n = *) | |
+(* if n = 0 then accum else *) | |
+(* loop (accum +. do_times ()) (n-1) *) | |
+(* in (loop 0.0 repeat) /. (float repeat) *) | |
+ cycles_est /. secs_est | |
+ | |
+let cycles_per_second = ref 0.0 | |
+ | |
+(* execute function f reasonable number of times and obtain the timing | |
+ data, based on the cycles counter. | |
+ s is an identification string. | |
+ return the result of f. | |
+ We use the function "pt s n at" to record the timing info. | |
+ The last argument of pt is timing in _milliseconds_! | |
+ We try to run f as many times to be close to | |
+ cycle_timing_target as possible. It makes no sense | |
+ to run f() for longer intervals as we inevitably get scheduled out. | |
+*) | |
+ | |
+let timecycles s f = | |
+ let () = if !cycles_per_second = 0.0 then | |
+ cycles_per_second := get_cycles_per_second () else () in | |
+ let rough_timing = (* in secs *) | |
+ let _ = f () in | |
+ let _ = f () in | |
+ let cbeg = get_ticks () in | |
+ let _ = f () in | |
+ (elapsed_ticks cbeg) /. !cycles_per_second in | |
+ let short_function = rough_timing < cycle_timing_target /. 50.0 in | |
+ let count_major = if short_function then | |
+ cycle_timing_target /. (5.0 *. rough_timing) else | |
+ cycle_timing_target /. rough_timing in | |
+ let count_major = max (int_of_float count_major) 5 in | |
+ let do_cycle f = (* the second smallest datum *) | |
+ if short_function then | |
+ let _ = f () in | |
+ let _ = f () in (* warm up the caches *) | |
+ let d1 = let cbeg = get_ticks () in | |
+ f(); f(); f(); f(); f(); elapsed_ticks cbeg in | |
+ let d2 = let cbeg = get_ticks () in | |
+ f(); f(); f(); f(); f(); elapsed_ticks cbeg in | |
+ let rec loop d1 d2 n = | |
+ if n = 0 then d2 else | |
+ let d = let cbeg = get_ticks () in | |
+ f(); f(); f(); f(); f(); elapsed_ticks cbeg in | |
+ if d < d1 then loop d d1 (n-1) else | |
+ if d < d2 then loop d1 d (n-1) else loop d1 d2 (n-1) in | |
+ loop (min d1 d2) (max d1 d2) (count_major-2) | |
+ else | |
+ let _ = f () in (* warm up the caches *) | |
+ let d1 = let cbeg = get_ticks () in f(); elapsed_ticks cbeg in | |
+ let d2 = let cbeg = get_ticks () in f(); elapsed_ticks cbeg in | |
+ let rec loop d1 d2 n = | |
+ if n = 0 then d2 else | |
+ let d = let cbeg = get_ticks () in f(); elapsed_ticks cbeg in | |
+ if d < d1 then loop d d1 (n-1) else | |
+ if d < d2 then loop d1 d (n-1) else loop d1 d2 (n-1) in | |
+ loop (min d1 d2) (max d1 d2) (count_major-2) | |
+ in | |
+ let result = f () in | |
+ let count_raw = do_cycle f in | |
+(* | |
+ let count_dummy = do_cycle (fun () -> ()) in | |
+ let count = if short_function then (count_raw -. count_dummy) /. 5.0 | |
+ else (count_raw -. count_dummy) in | |
+*) | |
+ let count = if short_function then count_raw /. 5.0 | |
+ else count_raw in | |
+ let () = pt s (if short_function then 5 else 1) | |
+ (1000.0 *. count /. !cycles_per_second) in | |
+ result | |
+;; | |
+ | |
+*) | |
diff -Naur ocaml-4.02.1/metalib/trxtime.mli ocaml-ber-n102/metalib/trxtime.mli | |
--- ocaml-4.02.1/metalib/trxtime.mli 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/metalib/trxtime.mli 2015-01-10 16:27:06.936030975 +0000 | |
@@ -0,0 +1,34 @@ | |
+(* time n s f executes f() n times and records message s plus elapsed time *) | |
+val time : int -> string -> (unit -> 'a) -> 'a | |
+val timenew : string -> (unit -> 'a) -> 'a | |
+val init_times : unit -> unit | |
+(* flushes recorded times *) | |
+val print_times : unit -> unit | |
+val get_times : unit -> ((string * int * float) list) | |
+ | |
+(* To be activated later.... | |
+ | |
+(* More precise timing functions *) | |
+type ticks | |
+ | |
+ | |
+external times: unit -> float * float = "sys_times" | |
+(* returns user and system process times, in whole and fractional seconds. | |
+ Resolution upto a microsecond. | |
+ *) | |
+ | |
+external get_ticks: unit -> ticks = "sys_get_ticks" | |
+(* Gets the CPU cycle counter as an abstract object. See cycle.h | |
+ from the FFTW project *) | |
+ | |
+external elapsed_ticks: ticks -> float = "sys_elapsed_ticks" | |
+(* Returns the elapsed time in abstract units from the | |
+ specified moment till now. *) | |
+ | |
+val timecycles: string -> (unit -> 'a) -> 'a | |
+(* like timenew but uses the CPU cycle counter for timing. | |
+ It's better for shorter functions that can run without being | |
+ scheduled out. | |
+*) | |
+ | |
+*) | |
diff -Naur ocaml-4.02.1/parsing/lexer.mll ocaml-ber-n102/parsing/lexer.mll | |
--- ocaml-4.02.1/parsing/lexer.mll 2015-01-10 16:27:54.270865651 +0000 | |
+++ ocaml-ber-n102/parsing/lexer.mll 2015-01-10 16:27:06.892032059 +0000 | |
@@ -270,6 +270,8 @@ | |
['A'-'Z' 'a'-'z' '_' '\192'-'\214' '\216'-'\246' '\248'-'\255' '\'' '0'-'9'] | |
let symbolchar = | |
['!' '$' '%' '&' '*' '+' '-' '.' '/' ':' '<' '=' '>' '?' '@' '^' '|' '~'] | |
+let symbolcharnodot = (* NNN *) | |
+ ['!' '$' '%' '&' '*' '+' '-' '/' ':' '<' '=' '>' '?' '@' '^' '|' '~'] (* NNN *) | |
let decimal_literal = | |
['0'-'9'] ['0'-'9' '_']* | |
let hex_literal = | |
@@ -302,6 +304,9 @@ | |
} | |
| blank + | |
{ token lexbuf } | |
+ | ".<" { DOTLESS } (* NNN *) | |
+ | ">." { GREATERDOT } (* NNN *) | |
+ | ".~" { DOTTILDE } (* NNN *) | |
| "_" | |
{ UNDERSCORE } | |
| "~" | |
@@ -472,8 +477,10 @@ | |
{ PREFIXOP(Lexing.lexeme lexbuf) } | |
| ['~' '?'] symbolchar + | |
{ PREFIXOP(Lexing.lexeme lexbuf) } | |
- | ['=' '<' '>' '|' '&' '$'] symbolchar * | |
+ | ['=' '<' '|' '&' '$'] symbolchar * (* NNN: ">." is not INFIXOP0 *) | |
{ INFIXOP0(Lexing.lexeme lexbuf) } | |
+ | ['>'] symbolcharnodot symbolchar * (* NNN exclude ">." case *) | |
+ { INFIXOP0(Lexing.lexeme lexbuf) } (* NNN *) | |
| ['@' '^'] symbolchar * | |
{ INFIXOP1(Lexing.lexeme lexbuf) } | |
| ['+' '-'] symbolchar * | |
@@ -483,6 +490,8 @@ | |
| '%' { PERCENT } | |
| ['*' '/' '%'] symbolchar * | |
{ INFIXOP3(Lexing.lexeme lexbuf) } | |
+ | "let" symbolchar* (* NNN *) | |
+ { LETOP(Lexing.lexeme lexbuf) } (* NNN *) | |
| eof { EOF } | |
| _ | |
{ raise (Error(Illegal_character (Lexing.lexeme_char lexbuf 0), | |
diff -Naur ocaml-4.02.1/parsing/parser.mly ocaml-ber-n102/parsing/parser.mly | |
--- ocaml-4.02.1/parsing/parser.mly 2015-01-10 16:27:54.270865651 +0000 | |
+++ ocaml-ber-n102/parsing/parser.mly 2015-01-10 16:27:06.892032059 +0000 | |
@@ -273,6 +273,21 @@ | |
in | |
(exp, ghtyp(Ptyp_poly(newtypes,varify_constructors newtypes core_type))) | |
+ (* NNN: the whole definition *) | |
+let let_operator op bindings cont = | |
+ let pat, expr = | |
+ match bindings with | |
+ | [] -> assert false | |
+ | [x] -> (x.pvb_pat,x.pvb_expr) | |
+ | l -> | |
+ let pats, exprs = | |
+ List.fold_right | |
+ (fun {pvb_pat=p;pvb_expr=e} (ps,es) -> (p::ps,e::es)) l ([],[]) in | |
+ ghpat (Ppat_tuple pats), ghexp (Pexp_tuple exprs) | |
+ in | |
+ mkexp(Pexp_apply(op, [("", expr); | |
+ ("", ghexp(Pexp_fun("", None, pat, cont)))])) | |
+ | |
let wrap_exp_attrs body (ext, attrs) = | |
(* todo: keep exact location for the entire attribute *) | |
let body = {body with pexp_attributes = attrs @ body.pexp_attributes} in | |
@@ -293,6 +308,9 @@ | |
/* Tokens */ | |
+%token DOTLESS /* NNN */ | |
+%token GREATERDOT /* NNN */ | |
+%token DOTTILDE /* NNN */ | |
%token AMPERAMPER | |
%token AMPERSAND | |
%token AND | |
@@ -358,6 +376,7 @@ | |
%token LESS | |
%token LESSMINUS | |
%token LET | |
+%token <string> LETOP /* NNN */ | |
%token <string> LIDENT | |
%token LPAREN | |
%token LBRACKETAT | |
@@ -441,6 +460,7 @@ | |
%nonassoc below_SEMI | |
%nonassoc SEMI /* below EQUAL ({lbl=...; lbl=...}) */ | |
%nonassoc LET /* above SEMI ( ...; let ... in ...) */ | |
+%nonassoc LETOP /* NNN */ | |
%nonassoc below_WITH | |
%nonassoc FUNCTION WITH /* below BAR (match ... with ...) */ | |
%nonassoc AND /* above WITH (module rec A: SIG with ... and ...) */ | |
@@ -468,6 +488,7 @@ | |
%nonassoc prec_unary_minus prec_unary_plus /* unary - */ | |
%nonassoc prec_constant_constructor /* cf. simple_expr (C versus C x) */ | |
%nonassoc prec_constr_appl /* above AS BAR COLONCOLON COMMA */ | |
+%left prec_escape /* NNN */ | |
%nonassoc below_SHARP | |
%nonassoc SHARP /* simple_expr/toplevel_directive */ | |
%nonassoc below_DOT | |
@@ -1084,6 +1105,8 @@ | |
{ mkexp(Pexp_apply($1, List.rev $2)) } | |
| LET ext_attributes rec_flag let_bindings_no_attrs IN seq_expr | |
{ mkexp_attrs (Pexp_let($3, List.rev $4, $6)) $2 } | |
+ | let_operator ext_attributes let_bindings IN seq_expr /* NNN */ | |
+ { wrap_exp_attrs (let_operator $1 $3 $5) $2 } /* NNN */ | |
| LET MODULE ext_attributes UIDENT module_binding_body IN seq_expr | |
{ mkexp_attrs (Pexp_letmodule(mkrhs $4 4, $5, $7)) $3 } | |
| LET OPEN override_flag ext_attributes mod_longident IN seq_expr | |
@@ -1200,6 +1223,12 @@ | |
{ reloc_exp $2 } | |
| LPAREN seq_expr error | |
{ unclosed "(" 1 ")" 3 } | |
+ | DOTLESS expr GREATERDOT /* NNN */ | |
+ { wrap_exp_attrs $2 | |
+ (None,[ghloc "metaocaml.bracket",PStr []]) } /* NNN */ | |
+ | DOTTILDE simple_expr %prec prec_escape /* NNN */ | |
+ { wrap_exp_attrs $2 | |
+ (None,[ghloc "metaocaml.escape",PStr []]) } /* NNN */ | |
| BEGIN ext_attributes seq_expr END | |
{ wrap_exp_attrs (reloc_exp $3) $2 (* check location *) } | |
| BEGIN ext_attributes END | |
@@ -1961,6 +1990,7 @@ | |
| INFIXOP2 { $1 } | |
| INFIXOP3 { $1 } | |
| INFIXOP4 { $1 } | |
+ | LETOP { $1 } /* NNN */ | |
| BANG { "!" } | |
| PLUS { "+" } | |
| PLUSDOT { "+." } | |
@@ -1978,6 +2008,16 @@ | |
| PLUSEQ { "+=" } | |
| PERCENT { "%" } | |
; | |
+ /* NNN: the whole definition */ | |
+let_operator: | |
+ LETOP { mkexp (Pexp_ident( | |
+ mkloc (Lident $1) | |
+ (symbol_rloc ()))) } | |
+ | mod_longident DOT LETOP { mkexp (Pexp_ident( | |
+ mkloc (Ldot($1,$3)) | |
+ (symbol_rloc ()))) } | |
+; | |
+ | |
constr_ident: | |
UIDENT { $1 } | |
/* | LBRACKET RBRACKET { "[]" } */ | |
diff -Naur ocaml-4.02.1/parsing/pprintast.ml ocaml-ber-n102/parsing/pprintast.ml | |
--- ocaml-4.02.1/parsing/pprintast.ml 2015-01-10 16:27:54.270865651 +0000 | |
+++ ocaml-ber-n102/parsing/pprintast.ml 2015-01-10 16:27:06.896031960 +0000 | |
@@ -47,6 +47,8 @@ | |
let view_fixity_of_exp = function | |
| {pexp_desc = Pexp_ident {txt=Lident l;_};_} -> fixity_of_string l | |
+ | {pexp_desc = Pexp_ident {txt=Ldot (Lident "Pervasives",l);_};_} -> (*NNN*) | |
+ fixity_of_string l (*NNN*) | |
| _ -> `Normal ;; | |
let is_infix = function | `Infix _ -> true | _ -> false | |
@@ -512,11 +514,44 @@ | |
end | |
| _ -> false | |
method expression f x = | |
- if x.pexp_attributes <> [] then begin | |
+ (* NNN begin *) | |
+ (* Keep in mind that there may be several metaocaml | |
+ attributes, and the order matters *) | |
+ (* Here we assume that all metaocaml attributes are at the front, | |
+ which is how they are generated. | |
+ *) | |
+ match x.pexp_attributes with | |
+ | ({txt="metaocaml.bracket"},_) :: t -> | |
+ pp f "@[<hov2>.<@ %a @ >.@]" self#expression {x with pexp_attributes=t} | |
+ | ({txt="metaocaml.escape"},_) :: t -> | |
+ begin | |
+ match x.pexp_desc with | |
+ | Pexp_ident li when t = [] -> pp f ".~%a" self#longident_loc li | |
+ | _ -> pp f ".~%a" (self#paren true self#expression) | |
+ {x with pexp_attributes=t} | |
+ end | |
+ | [({txt = "metaocaml.csp"},PStr [{pstr_desc = | |
+ Pstr_eval ({pexp_desc=Pexp_ident li},_)}])] -> | |
+ begin | |
+ (* This CSP is easy to print, so we print it *) | |
+ match x.pexp_desc with | |
+ | Pexp_apply (_,[("",{pexp_desc=Pexp_constant (Const_int _)})]) | |
+ -> | |
+ pp f "(* CSP %a *) %a" | |
+ self#longident_loc li | |
+ self#expression {x with pexp_attributes=[]} | |
+ | _ -> | |
+ pp f "(* CSP %a *)" | |
+ self#longident_loc li | |
+ end | |
+ (* if x.pexp_attributes <> [] then begin *) | |
+ | _::_ -> | |
pp f "((%a)%a)" self#expression {x with pexp_attributes=[]} | |
self#attributes x.pexp_attributes | |
- end | |
- else match x.pexp_desc with | |
+ (* end *) | |
+ | _ -> begin match x.pexp_desc with | |
+ (* else match x.pexp_desc with *) | |
+ (* NNN end *) | |
| Pexp_function _ | Pexp_fun _ | Pexp_match _ | Pexp_try _ | Pexp_sequence _ | |
when pipe || semi -> | |
self#paren true self#reset#expression f x | |
@@ -620,6 +655,7 @@ | |
pp f "@[<2>`%s@;%a@]" l self#simple_expr eo | |
| Pexp_extension e -> self#extension f e | |
| _ -> self#expression1 f x | |
+ end (* NNN *) | |
method expression1 f x = | |
if x.pexp_attributes <> [] then self#expression f x | |
else match x.pexp_desc with | |
diff -Naur ocaml-4.02.1/typing/predef.ml ocaml-ber-n102/typing/predef.ml | |
--- ocaml-4.02.1/typing/predef.ml 2015-01-10 16:27:54.270865651 +0000 | |
+++ ocaml-ber-n102/typing/predef.ml 2015-01-10 16:27:06.932031073 +0000 | |
@@ -120,6 +120,17 @@ | |
and ident_cons = ident_create "::" | |
and ident_none = ident_create "None" | |
and ident_some = ident_create "Some" | |
+ | |
+(* NNN ident_create "code" must be placed at the end of all other | |
+ ident creation expressions, to make sure that creating ident_code | |
+ does not shift the timestamps of other standard idents like | |
+ Eof, etc. Otherwise, binary compatibility with OCaml breaks, | |
+ and we have to do the expensive bootstrapping. | |
+*) | |
+let ident_code = ident_create "code" (* NNN *) | |
+let path_code = Pident ident_code (* NNN *) | |
+let type_code t = newgenty (Tconstr(path_code, [t], ref Mnil)) (* NNN *) | |
+ | |
let common_initial_env add_type add_extension empty_env = | |
let decl_bool = | |
{decl_abstr with | |
@@ -157,6 +168,13 @@ | |
type_params = [tvar]; | |
type_arity = 1; | |
type_variance = [Variance.covariant]} | |
+ (* NNN added decl_code *) | |
+ and decl_code = | |
+ let tvar = newgenvar() in | |
+ {decl_abstr with | |
+ type_params = [tvar]; | |
+ type_arity = 1; | |
+ type_variance = [Variance.covariant]} | |
in | |
let add_extension id l = | |
@@ -184,6 +202,7 @@ | |
[newgenty (Ttuple[type_string; type_int; type_int])] ( | |
add_extension ident_undefined_recursive_module | |
[newgenty (Ttuple[type_string; type_int; type_int])] ( | |
+ add_type ident_code decl_code ( (* NNN *) | |
add_type ident_int64 decl_abstr ( | |
add_type ident_int32 decl_abstr ( | |
add_type ident_nativeint decl_abstr ( | |
@@ -198,7 +217,8 @@ | |
add_type ident_string decl_abstr ( | |
add_type ident_char decl_abstr ( | |
add_type ident_int decl_abstr ( | |
- empty_env)))))))))))))))))))))))))) | |
+ empty_env)))))))))))))))))))))))))) ) (* NNN extra parenthesis *) | |
+ | |
let build_initial_env add_type add_exception empty_env = | |
let common = common_initial_env add_type add_exception empty_env in | |
diff -Naur ocaml-4.02.1/typing/predef.mli ocaml-ber-n102/typing/predef.mli | |
--- ocaml-4.02.1/typing/predef.mli 2015-01-10 16:27:54.270865651 +0000 | |
+++ ocaml-ber-n102/typing/predef.mli 2015-01-10 16:27:06.932031073 +0000 | |
@@ -29,6 +29,7 @@ | |
val type_int32: type_expr | |
val type_int64: type_expr | |
val type_lazy_t: type_expr -> type_expr | |
+val type_code: type_expr -> type_expr (* NNN *) | |
val path_int: Path.t | |
val path_char: Path.t | |
@@ -45,6 +46,7 @@ | |
val path_int32: Path.t | |
val path_int64: Path.t | |
val path_lazy_t: Path.t | |
+val path_code: Path.t (* NNN *) | |
val path_match_failure: Path.t | |
val path_assert_failure : Path.t | |
diff -Naur ocaml-4.02.1/typing/trx.ml ocaml-ber-n102/typing/trx.ml | |
--- ocaml-4.02.1/typing/trx.ml 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/typing/trx.ml 2015-01-10 16:27:06.932031073 +0000 | |
@@ -0,0 +1,2398 @@ | |
+(* | |
+ This file is to post-process the Typedtree built by the type checker | |
+ when it finished parsing the present-stage bracket expression. | |
+ (Unlike versions before N102, the full Typedtree is not traversed; | |
+ only the bracket expressions are traversed and lifted.) The | |
+ transformation is done by trx_bracket. | |
+ | |
+ For example, | |
+ <succ 1> | |
+ gets transformed to | |
+ mkApp <succ> <1> | |
+ and eventually to | |
+ mkApp (mkIdent "succ") (mkConst 1) | |
+ One may say that we `push the brackets inside'. We replace brackets | |
+ with calls to functions that will construct, at run-time, a | |
+ Parsetree, which is the representation of values of the code type. | |
+ | |
+ Generally, the Parsetree is constructed when the program is run. | |
+ In some cases we can construct the Parsetree at compile time, | |
+ that is, when this trx.ml is run. Constants like <1> is such a case. | |
+ If we see <1>, or, in terms of trees, | |
+ Texp_bracket (Texp_constant (Constant_int 1)) | |
+ we can immediately construct the Parsetree: | |
+ Pexp_constant (Constant_int 1) | |
+ After we construct the Parsetree at compile time, we use CSP to | |
+ pass it over to run-time. At run-time, we merely use the compiled constant. | |
+ This mechanism of building Parsetree at compile-time whenever possible | |
+ is one of the large differences from the previous versions of MetaOCaml. | |
+ | |
+ Future-stage Bindings. | |
+ Future-stage bindings are introduced by patterns in let, fun, | |
+ match, try and for forms. Global bindings are always at present-stage. | |
+ Since local modules in brackets are not allowed, all future bindings are | |
+ unqualified (i.e., simple names, without the module path). | |
+ The principal rule of translating binding forms is | |
+ <fun x -> e> ---> let x = gensym "x" in mkLAM x <e> | |
+ Emphatically, gensym cannot be run at compile time! | |
+ Reason: consider the recursive invocation: | |
+ let rec f z = <fun y -> ~( ... f 1 ... )> | |
+ | |
+ Thus, at run-time, we generate new names for bound variables and | |
+ use the OCaml's evaluator (the `run-time') to substitute these | |
+ new names in <e>. Therefore, a future-stage bound variable after | |
+ the translation becomes a present-stage bound variable, | |
+ but at a different type: string loc. We use string loc rather | |
+ than Longident.t loc since all, at present, future-stage bindings | |
+ are simple names. | |
+ | |
+ We now check for scope extrusion: we enforce the region discipline | |
+ for generated identifiers. To make it easier to impose checks, | |
+ the translation rule is modified as follows | |
+ <fun x -> e> ---> build_simple_fun "x" (fun x -> <e>) | |
+ One can say that <fun x -> e> of the type (a->b) code is translated | |
+ into (fun x -> <e>) of the type a code -> b code. This looks quite | |
+ like the HOAS syntax for lambda (see the code-generation approach | |
+ with code combinators. The function build_simple_fun generates a gensym | |
+ and establishes a region for the gensym variable. | |
+ | |
+ OCaml has more complicated functions, <function pattern -> body> with | |
+ complex patterns. If the patterns contain no binding variables, | |
+ there is no need to go into the gensym generation. The translation is | |
+ no more complex than that of <lazy e>. | |
+ For more complicated binding patterns, we generalize, for example | |
+ <fun (x1,true,x2) as x3 -> e1 | _ -> e2> ---> | |
+ build_fun ["x1";"x2"] (fun (x1,x2,x3) -> [<e1>;e2]) | |
+ That is, we pick all binding variables from the pattern and build a | |
+ function that receives the code for these variables and produces the | |
+ array of code for all alternatives. | |
+ | |
+ Here are the main patterns of scope extrusion | |
+ let r = <0> in | |
+ <fun x -> ~(r := <x + 1>; <()>)> | |
+ | |
+ let r = <0> in | |
+ <fun x -> .~(<fun x -> ~(let v = <x> in r := <fun x -> ~v>; <()>)>; !r)> | |
+ | |
+ exception E of int code | |
+ try <fun x -> ~(raise (E <x>)> with E x -> x | |
+ (actually we need a local polymorphic exception, but the idea is the same) | |
+ | |
+ and similar using control effects. | |
+ | |
+ The most obvious method of detecting the scope extrusion is traversing | |
+ the generated code looking for unbound identifiers. We can do | |
+ such a check when we are about to show or run the code, or | |
+ about to splice (in the latter case, we have to maintain | |
+ the dynamic environment of gensym'ed names generated by | |
+ build_fun_). However, reporting the scope | |
+ extrusion upon printing or running the code is reporting it too | |
+ late. Doing the check on each escape (since only splices can incorporate | |
+ scope-extruded code) means many repeated traversals of the generated | |
+ code. | |
+ | |
+ We use a different method: we mark each piece of the generated code | |
+ with the list of free variable the code contains. Each variable | |
+ is associated with a `stackmark', which identifiers the region | |
+ with which the variable is associated. All valid stackmarks form | |
+ a total order. Alas, delimited control can reshuffle that order. | |
+ The function build_simple_fun and others enter a new region | |
+ and then check that the generated body contains only valid stackmarks | |
+ (that is, stackmarks that correspond to active regions). | |
+ Every code building function ( build_* ) checks to see that the stackmarks | |
+ in the incorporated fragments are all valid, that is, | |
+ correspond currently alive variables. These code building function | |
+ merge the free variable lists (heaps actually) from the incorporated | |
+ fragments. | |
+ | |
+This file was based on trx.ml from the original MetaOCaml, but it is | |
+completely re-written from scratch and has many comments. The | |
+traversal algorithm, the way of compiling Parsetree builders, dealing | |
+ with CSP and many other algorithms are all different. | |
+ | |
+*) | |
+ | |
+open Parsetree | |
+open Asttypes | |
+open Typedtree | |
+open Types | |
+ | |
+ | |
+(*{{{ Preliminaries, common functions *) | |
+ | |
+(* BER MetaOCaml version string *) | |
+let meta_version = "N 102" | |
+ | |
+(* Co-opt the Preprocessor class of warnings *) | |
+let debug_print ?(loc = Location.none) : string -> unit = fun msg -> | |
+ Location.prerr_warning loc (Warnings.Preprocessor msg) | |
+ | |
+(* Emit a translation-time error *) | |
+exception Error of Location.error | |
+ | |
+let trx_error loc_err = raise @@ Error loc_err | |
+ | |
+let () = | |
+ Location.register_error_of_exn | |
+ (function | |
+ | Error err -> Some err | |
+ | _ -> None | |
+ ) | |
+ | |
+let not_supported loc msg = | |
+ trx_error @@ Location.errorf ~loc | |
+ "%s is not yet supported within brackets" msg | |
+ | |
+(* left-to-right accumulating map *) | |
+let rec map_accum : ('accum -> 'a -> 'b * 'accum) -> 'accum -> 'a list -> | |
+ 'b list * 'accum = fun f acc -> function | |
+ | [] -> ([],acc) | |
+ | h::t -> | |
+ let (h,acc) = f acc h in | |
+ let (t,acc) = map_accum f acc t in | |
+ (h::t, acc) | |
+ | |
+let initial_env = Env.initial_safe_string | |
+ | |
+(* Attributes *) | |
+(* In a Parsetree, brackets, escape and CSPs are attributes on | |
+ the corresponding nodes. | |
+*) | |
+ | |
+let attr_bracket = (Location.mknoloc "metaocaml.bracket",PStr []) | |
+ | |
+let attr_escape = (Location.mknoloc "metaocaml.escape",PStr []) | |
+ | |
+let rec get_attr : string -> attributes -> Parsetree.structure option = | |
+ fun name -> function | |
+ | [] -> None | |
+ | ({txt = n}, PStr str) :: _ when n = name -> Some str | |
+ | _ :: t -> get_attr name t | |
+ | |
+let attr_csp : Longident.t loc -> attribute = fun lid -> | |
+ (Location.mknoloc "metaocaml.csp",PStr [ | |
+ Ast_helper.Str.eval (Ast_helper.Exp.ident lid)]) | |
+ | |
+(* If the attribute is present, the expression is non-expansive | |
+ We use physical equality comparison, to speed things up | |
+*) | |
+let attr_nonexpansive : attribute = | |
+ (Location.mknoloc "metaocaml.nonexpansive",PStr []) | |
+ | |
+ | |
+(* The result of what_stage_attr *) | |
+type stage_attr_elim = | |
+ | Stage0 | |
+ | Bracket of attribute * (* bracket attribute *) | |
+ attributes (* other attributes *) | |
+ | Escape of attribute * (* escape attribute *) | |
+ attributes (* other attributes *) | |
+ | CSP of attribute * Longident.t loc * (* CSP attribute and lid *) | |
+ attributes (* other attributes *) | |
+ | |
+(* Determining if an AST node bears a staging attribute *) | |
+let what_stage_attr : attributes -> stage_attr_elim = | |
+ let rec loop acc = function | |
+ | [] -> Stage0 | |
+ | (({txt = "metaocaml.bracket"},_) as a) :: t -> | |
+ Bracket (a,acc @ t) | |
+ | (({txt = "metaocaml.escape"},_) as a) :: t -> | |
+ Escape (a,acc @ t) | |
+ | (({txt = "metaocaml.csp"},PStr [{pstr_desc = | |
+ Pstr_eval ({pexp_desc=Pexp_ident lid},_)}]) as a) :: t -> | |
+ CSP (a,lid,acc @ t) | |
+ | a :: t -> loop (a::acc) t | |
+ in loop [] | |
+ | |
+(* Staging level | |
+ It is set via an attribute on the value_description in the Typedtree | |
+*) | |
+type stage = int (* staging level *) | |
+ | |
+let attr_level n = | |
+ (Location.mknoloc "metaocaml.level",PStr [ | |
+ Ast_helper.Str.eval (Ast_helper.Exp.constant (Const_int n))]) | |
+ | |
+let get_level : Parsetree.attributes -> stage = fun attrs -> | |
+ match get_attr "metaocaml.level" attrs with | |
+ | None -> 0 | |
+ | Some [{pstr_desc = | |
+ Pstr_eval ({pexp_desc=Pexp_constant (Const_int n)},_)}] -> | |
+ assert (n>=0); n | |
+ | _ -> assert false (* Invalid level attribute *) | |
+ | |
+ | |
+(* In a Typedtree, <e> is represented as a sequence | |
+ begin 0; e end | |
+ again, with the corresponding attribute. | |
+ I chose 0 rather than () because if we forget to handle | |
+ bracket/escape properly, we get a warning. Still, begin 0; e end | |
+ is syntactically and type-correct . | |
+ Ditto for Escape. | |
+*) | |
+ | |
+(*}}}*) | |
+ | |
+ | |
+(*{{{ Path and location utilities *) | |
+ | |
+(* ------------------------------------------------------------------------ *) | |
+(* Path utilities *) | |
+ | |
+(* We always use path when available, and convert it to Longident | |
+ when needed -- even if the Typedtree already carries the longident. | |
+ The path is preferred because it is fully qualified for | |
+ external identifiers and it is unambiguous. | |
+ If we open a module, its components can be referred to without | |
+ qualification -- the path will be qualified nevertheless. | |
+ When we build a Parsetree representing the generated code, | |
+ we have to use fully qualified identifiers since the open statement | |
+ in the original code won't be represented in the generated | |
+ Parsetree. | |
+*) | |
+ | |
+(* Check to see if a path refers to an identifier, exception, or | |
+ constructor that is available from an external module. If so, the run-time | |
+ compiler invoked by run can get the definition for the identifier from | |
+ a .cmi file. The value of an external identifier can be obtained from | |
+ a .cmo file. | |
+ If a path contains several components like | |
+ M1.M2.M3.ident, we should check if the top-most component, that is, M1, | |
+ is external. | |
+*) | |
+(* XXX call Env.normalize_path first? *) | |
+ | |
+let rec is_external = function | |
+ | Path.Pident id -> (* not qualified *) | |
+ Ident.persistent id || Ident.global id || Ident.is_predef_exn id | |
+ | Path.Papply _ -> false | |
+ | Path.Pdot(p, _,_) -> is_external p | |
+ | |
+(* Convert a path to an identifier. Since the path is assumed to be | |
+ `global', time stamps don't matter and we can use just strings. | |
+*) | |
+let rec path_to_lid : Path.t -> Longident.t = function | |
+ | Path.Pident i -> Longident.Lident (Ident.name i) | |
+ | Path.Pdot (p,s,_) -> Longident.Ldot (path_to_lid p, s) | |
+ | Path.Papply (p1,p2) -> | |
+ Longident.Lapply(path_to_lid p1, path_to_lid p2) | |
+ | |
+(* Convert the path to lid but use the given str as the last component. | |
+ This in effect qualifies 'str' with the given module path | |
+*) | |
+let path_to_lid_but_last : Path.t -> string -> Longident.t = | |
+ fun p str -> | |
+ match p with | |
+ | Path.Pident _ -> Longident.Lident str | |
+ | Path.Pdot (p,_,pos) -> path_to_lid (Path.Pdot (p,str,pos)) | |
+ | _ -> assert false | |
+ | |
+(* Replace the last component of p1 with p2, which should be a Pident | |
+ path | |
+*) | |
+let path_replace_last : Path.t -> Path.t -> Path.t = fun p1 p2 -> | |
+ match (p1,p2) with | |
+ | (Path.Pident _,x) -> x | |
+ | (Path.Pdot(p1,_,s),Path.Pident id) -> Path.Pdot(p1,Ident.name id,s) | |
+ | _ -> assert false | |
+ | |
+ | |
+(* Check to make sure a constructor, label, exception, etc. | |
+ have the name that we can put into AST (Parsetree). | |
+ Local names can't be put into the Parsetree since the type env in which | |
+ they are declared is not represented in the Parsetree. | |
+*) | |
+let check_path_quotable msg path = | |
+ if not (is_external path) then | |
+ trx_error @@ Location.errorf | |
+ "%s %s cannot be used within brackets. Put into a separate file." | |
+ msg (Path.name path) | |
+ | |
+ | |
+(* Check to see that a constructor belongs to a type defined | |
+ in a persistent module or in the initial environment. | |
+ Return the fully qualified name to put into AST | |
+ (Pervasive constructors remain unqualified however). | |
+ | |
+ We have nothing to do if the constructor is already fully qualified | |
+ with a persistent module identifier: for example, Scanf.Scan_failure. | |
+ The major complexity comes from this scenario: | |
+ open Scanf | |
+ .<raise (Scan_failure "xx")>. | |
+ The Texp_construct node of Typedtree contains the lid and (was: the | |
+ path) that refer to "Scan_failure" without any module qualifications. | |
+ We have to find the fully qualified path and check | |
+ that it is external. We do that by finding the path for the _type_ | |
+ constructor, for the type of which the data constructor is a member. | |
+ That type_path is fully qualified. We can ascertain the later fact | |
+ from Env.constructors_of_type, which puts the complete path | |
+ into the type of the constructor, which is always of the form | |
+ Tconstr(ty_path,_,_). The function constructors_of_type is used | |
+ within Env.store_type, which is used when opening a module. | |
+ | |
+ Alternatively we could've used Env.lookup_constructor, which also | |
+ returns the qualified path? Searching the environment is costly | |
+ though. | |
+ Actually, using Env.lookup_constructor is a bad idea. Now labels and | |
+ constructors don;t have to be unique. The type checker goes to | |
+ a great length to disambiguate a constructor or a label. It records | |
+ the eventually determined type of the label/constructor in | |
+ label_description or constructor_description. | |
+ So, we should only use information from these descriptions. | |
+ | |
+ Alas, the predefined types (with no module qualification) are | |
+ not specially distinguished. So, we have to check the initial | |
+ environment. | |
+ *) | |
+let qualify_ctor : | |
+ Longident.t loc -> constructor_description -> Longident.t loc = | |
+ fun lid cdesc -> | |
+ let loc = lid.loc in | |
+ match (cdesc.cstr_tag, Ctype.repr cdesc.cstr_res) with | |
+ | (Cstr_extension (p,_),_) -> | |
+ if is_external p then Location.mkloc (path_to_lid p) loc else | |
+ trx_error @@ Location.errorf ~loc | |
+ "Exception (extension) %s cannot be used within brackets. Put into a separate file." | |
+ (Path.name p) | |
+ | (_,{desc = Tconstr((Path.Pident _ as ty_path), _, _)}) -> | |
+ begin | |
+ try ignore (Env.find_type ty_path initial_env); lid | |
+ with Not_found -> | |
+ trx_error @@ Location.errorf ~loc | |
+ "Unqualified constructor %s cannot be used within brackets. Put into a separate file." | |
+ cdesc.cstr_name | |
+ end | |
+ | (_,{desc = Tconstr(ty_path, _, _)}) -> | |
+ if is_external ty_path then | |
+ Location.mkloc (path_to_lid_but_last ty_path cdesc.cstr_name) loc | |
+ else | |
+ trx_error @@ Location.errorf ~loc | |
+ "Constructor %s cannot be used within brackets. Put into a separate file." | |
+ cdesc.cstr_name | |
+ | _ -> Printtyp.type_expr Format.err_formatter cdesc.cstr_res; | |
+ failwith ("qualify_ctor: cannot determine type_ctor from data_ctor "^ | |
+ cdesc.cstr_name) | |
+ | |
+(* Check to see that a record label belongs to a record defined | |
+ in a persistent module or in the initial environment. | |
+ This is a label version of qualify_ctor | |
+*) | |
+let qualify_label : Longident.t loc -> label_description -> Longident.t loc = | |
+ fun lid ldesc -> | |
+ let loc = lid.loc in | |
+ match Ctype.repr ldesc.lbl_res with | |
+ | {desc = Tconstr((Path.Pident _ as ty_path), _, _)} -> | |
+ begin | |
+ try ignore (Env.find_type ty_path initial_env); lid | |
+ with Not_found -> | |
+ trx_error @@ Location.errorf ~loc | |
+ "Unqualified label %s cannot be used within brackets. Put into a separate file." | |
+ ldesc.lbl_name | |
+ end | |
+ | {desc = Tconstr(ty_path, _, _)} -> | |
+ if is_external ty_path then | |
+ Location.mkloc | |
+ (path_to_lid_but_last ty_path ldesc.lbl_name) loc | |
+ else | |
+ trx_error @@ Location.errorf ~loc | |
+ "Label %s cannot be used within brackets. Put into a separate file." | |
+ ldesc.lbl_name | |
+ | _ -> Printtyp.type_expr Format.err_formatter ldesc.lbl_res; | |
+ failwith ("qualify_label: cannot determine type from label "^ | |
+ ldesc.lbl_name) | |
+ | |
+(* Test if we should refer to a CSP value by name rather than by | |
+ value | |
+*) | |
+(* Module identifiers for the modules that are expected to be | |
+ present at run-time -- that is, will be available for | |
+ dynamic linking of the run-time generated code. | |
+ | |
+TODO: check bytecomp/transclass.ml:const_path | |
+Perhaps that's a hint which unqualified identifiers will be persistent | |
+*) | |
+ | |
+let ident_can_be_quoted = is_external | |
+ | |
+(*}}}*) | |
+ | |
+ | |
+(*{{{ Templates for building Parsetree/Typedtree components *) | |
+ | |
+(* Local reference: trx.cmi is available but location.cmi is not | |
+ necessarily is in the current path. | |
+*) | |
+let loc_none = Location.none | |
+ | |
+let dummy_lid : string -> Longident.t loc = fun name -> | |
+ Location.mknoloc (Longident.Lident name) | |
+ | |
+(* Exported. Used as a template for constructing lid expressions *) | |
+let sample_lid = dummy_lid "*sample*" | |
+ | |
+(* Exported. Used as a template for constructing name expression *) | |
+let sample_name : string loc = mknoloc "*sample*" | |
+ | |
+(* Exported. Used as a template for constructing pattern lists expressions *) | |
+let sample_pat_list : Parsetree.pattern list = [] | |
+let sample_pats_names : Parsetree.pattern list * string loc list = ([],[]) | |
+ | |
+(*}}}*) | |
+ | |
+ | |
+(* ------------------------------------------------------------------------ *) | |
+(* Building Texp nodes *) | |
+(* initial_env is used for all look-ups. Unqualified identifiers | |
+ must be found there. For qualified identifiers, Env.lookup | |
+ functions look things up in the persistent structures, loading them | |
+ up as needed. | |
+*) | |
+ | |
+let mk_texp : ?env:Env.t -> ?attrs:Parsetree.attributes -> | |
+ ?loc:Location.t -> | |
+ Typedtree.expression_desc -> type_expr -> | |
+ Typedtree.expression = | |
+ fun ?(env=initial_env) ?(attrs=[]) ?(loc=Location.none) desc ty -> | |
+ { exp_desc = desc; exp_type = ty; | |
+ exp_loc = loc; exp_extra = []; | |
+ exp_attributes = attrs; | |
+ exp_env = env } | |
+ | |
+let texp_int : int -> Typedtree.expression = fun n -> | |
+ mk_texp ~env:Env.initial_safe_string (Texp_constant (Const_int n)) | |
+ (Ctype.instance_def Predef.type_int) | |
+ | |
+(* Make a bracket or an escape node | |
+ Here, the attr argument is a bracket/escape attribute | |
+*) | |
+let texp_zero = (* TExp node for constant 0 *) | |
+ texp_int 0 | |
+ | |
+let texp_braesc : | |
+ attribute -> Typedtree.expression -> Env.t -> type_expr -> | |
+ Typedtree.expression = | |
+ fun attr exp env ty -> | |
+ mk_texp ~env ~attrs:(attr :: exp.exp_attributes) | |
+ ~loc:exp.exp_loc (Texp_sequence (texp_zero, exp)) ty | |
+ | |
+ | |
+(* A CSP is in essence a constant. So, we represent CSP as a constant, | |
+ with an annotation that contains the name of the identifier | |
+ *) | |
+ | |
+let texp_csp_raw : | |
+ attribute -> Asttypes.constant -> Env.t -> type_expr -> Typedtree.expression = | |
+ fun attr cnt env ty -> | |
+ { | |
+ exp_desc = Texp_constant cnt; | |
+ exp_loc = Location.none; exp_extra = []; | |
+ exp_type = ty; | |
+ exp_attributes = [attr]; | |
+ exp_env = env } | |
+ | |
+ | |
+(* TODO: add memoization? *) | |
+ | |
+(* Compiling an identifier with a given (qualified) name *) | |
+let texp_ident : string -> expression = fun name -> | |
+ let lid = Longident.parse name in | |
+ let (p, vd) = try Env.lookup_value lid initial_env | |
+ with Not_found -> | |
+ Misc.fatal_error ("Trx.find_value: " ^ name) in | |
+ mk_texp (Texp_ident (p,mknoloc lid, vd)) | |
+ (Ctype.instance initial_env vd.val_type) | |
+ | |
+ | |
+(* Building an application *) | |
+let texp_apply : Typedtree.expression -> Typedtree.expression list -> | |
+ Typedtree.expression_desc = fun f args -> | |
+ Texp_apply(f, List.map (fun arg -> ("",Some arg, Required)) args) | |
+ | |
+(* Compiling a string constant *) | |
+(* The second argument of Const_string is the delimiter, | |
+ the decorator in the {decorator| ... |decorator} notation. | |
+*) | |
+let texp_string : string -> Typedtree.expression = fun str -> | |
+ mk_texp (Texp_constant (Const_string (str,None))) | |
+ (Ctype.instance_def Predef.type_string) | |
+ | |
+(* Compiling a boolean *) | |
+(* For prototype, see Typecore.option_none *) | |
+let texp_bool : bool -> Typedtree.expression = fun b -> | |
+ let lid = Longident.Lident (if b then "true" else "false") in | |
+ let cdec = Env.lookup_constructor lid initial_env in | |
+ mk_texp (Texp_construct(mknoloc lid, cdec, [])) | |
+ (Ctype.instance_def Predef.type_bool) | |
+ | |
+(* Given a value v, create a Typedtree node for an expression | |
+ that will evaluate to v. | |
+ This the the CSP used by the MetaOCaml itself. | |
+ Since this is an internal CSP, we don't put any attributes. | |
+*) | |
+let texp_csp : Obj.t -> Typedtree.expression = fun v -> | |
+ if Obj.is_int v then texp_int (Obj.obj v) | |
+ (* We treat strings and bytes identically *) | |
+ else if Obj.tag v = Obj.string_tag then texp_string (Obj.obj v) | |
+ else | |
+ let vstr = Marshal.to_string v [] in | |
+ let () = if false then debug_print ("texp_csp, marshall: size " ^ | |
+ string_of_int (String.length vstr)) in | |
+ mk_texp | |
+ (texp_apply (texp_ident "Marshal.from_string") | |
+ [texp_string vstr; texp_zero]) | |
+ (Btype.newgenvar ()) | |
+ | |
+ | |
+(* Compiling location data *) | |
+(* We could have made texp_loc an alias to texp_csp... We keep the | |
+ type information for location though, just to be fully correct. | |
+*) | |
+let texp_loc : Location.t -> Typedtree.expression = fun loc -> | |
+ let loc_exp = texp_ident "Trx.loc_none" in (* this fills in the type, etc.*) | |
+ if loc == Location.none then loc_exp else | |
+ {loc_exp with exp_desc = (texp_csp (Obj.repr loc)).exp_desc} | |
+ | |
+(* Compiling longident with location data *) | |
+let texp_lid : Longident.t loc -> Typedtree.expression = fun lid -> | |
+ let lid_exp = texp_ident "Trx.sample_lid" in (* this fills in the type, etc.*) | |
+ {lid_exp with exp_desc = (texp_csp (Obj.repr lid)).exp_desc} | |
+ | |
+(* Compiling a string with a location *) | |
+let texp_string_loc : string loc -> Typedtree.expression = fun name -> | |
+ let name_exp = texp_ident "Trx.sample_name" in | |
+ {name_exp with | |
+ exp_desc = (texp_csp (Obj.repr name)).exp_desc} | |
+ | |
+(* Compiling an option *) | |
+(* For prototype, see Typecore.option_none *) | |
+let texp_option : Typedtree.expression option -> Typedtree.expression = | |
+ function | |
+ | None -> | |
+ let lid = Longident.Lident "None" in | |
+ let cnone = Env.lookup_constructor lid initial_env in | |
+ mk_texp (Texp_construct(mknoloc lid, cnone, [])) | |
+ (Ctype.instance_def (Predef.type_option (Btype.newgenvar ()))) | |
+ | Some e -> | |
+ let lid = Longident.Lident "Some" in | |
+ let csome = Env.lookup_constructor lid initial_env in | |
+ mk_texp (Texp_construct(mknoloc lid, csome, [e])) | |
+ (Ctype.instance_def (Predef.type_option e.exp_type)) | |
+ ~env:e.exp_env | |
+ | |
+(* Compiling a tuple *) | |
+let texp_tuple : Typedtree.expression list -> Typedtree.expression = fun el -> | |
+ mk_texp (Texp_tuple el) | |
+ (Ctype.newty (Ttuple (List.map (fun e -> e.exp_type) el))) | |
+ | |
+(* Compiling an array *) | |
+(* We use this function for grouping trx_bracket-transformed expressions, | |
+ which have the same representation type (but may be different | |
+ code type). We ignore the differences in the code type, since | |
+ the representation type is the same. | |
+ | |
+ We don't use lists since they are harder to compile, and more | |
+ fragile. Texp_construct has more arguments, we have to locate | |
+ constructor information, etc. | |
+*) | |
+let texp_array : Typedtree.expression list -> Typedtree.expression = function | |
+ | [] -> | |
+ mk_texp (Texp_array []) | |
+ (Ctype.instance_def (Predef.type_array (Btype.newgenvar ()))) | |
+ | (h::_) as el -> | |
+ mk_texp (Texp_array el) | |
+ (Ctype.instance_def (Predef.type_array h.exp_type)) | |
+ | |
+(* Compiling patterns and the list of names bound by them *) | |
+let texp_pats_names : Parsetree.pattern list -> string loc list -> | |
+ Typedtree.expression = fun pats names -> | |
+ let pn_exp = texp_ident "Trx.sample_pats_names" in | |
+ {pn_exp with | |
+ exp_desc = (texp_csp (Obj.repr (pats,names))).exp_desc} | |
+ | |
+(* Utility function to build the case list *) | |
+let texp_case : ?guard:expression -> pattern -> expression -> case = | |
+ fun ?guard pat exp -> | |
+ {c_lhs=pat; c_guard=guard; c_rhs=exp} | |
+ | |
+(* ------------------------------------------------------------------------ *) | |
+(* Stack marks, a simple form of dynamic binding *) | |
+ | |
+(* In the earlier version, our stackmarks could be ordered. | |
+ Alas, it is hard to dynamically replace the implementation | |
+ below with the one adjusted for delimcc. The implementation below | |
+ does not work when partial continuations can be captured and reinstated. | |
+ Mainly, when delimited continuations are used, the order is | |
+ not stable. Delimited control operators can reshuffle the order | |
+ arbitrarily. Therefore, the fact that there is order among valid stackmarks | |
+ is not helpful anyway. | |
+ | |
+module type STACKMARK = sig | |
+ type t | |
+ val is_valid : t -> bool | |
+ (* compare is supposed to be called on stack marks that are | |
+ checked to be valid | |
+ *) | |
+ val compare : t -> t -> int | |
+ val with_stack_mark : (t -> 'w) -> 'w | |
+end | |
+ | |
+(* Simple implementation with shallow dynamic binding *) | |
+module StackMark : STACKMARK = struct | |
+ type t = int ref | |
+ | |
+ (* The global counter of the nesting depth of with_stack_mark *) | |
+ let stack_mark_cnt = ref 0 | |
+ | |
+ (* A stack mark is ref n where n is the depth of the corresponding | |
+ with_stack_mark form. | |
+ The stack mark is invalid if the counter is 0 | |
+ *) | |
+ let with_stack_mark body = | |
+ incr stack_mark_cnt; | |
+ let mark = ref !stack_mark_cnt in | |
+ let finalize () = | |
+ mark := 0; (* invalidate the mark *) | |
+ assert (!stack_mark_cnt > 0); | |
+ decr stack_mark_cnt | |
+ in | |
+ try | |
+ let r = body mark in finalize (); r | |
+ with e -> finalize (); raise e | |
+ | |
+ let is_valid mark = !mark > 0 | |
+ let compare m1 m2 = | |
+ assert (!m1 >0 && !m2 > 0); | |
+ compare !m1 !m2 | |
+end | |
+ | |
+*) | |
+ | |
+(* A robust and truly minimalistic implementation of stack-marks. | |
+ A stack-mark is created by 'with_stack_mark' function. Since | |
+ the only operation on a stackmark is to test if it is valid, | |
+ the stackmark is realized as a thunk unit -> bool. | |
+*) | |
+type stackmark = unit -> bool (* true if valid *) | |
+ | |
+(* The type of the with_stack_mark operation *) | |
+type stackmark_region_fn = | |
+ {stackmark_region_fn : 'w. (stackmark -> 'w) -> 'w} | |
+ | |
+(* The simple implementation of stackmark_region_fn, appropriate | |
+ when no delimited control is used. | |
+ The mark is a ref bool cell, containing true within | |
+ stackmark_region_fn's dynamic region. | |
+*) | |
+let with_stack_mark_simple : stackmark_region_fn = | |
+ {stackmark_region_fn = fun body -> | |
+ let mark = ref true in | |
+ try | |
+ let r = body (fun () -> !mark) in | |
+ mark := false; (* invalidate the mark *) | |
+ r | |
+ with e -> mark := false; raise e | |
+ } | |
+ | |
+let with_stack_mark : stackmark_region_fn ref = ref with_stack_mark_simple | |
+ | |
+(* Replace a with_stack_mark implementation, e.g., when delimcc is used *) | |
+let set_with_stack_mark : stackmark_region_fn -> unit = | |
+ fun smf -> with_stack_mark := smf | |
+ | |
+ | |
+(* ------------------------------------------------------------------------ *) | |
+(* Simple heap *) | |
+(* A mapping of keys to values. Priority is used for the sake of | |
+ efficient operations. Also, values with the same priority are | |
+ considered equivalent (belong to the same binding region) | |
+ and are collapsed, lazily. | |
+ | |
+ The invariant: for each non-leaf | |
+ node, the priority of the node is strictly greater than the priorities | |
+ of any of the child nodes. The order of priorities between | |
+ the children can be arbitrary. | |
+*) | |
+type prio = int | |
+type 'v heap = Nil | HNode of prio * stackmark * 'v * 'v heap * 'v heap | |
+let empty = Nil | |
+ | |
+let rec merge : 'v heap -> 'v heap -> 'v heap = fun h1 h2 -> | |
+ match (h1,h2) with | |
+ | (Nil,h) | (h,Nil)-> h | |
+ | (HNode (p1,k1,v1,l1,r1), HNode (p2,k2,v2,l2,r2)) -> | |
+ begin | |
+ match p1 - p2 with | |
+ | 0 -> HNode (p1,k1,v1, merge l1 l2, merge r1 r2) (* same keys *) | |
+ | n when n < 0 -> HNode (p2,k2,v2, merge h1 l2, r2) | |
+ | _ -> HNode (p1,k1,v1,l1,merge h2 r1) | |
+ end | |
+ | |
+(* Remove the node with a given priority *) | |
+let rec remove : prio -> 'v heap -> 'v heap = fun p -> function | |
+ | Nil -> Nil | |
+ | HNode (pn,k,v,h1,h2) as h -> | |
+ begin | |
+ match p - pn with | |
+ | 0 -> merge h1 h2 (* p cannot occur in h1 or h2 *) | |
+ | n when n > 0 -> h (* entire tree has the lower prio *) | |
+ | _ -> HNode (pn,k,v, remove p h1, remove p h2) | |
+ end | |
+ | |
+(* The representation of the possibly open code: AST plus the | |
+ set of free identifiers, annotated with the marks | |
+ of the corresponding with_binding_region forms | |
+*) | |
+type code_repr = Code of string loc heap * Parsetree.expression | |
+ | |
+(* The closed code is AST *) | |
+type closed_code_repr = Parsetree.expression | |
+ | |
+(* Check that the code is closed and return the closed code *) | |
+ | |
+(* The same as close_code but return the closedness check as a thunk | |
+ rather than performing it. | |
+ This is useful for debugging and for showing the code | |
+*) | |
+let close_code_delay_check : code_repr -> closed_code_repr * (unit -> unit) = | |
+ function | |
+ | Code (Nil,ast) -> (ast,fun () -> ()) | |
+ | Code (HNode (_,_,var,_,_),ast) -> | |
+ (ast, fun () -> | |
+ Format.fprintf Format.str_formatter | |
+ "The code built at %a is not closed: identifier %s bound at %a is free" | |
+ Location.print ast.pexp_loc var.txt Location.print var.loc; | |
+ failwith (Format.flush_str_formatter ())) | |
+ | |
+let close_code_repr : code_repr -> closed_code_repr = fun cde -> | |
+ let (ast, check) = close_code_delay_check cde in | |
+ check (); ast | |
+ | |
+let open_code : closed_code_repr -> code_repr = fun ast -> | |
+ Code (Nil,ast) | |
+ | |
+(* Compiling a closed code value: a structural constant of | |
+ type code_repr | |
+ This constant is transported via CSP (although we could have | |
+ built a Typedtree node for that purpose. | |
+ *) | |
+let texp_code : ?node_id:string -> | |
+ Location.t -> Parsetree.expression_desc -> Typedtree.expression_desc = | |
+ fun ?(node_id="") loc desc -> | |
+ let ast = Ast_helper.Exp.mk ~loc desc in | |
+ (texp_csp (Obj.repr (open_code ast))).exp_desc | |
+ | |
+ | |
+(* ------------------------------------------------------------------------ *) | |
+(* Bindings in the future stage *) | |
+(* Recall, all bindings at the future stage are introduced by | |
+ patterns, and hence are simple names, without any module qualifications. | |
+*) | |
+let gensym_count = ref 0 | |
+ | |
+(* Generate a fresh name with a given base name *) | |
+let gensym : string -> string = fun s -> | |
+ incr gensym_count; | |
+ s ^ "_" ^ string_of_int !gensym_count | |
+ | |
+let reset_gensym_counter () = gensym_count := 0 | |
+ | |
+(* Make a simple identifier unique *) | |
+let genident : string loc -> string loc = fun name -> | |
+ {name with txt = gensym name.txt} | |
+ | |
+(* This is a run-time error, rather than a translation-time error *) | |
+let scope_extrusion_error : | |
+ detected:Location.t -> occurred:Location.t -> string loc -> 'a = | |
+ fun ~detected ~occurred var -> | |
+ Format.fprintf Format.str_formatter | |
+ "Scope extrusion detected at %a for code built at %a for the identifier %s bound at %a" | |
+ Location.print detected Location.print occurred | |
+ var.txt Location.print var.loc; | |
+ failwith (Format.flush_str_formatter ()) | |
+ | |
+(* Check to make sure that free variables in the potentially open | |
+ code fragment are valid. | |
+ If it weren't for delimited control, the order of stack marks is | |
+ stable; therefore, if the maximal mark is valid then all | |
+ smaller marks are valid as well. | |
+ Delimited control spoils all that. | |
+ When we capture some of the inner-bindings | |
+ in a continuation and then reinstall that continuation at the | |
+ top level, the `latest' free variable is valid but earlier are | |
+ no longer valid: | |
+ | |
+ let r = ref ... in | |
+ .<fun x1 x2 -> .~(reset .<fun y1 y2 -> | |
+ .~(shift k (r := k; k .<0>.))>.)>. | |
+ .r .<2>. | |
+ Here, y1 and y2 are valid but x1 and x2 are not. | |
+*) | |
+let validate_vars : Location.t -> code_repr -> code_repr = | |
+ fun l -> function | |
+ | Code (Nil,_) as cde -> cde | |
+ | Code (h, ast) as cde -> begin | |
+ let rec check = function | |
+ | Nil -> () | |
+ | HNode (_,sm,var,h1,h2) -> | |
+ if sm () then (check h1; check h2) | |
+ else scope_extrusion_error ~detected:l ~occurred:ast.pexp_loc var | |
+ in check h; cde | |
+ end | |
+ | |
+let validate_vars_option : Location.t -> code_repr option -> | |
+ Parsetree.expression option * string loc heap = | |
+ fun l -> function | |
+ | None -> (None,Nil) | |
+ | Some e -> let Code (vars, e) = validate_vars l e in (Some e, vars) | |
+ | |
+let validate_vars_map : Location.t -> | |
+ (Location.t -> 'a -> 'b * string loc heap) -> 'a list -> | |
+ 'b list * string loc heap = fun loc f xs -> | |
+ map_accum (fun acc x -> | |
+ let (y,vars) = f loc x in | |
+ (y, merge vars acc)) | |
+ Nil xs | |
+ | |
+let validate_vars_list : Location.t -> code_repr list -> | |
+ Parsetree.expression list * string loc heap = fun l cs -> | |
+ validate_vars_map l | |
+ (fun l c -> let Code (vars,e) = validate_vars l c in (e,vars)) cs | |
+ | |
+(* Generate a fresh name off the given name, enter a new binding region | |
+ and evaluate a function passing it the generated name as code_repr. | |
+ Remove the generated name from the annotation on the resulting code_expr. | |
+ Return that result and the generated name. | |
+ This function embodies the translation of simple functions, for-loops, | |
+ simple let-expressions, etc. | |
+*) | |
+ (* Counter for assigning priorities to vars heap nodes. *) | |
+ (* Keep in mind the invariant that variables of the same priority | |
+ comes from the same binding location. So, we must keep the | |
+ priorities unique to binders. Giving binders monotonically | |
+ increasing priorities is helpful: the innermost binding | |
+ has the highest priority and it will be at the top of the heap, | |
+ the easiest to remove. | |
+ *) | |
+let prio_counter = ref 0 | |
+ | |
+let with_binding_region : | |
+ Location.t -> string loc -> (code_repr -> code_repr) -> | |
+ string loc * string loc heap * Parsetree.expression = fun l name f -> | |
+ let new_name = genident name in | |
+ let (vars,e) = | |
+ !with_stack_mark.stackmark_region_fn (fun mark -> | |
+ incr prio_counter; | |
+ let prio = !prio_counter in | |
+ let var_code = (* code that corresponds to the bound variable *) | |
+ Code (HNode (prio,mark,new_name,Nil,Nil), | |
+ Ast_helper.Exp.mk ~loc:name.loc (* the loc of the binder *) | |
+ (Pexp_ident (mkloc (Longident.Lident new_name.txt) new_name.loc))) in | |
+ let Code (vars,e) = validate_vars l (f var_code) in | |
+ (remove prio vars, e)) in | |
+ (new_name, vars, e) | |
+ | |
+(* The most general version with several bindings and several expressions | |
+ that use the bindings | |
+ *) | |
+let with_binding_region_gen : | |
+ Location.t -> string loc list -> | |
+ (Location.t -> 'a -> 'b * string loc heap) -> (code_repr array -> 'a array) -> | |
+ string loc list * string loc heap * 'b list | |
+ = fun l names tr f -> | |
+ let new_names = List.map genident names in | |
+ let (vars,es) = | |
+ !with_stack_mark.stackmark_region_fn (fun mark -> | |
+ incr prio_counter; | |
+ let prio = !prio_counter in | |
+ let vars_code = Array.of_list (List.map (fun new_name -> | |
+ (* code that corresponds to a bound variable *) | |
+ Code (HNode (prio,mark,new_name,Nil,Nil), | |
+ Ast_helper.Exp.mk ~loc:new_name.loc (* the loc of the binder *) | |
+ (Pexp_ident (mkloc (Longident.Lident new_name.txt) new_name.loc)))) | |
+ new_names) in | |
+ let cs = Array.to_list (f vars_code) in | |
+ let (es,vars) = map_accum (fun vars c -> | |
+ let (e,var) = tr l c in | |
+ (e,merge var vars)) Nil cs in | |
+ (remove prio vars, es)) in | |
+ (new_names, vars, es) | |
+ | |
+(* ------------------------------------------------------------------------ *) | |
+(* Building Parsetree nodes *) | |
+ | |
+(* Handle timestamp for builders of the type | |
+ Parsetree.expression -> Parsetree.expression | |
+*) | |
+let code_wrapper : | |
+ (Location.t -> Parsetree.expression -> Parsetree.expression) -> | |
+ (Location.t -> code_repr -> code_repr) = | |
+fun f l e -> | |
+ let Code (vars,e) = validate_vars l e in | |
+ Code (vars, f l e) | |
+ | |
+(* building a typical Parsetree node: Pexp_assert of expression*) | |
+let build_assert : Location.t -> code_repr -> code_repr = | |
+ code_wrapper | |
+ (fun loc e -> Ast_helper.Exp.assert_ ~loc e) | |
+ | |
+(* When we translate the typed-tree, we have to manually compile | |
+ the above code | |
+First, to see the AST for the phrase, invoke the top-level with the flag | |
+-dparsetree. Then | |
+ {pexp_loc = l; pexp_desc = Pexp_assert e} | |
+ | |
+gives the parsetree: | |
+let build_assert_ast : Location.t -> Parsetree.expression -> Parsetree.expression = | |
+{pexp_loc = l1; | |
+ pexp_desc = | |
+ Pexp_record | |
+ ([(Location.mknoloc (Longident.parse "Parsetree.pexp_loc"), | |
+ Pexp_ident "l"); | |
+ (Location.mknoloc (Longident.parse "Parsetree.pexp_desc"), | |
+ {pexp_loc = Location.none; | |
+ pexp_desc = Pexp_construct | |
+ ((Location.mknoloc (Longident.parse | |
+ "Parsetree.Pexp_assert")), | |
+ Some {pexp_loc = Location.none; | |
+ pexp_desc = Pexp_ident "e"}, | |
+ false)}) | |
+ ], | |
+ None)} | |
+type_expression | |
+ | |
+If building the parsetree on our own, beware! For example, labels in | |
+Texp_record must be sorted, in their declared order! | |
+*) | |
+ | |
+ | |
+(* Other similar builders *) | |
+let build_lazy : Location.t -> code_repr -> code_repr = | |
+ code_wrapper @@ | |
+ fun loc e -> Ast_helper.Exp.lazy_ ~loc e | |
+let build_bracket : Location.t -> code_repr -> code_repr = | |
+ code_wrapper @@ | |
+ fun _ e -> {e with pexp_attributes = | |
+ attr_bracket :: e.pexp_attributes } | |
+let build_escape : Location.t -> code_repr -> code_repr = | |
+ code_wrapper @@ | |
+ fun _ e -> {e with pexp_attributes = | |
+ attr_escape :: e.pexp_attributes} | |
+ | |
+let build_sequence : Location.t -> code_repr -> code_repr -> code_repr = | |
+ fun loc e1 e2 -> | |
+ let Code (vars1,e1) = validate_vars loc e1 in | |
+ let Code (vars2,e2) = validate_vars loc e2 in | |
+ Code (merge vars1 vars2, | |
+ Ast_helper.Exp.sequence ~loc e1 e2) | |
+let build_while : Location.t -> code_repr -> code_repr -> code_repr = | |
+ fun loc e1 e2 -> | |
+ let Code (vars1,e1) = validate_vars loc e1 in | |
+ let Code (vars2,e2) = validate_vars loc e2 in | |
+ Code (merge vars1 vars2, | |
+ Ast_helper.Exp.while_ ~loc e1 e2) | |
+ | |
+(* Build the application. The first element in the array is the | |
+ function. The others are arguments. *) | |
+let build_apply : Location.t -> (label * code_repr) array -> code_repr = | |
+ fun loc ea -> | |
+ assert (Array.length ea > 1); | |
+ match map_accum (fun vars (lbl,e) -> | |
+ let Code (var,e) = validate_vars loc e in | |
+ ((lbl,e),merge var vars)) | |
+ Nil (Array.to_list ea) with | |
+ | (("",eh)::elt,vars) -> | |
+ Code (vars, | |
+ Ast_helper.Exp.apply ~loc eh elt) | |
+ | _ -> assert false | |
+ | |
+ | |
+let build_tuple : Location.t -> code_repr array -> code_repr = | |
+ fun loc ea -> | |
+ let (els,vars) = validate_vars_list loc (Array.to_list ea) in | |
+ Code (vars, | |
+ Ast_helper.Exp.tuple ~loc els) | |
+ | |
+let build_array : Location.t -> code_repr array -> code_repr = | |
+ fun loc ea -> | |
+ let (els,vars) = validate_vars_list loc (Array.to_list ea) in | |
+ Code (vars, | |
+ Ast_helper.Exp.array ~loc els) | |
+ | |
+let build_ifthenelse : | |
+ Location.t -> code_repr -> code_repr -> code_repr option -> code_repr = | |
+ fun loc e1 e2 eo -> | |
+ let Code (vars1,e1) = validate_vars loc e1 in | |
+ let Code (vars2,e2) = validate_vars loc e2 in | |
+ let (eo,varso) = validate_vars_option loc eo in | |
+ Code (merge vars1 (merge vars2 varso), | |
+ Ast_helper.Exp.ifthenelse ~loc e1 e2 eo) | |
+ | |
+let build_construct : | |
+ Location.t -> Longident.t loc -> code_repr array -> code_repr = | |
+ fun loc lid args -> | |
+ let (args,vars) = validate_vars_list loc (Array.to_list args) in | |
+ Code (vars, | |
+ Ast_helper.Exp.construct ~loc lid | |
+ begin | |
+ match args with | |
+ | [] -> None | |
+ | [x] -> Some x | |
+ | xl -> Some (Ast_helper.Exp.tuple ~loc xl) | |
+ end) | |
+ | |
+let build_record : Location.t -> (Longident.t loc * code_repr) array -> | |
+ code_repr option -> code_repr = | |
+ fun loc lel eo -> | |
+ let (lel,vars) = map_accum (fun vars (lbl,e) -> | |
+ let Code (var,e) = validate_vars loc e in | |
+ ((lbl,e),merge var vars)) | |
+ Nil (Array.to_list lel) in | |
+ let (eo,varo) = validate_vars_option loc eo in | |
+ Code (merge vars varo, | |
+ Ast_helper.Exp.record ~loc lel eo) | |
+ | |
+let build_field : Location.t -> code_repr -> Longident.t loc -> code_repr = | |
+ fun loc e lid -> | |
+ let Code (vars,e) = validate_vars loc e in | |
+ Code (vars, | |
+ Ast_helper.Exp.field ~loc e lid) | |
+ | |
+let build_setfield : | |
+ Location.t -> code_repr -> Longident.t loc -> code_repr -> code_repr = | |
+ fun loc e1 lid e2 -> | |
+ let Code (vars1,e1) = validate_vars loc e1 in | |
+ let Code (vars2,e2) = validate_vars loc e2 in | |
+ Code (merge vars1 vars2, | |
+ Ast_helper.Exp.setfield ~loc e1 lid e2) | |
+ | |
+let build_variant : Location.t -> string -> code_repr option -> code_repr = | |
+ fun loc l eo -> | |
+ let (eo,vars) = validate_vars_option loc eo in | |
+ Code (vars, | |
+ Ast_helper.Exp.variant ~loc l eo) | |
+ | |
+let build_send : Location.t -> code_repr -> string -> code_repr = | |
+ fun loc e l -> | |
+ let Code (vars,e) = validate_vars loc e in | |
+ Code (vars, | |
+ Ast_helper.Exp.send ~loc e l) | |
+ | |
+let build_open : | |
+ Location.t -> Longident.t loc -> override_flag -> code_repr -> code_repr = | |
+ fun loc l ovf e -> | |
+ let Code (vars,e) = validate_vars loc e in | |
+ Code (vars, | |
+ Ast_helper.Exp.open_ ~loc ovf l e) | |
+ | |
+(* Build a function with a non-binding pattern, such as fun () -> ... *) | |
+let build_fun_nonbinding : | |
+ Location.t -> string -> Parsetree.pattern list -> | |
+ (code_repr option * code_repr) array -> code_repr = | |
+ fun loc label pats gbodies -> | |
+ let (egbodies,vars) = | |
+ validate_vars_map loc | |
+ (fun loc (eo,e) -> | |
+ let (eo,vo) = validate_vars_option loc eo in | |
+ let Code (vars,e) = validate_vars loc e in | |
+ ((eo,e),merge vo vars)) | |
+ (Array.to_list gbodies) in | |
+ Code (vars, | |
+ match (egbodies,pats) with | |
+ | ([(None,e)],[p]) -> | |
+ Ast_helper.Exp.fun_ ~loc label None p e | |
+ | _ when label="" -> | |
+ Ast_helper.Exp.function_ ~loc | |
+ (List.map2 (fun p (eo,e) -> {pc_lhs=p;pc_guard=eo;pc_rhs=e}) | |
+ pats egbodies) | |
+ | _ -> assert false) | |
+ | |
+(* Build a Parsetree for a future-stage identifier | |
+ It is always in scope of with_binding_region: | |
+ Bound variables are always in scope of their binders; | |
+ A well-typed code has no unbound variables. | |
+let build_ident : Location.t -> string loc -> code_repr = | |
+ fun loc l -> | |
+ not_supported loc "vars not supported" | |
+ Code (add_timestamp (Some l) | |
+ {pexp_loc = loc; | |
+ pexp_desc = Pexp_ident (mkloc (Longident.Lident l.txt) l.loc)} | |
+*) | |
+ | |
+(* Build a simple one-arg function, as described in the the title comments *) | |
+(* 'name' is the name of the variable from Ppat_var of the fun x -> ... | |
+ form. It is the real name with the location within the function pattern. | |
+ Use name.loc to identify the binder in the source code. | |
+*) | |
+let build_fun_simple : | |
+ Location.t -> string -> string loc -> (code_repr -> code_repr) -> code_repr = | |
+ fun loc label old_name fbody -> | |
+ let (name, vars, ebody) = with_binding_region loc old_name fbody in | |
+ let pat = Ast_helper.Pat.var ~loc:name.loc name in | |
+ Code (vars, | |
+ Ast_helper.Exp.fun_ ~loc label None pat ebody) | |
+ | |
+let build_for : | |
+ Location.t -> string loc -> code_repr -> code_repr -> | |
+ bool -> (code_repr -> code_repr) -> code_repr = | |
+ fun loc old_name elo ehi dir fbody -> | |
+ let (name, varsb, ebody) = with_binding_region loc old_name fbody in | |
+ let Code (varsl,elo) = validate_vars loc elo in | |
+ let Code (varsh,ehi) = validate_vars loc ehi in | |
+ Code (merge varsb (merge varsl varsh), | |
+ Ast_helper.Exp.for_ ~loc | |
+ (Ast_helper.Pat.var ~loc:name.loc name) elo ehi | |
+ (if dir then Upto else Downto) ebody) | |
+ | |
+ | |
+let build_let_simple_nonrec : | |
+ Location.t -> string loc -> code_repr -> (code_repr -> code_repr) -> | |
+ code_repr = fun loc old_name e fbody -> | |
+ let (name, varsb, ebody) = with_binding_region loc old_name fbody in | |
+ let pat = Ast_helper.Pat.var ~loc:name.loc name in | |
+ let Code (varse,e) = validate_vars loc e in | |
+ Code (merge varsb varse, | |
+ Ast_helper.Exp.let_ ~loc Nonrecursive | |
+ [Ast_helper.Vb.mk ~loc pat e] ebody) | |
+ | |
+(* | |
+let build_letrec : | |
+ Location.t -> string loc array -> | |
+ (code_repr array -> code_repr array) -> code_repr = | |
+ fun l old_names fbodies -> | |
+ let (names,vars,ebodies) = | |
+ with_binding_region_gen l (Array.to_list old_names) fbodies in | |
+ let (ebody,es) = | |
+ match ebodies with body::es -> (body,es) | _ -> assert false in | |
+ let pel = List.map2 (fun name e -> | |
+ ({ppat_loc = name.loc; ppat_desc = Ppat_var name},e)) names es in | |
+ Code (vars, | |
+ {pexp_loc = l; | |
+ pexp_desc = Pexp_let (Recursive, pel, ebody)}) | |
+*) | |
+ | |
+(*{{{ CSP *) | |
+ | |
+(* ------------------------------------------------------------------------ *) | |
+(* Dealing with CSP *) | |
+ | |
+exception CannotLift | |
+ | |
+(* Analyze the type of the expression and figure out if we can lift it. | |
+ Raise CannotLift if cannot (e.g., the type is polymorphic), or it is too | |
+ much to bother. | |
+ TODO: lists, arrays, option types of liftable types are themselves | |
+ liftable. We can lift many more types. For arrays, check their length. | |
+ If the array is short, it should be lifted. For long arrays, | |
+ building a CSP is better (although it make take a bit longer since | |
+ we will have to invoke dyn_quote at run-time). | |
+ | |
+ TODO: currently we generate calls to run-time functions like | |
+ lift_constant_int to do the Parsetree generation. In the future | |
+ we should `inline' those functions -- that is, obtain the Typedtree | |
+ for them and use the tree for building Texp_apply. | |
+*) | |
+let lift_as_literal : | |
+ Typedtree.expression -> Longident.t loc -> Typedtree.expression_desc = | |
+ fun exp li -> | |
+ let exp_ty = | |
+ Ctype.expand_head exp.exp_env (Ctype.correct_levels exp.exp_type) in | |
+ match Ctype.repr exp_ty with | |
+ | {desc = Tconstr(p, _, _)} when Path.same p Predef.path_int -> | |
+ texp_apply (texp_ident "Trx.lift_constant_int") [exp] | |
+ | {desc = Tconstr(p, _, _)} when Path.same p Predef.path_char -> | |
+ texp_apply (texp_ident "Trx.lift_constant_char") [exp] | |
+ | {desc = Tconstr(p, _, _)} when Path.same p Predef.path_bool -> | |
+ texp_apply (texp_ident "Trx.lift_constant_bool") [exp] | |
+ (* double and string are handled by dyn_quote *) | |
+ (* which hence handles polymorphic functions instantiated | |
+ to double and string. | |
+ *) | |
+ (* Deal with code type *) | |
+ | _ -> raise CannotLift | |
+ | |
+(* TODO: similarly handle Const_nativeint, Const_int32, Const_int64 *) | |
+let lift_constant_int : int -> code_repr = fun x -> | |
+ open_code @@ Ast_helper.Exp.constant (Const_int x) | |
+ | |
+let lift_constant_char : char -> code_repr = fun x -> | |
+ open_code @@ Ast_helper.Exp.constant (Const_char x) | |
+ | |
+let lift_constant_bool : bool -> code_repr = fun x -> | |
+ let b = if x then "true" else "false" in | |
+ open_code @@ Ast_helper.Exp.construct | |
+ (Location.mknoloc (Longident.Lident b)) None | |
+ | |
+ | |
+(* Lift the run-time value v into a Parsetree for the code that, when | |
+ run, will produce v. | |
+ We do not have the type information for v, but we can examine | |
+ its run-time representation, to decide if we lift it is a source | |
+ literal or as a CSP. | |
+ We attach the CSP attribute to an expression, for the sake of better | |
+ printing (also to simplify translation when CSP occurs within nested | |
+ brackets). | |
+ | |
+ TODO: also check for double_array_tag | |
+ and create a (structured) constant for a double array | |
+*) | |
+let obj_magic_exp = | |
+ Ast_helper.Exp.ident (Location.mknoloc @@ | |
+ (Longident.Ldot (Longident.Lident "Obj","magic"))) | |
+ | |
+(* Check to see if a value is easy to serialize *) | |
+let easy_to_serialize : Obj.t -> bool = | |
+ let depth_bound = 5 in | |
+ let rec loop n v = | |
+ Obj.is_int v || | |
+ let tag = Obj.tag v in | |
+ tag = Obj.string_tag || | |
+ tag = Obj.double_tag || | |
+ tag = Obj.double_array_tag || | |
+ if n <= 0 || | |
+ tag = Obj.closure_tag || | |
+ tag >= Obj.no_scan_tag | |
+ then false | |
+ else let rec inner i = | |
+ if i < 0 then true else | |
+ loop (n-1) (Obj.field v i) && inner (i-1) | |
+ in inner (Obj.size v - 1) | |
+ in loop depth_bound | |
+ | |
+ | |
+let dyn_quote : Obj.t -> Longident.t loc -> code_repr = | |
+ fun v li -> | |
+ let csp_attr = attr_csp li in | |
+ open_code @@ | |
+ match Obj.is_int v with | |
+ | true -> (* Looks like an integer: coerce from it using Obj.magic *) | |
+ Ast_helper.Exp.apply ~attrs:[csp_attr] ~loc:li.loc | |
+ obj_magic_exp [("",Ast_helper.Exp.constant (Const_int (Obj.obj v)))] | |
+ | false when Obj.tag v = Obj.double_tag -> | |
+ Ast_helper.Exp.constant (Const_float (string_of_float (Obj.obj v))) | |
+ | false when Obj.tag v = Obj.string_tag -> | |
+ Ast_helper.Exp.constant (Const_string (Obj.obj v,None)) | |
+ | _ -> (* general case *) | |
+ let () = | |
+ if not @@ easy_to_serialize v then | |
+ debug_print ~loc:li.loc | |
+ "The CSP value is a closure or too deep to serialize" in | |
+ Ast_helper.Exp.apply ~attrs:[csp_attr] ~loc:li.loc | |
+ obj_magic_exp [("",Ast_helper.Exp.constant | |
+ (Const_string (Obj.obj v,None)))] | |
+ | |
+ | |
+(* Build the Typedtree that lifts the variable with the given path and type. | |
+ Since this code receives the type of the variable, we use the | |
+ type to generate the lifting code for that particular type. | |
+ For example, we build the code to convert a float | |
+ 0.1 to the Parsetree node Pexp_constant(Const_float "0.1")). | |
+ If we cannot or would not do the type-dependent lifting and we cannot | |
+ refer to the variable by name (e.g., because it is local), | |
+ we generate the call to the dynamic quoter, dyn_quote. | |
+ The latter will receive the actual value to quote and will generate, | |
+ at run-time, a Parsetree constant or CSP, based on that value. | |
+ *) | |
+let trx_csp : | |
+ Typedtree.expression -> Path.t -> Longident.t loc -> | |
+ Typedtree.expression_desc = fun exp p li -> | |
+ (* First we try lifting as a constant *) | |
+ try lift_as_literal exp li | |
+ with CannotLift -> | |
+ (* Then check if we can pass by reference *) | |
+ if ident_can_be_quoted p then | |
+ texp_code ~node_id:"*id*" exp.exp_loc | |
+ (Pexp_ident (Location.mkloc (path_to_lid p) li.loc)) | |
+ else | |
+ (* Otherwise, do the lifting at run-time *) | |
+ texp_apply (texp_ident "Trx.dyn_quote") [exp; texp_lid li] | |
+ | |
+(*{{{ Historical: hints on native mode CSP *) | |
+ | |
+(* Native mode is moved out to the `userland' | |
+ | |
+let remove_texp_cspval exp = | |
+ if !native_mode = false then exp else | |
+ failwith "native mode CSP are not impemented yet" | |
+ | |
+old code | |
+ match exp.exp_desc with | |
+ | Texp_cspval (v,l) -> | |
+ let i = add_csp_value (v,l) in | |
+ let exp' = {exp with exp_desc = Texp_constant (Const_int i)} in | |
+ let desc = if !initial_native_compilation | |
+ then (Texp_apply (trx_array_get exp, [(Some !local_csp_arr_texp, Required);(Some exp', Required)])) | |
+ else (Texp_apply (trx_get_csp_value exp, [(Some exp', Required)])) in | |
+ {exp with exp_desc = desc} | |
+ | _ -> assert false | |
+*) | |
+ | |
+(*}}}*) | |
+ | |
+(*}}}*) | |
+ | |
+ | |
+(*{{{ Translating patterns and expressions using patterns *) | |
+ | |
+ | |
+(* Analyze and translate a pattern: | |
+ Typedtree.pattern -> Parsetree.pattern | |
+ The function is somewhat similar to tools/untypeast.ml:untype_pattern | |
+ | |
+ However, we also determine and return the list of bound variables. | |
+ The list is in the reverse of the order of variables occurring in the pattern. | |
+ Finally, we check that labels and constructors may be quoted. | |
+ | |
+ The algorithm of determining the names of bound variables is based | |
+ on Typedtree.pat_bound_idents. There is one subtle issue. | |
+ Normally all variables within a pattern are unique (patterns are | |
+ always linear). Identically named variables within a list of patterns, like | |
+ match ... with | |
+ | [x] -> | |
+ | [x;y] -> | |
+ are _distinct_ variables. They have different Ident.t values, even though | |
+ their names may be the same. However, components of an OR pattern | |
+ bind exactly the same identifiers. Don't count them twice! | |
+*) | |
+ | |
+ | |
+(* The first argument is a list of identifiers. Found identifiers are | |
+ prepended to that list. The order of identifiers is important! | |
+ If you change the traversal order, be sure to modify pattern_subst below! | |
+*) | |
+let rec trx_pattern : | |
+ (Ident.t * string loc) list -> Typedtree.pattern -> | |
+ Parsetree.pattern * (Ident.t * string loc) list = fun acc pat -> | |
+ let (pd,acc) = match pat with | |
+ | { pat_extra=[Tpat_unpack, _, _attrs]; pat_desc = Tpat_var (_,name); _ } -> | |
+ (Ppat_unpack name,acc) (* name must have been uppercase *) | |
+ | { pat_extra=[Tpat_type (_path, lid), _, _attrs]; _ } -> (Ppat_type lid,acc) | |
+ | { pat_extra= (Tpat_constraint ct, _, _attrs) :: rem; _ } -> | |
+ not_supported pat.pat_loc | |
+ "patterns with constraints, and other pat_extra"; | |
+ (* | |
+ Ppat_constraint (untype_pattern { pat with pat_extra=rem }, | |
+ untype_core_type ct) | |
+ *) | |
+ | _ -> match pat.pat_desc with | |
+ | Tpat_any -> (Ppat_any, acc) | |
+ | Tpat_var (id, name) when | |
+ (match (Ident.name id).[0] with 'A'..'Z' -> true | _ -> false) -> | |
+ (Ppat_unpack name,acc) (* We don't handle modules though...*) | |
+ | Tpat_var (id, name) -> | |
+ (Ppat_var name, (id,name)::acc) | |
+ | Tpat_alias (p, id, name) -> | |
+ let (p,acc) = trx_pattern acc p in | |
+ (Ppat_alias (p, name),(id,name)::acc) | |
+ | Tpat_constant cst -> (Ppat_constant cst, acc) | |
+ | Tpat_tuple lst -> | |
+ let (pl,acc) = map_accum trx_pattern acc lst | |
+ in (Ppat_tuple pl, acc) | |
+ | Tpat_construct (li, cdesc, args) -> | |
+ let lid = qualify_ctor li cdesc in | |
+ let (args,acc) = map_accum trx_pattern acc args in | |
+ (Ppat_construct (lid, | |
+ (match args with | |
+ | [] -> None | |
+ | [x] -> Some x | |
+ | _ -> Some (Ast_helper.Pat.tuple ~loc:pat.pat_loc args))), | |
+ acc) | |
+ | Tpat_variant (label, None, _) -> (Ppat_variant (label,None),acc) | |
+ | Tpat_variant (label, Some p, _) -> | |
+ let (p,acc) = trx_pattern acc p | |
+ in (Ppat_variant (label,Some p),acc) | |
+ | Tpat_record (lst, closed) -> | |
+ let dolab acc (li,ldesc,pat) = | |
+ let lid = qualify_label li ldesc in | |
+ let (pat,acc) = trx_pattern acc pat in | |
+ ((lid,pat),acc) | |
+ in | |
+ let (lpl,acc) = map_accum dolab acc lst in | |
+ (Ppat_record (lpl,closed),acc) | |
+ | Tpat_array lst -> | |
+ let (pl,acc) = map_accum trx_pattern acc lst | |
+ in (Ppat_array pl, acc) | |
+ | Tpat_or (p1, p2, _) -> | |
+ (* Invariant : both arguments bind the same variables *) | |
+ let (p1,acc) = trx_pattern acc p1 in | |
+ let (p2,_) = trx_pattern acc p2 in (* ignore vars in p2 *) | |
+ (Ppat_or (p1,p2),acc) | |
+ | Tpat_lazy p -> | |
+ let (p,acc) = trx_pattern acc p in (Ppat_lazy p,acc) | |
+ in | |
+ (Ast_helper.Pat.mk ~loc:pat.pat_loc ~attrs:pat.pat_attributes pd, acc) | |
+ | |
+ | |
+(* Process all patterns in the case list *) | |
+(* Patterns are processed left-to-right. The result is the processed | |
+ pattern list plus the list of names of the bound variables. | |
+ The variables are listed in the order they occur in the pattern. | |
+ Thus the following should hold: | |
+ let (pats,names,_) = trx_cl cl in | |
+ let (pats',acc) = pattern_subst_list names pats in | |
+ assert (pats = pats'); | |
+ assert (acc = []) | |
+ The final result of trx_cl is the pattern binding the names. | |
+ We build an array pattern rather than a more appropriate tuple. | |
+ Using array forces a single type to all arguments. Although | |
+ it is phantom anyway, it is still a bummer. But with the tuple | |
+ we can't generically write build_fun. | |
+ The second argument, typ_expr, should normally be a code type. | |
+ | |
+ This function is used when translating a future-stage function as the | |
+ present-stage whose argument is an array of variables. | |
+ See trx_bracket for functions, let, match and try | |
+*) | |
+let trx_cl : case list -> type_expr -> | |
+ Parsetree.pattern list * string loc list * Typedtree.pattern | |
+ = fun cl typ -> | |
+ let (pats, lst) = | |
+ map_accum (fun acc {c_lhs} -> trx_pattern acc c_lhs) [] cl in | |
+ let idnames = List.rev lst in | |
+ let (loc,env) = | |
+ match cl with {c_lhs=p}::_ -> (p.pat_loc, p.pat_env) |_ -> assert false in | |
+ (* Pattern representing one binding variable *) | |
+ let var_pat (id,name) = | |
+ {pat_loc = loc; pat_extra = []; pat_env = env; | |
+ pat_desc = Tpat_var (id,name); | |
+ pat_attributes=[]; | |
+ pat_type = typ} in | |
+ (pats, List.map snd idnames, | |
+ {pat_loc = loc; pat_extra = []; pat_env = env; | |
+ pat_attributes=[]; | |
+ pat_desc = Tpat_array (List.map var_pat idnames); | |
+ pat_type = Ctype.instance_def (Predef.type_array typ)}) | |
+ | |
+(* Substitute the names of bound variables in the pattern. | |
+ The new names are given in the string loc list. We | |
+ take advantage of the fact that patterns are linear and | |
+ the list of new names is ordered, in the order the bound | |
+ variables occur in the pattern. Therefore, we substitute based | |
+ on position. | |
+ OR-patterns bring complexity however: both branches of an OR | |
+ pattern bind exactly the same variables (but the order of | |
+ variable occurrence within branches may be different). | |
+ So for OR patterns we substitute by name, taking advantage | |
+ of the fact the new names differ from the old ones in _nnn | |
+ suffix. OR patterns are uncommon, so the complication of their processing | |
+ is not that bad. | |
+ | |
+ This function is closely related to trx_pattern; It relies on the | |
+ same pattern traversal order as trx_pattern. | |
+ *) | |
+ | |
+ (* two strings are the same up to (and including) n *) | |
+let rec same_upto s1 s2 n = | |
+ n < 0 || (s1.[n] = s2.[n] && same_upto s1 s2 (n-1)) | |
+ | |
+let rec pattern_subst : ?by_name:bool -> | |
+ string loc list -> Parsetree.pattern -> | |
+ Parsetree.pattern * string loc list = fun ?(by_name=false) acc pat -> | |
+ if acc = [] then (pat,acc) else (* no more variables to subst *) | |
+ let subst old_name acc = | |
+ if by_name then begin | |
+ let new_name = | |
+ try List.find (fun n -> | |
+ same_upto old_name.txt n.txt (String.rindex n.txt '_' - 1)) acc | |
+ with _ -> | |
+ begin | |
+ Format.fprintf Format.str_formatter "old_name %s %a\n" | |
+ old_name.txt Location.print old_name.loc; | |
+ List.iter (fun n -> Format.fprintf Format.str_formatter | |
+ "new name %s %a\n" n.txt Location.print n.loc) acc; | |
+ failwith (Format.flush_str_formatter ()) | |
+ end | |
+ in | |
+ (new_name, acc) (* don't bother removing from acc*) | |
+ end | |
+ else match acc with | |
+ | h::t -> (h,t) | |
+ | _ -> assert false | |
+ in | |
+ let (desc,acc) = match pat.ppat_desc with | |
+ | Ppat_any as x -> (x,acc) | |
+ | Ppat_var old_name -> | |
+ let (new_name,acc) = subst old_name acc in (Ppat_var new_name,acc) | |
+ | Ppat_alias (p,old_name) -> | |
+ let (p,acc) = pattern_subst ~by_name acc p in | |
+ let (new_name,acc) = subst old_name acc in | |
+ (Ppat_alias (p,new_name),acc) | |
+ | Ppat_constant _ as x -> (x,acc) | |
+ | Ppat_tuple pl -> | |
+ let (pl,acc) = map_accum (pattern_subst ~by_name) acc pl in | |
+ (Ppat_tuple pl,acc) | |
+ | Ppat_construct (_,None) as x -> (x,acc) | |
+ | Ppat_construct (lid,Some p) -> | |
+ let (p,acc) = pattern_subst ~by_name acc p in | |
+ (Ppat_construct (lid,Some p),acc) | |
+ | Ppat_variant (_,None) as x -> (x,acc) | |
+ | Ppat_variant (l,Some p) -> | |
+ let (p,acc) = pattern_subst ~by_name acc p in | |
+ (Ppat_variant (l,Some p),acc) | |
+ | Ppat_record (pl,cf) -> | |
+ let (pl,acc) = map_accum (fun acc (l,p) -> | |
+ let (p,acc) = pattern_subst ~by_name acc p in ((l,p),acc)) acc pl in | |
+ (Ppat_record (pl,cf),acc) | |
+ | Ppat_array pl -> | |
+ let (pl,acc) = map_accum (pattern_subst ~by_name) acc pl in | |
+ (Ppat_array pl,acc) | |
+ | Ppat_or (p1,p2) -> | |
+ let (p1,acc') = pattern_subst ~by_name acc p1 in | |
+ let (p2,_) = pattern_subst ~by_name:true acc p2 in | |
+ (Ppat_or (p1,p2), acc') | |
+ | Ppat_constraint (p,cty) -> | |
+ let (p,acc) = pattern_subst ~by_name acc p in | |
+ (Ppat_constraint (p,cty), acc) | |
+ | Ppat_type _ as x -> (x,acc) | |
+ | Ppat_lazy p -> | |
+ let (p,acc) = pattern_subst ~by_name acc p in | |
+ (Ppat_lazy p, acc) | |
+ | Ppat_unpack _ as x -> (x,acc) | |
+ | _ -> assert false (* we do not create other forms of Ppat *) | |
+ in | |
+ ({pat with ppat_desc = desc}, acc) | |
+ | |
+ | |
+let pattern_subst_list : | |
+ string loc list -> Parsetree.pattern list -> | |
+ Parsetree.pattern list * string loc list = fun acc pl -> | |
+ map_accum (pattern_subst ~by_name:false) acc pl | |
+ | |
+ | |
+ | |
+(* Build the general fun Parsetree *) | |
+ | |
+(* Build the fresh variable name for cases and build the Parsetree | |
+ case list | |
+ We implicitly assume that all variables bound by patterns in any clause | |
+ scopes over all clauses. That seems like a wild assumption: for example, | |
+ in | |
+ function | x -> e1 | y -> e2 | |
+ the variable x should scope only over e1 rather than also over e2. | |
+ However, this wild scoping is no problem: recall that we process | |
+ the Typedtree, and the type checker already determined the scoping. | |
+ In the type-checked example | |
+ let x = 1 in | |
+ function | x -> e1 | y -> x + 2 | |
+ the variables are represented not just by their names but by their Path, | |
+ which contains the unique timestamp. Therefore, we are actually dealing with | |
+ let x/1 = 1 in | |
+ function | x/2 -> e1 | y/3 -> x/1 + 2 | |
+ Therefore, if we make x/2 also scope over the second clause, that is | |
+ harmless. | |
+ Because of such scoping rules, prepare_cases is also useful | |
+ for processing letrec. | |
+*) | |
+let prepare_cases : Location.t -> | |
+ string loc heap -> (* extra free variables used in kontinuation *) | |
+ (* The following argument is a pair: a pattern list for the clauses | |
+ of the function, and the list of names of bound variables, in order. | |
+ *) | |
+ (Parsetree.pattern list * string loc list) -> | |
+ (* The following function returns the list of pairs of guards and bodies, | |
+ for each clause of the function | |
+ *) | |
+ (code_repr array -> (code_repr option * code_repr) array) -> | |
+ (* The continuation *) | |
+ (Parsetree.case list -> Parsetree.expression) -> code_repr = | |
+ fun loc evars (pats,old_names) fgbodies k -> | |
+ let tr loc (eo,e) = | |
+ let (eo,vo) = validate_vars_option loc eo in | |
+ let Code (vars,e) = validate_vars loc e in | |
+ ((eo,e),merge vo vars) in | |
+ let (names,vars,egbodies) = | |
+ with_binding_region_gen loc old_names tr fgbodies in | |
+ let pats = | |
+ if names = [] then pats else | |
+ let (pats,acc) = pattern_subst_list names pats in | |
+ assert (acc = []); pats | |
+ in | |
+ Code(merge evars vars, | |
+ k @@ List.map2 (fun p (eo,e) -> {pc_lhs=p;pc_guard=eo;pc_rhs=e}) | |
+ pats egbodies) | |
+ | |
+let build_fun : | |
+ Location.t -> string -> | |
+ (Parsetree.pattern list * string loc list) -> | |
+ (code_repr array -> (code_repr option * code_repr) array) -> code_repr = | |
+ fun loc label pon fgbodies -> | |
+ prepare_cases loc Nil pon fgbodies @@ function | |
+ | [{pc_lhs=p; pc_guard=None; pc_rhs=e}] -> | |
+ Ast_helper.Exp.fun_ ~loc label None p e | |
+ | cases when label="" -> | |
+ Ast_helper.Exp.function_ ~loc cases | |
+ | _ -> assert false | |
+ | |
+ | |
+(* Build the general let-Parsetree (like the fun-Parsetree) *) | |
+let build_let : | |
+ Location.t -> bool -> | |
+ (Parsetree.pattern list * string loc list) -> | |
+ (code_repr array -> (code_repr option * code_repr) array) -> code_repr = | |
+ fun loc recf pon fgbodies -> | |
+ prepare_cases loc Nil pon fgbodies @@ function | |
+ | [] | [_] -> assert false | |
+ (* The first case is the pseudo-case for the let body *) | |
+ | {pc_guard=None; pc_rhs=ebody} :: cases -> | |
+ Ast_helper.Exp.let_ ~loc (if recf then Recursive else Nonrecursive) | |
+ (List.map (function | |
+ | {pc_lhs;pc_guard=None;pc_rhs} -> | |
+ Ast_helper.Vb.mk ~loc:pc_lhs.ppat_loc pc_lhs pc_rhs | |
+ | _ -> assert false) | |
+ cases) | |
+ ebody | |
+ | _ -> assert false | |
+ | |
+(* build match and try: both are very similar and similar to build_fun *) | |
+let build_match : | |
+ Location.t -> (Parsetree.pattern list * string loc list) -> code_repr -> | |
+ int -> | |
+ (code_repr array -> (code_repr option * code_repr) array) -> code_repr = | |
+ fun loc pon ec nregular fgbodies -> | |
+ let Code (evars,exp) = validate_vars loc ec in | |
+ let split : int -> 'a list -> 'a list * 'a list = fun n lst -> | |
+ let rec loop n acc lst = match (n,lst) with | |
+ | (0,lst) -> (List.rev acc,lst) | |
+ | (n,h::t) -> loop (n-1) (h::acc) t | |
+ | _ -> assert false | |
+ in loop n [] lst | |
+ in | |
+ prepare_cases loc evars pon fgbodies @@ fun cases -> | |
+ Ast_helper.Exp.match_ ~loc exp | |
+ (let (rc,ec) = split nregular cases in | |
+ rc @ List.map | |
+ (fun c -> | |
+ let pat = {c.pc_lhs with ppat_desc = Ppat_exception c.pc_lhs} | |
+ in {c with pc_lhs = pat}) ec) | |
+ | |
+ | |
+(* Essentially the same as build_match. | |
+ TODO: implement the same check on the timestamp of the expression to try | |
+*) | |
+let build_try : | |
+ Location.t -> (Parsetree.pattern list * string loc list) -> code_repr -> | |
+ (code_repr array -> (code_repr option * code_repr) array) -> code_repr = | |
+ fun loc pon ec fgbodies -> | |
+ let Code (evars,exp) = validate_vars loc ec in | |
+ prepare_cases loc evars pon fgbodies @@ fun cases -> | |
+ Ast_helper.Exp.try_ ~loc exp cases | |
+ | |
+(*}}}*) | |
+ | |
+ | |
+(* ------------------------------------------------------------------------ *) | |
+(* The main function to translate away brackets. It receives | |
+ an expression at the level n > 0. | |
+ | |
+ Since bracket-translation is somewhat similar to un-typechecking, | |
+ see tools/untypeast.ml for hints on mapping Typedtree.expression | |
+ to Parsetree.expression. | |
+ | |
+TODO: an optimization idea. Consider <assert e> as a typical expression. | |
+We translate it to the invocation of build_assert that will construct | |
+the Parsetree node at run-time. However, if 'e' is simple (e.g., a constant) | |
+then we can construct the Parsetree node at compile time and pass it | |
+as a CSP. There are no longer any functions calls to make at run-time. | |
+So, we can modify the translation of <assert e> below to detect | |
+if the translation of e produced Texp_cspval. We extract the CSP value, | |
+invoke build_assert (at compile time, when trx.ml is run) to build | |
+the Pexp_assert node, and wrap it as a CSP. | |
+ | |
+Essentially the result of trx_bracket should be like | |
+ Transl_bracket of Parsetree.expression option * Typedtree.expression | |
+The first part of the result is the code built-in at compile time. | |
+This part is None of the expression to translate contains an escape | |
+or a true CSP (global id is OK). Sometimes we need both parts: consider | |
+ <fun x -> x + ~(...)> | |
+When we translate x we don't know if we can take a shortcut and | |
+build the function code at translation time. So, we have to account | |
+for both possibilities. If we can build the function at compile time, | |
+we don't even need to rename the bound variable! | |
+ | |
+*) | |
+ | |
+(* Given a type [ty], return [ty code code ... code] (n times code). | |
+ When we push the bracket in, expressions that had type ty before | |
+ will have the type ty code. | |
+ Here, ty code is an abstract type whose concrete representation | |
+ is code_repr. | |
+ Generally speaking we don't have to adjust the types since the | |
+ type checking is finished. However, code generator may look | |
+ at types; it's better if we don't lie. Thus, as trx_bracket | |
+ translates the expression, it should also adjust the types. | |
+*) | |
+ | |
+let rec wrap_ty_in_code : int -> type_expr -> type_expr = fun n ty -> | |
+ if n=0 then ty else | |
+ (* let clsfier = Btype.newgenvar () in *) | |
+ wrap_ty_in_code (n-1) (Predef.type_code ty) | |
+ | |
+let map_option : ('a -> 'b) -> 'a option -> 'b option = fun f -> function | |
+ | None -> None | |
+ | Some x -> Some (f x) | |
+ | |
+ | |
+let rec trx_bracket : int -> expression -> expression = fun n exp -> | |
+ (* | |
+ let _ = debug_print "Texp_bracket" in | |
+ let rec prattr = function | |
+ | [] -> () | |
+ | ({txt=name},_) :: t -> | |
+ debug_print ("attr: " ^ name); prattr t | |
+ in prattr exp.exp_attributes; | |
+ let _ = Location.print Format.err_formatter (exp.exp_loc) in | |
+ *) | |
+ (* Handle staging constructs, which are distinguished solely by | |
+ attributes *) | |
+ match what_stage_attr exp.exp_attributes with | |
+ | Stage0 -> trx_bracket_ n exp | |
+ (* see texp_braesc for the representation of brackets and escapes | |
+ in the Typedtree *) | |
+ | Bracket(_,attrs) -> | |
+ begin | |
+ match exp.exp_desc with | |
+ | Texp_sequence (_,exp) -> | |
+ {exp with exp_type = wrap_ty_in_code n exp.exp_type; | |
+ exp_attributes = attrs; | |
+ exp_desc = | |
+ texp_apply (texp_ident "Trx.build_bracket") | |
+ [texp_loc exp.exp_loc; trx_bracket (n+1) exp]} | |
+ | _ -> assert false (* corrupted representation of bracket *) | |
+ end | |
+ | Escape(_,attrs) -> | |
+ begin | |
+ match exp.exp_desc with | |
+ | Texp_sequence (_,exp) -> | |
+ if n = 1 then exp (* switch to 0 level *) | |
+ else | |
+ {exp with | |
+ exp_type = wrap_ty_in_code n exp.exp_type; | |
+ exp_attributes = attrs; | |
+ exp_desc = texp_apply (texp_ident "Trx.build_escape") | |
+ [texp_loc exp.exp_loc; trx_bracket (n-1) exp]} | |
+ | _ -> assert false (* corrupted representation of escape *) | |
+ end | |
+ | CSP(_,li,attrs) -> (* For CSP, we only need to propagate the CSP attr *) | |
+ {exp with | |
+ exp_type = wrap_ty_in_code n exp.exp_type; | |
+ exp_attributes = attrs; | |
+ exp_desc = texp_apply | |
+ (texp_ident "Trx.dyn_quote") [exp; texp_lid li]} | |
+ | |
+ (* convert the case list to the function that receives the sequence | |
+ of bound variables and returns the array of translated guards and | |
+ bodies | |
+ *) | |
+and trx_case_list_body : int -> Typedtree.pattern -> | |
+ expression -> (* used as the template for the result: we use | |
+ the env, location info *) | |
+ case list -> expression = fun n binding_pat exp cl -> | |
+ (* Translate the future-stage function as the present-stage | |
+ function whose argument is an array of variables | |
+ (should be a tuple, really) and the type | |
+ some_targ code array -> tres code array | |
+ Using array forces a single type to all arguments. Although | |
+ it is phantom anyway, it is still a bummer. Instead of | |
+ array, we should have used a tuple. But then we can't | |
+ generically write build_fun. | |
+ *) | |
+ (* Pattern representing the function's argument: | |
+ array of variables bound by the original pattern, in order. | |
+ *) | |
+ let body = | |
+ texp_array (List.map (fun {c_guard;c_rhs;_} -> | |
+ texp_tuple [texp_option @@ map_option (trx_bracket n) c_guard; | |
+ trx_bracket n c_rhs]) | |
+ cl) in | |
+ { exp with | |
+ exp_desc = Texp_function ("",[texp_case binding_pat body],Total); | |
+ exp_type = {exp.exp_type with desc = | |
+ Tarrow ("",binding_pat.pat_type, body.exp_type, Cok)} | |
+ } | |
+ | |
+ | |
+and trx_bracket_ : int -> expression -> expression = fun n exp -> | |
+ let new_desc = match exp.exp_desc with | |
+ (* Don't just do only for vd.val_kind = Val_reg | |
+ because (+) or Array.get are Val_prim *) | |
+ | Texp_ident (p,li,vd) -> | |
+ let stage = get_level vd.val_attributes in | |
+ (* We make CSP only if the variable is bound at the stage 0. | |
+ Variables bound at stage > 0 are subject to renaming. | |
+ They are translated into stage 0 variable but of a different | |
+ type (t code), as explained in the title comments. | |
+ *) | |
+ if stage = 0 then trx_csp exp p li | |
+ else | |
+ (* Future-stage bound variable becomes the present-stage | |
+ bound-variable, but at a different type. | |
+ *) | |
+ let () = assert (vd.val_kind = Val_reg) in | |
+ (* The drawback is that exp.exp_loc disappears. If the scope extrusion | |
+ is reported for a simple expression like <x>, we can no longer | |
+ print in the error message the location that <x> appeared. | |
+ We can only print the location x was bound. | |
+ *) | |
+ Texp_ident (p,li,{vd with val_type = wrap_ty_in_code n vd.val_type}) | |
+ | |
+ | Texp_constant cst -> | |
+ texp_code ~node_id:"*cst*" exp.exp_loc (Pexp_constant cst) | |
+ | |
+ (* The most common case of let-expressions: non-recursive | |
+ let x = e in body *) | |
+ | Texp_let (Nonrecursive,[{vb_pat = {pat_desc = Tpat_var (_,name)} as pat; | |
+ vb_expr = e}],ebody) -> | |
+ let pat = {pat with pat_type = wrap_ty_in_code n pat.pat_type} in | |
+ texp_apply (texp_ident "Trx.build_let_simple_nonrec") | |
+ [texp_loc exp.exp_loc; | |
+ texp_string_loc name; | |
+ trx_bracket n e; | |
+ { exp with | |
+ exp_desc = | |
+ Texp_function ("",[texp_case pat (trx_bracket n ebody)],Total); | |
+ exp_type = | |
+ {exp.exp_type with desc = | |
+ Tarrow ("",pat.pat_type, wrap_ty_in_code n ebody.exp_type, Cok)} | |
+ } | |
+ ] | |
+ | |
+ (* General case of let. There are two subcases: parallel and recursive: | |
+ let x = e1 and y = e2 ... in body | |
+ let rec x = e1 and y = e2 ... in body | |
+ | |
+ The difference between them is profound: in the first case, | |
+ x and y do not scope over e1 and e2, but in the recursive case, | |
+ they do. | |
+ And yet we translate the two cases uniformly. Recall that we are | |
+ processing the Typedtree: the type checker already determined | |
+ the scoping rules. The variables are represented with their paths, | |
+ which bear unique timestamps. See the comment in prepare_cases | |
+ above. | |
+ *) | |
+ (* Recursive let: | |
+ let rec f = e1 [and g = e2 ...] in body | |
+ According to transl_let in bytecomp/translcore.ml, | |
+ the patterns in recursive let are very restrictive: elther | |
+ let rec var = ... | |
+ or | |
+ let rec _ as var = ... | |
+ For instance, let rec (x1,x2) = ... is not allowed. | |
+ We do this test here. For simplicity, we are not going to support | |
+ let rec _ as var = ... | |
+ pattern. | |
+ | |
+ There is another constraint: see check_recursive_lambda in | |
+ bytecomp/translcore.ml. We use a simpler version of the test: | |
+ we allow only letrec expressions of the form | |
+ let rec f = fun x -> .... | |
+ that is, | |
+ let rec f x y ... = | |
+ *) | |
+ | Texp_let (recf,vbl,ebody) -> | |
+ let check_letrec ({vb_pat=p;vb_expr=e} as vb) = | |
+ begin | |
+ match p.pat_desc with | |
+ | Tpat_var (_,_) -> () | |
+ | _ -> | |
+ trx_error @@ Location.errorf ~loc:p.pat_loc | |
+ "Only variables are allowed as left-hand side of `let rec'" | |
+ end; | |
+ match e.exp_desc with | |
+ | Texp_function (_,_,_) -> () | |
+ | _ -> | |
+ trx_error @@ Location.errorf ~loc:vb.vb_loc | |
+ "Recursive let binding must be to a function" | |
+ (* Location.print e.exp_loc *) | |
+ in if recf = Recursive then | |
+ List.iter check_letrec vbl; | |
+ (* Artificially convert vbl to case list, making the body | |
+ the first case with the Pat_any pattern. | |
+ Of course the scoping rules are different for vbl and case list. | |
+ Again, scoping has been already determined and resolved, | |
+ and our case list processing assumes very wild scoping that | |
+ accommodates let, letrec, functions, etc. | |
+ *) | |
+ let cl = | |
+ {c_lhs = (* pseudo-pattern for ebody *) | |
+ {pat_desc = Tpat_any; pat_loc=ebody.exp_loc; | |
+ pat_attributes=[]; pat_extra=[]; | |
+ pat_type=ebody.exp_type; | |
+ pat_env=ebody.exp_env}; | |
+ c_guard=None; | |
+ c_rhs=ebody} :: | |
+ List.map (fun {vb_pat;vb_expr} -> texp_case vb_pat vb_expr) vbl in | |
+ let (pl,names,binding_pat) = | |
+ trx_cl cl (wrap_ty_in_code n (Btype.newgenvar ())) in | |
+ texp_apply (texp_ident "Trx.build_let") | |
+ [texp_loc exp.exp_loc; | |
+ texp_bool (recf = Recursive); | |
+ texp_pats_names pl names; | |
+ trx_case_list_body n binding_pat exp cl | |
+ ] | |
+ | |
+ (* The most common case of functions: fun x -> body *) | |
+ | Texp_function (l,[{c_guard=None; | |
+ c_lhs={pat_extra=[]; | |
+ pat_desc = Tpat_var (_,name)} as pat; | |
+ c_rhs=ebody}],_) -> | |
+ let pat = {pat with pat_type = wrap_ty_in_code n pat.pat_type} in | |
+ texp_apply (texp_ident "Trx.build_fun_simple") | |
+ [texp_loc exp.exp_loc; | |
+ texp_string l; | |
+ texp_string_loc name; | |
+ (* Translate the future-stage function as present-stage function; | |
+ with the same variables, but with a different type, | |
+ targ code -> tres code | |
+ *) | |
+ { exp with | |
+ exp_desc = | |
+ Texp_function | |
+ ("",[texp_case pat (trx_bracket n ebody)],Total); | |
+ exp_type = | |
+ {exp.exp_type with desc = | |
+ Tarrow ("",pat.pat_type, wrap_ty_in_code n ebody.exp_type, Cok)} | |
+ } | |
+ ] | |
+ | |
+ | Texp_function (l,cl,_) -> | |
+ begin | |
+ match trx_cl cl (wrap_ty_in_code n (Btype.newgenvar ())) with | |
+ | (pl, [], _) -> (* non-binding pattern *) | |
+ texp_apply (texp_ident "Trx.build_fun_nonbinding") | |
+ [texp_loc exp.exp_loc; | |
+ texp_string l; | |
+ begin | |
+ let pl_exp = texp_ident "Trx.sample_pat_list" in | |
+ {pl_exp with | |
+ exp_desc = (texp_csp (Obj.repr pl)).exp_desc} | |
+ end; | |
+ texp_array (List.map (fun {c_guard;c_rhs;_} -> | |
+ texp_tuple [texp_option @@ | |
+ map_option (trx_bracket n) c_guard; | |
+ trx_bracket n c_rhs]) | |
+ cl) | |
+ ] | |
+ | (pl, names, binding_pat) -> | |
+ texp_apply (texp_ident "Trx.build_fun") | |
+ [texp_loc exp.exp_loc; | |
+ texp_string l; | |
+ texp_pats_names pl names; | |
+ trx_case_list_body n binding_pat exp cl | |
+ ] | |
+ end | |
+ | |
+ | Texp_apply (e, el) -> | |
+ (* first, we remove from el the information added by the type-checker *) | |
+ let lel = List.fold_right (function (* keep the order! *) | |
+ | (_,None,_) -> fun acc -> acc | |
+ | (l,Some e,_) -> fun acc -> (l,e)::acc) el [] in | |
+ let lel = ("",e) :: lel in (* Add the operator *) | |
+ texp_apply (texp_ident "Trx.build_apply") | |
+ [texp_loc exp.exp_loc; | |
+ texp_array (List.map (fun (l,e) -> | |
+ texp_tuple [texp_string l;trx_bracket n e]) lel)] | |
+ | |
+ (* Pretty much like a function *) | |
+ (* rcl: regular cases; ecl: exceptional cases *) | |
+ | Texp_match (e,rcl,ecl,_) -> | |
+ let cl = rcl @ ecl in (* handle all cases uniformly *) | |
+ let (pl,names,binding_pat) = | |
+ trx_cl cl (wrap_ty_in_code n (Btype.newgenvar ())) in | |
+ texp_apply (texp_ident "Trx.build_match") | |
+ [texp_loc exp.exp_loc; | |
+ texp_pats_names pl names; | |
+ trx_bracket n e; | |
+ texp_int @@ List.length rcl; | |
+ trx_case_list_body n binding_pat exp cl | |
+ ] | |
+ | |
+ | Texp_try (e,cl) -> (* same as Texp_match *) | |
+ let (pl,names,binding_pat) = | |
+ trx_cl cl (wrap_ty_in_code n (Btype.newgenvar ())) in | |
+ texp_apply (texp_ident "Trx.build_try") | |
+ [texp_loc exp.exp_loc; | |
+ texp_pats_names pl names; | |
+ trx_bracket n e; | |
+ trx_case_list_body n binding_pat exp cl | |
+ ] | |
+ | |
+ | Texp_tuple el -> | |
+ texp_apply (texp_ident "Trx.build_tuple") | |
+ [texp_loc exp.exp_loc; | |
+ texp_array (List.map (trx_bracket n) el)] | |
+ | |
+ | Texp_construct (li, cdesc, args) -> | |
+ let lid = qualify_ctor li cdesc in | |
+ texp_apply (texp_ident "Trx.build_construct") | |
+ [texp_loc exp.exp_loc; | |
+ texp_lid lid; | |
+ texp_array (List.map (trx_bracket n) args)] | |
+ | |
+ | Texp_variant (l,eo) -> (* polymorphic variant *) | |
+ texp_apply (texp_ident "Trx.build_variant") | |
+ [texp_loc exp.exp_loc; | |
+ texp_string l; | |
+ texp_option (map_option (trx_bracket n) eo)] | |
+ | |
+ | Texp_record (lel,eo) -> | |
+ texp_apply (texp_ident "Trx.build_record") | |
+ [texp_loc exp.exp_loc; | |
+ texp_array (List.map (fun (li,ldesc,e) -> | |
+ texp_tuple [texp_lid (qualify_label li ldesc); | |
+ trx_bracket n e]) lel); | |
+ texp_option (map_option (trx_bracket n) eo)] | |
+ | |
+ | Texp_field (e,li,ldesc) -> | |
+ texp_apply (texp_ident "Trx.build_field") | |
+ [texp_loc exp.exp_loc; | |
+ trx_bracket n e; | |
+ texp_lid (qualify_label li ldesc)] | |
+ | |
+ | Texp_setfield (e1,li,ldesc,e2) -> | |
+ texp_apply (texp_ident "Trx.build_setfield") | |
+ [texp_loc exp.exp_loc; | |
+ trx_bracket n e1; | |
+ texp_lid (qualify_label li ldesc); | |
+ trx_bracket n e2] | |
+ | |
+ | Texp_array el -> | |
+ texp_apply (texp_ident "Trx.build_array") | |
+ [texp_loc exp.exp_loc; | |
+ texp_array (List.map (trx_bracket n) el)] | |
+ | |
+ | Texp_ifthenelse (e,et,efo) -> | |
+ texp_apply (texp_ident "Trx.build_ifthenelse") | |
+ [texp_loc exp.exp_loc; | |
+ trx_bracket n e; | |
+ trx_bracket n et; | |
+ texp_option (map_option (trx_bracket n) efo)] | |
+ | |
+ | Texp_sequence (e1,e2) -> | |
+ texp_apply (texp_ident "Trx.build_sequence") | |
+ [texp_loc exp.exp_loc; | |
+ trx_bracket n e1; trx_bracket n e2] | |
+ | Texp_while (e1,e2) -> | |
+ texp_apply (texp_ident "Trx.build_while") | |
+ [texp_loc exp.exp_loc; | |
+ trx_bracket n e1; trx_bracket n e2] | |
+ | |
+ | Texp_for (id, pat, elo, ehi, dir, ebody) -> | |
+ let name = | |
+ begin | |
+ match pat.ppat_desc with | |
+ | Ppat_any -> mknoloc "_for" (* the typechecker also makes a dummy*) | |
+ | Ppat_var x -> x | |
+ | _ -> assert false | |
+ end | |
+ in | |
+ texp_apply (texp_ident "Trx.build_for") | |
+ [texp_loc exp.exp_loc; | |
+ texp_string_loc name; | |
+ trx_bracket n elo; | |
+ trx_bracket n ehi; | |
+ texp_bool (dir = Upto); | |
+ let var_typ = wrap_ty_in_code n (Ctype.instance_def Predef.type_int) in | |
+ let pat = {pat_loc = exp.exp_loc; pat_extra = []; | |
+ pat_attributes = []; | |
+ pat_type = var_typ; pat_env = exp.exp_env; | |
+ pat_desc = Tpat_var (id,name)} in | |
+ { exp with | |
+ exp_desc = | |
+ Texp_function ("",[texp_case pat (trx_bracket n ebody)],Total); | |
+ exp_type = | |
+ {exp.exp_type with desc = | |
+ Tarrow ("",var_typ, wrap_ty_in_code n ebody.exp_type, Cok)} | |
+ } | |
+ ] | |
+ | |
+ | Texp_send (e,m,_) -> | |
+ (* We don't check the persistence of the method: after all, | |
+ a method name is somewhat like a polymorphic variant. | |
+ It's perfectly OK to have a function fun x -> x # foo | |
+ *) | |
+ texp_apply (texp_ident "Trx.build_send") | |
+ [texp_loc exp.exp_loc; | |
+ trx_bracket n e; | |
+ texp_string (match m with | |
+ | Tmeth_name name -> name | |
+ | Tmeth_val id -> Ident.name id)] | |
+ | |
+ | Texp_new (p,li,_) -> | |
+ check_path_quotable "Class" p; | |
+ texp_code ~node_id:"*new*" exp.exp_loc | |
+ (Pexp_new (Location.mkloc (path_to_lid p) li.loc)) | |
+ | |
+ | Texp_instvar (p1,p2,s) -> | |
+ not_supported exp.exp_loc "Objects (Texp_instvar)" | |
+ (* Alternatively: since instance variables are always bound | |
+ at level 0 (for now) | |
+ so this is like a csp variable | |
+ call_trx_mkcsp exp None (path_to_lid p2) | |
+ *) | |
+ | Texp_setinstvar _ -> not_supported exp.exp_loc "Objects (Texp_setinstvar)" | |
+ | Texp_override _ -> not_supported exp.exp_loc "Objects (Texp_override)" | |
+ | Texp_letmodule (id,s,me,e) -> not_supported exp.exp_loc "let module" | |
+ | |
+ | Texp_assert e -> | |
+ texp_apply (texp_ident "Trx.build_assert") | |
+ [texp_loc exp.exp_loc; trx_bracket n e] | |
+ | |
+ | Texp_lazy e -> | |
+ texp_apply (texp_ident "Trx.build_lazy") | |
+ [texp_loc exp.exp_loc; trx_bracket n e] | |
+ | |
+ | Texp_object (cl,fl) -> not_supported exp.exp_loc "Objects" | |
+ | Texp_pack _ -> not_supported exp.exp_loc "First-class modules" | |
+ (* | _ -> not_supported exp.exp_loc "not yet supported" *) | |
+ in | |
+ (* See untype_extra in tools/untypeast.ml *) | |
+ let trx_extra (extra, loc, attr) exp = (* TODO: take care of attr *) | |
+ let desc = | |
+ match extra with | |
+ (* Should check that cty1 and cty2 contain only globally declared | |
+ type components | |
+ *) | |
+ | Texp_constraint cty -> | |
+ not_supported loc "Texp_constraint" | |
+ | Texp_coerce (cto,ct) -> | |
+ not_supported loc "Texp_coerce" | |
+ | Texp_open (ovf, path, lid, _) -> | |
+ (* I don't need local open since all the constructors | |
+ and identifiers are qualified anyway. | |
+ *) | |
+ exp.exp_desc | |
+ (* | |
+ check_path_quotable "Texp_open" path; | |
+ texp_apply (texp_ident "Trx.build_open") | |
+ [texp_loc exp.exp_loc; | |
+ texp_lid (mkloc (path_to_lid path) lid.loc); | |
+ texp_csp (Obj.repr ovf); | |
+ exp] (* exp is the result of trx_bracket *) | |
+ *) | |
+ | Texp_poly cto -> not_supported loc "Texp_poly" | |
+ | Texp_newtype s -> not_supported loc "Texp_newtype" | |
+ in {exp with exp_loc = loc; exp_desc = desc} (* type is the same: code *) | |
+ in | |
+ List.fold_right trx_extra exp.exp_extra | |
+ {exp with exp_type = wrap_ty_in_code n exp.exp_type; | |
+ exp_desc = new_desc} | |
+ | |
+ | |
+ | |
+(*{{{ Typedtree traversal to eliminate bracket/escapes *) | |
+ | |
+(* This part is obsolete *) | |
+ | |
+(* ------------------------------------------------------------------------ *) | |
+(* Typedtree traversal to eliminate bracket/escapes *) | |
+ | |
+(* Functions to help traverse and transform a tree. | |
+ We assume that every tree mapping function of the type 'a -> 'a | |
+ throws the exception Not_modified if the tree has not been | |
+ modified. | |
+ This protocol helps minimize garbage and prevent useless tree | |
+ duplication. | |
+ | |
+ We do not traverse attributes. | |
+*) | |
+ | |
+exception Not_modified | |
+ | |
+let replace_list : ('a -> 'a) -> 'a list -> 'a list = fun f l -> | |
+ let rec loop mdf = function | |
+ | [] -> if mdf then [] else raise Not_modified | |
+ | h::t -> match (try Some (f h) with Not_modified -> None) with | |
+ | Some h -> h :: loop true t | |
+ | None -> h :: loop mdf t | |
+ in loop false l | |
+ | |
+let replace_pair : ('a -> 'a) -> ('b -> 'b) -> 'a * 'b -> 'a * 'b = | |
+ fun f g (x,y) -> | |
+ match ((try Some (f x) with Not_modified -> None), | |
+ (try Some (g y) with Not_modified -> None)) with | |
+ | (None,None) -> raise Not_modified | |
+ | (Some x, None) -> (x,y) | |
+ | (None, Some y) -> (x,y) | |
+ | (Some x, Some y) -> (x,y) | |
+ | |
+let replace_opt : ('a -> 'a) -> 'a option -> 'a option = fun f -> function | |
+ | Some e -> Some (f e) | |
+ | None -> raise Not_modified | |
+ | |
+(* The main function to scan the typed tree at the 0 level and | |
+ detect brackets | |
+*) | |
+ | |
+let rec trx_struct str = | |
+ {str with str_items = | |
+ replace_list (fun si -> {si with str_desc = trx_struct_item si.str_desc}) | |
+ str.str_items} | |
+ | |
+and trx_vb_list l = | |
+ replace_list (fun vb -> {vb with vb_expr = trx_exp vb.vb_expr}) l | |
+ | |
+and trx_struct_item = function | |
+| Tstr_eval (e,a) -> Tstr_eval (trx_exp e,a) | |
+| Tstr_value (rf,vbl) -> | |
+ Tstr_value(rf, trx_vb_list vbl) | |
+| Tstr_primitive _ | |
+| Tstr_type _ | |
+| Tstr_typext _ | |
+| Tstr_exception _ -> raise Not_modified | |
+| Tstr_module mb -> Tstr_module (trx_mb mb) | |
+| Tstr_recmodule mbl -> Tstr_recmodule (replace_list trx_mb mbl) | |
+| Tstr_modtype _ | |
+| Tstr_open _ -> raise Not_modified | |
+| Tstr_class l -> | |
+ Tstr_class (replace_list (fun (dcl,sl,vf) -> (trx_cdcl dcl,sl,vf)) l) | |
+| Tstr_class_type _ -> raise Not_modified | |
+| Tstr_include id -> | |
+ Tstr_include {id with incl_mod = trx_me id.incl_mod} | |
+| Tstr_attribute _ -> raise Not_modified | |
+ | |
+and trx_mb mb = | |
+ {mb with mb_expr = trx_me mb.mb_expr} | |
+ | |
+and trx_me me = | |
+ {me with mod_desc = trx_me_desc me.mod_desc} | |
+ | |
+and trx_me_desc = function | |
+| Tmod_ident _ -> raise Not_modified | |
+| Tmod_structure str -> Tmod_structure (trx_struct str) | |
+| Tmod_functor (i,l,t,me) -> Tmod_functor (i,l,t, trx_me me) | |
+| Tmod_apply (me1,me2,mc) -> | |
+ let (me1,me2) = replace_pair trx_me trx_me (me1,me2) in | |
+ Tmod_apply (me1, me2, mc) | |
+| Tmod_constraint (me,mt,mtc,mc) -> Tmod_constraint (trx_me me, mt, mtc, mc) | |
+| Tmod_unpack (e,mt) -> Tmod_unpack (trx_exp e,mt) | |
+ | |
+and trx_cdcl class_decl = | |
+ {class_decl with ci_expr = trx_ce class_decl.ci_expr} | |
+ | |
+and trx_ce class_expr = | |
+ {class_expr with cl_desc = trx_ce_desc class_expr.cl_desc} | |
+ | |
+and trx_cl_struct cs = | |
+ {cs with cstr_fields = | |
+ replace_list (fun cf -> {cf with cf_desc = trx_cf cf.cf_desc}) | |
+ cs.cstr_fields} | |
+ | |
+and trx_ce_desc = function | |
+| Tcl_ident (_,_,_) -> raise Not_modified | |
+| Tcl_structure cs -> Tcl_structure (trx_cl_struct cs) | |
+| Tcl_fun (l,p,el,ce,pa) -> | |
+ let (el,ce) = | |
+ replace_pair (replace_list (fun (i,l,e) -> (i,l,trx_exp e))) | |
+ trx_ce (el,ce) in | |
+ Tcl_fun (l,p,el,ce,pa) | |
+| Tcl_apply (ce,el) -> | |
+ let repel (l,eo,o) = (l,replace_opt trx_exp eo,o) in | |
+ let (ce,el) = replace_pair trx_ce (replace_list repel) (ce,el) in | |
+ Tcl_apply (ce,el) | |
+| Tcl_let (rf,vbl,el2,ce) -> | |
+ let repel2 = replace_list (fun (i,l,e) -> (i,l,trx_exp e)) in | |
+ let ((vbl,el2),ce) = replace_pair (replace_pair trx_vb_list repel2) trx_ce | |
+ ((vbl,el2),ce) | |
+ in Tcl_let (rf,vbl,el2,ce) | |
+| Tcl_constraint (ce,ct,sl1,sl2,cty) -> | |
+ Tcl_constraint (trx_ce ce,ct,sl1,sl2,cty) | |
+ | |
+and trx_cf = function | |
+| Tcf_inherit (ofl,ce,so,sl1,sl2) -> | |
+ Tcf_inherit (ofl,trx_ce ce,so,sl1,sl2) | |
+| Tcf_val (_,_,_,Tcfk_virtual _,_) -> raise Not_modified | |
+| Tcf_val (sl,mf,i,Tcfk_concrete (ovf,e),b) -> | |
+ Tcf_val (sl,mf,i,Tcfk_concrete (ovf,trx_exp e),b) | |
+| Tcf_method (sl,pf,Tcfk_virtual _) -> raise Not_modified | |
+| Tcf_method (sl,pf,Tcfk_concrete (ovf,e)) -> | |
+ Tcf_method (sl,pf,Tcfk_concrete (ovf,trx_exp e)) | |
+| Tcf_constraint (_,_) -> raise Not_modified | |
+| Tcf_initializer e -> Tcf_initializer (trx_exp e) | |
+| Tcf_attribute _ -> raise Not_modified | |
+ | |
+and trx_exp exp = | |
+ {exp with exp_desc = trx_expression exp.exp_desc} | |
+ | |
+and trx_caselist l = replace_list (fun cas -> | |
+ let (g,rhs) = replace_pair (replace_opt trx_exp) trx_exp | |
+ (cas.c_guard,cas.c_rhs) in | |
+ {cas with c_guard = g; c_rhs = rhs}) | |
+ l | |
+ | |
+and trx_expression = function | |
+| Texp_ident (_,_,_) | |
+| Texp_constant _ -> raise Not_modified | |
+| Texp_let (rf, vbl, e) -> | |
+ let (vbl,e) = replace_pair trx_vb_list trx_exp (vbl,e) | |
+ in Texp_let (rf, vbl, e) | |
+| Texp_function (l,cl,p) -> | |
+ Texp_function (l,trx_caselist cl,p) | |
+| Texp_apply (e,el) -> | |
+ let repl (l,eo,op) = (l,replace_opt trx_exp eo,op) in | |
+ let (e,el) = replace_pair trx_exp (replace_list repl) (e,el) | |
+ in Texp_apply (e,el) | |
+| Texp_match (e,cl1,cl2,p) -> | |
+ let (e,(cl1,cl2)) = replace_pair | |
+ trx_exp (replace_pair trx_caselist trx_caselist) (e,(cl1,cl2)) | |
+ in Texp_match (e,cl1,cl2,p) | |
+| Texp_try (e,cl) -> | |
+ let (e,cl) = replace_pair trx_exp trx_caselist (e,cl) | |
+ in Texp_try (e,cl) | |
+| Texp_tuple l -> Texp_tuple (replace_list trx_exp l) | |
+| Texp_construct (l,cd,el) -> | |
+ Texp_construct (l,cd,replace_list trx_exp el) | |
+| Texp_variant (l,eo) -> Texp_variant (l,replace_opt trx_exp eo) | |
+| Texp_record (ll,eo) -> | |
+ let repll (l,ld,e) = (l,ld,trx_exp e) in | |
+ let (ll,eo) = replace_pair (replace_list repll) (replace_opt trx_exp) (ll,eo) | |
+ in Texp_record (ll,eo) | |
+| Texp_field (e,l,ld) -> Texp_field (trx_exp e,l,ld) | |
+| Texp_setfield (e1,l,ld,e2) -> | |
+ let (e1,e2) = replace_pair trx_exp trx_exp (e1,e2) | |
+ in Texp_setfield (e1,l,ld,e2) | |
+| Texp_array el -> Texp_array (replace_list trx_exp el) | |
+| Texp_ifthenelse (e1,e2,eo) -> | |
+ let ((e1,e2),eo) = replace_pair (replace_pair trx_exp trx_exp) | |
+ (replace_opt trx_exp) ((e1,e2),eo) | |
+ in Texp_ifthenelse (e1,e2,eo) | |
+| Texp_sequence (e1,e2) -> | |
+ let (e1,e2) = replace_pair trx_exp trx_exp (e1,e2) | |
+ in Texp_sequence (e1,e2) | |
+| Texp_while (e1,e2) -> | |
+ let (e1,e2) = replace_pair trx_exp trx_exp (e1,e2) | |
+ in Texp_while (e1,e2) | |
+| Texp_for (i,p,e1,e2,df,e3) -> | |
+ let ((e1,e2),e3) = replace_pair (replace_pair trx_exp trx_exp) | |
+ trx_exp ((e1,e2),e3) | |
+ in Texp_for (i,p,e1,e2,df,e3) | |
+| Texp_send (e1,m,eo) -> | |
+ let (e1,eo) = replace_pair trx_exp (replace_opt trx_exp) (e1,eo) | |
+ in Texp_send (e1,m,eo) | |
+| Texp_new (_,_,_) | |
+| Texp_instvar (_,_,_) -> raise Not_modified | |
+| Texp_setinstvar (p1,p2,l,e) -> Texp_setinstvar (p1,p2,l,trx_exp e) | |
+| Texp_override (p, el) -> | |
+ Texp_override (p, replace_list (fun (p,l,e) -> (p,l,trx_exp e)) el) | |
+| Texp_letmodule (i,l,me,e) -> | |
+ let (me,e) = replace_pair trx_me trx_exp (me,e) | |
+ in Texp_letmodule (i,l,me,e) | |
+| Texp_assert e -> Texp_assert (trx_exp e) | |
+| Texp_lazy e -> Texp_lazy (trx_exp e) | |
+| Texp_object (cs,sl) -> Texp_object (trx_cl_struct cs,sl) | |
+| Texp_pack me -> Texp_pack (trx_me me) | |
+(* | |
+| Texp_bracket e -> | |
+ let trx_exp e = try trx_exp e with Not_modified -> e in | |
+ (trx_bracket trx_exp 1 e).exp_desc | |
+ | |
+| Texp_escape _ -> assert false (* Not possible in well-typed code *) | |
+| Texp_cspval (_,_) -> raise Not_modified | |
+*) | |
+ | |
+ | |
+(* public interface *) | |
+let trx_structure str = | |
+ try trx_struct str with Not_modified -> str | |
+ | |
+(*}}}*) | |
+ | |
+ | |
diff -Naur ocaml-4.02.1/typing/trx.mli ocaml-ber-n102/typing/trx.mli | |
--- ocaml-4.02.1/typing/trx.mli 1970-01-01 01:00:00.000000000 +0100 | |
+++ ocaml-ber-n102/typing/trx.mli 2015-01-10 16:27:06.932031073 +0000 | |
@@ -0,0 +1,171 @@ | |
+(* BER MetaOCaml compilation | |
+ Transforming the Typedtree to eliminate brackets and escapes, | |
+ replacing them with calls to ordinary OCaml functions | |
+ to build the code representation (that is, Parsetree). | |
+*) | |
+ | |
+val meta_version : string | |
+(** [meta_version] is the version of BER MetaOCaml*) | |
+ | |
+(* The function to process the body of the bracket at level n. | |
+ This function `lifts' the Typedtree to the code that will evaluate | |
+ to the corresponding Parsetree. | |
+*) | |
+val trx_bracket : int -> Typedtree.expression -> Typedtree.expression | |
+ | |
+(* The following functions deal with the representation of brackets, | |
+ escapes and CPS in Parsetree and Typedtree. | |
+ Staging annotations and other levels are distinguished by | |
+ attributes. | |
+ The following functions are used by the typecore.ml as well | |
+ when building the Typedtree. | |
+*) | |
+ | |
+(* The result of what_stage_attr *) | |
+type stage_attr_elim = | |
+ | Stage0 | |
+ | Bracket of Parsetree.attribute * (* bracket attribute *) | |
+ Parsetree.attributes (* other attributes *) | |
+ | Escape of Parsetree.attribute * (* escape attribute *) | |
+ Parsetree.attributes (* other attributes *) | |
+ | CSP of Parsetree.attribute * | |
+ Longident.t Location.loc * (* CSP attribute and lid *) | |
+ Parsetree.attributes (* other attributes *) | |
+ | |
+(* Determining if an AST node bears a staging attribute *) | |
+val what_stage_attr : Parsetree.attributes -> stage_attr_elim | |
+ | |
+(* Build a Typedtree node for brackets or escape (the attribute tells | |
+ which is which) | |
+*) | |
+val texp_braesc : | |
+ Parsetree.attribute -> Typedtree.expression -> Env.t -> Types.type_expr -> | |
+ Typedtree.expression | |
+ | |
+(* Build a Typedtree node for a CSP *) | |
+val texp_csp_raw : | |
+ Parsetree.attribute -> Asttypes.constant -> Env.t -> Types.type_expr -> | |
+ Typedtree.expression | |
+ | |
+(* Staging level | |
+ It is set via an attribute on the value_description in the Typedtree | |
+*) | |
+type stage = int (* staging level *) | |
+val attr_level : stage -> Parsetree.attribute | |
+val get_level : Parsetree.attributes -> stage | |
+ | |
+(* If the attribute is present, the expression is non-expansive | |
+ We use physical equality comparison, to speed things up | |
+*) | |
+val attr_nonexpansive : Parsetree.attribute | |
+ | |
+(* The following functions operate on untyped code_repr. | |
+ We cannot use the type constructor 'code' here since | |
+ it is not available in the bootstrap compiler. | |
+*) | |
+ | |
+(* The representation of possibly code: abstract *) | |
+type code_repr | |
+ | |
+type closed_code_repr = private Parsetree.expression | |
+ | |
+(* Check that the code is closed and return the closed code *) | |
+val close_code_repr : code_repr -> closed_code_repr | |
+ | |
+(* The same as close_code but return the closedness check as a thunk | |
+ rather than performing it. | |
+ This is useful for debugging and for showing the code: | |
+ If there is a scope extrusion error, it is still useful | |
+ to show the code with the extrusion before throwing the scope-extrusion | |
+ exception. | |
+*) | |
+val close_code_delay_check : code_repr -> closed_code_repr * (unit -> unit) | |
+ | |
+(* Total: a closed code can always be used in slices, etc. *) | |
+val open_code : closed_code_repr -> code_repr | |
+ | |
+(* Adjusting the implementation of stackmarks -- needed when delimited | |
+ control is used (other than mere exceptions). | |
+*) | |
+type stackmark = unit -> bool (* true if valid *) | |
+type stackmark_region_fn = | |
+ {stackmark_region_fn : 'w. (stackmark -> 'w) -> 'w} | |
+val set_with_stack_mark : stackmark_region_fn -> unit | |
+ | |
+ | |
+(* The following names are used by Trx itself to construct a Parsetree | |
+ or as templates to build the Typedtree. | |
+ Trx may generate code the refers to the functions below. | |
+ Therefore, do NOT rename the functions or change their types! | |
+*) | |
+ | |
+val loc_none : Location.t | |
+val sample_lid : Longident.t Location.loc (* A template for lid expressions *) | |
+val sample_name : string Location.loc | |
+val sample_pat_list : Parsetree.pattern list | |
+val sample_pats_names : Parsetree.pattern list * string Location.loc list | |
+ | |
+ (* Run-time quotator *) | |
+val dyn_quote : Obj.t -> Longident.t Location.loc -> code_repr | |
+ | |
+val lift_constant_int : int -> code_repr | |
+val lift_constant_char : char -> code_repr | |
+val lift_constant_bool : bool -> code_repr | |
+ | |
+(* Builders of the Parsetree *) | |
+val build_assert : Location.t -> code_repr -> code_repr | |
+val build_lazy : Location.t -> code_repr -> code_repr | |
+val build_bracket : Location.t -> code_repr -> code_repr | |
+val build_escape : Location.t -> code_repr -> code_repr | |
+ | |
+val build_sequence : Location.t -> code_repr -> code_repr -> code_repr | |
+val build_while : Location.t -> code_repr -> code_repr -> code_repr | |
+ | |
+val build_apply : Location.t -> (Asttypes.label * code_repr) array -> code_repr | |
+ | |
+val build_tuple : Location.t -> code_repr array -> code_repr | |
+val build_array : Location.t -> code_repr array -> code_repr | |
+val build_ifthenelse : | |
+ Location.t -> code_repr -> code_repr -> code_repr option -> code_repr | |
+val build_construct : | |
+ Location.t -> Longident.t Location.loc -> code_repr array -> code_repr | |
+val build_record : | |
+ Location.t -> (Longident.t Location.loc * code_repr) array -> | |
+ code_repr option -> code_repr | |
+val build_field : | |
+ Location.t -> code_repr -> Longident.t Location.loc -> code_repr | |
+val build_setfield : | |
+ Location.t -> code_repr -> Longident.t Location.loc -> code_repr -> code_repr | |
+val build_variant : Location.t -> string -> code_repr option -> code_repr | |
+val build_send : Location.t -> code_repr -> string -> code_repr | |
+val build_open : | |
+ Location.t -> Longident.t Location.loc -> Asttypes.override_flag -> | |
+ code_repr -> code_repr | |
+val build_fun_nonbinding : | |
+ Location.t -> string -> Parsetree.pattern list -> | |
+ (code_repr option * code_repr) array -> code_repr | |
+val build_fun_simple : | |
+ Location.t -> string -> string Location.loc -> | |
+ (code_repr -> code_repr) -> code_repr | |
+val build_for : | |
+ Location.t -> string Location.loc -> code_repr -> code_repr -> | |
+ bool -> (code_repr -> code_repr) -> code_repr | |
+val build_let_simple_nonrec : | |
+ Location.t -> string Location.loc -> code_repr -> | |
+ (code_repr -> code_repr) -> code_repr | |
+val build_fun : | |
+ Location.t -> string -> | |
+ (Parsetree.pattern list * string Location.loc list) -> | |
+ (code_repr array -> (code_repr option * code_repr) array) -> code_repr | |
+val build_let : | |
+ Location.t -> bool -> | |
+ (Parsetree.pattern list * string Location.loc list) -> | |
+ (code_repr array -> (code_repr option * code_repr) array) -> code_repr | |
+val build_match : | |
+ Location.t -> (Parsetree.pattern list * string Location.loc list) -> | |
+ code_repr -> int -> | |
+ (code_repr array -> (code_repr option * code_repr) array) -> code_repr | |
+val build_try : | |
+ Location.t -> (Parsetree.pattern list * string Location.loc list) -> | |
+ code_repr -> | |
+ (code_repr array -> (code_repr option * code_repr) array) -> code_repr | |
diff -Naur ocaml-4.02.1/typing/typecore.ml ocaml-ber-n102/typing/typecore.ml | |
--- ocaml-4.02.1/typing/typecore.ml 2015-01-10 16:27:54.274865552 +0000 | |
+++ ocaml-ber-n102/typing/typecore.ml 2015-01-10 16:27:06.932031073 +0000 | |
@@ -93,6 +93,91 @@ | |
Env.t -> Location.t -> Parsetree.class_structure -> | |
Typedtree.class_structure * Types.class_signature * string list) | |
+(* NNN: begin | |
+ The current stage level. | |
+ Type-checking the body of a bracket increases the level, | |
+ type-checking of an escape decreases. | |
+ Be sure to reset upon any exception; | |
+ alternatively; reset when beginning a new type-level | |
+ expression or binding | |
+ (whenever you do Typetexp.reset_type_variables();) | |
+ | |
+ Check all instances of Env.add_value and make sure that | |
+ we record the stage of every identifier that is added to the | |
+ value env (unless the stage is 0). | |
+also check all val_attributes and Val_reg | |
+*) | |
+let global_stage : Trx.stage ref = ref 0 | |
+ | |
+(* Obsolete; kept for reference | |
+ | |
+ The list of active classifiers. The length of the list | |
+ is the level of an expression. | |
+ Type-checking the body of a bracket adds a type variable | |
+ to the list; type-checking of an escape removes the | |
+ top-most classifier. | |
+ Be sure to reset this list upon any exception; | |
+ alternatively; reset the list when beginning a new type-level | |
+ expression or binding | |
+ (whenever you do Typetexp.reset_type_variables();) | |
+ | |
+let global_stage : Env.stage ref = ref [] | |
+ | |
+ Unify classifier lists, *right-to-left* | |
+ See the bug Tue Jan 20 12:18:00 GMTST 2004 in XXCC-BUG-OPEN-FIXED | |
+ why we need this order. | |
+ The current classifier is left-most, and the lists don't have | |
+ to have the same length. | |
+ Example: | |
+ .<fun x -> .< x >. >. | |
+ When type-checking the innermost bracket, the global_stage | |
+ will contain ['b,'a] and the level of x will be ['a] | |
+ The unification will succeed, without changing anything, as expected. | |
+ | |
+let unify_stage env tl1 tl2 = | |
+ let rec loop = function | |
+ | (t1::tl1,t2::tl2) -> unify env t1 t2; loop (tl1,tl2) | |
+ | _ -> () | |
+ in loop (List.rev tl1, List.rev tl2) | |
+*) | |
+ | |
+(* This function does not take the env argument. Normally env affects | |
+ the printing of paths (search for raise_wrong_stage_error | |
+ in this file and printtyp.ml). | |
+ The particular error message we emit here does not use paths. | |
+*) | |
+let raise_wrong_stage_error loc n m = | |
+ raise @@ | |
+ Error_forward(Location.errorf ~loc | |
+ "Wrong level: variable bound at level %d and used at level %d" n m) | |
+ | |
+let raise_unsupported loc txt = | |
+ raise @@ | |
+ Error_forward(Location.errorf ~loc | |
+ "Not supported within brackets: %s" txt) | |
+ | |
+let with_stage_up body = | |
+ let old_stage = !global_stage in | |
+ let () = incr global_stage in | |
+ try | |
+ let r = body () in | |
+ global_stage := old_stage; r | |
+ with e -> | |
+ global_stage := old_stage; raise e | |
+ | |
+let with_stage_down loc env body = | |
+ let old_stage = !global_stage in | |
+ if !global_stage = 0 then | |
+ raise @@ Error_forward(Location.errorf ~loc | |
+ "Wrong level: escape at level 0"); | |
+ decr global_stage; | |
+ try | |
+ let r = body () in | |
+ global_stage := old_stage; r | |
+ with e -> | |
+ global_stage := old_stage; raise e | |
+(* NNN end *) | |
+ | |
(* | |
Saving and outputting type information. | |
We keep these function names short, because they have to be | |
@@ -1258,7 +1343,7 @@ | |
let check = if as_var then check_as else check in | |
Env.add_value ?check id | |
{val_type = ty; val_kind = Val_reg; Types.val_loc = loc; | |
- val_attributes = []; | |
+ val_attributes = [Trx.attr_level !global_stage] (* NNN *) | |
} env | |
) | |
pv env, | |
@@ -1301,7 +1386,8 @@ | |
((id', name, id, ty)::pv, | |
Env.add_value id' {val_type = ty; | |
val_kind = Val_ivar (Immutable, cl_num); | |
- val_attributes = []; | |
+ val_attributes = | |
+ [Trx.attr_level !global_stage]; (* NNN *) | |
Types.val_loc = loc; | |
} ~check | |
env)) | |
@@ -1325,6 +1411,10 @@ | |
let pv = !pattern_variables in | |
pattern_variables := []; | |
let (val_env, met_env, par_env) = | |
+ (* NNN we don't record stage for | |
+ Env.add_value below | |
+ since we don't handle classes within brackets. | |
+ *) | |
List.fold_right | |
(fun (id, ty, name, loc, as_var) (val_env, met_env, par_env) -> | |
(Env.add_value id {val_type = ty; | |
@@ -1377,6 +1467,7 @@ | |
(* Generalization criterion for expressions *) | |
let rec is_nonexpansive exp = | |
+ List.memq Trx.attr_nonexpansive exp.exp_attributes || (* NNN *) | |
match exp.exp_desc with | |
Texp_ident(_,_,_) -> true | |
| Texp_constant _ -> true | |
@@ -1491,7 +1582,14 @@ | |
approx_type env sty | |
| _ -> newvar () | |
-let rec type_approx env sexp = | |
+let rec type_approx env sexp = (* NNN the whole function *) | |
+ let open Trx in | |
+ match what_stage_attr sexp.pexp_attributes with | |
+ | Stage0 -> type_approx_orig env sexp | |
+ (* instance env @@ Predef.type_code @@ type_approx_orig env sexp *) | |
+ | _ -> newvar () | |
+and | |
+ type_approx_orig env sexp = (* NNN end *) | |
match sexp.pexp_desc with | |
Pexp_let (_, _, e) -> type_approx env e | |
| Pexp_fun (p, _, _, e) when is_optional p -> | |
@@ -1702,6 +1800,11 @@ | |
I don't think this is what we want *) | |
let (path, desc) = Env.lookup_value (Longident.Lident s) env in | |
match path with | |
+ (* NNN The code below uses the existing desc (value-descriptor) | |
+ as the template and simply changes val_type. | |
+ The attributes, specifically staging level attribute, | |
+ are preserved then. | |
+ *) | |
Path.Pident id -> | |
let desc = {desc with val_type = correct_levels desc.val_type} in | |
Env.add_value id desc env | |
@@ -1736,8 +1839,66 @@ | |
(Cmt_format.Partial_expression exp :: previous_saved_types); | |
exp | |
+(* NNN This whole function type_expect_ *) | |
+(* Type checking staging constructs *) | |
+(* If we are type-checking bracket at level 0, don't build the | |
+ bracket Texp node. Rather, invoke trx_bracket to translate | |
+ the bracket body and convert it to the c ode generator. | |
+*) | |
and type_expect_ ?in_function env sexp ty_expected = | |
let loc = sexp.pexp_loc in | |
+ let open Trx in | |
+ (* Keep in mind that there may be several metaocaml attributes, | |
+ and their order matters. | |
+ *) | |
+ match what_stage_attr sexp.pexp_attributes with | |
+ | Stage0 -> type_expect_orig ?in_function env sexp ty_expected | |
+ | Bracket(battr,attrs) -> | |
+ (* Typechecking bracket *) | |
+ (* follow Pexp_array or Pexp_lazy as a template *) | |
+ (* Expected type: ty code where ty is the type | |
+ of the expression within brackets. | |
+ *) | |
+ let ty = newgenvar() in (* expected type for the bracketed sexp *) | |
+ let to_unify = Predef.type_code ty in | |
+ unify_exp_types loc env to_unify ty_expected; | |
+ let exp = | |
+ with_stage_up (fun () -> | |
+ (* drop bracket attr *) | |
+ let sexp = {sexp with pexp_attributes = attrs} in | |
+ type_expect env sexp ty) in | |
+ re @@ | |
+ if !global_stage = 0 then | |
+ (* Check if the expression non-expansive before the translation *) | |
+ let nonexp = is_nonexpansive exp in | |
+ let exp = trx_bracket 1 exp in | |
+ {exp with exp_type = instance env ty_expected; | |
+ exp_attributes = | |
+ if nonexp then attr_nonexpansive :: exp.exp_attributes | |
+ else exp.exp_attributes} | |
+ else | |
+ texp_braesc battr exp env (instance env ty_expected) | |
+ | |
+ (* NNN: Typechecking escapes *) | |
+ (* If ~e is expected to have the type ty then | |
+ e is expected to have the type ty code | |
+ *) | |
+ | Escape(battr,attrs) -> | |
+ with_stage_down loc env (fun () -> | |
+ let sexp_ty_expected = Predef.type_code ty_expected in | |
+ let sexp = {sexp with pexp_attributes = attrs} in (* drop bracket attr *) | |
+ let exp = type_expect env sexp sexp_ty_expected in | |
+ re @@ | |
+ texp_braesc battr exp env (instance env ty_expected)) | |
+ | |
+ (* There is nothing special in type-checking CSPs. | |
+ After lifting, a CSP value becomes an ordinaru expression. | |
+ *) | |
+ | _ -> type_expect_orig ?in_function env sexp ty_expected | |
+ (* NNN end *) | |
+ | |
+and type_expect_orig ?in_function env sexp ty_expected = (* NNN *) | |
+ let loc = sexp.pexp_loc in | |
(* Record the expression type before unifying it with the expected type *) | |
let rue exp = | |
unify_exp env (re exp) (instance env ty_expected); | |
@@ -1756,6 +1917,7 @@ | |
let name = Path.name ~paren:Oprint.parenthesized_ident path in | |
Stypes.record (Stypes.An_ident (loc, name, annot)) | |
end; | |
+ let stage = Trx.get_level desc.val_attributes in (* NNN *) | |
rue { | |
exp_desc = | |
begin match desc.val_kind with | |
@@ -1779,9 +1941,13 @@ | |
Env.add_required_global (Path.head p); | |
Texp_ident(path, lid, desc)*) | |
| _ -> | |
+ if stage > !global_stage then (* NNN *) | |
+ raise_wrong_stage_error loc stage !global_stage (* NNN *) | |
+ else (* NNN *) | |
Texp_ident(path, lid, desc) | |
end; | |
exp_loc = loc; exp_extra = []; | |
+(* NNN: Instantiates type scheme to a type *) | |
exp_type = instance env desc.val_type; | |
exp_attributes = sexp.pexp_attributes; | |
exp_env = env } | |
@@ -1875,9 +2041,9 @@ | |
in | |
type_expect ?in_function env sfun ty_expected | |
(* TODO: keep attributes, call type_function directly *) | |
- | Pexp_fun (l, None, spat, sexp) -> | |
+ | Pexp_fun (l, None, spat, sexp_body) -> (* NNN fixing the bug: sexp_body *) | |
type_function ?in_function loc sexp.pexp_attributes env ty_expected | |
- l [{pc_lhs=spat; pc_guard=None; pc_rhs=sexp}] | |
+ l [{pc_lhs=spat; pc_guard=None; pc_rhs=sexp_body}] | |
| Pexp_function caselist -> | |
type_function ?in_function | |
loc sexp.pexp_attributes env ty_expected "" caselist | |
@@ -2195,7 +2361,8 @@ | |
| Ppat_any -> Ident.create "_for", env | |
| Ppat_var {txt} -> | |
Env.enter_value txt {val_type = instance_def Predef.type_int; | |
- val_attributes = []; | |
+ val_attributes = (* NNN *) | |
+ [Trx.attr_level !global_stage]; | |
val_kind = Val_reg; Types.val_loc = loc; } env | |
~check:(fun s -> Warnings.Unused_for_index s) | |
| _ -> | |
@@ -2357,7 +2524,8 @@ | |
Texp_ident(Path.Pident method_id, lid, | |
{val_type = method_type; | |
val_kind = Val_reg; | |
- val_attributes = []; | |
+ val_attributes = (* NNN *) | |
+ [Trx.attr_level !global_stage]; | |
Types.val_loc = Location.none}); | |
exp_loc = loc; exp_extra = []; | |
exp_type = method_type; | |
@@ -2428,6 +2596,8 @@ | |
exp_env = env } | |
end | |
| Pexp_setinstvar (lab, snewval) -> | |
+ if !global_stage != 0 then (* NNN *) | |
+ raise_unsupported loc "setinstvar"; (* NNN *) | |
begin try | |
let (path, desc) = Env.lookup_value (Longident.Lident lab.txt) env in | |
match desc.val_kind with | |
@@ -2452,6 +2622,8 @@ | |
raise(Error(loc, env, Unbound_instance_variable lab.txt)) | |
end | |
| Pexp_override lst -> | |
+ if !global_stage != 0 then (* NNN *) | |
+ raise_unsupported loc "override"; (* NNN *) | |
let _ = | |
List.fold_right | |
(fun (lab, _) l -> | |
@@ -2490,6 +2662,8 @@ | |
assert false | |
end | |
| Pexp_letmodule(name, smodl, sbody) -> | |
+ if !global_stage != 0 then (* NNN *) | |
+ raise_unsupported loc "letmodule"; (* NNN *) | |
let ty = newvar() in | |
(* remember original level *) | |
begin_def (); | |
@@ -3094,7 +3268,7 @@ | |
exp_desc = | |
Texp_ident(Path.Pident id, mknoloc (Longident.Lident name), | |
{val_type = ty; val_kind = Val_reg; | |
- val_attributes = []; | |
+ val_attributes = [Trx.attr_level !global_stage]; (* NNN *) | |
Types.val_loc = Location.none})} | |
in | |
let eta_pat, eta_var = var_pair "eta" ty_arg in | |
@@ -3722,6 +3896,7 @@ | |
let type_binding env rec_flag spat_sexp_list scope = | |
Typetexp.reset_type_variables(); | |
+ global_stage := 0; (* NNN *) | |
let (pat_exp_list, new_env, unpacks) = | |
type_let | |
~check:(fun s -> Warnings.Unused_value_declaration s) | |
@@ -3739,17 +3914,34 @@ | |
let type_expression env sexp = | |
Typetexp.reset_type_variables(); | |
+ global_stage := 0; (* NNN *) | |
begin_def(); | |
let exp = type_exp env sexp in | |
end_def(); | |
if is_nonexpansive exp then generalize exp.exp_type | |
else generalize_expansive env exp.exp_type; | |
+ (* NNN The original code | |
match sexp.pexp_desc with | |
Pexp_ident lid -> | |
(* Special case for keeping type variables when looking-up a variable *) | |
let (path, desc) = Env.lookup_value lid.txt env in | |
{exp with exp_type = desc.val_type} | |
| _ -> exp | |
+ We have to modify it since <x> is also Pexp_ident, with the additional | |
+ attribute though. So, either we have to check for metaocaml.bracket | |
+ attribute, or, better, check exp. After type-checking, <x> is no longer | |
+ Pexp_ident. For ordinary identifiers though, Pexp_ident li maps to | |
+ Texp_ident (..,li,..) -- with the exception of instance vars, which | |
+ don't matter at the ttoplevel anyway. | |
+*) | |
+ (* NNN new code *) | |
+ match exp.exp_desc with | |
+ Texp_ident (_,lid,_) -> | |
+ (* Special case for keeping type variables when looking-up a variable *) | |
+ let (path, desc) = Env.lookup_value lid.txt env in | |
+ {exp with exp_type = desc.val_type} | |
+ | _ -> exp | |
+ (* NNN end *) | |
(* Error report *) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment