From 4e987026148fe65c323afbc93cd560c07bf06b3f Mon Sep 17 00:00:00 2001 From: Yale AI Dept Date: Wed, 14 Jul 1993 13:08:00 -0500 Subject: Import to github. --- Copyright | 18 + README | 37 + ast/README | 29 + ast/ast-td.scm | 20 + ast/ast.scm | 33 + ast/definitions.scm | 209 + ast/exp-structs.scm | 386 ++ ast/modules.scm | 252 ++ ast/predicates.scm | 18 + ast/tc-structs.scm | 62 + ast/type-structs.scm | 159 + ast/valdef-structs.scm | 276 ++ backend/README | 10 + backend/backend.scm | 21 + backend/box.scm | 417 ++ backend/codegen.scm | 600 +++ backend/interface-codegen.scm | 200 + backend/optimize.scm | 1986 +++++++++ backend/strictness.scm | 845 ++++ bin/cmu-clx-haskell | 9 + bin/cmu-haskell | 9 + bin/magic.scm | 10 + cfn/README | 35 + cfn/cfn.scm | 21 + cfn/main.scm | 83 + cfn/misc.scm | 113 + cfn/pattern.scm | 654 +++ cl-support/PORTING | 105 + cl-support/README | 3 + cl-support/cl-definitions.lisp | 1351 ++++++ cl-support/cl-init.lisp | 170 + cl-support/cl-setup.lisp | 30 + cl-support/cl-structs.lisp | 699 +++ cl-support/cl-support.lisp | 86 + cl-support/cl-types.lisp | 90 + cl-support/wcl-patches.lisp | 68 + com/README | 4 + com/akcl/README | 39 + com/akcl/build-prelude | 35 + com/akcl/clean | 4 + com/akcl/compile | 11 + com/akcl/savesys | 46 + com/allegro/README | 40 + com/allegro/build-prelude | 32 + com/allegro/build-xlib | 14 + com/allegro/clean | 5 + com/allegro/compile | 15 + com/allegro/next-patches/patch0149.fasl | Bin 0 -> 2361 bytes com/allegro/next-patches/patch0151.fasl | Bin 0 -> 3027 bytes com/allegro/savesys | 54 + com/allegro/savesys-xlib | 65 + com/allegro/sparc-patches/patch0151.fasl | Bin 0 -> 3519 bytes com/clean | 14 + com/cmu/README | 45 + com/cmu/build-prelude | 32 + com/cmu/build-xlib | 15 + com/cmu/clean | 4 + com/cmu/compile | 12 + com/cmu/savesys | 46 + com/cmu/savesys-xlib | 57 + com/lispworks/README | 43 + com/lispworks/build-prelude | 35 + com/lispworks/build-xlib | 12 + com/lispworks/clean | 5 + com/lispworks/compile | 13 + com/lispworks/patches/safe-fo-closure.wfasl | Bin 0 -> 2394 bytes com/lispworks/savesys | 43 + com/lispworks/savesys-xlib | 52 + com/locked | 14 + com/lookfor | 9 + com/lucid/README | 39 + com/lucid/build-prelude | 36 + com/lucid/build-xlib | 15 + com/lucid/clean | 5 + com/lucid/compile | 13 + com/lucid/savesys | 44 + com/lucid/savesys-xlib | 55 + com/unchecked | 10 + command-interface-help | 33 + command-interface/README | 2 + command-interface/command-interface.scm | 11 + command-interface/command-utils.scm | 208 + command-interface/command.scm | 308 ++ command-interface/incremental-compiler.scm | 168 + csys/README | 3 + csys/cache-structs.scm | 48 + csys/compiler-driver.scm | 640 +++ csys/csys.scm | 25 + csys/dump-cse.scm | 182 + csys/dump-flic.scm | 130 + csys/dump-interface.scm | 800 ++++ csys/dump-macros.scm | 37 + csys/dump-params.scm | 18 + csys/magic.scm | 10 + depend/README | 3 + depend/depend.scm | 13 + depend/dependency-analysis.scm | 151 + derived/README | 2 + derived/ast-builders.scm | 273 ++ derived/derived-instances.scm | 255 ++ derived/derived.scm | 21 + derived/eq-ord.scm | 69 + derived/ix-enum.scm | 116 + derived/text-binary.scm | 228 + doc/announcement | 64 + doc/comparison | 291 ++ doc/lisp-interface/lisp-interface.dvi | Bin 0 -> 23156 bytes doc/manual/haskell.dvi | Bin 0 -> 68832 bytes doc/optimizer/optimizer.dvi | Bin 0 -> 25624 bytes doc/tutorial/tutorial.ps | 6257 +++++++++++++++++++++++++++ doc/xinterface/xman.dvi | Bin 0 -> 13076 bytes emacs-tools/README | 5 + emacs-tools/comint.el | 1524 +++++++ emacs-tools/comint.elc | Bin 0 -> 24467 bytes emacs-tools/haskell.el | 2198 ++++++++++ emacs-tools/haskell.elc | 788 ++++ emacs-tools/optimizer-help.txt | 5 + emacs-tools/printer-help.txt | 24 + flic/README | 2 + flic/ast-to-flic.scm | 277 ++ flic/copy-flic.scm | 146 + flic/flic-structs.scm | 89 + flic/flic-td.scm | 21 + flic/flic-walker.scm | 21 + flic/flic.scm | 29 + flic/invariant.scm | 88 + flic/print-flic.scm | 130 + haskell-development | 69 + haskell-setup | 27 + import-export/README | 15 + import-export/ie-errors.scm | 154 + import-export/ie-utils.scm | 121 + import-export/ie.scm | 16 + import-export/import-export.scm | 209 + import-export/init-modules.scm | 142 + import-export/locate-entity.scm | 126 + import-export/top-definitions.scm | 98 + parser/README | 1 + parser/annotation-parser.scm | 184 + parser/decl-parser.scm | 175 + parser/exp-parser.scm | 230 + parser/interface-parser.scm | 98 + parser/lexer.scm | 651 +++ parser/module-parser.scm | 312 ++ parser/parser-debugger.scm | 81 + parser/parser-driver.scm | 48 + parser/parser-errors.scm | 74 + parser/parser-globals.scm | 27 + parser/parser-macros.scm | 327 ++ parser/parser.scm | 54 + parser/pattern-parser.scm | 220 + parser/token.scm | 364 ++ parser/type-parser.scm | 116 + parser/typedecl-parser.scm | 163 + prec/README | 2 + prec/prec-parse.scm | 253 ++ prec/prec.scm | 18 + prec/scope.scm | 367 ++ printers/README | 19 + printers/print-exps.scm | 410 ++ printers/print-modules.scm | 125 + printers/print-ntypes.scm | 61 + printers/print-types.scm | 201 + printers/print-valdefs.scm | 180 + printers/printers.scm | 28 + printers/util.scm | 214 + progs/README | 9 + progs/demo/Calendar.hs | 138 + progs/demo/README | 15 + progs/demo/X11/animation/README | 22 + progs/demo/X11/animation/animation.hs | 16 + progs/demo/X11/animation/animation.hu | 6 + progs/demo/X11/animation/birds.hs | 28 + progs/demo/X11/animation/birds.hu | 3 + progs/demo/X11/animation/doc.tex | 578 +++ progs/demo/X11/animation/palm.hs | 47 + progs/demo/X11/animation/palm.hu | 3 + progs/demo/X11/animation/planets.hs | 30 + progs/demo/X11/animation/planets.hu | 3 + progs/demo/X11/animation/r_behaviour.hs | 158 + progs/demo/X11/animation/r_behaviour.hu | 3 + progs/demo/X11/animation/r_constants.hs | 129 + progs/demo/X11/animation/r_constants.hu | 3 + progs/demo/X11/animation/r_curve.hs | 60 + progs/demo/X11/animation/r_curve.hu | 3 + progs/demo/X11/animation/r_defaults.hs | 76 + progs/demo/X11/animation/r_defaults.hu | 3 + progs/demo/X11/animation/r_display.hs | 114 + progs/demo/X11/animation/r_display.hu | 6 + progs/demo/X11/animation/r_inbetween.hs | 82 + progs/demo/X11/animation/r_inbetween.hu | 3 + progs/demo/X11/animation/r_movie.hs | 114 + progs/demo/X11/animation/r_movie.hu | 3 + progs/demo/X11/animation/r_picture.hs | 188 + progs/demo/X11/animation/r_picture.hu | 4 + progs/demo/X11/animation/r_ptypes.hs | 67 + progs/demo/X11/animation/r_ptypes.hu | 2 + progs/demo/X11/animation/r_shapes.hs | 38 + progs/demo/X11/animation/r_shapes.hu | 3 + progs/demo/X11/animation/r_utility.hs | 150 + progs/demo/X11/animation/r_utility.hu | 3 + progs/demo/X11/animation/seafigs.hs | 158 + progs/demo/X11/animation/seafigs.hu | 3 + progs/demo/X11/animation/seaside.hs | 25 + progs/demo/X11/animation/seaside.hu | 5 + progs/demo/X11/draw/README | 1 + progs/demo/X11/draw/draw.hs | 41 + progs/demo/X11/draw/draw.hu | 2 + progs/demo/X11/gobang/README | 66 + progs/demo/X11/gobang/gobang.hs | 364 ++ progs/demo/X11/gobang/gobang.hu | 7 + progs/demo/X11/gobang/misc.hi | 7 + progs/demo/X11/gobang/misc.hu | 2 + progs/demo/X11/gobang/redraw.hs | 160 + progs/demo/X11/gobang/redraw.hu | 4 + progs/demo/X11/gobang/utilities.hs | 305 ++ progs/demo/X11/gobang/utilities.hu | 6 + progs/demo/X11/gobang/weights.hs | 323 ++ progs/demo/X11/gobang/weights.hu | 4 + progs/demo/X11/graphics/README | 31 + progs/demo/X11/graphics/henderson.hs | 465 ++ progs/demo/X11/graphics/henderson.hu | 3 + progs/demo/X11/graphics/manual | 454 ++ progs/demo/X11/graphics/p.pic | 1 + progs/demo/X11/graphics/q.pic | 2 + progs/demo/X11/graphics/r.pic | 2 + progs/demo/X11/graphics/s.pic | 1 + progs/demo/X11/graphics/sqrlmt.hs | 177 + progs/demo/X11/graphics/sqrlmt.hu | 3 + progs/demo/X11/graphics/stop.pic | 1 + progs/demo/X11/graphics/strange.pic | 2 + progs/demo/X11/graphics/text.pic | 1 + progs/demo/X11/logo/EXAMPLES.LOGO | 70 + progs/demo/X11/logo/README | 104 + progs/demo/X11/logo/logo.hs | 1345 ++++++ progs/demo/X11/logo/logo.hu | 3 + progs/demo/X11/mdraw/README | 1 + progs/demo/X11/mdraw/mdraw.hs | 83 + progs/demo/X11/mdraw/mdraw.hu | 3 + progs/demo/X11/mdraw/t.hs | 16 + progs/demo/X11/mdraw/t.hu | 3 + progs/demo/add.hs | 21 + progs/demo/eliza.hs | 267 ++ progs/demo/fact.hs | 14 + progs/demo/improved-add.hs | 21 + progs/demo/merge.hs | 26 + progs/demo/pascal.hs | 24 + progs/demo/pfac.hs | 21 + progs/demo/primes.hs | 16 + progs/demo/prolog/Engine.hs | 61 + progs/demo/prolog/Engine.hu | 3 + progs/demo/prolog/Interact.hs | 76 + progs/demo/prolog/Interact.hu | 2 + progs/demo/prolog/Main.hs | 87 + progs/demo/prolog/Main.hu | 6 + progs/demo/prolog/Parse.hs | 116 + progs/demo/prolog/Parse.hu | 1 + progs/demo/prolog/PrologData.hs | 121 + progs/demo/prolog/PrologData.hu | 2 + progs/demo/prolog/README | 3 + progs/demo/prolog/Subst.hs | 65 + progs/demo/prolog/Subst.hu | 2 + progs/demo/prolog/Version.hs | 1 + progs/demo/prolog/Version.hu | 1 + progs/demo/prolog/stdlib | 38 + progs/demo/queens.hs | 40 + progs/demo/quicksort.hs | 13 + progs/lib/README | 1 + progs/lib/X11/README | 11 + progs/lib/X11/clx-patch.lisp | 39 + progs/lib/X11/xlib.hs | 877 ++++ progs/lib/X11/xlib.hu | 5 + progs/lib/X11/xlibclx.scm | 1262 ++++++ progs/lib/X11/xlibprims.hi | 1465 +++++++ progs/lib/X11/xlibprims.hu | 5 + progs/lib/cl/README | 2 + progs/lib/cl/logop-prims.hi | 78 + progs/lib/cl/logop-prims.scm | 81 + progs/lib/cl/logop.hs | 63 + progs/lib/cl/logop.hu | 5 + progs/lib/cl/maybe.hs | 12 + progs/lib/cl/maybe.hu | 3 + progs/lib/cl/random-prims.hi | 20 + progs/lib/cl/random.hs | 21 + progs/lib/cl/random.hu | 4 + progs/lib/hbc/Either.hs | 2 + progs/lib/hbc/Either.hu | 3 + progs/lib/hbc/Hash.hs | 79 + progs/lib/hbc/Hash.hu | 3 + progs/lib/hbc/ListUtil.hs | 48 + progs/lib/hbc/ListUtil.hu | 4 + progs/lib/hbc/Maybe.hs | 6 + progs/lib/hbc/Maybe.hu | 3 + progs/lib/hbc/Miranda.hs | 90 + progs/lib/hbc/Miranda.hu | 4 + progs/lib/hbc/Option.hs | 3 + progs/lib/hbc/Option.hu | 3 + progs/lib/hbc/Pretty.hs | 50 + progs/lib/hbc/Printf.hs | 150 + progs/lib/hbc/Printf.hu | 3 + progs/lib/hbc/QSort.hs | 47 + progs/lib/hbc/QSort.hu | 3 + progs/lib/hbc/README | 97 + progs/lib/hbc/Random.hs | 52 + progs/lib/hbc/Random.hu | 3 + progs/lib/hbc/Time.hs | 51 + progs/lib/hbc/Time.hu | 3 + progs/prelude/Prelude.hs | 187 + progs/prelude/Prelude.hu | 16 + progs/prelude/PreludeArray.hs | 201 + progs/prelude/PreludeArrayPrims.hi | 37 + progs/prelude/PreludeArrayPrims.hu | 4 + progs/prelude/PreludeComplex.hs | 94 + progs/prelude/PreludeCore.hs | 817 ++++ progs/prelude/PreludeIO.hs | 232 + progs/prelude/PreludeIOMonad.hs | 60 + progs/prelude/PreludeIOPrims.hi | 55 + progs/prelude/PreludeIOPrims.hu | 4 + progs/prelude/PreludeList.hs | 585 +++ progs/prelude/PreludeLocal.hs | 16 + progs/prelude/PreludeLocalIO.hs | 144 + progs/prelude/PreludePrims.hi | 252 ++ progs/prelude/PreludePrims.hu | 4 + progs/prelude/PreludeRatio.hs | 98 + progs/prelude/PreludeText.hs | 260 ++ progs/prelude/PreludeTuple.hs | 213 + progs/prelude/PreludeTuplePrims.hi | 48 + progs/prelude/PreludeTuplePrims.hu | 4 + progs/prelude/README | 12 + progs/tutorial/README | 12 + progs/tutorial/tutorial.hs | 2143 +++++++++ runtime/README | 8 + runtime/array-prims.scm | 55 + runtime/debug-utils.scm | 33 + runtime/io-primitives.scm | 178 + runtime/prims.scm | 595 +++ runtime/runtime-utils.scm | 384 ++ runtime/runtime.scm | 26 + runtime/tuple-prims.scm | 86 + support/README | 4 + support/compile.scm | 447 ++ support/format.scm | 683 +++ support/mumble.txt | 840 ++++ support/pprint.scm | 1788 ++++++++ support/support.scm | 35 + support/system.scm | 51 + support/utils.scm | 408 ++ tdecl/README | 2 + tdecl/alg-syn.scm | 228 + tdecl/class.scm | 258 ++ tdecl/instance.scm | 296 ++ tdecl/tdecl-utils.scm | 16 + tdecl/tdecl.scm | 18 + tdecl/type-declaration-analysis.scm | 72 + top/README | 12 + top/core-definitions.scm | 149 + top/core-init.scm | 14 + top/core-symbols.scm | 126 + top/errors.scm | 119 + top/globals.scm | 75 + top/has-macros.scm | 57 + top/has-utils.scm | 21 + top/phases.scm | 226 + top/prelude-core-syms.scm | 57 + top/symbol-table.scm | 412 ++ top/system-init.scm | 41 + top/top.scm | 46 + top/tuple.scm | 87 + type/README | 1 + type/default.scm | 47 + type/dictionary.scm | 229 + type/expression-typechecking.scm | 364 ++ type/pattern-binding.scm | 38 + type/type-decl.scm | 337 ++ type/type-error-handlers.scm | 40 + type/type-macros.scm | 159 + type/type-main.scm | 56 + type/type-vars.scm | 60 + type/type.scm | 32 + type/unify.scm | 154 + util/README | 2 + util/annotation-utils.scm | 41 + util/constructors.scm | 339 ++ util/haskell-utils.scm | 22 + util/instance-manager.scm | 161 + util/pattern-vars.scm | 40 + util/prec-utils.scm | 115 + util/signature.scm | 90 + util/type-utils.scm | 308 ++ util/walk-ast.scm | 156 + 390 files changed, 60154 insertions(+) create mode 100644 Copyright create mode 100644 README create mode 100644 ast/README create mode 100644 ast/ast-td.scm create mode 100644 ast/ast.scm create mode 100644 ast/definitions.scm create mode 100644 ast/exp-structs.scm create mode 100644 ast/modules.scm create mode 100644 ast/predicates.scm create mode 100644 ast/tc-structs.scm create mode 100644 ast/type-structs.scm create mode 100644 ast/valdef-structs.scm create mode 100644 backend/README create mode 100644 backend/backend.scm create mode 100644 backend/box.scm create mode 100644 backend/codegen.scm create mode 100644 backend/interface-codegen.scm create mode 100644 backend/optimize.scm create mode 100644 backend/strictness.scm create mode 100755 bin/cmu-clx-haskell create mode 100755 bin/cmu-haskell create mode 100644 bin/magic.scm create mode 100644 cfn/README create mode 100644 cfn/cfn.scm create mode 100644 cfn/main.scm create mode 100644 cfn/misc.scm create mode 100644 cfn/pattern.scm create mode 100644 cl-support/PORTING create mode 100644 cl-support/README create mode 100644 cl-support/cl-definitions.lisp create mode 100644 cl-support/cl-init.lisp create mode 100644 cl-support/cl-setup.lisp create mode 100644 cl-support/cl-structs.lisp create mode 100644 cl-support/cl-support.lisp create mode 100644 cl-support/cl-types.lisp create mode 100644 cl-support/wcl-patches.lisp create mode 100644 com/README create mode 100644 com/akcl/README create mode 100755 com/akcl/build-prelude create mode 100755 com/akcl/clean create mode 100755 com/akcl/compile create mode 100755 com/akcl/savesys create mode 100644 com/allegro/README create mode 100755 com/allegro/build-prelude create mode 100755 com/allegro/build-xlib create mode 100755 com/allegro/clean create mode 100755 com/allegro/compile create mode 100644 com/allegro/next-patches/patch0149.fasl create mode 100644 com/allegro/next-patches/patch0151.fasl create mode 100755 com/allegro/savesys create mode 100755 com/allegro/savesys-xlib create mode 100644 com/allegro/sparc-patches/patch0151.fasl create mode 100755 com/clean create mode 100644 com/cmu/README create mode 100755 com/cmu/build-prelude create mode 100755 com/cmu/build-xlib create mode 100755 com/cmu/clean create mode 100755 com/cmu/compile create mode 100755 com/cmu/savesys create mode 100755 com/cmu/savesys-xlib create mode 100644 com/lispworks/README create mode 100755 com/lispworks/build-prelude create mode 100755 com/lispworks/build-xlib create mode 100755 com/lispworks/clean create mode 100755 com/lispworks/compile create mode 100644 com/lispworks/patches/safe-fo-closure.wfasl create mode 100755 com/lispworks/savesys create mode 100755 com/lispworks/savesys-xlib create mode 100755 com/locked create mode 100755 com/lookfor create mode 100644 com/lucid/README create mode 100755 com/lucid/build-prelude create mode 100755 com/lucid/build-xlib create mode 100755 com/lucid/clean create mode 100755 com/lucid/compile create mode 100755 com/lucid/savesys create mode 100755 com/lucid/savesys-xlib create mode 100755 com/unchecked create mode 100644 command-interface-help create mode 100644 command-interface/README create mode 100644 command-interface/command-interface.scm create mode 100644 command-interface/command-utils.scm create mode 100644 command-interface/command.scm create mode 100644 command-interface/incremental-compiler.scm create mode 100644 csys/README create mode 100644 csys/cache-structs.scm create mode 100644 csys/compiler-driver.scm create mode 100644 csys/csys.scm create mode 100644 csys/dump-cse.scm create mode 100644 csys/dump-flic.scm create mode 100644 csys/dump-interface.scm create mode 100644 csys/dump-macros.scm create mode 100644 csys/dump-params.scm create mode 100644 csys/magic.scm create mode 100644 depend/README create mode 100644 depend/depend.scm create mode 100644 depend/dependency-analysis.scm create mode 100644 derived/README create mode 100644 derived/ast-builders.scm create mode 100644 derived/derived-instances.scm create mode 100644 derived/derived.scm create mode 100644 derived/eq-ord.scm create mode 100644 derived/ix-enum.scm create mode 100644 derived/text-binary.scm create mode 100644 doc/announcement create mode 100644 doc/comparison create mode 100644 doc/lisp-interface/lisp-interface.dvi create mode 100644 doc/manual/haskell.dvi create mode 100644 doc/optimizer/optimizer.dvi create mode 100644 doc/tutorial/tutorial.ps create mode 100644 doc/xinterface/xman.dvi create mode 100644 emacs-tools/README create mode 100644 emacs-tools/comint.el create mode 100644 emacs-tools/comint.elc create mode 100644 emacs-tools/haskell.el create mode 100644 emacs-tools/haskell.elc create mode 100644 emacs-tools/optimizer-help.txt create mode 100644 emacs-tools/printer-help.txt create mode 100644 flic/README create mode 100644 flic/ast-to-flic.scm create mode 100644 flic/copy-flic.scm create mode 100644 flic/flic-structs.scm create mode 100644 flic/flic-td.scm create mode 100644 flic/flic-walker.scm create mode 100644 flic/flic.scm create mode 100644 flic/invariant.scm create mode 100644 flic/print-flic.scm create mode 100755 haskell-development create mode 100755 haskell-setup create mode 100644 import-export/README create mode 100644 import-export/ie-errors.scm create mode 100644 import-export/ie-utils.scm create mode 100644 import-export/ie.scm create mode 100644 import-export/import-export.scm create mode 100644 import-export/init-modules.scm create mode 100644 import-export/locate-entity.scm create mode 100644 import-export/top-definitions.scm create mode 100644 parser/README create mode 100644 parser/annotation-parser.scm create mode 100644 parser/decl-parser.scm create mode 100644 parser/exp-parser.scm create mode 100644 parser/interface-parser.scm create mode 100644 parser/lexer.scm create mode 100644 parser/module-parser.scm create mode 100644 parser/parser-debugger.scm create mode 100644 parser/parser-driver.scm create mode 100644 parser/parser-errors.scm create mode 100644 parser/parser-globals.scm create mode 100644 parser/parser-macros.scm create mode 100644 parser/parser.scm create mode 100644 parser/pattern-parser.scm create mode 100644 parser/token.scm create mode 100644 parser/type-parser.scm create mode 100644 parser/typedecl-parser.scm create mode 100644 prec/README create mode 100644 prec/prec-parse.scm create mode 100644 prec/prec.scm create mode 100644 prec/scope.scm create mode 100644 printers/README create mode 100644 printers/print-exps.scm create mode 100644 printers/print-modules.scm create mode 100644 printers/print-ntypes.scm create mode 100644 printers/print-types.scm create mode 100644 printers/print-valdefs.scm create mode 100644 printers/printers.scm create mode 100644 printers/util.scm create mode 100644 progs/README create mode 100644 progs/demo/Calendar.hs create mode 100644 progs/demo/README create mode 100644 progs/demo/X11/animation/README create mode 100644 progs/demo/X11/animation/animation.hs create mode 100644 progs/demo/X11/animation/animation.hu create mode 100644 progs/demo/X11/animation/birds.hs create mode 100644 progs/demo/X11/animation/birds.hu create mode 100644 progs/demo/X11/animation/doc.tex create mode 100644 progs/demo/X11/animation/palm.hs create mode 100644 progs/demo/X11/animation/palm.hu create mode 100644 progs/demo/X11/animation/planets.hs create mode 100644 progs/demo/X11/animation/planets.hu create mode 100644 progs/demo/X11/animation/r_behaviour.hs create mode 100644 progs/demo/X11/animation/r_behaviour.hu create mode 100644 progs/demo/X11/animation/r_constants.hs create mode 100644 progs/demo/X11/animation/r_constants.hu create mode 100644 progs/demo/X11/animation/r_curve.hs create mode 100644 progs/demo/X11/animation/r_curve.hu create mode 100644 progs/demo/X11/animation/r_defaults.hs create mode 100644 progs/demo/X11/animation/r_defaults.hu create mode 100644 progs/demo/X11/animation/r_display.hs create mode 100644 progs/demo/X11/animation/r_display.hu create mode 100644 progs/demo/X11/animation/r_inbetween.hs create mode 100644 progs/demo/X11/animation/r_inbetween.hu create mode 100644 progs/demo/X11/animation/r_movie.hs create mode 100644 progs/demo/X11/animation/r_movie.hu create mode 100644 progs/demo/X11/animation/r_picture.hs create mode 100644 progs/demo/X11/animation/r_picture.hu create mode 100644 progs/demo/X11/animation/r_ptypes.hs create mode 100644 progs/demo/X11/animation/r_ptypes.hu create mode 100644 progs/demo/X11/animation/r_shapes.hs create mode 100644 progs/demo/X11/animation/r_shapes.hu create mode 100644 progs/demo/X11/animation/r_utility.hs create mode 100644 progs/demo/X11/animation/r_utility.hu create mode 100644 progs/demo/X11/animation/seafigs.hs create mode 100644 progs/demo/X11/animation/seafigs.hu create mode 100644 progs/demo/X11/animation/seaside.hs create mode 100644 progs/demo/X11/animation/seaside.hu create mode 100644 progs/demo/X11/draw/README create mode 100644 progs/demo/X11/draw/draw.hs create mode 100644 progs/demo/X11/draw/draw.hu create mode 100644 progs/demo/X11/gobang/README create mode 100644 progs/demo/X11/gobang/gobang.hs create mode 100644 progs/demo/X11/gobang/gobang.hu create mode 100644 progs/demo/X11/gobang/misc.hi create mode 100644 progs/demo/X11/gobang/misc.hu create mode 100644 progs/demo/X11/gobang/redraw.hs create mode 100644 progs/demo/X11/gobang/redraw.hu create mode 100644 progs/demo/X11/gobang/utilities.hs create mode 100644 progs/demo/X11/gobang/utilities.hu create mode 100644 progs/demo/X11/gobang/weights.hs create mode 100644 progs/demo/X11/gobang/weights.hu create mode 100644 progs/demo/X11/graphics/README create mode 100644 progs/demo/X11/graphics/henderson.hs create mode 100644 progs/demo/X11/graphics/henderson.hu create mode 100644 progs/demo/X11/graphics/manual create mode 100644 progs/demo/X11/graphics/p.pic create mode 100644 progs/demo/X11/graphics/q.pic create mode 100644 progs/demo/X11/graphics/r.pic create mode 100644 progs/demo/X11/graphics/s.pic create mode 100644 progs/demo/X11/graphics/sqrlmt.hs create mode 100644 progs/demo/X11/graphics/sqrlmt.hu create mode 100644 progs/demo/X11/graphics/stop.pic create mode 100644 progs/demo/X11/graphics/strange.pic create mode 100644 progs/demo/X11/graphics/text.pic create mode 100644 progs/demo/X11/logo/EXAMPLES.LOGO create mode 100644 progs/demo/X11/logo/README create mode 100644 progs/demo/X11/logo/logo.hs create mode 100644 progs/demo/X11/logo/logo.hu create mode 100644 progs/demo/X11/mdraw/README create mode 100644 progs/demo/X11/mdraw/mdraw.hs create mode 100644 progs/demo/X11/mdraw/mdraw.hu create mode 100644 progs/demo/X11/mdraw/t.hs create mode 100644 progs/demo/X11/mdraw/t.hu create mode 100644 progs/demo/add.hs create mode 100644 progs/demo/eliza.hs create mode 100755 progs/demo/fact.hs create mode 100644 progs/demo/improved-add.hs create mode 100755 progs/demo/merge.hs create mode 100644 progs/demo/pascal.hs create mode 100644 progs/demo/pfac.hs create mode 100755 progs/demo/primes.hs create mode 100644 progs/demo/prolog/Engine.hs create mode 100644 progs/demo/prolog/Engine.hu create mode 100644 progs/demo/prolog/Interact.hs create mode 100644 progs/demo/prolog/Interact.hu create mode 100644 progs/demo/prolog/Main.hs create mode 100644 progs/demo/prolog/Main.hu create mode 100644 progs/demo/prolog/Parse.hs create mode 100644 progs/demo/prolog/Parse.hu create mode 100644 progs/demo/prolog/PrologData.hs create mode 100644 progs/demo/prolog/PrologData.hu create mode 100644 progs/demo/prolog/README create mode 100644 progs/demo/prolog/Subst.hs create mode 100644 progs/demo/prolog/Subst.hu create mode 100644 progs/demo/prolog/Version.hs create mode 100644 progs/demo/prolog/Version.hu create mode 100644 progs/demo/prolog/stdlib create mode 100755 progs/demo/queens.hs create mode 100644 progs/demo/quicksort.hs create mode 100644 progs/lib/README create mode 100644 progs/lib/X11/README create mode 100644 progs/lib/X11/clx-patch.lisp create mode 100644 progs/lib/X11/xlib.hs create mode 100644 progs/lib/X11/xlib.hu create mode 100644 progs/lib/X11/xlibclx.scm create mode 100644 progs/lib/X11/xlibprims.hi create mode 100644 progs/lib/X11/xlibprims.hu create mode 100644 progs/lib/cl/README create mode 100644 progs/lib/cl/logop-prims.hi create mode 100644 progs/lib/cl/logop-prims.scm create mode 100644 progs/lib/cl/logop.hs create mode 100644 progs/lib/cl/logop.hu create mode 100644 progs/lib/cl/maybe.hs create mode 100644 progs/lib/cl/maybe.hu create mode 100644 progs/lib/cl/random-prims.hi create mode 100644 progs/lib/cl/random.hs create mode 100644 progs/lib/cl/random.hu create mode 100644 progs/lib/hbc/Either.hs create mode 100644 progs/lib/hbc/Either.hu create mode 100644 progs/lib/hbc/Hash.hs create mode 100644 progs/lib/hbc/Hash.hu create mode 100644 progs/lib/hbc/ListUtil.hs create mode 100644 progs/lib/hbc/ListUtil.hu create mode 100644 progs/lib/hbc/Maybe.hs create mode 100644 progs/lib/hbc/Maybe.hu create mode 100644 progs/lib/hbc/Miranda.hs create mode 100644 progs/lib/hbc/Miranda.hu create mode 100644 progs/lib/hbc/Option.hs create mode 100644 progs/lib/hbc/Option.hu create mode 100644 progs/lib/hbc/Pretty.hs create mode 100644 progs/lib/hbc/Printf.hs create mode 100644 progs/lib/hbc/Printf.hu create mode 100644 progs/lib/hbc/QSort.hs create mode 100644 progs/lib/hbc/QSort.hu create mode 100644 progs/lib/hbc/README create mode 100644 progs/lib/hbc/Random.hs create mode 100644 progs/lib/hbc/Random.hu create mode 100644 progs/lib/hbc/Time.hs create mode 100644 progs/lib/hbc/Time.hu create mode 100644 progs/prelude/Prelude.hs create mode 100644 progs/prelude/Prelude.hu create mode 100644 progs/prelude/PreludeArray.hs create mode 100644 progs/prelude/PreludeArrayPrims.hi create mode 100644 progs/prelude/PreludeArrayPrims.hu create mode 100644 progs/prelude/PreludeComplex.hs create mode 100644 progs/prelude/PreludeCore.hs create mode 100644 progs/prelude/PreludeIO.hs create mode 100644 progs/prelude/PreludeIOMonad.hs create mode 100644 progs/prelude/PreludeIOPrims.hi create mode 100644 progs/prelude/PreludeIOPrims.hu create mode 100644 progs/prelude/PreludeList.hs create mode 100644 progs/prelude/PreludeLocal.hs create mode 100644 progs/prelude/PreludeLocalIO.hs create mode 100644 progs/prelude/PreludePrims.hi create mode 100644 progs/prelude/PreludePrims.hu create mode 100644 progs/prelude/PreludeRatio.hs create mode 100644 progs/prelude/PreludeText.hs create mode 100644 progs/prelude/PreludeTuple.hs create mode 100644 progs/prelude/PreludeTuplePrims.hi create mode 100644 progs/prelude/PreludeTuplePrims.hu create mode 100644 progs/prelude/README create mode 100644 progs/tutorial/README create mode 100644 progs/tutorial/tutorial.hs create mode 100644 runtime/README create mode 100644 runtime/array-prims.scm create mode 100644 runtime/debug-utils.scm create mode 100644 runtime/io-primitives.scm create mode 100644 runtime/prims.scm create mode 100644 runtime/runtime-utils.scm create mode 100644 runtime/runtime.scm create mode 100644 runtime/tuple-prims.scm create mode 100644 support/README create mode 100644 support/compile.scm create mode 100644 support/format.scm create mode 100644 support/mumble.txt create mode 100644 support/pprint.scm create mode 100644 support/support.scm create mode 100644 support/system.scm create mode 100644 support/utils.scm create mode 100644 tdecl/README create mode 100644 tdecl/alg-syn.scm create mode 100644 tdecl/class.scm create mode 100644 tdecl/instance.scm create mode 100644 tdecl/tdecl-utils.scm create mode 100644 tdecl/tdecl.scm create mode 100644 tdecl/type-declaration-analysis.scm create mode 100644 top/README create mode 100644 top/core-definitions.scm create mode 100644 top/core-init.scm create mode 100644 top/core-symbols.scm create mode 100644 top/errors.scm create mode 100644 top/globals.scm create mode 100644 top/has-macros.scm create mode 100644 top/has-utils.scm create mode 100644 top/phases.scm create mode 100644 top/prelude-core-syms.scm create mode 100644 top/symbol-table.scm create mode 100644 top/system-init.scm create mode 100644 top/top.scm create mode 100644 top/tuple.scm create mode 100644 type/README create mode 100644 type/default.scm create mode 100644 type/dictionary.scm create mode 100644 type/expression-typechecking.scm create mode 100644 type/pattern-binding.scm create mode 100644 type/type-decl.scm create mode 100644 type/type-error-handlers.scm create mode 100644 type/type-macros.scm create mode 100644 type/type-main.scm create mode 100644 type/type-vars.scm create mode 100644 type/type.scm create mode 100644 type/unify.scm create mode 100644 util/README create mode 100644 util/annotation-utils.scm create mode 100644 util/constructors.scm create mode 100644 util/haskell-utils.scm create mode 100644 util/instance-manager.scm create mode 100644 util/pattern-vars.scm create mode 100644 util/prec-utils.scm create mode 100644 util/signature.scm create mode 100644 util/type-utils.scm create mode 100644 util/walk-ast.scm diff --git a/Copyright b/Copyright new file mode 100644 index 0000000..cb40c96 --- /dev/null +++ b/Copyright @@ -0,0 +1,18 @@ +Copyright (c) 1991 Yale University Computer Science Department + +Yale Haskell System Version 2.0-beta + +Permission to copy this software, to redistribute it, and to use it for any +purpose is granted, subject to the following restrictions and understandings. + +1. Any copy made of this software must include this copyright notice in full. +2. All materials developed as a consequence of the use of this software + shall duly acknowledge such use, in accordance with the usual standards + of acknowledging credit in academic research. +3. Yale has made no warrantee or representation that the operation of + this software will be error-free, and Yale is under no obligation to + provide any services, by way of maintenance, update, or otherwise. +4. In conjunction with products arising from the use of this material, + there shall be no use of the name of the Yale University nor of any + adaptation thereof in any advertising, promotional, or sales literature + without prior written consent from Yale in each case. diff --git a/README b/README new file mode 100644 index 0000000..b3afb11 --- /dev/null +++ b/README @@ -0,0 +1,37 @@ +This is the main directory for the 2.x release of Yale Haskell. This +file contains some basic information about how the system is organized +and put together. + +You should set the variable $HASKELL and source the haskell-setup +script in this directory before attempting to use Yale Haskell. + +If you are rebuilding from the source release, see the scripts and +README files in the $HASKELL/com area. You also need to modify +the haskell-development script. + +Yale-specific information: + +Source files in this directory area are under RCS control. Use the +`rci' and `rco' aliases (from haskell-development) to check things in +and out. By convention, each directory containing source files should +have subdirectories named RCS (for RCS files), t (for compiled T +files), lucid (for compiled Lucid CL files), and cmu (for compiled CMU +CL files). + +Each subdirectory containing source files should also have a file that +defines a compilation unit for that subdirectory. (See +support/compile.scm for information about the compilation unit +utility.) support/system.scm loads all the compilation unit definitions. + +To load the system into Common Lisp, you need to load the file +support/cl-support/cl-init.lisp. This will automagically compile any +outdated or missing files. However, you need to type in an +(in-package "MUMBLE-USER") once it finishes. + +Don't try to load the system into T. It's broken! See +support/t-support/t-init.t. + +All system-dependent code goes in either support/cl-support or +support/t-support. For information about the mumble compatibility +package used as the implementation language for the rest of the +system, see support/mumble.txt. diff --git a/ast/README b/ast/README new file mode 100644 index 0000000..ed2497d --- /dev/null +++ b/ast/README @@ -0,0 +1,29 @@ +This directory defines the primary data structures used in the compiler +using the `define-struct' macro defined in the struct directory. + +Structures are divided into the following catagories: + +Basic structures: (basic-structs) + References to variables, data constructors, classes, type constructors + All references contain the name of the object referred to and a + field that will receive the actual definition object when scoping + has been resolved. + Fixity: (l | n | r, Int) + +Module structures: (module-structs) + The module ast, import & export related ast's, and fixity definition. + +Type system structures: (type-structs) + The representation of data types and the type related declarations: + type, data, class, and instance. + +Value declarations: (valdef-structs) + +Expressions: (expr-structs) + +Definitions: (definition-structs) + +Flic structures: (flic-structs) + + + diff --git a/ast/ast-td.scm b/ast/ast-td.scm new file mode 100644 index 0000000..cf70016 --- /dev/null +++ b/ast/ast-td.scm @@ -0,0 +1,20 @@ +;;; ast-td.scm -- define ast type descriptor object +;;; +;;; author : Sandra Loosemore +;;; date : 6 Oct 1992 +;;; + + +;;; Give the type descriptors for AST nodes extra slots to hold walker +;;; functions. + +(define-struct ast-td + (include type-descriptor) + (slots + (cfn-walker (type (maybe procedure)) (default '#f)) + (cfn-simple-transform-walker (type (maybe procedure)) (default '#f)) + (depend-walker (type (maybe procedure)) (default '#f)) + (ast-to-flic-walker (type (maybe procedure)) (default '#f)) + (scope-walker (type (maybe procedure)) (default '#f)) + (type-walker (type (maybe procedure)) (default '#f)) + (collect-pattern-vars-walker (type (maybe procedure)) (default '#f)))) diff --git a/ast/ast.scm b/ast/ast.scm new file mode 100644 index 0000000..9169677 --- /dev/null +++ b/ast/ast.scm @@ -0,0 +1,33 @@ +;;; ast.scm -- compilation unit definition for ast definitions +;;; +;;; author : John +;;; date : 10 Dec 1991 +;;; + + +(define-compilation-unit ast + (source-filename "$Y2/ast/") + (unit ast-td + (source-filename "ast-td")) + (unit modules + (source-filename "modules.scm") + (require ast-td)) + (unit type-structs + (source-filename "type-structs.scm") + (require ast-td modules)) + (unit tc-structs + (source-filename "tc-structs.scm") + (require ast-td modules)) + (unit valdef-structs + (source-filename "valdef-structs.scm") + (require ast-td modules)) + (unit definitions + (source-filename "definitions.scm") + (require ast-td modules)) + (unit exp-structs + (source-filename "exp-structs.scm") + (require ast-td modules)) + (unit predicates + (require ast-td modules type-structs valdef-structs definitions + exp-structs tc-structs) + (source-filename "predicates.scm"))) diff --git a/ast/definitions.scm b/ast/definitions.scm new file mode 100644 index 0000000..9184b13 --- /dev/null +++ b/ast/definitions.scm @@ -0,0 +1,209 @@ +;;; File: ast/definitions.scm Author: John + +;;; this file contains definitions for the named entities in the +;;; system. These are used in both the front and back ends of the +;;; compiler. These are created early in the compilation process +;;; (import/export) and filled in during compilation. Binary interface +;;; files are just tables mapping names to definitions. + +;;; All definitions have these fields for managing name spaces. All +;;; names are uniquified; this requires adding `;' to the front of data +;;; constructors to separate them from type constructors. Module names +;;; do not have a `definition' data structure - the `module' structure +;;; serves the same purpose. + +;;; Definitions are found in two places: the symbol tables which are part of +;;; the module structure and the -ref nodes in the ast structure. The -ref +;;; nodes have two fields: a name (from the parser) and a field which will +;;; point to the associated definition after name resolution. Name resolution +;;; happens in a number of different places: top level definitions are +;;; resolved during import-export, type declarations are resolved during +;;; type declaration analysis, and everything else is resolved during scoping +;;; (alpha conversion). The parser generates pre-resolved -ref nodes when +;;; parsing some constructs. These refs denote pre-defined language +;;; constructs, such as lists, tuples, or prelude functions. + +;;; A special set of definitions constitutes the `core' of Haskell. These +;;; definitions are pre-allocated and are filled in during the compilation +;;; of the Prelude. This allows the bootstrap of the system. + + +;;; All defs require name, unit, and module args to make. +;;; Other slots should all have appropriate defaults. + +(define-struct def + (slots + ;; the uniquified name (from the definition) + (name (type symbol)) + ;; compilation unit defined in + (unit (type symbol)) + ;; name of the defining module + (module (type symbol)) + ;; used by the closure check + (exported? (type bool) (default '#f) (bit #t)) + ;; for symbols in `core' Haskell; special case for IO + (core? (type bool) (default '#f) (bit #t)) + ;; Always a core sym. Used to avoid putting in sym table + (prelude? (type bool) (default '#f) (bit #t)) + )) + + + +;;; Variable information + +(define-struct var + (include def) + (predicate var?) + (slots + ;; inferred during type inference + (type (type (maybe ntype)) (default '#f)) + ;; type affixed by sign-decl or class decl + (signature (type (maybe ntype)) (default '#f)) + (interface-type (type (maybe ntype)) (default '#f)) + ;; most variables have no fixity information. + (fixity (type (maybe fixity)) (default '#f)) + ;; The following attributes are used by the backend + (selector-fn? (type bool) (default '#f) (bit #t)) + (force-strict? (type bool) (default '#f) (bit #t)) + (force-inline? (type bool) (default '#f) (bit #t)) + (toplevel? (type bool) (default '#f) (bit #t)) + (simple? (type bool) (default '#f) (bit #t)) + (strict? (type bool) (default '#f) (bit #t)) + (optimized-refs? (type bool) (default '#f) (bit #t)) + (standard-refs? (type bool) (default '#f) (bit #t)) + (single-ref (type (maybe int)) (default '#f)) + (arity (type int) (default 0)) + (referenced (type int) (default 0)) + (value (type (maybe flic-exp)) (default '#f)) + (fullname (type (maybe symbol)) (default '#f)) + (inline-value (type (maybe flic-exp)) (default '#f)) + ;; Only function bindings use these slots + (strictness (type (list bool)) (default '())) + (complexity (type (maybe int)) (default '#f)) + (optimized-entry (type (maybe symbol)) (default '#f)) + (annotations (type (list annotation-value)) (default '())) + (fn-referenced (type int) (default 0)) + (arg-invariant-value (type (maybe flic-exp)) (default '#f)) + (arg-invariant? (type bool) (default '#f) (bit #t)) + )) + + +;;; This defines an individual class method + +(define-struct method-var + (include var) + (predicate method-var?) + (slots + (class (type class) (uninitialized? #t)) + (default (type (maybe var)) (uninitialized? #t)) + (method-signature (type signature) (uninitialized? #t)))) + + +;;; A data constructor + +(define-struct con + (include def) + (predicate con?) + (slots + ;; These slots are initialized in the type declaration phase + (arity (type int) (uninitialized? #t)) + (types (type (list type)) (uninitialized? #t)) + (slot-strict? (type (list bool)) (default '())) + (tag (type int) (uninitialized? #t)) + (alg (type algdata) (uninitialized? #t)) + (infix? (type bool) (bit #t) (default '#f)) + (signature (type ntype) (uninitialized? #t)) + ;; Assigned during import-export phase + (fixity (type (maybe fixity)) (default '#f)) + (lisp-fns (type t) (default '())) + )) + + +;;; Definitions used by the type system. + +(define-struct tycon-def + (include def) + (slots + (arity (type integer) (default -1)))) + +(define-struct synonym + (include tycon-def) + (predicate synonym?) + (slots + ;; These slots are explicitly initialized in the type declaration phase. + (args (type (list symbol)) (uninitialized? #t)) + (body (type type) (uninitialized? #t)) ; stored in ast form + )) + +(define-struct algdata + (include tycon-def) + (predicate algdata?) + (slots + ;; These slots are initialized explicitly in the type declaration phase + ;; number of constructors + (n-constr (type int) (uninitialized? #t)) + (constrs (type (list con)) (uninitialized? #t)) + (context (type (list context)) (uninitialized? #t)) + ;; arguments to tycon + (tyvars (type (list symbol)) (uninitialized? #t)) + ;; signature for the type as a whole + (signature (type (maybe ntype)) (default '#f)) + ;; classes this algdata is an instance of + (classes (type (list class)) (uninitialized? #t)) + ;; true if all constructors have 0 arity + (enum? (type bool) (bit #t) (uninitialized? #t)) + ;; true when only constructor + (tuple? (type bool) (bit #t) (uninitialized? #t)) + ;; true for `tuple-syntax' tuples. + (real-tuple? (type bool) (bit #t) (uninitialized? #t)) + ;; instances to derive + (deriving (type (list class)) (uninitialized? #t)) + (export-to-lisp? (type bool) (default '#f) (bit #t)) + (implemented-by-lisp? (type bool) (default '#f) (bit #t)) + )) + +(define-struct class + (include def) + (predicate class?) + (slots + ;; These slots are initialized in the import-export phase + (method-vars (type (list method-var)) (uninitialized? #t)) + ;; These slots are explicitly initialized in the type declaration phase + ;; immediate superclasses + (super (type (list class)) (uninitialized? #t)) + ;; all superclasses + (super* (type (list class)) (uninitialized? #t)) + ;; name of class type variable + (tyvar (type symbol) (uninitialized? #t)) + (instances (type (list instance)) (uninitialized? #t)) + (kind (type (enum standard numeric other)) (uninitialized? #t)) + (n-methods (type int) (uninitialized? #t)) + (dict-size (type int) (uninitialized? #t)) + (selectors (type (list (tuple method-var var))) (uninitialized? #t)) + )) + +;;; Since instances are not named there is no need to include def. + +(define-struct instance + (include ast-node) + (slots + ;; These slots always have initializers supplied with MAKE. + (algdata (type algdata)) + (tyvars (type (list symbol))) + (class (type class)) + (context (type (list context))) + (gcontext (type (list (list class)))) + (dictionary (type var)) + + ;; Explicitly initialized during the type declaration phase. + (methods (type (list (tuple method-var var))) (uninitialized? #t)) + + ;; These slots usually default on creation. + (decls (type (list decl)) (default '())) + ;; used during verification of derived instances + (ok? (type bool) (bit #t) (default #f)) + ;; marks magically generated tuple instances + (special? (type bool) (bit #t) (default #f)) + (suppress-readers? (type bool) (bit #t) (default #f)) + )) + diff --git a/ast/exp-structs.scm b/ast/exp-structs.scm new file mode 100644 index 0000000..847723d --- /dev/null +++ b/ast/exp-structs.scm @@ -0,0 +1,386 @@ +;;; File: ast/exp-structs Author: John + +;;; These ast structures define the expression syntax + + +;;; This is simplified; there are additional rules for associativity and +;;; precedence. +;;; +;;; -> +;;; -> +;;; -> +;;; -> +;;; -> +;;; -> ; treated like +;;; -> - +;;; -> +;;; -> +;;; + +(define-struct exp + (include ast-node)) + + +;;; -> \ ... -> + +(define-struct lambda + (include exp) + (slots + (pats (type (list pattern))) + (body (type exp)))) + +;;; -> let { [;] } in + +(define-struct let + (include exp) + (slots + (decls (type (list decl))) + (body (type exp)))) + +;;; -> if then else + +(define-struct if + (include exp) + (slots + (test-exp (type exp)) + (then-exp (type exp)) + (else-exp (type exp)))) + + +;;; -> case of { [;] } +;;; +;;; -> ; ... ; +;;; +;;; -> -> exp [where { [;] } ] +;;; -> [where { [;] } ] + +(define-struct case + (include exp) + (slots + (exp (type exp)) + (alts (type (list alt))))) + +(define-struct alt + (include ast-node) + (slots + (pat (type pattern)) + ;; defined in valdef-structs + (rhs-list (type (list guarded-rhs))) + (where-decls (type (list decl))) + ;; used internally by cfn + (test (type (maybe exp)) (default '#f)) + )) + +;;; -> :: [ =>] + +(define-struct exp-sign + (include exp) + (slots + (exp (type exp)) + (signature (type signature)))) + + +;;; -> + +(define-struct app + (include exp) + (predicate app?) + (slots + (fn (type exp)) + (arg (type exp)))) + +;;; -> var-ref +;;; -> con-ref +;;; -> const +;;; -> () constructor is Unit +;;; -> ( ) +;;; -> ( , ... , ) constructor is a tuple +;;; -> [ , ... , ] list +;;; -> +;;; -> [exp> | , ... , ] list-comp +;;; -> ( ) section-r +;;; -> ( ) section-l +;;; + +(define-struct aexp + (include exp)) + + +(define-struct var-ref + (include aexp) + (predicate var-ref?) + (slots + (name (type symbol)) + (var (type def)) + (infix? (type bool) (bit #t)))) + +(define-struct con-ref + (include aexp) + (predicate con-ref?) + (slots + (name (type symbol)) + (con (type def)) + (infix? (type bool) (bit #t)))) + +(define-struct const + (include aexp) + (slots + (overloaded? (type bool) (default '#t) (bit #t)))) + +(define-struct integer-const + (include const) + (predicate integer-const?) + (slots + (value (type integer)))) + +(define-struct float-const + (include const) + (predicate float-const?) + (slots + (numerator (type integer)) + (denominator (type integer)) + (exponent (type integer)))) + +(define-struct char-const + (include const) + (predicate char-const?) + (slots + (value (type char)))) + +(define-struct string-const + (include const) + (predicate string-const?) + (slots + (value (type string)))) + +(define-struct list-exp + (include aexp) + (slots + (exps (type (list exp))))) + + +;;; -> [ .. ] sequence +;;; -> [ , .. ] sequence-then +;;; -> [ .. ] sequence-to +;;; -> [ , .. ] sequence-then-to + +(define-struct sequence + (include aexp) + (slots + (from (type exp)))) + +(define-struct sequence-to + (include aexp) + (slots + (from (type exp)) + (to (type exp)))) + + +(define-struct sequence-then + (include aexp) + (slots + (from (type exp)) + (then (type exp)))) + +(define-struct sequence-then-to + (include aexp) + (slots + (from (type exp)) + (then (type exp)) + (to (type exp)))) + +(define-struct list-comp + (include aexp) + (slots + (exp (type exp)) + (quals (type (list qual))))) + +;;; Op on left +(define-struct section-l + (include aexp) + (slots + (exp (type exp)) + (op (type exp)))) ; either con-ref or var-ref + +(define-struct section-r + (include aexp) + (slots + (exp (type exp)) + (op (type exp)))) ; either con-ref or var-ref + +;;; -> <- +;;; -> + +(define-struct qual + (include ast-node)) + +(define-struct qual-generator + (include qual) + (slots + (pat (type pattern)) + (exp (type exp)))) + +(define-struct qual-filter + (include qual) + (slots + (exp (type exp)))) + + +;;; This is used as the guard slot in a guarded-rhs to represent lack of a +;;; guard. This is the same as True. + +(define-struct omitted-guard ; same as True; should print in the guardless form + (include exp)) + + +;;; These structures are used by the precedence parser. + +(define-struct pp-exp-list ; list of expressions & ops for the prec parser + (include exp) + (slots + (exps (type (list exp))))) + +;; This is a place holder for unary negation in pp-exp expressions. It is +;; changed to call the negate function by the prec parser + +(define-struct negate + (include exp) + (predicate negate?)) + +;; Note: operators are var / con structures with infix? set to #t + +;;; The following ast nodes do not directly correspond to Haskell syntax. +;;; They are generated during internal code transformations. + +;;; This returns a number (an Int) associated with the constructor of a +;;; value. + +(define-struct con-number + (include exp) + (slots + (type (type algdata)) + (value (type exp)))) + +;;; This selects a value (denoted by the Int in slot) from a data object +;;; created by a specified constructor. + +(define-struct sel + (include exp) + (slots + (constructor (type con)) + (slot (type int)) + (value (type exp)))) + +;;; This returns True if the data value was built with the designated +;;; constructor + +(define-struct is-constructor + (include exp) + (slots + (constructor (type con)) + (value (type exp)))) + +;;; this is for the type checker only. It turns off +;;; type checking for the argument. + +(define-struct cast + (include exp) + (slots + (exp (type exp)))) + +;; this is used as the body of the let generated by +;; dependency analysis + +(define-struct void + (include exp) + (predicate void?)) + + +;;; These structures are for the type checker. They serve as a placeholder +;;; for values which will evaluate to methods or dictionaries. + +(define-struct placeholder + (include exp) + (predicate placeholder?) + (slots + (exp (type (maybe exp))) + (tyvar (type ntype)) + (overloaded-var (type exp)) + (enclosing-decls (type (list decl))))) + +(define-struct method-placeholder + (include placeholder) + (predicate method-placeholder?) + (slots + ;; the method to be dispatched + (method (type method-var)) + )) + +(define-struct dict-placeholder + (include placeholder) + (predicate dict-placeholder?) + (slots + ;; the class of dictionary needed + (class (type class)))) + +(define-struct recursive-placeholder + (include exp) + (slots + (var (type var)) + (enclosing-decls (type (list decl))) + ;; this holds the code associated with recursive + ;; functions or variables. This code instantiates + ;; the recursive context if necessary. + (exp (type (maybe exp))) + )) + +;;; This is used in primitive modules only. It holds the definition of +;;; a lisp level primitive. + +(define-struct prim-definition + (include exp) + (slots + (lisp-name (type symbol)) + (atts (type (list (tuple symbol t)))))) + +;;; This is used by the type checker to hang on to the original +;;; version of a program for message printing. This is removed by +;;; the cfn pass. + +(define-struct save-old-exp + (include exp) + (slots + (old-exp (type exp)) + (new-exp (type exp)))) + + +;;; This is used for type checking overloaded methods. + +(define-struct overloaded-var-ref + (include exp) + (slots + (var (type var)) + (sig (type ntype)))) + + + +;;; These are used by the CFN. + + +(define-struct case-block + (include exp) + (slots + (block-name (type symbol)) + (exps (type (list exp))))) + +(define-struct return-from + (include exp) + (slots + (block-name (type symbol)) + (exp (type exp)))) + +(define-struct and-exp + (include exp) + (slots + (exps (type (list exp))))) + diff --git a/ast/modules.scm b/ast/modules.scm new file mode 100644 index 0000000..e445444 --- /dev/null +++ b/ast/modules.scm @@ -0,0 +1,252 @@ +;;; File: ast/module-structs Author: John + +;;; This contains AST structures which define the basic module structure. +;;; This is just the skeleton module structure: module, imports, exports, +;;; fixity, and default decls. + +;;; AST nodes defined in the file: +;;; module import-decl entity entity-module entity-var entity-con +;;; entity-class entity-abbreviated entity-datatype fixity-decl + + + +;;; All AST structs inherit from ast-node. Not instantiated directly. +;;; The line-number is a back pointer to the source code. + +(define-struct ast-node + (type-template ast-td) + (slots + (line-number (type (maybe source-pointer)) (default '#f)))) + +(define-struct source-pointer + (slots + (line (type int)) + (file (type string)))) + +;;; -> module [] where +;;; -> +;;; +;;; -> ( , ... ) +;;; +;;; -> { [;] [[;] [;]] } +;;; -> { [;] } +;;; +;;; -> ; ... ; +;;; +;;; -> ; ... ; +;;; +;;; -> ; ... ; +;;; +;;; -> +;;; -> +;;; -> +;;; -> +;;; -> +;;; -> +;;; -> + +;;; The module struct is used to represent the program internally. Binary +;;; files containing interface information contain these structures. +;;; Most compiler passes operate on this structure. A table maps module +;;; names to this structure. Within the module structure, local names are +;;; mapped to definitions. + +;;; Modules are also used to represent interfaces & primitives. +;;; Some of the module fields may be blank for non-standard modules. + +(define-struct module + (include ast-node) + (slots + + ;; These slots are required. + + (name (type symbol)) + (type (type (enum standard interface extension))) + (prelude? (type bool) (default '#f)) ; True when symbols define the core + (interface-module (type (maybe module)) (default '#f)) + ; link to previously compiled interface + + ;; The unit is filled in by the compilation system + + (unit (type symbol) (default '*undefined*)) + + ;; The following slots are defined at parse time. + ;; After a module is dumped, these are all empty. + + ;; , list of exported names + (exports (type (list entity)) (default '())) + ;; , local import decls + (imports (type (list import-decl)) (default '())) + ;; , local fixity decls + (fixities (type (list fixity-decl)) (default '())) + ;; , local type synonym decls + (synonyms (type (list synonym-decl)) (default '())) + ;; , local data decls + (algdatas (type (list data-decl)) (default '())) + ;; , local class decls + (classes (type (list class-decl)) (default '())) + ;; , local instance decls + (instances (type (list instance-decl)) (default '())) + ;; , default types + (annotations (type (list annotation)) (default '())) + (default (type (maybe default-decl)) (default '#f)) + ;; signatures, pattern, function bindings + (decls (type (list decl)) (default '())) + + ;; These slots are filled in by the type-declaration-analysis phase + ;; after conversion to definition form + + (synonym-defs (type (list synonym)) (default '())) + (alg-defs (type (list algdata)) (default '())) + (class-defs (type (list class)) (default '())) + (instance-defs (type (list instance)) (default '())) + + + ;; The import-export stage creates a set of tables which are used for + ;; imports and exports and local name resolution. All of these tables + ;; are indexed by names. These tables always deal with definitions. + ;; Every variable, type, class, instance, and synonym is converted into + ;; a definition. Blank definitions are created early (in import/export) + ;; and different aspects of the definitions are filled in as compilation + ;; progresses. The type-related definitions are filled in during + ;; declaration analysis. Only definitions are saved when a module is + ;; written to a file; the ast information is not retained. + + ;; Used to avoid copy of Prelude symbols. + (uses-standard-prelude? (type bool) (default '#f)) + ;; maps symbols in scope to definitions + (symbol-table (type (table symbol def)) (default (make-table))) + ;; maps names onto groups. + (export-table (type (table symbol (list (tuple symbol def)))) + (default (make-table))) + ;; Note: symbol groups are found in classes and data decls. An + ;; entire group is denoted by the (..) abbreviation in an entity. + ;; maps local names onto declared fixities + (fixity-table (type (table symbol fixity)) (default (make-table))) + ;; maps defs to local names + (inverted-symbol-table (type (table symbol symbol)) (default (make-table))) + ;; Used internally during import-export + (fresh-exports (type (list (list (tuple symbol def)))) (default '())) + (exported-modules (type (list module)) (default '())) + + ;; These slots are used to support incremental compilation. + + ;; vars defined in the module + (vars (type (list var)) (default '())) + ;; for incremental compilation + (inherited-env (type (maybe module)) (default '#f)) + ;; The following slots are for interfaces only + ;; These store renaming mappings defined in the import decls of + ;; the interface. Maps local name onto (module, original name). + (interface-imports (type (list (tuple symbol (typle symbol symbol)))) + (default '())) + (interface-codefile (type (list string)) (default '())) + )) + + +;;; -> import [] [renaming ] +;;; +;;; -> ( , ... , ) +;;; -> hiding ( , ... , ) +;;; +;;; -> +;;; +;;; -> ( , ... , ) +;;; +;;; -> to +;;; -> to + +(define-struct import-decl + (include ast-node) + (slots + ;; , module imported from + (module-name (type symbol)) + ;; all: import Foo; by-name: import Foo(x) import Foo() + (mode (type (enum all by-name))) + ;; , for mode = all this is the hiding list + (specs (type (list entity))) + ;; , alist maps symbol -> symbol + (renamings (type (list renaming))) + ;; place to put corresponding module-ast; filled in by import/export. + (module (type module) (uninitialized? #t)) + )) + + +;;; -> .. entity-module +;; -> entity-var +;;; -> entity-con +;;; -> (..) entity-abbreviated +;;; -> ( , ... , ) entity-datatype +;;; -> (..) entity-abbreviated +;;; note: this is indistinguishable from tycon (..) +;;; -> ( , ... , ) entity-class + +(define-struct entity + (include ast-node) + (slots + (name (type symbol)))) + +(define-struct entity-module + (include entity) + (predicate entity-module?) + (slots + ;; a direct pointer to the referenced module added later + (module (type module) (uninitialized? #t)) + )) + +(define-struct entity-var + (include entity) + (predicate entity-var?)) + +(define-struct entity-con + (include entity) + (predicate entity-con?)) + +(define-struct entity-abbreviated + (include entity) + (predicate entity-abbreviated?)) + +(define-struct entity-class + (include entity) + (predicate entity-class?) + (slots + (methods (type (list symbol))))) + +(define-struct entity-datatype + (include entity) + (predicate entity-datatype?) + (slots + (constructors (type (list symbol))))) + +(define-struct renaming + (include ast-node) + (slots + (from (type symbol)) + (to (type symbol)) + (referenced? (type bool)))) + + +;;; -> infixl [] +;;; -> infixr [] +;;; -> infix [] +;;; +;;; -> , ... , +;;; +;;; -> +;;; -> + +;;; Not sure where to put this decl - jcp +(define-struct fixity + (include ast-node) + (slots + (associativity (type (enum l n r))) + (precedence (type int)))) + +(define-struct fixity-decl + (include ast-node) + (slots + (fixity (type fixity)) + ;; + (names (type (list symbol))) + )) + diff --git a/ast/predicates.scm b/ast/predicates.scm new file mode 100644 index 0000000..20dfc13 --- /dev/null +++ b/ast/predicates.scm @@ -0,0 +1,18 @@ +;;; predicates.scm -- various useful predicates, collected from other places +;;; +;;; author : Sandra Loosemore +;;; date : 19 Mar 1992 +;;; + + +;;; Some predicates on patterns (used by CFN) + +(define-integrable (var-or-wildcard-pat? p) + (or (is-type? 'wildcard-pat p) + (is-type? 'var-pat p))) + +(define-integrable (irrefutable-pat? p) + (or (is-type? 'wildcard-pat p) + (is-type? 'var-pat p) + (is-type? 'irr-pat p))) + diff --git a/ast/tc-structs.scm b/ast/tc-structs.scm new file mode 100644 index 0000000..1433082 --- /dev/null +++ b/ast/tc-structs.scm @@ -0,0 +1,62 @@ +;;; These structures are used by the type checker for the internal +;;; representation of type information. These are referred to in +;;; general as `ntype' structures. Conversions are required between +;;; ast types and ntypes. + +(define-struct ntype + (include ast-node)) + +(define-struct ntycon + (include ntype) + (predicate ntycon?) + (slots + (tycon (type def)) + (args (type (list ntype))))) + +(define-struct ntyvar + (include ntype) + (predicate ntyvar?) + (slots + ;; non-instantiated tyvars use #f for a value. + (value (type (maybe ntype))) + ;; could be encoded in value. + (context (type (list class)) (default ())) + (read-only? (type bool) (default #f) (bit #t)) + (dict-params (type (list (tuple valdef (list (tuple class var)))))) + )) + +;;; This is used only at the top level of a type during letrec type +;;; checking. + +(define-struct recursive-type + (include ntype) + (predicate recursive-type?) + (slots + (type (type ntype)) + (placeholders (type (list exp))))) + +;;; Gtypes are generalized types which can be copied quickly & stored in +;;; interfaces. They may contain monomorphic type variables which will not +;;; be copied. + +(define-struct gtype + (include ntype) + (predicate gtype?) + (slots + (context (type (list (list class)))) + (type (type ntype)))) + +;;; These tyvars just index a list of pre-allocated tyvars. + +(define-struct gtyvar + (include ntype) + (predicate gtyvar?) + (slots + (varnum (type int)))) + +(define-struct const-type + (include ntype) + (predicate const-type?) + (slots + (type (type ntype)))) + diff --git a/ast/type-structs.scm b/ast/type-structs.scm new file mode 100644 index 0000000..0ba4705 --- /dev/null +++ b/ast/type-structs.scm @@ -0,0 +1,159 @@ +;;; File: ast/type-structs Author: John + +;;; This contains AST structures for the type-related declarations, +;;; including `data', `class', `instance', and `type' decls. Basic type +;;; syntax is also defined here. + +;;; Structures declared here: +;;; type type-var type-con context signature synonym-decl +;;; data-decl class-decl instance-decl + + +;;; -> +;;; -> -> *** +;;; -> ... tycon +;;; +;;; -> tyvar +;;; -> tycon +;;; -> () *** +;;; -> ( ) grouping syntax +;;; -> ( , ... , ) *** +;;; -> [ ] *** +;;; *** Special cases + +;;; Type with no context - either a tyvar or a constructor +(define-struct type + (include ast-node)) + +(define-struct tyvar + (include type) + (predicate tyvar?) + (slots + (name (type symbol)))) + +(define-struct tycon + (include type) + (predicate tycon?) + (slots + (name (type symbol)) + (def (type def)) + (args (type (list type))))) + +;;; -> [ =>] +;;; +;;; -> +;;; -> ( , ... , ) + +;;; A single class, variable pair +(define-struct context + (include ast-node) + (slots + (class (type class-ref)) + (tyvar (type symbol)))) + + +;;; Type + context +(define-struct signature + (include type) + (slots + (context (type (list context))) + (type (type type)))) + + +;;; Major type declarations. Note: no explicit structures for +;;; or are needed - these are just special cases of type. + +;;; -> type = +;;; +;;; -> ... + +(define-struct synonym-decl + (include ast-node) + (slots + (simple (type type)) + (body (type type)))) + + +;;; -> data [ => ] = +;;; [deriving | ( , ... ) ] +;;; +;;; -> | ... | +;;; + +(define-struct data-decl + (include ast-node) + (slots + (context (type (list context))) + (simple (type type)) + (constrs (type (list constr))) + (deriving (type (list class-ref))) + (annotations (type (list annotation-value))))) + +;;; -> ... +;;; -> + +(define-struct constr + (include ast-node) + (slots + (constructor (type con-ref)) ; this con-ref has an infix? flag. + (types (type (list (tuple type (list annotation-value))))))) + + +;;; -> class [ => ] [where { [;] } ] +;;; +;;; -> [ ; ] [ ] +;;; +;;; -> ; ... ; + +(define-struct class-decl + (include ast-node) + (slots + (class (type class-ref)) + (super-classes (type (list context))) + (class-var (type symbol)) ; name of type var for this class in decls + (decls (type (list decl))))) ; + + +;;; -> instance [ =>] +;;; [where { [;] } ] +;;; +;;; -> +;;; -> ( ... ) +;;; -> ( , ... , ) +;;; -> () +;;; -> [ ] +;;; -> ( -> ) +;;; + +(define-struct instance-decl + (include ast-node) + (slots + ;; + (context (type (list context))) + ;; + (class (type class-ref)) + ;; + (simple (type type)) + ;; + (decls (type (list valdef))) + )) + + + +;;; -> default +;;; -> default ( , ... , ) + +(define-struct default-decl + (include ast-node) + (slots + (types (type (list type))))) + + +;;; -> + +(define-struct class-ref + (include ast-node) + (slots + (name (type symbol)) + (class (type def)))) + diff --git a/ast/valdef-structs.scm b/ast/valdef-structs.scm new file mode 100644 index 0000000..eb0dc88 --- /dev/null +++ b/ast/valdef-structs.scm @@ -0,0 +1,276 @@ +;;; File: ast/valdef-structs Author: John + +;;; Ast structure for local declarations + +;;; -> +;;; -> + +;;; decl contains value declarations and type signatures.( +;;; type related decls are topdecls and are separated from +;;; these decls. + +(define-struct decl + (include ast-node)) + + + +;;; -> :: [ =>] +;;; +;;; -> , ... , +;;; + +(define-struct signdecl ; this affixes a signature to a list of variables + (include decl) + (predicate signdecl?) + (slots + (vars (type (list var-ref))) + (signature (type signature)))) + +;;; This is introduced into decl lists by dependency analysis +(define-struct recursive-decl-group + (include decl) + (slots + ;; none of these are recursive decl groups + (decls (type (list decl))) + )) + +;;; -> = [where { [;] }] +;;; -> [where { [;] }] +;;; +;;; -> +;;; -> +;;; +;;; -> +;;; -> +;;; -> +;;; -> +;;; +;;; -> +;;; -> ( ) (infix operator with more than 2 args) +;;; -> (multiple argument pattern) + +(define-struct valdef ; this defines values. + (include decl) + (predicate valdef?) + (slots + ;; this pattern contains all new variables defined. + ;; For a function definition the pattern will always + ;; be a simple variable. + (lhs (type pattern)) + ;; this is a list of right hand sides. + ;; for a pattern definition, this list is always a singleton. For + ;; a function definition, there is a member for every successive + ;; alternative for the function. + (definitions (type (list single-fun-def))) + ;; this is used internally by dependency analysis + (depend-val (type int) (uninitialized? #t)) + ;; this is filled in by the type phase + (dictionary-args (type (list var)) (uninitialized? #t)) + ;; used for defaulting + (module (type symbol) (default '|Prelude|)) + )) + +(define-struct single-fun-def + (include ast-node) + (slots + ;; this list is always empty for pattern definition + ;; and always non-empty for function definition. + ;; The length of this list is the arity of the function. + ;; All single-fun-defs for a function have the same arity. + (args (type (list pattern))) + ;; , this contains a list of guard , expression pairs + (rhs-list (type (list guarded-rhs))) + ;; this contains declarations local to the + ;; single fun def. It scopes over the args. The + ;; guarded-rhs may refer to these values. + (where-decls (type (list decl))) + ;; true when declared in infix style. Used for printing + ;; and to check precs in prec parsing. + (infix? (type bool) (bit #t)) + )) + + + +;;; -> = [] +;;; +;;; -> | + +(define-struct guarded-rhs ; a single guarded expression. A special expression + (include ast-node) + (slots + ;; node - omitted-guard - is used when no guard given + (guard (type exp)) + (rhs (type exp)))) + + +;;; Some examples of the above: +;;; (a,b) | z>y = (z,y) +;;; | otherwise = (1,2) +;;; where z = x-2 +;;; +;;; valdef: +;;; lhs = (a,b) +;;; definitions = +;;; [single-fun-def: +;;; args = [] +;;; rhs-list = [guarded-rhs: guard = z>y +;;; rhs = (z,y), +;;; guarded-rhs: guard = otherwise +;;; rhs = (1,2)] +;;; where-decls = [valdef: lhs = z +;;; definitions = +;;; [single-fun-def: +;;; args = [] +;;; rhs-list = [guarded-rhs: +;;; guard = omitted-guard +;;; exp = x-2] +;;; where-decls = []]]] +;;; +;;; fact 0 = 1 +;;; fact (n+1) = (n+1)*fact n +;;; +;;; valdef: +;;; lhs = fact +;;; definitions = +;;; [single-fun-def: +;;; args = [0] +;;; rhs-list = [guarded-rhs: guard = omitted-guard +;;; rhs = 1] +;;; where-decls = [], +;;; single-fun-def: +;;; args = [n+1] +;;; rhs-list = [guarded-rhs: guard = omitted-guard +;;; rhs = (n+1)*fact n] +;;; where-decls = []] + + + + +;;; Definitions for patterns + +;;; This is a simplification; the real syntax is complicated by +;;; rules for precedence and associativity. +;;; +;;; -> pcon +;;; -> + plus-pat +;;; -> - *** ??? const-pat? +;;; -> +;;; -> .... pcon +;;; +;;; -> var-pat +;;; -> @ as-pat +;;; -> *** ??? var-pat? +;;; -> const-pat +;;; -> _ wildcard-pat +;;; -> () pcon special case +;;; -> ( ) (grouping syntax) +;;; -> ( , ... , ) pcon special case +;;; -> [ , ... , ] list-pat +;;; -> ~ irr-pat + +(define-struct pattern + (include ast-node)) + +(define-struct apat + (include pattern)) + +(define-struct as-pat ;; var@pat + (include apat) + (slots + (var (type var-ref)) + (pattern (type pattern)))) + +(define-struct irr-pat ;; ~pat + (include apat) + (slots + (pattern (type pattern)))) + +(define-struct var-pat ;; v + (include apat) + (predicate var-pat?) + (slots + (var (type var-ref)))) + +(define-struct wildcard-pat ;; _ + (include apat) + (predicate wildcard-pat?)) + +(define-struct const-pat ;; literal + (include apat) + (predicate const-pat?) + (slots + (value (type const)) + ;; this is the code that actually performs the match. + ;; it's filled in by type phase. + (match-fn (type exp) (uninitialized? #t)))) + +(define-struct plus-pat ;; p+k + (include pattern) + (slots + (pattern (type pattern)) + (k (type integer)) + ;; code to check for match, filled in by type phase + (match-fn (type exp) (uninitialized? #t)) + ;; code to bind result, filled in by type phase + (bind-fn (type exp) (uninitialized? #t)) + )) + +(define-struct pcon ;; con pat1 pat2 ... + (include pattern) ;; pat1 con pat2 + (predicate pcon?) + (slots + (name (type symbol)) + (con (type def)) + (pats (type (list pattern))) + (infix? (type bool) (bit #t)))) + +(define-struct list-pat ;; [p1,p2,...] + (include apat) + (slots + (pats (type (list pattern))))) + +;;; The following structs deal with prec parsing of patterns. + +(define-struct pp-pat-list + (include pattern) + (slots + (pats (type (list pattern))))) + +(define-struct pp-pat-plus + (include pattern) + (predicate pp-pat-plus?)) + +(define-struct pp-pat-negated + (include pattern) + (predicate pp-pat-negated?)) + + + +;;; Structs for annotations + +(define-struct annotation + (include decl) + (predicate annotation?)) + +(define-struct annotation-decl + (include annotation) + (predicate annotation-decl?) + (slots + (names (type (list symbol))) + (annotations (type (list annotation-value))))) + +(define-struct annotation-value + (include annotation) + (predicate annotation-value?) + (slots + (name (type symbol)) + (args (type (list t))))) + +;;; This is a list of annotations placed in where decls lists in the same +;;; manner a signdecls. + +(define-struct annotation-decls + (include annotation) + (predicate annotation-decls?) + (slots + (annotations (type (list annotation))))) diff --git a/backend/README b/backend/README new file mode 100644 index 0000000..f221b1a --- /dev/null +++ b/backend/README @@ -0,0 +1,10 @@ +This directory contains the files for the compiler backend. All of these +phases operate on FLIC code. + +optimize -- performs various tweaks to compact the code and make it faster. + also includes a postpass to fill in some additional structure slots. + +strictness -- attaches strictness information to functions and decides + whether locally-bound variables have a boxed or unboxed representation. + +codegen -- generates Lisp code from the optimized FLIC code. diff --git a/backend/backend.scm b/backend/backend.scm new file mode 100644 index 0000000..b370ea7 --- /dev/null +++ b/backend/backend.scm @@ -0,0 +1,21 @@ +;;; backend.scm -- compilation unit for code generator stuff +;;; +;;; author : Sandra Loosemore +;;; date : 13 May 1992 +;;; + + +(define-compilation-unit backend + (source-filename "$Y2/backend/") + (require flic) + (unit optimize + (source-filename "optimize.scm")) + (unit strictness + (source-filename "strictness.scm")) + (unit box + (source-filename "box.scm")) + (unit codegen + (source-filename "codegen.scm")) + (unit interface-codegen + (source-filename "interface-codegen.scm"))) + diff --git a/backend/box.scm b/backend/box.scm new file mode 100644 index 0000000..c47848a --- /dev/null +++ b/backend/box.scm @@ -0,0 +1,417 @@ +;;; box.scm -- determine which expressions need to be boxed +;;; +;;; author : Sandra Loosemore +;;; date : 03 Apr 1993 +;;; +;;; +;;; This phase determines whether expressions need to be boxed or unboxed. +;;; +;;; In the case of an expression that needs to be boxed, it determines +;;; whether it can be evaluated eagerly and boxed or whether a delay +;;; must be constructed. +;;; +;;; In the case of an expression that needs to be unboxed, it determines +;;; whether it is already known to have been evaluated and can simply +;;; be unboxed instead of checking for a delay that must be forced. +;;; +;;; This phase may mark previously non-strict variables as strict if their +;;; initializers can be evaluated eagerly. However, doing this evaluation +;;; eagerly never causes any other non-strict variables to be forced, +;;; so there is no need to propagate this strictness information backwards +;;; (as happens in the var-strictness-walk pass). + + +;;;====================================================================== +;;; Top-level function +;;;====================================================================== + + +;;; Complexity computation + +(define-integrable delay-complexity 10) +(define-integrable unbox-complexity 1) +(define-integrable box-complexity 2) +(define-integrable sel-complexity 1) +(define-integrable is-constructor-complexity 1) +(define-integrable pack-complexity 2) +(define-integrable con-number-complexity 1) + +(define (add-complexity c1 c2) + (cond ((not c1) + '#f) + ((not c2) + '#f) + (else + ;; *** We might want to establish an arbitrary cutoff here. + ;; *** e.g., if complexity > N then set it to '#f. + (the fixnum (+ (the fixnum c1) (the fixnum c2)))))) + + + +;;; The second argument to the walker is a list of things +;;; that are known to have been forced already. +;;; The third argument is a list of variables that have not yet +;;; been initialized. +;;; Walkers return two values: a new value for already-forced and +;;; the complexity of the expression. + +;;; This helper function sets the unboxed? and cheap? bits for the +;;; code generator, and adjusts the basic complexity to account for +;;; forces, boxes, and delays. +;;; +;;; The basic decision tree for the code generator should be: +;;; if unboxed? +;;; then if strict-result? +;;; then generate x (1) +;;; else if cheap? +;;; then generate (unbox x) (2) +;;; else generate (force x) (3) +;;; else if strict-result? +;;; then if cheap? +;;; then generate (box x) (4) +;;; else generate (delay x) (5) +;;; else if cheap? +;;; then generate x (6) +;;; then generate (delay (force x)) (7) +;;; See function do-codegen in codegen.scm. + + +(define (do-box-analysis object already-forced uninitialized unboxed?) + (setf (flic-exp-unboxed? object) unboxed?) + (multiple-value-bind (result complexity) + (box-analysis object already-forced uninitialized) + (setf complexity + (if unboxed? + ;; If the expression returns a boxed value and we want + ;; an unboxed one, we may need to do a force. + (if (flic-exp-strict-result? object) + (begin ; case (1) + ;; this flic-exp-cheap? bit is used only by + ;; exp-would-be-cheap? below -- not by codegen + (setf (flic-exp-cheap? object) + (if complexity '#t '#f)) + complexity) + (if (already-forced? object already-forced) + (begin ; case (2) + (setf (flic-exp-cheap? object) '#t) + (add-complexity complexity unbox-complexity)) + (begin ; case (3) + (setf (flic-exp-cheap? object) '#f) + '#f))) + ;; We want a boxed value. If the expression already + ;; returns a boxed value, return its complexity directly; + ;; otherwise return the cost of either boxing or delaying it. + (if (flic-exp-strict-result? object) + (if complexity + (begin ; case (4) + (setf (flic-exp-cheap? object) '#t) + (add-complexity complexity box-complexity)) + (begin ; case (5) + (setf (flic-exp-cheap? object) '#f) + delay-complexity)) + (if complexity + (begin + (setf (flic-exp-cheap? object) '#t) ; case (6) + complexity) + (begin ; case (7) + (setf (flic-exp-cheap? object) '#f) + delay-complexity))) + )) + (values + (if unboxed? + (note-already-forced object result) + result) + complexity))) + + + + +;;;====================================================================== +;;; Code walk +;;;====================================================================== + + +(define *local-function-calls* '()) + +(define-flic-walker box-analysis (object already-forced uninitialized)) + +(define-box-analysis flic-lambda (object already-forced uninitialized) + (do-box-analysis (flic-lambda-body object) already-forced uninitialized '#t) + (values already-forced 0)) + +(define-box-analysis flic-let (object already-forced uninitialized) + (let ((bindings (flic-let-bindings object))) + (dynamic-let ((*local-function-calls* (dynamic *local-function-calls*))) + (dolist (var bindings) + ;; Note local functions + (when (and (not (var-toplevel? var)) + (is-type? 'flic-lambda (var-value var)) + (not (var-standard-refs? var))) + (push (cons var '()) (dynamic *local-function-calls*)))) + (multiple-value-bind (already-forced complexity) + (box-analysis-let-aux object already-forced uninitialized) + (dolist (var bindings) + ;; Go back and reexamine local functions to see whether + ;; we can make more arguments strict, based on the values + ;; the function is actually called with. + (let ((stuff (assq var (dynamic *local-function-calls*)))) + (when stuff + (maybe-make-more-arguments-strict var (cdr stuff))))) + (values already-forced complexity))))) + +(define (box-analysis-let-aux object already-forced uninitialized) + (let ((recursive? (flic-let-recursive? object)) + (bindings (flic-let-bindings object)) + (body (flic-let-body object))) + (when recursive? (setf uninitialized (append bindings uninitialized))) + (dolist (var bindings) + (let* ((value (var-value var)) + (strict? (var-strict? var)) + (result (do-box-analysis value already-forced uninitialized + strict?))) + (cond (strict? + ;; Propagate information about things forced. + (setf already-forced result)) + ((and (flic-exp-cheap? value) + (flic-exp-strict-result? value)) + ;; The value expression is cheap unboxed value, so mark + ;; the variable as strict. + (setf (var-strict? var) '#t) + (setf (flic-exp-unboxed? value) '#t)))) + (when recursive? (pop uninitialized))) + ;; *** Could be smarter about computing complexity. + (values + (do-box-analysis body already-forced uninitialized '#t) + '#f))) + +(define (maybe-make-more-arguments-strict var calls) + (setf (var-strictness var) + (maybe-make-more-arguments-strict-aux + (flic-lambda-vars (var-value var)) + calls))) + +(define (maybe-make-more-arguments-strict-aux vars calls) + (if (null? vars) + '() + (let ((var (car vars))) + ;; If the variable is not already strict, check to see + ;; whether it's always called with "cheap" arguments. + (when (and (not (var-strict? var)) + (every-1 (lambda (call) + (exp-would-be-cheap? (car call) var)) + calls)) + (setf (var-strict? var) '#t) + (dolist (call calls) + (setf (flic-exp-unboxed? (car call)) '#t))) + (cons (var-strict? var) + (maybe-make-more-arguments-strict-aux + (cdr vars) + (map (function cdr) calls)))) + )) + + +;;; Look for one special fixed-point case: argument used as counter-type +;;; variable. Otherwise ignore fixed points. + +(define (exp-would-be-cheap? exp var) + (or (and (flic-exp-cheap? exp) + (flic-exp-strict-result? exp)) + (and (is-type? 'flic-ref exp) + (eq? (flic-ref-var exp) var)) + (and (is-type? 'flic-app exp) + (is-type? 'flic-ref (flic-app-fn exp)) + (var-complexity (flic-ref-var (flic-app-fn exp))) + (every-1 (lambda (a) (exp-would-be-cheap? a var)) + (flic-app-args exp))) + )) + + + +(define-box-analysis flic-app (object already-forced uninitialized) + (let ((fn (flic-app-fn object)) + (args (flic-app-args object)) + (saturated? (flic-app-saturated? object))) + (cond ((and saturated? (is-type? 'flic-ref fn)) + (let* ((var (flic-ref-var fn)) + (stuff (assq var (dynamic *local-function-calls*)))) + (when stuff + (push args (cdr stuff))) + (box-analysis-app-aux + (var-strictness var) (var-complexity var) + args already-forced uninitialized))) + ((and saturated? (is-type? 'flic-pack fn)) + (box-analysis-app-aux + (con-slot-strict? (flic-pack-con fn)) pack-complexity + args already-forced uninitialized)) + (else + ;; The function is going to be forced but all the arguments + ;; are non-strict. + (dolist (a args) + (do-box-analysis a already-forced uninitialized '#f)) + (values + (do-box-analysis fn already-forced uninitialized '#t) + '#f)) + ))) + + + +;;; Propagation of already-forced information depends on whether or +;;; not the implementation evaluates function arguments in left-to-right +;;; order. If not, we can still propagate this information upwards. + +(define (box-analysis-app-aux + strictness complexity args already-forced uninitialized) + (let ((result already-forced)) + (dolist (a args) + (let ((strict? (pop strictness))) + (multiple-value-bind (new-result new-complexity) + (do-box-analysis a already-forced uninitialized strict?) + (when strict? + (setf result + (if left-to-right-evaluation + (setf already-forced new-result) + (union-already-forced + new-result already-forced result)))) + (setf complexity (add-complexity complexity new-complexity))))) + (values result complexity))) + + +(define-box-analysis flic-ref (object already-forced uninitialized) + (values + already-forced + (if (memq (flic-ref-var object) uninitialized) + '#f + 0))) + +(define-box-analysis flic-const (object already-forced uninitialized) + (declare (ignore object uninitialized)) + (values already-forced 0)) + +(define-box-analysis flic-pack (object already-forced uninitialized) + (declare (ignore object uninitialized)) + (values already-forced 0)) + + +;;; For case-block and and, already-forced information can be propagated +;;; sequentially in the clauses. But only the first expression is +;;; guaranteed to be evaluated, so only it can propagate the information +;;; outwards. + +(define-box-analysis flic-case-block (object already-forced uninitialized) + (values + (box-analysis-sequence + (flic-case-block-exps object) already-forced uninitialized) + '#f)) + +(define-box-analysis flic-and (object already-forced uninitialized) + (values + (box-analysis-sequence + (flic-and-exps object) already-forced uninitialized) + '#f)) + +(define (box-analysis-sequence exps already-forced uninitialized) + (let ((result + (setf already-forced + (do-box-analysis + (car exps) already-forced uninitialized '#t)))) + (dolist (e (cdr exps)) + (setf already-forced + (do-box-analysis e already-forced uninitialized '#t))) + (values result already-forced))) + + +(define-box-analysis flic-return-from (object already-forced uninitialized) + (values + (do-box-analysis + (flic-return-from-exp object) already-forced uninitialized '#t) + '#f)) + + +;;; For if, the test propagates to both branches and the result. +;;; Look for an important optimization: +;;; in (if (and e1 e2 ...) e-then e-else), +;;; e-then can inherit already-forced information from all of the ei +;;; rather than only from e1. +;;; *** Could be smarter about the complexity, I suppose.... +;;; *** Also could intersect already-forced results from both +;;; *** branches. + +(define-box-analysis flic-if (object already-forced uninitialized) + (if (is-type? 'flic-and (flic-if-test-exp object)) + (box-analysis-if-and-aux object already-forced uninitialized) + (box-analysis-if-other-aux object already-forced uninitialized))) + +(define (box-analysis-if-other-aux object already-forced uninitialized) + (setf already-forced + (do-box-analysis + (flic-if-test-exp object) already-forced uninitialized '#t)) + (do-box-analysis (flic-if-then-exp object) already-forced uninitialized '#t) + (do-box-analysis (flic-if-else-exp object) already-forced uninitialized '#t) + (values already-forced '#f)) + +(define (box-analysis-if-and-aux object already-forced uninitialized) + (let* ((test-exp (flic-if-test-exp object)) + (subexps (flic-and-exps test-exp)) + (then-exp (flic-if-then-exp object)) + (else-exp (flic-if-else-exp object))) + (setf (flic-exp-unboxed? test-exp) '#t) + (multiple-value-bind (result1 resultn) + (box-analysis-sequence subexps already-forced uninitialized) + (do-box-analysis then-exp resultn uninitialized '#t) + (do-box-analysis else-exp result1 uninitialized '#t) + (values result1 '#f)))) + + +(define-box-analysis flic-sel (object already-forced uninitialized) + (multiple-value-bind (result complexity) + (do-box-analysis + (flic-sel-exp object) already-forced uninitialized '#t) + (values result (add-complexity sel-complexity complexity)))) + +(define-box-analysis flic-is-constructor (object already-forced uninitialized) + (multiple-value-bind (result complexity) + (do-box-analysis + (flic-is-constructor-exp object) already-forced uninitialized '#t) + (values result (add-complexity is-constructor-complexity complexity)))) + +(define-box-analysis flic-con-number (object already-forced uninitialized) + (multiple-value-bind (result complexity) + (do-box-analysis + (flic-con-number-exp object) already-forced uninitialized '#t) + (values result (add-complexity con-number-complexity complexity)))) + +(define-box-analysis flic-void (object already-forced uninitialized) + (declare (ignore object uninitialized)) + (values already-forced 0)) + + + + +;;;====================================================================== +;;; Already-forced bookkeeping +;;;====================================================================== + + +;;; For now, we only keep track of variables that have been forced, +;;; and not data structure accesses. + +(define (already-forced? object already-forced) + (and (is-type? 'flic-ref object) + (memq (flic-ref-var object) already-forced))) + +(define (note-already-forced object already-forced) + (if (is-type? 'flic-ref object) + (cons (flic-ref-var object) already-forced) + already-forced)) + +(define (union-already-forced new tail result) + (cond ((eq? new tail) + result) + ((memq (car new) result) + (union-already-forced (cdr new) tail result)) + (else + (union-already-forced (cdr new) tail (cons (car new) result))) + )) + + + diff --git a/backend/codegen.scm b/backend/codegen.scm new file mode 100644 index 0000000..283594f --- /dev/null +++ b/backend/codegen.scm @@ -0,0 +1,600 @@ +;;; codegen.scm -- compile flic code to Lisp +;;; +;;; Author : Sandra Loosemore +;;; Date : 29 Apr 1992 +;;; +;;; to do: check completeness of special cases for constructors +;;; constants still need work +;;; optimized entry points +;;; +;;; The code generated here uses the following helper functions: +;;; (make-curried-fn opt-fn strictness) +;;; make a curried function that calls opt-fn after collecting the +;;; arguments and processing them according to strictness. Both +;;; the arguments are evaluated. +;;; (make-tuple-constructor arity) +;;; return a function that makes an untagged data structure with "arity" +;;; slots. "arity" is a constant. +;;; (make-tuple . args) +;;; uncurried version of the above +;;; (make-tagged-data-constructor n arity) +;;; return a function that makes a data structure with tag "n" and +;;; "arity" slots. +;;; (make-tagged-data n . args) +;;; uncurried version of the above +;;; (tuple-select arity i object) +;;; extract component "i" from untagged "object" +;;; (tagged-data-select arity i object) +;;; extract component "i" from tagged "object" +;;; (constructor-number object) +;;; return the tag from "object" +;;; (delay form) +;;; returns a delay object with unevaluated "form". +;;; (box form) +;;; returns a delay object with evaluated "form". +;;; (force delay) +;;; return the value of the delay object. +;;; (make-haskell-string string) +;;; Converts a Lisp string lazily to a haskell string (using a magic +;;; delay function). Returns an unboxed result. + + + +;;;====================================================================== +;;; Code walker +;;;====================================================================== + + +;;; Here is the main entry point. + +(define (codegen-top big-let) + (do ((bindings (flic-let-bindings big-let) (cdr bindings)) + (result '()) + (decls '())) + ((null? bindings) `(begin ,@(nreverse decls) ,@(nreverse result))) + (let ((var (car bindings))) + (push `(predefine ,(fullname var)) decls) + (push (codegen-definition var (var-value var)) result)))) + + +;;; See box.scm for more information about this... + +(define (do-codegen object) + (let ((x (codegen object)) + (unboxed? (flic-exp-unboxed? object)) + (strict-result? (flic-exp-strict-result? object)) + (cheap? (flic-exp-cheap? object))) + (if unboxed? + (if strict-result? + x + (if cheap? + `(unbox ,x) + `(force ,x))) + (if strict-result? + (if cheap? + `(box ,x) + `(delay ,x)) + (if cheap? + x + `(delay (force ,x))))))) + + +(define (do-codegen-list list) + (map (function do-codegen) list)) + + +(define-flic-walker codegen (object)) + + +;;; For top-level definitions bound to lambda expressions, make both +;;; a standard entry point (with possibly unboxed arguments) and +;;; a standard entry point. + +(define (codegen-definition var exp) + (let ((fullname (fullname var))) + (when (or (memq 'codegen (dynamic *printers*)) + (memq 'codegen-flic (dynamic *printers*))) +; (format '#t "~%Codegen of ~A [~A] " (def-name var) (struct-hash var)) + (format '#t "~%Codegen of ~A " (def-name var)) + (when (not (var-strict? var)) + (format '#t "Nonstrict ")) + (when (not (eq? (var-strictness var) '())) + (format '#t "Strictness: ") + (dolist (s (var-strictness var)) + (format '#t (if s "S " "N ")))) + (when (var-simple? var) + (format '#t " Inline ")) + (format '#t "~%") + (when (memq 'codegen-flic (dynamic *printers*)) + (pprint* exp))) + (let ((lisp-code + (if (not (flic-lambda? exp)) + `(define ,fullname ,(do-codegen exp)) + (let* ((optname (optname var)) + (lambda (codegen-lambda-aux exp)) + (def `(define (,optname ,@(cadr lambda)) + ,@(cddr lambda)))) + (if (var-selector-fn? var) + ;; Standard entry point for selectors is never used. + def + `(begin + ,def + (define ,fullname + ,(maybe-make-box-value + (codegen-curried-fn + `(function ,optname) (var-strictness var)) + (var-strict? var))))))))) + (when (or (memq 'codegen (dynamic *printers*)) + (memq 'codegen-flic (dynamic *printers*))) + (pprint* lisp-code)) + lisp-code))) + +(define (codegen-lambda-list vars) + (map (function fullname) vars)) + +(define (codegen-curried-fn opt-fn strictness) + (if (null? (cdr strictness)) + ;; one-argument special cases + (if (car strictness) + `(make-curried-fn-1-strict ,opt-fn) + `(make-curried-fn-1-nonstrict ,opt-fn)) + ;; general case + `(make-curried-fn ,opt-fn ',strictness))) + + +;;; Curry lambdas. Functions always return an unboxed value. + +(define-codegen flic-lambda (object) + (codegen-curried-fn + (codegen-lambda-aux object) + (map (lambda (x) (var-strict? x)) (flic-lambda-vars object)))) + +(define (codegen-lambda-aux object) + (let* ((vars (flic-lambda-vars object)) + (ignore '()) + (args (codegen-lambda-list vars))) + (dolist (v vars) + (if (eqv? (var-referenced v) 0) + (push (fullname v) ignore))) + `(lambda ,args + ,@(if (not (null? ignore)) + `((declare (ignore ,@ignore))) + '()) + ,(do-codegen (flic-lambda-body object))))) + + +;;; This is only for non-top-level lets. +;;; The boxing of the value of each of the bindings is controlled by its +;;; strict? property. + +(define-codegen flic-let (object) + (let ((bindings (flic-let-bindings object)) + (body (flic-let-body object)) + (recursive? (flic-let-recursive? object))) + (if recursive? + (codegen-letrec bindings body) + (codegen-let* bindings body)))) + + +;;; For efficiency reasons, we want to make all the function bindings +;;; in the function namespace (some implementations do not do tail-recursion +;;; or other optimizations correctly otherwise). This means we have +;;; to sort out the variable bindings from the function bindings here. + +(define (codegen-letrec bindings body) + (let ((let-bindings '()) + (labels-bindings '())) + (dolist (var bindings) + (let ((value (var-value var)) + (fullname (fullname var)) + (strict? (var-strict? var))) + (if (flic-lambda? value) + ;; Some functions may need only the optimized or standard + ;; entry points, but not both. + (let ((optname (optname var)) + (lambda (codegen-lambda-aux value)) + (optimized? (var-optimized-refs? var)) + (standard? (var-standard-refs? var))) + (when standard? + (push (list fullname + (maybe-make-box-value + (codegen-curried-fn + (if optimized? `(function ,optname) lambda) + (var-strictness var)) + strict?)) + let-bindings)) + (when optimized? + (push (cons optname (cdr lambda)) labels-bindings))) + (push (list fullname (do-codegen value)) let-bindings)))) + (setf let-bindings (nreverse let-bindings)) + (setf labels-bindings (nreverse labels-bindings)) + (cond ((null? let-bindings) + `(labels ,labels-bindings ,(do-codegen body))) + ((null? labels-bindings) + `(letrec ,let-bindings ,(do-codegen body))) + (t + `(let ,(map (lambda (b) `(,(car b) '#f)) let-bindings) + (labels ,labels-bindings + ,@(map (lambda (b) `(setf ,@b)) let-bindings) + ,(do-codegen body)))) + ))) + +(define (codegen-let* bindings body) + (if (null? bindings) + (do-codegen body) + (let* ((var (car bindings)) + (value (var-value var)) + (fullname (fullname var)) + (strict? (var-strict? var)) + (body (codegen-let* (cdr bindings) body))) + (if (flic-lambda? value) + ;; Some functions may need only the optimized or standard + ;; entry points, but not both. + (let ((optname (optname var)) + (lambda (codegen-lambda-aux value)) + (optimized? (var-optimized-refs? var)) + (standard? (var-standard-refs? var))) + (when standard? + (setf body + (add-let-binding + (list fullname + (maybe-make-box-value + (codegen-curried-fn + (if optimized? `(function ,optname) lambda) + (var-strictness var)) + strict?)) + body))) + (when optimized? + (setf body `(flet ((,optname ,@(cdr lambda))) ,body))) + body) + (add-let-binding (list fullname (do-codegen value)) body))))) + +(define (add-let-binding binding body) + (if (and (pair? body) (eq? (car body) 'let*)) + `(let* (,binding ,@(cadr body)) ,@(cddr body)) + `(let* (,binding) ,body))) + + +(define-codegen flic-app (object) + (let ((fn (flic-app-fn object)) + (args (flic-app-args object)) + (saturated? (flic-app-saturated? object))) + (cond ((and saturated? (flic-pack? fn)) + ;; Saturated call to constructor + (codegen-constructor-app-aux + (flic-pack-con fn) + (do-codegen-list args))) + ((and saturated? (flic-ref? fn)) + ;; Saturated call to named function + (let* ((var (flic-ref-var fn)) + (optname (optname var)) + (argcode (do-codegen-list args))) + `(,optname ,@argcode))) + (else + ;; Have to make a curried call to standard entry point. + (let ((fncode (do-codegen fn)) + (argcode (do-codegen-list args))) + (if (and (pair? fncode) + (eq? (car fncode) 'force)) + `(funcall-force ,(cadr fncode) ,@argcode) + `(funcall ,fncode ,@argcode)))) + ))) + +(define (codegen-constructor-app-aux con argcode) + (let ((alg (con-alg con))) + (cond ((eq? con (core-symbol ":")) + `(cons ,@argcode)) + ((algdata-implemented-by-lisp? alg) + (apply-maybe-lambda (cadr (con-lisp-fns con)) argcode)) + ((algdata-tuple? alg) + `(make-tuple ,@argcode)) + (else + `(make-tagged-data ,(con-tag con) ,@argcode))))) + + +(define-codegen flic-ref (object) + (fullname (flic-ref-var object))) + + +(define-codegen flic-const (object) + (let ((value (flic-const-value object))) + (cond ((string? value) + `(make-haskell-string ,value)) + ((char? value) + ;; *** I think the parser ought to convert characters to their + ;; *** ASCII codes instead of doing it here. There are problems + ;; *** with valid Haskell characters that can't be represented + ;; *** portably as Lisp characters. + (char->integer value)) + ((number? value) + value) + (else + ;; It must be a ratio. This is a bit of a hack - this depends on + ;; the fact that 2 tuples are represented in the same manner as + ;; rationals. Hacked for strict rationals - jcp + `(make-tuple ,(car value) ,(cadr value))) + ))) + + +;;; Returns a function or constant, so doesn't need to delay result. +;;; See flic-app for handling of saturated constructor calls. + +(define-codegen flic-pack (object) + (let* ((con (flic-pack-con object)) + (arity (con-arity con)) + (alg (con-alg con)) + (tuple? (algdata-tuple? alg)) + (strictness (con-slot-strict? con)) + (index (con-tag con))) + (cond ((eq? con (core-symbol "Nil")) + ''()) + ((eq? con (core-symbol "True")) + ''#t) + ((eq? con (core-symbol "False")) + ''#f) + ((eq? con (core-symbol ":")) + '(function make-cons-constructor)) + ((algdata-implemented-by-lisp? alg) + (let ((fn (cadr (con-lisp-fns con)))) + (if (eqv? (con-arity con) 0) + fn + (codegen-curried-fn + (if (and (pair? fn) (eq? (car fn) 'lambda)) + fn + `(function ,fn)) + strictness)))) + ((algdata-enum? alg) + ;; All constructors have 0 arity; represent them just + ;; by numbers. + index) + (tuple? + ;; Only a single constructor for this type. + (codegen-curried-fn + `(make-tuple-constructor ,arity) + strictness)) + ((eqv? arity 0) + ;; No arguments to this constructor. + `(make-tagged-data ,index)) + (else + ;; General case. + (codegen-curried-fn + `(make-tagged-data-constructor ,index ,arity) + strictness)) + ))) + + + +;;; These expressions translate directly into their Lisp equivalents. + +(define-codegen flic-case-block (object) + `(block ,(flic-case-block-block-name object) + ,@(do-codegen-list (flic-case-block-exps object)))) + +(define-codegen flic-return-from (object) + `(return-from ,(flic-return-from-block-name object) + ,(do-codegen (flic-return-from-exp object)))) + +(define-codegen flic-and (object) + `(and ,@(do-codegen-list (flic-and-exps object)))) + +(define-codegen flic-if (object) + `(if ,(do-codegen (flic-if-test-exp object)) + ,(do-codegen (flic-if-then-exp object)) + ,(do-codegen (flic-if-else-exp object)))) + +(define-codegen flic-sel (object) + (codegen-flic-sel-aux + (flic-sel-con object) + (flic-sel-i object) + (do-codegen (flic-sel-exp object)))) + +(define (codegen-flic-sel-aux con index exp) + (let* ((alg (con-alg con)) + (tuple? (algdata-tuple? alg)) + (arity (con-arity con))) + (cond ((eq? con (core-symbol ":")) + (if (eqv? index 0) + `(car ,exp) + `(cdr ,exp))) + ((algdata-implemented-by-lisp? alg) + (apply-maybe-lambda (list-ref (cddr (con-lisp-fns con)) index) + (list exp))) + (tuple? + `(tuple-select ,arity ,index ,exp)) + (else + `(tagged-data-select ,arity ,index ,exp)) + ))) + +(define-codegen flic-is-constructor (object) + (codegen-flic-is-constructor-aux + (flic-is-constructor-con object) + (do-codegen (flic-is-constructor-exp object)))) + +(define (codegen-flic-is-constructor-aux con exp) + (let ((type (con-alg con))) + (cond ((eq? type (core-symbol "Bool")) + (if (eq? con (core-symbol "True")) + exp + `(not ,exp))) + ((eq? type (core-symbol "List")) + (if (eq? con (core-symbol ":")) + `(pair? ,exp) + `(null? ,exp))) + ((algdata-implemented-by-lisp? type) + (let ((fn (car (con-lisp-fns con)))) + (apply-maybe-lambda fn (list exp)))) + ((algdata-tuple? type) + ;; This should never happen. + ''#t) + ((algdata-enum? type) + `(eqv? (the fixnum ,exp) (the fixnum ,(con-tag con)))) + (else + `(eqv? (the fixnum (constructor-number ,exp)) + (the fixnum ,(con-tag con)))) + ))) + + +(define-codegen flic-con-number (object) + (let ((type (flic-con-number-type object)) + (exp (do-codegen (flic-con-number-exp object)))) + `(the fixnum + ,(cond ((eq? type (core-symbol "Bool")) + `(if ,exp 1 0)) + ((eq? type (core-symbol "List")) + `(if (pair? ,exp) 0 1)) + ((algdata-tuple? type) + ;; This should never happen. + 0) + ((algdata-implemented-by-lisp? type) + (let ((var (gensym))) + `(let ((,var ,exp)) + (cond ,@(map (lambda (con) + `(,(apply-maybe-lambda + (car (con-lisp-fns con)) + (list var)) + ',(con-tag con))) + (algdata-constrs type)) + (else (error "No constructor satisfies ~A.~%" + ',(def-name type))))))) + ((algdata-enum? type) + exp) + (else + `(constructor-number ,exp)) + )) + )) + + + +;;;====================================================================== +;;; Utility functions +;;;====================================================================== + +;;; Here are some helper functions for handing boxing and unboxing +;;; of values. +;;; maybe-make-box-delay is used to box forms that are "expensive" to +;;; compute; maybe-make-box-value is used to box forms like constants +;;; or functions that are "cheap" to compute eagerly. +;;; Maybe-unbox is used to unbox a form that returns a boxed result. + +(define (maybe-make-box-delay form unboxed?) + (if unboxed? + form + `(delay ,form))) + +(define (maybe-make-box-value form unboxed?) + (if unboxed? + form + `(box ,form))) + +(define (maybe-unbox form unboxed?) + (if unboxed? + `(force ,form) + form)) + + +;;; These two var slots are filled in lazily by the code generator, +;;; since most vars generated don't need them. You should always +;;; use these functions instead of accessing the structure slot +;;; directly. + +(define (fullname var) + (or (var-fullname var) + (setf (var-fullname var) + (if (var-toplevel? var) + ;; For toplevel names, use module name glued onto base names. + ;; These are always interned symbols. + (if (def-core? var) + (symbol-append '|*Core:| (def-name var)) + (symbol-append (def-module var) '\: (def-name var))) + ;; Otherwise, make sure we have a gensym. + ;; The uniquification of interned symbols is required + ;; because there may be multiple nested bindings of the + ;; same name, and we want to be able to distinguish between + ;; the different bindings. + (let ((name (def-name var))) + (if (gensym? name) + name + (gensym (symbol->string name)))))) + )) + +(define (optname var) + (or (var-optimized-entry var) + (let ((name (string-append (symbol->string (fullname var)) "/OPT"))) + (setf (var-optimized-entry var) + (if (var-toplevel? var) + (string->symbol name) + (gensym name)))))) + + + +;;;====================================================================== +;;; Exported functions +;;;====================================================================== + +;;; This handles types exported to lisp from Haskell +;;; *** Is this really supposed to create variable bindings as +;;; *** opposed to function bindings??? +;;; *** I assume all of these functions want strict arguments and return +;;; *** strict results, even if the data structures contain boxed values. + +(define (codegen-exported-types mods) + (let ((defs '())) + (dolist (m mods) + (dolist (a (module-alg-defs m)) + (when (algdata-export-to-lisp? a) + (dolist (c (algdata-constrs a)) + (setf defs (nconc (codegen-constr c) defs)))))) + `(begin ,@defs))) + +(define (codegen-constr c) + (let ((lisp-fns (con-lisp-fns c))) + (if c + (let ((res + `(,(codegen-lisp-predicate (car lisp-fns) c) + ,(codegen-lisp-constructor (cadr lisp-fns) c) + ,@(codegen-lisp-accessors + (cddr lisp-fns) (con-slot-strict? c) c 0)))) + (when (memq 'codegen (dynamic *printers*)) + (dolist (d res) + (pprint* d))) + res) + '()))) + +(define (codegen-lisp-predicate name c) + `(define (,name x) + ,(codegen-flic-is-constructor-aux c 'x))) + +(define (codegen-lisp-constructor name c) + (let ((strictness (con-slot-strict? c)) + (args '()) + (exps '())) + (dolist (s strictness) + (let ((arg (gensym))) + (push arg args) + (push (if s arg `(box ,arg)) exps))) + `(define (,name ,@(nreverse args)) + ,(codegen-constructor-app-aux c (nreverse exps))))) + +(define (codegen-lisp-accessors names strictness c i) + (declare (type fixnum i)) + (if (null? names) + '() + (let ((body (codegen-flic-sel-aux c i 'x))) + (when (not (car strictness)) + (setf body `(force ,body))) + (cons `(define (,(car names) x) ,body) + (codegen-lisp-accessors (cdr names) (cdr strictness) c (+ i 1)))) + )) + + + +;;; This is a special hack needed due to brain-dead common lisp problems. +;;; This allows the user to place lambda defined functions in ImportLispType +;;; *** I'm not convinced this is necessary; ((lambda ...) args) +;;; *** is perfectly valid Common Lisp syntax! + +(define (apply-maybe-lambda fn args) + (if (and (pair? fn) + (eq? (car fn) 'lambda)) + `(funcall ,fn ,@args) + `(,fn ,@args))) diff --git a/backend/interface-codegen.scm b/backend/interface-codegen.scm new file mode 100644 index 0000000..50c8630 --- /dev/null +++ b/backend/interface-codegen.scm @@ -0,0 +1,200 @@ + +;;; This generates code for vars defined in an interface. This looks at +;;; annotations and fills in the slots of the var definition. + +(define (haskell-codegen/interface mods) + (codegen/interface (car mods))) + +(define (codegen/interface mod) + (let ((code '())) + (dolist (d (module-decls mod)) + (when (not (signdecl? d)) + (error 'bad-decl)) + (dolist (var (signdecl-vars d)) + (let ((v (var-ref-var var))) + (setf (var-type v) (var-signature v)) + (setf (var-toplevel? v) '#t) + (let ((a (lookup-annotation v '|Complexity|))) + (when (not (eq? a '#f)) + (setf (var-complexity v) + (car (annotation-value-args a))))) + (let ((a (lookup-annotation v '|LispName|))) + (when (not (eq? a '#f)) + (let ((lisp-entry (generate-lisp-entry v a))) + (push lisp-entry code) + (when (memq 'codegen (dynamic *printers*)) + (pprint* lisp-entry)))))))) + `(begin ,@code))) + +(define (generate-lisp-entry v a) + (let ((lisp-name (read-lisp-object (car (annotation-value-args a)))) + (type (maybe-expand-io-type (gtype-type (var-type v))))) + (setf (var-optimized-entry v) lisp-name) + (if (arrow-type? type) + (codegen-lisp-fn v (gather-arg-types type)) + (codegen-lisp-const v type)))) + +(define (codegen-lisp-fn var arg-types) + (let* ((aux-definition '()) + (wrapper? (foreign-fn-needs-wrapper? var arg-types)) + (strictness-annotation (lookup-annotation var '|Strictness|)) + (strictness (determine-strictness strictness-annotation arg-types)) + (temps (gen-temp-names strictness))) + (setf (var-strict? var) '#t) + (setf (var-arity var) (length strictness)) + (setf (var-strictness var) strictness) + (when wrapper? + (mlet (((code name) + (make-wrapper-fn var (var-optimized-entry var) arg-types))) + (setf (var-optimized-entry var) name) + (setf aux-definition (list code)))) + `(begin ,@aux-definition + (define ,(fullname var) + ,(maybe-make-box-value + (codegen-curried-fn + (if wrapper? + `(function ,(var-optimized-entry var)) + `(lambda ,temps + (,(var-optimized-entry var) ,@temps))) + (var-strictness var)) + '#t))))) + +(define (determine-strictness a args) + (if (eq? a '#f) + (map (lambda (x) (declare (ignore x)) '#t) (cdr args)) + (parse-strictness (car (annotation-value-args a))))) + +(define (codegen-lisp-const var type) + (let ((conversion-fn (output-conversion-fn type))) + (setf (var-strict? var) '#f) + (setf (var-arity var) 0) + (setf (var-strictness var) '()) + `(define ,(fullname var) + (delay + ,(if (eq? conversion-fn '#f) + (var-optimized-entry var) + `(,@conversion-fn ,(var-optimized-entry var))))))) + +(define (maybe-expand-io-type ty) + (cond ((and (ntycon? ty) + (eq? (ntycon-tycon ty) (core-symbol "IO"))) + (**ntycon (core-symbol "Arrow") + (list (**ntycon (core-symbol "SystemState") '()) + (**ntycon (core-symbol "IOResult") + (ntycon-args ty))))) + ((arrow-type? ty) + (**ntycon (core-symbol "Arrow") + (list (car (ntycon-args ty)) + (maybe-expand-io-type (cadr (ntycon-args ty)))))) + (else ty))) + +(define (gather-arg-types type) + (if (arrow-type? type) + (let ((a (ntycon-args type))) + (cons (car a) (gather-arg-types (cadr a)))) + (list type))) + +(define (input-conversion-fn ty) + (if (ntycon? ty) + (let ((tycon (ntycon-tycon ty))) + (cond ((eq? tycon (core-symbol "String")) + (lambda (x) `(haskell-string->string ,x))) + ((eq? tycon (core-symbol "List")) ; needs to convert elements + (let ((var (gensym "X")) + (inner-fn (input-conversion-fn (car (ntycon-args ty))))) + (lambda (x) `(haskell-list->list + (lambda (,var) + ,(if (eq? inner-fn '#f) + var + (funcall inner-fn var))) + ,x)))) + ((eq? tycon (core-symbol "Char")) + (lambda (x) `(integer->char ,x))) + (else '#f))) + '#f)) + +(define (output-conversion-fn ty) + (if (ntycon? ty) + (let ((tycon (ntycon-tycon ty))) + (cond ((eq? tycon (core-symbol "String")) + (lambda (x) `(make-haskell-string ,x))) + ((eq? tycon (core-symbol "List")) + (let ((var (gensym "X")) + (inner-fn (output-conversion-fn (car (ntycon-args ty))))) + (lambda (x) `(list->haskell-list + (lambda (,var) + ,(if (eq? inner-fn '#f) + var + (funcall inner-fn var))) + ,x)))) + ((eq? tycon (core-symbol "UnitType")) + (lambda (x) `(insert-unit-value ,x))) + ((eq? tycon (core-symbol "IOResult")) + (lambda (x) + (let ((c1 (output-conversion-fn (car (ntycon-args ty))))) + `(box ,(apply-conversion c1 x))))) + (else '#f))) + '#f)) + +(define (apply-conversion fn x) + (if (eq? fn '#f) + x + (funcall fn x))) + +(define (foreign-fn-needs-wrapper? var args) + (if (lookup-annotation var '|NoConversion|) + '#f + (ffnw-1 args))) + +(define (ffnw-1 args) + (if (null? (cdr args)) + (not (eq? (output-conversion-fn (car args)) '#f)) + (or (not (eq? (input-conversion-fn (car args)) '#f)) + (systemstate? (car args)) + (ffnw-1 (cdr args))))) + +(define (make-wrapper-fn var fn args) + (mlet ((new-fn (symbol-append (fullname var) '|/wrapper|)) + (avars (gen-temp-names (cdr args))) + (ignore-state? (systemstate? (cadr (reverse args)))) + ((arg-conversions res-conversion) + (collect-conversion-fns avars args))) + (values + `(define (,new-fn ,@avars) + ,@(if ignore-state? `((declare (ignore ,(car (last avars))))) + '()) + ,@arg-conversions + ,(apply-conversion res-conversion + `(,fn ,@(if ignore-state? + (butlast avars) + avars)))) + new-fn))) + +(define (collect-conversion-fns avars args) + (if (null? avars) + (values '() (output-conversion-fn (car args))) + (mlet ((fn (input-conversion-fn (car args))) + ((c1 r) (collect-conversion-fns (cdr avars) (cdr args)))) + (values (if (eq? fn '#f) + c1 + `((setf ,(car avars) ,(funcall fn (car avars))) ,@c1)) + r)))) + +(define (arrow-type? x) + (and (ntycon? x) + (eq? (ntycon-tycon x) (core-symbol "Arrow")))) + +(define (systemstate? x) + (and (ntycon? x) + (eq? (ntycon-tycon x) (core-symbol "SystemState")))) + +(define (gen-temp-names l) + (gen-temp-names-1 l '(A B C D E F G H I J K L M N O P))) + +(define (gen-temp-names-1 l1 l2) + (if (null? l1) + '() + (if (null? l2) + (gen-temp-names-1 l1 (list (gensym "T"))) + (cons (car l2) (gen-temp-names-1 (cdr l1) (cdr l2)))))) + diff --git a/backend/optimize.scm b/backend/optimize.scm new file mode 100644 index 0000000..1624e35 --- /dev/null +++ b/backend/optimize.scm @@ -0,0 +1,1986 @@ +;;; optimize.scm -- flic optimizer +;;; +;;; author : Sandra Loosemore +;;; date : 7 May 1992 +;;; +;;; +;;; The optimizer does these kinds of program transformations: +;;; +;;; * remove unreferenced variable bindings. +;;; +;;; * constant folding and various other kinds of compile-time +;;; evaluation. +;;; +;;; * beta reduction (replace references to variables bound to simple +;;; expressions with the expression) +;;; + + +;;; Since some of the optimizations can make additional transformations +;;; possible, we want to make multiple iteration passes. But since each +;;; pass is likely to have diminishing benefits, we don't want to keep +;;; iterating indefinitely. So establish a fairly arbitrary cutoff point. +;;; The value is based on empirical results from compiling the prelude. + +(define *max-optimize-iterations* 5) +(define *optimize-foldr-iteration* 0) ; when to inline foldr +(define *optimize-build-iteration* 0) ; when to inline build +(define *current-optimize-iteration* 0) + + +;;; Flags for enabling various optimizations + +(define *all-optimizers* '(foldr inline constant lisp)) +(define *optimizers* *all-optimizers*) + + +;;; Used to note whether we are doing the various optimizations + +(define-local-syntax (do-optimization? o) + `(memq ,o (dynamic *optimizers*))) + +(define *do-foldr-optimizations* (do-optimization? 'foldr)) +(define *do-inline-optimizations* (do-optimization? 'inline)) +(define *do-constant-optimizations* (do-optimization? 'constant)) + + +;;; If the foldr optimization is enabled, bind the corresponding +;;; variables to these values instead of the defaults. + +(define *foldr-max-optimize-iterations* 15) +(define *foldr-optimize-foldr-iteration* 8) +(define *foldr-optimize-build-iteration* 5) + + +;;; Some random other variables + +(define *structured-constants* '()) +(define *structured-constants-table* '#f) +(define *lambda-depth* 0) +(define *local-bindings* '()) + + +;;; This is for doing some crude profiling. +;;; Comment out the body of the macro to disable profiling. + +;;; Here are current counts from compiling the prelude: +;;; (LET-REMOVE-UNUSED-BINDING . 5835) +;;; (REF-INLINE-SINGLE-REF . 2890) +;;; (REF-INLINE . 2692) +;;; (LET-EMPTY-BINDINGS . 2192) +;;; (APP-LAMBDA-TO-LET . 1537) +;;; (APP-MAKE-SATURATED . 416) +;;; (LET-HOIST-RETURN-FROM . 310) +;;; (CASE-BLOCK-IDENTITY . 273) +;;; (CASE-BLOCK-DEAD-CODE . 234) +;;; (CASE-BLOCK-TO-IF . 212) +;;; (SEL-FOLD-VAR . 211) +;;; (APP-HOIST-LET . 190) +;;; (LET-HOIST-LAMBDA . 181) +;;; (FOLDR-INLINE . 176) +;;; (AND-UNARY . 172) +;;; (LAMBDA-COMPRESS . 168) +;;; (APP-FOLD-SELECTOR . 141) +;;; (BUILD-INLINE-LAMBDA . 134) +;;; (LET-COMPRESS . 134) +;;; (IF-FOLD . 128) +;;; (INTEGER-TO-INT-CONSTANT-FOLD . 124) +;;; (AND-COMPRESS . 94) +;;; (APP-COMPRESS . 93) +;;; (FOLDR-CONS-IDENTITY . 69) +;;; (IF-COMPRESS-TEST . 65) +;;; (IF-HOIST-LAMBDA . 61) +;;; (APP-HOIST-STRUCTURED-CONSTANT . 60) +;;; (FOLDR-PRIM-APPEND-INLINE . 55) +;;; (FOLDR-BUILD-IDENTITY . 40) +;;; (CASE-BLOCK-DISCARD-REDUNDANT-TEST . 37) +;;; (FOLDR-NIL-IDENTITY . 36) +;;; (LET-HOIST-INVARIANT-ARGS . 30) +;;; (FOLDR-HOIST-LET . 28) +;;; (CON-NUMBER-FOLD-TUPLE . 21) +;;; (FOLDR-CONS-NIL-IDENTITY . 15) +;;; (AND-CONTAINS-TRUE . 14) +;;; (IF-IDENTITY-INVERSE . 8) +;;; (IF-HOIST-RETURN-FROM . 7) +;;; (CASE-BLOCK-HOIST-LET . 7) +;;; (INTEGER-TO-INT-IDENTITY . 7) +;;; (APP-PACK-IDENTITY . 2) +;;; (CON-NUMBER-FOLD . 2) +;;; (IF-IDENTITY . 2) +;;; (INT-TO-INTEGER-CONSTANT-FOLD . 2) +;;; (LET-HOIST-STRUCTURED-CONSTANT . 1) + + +(define-local-syntax (record-hack type . args) + (declare (ignore args)) + `',type +; `(record-hack-aux ,type ,@args) + ) + +(define *hacks-done* '()) + +(define (record-hack-aux type . args) + ;; *** debug + ;; (format '#t "~s ~s~%" type args) + (declare (ignore args)) + (let ((stuff (assq type (car (dynamic *hacks-done*))))) + (if stuff + (incf (cdr stuff)) + (push (cons type 1) (car (dynamic *hacks-done*)))))) + +(define (total-hacks) + (let ((totals '())) + (dolist (alist *hacks-done*) + (dolist (entry alist) + (let ((stuff (assq (car entry) totals))) + (if stuff + (setf (cdr stuff) (+ (cdr stuff) (cdr entry))) + (push (cons (car entry) (cdr entry)) totals))))) + totals)) + + +;;; This is the main entry point. + +(define (optimize-top object) + (dynamic-let ((*structured-constants* '()) + (*structured-constants-table* (make-table)) + (*lambda-depth* 0) + (*local-bindings* '()) + (*do-inline-optimizations* + (do-optimization? 'inline)) + (*do-constant-optimizations* + (do-optimization? 'constant)) + (*max-optimize-iterations* + (if (do-optimization? 'foldr) + (dynamic *foldr-max-optimize-iterations*) + (dynamic *max-optimize-iterations*))) + (*optimize-foldr-iteration* + (if (do-optimization? 'foldr) + (dynamic *foldr-optimize-foldr-iteration*) + (dynamic *optimize-foldr-iteration*))) + (*optimize-build-iteration* + (if (do-optimization? 'foldr) + (dynamic *foldr-optimize-build-iteration*) + (dynamic *optimize-build-iteration*)))) + (setf *hacks-done* '()) + (dotimes (i (dynamic *max-optimize-iterations*)) + (dynamic-let ((*current-optimize-iteration* i)) +;; debug (*duplicate-object-table* (make-table))) + (when (memq 'optimize-extra (dynamic *printers*)) + (format '#t "~%Optimize pass ~s:" i) + (pprint object)) + (push '() *hacks-done*) + (setf object (optimize-flic-let-aux object '#t)))) + (setf (flic-let-bindings object) + (nconc (nreverse (dynamic *structured-constants*)) + (flic-let-bindings object)))) + (install-uninterned-globals (flic-let-bindings object)) + (postoptimize object) + object) + + +(define-flic-walker optimize (object)) + +;;; debugging stuff +;;; +;;; (define *duplicate-object-table* (make-table)) +;;; +;;; (define (new-optimize object) +;;; (if (table-entry (dynamic *duplicate-object-table*) object) +;;; (error "Duplicate object ~s detected." object) +;;; (begin +;;; (setf (table-entry (dynamic *duplicate-object-table*) object) '#t) +;;; (old-optimize object)))) +;;; +;;; (lisp:setf (lisp:symbol-function 'old-optimize) +;;; (lisp:symbol-function 'optimize)) +;;; (lisp:setf (lisp:symbol-function 'optimize) +;;; (lisp:symbol-function 'new-optimize)) + +(define (optimize-list objects) + (optimize-list-aux objects) + objects) + +(define (optimize-list-aux objects) + (if (null? objects) + '() + (begin + (setf (car objects) (optimize (car objects))) + (optimize-list-aux (cdr objects))))) + + +;;; Compress nested lambdas. This hack is desirable because saturating +;;; applications within the lambda body effectively adds additional +;;; parameters to the function. + +;;; *** Maybe this should look for hoistable constant lambdas too. + +(define-optimize flic-lambda (object) + (let ((vars (flic-lambda-vars object))) + (dynamic-let ((*lambda-depth* (1+ (dynamic *lambda-depth*))) + (*local-bindings* (cons vars (dynamic *local-bindings*)))) + (dolist (var vars) + (setf (var-referenced var) 0)) + (let ((new-body (optimize (flic-lambda-body object)))) + (setf (flic-lambda-body object) new-body) + (cond ((is-type? 'flic-lambda new-body) + (record-hack 'lambda-compress) + (setf (flic-lambda-vars object) + (nconc (flic-lambda-vars object) + (flic-lambda-vars new-body))) + (setf (flic-lambda-body object) (flic-lambda-body new-body))) + (else + '#f)) + object)))) + + +;;; For let, first mark all variables as unused and check for "simple" +;;; binding values that permit beta reduction. Then walk the subexpressions. +;;; Finally discard any bindings that are still marked as unused. +;;; *** This fails to detect unused recursive variables. + +(define-optimize flic-let (object) + (optimize-flic-let-aux object '#f)) + +(define (optimize-flic-let-aux object toplevel?) + (let ((bindings (flic-let-bindings object)) + (recursive? (flic-let-recursive? object))) + ;; *** This handling of *local-bindings* isn't quite right since + ;; *** it doesn't account for the sequential nature of bindings + ;; *** in a non-recursive let, but it's close enough. We won't + ;; *** get any semantic errors, but it might miss a few optimizations. + (dynamic-let ((*local-bindings* + (if (and recursive? (not toplevel?)) + (cons bindings (dynamic *local-bindings*)) + (dynamic *local-bindings*)))) + (optimize-flic-let-bindings bindings recursive? toplevel?) + (dynamic-let ((*local-bindings* + (if (and (not recursive?) (not toplevel?)) + (cons bindings (dynamic *local-bindings*)) + (dynamic *local-bindings*)))) + (setf (flic-let-body object) (optimize (flic-let-body object)))) + ;; Check for unused bindings and other rewrites. + ;; Only do this for non-toplevel lets. + (if toplevel? + object + (optimize-flic-let-rewrite object bindings recursive?))))) + +(define (optimize-flic-let-bindings bindings recursive? toplevel?) + ;; Initialize + (dolist (var bindings) + (setf (var-referenced var) 0) + (setf (var-fn-referenced var) 0) + (when (is-type? 'flic-lambda (var-value var)) + (dolist (v (flic-lambda-vars (var-value var))) + (setf (var-arg-invariant? v) '#t) + (setf (var-arg-invariant-value v) '#f)))) + ;; Traverse value subforms + (do ((bindings bindings (cdr bindings))) + ((null? bindings) '#f) + (let* ((var (car bindings)) + (val (var-value var))) + (if (and (is-type? 'flic-app val) + (dynamic *do-constant-optimizations*) + (let ((fn (flic-app-fn val)) + (args (flic-app-args val))) + (if recursive? + (structured-constant-app-recursive? + fn args bindings (list var)) + (structured-constant-app? fn args)))) + ;; Variable is bound to a structured constant. If this + ;; isn't already a top-level binding, replace the value + ;; of the constant with a reference to a top-level variable + ;; that is in turn bound to the constant expression. + ;; binding to top-level if this is a new constant. + ;; *** Maybe we should also look for variables bound + ;; *** to lambdas, that can also be hoisted to top level. + (when (not toplevel?) + (multiple-value-bind (con args cvar) + (enter-structured-constant-aux val '#t) + (record-hack 'let-hoist-structured-constant) + (if cvar + (setf (var-value var) (make-flic-ref cvar)) + (add-new-structured-constant var con args)))) + (begin + ;; If this is a function that's a candidate for foldr/build + ;; optimization, stash the value away prior to + ;; inlining the calls. + ;; *** We might try to automagically detect functions + ;; *** that are candidates for these optimizations here, + ;; *** but have to watch out for infinite loops! + (when (and (var-force-inline? var) + (eqv? (the fixnum + (dynamic *current-optimize-iteration*)) + (the fixnum + (dynamic *optimize-build-iteration*))) + (is-type? 'flic-lambda val) + (or (is-foldr-or-build-app? (flic-lambda-body val)))) + (setf (var-inline-value var) (copy-flic-top val))) + ;; Then walk value normally. + (let ((new-val (optimize val))) + (setf (var-value var) new-val) + (setf (var-simple? var) + (or (var-force-inline? var) + (and (not (var-selector-fn? var)) + (can-inline? + new-val + (if recursive? bindings '()) + toplevel?)))))) + )))) + + +(define (is-foldr-or-build-app? exp) + (typecase exp + (flic-app + (let ((fn (flic-app-fn exp))) + (and (is-type? 'flic-ref fn) + (or (eq? (flic-ref-var fn) (core-symbol "foldr")) + (eq? (flic-ref-var fn) (core-symbol "build")))))) + (flic-let + (is-foldr-or-build-app? (flic-let-body exp))) + (flic-ref + (let ((val (var-value (flic-ref-var exp)))) + (and val (is-foldr-or-build-app? val)))) + (else + '#f))) + + +(define (optimize-flic-let-rewrite object bindings recursive?) + ;; Delete unused variables from the list. + (setf bindings + (list-delete-if + (lambda (var) + (cond ((var-toplevel? var) + ;; This was a structured constant hoisted to top-level. + '#t) + ((eqv? (the fixnum (var-referenced var)) (the fixnum 0)) + (record-hack 'let-remove-unused-binding var) + '#t) + ((eqv? (the fixnum (var-referenced var)) (the fixnum 1)) + (setf (var-single-ref var) (dynamic *lambda-depth*)) + '#f) + (else + (setf (var-single-ref var) '#f) + '#f))) + bindings)) + ;; Add extra bindings for reducing functions with invariant + ;; arguments. Hopefully some of the extra bindings will go + ;; away in future passes! + (setf (flic-let-bindings object) + (setf bindings (add-stuff-for-invariants bindings))) + ;; Look for other special cases. + (cond ((null? bindings) + ;; Simplifying the expression by getting rid of the LET may + ;; make it possible to do additional optimizations on the + ;; next pass. + (record-hack 'let-empty-bindings) + (flic-let-body object)) + ((is-type? 'flic-return-from (flic-let-body object)) + ;; Hoist return-from outside of LET. This may permit + ;; further optimizations by an enclosing case-block. + (record-hack 'let-hoist-return-from) + (let* ((body (flic-let-body object)) + (inner-body (flic-return-from-exp body))) + (setf (flic-return-from-exp body) object) + (setf (flic-let-body object) inner-body) + body)) + ((and (not recursive?) + (is-type? 'flic-let (flic-let-body object)) + (not (flic-let-recursive? (flic-let-body object)))) + ;; This is purely to produce more compact code. + (record-hack 'let-compress) + (let ((body (flic-let-body object))) + (setf (flic-let-bindings object) + (nconc bindings (flic-let-bindings body))) + (setf (flic-let-body object) (flic-let-body body)) + object)) + ((is-type? 'flic-lambda (flic-let-body object)) + ;; Hoist lambda outside of LET. This may permit + ;; merging of nested lambdas on a future pass. + (record-hack 'let-hoist-lambda) + (let* ((body (flic-let-body object)) + (inner-body (flic-lambda-body body))) + (setf (flic-lambda-body body) object) + (setf (flic-let-body object) inner-body) + body)) + (else + object)) + ) + +;;; Look for constant-folding and structured constants here. + +(define-optimize flic-app (object) + (optimize-flic-app-aux object)) + +(define (optimize-flic-app-aux object) + (let ((new-fn (optimize (flic-app-fn object))) + (new-args (optimize-list (flic-app-args object)))) + (typecase new-fn + (flic-ref + ;; The function is a variable. + (let* ((var (flic-ref-var new-fn)) + (val (var-value var)) + (n (length new-args)) + (arity (guess-function-arity var))) + (cond ((and arity (< (the fixnum n) (the fixnum arity))) + ;; This is a first-class call that is not fully saturated. + ;; Make it saturated by wrapping a lambda around it. + (setf new-fn + (do-app-make-saturated object new-fn new-args arity n)) + (setf new-args '())) + ((var-selector-fn? var) + ;; This is a saturated call to a selector. We might + ;; be able to inline the call. + (multiple-value-bind (fn args) + (try-to-fold-selector var new-fn new-args) + (setf new-fn fn) + (setf new-args args))) + ((and (not (var-toplevel? var)) + (is-type? 'flic-lambda val)) + ;; This is a saturated call to a local function. + ;; Increment its reference count and note if any of + ;; the arguments are invariant. + (incf (var-fn-referenced var)) + (note-invariant-args new-args (flic-lambda-vars val))) + (else + (let ((magic (magic-optimize-function var))) + (when magic + (multiple-value-bind (fn args) + (funcall magic new-fn new-args) + (setf new-fn fn) + (setf new-args args))))) + ))) + (flic-lambda + ;; Turn application of lambda into a let. + (multiple-value-bind (fn args) + (do-lambda-to-let-aux new-fn new-args) + (setf new-fn fn) + (setf new-args args))) + (flic-pack + (let ((con (flic-pack-con new-fn)) + (temp '#f)) + (when (eqv? (length new-args) (con-arity con)) + (cond ((and (dynamic *do-constant-optimizations*) + (every-1 (function structured-constant?) new-args)) + ;; This is a structured constant that + ;; can be replaced with a top-level binding. + (setf (flic-app-fn object) new-fn) + (setf (flic-app-args object) new-args) + (record-hack 'app-hoist-structured-constant object) + (setf new-fn (enter-structured-constant object '#t)) + (setf new-args '())) + ((and (setf temp (is-selector? con 0 (car new-args))) + (is-selector-list? con 1 temp (cdr new-args))) + ;; This is an expression like (cons (car x) (cdr x)). + ;; Replace it with just plain x to avoid reconsing. + (record-hack 'app-pack-identity new-fn) + (setf new-fn (copy-flic-top temp)) + (setf new-args '())) + )))) + (flic-let + ;; Hoist let to surround entire application. + ;; Simplifying the function being applied may permit further + ;; optimizations on next pass. + ;; (We might try to hoist lets in the argument expressions, too, + ;; but I don't think that would lead to any real simplification + ;; of the code.) + (record-hack 'app-hoist-let) + (setf (flic-app-fn object) (flic-let-body new-fn)) + (setf (flic-app-args object) new-args) + (setf new-args '()) + (setf (flic-let-body new-fn) object) + ) + (flic-app + ;; Try to compress nested applications. + ;; This may make the call saturated and permit further optimizations + ;; on the next pass. + (record-hack 'app-compress) + (setf new-args (nconc (flic-app-args new-fn) new-args)) + (setf new-fn (flic-app-fn new-fn))) + ) + (if (null? new-args) + new-fn + (begin + (setf (flic-app-fn object) new-fn) + (setf (flic-app-args object) new-args) + object)) + )) + +(define (guess-function-arity var) + (or (let ((value (var-value var))) + (and value + (is-type? 'flic-lambda value) + (length (flic-lambda-vars value)))) + (var-arity var))) + +(define (do-app-make-saturated app fn args arity nargs) + (declare (type fixnum arity nargs)) + (record-hack 'app-make-saturated fn args) + (let ((newvars '()) + (newargs '())) + (dotimes (i (- arity nargs)) + (declare (type fixnum i)) + (let ((v (init-flic-var (create-temp-var 'arg) '#f '#f))) + (push v newvars) + (push (make-flic-ref v) newargs))) + (setf (flic-app-fn app) fn) + (setf (flic-app-args app) (nconc args newargs)) + (make-flic-lambda newvars app))) + + + +;;; If the function is a selector applied to a literal dictionary, +;;; inline it. + +(define (try-to-fold-selector var new-fn new-args) + (let ((exp (car new-args))) + (if (or (and (is-type? 'flic-ref exp) + ;; *** should check that var is top-level? + (is-bound-to-constructor-app? (flic-ref-var exp))) + (and (is-type? 'flic-app exp) + (is-constructor-app-prim? exp))) + (begin + (record-hack 'app-fold-selector) + (setf new-fn (copy-flic-top (var-value var))) + (do-lambda-to-let-aux new-fn new-args)) + (values new-fn new-args)))) + + +;;; Various primitive functions have special optimizer functions +;;; associated with them, that do constant folding and certain +;;; other identities. The optimizer function is called with the +;;; function expression and list of argument expressions (at least +;;; as many arguments as the arity of the function) and should return +;;; the two values. + +;;; *** This should really use some kind of hash table, but we'd +;;; *** have to initialize the table dynamically because core-symbols +;;; *** aren't defined when this file is loaded. + +(define (magic-optimize-function var) + (cond ((eq? var (core-symbol "foldr")) + (function optimize-foldr-aux)) + ((eq? var (core-symbol "build")) + (function optimize-build)) + ((eq? var (core-symbol "primIntegerToInt")) + (function optimize-integer-to-int)) + ((eq? var (core-symbol "primIntToInteger")) + (function optimize-int-to-integer)) + ((eq? var (core-symbol "primRationalToFloat")) + (function optimize-rational-to-float)) + ((eq? var (core-symbol "primRationalToDouble")) + (function optimize-rational-to-double)) + ((or (eq? var (core-symbol "primNegInt")) + (eq? var (core-symbol "primNegInteger")) + (eq? var (core-symbol "primNegFloat")) + (eq? var (core-symbol "primNegDouble"))) + (function optimize-neg)) + (else + '#f))) + + +;;; Foldr identities for deforestation + +(define (optimize-foldr fn args) + (multiple-value-bind (fn args) + (optimize-foldr-aux fn args) + (maybe-make-app fn args))) + +(define (optimize-foldr-aux fn args) + (let ((k (car args)) + (z (cadr args)) + (l (caddr args)) + (tail (cdddr args))) + (cond ((and (is-type? 'flic-pack k) + (eq? (flic-pack-con k) (core-symbol ":")) + (is-type? 'flic-pack z) + (eq? (flic-pack-con z) (core-symbol "Nil"))) + ;; foldr (:) [] l ==> l + ;; (We arrange for build to be inlined before foldr + ;; so that this pattern can be detected.) + (record-hack 'foldr-cons-nil-identity) + (values l tail)) + ((and (is-type? 'flic-app l) + (is-type? 'flic-ref (flic-app-fn l)) + (eq? (flic-ref-var (flic-app-fn l)) + (core-symbol "build")) + (null? (cdr (flic-app-args l)))) + ;; foldr k z (build g) ==> g k z + (record-hack 'foldr-build-identity) + (values + (car (flic-app-args l)) + (cons k (cons z tail)))) + ((and (is-type? 'flic-pack l) + (eq? (flic-pack-con l) (core-symbol "Nil"))) + ;; foldr k z [] ==> z + (record-hack 'foldr-nil-identity) + (values z tail)) + ((short-string-constant? l) + ;; If the list argument is a string constant, expand it inline. + ;; Only do this if the string is fairly short, though. + (optimize-foldr-aux + fn + (cons k (cons z (cons (expand-string-constant l) tail))))) + ((and (is-type? 'flic-app l) + (is-type? 'flic-pack (flic-app-fn l)) + (eq? (flic-pack-con (flic-app-fn l)) (core-symbol ":")) + (eqv? (length (flic-app-args l)) 2)) + ;; foldr k z x:xs ==> let c = k in c x (foldr c z xs) + (record-hack 'foldr-cons-identity) + (let ((x (car (flic-app-args l))) + (xs (cadr (flic-app-args l)))) + (values + (if (can-inline? k '() '#f) + (do-foldr-cons-identity k z x xs) + (let ((cvar (init-flic-var (create-temp-var 'c) k '#f))) + (make-flic-let + (list cvar) + (do-foldr-cons-identity (make-flic-ref cvar) z x xs) + '#f))) + tail))) + ((is-type? 'flic-let l) + ;; foldr k z (let bindings in body) ==> + ;; let bindings in foldr k z body + (record-hack 'foldr-hoist-let) + (setf (flic-let-body l) + (optimize-foldr fn (list k z (flic-let-body l)))) + (values l tail)) + ((not (eqv? (the fixnum (dynamic *current-optimize-iteration*)) + (the fixnum (dynamic *optimize-foldr-iteration*)))) + ;; Hope for more optimizations later. + (values fn args)) + ((and (is-type? 'flic-pack k) + (eq? (flic-pack-con k) (core-symbol ":"))) + ;; Inline to special case, highly optimized append primitive. + ;; Could also look for (++ (++ l1 l2) l3) => (++ l1 (++ l2 l3)) + ;; here, but I don't think that happens very often. + (record-hack 'foldr-prim-append-inline) + (values + (make-flic-ref (core-symbol "primAppend")) + (cons l (cons z tail)))) + (else + ;; Default inline. + (record-hack 'foldr-inline k z) + (let ((new-fn + (copy-flic-top (var-value (core-symbol "inlineFoldr"))))) + (if (is-type? 'flic-lambda new-fn) + (do-lambda-to-let-aux new-fn args) + (values new-fn args)))) + ))) + + +;;; Mess with compile-time expansion of short string constants. + +(define-integrable max-short-string-length 3) + +(define (short-string-constant? l) + (and (is-type? 'flic-const l) + (let ((string (flic-const-value l))) + (and (string? string) + (<= (the fixnum (string-length string)) + (the fixnum max-short-string-length)))))) + +(define (expand-string-constant l) + (let* ((string (flic-const-value l)) + (length (string-length string))) + (expand-string-constant-aux string 0 length))) + +(define (expand-string-constant-aux string i length) + (declare (type fixnum i length)) + (if (eqv? i length) + (make-flic-pack (core-symbol "Nil")) + (make-flic-app + (make-flic-pack (core-symbol ":")) + (list (make-flic-const (string-ref string i)) + (expand-string-constant-aux string (+ 1 i) length)) + '#f))) + + +;;; Helper function for the case of expanding foldr applied to cons call. + +(define (do-foldr-cons-identity c z x xs) + (make-flic-app + c + (list x + (optimize-foldr + (make-flic-ref (core-symbol "foldr")) + (list (copy-flic-top c) z xs))) + '#f)) + + + +;;; Short-circuit build inlining for the usual case where the +;;; argument is a lambda. (It would take several optimizer passes +;;; for this simplification to fall out, otherwise.) + +(define (optimize-build fn args) + (let ((arg (car args))) + (cond ((not (eqv? (dynamic *current-optimize-iteration*) + (dynamic *optimize-build-iteration*))) + (values fn args)) + ((is-type? 'flic-lambda arg) + (record-hack 'build-inline-lambda) + (do-lambda-to-let-aux + arg + (cons (make-flic-pack (core-symbol ":")) + (cons (make-flic-pack (core-symbol "Nil")) + (cdr args))))) + (else + (record-hack 'build-inline-other) + (let ((new-fn + (copy-flic-top (var-value (core-symbol "inlineBuild"))))) + (if (is-type? 'flic-lambda new-fn) + (do-lambda-to-let-aux new-fn args) + (values new-fn args)))) + ))) + + +;;; Various simplifications on numeric functions. +;;; *** Obviously, could get much fancier about this. + +(define (optimize-integer-to-int fn args) + (let ((arg (car args))) + (cond ((is-type? 'flic-const arg) + (record-hack 'integer-to-int-constant-fold) + (if (is-type? 'integer (flic-const-value arg)) + (let ((value (flic-const-value arg))) + (when (not (is-type? 'fixnum value)) + ;; Overflow is a user error, not an implementation error. + (phase-error 'int-overflow + "Int overflow in primIntegerToInt: ~s" + value)) + (values arg (cdr args))) + (error "Bad argument ~s to primIntegerToInt." arg))) + ((and (is-type? 'flic-app arg) + (is-type? 'flic-ref (flic-app-fn arg)) + (eq? (flic-ref-var (flic-app-fn arg)) + (core-symbol "primIntToInteger")) + (null? (cdr (flic-app-args arg)))) + (record-hack 'integer-to-int-identity) + (values (car (flic-app-args arg)) (cdr args))) + (else + (values fn args))))) + +(define (optimize-int-to-integer fn args) + (let ((arg (car args))) + (cond ((is-type? 'flic-const arg) + (record-hack 'int-to-integer-constant-fold) + (if (is-type? 'integer (flic-const-value arg)) + (values arg (cdr args)) + (error "Bad argument ~s to primIntToInteger." arg))) + ((and (is-type? 'flic-app arg) + (is-type? 'flic-ref (flic-app-fn arg)) + (eq? (flic-ref-var (flic-app-fn arg)) + (core-symbol "primIntegerToInt")) + (null? (cdr (flic-app-args arg)))) + (record-hack 'int-to-integer-identity) + (values (car (flic-app-args arg)) (cdr args))) + (else + (values fn args))))) + +(predefine (prim.rational-to-float-aux n d)) ; in prims.scm +(predefine (prim.rational-to-double-aux n d)) ; in prims.scm + +(define (optimize-rational-to-float fn args) + (let ((arg (car args))) + (cond ((is-type? 'flic-const arg) + (record-hack 'rational-to-float-constant-fold) + (if (is-type? 'list (flic-const-value arg)) + (let ((value (flic-const-value arg))) + (setf (flic-const-value arg) + (prim.rational-to-float-aux (car value) (cadr value))) + (values arg (cdr args))) + (error "Bad argument ~s to primRationalToFloat." arg))) + (else + (values fn args))))) + +(define (optimize-rational-to-double fn args) + (let ((arg (car args))) + (cond ((is-type? 'flic-const arg) + (record-hack 'rational-to-double-constant-fold) + (if (is-type? 'list (flic-const-value arg)) + (let ((value (flic-const-value arg))) + (setf (flic-const-value arg) + (prim.rational-to-double-aux (car value) (cadr value))) + (values arg (cdr args))) + (error "Bad argument ~s to primRationalToDouble." arg))) + (else + (values fn args))))) + +(define (optimize-neg fn args) + (let ((arg (car args))) + (cond ((is-type? 'flic-const arg) + (record-hack 'neg-constant-fold) + (if (is-type? 'number (flic-const-value arg)) + (begin + (setf (flic-const-value arg) (- (flic-const-value arg))) + (values arg (cdr args))) + (error "Bad argument ~s to ~s." arg fn))) + (else + (values fn args))))) + + + +;;; Convert lambda applications to lets. +;;; If application is not saturated, break it up into two nested +;;; lambdas before doing the transformation. +;;; It's better to do this optimization immediately than hoping +;;; the call will become fully saturated on the next pass. +;;; Maybe we could also look for a flic-let with a flic-lambda as +;;; the body to catch the cases where additional arguments can +;;; be found on a later pass. + +(define (do-lambda-to-let new-fn new-args) + (multiple-value-bind (fn args) + (do-lambda-to-let-aux new-fn new-args) + (maybe-make-app fn args))) + +(define (maybe-make-app fn args) + (if (null? args) + fn + (make-flic-app fn args '#f))) + +(define (do-lambda-to-let-aux new-fn new-args) + (let ((vars (flic-lambda-vars new-fn)) + (body (flic-lambda-body new-fn)) + (matched '())) + (record-hack 'app-lambda-to-let) + (do () + ((or (null? new-args) (null? vars))) + (let ((var (pop vars)) + (arg (pop new-args))) + (setf (var-value var) arg) + (setf (var-simple? var) (can-inline? arg '() '#t)) + (if (eqv? (var-referenced var) 1) + (setf (var-single-ref var) (dynamic *lambda-depth*))) + (push var matched))) + (setf matched (nreverse matched)) + (if (not (null? vars)) + (setf body (make-flic-lambda vars body))) + (setf new-fn (make-flic-let matched body '#f)) + (values new-fn new-args))) + + +;;; For references, check to see if we can beta-reduce. +;;; Don't increment reference count for inlineable vars, but do +;;; traverse the new value expression. + +(define-optimize flic-ref (object) + (optimize-flic-ref-aux object)) + +(define (optimize-flic-ref-aux object) + (let ((var (flic-ref-var object))) + (cond ((var-single-ref var) + ;; (or (eqv? (var-single-ref var) (dynamic *lambda-depth*))) + ;; *** The lambda-depth test is too conservative to handle + ;; *** inlining of stuff necessary for foldr/build optimizations. + ;; Can substitute value no matter how hairy it is. + ;; Note that this is potentially risky; if the single + ;; reference detected on the previous pass appeared as + ;; the value of a variable binding that is being inlined + ;; on the current pass, it might turn into multiple + ;; references again! + ;; We copy the value anyway to avoid problems with shared + ;; structure in the multiple reference case. + (record-hack 'ref-inline-single-ref var) + (optimize (copy-flic-top (var-value var)))) + ((and (var-inline-value var) (dynamic *do-inline-optimizations*)) + ;; Use the previously saved value in preference to the current + ;; value of the variable. + (record-hack 'ref-inline-foldr-hack) + (optimize (copy-flic-top (var-inline-value var)))) + ((and (var-simple? var) + (or (dynamic *do-inline-optimizations*) + (not (var-toplevel? var)))) + ;; Can substitute, but must copy. + (record-hack 'ref-inline var) + (optimize (copy-flic-top (var-value var)))) + ((eq? var (core-symbol "foldr")) + ;; Magic stuff for deforestation + (if (> (the fixnum (dynamic *current-optimize-iteration*)) + (the fixnum (dynamic *optimize-foldr-iteration*))) + (begin + (record-hack 'ref-inline-foldr) + (optimize (make-flic-ref (core-symbol "inlineFoldr")))) + object)) + ((eq? var (core-symbol "build")) + ;; Magic stuff for deforestation + (if (> (the fixnum (dynamic *current-optimize-iteration*)) + (the fixnum (dynamic *optimize-build-iteration*))) + (begin + (record-hack 'ref-inline-build) + (optimize (make-flic-ref (core-symbol "inlineBuild")))) + object)) + ((var-toplevel? var) + object) + (else + (incf (var-referenced var)) + object)))) + + +;;; Don't do anything exciting with constants. + +(define-optimize flic-const (object) + object) + +(define-optimize flic-pack (object) + object) + + + +;;; Various simplifications on and + +(define-optimize flic-and (object) + (maybe-simplify-and + object + (optimize-and-exps (flic-and-exps object) '()))) + +(define (maybe-simplify-and object exps) + (cond ((null? exps) + (record-hack 'and-empty) + (make-flic-pack (core-symbol "True"))) + ((null? (cdr exps)) + (record-hack 'and-unary) + (car exps)) + (else + (setf (flic-and-exps object) exps) + object))) + +(define (optimize-and-exps exps result) + (if (null? exps) + (nreverse result) + (let ((exp (optimize (car exps)))) + (typecase exp + (flic-pack + (cond ((eq? (flic-pack-con exp) (core-symbol "True")) + ;; True appears in subexpressions. + ;; Discard this test only. + (record-hack 'and-contains-true) + (optimize-and-exps (cdr exps) result)) + ((eq? (flic-pack-con exp) (core-symbol "False")) + ;; False appears in subexpressions. + ;; Discard remaining tests as dead code. + ;; Can't replace the whole and expression with false because + ;; of possible strictness side-effects. + (record-hack 'and-contains-false) + (nreverse (cons exp result))) + (else + ;; Should never happen. + (error "Non-boolean con ~s in and expression!" exp)))) + (flic-and + ;; Flatten nested ands. + (record-hack 'and-compress) + (optimize-and-exps + (cdr exps) + (nconc (nreverse (flic-and-exps exp)) result))) + (else + ;; No optimization possible. + (optimize-and-exps (cdr exps) (cons exp result))) + )))) + + +;;; Case-block optimizations. These optimizations are possible because +;;; of the restricted way this construct is used; return-froms are +;;; never nested, etc. + +(define-optimize flic-case-block (object) + (let* ((sym (flic-case-block-block-name object)) + (exps (optimize-case-block-exps + sym (flic-case-block-exps object) '()))) + (optimize-flic-case-block-aux object sym exps))) + +(define (optimize-flic-case-block-aux object sym exps) + (cond ((null? exps) + ;; This should never happen. It means all of the tests were + ;; optimized away, including the failure case! + (error "No exps left in case block ~s!" object)) + ((and (is-type? 'flic-and (car exps)) + (is-return-from-block? + sym + (car (last (flic-and-exps (car exps)))))) + ;; The first clause is a simple and. Hoist it out of the + ;; case-block and rewrite as if/then/else. + (record-hack 'case-block-to-if) + (let ((then-exp (car (last (flic-and-exps (car exps)))))) + (setf (flic-case-block-exps object) (cdr exps)) + (make-flic-if + (maybe-simplify-and + (car exps) + (butlast (flic-and-exps (car exps)))) + (flic-return-from-exp then-exp) + (optimize-flic-case-block-aux object sym (cdr exps))))) + ((is-return-from-block? sym (car exps)) + ;; Do an identity reduction. + (record-hack 'case-block-identity) + (flic-return-from-exp (car exps))) + ((is-type? 'flic-let (car exps)) + ;; The first clause is a let. Since this clause is going + ;; to be executed anyway, hoisting the bindings to surround + ;; the entire case-block should not change their strictness + ;; properties, and it may permit some further optimizations. + (record-hack 'case-block-hoist-let) + (let* ((exp (car exps)) + (body (flic-let-body exp))) + (setf (flic-let-body exp) + (optimize-flic-case-block-aux + object sym (cons body (cdr exps)))) + exp)) + (else + (setf (flic-case-block-exps object) exps) + object) + )) + + +(define (optimize-case-block-exps sym exps result) + (if (null? exps) + (nreverse result) + (let ((exp (optimize (car exps)))) + (cond ((is-return-from-block? sym exp) + ;; Any remaining clauses are dead code and should be removed. + (if (not (null? (cdr exps))) + (record-hack 'case-block-dead-code)) + (nreverse (cons exp result))) + ((is-type? 'flic-and exp) + ;; See if we can remove redundant tests. + (push (maybe-simplify-and + exp + (look-for-redundant-tests (flic-and-exps exp) result)) + result) + (optimize-case-block-exps sym (cdr exps) result)) + (else + ;; No optimization possible. + (optimize-case-block-exps sym (cdr exps) (cons exp result))) + )))) + + +;;; Look for case-block tests that are known to be either true or false +;;; because of tests made in previous clauses. +;;; For now, we only look at is-constructor tests. Such a test is known +;;; to be true if previous clauses have eliminated all other possible +;;; constructors. And such a test is known to be false if a previous +;;; clause has already matched this constructor. + +(define (look-for-redundant-tests exps previous-clauses) + (if (null? exps) + '() + (let ((exp (car exps))) + (cond ((and (is-type? 'flic-is-constructor exp) + (constructor-test-redundant? exp previous-clauses)) + ;; Known to be true. + (record-hack 'case-block-discard-redundant-test) + (cons (make-flic-pack (core-symbol "True")) + (look-for-redundant-tests (cdr exps) previous-clauses))) + + ((and (is-type? 'flic-is-constructor exp) + (constructor-test-duplicated? exp previous-clauses)) + ;; Known to be false. + (record-hack 'case-block-discard-duplicate-test) + (list (make-flic-pack (core-symbol "False")))) + (else + ;; No optimization. + (cons exp + (look-for-redundant-tests (cdr exps) previous-clauses))) + )))) + + +;;; In looking for redundant/duplicated tests, only worry about +;;; is-constructor tests that have an argument that is a variable. +;;; It's too hairy to consider any other cases. + +(define (constructor-test-duplicated? exp previous-clauses) + (let ((con (flic-is-constructor-con exp)) + (arg (flic-is-constructor-exp exp))) + (and (is-type? 'flic-ref arg) + (constructor-test-present? con arg previous-clauses)))) + +(define (constructor-test-redundant? exp previous-clauses) + (let ((con (flic-is-constructor-con exp)) + (arg (flic-is-constructor-exp exp))) + (and (is-type? 'flic-ref arg) + (every-1 (lambda (c) + (or (eq? c con) + (constructor-test-present? c arg previous-clauses))) + (algdata-constrs (con-alg con)))))) + +(define (constructor-test-present? con arg previous-clauses) + (cond ((null? previous-clauses) + '#f) + ((constructor-test-present-1? con arg (car previous-clauses)) + '#t) + (else + (constructor-test-present? con arg (cdr previous-clauses))))) + + +;;; The tricky thing here is that, even if the constructor test is +;;; present in the clause, we have to make sure that the entire clause won't +;;; fail due to the presence of some other test which fails. So look +;;; for a very specific pattern here, namely +;;; (and (is-constructor con arg) (return-from ....)) + +(define (constructor-test-present-1? con arg clause) + (and (is-type? 'flic-and clause) + (let ((exps (flic-and-exps clause))) + (and (is-type? 'flic-is-constructor (car exps)) + (is-type? 'flic-return-from (cadr exps)) + (null? (cddr exps)) + (let* ((inner-exp (car exps)) + (inner-con (flic-is-constructor-con inner-exp)) + (inner-arg (flic-is-constructor-exp inner-exp))) + (and (eq? inner-con con) + (flic-exp-eq? arg inner-arg))))))) + + + +;;; No fancy optimizations for return-from by itself. + +(define-optimize flic-return-from (object) + (setf (flic-return-from-exp object) + (optimize (flic-return-from-exp object))) + object) + + + +;;; Obvious simplification on if + +(define-optimize flic-if (object) + (let ((test-exp (optimize (flic-if-test-exp object))) + (then-exp (optimize (flic-if-then-exp object))) + (else-exp (optimize (flic-if-else-exp object)))) + (cond ((and (is-type? 'flic-pack test-exp) + (eq? (flic-pack-con test-exp) (core-symbol "True"))) + ;; Fold constant test + (record-hack 'if-fold) + then-exp) + ((and (is-type? 'flic-pack test-exp) + (eq? (flic-pack-con test-exp) (core-symbol "False"))) + ;; Fold constant test + (record-hack 'if-fold) + else-exp) + ((and (is-type? 'flic-is-constructor test-exp) + (eq? (flic-is-constructor-con test-exp) (core-symbol "True"))) + ;; Remove redundant is-constructor test. + ;; Doing this as a general is-constructor identity + ;; backfires because it prevents some of the important case-block + ;; optimizations from being recognized, but it works fine here. + (record-hack 'if-compress-test) + (setf (flic-if-test-exp object) (flic-is-constructor-exp test-exp)) + (setf (flic-if-then-exp object) then-exp) + (setf (flic-if-else-exp object) else-exp) + object) + ((and (is-type? 'flic-is-constructor test-exp) + (eq? (flic-is-constructor-con test-exp) (core-symbol "False"))) + ;; Remove redundant is-constructor test, flip branches. + (record-hack 'if-compress-test) + (setf (flic-if-test-exp object) (flic-is-constructor-exp test-exp)) + (setf (flic-if-then-exp object) else-exp) + (setf (flic-if-else-exp object) then-exp) + object) + ((and (is-type? 'flic-return-from then-exp) + (is-type? 'flic-return-from else-exp) + (eq? (flic-return-from-block-name then-exp) + (flic-return-from-block-name else-exp))) + ;; Hoist return-from outside of IF. + ;; This may permit further case-block optimizations. + (record-hack 'if-hoist-return-from) + (let ((return-exp then-exp)) + (setf (flic-if-test-exp object) test-exp) + (setf (flic-if-then-exp object) (flic-return-from-exp then-exp)) + (setf (flic-if-else-exp object) (flic-return-from-exp else-exp)) + (setf (flic-return-from-exp return-exp) object) + return-exp)) + ((and (is-type? 'flic-pack then-exp) + (is-type? 'flic-pack else-exp) + (eq? (flic-pack-con then-exp) (core-symbol "True")) + (eq? (flic-pack-con else-exp) (core-symbol "False"))) + ;; This if does nothing useful at all! + (record-hack 'if-identity) + test-exp) + ((and (is-type? 'flic-pack then-exp) + (is-type? 'flic-pack else-exp) + (eq? (flic-pack-con then-exp) (core-symbol "False")) + (eq? (flic-pack-con else-exp) (core-symbol "True"))) + ;; Inverse of previous case + (record-hack 'if-identity-inverse) + (make-flic-is-constructor (core-symbol "False") test-exp)) + ((or (is-type? 'flic-lambda then-exp) + (is-type? 'flic-lambda else-exp)) + ;; Hoist lambdas to surround entire if. This allows us to + ;; do a better job of saturating them. + (record-hack 'if-hoist-lambda) + (multiple-value-bind (vars then-exp else-exp) + (do-if-hoist-lambda then-exp else-exp) + (setf (flic-if-test-exp object) test-exp) + (setf (flic-if-then-exp object) then-exp) + (setf (flic-if-else-exp object) else-exp) + (make-flic-lambda vars object))) + (else + ;; No optimization possible + (setf (flic-if-test-exp object) test-exp) + (setf (flic-if-then-exp object) then-exp) + (setf (flic-if-else-exp object) else-exp) + object) + ))) + + + +;;; Try to pull as many variables as possible out to surround the entire +;;; let. + +(define (do-if-hoist-lambda then-exp else-exp) + (let ((vars '()) + (then-args '()) + (else-args '())) + (do ((then-vars (if (is-type? 'flic-lambda then-exp) + (flic-lambda-vars then-exp) + '()) + (cdr then-vars)) + (else-vars (if (is-type? 'flic-lambda else-exp) + (flic-lambda-vars else-exp) + '()) + (cdr else-vars))) + ((and (null? then-vars) (null? else-vars)) '#f) + (let ((var (init-flic-var (create-temp-var 'arg) '#f '#f))) + (push var vars) + (push (make-flic-ref var) then-args) + (push (make-flic-ref var) else-args))) + (values + vars + (if (is-type? 'flic-lambda then-exp) + (do-lambda-to-let then-exp then-args) + (make-flic-app then-exp then-args '#f)) + (if (is-type? 'flic-lambda else-exp) + (do-lambda-to-let else-exp else-args) + (make-flic-app else-exp else-args '#f))))) + + + +;;; Look for (sel (pack x)) => x + +(define-optimize flic-sel (object) + (optimize-flic-sel-aux object)) + +(define (optimize-flic-sel-aux object) + (let ((new-exp (optimize (flic-sel-exp object)))) + (setf (flic-sel-exp object) new-exp) + (typecase new-exp + (flic-ref + ;; Check to see whether this is bound to a pack application + (let ((val (is-bound-to-constructor-app? (flic-ref-var new-exp)))) + (if val + ;; Yup, it is. Now extract the appropriate component, + ;; provided it is inlineable. + (let* ((i (flic-sel-i object)) + (args (flic-app-args val)) + (newval (list-ref args i))) + (if (can-inline? newval '() '#t) + (begin + (record-hack 'sel-fold-var) + (optimize (copy-flic-top newval))) + object)) + ;; The variable was bound to something else. + object))) + (flic-app + ;; The obvious optimization. + (if (is-constructor-app-prim? new-exp) + (begin + (record-hack 'sel-fold-app) + (list-ref (flic-app-args new-exp) (flic-sel-i object))) + object)) + (else + object)))) + + + + +;;; Do similar stuff for is-constructor. + +(define-optimize flic-is-constructor (object) + (let ((con (flic-is-constructor-con object)) + (exp (optimize (flic-is-constructor-exp object))) + (exp-con '#f)) + (cond ((algdata-tuple? (con-alg con)) + ;; Tuples have only one constructor, so this is always true + (record-hack 'is-constructor-fold-tuple) + (make-flic-pack (core-symbol "True"))) + ((setf exp-con (is-constructor-app? exp)) + ;; The expression is a constructor application. + (record-hack 'is-constructor-fold) + (make-flic-pack + (if (eq? exp-con con) + (core-symbol "True") + (core-symbol "False")))) + (else + ;; No optimization possible + (setf (flic-is-constructor-exp object) exp) + object) + ))) + + +(define-optimize flic-con-number (object) + (let ((exp (flic-con-number-exp object)) + (type (flic-con-number-type object))) + ;; ***Maybe ast-to-flic should look for this one. + (if (algdata-tuple? type) + (begin + (record-hack 'con-number-fold-tuple) + (make-flic-const 0)) + (let* ((new-exp (optimize exp)) + (con (is-constructor-app? new-exp))) + (if con + (begin + (record-hack 'con-number-fold) + (make-flic-const (con-tag con))) + (begin + (setf (flic-con-number-exp object) new-exp) + object))) + ))) + +(define-optimize flic-void (object) + object) + + +;;;=================================================================== +;;; General helper functions +;;;=================================================================== + + +;;; Lucid's built-in every function seems to do a lot of unnecessary +;;; consing. This one is much faster. + +(define (every-1 fn list) + (cond ((null? list) + '#t) + ((funcall fn (car list)) + (every-1 fn (cdr list))) + (else + '#f))) + + + +;;; Equality predicate on flic expressions + +(define (flic-exp-eq? a1 a2) + (typecase a1 + (flic-const + (and (is-type? 'flic-const a2) + (equal? (flic-const-value a1) (flic-const-value a2)))) + (flic-ref + (and (is-type? 'flic-ref a2) + (eq? (flic-ref-var a1) (flic-ref-var a2)))) + (flic-pack + (and (is-type? 'flic-pack a2) + (eq? (flic-pack-con a1) (flic-pack-con a2)))) + (flic-sel + (and (is-type? 'flic-sel a2) + (eq? (flic-sel-con a1) (flic-sel-con a2)) + (eqv? (flic-sel-i a1) (flic-sel-i a2)) + (flic-exp-eq? (flic-sel-exp a1) (flic-sel-exp a2)))) + (else + '#f))) + + + +;;; Predicates for testing whether an expression matches a pattern. + +(define (is-constructor-app? exp) + (typecase exp + (flic-app + ;; See if we have a saturated call to a constructor. + (is-constructor-app-prim? exp)) + (flic-ref + ;; See if we can determine anything about the value the variable + ;; is bound to. + (let ((value (var-value (flic-ref-var exp)))) + (if value + (is-constructor-app? value) + '#f))) + (flic-let + ;; See if we can determine anything about the body of the let. + (is-constructor-app? (flic-let-body exp))) + (flic-pack + ;; See if this is a nullary constructor. + (let ((con (flic-pack-con exp))) + (if (eqv? (con-arity con) 0) + con + '#f))) + (else + '#f))) + +(define (is-return-from-block? sym exp) + (and (is-type? 'flic-return-from exp) + (eq? (flic-return-from-block-name exp) sym))) + +(define (is-constructor-app-prim? exp) + (let ((fn (flic-app-fn exp)) + (args (flic-app-args exp))) + (if (and (is-type? 'flic-pack fn) + (eqv? (length args) (con-arity (flic-pack-con fn)))) + (flic-pack-con fn) + '#f))) + +(define (is-bound-to-constructor-app? var) + (let ((val (var-value var))) + (if (and val + (is-type? 'flic-app val) + (is-constructor-app-prim? val)) + val + '#f))) + +(define (is-selector? con i exp) + (or (and (is-type? 'flic-ref exp) + (is-selector? con i (var-value (flic-ref-var exp)))) + (and (is-type? 'flic-sel exp) + (eq? (flic-sel-con exp) con) + (eqv? (the fixnum i) (the fixnum (flic-sel-i exp))) + (flic-sel-exp exp)) + )) + +(define (is-selector-list? con i subexp exps) + (declare (type fixnum i)) + (if (null? exps) + subexp + (let ((temp (is-selector? con i (car exps)))) + (and (flic-exp-eq? subexp temp) + (is-selector-list? con (+ 1 i) subexp (cdr exps)))))) + + + +;;;=================================================================== +;;; Inlining criteria +;;;=================================================================== + +;;; Expressions that can be inlined unconditionally are constants, variable +;;; references, and some functions. +;;; I've made some attempt here to arrange the cases in the order they +;;; are likely to occur. + +(define (can-inline? exp recursive-vars toplevel?) + (typecase exp + (flic-sel + ;; Listed first because it happens more frequently than + ;; anything else. + ;; *** Inlining these is an experiment. + ;; *** This transformation interacts with the strictness + ;; *** analyzer; if the variable referenced is not strict, then + ;; *** it is probably not a good thing to do since it adds extra + ;; *** forces. + ;; (let ((subexp (flic-sel-exp exp))) + ;; (and (is-type? 'flic-ref subexp) + ;; (not (memq (flic-ref-var subexp) recursive-vars)))) + '#f) + (flic-lambda + ;; Do not try to inline lambdas if the fancy inline optimization + ;; is disabled. + ;; Watch for problems with infinite loops with recursive variables. + (if (dynamic *do-inline-optimizations*) + (simple-function-body? (flic-lambda-body exp) + (flic-lambda-vars exp) + recursive-vars + toplevel?) + '#f)) + (flic-ref + ;; We get into infinite loops trying to inline recursive variables. + (not (memq (flic-ref-var exp) recursive-vars))) + ((or flic-pack flic-const) + '#t) + (else + '#f))) + + +;;; Determining whether to inline a function is difficult. This is +;;; very conservative to avoid code bloat. What we need to do is +;;; compare the cost (in program size mainly) of the inline call with +;;; an out of line call. For an out of line call, we pay for one function +;;; call and a setup for each arg. When inlining, we pay for function +;;; calls in the body and for args referenced more than once. In terms of +;;; execution time, we win big when a functional parameter is called +;;; since this `firstifies' the program. + +;;; Here's the criteria: +;;; An inline function gets to reference no more that 2 non-parameter +;;; values (including constants and repeated parameter references). +;;; For non-toplevel functions, be slightly more generous since the +;;; fixed overhead of binding the local function would go away. + +(define (simple-function-body? exp lambda-vars recursive-vars toplevel?) + (let ((c (if toplevel? 2 4))) + (>= (the fixnum (simple-function-body-1 exp lambda-vars recursive-vars c)) + 0))) + + +;;; I've made some attempt here to order the cases by how frequently +;;; they appear. + +(define (simple-function-body-1 exp lambda-vars recursive-vars c) + (declare (type fixnum c)) + (if (< c 0) + (values c '()) + (typecase exp + (flic-ref + (let ((var (flic-ref-var exp))) + (cond ((memq var lambda-vars) + (values c (list-remove-1 var lambda-vars))) + ((memq var recursive-vars) + (values -1 '())) + (else + (values (the fixnum (1- c)) lambda-vars))))) + (flic-app + (simple-function-body-1/l + (cons (flic-app-fn exp) (flic-app-args exp)) + lambda-vars recursive-vars c)) + (flic-sel + (simple-function-body-1 + (flic-sel-exp exp) + lambda-vars recursive-vars (the fixnum (1- c)))) + (flic-is-constructor + (simple-function-body-1 + (flic-is-constructor-exp exp) + lambda-vars recursive-vars (the fixnum (1- c)))) + ((or flic-const flic-pack) + (values (the fixnum (1- c)) lambda-vars)) + (else + ;; case & let & lambda not allowed. + (values -1 '()))))) + +(define (list-remove-1 item list) + (cond ((null? list) + '()) + ((eq? item (car list)) + (cdr list)) + (else + (cons (car list) (list-remove-1 item (cdr list)))) + )) + +(define (simple-function-body-1/l exps lambda-vars recursive-vars c) + (declare (type fixnum c)) + (if (or (null? exps) (< c 0)) + (values c lambda-vars) + (multiple-value-bind (c-1 lambda-vars-1) + (simple-function-body-1 (car exps) lambda-vars recursive-vars c) + (simple-function-body-1/l + (cdr exps) lambda-vars-1 recursive-vars c-1)))) + + + +;;;=================================================================== +;;; Constant structured data detection +;;;=================================================================== + + +;;; Look to determine whether an object is a structured constant, +;;; recursively examining its components if it's an app. This is +;;; necessary in order to detect constants with arbitrary circular +;;; reference to the vars in recursive-vars. + +(define (structured-constant-recursive? object recursive-vars stack) + (typecase object + (flic-const + '#t) + (flic-ref + (let ((var (flic-ref-var object))) + (or (memq var stack) + (var-toplevel? var) + (and (memq var recursive-vars) + (structured-constant-recursive? + (var-value var) recursive-vars (cons var stack)))))) + (flic-pack + '#t) + (flic-app + (structured-constant-app-recursive? + (flic-app-fn object) + (flic-app-args object) + recursive-vars + stack)) + (flic-lambda + (lambda-hoistable? object)) + (else + '#f))) + +(define (structured-constant-app-recursive? fn args recursive-vars stack) + (and (is-type? 'flic-pack fn) + (eqv? (length args) (con-arity (flic-pack-con fn))) + (every-1 (lambda (a) + (structured-constant-recursive? a recursive-vars stack)) + args))) + + +;;; Here's a non-recursive (and more efficient) version of the above. +;;; Instead of looking at the whole structure, it only looks one level +;;; deep. This can't detect circular constants, but is useful in +;;; contexts where circularities cannot appear. + +(define (structured-constant? object) + (typecase object + (flic-ref + (var-toplevel? (flic-ref-var object))) + (flic-const + '#t) + (flic-pack + '#t) + (flic-lambda + (lambda-hoistable? object)) + (else + '#f))) + +(define (structured-constant-app? fn args) + (and (is-type? 'flic-pack fn) + (eqv? (length args) (con-arity (flic-pack-con fn))) + (every-1 (function structured-constant?) args))) + + +;;; Determine whether a lambda can be hoisted to top-level. +;;; The main purpose of this code is to mark structured constants +;;; containing simple lambdas to permit later folding of sel expressions +;;; on those constants. Since the latter expression is permissible +;;; only on inlinable functions, stop if we hit an expression that +;;; would make the function not inlinable. + +(define (lambda-hoistable? object) + (and (can-inline? object '() '#t) + (lambda-hoistable-aux + (flic-lambda-body object) + (flic-lambda-vars object)))) + +(define (lambda-hoistable-aux object local-vars) + (typecase object + (flic-ref + (or (var-toplevel? (flic-ref-var object)) + (memq (flic-ref-var object) local-vars))) + ((or flic-const flic-pack) + '#t) + (flic-sel + (lambda-hoistable-aux (flic-sel-exp object) local-vars)) + (flic-is-constructor + (lambda-hoistable-aux (flic-is-constructor-exp object) local-vars)) + (flic-app + (and (lambda-hoistable-aux (flic-app-fn object) local-vars) + (every-1 (lambda (x) (lambda-hoistable-aux x local-vars)) + (flic-app-args object)))) + (else + '#f))) + + +;;; Having determined that something is a structured constant, +;;; enter it (and possibly its subcomponents) in the hash table +;;; and return a var-ref. + +(define (enter-structured-constant value recursive?) + (multiple-value-bind (con args var) + (enter-structured-constant-aux value recursive?) + (when (not var) + (setf var (create-temp-var 'constant)) + (add-new-structured-constant var con args)) + (make-flic-ref var))) + +(define (enter-structured-constant-aux value recursive?) + (let* ((fn (flic-app-fn value)) + (con (flic-pack-con fn)) + (args (if recursive? + (map (function enter-structured-constant-arg) + (flic-app-args value)) + (flic-app-args value)))) + (values con args (lookup-structured-constant con args)))) + +(define (enter-structured-constant-arg a) + (if (is-type? 'flic-app a) + (enter-structured-constant a '#t) + a)) + +(define (lookup-structured-constant con args) + (lookup-structured-constant-aux + (table-entry *structured-constants-table* con) args)) + +(define (lookup-structured-constant-aux alist args) + (cond ((null? alist) + '#f) + ((every (function flic-exp-eq?) (car (car alist)) args) + (cdr (car alist))) + (else + (lookup-structured-constant-aux (cdr alist) args)))) + +(define (add-new-structured-constant var con args) + (push (cons args var) (table-entry *structured-constants-table* con)) + (setf (var-toplevel? var) '#t) + (setf (var-value var) (make-flic-app (make-flic-pack con) args '#t)) + (push var *structured-constants*) + var) + + + +;;;=================================================================== +;;; Invariant argument stuff +;;;=================================================================== + + +;;; When processing a saturated call to a locally defined function, +;;; note whether any of the arguments are always passed the same value. + +(define (note-invariant-args args vars) + (when (and (not (null? args)) (not (null? vars))) + (let* ((arg (car args)) + (var (car vars)) + (val (var-arg-invariant-value var))) + (cond ((not (var-arg-invariant? var)) + ;; This argument already marked as having more than one + ;; value. + ) + ((and (is-type? 'flic-ref arg) + (eq? (flic-ref-var arg) var)) + ;; This is a recursive call with the same argument. + ;; Don't update the arg-invariant-value slot. + ) + ((or (not val) + (flic-exp-eq? arg val)) + ;; Either this is the first call, or a second call with + ;; the same argument. + (setf (var-arg-invariant-value var) arg)) + (else + ;; Different values for this argument are passed in + ;; different places, so we can't mess with it. + (setf (var-arg-invariant? var) '#f))) + (note-invariant-args (cdr args) (cdr vars))))) + + +;;; After processing a let form, check to see if any of the bindings +;;; are for local functions with invariant arguments. +;;; Suppose we have something like +;;; let foo = \ x y z -> +;;; in +;;; and y is known to be invariant; then we rewrite this as +;;; let foo1 = \ x z -> let y = in +;;; foo = \ x1 y1 z1 -> foo1 x1 z1 +;;; in +;;; The original foo binding is inlined on subsequent passes and +;;; should go away. Likewise, the binding of y should be inlined also. +;;; *** This is kind of bogus because of the way it depends on the +;;; *** magic force-inline bit. It would be better to do a code walk +;;; *** now on the entire let expression to rewrite all the calls to foo. + +(define (add-stuff-for-invariants bindings) + (if (null? bindings) + '() + (let* ((var (car bindings)) + (val (var-value var))) + (setf (cdr bindings) + (add-stuff-for-invariants (cdr bindings))) + (if (and (is-type? 'flic-lambda val) + ;; Don't mess with single-reference variable bindings, + ;; or things we are going to inline anyway. + (not (var-single-ref var)) + (not (var-simple? var)) + ;; All references must be in saturated calls to do this. + (eqv? (var-referenced var) (var-fn-referenced var)) + ;; There is at least one argument marked invariant. + (some (function var-arg-invariant?) (flic-lambda-vars val)) + ;; Every argument marked invariant must also be hoistable. + (every-1 (function arg-hoistable?) (flic-lambda-vars val))) + (hoist-invariant-args + var + val + bindings) + bindings)))) + +(define (arg-hoistable? var) + (if (var-arg-invariant? var) + (or (not (var-arg-invariant-value var)) + (flic-invariant? (var-arg-invariant-value var) + (dynamic *local-bindings*))) + '#t)) + +(define (hoist-invariant-args var val bindings) + (let ((foo1-var (copy-temp-var (def-name var))) + (foo1-def-vars '()) + (foo1-app-args '()) + (foo1-let-vars '()) + (foo-def-vars '())) + (push foo1-var bindings) + (dolist (v (flic-lambda-vars val)) + (let ((new-v (copy-temp-var (def-name v)))) + (push (init-flic-var new-v '#f '#f) foo-def-vars) + (if (var-arg-invariant? v) + (when (var-arg-invariant-value v) + (push (init-flic-var + v (copy-flic-top (var-arg-invariant-value v)) '#f) + foo1-let-vars)) + (begin + (push v foo1-def-vars) + (push (make-flic-ref new-v) foo1-app-args)) + ))) + (setf foo1-def-vars (nreverse foo1-def-vars)) + (setf foo1-app-args (nreverse foo1-app-args)) + (setf foo1-let-vars (nreverse foo1-let-vars)) + (setf foo-def-vars (nreverse foo-def-vars)) + (record-hack 'let-hoist-invariant-args var foo1-let-vars) + ;; Fix up the value of foo1 + (init-flic-var + foo1-var + (let ((body (make-flic-let foo1-let-vars (flic-lambda-body val) '#f))) + (if (null? foo1-def-vars) + ;; *All* of the arguments were invariant. + body + ;; Otherwise, make a new lambda + (make-flic-lambda foo1-def-vars body))) + '#f) + ;; Fix up the value of foo and arrange for it to be inlined. + (setf (flic-lambda-vars val) foo-def-vars) + (setf (flic-lambda-body val) + (if (null? foo1-app-args) + (make-flic-ref foo1-var) + (make-flic-app (make-flic-ref foo1-var) foo1-app-args '#t))) + (setf (var-simple? var) '#t) + (setf (var-force-inline? var) '#t) + ;; Return modified list of bindings + bindings)) + + + +;;;=================================================================== +;;; Install globals +;;;=================================================================== + + +;;; The optimizer, CFN, etc. can introduce new top-level variables that +;;; are not installed in the symbol table. This causes problems if +;;; those variables are referenced in the .hci file (as in the inline +;;; expansion of some other variables). So we need to fix up the +;;; symbol table before continuing. + +(define (install-uninterned-globals vars) + (dolist (v vars) + (let* ((module (locate-module (def-module v))) + (name (def-name v)) + (table (module-symbol-table module)) + (def (table-entry table name))) + (cond ((not def) + ;; This def was not installed. Rename it if it's a gensym + ;; and install it. + (when (gensym? name) + (setf name (rename-gensym-var v name table))) + (setf (table-entry table name) v)) + ((eq? def v) + ;; Already installed. + '#t) + (else + ;; Ooops! The symbol installed in the symbol table isn't + ;; this one! + (error "Duplicate defs ~s and ~s in symbol table for ~s!" + v def module)) + )))) + + +(define (rename-gensym-var var name table) + (setf name (string->symbol (symbol->string name))) + (if (table-entry table name) + ;; This name already in use; gensym a new one! + (rename-gensym-var var (gensym (symbol->string name)) table) + ;; OK, no problem + (setf (def-name var) name))) + + + +;;;=================================================================== +;;; Postoptimizer +;;;=================================================================== + +;;; This is another quick traversal of the structure to determine +;;; whether references to functions are fully saturated or not. +;;; Also makes sure that reference counts on variables are correct; +;;; this is needed so the code generator can generate ignore declarations +;;; for unused lambda variables. + +(define-flic-walker postoptimize (object)) + +(define-postoptimize flic-lambda (object) + (dolist (var (flic-lambda-vars object)) + (setf (var-referenced var) 0)) + (postoptimize (flic-lambda-body object))) + +(define-postoptimize flic-let (object) + (dolist (var (flic-let-bindings object)) + (setf (var-referenced var) 0) + (let ((val (var-value var))) + (setf (var-arity var) + (if (is-type? 'flic-lambda val) + (length (flic-lambda-vars val)) + 0)))) + (dolist (var (flic-let-bindings object)) + (postoptimize (var-value var))) + (postoptimize (flic-let-body object))) + +(define-postoptimize flic-app (object) + (let ((fn (flic-app-fn object))) + (typecase fn + (flic-ref + (let* ((var (flic-ref-var fn)) + (arity (var-arity var))) + (if (not (var-toplevel? var)) (incf (var-referenced var))) + (when (not (eqv? arity 0)) + (postoptimize-app-aux object var arity (flic-app-args object))))) + (flic-pack + (let* ((con (flic-pack-con fn)) + (arity (con-arity con))) + (postoptimize-app-aux object '#f arity (flic-app-args object)))) + (else + (postoptimize fn))) + (dolist (a (flic-app-args object)) + (postoptimize a)))) + +(define (postoptimize-app-aux object var arity args) + (declare (type fixnum arity)) + (let ((nargs (length args))) + (declare (type fixnum nargs)) + (cond ((< nargs arity) + ;; not enough arguments + (when var (setf (var-standard-refs? var) '#t))) + ((eqv? nargs arity) + ;; exactly the right number of arguments + (when var (setf (var-optimized-refs? var) '#t)) + (setf (flic-app-saturated? object) '#t)) + (else + ;; make the fn a nested flic-app + (multiple-value-bind (arghead argtail) + (split-list args arity) + (setf (flic-app-fn object) + (make-flic-app (flic-app-fn object) arghead '#t)) + (setf (flic-app-args object) argtail) + (when var (setf (var-optimized-refs? var) '#t)) + (dolist (a arghead) + (postoptimize a)))) + ))) + +(define-postoptimize flic-ref (object) + (let ((var (flic-ref-var object))) + (if (not (var-toplevel? var)) (incf (var-referenced var))) + (setf (var-standard-refs? var) '#t))) + +(define-postoptimize flic-const (object) + object) + +(define-postoptimize flic-pack (object) + object) + +(define-postoptimize flic-and (object) + (for-each (function postoptimize) (flic-and-exps object))) + +(define-postoptimize flic-case-block (object) + (for-each (function postoptimize) (flic-case-block-exps object))) + +(define-postoptimize flic-if (object) + (postoptimize (flic-if-test-exp object)) + (postoptimize (flic-if-then-exp object)) + (postoptimize (flic-if-else-exp object))) + +(define-postoptimize flic-return-from (object) + (postoptimize (flic-return-from-exp object))) + +(define-postoptimize flic-sel (object) + (postoptimize (flic-sel-exp object))) + +(define-postoptimize flic-is-constructor (object) + (postoptimize (flic-is-constructor-exp object))) + +(define-postoptimize flic-con-number (object) + (postoptimize (flic-con-number-exp object))) + +(define-postoptimize flic-void (object) + object) diff --git a/backend/strictness.scm b/backend/strictness.scm new file mode 100644 index 0000000..5e03aa6 --- /dev/null +++ b/backend/strictness.scm @@ -0,0 +1,845 @@ +;;; strictness.scm -- strictness analyzer +;;; +;;; author : Sandra Loosemore +;;; date : 28 May 1992 +;;; +;;; The algorithm used here follows Consel, "Fast Strictness Analysis +;;; Via Symbolic Fixpoint Interation". +;;; +;;; The basic idea is to do a traversal of the flic structure, building +;;; a boolean term that represents the strictness of each subexpression. +;;; The boolean terms are composed of ands & ors of the argument variables +;;; to each function. After traversing the body of the function, we can +;;; determine which argument variables are strict by examining the +;;; corresponding term, and then we can update the strictness attribute +;;; of the var that names the function. +;;; +;;; Another traversal needs to be done to attach strictness properties +;;; to locally bound variables. + + +;;; Here's the main entry point. + +(define (strictness-analysis-top big-let) + (fun-strictness-walk big-let) + (var-strictness-walk big-let '() '()) + ;; *** This probably belongs somewhere else? + (do-box-analysis big-let '() '() '#t) + big-let) + + +;;;====================================================================== +;;; Function strictness analyzer code walk +;;;====================================================================== + +;;; This actually involves two code walkers. The first merely traverses +;;; structure and identifies function definitions. The second traverses +;;; the definitions of the functions to compute their strictness. + + +;;; Fun-strictness-walk is the walker to find function definitions. +;;; This is trivial for everything other than flic-let. + +(define-flic-walker fun-strictness-walk (object)) + +(define-fun-strictness-walk flic-lambda (object) + (fun-strictness-walk (flic-lambda-body object))) + +(define-fun-strictness-walk flic-let (object) + (if (flic-let-recursive? object) + (fun-strictness-walk-letrec object) + (fun-strictness-walk-let* object)) + (dolist (v (flic-let-bindings object)) + (fun-strictness-walk (var-value v))) + (fun-strictness-walk (flic-let-body object))) + +(define-fun-strictness-walk flic-app (object) + (fun-strictness-walk (flic-app-fn object)) + (for-each (function fun-strictness-walk) (flic-app-args object))) + +(define-fun-strictness-walk flic-ref (object) + (declare (ignore object)) + '#f) + +(define-fun-strictness-walk flic-pack (object) + (declare (ignore object)) + '#f) + +(define-fun-strictness-walk flic-const (object) + (declare (ignore object)) + '#f) + +(define-fun-strictness-walk flic-case-block (object) + (for-each (function fun-strictness-walk) (flic-case-block-exps object))) + +(define-fun-strictness-walk flic-return-from (object) + (fun-strictness-walk (flic-return-from-exp object))) + +(define-fun-strictness-walk flic-and (object) + (for-each (function fun-strictness-walk) (flic-and-exps object))) + +(define-fun-strictness-walk flic-if (object) + (fun-strictness-walk (flic-if-test-exp object)) + (fun-strictness-walk (flic-if-then-exp object)) + (fun-strictness-walk (flic-if-else-exp object))) + +(define-fun-strictness-walk flic-sel (object) + (fun-strictness-walk (flic-sel-exp object))) + +(define-fun-strictness-walk flic-is-constructor (object) + (fun-strictness-walk (flic-is-constructor-exp object))) + +(define-fun-strictness-walk flic-con-number (object) + (fun-strictness-walk (flic-con-number-exp object))) + +(define-fun-strictness-walk flic-void (object) + (declare (ignore object)) + '#f) + + + +;;; Here is the magic for let bindings of function definitions. +;;; Sequential bindings are easy. For recursive bindings, we must +;;; keep track of mutually recursive functions. +;;; If a function binding has a strictness annotation attached, +;;; do not mess with it further. + +(define (fun-strictness-walk-let* object) + (dolist (var (flic-let-bindings object)) + (let ((val (var-value var))) + (when (is-type? 'flic-lambda val) + (if (var-strictness var) + (mark-argument-strictness + (var-strictness var) (flic-lambda-vars val)) + (compute-function-strictness var val '()))) + ))) + +(define (fun-strictness-walk-letrec object) + (let ((stack '())) + (dolist (var (flic-let-bindings object)) + (let ((val (var-value var))) + (if (and (is-type? 'flic-lambda val) (not (var-strictness var))) + (setf stack (add-recursive-function-1 var (init-var-env) stack))))) + (dolist (var (flic-let-bindings object)) + (let ((val (var-value var))) + (when (is-type? 'flic-lambda val) + (if (var-strictness var) + (mark-argument-strictness + (var-strictness var) (flic-lambda-vars val)) + (compute-function-strictness var val stack))) + )))) + +(define (compute-function-strictness var val stack) + (let* ((vars (flic-lambda-vars val)) + (env (add-var-binding-n vars (map (function list) vars) + (init-var-env))) + (term (compute-strictness-walk (flic-lambda-body val) env stack))) + (when (eq? term '#t) + (signal-infinite-loop-function var) + (setf (flic-lambda-body val) + (make-infinite-loop-error + (format '#f "Function ~s has an infinite loop." var)))) + (setf (var-strictness var) (munge-strictness-terms term vars)))) + + +(define (signal-infinite-loop-function var) + (recoverable-error 'infinite-loop-function + "Function ~s has an infinite loop." + var)) + +(define (make-infinite-loop-error msg) + (make-flic-app + (make-flic-ref (core-symbol "error")) + (list (make-flic-const msg)) + '#t)) + + +;;; compute-strictness-walk is the traversal to compute strictness +;;; terms. +;;; The purpose of the env is to map locally bound variables onto +;;; strictness terms which are expressed as lists of argument variables +;;; to the function being analyzed. +;;; The purpose of the stack is to keep track of recursive function +;;; walks and recognize when we've reached a fixed point. + +(define-flic-walker compute-strictness-walk (object env stack)) + + +;;; Making a function never forces anything. + +(define-compute-strictness-walk flic-lambda (object env stack) + (declare (ignore object env stack)) + '#f) + + +;;; For let, add bindings to environment and get strictness of body. + +(define-compute-strictness-walk flic-let (object env stack) + (let ((bindings (flic-let-bindings object)) + (body (flic-let-body object)) + (recursive? (flic-let-recursive? object))) + (if recursive? + ;; Must add stuff to env and stack before traversing anything. + (begin + (dolist (var bindings) + (setf env (add-var-binding-1 var '#f env))) + (dolist (var bindings) + (let ((val (var-value var))) + (when (is-type? 'flic-lambda val) + (setf stack (add-recursive-function-1 var env stack))))) + (dolist (var bindings) + (let ((val (var-value var))) + (set-var-env var env (compute-strictness-walk val env stack))))) + ;; Otherwise just do things sequentially. + ;; Note that even though there is no possibility of recursion + ;; here, we must add stuff to the stack anyway so that we can + ;; walk calls in the correct env. + (dolist (var bindings) + (let ((val (var-value var))) + (when (is-type? 'flic-lambda val) + (setf stack (add-recursive-function-1 var env stack))) + (setf env + (add-var-binding-1 + var (compute-strictness-walk val env stack) env))))) + (compute-strictness-walk body env stack))) + + +;;; Treat explicit, saturated calls to named functions specially. + +(define-compute-strictness-walk flic-app (object env stack) + (let ((fn (flic-app-fn object)) + (args (flic-app-args object)) + (saturated? (flic-app-saturated? object))) + (cond ((and (is-type? 'flic-ref fn) saturated?) + ;; Special handling for named functions. + (compute-application-strictness + (flic-ref-var fn) + args env stack)) + ((and (is-type? 'flic-pack fn) saturated?) + ;; Similarly for constructor applications, but we always + ;; know which arguments are strict in advance. + (compute-application-strictness-aux + (con-slot-strict? (flic-pack-con fn)) + args env stack)) + (else + ;; Otherwise, we know that the function expression is going to + ;; be forced, but all of its arguments are lazy. So ignore the + ;; arguments in computing the strictness of the whole expression. + (compute-strictness-walk fn env stack))))) + + +(define (compute-application-strictness var args env stack) + (let* ((strictness (var-strictness var)) + (info '#f) + (arg-strictness-list '#f)) + (cond ((eq? var (core-symbol "error")) + ;; This expression will return bottom no matter what. + 'error) + (strictness + ;; We've already completed the walk for this function and + ;; determined which of its arguments are strict. + ;; The strictness expression for the application is the + ;; OR of the strictness of its non-lazy arguments. + (compute-application-strictness-aux strictness args env stack)) + ((get-recursive-function-trace + (setf arg-strictness-list + (map (lambda (a) (compute-strictness-walk a env stack)) + args)) + (setf info (get-recursive-function var stack))) + ;; We're already tracing this call. Return true to + ;; terminate the fixpoint iteration. + '#t) + (else + ;; Otherwise, begin a new trace instance. + ;; Add stuff to the saved var-env to map references to + ;; the argument variables to the strictness terms for + ;; the actual arguments at this call site. + ;; References to closed-over variables within the function + ;; use the strictness values that were stored in the env + ;; at the point of function definition. + (let* ((env (get-recursive-function-env info)) + (lambda (var-value var)) + (body (flic-lambda-body lambda)) + (vars (flic-lambda-vars lambda)) + (result '#f)) + (push-recursive-function-trace arg-strictness-list info) + (setf result + (compute-strictness-walk + body + (add-var-binding-n vars arg-strictness-list env) + stack)) + (pop-recursive-function-trace info) + result)) + ))) + + +(define (compute-application-strictness-aux strictness args env stack) + (make-or-term + (map (lambda (strict? arg) + (if strict? (compute-strictness-walk arg env stack) '#f)) + strictness args))) + + +;;; For a reference, look up the term associated with the variable in env. +;;; If not present in the environment, ignore it; the binding was established +;;; outside the scope of the function being analyzed. + +(define-compute-strictness-walk flic-ref (object env stack) + (declare (ignore stack)) + (get-var-env (flic-ref-var object) env)) + + +;;; References to constants or constructors never fail. + +(define-compute-strictness-walk flic-const (object env stack) + (declare (ignore object env stack)) + '#f) + +(define-compute-strictness-walk flic-pack (object env stack) + (declare (ignore object env stack)) + '#f) + + +;;; The first clause of a case-block is the only one that is always +;;; executed, so it is the only one that affects the strictness of +;;; the overall expression. + +(define-compute-strictness-walk flic-case-block (object env stack) + (compute-strictness-walk (car (flic-case-block-exps object)) env stack)) + + +;;; Return-from fails if its subexpression fails. + +(define-compute-strictness-walk flic-return-from (object env stack) + (compute-strictness-walk (flic-return-from-exp object) env stack)) + + +;;; For and, the first subexpression is the only one that is always +;;; executed, so it is the only one that affects the strictness of +;;; the overall expression. + +(define-compute-strictness-walk flic-and (object env stack) + (compute-strictness-walk (car (flic-and-exps object)) env stack)) + + +;;; The strictness of an IF is the strictness of the test OR'ed +;;; with the AND of the strictness of its branches. + +(define-compute-strictness-walk flic-if (object env stack) + (make-or-term-2 + (compute-strictness-walk (flic-if-test-exp object) env stack) + (make-and-term-2 + (compute-strictness-walk (flic-if-then-exp object) env stack) + (compute-strictness-walk (flic-if-else-exp object) env stack)))) + + +;;; Selecting a component of a data structure causes it to be forced, +;;; so propagate the strictness of the subexpression upwards. + +(define-compute-strictness-walk flic-sel (object env stack) + (compute-strictness-walk (flic-sel-exp object) env stack)) + + +;;; Is-constructor and con-number force their subexpressions. + +(define-compute-strictness-walk flic-is-constructor (object env stack) + (compute-strictness-walk (flic-is-constructor-exp object) env stack)) + +(define-compute-strictness-walk flic-con-number (object env stack) + (compute-strictness-walk (flic-con-number-exp object) env stack)) + +(define-compute-strictness-walk flic-void (object env stack) + (declare (ignore object env stack)) + '#f) + + + +;;;====================================================================== +;;; Utilities for managing the env +;;;====================================================================== + +;;; The env is just an a-list. + +(define (init-var-env) + '()) + +(define (add-var-binding-1 var binding env) + (cons (cons var binding) env)) + +(define (add-var-binding-n vars bindings env) + (if (null? vars) + env + (add-var-binding-n (cdr vars) (cdr bindings) + (cons (cons (car vars) (car bindings)) env)))) + +(define (get-var-env var env) + (let ((stuff (assq var env))) + (if stuff + (cdr stuff) + '#f))) + +(define (set-var-env var env new-value) + (let ((stuff (assq var env))) + (if stuff + (setf (cdr stuff) new-value) + (error "Can't find binding for ~s in environment." var)))) + + + +;;;====================================================================== +;;; Utilities for managing the stack +;;;====================================================================== + +;;; For now, the stack is just an a-list too. +;;; Some sort of hashing scheme could also be used instead of a linear +;;; search, but if the iteration depth for the fixpoint analysis is +;;; small, it's probably not worth the trouble. + +(define (add-recursive-function-1 var env stack) + (cons (list var env '()) stack)) + +(define (get-recursive-function var stack) + (or (assq var stack) + (error "Can't find entry for ~s in stack." var))) + +(define (get-recursive-function-env entry) + (cadr entry)) + +(define (push-recursive-function-trace new-args entry) + (push new-args (caddr entry))) + +(define (pop-recursive-function-trace entry) + (pop (caddr entry))) + +(define (get-recursive-function-trace args entry) + (get-recursive-function-trace-aux args (caddr entry))) + +(define (get-recursive-function-trace-aux args list) + (cond ((null? list) + '#f) + ((every (function term=) args (car list)) + '#t) + (else + (get-recursive-function-trace-aux args (cdr list))))) + + +;;;====================================================================== +;;; Utilities for boolean terms +;;;====================================================================== + + +;;; A term is either #t, #f, the symbol 'error, or a list of variables +;;; (which are implicitly or'ed together). +;;; #t and 'error are treated identically, except that #t indicates +;;; failure because of infinite recursion and 'error indicates failure +;;; due to a call to the error function. +;;; In general, AND terms add nothing to the result, so to reduce +;;; needless computation we generally reduce (and a b) to #f. + +;;; Make an OR term. First look for some obvious special cases as an +;;; efficiency hack, otherwise fall through to more general code. + +(define (make-or-term terms) + (cond ((null? terms) + '#f) + ((null? (cdr terms)) + (car terms)) + ((eq? (car terms) '#t) + '#t) + ((eq? (car terms) 'error) + 'error) + ((eq? (car terms) '#f) + (make-or-term (cdr terms))) + (else + (make-or-term-2 (car terms) (make-or-term (cdr terms)))))) + +(define (make-or-term-2 term1 term2) + (cond ((eq? term2 '#t) + '#t) + ((eq? term2 'error) + 'error) + ((eq? term2 '#f) + term1) + ((eq? term1 '#t) + '#t) + ((eq? term1 'error) + 'error) + ((eq? term1 '#f) + term2) + ;; At this point we know both terms are variable lists. + ((implies? term2 term1) + term2) + ((implies? term1 term2) + term1) + (else + (merge-list-terms term1 term2)))) + + +;;; Merge the two lists, throwing out duplicate variables. + +(define (merge-list-terms list1 list2) + (cond ((null? list1) + list2) + ((null? list2) + list1) + ((eq? (car list1) (car list2)) + (cons (car list1) (merge-list-terms (cdr list1) (cdr list2)))) + ((var< (car list1) (car list2)) + (cons (car list1) (merge-list-terms (cdr list1) list2))) + (else + (cons (car list2) (merge-list-terms list1 (cdr list2)))))) + + +;;; Helper function: does term1 imply term2? +;;; True if every subterm of term2 is also included in term1. + +(define (implies? term1 term2) + (every (lambda (v2) (memq v2 term1)) term2)) + + +;;; Make an AND term. Because we don't want to build up arbitrarily +;;; complex AND expressions, basically just compute an OR list that +;;; represents the intersection of the subterms. + +(define (make-and-term terms) + (cond ((null? terms) + '#f) + ((null? (cdr terms)) + (car terms)) + ((eq? (car terms) '#t) + (make-and-term (cdr terms))) + ((eq? (car terms) 'error) + (make-and-term (cdr terms))) + ((eq? (car terms) '#f) + '#f) + (else + (make-and-term-2 (car terms) (make-and-term (cdr terms)))))) + +(define (make-and-term-2 term1 term2) + (cond ((eq? term2 '#t) + term1) + ((eq? term2 'error) + term1) + ((eq? term2 '#f) + '#f) + ((eq? term1 '#t) + term2) + ((eq? term1 'error) + term2) + ((eq? term1 '#f) + '#f) + ;; At this point we know both terms are variable lists. + ((implies? term2 term1) + term1) + ((implies? term1 term2) + term2) + (else + (let ((result '())) + (dolist (v term1) + (if (memq v term2) + (push v result))) + (if (null? result) + '#f + (nreverse result)))) + )) + + +;;; Subterms of an and/or term are always sorted, so that to compare +;;; two terms we can just compare subterms componentwise. + +(define (term= term1 term2) + (or (eq? term1 term2) + (and (pair? term1) + (pair? term2) + (eq? (car term1) (car term2)) + (term= (cdr term1) (cdr term2))))) + + +;;; Variables within an OR-list are sorted alphabetically by names. + +(define (var< var1 var2) + (stringstring (def-name var1)) + (symbol->string (def-name var2)))) + + +;;; Determine which of the vars are present in the term. + +(define (munge-strictness-terms term vars) + (map (lambda (v) + (setf (var-strict? v) + (cond ((var-force-strict? v) + '#t) + ((eq? term '#t) + '#t) + ((eq? term 'error) + '#t) + ((eq? term '#f) + '#f) + ((memq v term) + '#t) + (else + '#f)))) + vars)) + +(define (mark-argument-strictness strictness vars) + (map (lambda (s v) (setf (var-strict? v) s)) strictness vars)) + + + +;;;====================================================================== +;;; Variable strictness propagation code walk +;;;====================================================================== + +;;; Walk the code, marking any vars found in strict contexts as strict. +;;; Locally bound variables are consed onto the varlist. This is +;;; used to determine which variables can be marked as strict when they +;;; appear in strict contexts. +;;; When walking something that does not appear in a strict context +;;; or that is not always evaluated, reinitialize varlist to the empty +;;; list. +;;; The stack is used to keep track of variables that have not been +;;; initialized yet, so that we can detect some kinds of infinite loops. +;;; When walking something that is not always evaluated, reset this to +;;; the empty list. + +(define-flic-walker var-strictness-walk (object varlist stack)) + + + +;;; Since the body of the lambda might not be evaluated, reset +;;; both varlist and stack. + +(define-var-strictness-walk flic-lambda (object varlist stack) + (declare (ignore varlist stack)) + (var-strictness-walk (flic-lambda-body object) '() '())) + + +;;; The basic idea for let is to find the variables that are strict in +;;; the body first, and propagate that information backwards to the +;;; binding initializers. + +(define-var-strictness-walk flic-let (object varlist stack) + (let ((bindings (flic-let-bindings object))) + (var-strictness-walk-let-aux + bindings + (flic-let-body object) + (append bindings varlist) + (append bindings stack) + (flic-let-recursive? object)))) + +(define (var-strictness-walk-let-aux bindings body varlist stack recursive?) + (if (null? bindings) + (var-strictness-walk body varlist stack) + (begin + (var-strictness-walk-let-aux + (cdr bindings) body varlist (cdr stack) recursive?) + (let* ((var (car bindings)) + (val (var-value var))) + (cond ((var-strict? var) + ;; Recursive variables have to be set back to unstrict + ;; because the value form might contain forward references. + ;; The box analyzer will set them to strict again if the + ;; value forms are safe. + (when recursive? (setf (var-strict? var) '#f)) + ;; Detect x = 1 + x circularities here + (var-strictness-walk val varlist stack)) + ((flic-exp-strict-result? val) + ;; The val is going to be wrapped in a delay. + (var-strictness-walk val '() '())) + (else + ;; Watch out for x = x and x = cdr x circularities. + ;; *** I am still a little confused about this. It + ;; *** seems like the stack should be passed through + ;; *** when walking already-boxed values that appear as + ;; *** non-strict function arguments as well, but doing + ;; *** so generates some apparently bogus complaints + ;; *** about infinite loops. So maybe doing it here + ;; *** is incorrect too, and we just haven't run across + ;; *** a test case that triggers it??? + (var-strictness-walk val '() stack)) + ))))) + + +(define (flic-exp-strict-result? val) + (cond ((is-type? 'flic-ref val) + (var-strict? (flic-ref-var val))) + ((is-type? 'flic-sel val) + (list-ref (con-slot-strict? (flic-sel-con val)) (flic-sel-i val))) + (else + '#t))) + +(define-var-strictness-walk flic-app (object varlist stack) + (let ((fn (flic-app-fn object)) + (args (flic-app-args object)) + (saturated? (flic-app-saturated? object))) + (cond ((and saturated? (is-type? 'flic-ref fn)) + ;; Strictness of function should be stored on var + (do-var-strictness-flic-app-aux + (var-strictness (flic-ref-var fn)) + fn args varlist stack)) + ((and saturated? (is-type? 'flic-pack fn)) + ;; Strictness of constructor should be stored on con + (do-var-strictness-flic-app-aux + (con-slot-strict? (flic-pack-con fn)) + fn args varlist stack)) + (else + ;; All arguments are non-strict + (var-strictness-walk fn varlist stack) + (dolist (a args) + (var-strictness-walk a '() '())))))) + +(define (do-var-strictness-flic-app-aux strictness fn args varlist stack) + (when (not strictness) + (error "Can't find strictness for function ~s." fn)) + (dolist (a args) + (if (pop strictness) + (var-strictness-walk a varlist stack) + (var-strictness-walk a '() '())))) + + +(define-var-strictness-walk flic-ref (object varlist stack) + (let ((var (flic-ref-var object))) + (cond ((memq var stack) + ;; Circular variable definition detected. + (signal-infinite-loop-variable var) + (setf (var-value var) + (make-infinite-loop-error + (format '#f "Variable ~s has an infinite loop." var)))) + ((memq var varlist) + (setf (var-strict? var) '#t)) + (else + '#f)))) + +(define (signal-infinite-loop-variable var) + (recoverable-error 'infinite-loop-variable + "Variable ~s has an infinite loop." + var)) + +(define-var-strictness-walk flic-const (object varlist stack) + (declare (ignore object varlist stack)) + '#f) + +(define-var-strictness-walk flic-pack (object varlist stack) + (declare (ignore object varlist stack)) + '#f) + +(define-var-strictness-walk flic-case-block (object varlist stack) + (var-strictness-walk (car (flic-case-block-exps object)) varlist stack) + (dolist (exp (cdr (flic-case-block-exps object))) + (var-strictness-walk exp '() '()))) + +(define-var-strictness-walk flic-return-from (object varlist stack) + (var-strictness-walk (flic-return-from-exp object) varlist stack)) + +(define-var-strictness-walk flic-and (object varlist stack) + (var-strictness-walk (car (flic-and-exps object)) varlist stack) + (dolist (exp (cdr (flic-and-exps object))) + (var-strictness-walk exp '() '()))) + +(define-var-strictness-walk flic-if (object varlist stack) + (var-strictness-walk (flic-if-test-exp object) varlist stack) + (var-strictness-walk (flic-if-then-exp object) '() '()) + (var-strictness-walk (flic-if-else-exp object) '() '())) + +(define-var-strictness-walk flic-sel (object varlist stack) + (var-strictness-walk (flic-sel-exp object) varlist stack)) + +(define-var-strictness-walk flic-is-constructor (object varlist stack) + (var-strictness-walk (flic-is-constructor-exp object) varlist stack)) + +(define-var-strictness-walk flic-con-number (object varlist stack) + (var-strictness-walk (flic-con-number-exp object) varlist stack)) + +(define-var-strictness-walk flic-void (object varlist stack) + (declare (ignore object varlist stack)) + '#f) + + + +;;;====================================================================== +;;; Printer support +;;;====================================================================== + +(define (strictness-analysis-printer big-let) + (print-strictness big-let 0)) + +(define (print-strictness-list list depth) + (dolist (o list) + (print-strictness o depth))) + +(define (print-strictness-indent depth) + (dotimes (i (* 2 depth)) + (declare (ignorable i)) + (write-char #\space))) + +(define (strictness-string bool) + (if bool "#t" "#f")) + +(define-flic-walker print-strictness (object depth)) + +(define-print-strictness flic-lambda (object depth) + (print-strictness-indent depth) + (format '#t "In anonymous function:~%") + (print-strictness (flic-lambda-body object) (1+ depth))) + +(define-print-strictness flic-let (object depth) + (dolist (var (flic-let-bindings object)) + (let ((val (var-value var))) + (if (is-type? 'flic-lambda val) + (begin + (print-strictness-indent depth) + (format '#t "Function ~s has argument strictness ~a.~%" + var + (map (function strictness-string) (var-strictness var))) + (print-strictness (flic-lambda-body val) (1+ depth))) + (begin + (print-strictness-indent depth) + (format '#t "Variable ~s has strictness ~a.~%" + var + (strictness-string (var-strict? var))) + (print-strictness val depth))))) + (print-strictness (flic-let-body object) depth)) + +(define-print-strictness flic-app (object depth) + (print-strictness (flic-app-fn object) depth) + (print-strictness-list (flic-app-args object) depth)) + +(define-print-strictness flic-ref (object depth) + (declare (ignore object depth)) + '#f) + +(define-print-strictness flic-const (object depth) + (declare (ignore object depth)) + '#f) + +(define-print-strictness flic-pack (object depth) + (declare (ignore object depth)) + '#f) + +(define-print-strictness flic-case-block (object depth) + (print-strictness-list (flic-case-block-exps object) depth)) + +(define-print-strictness flic-return-from (object depth) + (print-strictness (flic-return-from-exp object) depth)) + +(define-print-strictness flic-and (object depth) + (print-strictness-list (flic-and-exps object) depth)) + +(define-print-strictness flic-if (object depth) + (print-strictness (flic-if-test-exp object) depth) + (print-strictness (flic-if-then-exp object) depth) + (print-strictness (flic-if-else-exp object) depth)) + +(define-print-strictness flic-sel (object depth) + (print-strictness (flic-sel-exp object) depth)) + +(define-print-strictness flic-is-constructor (object depth) + (print-strictness (flic-is-constructor-exp object) depth)) + +(define-print-strictness flic-con-number (object depth) + (print-strictness (flic-con-number-exp object) depth)) + +(define-print-strictness flic-void (object depth) + (declare (ignore object depth)) + '#f) + diff --git a/bin/cmu-clx-haskell b/bin/cmu-clx-haskell new file mode 100755 index 0000000..dba0998 --- /dev/null +++ b/bin/cmu-clx-haskell @@ -0,0 +1,9 @@ +#!/bin/csh +# +# run cmu clx haskell + +if (`arch -k` == "sun4c") then + $CMUCLBIN -core $HASKELL/bin/sun4c-clx-haskell.core +else + $CMUCLBIN -core $HASKELL/bin/sun4m-clx-haskell.core +endif diff --git a/bin/cmu-haskell b/bin/cmu-haskell new file mode 100755 index 0000000..ff82dfc --- /dev/null +++ b/bin/cmu-haskell @@ -0,0 +1,9 @@ +#!/bin/csh +# +# run cmu haskell + +if (`arch -k` == "sun4c") then + $CMUCLBIN -core $HASKELL/bin/sun4c-haskell.core +else + $CMUCLBIN -core $HASKELL/bin/sun4m-haskell.core +endif diff --git a/bin/magic.scm b/bin/magic.scm new file mode 100644 index 0000000..999e8b0 --- /dev/null +++ b/bin/magic.scm @@ -0,0 +1,10 @@ +;;; magic.scm -- magic support file for dumping compiled code files. +;;; +;;; author : Sandra Loosemore +;;; date : 8 Jul 1992 +;;; +;;; This file is used to dump compiled code files. The macro call below +;;; expands into the code being dumped. See dump-interface.scm for more +;;; details. + +(magic-form-to-compile) diff --git a/cfn/README b/cfn/README new file mode 100644 index 0000000..80a25c1 --- /dev/null +++ b/cfn/README @@ -0,0 +1,35 @@ +Whats what in the cfn. + +Language generated by cfn contains these ast node types: + lambda + let + if + case -- restriction: all patterns must be either literals or + a constructor with var and wildcard args + app + var-ref + con-ref + const + con-number + sel + is-constructor + +Transformations to do: + Convert lists to explicit calls to cons + Simplify patterns + Remove sequences + Remove list comprehensions + Remove sections + Reduce patterns on lhs of decls + Reduce patterns in function args + Convert where decls to let statements + Convert guarded-expressions to if - then - else form + +Done earlier: + signdecls are removed in scoping + exp-signs are removed in typechecker + prec parser removes `negate' & pp-* nodes + + + + diff --git a/cfn/cfn.scm b/cfn/cfn.scm new file mode 100644 index 0000000..bf43be0 --- /dev/null +++ b/cfn/cfn.scm @@ -0,0 +1,21 @@ +;;; cfn.scm -- module definition for CFN phase +;;; +;;; author : Sandra Loosemore +;;; date : 11 Mar 1992 +;;; + + +(define-compilation-unit cfn + (source-filename "$Y2/cfn/") + (require ast haskell-utils) + (unit main + (source-filename "main.scm")) + (unit misc + (source-filename "misc.scm") + (require main)) + (unit pattern + (source-filename "pattern.scm") + (require main))) + + + diff --git a/cfn/main.scm b/cfn/main.scm new file mode 100644 index 0000000..3853b03 --- /dev/null +++ b/cfn/main.scm @@ -0,0 +1,83 @@ +;;; main.scm -- main entry point for CFN pass +;;; +;;; author : Sandra Loosemore +;;; date : 27 Feb 1992 +;;; + + +;;;=================================================================== +;;; Basic support +;;;=================================================================== + + +;;; Define the basic walker and some helper functions. + +(define-walker cfn ast-td-cfn-walker) + +(define (cfn-ast-1 x) + (call-walker cfn x)) + +(define (cfn-ast/list l) + (map (lambda (x) (cfn-ast-1 x)) l)) + + +;;; This is the main entry point. It is called by the driver on +;;; each top-level decl in the module. + +(define (cfn-ast x) + (let ((result (cfn-ast-1 x))) +; (pprint result) ;*** debug + result)) + + + +;;;=================================================================== +;;; Default traversal methods +;;;=================================================================== + + +(define-local-syntax (make-cfn-code slot type) + (let ((stype (sd-type slot)) + (sname (sd-name slot))) + (cond ((and (symbol? stype) + (or (eq? stype 'exp) + (subtype? stype 'exp))) + `(setf (struct-slot ',type ',sname object) + (cfn-ast-1 (struct-slot ',type ',sname object)))) + ((and (pair? stype) + (eq? (car stype) 'list) + (symbol? (cadr stype)) + (or (eq? (cadr stype) 'exp) + (subtype? (cadr stype) 'exp))) + `(setf (struct-slot ',type ',sname object) + (cfn-ast/list (struct-slot ',type ',sname object)))) + ((and (pair? stype) + (eq? (car stype) 'list) + (eq? (cadr stype) 'decl)) + `(setf (struct-slot ',type ',sname object) + (cfn-valdef-list (struct-slot ',type ',sname object)))) + (else +; (format '#t "Cfn: skipping slot ~A in ~A~%" +; (sd-name slot) +; type) + '#f)))) + +(define-modify-walker-methods cfn + (let if + exp-sign + app + var-ref con-ref + integer-const float-const char-const string-const + con-number sel is-constructor + void + case-block return-from and-exp + ) + (object) + make-cfn-code) + + +;;; These have specialized walkers: +;;; lambda, case, valdef, list-comp (pattern.scm) +;;; list-exp, list-comp, section-l, section-r, dict-placeholder, +;;; recursive-placeholder, save-old-exp (misc.scm) + diff --git a/cfn/misc.scm b/cfn/misc.scm new file mode 100644 index 0000000..4bcba64 --- /dev/null +++ b/cfn/misc.scm @@ -0,0 +1,113 @@ +;;; misc.scm -- random other transformations done during CFN processing +;;; +;;; author : Sandra Loosemore +;;; date : 27 Feb 1992 +;;; +;;; This file contains specialized CFN walkers that implement rewrite rules +;;; for list-exp, sequence-xxx, list-comp, section-l, and section-r. + + +;;; Turn list-exps into cons chains. + +(define-walker-method cfn list-exp (object) + (do-cfn-list-exp (list-exp-exps object))) + +(define (do-cfn-list-exp exps) + (if (null? exps) + ;; Make a con-ref for [] + (**con/def (core-symbol "Nil")) + ;; Otherwise make an app of : + (let ((first (cfn-ast-1 (car exps))) + (rest (do-cfn-list-exp (cdr exps)))) + (**app (**con/def (core-symbol ":")) first rest)))) + + +;;; Sections get turned into lambda expressions. + +(define-walker-method cfn section-l (object) + (let ((def (create-temp-var 'section-arg))) + (**lambda/pat + (list (**var-pat/def def)) + (**app (cfn-ast-1 (section-l-op object)) + (**var/def def) + (cfn-ast-1 (section-l-exp object)))))) + +(define-walker-method cfn section-r (object) + (let ((def (create-temp-var 'section-arg))) + (**lambda/pat + (list (**var-pat/def def)) + (**app (cfn-ast-1 (section-r-op object)) + (cfn-ast-1 (section-r-exp object)) + (**var/def def))))) + + + +;;; Do list comprehensions. +;;; rewrite in terms of build and foldr so that we can apply +;;; deforestation techniques later. + +(define-walker-method cfn list-comp (object) + (let ((c (create-temp-var 'c)) + (n (create-temp-var 'n))) + (cfn-ast-1 + (**app (**var/def (core-symbol "build")) + (**lambda/pat + (list (**var-pat/def c) (**var-pat/def n)) + (do-cfn-list-comp + (list-comp-exp object) (list-comp-quals object) c n)))))) + +(define (do-cfn-list-comp exp quals c n) + (if (null? quals) + (**app (**var/def c) exp (**var/def n)) + (let ((qual (car quals))) + (if (is-type? 'qual-generator qual) + (do-cfn-list-comp-generator exp qual (cdr quals) c n) + (do-cfn-list-comp-filter exp qual (cdr quals) c n))))) + +(define (do-cfn-list-comp-filter exp qual quals c n) + (**if (qual-filter-exp qual) + (do-cfn-list-comp exp quals c n) + (**var/def n))) + +(define (do-cfn-list-comp-generator exp qual quals c n) + (let ((gen-pat (qual-generator-pat qual)) + (gen-exp (qual-generator-exp qual)) + (l (create-temp-var 'list)) + (b (create-temp-var 'rest))) + (**app (**var/def (core-symbol "foldr")) + (**lambda/pat + (list (**var-pat/def l) (**var-pat/def b)) + (**case (**var/def l) + (list (**alt/simple + gen-pat + (do-cfn-list-comp exp quals c b)) + (**alt/simple + (**wildcard-pat) + (**var/def b))))) + (**var/def n) + gen-exp))) + +;;; Placeholders just get eliminated + +(define-walker-method cfn dict-placeholder (object) + (if (eq? (dict-placeholder-exp object) '#f) + (error "Type checker screwed a dict placeholder object ~s." object) + (cfn-ast-1 (dict-placeholder-exp object)))) + +(define-walker-method cfn method-placeholder (object) + (if (eq? (method-placeholder-exp object) '#f) + (error "Type checker screwed a method placeholder object ~s." object) + (cfn-ast-1 (method-placeholder-exp object)))) + +(define-walker-method cfn recursive-placeholder (object) + (if (eq? (recursive-placeholder-exp object) '#f) + (error "Type checker screwed a recursive placeholder object ~s." object) + (cfn-ast-1 (recursive-placeholder-exp object)))) + +(define-walker-method cfn cast (object) + (cfn-ast-1 (cast-exp object))) + +;;; Eliminate saved old expressions + +(define-walker-method cfn save-old-exp (object) + (cfn-ast-1 (save-old-exp-new-exp object))) diff --git a/cfn/pattern.scm b/cfn/pattern.scm new file mode 100644 index 0000000..64badbf --- /dev/null +++ b/cfn/pattern.scm @@ -0,0 +1,654 @@ +;;; pattern.scm -- cfn processing of pattern-related AST structures +;;; +;;; author : Sandra Loosemore +;;; date : 27 Feb 1992 +;;; +;;; This file contains specialized CFN walkers for lambda, case, and valdef +;;; structures. + + + +;;;===================================================================== +;;; Top-level walkers +;;;===================================================================== + + +;;; The calls to remember-context are so an appropriate error message +;;; can be produced for pattern-matching failures. + +(define-walker-method cfn lambda (object) + (remember-context object + (do-cfn-lambda (lambda-pats object) (lambda-body object)))) + + +(define-walker-method cfn case (object) + (remember-context object + (do-cfn-case + (case-exp object) + (case-alts object)))) + + + + +;;; Valdefs are always processed as a list. + +(define (cfn-valdef-list list-of-valdefs) + (if (null? list-of-valdefs) + '() + (nconc (cfn-valdef (car list-of-valdefs)) + (cfn-valdef-list (cdr list-of-valdefs))))) + +(define (cfn-valdef object) + (remember-context object + (if (null? (single-fun-def-args (car (valdef-definitions object)))) + ;; This is a pattern binding. + (do-cfn-pattern-def-top object) + ;; This is a function binding. + ;; Branch on single-headed/multi-headed definition. + (list (add-dict-params + object + (if (null? (cdr (valdef-definitions object))) + (do-cfn-function-def-simple object) + (do-cfn-function-def-general object)))) + ))) + + +;;; This adds the dictionary parameters needed by the type system. A valdef +;;; structure has a dictionary-args field which contains the variables to be +;;; bound to dicationary arguments. + +(define (add-dict-params original-valdef generated-valdef) + (let ((vars (valdef-dictionary-args original-valdef))) + (when (not (null? vars)) + (let* ((sfd (car (valdef-definitions generated-valdef))) + (rhs (car (single-fun-def-rhs-list sfd))) + (exp (guarded-rhs-rhs rhs)) + (pats (map (function **var-pat/def) vars))) + (if (is-type? 'lambda exp) + (setf (lambda-pats exp) + (nconc pats (lambda-pats exp))) + (setf (guarded-rhs-rhs rhs) + (**lambda/pat pats exp)))))) + generated-valdef) + + +;;;===================================================================== +;;; Lambda rewriting +;;;===================================================================== + + +;;; For lambda, make all the argument patterns into var pats. +;;; Rewrite the body as a CASE to do any more complicated pattern +;;; matching. +;;; The CFN output for lambda is a modified lambda expression with +;;; all var-pats as arguments. + +(define (do-cfn-lambda pats body) + (let ((new-args '()) + (new-vars '()) + (new-pats '())) + (dolist (p pats) + (typecase p + (wildcard-pat + (push (**var-pat/def (create-temp-var 'arg)) new-args)) + (var-pat + (push p new-args)) + (as-pat + (let ((var (var-ref-var (as-pat-var p)))) + (push (**var-pat/def var) new-args) + (push (**var/def var) new-vars) + (push (as-pat-pattern p) new-pats))) + (else + (let ((var (create-temp-var 'arg))) + (push (**var-pat/def var) new-args) + (push (**var/def var) new-vars) + (push p new-pats))))) + (setf new-args (nreverse new-args)) + (setf new-vars (nreverse new-vars)) + (setf new-pats (nreverse new-pats)) + (**lambda/pat + new-args + (cond ((null? new-vars) + ;; No fancy pattern matching necessary. + (cfn-ast-1 body)) + ((null? (cdr new-vars)) + ;; Exactly one argument to match on. + (do-cfn-case (car new-vars) + (list (**alt/simple (car new-pats) body)))) + (else + ;; Multiple arguments to match on. + (do-cfn-case-tuple + new-vars + (list (**alt/simple (**tuple-pat new-pats) body)))) + )))) + + +;;;===================================================================== +;;; Function definitions +;;;===================================================================== + + +;;; The output of the CFN for function definitions is a simple +;;; valdef which binds a variable to a lambda expression. + + +;;; The simple case: there is only one set of arguments. + +(define (do-cfn-function-def-simple object) + (let* ((pat (valdef-lhs object)) + (sfd (car (valdef-definitions object)))) + (**valdef/pat + pat + (do-cfn-lambda + (single-fun-def-args sfd) + (rewrite-guards-and-where-decls + (single-fun-def-where-decls sfd) + (single-fun-def-rhs-list sfd) + '#f))))) + + +;;; The general case: generate new variables as the formal parameters +;;; to the resulting lambda, then use case to do the pattern matching. + +(define (do-cfn-function-def-general object) + (let ((pat (valdef-lhs object)) + (vars (map (lambda (p) + (declare (ignore p)) + (create-temp-var 'arg)) + (single-fun-def-args (car (valdef-definitions object))))) + (alts (map (lambda (sfd) + (**alt (**tuple-pat (single-fun-def-args sfd)) + (single-fun-def-rhs-list sfd) + (single-fun-def-where-decls sfd))) + (valdef-definitions object)))) + (**valdef/pat + pat + (**lambda/pat + (map (function **var-pat/def) vars) + (if (null? (cdr vars)) + ;; one-argument case + (do-cfn-case (**var/def (car vars)) alts) + ;; multi-argument case + (do-cfn-case-tuple (map (function **var/def) vars) alts)))) + )) + + +;;;===================================================================== +;;; Case +;;;===================================================================== + + +;;; For case, add failure alt, then call helper function to generate +;;; pattern matching tests. +;;; The CFN output for case is a case-block construct. + +(define (do-cfn-case exp alts) + (setf alts + (append alts + (list (**alt/simple (**wildcard-pat) (make-failure-exp))))) + (let ((list-of-pats (map (lambda (a) (list (alt-pat a))) alts))) + (if (is-type? 'var-ref exp) + (match-pattern-list (list exp) list-of-pats alts) + (let ((temp (create-temp-var 'cfn))) + (**let (list (**valdef/def temp (cfn-ast-1 exp))) + (match-pattern-list + (list (**var/def temp)) + list-of-pats + alts))) + ))) + + + +;;; Here's a special case, for when the exp being matched is a tuple +;;; of var-refs and all the alts also have tuple pats. + +(define (do-cfn-case-tuple exps alts) + (setf alts + (append alts + (list + (**alt/simple + (**tuple-pat + (map (lambda (e) (declare (ignore e)) (**wildcard-pat)) + exps)) + (make-failure-exp))))) + (match-pattern-list + exps + (map (lambda (a) (pcon-pats (alt-pat a))) alts) + alts)) + + +(define (match-pattern-list exps list-of-pats alts) + (let ((block-name (gensym "PMATCH"))) + (**case-block + block-name + (map (lambda (a p) (match-pattern exps p a block-name)) + alts + list-of-pats)))) + + +;;; Produce an exp that matches the given alt against the exps. +;;; If the match succeeds, it will return-from the given block-name. + +(define (match-pattern exps pats alt block-name) + (if (null pats) + ;; No more patterns to match. + ;; Return an exp that handles the guards and where-decls. + (cfn-ast-1 + (rewrite-guards-and-where-decls + (alt-where-decls alt) (alt-rhs-list alt) block-name)) + ;; Otherwise dispatch on type of first pattern. + (let ((pat (pop pats)) + (exp (pop exps))) + (funcall + (typecase pat + (wildcard-pat (function match-wildcard-pat)) + (var-pat (function match-var-pat)) + (pcon (function match-pcon)) + (as-pat (function match-as-pat)) + (irr-pat (function match-irr-pat)) + (const-pat (function match-const-pat)) + (plus-pat (function match-plus-pat)) + (list-pat (function match-list-pat)) + (else (error "Unrecognized pattern ~s." pat))) + pat + exp + pats + exps + alt + block-name)))) + + + + +;;; Wildcard patterns add no pattern matching test. +;;; Just recurse on the next pattern to be matched. + +(define (match-wildcard-pat pat exp pats exps alt block-name) + (declare (ignore pat exp)) + (match-pattern exps pats alt block-name)) + + +;;; A variable pattern likewise does not add any test. However, +;;; a binding of the variable to the corresponding exp must be added. + +(define (match-var-pat pat exp pats exps alt block-name) + (push (**valdef/pat pat exp) + (alt-where-decls alt)) + (match-pattern exps pats alt block-name)) + + +;;; Pcons are the hairy case because they may have subpatterns that need +;;; to be matched. +;;; If there are subpats and the exp is not a var-ref, make a let binding. +;;; If the con is a tuple type, there is no need to generate a test +;;; since the test would always succeed anyway. +;;; Do not generate let bindings here for subexpressions; do this lazily +;;; if and when necessary. + +(define (match-pcon pat exp pats exps alt block-name) + (let* ((var? (is-type? 'var-ref exp)) + (var (if var? + (var-ref-var exp) + (create-temp-var 'conexp))) + (con (pcon-con pat)) + (arity (con-arity con)) + (alg (con-alg con)) + (tuple? (algdata-tuple? alg)) + (subpats (pcon-pats pat)) + (subexps '())) + (dotimes (i arity) + (push (**sel con (**var/def var) i) subexps)) + (setf exps (nconc (nreverse subexps) exps)) + (setf pats (append subpats pats)) + (let ((tail (match-pattern exps pats alt block-name))) + (when (not tuple?) + (setf tail + (**and-exp (**is-constructor (**var/def var) con) tail))) + (when (not var?) + (setf tail + (**let (list (**valdef/def var (cfn-ast-1 exp))) tail))) + tail))) + + +;;; For as-pat, add a variable binding. +;;; If the expression being matched is not already a variable reference, +;;; take this opportunity to make the let binding. Otherwise push the +;;; let-binding onto the where-decls. + +(define (match-as-pat pat exp pats exps alt block-name) + (let ((var (var-ref-var (as-pat-var pat))) + (subpat (as-pat-pattern pat))) + (if (is-type? 'var-ref exp) + (begin + (push (**valdef/def var (**var/def (var-ref-var exp))) + (alt-where-decls alt)) + (match-pattern + (cons exp exps) + (cons subpat pats) + alt + block-name)) + (**let (list (**valdef/def var (cfn-ast-1 exp))) + (match-pattern + (cons (**var/def var) exps) + (cons subpat pats) + alt + block-name))))) + + +;;; An irrefutable pattern adds no test to the pattern matching, +;;; but adds a pattern binding to the where-decls. + +(define (match-irr-pat pat exp pats exps alt block-name) + (let ((subpat (irr-pat-pattern pat))) + (push (**valdef/pat subpat exp) (alt-where-decls alt)) + (match-pattern exps pats alt block-name))) + + +;;; A const pat has a little piece of code inserted by the typechecker +;;; to do the test. +;;; For matches against string constants, generate an inline test to match +;;; on each character of the string. + +(define (match-const-pat pat exp pats exps alt block-name) + (let ((const (const-pat-value pat))) + (**and-exp + (if (is-type? 'string-const const) + (let ((string (string-const-value const))) + (if (string=? string "") + (**is-constructor exp (core-symbol "Nil")) + (**app (**var/def (core-symbol "primStringEq")) const exp))) + (cfn-ast-1 (**app (const-pat-match-fn pat) exp))) + (match-pattern exps pats alt block-name)) + )) + + +;;; Plus pats have both a magic test and a piece of code to +;;; make a binding in the where-decls. Make a variable binding +;;; for the exp if it's not already a variable. + +(define (match-plus-pat pat exp pats exps alt block-name) + (let* ((var? (is-type? 'var-ref exp)) + (var (if var? (var-ref-var exp) (create-temp-var 'plusexp)))) + (push (**valdef/pat (plus-pat-pattern pat) + (**app (plus-pat-bind-fn pat) (**var/def var))) + (alt-where-decls alt)) + (let ((tail (match-pattern exps pats alt block-name))) + (setf tail + (**and-exp + (cfn-ast-1 (**app (plus-pat-match-fn pat) (**var/def var))) + tail)) + (if var? + tail + (**let (list (**valdef/def var exp)) tail))))) + + +;;; Rewrite list pats as pcons, then process recursively. + +(define (match-list-pat pat exp pats exps alt block-name) + (let ((newpat (rewrite-list-pat (list-pat-pats pat)))) + (match-pattern + (cons exp exps) + (cons newpat pats) + alt + block-name))) + +(define (rewrite-list-pat subpats) + (if (null? subpats) + (**pcon/def (core-symbol "Nil") '()) + (**pcon/def (core-symbol ":") + (list (car subpats) + (rewrite-list-pat (cdr subpats)))))) + + + + +;;;===================================================================== +;;; Pattern definitions +;;;===================================================================== + + +(define (do-cfn-pattern-def-top object) + (typecase (valdef-lhs object) + (var-pat + ;; If the pattern definition is a simple variable assignment, it + ;; may have dictionary parameters that need to be messed with. + ;; Complicated pattern bindings can't be overloaded in this way. + (list (add-dict-params object (do-cfn-pattern-def-simple object)))) + (irr-pat + ;; Irrefutable patterns are redundant here. + (setf (valdef-lhs object) (irr-pat-pattern (valdef-lhs object))) + (do-cfn-pattern-def-top object)) + (wildcard-pat + ;; Wildcards are no-ops. + '()) + (pcon + ;; Special-case because it's frequent and general case creates + ;; such lousy code + (do-cfn-pattern-def-pcon object)) + (else + (do-cfn-pattern-def-general object)))) + + +;;; Do a "simple" pattern definition, e.g. one that already has a +;;; var-pat on the lhs. + +(define (do-cfn-pattern-def-simple object) + (let* ((pat (valdef-lhs object)) + (sfd (car (valdef-definitions object))) + (exp (rewrite-guards-and-where-decls + (single-fun-def-where-decls sfd) + (single-fun-def-rhs-list sfd) + '#f))) + (**valdef/pat pat (cfn-ast-1 exp)))) + + +;;; Destructure a pcon. +;;; Note that the simplified expansion is only valid if none of +;;; the subpatterns introduce tests. Otherwise we must defer to +;;; the general case. + +(define (do-cfn-pattern-def-pcon object) + (let* ((pat (valdef-lhs object)) + (subpats (pcon-pats pat))) + (if (every (function irrefutable-pat?) subpats) + (let* ((con (pcon-con pat)) + (arity (con-arity con)) + (alg (con-alg con)) + (tuple? (algdata-tuple? alg)) + (temp (create-temp-var 'pbind)) + (result '())) + (dotimes (i arity) + (setf result + (nconc result + (do-cfn-pattern-def-top + (**valdef/pat (pop subpats) + (**sel con (**var/def temp) i)))))) + (if (null? result) + '() + (let* ((sfd (car (valdef-definitions object))) + (exp (cfn-ast-1 + (rewrite-guards-and-where-decls + (single-fun-def-where-decls sfd) + (single-fun-def-rhs-list sfd) + '#f)))) + (when (not tuple?) + (let ((temp1 (create-temp-var 'cfn))) + (setf exp + (**let (list (**valdef/def temp1 exp)) + (**if (**is-constructor (**var/def temp1) con) + (**var/def temp1) + (make-failure-exp)))))) + (cons (**valdef/def temp exp) result)))) + (do-cfn-pattern-def-general object)))) + + + +;;; Turn a complicated pattern definition into a list of simple ones. +;;; The idea is to use case to match the pattern and build a tuple of +;;; all the values which are being destructured into the pattern +;;; variables. + +(define (do-cfn-pattern-def-general object) + (multiple-value-bind (new-pat vars new-vars) + (copy-pattern-variables (valdef-lhs object)) + (if (not (null? vars)) + (let* ((sfd (car (valdef-definitions object))) + (exp (rewrite-guards-and-where-decls + (single-fun-def-where-decls sfd) + (single-fun-def-rhs-list sfd) + '#f)) + (arity (length vars))) + (if (eqv? arity 1) + (list (**valdef/def + (var-ref-var (car vars)) + (do-cfn-case + exp + (list (**alt/simple new-pat (car new-vars)))))) + (let ((temp (create-temp-var 'pbind)) + (bindings '())) + (dotimes (i arity) + (push (**valdef/def (var-ref-var (pop vars)) + (**tuple-sel arity i (**var/def temp))) + bindings)) + (cons (**valdef/def + temp + (do-cfn-case + exp + (list (**alt/simple new-pat (**tuple/l new-vars))))) + bindings)))) + '()))) + + + +;;; Helper function for above. +;;; All the variables in the pattern must be replaced with temporary +;;; variables. + +(define (copy-pattern-variables pat) + (typecase pat + (wildcard-pat + (values pat '() '())) + (var-pat + (let ((new (create-temp-var (var-ref-name (var-pat-var pat))))) + (values (**var-pat/def new) + (list (var-pat-var pat)) + (list (**var/def new))))) + (pcon + (multiple-value-bind (new-pats vars new-vars) + (copy-pattern-variables-list (pcon-pats pat)) + (values (**pcon/def (pcon-con pat) new-pats) + vars + new-vars))) + (as-pat + (let ((new (create-temp-var (var-ref-name (as-pat-var pat))))) + (multiple-value-bind (new-pat vars new-vars) + (copy-pattern-variables (as-pat-pattern pat)) + (values + (make as-pat + (var (**var/def new)) + (pattern new-pat)) + (cons (as-pat-var pat) vars) + (cons (**var/def new) new-vars))))) + (irr-pat + (multiple-value-bind (new-pat vars new-vars) + (copy-pattern-variables (irr-pat-pattern pat)) + (values + (make irr-pat (pattern new-pat)) + vars + new-vars))) + (const-pat + (values pat '() '())) + (plus-pat + (multiple-value-bind (new-pat vars new-vars) + (copy-pattern-variables (plus-pat-pattern pat)) + (values + (make plus-pat + (pattern new-pat) + (k (plus-pat-k pat)) + (match-fn (plus-pat-match-fn pat)) + (bind-fn (plus-pat-bind-fn pat))) + vars + new-vars))) + (list-pat + (multiple-value-bind (new-pats vars new-vars) + (copy-pattern-variables-list (list-pat-pats pat)) + (values (make list-pat (pats new-pats)) + vars + new-vars))) + (else + (error "Unrecognized pattern ~s." pat)))) + +(define (copy-pattern-variables-list pats) + (let ((new-pats '()) + (vars '()) + (new-vars '())) + (dolist (p pats) + (multiple-value-bind (p v n) (copy-pattern-variables p) + (push p new-pats) + (setf vars (nconc vars v)) + (setf new-vars (nconc new-vars n)))) + (values (nreverse new-pats) + vars + new-vars))) + + + +;;;===================================================================== +;;; Helper functions for processing guards and where-decls +;;;===================================================================== + +;;; Process guards and where-decls into a single expression. +;;; If block-name is non-nil, wrap the exp with a return-from. +;;; If block-name is nil, add a failure exp if necessary. +;;; Note that this does NOT do the CFN traversal on the result or +;;; any part of it. + +(define (rewrite-guards-and-where-decls where-decls rhs-list block-name) + (if (null? where-decls) + (rewrite-guards rhs-list block-name) + (**let where-decls + (rewrite-guards rhs-list block-name)))) + +(define (rewrite-guards rhs-list block-name) + (if (null? rhs-list) + (if block-name + (**con/def (core-symbol "False")) + (make-failure-exp)) + (let* ((rhs (car rhs-list)) + (guard (guarded-rhs-guard rhs)) + (exp (guarded-rhs-rhs rhs))) + (when block-name + (setf exp (**return-from block-name exp))) + (cond ((is-type? 'omitted-guard (guarded-rhs-guard (car rhs-list))) + exp) + ((and block-name (null? (cdr rhs-list))) + (**and-exp guard exp)) + (else + (**if guard + exp + (rewrite-guards (cdr rhs-list) block-name))) + )))) + + +(define (make-failure-exp) + (let ((c (dynamic *context*))) + (**abort + (if (not c) + "Pattern match failed." + (let* ((stuff (ast-node-line-number c)) + (line (source-pointer-line stuff)) + (file (source-pointer-file stuff))) + (if (and (is-type? 'valdef c) + (is-type? 'var-pat (valdef-lhs c))) + (format + '#f + "Pattern match failed in function ~a at line ~s in file ~a." + (valdef-lhs c) line file) + (format + '#f + "Pattern match failed at line ~s in file ~a." + line file))))))) + + + + diff --git a/cl-support/PORTING b/cl-support/PORTING new file mode 100644 index 0000000..2114be5 --- /dev/null +++ b/cl-support/PORTING @@ -0,0 +1,105 @@ +Here are the steps involved in porting to a new Common Lisp implementation. + +(0) Find the executable you want to use. If possible, use an image + that doesn't have stuff like CLX, CLOS, a snazzy editor, and the + like loaded, since we don't use any of that stuff. + + Put an environment variable in the haskell-development script to point + to the lisp you want to run. + +(1) You must add appropriate conditionalizations to cl-init.lisp and + cl-definitions.lisp in this directory. Look for places where there + are #+ things for the other dialects. + + As a matter of style, try to make an explicit case for each Lisp + instead of using #- to test for it *not* being a particular dialect. + This will prevent confusion on future ports. + + You may also need/want to add conditionals to the primitive + implementation files in the runtime directory. + + Do not add #+/#- conditionalizations to any other random .scm + files, since we want to keep this implementation-dependent stuff + centralized. + +(2) Make subdirectories to hold compiled files in each of the source + directories. The name of the subdirectory must match the constant + lisp-implementation-name in cl-definitions.lisp. + +(3) Try compiling the Haskell system (by loading cl-init.lisp) and + fix any compilation warnings that happen. (Hopefully there won't + be any.) + + You probably want to build a system initially with the default + compiler settings and verbose compiler diagnostics. This will make + any problems that show up later easier to debug. Also, it is + helpful to capture all the messages in a dribble file to make it + easier to verify that everything went OK. + +(4) Try compiling the prelude using (compile/prelude *prelude-unit*). + You need to create a subdirectory in the progs/prelude directory + to hold the output files, and define $PRELUDEBIN to point at + this directory (see the haskell-setup script). + + The important thing at this point is that the prelude makes it all + the way through the codegen phase and produces Lisp code. Don't worry + too much now if the Lisp compiler has trouble digesting the output. + + Once you get to this stage, it's time to start messing with + compiler optimize proclamations. We generally use (speed 3) and + (safety 0). Also, you should figure out how to suppress any + compiler messages (e.g., set *compile-print* and *compile-verbose* + to false). We usually leave *compile-verbose* on during compilation + of the Haskell compiler, but turn it off later so that people don't + get messages from the Lisp compiler when running Haskell programs. + +(5) Make a subdirectory in the com directory and make the following set + of scripts there: + + clean -- remove all binary files. Also change the main com/clean + script to invoke this. + compile -- recompile everything with the right compiler flags (see + step 5). + build-prelude -- run the prelude through the haskell compiler. + This should save the old compiled prelude files as old-prelude.* + case something goes wrong. + savesys -- load the compiled prelude and save a core file. + This should also be careful not to overwrite an existing file. + + Look at the scripts that have already been written for other Lisps + for hints. + + At some point you also need to put a README file in this directory. + + +(6) Now it's time to get serious about getting the prelude to compile. + Use the clean, compile, and build-prelude scripts you just wrote. + + Some compilers have a hard time dealing with the large pieces of + Lisp code produced for the prelude. You will probably need to do + something to make the heap bigger. (And, make sure the machine + you are using to do the build on has plenty of swap space.) You + may also need to tweak the chunk-size parameters to force the + output to be split up into smaller pieces. + + It's OK to leave the prelude interface file as a source file, or + to compile it with low speed optimizations. On the other hand, + the prelude code file ought to be processed with as many speed + optimizations as possible. + +(7) Build a new executable using the "savesys" script and take it for + a test drive. + +(8) You must also hack the emacs interface file, emacs-tools/haskell.el, + to recognize when it's gotten into the debugger or break loop. + To test your new executable with the emacs stuff, you must + define the environment variable HASKELLPROG to point at it, or + set the emacs variable haskell-program-name. + +(9) If you want to use the Haskell->CLX interface, you'll have to + mess with equivalents of the build-xlib and savesys-xlib scripts. + There is some system-dependent code in xlibclx.scm to set up an + error handler -- make sure you have conditionalized this appropriately + for your Lisp system. + + diff --git a/cl-support/README b/cl-support/README new file mode 100644 index 0000000..5553e4a --- /dev/null +++ b/cl-support/README @@ -0,0 +1,3 @@ +This directory contains Common-Lisp-syntax files to set up a more +Scheme-like environment. Load cl-init.lisp and it will suck in all +the rest. diff --git a/cl-support/cl-definitions.lisp b/cl-support/cl-definitions.lisp new file mode 100644 index 0000000..8727679 --- /dev/null +++ b/cl-support/cl-definitions.lisp @@ -0,0 +1,1351 @@ +;;; cl-definitions.lisp -- mumble compatibility package for Common Lisp +;;; +;;; author : Sandra Loosemore +;;; date : 11 Oct 1991 +;;; +;;; You must load cl-setup and cl-support before trying to compile this +;;; file. + +(in-package "MUMBLE-IMPLEMENTATION") + + +;;;===================================================================== +;;; Syntax +;;;===================================================================== + +(define-mumble-import quote) +(define-mumble-import function) + +;;; Lambda lists have to have dot syntax converted to &rest. + +(define-mumble-macro mumble::lambda (lambda-list &rest body) + `(function (lambda ,(mung-lambda-list lambda-list) ,@body))) + +(defun mung-lambda-list (lambda-list) + (cond ((consp lambda-list) + (let ((last (last lambda-list))) + (if (null (cdr last)) + lambda-list + `(,@(ldiff lambda-list last) ,(car last) &rest ,(cdr last))))) + ((null lambda-list) + '()) + (t + `(&rest ,lambda-list)))) + + +;;; We only funcall and apply things that are real functions. + + +;;; Gag. Lucid needs to see the procedure declaration to avoid putting +;;; a coerce-to-procedure check in, but there's a compiler bug that causes +;;; it to barf if the function is a lambda form. + +#+lucid +(define-mumble-macro mumble::funcall (fn . args) + (if (and (consp fn) (eq (car fn) 'mumble::lambda)) + `(funcall ,fn ,@args) + `(funcall (the system::procedure ,fn) ,@args))) + +#+(or cmu allegro akcl lispworks mcl) +(define-mumble-macro mumble::funcall (fn . args) + `(funcall (the function ,fn) ,@args)) + +#+wcl +(define-mumble-macro mumble::funcall (fn . args) + `(funcall (the lisp:procedure ,fn) ,@args)) + +#-(or lucid cmu allegro akcl mcl lispworks wcl) +(missing-mumble-definition mumble::funcall) + + +;;; Could make this declare its fn argument too + +(define-mumble-import apply) + +(define-mumble-synonym mumble::map mapcar) +(define-mumble-synonym mumble::for-each mapc) +(define-mumble-import some) +(define-mumble-import every) +(define-mumble-import notany) +(define-mumble-import notevery) +(define-mumble-synonym mumble::procedure? functionp) + + +(define-mumble-import if) +(define-mumble-import when) +(define-mumble-import unless) + + +;;; COND and CASE differ from Common Lisp because of using "else" instead +;;; of "t" as the fall-through case. + +(define-mumble-import mumble::else) + +(define-mumble-macro mumble::cond (&rest cases) + (let ((last (car (last cases)))) + (if (eq (car last) 'mumble::else) + `(cond ,@(butlast cases) (t ,@(cdr last))) + `(cond ,@cases)))) + +(define-mumble-macro mumble::case (data &rest cases) + (let ((last (car (last cases)))) + (if (eq (car last) 'mumble::else) + `(case ,data ,@(butlast cases) (t ,@(cdr last))) + `(case ,data ,@cases)))) + + +(define-mumble-import and) +(define-mumble-import or) +(define-mumble-import not) + +(define-mumble-macro mumble::set! (variable value) + `(setq ,variable ,value)) +(define-mumble-import setf) + + +;;; AKCL's SETF brokenly tries to macroexpand the place +;;; form before looking for a define-setf-method. Redefine the +;;; internal function to do the right thing. + +#+akcl +(defun system::setf-expand-1 (place newvalue env) + (multiple-value-bind (vars vals stores store-form access-form) + (get-setf-method place env) + (declare (ignore access-form)) + `(let* ,(mapcar #'list + (append vars stores) + (append vals (list newvalue))) + ,store-form))) + + +;;; Allegro has renamed this stuff as per ANSI CL. + +#+allegro +(eval-when (eval compile load) + (setf (macro-function 'define-setf-method) + (macro-function 'define-setf-expander)) + (setf (symbol-function 'get-setf-method) + (symbol-function 'get-setf-expansion)) + ) + +(define-mumble-import let) +(define-mumble-import let*) + +(define-mumble-macro mumble::letrec (bindings &rest body) + `(let ,(mapcar #'car bindings) + ,@(mapcar #'(lambda (b) (cons 'setq b)) bindings) + (locally ,@body))) + +(define-mumble-import flet) +(define-mumble-import labels) + +(define-mumble-macro mumble::dynamic-let (bindings &rest body) + `(let ,bindings + (declare (special ,@(mapcar #'car bindings))) + ,@body)) + +(define-mumble-macro mumble::dynamic (name) + `(locally (declare (special ,name)) ,name)) + +(define-setf-method mumble::dynamic (name) + (let ((store (gensym))) + (values nil + nil + (list store) + `(locally (declare (special ,name)) (setf ,name ,store)) + `(locally (declare (special ,name)) ,name)))) + + +(define-mumble-macro mumble::begin (&rest body) + `(progn ,@body)) + +(define-mumble-import block) +(define-mumble-import return-from) + +(define-mumble-import do) +(define-mumble-import dolist) +(define-mumble-import dotimes) + +(define-mumble-import values) +(define-mumble-import multiple-value-bind) + +(define-mumble-macro mumble::let/cc (variable &rest body) + (let ((tagvar (gensym))) + `(let* ((,tagvar (gensym)) + (,variable (let/cc-aux ,tagvar))) + (catch ,tagvar (locally ,@body))))) + +(defun let/cc-aux (tag) + #'(lambda (&rest values) + (throw tag (values-list values)))) + + +(define-mumble-import unwind-protect) + +(define-mumble-import declare) +(define-mumble-import ignore) + + +;;; IGNORABLE is part of ANSI CL but not implemented by Lucid yet. +;;; IGNORE in Lucid seems to behave like what ANSI CL says IGNORABLE +;;; should do, but there doesn't seem to be any way to rename it. + +#+(or lucid akcl lispworks wcl) +(progn + (proclaim '(declaration mumble::ignorable)) + (define-mumble-import mumble::ignorable)) + +#+(or cmu mcl allegro) +(define-mumble-import cl:ignorable) + +#-(or lucid cmu allegro akcl mcl lispworks wcl) +(missing-mumble-definition mumble::ignorable) + + +(define-mumble-import type) + + + +;;;===================================================================== +;;; Definitions +;;;===================================================================== + + +;;; *** This shouldn't really do a DEFPARAMETER, since that proclaims +;;; *** the variable SPECIAL and makes any LETs of the variable do +;;; *** special binding rather than lexical binding. But if you just +;;; *** SETF the variable, you'll get a compiler warning about an +;;; *** undeclared free variable on every reference!!! Argggh. + +(define-mumble-macro mumble::define (pattern &rest value) + (if (consp pattern) + `(defun ,(car pattern) ,(mung-lambda-list (cdr pattern)) ,@value) + `(defparameter ,pattern ,(car value)))) + +(define-mumble-macro mumble::define-integrable (pattern &rest value) + (if (consp pattern) + `(progn + (eval-when (eval compile load) + (proclaim '(inline ,(car pattern)))) + (defun ,(car pattern) ,(mung-lambda-list (cdr pattern)) ,@value)) + `(defconstant ,pattern ,(car value)))) + + +(define-mumble-macro mumble::define-syntax (pattern . body) + `(defmacro ,(car pattern) ,(mung-lambda-list (cdr pattern)) ,@body)) + +(define-mumble-macro mumble::define-local-syntax (pattern . body) + `(eval-when (eval compile) + (defmacro ,(car pattern) ,(mung-lambda-list (cdr pattern)) ,@body))) + + +(define-mumble-macro mumble::define-setf (getter setter) + `(define-setf-method ,getter (&rest subforms) + (define-setf-aux ',setter ',getter subforms))) + +(defun define-setf-aux (setter getter subforms) + (let ((temps nil) + (tempvals nil) + (args nil) + (store (gensym))) + (dolist (x subforms) + (if (constantp x) + (push x args) + (let ((temp (gensym))) + (push temp temps) + (push x tempvals) + (push temp args)))) + (setq temps (nreverse temps)) + (setq tempvals (nreverse tempvals)) + (setq args (nreverse args)) + (values temps + tempvals + (list store) + `(,setter ,store ,@args) + `(,getter ,@args)))) + + +;;; Declaring variables special will make the compiler not proclaim +;;; about references to them. +;;; A proclamation works to disable undefined function warnings in +;;; most Lisps. Harlequin seems to offer no way to shut up these warnings. +;;; In allegro, we have to work around a bug in the compiler's handling +;;; of PROCLAIM. + +(define-mumble-macro mumble::predefine (pattern) + `(eval-when (eval compile) + #+allegro (let ((excl::*compiler-environment* nil)) + (do-predefine ',pattern)) + #-allegro (do-predefine ',pattern) + )) + +(eval-when (eval compile load) + (defun do-predefine (pattern) + (if (consp pattern) + (proclaim `(ftype (function ,(mung-decl-lambda-list (cdr pattern)) t) + ,(car pattern))) + (proclaim `(special ,pattern)))) + (defun mung-decl-lambda-list (lambda-list) + (cond ((consp lambda-list) + (cons 't (mung-decl-lambda-list (cdr lambda-list)))) + ((null lambda-list) + '()) + (t + '(&rest t)))) + ) + + +;;; CMUCL doesn't complain about function redefinitions, but Lucid does. + +#+(or cmu akcl mcl lispworks wcl) +(define-mumble-macro mumble::redefine (pattern . value) + `(mumble::define ,pattern ,@value)) + +#+lucid +(define-mumble-macro mumble::redefine (pattern . value) + `(let ((lcl:*redefinition-action* nil)) + (mumble::define ,pattern ,@value))) + +#+allegro +(define-mumble-macro mumble::redefine (pattern . value) + `(let ((excl:*redefinition-warnings* nil)) + (mumble::define ,pattern ,@value))) + +#-(or cmu lucid allegro akcl mcl lispworks wcl) +(missing-mumble-definition mumble::redefine) + + +#+(or cmu akcl mcl lispworks wcl) +(define-mumble-macro mumble::redefine-syntax (pattern . body) + `(mumble::define-syntax ,pattern ,@body)) + +#+lucid +(define-mumble-macro mumble::redefine-syntax (pattern . body) + `(eval-when (eval compile load) + (let ((lcl:*redefinition-action* nil)) + (mumble::define-syntax ,pattern ,@body)))) + +#+allegro +(define-mumble-macro mumble::redefine-syntax (pattern . body) + `(eval-when (eval compile load) + (let ((excl:*redefinition-warnings* nil)) + (mumble::define-syntax ,pattern ,@body)))) + +#-(or cmu lucid allegro akcl mcl lispworks wcl) +(missing-mumble-definition mumble::redefine-syntax) + + + +;;;===================================================================== +;;; Equivalence +;;;===================================================================== + +(define-mumble-function-inline mumble::eq? (x y) + (eq x y)) +(define-mumble-function-inline mumble::eqv? (x y) + (eql x y)) + +(define-mumble-function mumble::equal? (x1 x2) + (cond ((eql x1 x2) + t) + ((consp x1) + (and (consp x2) + (mumble::equal? (car x1) (car x2)) + (mumble::equal? (cdr x1) (cdr x2)))) + ((simple-string-p x1) + (and (simple-string-p x2) + (string= x1 x2))) + ((simple-vector-p x1) + (and (simple-vector-p x2) + (eql (length (the simple-vector x1)) + (length (the simple-vector x2))) + (every #'mumble::equal? + (the simple-vector x1) + (the simple-vector x2)))) + (t nil))) + + +;;;===================================================================== +;;; Lists +;;;===================================================================== + +(define-mumble-function-inline mumble::pair? (x) + (consp x)) + +(define-mumble-import cons) + + +;;; Can't import this directly because of type problems. + +(define-mumble-synonym mumble::list list) + +(define-mumble-function-inline mumble::make-list (length &optional (init nil)) + (the list + (make-list length :initial-element init))) + +(define-mumble-import car) +(define-mumble-import cdr) +(define-mumble-import caar) +(define-mumble-import cadr) +(define-mumble-import cadr) +(define-mumble-import cddr) +(define-mumble-import caaar) +(define-mumble-import caadr) +(define-mumble-import caadr) +(define-mumble-import caddr) +(define-mumble-import cdaar) +(define-mumble-import cdadr) +(define-mumble-import cdadr) +(define-mumble-import cdddr) +(define-mumble-import caaaar) +(define-mumble-import caaadr) +(define-mumble-import caaadr) +(define-mumble-import caaddr) +(define-mumble-import cadaar) +(define-mumble-import cadadr) +(define-mumble-import cadadr) +(define-mumble-import cadddr) +(define-mumble-import cdaaar) +(define-mumble-import cdaadr) +(define-mumble-import cdaadr) +(define-mumble-import cdaddr) +(define-mumble-import cddaar) +(define-mumble-import cddadr) +(define-mumble-import cddadr) +(define-mumble-import cddddr) + +(define-mumble-function-inline mumble::null? (x) + (null x)) + +(define-mumble-function mumble::list? (x) + (cond ((null x) t) + ((consp x) (mumble::list? (cdr x))) + (t nil))) + +(define-mumble-function-inline mumble::length (x) + (the fixnum (length (the list x)))) + +(define-mumble-import append) +(define-mumble-import nconc) + +(define-mumble-function-inline mumble::reverse (x) + (the list (reverse (the list x)))) +(define-mumble-function-inline mumble::nreverse (x) + (the list (nreverse (the list x)))) + +(define-mumble-function-inline mumble::list-tail (list n) + (nthcdr n list)) +(define-mumble-function-inline mumble::list-ref (list n) + (nth n list)) + +(define-mumble-import last) +(define-mumble-import butlast) + +(define-setf-method mumble::list-ref (list n) + (get-setf-method `(nth ,n ,list))) + +(define-mumble-function-inline mumble::memq (object list) + (member object list :test #'eq)) +(define-mumble-function-inline mumble::memv (object list) + (member object list)) +(define-mumble-function-inline mumble::member (object list) + (member object list :test #'mumble::equal?)) + +;;; *** The Lucid compiler is not doing anything inline for assq so +;;; *** I'm rewriting this -- jcp +(define-mumble-function mumble::assq (object list) + (if (null list) + nil + (if (eq object (caar list)) + (car list) + (mumble::assq object (cdr list))))) + +(define-mumble-function-inline mumble::assv (object list) + (assoc object list)) +(define-mumble-function-inline mumble::assoc (object list) + (assoc object list :test #'mumble::equal?)) + +(define-mumble-import push) +(define-mumble-import pop) + +(define-mumble-synonym mumble::list-copy copy-list) + + +;;;===================================================================== +;;; Symbols +;;;===================================================================== + +(define-mumble-function-inline mumble::symbol? (x) + (symbolp x)) +(define-mumble-synonym mumble::symbol->string symbol-name) + +(define-mumble-function-inline mumble::string->symbol (x) + (intern x)) + + +;;; We want a gensym that follows the new ANSI CL gensym-name-stickiness +;;; decision. + +#+(or lucid akcl wcl) +(define-mumble-function mumble::gensym (&optional (prefix "G")) + (gensym prefix)) + +#+(or cmu allegro mcl lispworks) +(define-mumble-import gensym) + +#-(or lucid akcl wcl cmu allegro mcl lispworks) +(missing-mumble-definition mumble::gensym) + +(define-mumble-function mumble::gensym? (x) + (and (symbolp x) + (not (symbol-package x)))) + +(defun symbol-append (&rest symbols) + (intern (apply #'concatenate 'string (mapcar #'symbol-name symbols)))) +(define-mumble-import symbol-append) + + +;;;===================================================================== +;;; Characters +;;;===================================================================== + +(define-mumble-function-inline mumble::char? (x) + (characterp x)) + +(define-mumble-synonym mumble::char=? char=) +(define-mumble-synonym mumble::char? char>) +(define-mumble-synonym mumble::char>=? char>=) +(define-mumble-synonym mumble::char<=? char<=) + +(define-mumble-synonym mumble::char-ci=? char-equal) +(define-mumble-synonym mumble::char-ci? char-greaterp) +(define-mumble-synonym mumble::char-ci>=? char-not-lessp) +(define-mumble-synonym mumble::char-ci<=? char-not-greaterp) + +(define-mumble-synonym mumble::char-alphabetic? alpha-char-p) +(define-mumble-synonym mumble::char-numeric? digit-char-p) + +(define-mumble-function mumble::char-whitespace? (c) + (member c '(#\space #\tab #\newline #\linefeed #\page #\return))) + +(define-mumble-synonym mumble::char-upper-case? upper-case-p) +(define-mumble-synonym mumble::char-lower-case? lower-case-p) + +(define-mumble-synonym mumble::char->integer char-code) +(define-mumble-synonym mumble::integer->char code-char) + +(define-mumble-import char-upcase) +(define-mumble-import char-downcase) +(define-mumble-import char-name) + +(define-mumble-synonym mumble::char->digit digit-char-p) + + +;;;===================================================================== +;;; Strings +;;;===================================================================== + +(define-mumble-function-inline mumble::string? (x) + (simple-string-p x)) + +(define-mumble-function-inline mumble::make-string + (length &optional (init nil init-p)) + (the simple-string + (if init-p + (make-string length :initial-element init) + (make-string length)))) + +(define-mumble-function-inline mumble::string (char &rest more-chars) + (the simple-string (coerce (cons char more-chars) 'string))) + +(define-mumble-function-inline mumble::string-length (string) + (the fixnum (length (the simple-string string)))) + +(define-mumble-function-inline mumble::string-ref (x n) + (the character (schar (the simple-string x) (the fixnum n)))) + +(define-setf-method mumble::string-ref (string n) + (get-setf-method `(schar ,string ,n))) + +(define-mumble-synonym mumble::string=? string=) +(define-mumble-synonym mumble::string? string>) +(define-mumble-synonym mumble::string<=? string<=) +(define-mumble-synonym mumble::string>=? string>=) + +(define-mumble-synonym mumble::string-ci=? string-equal) +(define-mumble-synonym mumble::string-ci? string-greaterp) +(define-mumble-synonym mumble::string-ci<=? string-not-greaterp) +(define-mumble-synonym mumble::string-ci>=? string-not-lessp) + +(define-mumble-function-inline mumble::substring (string start end) + (the simple-string (subseq (the simple-string string) start end))) + +(define-mumble-function-inline mumble::string-append + (string &rest more-strings) + (declare (type simple-string string)) + (the simple-string (apply #'concatenate 'string string more-strings))) + +(define-mumble-function-inline mumble::string->list (string) + (the list (coerce (the simple-string string) 'list))) + +(define-mumble-function-inline mumble::list->string (list) + (the simple-string (coerce (the list list) 'string))) + +(define-mumble-function-inline mumble::string-copy (string) + (the simple-string (copy-seq (the simple-string string)))) + +(define-mumble-import string-upcase) +(define-mumble-import string-downcase) + + +;;;===================================================================== +;;; Vectors +;;;===================================================================== + +(define-mumble-function-inline mumble::vector? (x) + (simple-vector-p x)) + +(define-mumble-function-inline mumble::make-vector + (length &optional (init nil init-p)) + (declare (type fixnum length)) + (the simple-vector + (if init-p + (make-array length :initial-element init) + (make-array length)))) + + +;;; Can't import directly because types are incompatible. + +(define-mumble-synonym mumble::vector vector) + +(define-mumble-function-inline mumble::vector-length (vector) + (the fixnum (length (the simple-vector vector)))) + +(define-mumble-function-inline mumble::vector-ref (x n) + (svref (the simple-vector x) (the fixnum n))) + +(define-setf-method mumble::vector-ref (vector n) + (get-setf-method `(svref ,vector ,n))) + +(define-mumble-function-inline mumble::vector->list (vector) + (the list (coerce (the simple-vector vector) 'list))) + +(define-mumble-function-inline mumble::list->vector (list) + (the simple-vector (coerce (the list list) 'simple-vector))) + +(define-mumble-function-inline mumble::vector-copy (vector) + (the simple-vector (copy-seq (the simple-vector vector)))) + + +;;;===================================================================== +;;; Numbers +;;;===================================================================== + +(define-mumble-synonym mumble::number? numberp) +(define-mumble-synonym mumble::integer? integerp) +(define-mumble-synonym mumble::rational? rationalp) +(define-mumble-synonym mumble::float? floatp) + +(define-mumble-function-inline mumble::fixnum? (x) + (typep x 'fixnum)) + +(define-mumble-synonym mumble::exact->inexact float) + +(define-mumble-import =) +(define-mumble-import <) +(define-mumble-import >) +(define-mumble-import <=) +(define-mumble-import >=) + +(define-mumble-synonym mumble::zero? zerop) +(define-mumble-function-inline mumble::positive? (x) + (> x 0)) +(define-mumble-function-inline mumble::negative? (x) + (< x 0)) + +(define-mumble-import min) +(define-mumble-import max) + +(define-mumble-import +) +(define-mumble-import *) +(define-mumble-import -) +(define-mumble-import /) + +(define-mumble-synonym mumble::quotient floor) +(define-mumble-synonym mumble::remainder rem) +(define-mumble-synonym mumble::modulo mod) + +(define-mumble-function-inline mumble::floor (x) + (if (floatp x) (ffloor x) (floor (the rational x)))) +(define-mumble-function-inline mumble::ceiling (x) + (if (floatp x) (fceiling x) (ceiling (the rational x)))) +(define-mumble-function-inline mumble::truncate (x) + (if (floatp x) (ftruncate x) (truncate (the rational x)))) +(define-mumble-function-inline mumble::round (x) + (if (floatp x) (fround x) (round (the rational x)))) + +(define-mumble-synonym mumble::floor->exact floor) +(define-mumble-synonym mumble::ceiling->exact ceiling) +(define-mumble-synonym mumble::truncate->exact truncate) +(define-mumble-synonym mumble::round->exact round) + +(define-mumble-import 1+) +(define-mumble-import 1-) +(define-mumble-import incf) +(define-mumble-import decf) + +(define-mumble-function mumble::number->string (number &optional (radix 10)) + (let ((*print-base* radix)) + (prin1-to-string number))) + +(define-mumble-function mumble::string->number (string &optional (radix 10)) + (let ((*read-base* radix)) + (read-from-string string))) + +(define-mumble-import expt) + + + +;;;===================================================================== +;;; Tables +;;;===================================================================== + +(define-mumble-synonym mumble::table? hash-table-p) + +(define-mumble-function-inline mumble::make-table () + (make-hash-table :test #'eq)) + +(define-mumble-function-inline mumble::table-entry (table key) + (gethash key table)) + +(define-setf-method mumble::table-entry (table key) + (get-setf-method `(gethash ,key ,table))) + +(define-mumble-synonym mumble::table-for-each maphash) + +(define-mumble-function mumble::copy-table (old-table) + (let ((new-table (make-hash-table :test #'eq + :size (1+ (hash-table-count old-table))))) + (maphash #'(lambda (key val) (setf (gethash key new-table) val)) + old-table) + new-table)) + + +;;;===================================================================== +;;; I/O +;;;===================================================================== + +(define-mumble-function-inline mumble::call-with-input-file (string proc) + (with-open-file (stream (expand-filename string) :direction :input) + (funcall (the function proc) stream))) + +(define-mumble-function-inline mumble::call-with-output-file (string proc) + (with-open-file (stream (expand-filename string) + :direction :output :if-exists :new-version) + (funcall (the function proc) stream))) + +(define-mumble-function-inline mumble::call-with-input-string (string proc) + (with-input-from-string (stream string) + (funcall (the function proc) stream))) + +(define-mumble-function-inline mumble::call-with-output-string (proc) + (with-output-to-string (stream) + (funcall (the function proc) stream))) + +(define-mumble-synonym mumble::input-port? input-stream-p) +(define-mumble-synonym mumble::output-port? output-stream-p) + +(define-mumble-function-inline mumble::current-input-port () + *standard-input*) +(define-mumble-function-inline mumble::current-output-port () + *standard-output*) + +(define-mumble-function-inline mumble::open-input-file (string) + (open (expand-filename string) :direction :input)) + +(define-mumble-function-inline mumble::open-output-file (string) + (open (expand-filename string) :direction :output :if-exists :new-version)) + + +(define-mumble-synonym mumble::close-input-port close) +(define-mumble-synonym mumble::close-output-port close) + +(defvar *eof-object* (make-symbol "EOF")) + +(define-mumble-function-inline mumble::read + (&optional (port *standard-input*)) + (read port nil *eof-object*)) + +(define-mumble-function-inline mumble::read-char + (&optional (port *standard-input*)) + (read-char port nil *eof-object*)) + +(define-mumble-function-inline mumble::peek-char + (&optional (port *standard-input*)) + (peek-char nil port nil *eof-object*)) + +(define-mumble-function-inline mumble::read-line + (&optional (port *standard-input*)) + (read-line port nil *eof-object*)) + +(define-mumble-function-inline mumble::eof-object? (x) + (eq x *eof-object*)) + + +;;;===================================================================== +;;; Printer +;;;===================================================================== + +(define-mumble-function mumble::internal-write (object port) + (write object :stream port)) +(define-mumble-function-inline mumble::internal-output-width (port) + (declare (ignore port)) + nil) +(define-mumble-function-inline mumble::internal-output-position (port) + (declare (ignore port)) + nil) +(define-mumble-synonym mumble::internal-write-char write-char) +(define-mumble-function-inline mumble::internal-write-string + (string port start end) + (write-string string port :start start :end end)) +(define-mumble-synonym mumble::internal-newline terpri) +(define-mumble-synonym mumble::internal-fresh-line fresh-line) +(define-mumble-synonym mumble::internal-finish-output finish-output) +(define-mumble-synonym mumble::internal-force-output force-output) +(define-mumble-synonym mumble::internal-clear-output clear-output) + +(define-mumble-function mumble::internal-write-to-string (object) + (write-to-string object)) + + +(define-mumble-function-inline mumble::internal-warning (string) + (warn "~a" string)) + +(define-mumble-function-inline mumble::internal-error (string) + (error "~a" string)) + + +;;; Printer stuff used directly by the pretty printer + +(define-mumble-import *print-escape*) +(define-mumble-import *print-circle*) +(define-mumble-import *print-pretty*) +(define-mumble-import *print-level*) +(define-mumble-import *print-length*) +(define-mumble-import *print-base*) +(define-mumble-import *print-radix*) + + +;;; These functions and variables are all defined with the XP stuff. But, +;;; let's export all the symbols from the mumble package. + +(define-mumble-import mumble::write) +(define-mumble-import mumble::print) +(define-mumble-import mumble::prin1) +(define-mumble-import mumble::princ) +(define-mumble-import mumble::pprint) +(define-mumble-import mumble::prin1-to-string) +(define-mumble-import mumble::princ-to-string) +(define-mumble-import mumble::write-char) +(define-mumble-import mumble::write-string) +(define-mumble-import mumble::write-line) +(define-mumble-import mumble::terpri) +(define-mumble-import mumble::fresh-line) +(define-mumble-import mumble::finish-output) +(define-mumble-import mumble::force-output) +(define-mumble-import mumble::clear-output) +(define-mumble-import mumble::display) +(define-mumble-import mumble::newline) +(define-mumble-import mumble::*print-shared*) +(define-mumble-import mumble::*print-dispatch*) +(define-mumble-import mumble::*print-right-margin*) +(define-mumble-import mumble::*print-miser-width*) +(define-mumble-import mumble::*print-lines*) +(define-mumble-import mumble::*default-right-margin*) +(define-mumble-import mumble::*last-abbreviated-printing*) +(define-mumble-import mumble::*print-structure*) +(define-mumble-import mumble::*print-structure-slots*) +(define-mumble-import mumble::standard-print-dispatch) +(define-mumble-import mumble::pprint-newline) +(define-mumble-import mumble::pprint-logical-block) +(define-mumble-import mumble::pprint-pop) +(define-mumble-import mumble::pprint-exit-if-list-exhausted) +(define-mumble-import mumble::pprint-indent) +(define-mumble-import mumble::pprint-tab) +(define-mumble-import mumble::pprint-fill) +(define-mumble-import mumble::pprint-linear) +(define-mumble-import mumble::pprint-tabular) +(define-mumble-import mumble::format) +(define-mumble-import mumble::warning) +(define-mumble-import mumble::error) + + +;;; These are keywords for pprint-newline. + +(define-mumble-import mumble::linear) +(define-mumble-import mumble::fill) +(define-mumble-import mumble::miser) +(define-mumble-import mumble::mandatory) + +;;; These are keywords for pprint-indent + +;; (define-mumble-import mumble::block) ; already imported as special form +(define-mumble-import mumble::current) + +;;; These are keywords for pprint-tab + +(define-mumble-import mumble::line) +(define-mumble-import mumble::section) +(define-mumble-import mumble::line-relative) +(define-mumble-import mumble::section-relative) + + +;;;===================================================================== +;;; System Interface +;;;===================================================================== + +(define-mumble-import macroexpand-1) +(define-mumble-import macroexpand) + + +;;; WITH-COMPILATION-UNIT is an ANSI CL feature that isn't yet +;;; supported by all Lisps. + +#+lucid +(define-mumble-macro mumble::with-compilation-unit (options &body body) + (declare (ignore options)) + `(lcl:with-deferred-warnings ,@body)) + +#+(or cmu mcl allegro lispworks) +(define-mumble-import with-compilation-unit) + +#+(or akcl wcl) +(define-mumble-macro mumble::with-compilation-unit (options &body body) + (declare (ignore options)) + `(progn ,@body)) + +#-(or lucid allegro cmu akcl mcl lispworks wcl) +(missing-mumble-definition mumble::with-compilation-unit) + + +(define-mumble-function mumble::eval (form &optional compile-p) + (if compile-p + (mumble::with-compilation-unit () + (eval-compiling-functions form)) + (eval form))) + + +;;; Simply doing (funcall (compile nil `(lambda () ,form))) would work +;;; except that top-level-ness actions would be lost (causing extraneous +;;; warning messages about global variables whose references are compiled +;;; before a previous predefine is executed, etc). So make an attempt +;;; to process nested top-level forms in order. This doesn't look for +;;; all of the common-lispy things that might show up in macro expansions, +;;; but it's close enough. + +(defun eval-compiling-functions (form) + (if (atom form) + (eval form) + (let ((fn (car form))) + (cond ((or (eq fn 'mumble::begin) + (eq fn 'progn)) + (do ((forms (cdr form) (cdr forms))) + ((null (cdr forms)) (eval-compiling-functions (car forms))) + (eval-compiling-functions (car forms)))) + ((eq fn 'mumble::define) + (if (consp (cadr form)) + (compile-define form) + (compile-other form))) + ((eq fn 'mumble::define-integrable) + (if (consp (cadr form)) + (progn + (proclaim `(inline ,(car (cadr form)))) + (compile-define form)) + (compile-other form))) + ((eq fn 'mumble::predefine) + (do-predefine (cadr form))) + ((macro-function fn) + (eval-compiling-functions (macroexpand-1 form))) + (t + (compile-other form)))))) + +(defun compile-define (form) + (let ((name (car (cadr form))) + (args (mung-lambda-list (cdr (cadr form)))) + (body (cddr form))) + (compile name `(lambda ,args ,@body)) + name)) + +(defun compile-other (form) + (funcall (compile nil `(lambda () ,form)))) + + +;;; Load and compile-file aren't directly imported from the host +;;; Common Lisp because we want to do our own defaulting of file +;;; name extensions. + +(define-mumble-function mumble::load (filename) + (setq filename (expand-filename filename)) + (if (string= (mumble::filename-type filename) "") + (let ((source-file (build-source-filename filename)) + (binary-file (build-binary-filename filename))) + (if (and (probe-file binary-file) + (> (file-write-date binary-file) + (file-write-date source-file))) + (load binary-file) + (load source-file))) + (load filename))) + + +;;; This is used to control OPTIMIZE declarations in a somewhat more +;;; portable way -- different implementations may need slightly different +;;; combinations. +;;; 0 = do as little as possible when compiling code +;;; 1 = use "default" compiler settings +;;; 2 = omit safety checks and do "easy" speed optimizations. +;;; 3 = do as much as possible; type inference, inlining, etc. May be slow. +;;; #f = don't mess with optimize settings. + +(defvar *code-quality* nil) +(define-mumble-import *code-quality*) + +(defun code-quality-hack (q) + (cond ((eql q 0) + (proclaim '(optimize (speed 1) (safety 3) (compilation-speed 3) + #+cmu (ext:debug 1) + #+(or mcl allegro lispworks) (debug 1) + ))) + ((eql q 1) + (proclaim '(optimize (speed 1) (safety 1) (compilation-speed 3) + #+cmu (ext:debug 1) + #+(or mcl allegro lispworks) (debug 1) + ))) + ((eql q 2) + (proclaim '(optimize (speed 3) (safety 0) (compilation-speed 3) + #+cmu (ext:debug 0) + #+(or mcl allegro lispworks) (debug 0) + ))) + ((eql q 3) + (proclaim '(optimize (speed 3) (safety 0) (compilation-speed 0) + #+cmu (ext:debug 0) + #+(or mcl allegro lispworks) (debug 0) + ))) + (t + (warn "Bogus *code-quality* setting ~s." q)))) + + +;;; If we don't do this, code generated with high code-quality settings +;;; can't be interrupted with ^C. + +#+allegro +(setf compiler:generate-interrupt-checks-switch + #'(lambda (safety space speed debug) + (declare (ignore safety space speed debug)) + t)) + + +;;; Note that we expect the binary filename (if supplied) to be +;;; relative to the current directory, not to the source filename. +;;; Lucid and AKCL (and maybe other implementations) merge the :output-file +;;; pathname with the source filename, but the merge by expand-filename +;;; should prevent it from doing anything. + +(define-mumble-function mumble::compile-file (filename &optional binary) + (if *code-quality* (code-quality-hack *code-quality*)) + (setq filename (expand-filename filename)) + (if (string= (mumble::filename-type filename) "") + (setq filename (build-source-filename filename))) + (if binary + (compile-file filename :output-file (expand-filename binary)) + (compile-file filename))) + + +;;; See cl-init.lisp for initialization of *lisp-binary-file-type*. + +(defconstant source-file-type ".scm") +(defconstant binary-file-type *lisp-binary-file-type*) +(define-mumble-import source-file-type) +(define-mumble-import binary-file-type) + + +(defun build-source-filename (filename) + (mumble::assemble-filename filename filename source-file-type)) + +(defun build-binary-filename (filename) + (mumble::assemble-filename filename filename binary-file-type)) + +(proclaim '(ftype (function (simple-string) simple-string) + mumble::filename-place + mumble::filename-name + mumble::filename-type + expand-filename)) + +(proclaim '(ftype (function (simple-string simple-string simple-string) + simple-string) + mumble::assemble-filename)) + +(define-mumble-function mumble::assemble-filename (place name type) + (concatenate 'string + (mumble::filename-place place) + (mumble::filename-name name) + (mumble::filename-type type))) + +(define-mumble-function mumble::filename-place (filename) + (declare (simple-string filename)) + (let ((slash (position #\/ filename :from-end t))) + (if slash + (subseq filename 0 (1+ slash)) + ""))) + +(define-mumble-function mumble::filename-name (filename) + (declare (simple-string filename)) + (let* ((slash (position #\/ filename :from-end t)) + (beg (if slash (1+ slash) 0)) + (dot (position #\. filename :start beg))) + (if (or slash dot) + (subseq filename beg (or dot (length filename))) + filename))) + +(define-mumble-function mumble::filename-type (filename) + (declare (simple-string filename)) + (let* ((slash (position #\/ filename :from-end t)) + (beg (if slash (1+ slash) 0)) + (dot (position #\. filename :start beg))) + (if dot + (subseq filename dot (length filename)) + ""))) + + +;;; This function is called by all functions that pass filenames down +;;; to the operating system. It does environment variable substitution +;;; and merging with *default-pathname-defaults* (set by the cd function). +;;; Since this function translates mumble's notion of pathnames into +;;; a lower-level representation, this function should never need to +;;; be called outside of this file. + +(defun expand-filename (filename) + (declare (simple-string filename)) + (namestring + (merge-pathnames + (fix-filename-syntax + (if (eql (schar filename 0) #\$) + (let* ((end (length filename)) + (slash (or (position #\/ filename) end)) + (new (mumble::getenv (subseq filename 1 slash)))) + (if new + (concatenate 'string new (subseq filename slash end)) + filename)) + filename) + )))) + + +;;; On non-unix machines, may need to change the mumble unix-like filename +;;; syntax to whatever the normal syntax used by the implementation is. + +#+mcl +(defun fix-filename-syntax (filename) + (substitute #\: #\/ filename)) + +#-mcl +(defun fix-filename-syntax (filename) + filename) + + +;;; AKCL's compile-file merges the output pathname against the input +;;; pathname. If the output pathname doesn't have an explicit directory +;;; but the input pathname does, the wrong thing will happen. This +;;; hack is so that expand-filename will always put a directory +;;; specification on both pathnames. +;;; Lucid CL does similar merging, but *default-pathname-defaults* +;;; already defaults to the truename of the current directory. + +#+akcl +(setf *default-pathname-defaults* (truename "./")) + + +;;; WCL's *default-pathname-defaults* is OK except that it has a +;;; type of .lisp, which is inappropriate. + +#+wcl +(setf *default-pathname-defaults* + (make-pathname :directory + (pathname-directory *default-pathname-defaults*))) + +#+(or mcl lispworks) +(setf *default-pathname-defaults* + (truename *default-pathname-defaults*)) + + +(define-mumble-function mumble::file-exists? (filename) + (probe-file (expand-filename filename))) + +(define-mumble-function mumble::file-write-date (filename) + (file-write-date (expand-filename filename))) + +(define-mumble-synonym mumble::current-date get-universal-time) + +(define-mumble-function mumble::get-run-time () + (/ (get-internal-run-time) (float internal-time-units-per-second))) + + +;;; Get environment variables + +#+lucid +(progn + (mumble::predefine (mumble::getenv string)) + (define-mumble-synonym mumble::getenv lcl:environment-variable)) + +#+cmu +(define-mumble-function mumble::getenv (string) + (let ((symbol (intern string (find-package "KEYWORD")))) + (cdr (assoc symbol extensions:*environment-list*)))) + +#+(or akcl allegro lispworks) +(define-mumble-function mumble::getenv (string) + (system::getenv string)) + +#+wcl +(define-mumble-function mumble::getenv (string) + (lisp:getenv string)) + + +;;; Hmmm. The Mac doesn't have environment variables, so we'll have to +;;; roll our own. + +#+mcl +(progn + (defvar *environment-alist* '()) + (define-mumble-function mumble::getenv (string) + (cdr (assoc string *environment-alist* :test #'string=))) + ) + + +#-(or lucid allegro cmu akcl mcl lispworks wcl) +(missing-mumble-definition mumble::getenv) + + +;;; Change working directory. +;;; This stores a directory pathname in *default-pathname-defaults*. +;;; See also expand-filename. + +(define-mumble-function mumble::cd (filename) + (if (not (eql (schar filename (1- (length filename))) #\/)) + (setq filename (concatenate 'string filename "/"))) + (setq *default-pathname-defaults* (pathname (expand-filename filename)))) + + +;;; Leave Lisp + +#+lucid +(define-mumble-synonym mumble::exit lcl:quit) + +#+allegro +(define-mumble-synonym mumble::exit excl:exit) + +#+cmu +(define-mumble-synonym mumble::exit extensions:quit) + +#+akcl +(define-mumble-synonym mumble::exit lisp:bye) + +#+mcl +(define-mumble-synonym mumble::exit ccl:quit) + +#+lispworks +(define-mumble-synonym mumble::exit lw:bye) + +#+wcl +(define-mumble-synonym mumble::exit lisp:quit) + + +#-(or lucid allegro cmu akcl mcl lispworks wcl) +(missing-mumble-definition mumble::exit) + + + +;;;===================================================================== +;;; Reader support +;;;===================================================================== + + +;;; Make the default readtable recognize #f and #t. +;;; CMUCL's loader rebinds *readtable* when loading file, so can't +;;; setq it here; hack the default readtable instead. + +#+(or cmu mcl allegro lispworks) +(defparameter *mumble-readtable* *readtable*) + +#+(or lucid akcl wcl) +(progn + (defparameter *mumble-readtable* (copy-readtable nil)) + (setq *readtable* *mumble-readtable*) + ) + +#-(or lucid allegro cmu akcl mcl lispworks wcl) +(missing-mumble-definition *mumble-readtable*) + + +;;; Lucid's debugger uses the standard readtable rather than *readtable* +;;; unless you do this magic trick. + +#+lucid +(sys:add-debugger-binding '*readtable* *mumble-readtable*) + + + +(set-dispatch-macro-character #\# #\f + #'(lambda (stream subchar arg) + (declare (ignore stream subchar arg)) + nil)) + +(set-dispatch-macro-character #\# #\t + #'(lambda (stream subchar arg) + (declare (ignore stream subchar arg)) + t)) + + + +;;;===================================================================== +;;; Random stuff +;;;===================================================================== + +(defconstant mumble::lisp-implementation-name *lisp-implementation-name*) +(define-mumble-import mumble::lisp-implementation-name) + +(define-mumble-function mumble::identify-system () + (format nil "~a version ~a on ~a" + (or (lisp-implementation-type) + "Generic Common Lisp") + (or (lisp-implementation-version) + "Generic") + (or (machine-type) + "Generic Machine"))) + +(defconstant mumble::left-to-right-evaluation t) +(define-mumble-import mumble::left-to-right-evaluation) + + +#+excl +(define-mumble-function mumble::gc-messages (onoff) + (setf (sys:gsgc-switch :print) onoff)) +#+cmu +(define-mumble-function mumble::gc-messages (onoff) + (setf extensions:*gc-verbose* onoff)) +#+(or lispworks akcl wcl mcl) +(define-mumble-function mumble::gc-messages (onoff) + onoff) ; can't figure out if they have a hook or not +#+lucid +(define-mumble-function mumble::gc-messages (onoff) + (setf lcl:*gc-silence* (not onoff)) + onoff) + + +#-(or lucid cmu allegro akcl mcl lispworks wcl) +(missing-mumble-definition mumble::gc-messages) + + +(define-mumble-import identity) diff --git a/cl-support/cl-init.lisp b/cl-support/cl-init.lisp new file mode 100644 index 0000000..4d78cde --- /dev/null +++ b/cl-support/cl-init.lisp @@ -0,0 +1,170 @@ +;;; cl-init.lisp -- initialize Common Lisp, loading cl-specific files. +;;; +;;; author : Sandra Loosemore +;;; date : 23 Oct 1991 +;;; +;;; All of the files loaded here are assumed to be regular Common Lisp +;;; files. + +(in-package "MUMBLE-IMPLEMENTATION") + + +;;; Turn off bogus warnings and messages!!! + +;;; Lucid complains if files don't start with IN-PACKAGE. +#+lucid +(setq lcl:*warn-if-no-in-package* '()) + + +;;; CMU CL prints too many compiler progress messages. +#+cmu +(progn + (setq *compile-print* '()) + (setq *load-verbose* t) + ) + + +;;; AKCL complains if any package operations appear at top-level +;;; after any other code. +;;; Also prints useless notes about when it does tail recursion elimination. +#+akcl +(progn + (setq compiler:*suppress-compiler-notes* t) + (setq compiler:*compile-verbose* t) + (setq *load-verbose* t) + (setq compiler::*compile-ordinaries* t) + (si:putprop 'make-package nil 'compiler::package-operation) + (si:putprop 'shadow nil 'compiler::package-operation) + (si:putprop 'shadowing-import nil 'compiler::package-operation) + (si:putprop 'export nil 'compiler::package-operation) + (si:putprop 'unexport nil 'compiler::package-operation) + (si:putprop 'use-package nil 'compiler::package-operation) + (si:putprop 'unuse-package nil 'compiler::package-operation) + (si:putprop 'import nil 'compiler::package-operation) + (si:putprop 'provide nil 'compiler::package-operation) + (si:putprop 'require nil 'compiler::package-operation) + ) + + +;;; Allegro also issues too many messages. +;;; ***We really ought to rename the defstructs that give the package +;;; locked errors.... + +#+allegro +(progn + (setf *compile-print* nil) + (setf compiler:*cltl1-compile-file-toplevel-compatibility-p* nil) + (setq excl:*enable-package-locked-errors* nil) + (setf excl:*load-source-file-info* nil) + (setf excl:*record-source-file-info* nil) + (setf excl:*load-xref-info* nil) + (setf excl:*record-source-file-info* nil) + ) + + +;;; Harlequin Lispworks prints too many messages too. + +#+lispworks +(progn + (setf *compile-print* nil) + (setf *load-print* nil) + (lw:toggle-source-debugging nil) + ) + + +;;; Load up definitions + +(defvar *lisp-source-file-type* ".lisp") +(defvar *lisp-binary-file-type* + #+lucid + (namestring (make-pathname :type (car lcl:*load-binary-pathname-types*))) + #+allegro + (concatenate 'string "." excl:*fasl-default-type*) + #+cmu + (concatenate 'string "." (c:backend-fasl-file-type c:*backend*)) + #+akcl + ".o" + #+mcl + ".fasl" + #+lispworks + ".wfasl" + #+wcl + ".o" + #-(or lucid allegro cmu akcl mcl lispworks wcl) + (error "Don't know how to initialize *LISP-BINARY-FILE-TYPE*.") + ) + +(defvar *lisp-implementation-name* + #+lucid "lucid" + #+(and allegro next) "allegro-next" + #+(and allegro (not next)) "allegro" + #+cmu "cmu" + #+akcl "akcl" + #+mcl "mcl" + #+lispworks "lispworks" + #+wcl "wcl" + #-(or lucid allegro cmu akcl mcl lispworks wcl) + (error "Don't know how to initialize *LISP-IMPLEMENTATION-NAME*.") + ) + + + + +;;; Note that this assumes that the current directory is $Y2. +;;; Environment variables in pathnames may not be supported by the +;;; host Lisp. + +#-mcl (progn + (defvar *support-directory* "cl-support/") + (defvar *support-binary-directory* + (concatenate 'string + *support-directory* + *lisp-implementation-name* + "/"))) + +(defun load-compiled-cl-file (filename) + (let ((source-file (concatenate 'string + *support-directory* + filename + *lisp-source-file-type*)) + (binary-file (concatenate 'string + *support-binary-directory* + filename + *lisp-binary-file-type*))) + (if (or (not (probe-file binary-file)) + (< (file-write-date binary-file) (file-write-date source-file))) + (compile-file source-file :output-file (merge-pathnames binary-file))) + (load binary-file))) + + +;;; Do NOT change the load order of these files. + +(load-compiled-cl-file "cl-setup") +(load-compiled-cl-file "cl-support") +(load-compiled-cl-file "cl-definitions") +(load-compiled-cl-file "cl-types") +(load-compiled-cl-file "cl-structs") + + +;;; It would be nice if at this point we could switch *package* +;;; over to the right package. But because *package* is rebound while +;;; this file is being loaded, it will get set back to whatever it was +;;; anyway. Bummer. Well, let's at least make the package that we want +;;; to use. + +(make-package "MUMBLE-USER" :use '("MUMBLE")) + + +;;; Compile and load the rest of the system. (The Lucid compiler is fast +;;; enough to make it practical to compile things all the time.) + +(eval-when (eval compile load) + (setf *package* (find-package "MUMBLE-USER"))) + +(load "$Y2/support/system") +(compile-haskell) + + +;;; All done + +(write-line "Remember to do (in-package \"MUMBLE-USER\")!") diff --git a/cl-support/cl-setup.lisp b/cl-support/cl-setup.lisp new file mode 100644 index 0000000..361963c --- /dev/null +++ b/cl-support/cl-setup.lisp @@ -0,0 +1,30 @@ +;;; cl-setup.lisp -- set up mumble environment in Common Lisp +;;; +;;; author : Sandra Loosemore +;;; date : 10 Oct 1991 +;;; +;;; This file must be loaded before either compiling or loading +;;; the cl-definitions file. + + +;;; The mumble package exports only those symbols that have definitions +;;; in mumble. Many of these symbols shadow built-in CL definitions. +;;; Programs that use mumble should use the mumble package in place of +;;; (rather than in addition to) the CL package. + +(unless (find-package "MUMBLE") + (make-package "MUMBLE" :use nil)) + + +;;; The actual implementation of the mumble compatibility library happens +;;; in the MUMBLE-IMPLEMENTATION package. We'll explicitly package-qualify +;;; all symbols from the MUMBLE package that it references, and rely +;;; on the definitional macros to arrange to export them from the MUMBLE +;;; package. + +(unless (find-package "MUMBLE-IMPLEMENTATION") + (make-package "MUMBLE-IMPLEMENTATION" :use '("LISP"))) + + + + diff --git a/cl-support/cl-structs.lisp b/cl-support/cl-structs.lisp new file mode 100644 index 0000000..0d57693 --- /dev/null +++ b/cl-support/cl-structs.lisp @@ -0,0 +1,699 @@ +;;; cl-structs.lisp -- extended structure definitions +;;; +;;; author : Sandra Loosemore +;;; date : 19 Aug 1992 +;;; + + +;;;==================================================================== +;;; Basic structure types +;;;==================================================================== + + +;;; Use this hash table for mapping names -> type descriptors + +(defvar *struct-lookup-table* (make-hash-table :test #'eq)) + +(defmacro lookup-type (name) + `(gethash ,name *struct-lookup-table*)) + + +;;; Do NOT add or remove slots from these DEFSTRUCTS without also +;;; changing the bootstrap code below!!! +;;; Do NOT try to give these structs complicated defaulting behavior!!! + +;;; All of our objects are subtypes of STRUCT. + + +(mumble::predefine (mumble::write object . maybe-stream)) + +(defun print-struct-object (object stream depth) + (declare (ignore depth)) + (mumble::write object stream) +; (format stream "#" (td-name (struct-type-descriptor object))) + ) + + +;;; Note that non-exported slots are prefixed with % to prevent +;;; accidental slot name collisions. + +(defstruct (struct + (:print-function print-struct-object) + (:predicate struct?) + (:constructor nil) ; never instantiated directly + (:copier nil)) + (type-descriptor nil :type t) + (%bits 0 :type fixnum) + ) + + +(defstruct (type-descriptor + (:include struct + (type-descriptor (lookup-type 'type-descriptor))) + (:conc-name td-) + (:constructor create-type-descriptor ()) + (:predicate nil) + (:copier nil)) + (name nil :type symbol) + (slots nil :type list) ; all slots, including inherited + (parent-type nil :type t) + (printer nil :type t) + (%local-slots nil :type list) ; "real" structure slots + (%bits-used 0 :type fixnum) + (%constructor nil :type symbol) + ) + +(defstruct (slot-descriptor + (:include struct + (type-descriptor (lookup-type 'slot-descriptor))) + (:conc-name sd-) + (:constructor create-slot-descriptor ()) + (:predicate nil) + (:copier nil)) + (name nil :type symbol) + (type nil :type t) + (default nil :type t) + (getter nil :type symbol) + (%bit nil :type (mumble::maybe fixnum)) + (%read-only? nil :type mumble::bool) + (%required? nil :type mumble::bool) + (%uninitialized? nil :type mumble::bool)) + + +;;; Helper function for bootstrapping. + +(defun create-slot-simple (prefix name type default + &optional read-only? required? uninitialized?) + (let ((sd (create-slot-descriptor))) + (setf (sd-name sd) name) + (setf (sd-type sd) type) + (setf (sd-default sd) default) + (setf (sd-getter sd) (symbol-append prefix name)) + (setf (sd-%read-only? sd) read-only?) + (setf (sd-%required? sd) required?) + (setf (sd-%uninitialized? sd) uninitialized?) + sd)) + + +;;; Initialize descriptors for the predefined struct types. + +(let ((struct-td (setf (lookup-type 'struct) + (create-type-descriptor))) + (type-td (setf (lookup-type 'type-descriptor) + (create-type-descriptor))) + (slot-td (setf (lookup-type 'slot-descriptor) + (create-type-descriptor)))) + ;; struct + (setf (td-type-descriptor struct-td) type-td) + (setf (td-name struct-td) 'struct) + (setf (td-%bits-used struct-td) 0) + ;; type-descriptor + (setf (td-type-descriptor type-td) type-td) + (setf (td-name type-td) 'type-descriptor) + (setf (td-%local-slots type-td) + (list (create-slot-simple 'td- 'name 'symbol nil) + (create-slot-simple 'td- 'slots 'list nil) + (create-slot-simple 'td- 'parent-type 't nil) + (create-slot-simple 'td- 'printer 't nil) + (create-slot-simple 'td- '%local-slots 'list nil) + (create-slot-simple 'td- '%bits-used 'fixnum 0) + (create-slot-simple 'td- '%constructor 'symbol nil) + )) + (setf (td-slots type-td) (td-%local-slots type-td)) + (setf (td-%bits-used type-td) 0) + (setf (td-%constructor type-td) 'create-type-descriptor) + (setf (td-parent-type type-td) struct-td) + ;; slot-descriptor + (setf (td-type-descriptor slot-td) type-td) + (setf (td-name slot-td) 'slot-descriptor) + (setf (td-%local-slots slot-td) + (list (create-slot-simple 'sd- 'name 'symbol nil) + (create-slot-simple 'sd- 'type 't nil) + (create-slot-simple 'sd- 'default 't nil) + (create-slot-simple 'sd- 'getter 'symbol nil) + (create-slot-simple 'sd- '%bit '(mumble::maybe fixnum) nil) + (create-slot-simple 'sd- '%read-only? 'mumble::bool nil) + (create-slot-simple 'sd- '%required? 'mumble::bool nil) + (create-slot-simple 'sd- '%uninitialized? 'mumble::bool nil) + )) + (setf (td-slots slot-td) (td-%local-slots slot-td)) + (setf (td-%bits-used slot-td) 0) + (setf (td-%constructor slot-td) 'create-slot-descriptor) + (setf (td-parent-type type-td) struct-td) + ) + + + +;;;===================================================================== +;;; Support for bit slots +;;;===================================================================== + +(eval-when (eval compile load) + (defconstant max-bits (integer-length most-positive-fixnum))) + +(defvar *bit-slot-getters* (make-array max-bits)) +(defvar *bit-slot-setters* (make-array max-bits)) + +(defmacro bit-slot-getter (i) `(svref *bit-slot-getters* ,i)) +(defmacro bit-slot-setter (i) `(svref *bit-slot-setters* ,i)) + +(defmacro define-bit-accessors () + (let ((results nil)) + (dotimes (i max-bits) + (let ((getter (intern (format nil "GET-BIT-~a" i))) + (setter (intern (format nil "SET-BIT-~a" i))) + (mask (ash 1 i))) + (push + `(progn + (mumble::define-integrable (,getter x) + (not (eql (the fixnum + (logand (the fixnum (struct-%bits x)) + (the fixnum ,mask))) + 0))) + (mumble::define-integrable (,setter v x) + (setf (struct-%bits x) + (if v + (the fixnum + (logior (the fixnum (struct-%bits x)) + (the fixnum ,mask))) + (the fixnum + (logandc2 (the fixnum (struct-%bits x)) + (the fixnum ,mask))))) + v) + (setf (bit-slot-getter ,i) ',getter) + (setf (bit-slot-setter ,i) ',setter)) + results))) + `(progn ,@results))) + +(define-bit-accessors) + + + + +;;;===================================================================== +;;; Random helper functions +;;;===================================================================== + +(defun quoted? (x) + (and (consp x) (eq (car x) 'quote))) + +(defun quoted-value (x) + (cadr x)) + +(defun unknown-type-error (type) + (error "Struct type ~s has not been defined." type)) + +(defun unknown-slot-error (type slot) + (error "Struct type ~s has no slot named ~s." type slot)) + +(defun lookup-type-descriptor (type) + (or (lookup-type type) + (unknown-type-error type))) + +(defun lookup-slot-descriptor (type slot) + (let ((td (lookup-type-descriptor type))) + (or (find slot (td-slots td) :key #'sd-name) + (unknown-slot-error type slot)))) + +(defun slot-getter-name (type slot) + (sd-getter (lookup-slot-descriptor type slot))) + +(defun sd-getter-function (sd) + (symbol-function (sd-getter sd))) + + + +;;;===================================================================== +;;; Struct-slot macro +;;;===================================================================== + +;;; Note that this can be SETF'ed only if type and slot are quoted. + +(defmacro struct-slot (type slot object) + (if (and (quoted? type) (quoted? slot)) + (struct-slot-compiletime (quoted-value type) (quoted-value slot) object) + (progn + (warn "Type and/or slot argument to STRUCT-SLOT not constant.") + `(struct-slot-runtime ,type ,slot ,object)))) + +(defun struct-slot-compiletime (type slot object) + (let ((sd (lookup-slot-descriptor type slot))) + `(the ,(sd-type sd) (,(sd-getter sd) (the ,type ,object))))) + +(defun struct-slot-runtime (type slot object) + (let ((sd (lookup-slot-descriptor type slot))) + ;; *** Could insert explicit type checks here. + (funcall (sd-getter-function sd) object))) + + +;;;===================================================================== +;;; Make macro and support +;;;===================================================================== + +(defmacro make (type . inits) + (make-aux type inits)) + +;;; Turn the call to MAKE into a call to the boa constructor. +;;; The arguments to the BOA constructor are those slots that have +;;; the required? flag set to true. If initializers for other slots +;;; are provided, turn these into SETFs. Bit attributes are always +;;; handled via SETF. + +(defun make-aux (type inits) + (let* ((td (lookup-type-descriptor type)) + (boa (td-%constructor td)) + (slots (td-slots td)) + (tempvar (gensym)) + (setfs '()) + (bits-inits '()) + (slot-inits '())) + (check-slot-inits type inits) + (dolist (s slots) + (let* ((name (sd-name s)) + (supplied? (mumble::assq name inits)) + (required? (sd-%required? s)) + (uninitialized? (sd-%uninitialized? s)) + (init (if supplied? + (progn + ;; *** Maybe want to suppress this warning. + ;;(when (not required?) + ;; (override-slot-init-warning type name)) + (cadr supplied?)) + (progn + ;; *** Maybe want to suppress this warning. + (when (and required? (not uninitialized?)) + (missing-slot-init-warning type name)) + (sd-default s))))) + (cond ((sd-%bit s) + (cond ((or (eq init 'nil) (equal init '(quote nil))) + ;; do nothing, bit already defaults to 0 + ) + ((and uninitialized? (not supplied?) required?) + ;; no default or init supplied, leave uninitialized + ) + ((constantp init) + ;; it must be a non-false constant, set bit to 1 + (push (ash 1 (sd-%bit s)) bits-inits)) + (t + ;; have to do runtime test + (push `(the fixnum (if ,init ,(ash 1 (sd-%bit s)) 0)) + bits-inits)))) + ((and required? (not uninitialized?)) + ;; The constructor takes the value as a positional argument. + (push init slot-inits)) + (supplied? + ;; Make a setf. + ;; No point in putting the same value in twice. + (unless (and (constantp init) (equal init (sd-default s))) + (push `(setf (,(sd-getter s) ,tempvar) ,init) setfs))) + (t nil)))) + (unless (null bits-inits) + (push `(setf (struct-%bits ,tempvar) + ,(cond ((null (cdr bits-inits)) + (car bits-inits)) + ((every #'constantp bits-inits) + (apply #'logior bits-inits)) + (t + `(the fixnum (logior ,@(nreverse bits-inits)))))) + setfs)) + (if (null setfs) + `(,boa ,@(nreverse slot-inits)) + `(let ((,tempvar (,boa ,@(nreverse slot-inits)))) + ,@(nreverse setfs) + ,tempvar)))) + +(defun override-slot-init-warning (type name) + (warn "Overriding default for slot ~s in MAKE ~s." + name type)) + +(defun missing-slot-init-warning (type name) + (warn "No initializer or default for slot ~s in MAKE ~s." + name type)) + +(defun check-slot-inits (type inits) + (dolist (i inits) + (lookup-slot-descriptor type (car i)))) + + + +;;;==================================================================== +;;; Update-slots macro +;;;==================================================================== + +;;; Note that type is a literal here. +;;; *** Could be smarter about merging setters for bit slots. + +(defmacro update-slots (type exp . inits) + (let ((temp (gensym))) + `(let ((,temp ,exp)) + ,@(mapcar #'(lambda (i) + `(setf (struct-slot ',type ',(car i) ,temp) ,(cadr i))) + inits)))) + + + +;;;==================================================================== +;;; With-slots macro +;;;==================================================================== + +;;; Note that type is a literal here. +;;; ***Could be smarter about merging accesses for bit slots. + +(defmacro mumble::with-slots (type slots exp . body) + (let ((temp (gensym))) + `(let* ((,temp ,exp) + ,@(mapcar #'(lambda (s) + `(,s (struct-slot ',type ',s ,temp))) + slots)) + ,@body))) + + +;;;==================================================================== +;;; Define-struct macro +;;;==================================================================== + + +;;; The rather strange division here is so that the call to MAKE +;;; works right. +;;; All INSTALL-STRUCT-TYPE does is fill in and install the type +;;; descriptor object. + +(defmacro define-struct (name . fields) + (multiple-value-bind (include type-template slots prefix predicate) + (parse-struct-fields name fields) + `(progn + (eval-when (eval compile load) + (install-struct-type + ',name + ',include + ',prefix + (make ,type-template) + ',slots)) + (define-struct-aux ,name ,include ,prefix ,predicate)))) + + +;;; This is the macro that actually creates the DEFSTRUCT expansion. + +(defmacro define-struct-aux (name include prefix predicate) + (let* ((td (lookup-type name)) + (slots (td-slots td)) + (local-slots (td-%local-slots td)) + (bit-slots (remove-if-not #'sd-%bit slots))) + `(progn + ;; Make the struct definition. + ;; *** could put the type descriptor for the default in a + ;; *** global variable; it might speed up reference. + (defstruct (,name + (:include ,include + (type-descriptor (lookup-type ',name))) + (:conc-name ,prefix) + ;; Disable the default keyword constructor. + ;; If you do this in AKCL, it will complain about + ;; the BOA constructor. Bogus!!! + ;; If you do this in WCL, it will just quietly ignore + ;; the BOA. + #-(or akcl wcl) (:constructor nil) + (:constructor ,(td-%constructor td) ,(make-boa-args slots)) + (:predicate ,predicate) + (:copier nil)) + ,@(mapcar + #'(lambda (s) + `(,(sd-name s) ,(sd-default s) + ;; CMU common lisp initializes &aux boa constructor + ;; slots to NIL instead of leaving them uninitialized, + ;; and then complains if this doesn't match the declared + ;; slot type. I think this is a bug, not a feature, but + ;; here's a workaround for it. + :type + #+cmu ,(if (sd-%uninitialized? s) + `(or ,(sd-type s) null) + (sd-type s)) + #-cmu ,(sd-type s) + ;; Can make slots read-only only if a setf-er is not + ;; required by MAKE. + :read-only ,(and (sd-%read-only? s) (sd-%required? s)))) + local-slots)) + ;; Make accessor functions for bit slots. + ,@(mapcar + #'(lambda (s) + (let ((place (symbol-append prefix (sd-name s))) + (getter (bit-slot-getter (sd-%bit s))) + (setter (bit-slot-setter (sd-%bit s)))) + `(progn + (mumble::define-integrable (,place x) (,getter x)) + ,@(unless (sd-%read-only? s) + `((mumble::define-setf ,place ,setter)))) + )) + bit-slots) + ',name) + )) + + + +;;; Determine which arguments to make explicit to the boa constructor. +;;; Basically, expect an explicit initializer for any slot that does not +;;; have a default supplied. +;;; Supplying slot names as &aux parameters to a boa constructor is +;;; supposed to suppress initialization. + +(defun make-boa-args (slots) + (let ((required-args '()) + (uninitialized-args '())) + (dolist (s slots) + (when (and (sd-%required? s) (not (sd-%bit s))) + (if (sd-%uninitialized? s) + (push (sd-name s) uninitialized-args) + (push (sd-name s) required-args)))) + ;; Gag. AKCL does the wrong thing with &AUX arguments; defstruct sticks + ;; another &AUX at the end of the lambda list. Looks like it will do + ;; the right thing if you just omit the uninitialized arguments from + ;; the boa arglist entirely. + #+akcl (nreverse required-args) + #-akcl + (if (null uninitialized-args) + (nreverse required-args) + `(,@(nreverse required-args) &aux ,@(nreverse uninitialized-args))) + )) + + +;;; Install the type descriptor, filling in all the slots. + +(defun install-struct-type (name include prefix td slots) + (let* ((parent-type (lookup-type-descriptor include)) + (bits-used (td-%bits-used parent-type)) + (local-slots '()) + (all-slots '())) + (dolist (s slots) + (multiple-value-bind + (slot-name type default bit read-only? required? uninitialized?) + (parse-slot-fields name s) + (let ((sd (create-slot-simple + prefix slot-name type default + read-only? required? uninitialized?))) + (push sd all-slots) + (cond (bit + (if (eql bits-used max-bits) + (error "Too many bit slots in DEFINE-STRUCT ~s." name)) + (setf (sd-%bit sd) bits-used) + (incf bits-used)) + (t + (push sd local-slots)))))) + (setf local-slots (nreverse local-slots)) + (setf (td-name td) name) + (setf (td-slots td) (append (td-slots parent-type) (nreverse all-slots))) + (setf (td-%local-slots td) local-slots) + (setf (td-%bits-used td) bits-used) + (setf (td-%constructor td) (symbol-append '%create- name)) + (setf (td-parent-type td) parent-type) + (setf (lookup-type name) td))) + + +;;; Struct field parsing. + +(defun parse-struct-fields (name fields) + (when (not (symbolp name)) + (error "Structure name ~s is not a symbol." name)) + (let ((include nil) + (type-template nil) + (slots nil) + (prefix nil) + (predicate nil)) + (dolist (f fields) + (cond ((not (consp f)) + (unknown-field-error f name)) + ((eq (car f) 'include) + (if include + (duplicate-field-error 'include name) + (setf include (cadr f)))) + ((eq (car f) 'type-template) + (if type-template + (duplicate-field-error 'type-template name) + (setf type-template (cadr f)))) + ((eq (car f) 'slots) + (if slots + (duplicate-field-error 'slots name) + (setf slots (cdr f)))) + ((eq (car f) 'prefix) + (if prefix + (duplicate-field-error 'prefix name) + (setf prefix (cadr f)))) + ((eq (car f) 'predicate) + (if predicate + (duplicate-field-error 'predicate name) + (setf predicate (cadr f)))) + (t + (unknown-field-error f name)))) + (values + (or include 'struct) + (or type-template + (and include + (td-name (td-type-descriptor (lookup-type-descriptor include)))) + 'type-descriptor) + (or slots '()) + (or prefix (symbol-append name '-)) + predicate))) + +(defun unknown-field-error (f name) + (error "Unknown field ~s in DEFINE-STRUCT ~s." f name)) + +(defun duplicate-field-error (f name) + (error "Field ~s appears more than once in DEFINE-STRUCT ~s." f name)) + + + +;;; Parsing for slot specifications. + +(defun parse-slot-fields (struct-name slot) + (let ((name nil) + (type t) + (default '*default-slot-default*) + (bit nil) + (read-only? nil) + (required? t) + (uninitialized? nil)) + (if (or (not (consp slot)) + (not (symbolp (setf name (car slot))))) + (invalid-slot-error slot struct-name)) + (dolist (junk (cdr slot)) + (cond ((eq (car junk) 'type) + (setf type (cadr junk))) + ((eq (car junk) 'default) + (setf default (cadr junk)) + (setf required? nil)) + ((eq (car junk) 'bit) + (setf bit (cadr junk))) + ((eq (car junk) 'read-only?) + (setf read-only? (cadr junk))) + ((eq (car junk) 'uninitialized?) + (setf uninitialized? (cadr junk))) + (t + (invalid-slot-error slot struct-name)))) + (values + name + type + default + bit + read-only? + required? + uninitialized? + ))) + +;;; Some implementations of DEFSTRUCT complain if the default value +;;; for a slot doesn't match the declared type of that slot, even if +;;; the default is never used. +;;; Using this variable as the default init form for such slots should +;;; suppress such warnings. + +(defvar *default-slot-default* nil) + +(defun invalid-slot-error (slot struct-name) + (error "Invalid slot syntax ~s in DEFINE-STRUCT ~s." slot struct-name)) + + + +;;;===================================================================== +;;; Printer hooks +;;;===================================================================== + +;;; Here is the macro for associating a printer with a structure type. + +(defmacro define-struct-printer (type function) + `(define-struct-printer-aux ',type (function ,function))) + +(defun define-struct-printer-aux (type function) + (let ((td (lookup-type-descriptor type))) + (setf (td-printer td) function) + type)) + + +;;;===================================================================== +;;; Imports +;;;===================================================================== + + +;;; Generic stuff + +(define-mumble-import struct) +(define-mumble-import struct?) +(define-mumble-import struct-type-descriptor) + + +;;; Predefined types, slots, and accessors +;;; Note: not all slots are exported. + +(define-mumble-import type-descriptor) +(define-mumble-import name) +(define-mumble-import slots) +(define-mumble-import parent-type) +(define-mumble-import printer) +(define-mumble-import td-name) +(define-mumble-import td-slots) +(define-mumble-import td-parent-type) +(define-mumble-import td-printer) + +(define-mumble-import slot-descriptor) +(define-mumble-import name) +(define-mumble-import type) +(define-mumble-import default) +(define-mumble-import getter) +(define-mumble-import sd-name) +(define-mumble-import sd-type) +(define-mumble-import sd-default) +(define-mumble-import sd-getter) + + +;;; Utility functions + +(define-mumble-import lookup-type-descriptor) +(define-mumble-import lookup-slot-descriptor) +(define-mumble-import sd-getter-function) + + +;;; Macros + +(define-mumble-import make) +(define-mumble-import struct-slot) +(define-mumble-import define-struct) +(define-mumble-import mumble::with-slots) +(define-mumble-import update-slots) +(define-mumble-import define-struct-printer) + + +;;; Field names for define-struct + +(define-mumble-import include) +(define-mumble-import type-template) +(define-mumble-import slots) +(define-mumble-import prefix) +(define-mumble-import predicate) + + +;;; Field names for slot options + +(define-mumble-import type) +(define-mumble-import default) +(define-mumble-import bit) +(define-mumble-import read-only?) +(define-mumble-import uninitialized?) + + diff --git a/cl-support/cl-support.lisp b/cl-support/cl-support.lisp new file mode 100644 index 0000000..4f82ce2 --- /dev/null +++ b/cl-support/cl-support.lisp @@ -0,0 +1,86 @@ +;;; cl-support.lisp -- compile-time support for building mumble +;;; +;;; author : Sandra Loosemore +;;; date : 10 Oct 1991 +;;; +;;; This file must be loaded before compiling the cl-definitions file. +;;; However, it is not needed when loading the compiled file. + +(in-package "MUMBLE-IMPLEMENTATION") + + +;;; Use this macro for defining an exported mumble function. + +(defmacro define-mumble-function (name &rest stuff) + `(progn + (eval-when (eval compile load) (export (list ',name) "MUMBLE")) + (defun ,name ,@stuff))) + + +;;; This is similar, but also does some stuff to try to inline the +;;; function definition. + +(defmacro define-mumble-function-inline (name &rest stuff) + `(progn + (eval-when (eval compile load) (export (list ',name) "MUMBLE")) +#+lcl + (lcl:defsubst ,name ,@stuff) +#-lcl + (progn + (proclaim '(inline ,name)) + (defun ,name ,@stuff)) + ',name)) + + +;;; Use this macro for defining an exported mumble macro. + +(defmacro define-mumble-macro (name &rest stuff) + `(progn + (eval-when (eval compile load) (export (list ',name) "MUMBLE")) + (defmacro ,name ,@stuff))) + + +;;; Use this macro for importing a random symbol into the MUMBLE +;;; package. This is useful for things that can share directly with +;;; built-in Common Lisp definitions. + +(defmacro define-mumble-import (name) + `(progn + (eval-when (eval compile load) (import (list ',name) "MUMBLE")) + (eval-when (eval compile load) (export (list ',name) "MUMBLE")) + ',name)) + + +;;; Use this macro for defining a function in the MUMBLE package that +;;; is a synonym for some Common Lisp function. Try to do some stuff +;;; to make the function compile inline. + +(defmacro define-mumble-synonym (name cl-name) + `(progn + (eval-when (eval compile load) (export (list ',name) "MUMBLE")) + (setf (symbol-function ',name) (symbol-function ',cl-name)) +#+lcl + (lcl:def-compiler-macro ,name (&rest args) + (cons ',cl-name args)) + ',name)) + + + +;;; Use this macro to define a type synonym. + +(defmacro define-mumble-type (name &rest stuff) + `(progn + (eval-when (eval compile load) (export (list ',name) "MUMBLE")) + (deftype ,name ,@stuff))) + + +;;; This macro is used to signal a compile-time error in situations +;;; where an implementation-specific definition is missing. + +(defmacro missing-mumble-definition (name) + (error "No definition has been provided for ~s." name)) + + + + + diff --git a/cl-support/cl-types.lisp b/cl-support/cl-types.lisp new file mode 100644 index 0000000..6fb625e --- /dev/null +++ b/cl-support/cl-types.lisp @@ -0,0 +1,90 @@ +;;; cl-types.lisp -- type-related stuff +;;; +;;; author : Sandra Loosemore +;;; date : 5 Oct 1992 +;;; + + +;;; Export CL symbols for type names + +(define-mumble-import t) + +#+lucid +(define-mumble-type mumble::procedure () 'system::procedure) +#+(or cmu akcl allegro mcl lispworks) +(define-mumble-type mumble::procedure () 'function) +#+wcl +(define-mumble-type mumble::procedure () 'lisp:procedure) +#-(or lucid cmu akcl allegro mcl lispworks wcl) +(missing-mumble-definition procedure) + +(define-mumble-type mumble::pair () 'cons) + +(define-mumble-import null) + +(define-mumble-type mumble::list (&optional element-type) + ;; *** Common Lisp provides no way to make use of the element type + ;; *** without using SATISFIES. + (declare (ignore element-type)) + 'list) + +(define-mumble-import symbol) + +(define-mumble-type mumble::char () 'character) +(define-mumble-type mumble::string () 'simple-string) +(define-mumble-type mumble::vector () 'simple-vector) + +(define-mumble-import number) +(define-mumble-import integer) +(define-mumble-import rational) +(define-mumble-import float) +(define-mumble-import fixnum) + +(define-mumble-type mumble::int () 'fixnum) + +(define-mumble-type mumble::table (&optional key-type value-type) + ;; *** Common Lisp provides no way to make use of the element type + ;; *** without using SATISFIES. + (declare (ignore key-type value-type)) + 'hash-table) + + +;;; Extensions + +(define-mumble-type mumble::enum (&rest values) + `(member ,@values)) + +(define-mumble-type mumble::tuple (&rest element-types) + ;; *** Common Lisp provides no way to make use of the element type + ;; *** without using SATISFIES. + (let ((n (length element-types))) + (cond ((< n 2) + (error "Too few arguments to TUPLE type specifier.")) + ((eql n 2) + 'cons) + (t + 'simple-vector)))) + +(define-mumble-type mumble::bool () 't) + +(define-mumble-type mumble::alist (&optional key-type value-type) + `(mumble::list (tuple ,key-type ,value-type))) + +(define-mumble-type mumble::maybe (type) + `(or ,type null)) + + + +;;; Functions, etc. + +(define-mumble-import the) +(define-mumble-synonym mumble::subtype? subtypep) + +(define-mumble-function-inline mumble::is-type? (type object) + (typep object type)) + +(define-mumble-macro mumble::typecase (data &rest cases) + (let ((last (car (last cases)))) + (if (eq (car last) 'mumble::else) + `(typecase ,data ,@(butlast cases) (t ,@(cdr last))) + `(typecase ,data ,@cases)))) diff --git a/cl-support/wcl-patches.lisp b/cl-support/wcl-patches.lisp new file mode 100644 index 0000000..3e9395c --- /dev/null +++ b/cl-support/wcl-patches.lisp @@ -0,0 +1,68 @@ +(in-package "LISP") + + +;;; The default version of this function has a bug with relative +;;; pathnames. + +(defun pathname->string (p) + (let ((dirlist (pathname-directory p))) + (format nil "~A~{~A/~}~A~A~A" + (case (car dirlist) + (:absolute "/") + (:relative "./") + (:up "../") + (t "")) + (cdr dirlist) + (nil->empty-string (pathname-name p)) + (if (null (pathname-type p)) "" ".") + (nil->empty-string (pathname-type p))))) + + +;;; The default version of this function defaults the C file to the +;;; wrong directory -- LOAD can't find it. + +(defun my-comf (file &key + (output-file (merge-pathnames ".o" file)) + (c-file (merge-pathnames ".c" output-file)) + (verbose *compile-verbose*) + (print *compile-print*) + (config *config*) + (pic? *pic?*) + only-to-c?) + (old-comf file + :output-file output-file + :c-file c-file + :verbose verbose + :print print + :config config + :pic? pic? + :only-to-c? only-to-c?)) + +(when (not (fboundp 'old-comf)) + (setf (symbol-function 'old-comf) #'comf) + (setf (symbol-function 'comf) #'my-comf)) + + +;;; WCL's evaluator tries to macroexpand everything before executing +;;; anything. Unfortunately, this does the wrong thing with +;;; top-level PROGN's -- it tries to expand macros in subforms before +;;; executing earlier subforms that set up stuff required to do the +;;; the expansion properly. + +(defun eval-1 (form venv fenv tenv benv) + (let ((new-form (macroexpand form *eval-macro-env*))) + (if (and (consp new-form) + (eq (car new-form) 'progn)) + (do ((forms (cdr new-form) (cdr forms))) + ((null (cdr forms)) (eval-1 (car forms) venv fenv tenv benv)) + (eval-1 (car forms) venv fenv tenv benv)) + (let ((expansion (expand new-form))) + (when (and (listp expansion) + (eq (car expansion) 'define-function)) + (setf (get (second (second expansion)) + :function-definition) + form)) + (eval/5 expansion venv fenv tenv benv)) + ))) + + diff --git a/com/README b/com/README new file mode 100644 index 0000000..806c62a --- /dev/null +++ b/com/README @@ -0,0 +1,4 @@ +This directory contains various useful command scripts. Scripts +that are specific to a particular host Lisp are found in the appropriate +subdirectories. Scripts for building release distribution are in the +distrib subdirectory. diff --git a/com/akcl/README b/com/akcl/README new file mode 100644 index 0000000..1e997d5 --- /dev/null +++ b/com/akcl/README @@ -0,0 +1,39 @@ +This directory contains command scripts used for building Yale Haskell +from the source distribution under AKCL. We have been using +AKCL version 1.615 on a Sparc, but we don't expect that there would +be difficulties in building with AKCL on other platforms. + +Developers need to source haskell-development instead of haskell-setup +in the .cshrc file. + +To rebuild the system: + +* You need to define environment variables Y2 and AKCL to point to the + appropriate pathnames. See the haskell-development script for details. + +* Make sure that the environment variable PRELUDEBIN (in the + haskell-setup script) points to $PRELUDE/akcl. + +* Execute the "compile" script. This will recompile all of the Lisp + source files that make up the Yale Haskell system. Compiled files are + put in the "akcl" subdirectory of each source directory. + +* Execute the "build-prelude" script to compile the standard prelude. + Note that this process tends to use up a huge amount of memory! + +* Execute the "savesys" script to build a new executable. + +* The new executable is initially called "bin/new-akcl-haskell". Try + it out. If it works, you should rename it to "bin/akcl-haskell". + Make sure that HASKELLPROG (in the haskell-setup script) is correct. + +A word of warning: we have noticed that AKCL is slower by a factor of +three or four than the other Common Lisps we've ported Yale Haskell +to. We don't really support AKCL and we encourage you to buy one of +the commercial Lisp products instead. + +We do not support our Haskell-to-CLX interface under AKCL, either. + + + + diff --git a/com/akcl/build-prelude b/com/akcl/build-prelude new file mode 100755 index 0000000..8f61827 --- /dev/null +++ b/com/akcl/build-prelude @@ -0,0 +1,35 @@ +#!/bin/csh +# +# build-prelude -- recompile the prelude, saving the old one as old-prelude.* +# +# +cd $Y2 +setenv PRELUDEBIN $Y2/progs/prelude/akcl +rm $PRELUDEBIN/old* +if (-e $PRELUDEBIN/Prelude.o) then + foreach i ($PRELUDEBIN/Prelude*.{o,scm}) + mv $i $PRELUDEBIN/old-$i:t + end + endif +$AKCL <lisp-code mods) + (setf (table-entry *modules* *current-mod*) (car mods)) + (clear-extended-modules))) + +(define (eval-fragment eval?) + (cond ((not (cm-available?)) + (say "~&Module ~A is not loaded.~%" *current-mod*) + 'error) + ((memq *fragment-status* '(Compiled Saved)) + (when eval? + (eval-module *extension-module*)) + 'ok) + ((eq? *fragment-status* 'Error) + (say/ne "~&Errors exist in current fragment.~%") + 'error) + ((string=? *current-string* "") + (say/ne "~&Current extension is empty.~%") + 'error) + (else + (let ((res (compile-fragment + *current-mod* *current-string* + *extension-file-name*))) + (cond ((eq? res 'error) + (setf *fragment-status* 'Error) + (notify-error)) + (else + (setf *extension-module* res) + (setf *fragment-status* 'Compiled) + (when eval? + (eval-module *extension-module*)))))))) + +(define (set-current-file file) + (cond ((null? file) + '()) + ((null? (cdr file)) + (setf *remembered-file* (car file))) + (else + (say "~&Invalid file spec ~s.~%" file) + (funcall *abort-command*)))) + +(define (select-current-mod mods) + (when (pair? mods) + (when (not (memq *current-mod* mods)) + (setf *current-mod* (car mods)) + (say/ne "~&Now in module ~A.~%" *current-mod*)))) + +;;; Emacs mode stuff + +;;; *** bogus alert!!! This coercion may fail to produce a +;;; *** real character in some Lisps. + +(define *emacs-notify-char* (integer->char 1)) + +(define (notify-ready) + (when *emacs-mode* + (say/em "~Ar" *emacs-notify-char*) + (force-output (current-output-port)))) + +(define (notify-input-request) + (when *emacs-mode* + (say/em "~Ai" *emacs-notify-char*) + (force-output (current-output-port)))) + +(define (notify-error) + (when *emacs-mode* + (say/em "~Ae" *emacs-notify-char*) + (force-output (current-output-port)))) + +(define (notify-printers printers) + (notify-settings "p" printers)) + +(define (notify-optimizers optimizers) + (notify-settings "o" optimizers)) + +(define (notify-settings flag values) + (when *emacs-mode* + (say/em "~A~A(" *emacs-notify-char* flag) + (dolist (p values) + (say/em " ~A" (string-downcase (symbol->string p)))) + (say/em ")~%") + (force-output (current-output-port)))) + +(define (notify-status-line str) + (when *emacs-mode* + (say/em "~As~A~%" *emacs-notify-char* str) + (force-output (current-output-port)))) + +;;; These are used to drive the real compiler. + +(define *compile/compile-cflags* + (make cflags + (load-code? '#t) + (compile-code? '#t) + (write-code? '#t) + (write-interface? '#t))) + + +(define (compile/compile file) + (haskell-compile file *compile/compile-cflags*)) + + +(define *compile/load-cflags* + (make cflags + (load-code? '#t) + (compile-code? '#f) + (write-code? '#f) + (write-interface? '#f))) + +(define (compile/load file) + (haskell-compile file *compile/load-cflags*)) + + +;;; Printer setting support + +(define (set-printers args mode) + (set-switches *printers* (strings->syms args) + mode *all-printers* "printers")) + +(define (set-optimizers args mode) + (set-switches *optimizers* (strings->syms args) + mode *all-optimizers* "optimizers")) + +(define (set-switches current new mode all name) + (dolist (s new) + (when (and (not (eq? s 'all)) (not (memq s all))) + (signal-invalid-value s name all))) + (let ((res (cond ((eq? mode '+) + (set-union current new)) + ((eq? mode '-) + (set-difference current new)) + ((eq? mode '=) + (if (equal? new '(all)) + all + new))))) + res)) + +(define (signal-invalid-value s name all) + (recoverable-error 'invalid-value + "~A is not one of the valid ~A. Possible values are: ~%~A" + s name all)) + +(define (print-file file) + (call-with-input-file file (function write-all-chars))) + +(define (write-all-chars port) + (let ((line (read-line port))) + (if (eof-object? line) + 'ok + (begin + (write-line line) + (write-all-chars port))))) + +(define (strings->syms l) + (map (lambda (x) + (string->symbol (string-upcase x))) + l)) + diff --git a/command-interface/command.scm b/command-interface/command.scm new file mode 100644 index 0000000..3b98991 --- /dev/null +++ b/command-interface/command.scm @@ -0,0 +1,308 @@ + +;;; Globals used by the command interpreter + +(define *current-string* "") +(define *current-mod* '|Main|) +(define *current-command* '()) +(define *remembered-file* "Foo") +(define *fragment-status* '()) +(define *temp-counter* 0) +(define *last-compiled* "") +(define *abort-command* '()) +(define *command-dispatch* '()) +(define *extension-module* '()) +(define *extension-file-name* "interactive") + +(define (prompt mod) + (format '#f "~A> " mod)) + +(define-local-syntax (define-command name&args helpstr . body) + (let* ((str (car name&args)) + (args (cdr name&args)) + (fname (string->symbol (string-append "CMD-" str)))) + `(begin + (define (,fname arguments) + (verify-command-args ',args arguments ',helpstr) + (apply (lambda ,args ,@body) arguments)) + (setf *command-dispatch* + (nconc *command-dispatch* + (list (cons ',str (function ,fname))))) + ',fname))) + +(define (heval) + (initialize-haskell-system) + (setf *current-string* "") + (setf *fragment-status* 'Building) + (say "~&Yale Haskell ~A~A ~A~%Type :? for help.~%" + *haskell-compiler-version* *haskell-compiler-update* (identify-system)) + (read-commands)) + + +;;; This loop reads commands until a quit + +(define (read-commands) + (do ((cmd-status (read-command) (read-command))) + ((eq? cmd-status 'quit-command-loop) (exit)))) + +;;; This processes a single line of input. + +(define (read-command) + (let/cc abort-command + (setf *abort-command* (lambda () (funcall abort-command 'error))) + (setf *abort-compilation* *abort-command*) + (setf *phase* 'command-interface) + (setf *in-error-handler?* '#f) + (ready-for-input-line) + (let ((ch (peek-char))) + (cond ((eof-object? ch) + 'quit-command-loop) + ((char=? ch '#\:) + (read-char) + (execute-command)) + ((and (char=? ch '#\newline) + (not (eq? *fragment-status* 'Building))) + (read-char) + 'Ignored) + (else + (when (not (eq? *fragment-status* 'Building)) + (setf *fragment-status* 'Building) + (setf *current-string* "")) + (cond ((eqv? ch '#\=) + (read-char) + (append-to-current-string (expand-print-abbr (read-line)))) + ((eqv? ch '#\@) + (read-char) + (append-to-current-string (expand-exec-abbr (read-line)))) + (else + (append-to-current-string (read-line)))) + 'OK) + )))) + +(define (append-to-current-string string) + (setf *current-string* + (string-append *current-string* + string + (string #\newline)))) + + +(define (expand-print-abbr string) + (incf *temp-counter*) + (format '#f "temp_~a = print temp1_~a where temp1_~a = ~a" + *temp-counter* *temp-counter* *temp-counter* string)) + +(define (expand-exec-abbr string) + (incf *temp-counter*) + (format '#f "temp_~a :: Dialogue~%temp_~a = ~a" + *temp-counter* *temp-counter* string)) + + +(define (ready-for-input-line) + (when (not *emacs-mode*) + (fresh-line (current-output-port)) + (write-string (prompt *current-mod*) (current-output-port)) + (force-output (current-output-port))) + (notify-ready)) + +(define (execute-command) + (if (char=? (peek-char) '#\() ;this is the escape to the lisp evaluator + (let ((form (read))) + (eval form) + 'OK) + (let* ((string (read-line)) + (length (string-length string)) + (cmd+args (parse-command-args string 0 0 length))) + (cond ((null? cmd+args) + (say "~&Eh?~%") + 'OK) + (else + (let ((fn (assoc/test (function string-starts?) + (car cmd+args) + *command-dispatch*))) + (cond ((eq? fn '#f) + (say "~&~A: unknown command. Use :? for help.~%" + (car cmd+args)) + 'OK) + (else + (funcall (cdr fn) (cdr cmd+args)))))))))) + + +;;; This parses the command into a list of substrings. +;;; Args are separated by spaces. + +(define (parse-command-args string start next end) + (declare (type fixnum start next end) + (type string string)) + (cond ((eqv? next end) + (if (eqv? start next) + '() + (list (substring string start next)))) + ((char=? (string-ref string next) '#\space) + (let ((next-next (+ next 1))) + (if (eqv? start next) + (parse-command-args string next-next next-next end) + (cons (substring string start next) + (parse-command-args string next-next next-next end))))) + (else + (parse-command-args string start (+ next 1) end)))) + +(define (verify-command-args template args help) + (cond ((and (null? template) (null? args)) + '#t) + ((symbol? template) + '#t) + ((or (null? template) (null? args)) + (say "~&Command error.~%~A~%" help) + (funcall *abort-command*)) + (else + (verify-command-args (car template) (car args) help) + (verify-command-args (cdr template) (cdr args) help)))) + +(define-command ("?") + ":? Print the help file." + (print-file "$HASKELL/command-interface-help")) + +(define-command ("eval") + ":eval Evaluate current extension." + (eval-fragment '#t) + 'OK) + +(define-command ("save") + ":save Save current extension" + (eval-fragment '#f) + (cond ((eq? *fragment-status* 'Error) + (say/ne "~&Cannot save: errors encountered.~%")) + ((eq? *fragment-status* 'Compiled) + (extend-module *current-mod* *extension-module*) + (setf *fragment-status* 'Saved))) + 'OK) + +(define-command ("quit") + ":quit Quit the Haskell evaluator." + 'quit-command-loop) + +(define-command ("module" mod) + ":module module-name Select module for incremental evaluation." + (setf *current-mod* (string->symbol mod)) + (when (not (cm-available?)) + (say/ne "~&Warning: module ~A is not currently loaded.~%" *current-mod*)) + 'OK) + +(define-command ("run" . file) + ":run Compile, load, and run a file." + (set-current-file file) + (clear-extended-modules) + (let ((mods (compile/load *remembered-file*))) + (when (pair? mods) + (dolist (m mods) + (eval-module (table-entry *modules* m))))) + 'OK) + +(define-command ("compile" . file) + ":compile Compile and load a file." + (set-current-file file) + (clear-extended-modules) + (select-current-mod (compile/compile *remembered-file*)) + 'OK) + +(define-command ("load" . file) + ":load Load a file." + (set-current-file file) + (clear-extended-modules) + (select-current-mod (compile/load *remembered-file*)) + 'OK) + +(define-command ("Main") + ":Main Switch to an empty Main module." + (make-empty-main) + 'OK) + +(define-command ("clear") + ":clear Clear saved definitions from current module." + (remove-extended-modules *current-mod*) + (setf *current-string* "") + (setf *fragment-status* 'Building)) + +(define-command ("list") + ":list List current extension." + (say "~&Current Haskell extension:~%~a" *current-string*) + (cond ((eq? *fragment-status* 'Error) + (say "Extension contains errors.~%")) + ((eq? *fragment-status* 'Compiled) + (say "Extension is compiled and ready.~%"))) + 'OK) + +(define-command ("kill") + ":kill Clear the current fragment." + (when (eq? *fragment-status* 'Building) + (setf *current-string* "")) + 'OK) + +(define-command ("p?") + ":p? Show available printers." + (if *emacs-mode* + (notify-printers (dynamic *printers*)) + (begin + (print-file "$HASKELL/emacs-tools/printer-help.txt") + (say "~&Active printers: ~A~%" (dynamic *printers*))) + )) + +(define-command ("p=" . passes) + ":p= pass1 pass2 ... Set printers." + (setf *printers* (set-printers passes '=)) + (say/ne "~&Setting printers: ~A~%" *printers*)) + +(define-command ("p+" . passes) + ":p+ pass1 pass2 ... Add printers." + (setf *printers* (set-printers passes '+)) + (say/ne "~&Setting printers: ~A~%" *printers*)) + +(define-command ("p-" . passes) + ":p- pass1 pass2 ... Turn off printers." + (setf *printers* (set-printers passes '-)) + (say/ne "~&Setting printers: ~A~%" *printers*)) + + + +(define-command ("o?") + ":o? Show available optimizers." + (if *emacs-mode* + (notify-optimizers (dynamic *optimizers*)) + (begin + (print-file "$HASKELL/emacs-tools/optimizer-help.txt") + (say "~&Active optimizers: ~A~%" (dynamic *optimizers*))) + )) + +(define-command ("o=" . optimizers) + ":o= optimizer1 optimizer2 ... Set optimizers." + (setf *optimizers* (set-optimizers optimizers '=)) + (say/ne "~&Setting optimizers: ~A~%" *optimizers*)) + +(define-command ("o+" . optimizers) + ":o+ optimizer1 optimizer2 ... Add optimizers." + (setf *optimizers* (set-optimizers optimizers '+)) + (say/ne "~&Setting optimizers: ~A~%" *optimizers*)) + +(define-command ("o-" . optimizers) + ":o- optimizer1 optimizer2 ... Turn off optimizers." + (setf *optimizers* (set-optimizers optimizers '-)) + (say/ne "~&Setting optimizers: ~A~%" *optimizers*)) + + +(define-command ("cd" d) + ":cd directory Change the current directory." + (cd d) + 'OK) + +(define-command ("Emacs" mode) + ":Emacs on/off Turn on or off emacs mode." + (cond ((string=? mode "on") + (setf *emacs-mode* '#t)) + ((string=? mode "off") + (setf *emacs-mode* '#f)) + (else + (say "~&Use on or off.~%")))) + +(define-command ("file" name) + ":file name" + (setf *extension-file-name* name) + 'OK) diff --git a/command-interface/incremental-compiler.scm b/command-interface/incremental-compiler.scm new file mode 100644 index 0000000..207b79d --- /dev/null +++ b/command-interface/incremental-compiler.scm @@ -0,0 +1,168 @@ +;;; ================================================================== + +;;; This deals with incremental compilation as used by the command interface. +;;; The basic theory is to create new modules which import the entire +;;; symbol table of an existing module. + + +;;; This adds a new module to the extension environment. This env is an alist +;;; of module names & extended modules. + +(define *extension-env* '()) + +(define (extend-module mod-name new-ast) + (push (tuple mod-name new-ast) *extension-env*)) + +;;; This cleans out extensions for a module. + +(define (remove-extended-modules mod-name) + (setf *extension-env* (rem-ext1 *extension-env* mod-name))) + +(define (rem-ext1 env name) + (cond ((null? env) + '()) + ((eq? (tuple-2-1 (car env)) name) + (rem-ext1 (cdr env) name)) + (else + (cons (car env) (rem-ext1 (cdr env) name))))) + +(define (clear-extended-modules) + (setf *extension-env* '())) + +;;; This retrieves the current extension to a module (if any). + +(define (updated-module name) + (let ((name+mod (assq name *extension-env*))) + (if (not (eq? name+mod '#f)) + (tuple-2-2 name+mod) + (let ((mod-in-table (table-entry *modules* name))) + (cond ((eq? mod-in-table '#f) + (signal-module-not-ready name)) + ((eq? (module-type mod-in-table) 'interface) + (signal-cant-eval-interface name)) + (else mod-in-table)))))) + +(define (signal-module-not-ready name) + (fatal-error 'module-not-ready + "Module ~A is not loaded and ready." + name)) + +(define (signal-cant-eval-interface name) + (fatal-error 'no-evaluation-in-interface + "Module ~A is an interface: evaluation not allowed." + name)) + +(define (compile-fragment module str filename) + (let ((mod-ast (updated-module module))) + (dynamic-let + ((*printers* (if (memq 'extension *printers*) *printers* '())) + (*abort-phase* '#f)) + (mlet (((t-code new-ast) (compile-fragment1 module mod-ast str filename))) + (cond ((eq? t-code 'error) + 'error) + (else + (eval t-code) + new-ast)))))) + +(define (compile-fragment1 mod-name mod-ast str filename) + (let/cc x + (dynamic-let ((*abort-compilation* (lambda () (funcall x 'error '())))) + (let* ((mods (parse-from-string + (format '#f "module ~A where~%~A~%" mod-name str) + (function parse-module-list) + filename)) + (new-mod (car mods))) + (when (not (null? (cdr mods))) + (signal-module-decl-in-extension)) + (when (not (null? (module-imports new-mod))) + (signal-import-decl-in-extension)) + (fragment-initialize new-mod mod-ast) + (values (modules->lisp-code mods) new-mod))))) + +(define (signal-module-decl-in-extension) + (fatal-error 'module-decl-in-extension + "Module declarations are not allowed in extensions.")) + +(define (signal-import-decl-in-extension) + (fatal-error 'import-decl-in-extension + "Import declarations are not allowed in extensions.")) + + +;;; Copy stuff into the fragment module structure from its parent module. +;;; The inverted symbol table is not necessary since the module contains +;;; no imports. + +(define (fragment-initialize new old) + (setf (module-name new) (gensym)) + (setf (module-type new) 'extension) + (setf (module-unit new) (module-unit old)) + (setf (module-uses-standard-prelude? new) + (module-uses-standard-prelude? old)) + (setf (module-inherited-env new) old) + (setf (module-fixity-table new) + (copy-table (module-fixity-table old))) + (setf (module-default new) (module-default old))) + +;;; This code deals with the actual evaluation of Haskell code. + +;;; This decides whether a variable has type `Dialogue'. + +(define (io-type? var) + (let ((type (var-type var))) + (when (not (gtype? type)) + (error "~s is not a Gtype." type)) + (and (null? (gtype-context type)) + (is-dialogue? (gtype-type type))))) + +(define (is-dialogue? type) + (let ((type (expand-ntype-synonym type))) + (and (ntycon? type) + (eq? (ntycon-tycon type) (core-symbol "Arrow")) + (let* ((args (ntycon-args type)) + (a1 (expand-ntype-synonym (car args))) + (a2 (expand-ntype-synonym (cadr args)))) + (and + (ntycon? a1) + (eq? (ntycon-tycon a1) (core-symbol "SystemState")) + (ntycon? a2) + (eq? (ntycon-tycon a2) (core-symbol "IOResult"))))))) + +(define (is-list-of? type con) + (and (ntycon? type) + (eq? (ntycon-tycon type) (core-symbol "List")) + (let ((arg (expand-ntype-synonym (car (ntycon-args type))))) + (and (ntycon? arg) (eq? (ntycon-tycon arg) con))))) + +(define (apply-exec var) + (initialize-io-system) + (mlet (((_ sec) + (time-execution + (lambda () + (let/cc x + (setf *runtime-abort* (lambda () (funcall x 'error))) + (let ((fn (eval (fullname var)))) + (unless (var-strict? var) + (setf fn (force fn))) + (funcall fn (box 'state)))))))) + (say "~%") + (when (memq 'time *printers*) + (say "Execution time: ~A seconds~%" sec))) + 'done) + +(define (eval-module mod) + (dolist (v (module-vars mod)) + (when (io-type? v) + (when (not (string-starts? "temp_" (symbol->string (def-name v)))) + (say/ne "~&Evaluating ~A.~%" v)) + (apply-exec v)))) + +(define (run-program name) + (compile/load name) + (let ((main-mod (table-entry *modules* '|Main|))) + (if main-mod + (let ((main-var (table-entry (module-symbol-table main-mod) '|main|))) + (if main-var + (apply-exec main-var) + (error "Variable main missing"))) + (error "module Main missing")))) + diff --git a/csys/README b/csys/README new file mode 100644 index 0000000..6f9c183 --- /dev/null +++ b/csys/README @@ -0,0 +1,3 @@ +This directory contains everything relating to the compilation system, +including stuff for parsing unit files, incremental recompilation, and +reading and writing code and interface files. diff --git a/csys/cache-structs.scm b/csys/cache-structs.scm new file mode 100644 index 0000000..ba38840 --- /dev/null +++ b/csys/cache-structs.scm @@ -0,0 +1,48 @@ +;;; these structures deal with the compilation system and the unit cache. + +;;; An entry in the unit cache: + +(define-struct ucache + (slots + (ufile (type string)) ; the name of the file containing the unit definition + (cifile (type string)) ; the filename of the (compiled) interface file + (sifile (type string)) ; the filename of the (uncompiled) interface file + (cfile (type string)) ; the filename of the (compiled) output file + (sfile (type string)) ; the filename of the (uncompiled) output file + (udate (type integer)) ; the write date of ufile + (idate (type integer)) ; the time stamp of the binary interface file + (stable? (type bool)) ; the stable flag + (load-prelude? (type bool)) ; true if unit uses standard prelude + ;; status is initially available (in cache). It is set to loading when + ;; requested and loaded once all imported units are loaded. + (status (type (enum loaded loading available))) + (ifile-loaded (type bool)) ; true when interface is loaded (modules) + (code-loaded (type bool)) ; true when the associated code is in memory + (source-files (type (list string))) ; source files in the unit + (imported-units (type (list string))) ; the filenames of imported unit files + (lisp-files (type (list (tuple string string)))) ; source/binary pairs + (modules (type (list module))) + (printers-set? (type bool)) + (printers (type (list symbol))) + (optimizers-set? (type bool)) + (optimizers (type (list symbol))) + (chunk-size (type (maybe int))) + )) + + +;;; This is used to hold various flags used by the compilation system, +;;; instead of passing them all as individual arguments. + +(define-struct cflags + (slots + ;; Whether to load code for unit into core + (load-code? (type bool) (default '#t)) + ;; Whether to create an output code file. + (write-code? (type bool) (default '#t)) + ;; Affects whether write-code? creates a source or compiled file, + ;; and whether load-code? uses the interpreter or compiler. + ;; Ignored if load-code? and write-code? are both false. + (compile-code? (type bool) (default '#t)) + ;; Whether to create an output interface file. + (write-interface? (type bool) (default '#t)) + )) diff --git a/csys/compiler-driver.scm b/csys/compiler-driver.scm new file mode 100644 index 0000000..5ce5a71 --- /dev/null +++ b/csys/compiler-driver.scm @@ -0,0 +1,640 @@ +;;; compiler-driver.scm -- compilation unit management +;;; +;;; author : John & Sandra +;;; +;;; + + +;;; Flags for controlling various low-level behaviors of the compiler. +;;; You might want to tweak these in the system-building scripts for +;;; different Lisps, but users don't normally need to mess with them. + +(define *compile-interface* '#f) +(define *interface-code-quality* 2) +(define *interface-chunk-size* '#f) +(define *default-code-quality* 2) +(define *optimized-code-quality* 3) +(define *code-chunk-size* 300) + + + +;;;===================================================================== +;;; Main entry point +;;;===================================================================== + +;;; This is the top level driver for the compiler. It takes a file name +;;; and output controls. It returns '#f if compilation fails. + +(define *codefile-cache* '()) + +(define (haskell-compile filename cflags) + (initialize-haskell-system) + (let/cc abort-compile + (dynamic-let ((*abort-compilation* + (lambda () (funcall abort-compile '#f)))) + (initialize-compilation) + (let ((unit (find-cunit-name filename))) + (let ((res (load-compilation-unit unit cflags))) + (map (lambda (x) (module-name x)) (ucache-modules res))))))) + +;;; this is the initialization code that occurs at the start of compilation. + +(define (initialize-compilation) + (initialize-module-table) + (for-each-unit + (lambda (u) + (setf (ucache-status u) 'available)))) + + + +;;;===================================================================== +;;; Filename utilities +;;;===================================================================== + +;;; File extensions + +(define *source-file-extensions* '(".hs" ".lhs")) +(define *unit-file-extension* ".hu") +(define *interface-file-extension* ".hi") +(define *lisp-file-extensions* '(".lisp" ".scm")) + +(define (source-extension? x) + (mem-string x *source-file-extensions*)) + +(define (unit-extension? x) + (string=? x *unit-file-extension*)) + +(define (interface-extension? x) + (string=? x *interface-file-extension*)) + +(define (lisp-extension? x) + (mem-string x *lisp-file-extensions*)) + + +;;; Build file names. + +(define (make-cifilename filename) + (let ((place (filename-place filename)) + (name (string-append (filename-name filename) "-hci"))) + (assemble-filename place name binary-file-type))) + +(define (make-sifilename filename) + (let ((place (filename-place filename)) + (name (string-append (filename-name filename) "-hci"))) + (assemble-filename place name source-file-type))) + +(define (make-cfilename filename) + (add-extension filename binary-file-type)) + +(define (make-sfilename filename) + (add-extension filename source-file-type)) + + +;;; This take a file name (extension ignored) & searches for a unit file. + +(define (locate-existing-cunit name) + (locate-extension name (list *unit-file-extension*))) + +;;; This take a file name (extension ignored) & searches for a source file. + +(define (locate-existing-source-file name) + (locate-extension name *source-file-extensions*)) + +(define (locate-extension name extensions) + (if (null? extensions) + '#f + (let ((name-1 (add-extension name (car extensions)))) + (if (file-exists? name-1) + name-1 + (locate-extension name (cdr extensions)))))) + + +;;; This delivers the name of a compilation unit. The extension of the name +;;; is ignored & a test for the presence of a compilation unit with +;;; the same name is done. If none is found, signal an error. + +(define (find-cunit-name name) + (or (locate-existing-cunit name) + (locate-existing-source-file name) + (signal-file-not-found name))) + + + +;;;===================================================================== +;;; Compilation unit file parsing +;;;===================================================================== + +;;; This parses a unit file. The file simply contains a list of file names. +;;; The files are sorted into two catagories: other compilation units and +;;; source files in the current unit. When a file has no extension, the system +;;; checks for a unit file first and then a source file. + +(define (parse-compilation-unit filename) + (let ((unit-type (filename-type filename))) + (if (or (source-extension? unit-type) (interface-extension? unit-type)) + (create-ucache filename filename (list filename) '() '() '#f '#t + '#f '() '#f '() '#f) + (parse-compilation-unit-aux + filename + (call-with-input-file filename (function gather-file-names)))))) + +(define (create-ucache filename output-filename + source-files imports lisp-files + stable? load-prelude? + printers-set? printers optimizers-set? optimizers + chunk-size) + (let* ((cifilename + (make-cifilename output-filename)) + (sifilename + (make-sifilename output-filename)) + (all-imports + (if load-prelude? + (cons *prelude-unit-filename* imports) + imports)) + (cache-entry + (make ucache + (ufile filename) + (sifile sifilename) + (cifile cifilename) + (sfile (make-sfilename output-filename)) + (cfile (make-cfilename output-filename)) + (udate (current-date)) + (idate (get-latest-ifiledate cifilename sifilename)) + (stable? stable?) + (load-prelude? load-prelude?) + (status 'loading) + (ifile-loaded '#f) + (code-loaded '#f) + (source-files source-files) + (imported-units all-imports) + (lisp-files lisp-files) + (modules '()) + (printers-set? printers-set?) + (printers printers) + (optimizers-set? optimizers-set?) + (optimizers optimizers) + (chunk-size chunk-size)))) + (install-compilation-unit filename cache-entry) + cache-entry)) + +(define (get-latest-ifiledate cifilename sifilename) + (max (or (and (file-exists? cifilename) + (file-write-date cifilename)) + 0) + (or (and (file-exists? sifilename) + (file-write-date sifilename)) + 0))) + + +;;; This returns a list of strings. Blank lines and lines starting in - +;;; are ignored. + +(define (gather-file-names port) + (let ((char (peek-char port))) + (cond ((eof-object? char) + '()) + ((or (char=? char '#\newline) (char=? char '#\-)) + (read-line port) + (gather-file-names port)) + (else + (let ((line (read-line port))) + (cons line (gather-file-names port))))))) + + +;;; Actually parse contents of the unit file. + +;;; These are in the command-interface stuff. +(predefine (set-printers args mode)) +(predefine (set-optimizers args mode)) +(predefine (parse-command-args string start next end)) + +(define (parse-compilation-unit-aux filename strings) + (let ((input-defaults filename) + (output-defaults filename) + (import-defaults filename) + (stable? '#f) + (load-prelude? '#t) + (filenames '()) + (imports '()) + (sources '()) + (lisp-files '()) + (printers '()) + (printers-set? '#f) + (optimizers '()) + (optimizers-set? '#f) + (chunk-size '#f) + (temp '#f)) + ;;; First look for magic flags. + (dolist (s strings) + (cond ((setf temp (string-match-prefix ":input" s)) + (setf input-defaults (merge-file-defaults temp filename))) + ((setf temp (string-match-prefix ":output" s)) + (setf output-defaults (merge-file-defaults temp filename))) + ((setf temp (string-match-prefix ":import" s)) + (setf import-defaults (merge-file-defaults temp filename))) + ((string=? ":stable" s) + (setf stable? '#t)) + ((string=? ":prelude" s) + (setf load-prelude? '#f)) + ((setf temp (string-match-prefix ":p=" s)) + (setf printers-set? '#t) + (setf printers + (set-printers + (parse-command-args temp 0 0 (string-length temp)) + '=))) + ((setf temp (string-match-prefix ":o=" s)) + (setf optimizers-set? '#t) + (setf optimizers + (set-optimizers + (parse-command-args temp 0 0 (string-length temp)) + '=))) + ((setf temp (string-match-prefix ":chunk-size" s)) + (setf chunk-size (string->number temp))) + (else + (push s filenames)))) + ;;; Next sort filenames into imports and source files. + (dolist (s filenames) + (let ((type (filename-type s)) + (fname '#f)) + (cond ((string=? type "") ; punt for now on this issue + (signal-extension-needed s)) +; ((cond ((setf fname +; (locate-existing-cunit +; (merge-file-defaults s import-defaults))) +; (push fname imports)) +; ((setf fname +; (locate-existing-source-file +; (merge-file-defaults s input-defaults))) +; (push fname sources)) +; (else +; (signal-unit-not-found s)))) + ((unit-extension? type) + (setf fname (merge-file-defaults s import-defaults)) + (if (file-exists? fname) + (push fname imports) + (signal-unit-not-found fname))) + ((or (source-extension? type) (interface-extension? type)) + (setf fname (merge-file-defaults s input-defaults)) + (if (file-exists? fname) + (push fname sources) + (signal-unit-not-found fname))) + ((lisp-extension? type) + (setf fname (merge-file-defaults s input-defaults)) + (if (file-exists? fname) + (push (cons fname + (add-extension + (merge-file-defaults s output-defaults) + binary-file-type)) + lisp-files) + (signal-unit-not-found fname))) + (else + (signal-unknown-file-type s))))) + ;; Finally create the unit object. + (create-ucache filename output-defaults + sources imports lisp-files + stable? load-prelude? + printers-set? printers optimizers-set? optimizers + chunk-size))) + + +;;; Helper functions for the above. + +(define (string-match-prefix prefix s) + (let ((prefix-length (string-length prefix)) + (s-length (string-length s))) + (if (>= s-length prefix-length) + (string-match-prefix-aux prefix s prefix-length s-length 0) + '#f))) + +(define (string-match-prefix-aux prefix s prefix-length s-length i) + (cond ((eqv? i prefix-length) + (string-match-prefix-aux-aux s s-length i)) + ((not (char=? (string-ref s i) (string-ref prefix i))) + '#f) + (else + (string-match-prefix-aux prefix s prefix-length s-length (1+ i))))) + +(define (string-match-prefix-aux-aux s s-length i) + (cond ((eqv? i s-length) + "") + ((let ((ch (string-ref s i))) + (or (char=? ch '#\space) (char=? ch #\tab))) + (string-match-prefix-aux-aux s s-length (1+ i))) + (else + (substring s i s-length)))) + +(define (merge-file-defaults filename defaults) + (let ((place (filename-place filename)) + (name (filename-name filename)) + (type (filename-type filename))) + (assemble-filename + (if (string=? place "") defaults place) + (if (string=? name "") defaults name) + (if (string=? type "") defaults type)))) + + +;;;===================================================================== +;;; Guts +;;;===================================================================== + + +;;; This is the main entry to the compilation system. This causes a +;;; unit to be compiled and/or loaded. + +(define (load-compilation-unit filename cflags) + (let ((cunit (lookup-compilation-unit filename))) + (cond ((eq? cunit '#f) + ;; Unit not found in cache. + (load-compilation-unit-aux + (parse-compilation-unit filename) cflags)) + ((eq? (ucache-status cunit) 'loaded) + ;; Already loaded earlier in this compile. + cunit) + ((eq? (ucache-status cunit) 'loading) + (signal-circular-unit filename)) + (else + (load-compilation-unit-aux cunit cflags)) + ))) + + +(define (load-compilation-unit-aux c cflags) + (setf (ucache-status c) 'loading) + (load-imported-units c cflags) + (if (unit-valid? c cflags) + (load-compiled-unit c (cflags-load-code? cflags)) + (locally-compile c cflags)) + (setf (ucache-status c) 'loaded) + ;; Hack, hack. When loading the prelude, make sure magic symbol + ;; table stuff is initialized. + (when (string=? (ucache-ufile c) *prelude-unit-filename*) + (init-prelude-globals)) + c) + +(define (load-compiled-unit c load-code?) + (when (and load-code? (not (ucache-code-loaded c))) + (when (memq 'loading *printers*) + (format '#t "~&Loading unit ~s.~%" (ucache-ufile c)) + (force-output)) + (load-lisp-files (ucache-lisp-files c)) + (load-more-recent-file (ucache-cfile c) (ucache-sfile c)) + (setf (ucache-code-loaded c) '#t)) + (when (not (ucache-ifile-loaded c)) + (read-binary-interface c)) + (dolist (m (ucache-modules c)) + (add-module-to-symbol-table m)) + (link-instances (ucache-modules c))) + + +;;; These globals save the Prelude symbol table to avoid copying it +;;; into all modules which use the Prelude. + +;;; Danger! This assumes that every local symbol in the Prelude is +;;; exported. + +(define *prelude-initialized* '#f) + +(define (init-prelude-globals) + (when (not *prelude-initialized*) + (let ((pmod (locate-module '|Prelude|))) + (setf *prelude-symbol-table* (module-symbol-table pmod)) + (setf *prelude-fixity-table* (module-fixity-table pmod)) + (when (eq? (module-inverted-symbol-table pmod) '#f) + (let ((table (make-table))) + (table-for-each (lambda (name def) + (setf (table-entry table def) name)) + *prelude-symbol-table*) + (setf (module-inverted-symbol-table pmod) table))) + (setf *prelude-inverted-symbol-table* + (module-inverted-symbol-table pmod))) + (setf *prelude-initialized* '#t))) + + +;;; This recursively loads all units imported by a given unit. + +(define (load-imported-units c cflags) + (dolist (filename (ucache-imported-units c)) + (load-compilation-unit filename cflags))) + + + +;;; Load or compile lisp files. + +(define (load-lisp-files lisp-files) + (dolist (f lisp-files) + (load-more-recent-file (cdr f) (car f)))) + +(define (compile-lisp-files lisp-files) + (dolist (f lisp-files) + (let ((source (car f)) + (binary (cdr f))) + (when (not (lisp-binary-current source binary)) + (compile-file source binary)) + (load binary)))) + + + +;;; This determines whether a unit is valid. + +(define (unit-valid? c cflags) + (and (or (ucache-stable? c) + ;; If the unit is not stable, make sure its source files + ;; haven't changed. + (and (all-imports-current (ucache-imported-units c) + (ucache-idate c)) + (all-sources-current (ucache-source-files c) + (ucache-idate c)) + (all-lisp-sources-current (ucache-lisp-files c) + (ucache-idate c)))) + (or (ucache-ifile-loaded c) + ;; If the interface hasn't been loaded already, make sure + ;; that the interface file exists. + (file-exists? (ucache-cifile c)) + (file-exists? (ucache-sifile c))) + (or (not (cflags-load-code? cflags)) + ;; If we're going to load code, make sure that the code file + ;; exists. + (ucache-code-loaded c) + (file-exists? (ucache-cfile c)) + (file-exists? (ucache-sfile c))) + (or (not (cflags-write-code? cflags)) + ;; If we need to produce a code file, make sure this has + ;; already been done. + ;; Don't write files for stable units which have already + ;; been loaded, regardless of whether or not the file exists. + (and (ucache-stable? c) (ucache-code-loaded c)) + (file-exists? (ucache-cfile c)) + (and (not (cflags-compile-code? cflags)) + (file-exists? (ucache-sfile c)))) + (or (not (cflags-compile-code? cflags)) + ;; If we need to compile the lisp files, make sure this has + ;; already been done. + ;; Don't do this for stable units which have already + ;; been loaded. + (and (ucache-stable? c) (ucache-code-loaded c)) + (all-lisp-binaries-current (ucache-lisp-files c))) + (or (not (cflags-write-interface? cflags)) + ;; If we need to produce an interface file, make sure this has + ;; already been done. + ;; Don't write files for stable units which have already + ;; been loaded, regardless of whether or not the file exists. + (and (ucache-stable? c) (ucache-ifile-loaded c)) + (file-exists? (ucache-cifile c)) + (and (not *compile-interface*) + (file-exists? (ucache-sifile c)))) + )) + +(define (all-sources-current sources unit-write-date) + (every (lambda (s) + (let ((d (file-write-date s))) + (and d (> unit-write-date d)))) + sources)) + +(define (all-imports-current imports unit-write-date) + (every (lambda (s) (> unit-write-date + (ucache-idate (lookup-compilation-unit s)))) + imports)) + +(define (all-lisp-sources-current lisp-files unit-write-date) + (every (lambda (s) + (let ((d (file-write-date (car s)))) + (and d (> unit-write-date d)))) + lisp-files)) + +(define (all-lisp-binaries-current lisp-files) + (every (lambda (s) + (lisp-binary-current (car s) (cdr s))) + lisp-files)) + +(define (lisp-binary-current source binary) + (and (file-exists? binary) + (let ((sd (file-write-date source)) + (bd (file-write-date binary))) + (and sd bd (> bd sd))))) + + +;;; This does the actual job of compilation. + +(define (locally-compile c cflags) + (dynamic-let ((*printers* + (if (ucache-printers-set? c) + (ucache-printers c) + (dynamic *printers*))) + (*optimizers* + (if (ucache-optimizers-set? c) + (ucache-optimizers c) + (dynamic *optimizers*)))) + (when (memq 'compiling *printers*) + (format '#t "~&Compiling unit ~s.~%Optimizers: ~A~%" + (ucache-ufile c) + *optimizers*) + (force-output)) + (if (cflags-compile-code? cflags) + (compile-lisp-files (ucache-lisp-files c)) + (load-lisp-files (ucache-lisp-files c))) + (multiple-value-bind (mods code) + (compile-haskell-files (ucache-source-files c)) + ;; General bookkeeping to update module interface in cache. + (setf (ucache-modules c) mods) + (setf (ucache-idate c) (current-date)) + (setf (ucache-ifile-loaded c) '#t) + ;; Write interface file if necessary. + (when (cflags-write-interface? cflags) + (let ((phase-start-time (get-run-time)) + (icode (create-dump-code c mods (ucache-load-prelude? c)))) + (if (dynamic *compile-interface*) + (write-compiled-code-file + (ucache-cifile c) + icode + (dynamic *interface-code-quality*) + (dynamic *interface-chunk-size*)) + (write-interpreted-code-file (ucache-sifile c) icode '#f)) + (when (memq 'phase-time *printers*) + (let* ((current-time (get-run-time)) + (elapsed-time (- current-time phase-start-time))) + (format '#t "Interface complete: ~A seconds~%" elapsed-time) + (force-output))))) + ;; Write code file if necessary. + (when (cflags-write-code? cflags) + (if (cflags-compile-code? cflags) + (write-compiled-code-file + (ucache-cfile c) + code + (if (memq 'lisp (dynamic *optimizers*)) + (dynamic *optimized-code-quality*) + (dynamic *default-code-quality*)) + (or (ucache-chunk-size c) (dynamic *code-chunk-size*))) + (write-interpreted-code-file (ucache-sfile c) code '#t))) + ;; Load or evaluate code if necessary. + ;; If we just wrote a compiled code file, load that; otherwise + ;; do eval or in-core compilation. + (when (cflags-load-code? cflags) + (if (and (cflags-write-code? cflags) + (cflags-compile-code? cflags)) + (load (ucache-cfile c)) + (eval code (cflags-compile-code? cflags))) + (setf (ucache-code-loaded c) '#t)) + ))) + + + +;;;===================================================================== +;;; Cache manager +;;;===================================================================== + +;;; This is the cache manager for compilation units. We use an alist at +;;; the moment. + +(define *unit-cache* '()) + +(define (reset-unit-cache) + (setf *unit-cache* '())) + + +;;; This checks to make sure that the compilation unit it finds +;;; in the cache has not been made out-of-date by updates to the unit file. + +(define (lookup-compilation-unit name) + (let ((r (ass-string name *unit-cache*))) + (if r + (let ((c (cdr r))) + (if (or (ucache-stable? c) + (> (ucache-udate c) + (or (file-write-date (ucache-ufile c)) 0))) + c + '#f)) + '#f))) + +(define (install-compilation-unit name c) + (let ((r (ass-string name *unit-cache*))) + (if (eq? r '#f) + (push (cons name c) *unit-cache*) + (setf (cdr r) c)))) + +(define (for-each-unit proc) + (dolist (c *unit-cache*) + (funcall proc (cdr c)))) + + +;;;===================================================================== +;;; Error utilities +;;;===================================================================== + +(define (signal-circular-unit filename) + (fatal-error 'circular-unit + "The compilation unit ~a has a circular dependency." + filename)) + +(define (signal-unit-not-found filename) + (fatal-error 'unit-not-found + "The compilation unit file ~a was not found." + filename)) + +(define (signal-extension-needed filename) + (fatal-error 'extension-needed + "You must provide an extension on the filename ~a in the .hu file." + filename)) + + + + + diff --git a/csys/csys.scm b/csys/csys.scm new file mode 100644 index 0000000..a4683b6 --- /dev/null +++ b/csys/csys.scm @@ -0,0 +1,25 @@ +;;; csys.scm -- compilation unit definition for the compilation system + +(define-compilation-unit csys + (source-filename "$Y2/csys/") + (require global runtime flic) + (unit cache-structs + (source-filename "cache-structs.scm")) + (unit compiler-driver + (require cache-structs) + (source-filename "compiler-driver.scm")) + (unit dump-params + (require cache-structs) + (source-filename "dump-params.scm")) + (unit dump-macros + (require dump-params) + (source-filename "dump-macros.scm")) + (unit dump-interface + (require dump-macros) + (source-filename "dump-interface.scm")) + (unit dump-flic + (require dump-macros) + (source-filename "dump-flic.scm")) + (unit dump-cse + (require dump-macros) + (source-filename "dump-cse.scm"))) diff --git a/csys/dump-cse.scm b/csys/dump-cse.scm new file mode 100644 index 0000000..38ec020 --- /dev/null +++ b/csys/dump-cse.scm @@ -0,0 +1,182 @@ +;;; This file handles common subexpressions in the interface file. +;;; Common subexpressions are detected in two places: gtypes and strictness +;;; properties. + +;;; Compressing strictness signatures + +;;; A strictness is represented by a list of booleans. We do two things to +;;; compress strictnesses: all lists less than *pre-defined-strictness-size* +;;; are pre-computed in a vector and the first *pre-defined-strictness-vars* +;;; vector elements are cached in global vars. The strictness will dump as +;;; as either a global or as a vector reference into the vector. + +(define (initialize-strictness-table) + (setf (dynamic *pre-defined-strictness-table*) + (make-vector (expt 2 (1+ (dynamic *pre-defined-strictness-size*))))) + (setf (vector-ref *pre-defined-strictness-table* 1) '()) + (do ((i 1 (1+ i)) + (j 1 (* j 2)) + (k 2 (* k 2))) + ((> i *pre-defined-strictness-size*)) + (do ((l 0 (1+ l))) + ((>= l j)) + (setf (vector-ref *pre-defined-strictness-table* (+ k l)) + (cons '#f (vector-ref *pre-defined-strictness-table* (+ j l)))) + (setf (vector-ref *pre-defined-strictness-table* (+ k j l)) + (cons '#t (vector-ref *pre-defined-strictness-table* (+ j l)))))) + (set-strictness-vars)) + +(define (strictness-table-ref x) + (vector-ref (dynamic *pre-defined-strictness-table*) x)) + +(define (dump-strictness s) + (if (null? s) + ''() + (dump-strictness-1 s s 0 0))) + +(define (dump-strictness-1 s s1 n size) + (if (null? s1) + (if (> size *pre-defined-strictness-size*) + (dump-big-strictness (- size *pre-defined-strictness-size*) s) + (let ((k (+ n (expt 2 size)))) + (if (< k *pre-defined-strictness-vars*) + `(dynamic ,(vector-ref *pre-defined-strictness-names* k)) + `(strictness-table-ref ,k)))) + (dump-strictness-1 s (cdr s1) (+ (* 2 n) (if (car s1) 1 0)) (1+ size)))) + +(define (dump-big-strictness k s) + (if (= k 0) + (dump-strictness s) + `(cons ',(car s) + ,(dump-big-strictness (1- k) (cdr s))))) + +;;; This routine handles saving type signatures (gtypes). +;;; common subexpressions are detected in two places: the type body +;;; and the the contexts. + +(define (init-predefined-gtyvars) + (setf *saved-gtyvars* (make-vector *num-saved-gtyvars*)) + (dotimes (i *num-saved-gtyvars*) + (setf (vector-ref *saved-gtyvars* i) (**gtyvar i))) + (setup-gtyvar-vars)) + +(define (init-cse-structs) + (initialize-strictness-table) + (init-predefined-gtyvars)) + +(define (save-cse-value v) + (setf (vector-ref (dynamic *saved-cse-values*) (dynamic *cse-value-num*)) v) + (incf (dynamic *cse-value-num*))) + +(define (cse-init-code) + (let* ((n (length *cse-objects*)) + (init-code '())) + (do ((i (1- n) (1- i)) + (init *cse-objects* (cdr init))) + ((null? init)) + (push `(save-cse-value ,(car init)) init-code)) + `((setf *saved-cse-values* (make-vector ,n)) + (setf *cse-value-num* 0) + ,@init-code))) + +(define (remember-dumped-object init-code) + (push init-code *cse-objects*) + (incf *cse-object-num*) + *cse-object-num*) + +(define (cse-value-ref x) + (vector-ref (dynamic *saved-cse-values*) x)) + +(define (cse-ref-code n) + (cond ((eqv? n 0) + ''()) + ((<= n *num-saved-gtyvars*) + `(dynamic ,(vector-ref *saved-gtyvar-varnames* (1- n)))) + (else + `(cse-value-ref ,(- n *num-saved-gtyvars* 1))))) + +(define (dump-gtyvar g) + (let ((n (gtyvar-varnum g))) + (if (< n *num-saved-gtyvars*) + (1+ n) + (remember-dumped-object `(**gtyvar ,n))))) + +(define (dump-context-list contexts) + (if (null? contexts) + 0 + (let* ((rest (dump-context-list (cdr contexts))) + (classes (dump-class-list (car contexts))) + (t1 (assq/insert-l classes *gtype-class-index*)) + (res (assq/insert rest (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(cons ,(cse-ref-code classes) ,(cse-ref-code rest))))) + (setf (cdr res) z) + z) + (cdr res))))) + +(define (dump-class-list classes) + (if (null? classes) + 0 + (let* ((rest (dump-class-list (cdr classes))) + (class (dump-class/n (car classes))) + (t1 (assq/insert-l class *context-class-index*)) + (res (assq/insert rest (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(cons ,class ,(cse-ref-code rest))))) + (setf (cdr res) z) + z) + (cdr res))))) + +(define (dump-gtype-1 g) + (cond ((gtyvar? g) + (dump-gtyvar g)) + ((ntyvar? g) + (dump-gtype-1 (prune g))) + (else + (dump-gtycon g)))) + +(define (dump-gtycon g) + (let* ((ty (ntycon-tycon g)) + (tycon (if (algdata? ty) (dump-algdata/n ty) (dump-synonym/n ty))) + (l (dump-gtype-list (ntycon-args g))) + (t1 (assq/insert-l tycon *gtype-tycon-index*)) + (res (assq/insert l (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(**ntycon ,tycon ,(cse-ref-code l))))) + (setf (cdr res) z) + z) + (cdr res)))) + +(define (dump-gtype-list l) + (if (null? l) + 0 + (let* ((g (dump-gtype-1 (car l))) + (rest (dump-gtype-list (cdr l))) + (t1 (assq/insert-l g *gtype-list-index*)) + (res (assq/insert rest (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(cons ,(cse-ref-code g) + ,(cse-ref-code rest))))) + (setf (cdr res) z) + z) + (cdr res))))) + +(define (dump-gtype/cse g) + (cse-ref-code + (let* ((context (dump-context-list (gtype-context g))) + (type (dump-gtype-1 (gtype-type g))) + (t1 (assq/insert-l type *gtype-index*)) + (res (assq/insert context (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(**gtype ,(cse-ref-code context) + ,(cse-ref-code type))))) + (setf (cdr res) z) + z) + (cdr res))))) + + diff --git a/csys/dump-flic.scm b/csys/dump-flic.scm new file mode 100644 index 0000000..0fc654d --- /dev/null +++ b/csys/dump-flic.scm @@ -0,0 +1,130 @@ +;;; dump-flic.scm -- general dump functions for flic structures +;;; +;;; author : Sandra Loosemore +;;; date : 24 Feb 1993 +;;; +;;; +;;; This stuff is used to write inline expansions to the interface file. +;;; + + +(define-flic-walker dump-flic (object var-renamings)) + +(define (dump-flic-list objects var-renamings) + (let ((result '())) + (dolist (o objects) + (push (dump-flic o var-renamings) result)) + `(list ,@(nreverse result)))) + +(define (dump-flic-top object) + (dump-flic object '())) + + +(define (make-temp-bindings-for-dump oldvars var-renamings) + (let ((vars '()) + (bindings '())) + (dolist (v oldvars) + (let ((var (def-name v)) + (temp (gensym))) + (push temp vars) + (push `(,temp (create-temp-var ',var)) bindings) + (push (cons v temp) var-renamings))) + (setf bindings (nreverse bindings)) + (setf vars (nreverse vars)) + (values vars bindings var-renamings))) + +(define-dump-flic flic-lambda (object var-renamings) + (multiple-value-bind (vars bindings var-renamings) + (make-temp-bindings-for-dump (flic-lambda-vars object) var-renamings) + `(let ,bindings + (make-flic-lambda + (list ,@vars) + ,(dump-flic (flic-lambda-body object) var-renamings))) + )) + +(define-dump-flic flic-let (object var-renamings) + (multiple-value-bind (vars bindings var-renamings) + (make-temp-bindings-for-dump (flic-let-bindings object) var-renamings) + `(let ,bindings + ,@(map (lambda (temp v) + `(setf (var-value ,temp) + ,(dump-flic (var-value v) var-renamings))) + vars + (flic-let-bindings object)) + (make-flic-let + (list ,@vars) + ,(dump-flic (flic-let-body object) var-renamings) + ',(flic-let-recursive? object))) + )) + +(define-dump-flic flic-app (object var-renamings) + `(make-flic-app + ,(dump-flic (flic-app-fn object) var-renamings) + ,(dump-flic-list (flic-app-args object) var-renamings) + ',(flic-app-saturated? object))) + +(define-dump-flic flic-ref (object var-renamings) + (let* ((var (flic-ref-var object)) + (entry (assq var var-renamings))) + (if entry + `(make-flic-ref ,(cdr entry)) + `(make-flic-ref ,(dump-object var))))) + +(define-dump-flic flic-const (object var-renamings) + (declare (ignore var-renamings)) + `(make-flic-const ',(flic-const-value object))) + +(define-dump-flic flic-pack (object var-renamings) + (declare (ignore var-renamings)) + `(make-flic-pack ,(dump-object (flic-pack-con object)))) + +(define-dump-flic flic-case-block (object var-renamings) + `(make-flic-case-block + ',(flic-case-block-block-name object) + ,(dump-flic-list (flic-case-block-exps object) var-renamings))) + +(define-dump-flic flic-return-from (object var-renamings) + `(make-flic-return-from + ',(flic-return-from-block-name object) + ,(dump-flic (flic-return-from-exp object) var-renamings))) + +(define-dump-flic flic-and (object var-renamings) + `(make-flic-and + ,(dump-flic-list (flic-and-exps object) var-renamings))) + +(define-dump-flic flic-if (object var-renamings) + `(make-flic-if + ,(dump-flic (flic-if-test-exp object) var-renamings) + ,(dump-flic (flic-if-then-exp object) var-renamings) + ,(dump-flic (flic-if-else-exp object) var-renamings))) + +(define-dump-flic flic-sel (object var-renamings) + `(make-flic-sel + ,(dump-object (flic-sel-con object)) + ,(flic-sel-i object) + ,(dump-flic (flic-sel-exp object) var-renamings))) + +(define-dump-flic flic-is-constructor (object var-renamings) + `(make-flic-is-constructor + ,(dump-object (flic-is-constructor-con object)) + ,(dump-flic (flic-is-constructor-exp object) var-renamings))) + +(define-dump-flic flic-con-number (object var-renamings) + `(make-flic-con-number + ,(dump-object (flic-con-number-type object)) + ,(dump-flic (flic-con-number-exp object) var-renamings))) + +(define-dump-flic flic-void (object var-renamings) + (declare (ignore object var-renamings)) + `(make-flic-void)) + + + + + + + + + + + diff --git a/csys/dump-interface.scm b/csys/dump-interface.scm new file mode 100644 index 0000000..37b3bbd --- /dev/null +++ b/csys/dump-interface.scm @@ -0,0 +1,800 @@ +;;; dump-interface.scm -- interface file writer/loader +;;; +;;; author : John & Sandra +;;; date : 8 Jul 1992 +;;; +;;; This writes binary interface files. A binary interface file is just +;;; a lisp (mumble) source file which directly builds the ast structure +;;; created by a compilation. These files could be stored in either +;;; source or binary (compiled lisp) form. + +;;; An interface may reference entities defined in other interfaces. +;;; To ensure consistancy between when an interface is written and +;;; when it is read back in, a stamp is assigned to all interface files +;;; which serves as a unique id. The stamps of all imported units are +;;; saved and examined at load time. + + + +;;;================================================================== +;;; Interface to compilation system +;;;================================================================== + + +;;; For compiled code, don't actually write out all the source code. +;;; Use a magic macro to memoize the form to be compiled. + +(define *form-to-compile* '#f) +(define *magic-file-to-compile* "$HASKELL/bin/magic.scm") + + +;;; The output from compiling the prelude can completely overwhelm +;;; the Lisp compiler. If this variable is a number, it specifies +;;; a "reasonable" number of top-level forms which can be compiled +;;; and write-compiled-code-file will try to break up the input +;;; code automagically. + +(define *magic-chunk-size* '#f) + + +;;; This is called to write both the code file and the interface file. + +(define (write-compiled-code-file filename code code-quality chunk-size) + (let ((phase-start-time (get-run-time)) + (forms (flatten-forms code))) + (dynamic-let ((*magic-chunk-size* + (or chunk-size (dynamic *magic-chunk-size*))) + (*code-quality* + (or code-quality (dynamic *code-quality*)))) + (if (or (not (dynamic *magic-chunk-size*)) + (<= (the fixnum (length forms)) + (the fixnum (dynamic *magic-chunk-size*)))) + (write-compiled-code-file-aux filename `(begin ,@forms)) + (with-compilation-unit () + (write-compiled-code-file-aux + filename + `(begin + ,@(map (lambda (f) `(load ,f)) + (write-compiled-code-file-split filename forms))) + )))) + (when (memq 'phase-time *printers*) + (let* ((current-time (get-run-time)) + (elapsed-time (- current-time phase-start-time))) + (format '#t "Lisp compilation complete: ~A seconds~%" elapsed-time))) + )) + +(define (write-compiled-code-file-split filename forms) + (let ((place (filename-place filename)) + (name (filename-name filename)) + (type (filename-type filename)) + (result '())) + (do ((i 0 (1+ i))) + ((null? forms)) + (multiple-value-bind (head tail) + (split-list forms (dynamic *magic-chunk-size*)) + (let ((fname + (assemble-filename + place (format '#f "~a-part~a" name i) type))) + (push fname result) + (write-compiled-code-file-aux fname `(begin ,@head)) + (setf forms tail)))) + (nreverse result))) + +(define (flatten-forms code) + (if (and (pair? code) (eq? (car code) 'begin)) + (nreverse (flatten-forms-aux (cdr code) '())) + (list code))) + +(define (flatten-forms-aux forms result) + (dolist (f forms) + (if (and (pair? f) (eq? (car f) 'begin)) + (setf result (flatten-forms-aux (cdr f) result)) + (push f result))) + result) + + +(define (write-compiled-code-file-aux filename code) + (dynamic-let ((*form-to-compile* code)) + (compile-file (dynamic *magic-file-to-compile*) filename))) + +(define-syntax (magic-form-to-compile) + (dynamic *form-to-compile*)) + + +;;; Writing source code is good for debugging purposes, but slow. +;;; The *print-circle* and *print-shared* flags have to be set because +;;; the code printed out may contain gensyms, and this will ensure +;;; that the code can be read in again. + +(define (write-interpreted-code-file filename code hairy?) + (dynamic-let ((*print-circle* '#t) + (*print-shared* '#t)) + (call-with-output-file + filename + (lambda (port) + (if hairy? + (pprint-flatten code port) + (print-flatten code port)))))) + + +;;; This attempts to read a compiled interface for a unit. This is +;;; done whenever the unit file is newer than the source file. If +;;; imported units have changed, the load will fail and recompilation +;;; will be attempted. +;;; The caller is responsible for making sure that the interface file exists +;;; and for making sure that the interface file is up-to-date with +;;; respect to imported modules and that all the imported modules are +;;; known. + +;;; These variables are assigned by the code in the dump file. + +(define *modules-loaded* '()) +(define *modules-imported* '()) +(define *defs-referenced* '()) +(define *saved-cse-values* '()) +(define *writer-version* '()) + +(define (read-binary-interface unit) + (dynamic-let ((*modules-loaded* '()) + (*modules-imported* '()) + (*defs-referenced* '()) + (*saved-cse-values* '()) + (*writer-version* '())) + (let ((file-date + (load-more-recent-file (ucache-cifile unit) (ucache-sifile unit)))) + (cond ((string=? *writer-version* *haskell-compiler-version*) + (setf (ucache-idate unit) file-date) + (setf (ucache-modules unit) (vector->list *modules-loaded*)) + (setf (ucache-ifile-loaded unit) '#t) + '#t) + (else + (signal-incompatible-interface-file (ucache-cifile unit)) + '#f))))) + +(define (signal-incompatible-interface-file filename) + (fatal-error 'incompatible-interface-file + "File ~A~%~ + was written by a different version of the Haskell system.~%~ + You must remove it and recompile." + filename)) + + +(define (load-more-recent-file cfile sfile) + (cond ((file-exists? cfile) + (if (or (not (file-exists? sfile)) + (> (file-write-date cfile) + (file-write-date sfile))) + (load-compiled-interface-file cfile) + (load-interpreted-interface-file sfile))) + ((file-exists? sfile) + (load-interpreted-interface-file sfile)) + (else + (signal-file-not-found cfile)))) + +(define (load-interpreted-interface-file file) + (load file) + (file-write-date file)) + +(define (load-compiled-interface-file file) + (load file) + (file-write-date file)) + + +;;;================================================================== +;;; Dump code generator +;;;================================================================== + +;;; Globals + +(define *dump-defs* '()) +(define *dump-slot-init-code* '()) +(define *dump-def-counter* 0) +(define *dump-def-code-table* (make-table)) +(define *cse-objects* '()) +(define *cse-value-num* 0) +(define *cse-object-num* '()) +(define *gtype-class-index* '()) +(define *context-class-index* '()) +(define *gtype-tycon-index* '()) +(define *gtype-list-index* '()) +(define *gtype-index* '()) +(define *number-vars-dumped* 0) + + +(define-syntax (def-dump-code def) + `(table-entry *dump-def-code-table* ,def)) + +;;; This saves slot initialization code. + +(define (add-dump-init code) + (push code *dump-slot-init-code*)) + + +;;; Here is the top-level call. + +(define (create-dump-code unit modules load-prelude?) + (dynamic-let ((*unit* (module-unit (car modules))) + (*dump-defs* '()) + (*dump-slot-init-code* '()) + (*dump-def-counter* 0) + (*dump-def-code-table* (make-table)) + (*cse-objects* '()) + (*cse-object-num* *num-saved-gtyvars*) + (*gtype-class-index* '()) + (*context-class-index* '()) + (*gtype-tycon-index* '()) + (*gtype-list-index* '()) + (*gtype-index* '()) + (*number-vars-dumped* 0) + (*number-types-dumped* 0) + (*number-classes-dumped* 0)) + (let ((res (create-dump-code-aux unit modules load-prelude?))) + (when (memq 'dumper (dynamic *printers*)) + (pprint* res)) + (when (memq 'dump-stat (dynamic *printers*)) + (format '#t + "~&Dumped ~A definitions, ~A type objects, and ~A classes.~%" + *number-vars-dumped* *number-types-dumped* + *number-classes-dumped*) + (format '#t "Used ~A definitions and ~A type cells.~%" + *dump-def-counter* (length *cse-objects*))) + res))) + +;;; This assumes all modules are in the same compilation unit and that +;;; *unit* is set to that unit. +;;; imod-code establishes local bindings for all the imported modules. +;;; dmod-code establishes local bindings for all the modules defined in +;;; this compilation unit. + +(define (create-dump-code-aux unit modules load-prelude?) + (let* ((imod-counter 0) + (imod-alist '()) + (explicit-imports (collect-all-imported-modules unit)) + (all-imports (if load-prelude? + (append (collect-prelude-modules) explicit-imports) + explicit-imports)) + (imod-code (map (lambda (m) + (push (cons (module-name m) imod-counter) + imod-alist) + (incf imod-counter) + `(locate-module ',(module-name m))) + all-imports)) + (dmod-counter 0) + (dmod-alist '()) + (dmod-code (map (lambda (m) + (push (cons (module-name m) dmod-counter) + dmod-alist) + (incf dmod-counter) + `(make module + (unit ',(module-unit m)) + (name ',(module-name m)) + (type ',(module-type m)))) + modules))) + ;; This actually does most of the work. It dumps the module asts by + ;; placing inits for each slot into *dump-slot-init-code*. A list of + ;; definitions referenced is maintained in *dump-defs*. + (dolist (m modules) + (dump-module m (cdr (assq (module-name m) dmod-alist)))) + ;; This creates the final code + `(begin + (setf *writer-version* ',*haskell-compiler-version*) + (setf *modules-imported* (vector ,@imod-code)) + (setf *modules-loaded* (vector ,@dmod-code)) + ;; This sets the elements individually instead of using the vector + ;; function, because the vector may be longer than + ;; call-arguments-limit. + (setf *defs-referenced* + (make-vector ,(dynamic *dump-def-counter*))) + ,@(map (lambda (d) + `(setf ,(def-dump-code d) + ,(make-def-init-code d imod-alist dmod-alist))) + *dump-defs*) + ,@(cse-init-code) + ,@(dynamic *dump-slot-init-code*) + ) + )) + + +;;; Runtime support + +(define-syntax (lookup-imported-mod i) + `(vector-ref *modules-imported* ,i)) + +(define-syntax (lookup-defined-mod i) + `(vector-ref *modules-loaded* ,i)) + +(define (set-export-from-def-vector table key index) + (setf (table-entry table key) + (list (cons key (vector-ref *defs-referenced* index))))) + +(define (set-export-from-def table key def) + (setf (table-entry table key) + (list (cons key def)))) + +(define (set-symtab-from-def-vector table key index) + (setf (table-entry table key) + (vector-ref *defs-referenced* index))) + +(define (init-variable-slots var exported? toplevel? type simple? strict?) + (setf (def-exported? var) exported?) + (setf (var-toplevel? var) toplevel?) + (setf (var-type var) type) + (setf (var-simple? var) simple?) + (setf (var-strict? var) strict?) + var) + +(define (init-function-slots var exported? toplevel? type simple? strict? + arity strictness opt-entry) + (setf (def-exported? var) exported?) + (setf (var-toplevel? var) toplevel?) + (setf (var-type var) type) + (setf (var-simple? var) simple?) + (setf (var-strict? var) strict?) + (setf (var-arity var) arity) + (setf (var-strictness var) strictness) + (setf (var-optimized-entry var) opt-entry) + var) + +(define (init-method-var-slots var class default method-signature) + (setf (method-var-class var) class) + (setf (method-var-default var) default) + (setf (method-var-method-signature var) method-signature) + var) + +(define (init-constructor-slots + con arity types signature tag alg fixity infix?) + (setf (con-arity con) arity) + (setf (con-types con) types) + (setf (con-signature con) signature) + (setf (con-tag con) tag) + (setf (con-alg con) alg) + (setf (con-fixity con) fixity) + (setf (con-infix? con) infix?) + (dotimes (i arity) + (push '#f (con-slot-strict? con))) + con) + +(define (make-new-instance algdata tyvars class context gcontext dictionary m) + (make instance + (algdata algdata) + (tyvars tyvars) + (class class) + (context context) + (gcontext gcontext) + (dictionary dictionary) + (methods m) + (ok? '#t))) + + +;;; This computes the transitive closure of all modules available to +;;; a unit. + +(define (collect-all-imported-modules unit) + (collect-all-modules-1 (ucache-imported-units unit) '() '())) + +(define (collect-all-modules-1 units mods-so-far units-seen) + (cond ((null? units) + mods-so-far) + ((mem-string (car units) units-seen) + (collect-all-modules-1 (cdr units) mods-so-far units-seen)) + (else + (let ((u (lookup-compilation-unit (car units)))) + (collect-all-modules-1 + (append (ucache-imported-units u) (cdr units)) + (append (ucache-modules u) mods-so-far) + (cons (ucache-ufile u) units-seen)))) + )) + +(define (collect-prelude-modules) + (let ((prelude-unit (lookup-compilation-unit *prelude-unit-filename*))) + (append (ucache-modules prelude-unit) + (collect-all-imported-modules prelude-unit)))) + +(define (def->core-name-string def) + (if (con? def) + (remove-con-prefix (symbol->string (def-name def))) + (symbol->string (def-name def)))) + +;;; This code returns the load time definition for an object. When the +;;; object is a core symbol or in a different unit, previously +;;; created definitions are returned. Otherwise, a new definition is +;;; created. + +(define (make-def-init-code d imod-alist dmod-alist) + (declare (ignore dmod-alist)) + (cond ((def-core? d) + `(core-symbol ,(def->core-name-string d))) + ((eq? (def-unit d) *unit*) + `(create-definition/inner + ',(def-module d) + ',(def-name d) + ',(cond ((method-var? d) 'method-var) + ((var? d) 'var) + ((con? d) 'con) + ((synonym? d) 'synonym) + ((algdata? d) 'algdata) + ((class? d) 'class)))) + ((is-tuple-constructor? d) + `(tuple-constructor ,(tuple-constructor-arity d))) + ((is-tuple-tycon? d) + `(tuple-tycon ,(tuple-constructor-arity (car (algdata-constrs d))))) + (else + (let ((m (assq (def-module d) imod-alist))) + ;; This is a bogus error message. The problem is that nothing + ;; so far ensures units are closed under import/export: some + ;; modules may be referenced that are accidentally in the symbol + ;; table. The unif file for the current module needs to be + ;; updated when this happens. + (when (eq? m '#f) + (fatal-error 'symbol-not-in-unit + "Reference to symbol ~A in module ~A: not in compilation unit.~%" + (def-name d) (def-module d))) + `(table-entry + (module-symbol-table + (lookup-imported-mod ,(tuple-2-2 m))) + ',(def-name d)))) + )) + + +;;; Once a module has been compiled, most of its slots are useless. +;;; All we really need to save are the identifying information, +;;; symbol table, and export table. +;;; Instances also need to be dumped here instead of with class objects; +;;; this is because links can go across compilation unit boundaries. +;;; They are fixed up when pulling units out of the cache. +;;; The identifying info is stored when the module variable is bound. + + +(define (dump-module module index) + (let ((mod-exp `(lookup-defined-mod ,index)) + (save-all-symbols (or (eq? (module-type module) 'standard) + (eq? (module-name module) '|Prelude|)))) + ;; Dump symbol table entries only for defs for which this is + ;; the "home" module. (In other words, ignore imported defs.) + ;; The purpose of this is to allow references from other + ;; interface files to be resolved; see make-def-init-code. + ;; Jcp: we need to save the complete symbol table for incremental + ;; compilation to work. + (let ((code '())) + (table-for-each + (lambda (key val) + (when (or save-all-symbols + (eq? (def-module val) (module-name module))) + (let ((def (dump-object val))) + (push + (if (and (pair? def) + (eq? (car def) 'vector-ref) + (eq? (cadr def) '*defs-referenced*)) + `(set-symtab-from-def-vector table ',key ,(caddr def)) + `(setf (table-entry table ',key) ,def)) + code)))) + (module-symbol-table module)) + (add-dump-init `(setf (module-symbol-table ,mod-exp) + (let ((table (make-table))) ,@code table)))) + ;; dump the fixity table - needed by the incremental compiler + (when save-all-symbols + (let ((code '())) + (table-for-each + (lambda (key val) + (push `(setf (table-entry table ',key) + (make-fixity ',(fixity-associativity val) + ',(fixity-precedence val))) + code)) + (module-fixity-table module)) + (add-dump-init `(setf (module-fixity-table ,mod-exp) + (let ((table (make-table))) ,@code table))))) + ;; Dump all export table entries. This is used by the import/export + ;; phase to resolve references. + (let ((code '())) + (table-for-each + (lambda (key val) + ;; val is an a-list of (sym . def) pairs. + ;; Look for shortcut to reduce size of generated code. + (push + (if (and (null? (cdr val)) + (eq? (car (car val)) key)) + (let ((def (dump-object (cdr (car val))))) + (if (and (pair? def) + (eq? (car def) 'vector-ref) + (eq? (cadr def) '*defs-referenced*)) + `(set-export-from-def-vector table ',key ,(caddr def)) + `(set-export-from-def table ',key ,def))) + `(setf (table-entry table ',key) ,(dump-object val))) + code)) + (module-export-table module)) + (add-dump-init `(setf (module-export-table ,mod-exp) + (let ((table (make-table))) ,@code table)))) + ;; Dump the instances. + (add-dump-init `(setf (module-instance-defs ,mod-exp) + ,(dump-object (module-instance-defs module)))) + (add-dump-init `(setf (module-default ,mod-exp) + ,(dump-object (module-default module)))) + (add-dump-init `(setf (module-uses-standard-prelude? ,mod-exp) + ,(dump-object + (module-uses-standard-prelude? module)))) + )) + +(define (make-fixity a p) + (make fixity (associativity a) (precedence p))) + + +;;;================================================================== +;;; Dump structure traversal +;;;================================================================== + +;;; This is the general object dumper. It recognizes the basic Lisp +;;; objects and dumps them. Given an object, this generates lisp code +;;; to recreate the object at load time. + +(define (dump-object x) + (cond ((struct? x) + (dump x)) + ((or (symbol? x) (null? x)) + ;; Symbols and lists must be quoted. + `',x) + ((or (number? x) + (eq? x '#t) + (eq? x '#f) + (string? x) ; This makes dumped strings immutable. + (char? x)) + ;; These objects are self-evaluating. + x) + ((list? x) + ;; True lists + `(list ,@(map (function dump-object) x))) + ((pair? x) + `(cons ,(dump-object (car x)) + ,(dump-object (cdr x)))) + ((vector? x) + `(vector ,@(map (function dump-object) (vector->list x)))) + ((table? x) + `(list->table ,@(dump-object (table->list x)))) + (else + (error "Don't know how to dump ~A." x)))) + + +;;; *** Should install the walker in the type descriptor. + +(define-walker dump) + +(define (dump x) + (call-walker dump x)) + + + +;;;================================================================== +;;; Dumpers for defs +;;;================================================================== + + +;;; All walkers for def structures should call this macro. The body +;;; is invoked only if the def belongs to the current compilation unit +;;; and hasn't already been traversed. Within the body, the +;;; variable "v" is bound to a form that will evaluate to the +;;; corresponding def structure at run time. This is also +;;; the return value from the macro. + +(define-local-syntax (with-new-def (v d stat-var) . body) + (let ((temp (gensym)) + (expvar (gensym))) + `(let ((,temp ,d) + (,expvar '#f)) + (if (not (def-dump-code ,temp)) + (begin + (cond ((not (def-core? ,temp)) + (setf ,expvar + (list 'vector-ref + '*defs-referenced* + (dynamic *dump-def-counter*))) + (incf (dynamic *dump-def-counter*)) + (push ,temp *dump-defs*)) + (else + (setf ,expvar + (make-core-symbol-name + (def->core-name-string ,temp))))) + (setf (def-dump-code ,temp) ,expvar) + (when (eq? (def-unit ,temp) *unit*) + (incf (dynamic ,stat-var)) + (let ((,v ,expvar)) + ,@body)) + ,expvar) + (def-dump-code ,temp))))) + + +;;; This macro is used to save the value of a structure slot in the +;;; initforms of the dump. + +(define-local-syntax (dump-def-slots obj-var type dexp slots) + `(add-dump-init + (list 'update-slots ',type ,dexp + ,@(map (lambda (s) + `(list ',s + (dump-object (struct-slot ',type ',s ,obj-var)))) + slots))) + ) + + + +(define-walker-method dump var (var) + (dump-var/n var)) + +(define (dump-var/n var) + (with-new-def (dexp var *number-vars-dumped*) + (do-dump-var dexp var '#f))) + +(define (do-dump-var dexp var method-var?) + (let ((code '()) + (exported? (def-exported? var)) + (toplevel? (var-toplevel? var)) + (type (var-type var)) + (simple? (var-simple? var)) + (strict? (var-strict? var)) + (arity (var-arity var)) + (strictness (var-strictness var)) + (opt-entry (var-optimized-entry var)) + (complexity (var-complexity var)) + (fixity (var-fixity var)) + (value (var-value var)) + (inline-value (var-inline-value var)) + (sel? (var-selector-fn? var))) + ;; Some slots are useless for vars that don't name functions. + (if (eqv? arity 0) + (push `(init-variable-slots var + ',exported? + ',toplevel? + ,(dump-object type) + ',simple? + ',strict?) + code) + (push `(init-function-slots var + ',exported? + ',toplevel? + ,(dump-object type) + ',simple? + ',strict? + ',arity + ,(dump-strictness strictness) + ',opt-entry) + code)) + ;; These slots rarely need to be tweaked from the default. + (when sel? + (push `(setf (var-selector-fn? var) '#t) code)) + (when complexity + (push `(setf (var-complexity var) ,complexity) code)) + (when fixity + (push `(setf (var-fixity var) ,(dump-object fixity)) code)) + ;; Save values of simple variables to permit inlining. + ;; Save values of structured constants to permit folding of flic-sel + ;; operations -- this is necessary to optimize dictionary lookups. + (when (or simple? sel? + (and value + (is-type? 'flic-app value) + (structured-constant-app? + (flic-app-fn value) (flic-app-args value)))) + (push `(setf (var-value var) ,(dump-flic-top value)) code)) + (when inline-value + (push `(setf (var-inline-value var) ,(dump-flic-top inline-value)) code)) + ;; Save extra stuff for method vars + (when method-var? + (push `(init-method-var-slots var + ,(dump-object (method-var-class var)) + ,(dump-object (method-var-default var)) + ,(dump-object (method-var-method-signature var))) + code)) + ;; Push the whole mess onto the init code. + (add-dump-init `(let ((var ,dexp)) ,@(nreverse code))))) + + +(define-walker-method dump method-var (var) + (dump-method-var/n var)) + +(define (dump-method-var/n var) + (with-new-def (dexp var *number-vars-dumped*) + (do-dump-var dexp var '#t))) + +(define-walker-method dump con (con) + (dump-con/n con)) + +(define (dump-con/n con) + (with-new-def (dexp con *number-types-dumped*) + (add-dump-init + `(let ((con (init-constructor-slots + ,dexp + ,(con-arity con) + ,(dump-object (con-types con)) + ,(dump-object (con-signature con)) + ,(con-tag con) + ,(dump-object (con-alg con)) + ,(dump-object (con-fixity con)) + ',(con-infix? con)))) + ,@(if (memq '#t (con-slot-strict? con)) + `((setf (con-slot-strict? con) ',(con-slot-strict? con))) + '()) + ,@(if (eq? (con-lisp-fns con) '()) + '() + `((setf (con-lisp-fns con) ',(con-lisp-fns con)))) + con)))) + +;;; *** Could define similar init functions for other defs instead +;;; *** of setting slots inline, but I'm lazy and they don't show up +;;; *** nearly as often as the others. + +(define-walker-method dump algdata (alg) + (dump-algdata/n alg)) + +(define (dump-algdata/n alg) + (with-new-def (dexp alg *number-types-dumped*) + (dump-def-slots alg algdata dexp + (arity n-constr constrs context tyvars signature + enum? tuple? real-tuple? implemented-by-lisp?)))) + +(define-walker-method dump synonym (syn) + (dump-synonym/n syn)) + +(define (dump-synonym/n syn) + (with-new-def (dexp syn *number-types-dumped*) + (dump-def-slots syn synonym dexp (arity args body)))) + +(define-walker-method dump class (class) + (dump-class/n class)) + +(define (dump-class/n class) + (with-new-def (dexp class *number-classes-dumped*) + (dump-def-slots class class dexp + (super super* tyvar method-vars selectors kind + n-methods dict-size)))) + + +;;;================================================================== +;;; Dumpers for non-def AST structs +;;;================================================================== + +;;; This section contains dumpers to handle type-related structs that +;;; are referenced by the various def guys. + + +(define-walker-method dump instance (o) + (if (not (instance-ok? o)) + (error "Attempt to dump instance that's not ok!")) + `(make-new-instance + ,(dump-object (instance-algdata o)) + ,(dump-object (instance-tyvars o)) + ,(dump-object (instance-class o)) + ,(dump-object (instance-context o)) + ,(dump-object (instance-gcontext o)) + ,(dump-object (instance-dictionary o)) + ,(dump-object (instance-methods o)))) + + + +(define-walker-method dump gtype (o) + (dump-gtype/cse o)) + +(define-walker-method dump fixity (o) + `(**fixity ',(fixity-associativity o) ',(fixity-precedence o))) + +(define-walker-method dump tyvar (o) + `(**tyvar ',(tyvar-name o))) + +(define-walker-method dump class-ref (o) + `(**class/def ,(dump-object (class-ref-class o)))) + +(define-walker-method dump context (o) + `(**context ,(dump-object (context-class o)) + ,(dump-object (context-tyvar o)))) + +(define-walker-method dump tycon (o) + `(**tycon/def ,(dump-object (tycon-def o)) + ,(dump-object (tycon-args o)))) + +(define-walker-method dump default-decl (o) + `(make default-decl (types ,(dump-object (default-decl-types o))))) + +(define-walker-method dump signature (o) + `(make signature (context ,(dump-object (signature-context o))) + (type ,(dump-object (signature-type o))))) + +;;; All ntyvars should be instantiated at this point + +; (define-walker-method dump ntyvar (o) +; (dump-object (prune o))) diff --git a/csys/dump-macros.scm b/csys/dump-macros.scm new file mode 100644 index 0000000..404adf8 --- /dev/null +++ b/csys/dump-macros.scm @@ -0,0 +1,37 @@ +(define-syntax (set-strictness-vars) + (let ((res '())) + (dotimes (i *pre-defined-strictness-vars*) + (push `(setf (dynamic ,(vector-ref *pre-defined-strictness-names* i)) + (vector-ref *pre-defined-strictness-table* ',i)) + res)) + `(begin ,@res))) + +(define-syntax (setup-gtyvar-vars) + (let ((res '())) + (dotimes (i *num-saved-gtyvars*) + (push `(setf (dynamic ,(vector-ref *saved-gtyvar-varnames* i)) + (vector-ref *saved-gtyvars* ',i)) + res)) + `(begin ,@res))) + +(define-syntax (assq/insert x table) + `(let ((res (assq ,x ,table))) + (if (eqv? res '#f) + (begin + (let ((new-pair (cons ,x '#f))) + (push new-pair ,table) + new-pair)) + res))) + +(define-syntax (assq/insert-l x table) + `(let ((res (assq ,x ,table))) + (if (eqv? res '#f) + (begin + (let ((new-pair (cons ,x '()))) + (push new-pair ,table) + new-pair)) + res))) + + + + diff --git a/csys/dump-params.scm b/csys/dump-params.scm new file mode 100644 index 0000000..cabbfd0 --- /dev/null +++ b/csys/dump-params.scm @@ -0,0 +1,18 @@ +(define *num-saved-gtyvars* 19) +(define *pre-defined-strictness-size* 7) ; length of max strictness list +(define *pre-defined-strictness-table* '()) +(define *pre-defined-strictness-vars* 32) ; number of global vars +(define *pre-defined-strictness-names* + (make-vector *pre-defined-strictness-vars*)) + +(dotimes (i *pre-defined-strictness-vars*) + (setf (vector-ref *pre-defined-strictness-names* i) + (string->symbol (format '#f "SAVED-STRICTNESS-~A" i)))) + +(define *saved-gtyvars* '()) +(define *saved-gtyvar-varnames* (make-vector *num-saved-gtyvars*)) +(dotimes (i *num-saved-gtyvars*) + (setf (vector-ref *saved-gtyvar-varnames* i) + (string->symbol (format '#f "SAVED-GTYVAR-NAME~A" i)))) + + diff --git a/csys/magic.scm b/csys/magic.scm new file mode 100644 index 0000000..999e8b0 --- /dev/null +++ b/csys/magic.scm @@ -0,0 +1,10 @@ +;;; magic.scm -- magic support file for dumping compiled code files. +;;; +;;; author : Sandra Loosemore +;;; date : 8 Jul 1992 +;;; +;;; This file is used to dump compiled code files. The macro call below +;;; expands into the code being dumped. See dump-interface.scm for more +;;; details. + +(magic-form-to-compile) diff --git a/depend/README b/depend/README new file mode 100644 index 0000000..0e73262 --- /dev/null +++ b/depend/README @@ -0,0 +1,3 @@ +This directory contains the dependency analysis phase. Its function +is to sort out local variable bindings into sequential and recursive +groups. diff --git a/depend/depend.scm b/depend/depend.scm new file mode 100644 index 0000000..0f42ca9 --- /dev/null +++ b/depend/depend.scm @@ -0,0 +1,13 @@ +;;; depend.scm -- module definition for dependency analysis +;;; +;;; author : John +;;; date : 24 Mar 1992 +;;; + + +(define-compilation-unit depend + (source-filename "$Y2/depend/") + (require ast haskell-utils) + (unit dependency-analysis + (source-filename "dependency-analysis.scm"))) + \ No newline at end of file diff --git a/depend/dependency-analysis.scm b/depend/dependency-analysis.scm new file mode 100644 index 0000000..c8d259a --- /dev/null +++ b/depend/dependency-analysis.scm @@ -0,0 +1,151 @@ +;;; depend/depend.scm Author: John + +;;; This performs dependency analysis. All module definitions are gathered +;;; into a single nested let/let*. + +(define-walker depend ast-td-depend-walker) + +;;; This extracts the declarations out of the top level of the modules and +;;; creates a single let defining all values from the modules. + +(define (do-dependency-analysis modules) + (let ((all-decls '())) + (dolist (mod modules) + (setf all-decls (append (module-decls mod) all-decls))) + (analyze-dependency-top + (**let all-decls (make void))))) + + +(define *depend-fn-table* (make-table)) + +(define-syntax (var-depend-fn var) + `(table-entry *depend-fn-table* ,var)) + +(define (analyze-dependency-top x) + (dynamic-let ((*depend-fn-table* (make-table))) + (analyze-dependency x))) + + +;;; This is the entry point to dependency analysis for an expression or decl + +(define (analyze-dependency x) + (call-walker depend x)) + +(define (analyze-dependency/list l) + (dolist (x l) + (analyze-dependency x))) + +;;; This makes default walkers for dependency analysis. Expressions are +;;; walked into; declaration lists must be sorted. + +(define-local-syntax (make-depend-code slot type) + (let ((stype (sd-type slot)) + (sname (sd-name slot)) + (depend-exp-types '(exp alt qual single-fun-def guarded-rhs))) + (cond ((and (symbol? stype) + (memq stype depend-exp-types)) + `(analyze-dependency (struct-slot ',type ',sname object))) + ((and (pair? stype) + (eq? (car stype) 'list) + (symbol? (cadr stype)) + (memq (cadr stype) depend-exp-types) + `(analyze-dependency/list + (struct-slot ',type ',sname object)))) + ((equal? stype '(list decl)) + `(setf (struct-slot ',type ',sname object) + (restructure-decl-list (struct-slot ',type ',sname object)))) + (else +; (format '#t "Depend: skipping slot ~A in ~A~%" +; (sd-name slot) +; type) + '#f)))) + +(define-modify-walker-methods depend + (lambda let if case alt exp-sign app con-ref + integer-const float-const char-const string-const + list-exp sequence sequence-then sequence-to sequence-then-to + list-comp section-l section-r qual-generator qual-filter omitted-guard + con-number sel is-constructor cast void + single-fun-def guarded-rhs + case-block return-from and-exp + ) + (object) + make-depend-code) + +;;; This sorts a list of decls. Recursive groups are placed in +;;; special structures: recursive-decl-group + +(define (restructure-decl-list decls) + (let ((stack '()) + (now 0) + (sorted-decls '()) + (edge-fn '())) + (letrec ((visit (lambda (k) + (let ((minval 0) + (recursive? '#f) + (old-edge-fn edge-fn)) + (incf now) +; (format '#t "Visiting ~A: id = ~A~%" (valdef-lhs k) now) + (setf (valdef-depend-val k) now) + (setf minval now) + (push k stack) + (setf edge-fn + (lambda (tv) +; (format '#t "Edge ~A -> ~A~%" (valdef-lhs k) +; (valdef-lhs tv)) + (let ((val (valdef-depend-val tv))) + (cond ((eq? tv k) + (setf recursive? '#t)) + ((eqv? val 0) + (setf minval (min minval + (funcall visit tv)))) + (else + (setf minval (min minval val)))) +; (format '#t "Min for ~A is ~A~%" +; (valdef-lhs k) minval) + ))) + (analyze-dependency/list (valdef-definitions k)) + (setf edge-fn old-edge-fn) + (when (eqv? minval (valdef-depend-val k)) + (let ((defs '())) + (do ((quit? '#f)) (quit?) + (push (car stack) defs) + (setf (valdef-depend-val (car stack)) 100000) + (setf quit? (eq? (car stack) k)) + (setf stack (cdr stack))) +; (format '#t "Popping stack: ~A~%" +; (map (lambda (x) (valdef-lhs x)) defs)) + (if (and (null? (cdr defs)) + (not recursive?)) + (push k sorted-decls) + (push (make recursive-decl-group (decls defs)) + sorted-decls)))) + minval)))) + ;; for now assume all decl lists have only valdefs + (dolist (d decls) + (let ((decl d)) ; to force new binding for each closure + (setf (valdef-depend-val decl) 0) + (dolist (var (collect-pattern-vars (valdef-lhs decl))) + (setf (var-depend-fn (var-ref-var var)) + (lambda () (funcall edge-fn decl)))))) + (dolist (decl decls) + (when (eqv? (valdef-depend-val decl) 0) + (funcall visit decl))) + (dolist (decl decls) + (dolist (var (collect-pattern-vars (valdef-lhs decl))) + (setf (var-depend-fn (var-ref-var var)) '#f))) + (nreverse sorted-decls)))) + +;;; This is the only non-default walker needed. When a reference to a +;;; variable is encountered, the sort algorithm above is notified. + +(define-walker-method depend var-ref (object) + (let ((fn (var-depend-fn (var-ref-var object)))) + (when (not (eq? fn '#f)) + (funcall fn)))) + +(define-walker-method depend overloaded-var-ref (object) + (let ((fn (var-depend-fn (overloaded-var-ref-var object)))) + (when (not (eq? fn '#f)) + (funcall fn)))) + diff --git a/derived/README b/derived/README new file mode 100644 index 0000000..9b461ac --- /dev/null +++ b/derived/README @@ -0,0 +1,2 @@ +This directory contains code to generate AST structure for derived +instances. diff --git a/derived/ast-builders.scm b/derived/ast-builders.scm new file mode 100644 index 0000000..3a16ee6 --- /dev/null +++ b/derived/ast-builders.scm @@ -0,0 +1,273 @@ +;;; These functions build non-trivial ast structure. + +;;; Prelude functions: booleans + +(define (**== e1 e2) + (**app (**var/def (core-symbol "==")) e1 e2)) + +(define (**<= e1 e2) + (**app (**var/def (core-symbol "<=")) e1 e2)) + +(define (**< e1 e2) + (**app (**var/def (core-symbol "<")) e1 e2)) + +(define (**> e1 e2) + (**app (**var/def (core-symbol ">")) e1 e2)) + +(define (**and e1 e2) + (**app (**var/def (core-symbol "&&")) e1 e2)) + +(define (**or e1 e2) + (**app (**var/def (core-symbol "||")) e1 e2)) + +(define (**true) (**con/def (core-symbol "True"))) + +(define (**false) (**con/def (core-symbol "False"))) + +;; Tuples + +(define (**tuple2 x y) + (**app (**con/def (tuple-constructor 2)) x y)) + +(define (**tupleN exps) + (**app/l (**con/def (tuple-constructor (length exps))) exps)) + +;; Arithmetic + +(define (**+ x y) + (**app (**var/def (core-symbol "+")) x y)) + +(define (**+/Int x y) + (**app (**var/def (core-symbol "primPlusInt")) x y)) + +(define (**- x y) + (**app (**var/def (core-symbol "-")) x y)) + +(define (**1+ x) + (**+ x (**int 1))) + +;; Lists + +(define (**cons x y) + (**app (**con/def (core-symbol ":")) x y)) + +(define (**null) + (**con/def (core-symbol "Nil"))) + +(define (**list . args) + (**list/l args)) + +(define (**list/l args) + (if (null? args) + (**null) + (**cons (car args) + (**list/l (cdr args))))) + +(define (**list/pattern pats) + (if (null? pats) + (**pcon/def (core-symbol "Nil") '()) + (**pcon/def (core-symbol ":") + (list (car pats) (**list/pattern (cdr pats)))))) + +(define (**append . lists) + (**append/l lists)) + +(define (**append/l lists) + (if (null? (cdr lists)) + (car lists) + (**app (**var/def (core-symbol "++")) + (car lists) + (**append/l (cdr lists))))) + +(define (**take n l) + (**app (**var/def (core-symbol "take")) n l)) + +(define (**drop n l) + (**app (**var/def (core-symbol "drop")) n l)) + +;; Functionals + +(define (**dot fn . args) + (**dot/l fn args)) + +(define (**dot/l fn args) + (if (null? args) + fn + (**app (**var/def (core-symbol ".")) fn (**dot/l (car args) (cdr args))))) + +;; Printing + +(define (**showChar x) + (**app (**var/def (core-symbol "showChar")) x)) + +(define (**space) + (**showChar (**char #\ ))) + +(define (**comma) + (**showChar (**char #\,))) + +(define (**showsPrec x y) + (**app (**var/def (core-symbol "showsPrec")) x y)) + +(define (**shows x) + (**app (**var/def (core-symbol "shows")) x)) + +(define (**showString x) + (**app (**var/def (core-symbol "showString")) x)) + +(define (**showParen x y) + (**app (**var/def (core-symbol "showParen")) x y)) + +;; Reading + +(define (**readsPrec x y) + (**app (**var/def (core-symbol "readsPrec")) x y)) + +(define (**lex x) + (**app (**var/def (core-symbol "lex")) x)) + +(define (**readParen bool fn r) + (**app (**var/def (core-symbol "readParen")) bool fn r)) + +(define (**reads s) + (**app (**var/def (core-symbol "reads")) s)) + +;;; Binary + +(define (**showBinInt i b) + (**app (**var/def (core-symbol "primShowBinInt")) i b)) + +(define (**readBinSmallInt max b) + (**app (**var/def (core-symbol "primReadBinSmallInt")) max b)) + +(define (**showBin x b) + (**app (**var/def (core-symbol "showBin")) x b)) + +(define (**readBin b) + (**app (**var/def (core-symbol "readBin")) b)) + +;;; Some higher level code generators + +;;; foldr (expanded inline) + +(define (**foldr build-fn terms init) + (if (null? terms) + init + (funcall build-fn (car terms) (**foldr build-fn (cdr terms) init)))) + +;;; Unlike foldr, this uses two sets of args to avoid tupling + +(define (**foldr2 build-fn terms1 terms2 init-fn) + (if (null? (cdr terms1)) + (funcall init-fn (car terms1) (car terms2)) + (funcall build-fn (car terms1) (car terms2) + (**foldr2 build-fn (cdr terms1) (cdr terms2) init-fn)))) + +;;; Enum + +(define (**enumFrom x) + (**app (**var/def (core-symbol "enumFrom")) x)) + +(define (**enumFromThen from then) + (**app (**var/def (core-symbol "enumFromThen")) from then)) + +(define (**enumFromTo from to) + (**app (**var/def (core-symbol "enumFromTo")) from to)) + +(define (**enumFromThenTo from then to) + (**app (**var/def (core-symbol "enumFromThenTo")) from then to)) + +;;; Cast overrides the type system + +(define (**cast x) + (make cast (exp x))) + +;;; Case. This also generates the alts. All variants of case generate +;;; an arm for each constructor in a datatype. This arm can be selected +;;; by pattern matching a value of the type, with all fields bound to vars, +;;; or with numbered or named selections. + +;;; The fn always generates the arms given the constructor. In the /con case, +;;; the fn also gets the variable list of values bound in the fields. + +(define (**case/con alg exp fn) + (**case exp + (map (lambda (con) + (let* ((arity (con-arity con)) + (vars (temp-vars "x" arity))) + (**alt/simple (**pat (cons con vars)) + (funcall fn con vars)))) + (algdata-constrs alg)))) + +;;; Selectors are integers (used for Bin) + +(define (**case/int alg exp fn) + (**case exp + (map (lambda (con) + (**alt/simple + (**pat (con-tag con)) + (funcall fn con))) + (algdata-constrs alg)))) + +;;; Selectors are strings (Text) + +(define (**case/strings alg exp fn) + (**case exp + (map (lambda (con) + (**alt/simple + (**pat (remove-con-prefix (symbol->string (def-name con)))) + (funcall fn con))) + (algdata-constrs alg)))) + +;;; Definitions containing multi-body + +(define (**multi-define fname alg nullary-fn single-fn + combine-fn else-val) + (**define/multiple fname + (append + (map (lambda (con) (**define/2 con nullary-fn single-fn combine-fn)) + (algdata-constrs alg)) + (if (not (eq? else-val '#f)) + `(((_ _) ,(funcall else-val))) + '())))) + +(define (**define/2 con nullary-fn single-fn combine-fn) + (let* ((arity (con-arity con)) + (vars1 (temp-vars "l" arity)) + (vars2 (temp-vars "r" arity))) + `(((,con ,@vars1) (,con ,@vars2)) + ,(if (eqv? arity 0) + (funcall nullary-fn) + (**foldr2 combine-fn (suspend-vars vars1) (suspend-vars vars2) + single-fn))))) + +(define (**define/multiple fn args) + (make valdef + (lhs (**pat fn)) + (definitions + (map (lambda (arg) + (make single-fun-def + (args (map (function **pat) (car arg))) + (rhs-list (list (make guarded-rhs + (guard (**omitted-guard)) + (rhs (cadr arg))))) + (where-decls '()) + (infix? '#f))) + args)))) + +(define (suspend-vars vars) (map (lambda (v) (lambda () (**var v))) vars)) + +(define (temp-vars root arity) + (temp-vars1 root 1 arity)) + +(define (temp-vars1 root i arity) + (if (> i arity) + '() + (cons (string->symbol (string-append root (number->string i))) + (temp-vars1 root (1+ i) arity)))) + +(define (tuple-con algdata) + (car (algdata-constrs algdata))) + +(define (con-string x) + (remove-con-prefix (symbol->string (def-name x)))) diff --git a/derived/derived-instances.scm b/derived/derived-instances.scm new file mode 100644 index 0000000..2c65084 --- /dev/null +++ b/derived/derived-instances.scm @@ -0,0 +1,255 @@ + +;;; Basic DI structure: +;;; a. Create the set of instances +;;; b. Expand the context of each potential instance. +;;; c. Once b. reaches a fixpoint, fill in the ast for the generated instances + +(define *di-context-changed* '#f) + +(define (add-derived-instances modules) + (let ((insts '())) + (walk-modules modules + (lambda () (setf insts (append (find-derivable-instances) insts)))) + (compute-di-fixpoint insts) + (dolist (inst insts) + (when (instance-ok? inst) + (create-instance-fns inst) + (push inst (module-instance-defs + (table-entry *modules* + (def-module (instance-algdata inst))))))))) + +(define (compute-di-fixpoint insts) + (setf *di-context-changed* '#f) + (dolist (inst insts) + (propagate-di-context inst)) + (when *di-context-changed* (compute-di-fixpoint insts))) + +;;; Create instance decls for all derived instances in a module. Filter +;;; out underivable instances (Ix & Enum only) + +(define (find-derivable-instances) + (let ((algs (module-alg-defs *module*)) + (insts '())) + (dolist (alg algs) + (dolist (class (algdata-deriving alg)) + (cond ((memq class (list (core-symbol "Eq") + (core-symbol "Ord") + (core-symbol "Text") + (core-symbol "Binary"))) + (setf insts (add-derivable-instance insts alg class '#f))) + ((eq? class *printer-class*) + (setf insts (add-derivable-instance + insts alg (core-symbol "Text") '#t))) + ((eq? class (core-symbol "Ix")) + (if (or (algdata-enum? alg) + (algdata-tuple? alg)) + (setf insts (add-derivable-instance insts alg class '#f)) + (signal-cant-derive-ix alg))) + ((eq? class (core-symbol "Enum")) + (if (algdata-enum? alg) + (setf insts (add-derivable-instance insts alg class '#f)) + (signal-cant-derive-enum alg))) + (else + (signal-not-derivable class))))) + insts)) + + +(define (signal-cant-derive-ix alg) + (phase-error 'cant-derive-IX + "An Ix instance for ~A cannot be derived. It is not an enumeration~%~ + or single-constructor datatype." + alg)) + +(define (signal-cant-derive-enum alg) + (phase-error 'cant-derive-Enum + "An Enum instance for ~A cannot be derived. It is not an enumeration." + alg)) + +(define (signal-not-derivable class) + (recoverable-error 'not-derivable + "Class ~A is not one of the classes that permits derived instances." + class)) + + +;; This adds a provisional instance template. Of course, there may already +;;; be an instance (error!) + +(define (add-derivable-instance insts alg cls sp) + (let ((existing-inst (lookup-instance alg cls))) + (cond ((eq? existing-inst '#f) + (let ((inst (new-instance cls alg (algdata-tyvars alg)))) + (setf (instance-context inst) (algdata-context alg)) + (setf (instance-decls inst) '()) + (setf (instance-ok? inst) '#t) + (setf (instance-suppress-readers? inst) sp) + (cons inst insts))) + (else + (signal-instance-exists alg cls) + insts)))) + +(define (signal-instance-exists alg cls) + (recoverable-error 'instance-exists + "An instance for type ~A in class ~A already exists;~%~ + the deriving clause is being ignored." + alg cls)) + +;;; This updates all instance contexts for an algdata. Each derivable +;;; instance generates a recursive context for every field. If a +;;; component cannot satisfy the desired context, the ok? field is set to +;;; #f to mark the instance as bogus. + +(define (propagate-di-context inst) + (when (instance-ok? inst) + (propagate-constructor-contexts inst + (algdata-constrs (instance-algdata inst))))) + +;;; These two functions propagate the context to ever field of every +;;; constructor + +(define (propagate-constructor-contexts inst constrs) + (or (null? constrs) + (and (propagate-contexts inst (instance-class inst) + (con-types (car constrs))) + (propagate-constructor-contexts inst (cdr constrs))))) + +(define (propagate-contexts inst class types) + (or (null? types) + (and (propagate-type-context inst class (car types)) + (propagate-contexts inst class (cdr types))))) + +;;; This propagates a context out to a given type. The type can only contain +;;; the tyvars which are args to the algdata. + +(define (propagate-type-context inst class type) + (cond ((tyvar? type) + (cond ((single-ast-context-implies? + (instance-context inst) class (tyvar-name type)) + '#t) + (else + (setf *di-context-changed* '#t) + (setf (instance-context inst) + (augment-context (instance-context inst) class + (tyvar-name type))) + '#t))) + ((synonym? (tycon-def type)) + (propagate-type-context inst class (expand-synonym type))) + (else + (let* ((algdata (tycon-def type)) ; must be a algdata + (args (tycon-args type)) + (new-inst (lookup-instance algdata class))) + (cond ((or (eq? new-inst '#f) + (not (instance-ok? new-inst))) + (signal-cannot-derive-instance + (instance-class inst) (instance-algdata inst)) + (setf (instance-ok? inst) '#f) + (setf *di-context-changed* '#t) + '#f) + (else + (propagate-instance-contexts inst + (instance-context new-inst) + (instance-tyvars new-inst) + args))))))) + + +(define (single-ast-context-implies? ast-context class tyvar) + (cond ((null? ast-context) + '#f) + ((eq? tyvar (context-tyvar (car ast-context))) + (let ((class1 (class-ref-class (context-class (car ast-context))))) + (or (eq? class1 class) + (memq class (class-super* class1)) + (single-ast-context-implies? (cdr ast-context) class tyvar)))) + (else + (single-ast-context-implies? (cdr ast-context) class tyvar)))) + +;;; *** This message makes no sense to me. What is the problem that +;;; *** makes it impossible to derive the instance? + +(define (signal-cannot-derive-instance class alg) + (phase-error 'cannot-derive-instance + "Instance ~A(~A) cannot be derived." + class alg)) + + +;;; This propagates contexts into structure components. The context +;;; changes due to the context associated with the various instance +;;; decls encountered. + +;;; Here's the plan for expanding Cls(Alg t1 t2 .. tn) using +;;; instance (Cls1(vx),Cls2(vy),...) => Cls(Alg(v1 v2 .. vn)) +;;; for each Clsx in the instance context, propagate Clsx to the +;;; ti corresponding to vx, where vx must be in the set vi. + +(define (propagate-instance-contexts inst contexts tyvars args) + (or (null? contexts) + (and (propagate-type-context inst + (class-ref-class (context-class (car contexts))) + (find-corresponding-tyvar + (context-tyvar (car contexts)) tyvars args)) + (propagate-instance-contexts inst (cdr contexts) tyvars args)))) + +;;; Given the t(i) and the v(i), return the t corresponding to a v. + +(define (find-corresponding-tyvar tyvar tyvars args) + (if (eq? tyvar (car tyvars)) + (car args) + (find-corresponding-tyvar tyvar (cdr tyvars) (cdr args)))) + +;;; 1 level type synonym expansion + +(define (expand-synonym type) + (let* ((synonym (tycon-def type)) + (args (synonym-args synonym)) + (body (synonym-body synonym))) + (let ((alist (map (lambda (tyvar arg) (tuple tyvar arg)) + args (tycon-args type)))) + (copy-synonym-body body alist)))) + +(define (copy-synonym-body type alist) + (if (tyvar? type) + (tuple-2-2 (assq (tyvar-name type) alist)) + (make tycon (def (tycon-def type)) + (name (tycon-name type)) + (args (map (lambda (ty) + (copy-synonym-body ty alist)) + (tycon-args type)))))) + +;;; This fills in the body decls for an instance function. + +(define (create-instance-fns inst) + (let ((class (instance-class inst)) + (alg (instance-algdata inst))) + (cond ((eq? class (core-symbol "Eq")) + (add-instance inst (eq-fns alg))) + ((eq? class (core-symbol "Ord")) + (add-instance inst (ord-fns alg))) + ((eq? class (core-symbol "Ix")) + (add-instance inst (ix-fns alg))) + ((eq? class (core-symbol "Enum")) + (add-instance inst (enum-fns alg))) + ((eq? class (core-symbol "Text")) + (add-instance inst (text-fns alg (instance-suppress-readers? inst)))) + ((eq? class (core-symbol "Binary")) + (add-instance inst (binary-fns alg)))))) + +(define (add-instance inst decls) + (setf (instance-decls inst) decls)) + +;;; Add class(var) to a context, removing any contexts made redundant by +;;; the new addition. Example: adding Ord a to (Eq a, Eq b) would yield +;;; (Ord a,Eq b). + +(define (augment-context contexts cl var) + (cons (**context (**class/def cl) var) + (remove-implied-contexts cl var contexts))) + +(define (remove-implied-contexts class1 tyvar1 contexts) + (if (null? contexts) + '#f + (with-slots context (class tyvar) (car contexts) + (let ((rest (remove-implied-contexts class1 tyvar1 (cdr contexts))) + (class2 (class-ref-class class))) + (if (and (eq? tyvar1 tyvar) + (memq class2 (class-super* class1))) + rest + (cons (car contexts) rest)))))) diff --git a/derived/derived.scm b/derived/derived.scm new file mode 100644 index 0000000..975dab6 --- /dev/null +++ b/derived/derived.scm @@ -0,0 +1,21 @@ +;;; -- compilation unit definition for derived instances +;;; +;;; author : John +;;; + +(define-compilation-unit derived + (source-filename "$Y2/derived/") + (require global) + (unit derived-instances + (source-filename "derived-instances.scm")) + (unit ast-builders + (source-filename "ast-builders")) + (unit eq-ord + (source-filename "eq-ord")) + (unit ix-enum + (source-filename "ix-enum")) + (unit text-binary + (source-filename "text-binary")) + ) + + diff --git a/derived/eq-ord.scm b/derived/eq-ord.scm new file mode 100644 index 0000000..b005b58 --- /dev/null +++ b/derived/eq-ord.scm @@ -0,0 +1,69 @@ +;;; ---------------------------------------------------------------- +;;; Eq +;;; ---------------------------------------------------------------- + +(define (Eq-fns algdata) + (list + (cond ((algdata-enum? algdata) + (**define '== '(|x| |y|) + (**== (**con-number (**var '|x|) algdata) + (**con-number (**var '|y|) algdata)))) + (else + (**multi-define '== algdata + ;; For nullary constructors + (function **true) + ;; For unary constructors + (lambda (v1 v2) + (**== (funcall v1) (funcall v2))) + ;; For n-ary constructors + (lambda (v1 v2 bool) + (**and (**== (funcall v1) (funcall v2)) bool)) + ;; The else clause in case the constructors do + ;; not match. + (if (algdata-tuple? algdata) + '#f + (function **false))))))) + +;;; ---------------------------------------------------------------- +;;; Ord +;;; ---------------------------------------------------------------- + +(define (Ord-fns algdata) + (list (ord-fn1 algdata '< (function **<)) + (ord-fn1 algdata '<= (function **<=)))) + +(define (Ord-fn1 algdata fn prim) + (cond ((algdata-enum? algdata) + (**define fn '(|x| |y|) + (funcall prim (**con-number (**var '|x|) algdata) + (**con-number (**var '|y|) algdata)))) + ((algdata-tuple? algdata) + (**multi-define fn algdata + (function **false) + (lambda (x y) (funcall prim (funcall x) (funcall y))) + (function combine-eq-<) + '#f)) + (else + (**define fn '(|x| |y|) + (**let + (list + (**multi-define '|inner| algdata + (if (eq? fn '<) (function **false) + (function **true)) + (lambda (x y) + (funcall prim (funcall x) (funcall y))) + (function combine-eq-<) + '#f) + (**define '|cx| '() (**con-number (**var '|x|) algdata)) + (**define '|cy| '() (**con-number (**var '|y|) algdata))) + (**or (**< (**var '|cx|) (**var '|cy|)) + (**and (**== (**var `|cx|) (**var '|cy|)) + (**app (**var '|inner|) + (**var '|x|) + (**var '|y|))))))))) + +(define (combine-eq-< v1 v2 rest) + (**or (**< (funcall v1) (funcall v2)) + (**and (**== (funcall v1) (funcall v2)) + rest))) + diff --git a/derived/ix-enum.scm b/derived/ix-enum.scm new file mode 100644 index 0000000..fb9a282 --- /dev/null +++ b/derived/ix-enum.scm @@ -0,0 +1,116 @@ +;;; ---------------------------------------------------------------- +;;; Ix +;;; ---------------------------------------------------------------- + +(define (ix-fns algdata) + (if (algdata-enum? algdata) + (ix-fns/enum algdata) + (ix-fns/tuple algdata))) + +(define (ix-fns/enum algdata) + (list + (**define '|range| '((tuple |l| |u|)) + (**let + (list + (**define '|cl| '() (**con-number (**var '|l|) algdata)) + (**define '|cu| '() (**con-number (**var '|u|) algdata))) + (**if (**< (**var '|cu|) (**var '|cl|)) + (**null) + (**take (**+ (**- (**var '|cu|) (**var '|cl|)) (**int 1)) + (**drop (**var '|cl|) + (**list/l + (map (function **con/def) + (algdata-constrs algdata)))))))) + (**define '|index| '((tuple |l| |u|) |x|) + (**- (**con-number (**var '|x|) algdata) + (**con-number (**var '|l|) algdata))) + (**define '|inRange| '((tuple |l| |u|) |x|) + (**and (**<= (**con-number (**var '|l|) algdata) + (**con-number (**var '|x|) algdata)) + (**<= (**con-number (**var '|x|) algdata) + (**con-number (**var '|u|) algdata)))))) + +(define (ix-fns/tuple algdata) + (let* ((con (tuple-con algdata)) + (arity (con-arity con)) + (llist (temp-vars "l" arity)) + (ulist (temp-vars "u" arity)) + (ilist (temp-vars "i" arity))) + (list + (**define '|range| `((tuple (,con ,@llist) (,con ,@ulist))) + (**listcomp (**app/l (**con/def con) (map (function **var) ilist)) + (map (lambda (iv lv uv) + (**gen iv + (**app (**var '|range|) + (**tuple2 (**var lv) + (**var uv))))) + ilist llist ulist))) + (**define '|index| `((tuple (,con ,@llist) (,con ,@ulist)) + (,con ,@ilist)) + (index-body (reverse ilist) (reverse llist) (reverse ulist))) + (**define '|inRange| `((tuple (,con ,@llist) (,con ,@ulist)) + (,con ,@ilist)) + (inrange-body ilist llist ulist))))) + +(define (index-body is ls us) + (let ((i1 (**app (**var '|index|) + (**tuple2 (**var (car ls)) (**var (car us))) + (**var (car is))))) + (if (null? (cdr is)) + i1 + (**app (**var '|+|) + i1 (**app (**var '|*|) + (**1+ (**app (**var '|index|) + (**tuple2 (**var (car ls)) + (**var (car us))) + (**var (car us)))) + (index-body (cdr is) (cdr ls) (cdr us))))))) + +(define (inrange-body is ls us) + (let ((i1 (**app (**var '|inRange|) + (**tuple2 (**var (car ls)) (**var (car us))) + (**var (car is))))) + (if (null? (cdr is)) + i1 + (**app (**var/def (core-symbol "&&")) + i1 + (inrange-body (cdr is) (cdr ls) (cdr us)))))) + +;;; ---------------------------------------------------------------- +;;; Enum +;;; ---------------------------------------------------------------- + +; Enum uses the Int methods since Enums are represented as Ints. + +(define (enum-fns algdata) + (list + (**define '|enumFrom| '(|x|) + (**let + (list + (**define '|from'| '(|x'|) + (**if (**> (**var '|x'|) + (**con-number (**con/def (last-con algdata)) algdata)) + (**null) + (**cons (**var '|x'|) + (**app (**var '|from'|) (**1+ (**var '|x'|))))))) + (**cast (**app (**var '|from'|) + (**con-number (**var '|x|) algdata))))) + (**define '|enumFromThen| '(|x| |y|) + (**let + (list + (**define '|step| '() + (**- (**con-number (**var '|y|) algdata) + (**con-number (**var '|x|) algdata))) + (**define '|from'| '(|x'|) + (**if (**or (**> (**var '|x'|) + (**con-number (**con/def (last-con algdata)) algdata)) + (**< (**var '|x'|) (**int 0))) + (**null) + (**cons (**var '|x'|) + (**app (**var '|from'|) + (**+ (**var '|x'|) (**var '|step|))))))) + (**cast (**app (**var '|from'|) (**con-number (**var '|x|) algdata))))))) + +(define (last-con algdata) + (car (reverse (algdata-constrs algdata)))) + diff --git a/derived/text-binary.scm b/derived/text-binary.scm new file mode 100644 index 0000000..1779d1a --- /dev/null +++ b/derived/text-binary.scm @@ -0,0 +1,228 @@ +;;; ---------------------------------------------------------------- +;;; Text +;;; ---------------------------------------------------------------- + +(define (text-fns algdata suppress-reader?) + (let ((print+read + (cond ((algdata-enum? algdata) + (text-enum-fns algdata)) + (else + (text-general-fns algdata))))) + (when suppress-reader? + (setf print+read (list (car print+read)))) + print+read)) + +(define (text-enum-fns algdata) + (list + (**define '|showsPrec| '(|d| |x|) + (**case/con algdata (**var '|x|) + (lambda (con vars) + (declare (ignore vars)) + (**showString (**string (con-string con)))))) + (**define '|readsPrec| '(|d| |str|) + (**listcomp + (**var '|s|) + (list + (**gen '(tuple |tok| |rest|) (**lex (**var '|str|))) + (**gen '|s| + (**case (**var '|tok|) + `(,@(map (lambda (con) + (**alt/simple + (**pat (con-string con)) + (**list (**tuple2 (**con/def con) + (**var '|rest|))))) + (algdata-constrs algdata)) + ,(**alt/simple (**pat '_) (**null)))))))))) + +;;; This has been hacked to split up the read function for large +;;; data types to avoid choking the lisp compiler. + +(define (text-general-fns algdata) + (let ((split-fn-def? (> (algdata-n-constr algdata) 6))) ;; pretty arbitrary! + (list + (**define '|showsPrec| '(|d| |x|) + (**case/con algdata (**var '|x|) + (lambda (con vars) + (if (con-infix? con) + (show-infix con vars) + (show-prefix con vars))))) + (**define '|readsPrec| '(|d| |str|) + (**append/l + (map (lambda (con) + (cond ((con-infix? con) + (read-infix con)) + (else + (read-prefix con split-fn-def?)))) + (algdata-constrs algdata))))))) + +(define (show-infix con vars) + (multiple-value-bind (p lp rp) (get-con-fixity con) + (**showParen + (**< (**Int p) (**var '|d|)) + (**dot (**showsPrec (**int lp) (**var (car vars))) + (**showString + (**string (string-append " " (con-string con) " "))) + (**showsPrec (**int rp) (**var (cadr vars))))))) + +(define (show-prefix con vars) + (**showParen + (**<= (**int 10) (**var '|d|)) + (**dot/l (**showString (**string (con-string con))) + (show-fields vars)))) + +(define (show-fields vars) + (if (null? vars) + '() + `(,(**space) ,(**showsPrec (**int 10) (**var (car vars))) + ,@(show-fields (cdr vars))))) + +(define (read-infix con) + (multiple-value-bind (p lp rp) (get-con-fixity con) + (**let + (list + (**define '|readVal| '(|r|) + (**listcomp + (**tuple2 (**app (**con/def con) (**var '|u|) (**var '|v|)) + (**var '|s2|)) + (list + (**gen '(tuple |u| |s0|) + (**readsPrec (**int lp) (**var '|r|))) + (**gen `(tuple ,(con-string con) |s1|) + (**lex (**var '|s0|))) + (**gen '(tuple |v| |s2|) + (**readsprec (**int rp) (**var '|s1|))))))) + (**readParen (**< (**int p) (**var '|d|)) + (**var '|readVal|) (**var '|str|))))) + +(define (read-prefix con split?) + (let ((res (read-prefix-1 con))) + (if (not split?) + res + (dynamic-let ((*module-name* (def-module con))) + (dynamic-let ((*module* (table-entry *modules* *module-name*))) + (let* ((alg (con-alg con)) + (fn (make-new-var + (string-append (symbol->string (def-name alg)) + "/read-" + (remove-con-prefix + (symbol->string (def-name con)))))) + (new-code (**app (**var/def fn) (**var '|str|) (**var '|d|))) + (def (**define fn '(|str| |d|) res))) + (setf (module-decls *module*) (cons def (module-decls *module*))) + new-code)))))) + +(define (read-prefix-1 con) + (let* ((arity (con-arity con)) + (vars (temp-vars "x" arity)) + (svars (cons '|rest| (temp-vars "s" arity)))) + (**let + (list + (**define '|readVal| '(|r|) + (**listcomp + (**tuple2 (**app/l (**con/def con) (map (function **var) vars)) + (**var (car (reverse svars)))) + (cons + (**gen `(tuple ,(con-string con) |rest|) + (**lex (**var '|r|))) + (read-fields vars svars (cdr svars)))))) + (**readParen (**< (**int 9) (**var '|d|)) + (**var '|readVal|) (**var '|str|))))) + +(define (read-fields vars s0 s1) + (if (null? vars) + '() + (cons + (**gen `(tuple ,(car vars) ,(car s1)) + (**readsprec (**int 10) (**var (car s0)))) + (read-fields (cdr vars) (cdr s0) (cdr s1))))) + + +;;; ---------------------------------------------------------------- +;;; Binary +;;; ---------------------------------------------------------------- + +(define (binary-fns algdata) + (let ((res + (cond ((algdata-enum? algdata) + (binary-enum-fns algdata)) + ((algdata-tuple? algdata) + (binary-tuple-fns algdata)) + (else + (binary-general-fns algdata))))) +; (dolist (x res) +; (fresh-line) +; (pprint x)) + res)) + + +(define (binary-enum-fns algdata) + (list + (**define '|showBin| '(|x| |b|) + (**showBinInt (**con-number (**var '|x|) algdata) (**var '|b|))) + (**define '|readBin| '(|b|) + (**let + (list + (**define '(tuple |n| |b1|) '() + (**readBinSmallInt + (**var '|b|) + (**int (1- (algdata-n-constr algdata)))))) + (**tuple2 + (**case/int algdata (**var '|n|) + (lambda (con) + (**con/def con))) + (**var '|b1|)))))) + +(define (binary-tuple-fns algdata) + (let* ((con (tuple-con algdata)) + (arity (con-arity con)) + (vars (temp-vars "v" arity))) + (list + (**define '|showBin| `((,con ,@vars) |b|) + (show-binary-body vars '|b|)) + (**define '|readBin| '(|b|) + (read-binary-body con))))) + +(define (show-binary-body vars b) + (**foldr (lambda (new-term prev-terms) + (**showBin new-term prev-terms)) + (map (function **var) vars) + (**var b))) + +(define (read-binary-body con) + (let* ((arity (con-arity con)) + (vars (temp-vars "v" arity)) + (bvars (cons '|b| (temp-vars "b" arity)))) + (**let + (map (lambda (v b nb) + (**define `(tuple ,v ,nb) '() + (**readBin (**var b)))) + vars bvars (cdr bvars)) + (**tuple2 + (**app/l (**con/def con) + (map (function **var) vars)) + (**var (car (reverse bvars))))))) + +(define (binary-general-fns algdata) + (list + (**define '|showBin| '(|x| |b|) + (**showBinInt + (**con-number (**var '|x|) algdata) + (**case/con algdata (**var '|x|) + (lambda (con vars) + (declare (ignore con)) + (show-binary-body vars '|b|))))) + (**define '|readBin| '(|bin|) + (**let + (list + (**define '(tuple |i| |b|) '() + (**readBinSmallInt (**var '|bin|) + (**int (1- (algdata-n-constr algdata)))))) + (**case/int algdata (**var '|i|) (function read-binary-body)))))) + +(define (get-con-fixity con) + (let ((fixity (con-fixity con))) + (if (not (eq? fixity '#f)) + (let ((p (fixity-precedence fixity)) + (a (fixity-associativity fixity))) + (values p (if (eq? a 'L) p (1+ p)) (if (eq? a 'R) p (1+ p)))) + (values 9 10 9)))) diff --git a/doc/announcement b/doc/announcement new file mode 100644 index 0000000..8c9e8f1 --- /dev/null +++ b/doc/announcement @@ -0,0 +1,64 @@ + + Announcing the release of Yale Haskell 2.0.5 + +We are releasing the latest version of the Yale Haskell system, +Y2.0.5, in source form. This fixes a number of minor problems in the +2.0.5a release (for Sparc only) and should be relatively bug free. + +Yale Haskell can be built from sources using CMU Common Lisp, Lucid +Common Lisp, Allegro Common Lisp, or Harlequin LispWorks. The system +may also build on akcl but the performance is very poor. + +Compiled versions of the system are available for Sparc systems running +SunOS 4.1.2 and Sparc 10's (sun4m) running 4.1.3. Anyone building a system +using CMU lisp on a different platform should let us know and we will +add more executables to the ftp area. Look at the README for further +information. + +This release features an X window interface. Using CLX, the full +functionality of X windows has been made available at the Haskell +level. There is also a Haskell <-> Lisp interface similar to the C +interface in the Glasgow system. + + +Our system is available for anonymous ftp from the Yale Haskell ftp site: + + Site Host name Raw IP address + Yale nebula.cs.yale.edu 128.36.13.1 + +All files are in the directory pub/haskell/yale. + +haskell-source-205.tar.gz -- The full sources +haskell-205-.tar.gz +Compiling from scratch will take an hour or two, depending on system +resources. The file $HASKELL/com//README will contain +complete building instructions. + +To obtain Yale Haskell via ftp: + + - Move to the directory where you intend to place Yale Haskell + - Ftp to nebula and login anonymously + - cd to pub/haskell/yale + - Get the tar file: get haskell-source-205.tar.gz (a .Z file is available + for those without gzip). + - Unzip the file: (your zip utility may have a different name) + gzip -d *.gz + - Untar the file: tar xf *.tar + - Consult the documentation for further instructions. Either print + out the reference manual in doc/manual/haskell.dvi or look at + install.verb in the same directory. + +Send any comments or questions to haskell-request@cs.yale.edu + + +New features in this release include: + + * Much better optimization + * Monadic I/O + * A general Haskell to Lisp interface + * An X window interface + * Strictness annotations and strict data constructors + * Lots of bugs fixed + * Improvements to the tutorial and Emacs interface + * Ported to all major Lisp systems + diff --git a/doc/comparison b/doc/comparison new file mode 100644 index 0000000..43c6073 --- /dev/null +++ b/doc/comparison @@ -0,0 +1,291 @@ + + Different Versions of Yale Haskell Compared + ------------------------------------------- + + +There are currently three different platforms running Yale Haskell. +Yale Haskell runs on Lucid Common Lisp, CMU Common Lisp, and AKCL. This +document describes the differences between these systems. + +Differences in performance between the different versions of Yale +Haskell reflect the underlying Lisp systems. The better the Lisp +system, the better the Haskell system built on it. However, getting +optimal performance from our Haskell system on top of a Common Lisp +system requires careful attention to the underlying compiler. Small +changes in the optimization settings or the addition of crucial +declarations can make significant differences in performance. We have +been doing most of our work using the Lucid system and have tuned it +more than the others. These comparisons are greatly influenced by the +amount of time we have spent tuning the system: the CMU version has +been tuned only a little and the AKCL version hardly at all. + + + Methodology + +The following timings are only approximate. They were obtained using +the timing functions provided by the Common Lisp system. All timings +were done on an unloaded Sparc 1. No attempt was made to account for +garbage collection, differences in heap size, or similar factors. We +don't intend these benchmark results to be taken as an exhaustive +comparison of the different Lisp implementations involved. + + + Portability + +We have had no trouble moving our system to different hardware +platforms under the same Lisp system. Since the release is in source +form, we expect that users will be able to build on any hardware +platform supported by one the Lisps we have ported to. Probably the +only real constraint on portability is the requirement for a large +virtual memory space. + +From the comp.lang.lisp FAQ: + + Lucid Common Lisp runs on a variety of platforms, including PCs (AIX), + Apollo, HP, Sun-3, Sparc, IBM RT, IBM RS/6000, Decstation 3100, + Silicon Graphics, and Vax. + + CMU Common Lisp is free, and runs on Sparcs (Mach and SunOs), + DecStation 3100 (Mach), IBM RT (Mach) and requires 16mb RAM, 25mb disk. + + Kyoto Common Lisp (KCL) is free, but requires a license. Conforms to CLtL1. + It is available by anonymous ftp from rascal.ics.utexas.edu [128.83.138.20], + cli.com [192.31.85.1], or [133.11.11.11] (a machine in Japan) + in the directory /pub. AKCL is in the file akcl-xxx.tar.Z (take the + highest value of xxx). To obtain KCL, one must first sign and mail a + copy of the license agreement to: Special Interest Group in LISP, + c/o Taiichi Yuasa, Department of Computer Science, Toyohashi + University of Technology, Toyohashi 441, JAPAN. Runs on Sparc, + IBM RT, RS/6000, DecStation 3100, hp300, hp800, Macintosh II (under AUX), + mp386, IBM PS2, Silicon Graphics 4d, Sun3, Sun4, Sequent Symmetry, + IBM 370, NeXT and Vax. A port to DOS is in beta test as + math.utexas.edu:pub/beta2.zip. + +We have not yet completed ports of Yale Haskell to any other Lisp +implementations, although we are likely to do so in the future. + + + System Size + +The overall size of the Haskell system depends on the size of the +underlying Lisp system and how much unnecessary Lisp overhead has been +removed for the system. We have removed large Lisp packages (like +CLOS or CLX), but have not attempted to do any tree shaking. The size +of the saved images (including the Lisp system, the Haskell compiler, +and the compiled prelude) is + +Image Size: + +Lucid 10 meg +CMU 18 meg +AKCL 11 meg + +The larger size of the CMU system is probably an artifact of their way +of saving the system. + + + Compilation Time + +There are three possible ways to compile a Haskell program. All +Haskell programs must be translated into Lisp. The generated Lisp can +then be interpreted, using no additional compilation time; compiled +with a `fast' but nonoptimizing Lisp compiler; or compiled with the +`slow' compiler that aggressively attempts to perform as many +optimizations as possible. + +To time the `fast', nonoptimizing compiler, we have been using + +(PROCLAIM '(OPTIMIZE (SPEED 3) (SAFETY 0) (COMPILATION-SPEED 3))) + +and for the `slow', fully optimizing compiler, we have been using + +(PROCLAIM '(OPTIMIZE (SPEED 3) (SAFETY 0) (COMPILATION-SPEED 0))) + +so that the only difference is in the COMPILATION-SPEED quality. +Lucid does, in fact, provide two completely different compilers that +correspond to these optimize settings. For all three implementations, +it appears that that the effect of a higher compilation speed setting +is primarily in being less aggressive about inlining and making use of +type declarations. + +The Haskell system itself (including the Prelude) is normally built +with the fully optimizing compiler. + +To show just the Haskell to Lisp compilation time, here are the times +needed to compile the Prelude (about 2500 lines of Haskell code). +This does not include the time in the Lisp compiler or starting up the +system. + +Time to compile the Prelude into Lisp: (CPU times) + +Lucid 111 sec +CMU 87 sec +AKCL 576 sec + +Running the Lisp compiler on the generated code takes far longer than +running the Haskell compiler to produce the Lisp code. For example, +the optimizing Lucid compiler takes 47 minutes to compile the Prelude +(about x20 slower than Haskell -> Lisp). The nonoptimizing compiler +is significantly faster but generates poorer code. + +The following times are the Lisp compilation time for the Prolog +interpreter (found in the demo directory of our release): + +Lucid - interpreted 8.8 sec Haskell -> Lisp +Lucid - nonopt 20.0 sec Lisp -> Machine code +Lucid - optimizing 320.0 sec Lisp -> Machine code +CMU - interpreted 12.4 sec Haskell -> Lisp +CMU - nonopt 121.0 sec Lisp -> Machine code +CMU - optimizing 152.8 sec Lisp -> Machine code +AKCL - interpreted 47.8 sec Haskell -> Lisp +AKCL - nonopt ~180 sec Lisp -> Machine code +AKCL - optimizing ~360 sec Lisp -> Machine code + +The AKCL timings are only approximate, because the Lisp timing +functions do not capture the time spent in the C compiler. + + +Code Speed + +The speed of the Haskell program depends on whether the Lisp code +has been compiled with the optimizing or nonoptimizing compiler, or +is running interpretively. + +The first benchmark is nfib, which indicates the basic speed of +function calling and Int arithmetic. + +module Main where + +nfib :: Int -> Int +nfib 0 = 1 +nfib 1 = 1 +nfib n = nfib (n-1) + nfib (n-2) + + + nfib 20 nfib 30 +Lucid (Interpreted) 116 sec * +Lucid (nonopt) 0.14 sec 9.4 sec +Lucid (optimizing) 0.08 sec 4.8 sec +CMU (Interpreted) 23.8 sec * +CMU (nonopt) 0.24 sec 6.9 sec +CMU (optimizing) 0.11 sec 7.0 sec +AKCL (Interpreted) 141 sec * +AKCL (nonopt) 0.20 sec 21.3 sec +AKCL (optimizing) 0.15 sec 18.2 sec + +* Too slow to benchmark + +For other data types, there was no significant difference betwen +optimizing and nonoptimizing compilation in any of the systems. +Changing the signature of nfib to Integer -> Integer: + + nfib 20 nfib 30 +Lucid (interpreted) 140 sec * +Lucid (compiled) 0.18 sec 10.2 sec +CMU (interpreted) 24.2 sec * +CMU (compiled) 0.16 sec 10.5 sec +AKCL (interpreted) 145 sec * +AKCL (compiled) 1.07 sec 127 sec + +Nfib with signature Float -> Float: + + nfib 20 nfib 30 +Lucid (interpreted) 222 sec * +Lucid (compiled) 16.4 sec 2416 sec +CMU (interpreted) 44.2 sec * +CMU (compiled) 1.61 sec 352 sec +AKCL (interpreted) 161 sec * +AKCL (compiled) 103 sec * + +Overloaded functions run considerably slower than nonoverloaded +functions. By allowing nfib to remain overloaded, Num a => a -> a, +and using the Int overloading the same benchmarks run much slower. +Again, there is no real difference between the different compiler +optimization settings. + + nfib 15 nfib 20 +Lucid (interpreted) 14.2 sec 156 sec +Lucid (compiled) 0.97 sec 9.3 sec +CMU (interpreted) 23.8 sec 155 sec +CMU (compiled) 0.89 sec 15.6 sec +AKCL (interpreted) 30.8 sec 387 sec +AKCL (compiled) 10.3 sec 119 sec + +Basic Haskell data structuring operations (pattern matching and +construction) can be tested using another version of nfib which uses +natural numbers: + + data Nat = Z | S Nat + +The difference betwen CMU and Lucid here is consistent with other +benchmarks that manipulate structures. + + nfib 10 nfib 15 +Lucid (Interpreted) 1.39 sec 26.7 sec +Lucid (compiled) 0.26 sec 2.28 sec +CMU (interpreted) 3.1 sec +CMU (compiled) 0.16 sec 0.54 sec +AKCL (Interpreted) 4.25 sec +AKCL (compiled) 0.21 sec 13.9 sec + + + A Large Program + +For a final benchmark, we use the Prolog interpreter as a way of +getting a feel for general performance of larger programs. This +program is typical of symbolic (as opposed to numeric) computation. + +Time to solve append(X,Y,cons(a,cons(b,cons(c,nil)))): + +Lucid 12.2 sec +CMU 12.0 sec +AKCL 69.1 sec + +My interpretation of this result is that although Lucid is a bit +slower on the previous small benchmarks, it makes up for this is +larger programs where advantages like better instruction scheduling, +register allocation, or memory usage may make a difference. In +general, Lucid and CMU are very similar in performance for larger +programs. + + + Conclusions + +Briefly stated, the pluses and minuses of each system are as follows: + +Lucid (4.0.0): + + Development (nonoptimizing) compiler is very fast + + Fast Haskell -> Lisp compilation + + Generates good code + + Very robust + - Costs money + - Slow floating point code + - Fairly slow interpreter + - The production (optimizing) compiler is extremely slow. + +CMU (16e): + + Free + + As fast as Lucid for Haskell -> Lisp + + Good floating point performance + + Generated code is very fast + + Fast interpreter + - Slow Lisp -> machine code compilation + - Doesn't run on many systems + +AKCL (1.615): + + Free + + Widely portable + - Slow (generally 3 - 5 times slower, sometimes much worse) + - Flakey (tends to core dump on errors, choke on large programs, etc.) + +Generally, using the fully optimizing compiler seems to be useful only +in code involving Int arithmetic. + +The fast compiler for Lucid is a big advantage, delivering by far the +fastest compilation to machine code with relatively little loss in +speed compared to the optimizing compiler. + + + Yale Haskell Group + September 25, 1992 + diff --git a/doc/lisp-interface/lisp-interface.dvi b/doc/lisp-interface/lisp-interface.dvi new file mode 100644 index 0000000..b45e902 Binary files /dev/null and b/doc/lisp-interface/lisp-interface.dvi differ diff --git a/doc/manual/haskell.dvi b/doc/manual/haskell.dvi new file mode 100644 index 0000000..a789515 Binary files /dev/null and b/doc/manual/haskell.dvi differ diff --git a/doc/optimizer/optimizer.dvi b/doc/optimizer/optimizer.dvi new file mode 100644 index 0000000..8d0d2bc Binary files /dev/null and b/doc/optimizer/optimizer.dvi differ diff --git a/doc/tutorial/tutorial.ps b/doc/tutorial/tutorial.ps new file mode 100644 index 0000000..333d9da --- /dev/null +++ b/doc/tutorial/tutorial.ps @@ -0,0 +1,6257 @@ +%!PS-Adobe-2.0 +%%Creator: dvips, version 5.4 (C) 1986-90 Radical Eye Software +%%Title: tutorial.dvi +%%Pages: 53 1 +%%BoundingBox: 0 0 612 792 +%%EndComments +%%BeginProcSet: tex.pro +/TeXDict 200 dict def TeXDict begin /N /def load def /B{bind def}N /S /exch +load def /X{S N}B /TR /translate load N /isls false N /vsize 10 N /@rigin{ +isls{[0 1 -1 0 0 0]concat}if 72 Resolution div 72 VResolution div neg scale +Resolution VResolution vsize neg mul TR}B /@letter{/vsize 10 N}B /@landscape{ +/isls true N /vsize -1 N}B /@a4{/vsize 10.6929133858 N}B /@a3{/vsize 15.5531 N +}B /@ledger{/vsize 16 N}B /@legal{/vsize 13 N}B /@manualfeed{statusdict +/manualfeed true put}B /@copies{/#copies X}B /FMat[1 0 0 -1 0 0]N /FBB[0 0 0 0 +]N /df{/sf 1 N /fntrx FMat N df-tail}B /dfs{div /sf X /fntrx[sf 0 0 sf neg 0 0 +]N df-tail}B /df-tail{/nn 8 dict N nn begin /FontType 3 N /FontMatrix fntrx N +/FontBBox FBB N string /base X array /BitMaps X /BuildChar{CharBuilder}N +/Encoding IE N end dup{/foo setfont}2 array copy cvx N load 0 nn put /ctr 0 N[ +}B /E{pop nn dup definefont setfont}B /ch-image{ch-data dup type /stringtype +ne{ctr get /ctr ctr 1 add N}if}B /ch-width{ch-data dup length 5 sub get}B +/ch-height{ch-data dup length 4 sub get}B /ch-xoff{128 ch-data dup length 3 +sub get sub}B /ch-yoff{ch-data dup length 2 sub get 127 sub}B /ch-dx{ch-data +dup length 1 sub get}B /ctr 0 N /CharBuilder{save 3 1 roll S dup /base get 2 +index get S /BitMaps get S get /ch-data X pop /ctr 0 N ch-dx 0 ch-xoff ch-yoff +ch-height sub ch-xoff ch-width add ch-yoff setcachedevice ch-width ch-height +true[1 0 0 -1 -.1 ch-xoff sub ch-yoff .1 add]{ch-image}imagemask restore}B /D{ +/cc X dup type /stringtype ne{]}if nn /base get cc ctr put nn /BitMaps get S +ctr S sf 1 ne{dup dup length 1 sub dup 2 index S get sf div put}if put /ctr +ctr 1 add N}B /I{cc 1 add D}B /bop{userdict /bop-hook known{bop-hook}if /SI +save N @rigin 0 0 moveto}B /eop{clear SI restore showpage userdict /eop-hook +known{eop-hook}if}B /@start{userdict /start-hook known{start-hook}if +/VResolution X /Resolution X 1000 div /DVImag X /IE 256 array N 0 1 255{IE S 1 +string dup 0 3 index put cvn put}for}B /p /show load N /RMat[1 0 0 -1 0 0]N +/BDot 8 string N /v{/ruley X /rulex X V}B /V{gsave TR -.1 -.1 TR rulex ruley +scale 1 1 false RMat{BDot}imagemask grestore}B /a{moveto}B /delta 0 N /tail{ +dup /delta X 0 rmoveto}B /M{S p delta add tail}B /b{S p tail}B /c{-4 M}B /d{ +-3 M}B /e{-2 M}B /f{-1 M}B /g{0 M}B /h{1 M}B /i{2 M}B /j{3 M}B /k{4 M}B /l{p +-4 w}B /m{p -3 w}B /n{p -2 w}B /o{p -1 w}B /q{p 1 w}B /r{p 2 w}B /s{p 3 w}B /t +{p 4 w}B /w{0 rmoveto}B /x{0 S rmoveto}B /y{3 2 roll p a}B /bos{/SS save N}B +/eos{clear SS restore}B end +%%EndProcSet +%%BeginProcSet: special.pro +TeXDict begin /SDict 200 dict N SDict begin /@SpecialDefaults{/hs 612 N /vs +792 N /ho 0 N /vo 0 N /hsc 1 N /vsc 1 N /ang 0 N /CLIP false N /BBcalc false N +/p 3 def}B /@scaleunit 100 N /@hscale{@scaleunit div /hsc X}B /@vscale{ +@scaleunit div /vsc X}B /@hsize{/hs X /CLIP true N}B /@vsize{/vs X /CLIP true +N}B /@hoffset{/ho X}B /@voffset{/vo X}B /@angle{/ang X}B /@rwi{10 div /rwi X} +B /@llx{/llx X}B /@lly{/lly X}B /@urx{/urx X}B /@ury{/ury X /BBcalc true N}B +/magscale true def end /@MacSetUp{userdict /md known{userdict /md get type +/dicttype eq{md begin /letter{}N /note{}N /legal{}N /od{txpose 1 0 mtx +defaultmatrix dtransform S atan/pa X newpath clippath mark{transform{ +itransform moveto}}{transform{itransform lineto}}{6 -2 roll transform 6 -2 +roll transform 6 -2 roll transform{itransform 6 2 roll itransform 6 2 roll +itransform 6 2 roll curveto}}{{closepath}}pathforall newpath counttomark array +astore /gc xdf pop ct 39 0 put 10 fz 0 fs 2 F/|______Courier fnt invertflag{ +PaintBlack}if}N /txpose{pxs pys scale ppr aload pop por{noflips{pop S neg S TR +pop 1 -1 scale}if xflip yflip and{pop S neg S TR 180 rotate 1 -1 scale ppr 3 +get ppr 1 get neg sub neg ppr 2 get ppr 0 get neg sub neg TR}if xflip yflip +not and{pop S neg S TR pop 180 rotate ppr 3 get ppr 1 get neg sub neg 0 TR}if +yflip xflip not and{ppr 1 get neg ppr 0 get neg TR}if}{noflips{TR pop pop 270 +rotate 1 -1 scale}if xflip yflip and{TR pop pop 90 rotate 1 -1 scale ppr 3 get +ppr 1 get neg sub neg ppr 2 get ppr 0 get neg sub neg TR}if xflip yflip not +and{TR pop pop 90 rotate ppr 3 get ppr 1 get neg sub neg 0 TR}if yflip xflip +not and{TR pop pop 270 rotate ppr 2 get ppr 0 get neg sub neg 0 S TR}if} +ifelse scaleby96{ppr aload pop 4 -1 roll add 2 div 3 1 roll add 2 div 2 copy +TR .96 dup scale neg S neg S TR}if}N /cp{pop pop showpage pm restore}N end}if} +if}N /normalscale{Resolution 72 div VResolution 72 div neg scale magscale{ +DVImag dup scale}if}N /psfts{S 65536 div N}N /startTexFig{/psf$SavedState save +N userdict maxlength dict begin /magscale false def normalscale currentpoint +TR /psf$ury psfts /psf$urx psfts /psf$lly psfts /psf$llx psfts /psf$y psfts +/psf$x psfts currentpoint /psf$cy X /psf$cx X /psf$sx psf$x psf$urx psf$llx +sub div N /psf$sy psf$y psf$ury psf$lly sub div N psf$sx psf$sy scale psf$cx +psf$sx div psf$llx sub psf$cy psf$sy div psf$ury sub TR /showpage{}N +/erasepage{}N /copypage{}N @MacSetUp}N /doclip{psf$llx psf$lly psf$urx psf$ury +currentpoint 6 2 roll newpath 4 copy 4 2 roll moveto 6 -1 roll S lineto S +lineto S lineto closepath clip newpath moveto}N /endTexFig{end psf$SavedState +restore}N /@beginspecial{SDict begin /SpecialSave save N gsave normalscale +currentpoint TR @SpecialDefaults}B /@setspecial{CLIP{newpath 0 0 moveto hs 0 +rlineto 0 vs rlineto hs neg 0 rlineto closepath clip}{initclip}ifelse ho vo TR +hsc vsc scale ang rotate BBcalc{rwi urx llx sub div dup scale llx neg lly neg +TR}if /showpage{}N /erasepage{}N /copypage{}N newpath}B /@endspecial{grestore +clear SpecialSave restore end}B /@defspecial{SDict begin}B /@fedspecial{end}B +/li{lineto}B /rl{rlineto}B /rc{rcurveto}B /np{/SaveX currentpoint /SaveY X N 1 +setlinecap newpath}B /st{stroke SaveX SaveY moveto}B /fil{fill SaveX SaveY +moveto}B /ellipse{/endangle X /startangle X /yrad X /xrad X /savematrix matrix +currentmatrix N TR xrad yrad scale 0 0 1 startangle endangle arc savematrix +setmatrix}B end +%%EndProcSet +TeXDict begin 1000 300 300 @start /Fa 3 84 df<00000000C000000001E000000003E000 +000003C000000007C00000000F800000001F000000001E000000003E000000007C00000000F800 +000000F000000001F000000003E000000007C000000007800000000F800000001F000000003E00 +0000003C000000007C00000000F800000001F000000001E000000003E000000007C00000000780 +0000000F800000001F000000003E000000003C000000007C00000000F800000001F000000001E0 +00000003E000000007C00000000F800000000F000000001F000000003E000000007C0000000078 +00000000F800000000F0000000006000000000232E82AB1F>19 D<00000000001800000000003C +0000000000FC0000000001F80000000003F00000000007E0000000001F80000000003F00000000 +007E0000000001FC0000000003F00000000007E0000000000FC0000000003F00000000007E0000 +000000FC0000000001F80000000007E0000000000FC0000000001F80000000007F0000000000FC +0000000001F80000000003F0000000000FC0000000001F80000000003F00000000007E00000000 +01F80000000003F00000000007E0000000001FC0000000003F00000000007E0000000000FC0000 +000000F000000000006000000000002E2582A22A>35 D<6000000000F000000000F80000000078 +000000007C000000003E000000001F000000000F000000000F8000000007C000000003E0000000 +01E000000001F000000000F8000000007C000000003C000000003E000000001F000000000F8000 +0000078000000007C000000003E000000001E000000001F000000000F8000000007C000000003C +000000003E000000001F000000000F80000000078000000007C000000003E000000001F0000000 +00F000000000F8000000007C000000003E000000001E000000001F000000000F8000000007C000 +000003C000000003E000000001E000000000C0232E82AB1F>83 D E /Fb +5 111 df<004000C000C003800D8001800180030003000300030006000600060006000C000C00 +0C000C001800FF800A157C9412>49 D<030706000000000000384C4C4C8C181818303262622438 +08177D960B>105 D<003000700020000000000000000000000000038004400460046008C000C0 +00C000C0018001800180018003000300030003006600E600CC0070000C1D81960B>I<1F000600 +0600060006000C000C000C000C001870189819381A30340038003E0033006300631063106310C3 +20C1C00D177D9610>I<387044984708460C8C180C180C180C1818301831186118623026303810 +0E7D8D14>110 D E /Fc 46 122 df<000FF07F00007FF9FF8000F83FC7C001E07F8FC003E07F +0FC007C07F0FC007C03F078007C01F000007C01F000007C01F000007C01F000007C01F0000FFFF +FFF800FFFFFFF80007C01F000007C01F000007C01F000007C01F000007C01F000007C01F000007 +C01F000007C01F000007C01F000007C01F000007C01F000007C01F000007C01F000007C01F0000 +07C01F000007C01F00003FF8FFF0003FF8FFF0002220809F1F>11 D45 D<387CFEFEFE7C3807077C860F>I<00E00001E0000FE000FFE000F3E00003E00003E0 +0003E00003E00003E00003E00003E00003E00003E00003E00003E00003E00003E00003E00003E0 +0003E00003E00003E00003E00003E00003E00003E000FFFF80FFFF80111D7C9C1A>49 +D<07F0001FFE00383F007C1F80FE0FC0FE0FC0FE0FE0FE07E07C07E03807E0000FE0000FC0000F +C0001F80001F00003E0000780000F00000E00001C0000380600700600E00601C00E01FFFC03FFF +C07FFFC0FFFFC0FFFFC0131D7D9C1A>I<01FC0007FF000E0F801E0FC03F07E03F07E03F07E03F +07E01E0FC0000FC0000F80001F0001FC0001FC00000F800007C00003E00003F00003F83803F87C +03F8FE03F8FE03F8FE03F0FC03F07807E03C0FC01FFF8003FC00151D7E9C1A>I<0001C00003C0 +0007C00007C0000FC0001FC0003BC00073C00063C000C3C00183C00383C00703C00E03C00C03C0 +1803C03803C07003C0E003C0FFFFFEFFFFFE0007C00007C00007C00007C00007C00007C000FFFE +00FFFE171D7F9C1A>I<3803803FFF803FFF003FFE003FFC003FF0003F80003000003000003000 +0030000033F80037FE003C1F00380F801007C00007C00007E00007E07807E0FC07E0FC07E0FC07 +E0FC07C0780FC0600F80381F001FFC0007F000131D7D9C1A>I<387CFEFEFE7C38000000000000 +387CFEFEFE7C3807147C930F>58 D<0000E000000000E000000001F000000001F000000001F000 +000003F800000003F800000006FC00000006FC0000000EFE0000000C7E0000000C7E000000183F +000000183F000000303F800000301F800000701FC00000600FC00000600FC00000C007E00000FF +FFE00001FFFFF000018003F000018003F000030001F800030001F800060001FC00060000FC000E +0000FE00FFE00FFFE0FFE00FFFE0231F7E9E28>65 DI<0007FC +02003FFF0E00FE03DE03F000FE07E0003E0FC0001E1F80001E3F00000E3F00000E7F0000067E00 +00067E000006FE000000FE000000FE000000FE000000FE000000FE000000FE0000007E0000007E +0000067F0000063F0000063F00000C1F80000C0FC0001807E0003803F0007000FE01C0003FFF80 +0007FC001F1F7D9E26>IIII72 D76 D78 +D<001FF80000FFFF0001F81F8007E007E00FC003F01F8001F81F0000F83F0000FC7F0000FE7E00 +007E7E00007EFE00007FFE00007FFE00007FFE00007FFE00007FFE00007FFE00007FFE00007FFE +00007F7E00007E7F0000FE7F0000FE3F0000FC3F8001FC1F8001F80FC003F007E007E001F81F80 +00FFFF00001FF800201F7D9E27>II<03FC080FFF381E03F838 +00F8700078700038F00038F00018F00018F80000FC00007FC0007FFE003FFF801FFFE00FFFF007 +FFF000FFF80007F80000FC00007C00003CC0003CC0003CC0003CE00038E00078F80070FE01E0E7 +FFC081FF00161F7D9E1D>83 D<7FFFFFFC7FFFFFFC7C07E07C7007E01C6007E00C6007E00CE007 +E00EC007E006C007E006C007E006C007E0060007E0000007E0000007E0000007E0000007E00000 +07E0000007E0000007E0000007E0000007E0000007E0000007E0000007E0000007E0000007E000 +0007E0000007E00003FFFFC003FFFFC01F1E7E9D24>I87 D<07FC001FFF003F0F803F07C03F03E03F03E00C03E00003E000 +7FE007FBE01F03E03C03E07C03E0F803E0F803E0F803E0FC05E07E0DE03FF8FE0FE07E17147F93 +19>97 DI<01FE0007FF801F0FC03E +0FC03E0FC07C0FC07C0300FC0000FC0000FC0000FC0000FC0000FC00007C00007E00003E00603F +00C01F81C007FF0001FC0013147E9317>I<0007F80007F80000F80000F80000F80000F80000F8 +0000F80000F80000F80000F80000F801F8F80FFEF81F83F83E01F87E00F87C00F87C00F8FC00F8 +FC00F8FC00F8FC00F8FC00F8FC00F87C00F87C00F87E00F83E01F81F07F80FFEFF03F8FF18207E +9F1D>I<01FE0007FF800F83C01E01E03E00F07C00F07C00F8FC00F8FFFFF8FFFFF8FC0000FC00 +00FC00007C00007C00003E00181E00180F807007FFE000FF8015147F9318>I<001F8000FFC001 +F3E003E7E003C7E007C7E007C3C007C00007C00007C00007C00007C000FFFC00FFFC0007C00007 +C00007C00007C00007C00007C00007C00007C00007C00007C00007C00007C00007C00007C00007 +C00007C0003FFC003FFC0013207F9F10>I<01FC3C07FFFE0F079E1E03DE3E03E03E03E03E03E0 +3E03E03E03E01E03C00F07800FFF0009FC001800001800001C00001FFF800FFFF007FFF81FFFFC +3C007C70003EF0001EF0001EF0001E78003C78003C3F01F80FFFE001FF00171E7F931A>II<1C003E007F007F007F003E001C000000 +00000000000000000000FF00FF001F001F001F001F001F001F001F001F001F001F001F001F001F +001F001F001F00FFE0FFE00B217EA00E>I107 DIII<01FF0007FFC01F83F03E00F83E00F87C007C7C007CFC +007EFC007EFC007EFC007EFC007EFC007E7C007C7C007C3E00F83E00F81F83F007FFC001FF0017 +147F931A>II<01F81807FE381F87783F01F83E01F87E00 +F87C00F8FC00F8FC00F8FC00F8FC00F8FC00F8FC00F87C00F87E00F87E00F83F01F81F87F80FFE +F803F8F80000F80000F80000F80000F80000F80000F80000F80007FF0007FF181D7E931C>II<0FE63FFE701E600EE006E006 +F800FFC07FF83FFC1FFE03FE001FC007C007E007F006F81EFFFCC7F010147E9315>I<01800180 +018003800380038007800F803F80FFFCFFFC0F800F800F800F800F800F800F800F800F800F800F +860F860F860F860F8607CC03F801F00F1D7F9C14>III120 +DI E /Fd 2 111 df<0300038003000000000000000000000000001C002400460046 +008C000C0018001800180031003100320032001C0009177F960C>105 D<383C0044C600470200 +4602008E06000C06000C06000C0C00180C00180C40181840181880300880300F00120E7F8D15> +110 D E /Fe 20 122 df<03CC0E2E181C381C301C701CE038E038E038E038C072C072C07260F2 +61341E180F107C8F14>97 D<7E000E000E000E001C001C001C001C00380038003BC03C30783070 +1870187018E038E038E038E038C070C060C0E060C063801E000D1A7C9912>I<01F006080C1818 +38301070006000E000E000E000E000E008E010602030C01F000D107C8F12>I<001F8000038000 +0380000380000700000700000700000700000E00000E0003CE000E2E00181C00381C00301C0070 +1C00E03800E03800E03800E03800C07200C07200C0720060F2006134001E1800111A7C9914>I< +01E006181C08380870087010FFE0E000E000E000E000E0086010602030C01F000D107C8F12>I< +1F80000380000380000380000700000700000700000700000E00000E00000E7C000F86001E0700 +1E07001C07001C0700380E00380E00380E00381C00701C80701C80703880703900E01900600E00 +111A7E9914>104 D<030706000000000000384C4E8E9C9C1C3838707272E2E4643808197C980C> +I<1F8003800380038007000700070007000E000E000E0E0E131C271C431C801F003C003F8039C0 +38E070E270E270E270E4E0646038101A7E9912>107 D<3F0707070E0E0E0E1C1C1C1C38383838 +70707070E4E4E4E46830081A7D990A>I<307C1E00598663009E0783809E0703809C0703809C07 +0380380E0700380E0700380E0700380E0E00701C0E40701C0E40701C1C40701C1C80E0380C8060 +1807001A107C8F1F>I<307C005986009E07009E07009C07009C0700380E00380E00380E00381C +00701C80701C80703880703900E01900600E0011107C8F16>I<01F006180C0C180E300E700E60 +0EE00EE00EE00CE01CE018E030606030C01F000F107C8F14>I<030F000590C009E0C009C06009 +C06009C0600380E00380E00380E00380E00701C00701800703800703000E8E000E78000E00000E +00001C00001C00001C00001C0000FF00001317808F14>I<03C20E2E181C381C301C701CE038E0 +38E038E038C070C070C07060F061E01EE000E000E001C001C001C001C01FF00F177C8F12>I<30 +F059189E389C189C009C0038003800380038007000700070007000E00060000D107C8F10>I<03 +E004300830187018601C001F801FC00FE000E00060E060E06080C041803E000C107D8F10>I<06 +000E000E000E000E001C001C00FFC01C0038003800380038007000700070007000E100E100E100 +E200640038000A177C960D>I<38064C074E0E8E0E9C0E9C0E1C1C381C381C381C703970397039 +3079389A0F0C10107C8F15>I<38184C1C4E1C8E0C9C0C9C0C1C08380838083808701070107020 +304018C00F000E107C8F12>I<38064C074E0E8E0E9C0E9C0E1C1C381C381C381C703870387038 +307838F00F700070006060E0E1C0C18047003C0010177C8F13>121 D E +/Ff 27 122 df<60F0F878181818303060C080050C789614>39 D<00C001C0030006000C001C00 +38003000700070006000E000E000E000E000E000E000E000600070007000300038001C000C0006 +00030001C000C00A1D7A9914>I<8000C0006000300018001C000E000600070007000300038003 +800380038003800380038003000700070006000E001C00180030006000C0008000091D7C9914> +I<01C00001C00001C00001C00001C00001C00001C000FFFF80FFFF80FFFF8001C00001C00001C0 +0001C00001C00001C00001C00011117F9314>43 D<70F8FCFC7C0C1830E0C0060A798414>I<70 +F8F8F8700505798414>46 D<0006000E000E001C001C003800380070007000E000E001C001C003 +8003800380070007000E000E001C001C003800380070007000E000E000C0000F1D7E9914>I<70 +F8F8F87000000000000070F8F8F8700510798F14>58 D61 D91 D93 +D<0818306060C0C0C0F0F87830050C799914>96 D<1FC0007FF000707800201800001C00001C00 +07FC001FFC003C1C00701C00E01C00E01C00E01C00707C003FFF800F8F8011107E8F14>I<03F8 +0FFC1C1C380870006000E000E000E000E00060007000380E1C1E0FFC03F00F107E8F14>99 +D<007E00007E00000E00000E00000E00000E00000E0007CE000FFE001C3E00301E00700E00E00E +00E00E00E00E00E00E00E00E00E00E00700E00301E00383E001FEFC007CFC012177F9614>I<07 +E00FF01C38301C700CE00EE00EFFFEFFFEE00060007000380E1C1E0FFC03F00F107E8F14>I<00 +7C00FE01CE03840380038003807FFEFFFE03800380038003800380038003800380038003800380 +03807FFC7FFC0F177F9614>I<030007800780030000000000000000007F807F80038003800380 +038003800380038003800380038003800380FFFCFFFC0E187D9714>105 +D108 D110 +D<07C01FF03C78701C701CE00EE00EE00EE00EE00EE00E701C783C3C781FF007C00F107E8F14> +I<03CE000FFE001C3E00301E00700E00E00E00E00E00E00E00E00E00E00E00E00E00700E00301E +001C3E000FEE0007CE00000E00000E00000E00000E00000E00000E00007FC0007FC012187F8F14 +>113 D<0FD83FF86038C038C038F0007F803FF007F8001C6006E006F006F81CFFF8CFE00F107E +8F14>115 D<030007000700070007007FFCFFFC07000700070007000700070007000700070E07 +0E070E070C03FC00F00F157F9414>I118 +D<7E3F007E3F001E38000E780007700007E00003E00001C00003C00003E0000770000E78000E38 +001C1C00FE3F80FE3F8011107F8F14>120 DI E /Fg 56 123 df<000FF000007FFC0001F80E +0003E01F0007C03F000F803F000F803F000F801E000F800C000F8000000F8000000F8000000F80 +0000FFFFFF00FFFFFF000F801F000F801F000F801F000F801F000F801F000F801F000F801F000F +801F000F801F000F801F000F801F000F801F000F801F000F801F000F801F000F801F000F801F00 +0F801F007FF0FFE07FF0FFE01B237FA21F>12 D<3803807C07C0FE0FE0FF0FF0FF0FF07F07F03B +03B00300300300300700700600600600600C00C01C01C018018070070020020014117EA21D>34 +D<387CFEFFFF7F3B03030706060C1C18702008117C8610>44 DI<387CFEFEFE7C3807077C8610>I<0000180000380000380000700000700000E00000E00000E0 +0001C00001C0000380000380000380000700000700000700000E00000E00001C00001C00001C00 +00380000380000700000700000700000E00000E00001C00001C00001C000038000038000070000 +0700000700000E00000E00000E00001C00001C0000380000380000380000700000700000E00000 +E00000C0000015317DA41C>I<00180000780001F800FFF800FFF80001F80001F80001F80001F8 +0001F80001F80001F80001F80001F80001F80001F80001F80001F80001F80001F80001F80001F8 +0001F80001F80001F80001F80001F80001F80001F80001F8007FFFE07FFFE013207C9F1C>49 +D<03FC000FFF003C1FC07007E07C07F0FE03F0FE03F8FE03F8FE01F87C01F83803F80003F80003 +F00003F00007E00007C0000F80001F00003E0000380000700000E01801C0180380180700180E00 +380FFFF01FFFF03FFFF07FFFF0FFFFF0FFFFF015207D9F1C>I<00FE0007FFC00F07E01E03F03F +03F03F81F83F81F83F81F81F03F81F03F00003F00003E00007C0001F8001FE0001FF000007C000 +01F00001F80000FC0000FC3C00FE7E00FEFF00FEFF00FEFF00FEFF00FC7E01FC7801F81E07F00F +FFC001FE0017207E9F1C>I<0000E00001E00003E00003E00007E0000FE0001FE0001FE00037E0 +0077E000E7E001C7E00187E00307E00707E00E07E00C07E01807E03807E07007E0E007E0FFFFFE +FFFFFE0007E00007E00007E00007E00007E00007E00007E000FFFE00FFFE17207E9F1C>I<1000 +201E01E01FFFC01FFF801FFF001FFE001FF8001BC00018000018000018000018000019FC001FFF +001E0FC01807E01803E00003F00003F00003F80003F83803F87C03F8FE03F8FE03F8FC03F0FC03 +F07007E03007C01C1F800FFF0003F80015207D9F1C>I<001F8000FFE003F07007C0F00F01F81F +01F83E01F83E01F87E00F07C00007C0000FC0800FC7FC0FCFFE0FD80F0FF00F8FE007CFE007CFC +007EFC007EFC007EFC007E7C007E7C007E7C007E3C007C3E007C1E00F80F00F00783E003FFC000 +FF0017207E9F1C>I<6000007800007FFFFE7FFFFE7FFFFC7FFFF87FFFF87FFFF0E00060E000C0 +C00180C00300C00300000600000C00001C0000180000380000780000780000F00000F00000F000 +01F00001F00001F00003F00003F00003F00003F00003F00003F00003F00001E00017227DA11C> +I<00FE0003FFC00703E00E00F01C00F01C00783C00783E00783F00783F80783FE0F01FF9E01FFF +C00FFF8007FFC003FFE007FFF01E7FF83C1FFC7807FC7801FEF000FEF0003EF0001EF0001EF000 +1CF8001C7800383C00381F01F00FFFC001FF0017207E9F1C>I<01FE0007FF800F83E01E01F03E +00F07C00F87C0078FC007CFC007CFC007CFC007EFC007EFC007EFC007E7C00FE7C00FE3E01FE1E +037E0FFE7E07FC7E00207E00007C00007C1E007C3F00F83F00F83F00F03F01E01E03C01C0F800F +FE0003F80017207E9F1C>I<387CFEFEFE7C380000000000000000387CFEFEFE7C3807167C9510> +I<000070000000007000000000F800000000F800000000F800000001FC00000001FC00000003FE +00000003FE00000003FE00000006FF000000067F0000000E7F8000000C3F8000000C3F80000018 +3FC00000181FC00000381FE00000300FE00000300FE00000600FF000006007F00000E007F80000 +FFFFF80000FFFFF800018001FC00018001FC00038001FE00030000FE00030000FE000600007F00 +0600007F00FFE00FFFF8FFE00FFFF825227EA12A>65 DI<0003FE0080001FFF818000FF01E38001F8003F8003E0001F8007C0000F800F800007 +801F800007803F000003803F000003807F000001807E000001807E00000180FE00000000FE0000 +0000FE00000000FE00000000FE00000000FE00000000FE00000000FE000000007E000000007E00 +0001807F000001803F000001803F000003801F800003000F8000030007C000060003F0000C0001 +F800380000FF00F000001FFFC0000003FE000021227DA128>IIII73 D +76 DI +I<0007FC0000003FFF800000FC07E00003F001F80007E000FC000FC0007E001F80003F001F8000 +3F003F00001F803F00001F807F00001FC07E00000FC07E00000FC0FE00000FE0FE00000FE0FE00 +000FE0FE00000FE0FE00000FE0FE00000FE0FE00000FE0FE00000FE0FE00000FE07E00000FC07F +00001FC07F00001FC03F00001F803F80003F801F80003F000FC0007E0007E000FC0003F001F800 +00FC07E000003FFF80000007FC000023227DA12A>II82 +D<01FC0407FF8C1F03FC3C007C7C003C78001C78001CF8000CF8000CFC000CFC0000FF0000FFE0 +007FFF007FFFC03FFFF01FFFF80FFFFC03FFFE003FFE0003FF00007F00003F00003FC0001FC000 +1FC0001FE0001EE0001EF0003CFC003CFF00F8C7FFE080FF8018227DA11F>I<7FFFFFFF807FFF +FFFF807E03F80F807803F807807003F803806003F80180E003F801C0E003F801C0C003F800C0C0 +03F800C0C003F800C0C003F800C00003F800000003F800000003F800000003F800000003F80000 +0003F800000003F800000003F800000003F800000003F800000003F800000003F800000003F800 +000003F800000003F800000003F800000003F800000003F800000003F800000003F8000003FFFF +F80003FFFFF80022227EA127>II<0400400E +00E0180180380380300300600600600600E00E00C00C00C00C00DC0DC0FE0FE0FF0FF0FF0FF07F +07F03E03E01C01C014117AA21D>92 D<07FC001FFF803F07C03F03E03F01E03F01F01E01F00001 +F00001F0003FF003FDF01FC1F03F01F07E01F0FC01F0FC01F0FC01F0FC01F07E02F07E0CF81FF8 +7F07E03F18167E951B>97 DI<00FF8007FFE00F83F01F03F03E03F07E03F07C01E07C0000FC0000FC0000FC0000FC0000 +FC0000FC00007C00007E00007E00003E00301F00600FC0E007FF8000FE0014167E9519>I<0001 +FE000001FE0000003E0000003E0000003E0000003E0000003E0000003E0000003E0000003E0000 +003E0000003E0000003E0001FC3E0007FFBE000F81FE001F007E003E003E007E003E007C003E00 +FC003E00FC003E00FC003E00FC003E00FC003E00FC003E00FC003E00FC003E007C003E007C003E +003E007E001E00FE000F83BE0007FF3FC001FC3FC01A237EA21F>I<00FE0007FF800F87C01E01 +E03E01F07C00F07C00F8FC00F8FC00F8FFFFF8FFFFF8FC0000FC0000FC00007C00007C00007E00 +003E00181F00300FC07003FFC000FF0015167E951A>I<003F8000FFC001E3E003C7E007C7E00F +87E00F83C00F80000F80000F80000F80000F80000F8000FFFC00FFFC000F80000F80000F80000F +80000F80000F80000F80000F80000F80000F80000F80000F80000F80000F80000F80000F80000F +80000F80007FF8007FF80013237FA211>I<03FC1E0FFF7F1F0F8F3E07CF3C03C07C03E07C03E0 +7C03E07C03E07C03E03C03C03E07C01F0F801FFF0013FC003000003000003800003FFF801FFFF0 +0FFFF81FFFFC3800FC70003EF0001EF0001EF0001EF0001E78003C7C007C3F01F80FFFE001FF00 +18217E951C>II<1C00 +3E007F007F007F003E001C000000000000000000000000000000FF00FF001F001F001F001F001F +001F001F001F001F001F001F001F001F001F001F001F001F001F00FFE0FFE00B247EA310>I108 +DI +I<00FE0007FFC00F83E01E00F03E00F87C007C7C007C7C007CFC007EFC007EFC007EFC007EFC00 +7EFC007EFC007E7C007C7C007C3E00F81F01F00F83E007FFC000FE0017167E951C>II<00FE030007FF87000FC1C7001F006F003F003F007E003F007E +001F007C001F00FC001F00FC001F00FC001F00FC001F00FC001F00FC001F00FC001F007E001F00 +7E001F003E003F001F007F000FC1DF0007FF9F0001FC1F0000001F0000001F0000001F0000001F +0000001F0000001F0000001F0000001F000000FFE00000FFE01B207E951E>II<0FF3003FFF00781F00600700 +E00300E00300F00300FC00007FE0007FF8003FFE000FFF0001FF00000F80C00780C00380E00380 +E00380F00700FC0E00EFFC00C7F00011167E9516>I<0180000180000180000180000380000380 +000780000780000F80003F8000FFFF00FFFF000F80000F80000F80000F80000F80000F80000F80 +000F80000F80000F80000F80000F81800F81800F81800F81800F81800F830007C30003FE0000F8 +0011207F9F16>III120 D +I<7FFFF07FFFF07C03E07007C0600FC0E01F80C01F00C03E00C07E0000FC0000F80001F00003F0 +3007E03007C0300F80701F80703F00603E00E07C03E0FFFFE0FFFFE014167E9519>I +E /Fh 11 121 df<70F8F8F87005057C840D>58 D<70F8FCFC74040404080810102040060E7C84 +0D>I<00F1800389C00707800E03801C03803C0380380700780700780700780700F00E00F00E00 +F00E00F00E10F01C20F01C20703C20705C40308C400F078014147E9318>97 +D<007C01C207010E011C013C013802780C7BF07C00F000F000F000F00070007001700230041838 +07C010147E9315>101 D<01E0000FE00001C00001C00001C00001C00003800003800003800003 +80000700000700000701E00706100E08700E10F00E20F00E40601C80001D00001E00001FC00038 +7000383800383800381C20703840703840703840701880E01880600F0014207E9F18>107 +D<1E07802318C023A06043C0704380704380708700E00700E00700E00700E00E01C00E01C00E01 +C00E03821C03841C07041C07081C03083803101801E017147E931B>110 +D<03C1E004621804741C08781C08701E08701E10E01E00E01E00E01E00E01E01C03C01C03C01C0 +3C01C0380380780380700380E003C1C0072380071E000700000700000E00000E00000E00000E00 +001C00001C0000FFC000171D819317>112 D<00C000E001C001C001C001C003800380FFF80380 +07000700070007000E000E000E000E001C001C001C001C10382038203820384018800F000D1C7F +9B10>116 D<0F00601180702180E021C0E041C0E04380E08381C00701C00701C00701C00E0380 +0E03800E03800E03840E07080C07080C07080E0F1006131003E1E016147E931A>I<0F01801183 +C02183E021C1E041C0E04380608380400700400700400700400E00800E00800E00800E01000E01 +000C02000E04000E040006180001E00013147E9316>I<03C1C00C62201034701038F02038F020 +386040700000700000700000700000E00000E00000E00000E02061C040F1C040F1C080E2C08044 +6300383C0014147E931A>120 D E /Fi 86 127 df<70F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8 +70000000000070F8F8F870051C779B18>33 D<4010E038F078E038E038E038E038E038E038E038 +E038E038E03860300D0E7B9C18>I<00C00001C00001C00001C00003F0000FFC003FFE007DCF00 +71C700E1C380E1C780E1C780E1C780F1C00079C0003DC0001FE0000FF80003FC0001DE0001CF00 +01C70061C380F1C380F1C380E1C380E1C70071C70079DE003FFE001FF80007E00001C00001C000 +01C00000C00011247D9F18>36 D<3803007C07807C0780EE0F80EE0F00EE0F00EE1F00EE1E00EE +1E00EE3E007C3C007C3C00387C0000780000780000F80000F00001F00001E00001E00003E00003 +C00003C00007C0000783800787C00F87C00F0EE00F0EE01F0EE01E0EE01E0EE03E0EE03C07C03C +07C018038013247E9F18>I<01C00007E0000FF0000E70001C38001C38001C38001C38001C73F0 +1C73F01CE3F00FE3800FC7000F87000F07001F0E003F0E007B8E0073DC00E1DC00E0F800E0F800 +E07070E0787070FC707FFFE03FCFE00F03C0141C7F9B18>I<387C7C7E3E0E0E0E1C1C38F8F0C0 +070E789B18>I<007000F001E003C007800F001E001C00380038007000700070007000E000E000 +E000E000E000E000E000E0007000700070007000380038001C001E000F00078003C001F000F000 +700C24799F18>I<6000F00078003C001E000F000780038001C001C000E000E000E000E0007000 +7000700070007000700070007000E000E000E000E001C001C0038007800F001E003C007800F000 +60000C247C9F18>I<01C00001C00001C00001C000C1C180F1C780F9CF807FFF001FFC0007F000 +07F0001FFC007FFF00F9CF80F1C780C1C18001C00001C00001C00001C00011147D9718>I<0060 +0000F00000F00000F00000F00000F00000F00000F0007FFFC0FFFFE0FFFFE07FFFC000F00000F0 +0000F00000F00000F00000F00000F00000600013147E9718>I<1C3E7E7F3F1F070E1E7CF86008 +0C788518>I<7FFF00FFFF80FFFF807FFF0011047D8F18>I<3078FCFC78300606778518>I<0003 +00000780000780000F80000F00001F00001E00001E00003E00003C00007C0000780000780000F8 +0000F00001F00001E00003E00003C00003C00007C0000780000F80000F00000F00001F00001E00 +003E00003C00003C00007C0000780000F80000F00000F0000060000011247D9F18>I<01F00007 +FC000FFE001F1F001C07003803807803C07001C07001C0E000E0E000E0E000E0E000E0E000E0E0 +00E0E000E0E000E0E000E0F001E07001C07001C07803C03803801C07001F1F000FFE0007FC0001 +F000131C7E9B18>I<01800380038007800F803F80FF80FB804380038003800380038003800380 +03800380038003800380038003800380038003807FFCFFFE7FFC0F1C7B9B18>I<03F0000FFE00 +3FFF007C0F807003C0E001C0F000E0F000E06000E00000E00000E00001C00001C00003C0000780 +000F00001E00003C0000780000F00001E00007C0000F80001E00E03C00E07FFFE0FFFFE07FFFE0 +131C7E9B18>I<07F8001FFE003FFF007807807803C07801C03001C00001C00003C0000380000F +0003FF0003FE0003FF000007800003C00001C00000E00000E00000E0F000E0F000E0F001C0F003 +C07C07803FFF001FFE0003F800131C7E9B18>I<001F00003F0000770000770000E70001E70001 +C7000387000787000707000E07001E07003C0700380700780700F00700FFFFF8FFFFF8FFFFF800 +0700000700000700000700000700000700007FF000FFF8007FF0151C7F9B18>I<1FFF803FFF80 +3FFF803800003800003800003800003800003800003800003800003BF8003FFE003FFF003C0780 +1803C00001C00000E00000E06000E0F000E0F000E0E001C07003C07C0F803FFF001FFC0003F000 +131C7E9B18>I<007E0001FF0007FF800F83C01E03C01C03C0380180380000700000700000E1F8 +00E7FE00FFFF00FE0780F803C0F001C0F000E0E000E0F000E07000E07000E07000E03801C03C03 +C01E07800FFF0007FE0001F800131C7E9B18>II<03F800 +0FFE001FFF003E0F803803807001C07001C07001C07001C03803803C07801FFF0007FC000FFE00 +1F1F003C07807001C0F001E0E000E0E000E0E000E0E000E07001C07803C03E0F801FFF000FFE00 +03F800131C7E9B18>I<03F0000FFC001FFE003C0F00780780700380E001C0E001C0E001C0E001 +E0E001E07001E07803E03C0FE01FFFE00FFEE003F0E00000E00001C00001C00001C03003807807 +80780F00783E003FFC001FF00007C000131C7E9B18>I<3078FCFC783000000000000000003078 +FCFC78300614779318>I<183C7E7E3C180000000000000000183C7E7E3E1E0E1C3C78F060071A +789318>I<000300000780001F80003F00007E0001FC0003F00007E0001FC0003F00007E0000FC +0000FC00007E00003F00001FC00007E00003F00001FC00007E00003F00001F8000078000030011 +187D9918>I<7FFFC0FFFFE0FFFFE0FFFFE0000000000000000000000000FFFFE0FFFFE0FFFFE0 +7FFFC0130C7E9318>I<600000F00000FC00007E00003F00001FC00007E00003F00001FC00007E +00003F00001F80001F80003F00007E0001FC0003F00007E0001FC0003F00007E0000FC0000F000 +0060000011187D9918>I<007C0001FE0007FF000F87801E03C03C1DC0387FC070FFE071E3E071 +C1E0E1C1E0E380E0E380E0E380E0E380E0E380E0E380E0E1C1C071C1C071E3C070FF80387F003C +1C001E00E00F83E007FFC001FF80007E00131C7E9B18>64 D<00700000F80000F80000D80000D8 +0001DC0001DC0001DC00018C00038E00038E00038E00038E000306000707000707000707000707 +000FFF800FFF800FFF800E03800E03801C01C01C01C07F07F0FF8FF87F07F0151C7F9B18>II<00F8E003FEE007FFE00F07E01E03E03C01E03800E07000E07000E0 +700000E00000E00000E00000E00000E00000E00000E00000E000007000007000E07000E03800E0 +3C00E01E01C00F07C007FF8003FE0000F800131C7E9B18>I<7FF800FFFE007FFF001C0F801C03 +C01C03C01C01E01C00E01C00E01C00F01C00701C00701C00701C00701C00701C00701C00701C00 +701C00F01C00E01C00E01C01E01C01C01C03C01C0F807FFF00FFFE007FF800141C7F9B18>III<01F1C003FDC00FFFC01F0FC01C03 +C03803C03801C07001C07001C0700000E00000E00000E00000E00000E00000E00FF0E01FF0E00F +F07001C07001C07003C03803C03803C01C07C01F0FC00FFFC003FDC001F1C0141C7E9B18>I<7F +07F0FF8FF87F07F01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01FFFC01F +FFC01FFFC01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C07F07F0FF +8FF87F07F0151C7F9B18>I<7FFF00FFFF807FFF0001C00001C00001C00001C00001C00001C000 +01C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C000 +01C00001C00001C0007FFF00FFFF807FFF00111C7D9B18>I<7FE000FFE0007FE0000E00000E00 +000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00 +000E00000E00000E00700E00700E00700E00700E00707FFFF0FFFFF07FFFF0141C7F9B18>76 +DI<7E07F0FF0FF87F07F01D81C01D81C01D81C01DC1C01CC1C01C +C1C01CE1C01CE1C01CE1C01C61C01C71C01C71C01C31C01C39C01C39C01C39C01C19C01C19C01C +1DC01C0DC01C0DC01C0DC07F07C0FF87C07F03C0151C7F9B18>I<0FF8003FFE007FFF00780F00 +700700F00780E00380E00380E00380E00380E00380E00380E00380E00380E00380E00380E00380 +E00380E00380E00380E00380E00380F00780700700780F007FFF003FFE000FF800111C7D9B18> +II<7FF800FFFE007FFF001C0F801C03801C03C01C01C01C01C01C +01C01C03C01C03801C0F801FFF001FFE001FFE001C0F001C07001C03801C03801C03801C03801C +03801C039C1C039C1C039C7F01F8FF81F87F00F0161C7F9B18>82 D<03F3801FFF803FFF807C0F +80700780E00380E00380E00380E000007000007800003F00001FF00007FE0000FF00000F800003 +C00001C00000E00000E06000E0E000E0E001E0F001C0F80780FFFF80FFFE00E7F800131C7E9B18 +>I<7FFFF8FFFFF8FFFFF8E07038E07038E07038E0703800700000700000700000700000700000 +700000700000700000700000700000700000700000700000700000700000700000700000700007 +FF0007FF0007FF00151C7F9B18>I86 DI<7F8FE07F9FE07F8FE00E07000F0700070E00078E00039C0003DC0001F80001F80000F000 +00F00000700000F00000F80001F80001DC00039E00038E00070F000707000E07800E03801E03C0 +7F07F0FF8FF87F07F0151C7F9B18>I91 D<600000F00000F00000F800007800007C00003C0000 +3C00003E00001E00001F00000F00000F00000F800007800007C00003C00003C00003E00001E000 +01F00000F00000F800007800007800007C00003C00003E00001E00001E00001F00000F00000F80 +00078000078000030011247D9F18>II<018007C01FF07EFCF83EE00E0F067C9B18>I<7FFF00FF +FF80FFFF807FFF0011047D7F18>I<061E3E387070E0E0E0F8FC7C7C38070E789E18>I<1FE0003F +F8007FFC00781E00300E0000070000070000FF0007FF001FFF007F0700780700E00700E00700E0 +0700F00F00781F003FFFF01FFBF007E1F014147D9318>I<7E0000FE00007E00000E00000E0000 +0E00000E00000E00000E3E000EFF800FFFC00FC1E00F80E00F00700E00700E00380E00380E0038 +0E00380E00380E00380F00700F00700F80E00FC1E00FFFC00EFF80063E00151C809B18>I<01FE +0007FF001FFF803E0780380300700000700000E00000E00000E00000E00000E00000E000007000 +007001C03801C03E03C01FFF8007FF0001FC0012147D9318>I<001F80003F80001F8000038000 +038000038000038000038003E3800FFB801FFF803C1F80380F80700780700380E00380E00380E0 +0380E00380E00380E00380700780700780380F803C1F801FFFF00FFBF803E3F0151C7E9B18>I< +01F00007FC001FFE003E0F00380780700380700380E001C0E001C0FFFFC0FFFFC0FFFFC0E00000 +7000007001C03801C03E03C01FFF8007FF0001FC0012147D9318>I<001F80007FC000FFE000E1 +E001C0C001C00001C00001C0007FFFC0FFFFC0FFFFC001C00001C00001C00001C00001C00001C0 +0001C00001C00001C00001C00001C00001C00001C00001C0007FFF007FFF007FFF00131C7F9B18 +>I<01E1F007FFF80FFFF81E1E301C0E003807003807003807003807003807001C0E001E1E001F +FC001FF80039E0003800001C00001FFE001FFFC03FFFE07801F0700070E00038E00038E00038E0 +00387800F07E03F01FFFC00FFF8001FC00151F7F9318>I<7E0000FE00007E00000E00000E0000 +0E00000E00000E00000E3E000EFF800FFFC00FC1C00F80E00F00E00E00E00E00E00E00E00E00E0 +0E00E00E00E00E00E00E00E00E00E00E00E00E00E07FC3FCFFE7FE7FC3FC171C809B18>I<0380 +0007C00007C00007C0000380000000000000000000000000007FC000FFC0007FC00001C00001C0 +0001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C000FFFF +00FFFF80FFFF00111D7C9C18>I<0038007C007C007C003800000000000000000FFC1FFC0FFC00 +1C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C +001C001C6038F078FFF07FE03F800E277E9C18>II<7FE000FFE0 +007FE00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E0 +0000E00000E00000E00000E00000E00000E00000E00000E00000E00000E0007FFFC0FFFFE07FFF +C0131C7E9B18>I<7CE0E000FFFBF8007FFFF8001F1F1C001E1E1C001E1E1C001C1C1C001C1C1C +001C1C1C001C1C1C001C1C1C001C1C1C001C1C1C001C1C1C001C1C1C001C1C1C001C1C1C007F1F +1F00FFBFBF807F1F1F001914819318>I<7E3E00FEFF807FFFC00FC1C00F80E00F00E00E00E00E +00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E07FC3FCFFE7FE7FC3FC17 +14809318>I<01F0000FFE001FFF003E0F803803807001C07001C0E000E0E000E0E000E0E000E0 +E000E0F001E07001C07803C03C07803E0F801FFF000FFE0001F00013147E9318>I<7E3E00FEFF +807FFFC00FC1E00F80E00F00700E00700E00380E00380E00380E00380E00380E00380F00700F00 +700F80E00FC1E00FFFC00EFF800E3E000E00000E00000E00000E00000E00000E00000E00007FC0 +00FFE0007FC000151E809318>I<01E38007FB801FFF803E1F80380F80700780700780E00380E0 +0380E00380E00380E00380E00380700780700780380F803C1F801FFF800FFB8003E38000038000 +0380000380000380000380000380000380003FF8003FF8003FF8151E7E9318>I<7F87E0FF9FF0 +7FBFF803F87803F03003E00003C00003C000038000038000038000038000038000038000038000 +0380000380007FFE00FFFF007FFE0015147F9318>I<07F7003FFF007FFF00780F00E00700E007 +00E007007C00007FE0001FFC0003FE00001F00600780E00380E00380F00380F80F00FFFF00FFFC +00E7F00011147D9318>I<0180000380000380000380000380007FFFC0FFFFC0FFFFC003800003 +80000380000380000380000380000380000380000380000380400380E00380E00380E001C1C001 +FFC000FF80003E0013197F9818>I<7E07E0FE0FE07E07E00E00E00E00E00E00E00E00E00E00E0 +0E00E00E00E00E00E00E00E00E00E00E00E00E00E00E01E00F03E007FFFC03FFFE01FCFC171480 +9318>I<7F8FF0FF8FF87F8FF01E03C00E03800E03800E0380070700070700070700038E00038E +00038E00038E0001DC0001DC0001DC0000F80000F80000700015147F9318>II<7F8FF07F9FF07F8FF0070700078E00039E00 +01DC0001F80000F80000700000F00000F80001DC00039E00038E000707000F07807F8FF0FF8FF8 +7F8FF015147F9318>I<7F8FF0FF8FF87F8FF00E01C00E03800E03800703800707000707000387 +00038600038E0001CE0001CE0000CC0000CC0000DC000078000078000078000070000070000070 +0000F00000E00079E0007BC0007F80003F00001E0000151E7F9318>I<3FFFF07FFFF07FFFF070 +01E07003C0700780000F00001E00003C0000F80001F00003C0000780000F00701E00703C007078 +0070FFFFF0FFFFF0FFFFF014147F9318>I<0007E0001FE0007FE000780000E00000E00000E000 +00E00000E00000E00000E00000E00000E00000E00000E00001E0007FC000FF8000FF80007FC000 +01E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E000007800 +007FE0001FE00007E013247E9F18>I<60F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0 +F0F0F0F0F0F0F0F0F0F0F0F0600424769F18>I<7C0000FF0000FFC00003C00000E00000E00000 +E00000E00000E00000E00000E00000E00000E00000E00000E00000F000007FC0003FE0003FE000 +7FC000F00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00003 +C000FFC000FF00007C000013247E9F18>I<060C1F1E3FBEFBF8F1F060C00F067C9B18>I +E /Fj 59 123 df<0003E0001C1800381800703C00E03C00E03801C00001C00001C00001C00001 +C0000380007FFFF00380700380700380700380700700E00700E00700E00700E00700E00700E00E +01C00E01C00E01C00E01C00E01C00E01C01C03801E03C0FF0FF816207E9F19>12 +D<381C7C3E7C3E7E3F3A1D040204020402080408041008201040208040100E7A9F17>34 +D<1C3E7E7E3A0202040408081020C0070E7D840D>44 DI<3078F87870 +05057C840D>I<00000400000C00000C0000180000180000300000300000600000600000C00000 +C0000180000180000300000300000600000600000C00000C000018000018000030000030000060 +0000600000C00000C0000180000180000300000300000600000600000600000C00000C00001800 +00180000300000300000600000600000C00000C00000800000162D7EA117>I<000C001C00FC0F +380038003800380038003800700070007000700070007000E000E000E000E000E000E001C001C0 +01C001C001C001C0038003C0FFFE0F1E7C9D17>49 D<003F8000C1E00100F00200780400780400 +780F007C0F807C0F807C0F00780600780000F80000F00001E00001C0000380000700000E00001C +0000380000600000C0000180000300200600200800401000403FFFC07FFF80FFFF80161E7E9D17 +>I<007F000183C00201E00400F00700F00F00F00F01F00F01F00001E00001E00003C000038000 +0700000E0000F800000E000007000007800007C00003C00007C03007C07807C0F807C0F807C0F0 +0780800F00400E00201C0018780007E000141F7D9D17>I<0000600000600000E00001C00003C0 +0005C0000DC00009C00011C000238000438000C380008380010380020380040700080700180700 +100700200700400700FFFFF0000E00000E00000E00000E00000E00001C00001E0001FFE0141E7E +9D17>I<01803001FFE003FFC003FF0003FC00020000020000020000040000040000040000047C +000587000603800C01800801C00001C00001E00001E00001E00001E07003C0F803C0F003C0E003 +80800780400700400E00201C0018700007C000141F7D9D17>I<000F8000704000C0200180E003 +01E00701E00E00C01E00001C00003C000038000078F800790E007A07007C0300F80380F80380F0 +03C0F003C0F003C0F003C0F00780E00780E00780E00700E00F00600E00701C0030180018700007 +C000131F7C9D17>I<2000003FFFE07FFFC07FFF80400100C00200800200800400000800001000 +0020000040000040000080000180000300000300000700000600000E00000E00001E00001C0000 +1C00003C00003C00003C0000780000780000780000300000131F799D17>I<003F0000C1C00100 +600200600400300C00300C00300C00300C00600E00600F80C00FC18007F60003FC0001FC0001FF +00063F800C0F801007C03003C06001C06000C0C000C0C000C0C000C0C00080C001006003003004 +0018180007E000141F7D9D17>I<007E0001C3000301800601C00E01C01C00C03C00E03C00E03C +01E07801E07801E07801E07801E07803E07803E03803C03807C01C0BC00C13C003E38000078000 +0780000700000E00600E00F01C00F01800E0300080600041C0003F0000131F7C9D17>I<000010 +0000001800000038000000380000007800000078000000FC000001BC0000013C0000033C000002 +3C0000063C0000043E0000081E0000081E0000101E0000101E0000201E0000200F0000400F0000 +400F0000FFFF0000800F0001000F8001000780020007800200078004000780040007800C0007C0 +3E0007C0FF807FFC1E207E9F22>65 D<07FFFF00007C01C0003C01E0003C00F0007800F8007800 +F8007800F8007800F8007800F8007800F000F001F000F001E000F003C000F00F8000FFFE0000F0 +0F0001E007C001E003C001E003E001E001E001E001E001E001E003C001E003C003E003C003E003 +C003C003C007C003C00F8007800F0007803E00FFFFF0001D1F7E9E20>I<0001F808000E061800 +380138007000F801E0007803C0007007800030078000300F0000301F0000301E0000303E000020 +3C0000007C0000007C0000007C0000007C000000F8000000F8000000F8000000F8000000F80000 +007800004078000080780000803C0000803C0001001C0002000E00020006000C000300100001C0 +E000003F00001D217B9F21>I<07FFFF00007C01E0003C00F0003C00780078003C0078003C0078 +001E0078001E0078001E0078001F00F0001F00F0001F00F0001F00F0001F00F0001F00F0001F01 +E0001E01E0003E01E0003E01E0003E01E0003C01E0007C03C0007803C000F003C000F003C001E0 +03C003C003C0078007800F0007803C00FFFFE000201F7E9E23>I<07FFFFF8007C0078003C0038 +003C001800780018007800080078000800780008007800080078080800F0100000F0100000F010 +0000F0300000FFF00000F0700001E0200001E0200001E0200001E0200001E0000801E0001003C0 +001003C0001003C0002003C0002003C0006003C000C0078001C0078007C0FFFFFF801D1F7E9E1F +>I<07FFFFF8007C0078003C0038003C0018007800180078000800780008007800080078000800 +78000800F0100000F0100000F0100000F0300000F0700000FFF00001E0600001E0200001E02000 +01E0200001E0200001E0000003C0000003C0000003C0000003C0000003C0000003C00000078000 +0007C00000FFFE00001D1F7E9E1E>I<0001FC04000F030C003C009C0070007C00E0003C01C000 +3803800018078000180F0000181F0000181E0000183E0000103C0000007C0000007C0000007C00 +00007C000000F8000000F8000000F8007FFCF80003E0780001E0780001E0780003C0780003C03C +0003C03C0003C01C0003C00E0007C007000B800380118001E06080003F80001E217B9F24>I<07 +FFC7FFC0007C00F800003C007800003C007800007800F000007800F000007800F000007800F000 +007800F000007800F00000F001E00000F001E00000F001E00000F001E00000FFFFE00000F001E0 +0001E003C00001E003C00001E003C00001E003C00001E003C00001E003C00003C007800003C007 +800003C007800003C007800003C007800003C007800007800F000007C00F8000FFF8FFF800221F +7E9E22>I<07FFE0007C00003C00003C0000780000780000780000780000780000780000F00000 +F00000F00000F00000F00000F00001E00001E00001E00001E00001E00001E00003C00003C00003 +C00003C00003C00003C00007800007C000FFFC00131F7F9E10>I<07FFF000007E0000003C0000 +003C000000780000007800000078000000780000007800000078000000F0000000F0000000F000 +0000F0000000F0000000F0000001E0000001E0000001E0000001E0000001E0008001E0010003C0 +010003C0010003C0030003C0020003C0060003C0060007801E0007807C00FFFFFC00191F7E9E1C +>76 D<07FC0000FFC0007C0000F800003C00017800003C00017800004E0002F000004E0002F000 +004E0004F000004E0004F000004E0008F000004E0008F00000870011E00000870011E000008700 +21E00000870021E00000870041E00000838041E00001038083C00001038083C00001038103C000 +01038203C0000101C203C0000101C403C0000201C40780000201C80780000201C80780000201D0 +0780000200F00780000600E00780000600E00F00000F00C00F8000FFE0C1FFF8002A1F7E9E2A> +I<07FC01FFC0003E003E00003E001800003E001800004F001000004F0010000047801000004780 +10000043C010000043C010000083C020000081E020000081E020000080F020000080F020000080 +782000010078400001007C400001003C400001003C400001001E400001001E400002000F800002 +000F800002000F800002000780000200078000060003800006000300000F00010000FFE0010000 +221F7E9E22>I<0003F800001E0E000038070000E0038001C001C003C001E0078000E00F0000F0 +0F0000F01E0000F01E0000F83E0000F83C0000F87C0000F87C0000F87C0000F87C0000F8F80001 +F0F80001F0F80001F0F80001F0F80003E0780003E0780003C0780007C07C0007803C000F003C00 +1E001E001C000E0038000700F00003C3C00000FE00001D217B9F23>I<07FFFF00007C03C0003C +01E0003C00F0007800F0007800F8007800F8007800F8007800F8007800F000F001F000F001E000 +F003C000F0078000F00F0000FFF80001E0000001E0000001E0000001E0000001E0000001E00000 +03C0000003C0000003C0000003C0000003C0000003C000000780000007C00000FFFC00001D1F7E +9E1F>I<07FFFC00007C0700003C03C0003C01E0007801E0007801F0007801F0007801F0007801 +F0007801E000F003E000F003C000F0078000F00F0000F03C0000FFF00001E0300001E0380001E0 +1C0001E01C0001E01C0001E01E0003C03E0003C03E0003C03E0003C03E0003C03E0003C03E0207 +803E0407C01F04FFFC0F18000003E01F207E9E21>82 D<003F040060CC01803C03801C03001C07 +00180600080E00080E00080E00080E00000F00000F80000FE00007FE0003FF8001FFC0007FE000 +07E00001E00000E00000F00000F04000E04000E04000E04000E06000C0600180E00380F80300C6 +0C0081F80016217D9F19>I<3FFFFFF03C0780F03007803060078030400F0010400F0010C00F00 +10800F0010800F0010800F0010001E0000001E0000001E0000001E0000001E0000001E0000003C +0000003C0000003C0000003C0000003C0000003C00000078000000780000007800000078000000 +7800000078000000F0000001F800007FFFE0001C1F7A9E21>III<03FFC0FFC0007F007E00003E00380000 +1E003000001E002000000F004000000F008000000F81000000078200000007C600000003C40000 +0003E800000001F000000001F000000000F000000000F800000000F8000000017C000000023C00 +0000043C0000000C1E000000081E000000101F000000200F000000400F800000C0078000008007 +C000010003C000070003E0001F8007E000FFE01FFE00221F7F9E22>88 DI<060308041008201020104020402080408040B85CF87CF87CF87C7038100E779F17> +92 D<07F8000C0C001E06001E07001C070000070000070000070000FF0007C7001E07003C0E00 +780E00F00E10F00E10F00E10F01E10F02E20784F401F878014147D9317>97 +D<0700003F00000F00000700000700000E00000E00000E00000E00000E00000E00001C00001C7C +001D87001E03801C01C01C01C03801C03801E03801E03801E03801E03801E07003C07003C07003 +80700780700700700E00E81C00C4380083E00013207B9F19>I<01FC07060E0F1C0F380E780070 +00F000F000F000F000E000E000E000E000F0027004300818300FC010147C9314>I<0000700003 +F00000F00000700000700000E00000E00000E00000E00000E00000E00001C000F9C00305C00E03 +C01C03C03801C0780380700380F00380F00380F00380F00380E00700E00700E00700E00700E007 +00700F00301E00186F000F8FE014207C9F19>I<00F800070E000E07001C070038038078038070 +0380F00380F00380FFFF80F00000E00000E00000E00000E00000F001007002003004001C180007 +E00011147D9314>I<0007800018C00031E00061E000E1C000C00001C00001C00001C00001C000 +01C0000380007FF800038000038000038000038000070000070000070000070000070000070000 +0E00000E00000E00000E00000E00000E00001C00001E0000FFE00013207E9F0E>I<00000E003E +1100E1A301C1C20381E00780E00701E00F01E00F01E00F01E00703C007038007870004FC000800 +000800001800001C00000FFF000FFFC007FFE01800F0300030600030C00030C00030C000306000 +603000C01C070007FC00181F809417>I<00E00007E00001E00000E00000E00001C00001C00001 +C00001C00001C00001C000038000038F800390E003A0E003C0600380600780E00700E00700E007 +00E00700E00700E00E01C00E01C00E01C00E01C00E01C00E01C01C03801E03C0FFCFF815207E9F +19>I<01C003E003E003C0018000000000000000000000000003801F8007800380038007000700 +07000700070007000E000E000E000E000E000E001C001E00FF800B1F7F9E0C>I<00E007E001E0 +00E000E001C001C001C001C001C001C00380038003800380038003800700070007000700070007 +000E000E000E000E000E000E001C001E00FFC00B207F9F0C>108 D<0387C07C001F9861860007 +A072070003C0340300038038030007807807000700700700070070070007007007000700700700 +07007007000E00E00E000E00E00E000E00E00E000E00E00E000E00E00E000E00E00E001C01C01C +001E01E01E00FFCFFCFFC022147E9326>I<038F801F90E007A0E003C0600380600780E00700E0 +0700E00700E00700E00700E00E01C00E01C00E01C00E01C00E01C00E01C01C03801E03C0FFCFF8 +15147E9319>I<00FC000387000E01801C00C03800E03800E07000F0F000F0F000F0F000F0F000 +F0E001E0E001E0E001C0E003C0F00380700700380E001C1C0007E00014147D9317>I<00E3E007 +EC3800F01C00E01E00E00E01C00E01C00F01C00F01C00F01C00F01C00F03801E03801E03801C03 +803C0380380380700740E00721C0071F000700000700000700000E00000E00000E00000E00001E +0000FFC000181D809319>I<038E001FB38007C78003C780038300078000070000070000070000 +0700000700000E00000E00000E00000E00000E00000E00001C00001E0000FFE00011147E9312> +114 D<01F2060E080618061802380438001E001FE00FF003F8003C401C400C400C600C6018E010 +D0608FC00F147E9312>I<0080010001000100030007000F001E00FFF80E000E000E000E001C00 +1C001C001C001C001C00380038203820382038203840384018800F000D1C7C9B12>I<1C0380FC +1F803C07801C03801C0380380700380700380700380700380700380700700E00700E00700E0070 +0E00701E00701E00703C00305E001F9FC012147B9319>II<1FF0FF03C07801C06001C04000E08000E180007300007600003C +00003C00001C00002E00004E000087000107000203800603800C01C03E03E0FF07FC18147F9318 +>120 D<0FF83F8001E00E0001C00C0001C0080000E0180000E0100000E0200000E0200000F040 +000070400000708000007080000071000000390000003A0000003E0000003C0000003800000018 +0000001000000010000000200000002000000040000070C00000F0800000F1000000E20000007C +000000191D809318>I<07FFE00701E00401C00C0380080700080E00101C000038000070000070 +0000E00001C0000380800700800E00801C01001C0100380300700E00FFFE0013147F9314>I +E /Fk 55 123 df<00FC7C0183C607078E0607040E07000E07000E07000E07000E07000E0700FF +FFF00E07000E07000E07000E07000E07000E07000E07000E07000E07000E07000E07000E07000E +07000E07007F0FF0171A809916>11 D<00FC000182000703000607000E02000E00000E00000E00 +000E00000E0000FFFF000E07000E07000E07000E07000E07000E07000E07000E07000E07000E07 +000E07000E07000E07000E07007F0FE0131A809915>I<60F0F0F0F0F0F0606060606060606060 +60200000000060F0F060041A7D990B>33 D<60C0F1E0F9F068D0081008100810102010202040C1 +800C0B7F9913>I<60F0F868080808101020C0050B7D990B>39 D<00800100020004000C000800 +18003000300030006000600060006000E000E000E000E000E000E000E000E000E000E000600060 +0060006000300030003000180008000C00040002000100008009267D9B0F>I<80004000200010 +00180008000C000600060006000300030003000300038003800380038003800380038003800380 +038003000300030003000600060006000C0008001800100020004000800009267E9B0F>I<60F0 +F07010101020204080040B7D830B>44 DI<60F0F06004047D830B>I<01 +E006100C1818383038300070006000E000E7C0E860F030F018E018E01CE01CE01C601C601C7018 +30183030186007C00E187E9713>54 D<078018603030201860186018601870103C303E600F8007 +C019F030F86038401CC00CC00CC00CC00C6008201018600FC00E187E9713>56 +D<60F0F060000000000000000060F0F06004107D8F0B>58 D<60F0F060000000000000000060F0 +F0701010102020408004177D8F0B>I<000C0000000C0000000C0000001E0000001E0000003F00 +0000270000002700000043800000438000004380000081C0000081C0000081C0000100E0000100 +E00001FFE000020070000200700006007800040038000400380008001C0008001C001C001E00FF +00FFC01A1A7F991D>65 DI<003F0201C0C603002E0E001E1C000E1C0006380006 +780002700002700002F00000F00000F00000F00000F00000F00000700002700002780002380004 +1C00041C00080E000803003001C0C0003F00171A7E991C>I70 +D72 DI76 +DII80 D82 D<0FC21836200E6006C006C002C002C002E00070007E003FE01FF807FC003E000E0007 +0003800380038003C002C006E004D81887E0101A7E9915>I<7FFFFF00701C0700401C0100401C +0100C01C0180801C0080801C0080801C0080001C0000001C0000001C0000001C0000001C000000 +1C0000001C0000001C0000001C0000001C0000001C0000001C0000001C0000001C0000001C0000 +001C0000001C000003FFE000191A7F991C>I87 +D<1830204040804080810081008100B160F9F078F030600C0B7B9913>92 +D<3F8070C070E020700070007007F01C7030707070E070E071E071E0F171FB1E3C10107E8F13> +97 DI<07F80C1C381C30087000E000E000E000E000E000E0007000300438080C +1807E00E107F8F11>I<007E00000E00000E00000E00000E00000E00000E00000E00000E00000E +0003CE000C3E00380E00300E00700E00E00E00E00E00E00E00E00E00E00E00E00E00600E00700E +00381E001C2E0007CFC0121A7F9915>I<07C01C3030187018600CE00CFFFCE000E000E000E000 +6000300438080C1807E00E107F8F11>I<01F0031807380E100E000E000E000E000E000E00FFC0 +0E000E000E000E000E000E000E000E000E000E000E000E000E000E007FE00D1A80990C>I<0FCE +187330307038703870387038303018602FC02000600070003FF03FFC1FFE600FC003C003C003C0 +036006381C07E010187F8F13>II<18003C003C00180000000000000000000000 +0000FC001C001C001C001C001C001C001C001C001C001C001C001C001C001C00FF80091A80990A +>I<018003C003C001800000000000000000000000000FC001C001C001C001C001C001C001C001 +C001C001C001C001C001C001C001C001C001C001C041C0E180E3007E000A2182990C>IIIII<07E01C38300C +700E6006E007E007E007E007E007E0076006700E381C1C3807E010107F8F13>II<03C2000C2600381E +00300E00700E00E00E00E00E00E00E00E00E00E00E00E00E00700E00700E00381E001C2E0007CE +00000E00000E00000E00000E00000E00000E00007FC012177F8F14>II<1F2060E04020C020C020 +F0007F003FC01FE000F080708030C030C020F0408F800C107F8F0F>I<0400040004000C000C00 +1C003C00FFC01C001C001C001C001C001C001C001C001C201C201C201C201C200E4003800B177F +960F>IIIIII<7FF86070407040E041C041C00380070007000E081C081C08381070107030FFF00D107F +8F11>I E /Fl 10 58 df<1F00318060C04040C060C060C060C060C060C060C060C060404060C0 +31801F000B107F8F0F>48 D<0C003C00CC000C000C000C000C000C000C000C000C000C000C000C +000C00FF8009107E8F0F>I<1F00618040C08060C0600060006000C00180030006000C00102020 +207FC0FFC00B107F8F0F>I<1F00218060C060C000C0008001800F00008000400060C060C06080 +4060801F000B107F8F0F>I<0300030007000F000B001300330023004300C300FFE00300030003 +0003001FE00B107F8F0F>I<20803F002C002000200020002F0030802040006000600060C06080 +C061801F000B107F8F0F>I<0780184030C060C06000C000CF00F080E040C060C060C060406060 +C030801F000B107F8F0F>I<40007FE07FC08080808001000200040004000C0008000800180018 +001800180018000B117E900F>I<1F00318060C060C060C071803F000F00338061C0C060C060C0 +60404060801F000B107F8F0F>I<1F00318060C0C040C060C060C06040E021E01E600060004060 +C0608043003E000B107F8F0F>I E /Fm 11 58 df<003000003000003000003000003000003000 +003000003000003000003000003000FFFFFCFFFFFC003000003000003000003000003000003000 +00300000300000300000300000300016187E931B>43 D<07C018303018701C600C600CE00EE00E +E00EE00EE00EE00EE00EE00EE00E600C600C701C30181C7007C00F157F9412>48 +D<03000700FF000700070007000700070007000700070007000700070007000700070007000700 +07007FF00C157E9412>I<0F8030E040708030C038E0384038003800700070006000C001800300 +06000C08080810183FF07FF0FFF00D157E9412>I<0FE030306018701C701C001C001800380060 +07E000300018000C000E000EE00EE00EC00C401830300FE00F157F9412>I<00300030007000F0 +01F001700270047008701870107020704070C070FFFE0070007000700070007003FE0F157F9412 +>I<20303FE03FC0240020002000200020002F8030E020700030003800384038E038E038803040 +6020C01F000D157E9412>I<01F00608080C181C301C70006000E000E3E0EC30F018F00CE00EE0 +0EE00E600E600E300C3018183007C00F157F9412>I<40007FFE7FFC7FF8C00880108020004000 +8000800100010003000200060006000E000E000E000E000E0004000F167E9512>I<07E0183020 +18600C600C700C78183E101F600FC00FF018F8607C601EC00EC006C006C004600C38300FE00F15 +7F9412>I<07C0183030186018E00CE00CE00EE00EE00E601E301E186E0F8E000E000C001C7018 +7018603020C01F800F157F9412>I E /Fn 9 121 df0 +D<01800180018001800180C183F18F399C0FF003C003C00FF0399CF18FC1830180018001800180 +018010147D9417>3 D<03C00FF01FF83FFC7FFE7FFEFFFFFFFFFFFFFFFFFFFFFFFF7FFE7FFE3F +FC1FF80FF003C010127D9317>15 D17 D21 D<000002000000000300000000030000000001 +8000000000C000000000C00000000060007FFFFFF000FFFFFFFC000000000E0000000003800000 +0001F0000000007C00000000F000000003C000000007000000000C00FFFFFFF8007FFFFFF00000 +00006000000000C00000000180000000018000000003000000000300000000020000261A7D972D +>41 D<400001C0000360000660000660000630000C30000C30000C1800181800181800180FFFF0 +0FFFF00C00300600600600600600600300C00300C001818001818001818000C30000C30000C300 +006600006600006600003C00003C00003C000018000018001821809F19>56 +D<00040000000C0000000C0000000C0000000C0000000C0000000C0000000C0000000C0000000C +0000000C0000000C0000000C0000000C0000000C0000000C0000000C0000000C0000000C000000 +0C0000000C0000000C0000000C0000000C0000000C0000000C0000FFFFFFE0FFFFFFE01B1C7C9B +23>63 D<0F80184030207010E030E070E020E000E00060007000300018000600198030C0706060 +70E030E038E038E03860387030307018600CC0030000C000600070003000380038203870386038 +4070206010C00F800D297D9F14>120 D E /Fo 50 123 df<00003F03E00000C386700001878C +F00003879CF00003031860000700380000070038000007003800000E003800000E007000000E00 +7000000E00700000FFFFFF80001C007000001C00E000001C00E000001C00E000001C00E0000038 +00E000003801C000003801C000003801C000003801C000007001C0000070038000007003800000 +700380000070038000006003800000E007000000E007000000E007000000E007000000C0060000 +01C00E000001C00E000031860C0000798F180000F31E100000620C6000003C07C000002429829F +1C>11 D<00003FE00000E010000180380003807800030078000700300007000000070000000700 +00000E0000000E0000000E000000FFFFE0000E00E0001C01C0001C01C0001C01C0001C01C0001C +03800038038000380380003803800038070000380700007007000070071000700E2000700E2000 +700E2000E00E2000E0064000E0038000E0000000C0000001C0000001C000003180000079800000 +F3000000620000003C0000001D29829F1A>I<1C3C3C3C3C040408081020204080060E7D840E> +44 D<7FF0FFE07FE00C037D8A10>I<70F8F8F0E005057B840E>I<00000040000000C000000180 +000001800000030000000300000006000000060000000C00000018000000180000003000000030 +00000060000000C0000000C0000001800000018000000300000003000000060000000C0000000C +0000001800000018000000300000003000000060000000C0000000C00000018000000180000003 +00000003000000060000000C0000000C0000001800000018000000300000003000000060000000 +C0000000C0000000800000001A2D7FA117>I<000F800030E000E07001C0700380300380380700 +380F00780F00780E00781E00781E00703C00F03C00F03C00F03C00F07801E07801E07801E07801 +C07003C0F003C0F00380F00780F00700700700700E00701C003038001870000FC000151F7C9D17 +>I<000200020006000E003C00DC031C001C0038003800380038007000700070007000E000E000 +E000E001C001C001C001C003800380038003800780FFF80F1E7B9D17>I<001F000061800080E0 +0100E00200700220700420700410700820F00820F00820F00840E00881E00703C0000380000700 +000C000018000060000080000300000400000800401000401000802001807E030047FF0041FE00 +80FC00807800141F7C9D17>I<070F1F1F0E0000000000000000000070F8F8F0E008147B930E> +58 D<00000200000006000000060000000E0000001E0000001E0000003F0000002F0000004F00 +00004F0000008F0000010F0000010F0000020F0000020F0000040F00000C0F0000080F0000100F +0000100F0000200F80003FFF800040078000C00780008007800100078001000780020007800200 +0780060007801E000F80FF807FF81D207E9F22>65 D<0000FE0200078186001C004C0038003C00 +60003C00C0001C01C0001803800018070000180F0000181E0000101E0000103C0000003C000000 +78000000780000007800000078000000F0000000F0000000F0000000F0000000F0000080700000 +8070000080700001003800010038000200180004000C001800060020000381C00000FE00001F21 +7A9F21>67 D<01FFFFFC001E0038001E0018001E0008001E0008003C0008003C0008003C000800 +3C00080078001000780800007808000078080000F0100000F0300000FFF00000F0300001E02000 +01E0200001E0200001E0200003C0000003C0000003C0000003C000000780000007800000078000 +00078000000F800000FFF800001E1F7D9E1E>70 D<0000FC040007030C001C00980030007800E0 +007801C000380380003003800030070000300E0000301E0000201E0000203C0000003C00000078 +000000780000007800000078000000F0000000F000FFF0F0000780F0000780F0000F0070000F00 +70000F0070000F0070001E0038001E0018003E001C002E000E00CC000383040000FC00001E217A +9F23>I<01FFF3FFE0001F003E00001E003C00001E003C00001E003C00003C007800003C007800 +003C007800003C007800007800F000007800F000007800F000007800F00000F001E00000FFFFE0 +0000F001E00000F001E00001E003C00001E003C00001E003C00001E003C00003C007800003C007 +800003C007800003C007800007800F000007800F000007800F000007800F00000F801F0000FFF1 +FFE000231F7D9E22>I<01FFF0001F00001E00001E00001E00003C00003C00003C00003C000078 +0000780000780000780000F00000F00000F00000F00001E00001E00001E00001E00003C00003C0 +0003C00003C0000780000780000780000780000F8000FFF800141F7D9E12>I<01FFF800001F00 +00001E0000001E0000001E0000003C0000003C0000003C0000003C000000780000007800000078 +00000078000000F0000000F0000000F0000000F0000001E0000001E0000001E0000001E0008003 +C0010003C0010003C0030003C00200078006000780060007800C0007801C000F007800FFFFF800 +191F7D9E1D>76 D<01FE00007FC0001E0000FC00001E0000F80000170001780000170001780000 +270002F00000270004F00000270004F00000270008F00000470009E00000470011E00000470021 +E00000470021E00000870043C00000838043C00000838083C00000838083C00001038107800001 +03820780000103820780000103840780000203840F00000203880F00000203900F00000203900F +00000401E01E00000401E01E00000401C01E00000C01801E00001C01803E0000FF8103FFC0002A +1F7D9E29>I<01FF007FE0001F000F00001F0004000017800400001780040000278008000023C0 +08000023C008000023C008000041E010000041E010000041F010000040F010000080F020000080 +7820000080782000008078200001003C400001003C400001003C400001001E400002001E800002 +001E800002000F800002000F800004000F0000040007000004000700000C000700001C00020000 +FF80020000231F7D9E22>I<0001FC0000070700001C01C0003000E000E0006001C00070038000 +7007800038070000380E0000381E0000381C0000383C0000383C00003878000078780000787800 +007878000078F00000F0F00000F0F00000E0F00001E0F00001C0F00003C0700003807000070078 +000F0038001E0038003C001C0070000E00E0000783800001FC00001D217A9F23>I<01FFFF8000 +1E00E0001E0070001E0038001E003C003C003C003C003C003C003C003C003C0078007800780078 +007800F0007800E000F003C000F00F0000FFFC0000F0000001E0000001E0000001E0000001E000 +0003C0000003C0000003C0000003C00000078000000780000007800000078000000F800000FFF0 +00001E1F7D9E1F>I<0007E040001C18C0003005800060038000C0038001C00180018001000380 +010003800100038001000380000003C0000003C0000003F8000001FF800001FFE000007FF00000 +1FF0000001F8000000780000007800000038000000380020003800200038002000300060007000 +600060006000E0007000C000E8038000C606000081F800001A217D9F1A>83 +D<0FFFFFF01E0780E0180780201007802020078020200F0020600F0020400F0020400F0020801E +0040001E0000001E0000001E0000003C0000003C0000003C0000003C0000007800000078000000 +7800000078000000F0000000F0000000F0000000F0000001E0000001E0000001E0000001E00000 +03E00000FFFF00001C1F789E21>I86 +D<00F1800389C00707800E03801C03803C0380380700780700780700780700F00E00F00E00F00E +00F00E20F01C40F01C40703C40705C40308C800F070013147C9317>97 D<07803F800700070007 +0007000E000E000E000E001C001C001CF01D0C3A0E3C0E380F380F700F700F700F700FE01EE01E +E01EE01CE03CE038607060E031C01F0010207B9F15>I<007E0001C1000300800E07801E07801C +07003C0200780000780000780000F00000F00000F00000F00000F0000070010070020030040018 +380007C00011147C9315>I<0000780003F80000700000700000700000700000E00000E00000E0 +0000E00001C00001C000F1C00389C00707800E03801C03803C0380380700780700780700780700 +F00E00F00E00F00E00F00E20F01C40F01C40703C40705C40308C800F070015207C9F17>I<007C +01C207010E011C013C013802780C7BF07C00F000F000F000F0007000700170023804183807C010 +147C9315>I<00007800019C00033C00033C000718000700000700000E00000E00000E00000E00 +000E0001FFE0001C00001C00001C00001C00003800003800003800003800003800007000007000 +00700000700000700000700000E00000E00000E00000E00000C00001C00001C000018000318000 +7B0000F300006600003C00001629829F0E>I<003C6000E27001C1E00380E00700E00F00E00E01 +C01E01C01E01C01E01C03C03803C03803C03803C03803C07003C07001C0F001C17000C2E0003CE +00000E00000E00001C00001C00301C00783800F0700060E0003F8000141D7E9315>I<01E0000F +E00001C00001C00001C00001C000038000038000038000038000070000070000071E000763000E +81800F01C00E01C00E01C01C03801C03801C03801C0380380700380700380700380E10700E2070 +0C20701C20700C40E00CC060070014207D9F17>I<00C001E001E001C000000000000000000000 +000000000E003300230043804300470087000E000E000E001C001C001C00384038803080708031 +0033001C000B1F7C9E0E>I<0001800003C00003C0000380000000000000000000000000000000 +000000000000003C00004600008700008700010700010700020E00000E00000E00000E00001C00 +001C00001C00001C0000380000380000380000380000700000700000700000700000E00000E000 +30E00079C000F180006300003C00001228829E0E>I<01E0000FE00001C00001C00001C00001C0 +000380000380000380000380000700000700000703C00704200E08E00E11E00E21E00E40C01C80 +001D00001E00001FC00038E000387000387000383840707080707080707080703100E03100601E +0013207D9F15>I<03C01FC0038003800380038007000700070007000E000E000E000E001C001C +001C001C0038003800380038007000700070007100E200E200E200E200640038000A207C9F0C> +I<1C0F80F0002630C318004740640C004780680E004700700E004700700E008E00E01C000E00E0 +1C000E00E01C000E00E01C001C01C038001C01C038001C01C038001C01C0708038038071003803 +806100380380E10038038062007007006600300300380021147C9325>I<1C0F802630C0474060 +4780604700704700708E00E00E00E00E00E00E00E01C01C01C01C01C01C01C0384380388380308 +3807083803107003303001C016147C931A>I<007C0001C3000301800E01C01E01C01C01E03C01 +E07801E07801E07801E0F003C0F003C0F003C0F00780F00700700F00700E0030180018700007C0 +0013147C9317>I<01C1E002621804741C04781C04701E04701E08E01E00E01E00E01E00E01E01 +C03C01C03C01C03C01C0380380780380700380E003C1C0072380071E000700000700000E00000E +00000E00000E00001C00001C0000FFC000171D809317>I<00F0400388C00705800E03801C0380 +3C0380380700780700780700780700F00E00F00E00F00E00F00E00F01C00F01C00703C00705C00 +30B8000F380000380000380000700000700000700000700000E00000E0000FFE00121D7C9315> +I<1C1E002661004783804787804707804703008E00000E00000E00000E00001C00001C00001C00 +001C000038000038000038000038000070000030000011147C9313>I<00FC030206010C030C07 +0C060C000F800FF007F803FC003E000E700EF00CF00CE008401020601F8010147D9313>I<0180 +01C0038003800380038007000700FFF007000E000E000E000E001C001C001C001C003800380038 +003820704070407080708031001E000C1C7C9B0F>I<0E00C03300E02301C04381C04301C04701 +C08703800E03800E03800E03801C07001C07001C07001C07101C0E20180E20180E201C1E200C26 +4007C38014147C9318>I<0E03803307802307C04383C04301C04700C08700800E00800E00800E +00801C01001C01001C01001C02001C02001C04001C04001C08000E300003C00012147C9315>I< +0E00C1C03300E3C02301C3E04381C1E04301C0E04701C060870380400E0380400E0380400E0380 +401C0700801C0700801C0700801C0701001C0701001C0602001C0F02000C0F04000E13080003E1 +F0001B147C931E>I<0383800CC4401068E01071E02071E02070C040E00000E00000E00000E000 +01C00001C00001C00001C040638080F38080F38100E5810084C60078780013147D9315>I<0E00 +C03300E02301C04381C04301C04701C08703800E03800E03800E03801C07001C07001C07001C07 +001C0E00180E00180E001C1E000C3C0007DC00001C00001C00003800F03800F07000E06000C0C0 +004380003E0000131D7C9316>I<01C04003E08007F1800C1F0008020000040000080000100000 +20000040000080000100000200000401000802001002003E0C0063FC0041F80080E00012147D93 +13>I E /Fp 81 125 df<001F83E000F06E3001C078780380F8780300F0300700700007007000 +0700700007007000070070000700700007007000FFFFFF80070070000700700007007000070070 +000700700007007000070070000700700007007000070070000700700007007000070070000700 +7000070070000700700007007000070070007FE3FF001D20809F1B>11 D<003F0000E0C001C0C0 +0381E00701E00701E0070000070000070000070000070000070000FFFFE00700E00700E00700E0 +0700E00700E00700E00700E00700E00700E00700E00700E00700E00700E00700E00700E00700E0 +0700E00700E07FC3FE1720809F19>I<003FE000E0E001C1E00381E00700E00700E00700E00700 +E00700E00700E00700E00700E0FFFFE00700E00700E00700E00700E00700E00700E00700E00700 +E00700E00700E00700E00700E00700E00700E00700E00700E00700E00700E07FE7FE1720809F19 +>I<001F81F80000F04F040001C07C06000380F80F000300F00F000700F00F0007007000000700 +7000000700700000070070000007007000000700700000FFFFFFFF000700700700070070070007 +007007000700700700070070070007007007000700700700070070070007007007000700700700 +070070070007007007000700700700070070070007007007000700700700070070070007007007 +007FE3FE3FF02420809F26>I<70F8F8F8F8F8F8F8707070707070707070702020202020000000 +000070F8F8F87005217CA00D>33 D<7038F87CFC7EFC7E743A0402040204020804080410081008 +201040200F0E7E9F17>I<70F8FCFC74040404080810102040060E7C9F0D>39 +D<0020004000800100020006000C000C00180018003000300030007000600060006000E000E000 +E000E000E000E000E000E000E000E000E000E0006000600060007000300030003000180018000C +000C000600020001000080004000200B2E7DA112>I<800040002000100008000C000600060003 +00030001800180018001C000C000C000C000E000E000E000E000E000E000E000E000E000E000E0 +00E000C000C000C001C001800180018003000300060006000C00080010002000400080000B2E7D +A112>I<70F8FCFC74040404080810102040060E7C840D>44 DI<70F8F8 +F87005057C840D>I<000100030003000600060006000C000C000C001800180018003000300030 +00600060006000C000C000C00180018001800300030003000600060006000C000C000C00180018 +001800300030003000600060006000C000C000C000102D7DA117>I<03F0000E1C001C0E001806 +00380700700380700380700380700380F003C0F003C0F003C0F003C0F003C0F003C0F003C0F003 +C0F003C0F003C0F003C0F003C0F003C07003807003807003807807803807001806001C0E000E1C +0003F000121F7E9D17>I<018003800F80F3800380038003800380038003800380038003800380 +0380038003800380038003800380038003800380038003800380038007C0FFFE0F1E7C9D17>I< +03F0000C1C00100E00200700400780800780F007C0F803C0F803C0F803C02007C00007C0000780 +000780000F00000E00001C0000380000700000600000C0000180000300000600400C0040180040 +1000803FFF807FFF80FFFF80121E7E9D17>I<03F0000C1C00100E00200F00780F807807807807 +80380F80000F80000F00000F00000E00001C0000380003F000003C00000E00000F000007800007 +800007C02007C0F807C0F807C0F807C0F00780400780400F00200E001C3C0003F000121F7E9D17 +>I<000600000600000E00000E00001E00002E00002E00004E00008E00008E00010E00020E0002 +0E00040E00080E00080E00100E00200E00200E00400E00C00E00FFFFF0000E00000E00000E0000 +0E00000E00000E00000E0000FFE0141E7F9D17>I<1803001FFE001FFC001FF8001FE000100000 +10000010000010000010000010000011F000161C00180E001007001007800003800003800003C0 +0003C00003C07003C0F003C0F003C0E00380400380400700200600100E000C380003E000121F7E +9D17>I<007C000182000701000E03800C07801C0780380300380000780000700000700000F1F0 +00F21C00F40600F80700F80380F80380F003C0F003C0F003C0F003C0F003C07003C07003C07003 +803803803807001807000C0E00061C0001F000121F7E9D17>I<4000007FFFC07FFF807FFF8040 +010080020080020080040000080000080000100000200000200000400000400000C00000C00001 +C00001800003800003800003800003800007800007800007800007800007800007800007800003 +0000121F7D9D17>I<03F0000C0C00100600300300200180600180600180600180700180780300 +3E03003F06001FC8000FF00003F80007FC000C7E00103F00300F806003804001C0C001C0C000C0 +C000C0C000C0C000806001802001001002000C0C0003F000121F7E9D17>I<03F0000E18001C0C +00380600380700700700700380F00380F00380F003C0F003C0F003C0F003C0F003C07007C07007 +C03807C0180BC00E13C003E3C0000380000380000380000700300700780600780E00700C002018 +001070000FC000121F7E9D17>I<70F8F8F8700000000000000000000070F8F8F87005147C930D> +I<70F8F8F8700000000000000000000070F0F8F878080808101010202040051D7C930D>I<7FFF +FFE0FFFFFFF00000000000000000000000000000000000000000000000000000000000000000FF +FFFFF07FFFFFE01C0C7D9023>61 D<0FC0307040384038E03CF03CF03C603C0038007000E000C0 +01800180010003000200020002000200020002000000000000000000000007000F800F800F8007 +000E207D9F15>63 D<000100000003800000038000000380000007C0000007C0000007C0000009 +E0000009E0000009E0000010F0000010F0000010F00000207800002078000020780000403C0000 +403C0000403C0000801E0000801E0000FFFE0001000F0001000F0001000F000200078002000780 +02000780040003C00E0003C01F0007E0FFC03FFE1F207F9F22>65 DI<000FC040007030C001C009C0038005C0070003C00E0001C01E0000C01C0000C03C0000C0 +7C0000407C00004078000040F8000000F8000000F8000000F8000000F8000000F8000000F80000 +00F8000000F8000000780000007C0000407C0000403C0000401C0000401E0000800E0000800700 +01000380020001C0040000703800000FC0001A217D9F21>II< +FFFFFF000F800F0007800300078003000780010007800180078000800780008007800080078080 +800780800007808000078080000781800007FF8000078180000780800007808000078080000780 +8000078000200780002007800020078000400780004007800040078000C0078000C0078001800F +800F80FFFFFF801B1F7E9E1F>II<000FE0200078186000E004 +E0038002E0070001E00F0000E01E0000601E0000603C0000603C0000207C00002078000020F800 +0000F8000000F8000000F8000000F8000000F8000000F8000000F8007FFCF80003E0780001E07C +0001E03C0001E03C0001E01E0001E01E0001E00F0001E0070001E0038002E000E0046000781820 +000FE0001E217D9F24>III<0FFFC0007C00003C00003C00003C00003C00003C00003C +00003C00003C00003C00003C00003C00003C00003C00003C00003C00003C00003C00003C00003C +00003C00003C00203C00F83C00F83C00F83C00F0380040780040700030E0000F800012207E9E17 +>IIIII<001F800000F0F00001C0380007801E000F000F00 +0E0007001E0007803C0003C03C0003C07C0003E0780001E0780001E0F80001F0F80001F0F80001 +F0F80001F0F80001F0F80001F0F80001F0F80001F0F80001F0780001E07C0003E07C0003E03C00 +03C03C0003C01E0007800E0007000F000F0007801E0001C0380000F0F000001F80001C217D9F23 +>II82 +D<07E0800C1980100780300380600180600180E00180E00080E00080E00080F00000F000007800 +007F00003FF0001FFC000FFE0003FF00001F800007800003C00003C00001C08001C08001C08001 +C08001C0C00180C00380E00300F00600CE0C0081F80012217D9F19>I<7FFFFFE0780F01E0600F +0060400F0020400F0020C00F0030800F0010800F0010800F0010800F0010000F0000000F000000 +0F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000 +000F0000000F0000000F0000000F0000000F0000000F0000000F0000001F800007FFFE001C1F7E +9E21>II +II89 D91 +D<080410082010201040204020804080408040B85CFC7EFC7E7C3E381C0F0E7B9F17>II<1FE000303000781800781C00300E00000E00000E00000E0000FE00078E +001E0E00380E00780E00F00E10F00E10F00E10F01E10781E103867200F83C014147E9317>97 +D<0E0000FE00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E3E +000EC3800F01C00F00E00E00E00E00700E00700E00780E00780E00780E00780E00780E00780E00 +700E00700E00E00F00E00D01C00CC300083E0015207F9F19>I<03F80E0C1C1E381E380C700070 +00F000F000F000F000F000F00070007000380138011C020E0C03F010147E9314>I<000380003F +8000038000038000038000038000038000038000038000038000038000038003E380061B801C07 +80380380380380700380700380F00380F00380F00380F00380F00380F003807003807003803803 +803807801C07800E1B8003E3F815207E9F19>I<03F0000E1C001C0E0038070038070070070070 +0380F00380F00380FFFF80F00000F00000F000007000007000003800801800800C010007060001 +F80011147F9314>I<007C00C6018F038F07060700070007000700070007000700FFF007000700 +07000700070007000700070007000700070007000700070007000700070007007FF01020809F0E +>I<0000E003E3300E3C301C1C30380E00780F00780F00780F00780F00780F00380E001C1C001E +380033E0002000002000003000003000003FFE001FFF800FFFC03001E0600070C00030C00030C0 +0030C000306000603000C01C038003FC00141F7F9417>I<0E0000FE00000E00000E00000E0000 +0E00000E00000E00000E00000E00000E00000E00000E3E000E43000E81800F01C00F01C00E01C0 +0E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C0 +FFE7FC16207F9F19>I<1C001E003E001E001C000000000000000000000000000E007E000E000E +000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E00FFC00A1F809E0C> +I<00E001F001F001F000E0000000000000000000000000007007F000F000700070007000700070 +00700070007000700070007000700070007000700070007000700070007000706070F060F0C061 +803F000C28829E0E>I<0E0000FE00000E00000E00000E00000E00000E00000E00000E00000E00 +000E00000E00000E0FF00E03C00E03000E02000E04000E08000E10000E30000E70000EF8000F38 +000E1C000E1E000E0E000E07000E07800E03800E03C00E03E0FFCFF815207F9F18>I<0E00FE00 +0E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E +000E000E000E000E000E000E000E000E000E00FFE00B20809F0C>I<0E1F01F000FE618618000E +81C81C000F00F00E000F00F00E000E00E00E000E00E00E000E00E00E000E00E00E000E00E00E00 +0E00E00E000E00E00E000E00E00E000E00E00E000E00E00E000E00E00E000E00E00E000E00E00E +000E00E00E00FFE7FE7FE023147F9326>I<0E3E00FE43000E81800F01C00F01C00E01C00E01C0 +0E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C0FFE7FC +16147F9319>I<01F800070E001C03803801C03801C07000E07000E0F000F0F000F0F000F0F000 +F0F000F0F000F07000E07000E03801C03801C01C0380070E0001F80014147F9317>I<0E3E00FE +C3800F01C00F00E00E00E00E00F00E00700E00780E00780E00780E00780E00780E00780E00700E +00F00E00E00F01E00F01C00EC3000E3E000E00000E00000E00000E00000E00000E00000E00000E +0000FFE000151D7F9319>I<03E0800619801C05803C0780380380780380700380F00380F00380 +F00380F00380F00380F003807003807803803803803807801C0B800E138003E380000380000380 +000380000380000380000380000380000380003FF8151D7E9318>I<0E78FE8C0F1E0F1E0F0C0E +000E000E000E000E000E000E000E000E000E000E000E000E000E00FFE00F147F9312>I<1F9030 +704030C010C010C010E00078007F803FE00FF00070803880188018C018C018E030D0608F800D14 +7E9312>I<020002000200060006000E000E003E00FFF80E000E000E000E000E000E000E000E00 +0E000E000E000E080E080E080E080E080610031001E00D1C7F9B12>I<0E01C0FE1FC00E01C00E +01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E01C00E +03C00603C0030DC001F1FC16147F9319>III<7FC3FC0F01E00701C007018003810001C20000E40000EC +00007800003800003C00007C00004E000087000107000303800201C00601E01E01E0FF07FE1714 +809318>II<3FFF380E200E201C40384078407000E001E0 +01C00380078007010E011E011C0338027006700EFFFE10147F9314>III E /Fq 48 122 df<1C0038007F00FE007F00FE00FF81FF00FFC1FF +80FFC1FF807FC0FF807FC0FF801CC0398000C0018000C001800180030001800300018003000300 +06000300060006000C000C00180018003000300060002000400019157EA924>34 +D<1C007F007F00FF80FFC0FFC07FC07FC01CC000C000C00180018001800300030006000C001800 +300020000A157B8813>44 D<00000300000007800000078000000F8000000F0000000F0000001F +0000001E0000003E0000003C0000003C0000007C0000007800000078000000F8000000F0000001 +F0000001E0000001E0000003E0000003C0000003C0000007C00000078000000F8000000F000000 +0F0000001F0000001E0000003E0000003C0000003C0000007C0000007800000078000000F80000 +00F0000001F0000001E0000001E0000003E0000003C0000003C0000007C00000078000000F8000 +000F0000000F0000001F0000001E0000001E0000003E0000003C0000007C000000780000007800 +0000F8000000F0000000F000000060000000193C7CAC22>47 D<003F800001FFF00007E0FC000F +C07E001F803F001F803F003F001F803F001F807F001FC07F001FC07F001FC07F001FC0FF001FE0 +FF001FE0FF001FE0FF001FE0FF001FE0FF001FE0FF001FE0FF001FE0FF001FE0FF001FE0FF001F +E0FF001FE0FF001FE0FF001FE0FF001FE07F001FC07F001FC07F001FC07F001FC03F001F803F00 +1F801F803F001F803F000FC07E0007E0FC0001FFF000003F80001B277DA622>I<000E00001E00 +007E0007FE00FFFE00FFFE00F8FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE00 +00FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE00 +00FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE007FFFFE7FFFFE7FFFFE17277BA622> +I<00FF800003FFF0000FFFFC001F03FE003800FF007C007F80FE003FC0FF003FC0FF003FE0FF00 +1FE0FF001FE07E001FE03C003FE000003FE000003FC000003FC000007F8000007F000000FE0000 +00FC000001F8000003F0000003E00000078000000F0000001E0000003C00E0007000E000E000E0 +01C001C0038001C0070001C00FFFFFC01FFFFFC03FFFFFC07FFFFFC0FFFFFF80FFFFFF80FFFFFF +801B277DA622>I<007F800003FFF00007FFFC000F81FE001F00FF003F80FF003F807F803F807F +803F807F801F807F800F007F800000FF000000FF000000FE000001FC000001F8000007F00000FF +C00000FFF0000001FC0000007E0000007F0000007F8000003FC000003FC000003FE000003FE03C +003FE07E003FE0FF003FE0FF003FE0FF003FC0FF007FC07E007F807C007F003F01FE001FFFFC00 +07FFF00000FF80001B277DA622>I<00000E0000001E0000003E0000007E000000FE000000FE00 +0001FE000003FE0000077E00000E7E00000E7E00001C7E0000387E0000707E0000E07E0000E07E +0001C07E0003807E0007007E000E007E000E007E001C007E0038007E0070007E00E0007E00FFFF +FFF8FFFFFFF8FFFFFFF80000FE000000FE000000FE000000FE000000FE000000FE000000FE0000 +00FE00007FFFF8007FFFF8007FFFF81D277EA622>I<0C0003000F803F000FFFFE000FFFFC000F +FFF8000FFFF0000FFFE0000FFFC0000FFE00000E0000000E0000000E0000000E0000000E000000 +0E0000000E7FC0000FFFF8000F80FC000E003E000C003F0000001F8000001FC000001FC000001F +E000001FE018001FE07C001FE0FE001FE0FE001FE0FE001FE0FE001FC0FC001FC078003F807800 +3F803C007F001F01FE000FFFF80003FFF00000FF80001B277DA622>I<0007F000003FFC0000FF +FE0001FC0F0003F01F8007E03F800FC03F801FC03F801F803F803F801F003F8000007F0000007F +0000007F000000FF000000FF0FC000FF3FF800FF707C00FFC03E00FFC03F00FF801F80FF801FC0 +FF001FC0FF001FE0FF001FE0FF001FE07F001FE07F001FE07F001FE07F001FE03F001FE03F001F +C01F801FC01F803F800FC03F0007E07E0003FFFC0000FFF000003FC0001B277DA622>I<380000 +003E0000003FFFFFF03FFFFFF03FFFFFF07FFFFFE07FFFFFC07FFFFF807FFFFF0070000E007000 +0E0070001C00E0003800E0007000E000E0000000E0000001C00000038000000780000007800000 +0F0000000F0000001F0000001F0000003F0000003E0000003E0000007E0000007E0000007E0000 +007E000000FE000000FE000000FE000000FE000000FE000000FE000000FE000000FE0000007C00 +00003800001C297CA822>I<003FC00001FFF00003FFFC0007C07E000F003F001E001F001E000F +803E000F803E000F803F000F803F000F803FC00F003FF01F001FFC1E001FFE3C000FFFF80007FF +E00003FFF80001FFFC0001FFFE0007FFFF000F0FFF801E03FFC03C01FFC07C007FE078001FE0F8 +0007E0F80007E0F80003E0F80003E0F80003E0F80003C07C0003C07C0007803F000F001FC03E00 +0FFFFC0003FFF800007FC0001B277DA622>I<007F800001FFF00007FFF8000FE0FC001F807E00 +3F803F007F003F007F001F80FF001F80FF001FC0FF001FC0FF001FC0FF001FE0FF001FE0FF001F +E0FF001FE07F001FE07F003FE03F003FE01F807FE00F807FE007C1DFE003FF9FE0007E1FE00000 +1FE000001FC000001FC000001FC000003F801F003F803F803F003F803F003F807E003F807C001F +01F8001E03F0000FFFE00007FF800001FE00001B277DA622>I<000003800000000007C0000000 +0007C0000000000FE0000000000FE0000000000FE0000000001FF0000000001FF0000000003FF8 +000000003FF8000000003FF80000000073FC0000000073FC00000000F3FE00000000E1FE000000 +00E1FE00000001C0FF00000001C0FF00000003C0FF80000003807F80000007807FC0000007003F +C0000007003FC000000E003FE000000E001FE000001E001FF000001C000FF000001FFFFFF00000 +3FFFFFF800003FFFFFF80000780007FC0000700003FC0000700003FC0000E00001FE0000E00001 +FE0001E00001FF0001C00000FF0001C00000FF00FFFE001FFFFEFFFE001FFFFEFFFE001FFFFE2F +297EA834>65 D<00003FF001800003FFFE0380000FFFFF8780003FF007DF8000FF8001FF8001FE +00007F8003FC00003F8007F000001F800FF000000F801FE0000007801FE0000007803FC0000007 +803FC0000003807FC0000003807F80000003807F8000000000FF8000000000FF8000000000FF80 +00000000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF80000000 +007F80000000007F80000000007FC0000003803FC0000003803FC0000003801FE0000003801FE0 +000007000FF00000070007F000000E0003FC00001E0001FE00003C0000FF8000F800003FF007E0 +00000FFFFFC0000003FFFF000000003FF8000029297CA832>67 D69 DI<00007FE003000003FFFC0700001FFFFF +0F00003FF00FFF0000FF8001FF0001FE0000FF0003F800003F0007F000003F000FF000001F001F +E000000F001FE000000F003FC000000F003FC0000007007FC0000007007F80000007007F800000 +0000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF +8000000000FF8000000000FF8001FFFFF87F8001FFFFF87F8001FFFFF87FC00000FF003FC00000 +FF003FC00000FF001FE00000FF001FE00000FF000FF00000FF0007F00000FF0003F80000FF0001 +FE0000FF0000FF8001FF00003FF007BF00001FFFFF1F000003FFFE0F0000007FF003002D297CA8 +36>I73 D +77 D<0000FFE000000007FFFC0000003FC07F8000007F001FC00001FC0007F00003F80003F800 +07F00001FC000FF00001FE001FE00000FF001FE00000FF003FC000007F803FC000007F807FC000 +007FC07F8000003FC07F8000003FC07F8000003FC0FF8000003FE0FF8000003FE0FF8000003FE0 +FF8000003FE0FF8000003FE0FF8000003FE0FF8000003FE0FF8000003FE0FF8000003FE0FF8000 +003FE07F8000003FC07FC000007FC07FC000007FC03FC000007F803FC000007F801FE00000FF00 +1FE00000FF000FF00001FE0007F00001FC0003F80003F80001FC0007F00000FF001FE000003FC0 +7F8000000FFFFE00000000FFE000002B297CA834>79 D +I82 D<7FFFFFFFFFC07FFFFFFFFFC07FFFFFFFFFC0 +7F803FC03FC07E003FC007C078003FC003C078003FC003C070003FC001C0F0003FC001E0F0003F +C001E0E0003FC000E0E0003FC000E0E0003FC000E0E0003FC000E0E0003FC000E000003FC00000 +00003FC0000000003FC0000000003FC0000000003FC0000000003FC0000000003FC0000000003F +C0000000003FC0000000003FC0000000003FC0000000003FC0000000003FC0000000003FC00000 +00003FC0000000003FC0000000003FC0000000003FC0000000003FC0000000003FC0000000003F +C0000000003FC00000007FFFFFE000007FFFFFE000007FFFFFE0002B287EA730>84 +D86 D<010002000300060006000C000C001800 +1800300030006000300060006000C0006000C0006000C000C0018000C0018000CE019C00FF81FF +00FF81FF00FFC1FF80FFC1FF807FC0FF803F807F003F807F000E001C00191578A924>92 +D<01FF800007FFF0000F81F8001FC07E001FC07E001FC03F000F803F8007003F8000003F800000 +3F8000003F80000FFF8000FFFF8007FC3F800FE03F803F803F803F003F807F003F80FE003F80FE +003F80FE003F80FE003F807E007F807F00DF803F839FFC0FFF0FFC01FC03FC1E1B7E9A21>97 +D<001FF80000FFFE0003F01F0007E03F800FC03F801F803F803F801F007F800E007F0000007F00 +0000FF000000FF000000FF000000FF000000FF000000FF000000FF0000007F0000007F0000007F +8000003F8001C01F8001C00FC0038007E0070003F01E0000FFFC00001FE0001A1B7E9A1F>99 +D<00003FF80000003FF80000003FF800000003F800000003F800000003F800000003F800000003 +F800000003F800000003F800000003F800000003F800000003F800000003F800000003F800001F +E3F80000FFFBF80003F03FF80007E00FF8000FC007F8001F8003F8003F8003F8007F0003F8007F +0003F8007F0003F800FF0003F800FF0003F800FF0003F800FF0003F800FF0003F800FF0003F800 +FF0003F8007F0003F8007F0003F8007F0003F8003F8003F8001F8003F8000F8007F80007C00FF8 +0003F03BFF8000FFF3FF80003FC3FF80212A7EA926>I<003FE00001FFF80003F07E0007C01F00 +0F801F801F800F803F800FC07F000FC07F0007C07F0007E0FF0007E0FF0007E0FFFFFFE0FFFFFF +E0FF000000FF000000FF0000007F0000007F0000007F0000003F8000E01F8000E00FC001C007E0 +038003F81F0000FFFE00001FF0001B1B7E9A20>I<0007F0003FFC00FE3E01F87F03F87F03F07F +07F07F07F03E07F00007F00007F00007F00007F00007F00007F000FFFFC0FFFFC0FFFFC007F000 +07F00007F00007F00007F00007F00007F00007F00007F00007F00007F00007F00007F00007F000 +07F00007F00007F00007F00007F00007F00007F0007FFF807FFF807FFF80182A7EA915>I<00FF +81F003FFE7F80FC1FE7C1F80FC7C1F007C383F007E107F007F007F007F007F007F007F007F007F +007F007F007F003F007E001F007C001F80FC000FC1F8001FFFE00018FF80003800000038000000 +3C0000003E0000003FFFF8001FFFFF001FFFFF800FFFFFC007FFFFE01FFFFFF03E0007F07C0001 +F8F80000F8F80000F8F80000F8F80000F87C0001F03C0001E01F0007C00FC01F8003FFFE00007F +F0001E287E9A22>II<07000F801FC03FE0 +3FE03FE01FC00F8007000000000000000000000000000000FFE0FFE0FFE00FE00FE00FE00FE00F +E00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE0FFFEFFFEFFFE +0F2B7DAA14>I107 DIII<003FE00001FFFC0003F07E +000FC01F801F800FC03F800FE03F0007E07F0007F07F0007F07F0007F0FF0007F8FF0007F8FF00 +07F8FF0007F8FF0007F8FF0007F8FF0007F8FF0007F87F0007F07F0007F03F800FE03F800FE01F +800FC00FC01F8007F07F0001FFFC00003FE0001D1B7E9A22>II114 D<03FE300FFFF01E03F03800F0700070F00070F00070F80070FC0000FFE0007FFE00 +7FFF803FFFE01FFFF007FFF800FFF80003FC0000FC60007CE0003CF0003CF00038F80038FC0070 +FF01E0F7FFC0C1FF00161B7E9A1B>I<00700000700000700000700000F00000F00000F00001F0 +0003F00003F00007F0001FFFF0FFFFF0FFFFF007F00007F00007F00007F00007F00007F00007F0 +0007F00007F00007F00007F00007F00007F00007F03807F03807F03807F03807F03807F03803F0 +3803F87001F86000FFC0001F8015267FA51B>IIIIII +E /Fr 33 122 df<70F8F8F87005057C840E>46 D<0001800000018000000180000003C0000003 +C0000003C0000005E0000005E000000DF0000008F0000008F0000010F800001078000010780000 +203C0000203C0000203C0000401E0000401E0000401E0000800F0000800F0000FFFF0001000780 +01000780030007C0020003C0020003C0040003E0040001E0040001E00C0000F00C0000F03E0001 +F8FF800FFF20237EA225>65 D<0007E0100038183000E0063001C00170038000F0070000F00E00 +00701E0000701C0000303C0000303C0000307C0000107800001078000010F8000000F8000000F8 +000000F8000000F8000000F8000000F8000000F800000078000000780000107C0000103C000010 +3C0000101C0000201E0000200E000040070000400380008001C0010000E0020000381C000007E0 +001C247DA223>67 DI70 D72 D<03FFF0001F00000F00000F00000F00000F00000F00000F00000F0000 +0F00000F00000F00000F00000F00000F00000F00000F00000F00000F00000F00000F00000F0000 +0F00000F00000F00000F00700F00F80F00F80F00F80E00F01E00401C0020380018700007C00014 +237EA119>74 D76 +D78 D80 D<03F0200C0C601802603001E07000E0600060E00060E000 +60E00020E00020E00020F00000F000007800007F00003FF0001FFE000FFF0003FF80003FC00007 +E00001E00000F00000F0000070800070800070800070800070C00060C00060E000C0F000C0C801 +80C6070081FC0014247DA21B>83 D85 D89 D<0FE0001838003C0C003C +0E0018070000070000070000070000FF0007C7001E07003C0700780700700700F00708F00708F0 +0708F00F087817083C23900FC1E015157E9418>97 D<0E0000FE00001E00000E00000E00000E00 +000E00000E00000E00000E00000E00000E00000E00000E00000E1F000E61C00E80600F00300E00 +380E003C0E001C0E001E0E001E0E001E0E001E0E001E0E001E0E001E0E001C0E003C0E00380F00 +700C80600C41C0083F0017237FA21B>I<01FE000703000C07801C0780380300780000700000F0 +0000F00000F00000F00000F00000F00000F000007000007800403800401C00800C010007060001 +F80012157E9416>I<0000E0000FE00001E00000E00000E00000E00000E00000E00000E00000E0 +0000E00000E00000E00000E001F8E00704E00C02E01C01E03800E07800E07000E0F000E0F000E0 +F000E0F000E0F000E0F000E0F000E07000E07800E03800E01801E00C02E0070CF001F0FE17237E +A21B>I<01FC000707000C03801C01C03801C07801E07000E0F000E0FFFFE0F00000F00000F000 +00F00000F000007000007800203800201C00400E008007030000FC0013157F9416>I<003C00C6 +018F038F030F070007000700070007000700070007000700FFF807000700070007000700070007 +000700070007000700070007000700070007000700070007807FF8102380A20F>I<0E0000FE00 +001E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E1F +800E60C00E80E00F00700F00700E00700E00700E00700E00700E00700E00700E00700E00700E00 +700E00700E00700E00700E00700E00700E0070FFE7FF18237FA21B>104 +D<1C001E003E001E001C00000000000000000000000000000000000E00FE001E000E000E000E00 +0E000E000E000E000E000E000E000E000E000E000E000E000E000E00FFC00A227FA10E>I<0E00 +00FE00001E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00 +000E03FC0E01F00E01C00E01800E02000E04000E08000E10000E38000EF8000F1C000E1E000E0E +000E07000E07800E03C00E01C00E01E00E00F00E00F8FFE3FE17237FA21A>107 +D<0E00FE001E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E00 +0E000E000E000E000E000E000E000E000E000E000E000E000E000E000E00FFE00B237FA20E>I< +0E1FC07F00FE60E183801E807201C00F003C00E00F003C00E00E003800E00E003800E00E003800 +E00E003800E00E003800E00E003800E00E003800E00E003800E00E003800E00E003800E00E0038 +00E00E003800E00E003800E00E003800E00E003800E0FFE3FF8FFE27157F942A>I<0E1F80FE60 +C01E80E00F00700F00700E00700E00700E00700E00700E00700E00700E00700E00700E00700E00 +700E00700E00700E00700E00700E0070FFE7FF18157F941B>I<01FC000707000C01801800C038 +00E0700070700070F00078F00078F00078F00078F00078F00078F000787000707800F03800E01C +01C00E038007070001FC0015157F9418>I<0E1F00FE61C00E80600F00700E00380E003C0E001C +0E001E0E001E0E001E0E001E0E001E0E001E0E001E0E003C0E003C0E00380F00700E80E00E41C0 +0E3F000E00000E00000E00000E00000E00000E00000E00000E00000E0000FFE000171F7F941B> +I<0E3CFE461E8F0F0F0F060F000E000E000E000E000E000E000E000E000E000E000E000E000E00 +0F00FFF010157F9413>114 D<0F8830786018C018C008C008E008F0007F803FE00FF001F8003C +801C800C800CC00CC008E018D0308FC00E157E9413>I<02000200020002000600060006000E00 +1E003E00FFF80E000E000E000E000E000E000E000E000E000E000E000E040E040E040E040E040E +040708030801F00E1F7F9E13>I<0E0070FE07F01E00F00E00700E00700E00700E00700E00700E +00700E00700E00700E00700E00700E00700E00700E00700E00F00E00F006017003827800FC7F18 +157F941B>II121 D E /Fs 17 118 df<000003000000000003 +00000000000300000000000780000000000780000000000FC0000000000FC0000000000FC00000 +000017E00000000013E00000000013E00000000023F00000000021F00000000021F00000000040 +F80000000040F80000000040F800000000807C00000000807C00000001807E00000001003E0000 +0001003E00000002003F00000002001F00000002001F00000004000F80000004000F8000000400 +0F800000080007C00000080007C00000180007E000001FFFFFE000001FFFFFE00000200003F000 +00200001F00000200001F00000400001F80000400000F80000400000F800008000007C00008000 +007C00008000007C00010000003E00010000003E00030000003F00030000001F00070000001F00 +1F8000003F80FFE00003FFFCFFE00003FFFC2E327EB132>65 D<00001FE000800000FFFC018000 +07F00F0180000F80018380003E0000C38000780000278000F00000178001E000000F8003C00000 +0F800780000007800780000003800F00000003801F00000001801E00000001803E00000001803C +00000001803C00000000807C00000000807C0000000080780000000000F80000000000F8000000 +0000F80000000000F80000000000F80000000000F80000000000F80000000000F80000000000F8 +0000000000F80000000000F800000FFFFC7800000FFFFC7C0000001FC07C0000000F803C000000 +0F803C0000000F803E0000000F801E0000000F801F0000000F800F0000000F80078000000F8007 +C000000F8003C000000F8001E000000F8000F000001780007C00001780003E00006380000F8000 +C3800007F00781800000FFFE008000001FF000002E337CB134>71 DII<00FE00000303C0000C00E00010007000100038003C003C00 +3E001C003E001E003E001E0008001E0000001E0000001E0000001E00000FFE0000FC1E0003E01E +000F801E001F001E003E001E003C001E007C001E00F8001E04F8001E04F8001E04F8003E04F800 +3E0478003E047C005E043E008F080F0307F003FC03E01E1F7D9E21>97 D<003F8000E060038018 +0700040F00041E001E1C003E3C003E7C003E7C0008780000F80000F80000F80000F80000F80000 +F80000F80000F80000F800007800007C00007C00003C00011E00011E00020F0002070004038018 +00E060003F80181F7D9E1D>99 D<000001E000003FE000003FE0000003E0000001E0000001E000 +0001E0000001E0000001E0000001E0000001E0000001E0000001E0000001E0000001E0000001E0 +000001E0000001E0000001E0001F81E000F061E001C019E0078005E00F0003E00E0003E01E0001 +E03C0001E03C0001E07C0001E0780001E0F80001E0F80001E0F80001E0F80001E0F80001E0F800 +01E0F80001E0F80001E0F80001E0780001E0780001E03C0001E03C0001E01C0001E01E0003E00E +0005E0070009E0038011F000E061FF003F81FF20327DB125>I<003F800000E0E0000380380007 +003C000E001E001E001E001C000F003C000F007C000F0078000F8078000780F8000780F8000780 +FFFFFF80F8000000F8000000F8000000F8000000F8000000F8000000780000007C0000003C0000 +003C0000801E0000800E0001000F0002000780020001C00C0000F03000001FC000191F7E9E1D> +I<07000F801F801F800F800700000000000000000000000000000000000000000000000780FF80 +FF800F800780078007800780078007800780078007800780078007800780078007800780078007 +800780078007800780078007800FC0FFF8FFF80D307EAF12>105 D<07800000FF800000FF8000 +000F80000007800000078000000780000007800000078000000780000007800000078000000780 +000007800000078000000780000007800000078000000780000007801FFC07801FFC078007E007 +800780078006000780040007800800078010000780600007808000078100000783800007878000 +078FC0000793C00007A1E00007C1F0000780F0000780780007807C0007803C0007803E0007801F +0007800F0007800F80078007C0078003C0078003E00FC007F8FFFC0FFFFFFC0FFF20327EB123> +107 D<0780FF80FF800F8007800780078007800780078007800780078007800780078007800780 +078007800780078007800780078007800780078007800780078007800780078007800780078007 +800780078007800780078007800780078007800FC0FFFCFFFC0E327EB112>I<0780FE0000FF83 +078000FF8C03C0000F9001E00007A001E00007A000F00007C000F00007C000F000078000F00007 +8000F000078000F000078000F000078000F000078000F000078000F000078000F000078000F000 +078000F000078000F000078000F000078000F000078000F000078000F000078000F000078000F0 +00078000F000078000F000078000F0000FC001F800FFFC1FFF80FFFC1FFF80211F7E9E25>110 +D<001FC00000F0780001C01C00070007000F0007801E0003C01C0001C03C0001E03C0001E07800 +00F0780000F0780000F0F80000F8F80000F8F80000F8F80000F8F80000F8F80000F8F80000F8F8 +0000F8780000F07C0001F03C0001E03C0001E01E0003C01E0003C00F00078007800F0001C01C00 +00F07800001FC0001D1F7E9E21>I<0783E0FF8C18FF907C0F907C07A07C07C03807C00007C000 +07C000078000078000078000078000078000078000078000078000078000078000078000078000 +0780000780000780000780000780000780000780000FC000FFFE00FFFE00161F7E9E19>114 +D<01FC100E03301800F0300070600030E00030E00010E00010E00010F00010F800007E00003FF0 +001FFF000FFFC003FFE0003FF00001F80000F880003C80003C80001CC0001CC0001CE0001CE000 +18F00038F00030CC0060C301C080FE00161F7E9E1A>I<00400000400000400000400000400000 +C00000C00000C00001C00001C00003C00007C0000FC0001FFFE0FFFFE003C00003C00003C00003 +C00003C00003C00003C00003C00003C00003C00003C00003C00003C00003C00003C00003C00003 +C01003C01003C01003C01003C01003C01003C01003C01001C02001E02000E0400078C0001F0014 +2C7FAB19>I<078000F000FF801FF000FF801FF0000F8001F000078000F000078000F000078000 +F000078000F000078000F000078000F000078000F000078000F000078000F000078000F0000780 +00F000078000F000078000F000078000F000078000F000078000F000078000F000078000F00007 +8000F000078001F000078001F000078001F000038002F00003C004F00001C008F800007030FF80 +001FC0FF80211F7E9E25>I E end +%%EndProlog +%%BeginSetup +%%Feature: *Resolution 300 +TeXDict begin @letter /letter where {pop letter} if +%%EndSetup +%%Page: 1 1 +bop 501 287 a Fs(A)21 b(Gen)n(tle)h(In)n(tro)r(duction)g(to)g(Hask)n(ell)432 +443 y Fr(P)o(aul)16 b(Hudak)395 501 y(Y)l(ale)g(Univ)o(ersit)o(y)203 +559 y(Departmen)o(t)e(of)j(Computer)e(Science)1228 443 y(Joseph)i(H.)f(F)l +(asel)1145 501 y(Univ)o(ersit)o(y)e(of)i(California)1044 559 +y(Los)h(Alamos)e(National)h(Lab)q(oratory)0 788 y Fq(1)69 b(In)n(tro)r +(duction)0 920 y Fp(Our)20 b(purp)q(ose)h(in)f(writing)g(this)h(tutorial)e +(is)i(not)e(to)g(teac)o(h)h(programming,)g(nor)f(ev)o(en)h(to)f(teac)o(h)h +(functional)0 977 y(programming.)j(Rather,)17 b(it)f(is)h(in)o(tended)h(to)e +(serv)o(e)g(as)g(a)h(supplemen)o(t)g(to)f(the)h(Hask)o(ell)g(Rep)q(ort)g([3)o +(],)f(whic)o(h)h(is)0 1033 y(otherwise)11 b(a)g(rather)g(dense)h(tec)o +(hnical)g(exp)q(osition.)20 b(Our)11 b(goal)g(is)h(to)e(pro)o(vide)i(a)f(gen) +o(tle)g(in)o(tro)q(duction)i(to)d(Hask)o(ell)0 1090 y(for)k(someone)g(who)g +(has)g(exp)q(erience)j(with)e(at)f(least)g(one)h(other)f(language,)g +(preferably)h(a)f(functional)i(language)0 1146 y(\(ev)o(en)k(if)h(only)g(an)f +(\\almost-functional")h(language)f(suc)o(h)h(as)f(ML)g(or)g(Sc)o(heme\).)35 +b(If)21 b(the)f(reader)h(wishes)g(to)0 1202 y(learn)15 b(more)g(ab)q(out)g +(the)g(functional)h(programming)e(st)o(yle,)h(w)o(e)f(highly)j(recommend)e +(Bird)g(and)h(W)l(adler's)e(text)0 1259 y Fo(Intr)n(o)n(duction)k(to)g(F)m +(unctional)f(Pr)n(o)n(gr)n(amming)g Fp([1)o(],)g(whic)o(h)i(uses)e(a)g +(language)h(su\016cien)o(tly)h(similar)f(to)f(Hask)o(ell)0 +1315 y(to)h(mak)o(e)g(translation)h(b)q(et)o(w)o(een)f(the)h(t)o(w)o(o)e +(quite)i(easy)l(.)30 b(F)l(or)18 b(a)h(useful)g(surv)o(ey)g(of)f(functional)i +(programming)0 1372 y(languages)14 b(and)g(tec)o(hniques,)h(including)h(some) +e(of)f(the)h(language)g(design)h(principals)h(used)e(in)h(Hask)o(ell,)f(see)g +([2].)71 1457 y(Our)j(general)g(strategy)f(for)g(in)o(tro)q(ducing)i +(language)f(features)g(is)g(this:)24 b(motiv)m(ate)17 b(the)g(idea,)g +(de\014ne)h(some)0 1514 y(terms,)12 b(giv)o(e)i(some)e(examples,)i(and)f +(then)g(p)q(oin)o(t)g(to)g(the)g(Rep)q(ort)g(for)f(details.)20 +b(W)l(e)13 b(suggest,)g(ho)o(w)o(ev)o(er,)f(that)g(the)0 1570 +y(reader)17 b(completely)h(ignore)f(the)g(details)g(un)o(til)h(this)f(do)q +(cumen)o(t)g(has)g(b)q(een)h(completely)g(read.)24 b(On)18 +b(the)e(other)0 1627 y(hand,)g(Hask)o(ell's)h(Standard)f(Prelude)i(\(in)e +(App)q(endix)i(A)f(of)e(the)i(Rep)q(ort\))f(con)o(tains)g(lots)g(of)g(useful) +h(examples)0 1683 y(of)h(Hask)o(ell)h(co)q(de;)i(w)o(e)d(encourage)g(a)h +(thorough)e(reading)i(once)g(this)g(tutorial)g(is)g(completed.)30 +b(This)19 b(will)h(not)0 1740 y(only)d(giv)o(e)g(the)f(reader)h(a)f(feel)i +(for)d(what)h(real)h(Hask)o(ell)h(co)q(de)f(lo)q(oks)g(lik)o(e,)g(but)g(will) +h(also)e(familiarize)j(her)e(with)0 1796 y(Hask)o(ell's)f(standard)e(set)h +(of)g(prede\014ned)i(functions)f(and)f(t)o(yp)q(es.)71 1882 +y([W)l(e)e(ha)o(v)o(e)g(also)h(tak)o(en)f(the)h(course)g(of)f(not)g(la)o +(ying)h(out)g(a)f(plethora)h(of)f(lexical)j(syn)o(tax)d(rules)h(at)f(the)h +(outset.)0 1938 y(Rather,)i(w)o(e)g(in)o(tro)q(duce)h(them)f(incremen)o +(tally)i(as)e(our)f(examples)i(demand,)g(and)f(enclose)h(them)f(in)h(brac)o +(k)o(ets,)0 1995 y(as)f(with)i(this)f(paragraph.)24 b(This)18 +b(is)f(in)h(stark)e(con)o(trast)f(to)h(the)h(organization)g(of)g(the)g(Rep)q +(ort,)g(although)g(the)0 2051 y(Rep)q(ort)h(remains)g(the)f(authoritativ)o(e) +g(source)h(for)e(details)j(\(references)e(suc)o(h)h(as)f(\\)p +Fn(x)p Fp(2.1")f(refer)h(to)g(sections)h(in)0 2107 y(the)d(Rep)q(ort\).])71 +2193 y(Hask)o(ell)g(is)h(a)e Fo(typ)n(eful)h Fp(programming)f(language:)922 +2177 y Fm(1)962 2193 y Fp(T)o(yp)q(es)h(are)f(p)q(erv)m(asiv)o(e,)i(and)f +(the)g(new)o(comer)f(is)i(b)q(est)f(o\013)0 2249 y(b)q(ecoming)f(w)o(ell-a)o +(w)o(are)e(of)h(the)f(full)i(p)q(o)o(w)o(er)f(and)f(complexit)o(y)i(of)e +(Hask)o(ell's)h(t)o(yp)q(e)g(system)f(from)g(the)h(outset.)19 +b(F)l(or)0 2306 y(those)12 b(whose)g(only)g(exp)q(erience)i(is)f(with)f +(relativ)o(ely)h(\\un)o(t)o(yp)q(eful")g(languages)f(suc)o(h)g(as)g(Basic)g +(or)g(Lisp,)h(this)g(ma)o(y)0 2362 y(b)q(e)f(a)e(di\016cult)i(adjustmen)o(t;) +g(for)e(those)h(familiar)g(with)h(P)o(ascal,)f(Mo)q(dula,)h(or)e(ev)o(en)h +(ML,)g(the)g(adjustmen)o(t)f(should)0 2419 y(b)q(e)19 b(easier)h(but)e(still) +j(not)d(insigni\014can)o(t,)j(since)f(Hask)o(ell's)f(t)o(yp)q(e)g(system)f +(is)i(di\013eren)o(t)f(and)f(somewhat)g(ric)o(her)0 2475 y(than)g(most.)29 +b(In)19 b(an)o(y)g(case,)g(\\t)o(yp)q(eful)g(programming")e(is)i(part)f(of)g +(the)h(Hask)o(ell)g(programming)f(exp)q(erience,)0 2532 y(and)d(cannot)g(b)q +(e)h(a)o(v)o(oided.)p 0 2571 780 2 v 52 2598 a Fl(1)69 2614 +y Fk(A)d(phrase)h(due)f(to)g(Luca)g(Cardelli.)940 2738 y Fp(T-1)p +eop +%%Page: 2 2 +bop 0 -40 a Fp(T-2)906 b Fj(2)45 b(V)-5 b(ALUES,)16 b(TYPES,)e(AND)h(OTHER)h +(GOODIES)0 105 y Fq(2)69 b(V)-6 b(alues,)23 b(T)n(yp)r(es,)g(and)g(Other)g +(Go)r(o)r(dies)0 228 y Fp(Because)g(Hask)o(ell)g(is)f(a)g(purely)i +(functional)f(language,)h(all)f(computations)f(are)g(done)g(via)g(the)h(ev)m +(aluation)0 285 y(of)18 b Fo(expr)n(essions)e Fp(\(syn)o(tactic)i(terms\))f +(to)g(yield)j Fo(values)e Fp(\(abstract)f(en)o(tities)h(that)g(w)o(e)g +(regard)f(as)h(answ)o(ers\).)27 b(In)0 341 y(particular,)12 +b(there)g(are)f(no)g Fo(c)n(ommands)g Fp(that)f(op)q(erate)h(b)o(y)g +(implicit)j(side)e(e\013ects)f(to)g(a)g(global)h(store.)17 +b(In)12 b(addition,)0 398 y(ev)o(ery)j(v)m(alue)g(has)g(an)f(asso)q(ciated)h +Fo(typ)n(e)p Fp(.)20 b(\(In)o(tuitiv)o(ely)l(,)c(w)o(e)e(can)h(think)g(of)g +(t)o(yp)q(es)f(as)g(sets)h(of)f(v)m(alues.\))20 b(Examples)0 +454 y(of)f(expressions)g(include)j(atomic)c(v)m(alues)j(suc)o(h)e(as)g(the)g +(in)o(teger)g Fi(5)p Fp(,)g(the)g(c)o(haracter)f Fi('a')p Fp(,)i(and)f(the)g +(successor)0 511 y(function)d Fi(succ)o Fp(,)f(as)g(w)o(ell)h(as)f +(structured)g(v)m(alues)i(suc)o(h)e(as)g(the)g(list)h Fi([1,2,3])e +Fp(and)i(the)f(pair)h Fi(\('b',4\))o Fp(.)71 589 y(Just)f(as)h(expressions)g +(denote)g(v)m(alues,)h Fo(typ)n(e)f(expr)n(essions)f Fp(are)g(syn)o(tactic)h +(terms)f(that)g(denote)h Fo(typ)n(e)g(values)0 645 y Fp(\(or)10 +b(just)h Fo(typ)n(es)p Fp(\).)18 b(Examples)11 b(of)g(t)o(yp)q(e)g +(expressions)h(include)i(the)d(atomic)g(t)o(yp)q(es)g Fi(Int)f +Fp(\(\014xed-precision)k(in)o(tegers\),)0 702 y Fi(Char)h Fp(\(ascii)h(c)o +(haracters\),)d Fi(Int->Int)h Fp(\(functions)i(mapping)g Fi(Int)f +Fp(to)f Fi(Int)p Fp(\),)h(as)f(w)o(ell)j(as)d(the)i(structured)f(t)o(yp)q(es) +0 758 y Fi([Int])f Fp(\(homogeneous)h(lists)h(of)f(in)o(tegers\))g(and)g +Fi(\(Char,Int\))f Fp(\(c)o(haracter/in)o(teger)g(pairs\).)71 +837 y(All)h(Hask)o(ell)g(v)m(alues)g(are)f(\\\014rst-class"|they)g(ma)o(y)g +(b)q(e)g(passed)h(as)e(argumen)o(ts)h(to)f(functions,)i(returned)f(as)0 +893 y(results,)i(placed)g(in)h(data)e(structures,)g(etc.)21 +b(Hask)o(ell)16 b(t)o(yp)q(es,)g(on)f(the)h(other)f(hand,)h(are)f +Fo(not)h Fp(\014rst-class.)21 b(T)o(yp)q(es)0 949 y(in)16 b(a)e(sense)h +Fo(describ)n(e)f Fp(v)m(alues,)i(and)f(the)f(asso)q(ciation)i(of)e(a)g(v)m +(alue)i(with)f(its)g(t)o(yp)q(e)g(is)g(called)i(a)d Fo(typing)p +Fp(.)20 b(Using)15 b(the)0 1006 y(examples)h(of)f(v)m(alues)h(and)f(t)o(yp)q +(es)h(ab)q(o)o(v)o(e,)e(w)o(e)h(write)g(t)o(ypings)h(as)e(follo)o(ws:)691 +1115 y Fi(5)48 b(::)23 b(Int)667 1171 y('a')h(::)f(Char)643 +1228 y(succ)h(::)f(Int)h(->)f(Int)572 1284 y([1,2,3])g(::)g([Int])572 +1341 y(\('b',4\))g(::)g(\(Char,Int\))0 1450 y Fp(The)15 b Fi(::)g +Fp(can)h(b)q(e)g(read)f(\\has)f(t)o(yp)q(e.")71 1528 y(F)l(unctions)i(in)h +(Hask)o(ell)g(are)f(normally)h(de\014ned)h(b)o(y)e(a)f(series)i(of)f +Fo(e)n(quations)p Fp(.)22 b(F)l(or)16 b(example,)h(the)f(successor)0 +1585 y(function)g Fi(succ)f Fp(can)g(b)q(e)h(de\014ned)g(b)o(y)g(the)f +(single)h(equation:)71 1694 y Fi(succ)23 b(n)238 b(=)24 b(n+1)0 +1803 y Fp(An)16 b(equation)g(is)g(an)g(example)h(of)e(a)g Fo(de)n(clar)n +(ation)p Fp(.)21 b(Another)16 b(kind)h(of)e(declaration)i(is)f(a)f +Fo(typ)n(e)i(signatur)n(e)f(de)n(cla-)0 1859 y(r)n(ation)f +Fp(\()p Fn(x)p Fp(4.4.1\),)e(with)i(whic)o(h)h(w)o(e)f(can)h(declare)g(an)f +(explicit)i(t)o(yping)f(for)e Fi(succ)p Fp(:)71 1971 y Fi(succ)285 +b(::)24 b(Int)f(->)h(Int)0 2080 y Fp(W)l(e)15 b(will)i(ha)o(v)o(e)e(m)o(uc)o +(h)g(more)g(to)f(sa)o(y)h(ab)q(out)g(function)h(de\014nitions)h(in)f(Section) +g(3.)71 2158 y(F)l(or)i(p)q(edagogical)i(purp)q(oses,)g(when)g(w)o(e)f(wish)h +(to)e(indicate)j(that)d(an)h(expression)h Fh(e)1562 2165 y +Fm(1)1601 2158 y Fp(ev)m(aluates,)h(or)d(\\re-)0 2214 y(duces,")d(to)g +(another)g(expression)h(or)e(v)m(alue)j Fh(e)795 2221 y Fm(2)815 +2214 y Fp(,)e(w)o(e)f(will)j(write:)838 2307 y Fh(e)859 2314 +y Fm(1)952 2307 y Fn(\))74 b Fh(e)1092 2314 y Fm(2)0 2400 y +Fp(F)l(or)15 b(example,)g(note)g(that:)712 2457 y Fi(succ)23 +b(\(succ)g(3\))73 b Fn(\))h Fi(5)71 2557 y Fp(Hask)o(ell's)17 +b Fo(static)h(typ)n(e)h(system)d Fp(de\014nes)j(the)e(formal)g(relationship)i +(b)q(et)o(w)o(een)f(t)o(yp)q(es)f(and)h(v)m(alues)g(\()p Fn(x)p +Fp(4.1.3\).)0 2614 y(The)11 b(static)f(t)o(yp)q(e)h(system)f(ensures)i(that)d +(Hask)o(ell)j(programs)d(are)i Fo(typ)n(e)h(safe)p Fp(;)f(that)f(is,)i(that)e +(the)h(programmer)e(has)p eop +%%Page: 3 3 +bop 0 -40 a Fj(2.1)45 b(P)o(olymorphic)15 b(T)o(yp)q(es)1390 +b Fp(T-3)0 105 y(not)13 b(mismatc)o(hed)h(t)o(yp)q(es)f(in)h(some)f(w)o(a)o +(y)l(.)19 b(F)l(or)12 b(example,)i(w)o(e)f(cannot)h(generally)g(add)g +(together)e(t)o(w)o(o)g(c)o(haracters,)0 162 y(so)17 b(the)g(expression)h +Fi('a'+'b')d Fp(is)j(ill-t)o(yp)q(ed.)28 b(The)17 b(main)g(adv)m(an)o(tage)g +(of)f(statically)i(t)o(yp)q(ed)f(languages)g(is)h(w)o(ell-)0 +218 y(kno)o(wn:)g(All)c(t)o(yp)q(e)e(errors)g(are)g(detected)h(at)f +(compile-time.)21 b(This)13 b(not)f(only)g(aids)h(the)g(user)f(in)i +(reasoning)e(ab)q(out)0 274 y(programs,)17 b(but)h(also)g(p)q(ermits)g(a)f +(compiler)j(to)d(generate)g(more)h(e\016cien)o(t)g(co)q(de)h(\(for)d +(example,)j(no)f(run-time)0 331 y(t)o(yp)q(e)d(tags)f(or)h(tests)g(are)g +(required\).)71 410 y(The)h(t)o(yp)q(e)g(system)f(also)h(ensures)h(that)e +(user-supplied)k(t)o(yp)q(e)d(signatures)g(are)g(correct.)22 +b(In)16 b(fact,)g(Hask)o(ell's)0 467 y(t)o(yp)q(e)h(system)f(is)h(p)q(o)o(w)o +(erful)g(enough)g(to)f(allo)o(w)h(us)f(to)g(a)o(v)o(oid)h(writing)g(an)o(y)f +(t)o(yp)q(e)h(signatures)f(at)g(all,)1743 450 y Fm(2)1781 467 +y Fp(in)h(whic)o(h)0 523 y(case)d(w)o(e)h(sa)o(y)e(that)h(the)h(t)o(yp)q(e)f +(system)g Fo(infers)g Fp(the)g(correct)g(t)o(yp)q(es)h(for)e(us.)20 +b(Nev)o(ertheless,)15 b(judicious)h(placemen)o(t)0 580 y(of)f(t)o(yp)q(e)g +(signatures)g(is)h(a)f(go)q(o)q(d)g(idea,)h(as)e(w)o(e)h(did)i(for)d +Fi(succ)p Fp(,)g(since)j(it)e(impro)o(v)o(es)g(readabilit)o(y)i(and)e(helps)h +(bring)0 636 y(programming)f(errors)f(to)h(ligh)o(t.)71 715 +y([The)e(reader)g(will)j(note)d(that)g(w)o(e)g(ha)o(v)o(e)g(capitalized)j +(iden)o(ti\014ers)f(that)e(denote)g(sp)q(eci\014c)j(t)o(yp)q(es,)d(suc)o(h)h +(as)f Fi(Int)0 772 y Fp(and)18 b Fi(Char)o Fp(,)g(but)g(not)g(iden)o +(ti\014ers)h(that)e(denote)h(v)m(alues,)h(suc)o(h)f(as)g Fi(succ)o +Fp(.)28 b(This)18 b(is)g(not)g(just)f(a)h(con)o(v)o(en)o(tion:)25 +b(it)0 828 y(is)16 b(enforced)g(b)o(y)g(Hask)o(ell's)g(lexical)i(syn)o(tax.)i +(In)c(fact,)f(the)h(case)f(of)h(the)f(other)h(c)o(haracters)e(matters,)h(to)q +(o:)20 b Fi(foo)o Fp(,)0 885 y Fi(fOo)p Fp(,)14 b(and)i Fi(fOO)e +Fp(are)h(all)i(distinct)f(iden)o(ti\014ers.])0 1028 y Fg(2.1)56 +b(P)n(olymorphic)17 b(T)n(yp)r(es)0 1137 y Fp(Hask)o(ell)k(also)g(incorp)q +(orates)f Fo(p)n(olymorphic)h Fp(t)o(yp)q(es|t)o(yp)q(es)g(that)f(are)g(univ) +o(ersally)i(quan)o(ti\014ed)g(in)f(some)f(w)o(a)o(y)0 1194 +y(o)o(v)o(er)d Fo(al)r(l)g Fp(t)o(yp)q(es.)27 b(P)o(olymorphic)18 +b(t)o(yp)q(e)f(expressions)i(essen)o(tially)g(describ)q(e)g +Fo(families)e Fp(of)g(t)o(yp)q(es.)27 b(F)l(or)16 b(example,)0 +1250 y(\()p Fn(8)p Fi(a)p Fp(\))p Fi([a])21 b Fp(is)h(the)g(family)h(of)f(t)o +(yp)q(es)f(consisting)i(of,)g(for)e(ev)o(ery)h(t)o(yp)q(e)g +Fi(a)p Fp(,)h(the)f(t)o(yp)q(e)g(of)g(lists)h(of)e Fi(a)p Fp(.)40 +b(Lists)22 b(of)0 1306 y(in)o(tegers)15 b(\(e.g.)f Fi([1,2,3])o +Fp(\),)g(lists)i(of)f(c)o(haracters)f(\()p Fi(['a','b','c'])n +Fp(\),)g(ev)o(en)h(lists)h(of)f(lists)h(of)e(in)o(tegers,)h(etc.,)f(are)0 +1363 y(all)j(mem)o(b)q(ers)e(of)h(this)g(family)l(.)22 b(\(Note,)15 +b(ho)o(w)o(ev)o(er,)f(that)h Fi([2,'b'])g Fp(is)h Fo(not)f +Fp(a)h(v)m(alid)h(example,)f(since)h(there)f(is)g(no)0 1419 +y(single)h(t)o(yp)q(e)e(that)f(con)o(tains)i(b)q(oth)f Fi(2)g +Fp(and)g Fi('b')p Fp(.\))71 1499 y([Iden)o(ti\014ers)21 b(suc)o(h)f(as)44 +b Fi(a)g Fp(ab)q(o)o(v)o(e)20 b(are)g(called)i Fo(typ)n(e)f(variables)p +Fp(,)g(and)f(are)g(uncapitalized)j(to)d(distinguish)0 1555 +y(them)d(from)g(sp)q(eci\014c)i(t)o(yp)q(es)f(suc)o(h)f(as)g +Fi(Int)p Fp(.)26 b(F)l(urthermore,)17 b(since)i(Hask)o(ell)f(has)f(only)h +(univ)o(ersally)h(quan)o(ti\014ed)0 1612 y(t)o(yp)q(es,)c(there)g(is)h(no)f +(need)h(to)f(explicitly)j(write)d(out)g(the)g(sym)o(b)q(ol)g(for)g(univ)o +(ersal)h(quan)o(ti\014cation,)g(and)f(th)o(us)g(w)o(e)0 1668 +y(simply)g(write)f Fi([a])f Fp(in)i(the)f(example)g(ab)q(o)o(v)o(e.)19 +b(In)c(other)e(w)o(ords,)g(all)i(t)o(yp)q(e)f(v)m(ariables)h(are)e +(implicitly)k(univ)o(ersally)0 1725 y(quan)o(ti\014ed.])71 +1804 y(Lists)g(are)g(a)g(commonly)h(used)g(data)e(structure)i(in)g +(functional)g(languages,)g(and)f(are)g(a)g(go)q(o)q(d)g(v)o(ehicle)j(for)0 +1860 y(explaining)f(the)e(principals)j(of)c(p)q(olymorphism.)27 +b(The)17 b(list)h Fi([1,2,3])e Fp(in)i(Hask)o(ell)f(is)h(actually)g +(shorthand)f(for)0 1917 y(the)i(list)g Fi(1:\(2:\(3:[]\)\))o +Fp(,)g(where)g Fi([])f Fp(is)i(the)e(empt)o(y)h(list)g(and)g +Fi(:)g Fp(is)g(the)g(in\014x)h(op)q(erator)e(that)g(adds)g(its)h(\014rst)0 +1973 y(argumen)o(t)f(to)g(the)h(fron)o(t)f(of)h(its)g(second)g(argumen)o(t)f +(\(a)g(list\).)1093 1957 y Fm(3)1145 1973 y Fp(Since)i Fi(:)f +Fp(is)g(righ)o(t)g(asso)q(ciativ)o(e,)g(w)o(e)g(can)g(also)0 +2030 y(write)c(this)h(list)g(as)f Fi(1:2:3:[])o Fp(.)71 2109 +y(As)c(an)h(example)g(of)f(a)h(user-de\014ned)h(function)f(that)f(op)q +(erates)h(on)f(lists,)i(consider)f(the)g(problem)g(of)g(coun)o(ting)0 +2165 y(the)j(n)o(um)o(b)q(er)h(of)f(elemen)o(ts)g(in)h(a)f(list:)71 +2266 y Fi(length)428 b(::)24 b([a])f(->)h(Int)71 2322 y(length)46 +b([])334 b(=)24 b(0)71 2379 y(length)f(\(x:xs\))261 b(=)24 +b(1)g(+)f(length)h(xs)0 2488 y Fp(This)14 b(de\014nition)i(is)e(almost)g +(self-explanatory)l(.)20 b(W)l(e)14 b(can)g(read)g(the)g(equations)g(as)f(sa) +o(ying:)19 b(\\The)14 b(length)g(of)g(the)p 0 2525 780 2 v +52 2552 a Fl(2)69 2568 y Fk(With)g(a)f(few)g(exceptions)i(to)d(b)q(e)i +(describ)q(ed)h(later.)52 2598 y Fl(3)69 2614 y Ff(:)e Fk(and)g +Ff([])f Fk(are)h(lik)o(e)i(Lisp's)e Ff(cons)f Fk(and)h Ff(nil)o +Fk(,)g(resp)q(ectiv)o(ely)m(.)p eop +%%Page: 4 4 +bop 0 -40 a Fp(T-4)906 b Fj(2)45 b(V)-5 b(ALUES,)16 b(TYPES,)e(AND)h(OTHER)h +(GOODIES)0 105 y Fp(empt)o(y)g(list)i(is)f(0,)f(and)h(the)g(length)g(of)f(a)h +(list)g(whose)g(\014rst)f(elemen)o(t)h(is)h Fi(x)e Fp(and)h(remainder)g(is)g +Fi(xs)g Fp(is)g(1)f(plus)i(the)0 162 y(length)g(of)f Fi(xs)p +Fp(.")26 b(\(Note)16 b(the)i(naming)f(con)o(v)o(en)o(tion)h(used)g(here;)g +Fi(xs)f Fp(is)h(the)f(plural)i(of)e Fi(x)p Fp(,)g(and)h(should)g(b)q(e)g +(read)0 218 y(that)c(w)o(a)o(y)l(.\))71 300 y(Although)20 b(in)o(tuitiv)o(e,) +h(this)f(example)g(highligh)o(ts)h(an)e(imp)q(ortan)o(t)g(asp)q(ect)h(of)f +(Hask)o(ell)h(that)f(is)h(y)o(et)f(to)f(b)q(e)0 357 y(explained:)k +Fo(p)n(attern)17 b(matching)p Fp(.)j(The)15 b(left-hand)i(sides)f(of)f(the)g +(equations)h(con)o(tain)f Fo(p)n(atterns)g Fp(suc)o(h)h(as)f +Fi([])g Fp(and)0 413 y Fi(x:xs)o Fp(.)28 b(In)19 b(a)e(function)i +(application)g(these)f(patterns)g(are)f Fo(matche)n(d)i Fp(against)e(actual)h +(parameters)f(in)i(a)e(fairly)0 469 y(in)o(tuitiv)o(e)c(w)o(a)o(y)d(\()p +Fi([])h Fp(only)h(matc)o(hes)f(the)h(empt)o(y)f(list,)i(and)e +Fi(x:xs)g Fp(will)i(successfully)h(matc)o(h)d(an)o(y)g(list)h(with)g(at)f +(least)0 526 y(one)17 b(elemen)o(t,)g(binding)i Fi(x)d Fp(to)g(the)h(\014rst) +f(elemen)o(t)i(and)e Fi(xs)h Fp(to)f(the)h(rest)f(of)g(the)h(list\).)24 +b(If)17 b(the)g(matc)o(h)f(succeeds,)0 582 y(the)h(righ)o(t-hand)g(side)g(is) +h(ev)m(aluated)f(and)g(returned)g(as)f(the)h(result)g(of)f(the)h +(application.)26 b(If)17 b(it)g(fails,)g(the)g(next)0 639 y(equation)f(is)f +(tried,)h(and)f(if)h(all)g(equations)f(fail,)h(an)f(error)g(results.)71 +721 y(De\014ning)e(functions)g(b)o(y)f(pattern)f(matc)o(hing)i(is)f(quite)h +(common)f(in)h(Hask)o(ell,)g(and)g(the)f(user)g(should)h(b)q(ecome)0 +777 y(familiar)j(with)g(the)f(v)m(arious)h(kinds)g(of)f(patterns)g(that)g +(are)g(allo)o(w)o(ed;)g(w)o(e)g(will)i(return)e(to)g(this)h(issue)g(in)g +(Section)0 834 y(3.14.)71 916 y Fi(length)i Fp(is)i(also)f(an)g(example)i(of) +e(a)g Fo(p)n(olymorphic)h(function)p Fp(.)32 b(It)20 b(can)f(b)q(e)h(applied) +h(to)e(a)g(list)h(con)o(taining)0 972 y(elemen)o(ts)c(of)f(an)o(y)f(t)o(yp)q +(e.)20 b(F)l(or)15 b(example:)600 1071 y Fi(length)23 b([1,2,3])245 +b Fn(\))102 b Fp(3)600 1128 y Fi(length)23 b(['a','b','c'])101 +b Fn(\))h Fp(3)600 1184 y Fi(length)23 b([[],[],[]])173 b Fn(\))102 +b Fp(3)71 1309 y(Here)15 b(are)g(t)o(w)o(o)f(other)h(useful)h(p)q(olymorphic) +h(functions)f(on)f(lists)h(that)e(will)j(b)q(e)f(used)g(later:)71 +1418 y Fi(head)476 b(::)24 b([a])f(->)h(a)71 1475 y(head)f(\(x:xs\))309 +b(=)48 b(x)71 1554 y(tail)476 b(::)24 b([a])f(->)h([a])71 1611 +y(tail)f(\(x:xs\))309 b(=)48 b(xs)71 1770 y Fp(With)10 b(p)q(olymorphic)i(t)o +(yp)q(es,)g(w)o(e)e(\014nd)h(that)f(some)g(t)o(yp)q(es)h(are)f(in)h(a)g +(sense)g(strictly)g Fo(mor)n(e)h(gener)n(al)d Fp(than)i(others.)0 +1827 y(F)l(or)18 b(example,)h(the)f(t)o(yp)q(e)h Fi([a])e Fp(is)i(more)f +(general)h(than)f Fi([Char])o Fp(.)29 b(In)19 b(other)f(w)o(ords,)f(the)i +(latter)f(t)o(yp)q(e)g(can)g(b)q(e)0 1883 y(deriv)o(ed)12 b(from)e(the)h +(former)f(b)o(y)h(a)g(suitable)h(substitution)g(for)e Fi(a)p +Fp(.)18 b(With)12 b(regard)e(to)g(this)i(generalization)g(ordering,)0 +1940 y(Hask)o(ell's)21 b(t)o(yp)q(e)g(system)g(p)q(ossesses)g(t)o(w)o(o)e +(imp)q(ortan)o(t)h(prop)q(erties:)32 b(First,)22 b(ev)o(ery)e(w)o(ell-t)o(yp) +q(ed)j(expression)e(is)0 1996 y(guaran)o(teed)16 b(to)f(ha)o(v)o(e)h(a)g +(unique)i Fo(princip)n(al)d Fp(t)o(yp)q(e)i(\(explained)g(b)q(elo)o(w\),)g +(and)f(second,)h(the)f(principal)j(t)o(yp)q(e)d(can)0 2053 +y(b)q(e)k Fo(inferr)n(e)n(d)f Fp(automatically)h(\()p Fn(x)o +Fp(4.1.3\).)31 b(In)20 b(comparison)g(to)f(a)g Fo(monomorphic)n(al)r(ly)i +(typ)n(e)n(d)e Fp(language)h(suc)o(h)f(as)0 2109 y(P)o(ascal,)c(the)h(reader) +g(will)i(\014nd)e(that)f Fo(p)n(olymorphism)i Fp(impro)o(v)o(es)e(expressiv)o +(eness,)i(and)f Fo(typ)n(e)h(infer)n(enc)n(e)d Fp(lessens)0 +2166 y(the)h(burden)h(of)f(t)o(yp)q(es)g(on)h(the)f(programmer.)71 +2248 y(An)f(expression's)h(or)e(function's)i(principal)h(t)o(yp)q(e)f(is)f +(the)g(least)h(general)f(t)o(yp)q(e)h(that,)e(in)o(tuitiv)o(ely)l(,)j(\\con)o +(tains)0 2304 y(all)22 b(instances)f(of)f(the)h(expression.")37 +b(F)l(or)20 b(example,)j(the)e(principal)i(t)o(yp)q(e)d(of)h +Fi(head)f Fp(is)h Fi([a]->a)o Fp(;)i(the)e(t)o(yp)q(es)0 2361 +y Fi([b]->a)o Fp(,)15 b Fi(a->a)o Fp(,)f(or)g(ev)o(en)39 b +Fi(a)g Fp(are)14 b Fo(to)n(o)i(gener)n(al)p Fp(,)d(whereas)i(something)g(lik) +o(e)g Fi([Int]->Int)f Fp(is)h Fo(to)n(o)h(sp)n(e)n(ci\014c)p +Fp(.)i(The)0 2417 y(existence)c(of)e(unique)h(principal)i(t)o(yp)q(es)d(is)h +(the)g(hallmark)g(feature)f(of)g(the)g Fo(Hind)r(ley-Milner)h(typ)n(e)g +(system)p Fp(,)f(whic)o(h)0 2473 y(forms)g(the)g(basis)h(of)f(the)h(t)o(yp)q +(e)f(systems)g(of)g(Hask)o(ell,)h(ML,)g(Miranda,)1191 2457 +y Fm(4)1223 2473 y Fp(and)g(sev)o(eral)f(other)g(\(mostly)g(functional\))0 +2530 y(languages.)p 0 2571 780 2 v 52 2598 a Fl(4)69 2614 y +Fk(\\Miranda")j(is)f(a)f(trademark)h(of)e(Researc)o(h)i(Soft)o(w)o(are,)f +(Ltd.)p eop +%%Page: 5 5 +bop 0 -40 a Fj(2.2)45 b(User-De\014ned)16 b(T)o(yp)q(es)1384 +b Fp(T-5)0 105 y Fg(2.2)56 b(User-De\014ned)17 b(T)n(yp)r(es)0 +217 y Fp(W)l(e)f(can)h(de\014ne)g(our)f(o)o(wn)f(t)o(yp)q(es)i(in)g(Hask)o +(ell)g(using)g(a)f Fi(data)f Fp(declaration,)i(whic)o(h)g(w)o(e)f(in)o(tro)q +(duce)h(via)f(a)g(series)0 273 y(of)f(examples)h(\()p Fn(x)p +Fp(4.2.1\).)71 355 y(An)f(imp)q(ortan)o(t)g(prede\014ned)i(t)o(yp)q(e)e(in)h +(Hask)o(ell)g(is)g(that)e(of)h(truth)g(v)m(alues:)71 464 y +Fi(data)23 b(Bool)357 b(=)24 b(False)f(|)h(True)0 573 y Fp(The)17 +b(t)o(yp)q(e)h(b)q(eing)g(de\014ned)h(here)e(is)h Fi(Bool)o +Fp(,)g(and)f(it)g(has)g(exactly)h(t)o(w)o(o)e(v)m(alues:)25 +b Fi(True)16 b Fp(and)i Fi(False)o Fp(.)26 b Fi(Bool)16 b Fp(is)i(an)0 +630 y(example)g(of)e(a)h(\(n)o(ullary\))g Fo(typ)n(e)h(c)n(onstructor)p +Fp(,)f(and)h Fi(True)e Fp(and)h Fi(False)g Fp(are)f(\(also)h(n)o(ullary\))h +Fo(data)g(c)n(onstructors)0 686 y Fp(\(or)c(just)h Fo(c)n(onstructors)p +Fp(,)g(for)f(short\).)71 768 y(Similarly)l(,)j(w)o(e)e(migh)o(t)g(wish)g(to)g +(de\014ne)h(a)f(color)g(t)o(yp)q(e:)71 877 y Fi(data)23 b(Color)333 +b(=)24 b(Red)f(|)h(Green)f(|)h(Blue)f(|)h(Indigo)f(|)h(Violet)0 +986 y Fp(Both)18 b Fi(Bool)f Fp(and)h Fi(Color)f Fp(are)g(examples)i(of)e +Fo(enumer)n(ate)n(d)h(typ)n(es)p Fp(,)g(since)h(they)f(consist)g(of)f(a)h +(\014nite)g(n)o(um)o(b)q(er)g(of)0 1043 y(n)o(ullary)e(data)f(constructors.) +71 1125 y(Here)g(is)h(an)f(example)h(of)f(a)g(t)o(yp)q(e)g(with)g(just)g(one) +h(data)e(constructor:)71 1234 y Fi(data)23 b(Point)g(a)286 +b(=)24 b(Pt)g(a)f(a)0 1343 y Fp(Because)13 b(of)g(the)g(single)h +(constructor,)e(a)g(t)o(yp)q(e)h(lik)o(e)h Fi(Point)e Fp(is)i(often)e(called) +j(a)d Fo(tuple)i(typ)n(e)p Fp(,)f(since)h(it)f(is)g(essen)o(tially)0 +1399 y(just)h(a)h(cartesian)f(pro)q(duct)h(\(in)g(this)g(case)g(binary\))g +(of)f(other)g(t)o(yp)q(es.)1189 1383 y Fm(5)1228 1399 y Fp(In)i(con)o(trast,) +d(m)o(ulti-constructor)h(t)o(yp)q(es,)0 1456 y(suc)o(h)i(as)e +Fi(Bool)h Fp(and)g Fi(Color)p Fp(,)f(are)h(called)i(\(disjoin)o(t\))e +Fo(union)g Fp(t)o(yp)q(es.)71 1538 y(More)c(imp)q(ortan)o(tly)l(,)h(ho)o(w)o +(ev)o(er,)f Fi(Point)g Fp(is)i(an)e(example)i(of)e(a)h Fo(p)n(olymorphic)g +Fp(t)o(yp)q(e:)18 b(for)11 b(an)o(y)h(t)o(yp)q(e)f Fh(t)p Fp(,)i(it)f +(de\014nes)0 1594 y(the)17 b(t)o(yp)q(e)h(of)f(cartesian)g(p)q(oin)o(ts)h +(that)f(use)g Fh(t)h Fp(as)f(the)h(co)q(ordinate)f(t)o(yp)q(e.)27 +b Fi(Point)17 b Fp(can)g(no)o(w)g(b)q(e)h(seen)g(clearly)g(as)0 +1651 y(a)g(unary)g(t)o(yp)q(e)g(constructor,)f(since)j(from)d(the)h(t)o(yp)q +(e)g Fh(t)h Fp(it)f(constructs)g(a)f(new)i(t)o(yp)q(e)f Fi(Point)23 +b Fh(t)p Fp(.)29 b(\(In)18 b(the)g(same)0 1707 y(sense,)e(using)h(the)f(list) +h(example)g(giv)o(en)g(earlier,)g Fi([)p 884 1707 14 2 v 16 +w(])f Fp(is)h(also)f(a)f(t)o(yp)q(e)i(constructor)e(\(where)h(w)o(e)g(ha)o(v) +o(e)f(used)i(\\)p 1914 1707 V 16 w(")0 1764 y(to)g(denote)i(the)f(missing)h +(argumen)o(t\):)24 b(giv)o(en)19 b(an)o(y)f(t)o(yp)q(e)g Fh(t)g +Fp(w)o(e)g(can)g(\\apply")h Fi([)p 1404 1764 V 16 w(])f Fp(to)f(yield)j(a)e +(new)g(t)o(yp)q(e)g Fi([)p Fh(t)p Fi(])p Fp(.)0 1820 y(Similarly)l(,)p +203 1820 V 32 w Fi(->)p 267 1820 V 28 w Fp(is)c(a)e(t)o(yp)q(e)h +(constructor:)18 b(giv)o(en)13 b(t)o(w)o(o)f(t)o(yp)q(es)g +Fh(t)i Fp(and)f Fh(u)p Fp(,)f Fh(t)p Fi(->)q Fh(u)g Fp(is)i(the)f(t)o(yp)q(e) +f(of)h(functions)g(mapping)0 1876 y(elemen)o(ts)j(of)f(t)o(yp)q(e)g +Fh(t)g Fp(to)g(elemen)o(ts)h(of)f(t)o(yp)q(e)g Fh(u)p Fp(.\))71 +1958 y(Note)f(that)g(the)g(t)o(yp)q(e)h(of)f(the)h(binary)g(constructor)f +Fi(Pt)g Fp(is)h Fi(a)24 b(->)g(a)f(->)h(Point)f(a)p Fp(,)14 +b(and)h(th)o(us)g(the)f(follo)o(wing)0 2015 y(t)o(ypings)h(are)g(v)m(alid:)71 +2124 y Fi(Pt)47 b(2.0)g(3.0)286 b(::)24 b(Point)f(Float)71 +2180 y(Pt)47 b('a')g('b')286 b(::)24 b(Point)f(Char)71 2237 +y(Pt)g(True)g(False)262 b(::)24 b(Point)f(Bool)0 2346 y Fp(On)16 +b(the)f(other)g(hand,)g(an)g(expression)h(suc)o(h)g(as)f Fi(Pt)23 +b('a')h(1)15 b Fp(is)g(ill-t)o(yp)q(ed.)71 2428 y(It)h(is)h(imp)q(ortan)o(t)f +(to)g(distinguish)i(b)q(et)o(w)o(een)f(applying)g(a)f Fo(c)n(onstructor)h +Fp(to)f(yield)i(a)e Fo(value)p Fp(,)g(and)g(applying)i(a)0 +2484 y Fo(typ)n(e)i(c)n(onstructor)e Fp(to)h(yield)h(a)f Fo(typ)n(e)p +Fp(;)h(the)f(former)f(happ)q(ens)i(at)e(run-time)i(and)f(is)h(ho)o(w)e(w)o(e) +h(compute)g(things)p 0 2525 780 2 v 52 2552 a Fl(5)69 2568 +y Fk(T)m(uples)13 b(are)g(somewhat)g(lik)o(e)h Fe(r)n(e)n(c)n(or)n(ds)d +Fk(in)i(other)g(languages,)h(except)f(that)g(the)g(elemen)o(ts)g(are)g(p)q +(ositional,)i(rather)e(than)g(ha)o(ving)0 2614 y(names)h(\(lab)q(els\))g +(asso)q(ciated)h(with)f(them.)p eop +%%Page: 6 6 +bop 0 -40 a Fp(T-6)906 b Fj(2)45 b(V)-5 b(ALUES,)16 b(TYPES,)e(AND)h(OTHER)h +(GOODIES)0 105 y Fp(in)g(Hask)o(ell,)h(whereas)e(the)h(latter)f(happ)q(ens)i +(at)e(compile-time)i(and)f(is)g(part)f(of)g(the)h(t)o(yp)q(e)g(system's)f +(pro)q(cess)g(of)0 162 y(ensuring)h(t)o(yp)q(e)f(safet)o(y)l(.)0 +298 y Fg(2.3)56 b(Recursiv)n(e)17 b(T)n(yp)r(es)0 402 y Fp(T)o(yp)q(es)e(can) +h(also)f(b)q(e)h(recursiv)o(e,)f(as)g(in:)71 511 y Fi(data)23 +b(Tree)g(a)310 b(=)24 b(Leaf)f(a)h(|)g(Branch)f(\(Tree)g(a\))h(\(Tree)f(a\))0 +620 y Fp(Here)11 b(w)o(e)g(ha)o(v)o(e)f(de\014ned)i(a)f(p)q(olymorphic)h +(binary)g(tree)e(t)o(yp)q(e)h(whose)g(elemen)o(ts)g(are)g(either)h(leaf)f(no) +q(des)g(con)o(taining)0 677 y(a)k(v)m(alue)h(of)f(t)o(yp)q(e)g +Fi(a)p Fp(,)g(or)g(in)o(ternal)h(no)q(des)g(\(\\branc)o(hes"\))e(con)o +(taining)i(\(recursiv)o(ely\))g(t)o(w)o(o)e(sub-trees.)71 751 +y(When)e(reading)g(data)f(declarations)h(suc)o(h)g(as)f(this,)i(remem)o(b)q +(er)f(that)f Fi(Tree)g Fp(is)h(a)f(t)o(yp)q(e)h(constructor,)f(whereas)0 +808 y Fi(Branch)k Fp(and)i Fi(Leaf)f Fp(are)g(data)f(constructors.)22 +b(Aside)c(from)d(establishing)j(a)e(connection)i(b)q(et)o(w)o(een)e(these)h +(con-)0 864 y(structors,)d(the)h(ab)q(o)o(v)o(e)g(declaration)h(is)g(essen)o +(tially)g(de\014ning)h(the)e(follo)o(wing)h(t)o(yp)q(es)f(for)g +Fi(Branch)f Fp(and)i Fi(Leaf)o Fp(:)71 973 y Fi(Branch)428 +b(::)24 b(Tree)f(a)h(->)g(Tree)f(a)h(->)f(Tree)g(a)71 1030 +y(Leaf)476 b(::)24 b(a)g(->)f(Tree)h(a)71 1182 y Fp(With)18 +b(this)h(example)g(w)o(e)f(ha)o(v)o(e)f(de\014ned)j(a)e(t)o(yp)q(e)g +(su\016cien)o(tly)i(ric)o(h)f(to)e(allo)o(w)i(de\014ning)g(some)f(in)o +(teresting)0 1238 y(\(recursiv)o(e\))f(functions)h(that)e(use)h(it.)26 +b(F)l(or)16 b(example,)i(supp)q(ose)g(w)o(e)e(wish)i(to)e(de\014ne)i(a)f +(function)h Fi(fringe)e Fp(that)0 1295 y(returns)c(a)h(list)g(of)f(all)h(the) +g(elemen)o(ts)g(in)g(the)g(lea)o(v)o(es)g(of)f(a)g(tree)g(from)g(left)h(to)f +(righ)o(t.)18 b(It's)13 b(usually)g(helpful)i(to)d(write)0 +1351 y(do)o(wn)k(the)g(t)o(yp)q(e)g(of)g(new)g(functions)h(\014rst;)f(in)h +(this)f(case)g(w)o(e)g(see)h(that)e(the)h(t)o(yp)q(e)g(should)h(b)q(e)g +Fi(Tree)23 b(a)h(->)g([a])o Fp(.)0 1408 y(That)16 b(is,)h Fi(fringe)e +Fp(is)i(a)f(p)q(olymorphic)i(function)f(that,)f(for)g(an)o(y)g(t)o(yp)q(e)g +Fi(a)p Fp(,)h(maps)f(trees)g(of)g Fi(a)g Fp(in)o(to)g(lists)i(of)e +Fi(a)o Fp(.)24 b(A)0 1464 y(suitable)16 b(de\014nition)h(follo)o(ws:)71 +1564 y Fi(fringe)23 b(::)g(Tree)h(a)f(->)h([a])71 1621 y(fringe)f(\(Leaf)g +(x\))286 b(=)24 b([x])71 1677 y(fringe)f(\(Branch)g(left)g(right\))g(=)h +(fringe)f(left)g(++)h(fringe)f(right)0 1789 y Fp(Where)13 b +Fi(++)f Fp(is)i(the)f(in\014x)h(op)q(erator)e(that)g(concatenates)g(t)o(w)o +(o)g(lists)h(\(its)g(full)h(de\014nition)h(will)g(b)q(e)e(giv)o(en)g(in)h +(Section)0 1845 y(3.2\).)20 b(As)15 b(with)h(the)g Fi(length)e +Fp(example)j(giv)o(en)f(earlier,)g Fi(fringe)f Fp(is)h(de\014ned)h(using)f +(pattern)f(matc)o(hing,)g(except)0 1902 y(that)j(here)g(w)o(e)g(see)h +(patterns)f(in)o(v)o(olving)i(user-de\014ned)g(constructors:)25 +b Fi(Leaf)18 b Fp(and)g Fi(Branch)o Fp(.)30 b([Note)17 b(that)h(the)0 +1958 y(formal)d(parameters)f(are)h(easily)h(iden)o(ti\014ed)i(as)c(the)i +(ones)f(b)q(eginning)i(with)f(lo)o(w)o(er-case)f(letters.])0 +2095 y Fg(2.4)56 b(T)n(yp)r(e)18 b(Synon)n(yms)0 2199 y Fp(F)l(or)g(con)o(v)o +(enience,)i(Hask)o(ell)g(pro)o(vides)e(a)h(w)o(a)o(y)e(to)h(de\014ne)h +Fo(typ)n(e)h(synonyms)p Fp(;)e(i.e.)g(names)h(for)f(commonly)g(used)0 +2255 y(t)o(yp)q(es.)i(T)o(yp)q(e)15 b(synon)o(yms)g(are)g(created)g(using)h +(a)f Fi(type)g Fp(declaration)h(\()p Fn(x)p Fp(4.2.2\).)h(Here)f(are)f(sev)o +(eral)g(examples:)71 2367 y Fi(type)23 b(String)309 b(=)24 +b([Char])71 2423 y(type)f(Person)309 b(=)24 b(\(Name,Address\))71 +2480 y(type)f(Name)357 b(=)24 b(String)71 2536 y(data)f(Address)285 +b(=)24 b(None)f(|)h(Addr)f(String)p eop +%%Page: 7 7 +bop 0 -40 a Fj(2.5)45 b(Built-in)17 b(T)o(yp)q(es)e(Are)h(Not)e(Sp)q(ecial) +1157 b Fp(T-7)71 105 y(T)o(yp)q(e)18 b(synon)o(yms)g(do)g(not)g(de\014ne)i +(new)f(t)o(yp)q(es,)f(but)h(simply)h(giv)o(e)e(new)h(names)f(for)g(existing)h +(t)o(yp)q(es.)30 b(F)l(or)0 162 y(example,)15 b(the)g(t)o(yp)q(e)f +Fi(Person)23 b(->)h(Name)14 b Fp(is)h(precisely)h(equiv)m(alen)o(t)h(to)d +Fi(\(String,Address\))21 b(->)j(String)o Fp(.)c(The)0 218 y(new)f(names)f +(are)g(often)g(shorter)g(than)g(the)h(t)o(yp)q(es)f(they)g(are)h(synon)o +(ymous)f(with,)h(but)f(this)h(is)g(not)f(the)g(only)0 274 y(purp)q(ose)13 +b(of)e(t)o(yp)q(e)h(synon)o(yms:)18 b(they)12 b(can)g(also)g(impro)o(v)o(e)g +(readabilit)o(y)h(of)e(programs)g(b)o(y)h(b)q(eing)h(more)e(mnemonic;)0 +331 y(indeed,)17 b(the)e(ab)q(o)o(v)o(e)g(examples)h(highligh)o(t)g(this.)21 +b(W)l(e)15 b(can)g(ev)o(en)h(giv)o(e)f(new)h(names)f(to)f(p)q(olymorphic)j(t) +o(yp)q(es:)71 440 y Fi(type)23 b(AssocList)g(a)g(b)334 b(=)24 +b([\(a,b\)])0 549 y Fp(This)16 b(is)f(the)h(t)o(yp)q(e)f(of)g(\\asso)q +(ciation)g(lists")h(whic)o(h)g(asso)q(ciate)f(v)m(alues)h(of)f(t)o(yp)q(e)g +Fi(a)g Fp(with)h(those)f(of)g(t)o(yp)q(e)g Fi(b)p Fp(.)0 723 +y Fg(2.5)56 b(Built-in)17 b(T)n(yp)r(es)h(Are)g(Not)g(Sp)r(ecial)0 +845 y Fp(Earlier)12 b(w)o(e)e(in)o(tro)q(duced)j(sev)o(eral)e(\\built-in")i +(t)o(yp)q(es)d(suc)o(h)i(as)e(lists,)j(tuples,)f(in)o(tegers,)f(and)h(c)o +(haracters.)17 b(W)l(e)11 b(ha)o(v)o(e)0 902 y(also)k(sho)o(wn)g(ho)o(w)g +(new)g(user-de\014ned)i(t)o(yp)q(es)f(can)f(b)q(e)h(de\014ned.)21 +b(Aside)c(from)d(sp)q(ecial)j(syn)o(tax,)d(are)h(the)h(built-in)0 +958 y(t)o(yp)q(es)e(in)h(an)o(y)f(w)o(a)o(y)f(more)h(sp)q(ecial)i(than)e(the) +g(user-de\014ned)i(ones?)k(The)14 b(answ)o(er)g(is)h Fo(no)p +Fp(.)k(The)14 b(sp)q(ecial)i(syn)o(tax)d(is)0 1015 y(for)i(con)o(v)o(enience) +h(and)g(for)e(consistency)i(with)g(historical)g(con)o(v)o(en)o(tion,)f(but)h +(has)f(no)g(seman)o(tic)g(consequence.)71 1104 y(W)l(e)d(can)h(emphasize)h +(this)f(p)q(oin)o(t)g(b)o(y)f(considering)i(what)e(the)h(t)o(yp)q(e)f +(declarations)h(w)o(ould)g(lo)q(ok)g(lik)o(e)h(for)e(these)0 +1161 y(built-in)19 b(t)o(yp)q(es)e(if)g(in)h(fact)e(w)o(e)g(w)o(ere)h(allo)o +(w)o(ed)g(to)f(use)h(the)g(sp)q(ecial)h(syn)o(tax)e(in)i(de\014ning)g(them.) +24 b(F)l(or)16 b(example,)0 1217 y(the)f Fi(Char)g Fp(t)o(yp)q(e)g(migh)o(t)g +(b)q(e)h(written)f(as:)71 1326 y Fi(data)23 b(Char)166 b(=)24 +b('a')g(|)f('b')h(|)f('c')h(|)g(...)214 b(--)24 b(This)f(is)h(not)f(valid)452 +1382 y(|)h('A')g(|)f('B')h(|)f('C')h(|)g(...)214 b(--)24 b(Haskell)f(code!) +452 1439 y(|)h('1')g(|)f('2')h(|)f('3')h(|)g(...)452 1495 y(...)0 +1604 y Fp(These)14 b(constructor)f(names)h(are)g(not)f(syn)o(tactically)i(v)m +(alid;)h(to)d(\014x)h(them)g(w)o(e)g(w)o(ould)g(ha)o(v)o(e)f(to)h(write)g +(something)0 1661 y(lik)o(e:)71 1761 y Fi(data)23 b(Char)166 +b(=)24 b(Ca)g(|)f(Cb)h(|)g(Cc)f(|)h(...)452 1818 y(|)g(CA)g(|)f(CB)h(|)g(CC)f +(|)h(...)452 1874 y(|)g(C1)g(|)f(C2)h(|)g(C3)f(|)h(...)452 +1930 y(...)0 2040 y Fp(Ev)o(en)18 b(though)g(these)g(constructors)f(are)g +(more)h(concise,)h(they)f(are)g(quite)g(uncon)o(v)o(en)o(tional)h(for)e +(represen)o(ting)0 2096 y(c)o(haracters.)71 2185 y(In)k(an)o(y)g(case,)h +(writing)g(\\pseudo-Hask)o(ell")g(co)q(de)g(in)g(this)g(w)o(a)o(y)e(helps)i +(us)f(to)g(see)g(through)g(the)g(sp)q(ecial)0 2242 y(syn)o(tax.)h(W)l(e)16 +b(see)g(no)o(w)g(that)f Fi(Char)h Fp(is)g(just)g(an)g(en)o(umerated)g(t)o(yp) +q(e)g(consisting)h(of)f(a)g(large)g(n)o(um)o(b)q(er)g(of)g(n)o(ullary)0 +2298 y(constructors.)j(Thinking)d(of)e Fi(Char)g Fp(in)i(this)f(w)o(a)o(y)e +(mak)o(es)h(it)h(clear)g(wh)o(y)l(,)g(for)f(example,)h(w)o(e)f(can)h +(pattern-matc)o(h)0 2355 y(against)j(c)o(haracters)g(in)i(function)f +(de\014nitions;)j(i.e.,)e(w)o(e)e(w)o(ould)h(exp)q(ect)h(to)e(b)q(e)h(able)g +(to)g(do)f(so)h(for)f(an)o(y)g(of)g(a)0 2411 y(t)o(yp)q(e's)d(constructors.) +71 2501 y([This)20 b(example)i(also)e(demonstrates)g(the)h(use)g(of)f +Fo(c)n(omments)f Fp(in)j(Hask)o(ell;)i(the)c(c)o(haracters)g +Fi(--)g Fp(and)h(all)0 2557 y(subsequen)o(t)f(c)o(haracters)e(to)g(the)h(end) +g(of)g(the)g(line)h(are)f(ignored.)32 b(Hask)o(ell)19 b(also)g(p)q(ermits)h +Fo(neste)n(d)e Fp(commen)o(ts)0 2614 y(whic)o(h)e(ha)o(v)o(e)f(the)g(form)g +Fi({-)o Fh(:)8 b(:)g(:)n Fi(-})15 b Fp(and)h(can)f(app)q(ear)g(an)o(ywhere)g +(\()p Fn(x)p Fp(2.2\).])p eop +%%Page: 8 8 +bop 0 -40 a Fp(T-8)906 b Fj(2)45 b(V)-5 b(ALUES,)16 b(TYPES,)e(AND)h(OTHER)h +(GOODIES)71 105 y Fp(Similarly)l(,)h(w)o(e)e(could)h(de\014ne)g +Fi(Int)f Fp(and)g Fi(Integer)f Fp(b)o(y:)71 215 y Fi(data)23 +b(Int)119 b(=)24 b(-65532)f(|)g(...)h(|)f(-1)h(|)g(0)g(|)f(1)h(|)g(...)f(|)h +(65532)47 b(--)24 b(more)f(pseudo-code)71 272 y(data)g(Integer)g(=)167 +b(...)23 b(-2)h(|)f(-1)h(|)g(0)g(|)f(1)h(|)g(2)f(...)0 381 +y Fp(where)15 b Fi(-65532)g Fp(and)g Fi(65532)p Fp(,)f(sa)o(y)l(,)h(are)g +(the)g(maxim)o(um)g(and)h(minim)o(um)g(\014xed)g(precision)h(in)o(tegers)e +(for)g(a)f(giv)o(en)0 437 y(implemen)o(tation.)22 b Fi(Int)15 +b Fp(is)h(a)f(m)o(uc)o(h)h(larger)f(en)o(umeration)h(than)f +Fi(Char)p Fp(,)g(but)h(it's)f(still)i(\014nite!)22 b(In)16 +b(con)o(trast,)e(the)0 494 y(pseudo-co)q(de)h(for)f Fi(Integer)f +Fp(\(the)g(t)o(yp)q(e)h(of)g(arbitrary)f(precision)j(in)o(tegers\))e(is)g(in) +o(tended)h(to)f(con)o(v)o(ey)g(an)f Fo(in\014nite)0 550 y Fp(en)o(umeration.) +71 671 y(T)l(uples)j(are)f(also)g(easy)g(to)g(de\014ne)h(pla)o(ying)g(this)g +(game:)71 780 y Fi(data)23 b(\(a,b\))333 b(=)24 b(\(a,b\))596 +b(--)24 b(more)f(pseudo-code)71 836 y(data)g(\(a,b,c\))285 +b(=)24 b(\(a,b,c\))71 893 y(data)f(\(a,b,c,d\))237 b(=)24 b(\(a,b,c,d\))94 +949 y(.)597 b(.)94 1006 y(.)g(.)94 1062 y(.)g(.)0 1171 y Fp(Eac)o(h)12 +b(declaration)h(ab)q(o)o(v)o(e)e(de\014nes)j(a)d(tuple)i(t)o(yp)q(e)g(of)e(a) +h(particular)h(length,)g(with)f Fi(\(...\))g Fp(pla)o(ying)h(a)e(role)i(in)g +(b)q(oth)0 1228 y(the)g(expresssion)g(syn)o(tax)f(\(as)f(data)h +(constructor\))g(and)g(t)o(yp)q(e-expression)i(syn)o(tax)e(\(as)f(t)o(yp)q(e) +i(constructor\).)18 b(The)0 1284 y(v)o(ertical)11 b(dots)f(after)g(the)g +(last)h(declaration)g(are)f(in)o(tended)i(to)e(con)o(v)o(ey)g(an)h +(in\014nite)h(n)o(um)o(b)q(er)f(of)f(suc)o(h)h(declarations,)0 +1341 y(re\015ecting)16 b(the)f(fact)g(that)g(tuples)h(of)e(all)j(lengths)e +(are)g(allo)o(w)o(ed)h(in)g(Hask)o(ell.)71 1461 y(Lists)f(are)g(also)g +(easily)i(handled,)f(and)f(more)g(in)o(terestingly)l(,)h(they)g(are)f +(recursiv)o(e:)94 1570 y Fi(data)24 b([a])357 b(=)24 b([])g(|)f(a)h(:)g([a]) +429 b(--)24 b(more)f(pseudo-code)0 1679 y Fp(W)l(e)13 b(can)g(no)o(w)g(see)g +(clearly)i(what)d(w)o(e)h(describ)q(ed)i(ab)q(out)e(lists)h(earlier:)20 +b Fi([])12 b Fp(is)i(the)f(empt)o(y)g(list,)h(and)f Fi(:)g +Fp(is)h(the)f(in\014x)0 1736 y(list)20 b(constructor;)f(th)o(us)g +Fi([1,2,3])f Fp(m)o(ust)g(b)q(e)i(equiv)m(alen)o(t)g(to)e(the)h(list)h +Fi(1:2:3:[])o Fp(.)31 b(\()p Fi(:)18 b Fp(is)i(righ)o(t)e(asso)q(ciativ)o +(e.\))0 1792 y(The)d(t)o(yp)q(e)h(of)e Fi([])h Fp(is)h Fi([a])p +Fp(,)e(and)i(the)f(t)o(yp)q(e)g(of)g Fi(:)g Fp(is)h Fi(a->[a]->[a])n +Fp(.)71 1913 y([The)i(w)o(a)o(y)f Fi(:)h Fp(is)h(de\014ned)h(here)f(is)g +(actually)g(legal)g(syn)o(tax|in\014x)h(constructors)d(are)h(p)q(ermitted)h +(in)h Fi(data)0 1969 y Fp(declarations,)13 b(and)f(are)g(distinguished)j +(from)c(in\014x)j(op)q(erators)d(\(for)g(pattern-matc)o(hing)h(purp)q(oses\)) +g(b)o(y)g(the)h(fact)0 2026 y(that)h(they)i(m)o(ust)e(b)q(egin)j(with)e(a)g +Fi(:)g Fp(\(a)g(prop)q(ert)o(y)g(trivially)i(satis\014ed)e(b)o(y)g +Fi(:)p Fp(\).])71 2146 y(A)o(t)i(this)i(p)q(oin)o(t)f(the)g(reader)g(should)h +(note)f(carefully)h(the)f(di\013erences)i(b)q(et)o(w)o(een)e(tuples)h(and)f +(lists,)h(whic)o(h)0 2203 y(the)g(ab)q(o)o(v)o(e)f(de\014nitions)i(mak)o(e)e +(abundan)o(tly)h(clear.)31 b(In)19 b(particular,)h(note)e(the)h(recursiv)o(e) +g(nature)f(of)h(the)f(list)0 2259 y(t)o(yp)q(e)f(whose)g(elemen)o(ts)h(are)e +(homogeneous)h(and)g(of)g(arbitrary)f(length,)i(and)f(the)g(non-recursiv)o(e) +h(nature)f(of)f(a)0 2316 y(\(particular\))g(tuple)h(t)o(yp)q(e)f(whose)g +(elemen)o(ts)h(are)e(heterogenous)h(and)g(of)g(\014xed)g(length.)23 +b(The)17 b(t)o(yping)f(rules)h(for)0 2372 y(tuples)f(and)f(lists)h(should)h +(no)o(w)d(also)h(b)q(e)h(clear:)71 2493 y(F)l(or)e Fi(\()p +Fh(e)197 2500 y Fm(1)216 2493 y Fi(,)p Fh(e)261 2500 y Fm(2)281 +2493 y Fi(,)8 b Fh(:)g(:)g(:)d Fi(,)p Fh(e)418 2500 y Fd(n)442 +2493 y Fi(\))p Fh(;)22 b(n)13 b Fn(\025)g Fp(2,)h(if)h Fh(t)696 +2500 y Fd(i)725 2493 y Fp(is)h(the)e(t)o(yp)q(e)h(of)g Fh(e)1023 +2500 y Fd(i)1037 2493 y Fp(,)f(then)i(the)e(t)o(yp)q(e)h(of)g(the)g(tuple)g +(is)h Fi(\()p Fh(t)1678 2500 y Fm(1)1698 2493 y Fi(,)p Fh(t)1738 +2500 y Fm(2)1758 2493 y Fi(,)7 b Fh(:)h(:)g(:)e Fi(,)p Fh(t)1890 +2500 y Fd(n)1914 2493 y Fi(\))o Fp(.)71 2614 y(F)l(or)14 b +Fi([)p Fh(e)197 2621 y Fm(1)217 2614 y Fi(,)p Fh(e)262 2621 +y Fm(2)282 2614 y Fi(,)7 b Fh(:)h(:)g(:)e Fi(,)p Fh(e)419 2621 +y Fd(n)442 2614 y Fi(])p Fh(;)22 b(n)13 b Fn(\025)g Fp(0,)i(eac)o(h)g +Fh(e)763 2621 y Fd(i)792 2614 y Fp(m)o(ust)g(ha)o(v)o(e)g(the)g(same)g(t)o +(yp)q(e)g Fh(t)p Fp(,)h(and)f(the)g(t)o(yp)q(e)g(of)g(the)h(list)g(is)f +Fi([)p Fh(t)p Fi(])p Fp(.)p eop +%%Page: 9 9 +bop 0 -40 a Fj(2.5)45 b(Built-in)17 b(T)o(yp)q(es)e(Are)h(Not)e(Sp)q(ecial) +1157 b Fp(T-9)0 105 y Fc(2.5.1)52 b(List)18 b(Comprehensions)e(and)i +(Arithmetic)g(Sequences)0 211 y Fp(As)13 b(with)g(Lisp)h(dialects,)h(lists)e +(are)g(p)q(erv)m(asiv)o(e)h(in)g(Hask)o(ell,)g(and)f(as)f(with)i(other)e +(functional)i(languages,)f(there)g(is)0 268 y(y)o(et)e(more)g(syn)o(tactic)h +(sugar)f(to)f(aid)j(in)f(their)g(creation.)19 b(Aside)12 b(from)f(the)h +(constructors)e(for)h(lists)i(just)e(discussed,)0 324 y(Hask)o(ell)16 +b(pro)o(vides)g(an)f(expression)h(kno)o(wn)f(as)g(a)g Fo(list)g(c)n(ompr)n +(ehension)f Fp(that)h(is)g(b)q(est)h(explained)h(b)o(y)e(example:)71 +433 y Fi([)23 b(f)h(x)g(|)f(x)h(<-)g(xs)f(])0 542 y Fp(This)17 +b(expression)f(can)h(in)o(tuitiv)o(ely)h(b)q(e)e(read)g(as)g(\\the)g(list)g +(of)g(all)h Fi(f)24 b(x)15 b Fp(suc)o(h)i(that)e Fi(x)h Fp(is)g(dra)o(wn)g +(from)f Fi(xs)p Fp(.")22 b(The)0 599 y(similarit)o(y)d(to)f(set)g(notation)f +(is)i(not)e(a)h(coincidence.)31 b(The)19 b(phrase)f Fi(x<-xs)f +Fp(is)i(called)g(a)f Fo(gener)n(ator)p Fp(,)g(of)g(whic)o(h)0 +655 y(more)d(than)g(one)g(is)h(allo)o(w)o(ed,)f(as)g(in:)71 +764 y Fi([)23 b(\(x,y\))g(|)h(x<-xs,)f(y<-ys)g(])0 873 y Fp(This)17 +b(list)f(comprehension)i(forms)d(the)h(cartesian)g(pro)q(duct)g(of)g(the)g(t) +o(w)o(o)e(lists)j Fi(xs)f Fp(and)g Fi(ys)o Fp(.)22 b(The)17 +b(elemen)o(ts)f(are)0 930 y(selected)f(as)f(if)h(the)f(generators)g(w)o(ere)g +(\\nested")g(from)f(left)i(to)e(righ)o(t)h(\(with)h(the)f(righ)o(tmost)f +(generator)g(v)m(arying)0 986 y(fastest\);)g(th)o(us,)i(if)h +Fi(xs)f Fp(is)g Fi([1,2])g Fp(and)g Fi(ys)g Fp(is)h Fi([3,4])o +Fp(,)f(the)g(result)h(is)g Fi([\(1,3\),\(1,4\),\(2,3\),\(2,4)o(\)])m +Fp(.)71 1063 y(Besides)h(generators,)e(b)q(o)q(olean)i(expressions)g(called)g +Fo(guar)n(ds)g Fp(are)e(p)q(ermitted.)24 b(Guards)15 b(place)i(constrain)o +(ts)0 1119 y(on)d(the)g(elemen)o(ts)h(generated.)k(F)l(or)13 +b(example,)i(here)f(is)h(a)f(concise)h(de\014nition)h(of)d(ev)o(eryb)q(o)q +(dy's)h(fa)o(v)o(orite)f(sorting)0 1176 y(algorithm:)71 1285 +y Fi(quicksort)46 b([])262 b(=)48 b([])71 1341 y(quicksort)22 +b(\(x:xs\))190 b(=)48 b(quicksort)23 b([y)g(|)h(y)g(<-)f(xs,)h(y=x])71 +1609 y Fp(T)l(o)16 b(further)h(supp)q(ort)g(the)g(use)g(of)g(lists,)h(Hask)o +(ell)f(has)g(sp)q(ecial)i(syn)o(tax)d(for)h Fo(arithmetic)h(se)n(quenc)n(es)p +Fp(,)d(whic)o(h)0 1665 y(are)g(b)q(est)g(explained)j(b)o(y)d(a)g(series)g(of) +g(examples:)360 1750 y Fi([1..10])133 b Fn(\))88 b Fi([1,2,3,4,5,6,7,8,9,10]) +360 1806 y([1,3..10])d Fn(\))j Fi([1,3,5,7,9])360 1862 y([1,3..])133 +b Fn(\))88 b Fi([1,3,5,7,9,)22 b(...)76 b Fp(\(in\014nite)16 +b(sequence\))0 1947 y(More)f(will)h(b)q(e)g(said)g(ab)q(out)f(arithmetic)h +(sequences)g(in)g(Section)g(5.2,)e(and)i(\\in\014nite)g(lists")g(in)g +(Section)g(3.4.)0 2085 y Fc(2.5.2)52 b(Strings)0 2192 y Fp(As)15 +b(another)f(example)h(of)f(syn)o(tactic)h(sugar)f(for)g(built-in)j(t)o(yp)q +(es,)d(w)o(e)h(note)f(that)g(the)g(literal)i(string)f Fi("hello")e +Fp(is)0 2248 y(actually)h(shorthand)f(for)f(the)h(list)h(of)f(c)o(haracters)f +Fi(['h','e','l','l','o'])m Fp(.)19 b(Indeed,)c(the)e(t)o(yp)q(e)g(of)g +Fi("hello")0 2305 y Fp(is)j Fi(String)o Fp(,)f(where)g Fi(String)f +Fp(is)i(a)f(prede\014ned)i(t)o(yp)q(e)e(synon)o(ym)g(\(that)f(w)o(e)h(ga)o(v) +o(e)f(as)h(an)g(earlier)h(example\):)71 2416 y Fi(type)23 b(String)309 +b(=)24 b([Char])0 2525 y Fp(This)16 b(means)f(w)o(e)g(can)g(use)h +(prede\014ned)h(p)q(olymorphic)f(list)g(functions)g(to)f(op)q(erate)g(on)g +(strings.)20 b(F)l(or)14 b(example:)497 2614 y Fi("hello")23 +b(++)h(")f(world")73 b Fn(\))h Fi("hello)23 b(world")p eop +%%Page: 10 10 +bop 0 -40 a Fp(T-10)1513 b Fj(3)45 b(FUNCTIONS)0 105 y Fq(3)69 +b(F)-6 b(unctions)0 266 y Fp(Since)15 b(Hask)o(ell)g(is)f(a)f(functional)i +(language,)f(one)f(w)o(ould)h(exp)q(ect)h(functions)f(to)f(pla)o(y)h(a)f(ma)s +(jor)g(role,)g(and)h(indeed)0 323 y(they)h(do.)20 b(In)c(this)g(section,)f(w) +o(e)g(lo)q(ok)g(at)g(sev)o(eral)g(asp)q(ects)h(of)e(functions)i(in)g(Hask)o +(ell.)71 428 y(First,)e(consider)i(this)g(de\014nition)h(of)e(a)g(function)h +(whic)o(h)g(adds)f(its)g(t)o(w)o(o)f(argumen)o(ts:)71 537 y +Fi(add)500 b(::)24 b(Int)f(->)h(Int)f(->)h(Int)71 593 y(add)f(x)h(y)405 +b(=)24 b(x)g(+)f(y)0 709 y Fp(This)18 b(is)g(an)f(example)i(of)e(a)g +Fo(currie)n(d)h Fp(function.)826 692 y Fm(6)873 709 y Fp(An)g(application)h +(of)e Fi(add)g Fp(has)g(the)h(form)f Fi(add)23 b Fh(e)1720 +716 y Fm(1)1757 709 y Fh(e)1778 716 y Fm(2)1798 709 y Fp(,)18 +b(and)f(is)0 765 y(equiv)m(alen)o(t)c(to)d Fi(\(add)24 b Fh(e)404 +772 y Fm(1)423 765 y Fi(\))g Fh(e)492 772 y Fm(2)512 765 y +Fp(,)12 b(since)g(function)g(application)h(asso)q(ciates)e(to)f(the)h +Fo(left)p Fp(.)18 b(In)12 b(other)f(w)o(ords,)g(applying)0 +822 y Fi(add)k Fp(to)g(one)h(argumen)o(t)f(yields)i(a)f(new)g(function)g +(whic)o(h)h(is)f(then)g(applied)i(to)d(the)g(second)i(argumen)o(t.)j(This)c +(is)0 878 y(consisten)o(t)g(with)h(the)f(t)o(yp)q(e)g(of)f +Fi(add)p Fp(,)h Fi(Int->Int->Int)n Fp(,)g(whic)o(h)h(is)f(equiv)m(alen)o(t)i +(to)d Fi(Int->\(Int->Int\))n Fp(;)h(i.e.)g Fi(->)0 934 y Fp(asso)q(ciates)f +(to)f(the)h Fo(right)p Fp(.)21 b(Indeed,)16 b(using)g Fi(add)o +Fp(,)f(w)o(e)g(can)g(de\014ne)h Fi(succ)e Fp(in)i(a)f(di\013eren)o(t)g(w)o(a) +o(y)f(from)h(what)f(w)o(e)h(did)0 991 y(earlier:)71 1091 y +Fi(succ)476 b(=)24 b(add)f(1)0 1200 y Fp(This)16 b(is)h(an)f(example)g(of)g +(the)g Fo(p)n(artial)h(applic)n(ation)f Fp(of)f(a)h(curried)h(function,)f +(and)g(is)h(one)f(w)o(a)o(y)e(that)i(a)f(function)0 1257 y(can)20 +b(b)q(e)h(returned)g(as)f(a)f(v)m(alue.)36 b(Let's)20 b(consider)i(a)d(case)i +(in)g(whic)o(h)g(it's)f(useful)h(to)e(pass)h(a)g(function)h(as)f(an)0 +1313 y(argumen)o(t.)f(The)d(w)o(ell-kno)o(wn)g Fi(map)e Fp(function)i(is)g(a) +f(p)q(erfect)h(example:)71 1423 y Fi(map)500 b(::)24 b(\(a->b\))f(->)h([a])f +(->)h([b])71 1480 y(map)f(f)48 b([])357 b(=)24 b([])71 1536 +y(map)f(f)h(\(x:xs\))285 b(=)24 b(f)g(x)f(:)h(map)g(f)f(xs)0 +1648 y Fp([F)l(unction)16 b(application)h(has)f(higher)g(precedence)i(than)d +(an)o(y)h(in\014x)g(op)q(erator,)f(and)h(th)o(us)f(the)h(righ)o(t-hand)g +(side)0 1704 y(of)h(the)h(second)g(equation)f(parses)h(as)f +Fi(\(f)23 b(x\))h(:)g(\(map)f(f)h(xs\))o Fp(.])52 b Fi(map)17 +b Fp(is)h(a)f(p)q(olymorphic)i(function,)f(and)g(its)0 1761 +y(t)o(yp)q(e)j(indicates)h(clearly)g(that)e(its)g(\014rst)h(argumen)o(t)f(is) +h(a)f(function;)k(note)c(also)h(that)f(the)h(t)o(w)o(o)e Fi(a)p +Fp('s)h(m)o(ust)g(b)q(e)0 1817 y(instan)o(tiated)e(with)h(the)f(same)g(t)o +(yp)q(e)g(\(lik)o(ewise)i(for)d(the)i Fi(b)p Fp('s\).)27 b(As)19 +b(an)f(example)h(of)e(the)i(use)f(of)g Fi(map)o Fp(,)h(w)o(e)f(can)0 +1874 y(incremen)o(t)e(the)f(elemen)o(ts)h(in)g(a)f(list:)569 +2011 y Fi(map)23 b(\(add)h(1\))f([1,2,3])72 b Fn(\))i Fi([2,3,4])71 +2197 y Fp(These)18 b(examples)h(demonstrate)e(the)h(\014rst-class)g(nature)g +(of)g(functions,)h(whic)o(h)g(when)f(used)h(in)g(this)f(w)o(a)o(y)0 +2254 y(are)d(usually)h(called)h Fo(higher-or)n(der)f Fp(functions.)p +0 2341 780 2 v 52 2368 a Fl(6)69 2383 y Fk(The)i(name)h Fe(curry)e +Fk(deriv)o(es)i(from)f(the)h(p)q(erson)g(who)f(p)q(opularized)j(the)e(idea:) +28 b(Hask)o(ell)20 b(Curry)m(.)32 b(T)m(o)18 b(get)g(the)g(e\013ect)h(of)e +(an)0 2429 y Fe(uncurrie)n(d)11 b Fk(function,)j(w)o(e)e(could)j(use)e(a)g +Fe(tuple)p Fk(,)e(as)i(in:)71 2518 y Ff(add)18 b(\(x,y\))292 +b(=)20 b(x)f(+)g(y)0 2614 y Fk(But)13 b(then)h(w)o(e)e(see)h(that)h(this)f(v) +o(ersion)i(of)e Ff(add)e Fk(is)j(really)h(just)e(a)g(function)h(of)f(one)g +(argumen)o(t!)p eop +%%Page: 11 11 +bop 0 -40 a Fj(3.1)45 b(Lam)o(b)q(da)15 b(Abstractions)1324 +b Fp(T-11)0 105 y Fg(3.1)56 b(Lam)n(b)r(da)17 b(Abstractions)0 +215 y Fp(Instead)c(of)f(using)i(equations)e(to)g(de\014ne)i(functions,)g(w)o +(e)e(can)h(also)f(de\014ne)i(them)f(\\anon)o(ymously")f(via)h(a)f +Fo(lamb)n(da)0 271 y(abstr)n(action)p Fp(.)19 b(F)l(or)c(example,)g(a)g +(function)h(equiv)m(alen)o(t)g(to)f Fi(succ)f Fp(could)i(b)q(e)g(written)f +(as)f Fi(\\x)24 b(->)g(x+1)o Fp(.)c(Similarly)l(,)0 328 y(the)15 +b(function)h Fi(add)f Fp(is)h(equiv)m(alen)o(t)h(to)e Fi(\\x)23 +b(->)h(\\y)f(->)h(x+y)p Fp(.)c(Nested)15 b(lam)o(b)q(da)h(abstractions)f(suc) +o(h)g(as)g(this)h(ma)o(y)0 384 y(b)q(e)g(written)f(using)h(the)f(equiv)m +(alen)o(t)i(shorthand)e(notation)g Fi(\\x)24 b(y)f(->)h(x+y)p +Fp(.)19 b(In)d(fact,)e(the)i(equations:)71 494 y Fi(succ)23 +b(x)429 b(=)24 b(x+1)71 551 y(add)47 b(x)24 b(y)381 b(=)24 +b(x+y)0 660 y Fp(are)15 b(really)h(shorthand)f(for:)71 769 +y Fi(succ)476 b(=)24 b(\\x)71 b(->)24 b(x+1)71 825 y(add)500 +b(=)24 b(\\x)g(y)f(->)h(x+y)0 934 y Fp(W)l(e)15 b(will)i(ha)o(v)o(e)e(more)g +(to)f(sa)o(y)h(ab)q(out)g(suc)o(h)g(equiv)m(alences)j(later.)71 +1015 y(In)d(general,)h(giv)o(en)f(that)g Fi(x)g Fp(has)g(t)o(yp)q(e)g +Fh(t)752 1022 y Fm(1)788 1015 y Fp(and)g Fi(exp)g Fp(has)g(t)o(yp)q(e)g +Fh(t)1162 1022 y Fm(2)1182 1015 y Fp(,)g(then)g Fi(\\x->exp)g +Fp(has)g(t)o(yp)q(e)g Fh(t)1695 1022 y Fm(1)1715 1015 y Fi(->)p +Fh(t)1779 1022 y Fm(2)1799 1015 y Fp(.)0 1160 y Fg(3.2)56 b(In\014x)18 +b(Op)r(erators)0 1270 y Fp(In\014x)c(op)q(erators)d(are)i(really)h(just)e +(functions,)i(and)f(can)g(also)f(b)q(e)i(de\014ned)g(using)f(equations.)20 +b(F)l(or)12 b(example,)i(here)0 1326 y(is)i(the)f(de\014nition)i(of)e(Hask)o +(ell's)g(list)h(concatenation)g(op)q(erator:)71 1435 y Fi(\(++\))476 +b(::)24 b([a])f(->)h([a])f(->)h([a])71 1491 y([])119 b(++)23 +b(ys)286 b(=)48 b(ys)71 1548 y(\(x:xs\))23 b(++)g(ys)286 b(=)48 +b(x)24 b(:)f(\(xs++ys\))0 1660 y Fp([Lexically)l(,)17 b(in\014x)f(op)q +(erators)d(consist)i(en)o(tirely)h(of)f(\\sym)o(b)q(ols,")f(as)g(opp)q(osed)i +(to)e(normal)g(iden)o(ti\014ers)j(whic)o(h)e(are)0 1716 y(alphan)o(umeric)20 +b(\()p Fn(x)p Fp(2.3\).)30 b(Hask)o(ell)20 b(has)e(no)h(pre\014x)h(op)q +(erators,)e(with)h(the)g(exception)h(of)f(min)o(us)g(\()p Fi(-)p +Fp(\),)g(whic)o(h)g(is)0 1772 y(b)q(oth)c(in\014x)i(and)e(pre\014x.])71 +1853 y(As)f(another)h(example,)g(an)f(imp)q(ortan)o(t)h(in\014x)g(op)q +(erator)f(on)h(functions)g(is)g(that)f(for)g Fo(function)i(c)n(omp)n(osition) +p Fp(:)71 1962 y Fi(\(.\))500 b(::)24 b(\(b->c\))f(->)h(\(a->b\))f(->)g +(\(a->c\))71 2018 y(f)g(.)h(g)453 b(=)24 b(\\)g(x)f(->)h(f)g(\(g)f(x\))0 +2239 y Fc(3.2.1)52 b(Sections)0 2349 y Fp(Since)16 b(in\014x)g(op)q(erators)e +(are)h(really)h(just)f(functions,)g(it)g(mak)o(es)f(sense)i(to)e(b)q(e)i +(able)f(to)g(partially)h(apply)f(them)g(as)0 2405 y(w)o(ell.)21 +b(In)16 b(Hask)o(ell)g(the)f(partial)h(application)g(of)f(an)g(in\014x)i(op)q +(erator)d(is)i(called)g(a)f Fo(se)n(ction)p Fp(.)k(F)l(or)c(example:)676 +2500 y Fi(\(x+\))102 b Fn(\021)126 b Fi(\\y)24 b(->)f(x+y)676 +2557 y(\(+y\))102 b Fn(\021)126 b Fi(\\x)24 b(->)f(x+y)688 +2613 y(\(+\))114 b Fn(\021)102 b Fi(\\x)24 b(y)g(->)f(x+y)p +eop +%%Page: 12 12 +bop 0 -40 a Fp(T-12)1513 b Fj(3)45 b(FUNCTIONS)0 105 y Fp([The)15 +b(paren)o(theses)g(are)g(mandatory)l(.])71 199 y(The)20 b(last)f(form)g(of)g +(section)i(giv)o(en)f(ab)q(o)o(v)o(e)f(essen)o(tially)i(co)q(erces)g(an)e +(in\014x)i(op)q(erator)e(in)o(to)h(an)f(equiv)m(alen)o(t)0 +255 y(functional)h(v)m(alue,)h(and)e(is)g(handy)g(when)h(passing)f(an)g +(in\014x)h(op)q(erator)e(as)g(an)h(argumen)o(t)f(to)h(a)f(function,)i(as)0 +312 y(in)i Fi(map)h(\(+\))g([1,2,3])d Fp(\(the)h(reader)f(should)i(v)o(erify) +f(that)f(this)h(returns)f(a)h(list)g(of)g(functions!\).)36 +b(It)21 b(is)g(also)0 368 y(necessary)15 b(when)g(giving)g(a)g(function)g(t)o +(yp)q(e)g(signature,)f(as)h(in)g(the)g(examples)g(of)f Fi(\(++\))g +Fp(and)h Fi(\(.\))f Fp(giv)o(en)h(earlier.)71 462 y(W)l(e)20 +b(can)h(no)o(w)g(see)g(that)f Fi(add)g Fp(de\014ned)i(earlier)g(is)f(just)g +Fi(\(+\))p Fp(,)g(and)g Fi(succ)f Fp(is)i(just)e Fi(\(+1\))p +Fp(!)36 b(Indeed,)24 b(these)0 518 y(de\014nitions)17 b(w)o(ould)f(do)f(just) +g(\014ne:)71 627 y Fi(succ)476 b(=)24 b(\(+1\))71 684 y(add)500 +b(=)24 b(\(+\))71 855 y Fp(W)l(e)14 b(can)g(co)q(erce)h(an)f(in\014x)h(op)q +(erator)e(in)o(to)h(a)g(functional)h(v)m(alue,)g(but)g(can)f(w)o(e)g(go)f +(the)h(other)g(w)o(a)o(y?)19 b(Y)l(es|w)o(e)0 912 y(simply)g(enclose)g(an)f +(iden)o(ti\014er)h(b)q(ound)g(to)e(a)h(functional)h(v)m(alue)g(in)g(bac)o +(kquotes.)27 b(F)l(or)17 b(example,)i Fi(x)24 b(`add`)f(y)0 +968 y Fp(is)17 b(the)g(same)g(as)f Fi(add)23 b(x)h(y)p Fp(.)480 +952 y Fm(7)524 968 y Fp(Some)17 b(functions)h(read)e(b)q(etter)h(this)g(w)o +(a)o(y)l(.)24 b(An)17 b(example)h(is)f(the)g(prede\014ned)h(list)0 +1025 y(mem)o(b)q(ership)13 b(predicate)g Fi(elem)p Fp(;)f(the)g(expression)h +Fi(x)24 b(`elem`)f(xs)12 b Fp(can)g(b)q(e)h(read)f(in)o(tuitiv)o(ely)i(as)d +(\\)p Fi(x)h Fp(is)g(an)g(elemen)o(t)0 1081 y(of)j Fi(xs)o +Fp(.")71 1175 y([There)c(are)g(some)g(sp)q(ecial)i(rules)f(regarding)g +(sections)g(in)o(v)o(olving)g(the)g(pre\014x/in\014x)g(op)q(erator)f +Fi(-)g Fp(\()p Fn(x)p Fp(3.4,)p Fn(x)o Fp(3.3\).])71 1269 y(A)o(t)16 +b(this)i(p)q(oin)o(t,)g(the)f(reader)g(ma)o(y)g(b)q(e)h(confused)g(at)e(ha)o +(ving)i(so)f(man)o(y)g(w)o(a)o(ys)f(to)g(de\014ne)j(a)e(function!)27 +b(The)0 1325 y(decision)18 b(to)d(pro)o(vide)h(these)g(mec)o(hanisms)g +(partly)g(re\015ects)g(historical)h(con)o(v)o(en)o(tions,)e(and)h(partly)g +(re\015ects)g(the)0 1382 y(desire)g(for)f(consistency)h(\(for)e(example,)i +(in)g(the)f(treatmen)o(t)f(of)h(in\014x)h(vs.)k(regular)15 +b(functions\).)0 1568 y Fc(3.2.2)52 b(Fixit)o(y)17 b(Declarations)0 +1697 y Fp(A)f Fo(\014xity)g(de)n(clar)n(ation)f Fp(can)h(b)q(e)h(giv)o(en)f +(for)f(an)o(y)g(in\014x)i(op)q(erator)e(or)g(constructor)g(\(including)j +(those)e(made)g(from)0 1754 y(ordinary)j(iden)o(ti\014ers,)j(suc)o(h)d(as)g +Fi(`elem`)o Fp(\).)754 1737 y Fm(8)805 1754 y Fp(This)h(declaration)g(sp)q +(eci\014es)g(a)f(precedence)i(lev)o(el)g(from)d(0)h(to)f(9)0 +1810 y(\(with)e(9)g(b)q(eing)i(the)e(strongest;)f(normal)i(application)h(is)e +(assumed)h(to)e(ha)o(v)o(e)h(a)g(precedence)i(lev)o(el)g(of)e(10\),)f(and)0 +1867 y(left-,)g(righ)o(t-,)g(or)g(non-asso)q(ciativit)o(y)l(.)21 +b(F)l(or)14 b(example,)i(the)f(\014xit)o(y)h(declarations)f(for)g +Fi(++)g Fp(and)g Fi(.)g Fp(are:)71 1976 y Fi(infixr)23 b(5)g(++)71 +2032 y(infixr)g(9)g(.)0 2141 y Fp(Both)18 b(of)g(these)g(sp)q(ecify)i(righ)o +(t-asso)q(ciativit)o(y)l(,)f(the)g(\014rst)e(with)i(a)f(precedence)i(lev)o +(el)g(of)d(5,)i(the)f(other)g(9.)29 b(Left)0 2198 y(asso)q(ciativit)o(y)15 +b(is)h(sp)q(eci\014ed)g(via)g Fi(infixl)o Fp(,)e(and)h(non-asso)q(ciativit)o +(y)h(b)o(y)f Fi(infix)o Fp(.)20 b(Also,)14 b(the)h(\014xit)o(y)g(of)g(more)f +(than)0 2254 y(one)h(op)q(erator)f(ma)o(y)g(b)q(e)h(sp)q(eci\014ed)i(with)f +(the)e(same)h(\014xit)o(y)g(declaration.)21 b(If)15 b(no)f(\014xit)o(y)h +(declaration)h(is)f(giv)o(en)h(for)0 2310 y(a)c(particular)h(op)q(erator,)f +(it)h(defaults)g(to)f Fi(infixl)23 b(9)p Fp(.)c(\(See)13 b +Fn(x)p Fp(5.7)e(for)h(a)g(detailed)i(de\014nition)h(of)d(the)h(asso)q +(ciativit)o(y)0 2367 y(rules.\))p 0 2434 780 2 v 52 2461 a +Fl(7)69 2477 y Fk(Note)g(carefully)i(that)e Ff(add)f Fk(is)i(enclosed)h(in)e +Fe(b)n(ackquotes)p Fk(,)c(not)14 b Fe(ap)n(ostr)n(ophes)9 b +Fk(as)14 b(used)f(in)h(the)g(syn)o(tax)g(of)e(c)o(haracters;)i(i.e.)j +Ff('f')12 b Fk(is)0 2522 y(a)g(c)o(haracter,)h(whereas)g Ff(`f`)e +Fk(is)i(an)f(in\014x)i(op)q(erator.)k(F)m(ortunately)m(,)13 +b(most)f(ASCI)q(I)g(terminals)i(distingui)q(sh)h(these)e(m)o(uc)o(h)g(b)q +(etter)f(than)0 2568 y(the)h(t)o(yp)q(efon)o(t)h(used)f(in)h(this)g(man)o +(uscript.)52 2598 y Fl(8)69 2614 y Fk(Fixit)o(y)f(declarations)i(m)o(ust)c +(only)i(app)q(ear)g(at)e(the)h(v)o(ery)g(b)q(eginning)j(of)c(a)g(Hask)o(ell)j +Fe(mo)n(dule)p Fk(,)c(as)h(will)i(b)q(e)f(describ)q(ed)i(in)e(Section)h(6.)p +eop +%%Page: 13 13 +bop 0 -40 a Fj(3.3)45 b(F)l(unctions)15 b(are)g(Non-strict)1269 +b Fp(T-13)0 105 y Fg(3.3)56 b(F)-5 b(unctions)19 b(are)f(Non-strict)0 +215 y Fp(Supp)q(ose)e Fi(bot)f Fp(is)h(de\014ned)g(b)o(y:)71 +324 y Fi(bot)500 b(=)24 b(bot)0 433 y Fp(In)c(other)e(w)o(ords,)h +Fi(bot)g Fp(is)h(a)e(non-terminating)i(expression.)33 b(Abstractly)l(,)20 +b(w)o(e)e(denote)i(the)f Fo(value)g Fp(of)g(a)g(non-)0 489 +y(terminating)d(expression)h(as)f Fn(?)g Fp(\(read)g(\\b)q(ottom"\).)21 +b(Expressions)16 b(that)g(result)g(in)h(some)f(kind)h(of)e(a)h(run-time)0 +546 y(error,)e(suc)o(h)i(as)e Fi(1/0)p Fp(,)h(also)g(ha)o(v)o(e)g(this)g(v)m +(alue.)71 626 y(A)f(function)i Fi(f)e Fp(is)h(said)g(to)f(b)q(e)i +Fo(strict)e Fp(if,)h(when)g(applied)h(to)e(a)h(non)o(terminating)g +(expression,)g(it)g(also)f(fails)i(to)0 682 y(terminate.)j(In)14 +b(other)g(w)o(ords,)e Fi(f)i Fp(is)g(strict)f(i\013)h(the)f(v)m(alue)i(of)e +Fi(f)24 b(bot)13 b Fp(is)h Fn(?)p Fp(.)20 b(F)l(or)13 b(most)f(programming)h +(languages,)0 739 y Fo(al)r(l)k Fp(functions)h(are)e(strict.)25 +b(But)17 b(this)h(is)f(not)g(so)g(in)g(Hask)o(ell.)27 b(As)17 +b(a)f(simple)j(example,)f(consider)g Fi(const1)o Fp(,)f(the)0 +795 y(constan)o(t)d(1)h(function,)h(de\014ned)g(b)o(y:)71 904 +y Fi(const1)23 b(x)381 b(=)24 b(1)0 1013 y Fp(The)18 b(v)m(alue)h(of)f +Fi(const1)23 b(bot)17 b Fp(in)i(Hask)o(ell)f(is)h Fi(1)p Fp(.)27 +b(Op)q(erationally)20 b(sp)q(eaking,)f(since)g Fi(const1)e +Fp(do)q(es)h(not)g(\\need")0 1070 y(the)d(v)m(alue)g(of)f(its)h(argumen)o(t,) +f(it)g(nev)o(er)h(attempts)e(to)h(ev)m(aluate)i(it,)e(and)h(th)o(us)f(nev)o +(er)h(gets)f(caugh)o(t)g(in)h(a)f(non)o(ter-)0 1126 y(minating)k +(computation.)26 b(F)l(or)16 b(this)i(reason,)f(non-strict)h(functions)g(are) +e(also)i(called)g(\\lazy)g(functions,")g(and)0 1183 y(are)d(said)h(to)e(ev)m +(aluate)i(their)g(argumen)o(ts)e(\\lazily)l(,")i(or)f(\\b)o(y)g(need.")71 +1263 y(Since)e(error)e(and)h(non)o(terminating)h(v)m(alues)g(are)e(seman)o +(tically)j(the)e(same)f(in)i(Hask)o(ell,)g(the)f(ab)q(o)o(v)o(e)g(argumen)o +(t)0 1319 y(also)j(holds)h(for)f(errors.)k(F)l(or)14 b(example,)i +Fi(const1)23 b(\(1/0\))15 b Fp(also)g(ev)m(aluates)h(prop)q(erly)g(to)e +Fi(1)p Fp(.)71 1400 y(Non-strict)h(functions)i(are)f(extremely)g(useful)h(in) +g(a)e(v)m(ariet)o(y)h(of)g(con)o(texts.)21 b(The)16 b(main)g(adv)m(an)o(tage) +f(is)i(that)0 1456 y(they)d(free)f(the)h(programmer)f(from)f(man)o(y)i +(concerns)g(ab)q(out)f(ev)m(aluation)i(order.)k(Computationally)14 +b(exp)q(ensiv)o(e)0 1513 y(v)m(alues)j(ma)o(y)f(b)q(e)h(passed)g(as)e +(argumen)o(ts)h(to)g(functions)h(without)f(fear)g(of)g(them)g(b)q(eing)h +(computed)g(if)g(they)f(are)0 1569 y(not)f(needed.)21 b(An)15 +b(imp)q(ortan)o(t)g(example)h(of)f(this)h(is)f(a)g(p)q(ossibly)i +Fo(in\014nite)d Fp(data)g(structure.)0 1714 y Fg(3.4)56 b(\\In\014nite")17 +b(Data)i(Structures)0 1824 y Fp(One)13 b(adv)m(an)o(tage)f(of)g(the)g +(non-strict)h(nature)f(of)g(Hask)o(ell)h(is)g(that)f(data)g(constructors)f +(are)h(non-strict,)h(to)q(o.)18 b(This)0 1880 y(should)h(not)e(b)q(e)i +(surprising,)g(since)g(constructors)e(are)g(really)i(just)e(a)h(sp)q(ecial)h +(kind)g(of)e(function)i(\(the)e(distin-)0 1937 y(guishing)h(feature)e(b)q +(eing)i(that)e(they)g(can)h(b)q(e)g(used)g(in)g(pattern)f(matc)o(hing\).)24 +b(F)l(or)16 b(example,)h(the)g(constructor)0 1993 y(for)e(lists,)g +Fi(\(:\))p Fp(,)g(is)g(non-strict.)71 2073 y(Non-strict)g(constructors)f(p)q +(ermit)h(the)g(de\014nition)i(of)e(\(conceptually\))h Fo(in\014nite)d +Fp(data)i(structures.)k(Here)c(is)0 2130 y(an)g(in\014nite)i(list)f(of)f +(ones:)71 2230 y Fi(ones)476 b(=)24 b(1)g(:)f(ones)0 2339 y +Fp(P)o(erhaps)15 b(more)g(in)o(teresting)h(is)f(the)h(function)g +Fi(numsFrom)o Fp(:)71 2448 y Fi(numsFrom)22 b(n)334 b(=)24 +b(n)g(:)f(numsFrom)g(\(n+1\))0 2557 y Fp(Th)o(us)10 b Fi(numsFrom)23 +b(n)11 b Fp(is)g(the)f(in\014nite)j(list)e(of)f(successiv)o(e)i(in)o(tegers)e +(b)q(eginning)j(with)e Fi(n)p Fp(.)18 b(F)l(rom)10 b(it)g(w)o(e)h(can)f +(construct)0 2614 y(an)15 b(in\014nite)i(list)f(of)f(squares:)p +eop +%%Page: 14 14 +bop 0 -40 a Fp(T-14)1513 b Fj(3)45 b(FUNCTIONS)71 160 y Fi(squares)404 +b(=)24 b(map)f(\(^2\))h(\(numsfrom)e(0\))0 271 y Fp(\(Note)15 +b(the)g(use)g(of)g(a)g(section;)g Fi(^)g Fp(is)h(the)f(in\014x)i(exp)q(onen)o +(tiation)f(op)q(erator.\))71 353 y(Of)c(course,)h(ev)o(en)o(tually)g(w)o(e)f +(exp)q(ect)i(to)d(extract)h(some)g(\014nite)i(p)q(ortion)e(of)g(the)h(list)g +(for)f(actual)g(computation,)0 409 y(and)17 b(there)f(are)h(lots)f(of)g +(prede\014ned)j(functions)e(in)h(Hask)o(ell)f(that)f(do)g(this)i(sort)d(of)h +(thing:)24 b Fi(take)o Fp(,)17 b Fi(takeWhile)n Fp(,)0 466 +y Fi(filter)o Fp(,)g(and)h(others)f(\(see)g(the)g(p)q(ortion)h(of)e(the)i +(Standard)f(Prelude)h(called)h Fi(PreludeList)n Fp(\).)26 b(F)l(or)16 +b(example,)0 522 y Fi(take)f Fp(remo)o(v)o(es)f(the)h(\014rst)g +Fi(n)g Fp(elemen)o(ts)h(from)e(a)h(list:)569 625 y Fi(take)23 +b(5)h(squares)72 b Fn(\))i Fi([0,1,4,9,16])71 753 y Fp(The)14 +b(de\014nition)i(of)d Fi(ones)g Fp(ab)q(o)o(v)o(e)h(is)h(an)e(example)i(of)f +(a)f Fo(cir)n(cular)j(list)p Fp(.)i(In)d(most)e(circumstances)i(this)f(has)g +(an)0 809 y(imp)q(ortan)o(t)h(impact)h(on)g(e\016ciency)l(,)h(since)g(an)f +(implemen)o(tation)h(can)f(b)q(e)h(exp)q(ected)g(to)e(implemen)o(t)i(the)f +(list)g(as)0 866 y(a)f(true)g(circular)h(structure,)f(th)o(us)g(sa)o(ving)g +(space.)71 947 y(F)l(or)10 b(another)h(example)i(of)e(the)g(use)h(of)f +(circularit)o(y)l(,)i(the)f(Fib)q(onacci)h(sequence)f(can)g(b)q(e)g(computed) +g(e\016cien)o(tly)0 1004 y(as)j(the)g(follo)o(wing)h(in\014nite)h(sequence:) +71 1113 y Fi(fib)309 b(=)24 b(1)g(:)g(1)f(:)h([)g(a+b)f(|)h(\(a,b\))f(<-)h +(zip)f(fib)h(\(tail)f(fib\))g(])0 1222 y Fp(where)e Fi(zip)g +Fp(is)g(a)g(Standard)g(Prelude)h(function)g(that)e(returns)h(the)g(pairwise)h +(in)o(terlea)o(ving)g(of)e(its)i(t)o(w)o(o)d(list)0 1278 y(argumen)o(ts:)71 +1387 y Fi(zip)k(\(x:xs\))g(\(y:ys\))166 b(=)24 b(\(x,y\))f(:)h(zip)f(xs)h(ys) +71 1444 y(zip)47 b(xs)119 b(ys)238 b(=)24 b([])0 1553 y Fp(Note)16 +b(ho)o(w)g Fi(fib)o Fp(,)g(an)h(in\014nite)h(list,)f(is)f(de\014ned)i(in)f +(terms)f(of)g(itself,)h(as)e(if)i(it)g(w)o(ere)f(\\c)o(hasing)g(its)g(tail.") +24 b(Indeed,)0 1609 y(w)o(e)15 b(can)g(dra)o(w)g(a)g(picture)h(of)f(this)g +(computation)g(as)g(sho)o(wn)g(in)h(Figure)g(1a.)71 1691 y(F)l(or)e(another)h +(application)i(of)e(in\014nite)i(lists,)e(see)h(Section)g(4.4.)0 +1839 y Fg(3.5)56 b(The)18 b(Error)g(F)-5 b(unction)0 1950 y +Fp(Hask)o(ell)20 b(has)f(a)g(built-in)i(function)f(called)h +Fi(error)d Fp(whose)h(t)o(yp)q(e)g(is)h Fi(String->a)o Fp(.)31 +b(This)20 b(is)f(a)g(somewhat)g(o)q(dd)0 2007 y(function:)k(F)l(rom)15 +b(its)h(t)o(yp)q(e)g(it)h(lo)q(oks)f(as)g(if)h(it)f(is)h(returning)f(a)g(v)m +(alue)h(of)f(a)g(p)q(olymorphic)i(t)o(yp)q(e)e(ab)q(out)g(whic)o(h)h(it)0 +2063 y(kno)o(ws)e(nothing,)g(since)h(it)g(nev)o(er)f(receiv)o(es)h(a)f(v)m +(alue)h(of)f(that)g(t)o(yp)q(e)g(as)g(an)g(argumen)o(t!)71 +2145 y(In)h(fact,)f(there)h Fo(is)g Fp(one)g(v)m(alue)h(\\shared")e(b)o(y)h +(all)h(t)o(yp)q(es:)k Fn(?)p Fp(.)h(Indeed,)c(seman)o(tically)f(that)e(is)h +(exactly)h(what)0 2201 y(v)m(alue)c(is)g(alw)o(a)o(ys)e(returned)h(b)o(y)g +Fi(error)f Fp(\(recall)i(that)e(all)i(errors)e(ha)o(v)o(e)h(v)m(alue)h +Fn(?)p Fp(\).)19 b(Ho)o(w)o(ev)o(er,)11 b(w)o(e)h(can)g(exp)q(ect)h(that)0 +2258 y(a)g(reasonable)g(implemen)o(tation)i(will)g(prin)o(t)e(the)g(string)h +(argumen)o(t)e(to)g Fi(error)h Fp(for)f(diagnostic)i(purp)q(oses.)20 +b(Th)o(us)0 2314 y(this)e(function)g(is)g(useful)g(when)g(w)o(e)f(wish)h(to)f +(terminate)g(a)g(program)g(when)h(something)f(has)g(\\gone)g(wrong.")0 +2371 y(F)l(or)e(example,)g(the)h(actual)f(de\014nition)i(of)e +Fi(head)f Fp(tak)o(en)h(from)f(the)i(Standard)f(Prelude)h(is:)71 +2480 y Fi(head)23 b(\(x:xs\))309 b(=)48 b(x)71 2536 y(head)f([])381 +b(=)48 b(error)23 b("head{PreludeList}:)e(head)j([]")p eop +%%Page: 15 15 +bop 1857 -40 a Fp(T-15)33 869 y @beginspecial 5.156800 @llx +435.382507 @lly 327.294495 @urx 781.187195 @ury 1800 @rwi @setspecial +%%BeginDocument: fib.eps +/FHIODict 30 dict def +FHIODict begin +/bdf{bind def}bind def +/d{setdash}bdf +/h{closepath}bdf +/H{}bdf +/J{setlinecap}bdf +/j{setlinejoin}bdf +/M{setmiterlimit}bdf +/n{newpath}bdf +/N{newpath}bdf +/q{gsave}bdf +/Q{grestore}bdf +/w{setlinewidth}bdf +/u{}bdf +/U{}bdf +/sepdef{ +dup where not +{ +FreeHandSepDict +} +if +3 1 roll exch put +}bdf +/` +{end %. FreeHandDict +/-save0- save def +pop pop pop pop pop +concat +userdict begin +/showpage {} def +0 setgray 0 setlinecap 1 setlinewidth +0 setlinejoin 10 setmiterlimit [] 0 setdash newpath +/languagelevel where {pop languagelevel 1 ne{false setstrokeadjust false setoverprint}if}if +} bdf +/~ +{end +-save0- restore +FreeHandDict begin +}bdf +/FreeHandDict 190 dict def +FreeHandDict begin +/currentpacking where{pop true setpacking}if +/xdf{exch def}bdf +/ndf{1 index where{pop pop pop}{dup xcheck{bind}if def}ifelse}bdf +/min{2 copy gt{exch}if pop}bdf +/max{2 copy lt{exch}if pop}bdf +/isLino statusdict /product get (Lino) anchorsearch{pop pop true}{pop false}ifelse def +/dr{transform .25 sub round .25 add +exch .25 sub round .25 add exch itransform}bdf +/C{dr curveto}bdf +/L{dr lineto}bdf +/m{dr moveto}bdf +/printerRes +gsave +matrix defaultmatrix setmatrix +72 72 dtransform +abs exch abs +max +grestore +def +/maxsteps 256 def +/calcgraysteps { +currentscreen pop exch +printerRes exch div exch +2 copy +sin mul round dup mul +3 1 roll +cos mul round dup mul +add 1 add +dup maxsteps gt {pop maxsteps} if +} bdf +/bottom -0 def +/delta -0 def +/frac -0 def +/left -0 def +/numsteps -0 def +/numsteps1 -0 def +/radius -0 def +/right -0 def +/top -0 def +/xt -0 def +/yt -0 def +/df currentflat def +/tempstr 1 string def +/clipflatness currentflat def +/inverted? +0 currenttransfer exec .5 ge def +/colorexists +systemdict/setcmykcolor known def +/tc1 [0 0 0 1] def +/tc2 [0 0 0 1] def +/fc [0 0 0 1] def +/sc [0 0 0 1] def +/concatprocs{ +/proc2 exch cvlit def/proc1 exch cvlit def +/newproc proc1 length proc2 length add array def +newproc 0 proc1 putinterval newproc proc1 length proc2 putinterval +newproc cvx}bdf +/storerect{/top xdf/right xdf/bottom xdf/left xdf}bdf +/rectpath{newpath left bottom m left top L +right top L right bottom L closepath}bdf +/i{dup 0 eq +{pop df dup} +{dup} ifelse +/clipflatness xdf setflat +}bdf +version cvr 38.0 le +{/setrgbcolor{ +currenttransfer exec 3 1 roll +currenttransfer exec 3 1 roll +currenttransfer exec 3 1 roll +setrgbcolor}bdf}if +/gettint{0 get}bdf +/puttint{0 exch put}bdf +/vms {/vmsv save def} bdf +/vmr {vmsv restore} bdf +/vmrs{vmsv restore /vmsv save def}bdf +/eomode{ +{/filler /eofill load def /clipper /eoclip load def} +{/filler /fill load def /clipper /clip load def} +ifelse +}bdf +/CD{/NF exch def{exch dup/FID ne 1 index/UniqueID ne and{exch NF 3 1 roll put} +{pop pop}ifelse}forall NF}bdf +/MN{1 index length/Len exch def +dup length Len add string dup +Len 4 -1 roll putinterval dup 0 4 -1 roll putinterval}bdf +/RC{4 -1 roll /ourvec xdf 256 string cvs(|______)anchorsearch +{1 index MN cvn/NewN exch def cvn +findfont dup maxlength dict CD dup/FontName NewN put dup +/Encoding ourvec put NewN exch definefont pop}{pop}ifelse}bdf +/RF{dup FontDirectory exch known{pop 3 -1 roll pop}{RC}ifelse}bdf +/FF{dup 256 string cvs(|______)exch MN cvn dup FontDirectory exch known +{exch pop findfont 3 -1 roll pop}{pop dup findfont dup maxlength dict CD dup dup +/Encoding exch /Encoding get 256 array copy 7 -1 roll {3 -1 roll dup 4 -2 roll put}forall put definefont}ifelse}bdf +userdict begin /BDFontDict 20 dict def end +BDFontDict begin +/bu{}def +/bn{}def +/setTxMode{av 70 ge{pop}if pop}def +/gm{m}def +/show{pop}def +/gr{pop}def +/fnt{pop pop pop}def +/fs{pop}def +/fz{pop}def +/lin{pop pop}def +end +/MacVec 256 array def +MacVec 0 /Helvetica findfont +/Encoding get 0 128 getinterval putinterval +MacVec 127 /DEL put MacVec 16#27 /quotesingle put MacVec 16#60 /grave put +/NUL/SOH/STX/ETX/EOT/ENQ/ACK/BEL/BS/HT/LF/VT/FF/CR/SO/SI +/DLE/DC1/DC2/DC3/DC4/NAK/SYN/ETB/CAN/EM/SUB/ESC/FS/GS/RS/US +MacVec 0 32 getinterval astore pop +/Adieresis/Aring/Ccedilla/Eacute/Ntilde/Odieresis/Udieresis/aacute +/agrave/acircumflex/adieresis/atilde/aring/ccedilla/eacute/egrave +/ecircumflex/edieresis/iacute/igrave/icircumflex/idieresis/ntilde/oacute +/ograve/ocircumflex/odieresis/otilde/uacute/ugrave/ucircumflex/udieresis +/dagger/degree/cent/sterling/section/bullet/paragraph/germandbls +/register/copyright/trademark/acute/dieresis/notequal/AE/Oslash +/infinity/plusminus/lessequal/greaterequal/yen/mu/partialdiff/summation +/product/pi/integral/ordfeminine/ordmasculine/Omega/ae/oslash +/questiondown/exclamdown/logicalnot/radical/florin/approxequal/Delta/guillemotleft +/guillemotright/ellipsis/nbspace/Agrave/Atilde/Otilde/OE/oe +/endash/emdash/quotedblleft/quotedblright/quoteleft/quoteright/divide/lozenge +/ydieresis/Ydieresis/fraction/currency/guilsinglleft/guilsinglright/fi/fl +/daggerdbl/periodcentered/quotesinglbase/quotedblbase +/perthousand/Acircumflex/Ecircumflex/Aacute +/Edieresis/Egrave/Iacute/Icircumflex/Idieresis/Igrave/Oacute/Ocircumflex +/apple/Ograve/Uacute/Ucircumflex/Ugrave/dotlessi/circumflex/tilde +/macron/breve/dotaccent/ring/cedilla/hungarumlaut/ogonek/caron +MacVec 128 128 getinterval astore pop +/fps{ +currentflat +exch +dup 0 le{pop 1}if +{ +dup setflat 3 index stopped +{1.3 mul dup 3 index gt{pop setflat pop pop stop}if} +{exit} +ifelse +}loop +pop setflat pop pop +}bdf +/fp{100 currentflat fps}bdf +/clipper{clip}bdf +/W{/clipper load 100 clipflatness fps}bdf +/fixtrans1 { +dup{ic mul ic sub 1 add}concatprocs exch +dup{im mul im sub 1 add}concatprocs exch +dup{iy mul iy sub 1 add}concatprocs exch +{ik mul ik sub 1 add}concatprocs +}bdf +/fixtrans2 { +currentcolortransfer +5 -1 roll exch concatprocs 7 1 roll +4 -1 roll exch concatprocs 6 1 roll +3 -1 roll exch concatprocs 5 1 roll +concatprocs 4 1 roll +setcolortransfer +}bdf +end%. FreeHandDict +end%. FHIODict +FHIODict begin +FreeHandDict begin +5.1568 435.3825 327.2945 781.1872 storerect rectpath clip newpath +/onlyk{false}ndf +/ccmyk{dup 5 -1 roll sub 0 max exch}ndf +/setcmykcolor{1 exch sub ccmyk ccmyk ccmyk pop setrgbcolor}ndf +/setcmykcoloroverprint{4{dup -1 eq{pop 0}if 4 1 roll}repeat setcmykcolor}ndf +/findcmykcustomcolor{5 /packedarray where{pop packedarray}{array astore readonly}ifelse}ndf +/setcustomcolor{exch aload pop pop 4{4 index mul 4 1 roll}repeat setcmykcolor pop}ndf +/setseparationgray{1 exch sub dup dup dup setcmykcolor}ndf +/setoverprint{pop}ndf +/currentoverprint false ndf +/colorimage{pop pop +[5 -1 roll/exec cvx 6 -1 roll/exec cvx 7 -1 roll/exec cvx 8 -1 roll/exec cvx +/cmykbufs2gray cvx]cvx image} +version cvr 47.1 le isLino and{userdict begin bdf end}{ndf}ifelse +/cci1 { +currentcolortransfer +{ik mul ik sub 1 add}concatprocs 4 1 roll +{iy mul iy sub 1 add}concatprocs 4 1 roll +{im mul im sub 1 add}concatprocs 4 1 roll +{ic mul ic sub 1 add}concatprocs 4 1 roll +setcolortransfer +}ndf +/cci2 { +{invbuf dup length magentabuf length ne +{dup length dup dup +/magentabuf exch string def +/yellowbuf exch string def +/blackbuf exch string def}if +dup magentabuf copy yellowbuf copy blackbuf copy pop}concatprocs +}ndf +/customcolorimage{colorexists{ +aload pop pop 4 array astore +setimagecmyk +cci1 +/magentabuf 0 string def +/yellowbuf 0 string def +/blackbuf 0 string def +cci2 {magentabuf}{yellowbuf}{blackbuf}true 4 colorimage} +{pop image}ifelse}ndf +/separationimage{image}ndf +/newcmykcustomcolor{6 /packedarray where{pop packedarray}{array astore readonly}ifelse}ndf +/inkoverprint false ndf +/setinkoverprint{pop}ndf +/overprintprocess{pop}ndf +/setspotcolor +{spots exch get 0 5 getinterval exch setcustomcolor}ndf +/currentcolortransfer{currenttransfer dup dup dup}ndf +/setcolortransfer{systemdict begin settransfer end pop pop pop}ndf +/getcmyk { +dup length 4 eq +{aload pop} +{aload pop spots exch get 0 4 getinterval aload pop 4 +{4 index mul 4 1 roll}repeat 5 -1 roll pop} ifelse +}bdf +/setimagecmyk{ +getcmyk/ik xdf /iy xdf /im xdf /ic xdf +}ndf +/autospread{pop}ndf +/fhsetspreadsize{pop}ndf +/strokeopf false def +/fillopf false def +/R{0 ne /strokeopf xdf}bdf +/O{0 ne /fillopf xdf}bdf +/filler{fill}bdf +/F{fc fhsetcolor fillopf setoverprint false autospread +gsave /filler load fp grestore false setoverprint}bdf +/f{closepath F}bdf +/S{sc fhsetcolor strokeopf setoverprint true autospread {stroke}fp false setoverprint}bdf +/s{closepath S}bdf +/B{fc fhsetcolor fillopf setoverprint gsave /filler load fp grestore +sc fhsetcolor strokeopf setoverprint true autospread {stroke}fp false setoverprint}bdf +/b{closepath B}bdf +colorexists not{/setcolorscreen {setscreen pop pop pop pop pop pop pop pop pop}bdf}if +/fhsetcolor{dup length 4 eq +{aload overprintprocess setcmykcolor} +{aload 1 get spots exch get 5 get setinkoverprint setspotcolor} +ifelse +}ndf +/settextcolor{dup fhsetcolor dup length 4 eq +{onlyk{3 get 1.0 eq{true setinkoverprint}if}{pop}ifelse} +{pop} +ifelse +}ndf +/ka{/fc xdf}bdf +/Ka{/sc xdf}bdf +/xa{/fc xdf} bdf +/Xa{/sc xdf} bdf +/bc2[0 0]def +/bc4[0 0 0 0]def +/absmax{2 copy abs exch abs gt{exch}if pop}bdf +/calcstep +{ colorexists not and{calcgraysteps}{maxsteps}ifelse +tc1 length 4 eq +{ +0 1 3 +{tc1 1 index get +tc2 3 -1 roll get +sub +}for +absmax absmax absmax +} +{ +bc2 tc1 1 get 1 exch put +tc1 gettint tc2 gettint +sub abs +} +ifelse +mul abs round dup 0 eq{pop 1}if +dup /numsteps xdf 1 sub dup 0 eq{pop 1}if /numsteps1 xdf +}bdf +/cblend{ +tc1 length 4 eq +{ +0 1 3 +{bc4 exch +tc1 1 index get +tc2 2 index get +1 index sub +frac mul add put +}for bc4 +} +{ +bc2 +tc1 gettint +tc2 gettint +1 index sub +frac mul add +puttint bc2 +} +ifelse +fhsetcolor +}bdf +/logtaper{/frac frac 9 mul 1 add log def}bdf +FHIODict begin +/origmtx matrix currentmatrix def +/iminv false def +/invbuf{0 1 2 index length 1 sub{dup 2 index exch get 255 exch sub 2 index 3 1 roll put}for}bdf +/cyanrp{currentfile cyanbuf readhexstring pop iminv{invbuf}if}def +/magentarp{cyanbuf magentabuf copy}bdf +/yellowrp{cyanbuf yellowbuf copy}bdf +/blackrp{cyanbuf blackbuf copy}bdf +/fixtransfer{ +colorexists +{fixtrans1 fixtrans2} +{{dup 1 exch sub currentgray mul add}concatprocs +currenttransfer exch concatprocs +systemdict begin settransfer end}ifelse +}ndf +/cmykbufs2gray{ +dup length 0 1 3 -1 roll 1 sub +{4 index 1 index get +4 index 2 index get +4 index 3 index get +4 index 4 index get +255 exch sub ccmyk ccmyk ccmyk pop 5 mul exch 45 mul add exch 14 mul add -6 bitshift +2 index 3 1 roll put}for +4 1 roll pop pop pop +}bdf +end +/textopf false def +/curtextmtx{}def +/otw .25 def +/msf{dup/curtextmtx xdf makefont setfont}bdf +/makesetfont/msf load def +/curtextheight{.707104 .707104 curtextmtx dtransform +dup mul exch dup mul add sqrt}bdf +/ta{1 index +{tempstr 0 2 index put tempstr 2 index +gsave exec grestore +tempstr stringwidth rmoveto +5 index eq{6 index 6 index rmoveto}if +3 index 3 index rmoveto +}forall 7{pop}repeat}bdf +/sts{settextcolor textopf setoverprint/ts{awidthshow}def exec false setoverprint}bdf +/stol{setlinewidth settextcolor textopf setoverprint newpath +/ts{{false charpath stroke}ta}def exec false setoverprint}bdf +/currentpacking where{pop false setpacking}if +/spots[1 0 0 0 (Process Cyan) false newcmykcustomcolor +0 1 0 0 (Process Magenta) false newcmykcustomcolor +0 0 1 0 (Process Yellow) false newcmykcustomcolor +0 0 0 1 (Process Black) false newcmykcustomcolor +0 0 0 0 (White) false +newcmykcustomcolor +]def +0 dict dup begin +end +/f0 /Symbol FF def +[] 0 d +3.863708 M +1 w +0 j +0 J +0 O +0 R +0 i +false eomode +[0 0 0 1] Ka +[0 0 0 1] ka +vms +u +vmrs +MacVec 256 array copy +/f1 /|______Helvetica-Bold dup RF findfont def +{ +f1 [18 0 0 18 0 0] makesetfont +9.656845 613.020248 m +0 0 32 0 0 (1) ts +} +[0 0 0 1] +sts +78.6568 699.0202 m +78.6568 745.0202 L +2 J +S +78.6568 745.0202 m +83.6568 745.0202 L +78.6568 757.0202 L +73.6568 745.0202 L +78.6568 745.0202 L +f +n +160.6568 624.0202 m +162.6568 627.0202 L +165.6568 631.0202 L +168.6568 634.0202 L +171.6568 636.0202 L +175.6568 639.0202 L +178.6568 641.0202 L +180.6568 642.0202 L +182.6568 643.0202 L +184.6568 644.0202 L +186.6568 645.0202 L +188.6568 646.0202 L +190.6568 647.0202 L +192.6568 647.0202 L +194.6568 648.0202 L +196.6568 648.0202 L +198.6568 649.0202 L +200.6568 649.0202 L +202.6568 650.0202 L +205.6568 650.0202 L +207.6568 650.0202 L +209.6568 650.0202 L +211.6568 651.0202 L +213.6568 651.0202 L +215.6568 651.0202 L +217.6568 650.0202 L +219.6568 650.0202 L +221.6568 650.0202 L +223.6568 650.0202 L +225.6568 649.0202 L +227.6568 649.0202 L +231.6568 648.0202 L +235.6568 646.0202 L +238.6568 644.0202 L +241.6568 642.0202 L +244.6568 640.0202 L +247.6568 637.0202 L +249.6568 635.0202 L +251.6568 632.0202 L +253.6568 630.0202 L +255.6568 628.0202 L +257.6568 625.0202 L +258.6568 623.0202 L +260.6568 621.0202 L +262.6568 618.0202 L +263.6568 616.0202 L +265.6568 613.0202 L +267.6568 611.0202 L +268.6568 608.0202 L +270.6568 606.0202 L +271.6568 603.0202 L +272.6568 601.0202 L +274.6568 598.0202 L +275.6568 595.0202 L +276.6568 593.0202 L +277.6568 590.0202 L +279.6568 588.0202 L +280.6568 585.0202 L +281.6568 582.0202 L +282.6568 580.0202 L +283.6568 577.0202 L +284.6568 574.0202 L +285.6568 572.0202 L +286.6568 569.0202 L +286.6568 566.0202 L +287.6568 563.0202 L +288.6568 561.0202 L +289.6568 558.0202 L +289.6568 555.0202 L +290.6568 552.0202 L +291.6568 550.0202 L +291.6568 547.0202 L +292.6568 544.0202 L +292.6568 541.0202 L +293.6568 538.0202 L +293.6568 536.0202 L +293.6568 533.0202 L +294.6568 530.0202 L +294.6568 527.0202 L +294.6568 524.0202 L +295.6568 521.0202 L +295.6568 519.0202 L +295.6568 516.0202 L +295.6568 513.0202 L +295.6568 510.0202 L +295.6568 506.0202 L +295.6568 503.0202 L +295.6568 500.0202 L +294.6568 498.0202 L +294.6568 495.0202 L +293.6568 493.0202 L +293.6568 491.0202 L +292.6568 489.0202 L +291.6568 487.0202 L +290.6568 486.0202 L +289.6568 484.0202 L +288.6568 483.0202 L +286.6568 481.0202 L +284.6568 480.0202 L +281.6568 481.0202 L +279.6568 481.0202 L +276.6568 482.0202 L +274.6568 484.0202 L +235.6568 520.0202 L +S +91.6568 694.0202 m +93.6568 696.0202 L +95.6568 698.0202 L +97.6568 700.0202 L +100.6568 702.0202 L +102.6568 703.0202 L +104.6568 705.0202 L +107.6568 706.0202 L +109.6568 708.0202 L +112.6568 709.0202 L +114.6568 710.0202 L +117.6568 711.0202 L +119.6568 713.0202 L +122.6568 714.0202 L +125.6568 715.0202 L +128.6568 715.0202 L +130.6568 716.0202 L +133.6568 717.0202 L +136.6568 718.0202 L +139.6568 718.0202 L +142.6568 719.0202 L +145.6568 719.0202 L +148.6568 720.0202 L +151.6568 720.0202 L +154.6568 720.0202 L +156.6568 720.0202 L +159.6568 720.0202 L +162.6568 720.0202 L +165.6568 720.0202 L +168.6568 720.0202 L +171.6568 720.0202 L +174.6568 720.0202 L +177.6568 720.0202 L +180.6568 720.0202 L +183.6568 719.0202 L +186.6568 719.0202 L +189.6568 719.0202 L +192.6568 718.0202 L +195.6568 718.0202 L +198.6568 717.0202 L +201.6568 716.0202 L +203.6568 716.0202 L +206.6568 715.0202 L +209.6568 714.0202 L +212.6568 714.0202 L +214.6568 713.0202 L +217.6568 712.0202 L +220.6568 711.0202 L +222.6568 710.0202 L +225.6568 709.0202 L +228.6568 707.0202 L +232.6568 706.0202 L +235.6568 704.0202 L +238.6568 703.0202 L +241.6568 701.0202 L +244.6568 699.0202 L +246.6568 697.0202 L +249.6568 695.0202 L +252.6568 693.0202 L +255.6568 691.0202 L +257.6568 689.0202 L +260.6568 687.0202 L +262.6568 684.0202 L +265.6568 682.0202 L +267.6568 679.0202 L +270.6568 677.0202 L +272.6568 674.0202 L +274.6568 672.0202 L +276.6568 669.0202 L +278.6568 666.0202 L +280.6568 663.0202 L +282.6568 660.0202 L +284.6568 657.0202 L +286.6568 655.0202 L +288.6568 652.0202 L +290.6568 649.0202 L +292.6568 646.0202 L +294.6568 642.0202 L +295.6568 639.0202 L +297.6568 636.0202 L +298.6568 633.0202 L +300.6568 630.0202 L +301.6568 626.0202 L +303.6568 623.0202 L +304.6568 620.0202 L +305.6568 617.0202 L +307.6568 613.0202 L +308.6568 610.0202 L +309.6568 607.0202 L +310.6568 603.0202 L +311.6568 600.0202 L +312.6568 597.0202 L +313.6568 593.0202 L +314.6568 590.0202 L +315.6568 587.0202 L +315.6568 583.0202 L +316.6568 580.0202 L +316.6568 578.0202 L +317.6568 575.0202 L +318.6568 573.0202 L +318.6568 570.0202 L +319.6568 567.0202 L +319.6568 564.0202 L +320.6568 562.0202 L +320.6568 559.0202 L +321.6568 556.0202 L +321.6568 553.0202 L +322.6568 550.0202 L +322.6568 547.0202 L +323.6568 544.0202 L +323.6568 541.0202 L +324.6568 538.0202 L +324.6568 534.0202 L +324.6568 531.0202 L +325.6568 528.0202 L +325.6568 525.0202 L +325.6568 522.0202 L +325.6568 519.0202 L +326.6568 515.0202 L +326.6568 512.0202 L +326.6568 509.0202 L +326.6568 506.0202 L +326.6568 503.0202 L +325.6568 500.0202 L +325.6568 497.0202 L +325.6568 494.0202 L +324.6568 491.0202 L +324.6568 488.0202 L +323.6568 486.0202 L +323.6568 483.0202 L +322.6568 480.0202 L +321.6568 477.0202 L +320.6568 475.0202 L +319.6568 472.0202 L +318.6568 470.0202 L +317.6568 468.0202 L +315.6568 466.0202 L +314.6568 464.0202 L +312.6568 462.0202 L +311.6568 460.0202 L +309.6568 458.0202 L +307.6568 456.0202 L +305.6568 455.0202 L +302.6568 453.0202 L +300.6568 452.0202 L +297.6568 451.0202 L +294.6568 449.0202 L +291.6568 448.0202 L +288.6568 447.0202 L +285.6568 446.0202 L +282.6568 445.0202 L +279.6568 444.0202 L +276.6568 443.0202 L +273.6568 442.0202 L +270.6568 441.0202 L +267.6568 440.0202 L +263.6568 439.0202 L +260.6568 439.0202 L +257.6568 438.0202 L +253.6568 438.0202 L +250.6568 437.0202 L +247.6568 437.0202 L +244.6568 437.0202 L +240.6568 436.0202 L +237.6568 436.0202 L +234.6568 436.0202 L +230.6568 436.0202 L +227.6568 436.0202 L +224.6568 436.0202 L +221.6568 436.0202 L +218.6568 437.0202 L +214.6568 437.0202 L +211.6568 437.0202 L +208.6568 438.0202 L +205.6568 439.0202 L +202.6568 439.0202 L +199.6568 440.0202 L +196.6568 441.0202 L +194.6568 442.0202 L +191.6568 443.0202 L +188.6568 444.0202 L +185.6568 445.0202 L +183.6568 447.0202 L +180.6568 448.0202 L +178.6568 450.0202 L +176.6568 451.0202 L +173.6568 453.0202 L +171.6568 455.0202 L +169.6568 456.0202 L +167.6568 458.0202 L +165.6568 461.0202 L +164.6568 463.0202 L +162.6568 465.0202 L +161.6568 467.0202 L +160.6568 469.0202 L +160.6568 471.0202 L +160.6568 473.0202 L +160.6568 476.0202 L +160.6568 478.0202 L +161.6568 481.0202 L +162.6568 483.0202 L +163.6568 486.0202 L +165.6568 488.0202 L +166.6568 491.0202 L +168.6568 494.0202 L +170.6568 496.0202 L +172.6568 499.0202 L +174.6568 502.0202 L +177.6568 505.0202 L +179.6568 507.0202 L +181.6568 510.0202 L +184.6568 513.0202 L +186.6568 515.0202 L +188.6568 518.0202 L +191.6568 520.0202 L +193.6568 523.0202 L +195.6568 525.0202 L +S +u +134.6568 623.0202 m +101.6568 657.0202 L +S +101.6568 657.0202 m +105.6568 661.0202 L +93.6568 666.0202 L +98.6568 653.0202 L +101.6568 657.0202 L +f +n +U +u +202.6568 554.0202 m +171.6568 586.0202 L +S +171.6568 586.0202 m +174.6568 590.0202 L +162.6568 595.0202 L +167.6568 582.0202 L +171.6568 586.0202 L +f +n +U +u +u +60.1568 680.5202 m +60.1568 691.0138 68.6633 699.5202 79.1568 699.5202 C +89.6504 699.5202 98.1568 691.0138 98.1568 680.5202 C +98.1568 670.0267 89.6504 661.5202 79.1568 661.5202 C +68.6633 661.5202 60.1568 670.0267 60.1568 680.5202 C +[0 0 0 0] ka +b +U +vmrs +0 dict dup begin +end +/f2 /Symbol FF def +{ +f2 [36 0 0 36 0 0] makesetfont +73.656845 672.020248 m +0 0 32 0 0 (:) ts +} +[0 0 0 1] +sts +U +u +u +129.1568 610.5202 m +129.1568 621.0138 137.6633 629.5202 148.1568 629.5202 C +158.6504 629.5202 167.1568 621.0138 167.1568 610.5202 C +167.1568 600.0267 158.6504 591.5202 148.1568 591.5202 C +137.6633 591.5202 129.1568 600.0267 129.1568 610.5202 C +[0 0 0 0] ka +2 J +b +U +vmrs +0 dict dup begin +end +/f2 /Symbol FF def +{ +f2 [36 0 0 36 0 0] makesetfont +142.656845 602.020248 m +0 0 32 0 0 (:) ts +} +[0 0 0 1] +sts +U +u +199.1568 541.5202 m +199.1568 552.0138 207.6633 560.5202 218.1568 560.5202 C +228.6504 560.5202 237.1568 552.0138 237.1568 541.5202 C +237.1568 531.0267 228.6504 522.5202 218.1568 522.5202 C +207.6633 522.5202 199.1568 531.0267 199.1568 541.5202 C +[0 0 0 0] ka +2 J +b +U +vmrs +0 dict dup begin +end +/f2 /Symbol FF def +{ +f2 [36 0 0 36 0 0] makesetfont +208.12738 532.080994 m +0 0 32 0 0 (+) ts +} +[0 0 0 1] +sts +u +22.6568 624.0202 m +55.6568 658.0202 L +2 J +S +55.6568 658.0202 m +58.6568 654.0202 L +63.6568 667.0202 L +51.6568 662.0202 L +55.6568 658.0202 L +f +n +U +u +90.6568 551.0202 m +123.6568 585.0202 L +S +123.6568 585.0202 m +126.6568 581.0202 L +131.6568 594.0202 L +119.6568 589.0202 L +123.6568 585.0202 L +f +n +U +u +192.6568 520.0202 m +191.6568 519.0202 L +S +191.6568 519.0202 m +195.6568 515.0202 L +200.6568 528.0202 L +187.6568 523.0202 L +191.6568 519.0202 L +f +n +U +u +237.6568 518.0202 m +240.6568 515.0202 L +S +240.6568 515.0202 m +244.6568 519.0202 L +231.6568 524.0202 L +236.6568 511.0202 L +240.6568 515.0202 L +f +n +U +vmrs +MacVec 256 array copy +/f1 /|______Helvetica-Bold dup RF findfont def +{ +f1 [18 0 0 18 0 0] makesetfont +76.656845 538.020248 m +0 0 32 0 0 (1) ts +} +[0 0 0 1] +sts +vmrs +MacVec 256 array copy +/f1 /|______Helvetica-Bold dup RF findfont def +{ +f1 [18 0 0 18 0 0] makesetfont +126.656845 724.020248 m +0 0 32 0 0 (1,1,2,3,5,...) ts +} +[0 0 0 1] +sts +vmrs +MacVec 256 array copy +/f1 /|______Helvetica-Bold dup RF findfont def +{ +f1 [18 0 0 18 0 0] makesetfont +164.656845 655.020248 m +0 0 32 0 0 (1,2,3,5,8,...) ts +} +[0 0 0 1] +sts +vmrs +MacVec 256 array copy +/f3 /|______Times-BoldItalic dup RF findfont def +{ +f3 [18 0 0 18 0 0] makesetfont +69.656845 763.020248 m +0 0 32 0 0 (fib) ts +} +[0 0 0 1] +sts +U +vmr +end % FreeHandDict +end % FHIODict +%%EndDocument + @endspecial 1134 w @beginspecial 14.258400 @llx 620.398376 +@lly 377.258392 @urx 783.398376 @ury 1800 @rwi @setspecial +%%BeginDocument: io.eps +/FHIODict 30 dict def +FHIODict begin +/bdf{bind def}bind def +/d{setdash}bdf +/h{closepath}bdf +/H{}bdf +/J{setlinecap}bdf +/j{setlinejoin}bdf +/M{setmiterlimit}bdf +/n{newpath}bdf +/N{newpath}bdf +/q{gsave}bdf +/Q{grestore}bdf +/w{setlinewidth}bdf +/u{}bdf +/U{}bdf +/sepdef{ +dup where not +{ +FreeHandSepDict +} +if +3 1 roll exch put +}bdf +/` +{end %. FreeHandDict +/-save0- save def +pop pop pop pop pop +concat +userdict begin +/showpage {} def +0 setgray 0 setlinecap 1 setlinewidth +0 setlinejoin 10 setmiterlimit [] 0 setdash newpath +/languagelevel where {pop languagelevel 1 ne{false setstrokeadjust false setoverprint}if}if +} bdf +/~ +{end +-save0- restore +FreeHandDict begin +}bdf +/FreeHandDict 190 dict def +FreeHandDict begin +/currentpacking where{pop true setpacking}if +/xdf{exch def}bdf +/ndf{1 index where{pop pop pop}{dup xcheck{bind}if def}ifelse}bdf +/min{2 copy gt{exch}if pop}bdf +/max{2 copy lt{exch}if pop}bdf +/isLino statusdict /product get (Lino) anchorsearch{pop pop true}{pop false}ifelse def +/dr{transform .25 sub round .25 add +exch .25 sub round .25 add exch itransform}bdf +/C{dr curveto}bdf +/L{dr lineto}bdf +/m{dr moveto}bdf +/printerRes +gsave +matrix defaultmatrix setmatrix +72 72 dtransform +abs exch abs +max +grestore +def +/maxsteps 256 def +/calcgraysteps { +currentscreen pop exch +printerRes exch div exch +2 copy +sin mul round dup mul +3 1 roll +cos mul round dup mul +add 1 add +dup maxsteps gt {pop maxsteps} if +} bdf +/bottom -0 def +/delta -0 def +/frac -0 def +/left -0 def +/numsteps -0 def +/numsteps1 -0 def +/radius -0 def +/right -0 def +/top -0 def +/xt -0 def +/yt -0 def +/df currentflat def +/tempstr 1 string def +/clipflatness currentflat def +/inverted? +0 currenttransfer exec .5 ge def +/colorexists +systemdict/setcmykcolor known def +/tc1 [0 0 0 1] def +/tc2 [0 0 0 1] def +/fc [0 0 0 1] def +/sc [0 0 0 1] def +/concatprocs{ +/proc2 exch cvlit def/proc1 exch cvlit def +/newproc proc1 length proc2 length add array def +newproc 0 proc1 putinterval newproc proc1 length proc2 putinterval +newproc cvx}bdf +/storerect{/top xdf/right xdf/bottom xdf/left xdf}bdf +/rectpath{newpath left bottom m left top L +right top L right bottom L closepath}bdf +/i{dup 0 eq +{pop df dup} +{dup} ifelse +/clipflatness xdf setflat +}bdf +version cvr 38.0 le +{/setrgbcolor{ +currenttransfer exec 3 1 roll +currenttransfer exec 3 1 roll +currenttransfer exec 3 1 roll +setrgbcolor}bdf}if +/gettint{0 get}bdf +/puttint{0 exch put}bdf +/vms {/vmsv save def} bdf +/vmr {vmsv restore} bdf +/vmrs{vmsv restore /vmsv save def}bdf +/eomode{ +{/filler /eofill load def /clipper /eoclip load def} +{/filler /fill load def /clipper /clip load def} +ifelse +}bdf +/CD{/NF exch def{exch dup/FID ne 1 index/UniqueID ne and{exch NF 3 1 roll put} +{pop pop}ifelse}forall NF}bdf +/MN{1 index length/Len exch def +dup length Len add string dup +Len 4 -1 roll putinterval dup 0 4 -1 roll putinterval}bdf +/RC{4 -1 roll /ourvec xdf 256 string cvs(|______)anchorsearch +{1 index MN cvn/NewN exch def cvn +findfont dup maxlength dict CD dup/FontName NewN put dup +/Encoding ourvec put NewN exch definefont pop}{pop}ifelse}bdf +/RF{dup FontDirectory exch known{pop 3 -1 roll pop}{RC}ifelse}bdf +/FF{dup 256 string cvs(|______)exch MN cvn dup FontDirectory exch known +{exch pop findfont 3 -1 roll pop}{pop dup findfont dup maxlength dict CD dup dup +/Encoding exch /Encoding get 256 array copy 7 -1 roll {3 -1 roll dup 4 -2 roll put}forall put definefont}ifelse}bdf +userdict begin /BDFontDict 20 dict def end +BDFontDict begin +/bu{}def +/bn{}def +/setTxMode{av 70 ge{pop}if pop}def +/gm{m}def +/show{pop}def +/gr{pop}def +/fnt{pop pop pop}def +/fs{pop}def +/fz{pop}def +/lin{pop pop}def +end +/MacVec 256 array def +MacVec 0 /Helvetica findfont +/Encoding get 0 128 getinterval putinterval +MacVec 127 /DEL put MacVec 16#27 /quotesingle put MacVec 16#60 /grave put +/NUL/SOH/STX/ETX/EOT/ENQ/ACK/BEL/BS/HT/LF/VT/FF/CR/SO/SI +/DLE/DC1/DC2/DC3/DC4/NAK/SYN/ETB/CAN/EM/SUB/ESC/FS/GS/RS/US +MacVec 0 32 getinterval astore pop +/Adieresis/Aring/Ccedilla/Eacute/Ntilde/Odieresis/Udieresis/aacute +/agrave/acircumflex/adieresis/atilde/aring/ccedilla/eacute/egrave +/ecircumflex/edieresis/iacute/igrave/icircumflex/idieresis/ntilde/oacute +/ograve/ocircumflex/odieresis/otilde/uacute/ugrave/ucircumflex/udieresis +/dagger/degree/cent/sterling/section/bullet/paragraph/germandbls +/register/copyright/trademark/acute/dieresis/notequal/AE/Oslash +/infinity/plusminus/lessequal/greaterequal/yen/mu/partialdiff/summation +/product/pi/integral/ordfeminine/ordmasculine/Omega/ae/oslash +/questiondown/exclamdown/logicalnot/radical/florin/approxequal/Delta/guillemotleft +/guillemotright/ellipsis/nbspace/Agrave/Atilde/Otilde/OE/oe +/endash/emdash/quotedblleft/quotedblright/quoteleft/quoteright/divide/lozenge +/ydieresis/Ydieresis/fraction/currency/guilsinglleft/guilsinglright/fi/fl +/daggerdbl/periodcentered/quotesinglbase/quotedblbase +/perthousand/Acircumflex/Ecircumflex/Aacute +/Edieresis/Egrave/Iacute/Icircumflex/Idieresis/Igrave/Oacute/Ocircumflex +/apple/Ograve/Uacute/Ucircumflex/Ugrave/dotlessi/circumflex/tilde +/macron/breve/dotaccent/ring/cedilla/hungarumlaut/ogonek/caron +MacVec 128 128 getinterval astore pop +/fps{ +currentflat +exch +dup 0 le{pop 1}if +{ +dup setflat 3 index stopped +{1.3 mul dup 3 index gt{pop setflat pop pop stop}if} +{exit} +ifelse +}loop +pop setflat pop pop +}bdf +/fp{100 currentflat fps}bdf +/clipper{clip}bdf +/W{/clipper load 100 clipflatness fps}bdf +/fixtrans1 { +dup{ic mul ic sub 1 add}concatprocs exch +dup{im mul im sub 1 add}concatprocs exch +dup{iy mul iy sub 1 add}concatprocs exch +{ik mul ik sub 1 add}concatprocs +}bdf +/fixtrans2 { +currentcolortransfer +5 -1 roll exch concatprocs 7 1 roll +4 -1 roll exch concatprocs 6 1 roll +3 -1 roll exch concatprocs 5 1 roll +concatprocs 4 1 roll +setcolortransfer +}bdf +end%. FreeHandDict +end%. FHIODict +FHIODict begin +FreeHandDict begin +14.2584 620.3984 377.2584 783.3984 storerect rectpath clip newpath +/onlyk{false}ndf +/ccmyk{dup 5 -1 roll sub 0 max exch}ndf +/setcmykcolor{1 exch sub ccmyk ccmyk ccmyk pop setrgbcolor}ndf +/setcmykcoloroverprint{4{dup -1 eq{pop 0}if 4 1 roll}repeat setcmykcolor}ndf +/findcmykcustomcolor{5 /packedarray where{pop packedarray}{array astore readonly}ifelse}ndf +/setcustomcolor{exch aload pop pop 4{4 index mul 4 1 roll}repeat setcmykcolor pop}ndf +/setseparationgray{1 exch sub dup dup dup setcmykcolor}ndf +/setoverprint{pop}ndf +/currentoverprint false ndf +/colorimage{pop pop +[5 -1 roll/exec cvx 6 -1 roll/exec cvx 7 -1 roll/exec cvx 8 -1 roll/exec cvx +/cmykbufs2gray cvx]cvx image} +version cvr 47.1 le isLino and{userdict begin bdf end}{ndf}ifelse +/cci1 { +currentcolortransfer +{ik mul ik sub 1 add}concatprocs 4 1 roll +{iy mul iy sub 1 add}concatprocs 4 1 roll +{im mul im sub 1 add}concatprocs 4 1 roll +{ic mul ic sub 1 add}concatprocs 4 1 roll +setcolortransfer +}ndf +/cci2 { +{invbuf dup length magentabuf length ne +{dup length dup dup +/magentabuf exch string def +/yellowbuf exch string def +/blackbuf exch string def}if +dup magentabuf copy yellowbuf copy blackbuf copy pop}concatprocs +}ndf +/customcolorimage{colorexists{ +aload pop pop 4 array astore +setimagecmyk +cci1 +/magentabuf 0 string def +/yellowbuf 0 string def +/blackbuf 0 string def +cci2 {magentabuf}{yellowbuf}{blackbuf}true 4 colorimage} +{pop image}ifelse}ndf +/separationimage{image}ndf +/newcmykcustomcolor{6 /packedarray where{pop packedarray}{array astore readonly}ifelse}ndf +/inkoverprint false ndf +/setinkoverprint{pop}ndf +/overprintprocess{pop}ndf +/setspotcolor +{spots exch get 0 5 getinterval exch setcustomcolor}ndf +/currentcolortransfer{currenttransfer dup dup dup}ndf +/setcolortransfer{systemdict begin settransfer end pop pop pop}ndf +/getcmyk { +dup length 4 eq +{aload pop} +{aload pop spots exch get 0 4 getinterval aload pop 4 +{4 index mul 4 1 roll}repeat 5 -1 roll pop} ifelse +}bdf +/setimagecmyk{ +getcmyk/ik xdf /iy xdf /im xdf /ic xdf +}ndf +/autospread{pop}ndf +/fhsetspreadsize{pop}ndf +/strokeopf false def +/fillopf false def +/R{0 ne /strokeopf xdf}bdf +/O{0 ne /fillopf xdf}bdf +/filler{fill}bdf +/F{fc fhsetcolor fillopf setoverprint false autospread +gsave /filler load fp grestore false setoverprint}bdf +/f{closepath F}bdf +/S{sc fhsetcolor strokeopf setoverprint true autospread {stroke}fp false setoverprint}bdf +/s{closepath S}bdf +/B{fc fhsetcolor fillopf setoverprint gsave /filler load fp grestore +sc fhsetcolor strokeopf setoverprint true autospread {stroke}fp false setoverprint}bdf +/b{closepath B}bdf +colorexists not{/setcolorscreen {setscreen pop pop pop pop pop pop pop pop pop}bdf}if +/fhsetcolor{dup length 4 eq +{aload overprintprocess setcmykcolor} +{aload 1 get spots exch get 5 get setinkoverprint setspotcolor} +ifelse +}ndf +/settextcolor{dup fhsetcolor dup length 4 eq +{onlyk{3 get 1.0 eq{true setinkoverprint}if}{pop}ifelse} +{pop} +ifelse +}ndf +/ka{/fc xdf}bdf +/Ka{/sc xdf}bdf +/xa{/fc xdf} bdf +/Xa{/sc xdf} bdf +/bc2[0 0]def +/bc4[0 0 0 0]def +/absmax{2 copy abs exch abs gt{exch}if pop}bdf +/calcstep +{ colorexists not and{calcgraysteps}{maxsteps}ifelse +tc1 length 4 eq +{ +0 1 3 +{tc1 1 index get +tc2 3 -1 roll get +sub +}for +absmax absmax absmax +} +{ +bc2 tc1 1 get 1 exch put +tc1 gettint tc2 gettint +sub abs +} +ifelse +mul abs round dup 0 eq{pop 1}if +dup /numsteps xdf 1 sub dup 0 eq{pop 1}if /numsteps1 xdf +}bdf +/cblend{ +tc1 length 4 eq +{ +0 1 3 +{bc4 exch +tc1 1 index get +tc2 2 index get +1 index sub +frac mul add put +}for bc4 +} +{ +bc2 +tc1 gettint +tc2 gettint +1 index sub +frac mul add +puttint bc2 +} +ifelse +fhsetcolor +}bdf +/logtaper{/frac frac 9 mul 1 add log def}bdf +FHIODict begin +/origmtx matrix currentmatrix def +/iminv false def +/invbuf{0 1 2 index length 1 sub{dup 2 index exch get 255 exch sub 2 index 3 1 roll put}for}bdf +/cyanrp{currentfile cyanbuf readhexstring pop iminv{invbuf}if}def +/magentarp{cyanbuf magentabuf copy}bdf +/yellowrp{cyanbuf yellowbuf copy}bdf +/blackrp{cyanbuf blackbuf copy}bdf +/fixtransfer{ +colorexists +{fixtrans1 fixtrans2} +{{dup 1 exch sub currentgray mul add}concatprocs +currenttransfer exch concatprocs +systemdict begin settransfer end}ifelse +}ndf +/cmykbufs2gray{ +dup length 0 1 3 -1 roll 1 sub +{4 index 1 index get +4 index 2 index get +4 index 3 index get +4 index 4 index get +255 exch sub ccmyk ccmyk ccmyk pop 5 mul exch 45 mul add exch 14 mul add -6 bitshift +2 index 3 1 roll put}for +4 1 roll pop pop pop +}bdf +end +/textopf false def +/curtextmtx{}def +/otw .25 def +/msf{dup/curtextmtx xdf makefont setfont}bdf +/makesetfont/msf load def +/curtextheight{.707104 .707104 curtextmtx dtransform +dup mul exch dup mul add sqrt}bdf +/ta{1 index +{tempstr 0 2 index put tempstr 2 index +gsave exec grestore +tempstr stringwidth rmoveto +5 index eq{6 index 6 index rmoveto}if +3 index 3 index rmoveto +}forall 7{pop}repeat}bdf +/sts{settextcolor textopf setoverprint/ts{awidthshow}def exec false setoverprint}bdf +/stol{setlinewidth settextcolor textopf setoverprint newpath +/ts{{false charpath stroke}ta}def exec false setoverprint}bdf +/currentpacking where{pop false setpacking}if +/spots[1 0 0 0 (Process Cyan) false newcmykcustomcolor +0 1 0 0 (Process Magenta) false newcmykcustomcolor +0 0 1 0 (Process Yellow) false newcmykcustomcolor +0 0 0 1 (Process Black) false newcmykcustomcolor +0 0 0 0 (White) false +newcmykcustomcolor +]def +0 dict dup begin +end +/f0 /Symbol FF def +[] 0 d +3.863708 M +1 w +0 j +0 J +0 O +0 R +0 i +false eomode +[0 0 0 1] Ka +[0 0 0 1] ka +vms +u +u +u +u +u +14.7584 767.5217 m +119.7584 767.5217 L +119.7584 710.5217 L +14.7584 710.5217 L +14.7584 767.5217 L +[0 0 0 0] ka +2 J +b +U +vmrs +MacVec 256 array copy +/f1 /|______Helvetica-Bold dup RF findfont def +{ +f1 [24 0 0 24 0 0] makesetfont +34.258438 731.021698 m +0 0 32 0 0 (client) ts +} +[0 0 0 1] +sts +U +u +271.7584 767.5217 m +376.7584 767.5217 L +376.7584 710.5217 L +271.7584 710.5217 L +271.7584 767.5217 L +[0 0 0 0] ka +2 J +b +U +vmrs +MacVec 256 array copy +/f1 /|______Helvetica-Bold dup RF findfont def +{ +f1 [24 0 0 24 0 0] makesetfont +286.258438 732.021698 m +0 0 32 0 0 (server) ts +} +[0 0 0 1] +sts +u +119.2584 756.0217 m +254.2584 756.0217 L +2 J +S +254.2584 756.0217 m +254.2584 751.0217 L +266.2584 756.0217 L +254.2584 761.0217 L +254.2584 756.0217 L +f +n +U +u +271.2584 721.0217 m +135.2584 721.0217 L +S +135.2584 721.0217 m +135.2584 726.0217 L +123.2584 721.0217 L +135.2584 716.0217 L +135.2584 721.0217 L +f +n +U +u +65.2584 653.0217 m +65.2584 694.0217 L +S +65.2584 694.0217 m +70.2584 694.0217 L +65.2584 706.0217 L +60.2584 694.0217 L +65.2584 694.0217 L +f +n +U +vmrs +MacVec 256 array copy +/f2 /|______Courier-Bold dup RF findfont def +{ +f2 [24 0 0 24 0 0] makesetfont +163.258438 760.021698 m +0 0 32 0 0 (reqs) ts +} +[0 0 0 1] +sts +vmrs +MacVec 256 array copy +/f2 /|______Courier-Bold dup RF findfont def +{ +f2 [24 0 0 24 0 0] makesetfont +158.258438 705.021698 m +0 0 32 0 0 (resps) ts +} +[0 0 0 1] +sts +vmrs +MacVec 256 array copy +/f2 /|______Courier-Bold dup RF findfont def +{ +f2 [24 0 0 24 0 0] makesetfont +38.258438 633.021698 m +0 0 32 0 0 (init) ts +} +[0 0 0 1] +sts +U +U +U +vmr +end % FreeHandDict +end % FHIODict +%%EndDocument + @endspecial 106 967 a(Figure)15 b(1:)20 b(\(a\))14 b(Circular)i(Fib)q +(onacci)h(Sequence)331 b(\(b\))15 b(Serv)o(er/Clien)o(t)h(Sim)o(ulation)0 +1093 y Fq(4)69 b(Case)23 b(Expressions)g(and)h(P)n(attern)f(Matc)n(hing)0 +1215 y Fp(Earlier)c(w)o(e)e(ga)o(v)o(e)g(sev)o(eral)h(examples)h(of)e +(pattern)h(matc)o(hing)g(in)g(de\014ning)i(functions|for)e(example)h +Fi(length)0 1271 y Fp(and)f Fi(fringe)o Fp(.)28 b(In)19 b(this)f(section)g(w) +o(e)g(will)h(lo)q(ok)f(at)g(the)g(pattern-matc)o(hing)f(pro)q(cess)h(in)h(m)o +(uc)o(h)f(greater)f(detail)0 1328 y(\()p Fn(x)p Fp(3.14\).)151 +1311 y Fm(9)71 1404 y Fp(P)o(atterns)e(are)i(not)f(\\\014rst-class;")h(there) +g(is)h(only)f(a)g(\014xed)g(set)g(of)f(di\013eren)o(t)i(kinds)f(of)g +(patterns.)24 b(W)l(e)17 b(ha)o(v)o(e)0 1461 y(already)c(seen)g(sev)o(eral)g +(examples)h(of)e Fo(data)j(c)n(onstructor)e Fp(patterns;)f(b)q(oth)h +Fi(length)f Fp(and)h Fi(fringe)f Fp(de\014ned)i(earlier)0 1517 +y(use)i(suc)o(h)h(patterns,)e(the)h(former)f(on)h(the)g(constructors)f(of)g +(a)h(\\built-in")i(t)o(yp)q(e)e(\(lists\),)g(the)g(latter)f(on)h(a)g(user-)0 +1574 y(de\014ned)e(t)o(yp)q(e)e(\()p Fi(Tree)o Fp(\).)19 b(Indeed,)14 +b(matc)o(hing)e(is)h(p)q(ermitted)g(using)h(the)e(constructors)g(of)g(an)o(y) +g(t)o(yp)q(e,)g(user-de\014ned)0 1630 y(or)19 b(not.)31 b(This)20 +b(includes)h(tuples,)g(strings,)f(n)o(um)o(b)q(ers,)g(c)o(haracters,)f(etc.) +32 b(F)l(or)18 b(example,)j(here's)e(a)g(con)o(triv)o(ed)0 +1686 y(function)d(that)f(matc)o(hes)f(against)h(a)g(tuple)h(of)f(\\constan)o +(ts:")71 1795 y Fi(contrived)22 b(::)i(\([a],)f(Char,)g(\(Int,)g(Float\),)g +(String,)g(Bool\))g(->)h(Bool)71 1852 y(contrived)94 b(\([],)47 +b('b',)g(\(1,)71 b(2.0\),)g("hi",)g(True\))23 b(=)h(False)0 +1964 y Fp(This)16 b(example)g(also)f(demonstrates)f(that)h +Fo(nesting)f Fp(of)g(patterns)h(is)h(p)q(ermitted)g(\(to)e(arbitrary)h +(depth\).)71 2040 y(T)l(ec)o(hnically)21 b(sp)q(eaking,)f Fo(formal)g(p)n(ar) +n(ameters)881 2024 y Fm(10)937 2040 y Fp(are)f(also)f(patterns|it's)h(just)f +(that)g(they)h Fo(never)g(fail)h(to)0 2097 y(match)f(a)g(value)p +Fp(.)28 b(As)18 b(a)f(\\side)h(e\013ect")g(of)f(the)h(successful)h(matc)o(h,) +e(the)h(formal)g(parameter)f(is)h(b)q(ound)h(to)e(the)0 2153 +y(v)m(alue)j(it)e(is)h(b)q(eing)h(matc)o(hed)e(against.)30 +b(F)l(or)18 b(this)g(reason)h(patterns)e(in)j(an)o(y)e(one)g(equation)h(are)f +(not)h(allo)o(w)o(ed)0 2210 y(to)14 b(ha)o(v)o(e)h(more)f(than)h(one)f(o)q +(ccurrence)i(of)f(the)g(same)f(formal)g(parameter)g(\(a)h(prop)q(ert)o(y)f +(called)i Fo(line)n(arity)e Fn(x)q Fp(3.14,)0 2266 y Fn(x)p +Fp(3.2,)p Fn(x)o Fp(4.4.2\).)71 2343 y(P)o(atterns)c(suc)o(h)i(as)e(formal)h +(parameters)g(that)g(nev)o(er)g(fail)h(to)f(matc)o(h)g(are)g(said)h(to)e(b)q +(e)i Fo(irr)n(efutable)p Fp(,)g(in)g(con)o(trast)0 2399 y(to)g +Fo(r)n(efutable)h Fp(patterns)f(suc)o(h)h(as)f(the)h(ones)g(giv)o(en)g(in)h +(the)e Fi(contrived)g Fp(example)h(ab)q(o)o(v)o(e.)19 b(There)13 +b(are)g(three)f(other)p 0 2434 780 2 v 52 2461 a Fl(9)69 2477 +y Fk(P)o(attern)k(matc)o(hing)h(in)f(Hask)o(ell)h(is)f(v)o(ery)g(di\013eren)o +(t)h(from)e(that)g(found)h(in)h(logic)g(programming)g(languages)g(suc)o(h)f +(as)g(Prolog;)0 2522 y(in)e(particular,)h(it)e(can)g(b)q(e)h(view)o(ed)g(as)f +(\\one-w)o(a)o(y")g(matc)o(hing,)h(whereas)g(Prolog)g(allo)o(ws)g(\\t)o(w)o +(o-w)o(a)o(y")f(matc)o(hing)i(\(via)e(uni\014cation\),)0 2568 +y(along)h(with)g(implicit)i(bac)o(ktrac)o(king)f(in)f(its)f(ev)n(aluation)j +(mec)o(hanism.)37 2598 y Fl(10)69 2614 y Fk(The)d(Rep)q(ort)h(calls)g(these)g +Fe(variables)p Fk(.)p eop +%%Page: 16 16 +bop 0 -40 a Fp(T-16)700 b Fj(4)45 b(CASE)15 b(EXPRESSIONS)i(AND)e(P)l(A)l +(TTERN)h(MA)l(TCHING)0 105 y Fp(kinds)k(of)f(irrefutable)i(patterns,)e(t)o(w) +o(o)g(of)g(whic)o(h)h(w)o(e)f(will)i(in)o(tro)q(duce)f(no)o(w)f(\(the)h +(other)f(w)o(e)g(will)i(dela)o(y)f(un)o(til)0 162 y(Section)c(4.4\).)0 +308 y Fc(As-patterns.)45 b Fp(Sometimes)16 b(it)h(is)f(con)o(v)o(enien)o(t)h +(to)e(name)h(a)g(pattern)g(for)f(use)h(on)g(the)h(righ)o(t-hand)f(side.)24 +b(F)l(or)0 365 y(example,)16 b(a)f(function)h(that)e(duplicates)j(the)e +(\014rst)g(elemen)o(t)h(in)g(a)f(list)h(migh)o(t)f(b)q(e)h(written)f(as:)71 +474 y Fi(f)23 b(\(x:xs\))381 b(=)24 b(x:x:xs)0 585 y Fp(\(Recall)17 +b(that)d Fi(:)h Fp(asso)q(ciates)g(to)g(the)g(righ)o(t.\))k(Note)c(that)g +Fi(x:xs)f Fp(app)q(ears)i(b)q(oth)f(as)g(a)g(pattern)g(on)g(the)g(left-hand)0 +642 y(side,)k(and)f(an)f(expression)h(on)g(the)g(righ)o(t-hand)g(side.)28 +b(T)l(o)17 b(impro)o(v)o(e)g(readabilit)o(y)l(,)j(w)o(e)d(migh)o(t)g(prefer)h +(to)f(write)0 698 y Fi(x:xs)e Fp(just)g(once,)g(whic)o(h)h(w)o(e)f(can)g(ac)o +(hiev)o(e)h(using)g(an)f Fo(as-p)n(attern)g Fp(as)g(follo)o(ws:)1353 +682 y Fm(11)71 807 y Fi(f)23 b(s@\(x:xs\))309 b(=)24 b(x:s)0 +919 y Fp(T)l(ec)o(hnically)17 b(sp)q(eaking,)d(as-patterns)g(alw)o(a)o(ys)f +(result)i(in)g(a)f(successful)h(matc)o(h,)f(although)g(the)g(sub-pattern)g +(\(in)0 975 y(this)i(case)f Fi(x:xs)o Fp(\))g(could,)h(of)f(course,)g(fail.)0 +1122 y Fc(Wild-cards.)46 b Fp(Another)12 b(common)e(situation)i(is)g(matc)o +(hing)f(against)g(a)g(v)m(alue)h(w)o(e)f(really)i(care)e(nothing)g(ab)q(out.) +0 1179 y(F)l(or)k(example,)g(the)h(functions)f Fi(head)g Fp(and)g +Fi(tail)g Fp(de\014ned)i(in)f(Section)g(2.1)e(can)h(b)q(e)h(rewritten)f(as:) +71 1288 y Fi(head)23 b(\(_:xs\))309 b(=)24 b(x)71 1344 y(tail)f(\(x:_\))333 +b(=)24 b(xs)0 1453 y Fp(in)14 b(whic)o(h)h(w)o(e)e(ha)o(v)o(e)g(\\adv)o +(ertised")h(the)f(fact)g(that)g(w)o(e)g(don't)g(care)h(what)f(a)g(certain)h +(part)f(of)g(the)h(input)g(is.)20 b(Eac)o(h)0 1510 y(wild-card)15 +b(will)g(indep)q(enden)o(tly)h(matc)o(h)d(an)o(ything,)h(but)f(in)h(con)o +(trast)f(to)f(a)h(formal)g(parameter,)g(eac)o(h)h(will)h(bind)0 +1566 y(nothing;)g(for)g(this)h(reason)e(more)h(than)g(one)g(are)g(allo)o(w)o +(ed)h(in)g(an)f(equation.)0 1713 y Fc(n)p Fi(+)p Fc(k-patterns.)45 +b Fp(There)21 b(is)h(one)f(other)f(kind)i(of)e Fo(r)n(efutable)h +Fp(pattern)f(in)i(Hask)o(ell,)h(called)f(an)f Fo(n)p Fi(+)o +Fo(k-p)n(attern)p Fp(,)0 1769 y(whic)o(h)15 b(is)g(useful)h(when)f(writing)g +(inductiv)o(e)h(de\014nitions)g(o)o(v)o(er)e(in)o(tegers.)20 +b(F)l(or)13 b(example,)j(here's)e(a)g(de\014nition)i(of)0 1826 +y(an)f(in\014x)h(op)q(erator)f Fi(^)g Fp(that)f(raises)i(its)f(\014rst)g +(argumen)o(t)f(to)h(the)g(p)q(o)o(w)o(er)g(indicated)i(b)o(y)e(the)g(second:) +71 1935 y Fi(x)23 b(^)48 b(0)429 b(=)24 b(1)71 1991 y(x)f(^)h(\(n+1\))357 +b(=)24 b(x*\(x^n\))0 2103 y Fp(\(A)15 b(more)g(e\016cien)o(t)h(de\014nition)h +(of)d Fi(^)h Fp(is)h(giv)o(en)g(in)g(the)f(Standard)g(Prelude.\))71 +2185 y(In)e(general,)h(the)f(pattern)g Fi(n+)p Fh(k)h Fp(matc)o(hes)f(an)o(y) +g(in)o(teger)g(v)m(alue)i Fh(v)f Fn(\025)f Fh(k)q Fp(,)g(and)h(binds)g +Fi(n)f Fp(to)g Fh(v)7 b Fn(\000)f Fh(k)q Fp(.)20 b(n)p Fi(+)p +Fp(k-patterns)0 2241 y(ha)o(v)o(e)15 b(the)g(adv)m(an)o(tage)f(of)g(making)i +(de\014nitions)g(suc)o(h)g(as)e(the)h(ab)q(o)o(v)o(e)g(lo)q(ok)g(v)o(ery)g +(similar)h(to)e(the)h(corresp)q(onding)0 2298 y(mathematical)g(de\014nition:) +858 2342 y Fh(x)884 2326 y Fm(0)945 2342 y Fp(=)42 b(1)809 +2399 y Fh(x)835 2382 y Fd(n)p Fm(+1)945 2399 y Fp(=)g Fh(x)10 +b Fn(\003)g Fh(x)1117 2382 y Fd(n)0 2482 y Fp(\(See)15 b Fn(x)q +Fp(3.14)f(for)g(a)h(formal)g(translation)g(of)g(n)p Fi(+)p +Fp(k-patterns)g(in)o(to)g(a)g(more)g(primitiv)o(e)h(form.\))p +0 2525 780 2 v 37 2552 a Fl(11)69 2568 y Fk(Another)e(adv)n(an)o(tage)h(to)e +(doing)i(this)g(is)f(that)f(a)h(naiv)o(e)h(implemen)o(tation)h(migh)o(t)f +(completely)g(reconstruct)g Ff(x:xs)c Fk(rather)j(than)0 2614 +y(re-use)f(the)g(v)n(alue)i(b)q(eing)f(matc)o(hed)g(against.)p +eop +%%Page: 17 17 +bop 0 -40 a Fj(4.1)45 b(P)o(attern-Matc)o(hing)14 b(Seman)o(tics)1187 +b Fp(T-17)0 105 y Fg(4.1)56 b(P)n(attern-Matc)n(hing)19 b(Seman)n(tics)0 +220 y Fp(So)e(far)f(w)o(e)h(ha)o(v)o(e)f(discussed)j(ho)o(w)d(individual)k +(patterns)c(are)h(matc)o(hed,)g(ho)o(w)f(some)h(are)f(refutable,)i(some)e +(are)0 277 y(irrefutable,)i(etc.)26 b(But)17 b(what)g(driv)o(es)h(the)f(o)o +(v)o(erall)g(pro)q(cess?)27 b(In)18 b(what)e(order)h(are)g(the)g(matc)o(hes)g +(attempted?)0 333 y(What)e(if)g(none)h(succeed?)22 b(These)15 +b(are)g(the)g(questions)h(addressed)g(in)g(this)g(section.)71 +418 y(A)c(particular)g(matc)o(h)g(of)g(a)f(pattern)h(to)f(a)h(v)m(alue)i(can) +e(actually)h(yield)g(one)g(of)e(three)h(results:)19 b Fo(failur)n(e)p +Fp(;)13 b Fo(suc)n(c)n(ess)0 474 y Fp(\(returning)h(a)f(binding)i(for)d(eac)o +(h)i(formal)f(parameter)f(in)i(the)g(pattern\);)f(or)f Fo(diver)n(genc)n(e)g +Fp(\(i.e.)h(non)o(termination\).)0 531 y(The)i(matc)o(hing)h(pro)q(cess)f +(itself)h(o)q(ccurs)g(\\top-do)o(wn,)e(left-to-righ)o(t.")20 +b(F)l(ailure)c(of)f(a)g(pattern)g(an)o(ywhere)g(in)h(one)0 +587 y(equation)c(results)g(in)g(failure)h(of)e(the)h(whole)g(equation,)h(and) +e(the)h(next)g(equation)g(is)g(then)g(tried.)19 b(If)12 b(all)h(equations)0 +644 y(fail,)j(the)f(v)m(alue)h(of)f(the)g(function)h(application)h(is)f +Fn(?)p Fp(,)f(and)h(results)f(in)h(a)f(run-time)h(error.)71 +728 y(F)l(or)i(example,)k(if)e Fi([1,2])e Fp(is)i(matc)o(hed)g(against)f +Fi([0,bot])o Fp(,)h(then)g Fi(1)f Fp(fails)i(to)e(matc)o(h)g +Fi(0)o Fp(,)i(so)e(the)g(result)h(is)0 785 y(a)f(failed)j(matc)o(h.)33 +b(But)19 b(if)i Fi([1,2])e Fp(is)h(matc)o(hed)g(against)f Fi([bot,0])o +Fp(,)i(then)f(matc)o(hing)f Fi(1)h Fp(against)f Fi(bot)h Fp(causes)0 +841 y(div)o(ergence)c(\(i.e.)f Fn(?)p Fp(\).)71 926 y(The)d(only)h(other)f(t) +o(wist)g(to)f(this)i(set)f(of)g(rules)h(is)g(that)e(top-lev)o(el)j(patterns)e +(ma)o(y)f(also)i(ha)o(v)o(e)e(a)h(b)q(o)q(olean)i Fo(guar)n(d)p +Fp(,)0 982 y(as)h(in)h(this)f(de\014nition)i(of)e(a)g(function)h(that)f +(forms)f(an)h(abstract)f(v)o(ersion)i(of)f(a)f(n)o(um)o(b)q(er's)h(sign:)71 +1091 y Fi(sign)23 b(x)h(|)47 b(x)24 b(>)48 b(0)190 b(=)72 b(1)238 +1147 y(|)47 b(x)24 b(==)g(0)190 b(=)72 b(0)238 1204 y(|)47 +b(x)24 b(<)48 b(0)190 b(=)48 b(-1)0 1313 y Fp(Note)16 b(that)f(a)h(sequence)i +(of)e(guards)g(ma)o(y)f(b)q(e)i(pro)o(vided)g(for)f(the)g(same)g(pattern;)g +(as)g(with)g(patterns,)g(they)g(are)0 1370 y(ev)m(aluated)g(top-do)o(wn,)f +(and)g(the)g(\014rst)g(that)g(ev)m(aluates)h(to)e Fi(True)h +Fp(results)g(in)h(a)f(successful)i(matc)o(h.)0 1527 y Fg(4.2)56 +b(An)19 b(Example)0 1642 y Fp(The)i(pattern-matc)o(hing)f(rules)i(can)f(ha)o +(v)o(e)f(subtle)h(e\013ects)g(on)f(the)h(meaning)g(of)f(functions.)37 +b(F)l(or)20 b(example,)0 1698 y(consider)c(this)g(de\014nition)h(of)e +Fi(take)o Fp(:)71 1799 y Fi(take)47 b(0)119 b(_)262 b(=)48 +b([])71 1855 y(take)f(_)119 b([])238 b(=)48 b([])71 1912 y(take)23 +b(\(n+1\))g(\(x:xs\))166 b(=)48 b(x)24 b(:)f(take)h(n)f(xs)0 +2023 y Fp(and)15 b(this)h(sligh)o(tly)g(di\013eren)o(t)g(v)o(ersion)f(\(the)g +(\014rst)g(2)g(equations)g(ha)o(v)o(e)g(b)q(een)i(rev)o(ersed\):)71 +2135 y Fi(take1)47 b(_)119 b([])214 b(=)48 b([])71 2191 y(take1)f(0)119 +b(_)238 b(=)48 b([])71 2248 y(take1)23 b(\(n+1\))g(\(x:xs\))142 +b(=)48 b(x)24 b(:)f(take1)h(n)f(xs)0 2357 y Fp(No)o(w)15 b(note)g(the)g +(follo)o(wing:)698 2414 y Fi(take)47 b(0)24 b(bot)111 b Fn(\))87 +b Fi([])698 2470 y(take1)23 b(0)h(bot)111 b Fn(\))87 b(?)698 +2557 y Fi(take)47 b(bot)24 b([])87 b Fn(\))g(?)698 2613 y Fi(take1)23 +b(bot)h([])87 b Fn(\))g Fi([])p eop +%%Page: 18 18 +bop 0 -40 a Fp(T-18)700 b Fj(4)45 b(CASE)15 b(EXPRESSIONS)i(AND)e(P)l(A)l +(TTERN)h(MA)l(TCHING)0 105 y Fp(W)l(e)h(see)h(that)f Fi(take)g +Fp(is)h(\\more)e(de\014ned")j(with)f(resp)q(ect)f(to)g(its)h(second)g +(argumen)o(t,)e(whereas)i Fi(take1)e Fp(is)i(more)0 162 y(de\014ned)h(with)f +(resp)q(ect)g(to)f(its)h(\014rst.)27 b(It)18 b(is)g(di\016cult)h(to)f(sa)o(y) +f(in)h(this)g(case)g(whic)o(h)h(de\014nition)g(is)f(b)q(etter.)28 +b(Just)0 218 y(remem)o(b)q(er)16 b(that)f(in)i(certain)g(applications,)g(it)g +(ma)o(y)e(mak)o(e)g(a)h(di\013erence.)24 b(\(The)15 b(Standard)h(Prelude)i +(includes)0 274 y(a)d(de\014nition)i(corresp)q(onding)f(to)f +Fi(take)o Fp(.\))0 423 y Fg(4.3)56 b(Case)19 b(Expressions)0 +535 y Fp(P)o(attern)d(matc)o(hing)h(pro)o(vides)g(a)g(w)o(a)o(y)e(to)i +(\\dispatc)o(h)g(con)o(trol")f(based)h(on)g(structural)g(prop)q(erties)g(of)g +(a)f(v)m(alue.)0 591 y(Ho)o(w)o(ev)o(er,)21 b(in)h(man)o(y)e(circumstances)i +(w)o(e)f(don't)g(wish)g(to)f(de\014ne)j(a)d Fo(function)h Fp(ev)o(ery)g(time) +g(w)o(e)g(need)h(to)e(do)0 648 y(this,)c(but)g(so)g(far)g(w)o(e)f(ha)o(v)o(e) +h(only)g(sho)o(wn)g(ho)o(w)g(to)f(do)h(pattern)g(matc)o(hing)g(in)h(function) +f(de\014nitions.)25 b(Hask)o(ell's)0 704 y Fo(c)n(ase)16 b(expr)n(ession)e +Fp(pro)o(vides)i(a)f(w)o(a)o(y)g(to)f(solv)o(e)i(this)g(problem.)21 +b(Indeed,)c(the)f(meaning)g(of)f(pattern)g(matc)o(hing)g(in)0 +761 y(function)k(de\014nitions)i(is)e(sp)q(eci\014ed)h(in)g(the)e(Rep)q(ort)h +(in)h(terms)e(of)g(case)g(expressions,)i(whic)o(h)f(are)g(considered)0 +817 y(more)c(primitiv)o(e.)21 b(In)16 b(particular,)f(a)g(function)h +(de\014nition)h(of)e(the)g(form:)787 916 y Fi(f)h Fo(p)850 +923 y Fb(11)917 916 y Fh(:)8 b(:)g(:)22 b Fo(p)1017 923 y Fb(1k)1074 +916 y Fi(=)16 b Fo(e)1135 923 y Fb(1)787 973 y Fh(:)8 b(:)g(:)787 +1029 y Fi(f)16 b Fo(p)850 1036 y Fb(n1)919 1029 y Fh(:)8 b(:)g(:)22 +b Fo(p)1019 1036 y Fb(nk)1077 1029 y Fi(=)16 b Fo(e)1138 1036 +y Fb(n)0 1129 y Fp(where)f(eac)o(h)h Fo(p)257 1136 y Fb(ij)301 +1129 y Fp(is)g(a)e(pattern,)h(is)g(seman)o(tically)i(equiv)m(alen)o(t)g(to:) +288 1232 y Fi(f)23 b(x1)h(x2)g Fh(:)8 b(:)g(:)21 b Fi(xk)j(=)g(case)f(\(x1,) +31 b Fh(:)8 b(:)g(:)e Fi(,)23 b(xk\))h(of)c(\()p Fo(p)1240 +1239 y Fb(11)1283 1232 y Fh(;)k(:)8 b(:)g(:)d(;)24 b Fo(p)1440 +1239 y Fb(1k)1480 1232 y Fi(\))g(->)16 b Fo(e)1613 1239 y Fb(1)1193 +1288 y Fh(:)8 b(:)g(:)1193 1344 y Fi(\()p Fo(p)1240 1351 y +Fb(n1)1285 1344 y Fh(;)24 b(:)8 b(:)g(:)d(;)24 b Fo(p)1442 +1351 y Fb(nk)1484 1344 y Fi(\))g(->)16 b Fo(e)1617 1351 y Fb(n)0 +1446 y Fp(where)f(the)g Fi(xi)f Fp(are)h(new)g(iden)o(ti\014ers.)21 +b(\(F)l(or)14 b(a)g(more)h(general)g(translation)g(that)f(includes)j(guards,) +d(see)h Fn(x)p Fp(4.4.2.\))0 1503 y(F)l(or)g(example,)g(the)h(de\014nition)h +(of)d Fi(take)h Fp(giv)o(en)h(earlier)g(is)f(equiv)m(alen)o(t)i(to:)71 +1612 y Fi(take)23 b(m)h(ys)357 b(=)24 b(case)f(\(m,ys\))g(of)739 +1668 y(\(0,_\))166 b(->)48 b([])739 1725 y(\(_,[]\))142 b(->)48 +b([])739 1781 y(\(n+1,x:xs\))e(->)i(x)23 b(:)h(take)f(n)h(xs)71 +1941 y Fp(A)13 b(p)q(oin)o(t)g(not)g(made)g(earlier)h(is)g(that,)f(for)f(t)o +(yp)q(e)h(correctness,)g(the)h(t)o(yp)q(es)f(of)g(the)g(righ)o(t-hand)g +(sides)h(of)f(a)g(case)0 1997 y(expression)h(or)e(set)h(of)f(equations)h +(comprising)h(a)e(function)i(de\014nition)h(m)o(ust)d(all)i(b)q(e)f(the)g +(same;)g(more)f(precisely)l(,)0 2054 y(they)j(m)o(ust)g(all)h(share)f(a)g +(common)g(principal)i(t)o(yp)q(e.)71 2136 y(The)j(pattern-matc)o(hing)g +(rules)g(for)g(case)g(expressions)h(are)e(the)h(same)g(as)g(w)o(e)g(ha)o(v)o +(e)f(giv)o(en)i(for)e(function)0 2192 y(de\014nitions,)h(so)d(there)h(is)g +(really)h(nothing)f(new)g(to)f(learn)h(here,)h(other)e(than)h(to)f(note)h +(the)f(con)o(v)o(enience)j(that)0 2248 y(case)d(expressions)g(o\013er.)24 +b(Indeed,)19 b(there's)d(one)h(use)g(of)g(a)f(case)h(expression)h(that)e(is)h +(so)f(common)h(that)f(it)h(has)0 2305 y(sp)q(ecial)k(syn)o(tax:)27 +b(the)19 b Fo(c)n(onditional)g(expr)n(ession)p Fp(.)31 b(In)20 +b(Hask)o(ell,)g(conditional)h(expressions)f(ha)o(v)o(e)f(the)g(familiar)0 +2361 y(form:)756 2419 y Fi(if)c Fh(e)840 2426 y Fm(1)875 2419 +y Fi(then)g Fh(e)1007 2426 y Fm(2)1042 2419 y Fi(else)g Fh(e)1174 +2426 y Fm(3)0 2503 y Fp(whic)o(h)h(is)g(really)g(short-hand)f(for:)724 +2557 y Fi(case)f Fh(e)855 2564 y Fm(1)890 2557 y Fi(of)41 b(True)48 +b(->)15 b Fh(e)1207 2564 y Fm(2)979 2613 y Fi(False)24 b(->)15 +b Fh(e)1207 2620 y Fm(3)p eop +%%Page: 19 19 +bop 0 -40 a Fj(4.4)45 b(Lazy)15 b(P)o(atterns)1473 b Fp(T-19)0 +105 y(F)l(rom)15 b(this)g(expansion)i(it)e(should)i(b)q(e)f(clear)g(that)e +Fh(e)900 112 y Fm(1)936 105 y Fp(m)o(ust)h(ha)o(v)o(e)g(t)o(yp)q(e)g +Fi(Bool)o Fp(,)g(and)h Fh(e)1489 112 y Fm(2)1524 105 y Fp(and)g +Fh(e)1634 112 y Fm(3)1669 105 y Fp(m)o(ust)f(ha)o(v)o(e)g(the)0 +162 y(same)e(\(but)g(otherwise)h(arbitrary\))e(t)o(yp)q(e.)19 +b(In)14 b(other)f(w)o(ords,)f Fi(if)p 1104 162 14 2 v 16 w(then)p +1216 162 V 16 w(else)p 1327 162 V 29 w Fp(when)i(view)o(ed)g(as)f(a)g +(function)h(has)0 218 y(t)o(yp)q(e)h Fi(Bool->a->a->a)n Fp(.)0 +365 y Fg(4.4)56 b(Lazy)18 b(P)n(atterns)0 476 y Fp(There)e(is)f(one)h(other)f +(kind)h(of)f(pattern)g(allo)o(w)o(ed)h(in)g(Hask)o(ell.)21 +b(It)16 b(is)g(called)h(a)e Fo(lazy)h(p)n(attern)p Fp(,)e(and)i(has)f(the)h +(form)0 533 y Fi(~)p Fh(pat)p Fp(.)28 b(Lazy)18 b(patterns)f(are)h +Fo(irr)n(efutable)p Fp(:)25 b(matc)o(hing)18 b(a)f(v)m(alue)j +Fh(v)f Fp(against)e Fi(~)p Fh(pat)h Fp(alw)o(a)o(ys)g(succeeds,)h(regardless) +0 589 y(of)c Fh(pat)p Fp(.)20 b(Op)q(erationally)d(sp)q(eaking,)f(if)g(an)f +(iden)o(ti\014er)i(in)f Fh(pat)f Fp(is)h(later)f(\\used")h(on)f(the)g(righ)o +(t-hand-side,)i(it)e(will)0 646 y(b)q(e)g(b)q(ound)h(to)e(that)g(p)q(ortion)h +(of)g(the)f(v)m(alue)i(that)f(w)o(ould)g(result)g(if)g Fh(v)h +Fp(w)o(ere)f(to)f(successfully)j(matc)o(h)d Fh(pat)p Fp(,)h(and)g +Fn(?)0 702 y Fp(otherwise.)71 784 y(Lazy)22 b(patterns)g(are)g(useful)i(in)f +(con)o(texts)f(where)h(in\014nite)h(lists)g(are)e(b)q(eing)h(de\014ned)h +(recursiv)o(ely)l(.)44 b(F)l(or)0 840 y(example,)16 b(in\014nite)h(lists)e +(are)g(an)g(excellen)o(t)i(v)o(ehicle)g(for)e(writing)g Fo(simulation)g +Fp(programs,)f(and)h(in)h(this)g(con)o(text)0 897 y(the)h(in\014nite)j(lists) +e(are)f(often)g(called)i Fo(str)n(e)n(ams)p Fp(.)25 b(Consider)18 +b(the)g(simple)g(case)g(of)f(sim)o(ulating)h(the)g(in)o(teractions)0 +953 y(b)q(et)o(w)o(een)g(a)f(serv)o(er)g(pro)q(cess)h Fi(server)f +Fp(and)g(a)h(clien)o(t)g(pro)q(cess)g Fi(client)o Fp(,)g(where)g +Fi(client)e Fp(sends)i(a)g(sequence)g(of)0 1010 y Fo(r)n(e)n(quests)c +Fp(to)h Fi(server)o Fp(,)g(and)g Fi(server)g Fp(replies)i(to)d(eac)o(h)i +(request)f(with)h(some)f(kind)h(of)f Fo(r)n(esp)n(onse)p Fp(.)k(This)d +(situation)0 1066 y(is)i(sho)o(wn)e(pictorially)j(in)f(Figure)f(1b,)g(just)g +(as)f(w)o(e)h(did)h(with)g(the)f(Fib)q(onacci)h(example.)26 +b(\(Note)17 b(that)f Fi(client)0 1122 y Fp(also)g(tak)o(es)f(an)h(initial)i +(message)d(as)h(argumen)o(t.\))21 b(Using)c(streams)e(to)g(sim)o(ulate)h(the) +h(message)e(sequences,)i(the)0 1179 y(Hask)o(ell)f(co)q(de)g(corresp)q +(onding)g(to)f(this)g(diagram)g(is:)71 1288 y Fi(reqs)500 b(=)24 +b(client)f(init)g(resps)71 1344 y(resps)476 b(=)24 b(server)f(reqs)0 +1453 y Fp(These)16 b(recursiv)o(e)f(equations)h(are)f(a)g(direct)h(lexical)h +(transliteration)e(of)g(the)g(diagram.)71 1535 y(Let)g(us)g(further)g(assume) +g(that)g(the)g(structure)g(of)g(the)g(serv)o(er)g(and)g(clien)o(t)i(lo)q(ok)e +(something)h(lik)o(e)g(this:)71 1644 y Fi(client)23 b(init)g(\(resp:resps\))f +(=)i(init)f(:)h(client)f(\(next)g(resp\))g(resps)71 1700 y(server)142 +b(\(req:reqs\))70 b(=)24 b(process)f(req)g(:)h(server)f(reqs)0 +1809 y Fp(where)18 b(w)o(e)g(assume)g(that)g Fi(next)f Fp(is)i(a)f(function)h +(that,)f(giv)o(en)g(a)g(resp)q(onse)h(from)e(the)i(serv)o(er,)f(determines)h +(the)0 1866 y(next)i(request,)g(and)g Fi(process)f Fp(is)h(a)g(function)g +(that)f(pro)q(cesses)h(a)f(request)h(from)f(the)h(clien)o(t,)i(returning)e +(an)0 1922 y(appropriate)15 b(resp)q(onse.)71 2004 y(Unfortunately)l(,)d +(this)f(program)f(has)h(a)g(serious)h(problem:)18 b(it)12 b(will)h(not)e(pro) +q(duce)h(an)o(y)e(output!)19 b(The)11 b(problem)0 2060 y(is)h(that)e +Fi(client)o Fp(,)i(as)f(used)g(in)h(the)g(recursiv)o(e)g(setting)f(of)g +Fi(reqs)f Fp(and)i Fi(resps)o Fp(,)f(attempts)g(a)f(matc)o(h)h(on)g(the)g +(resp)q(onse)0 2117 y(list)18 b(b)q(efore)g(it)g(has)g(submitted)g(its)g +(\014rst)f(request!)27 b(In)19 b(other)e(w)o(ords,)g(the)h(pattern)f(matc)o +(hing)h(is)g(b)q(eing)h(done)0 2173 y(\\to)q(o)14 b(early)l(.")20 +b(One)c(w)o(a)o(y)f(to)f(\014x)h(this)h(is)g(to)e(rede\014ne)j +Fi(client)d Fp(as)h(follo)o(ws:)71 2282 y Fi(client)23 b(init)g(resps)214 +b(=)24 b(init)f(:)h(client)f(\(next)g(\(head)g(resps\)\))g(\(tail)g(resps\))0 +2391 y Fp(Although)15 b(w)o(ork)m(able,)f(this)g(solution)h(do)q(es)g(not)e +(read)h(as)g(w)o(ell)h(as)f(that)f(giv)o(en)i(earlier.)20 b(A)14 +b(b)q(etter)g(solution)h(is)g(to)0 2448 y(use)h(a)e(lazy)i(pattern:)71 +2557 y Fi(client)23 b(init)g(~\(resp:resps\))f(=)i(init)f(:)h(client)f +(\(next)g(resp\))g(resps)p eop +%%Page: 20 20 +bop 0 -40 a Fp(T-20)700 b Fj(4)45 b(CASE)15 b(EXPRESSIONS)i(AND)e(P)l(A)l +(TTERN)h(MA)l(TCHING)0 105 y Fp(Because)22 b(lazy)h(patterns)e(are)g +(irrefutable,)j(the)e(matc)o(h)f(will)j(immediately)f(succeed,)h(allo)o(wing) +f(the)f(initial)0 162 y(request)17 b(to)f(b)q(e)i(\\submitted,")g(in)f(turn)g +(allo)o(wing)h(the)f(\014rst)g(resp)q(onse)h(to)e(b)q(e)i(generated;)f(the)h +(engine)g(is)f(no)o(w)0 218 y(\\primed,")e(and)h(the)f(recursion)h(tak)o(es)e +(care)h(of)g(the)g(rest.)71 306 y(As)g(an)g(example)h(of)f(this)g(program)f +(in)i(action,)f(if)h(w)o(e)f(de\014ne:)71 415 y Fi(init)476 +b(=)24 b(0)71 472 y(next)f(resp)357 b(=)24 b(resp)71 528 y(process)e(req)310 +b(=)24 b(req+1)0 637 y Fp(then)16 b(w)o(e)e(see)i(that:)485 +704 y Fi(take)24 b(10)f(reqs)73 b Fn(\))h Fi([0,1,2,3,4,5,6,7,8,9])71 +830 y Fp(The)13 b(serv)o(er/clien)o(t)h(example)g(w)o(as)e(delib)q(erately)k +(c)o(hosen)d(to)f(demonstrate)h(a)g(t)o(ypical)h(use)f(of)g(lazy)g(patterns)0 +887 y(and)19 b(streams,)f(and)g(also)h(to)f(aid)h(the)f(user)h(in)o(terested) +g(in)g(using)h(Hask)o(ell's)f Fo(str)n(e)n(am-b)n(ase)n(d)f(I/O)p +Fp(,)g(in)h(whic)o(h)g(a)0 943 y(Hask)o(ell)14 b(program)f(is)g(essen)o +(tially)i(the)f(clien)o(t,)g(with)g(the)g(op)q(erating)f(system)g(acting)h +(as)f(serv)o(er)g(\()p Fn(x)p Fp(7\).)18 b(Although)0 1000 +y(in)d(this)g(tutorial)f(w)o(e)g(will)i(only)f(discuss)g Fo(c)n +(ontinuation-b)n(ase)n(d)g(I/O)f Fp(\(see)g(Section)h(8\),)e(the)i +(stream-based)f(alter-)0 1056 y(nativ)o(e)j(is)h(an)f(excellen)o(t)i(example) +f(of)e(the)h(use)h(of)e(lazy)i(patterns)f(and)g(streams.)24 +b(Indeed,)19 b(the)e(stream-based)0 1113 y(I/O)f(example)g(in)g(the)f(Rep)q +(ort)h(uses)f(lazy)h(patterns.)71 1201 y(As)11 b(another)g(example)i(of)e +(the)h(use)g(of)f(lazy)h(patterns,)g(consider)h(the)e(de\014nition)j(of)d +(Fib)q(onacci)i(giv)o(en)f(earlier:)71 1310 y Fi(fib)309 b(=)24 +b(1)g(:)g(1)f(:)h([)g(a+b)f(|)h(\(a,b\))f(<-)h(zip)f(fib)h(\(tail)f(fib\))g +(])0 1419 y Fp(W)l(e)15 b(migh)o(t)g(try)g(rewriting)h(this)f(using)h(an)f +(as-pattern:)71 1528 y Fi(fib@\(1:tfib\))70 b(=)23 b(1)h(:)g(1)f(:)h([)g(a+b) +f(|)h(\(a,b\))f(<-)h(zip)f(fib)h(tfib)f(])0 1639 y Fp(This)16 +b(v)o(ersion)g(of)f Fi(fib)g Fp(has)h(the)f(\(small)h(adv)m(an)o(tage\))f(of) +g(not)g(using)i Fi(tail)e Fp(on)g(the)h(righ)o(t-hand)g(side,)g(since)h(it)e +(is)0 1696 y(a)o(v)m(ailable)i(in)f(\\destructured")f(form)f(on)i(the)f +(left-hand)h(side)g(as)f Fi(tfib)o Fp(.)71 1784 y([This)f(kind)h(of)f +(equation)g(is)h(called)h(a)e Fo(p)n(attern)h(binding)e Fp(b)q(ecause)i(it)g +(is)f(a)g(top-lev)o(el)h(equation)g(in)g(whic)o(h)g(the)0 1840 +y(en)o(tire)k(left-hand)g(side)g(is)g(a)f(pattern;)h(i.e.)f(b)q(oth)g +Fi(fib)g Fp(and)h Fi(tfib)e Fp(b)q(ecome)i(b)q(ound)g(within)h(the)e(scop)q +(e)h(of)f(the)0 1897 y(declaration.])71 1985 y(No)o(w,)12 b(using)h(the)g +(same)g(reasoning)g(w)o(e)f(did)i(earlier,)g(w)o(e)f(should)h(b)q(e)f(led)h +(to)e(b)q(eliev)o(e)j(that)d(this)i(program)d(will)0 2041 y(not)j(generate)h +(an)o(y)f(output.)19 b(Curiously)l(,)d(ho)o(w)o(ev)o(er,)d(it)i +Fo(do)n(es)p Fp(,)f(and)h(the)f(reason)h(is)g(simple:)21 b(in)15 +b(Hask)o(ell,)g(pattern)0 2098 y(bindings)i(are)e(assumed)g(to)f(ha)o(v)o(e)h +(an)f(implicit)k Fi(~)d Fp(in)h(fron)o(t)e(of)g(them,)h(re\015ecting)h(the)f +(most)f(common)g(b)q(eha)o(vior)0 2154 y(exp)q(ected)h(of)f(pattern)g +(bindings,)i(and)e(a)o(v)o(oiding)h(some)f(anomalous)g(situations)g(whic)o(h) +h(are)f(b)q(ey)o(ond)h(the)f(scop)q(e)0 2211 y(of)f(this)h(tutorial.)19 +b(Th)o(us)14 b(w)o(e)f(see)h(that)e(lazy)i(patterns)f(pla)o(y)h(an)g(imp)q +(ortan)o(t)f(role)g(in)i(Hask)o(ell,)f(if)g(only)g(implicitly)l(.)0 +2380 y Fg(4.5)56 b(Lexical)16 b(Scoping)j(and)g(Nested)f(F)-5 +b(orms)0 2501 y Fp(It)14 b(is)h(often)f(desirable)i(to)e(create)g(a)g(nested) +h(scop)q(e)g(within)g(an)f(expression,)h(for)f(the)g(purp)q(ose)h(of)f +(creating)h(lo)q(cal)0 2557 y(bindings)i(not)f(seen)g(elsewhere|i.e.)23 +b(some)15 b(kind)h(of)g(\\blo)q(c)o(k-structuring")g(form.)k(In)c(Hask)o(ell) +g(there)g(are)f(t)o(w)o(o)0 2614 y(w)o(a)o(ys)f(to)h(ac)o(hiev)o(e)g(this:)p +eop +%%Page: 21 21 +bop 0 -40 a Fj(4.6)45 b(La)o(y)o(out)1614 b Fp(T-21)0 105 y +Fc(Let)23 b(Expressions.)44 b Fp(Hask)o(ell's)19 b Fo(let)h(expr)n(essions)d +Fp(are)i(useful)h(whenev)o(er)g(a)e(nested)i(set)f(of)f(bindings)j(is)f(re-)0 +162 y(quired.)h(As)15 b(a)g(simple)i(example,)e(consider:)71 +271 y Fi(let)23 b(y)71 b(=)24 b(a*b)166 327 y(f)g(x)f(=)h(\(x+y\)/y)71 +383 y(in)f(f)h(c)g(+)f(f)h(d)0 493 y Fp(The)19 b(set)g(of)f(bindings)j +(created)e(b)o(y)g(a)g Fi(let)f Fp(expression)i(is)f Fo(mutual)r(ly)i(r)n(e)n +(cursive)p Fp(,)e(and)g(pattern)f(bindings)j(are)0 549 y(treated)c(as)g(lazy) +i(patterns)e(\(i.e.)27 b(they)18 b(carry)f(an)h(implicit)i +Fi(~)p Fp(\).)27 b(The)18 b(only)g(kind)h(of)e(declarations)h(p)q(ermitted)0 +605 y(are)d Fo(typ)n(e)h(signatur)n(es)p Fp(,)e Fo(function)i(bindings)p +Fp(,)d(and)j Fo(p)n(attern)g(bindings)p Fp(.)0 780 y Fc(Where)j(Clauses.)45 +b Fp(Sometimes)18 b(it)f(is)h(con)o(v)o(enien)o(t)g(to)e(scop)q(e)i(bindings) +h(o)o(v)o(er)d(sev)o(eral)i(guarded)f(equations,)0 836 y(whic)o(h)f(requires) +g(a)f Fo(wher)n(e)h(clause)p Fp(:)71 945 y Fi(f)23 b(x)h(y)48 +b(|)f(y>z)262 b(=)48 b(...)238 1001 y(|)f(y==z)238 b(=)48 b(...)238 +1058 y(|)f(y[a]->Bool)n +Fp(.)f(But)14 b(this)f(w)o(ould)h(imply)h(that)d Fi(==)0 1202 +y Fp(has)i(t)o(yp)q(e)g Fi(a->a->Bool)n Fp(,)g(ev)o(en)g(though)g(w)o(e)g +(just)f(said)i(that)e(w)o(e)h(don't)f(exp)q(ect)i Fi(==)e Fp(to)g(b)q(e)i +(de\014ned)g(for)e(all)i(t)o(yp)q(es.)71 1279 y(F)l(urthermore,)20 +b(as)g(w)o(e)f(ha)o(v)o(e)h(noted)g(earlier,)i(ev)o(en)e(if)h +Fi(==)f Fo(wer)n(e)g Fp(de\014ned)h(on)f(all)h(t)o(yp)q(es,)g(comparing)f(t)o +(w)o(o)0 1335 y(lists)e(for)e(equalit)o(y)i(is)g Fo(very)g(di\013er)n(ent)e +Fp(from)h(comparing)g(t)o(w)o(o)f(in)o(tegers.)25 b(In)18 b(this)g(sense,)f +(w)o(e)g(exp)q(ect)h Fi(==)f Fp(to)f(b)q(e)0 1392 y Fo(overlo)n(ade)n(d)f +Fp(to)g(carry)f(on)i(these)f(v)m(arious)h(tasks.)71 1468 y +Fo(T)m(yp)n(e)h(classes)f Fp(con)o(v)o(enien)o(tly)j(solv)o(e)f(b)q(oth)g(of) +f(these)h(problems)g(b)o(y)g(allo)o(wing)h(us)f(to)f(declare)h(whic)o(h)h(t)o +(yp)q(es)0 1525 y(are)13 b Fo(instanc)n(es)e Fp(of)i(whic)o(h)i(class,)e(and) +h(to)f(pro)o(vide)g(de\014nitions)j(of)c(the)i(o)o(v)o(erloaded)f +Fo(op)n(er)n(ations)g Fp(asso)q(ciated)h(with)0 1581 y(a)h(class.)20 +b(F)l(or)15 b(example,)g(let's)h(de\014ne)g(a)f(t)o(yp)q(e)g(class)h(con)o +(taining)g(an)f(equalit)o(y)h(op)q(erator:)71 1690 y Fi(class)23 +b(Eq)g(a)h(where)118 1747 y(\(==\))429 b(::)24 b(a)g(->)f(a)h(->)g(Bool)0 +1856 y Fp(Here)18 b Fi(Eq)g Fp(is)g(the)g(name)g(of)f(the)h(class)h(b)q(eing) +g(de\014ned,)g(and)f Fi(==)g Fp(is)g(the)g(single)h(op)q(eration)f(in)h(the)f +(class.)28 b(This)0 1912 y(declaration)13 b(ma)o(y)f(b)q(e)h(read)g(\\a)f(t)o +(yp)q(e)g Fi(a)h Fp(is)g(an)f(instance)i(of)e(the)h(class)f +Fi(Eq)h Fp(if)g(there)f(is)h(an)g(\(o)o(v)o(erloaded\))f(op)q(eration)0 +1969 y Fi(==)p Fp(,)i(of)g(the)h(appropriate)g(t)o(yp)q(e,)f(de\014ned)i(on)f +(it.")20 b(\(Note)14 b(that)g Fi(==)g Fp(is)h(only)h(de\014ned)g(on)e(pairs)h +(of)g(ob)s(jects)f(of)g(the)0 2025 y(same)h(t)o(yp)q(e.\))71 +2102 y(The)i(constrain)o(t)f(that)h(a)f(t)o(yp)q(e)i Fi(a)e +Fp(m)o(ust)h(b)q(e)h(an)f(instance)g(of)g(the)g(class)g Fi(Eq)g +Fp(is)h(written)f Fi(Eq)23 b(a)p Fp(.)j(Th)o(us)17 b Fi(Eq)23 +b(a)0 2158 y Fp(is)c(not)g(a)g(t)o(yp)q(e)g(expression,)h(but)f(rather)f(it)i +(expresses)f(a)g(constrain)o(t)f(on)h(a)g(t)o(yp)q(e,)g(and)g(is)h(called)g +(a)f Fo(c)n(ontext)p Fp(.)0 2215 y(Con)o(texts)14 b(are)h(app)q(ended)i(to)e +(the)g(fron)o(t)f(of)h(t)o(yp)q(e)h(expressions.)21 b(F)l(or)14 +b(example,)i(the)f(e\013ect)g(of)g(the)h(ab)q(o)o(v)o(e)f(class)0 +2271 y(declaration)h(is)g(to)e(assign)i(the)f(follo)o(wing)h(t)o(yp)q(e)f(to) +g Fi(==)o Fp(:)71 2380 y Fi(\(==\))476 b(::)24 b(\(Eq)f(a\))h(=>)g(a)f(->)h +(a)g(->)f(Bool)p 0 2480 780 2 v 37 2506 a Fl(13)69 2522 y Fk(The)15 +b(kind)h(of)e(equalit)o(y)j(w)o(e)d(are)h(referring)h(to)f(here)g(is)g(\\v)n +(alue)h(equalit)o(y)m(,")h(and)e(opp)q(osed)i(to)d(the)h(\\p)q(oin)o(ter)h +(equalit)o(y")h(found,)0 2568 y(for)f(example,)h(in)g(Lisp's)g +Ff(eq)o Fk(.)26 b(P)o(oin)o(ter)17 b(equalit)o(y)h(is)e(not)h(referen)o +(tially)h(transparen)o(t,)g(and)e(th)o(us)h(do)q(es)f(not)g(sit)h(w)o(ell)g +(in)f(a)g(purely)0 2614 y(functional)f(language.)p eop +%%Page: 24 24 +bop 0 -40 a Fp(T-24)771 b Fj(5)45 b(TYPE)15 b(CLASSES,)h(O)o(VERLO)o(ADING,)g +(AND)f(\\OOP")0 105 y Fp(This)e(should)g(b)q(e)g(read,)f(\\F)l(or)f(ev)o(ery) +i(t)o(yp)q(e)f Fi(a)g Fp(that)f(is)i(an)f(instance)h(of)f(the)g(class)h +Fi(Eq)o Fp(,)g Fi(==)f Fp(has)g(t)o(yp)q(e)g Fi(a->a->Bool)o +Fp(.")0 162 y(This)i(is)g(the)g(t)o(yp)q(e)f(that)g(w)o(ould)h(b)q(e)g(used)h +(for)e Fi(==)g Fp(in)h(the)g Fi(elem)f Fp(example,)h(and)g(indeed)h(the)f +(constrain)o(t)f(imp)q(osed)0 218 y(b)o(y)i(the)g(con)o(text)g(propagates)f +(to)h(the)g(principal)j(t)o(yp)q(e)d(for)f Fi(elem)p Fp(:)71 +327 y Fi(elem)476 b(::)24 b(\(Eq)f(a\))h(=>)g(a)f(->)h([a])f(->)h(Bool)0 +436 y Fp(This)11 b(should)g(b)q(e)g(read,)f(\\F)l(or)g(ev)o(ery)g(t)o(yp)q(e) +g Fi(a)g Fp(that)f(is)i(an)f(instance)h(of)f(the)g(class)g +Fi(Eq)p Fp(,)h Fi(elem)e Fp(has)i(t)o(yp)q(e)f Fi(a->[a]->Bool)n +Fp(.")0 493 y(This)18 b(is)f(just)g(what)f(w)o(e)h(w)o(an)o(t|it)g(expresses) +g(the)g(fact)g(that)f Fi(elem)g Fp(is)i(not)e(de\014ned)j(on)e +Fo(al)r(l)f Fp(t)o(yp)q(es,)i(just)e(those)0 549 y(for)f(whic)o(h)h(w)o(e)f +(kno)o(w)f(ho)o(w)h(to)f(compare)h(its)h(elemen)o(ts)g(for)e(equalit)o(y)l(.) +71 625 y(So)f(far)h(so)f(go)q(o)q(d.)19 b(But)14 b(ho)o(w)g(do)f(w)o(e)h(sp)q +(ecify)h(whic)o(h)g(t)o(yp)q(es)f(are)f(instances)i(of)e(the)h(class)g +Fi(Eq)p Fp(,)g(and)g(the)g(actual)0 682 y(b)q(eha)o(vior)i(of)f +Fi(==)f Fp(on)i(eac)o(h)f(of)g(those)f(t)o(yp)q(es?)21 b(This)16 +b(is)g(done)f(with)h(an)f Fo(instanc)n(e)g(de)n(clar)n(ation)p +Fp(.)k(F)l(or)c(example:)71 791 y Fi(instance)22 b(Eq)i(Int)f(where)118 +847 y(x)h(==)g(y)381 b(=)48 b(intEq)23 b(x)h(y)0 956 y Fp(The)15 +b(de\014nition)h(of)e Fi(==)g Fp(is)i(called)g(a)e Fo(metho)n(d)p +Fp(.)20 b Fi(intEq)14 b Fp(happ)q(ens)h(to)f(b)q(e)i(the)e(primitiv)o(e)i +(function)f(that)f(compares)0 1013 y(in)o(tegers)i(for)g(equalit)o(y)l(,)h +(but)f(in)h(general)g(an)o(y)f(v)m(alid)h(expression)g(is)g(allo)o(w)o(ed)g +(on)f(the)g(righ)o(t-hand)h(side,)f(just)g(as)0 1069 y(for)e(an)o(y)f(other)h +(function)h(de\014nition.)21 b(The)15 b(o)o(v)o(erall)f(declaration)h(is)g +(essen)o(tially)g(sa)o(ying:)k(\\The)c(t)o(yp)q(e)f Fi(Int)f +Fp(is)i(an)0 1126 y(instance)j(of)f(the)h(class)g Fi(Eq)p Fp(,)f(and)h(here)g +(is)g(the)g(de\014nition)h(of)e(the)h(metho)q(d)g(corresp)q(onding)g(to)f +(the)h(op)q(eration)0 1182 y Fi(==)p Fp(.")31 b(Giv)o(en)19 +b(this)h(declaration,)g(w)o(e)f(can)g(no)o(w)f(compare)h(\014xed)h(precision) +h(in)o(tegers)e(for)f(equalit)o(y)i(using)g Fi(==)o Fp(.)0 +1239 y(Similarly:)71 1347 y Fi(instance)i(Eq)i(Float)f(where)118 +1404 y(x)h(==)g(y)381 b(=)48 b(floatEq)23 b(x)h(y)0 1513 y +Fp(allo)o(ws)15 b(us)h(to)e(compare)h(\015oating)g(p)q(oin)o(t)h(n)o(um)o(b)q +(ers)f(using)h Fi(==)p Fp(.)71 1589 y(Recursiv)o(e)g(t)o(yp)q(es)f(suc)o(h)h +(as)f Fi(Tree)f Fp(de\014ned)j(earlier)f(can)f(also)g(b)q(e)h(handled:)71 +1698 y Fi(instance)22 b(\(Eq)i(a\))f(=>)h(Eq)g(\(Tree)f(a\))g(where)118 +1755 y(Leaf)h(a)214 b(==)24 b(Leaf)f(b)239 b(=)47 b(a)24 b(==)g(b)118 +1811 y(\(Branch)f(l1)h(r1\))f(==)h(\(Branch)f(l2)g(r2\))48 +b(=)f(\(l1==l2\))23 b(&&)h(\(r1==r2\))118 1868 y(_)334 b(==)24 +b(_)358 b(=)47 b(False)0 1979 y Fp(Note)16 b(the)g(con)o(text)40 +b Fi(Eq)23 b(a)40 b Fp(in)17 b(the)g(\014rst)e(line|this)k(is)e(necessary)g +(b)q(ecause)g(the)f(elemen)o(ts)h(in)g(the)f(lea)o(v)o(es)h(\(of)0 +2036 y(t)o(yp)q(e)e Fi(a)p Fp(\))f(are)h(compared)g(for)f(equalit)o(y)i(in)g +(line)h(2.)i(The)c(additional)i(constrain)o(t)d(is)i(essen)o(tially)g(sa)o +(ying)f(that)f(w)o(e)0 2092 y(can)k(compare)f(trees)g(of)g +Fi(a)p Fp('s)g(for)g(equalit)o(y)i(as)e(long)h(as)f(w)o(e)g(kno)o(w)g(ho)o(w) +g(to)g(compare)g Fi(a)p Fp('s)g(for)g(equalit)o(y)l(.)28 b(If)18 +b(the)0 2149 y(con)o(text)d(w)o(ere)g(omitted,)f(a)h(static)g(t)o(yp)q(e)h +(error)e(w)o(ould)i(result.)71 2225 y(The)h(Hask)o(ell)h(Rep)q(ort,)f(esp)q +(ecially)j(the)d(Standard)g(Prelude,)i(con)o(tains)e(a)g(w)o(ealth)g(of)f +(useful)j(examples)e(of)0 2281 y(t)o(yp)q(e)e(classes.)21 b(Indeed,)16 +b(a)f(class)g Fi(Eq)g Fp(is)h(de\014ned)h(that)d(is)i(sligh)o(tly)g(larger)f +(than)g(the)h(one)f(de\014ned)h(earlier:)71 2392 y Fi(class)47 +b(Eq)23 b(a)48 b(where)118 2448 y(\(==\),)23 b(\(/=\))286 b(::)24 +b(a)g(->)f(a)h(->)g(Bool)118 2504 y(x)g(/=)g(y)381 b(=)48 b(not)23 +b(\(x)h(==)g(y\))0 2614 y Fp(This)15 b(is)g(an)g(example)g(of)g(a)f(class)h +(with)g Fo(two)g Fp(op)q(erations,)g(one)f(for)g(equalit)o(y)l(,)i(the)e +(other)h(for)f(inequalit)o(y)l(.)21 b(It)15 b(also)p eop +%%Page: 25 25 +bop 1857 -40 a Fp(T-25)0 105 y(demonstrates)14 b(the)h(use)g(of)f(a)h +Fo(default)h(metho)n(d)p Fp(,)f(in)h(this)f(case)g(for)f(the)h(inequalit)o(y) +i(op)q(eration)e Fi(/=)o Fp(.)20 b(If)15 b(a)g(metho)q(d)0 +162 y(for)k(a)f(particular)i(op)q(eration)g(is)f(omitted)h(in)g(an)f +(instance)h(declaration,)h(then)e(the)g(default)h(one)g(de\014ned)g(in)0 +218 y(the)e(class)g(declaration,)h(if)g(it)f(exists,)g(is)h(used)f(instead.) +29 b(F)l(or)17 b(example,)i(the)f(three)g(instances)h(of)e +Fi(Eq)h Fp(de\014ned)0 274 y(earlier)d(will)g(w)o(ork)e(p)q(erfectly)i(w)o +(ell)g(with)g(the)f(ab)q(o)o(v)o(e)f(class)h(declaration,)h(yielding)h(just)e +(the)g(righ)o(t)g(de\014nition)h(of)0 331 y(inequalit)o(y)i(that)d(w)o(e)h(w) +o(an)o(t:)k(the)c(logical)i(negation)e(of)g(equalit)o(y)l(.)71 +408 y(Hask)o(ell)j(also)f(supp)q(orts)g(a)g(notion)g(of)g Fo(class)g +(inclusion)p Fp(.)24 b(F)l(or)17 b(example,)h(w)o(e)f(ma)o(y)f(wish)i(to)e +(de\014ne)j(a)e(class)0 464 y Fi(Ord)f Fp(whic)o(h)g Fo(inherits)g +Fp(all)h(of)e(the)h(op)q(erations)g(in)h(Eq,)f(but)g(in)h(addition)g(has)f(a) +f(set)h(of)g(comparison)g(op)q(erations)0 520 y(and)f(minim)o(um)i(and)e +(maxim)o(um)g(functions:)71 621 y Fi(class)47 b(\(Eq)23 b(a\))h(=>)f(Ord)h(a) +47 b(where)118 677 y(\(<\),)24 b(\(<=\),)f(\(>=\),)g(\(>\))47 +b(::)24 b(a)g(->)f(a)h(->)g(Bool)118 733 y(max,)g(min)333 b(::)24 +b(a)g(->)f(a)h(->)g(a)0 845 y Fp(Note)16 b(the)g(con)o(text)f(in)i(the)f +Fi(class)g Fp(declaration.)23 b(W)l(e)16 b(sa)o(y)f(that)h +Fi(Eq)f Fp(is)i(a)f Fo(sup)n(er)n(class)e Fp(of)i Fi(Ord)f +Fp(\(con)o(v)o(ersely)l(,)h Fi(Ord)0 902 y Fp(is)h(a)f Fo(sub)n(class)f +Fp(of)g Fi(Eq)p Fp(\),)h(and)h(an)o(y)f(instance)h(of)f Fi(Ord)f +Fp(m)o(ust)h(also)g(b)q(e)h(an)g(instance)g(of)f Fi(Eq)o Fp(.)24 +b(\(In)16 b(the)h(next)f(Section)0 958 y(w)o(e)f(giv)o(e)g(a)g(fuller)i +(de\014nition)g(or)d Fi(Ord)h Fp(tak)o(en)g(from)f(the)i(Standard)f +(Prelude.\))71 1035 y(One)24 b(b)q(ene\014t)h(of)e(suc)o(h)h(class)g +(inclusions)i(is)e(shorter)f(con)o(texts:)36 b(A)24 b(t)o(yp)q(e)f +(expression)i(for)e(a)g(function)0 1091 y(that)17 b(uses)g(op)q(erations)h +(from)e(b)q(oth)h(the)h Fi(Eq)f Fp(and)g Fi(Ord)g Fp(classes)h(can)f(use)h +(the)f(con)o(text)g Fi(\(Ord)23 b(a\))p Fp(,)17 b(rather)g(than)0 +1147 y Fi(\(Eq)23 b(a,)h(Ord)f(a\))p Fp(,)18 b(since)i Fi(Ord)d +Fp(\\implies")j Fi(Eq)o Fp(.)28 b(More)18 b(imp)q(ortan)o(tly)l(,)g(metho)q +(ds)g(for)g(sub)q(class)h(op)q(erations)f(can)0 1204 y(assume)13 +b(the)h(existence)g(of)f(metho)q(ds)g(for)g(sup)q(erclass)h(op)q(erations.)20 +b(F)l(or)12 b(example,)i(the)g Fi(Ord)f Fp(declaration)h(in)g(the)0 +1260 y(Standard)h(Prelude)i(con)o(tains)e(this)g(default)h(metho)q(d)g(for)e +Fi(\(<\))p Fp(:)166 1364 y Fi(x)24 b(<)f(y)358 b(=)48 b(x)24 +b(<=)f(y)h(&&)g(x)f(/=)h(y)71 1518 y Fp(As)15 b(an)g(example)h(of)f(the)g +(use)g(of)g Fi(Ord)p Fp(,)g(the)g(principal)i(t)o(yping)f(of)f +Fi(quicksort)f Fp(de\014ned)i(in)g(Section)g(2.5.1)e(is:)71 +1629 y Fi(quicksort)356 b(::)48 b(\(Ord)23 b(a\))h(=>)f([a])h(->)f([a])0 +1738 y Fp(In)g(other)g(w)o(ords,)g Fi(quicksort)e Fp(only)j(op)q(erates)e(on) +h(lists)g(of)f(v)m(alues)i(of)f Fo(or)n(der)n(e)n(d)f Fp(t)o(yp)q(es.)42 +b(This)24 b(t)o(yping)f(for)0 1794 y Fi(quicksort)14 b Fp(arises)h(b)q +(ecause)h(of)f(the)h(use)f(of)g(the)g(comparison)g(op)q(erators)g +Fi(<)g Fp(and)g Fi(>=)g Fp(in)h(its)f(de\014nition.)71 1871 +y(Hask)o(ell)21 b(also)g(p)q(ermits)g Fo(multiple)h(inheritanc)n(e)p +Fp(,)f(since)h(classes)f(ma)o(y)f(ha)o(v)o(e)g(more)h(than)f(one)h(sup)q +(erclass.)0 1927 y(Name)16 b(con\015icts)g(are)g(a)o(v)o(oided)g(b)o(y)f(ha)o +(ving)h(the)g(constrain)o(t)f(that)g(a)h(particular)g(op)q(eration)g(can)g(b) +q(e)g(a)g(mem)o(b)q(er)0 1984 y(of)f(at)f(most)h(one)g(class)h(in)g(an)o(y)e +(giv)o(en)i(scop)q(e.)71 2060 y(Con)o(texts)e(are)h(also)g(allo)o(w)o(ed)g +(in)h Fi(data)f Fp(declarations;)h(see)f Fn(x)p Fp(4.2.1.)0 +2198 y Fc(A)k(Di\013eren)o(t)f(P)o(ersp)q(ectiv)o(e.)44 b Fp(Before)17 +b(going)g(on)f(to)g(further)h(examples)g(of)f(the)h(use)g(of)g(t)o(yp)q(e)f +(classes,)i(it)e(is)0 2255 y(w)o(orth)c(p)q(oin)o(ting)i(out)f(t)o(w)o(o)e +(other)i(viewp)q(oin)o(ts)h(of)f(Hask)o(ell's)g(t)o(yp)q(e)g(classes.)20 +b(The)13 b(\014rst)g(is)g(b)o(y)g(analogy)g(to)f(ob)s(ject-)0 +2311 y(orien)o(ted)j(programming)f(\(OOP\).)h(In)g(the)g(follo)o(wing)g +(general)h(statemen)o(t)d(ab)q(out)i(OOP)l(,)g(simply)h(substituting)0 +2368 y Fo(typ)n(e)g(class)e Fp(for)h(class,)g(and)g Fo(typ)n(e)h +Fp(for)e(ob)s(ject,)g(yields)j(a)e(v)m(alid)i(summary)e(of)f(Hask)o(ell's)i +(t)o(yp)q(e)f(class)h(mec)o(hanism:)71 2444 y(\\)p Fo(Classes)c +Fp(capture)j(common)g(sets)f(of)h Fo(op)n(er)n(ations)p Fp(.)k(A)c +(particular)g Fo(obje)n(ct)g Fp(ma)o(y)f(b)q(e)i(an)f Fo(instanc)n(e)e +Fp(of)h(a)h(class,)0 2501 y(and)i(will)i(ha)o(v)o(e)d(a)h Fo(metho)n(d)g +Fp(corresp)q(onding)h(to)e(eac)o(h)h(op)q(eration.)25 b(Classes)17 +b(ma)o(y)f(b)q(e)h(arranged)g(hierarc)o(hically)l(,)0 2557 +y(forming)e(notions)g(of)g Fo(sup)n(er)n(classes)e Fp(and)j +Fo(sub)n(classes)p Fp(,)d(and)i(p)q(ermitting)h Fo(inheritanc)n(e)e +Fp(of)h(op)q(erations/metho)q(ds.)0 2614 y(A)g Fo(default)i(metho)n(d)f +Fp(ma)o(y)e(also)h(b)q(e)h(asso)q(ciated)g(with)f(an)g(op)q(eration.")p +eop +%%Page: 26 26 +bop 0 -40 a Fp(T-26)771 b Fj(5)45 b(TYPE)15 b(CLASSES,)h(O)o(VERLO)o(ADING,)g +(AND)f(\\OOP")71 105 y Fp(In)h Fo(c)n(ontr)n(ast)e Fp(to)h(OOP)l(,)h(it)g +(should)g(b)q(e)g(clear)g(that)f(t)o(yp)q(es)h(are)f(not)g(ob)s(jects,)f(and) +i(in)g(particular)g(there)g(is)g(no)0 162 y(notion)f(of)g(an)f(ob)s(ject's)g +(or)h(t)o(yp)q(e's)f(in)o(ternal)i(m)o(utable)f(state.)k(An)c(adv)m(an)o +(tage)g(o)o(v)o(er)f(OOP)h(is)h(that)e(metho)q(ds)h(in)0 218 +y(Hask)o(ell)i(are)f(completely)i(t)o(yp)q(e-safe:)k(an)o(y)17 +b(attempt)e(to)h(apply)h(a)f(metho)q(d)h(to)f(a)g(v)m(alue)h(whose)g(t)o(yp)q +(e)f(is)h(not)f(in)0 274 y(the)f(required)h(class)f(will)h(b)q(e)f(detected)h +(at)e(compile)i(time)f(instead)g(of)f(at)g(run)o(time.)21 b(In)15 +b(other)f(w)o(ords,)g(metho)q(ds)0 331 y(are)h(not)g(\\lo)q(ok)o(ed)g(up")g +(at)g(run)o(time)h(but)f(are)g(simply)h(passed)g(as)f(higher-order)g +(functions.)71 428 y(A)d(di\013eren)o(t)g(p)q(ersp)q(ectiv)o(e)h(can)f(b)q(e) +h(gotten)e(b)o(y)h(considering)h(the)f(relationship)i(b)q(et)o(w)o(een)e +(parametric)g(and)g(ad)0 485 y(ho)q(c)k(p)q(olymorphism.)21 +b(W)l(e)16 b(ha)o(v)o(e)f(sho)o(wn)g(ho)o(w)f(parametric)i(p)q(olymorphism)g +(is)g(useful)h(in)f(de\014ning)h(families)f(of)0 541 y(t)o(yp)q(es)c(b)o(y)f +(univ)o(ersally)j(quan)o(tifying)e(o)o(v)o(er)f Fo(al)r(l)h +Fp(t)o(yp)q(es.)18 b(Sometimes,)13 b(ho)o(w)o(ev)o(er,)e(that)g(univ)o(ersal) +i(quan)o(ti\014cation)f(is)0 597 y(to)q(o)h(broad|w)o(e)h(wish)h(to)e(quan)o +(tify)h(o)o(v)o(er)f(some)h(smaller)g(set)g(of)f(t)o(yp)q(es,)h(suc)o(h)g(as) +g(those)g(t)o(yp)q(es)f(whose)h(elemen)o(ts)0 654 y(can)j(b)q(e)h(compared)f +(for)f(equalit)o(y)l(.)26 b(T)o(yp)q(e)17 b(classes)h(can)f(b)q(e)h(seen)f +(as)g(pro)o(viding)h(a)e(structured)h(w)o(a)o(y)f(to)g(do)h(just)0 +710 y(this.)j(Indeed,)c(w)o(e)f(can)g(think)g(of)f(parametric)h(p)q +(olymorphism)h(as)e(a)h(kind)h(of)e(o)o(v)o(erloading)h(to)q(o!)k(It's)14 +b(just)h(that)0 767 y(the)k(o)o(v)o(erloading)g(o)q(ccurs)g(implicitly)j(o)o +(v)o(er)c(all)i(t)o(yp)q(es)e(instead)i(of)e(a)h(constrained)g(set)g(of)f(t)o +(yp)q(es)h(\(i.e.)f(a)h(t)o(yp)q(e)0 823 y(class\).)71 920 +y(In)g(the)g(remainder)h(of)e(this)h(section)h(w)o(e)f(in)o(tro)q(duce)g(the) +g(sev)o(eral)g(prede\014ned)i Fo(standar)n(d)e Fp(t)o(yp)q(e)g(classes)g(in)0 +977 y(Hask)o(ell.)0 1176 y Fg(5.1)56 b(Equalit)n(y)17 b(and)i(Ordered)f +(Classes)0 1310 y Fp(Hask)o(ell's)c(standard)e(classes)i(form)e(the)h +(somewhat)f(frigh)o(tening)i(inclusion)i(structure)d(sho)o(wn)f(in)i(Figure)g +(2.)k(A)o(t)0 1367 y(the)e(top)f(of)g(the)g(\014gure,)h(w)o(e)f(see)h +Fi(Eq)f Fp(with)h(its)g(sub)q(class)g Fi(Ord)f Fp(b)q(elo)o(w)h(it.)21 +b(These)16 b(w)o(ere)f(de\014ned)i(in)g(the)e(previous)0 1423 +y(section.)20 b(Con)o(tin)o(uing)c(do)o(wn,)f(w)o(e)g(encoun)o(ter)g(t)o(w)o +(o)f(sub)q(classes)j(of)d Fi(Ord)p Fp(,)h Fi(Enum)f Fp(and)i +Fi(Ix)o Fp(.)0 1623 y Fg(5.2)56 b(En)n(umeration)17 b(and)i(Index)f(Classes)0 +1757 y Fi(Enum)13 b Fp(has)g(a)h(set)f(of)g(op)q(erations)h(that)f(underlie)i +(the)f(syn)o(tactic)f(sugar)g(of)h(arithmetic)g(sequences;)h(for)d(example,)0 +1813 y(the)j(arithmetic)g(sequence)h(expression)f Fi([1,3..])f +Fp(stands)h(for)f Fi(enumFromThen)22 b(1)i(3)14 b Fp(\(see)h +Fn(x)p Fp(3.9)f(for)g(the)g(formal)0 1870 y(translation\).)29 +b(Arithmetic)20 b(sequences)f(are)g(ordered,)g(so)f(naturally)l(,)h +Fi(Enum)f Fp(is)h(a)f(sub)q(class)i(of)e Fi(Ord)p Fp(.)29 b(W)l(e)19 +b(can)0 1926 y(no)o(w)d(see)h(that)f(arithmetic)i(sequence)f(expressions)h +(can)f(b)q(e)g(used)g(to)f(generate)h(lists)g(of)g(an)o(y)f(t)o(yp)q(e)h +(that)f(is)h(an)0 1983 y(instance)f(of)g Fi(Enum)o Fp(.)21 +b(This)16 b(includes)i(not)e(only)g(most)f(n)o(umeric)h(t)o(yp)q(es,)f(but)h +(also)g Fi(Char)o Fp(,)g(so)f(that,)g(for)g(instance,)0 2039 +y Fi(['a'..'z'])g Fp(denotes)h(the)g(list)h(of)f(lo)o(w)o(er-case)f(letters)i +(in)g(alphab)q(etical)h(order.)k(F)l(urthermore,)15 b(user-de\014ned)0 +2096 y(en)o(umerated)g(t)o(yp)q(es)h(lik)o(e)g Fi(Color)e Fp(can)i(easily)g +(b)q(e)g(giv)o(en)f Fi(Enum)g Fp(instance)h(declarations.)21 +b(If)15 b(so:)318 2221 y Fi([Red..Violet])72 b Fn(\))h Fi([Red,)24 +b(Green,)f(Blue,)g(Indigo,)g(Violet])0 2347 y Fp(Note)13 b(that)g(suc)o(h)h +(a)g(sequence)h(is)f Fo(arithmetic)g Fp(in)h(the)f(sense)g(that)f(the)h +(incremen)o(t)g(b)q(et)o(w)o(een)g(v)m(alues)h(is)f(constan)o(t,)0 +2404 y(ev)o(en)i(though)f(the)g(v)m(alues)h(are)f(not)g(n)o(um)o(b)q(ers.)71 +2501 y(The)j(other)h(immediate)g(standard)f(sub)q(class)i(of)e +Fi(Ord)g Fp(is)h Fi(Ix)p Fp(,)g(whic)o(h)g(is)g(the)g(class)g(of)f(t)o(yp)q +(es)h(that)f(can)g(b)q(e)0 2557 y(used)d(as)g(arra)o(y)e(indices.)22 +b(Again,)15 b(it)g(is)g(natural)f(that)g Fi(Ix)h Fp(should)g(b)q(e)h(a)e(sub) +q(class)i(of)e Fi(Ord)p Fp(,)g(since)i(w)o(e)e(exp)q(ect)i(the)0 +2614 y(elemen)o(ts)g(of)f(an)g(arra)o(y)f(to)g(ha)o(v)o(e)h(an)g(index)i +(order.)i(W)l(e)d(deal)g(with)f(class)h Fi(Ix)e Fp(in)j(Section)f(6.9.)p +eop +%%Page: 27 27 +bop 0 -40 a Fj(5.3)45 b(T)l(ext)15 b(and)g(Binary)h(Classes)1267 +b Fp(T-27)793 675 y Fa(#)751 708 y(#)741 717 y(#)943 525 y(#)901 +558 y(#)891 567 y(#)1074 375 y(#)1033 409 y(#)1022 417 y(#)1093 +675 y(#)1051 708 y(#)1041 717 y(#)1187 825 y(#)1145 858 y(#)1134 +867 y(#)816 534 y(S)841 567 y(S)816 234 y(S)847 275 y(S)878 +317 y(S)909 358 y(S)940 400 y(S)953 417 y(S)1022 833 y(S)1047 +867 y(S)1041 534 y(S)1066 567 y(S)1153 684 y(S)1178 717 y(S)909 +684 y(S)934 717 y(S)546 609 y(S)577 650 y(S)608 692 y(S)627 +717 y(S)631 384 y(\023)600 425 y(\023)569 467 y(\023)549 492 +y(\023)728 234 y(\023)703 267 y(\023)703 384 y(S)728 417 y(S)1022 +923 y Fi(RealFloat)591 773 y(Integral)389 b(Floating)-473 b(RealFrac)1022 +623 y(Fractional)516 548 y(Ix)816 623 y(Real)966 473 y(Num)-279 +b(Enum)628 323 y(Ord)759 173 y(Eq)1097 323 y(Text)185 b(Binary)468 +1071 y Fp(Figure)16 b(2:)j(Hask)o(ell's)d(Standard)f(T)o(yp)q(e)g(Class)h +(Hierarc)o(h)o(y)0 1254 y Fg(5.3)56 b(T)-5 b(ext)18 b(and)h(Binary)f(Classes) +0 1438 y Fc(5.3.1)52 b(T)l(ext)17 b(Class)0 1622 y Fp(The)h(instances)h(of)e +(class)h Fi(Text)f Fp(are)h(those)f(t)o(yp)q(es)h(that)f(can)h(b)q(e)g(con)o +(v)o(erted)g(to)f(c)o(haracter)g(strings)h(\(t)o(ypically)0 +1678 y(for)e(I/O\))h(and)g(bac)o(k;)f(th)o(us,)h(this)g(class)g(pro)o(vides)g +(op)q(erations)g(for)f(parsing)h(c)o(haracter)f(strings)g(to)g(obtain)h(the)0 +1735 y(v)m(alues)j(they)f(ma)o(y)f(represen)o(t)h(and)g(for)f(pro)q(ducing)j +(the)e(canonical)h(textual)f(represen)o(tation)g(of)f(a)h(prin)o(table)0 +1791 y(v)m(alue.)i(As)14 b(these)g(primitiv)o(e)i(op)q(erations)e(are)g +(somewhat)g(esoteric,)g(let's)g(b)q(egin)i(with)e(one)h(of)e(the)i +(higher-lev)o(el)0 1848 y(functions)h(that)e(is)i(de\014ned)h(in)f(terms)e +(of)h(them:)71 1948 y Fi(show)476 b(::)24 b(\(Text)f(a\))h(=>)f(a)h(->)g +(String)0 2057 y Fp(Naturally)16 b(enough,)f Fi(show)f Fp(tak)o(es)h(an)o(y)f +(v)m(alue)j(of)d(an)h(appropriate)h(t)o(yp)q(e)f(and)g(returns)g(its)g +(represen)o(tation)h(as)e(a)0 2113 y(c)o(haracter)i(string)g(\(list)h(of)f(c) +o(haracters\),)f(as)h(in)h Fi(show)23 b(\(2+2\))o Fp(,)16 b(whic)o(h)i +(results)e(in)h Fi("4")p Fp(.)23 b(This)17 b(is)g(\014ne)g(as)f(far)f(as)0 +2170 y(it)h(go)q(es,)e(but)i(w)o(e)f(t)o(ypically)i(need)f(to)f(pro)q(duce)h +(more)f(complex)h(strings)f(that)g(ma)o(y)g(ha)o(v)o(e)g(the)g(represen)o +(tations)0 2226 y(of)g(man)o(y)g(v)m(alues)h(in)g(them,)f(as)f(in)71 +2335 y Fi("The)23 b(sum)g(of)h(")g(++)f(show)h(x)f(++)h(")g(and)f(")h(++)f +(show)h(y)f(++)h(")g(is)f(")h(++)g(show)f(\(x+y\))g(++)h(".")0 +2444 y Fp(and)18 b(after)f(a)g(while,)j(all)f(that)e(concatenation)h(gets)f +(to)g(b)q(e)h(a)g(bit)g(ine\016cien)o(t.)29 b(Sp)q(eci\014call)q(y)l(,)21 +b(let's)d(consider)g(a)0 2501 y(function)h(to)f(represen)o(t)g(the)g(binary)h +(trees)f(of)g(Section)h(2.3)e(as)h(a)f(string,)i(with)f(suitable)i(markings)e +(to)g(sho)o(w)0 2557 y(the)d(nesting)h(of)e(subtrees)i(and)f(the)g +(separation)g(of)f(left)i(and)f(righ)o(t)g(branc)o(hes)g(\(pro)o(vided)h(the) +f(elemen)o(t)g(t)o(yp)q(e)g(is)0 2614 y(represen)o(table)h(as)f(a)g +(string\):)p eop +%%Page: 28 28 +bop 0 -40 a Fp(T-28)771 b Fj(5)45 b(TYPE)15 b(CLASSES,)h(O)o(VERLO)o(ADING,)g +(AND)f(\\OOP")71 160 y Fi(showTree)380 b(::)24 b(\(Text)f(a\))h(=>)f(Tree)h +(a)f(->)h(String)71 216 y(showTree)e(\(Leaf)i(x\))166 b(=)48 +b(show)23 b(x)71 272 y(showTree)f(\(Branch)h(l)h(r\))71 b(=)48 +b("<")23 b(++)h(showTree)f(l)g(++)h("|")f(++)h(showTree)f(r)h(++)f(">")0 +382 y Fp(Because)16 b Fi(\(++\))f Fp(has)g(time)h(complexit)o(y)g(linear)h +(in)f(the)f(length)h(of)f(its)h(left)f(argumen)o(t,)g Fi(showTree)f +Fp(is)i(quadratic)0 438 y(in)g(the)f(size)h(of)f(the)g(tree.)71 +531 y(T)l(o)f(restore)h(linear)h(complexit)o(y)l(,)g(the)g(function)g +Fi(shows)e Fp(is)i(pro)o(vided:)71 640 y Fi(shows)452 b(::)24 +b(\(Text)f(a\))h(=>)f(a)h(->)g(String)f(->)g(String)0 749 y(shows)15 +b Fp(tak)o(es)g(a)h(prin)o(table)h(v)m(alue)g(and)f(a)f(string)h(and)g +(returns)g(that)f(string)h(with)g(the)g(v)m(alue's)h(represen)o(tation)0 +805 y(concatenated)f(at)g(the)g(fron)o(t.)22 b(The)16 b(second)h(argumen)o(t) +e(serv)o(es)h(as)g(a)g(sort)f(of)h(string)g Fo(ac)n(cumulator,)h +Fp(and)g Fi(show)0 862 y Fp(can)e(no)o(w)g(b)q(e)h(de\014ned)g(as)f +Fi(shows)g Fp(with)g(the)h(n)o(ull)g(accum)o(ulator:)71 962 +y Fi(show)23 b(x)429 b(=)48 b(shows)23 b(x)h("")0 1071 y Fp(W)l(e)16 +b(can)g(use)g Fi(shows)f Fp(to)g(de\014ne)i(a)e(more)g(e\016cien)o(t)i(v)o +(ersion)f(of)f Fi(showTree)o Fp(,)h(whic)o(h)g(also)g(has)f(a)h(string)g +(accum)o(u-)0 1128 y(lator)f(argumen)o(t:)71 1237 y Fi(showsTree)356 +b(::)24 b(\(Text)f(a\))h(=>)f(Tree)h(a)f(->)h(String)f(->)h(String)71 +1293 y(showsTree)e(\(Leaf)h(x\))h(s)95 b(=)48 b(shows)23 b(x)h(s)71 +1349 y(showsTree)e(\(Branch)h(l)h(r\))f(s=)48 b('<')23 b(:)h(showsTree)f(l)g +(\('|')h(:)f(showsTree)g(r)h(\('>')f(:)h(s\)\))0 1461 y Fp(This)16 +b(solv)o(es)f(our)g(e\016ciency)i(problem)f(\()p Fi(showsTree)e +Fp(has)h(linear)h(complexit)o(y\),)g(but)f(the)g(presen)o(tation)h(of)f(this) +0 1518 y(function)h(\(and)f(others)g(lik)o(e)h(it\))f(can)h(b)q(e)g(impro)o +(v)o(ed.)k(First,)14 b(let's)h(create)g(a)g(t)o(yp)q(e)g(synon)o(ym:)71 +1629 y Fi(type)23 b(ShowS)333 b(=)48 b(String)23 b(->)h(String)0 +1738 y Fp(This)d(is)f(the)g(t)o(yp)q(e)g(of)g(a)f(function)i(that)f(returns)f +(a)h(string)g(represen)o(tation)g(of)g(something)g(follo)o(w)o(ed)g(b)o(y)g +(an)0 1795 y(accum)o(ulator)15 b(string.)21 b(Second,)c(w)o(e)e(can)h(a)o(v)o +(oid)f(carrying)h(accum)o(ulators)f(around,)g(and)h(also)g(a)o(v)o(oid)f +(amassing)0 1851 y(paren)o(theses)g(at)g(the)g(righ)o(t)g(end)h(of)f(long)g +(constructions,)g(b)o(y)g(using)h(functional)h(comp)q(osition:)71 +1960 y Fi(showsTree)356 b(::)24 b(\(Text)f(a\))h(=>)f(Tree)h(a)f(->)h(ShowS) +71 2016 y(showsTree)e(\(Leaf)h(x\))143 b(=)48 b(shows)23 b(x)71 +2073 y(showsTree)f(\(Branch)h(l)h(r\))47 b(=)h(\('<':\))23 +b(.)h(showsTree)e(l)i(.)g(\('|':\))f(.)h(showsTree)e(r)i(.)g(\('>':\))0 +2182 y Fp(Something)16 b(more)g(imp)q(ortan)o(t)g(than)f(just)h(tidying)h(up) +f(the)g(co)q(de)h(has)f(come)g(ab)q(out)f(b)o(y)h(this)h(transformation:)0 +2238 y(W)l(e)f(ha)o(v)o(e)f(raised)h(the)g(presen)o(tation)g(from)e(an)i +Fo(obje)n(ct)g(level)f Fp(\(in)h(this)g(case,)g(strings\))f(to)g(a)g +Fo(function)h(level.)21 b Fp(W)l(e)0 2295 y(can)16 b(think)h(of)e(the)h(t)o +(yping)h(as)e(sa)o(ying)h(that)f Fi(showsTree)g Fp(maps)h(a)f(tree)h(in)o(to) +g(a)g Fo(showing)g(function)p Fp(.)22 b(F)l(unctions)0 2351 +y(lik)o(e)16 b Fi(\('<')23 b(:\))14 b Fp(or)g Fi(\("a)23 b(string")g(++\))14 +b Fp(are)g(primitiv)o(e)i(sho)o(wing)e(functions,)h(and)g(w)o(e)f(build)i(up) +f(more)f(complex)0 2408 y(functions)i(b)o(y)f(comp)q(osition.)71 +2501 y(No)o(w)i(that)g(w)o(e)h(can)g(turn)g(trees)g(in)o(to)g(strings,)g +(let's)g(turn)g(to)g(the)g(in)o(v)o(erse)g(problem.)30 b(The)18 +b(basic)h(idea)f(is)0 2557 y(a)g Fo(p)n(arser)h Fp(for)f(a)g(t)o(yp)q(e)h +Fi(a)p Fp(,)g(whic)o(h)g(is)h(a)e(function)h(that)f(tak)o(es)g(a)h(string)f +(and)h(returns)f(a)h(list)g(of)g Fi(\(a,)k(String\))0 2614 +y Fp(pairs[7].)c(The)d(Standard)f(Prelude)h(pro)o(vides)g(a)f(t)o(yp)q(e)g +(synon)o(ym)g(for)f(suc)o(h)i(functions:)p eop +%%Page: 29 29 +bop 0 -40 a Fj(5.3)45 b(T)l(ext)15 b(and)g(Binary)h(Classes)1267 +b Fp(T-29)71 160 y Fi(type)23 b(ReadS)g(a)286 b(=)48 b(String)23 +b(->)h([\(a,String\)])0 269 y Fp(Normally)l(,)13 b(a)f(parser)f(returns)h(a)g +(singleton)h(list,)g(con)o(taining)f(a)g(v)m(alue)h(of)f(t)o(yp)q(e)g +Fi(a)f Fp(that)h(w)o(as)f(read)h(from)f(the)h(input)0 325 y(string)g(and)g +(the)g(remaining)i(string)e(that)f(follo)o(ws)h(what)g(w)o(as)f(parsed.)19 +b(If)12 b(no)g(parse)g(w)o(as)f(p)q(ossible,)j(ho)o(w)o(ev)o(er,)e(the)0 +382 y(result)k(is)h(the)f(empt)o(y)f(list,)i(and)f(if)g(there)g(is)g(more)g +(than)g(one)g(p)q(ossible)h(parse)f(\(an)f(am)o(biguit)o(y\),)h(the)g +(resulting)0 438 y(list)e(con)o(tains)g(more)f(than)g(one)h(pair.)20 +b(The)14 b(standard)f(function)h Fi(reads)f Fp(is)h(a)f(parser)g(for)g(an)o +(y)g(instance)i(of)e Fi(Text)o Fp(:)71 547 y Fi(reads)452 b(::)24 +b(\(Text)f(a\))h(=>)f(ReadS)g(a)0 656 y Fp(W)l(e)16 b(can)h(use)g(this)f +(function)i(to)d(de\014ne)j(a)e(parsing)g(function)i(for)d(the)i(string)f +(represen)o(tation)g(of)g(binary)h(trees)0 713 y(pro)q(duced)k(b)o(y)f +Fi(showsTree)n Fp(.)34 b(List)21 b(comprehensions)g(giv)o(e)f(us)g(a)f(con)o +(v)o(enien)o(t)h(idiom)h(for)f(constructing)g(suc)o(h)0 769 +y(parsers:)71 878 y Fi(readsTree)356 b(::)24 b(\(Text)f(a\))h(=>)f(ReadS)g +(\(Tree)h(a\))71 934 y(readsTree)e(\('<':s\))166 b(=)48 b([\(Branch)23 +b(l)g(r,)h(u\))g(|)f(\(l,)h('|':t\))f(<-)g(readsTree)g(s,)1168 +991 y(\(r,)h('>':u\))f(<-)g(readsTree)g(t)h(])71 1047 y(readsTree)e(s)310 +b(=)48 b([\(Leaf)23 b(x,)h(t\))119 b(|)23 b(\(x,t\))143 b(<-)23 +b(reads)h(s])0 1156 y Fp(Let's)19 b(tak)o(e)f(a)g(momen)o(t)g(to)h(examine)g +(this)h(function)f(de\014nition)i(in)f(detail.)31 b(There)20 +b(are)e(t)o(w)o(o)g(main)h(cases)g(to)0 1213 y(consider:)i(If)15 +b(the)g(\014rst)g(c)o(haracter)f(of)h(the)g(string)g(to)f(b)q(e)i(parsed)f +(is)g Fi('<')p Fp(,)g(w)o(e)f(should)i(ha)o(v)o(e)f(the)g(represen)o(tation)0 +1269 y(of)e(a)g(branc)o(h;)g(otherwise,)h(w)o(e)f(ha)o(v)o(e)f(a)h(leaf.)20 +b(In)14 b(the)f(\014rst)g(case,)g(calling)i(the)e(rest)g(of)g(the)g(input)h +(string)f(follo)o(wing)0 1326 y(the)j(op)q(ening)h(angle)f(brac)o(k)o(et)f +Fi(s)p Fp(,)g(an)o(y)h(p)q(ossible)h(parse)f(m)o(ust)f(b)q(e)h(a)f(tree)h +Fi(Branch)23 b(l)h(r)15 b Fp(with)h(remaining)h(string)0 1382 +y Fi(u)p Fp(,)e(sub)s(ject)g(to)f(the)i(follo)o(wing)f(conditions:)56 +1510 y(1.)22 b(The)15 b(tree)g Fi(l)g Fp(can)g(b)q(e)h(parsed)g(from)e(the)h +(b)q(eginning)j(of)c(the)i(string)f Fi(s)p Fp(.)56 1611 y(2.)22 +b(The)14 b(string)f(remaining)i(\(follo)o(wing)f(the)g(represen)o(tation)g +(of)f Fi(l)p Fp(\))g(b)q(egins)i(with)g Fi('|')o Fp(.)k(Call)c(the)f(tail)g +(of)g(this)114 1668 y(string)h Fi(t)p Fp(.)56 1769 y(3.)22 +b(The)15 b(tree)g Fi(r)g Fp(can)g(b)q(e)h(parsed)g(from)e(the)h(b)q(eginning) +j(of)c Fi(t)p Fp(.)56 1870 y(4.)22 b(The)15 b(string)g(remaining)h(from)f +Fo(that)h Fp(parse)f(b)q(egins)h(with)g Fi('>')o Fp(,)f(and)h +Fi(u)f Fp(is)g(the)h(tail.)0 1997 y(Notice)e(the)f(expressiv)o(e)h(p)q(o)o(w) +o(er)f(w)o(e)g(get)g(from)g(the)g(com)o(bination)h(of)f(pattern)g(matc)o +(hing)g(with)h(list)g(comprehen-)0 2054 y(sion:)22 b(The)16 +b(form)f(of)h(a)f(resulting)i(parse)f(is)g(giv)o(en)h(b)o(y)f(the)g(main)g +(expression)h(of)e(the)h(list)h(comprehension,)g(the)0 2110 +y(\014rst)d(t)o(w)o(o)e(conditions)k(ab)q(o)o(v)o(e)d(are)h(expressed)h(b)o +(y)f(the)g(\014rst)f(generator)h(\(\\)p Fi(\(l,)22 b('|':t\))14 +b Fp(is)g(dra)o(wn)g(from)f(the)h(list)0 2167 y(of)h(parses)g(of)g +Fi(s)o Fp(."\),)f(and)i(the)f(remaining)h(conditions)h(are)d(expressed)i(b)o +(y)f(the)h(second)f(generator.)71 2252 y(The)e(second)g(de\014ning)h +(equation)f(ab)q(o)o(v)o(e)f(just)h(sa)o(ys)f(that)g(to)g(parse)g(the)h +(represen)o(tation)g(of)f(a)g(leaf,)i(w)o(e)e(parse)0 2308 +y(a)k(represen)o(tation)g(of)f(the)i(elemen)o(t)f(t)o(yp)q(e)h(of)e(the)h +(tree)g(and)h(apply)f(the)h(constructor)e Fi(Leaf)g Fp(to)h(the)g(v)m(alue)h +(th)o(us)0 2365 y(obtained.)71 2450 y(W)l(e'll)f(accept)h(on)e(faith)h(for)g +(the)g(momen)o(t)f(that)g(there)h(is)h(a)e Fi(Text)g Fp(instance)i(of)f +Fi(Int)f Fp(\(among)g(man)o(y)g(other)0 2506 y(t)o(yp)q(es\),)g(pro)o(viding) +h(a)f Fi(reads)f Fp(that)g(b)q(eha)o(v)o(es)i(as)f(one)g(w)o(ould)h(exp)q +(ect,)f(e.g.:)115 2614 y Fi(\(reads)23 b("5)h(golden)f(rings"\))g(::)h +([\(Int,String\)])71 b Fn(\))j Fi([\(5,)23 b(")h(golden)f(rings"\)])p +eop +%%Page: 30 30 +bop 0 -40 a Fp(T-30)771 b Fj(5)45 b(TYPE)15 b(CLASSES,)h(O)o(VERLO)o(ADING,)g +(AND)f(\\OOP")0 105 y Fp(With)g(this)h(understanding,)g(the)f(reader)g +(should)i(v)o(erify)e(the)g(follo)o(wing)h(ev)m(aluations:)21 +194 y Fi(readsTree)22 b("<1|<2|3>>")101 b Fn(\))i Fi([\(Branch)22 +b(\(Leaf)i(1\))f(\(Branch)g(\(Leaf)g(2\))h(\(Leaf)f(3\)\),)g(""\)])21 +251 y(readsTree)f("<1|2")221 b Fn(\))103 b Fi([])71 362 y Fp(Because)19 +b(the)g(textual)g(represen)o(tation)f(w)o(e)h(ha)o(v)o(e)f(c)o(hosen)h(for)f +(trees)h(is)g(unam)o(biguous,)h Fi(readsTree)d Fp(will)0 418 +y(alw)o(a)o(ys)11 b(return)g(either)h(a)f(singleton)i(list)f(or)f(an)g(empt)o +(y)g(list,)i(pro)o(vided)f(that)f(the)g(represen)o(tation)h(of)f(the)g +(elemen)o(t)0 474 y(t)o(yp)q(e)k(is)g(also)f(unam)o(biguous.)21 +b(Supp)q(ose,)15 b(ho)o(w)o(ev)o(er,)f(that)f(w)o(e)i(w)o(ere)f(to)g(c)o +(hange)h(our)f(textual)h(represen)o(tation)f(of)0 531 y(trees)h(to)g(omit)g +(the)g(angle)h(brac)o(k)o(ets:)71 640 y Fi(readsTree)22 b(s)310 +b(=)48 b([\(Branch)23 b(l)g(r,)h(u\))g(|)f(\(l,)h('|':t\))f(<-)g(readsTree)g +(s,)1168 696 y(\(r,)h(u\))119 b(<-)23 b(readsTree)g(t)h(])715 +753 y(++)715 809 y([\(Leaf)f(x,)h(t\))119 b(|)23 b(\(x,t\))143 +b(<-)23 b(reads)h(s])0 921 y Fp(\(The)16 b Fi(\(++\))g Fp(here)h(can)g(b)q(e) +g(though)o(t)e(of)h(as)g(a)h(list)g(analogue)f(of)g(set)h(union;)g(that)f +(is,)h(the)g(tree)f(parses)g(of)g Fi(s)g Fp(are)0 977 y(all)g(its)g(parses)f +(as)f(a)h(branc)o(h)h(plus)g(all)g(its)f(parses)g(as)g(a)g(leaf.\))20 +b(No)o(w,)14 b(w)o(e)h(see)h(that:)51 1069 y Fi(readsTree)22 +b("1|2|3")102 b Fn(\))g Fi([\(Branch)23 b(\(Branch)g(\(Leaf)g(1\))h(\(Leaf)f +(2\)\))g(\(Leaf)g(3\),)h(""\),)730 1125 y(\(Branch)f(\(Leaf)g(1\))h(\(Branch) +f(\(Leaf)g(2\))g(\(Leaf)g(3\)\),)h(""\)])71 1236 y Fp(Returning)15 +b(to)f(our)g(unam)o(biguous)h(represen)o(tation,)f(there)h(are)f(a)g(couple)h +(of)f(shortcomings)g(to)g(deal)h(with.)0 1293 y(One)g(is)g(that)e(the)h +(parser)g(is)h(quite)g(rigid,)g(allo)o(wing)g(no)f(white)h(space)f(b)q(efore) +h(or)e(b)q(et)o(w)o(een)i(the)f(elemen)o(ts)h(of)f(the)0 1349 +y(tree)i(represen)o(tation;)h(the)f(other)g(is)h(that)e(the)i(w)o(a)o(y)e(w)o +(e)h(parse)g(our)g(punctuation)h(sym)o(b)q(ols)g(is)g(quite)g(di\013eren)o(t) +0 1406 y(from)10 b(the)g(w)o(a)o(y)g(w)o(e)g(parse)h(leaf)g(v)m(alues)g(and)g +(subtrees,)g(this)g(lac)o(k)g(of)f(uniformit)o(y)h(making)g(the)g(function)g +(de\014nition)0 1462 y(harder)16 b(to)g(read.)23 b(W)l(e)17 +b(can)f(address)h(b)q(oth)f(of)g(these)h(problems)g(b)o(y)f(using)h(the)g +(lexical)h(analyzer)f(pro)o(vided)g(b)o(y)0 1519 y(the)e(Standard)g(Prelude:) +71 1619 y Fi(lex)500 b(::)24 b(ReadS)f(String)0 1728 y(lex)c +Fp(normally)i(returns)f(a)f(singleton)i(list)g(con)o(taining)g(a)e(pair)i(of) +e(strings:)30 b(the)20 b(\014rst)f(lexeme)i(in)g(the)f(input)0 +1784 y(string)d(and)g(the)g(remainder)g(of)g(the)g(input.)26 +b(The)17 b(lexical)i(rules)e(are)g(those)f(of)h(Hask)o(ell)g(programs,)f +(including)0 1841 y(commen)o(ts,)h(whic)o(h)g Fi(lex)g Fp(skips,)h(along)f +(with)g(whitespace.)26 b(If)18 b(the)f(input)h(string)f(is)g(empt)o(y)g(or)f +(con)o(tains)h(only)0 1897 y(whitespace)e(and)f(commen)o(ts,)f +Fi(lex)h Fp(returns)g Fi([\("",""\)])o Fp(;)g(if)g(the)g(input)h(is)g(not)e +(empt)o(y)h(in)h(this)f(sense,)h(but)f(also)0 1954 y(do)q(es)i(not)e(b)q +(egin)j(with)e(a)g(v)m(alid)i(lexeme)f(after)f(an)o(y)g(leading)h(whitespace) +g(and)g(commen)o(ts,)e Fi(lex)h Fp(returns)g Fi([])o Fp(.)71 +2032 y(Using)g(the)h(lexical)h(analyzer,)e(our)g(tree)g(parser)g(no)o(w)g(lo) +q(oks)g(lik)o(e)h(this:)71 2141 y Fi(readsTree)356 b(::)24 +b(\(Text)f(a\))h(=>)f(ReadS)g(\(Tree)h(a\))71 2197 y(readsTree)e(s)310 +b(=)48 b([\(Branch)23 b(l)g(r,)h(x\))g(|)f(\("<",)g(t\))h(<-)g(lex)f(s,)1168 +2254 y(\(l,)71 b(u\))24 b(<-)g(readsTree)e(t,)1168 2310 y(\("|",)h(v\))h(<-)g +(lex)f(u,)1168 2367 y(\(r,)71 b(w\))24 b(<-)g(readsTree)e(v,)1168 +2423 y(\(">",)h(x\))h(<-)g(lex)f(w)215 b(])715 2480 y(++)715 +2536 y([\(Leaf)23 b(x,)h(t\))119 b(|)23 b(\(x,)71 b(t\))24 +b(<-)g(reads)f(s)167 b(])p eop +%%Page: 31 31 +bop 0 -40 a Fj(5.4)45 b(Deriv)o(ed)15 b(Instances)1402 b Fp(T-31)71 +105 y(W)l(e)10 b(ma)o(y)g(no)o(w)h(wish)g(to)f(use)h Fi(readsTree)f +Fp(and)h Fi(showsTree)e Fp(to)h(declare)i Fi(\(Text)23 b(a\))h(=>)g(Tree)f(a) +10 b Fp(an)h(instance)0 162 y(of)18 b Fi(Text)o Fp(.)30 b(This)19 +b(w)o(ould)f(allo)o(w)h(us)f(to)g(use)h(the)f(generic)h(o)o(v)o(erloaded)g +(functions)g(from)e(the)i(Prelude)g(to)f(parse)0 218 y(and)c(displa)o(y)g +(trees.)19 b(Moreo)o(v)o(er,)12 b(w)o(e)i(w)o(ould)g(automatically)f(then)h +(b)q(e)g(able)h(to)e(parse)g(and)h(displa)o(y)g(man)o(y)f(other)0 +274 y(t)o(yp)q(es)k(con)o(taining)g(trees)f(as)g(comp)q(onen)o(ts,)h(for)f +(example,)h Fi([Tree)23 b(Int])p Fp(.)h(As)16 b(it)h(turns)f(out,)g +Fi(readsTree)g Fp(and)0 331 y Fi(showsTree)c Fp(are)h(of)h(almost)f(the)g +(righ)o(t)h(t)o(yp)q(es)f(to)g(b)q(e)h Fi(Text)f Fp(metho)q(ds,)h(needing)h +(only)f(the)f(addition)i(of)e(an)h(extra)0 387 y(parameter)f(eac)o(h)g(that)g +(has)g(do)h(do)f(with)h(paren)o(thesization)g(of)f(forms)g(with)h(in\014x)h +(constructors.)j(W)l(e)c(refer)f(the)0 444 y(in)o(terested)j(reader)f(to)f +Fn(x)q Fp(E.2)g(for)h(details.)71 527 y(W)l(e)i(can)g(test)f(suc)o(h)h(a)g +Fi(Text)f Fp(instance)i(b)o(y)f(applying)h Fi(\(read)24 b(.)f(show\))16 +b Fp(\(whic)o(h)i(should)g(b)q(e)g(the)f(iden)o(tit)o(y\))0 +584 y(to)e(some)f(trees,)h(where)g Fi(read)g Fp(is)h(a)f(sp)q(ecialization)i +(of)e Fi(reads)p Fp(:)71 693 y Fi(read)476 b(::)24 b(\(Text)f(a\))h(=>)f +(String)g(->)h(a)0 802 y Fp(This)c(function)g(fails)h(if)f(there)f(is)h(not)f +(a)g(unique)i(parse)f(or)f(if)h(the)f(input)i(con)o(tains)e(an)o(ything)h +(more)f(than)g(a)0 858 y(represen)o(tation)c(of)g(one)g(v)m(alue)i(of)e(t)o +(yp)q(e)g Fi(a)g Fp(\(and)g(p)q(ossibly)l(,)h(commen)o(ts)f(and)g +(whitespace\).)0 1010 y Fc(5.3.2)52 b(Binary)17 b(Class)0 1123 +y Fp(The)e Fi(Binary)f Fp(class)i(is)f(similar)h(to)f Fi(Text)o +Fp(,)g(but)g(uses)g(a)g(primitiv)o(e)h(abstract)e(t)o(yp)q(e)h +Fi(Bin)g Fp(instead)g(of)g Fi(String)o Fp(,)g(the)0 1180 y(purp)q(ose)f(of)g +(whic)o(h)g(is)h(e\016cien)o(t)f(transparen)o(t)f(I/O.)h(\(See)g +Fn(x)p Fp(7.\))19 b(Generally)l(,)c(only)f(deriv)o(ed)h(instances)f(of)g +Fi(Binary)0 1236 y Fp(are)22 b(used)g(\(see)g(b)q(elo)o(w\),)h(whic)o(h)g +(generate)e(implemen)o(tation-de\014ned)k(op)q(erations)d Fi(readBin)f +Fp(and)h Fi(showBin)0 1293 y Fp(\(analogous)15 b(to)f Fi(reads)h +Fp(and)g Fi(shows)o Fp(\).)0 1446 y Fg(5.4)56 b(Deriv)n(ed)17 +b(Instances)0 1560 y Fp(Recall)12 b(the)f Fi(Eq)f Fp(instance)i(for)e(trees)g +(w)o(e)h(presen)o(ted)g(in)g(Section)h(5;)f(suc)o(h)g(a)g(declaration)g(is)g +(simple|and)i(b)q(oring|)0 1616 y(to)f(pro)q(duce:)20 b(W)l(e)12 +b(require)i(that)e(the)h(elemen)o(t)g(t)o(yp)q(e)g(in)h(the)e(lea)o(v)o(es)h +(b)q(e)h(an)e(equalit)o(y)i(t)o(yp)q(e;)f(then,)g(t)o(w)o(o)e(lea)o(v)o(es)i +(are)0 1673 y(equal)18 b(i\013)f(they)h(con)o(tain)f(equal)h(elemen)o(ts,)g +(and)f(t)o(w)o(o)f(branc)o(hes)i(are)f(equal)h(i\013)f(their)h(left)f(and)h +(righ)o(t)f(subtrees)0 1729 y(are)e(equal,)g(resp)q(ectiv)o(ely)l(.)22 +b(An)o(y)15 b(other)g(t)o(w)o(o)f(trees)h(are)g(unequal:)71 +1838 y Fi(instance)46 b(\(Eq)24 b(a\))f(=>)h(Eq)f(\(Tree)h(a\))47 +b(where)166 1895 y(\(Leaf)23 b(x\))119 b(==)24 b(\(Leaf)f(y\))191 +b(=)47 b(x)24 b(==)g(y)166 1951 y(\(Branch)f(l)h(r\))f(==)h(\(Branch)f(l')g +(r'\))48 b(=)f(l)24 b(==)g(l')f(&&)h(r)f(==)h(r')166 2008 y(_)286 +b(==)24 b(_)358 b(=)47 b(False)71 2168 y Fp(F)l(ortunately)l(,)13 +b(w)o(e)g(don't)g(need)i(to)e(go)g(through)g(this)h(tedium)g(ev)o(ery)g(time) +g(w)o(e)f(need)i(equalit)o(y)f(op)q(erators)f(for)0 2225 y(a)f(new)i(t)o(yp)q +(e;)f(the)g Fi(Eq)f Fp(instance)i(can)f(b)q(e)g Fo(derive)n(d)h(automatic)n +(al)r(ly)g Fp(from)e(the)h Fi(data)f Fp(declaration)i(if)f(w)o(e)g(so)f(sp)q +(ecify:)71 2335 y Fi(data)47 b(Tree)23 b(a)286 b(=)48 b(Leaf)23 +b(a)h(|)g(Branch)f(\(Tree)g(a\))g(\(Tree)h(a\))47 b(deriving)23 +b(Eq)0 2444 y Fp(The)16 b Fi(deriving)e Fp(clause)i(implicitly)i(pro)q(duces) +e(an)g Fi(Eq)f Fp(instance)h(declaration)g(just)f(lik)o(e)h(the)g(one)f(in)h +(Section)h(5.)0 2501 y(Instances)d(of)f Fi(Ord)o Fp(,)h Fi(Enum)o +Fp(,)g Fi(Ix)o Fp(,)g Fi(Text)o Fp(,)f(and)h Fi(Binary)e Fp(can)i(also)f(b)q +(e)h(generated)g(b)o(y)f(the)h Fi(deriving)e Fp(clause.)20 +b([More)0 2557 y(than)d(one)g(class)h(name)f(can)h(b)q(e)g(sp)q(eci\014ed,)h +(in)f(whic)o(h)g(case)f(the)h(list)g(of)f(names)g(m)o(ust)f(b)q(e)i(paren)o +(thesized)h(and)0 2614 y(the)c(names,)g(separated)g(b)o(y)g(commas.])p +eop +%%Page: 32 32 +bop 0 -40 a Fp(T-32)771 b Fj(5)45 b(TYPE)15 b(CLASSES,)h(O)o(VERLO)o(ADING,)g +(AND)f(\\OOP")71 105 y Fp(The)g(deriv)o(ed)h Fi(Ord)f Fp(instance)h(for)f +Fi(Tree)f Fp(is)i(sligh)o(tly)g(more)f(complicated)h(than)g(the)f +Fi(Eq)g Fp(instance:)71 215 y Fi(instance)46 b(\(Ord)24 b(a\))f(=>)h(Ord)f +(\(Tree)g(a\))48 b(where)166 272 y(\(Leaf)23 b(_\))119 b(<=)24 +b(\(Branch)f(_\))143 b(=)47 b(True)166 328 y(\(Leaf)23 b(x\))119 +b(<=)24 b(\(Leaf)f(y\))191 b(=)47 b(x)24 b(<=)g(y)166 385 y(\(Branch)f(_\))71 +b(<=)24 b(\(Leaf)f(_\))191 b(=)47 b(False)166 441 y(\(Branch)23 +b(l)h(r\))f(<=)h(\(Branch)f(l')g(r'\))48 b(=)f(l)24 b(==)g(l')f(&&)h(r)f(<=)h +(r')g(||)f(l)h(<=)g(l')0 550 y Fp(This)17 b(sp)q(eci\014es)i(a)d +Fo(lexic)n(o)n(gr)n(aphic)g Fp(order:)23 b(Constructors)15 +b(are)i(ordered)g(b)o(y)f(the)h(order)f(of)h(their)g(app)q(earance)g(in)0 +607 y(the)f Fi(data)g Fp(declaration,)h(and)g(the)f(argumen)o(ts)g(of)g(a)g +(constructor)f(are)i(compared)f(from)g(left)g(to)g(righ)o(t.)23 +b(Recall)0 663 y(that)16 b(the)g(built-in)j(list)e(t)o(yp)q(e)g(is)g(seman)o +(tically)g(equiv)m(alen)o(t)h(to)e(an)g(ordinary)h(t)o(w)o(o-constructor)d(t) +o(yp)q(e.)24 b(In)17 b(fact,)0 720 y(this)f(is)f(the)h(full)g(declaration:)71 +820 y Fi(data)23 b([a])190 b(=)24 b([])g(|)f(a)h(:)g([a])f(deriving)g(\(Eq,)g +(Ord,)h(Binary\))118 b(--)24 b(pseudocode)0 931 y Fp(\(Lists)16 +b(also)f(ha)o(v)o(e)g(a)g Fi(Text)g Fp(instance,)h(whic)o(h)g(is)g(not)f +(deriv)o(ed.\))21 b(The)15 b(deriv)o(ed)i Fi(Eq)e Fp(and)g +Fi(Ord)g Fp(instances)h(for)f(lists)0 988 y(are)e(the)h(usual)h(ones;)f(in)g +(particular,)h(c)o(haracter)e(strings,)h(as)f(lists)h(of)g(c)o(haracters,)f +(are)g(ordered)h(as)g(determined)0 1044 y(b)o(y)20 b(the)g(underlying)j +Fi(Char)c Fp(t)o(yp)q(e,)i(with)g(an)f(initial)i(substring)f(comparing)f +(less)h(than)f(a)g(longer)g(string;)j(for)0 1101 y(example,)16 +b Fi("cat")23 b(<)h("catalog")n Fp(.)71 1194 y(In)11 b(practice,)h +Fi(Eq)f Fp(and)g Fi(Ord)f Fp(instances)i(are)f(almost)f(alw)o(a)o(ys)g(deriv) +o(ed,)j(rather)d(than)h(user-de\014ned.)20 b(In)11 b(fact,)g(w)o(e)0 +1250 y(should)16 b(pro)o(vide)f(our)f(o)o(wn)g(de\014nitions)i(of)e(equalit)o +(y)i(and)e(ordering)h(predicates)g(only)g(with)g(some)g(trepidation,)0 +1307 y(b)q(eing)i(careful)g(to)e(main)o(tain)h(the)g(exp)q(ected)h(algebraic) +g(prop)q(erties)f(of)f(equiv)m(alence)k(relations)d(and)g(partial)g(or)0 +1363 y(total)h(orders.)28 b(An)19 b(in)o(transitiv)o(e)f Fi(\(==\))g +Fp(predicate,)h(for)e(example,)i(could)h(b)q(e)e(disastrous,)g(confusing)h +(readers)0 1420 y(of)f(the)g(program)f(and)h(confounding)h(man)o(ual)g(or)e +(automatic)h(program)f(transformations)g(that)g(rely)i(on)f(the)0 +1476 y Fi(\(==\))i Fp(predicate's)h(b)q(eing)h(an)f(appro)o(ximation)f(to)g +(de\014nitional)j(equalit)o(y)l(.)37 b(Nev)o(ertheless,)23 +b(it)e(is)g(sometimes)0 1533 y(necessary)16 b(to)f(pro)o(vide)h +Fi(Eq)f Fp(or)g Fi(Ord)h Fp(instances)g(di\013eren)o(t)g(from)f(those)g(that) +g(w)o(ould)h(b)q(e)g(deriv)o(ed;)h(probably)f(the)0 1589 y(most)h(imp)q +(ortan)o(t)h(example)h(is)f(that)g(of)f(an)h(abstract)f(data)h(t)o(yp)q(e)g +(in)h(whic)o(h)g(di\013eren)o(t)f(concrete)g(v)m(alues)i(ma)o(y)0 +1645 y(represen)o(t)15 b(the)g(same)g(abstract)g(v)m(alue.)71 +1738 y(An)h(en)o(umerated)g(t)o(yp)q(e)g(can)g(ha)o(v)o(e)f(a)h(deriv)o(ed)h +Fi(Enum)e Fp(instance,)i(and)f(here)g(again,)g(the)g(ordering)g(is)g(that)f +(of)0 1795 y(the)g(constructors)g(in)h(the)f Fi(data)g Fp(declaration.)20 +b(F)l(or)15 b(example:)71 1904 y Fi(data)23 b(Day)g(=)h(Sunday)f(|)h(Monday)f +(|)h(Tuesday)f(|)g(Wednesday)285 1960 y(|)h(Thursday)f(|)h(Friday)f(|)g +(Saturday)214 b(deriving)23 b(\(Eq,)g(Ord,)g(Enum\))272 2099 +y([Wednesday..Friday])141 b Fn(\))72 b Fi([Wednesday,)22 b(Thursday,)h +(Friday])272 2156 y([Monday,)g(Wednesday)f(..])72 b Fn(\))g +Fi([Monday,)23 b(Wednesday,)f(Friday])71 2295 y Fp(An)17 b(en)o(umerated)g(t) +o(yp)q(e)h(or)e(tuple)i(\(single-constructor\))g(t)o(yp)q(e)f(all)h(of)f +(whose)g(comp)q(onen)o(t)g(t)o(yp)q(es)g(ha)o(v)o(e)g Fi(Ix)0 +2351 y Fp(instances)f(can)f(ha)o(v)o(e)g(a)g(deriv)o(ed)h Fi(Ix)f +Fp(instance.)21 b(\(See)15 b(Section)h Fn(x)q Fp(6.9.\))71 +2444 y(Deriv)o(ed)j Fi(Text)f Fp(instances)i(are)e(p)q(ossible)i(for)f(en)o +(umerated)g(t)o(yp)q(es)f(and)h(other)g(t)o(yp)q(es)f(all)i(of)e(whose)h +(com-)0 2501 y(p)q(onen)o(t)g(t)o(yp)q(es)f(also)g(ha)o(v)o(e)g +Fi(Text)g Fp(instances.)30 b(\()p Fi(Text)17 b Fp(instances)i(for)f(most)f +(of)h(the)g(standard)g(t)o(yp)q(es,)h(but)f(not)0 2557 y(functions,)d(are)f +(pro)o(vided)h(b)o(y)f(the)h(Standard)f(Prelude.\))21 b(The)14 +b(textual)h(represen)o(tation)f(de\014ned)i(b)o(y)e(a)g(deriv)o(ed)0 +2614 y Fi(Text)21 b Fp(instance)h(is)f(consisten)o(t)h(with)f(the)h(app)q +(earance)f(of)g(constan)o(t)g(Hask)o(ell)h(expressions)g(of)f(the)g(t)o(yp)q +(e)g(in)p eop +%%Page: 33 33 +bop 0 -40 a Fj(5.5)45 b(Num)o(b)q(ers)1575 b Fp(T-33)0 105 +y(question.)20 b(F)l(or)15 b(example,)h(if)f(w)o(e)g(add)h +Fi(Text)e Fp(to)h(the)g Fi(deriving)f Fp(clause)i(for)f(t)o(yp)q(e)g +Fi(Day)p Fp(,)f(ab)q(o)o(v)o(e,)h(w)o(e)g(obtain)259 209 y +Fi(show)23 b([Monday..Wednesday])71 b Fn(\))i Fi +("[Monday,Tuesday,Wednesday]")0 361 y Fg(5.5)56 b(Num)n(b)r(ers)0 +474 y Fp(Hask)o(ell)21 b(pro)o(vides)f(a)g(ric)o(h)g(collection)i(of)d(n)o +(umeric)i(t)o(yp)q(es,)f(based)h(on)e(those)h(of)f(Sc)o(heme[5],)i(whic)o(h)f +(in)h(turn)0 530 y(are)15 b(based)h(on)f(Common)g(Lisp[6)q(].)20 +b(\(Those)15 b(languages,)g(ho)o(w)o(ev)o(er,)g(are)g(dynamically)i(t)o(yp)q +(ed.\))k(The)15 b(standard)0 587 y(t)o(yp)q(es)j(include)j(\014xed-)e(and)f +(arbitrary-precision)h(in)o(tegers,)g(ratios)f(\(rational)f(n)o(um)o(b)q +(ers\))i(formed)e(from)h(eac)o(h)0 643 y(in)o(teger)c(t)o(yp)q(e,)g(and)g +(single-)h(and)f(double-precision)j(real)d(and)g(complex)h(\015oating-p)q +(oin)o(t.)20 b(W)l(e)14 b(outline)h(here)f(the)0 700 y(basic)i(c)o +(haracteristics)f(of)g(the)g(n)o(umeric)i(t)o(yp)q(e)e(class)g(structure)g +(and)h(refer)f(the)g(reader)g(to)g Fn(x)p Fp(6.8)f(for)h(details.)0 +850 y Fc(5.5.1)52 b(Numeric)17 b(Class)g(Structure)0 963 y +Fp(Returning)f(to)f(Figure)g(2,)f(w)o(e)h(notice)g(that)g(the)g(n)o(umeric)h +(t)o(yp)q(e)f(classes)g(\(class)g Fi(Num)g Fp(and)g(those)g(that)f(lie)i(b)q +(elo)o(w)0 1019 y(it\))h(accoun)o(t)f(for)g(more)g(than)h(half)g(of)f(the)h +(standard)f(classes.)24 b(W)l(e)17 b(also)g(note)f(that)g Fi(Num)g +Fp(is)h(a)g(sub)q(class)g(of)g Fi(Eq)o Fp(,)0 1076 y(but)c(not)f(of)h +Fi(Ord)o Fp(;)h(this)f(is)g(b)q(ecause)h(the)f(order)f(predicates)i(do)f(not) +f(apply)i(to)e(complex)i(n)o(um)o(b)q(ers.)19 b(The)13 b(sub)q(class)0 +1132 y Fi(Real)i Fp(of)f Fi(Num)p Fp(,)h(ho)o(w)o(ev)o(er,)f(is)h(a)g(sub)q +(class)i(of)d Fi(Ord)h Fp(as)g(w)o(ell.)71 1215 y(The)k Fi(Num)f +Fp(class)i(pro)o(vides)f(sev)o(eral)g(basic)h(op)q(erations)f(common)g(to)f +(all)i(n)o(umeric)g(t)o(yp)q(es;)h(these)e(include,)0 1271 +y(among)14 b(others,)h(addition,)h(subtraction,)f(negation,)g(m)o +(ultiplication,)i(and)e(absolute)h(v)m(alue:)71 1380 y Fi(\(+\),)23 +b(\(-\),)g(\(*\))262 b(::)24 b(\(Num)f(a\))h(=>)f(a)h(->)g(a)f(->)h(a)71 +1437 y(negate,)e(abs)310 b(::)24 b(\(Num)f(a\))h(=>)f(a)h(->)g(a)0 +1548 y Fp([)p Fi(negate)10 b Fp(is)h(the)g(function)h(applied)h(b)o(y)e(Hask) +o(ell's)g(only)h(pre\014x)f(op)q(erator,)g(min)o(us;)i(w)o(e)d(can't)h(call)h +(it)f Fi(\(-\))p Fp(,)g(b)q(ecause)0 1605 y(that)j(is)h(the)g(subtraction)g +(function,)g(so)g(this)g(name)g(is)g(pro)o(vided)h(instead.)k(F)l(or)14 +b(example,)i Fi(-x*y)e Fp(is)h(equiv)m(alen)o(t)0 1661 y(to)d +Fi(negate)23 b(\(x*y\))o Fp(.)c(\(Pre\014x)12 b(min)o(us)h(has)f(the)g(same)g +(syn)o(tactic)g(precedence)i(as)e(in\014x)h(min)o(us,)g(whic)o(h,)g(of)f +(course,)0 1718 y(is)k(lo)o(w)o(er)f(than)g(that)f(of)h(m)o +(ultiplication.\)])71 1801 y(Note)d(that)h Fi(Num)g Fp(do)q(es)g +Fo(not)h Fp(pro)o(vide)f(a)g(division)j(op)q(erator;)c(t)o(w)o(o)g +(di\013eren)o(t)i(kinds)g(of)f(division)i(op)q(erators)d(are)0 +1857 y(pro)o(vided)k(in)g(t)o(w)o(o)e(non-o)o(v)o(erlapping)i(sub)q(classes)g +(of)f Fi(Num)p Fp(:)71 1940 y(The)i(class)h Fi(Integral)e Fp(pro)o(vides)i +(whole-n)o(um)o(b)q(er)h(division)g(and)f(remainder)g(op)q(erations,)f(as)g +(w)o(ell)i(as)e(the)0 1996 y Fi(even)e Fp(and)h Fi(odd)g Fp(predicates.)23 +b(The)16 b(standard)f(instances)i(of)e Fi(Integral)g Fp(are)h +Fi(Integer)f Fp(\(un)o(b)q(ounded)i(or)e(math-)0 2053 y(ematical)20 +b(in)o(tegers,)f(also)g(kno)o(wn)g(as)g(\\bign)o(ums"\))f(and)i +Fi(Int)e Fp(\(b)q(ounded,)j(mac)o(hine)f(in)o(tegers,)f(with)h(a)e(range)0 +2109 y(equiv)m(alen)o(t)i(to)f(at)f(least)h(29-bit)f(signed)i(binary\).)31 +b(A)19 b(particular)g(Hask)o(ell)h(implemen)o(tation)g(migh)o(t)e(pro)o(vide) +0 2166 y(other)d(in)o(tegral)g(t)o(yp)q(es)g(in)g(addition)h(to)f(these.)20 +b(Note)14 b(that)g Fi(Integral)g Fp(is)h(a)g(sub)q(class)h(of)e +Fi(Real)p Fp(,)g(rather)h(than)f(of)0 2222 y Fi(Num)h Fp(directly;)h(this)g +(means)f(that)f(there)h(is)h(no)f(attempt)g(to)f(pro)o(vide)i(Gaussian)f(in)o +(tegers.)71 2305 y(All)23 b(other)e(n)o(umeric)i(t)o(yp)q(es)e(fall)i(in)g +(the)f(class)g Fi(Fractional)n Fp(,)h(whic)o(h)g(pro)o(vides)f(the)g +(ordinary)g(division)0 2361 y(op)q(erator)13 b Fi(\(/\))p Fp(.)19 +b(The)c(further)e(sub)q(class)j Fi(Floating)d Fp(con)o(tains)h +(trigonometric,)f(logarithmic,)i(and)f(exp)q(onen)o(tial)0 +2418 y(functions.)71 2501 y(The)j Fi(RealFrac)g Fp(sub)q(class)h(of)g +Fi(Fractional)e Fp(and)i Fi(Real)f Fp(pro)o(vides)h(a)f(function)h +Fi(properFraction)n Fp(,)g(whic)o(h)0 2557 y(decomp)q(oses)e(a)f(n)o(um)o(b)q +(er)h(in)o(to)f(its)h(whole)g(and)g(fractional)f(parts,)g(and)g(a)g +(collection)j(of)d(functions)h(that)f(round)0 2614 y(to)g(in)o(tegral)g(v)m +(alues)h(b)o(y)g(di\013ering)g(rules:)p eop +%%Page: 34 34 +bop 0 -40 a Fp(T-34)771 b Fj(5)45 b(TYPE)15 b(CLASSES,)h(O)o(VERLO)o(ADING,)g +(AND)f(\\OOP")71 160 y Fi(properFraction)236 b(::)24 b(\(Fractional)e(a,)i +(Integral)f(b\))g(=>)h(a)g(->)f(\(b,a\))71 216 y(truncate,)f(round,)71 +272 y(floor,)h(ceiling:)213 b(::)24 b(\(Fractional)e(a,)i(Integral)f(b\))g +(=>)h(a)g(->)f(b)71 453 y Fp(The)d Fi(RealFloat)f Fp(sub)q(class)j(of)e +Fi(Floating)f Fp(and)i Fi(RealFrac)e Fp(pro)o(vides)i(some)f(sp)q(ecialized)j +(functions)f(for)0 509 y(e\016cien)o(t)c(access)g(to)f(the)h(comp)q(onen)o +(ts)g(of)f(a)g(\015oating-p)q(oin)o(t)i(n)o(um)o(b)q(er,)f(the)g +Fo(exp)n(onent)f Fp(and)g Fo(signi\014c)n(and)p Fp(.)26 b(The)0 +566 y(standard)15 b(t)o(yp)q(es)g Fi(Float)f Fp(and)i Fi(Double)e +Fp(fall)i(in)g(class)g Fi(RealFloat)o Fp(.)0 782 y Fc(5.5.2)52 +b(Constructed)17 b(Num)o(b)q(ers)0 924 y Fp(Of)h(the)g(standard)f(n)o(umeric) +i(t)o(yp)q(es,)f Fi(Int)p Fp(,)g Fi(Integer)o Fp(,)g Fi(Float)o +Fp(,)g(and)g Fi(Double)f Fp(are)h(primitiv)o(e.)29 b(The)18 +b(others)f(are)0 981 y(made)e(from)g(these)g(b)o(y)g(t)o(yp)q(e)g +(constructors:)71 1083 y Fi(Complex)e Fp(is)i(a)g(t)o(yp)q(e)f(constructor)g +(that)g(mak)o(es)g(a)h(complex)g(t)o(yp)q(e)g(in)g(class)g +Fi(Floating)f Fp(from)g(a)g Fi(RealFloat)0 1140 y Fp(t)o(yp)q(e:)71 +1249 y Fi(data)23 b(\(RealFloat)f(a\))i(=>)g(Complex)e(a)i(=)g(a)g(:+)f(a)48 +b(deriving)23 b(\(Eq,)g(Binary,)g(Text\))0 1358 y Fp(Notice)14 +b(the)f(con)o(text)g Fi(\(RealFloat)23 b(a\))g(=>)p Fp(,)13 +b(whic)o(h)i(restricts)e(the)g(argumen)o(t)g(t)o(yp)q(e;)g(th)o(us,)h(the)f +(standard)g(com-)0 1414 y(plex)18 b(t)o(yp)q(es)g(are)f Fi(Complex)23 +b(Float)17 b Fp(and)g Fi(Complex)23 b(Double)o Fp(.)k(W)l(e)17 +b(can)h(also)f(see)h(from)f(the)g Fi(data)g Fp(declaration)0 +1471 y(that)12 b(a)g(complex)i(n)o(um)o(b)q(er)e(is)h(written)g +Fo(x)18 b Fi(:+)12 b Fo(y)t Fp(;)i(the)e(argumen)o(ts)g(are)g(the)h +(cartesian)f(real)h(and)g(imaginary)g(parts,)0 1527 y(resp)q(ectiv)o(ely)l(.) +22 b(Since)16 b Fi(:+)f Fp(is)h(a)f(data)f(constructor,)g(w)o(e)h(can)h(use)f +(it)h(in)g(pattern)e(matc)o(hing:)71 1636 y Fi(conjugate)356 +b(::)24 b(\(RealFloat)e(a\))i(=>)g(Complex)e(a)i(->)g(Complex)f(a)71 +1693 y(conjugate)f(\(x:+y\))190 b(=)48 b(x)24 b(:+)f(\(-y\))71 +1873 y Fp(Similarly)l(,)15 b(the)f(t)o(yp)q(e)g(constructor)f +Fi(Ratio)f Fp(mak)o(es)h(a)h(rational)f(t)o(yp)q(e)h(in)g(class)g +Fi(RealFrac)f Fp(from)g(an)g(instance)0 1929 y(of)k Fi(Integral)o +Fp(.)26 b(\()p Fi(Rational)16 b Fp(is)i(a)f(t)o(yp)q(e)h(synon)o(ym)f(for)g +Fi(Ratio)23 b(Integer)o Fp(.\))j Fi(Ratio)o Fp(,)18 b(ho)o(w)o(ev)o(er,)f(is) +h(an)f(abstract)0 1986 y(t)o(yp)q(e)h(constructor,)g(b)q(eing)h(de\014ned)h +(in)f(the)f(Prelude)h(mo)q(dule)h Fi(PreludeRatio)n Fp(,)e(from)g(whic)o(h)h +(it)f(is)h(exp)q(orted)0 2042 y(without)d(its)g(data)f(constructor;)g(th)o +(us,)h(ratios)f(cannot)h(b)q(e)g(pattern-matc)o(hed.)22 b(Instead,)16 +b(a)g(function)g(to)g(form)0 2099 y(a)f(ratio)g(from)f(t)o(w)o(o)g(in)o +(tegers)h(is)h(pro)o(vided,)g(as)e(w)o(ell)j(as)d(comp)q(onen)o(t)i +(extraction)f(functions:)71 2208 y Fi(\(\045\))500 b(::)24 +b(\(Integral)f(a\))g(=>)h(a)f(->)h(a)g(->)f(Ratio)h(a)71 2264 +y(numerator,)e(denominator)46 b(::)24 b(\(Integral)f(a\))g(=>)h(Ratio)f(a)h +(->)f(a)71 2444 y Fp(Wh)o(y)17 b(the)g(di\013erence?)29 b(Complex)19 +b(n)o(um)o(b)q(ers)e(in)i(cartesian)e(form)g(are)h(unique|there)h(are)e(no)h +(non)o(trivial)0 2501 y(iden)o(tities)g(in)o(v)o(olving)g Fi(:+)p +Fp(.)23 b(On)17 b(the)f(other)h(hand,)f(ratios)g(are)g(not)g(unique,)i(but)f +(ha)o(v)o(e)f(a)g(canonical)h(\(reduced\))0 2557 y(form)h(that)f(the)i +(implemen)o(tation)h(of)e(the)g(abstract)g(data)g(t)o(yp)q(e)g(m)o(ust)g +(main)o(tain;)i(it)f(is)g(not)f(necessarily)i(the)0 2614 y(case,)14 +b(for)h(instance,)g(that)f Fi(numerator)23 b(\(x\045y\))14 +b Fp(is)h(equal)g(to)f Fi(x)p Fp(,)h(although)g(the)g(real)g(part)f(of)g +Fi(x:+y)g Fp(is)i(alw)o(a)o(ys)e Fi(x)o Fp(.)p eop +%%Page: 35 35 +bop 0 -40 a Fj(5.5)45 b(Num)o(b)q(ers)1575 b Fp(T-35)0 105 +y Fc(5.5.3)52 b(Numeric)17 b(Co)q(ercions)h(and)g(Ov)o(erloaded)f(Literals)0 +269 y Fp(The)e(Standard)h(Prelude)g(pro)o(vides)g(sev)o(eral)f(o)o(v)o +(erloaded)g(functions)h(that)f(serv)o(e)g(as)f(explicit)k(co)q(ercions:)71 +378 y Fi(fromInteger)308 b(::)24 b(\(Num)f(a\))h(=>)f(Integer)g(->)h(a)71 +435 y(fromRational)284 b(::)24 b(\(Fractional)e(a\))i(=>)f(Rational)g(->)h(a) +71 491 y(toInteger)356 b(::)24 b(\(Integral)f(a\))g(=>)h(a)f(->)h(Integer)71 +548 y(toRational)332 b(::)24 b(\(RealFrac)f(a\))g(=>)h(a)f(->)h(Rational)71 +604 y(fromIntegral)284 b(::)24 b(\(Integral)f(a,)g(Num)h(b\))f(=>)h(a)f(->)h +(b)71 661 y(fromRealFrac)284 b(::)24 b(\(RealFrac)f(a,)g(Fractional)g(b\))g +(=>)h(a)g(->)f(b)71 740 y(fromIntegral)284 b(=)48 b(fromInteger)22 +b(.)i(toInteger)71 797 y(fromRealFrac)284 b(=)48 b(fromRational)22 +b(.)i(toRational)71 992 y Fp(Tw)o(o)18 b(of)h(these)g(are)g(implicitly)k +(used)c(to)g(pro)o(vide)h(o)o(v)o(erloaded)f(n)o(umeric)h(literals:)29 +b(An)20 b(in)o(teger)g(n)o(umeral)0 1049 y(\(without)c(a)g(decimal)i(p)q(oin) +o(t\))e(is)h(actually)g(equiv)m(alen)o(t)h(to)e(an)g(application)i(of)e +Fi(fromInteger)f Fp(to)g(the)i(v)m(alue)g(of)0 1105 y(the)i(n)o(umeral)g(as)f +(an)h Fi(Integer)o Fp(.)31 b(Similarly)l(,)21 b(a)e(\015oating)g(n)o(umeral)g +(\(with)g(a)f(decimal)i(p)q(oin)o(t\))f(is)h(regarded)e(as)0 +1162 y(an)e(application)h(of)e Fi(fromRational)g Fp(to)g(the)h(v)m(alue)h(of) +e(the)h(n)o(umeral)g(as)f(a)h Fi(Rational)o Fp(.)21 b(Th)o(us,)16 +b Fi(7)f Fp(has)h(the)g(t)o(yp)q(e)0 1218 y Fi(\(Num)23 b(a\))h(=>)f(a)p +Fp(,)18 b(and)f Fi(7.3)g Fp(has)g(the)h(t)o(yp)q(e)f Fi(Fractional)23 +b(a)g(=>)h(a)p Fp(.)i(This)18 b(means)f(that)g(w)o(e)g(can)g(use)h(n)o +(umeric)0 1274 y(literals)e(in)g(generic)h(n)o(umeric)f(functions,)f(for)g +(example:)71 1383 y Fi(halve)452 b(::)24 b(\(Fractional)e(a\))i(=>)f(a)h(->)g +(a)71 1440 y(halve)f(x)405 b(=)48 b(x)24 b(*)f(0.5)0 1549 y +Fp(This)16 b(rather)e(indirect)i(w)o(a)o(y)e(of)h(o)o(v)o(erloading)g(n)o +(umerals)g(has)g(the)g(additional)i(adv)m(an)o(tage)d(that)g(the)h(metho)q(d) +g(of)0 1605 y(in)o(terpreting)f(a)g(n)o(umeral)g(as)f(a)g(n)o(um)o(b)q(er)h +(of)f(a)g(giv)o(en)i(t)o(yp)q(e)e(can)h(b)q(e)g(sp)q(eci\014ed)i(in)e(an)g +Fi(Integral)e Fp(or)h Fi(Fractional)0 1662 y Fp(instance)j(declaration)g +(\(since)g Fi(fromInteger)d Fp(and)i Fi(fromRational)f Fp(are)g(op)q(erators) +g(of)h(those)g(classes,)g(resp)q(ec-)0 1718 y(tiv)o(ely\).)20 +b(F)l(or)15 b(example,)h(the)f Fi(Num)g Fp(instance)h(of)e +Fi(\(RealFloat)23 b(a\))g(=>)h(Complex)f(a)15 b Fp(con)o(tains)g(this)h +(metho)q(d:)71 1830 y Fi(fromInteger)22 b(x)262 b(=)48 b(fromInteger)22 +b(x)i(:+)g(0)0 1939 y Fp(This)15 b(sa)o(ys)f(that)g(a)h Fi(Complex)f +Fp(instance)h(of)g Fi(fromInteger)e Fp(is)i(de\014ned)h(to)e(pro)q(duce)i(a)e +(complex)i(n)o(um)o(b)q(er)f(whose)0 1995 y(real)h(part)e(is)i(supplied)i(b)o +(y)d(an)g(appropriate)g Fi(RealFloat)f Fp(instance)j(of)d Fi(fromInteger)o +Fp(.)20 b(In)c(this)g(manner,)f(ev)o(en)0 2052 y(user-de\014ned)i(n)o(umeric) +f(t)o(yp)q(es)f(\(sa)o(y)l(,)f(quaternions\))i(can)f(mak)o(e)g(use)g(of)g(o)o +(v)o(erloaded)g(n)o(umerals.)71 2170 y(As)g(another)g(example,)g(recall)i +(our)d(\014rst)h(de\014nition)i(of)e Fi(succ)g Fp(from)f(Section)i(2:)71 +2279 y Fi(succ)23 b(::)g(Int)h(->)g(Int)71 2335 y(succ)f(n)h(=)f(n+1)0 +2444 y Fp(Ignoring)15 b(the)g(t)o(yp)q(e)f(signature,)g(the)h(most)f(general) +h(t)o(yp)q(e)f(of)g Fi(succ)g Fp(is)h Fi(\(Num)23 b(a\))h(=>)g(a->a)o +Fp(.)c(The)14 b(explicit)j(t)o(yp)q(e)0 2501 y(signature)h(is)g(legal,)h(ho)o +(w)o(ev)o(er,)e(since)i(it)f(is)g Fo(mor)n(e)g(sp)n(e)n(ci\014c)e +Fp(than)i(the)g(principal)i(t)o(yp)q(e)d(\(a)g(more)h(general)g(t)o(yp)q(e)0 +2557 y(signature)e(w)o(ould)g(cause)g(a)f(static)g(error\).)20 +b(The)c(t)o(yp)q(e)g(signature)g(has)f(the)h(e\013ect)f(of)h(restricting)g +Fi(succ)o Fp('s)f(t)o(yp)q(e,)0 2614 y(and)g(in)h(this)g(case)f(w)o(ould)h +(cause)f(something)h(lik)o(e)g Fi(succ)23 b(\(1::Float\))14 +b Fp(to)h(b)q(e)h(ill-t)o(yp)q(ed.)p eop +%%Page: 36 36 +bop 0 -40 a Fp(T-36)771 b Fj(5)45 b(TYPE)15 b(CLASSES,)h(O)o(VERLO)o(ADING,)g +(AND)f(\\OOP")0 105 y Fc(5.5.4)52 b(Default)18 b(Numeric)g(T)o(yp)q(es)0 +216 y Fp(Consider)e(the)f(follo)o(wing)h(function)g(de\014nition:)71 +325 y Fi(rms)500 b(::)24 b(\(Floating)f(a\))g(=>)h(a)f(->)h(a)g(->)f(a)71 +381 y(rms)g(x)h(y)405 b(=)48 b(sqrt)23 b(\(\(x^2)g(+)h(y^2\))f(*)h(0.5\))0 +493 y Fp(The)c(exp)q(onen)o(tiation)g(function)g Fi(\(^\))f +Fp(\(one)g(of)g(three)g(di\013eren)o(t)h(standard)f(exp)q(onen)o(tiation)h +(op)q(erators)f(with)0 549 y(di\013eren)o(t)13 b(t)o(ypings,)g(see)g +Fn(x)p Fp(6.8.5\))e(has)h(the)h(t)o(yp)q(e)g Fi(\(Num)23 b(a,)h(Integral)e +(b\))i(=>)g(a)f(->)h(b)g(->)f(a)p Fp(,)13 b(and)g(since)h Fi(2)e +Fp(has)0 606 y(the)k(t)o(yp)q(e)f Fi(\(Num)24 b(a\))f(=>)h(a)p +Fp(,)15 b(the)h(t)o(yp)q(e)g(of)f Fi(x^2)g Fp(is)h Fi(\(Num)23 +b(a,)h(Integral)f(b\))g(=>)h(a)p Fp(.)d(This)16 b(is)g(a)g(problem;)g(there)0 +662 y(is)g(no)g(w)o(a)o(y)e(to)i(resolv)o(e)f(the)h(o)o(v)o(erloading)g(asso) +q(ciated)g(with)g(the)g(t)o(yp)q(e)f(v)m(ariable)i Fi(b)p Fp(,)f(since)g(it)g +(is)h(in)f(the)g(con)o(text,)0 719 y(but)j(has)f(otherwise)h(v)m(anished)i +(from)d(the)g(t)o(yp)q(e)h(expression.)31 b(Essen)o(tially)l(,)21 +b(the)e(programmer)e(has)i(sp)q(eci\014ed)0 775 y(that)e Fi(x)h +Fp(should)g(b)q(e)h(squared,)f(but)f(has)h(not)f(sp)q(eci\014ed)j(whether)e +(it)g(should)h(b)q(e)f(squared)g(with)g(an)g Fi(Int)f Fp(or)g(an)0 +832 y Fi(Integer)d Fp(v)m(alue)j(of)d(t)o(w)o(o.)19 b(Of)c(course,)g(w)o(e)g +(can)g(\014x)h(this:)71 942 y Fi(rms)23 b(x)h(y)405 b(=)48 +b(sqrt)23 b(\(\(x)h(^)f(\(2::Int\))g(+)h(y)g(^)f(\(2::Int\)\))g(*)h(0.5\))0 +1051 y Fp(It's)15 b(ob)o(vious)g(that)g(this)g(sort)g(of)f(thing)i(will)h(so) +q(on)e(gro)o(w)f(tiresome,)h(ho)o(w)o(ev)o(er.)71 1132 y(In)g(fact,)g(this)g +(kind)i(of)d(o)o(v)o(erloading)i(am)o(biguit)o(y)f(is)h(not)f(restricted)g +(to)g(n)o(um)o(b)q(ers:)71 1241 y Fi(show)23 b(\(read)g("xyz"\))0 +1350 y Fp(As)d(what)f(t)o(yp)q(e)h(is)g(the)g(string)f(supp)q(osed)i(to)e(b)q +(e)h(read?)35 b(This)20 b(is)g(more)f(serious)h(than)g(the)g(exp)q(onen)o +(tiation)0 1407 y(am)o(biguit)o(y)l(,)14 b(b)q(ecause)h(there,)f(an)o(y)f +Fi(Integral)g Fp(instance)i(will)g(do,)f(whereas)g(here,)g(v)o(ery)f +(di\013eren)o(t)h(b)q(eha)o(vior)h(can)0 1463 y(b)q(e)h(exp)q(ected)g(dep)q +(ending)i(on)d(what)f(instance)i(of)f Fi(Text)g Fp(is)h(used)f(to)g(resolv)o +(e)g(the)h(am)o(biguit)o(y)l(.)71 1545 y(Because)h(of)f(the)g(di\013erence)i +(b)q(et)o(w)o(een)f(the)f(n)o(umeric)i(and)e(general)h(cases)g(of)f(the)g(o)o +(v)o(erloading)h(am)o(biguit)o(y)0 1601 y(problem,)24 b(Hask)o(ell)e(pro)o +(vides)g(a)g(solution)g(that)f(is)h(restricted)g(to)f(n)o(um)o(b)q(ers:)33 +b(Eac)o(h)22 b(mo)q(dule)h(ma)o(y)e(con)o(tain)0 1658 y(a)h +Fo(default)h(de)n(clar)n(ation,)g Fp(consisting)g(of)f(the)g(k)o(eyw)o(ord)g +Fi(default)f Fp(follo)o(w)o(ed)i(b)o(y)f(a)g(paren)o(thesized,)i(comma-)0 +1714 y(separated)17 b(list)h(of)f(n)o(umeric)h(monot)o(yp)q(es)f(\(t)o(yp)q +(es)h(with)f(no)h(v)m(ariables\).)27 b(When)18 b(an)f(am)o(bigous)g(t)o(yp)q +(e)h(v)m(ariable)0 1770 y(is)h(disco)o(v)o(ered)f(\(suc)o(h)h(as)e +Fi(b)p Fp(,)i(ab)q(o)o(v)o(e\),)f(if)g(at)g(least)g(one)g(of)g(its)h(classes) +f(is)h(n)o(umeric)g(and)f(all)h(of)f(its)h(classes)f(are)0 +1827 y(standard,)d(the)g(default)h(list)h(is)f(consulted,)g(and)f(the)h +(\014rst)f(t)o(yp)q(e)g(from)g(the)h(list)g(that)f(will)i(satisfy)e(the)h +(con)o(text)0 1883 y(of)g(the)h(t)o(yp)q(e)g(v)m(ariable)h(is)f(used.)25 +b(F)l(or)16 b(example,)h(if)h(the)e(default)i(declaration)f +Fi(default)23 b(\(Int,)g(Float\))16 b Fp(is)h(in)0 1940 y(e\013ect,)e(the)g +(am)o(biguous)g(exp)q(onen)o(t)h(ab)q(o)o(v)o(e)f(will)h(b)q(e)g(resolv)o(ed) +g(as)f(t)o(yp)q(e)g Fi(Int)p Fp(.)k(\(See)d Fn(x)p Fp(4.3.4)e(for)g(more)h +(details.\))71 2021 y(The)f(\\default)h(default")g(is)g Fi(\(Int,)23 +b(Double\))o Fp(,)15 b(but)f Fi(\(Integer,)23 b(Rational,)g(Double\))13 +b Fp(ma)o(y)h(also)h(b)q(e)g(ap-)0 2078 y(propriate.)20 b(V)l(ery)15 +b(cautious)h(programmers)e(ma)o(y)g(prefer)i Fi(default)22 +b(\(\))p Fp(,)15 b(whic)o(h)h(pro)o(vides)g(no)f(defaults.)p +eop +%%Page: 37 37 +bop 1857 -40 a Fp(T-37)0 105 y Fq(6)69 b(Mo)r(dules)0 237 y +Fp(A)o(t)14 b(the)h(top)f(lev)o(el,)i(a)f(Hask)o(ell)g(program)f(consists)h +(of)f(a)h(collection)h(of)f Fo(mo)n(dules)p Fp(.)k(A)c(mo)q(dule)h(in)g(Hask) +o(ell)f(serv)o(es)0 294 y(the)g(dual)h(purp)q(ose)g(of)f(con)o(trolling)h +(name-spaces)g(and)f(creating)g(abstract)g(data)f(t)o(yp)q(es.)71 +379 y(The)19 b(top)f(lev)o(el)i(of)f(a)g(mo)q(dule)h(con)o(tains)f(an)o(y)f +(of)h(the)g(v)m(arious)g(declarations)h(w)o(e)e(ha)o(v)o(e)h(discussed:)29 +b(\014xit)o(y)0 435 y(declarations,)15 b(data)f(and)h(t)o(yp)q(e)g +(declarations,)g(class)g(and)f(instance)i(declarations,)f(t)o(yp)q(e)g +(signatures,)f(function)0 492 y(de\014nitions,)k(and)e(pattern)f(bindings.)25 +b(Except)16 b(for)f(the)h(fact)g(that)f(\014xit)o(y)h(declarations)h(and)f +(imp)q(ort)g(declara-)0 548 y(tions)i(\(to)f(b)q(e)i(describ)q(ed)h +(shortly\))e(m)o(ust)f(app)q(ear)h(\014rst,)g(the)h(declarations)f(ma)o(y)g +(app)q(ear)g(in)h(an)o(y)e(order)h(\(the)0 605 y(top-lev)o(el)e(scop)q(e)g +(is)g(m)o(utually)g(recursiv)o(e\).)71 690 y(Hask)o(ell's)d(mo)q(dule)g +(design)h(is)f(relativ)o(ely)g(conserv)m(ativ)o(e:)19 b(The)13 +b(namespace)g(of)f(mo)q(dules)i(is)f(completely)h(\015at,)0 +746 y(and)h(mo)q(dules)i(are)d(in)j(no)e(w)o(a)o(y)f(\\\014rst-class.")19 +b(Mo)q(dule)d(names)g(are)e(alphan)o(umeric)j(and)e(m)o(ust)g(b)q(egin)i +(with)e(an)0 803 y(upp)q(ercase)g(letter.)20 b(There)14 b(is)h(no)f(formal)f +(connection)i(b)q(et)o(w)o(een)g(a)e(Hask)o(ell)i(mo)q(dule)h(and)e(the)g +(\014le)h(system)f(that)0 859 y(w)o(ould)i(\(t)o(ypically\))h(supp)q(ort)f +(it.)22 b(In)17 b(particular,)f(there)g(is)h(no)f(connection)h(b)q(et)o(w)o +(een)f(mo)q(dule)h(names)f(and)g(\014le)0 916 y(names,)g(and)g(more)g(than)g +(one)g(mo)q(dule)h(could)g(conceiv)m(ably)i(reside)e(in)g(a)f(single)h +(\014le)g(\(one)f(mo)q(dule)h(ma)o(y)f(ev)o(en)0 972 y(span)g(sev)o(eral)f +(\014les\).)22 b(Of)15 b(course,)h(a)f(particular)h(implemen)o(tation)h(will) +g(most)e(lik)o(ely)i(adopt)e(con)o(v)o(en)o(tions)h(that)0 +1029 y(mak)o(e)f(the)g(connection)h(b)q(et)o(w)o(een)g(mo)q(dules)g(and)f +(\014les)i(more)d(stringen)o(t.)71 1114 y(T)l(ec)o(hnically)h(sp)q(eaking,)f +(a)f(mo)q(dule)h(is)g(really)g(just)f(one)g(big)h(declaration)g(whic)o(h)g(b) +q(egins)g(with)g(the)f(k)o(eyw)o(ord)0 1170 y Fi(module)o Fp(;)i(here's)g(an) +g(example)h(for)f(a)g(mo)q(dule)h(whose)f(name)g(is)h Fi(Tree)p +Fp(:)71 1279 y Fi(module)23 b(Tree)g(\()h(Tree\(Leaf,Branch\),)d(fringe)i(\)) +h(where)71 1359 y(data)f(Tree)g(a)382 b(=)24 b(Leaf)f(a)h(|)f(Branch)g +(\(Tree)h(a\))f(\(Tree)g(a\))71 1439 y(fringe)g(::)g(Tree)h(a)f(->)h([a])71 +1495 y(fringe)f(\(Leaf)g(x\))286 b(=)24 b([x])71 1551 y(fringe)f(\(Branch)g +(left)g(right\))g(=)h(fringe)f(left)g(++)h(fringe)f(right)0 +1661 y Fp(The)14 b(t)o(yp)q(e)g Fi(Tree)f Fp(and)h(the)g(function)h +Fi(fringe)e Fp(should)i(b)q(e)g(familiar;)f(they)g(w)o(ere)g(giv)o(en)g(as)g +(examples)g(in)h(Section)0 1717 y(2.3.)29 b([Because)19 b(of)f(the)h +Fi(where)f Fp(k)o(eyw)o(ord,)g(la)o(y)o(out)g(is)h(activ)o(e)g(at)e(the)i +(top)f(lev)o(el)i(of)e(a)h(mo)q(dule,)h(and)e(th)o(us)h(the)0 +1773 y(declarations)f(m)o(ust)e(all)i(line)g(up)f(in)h(the)f(same)f(column)i +(\(t)o(ypically)g(the)f(\014rst\).)24 b(Also)17 b(note)g(that)f(the)h(mo)q +(dule)0 1830 y(name)e(is)h(the)f(same)g(as)g(that)f(of)h(the)g(t)o(yp)q(e;)g +(this)h(is)g(allo)o(w)o(ed.])71 1915 y(This)e(mo)q(dule)h(explicitly)i +Fo(exp)n(orts)d Fi(Tree)o Fp(,)g Fi(Leaf)o Fp(,)g Fi(Branch)o +Fp(,)g(and)g Fi(fringe)o Fp(.)20 b(If)14 b(the)g(exp)q(ort)g(list)g(follo)o +(wing)h(the)0 1972 y Fi(module)10 b Fp(k)o(eyw)o(ord)g(is)h(omitted,)g +Fo(al)r(l)g Fp(of)f(the)h(names)g(b)q(ound)h(at)e(the)h(top)f(lev)o(el)i(of)f +(the)f(mo)q(dule)i(w)o(ould)g(b)q(e)f(exp)q(orted.)0 2028 y(\(In)h(the)h(ab)q +(o)o(v)o(e)e(example)i(ev)o(erything)g(is)f(explicitly)j(exp)q(orted,)e(so)e +(the)i(e\013ect)e(w)o(ould)i(b)q(e)g(the)f(same.\))18 b(Note)12 +b(that)0 2084 y(the)k(name)f(of)g(a)h(t)o(yp)q(e)f(and)h(its)g(constructors)f +(m)o(ust)g(b)q(e)h(group)q(ed)g(together,)f(as)g(in)h Fi(Tree\(Leaf,Branch\)) +n Fp(.)21 b(As)0 2141 y(short-hand,)15 b(w)o(e)g(could)h(also)f(write)g +Fi(Tree\(..\))f Fp(\(exp)q(orting)i(a)f(subset)g(of)g(the)g(constructors)f +(is)i(not)f(allo)o(w)o(ed\).)71 2226 y(The)g Fi(Tree)g Fp(mo)q(dule)h(ma)o(y) +e(no)o(w)h(b)q(e)h Fo(imp)n(orte)n(d)g Fp(in)o(to)f(some)g(other)f(mo)q +(dule:)71 2335 y Fi(module)23 b(Main)g(\(main\))g(where)71 +2392 y(import)g(Tree)g(\()h(Tree\(Leaf,Branch\),)d(fringe)i(\))71 +2448 y(main)g(::)g(Dialogue)71 2504 y(main)g(=)h(print)f(\(fringe)g(\(Branch) +g(\(Leaf)g(1\))g(\(Leaf)h(2\)\)\))0 2614 y Fp(The)16 b(v)m(arious)g(items)g +(b)q(eing)g(imp)q(orted)g(in)o(to)g(and)g(exp)q(orted)g(out)f(of)g(a)g(mo)q +(dule)i(are)e(called)i Fo(entities)p Fp(.)j(Note)15 b(the)p +eop +%%Page: 38 38 +bop 0 -40 a Fp(T-38)1557 b Fj(6)46 b(MODULES)0 105 y Fp(explicit)21 +b(imp)q(ort)d(list)h(in)h(the)e(imp)q(ort)h(declaration;)h(omitting)f(it)g(w) +o(ould)g(cause)f(all)i(en)o(tities)f(exp)q(orted)g(from)0 162 +y Fi(Tree)c Fp(to)f(b)q(e)i(imp)q(orted.)71 244 y(The)c(en)o(tit)o(y)h +Fi(main)e Fp(\(and)i(its)f(t)o(yping\))h(and)f(the)h(mo)q(dule)g(name)g +Fi(Main)o Fp(,)g(in)g(the)g(ab)q(o)o(v)o(e)f(example,)h(ha)o(v)o(e)f(sp)q +(ecial)0 300 y(signi\014cance,)17 b(as)e(sp)q(eci\014ed)i(in)f(the)f(Rep)q +(ort:)71 383 y(\\A)d(Hask)o(ell)i Fo(pr)n(o)n(gr)n(am)g Fp(is)f(a)g +(collection)i(of)d(mo)q(dules,)j(one)e(of)f(whic)o(h,)i(b)o(y)f(con)o(v)o(en) +o(tion,)h(m)o(ust)e(b)q(e)i(called)h Fi(Main)0 439 y Fp(and)j(m)o(ust)f(exp)q +(ort)g(the)h(v)m(alue)h Fi(main)o Fp(.)27 b(The)18 b Fo(value)g +Fp(of)f(the)h(program)e(is)i(the)g(v)m(alue)h(of)e(the)h(iden)o(ti\014er)h +Fi(main)e Fp(in)0 496 y(mo)q(dule)f Fi(Main)p Fp(,)f(and)g +Fi(main)g Fp(m)o(ust)f(ha)o(v)o(e)h(t)o(yp)q(e)g Fi(Dialogue)o +Fp(.")71 578 y(\(The)e(t)o(yp)q(e)g Fi(Dialogue)f Fp(relates)h(to)g(I/O,)g +(and)h(is)f(discussed)i(in)f(Section)g(8.\))19 b(Th)o(us)13 +b(the)g(ab)q(o)o(v)o(e)g(t)o(w)o(o)f(mo)q(dules)0 634 y(together)i +(constitute)i(a)f(v)m(alid)i(Hask)o(ell)f(program.)0 784 y +Fg(6.1)56 b(Original)17 b(Names)h(and)h(Renaming)0 896 y Fp(Abstractly)d +(asso)q(ciated)h(with)g(ev)o(ery)f(en)o(tit)o(y)g(is)h(its)g +Fo(original)g(name)p Fp(|a)f(pair)h(consisting)g(of)f(the)g(name)h(of)f(the)0 +953 y(en)o(tit)o(y)d(and)h(the)f(name)g(of)g(the)h(mo)q(dule)g(in)h(whic)o(h) +f(it)f(w)o(as)g(originally)i(de\014ned.)20 b(This)14 b(concept)g(is)g(useful) +g(in)h(that)0 1009 y(it)f(allo)o(ws)g(an)f(en)o(tit)o(y)h(with)g(the)g(same)f +(name)h(to)f(b)q(e)h(imp)q(orted)g(from)f(a)g(v)m(ariet)o(y)h(of)f +(di\013eren)o(t)h(mo)q(dules,)h(without)0 1066 y(con\015ict,)h(as)f(long)g +(as)g(the)g(original)h(name)g(is)f(the)h(same.)71 1148 y(En)o(tities)f(b)q +(eing)i(imp)q(orted)f(can)f(also)g(b)q(e)h Fo(r)n(ename)n(d)p +Fp(;)e(for)h(example:)71 1257 y Fi(import)23 b(Tree)g(\()h +(Tree\(Leaf,Branch\),)d(fringe)i(\))238 1313 y(renaming)f(\(Leaf)i(to)f +(Root,)g(Branch)g(to)h(Twig\))0 1541 y Fg(6.2)56 b(In)n(terfaces)18 +b(and)h(Impleme)o(n)n(tations)0 1653 y Fp(The)d(mo)q(dules)h +Fi(Tree)e Fp(and)g Fi(Main)g Fp(sho)o(wn)h(ab)q(o)o(v)o(e)f(are)g(in)i +(actualit)o(y)f(mo)q(dule)g Fo(implementations)p Fp(|they)g(con)o(tain)0 +1709 y(all)j(of)e(the)h(Hask)o(ell)g(co)q(de)g(to)g(completely)h(de\014ne)f +(a)g(mo)q(dule.)28 b(The)18 b(information)g(passed)g(b)q(et)o(w)o(een)g(mo)q +(dules)0 1766 y(during)f(exp)q(ort)e(and)h(imp)q(ort,)g(ho)o(w)o(ev)o(er,)f +(is)h(more)f(abstract,)g(and)h(is)g(captured)g(formally)g(in)h(a)e(mo)q(dule) +i Fo(inter-)0 1822 y(fac)n(e)p Fp(.)i(The)c(in)o(terface)g(for)f(a)g(mo)q +(dule)i Fi(M)e Fp(con)o(tains)h(all)g(of)f(the)h(information)f(ab)q(out)h +(the)f(en)o(tities)i(exp)q(orted)f(from)0 1879 y Fi(M)h Fp(that)f(is)i +(needed)h(to)d(ensure)i(prop)q(er)f(t)o(yping)h(of)e(some)h(other)g(mo)q +(dule)h Fi(N)f Fp(that)g(ma)o(y)f(imp)q(ort)i Fi(M)p Fp(.)22 +b(The)17 b(prop)q(er)0 1935 y(in)o(terfaces)e(for)g(the)g(mo)q(dules)i +Fi(Tree)d Fp(and)i Fi(Main)e Fp(are:)71 2035 y Fi(interface)22 +b(Tree)i(\()f(Tree\(Leaf,Branch\),)f(fringe)h(\))g(where)71 +2092 y(data)g(Tree)g(a)h(=)g(Leaf)f(a)h(|)f(Branch)g(\(Tree)h(a\))f(\(Tree)g +(a\))71 2148 y(fringe)g(::)g(Tree)h(a)f(->)h([a])71 2228 y(interface)e(Main)i +(\(main\))f(where)71 2284 y(import)g(Tree)g(\()h(Tree\(Leaf,Branch\),)d +(fringe)i(\))71 2341 y(main)g(::)g(Dialogue)71 2501 y Fp(Normally)12 +b(the)f(user)h(need)g(not)f(b)q(e)i(concerned)f(ab)q(out)g(mo)q(dule)g(in)o +(terfaces,)g(since)h(they)f(are)f(usually)i(deriv)o(ed)0 2557 +y(automatically)k(from)f(the)h(mo)q(dule)h(implemen)o(tation.)27 +b(On)17 b(the)g(other)f(hand,)i(it)f(is)g(not)g(un)o(usual)h(to)e(w)o(an)o(t) +g(to)0 2614 y(statically)f(debug)h(a)e(mo)q(dule)i(implemen)o(tation)g(that)e +(imp)q(orts)h(some)f(other)g(mo)q(dule)i(whose)f(implemen)o(tation)p +eop +%%Page: 39 39 +bop 0 -40 a Fj(6.3)45 b(Abstract)14 b(Data)g(T)o(yp)q(es)1332 +b Fp(T-39)0 105 y(do)q(es)21 b(not)g(exist)g(y)o(et;)i(b)q(eing)f(able)g(to)e +(explicitly)k(write)d(the)g(in)o(terface)g(for)g(this)g(imp)q(orted)g(mo)q +(dule)i(w)o(ould)0 162 y(p)q(ermit)f(this.)41 b(Indeed,)24 +b(in)o(terfaces)e(supp)q(ort)g(top-do)o(wn,)h(successiv)o(e)g(re\014nemen)o +(t,)g(soft)o(w)o(are)d(dev)o(elopmen)o(t)0 218 y(metho)q(dologies,)c(in)g +(whic)o(h)g Fo(al)r(l)f Fp(in)o(terfaces)g(are)g(ideally)i(written)e +Fo(\014rst)p Fp(.)0 493 y Fg(6.3)56 b(Abstract)19 b(Data)f(T)n(yp)r(es)0 +661 y Fp(Aside)j(from)f(con)o(trolling)h(namespaces,)g(mo)q(dules)g(pro)o +(vide)g(the)f(only)h(w)o(a)o(y)e(to)g(build)j(abstract)d(data)h(t)o(yp)q(es)0 +717 y(\(ADTs\))c(in)i(Hask)o(ell.)27 b(F)l(or)17 b(example,)h(the)g(c)o +(haracteristic)f(feature)g(of)g(an)g(ADT)g(is)h(that)f(the)g +Fo(r)n(epr)n(esentation)0 774 y(typ)n(e)i Fp(is)h Fo(hidden)p +Fp(;)h(all)f(op)q(erations)f(on)g(the)g(ADT)g(are)g(done)h(at)e(an)h +(abstract)f(lev)o(el)j(whic)o(h)f(do)q(es)g(not)e(dep)q(end)0 +830 y(on)f(the)h(represen)o(tation.)26 b(F)l(or)16 b(example,)j(although)e +(the)g Fi(Tree)g Fp(t)o(yp)q(e)g(is)h(simple)h(enough)f(that)e(w)o(e)h(migh)o +(t)g(not)0 886 y(normally)e(mak)o(e)g(it)g(abstract,)e(a)h(suitable)i(ADT)f +(for)f(it)h(migh)o(t)g(include)i(the)e(follo)o(wing)g(op)q(erations,)g(whic)o +(h)g(w)o(e)0 943 y(sp)q(ecify)i(in)f(the)f(form)f(of)h(an)g(in)o(terface:)71 +1052 y Fi(interface)22 b(TreeADT)h(\(Tree,)g(leaf,)g(branch,)g(cell,)524 +1108 y(left,)g(right,)g(isLeaf,)g(isBranch\))g(where)71 1165 +y(data)g(Tree)g(a)71 1221 y(leaf)476 b(::)24 b(a)g(->)f(Tree)h(a)71 +1278 y(branch)428 b(::)24 b(Tree)f(a)h(->)g(Tree)f(a)h(->)f(Tree)g(a)71 +1334 y(cell)476 b(::)24 b(Tree)f(a)h(->)g(a)71 1391 y(left,)f(right)309 +b(::)24 b(Tree)f(a)h(->)g(Tree)f(a)71 1447 y(isLeaf)428 b(::)24 +b(Tree)f(a)h(->)g(Bool)0 1556 y Fp(A)15 b(mo)q(dule)i(implemen)o(tation)f +(supp)q(orting)g(this)g(is:)71 1665 y Fi(module)23 b(TreeADT)g(\(Tree,)g +(leaf,)g(branch,)g(cell,)452 1722 y(left,)h(right,)f(isLeaf\))g(where)71 +1801 y(data)g(Tree)g(a)310 b(=)24 b(Leaf)f(a)h(|)g(Branch)f(\(Tree)g(a\))h +(\(Tree)f(a\))71 1881 y(leaf)476 b(=)24 b(Leaf)71 1937 y(branch)428 +b(=)24 b(Branch)71 1994 y(cell)47 b(\(Leaf)23 b(a\))238 b(=)24 +b(a)71 2050 y(left)47 b(\(Branch)23 b(l)g(r\))143 b(=)24 b(l)71 +2107 y(right)f(\(Branch)g(l)g(r\))143 b(=)24 b(r)71 2163 y(isLeaf)70 +b(\(Leaf)24 b(_\))166 b(=)24 b(True)71 2220 y(isLeaf)70 b(_)334 +b(=)24 b(False)0 2331 y Fp(Note)d(in)h(the)g(exp)q(ort)f(list)i(that)d(the)i +(t)o(yp)q(e)f(name)h Fi(Tree)f Fp(app)q(ears)g(alone)h(\(i.e.)f(without)h +(its)g(constructors\);)0 2388 y(similarly)l(,)e(in)f(the)e(in)o(terface)h +(the)g Fi(data)f Fp(declaration)i(for)e Fi(Tree)g Fp(do)q(es)h(not)f(include) +k(the)c(constructors.)27 b(Th)o(us)0 2444 y Fi(Leaf)17 b Fp(and)h +Fi(Branch)f Fp(are)h(not)f(exp)q(orted,)h(and)g(the)g(only)h(w)o(a)o(y)d(to)h +(build)j(or)e(tak)o(e)f(apart)g(trees)g(outside)i(of)e(the)0 +2501 y(mo)q(dule)f(is)f(b)o(y)g(using)h(the)f(v)m(arious)g(\(abstract\))e(op) +q(erations.)20 b(Of)15 b(course,)g(the)g(adv)m(an)o(tage)f(of)h(this)g +(information)0 2557 y(hiding)20 b(is)e(that)f(at)h(a)g(later)g(time)g(w)o(e)g +(could)h Fo(change)e Fp(the)h(represen)o(tation)g(t)o(yp)q(e)g(without)g +(a\013ecting)g(users)g(of)0 2614 y(the)d(t)o(yp)q(e.)p eop +%%Page: 40 40 +bop 0 -40 a Fp(T-40)1368 b Fj(7)45 b(TYPING)15 b(PITF)-5 b(ALLS)0 +105 y Fg(6.4)56 b(Rules,)17 b(Rules,)g(and)j(More)e(Rules)0 +227 y Fp(Although)f(Hask)o(ell's)h(mo)q(dule)g(system)e(is)i(relativ)o(ely)g +(conserv)m(ativ)o(e,)f(there)g(are)g(man)o(y)f(rules)i(concerning)g(the)0 +283 y(imp)q(ort)g(and)g(exp)q(ort)g(of)g(v)m(alues.)29 b(Most)17 +b(of)g(these)h(are)g(ob)o(vious|for)g(instance,)h(it)f(is)h(illegal)h(to)d +(imp)q(ort)h(t)o(w)o(o)0 340 y(di\013eren)o(t)f(en)o(tities)h(ha)o(ving)f +(the)f(same)h(name)f(in)o(to)h(the)g(same)f(scop)q(e.)25 b(Other)17 +b(rules)h(are)e(not)h(so)f(ob)o(vious|for)0 396 y(example,)f(an)e +Fi(instance)g Fp(declaration)i(can)f(only)g(app)q(ear)g(in)h(the)f(mo)q(dule) +g(in)h(whic)o(h)g(the)f(corresp)q(onding)h Fi(data)0 453 y +Fp(or)g Fi(class)f Fp(declaration)i(app)q(ears.)k(The)c(reader)f(should)h +(read)f(the)g(Rep)q(ort)h(for)e(details)j(\()p Fn(x)o Fp(5\).)0 +646 y Fq(7)69 b(T)n(yping)23 b(Pitfalls)0 783 y Fp(This)c(short)e(section)i +(giv)o(e)f(an)g(in)o(tuitiv)o(e)i(description)f(of)f(a)g(few)g(common)g +(problems)g(that)g(no)o(vices)h(run)f(in)o(to)0 840 y(using)e(Hask)o(ell's)g +(t)o(yp)q(e)f(system.)0 1011 y Fg(7.1)56 b(Let-Bound)17 b(P)n(olymorphism)0 +1133 y Fp(An)o(y)i(language)h(using)g(the)f(Hindley-Milne)q(r)j(t)o(yp)q(e)d +(system)g(has)g(what)f(is)i(called)h Fo(let-b)n(ound)f(p)n(olymorphism)p +Fp(,)0 1189 y(b)q(ecause)g(iden)o(ti\014ers)g(not)e(b)q(ound)i(using)g(a)e +Fi(let)g Fp(or)h Fi(where)f Fp(clause)h(\(or)f(at)g(the)h(top)g(lev)o(el)h +(of)e(a)h(mo)q(dule\))g(are)0 1246 y(limited)14 b(with)e(resp)q(ect)h(to)e +(their)h(p)q(olymorphism.)20 b(In)13 b(particular,)g(a)f Fo(lamb)n(da-b)n +(ound)g Fp(function)h(\(i.e.,)f(one)g(passed)0 1302 y(as)k(argumen)o(t)g(to)f +(another)h(function\))h(cannot)f(b)q(e)h(instan)o(tiated)g(in)g(t)o(w)o(o)e +(di\013eren)o(t)i(w)o(a)o(ys.)22 b(F)l(or)15 b(example,)j(this)0 +1359 y(program)c(is)i(illegal:)71 1468 y Fi(let)23 b(f)h(g)47 +b(=)h(\(g)24 b([],)f(g)h('a'\))548 b(--)24 b(ill-typed)e(expression)71 +1524 y(in)h(f)h(\(\\x->x\))0 1633 y Fp(b)q(ecause)19 b Fi(g)p +Fp(,)f(b)q(ound)h(to)e(a)h(lam)o(b)q(da)g(abstraction)g(whose)g(principal)i +(t)o(yp)q(e)e(is)h Fi(a->a)o Fp(,)f(is)h(used)f(within)i Fi(f)d +Fp(in)i(t)o(w)o(o)0 1690 y(di\013eren)o(t)c(w)o(a)o(ys:)k(once)d(with)f(t)o +(yp)q(e)h Fi([a]->[a])n Fp(,)f(and)h(once)f(with)h(t)o(yp)q(e)f +Fi(Char->Char)o Fp(.)0 1861 y Fg(7.2)56 b(Numeric)16 b(Ov)n(erloading)0 +1983 y Fp(It)f(is)g(easy)g(to)f(forget)g(at)g(times)h(that)g(n)o(umerals)g +(are)f Fo(overlo)n(ade)n(d,)h Fp(and)g Fo(not)h(implicitly)g(c)n(o)n(er)n(c)n +(e)n(d)d Fp(to)i(the)f(v)m(arious)0 2040 y(n)o(umeric)k(t)o(yp)q(es,)g(as)e +(in)j(man)o(y)d(other)h(languages.)26 b(More)17 b(general)h(n)o(umeric)g +(expressions)g(sometimes)f(cannot)0 2096 y(b)q(e)f(quite)g(so)f(generic.)20 +b(A)c(common)e(n)o(umeric)j(t)o(yping)e(error)f(is)i(something)g(lik)o(e)g +(the)f(follo)o(wing:)71 2205 y Fi(average)22 b(xs)334 b(=)48 +b(sum)23 b(xs)h(/)g(length)f(xs)262 b(--)24 b(Wrong!)0 2314 +y(\(/\))c Fp(requires)h(fractional)f(argumen)o(ts,)g(but)h +Fi(length)o Fp('s)f(result)g(is)h(an)f Fi(Int)p Fp(.)35 b(The)20 +b(t)o(yp)q(e)h(mismatc)o(h)f(m)o(ust)f(b)q(e)0 2371 y(corrected)c(with)h(an)f +(explicit)i(co)q(ercion:)71 2480 y Fi(average)404 b(::)24 b(\(Fractional)e +(a\))i(=>)f([a])h(->)f(a)71 2536 y(average)f(xs)334 b(=)48 +b(sum)23 b(xs)h(/)g(fromIntegral)e(\(length)h(xs\))p eop +%%Page: 41 41 +bop 0 -40 a Fj(7.3)45 b(The)15 b(Monomorphism)g(Restriction)1118 +b Fp(T-41)0 105 y Fg(7.3)56 b(The)18 b(Monomorphism)e(Restriction)0 +213 y Fp(The)e(Hask)o(ell)h(t)o(yp)q(e)g(system)e(con)o(tains)i(a)f +(restriction)g(related)h(to)e(t)o(yp)q(e)i(classes)f(that)g(is)h(not)e(found) +i(in)g(ordinary)0 269 y(Hindley-Milne)q(r)23 b(t)o(yp)q(e)d(systems:)29 +b(the)20 b Fo(monomorphism)i(r)n(estriction)p Fp(.)34 b(The)21 +b(reason)f(for)f(this)i(restriction)f(is)0 326 y(related)c(to)e(a)h(subtle)h +(t)o(yp)q(e)f(am)o(biguit)o(y)g(and)h(is)f(explained)j(in)d(full)i(detail)f +(in)g(the)f(Rep)q(ort)h(\()p Fn(x)p Fp(4.5.4\).)i(A)d(simpler)0 +382 y(explanation)h(follo)o(ws:)71 461 y(The)g(monomorphism)f(restriction)i +(sa)o(ys)e(that)g(an)o(y)g(iden)o(ti\014er)j(b)q(ound)e(b)o(y)g(a)g(pattern)f +(binding)j(\(whic)o(h)e(in-)0 517 y(cludes)e(bindings)g(to)e(a)g(single)i +(iden)o(ti\014er\),)g(and)e(ha)o(ving)h(no)f(explicit)j(t)o(yp)q(e)e +(signature,)f(m)o(ust)g(b)q(e)h Fo(monomorphic)p Fp(.)0 574 +y(An)h(iden)o(ti\014er)h(is)f(monomorphic)g(if)f(is)h(either)h(not)e(o)o(v)o +(erloaded,)g(or)g(is)h(o)o(v)o(erloaded)f(but)h(is)g(used)g(in)g(at)f(most)g +(one)0 630 y(sp)q(eci\014c)k(o)o(v)o(erloading)e(and)h(is)f(not)g(exp)q +(orted.)71 709 y(Violations)e(of)e(this)i(restriction)g(result)f(in)h(a)f +(static)g(t)o(yp)q(e)h(error.)18 b(The)12 b(simplest)h(w)o(a)o(y)f(to)f(a)o +(v)o(oid)h(the)g(problem)0 765 y(is)18 b(to)g(pro)o(vide)g(an)g(explicit)i(t) +o(yp)q(e)e(signature.)28 b(Note)17 b(that)g Fo(any)h Fp(t)o(yp)q(e)g +(signature)g(will)h(do)f(\(as)f(long)h(it)h(is)f(t)o(yp)q(e)0 +822 y(correct\).)71 900 y(A)12 b(common)h(violation)g(of)g(the)f(restriction) +i(happ)q(ens)f(with)g(functions)h(de\014ned)g(in)f(a)g(higher-order)g +(manner,)0 957 y(as)i(in)h(this)f(de\014nition)i(of)e Fi(sum)g +Fp(from)f(the)i(Standard)f(Prelude:)71 1057 y Fi(sum)500 b(=)48 +b(foldl)23 b(\(+\))h(0)0 1166 y Fp(As)15 b(is,)h(this)f(w)o(ould)h(cause)f(a) +g(static)g(t)o(yp)q(e)g(error.)k(W)l(e)d(can)f(\014x)h(the)f(problem)h(b)o(y) +f(adding)h(the)f(t)o(yp)q(e)g(signature:)71 1275 y Fi(sum)500 +b(::)24 b(\(Num)f(a\))h(=>)f([a])h(->)f(a)0 1384 y Fp(Also)16 +b(note)f(that)f(this)i(problem)g(w)o(ould)f(not)g(ha)o(v)o(e)g(arisen)g(if)h +(w)o(e)f(had)g(written:)71 1493 y Fi(sum)23 b(xs)429 b(=)48 +b(foldl)23 b(\(+\))h(0)f(xs)0 1602 y Fp(b)q(ecause)16 b(the)f(restriction)h +(only)g(applies)h(to)d(pattern)h(bindings.)0 1766 y Fq(8)69 +b(Input/Output)0 1890 y Fp(The)17 b(I/O)h(system)e(in)i(Hask)o(ell)g(is)g +(purely)g(functional,)g(y)o(et)f(has)g(all)h(the)f(expressiv)o(e)h(p)q(o)o(w) +o(er)f(of)f(that)h(found)g(in)0 1946 y(con)o(v)o(en)o(tional)h(programming)f +(languages.)27 b(T)l(o)18 b(ac)o(hiev)o(e)g(this,)g(Hask)o(ell)g(relies)h +(critically)h(on)e(lazy)g(ev)m(aluation)0 2002 y(and)d(higher-order)h +(functions,)g(the)f(k)o(ey)g(building)j(blo)q(c)o(ks)e(of)f(an)o(y)g +(functional)h(program.)j(\()p Fn(x)p Fp(7\))71 2081 y(The)13 +b(Rep)q(ort)h(describ)q(es)i(t)o(w)o(o)c(equiv)m(alen)o(t)j(w)o(a)o(ys)e(to)g +(do)h(I/O)g(in)g(Hask)o(ell:)20 b(the)14 b Fo(str)n(e)n(am-b)n(ase)n(d)e +Fp(approac)o(h)i(and)0 2137 y(the)f Fo(c)n(ontinuation-b)n(ase)n(d)e +Fp(approac)o(h.)19 b(The)13 b(former)f(is)h(probably)g(easier)g(to)f(explain) +j(conceptually)l(,)f(and)f(indeed)0 2194 y(the)19 b(latter)f(is)h(de\014ned)h +(in)f(terms)f(of)g(the)h(former)f(in)h(the)g(Rep)q(ort.)30 +b(Ho)o(w)o(ev)o(er,)18 b(for)g(practical)h(programming,)0 2250 +y(the)g(latter)f(\(con)o(tin)o(uation-based\))h(approac)o(h)g(is)g(the)g +(preferred)g(metho)q(dology)l(,)g(and)g(that)f(is)i(what)e(w)o(e)g(will)0 +2307 y(concen)o(trate)d(on)g(in)h(this)g(section.)k(\()p Fn(x)p +Fp(7.5\))0 2449 y Fg(8.1)56 b(In)n(tro)r(duction)18 b(to)g(Con)n(tin)n +(uations)0 2557 y Fp(T)l(o)13 b(understand)g(this)h(metho)q(dology)l(,)f(it)g +(is)g(helpful)i(to)e(ha)o(v)o(e)f(some)h(understanding)h(of)e(the)h(notion)h +(of)e(a)h Fo(c)n(ontin-)0 2614 y(uation)p Fp(.)20 b(A)13 b(con)o(tin)o +(uation)h(is)f(basically)i(a)e(\(p)q(ossibly)i(n)o(ullary\))f +Fo(function)e Fp(that)h(maps)g(an)g(\\in)o(termediate)h(v)m(alue")p +eop +%%Page: 42 42 +bop 0 -40 a Fp(T-42)1417 b Fj(8)45 b(INPUT/OUTPUT)0 105 y Fp(\(p)q(ossibly)20 +b(empt)o(y\))e(to)g(\\the)g(rest)g(of)h(the)f(program.")29 +b(In)19 b(this)g(w)o(a)o(y)l(,)g(con)o(tin)o(uations)g(are)f(used)h(to)f +(explicitly)0 162 y(manage)d(\\\015o)o(w)h(of)f(con)o(trol,")h(and)g(th)o(us) +g(w)o(e)f(tend)i(to)e(de\014ne)i(functions)g(that,)e(instead)i(of)e +(returning)i(with)f(an)0 218 y(answ)o(er,)g(will)j(apply)e(a)f(con)o(tin)o +(uation)i(\(passed)e(in)i(as)e(an)h(argumen)o(t\))e(to)h(the)h(answ)o(er.)24 +b(Because)17 b(of)g(this,)g(the)0 274 y(resulting)f(programming)f(st)o(yle)g +(is)h(often)f(called)i Fo(c)n(ontinuation)e(p)n(assing)g(style)p +Fp(,)f(or)h(CPS)g(for)g(short.)71 359 y(T)l(o)e(giv)o(e)h(an)g(example)g(of)f +(this)i(idea)f(without)g(reference)g(to)f(I/O,)h(let's)g(consider)g(the)g +(use)g(of)g(con)o(tin)o(uations)0 416 y(to)i(manage)g(errors|i.e.,)g(to)f +(create)i(the)f(e\013ect)g(of)g(a)g(non-lo)q(cal)i(exit,)f(or)f(call)h(to)f +(an)g(error-handler.)24 b(A)17 b(con-)0 472 y(v)o(en)o(tional)f(w)o(a)o(y)e +(of)h(handling)i(error)d(v)m(alues)i(in)g(Hask)o(ell)g(migh)o(t)g(b)q(e)f +(the)h(follo)o(wing:)71 581 y Fi(data)23 b(Maybe)g(a)286 b(=)24 +b(Ok)g(a)f(|)h(Oops)f(String)71 638 y(f)548 b(::)24 b(Int)f(->)h(Maybe)f(Int) +71 694 y(f)g(x)501 b(=)24 b(let)f(y)h(=)g(...)691 751 y(in)47 +b(if)24 b(y==0)47 b(then)g(Oops)24 b("divide)f(by)g(zero")786 +807 y(else)48 b(Ok)23 b(\(x/y\))0 916 y Fp(Here)c(the)g Fi(Maybe)f +Fp(t)o(yp)q(e)h(is)g(used)g(to)f(enco)q(de)i(the)f(p)q(ossibilit)o(y)i(of)d +(error)g(in)o(v)o(olving)i(a)f(particular)g(t)o(yp)q(e.)31 +b(No)o(w)0 973 y(supp)q(ose)14 b Fi(f)f Fp(is)h(used)g(in)g(con)o(text)e +(somewhere;)i(for)f(example,)h(supp)q(ose)g(when)f(the)h(divide-b)o(y-zero)h +(error)e(o)q(ccurs)0 1029 y(there)i(is)h(some)f(default)h(v)m(alue)g +Fi(d)f Fp(that)g(is)g(appropriate)g(to)g(use)h(instead:)118 +1138 y Fi(case)24 b(f)f(z)h(of)166 1195 y(Ok)g(x)71 b(->)24 +b(x)166 1251 y(Oops)f(s)h(->)g(d)71 1414 y Fp(In)16 b(con)o(trast)f(to)h +(this,)h(an)f(approac)o(h)g(to)f(handling)j(errors)e(based)g(on)h(explicit)h +(con)o(tin)o(uations)f(migh)o(t)f(lo)q(ok)0 1470 y(something)f(lik)o(e)i +(this:)71 1579 y Fi(f)548 b(::)24 b(Int)f(->)h(\(String)f(->)g(Int\))h(->)f +(Int)71 1635 y(f)g(x)h(c)453 b(=)24 b(let)f(y)h(=)g(...)691 +1692 y(in)47 b(if)24 b(x==0)47 b(then)g(c)24 b("divide)f(by)h(zero")786 +1748 y(else)48 b(x/y)0 1864 y Fp(Note)17 b(that)f(an)h(error)f(con)o(tin)o +(uation)i Fi(c)f Fp(is)g(explicitly)j(supplied)g(to)c Fi(f)h +Fp(as)f(an)h(argumen)o(t.)1546 1847 y Fm(14)1608 1864 y Fp(No)o(w,)g(to)f +(sim)o(ulate)0 1920 y(the)f(ab)q(o)o(v)o(e)g Fo(use)g Fp(of)g +Fi(f)p Fp(,)g(w)o(e)f(w)o(ould)i(simply)g(write:)118 2029 y +Fi(f)24 b(z)g(\(\\s)f(->)h(d\))71 2192 y Fp(The)12 b(trade-o\013s)f(b)q(et)o +(w)o(een)h(con)o(v)o(en)o(tional)h(and)f(con)o(tin)o(uation-based)h(approac)o +(hes)f(to)f(handling)j(suc)o(h)f(things)0 2248 y(as)i(errors)f(are)h(sub)s +(jectiv)o(e.)20 b(The)c(passing)f(of)g(con)o(tin)o(uations)g(can)h(b)q(e)f +(cum)o(b)q(ersome,)h(but)f(on)g(the)g(other)g(hand,)p 0 2295 +780 2 v 37 2322 a Fl(14)69 2338 y Fk(As)h(an)h(aside,)h(w)o(e)e(p)q(oin)o(t)h +(out)g(that)g(an)f(ev)o(en)h(b)q(etter)g(solution)h(w)o(ould)g(exist)f(if)f +(the)h(primitiv)o(e)h(arithmetic)g(functions)g(to)q(ok)0 2383 +y(con)o(tin)o(uation)e(argumen)o(ts)e(to)q(o!)j(If)12 b Ff(divide)f +Fk(w)o(ere)h(suc)o(h)i(a)f(function)h(for)f Ff(/)g Fk(then)g(w)o(e)g(could)h +(simply)h(write:)71 2472 y Ff(f)k(x)g(c)372 b(=)20 b(let)e(y)h(=)g(...)581 +2518 y(in)38 b(divide)17 b(x)j(y)f(c)p eop +%%Page: 43 43 +bop 0 -40 a Fj(8.2)45 b(Con)o(tin)o(uation)15 b(Based)g(I/O)1271 +b Fp(T-43)0 105 y(the)12 b(enco)q(ding)g(of)f(error)g(in)o(to)h(sp)q(ecial)h +(t)o(yp)q(es)e(is)h(also)g(cum)o(b)q(ersome.)18 b(The)12 b(programmer)e(m)o +(ust)h(mak)o(e)g(a)g(judicious)0 162 y(c)o(hoice)16 b(based)g(on)f(her)g +(particular)h(circumstances.)71 259 y(Before)e(mo)o(ving)g(on)g(to)f(I/O,)h +(ho)o(w)o(ev)o(er,)f(w)o(e)h(p)q(oin)o(t)g(out)g(one)g(\014nal)h(adv)m(an)o +(tage)f(of)f(con)o(tin)o(uations)i(b)o(y)f(noting)0 316 y(that)g(if)h +Fi(f)g Fp(w)o(ere)f Fo(r)n(e)n(cursive)p Fp(,)g(the)h(error)f(v)m(alue)i(in)f +(the)g(con)o(v)o(en)o(tional)g(approac)o(h)f(migh)o(t)h(ha)o(v)o(e)f(to)g(b)q +(e)i(propagated)0 372 y(bac)o(k)c(through)f(ev)o(ery)h(lev)o(el)i(of)d +(recursion,)i(whereas)f(in)h(the)f(con)o(tin)o(uation-based)h(approac)o(h,)e +(the)i(con)o(tin)o(uation)0 429 y(is)f(called)h(directly)l(,)g(m)o(uc)o(h)f +(lik)o(e)h(a)e(non-lo)q(cal)i(exit)f(to)e(an)i(error)f(handler!)19 +b(This)12 b(ma)o(y)f(ha)o(v)o(e)g(an)h(imp)q(ortan)o(t)f(impact)0 +485 y(on)k(e\016ciency)l(.)0 688 y Fg(8.2)56 b(Con)n(tin)n(uation)19 +b(Based)g(I/O)0 823 y Fp(Giv)o(en)12 b(this)f(bac)o(kground,)h(understanding) +g(con)o(tin)o(uation-based)g(I/O)g(in)g(Hask)o(ell)g(should)g(b)q(e)f +(straigh)o(tforw)o(ard;)0 879 y(there)k(are)g(only)h(t)o(w)o(o)e(small)i(t)o +(wists.)71 977 y(First,)11 b(a)h(program)f(engaged)h(in)h(I/O)f(\\comm)o +(unicates")g(to)f(the)h(outside)h(w)o(orld)f(\(nominally)l(,)i(the)e(op)q +(erating)0 1033 y(system\))19 b(using)h(con)o(tin)o(uations.)32 +b(A)20 b(Hask)o(ell)g(program)e(engaged)i(in)g(I/O)g(is)g(required)g(to)f(ha) +o(v)o(e)g(a)g(top-lev)o(el)0 1090 y(iden)o(ti\014er)j Fi(main)d +Fp(whose)h(t)o(yp)q(e)g(is)h Fi(Dialogue)o Fp(;)h(this)e(is)h(the)f(t)o(yp)q +(e)g(of)g(the)g(con)o(tin)o(uation)g(that)g(the)g(op)q(erating)0 +1146 y(system)15 b(is)g(exp)q(ecting.)71 1244 y(The)j(other)g(t)o(wist)f(is)i +(that)f(not)f(only)i(is)g Fo(failur)n(e)f Fp(enco)q(ded)h(as)f(a)g(con)o(tin) +o(uation,)h(but)g(so)e(is)i Fo(suc)n(c)n(ess)p Fp(,)e(since)0 +1301 y(w)o(e)d(need)i(to)e(con)o(trol)h(the)f(\015o)o(w)h(of)f(all)i(asp)q +(ects)e(of)h(I/O.)g(Th)o(us)f(the)h(I/O)g(\\commands")f(in)i(Hask)o +(ell|functions)0 1357 y(called)h Fo(tr)n(ansactions)p Fp(|tak)o(e)d +Fo(two)h Fp(con)o(tin)o(uation)h(argumen)o(ts,)e(one)h(for)g(success)h(and)f +(the)g(other)g(for)g(failure.)71 1455 y(The)d(simplest)h(thing)g(a)f(program) +f(could)i(do)f(is)g Fo(halt)p Fp(.)19 b(T)l(o)12 b(do)g(that,)g(w)o(e)g(use)g +(the)g(sp)q(ecial)i Fi(done)e Fp(con)o(tin)o(uation,)0 1512 +y(whose)j(t)o(yp)q(e)g(is)h Fi(Dialogue)o Fp(:)71 1622 y Fi(main)476 +b(=)48 b(done)71 1797 y Fp(F)l(or)18 b(a)g(little)i(more)f(sophistication,)h +(supp)q(ose)f(w)o(e)g(wish)g(to)f(write)h(a)g(string)f Fi(s)h +Fp(to)f(a)g(\014le)i(whose)f(name)f(is)0 1854 y Fi("ReadMe")o +Fp(.)i(W)l(e)15 b(w)o(ould)h(do)f(this)g(in)h(Hask)o(ell)g(using)g(the)g +Fi(writeFile)e Fp(transaction:)71 1963 y Fi(main)476 b(=)24 +b(writeFile)f("ReadMe")f(s)i(failCont)f(succCont)0 2072 y Fp(where)12 +b Fi(failCont)f Fp(and)h Fi(succCont)f Fp(are)h(failure)h(and)f(success)g +(con)o(tin)o(uations,)h(resp)q(ectiv)o(ely)l(,)h(that)d(will)j(b)q(e)e(giv)o +(en)0 2128 y(sp)q(eci\014c)17 b(v)m(alues)f(later.)k(The)c(t)o(yp)q(e)f(of)g +Fi(writeFile)f Fp(is:)71 2237 y Fi(writeFile)356 b(::)24 b(Name)f(->)h +(String)f(->)g(FailCont)g(->)h(SuccCont)f(->)g(Dialogue)71 +2294 y(type)g(Name)357 b(=)24 b(String)0 2403 y Fp(So)15 b(w)o(e)g(see)h +(that)e(the)h(result)h(of)f(the)g(application)i(of)e Fi(writeFile)f +Fp(has)h(the)g(appropriate)g(t)o(yp)q(e:)20 b Fi(Dialogue)o +Fp(.)71 2501 y(But)g(what)f(exactly)h(are)g Fi(FailCont)e Fp(and)j +Fi(SuccCont)n Fp(?)35 b(T)l(o)20 b(answ)o(er)f(that,)h(w)o(e)g(m)o(ust)f +(understand)i(what)0 2557 y(kinds)16 b(of)e(\\in)o(termediate)h(v)m(alues")h +(are)e(generated)h(b)o(y)f(the)h(v)m(arious)g(transactions.)k(F)l(or)14 +b(example,)i Fo(failur)n(e)e Fp(of)g(a)0 2614 y(transaction)h(ma)o(y)f +(generate)h(an)o(y)g(of)g(the)g(follo)o(wing)h(kinds)g(of)f(errors:)p +eop +%%Page: 44 44 +bop 0 -40 a Fp(T-44)1417 b Fj(8)45 b(INPUT/OUTPUT)71 160 y +Fi(data)23 b(IOError)285 b(=)24 b(WriteError)46 b(String)643 +216 y(|)24 b(ReadError)70 b(String)643 272 y(|)24 b(SearchError)e(String)643 +329 y(|)i(FormatError)e(String)643 385 y(|)i(OtherError)46 +b(String)0 494 y Fp(and)15 b(th)o(us)g Fi(FailCont)f Fp(is)i(de\014ned)h(as:) +71 595 y Fi(type)23 b(FailCont)261 b(=)24 b(IOError)f(->)h(Dialogue)0 +704 y Fp(In)16 b(con)o(trast)e(to)g(failure,)i Fo(suc)n(c)n(ess)e +Fp(of)g Fi(writeFile)g Fp(do)q(esn't)h(really)h(return)g Fo(anything)p +Fp(,)e(so)h Fi(SuccCont)f Fp(is)i(simply:)71 813 y Fi(type)23 +b(SuccCont)261 b(=)24 b(Dialogue)0 922 y Fp(A)19 b(simple)h(failure)f(con)o +(tin)o(uation)g(that)f(is)h(prede\014ned)h(in)f(Hask)o(ell)h(is)f(one)f(that) +g(ignores)h(the)g(error)e(message)0 978 y(and)e(halts:)71 1078 +y Fi(abort)452 b(::)24 b(FailCont)71 1135 y(abort)f(err)357 +b(=)24 b(done)0 1244 y Fp(A)15 b(b)q(etter)g(one,)g(also)h(prede\014ned,)g +(prin)o(ts)f(the)h(error)e(message)h(to)g Fi(stderror)f Fp(b)q(efore)h +(halting:)71 1353 y Fi(exit)476 b(::)48 b(FailCont)71 1409 +y(exit)23 b(err)381 b(=)48 b(appendChan)22 b(stderr)h(msg)h(abort)f(done)715 +1466 y(where)g(msg)h(=)f(case)h(err)f(of)h(ReadError)70 b(s)24 +b(->)f(s)1288 1522 y(WriteError)46 b(s)24 b(->)f(s)1288 1579 +y(SearchError)f(s)i(->)f(s)1288 1635 y(FormatError)f(s)i(->)f(s)1288 +1692 y(OtherError)46 b(s)24 b(->)f(s)71 1850 y Fp(Returning)18 +b(no)o(w)g(to)f(the)g(previous)i(example,)g(if)f(what)f(w)o(e)g(w)o(an)o(ted) +g(is)i(simply)g(to)e(halt)g(after)g(writing)i(to)0 1906 y(the)c(\014le,)h +(and)g(prin)o(t)f(an)o(y)g(error)f(message)h(that)g(migh)o(t)g(arise,)g(w)o +(e)g(w)o(ould)g(write:)71 2015 y Fi(main)476 b(=)24 b(writeFile)f("ReadMe")f +(s)i(exit)f(done)71 2173 y Fp(The)15 b(transaction)g Fi(readFile)f +Fp(returns)h(the)g(con)o(ten)o(ts)g(of)f(a)h(\014le,)h(and)g(th)o(us)f(its)g +(t)o(yp)q(e)g(is)h(giv)o(en)g(b)o(y:)71 2282 y Fi(readFile)380 +b(::)24 b(Name)f(->)h(FailCont)f(->)g(StrCont)g(->)h(Dialogue)71 +2339 y(type)f(StrCont)285 b(=)24 b(String)f(->)h(Dialogue)0 +2448 y Fp(F)l(or)15 b(example,)g(w)o(e)g(could)h(use)g(it)f(to)g(read)g(the)g +(\014le)i(previously)f(written:)71 2557 y Fi(main)476 b(=)24 +b(readFile)f("ReadMe")g(exit)g(\(\\s->...\))p eop +%%Page: 45 45 +bop 0 -40 a Fj(8.2)45 b(Con)o(tin)o(uation)15 b(Based)g(I/O)1271 +b Fp(T-45)0 105 y(where)15 b(the)h Fi(...)e Fp(re\015ects)i(whatev)o(er)e(it) +i(is)g(w)o(e)f(wish)g(to)g(do)g(with)h(what)e(w)o(e)h(ha)o(v)o(e)g(read.)71 +183 y(Pragmatically)l(,)f(it)h(ma)o(y)e(seem)i(that)e Fi(readFile)g +Fp(m)o(ust)h(read)g(an)h(en)o(tire)f(\014le,)h(resulting)h(in)f(p)q(o)q(or)f +(space)h(and)0 240 y(time)k(p)q(erformance)g(under)h(certain)g +(circumstances.)32 b(Ho)o(w)o(ev)o(er,)18 b(this)i(is)f(not)g(the)g(case.)31 +b(The)19 b(k)o(ey)g(p)q(oin)o(t)g(is)0 296 y(that)c Fi(readFile)g +Fp(returns)h(a)g(\\lazy")g(\(i.e.)23 b(non-strict\))16 b(list)h(of)f(c)o +(haracters)f(\(recall)i(that)f(strings)g(are)f(just)h(lists)0 +353 y(of)e(c)o(haracters)g(in)i(Hask)o(ell\),)f(whose)g(elemen)o(ts)g(are)f +(read)h(\\b)o(y)g(demand")g(just)f(lik)o(e)i(an)o(y)e(other)h(list.)20 +b(An)15 b(imple-)0 409 y(men)o(tation)f(can)g(b)q(e)h(exp)q(ected)h(to)e +(implemen)o(t)h(this)g(demand-driv)o(en)h(b)q(eha)o(vior)f(b)o(y)f(reading)h +(one)f(c)o(haracter)g(at)0 465 y(a)h(time)g(from)g(the)g(\014le.)71 +543 y(There)k(are)g(lots)h(of)f(other)g(transactions)g(a)o(v)m(ailable)i(in)f +(the)g(Hask)o(ell)g(I/O)g(design,)h(whic)o(h)f(primarily)h(re-)0 +600 y(\015ects)d(the)g(desire)h(for)e(compatibilit)o(y)j(with)e(con)o(v)o(en) +o(tional)g(op)q(erating)g(systems.)27 b(F)l(or)18 b(example,)h(there)f(is)g +(an)0 656 y Fi(appendFile)e Fp(transaction)g(whic)o(h)i(writes)f(a)f(string)h +(to)g(the)g(end)g(of)g(a)f(\014le.)26 b(There)18 b(are)e(also)h +Fo(binary)g Fp(v)o(ersions)0 713 y(of)e Fi(readFile)o Fp(,)f +Fi(writeFile)o Fp(,)h(and)g Fi(appendFile)f Fp(\()p Fn(x)p +Fp(7\).)71 791 y(Also)h(included)j(among)c(the)h(standard)f(op)q(erations)i +(are)e(those)h(whic)o(h)h(in)o(teract)f(with)g Fo(channels)p +Fp(,)e(whic)o(h)j(in)0 847 y(Unix-land)k(includes)g(things)e(lik)o(e)h +(standard-input,)g(standard-output,)f(etc.)27 b(The)19 b(t)o(w)o(o)d(most)h +(imp)q(ortan)o(t)g(of)0 904 y(these)e(are:)71 1004 y Fi(readChan)380 +b(::)24 b(Name)f(->)262 b(FailCont)23 b(->)h(StrCont)47 b(->)23 +b(Dialogue)71 1060 y(appendChan)332 b(::)24 b(Name)f(->)h(String)f(->)g +(FailCont)g(->)h(SuccCont)f(->)g(Dialogue)0 1169 y Fp(There)12 +b(is)g(no)g Fi(writeChan)f Fp(transaction,)h(since)h(the)f(output)f(is)i(alw) +o(a)o(ys)e(app)q(ended)i(to)e(whatev)o(er)h(w)o(as)f(previously)0 +1226 y(written)k(\(th)o(us)g Fi(appendChan)f Fp(is)h(akin)h(to)f +Fi(appendFile)n Fp(\).)71 1304 y(Note)g(that)h(c)o(hannel)h(names)f(are)g +(just)f(strings)h(\(lik)o(e)h(\014le)g(names\).)23 b(There)16 +b(are)g(four)f(c)o(hannel)j(names)e(that)0 1360 y(are)f(alw)o(a)o(ys)f(supp)q +(orted)i(b)o(y)f(a)g(prop)q(er)h(implemen)o(tation,)g(and)f(are)g(b)q(ound)h +(to)f(the)g(follo)o(wing)h(iden)o(ti\014ers:)71 1469 y Fi(stdin)452 +b(=)48 b("stdin")71 1526 y(stdout)428 b(=)48 b("stdout")71 +1582 y(stderr)428 b(=)48 b("stderr")71 1639 y(stdecho)404 b(=)48 +b("stdecho")0 1750 y Fp(On)12 b(most)f(systems,)g Fi(stdout)p +Fp(,)g Fi(stderr)p Fp(,)h(and)f Fi(stdecho)g Fp(all)h(represen)o(t)g(the)g +(same)f(ph)o(ysical)i(c)o(hannel)g(\(nominally)l(,)0 1807 y(the)i(user's)g +(terminal)h(displa)o(y\).)71 1885 y(Let's)k(no)o(w)g(consider)h(a)f(sligh)o +(tly)i(bigger)f(program|one)f(that)f(writes)i(to)f(a)g(\014le,)i(reads)e(the) +h(\014le)g(bac)o(k,)0 1941 y(compares)15 b(the)h(con)o(ten)o(ts)e(for)h +(equalit)o(y)h(with)g(what)f(w)o(as)f(previously)j(written,)e(prin)o(ts)h(a)f +(suitable)i(message)e(to)0 1998 y(standard)g(output,)f(and)i(halts:)71 +2107 y Fi(main)94 b(=)24 b(writeFile)f("ReadMe")g(s1)g(exit)h(\()309 +2163 y(readFile)47 b("ReadMe")94 b(exit)24 b(\(\\s2->)309 2220 +y(appendChan)f(stdout)g(\(if)g(s1==s2)g(then)h("contents)e(match")1001 +2276 y(else)i("something)e(intervened!"\))g(exit)309 2333 y(done\)\))0 +2444 y Fp(This)f(program)e(demonstrates)g(an)h(imp)q(ortan)o(t)f(asp)q(ect)i +(of)e(Hask)o(ell)i(I/O|it)g(p)q(ermits)g Fo(nondeterminism)e +Fp(in)0 2501 y(the)e(op)q(erating)g(system)f(\(if)h(it)g(didn't,)g(it)g(w)o +(ould)g(hardly)h(b)q(e)f(practical!\),)g(y)o(et)f(in)o(ternally)j(programs)c +(are)i(still)0 2557 y(referen)o(tially)d(transparen)o(t.)k(In)13 +b(this)g(example)h(there)f(is)g(the)f(p)q(ossibilit)o(y)j(\(alb)q(eit)f +(small\))f(that)f(the)h(test)f Fi(s1==s2)0 2614 y Fp(will)17 +b(fail,)e(b)q(ecause)h(some)e(other)h(agen)o(t)f(ma)o(y)g(mo)q(dify)h(the)g +(\014le)h(b)q(et)o(w)o(een)f(the)g(times)g(it)g(w)o(as)f(written)h(and)g +(read.)p eop +%%Page: 46 46 +bop 0 -40 a Fp(T-46)1417 b Fj(8)45 b(INPUT/OUTPUT)71 105 y +Fp(Also)16 b(note)g(ho)o(w)g(this)g(program)f(is)i(formatted,)e(giving)i(it)f +(an)g(\\imp)q(erativ)o(e)h(feel.")24 b(W)l(e)16 b(can)g(enhance)i(this)0 +162 y(st)o(yle)d(b)o(y)g(using)h(an)g(in\014x)g(apply)g(op)q(erator:)71 +271 y Fi(infixr)23 b(0)g($)71 327 y(f)g($)h(a)71 b(=)24 b(f)g(a)71 +407 y(main)94 b(=)24 b(writeFile)47 b("ReadMe")22 b(s1)i(exit)f($)309 +463 y(readFile)71 b("ReadMe")94 b(exit)23 b($)h(\\s2->)309 +520 y(appendChan)f(stdout)g(\(if)g(s1==s2)g(then)h("contents)e(match")1001 +576 y(else)i("somebody)e(intervened!"\))g(exit)i($)309 632 +y(done)0 742 y Fp(This)12 b(v)o(ersion)h(has)e(the)h(adv)m(an)o(tage)g(of)f +(not)h(requiring)h(the)f(nesting)g(of)g(paren)o(theses,)g(whic)o(h)h(in)f +(larger)g(programs)0 798 y(can)j(b)q(ecome)h(quite)g(anno)o(ying.)0 +1050 y Fg(8.3)56 b(T)-5 b(erminal)16 b(I/O)0 1208 y Fp(Sp)q(ecial)h(men)o +(tion)d(should)i(b)q(e)f(made)g(of)f(reading)h(from)f(standard)g(input,)h(in) +g(that)f(what)g(is)h(returned)g(is)g(a)f(lazy)0 1264 y(string,)f(just)g(as)g +(for)g(a)g(\014le.)20 b(The)13 b(\014rst)g(issue)h(that)f(arises)g(is)h +(whether)f(the)h(input)g(is)g(\\ec)o(ho)q(ed")f(on)g(the)h(terminal,)0 +1320 y(or)e(whether)h(the)g(user)f(is)h(required)h(to)e(do)h(so)f(explicitly) +l(.)22 b(The)13 b(answ)o(er)f(is)h(that)f(w)o(e)g(ha)o(v)o(e)g(a)h(c)o +(hoice!)20 b(The)12 b(default)0 1377 y(is)k(that)f(ec)o(hoing)i(is)f(\\on,")f +(but)g(it)h(can)g(b)q(e)g(turned)h(o\013)d(\(or)h(on\))h(using)g(the)g +Fi(echo)f Fp(transaction,)g(whic)o(h)h(tak)o(es)f(a)0 1433 +y(b)q(o)q(olean)h(argumen)o(t)f(along)g(with)g(the)h(\(ubiquitous\))g +(failure)g(and)f(success)h(con)o(tin)o(uations:)71 1545 y Fi(echo)476 +b(::)24 b(Bool)f(->)h(FailCont)f(->)g(SuccCont)g(->)h(Dialogue)0 +1654 y Fp(T)l(urning)19 b(ec)o(hoing)f(o\013)f(w)o(ould)h(b)q(e)g(required)h +(if,)f(for)f(example,)h(w)o(e)g(w)o(an)o(ted)f(to)g(write)g(a)h(screen-orien) +o(ted)g(text)0 1710 y(editor.)71 1823 y(Since)13 b(the)f(result)g(of)f +(reading)i(from)e Fi(stdin)g Fp(is)h(a)g(lazy)g(string,)g(that)f(means)h(it)g +(only)g(has)g(to)f(b)q(e)h(done)h Fo(onc)n(e)e Fp(for)0 1880 +y(a)k(giv)o(en)h(program|indeed,)g(a)f(run-time)i(error)e(will)i(result)f(if) +f(it)h(is)g(done)g(more)f(than)g(once.)21 b(But)16 b(this)g(raises)0 +1936 y(the)f(follo)o(wing)g(question:)20 b(if)c(ec)o(hoing)f(is)g(enabled,)h +(the)f(input)h(stream)d(will)k(app)q(ear)e(on)f(the)h Fi(stdecho)f +Fp(c)o(hannel)0 1993 y(\(whic)o(h)k(on)g(most)f(systems)h(is)g(synon)o(ymous) +g(with)g Fi(stdout)o Fp(\);)h(but)f(then)g(ho)o(w)f(is)i(this)f(output)g +(\\in)o(terlea)o(v)o(ed")0 2049 y(with)e(explicit)h(output)e(to)f(the)i(same) +f(c)o(hannel?)71 2162 y(T)l(o)c(giv)o(e)h(a)g(formal)g(answ)o(er)f(to)h(this) +g(question)h(requires)f(de\014ning)i(precisely)g(the)e(b)q(eha)o(vior)g(of)g +(the)g(op)q(erating)0 2218 y(system;)j(that's)g(wh)o(y)g(in)i(the)e(App)q +(endix)j(of)d(the)h(Rep)q(ort)g(a)g(sp)q(eci\014cation)h(of)e(the)h(op)q +(erating)g(system)f(is)h(giv)o(en)0 2275 y(in)g(Hask)o(ell)g(co)q(de.)21 +b(Here,)15 b(w)o(e)g(simply)h(giv)o(e)f(an)h(op)q(erational)f(description)i +(of)e(the)g(exp)q(ected)i(b)q(eha)o(vior.)71 2388 y(First,)e(w)o(e)h(m)o(ust) +g(consider)h(whether)f(ec)o(hoing)h(is)g(enabled)g(or)f(disabled.)25 +b(With)16 b(ec)o(ho)g(enabled,)i(w)o(e)e(w)o(ould)0 2444 y(exp)q(ect)h(line)h +(editing)f(functions)g(lik)o(e)g(\\rub)q(out")f(to)g(b)q(e)g(handled)i +Fo(b)n(efor)n(e)e Fp(the)g(program)f(is)h(allo)o(w)o(ed)h(to)e(see)i(the)0 +2501 y(result,)i(and)f(indeed)i(that's)d(what)g(happ)q(ens)i(in)g(Hask)o +(ell.)29 b(This)19 b(means)f(that)f(the)h(user)g(input)h(is)g(e\013ectiv)o +(ely)0 2557 y(seen)e(\\line-at-a-time,")h(since)f(a)f(series)h(of)f(rub)q +(outs)h(could)h(o)q(ccur)e(at)g(an)o(y)g(p)q(oin)o(t)h(in)h(the)e(line.)26 +b(Consider,)17 b(for)0 2614 y(example,)f(this)f(program:)p +eop +%%Page: 47 47 +bop 0 -40 a Fj(8.4)45 b(Finer)15 b(Lev)o(el)i(of)d(Con)o(trol)1311 +b Fp(T-47)71 160 y Fi(main)285 b(=)24 b(echo)f(flag)h(exit)262 +b($)500 216 y(readChan)23 b(stdin)g(exit)143 b($)23 b(\\s->)500 +272 y(appendChan)g(stdout)g(s)g(exit)h($)500 329 y(done)0 438 +y Fp(If)13 b Fi(flag)e Fp(is)i Fi(True)p Fp(,)f(ec)o(hoing)h(will)h(b)q(e)f +(enabled,)h(and)e(the)h(user)f(will)i(b)q(e)f(able)g(to)f(t)o(yp)q(e)g(an)g +(en)o(tire)h(line,)h(with)f(correc-)0 494 y(tions)k(b)q(eing)h(dutifully)h +(pro)q(cessed.)25 b(When)17 b(a)f(new-line)j(c)o(haracter)d(is)h(t)o(yp)q +(ed,)g(the)g Fi(appendChan)e Fp(transaction)0 551 y(will)20 +b(then)f(b)q(e)g(able)g(to)f(\\see")g(the)h(result,)g(and)g(will)h(th)o(us)e +(displa)o(y)h(the)g(line)h(just)e(t)o(yp)q(ed,)h(with)g(corrections.)0 +607 y(This)d(pro)q(cess)f(will)i(then)f(rep)q(eat)f(for)g(subsequen)o(t)h +(lines;)g(in)g(other)f(w)o(ords,)f(the)h(ec)o(hoing)h(and)g(explicit)h +(output)0 664 y(are)f(in)o(terlea)o(v)o(ed)h(line-at-a-time.)26 +b(Note)16 b(that)g(this)h(program)e(is)i(essen)o(tially)h(in)f(an)g +(in\014nite)h(lo)q(op,)f(unless)h(the)0 720 y(user)h(terminates)h(the)f +(input)h(sequence)h(\(suc)o(h)e(as)g(via)h(Cn)o(trl-D)f(in)h(a)f(Unix)h(en)o +(vironmen)o(t\),)g(in)g(whic)o(h)g(case)0 777 y Fi(appendChan)14 +b Fp(can)h(\014nish)i(its)e(task.)71 858 y(On)j(the)h(other)f(hand,)h(if)f +Fi(flag)g Fp(is)h Fi(False)o Fp(,)g(ec)o(hoing)g(is)f(disabled,)j(and)d +Fi(appendChan)f Fp(will)j(see)f(the)f(input)0 915 y(c)o(haracter-b)o(y-c)o +(haracter.)g(Th)o(us)12 b(the)g(e\013ect)f(of)h Fi(appendChan)e +Fp(will)k(b)q(e)f(to)e(do)h(c)o(haracter-b)o(y-c)o(haracter)f(ec)o(hoing!)71 +996 y(The)17 b(other)f(factor)g(in)i(determining)g(the)g(in)o(terlea)o(ving)g +(b)q(eha)o(vior)f(is)h(the)f(degree)g(to)f(whic)o(h)i(the)f(program)0 +1052 y Fo(dep)n(ends)j Fp(on)h(the)g(input)h(stream.)36 b(F)l(or)20 +b(example,)j(a)e(program)f(that)g(prin)o(ts)h(one)g Fi("X")g +Fp(for)f(ev)o(ery)h(10)f(input)0 1109 y(c)o(haracters)14 b(will)j(b)q(eha)o +(v)o(e)f(as)f(exp)q(ected;)h(here)f(is)h(suc)o(h)f(a)g(program:)71 +1218 y Fi(main)94 b(=)24 b(readChan)f(stdin)g(exit)g($)h(\\s->)309 +1274 y(let)g(loop)f(n)48 b([])310 b(=)23 b(done)405 1331 y(loop)g(n)h +(\(x:xs\))f(|)g(n==10)48 b(=)23 b(appendChan)g(stdout)g("X")g(exit)h($)1001 +1387 y(loop)95 b(1)48 b(xs)739 1444 y(|)23 b(True)72 b(=)23 +b(loop)h(\(n+1\))f(xs)309 1500 y(in)h(loop)f(1)h(s)0 1612 y +Fp(This)19 b(program)e(also)h(demonstrates)g(ho)o(w)f(to)h(write)g(a)g(lo)q +(op)h(\(i.e.)f(a)g(recursion\))g(using)h(con)o(tin)o(uations.)30 +b(\(See)0 1668 y Fn(x)p Fp(7.7)14 b(for)h(another)g(example)h(of)f(sync)o +(hronizing)h(input)h(with)e(output.\))71 1750 y(There)g(are)g(sev)o(eral)g(v) +o(ery)g(useful)h(prede\014ned)h(functions)f(in)g(Hask)o(ell)g(that)f(mak)o(e) +f(in)o(teractions)i(with)f(stan-)0 1806 y(dard)20 b(input)h(and)f(output)g +(relativ)o(ely)h(painless.)36 b(These)21 b(are)e Fi(print)p +Fp(,)h Fi(prints)p Fp(,)g(and)h Fi(interact)o Fp(,)f(whic)o(h)h(are)0 +1862 y(describ)q(ed)c(in)f(Section)g Fn(x)p Fp(7.5)f(of)f(the)i(Rep)q(ort.)0 +2009 y Fg(8.4)56 b(Finer)18 b(Lev)n(el)f(of)i(Con)n(trol)0 +2120 y Fp(Sometimes)c(\(although)f(rarely\))g(the)h(user)f(will)i(need)g(a)e +(\014ner)h(lev)o(el)h(of)e(con)o(trol)g(o)o(v)o(er)f(I/O)i(than)f(that)g(pro) +o(vided)0 2176 y(b)o(y)g(the)f(standard)h(set)f(of)g(I/O)h(transactions)f +(pro)o(vided)i(in)f(Hask)o(ell)h(\(for)e(example,)h(sometimes)g(it)g(is)g +(necessary)0 2233 y(to)f(explicitly)k(op)q(en)e(and)f(close)h(\014les\).)20 +b(This)15 b(fact)e(is)i(recognized)g(in)g(App)q(endix)h Fn(x)p +Fp(C,)e(where)g(an)g(alternativ)o(e)g(set)0 2289 y(of)h(I/O)g(op)q(erations)h +(is)f(suggested)h(and)f(that)f(some)h(implemen)o(tations)i(ma)o(y)d(supp)q +(ort.)p eop +%%Page: 48 48 +bop 0 -40 a Fp(T-48)1597 b Fj(9)45 b(ARRA)l(YS)0 105 y Fq(9)69 +b(Arra)n(ys)0 236 y Fp(Ideally)l(,)13 b(arra)o(ys)d(in)i(a)f(functional)h +(language)f(w)o(ould)g(b)q(e)h(regarded)f(simply)h(as)f(functions)h(from)e +(indices)j(to)d(v)m(alues,)0 292 y(but)16 b(pragmatically)l(,)g(in)h(order)e +(to)g(assure)h(e\016cien)o(t)g(access)g(to)f(arra)o(y)g(elemen)o(ts,)h(w)o(e) +f(need)i(to)e(b)q(e)h(sure)g(w)o(e)g(can)0 349 y(tak)o(e)h(adv)m(an)o(tage)g +(of)h(the)g(sp)q(ecial)h(prop)q(erties)g(of)e(the)h(domains)g(of)g(these)g +(functions,)h(whic)o(h)f(are)g(isomorphic)0 405 y(to)h(\014nite)i(con)o +(tiguous)f(subsets)g(of)f(the)h(in)o(tegers.)34 b(Hask)o(ell,)21 +b(therefore,)f(do)q(es)g(not)g(treat)f(arra)o(ys)f(as)h(general)0 +462 y(functions)d(with)f(an)h(application)g(op)q(eration,)g(but)f(as)g +(abstract)f(data)g(t)o(yp)q(es)i(with)f(a)g(subscript)h(op)q(eration.)71 +546 y(Tw)o(o)11 b(main)h(approac)o(hes)g(to)f(functional)j(arra)o(ys)c(ma)o +(y)i(b)q(e)g(discerned:)20 b Fo(incr)n(emental)11 b Fp(and)h +Fo(monolithic)g Fp(de\014ni-)0 603 y(tion.)20 b(In)15 b(the)f(incremen)o(tal) +h(case,)f(w)o(e)g(ha)o(v)o(e)g(a)g(function)h(that)e(pro)q(duces)i(an)g(empt) +o(y)e(arra)o(y)g(of)h(a)g(giv)o(en)h(size)g(and)0 659 y(another)f(that)f(tak) +o(es)g(an)h(arra)o(y)l(,)f(an)h(index,)h(and)f(a)g(v)m(alue,)h(pro)q(ducing)g +(a)f(new)g(arra)o(y)f(that)g(di\013ers)h(from)f(the)h(old)0 +716 y(one)i(only)g(at)f(the)h(giv)o(en)h(index.)23 b(Ob)o(viously)l(,)17 +b(a)e(naiv)o(e)i(implemen)o(tation)g(of)e(suc)o(h)h(an)g(arra)o(y)f(seman)o +(tics)h(w)o(ould)0 772 y(b)q(e)h(in)o(tolerably)g(ine\016cien)o(t,)g +(requiring)g(a)e(new)h(cop)o(y)g(of)g(an)f(arra)o(y)g(for)g(eac)o(h)h +(incremen)o(tal)h(rede\014nition;)h(th)o(us,)0 829 y(serious)13 +b(attempts)e(at)g(using)i(this)f(approac)o(h)g(emplo)o(y)h(sophisticated)g +(static)f(analysis)g(and)h(clev)o(er)g(run-time)f(de-)0 885 +y(vices)j(to)f(a)o(v)o(oid)h(excessiv)o(e)g(cop)o(ying.)20 +b(The)15 b(monolithic)h(approac)o(h,)e(on)g(the)h(other)f(hand,)h(constructs) +f(an)g(arra)o(y)0 941 y(all)k(at)f(once,)g(without)g(reference)h(to)f(in)o +(termediate)g(arra)o(y)f(v)m(alues.)27 b(Although)18 b(Hask)o(ell)g(has)f(an) +g(incremen)o(tal)0 998 y(arra)o(y)d(up)q(date)i(op)q(erator,)e(the)h(main)h +(thrust)e(of)h(the)g(arra)o(y)f(facilit)o(y)j(is)e(monolithic.)0 +1155 y Fg(9.1)56 b(Index)18 b(t)n(yp)r(es)0 1270 y Fp(The)d(Standard)h +(Prelude)g(de\014nes)g(a)f(t)o(yp)q(e)g(class)h(of)f(arra)o(y)f(indices:)71 +1379 y Fi(class)47 b(\(Ord)23 b(a\))h(=>)f(Ix)h(a)47 b(where)166 +1436 y(range)166 b(::)24 b(\(a,a\))f(->)h([a])166 1492 y(index)166 +b(::)24 b(\(a,a\))f(a)h(->)f(Int)166 1549 y(inRange)118 b(::)24 +b(\(a,a\))f(->)h(a)f(->)h(Bool)0 1658 y Fp(Instance)12 b(declarations)g(are)e +(pro)o(vided)i(for)f Fi(Int)o Fp(,)h Fi(Integer)o Fp(,)f Fi(Char)p +Fp(,)g Fi(Bool)p Fp(,)g(and)h(tuples)g(of)e Fi(Ix)h Fp(t)o(yp)q(es;)h(in)g +(addition,)0 1714 y(instances)17 b(ma)o(y)e(b)q(e)i(automatically)f(deriv)o +(ed)h(for)f(en)o(umerated)g(and)g(tuple)h(t)o(yp)q(es.)23 b(W)l(e)16 +b(regard)f(the)i(primitiv)o(e)0 1771 y(t)o(yp)q(es)j(as)f(v)o(ector)g +(indices)i(and)f(tuples)h(as)e(indices)i(of)f(m)o(ultidimensional)i +(rectangular)e(arra)o(ys.)31 b(Note)20 b(that)0 1827 y(the)c(\014rst)g +(argumen)o(t)f(of)h(eac)o(h)g(of)f(the)h(op)q(erations)h(of)e(class)h +Fi(Ix)g Fp(is)h(a)e(pair)i(of)e(indices;)j(these)f(are)e(t)o(ypically)j(the)0 +1883 y Fo(b)n(ounds)g Fp(\(\014rst)f(and)h(last)g(indices\))i(of)e(an)g(arra) +o(y)l(.)28 b(F)l(or)17 b(example,)j(the)e(b)q(ounds)h(of)f(a)f(10-elemen)o +(t,)i(zero-origin)0 1940 y(v)o(ector)13 b(with)h Fi(Int)f Fp(indices)j(w)o +(ould)e(b)q(e)h Fi(\(0,9\))o Fp(,)f(while)h(a)e(100)g(b)o(y)h(100)f(1-origin) +h(matrix)f(migh)o(t)h(ha)o(v)o(e)f(the)h(b)q(ounds)0 1996 y +Fi(\(\(1,1\),\(100,100\)\))n Fp(.)38 b(\(In)22 b(man)o(y)e(other)h +(languages,)i(suc)o(h)f(b)q(ounds)g(w)o(ould)g(b)q(e)g(written)f(in)h(a)f +(form)g(lik)o(e)0 2053 y Fi(1:100,)i(1:100)o Fp(,)17 b(but)g(the)g(presen)o +(t)f(form)g(\014ts)h(the)g(t)o(yp)q(e)g(system)f(b)q(etter,)h(since)g(eac)o +(h)g(b)q(ound)h(is)f(of)f(the)h(same)0 2109 y(t)o(yp)q(e)e(as)g(a)g(general)h +(index.\))71 2194 y(The)g Fi(range)g Fp(op)q(eration)g(tak)o(es)g(a)f(b)q +(ounds)j(pair)e(and)h(pro)q(duces)g(the)f(list)h(of)f(indices)i(lying)g(b)q +(et)o(w)o(een)e(those)0 2250 y(b)q(ounds,)g(in)g(index)g(order.)k(F)l(or)14 +b(example,)617 2357 y Fi(range)23 b(\(0,4\))72 b Fn(\))i Fi([0,1,2,3,4])151 +2468 y(range)23 b(\(\(0,0\),\(1,2\)\))72 b Fn(\))i Fi([\(0,0\),)22 +b(\(0,1\),)h(\(0,2\),)h(\(1,0\),)f(\(1,1\),)g(\(1,2\)])0 2557 +y Fp(The)17 b Fi(inRange)g Fp(predicate)h(determines)g(whether)f(an)g(index)i +(lies)f(b)q(et)o(w)o(een)g(a)e(giv)o(en)i(pair)g(of)e(b)q(ounds.)27 +b(\(F)l(or)16 b(a)0 2614 y(tuple)d(t)o(yp)q(e,)f(this)g(test)f(is)h(p)q +(erformed)g(comp)q(onen)o(t)o(wise.\))19 b(Finally)l(,)14 b(the)e +Fi(index)f Fp(op)q(eration)h(is)g(what)f(is)i(needed)g(to)p +eop +%%Page: 49 49 +bop 0 -40 a Fj(9.2)45 b(Arra)o(y)14 b(Creation)1451 b Fp(T-49)0 +105 y(address)13 b(a)g(particular)h(elemen)o(t)g(of)f(an)g(arra)o(y:)k(Giv)o +(en)d(a)f(b)q(ounds)h(pair)g(and)f(an)g(in-range)h(index,)g(the)g(op)q +(eration)0 162 y(yields)j(the)e(zero-origin)h(ordinal)g(of)f(the)g(index)i +(within)f(the)f(range;)g(for)g(example:)712 259 y Fi(index)23 +b(\(1,9\))g(2)73 b Fn(\))h Fi(1)569 357 y(index)23 b(\(\(0,0\),\(1,2\)\))f +(\(1,1\))73 b Fn(\))g Fi(5)0 501 y Fg(9.2)56 b(Arra)n(y)19 +b(Creation)0 610 y Fp(Hask)o(ell's)h(monolithic)h(arra)o(y)d(creation)i +(function)h(forms)d(an)i(arra)o(y)e(from)h(a)g(pair)h(of)f(b)q(ounds)i(and)f +(a)f(list)h(of)0 666 y(index-v)m(alue)e(pairs)d(\(an)g Fo(asso)n(ciation)h +(list)p Fp(\):)71 778 y Fi(array)452 b(::)24 b(\(Ix)f(a\))h(=>)g(\(a,a\))f +(->)g([Assoc)g(a)h(b])g(->)f(Array)g(a)h(b)0 887 y Fp(Notice)15 +b(the)f(t)o(yp)q(e)h Fi(Assoc)o Fp(;)f(to)g(impro)o(v)o(e)g(the)h(readabilit) +o(y)g(of)f(arra)o(y)f(expressions,)i(the)f(pairs)h(in)g(the)g(asso)q(ciation) +0 944 y(list)h(are)f(not)g(of)f(the)i(ordinary)f(sort,)f(but)h(of)g(another)g +(tuple)h(t)o(yp)q(e)f(with)h(the)f(data)g(constructor)f Fi(\(:=\))p +Fp(:)71 1053 y Fi(data)47 b(Assoc)23 b(a)h(b)214 b(=)48 b(a)24 +b(:=)f(b)0 1162 y Fp(Here,)15 b(for)g(example,)g(is)h(a)f(de\014nition)i(of)e +(an)g(arra)o(y)f(of)h(the)g(squares)g(of)g(n)o(um)o(b)q(ers)g(from)f(1)h(to)g +(100:)71 1271 y Fi(squares)404 b(=)48 b(array)23 b(\(1,100\))g([i)h(:=)f(i)h +(*)g(i)f(|)h(i)g(<-)f([1..100]])0 1380 y Fp(This)17 b(arra)o(y)e(expression)j +(is)f(t)o(ypical)g(in)h(using)f(a)f(list)h(comprehension)h(for)e(the)h(asso)q +(ciation)g(list;)g(in)h(fact,)e(this)0 1436 y(usage)e(results)h(in)g(arra)o +(y)e(expressions)i(m)o(uc)o(h)f(lik)o(e)h(the)g Fo(arr)n(ay)g(c)n(ompr)n +(ehensions)e Fp(of)h(the)g(language)h(Id[4].)k(Arra)o(y)0 1493 +y(subscripting)f(is)f(p)q(erformed)g(with)g(the)g(in\014x)g(op)q(erator)f +Fi(!)p Fp(,)g(and)h(the)g(b)q(ounds)g(of)f(an)h(arra)o(y)e(can)i(b)q(e)g +(extracted)0 1549 y(with)f(the)f(function)h Fi(bounds)o Fp(:)748 +1606 y Fi(squares!7)72 b Fn(\))h Fi(49)628 1687 y(bounds)24 +b(squares)72 b Fn(\))h Fi(\(1,100\))0 1768 y Fp(W)l(e)15 b(migh)o(t)f +(generalize)j(this)e(example)g(b)o(y)g(parameterizing)h(the)e(b)q(ounds)i +(and)f(the)g(function)g(to)g(b)q(e)g(applied)i(to)0 1824 y(eac)o(h)e(index:) +71 1924 y Fi(mkArray)404 b(::)24 b(\(Ix)f(a\))h(=>)g(\(a)f(->)h(b\))f(->)h +(\(a,a\))f(->)h(Array)f(a)h(b)71 1981 y(mkArray)e(f)i(bnds)238 +b(=)48 b(array)23 b(bnds)g([i)h(:=)g(f)f(i)h(|)g(i)f(<-)h(range)f(bnds])0 +2090 y Fp(Th)o(us,)15 b(w)o(e)g(could)h(de\014ne)g Fi(squares)e +Fp(as)h Fi(mkArray)23 b(\(\\i)h(->)f(i)h(*)g(i\))f(\(1,100\))o +Fp(.)71 2170 y(Man)o(y)12 b(arra)o(ys)g(are)h(de\014ned)i(recursiv)o(ely;)g +(that)e(is,)g(with)h(the)g(v)m(alues)g(of)f(some)g(elemen)o(ts)h(dep)q +(ending)i(on)d(the)0 2226 y(v)m(alues)j(of)f(others.)20 b(Here,)15 +b(for)f(example,)i(w)o(e)f(ha)o(v)o(e)g(a)f(function)i(returning)g(an)f(arra) +o(y)f(of)h(Fib)q(onacci)i(n)o(um)o(b)q(ers:)71 2335 y Fi(fibs)94 +b(::)24 b(Int)g(->)f(Array)g(Int)h(Int)71 2392 y(fibs)f(n)47 +b(=)h(a)g(where)23 b(a)h(=)f(array)g(\(0,n\))h(\([0)f(:=)h(1,)f(1)h(:=)g(1])f +(++)954 2448 y([i)g(:=)h(a!\(i-2\))f(+)g(a!\(i-1\))g(|)h(i)g(<-)f([2..n]]\))0 +2557 y Fp(Another)18 b(example)i(of)e(suc)o(h)h(a)f(recurrence)h(is)g(the)g +Fo(n)i Fp(b)o(y)e Fo(n)i(wavefr)n(ont)d Fp(matrix,)h(in)g(whic)o(h)h(elemen)o +(ts)f(of)f(the)0 2614 y(\014rst)f(ro)o(w)e(and)j(\014rst)e(column)i(all)g(ha) +o(v)o(e)e(the)h(v)m(alue)h Fo(1)24 b Fp(and)17 b(other)f(elemen)o(ts)i(are)e +(sums)h(of)g(their)g(neigh)o(b)q(ors)h(to)p eop +%%Page: 50 50 +bop 0 -40 a Fp(T-50)1597 b Fj(9)45 b(ARRA)l(YS)0 105 y Fp(the)15 +b(w)o(est,)f(north)o(w)o(est,)g(and)h(north:)71 214 y Fi(wavefront)165 +b(::)24 b(Int)f(->)h(Array)f(\(Int,Int\))g(Int)71 271 y(wavefront)f(n)119 +b(=)48 b(a)g(where)524 327 y(a)24 b(=)f(array)h(\(\(1,1\),\(n,n\)\))643 +383 y(\([\(1,j\))f(:=)h(1)g(|)f(j)h(<-)g([1..n]])e(++)667 440 +y([\(i,1\))h(:=)h(1)g(|)f(i)h(<-)g([2..n]])e(++)667 496 y([\(i,j\))h(:=)h +(a!\(i,j-1\))e(+)i(a!\(i-1,j-1\))f(+)g(a!\(i-1,j\))954 553 +y(|)g(i)h(<-)g([2..n],)e(j)i(<-)g([2..n]]\))0 662 y Fp(The)13 +b(w)o(a)o(v)o(efron)o(t)d(matrix)i(is)h(so)f(called)i(b)q(ecause)g(in)f(a)f +(parallel)i(implemen)o(tation,)g(the)f(recurrence)g(dictates)g(that)0 +718 y(the)f(computation)h(can)f(b)q(egin)h(with)g(the)f(\014rst)g(ro)o(w)f +(and)i(column)g(in)g(parallel)h(and)e(pro)q(ceed)h(as)f(a)g(w)o(edge-shap)q +(ed)0 775 y(w)o(a)o(v)o(e,)17 b(tra)o(v)o(elling)i(from)e(north)o(w)o(est)g +(to)g(southeast.)28 b(It)18 b(is)g(imp)q(ortan)o(t)g(to)f(note,)h(ho)o(w)o +(ev)o(er,)g(that)f(no)h(order)f(of)0 831 y(computation)e(is)h(sp)q(eci\014ed) +h(b)o(y)e(the)h(asso)q(ciation)f(list.)71 959 y(In)h(eac)o(h)h(of)e(our)h +(examples)h(so)f(far,)f(w)o(e)h(ha)o(v)o(e)g(giv)o(en)h(a)f(unique)h(asso)q +(ciation)g(for)e(eac)o(h)i(index)g(of)f(the)g(arra)o(y)0 1015 +y(and)j(only)f(for)g(the)h(indices)h(within)g(the)e(b)q(ounds)h(of)f(the)h +(arra)o(y)l(,)e(and)i(indeed,)h(w)o(e)e(m)o(ust)g(do)g(this)h(in)g(general)0 +1072 y(for)14 b(an)h(arra)o(y)f(b)q(e)i(fully)h(de\014ned.)k(An)15 +b(asso)q(ciation)h(with)f(an)g(out-of-b)q(ounds)h(index)g(results)f(in)h(an)f +(error;)f(if)i(an)0 1128 y(index)g(is)e(missing)i(or)d(app)q(ears)i(more)f +(than)g(once,)g(ho)o(w)o(ev)o(er,)g(there)g(is)h(no)f(immediate)i(error,)d +(but)h(the)h(v)m(alue)g(of)0 1185 y(the)g(arra)o(y)f(at)g(that)g(index)i(is)g +(then)f(unde\014ned,)i(so)d(that)h(subscripting)h(the)f(arra)o(y)f(with)h +(suc)o(h)g(an)g(index)h(yields)0 1241 y(an)f(error.)0 1542 +y Fg(9.3)56 b(Accum)n(ulation)0 1721 y Fp(W)l(e)14 b(can)g(relax)g(the)g +(restriction)g(that)f(an)h(index)h(app)q(ear)f(at)g(most)f(once)h(in)g(the)g +(asso)q(ciation)h(list)f(b)o(y)g(sp)q(ecifying)0 1777 y(ho)o(w)h(to)g(com)o +(bine)h(m)o(ultiple)i(v)m(alues)f(asso)q(ciated)e(with)h(a)g(single)g(index;) +h(the)f(result)g(is)g(called)h(an)f Fo(ac)n(cumulate)n(d)0 +1834 y(arr)n(ay)p Fp(:)71 1943 y Fi(accumArray)22 b(::)i(\(Ix)f(a\))h(->)f +(\(b)h(->)f(c)h(->)g(b\))f(->)h(b)g(->)f(\(a,a\))g(->)h([Assoc)f(a)h(c])f(->) +h(Array)f(a)h(b)0 2054 y Fp(The)15 b(\014rst)g(argumen)o(t)g(of)g +Fi(accumArray)e Fp(is)j(the)g Fo(ac)n(cumulating)g(function)p +Fp(,)e(the)i(second)f(is)h(an)f(initial)j(v)m(alue)e(\(the)0 +2111 y(same)g(for)f(eac)o(h)i(elemen)o(t)g(of)e(the)i(arra)o(y\),)d(and)i +(the)h(remaining)g(argumen)o(ts)e(are)h(b)q(ounds)h(and)f(an)h(asso)q +(ciation)0 2167 y(list,)e(as)f(with)h(the)g Fi(array)e Fp(function.)21 +b(T)o(ypically)l(,)16 b(the)f(accum)o(ulating)g(function)g(is)g +Fi(\(+\))p Fp(,)f(and)h(the)f(initial)j(v)m(alue,)0 2224 y(zero;)f(for)g +(example,)h(this)f(function)h(tak)o(es)f(a)f(pair)i(of)f(b)q(ounds)h(and)f(a) +g(list)h(of)f(v)m(alues)h(\(of)e(an)h(index)i(t)o(yp)q(e\))e(and)0 +2280 y(yields)h(a)e(histogram;)f(that)g(is,)i(a)e(table)i(of)f(the)g(n)o(um)o +(b)q(er)h(of)e(o)q(ccurrences)j(of)d(eac)o(h)i(v)m(alue)g(within)h(the)e(b)q +(ounds:)71 2389 y Fi(hist)285 b(::)24 b(\(Ix)f(a,)h(Integral)f(b\))g(=>)h +(\(a,a\))f(->)h([a])f(->)h(Array)f(a)h(b)71 2446 y(hist)f(bnds)g(is)95 +b(=)48 b(accumArray)22 b(\(+\))i(0)g(bnds)f([i)h(:=)f(1)h(|)g(i)f(<-)h(is,)f +(inRange)g(bnds)h(i])0 2557 y Fp(Supp)q(ose)c(w)o(e)e(ha)o(v)o(e)h(a)f +(collection)j(of)d(measuremen)o(ts)h(on)g(the)f(in)o(terv)m(al)i([)p +Fo(a)s Fh(;)8 b Fo(b)s Fp(\))o(,)20 b(and)f(w)o(e)f(w)o(an)o(t)g(to)g(divide) +j(the)0 2614 y(in)o(terv)m(al)16 b(in)o(to)f(decades)h(and)g(coun)o(t)f(the)g +(n)o(um)o(b)q(er)h(of)e(measuremen)o(ts)h(within)i(eac)o(h:)p +eop +%%Page: 51 51 +bop 0 -40 a Fj(9.4)45 b(Incremen)o(tal)16 b(up)q(dates)1348 +b Fp(T-51)71 160 y Fi(decades)213 b(::)24 b(\(RealFrac)f(a\))g(=>)h(a)g(->)f +(a)h(->)f([a])h(->)f(Array)h(Int)f(Int)71 216 y(decades)f(a)i(b)119 +b(=)48 b(hist)23 b(\(0,9\))g(.)h(map)g(decade)524 272 y(where)f(decade)g(x)h +(=)g(floor)f(\(\(x)g(-)h(a\))g(*)f(s\))667 329 y(s)191 b(=)24 +b(10)f(/)h(\(b)g(-)f(a\))0 572 y Fg(9.4)56 b(Incremen)n(tal)16 +b(up)r(dates)0 691 y Fp(In)d(addition)g(to)e(the)i(monolithic)g(arra)o(y)e +(creation)h(functions,)h(Hask)o(ell)g(also)f(has)g(an)g(incremen)o(tal)i +(arra)o(y)c(up)q(date)0 747 y(function,)15 b(written)g(as)f(the)h(in\014x)g +(op)q(erator)f Fi(//)p Fp(;)g(the)h(simplest)h(case,)e(an)h(arra)o(y)e +Fi(a)h Fp(with)h(elemen)o(t)h Fi(i)e Fp(up)q(dated)i(to)0 804 +y Fi(v)p Fp(,)e(is)i(written)e Fi(a)24 b(//)g([i)f(:=)h(v])p +Fp(.)19 b(The)c(reason)g(for)f(the)g(square)h(brac)o(k)o(ets)f(is)h(that)f +(the)h(left)g(argumen)o(t)f(of)h Fi(\(//\))0 860 y Fp(is)h(an)f(asso)q +(ciation)g(list,)h(usually)h(con)o(taining)f(a)e(prop)q(er)i(subset)f(of)g +(the)g(indices)j(of)c(the)i(arra)o(y:)71 969 y Fi(\(//\))285 +b(::)24 b(\(Ix)f(a\))h(=>)g(Array)f(a)h(b)f(->)h([Assoc)f(a)h(b])f(->)h +(Array)f(a)h(b)0 1078 y Fp(As)15 b(with)h(the)f Fi(array)g +Fp(function,)h(the)f(indices)i(in)f(the)g(asso)q(ciation)g(list)g(m)o(ust)e +(b)q(e)i(unique)h(for)e(the)g(v)m(alues)i(to)d(b)q(e)0 1135 +y(de\014ned.)21 b(F)l(or)15 b(example,)g(here)h(is)g(a)f(function)h(to)e(in)o +(terc)o(hange)i(t)o(w)o(o)e(ro)o(ws)g(of)h(a)f(matrix:)71 1243 +y Fi(swapRows)22 b(::)i(\(Ix)f(a,)h(Ix)g(b,)f(Enum)g(b\))h(=>)g(a)f(->)h(a)g +(->)f(Array)g(\(a,b\))h(c)f(->)h(Array)f(\(a,b\))g(c)71 1300 +y(swapRows)f(i)i(i')g(a)f(=)48 b(a)24 b(//)f(\([\(i,j\))47 +b(:=)24 b(a!\(i',j\))e(|)i(j)g(<-)f([jLo..jHi]])g(++)667 1356 +y([\(i',j\))g(:=)h(a!\(i,)f(j\))g(|)h(j)g(<-)f([jLo..jHi]]\))524 +1413 y(where)g(\(\(iLo,jLo\),\(iHi,jHi\)\))e(=)j(bounds)f(a)0 +1522 y Fp(The)15 b(concatenation)f(here)h(of)g(t)o(w)o(o)e(separate)h(list)h +(comprehensions)h(o)o(v)o(er)d(the)i(same)f(list)i(of)e Fi(j)g +Fp(indices)j(is,)d(ho)o(w-)0 1578 y(ev)o(er,)i(a)h(sligh)o(t)g +(ine\016ciency;)i(it's)d(lik)o(e)i(writing)f(t)o(w)o(o)e(lo)q(ops)i(where)g +(one)f(will)i(do)f(in)g(an)g(imp)q(erativ)o(e)g(language.)0 +1635 y(Nev)o(er)e(fear,)g(w)o(e)f(can)i(p)q(erform)f(the)g(equiv)m(alen)o(t)i +(of)e(a)g(lo)q(op)g(fusion)h(optimization)g(in)g(Hask)o(ell:)71 +1744 y Fi(swapRows)22 b(i)i(i')g(a)f(=)48 b(a)24 b(//)f([assoc)g(|)h(j)g(<-)f +([jLo..jHi],)858 1800 y(assoc)g(<-)h([\(i,)f(j\))h(:=)f(a!\(i',j\),)1097 +1857 y(\(i',j\))g(:=)g(a!\(i,)h(j\)])f(])524 1913 y(where)g +(\(\(iLo,jLo\),\(iHi,jHi\)\))e(=)j(bounds)f(a)0 2156 y Fg(9.5)56 +b(An)19 b(example:)j(Matrix)c(Multiplication)0 2275 y Fp(W)l(e)13 +b(complete)h(our)f(in)o(tro)q(duction)h(to)e(Hask)o(ell)i(arra)o(ys)e(with)h +(the)g(familiar)h(example)g(of)e(matrix)h(m)o(ultiplication,)0 +2331 y(taking)k(adv)m(an)o(tage)f(of)g(o)o(v)o(erloading)h(to)f(de\014ne)i(a) +e(fairly)i(general)f(function.)25 b(Since)18 b(only)g(m)o(ultiplication)h +(and)0 2388 y(addition)c(on)f(the)f(elemen)o(t)i(t)o(yp)q(e)f(of)f(the)h +(matrices)g(is)g(in)o(v)o(olv)o(ed,)h(w)o(e)e(get)h(a)f(function)i(that)e(m)o +(ultiplies)j(matrices)0 2444 y(of)h(an)o(y)g(n)o(umeric)h(t)o(yp)q(e)g +(unless)h(w)o(e)e(try)g(hard)g(not)g(to.)26 b(Additionally)l(,)20 +b(if)e(w)o(e)f(are)g(careful)h(to)f(apply)h(only)g Fi(\(!\))0 +2501 y Fp(and)f(the)h(op)q(erations)f(of)g Fi(Ix)g Fp(to)g(indices,)i(w)o(e)e +(get)g(genericit)o(y)h(o)o(v)o(er)e(index)j(t)o(yp)q(es,)e(and)h(in)g(fact,)f +(the)g(four)g(ro)o(w)0 2557 y(and)e(column)h(index)f(t)o(yp)q(es)g(need)h +(not)e(all)i(b)q(e)f(the)g(same.)k(F)l(or)14 b(simplicit)o(y)l(,)j(ho)o(w)o +(ev)o(er,)d(w)o(e)g(require)i(that)e(the)h(left)0 2614 y(column)g(indices)g +(and)f(righ)o(t)g(ro)o(w)e(indices)k(b)q(e)f(of)e(the)h(same)f(t)o(yp)q(e,)h +(and)g(moreo)o(v)o(er,)e(that)h(the)h(b)q(ounds)g(b)q(e)h(equal:)p +eop +%%Page: 52 52 +bop 0 -40 a Fp(T-52)1597 b Fj(9)45 b(ARRA)l(YS)71 160 y Fi(matMult)213 +b(::)24 b(\(Ix)f(a,)h(Ix)g(b,)f(Ix)h(c,)f(Num)h(d\))f(=>)524 +216 y(Array)g(\(a,b\))g(d)h(->)g(Array)f(\(b,c\))g(d)h(->)f(Array)h(\(a,c\))f +(d)71 272 y(matMult)f(x)i(y)119 b(=)48 b(array)23 b(resultBounds)667 +329 y([\(i,j\))g(:=)h(sum)f([x!\(i,k\))g(*)h(y!\(k,j\))f(|)g(k)h(<-)g(range)f +(\(lj,uj\)])1025 385 y(|)h(i)g(<-)f(range)g(\(li,ui\),)1073 +442 y(j)h(<-)f(range)g(\(lj',uj'\))g(])261 498 y(where)h +(\(\(li,lj\),\(ui,uj\)\))212 b(=)48 b(bounds)23 b(x)405 555 +y(\(\(li',lj'\),\(ui',uj'\)\))116 b(=)48 b(bounds)23 b(y)405 +611 y(resultBounds)452 668 y(|)h(\(lj,uj\)==\(li',ui'\))93 +b(=)48 b(\(\(li,lj'\),\(ui,uj'\)\))452 724 y(|)24 b(otherwise)309 +b(=)24 b(error)f("matMult:)g(incompatible)f(bounds")0 833 y +Fp(As)16 b(an)h(aside,)g(w)o(e)f(can)g(also)h(de\014ne)g Fi(matMult)f +Fp(using)h Fi(accumArray)n Fp(,)g(resulting)g(in)g(a)f(presen)o(tation)h +(that)e(more)0 890 y(closely)h(resem)o(bles)g(the)g(usual)g(form)o(ulation)f +(in)h(an)f(imp)q(erativ)o(e)h(language:)71 999 y Fi(matMult)22 +b(x)i(y)119 b(=)48 b(accumArray)22 b(\(+\))i(0)g(resultBounds)786 +1055 y([\(i,j\))g(:=)f(x!\(i,k\))g(*)h(y!\(k,j\))977 1112 y(|)g(i)g(<-)f +(range)h(\(li,ui\),)1025 1168 y(j)g(<-)f(range)h(\(lj',uj'\))1025 +1224 y(k)g(<-)f(range)h(\(lj,uj\))46 b(])261 1281 y(where)24 +b(\(\(li,lj\),\(ui,uj\)\))212 b(=)48 b(bounds)23 b(x)405 1337 +y(\(\(li',lj'\),\(ui',uj'\)\))116 b(=)48 b(bounds)23 b(y)405 +1394 y(resultBounds)452 1450 y(|)h(\(lj,uj\)==\(li',ui'\))93 +b(=)48 b(\(\(li,lj'\),\(ui,uj'\)\))452 1507 y(|)24 b(otherwise)309 +b(=)24 b(error)f("matMult:)g(incompatible)f(bounds")71 1718 +y Fp(W)l(e)16 b(can)h(generalize)h(further)e(b)o(y)h(making)g(the)f(function) +i(higher-order,)f(simply)h(replacing)g Fi(sum)e Fp(and)g Fi(\(*\))0 +1775 y Fp(b)o(y)f(functional)i(parameters:)71 1883 y Fi(genMatMult)141 +b(::)24 b(\(Ix)f(a,)h(Ix)g(b,)f(Ix)h(c\))f(=>)524 1940 y(\([f])g(->)h(g\))f +(->)h(\(d)g(->)f(e)h(->)g(f\))f(->)524 1996 y(Array)g(\(a,b\))g(d)h(->)g +(Array)f(\(b,c\))g(e)h(->)f(Array)h(\(a,c\))f(g)71 2053 y(genMatMult)f(f)i(g) +g(x)f(y)48 b(=)f(array)24 b(resultBounds)643 2109 y([\(i,j\))f(:=)h(f)g +([x!\(i,k\))e(`g`)i(y!\(k,j\))f(|)h(k)f(<-)h(range)f(\(lj,uj\)])858 +2166 y(|)h(i)g(<-)f(range)g(\(li,ui\),)906 2222 y(j)h(<-)f(range)g +(\(lj',uj'\))g(])261 2279 y(where)h(\(\(li,lj\),\(ui,uj\)\))212 +b(=)48 b(bounds)23 b(x)405 2335 y(\(\(li',lj'\),\(ui',uj'\)\))116 +b(=)48 b(bounds)23 b(y)405 2392 y(resultBounds)452 2448 y(|)h +(\(lj,uj\)==\(li',ui'\))93 b(=)48 b(\(\(li,lj'\),\(ui,uj'\)\))452 +2504 y(|)24 b(otherwise)309 b(=)24 b(error)f("matMult:)g(incompatible)f +(bounds")0 2614 y Fp(APL)16 b(fans)f(will)h(recognize)g(the)g(usefulness)g +(of)f(functions)h(lik)o(e)g(the)g(follo)o(wing:)p eop +%%Page: 53 53 +bop 1857 -40 a Fp(T-53)71 160 y Fi(genMatMult)22 b(maximum)h(\(-\))71 +216 y(genMatMult)f(and)i(\(==\))0 328 y Fp(With)15 b(the)g(\014rst)f(of)g +(these,)h(the)g(argumen)o(ts)f(are)g(n)o(umeric)i(matrices,)e(and)h(the)g(\() +p Fo(i)5 b Fh(;)j Fo(j)f Fp(\))m(-th)15 b(elemen)o(t)g(of)g(the)g(result)0 +384 y(is)g(the)g(maxim)o(um)f(di\013erence)i(b)q(et)o(w)o(een)f(corresp)q +(onding)h(elemen)o(ts)f(of)f(the)h Fo(i)5 b Fp(-th)14 b(ro)o(w)g(and)h +Fo(j)6 b Fp(-th)15 b(column)h(of)e(the)0 441 y(inputs.)30 b(In)18 +b(the)h(second)f(case,)h(the)f(argumen)o(ts)f(are)h(matrices)g(of)g(an)o(y)g +(equalit)o(y)h(t)o(yp)q(e,)f(and)h(the)f(result)h(is)f(a)0 +497 y(Bo)q(olean)d(matrix)f(in)h(whic)o(h)g(elemen)o(t)g(\()p +Fo(i)5 b Fh(;)j Fo(j)f Fp(\))k(is)k Fi(True)f Fp(if)g(and)h(only)g(if)f(the)g +Fo(i)5 b Fp(-th)14 b(ro)o(w)g(of)f(the)i(\014rst)e(argumen)o(t)h(and)0 +553 y Fo(j)7 b Fp(-th)15 b(column)h(of)f(the)g(second)h(are)e(equal)i(as)f(v) +o(ectors.)71 629 y(Notice)k(that)f(the)h(elemen)o(t)g(t)o(yp)q(es)g(of)g +Fi(genMatMult)e Fp(need)j(not)e(b)q(e)i(the)e(same,)i(but)e(merely)i +(appropriate)0 685 y(for)c(the)i(function)f(parameter)g Fi(g)p +Fp(.)25 b(W)l(e)17 b(could)h(generalize)h(still)f(further)f(b)o(y)g(dropping) +h(the)f(requiremen)o(t)h(that)0 742 y(the)i(\014rst)f(column)i(index)f(and)g +(second)g(ro)o(w)f(index)i(t)o(yp)q(es)f(b)q(e)g(the)g(same;)h(clearly)l(,)h +(t)o(w)o(o)c(matrices)i(could)g(b)q(e)0 798 y(considered)14 +b(conformable)g(as)e(long)i(as)e(the)i(lengths)f(of)g(the)g(columns)h(of)e +(the)i(\014rst)e(and)i(the)f(ro)o(ws)f(of)g(the)h(second)0 +855 y(are)k(equal.)28 b(The)18 b(reader)g(ma)o(y)f(wish)h(to)f(deriv)o(e)h +(this)h(still)g(more)e(general)h(v)o(ersion.)27 b(\()p Fc(Hin)o(t:)e +Fp(Use)18 b(the)g Fi(index)0 911 y Fp(op)q(eration)d(to)g(determine)h(the)f +(lengths.\))0 1070 y Fq(10)69 b(Ac)n(kno)n(wledgemen)n(ts)0 +1190 y Fp(Thanks)20 b(to)g(P)o(atricia)h(F)l(asel)f(and)h(Mark)f(Mundt)g(at)g +(Los)g(Alamos,)i(and)e(Nic)o(k)h(Carriero,)g(Charles)g(Consel,)0 +1247 y(Amir)f(Kishon,)h(Sandra)e(Lo)q(osemore,)h(Martin)f(Odersky)l(,)h(John) +g(P)o(eterson,)g(and)f(Da)o(vid)h(Ro)q(c)o(h)o(b)q(erg)f(at)g(Y)l(ale)0 +1303 y(Univ)o(ersit)o(y)i(for)f(their)g(quic)o(k)i(readings)e(of)g(earlier)h +(drafts)f(of)g(this)h(man)o(uscript.)35 b(Sp)q(ecial)23 b(thanks)d(to)f(John) +0 1360 y(P)o(eterson)c(for)f(getting)h(the)h(bugs)f(out)g(of)f(our)h(Hask)o +(ell)h(co)q(de.)0 1519 y Fq(References)0 1636 y Fp([1])22 b(R.)d(Bird)h(and)f +(P)l(.)g(W)l(adler.)32 b Fo(Intr)n(o)n(duction)19 b(to)h(F)m(unctional)f(Pr)n +(o)n(gr)n(amming)p Fp(.)31 b(Pren)o(tice)20 b(Hall,)g(New)g(Y)l(ork,)71 +1692 y(1988.)0 1780 y([2])i(P)l(.)14 b(Hudak.)20 b(Conception,)c(ev)o +(olution,)f(and)g(application)i(of)e(functional)h(programming)e(languages.)20 +b Fo(A)o(CM)71 1836 y(Computing)c(Surveys)p Fp(,)e(21\(3\):359{411,)d(1989.)0 +1924 y([3])22 b(P)l(.)c(Hudak,)h(S.)f(P)o(eyton)f(Jones,)i(and)g(P)l(.)f(W)l +(adler)g(\(editors\).)29 b(Rep)q(ort)18 b(on)g(the)h(Programming)e(Language) +71 1981 y(Hask)o(ell,)23 b(A)e(Non-strict)g(Purely)h(Functional)g(Language)f +(\(Version)g(1.2\).)36 b Fo(A)o(CM)20 b(SIGPLAN)g(Notic)n(es)p +Fp(,)71 2037 y(27\(5\),)13 b(Ma)o(y)h(1992.)0 2125 y([4])22 +b(R.S.)c(Nikhil.)30 b(Id)18 b(\(v)o(ersion)g(90.0\))f(reference)h(man)o(ual.) +29 b(T)l(ec)o(hnical)19 b(rep)q(ort,)f(Massac)o(h)o(usetts)f(Institute)h(of) +71 2181 y(T)l(ec)o(hnology)l(,)d(Lab)q(oratory)f(for)h(Computer)g(Science,)i +(Septem)o(b)q(er)e(1990.)0 2269 y([5])22 b(J.)15 b(Rees)h(and)g(W.)f(Clinger) +h(\(eds.\).)k(The)c(revised)928 2252 y Fm(3)964 2269 y Fp(rep)q(ort)f(on)h +(the)f(algorithmic)h(language)g(Sc)o(heme.)21 b Fo(SIG-)71 +2325 y(PLAN)15 b(Notic)n(es)p Fp(,)f(21\(12\):37{79)o(,)e(Decem)o(b)q(er)k +(1986.)0 2413 y([6])22 b(G.L.)14 b(Steele)j(Jr.)i Fo(Common)e(Lisp:)j(The)c +(L)n(anguage)p Fp(.)j(Digital)d(Press,)e(Burlington,)i(Mass.,)e(1984.)0 +2501 y([7])22 b(P)l(.)f(W)l(adler.)39 b(Ho)o(w)21 b(to)g(replace)i(failure)g +(b)o(y)e(a)h(list)g(of)f(successes.)40 b(In)22 b Fo(Pr)n(o)n(c)n(e)n(e)n +(dings)e(of)i(Confer)n(enc)n(e)e(on)71 2557 y(F)m(unctional)e(Pr)n(o)n(gr)n +(amming)h(L)n(anguages)g(and)h(Computer)h(A)o(r)n(chite)n(ctur)n(e,)f(LNCS)e +(V)m(ol.)h(201)p Fp(,)h(pages)f(113{)71 2614 y(128.)14 b(Springer)i(V)l +(erlag,)f(1985.)p eop +%%Trailer +end +userdict /end-hook known{end-hook}if +%%EOF diff --git a/doc/xinterface/xman.dvi b/doc/xinterface/xman.dvi new file mode 100644 index 0000000..0306db9 Binary files /dev/null and b/doc/xinterface/xman.dvi differ diff --git a/emacs-tools/README b/emacs-tools/README new file mode 100644 index 0000000..bb22105 --- /dev/null +++ b/emacs-tools/README @@ -0,0 +1,5 @@ +This directory contains GNU Emacs support for editing Haskell files. +We don't yet have a fancy editing mode, but haskell.el contains stuff +for running Haskell as an inferior process from Emacs with key bindings +for evaluating code from buffers, etc. Look at the comments in haskell.el +for more information. diff --git a/emacs-tools/comint.el b/emacs-tools/comint.el new file mode 100644 index 0000000..e690005 --- /dev/null +++ b/emacs-tools/comint.el @@ -0,0 +1,1524 @@ +;;; -*-Emacs-Lisp-*- General command interpreter in a window stuff +;;; Copyright Olin Shivers (1988). +;;; Please imagine a long, tedious, legalistic 5-page gnu-style copyright +;;; notice appearing here to the effect that you may use this code any +;;; way you like, as long as you don't charge money for it, remove this +;;; notice, or hold me liable for its results. + +;;; The changelog is at the end of this file. + +;;; Please send me bug reports, bug fixes, and extensions, so that I can +;;; merge them into the master source. +;;; - Olin Shivers (shivers@cs.cmu.edu) + +;;; This hopefully generalises shell mode, lisp mode, tea mode, soar mode,... +;;; This file defines a general command-interpreter-in-a-buffer package +;;; (comint mode). The idea is that you can build specific process-in-a-buffer +;;; modes on top of comint mode -- e.g., lisp, shell, scheme, T, soar, .... +;;; This way, all these specific packages share a common base functionality, +;;; and a common set of bindings, which makes them easier to use (and +;;; saves code, implementation time, etc., etc.). + +;;; Several packages are already defined using comint mode: +;;; - cmushell.el defines a shell-in-a-buffer mode. +;;; - cmulisp.el defines a simple lisp-in-a-buffer mode. +;;; Cmushell and cmulisp mode are similar to, and intended to replace, +;;; their counterparts in the standard gnu emacs release (in shell.el). +;;; These replacements are more featureful, robust, and uniform than the +;;; released versions. The key bindings in lisp mode are also more compatible +;;; with the bindings of Hemlock and Zwei (the Lisp Machine emacs). +;;; +;;; - The file cmuscheme.el defines a scheme-in-a-buffer mode. +;;; - The file tea.el tunes scheme and inferior-scheme modes for T. +;;; - The file soar.el tunes lisp and inferior-lisp modes for Soar. +;;; - cmutex.el defines tex and latex modes that invoke tex, latex, bibtex, +;;; previewers, and printers from within emacs. +;;; - background.el allows csh-like job control inside emacs. +;;; It is pretty easy to make new derived modes for other processes. + +;;; For documentation on the functionality provided by comint mode, and +;;; the hooks available for customising it, see the comments below. +;;; For further information on the standard derived modes (shell, +;;; inferior-lisp, inferior-scheme, ...), see the relevant source files. + +;;; For hints on converting existing process modes (e.g., tex-mode, +;;; background, dbx, gdb, kermit, prolog, telnet) to use comint-mode +;;; instead of shell-mode, see the notes at the end of this file. + +(provide 'comint) +(defconst comint-version "2.01") + + + + + + + + + + + + + + + + + + + + + +;;; Brief Command Documentation: +;;;============================================================================ +;;; Comint Mode Commands: (common to all derived modes, like cmushell & cmulisp +;;; mode) +;;; +;;; m-p comint-previous-input Cycle backwards in input history +;;; m-n comint-next-input Cycle forwards +;;; m-s comint-previous-similar-input Previous similar input +;;; c-c r comint-previous-input-matching Search backwards in input history +;;; return comint-send-input +;;; c-a comint-bol Beginning of line; skip prompt. +;;; c-d comint-delchar-or-maybe-eof Delete char unless at end of buff. +;;; c-c c-u comint-kill-input ^u +;;; c-c c-w backward-kill-word ^w +;;; c-c c-c comint-interrupt-subjob ^c +;;; c-c c-z comint-stop-subjob ^z +;;; c-c c-\ comint-quit-subjob ^\ +;;; c-c c-o comint-kill-output Delete last batch of process output +;;; c-c c-r comint-show-output Show last batch of process output +;;; +;;; Not bound by default in comint-mode +;;; send-invisible Read a line w/o echo, and send to proc +;;; (These are bound in shell-mode) +;;; comint-dynamic-complete Complete filename at point. +;;; comint-dynamic-list-completions List completions in help buffer. +;;; comint-replace-by-expanded-filename Expand and complete filename at point; +;;; replace with expanded/completed name. +;;; comint-kill-subjob No mercy. +;;; comint-continue-subjob Send CONT signal to buffer's process +;;; group. Useful if you accidentally +;;; suspend your process (with C-c C-z). +;;; +;;; Bound for RMS -- I prefer the input history stuff, but you might like 'em. +;;; m-P comint-msearch-input Search backwards for prompt +;;; m-N comint-psearch-input Search forwards for prompt +;;; C-cR comint-msearch-input-matching Search backwards for prompt & string + +;;; comint-mode-hook is the comint mode hook. Basically for your keybindings. +;;; comint-load-hook is run after loading in this package. + + + + + +;;; Buffer Local Variables: +;;;============================================================================ +;;; Comint mode buffer local variables: +;;; comint-prompt-regexp - string comint-bol uses to match prompt. +;;; comint-last-input-end - marker For comint-kill-output command +;;; input-ring-size - integer For the input history +;;; input-ring - ring mechanism +;;; input-ring-index - marker ... +;;; comint-last-input-match - string ... +;;; comint-get-old-input - function Hooks for specific +;;; comint-input-sentinel - function process-in-a-buffer +;;; comint-input-filter - function modes. +;;; comint-input-send - function +;;; comint-eol-on-send - boolean + +(defvar comint-prompt-regexp "^" + "Regexp to recognise prompts in the inferior process. +Defaults to \"^\", the null string at BOL. + +Good choices: + Canonical Lisp: \"^[^> ]*>+:? *\" (Lucid, franz, kcl, T, cscheme, oaklisp) + Lucid Common Lisp: \"^\\(>\\|\\(->\\)+\\) *\" + franz: \"^\\(->\\|<[0-9]*>:\\) *\" + kcl: \"^>+ *\" + shell: \"^[^#$%>]*[#$%>] *\" + T: \"^>+ *\" + +This is a good thing to set in mode hooks.") + +(defvar input-ring-size 30 + "Size of input history ring.") + +;;; Here are the per-interpreter hooks. +(defvar comint-get-old-input (function comint-get-old-input-default) + "Function that submits old text in comint mode. +This function is called when return is typed while the point is in old text. +It returns the text to be submitted as process input. The default is +comint-get-old-input-default, which grabs the current line, and strips off +leading text matching comint-prompt-regexp") + +(defvar comint-input-sentinel (function ignore) + "Called on each input submitted to comint mode process by comint-send-input. +Thus it can, for instance, track cd/pushd/popd commands issued to the csh.") + +(defvar comint-input-filter + (function (lambda (str) (not (string-match "\\`\\s *\\'" str)))) + "Predicate for filtering additions to input history. +Only inputs answering true to this function are saved on the input +history list. Default is to save anything that isn't all whitespace") + +(defvar comint-input-sender (function comint-simple-send) + "Function to actually send to PROCESS the STRING submitted by user. +Usually this is just 'comint-simple-send, but if your mode needs to +massage the input string, this is your hook. This is called from +the user command comint-send-input. comint-simple-send just sends +the string plus a newline.") + +(defvar comint-eol-on-send 'T + "If non-nil, then jump to the end of the line before sending input to process. +See COMINT-SEND-INPUT") + +(defvar comint-mode-hook '() + "Called upon entry into comint-mode") + +(defvar comint-mode-map nil) + +(defun comint-mode () + "Major mode for interacting with an inferior interpreter. +Interpreter name is same as buffer name, sans the asterisks. +Return at end of buffer sends line as input. +Return not at end copies rest of line to end and sends it. +Setting mode variable comint-eol-on-send means jump to the end of the line +before submitting new input. + +This mode is typically customised to create inferior-lisp-mode, +shell-mode, etc.. This can be done by setting the hooks +comint-input-sentinel, comint-input-filter, comint-input-sender and +comint-get-old-input to appropriate functions, and the variable +comint-prompt-regexp to the appropriate regular expression. + +An input history is maintained of size input-ring-size, and +can be accessed with the commands comint-next-input [\\[comint-next-input]] and +comint-previous-input [\\[comint-previous-input]]. Commands not keybound by +default are send-invisible, comint-dynamic-complete, and +comint-list-dynamic-completions. + +If you accidentally suspend your process, use \\[comint-continue-subjob] +to continue it. + +\\{comint-mode-map} + +Entry to this mode runs the hooks on comint-mode-hook" + (interactive) + (let ((old-ring (and (assq 'input-ring (buffer-local-variables)) + (boundp 'input-ring) + input-ring)) + (old-ptyp comint-ptyp)) ; preserve across local var kill. gross. + (kill-all-local-variables) + (setq major-mode 'comint-mode) + (setq mode-name "Comint") + (setq mode-line-process '(": %s")) + (use-local-map comint-mode-map) + (make-local-variable 'comint-last-input-end) + (setq comint-last-input-end (make-marker)) + (make-local-variable 'comint-last-input-match) + (setq comint-last-input-match "") + (make-local-variable 'comint-prompt-regexp) ; Don't set; default + (make-local-variable 'input-ring-size) ; ...to global val. + (make-local-variable 'input-ring) + (make-local-variable 'input-ring-index) + (setq input-ring-index 0) + (make-local-variable 'comint-get-old-input) + (make-local-variable 'comint-input-sentinel) + (make-local-variable 'comint-input-filter) + (make-local-variable 'comint-input-sender) + (make-local-variable 'comint-eol-on-send) + (make-local-variable 'comint-ptyp) + (setq comint-ptyp old-ptyp) + (run-hooks 'comint-mode-hook) + ;Do this after the hook so the user can mung INPUT-RING-SIZE w/his hook. + ;The test is so we don't lose history if we run comint-mode twice in + ;a buffer. + (setq input-ring (if (ring-p old-ring) old-ring + (make-ring input-ring-size))))) + +;;; The old-ptyp stuff above is because we have to preserve the value of +;;; comint-ptyp across calls to comint-mode, in spite of the +;;; kill-all-local-variables that it does. Blech. Hopefully, this will all +;;; go away when a later release fixes the signalling bug. + +(if comint-mode-map + nil + (setq comint-mode-map (make-sparse-keymap)) + (define-key comint-mode-map "\ep" 'comint-previous-input) + (define-key comint-mode-map "\en" 'comint-next-input) + (define-key comint-mode-map "\es" 'comint-previous-similar-input) + (define-key comint-mode-map "\C-m" 'comint-send-input) + (define-key comint-mode-map "\C-d" 'comint-delchar-or-maybe-eof) + (define-key comint-mode-map "\C-a" 'comint-bol) + (define-key comint-mode-map "\C-c\C-u" 'comint-kill-input) + (define-key comint-mode-map "\C-c\C-w" 'backward-kill-word) + (define-key comint-mode-map "\C-c\C-c" 'comint-interrupt-subjob) + (define-key comint-mode-map "\C-c\C-z" 'comint-stop-subjob) + (define-key comint-mode-map "\C-c\C-\\" 'comint-quit-subjob) + (define-key comint-mode-map "\C-c\C-o" 'comint-kill-output) + (define-key comint-mode-map "\C-cr" 'comint-previous-input-matching) + (define-key comint-mode-map "\C-c\C-r" 'comint-show-output) + ;;; Here's the prompt-search stuff I installed for RMS to try... + (define-key comint-mode-map "\eP" 'comint-msearch-input) + (define-key comint-mode-map "\eN" 'comint-psearch-input) + (define-key comint-mode-map "\C-cR" 'comint-msearch-input-matching)) + + +;;; This function is used to make a full copy of the comint mode map, +;;; so that client modes won't interfere with each other. This function +;;; isn't necessary in emacs 18.5x, but we keep it around for 18.4x versions. +(defun full-copy-sparse-keymap (km) + "Recursively copy the sparse keymap KM" + (cond ((consp km) + (cons (full-copy-sparse-keymap (car km)) + (full-copy-sparse-keymap (cdr km)))) + (t km))) + +(defun comint-check-proc (buffer-name) + "True if there is a process associated w/buffer BUFFER-NAME, and +it is alive (status RUN or STOP)." + (let ((proc (get-buffer-process buffer-name))) + (and proc (memq (process-status proc) '(run stop))))) + +;;; Note that this guy, unlike shell.el's make-shell, barfs if you pass it () +;;; for the second argument (program). +(defun make-comint (name program &optional startfile &rest switches) + (let* ((buffer (get-buffer-create (concat "*" name "*"))) + (proc (get-buffer-process buffer))) + ;; If no process, or nuked process, crank up a new one and put buffer in + ;; comint mode. Otherwise, leave buffer and existing process alone. + (cond ((or (not proc) (not (memq (process-status proc) '(run stop)))) + (save-excursion + (set-buffer buffer) + (comint-mode)) ; Install local vars, mode, keymap, ... + (comint-exec buffer name program startfile switches))) + buffer)) + +(defvar comint-ptyp t + "True if communications via pty; false if by pipe. Buffer local. +This is to work around a bug in emacs process signalling.") + +(defun comint-exec (buffer name command startfile switches) + "Fires up a process in buffer for comint modes. +Blasts any old process running in the buffer. Doesn't set the buffer mode. +You can use this to cheaply run a series of processes in the same comint +buffer." + (save-excursion + (set-buffer buffer) + (let ((proc (get-buffer-process buffer))) ; Blast any old process. + (if proc (delete-process proc))) + ;; Crank up a new process + (let ((proc (comint-exec-1 name buffer command switches))) + (make-local-variable 'comint-ptyp) + (setq comint-ptyp process-connection-type) ; T if pty, NIL if pipe. + ;; Jump to the end, and set the process mark. + (goto-char (point-max)) + (set-marker (process-mark proc) (point))) + ;; Feed it the startfile. + (cond (startfile + ;;This is guaranteed to wait long enough + ;;but has bad results if the comint does not prompt at all + ;; (while (= size (buffer-size)) + ;; (sleep-for 1)) + ;;I hope 1 second is enough! + (sleep-for 1) + (goto-char (point-max)) + (insert-file-contents startfile) + (setq startfile (buffer-substring (point) (point-max))) + (delete-region (point) (point-max)) + (comint-send-string proc startfile))) + buffer)) + +;;; This auxiliary function cranks up the process for comint-exec in +;;; the appropriate environment. It is twice as long as it should be +;;; because emacs has two distinct mechanisms for manipulating the +;;; process environment, selected at compile time with the +;;; MAINTAIN-ENVIRONMENT #define. In one case, process-environment +;;; is bound; in the other it isn't. + +(defun comint-exec-1 (name buffer command switches) + (if (boundp 'process-environment) ; Not a completely reliable test. + (let ((process-environment + (comint-update-env process-environment + (list (format "TERMCAP=emacs:co#%d:tc=unknown" + (screen-width)) + "TERM=emacs" + "EMACS=t")))) + (apply 'start-process name buffer command switches)) + + (let ((tcapv (getenv "TERMCAP")) + (termv (getenv "TERM")) + (emv (getenv "EMACS"))) + (unwind-protect + (progn (setenv "TERMCAP" (format "emacs:co#%d:tc=unknown" + (screen-width))) + (setenv "TERM" "emacs") + (setenv "EMACS" "t") + (apply 'start-process name buffer command switches)) + (setenv "TERMCAP" tcapv) + (setenv "TERM" termv) + (setenv "EMACS" emv))))) + + + +;; This is just (append new old-env) that compresses out shadowed entries. +;; It's also pretty ugly, mostly due to elisp's horrible iteration structures. +(defun comint-update-env (old-env new) + (let ((ans (reverse new)) + (vars (mapcar (function (lambda (vv) + (and (string-match "^[^=]*=" vv) + (substring vv 0 (match-end 0))))) + new))) + (while old-env + (let* ((vv (car old-env)) ; vv is var=value + (var (and (string-match "^[^=]*=" vv) + (substring vv 0 (match-end 0))))) + (setq old-env (cdr old-env)) + (cond ((not (and var (comint-mem var vars))) + (if var (setq var (cons var vars))) + (setq ans (cons vv ans)))))) + (nreverse ans))) + +;;; This should be in emacs, but it isn't. +(defun comint-mem (item list &optional elt=) + "Test to see if ITEM is equal to an item in LIST. +Option comparison function ELT= defaults to equal." + (let ((elt= (or elt= (function equal))) + (done nil)) + (while (and list (not done)) + (if (funcall elt= item (car list)) + (setq done list) + (setq list (cdr list)))) + done)) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +;;; Ring Code +;;;============================================================================ +;;; This code defines a ring data structure. A ring is a +;;; (hd-index tl-index . vector) +;;; list. You can insert to, remove from, and rotate a ring. When the ring +;;; fills up, insertions cause the oldest elts to be quietly dropped. +;;; +;;; HEAD = index of the newest item on the ring. +;;; TAIL = index of the oldest item on the ring. +;;; +;;; These functions are used by the input history mechanism, but they can +;;; be used for other purposes as well. + +(defun ring-p (x) + "T if X is a ring; NIL otherwise." + (and (consp x) (integerp (car x)) + (consp (cdr x)) (integerp (car (cdr x))) + (vectorp (cdr (cdr x))))) + +(defun make-ring (size) + "Make a ring that can contain SIZE elts" + (cons 1 (cons 0 (make-vector (+ size 1) nil)))) + +(defun ring-plus1 (index veclen) + "INDEX+1, with wraparound" + (let ((new-index (+ index 1))) + (if (= new-index veclen) 0 new-index))) + +(defun ring-minus1 (index veclen) + "INDEX-1, with wraparound" + (- (if (= 0 index) veclen index) 1)) + +(defun ring-length (ring) + "Number of elts in the ring." + (let ((hd (car ring)) (tl (car (cdr ring))) (siz (length (cdr (cdr ring))))) + (let ((len (if (<= hd tl) (+ 1 (- tl hd)) (+ 1 tl (- siz hd))))) + (if (= len siz) 0 len)))) + +(defun ring-empty-p (ring) + (= 0 (ring-length ring))) + +(defun ring-insert (ring item) + "Insert a new item onto the ring. If the ring is full, dump the oldest +item to make room." + (let* ((vec (cdr (cdr ring))) (len (length vec)) + (new-hd (ring-minus1 (car ring) len))) + (setcar ring new-hd) + (aset vec new-hd item) + (if (ring-empty-p ring) ;overflow -- dump one off the tail. + (setcar (cdr ring) (ring-minus1 (car (cdr ring)) len))))) + +(defun ring-remove (ring) + "Remove the oldest item retained on the ring." + (if (ring-empty-p ring) (error "Ring empty") + (let ((tl (car (cdr ring))) (vec (cdr (cdr ring)))) + (set-car (cdr ring) (ring-minus1 tl (length vec))) + (aref vec tl)))) + +;;; This isn't actually used in this package. I just threw it in in case +;;; someone else wanted it. If you want rotating-ring behavior on your history +;;; retrieval (analagous to kill ring behavior), this function is what you +;;; need. I should write the yank-input and yank-pop-input-or-kill to go with +;;; this, and not bind it to a key by default, so it would be available to +;;; people who want to bind it to a key. But who would want it? Blech. +(defun ring-rotate (ring n) + (if (not (= n 0)) + (if (ring-empty-p ring) ;Is this the right error check? + (error "ring empty") + (let ((hd (car ring)) (tl (car (cdr ring))) (vec (cdr (cdr ring)))) + (let ((len (length vec))) + (while (> n 0) + (setq tl (ring-plus1 tl len)) + (aset ring tl (aref ring hd)) + (setq hd (ring-plus1 hd len)) + (setq n (- n 1))) + (while (< n 0) + (setq hd (ring-minus1 hd len)) + (aset vec hd (aref vec tl)) + (setq tl (ring-minus1 tl len)) + (setq n (- n 1)))) + (set-car ring hd) + (set-car (cdr ring) tl))))) + +(defun comint-mod (n m) + "Returns N mod M. M is positive. Answer is guaranteed to be non-negative, +and less than m." + (let ((n (% n m))) + (if (>= n 0) n + (+ n + (if (>= m 0) m (- m)))))) ; (abs m) + +(defun ring-ref (ring index) + (let ((numelts (ring-length ring))) + (if (= numelts 0) (error "indexed empty ring") + (let* ((hd (car ring)) (tl (car (cdr ring))) (vec (cdr (cdr ring))) + (index (comint-mod index numelts)) + (vec-index (comint-mod (+ index hd) + (length vec)))) + (aref vec vec-index))))) + + +;;; Input history retrieval commands +;;; M-p -- previous input M-n -- next input +;;; C-c r -- previous input matching +;;; =========================================================================== + +(defun comint-previous-input (arg) + "Cycle backwards through input history." + (interactive "*p") + (let ((len (ring-length input-ring))) + (cond ((<= len 0) + (message "Empty input ring") + (ding)) + ((not (comint-after-pmark-p)) + (message "Not after process mark") + (ding)) + (t + (cond ((eq last-command 'comint-previous-input) + (delete-region (mark) (point))) + ((eq last-command 'comint-previous-similar-input) + (delete-region + (process-mark (get-buffer-process (current-buffer))) + (point))) + (t + (setq input-ring-index + (if (> arg 0) -1 + (if (< arg 0) 1 0))) + (push-mark (point)))) + (setq input-ring-index (comint-mod (+ input-ring-index arg) len)) + (message "%d" (1+ input-ring-index)) + (insert (ring-ref input-ring input-ring-index)) + (setq this-command 'comint-previous-input))))) + +(defun comint-next-input (arg) + "Cycle forwards through input history." + (interactive "*p") + (comint-previous-input (- arg))) + +(defvar comint-last-input-match "" + "Last string searched for by comint input history search, for defaulting. +Buffer local variable.") + +(defun comint-previous-input-matching (str) + "Searches backwards through input history for substring match." + (interactive (let* ((last-command last-command) ; preserve around r-f-m + (s (read-from-minibuffer + (format "Command substring (default %s): " + comint-last-input-match)))) + (list (if (string= s "") comint-last-input-match s)))) +; (interactive "sCommand substring: ") + (setq comint-last-input-match str) ; update default + (if (not (eq last-command 'comint-previous-input)) + (setq input-ring-index -1)) + (let ((str (regexp-quote str)) + (len (ring-length input-ring)) + (n (+ input-ring-index 1))) + (while (and (< n len) (not (string-match str (ring-ref input-ring n)))) + (setq n (+ n 1))) + (cond ((< n len) + (comint-previous-input (- n input-ring-index))) + (t (if (eq last-command 'comint-previous-input) + (setq this-command 'comint-previous-input)) + (message "Not found.") + (ding))))) + + +;;; These next three commands are alternatives to the input history commands -- +;;; comint-next-input, comint-previous-input and +;;; comint-previous-input-matching. They search through the process buffer +;;; text looking for occurrences of the prompt. RMS likes them better; +;;; I don't. Bound to M-P, M-N, and C-c R (uppercase P, N, and R) for +;;; now. Try'em out. Go with what you like... + +;;; comint-msearch-input-matching prompts for a string, not a regexp. +;;; This could be considered to be the wrong thing. I decided to keep it +;;; simple, and not make the user worry about regexps. This, of course, +;;; limits functionality. + +(defun comint-psearch-input () + "Search forwards for next occurrence of prompt and skip to end of line. +\(prompt is anything matching regexp comint-prompt-regexp)" + (interactive) + (if (re-search-forward comint-prompt-regexp (point-max) t) + (end-of-line) + (error "No occurrence of prompt found"))) + +(defun comint-msearch-input () + "Search backwards for previous occurrence of prompt and skip to end of line. +Search starts from beginning of current line." + (interactive) + (let ((p (save-excursion + (beginning-of-line) + (cond ((re-search-backward comint-prompt-regexp (point-min) t) + (end-of-line) + (point)) + (t nil))))) + (if p (goto-char p) + (error "No occurrence of prompt found")))) + +(defun comint-msearch-input-matching (str) + "Search backwards for occurrence of prompt followed by STRING. +STRING is prompted for, and is NOT a regular expression." + (interactive (let ((s (read-from-minibuffer + (format "Command (default %s): " + comint-last-input-match)))) + (list (if (string= s "") comint-last-input-match s)))) +; (interactive "sCommand: ") + (setq comint-last-input-match str) ; update default + (let* ((r (concat comint-prompt-regexp (regexp-quote str))) + (p (save-excursion + (beginning-of-line) + (cond ((re-search-backward r (point-min) t) + (end-of-line) + (point)) + (t nil))))) + (if p (goto-char p) + (error "No match")))) + +;;; +;;; Similar input -- contributed by ccm and highly winning. +;;; +;;; Reenter input, removing back to the last insert point if it exists. +;;; +(defvar comint-last-similar-string "" + "The string last used in a similar string search.") +(defun comint-previous-similar-input (arg) + "Reenters the last input that matches the string typed so far. If repeated +successively older inputs are reentered. If arg is 1, it will go back +in the history, if -1 it will go forward." + (interactive "p") + (if (not (comint-after-pmark-p)) + (error "Not after process mark")) + (if (not (eq last-command 'comint-previous-similar-input)) + (setq input-ring-index -1 + comint-last-similar-string + (buffer-substring + (process-mark (get-buffer-process (current-buffer))) + (point)))) + (let* ((size (length comint-last-similar-string)) + (len (ring-length input-ring)) + (n (+ input-ring-index arg)) + entry) + (while (and (< n len) + (or (< (length (setq entry (ring-ref input-ring n))) size) + (not (equal comint-last-similar-string + (substring entry 0 size))))) + (setq n (+ n arg))) + (cond ((< n len) + (setq input-ring-index n) + (if (eq last-command 'comint-previous-similar-input) + (delete-region (mark) (point)) ; repeat + (push-mark (point))) ; 1st time + (insert (substring entry size))) + (t (message "Not found.") (ding) (sit-for 1))) + (message "%d" (1+ input-ring-index)))) + + + + + + + + + +(defun comint-send-input () + "Send input to process. After the process output mark, sends all text +from the process mark to point as input to the process. Before the +process output mark, calls value of variable comint-get-old-input to retrieve +old input, copies it to the end of the buffer, and sends it. A terminal +newline is also inserted into the buffer and sent to the process. In either +case, value of variable comint-input-sentinel is called on the input before +sending it. The input is entered into the input history ring, if value of +variable comint-input-filter returns non-nil when called on the input. + +If variable comint-eol-on-send is non-nil, then point is moved to the end of +line before sending the input. + +comint-get-old-input, comint-input-sentinel, and comint-input-filter are chosen +according to the command interpreter running in the buffer. E.g., +If the interpreter is the csh, + comint-get-old-input is the default: take the current line, discard any + initial string matching regexp comint-prompt-regexp. + comint-input-sentinel monitors input for \"cd\", \"pushd\", and \"popd\" + commands. When it sees one, it cd's the buffer. + comint-input-filter is the default: returns T if the input isn't all white + space. + +If the comint is Lucid Common Lisp, + comint-get-old-input snarfs the sexp ending at point. + comint-input-sentinel does nothing. + comint-input-filter returns NIL if the input matches input-filter-regexp, + which matches (1) all whitespace (2) :a, :c, etc. + +Similarly for Soar, Scheme, etc.." + (interactive) + ;; Note that the input string does not include its terminal newline. + (let ((proc (get-buffer-process (current-buffer)))) + (if (not proc) (error "Current buffer has no process") + (let* ((pmark (process-mark proc)) + (pmark-val (marker-position pmark)) + (input (if (>= (point) pmark-val) + (progn (if comint-eol-on-send (end-of-line)) + (buffer-substring pmark (point))) + (let ((copy (funcall comint-get-old-input))) + (goto-char pmark) + (insert copy) + copy)))) + (insert ?\n) + (if (funcall comint-input-filter input) (ring-insert input-ring input)) + (funcall comint-input-sentinel input) + (funcall comint-input-sender proc input) + (set-marker (process-mark proc) (point)) + (set-marker comint-last-input-end (point)))))) + +(defun comint-get-old-input-default () + "Default for comint-get-old-input: take the current line, and discard +any initial text matching comint-prompt-regexp." + (save-excursion + (beginning-of-line) + (comint-skip-prompt) + (let ((beg (point))) + (end-of-line) + (buffer-substring beg (point))))) + +(defun comint-skip-prompt () + "Skip past the text matching regexp comint-prompt-regexp. +If this takes us past the end of the current line, don't skip at all." + (let ((eol (save-excursion (end-of-line) (point)))) + (if (and (looking-at comint-prompt-regexp) + (<= (match-end 0) eol)) + (goto-char (match-end 0))))) + + +(defun comint-after-pmark-p () + "Is point after the process output marker?" + ;; Since output could come into the buffer after we looked at the point + ;; but before we looked at the process marker's value, we explicitly + ;; serialise. This is just because I don't know whether or not emacs + ;; services input during execution of lisp commands. + (let ((proc-pos (marker-position + (process-mark (get-buffer-process (current-buffer)))))) + (<= proc-pos (point)))) + +(defun comint-simple-send (proc string) + "Default function for sending to PROC input STRING. +This just sends STRING plus a newline. To override this, +set the hook COMINT-INPUT-SENDER." + (comint-send-string proc string) + (comint-send-string proc "\n")) + +(defun comint-bol (arg) + "Goes to the beginning of line, then skips past the prompt, if any. +If a prefix argument is given (\\[universal-argument]), then no prompt skip +-- go straight to column 0. + +The prompt skip is done by skipping text matching the regular expression +comint-prompt-regexp, a buffer local variable. + +If you don't like this command, reset c-a to beginning-of-line +in your hook, comint-mode-hook." + (interactive "P") + (beginning-of-line) + (if (null arg) (comint-skip-prompt))) + +;;; These two functions are for entering text you don't want echoed or +;;; saved -- typically passwords to ftp, telnet, or somesuch. +;;; Just enter m-x send-invisible and type in your line. + +(defun comint-read-noecho (prompt) + "Prompt the user with argument PROMPT. Read a single line of text +without echoing, and return it. Note that the keystrokes comprising +the text can still be recovered (temporarily) with \\[view-lossage]. This +may be a security bug for some applications." + (let ((echo-keystrokes 0) + (answ "") + tem) + (if (and (stringp prompt) (not (string= (message prompt) ""))) + (message prompt)) + (while (not(or (= (setq tem (read-char)) ?\^m) + (= tem ?\n))) + (setq answ (concat answ (char-to-string tem)))) + (message "") + answ)) + +(defun send-invisible (str) + "Read a string without echoing, and send it to the process running +in the current buffer. A new-line is additionally sent. String is not +saved on comint input history list. +Security bug: your string can still be temporarily recovered with +\\[view-lossage]." +; (interactive (list (comint-read-noecho "Enter non-echoed text"))) + (interactive "P") ; Defeat snooping via C-x esc + (let ((proc (get-buffer-process (current-buffer)))) + (if (not proc) (error "Current buffer has no process") + (comint-send-string proc + (if (stringp str) str + (comint-read-noecho "Enter non-echoed text"))) + (comint-send-string proc "\n")))) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +;;; Low-level process communication + +(defvar comint-input-chunk-size 512 + "*Long inputs send to comint processes are broken up into chunks of this size. +If your process is choking on big inputs, try lowering the value.") + +(defun comint-send-string (proc str) + "Send PROCESS the contents of STRING as input. +This is equivalent to process-send-string, except that long input strings +are broken up into chunks of size comint-input-chunk-size. Processes +are given a chance to output between chunks. This can help prevent processes +from hanging when you send them long inputs on some OS's." + (let* ((len (length str)) + (i (min len comint-input-chunk-size))) + (process-send-string proc (substring str 0 i)) + (while (< i len) + (let ((next-i (+ i comint-input-chunk-size))) + (accept-process-output) + (process-send-string proc (substring str i (min len next-i))) + (setq i next-i))))) + +(defun comint-send-region (proc start end) + "Sends to PROC the region delimited by START and END. +This is a replacement for process-send-region that tries to keep +your process from hanging on long inputs. See comint-send-string." + (comint-send-string proc (buffer-substring start end))) + + + + + + + + + + + + + + + + + + +;;; Random input hackage + +(defun comint-kill-output () + "Kill all output from interpreter since last input." + (interactive) + (let ((pmark (process-mark (get-buffer-process (current-buffer))))) + (kill-region comint-last-input-end pmark) + (goto-char pmark) + (insert "*** output flushed ***\n") + (set-marker pmark (point)))) + +(defun comint-show-output () + "Display start of this batch of interpreter output at top of window. +Also put cursor there." + (interactive) + (goto-char comint-last-input-end) + (backward-char) + (beginning-of-line) + (set-window-start (selected-window) (point)) + (end-of-line)) + +(defun comint-interrupt-subjob () + "Interrupt the current subjob." + (interactive) + (interrupt-process nil comint-ptyp)) + +(defun comint-kill-subjob () + "Send kill signal to the current subjob." + (interactive) + (kill-process nil comint-ptyp)) + +(defun comint-quit-subjob () + "Send quit signal to the current subjob." + (interactive) + (quit-process nil comint-ptyp)) + +(defun comint-stop-subjob () + "Stop the current subjob. +WARNING: if there is no current subjob, you can end up suspending +the top-level process running in the buffer. If you accidentally do +this, use \\[comint-continue-subjob] to resume the process. (This +is not a problem with most shells, since they ignore this signal.)" + (interactive) + (stop-process nil comint-ptyp)) + +(defun comint-continue-subjob () + "Send CONT signal to process buffer's process group. +Useful if you accidentally suspend the top-level process." + (interactive) + (continue-process nil comint-ptyp)) + +(defun comint-kill-input () + "Kill all text from last stuff output by interpreter to point." + (interactive) + (let* ((pmark (process-mark (get-buffer-process (current-buffer)))) + (p-pos (marker-position pmark))) + (if (> (point) p-pos) + (kill-region pmark (point))))) + +(defun comint-delchar-or-maybe-eof (arg) + "Delete ARG characters forward, or send an EOF to process if at end of buffer." + (interactive "p") + (if (eobp) + (process-send-eof) + (delete-char arg))) + + + + + + + + + + + + + + + + + + + + + + + +;;; Support for source-file processing commands. +;;;============================================================================ +;;; Many command-interpreters (e.g., Lisp, Scheme, Soar) have +;;; commands that process files of source text (e.g. loading or compiling +;;; files). So the corresponding process-in-a-buffer modes have commands +;;; for doing this (e.g., lisp-load-file). The functions below are useful +;;; for defining these commands. +;;; +;;; Alas, these guys don't do exactly the right thing for Lisp, Scheme +;;; and Soar, in that they don't know anything about file extensions. +;;; So the compile/load interface gets the wrong default occasionally. +;;; The load-file/compile-file default mechanism could be smarter -- it +;;; doesn't know about the relationship between filename extensions and +;;; whether the file is source or executable. If you compile foo.lisp +;;; with compile-file, then the next load-file should use foo.bin for +;;; the default, not foo.lisp. This is tricky to do right, particularly +;;; because the extension for executable files varies so much (.o, .bin, +;;; .lbin, .mo, .vo, .ao, ...). + + +;;; COMINT-SOURCE-DEFAULT -- determines defaults for source-file processing +;;; commands. +;;; +;;; COMINT-CHECK-SOURCE -- if FNAME is in a modified buffer, asks you if you +;;; want to save the buffer before issuing any process requests to the command +;;; interpreter. +;;; +;;; COMINT-GET-SOURCE -- used by the source-file processing commands to prompt +;;; for the file to process. + +;;; (COMINT-SOURCE-DEFAULT previous-dir/file source-modes) +;;;============================================================================ +;;; This function computes the defaults for the load-file and compile-file +;;; commands for tea, soar, cmulisp, and cmuscheme modes. +;;; +;;; - PREVIOUS-DIR/FILE is a pair (directory . filename) from the last +;;; source-file processing command. NIL if there hasn't been one yet. +;;; - SOURCE-MODES is a list used to determine what buffers contain source +;;; files: if the major mode of the buffer is in SOURCE-MODES, it's source. +;;; Typically, (lisp-mode) or (scheme-mode). +;;; +;;; If the command is given while the cursor is inside a string, *and* +;;; the string is an existing filename, *and* the filename is not a directory, +;;; then the string is taken as default. This allows you to just position +;;; your cursor over a string that's a filename and have it taken as default. +;;; +;;; If the command is given in a file buffer whose major mode is in +;;; SOURCE-MODES, then the the filename is the default file, and the +;;; file's directory is the default directory. +;;; +;;; If the buffer isn't a source file buffer (e.g., it's the process buffer), +;;; then the default directory & file are what was used in the last source-file +;;; processing command (i.e., PREVIOUS-DIR/FILE). If this is the first time +;;; the command has been run (PREVIOUS-DIR/FILE is nil), the default directory +;;; is the cwd, with no default file. (\"no default file\" = nil) +;;; +;;; SOURCE-REGEXP is typically going to be something like (tea-mode) +;;; for T programs, (lisp-mode) for Lisp programs, (soar-mode lisp-mode) +;;; for Soar programs, etc. +;;; +;;; The function returns a pair: (default-directory . default-file). + +(defun comint-source-default (previous-dir/file source-modes) + (cond ((and buffer-file-name (memq major-mode source-modes)) + (cons (file-name-directory buffer-file-name) + (file-name-nondirectory buffer-file-name))) + (previous-dir/file) + (t + (cons default-directory nil)))) + + +;;; (COMINT-CHECK-SOURCE fname) +;;;============================================================================ +;;; Prior to loading or compiling (or otherwise processing) a file (in the CMU +;;; process-in-a-buffer modes), this function can be called on the filename. +;;; If the file is loaded into a buffer, and the buffer is modified, the user +;;; is queried to see if he wants to save the buffer before proceeding with +;;; the load or compile. + +(defun comint-check-source (fname) + (let ((buff (get-file-buffer fname))) + (if (and buff + (buffer-modified-p buff) + (y-or-n-p (format "Save buffer %s first? " + (buffer-name buff)))) + ;; save BUFF. + (let ((old-buffer (current-buffer))) + (set-buffer buff) + (save-buffer) + (set-buffer old-buffer))))) + + +;;; (COMINT-GET-SOURCE prompt prev-dir/file source-modes mustmatch-p) +;;;============================================================================ +;;; COMINT-GET-SOURCE is used to prompt for filenames in command-interpreter +;;; commands that process source files (like loading or compiling a file). +;;; It prompts for the filename, provides a default, if there is one, +;;; and returns the result filename. +;;; +;;; See COMINT-SOURCE-DEFAULT for more on determining defaults. +;;; +;;; PROMPT is the prompt string. PREV-DIR/FILE is the (directory . file) pair +;;; from the last source processing command. SOURCE-MODES is a list of major +;;; modes used to determine what file buffers contain source files. (These +;;; two arguments are used for determining defaults). If MUSTMATCH-P is true, +;;; then the filename reader will only accept a file that exists. +;;; +;;; A typical use: +;;; (interactive (comint-get-source "Compile file: " prev-lisp-dir/file +;;; '(lisp-mode) t)) + +;;; This is pretty stupid about strings. It decides we're in a string +;;; if there's a quote on both sides of point on the current line. +(defun comint-extract-string () + "Returns string around point that starts the current line or nil." + (save-excursion + (let* ((point (point)) + (bol (progn (beginning-of-line) (point))) + (eol (progn (end-of-line) (point))) + (start (progn (goto-char point) + (and (search-backward "\"" bol t) + (1+ (point))))) + (end (progn (goto-char point) + (and (search-forward "\"" eol t) + (1- (point)))))) + (and start end + (buffer-substring start end))))) + +(defun comint-get-source (prompt prev-dir/file source-modes mustmatch-p) + (let* ((def (comint-source-default prev-dir/file source-modes)) + (stringfile (comint-extract-string)) + (sfile-p (and stringfile + (file-exists-p stringfile) + (not (file-directory-p stringfile)))) + (defdir (if sfile-p (file-name-directory stringfile) + (car def))) + (deffile (if sfile-p (file-name-nondirectory stringfile) + (cdr def))) + (ans (read-file-name (if deffile (format "%s(default %s) " + prompt deffile) + prompt) + defdir + (concat defdir deffile) + mustmatch-p))) + (list (expand-file-name (substitute-in-file-name ans))))) + +;;; I am somewhat divided on this string-default feature. It seems +;;; to violate the principle-of-least-astonishment, in that it makes +;;; the default harder to predict, so you actually have to look and see +;;; what the default really is before choosing it. This can trip you up. +;;; On the other hand, it can be useful, I guess. I would appreciate feedback +;;; on this. +;;; -Olin + + + + + + + + + + + + + + + + + + + + + + + +;;; Simple process query facility. +;;; =========================================================================== +;;; This function is for commands that want to send a query to the process +;;; and show the response to the user. For example, a command to get the +;;; arglist for a Common Lisp function might send a "(arglist 'foo)" query +;;; to an inferior Common Lisp process. +;;; +;;; This simple facility just sends strings to the inferior process and pops +;;; up a window for the process buffer so you can see what the process +;;; responds with. We don't do anything fancy like try to intercept what the +;;; process responds with and put it in a pop-up window or on the message +;;; line. We just display the buffer. Low tech. Simple. Works good. + +;;; Send to the inferior process PROC the string STR. Pop-up but do not select +;;; a window for the inferior process so that its response can be seen. +(defun comint-proc-query (proc str) + (let* ((proc-buf (process-buffer proc)) + (proc-mark (process-mark proc))) + (display-buffer proc-buf) + (set-buffer proc-buf) ; but it's not the selected *window* + (let ((proc-win (get-buffer-window proc-buf)) + (proc-pt (marker-position proc-mark))) + (comint-send-string proc str) ; send the query + (accept-process-output proc) ; wait for some output + ;; Try to position the proc window so you can see the answer. + ;; This is bogus code. If you delete the (sit-for 0), it breaks. + ;; I don't know why. Wizards invited to improve it. + (if (not (pos-visible-in-window-p proc-pt proc-win)) + (let ((opoint (window-point proc-win))) + (set-window-point proc-win proc-mark) (sit-for 0) + (if (not (pos-visible-in-window-p opoint proc-win)) + (push-mark opoint) + (set-window-point proc-win opoint))))))) + + + + + + + + + + + +;;; Filename completion in a buffer +;;; =========================================================================== +;;; Useful completion functions, courtesy of the Ergo group. +;;; M- will complete the filename at the cursor as much as possible +;;; M-? will display a list of completions in the help buffer. + +;;; Three commands: +;;; comint-dynamic-complete Complete filename at point. +;;; comint-dynamic-list-completions List completions in help buffer. +;;; comint-replace-by-expanded-filename Expand and complete filename at point; +;;; replace with expanded/completed name. + +;;; These are not installed in the comint-mode keymap. But they are +;;; available for people who want them. Shell-mode installs them: +;;; (define-key cmushell-mode-map "\M-\t" 'comint-dynamic-complete) +;;; (define-key cmushell-mode-map "\M-?" 'comint-dynamic-list-completions))) +;;; +;;; Commands like this are fine things to put in load hooks if you +;;; want them present in specific modes. Example: +;;; (setq cmushell-load-hook +;;; '((lambda () (define-key lisp-mode-map "\M-\t" +;;; 'comint-replace-by-expanded-filename)))) +;;; + + +(defun comint-match-partial-pathname () + "Returns the string of an existing filename or causes an error." + (if (save-excursion (backward-char 1) (looking-at "\\s ")) "" + (save-excursion + (re-search-backward "[^~/A-Za-z0-9---_.$#,]+") + (re-search-forward "[~/A-Za-z0-9---_.$#,]+") + (substitute-in-file-name + (buffer-substring (match-beginning 0) (match-end 0)))))) + + +(defun comint-replace-by-expanded-filename () +"Replace the filename at point with an expanded, canonicalised, and +completed replacement. +\"Expanded\" means environment variables (e.g., $HOME) and ~'s are +replaced with the corresponding directories. \"Canonicalised\" means .. +and \. are removed, and the filename is made absolute instead of relative. +See functions expand-file-name and substitute-in-file-name. See also +comint-dynamic-complete." + (interactive) + (let* ((pathname (comint-match-partial-pathname)) + (pathdir (file-name-directory pathname)) + (pathnondir (file-name-nondirectory pathname)) + (completion (file-name-completion pathnondir + (or pathdir default-directory)))) + (cond ((null completion) + (message "No completions of %s." pathname) + (ding)) + ((eql completion t) + (message "Unique completion.")) + (t ; this means a string was returned. + (delete-region (match-beginning 0) (match-end 0)) + (insert (expand-file-name (concat pathdir completion))))))) + + +(defun comint-dynamic-complete () + "Dynamically complete the filename at point. +This function is similar to comint-replace-by-expanded-filename, except +that it won't change parts of the filename already entered in the buffer; +it just adds completion characters to the end of the filename." + (interactive) + (let* ((pathname (comint-match-partial-pathname)) + (pathdir (file-name-directory pathname)) + (pathnondir (file-name-nondirectory pathname)) + (completion (file-name-completion pathnondir + (or pathdir default-directory)))) + (cond ((null completion) + (message "No completions of %s." pathname) + (ding)) + ((eql completion t) + (message "Unique completion.")) + (t ; this means a string was returned. + (goto-char (match-end 0)) + (insert (substring completion (length pathnondir))))))) + +(defun comint-dynamic-list-completions () + "List in help buffer all possible completions of the filename at point." + (interactive) + (let* ((pathname (comint-match-partial-pathname)) + (pathdir (file-name-directory pathname)) + (pathnondir (file-name-nondirectory pathname)) + (completions + (file-name-all-completions pathnondir + (or pathdir default-directory)))) + (cond ((null completions) + (message "No completions of %s." pathname) + (ding)) + (t + (let ((conf (current-window-configuration))) + (with-output-to-temp-buffer "*Help*" + (display-completion-list completions)) + (sit-for 0) + (message "Hit space to flush.") + (let ((ch (read-char))) + (if (= ch ?\ ) + (set-window-configuration conf) + (setq unread-command-char ch)))))))) + +; Ergo bindings +; (global-set-key "\M-\t" 'comint-replace-by-expanded-filename) +; (global-set-key "\M-?" 'comint-dynamic-list-completions) +; (define-key shell-mode-map "\M-\t" 'comint-dynamic-complete) + + + + + + + + + + + + + + + + + + + + + + + + + + +;;; Converting process modes to use comint mode +;;; =========================================================================== +;;; Several gnu packages (tex-mode, background, dbx, gdb, kermit, prolog, +;;; telnet are some) use the shell package as clients. Most of them would +;;; be better off using the comint package, but they predate it. +;;; +;;; Altering these packages to use comint mode should greatly +;;; improve their functionality, and is fairly easy. +;;; +;;; Renaming variables +;;; Most of the work is renaming variables and functions. These are the common +;;; ones: +;;; Local variables: +;;; last-input-end comint-last-input-end +;;; last-input-start +;;; shell-prompt-pattern comint-prompt-regexp +;;; shell-set-directory-error-hook +;;; Miscellaneous: +;;; shell-set-directory +;;; shell-mode-map comint-mode-map +;;; Commands: +;;; shell-send-input comint-send-input +;;; shell-send-eof comint-delchar-or-maybe-eof +;;; kill-shell-input comint-kill-input +;;; interrupt-shell-subjob comint-interrupt-subjob +;;; stop-shell-subjob comint-stop-subjob +;;; quit-shell-subjob comint-quit-subjob +;;; kill-shell-subjob comint-kill-subjob +;;; kill-output-from-shell comint-kill-output +;;; show-output-from-shell comint-show-output +;;; copy-last-shell-input Use comint-previous-input/comint-next-input +;;; +;;; LAST-INPUT-START is no longer necessary because inputs are stored on the +;;; input history ring. SHELL-SET-DIRECTORY is gone, its functionality taken +;;; over by SHELL-DIRECTORY-TRACKER, the shell mode's comint-input-sentinel. +;;; Comint mode does not provide functionality equivalent to +;;; shell-set-directory-error-hook; it is gone. +;;; +;;; If you are implementing some process-in-a-buffer mode, called foo-mode, do +;;; *not* create the comint-mode local variables in your foo-mode function. +;;; This is not modular. Instead, call comint-mode, and let *it* create the +;;; necessary comint-specific local variables. Then create the +;;; foo-mode-specific local variables in foo-mode. Set the buffer's keymap to +;;; be foo-mode-map, and its mode to be foo-mode. Set the comint-mode hooks +;;; (comint-prompt-regexp, comint-input-filter, comint-input-sentinel, +;;; comint-get-old-input) that need to be different from the defaults. Call +;;; foo-mode-hook, and you're done. Don't run the comint-mode hook yourself; +;;; comint-mode will take care of it. The following example, from cmushell.el, +;;; is typical: +;;; +;;; (defun shell-mode () +;;; (interactive) +;;; (comint-mode) +;;; (setq comint-prompt-regexp shell-prompt-pattern) +;;; (setq major-mode 'shell-mode) +;;; (setq mode-name "Shell") +;;; (cond ((not shell-mode-map) +;;; (setq shell-mode-map (full-copy-sparse-keymap comint-mode-map)) +;;; (define-key shell-mode-map "\M-\t" 'comint-dynamic-complete) +;;; (define-key shell-mode-map "\M-?" +;;; 'comint-dynamic-list-completions))) +;;; (use-local-map shell-mode-map) +;;; (make-local-variable 'shell-directory-stack) +;;; (setq shell-directory-stack nil) +;;; (setq comint-input-sentinel 'shell-directory-tracker) +;;; (run-hooks 'shell-mode-hook)) +;;; +;;; +;;; Note that make-comint is different from make-shell in that it +;;; doesn't have a default program argument. If you give make-shell +;;; a program name of NIL, it cleverly chooses one of explicit-shell-name, +;;; $ESHELL, $SHELL, or /bin/sh. If you give make-comint a program argument +;;; of NIL, it barfs. Adjust your code accordingly... +;;; + + + + + + + + + + + + + + +;;; Do the user's customisation... + +(defvar comint-load-hook nil + "This hook is run when comint is loaded in. +This is a good place to put keybindings.") + +(run-hooks 'comint-load-hook) + +;;; Change log: +;;; 9/12/89 +;;; - Souped up the filename expansion procedures. +;;; Doc strings are much clearer and more detailed. +;;; Fixed a bug where doing a filename completion when the point +;;; was in the middle of the filename instead of at the end would lose. +;;; +;;; 2/17/90 +;;; - Souped up the command history stuff so that text inserted +;;; by comint-previous-input-matching is removed by following +;;; command history recalls. comint-next/previous-input-matching +;;; is now much more smoothly integrated w/the command history stuff. +;;; - Added comint-eol-on-send flag and comint-input-sender hook. +;;; Comint-input-sender based on code contributed by Jeff Peck +;;; (peck@sun.com). +;;; +;;; 3/13/90 ccm@cmu.cs.edu +;;; - Added comint-previous-similar-input for looking up similar inputs. +;;; - Added comint-send-and-get-output to allow snarfing input from +;;; buffer. +;;; - Added the ability to pick up a source file by positioning over +;;; a string in comint-get-source. +;;; - Added add-hook to make it a little easier for the user to use +;;; multiple hooks. +;;; +;;; 5/22/90 shivers +;;; - Moved Chris' multiplexed ipc stuff to comint-ipc.el. +;;; - Altered Chris' comint-get-source string feature. The string +;;; is only offered as a default if it names an existing file. +;;; - Changed comint-exec to directly crank up the process, instead +;;; of calling the env program. This made background.el happy. +;;; - Added new buffer-local var comint-ptyp. The problem is that +;;; the signalling functions don't work as advertised. If you are +;;; communicating via pipes, the CURRENT-GROUP arg is supposed to +;;; be ignored, but, unfortunately it seems to be the case that you +;;; must pass a NIL for this arg in the pipe case. COMINT-PTYP +;;; is a flag that tells whether the process is communicating +;;; via pipes or a pty. The comint signalling functions use it +;;; to determine the necessary CURRENT-GROUP arg value. The bug +;;; has been reported to the Gnu folks. +;;; - comint-dynamic-complete flushes the help window if you hit space +;;; after you execute it. +;;; - Added functions comint-send-string, comint-send-region and var +;;; comint-input-chunk-size. comint-send-string tries to prevent processes +;;; from hanging when you send them long strings by breaking them into +;;; chunks and allowing process output between chunks. I got the idea from +;;; Eero Simoncelli's Common Lisp package. Note that using +;;; comint-send-string means that the process buffer's contents can change +;;; during a call! If you depend on process output only happening between +;;; toplevel commands, this could be a problem. In such a case, use +;;; process-send-string instead. If this is a problem for people, I'd like +;;; to hear about it. +;;; - Added comint-proc-query as a simple mechanism for commands that +;;; want to query an inferior process and display its response. For a +;;; typical use, see lisp-show-arglist in cmulisp.el. +;;; - Added constant comint-version, which is now "2.01". +;;; +;;; 6/14/90 shivers +;;; - Had comint-update-env defined twice. Removed extra copy. Also +;;; renamed mem to be comint-mem, for modularity. The duplication +;;; was reported by Michael Meissner. +;;; 6/16/90 shivers +;;; - Emacs has two different mechanisms for maintaining the process +;;; environment, determined at compile time by the MAINTAIN-ENVIRONMENT +;;; #define. One uses the process-environment global variable, and +;;; one uses a getenv/setenv interface. comint-exec assumed the +;;; process-environment interface; it has been generalised (with +;;; comint-exec-1) to handle both cases. Pretty bogus. We could, +;;; of course, skip all this and just use the etc/env program to +;;; handle the environment tweaking, but that obscures process +;;; queries that other modules (like background.el) depend on. etc/env +;;; is also fairly bogus. This bug, and some of the fix code was +;;; reported by Dan Pierson. +;;; +;;; 9/5/90 shivers +;;; - Changed make-variable-buffer-local's to make-local-variable's. +;;; This leaves non-comint-mode buffers alone. Stephane Payrard +;;; reported the sloppy useage. +;;; - You can now go from comint-previous-similar-input to +;;; comint-previous-input with no problem. + + diff --git a/emacs-tools/comint.elc b/emacs-tools/comint.elc new file mode 100644 index 0000000..0b9bf63 Binary files /dev/null and b/emacs-tools/comint.elc differ diff --git a/emacs-tools/haskell.el b/emacs-tools/haskell.el new file mode 100644 index 0000000..4130aea --- /dev/null +++ b/emacs-tools/haskell.el @@ -0,0 +1,2198 @@ +;;; ================================================================== +;;; File: haskell.el ;;; +;;; ;;; +;;; Author: A. Satish Pai ;;; +;;; Maria M. Gutierrez ;;; +;;; Dan Rabin (Jul-1991) ;;; +;;; ================================================================== + +;;; Description: Haskell mode for GNU Emacs. + +;;; Related files: comint.el + +;;; Contents: + +;;; Update Log + +;;; Known bugs / problems +;;; - the haskell editing mode (indentation, etc) is still missing. +;;; - the handling for errors from haskell needs to be rethought. +;;; - general cleanup of code. + + +;;; Errors generated + +;;; ================================================================== +;;; Haskell mode for editing files, and an Inferior Haskell mode to +;;; run a Haskell process. This file contains stuff snarfed and +;;; modified from tea.el, scheme.el, etc. This file may be freely +;;; modified; however, if you have any bug-corrections or useful +;;; improvements, I'd appreciate it if you sent me the mods so that +;;; I can merge them into the version I maintain. +;;; +;;; The inferior Haskell mode requires comint.el. +;;; +;;; You might want to add this to your .emacs to go automagically +;;; into Haskell mode while finding .hs files. +;;; +;;; (setq auto-mode-alist +;;; (cons '("\\.hs$" . haskell-mode) +;;; auto-mode-alist)_) +;;; +;;; To use this file, set up your .emacs to autoload this file for +;;; haskell-mode. For example: +;;; +;;; (autoload 'haskell-mode "$HASKELL/emacs-tools/haskell.elc" +;;; "Load Haskell mode" t) +;;; +;;; (autoload 'run-mode "$HASKELL/emacs-tools/haskell.elc" +;;; "Load Haskell mode" t) +;;; +;;; [Note: The path name given above is Yale specific!! Modify as +;;; required.] +;;; ================================================================ + +;;; Announce your existence to the world at large. + +(provide 'haskell) + + +;;; Load these other files. + +(require 'comint) ; Olin Shivers' comint mode is the substratum + + + + +;;; ================================================================ +;;; Declare a bunch of variables. +;;; ================================================================ + + +;;; User settable (via M-x set-variable and M-x edit-options) + +(defvar haskell-program-name (getenv "HASKELLPROG") + "*Program invoked by the haskell command") + +(defvar *haskell-buffer* "*haskell*" + "*Name of the haskell process buffer") + +(defvar *haskell-show-error* 1 + "*If not nil move to the buffer where the error was found") + + +(defvar haskell-auto-create-process t + "*If not nil, create a Haskell process automatically when required to evaluate or compile Haskell code") + +(defvar *haskell-debug-in-lisp* nil + "*If not nil, enter Lisp debugger on error; otherwise, automagically return +to Haskell top-level.") + + +;;; Command interface related variables + +(defvar *emacs* nil + "When not nil means haskell is in emacs mode") + + +;;; Pad/buffer Initialization variables + +(defvar haskell-main-pad "\*Main-pad\*" + "Scratch pad associated with module Main") + +(defvar haskell-main-file "Main") + +(defvar haskell-main-module "Main") + + +(defvar *last-loaded* haskell-main-file + "Last file loaded with a :load command - Defaults to Main") + +(defvar *last-loaded-modtime* nil + "Modification time of last file loaded, used to determine whether it +needs to be reloaded.") + +(defvar *last-module* haskell-main-module + "Last module set with a :module command - Defaults to Main") + +(defvar *last-pad* haskell-main-pad + "Last pad saved with a :save command - Defaults to Main") + + +;;; These are used for haskell-tutorial mode. + +(defvar *ht-source-file* "$HASKELL/progs/tutorial/tutorial.hs") +(defvar *ht-temp-buffer* nil) +(defvar *ht-file-buffer* "Haskell-Tutorial-Master") + + + +;;; ================================================================ +;;; Haskell editing mode stuff +;;; ================================================================ + +;;; Leave this place alone... +;;; The definitions below have been pared down to the bare +;;; minimum; they will be restored later. +;;; +;;; -Satish 2/5. + +;;; Keymap for Haskell mode +(defvar haskell-mode-map nil + "Keymap used for haskell-mode") + +(defun haskell-establish-key-bindings (keymap) + (define-key keymap "\C-ce" 'haskell-eval) + (define-key keymap "\C-cr" 'haskell-run) + (define-key keymap "\C-cm" 'haskell-run-main) + (define-key keymap "\C-c\C-r" 'haskell-run-file) + (define-key keymap "\C-cp" 'haskell-get-pad) + (define-key keymap "\C-c\C-o" 'haskell-optimizers) + (define-key keymap "\C-c\C-p" 'haskell-printers) + (define-key keymap "\C-cc" 'haskell-compile) + (define-key keymap "\C-cl" 'haskell-load) + (define-key keymap "\C-ch" 'haskell-switch) + (define-key keymap "\C-c:" 'haskell-command) + (define-key keymap "\C-cq" 'haskell-exit) + (define-key keymap "\C-ci" 'haskell-interrupt) + (define-key keymap "\C-cu" 'haskell-edit-unit) + (define-key keymap "\C-cd" 'haskell-please-recover) + (define-key keymap "\C-c(" 'haskell-ensure-lisp-mode) + (define-key keymap "\C-c)" 'haskell-resume-command-loop)) + + +(if haskell-mode-map + nil + (progn + (setq haskell-mode-map (make-sparse-keymap)) + ;; Compiler commands + (haskell-establish-key-bindings haskell-mode-map) + )) + +(defvar haskell-mode-syntax-table nil + "Syntax table used for haskell-mode") + +(if haskell-mode-syntax-table + nil + (setq haskell-mode-syntax-table (standard-syntax-table))) + +;;; Command for invoking the Haskell mode +(defun haskell-mode nil + "Major mode for editing Haskell code to run in Emacs +The following commands are available: +\\{haskell-mode-map} + +A Haskell process can be fired up with \"M-x haskell\". + +Customization: Entry to this mode runs the hooks that are the value of variable +haskell-mode-hook. + +Windows: + +There are 3 types of windows associated with Haskell mode. They are: + *haskell*: which is the process window. + Pad: which are buffers available for each module. It is here + where you want to test things before preserving them in a + file. Pads are always associated with a module. + When issuing a command: + The pad and its associated module are sent to the Haskell + process prior to the execution of the command. + .hs: These are the files where Haskell programs live. They + have .hs as extension. + When issuing a command: + The file is sent to the Haskell process prior to the + execution of the command. + +Commands: + +Each command behaves differently according to the type of the window in which +the cursor is positioned when the command is issued . + +haskell-eval: \\[haskell-eval] + Always promts user for a Haskell expression to be evaluated. If in a + .hs file buffer, then the cursor tells which module is the current + module and the pad for that module (if any) gets loaded as well. + +haskell-run: \\[haskell-run] + Always queries for a variable of type Dialogue to be evaluated. + +haskell-run-main: \\[haskell-run-main] + Run Dialogue named main. + +haskell-run-file: \\[haskell-run-file] + Runs a file. Ideally the file has a set of variable of type Dialogue + that get evaluated. + +haskell-mode: \\[haskell-mode] + Puts the current buffer in haskell mode. + +haskell-compile: \\[haskell-compile] + Compiles file in current buffer. + +haskell-load: \\[haskell-load] + Loads file in current buffer. + +haskell-pad: \\[haskell-pad] + Creates a scratch pad for the current module. + +haskell-optimizers: \\[haskell-optimizers] + Shows the list of available optimizers. Commands for turning them on/off. + +haskell-printers: \\[haskell-printers] + Shows the list of available printers. Commands for turning them on/off. + +haskell-command: \\[haskell-command] + Prompts for a command to be sent to the command interface. You don't + need to put the : before the command. + +haskell-quit: \\[haskell-quit] + Terminates the haskell process. + +switch-to-haskell: \\[switch-to-haskell] + Switchs to the inferior Haskell buffer (*haskell*) and positions the + cursor at the end of the buffer. + +haskell-interrupt: \\[haskell-interrupt] + Interrupts haskell process and resets it. + +haskell-edit-unit: \\[haskell-edit-unit] + Edit the .hu file for the unit containing this file. +" + (interactive) + (kill-all-local-variables) + (use-local-map haskell-mode-map) + (setq major-mode 'haskell-mode) + (setq mode-name "Haskell") + (make-local-variable 'indent-line-function) + (setq indent-line-function 'indent-relative-maybe) + ;(setq local-abbrev-table haskell-mode-abbrev-table) + (set-syntax-table haskell-mode-syntax-table) + ;(setq tab-stop-list haskell-tab-stop-list) ;; save old list?? + (run-hooks 'haskell-mode-hook)) + + + +;;;================================================================ +;;; Inferior Haskell stuff +;;;================================================================ + + +(defvar inferior-haskell-mode-map nil) + +(if inferior-haskell-mode-map + nil + (setq inferior-haskell-mode-map + (full-copy-sparse-keymap comint-mode-map)) + ;;; Haskell commands + (haskell-establish-key-bindings inferior-haskell-mode-map) + (define-key inferior-haskell-mode-map "\C-m" 'haskell-send-input)) + +(defvar haskell-source-modes '(haskell-mode) + "*Used to determine if a buffer contains Haskell source code. +If it's loaded into a buffer that is in one of these major modes, +it's considered a Haskell source file.") + +(defvar haskell-prev-l/c-dir/file nil + "Caches the (directory . file) pair used in the last invocation of +haskell-run-file.") + +(defvar haskell-prompt-pattern "^[A-Z]\\([A-Z]\\|[a-z]\\|[0-9]\\)*>\\s-*" + "Regular expression capturing the Haskell system prompt.") + +(defvar haskell-prompt-ring () + "Keeps track of input to haskell process from the minibuffer") + +(defvar tea-prompt-pattern "^>+\\s-*" + "Regular expression capturing the T system prompt.") + +(defvar haskell-version "Yale University Haskell Version 0.8, 1991" + "Current Haskell system version") + +(defun inferior-haskell-mode-variables () + nil) + + +;;; INFERIOR-HASKELL-MODE (adapted from comint.el) + +(defun inferior-haskell-mode () + "Major mode for interacting with an inferior Haskell process. + +The following commands are available: +\\{inferior-haskell-mode-map} + +A Haskell process can be fired up with \"M-x haskell\". + +Customization: Entry to this mode runs the hooks on comint-mode-hook and +inferior-haskell-mode-hook (in that order). + +You can send text to the inferior Haskell process from other buffers containing +Haskell source. + + +Windows: + +There are 3 types of windows in the inferior-haskell-mode. They are: + *haskell*: which is the process window. + Pad: which are buffers available for each module. It is here + where you want to test things before preserving them in a + file. Pads are always associated with a module. + When issuing a command: + The pad and its associated module are sent to the Haskell + process prior to the execution of the command. + .hs: These are the files where Haskell programs live. They + have .hs as extension. + When issuing a command: + The file is sent to the Haskell process prior to the + execution of the command. + +Commands: + +Each command behaves differently according to the type of the window in which +the cursor is positioned when the command is issued. + +haskell-eval: \\[haskell-eval] + Always promts user for a Haskell expression to be evaluated. If in a + .hs file, then the cursor tells which module is the current module and + the pad for that module (if any) gets loaded as well. + +haskell-run: \\[haskell-run] + Always queries for a variable of type Dialogue to be evaluated. + +haskell-run-main: \\[haskell-run-main] + Run Dialogue named main. + +haskell-run-file: \\[haskell-run-file] + Runs a file. Ideally the file has a set of variable of type Dialogue + that get evaluated. + +haskell-mode: \\[haskell-mode] + Puts the current buffer in haskell mode. + +haskell-compile: \\[haskell-compile] + Compiles file in current buffer. + +haskell-load: \\[haskell-load] + Loads file in current buffer. + +haskell-pad: \\[haskell-pad] + Creates a scratch pad for the current module. + +haskell-optimizers: \\[haskell-optimizers] + Shows the list of available optimizers. Commands for turning them on/off. + +haskell-printers: \\[haskell-printers] + Shows the list of available printers. Commands for turning them on/off. + +haskell-command: \\[haskell-command] + Prompts for a command to be sent to the command interface. You don't + need to put the : before the command. + +haskell-quit: \\[haskell-quit] + Terminates the haskell process. + +switch-to-haskell: \\[switch-to-haskell] + Switchs to the inferior Haskell buffer (*haskell*) and positions the + cursor at the end of the buffer. + +haskell-interrupt: \\[haskell-interrupt] + Interrupts haskell process and resets it. + +haskell-edit-unit: \\[haskell-edit-unit] + Edit the .hu file for the unit containing this file. + +The usual comint functions are also available. In particular, the +following are all available: + +comint-bol: Beginning of line, but skip prompt. Bound to C-a by default. +comint-delchar-or-maybe-eof: Delete char, unless at end of buffer, in + which case send EOF to process. Bound to C-d by default. + +Note however, that the default keymap bindings provided shadow some of +the default comint mode bindings, so that you may want to bind them +to your choice of keys. + +Comint mode's dynamic completion of filenames in the buffer is available. +(Q.v. comint-dynamic-complete, comint-dynamic-list-completions.) + +If you accidentally suspend your process, use \\[comint-continue-subjob] +to continue it." + + (interactive) + (comint-mode) + (setq comint-prompt-regexp haskell-prompt-pattern) + ;; Customise in inferior-haskell-mode-hook + (inferior-haskell-mode-variables) + (setq major-mode 'inferior-haskell-mode) + (setq mode-name "Inferior Haskell") + (setq mode-line-process '(": %s : busy")) + (use-local-map inferior-haskell-mode-map) + (setq comint-input-filter 'haskell-input-filter) + (setq comint-input-sentinel 'ignore) + (setq comint-get-old-input 'haskell-get-old-input) + (run-hooks 'inferior-haskell-mode-hook) + ;Do this after the hook so the user can mung INPUT-RING-SIZE w/his hook. + ;The test is so we don't lose history if we run comint-mode twice in + ;a buffer. + (setq haskell-prompt-ring (make-ring input-ring-size))) + + +;;; Install the process communication commands in the +;;; inferior-haskell-mode keymap. + +(defvar inferior-haskell-mode-hook 'haskell-fresh-start + "*Hook for customizing inferior-Haskell mode") + +(defun haskell-input-filter (str) + "Don't save whitespace." + (not (string-match "\\s *" str))) + + + +;;; ================================================================== +;;; Handle output from Haskell process +;;; ================================================================== + + +;;; This keeps track of the status of the haskell process. +;;; Values are: +;;; busy -- The process is busy. +;;; ready -- The process is ready for a command. +;;; input -- The process is waiting for input. +;;; dead -- The process is dead (exited or not started yet). + + +(defvar *haskell-status* 'dead + "Status of the haskell process") + +(defun set-haskell-status (value) + (setq *haskell-status* value) + (update-mode-line)) + +(defun get-haskell-status () + *haskell-status*) + +(defun update-mode-line () + (save-excursion + (set-buffer *haskell-buffer*) + (cond ((eq *haskell-status* 'ready) + (setq mode-line-process '(": %s: ready"))) + ((eq *haskell-status* 'input) + (setq mode-line-process '(": %s: input"))) + ((eq *haskell-status* 'busy) + (setq mode-line-process '(": %s: busy"))) + ((eq *haskell-status* 'dead) + (setq mode-line-process '(": %s: dead"))) + (t + (haskell-mode-error "Confused about status of haskell process!"))) + ;; Yes, this is the officially sanctioned technique for forcing + ;; a redisplay of the mode line. + (set-buffer-modified-p (buffer-modified-p)))) + + +;;; Filter +;;; The haskell process produces output with embedded control codes. +;;; These control codes are used to keep track of what kind of input +;;; the haskell process is expecting. Ordinary output is just displayed. +;;; +;;; This is kind of complicated because control sequences can be broken +;;; across multiple batches of text received from the haskell process. +;;; If the string ends in the middle of a control sequence, save it up +;;; for the next call. + +(defvar *haskell-saved-output* nil) + +(defun process-haskell-output (process str) + "Filter for output from Yale Haskell command interface" + (let ((idx 0) + (lastidx 0) + (data (match-data))) + (unwind-protect + (progn + ;; If there was saved output from last time, glue it in front of the + ;; newly received input. + (if *haskell-saved-output* + (progn + (setq str (concat *haskell-saved-output* str)) + (setq *haskell-saved-output* nil))) + ;; Loop, looking for complete command sequences. + ;; Set idx to point to the first one. + ;; lastidx points to next character to be processed. + (while (setq idx (ci-response-start str lastidx)) + ;; Display any intervening ordinary text. + (if (not (eq idx lastidx)) + (haskell-display-output (substring str lastidx idx))) + ;; Now dispatch on the particular command sequence found. + ;; Handler functions are called with the string and start index + ;; as arguments, and should return the index of the "next" + ;; character -- usually (match-end 0). + (setq lastidx (funcall (ci-response-handler str idx) str idx))) + ;; Look to see whether the string ends with an incomplete + ;; command sequence. + ;; If so, save the tail of the string for next time. + (if (setq idx (ci-prefix-start str lastidx)) + (setq *haskell-saved-output* (substring str idx)) + (setq idx (length str))) + ;; Display any leftover ordinary text. + (if (not (eq idx lastidx)) + (haskell-display-output (substring str lastidx idx)))) + (store-match-data data)))) + + + +;;; Here is code for matching command sequences from haskell. + +;;; The first entry of each item is the full regexp; the second is a prefix +;;; regexp; the third is a handler function to call. + +(defvar *ci-responses* + '(("\C-Ar" "\C-A" haskell-got-ready) + ("\C-Ai" "\C-A" haskell-got-input-request) + ("\C-Ae" "\C-A" haskell-got-error) + ("\C-Ap.*\n" "\C-A\\(p.*\\)?" haskell-got-printers) + ("\C-Ao.*\n" "\C-A\\(o.*\\)?" haskell-got-optimizers) + ("\C-As.*\n" "\C-A\\(s.*\\)?" haskell-got-message) + ;; This is the error string for T +; ("^\\*\\* Error" +; "^\\*\\(\\*\\( \\(E\\(r\\(r\\(or?\\)?\\)?\\)?\\)?\\)?\\)?" +; haskell-got-lisp-error) + ;; This is the prompt for Lucid's break loop + ("\n-> " "\n\\(-\\(> ?\\)?\\)?" haskell-got-lisp-error) + ;; This is the prompt for CMU CL's break loop + ("0\\] " "0\\(\\] ?\\)?" haskell-got-lisp-error) + ;; This is the prompt for AKCL's break loop + ("USER>>" "U\\(S\\(E\\(R\\(>>?\\)?\\)?\\)?\\)?" haskell-got-lisp-error) + ;; This is the prompt for Allegro CL + ("USER(.*):" "U\\(S\\(E\\(R\\((.*)?\\)?\\)?\\)?\\)?" haskell-got-lisp-error) + ;; This is the prompt for Harlequin Lispworks + ("USER .* : .* >" "U\\(S\\(E\\(R\\( .*\\( \\(:\\( .*\\( >?\\)?\\)?\\)?\\)?\\)?\\)?\\)?\\)?" haskell-got-lisp-error) + )) + +(defun command-match-regexp (x) (car x)) +(defun command-prefix-regexp (x) (car (cdr x))) +(defun command-handler (x) (car (cdr (cdr x)))) + +(defun glue-together (extractor) + (let ((result (concat "\\(" (funcall extractor (car *ci-responses*)) "\\)")) + (stuff (cdr *ci-responses*))) + (while stuff + (setq result + (concat result "\\|\\(" (funcall extractor (car stuff)) "\\)")) + (setq stuff (cdr stuff))) + result)) + +(defvar *ci-response-regexp* (glue-together 'command-match-regexp)) + +(defvar *ci-prefix-regexp* + (concat "\\(" (glue-together 'command-prefix-regexp) "\\)\\'")) + +(defun ci-response-start (str idx) + (string-match *ci-response-regexp* str idx)) + +(defun ci-prefix-start (str idx) + (string-match *ci-prefix-regexp* str idx)) + +(defun ci-response-handler (str idx) + (let ((list *ci-responses*) + (result nil)) + (while (and list (null result)) + (if (eq (string-match (command-match-regexp (car list)) str idx) idx) + (setq result (command-handler (car list))) + (setq list (cdr list)))) + (if (null result) + (haskell-mode-error "Failed to find command handler!!!")) + result)) + + +;;; Here are the low-level handler functions. Basically, these +;;; guys just parse the input for the command sequence and then call some +;;; other function to do the real work. + +(defun haskell-got-ready (str idx) + (let ((result (match-end 0))) + (haskell-reset) + result)) + +(defun haskell-got-input-request (str idx) + (let ((result (match-end 0))) + (get-user-input) + result)) + +(defun haskell-got-error (str idx) + (let ((result (match-end 0))) + (haskell-error-handler) + result)) + +(defun haskell-got-printers (str idx) + (let ((result (match-end 0))) + (update-printers-list (substring str (+ idx 2) (- result 1))) + result)) + +(defun haskell-got-optimizers (str idx) + (let ((result (match-end 0))) + (update-optimizers-list (substring str (+ idx 2) (- result 1))) + result)) + +(defun haskell-got-message (str idx) + (let ((result (match-end 0))) + (message (substring str (+ idx 2) (- result 1))) + result)) + +(defun haskell-got-lisp-error (str idx) + (haskell-handle-lisp-error idx str) + (length str)) + + +;;; Something really bad happened and we got a Lisp error. +;;; Either let the user mess around in the Lisp debugger, or else +;;; just get out of it and go back into the Haskell command loop. + +(defun haskell-handle-lisp-error (location str) + (haskell-display-output (substring str location)) + (if *emacs* + ;; Don't ding if we were already in the break loop when the + ;; error happened. + (progn + (ding) + (if *haskell-debug-in-lisp* + (haskell-talk-to-lisp) + (haskell-flush-commands-and-reset))))) + +(defun loaded-tutorial-p () + (and *ht-temp-buffer* + (get-buffer *ht-temp-buffer*) + (equal *last-loaded* (buffer-file-name (get-buffer *ht-temp-buffer*))))) + +(defun haskell-flush-commands-and-reset () + (haskell-flush-command-queue) + (save-excursion + (switch-to-buffer *haskell-buffer*) + (haskell-ensure-lisp-mode) + (haskell-resume-command-loop))) + +(defun haskell-talk-to-lisp () + (pop-to-buffer *haskell-buffer*) + (goto-char (point-max)) + (haskell-ensure-lisp-mode)) + + +(defun haskell-resume-command-loop () + "Resumes Haskell command processing after debugging in Lisp. \\[haskell-resume-command-loop]" + (interactive) + (if (not *emacs*) + (progn + (process-send-string "haskell" "(mumble-user::restart-haskell)\n") + (haskell-ensure-emacs-mode)))) + + + +;;; Displays output at end of given buffer. +;;; This function only ensures that the output is visible, without +;;; selecting the buffer in which it is displayed. +;;; Note that just using display-buffer instead of all this rigamarole +;;; won't work; you need to temporarily select the window containing +;;; the *haskell-buffer*, or else the display won't be scrolled to show +;;; the new output. +;;; *** This should really position the window in the buffer so that +;;; *** the point is on the last line of the window. + +(defun haskell-display-output (str) + (if (eq (get-haskell-status) 'dead) + (save-excursion + (set-buffer *haskell-buffer*) + (haskell-display-output-aux str)) + (let ((window (selected-window))) + (unwind-protect + (progn + (pop-to-buffer *haskell-buffer*) + (haskell-display-output-aux str)) + (select-window window))))) + +(defun haskell-display-output-aux (str) + (haskell-move-marker) + (insert str) + (haskell-move-marker)) + + + +;;; The haskell process says it's expecting the user to type in some input. +;;; Switch to the *haskell-buffer* so the user can type things. +;;; Once we have received an input message, stay in input mode until +;;; we get a ready message back from haskell. This permits multiple +;;; data messages to be sent to haskell from a single input request. +;;; +;;; This user interface isn't really ideal. You can be typing +;;; away in some other buffer and all of a sudden have Haskell decide +;;; it wants some input, and bingo! You're switched into the Haskell +;;; buffer behind your back. There's also the problem that you're +;;; left in the Haskell buffer afterwards, instead of getting swapped +;;; back into the buffer that was current when the input request was +;;; received. +;;; Not sure how to fix this -- seems like a totally synchronous interface +;;; would be worse.... + +(defun get-user-input () + (message "Haskell is waiting for input...") + (pop-to-buffer *haskell-buffer*) + (goto-char (point-max)) + (set-haskell-status 'input) + (haskell-pop-data-queue)) + + +;;; The haskell process says it encountered an error. +;;; Remember to flush the command queue before continuing. + +(defun haskell-error-handler () + (ding) + (haskell-flush-command-queue) + ;; *** See comments below for why this is disabled. +; (if *haskell-show-error* +; (haskell-show-error)) + (set-haskell-status 'ready) + (haskell-end-interaction nil)) + + +;;; Pop up a buffer containing the file with the error, and put the +;;; point on the line where the error was reported. +;;; *** This code does the wrong thing in some situations. For example, +;;; *** if you type in garbage to C-c e, it thinks that it should +;;; *** show you the last pad sent to the haskell process, which is +;;; *** clearly bogus. +;;; *** I also think it would be better interaction style to have to +;;; *** request to be shown the error explicitly, instead of unexpectedly +;;; *** being thrown into some other buffer. + +;;; Error handling Variables + +(defvar *yh-error-def* "Error occured in definition of\\s *") +(defvar *yh-error-line* "at line\\s *") +(defvar *yh-error-file* "of file\\s *") +(defvar *haskell-line* "\\([0-9]\\)*") + +(defun haskell-show-error () + "Point out error to user if possible" + (set-buffer *haskell-buffer*) + (save-excursion + (let ((function-name nil) + (line-number nil) + (filename nil)) + (if (and (setq function-name (get-function-name)) + (setq line-number (get-line-number)) + (setq filename (get-filename))) + (point-error-to-user function-name line-number filename))))) + +(defvar *haskell-function-name* + "\\([a-z]\\|[A-Z]\\|[0-9]\\|'\\|_\\|\-\\)*") + +(defun get-function-name () + (if (and (re-search-backward *yh-error-def* (point-min) t) + (re-search-forward *yh-error-def* (point-max) t)) + (let ((beg (point))) + (if (re-search-forward *haskell-function-name* (point-max) t) + (buffer-substring beg (point)) + nil)) + nil)) + +(defun get-line-number () + (if (re-search-forward *yh-error-line* (point-max) t) + (let ((beg (point))) + (if (re-search-forward *haskell-line* (point-max) t) + (string-to-int (buffer-substring beg (point))) + nil)) + nil)) + + +(defun get-filename () + (if (re-search-forward *yh-error-file* (point-max) t) + (let ((beg (point))) + (if (re-search-forward "\\($\\| \\|\t\\)" (point-max) t) + (buffer-substring beg (point)) + nil)) + nil)) + +(defun point-error-to-user (function-name line-number filename) + (if (equal filename "Interactive") + (pop-to-buffer *last-pad*) + (let ((fname (strip-fext filename))) + (if (get-buffer fname) + (pop-to-buffer fname) + (find-file-other-window filename)))) + (goto-line line-number)) + + +;;; The haskell process says it is ready to execute another command. +;;; Tell the user the last command has finished and execute the next +;;; command from the queue, if there is one. + +(defun haskell-reset () + (set-haskell-status 'ready) + (haskell-pop-command-queue)) + + + + +;;; ================================================================== +;;; Command queue utilities +;;; ================================================================== + +;;; Here's the stuff for managing the command queue. +;;; There are three kinds of things that show up in the queue: +;;; * Strings to be sent as commands to the haskell process. These +;;; are queued with haskell-send-command. +;;; * Other stuff to be sent to the haskell process (e.g., text to +;;; be read as dialogue input). These are queued with +;;; haskell-send-data. +;;; * Messages indicating start of an interaction sequence. These +;;; are just shown to the user. These are added to the queue with +;;; haskell-begin-interaction. +;;; * Messages indicating end of an interaction sequence. These are +;;; queued with haskell-end-interaction. +;;; +;;; Representationally, the queue is just a list of conses. The car of each +;;; entry is a symbol that identifies the kind of queue entry, and the cdr +;;; is associated data. Only the functions in this section need to know +;;; about the internal format of the queue. + + +(defvar *command-interface-queue* nil + "Contains the commands to be sent to the Haskell command interface") + + +;;; Here's a helper function. + +(defun haskell-queue-or-execute (fn request data) + (cond (*command-interface-queue* + (setq *command-interface-queue* + (nconc *command-interface-queue* (list (cons request data))))) + ((eq (get-haskell-status) 'ready) + (funcall fn data)) + (t + (setq *command-interface-queue* (list (cons request data)))))) + + +;;; Queue a command. + +(defun haskell-send-command (str) + "Queues STRING for transmission to haskell process." + (haskell-queue-or-execute 'haskell-send-command-aux 'command str)) + +(defun haskell-send-command-aux (str) + (process-send-string "haskell" str) + (process-send-string "haskell" "\n") + (if (not (eq (get-haskell-status) 'input)) + (set-haskell-status 'busy))) + + +;;; Queue a begin-interaction message. + +(defvar *begin-interaction-delimiter* nil ;; "-------------\n" + "*Delimiter showing an interaction has begun") + +(defun haskell-begin-interaction (msg) + (haskell-queue-or-execute 'haskell-begin-interaction-aux 'begin msg)) + +(defun haskell-begin-interaction-aux (msg) + (if *begin-interaction-delimiter* + (haskell-display-output *begin-interaction-delimiter*)) + (if msg + (haskell-display-output (concat "\n" msg "\n")))) + + +;;; Queue an end-interaction message. + +(defvar *end-interaction-delimiter* nil ;; "\n--- ready ---\n\n" + "*Delimiter showing an interaction has ended") + +(defun haskell-end-interaction (msg) + (haskell-queue-or-execute 'haskell-end-interaction-aux 'end msg)) + +(defun haskell-end-interaction-aux (msg) + (if *end-interaction-delimiter* + (haskell-display-output *end-interaction-delimiter*)) + (if msg + (message "%s" msg))) + + +;;; Queue data. This is treated a little differently because we want +;;; text typed in as input to the program to be sent down the pipe to +;;; the process before processing end-interaction messages and additional +;;; commands in the queue. + +(defun haskell-send-data (str) + (cond ((assoc 'data *command-interface-queue*) + (setq *command-interface-queue* + (merge-data-into-queue + (list (cons 'data str)) + *command-interface-queue* + *command-interface-queue* + nil))) + ((or (eq (get-haskell-status) 'ready) (eq (get-haskell-status) 'input)) + (haskell-send-command-aux str)) + (t + (setq *command-interface-queue* (list (cons 'data str)))))) + +(defun merge-data-into-queue (new head tail lasttail) + (cond ((null tail) + (rplacd lasttail new) + head) + ((eq (car (car tail)) 'data) + (merge-data-into-queue new head (cdr tail) tail)) + (lasttail + (rplacd lasttail new) + (rplacd new tail) + head) + (t + (rplacd new tail) + new))) + + +;;; This function is called when the haskell process reports that it +;;; has finished processing a command. It sends the next queued +;;; command (if there is one) down the pipe. + +(defun haskell-pop-command-queue () + (if *command-interface-queue* + (let ((entry (car *command-interface-queue*))) + (setq *command-interface-queue* (cdr *command-interface-queue*)) + (cond ((eq (car entry) 'command) + (haskell-send-command-aux (cdr entry))) + ((eq (car entry) 'begin) + (haskell-begin-interaction-aux (cdr entry)) + (haskell-pop-command-queue)) + ((eq (car entry) 'end) + (haskell-end-interaction-aux (cdr entry)) + (haskell-pop-command-queue)) + ((eq (car entry) 'data) + (haskell-send-command-aux (cdr entry))) + (t + (haskell-mode-error "Invalid command in queue!!!")) + )))) + + +;;; This function is called when the haskell process reports that it +;;; wants to read some input. If there's queued data, send it; but +;;; don't do commands or messages on the queue. +;;; Remember, we can send multiple pieces of input data for one input +;;; request from haskell. + +(defun haskell-pop-data-queue () + (if *command-interface-queue* + (let ((entry (car *command-interface-queue*))) + (if (eq (car entry) 'data) + (progn + (setq *command-interface-queue* (cdr *command-interface-queue*)) + (haskell-send-command-aux (cdr entry)) + (haskell-pop-data-queue)))))) + + +;;; This is called when there is an error. + +(defun haskell-flush-command-queue () + (setq *command-interface-queue* nil)) + + + +;;; ================================================================== +;;; Interactive commands +;;; ================================================================== + + +;;; HASKELL and RUN HASKELL +;;; ------------------------------------------------------------------ + +;;; These are the two functions that start a Haskell process. +;;; Rewritten to avoid doing anything if a Haskell process +;;; already exists. 1991-Sep-09 Dan Rabin. + +;;; *** Dan says: +;;; *** If the *haskell* buffer still exists, and the process has status +;;; *** `dead', the usual evaluation commands don't create a new one, so no +;;; *** evaluation happens. + + +(defun haskell () + "Run an inferior Haskell process with input and output via buffer *haskell*. +Takes the program name from the variable haskell-program-name. +Runs the hooks from inferior-haskell-mode-hook +(after the comint-mode-hook is run). +\(Type \\[describe-mode] in the process buffer for a list of commands.)" + (interactive) + (let ((haskell-buffer (get-buffer *haskell-buffer*))) + (if (not (and haskell-buffer (comint-check-proc haskell-buffer))) + (progn + (setq haskell-buffer + (apply 'make-comint + "haskell" + haskell-program-name + nil + nil)) + (save-excursion + (set-buffer haskell-buffer) + (inferior-haskell-mode)) + (display-buffer haskell-buffer))))) + + +;;; Fresh start + +(defun haskell-fresh-start () + (set-haskell-status 'busy) + (setq *command-interface-queue* nil) + (setq *last-loaded* haskell-main-file) + (setq *last-pad* haskell-main-pad) + (setq *emacs* nil) + (setq *haskell-saved-output* nil) + (haskell-ensure-emacs-mode)) + + +;;; Called from evaluation and compilation commands to start up a Haskell +;;; process if none is already in progress. + +(defun haskell-maybe-create-process () + (if haskell-auto-create-process + (haskell))) + + +;;; This is called from HASKELL-FRESH-START to ensure that +;;; there is a pad when starting up a Haskell interaction. + +(defun haskell-ensure-emacs-mode () + (create-main-pad) + (setq *emacs* t) + (ci-emacs)) + + +;;; This is called when a Lisp error has been detected. + +(defun haskell-ensure-lisp-mode () + "Switch to talking to Lisp. \\[haskell-ensure-lisp-mode]" + (interactive) + (setq *emacs* nil)) + + +;;; HASKELL-GET-PAD +;;; ------------------------------------------------------------------ + +;;; This always puts the pad buffer in the "other" window. +;;; Having it wipe out the .hs file window is clearly the wrong +;;; behavior. + +(defun haskell-get-pad () + "Creates a new scratch pad for the current module. +Signals an error if the current buffer is not a .hs file." + (interactive) + (let ((fname (buffer-file-name))) + (if fname + (do-get-pad fname (current-buffer)) + (haskell-mode-error "Not in a .hs buffer")))) + + +(defun do-get-pad (fname buff) + (let* ((mname (or (get-modname buff) + (read-no-blanks-input "Scratch pad for module? " nil))) + (pname (lookup-pad mname fname)) + (pbuff nil)) + ;; Generate the base name of the pad buffer, then create the + ;; buffer. The actual name of the pad buffer may be something + ;; else because of name collisions. + (if (or (not pname) (not (setq pbuff (get-buffer pname)))) + (progn + (setq pname (get-padname mname)) + (setq pbuff (generate-new-buffer pname)) + (setq pname (buffer-name pbuff)) + (record-pad-mapping pname mname fname) + )) + ;; Make sure the pad buffer is in haskell mode. + (pop-to-buffer pbuff) + (haskell-mode))) + + +;;; HASKELL-SWITCH +;;; ------------------------------------------------------------------ + +(defun haskell-switch () + "Switches to \*haskell\* buffer" + (interactive) + (haskell-maybe-create-process) + (switch-to-haskell t)) + + +(defun switch-to-haskell (eob-p) + "Really switch to the \*haskell\* buffer. +With argument, positions cursor at end of buffer." + (interactive "P") + (pop-to-buffer *haskell-buffer*) + (cond (eob-p + (push-mark) + (goto-char (point-max))))) + + +;;; HASKELL-COMMAND +;;; ------------------------------------------------------------------ + +(defun haskell-command (str) + "Format STRING as a haskell command and send it to haskell process. \\[haskell-command]" + (interactive "sHaskell command: ") + (if (eq ?Q (capitalize (aref str 0))) + (ci-quit) + (progn + (haskell-begin-interaction + (concat "Executing command: :" str)) + (haskell-send-command (concat ":" str)) + (haskell-end-interaction + (concat "Executing command: :" str " ...done."))))) + + +;;; HASKELL-EVAL and HASKELL-RUN +;;; ------------------------------------------------------------------ + +(defun haskell-eval () + "Evaluate expression in current module. \\[haskell-eval]" + (interactive) + (haskell-maybe-create-process) + (haskell-eval-aux (get-haskell-expression "Haskell expression: ") + nil + "Evaluating")) + +(defun haskell-run () + "Run Haskell Dialogue in current module" + (interactive) + (haskell-maybe-create-process) + (haskell-eval-aux (get-haskell-expression "Haskell dialogue: ") + t + "Running")) + +(defun haskell-run-main () + "Run Dialogue named main in current module" + (interactive) + (haskell-maybe-create-process) + (haskell-eval-aux "main" t "Running")) + +(defun haskell-eval-aux (exp dialogue-p what) + (cond ((equal *haskell-buffer* (buffer-name)) + (let* ((pname *last-pad*) + (mname *last-module*) + (fname *last-loaded*) + (msg (format "%s: %s" what exp))) + (haskell-eval-aux-aux exp pname mname fname msg dialogue-p))) + ((equal *ht-temp-buffer* (buffer-name)) + (let* ((fname (buffer-file-name)) + (mname (get-modname (current-buffer))) + (pname (lookup-pad mname fname)) + (msg (format "%s (in tutorial): %s" what exp))) + (haskell-eval-aux-aux exp pname mname fname msg dialogue-p))) + ((buffer-file-name) + (let* ((fname (buffer-file-name)) + (mname (get-modname (current-buffer))) + (pname (lookup-pad mname fname)) + (msg (format "%s (in file %s): %s" + what (file-name-nondirectory fname) exp))) + (haskell-eval-aux-aux exp pname mname fname msg dialogue-p))) + (t + (let* ((pname (buffer-name (current-buffer))) + (mname (get-module-from-pad pname)) + (fname (get-file-from-pad pname)) + (msg (format "%s (in pad %s): %s" what pname exp))) + (haskell-eval-aux-aux exp pname mname fname msg dialogue-p))) + )) + +(defun haskell-eval-aux-aux (exp pname mname fname msg dialogue-p) + (haskell-begin-interaction msg) + (ci-kill) + (haskell-load-file-if-modified fname) + (ci-module mname) + (if pname (haskell-save-pad-if-modified pname)) + (if dialogue-p + (ci-send-name exp) + (ci-print-exp exp)) + (ci-eval) + (haskell-end-interaction (concat msg " ...done."))) + + +;;; Save pad only if modified. Keep track of *last-pad* sent to process. + +(defun haskell-save-pad-if-modified (pad) + (save-excursion + (set-buffer pad) + (if (or (equal pad haskell-main-pad) (buffer-modified-p)) + (progn + (setq *last-pad* pad) + (ci-clear) + (ci-set-file pad) + (ci-send-buffer pad) +; (set-buffer-modified-p t) ;***??? + (ci-save))))) + + + +;;; HASKELL-RUN-FILE +;;; ------------------------------------------------------------------ + +(defun haskell-run-file () + "Run all Dialogues in current file" + (interactive) + (haskell-maybe-create-process) + (cond ((equal *haskell-buffer* (buffer-name)) + ;; When called from the haskell process buffer, prompt for + ;; a file to run. + (call-interactively 'haskell-run-file/process)) + ((buffer-file-name) + ;; When called from a .hs file buffer, run that file. + (haskell-run-file-aux (buffer-file-name))) + (t + ;; When called from a pad, run the file that the module the + ;; pad belongs to lives in. + (haskell-run-file-aux + (get-file-from-pad (buffer-name (current-buffer))))) + )) + +(defun haskell-run-file/process (filename) + (interactive (comint-get-source "Haskell file to run: " + haskell-prev-l/c-dir/file + haskell-source-modes t)) + (comint-check-source filename) + (setq haskell-prev-l/c-dir/file + (cons (file-name-directory filename) + (file-name-nondirectory filename))) + (haskell-run-file-aux filename)) + +(defun haskell-run-file-aux (fname) + (let ((msg (concat "Running file: " fname))) + (haskell-begin-interaction msg) + (ci-kill) + (save-modified-source-files buffer-file-name) + (ci-run (strip-fext fname)) + (haskell-end-interaction (concat msg " ...done.")))) + + +;;; HASKELL-LOAD +;;; ------------------------------------------------------------------ + +(defun haskell-load () + "Load current file" + (interactive) + (haskell-maybe-create-process) + (let* ((fname (buffer-file-name)) + (msg (concat "Loading file: " fname))) + (cond (fname + (haskell-begin-interaction msg) + (haskell-load-file-if-modified fname) + (haskell-end-interaction (concat msg " ...done."))) + (t + (haskell-mode-error "Must be in a file to load"))))) + + +;;; Load file only if modified or not *last-loaded*. +;;; For now, this just loads the file unconditionally. + +(defun haskell-load-file-if-modified (filename) + (save-modified-source-files buffer-file-name) + (cond ((string= filename haskell-main-file) + (setq *last-loaded* haskell-main-file) + (ci-load-main)) + (t + (setq *last-loaded* filename) + (ci-load (strip-fext filename))))) + + +;;; ***This isn't used any more. +;(defun file-modification-time (file) +; "Get modification time for FILE from filesystem information." +; (car (cdr (car (nthcdr 5 (file-attributes file)))))) + + +;;; HASKELL-COMPILE +;;; ------------------------------------------------------------------ + +(defun haskell-compile () + "Compile current file" + (interactive) + (haskell-maybe-create-process) + (let ((fname (buffer-file-name))) + (cond (fname + (haskell-begin-interaction (concat "Compiling: " fname)) + (haskell-compile-file-if-modified fname) + (haskell-end-interaction + (concat "Compiling: " fname " ...done."))) + (t + (haskell-mode-error "Must be in a file to compile"))))) + +(defun haskell-compile-file-if-modified (fname) + ;; *** For now it unconditionally compiles the file. + (save-modified-source-files buffer-file-name) + (ci-compile (strip-fext fname))) + + +;;; HASKELL-EXIT +;;; ------------------------------------------------------------------ + +(defun haskell-exit () + "Quit the haskell process" + (interactive) + (ci-quit) + ;; If we were running the tutorial, mark the temp buffer as unmodified + ;; so we don't get asked about saving it later. + (if (and *ht-temp-buffer* + (get-buffer *ht-temp-buffer*)) + (save-excursion + (set-buffer *ht-temp-buffer*) + (set-buffer-modified-p nil))) + ;; Try to remove the haskell output buffer from the screen. + (bury-buffer *haskell-buffer*) + (replace-buffer-in-windows *haskell-buffer*)) + + +;;; HASKELL-INTERRUPT +;;; ------------------------------------------------------------------ + +(defun haskell-interrupt () + "Interrupt the haskell process" + (interactive) + ;; Do not queue the interrupt character; send it immediately. + (haskell-send-command-aux "\C-c") ; interrupt Haskell + (haskell-end-interaction "done.") ; send a reset to Lisp + ) + + +;;; HASKELL-EDIT-UNIT +;;; ------------------------------------------------------------------ + +(defun haskell-edit-unit () + "Edit the .hu file." + (interactive) + (let ((fname (buffer-file-name))) + (if fname + (let ((find-file-not-found-hooks (list 'haskell-new-unit)) + (file-not-found nil) + (units-fname (haskell-get-unit-file))) + (find-file-other-window units-fname) + (if file-not-found + ;; *** this is broken. + (units-add-source-file + (if (string= (file-name-directory fname) + (file-name-directory units-fname)) + (file-name-nondirectory fname) + fname)))) + (haskell-mode-error "Not in a .hs buffer")))) + +(defun haskell-new-unit () + (setq file-not-found t)) + +(defun units-add-source-file (file) + (save-excursion + (insert (strip-fext file) "\n"))) + + +;;; Look for a comment like "-- unit:" at top of file. +;;; If not found, assume unit file has same name as the buffer but +;;; a .hu extension. + +(defun haskell-get-unit-file () + (let ((name nil)) + (save-excursion + (beginning-of-buffer) + (if (re-search-forward "-- unit:[ \t]*" (point-max) t) + (let ((beg (match-end 0))) + (end-of-line) + (setq name (buffer-substring beg (point)))) + (setq name (concat (strip-fext (buffer-file-name)) ".hu")))) + name)) + + +;;; HASKELL-PLEASE-RECOVER +;;; ------------------------------------------------------------------ + +(defun haskell-please-recover () + (interactive) + (haskell-flush-commands-and-reset) + (haskell-end-interaction "done.")) + + + +;;; ================================================================== +;;; Support for printers/optimizers menus +;;; ================================================================== + +;;; This code was adapted from the standard buff-menu.el code. + +(defvar haskell-menu-mode-map nil "") + +(if (not haskell-menu-mode-map) + (progn + (setq haskell-menu-mode-map (make-keymap)) + (suppress-keymap haskell-menu-mode-map t) + (define-key haskell-menu-mode-map "m" 'haskell-menu-mark) + (define-key haskell-menu-mode-map "u" 'haskell-menu-unmark) + (define-key haskell-menu-mode-map "x" 'haskell-menu-exit) + (define-key haskell-menu-mode-map "q" 'haskell-menu-exit) + (define-key haskell-menu-mode-map " " 'next-line) + (define-key haskell-menu-mode-map "\177" 'haskell-menu-backup-unmark) + (define-key haskell-menu-mode-map "?" 'describe-mode))) + +;; Printers Menu mode is suitable only for specially formatted data. + +(put 'haskell-menu-mode 'mode-class 'special) + +(defun haskell-menu-mode () + "Major mode for editing Haskell flags. +Each line describes a flag. +Letters do not insert themselves; instead, they are commands. +m -- mark flag (turn it on) +u -- unmark flag (turn it off) +x -- exit; tell the Haskell process to update the flags, then leave menu. +q -- exit; same as x. +Precisely,\\{haskell-menu-mode-map}" + (kill-all-local-variables) + (use-local-map haskell-menu-mode-map) + (setq truncate-lines t) + (setq buffer-read-only t) + (setq major-mode 'haskell-menu-mode) + (setq mode-name "Haskell Flags Menu") + ;; These are all initialized elsewhere + (make-local-variable 'haskell-menu-current-flags) + (make-local-variable 'haskell-menu-request-fn) + (make-local-variable 'haskell-menu-update-fn) + (run-hooks 'haskell-menu-mode-hook)) + + +(defun haskell-menu (help-file buffer request-fn update-fn) + (haskell-maybe-create-process) + (if (get-buffer buffer) + (progn + (pop-to-buffer buffer) + (goto-char (point-min))) + (progn + (pop-to-buffer buffer) + (insert-file-contents help-file) + (haskell-menu-mode) + (setq haskell-menu-request-fn request-fn) + (setq haskell-menu-update-fn update-fn) + )) + (haskell-menu-mark-current) + (message "m = mark; u = unmark; x = execute; q = quit; ? = more help.")) + + + +;;; A line that starts with *haskell-menu-marked* is a menu item turned on. +;;; A line that starts with *haskell-menu-unmarked* is turned off. +;;; A line that starts with anything else is just random text and is +;;; ignored by commands that deal with menu items. + +(defvar *haskell-menu-marked* " on") +(defvar *haskell-menu-unmarked* " ") +(defvar *haskell-menu-marked-regexp* " on \\w") +(defvar *haskell-menu-unmarked-regexp* " \\w") + +(defun haskell-menu-mark () + "Mark flag to be turned on." + (interactive) + (beginning-of-line) + (cond ((looking-at *haskell-menu-marked-regexp*) + (forward-line 1)) + ((looking-at *haskell-menu-unmarked-regexp*) + (let ((buffer-read-only nil)) + (delete-char (length *haskell-menu-unmarked*)) + (insert *haskell-menu-marked*) + (forward-line 1))) + (t + (forward-line 1)))) + +(defun haskell-menu-unmark () + "Unmark flag." + (interactive) + (beginning-of-line) + (cond ((looking-at *haskell-menu-unmarked-regexp*) + (forward-line 1)) + ((looking-at *haskell-menu-marked-regexp*) + (let ((buffer-read-only nil)) + (delete-char (length *haskell-menu-marked*)) + (insert *haskell-menu-unmarked*) + (forward-line 1))) + (t + (forward-line 1)))) + +(defun haskell-menu-backup-unmark () + "Move up and unmark." + (interactive) + (forward-line -1) + (haskell-menu-unmark) + (forward-line -1)) + + +;;; Actually make the changes. + +(defun haskell-menu-exit () + "Update flags, then leave menu." + (interactive) + (haskell-menu-execute) + (haskell-menu-quit)) + +(defun haskell-menu-execute () + "Tell haskell process to tweak flags." + (interactive) + (start-setting-flags) + (save-excursion + (goto-char (point-min)) + (while (not (eq (point) (point-max))) + (cond ((looking-at *haskell-menu-unmarked-regexp*) + (funcall haskell-menu-update-fn (haskell-menu-flag) nil)) + ((looking-at *haskell-menu-marked-regexp*) + (funcall haskell-menu-update-fn (haskell-menu-flag) t)) + (t + nil)) + (forward-line 1))) + (finish-setting-flags)) + +(defun haskell-menu-quit () + (interactive) + "Make the menu go away." + (bury-buffer (current-buffer)) + (replace-buffer-in-windows (current-buffer))) + + +(defun haskell-menu-flag () + (save-excursion + (beginning-of-line) + (forward-char 6) + (let ((beg (point))) + ;; End of flag name marked by tab or two spaces. + (re-search-forward "\t\\| ") + (buffer-substring beg (match-beginning 0))))) + + +(defun start-setting-flags () + nil) + +(defun finish-setting-flags () + (haskell-end-interaction "Setting flags....done.")) + + +;;; Update the menu to mark only those items currently turned on. + +(defun haskell-menu-mark-current () + (funcall haskell-menu-request-fn) + (save-excursion + (goto-char (point-min)) + (while (not (eq (point) (point-max))) + (cond ((and (looking-at *haskell-menu-unmarked-regexp*) + (menu-item-currently-on-p (haskell-menu-flag))) + (haskell-menu-mark)) + ((and (looking-at *haskell-menu-marked-regexp*) + (not (menu-item-currently-on-p (haskell-menu-flag)))) + (haskell-menu-unmark)) + (t + (forward-line 1)))))) + + +;;; See if a menu item is turned on. + +(defun menu-item-currently-on-p (item) + (member-string= item haskell-menu-current-flags)) + +(defun member-string= (item list) + (cond ((null list) + nil) + ((string= item (car list)) + list) + (t + (member-string= item (cdr list))))) + + + +;;; Make the menu for printers. + +(defvar *haskell-printers-help* + (concat (getenv "HASKELL") "/emacs-tools/printer-help.txt") + "Help file for printers.") + +(defvar *haskell-printers-buffer* "*Haskell printers*") + +(defun haskell-printers () + "Set printers interactively." + (interactive) + (haskell-menu + *haskell-printers-help* + *haskell-printers-buffer* + 'get-current-printers + 'set-current-printers)) + +(defun get-current-printers () + (setq haskell-menu-current-flags t) + (haskell-send-command ":p?") + (while (eq haskell-menu-current-flags t) + (sleep-for 1))) + +(defun update-printers-list (data) + (setq haskell-menu-current-flags (read data))) + +(defun set-current-printers (flag on) + (let ((was-on (menu-item-currently-on-p flag))) + (cond ((and on (not was-on)) + (haskell-send-command (format ":p+ %s" flag))) + ((and (not on) was-on) + (haskell-send-command (format ":p- %s" flag))) + (t + nil)))) + + +;;; Equivalent stuff for the optimizers menu + +(defvar *haskell-optimizers-help* + (concat (getenv "HASKELL") "/emacs-tools/optimizer-help.txt") + "Help file for optimizers.") + +(defvar *haskell-optimizers-buffer* "*Haskell optimizers*") + +(defun haskell-optimizers () + "Set optimizers interactively." + (interactive) + (haskell-menu + *haskell-optimizers-help* + *haskell-optimizers-buffer* + 'get-current-optimizers + 'set-current-optimizers)) + +(defun get-current-optimizers () + (setq haskell-menu-current-flags t) + (haskell-send-command ":o?") + (while (eq haskell-menu-current-flags t) + (sleep-for 1))) + +(defun update-optimizers-list (data) + (setq haskell-menu-current-flags (read data))) + +(defun set-current-optimizers (flag on) + (let ((was-on (menu-item-currently-on-p flag))) + (cond ((and on (not was-on)) + (haskell-send-command (format ":o+ %s" flag))) + ((and (not on) was-on) + (haskell-send-command (format ":o- %s" flag))) + (t + nil)))) + + + + +;;; ================================================================== +;;; Random utilities +;;; ================================================================== + + +;;; Keep track of the association between pads, modules, and files. +;;; The global variable is a list of (pad-buffer-name module-name file-name) +;;; lists. + +(defvar *pad-mappings* () + "Associates pads with their corresponding module and file.") + +(defun record-pad-mapping (pname mname fname) + (setq *pad-mappings* + (cons (list pname mname fname) *pad-mappings*))) + +(defun get-module-from-pad (pname) + (car (cdr (assoc pname *pad-mappings*)))) + +(defun get-file-from-pad (pname) + (car (cdr (cdr (assoc pname *pad-mappings*))))) + +(defun lookup-pad (mname fname) + (lookup-pad-aux mname fname *pad-mappings*)) + +(defun lookup-pad-aux (mname fname list) + (cond ((null list) + nil) + ((and (equal mname (car (cdr (car list)))) + (equal fname (car (cdr (cdr (car list)))))) + (car (car list))) + (t + (lookup-pad-aux mname fname (cdr list))))) + + + +;;; Save any modified .hs and .hu files. +;;; Yes, the two set-buffer calls really seem to be necessary. It seems +;;; that y-or-n-p makes emacs forget we had temporarily selected some +;;; other buffer, and if you just do save-buffer directly it will end +;;; up trying to save the current buffer instead. The built-in +;;; save-some-buffers function has this problem.... + +(defvar *ask-before-saving* t) + +(defun save-modified-source-files (filename) + (let ((buffers (buffer-list)) + (found-any nil)) + (while buffers + (let ((buffer (car buffers))) + (if (and (buffer-modified-p buffer) + (save-excursion + (set-buffer buffer) + (and buffer-file-name + (source-file-p buffer-file-name) + (setq found-any t) + (or (null *ask-before-saving*) + (string= buffer-file-name filename) + (y-or-n-p + (format "Save file %s? " buffer-file-name)))))) + (save-excursion + (set-buffer buffer) + (save-buffer)))) + (setq buffers (cdr buffers))) + (if found-any + (message "") + (message "(No files need saving)")))) + +(defun source-file-p (filename) + (or (string-match "\\.hs$" filename) + (string-match "\\.lhs$" filename) + (string-match "\\.hu$" filename) + (string-match "\\.shu$" filename) + (string-match "\\.hsp$" filename) + (string-match "\\.prim$" filename))) + + +;;; Buffer utilities + +(defun haskell-move-marker () + "Moves the marker and point to the end of buffer" + (set-marker comint-last-input-end (point-max)) + (set-marker (process-mark (get-process "haskell")) (point-max)) + (goto-char (point-max))) + + +;;; Pad utils + +(defun create-main-pad () + (let ((buffer (get-buffer-create haskell-main-pad))) + (save-excursion + (set-buffer buffer) + (haskell-mode)) + (record-pad-mapping haskell-main-pad haskell-main-module haskell-main-file) + buffer)) + + +;;; Extract the name of the module the point is in, from the given buffer. + +(defvar *re-module* "^module\\s *\\|^>\\s *module\\s *") +(defvar *re-modname* "[A-Z]\\([a-z]\\|[A-Z]\\|[0-9]\\|'\\|_\\)*") + +(defun get-modname (buff) + "Get module name in BUFFER that point is in." + (save-excursion + (set-buffer buff) + (if (or (looking-at *re-module*) + (re-search-backward *re-module* (point-min) t) + (re-search-forward *re-module* (point-max) t)) + (progn + (goto-char (match-end 0)) + (if (looking-at *re-modname*) + (buffer-substring (match-beginning 0) (match-end 0)) + (haskell-mode-error "Module name not found!!"))) + "Main"))) + + +;;; Build the base name for a pad buffer. + +(defun get-padname (m) + "Build padname from module name" + (concat "*" m "-pad*")) + + +;;; Strip file extensions. +;;; Only strip off extensions we know about; e.g. +;;; "foo.hs" -> "foo" but "foo.bar" -> "foo.bar". + +(defvar *haskell-filename-regexp* "\\(.*\\)\\.\\(hs\\|lhs\\)$") + +(defun strip-fext (filename) + "Strip off the extension from a filename." + (if (string-match *haskell-filename-regexp* filename) + (substring filename (match-beginning 1) (match-end 1)) + filename)) + + +;;; Haskell mode error + +(defun haskell-mode-error (msg) + "Show MSG in message line as an error from the haskell mode" + (error (concat "Haskell mode: " msg))) + + + + +;;; ================================================================== +;;; Command generators +;;; ================================================================== + +;;; Generate Haskell command interface commands. These are very simple +;;; routines building the string commands to be sent to the haskell +;;; process. + +(defun ci-send-buffer (buff) + "Send BUFFER to haskell process." + (let ((str (buffer-string))) + (if (not (string-match "\\`\\s *\\'" str)) ; ignore if all whitespace + (save-excursion + (set-buffer buff) + (haskell-send-command str))))) + +(defun ci-kill () + (haskell-send-command ":kill")) + +(defun ci-clear () + (haskell-send-command ":clear")) + +(defun ci-set-file (file-name) + (haskell-send-command (concat ":file " file-name))) + +(defun ci-module (modname) + (setq *last-module* modname) + (haskell-send-command (concat ":module " modname))) + + +;;; Keeps track of the last file loaded. +;;; Change to do a :compile (temporary until new csys) +;;; 2-Aug-91 Dan Rabin. + +(defun ci-load (filename) + (haskell-send-command (concat ":load " filename))) + +(defun ci-load-main () + (haskell-send-command ":Main")) + +(defun ci-save () + (haskell-send-command ":save")) + +(defun ci-compile (filename) + (haskell-send-command (concat ":compile " filename))) + +(defun ci-run (filename) + (haskell-send-command (concat ":run " filename))) + +(defun ci-print-exp (exp) + (ci-set-file "interactive-expression-buffer") + (haskell-send-command (concat "= " exp))) + +(defun ci-send-name (name) + (let ((temp (make-temp-name "etemp"))) + (ci-set-file "interactive-expression-buffer") + (haskell-send-command (concat temp " = " name)))) + +(defun ci-eval () + (haskell-send-command ":eval")) + +(defun ci-quit () + (cond ((not (get-buffer-process *haskell-buffer*)) + (message "No process currently running.")) + ((y-or-n-p "Do you really want to quit Haskell? ") + (process-send-string "haskell" ":quit\n") + (set-haskell-status 'dead)) + (t + nil))) + + +;;; When setting emacs mode (on/off) +;;; (a) Set process-filter +;;; (b) Send :Emacs command to Haskell process + +(defun ci-emacs () + (haskell-reset) + (set-process-filter (get-process "haskell") 'process-haskell-output) + (haskell-send-command ":Emacs on")) + + + + + + +;;; ================================================================== +;;; Handle input in haskell process buffer; history commands. +;;; ================================================================== + +(defun haskell-get-old-input () + "Get old input text from Haskell process buffer." + (save-excursion + (if (re-search-forward haskell-prompt-pattern (point-max) 'move) + (goto-char (match-beginning 0))) + (cond ((re-search-backward haskell-prompt-pattern (point-min) t) + (comint-skip-prompt) + (let ((temp (point))) + (end-of-line) + (buffer-substring temp (point))))))) + + +;;; Modified for Haskell (taken from comint-send-input) + +(defun haskell-send-input () + "Send input to Haskell while in the process buffer" + (interactive) + (if *emacs* + (haskell-send-input-aux) + (comint-send-input))) + +(defun haskell-send-input-aux () + ;; Note that the input string does not include its terminal newline. + (let ((proc (get-buffer-process (current-buffer)))) + (if (not proc) + (haskell-mode-error "Current buffer has no process") + (let* ((pmark (process-mark proc)) + (pmark-val (marker-position pmark)) + (input (if (>= (point) pmark-val) + (buffer-substring pmark (point)) + (let ((copy (funcall comint-get-old-input))) + (goto-char pmark) + (insert copy) + copy)))) + (insert ?\n) + (if (funcall comint-input-filter input) + (ring-insert input-ring input)) + (funcall comint-input-sentinel input) + (set-marker (process-mark proc) (point)) + (set-marker comint-last-input-end (point)) + (haskell-send-data input))))) + + + +;;; ================================================================== +;;; Minibuffer input stuff +;;; ================================================================== + +;;; Haskell input history retrieval commands (taken from comint.el) +;;; M-p -- previous input M-n -- next input + +(defvar haskell-minibuffer-local-map nil + "Local map for minibuffer when in Haskell") + +(if haskell-minibuffer-local-map + nil + (progn + (setq haskell-minibuffer-local-map + (full-copy-sparse-keymap minibuffer-local-map)) + ;; Haskell commands + (define-key haskell-minibuffer-local-map "\ep" 'haskell-previous-input) + (define-key haskell-minibuffer-local-map "\en" 'haskell-next-input) + )) + +(defun haskell-previous-input (arg) + "Cycle backwards through input history." + (interactive "*p") + (let ((len (ring-length haskell-prompt-ring))) + (cond ((<= len 0) + (message "Empty input ring") + (ding)) + (t + (cond ((eq last-command 'haskell-previous-input) + (delete-region (mark) (point)) + (set-mark (point))) + (t + (setq input-ring-index + (if (> arg 0) -1 + (if (< arg 0) 1 0))) + (push-mark (point)))) + (setq input-ring-index (comint-mod (+ input-ring-index arg) len)) + (insert (ring-ref haskell-prompt-ring input-ring-index)) + (setq this-command 'haskell-previous-input)) + (t (ding))))) + +(defun haskell-next-input (arg) + "Cycle forwards through input history." + (interactive "*p") + (haskell-previous-input (- arg))) + +(defvar haskell-last-input-match "" + "Last string searched for by Haskell input history search, for defaulting. +Buffer local variable.") + +(defun haskell-previous-input-matching (str) + "Searches backwards through input history for substring match" + (interactive (let ((s (read-from-minibuffer + (format "Command substring (default %s): " + haskell-last-input-match)))) + (list (if (string= s "") haskell-last-input-match s)))) + (setq haskell-last-input-match str) ; update default + (let ((str (regexp-quote str)) + (len (ring-length haskell-prompt-ring)) + (n 0)) + (while (and (<= n len) + (not (string-match str (ring-ref haskell-prompt-ring n)))) + (setq n (+ n 1))) + (cond ((<= n len) (haskell-previous-input (+ n 1))) + (t (haskell-mode-error "Not found."))))) + + +;;; Actually read an expression from the minibuffer using the new keymap. + +(defun get-haskell-expression (prompt) + (let ((exp (read-from-minibuffer prompt nil haskell-minibuffer-local-map))) + (ring-insert haskell-prompt-ring exp) + exp)) + + + + +;;; ================================================================== +;;; User customization +;;; ================================================================== + +(defvar haskell-load-hook nil + "This hook is run when haskell is loaded in. +This is a good place to put key bindings." + ) + +(run-hooks 'haskell-load-hook) + + + + +;;;====================================================================== +;;; Tutorial mode setup +;;;====================================================================== + +;;; Set up additional key bindings for tutorial mode. + +(defvar ht-mode-map nil) + +(if ht-mode-map + nil + (progn + (setq ht-mode-map (make-sparse-keymap)) + (haskell-establish-key-bindings ht-mode-map) + (define-key ht-mode-map "\C-c\C-f" 'ht-next-page) + (define-key ht-mode-map "\C-c\C-b" 'ht-prev-page) + (define-key ht-mode-map "\C-c\C-l" 'ht-restore-page) + (define-key ht-mode-map "\C-c?" 'describe-mode))) + +(defun haskell-tutorial-mode () + "Major mode for running the Haskell tutorial. +You can use these commands: +\\{ht-mode-map}" + (interactive) + (kill-all-local-variables) + (use-local-map ht-mode-map) + (setq major-mode 'haskell-tutorial-mode) + (setq mode-name "Haskell Tutorial") + (set-syntax-table haskell-mode-syntax-table) + (run-hooks 'haskell-mode-hook)) + + +(defun haskell-tutorial () + "Run the haskell tutorial." + (interactive) + (ht-load-tutorial) + (ht-make-buffer) + (ht-display-page)) + + +;;; Load the tutorial file into a read-only buffer. Do not display this +;;; buffer. + +(defun ht-load-tutorial () + (let ((buffer (get-buffer *ht-file-buffer*))) + (if buffer + (save-excursion + (set-buffer buffer) + (beginning-of-buffer)) + (save-excursion + (set-buffer (setq buffer (get-buffer-create *ht-file-buffer*))) + (let ((fname (substitute-in-file-name *ht-source-file*))) + (if (file-readable-p fname) + (ht-load-tutorial-aux fname) + (call-interactively 'ht-load-tutorial-aux))))))) + +(defun ht-load-tutorial-aux (filename) + (interactive "fTutorial file: ") + (insert-file filename) + (set-buffer-modified-p nil) + (setq buffer-read-only t) + (beginning-of-buffer)) + + +;;; Create a buffer to use for messing about with each page of the tutorial. +;;; Put the buffer into haskell-tutorial-mode. + +(defun ht-make-buffer () + (find-file (concat "/tmp/" (make-temp-name "ht") ".hs")) + (setq *ht-temp-buffer* (buffer-name)) + (haskell-tutorial-mode)) + + +;;; Commands for loading text into the tutorial pad buffer + +(defun ht-next-page () + "Go to the next tutorial page." + (interactive) + (if (ht-goto-next-page) + (ht-display-page) + (beep))) + +(defun ht-goto-next-page () + (let ((buff (current-buffer))) + (unwind-protect + (progn + (set-buffer *ht-file-buffer*) + (search-forward "\C-l" nil t)) + (set-buffer buff)))) + +(defun ht-prev-page () + "Go to the previous tutorial page." + (interactive) + (if (ht-goto-prev-page) + (ht-display-page) + (beep))) + +(defun ht-goto-prev-page () + (let ((buff (current-buffer))) + (unwind-protect + (progn + (set-buffer *ht-file-buffer*) + (search-backward "\C-l" nil t)) + (set-buffer buff)))) + +(defun ht-goto-page (arg) + "Go to the tutorial page specified as the argument." + (interactive "sGo to page: ") + (if (ht-searchfor-page (format "-- Page %s " arg)) + (ht-display-page) + (beep))) + +(defun ht-goto-section (arg) + "Go to the tutorial section specified as the argument." + (interactive "sGo to section: ") + (if (ht-searchfor-page (format "-- Section %s " arg)) + (ht-display-page) + (beep))) + +(defun ht-searchfor-page (search-string) + (let ((buff (current-buffer))) + (unwind-protect + (progn + (set-buffer *ht-file-buffer*) + (let ((point (point))) + (beginning-of-buffer) + (if (search-forward search-string nil t) + t + (progn + (goto-char point) + nil)))) + (set-buffer buff)))) + +(defun ht-restore-page () + (interactive) + (let ((old-point (point))) + (ht-display-page) + (goto-char old-point))) + +(defun ht-display-page () + (set-buffer *ht-file-buffer*) + (let* ((beg (progn + (if (search-backward "\C-l" nil t) + (forward-line 1) + (beginning-of-buffer)) + (point))) + (end (progn + (if (search-forward "\C-l" nil t) + (beginning-of-line) + (end-of-buffer)) + (point))) + (text (buffer-substring beg end))) + (set-buffer *ht-temp-buffer*) + (erase-buffer) + (insert text) + (beginning-of-buffer))) diff --git a/emacs-tools/haskell.elc b/emacs-tools/haskell.elc new file mode 100644 index 0000000..165e126 --- /dev/null +++ b/emacs-tools/haskell.elc @@ -0,0 +1,788 @@ + +(provide (quote haskell)) + +(require (quote comint)) + +(defvar haskell-program-name (getenv "HASKELLPROG") "\ +*Program invoked by the haskell command") + +(defvar *haskell-buffer* "*haskell*" "\ +*Name of the haskell process buffer") + +(defvar *haskell-show-error* 1 "\ +*If not nil move to the buffer where the error was found") + +(defvar haskell-auto-create-process t "\ +*If not nil, create a Haskell process automatically when required to evaluate or compile Haskell code") + +(defvar *haskell-debug-in-lisp* nil "\ +*If not nil, enter Lisp debugger on error; otherwise, automagically return +to Haskell top-level.") + +(defvar *emacs* nil "\ +When not nil means haskell is in emacs mode") + +(defvar haskell-main-pad "*Main-pad*" "\ +Scratch pad associated with module Main") + +(defvar haskell-main-file "Main") + +(defvar haskell-main-module "Main") + +(defvar *last-loaded* haskell-main-file "\ +Last file loaded with a :load command - Defaults to Main") + +(defvar *last-loaded-modtime* nil "\ +Modification time of last file loaded, used to determine whether it +needs to be reloaded.") + +(defvar *last-module* haskell-main-module "\ +Last module set with a :module command - Defaults to Main") + +(defvar *last-pad* haskell-main-pad "\ +Last pad saved with a :save command - Defaults to Main") + +(defvar *ht-source-file* "$HASKELL/progs/tutorial/tutorial.hs") + +(defvar *ht-temp-buffer* nil) + +(defvar *ht-file-buffer* "Haskell-Tutorial-Master") + +(defvar haskell-mode-map nil "\ +Keymap used for haskell-mode") + +(defun haskell-establish-key-bindings (keymap) (byte-code "ÁÂÃ#ˆÁÄÅ#ˆÁÆÇ#ˆÁÈÉ#ˆÁÊË#ˆÁÌÍ#ˆÁÎÏ#ˆÁÐÑ#ˆÁÒÓ#ˆÁÔÕ#ˆÁÖ×#ˆÁØÙ#ˆÁÚÛ#ˆÁÜÝ#ˆÁÞß#ˆÁàá#ˆÁâã#‡" [keymap define-key "e" haskell-eval "r" haskell-run "m" haskell-run-main "" haskell-run-file "p" haskell-get-pad "" haskell-optimizers "" haskell-printers "c" haskell-compile "l" haskell-load "h" haskell-switch ":" haskell-command "q" haskell-exit "i" haskell-interrupt "u" haskell-edit-unit "d" haskell-please-recover "(" haskell-ensure-lisp-mode ")" haskell-resume-command-loop] 20)) + +(if haskell-mode-map nil (progn (setq haskell-mode-map (make-sparse-keymap)) (haskell-establish-key-bindings haskell-mode-map))) + +(defvar haskell-mode-syntax-table nil "\ +Syntax table used for haskell-mode") + +(if haskell-mode-syntax-table nil (setq haskell-mode-syntax-table (standard-syntax-table))) + +(defun haskell-mode nil "\ +Major mode for editing Haskell code to run in Emacs +The following commands are available: +\\{haskell-mode-map} + +A Haskell process can be fired up with \"M-x haskell\". + +Customization: Entry to this mode runs the hooks that are the value of variable +haskell-mode-hook. + +Windows: + +There are 3 types of windows associated with Haskell mode. They are: + *haskell*: which is the process window. + Pad: which are buffers available for each module. It is here + where you want to test things before preserving them in a + file. Pads are always associated with a module. + When issuing a command: + The pad and its associated module are sent to the Haskell + process prior to the execution of the command. + .hs: These are the files where Haskell programs live. They + have .hs as extension. + When issuing a command: + The file is sent to the Haskell process prior to the + execution of the command. + +Commands: + +Each command behaves differently according to the type of the window in which +the cursor is positioned when the command is issued . + +haskell-eval: \\[haskell-eval] + Always promts user for a Haskell expression to be evaluated. If in a + .hs file buffer, then the cursor tells which module is the current + module and the pad for that module (if any) gets loaded as well. + +haskell-run: \\[haskell-run] + Always queries for a variable of type Dialogue to be evaluated. + +haskell-run-main: \\[haskell-run-main] + Run Dialogue named main. + +haskell-run-file: \\[haskell-run-file] + Runs a file. Ideally the file has a set of variable of type Dialogue + that get evaluated. + +haskell-mode: \\[haskell-mode] + Puts the current buffer in haskell mode. + +haskell-compile: \\[haskell-compile] + Compiles file in current buffer. + +haskell-load: \\[haskell-load] + Loads file in current buffer. + +haskell-pad: \\[haskell-pad] + Creates a scratch pad for the current module. + +haskell-optimizers: \\[haskell-optimizers] + Shows the list of available optimizers. Commands for turning them on/off. + +haskell-printers: \\[haskell-printers] + Shows the list of available printers. Commands for turning them on/off. + +haskell-command: \\[haskell-command] + Prompts for a command to be sent to the command interface. You don't + need to put the : before the command. + +haskell-quit: \\[haskell-quit] + Terminates the haskell process. + +switch-to-haskell: \\[switch-to-haskell] + Switchs to the inferior Haskell buffer (*haskell*) and positions the + cursor at the end of the buffer. + +haskell-interrupt: \\[haskell-interrupt] + Interrupts haskell process and resets it. + +haskell-edit-unit: \\[haskell-edit-unit] + Edit the .hu file for the unit containing this file. +" (interactive) (byte-code "ÅˆÆ ˆÇ!ˆÈ‰ˆÉ‰ˆÊÃ!ˆË‰ˆÌ !ˆÍÎ!‡" [haskell-mode-map major-mode mode-name indent-line-function haskell-mode-syntax-table nil kill-all-local-variables use-local-map haskell-mode "Haskell" make-local-variable indent-relative-maybe set-syntax-table run-hooks haskell-mode-hook] 6)) + +(defvar inferior-haskell-mode-map nil) + +(if inferior-haskell-mode-map nil (setq inferior-haskell-mode-map (full-copy-sparse-keymap comint-mode-map)) (haskell-establish-key-bindings inferior-haskell-mode-map) (define-key inferior-haskell-mode-map " " (quote haskell-send-input))) + +(defvar haskell-source-modes (quote (haskell-mode)) "\ +*Used to determine if a buffer contains Haskell source code. +If it's loaded into a buffer that is in one of these major modes, +it's considered a Haskell source file.") + +(defvar haskell-prev-l/c-dir/file nil "\ +Caches the (directory . file) pair used in the last invocation of +haskell-run-file.") + +(defvar haskell-prompt-pattern "^[A-Z]\\([A-Z]\\|[a-z]\\|[0-9]\\)*>\\s-*" "\ +Regular expression capturing the Haskell system prompt.") + +(defvar haskell-prompt-ring nil "\ +Keeps track of input to haskell process from the minibuffer") + +(defvar tea-prompt-pattern "^>+\\s-*" "\ +Regular expression capturing the T system prompt.") + +(defvar haskell-version "Yale University Haskell Version 0.8, 1991" "\ +Current Haskell system version") + +(defun inferior-haskell-mode-variables nil (byte-code "À‡" [nil] 1)) + +(defun inferior-haskell-mode nil "\ +Major mode for interacting with an inferior Haskell process. + +The following commands are available: +\\{inferior-haskell-mode-map} + +A Haskell process can be fired up with \"M-x haskell\". + +Customization: Entry to this mode runs the hooks on comint-mode-hook and +inferior-haskell-mode-hook (in that order). + +You can send text to the inferior Haskell process from other buffers containing +Haskell source. + + +Windows: + +There are 3 types of windows in the inferior-haskell-mode. They are: + *haskell*: which is the process window. + Pad: which are buffers available for each module. It is here + where you want to test things before preserving them in a + file. Pads are always associated with a module. + When issuing a command: + The pad and its associated module are sent to the Haskell + process prior to the execution of the command. + .hs: These are the files where Haskell programs live. They + have .hs as extension. + When issuing a command: + The file is sent to the Haskell process prior to the + execution of the command. + +Commands: + +Each command behaves differently according to the type of the window in which +the cursor is positioned when the command is issued. + +haskell-eval: \\[haskell-eval] + Always promts user for a Haskell expression to be evaluated. If in a + .hs file, then the cursor tells which module is the current module and + the pad for that module (if any) gets loaded as well. + +haskell-run: \\[haskell-run] + Always queries for a variable of type Dialogue to be evaluated. + +haskell-run-main: \\[haskell-run-main] + Run Dialogue named main. + +haskell-run-file: \\[haskell-run-file] + Runs a file. Ideally the file has a set of variable of type Dialogue + that get evaluated. + +haskell-mode: \\[haskell-mode] + Puts the current buffer in haskell mode. + +haskell-compile: \\[haskell-compile] + Compiles file in current buffer. + +haskell-load: \\[haskell-load] + Loads file in current buffer. + +haskell-pad: \\[haskell-pad] + Creates a scratch pad for the current module. + +haskell-optimizers: \\[haskell-optimizers] + Shows the list of available optimizers. Commands for turning them on/off. + +haskell-printers: \\[haskell-printers] + Shows the list of available printers. Commands for turning them on/off. + +haskell-command: \\[haskell-command] + Prompts for a command to be sent to the command interface. You don't + need to put the : before the command. + +haskell-quit: \\[haskell-quit] + Terminates the haskell process. + +switch-to-haskell: \\[switch-to-haskell] + Switchs to the inferior Haskell buffer (*haskell*) and positions the + cursor at the end of the buffer. + +haskell-interrupt: \\[haskell-interrupt] + Interrupts haskell process and resets it. + +haskell-edit-unit: \\[haskell-edit-unit] + Edit the .hu file for the unit containing this file. + +The usual comint functions are also available. In particular, the +following are all available: + +comint-bol: Beginning of line, but skip prompt. Bound to C-a by default. +comint-delchar-or-maybe-eof: Delete char, unless at end of buffer, in + which case send EOF to process. Bound to C-d by default. + +Note however, that the default keymap bindings provided shadow some of +the default comint mode bindings, so that you may want to bind them +to your choice of keys. + +Comint mode's dynamic completion of filenames in the buffer is available. +(Q.v. comint-dynamic-complete, comint-dynamic-list-completions.) + +If you accidentally suspend your process, use \\[comint-continue-subjob] +to continue it." (interactive) (byte-code "ËˆÌ ˆ ‰ˆÍ ˆÎ‰ˆÏ‰ˆÐ‰ˆÑ !ˆÒ‰ˆÓ‰ˆÔ‰ˆÕÖ!ˆ× +!‰ ‡" [comint-prompt-regexp haskell-prompt-pattern major-mode mode-name mode-line-process inferior-haskell-mode-map comint-input-filter comint-input-sentinel comint-get-old-input haskell-prompt-ring input-ring-size nil comint-mode inferior-haskell-mode-variables inferior-haskell-mode "Inferior Haskell" (": %s : busy") use-local-map haskell-input-filter ignore haskell-get-old-input run-hooks inferior-haskell-mode-hook make-ring] 7)) + +(defvar inferior-haskell-mode-hook (quote haskell-fresh-start) "\ +*Hook for customizing inferior-Haskell mode") + +(defun haskell-input-filter (str) "\ +Don't save whitespace." (byte-code "ÁÂ\"?‡" [str string-match "\\s *"] 3)) + +(defvar *haskell-status* (quote dead) "\ +Status of the haskell process") + +(defun set-haskell-status (value) (byte-code " ‰ˆÂ ‡" [*haskell-status* value update-mode-line] 2)) + +(defun get-haskell-status nil (byte-code "‡" [*haskell-status*] 1)) + +(defun update-mode-line nil (byte-code "Šqˆ Ä=ƒʼn‚7 Æ=ƒlj‚7 È=ƒ(ɉ‚7 Ê=ƒ4ˉ‚7ÌÍ!ˆÎÏ !)‡" [*haskell-buffer* *haskell-status* mode-line-process t ready (": %s: ready") input (": %s: input") busy (": %s: busy") dead (": %s: dead") haskell-mode-error "Confused about status of haskell process!" set-buffer-modified-p buffer-modified-p] 4)) + +(defvar *haskell-saved-output* nil) + +(defun process-haskell-output (process str) "\ +Filter for output from Yale Haskell command interface" (byte-code "ÆÆÇ ÈŽ … P‰ˆÅ‰ˆÉ \"‰…< =?…-Ê O!ˆËÌ \" #‰ˆ‚ˆÍ \"‰ƒO ÅO‰‚S G‰ˆ =?…aÊ O!)+‡" [idx lastidx data *haskell-saved-output* str nil 0 match-data ((byte-code "Á!‡" [data store-match-data] 2)) ci-response-start haskell-display-output funcall ci-response-handler ci-prefix-start] 10)) + +(defvar *ci-responses* (quote (("r" "" haskell-got-ready) ("i" "" haskell-got-input-request) ("e" "" haskell-got-error) ("p.* +" "\\(p.*\\)?" haskell-got-printers) ("o.* +" "\\(o.*\\)?" haskell-got-optimizers) ("s.* +" "\\(s.*\\)?" haskell-got-message) (" +-> " " +\\(-\\(> ?\\)?\\)?" haskell-got-lisp-error) ("0\\] " "0\\(\\] ?\\)?" haskell-got-lisp-error) ("USER>>" "U\\(S\\(E\\(R\\(>>?\\)?\\)?\\)?\\)?" haskell-got-lisp-error) ("USER(.*):" "U\\(S\\(E\\(R\\((.*)?\\)?\\)?\\)?\\)?" haskell-got-lisp-error) ("USER .* : .* >" "U\\(S\\(E\\(R\\( .*\\( \\(:\\( .*\\( >?\\)?\\)?\\)?\\)?\\)?\\)?\\)?\\)?" haskell-got-lisp-error)))) + +(defun command-match-regexp (x) (byte-code "@‡" [x] 1)) + +(defun command-prefix-regexp (x) (byte-code "A@‡" [x] 1)) + +(defun command-handler (x) (byte-code "AA@‡" [x] 1)) + +(defun glue-together (extractor) (byte-code "ÄÅ +@\"ÆQ +A …$ÇÅ @\"ÆR‰ˆ A‰ˆ‚ ˆ*‡" [result extractor *ci-responses* stuff "\\(" funcall "\\)" "\\|\\("] 7)) + +(defvar *ci-response-regexp* (glue-together (quote command-match-regexp))) + +(defvar *ci-prefix-regexp* (concat "\\(" (glue-together (quote command-prefix-regexp)) "\\)\\'")) + +(defun ci-response-start (str idx) (byte-code "à +#‡" [*ci-response-regexp* str idx string-match] 4)) + +(defun ci-prefix-start (str idx) (byte-code "à +#‡" [*ci-prefix-regexp* str idx string-match] 4)) + +(defun ci-response-handler (str idx) (byte-code " Ã… + +?…+ÆÇ@! # =ƒ#È@!‰‚'A‰ˆ‚ˆ +?…4ÉÊ!ˆ +*‡" [list *ci-responses* result nil str idx string-match command-match-regexp command-handler haskell-mode-error "Failed to find command handler!!!"] 6)) + +(defun haskell-got-ready (str idx) (byte-code "ÁÂ!à ˆ)‡" [result match-end 0 haskell-reset] 3)) + +(defun haskell-got-input-request (str idx) (byte-code "ÁÂ!à ˆ)‡" [result match-end 0 get-user-input] 3)) + +(defun haskell-got-error (str idx) (byte-code "ÁÂ!à ˆ)‡" [result match-end 0 haskell-error-handler] 3)) + +(defun haskell-got-printers (str idx) (byte-code "ÃÄ!Å +Æ\\ÇZO!ˆ)‡" [result str idx match-end 0 update-printers-list 2 1] 6)) + +(defun haskell-got-optimizers (str idx) (byte-code "ÃÄ!Å +Æ\\ÇZO!ˆ)‡" [result str idx match-end 0 update-optimizers-list 2 1] 6)) + +(defun haskell-got-message (str idx) (byte-code "ÃÄ!Å +Æ\\ÇZO!ˆ)‡" [result str idx match-end 0 message 2 1] 6)) + +(defun haskell-got-lisp-error (str idx) (byte-code " \"ˆ G‡" [idx str haskell-handle-lisp-error] 3)) + +(defun haskell-handle-lisp-error (location str) (byte-code "Ä ÅO!ˆ +…Æ ˆ ƒÇ ‚È ‡" [str location *emacs* *haskell-debug-in-lisp* haskell-display-output nil ding haskell-talk-to-lisp haskell-flush-commands-and-reset] 5)) + +(defun loaded-tutorial-p nil (byte-code "…Â!…à ÄÂ!!\"‡" [*ht-temp-buffer* *last-loaded* get-buffer equal buffer-file-name] 6)) + +(defun haskell-flush-commands-and-reset nil (byte-code "Á ˆŠÂ!ˆÃ ˆÄ )‡" [*haskell-buffer* haskell-flush-command-queue switch-to-buffer haskell-ensure-lisp-mode haskell-resume-command-loop] 5)) + +(defun haskell-talk-to-lisp nil (byte-code "Á!ˆdbˆÂ ‡" [*haskell-buffer* pop-to-buffer haskell-ensure-lisp-mode] 3)) + +(defun haskell-resume-command-loop nil "\ +Resumes Haskell command processing after debugging in Lisp. \\[haskell-resume-command-loop]" (interactive) (byte-code "Áˆ?…ÂÃÄ\"ˆÅ ‡" [*emacs* nil process-send-string "haskell" "(mumble-user::restart-haskell) +" haskell-ensure-emacs-mode] 3)) + +(defun haskell-display-output (str) (byte-code "à Ä=ƒŠqˆÅ !)‚ Æ ÇŽÈ!ˆÅ !))‡" [*haskell-buffer* str window get-haskell-status dead haskell-display-output-aux selected-window ((byte-code "Á!‡" [window select-window] 2)) pop-to-buffer] 6)) + +(defun haskell-display-output-aux (str) (byte-code "Á ˆcˆÁ ‡" [str haskell-move-marker] 3)) + +(defun get-user-input nil (byte-code "ÁÂ!ˆÃ!ˆdbˆÄÅ!ˆÆ ‡" [*haskell-buffer* message "Haskell is waiting for input..." pop-to-buffer set-haskell-status input haskell-pop-data-queue] 5)) + +(defun haskell-error-handler nil (byte-code "Á ˆÂ ˆÃÄ!ˆÅÀ!‡" [nil ding haskell-flush-command-queue set-haskell-status ready haskell-end-interaction] 5)) + +(defvar *yh-error-def* "Error occured in definition of\\s *") + +(defvar *yh-error-line* "at line\\s *") + +(defvar *yh-error-file* "of file\\s *") + +(defvar *haskell-line* "\\([0-9]\\)*") + +(defun haskell-show-error nil "\ +Point out error to user if possible" (byte-code "qˆŠÂÂÂÅ ‰…Æ ‰…Ç ‰…$È #+)‡" [*haskell-buffer* function-name nil line-number filename get-function-name get-line-number get-filename point-error-to-user] 7)) + +(defvar *haskell-function-name* "\\([a-z]\\|[A-Z]\\|[0-9]\\|'\\|_\\|-\\)*") + +(defun get-function-name nil (byte-code "ÅeÁ#… ÆdÁ#ƒ&`Æ dÁ#ƒ!Ç +`\"‚\"Ä)‚'ć" [*yh-error-def* t beg *haskell-function-name* nil re-search-backward re-search-forward buffer-substring] 6)) + +(defun get-line-number nil (byte-code "ÅdÁ#ƒ `Å dÁ#ƒÆÇ +`\"!‚Ä)‚!ć" [*yh-error-line* t beg *haskell-line* nil re-search-forward string-to-int buffer-substring] 6)) + +(defun get-filename nil (byte-code "ÄdÁ#ƒ`ÄÅdÁ#ƒÆ +`\"‚Ã)‚Ç" [*yh-error-file* t beg nil re-search-forward "\\($\\| \\| \\)" buffer-substring] 5)) + +(defun point-error-to-user (function-name line-number filename) (byte-code "ÄÅ\"ƒ Æ !‚!Ç!È +!ƒÆ +!‚ É!)ˆÊ !‡" [filename *last-pad* fname line-number equal "Interactive" pop-to-buffer strip-fext get-buffer find-file-other-window goto-line] 8)) + +(defun haskell-reset nil (byte-code "ÀÁ!ˆÂ ‡" [set-haskell-status ready haskell-pop-command-queue] 3)) + +(defvar *command-interface-queue* nil "\ +Contains the commands to be sent to the Haskell command interface") + +(defun haskell-queue-or-execute (fn request data) (byte-code "ƒÅ +BC\"‰‚$Æ Ç=ƒÈ +\"‚$ +BC‰‡" [*command-interface-queue* request data fn t nconc get-haskell-status ready funcall] 5)) + +(defun haskell-send-command (str) "\ +Queues STRING for transmission to haskell process." (byte-code "ÁÂÃ#‡" [str haskell-queue-or-execute haskell-send-command-aux command] 4)) + +(defun haskell-send-command-aux (str) (byte-code "ÁÂ\"ˆÁÂÃ\"ˆÄ Å=?…ÆÇ!‡" [str process-send-string "haskell" " +" get-haskell-status input set-haskell-status busy] 5)) + +(defvar *begin-interaction-delimiter* nil "\ +*Delimiter showing an interaction has begun") + +(defun haskell-begin-interaction (msg) (byte-code "ÁÂÃ#‡" [msg haskell-queue-or-execute haskell-begin-interaction-aux begin] 4)) + +(defun haskell-begin-interaction-aux (msg) (byte-code "…Â!ˆ …Âà ÃQ!‡" [*begin-interaction-delimiter* msg haskell-display-output " +"] 5)) + +(defvar *end-interaction-delimiter* nil "\ +*Delimiter showing an interaction has ended") + +(defun haskell-end-interaction (msg) (byte-code "ÁÂÃ#‡" [msg haskell-queue-or-execute haskell-end-interaction-aux end] 4)) + +(defun haskell-end-interaction-aux (msg) (byte-code "…Â!ˆ …ÃÄ \"‡" [*end-interaction-delimiter* msg haskell-display-output message "%s"] 4)) + +(defun haskell-send-data (str) (byte-code "ÄÅ\"ƒÆÅ BCÂ$‰‚/Ç È=† Ç É=ƒ)Ê !‚/Å BC‰‡" [*command-interface-queue* str nil t assoc data merge-data-into-queue get-haskell-status ready input haskell-send-command-aux] 7)) + +(defun merge-data-into-queue (new head tail lasttail) (byte-code "?ƒÅ +\"ˆ ‚8@@Æ=ƒ Ç + A$‚8 ƒ2Å +\"ˆÅ +\"ˆ ‚8Å +\"ˆ +‡" [tail lasttail new head t rplacd data merge-data-into-queue] 7)) + +(defun haskell-pop-command-queue nil (byte-code "…N@A‰ˆ @Ã=ƒÄ A!‚M @Å=ƒ+Æ A!ˆÇ ‚M @È=ƒ<É A!ˆÇ ‚M @Ê=ƒJÄ A!‚MËÌ!)‡" [*command-interface-queue* entry t command haskell-send-command-aux begin haskell-begin-interaction-aux haskell-pop-command-queue end haskell-end-interaction-aux data haskell-mode-error "Invalid command in queue!!!"] 8)) + +(defun haskell-pop-data-queue nil (byte-code "…@ @Â=…A‰ˆÃ A!ˆÄ )‡" [*command-interface-queue* entry data haskell-send-command-aux haskell-pop-data-queue] 3)) + +(defun haskell-flush-command-queue nil (byte-code "Á‰‡" [*command-interface-queue* nil] 2)) + +(defun haskell nil "\ +Run an inferior Haskell process with input and output via buffer *haskell*. +Takes the program name from the variable haskell-program-name. +Runs the hooks from inferior-haskell-mode-hook +(after the comint-mode-hook is run). +(Type \\[describe-mode] in the process buffer for a list of commands.)" (interactive) (byte-code "ÃˆÄ !… Å!?…&ÆÇÈ +ÃÃ%‰ˆŠqˆÉ )ˆÊ!)‡" [haskell-buffer *haskell-buffer* haskell-program-name nil get-buffer comint-check-proc apply make-comint "haskell" inferior-haskell-mode display-buffer] 8)) + +(defun haskell-fresh-start nil (byte-code "ÈÉ!ˆÁ‰ˆ ‰ˆ ‰ˆÁ‰ˆÁ‰ˆÊ ‡" [*command-interface-queue* nil *last-loaded* haskell-main-file *last-pad* haskell-main-pad *emacs* *haskell-saved-output* set-haskell-status busy haskell-ensure-emacs-mode] 3)) + +(defun haskell-maybe-create-process nil (byte-code "…Á ‡" [haskell-auto-create-process haskell] 2)) + +(defun haskell-ensure-emacs-mode nil (byte-code " ˆÁ‰ˆÃ ‡" [*emacs* t create-main-pad ci-emacs] 3)) + +(defun haskell-ensure-lisp-mode nil "\ +Switch to talking to Lisp. \\[haskell-ensure-lisp-mode]" (interactive) (byte-code "ÁˆÁ‰‡" [*emacs* nil] 2)) + +(defun haskell-get-pad nil "\ +Creates a new scratch pad for the current module. +Signals an error if the current buffer is not a .hs file." (interactive) (byte-code "ÁˆÂ ƒÃp\"‚ÄÅ!)‡" [fname nil buffer-file-name do-get-pad haskell-mode-error "Not in a .hs buffer"] 4)) + +(defun do-get-pad (fname buff) (byte-code "Æ !† +ÇÈÂ\"É \" ?†Ê !‰?…7Ë!‰ˆÌ !‰ˆÍ !‰ˆÎ  #ˆÏ !ˆÐ +‡" [mname buff nil pname fname pbuff get-modname read-no-blanks-input "Scratch pad for module? " lookup-pad get-buffer get-padname generate-new-buffer buffer-name record-pad-mapping pop-to-buffer haskell-mode] 11)) + +(defun haskell-switch nil "\ +Switches to *haskell* buffer" (interactive) (byte-code "ÁˆÂ ˆÃÀ!‡" [t nil haskell-maybe-create-process switch-to-haskell] 3)) + +(defun switch-to-haskell (eob-p) "\ +Really switch to the *haskell* buffer. +With argument, positions cursor at end of buffer." (interactive "P") (byte-code "ˆÃ!ˆ …Ä ˆdb‡" [*haskell-buffer* eob-p nil pop-to-buffer push-mark] 3)) + +(defun haskell-command (str) "\ +Format STRING as a haskell command and send it to haskell process. \\[haskell-command]" (interactive "sHaskell command: ") (byte-code "ÁˆÂÃÄH!=ƒÅ ‚#ÆÇP!ˆÈÉP!ˆÊÇËQ!‡" [str nil 81 capitalize 0 ci-quit haskell-begin-interaction "Executing command: :" haskell-send-command ":" haskell-end-interaction " ...done."] 8)) + +(defun haskell-eval nil "\ +Evaluate expression in current module. \\[haskell-eval]" (interactive) (byte-code "ÀˆÁ ˆÂÃÄ!ÀÅ#‡" [nil haskell-maybe-create-process haskell-eval-aux get-haskell-expression "Haskell expression: " "Evaluating"] 6)) + +(defun haskell-run nil "\ +Run Haskell Dialogue in current module" (interactive) (byte-code "ÁˆÂ ˆÃÄÅ!ÀÆ#‡" [t nil haskell-maybe-create-process haskell-eval-aux get-haskell-expression "Haskell dialogue: " "Running"] 6)) + +(defun haskell-run-main nil "\ +Run Dialogue named main in current module" (interactive) (byte-code "ÁˆÂ ˆÃÄÀÅ#‡" [t nil haskell-maybe-create-process haskell-eval-aux "main" "Running"] 5)) + +(defun haskell-eval-aux (exp dialogue-p what) (byte-code "ÍÎ \"ƒ( + ÏÐ #Ñ  +&,‚¦Í Î \"ƒVÒ Óp!Ô \"ÏÕ #Ñ  +&,‚¦Ò ƒƒÒ Óp!Ô \"ÏÖ× ! $Ñ  +&,‚¦Îp!Ø !Ù !ÏÚ  $Ñ  +&,‡" [*haskell-buffer* pname *last-pad* mname *last-module* fname *last-loaded* msg what exp dialogue-p *ht-temp-buffer* t equal buffer-name format "%s: %s" haskell-eval-aux-aux buffer-file-name get-modname lookup-pad "%s (in tutorial): %s" "%s (in file %s): %s" file-name-nondirectory get-module-from-pad get-file-from-pad "%s (in pad %s): %s"] 29)) + +(defun haskell-eval-aux-aux (exp pname mname fname msg dialogue-p) (byte-code "Æ!ˆÇ ˆÈ !ˆÉ +!ˆ …Ê !ˆ ƒ!Ë !‚$Ì !ˆÍ ˆÎÏP!‡" [msg fname mname pname dialogue-p exp haskell-begin-interaction ci-kill haskell-load-file-if-modified ci-module haskell-save-pad-if-modified ci-send-name ci-print-exp ci-eval haskell-end-interaction " ...done."] 11)) + +(defun haskell-save-pad-if-modified (pad) (byte-code "ŠqˆÃ \"† Ä …!‰ˆÅ ˆÆ!ˆÇ!ˆÈ )‡" [pad haskell-main-pad *last-pad* equal buffer-modified-p ci-clear ci-set-file ci-send-buffer ci-save] 7)) + +(defun haskell-run-file nil "\ +Run all Dialogues in current file" (interactive) (byte-code "ˆà ˆÄÅ \"ƒÆÇ!‚&È ƒÉÈ !‚&ÉÊÅp!!!‡" [*haskell-buffer* t nil haskell-maybe-create-process equal buffer-name call-interactively haskell-run-file/process buffer-file-name haskell-run-file-aux get-file-from-pad] 11)) + +(defun haskell-run-file/process (filename) (interactive (byte-code "ÃÄ Â$‡" [haskell-prev-l/c-dir/file haskell-source-modes t comint-get-source "Haskell file to run: "] 5)) (byte-code "ĈŠ!ˆÆ !Ç !B‰ˆÈ !‡" [haskell-prev-l/c-dir/file haskell-source-modes t filename nil comint-check-source file-name-directory file-name-nondirectory haskell-run-file-aux] 5)) + +(defun haskell-run-file-aux (fname) (byte-code "à PÄ!ˆÅ ˆÆ +!ˆÇÈ !!ˆÉÊP!)‡" [msg fname buffer-file-name "Running file: " haskell-begin-interaction ci-kill save-modified-source-files ci-run strip-fext haskell-end-interaction " ...done."] 8)) + +(defun haskell-load nil "\ +Load current file" (interactive) (byte-code "ÃˆÄ ˆÅ ÆPƒ Ç !ˆÈ!ˆÉ ÊP!‚#ËÌ!*‡" [fname msg t nil haskell-maybe-create-process buffer-file-name "Loading file: " haskell-begin-interaction haskell-load-file-if-modified haskell-end-interaction " ...done." haskell-mode-error "Must be in a file to load"] 7)) + +(defun haskell-load-file-if-modified (filename) (byte-code "Å!ˆÆ +\"ƒ +‰ˆÇ ‚ ‰ˆÈÉ !!‡" [buffer-file-name filename haskell-main-file *last-loaded* t save-modified-source-files string= ci-load-main ci-load strip-fext] 6)) + +(defun haskell-compile nil "\ +Compile current file" (interactive) (byte-code "ˆà ˆÄ ƒÅÆP!ˆÇ!ˆÈÆÉQ!‚\"ÊË!)‡" [fname t nil haskell-maybe-create-process buffer-file-name haskell-begin-interaction "Compiling: " haskell-compile-file-if-modified haskell-end-interaction " ...done." haskell-mode-error "Must be in a file to compile"] 8)) + +(defun haskell-compile-file-if-modified (fname) (byte-code "Â!ˆÃÄ !!‡" [buffer-file-name fname save-modified-source-files ci-compile strip-fext] 4)) + +(defun haskell-exit nil "\ +Quit the haskell process" (interactive) (byte-code "ÁˆÃ ˆ… Ä!…ŠqˆÅÁ!)ˆÆ +!ˆÇ +!‡" [*ht-temp-buffer* nil *haskell-buffer* ci-quit get-buffer set-buffer-modified-p bury-buffer replace-buffer-in-windows] 6)) + +(defun haskell-interrupt nil "\ +Interrupt the haskell process" (interactive) (byte-code "ÀˆÁÂ!ˆÃÄ!‡" [nil haskell-send-command-aux "" haskell-end-interaction "done."] 3)) + +(defun haskell-edit-unit nil "\ +Edit the .hu file." (interactive) (byte-code "ÈŠƒ1ÆCÃÇ È !ˆ +…-ÉÊË!Ë !\"ƒ+Ì!‚,!+‚4ÍÎ!)‡" [fname find-file-not-found-hooks file-not-found nil units-fname buffer-file-name haskell-new-unit haskell-get-unit-file find-file-other-window units-add-source-file string= file-name-directory file-name-nondirectory haskell-mode-error "Not in a .hs buffer"] 10)) + +(defun haskell-new-unit nil (byte-code "Á‰‡" [file-not-found t] 2)) + +(defun units-add-source-file (file) (byte-code "ŠÁÂ!Ã\")‡" [file insert strip-fext " +"] 4)) + +(defun haskell-get-unit-file nil (byte-code "ÁŠÄ ˆÅÆdÂ#ƒÇÈ!É ˆÊ `\"‰)‚'ËÌ !ÍP‰)ˆ)‡" [name nil t beg beginning-of-buffer re-search-forward "-- unit:[ ]*" match-end 0 end-of-line buffer-substring strip-fext buffer-file-name ".hu"] 9)) + +(defun haskell-please-recover nil (interactive) (byte-code "ÀˆÁ ˆÂÃ!‡" [nil haskell-flush-commands-and-reset haskell-end-interaction "done."] 3)) + +(defvar haskell-menu-mode-map nil "\ +") + +(if (not haskell-menu-mode-map) (progn (setq haskell-menu-mode-map (make-keymap)) (suppress-keymap haskell-menu-mode-map t) (define-key haskell-menu-mode-map "m" (quote haskell-menu-mark)) (define-key haskell-menu-mode-map "u" (quote haskell-menu-unmark)) (define-key haskell-menu-mode-map "x" (quote haskell-menu-exit)) (define-key haskell-menu-mode-map "q" (quote haskell-menu-exit)) (define-key haskell-menu-mode-map " " (quote next-line)) (define-key haskell-menu-mode-map "" (quote haskell-menu-backup-unmark)) (define-key haskell-menu-mode-map "?" (quote describe-mode)))) + +(put (quote haskell-menu-mode) (quote mode-class) (quote special)) + +(defun haskell-menu-mode nil "\ +Major mode for editing Haskell flags. +Each line describes a flag. +Letters do not insert themselves; instead, they are commands. +m -- mark flag (turn it on) +u -- unmark flag (turn it off) +x -- exit; tell the Haskell process to update the flags, then leave menu. +q -- exit; same as x. +Precisely,\\{haskell-menu-mode-map}" (byte-code "Æ ˆÇ!ˆÂ‰ˆÂ‰ˆÈ‰ˆÉ‰ˆÊË!ˆÊÌ!ˆÊÍ!ˆÎÏ!‡" [haskell-menu-mode-map truncate-lines t buffer-read-only major-mode mode-name kill-all-local-variables use-local-map haskell-menu-mode "Haskell Flags Menu" make-local-variable haskell-menu-current-flags haskell-menu-request-fn haskell-menu-update-fn run-hooks haskell-menu-mode-hook] 7)) + +(defun haskell-menu (help-file buffer request-fn update-fn) (byte-code "Æ ˆÇ!ƒÈ!ˆeb‚$È!ˆÉ !ˆÊ ˆ ‰ˆ ‰ˆË ˆÌÍ!‡" [buffer help-file haskell-menu-request-fn request-fn haskell-menu-update-fn update-fn haskell-maybe-create-process get-buffer pop-to-buffer insert-file-contents haskell-menu-mode haskell-menu-mark-current message "m = mark; u = unmark; x = execute; q = quit; ? = more help."] 9)) + +(defvar *haskell-menu-marked* " on") + +(defvar *haskell-menu-unmarked* " ") + +(defvar *haskell-menu-marked-regexp* " on \\w") + +(defvar *haskell-menu-unmarked-regexp* " \\w") + +(defun haskell-menu-mark nil "\ +Mark flag to be turned on." (interactive) (byte-code "ÃˆÇ ˆÈ!ƒÉÊ!‚+È !ƒ(ÃË G!ˆ cˆÉÊ!)‚+ÉÊ!‡" [*haskell-menu-marked-regexp* *haskell-menu-unmarked-regexp* buffer-read-only nil *haskell-menu-unmarked* *haskell-menu-marked* t beginning-of-line looking-at forward-line 1 delete-char] 8)) + +(defun haskell-menu-unmark nil "\ +Unmark flag." (interactive) (byte-code "ÃˆÇ ˆÈ!ƒÉÊ!‚+È !ƒ(ÃË G!ˆ cˆÉÊ!)‚+ÉÊ!‡" [*haskell-menu-unmarked-regexp* *haskell-menu-marked-regexp* buffer-read-only nil *haskell-menu-marked* *haskell-menu-unmarked* t beginning-of-line looking-at forward-line 1 delete-char] 8)) + +(defun haskell-menu-backup-unmark nil "\ +Move up and unmark." (interactive) (byte-code "ÀˆÁÂ!ˆÃ ˆÁÂ!‡" [nil forward-line -1 haskell-menu-unmark] 4)) + +(defun haskell-menu-exit nil "\ +Update flags, then leave menu." (interactive) (byte-code "ÀˆÁ ˆÂ ‡" [nil haskell-menu-execute haskell-menu-quit] 3)) + +(defun haskell-menu-execute nil "\ +Tell haskell process to tweak flags." (interactive) (byte-code "ˆŠˆŠebˆ`d=?…7Æ!ƒÇ È Â#‚/Æ !ƒ.Ç È Ä#‚/ˆÉÊ!ˆ‚ )ˆË ‡" [*haskell-menu-unmarked-regexp* haskell-menu-update-fn nil *haskell-menu-marked-regexp* t start-setting-flags looking-at funcall haskell-menu-flag forward-line 1 finish-setting-flags] 11)) + +(defun haskell-menu-quit nil (interactive) (byte-code "ÀˆÁˆÂp!ˆÃp!‡" [nil "Make the menu go away." bury-buffer replace-buffer-in-windows] 3)) + +(defun haskell-menu-flag nil (byte-code "ŠÁ ˆÂÃ!ˆ`ÄÅ!ˆÆÇÈ!\"))‡" [beg beginning-of-line forward-char 6 re-search-forward " \\| " buffer-substring match-beginning 0] 7)) + +(defun start-setting-flags nil (byte-code "À‡" [nil] 1)) + +(defun finish-setting-flags nil (byte-code "ÀÁ!‡" [haskell-end-interaction "Setting flags....done."] 2)) + +(defun haskell-menu-mark-current nil (byte-code "Ä!ˆŠebˆ`d=?…;Å !…ÆÇ !ƒ!È ‚7Å +!…,ÆÇ !?ƒ4É ‚7ÊË!ˆ‚)‡" [haskell-menu-request-fn *haskell-menu-unmarked-regexp* *haskell-menu-marked-regexp* t funcall looking-at menu-item-currently-on-p haskell-menu-flag haskell-menu-mark haskell-menu-unmark forward-line 1] 12)) + +(defun menu-item-currently-on-p (item) (byte-code " \"‡" [item haskell-menu-current-flags member-string=] 3)) + +(defun member-string= (item list) (byte-code "?ƒ Á‚Ä +@\"ƒ‚Å +A\"‡" [list nil item t string= member-string=] 4)) + +(defvar *haskell-printers-help* (concat (getenv "HASKELL") "/emacs-tools/printer-help.txt") "\ +Help file for printers.") + +(defvar *haskell-printers-buffer* "*Haskell printers*") + +(defun haskell-printers nil "\ +Set printers interactively." (interactive) (byte-code "ˆÃ ÄÅ$‡" [*haskell-printers-help* *haskell-printers-buffer* nil haskell-menu get-current-printers set-current-printers] 5)) + +(defun get-current-printers nil (byte-code "Á‰ˆÂÃ!ˆÁ=…ÄÅ!ˆ‚‡" [haskell-menu-current-flags t haskell-send-command ":p?" sleep-for 1] 4)) + +(defun update-printers-list (data) (byte-code " !‰‡" [haskell-menu-current-flags data read] 3)) + +(defun set-current-printers (flag on) (byte-code "Å ! +… +?ƒÆÇÈ \"!‚) +?…ƒ(ÆÇÉ \"!‚)Ä)‡" [was-on flag on t nil menu-item-currently-on-p haskell-send-command format ":p+ %s" ":p- %s"] 7)) + +(defvar *haskell-optimizers-help* (concat (getenv "HASKELL") "/emacs-tools/optimizer-help.txt") "\ +Help file for optimizers.") + +(defvar *haskell-optimizers-buffer* "*Haskell optimizers*") + +(defun haskell-optimizers nil "\ +Set optimizers interactively." (interactive) (byte-code "ˆÃ ÄÅ$‡" [*haskell-optimizers-help* *haskell-optimizers-buffer* nil haskell-menu get-current-optimizers set-current-optimizers] 5)) + +(defun get-current-optimizers nil (byte-code "Á‰ˆÂÃ!ˆÁ=…ÄÅ!ˆ‚‡" [haskell-menu-current-flags t haskell-send-command ":o?" sleep-for 1] 4)) + +(defun update-optimizers-list (data) (byte-code " !‰‡" [haskell-menu-current-flags data read] 3)) + +(defun set-current-optimizers (flag on) (byte-code "Å ! +… +?ƒÆÇÈ \"!‚) +?…ƒ(ÆÇÉ \"!‚)Ä)‡" [was-on flag on t nil menu-item-currently-on-p haskell-send-command format ":o+ %s" ":o- %s"] 7)) + +(defvar *pad-mappings* nil "\ +Associates pads with their corresponding module and file.") + +(defun record-pad-mapping (pname mname fname) (byte-code " + EB‰‡" [*pad-mappings* pname mname fname] 3)) + +(defun get-module-from-pad (pname) (byte-code " \"A@‡" [pname *pad-mappings* assoc] 3)) + +(defun get-file-from-pad (pname) (byte-code " \"AA@‡" [pname *pad-mappings* assoc] 3)) + +(defun lookup-pad (mname fname) (byte-code "à +#‡" [mname fname *pad-mappings* lookup-pad-aux] 4)) + +(defun lookup-pad-aux (mname fname list) (byte-code "?ƒ Á‚*Å +@A@\"…Å @AA@\"ƒ$@@‚*Æ + A#‡" [list nil mname fname t equal lookup-pad-aux] 6)) + +(defvar *ask-before-saving* t) + +(defun save-modified-source-files (filename) (byte-code "È Â…O@É !…;Š qˆ …:Ê !…:ʼn…:?†:Ë \"†:ÌÍÎ \"!)…EŠ qˆÏ ))ˆA‰ˆ‚ˆ ƒZÐÑ!‚]ÐÒ!*‡" [buffers found-any nil buffer buffer-file-name t *ask-before-saving* filename buffer-list buffer-modified-p source-file-p string= y-or-n-p format "Save file %s? " save-buffer message "" "(No files need saving)"] 10)) + +(defun source-file-p (filename) (byte-code "ÁÂ\"†'ÁÃ\"†'ÁÄ\"†'ÁÅ\"†'ÁÆ\"†'ÁÇ\"‡" [filename string-match "\\.hs$" "\\.lhs$" "\\.hu$" "\\.shu$" "\\.hsp$" "\\.prim$"] 8)) + +(defun haskell-move-marker nil "\ +Moves the marker and point to the end of buffer" (byte-code "Ád\"ˆÁÂÃÄ!!d\"ˆdb‡" [comint-last-input-end set-marker process-mark get-process "haskell"] 6)) + +(defun create-main-pad nil (byte-code "Ä !ŠqˆÅ )ˆÆ + #ˆ)‡" [buffer haskell-main-pad haskell-main-module haskell-main-file get-buffer-create haskell-mode record-pad-mapping] 6)) + +(defvar *re-module* "^module\\s *\\|^>\\s *module\\s *") + +(defvar *re-modname* "[A-Z]\\([a-z]\\|[A-Z]\\|[0-9]\\|'\\|_\\)*") + +(defun get-modname (buff) "\ +Get module name in BUFFER that point is in." (byte-code "ŠqˆÄ !†Å eÂ#†Æ dÂ#ƒ6ÇÈ!bˆÄ !ƒ0ÉÊÈ!ÇÈ!\"‚3ËÌ!‚7Í)‡" [buff *re-module* t *re-modname* looking-at re-search-backward re-search-forward match-end 0 buffer-substring match-beginning haskell-mode-error "Module name not found!!" "Main"] 10)) + +(defun get-padname (m) "\ +Build padname from module name" (byte-code "ÁÂQ‡" [m "*" "-pad*"] 3)) + +(defvar *haskell-filename-regexp* "\\(.*\\)\\.\\(hs\\|lhs\\)$") + +(defun strip-fext (filename) "\ +Strip off the extension from a filename." (byte-code " \"ƒ ÃÄ!ÅÄ!O‚ ‡" [*haskell-filename-regexp* filename string-match match-beginning 1 match-end] 6)) + +(defun haskell-mode-error (msg) "\ +Show MSG in message line as an error from the haskell mode" (byte-code "ÁÂP!‡" [msg error "Haskell mode: "] 3)) + +(defun ci-send-buffer (buff) "\ +Send BUFFER to haskell process." (byte-code " ÃÄ\"?…Š qˆÅ!))‡" [str buff buffer-string string-match "\\`\\s *\\'" haskell-send-command] 4)) + +(defun ci-kill nil (byte-code "ÀÁ!‡" [haskell-send-command ":kill"] 2)) + +(defun ci-clear nil (byte-code "ÀÁ!‡" [haskell-send-command ":clear"] 2)) + +(defun ci-set-file (file-name) (byte-code "ÁÂP!‡" [file-name haskell-send-command ":file "] 3)) + +(defun ci-module (modname) (byte-code " ‰ˆÂà P!‡" [*last-module* modname haskell-send-command ":module "] 3)) + +(defun ci-load (filename) (byte-code "ÁÂP!‡" [filename haskell-send-command ":load "] 3)) + +(defun ci-load-main nil (byte-code "ÀÁ!‡" [haskell-send-command ":Main"] 2)) + +(defun ci-save nil (byte-code "ÀÁ!‡" [haskell-send-command ":save"] 2)) + +(defun ci-compile (filename) (byte-code "ÁÂP!‡" [filename haskell-send-command ":compile "] 3)) + +(defun ci-run (filename) (byte-code "ÁÂP!‡" [filename haskell-send-command ":run "] 3)) + +(defun ci-print-exp (exp) (byte-code "ÁÂ!ˆÃÄP!‡" [exp ci-set-file "interactive-expression-buffer" haskell-send-command "= "] 4)) + +(defun ci-send-name (name) (byte-code "ÂÃ!ÄÅ!ˆÆÇ Q!)‡" [temp name make-temp-name "etemp" ci-set-file "interactive-expression-buffer" haskell-send-command " = "] 6)) + +(defun ci-eval nil (byte-code "ÀÁ!‡" [haskell-send-command ":eval"] 2)) + +(defun ci-quit nil (byte-code "Ã!?ƒ ÄÅ!‚ÆÇ!ƒÈÉÊ\"ˆËÌ!‚‡" [*haskell-buffer* t nil get-buffer-process message "No process currently running." y-or-n-p "Do you really want to quit Haskell? " process-send-string "haskell" ":quit +" set-haskell-status dead] 6)) + +(defun ci-emacs nil (byte-code "À ˆÁÂÃ!Ä\"ˆÅÆ!‡" [haskell-reset set-process-filter get-process "haskell" process-haskell-output haskell-send-command ":Emacs on"] 5)) + +(defun haskell-get-old-input nil "\ +Get old input text from Haskell process buffer." (byte-code "ŠÃdÄ#… ÅÆ!bˆÇeÁ#…#È ˆ`É ˆÊ +`\"))‡" [haskell-prompt-pattern t temp re-search-forward move match-beginning 0 re-search-backward comint-skip-prompt end-of-line buffer-substring] 8)) + +(defun haskell-send-input nil "\ +Send input to Haskell while in the process buffer" (interactive) (byte-code "Áˆƒ  ‚ à ‡" [*emacs* nil haskell-send-input-aux comint-send-input] 3)) + +(defun haskell-send-input-aux nil (byte-code "Êp!?ƒËÌ!‚YÍ!Î !` +Yƒ$Ï `\"‚0Ð ! bˆ cˆ )ÑcˆÐ \"…AÒ \"ˆÐ \"ˆÓÍ!`\"ˆÓ `\"ˆÔ !+)‡" [proc pmark pmark-val input copy comint-get-old-input comint-input-filter input-ring comint-input-sentinel comint-last-input-end get-buffer-process haskell-mode-error "Current buffer has no process" process-mark marker-position buffer-substring funcall 10 ring-insert set-marker haskell-send-data] 14)) + +(defvar haskell-minibuffer-local-map nil "\ +Local map for minibuffer when in Haskell") + +(if haskell-minibuffer-local-map nil (progn (setq haskell-minibuffer-local-map (full-copy-sparse-keymap minibuffer-local-map)) (define-key haskell-minibuffer-local-map "p" (quote haskell-previous-input)) (define-key haskell-minibuffer-local-map "n" (quote haskell-next-input)))) + +(defun haskell-previous-input (arg) "\ +Cycle backwards through input history." (interactive "*p") (byte-code "ÇˆÈ !ÉXƒÊË!ˆÌ ‚V Í=ƒ'ÎÏ `\"ˆÐ`!‚B ÉVƒ1Ñ‚< ÉWƒ;Ò‚<ɉˆÓ`!ˆÔ \\\"‰ˆÕ \"cˆÍ‰)‡" [len haskell-prompt-ring t last-command input-ring-index arg this-command nil ring-length 0 message "Empty input ring" ding haskell-previous-input delete-region mark set-mark -1 1 push-mark comint-mod ring-ref] 11)) + +(defun haskell-next-input (arg) "\ +Cycle forwards through input history." (interactive "*p") (byte-code "ÁˆÂ[!‡" [arg nil haskell-previous-input] 2)) + +(defvar haskell-last-input-match "" "\ +Last string searched for by Haskell input history search, for defaulting. +Buffer local variable.") + +(defun haskell-previous-input-matching (str) "\ +Searches backwards through input history for substring match" (interactive (byte-code "ÂÃÄ \"!ÅÆ\"ƒ ‚C)‡" [s haskell-last-input-match read-from-minibuffer format "Command substring (default %s): " string= ""] 5)) (byte-code "Lj +‰ˆÈ +!É !Ê X…Ë +Ì \"\"?…* Í\\‰ˆ‚ˆ Xƒ9Î Í\\!‚<ÏÐ!+‡" [s haskell-last-input-match str len haskell-prompt-ring n t nil regexp-quote ring-length 0 string-match ring-ref 1 haskell-previous-input haskell-mode-error "Not found."] 7)) + +(defun get-haskell-expression (prompt) (byte-code "Å Â #Æ \"ˆ)‡" [exp prompt nil haskell-minibuffer-local-map haskell-prompt-ring read-from-minibuffer ring-insert] 4)) + +(defvar haskell-load-hook nil "\ +This hook is run when haskell is loaded in. +This is a good place to put key bindings.") + +(run-hooks (quote haskell-load-hook)) + +(defvar ht-mode-map nil) + +(if ht-mode-map nil (progn (setq ht-mode-map (make-sparse-keymap)) (haskell-establish-key-bindings ht-mode-map) (define-key ht-mode-map "" (quote ht-next-page)) (define-key ht-mode-map "" (quote ht-prev-page)) (define-key ht-mode-map " " (quote ht-restore-page)) (define-key ht-mode-map "?" (quote describe-mode)))) + +(defun haskell-tutorial-mode nil "\ +Major mode for running the Haskell tutorial. +You can use these commands: +\\{ht-mode-map}" (interactive) (byte-code "ĈŠˆÆ!ˆÇ‰ˆÈ‰ˆÉ !ˆÊË!‡" [ht-mode-map major-mode mode-name haskell-mode-syntax-table nil kill-all-local-variables use-local-map haskell-tutorial-mode "Haskell Tutorial" set-syntax-table run-hooks haskell-mode-hook] 5)) + +(defun haskell-tutorial nil "\ +Run the haskell tutorial." (interactive) (byte-code "ÀˆÁ ˆÂ ˆÃ ‡" [nil ht-load-tutorial ht-make-buffer ht-display-page] 4)) + +(defun ht-load-tutorial nil (byte-code "Ä !ƒŠqˆÅ )‚/ŠÆ !‰qˆÇ !È +!ƒ*É +!‚-ÊÉ!)))‡" [buffer *ht-file-buffer* fname *ht-source-file* get-buffer beginning-of-buffer get-buffer-create substitute-in-file-name file-readable-p ht-load-tutorial-aux call-interactively] 8)) + +(defun ht-load-tutorial-aux (filename) (interactive "fTutorial file: ") (byte-code "ÁˆÄ!ˆÅÁ!ˆÃ‰ˆÆ ‡" [filename nil buffer-read-only t insert-file set-buffer-modified-p beginning-of-buffer] 4)) + +(defun ht-make-buffer nil (byte-code "ÁÂÃÄ!ÅQ!ˆÆ ‰ˆÇ ‡" [*ht-temp-buffer* find-file "/tmp/" make-temp-name "ht" ".hs" buffer-name haskell-tutorial-mode] 5)) + +(defun ht-next-page nil "\ +Go to the next tutorial page." (interactive) (byte-code "ÀˆÁ ƒ  ‚à ‡" [nil ht-goto-next-page ht-display-page beep] 4)) + +(defun ht-goto-next-page nil (byte-code "pÄŽ qˆÅÆÂÃ#))‡" [buff *ht-file-buffer* nil t ((byte-code "q‡" [buff] 1)) search-forward " "] 4)) + +(defun ht-prev-page nil "\ +Go to the previous tutorial page." (interactive) (byte-code "ÀˆÁ ƒ  ‚à ‡" [nil ht-goto-prev-page ht-display-page beep] 4)) + +(defun ht-goto-prev-page nil (byte-code "pÄŽ qˆÅÆÂÃ#))‡" [buff *ht-file-buffer* nil t ((byte-code "q‡" [buff] 1)) search-backward " "] 4)) + +(defun ht-goto-page (arg) "\ +Go to the tutorial page specified as the argument." (interactive "sGo to page: ") (byte-code "ÁˆÂÃÄ\"!ƒÅ ‚Æ ‡" [arg nil ht-searchfor-page format "-- Page %s " ht-display-page beep] 5)) + +(defun ht-goto-section (arg) "\ +Go to the tutorial section specified as the argument." (interactive "sGo to section: ") (byte-code "ÁˆÂÃÄ\"!ƒÅ ‚Æ ‡" [arg nil ht-searchfor-page format "-- Section %s " ht-display-page beep] 5)) + +(defun ht-searchfor-page (search-string) (byte-code "pÆŽ qˆ`Ç ˆÈ ÄÅ#ƒÅ‚ +bˆÄ)))‡" [buff *ht-file-buffer* point search-string nil t ((byte-code "q‡" [buff] 1)) beginning-of-buffer search-forward] 5)) + +(defun ht-restore-page nil (interactive) (byte-code "Áˆ` ˆb)‡" [old-point nil ht-display-page] 2)) + +(defun ht-display-page nil (byte-code "qˆÇÈÂÃ#ƒÉÊ!‚Ë ˆ`ÌÈÂÃ#ƒ#Í ‚%Î ˆ`Ï \"qˆÐ ˆ cˆË +‡" [*ht-file-buffer* beg nil t end text *ht-temp-buffer* search-backward " " forward-line 1 beginning-of-buffer search-forward beginning-of-line end-of-buffer buffer-substring erase-buffer] 10)) diff --git a/emacs-tools/optimizer-help.txt b/emacs-tools/optimizer-help.txt new file mode 100644 index 0000000..3ed2ae2 --- /dev/null +++ b/emacs-tools/optimizer-help.txt @@ -0,0 +1,5 @@ +Optimizer switches + inline Aggressively inline functions + constant Hoist constant expressions to top-level + foldr Perform foldr/build deforestation + lisp Tell the Lisp compiler to work hard to produce best code diff --git a/emacs-tools/printer-help.txt b/emacs-tools/printer-help.txt new file mode 100644 index 0000000..a525ad1 --- /dev/null +++ b/emacs-tools/printer-help.txt @@ -0,0 +1,24 @@ +General messages + compiling Printed when the compilation system starts a compilation + loading Printed when a previously compiled unit is loaded + reading Prints the name of the file being parsed + extension Enables printing withinn extensions +Timings + time Prints the time that it takes to execute a computation + phase-time Prints the time of each phase of compilation +Compiler passes + parse Prints the program recreated from ast + import Lists all symbols imported and exported for each module + scope Print the program after scoping and precedence parsing + depend Prints entire program in nested let's + type Prints signatures during inference + cfn Prints entire program after context free normalization + depend2 Like depend + flic Prints entire program as flic code + optimize Prints entire program as optimized flic code + optimize-extra Prints extra verbose information during optimization + strictness Print strictness of all functions and variables + codegen Prints generated Lisp code + codegen-flic Prints generated Lisp code and associated flic code + dumper Prints the code in the interface + dump-stat Prints statistics for the interface file diff --git a/flic/README b/flic/README new file mode 100644 index 0000000..51af8a5 --- /dev/null +++ b/flic/README @@ -0,0 +1,2 @@ +This directory contains code to define FLIC structures and associated +pretty-printers, and the traversal to convert AST to FLIC structures. diff --git a/flic/ast-to-flic.scm b/flic/ast-to-flic.scm new file mode 100644 index 0000000..d756723 --- /dev/null +++ b/flic/ast-to-flic.scm @@ -0,0 +1,277 @@ +;;; ast-to-flic.scm -- convert AST to flic structures. +;;; +;;; author : Sandra Loosemore +;;; date : 3 Apr 1992 +;;; +;;; + + +;;; ==================================================================== +;;; Support +;;; ==================================================================== + + +(define-walker ast-to-flic ast-td-ast-to-flic-walker) + +(define-local-syntax (define-ast-to-flic ast-type lambda-list . body) + `(define-walker-method ast-to-flic ,ast-type ,lambda-list ,@body)) + +(define (ast-to-flic big-let) + (ast-to-flic-let-aux (let-decls big-let) (make-flic-void) '#t)) + +(define (ast-to-flic-1 ast-node) + (call-walker ast-to-flic ast-node)) + +(define (ast-to-flic/list l) + (map (function ast-to-flic-1) l)) + +(define (init-flic-var var value toplevel?) + (setf (var-value var) value) + (setf (var-toplevel? var) toplevel?) + (setf (var-simple? var) + (and value + (or (is-type? 'flic-const value) + (is-type? 'flic-pack value)))) + (setf (var-strict? var) '#f) + ;; Remember the strictness annotation. + (let ((strictness-ann (lookup-annotation var '|Strictness|))) + (setf (var-strictness var) + (if strictness-ann + (adjust-annotated-strictness var + (parse-strictness (car (annotation-value-args strictness-ann)))) + '#f))) + ;; If the variable has an inline annotation, rewrite its value + ;; from var = value + ;; to var = let temp = value in temp + ;; (Necessary for inlining recursive definitions.) + (let ((inline-ann (lookup-annotation var '|Inline|))) + (when inline-ann + (setf (var-force-inline? var) '#t) + (setf (var-value var) (wrap-with-let var value)))) + var) + +(define (wrap-with-let var value) + (let ((temp (copy-temp-var (def-name var)))) + (init-flic-var temp (copy-flic value (list (cons var temp))) '#f) + (make-flic-let (list temp) (make-flic-ref temp) '#t))) + + +;;; ==================================================================== +;;; ast expression structs +;;; ==================================================================== + + +(define-ast-to-flic lambda (object) + (make-flic-lambda + (map (lambda (pat) + (init-flic-var + (cond ((var-pat? pat) + (var-ref-var (var-pat-var pat))) + (else + (error "Bad lambda pattern: ~s." pat))) + '#f + '#f)) + (lambda-pats object)) + (ast-to-flic-1 (lambda-body object)))) + + +;;; For LET, the CFN has turned all of the definitions into +;;; simple assignments to a variable. The dependency analyzer +;;; adds recursive-decl-groups for things which need to be bound +;;; with LETREC. + +(define-ast-to-flic let (object) + (ast-to-flic-let-aux + (let-decls object) + (ast-to-flic-1 (let-body object)) + '#f)) + +(define (ast-to-flic-let-aux decls body toplevel?) + (multiple-value-bind (bindings newbody) + (ast-to-flic-bindings decls body toplevel?) + (if (null? bindings) + newbody + (make-flic-let bindings newbody toplevel?)))) + +(define (ast-to-flic-bindings decls body toplevel?) + (if (null? decls) + (values '() body) + (multiple-value-bind (bindings newbody) + (ast-to-flic-bindings (cdr decls) body toplevel?) + (cond ((is-type? 'valdef (car decls)) + ;; Continue collecting bindings. + (let* ((decl (car decls)) + (pat (valdef-lhs decl)) + (exp (single-definition-rhs decl))) + (values + (cond ((var-pat? pat) + (cons + (init-flic-var + (var-ref-var (var-pat-var pat)) + (ast-to-flic-1 exp) + toplevel?) + bindings)) + (else + (error "Definition has invalid pattern: ~s." decl))) + newbody))) + ((not (is-type? 'recursive-decl-group (car decls))) + (error "Decl has weird value: ~s." (car decls))) + (toplevel? + ;; We don't do any of this mess with top level bindings. + ;; Turn it into one big letrec. + (multiple-value-bind (more-bindings newerbody) + (ast-to-flic-bindings + (recursive-decl-group-decls (car decls)) + newbody + toplevel?) + (values (nconc more-bindings bindings) + newerbody))) + (else + ;; Otherwise, turn remaining bindings into a nested + ;; let or letrec, and put that in the body of a new + ;; letrec. + (multiple-value-bind (more-bindings newerbody) + (ast-to-flic-bindings + (recursive-decl-group-decls (car decls)) + (if (null? bindings) + newbody + (make-flic-let bindings newbody '#f)) + toplevel?) + (values + '() + (if (null? more-bindings) + newerbody + (make-flic-let more-bindings newerbody '#t))))) + )))) + + +(define (single-definition-rhs decl) + (let* ((def-list (valdef-definitions decl)) + (def (car def-list)) + (rhs-list (single-fun-def-rhs-list def)) + (rhs (car rhs-list))) + ;; All of this error checking could be omitted for efficiency, since + ;; none of these conditions are supposed to happen anyway. + (cond ((not (null? (cdr def-list))) + (error "Decl has multiple definitions: ~s." decl)) + ((not (null? (single-fun-def-where-decls def))) + (error "Definition has non-null where-decls list: ~s." decl)) + ((not (null? (cdr rhs-list))) + (error "Definition has multiple right-hand-sides: ~s." decl)) + ((not (is-type? 'omitted-guard (guarded-rhs-guard rhs))) + (error "Definition has a guard: ~s." decl))) + (guarded-rhs-rhs rhs))) + + + +;;; These are all straightforward translations. + +(define-ast-to-flic if (object) + (make-flic-if + (ast-to-flic-1 (if-test-exp object)) + (ast-to-flic-1 (if-then-exp object)) + (ast-to-flic-1 (if-else-exp object)))) + +(define-ast-to-flic case-block (object) + (make-flic-case-block + (case-block-block-name object) + (ast-to-flic/list (case-block-exps object)))) + +(define-ast-to-flic return-from (object) + (make-flic-return-from + (return-from-block-name object) + (ast-to-flic-1 (return-from-exp object)))) + +(define-ast-to-flic and-exp (object) + (make-flic-and (ast-to-flic/list (and-exp-exps object)))) + + +;;; Applications. Uncurry here. It's more convenient to do the +;;; optimizer on fully uncurried applications. After the optimizer +;;; has run, all applications are adjusted based on observed arity +;;; of the functions and the saturated? flag is set correctly. + +(define-ast-to-flic app (object) + (ast-to-flic-app-aux object '())) + +(define (ast-to-flic-app-aux object args) + (if (is-type? 'app object) + (ast-to-flic-app-aux + (app-fn object) + (cons (ast-to-flic-1 (app-arg object)) args)) + (make-flic-app (ast-to-flic-1 object) args '#f))) + + +;;; References + +(define-ast-to-flic var-ref (object) + (make-flic-ref (var-ref-var object))) + +(define-ast-to-flic con-ref (object) + (make-flic-pack (con-ref-con object))) + + +;;; Constants + +(define-ast-to-flic integer-const (object) + (make-flic-const (integer-const-value object))) + + +;;; We should probably add a type field to flic-const but at the moment +;;; I'll force the value to be a list of numerator, denominator. + +(define-ast-to-flic float-const (object) + (let ((e (float-const-exponent object)) + (n (float-const-numerator object)) + (d (float-const-denominator object))) + (make-flic-const + (if (> e 0) + (list (* n (expt 10 e)) d) + (list n (* d (expt 10 (- e)))))))) + +(define-ast-to-flic char-const (object) + (make-flic-const (char-const-value object))) + + +(define-ast-to-flic string-const (object) + (let ((value (string-const-value object))) + (if (equal? value "") + (make-flic-pack (core-symbol "Nil")) + (make-flic-const value)))) + + + +;;; Random stuff + +(define-ast-to-flic con-number (object) + (make-flic-con-number + (con-number-type object) + (ast-to-flic-1 (con-number-value object)))) + +(define-ast-to-flic sel (object) + (make-flic-sel + (sel-constructor object) + (sel-slot object) + (ast-to-flic-1 (sel-value object)))) + +(define-ast-to-flic is-constructor (object) + (make-flic-is-constructor + (is-constructor-constructor object) + (ast-to-flic-1 (is-constructor-value object)))) + +(define-ast-to-flic void (object) + (declare (ignore object)) + (make-flic-void)) + + +;;; This hack make strictness annotations work. It adds #t's which correspond +;;; to the strictness of the dict params. + +(define (adjust-annotated-strictness v s) + (let* ((ty (var-type v)) + (c (gtype-context ty))) + (dolist (c1 c) + (dolist (c2 c1) + (declare (ignorable c2)) + (push '#t s))) + s)) diff --git a/flic/copy-flic.scm b/flic/copy-flic.scm new file mode 100644 index 0000000..373fbd4 --- /dev/null +++ b/flic/copy-flic.scm @@ -0,0 +1,146 @@ +;;; copy-flic.scm -- general copy functions for flic structures +;;; +;;; author : Sandra Loosemore +;;; date : 23 Feb 1993 +;;; +;;; + + +;;; The var-renamings argument is an a-list. It's used to map local vars +;;; in the input expression to new, gensymed vars. + +(define-flic-walker copy-flic (object var-renamings)) + +(define (copy-flic-list objects var-renamings) + (let ((result '())) + (dolist (o objects) + (push (copy-flic o var-renamings) result)) + (nreverse result))) + + +(define (copy-flic-top object) + (copy-flic object '())) + + +(define-copy-flic flic-lambda (object var-renamings) + (let ((new-vars (map (lambda (v) + (let ((new (copy-temp-var (def-name v)))) + (push (cons v new) var-renamings) + (when (var-force-strict? v) + (setf (var-force-strict? new) '#t)) + (init-flic-var new '#f '#f))) + (flic-lambda-vars object)))) + (make-flic-lambda + new-vars + (copy-flic (flic-lambda-body object) var-renamings)))) + + +;;; Hack to avoid concatenating multiple gensym suffixes. + +(define (copy-temp-var sym) + (if (gensym? sym) + (let* ((string (symbol->string sym)) + (n (string-length string)) + (root (find-string-prefix string 0 n))) + (create-temp-var root)) + (create-temp-var sym))) + +(define (find-string-prefix string i n) + (declare (type string string) (type fixnum i n)) + (cond ((eqv? i n) + string) + ((char-numeric? (string-ref string i)) + (substring string 0 i)) + (else + (find-string-prefix string (+ i 1) n)))) + + +(define-copy-flic flic-let (object var-renamings) + (let ((new-vars (map (lambda (v) + (let ((new (copy-temp-var (def-name v)))) + (when (var-force-inline? v) + (setf (var-force-inline? new) '#t)) + (push (cons v new) var-renamings) + new)) + (flic-let-bindings object)))) + (for-each + (lambda (new old) + (init-flic-var new (copy-flic (var-value old) var-renamings) '#f)) + new-vars + (flic-let-bindings object)) + (make-flic-let + new-vars + (copy-flic (flic-let-body object) var-renamings) + (flic-let-recursive? object)))) + +(define-copy-flic flic-app (object var-renamings) + (make-flic-app + (copy-flic (flic-app-fn object) var-renamings) + (copy-flic-list (flic-app-args object) var-renamings) + (flic-app-saturated? object))) + +(define-copy-flic flic-ref (object var-renamings) + (let* ((var (flic-ref-var object)) + (entry (assq var var-renamings))) + (if entry + (make-flic-ref (cdr entry)) + (make-flic-ref var)))) ; don't share structure + + +(define-copy-flic flic-const (object var-renamings) + (declare (ignore var-renamings)) + (make-flic-const (flic-const-value object))) ; don't share structure + +(define-copy-flic flic-pack (object var-renamings) + (declare (ignore var-renamings)) + (make-flic-pack (flic-pack-con object))) ; don't share structure + + +;;; Don't have to gensym new block names; these constructs always +;;; happen in pairs. + +(define-copy-flic flic-case-block (object var-renamings) + (make-flic-case-block + (flic-case-block-block-name object) + (copy-flic-list (flic-case-block-exps object) var-renamings))) + +(define-copy-flic flic-return-from (object var-renamings) + (make-flic-return-from + (flic-return-from-block-name object) + (copy-flic (flic-return-from-exp object) var-renamings))) + +(define-copy-flic flic-and (object var-renamings) + (make-flic-and + (copy-flic-list (flic-and-exps object) var-renamings))) + +(define-copy-flic flic-if (object var-renamings) + (make-flic-if + (copy-flic (flic-if-test-exp object) var-renamings) + (copy-flic (flic-if-then-exp object) var-renamings) + (copy-flic (flic-if-else-exp object) var-renamings))) + +(define-copy-flic flic-sel (object var-renamings) + (make-flic-sel + (flic-sel-con object) + (flic-sel-i object) + (copy-flic (flic-sel-exp object) var-renamings))) + +(define-copy-flic flic-is-constructor (object var-renamings) + (make-flic-is-constructor + (flic-is-constructor-con object) + (copy-flic (flic-is-constructor-exp object) var-renamings))) + +(define-copy-flic flic-con-number (object var-renamings) + (make-flic-con-number + (flic-con-number-type object) + (copy-flic (flic-con-number-exp object) var-renamings))) + +(define-copy-flic flic-void (object var-renamings) + (declare (ignore object var-renamings)) + (make-flic-void)) ; don't share structure + + + + + + diff --git a/flic/flic-structs.scm b/flic/flic-structs.scm new file mode 100644 index 0000000..2aab75c --- /dev/null +++ b/flic/flic-structs.scm @@ -0,0 +1,89 @@ +;;; flic-structs.scm -- structures to define FLIC intermediate language +;;; +;;; author : Sandra Loosemore +;;; date : 24 Mar 1992 + + + +(define-struct flic-exp + (type-template flic-td) + (slots + (unboxed? (type bool) (default '#f) (bit #t)) + (cheap? (type bool) (default '#f) (bit #t)))) + + +;;; Use a macro to define each subtype and a BOA constructor. +;;; Maybe eventually the constructors will need to do additional +;;; initialization and have to be defined by hand. + +(define-local-syntax (define-flic name . slots) + (let* ((maker (symbol-append 'make- name)) + (pred (symbol-append name '?)) + (args (map (function car) slots)) + (inits (map (lambda (x) (list x x)) args))) + `(begin + (define-struct ,name + (include flic-exp) + (predicate ,pred) + (slots ,@slots)) + (define (,maker ,@args) (make ,name ,@inits)) + ',name))) + +(define-flic flic-lambda + (vars (type (list var))) + (body (type flic-exp))) + +(define-flic flic-let + ;; value exp is stored in var-value slot + (bindings (type (list var))) + (body (type flic-exp)) + (recursive? (type bool) (bit #t))) + +(define-flic flic-app + (fn (type flic-exp)) + (args (type (list flic-exp))) + ;; true if number of args exactly matches arity of fn + (saturated? (type bool) (bit #t))) + +(define-flic flic-ref + (var (type var))) + +(define-flic flic-const + (value (type t))) + +(define-flic flic-pack + (con (type con))) + +(define-flic flic-case-block + (block-name (type symbol)) + (exps (type (list flic-exp)))) + +(define-flic flic-return-from + (block-name (type symbol)) + (exp (type flic-exp))) + +(define-flic flic-and + (exps (type (list flic-exp)))) + +(define-flic flic-if + (test-exp (type flic-exp)) + (then-exp (type flic-exp)) + (else-exp (type flic-exp))) + +(define-flic flic-sel + (con (type con)) + (i (type int)) + (exp (type flic-exp))) + +(define-flic flic-is-constructor + (con (type con)) + (exp (type flic-exp))) + +(define-flic flic-con-number + (type (type algdata)) + (exp (type flic-exp))) + +(define-flic flic-void + ) + + diff --git a/flic/flic-td.scm b/flic/flic-td.scm new file mode 100644 index 0000000..01253b0 --- /dev/null +++ b/flic/flic-td.scm @@ -0,0 +1,21 @@ +;;; flic-td.scm -- define type descriptor for flic structs +;;; +;;; author : Sandra Loosemore +;;; date : 6 Oct 1992 +;;; + +(define-struct flic-td + (include type-descriptor) + (slots + (codegen-walker (type (maybe procedure)) (default '#f)) + (optimize-walker (type (maybe procedure)) (default '#f)) + (postoptimize-walker (type (maybe procedure)) (default '#f)) + (fun-strictness-walk-walker (type (maybe procedure)) (default '#f)) + (var-strictness-walk-walker (type (maybe procedure)) (default '#f)) + (compute-strictness-walk-walker (type (maybe procedure)) (default '#f)) + (print-strictness-walker (type (maybe procedure)) (default '#f)) + (box-analysis-walker (type (maybe procedure)) (default '#f)) + (copy-flic-walker (type (maybe procedure)) (default '#f)) + (dump-flic-walker (type (maybe procedure)) (default '#f)) + (flic-invariant?-walker (type (maybe procedure)) (default '#f)) + )) diff --git a/flic/flic-walker.scm b/flic/flic-walker.scm new file mode 100644 index 0000000..846d89f --- /dev/null +++ b/flic/flic-walker.scm @@ -0,0 +1,21 @@ +;;; flic-walker.scm -- macros for defining code walkers for flic +;;; +;;; author : Sandra Loosemore +;;; date : 7 May 1992 +;;; + + +;;; (define-flic-walker foo (object)) +;;; creates a macro (define-foo type (object) . body) +;;; and a function (foo object) that dispatches on the type of object. + +(define-syntax (define-flic-walker name args) + (let ((accessor-name (symbol-append 'flic-td- name '-walker)) + (definer-name (symbol-append 'define- name))) + `(begin + (define-walker ,name ,accessor-name) + (define-local-syntax (,definer-name type args . body) + `(define-walker-method ,',name ,type ,args ,@body)) + (define (,name ,@args) + (call-walker ,name ,@args))))) + diff --git a/flic/flic.scm b/flic/flic.scm new file mode 100644 index 0000000..8aa389a --- /dev/null +++ b/flic/flic.scm @@ -0,0 +1,29 @@ +;;; flic.scm -- compilation unit for flic stuff +;;; +;;; author : Sandra Loosemore +;;; date : 7 Apr 1992 +;;; + + +(define-compilation-unit flic + (source-filename "$Y2/flic/") + (unit flic-td + (source-filename "flic-td.scm")) + (unit flic-structs + (source-filename "flic-structs.scm") + (require flic-td)) + (unit print-flic + (source-filename "print-flic.scm") + (require flic-structs printer-support)) + (unit ast-to-flic + (source-filename "ast-to-flic.scm") + (require flic-structs ast haskell-utils)) + (unit flic-walker + (source-filename "flic-walker.scm")) + (unit copy-flic + (source-filename "copy-flic.scm") + (require flic-walker flic-structs)) + (unit invariant + (source-filename "invariant.scm") + (require flic-walker flic-structs)) + ) diff --git a/flic/invariant.scm b/flic/invariant.scm new file mode 100644 index 0000000..c6c0486 --- /dev/null +++ b/flic/invariant.scm @@ -0,0 +1,88 @@ +;;; invariant.scm -- look for invariant expressions +;;; +;;; author : Sandra Loosemore +;;; date : 12 Mar 1993 +;;; +;;; +;;; The function flic-invariant? returns true if the expression is +;;; invariant with respect to a set of local variable bindings. + +(define-flic-walker flic-invariant? (object local-bindings)) + +(define (flic-invariant-list? objects local-bindings) + (if (null objects) + '#t + (and (flic-invariant? (car objects) local-bindings) + (flic-invariant-list? (cdr objects) local-bindings)))) + +(define-flic-invariant? flic-lambda (object local-bindings) + (flic-invariant? (flic-lambda-body object) + (cons (flic-lambda-vars object) local-bindings))) + +(define-flic-invariant? flic-let (object local-bindings) + (let* ((bindings (flic-let-bindings object)) + (body (flic-let-body object)) + (recursive? (flic-let-recursive? object)) + (inner-stuff (cons bindings local-bindings))) + (and (flic-invariant-list? (map (function var-value) bindings) + (if recursive? inner-stuff local-bindings)) + (flic-invariant? body inner-stuff)))) + +(define-flic-invariant? flic-app (object local-bindings) + (and (flic-invariant? (flic-app-fn object) local-bindings) + (flic-invariant-list? (flic-app-args object) local-bindings))) + +(define-flic-invariant? flic-ref (object local-bindings) + (let ((var (flic-ref-var object))) + (or (var-toplevel? var) + (flic-local-var? var local-bindings)))) + +(define (flic-local-var? var local-bindings) + (cond ((null? local-bindings) + '#f) + ((memq var (car local-bindings)) + '#t) + (else + (flic-local-var? var (cdr local-bindings))))) + +(define-flic-invariant? flic-const (object local-bindings) + (declare (ignore object local-bindings)) + '#t) + +(define-flic-invariant? flic-pack (object local-bindings) + (declare (ignore object local-bindings)) + '#t) + +(define-flic-invariant? flic-case-block (object local-bindings) + (flic-invariant-list? (flic-case-block-exps object) local-bindings)) + +(define-flic-invariant? flic-return-from (object local-bindings) + (flic-invariant? (flic-return-from-exp object) local-bindings)) + +(define-flic-invariant? flic-and (object local-bindings) + (flic-invariant-list? (flic-and-exps object) local-bindings)) + +(define-flic-invariant? flic-if (object local-bindings) + (and (flic-invariant? (flic-if-test-exp object) local-bindings) + (flic-invariant? (flic-if-then-exp object) local-bindings) + (flic-invariant? (flic-if-else-exp object) local-bindings))) + +(define-flic-invariant? flic-sel (object local-bindings) + (flic-invariant? (flic-sel-exp object) local-bindings)) + +(define-flic-invariant? flic-is-constructor (object local-bindings) + (flic-invariant? (flic-is-constructor-exp object) local-bindings)) + +(define-flic-invariant? flic-con-number (object local-bindings) + (flic-invariant? (flic-con-number-exp object) local-bindings)) + +(define-flic-invariant? flic-void (object local-bindings) + (declare (ignore object local-bindings)) + '#t) + + + + + + + diff --git a/flic/print-flic.scm b/flic/print-flic.scm new file mode 100644 index 0000000..6077f57 --- /dev/null +++ b/flic/print-flic.scm @@ -0,0 +1,130 @@ +;;; print-flic.scm -- printers for FLIC structures +;;; +;;; author : Sandra Loosemore +;;; date : 30 Mar 1992 +;;; +;;; + + +;;; For now, printing of FLIC structures is controlled by the same +;;; *print-ast-syntax* variable as for AST structures. +;;; Maybe eventually this should use its own variable. + +(define-syntax (define-flic-printer type lambda-list . body) + `(define-ast-printer ,type ,lambda-list ,@body)) + +(define-flic-printer flic-lambda (object xp) + (with-ast-block (xp) + (write-string "\\ " xp) + (write-ordinary-list (flic-lambda-vars object) xp) + (write-string " ->" xp) + (write-whitespace xp) + (write (flic-lambda-body object) xp))) + +(define-flic-printer flic-let (object xp) + (pprint-logical-block (xp '() "" "") ; no extra indentation + (write-string "let " xp) + (write-layout-rule (flic-let-bindings object) xp + (lambda (v xp) + (with-ast-block (xp) + (write v xp) + (write-string " =" xp) + (write-whitespace xp) + (write (var-value v) xp)))) + (write-whitespace xp) + (write-string "in " xp) + (write (flic-let-body object) xp))) + +(define-flic-printer flic-app (object xp) + (with-ast-block (xp) + (maybe-paren-flic-object (flic-app-fn object) xp) + (write-whitespace xp) + (write-flic-list (flic-app-args object) xp))) + +(define (maybe-paren-flic-object object xp) + (cond ((or (flic-ref? object) + (flic-const? object) + (flic-pack? object)) + (write object xp)) + (else + (write-char #\( xp) + (write object xp) + (write-char #\) xp)))) + +(define (write-flic-list objects xp) + (write-delimited-list objects xp (function maybe-paren-flic-object) "" "" "")) + +(define-flic-printer flic-ref (object xp) + (write (flic-ref-var object) xp)) + +(define-flic-printer flic-const (object xp) + (write (flic-const-value object) xp)) + +(define-flic-printer flic-pack (object xp) + (write-string "pack/" xp) + (write (flic-pack-con object) xp)) + +(define-flic-printer flic-case-block (object xp) + (with-ast-block (xp) + (write-string "case-block " xp) + (write (flic-case-block-block-name object) xp) + (write-whitespace xp) + (write-layout-rule (flic-case-block-exps object) xp (function write)))) + +(define-flic-printer flic-return-from (object xp) + (with-ast-block (xp) + (write-string "return-from " xp) + (write (flic-return-from-block-name object) xp) + (write-whitespace xp) + (write (flic-return-from-exp object) xp))) + +(define-flic-printer flic-and (object xp) + (with-ast-block (xp) + (write-string "and " xp) + (write-layout-rule (flic-and-exps object) xp (function write)))) + +(define-flic-printer flic-if (object xp) + (with-ast-block (xp) + (write-string "if " xp) + (write (flic-if-test-exp object) xp) + (write-whitespace xp) + (with-ast-block (xp) + (write-string "then" xp) + (write-whitespace xp) + (write (flic-if-then-exp object) xp)) + (write-whitespace xp) + (with-ast-block (xp) + (write-string "else" xp) + (write-whitespace xp) + (write (flic-if-else-exp object) xp)) + )) + + +(define-flic-printer flic-sel (object xp) + (with-ast-block (xp) + (write-string "sel/" xp) + (write (flic-sel-con object) xp) + (write-char #\/ xp) + (write (flic-sel-i object) xp) + (write-whitespace xp) + (write (flic-sel-exp object) xp))) + +(define-flic-printer flic-is-constructor (object xp) + (with-ast-block (xp) + (write-string "is-constructor/" xp) + (write (flic-is-constructor-con object) xp) + (write-whitespace xp) + (write (flic-is-constructor-exp object) xp))) + +(define-flic-printer flic-con-number (object xp) + (with-ast-block (xp) + (write-string "con/" xp) + (write (flic-con-number-type object) xp) + (write-whitespace xp) + (write (flic-con-number-exp object) xp))) + +(define-flic-printer flic-void (object xp) + (declare (ignore object)) + (write-string "Void" xp)) + + \ No newline at end of file diff --git a/haskell-development b/haskell-development new file mode 100755 index 0000000..bf318af --- /dev/null +++ b/haskell-development @@ -0,0 +1,69 @@ +#!/bin/csh +# +# Set up for Yale Haskell 2.x development +# + +source $HASKELL/haskell-setup + + +# You need to set this environment variable to point to the root +# directory where you have installed the Yale Haskell sources. + +setenv Y2 $HASKELL + + +# Define some aliases for getting the right RCS options. +# These aliases are only for use at Yale. + +alias rco 'co -l' +alias rci 'ci -u' + + +# Find the "right" lisp executable. +# You really only need to set up for the particular lisp implementation(s) +# you are going to build the system under (you can comment out the rest). + +# The Lucid CL executable we've been using is the one without fancy +# stuff like CLOS loaded. + +setenv LUCID /cs/licensed/sclisp-4.0/lisp-4-0-base + + +# Setup for CMUCL. We have this aliased to a script that will select +# the right core file for the machine you are running on. + +setenv CMUCL $Y2/bin/run-cmucl +setenv CMUCLBIN $Y2/bin/cmucl +setenv CMUCLLIB $Y2/bin + + +# This is AKCL, not ordinary KCL (which is too brain-damaged). + +setenv AKCL /net/nebula/homes/systems/hcompile/akcl/xbin/kcl + + +# Set up for Franz Allegro. +# This is a hack; we run Allegro on both sparc and next boxes, and +# we need to be able to tell which kind of machine we're running on so +# the binaries don't get jumbled up together. + +if (-e /vmunix) then + setenv ALLEGRODIR allegro + setenv ALLEGRO /usr/licensed/allegro/cl +else if (-e /mach) then + setenv ALLEGRODIR allegro-next + setenv ALLEGRO /usr/local/bin/lisp +else + echo "Can't figure out what kind of machine you're on!" +endif + + +# Set up for Harlequin Lispworks. + +setenv LISPWORKS /usr/licensed/bin/lispworks + + +# Set up for WCL +# This is not supported! +# setenv WCL /net/nebula/homes/systems/hcompile/wcl-2.14/bin/wcl +# setenv LD_LIBRARY_PATH /net/nebula/homes/systems/hcompile/wcl-2.14/lib diff --git a/haskell-setup b/haskell-setup new file mode 100755 index 0000000..16d6dcd --- /dev/null +++ b/haskell-setup @@ -0,0 +1,27 @@ +#!/bin/csh +# +# Set up for Yale Haskell 2.x users. +# + +setenv PRELUDE $HASKELL/progs/prelude +setenv HASKELL_LIBRARY $HASKELL/progs/lib + +# You may need to change this to point at the appropriate subdirectory, +# depending on which Lisp is being used. +setenv PRELUDEBIN $PRELUDE/lucid +#setenv PRELUDEBIN $PRELUDE/cmu + +# You may need to change this to point at the appropriate subdirectory, +# depending on which Lisp is being used. +setenv LIBRARYBIN $HASKELL_LIBRARY/bin/lucid +#setenv LIBRARYBIN $HASKELL_LIBRARY/bin/cmu + +# You may need to change this to point at the appropriate executable, +# depending on which Lisp is being used. +setenv HASKELLPROG $HASKELL/bin/haskell +#setenv HASKELLPROG $HASKELL/bin/cmu-haskell + +# You only need this next definition if you are using the CMU CL version +# of haskell and you have /tmp mounted on a tmpfs file system (it won't +# be able to initialize itself otherwise). +setenv CMUCL_EMPTYFILE /var/tmp/empty diff --git a/import-export/README b/import-export/README new file mode 100644 index 0000000..4bd3bc4 --- /dev/null +++ b/import-export/README @@ -0,0 +1,15 @@ +This is the import / export phase. This process is accomplished as follows: + +a) Local definitions are created in each module. These are entered into the + local symbol table. +b) Imports to non-local modules are completely resolved. +c) Local import/export is performed via a fixpoint: + 1) Export: definitions added in the previous round are filtered by the + export list and placed in a fresh export list. + 2) Each module imports from the fresh export list of the other modules. + Any import not already present is placed on a new fresh export list. + When no fresh exports are generated, the iteration is complete. +d) Missing exports and imports are checked for. + + + diff --git a/import-export/ie-errors.scm b/import-export/ie-errors.scm new file mode 100644 index 0000000..16180f5 --- /dev/null +++ b/import-export/ie-errors.scm @@ -0,0 +1,154 @@ +;;; Error checks & calls for the import-export code + +;;; this is called at the end of import-export to look for +;;; a) exported entities that were never found +;;; b) imported entities that were never found +;;; c) renamed entities that were never found +;;; d) hidden entities that were never found + +(define (check-missing-names) + (dolist (export (module-exports *module*)) + (remember-context export + (signal-missing-export export))) + (dolist (import-decl (module-imports *module*)) + (remember-context import-decl + (with-slots import-decl (mode specs renamings) import-decl + ;; *** I'm confused. Aren't these errors already detected + ;; *** by import-all-entities and import-named-entities? + ;; jcp: no - a final check is needed after all symbols have moved. + (cond ((eq? mode 'all) + (dolist (entity specs) + (signal-unused-hiding + (entity-name entity) + (import-decl-module-name import-decl)))) + (else + (dolist (entity specs) + (signal-entity-not-found + (entity-name entity) + (import-decl-module-name import-decl))))) + (find-unused-renamings renamings import-decl))))) + +(define (find-unused-renamings renamings import-decl) + (dolist (r renamings) + (when (not (renaming-referenced? r)) + (remember-context r + (signal-unused-renaming (renaming-from r) + (import-decl-module-name import-decl)))))) + +(define (check-duplicates l entity) + (when (not (null? (find-duplicates l))) + (signal-duplicate-names-in-entity entity))) + +;;; There are a ton of possible errors in import-export. All error +;;; calls are found here: + +(define (signal-missing-export export) + (recoverable-error 'missing-export + "Module ~A exports ~A, but provides no definition for it." + *module-name* export)) + +(define (signal-unused-renaming name module-name) + (recoverable-error 'unused-renaming + "The name ~a is included in the renaming list of an import declaration,~%~ + but is not among the entities being imported from module ~a." + name module-name)) + +(define (signal-unused-hiding name module-name) + (recoverable-error 'unused-hiding + "The name ~a is included in the hiding list of an import declaration,~%~ + but is not among the entities exported from module ~a." + name module-name)) + +(define (signal-multiple-name-conflict name old-local-name def) + (recoverable-error 'multiple-name-conflict + "In module ~A, the symbol ~A from module ~A is known as both ~A and ~A." + *module-name* (def-name def) (def-module def) name old-local-name)) + + +(define (signal-undefined-module-import name) + (fatal-error 'undefined-module-import + "Cannot find module ~A, imported by module ~A." + name *module-name*)) + + +(define (signal-undefined-module-export name) + (fatal-error 'undefined-module-export + "Cannot find module ~A, exported by module ~A." + name *module-name*)) + + +(define (signal-self-import name) + (fatal-error 'self-import + "Module ~A cannot import itself." + name)) + +(define (signal-missing-prelude) + (fatal-error 'missing-prelude "Can't find module Prelude.")) + +(define (signal-missing-prelude-core) + (fatal-error 'missing-prelude "Can't find module PreludeCore.")) + +(define (signal-export-not-imported name) + (recoverable-error 'export-not-imported + "Module ~A is exported from ~A,~%~ + but is not also imported into that module." + name *module-name*)) + +(define (signal-entity-not-found name module-name) + (fatal-error 'entity-not-found + "The entity ~a is not exported from module ~a." name module-name)) + +(define (signal-synonym-needs-dots name module-name) + (declare (ignore module-name)) + (fatal-error 'synonym-needs-dots + "The entity ~a is a type synonym; to name it in an import or export~%~ + list, you must use `~a(..)' as the entity." + name name)) + +(define (signal-wrong-definition expected name module-name) + (fatal-error 'wrong-definition + "The entity ~a does not name a ~a in module ~a." + name expected module-name)) + +(define (signal-abstract-type name module-name) + (fatal-error 'abstract-type + "The entity ~a names an abstract type in module ~a;~%~ + you cannot import or export its constructors." + name module-name)) + +(define (signal-extra-constituent entity name what) + (fatal-error 'extra-constituent + "The entity specification ~a includes the ~a name ~a,~%~ + which is not present in its definition." + entity what name)) + +(define (signal-missing-constituent entity name what) + (fatal-error 'missing-constituent + "The entity specification ~a does not include the ~a name ~a,~%~ + which is part of its definition." + entity what name)) + +(define (signal-duplicate-names-in-entity entity) + (fatal-error 'duplicate-names-in-entity + "The entity specification ~a includes duplicate names." + entity)) + +(define (signal-export-method-var name) + (fatal-error 'export-method-var + "You can't export the method ~a like an ordinary variable." + name)) + +(define (signal-prelude-renaming def name) + (recoverable-error 'cant-rename-core + "Names in PreludeCore cannot be renamed: ~a was renamed to ~a" + (def-name def) name)) + +(define (signal-non-local-fixity op) + (recoverable-error 'fixity-must-be-local + "The fixity for ~A will be ignored since it is not defined in this module" + op)) + +(define (signal-fixity-not-var/con op) + (recoverable-error 'fixity-requires-var-or-con + "The fixity for ~A will be ignored since it is not a value or constructor" + op)) diff --git a/import-export/ie-utils.scm b/import-export/ie-utils.scm new file mode 100644 index 0000000..20b6c1d --- /dev/null +++ b/import-export/ie-utils.scm @@ -0,0 +1,121 @@ + +;;; This file contains utilities, globals, and macros used by the +;;; import-export system. + +(define *new-exports-found?* '#f) ; used by the fixpoint iteration + +;;; A group is a collection of related symbols. It is represented +;;; by a list of (name,def) pairs. The first element is the head +;;; of the group; the group is entered in the export table under the +;;; name of the head only. Groups for vars and synonyms have only the +;;; head. Data types and classes have the constructors or methods in +;;; the tail of the group. + +(define (group-name x) ; name of the head + (tuple-2-1 (car x))) + +(define (group-definition x) ; definition of the head + (tuple-2-2 (car x))) + +;;; The name & entry are the head of the group. Others is a list of +;;; name - definition pairs. +(define (make-group name entry . others) + (if (null? others) + (list (cons name entry)) + (cons (cons name entry) (car others)))) + +(define (hidden-constructors? group) + (null? (cdr group))) + +(define (strip-constructors group) + (list (car group))) + +;;; rename-group applies the current renaming to every +;;; name in a group. When uses, a renaming is marked to allow unused +;;; renamings to be detected. + +(define (rename-group g renamings) + (if (null? renamings) + g + (map (lambda (n-d) + (let* ((def (tuple-2-2 n-d)) + (keep-name? (or (con? def) (var? def))) + (n (tuple-2-1 n-d)) + (name (if keep-name? n (add-con-prefix/symbol n))) + (renaming (locate-renaming name renamings))) + (cond (renaming + (let ((new-name + (if keep-name? + (renaming-to renaming) + (remove-con-prefix/symbol + (renaming-to renaming))))) + (when (and (def-prelude? def) + (not (eq? (def-name def) new-name))) + (signal-prelude-renaming def new-name) + (setf new-name (def-name def))) + (setf (renaming-referenced? renaming) '#t) + (tuple new-name def))) + (else n-d)))) + g))) + +(define (locate-renaming name renamings) + (if (null? renamings) + '#f + (if (eq? name (renaming-from (car renamings))) + (car renamings) + (locate-renaming name (cdr renamings))))) + +(define (gather-algdata-group name def) + (cons (tuple name def) + (gather-group (algdata-constrs def)))) + +(define (gather-class-group name def) + (cons (tuple name def) + (gather-group (class-method-vars def)))) + +(define (gather-group defs) + (if (null? defs) + '() + (let ((local-name (local-name (car defs)))) + (if (eq? local-name '#f) + '() + (cons (tuple local-name (car defs)) + (gather-group (cdr defs))))))) + +;;; These deal with `hiding' lists. + +;;; Note: as per the new report, no need to worry about anything but the +;;; group head and the entity name since only var, Class(..),Alg(..) allowed + +(define (in-hiding-list? group hiding) + (cond ((null? hiding) + '#f) + ((eq? (entity-name (car hiding)) (group-name group)) + '#t) + (else (in-hiding-list? group (cdr hiding))))) + +(define (remove-entity group hiding) + (cond ((eq? (entity-name (car hiding)) (group-name group)) + (cdr hiding)) + (else (cons (car hiding) (remove-entity group (cdr hiding)))))) + +;;; This moves fixity information to the local symbols. This must be +;;; called after local symbols are installed but before imported +;;; symbols arrive. + +(define (attach-fixities) + (dolist (fixity-decl (module-fixities *module*)) + (let ((fixity (fixity-decl-fixity fixity-decl))) + (dolist (op (fixity-decl-names fixity-decl)) + (let ((def (resolve-toplevel-name op))) + (cond ((or (eq? def '#f) (not (eq? *module-name* (def-module def)))) + ;;; ***This is WRONG! Inner fixities may be found. + (signal-non-local-fixity op)) + ((var? def) + (setf (var-fixity def) fixity) + (setf (table-entry *fixity-table* op) fixity)) + ((con? def) + (setf (con-fixity def) fixity) + (setf (table-entry *fixity-table* op) fixity)) + (else (signal-fixity-not-var/con op)))))))) + diff --git a/import-export/ie.scm b/import-export/ie.scm new file mode 100644 index 0000000..9cd6de9 --- /dev/null +++ b/import-export/ie.scm @@ -0,0 +1,16 @@ +(define-compilation-unit ie + (source-filename "$Y2/import-export/") + (require global) + (unit ie-utils + (source-filename "ie-utils")) + (unit import-export + (source-filename "import-export")) + (unit init-modules + (source-filename "init-modules")) + (unit top-definitions + (source-filename "top-definitions")) + (unit locate-entity + (source-filename "locate-entity")) + (unit ie-errors + (source-filename "ie-errors"))) + diff --git a/import-export/import-export.scm b/import-export/import-export.scm new file mode 100644 index 0000000..25fdfcf --- /dev/null +++ b/import-export/import-export.scm @@ -0,0 +1,209 @@ +;;; This is the main driver for the import / export routine + +(define (import-export modules) + (walk-modules modules + (lambda () (add-module-to-symbol-table *module*))) + (walk-modules modules + (lambda () (init-module-structure))) + (import-export/fixpoint modules '#t) + (walk-modules modules (lambda () (check-missing-names))) + (when (memq 'import (dynamic *printers*)) + (show-export-tables modules)) + modules) + +(define (import-export/interface modules) + (walk-modules modules + (lambda () (add-module-to-symbol-table *module*))) + (walk-modules modules + (lambda () (init-module-structure))) + (walk-modules modules + (lambda () (create-top-definitions) + (attach-fixities)))) + +(define (import-export/fixpoint modules initial-cycle?) + (setf *new-exports-found?* '#f) + (walk-modules modules + (lambda () + (setf (module-fresh-exports *module*) '()) + (when initial-cycle? + (create-top-definitions) + (attach-fixities) + (import-non-local)) + (locally-import) + (locally-export))) + (when *new-exports-found?* + (import-export/fixpoint modules '#f))) + +;;; This does the non-local importing from previously defined modules + +(define (import-non-local) + (setf (module-imports *module*) + (process-non-local-imports (module-imports *module*)))) + +(define (process-non-local-imports imports) + (if (null? imports) + '() + (let* ((import (car imports))) + (with-slots import-decl (module mode specs renamings) import + (cond ((eq? *unit* (module-unit module)) + (cons import (process-non-local-imports (cdr imports)))) + ((eq? mode 'all) + (import-all-entities module specs renamings import) + (process-non-local-imports (cdr imports))) + (else + (import-named-entities module specs renamings import) + (process-non-local-imports (cdr imports)))))))) + +(define (import-all-entities module hiding renamings import-decl) + (table-for-each + (lambda (name group) + (declare (ignore name)) + (cond ((in-hiding-list? group hiding) + (setf hiding (remove-entity group hiding))) + (else + (import-group (rename-group group renamings) module)))) + (module-export-table module)) + (when (not (null? hiding)) + (remember-context import-decl + (dolist (h hiding) + (signal-unused-hiding (entity-name h) (module-name module))))) + (find-unused-renamings renamings import-decl)) + +(define (import-named-entities mod specs renamings import-decl) + (dolist (entity specs) + (let ((group (locate-entity/export-table entity mod '#t))) + (when (not (eq? group 'error)) + (setf group (rename-group group renamings)) + (import-group group mod)))) + (find-unused-renamings renamings import-decl)) + +;;; This takes a module and processes the import declarations, moving as +;;; many entities from the freshly exported components of other modules into +;;; the current module. + +(define (locally-import) + (dolist (import (module-imports *module*)) + (with-slots import-decl (module mode specs renamings) import + (if (eq? mode 'all) + (import-fresh-entities import module specs renamings) + (setf (import-decl-specs import) + (import-entities specs module renamings)))))) + +(define (import-fresh-entities import module hiding renamings) + (dolist (group (module-fresh-exports module)) + (cond ((in-hiding-list? group hiding) + (setf hiding (remove-entity group hiding))) + (else + (import-group (rename-group group renamings) module)))) + (setf (import-decl-specs import) hiding)) + +(define (import-entities entities module renamings) + (if (null? entities) + '() + (let ((group (locate-entity/export-table (car entities) module '#f))) + (cond ((eq? group 'not-found) + (cons (car entities) + (import-entities (cdr entities) module renamings))) + ((eq? group 'error) + (import-entities (cdr entities) module renamings)) + (else + (setf group (rename-group group renamings)) + (import-group group module) + (import-entities (cdr entities) module renamings)))))) + +;;; This imports a group into *module*. module is the place the group is +;;; taken from. + +(define (import-group group module) + (when (memq module (module-exported-modules *module*)) + (export-group group)) + (dolist (n-d group) + (insert-top-definition (tuple-2-1 n-d) (tuple-2-2 n-d)))) + +;;; This takes as yet unresolved exports and moves them to the export table. + +(define (locally-export) + (setf (module-exports *module*) + (export-entities (module-exports *module*)))) + +(define (export-entities entities) + (if (null? entities) + '() + (let* ((entity (car entities)) + (group (locate-entity entity))) + (cond ((eq? group 'error) + (export-entities (cdr entities))) + ((eq? group 'not-found) + (cons entity (export-entities (cdr entities)))) + (else + (export-group group) + (export-entities (cdr entities))))))) + + +;;; This moves a group into the export table. If this export is new, +;;; a flag is set. + +(define (export-group group) + (let* ((export-table (module-export-table *module*)) + (old-group (table-entry export-table (group-name group)))) + (when (or (eq? old-group '#f) + (and (hidden-constructors? old-group) + (not (hidden-constructors? group)))) + (setf (table-entry export-table (group-name group)) group) + (dolist (n-d group) + (setf (def-exported? (tuple-2-2 n-d)) '#t)) + (push group (module-fresh-exports *module*)) + (setf *new-exports-found?* '#t)))) + +(define (show-export-tables modules) + (walk-modules modules + (lambda () + (format '#t "~%Exports from module ~A~%" *module-name*) + (let ((exports '())) + (table-for-each (lambda (key val) + (push (cons key val) exports)) + (module-export-table *module*)) + (setf exports (sort-list exports + (lambda (x y) + (string-cistring (car x)) + (symbol->string (car y)))))) + (dolist (e exports) + (print-exported-group (car e) (group-definition (cdr e)) + (cdr (cdr e)))))))) + +(define (print-exported-group name def extras) + (if (eq? (def-module def) *module-name*) + (format '#t " ") + (format '#t "*")) + (cond ((synonym? def) + (format '#t "type ")) + ((algdata? def) + (format '#t "data ")) + ((class? def) + (format '#t "class ")) + (else + (format '#t " "))) + (format '#t "~A" name) + (when (not (eq? name (def-name def))) + (format '#t "[~A]" (def-name def))) + (when extras + (format '#t " (") + (print-exported-group-1 extras (algdata? def))) + (format '#t "~%")) + +(define (print-exported-group-1 extras alg?) + (let* ((name (tuple-2-1 (car extras))) + (ns (symbol->string name)) + (def (tuple-2-2 (car extras)))) + (format '#t "~A" (if alg? (remove-con-prefix ns) ns)) + (when (not (eq? name (def-name def))) + (let ((name1 (symbol->string (def-name def)))) + (format '#t "[~A]" (if alg? (remove-con-prefix name1) name1)))) + (if (null? (cdr extras)) + (format '#t ")") + (begin + (format '#t ",") + (print-exported-group-1 (cdr extras) alg?))))) + + + diff --git a/import-export/init-modules.scm b/import-export/init-modules.scm new file mode 100644 index 0000000..5198c0c --- /dev/null +++ b/import-export/init-modules.scm @@ -0,0 +1,142 @@ +;;; This initializes the module ast structures. + +;;; This requires that the module table be created and updated with new +;;; modules first. *unit* must also be defined. + +;;; Things initialized there: +;;; all tables in the module structure +;;; the module slot of all import declarations and entity-modules +;;; The import Prelude is added when necessary +;;; Empty export lists are explicated + +(define (init-module-structure) + (when (not (eq? (module-type *module*) 'extension)) + ;; If this is an extension, the incremental compiler has already + ;; filled in the compilation unit. + (setf (module-unit *module*) *unit*)) + ;;; This processes the annotations. Annotations used at the top + ;;; level of the module: + ;;; {-#PRELUDE#-} : this contains definitions in the Haskell prelude + (setf (module-prelude? *module*) '#f) + (setf (module-interface-codefile *module*) '()) + (dolist (a (module-annotations *module*)) + (when (annotation-value? a) + (let ((name (annotation-value-name a))) + (cond ((eq? name '|Prelude|) + (setf (module-prelude? *module*) '#t)))))) + (cond ((eq? (module-type *module*) 'interface) + (setf (module-exported-modules *module*) (list *module*)) + (process-interface-imports *module*)) + ((eq? (module-type *module*) 'standard) + (init-standard-module)))) + +(define (init-standard-module) + (let ((seen-prelude? '#f)) + (dolist (import (module-imports *module*)) + (let* ((name (import-decl-module-name import)) + (imported-mod (locate-module name))) + (when (eq? name '|Prelude|) + (setf seen-prelude? '#t)) + (if (eq? imported-mod '#f) + (signal-undefined-module-import name) + (setf (import-decl-module import) imported-mod)) + (when (eq? name *module-name*) + (signal-self-import name)))) + (when (null? (module-exports *module*)) + (setf (module-exports *module*) + (list (make entity-module (name *module-name*) + (module *module*))))) + (when (not seen-prelude?) + (let ((prelude (locate-module '|Prelude|))) + (cond ((eq? prelude '#f) + (signal-missing-prelude)) + ((module-prelude? *module*) + (setf (module-uses-standard-prelude? *module*) '#f) + (add-imported-module prelude)) + (else + (setf (module-uses-standard-prelude? *module*) '#t) + (let ((fix-table (module-fixity-table *module*))) + (table-for-each (lambda (k v) + (setf (table-entry fix-table k) v)) + *prelude-fixity-table*)))))) + (let ((prelude-core (locate-module '|PreludeCore|))) + (if (eq? prelude-core '#f) + (signal-missing-prelude-core) + (when (module-prelude? *module*) + (add-imported-module prelude-core)))) + (setf (module-exports *module*) + (filter-complete-module-exports (module-exports *module*)))) + ) + + +(define (add-imported-module module) + (setf (module-imports *module*) + (cons (make import-decl + (module-name (module-name module)) + (module module) + (mode 'all) + (specs '()) + (renamings '())) + (module-imports *module*)))) + +(define (filter-complete-module-exports exports) + (if (null? exports) + '() + (let ((export (car exports)) + (others (filter-complete-module-exports (cdr exports)))) + (if (is-type? 'entity-module export) + (let* ((name (entity-name export)) + (exported-mod (locate-module name))) + (when (eq? exported-mod '#f) + (signal-undefined-module-export name)) + (push exported-mod (module-exported-modules *module*)) + (when (not (memq name + (cons *module-name* + (map + (lambda (import) + (import-decl-module-name import)) + (module-imports *module*))))) + (signal-export-not-imported name)) + others) + (cons export others))))) + +(define (process-interface-imports module) + (let ((imports '())) + (dolist (i (module-imports module)) + (let ((module (import-decl-module-name i)) + (renamings (import-decl-renamings i))) + (dolist (s (import-decl-specs i)) + (let* ((n (entity-name s)) + (n1 (do-interface-rename n renamings))) + (when (assq n1 imports) + (signal-multiple-imports n1)) + (push (tuple n1 (tuple module n)) imports) + (cond ((entity-class? s) + (dolist (m (entity-class-methods s)) + (let ((m1 (do-interface-rename m renamings))) + (when (assq m1 imports) + (signal-multiple-imports m1)) + (push (tuple m1 (tuple module m)) imports)))) + ((entity-datatype? s) + (dolist (m (entity-datatype-constructors s)) + (let ((m1 (do-interface-rename m renamings))) + (when (assq m1 imports) + (signal-multiple-imports m1)) + (push (tuple m1 (tuple module m)) imports))))))))) + (setf (module-interface-imports module) imports))) + +(define (signal-multiple-imports name) + (phase-error 'multuple-interface-import + "Interface file has more than one definition of ~A~%" name)) + +(define (do-interface-rename name renamings) + (if (has-con-prefix? (symbol->string name)) + (let* ((n1 (remove-con-prefix/symbol name)) + (res (locate-renaming n1 renamings))) + (if (eq? res '#f) + name + (add-con-prefix/symbol (renaming-to res)))) + (let ((res (locate-renaming name renamings))) + (if (eq? res '#f) + name + (renaming-to res))))) diff --git a/import-export/locate-entity.scm b/import-export/locate-entity.scm new file mode 100644 index 0000000..a001b62 --- /dev/null +++ b/import-export/locate-entity.scm @@ -0,0 +1,126 @@ +;;; This file deals with entities in import / export lists + +;;; This resolves an entity with the export table of a +;;; module. It returns either a group, the symbol 'error, or the symbol +;;; 'not-found. When force-error? is true, signal an error when +;;; the module is not found & return 'error. + +(define (locate-entity/export-table entity mod force-error?) + (let* ((name (entity-name entity)) + (group (table-entry (module-export-table mod) name))) + (if (eq? group '#f) + (if (not force-error?) + 'not-found + (signal-entity-not-found name (module-name mod))) + (let ((def (group-definition group))) + (cond ((is-type? 'entity-var entity) + group) + ((is-type? 'entity-con entity) + (cond ((algdata? def) + (strip-constructors group)) + ((synonym? def) + (signal-synonym-needs-dots name (module-name mod))) + (else + (signal-wrong-definition + "type constructor" name (module-name mod))))) + ((is-type? 'entity-abbreviated entity) + (cond ((algdata? def) + (cond ((hidden-constructors? group) + (if force-error? + (signal-abstract-type + name (module-name mod)) + 'not-found)) + (else + group))) + ((or (class? def) (synonym? def)) + group) + (else + (signal-wrong-definition + "class or datatype" name (module-name mod))))) + ((is-type? 'entity-class entity) + (if (class? def) + (match-constituents group (entity-class-methods entity) + entity "method") + (signal-wrong-definition "class" name (module-name mod)))) + ((is-type? 'entity-datatype entity) + (if (algdata? def) + (match-constituents group + (entity-datatype-constructors entity) + entity "constructor") + (signal-wrong-definition + "data type" name (module-name mod)))) + (else + (error "Bad entity ~s." entity)) + ))))) + +(define (match-constituents group names entity what) + (check-duplicates names entity) + (dolist (n-d (cdr group)) + (when (not (memq (tuple-2-1 n-d) names)) + (signal-extra-constituent entity (tuple-2-1 n-d) what))) + (dolist (name names) + (when (not (assq name (cdr group))) + (signal-missing-constituent entity name what))) + group) + + +;;; The following routine locates an entity in the current module. +;;; It may return 'error, 'not-found, or a group. + +(define (locate-entity entity) + (let* ((name (entity-name entity)) + (def (resolve-toplevel-name name))) + (cond ((eq? def '#f) + 'not-found) + ((is-type? 'entity-var entity) + (if (method-var? def) + (signal-export-method-var name) + (make-group name def))) + ((is-type? 'entity-con entity) + (cond ((algdata? def) + (make-group name def)) + ((synonym? def) + (signal-synonym-needs-dots name *module-name*)) + (else + (signal-wrong-definition + "type constructor" name *module-name*)))) + ((is-type? 'entity-abbreviated entity) + (cond ((algdata? def) + (require-complete-algdata + (gather-algdata-group name def))) + ((synonym? def) + (make-group name def)) + ((class? def) + (gather-class-group name def)) + (else + (signal-wrong-definition + "type constructor or class" name *module-name*)))) + ((is-type? 'entity-class entity) + (if (class? def) + (match-group-names + (gather-class-group name def) + (entity-class-methods entity) + entity + "method") + (signal-wrong-definition "class" name *module-name*))) + ((is-type? 'entity-datatype entity) + (if (algdata? def) + (match-group-names + (require-complete-algdata (gather-algdata-group name def)) + (entity-datatype-constructors entity) + entity "constructor") + (signal-wrong-definition "data type" name *module-name*))) + (else + (error "Bad entity ~s." entity))))) + +(define (require-complete-algdata group) + (if (hidden-constructors? group) + 'not-found + group)) + +(define (match-group-names group names entity what) + (when (not (eq? group 'not-found)) + (match-constituents group names entity what)) + group) + + diff --git a/import-export/top-definitions.scm b/import-export/top-definitions.scm new file mode 100644 index 0000000..07722dd --- /dev/null +++ b/import-export/top-definitions.scm @@ -0,0 +1,98 @@ +;;; File: top-definitions.scm + +;;; Description: This creates definitions for all top level (exportable) +;;; object in a module. + +(define (create-top-definitions) + (dolist (decl (module-decls *module*)) + (if (eq? (module-type *module*) 'interface) + (when (signdecl? decl) + (create-var-definitions decl (signdecl-vars decl))) + (when (valdef? decl) + (create-var-definitions + decl (collect-pattern-vars (valdef-lhs decl)))))) + (dolist (algdata (module-algdatas *module*)) + (create-alg-definitions algdata)) + (dolist (synonym (module-synonyms *module*)) + (create-syn-definitions synonym)) + (dolist (class (module-classes *module*)) + (create-class-definitions class))) + +;;; ------------------------------------------------------------------------ +;;; creation of definitions +;;; ------------------------------------------------------------------------ + +(define (create-var-definitions decl vars) + (remember-context decl + (dolist (v vars) + (let* ((var-name (var-ref-name v)) + (def (create-top-definition var-name 'var))) + (setf (var-ref-var v) def) + (push def (module-vars *module*)) + (add-new-group var-name def))))) + +;;; This also creates definitions for the constructors + +(define (create-alg-definitions algdata) + (remember-context algdata + (with-slots data-decl (simple constrs) algdata + (let* ((alg-name (tycon-name simple)) + (def (create-top-definition alg-name 'algdata))) + (setf (tycon-def simple) def) + (let ((constr-group + (map (lambda (constr) + (let* ((con-ref (constr-constructor constr)) + (con-name (con-ref-name con-ref)) + (con-def (create-top-definition con-name 'con))) + (setf (con-ref-con con-ref) con-def) + (tuple con-name con-def))) + constrs))) + (setf (algdata-constrs def) (map (function tuple-2-2) constr-group)) + (setf (tycon-def-arity def) (length (tycon-args simple))) + (add-new-group alg-name def constr-group)))))) + +(define (create-class-definitions class-decl) + (remember-context class-decl + (with-slots class-decl (class decls) class-decl + (let* ((class-name (class-ref-name class)) + (class-def (create-top-definition class-name 'class))) + (setf (class-ref-class class) class-def) + (let ((method-group + (concat + (map + (lambda (decl) + (if (is-type? 'signdecl decl) + (remember-context decl + (map (lambda (method-var) + (let* ((var-name (var-ref-name method-var)) + (def (create-top-definition + var-name 'method-var))) + (setf (method-var-class def) class-def) + (setf (method-var-default def) '#f) + (setf (var-ref-var method-var) def) + (tuple var-name def))) + (signdecl-vars decl))) + '())) + decls)))) + (setf (class-method-vars class-def) + (map (function tuple-2-2) method-group)) + (add-new-group class-name class-def method-group)))))) + +(define (create-syn-definitions synonym-decl) + (remember-context synonym-decl + (let* ((simple (synonym-decl-simple synonym-decl)) + (syn-name (tycon-name simple)) + (def (create-top-definition syn-name 'synonym))) + (setf (tycon-def simple) def) + (setf (tycon-def-arity def) (length (tycon-args simple))) + (add-new-group syn-name def)))) + +(define (add-new-group name def . others) + (when (memq *module* (module-exported-modules *module*)) + (export-group (cons (tuple name def) + (if (null? others) + '() + (car others)))))) + + + diff --git a/parser/README b/parser/README new file mode 100644 index 0000000..a2facd7 --- /dev/null +++ b/parser/README @@ -0,0 +1 @@ +This directory contains the lexer and parser. diff --git a/parser/annotation-parser.scm b/parser/annotation-parser.scm new file mode 100644 index 0000000..4ae33cf --- /dev/null +++ b/parser/annotation-parser.scm @@ -0,0 +1,184 @@ + +(define *annotation-escape* '()) + +(define (parse-annotations) + (let ((save-layout (dynamic *layout-stack*))) + (setf (dynamic *layout-stack*) '()) + (advance-token) + (let/cc annotation-escape + (setf *annotation-escape* (lambda () + (setf (dynamic *layout-stack*) save-layout) + (advance-to-annotation-end) + (funcall annotation-escape '()))) + (let ((res (start-layout (function parse-annotation-list-1)))) + (setf (dynamic *layout-stack*) save-layout) + (token-case + (end-annotation res) + (else (signal-annotation-error))))))) + +(define (parse-annotation-list-1 in-layout?) + (let ((kind (get-annotation-kind))) + (cond ((eq? kind 'decl) + (let ((d (parse-annotation-decl))) + (token-case + (\; (cons d (parse-annotation-list-1 in-layout?))) + (else (close-layout in-layout?) + (list d))))) + ((eq? kind 'value) + (let ((d (parse-annotation-value))) + (token-case + (\; (cons d (parse-annotation-list-1 in-layout?))) + (else (close-layout in-layout?) + (list d))))) + (else + (close-layout in-layout?) + '())))) + +(define (get-annotation-kind) + (token-case + ((no-advance end-annotation) 'end) + ((no-advance \() 'decl) + ((var con) + (let ((next (peek-1-type))) + (cond ((eq? next '|,|) + 'decl) + ((eq? next '|::|) + 'decl) + (else + 'value)))) + (else 'error))) + +(define (parse-annotation-decl) + (let* ((names (parse-aname-list)) + (decls (parse-aval-list))) + (make annotation-decl (names names) (annotations decls)))) + +(define (parse-aname-list) + (let ((name 'foo)) + (token-case + (var + (setf name (var->symbol))) + (con + (setf name (con->symbol))) + (else (signal-annotation-error))) + (token-case (\, (cons name (parse-aname-list))) + (|::| (list name)) + (else (signal-annotation-error))))) + + +(define (parse-aval-list) + (let ((ann (parse-annotation-value))) + (token-case (\, (cons ann (parse-aval-list))) + (else (list ann))))) + +(define (parse-annotation-value) + (token-case + (name (let* ((name (token->symbol)) + (args (parse-annotation-args name))) + (make annotation-value (name name) (args args)))))) + +(define (parse-annotation-args name) + (token-case + (\( (parse-annotation-args-1 name 0)) + (else '()))) + +;;; This routine can invoke special parsers for the arguments + +(define (parse-annotation-args-1 name i) + (let* ((argtype (get-annotation-arg-description name i)) + (arg (parse-annotation-arg argtype))) + (token-case + (\) (list arg)) + (\, (cons arg (parse-annotation-args-1 name (1+ i)))) + (else (signal-annotation-error))))) + +(define (parse-annotation-arg type) + (cond ((eq? type 'string) + (token-case + ((string no-advance) + (let ((res (car *token-args*))) + (advance-token) + res)) + (else (signal-annotation-error)))) + ;; The following is for a datatype import/export. It is + ;; Type(Con1(strs),Con2(strs),...) + ((eq? type 'integer) + (token-case + ((integer no-advance) (token->integer)) + (else (signal-annotation-error)))) + ((eq? type 'constr-list) + (parse-annotation-constr-list)) + (else + (signal-annotation-error)))) + +(define (signal-annotation-error) + (parser-error/recoverable 'annotation-error "Error in annotation syntax") + (funcall *annotation-escape*)) + +(define (parse-annotation-constr-list) + (token-case + (tycon (let ((type-name (token->symbol))) + (token-case (\( (let* ((args (parse-acl1)) + (res (tuple type-name args))) + (token-case ; leave the ) to end the args + ((no-advance \)) (list res)) + (\, (cons res (parse-annotation-constr-list))) + (else (signal-annotation-error))))) + (else (signal-annotation-error))))) + (else (signal-annotation-error)))) + +(define (parse-acl1) + (token-case + (con (let ((con-name (con->symbol))) + (token-case (\( (let ((str-args (parse-string-list))) + (token-case + (\, (cons (tuple con-name str-args) + (parse-acl1))) + (\) (list (tuple con-name str-args))) + (else (signal-annotation-error))))) + (else (signal-annotation-error))))) + (else (signal-annotation-error)))) + +(define (parse-string-list) + (token-case + ((string no-advance) + (let ((res (read-lisp-object (car *token-args*)))) + (advance-token) + (token-case + (\) (list res)) + (\, (cons res (parse-string-list))) + (else (signal-annotation-error))))) + (else (signal-annotation-error)))) + +(define (advance-to-annotation-end) + (token-case + (eof '()) + (end-annotation + (advance-token)) + (else + (advance-token) + (advance-to-annotation-end)))) + +(define *known-annotations* '( + (|LispName| string) + (|Prelude|) + (|Strictness| string) + (|Strict|) + (|NoConversion|) + (|Inline|) + (|STRICT|) + (|ImportLispType| constr-list) + (|ExportLispType| constr-list) + (|Complexity| integer) + )) + +(define (get-annotation-arg-description annotation i) + (let ((s (assq annotation *known-annotations*))) + (cond ((eq? s '#f) + (parser-error/recoverable 'unknown-annotation + "Annotation ~A is not defined in this system - ignored." + annotation) + 'unknown) + ((>= i (length s)) + 'error) + (else (list-ref s (1+ i)))))) diff --git a/parser/decl-parser.scm b/parser/decl-parser.scm new file mode 100644 index 0000000..bf924b5 --- /dev/null +++ b/parser/decl-parser.scm @@ -0,0 +1,175 @@ +;;; File: decl-parser Author: John + +(define (parse-decl) + (let ((decl-type (find-decl-type))) + (cond ((eq? decl-type 'signdecl) + (parse-signdecl)) + ((eq? decl-type 'pat-or-op) + (parse-pat-or-op)) + ((eq? decl-type 'fundef) + (parse-fundef)) + ((eq? decl-type 'plus-def) + (parse-plus-def)) + ((eq? decl-type 'annotation) + (make annotation-decls (annotations (parse-annotations))))))) + +;;; This looks at the first tokens in a definition to determine it's type. +;;; var (:: | ,) - signdecl +;;; var apat-start - function definition +;;; (var | _) + - definition of infix + +;;; anything alse - pattern binding or infix definition + +(define (find-decl-type) + (let* ((saved-excursion (save-scanner-state)) + (decl-type + (token-case + (var (scan-var) + (token-case + ((\, \:\:) 'signdecl) + (apat-start 'fundef) + (+ 'plus-def) + (else 'pat-or-op))) + (_ (token-case + (+ 'plus-def) + (else 'pat-or-op))) + (begin-annotation 'annotation) + (else 'pat-or-op)))) + (restore-excursion saved-excursion) + decl-type)) + +;;; These are the different flavors of decl parsers + +(define (parse-signdecl) + (save-parser-context + (trace-parser signdecl + (let ((vars (parse-signdecl-vars))) + (require-token \:\: + (signal-missing-token "`::'" "signature declaration")) + (let ((signature (parse-signature))) + (make signdecl (vars vars) (signature signature))))))) + +(define (parse-signdecl-vars) + (token-case + (var (let ((var (var->ast))) + (token-case (\, (cons var (parse-signdecl-vars))) + (else (list var))))) + (else (signal-missing-token "" "signature declaration")))) + +(define (parse-pat-or-op) + (trace-parser patdef + (let* ((line-number (capture-current-line)) + (pat (parse-pat))) + (token-case + (varop (parse-infix-def pat line-number)) + (else (add-rhs pat '() '#f line-number)))))) + +(define (parse-infix-def pat1 line-number) + (let* ((op (make var-pat (var (varop->ast)))) + (pat2 (parse-pat))) + (add-rhs op (list pat1 pat2) '#t line-number))) + +(define (parse-fundef) + (trace-parser fundef + (let* ((start-line (capture-current-line)) + (fn (parse-apat)) ; must be a single variable + (args (parse-apat-list))) + (add-rhs fn args '#f start-line)))) + +(define (parse-plus-def) + (trace-parser plus-def + (let* ((start-line (capture-current-line)) + (var (parse-apat))) + (parse-infix-def var start-line)))) + +(define (add-rhs pat args infix? start-line) + (let* ((rhs (parse-rhs)) + (decls (parse-where-decls)) + (single (make single-fun-def + (args args) + (rhs-list rhs) + (where-decls decls) + (infix? infix?))) + (valdef (make valdef (lhs pat) (definitions (list single))))) + (setf (ast-node-line-number single) start-line) + (setf (ast-node-line-number valdef) start-line) + valdef)) + +(define (parse-rhs) + (token-case + (= (let ((rhs (parse-exp))) + (list (make guarded-rhs (guard (make omitted-guard)) (rhs rhs))))) + (\| (parse-guarded-rhs)) + (else + (signal-missing-token "`=' or `|'" "rhs of valdef")))) + +(define (parse-guarded-rhs) ; assume just past | + (trace-parser guard + (let ((guard (parse-exp-i))) ; 1.2 change + (require-token = (signal-missing-token "`='" "guarded rhs")) + (let* ((exp (parse-exp)) + (res (make guarded-rhs (guard guard) (rhs exp)))) + (token-case + (\| (cons res (parse-guarded-rhs))) + (else (list res))))))) + +(define (parse-where-decls) + (token-case + (|where| + (parse-decl-list)) + (else '()))) + +(define (parse-decl-list) + (start-layout (function parse-decl-list-1))) + +(define (parse-decl-list-1 in-layout?) + (token-case + ((apat-start begin-annotation) + (let ((decl (parse-decl))) + (token-case + (\; (decl-cons decl (parse-decl-list-1 in-layout?))) + (else (close-layout in-layout?) + (list decl))))) + (else + (close-layout in-layout?) + '()))) + +;;; This adds a new decl to a decl list. Successive decls for the same fn +;;; are combined. + +(define (decl-cons decl decl-list) + (cond ((null? decl-list) + (list decl)) + (else (nconc (combine-decls decl (car decl-list)) (cdr decl-list))))) + +(define (decl-push decl decl-stack) + (cond ((null? decl-stack) + (list decl)) + (else (nconc (nreverse (combine-decls (car decl-stack) decl)) + (cdr decl-stack))))) + +(define (combine-decls decl1 decl2) + (if (and (is-type? 'valdef decl1) + (is-type? 'valdef decl2) + (same-decl-var? (valdef-lhs decl1) (valdef-lhs decl2))) + (if (eqv? (length (single-fun-def-args (car (valdef-definitions decl1)))) + (length (single-fun-def-args (car (valdef-definitions decl2))))) + (begin + (setf (valdef-definitions decl1) + (nconc (valdef-definitions decl1) + (valdef-definitions decl2))) + (list decl1)) + (signal-multiple-definitions-arity-mismatch (valdef-lhs decl1))) + (list decl1 decl2))) + +(define (same-decl-var? pat1 pat2) + (and (is-type? 'var-pat pat1) + (is-type? 'var-pat pat2) + (eq? (var-ref-name (var-pat-var pat1)) + (var-ref-name (var-pat-var pat2))))) + +(define (signal-multiple-definitions-arity-mismatch pat) + (parser-error 'multiple-definitions-arity-mismatch + "Definition of ~a does not match arity of previous definition." + pat)) + + diff --git a/parser/exp-parser.scm b/parser/exp-parser.scm new file mode 100644 index 0000000..6f941ae --- /dev/null +++ b/parser/exp-parser.scm @@ -0,0 +1,230 @@ +;;; File: expr-parser Author: John + +(define (parse-exp) + (trace-parser exp + (parse-exp-0))) + +(define (parse-exp-0) ;; This picks up expr type signatures + (let ((exp (parse-exp-i))) + (token-case + (\:\: (let ((signature (parse-signature))) + (make exp-sign (exp exp) (signature signature)))) + (else exp)))) + +(define (parse-exp-i) ;; This collects a list of exps for later prec parsing + (let ((exps (parse-infix-exps))) + (if (null? (cdr exps)) + (car exps) + (make pp-exp-list (exps exps))))) + +(define (parse-infix-exps) + (token-case + (- (cons (make negate) (parse-infix-exps))) + (\\ (list (parse-lambda))) + (|let| (list (parse-let))) + (|if| (list (parse-if))) + (|case| (parse-possible-app (parse-case))) + (else (let ((aexp (parse-aexp))) + (parse-possible-app aexp))))) + +(define (parse-possible-app exp) + (token-case + (aexp-start + (let ((exp2 (parse-aexp))) + (parse-possible-app (make app (fn exp) (arg exp2))))) + (varop + (let ((varop (varop->ast))) + (if (eq-token? '\)) + (list exp varop) + `(,exp ,varop ,@(parse-infix-exps))))) + (conop + (let ((conop (conop->ast))) + (if (eq-token? '\)) + (list exp conop) + `(,exp ,conop ,@(parse-infix-exps))))) + (else (list exp)))) + +(define (parse-lambda) + (trace-parser lambda + (save-parser-context + (let ((pats (parse-apat-list))) + (require-token -> (signal-missing-token "`->'" "lambda expression")) + (let ((exp (parse-exp))) + (make lambda (pats pats) (body exp))))))) + +(define (parse-let) + (trace-parser let + (save-parser-context + (let ((decls (parse-decl-list))) + (require-token |in| (signal-missing-token "`in'" "let expression")) + (let ((exp (parse-exp))) + (make let (decls decls) (body exp))))))) + +(define (parse-if) + (trace-parser if + (save-parser-context + (let ((test-exp (parse-exp))) + (require-token |then| (signal-missing-token "`then'" "if expression")) + (let ((then-exp (parse-exp))) + (require-token |else| (signal-missing-token "`else'" "if expression")) + (let ((else-exp (parse-exp))) + (make if (test-exp test-exp) + (then-exp then-exp) + (else-exp else-exp)))))))) + +(define (parse-case) + (trace-parser case + (save-parser-context + (let ((exp (parse-exp))) + (require-token |of| (signal-missing-token "`of'" "case expression")) + (let ((alts (start-layout (function parse-alts)))) + (make case (exp exp) (alts alts))))))) + +(define (parse-alts in-layout?) + (token-case + (pat-start + (let ((alt (parse-alt))) + (token-case + (\; (cons alt (parse-alts in-layout?))) + (else (close-layout in-layout?) + (list alt))))) + (else + (close-layout in-layout?) + '()))) + +(define (parse-alt) + (trace-parser alt + (let* ((pat (parse-pat)) + (rhs-list (token-case + (-> (let ((exp (parse-exp))) + (list (make guarded-rhs (guard (make omitted-guard)) + (rhs exp))))) + (\| (parse-guarded-alt-rhs)) + (else (signal-missing-token "`->' or `|'" "rhs of alt")))) + (decls (parse-where-decls))) + (make alt (pat pat) (rhs-list rhs-list) (where-decls decls))))) + +(define (parse-guarded-alt-rhs) + (let ((guard (parse-exp))) + (require-token -> (signal-missing-token "`->'" "alt")) + (let* ((exp (parse-exp)) + (res (make guarded-rhs (guard guard) (rhs exp)))) + (token-case + (\| (cons res (parse-guarded-alt-rhs))) + (else (list res)))))) + +(define (parse-aexp) + (trace-parser aexp + (token-case + (var (save-parser-context (var->ast))) + (con (save-parser-context (con->ast))) + (literal (literal->ast)) + (\( + (token-case + (\) (**con/def (core-symbol "UnitConstructor"))) + ((no-advance -) (parse-exp-or-tuple)) + (varop + (let ((varop (varop->ast))) + (make-right-section varop))) + (conop + (let ((conop (conop->ast))) + (make-right-section conop))) + (else + (parse-exp-or-tuple)))) + (\[ + (token-case + (\] (make list-exp (exps '()))) + (else + (let ((exp (parse-exp))) + (token-case + (\, (let ((exp2 (parse-exp))) + (token-case + (\] (make list-exp (exps (list exp exp2)))) + (\.\. (token-case + (\] (make sequence-then (from exp) (then exp2))) + (else + (let ((exp3 (parse-exp))) + (require-token + \] + (signal-missing-token + "`]'" "sequence expression")) + (make sequence-then-to (from exp) (then exp2) + (to exp3)))))) + (else + (make list-exp + (exps `(,exp ,exp2 ,@(parse-exp-list)))))))) + (\.\. (token-case + (\] (make sequence (from exp))) + (else + (let ((exp2 (parse-exp))) + (require-token + \] + (signal-missing-token "`]'" "sequence expression")) + (make sequence-to (from exp) (to exp2)))))) + (\] (make list-exp (exps (list exp)))) + (\| (parse-list-comp exp)) + (else + (signal-invalid-syntax + "a list, sequence, or list comprehension"))))))) + (else + (signal-invalid-syntax "an aexp"))))) + +(define (make-right-section op) + (let ((exps (parse-infix-exps))) + (token-case + (\) (make pp-exp-list (exps (cons op exps)))) + (else (signal-missing-token "`)'" "right section expression"))))) + +(define (parse-exp-list) + (token-case + (\] '()) + (\, (let ((exp (parse-exp))) (cons exp (parse-exp-list)))) + (else (signal-missing-token "`]' or `,'" "list expression")))) + +(define (parse-exp-or-tuple) + (let ((exp (parse-exp))) + (token-case + (\) exp) ; Note - sections ending in an op are parsed elsewhere + (else (make-tuple-cons (cons exp (parse-tuple-exp))))))) + +(define (parse-tuple-exp) + (token-case + (\) '()) + (\, (let ((exp (parse-exp))) (cons exp (parse-tuple-exp)))) + (else (signal-missing-token + "`)' or `,'" "tuple or parenthesized expression")))) + +;;; List comprehensions + +;;; Assume | has been consumed + +(define (parse-list-comp exp) + (save-parser-context + (let ((quals (parse-qual-list))) + (make list-comp (exp exp) (quals quals))))) + +(define (parse-qual-list) + (let ((qual (parse-qual))) + (token-case + (\, (cons qual (parse-qual-list))) + (\] (list qual)) + (else (signal-missing-token "`]' or `,'" "list comprehension"))))) + +(define (parse-qual) + (trace-parser qual + (save-parser-context + (let* ((saved-excursion (save-scanner-state)) + (is-gen? (and (scan-pat) (eq-token? '<-)))) + (restore-excursion saved-excursion) + (cond (is-gen? + (let ((pat (parse-pat))) + (advance-token) ; past the <- + (let ((exp (parse-exp))) + (make qual-generator (pat pat) (exp exp))))) + (else + (let ((exp (parse-exp))) + (make qual-filter (exp exp))))))))) + +(define (make-tuple-cons args) + (let ((tuple-con (**con/def (tuple-constructor (length args))))) + (**app/l tuple-con args))) diff --git a/parser/interface-parser.scm b/parser/interface-parser.scm new file mode 100644 index 0000000..184fdb0 --- /dev/null +++ b/parser/interface-parser.scm @@ -0,0 +1,98 @@ +;;; This is the parser for interface files. + +(define (parse-tokens/interface tokens) + (init-token-stream tokens) + (let ((interface (token-case + (|interface| (parse-interface)) + (|module| (interface-required-error)) + (else (crud-in-interface-error))))) + (cons interface (parse-interface-list)))) + +(define (interface-required-error) + (parser-error 'interface-required "Expecting `interface' keyword")) + +(define (crud-in-interface-error) + (parser-error 'unexpected-interface-crud "Junk after interface")) + +(define (parse-interface-list) + (token-case + (|interface| + (let ((interface (parse-interface))) + (cons interface (parse-interface-list)))) + (|module| (interface-required-error)) + (eof '()) + (else (crud-in-interface-error)))) + +(define (parse-interface) + (token-case + (modid + (let ((module-name (token->symbol))) + (require-token |where| + (signal-missing-token "`where'" "interface definition")) + (let ((mod-ast (make module (name module-name) + (type 'interface) + (exports '())))) + (start-layout (lambda (in-layout?) + (parse-interface-decls mod-ast in-layout? 'import)))))))) + +(define (parse-interface-decls mod-ast in-layout? state) + (token-case + (|import| (let ((import (parse-import))) + (when (not (eq? (import-decl-mode import) 'by-name)) + (phase-error 'illegal-import + "Imports in interfaces must specify specific entities")) + (if (eq? state 'import) + (push-decl-list import (module-imports mod-ast)) + (signal-misplaced-import))) + (terminate-interface-topdecl mod-ast in-layout? state)) + (|infix| (terminate-interface-topdecl mod-ast in-layout? + (parse-fixity 'n mod-ast state))) + (|infixl| (terminate-interface-topdecl mod-ast in-layout? + (parse-fixity 'l mod-ast state))) + (|infixr| (terminate-interface-topdecl mod-ast in-layout? + (parse-fixity 'r mod-ast state))) + (|data| (let ((data-decl (parse-type-decl '#t))) + (push-decl-list data-decl (module-algdatas mod-ast))) + (terminate-interface-topdecl mod-ast in-layout? 'topdecl)) + (|type| (let ((synonym-decl (parse-synonym-decl))) + (push-decl-list synonym-decl (module-synonyms mod-ast))) + (terminate-interface-topdecl mod-ast in-layout? 'topdecl)) + (|class| (let ((class-decl (parse-class-decl))) + (check-class-default-decls class-decl) + (push-decl-list class-decl (module-classes mod-ast))) + (terminate-interface-topdecl mod-ast in-layout? 'topdecl)) + (|instance| (let ((instance-decl (parse-instance-decl '#t))) + (push-decl-list instance-decl (module-instances mod-ast))) + (terminate-interface-topdecl mod-ast in-layout? 'topdecl)) + (var (let ((decl (parse-signdecl))) + (setf (module-decls mod-ast) + (decl-push decl (module-decls mod-ast)))) + (terminate-interface-topdecl mod-ast in-layout? 'topdecl)) + ((begin-annotation no-advance) + (let ((annotations (parse-annotations))) + (setf (module-annotations mod-ast) + (append (module-annotations mod-ast) annotations))) + (terminate-interface-topdecl mod-ast in-layout? state)) + (else + (maybe-end-interface mod-ast in-layout?)))) + +(define (maybe-end-interface mod-ast in-layout?) + (cond ((or (eq-token? '|interface|) (eq-token? 'eof) (eq-token? '\}) + (eq-token? '$\})) + (close-layout in-layout?) + (wrapup-module mod-ast) + mod-ast) + (else + (signal-invalid-syntax "a topdecl")))) + +(define (terminate-interface-topdecl mod-ast in-layout? state) + (token-case + (\; (parse-interface-decls mod-ast in-layout? state)) + (else (maybe-end-interface mod-ast in-layout?)))) + +(define (check-class-default-decls class-decl) + (dolist (d (class-decl-decls class-decl)) + (when (valdef? d) + (remember-context d + (recoverable-error 'no-defaults-in-interface + "Class defaults should not be put in interface files"))))) diff --git a/parser/lexer.scm b/parser/lexer.scm new file mode 100644 index 0000000..7230613 --- /dev/null +++ b/parser/lexer.scm @@ -0,0 +1,651 @@ +;;; File: parser/lexer Author: John + +;;; token data structure: a list with the token type in the +;;; car and other information in the rest of the list. Symbols +;;; designate the token type. + +;;; Reserved tokens use the name as the type and have no args. +;;; Reserved tokens: +;;; case class data default deriving else hiding if import in infix +;;; infixl infixr instance interface let module of renaming then to +;;; type where .. :: => = @ \ | ~ <- -> ` +;;; Other tokens: +;;; (file string) +;;; (newline line indent-column) +;;; (conid string) +;;; (varid string) +;;; (consym string) +;;; (varsym string) +;;; (comment string) ;;; not used at the moment +;;; (integer integer) +;;; (float integer fraction exponent) +;;; (string string) +;;; (eof) + + +;;; *** All of the stuff for lexing character and string literals is +;;; *** broken because it assumes that the host Lisp uses the ASCII +;;; *** encoding for characters and supports at least 255 characters. +;;; *** I have marked the specific places in the code where these +;;; *** assumptions are made, but fixing the problem will probably +;;; *** require more drastic changes anyway -- such as using integers +;;; *** instead of characters and vectors of integers instead of characters +;;; *** throughout the compiler. + +(define *max-char* 255) ; highest char-code allowed. + +;;; This defines the long names of the control chars. Note that some of +;;; this duplicates the table above & the reader. + +(define *control-char-names* '( + ("NUL" . 0) ("SOH" . 1) ("STX" . 2) ("ETX" . 3) + ("EOT" . 4) ("ENQ" . 5) ("ACK" . 6) ("BEL" . 7) + ("BS" . 8) ("HT" . 9) ("LF" . 10) ("VT" . 11) + ("FF" . 12) ("CR" . 13) ("SO" . 14) ("SI" . 15) + ("DLE" . 16) ("DC1" . 17) ("DC2" . 18) ("DC3" . 19) + ("DC4" . 20) ("NAK" . 21) ("SYN" . 22) ("ETB" . 23) + ("CAN" . 24) ("EM" . 25) ("SUB" . 26) ("ESC" . 27) + ("FS" . 28) ("GS" . 29) ("RS" . 30) ("US" . 31) + ("SP" . 32) ("DEL" . 127))) + +;;; This defines the short names for a few control chars. This +;;; is keyed off the previous table + +(define *short-control-char-names* '( + (#\a . "BEL") (#\b . "BS") (#\f . "FF") (#\n . "LF") + (#\r . "CR") (#\t . "HT") (#\v . "VT"))) + +;;; This is used in the ^X construct. Assume that ^X = code for ^A + X-A +;;; *** This is an invalid assumption. + +(define *control-A* 1) + +;;; This function is the interface between the lexer and the rest +;;; of the system. Note that the `file' reported in error messages +;;; must be bound in an outer context. + + +;;; *** I think this function should be binding these variables and not +;;; *** just assigning them. + +(define (lex-port port literate?) + (setf *lex-literate?* literate?) + (setf *current-line* 1) + (setf *current-col* 0) + (setf *on-new-line?* '#t) + (setf *save-col?* '#f) + (setf *port* port) + (setf *tokens* '()) + (setf *char* (read-char *port*)) + (setf *peek-char* (read-char *port*)) + (when (eof-object? *char*) + (setf *char* '#\space)) + (when (eof-object? *peek-char*) + (setf *peek-char* '#\space)) + (setf *at-eof/p?* '#f) + (setf *at-eof?* '#f) + (when *lex-literate?* + (process-literate-comments '#t)) + (parse-till-eof) + (nreverse *tokens*)) + +(define (parse-till-eof) + (cond (*at-eof?* + (emit-token 'eof) + '()) + (else + (lex-one-token) + (parse-till-eof)))) + +;;; There is an assumption that the scanner never peeks beyond a newline. +;;; In literate mode, this may reveal the wrong thing. + +(define (advance-char) + (if (and *lex-literate?* (eqv? *char* #\newline)) + (process-literate-comments '#f) + (advance-char-1))) + +(define (advance-char-1) + (cond ((eqv? *char* #\newline) + (setf *on-new-line?* '#t) + (incf (the fixnum *current-line*)) + (setf *current-col* 0)) + ((eqv? *char* #\tab) + (incf (the fixnum *current-col*) (- 8 (modulo *current-col* 8)))) + (else + (incf (the fixnum *current-col*)))) + (setf *char* *peek-char*) + (setf *at-eof?* *at-eof/p?*) + (setf *peek-char* (read-char *port*)) + (when (eof-object? *peek-char*) + (setf *at-eof/p?* '#t) + (setf *peek-char* '#\space)) + *char*) + +(define (peek-char-2) + (let ((ch (peek-char *port*))) + (if (eof-object? ch) + '#\space + ch))) + +(define (lex-one-token) + (setf *start-line* *current-line*) ; capture the loc at the start of the token + (setf *start-col* *current-col*) + (unless *at-eof?* + (char-case *char* + (whitechar + (advance-char) + (lex-one-token)) + (#\- (char-case *peek-char* + (#\- (lex-comment)) + (#\> (advance-char) + (advance-char) + (emit-token '\-\>)) + (#\} (signal-missing-begin-comment) + (advance-char) + (advance-char) + (lex-one-token)) + (else + (lex-varsym)))) + (#\{ (cond ((char=? *peek-char* '#\-) + (advance-char) + (advance-char) + (cond ((char=? *char* '#\#) + (advance-char) + (emit-token 'begin-annotation)) + (else + (lex-ncomment) + (lex-one-token)))) + (else + (advance-char) + (emit-token '\{ )))) + (small (lex-varid)) + (large (lex-conid)) + (#\( (advance-char) + (emit-token '\()) + (#\: (lex-consym)) + (#\` (advance-char) + (emit-token '\`)) + ((symbol presymbol) (lex-varsym)) + (digit (lex-numeric)) + (#\' (lex-char)) + (#\" (lex-string)) + (#\) (advance-char) + (emit-token '\))) + (#\, (advance-char) + (emit-token '\,)) + (#\; (advance-char) + (emit-token '\;)) + (#\[ (advance-char) + (emit-token '\[)) + (#\] (advance-char) + (emit-token '\])) + (#\_ (advance-char) + (emit-token '\_)) + (#\} (advance-char) + (emit-token '\})) + (else + (signal-invalid-character *char*) + (advance-char) + (lex-one-token))))) + +(define (signal-missing-begin-comment) + (lexer-error 'missing-begin-comment + "`-}' appears outside of a nested comment.")) + +(define (signal-invalid-character ch) + (lexer-error 'invalid-character + "Invalid character `~a' appears in source program." ch)) + +(define (advance-past-white) + (unless *at-eof?* + (char-case *char* + (whitechar + (advance-char) + (advance-past-white)) + (else + '())))) + +(define (process-literate-comments at-start?) + (unless at-start? (advance-char-1)) + (let ((l (classify-line))) + (cond ((or *at-eof?* (eq? l 'program)) + '()) + ((eq? l 'blank) + (skip-literate-comment '#t)) + (else + (when (not at-start?) + (lexer-error 'blank-line-needed + "Literate comments must be preceeded by a blank line")) + (skip-literate-comment '#f))))) + +(define (skip-literate-comment prev-blank) + (skip-past-line) + (let ((l (classify-line))) + (cond (*at-eof?* + '()) + ((eq? l 'comment) + (skip-literate-comment '#f)) + ((eq? l 'blank) + (skip-literate-comment '#t)) + (else + (when (not prev-blank) + (lexer-error 'blank-line-needed + "Literate comments must be followed by a blank line")))))) + +(define (classify-line) + (if *at-eof?* + 'blank + (char-case *char* + (#\> + (advance-char-1) + 'program) + (#\newline 'blank) + (whitechar + (classify-line-1)) + (else 'comment)))) + +(define (classify-line-1) + (advance-char-1) + (char-case *char* + (#\newline 'blank) + (whitechar (classify-line-1)) + (else 'comment))) + +(define (skip-past-line) + (when (not *at-eof?*) + (char-case *char* + (#\newline + (advance-char-1)) + (else + (advance-char-1) + (skip-past-line))))) + +(define (lex-comment) ;; a -- style comment + (advance-char) + (cond (*at-eof?* (lexer-eof-in-comment *current-line*)) + ((char=? *char* #\newline) + (lex-one-token)) + (else + (lex-comment)))) + +(define (lexer-eof-in-comment start-line) + (signal-eof-in-comment start-line) + (lex-one-token)) ; will return the eof token + +(define (signal-eof-in-comment start-line) + (lexer-error 'eof-in-comment + "End of file in comment starting at line ~A." start-line)) + +;;; Here *char* and *peek-char* are the first two chars on a line. + +(define (scan-symbol) + (scan-list-of (symbol #\:))) + +(define (scan-var-con) + (scan-list-of (large small digit #\' #\_))) + +(define (lex-ncomment) + (lex-ncomment-1 *current-line*)) + +(define (lex-ncomment-1 start-line) + (if *at-eof?* + (lexer-eof-in-comment start-line) + (char-case *char* + (#\- (cond ((char=? *peek-char* #\}) + (advance-char) + (advance-char)) + (else + (advance-char) + (lex-ncomment-1 start-line)))) + (#\{ (cond ((char=? *peek-char* #\-) + (advance-char) + (advance-char) + (lex-ncomment) + (lex-ncomment-1 start-line)) + (else + (advance-char) + (lex-ncomment-1 start-line)))) + (else + (advance-char) + (lex-ncomment-1 start-line))))) + +(define (lex-varid) + (let ((sym (scan-var-con))) + (parse-reserved sym varid + "case" "class" + "data" "default" "deriving" + "else" + "hiding" + "if" "import" "in" "infix" "infixl" "infixr" "instance" "interface" + "let" + "module" + "of" + "renaming" + "then" "to" "type" + "where"))) + +(define (lex-conid) + (let ((sym (scan-var-con))) + (emit-token/string 'conid sym))) + +(define (lex-consym) + (let ((sym (scan-symbol))) + (cond ((string=/list? (cdr sym) ":") + (emit-token '\:\:)) + (else + (emit-token/string 'consym sym))))) + +(define (lex-varsym) + (let ((sym (scan-symbol))) + (cond ((and (string=/list? sym "<") (char=? *char* #\-)) + (advance-char) + (emit-token '\<\-)) + ((and (string=/list? sym "#") + (char=? *char* #\-) + (char=? *peek-char* #\})) + (advance-char) + (advance-char) + (emit-token 'end-annotation)) + (else + (parse-reserved sym varsym + ".." + "=>" "=" + "@" + "\\" + "|" + "~"))))) + +(define (lex-integer radix) + (lex-integer-1 radix 0)) + +(define (lex-integer-1 radix psum) + (declare (type fixnum radix) + (type integer psum)) + (let ((d (char->digit *char* radix))) + (if d + (begin + (advance-char) + (lex-integer-1 radix (+ (* psum radix) (the fixnum d)))) + psum))) + +(define (lex-fraction int-part denominator) + (declare (type integer int-part denominator)) + (let ((d (char->digit *char* 10))) + (if d + (begin + (advance-char) + (lex-fraction + (+ (* int-part 10) (the fixnum d)) (* denominator 10))) + (values int-part denominator)))) + +(define (lex-numeric) + (let ((int-part (lex-integer 10))) + (if (and (char=? *char* #\.) + (char->digit *peek-char* 10)) + (lex-float int-part) + (emit-token 'integer int-part)))) + +(define (lex-float int-part) + (advance-char) + (multiple-value-bind (numerator denominator) (lex-fraction int-part 1) + (let ((no-exponent + (lambda () (emit-token 'float numerator denominator 0)))) + (char-case *char* + (exponent + (char-case *peek-char* + (digit + (advance-char) + (lex-float/exp numerator denominator 1)) + ((#\+ #\-) + (cond ((char->digit (peek-char-2) 10) + (let ((sign (if (char=? *peek-char* '#\+) 1 -1))) + (advance-char) + (advance-char) + (lex-float/exp numerator denominator sign))) + (else + (funcall no-exponent)))) + (else + (funcall no-exponent)))) + (else + (emit-token 'float numerator denominator 0)))))) + +(define (lex-float/exp numerator denominator sign) + (let ((exponent (lex-integer 10))) + (emit-token 'float numerator denominator (* sign exponent)))) + +(define (lex-char) + (advance-char) + (let ((c + (char-case *char* + (#\' (signal-null-character) + '#\?) + (#\\ (lex-escaped-char '#f)) + ((#\space graphic) + (let ((ch *char*)) + (advance-char) + ch)) + (else + (signal-bad-character-constant *char*) + (advance-char) + `#\?)))) + (cond ((char=? *char* '#\') + (advance-char) + (emit-token 'char c)) + (else + (signal-missing-char-quote) + (skip-to-quote-or-eol))))) + +(define (signal-null-character) + (lexer-error 'null-character + "Null character '' is illegal - use '\\'' for a quote.")) + +(define (signal-bad-character-constant ch) + (lexer-error 'bad-character-constant + "The character `~a' may not appear in a character literal." ch)) + +(define (signal-missing-char-quote) + (lexer-error 'missing-char-quote + "Character constant has more than one character.")) + + +(define (skip-to-quote-or-eol) + (if *at-eof?* + (lex-one-token) + (char-case *char* + (#\' (advance-char) + (lex-one-token)) + (#\newline (advance-char) + (lex-one-token)) + (else + (advance-char) + (skip-to-quote-or-eol))))) + +(define (lex-string) + (advance-char) + (emit-token 'string (list->string (gather-string-chars)))) + +(define (gather-string-chars) + (char-case *char* + (#\\ + (let ((ch (lex-escaped-char '#t))) + (if (eq? ch 'null) + (gather-string-chars) + (cons ch (gather-string-chars))))) + (#\" + (advance-char) + '()) + ((graphic #\space) + (let ((ch *char*)) + (advance-char) + (cons ch (gather-string-chars)))) + (#\newline + (signal-missing-string-quote) + '()) + (else + (signal-bad-string-constant *char*) + (advance-char) + (gather-string-chars)))) + +(define (signal-missing-string-quote) + (lexer-error 'missing-string-quote + "String continued over end of line.")) + +(define (signal-bad-string-constant ch) + (lexer-error 'bad-string-constant + "The character `~a' may not appear in a string literal." ch)) + + +(define (convert-stupid-control-character-names) + (let ((c1 *char*) + (c2 *peek-char*)) + (advance-char) + (advance-char) + (let ((s2 (string c1 c2)) + (s3 (string c1 c2 *char*))) + (let ((srch3 (assoc s3 *control-char-names*))) + (cond (srch3 + (advance-char) + (integer->char (cdr srch3))) + (else + (let ((srch2 (assoc s2 *control-char-names*))) + (cond (srch2 + (integer->char (cdr srch2))) + (else + (signal-bad-control-char s3) + `#\?))))))))) + +(define (signal-bad-control-char name) + (lexer-error 'invalid-control-char + "`~a' is not a recognized control character name." name)) + + +(define (lex-escaped-char in-string?) + (advance-char) + (char-case *char* + ((#\a #\b #\f #\n #\r #\t #\v) + (let* ((ccode (cdr (assoc *char* *short-control-char-names*))) + (ccode1 (cdr (assoc ccode *control-char-names*)))) + (advance-char) + (integer->char ccode1))) + ((#\\ #\' #\") + (let ((ch *char*)) + (advance-char) + ch)) + (#\& + (advance-char) + (cond (in-string? 'null) + (else + (signal-bad-&-escape) + '#\?))) + (#\^ + ;; *** This code is problematic because it assumes + ;; *** (1) that you can do the arithmetic on the character codes + ;; *** (2) that the resulting integer can actually be coerced to + ;; *** the right character object in the host Lisp. + (advance-char) + (char-case *char* + ((large #\@ #\[ #\\ #\] #\^ #\_) + (let ((code (+ (- (char->integer *char*) + (char->integer '#\A)) + *control-A*))) + (advance-char) + (integer->char code))) + (else + (signal-bad-^-escape *char*) + '#\?))) + (large + (convert-stupid-control-character-names)) + (digit + (convert-num-to-char (lex-integer 10))) + (#\o + (advance-char) + (cond ((char->digit *char* 8) + (convert-num-to-char (lex-integer 8))) + (else + (signal-missing-octal-digits) + '#\?))) + (#\x + (advance-char) + (cond ((char->digit *char* 16) + (convert-num-to-char (lex-integer 16))) + (else + (signal-missing-hex-digits) + `#\?))) + (whitechar + (cond (in-string? + (lex-gap)) + (else + (signal-bad-gap) + `#\?))) + (else + (signal-bad-escape *char*) + `#\?))) + +(define (signal-bad-&-escape) + (lexer-error 'bad-&-escape + "The escape `\\&' is not allowed inside a character literal.")) + +(define (signal-bad-^-escape ch) + (lexer-error 'bad-^-escape + "The escape `\\^~a' is not recognized." ch)) + +(define (signal-missing-octal-digits) + (lexer-error 'missing-octal-digits + "No digits provided for `\\o' escape.")) + +(define (signal-missing-hex-digits) + (lexer-error 'missing-hex-digits + "No digits provided for `\\x' escape.")) + +(define (signal-bad-gap) + (lexer-error 'invalid-gap + "Gaps are not allowed inside character literals.")) + +(define (signal-bad-escape ch) + (lexer-error 'bad-escape + "The escape `\\~a' is not recognized." ch)) + + + +;;; *** This code is problematic because it assumes that integers +;;; *** between 0 and 255 map on to characters with the corresponding +;;; *** ASCII encoding in the host Lisp, and that the host Lisp actually +;;; *** supports 255 characters. + +(define (convert-num-to-char num) + (cond ((and (>= num 0) (>= *max-char* num)) + (integer->char num)) + (else + (signal-char-out-of-range num) + '#\?))) + +(define (signal-char-out-of-range num) + (lexer-error 'char-out-of-range + "There is no character corresponding to code ~s." num)) + + +(define (lex-gap) + (cond (*at-eof?* + (signal-eof-in-gap) + 'null) + (else + (char-case *char* + (whitechar + (advance-char) + (lex-gap)) + (#\\ + (advance-char) + 'null) + (else + (signal-missing-gap) + 'null))))) + + +(define (signal-eof-in-gap) + (lexer-error 'eof-in-gap + "End of file encountered inside gap.")) + +(define (signal-missing-gap) + (lexer-error 'missing-gap + "Missing gap delimiter, or junk inside gap.")) diff --git a/parser/module-parser.scm b/parser/module-parser.scm new file mode 100644 index 0000000..2ffa391 --- /dev/null +++ b/parser/module-parser.scm @@ -0,0 +1,312 @@ +;;; File: module-parser Author: John + +;;; This is for using the parser to parse strings. + +(define (parse-from-string string parse-proc filename) + (dynamic-let ((*current-file* filename)) + (call-with-input-string string + (lambda (port) + (let ((tokens (lex-port port '#f))) + (init-token-stream tokens) + (let ((res (funcall parse-proc))) + (if (not (eq-token? 'eof)) + (signal-leftover-tokens) + res))))))) + +(define (signal-leftover-tokens) + (fatal-error 'leftover-tokens + "Leftover tokens after parsing.")) + + +;;; This file deals with the basic structure of a module. It also adds +;;; the `module Main where' required by abbreviated modules. + +(define (parse-tokens tokens) + (init-token-stream tokens) + (let ((mod (token-case + (|module| (parse-module)) + (else (parse-modules/named '|Main| '()))))) + (cons mod (parse-module-list)))) + +(define (parse-module) + (token-case + (modid (let* ((mod-name (token->symbol)) + (exports (parse-exports))) + (require-token + |where| + (signal-missing-token "`where'" "module definition")) + (parse-modules/named mod-name exports))) + (else (signal-missing-token "" "module definition")))) + +(define (parse-module-list) + (token-case + (|module| + (let ((mod (parse-module))) + (cons mod (parse-module-list)))) + (eof '()) + (else (signal-missing-module)))) + +(define (signal-missing-module) + (parser-error 'missing-module + "Missing `module', or leftover junk after module definition.")) + +(define (parse-exports) + (token-case + (\( (parse-export-list)) + (else '()))) + +(define (parse-export-list) + (let ((entity (parse-entity 'export))) + (token-case + (\) (list entity)) + (\, (cons entity (parse-export-list))) + (else (signal-missing-token "`)' or ','" "export list"))))) + +(define (parse-modules/named mod-name exports) + (trace-parser module + (let ((mod-ast (make module + (name mod-name) + (type 'standard) + (exports exports) + (default *standard-module-default*)))) + (start-layout (lambda (in-layout?) + (parse-module-decls mod-ast in-layout? 'import)))))) + +;;; The mod-ast fields are kept in non-reversed order by appending +;;; each decl to the end of the appropriate list. This loses for +;;; value decls, so these are in reversed order!! + +(define (parse-module-decls mod-ast in-layout? state) + (token-case + (|import| (let ((import (parse-import))) + (if (eq? state 'import) + (push-decl-list import (module-imports mod-ast)) + (signal-misplaced-import))) + (terminate-topdecl mod-ast in-layout? state)) + (|infix| (terminate-topdecl mod-ast in-layout? + (parse-fixity 'n mod-ast state))) + (|infixl| (terminate-topdecl mod-ast in-layout? + (parse-fixity 'l mod-ast state))) + (|infixr| (terminate-topdecl mod-ast in-layout? + (parse-fixity 'r mod-ast state))) + (|data| (let ((data-decl (parse-type-decl '#f))) + (push-decl-list data-decl (module-algdatas mod-ast))) + (terminate-topdecl mod-ast in-layout? 'topdecl)) + (|type| (let ((synonym-decl (parse-synonym-decl))) + (push-decl-list synonym-decl (module-synonyms mod-ast))) + (terminate-topdecl mod-ast in-layout? 'topdecl)) + (|class| (let ((class-decl (parse-class-decl))) + (push-decl-list class-decl (module-classes mod-ast))) + (terminate-topdecl mod-ast in-layout? 'topdecl)) + (|instance| (let ((instance-decl (parse-instance-decl '#f))) + (push-decl-list instance-decl (module-instances mod-ast))) + (terminate-topdecl mod-ast in-layout? 'topdecl)) + (|default| (let ((types + (token-case + (\( (token-case (\) '()) + (else (parse-type-list)))) + (else (list (parse-type)))))) + (if (eq? (module-default mod-ast) *standard-module-default*) + (setf (module-default mod-ast) + (make default-decl (types types))) + (signal-multiple-defaults))) + (terminate-topdecl mod-ast in-layout? 'topdecl)) + ((begin-annotation no-advance) + (let ((annotations (parse-annotations))) + (setf (module-annotations mod-ast) + (append (module-annotations mod-ast) annotations))) + (terminate-topdecl mod-ast in-layout? state)) + (pat-start (let ((decl (parse-decl))) + (setf (module-decls mod-ast) + (decl-push decl (module-decls mod-ast)))) + (terminate-topdecl mod-ast in-layout? 'topdecl)) + (else + (maybe-end-module mod-ast in-layout? state)))) + +(define (signal-misplaced-import) + (parser-error 'misplaced-import + "The import declaration is misplaced.")) + +(define (signal-multiple-defaults) + (parser-error 'multiple-defaults + "There are multiple default declarations.")) + +(define (terminate-topdecl mod-ast in-layout? state) + (token-case + (\; (parse-module-decls mod-ast in-layout? state)) + (else (maybe-end-module mod-ast in-layout? state)))) + +(define (maybe-end-module mod-ast in-layout? state) + (declare (ignore state)) + (cond ((or (eq-token? '|module|) (eq-token? 'eof) (eq-token? '\}) + (eq-token? '$\})) + (close-layout in-layout?) + (wrapup-module mod-ast) + mod-ast) + (else + (signal-invalid-syntax "a topdecl")))) + +(define (wrapup-module mod-ast) + (setf (module-decls mod-ast) + (nreverse (module-decls mod-ast))) + (when (and (null? (module-imports mod-ast)) + (null? (module-decls mod-ast)) + (null? (module-algdatas mod-ast)) + (null? (module-synonyms mod-ast)) + (null? (module-instances mod-ast)) + (null? (module-classes mod-ast))) + (signal-empty-module))) + +(define (signal-empty-module) + (parser-error 'empty-module "Module definition is empty.")) + +(define (parse-import) + (save-parser-context + (token-case + (modid (let ((mod (token->symbol)) + (mode 'all) + (specs '())) + (token-case + (\( (setf mode 'by-name) + (token-case + (\) (setf specs '())) + (else (setf specs (parse-import-list))))) + (|hiding| (require-token + \( + (signal-missing-token "`('" "hiding clause")) + (setf specs (parse-import-list))) + (else '())) + (let ((renamings (token-case (|renaming| + (require-token + \( + (signal-missing-token + "`('" "renaming clause")) + (parse-renamings)) + (else '())))) + (make import-decl (module-name mod) (mode mode) (specs specs) + (renamings renamings))))) + (else + (signal-missing-token "" "import declaration"))))) + +(define (parse-import-list) + (let ((import (parse-entity 'import))) + (token-case + (\, (cons import (parse-import-list))) + (\) (list import)) + (else (signal-missing-token "`)' or `,'" "import list"))))) + +(define (parse-renamings) + (let ((renaming + (save-parser-context + (token-case + (var (let ((name1 (var->symbol))) + (require-token + |to| + (signal-missing-token "`to'" "import renaming clause")) + (token-case + (var (let ((name2 (var->symbol))) + (make renaming (from name1) (to name2) + (referenced? '#f)))) + (else (signal-invalid-syntax "import renaming clause"))))) + (con (let ((name1 (con->symbol))) + (require-token + |to| + (signal-missing-token "`to'" "import renaming clause")) + (token-case + (con (let ((name2 (con->symbol))) + (make renaming (from name1) (to name2) + (referenced? '#f)))) + (else (signal-invalid-syntax "import renaming clause"))))) + (else (signal-invalid-syntax "import renaming clause")))))) + (token-case (\, (cons renaming (parse-renamings))) + (\) (list renaming))))) + +(define (parse-fixity associativity mod-ast state) + (let ((fixity-decl + (save-parser-context + (let* ((prec (token-case + (k (let ((p (token->integer))) + (cond ((<= p 9) + p) + (else + (signal-bad-fixity) + 9)))) + (else 9))) + (ops (parse-op-list)) + (fixity (make fixity (associativity associativity) + (precedence prec)))) + (make fixity-decl (fixity fixity) (names ops)))))) + (push-decl-list fixity-decl (module-fixities mod-ast)) + (cond ((or (eq? state 'import) + (eq? state 'fixity)) + 'fixity) + (else + (signal-misplaced-fixity) + state)))) + + +(define (signal-bad-fixity) + (parser-error 'bad-fixity + "Expecting fixity value of 0 - 9.")) + +(define (signal-misplaced-fixity) + (parser-error 'misplaced-fixity "The fixity declaration is misplaced.")) + +(define (parse-op-list) + (let ((name (token-case + (op (op->symbol)) + (else (signal-missing-token "" "fixity declaration"))))) + (token-case + (\, (cons name (parse-op-list))) + (else (list name))))) + +(define (parse-entity context) + (trace-parser entity + (save-parser-context + (token-case + (var (var->entity)) + (tycon + (let ((name (token->symbol))) + (token-case + (\( (token-case + (\.\. (require-token + '\) + (signal-missing-token "`)'" "class or datatype entity")) + (make entity-abbreviated (name name))) + (var (parse-entity-class name)) + (con (parse-entity-datatype name)) + (\) (make entity-class (name name) (methods '()))) + (else (signal-invalid-syntax "an entity")))) + (\.\. (if (eq? context 'export) + (make entity-module (name name)) + (signal-invalid-syntax "an entity"))) + (else + (make entity-con (name name)))))) + (else (signal-invalid-syntax "an entity")))))) + +(define (parse-entity-class class-name) + (let ((vars (parse-var-list))) + (make entity-class (name class-name) (methods vars)))) + +(define (parse-entity-datatype type-name) + (let ((constrs (parse-con-list))) + (make entity-datatype (name type-name) (constructors constrs)))) + +(define (parse-var-list) + (token-case + (var (let ((name (var->symbol))) + (token-case + (\) (list name)) + (\, (cons name (parse-var-list))) + (else + (signal-missing-token "`)' or `,'" "class entity"))))) + (else (signal-missing-token "" "class entity")))) + +(define (parse-con-list) + (token-case + (con (let ((name (con->symbol))) + (token-case + (\) (list name)) + (\, (cons name (parse-con-list))) + (else (signal-missing-token "`)' or `,'" "datatype entity"))))) + (else (signal-missing-token "" "datatype entity")))) diff --git a/parser/parser-debugger.scm b/parser/parser-debugger.scm new file mode 100644 index 0000000..40d9382 --- /dev/null +++ b/parser/parser-debugger.scm @@ -0,0 +1,81 @@ +;;; These routines are strictly for debugging the parser. They could +;;; be removed from the system later. + +;;; define some debugging stuff +;;; Here's the debugging control: +;;; Capabilities: +;;; record start (line,token,production,k) +;;; record end (line,token,prodection,k) +;;; print end (line,token,prodection,k,value) +;;; break start +;;; break end + +(define *parser-debug-options* '()) +(define *parser-debug-lines* '()) +(define *parser-debug-id* 0) + +(define (watch-lines . lines) + (setf *parser-debug-lines* lines)) + +(define (watching-this-line?) + (and (not (eq? *parser-debug-lines* 'none)) + (or (null? *parser-debug-lines*) + (and (>= *current-line* (car *parser-debug-lines*)) + (or (null? (cdr *parser-debug-lines*)) + (<= *current-line* (cadr *parser-debug-lines*))))))) + +(define (ptrace-print-obj x) + (pprint x)) + +(define (ptrace-breakpoint) + (error "Breakpoint~%")) + +(define (parser-show-context id tag msg) + (format '#t "~A parse of ~A(~A) Line: ~A Token: ~A" + msg tag id *current-line* *token*) + (when (not (null? *token-args*)) + (format '#t " ~A" *token-args*)) + (format '#t "~%")) + +(define (ptrace-clear) + (setf *parser-debug-options* '())) + +(define (ptrace-pop) + (pop *parser-debug-options*)) + +(define (ptrace-watch . things) + (dolist (x things) + (push (cons x 'watch) *parser-debug-options*))) + +(define (ptrace-show . things) + (dolist (x things) + (push (cons x 'show) *parser-debug-options*))) + +(define (ptrace-break . things) + (dolist (x things) + (push (cons x 'break) *parser-debug-options*))) + +;;; Routines called by the trace-parser macro + +(define (tracing-parse/entry tag) + (let ((all? (assq 'all *parser-debug-options*)) + (this? (assq tag *parser-debug-options*))) + (cond ((or all? this?) + (incf *parser-debug-id*) + (parser-show-context *parser-debug-id* tag "Entering") + (when (and this? (eq? (cdr this?) 'break)) + (ptrace-breakpoint)) + *parser-debug-id*) + (else 0)))) + +(define (tracing-parse/exit tag id res) + (let ((all? (assq 'all *parser-debug-options*)) + (this? (assq tag *parser-debug-options*))) + (when (and (or all? this?) (not (eq? tag 0))) + (setf (dynamic *returned-obj*) res) + (parser-show-context id tag "Exiting") + (when (and this? (eq? (cdr this?) 'show)) + (ptrace-print-obj res)) + (when (and this? (eq? (cdr this?) 'break)) + (ptrace-breakpoint))))) + diff --git a/parser/parser-driver.scm b/parser/parser-driver.scm new file mode 100644 index 0000000..cd42d3d --- /dev/null +++ b/parser/parser-driver.scm @@ -0,0 +1,48 @@ + +;;; This is the top level entry to the parse. The input is a list of file +;;; names to be parsed and the output is a list of modules. Interface files +;;; generate modules similar to ordinary files. + +(define (parse-files filenames) + (let ((all-mods '())) + (dolist (file filenames) + (let* ((ext (filename-type file)) + (mods (cond ((string=? ext ".hs") + (parse-single-file file)) + ((string=? ext ".lhs") + (parse-single-file/literate file)) + ((string=? ext ".hi") + (parse-single-file/interface file))))) + (setf all-mods (append all-mods mods)))) + all-mods)) + +(define (parse-single-file filename) + (parse-single-file-1 filename '#f '#f)) + +(define (parse-single-file/literate filename) + (parse-single-file-1 filename '#t '#f)) + +(define (parse-single-file/interface filename) + (parse-single-file-1 filename '#f '#t)) + +(define (parse-single-file-1 filename literate? interface?) + (when (memq 'reading *printers*) + (format '#t "Reading Haskell source file ~s.~%" filename)) + (when (not (file-exists? filename)) + (signal-file-not-found filename)) + (dynamic-let ((*current-file* filename)) + (let ((mods '())) + (call-with-input-file filename + (lambda (port) + (let* ((tokens (lex-port port literate?)) + (module-asts (if interface? + (parse-tokens/interface tokens) + (parse-tokens tokens)))) + (setf mods module-asts)))) + (when (memq 'parse *printers*) + (dolist (m mods) + (format '#t "~%") + (print-full-module m))) + mods))) + + diff --git a/parser/parser-errors.scm b/parser/parser-errors.scm new file mode 100644 index 0000000..ae4d097 --- /dev/null +++ b/parser/parser-errors.scm @@ -0,0 +1,74 @@ +;;; This contains parser error handlers. They, in turn, call the +;;; system error handlers. + +(define (lexer-error id . msgs) + (parser-error/common id 'recoverable msgs '#t) + `#\?) + +(define (parser-error id . msgs) + (parser-error/common id 'phase msgs '#f) + (if (null? *layout-stack*) + (abort-compilation) + (recover-to-next-decl *token-stream*))) + +(define (parser-error/recoverable id . args) + (parser-error/common id 'recoverable args '#f)) + +(define (parser-error/common id type msgs in-lexer?) + (let ((place + (if in-lexer? + (list "Error occured at in file ~A at line ~A, column ~A." + *current-file* *current-line* *current-col*) + (list "Error occured at in file ~A at line ~A, token ~A." + *current-file* *current-line* + (cond ((null? *token-args*) + *token*) + ((null? (cdr *token-args*)) + (car *token-args*)) + (else *token-args*)))))) ; could be better + (haskell-error id type (list place msgs)))) + +(define (recover-to-next-decl tokens) + (cond ((null? tokens) + (abort-compilation)) + ((eq? (car (car tokens)) 'line) + (search-layout-stack *layout-stack* tokens (caddr (car tokens)))) + (else (recover-to-next-decl (cdr tokens))))) + +(define (search-layout-stack layouts tokens column) + (cond ((null? layouts) + (abort-compilation)) + ((> column (layout-col (car layouts))) + (recover-to-next-decl (cdr tokens))) + ((= column (layout-col (car layouts))) + (setf *current-col* column) + (setf *current-line* (cadr (car tokens))) + (setf *token-stream* (cdr tokens)) + (advance-token) ; loads up *token* + ;; *** layout-recovery-fn is not defined anywhere! + (funcall (layout-recovery-fn (car layouts)))) + (else + (setf *layout-stack* (cdr *layout-stack*)) + (search-layout-stack (cdr layouts) tokens column)))) + + +;;; Here are some very commonly used signalling functions. +;;; Other (more specific) signalling functions are defined near +;;; the places where they are called. + + +;;; This is used when a particular token isn't found. + +(define (signal-missing-token what where) + (parser-error 'missing-token + "Missing ~a in ~a." what where)) + + +;;; This is used to signal more complicated parse failures involving +;;; failure to match a nonterminal. + +(define (signal-invalid-syntax where) + (parser-error 'invalid-syntax + "Invalid syntax appears where ~a is expected." where)) + + diff --git a/parser/parser-globals.scm b/parser/parser-globals.scm new file mode 100644 index 0000000..528e582 --- /dev/null +++ b/parser/parser-globals.scm @@ -0,0 +1,27 @@ +;;; Global vars used in the parser + +(define *current-line* '()) ; current line the scanner is on +(define *current-col* '()) ; current col; valid at start of line & + ; after where,let,of + +;;; Lexer + +(define *lex-literate?* '#f) +(define *start-line* 0) +(define *start-col* 0) +(define *on-new-line?* '#t) +(define *save-col?* '#f) +(define *port* '()) +(define *tokens* '()) +(define *char* 0) +(define *peek-char* 0) +(define *at-eof/p?* 0) +(define *at-eof?* 0) +(define *on-new-line? '#f) + +;;; Parser + +(define *token-stream* '()) ; remaining tokens to be parsed +(define *token* '()) ; current token type +(define *token-args* '()) ; current token arguments +(define *layout-stack* '()) ; columns at which layout is being done diff --git a/parser/parser-macros.scm b/parser/parser-macros.scm new file mode 100644 index 0000000..c4f5a63 --- /dev/null +++ b/parser/parser-macros.scm @@ -0,0 +1,327 @@ +;;; Macro definitions for the parser & lexer. + + +;;; This macro allows debugging of the lexer. Before releasing, this can +;;; be replaced by (begin ,@body) for faster code. + +(define-syntax (trace-parser tag . body) +; `(begin +; (let* ((k (tracing-parse/entry ',tag)) +; (res (begin ,@body))) +; (tracing-parse/exit ',tag k res) +; res)) + (declare (ignore tag)) + `(begin ,@body) + ) + +;;; Macros used by the lexer. + +;;; The lexer used a macro, char-case, to dispatch on the syntactic catagory of +;;; a character. These catagories (processed at compile time) are defined +;;; here. Note that some of these definitions use the char-code +;;; directly and would need updating for different character sets. + +(define *lex-definitions* + '((vtab 11) ; define by ascii code to avoid relying of the reader + (formfeed 12) + (whitechar #\newline #\space #\tab formfeed vtab) + (small #\a - #\z) + (large #\A - #\Z) + (digit #\0 - #\9) + (symbol #\! #\# #\$ #\% #\& #\* #\+ #\. #\/ #\< #\= #\> #\? #\@ + #\\ #\^ #\|) + (presymbol #\- #\~) + (exponent #\e #\E) + (graphic large small digit + #\! #\" #\# #\$ #\% #\& #\' #\( #\) #\* #\+ + #\, #\- #\. #\/ #\: #\; #\< #\= #\> #\? #\@ + #\[ #\\ #\] #\^ #\_ #\` #\{ #\| #\} #\~) + (charesc #\a #\b #\f #\n #\r #\t #\v #\\ #\" #\' #\&) + (cntrl large #\@ #\[ #\\ #\] #\^ #\_))) + +;;; The char-case macro is similar to case using characters to select. +;;; The following capabilities are added by char-case: +;;; pre-defined constants are denoted by symbols (defined above) +;;; ranges of characters are represented using -. For example, +;;; (#\a - #\z #\A - #\Z) denotes all alphabetics. +;;; numbers refer to the char code of a character. +;;; The generated code is optimized somewhat to take advantage of +;;; consecutive character ranges. With a little work, this could be +;;; implemented using jump tables someday. + +(define-syntax (char-case exp . alts) + (expand-char-case exp alts)) + +(define (expand-char-case exp alts) + (let ((temp (gensym))) + `(let ((,temp ,exp)) + ,(expand-char-case1 temp alts)))) + +(define (expand-char-case1 temp alts) + (if (null? alts) + '() + (let* ((alt (car alts)) + (test (car alt)) + (body (cons 'begin (cdr alt))) + (rest (expand-char-case1 temp (cdr alts)))) + (cond ((eq? test 'else) + body) + (else + `(if (or ,@(gen-char-tests temp + (if (pair? test) test (list test)))) + ,body + ,rest)))))) + +(define (gen-char-tests temp tests) + (gen-char-tests-1 temp + (sort-list (gather-char-tests tests) (function char=? ,temp ',first) + (char<=? ,temp ',current)) + ,@(gen-char-tests-1 temp chars)))) + +(define (consec-chars? c1 c2) + (eqv? (+ 1 (char->integer c1)) (char->integer c2))) + +(define (long-enough-run? l n) + (or (eqv? n 1) + (and (pair? (cdr l)) + (consec-chars? (car l) (cadr l)) + (long-enough-run? (cdr l) (1- n))))) + +(define (gather-char-tests tests) + (cond ((null? tests) + '()) + ((symbol? (car tests)) + (let ((new-test (assq (car tests) *lex-definitions*))) + (if new-test + (gather-char-tests (append (cdr new-test) (cdr tests))) + (error "Unknown character class: ~A~%" (car tests))))) + ((integer? (car tests)) + (cons (integer->char (car tests)) + (gather-char-tests (cdr tests)))) + ((and (pair? (cdr tests)) (eq? '- (cadr tests))) + (letrec ((fn (lambda (a z) + (if (char>? a z) + (gather-char-tests (cdddr tests)) + (cons a (funcall + fn (integer->char + (+ 1 (char->integer a))) z)))))) + (funcall fn (car tests) (caddr tests)))) + ((char? (car tests)) + (cons (car tests) (gather-char-tests (cdr tests)))) + (else + (error "Invalid selector in char-case: ~A~%" (car tests))))) + +;;; This macro scans a list of characters on a given syntaxtic catagory. +;;; The current character is always included in the resulting list. + +(define-syntax (scan-list-of char-type) + `(letrec ((test-next (lambda () + (char-case *char* + (,char-type + (let ((c *char*)) + (advance-char) + (cons c (funcall test-next)))) + (else '()))))) + (let ((c *char*)) + (advance-char) + (cons c (funcall test-next))))) + +;;; This macro tests for string equality in which the strings are +;;; represented by lists of characters. The comparisons are expanded +;;; inline (really just a little partial evaluation going on here!) for +;;; fast execution. The tok argument evaluate to a list of chars. The string +;;; argument must be a string constant, which is converted to characters +;;; as the macro expands. + +(define-syntax (string=/list? tok string) + (let ((temp (gensym))) + `(let ((,temp ,tok)) + ,(expand-string=/list? temp (string->list string))))) + +(define (expand-string=/list? var chars) + (if (null? chars) + `(null? ,var) + (let ((new-temp (gensym))) + `(and (pair? ,var) + (char=? (car ,var) ',(car chars)) + (let ((,new-temp (cdr ,var))) + ,(expand-string=/list? new-temp (cdr chars))))))) + +;;; This macro extends the string equality defined above to search a +;;; list of reserved words quickly for keywords. It does this by a case +;;; dispatch on the first character of the string and then processing +;;; the remaining characters wirh string=/list. This would go a little +;;; faster with recursive char-case statements, but I'm a little too +;;; lazy at for this at the moment. If a keyword is found is emitted +;;; as a symbol. If not, the token string is emitted with the token +;;; type indicated. Assume the string being scanned is a list of +;;; chars assigned to a var. (Yeah - I know - I should add a gensym +;;; var for this argument!!). + +(define-syntax (parse-reserved var token-type . reserved-words) + (let ((sorted-rws (sort-list reserved-words (function stringsymbol (car group)))) + ,@(expand-parse-reserved/group var (cdr group))))) + + +;;; The following macros are used by the parser. + +;;; The primary macro used by the parser is token-case, which dispatches +;;; on the type of the current token (this is always *token* - unlike the +;;; lexer, no lookahead is provided; however, some of these dispatches are +;;; procedures that do a limited lookahead. The problem with lookahead is that +;;; the layout rule adds tokens which are not visible looking into the +;;; token stream directly. + +;;; Unlike char-case, the token is normally advanced unless the selector +;;; includes `no-advance'. The final else also avoids advancing the token. + +;;; In addition to raw token types, more complex types can be used. These +;;; are defined here. The construct `satisfies fn' calls the indicated +;;; function to determine whether the current token matches. + +;;; If the token type to be matched is not a constant, the construct +;;; `unquote var' matches the current token against the type in the var. + +(define *predefined-syntactic-catagories* '( + (+ satisfies at-varsym/+?) + (- satisfies at-varsym/-?) + (tycon no-advance conid) + (tyvar no-advance varid) + (var no-advance varid satisfies at-varsym/paren?) + (con no-advance conid satisfies at-consym/paren?) + (name no-advance var con) + (consym/paren no-advance satisfies at-consym/paren?) + (varsym? no-advance varsym) + (consym? no-advance consym) + (varid? no-advance varid) + (conid? no-advance conid) + (op no-advance varsym consym \`) + (varop no-advance varsym satisfies at-varid/quoted?) + (conop no-advance consym satisfies at-conid/quoted?) + (modid no-advance conid) + (literal no-advance integer float char string) + (numeric no-advance integer float) + (k no-advance integer) + (+k no-advance satisfies at-+k?) + (-n no-advance satisfies at--n?) + (apat-start no-advance varid conid literal _ \( \[ \~) + (pat-start no-advance - apat-start) + (atype-start no-advance tycon tyvar \( \[) + (aexp-start no-advance varid conid \( \[ literal) + )) + +;;; The format of token-case is +;;; (token-case +;;; (sel1 . e1) (sel2 . e2) ... [(else . en)]) +;;; If the sel is a symbol it is the same as a singleton list: (@ x) = ((@) x) + +;;; Warning: this generates rather poor code! Should be fixed up someday. + +(define-syntax (token-case . alts) + `(cond ,@(map (function gen-token-case-alt) alts))) + +(define (gen-token-case-alt alt) + (let ((test (car alt)) + (code (cdr alt))) + (cond ((eq? test 'else) + `(else ,@code)) + ((symbol? test) + (gen-token-case-alt-1 (expand-catagories (list test)) code)) + (else + (gen-token-case-alt-1 (expand-catagories test) code))))) + +(define (expand-catagories terms) + (if (null? terms) + terms + (let ((a (assq (car terms) *predefined-syntactic-catagories*)) + (r (expand-catagories (cdr terms)))) + (if (null? a) + (cons (car terms) r) + (expand-catagories (append (cdr a) r)))))) + +(define (gen-token-case-alt-1 test code) + `((or ,@(gen-token-test test)) + ,@(if (memq 'no-advance test) '() '((advance-token))) + ,@code)) + +(define (gen-token-test test) + (cond ((null? test) + '()) + ((eq? (car test) 'no-advance) + (gen-token-test (cdr test))) + ((eq? (car test) 'unquote) + (cons `(eq? *token* ,(cadr test)) (gen-token-test (cddr test)))) + ((eq? (car test) 'satisfies) + (cons (list (cadr test)) (gen-token-test (cddr test)))) + (else + (cons `(eq? *token* ',(car test)) (gen-token-test (cdr test)))))) + +;;; require-tok requires a specific token to be at the scanner. If it +;;; is found, the token is advanced over. Otherwise, the error +;;; routine is called. + +(define-syntax (require-token tok error-handler) + `(token-case + (,tok '()) + (else ,error-handler))) + +;;; The save-parser-context macro captures the current line & file and +;;; attaches it to the ast node generated. + +(define-syntax (save-parser-context . body) + (let ((temp1 (gensym)) + (temp2 (gensym))) + `(let ((,temp1 (capture-current-line)) + (,temp2 (begin ,@body))) + (setf (ast-node-line-number ,temp2) ,temp1) + ,temp2))) + +(define (capture-current-line) + (make source-pointer (line *current-line*) (file *current-file*))) + +(define-syntax (push-decl-list decl place) + `(setf ,place (nconc ,place (list ,decl)))) + diff --git a/parser/parser.scm b/parser/parser.scm new file mode 100644 index 0000000..7a91930 --- /dev/null +++ b/parser/parser.scm @@ -0,0 +1,54 @@ +;;; parser.scm -- compilation unit definition for the lexer and parser +;;; +;;; author : John +;;; date : 10 Dec 1991 +;;; + +(define-compilation-unit parser + (source-filename "$Y2/parser/") + (require global) + (unit parser-globals + (source-filename "parser-globals.scm")) + (unit parser-macros + (source-filename "parser-macros.scm") + (require parser-globals)) + (unit parser-errors + (source-filename "parser-errors.scm") + (require parser-macros)) + (unit lexer + (source-filename "lexer.scm") + (require parser-macros)) + (unit token + (source-filename "token.scm") + (require parser-macros)) + (unit parser-driver + (source-filename "parser-driver.scm") + (require parser-macros)) + (unit module-parser + (source-filename "module-parser.scm") + (require parser-macros)) + (unit interface-parser + (source-filename "interface-parser.scm") + (require parser-macros)) + (unit decl-parser + (source-filename "decl-parser.scm") + (require parser-macros)) + (unit type-parser + (source-filename "type-parser.scm") + (require parser-macros)) + (unit typedecl-parser + (source-filename "typedecl-parser.scm") + (require parser-macros)) + (unit exp-parser + (source-filename "exp-parser.scm") + (require parser-macros)) + (unit annotation-parser + (source-filename "annotation-parser.scm") + (require parser-macros)) + (unit pattern-parser + (source-filename "pattern-parser.scm") + (require parser-macros)) + (unit parser-debugger + (source-filename "parser-debugger.scm") + (require parser-macros))) + diff --git a/parser/pattern-parser.scm b/parser/pattern-parser.scm new file mode 100644 index 0000000..39a82cc --- /dev/null +++ b/parser/pattern-parser.scm @@ -0,0 +1,220 @@ +;;; File: pattern-parser Author: John + +;;; This parses the pattern syntax except for the parts which need to be +;;; resolved by precedence parsing. + +;;; This parses a list of alternating pats & conops. + +(define (parse-pat) + (trace-parser pat + (let ((res (parse-pat/list))) + (if (null? (cdr res)) + (car res) + (make pp-pat-list (pats res)))))) + +;;; This parses a list of patterns with intervening conops and + patterns + +(define (parse-pat/list) + (token-case + (con (let ((pcon (pcon->ast))) + (setf (pcon-pats pcon) (parse-apat-list)) + (cons pcon (parse-pat/tail)))) + (-n + (advance-token) ; past - + (token-case + (numeric (let ((val (literal->ast))) + (cons (make pp-pat-negated) + (cons (make const-pat (value val)) + (parse-pat/tail))))) + (else + (signal-missing-token "" "negative literal pattern")))) + (var + (let ((var (var->ast))) + (token-case + (+k (cons (make var-pat (var var)) + (parse-+k-pat))) + (@ (let ((pattern (parse-apat))) + (cons (make as-pat (var var) (pattern pattern)) + (parse-pat/tail)))) + (else (cons (make var-pat (var var)) (parse-pat/tail)))))) + (_ + (let ((pat (make wildcard-pat))) + (token-case + (+k (cons pat (parse-+k-pat))) + (else (cons pat (parse-pat/tail)))))) + (else (let ((apat (parse-apat))) + (cons apat (parse-pat/tail)))))) + + +(define (parse-+k-pat) + (advance-token) ; past + + (token-case + (k (let ((val (literal->ast))) + (cons (make pp-pat-plus) + (cons (make const-pat (value val)) + (parse-pat/tail))))) + (else (signal-missing-token "" "successor pattern")))) + +(define (parse-pat/tail) + (token-case + (conop + (let ((con (pconop->ast))) + (cons con (parse-pat/list)))) + (else '()))) + +(define (parse-apat) + (trace-parser apat + (token-case + (var (let ((var (var->ast))) + (token-case + (@ + (let ((pattern (parse-apat))) + (make as-pat (var var) (pattern pattern)))) + (else (make var-pat (var var)))))) + (con (pcon->ast)) + (literal (let ((value (literal->ast))) + (make const-pat (value value)))) + (_ (make wildcard-pat)) + (\( (token-case + (\) (**pcon/def (core-symbol "UnitConstructor") '())) + (else + (let ((pat (parse-pat))) + (token-case + (\, (**pcon/tuple (cons pat (parse-pat-list '\))))) + (\) pat) + (else + (signal-missing-token "`)' or `,'" "pattern"))))))) + (\[ (token-case + (\] (make list-pat (pats '()))) + (else (make list-pat (pats (parse-pat-list '\])))))) + (\~ (let ((pattern (parse-apat))) + (make irr-pat (pattern pattern)))) + (else + (signal-invalid-syntax "an apat"))))) + +(define (parse-pat-list term) ;; , separated + (let ((pat (parse-pat))) + (token-case + (\, (cons pat (parse-pat-list term))) + ((unquote term) (list pat)) + (else + (signal-missing-token + (if (eq? term '\)) "`)'" "`]'") + "pattern"))))) + +(define (parse-apat-list) ;; space separated + (token-case + (apat-start + (let ((pat (parse-apat))) + (cons pat (parse-apat-list)))) + (else + '()))) + +;;; The following routine scans patterns without creating ast structure. +;;; They return #t or #f depending on whether a valid pattern was encountered. +;;; The leave the scanner pointing to the next token after the pattern. + +(define (scan-pat) ; same as parse-pat/list + (and + (token-case + (con (scan-con) + (scan-apat-list)) + (-n (advance-token) + (token-case + (numeric (advance-token) + '#t) + (else '#f))) + (var (and (scan-var) + (token-case + (@ (scan-apat)) + (+k (scan-+k)) + (else '#t)))) + (_ (scan-+k)) + (else (scan-apat))) + (scan-pat/tail))) + +(define (scan-pat/tail) + (token-case + (conop (and (scan-conop) + (scan-pat))) + (else '#t))) + +(define (scan-apat) + (token-case + (var (scan-var) + (token-case + (@ (scan-apat)) + (else '#t))) + (con (scan-con)) + (literal (advance-token) + '#t) + (_ '#t) + (\( (token-case + (\) '#t) + (else + (and (scan-pat) + (token-case + (\, (scan-pat-list '\))) + (\) '#t) + (else '#f)))))) + (\[ (token-case + (\] '#t) + (else (scan-pat-list '\])))) + (\~ (scan-apat)) + (else '#f))) + +(define (scan-pat-list term) + (and (scan-pat) + (token-case + (\, (scan-pat-list term)) + ((unquote term) '#t) + (else '#f)))) + +(define (scan-apat-list) + (token-case + (apat-start + (and (scan-apat) + (scan-apat-list))) + (else '#t))) + +(define (scan-var) + (token-case + (varid '#t) + (\( (token-case + (varsym + (token-case + (\) '#t) + (else '#f))) + (else '#f))) + (else '#f))) + +(define (scan-con) + (token-case + (conid '#t) + (\( (token-case + (consym + (token-case + (\) '#t) + (else '#f))) + (else '#f))) + (else '#f))) + +(define (scan-conop) + (token-case + (consym '#t) + (\` (token-case + (conid + (token-case + (\` '#t) + (else '#f))) + (else '#f))) + (else '#f))) + +(define (scan-+k) + (token-case + (+k (advance-token) ; past the + + (token-case + (integer '#t) + (else '#f))) + (else '#t))) + diff --git a/parser/token.scm b/parser/token.scm new file mode 100644 index 0000000..6ca9981 --- /dev/null +++ b/parser/token.scm @@ -0,0 +1,364 @@ +;;; This file abstracts the representation of tokens. It is used by both +;;; the lexer & parser. This also contains routines for converting +;;; individual tokens to ast structure. Routines used by the +;;; token-case macro in `satisfies' clauses are here too. + +;;; Lexer routines for emitting tokens: + +(define (emit-token type . args) + (cond (*on-new-line?* + (push (list 'line *start-line* *start-col*) *tokens*)) + (*save-col?* + (push (list 'col *start-col*) *tokens*))) + (push (cons type args) *tokens*) + (setf *on-new-line?* '#f) + (setf *save-col?* (memq type '(|where| |of| |let|)))) + +(define (emit-token/string type string-as-list) + (emit-token type (list->string string-as-list))) + +;;; Parser routines: + +;;; These routines take care of the token stream in the parser. They +;;; maintain globals for the current token and its location. + +;;; Globals used: +;;; *token-stream* remaining tokens to be parsed +;;; *token* current token type +;;; *token-args* current token arguments +;;; *layout-stack* columns at which layout is being done +;;; *current-line* current line the scanner is on +;;; *current-col* current col; valid at start of line & after where,let,of +;;; *current-file* + +(define (init-token-stream tokens) + (setf *token-stream* tokens) + (setf *layout-stack* '()) + (advance-token)) + +(define (advance-token) + (cond ((null? *token-stream*) + (setf *token* 'eof)) + (else + (let* ((token (car *token-stream*))) + (setf *token-stream* (cdr *token-stream*)) + (advance-token-1 (car token) (cdr token)))))) + +(define (advance-token-1 type args) + (cond ((eq? type 'file) + (setf *current-file* (car args)) + (advance-token)) + ((eq? type 'col) + (setf *current-col* (car args)) + (advance-token)) + ((eq? type 'line) ;; assume blank lines have been removed + (let ((line (car args)) + (col (cadr args))) + (setf *current-line* line) + (setf *current-col* col) + (setf *token-stream* + (resolve-layout *token-stream* *layout-stack*))) + (advance-token)) + (else + (setf *token* type) + (setf *token-args* args) + type))) + +(define (insert-extra-token tok-type stream) ; used by layout + (cons (list tok-type) stream)) + +;;; This looks for the { to decide of layout will apply. If so, the layout +;;; stack is pushed. The body function, fn, is called with a boolean which +;;; tells it the whether layout rule is in force. + +;;; *** The CMU CL compiler barfs with some kind of internal error +;;; *** on this function. See the revised definition below. + +;(define (start-layout fn) +; (token-case +; (\{ (funcall fn '#f)) +; (else +; (let/cc recovery-fn +; (push (cons *current-col* (lambda () +; (let ((res (funcall fn '#t))) +; (funcall recovery-fn res)))) +; *layout-stack*) +; (funcall fn '#t))))) + +(define (start-layout fn) + (token-case + (\{ (funcall fn '#f)) + (else + (let/cc recovery-fn + (start-layout-1 fn recovery-fn))))) + +(define (start-layout-1 fn recovery-fn) + (push (cons *current-col* + (lambda () + (let ((res (funcall fn '#t))) + (funcall recovery-fn res)))) + *layout-stack*) + (funcall fn '#t)) + +(define (layout-col x) + (car x)) + +(define (layout-recovery-fn x) + (cdr x)) + +(define (close-layout in-layout?) + (cond (in-layout? + (setf *layout-stack* (cdr *layout-stack*)) + (token-case + ($\} '()) ; the advance-token routine may have inserted this + (else '()))) + (else + (token-case + (\} '()) + (else + (signal-missing-brace)))))) + +(define (signal-missing-brace) + (parser-error 'missing-brace + "Missing `}'.")) + +(define (resolve-layout stream layout-stack) + (if (null? layout-stack) + stream + (let ((col (layout-col (car layout-stack)))) + (declare (type fixnum col)) + (cond ((= (the fixnum *current-col*) col) + (insert-extra-token '\; stream)) + ((< (the fixnum *current-col*) col) + (insert-extra-token + '$\} (resolve-layout stream (cdr layout-stack)))) + (else + stream) + )))) + + +;;; The following routines are used for backtracking. This is a bit of +;;; a hack at the moment. + +(define (save-scanner-state) + (vector *token* *token-args* *token-stream* *layout-stack* *current-line* + *current-col*)) + +(define (restore-excursion state) + (setf *token* (vector-ref state 0)) + (setf *token-args* (vector-ref state 1)) + (setf *token-stream* (vector-ref state 2)) + (setf *layout-stack* (vector-ref state 3)) + (setf *current-line* (vector-ref state 4)) + (setf *current-col* (vector-ref state 5))) + +(define (eq-token? type) + (eq? type *token*)) + +(define (eq-token-arg? str) + (string=? str (car *token-args*))) + +;;; lookahead into the token stream + +(define (peek-1-type) + (peek-toks 0 *token-stream*)) + +(define (peek-2-type) + (peek-toks 1 *token-stream*)) + +;;; This is a Q&D way of looking ahead. It does not expand the layout +;;; as it goes so there may be missing } and ;. This should not matter +;;; in the places where this is used since these would be invalid anyway. +;;; To be safe, token types are rechecked while advancing to verify the +;;; lookahead. + +(define (peek-toks n toks) + (declare (type fixnum n)) + (cond ((null? toks) + 'eof) + ((memq (caar toks) '(col line)) + (peek-toks n (cdr toks))) + ((eqv? n 0) + (caar toks)) + (else (peek-toks (1- n) (cdr toks))))) + +;; These routines handle the `satisfies' clauses used in token-case. + +(define (at-varsym/+?) + (and (eq? *token* 'varsym) + (string=? (car *token-args*) "+"))) + +(define (at-varsym/-?) + (and (eq? *token* 'varsym) + (string=? (car *token-args*) "-"))) + +(define (at-varsym/paren?) + (and (eq? *token* '\() + (eq? (peek-1-type) 'varsym) + (eq? (peek-2-type) '\)))) + +(define (at-consym/paren?) + (and (eq? *token* '\() + (eq? (peek-1-type) 'consym) + (eq? (peek-2-type) '\)))) + +(define (at-varid/quoted?) + (and (eq? *token* '\`) + (eq? (peek-1-type) 'varid))) + +(define (at-conid/quoted?) + (and (eq? *token* '\`) + (eq? (peek-1-type) 'conid))) + +(define (at-+k?) + (and (at-varsym/+?) + (eq? (peek-1-type) 'integer))) + +(define (at--n?) + (and (at-varsym/-?) + (memq (peek-1-type) '(integer float)))) + +;;; The following routines convert the simplest tokens to AST structure. + +(define-local-syntax (return+advance x) + `(let ((x ,x)) + (advance-token) + x)) + +(define (token->symbol) + (return+advance + (string->symbol (car *token-args*)))) + +(define (token->symbol/con) ; for conid, aconid + (return+advance + (string->symbol (add-con-prefix (car *token-args*))))) + +(define (var->symbol) + (token-case + (\( (token-case + (varsym? + (let ((res (token->symbol))) + (token-case + (\) res) + (else (signal-missing-token "`)'" "var"))))) + (else (signal-missing-token "" "var")))) + (varid? (token->symbol)))) + +(define (var->ast) + (let ((vname (var->symbol))) + (make var-ref (name vname) (infix? '#f) (var *undefined-def*)))) + +(define (var->entity) + (let ((vname (var->symbol))) + (make entity-var (name vname)))) + +(define (con->symbol) + (token-case + (\( (token-case + (consym? + (let ((res (token->symbol/con))) + (token-case + (\) res) + (else (signal-missing-token "`)'" "con"))))) + (else (signal-missing-token "" "con")))) + (conid? (token->symbol/con)))) + +(define (varop->symbol) + (token-case + (\` (token-case + (varid? + (let ((res (token->symbol))) + (token-case + (\` res) + (else (signal-missing-token "``'" "varop"))))) + (else (signal-missing-token "" "varop")))) + (varsym? (token->symbol)))) + +(define (varop->ast) + (let ((varop-name (varop->symbol))) + (make var-ref (name varop-name) (infix? '#t) (var *undefined-def*)))) + +(define (conop->symbol) + (token-case + (\` (token-case + (conid? + (let ((res (token->symbol/con))) + (token-case + (\` res) + (else (signal-missing-token "``'" "conop"))))) + (else (signal-missing-token "" "conop")))) + (consym? (token->symbol/con)))) + +(define (conop->ast) + (let ((conop-name (conop->symbol))) + (make con-ref (name conop-name) (infix? '#t) (con *undefined-def*)))) + +(define (op->symbol) + (token-case + (\` (token-case + (conid? + (let ((res (token->symbol/con))) + (token-case + (\` res) + (else (signal-missing-token "``'" "op"))))) + (varid? + (let ((res (token->symbol))) + (token-case + (\` res) + (else (signal-missing-token "``'" "op"))))) + (else (signal-missing-token " or " "op")))) + (consym? (token->symbol/con)) + (varsym? (token->symbol)))) + +(define (con->ast) ; for conid, aconid + (let ((name (con->symbol))) + (make con-ref (name name) (con *undefined-def*) (infix? '#f)))) + +(define (pcon->ast) ; for aconid, conid + (let ((name (con->symbol))) + (make pcon (name name) (con *undefined-def*) (pats '()) (infix? '#f)))) + +(define (pconop->ast) ; for aconop, conop + (let ((name (conop->symbol))) + (make pcon (name name) (con *undefined-def*) (pats '()) (infix? '#t)))) + +(define (tycon->ast) ; for aconid + (let ((name (token->symbol))) + (make tycon (name name) (def *undefined-def*) (args '())))) + +(define (class->ast) ; for aconid + (let ((name (token->symbol))) + (make class-ref (name name) (class *undefined-def*)))) + +(define (tyvar->ast) ; for avarid + (let ((name (token->symbol))) + (make tyvar (name name)))) + +(define (token->integer) ; for integer + (return+advance + (car *token-args*))) + +(define (integer->ast) ; for integer + (return+advance + (make integer-const (value (car *token-args*))))) + +(define (float->ast) + (return+advance + (make float-const (numerator (car *token-args*)) + (denominator (cadr *token-args*)) + (exponent (caddr *token-args*))))) + +(define (string->ast) + (return+advance + (make string-const (value (car *token-args*))))) + +(define (char->ast) + (return+advance + (make char-const (value (car *token-args*))))) + +(define (literal->ast) + (token-case + ((no-advance integer) (integer->ast)) + ((no-advance float) (float->ast)) + ((no-advance string) (string->ast)) + ((no-advance char) (char->ast)))) diff --git a/parser/type-parser.scm b/parser/type-parser.scm new file mode 100644 index 0000000..79c5dbf --- /dev/null +++ b/parser/type-parser.scm @@ -0,0 +1,116 @@ +;;; File: type-parser Author: John + +(define (parse-type) + (let ((type (parse-btype))) + (token-case + (-> + (**tycon/def (core-symbol "Arrow") (list type (parse-type)))) + (else type)))) + +(define (parse-btype) + (token-case + (tycon (let* ((tycon (tycon->ast)) + (tycon-args (parse-atype-list))) + (setf (tycon-args tycon) tycon-args) + tycon)) + (else + (parse-atype)))) + +(define (parse-atype-list) + (token-case + (atype-start + (let ((atype (parse-atype))) + (cons atype (parse-atype-list)))) + (else '()))) + +(define (parse-atype) + (token-case + (tyvar (tyvar->ast)) + (tycon (tycon->ast)) + (\( (token-case + (\) (**tycon/def (core-symbol "UnitType") '())) + (else + (let ((type (parse-type))) + (token-case + (\) type) + (\, (let ((types (cons type (parse-type-list)))) + (**tycon/def (tuple-tycon (length types)) types))) + (else + (signal-missing-token "`)' or `,'" "type expression"))))))) + (\[ (let ((type (parse-type))) + (require-token \] (signal-missing-token "`]'" "type expression")) + (**tycon/def (core-symbol "List") (list type)))) + (else + (signal-invalid-syntax "an atype")))) + +(define (parse-type-list) + (let ((type (parse-type))) + (token-case (\, (cons type (parse-type-list))) + (\) (list type)) + (else (signal-missing-token "`)' or `,'" "type expression"))))) + +;;; This is used to determine whether a type is preceded by a context + +(define (has-optional-context?) + (let* ((saved-excursion (save-scanner-state)) + (res (token-case + (conid + (token-case + (varid (eq-token? '=>)) + (else '#f))) + (\( (scan-context)) + (else '#f)))) + (restore-excursion saved-excursion) + res)) + +(define (scan-context) + (token-case + (conid + (token-case + (varid + (token-case + (\) (eq-token? '=>)) + (\, (scan-context)) + (else '#f))) + (else '#f))) + (else '#f))) + +(define (parse-context) + (let ((contexts (token-case + (tycon + (list (parse-single-context))) + (\( (parse-contexts)) + (else + (signal-invalid-syntax "a context"))))) + (require-token => (signal-missing-token "`=>'" "context")) + contexts)) + +(define (parse-single-context) + (let ((class (class->ast))) + (token-case + (tyvar + (let ((tyvar (token->symbol))) + (make context (class class) (tyvar tyvar)))) + (else (signal-missing-token "" "class assertion"))))) + +(define (parse-contexts) + (token-case + (tycon (let ((context (parse-single-context))) + (token-case + (\, (cons context (parse-contexts))) + (\) (list context)) + (else (signal-missing-token "`)' or `,'" "context"))))) + (else (signal-missing-token "" "class assertion")))) + +(define (parse-optional-context) + (if (has-optional-context?) + (parse-context) + '())) + +(define (parse-signature) + (let* ((contexts (parse-optional-context)) + (type (parse-type))) + (make signature (context contexts) (type type)))) + + + \ No newline at end of file diff --git a/parser/typedecl-parser.scm b/parser/typedecl-parser.scm new file mode 100644 index 0000000..4995dc7 --- /dev/null +++ b/parser/typedecl-parser.scm @@ -0,0 +1,163 @@ +;;; File: parser/typedecl-parser Author: John + +(define (parse-type-decl interface?) + (save-parser-context + (let* ((sig (parse-signature)) + (contexts (signature-context sig)) + (simple (signature-type sig)) + (deriving '()) + (constrs '())) + ;; #t = builtins ([] (,,) ->) not allowed + (check-simple simple '#t "type declaration") + (let ((annotations (parse-constr-annotations))) + (token-case + (= (setf constrs (parse-constrs)) + (token-case + (|deriving| + (setf deriving + (token-case + (\( + (token-case + (\) '()) + (else (parse-class-list)))) + (tycon (list (class->ast))) + (else (signal-invalid-syntax "a deriving clause"))))))) + (else + (when (not interface?) + (signal-missing-constructors)))) + (make data-decl (context contexts) (simple simple) + (constrs constrs) (deriving deriving) + (annotations annotations)))))) + +(define (signal-missing-constructors) + (parser-error 'missing-constructors + "Data type definition requires constructors")) + +(define (check-simple simple fresh? where) + (when (not (tycon? simple)) + (signal-not-simple where)) + (when (and fresh? (not (eq? (tycon-def simple) *undefined-def*))) + (signal-not-simple where)) + (let ((tyvars (map (lambda (arg) + (when (not (tyvar? arg)) + (signal-not-simple where)) + (tyvar-name arg)) + (tycon-args simple)))) + (when (not (null? (find-duplicates tyvars))) + (signal-unique-tyvars-required)))) + +(define (signal-unique-tyvars-required) + (parser-error 'unique-tyvars-required + "Duplicate type variables appear in simple.")) + +(define (signal-not-simple where) + (parser-error 'not-simple "Simple type required in ~a." where)) + +(define (parse-constrs) + (let ((constr (parse-constr))) + (token-case + (\| (cons constr (parse-constrs))) + (else (list constr))))) + +(define (parse-constr) + (save-parser-context + (let ((saved-excursion (save-scanner-state))) + (token-case + (consym/paren + (parse-prefix-constr)) + (else + (let ((type1 (parse-btype)) + (anns (parse-constr-annotations))) + (token-case + (conop + (parse-infix-constr (tuple type1 anns))) + (else + (restore-excursion saved-excursion) + (parse-prefix-constr))))))))) + +(define (parse-prefix-constr) + (token-case + (con + (let* ((con (con->ast)) + (types (parse-constr-type-list))) + (make constr (constructor con) (types types)))) + (else + (signal-missing-token "" "constrs list")))) + +(define (parse-constr-type-list) + (token-case + (atype-start + (let* ((atype (parse-atype)) + (anns (parse-constr-annotations))) + (cons (tuple atype anns) + (parse-constr-type-list)))) + (else '()))) + +(define (parse-infix-constr t+a1) + (let* ((con (conop->ast)) + (type2 (parse-btype)) + (anns (parse-constr-annotations))) + (make constr (constructor con) (types (list t+a1 (tuple type2 anns)))))) + +(define (parse-class-list) + (token-case + (tycon (let ((class (class->ast))) + (token-case + (\, (cons class (parse-class-list))) + (\) (list class)) + (else (signal-missing-token "`)' or `,'" "deriving clause"))))) + (else (signal-missing-token "" "deriving clause")))) + +(define (parse-constr-annotations) + (token-case + ((begin-annotation no-advance) + (let ((annotations (parse-annotations))) + (append annotations (parse-constr-annotations)))) + (else '()))) + +(define (parse-synonym-decl) + (save-parser-context + (let* ((sig (parse-signature)) + (contexts (signature-context sig)) + (simple (signature-type sig))) + (check-simple simple '#t "type synonym declaration") + (when (not (null? contexts)) + (signal-no-context-in-synonym)) + (require-token = (signal-missing-token "`='" "type synonym declaration")) + (let ((body (parse-type))) + (make synonym-decl (simple simple) (body body)))))) + +(define (signal-no-context-in-synonym) + (parser-error 'no-context-in-synonym + "Context is not permitted in type synonym declaration.")) + +(define (parse-class-decl) + (save-parser-context + (let ((supers (parse-optional-context))) + (token-case + (tycon + (let ((class (class->ast))) + (token-case + (tyvar + (let* ((class-var (token->symbol)) + (decls (parse-where-decls))) + (make class-decl (class class) (super-classes supers) + (class-var class-var) (decls decls)))) + (else + (signal-missing-token "" "class declaration"))))) + (else (signal-missing-token "" "class declaration")))))) + +(define (parse-instance-decl interface?) + (save-parser-context + (let ((contexts (parse-optional-context)) + (decls '())) + (token-case + (tycon + (let* ((class (class->ast)) + (simple (parse-type))) + (when (not interface?) + (setf decls (parse-where-decls))) + (check-simple simple '#f "instance declaration") + (make instance-decl (context contexts) (class class) + (simple simple) (decls decls)))) + (else (signal-missing-token "" "instance declaration")))))) diff --git a/prec/README b/prec/README new file mode 100644 index 0000000..ea455c4 --- /dev/null +++ b/prec/README @@ -0,0 +1,2 @@ +This directory contains the code walker for the scoping and +precedence parsing phase of the compiler. diff --git a/prec/prec-parse.scm b/prec/prec-parse.scm new file mode 100644 index 0000000..9df06a6 --- /dev/null +++ b/prec/prec-parse.scm @@ -0,0 +1,253 @@ +;;; prec-parse.scm -- do precedence parsing of expressions and patterns +;;; +;;; author : John & Sandra +;;; date : 04 Feb 1992 +;;; +;;; + + +;;; ================================================================== +;;; Handling for pp-exp-list +;;; ================================================================== + +;;; This function is called during the scope phase after all of the +;;; exps in a pp-exp-list have already been walked. Basically, the +;;; purpose is to turn the original pp-exp-list into something else. +;;; Look for the section cases first and treat them specially. + +;;; Sections are handled by inserting a magic cookie (void) into the +;;; list where the `missing' operand of the section would be and then +;;; making sure the cookie stays at the top. + +;;; Unary minus needs checking to avoid things like a*-a. + +(define (massage-pp-exp-list exps) + (let* ((first-term (car exps)) + (last-term (car (last exps))) + (type (cond ((infix-var-or-con? first-term) 'section-l) + ((infix-var-or-con? last-term) 'section-r) + (else 'exp))) + (exps1 (cond ((eq? type 'section-l) + (cons (make void) exps)) + ((eq? type 'section-r) + (append exps (list (make void)))) + (else exps))) + (parsed-exp (parse-pp-list '#f exps1))) + (if (eq? type 'exp) + parsed-exp + (if (or (not (app? parsed-exp)) + (not (app? (app-fn parsed-exp)))) + (begin + (signal-section-precedence-conflict + (if (eq? type 'section-l) first-term last-term)) + (make void)) + (let ((rhs (app-arg parsed-exp)) + (op (app-fn (app-fn parsed-exp))) + (lhs (app-arg (app-fn parsed-exp)))) + (if (eq? type 'section-l) + (if (void? lhs) + (make section-l (op op) (exp rhs)) + (begin + (signal-section-precedence-conflict first-term) + (make void))) + (if (void? rhs) + (make section-r (op op) (exp lhs)) + (begin + (signal-section-precedence-conflict last-term) + (make void))))))))) + + +;;; ================================================================== +;;; Handling for pp-pat-list +;;; ================================================================== + +;;; In this case, we have to do an explicit walk of the pattern looking +;;; at all of its subpatterns. +;;; ** This is a crock - the scope walker needs fixing. + +(define (massage-pattern pat) + (cond ((is-type? 'as-pat pat) + (setf (as-pat-pattern pat) (massage-pattern (as-pat-pattern pat))) + pat) + ((is-type? 'irr-pat pat) + (setf (irr-pat-pattern pat) (massage-pattern (irr-pat-pattern pat))) + pat) + ((is-type? 'plus-pat pat) + (setf (plus-pat-pattern pat) (massage-pattern (plus-pat-pattern pat))) + pat) + ((is-type? 'pcon pat) + (when (eq? (pcon-con pat) *undefined-def*) + (setf (pcon-con pat) (lookup-toplevel-name (pcon-name pat)))) + (setf (pcon-pats pat) (massage-pattern-list (pcon-pats pat))) + pat) + ((is-type? 'list-pat pat) + (setf (list-pat-pats pat) (massage-pattern-list (list-pat-pats pat))) + pat) + ((is-type? 'pp-pat-list pat) + (parse-pp-list '#t (massage-pattern-list (pp-pat-list-pats pat)))) + (else + pat))) + +(define (massage-pattern-list pats) + (map (function massage-pattern) pats)) + + +;;; ================================================================== +;;; Shared support +;;; ================================================================== + +;;; This is the main routine. + +(define (parse-pp-list pattern? l) + (mlet (((stack terms) (push-pp-stack '() l))) + (pp-parse-next-term pattern? stack terms))) + +(define (pp-parse-next-term pattern? stack terms) + (if (null? terms) + (reduce-complete-stack pattern? stack) + (let ((stack (reduce-stronger-ops pattern? stack (car terms)))) + (mlet (((stack terms) + (push-pp-stack (cons (car terms) stack) (cdr terms)))) + (pp-parse-next-term pattern? stack terms))))) + +(define (reduce-complete-stack pattern? stack) + (if (pp-stack-op-empty? stack) + (car stack) + (reduce-complete-stack pattern? (reduce-pp-stack pattern? stack)))) + +(define (reduce-pp-stack pattern? stack) + (let ((term (car stack)) + (op (cadr stack))) + (if pattern? + (cond ((pp-pat-plus? op) + (let ((lhs (caddr stack))) + (cond ((or (not (const-pat? term)) + (and (not (var-pat? lhs)) + (not (wildcard-pat? lhs)))) + (signal-plus-precedence-conflict term) + (cddr stack)) + (else + (cons (make plus-pat (pattern lhs) + (k (integer-const-value + (const-pat-value term)))) + (cdddr stack)))))) + ((pp-pat-negated? op) + (cond ((const-pat? term) + (let ((v (const-pat-value term))) + (if (integer-const? v) + (setf (integer-const-value v) + (- (integer-const-value v))) + (setf (float-const-numerator v) + (- (float-const-numerator v))))) + (cons term (cddr stack))) + (else + (signal-minus-precedence-conflict term) + (cons term (cddr stack))))) + (else + (setf (pcon-pats op) (list (caddr stack) term)) + (cons op (cdddr stack)))) + (cond ((negate? op) + (cons (**app (**var/def (core-symbol "negate")) term) + (cddr stack))) + (else + (cons (**app op (caddr stack) term) (cdddr stack))))))) + +(define (pp-stack-op-empty? stack) + (null? (cdr stack))) + +(define (top-stack-op stack) + (cadr stack)) + +(define (push-pp-stack stack terms) + (let ((term (car terms))) + (if (or (negate? term) (pp-pat-negated? term)) + (begin + (when (and stack (stronger-op? (car stack) term)) + (unary-minus-prec-conflict term)) + (push-pp-stack (cons term stack) (cdr terms))) + (values (cons term stack) (cdr terms))))) + +(define (reduce-stronger-ops pattern? stack op) + (cond ((pp-stack-op-empty? stack) stack) + ((stronger-op? (top-stack-op stack) op) + (reduce-stronger-ops pattern? (reduce-pp-stack pattern? stack) op)) + (else stack))) + +(define (stronger-op? op1 op2) + (let ((fixity1 (get-op-fixity op1)) + (fixity2 (get-op-fixity op2))) + (cond ((> (fixity-precedence fixity1) (fixity-precedence fixity2)) + '#t) + ((< (fixity-precedence fixity1) (fixity-precedence fixity2)) + '#f) + (else + (let ((a1 (fixity-associativity fixity1)) + (a2 (fixity-associativity fixity2))) + (if (eq? a1 a2) + (cond ((eq? a1 'l) + '#t) + ((eq? a1 'r) + '#f) + (else + (signal-precedence-conflict op1 op2) + '#t)) + (begin + (signal-precedence-conflict op1 op2) + '#t)))) + ))) + +(define (get-op-fixity op) + (cond ((var-ref? op) + (pp-get-var-fixity (var-ref-var op))) + ((con-ref? op) + (pp-get-con-fixity (con-ref-con op))) + ((pcon? op) + (pp-get-con-fixity (pcon-con op))) + ((or (negate? op) (pp-pat-negated? op)) + (pp-get-var-fixity (core-symbol "-"))) + ((pp-pat-plus? op) + (pp-get-var-fixity (core-symbol "+"))) + (else + (error "Bad op ~s in pp-parse." op)))) + +(define (pp-get-var-fixity def) + (if (eq? (var-fixity def) '#f) + default-fixity + (var-fixity def))) + +(define (pp-get-con-fixity def) + (if (eq? (con-fixity def) '#f) + default-fixity + (con-fixity def))) + +;;; Error handlers + +(define (signal-section-precedence-conflict op) + (phase-error 'section-precedence-conflict + "Operators in section body have lower precedence than section operator ~A." + op)) + +(define (signal-precedence-conflict op1 op2) + (phase-error 'precedence-conflict + "The operators ~s and ~s appear consecutively, but they have the same~%~ + precedence and are not either both left or both right associative.~% + You must add parentheses to avoid a precedence conflict." + op1 op2)) + +(define (signal-plus-precedence-conflict term) + (phase-error 'plus-precedence-conflict + "You need to put parentheses around the plus-pattern ~a~%~ + to avoid a precedence conflict." + term)) + +(define (signal-minus-precedence-conflict arg) + (phase-error 'minus-precedence-conflict + "You need to put parentheses around the negative literal ~a~%~ + to avoid a precedence conflict." + arg)) + +(define (unary-minus-prec-conflict arg) + (recoverable-error 'minus-precedence-conflict + "Operator ~A too strong for unary minus - add parens please!~%" + arg)) + diff --git a/prec/prec.scm b/prec/prec.scm new file mode 100644 index 0000000..a7948f2 --- /dev/null +++ b/prec/prec.scm @@ -0,0 +1,18 @@ +;;; prec.scm -- module definition for scoping/precedence-parsing phase +;;; +;;; author : Sandra Loosemore +;;; date : 13 Feb 1992 +;;; + + +(define-compilation-unit prec + (source-filename "$Y2/prec/") + (require ast haskell-utils) + (unit scope + (source-filename "scope.scm")) + (unit prec-parse + (source-filename "prec-parse.scm"))) + + + + diff --git a/prec/scope.scm b/prec/scope.scm new file mode 100644 index 0000000..e57ed64 --- /dev/null +++ b/prec/scope.scm @@ -0,0 +1,367 @@ +;;; scope.scm -- variable scoping and precedence parsing phase +;;; +;;; author : John & Sandra +;;; date : 11 Feb 1992 +;;; +;;; + + +;;;=================================================================== +;;; Basic support +;;;=================================================================== + +(define (scope-modules modules) + (walk-modules modules + (lambda () + (setf (module-decls *module*) (scope-ast-decls (module-decls *module*))) + (dolist (a (module-annotations *module*)) +;;; This is currently bogus since it assumes only vars are annotated. + (when (annotation-decl? a) + (dolist (n (annotation-decl-names a)) + (let ((v (table-entry *symbol-table* n))) + (when (or (eq? v '#f) (not (var? v))) + (fatal-error 'unknown-annotation-name + "~A: not a var in annotation decl~%" n)) + (when (not (eq? (def-module v) *module-name*)) + (fatal-error 'non-local-name-in-annotation + "~A: not a local var in annotation decl~%" n)) + (setf (var-annotations v) + (append (var-annotations v) + (annotation-decl-annotations a)))))))))) + +;;; Define the basic walker and some helper functions. + +(define-walker scope ast-td-scope-walker) + +(define (scope-ast-1 x env) +; (call-walker scope x env)) + (remember-context x + (call-walker scope x env))) + + +(define (scope-ast/list l env) + (scope-ast/list-aux l env) + l) + +(define (scope-ast/list-aux l env) + (when (not (null? l)) + (setf (car l) (scope-ast-1 (car l) env)) + (scope-ast/list-aux (cdr l) env))) + +;;; This filters out signdecls from decl lists. These declarations are moved +;;; into the var definitions. + +(define (scope-ast/decl-list l env) + (if (null? l) + '() + (let ((x (scope-ast-1 (car l) env)) + (rest (scope-ast/decl-list (cdr l) env))) + (if (or (annotation-decls? x) + (and (signdecl? x) + (not (eq? (module-type *module*) 'interface)))) + rest + (begin + (setf (car l) x) + (setf (cdr l) rest) + l))))) + +;;; This is the main entry point. It is called by the driver +;;; on each top-level decl in the module. + +(define (scope-ast-decls x) + (let ((result (scope-ast/decl-list x '()))) +; (pprint result) ;*** debug + result)) + + +;;; All top-level names are entered in the *symbol-table* hash table. +;;; This is done by the import/export phase of the compiler before +;;; we get here. +;;; The env is a list of a-lists that associates locally-defined names with +;;; their definitions. Each nested a-list corresponds to a "level" or +;;; scope. +;;; *** If many variables are being added in each scope, it might be +;;; *** better to use a table instead of an alist to represent each contour. + +(define (lookup-name name env) + (if (null? env) + (lookup-toplevel-name name) + (let ((info (assq name (car env)))) + (if info + (cdr info) + (lookup-name name (cdr env)))))) + + +;;; Some kinds of names (e.g. type definitions) appear only at top-level, +;;; so use this to look for them directly. + +(define (lookup-toplevel-name name) + (or (resolve-toplevel-name name) + (begin + (signal-undefined-symbol name) + *undefined-def*))) + + +;;; Some kinds of lookups (e.g., matching a signature declaration) +;;; require that the name be defined in the current scope and not +;;; an outer one. Use this function. + +(define (lookup-local-name name env) + (if (null? env) + (lookup-toplevel-name name) + (let ((info (assq name (car env)))) + (if info + (cdr info) + (begin + (signal-undefined-local-symbol name) + *undefined-def*))))) + + +;;; Add local declarations to the environment, returning a new env. +;;; Do not actually walk the local declarations here. + +(define *scope-info* '()) + +(define (add-local-declarations decls env) + (if (null? decls) + env + (let ((contour '())) + (dolist (d decls) + (if (is-type? 'valdef d) + (setf contour + (add-bindings (collect-pattern-vars (valdef-lhs d)) + contour)))) + (cons contour env)))) + + +;;; Similar, but for adding lambda and function argument bindings to the +;;; environment. + +(define (add-pattern-variables patterns env) + (if (null? patterns) + env + (let ((contour '())) + (dolist (p patterns) + (setf contour (add-bindings (collect-pattern-vars p) contour))) + (cons contour env)))) + + +;;; Given a list of var-refs, create defs for them and add them to +;;; the local environment. +;;; Also check to see that there are no duplicates. + +(define (add-bindings var-refs contour) + (dolist (v var-refs) + (when (eq? (var-ref-var v) *undefined-def*) + (let* ((name (var-ref-name v)) + (def (create-local-definition name))) + (setf (var-ref-var v) def) + (if (assq name contour) + (signal-multiple-bindings name) + (push (cons name def) contour))))) + contour) + + +;;; Error signalling utilities. + +(define (signal-undefined-local-symbol name) + (phase-error 'undefined-local-symbol + "The name ~a has no definition in the current scope." + name)) + +(define (signal-multiple-signatures name) + (phase-error 'multiple-signatures + "There are multiple signatures for the name ~a." + name)) + +(define (signal-multiple-bindings name) + (phase-error 'multiple-bindings + "The name ~a appears more than once in a function or pattern binding." + name)) + + + +;;;=================================================================== +;;; Default traversal methods +;;;=================================================================== + + +(define-local-syntax (make-scope-code slot type) + (let ((stype (sd-type slot)) + (sname (sd-name slot))) + (cond ((and (symbol? stype) + (or (eq? stype 'exp) + (subtype? stype 'exp))) + `(setf (struct-slot ',type ',sname object) + (scope-ast-1 (struct-slot ',type ',sname object) env))) + ((and (pair? stype) + (eq? (car stype) 'list) + (symbol? (cadr stype)) + (or (eq? (cadr stype) 'exp) + (subtype? (cadr stype) 'exp))) + `(setf (struct-slot ',type ',sname object) + (scope-ast/list (struct-slot ',type ',sname object) env))) + (else +; (format '#t "Scope: skipping slot ~A in ~A~%" +; (sd-name slot) +; type) + '#f)))) + + +(define-modify-walker-methods scope + (guarded-rhs ; exp slots + if ; exp slots + app ; exp slots + integer-const float-const char-const string-const ; no slots + list-exp ; (list exp) slot + sequence sequence-to sequence-then sequence-then-to ; exp slots + section-l section-r ; exp slots + omitted-guard overloaded-var-ref ; no slots + negate ; no slots + sel + prim-definition + con-number cast + ) + (object env) + make-scope-code) + + +;;;=================================================================== +;;; valdef-structs +;;;=================================================================== + + +;;; Signature declarations must appear at the same level as the names +;;; they apply to. There must not be more than one signature declaration +;;; applying to a given name. + +(define-walker-method scope signdecl (object env) + (let ((signature (signdecl-signature object))) + (resolve-signature signature) + (let ((gtype (ast->gtype (signature-context signature) + (signature-type signature)))) + (dolist (v (signdecl-vars object)) + (when (eq? (var-ref-var v) *undefined-def*) + (setf (var-ref-var v) + (lookup-local-name (var-ref-name v) env))) + (let ((def (var-ref-var v))) + (when (not (eq? def *undefined-def*)) + ;; The lookup-local-name may fail if there is a program error. + ;; In that case, skip this. + (if (var-signature def) + (signal-multiple-signatures (var-ref-name v)) + (setf (var-signature def) gtype)))))) + object)) + +;;; This attaches annotations to locally defined vars in the same +;;; manner as signdecl annotations. + +(define-walker-method scope annotation-decls (object env) + (let ((anns (annotation-decls-annotations object))) + (dolist (a anns) + (cond ((annotation-value? a) + (recoverable-error 'unknown-annotation "Unknown annotation: ~A" a)) + ((annotation-decl? a) + (dolist (v (annotation-decl-names a)) + (let ((name (lookup-local-name v env))) + (when (not (eq? name *undefined-def*)) + (setf (var-annotations name) + (append (var-annotations name) + (annotation-decl-annotations a)))))))))) + object) + +(define-walker-method scope exp-sign (object env) + (resolve-signature (exp-sign-signature object)) + (setf (exp-sign-exp object) (scope-ast-1 (exp-sign-exp object) env)) + object) + +;;; By the time we get to walking a valdef, all the variables it +;;; declares have been entered into the environment. All we need to +;;; do is massage the pattern and recursively walk the definitions. + +(define-walker-method scope valdef (object env) + (setf (valdef-module object) *module-name*) + (setf (valdef-lhs object) (massage-pattern (valdef-lhs object))) + (setf (valdef-definitions object) + (scope-ast/list (valdef-definitions object) env)) + object) + + +;;; For a single-fun-def, do the where-decls first, and then walk the +;;; rhs in an env that includes both the where-decls and the args. + +(define-walker-method scope single-fun-def (object env) + (setf env (add-pattern-variables (single-fun-def-args object) env)) + (setf env (add-local-declarations (single-fun-def-where-decls object) env)) + (setf (single-fun-def-where-decls object) + (scope-ast/decl-list (single-fun-def-where-decls object) env)) + (setf (single-fun-def-args object) + (massage-pattern-list (single-fun-def-args object))) + (setf (single-fun-def-rhs-list object) + (scope-ast/list (single-fun-def-rhs-list object) env)) + object) + + +;;;=================================================================== +;;; exp-structs +;;;=================================================================== + +(define-walker-method scope lambda (object env) + (setf env (add-pattern-variables (lambda-pats object) env)) + (setf (lambda-pats object) (massage-pattern-list (lambda-pats object))) + (setf (lambda-body object) (scope-ast-1 (lambda-body object) env)) + object) + +(define-walker-method scope let (object env) + (setf env (add-local-declarations (let-decls object) env)) + (setf (let-decls object) (scope-ast/decl-list (let-decls object) env)) + (setf (let-body object) (scope-ast-1 (let-body object) env)) + object) + + +;;; Case alts are treated very much like single-fun-defs. + +(define-walker-method scope case (object env) + (setf (case-exp object) (scope-ast-1 (case-exp object) env)) + (dolist (a (case-alts object)) + (let ((env (add-pattern-variables (list (alt-pat a)) env))) + (setf env (add-local-declarations (alt-where-decls a) env)) + (setf (alt-where-decls a) + (scope-ast/decl-list (alt-where-decls a) env)) + (setf (alt-pat a) (massage-pattern (alt-pat a))) + (setf (alt-rhs-list a) + (scope-ast/list (alt-rhs-list a) env)))) + object) + + +(define-walker-method scope var-ref (object env) + (when (eq? (var-ref-var object) *undefined-def*) + (setf (var-ref-var object) + (lookup-name (var-ref-name object) env))) + object) + +(define-walker-method scope con-ref (object env) + (declare (ignore env)) + (when (eq? (con-ref-con object) *undefined-def*) + (setf (con-ref-con object) + (lookup-toplevel-name (con-ref-name object)))) + object) + +(define-walker-method scope list-comp (object env) + (dolist (q (list-comp-quals object)) + (cond ((is-type? 'qual-generator q) + (setf (qual-generator-exp q) + (scope-ast-1 (qual-generator-exp q) env)) + (setf env + (add-pattern-variables (list (qual-generator-pat q)) env)) + (setf (qual-generator-pat q) + (massage-pattern (qual-generator-pat q)))) + ((is-type? 'qual-filter q) + (setf (qual-filter-exp q) + (scope-ast-1 (qual-filter-exp q) env))))) + (setf (list-comp-exp object) (scope-ast-1 (list-comp-exp object) env)) + object) + +(define-walker-method scope pp-exp-list (object env) + (massage-pp-exp-list (scope-ast/list (pp-exp-list-exps object) env))) + diff --git a/printers/README b/printers/README new file mode 100644 index 0000000..36530ed --- /dev/null +++ b/printers/README @@ -0,0 +1,19 @@ +This directory contains print routines for the structures defined in +the ast/ directory. + +The global *print-structure* controls printing of objects in the +structure system. Values are: + haskell -- Prints haskell format expressions from ast + struct -- Prints the raw structs (with circularity check) + top -- Prints top level only of the struct + +The file defs.scm has the basic hooks to the printer mechanism. The +idea is that when *print-structure* is 'haskell, the print function stored +in the type descriptor will get used. If there isn't a print function, +or if *print-structure* is false, then the thing will print out in +some generic way that's good for debugging purposes. + +The macro define-printer is used to associate a print function with a +structure type. Since these can be defined on the fly, the print +dispatching routine has to look up the inheritance chain of type +descriptors looking for the first inherited type that has a printer. diff --git a/printers/print-exps.scm b/printers/print-exps.scm new file mode 100644 index 0000000..2a9d89b --- /dev/null +++ b/printers/print-exps.scm @@ -0,0 +1,410 @@ +;;; print-exps.scm -- print expression AST structures +;;; +;;; author : Sandra Loosemore +;;; date : 10 Jan 1992 +;;; +;;; This file corresponds to ast/exp-structs.scm. +;;; + +(define-ast-printer lambda (object xp) + (with-ast-block (xp) + (write-string "\\ " xp) + (write-delimited-list + (lambda-pats object) xp (function write-apat) "" "" "") + (write-string " ->" xp) + (write-whitespace xp) + (write (lambda-body object) xp))) + +(define-ast-printer let (object xp) + (write-lets-body "let " (let-decls object) (let-body object) xp)) + +(define (write-lets-body let-name decls body xp) + (pprint-logical-block (xp '() "" "") ; no extra indentation + (write-string let-name xp) + (write-layout-rule (remove-recursive-grouping decls) xp (function write)) + (write-whitespace xp) + (write-string "in " xp) + (write body xp))) + +(define-ast-printer if (object xp) + (with-ast-block (xp) + (write-string "if " xp) + (write (if-test-exp object) xp) + (write-whitespace xp) + (with-ast-block (xp) + (write-string "then" xp) + (write-whitespace xp) + (write (if-then-exp object) xp)) + (write-whitespace xp) + (with-ast-block (xp) + (write-string "else" xp) + (write-whitespace xp) + (write (if-else-exp object) xp)))) + +(define-ast-printer case (object xp) + (with-ast-block (xp) + (write-string "case " xp) + (write (case-exp object) xp) + (write-string " of" xp) + (write-whitespace xp) + (write-layout-rule (case-alts object) xp (function write)))) + +(define-ast-printer alt (object xp) + (with-ast-block (xp) + (write (alt-pat object) xp) + (dolist (r (alt-rhs-list object)) + (write-whitespace xp) + (unless (is-type? 'omitted-guard (guarded-rhs-guard r)) + (write-string "| " xp) + (write (guarded-rhs-guard r) xp)) + (write-string " -> " xp) + (write (guarded-rhs-rhs r) xp)) + (write-wheredecls (alt-where-decls object) xp))) + +(define-ast-printer exp-sign (object xp) + (with-ast-block (xp) + (write (exp-sign-exp object) xp) + (write-string " ::" xp) + (write-whitespace xp) + (write (exp-sign-signature object) xp))) + +;;; Have to look for application of special-case constructors before +;;; doing the normal prefix/infix cases. + +(define-ast-printer app (object xp) + (let* ((fn (app-fn object)) + (arg (app-arg object))) + (multiple-value-bind (con args) (extract-constructor fn (list arg)) + (cond ;; ((eq? con (core-symbol "UnitConstructor")) + ;; *** Does this ever happen? + ;; (write-string "()" xp)) + ((and con (is-tuple-constructor? con)) + (write-commaized-list args xp)) + (else + (multiple-value-bind (fixity op arg1) (extract-infix-operator fn) + (if fixity + (write-infix-application fixity op arg1 arg xp) + (write-prefix-application fn arg xp)))) + )))) + + +(define (write-infix-application fixity op arg1 arg2 xp) + (let ((precedence (fixity-precedence fixity)) + (associativity (fixity-associativity fixity))) + (with-ast-block (xp) + (write-exp-with-precedence + arg1 (1+ precedence) (if (eq? associativity 'l) 'l '#f) xp) + (write-whitespace xp) + (write op xp) + (write-whitespace xp) + (write-exp-with-precedence + arg2 (1+ precedence) (if (eq? associativity 'r) 'r '#f) xp)))) + +(define (write-prefix-application fn arg xp) + (with-ast-block (xp) + (write-exp-with-precedence fn 10 '#f xp) + (write-whitespace xp) + (write-aexp arg xp))) + + +;;; Write an expression with at least the given precedence. If the +;;; actual precedence is lower, put parens around it. + +(define *print-exp-parens* '#f) + +(define (write-exp-with-precedence exp precedence associativity xp) + (if *print-exp-parens* + (write-aexp exp xp) + (if (< (precedence-of-exp exp associativity) precedence) + (begin + (write-char #\( xp) + (write exp xp) + (write-char #\) xp)) + (write exp xp)))) + + +;;; Similar to the above: write an aexp. + +(define *print-original-code* '#t) + +(define (write-aexp object xp) + (if (is-type? 'save-old-exp object) + (write-aexp (if *print-original-code* + (save-old-exp-old-exp object) + (save-old-exp-new-exp object)) + xp) + (if (or (is-type? 'aexp object) + (pp-exp-list-section? object) + (is-type? 'negate object)) + (write object xp) + (begin + (write-char #\( xp) + (write object xp) + (write-char #\) xp))))) + + +;;; The infix? slot on var-ref and con-ref structs refers to whether +;;; the thing appears as an infix operator or not, not whether the name +;;; has operator or identifier syntax. + +(define-ast-printer var-ref (object xp) + (let ((name (var-ref-name object))) + (if (var-ref-infix? object) + (write-varop name xp) + (write-varid name xp)))) + +(define-ast-printer con-ref (object xp) + (if (eq? (con-ref-con object) (core-symbol "UnitConstructor")) + (write-string "()" xp) + (let ((name (con-ref-name object))) + (if (con-ref-infix? object) + (write-conop name xp) + (write-conid name xp))))) + + +(define-ast-printer integer-const (object xp) + (write (integer-const-value object) xp)) + +(define-ast-printer float-const (object xp) + (let* ((numerator (float-const-numerator object)) + (denominator (float-const-denominator object)) + (exponent (float-const-exponent object)) + (whole (quotient numerator denominator)) + (fraction (remainder numerator denominator))) + (write whole xp) + (write-char #\. xp) + (write-precision-integer fraction denominator xp) + (unless (zero? exponent) + (write-char #\E xp) + (write exponent xp)))) + +(define (write-precision-integer fraction denominator xp) + (cond ((eqv? denominator 1) + ; no fraction + ) + ((eqv? denominator 10) + (write-digit fraction xp)) + (else + (write-digit (quotient fraction 10) xp) + (write-precision-integer (remainder fraction 10) + (quotient denominator 10) + xp)) + )) + +(define (write-digit n xp) + (write-char (string-ref "0123456789" n) xp)) + + +;;; Character and string printers need to handle weird escapes. +;;; Fortunately we can just choose one canonical style for printing +;;; unprintable characters. + +(define-ast-printer char-const (object xp) + (write-char #\' xp) + (write-char-literal (char-const-value object) xp #\') + (write-char #\' xp)) + +(define-ast-printer string-const (object xp) + (write-char #\" xp) + (let ((s (string-const-value object))) + (dotimes (i (string-length s)) + (write-char-literal (string-ref s i) xp #\"))) + (write-char #\" xp)) + +(define (write-char-literal c xp special) + (cond ((eqv? c special) + (write-char #\\ xp) + (write c xp)) + ((eqv? c #\newline) + (write-char #\\ xp) + (write-char #\n xp)) + (else + (let ((code (char->integer c))) + (if (and (>= code 32) (< code 128)) + ;; printing ascii characters + (write-char c xp) + ;; "control" characters print in \ddd notation + (begin + (write-char #\\ xp) + (write code xp))))) + )) + +(define-ast-printer list-exp (object xp) + (write-delimited-list + (list-exp-exps object) xp (function write) "," "[" "]")) + +(define-ast-printer sequence (object xp) + (with-ast-block (xp) + (write-string "[" xp) + (write (sequence-from object) xp) + (write-string "..]" xp))) + +(define-ast-printer sequence-to (object xp) + (with-ast-block (xp) + (write-string "[" xp) + (write (sequence-to-from object) xp) + (write-string " .." xp) + (write-whitespace xp) + (write (sequence-to-to object) xp) + (write-string "]" xp))) + +(define-ast-printer sequence-then (object xp) + (with-ast-block (xp) + (write-string "[" xp) + (write (sequence-then-from object) xp) + (write-string "," xp) + (write-whitespace xp) + (write (sequence-then-then object) xp) + (write-string "..]" xp))) + +(define-ast-printer sequence-then-to (object xp) + (with-ast-block (xp) + (write-string "[" xp) + (write (sequence-then-to-from object) xp) + (write-string "," xp) + (write-whitespace xp) + (write (sequence-then-to-then object) xp) + (write-string " .." xp) + (write-whitespace xp) + (write (sequence-then-to-to object) xp) + (write-string "]" xp))) + +(define-ast-printer list-comp (object xp) + (with-ast-block (xp) + (write-string "[" xp) + (write (list-comp-exp object) xp) + (write-string " |" xp) + (write-whitespace xp) + (write-delimited-list + (list-comp-quals object) xp (function write) "," "" "") + (write-string "]" xp))) + + +(define-ast-printer section-l (object xp) + (let* ((exp (section-l-exp object)) + (op (section-l-op object)) + (fixity (operator-fixity op)) + (precedence (fixity-precedence fixity))) + (with-ast-block (xp) + (write-string "(" xp) + (write op xp) + (write-whitespace xp) + (write-exp-with-precedence exp (1+ precedence) '#f xp) + (write-string ")" xp)))) + +(define-ast-printer section-r (object xp) + (let* ((exp (section-r-exp object)) + (op (section-r-op object)) + (fixity (operator-fixity op)) + (precedence (fixity-precedence fixity))) + (with-ast-block (xp) + (write-string "(" xp) + (write-exp-with-precedence exp (1+ precedence) '#f xp) + (write-whitespace xp) + (write op xp) + (write-string ")" xp)))) + +(define-ast-printer qual-generator (object xp) + (with-ast-block (xp) + (write (qual-generator-pat object) xp) + (write-string " <-" xp) + (write-whitespace xp) + (write (qual-generator-exp object) xp))) + +(define-ast-printer qual-filter (object xp) + (write (qual-filter-exp object) xp)) + + +;;; A pp-exp-list with an op as the first or last element is really +;;; a section. These always get parens and are treated like aexps. +;;; Other pp-exp-lists are treated as exps with precedence 0. +;;; Bleah... Seems like the parser ought to recognize this up front.... +;;; Yeah but I'm lazy ... + +(define-ast-printer pp-exp-list (object xp) + (let ((section? (pp-exp-list-section? object))) + (if section? (write-char #\( xp)) + (write-delimited-list + (pp-exp-list-exps object) xp (function write-aexp) "" "" "") + (if section? (write-char #\) xp)))) + +(define-ast-printer negate (object xp) + (declare (ignore object)) + (write-string "-" xp)) + +(define-ast-printer def (object xp) + (write-string (symbol->string (def-name object)) xp)) + +(define-ast-printer con (object xp) + (write-string (remove-con-prefix (symbol->string (def-name object))) xp)) + +(define-ast-printer con-number (object xp) + (with-ast-block (xp) + (write-string "con-number/" xp) + (write (con-number-type object) xp) + (write-whitespace xp) + (write-aexp (con-number-value object) xp))) + +(define-ast-printer sel (object xp) + (with-ast-block (xp) + (write-string "sel/" xp) + (write (sel-constructor object) xp) + (write-whitespace xp) + (write (sel-slot object) xp) + (write-whitespace xp) + (write-aexp (sel-value object) xp))) + +(define-ast-printer is-constructor (object xp) +(with-ast-block (xp) + (write-string "is-constructor/" xp) + (write (is-constructor-constructor object) xp) + (write-whitespace xp) + (write-aexp (is-constructor-value object) xp))) + +(define-ast-printer void (object xp) + (declare (ignore object)) + (write-string "Void" xp)) + +;;; Special cfn constructs + +(define-ast-printer case-block (object xp) + (with-ast-block (xp) + (write-string "case-block " xp) + (write (case-block-block-name object) xp) + (write-whitespace xp) + (write-layout-rule (case-block-exps object) xp (function write)))) + +(define-ast-printer return-from (object xp) + (with-ast-block (xp) + (write-string "return-from " xp) + (write (return-from-block-name object) xp) + (write-whitespace xp) + (write (return-from-exp object) xp))) + +(define-ast-printer and-exp (object xp) + (with-ast-block (xp) + (write-string "and " xp) + (write-layout-rule (and-exp-exps object) xp (function write)))) + +;;; Expression types used by the type checker. + +(define-ast-printer dict-placeholder (object xp) + (cond ((not (eq? (dict-placeholder-exp object) '#f)) + (write (dict-placeholder-exp object) xp)) + (else + (write-string "%" xp) + (write-string (symbol->string + (def-name (dict-placeholder-class object))) xp)))) + +(define-ast-printer recursive-placeholder (object xp) + (cond ((not (eq? (recursive-placeholder-exp object) '#f)) + (write (recursive-placeholder-exp object) xp)) + (else + (write-varid (def-name (recursive-placeholder-var object)) xp)))) + +;;; This should probably have a flag to allow the dictionary converted code +;;; to be printed during debugging. + +(define-ast-printer save-old-exp (object xp) + (write (save-old-exp-old-exp object) xp)) + diff --git a/printers/print-modules.scm b/printers/print-modules.scm new file mode 100644 index 0000000..2372714 --- /dev/null +++ b/printers/print-modules.scm @@ -0,0 +1,125 @@ +;;; print-modules.scm -- print routines for module-related AST structures +;;; +;;; author : Sandra Loosemore +;;; date : 6 Jan 1992 +;;; +;;; +;;; This file corresponds to the file ast/modules.scm. + +;;; Note: by default, only the module name is printed. To print the +;;; full module, the function print-full-module must be called. + +(define *print-abbreviated-modules* '#t) + +(define-ast-printer module (object xp) + (if *print-abbreviated-modules* + (begin + (write-string "Module " xp) + (write-string (symbol->string (module-name object)) xp)) + (do-print-full-module object xp))) + +(define (print-full-module object . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (dynamic-let ((*print-abbreviated-modules* '#f)) + (pprint object stream)))) + +(define (do-print-full-module object xp) + (dynamic-let ((*print-abbreviated-modules* '#t)) + (let ((modid (module-name object)) + (exports (module-exports object)) + (body (append (module-imports object) + (module-fixities object) + (module-synonyms object) + (module-algdatas object) + (module-classes object) + (module-instances object) + (if (or (not (module-default object)) + (eq? (module-default object) + *standard-module-default*)) + '() + (list (module-default object))) + (module-decls object)))) + (write-string "module " xp) + (write-modid modid xp) + (when (not (null? exports)) + (write-whitespace xp) + (write-commaized-list exports xp)) + (write-wheredecls body xp)))) + +(define-ast-printer import-decl (object xp) + (let ((modid (import-decl-module-name object)) + (mode (import-decl-mode object)) + (specs (import-decl-specs object)) + (renamings (import-decl-renamings object))) + (with-ast-block (xp) + (write-string "import " xp) + (write-modid modid xp) + (if (eq? mode 'all) + (when (not (null? specs)) + (write-whitespace xp) + (write-string "hiding " xp) + (write-commaized-list specs xp)) + (begin + (write-whitespace xp) + (write-commaized-list specs xp))) + (when (not (null? renamings)) + (write-whitespace xp) + (write-string "renaming " xp) + (write-commaized-list renamings xp)) + ))) + +(define-ast-printer entity-module (object xp) + (write-modid (entity-name object) xp) + (write-string ".." xp)) + +(define-ast-printer entity-var (object xp) + (write-varid (entity-name object) xp)) + +(define-ast-printer entity-con (object xp) + (write-tyconid (entity-name object) xp)) + +(define-ast-printer entity-abbreviated (object xp) + (write-tyconid (entity-name object) xp) + (write-string "(..)" xp)) + +(define-ast-printer entity-class (object xp) + (with-ast-block (xp) + (write-tyclsid (entity-name object) xp) + (write-whitespace xp) + (write-delimited-list (entity-class-methods object) xp + (function write-varid) "," "(" ")"))) + +(define-ast-printer entity-datatype (object xp) + (with-ast-block (xp) + (write-tyconid (entity-name object) xp) + (write-whitespace xp) + (write-delimited-list (entity-datatype-constructors object) xp + (function write-conid) "," "(" ")"))) + + +(define-ast-printer renaming (object xp) + (with-ast-block (xp) + (write-varid-conid (renaming-from object) xp) + (write-string " to" xp) + (write-whitespace xp) + (write-varid-conid (renaming-to object) xp))) + +;;; *** Should it omit precedence if it's 9? + +(define-ast-printer fixity-decl (object xp) + (let* ((fixity (fixity-decl-fixity object)) + (associativity (fixity-associativity fixity)) + (precedence (fixity-precedence fixity)) + (ops (fixity-decl-names object))) + (with-ast-block (xp) + (cond ((eq? associativity 'l) + (write-string "infixl " xp)) + ((eq? associativity 'r) + (write-string "infixr " xp)) + ((eq? associativity 'n) + (write-string "infix " xp))) + (write precedence xp) + (write-whitespace xp) + (write-delimited-list ops xp (function write-varop-conop) "," "" "")))) diff --git a/printers/print-ntypes.scm b/printers/print-ntypes.scm new file mode 100644 index 0000000..c018f40 --- /dev/null +++ b/printers/print-ntypes.scm @@ -0,0 +1,61 @@ +;;; These printers deal with ntype structures. + +;;; Too much of this file is copied from print-types! + +(define-ast-printer ntyvar (object xp) + (let ((object (prune object))) + (if (ntyvar? object) + (begin + (write-char #\t xp) + (write (tyvar->number object) xp)) + (write object xp)))) + +;;; Various type special cases have a magic cookie in the def field. + +(define-ast-printer ntycon (object xp) + (let ((tycon (ntycon-tycon object))) + (if (eq? tycon '#f) + (write-string "" xp) + (print-general-tycon tycon (ntycon-args object) object xp)))) + +(define-ast-printer gtype (object xp) + (let ((var 0) + (res '())) + (dolist (classes (gtype-context object)) + (let ((v (gtyvar->symbol var))) + (dolist (class classes) + (push (**context (**class/def class) v) res))) + (incf var)) + (write-contexts (reverse res) xp) + (write (gtype-type object) xp))) + +(define-ast-printer gtyvar (object xp) + (write-string (symbol->string (gtyvar->symbol (gtyvar-varnum object))) xp)) + +(define (gtyvar->symbol n) + (cond ((< n 26) + (list-ref '(|a| |b| |c| |d| |e| |f| |g| + |h| |i| |j| |k| |l| |m| |n| + |o| |p| |q| |r| |s| |t| |u| + |v| |w| |x| |y| |z|) + n)) + (else + (string->symbol (format '#f "g~A" (- n 25)))))) + +(define-ast-printer recursive-type (object xp) + (write (recursive-type-type object) xp)) + +(define (tyvar->number tyvar) + (tyvar->number-1 tyvar (dynamic *printed-tyvars*) 1)) + +(define (tyvar->number-1 tyvar vars n) + (cond ((null? vars) + (setf (dynamic *printed-tyvars*) + (nconc (dynamic *printed-tyvars*) (list tyvar))) + n) + ((eq? tyvar (car vars)) + n) + (else + (tyvar->number-1 tyvar (cdr vars) (1+ n))))) + + diff --git a/printers/print-types.scm b/printers/print-types.scm new file mode 100644 index 0000000..53d3bee --- /dev/null +++ b/printers/print-types.scm @@ -0,0 +1,201 @@ +;;; print-types.scm -- print type-related AST structures +;;; +;;; author : Sandra Loosemore +;;; date : 15 Jan 1991 +;;; +;;; This file corresponds to the stuff in ast/type-structs.scm +;;; + +(define-ast-printer tyvar (object xp) + (write-avarid (tyvar-name object) xp)) + + +;;; Various type special cases have a magic cookie in the def field. + +(define-ast-printer tycon (object xp) + (print-general-tycon (tycon-def object) (tycon-args object) object xp)) + +(define (print-general-tycon def args object xp) + (cond ((eq? def (core-symbol "Arrow")) + (write-arrow-tycon args xp)) + ((eq? def (core-symbol "UnitType")) + (write-unit-tycon xp)) + ((eq? def (core-symbol "List")) + (write-list-tycon args xp)) + ((is-tuple-tycon? def) + (write-tuple-tycon args xp)) + (else + (write-ordinary-tycon def args object xp)))) + +(define (write-arrow-tycon args xp) + (with-ast-block (xp) + (write-btype (car args) xp) + (write-string " ->" xp) + (write-whitespace xp) + (write (cadr args) xp))) + +(define (write-unit-tycon xp) + (write-string "()" xp)) + +(define (write-list-tycon args xp) + (with-ast-block (xp) + (write-char #\[ xp) + (write (car args) xp) + (write-char #\] xp))) + +(define (write-tuple-tycon args xp) + (write-commaized-list args xp)) + +(define (write-ordinary-tycon def args object xp) + (with-ast-block (xp) + (if (tycon? object) + (write-tyconid (tycon-name object) xp) + (write-tyconid (def-name def) xp)) + (when (not (null? args)) + (write-whitespace xp) + (write-delimited-list + args xp (function write-atype) "" "" "")))) + + +;;; All of the special cases above except "Arrow" are atypes, as is +;;; a tyvar or a tycon with no arguments. + +(define (write-atype object xp) + (let ((object (maybe-prune object))) + (if (or (tyvar? object) + (gtyvar? object) + (ntyvar? object) + (is-some-tycon? object + (lambda (def) + (or (eq? def (core-symbol "UnitType")) + (eq? def (core-symbol "List")) + (is-tuple-tycon? def))))) + (write object xp) + (begin + (write-char #\( xp) + (write object xp) + (write-char #\) xp))))) + + +;;; A btype is any type except the arrow tycon. + +(define (write-btype object xp) + (let ((object (maybe-prune object))) + (if (or (and (tycon? object) + (eq? (tycon-def object) (core-symbol "Arrow"))) + (and (ntycon? object) + (eq? (ntycon-tycon object) (core-symbol "Arrow")))) + (begin + (write-char #\( xp) + (write object xp) + (write-char #\) xp)) + (write object xp)))) + +(define (maybe-prune object) + (if (ntyvar? object) + (prune object) + object)) + +(define (is-some-tycon? object fn) + (let ((object (maybe-prune object))) + (or (and (tycon? object) + (or (null? (tycon-args object)) + (funcall fn (tycon-def object)))) + (and (ntycon? object) + (or (null? (ntycon-args object)) + (funcall fn (ntycon-tycon object))))))) + +(define-ast-printer context (object xp) + (with-ast-block (xp) + (write (context-class object) xp) + (write-whitespace xp) + (write-avarid (context-tyvar object) xp))) + +(define-ast-printer signature (object xp) + (write-contexts (signature-context object) xp) + (write (signature-type object) xp)) + +(define (write-contexts contexts xp) + (when (not (null? contexts)) + (if (null? (cdr contexts)) + (write (car contexts) xp) + (write-commaized-list contexts xp)) + (write-string " =>" xp) + (write-whitespace xp))) + +(define-ast-printer synonym-decl (object xp) + (with-ast-block (xp) + (write-string "type " xp) + (write (synonym-decl-simple object) xp) + (write-string " =" xp) + (write-whitespace xp) + (write (synonym-decl-body object) xp))) + +(define-ast-printer data-decl (object xp) + (with-ast-block (xp) + (write-string "data " xp) + (write-contexts (data-decl-context object) xp) + (write (data-decl-simple object) xp) + (write-whitespace xp) + (write-char #\= xp) + (write-whitespace xp) + (write-delimited-list + (data-decl-constrs object) xp (function write) " |" "" "") + (write-whitespace xp) + (let ((deriving (data-decl-deriving object))) + (when (not (null? deriving)) + (write-string "deriving " xp) + (if (null? (cdr deriving)) + (write (car deriving) xp) + (write-commaized-list deriving xp)))))) + +(define-ast-printer constr (object xp) + (if (con-ref-infix? (constr-constructor object)) + (with-ast-block (xp) + (write-btype (car (constr-types object)) xp) + (write-whitespace xp) + (write (constr-constructor object) xp) + (write-whitespace xp) + (write-btype (cadr (constr-types object)) xp)) + (with-ast-block (xp) + (write (constr-constructor object) xp) + (when (not (null? (constr-types object))) + (write-whitespace xp) + (write-delimited-list + (constr-types object) xp (function write-atype) "" "" ""))))) + + +(define-ast-printer class-decl (object xp) + (with-ast-block (xp) + (write-string "class " xp) + (write-contexts (class-decl-super-classes object) xp) + (write (class-decl-class object) xp) + (write-whitespace xp) + (write-avarid (class-decl-class-var object) xp) + (write-wheredecls (class-decl-decls object) xp))) + +(define-ast-printer instance-decl (object xp) + (with-ast-block (xp) + (write-string "instance " xp) + (write-contexts (instance-decl-context object) xp) + (write (instance-decl-class object) xp) + (write-whitespace xp) + (write-atype (instance-decl-simple object) xp) + (write-wheredecls (instance-decl-decls object) xp))) + + +;;; Don't print out default decl if the value is the default. + +(define-ast-printer default-decl (object xp) + (with-ast-block (xp) + (write-string "default " xp) + (let ((types (default-decl-types object))) + (if (null? (cdr types)) + (write (car types) xp) + (write-commaized-list types xp))))) + +(define-ast-printer class-ref (object xp) + (write-tyclsid (class-ref-name object) xp)) + + + diff --git a/printers/print-valdefs.scm b/printers/print-valdefs.scm new file mode 100644 index 0000000..908adea --- /dev/null +++ b/printers/print-valdefs.scm @@ -0,0 +1,180 @@ +;;; print-valdefs.scm -- print AST structures for local declarations +;;; +;;; author : Sandra Loosemore +;;; date : 14 Jan 1992 +;;; +;;; This file corresponds to ast/valdef-structs.scm. +;;; +;;; + + + +(define-ast-printer signdecl (object xp) + (with-ast-block (xp) + (write-delimited-list (signdecl-vars object) xp (function write) "," "" "") + (write-string " ::" xp) + (write-whitespace xp) + (write (signdecl-signature object) xp))) + + +;;; This interacts with the layout rule stuff. See util.scm. + +(define-ast-printer valdef (object xp) + (let ((lhs (valdef-lhs object)) + (definitions (valdef-definitions object))) + (write-definition lhs (car definitions) xp) + (dolist (d (cdr definitions)) + (if (dynamic *print-pretty*) + (pprint-newline 'mandatory xp) + (write-string "; " xp)) + (write-definition lhs d xp)))) + + +(define (write-definition lhs d xp) + (with-ast-block (xp) + (let ((args (single-fun-def-args d)) + (rhs-list (single-fun-def-rhs-list d)) + (where-decls (single-fun-def-where-decls d)) + (infix? (single-fun-def-infix? d))) + (write-lhs lhs args infix? xp) + (write-rhs rhs-list xp) + (write-wheredecls where-decls xp) + ))) + +(define (write-lhs lhs args infix? xp) + (cond ((null? args) + ;; pattern definition + (write-apat lhs xp) + ) + ;; If there are args, the lhs is always a var-pat pointing to a + ;; var-ref. The infix? slot from the single-fun-def must override + ;; the slot on the var-ref, since there can be a mixture of + ;; infix and prefix definitions for the same lhs. + (infix? + ;; operator definition + (when (not (null? (cddr args))) + (write-char #\( xp)) + (write-apat (car args) xp) + (write-whitespace xp) + (write-varop (var-ref-name (var-pat-var lhs)) xp) + (write-whitespace xp) + (write-apat (cadr args) xp) + (when (not (null? (cddr args))) + (write-char #\) xp) + (write-whitespace xp) + (write-delimited-list (cddr args) xp (function write-apat) + "" "" ""))) + (else + ;; normal prefix function definition + (write-varid (var-ref-name (var-pat-var lhs)) xp) + (write-whitespace xp) + (write-delimited-list args xp (function write-apat) "" "" "")) + )) + +(define (write-rhs rhs-list xp) + (let ((guard (guarded-rhs-guard (car rhs-list))) + (rhs (guarded-rhs-rhs (car rhs-list)))) + (when (not (is-type? 'omitted-guard guard)) + (write-string " | " xp) + (write guard xp)) + (write-string " =" xp) + (write-whitespace xp) + (write rhs xp) + (when (not (null? (cdr rhs-list))) + (write-newline xp) + (write-rhs (cdr rhs-list) xp)))) + + +;;; Pattern printers + + +;;; As per jcp suggestion, don't put whitespace after @; line break comes +;;; before, not after (as is the case for other infix-style punctuation). + +(define-ast-printer as-pat (object xp) + (with-ast-block (xp) + (write (as-pat-var object) xp) + (write-whitespace xp) + (write-string "@" xp) + (write-apat (as-pat-pattern object) xp))) + +(define (write-apat pat xp) + (if (or (is-type? 'apat pat) + (is-type? 'pp-pat-plus pat) ; hack per jcp + (and (is-type? 'pcon pat) + (or (null? (pcon-pats pat)) + (eq? (pcon-con pat) (core-symbol "UnitConstructor")) + (is-tuple-constructor? (pcon-con pat))))) + (write pat xp) + (begin + (write-char #\( xp) + (write pat xp) + (write-char #\) xp)))) + +(define-ast-printer irr-pat (object xp) + (write-string "~" xp) + (write-apat (irr-pat-pattern object) xp)) + +(define-ast-printer var-pat (object xp) + (write (var-pat-var object) xp)) + +(define-ast-printer wildcard-pat (object xp) + (declare (ignore object)) + (write-char #\_ xp)) + +(define-ast-printer const-pat (object xp) + (write (const-pat-value object) xp)) + +(define-ast-printer plus-pat (object xp) + (write (plus-pat-pattern object) xp) + (write-string " + " xp) + (write (plus-pat-k object) xp)) + + + +(define-ast-printer pcon (object xp) + (let ((name (pcon-name object)) + (pats (pcon-pats object)) + (infix? (pcon-infix? object)) + (def (pcon-con object))) + (cond ((eq? def (core-symbol "UnitConstructor")) + (write-string "()" xp)) + ((is-tuple-constructor? def) + (write-commaized-list pats xp)) + ((null? pats) + (if infix? + ;; infix pcon with no arguments can happen inside pp-pat-list + ;; before precedence parsing happens. + (write-conop name xp) + (write-conid name xp))) + (infix? + ;; This could be smarter about dealing with precedence of patterns. + (with-ast-block (xp) + (write-apat (car pats) xp) + (write-whitespace xp) + (write-conop name xp) + (write-whitespace xp) + (write-apat (cadr pats) xp))) + (else + (with-ast-block (xp) + (write-conid name xp) + (write-whitespace xp) + (write-delimited-list pats xp (function write-apat) "" "" ""))) + ))) + +(define-ast-printer list-pat (object xp) + (write-delimited-list + (list-pat-pats object) xp (function write) "," "[" "]")) + +(define-ast-printer pp-pat-list (object xp) + (write-delimited-list (pp-pat-list-pats object) xp (function write-apat) + "" "" "")) + +(define-ast-printer pp-pat-plus (object xp) + (declare (ignore object)) + (write-string "+ " xp)) + +(define-ast-printer pp-pat-negated (object xp) + (declare (ignore object)) + (write-string "-" xp)) + diff --git a/printers/printers.scm b/printers/printers.scm new file mode 100644 index 0000000..3ac4fe6 --- /dev/null +++ b/printers/printers.scm @@ -0,0 +1,28 @@ +;;; printers.scm -- compilation unit definition for structure printers +;;; +;;; author : Sandra Loosemore +;;; date : 3 Jan 1992 +;;; +;;; + +(define-compilation-unit printer-support + (source-filename "$Y2/printers/") + (require global) + (unit util + (source-filename "util.scm"))) + +(define-compilation-unit printers + (source-filename "$Y2/printers/") + (require printer-support) + (unit print-exps + (source-filename "print-exps.scm")) + (unit print-modules + (source-filename "print-modules.scm")) + (unit print-types + (source-filename "print-types.scm")) + (unit print-ntypes + (source-filename "print-ntypes.scm")) + (unit print-valdefs + (source-filename "print-valdefs.scm")) + ) + diff --git a/printers/util.scm b/printers/util.scm new file mode 100644 index 0000000..498aa25 --- /dev/null +++ b/printers/util.scm @@ -0,0 +1,214 @@ +;;; util.scm -- utilities for printing AST structures +;;; +;;; author : Sandra Loosemore +;;; date : 15 Jan 1992 +;;; +;;; + + +;;; The AST syntax printers are only used if this variable is true. + +(define *print-ast-syntax* '#t) + + +;;; Here's a macro for defining AST printers. + +(define-syntax (define-ast-printer type lambda-list . body) + (let ((printer (symbol-append 'write- type))) + `(begin + (define (,printer ,@lambda-list) ,@body) + (define-struct-printer ,type ,printer)) + )) + + +;;; This variable controls how much indentation to perform on block +;;; bodies. + +(define *print-ast-indent* 2) + + +;;; Begin a logical block with the default indentation. + +(define-syntax (with-ast-block xp-stuff . body) + (let ((xp (car xp-stuff))) + `(pprint-logical-block (,xp '() "" "") + (pprint-indent 'block (dynamic *print-ast-indent*) ,xp) + (pprint-pop) ; prevents unused variable warning + ,@body))) + + +;;; Write a space and maybe a fill line break. + +(define (write-whitespace xp) + (write-char #\space xp) + (pprint-newline 'fill xp)) + + +;;; Write a space and maybe a mandatory line break. + +(define (write-newline xp) + (write-char #\space xp) + (pprint-newline 'mandatory xp)) + + + +;;; Write a list of things separated by delimiters and maybe +;;; surrounded by delimiters. + +(define (write-delimited-list objects xp fn delim prefix suffix) + (pprint-logical-block (xp '() prefix suffix) + (do ((objects objects (cdr objects))) + ((null? objects) '#f) + (pprint-pop) + (funcall fn (car objects) xp) + (when (cdr objects) + (write-string delim xp) + (write-whitespace xp))))) + + +;;; Here's a couple common special cases of the above. + +(define (write-commaized-list objects xp) + (write-delimited-list objects xp (function write) "," "(" ")")) + +(define (write-ordinary-list objects xp) + (write-delimited-list objects xp (function write) "" "" "")) + + +;;; Here's another helper function that's used to implement the layout +;;; rule. The layout rule is only used to format output if *print-pretty* +;;; is true. +;;; *** should do pprint-indent here? + +(define (write-layout-rule objects xp fn) + (pprint-logical-block (xp '() + (if (dynamic *print-pretty*) "" "{") + (if (dynamic *print-pretty*) "" "}")) + (do ((objects objects (cdr objects))) + ((null? objects) '#f) + (pprint-pop) + (funcall fn (car objects) xp) + (when (cdr objects) + (if (dynamic *print-pretty*) + (pprint-newline 'mandatory xp) + (write-string "; " xp)))))) + + +;;; This filters a list of decls, removing the recursive marker added by +;;; dependency analysis. + +(define (remove-recursive-grouping decls) + (cond ((null? decls) '()) + ((is-type? 'recursive-decl-group (car decls)) + (append (recursive-decl-group-decls (car decls)) + (remove-recursive-grouping (cdr decls)))) + (else + (cons (car decls) (remove-recursive-grouping (cdr decls)))))) + +;;; Write where-decls, using the layout rule if appropriate. + +(define (write-wheredecls decls xp) + (when (not (null? decls)) + (write-whitespace xp) + (write-string "where" xp) + (write-whitespace xp) + (write-layout-rule (remove-recursive-grouping decls) xp (function write)))) + + +;;; Write an ordinary variable name. + +(define (write-avarid name xp) + (write-string (symbol->string name) xp)) + + +;;; Constructor name symbols have a funny prefix attached; have to strip +;;; this off, so can't just print the symbol using write-avarid. + +(define (write-aconid name xp) + (let ((s (symbol->string name))) + (write-string (substring s 1 (string-length s)) xp))) + + +;;; There are a couple places where conids and varids are mixed up +;;; together. + +(define (conid? name) + (eqv? (string-ref (symbol->string name) 0) #\;)) + +(define (write-varop-conop name xp) + (if (conid? name) + (write-conop name xp) + (write-varop name xp))) + +(define (write-varid-conid name xp) + (if (conid? name) + (write-conid name xp) + (write-varid name xp))) + + + +;;; Stuff for writing a variable name as either an operator or an ordinary +;;; variable ID. This is necessary because some kinds of symbol names +;;; default to being operators and others default to being ordinary names. +;;; Bleah.... + + +(define (write-varop name xp) + (if (avarid? name) + (begin + (write-char #\` xp) + (write-avarid name xp) + (write-char #\` xp)) + (write-avarid name xp))) + +(define (write-varid name xp) + (if (avarid? name) + (write-avarid name xp) + (begin + (write-char #\( xp) + (write-avarid name xp) + (write-char #\) xp)))) + + +;;; This tests for alphabetic rather than lower-case characters +;;; so that gensym'ed variables with uppercase names don't print funny. + +(define (avarid? name) + (let ((ch (string-ref (symbol->string name) 0))) + (char-alphabetic? ch))) + + +;;; Similar stuff for doing constructor names. Moby bleah.... + +(define (write-conop name xp) + (if (aconid? name) + (begin + (write-char #\` xp) + (write-aconid name xp) + (write-char #\` xp)) + (write-aconid name xp))) + +(define (write-conid name xp) + (if (aconid? name) + (write-aconid name xp) + (begin + (write-char #\( xp) + (write-aconid name xp) + (write-char #\) xp)))) + +(define (aconid? name) + (let ((ch (string-ref (symbol->string name) 1))) + (char-upper-case? ch))) + + +;;; These are officially aconid in the syntax, but they aren't +;;; prefixed so write them using write-avarid instead. Barf. + +(define (write-modid name xp) + (write-avarid name xp)) + +(define (write-tyconid name xp) + (write-avarid name xp)) + +(define (write-tyclsid name xp) + (write-avarid name xp)) diff --git a/progs/README b/progs/README new file mode 100644 index 0000000..8b28d8f --- /dev/null +++ b/progs/README @@ -0,0 +1,9 @@ +This directory contains Haskell source code. +Subdirectories: +prelude The prelude used in this system +tutorial The online supplement to the Hudak & Fasel tutorial +demo A set of random demo programs +lib Various random extensions + +Other programs can be found in the Haskell program library on the +official Haskell ftp sites. diff --git a/progs/demo/Calendar.hs b/progs/demo/Calendar.hs new file mode 100644 index 0000000..fa2e4a4 --- /dev/null +++ b/progs/demo/Calendar.hs @@ -0,0 +1,138 @@ +-- This is a modification of the calendar program described in section 4.5 +-- of Bird and Wadler's ``Introduction to functional programming'', with +-- two ways of printing the calendar ... as in B+W, or like UNIX `cal': +-- +-- Use from within Yale Haskell: +-- +-- Main> :l Calendar +-- Now in module Calendar. +-- Calendar> @ do cal 1992 +-- Calendar> :e +-- +-- ... Unix style calendar ... +-- +-- Calendar> @ do calendar 1992 +-- Calendar> :e +-- +-- ... Bird and Wadler style calendar ... +-- +-- Calendar> + +module Calendar(cal,calendar) where + +infixr 5 `above`, `beside` + +do cal year = appendChan stdout (cal year) exit done + +-- Picture handling: + +type Picture = [[Char]] + +height, width :: Picture -> Int +height p = length p +width p = length (head p) + +above, beside :: Picture -> Picture -> Picture +above = (++) +beside = zipWith (++) + +stack, spread :: [Picture] -> Picture +stack = foldr1 above +spread = foldr1 beside + +empty :: (Int,Int) -> Picture +empty (h,w) = copy h (copy w ' ') + +block, blockT :: Int -> [Picture] -> Picture +block n = stack . map spread . group n +blockT n = spread . map stack . group n + +group :: Int -> [a] -> [[a]] +group n [] = [] +group n xs = take n xs : group n (drop n xs) + +lframe :: (Int,Int) -> Picture -> Picture +lframe (m,n) p = (p `beside` empty (h,n-w)) `above` empty (m-h,n) + where h = height p + w = width p + +-- Information about the months in a year: + +monthLengths year = [31,feb,31,30,31,30,31,31,30,31,30,31] + where feb | leap year = 29 + | otherwise = 28 + +leap year = if year`mod`100 == 0 then year`mod`400 == 0 + else year`mod`4 == 0 + +monthNames = ["January","February","March","April", + "May","June","July","August", + "September","October","November","December"] + +jan1st year = (year + last`div`4 - last`div`100 + last`div`400) `mod` 7 + where last = year - 1 + +firstDays year = take 12 + (map (`mod`7) + (scanl (+) (jan1st year) (monthLengths year))) + +-- Producing the information necessary for one month: + +dates fd ml = map (date ml) [1-fd..42-fd] + where date ml d | d<1 || ml String +calendar = unlines . block 3 . map picture . months + where picture (mn,yr,fd,ml) = title mn yr `above` table fd ml + title mn yr = lframe (2,25) [mn ++ " " ++ show yr] + table fd ml = lframe (8,25) + (daynames `beside` entries fd ml) + daynames = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"] + entries fd ml = blockT 7 (dates fd ml) + months year = zip4 monthNames + (copy 12 year) + (firstDays year) + (monthLengths year) + +-- In a format somewhat closer to UNIX cal: + +cal :: Int -> String +cal year = unlines (banner year `above` body year) + where banner yr = [cjustify 75 (show yr)] `above` empty (1,75) + body = block 3 . map (pad . pic) . months + pic (mn,fd,ml) = title mn `above` table fd ml + pad p = (side`beside`p`beside`side)`above`end + side = empty (8,2) + end = empty (1,25) + title mn = [cjustify 21 mn] + table fd ml = daynames `above` entries fd ml + daynames = [" Su Mo Tu We Th Fr Sa"] + entries fd ml = block 7 (dates fd ml) + months year = zip3 monthNames + (firstDays year) + (monthLengths year) + +-- Additional (B+W)-isms: these really ought to go in a separate module, +-- in a spearate file. But for ease of packaging this simple application, +-- it doesn't seem worth the trouble! + +copy :: Int -> a -> [a] +copy n x = take n (repeat x) + +space :: Int -> String +space n = copy n ' ' + +-- Simple string formatting: + +cjustify, ljustify, rjustify :: Int -> String -> String + +cjustify n s = space halfm ++ s ++ space (m - halfm) + where m = n - length s + halfm = m `div` 2 +ljustify n s = s ++ space (n - length s) +rjustify n s = space (n - length s) ++ s + +-- End of calendar program diff --git a/progs/demo/README b/progs/demo/README new file mode 100644 index 0000000..4a683cd --- /dev/null +++ b/progs/demo/README @@ -0,0 +1,15 @@ + +This directory contains Haskell demo programs. All of these programs +compile and execute properly. + +This directory contains: + + +fact.hs factorial +merge.hs merge sort +pfac.hs parallel factorial +primes.hs prime number generator +qs.hs quick sort +queens.hs N queens +symalg/ A symbolic algebra program +prolog/ A prolog interpreter diff --git a/progs/demo/X11/animation/README b/progs/demo/X11/animation/README new file mode 100644 index 0000000..c14b867 --- /dev/null +++ b/progs/demo/X11/animation/README @@ -0,0 +1,22 @@ +In his paper "A Functional Animation Starter Kit" [ARYA88], Kevi Arya +proposes an approach to animation that uses functional languages. As +Arya describes, the cost of computing power is falling. This is making +the use of computer animation much more prevalent. However, languages +such as C make it difficult to program animations. What is needed is +a simpler, faster and more accessible way to program graphics. Functional +languages are a very effective means for this, due to their higher order +functions. + + Kevi Arya goes on to provide such a functional animation package in +the language Miranda. Haskell in particular is good functional language for +two reasons. It is a completely functional language, doing even I/O in a +functional manner. Variables are evaluated in a lazy manner allowing infinite +lists to be manipulated easily, which suits the infinite frames format +of animation. + +The following animations are provided here: + + seaside.hs - a seaside scene + planets.hs - planets in orbit + palm.hs - another seaside scene + birds.hs - flying birds diff --git a/progs/demo/X11/animation/animation.hs b/progs/demo/X11/animation/animation.hs new file mode 100644 index 0000000..3c0750c --- /dev/null +++ b/progs/demo/X11/animation/animation.hs @@ -0,0 +1,16 @@ +-- This bundles all the animation stuff into a single module. + +module Animation(R_Ptypes..,R_Constants..,R_Utility..,R_Picture..,R_Behaviour.., + R_Movie..,R_Shapes..,R_Defaults..,R_Inbetween.., + R_Display..) where +import R_Ptypes +import R_Constants +import R_Utility +import R_Picture +import R_Behaviour +import R_Movie +import R_Shapes +import R_Defaults +import R_Inbetween +import R_Display + diff --git a/progs/demo/X11/animation/animation.hu b/progs/demo/X11/animation/animation.hu new file mode 100644 index 0000000..0dd71ee --- /dev/null +++ b/progs/demo/X11/animation/animation.hu @@ -0,0 +1,6 @@ +animation.hs +r_movie.hu +r_defaults.hu +r_shapes.hu +r_inbetween.hu +r_display.hu diff --git a/progs/demo/X11/animation/birds.hs b/progs/demo/X11/animation/birds.hs new file mode 100644 index 0000000..daba730 --- /dev/null +++ b/progs/demo/X11/animation/birds.hs @@ -0,0 +1,28 @@ +module Birds where + +import Animation + +bird :: Movie +--bird = osc [bird1,bird2] +bird = rOVERLAY + [apply (bPar [right,right,right,right]) bm1, + apply (bPar [up,right,right,right]) bm2] + where bm1 = osc [bird1] + bm2 = osc [bird2] + +bird1 = [(black,b1)] + where b1 = [(0,90),(20,100),(30,110),(40,110),(50,100),(110,120), + (130,100),(120,90),(80,90),(0,90), + (80,90),(90,70),(140,50),(120,90),(80,90), + (80,90),(70,70),(80,60),(90,70)] + +bird2 = [(red,b2)] + where b2 = [(0,60),(20,70),(30,80),(40,80),(50,70),(110,70), + (140,30),(110,35),(100,35),(70,50),(50,60),(0,60), + (70,50),(100,90),(150,100),(120,60),(110,35), + (70,50),(65,100),(85,115),(97,86)] + +main = getEnv "DISPLAY" exit + (\ host -> displaym host 30 bird) + + diff --git a/progs/demo/X11/animation/birds.hu b/progs/demo/X11/animation/birds.hu new file mode 100644 index 0000000..cd94b30 --- /dev/null +++ b/progs/demo/X11/animation/birds.hu @@ -0,0 +1,3 @@ +:o= all +birds.hs +animation.hu diff --git a/progs/demo/X11/animation/doc.tex b/progs/demo/X11/animation/doc.tex new file mode 100644 index 0000000..1b66751 --- /dev/null +++ b/progs/demo/X11/animation/doc.tex @@ -0,0 +1,578 @@ + +% This is obsolete regarding the X system -- jcp + +% -*-latex-*- +% Creator: John Tinmouth +% Creation Date: Thu May 9 1991 +\documentstyle[11pt]{article} +\newcommand{\X}[1]{{#1}\index{{#1}}} +\begin{document} + +\title{A Functional Animation Package in Haskell} +\author{ + John Tinmouth\\ + Computer Science Senior Project\\ + Yale University\\ + Professor Paul Hudak } +\date{9 May 1991} +\maketitle + + + +\section{Introduction} + + In his paper "A Functional Animation Starter Kit" [ARYA88], Kevi Arya +proposes an approach to animation that uses functional languages. As +Arya describes, the cost of computing power is falling. This is making +the use of computer animation much more prevalent. However, languages +such as C make it difficult to program animations. What is needed is +a simpler, faster and more accessible way to program graphics. Functional +languages are a very effective means for this, due to their higher order +functions. + + Kevi Arya goes on to provide such a functional animation package in +the language Miranda. Haskell in particular is good functional language for +two reasons. It is a completely functional language, doing even I/O in a +functional manner. Variables are evaluated in a lazy manner allowing infinite +lists to be manipulated easily, which suits the infinite frames format +of animation. As it is now possible to complete the implementation of +this package is Haskell, my work has been converting these Miranda programs +to Haskell version 1.0-0, Yale Haskell Group. + + + +\section{How to Use the Graphics: Overview} + + By using higher order functions, it becomes very easy to do rapid +prototyping of animations. You can quickly throw out an animation of +simple images manipulated in simple ways. For example, if there was +an image of a car, and you wanted it to move left, you could almost +just describe it in english, and that would be the animation. +\begin{verbatim} + movie = apply left car +\end{verbatim} + + After the simple model is done, converting it to a more complex model +is simple. Simply make the image, "car" in this case, more complex, and +then modify the "left" function, and you are done. + + There are three stages in making a movie. First of all, you must +define your basic images. These will tend to be Pics put into lists, either +finite or infinite, to be basic Movies. Second, you decide precisely +what kind of motion you want in animation. These are behaviours. A behaviour +modifies a movie over time, changing each successive frame. This includes +motion, changing size, changing from one image to another and so forth. These +are applied to your basic Movies. Third, you must combine your basic Movies +into your final Movie. If you want a scene of clouds and a man walking, you +must overlay your basic Movie of clouds with your Movie of a walking man. + +\section {Original Images or Pics} + + A Movie is a list of frames called Pics. Each of these Pics is a list +of colored polygons. The Pic is a Color followed by a list of Vectors, +representing the vertices of the Polygon. The original Pic usually must +be entered by hand, although simple generation routines for boxes, +triangles and circles are available. You need to produce some of these +basic images in one way or another, so that you have something to +manipulate. + + To make a Movie, you need a list of these Pics. With a single Pic, you +can generate a sequence of that Pic. With several Pics, you can oscillate +through the Pics in an inifinite list. To generate an infinite list of +Pics of p1, define a Movie, m1 = i p1. + The following datatypes are used in this package: + +\begin{verbatim} +type Vec = (Int,Int) +type Color = Int +type Poly = (Color,[Vec]) +type Pic = [Poly] +type Movie = [Pic] +type Behaviour = [Pic -> Pic] +\end{verbatim} + + +\subsection {Modifying Pics} + + Starting with a single Pic, it is possible to create a short list of +Pics to oscillate on. You can flip it, scale it, or otherwise modify the +original Pic (p1) in some way to create another Pic (p2). You can either +keep doing this and produce N images for a Movie of [p1,p2,...,pN], or use +the interpolation functions available to shift from p1 to p2 in N frames, +resulting in a Movie [p1,interp1,interp2,...,interpN-2,p2]. + The list of specific Pic-to-Pic functions is included in the next section, +along with short explanations of what they do. + +\subsection {Pic-to-Pic Functions Available} + +\begin{verbatim} +overlay_Pic Args: Pic Pic + This takes 2 Pics and just puts them together into one Pic. + module: R_Picture + +put_Pic Args: Vec Pic Pic + This overlays the two Pics, putting Pic-1's center the Vec + distance away from Pic-2's center. + module: R_Picture + +over_Pic Args: Pic Pic + This puts two images on top of one another, explicitly + centering the first on top of the second and forms one Pic. + module: R_Picture + +above_Pic Args: Pic Pic + This puts the first Pic above the second Pic, at a distance + of half the combined heights of the Pics and overlays them + to form one Pic. + module: R_Picture + +beside_Pic Args: Pic Pic + This puts the first Pic to the right of the second Pic, at + a distance of half the combined widths of the Pics and + overlays them to form one Pic. + module: R_Picture + +beside2_Pic Args: Pic Pic + Withouth analysing the widths of the Pics, it puts the + first Pic the width of the second Pic to the right and + overlays them to form one Pic. + module: R_Picture + +scale_Pic Args: Int Pic + This scales the picture in elevenths around its own origin + and returns that Pic. So if the Int is 22, the Pic will + scaled by a factor of 2 (22/11). + module: R_Picture + +scale_rel_Pic Args: Vev Int Pic + This is another scaling function, but it scales the image + from the Vec, treating it as the origin. + module: R_Picture + +mov_Pic Args: Vec Pic + This moves the Pic by the amount of the vector. + module: R_Picture + +movto_Pic Args: Vec Pic + This moves the Pic's center to the Vec. + module: R_Picture + +to_orig Args: Pic + This moves the Pic's center to the lower,left side of + the Pic. + module: R_Picture + +rot_Pic Args: Vec Float Pic + This rotates the Pic by the Float in radians, using the Vec + as the origin of rotation. + module: R_Picture + +twist_Pic Args: Float Pic + This rotates the Pic by the Float amount of radians around + its own center. + module: R_Picture + +rot_Pic' Args: Vec Pic + This rotates the Pic by a certain amount (set in R_Constants) + using the Vec as the center of rotation. The set amount of + rotation makes it faster than rot_Pic. + module: R_Picture + +twist_Pic' Args: Pic + This rotates the Pic by a certain amoutn (set in R_Constants) + around the Pic's origin. The set amount of rotation makes + it faster than twist_Pic. + module: R_Picture + +flipx_Pic Args: Int Pic + This flips the Pic around the line x=Int, essentially giving + a mirror image of the Pic, reversing right and left. + module: R_Picture + + +flipy_Pic Args: Int Pic + This flips the Pic around the line y=Int, mirror-imaging the + Pic, reversing up and down. + module: R_Picture + +flip_Pic Args: Pic + This flips the Pic around its own x-origin, reversing + left and right. + module: R_Picture + +flock_Pic Args: Int Pic + This takes the image Pic and copies it out Int*Int times in + a Int by Int grid pattern, and returns that as an Pic. + module: R_Picture + +set_Color Args: Int Pic + This takes an Int standing for a color, and changes the + color of the Pic to that. + module: R_Picture +\end{verbatim} + +\subsection{Other Functions for Manipulating Pics} + +\begin{verbatim} +i Args: Any + This will take anything and return an infinite stream of them. + module: R_Utility + +osc Args: [Any] + This will take a Movie, which is a list of Pics and + oscillate them. + [p1] will give [p1,p1,p1,p1....] + [p1,p2,p3,p4] will give [p1,p2,p3,p4,p3,p2,p1,p2...] + module: R_Utility +\end{verbatim} + +\section{Behaviours and their Application to Movies} + + A Behaviour is a list of functions that will convert one Pic to +another Pic. This list then can be applied to any Movie with one +of the application functions (most often apply). The beauty of the Behaviour +is that once you have a behaviour for moving left, you can move any +Movie left without rewriting the routine every time. + + There are specific functions that take a Behaviour and a Movie and +return a new Movie. These are apply and while. If you had a Movie of a +man walking in place, and a Behaviour called left that moves Pics ever +increasing distances left, then you could create a man walking left by: +\begin{verbatim} + apply left man +\end{verbatim} + + If you want to apply more than one Behaviour to a Movie, you must first +decide whether to do that in sequence or in parallel, and use bSeq and bPar +to reduce the list of Behaviours to a single Behaviour, and then apply +that to a movie. For example: +\begin{verbatim} + apply (bPar left up) gull +\end{verbatim} +will take a Movie of a gull and move the Pics up and left. + + Most of the basic Behaviours are defined in R\_Behaviour. + + +\subsection{Defining Customized Packages of Behaviours} + + Often you will have more specialized, or just simpler Behaviours you +want to use. Using the Behaviours and Pic-to-Pic functions, it is very +easy to create your own small library of Behaviours. R\_Defaults is a +module of such Behaviours. For example, to create a Behaviour to move +a Movie right, you would create a list of mov\_Pic's, each taking a +everincreasingly large x-coordinate. +\begin{verbatim} + right = [ mov_Pic (x,y) | (x,y) <- zip [0,10,..] [0,..] ] +\end{verbatim} + + Or if you wanted a behavour to place a Movie at (100,100) twice as +large as before, you could create a new Behaviour from old ones as: + scaleat= bPar [movto (i (100,100)), scale (i 22)] + +\subsection{Behaviours Available} +\begin{verbatim} +flip Args: none + This will flip every Pic around its x-origin, resulting in + mirror images reversing left and right. + module: R_Behaviour + +twist' Args: none + This will rotate each Pic by the amount rotunit (see + R_Constants) around its origin. + module: R_Behaviour + +mov Args: [Vec] + This will move each Pic by its corresponding vector. + module: R_Behaviour + +movto Args: [Vec] + This will move each Pic's origin to its corresponding vector. + module: R_Behaviour + +circ_mov Args: Float Float + This will move each Pic in a circle, of radius of the first + Float and by an increment of the second Float, using (0,0) + as the origin of rotation. + module: R_Behaviour + +scale Args: [Int] + Scales every Pic on its origin by the the corresponding Int + in the list. These Ints represents elevenths, so that a + [2,2,...] will scale every Pic by 2/11 . + module: R_Behaviour + +scale_rel Args: Vec [Int] + Same as scale, except that the Pics are all scaled using the + Vec as the point of origin. + module: R_Behaviour + +twist Args: [Float] + This will rotate every Pic by its corresponding Float from + the list in radians. + module: R_Behaviour + +set_color Args: [Int] + This sets each Pic to the color indicated by its + corresponding int in the list. + module: R_Behaviour + +rot Args: [Vec] [Float] + This will rotate each Pic around its corresponding Vec by + its corresponding Float in radians. + module: R_Behaviour + +big Args: none + Scales every Pic up by scaleunit + module: R_Defaults + +huge Args: none + This scales every Pic up by 2*scaleunit + module: R_Defaults + +small Args: none + This scales every Pic down by 10/11 + module: R_Defaults + +tiny Args: none + This scale every Pic down by 5/11 + module: R_Defaults + +bigger Args: none + This scales every Pic in the list by scaleunit more + than the previous Pic, so that the n-th element is + scaled up by (n-1)*scaleunit + module: R_Defaults + +smaller Args: none + This scales every Pic down, so that the n-th element + is scaled down by (n-1)*(10/11) + module: R_Defaults + +ccw Args: none + This rotates every Pic by one rotunit more than the + previous Pic, in a counterclockwise fashion. + module: R_Defaults + +cw Args: none + This rotates every Pic by one rotunit more than the + previous Pic, in a clockwise fashion. + module: R_Defaults + +up Args: none + This moves every Pic up by one unit more than the + Previous Pic, so that the n-th element is moved up + (n-1) units. + module: R_Defaults + +down Args: none + This is same as up, but the Pics move down. + module: R_Defaults + +right Args: none + This is same as up, but the Pics move right. + module: R_Defaults + +left Args: none + This is same as up, but the Pics move left. + module: R_Defaults +\end{verbatim} + +\subsection{Functions For Behaviours} + +\begin{verbatim} +do Args: Int Behaviour + This takes the first Int elements of the Behaviour and + return that. + module: R_Behaviour + +rpt Args: Int Behaviour + This takes an Int and returns a Behaviour of length Int. + However, the n-th Pic-to-Pic in the Behaviour returned + is made up of the first through (n-1)the Pic-to-Pics of + the input list. + module: R_Behaviour + +forever Args: Behaviour + This makes a finite Behaviour list an infinite one by + appending the list to itself endlessly. + module: R_Behaviour + +apply Args: Behaviour Movie + This takes a Behaviour and applies it to a Movie + module: R_Behaviour + +while Args: (Boolean function) Behaviour Movie + As long as the Boolean function evaluates true, this + takes a Behaviour and applies it to a Movie. When it + evaluates to false, no more Pics are produced and + the Movie is cut short there. + module: R_Behaviour + +bseq Args: Behaviour Behaviour + This takes two Behaviour and creates one Behaviour made + up of the two inputs applies in sequence. + module: R_Behaviour + +bSeq Args: [Behaviour] Behaviour + This takes two Behaviour and creates one Behaviour made + up of the two inputs applies in sequence. + module: R_Behaviour + +bpar Args: Behaviour Behaviour + This takes two Behaviour and creates one Behaviour made + up of the two inputs applies in parallel. + module: R_Behaviour + +bPar Args: [Behaviour] Behaviour + This takes two Behaviour and creates one Behaviour made + up of the two inputs applies in parallel. + module: R_Behaviour +\end{verbatim} + +\section{Creating the Final Movie} + + Finally, you have your basic Movies made up of Pictures and Behaviours. +Now you need to combine them into one Movie. The functions that do this +are found in the module R\_Movie. These functions will take a list of +Movies and return a single Movie combining all the Movies in the list. +How they are combined can be controlled to some extent. Usually they are +just overlayed, but they can be put beside one another, or on top of +one another, or put a Vec distance apart. + + It is also possible to use a combination of these forms. If you wanted +to overaly M1 and M2, and then put that beside M3, you would do: +\begin{verbatim} + rBESIDE [M3, rOVERLAY [M1,M2] ] +\end{verbatim} +This is acceptable as rOVERLAY will return a single Movie. + +\subsection{Movie Combining Functions} + +\begin{verbatim} +rABOVE Args: [Movie] + Puts all the Movies into one movie, all above one another. + module: R_Movie + +rBESIDE Args: [Movie] + Puts all the Movies into one movie, all beside one another. + module: R_Movie + +rBESIDE2 Args: [Movie] + Using their absolute coordinates, puts all the Movies + beside one another. + module: R_Movie + +rOVER Args: [Movie] + This lays the Movies on top of one another, centering + each Pic so that they share the same origin. + module: R_Movie + +rOVERLAY Args: [Movie] + This lays the Movies on top of one another, centering + each Pic so that they share the smae origin. + module: R_Movie + +pUT Args: [Vec] Movie Movie + This takes a list of Vec, and puts each Pic of the + first Movie in the location of the corresponding + Vec on top of the Pic of the second Movie and + returns that list as the new Movie. + module: R_Movie + +\end{verbatim} + +\section{Displaying Your Movie} + + Once you have your function for the Movie defined, you need to output +it in some way. Currently, this is done by outputting characters to a file and +running a C Program in X-Windows that displays the contents of the file +as a graphic in the X system. First of all, you must convert the +Movie variable to a stream of characters. This is done by running +"showm" on the Movie. Be carefull you don't try to convert an infinite list +into characters as the compiler will take awhile to do this. Instead, take +a certain number of frames and convert them with "showm". +\begin{verbatim} + man\_vm = rOVERLAY [man,vm] + man\_vmstring = showm (take 20 man&vm) +\end{verbatim} + Now that you have this string, you need to write it to disk. The +"writetofile" function does this. It takes a characater string(ie [Char] ) +as an argument, and then prompts you for a filename. It then writes the +string to the filename. So to put man\_vm string into a file: +\begin{verbatim} + main = writetofile man_vmstring +\end{verbatim} +and run the program, where you will prompted for the filename. Or you could: +\begin{verbatim} + main = writetofile (showm (take 20 man_vm)) +\end{verbatim} +to make it more compact. + + +\subsection{Miscellaneous Usefull Functions} + +\begin{verbatim} +inbetween Args: Int Pic Pic + This takes an Int and two Pics and returns a Movie + with Int Pics interpolating between the two Pics. + module: R_Inbetween + +tween Args: Int Movie Movie + This takes an Int and two Movies and returns one + Movie made up of the first Movie, Int number of + frames of Pics interpolating between the last + Pic of the first Movie and the first Pic of the + second Movie, followed by the second Movie + module: R_Inbetween + +box Args: Int Int Int + This takes 3 Ints, the color, width and height of + the box and returns a Pic of a box + module: R_Shapes + +tri Args: Int Vec Vec Vec + This takes a color and three vectors and returns a + Pic of a triangle of that colour with those vertices. + module: R_Shapes + +circ Args: Int Int Int + This takes a color, the radius and the number of points + around the circle, and returns a circle with origin at + (0,0). + module: R_Shapes +\end{verbatim} + +\pagebreak +\large {\bf Appendix: C Programs to Display on X-Windows} +\\ +\\ + The program currently used to run these graphics is called "xcshow". +This takes one argument, the name of the file to be run. When run +in X-Windows, it will produce a window with the first Pic. To run it, click +on the left mouse button inside the window. Clicking again will freeze it. +This will keep cycling through the file, replaying again when it hits the +end of the file, until the window is killed. + + There is also "xshow" which is used to run the monochrome Movies, as +"xcshow" is used to run the color Movies. As this animation package +only produces color Movies, it isn't too usefull. + + +\pagebreak +\large {\bf References} +\\ +\\ +\begin{verbatim} +[ARYA88] "The Formal Analysis of a Functiona Animation System", Kevi Arya, + DPhil,Thesis,Oxford University, Programming Research Group, + April 1988 + +[ARYA89] "Processes In A Functional Animation System", Kevi Arya,IBM + T.J. Research Center, 1989 + +[HASK90] "Report On The Programming Language Haskell, Version 1.0", + YALEU/DCS/RR-777,Yale University,1990 +\end{verbatim} + +\end{document} diff --git a/progs/demo/X11/animation/palm.hs b/progs/demo/X11/animation/palm.hs new file mode 100644 index 0000000..9deef12 --- /dev/null +++ b/progs/demo/X11/animation/palm.hs @@ -0,0 +1,47 @@ +module Palm (main) where + +import Animation +import SeaFigs + +main = getEnv "DISPLAY" exit + (\ host -> displaym host 30 trans) + +trans :: Movie +trans = manright++change++gull2 + +manright::Movie +manright = mirrorx (take 10 (apply left man)) + +gull2::Movie +gull2 = apply (bPar [right,up,huge,huge,huge,(mov (i (275,0)))]) gull + +change::Movie +change = inbetween 5 manf1 gull1 + where gull1 = head gull2 + manf1 = last manright + + + +mirrorx :: Movie -> Movie +mirrorx m = map (flipx_Pic x) m + where (x,_)=orig_Movie m + + +orig_Movie :: Movie -> Vec +orig_Movie m = ((x2-x1) `div` 2,(y2-y1) `div` 2) + where x2 = reduce max (map maxx m) + x1 = reduce min (map minx m) + y2 = reduce max (map maxy m) + y1 = reduce min (map miny m) + +maxx :: Pic -> Int +maxx p = reduce max [x | (c,q) <- p, (x,y) <- q] + +minx :: Pic -> Int +minx p = reduce min [x | (c,q) <- p, (x,y) <- q] + +maxy :: Pic -> Int +maxy p = reduce max [y | (c,q) <- p, (x,y) <- q] + +miny :: Pic -> Int +miny p = reduce min [y | (c,q) <- p, (x,y) <- q] diff --git a/progs/demo/X11/animation/palm.hu b/progs/demo/X11/animation/palm.hu new file mode 100644 index 0000000..a86fcb9 --- /dev/null +++ b/progs/demo/X11/animation/palm.hu @@ -0,0 +1,3 @@ +:o= all +palm.hs +seafigs.hu diff --git a/progs/demo/X11/animation/planets.hs b/progs/demo/X11/animation/planets.hs new file mode 100644 index 0000000..38f278a --- /dev/null +++ b/progs/demo/X11/animation/planets.hs @@ -0,0 +1,30 @@ +module Planets (main) where + +import Animation + +planets:: Float -> Float -> Int -> Int -> Int -> Int -> Movie +planets i1 i2 r1 r2 c1 c2 + = rOVERLAY + [ apply f1 earth, + apply (bpar f1 f2) moon + ] + where f1 = circ_mov (fromIntegral r1) i1 + f2 = circ_mov (fromIntegral r2) i2 + earth = osc [mov_Pic (vplus center (r1,0)) (box c1 30 30)] + moon = osc [mov_Pic (vplus center (r1+r2,0)) (box c2 15 15)] + +gen a b c d = c :(gen a b (c+b) d) + + +planet_scene:: Movie +planet_scene = rOVERLAY + [apply (bpar (set_color (i yellow)) (movto (i center))) orb, + planets (pi/40.0) (pi/10.0) 450 80 darkblue lightblue, + planets (pi/20.0) (pi/8.0) 300 50 brown black, + planets (pi/10.0) (pi/4.0) 150 40 green red + ] + +orb = osc [circ red 50 10] + +main = getEnv "DISPLAY" exit + (\ host -> displaym host 60 planet_scene) diff --git a/progs/demo/X11/animation/planets.hu b/progs/demo/X11/animation/planets.hu new file mode 100644 index 0000000..3473372 --- /dev/null +++ b/progs/demo/X11/animation/planets.hu @@ -0,0 +1,3 @@ +:o= all +planets.hs +animation.hu diff --git a/progs/demo/X11/animation/r_behaviour.hs b/progs/demo/X11/animation/r_behaviour.hs new file mode 100644 index 0000000..6f58e3b --- /dev/null +++ b/progs/demo/X11/animation/r_behaviour.hs @@ -0,0 +1,158 @@ +{-********************************************************************** + MODULE R_BEHAVIOUR + + This module defines the basic Behaviours available to manipulate + Movies. These functions can either be used directly, or used to + easily create personnalized Behaviours (see R_Defaults). + There are the Behaviours that affect one Movie, which are mov,movto + circ_mov,scale,scale_rel,rot,flip and set_color. These change some + aspect of the movie over time. + There are functions that combine several movies into one, namely + bseq,bSeq,bpar and bPar. + Some functions modify the Behaviours. These are do, rpt and forever. + They put limits on how long the Behaviour is. + Finally, there are the functions that apply the Behaviours to a Movie. + These are apply and while. Apply applies a Behaviour to a Movie until + it runs out of Movie or Behaviour. While takes a conditional and + applies the Behaviour to it until that condition is fullfilled. + +***********************************************************************-} + +module R_Behaviour (mov,movto,circ_mov,scale,scale_rel,rot,flipb, + set_color, + bseq,bSeq,bpar,bPar, + do,rpt,forever, + apply,while ) where + +import R_Ptypes +import R_Utility +import R_Picture + + -- takes a Pic to Pic and makes an infinite list Behaviour out of it +makeb1 :: (Pic->Pic) -> Behaviour +makeb1 f = f : makeb1 f + + -- takes a movie and flips it around the x-origin using flip_Pic +flipb :: Behaviour +flipb = makeb1 flip_Pic + + -- twist makes twist_Pic into a Behaviour, rotating the image by rotunit +twist' :: Behaviour +twist' = makeb1 twist_Pic' + + -- makeb2 makes a Behaviour out of a function that takes a list and a + -- function and outputs a Behaviour. +makeb2 :: (a->Pic->Pic) -> [a] -> Behaviour +makeb2 f [] = [] +makeb2 f (v:vs) = f v : makeb2 f vs + + -- mov takes a list of Vec's and applies each Pic-to-Pic in the Behaviour + -- list to its corresponding Vec, and gives back a new Behaviour +mov :: [Vec] ->Behaviour +mov = makeb2 mov_Pic + + -- movto creates a list of Pic-to-Pic Behaviours that move each Pic to its + -- corresponding Vec +movto :: [Vec] -> Behaviour +movto = makeb2 movto_Pic + + -- produces a Behaviour that produces movement in a circle, taking + -- the radius and the increment as arguments. +circ_mov :: Float -> Float -> Behaviour +circ_mov r inc = mov (map (vmin' (head vs')) vs') + where vs = [ (r*(cos theta),r*(sin theta)) | + theta <- gen inc 0.0 ] + vmin' x y = vmin y x + vs' = map vftov vs + +gen :: Float -> Float -> [Float] +gen b c = c : (gen b (c+b) ) + + + -- scale outputs a list of Pic-to-Pic's that scale according to its + -- corresponding Int in the input list +scale :: [Int] -> Behaviour +scale = makeb2 scale_Pic + + -- scale_rel does the same thing, but centers on the lower-left corner of + -- the image +scale_rel :: Vec -> [Int] -> Behaviour +scale_rel v = makeb2 (scale_rel_Pic v) + + -- twist outputs a list of Behaviours that rotate each pick by its + -- corresponding Float in the list +twist :: [Float] -> Behaviour +twist = makeb2 twist_Pic + + -- set_color takes a list of Colors, and returns a list of Pic-to-Pic's + -- that change to the corresponding color in the list +set_color :: [Color] -> Behaviour +set_color = makeb2 set_Color_Pic + + -- makeb3 takes a function with two inputs, and two input lists and + -- returns a behaviour made up of functions with inputs fromt the lists +makeb3 :: (a->b->Pic->Pic) -> [a] -> [b] -> Behaviour +makeb3 f [] (p:ps) = [] +makeb3 f (v:vs) [] = [] +makeb3 f (v:vs) (p:ps) = f v p : makeb3 f vs ps + + -- rot produces behaviours rotating by the Float, around the point + -- of the Vec, both provided by lists. +rot :: [Vec] -> [Float] -> Behaviour +rot = makeb3 rot_Pic + + -- bseq takes two Behaviours and combines them into one, in sequence. + -- It first applies all of the first Behaviour, then all of the second +bseq :: Behaviour -> Behaviour -> Behaviour +bseq ps [] = [] +bseq [] qs = [] +bseq ps qs = ps ++ (mapc (last ps) qs) + + -- bSeq takes a list of Behaviour and makes them into one Behaviour, in + -- sequence. +bSeq :: [Behaviour] -> Behaviour +bSeq = reduce bseq + + -- bpar takes two behaviours and applies them both simultaneously, + -- producing a list of Pic-to-Pic's, each one made up of a function + -- from the first list combined with a function from the second list +bpar :: Behaviour -> Behaviour -> Behaviour +bpar [] (q:qs) = [] +bpar (p:ps) [] = [] +bpar (p:ps) (q:qs) = (p.q):(bpar ps qs) + + -- bPar takes a list of Behaviours and makes them all into one Behaviour, + -- in paralell +bPar :: [Behaviour] -> Behaviour +bPar = reduce bpar + + -- takes the first n POic-to-Pics in a Behaviour and returns that Behaviour +do :: Int -> Behaviour -> Behaviour +do n f = take n f + + -- applies bseq to the list of behaviours, so that the nth element of + -- the returned list has n-1 behaviours in it, applied in sequence +rpt :: Int -> Behaviour -> Behaviour +rpt n f = replicate n bseq [] f + + -- takes the behaviour and applies all the behaviours up the nth element + -- to the nth element, in an infinite list +forever :: Behaviour -> Behaviour +forever f = bseq f (forever f) + + -- takes a behaviour, applies each from to a Pic in a Movie and returns + -- the new Movie +apply :: Behaviour -> Movie -> Movie +apply [] ms = [] +apply fs [] = [] +apply (f:fs) (m:ms) = (f m):(apply fs ms) + + -- applies the Behaviour to the Movie until the condition is fullfilled, + -- then returns the movie to that point +while :: (Pic -> Bool) -> Behaviour -> Movie -> Movie +while c [] ms = [] +while c fs [] = [] +while c (f:fs) (m:ms) = if (c m) then ( (f m):(while c fs ms)) + else [] + + diff --git a/progs/demo/X11/animation/r_behaviour.hu b/progs/demo/X11/animation/r_behaviour.hu new file mode 100644 index 0000000..afa52bb --- /dev/null +++ b/progs/demo/X11/animation/r_behaviour.hu @@ -0,0 +1,3 @@ +:o= all +r_behaviour.hs +r_picture.hu diff --git a/progs/demo/X11/animation/r_constants.hs b/progs/demo/X11/animation/r_constants.hs new file mode 100644 index 0000000..f36e3f2 --- /dev/null +++ b/progs/demo/X11/animation/r_constants.hs @@ -0,0 +1,129 @@ +{-**************************************************************** + MODULE R_CONSTANTS + + This module sets up all the constants used in this functional + animation package. + Defined here are the basic units of movement, scale and rotation. + The screen height and width are set, and the various parts of + the screen such as the top-middle, lower-left and center are + all set. Finally the color values used by xcshow, the c-program + that displays the movies in X, are set. + +******************************************************************-} + +module R_Constants (fps, unit, hf, qt, scaleunit, rotunit, + nullpic, nullseq, + sinunit,cosunit, + screenwid, screenht, botl, leftm, topl, topm, topr, + rightm, botr, botm, center, + white,black,red,green,darkblue,lightblue,brown,yellow, + colorName, allColors + ) where + +import R_Ptypes + + -- units are set. The scaleunit is in 11th, so that the 12 is + -- actually 12/11'ths +fps :: Int +unit :: Int +hf :: Int +qt :: Int +scaleunit :: Int +fps = 25 +unit = 15 +hf = unit `div` 2 +qt = unit `div`4 +scaleunit = 12 + --scaleunit is div'ed by 12 later + +rotunit :: Float +rotunit = pi/18 +sinunit = sin rotunit +cosunit = cos rotunit + + +nullpic :: Pic +nullpic = [] +nullseq :: Movie +nullseq= nullpic : [ nullseq2 | nullseq2 <- nullseq] + + -- Screen Parameters +screenwid :: Int +screenwid = 800 +screenht :: Int +screenht = 800 + +botl :: Vec +leftm :: Vec +topl :: Vec +topm :: Vec +topr :: Vec +rightm :: Vec +botr :: Vec +botm :: Vec +center :: Vec + +leftmb :: Vec +leftmt :: Vec +topml :: Vec +topmr :: Vec +rightmt :: Vec +rightmb :: Vec +botml :: Vec +botmr :: Vec + +botl = ( 0, 0 ) +leftm = ( 0, screenht `div` 2) +topl = ( 0, screenht ) +topm = ( screenwid `div` 2, screenht ) +topr = ( screenwid, screenht ) +rightm = ( screenwid, screenht `div` 2 ) +botr = ( screenwid, 0 ) +botm = ( screenwid `div` 2, 0 ) +center = ( screenwid `div` 2, screenht `div` 2 ) + +leftmb = ( 0, screenht `div` 4 ) +leftmt = ( 0, (screenht*3) `div` 4 ) +topml = ( screenwid `div` 4, screenht ) +topmr = ( (screenwid*3) `div` 4, screenht ) +rightmt = ( screenwid, (screenht*3) `div` 4 ) +rightmb = ( screenwid, screenht `div` 4 ) +botml = ( screenwid `div` 4, 0 ) +botmr = ( (screenwid*3) `div` 4, 0 ) + + -- Colors values set to names + +white :: Color +white = 1 +black :: Color +black = 2 +red :: Color +red = 4 +green :: Color +green = 6 +darkblue :: Color +darkblue = 8 +lightblue :: Color +lightblue = 10 +yellow :: Color +yellow = 12 +brown :: Color +brown = 14 + +colorName :: Color -> String +colorName 1 = "white" +colorName 2 = "black" +colorName 4 = "red" +colorName 6 = "green" +colorName 8 = "blue" +colorName 10 = "lightblue" +colorName 12 = "yellow" +colorName 14 = "brown" + +allColors :: [Color] +allColors = [1,2,4,6,8,10,12,14] + + + + + diff --git a/progs/demo/X11/animation/r_constants.hu b/progs/demo/X11/animation/r_constants.hu new file mode 100644 index 0000000..d61580f --- /dev/null +++ b/progs/demo/X11/animation/r_constants.hu @@ -0,0 +1,3 @@ +:o= all +r_constants.hs +r_ptypes.hu diff --git a/progs/demo/X11/animation/r_curve.hs b/progs/demo/X11/animation/r_curve.hs new file mode 100644 index 0000000..14d288c --- /dev/null +++ b/progs/demo/X11/animation/r_curve.hs @@ -0,0 +1,60 @@ +{-************************************************************** + MODULE R_CURVE + + This module produces sequences of numbers to be used by + Behaviours. The sequences used for moving or scaling can + be produced here, in either linear sequences or accelerating + and decelerating sequences. + The acceleration functions produce floats, so the vftov function + would have to be used to convert floating point vectors to integer + vectors. + +***************************************************************-} + +module R_Curve(lnr,hold, acc, dec, accdec, decacc) where + +import R_Ptypes +import R_Constants +import R_Utility +import R_Picture +import R_Behaviour + + -- lnr takes the start, finish and the number of intervals and + -- produces a linear list of ints going from the start to finish. +lnr :: Int -> Int -> Int ->[Int] +lnr start fin n = take n [start,(start+step)..] + where step = ((fin-start)`div`(n-1)) + + -- hold produces an infinite number of ints starting at v, modified + -- by step every time. +hold :: Int -> Int -> [Int] +hold v step = [v,v+step..] + + -- acc accelerates from 0 to the max in n steps. +acc :: Int -> Int -> Int -> [Int] +acc min max n = min:acc' min (max-min) n 1 + +acc' :: Int -> Int -> Int -> Int -> [Int] +acc' min max n c | (c>n) = [] +acc' min max n c = (min + (((max*c*c) `div` (n*n)))) + : (acc' min max n (c+1)) + + + -- dec decelerates from the max to 0 in n steps. +dec :: Int -> Int -> Int -> [Int] +dec min max n = reverse (acc min max n) + + -- accdec accelerates from start up to max and back to fin, in an steps + -- accelerating and dn steps decelerating +accdec :: Int -> Int -> Int -> Int -> Int -> [Int] +accdec start max fin an dn = (acc start max an)++(tail (dec fin max dn)) + + -- decacc decelerates from start to min in dn steps and then accelerates + -- back up to fin in an more steps +decacc :: Int -> Int -> Int -> Int -> Int -> [Int] +decacc start min fin dn an = (dec min start dn)++(tail (acc min fin an)) + + + + + diff --git a/progs/demo/X11/animation/r_curve.hu b/progs/demo/X11/animation/r_curve.hu new file mode 100644 index 0000000..9aa9629 --- /dev/null +++ b/progs/demo/X11/animation/r_curve.hu @@ -0,0 +1,3 @@ +:o= all +r_curve.hs +r_behaviour.hu diff --git a/progs/demo/X11/animation/r_defaults.hs b/progs/demo/X11/animation/r_defaults.hs new file mode 100644 index 0000000..1b7070a --- /dev/null +++ b/progs/demo/X11/animation/r_defaults.hs @@ -0,0 +1,76 @@ +{-**************************************************************** + MODULE R_DEFAULTS + + This module uses the R_Behaviour module to define convient and + easy to use behaviours. These aren't very sophistated, but they + can be used to quickly animate a movie. For more sophistated + animation, a similiar library of sophistocated personnalized + functions can be created. + +******************************************************************-} + +module R_Defaults (big, huge, bigger, smaller, ccw, cw, + up, down, left, right,small,tiny) +where + +import R_Ptypes +import R_Constants +import R_Utility +import R_Picture +import R_Behaviour + + + -- big scales everything up by the scaleunit (now 12/11ths) +big :: Behaviour +big = [scale_Pic x | x <- [scaleunit,scaleunit..]] + + -- huge scales everything up by twice the scaleunit (24/11ths) +huge :: Behaviour +huge= [scale_Pic x | x <- [scaleunit*2,(scaleunit*2)..]] + + -- small scales everything down by 10/11ths +small :: Behaviour +small = [scale_Pic x | x <- [s,s..]] + where s = 10 + + -- tiny scales everything down by 5/11ths +tiny :: Behaviour +tiny = [scale_Pic x | x <- [s,s..]] + where s = 5 + + -- bigger causes the Pics to be scaled up by 12/11ths,24/11ths,36/11ths + -- and so on, everincreasing. +bigger :: Behaviour +bigger = [scale_Pic x | x <- (rept (\x -> div (x*scaleunit) 11) 1)] + + -- smaller causes the Pics to be scaled downwards in ever decreasing + -- amounts. +smaller :: Behaviour +smaller = [scale_Pic x | x <- (rept (\x -> div (x*10) 11) 1)] + + -- a hardwired version of ccw that rotates the Pics by one rotunit + -- more every Pic, counterclockwise. +ccw :: Behaviour +ccw = [twist_Pic x | x <- [0.0,rotunit..]] + + -- same as ccw, but rotates the Pics clockwise +cw :: Behaviour +cw = [twist_Pic x | x <- [0.0,-rotunit..]] + + -- moves the Pic up by one more unit every Pic. +up :: Behaviour +up = [mov_Pic (x,y) | (x,y)<- zip2 [0,0..] [0,unit..]] + + -- moves the Pic down by one more unit every Pic. +down :: Behaviour +down = [mov_Pic (x,y) | (x,y)<-zip2 [0,0..] [0,-unit]] + + -- moves the Pic left by one more unit every Pic. +left :: Behaviour +left = [mov_Pic (x,y) | (x,y)<- zip2 [0,-unit..] [0,0..]] + + -- moves the Pic right by one more unit every Pic. +right :: Behaviour +right = [mov_Pic (x,y) | (x,y)<- zip2 [0,unit..] [0,0..]] + + diff --git a/progs/demo/X11/animation/r_defaults.hu b/progs/demo/X11/animation/r_defaults.hu new file mode 100644 index 0000000..f945bbc --- /dev/null +++ b/progs/demo/X11/animation/r_defaults.hu @@ -0,0 +1,3 @@ +:o= all +r_defaults.hs +r_behaviour.hu diff --git a/progs/demo/X11/animation/r_display.hs b/progs/demo/X11/animation/r_display.hs new file mode 100644 index 0000000..19f1d4a --- /dev/null +++ b/progs/demo/X11/animation/r_display.hs @@ -0,0 +1,114 @@ +module R_Display (displaym) where + +import R_Ptypes +import R_Utility +import Xlib +import R_Constants + +displaym :: String -> Int -> Movie -> IO () + +displaym host n movie = + let + movie' = cycle (take n (map (map translatePoly) movie)) + in + xOpenDisplay host + `thenIO` \ display -> + let (screen:_) = xDisplayRoots display + fg_color = xScreenBlackPixel screen + bg_color = xScreenWhitePixel screen + color_map = xScreenDefaultColormap screen + getPixels [] = returnIO [] + getPixels (c:cs) = + xLookupColor color_map c `thenIO` \ (xc, _) -> + xAllocColor color_map xc `thenIO` \ (p,_,_) -> + getPixels cs `thenIO` \ ps -> + returnIO (p:ps) + in + getPixels (map colorName allColors) + `thenIO` \ pixels -> + let + lookupPixel c = lookupPixel1 c allColors pixels + + lookupPixel1 x [] _ = head pixels + lookupPixel1 x (c:cs) (p:ps) = + if x == c then p + else lookupPixel1 x cs ps + parent = xScreenRoot screen + in + xMArrayCreate [lookupPixel i | i <- [0..15]] + `thenIO` \ pixelArray -> + xCreateGcontext (XDrawWindow parent) + [XGCBackground bg_color, + XGCForeground fg_color] + `thenIO` \ gcontext -> + xCreateGcontext (XDrawWindow parent) + [XGCBackground bg_color, + XGCForeground bg_color] + `thenIO` \ blank_gcontext -> + xCreateWindow parent + (XRect 100 100 500 500) + [XWinBackground bg_color, + XWinEventMask (XEventMask [XButtonPress])] + `thenIO` \window -> + let depth = xDrawableDepth (XDrawWindow window) + in + xCreatePixmap (XSize 500 500) depth (XDrawWindow parent) + `thenIO` \ pixmap -> + xMapWindow window + `thenIO` \() -> + let + dispFrame m = + xDrawRectangle (XDrawPixmap pixmap) + blank_gcontext + (XRect 0 0 500 500) + True + `thenIO_` + dispPic m + `thenIO_` + xCopyArea (XDrawPixmap pixmap) gcontext (XRect 0 0 500 500) + (XDrawWindow window) (XPoint 0 0) + `thenIO_` + xDisplayForceOutput display + + dispPic [] = returnIO () + dispPic (p:ps) = dispPoly p `thenIO_` dispPic ps + + dispPoly (c, vec) = +-- xLookupColor color_map (colorName c) `thenIO` \ ec -> +-- xAllocColor color_map ec `thenIO` \ p -> + xMArrayLookup pixelArray c `thenIO` \p -> + xUpdateGcontext gcontext [XGCForeground p] `thenIO` \ () -> +-- xSetGcontextForeground gcontext (lookupPixel c) `thenIO` \ () -> + xDrawLines (XDrawPixmap pixmap) gcontext vec True + + untilButton3 (frame:frames) = + let + action = dispFrame frame `thenIO_` untilButton3 frames + in + xEventListen display `thenIO` \count -> + if count == 0 then action else + xGetEvent display `thenIO` \event -> + case (xEventType event) of + XButtonPressEvent -> + case (xEventCode event) of + 3 -> returnIO () + _ -> action + _ -> action + in + printString ("Click right button to end.\n") `thenIO_` + untilButton3 movie' `thenIO_` + xFreePixmap pixmap `thenIO_` + xCloseDisplay display + +type Movie' = [Pic'] +type Pic' = [Poly'] +type Poly' = (Int, [XPoint]) + +translatePoly :: Poly -> Poly' +translatePoly (c, vs) = (c, flatten_2 vs) + +flatten_2 [] = [] +flatten_2 ((a,b):r) = (XPoint (a `div` 2) (b `div` 2)):(flatten_2 r) + +printString :: String -> IO () +printString s = appendChan "stdout" s abort (returnIO ()) diff --git a/progs/demo/X11/animation/r_display.hu b/progs/demo/X11/animation/r_display.hu new file mode 100644 index 0000000..23f2c77 --- /dev/null +++ b/progs/demo/X11/animation/r_display.hu @@ -0,0 +1,6 @@ +:o= foldr inline constant +r_constants.hu +r_utility.hu +r_ptypes.hu +r_display.hs +$HASKELL_LIBRARY/X11/xlib.hu diff --git a/progs/demo/X11/animation/r_inbetween.hs b/progs/demo/X11/animation/r_inbetween.hs new file mode 100644 index 0000000..a7fb7d3 --- /dev/null +++ b/progs/demo/X11/animation/r_inbetween.hs @@ -0,0 +1,82 @@ +{-****************************************************************** + MODULE R_INBETWEEN + + This module takes care of interpolation functions. Basically, + given two Pics, inbetween will give you a movie gradually + converting from one Pic to the other Pic, using linear interpolation. + Tween will take two Movies, and append them, interpolating n + frames between the last Pic of the first Movie and the first Pic of + the last Movie. + +******************************************************************-} + +module R_Inbetween (inbetween,tween) where + +import R_Ptypes +import R_Utility +import R_Picture +import R_Behaviour + + -- inbetween takes an int and two Pics, and interpolates n Pics + -- of interpolated Pics. +inbetween :: Int -> Pic -> Pic -> Movie +inbetween n p1 p2 | (length p1 == length p2) = + ((zip1.(map (inbetweenp n))).zip1) [p1,p2] +inbetween n p1 p2 = inbetween n [(col,p1')] [(col,p2')] + where p1' = concat [ vs | (c,vs) <- p1] + p2' = concat [ vs | (c,vs) <- p2] + col = head [ c | (c,vs) <- p1 ] + + -- inbetweenp takes a list of 2 Polygons ([[Vec]]) and returns a + -- sequence of interpolated Polygons. Should the Number of vertices + -- of one Polygon be less than those in the other, it splits it so + -- as to have two Polygons of the same length. +inbetweenp :: Int -> Pic -> Pic +inbetweenp n [(c1,vs),(c2,ws)] = + if ((length vs) < (length ws)) then + inbetween1 (split (length ws) (c1,vs)) (c2,ws) 0 n + else if ((length vs) > (length ws)) then + inbetween1 (c1,vs) (split (length vs) (c2,ws)) 0 n + else inbetween1 (c1,vs) (c2,ws) 0 n + + + -- inbetween1 returns a sequence of interpolated Polygons. +inbetween1 :: Poly -> Poly -> Int -> Int -> Pic +inbetween1 p1 p2 m n | m>n || n<=0 = [] +inbetween1 p1 p2 m n = inbetween2 p1 p2 m n + :inbetween1 p1 p2 (m+1) n + + -- inbetween2 returns ONE of the required sequence of + -- interpolated Polygons. +inbetween2 :: Poly -> Poly -> Int -> Int -> Poly +inbetween2 (c1,vs) (c2,ws) p q = (c1, map (partway p q) (zip1 [vs,ws])) + + -- split splits up a Polygon so as to have the given #vertices. +split :: Int -> Poly -> Poly +split n (c,vs) = (c, split' n vs) + +split' :: Int -> [Vec] -> [Vec] +split' n vs | n<= (length vs) = vs +split' n vs = if (n>double) then + split' n (split' double vs) + else + v1:(mid v1 v2):(split' (n-2) (v2:vss)) + where double = 2*((length vs)) - 1 + (v1:v2:vss) = vs + + + -- tween will interpolate n Pics transforming the last Pic of + -- the first Movie into the first Pic of the second Movie, and + -- then run the second Movie +tween :: Int -> Movie -> Movie -> Movie +tween n m1 [] = m1 +tween n m1 m2 = m1 ++ (inbetween n (last m1) (head m2')) ++ (tail m2') + where m2' = apply (mov (repeat v)) m2 + v = vmin (orig_Pic (last m1)) (orig_Pic (head m2)) + + -- tweens will take a list of Movies and append them all, interpolating + -- n frames between every Movie. +tweens :: Int -> [Movie] -> Movie +tweens n = foldr (tween n) [] + + diff --git a/progs/demo/X11/animation/r_inbetween.hu b/progs/demo/X11/animation/r_inbetween.hu new file mode 100644 index 0000000..52771d0 --- /dev/null +++ b/progs/demo/X11/animation/r_inbetween.hu @@ -0,0 +1,3 @@ +:o= all +r_inbetween.hs +r_behaviour.hu diff --git a/progs/demo/X11/animation/r_movie.hs b/progs/demo/X11/animation/r_movie.hs new file mode 100644 index 0000000..a97a452 --- /dev/null +++ b/progs/demo/X11/animation/r_movie.hs @@ -0,0 +1,114 @@ +{-********************************************************************* + MODULE R_MOVIE + + This module contains necessary functions for editing Movies. There + are several that give information on a Movie, such as the heights or + wirdths of its Pics. The others all deal with the various ways of + combining various Movies into one Movie, a vital set of functions. + +*********************************************************************-} + +module R_Movie (ht, wid, orig, + above, rABOVE, beside, rBESIDE,rBESIDE2, over, rOVER, + overlay, rOVERLAY, pUT, + uncurry, curry + ) where + +import R_Ptypes +import R_Constants +import R_Utility +import R_Picture + + -- takes a function and a list and returns a new list of element operated + -- on by the function. +promote:: (a->b)->[a]->[b] +promote f [] = [] +promote f [p] = f p:promote f [p] +promote f (p:ps) = f p:promote f ps + + -- promote1 takes a function that analyzes a Pic, and then applies it + -- to analyse a movie, returning a list. +promote1:: (Pic->a) -> Movie -> [a] +promote1 f ps = [f p | p <- ps] + + -- ht takes a Movie and returns a list of the heights of the Pics +ht :: Movie -> [Int] +ht = promote1 ht_Pic + + -- wid takes a Movie and returns a list of the widths of the Pics +wid :: Movie -> [Int] +wid = promote1 wid_Pic + + -- orig takes a Movie and returns a list of vectors that are the + -- origins of the Pics +orig:: Movie -> [Vec] +orig = promote1 orig_Pic + + -- promote2 takes a function accepting an element and a Pic, and + -- applies the function to the Movie and list, producing a new Movie +promote2:: (a->Pic->Pic) -> [a] -> Movie -> Movie +promote2 f ps qs = [f p q | (p,q) <- zip2 ps qs] + + -- takes two Movies and puts them above one another +above:: Movie -> Movie -> Movie +above = promote2 above_Pic + + -- takes a list of Movies and puts them all above one another +rABOVE:: [Movie] -> Movie +rABOVE = reduce above + + -- takes two Movies and puts them beside one another +beside:: Movie -> Movie -> Movie +beside = promote2 beside_Pic + + -- takes a list of Movies and puts them all beside one another +rBESIDE:: [Movie] -> Movie +rBESIDE = reduce beside + + -- same as beside, but with absolute coordinates. +beside2:: Movie -> Movie -> Movie +beside2 = promote2 beside2_Pic + + -- same as rBESIDE, but with absolute coordinates. +rBESIDE2:: [Movie] -> Movie +rBESIDE2 = reduce beside2 + + -- puts one Movie on top of the other Movie +over:: Movie -> Movie -> Movie +over = promote2 over_Pic + + -- takes a list of Movies, and puts the n-th on top of the first + -- through 9n-1)th. +rOVER:: [Movie] -> Movie +rOVER = reduce over + + -- just overlays the two Movies by appending the Pics. +overlay:: Movie -> Movie -> Movie +overlay = promote2 overlay_Pic + + -- overlays a list of Movies by appending the Pics +rOVERLAY:: [Movie] -> Movie +rOVERLAY = reduce overlay + + -- promote3 takes a function that takes two items and a Pic and + -- returns a Pic, and then applies it to two input lists and a Movie, + -- producing a new Movie. +promote3:: (a->b->Pic->Pic) -> [a] -> [b] -> Movie -> Movie +promote3 f ps qs rs = [f p q r | (p,q,r) <- zip3 ps qs rs] + + -- pUT takes a list of Vectors, and puts each Pic of the first Movie + -- in the location of the corresponding vector, on top of the Pic of + -- the second Movie, and returns that list as a new Movie. +pUT:: [Vec] -> Movie -> Movie -> Movie +pUT = promote3 put_Pic + + -- uncurry takes a function that takes two elements and a list of + -- two elements and applies the function to them. +uncurry:: (a->a->b) -> [a] -> b +uncurry f [a,b] = f a b + + -- curry takes a function that takes a list, and two elements, and + -- then applies the function to the elements in a list. +curry:: ([a]->b) -> a -> a -> b +curry f a b = f [a,b] + diff --git a/progs/demo/X11/animation/r_movie.hu b/progs/demo/X11/animation/r_movie.hu new file mode 100644 index 0000000..0023a04 --- /dev/null +++ b/progs/demo/X11/animation/r_movie.hu @@ -0,0 +1,3 @@ +:o= all +r_movie.hs +r_picture.hu diff --git a/progs/demo/X11/animation/r_picture.hs b/progs/demo/X11/animation/r_picture.hs new file mode 100644 index 0000000..ed3a50f --- /dev/null +++ b/progs/demo/X11/animation/r_picture.hs @@ -0,0 +1,188 @@ +{-************************************************************ + MODULE R_PICTURE + + This module contains all the functions that can be used to manipulate + Pic's. The user will probably never use any of these functions. They + are used by Behaviours and such higher-order functions, which apply + these routines to all the Pic's in the list. + +*************************************************************-} + +module R_Picture (close_Pic, ht_Pic, wid_Pic, orig_Pic, + overlay_Pic, put_Pic, over_Pic, above_Pic, beside_Pic, + map_Pic,beside2_Pic, + scale_Pic, scale_rel_Pic, mov_Pic, rot_Pic, twist_Pic, + twist_Pic', flipx_Pic, flipy_Pic, flip_Pic, {- flock_Pic, -} + set_Color_Pic, + to_orig_Pic, + movto_Pic + ) where + +import R_Ptypes +import R_Constants +import R_Utility + + -- close_Pic makes sure that the polygon is closed +close_Pic:: Pic -> Pic +close_Pic p = map close_Poly p + where + close_Poly (c,ply) | (head ply) == (last ply) = (c,ply) + close_Poly (c,ply) = (c,ply++(tail (reverse ply))) + + --these functions find the max and min x and y coordinates of a Pic +maxx :: Pic -> Int +maxx p = reduce max [x | (c,q) <- p, (x,y) <- q] + +minx :: Pic -> Int +minx p = reduce min [x | (c,q) <- p, (x,y) <- q] + +maxy :: Pic -> Int +maxy p = reduce max [y | (c,q) <- p, (x,y) <- q] + +miny :: Pic -> Int +miny p = reduce min [y | (c,q) <- p, (x,y) <- q] + + -- these functions find the height, width and origin of a Pic +ht_Pic :: Pic -> Int +ht_Pic p = (maxy p) - (miny p) + +wid_Pic :: Pic -> Int +wid_Pic p = (maxx p) - (minx p) + +orig_Pic:: Pic -> Vec +orig_Pic p = ( (maxx p + minx p) `div` 2, (maxy p + miny p) `div` 2 ) + +-- PICTURE COMBINING OPERATIONS: + + -- overlay_Pic just takes 2 Pics and puts them together into one +overlay_Pic:: Pic -> Pic -> Pic +overlay_Pic p q = p ++ q + + -- put_Pic overlays the Pics, offsetting the first Pic by a vector + -- amount from the origin of the second +put_Pic:: Vec -> Pic -> Pic -> Pic +put_Pic v p q = overlay_Pic + (movto_Pic (vplus (orig_Pic q) v) p ) + q + + -- over_Pic puts one Pic directly on top of the other +over_Pic:: Pic -> Pic -> Pic +over_Pic p q = put_Pic (0,0) p q + + -- above_Pic puts the first Pic on top of the second +above_Pic:: Pic -> Pic -> Pic +above_Pic p q = put_Pic (0,(((ht_Pic q) + (ht_Pic p)) `div` 2)) p q + + -- beside_Pic puts the first Pic beside the second. The width of + -- the Pic is defined as the max x minus the min x, so a moving + -- figure will stand still in this implementation +beside_Pic:: Pic -> Pic -> Pic +beside_Pic p q = put_Pic (((wid_Pic q)+(wid_Pic p)) `div` 2, 0) p q + + -- beside2_Pic puts the first Pic beside the second, without + -- shifting to the width of the Pic. It uses the absolute coordinates. +beside2_Pic:: Pic -> Pic -> Pic +beside2_Pic p q = put ((wid_Pic q), 0) p q + where put v p q = overlay_Pic (mov_Pic v p) q + + + -- The following maps a given function over the Vector-list of each Polygon: +map_Pic:: (Vec -> Vec) -> Pic -> Pic +map_Pic f p = map f' p + where f' (c,vs) = (c, map f vs) + +-- THE GEOMETRIC TRANSFORMATIONS: + + -- scales the Pic by r, where r is in units of 11th. ie r=1, the Pic is + -- scaled by 1/11 to its origin. +scale_Pic :: Int -> Pic -> Pic +scale_Pic r p + = map_Pic (scalep r) p + where scalep r (v1,v2) = (div ((r*(v1-dx))+dx) 11,div ((r*(v2-dy))+dy) 11) + dx = fst (orig_Pic p) + dy = snd (orig_Pic p) + + -- this is another scaling function, but it centers the image at the Vec +scale_rel_Pic :: Vec -> Int -> Pic -> Pic +scale_rel_Pic v r + = map_Pic (scalep r) + where scalep r (v1,v2) = (div ((r*(v1-dx))+dx) 11,div ((r*(v2-dy))+dy) 11) + dx = fst v + dy = snd v + + -- moves a Pic by the vector amount +mov_Pic:: Vec -> Pic -> Pic +mov_Pic v = map_Pic (vplus v) + + -- moves a Pic to the vector +movto_Pic:: Vec -> Pic -> Pic +movto_Pic v p = mov_Pic (vmin v (orig_Pic p)) p + + -- moves the origin of the Pic to the lower left side of the Pic +to_orig_Pic:: Pic -> Pic +to_orig_Pic p = mov_Pic (-mx,-my) p + where mx = minx p + my = miny p + + -- rotates the Pic about the Vector by theta +rot_Pic :: Vec -> Float -> Pic -> Pic +rot_Pic (a,b) theta + = map_Pic (rotp (a,b) theta) + where rotp (a,b) t (v1,v2) + = vftov (a2+ (u * cos theta - v * sin theta), + b2+ (u * sin theta + v * cos theta)) + where u = u1 -a2 + v = u2 -b2 + (u1,u2) = vtovf (v1,v2) + (a2,b2) = vtovf (a,b) + + -- rotates a Pic about its origin by theta +twist_Pic :: Float -> Pic -> Pic +twist_Pic theta p = rot_Pic (orig_Pic p) theta p + + + -- hardwired version of rot_Pic that runs faster by rotating a set + -- unit, the rotunit, every time +rot_Pic':: Vec -> Pic -> Pic +rot_Pic' (a,b) = map_Pic (rotp (a,b)) + where rotp (a,b) (v1,v2) + = vftov (a2+ (u * cosunit - v * sinunit), + b2+ (u * sinunit + v * cosunit)) + where u = u1-a2 + v = u2-b2 + (u1,u2) = vtovf (v1,v2) + (a2,b2) = vtovf (a,b) + + -- hardwired version of twist_Pic that runs faster using rot_Pic' +twist_Pic':: Pic -> Pic +twist_Pic' p = rot_Pic' (orig_Pic p) p + + -- flips the Pic about the line x=n (x-coordinates change) +flipx_Pic :: Int -> Pic -> Pic +flipx_Pic n = map_Pic (flipvx n) + where + flipvx n (a,b) = (2*(n-a)+a,b) + + -- flips the Pic about the line y=n (y-coordinates change) +flipy_Pic :: Int -> Pic -> Pic +flipy_Pic n = map_Pic (flipvy n) + where + flipvy n (a,b) = (a, 2*(n-b)+b) + + -- flips the Pic about its own x origin. +flip_Pic:: Pic -> Pic +flip_Pic p = map_Pic (flipvx x) p + where (x,y) = orig_Pic p + flipvx n (a,b) = (2*(n-a)+a,b) + + -- copies the Pic into another Pic n*n times in an n by n array pattern +flock_Pic :: Int -> Pic -> Pic +flock_Pic 1 p = p +flock_Pic (n+2) p = beside_Pic (flock_Pic (n-1) p) (row n p) + where row n p = replicate n above_Pic nullpic p + + -- changes the color of the Pic +set_Color_Pic:: Color -> Pic -> Pic +set_Color_Pic c p = map f p + where f (c',vs) = (c,vs) + diff --git a/progs/demo/X11/animation/r_picture.hu b/progs/demo/X11/animation/r_picture.hu new file mode 100644 index 0000000..932d87c --- /dev/null +++ b/progs/demo/X11/animation/r_picture.hu @@ -0,0 +1,4 @@ +:o= all +r_picture.hs +r_constants.hu +r_utility.hu diff --git a/progs/demo/X11/animation/r_ptypes.hs b/progs/demo/X11/animation/r_ptypes.hs new file mode 100644 index 0000000..c020f82 --- /dev/null +++ b/progs/demo/X11/animation/r_ptypes.hs @@ -0,0 +1,67 @@ +{-*********************************************************************** + MODULE PTYPES + + This module contains the definitions for all the basic datatypes used to + create functional movies. + The basis of all the images is the Poly, which is a tuple of a color + and a list of points. This is displayed as a polygon of that color. The + form is a line drawn to each of the points, in order. + A list of these Poly's is a Pic, or picture. Each picture is a single + frame of the movie. A list of Pic's makes up a Movie, which is a series + of Pic's displayed in order. + Behaviours affect the movies, such as moving them left, or right. + PictoPic's affect a single picture. + The other functions simply convert regular values such as integers + and floats to the datatypes used by the functional programming. + +************************************************************************-} + + +module R_Ptypes (Vec(..), Color(..), Pic(..), Poly(..), Movie(..), Behaviour(..), PictoPic(..), Process(..), + Vecfloat(..), + Msg(..), Chan(..), + Val (..), + ntov, vtov, nstov, vstov, pstov, bstov + ) where + + + --These are the basic data types for storing and manipulating the movies. + +type Vec = (Int,Int) +type Color = Int +type Pic = [Poly] +type Poly = (Color,[Vec]) +type Movie = [Pic] +type Behaviour = [Pic -> Pic] +type PictoPic = Pic -> Pic + +type Process = [Msg] -> [Msg] +type Msg = [(Chan,Val)] +type Chan = [Char] + +data Val = N Int | V (Int,Int) | P Pic | B PictoPic + +type Vecfloat = (Float,Float) + + + +--Those convert from the various regular values to Val's. + +ntov n = N n + +vtov:: Vec -> Val +vtov v = V v + +ptov:: Pic -> Val +ptov p = P p + +nstov ns = [N n|n<-ns] + +vstov:: [Vec] -> [Val] +vstov vs = [V v|v<-vs] + +pstov:: [Pic] -> [Val] +pstov ps = [P p|p<-ps] + +bstov:: [PictoPic] -> [Val] +bstov bs = [B b|b<-bs] diff --git a/progs/demo/X11/animation/r_ptypes.hu b/progs/demo/X11/animation/r_ptypes.hu new file mode 100644 index 0000000..8a99f8f --- /dev/null +++ b/progs/demo/X11/animation/r_ptypes.hu @@ -0,0 +1,2 @@ +:o= all +r_ptypes.hs diff --git a/progs/demo/X11/animation/r_shapes.hs b/progs/demo/X11/animation/r_shapes.hs new file mode 100644 index 0000000..aef3362 --- /dev/null +++ b/progs/demo/X11/animation/r_shapes.hs @@ -0,0 +1,38 @@ +{-***************************************************************** + MODULE R_SHAPES + + This modules produces Pic's of boxes and triangles to help build + Pic's to animate. + +******************************************************************-} + +module R_Shapes (box, tri, circ_mov, circ) where + +import R_Ptypes +import R_Utility +import R_Picture +import R_Behaviour + + -- box takes four three ints, the color, width and height of the box and + -- returns a Pic of a box +box :: Int -> Int -> Int -> Pic +box c width height= [(c,[(0,0),(width,0),(width,height),(0,height),(0,0)])] + + -- tri takes a color and three vectors, and returns a Pic of a triangle + -- with the vectors as vertices +tri:: Color -> Vec -> Vec -> Vec -> Pic +tri c (x1,y1) (x2,y2) (x3,y3) = [(c,[(x1,y1),(x2,y2),(x3,y3),(x1,y1)])] + + + -- circ takes a color, the radius +circ :: Color -> Int -> Int -> Pic +circ c r inc = [(c,(r+r,r):(circ' r' inc' 1.0))] + where r' = (fromIntegral r) + inc' = (fromIntegral inc) + +circ' :: Float -> Float -> Float -> [Vec] +circ' r inc c | c>inc = [] +circ' r inc c = vftov (x+r,y+r) : (circ' r inc (c+1.0)) + where x = r*(cos((2*c*pi)/inc)) + y = r*(sin((2*c*pi)/inc)) + diff --git a/progs/demo/X11/animation/r_shapes.hu b/progs/demo/X11/animation/r_shapes.hu new file mode 100644 index 0000000..ad0bf40 --- /dev/null +++ b/progs/demo/X11/animation/r_shapes.hu @@ -0,0 +1,3 @@ +:o= all +r_shapes.hs +r_behaviour.hu diff --git a/progs/demo/X11/animation/r_utility.hs b/progs/demo/X11/animation/r_utility.hs new file mode 100644 index 0000000..9dfcc65 --- /dev/null +++ b/progs/demo/X11/animation/r_utility.hs @@ -0,0 +1,150 @@ +{-********************************************************************* + MODULE R_UTILITY + + This module contains all the basic utility functions that the other + modules need to have to write their code. These are made generally + low level functions, manipulating vectors or defining very + general functions + +**********************************************************************-} + + +module R_Utility (vtovf,vftov, + vplus, vmin, mid, partway, + mag, + reduce, power, i, + member, repeat, zip1, zip2, zip3, rept, replicate, + mapc, + append, flatten, rptseq, osc + ) where + +import R_Ptypes + + +-- CONVERSION + + -- vtovf takes a vector of integers, and converts it to a vector of floats +vtovf :: Vec -> Vecfloat +vtovf (x,y) = (fromIntegral x,fromIntegral y) + + -- vftov takes a vector of floats and converts it to a vector of integers. + -- It rounds the floats off to do this. +vftov :: Vecfloat -> Vec +vftov (x,y) = (round x,round y) + + +-- VECTOR OPERATIONS: + + -- vector addition +vplus:: Vec -> Vec -> Vec +vplus (a,b) (c,d) = (a+c,b+d) + + -- vector substraction +vmin:: Vec -> Vec -> Vec +vmin (a,b) (c,d) = (a-c,b-d) + + -- finds the midpoint between two vectors +mid:: Vec -> Vec -> Vec +mid (x1,y1) (x2,y2) = (div (x1+x2) 2,div (y1+y2) 2 ) + + -- finds a point p/q along the way between two vectors +partway :: Int -> Int -> [Vec] -> Vec +partway p q [(x1,y1),(x2,y2)] + = vplus (x1,y1) ( div (p*(x2-x1)) q, div (p*(y2-y1)) q ) + + -- finds the magnitude of two vectors +mag :: Vec -> Int +mag p = round (magfloat (vtovf p)) + +magfloat :: Vecfloat -> Float +magfloat (x,y) = sqrt (x*x + y*y) + + -- returns a vector at right angles to the input vector +normal:: Vec -> Vec +normal (x,y) = (-y,x) + + -- returns the first vector projected onto the second +project:: Vec -> Vec -> Vec +project (vx,vy) (wx,wy) = partway (vx*wx+vy*wy) (mw*mw) [(0,0),(wx,wy)] + where mw = mag (wx,wy) + + +-- HIGHER-ORDER FUNCTIONS: + + -- just foldr1. It applies a function of two inputs to an entire list + -- recursively, and displays the single element result +reduce :: (a->a->a) -> [a] -> a +reduce = foldr1 + + -- power applies a single function n times to a seed +power :: Int -> (a->a) -> a -> a +power 0 f seed = seed +power (n+1) f seed = f (power n f seed) + + -- i takes an element and returns an infinite list of them +i :: a -> [a] +i x = x: (i x) + + -- checks to see if x is in the list of xs +member :: (Eq a) => [a] -> a -> Bool +member [] x = False +member (y:ys) x = x == y || member ys x + + -- zip1 takes lists of lists, and rearranges them so that all the first + -- elements are in the first list, all the second in the second and so on. +zip1 :: (Eq a) => [[a]] -> [[a]] +zip1 xs | member xs [] = [] +zip1 xs = (map head xs):(zip1 (map tail xs)) + + -- takes two lists and makes a list of tuples. +zip2 :: [a] -> [b] -> [(a,b)] +zip2=zip + + -- rept takes a function and a list of elements, and applies the function + -- n-1 times to the n-th element +rept :: (a->a) -> a -> [a] +rept f x = x:(rept f (f x)) + + -- replicate creates an list n elements long of a, with the function + -- applies to the n-th element n-1 times. +replicate :: Int -> (a->a->a) -> a -> a -> a +replicate 0 f zero a = zero +replicate 1 f zero a = a +replicate (n+2) f zero a = f a (replicate (n+1) f zero a) + + -- mapc is a map function for lists of functions (behaviours) +mapc :: (a->b) -> [c->a] -> [c->b] +mapc f as = [f.a | a <- as] + + +-- FUNCTIONS OVER SEQUENCES: + + -- append takes a list of lists, and makes them into one giant happy list. +append :: [[a]] -> [a] +append = foldr (++) [] + + -- flatten takes a list of lists of tuples and gives one giant happy list + -- of single elements back. +flatten:: [[(a,a)]] -> [a] +flatten s = foldr f [] (append s) + where f (x,y) [] = [x,y] + f (x,y) (z:zs) = x:y:(z:zs) + + -- rptseq takes a list of elements and applies a function to them, + -- n-1 times for the n-th element, but using map +rptseq :: (a->a) -> [a] -> [a] +rptseq f [] = [] +rptseq f (x:xs) = x:rptseq f (map f xs) + + -- osc takes a list, and makes sure it oscillates. If the head is + -- equal to the tail, it simply repeats the sequence infinitely. If + -- the head is not equal to the tail, it adds the sequence then adds + -- the reversed sequence minus the first and last elements, and then repeats +osc :: [a] -> [a] +osc s | (length s) == 0 = [] +osc s | (length s) == 1 = head s: osc s +osc s = (s ++ (((tail.reverse).tail) s)) ++ (osc s) + + + + diff --git a/progs/demo/X11/animation/r_utility.hu b/progs/demo/X11/animation/r_utility.hu new file mode 100644 index 0000000..6fac189 --- /dev/null +++ b/progs/demo/X11/animation/r_utility.hu @@ -0,0 +1,3 @@ +:o= all +r_utility.hs +r_ptypes.hu diff --git a/progs/demo/X11/animation/seafigs.hs b/progs/demo/X11/animation/seafigs.hs new file mode 100644 index 0000000..c216a63 --- /dev/null +++ b/progs/demo/X11/animation/seafigs.hs @@ -0,0 +1,158 @@ +module Seafigs (sky,blue_sky,clouds,clouds2,gull,man,sun,vm,windmill,palm) where + +import Animation + +blue_sky:: Movie +blue_sky = osc [box lightblue 1000 1000] + +sky:: Color -> Movie +sky c = osc [box c 1000 1000] + +clouds2:: Movie +clouds2 = apply (mov (i (cld_wid,0))) (rBESIDE[cld,cld]) + where cld_wid = -(wid_Pic (cld!!0)) + cld= apply (bPar [right,mov (repeat (250,-50))]) cldm1 + cldm1=osc[cloud1] + +clouds:: Movie +clouds + = rOVERLAY + [apply (bPar [right,mov (repeat (250,-50))]) cloudm1, + apply (bPar [right,mov (repeat (0,-50))]) cloudm2, + apply (bPar [right,mov (repeat (250,-75))]) cloudm2, + apply (bPar [right,flipb,smaller,mov(repeat (200,-100))]) cloudm2, + apply (bPar [right,flipb,smaller,mov(repeat (300,-125))]) cloudm1, + apply (bPar [right,right,mov (repeat (-50,50))]) cloudm1] + where cloudm1 = osc [cloud1] + cloudm2 = osc [cloud2] + + +cloud1 = [(white,ply)] + where ply = [(142,301),(169,309),(180,315),(192,312), + (196,308),(202,302),(216,300),(224,308), + (238,312),(258,311),(274,301),(278,283), + (265,279),(246,279),(230,281),(197,286), + (185,288),(167,287),(148,287),(136,292), + (136,292),(142,301)] + + +cloud2 = [(white,ply)] + where ply = [(51,262), (56,266), + (66,265), (90,264), (92,266), (98,270), + (111,268),(137,268),(155,266),(174,266), + (183,262),(183,253),(162,251),(136,254), + (132,250),(126,248),(115,252),(109,253), + (98,252), (90,253), (88,254), (67,254), + (56,252), (49,254), (47,259), (51,262)] + +gull :: Movie +gull = osc [gull1,gull2] + +gull1 = [(black,[(2,4),(6,4),(9,2),(10,0),(11,2), + (16,4),(20,4)])] + +gull2 = [(black,[(0,0),(2,2),(6,3),(9,2),(12,3), + (16,2),(18,0)])] + +man :: Movie +man = osc [man1,man2,man3] + + +man1 = [(black,[(0,0),(10,0),(20,40),(30,60),(40,20), + (60,0),(50,0)]), + (black,[(0,40),(20,60),(30,80),(50,70),(60,60)]), + (black,[(30,60),(30,100)]), + (black,[(30,100),(25,100),(20,105),(23,112), + (20,115),(30,120),(35,120),(40,115), + (40,110),(35,105),(30,100)]) + ] + +man2 = [(black,[(20,0),(30,0),(20,40),(30,60),(45,30), + (60,20),(50,0)]), + (black,[(0,60),(20,60),(20,80),(40,80),(50,60)]), + (black,[(30,60),(20,100)]), + (black,[(20,100),(15,100),(10,105),(13,112), + (10,115),(20,120),(30,120),(30,115), + (30,110),(25,105),(20,100)]) + ] + +man3 = [(black,[(0,15),(5,10),(15,45),(30,60),(35,25), + (44,10),(35,0)]), + (black,[(10,40),(22,60),(20,80),(40,75),(45,44)]), + (black,[(30,60),(20,100)]), + (black,[(20,100),(19,100),(14,105),(17,112), + (14,115),(24,120),(34,120),(34,115), + (34,110),(29,105),(200,100)]) + ] + +sun :: Movie +sun = osc [sun'] + where + sun' = reduce overlay_Pic [sun1, + twist_Pic (pi/24.0) sun1, + twist_Pic (pi/12.0) sun1] + +sun1 = [(yellow,[(43,16),(18,27),(9,51),(20,71),(42,81), + (66,73),(76,47),(69,25),(43,15),(43,16)])] + +vm :: Movie +vm = osc[vm1,vm2] + +vm1 = beside_Pic (box brown 10 15) + (above_Pic light1 (box brown 40 80)) + where light1 = box yellow 10 10 + +vm2 = beside_Pic (box brown 10 15) + (reduce above_Pic [light,light2,box brown 40 80]) + where light2 = over_Pic (box red 10 10) (box white 5 5) + light = [ (red,[(5,5), (10,2), (0,30),(5,5)]), + (red,[(20,2),(25,5),(30,30),(20,2)]), + (red,[(15,15),(20,15),(15,50),(10,25)])] + +windmill :: Movie +windmill + = apply + (bpar (mov (repeat (unit*3,0))) (scale_rel (0,0) (repeat 3))) + (overlay body (apply (movto (repeat (100,400))) prop)) + +blade = osc [tri red (0,0) (100,0) (50,300)] +prop = apply cw fan + +fan = rOVERLAY [fan1,fan2,fan3,fan4] +fan1 = blade +fan2 = apply (rot (osc[(50,300)]) (osc[pi/2.0])) fan1 +fan3 = apply (rot (osc[(50,300)]) (osc[pi/2.0])) fan2 +fan4 = apply (rot (osc[(50,300)]) (osc[pi/2.0])) fan3 + +body = osc [ [(brown,[(0,0),(200,0),(170,300), + (100,400),(30,300),(0,0)]) ] ] + + +palm :: Movie +palm + = osc palms + where palms = inbetween 3 palm1 (flipx_Pic 100 palm1) + palm1 = reduce overlay_Pic [trunk,frond1,frond2,frond3,frond4] + where frond1 = [ (green,[(50,60),(60,70),(80,60)]), + (green,[(50,70),(60,80),(80,70)]), + (green,[(50,80),(55,90),(70,80)]), + (green,[(60,70),(55,90),(50,100)]) ] + + frond2 = flipx_Pic 50 frond1 + + frond3 = [ (green,[(10,70),(5,80)]), + (green,[(10,80),(10,90)]), + (green,[(20,90),(20,100)]), + (green,[(30,95),(40,104)]), + (green,[(5,80),(20,100),(40,104), + (50,100)])] + + frond4 = [(green,[(0,100),(5,110)]), + (green,[(15,105),(15,115)]), + (green,[(25,105),(30,115)]), + (green,[(35,105),(40,115)]), + (green,[(5,110),(30,115),(50,110), + (50,100)])] + + trunk = [(brown,[(100,0),(95,40),(80,80), + (70,90),(60,97),(50,100)])] diff --git a/progs/demo/X11/animation/seafigs.hu b/progs/demo/X11/animation/seafigs.hu new file mode 100644 index 0000000..83fcfcf --- /dev/null +++ b/progs/demo/X11/animation/seafigs.hu @@ -0,0 +1,3 @@ +:o= all +animation.hu +seafigs.hs diff --git a/progs/demo/X11/animation/seaside.hs b/progs/demo/X11/animation/seaside.hs new file mode 100644 index 0000000..fe11e12 --- /dev/null +++ b/progs/demo/X11/animation/seaside.hs @@ -0,0 +1,25 @@ +module Seaside (main) where + +import Animation +import Seafigs + +seaside :: Movie +seaside = rOVERLAY [blue_sky, + apply (bPar [up,cw,movto (repeat botm)]) sun, + apply right clouds, + apply (bPar [right,bigger]) gull, + apply (bPar [right,right,bigger]) gull, + apply (bPar [up,up,right,bigger]) gull, + apply (bPar [up,right,right,right]) gull, + windm, + apply (mov (repeat botm)) palm, + man_and_vm + ] + where man_and_vm = rBESIDE2 [manfig, vm] + manfig = apply left (apply (mov (i (700,0))) + man) + windm = apply (mov (i (500,0))) windmill + + +main = getEnv "DISPLAY" exit + (\ host -> displaym host 30 (map (flipy_Pic 500) seaside)) diff --git a/progs/demo/X11/animation/seaside.hu b/progs/demo/X11/animation/seaside.hu new file mode 100644 index 0000000..df2c4b9 --- /dev/null +++ b/progs/demo/X11/animation/seaside.hu @@ -0,0 +1,5 @@ +:o= all +seaside.hs +seafigs.hu + + diff --git a/progs/demo/X11/draw/README b/progs/demo/X11/draw/README new file mode 100644 index 0000000..b844d2b --- /dev/null +++ b/progs/demo/X11/draw/README @@ -0,0 +1 @@ +This is the draw program used in the X window documentation diff --git a/progs/demo/X11/draw/draw.hs b/progs/demo/X11/draw/draw.hs new file mode 100644 index 0000000..1ba68ce --- /dev/null +++ b/progs/demo/X11/draw/draw.hs @@ -0,0 +1,41 @@ +module Draw where + +import Xlib + +main = getEnv "DISPLAY" exit (\ host -> draw host) + +draw :: String -> IO () +draw host = + xOpenDisplay host `thenIO` \ display -> + let (screen:_) = xDisplayRoots display + fg_color = xScreenBlackPixel screen + bg_color = xScreenWhitePixel screen + root = xScreenRoot screen + in + xCreateWindow root + (XRect 100 100 400 400) + [XWinBackground bg_color, + XWinEventMask (XEventMask [XButtonMotion, + XButtonPress, + XKeyPress])] + `thenIO` \window -> + xMapWindow window `thenIO` \() -> + xCreateGcontext (XDrawWindow root) + [XGCBackground bg_color, + XGCForeground fg_color] `thenIO` \ gcontext -> + let + handleEvent :: XPoint -> IO () + handleEvent last = + xGetEvent display `thenIO` \event -> + let pos = xEventPos event + in + case (xEventType event) of + XButtonPressEvent -> handleEvent pos + XMotionNotifyEvent -> + xDrawLine (XDrawWindow window) gcontext last pos `thenIO` \() -> + handleEvent pos + XKeyPressEvent -> xCloseDisplay display + _ -> handleEvent last + in + appendChan stdout "Press any key to quit.\n" exit done `thenIO` \ _ -> + handleEvent (XPoint 0 0) diff --git a/progs/demo/X11/draw/draw.hu b/progs/demo/X11/draw/draw.hu new file mode 100644 index 0000000..f09a72e --- /dev/null +++ b/progs/demo/X11/draw/draw.hu @@ -0,0 +1,2 @@ +$HASKELL_LIBRARY/X11/xlib.hu +draw.hs diff --git a/progs/demo/X11/gobang/README b/progs/demo/X11/gobang/README new file mode 100644 index 0000000..d5634a4 --- /dev/null +++ b/progs/demo/X11/gobang/README @@ -0,0 +1,66 @@ +gobang Weiming Wu & Niping Wu + + +Introduction + +Our final project is to design and implement a Gobang game under +X-Window3.2 environment, using the Haskell programming language. Users +can play the game human-vs-human. The program also provides a robot +player with whom the user can play the game with. We wrote altogether +ten modules which were saved in different files to control the whole +game. + + +About Gobang + +The checkerboard of Gobang consists of 19 vertical lines and 19 +horizontal lines. Two players in turn place a unit on the +checkerboard. Each unit should be put on an unoccupied intersection +of a vertical and a horizontal line. The winner is the player who +first makes five consecutive units on either vertical, horizontal or +diagonal direction. + +The program is able to perform the following tasks: 1) Use a new +window under X-Window interface to display the checkerboard. Players +will use a mouse to place units onto the chessboard, where a unit is a +circle with the color black or white. 2) Prompt for the names of both +players and display them. 3) Calculate the time both players have +used up. 4) Supervise the progress of the game, declare winner and +end the game once one player wins. 5) At each point of the game, +store the progress of the game, so players can review each step during +the game. 6) There are five buttons on the screen which would provide +some special services such as starting a new game, quitting the game, +saving the game, importing the saved game, or reviewing the game as +soon as the user selects the corresponding buttons. 7) Provide a +moderately well robot player for that game (using minimum-maximum +algorithm). + + +Running Gobang + +A window titled "gobang" will appear on the screen. On it is a +checkerboard, clocks and buttons. There will be an instruction saying +"Please enter the name of player-1". The user can do two things: +either enter the name of a player or choose the "import" button. Once +the "import" button is selected, an unfinished game, which was saved +in the file "###go.bhs###" will be imported. Please notice that the +character "@" is reserved for the robot player, so if the user types +in @ as the name of the first player, it is assumed that player-1 is +the robot player. Then the name of player 2 is prompted. The game +starts and at each turn an instruction like "Please enter your play." +would appear on the screen. The user should put a unit onto the +checkerboard. If the button is clicked on a wrong place or a unit is +put onto an occupied position, an error message saying "Wrong Point. +Please reenter." will appear on the screen and the user should reenter +his play. The marker next to the name of a player indicates whose +turn it is. At any point of the game the user can choose the other +four buttons. If the "new" button is selected, the present game will +be terminated and a new blank checkerboard will be displayed on the +screen; if the "review" button is selected, one step of the previous +plays will be displayed each time after the user hits any key; if the +"save" button is selected, the steps so far will be saved into the +file "###go.bhs###"; if the "quit" button is selected, the game will +be terminated. + + + diff --git a/progs/demo/X11/gobang/gobang.hs b/progs/demo/X11/gobang/gobang.hs new file mode 100644 index 0000000..f4844dc --- /dev/null +++ b/progs/demo/X11/gobang/gobang.hs @@ -0,0 +1,364 @@ +module Gobang where + +import Xlib +import Utilities +import Redraw +import Weights + +getXInfo :: String -> IO XInfo +getXInfo host = + xOpenDisplay host `thenIO` \ display -> + let (screen:_) = xDisplayRoots display + fg_pixel = xScreenBlackPixel screen + bg_pixel = xScreenWhitePixel screen + root = xScreenRoot screen + in + xCreateWindow root + (XRect 0 0 900 600) + [XWinBackground bg_pixel, + XWinEventMask (XEventMask [XButtonPress, + XKeyPress, + XExposure])] + `thenIO` \ window -> + xSetWmName window "Gobang" `thenIO` \() -> + xMapWindow window `thenIO` \() -> + xOpenFont display "10x20" `thenIO` \ playerfont -> + xOpenFont display "6x13" `thenIO` \ genericfont -> + xCreateGcontext (XDrawWindow window) + [XGCBackground bg_pixel, + XGCForeground fg_pixel] `thenIO` \ gcontext -> + xCreateGcontext (XDrawWindow window) + [XGCBackground fg_pixel, + XGCForeground bg_pixel, + XGCFont genericfont] `thenIO` \ gcontext2 -> + xCreateGcontext (XDrawWindow window) + [XGCBackground bg_pixel, + XGCForeground fg_pixel, + XGCFont playerfont] `thenIO` \ gcontextp -> + returnIO (XInfo display window gcontext gcontext2 gcontextp) + +demo = main + +main = getEnv "DISPLAY" exit $ \ host -> + xHandleError (\(XError msg) -> appendChan stdout msg exit done) $ + gobang host + +gobang :: String -> IO () +gobang host = + getXInfo host `thenIO` \ xinfo -> + xMArrayCreate [1..361] `thenIO` \ board -> + xMArrayCreate [1..361] `thenIO` \ weight1 -> + xMArrayCreate [1..361] `thenIO` \ weight2 -> + xMArrayCreate [1..722] `thenIO` \ steps -> + xMArrayCreate [""] `thenIO` \ player1 -> + xMArrayCreate [""] `thenIO` \ player2 -> + xMArrayCreate [1..4] `thenIO` \ time -> + xMArrayCreate [1] `thenIO` \ numbersteps -> + xMArrayCreate [""] `thenIO` \ promptString -> + xMArrayCreate [1] `thenIO` \ next_player -> + let state = GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player + in + initGame xinfo state `thenIO` \ _ -> + promptPlayers xinfo state `thenIO` \ _ -> + playGame xinfo state + +promptPlayers xinfo state = + let (XInfo display window gcontext gcontext2 gcontextp) = xinfo + (GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) = state + in + promptFor "player 1:" xinfo state `thenIO` \ player1_name -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 710 65) player1_name + `thenIO` \ _ -> + xMArrayUpdate player1 0 player1_name `thenIO` \ _ -> + promptFor "player 2:" xinfo state `thenIO` \ player2_name -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 710 205) player2_name + `thenIO` \ _ -> + xMArrayUpdate player2 0 player2_name `thenIO` \ _ -> + clearCmd xinfo state + +initGame :: XInfo -> GameState -> IO () +initGame xinfo + state@(GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) = + getTime `thenIO` \ curtime -> + initArray time 0 2 0 `thenIO` \() -> + initArray time 2 4 curtime `thenIO` \() -> + initArray numbersteps 0 1 0 `thenIO` \() -> + initArray board 0 361 0 `thenIO` \() -> + initArray weight1 0 361 0 `thenIO` \() -> + initArray weight2 0 361 0 `thenIO` \ () -> + initArray next_player 0 1 1 `thenIO` \ () -> + clearCmd xinfo state `thenIO` \ () -> + redraw xinfo state + + +handleButton :: XPoint -> XInfo -> GameState -> GameCont -> IO () +handleButton (XPoint x y) + xinfo + state@(GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) + cont + | buttonPress 700 330 x y = initArray player1 0 1 "" `thenIO` \ _ -> + initArray player2 0 1 "" `thenIO` \ _ -> + initGame xinfo state `thenIO` \ _ -> + promptPlayers xinfo state `thenIO` \ _ -> + playGame xinfo state + | buttonPress 700 360 x y = initGame xinfo state `thenIO` \ _ -> + playGame xinfo state + | buttonPress 700 390 x y = undoGame xinfo state cont + | buttonPress 700 420 x y = loadGame xinfo state cont + | buttonPress 700 450 x y = saveGame xinfo state `thenIO` \ () -> + cont xinfo state + | buttonPress 700 480 x y = quitGame xinfo state cont + | ishelp x y = helpGame xinfo state `thenIO` \ () -> + cont xinfo state + | otherwise = cont xinfo state + +when :: Bool -> IO () -> IO () +when cond action = if cond then action else returnIO () + +undoGame xinfo@(XInfo display window gcontext gcontext2 gcontextp) + state@(GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) + cont = + xMArrayLookup next_player 0 `thenIO` \ next_p -> + xMArrayLookup player1 0 `thenIO` \ name1 -> + xMArrayLookup player2 0 `thenIO` \ name2 -> + let undoStep n = + xMArrayLookup steps (2*n) `thenIO` \ x -> + xMArrayLookup steps (2*n+1) `thenIO` \ y -> + xMArrayUpdate board ((x-1)*19 + y-1) 0 `thenIO` \ _ -> + (if (name1 == "computer" || name2 == "computer") + then draw_unit board weight1 weight2 x y + else returnIO ()) `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext2 + (XRect (x*30-15) (y*30-15) 30 30) True + `thenIO` \() -> +-- drawBoard xinfo `thenIO` \ _ -> +-- drawPieces 1 1 board xinfo `thenIO` \ _ -> + let x30 = x * 30 + y30 = y * 30 + c = XPoint x30 y30 + w = XPoint (x30-15) y30 + e = XPoint (x30+15) y30 + no = XPoint x30 (y30-15) + s = XPoint x30 (y30+15) + m = XArc (x30-3) (y30-3) 6 6 (-1.0) 6.283 + in + when (x > 1) (xDrawLine (XDrawWindow window) gcontext w c) + `thenIO` \ _ -> + when (x < 19) (xDrawLine (XDrawWindow window) gcontext c e) + `thenIO` \ _ -> + when (y > 1) (xDrawLine (XDrawWindow window) gcontext no c) + `thenIO` \ _ -> + when (y < 19) (xDrawLine (XDrawWindow window) gcontext c s) + `thenIO` \ _ -> + when ((x `elem` [4,10,16]) && (y `elem` [4,10,16])) + (xDrawArc (XDrawWindow window) gcontext m True) + `thenIO` \ _ -> + xDisplayForceOutput display `thenIO` \ _ -> + xMArrayUpdate numbersteps 0 n `thenIO` \ _ -> + xMArrayLookup next_player 0 `thenIO` \ next_p -> + xMArrayUpdate next_player 0 (if next_p == 1 then 2 else 1) + + cur_name = if next_p == 1 then name1 else name2 + last_name = if next_p == 1 then name2 else name1 + in + xMArrayLookup numbersteps 0 `thenIO` \ n -> + if n==0 then drawCmd "No more steps to undo!" xinfo state `thenIO` \ _ -> + cont xinfo state + else + if cur_name == "computer" then cont xinfo state + else + (undoStep (n-1) `thenIO` \_ -> + if (last_name == "computer" && n /= 1) then undoStep (n-2) + else + returnIO ()) `thenIO` \ _ -> + playGame xinfo state + + + + +promptFile xinfo state cont = + promptFor "File name:" xinfo state `thenIO` \ name -> + readFile name + (\ _ -> drawCmd ("Can't read file:" ++ name) xinfo state + `thenIO` \ _ -> + cont XNull) + (\ content -> cont (XSome content)) + +loadGame xinfo state cont = + promptFile xinfo state $ \ file -> + case file of + XNull -> cont xinfo state + XSome file_content -> + readGameState file_content `thenIO` \ new_state -> + let (GameState _ _ _ _ _ _ time _ _ _) = new_state + in + getTime `thenIO` \ curtime -> + initArray time 2 4 curtime `thenIO` \() -> + redraw xinfo new_state `thenIO` \ _ -> + playGame xinfo new_state + +saveGame :: XInfo -> GameState -> IO () +saveGame xinfo state = + promptFor "File name:" xinfo state `thenIO` \ name -> + showGameState state `thenIO` \ str -> + writeFile name str + (\ _ -> drawCmd ("Can't write file: " ++ name) xinfo state) + done + +quitGame :: XInfo -> GameState -> GameCont -> IO () +quitGame xinfo state cont = + let (XInfo display window gcontext gcontext2 gcontextp) = xinfo + in + promptFor "Are you sure? (y/n)" xinfo state `thenIO` \ reps -> + if (reps == "y" || reps == "Y") then xCloseDisplay display + else clearCmd xinfo state `thenIO` \ _ -> + cont xinfo state + +playGame :: XInfo -> GameState -> IO () +playGame xinfo state = + let + (XInfo display window gcontext gcontext2 gcontextp) = xinfo + (GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) = state + in + xMArrayLookup numbersteps 0 `thenIO` \ x -> + (\cont -> if x == 361 + then drawCmd "It's a tie!" xinfo state `thenIO` \ _ -> + let loop xinfo state = waitButton xinfo state (\ _ -> loop) + in loop xinfo state + else cont) $ + xMArrayLookup next_player 0 `thenIO` \ next_player_num -> + getTime `thenIO` \ curtime -> + xMArrayLookup time 0 `thenIO` \ lstm0 -> + xMArrayLookup time 1 `thenIO` \ lstm1 -> + xMArrayLookup time 2 `thenIO` \ lstm2 -> + xMArrayLookup time 3 `thenIO` \ lstm3 -> + drawCmd ("Waiting for player # " ++ (show next_player_num)) xinfo state + `thenIO` \() -> + if (next_player_num == 1) + then xDrawGlyph (XDrawWindow window) gcontextp (XPoint 850 70) + '<' `thenIO` \(trash) -> + xDrawRectangle (XDrawWindow window) gcontext2 + (XRect 840 180 40 40) True `thenIO` \() -> + xMArrayUpdate time 2 curtime `thenIO` \() -> + xMArrayUpdate time 1 (lstm1+curtime-lstm3) `thenIO` \() -> + showtime 705 270 (lstm1+curtime-lstm3) xinfo `thenIO` \() -> + xMArrayLookup player1 0 `thenIO` \ x -> + if (x == "computer") + then computerplay xinfo state + else humanplay xinfo state + else xDrawGlyph (XDrawWindow window) gcontextp (XPoint 850 210) + '<' `thenIO` \(trash) -> + xDrawRectangle (XDrawWindow window) gcontext2 + (XRect 840 40 40 40) True `thenIO` \() -> + xMArrayUpdate time 3 curtime `thenIO` \() -> + xMArrayUpdate time 0 (lstm0+curtime-lstm2) `thenIO` \() -> + showtime 705 130 (lstm0+curtime-lstm3) xinfo `thenIO` \() -> + xMArrayLookup player2 0 `thenIO` \ x -> + if (x == "computer") + then computerplay xinfo state + else humanplay xinfo state + +waitButton xinfo@(XInfo display _ _ _ _) state cont = + let + loop xinfo state = + xGetEvent display `thenIO` \ event -> + case (xEventType event) of + XExposureEvent -> may_redraw (xEventCount event == 0) xinfo state + `thenIO` \ _ -> + loop xinfo state + XButtonPressEvent -> + let pos = xEventPos event + in + handleButton pos xinfo state (cont pos) + _ -> xBell display 0 `thenIO` \ _ -> + loop xinfo state + in + loop xinfo state + +updateboard :: XInfo -> GameState -> Int -> Int -> IO () +updateboard xinfo state x y = + let (GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) = state + (XInfo display window gcontext gcontext2 gcontextp) = xinfo + in + xMArrayLookup next_player 0 `thenIO` \ next_player_num -> + xMArrayUpdate next_player 0 (if next_player_num == 1 then 2 else 1) + `thenIO` \ _ -> + xMArrayLookup numbersteps 0 `thenIO` \ z -> + xMArrayUpdate numbersteps 0 (z+1) `thenIO` \() -> + xMArrayUpdate steps (2*z) x `thenIO` \() -> + xMArrayUpdate steps (2*z+1) y `thenIO` \() -> + xMArrayLookup player1 0 `thenIO` \ name1 -> + xMArrayLookup player2 0 `thenIO` \ name2 -> + xMArrayUpdate board (19*(x-1)+y-1) next_player_num + `thenIO` \() -> + human_unit board x y `thenIO` \ win -> + if win + then drawCmd ("Player " ++ (show next_player_num) ++ " has won!") + xinfo state `thenIO` \ _ -> + let loop xinfo state = waitButton xinfo state (\ _ -> loop) + in loop xinfo state + else if (name1 == "computer" || name2 == "computer") + then draw_unit board weight1 weight2 x y `thenIO` \() -> + xMArrayUpdate weight1 (19*(x-1)+y-1) (-1) `thenIO` \() -> + xMArrayUpdate weight2 (19*(x-1)+y-1) (-1) `thenIO` \() -> + playGame xinfo state + else playGame xinfo state + +choice :: XPoint -> XInfo -> GameState -> IO () +choice (XPoint x y) xinfo@(XInfo display _ _ _ _) state = + let (GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) = state + in + case (getposition x y) of + XNull -> humanplay xinfo state + XSome (x, y) -> + xMArrayLookup board (19*(x-1)+y-1) `thenIO` \ z -> + if (z>0) + then xBell display 0 `thenIO` \ _ -> + drawCmd "Wrong point, please re-enter" xinfo state `thenIO` \() -> + humanplay xinfo state + else xMArrayLookup next_player 0 `thenIO` \ next_player_num -> + drawPiece x y xinfo (next_player_num == 1) `thenIO` \() -> + updateboard xinfo state x y + +humanplay :: XInfo -> GameState -> IO () +humanplay xinfo state = waitButton xinfo state choice + +computerplay :: XInfo -> GameState -> IO () +computerplay xinfo@(XInfo display window gcontext gcontext2 gcontextp) + state = + let process_events xinfo state cont = + xEventListen display `thenIO` \ n_event -> + if n_event == 0 then cont xinfo state + else xGetEvent display `thenIO` \ event -> + case (xEventType event) of + XButtonPressEvent -> + handleButton (xEventPos event) xinfo state cont + XExposureEvent -> + may_redraw (xEventCount event == 0) + xinfo state + `thenIO` \ _ -> + process_events xinfo state cont + XKeyPressEvent -> + process_events xinfo state cont + in + process_events xinfo state $ + \ xinfo@(XInfo display window gcontext gcontext2 gcontextp) + state@(GameState _ _ _ _ weight1 weight2 _ numbersteps _ next_player) -> + robot numbersteps weight1 weight2 `thenIO` \pt -> + let (XPoint x y) = pt + in + xMArrayLookup next_player 0 `thenIO` \ next_player_num -> + drawPiece x y xinfo (next_player_num == 1) `thenIO` \() -> + updateboard xinfo state x y + + + + diff --git a/progs/demo/X11/gobang/gobang.hu b/progs/demo/X11/gobang/gobang.hu new file mode 100644 index 0000000..d228bb2 --- /dev/null +++ b/progs/demo/X11/gobang/gobang.hu @@ -0,0 +1,7 @@ +:o= foldr inline constant +$HASKELL_LIBRARY/X11/xlib.hu +gobang.hs +misc.hi +utilities.hs +redraw.hs +weights.hs diff --git a/progs/demo/X11/gobang/misc.hi b/progs/demo/X11/gobang/misc.hi new file mode 100644 index 0000000..29a29be --- /dev/null +++ b/progs/demo/X11/gobang/misc.hi @@ -0,0 +1,7 @@ +interface Misc where + +random :: Int -> IO Int + +{-# +random :: LispName("lisp:random") +#-} \ No newline at end of file diff --git a/progs/demo/X11/gobang/misc.hu b/progs/demo/X11/gobang/misc.hu new file mode 100644 index 0000000..42a9c68 --- /dev/null +++ b/progs/demo/X11/gobang/misc.hu @@ -0,0 +1,2 @@ +misc.hi + diff --git a/progs/demo/X11/gobang/redraw.hs b/progs/demo/X11/gobang/redraw.hs new file mode 100644 index 0000000..9ec772f --- /dev/null +++ b/progs/demo/X11/gobang/redraw.hs @@ -0,0 +1,160 @@ +module Redraw where + +import Xlib +import Utilities + +may_redraw :: Bool -> XInfo -> GameState -> IO () +may_redraw ok xinfo state = if ok then redraw xinfo state else returnIO () + +redraw :: XInfo -> GameState -> IO () + +redraw xinfo state = + let (XInfo display window gcontext gcontext2 gcontextp) = xinfo + in + xDrawRectangle (XDrawWindow window) gcontext2 (XRect 0 0 900 600) True + `thenIO` \ _ -> + drawBoard xinfo `thenIO` \ () -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 610 65) "Player 1" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 620 125) "Clock 1" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 610 205) "Player 2" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 620 265) "Clock 2" + `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 700 45 130 30) False + `thenIO` \ () -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 700 105 90 30) False + `thenIO` \ () -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 700 185 130 30) False + `thenIO` \() -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 700 245 90 30) False + `thenIO` \() -> + button 700 330 "New players" xinfo `thenIO` \() -> + button 700 360 "New game" xinfo `thenIO` \() -> + button 700 390 "Undo" xinfo `thenIO` \() -> + button 700 420 "Load" xinfo `thenIO` \() -> + button 700 450 "Save" xinfo `thenIO` \() -> + button 700 480 "Quit" xinfo `thenIO` \() -> + helpButton xinfo `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 615 535 250 30) False + `thenIO` \ _ -> + let (GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) = state + in + xMArrayLookup time 0 `thenIO` \ lstm0 -> + xMArrayLookup time 1 `thenIO` \ lstm1 -> + showtime 705 270 (lstm1) xinfo `thenIO` \() -> + showtime 705 130 (lstm0) xinfo `thenIO` \() -> + xMArrayLookup player1 0 `thenIO` \ player1_name -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 710 65) player1_name + `thenIO` \ _ -> + xMArrayLookup player2 0 `thenIO` \ player2_name -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 710 205) player2_name + `thenIO` \ _ -> + xMArrayLookup promptString 0 `thenIO` \ ps -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 620 550) ps + `thenIO` \ _ -> + xMArrayLookup next_player 0 `thenIO` \ next_player_num -> + (if (next_player_num == 1) + then xDrawGlyph (XDrawWindow window) gcontextp (XPoint 850 70) '<' + else xDrawGlyph (XDrawWindow window) gcontextp (XPoint 850 210) '<') + `thenIO` \ _ -> + drawPieces 1 1 board xinfo `thenIO` \ _ -> + returnIO () + +drawHelp (XInfo display window gcontext gcontext2 gcontextp) = + xDrawRectangle (XDrawWindow window) gcontext2 (XRect 100 100 300 200) True + `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 100 100 300 200) False + `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 102 102 296 196) False + `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 200 230 100 60) False + `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 202 232 96 56) False + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 240 265) "OK" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 120 120) + "Two players in turn place black and white" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 120 135) + "pieces on the board. The winner is the" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 120 150) + "player who first makes five consecutive" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 120 165) + "pieces in either vertical, horizontal or" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 120 180) + "diagonal directions." + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 120 200) + "To play with a robot, type \"computer\" as" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 120 215) + "the name of another player." + + +drawBoard (XInfo display window gcontext gcontext2 gcontextp) = + drawvlines 30 30 1 `thenIO` \() -> + drawhlines 30 30 1 `thenIO` \() -> + drawmarks where + + drawvlines :: Int -> Int -> Int -> IO () + drawvlines x y z + | z <= 19 + = xDrawLine (XDrawWindow window) gcontext + (XPoint x y) (XPoint x (y+30*18)) `thenIO` \() -> + drawvlines (x+30) y (z+1) + | otherwise + = returnIO () + + drawhlines :: Int -> Int -> Int -> IO () + drawhlines x y z + | z <= 19 + = xDrawLine (XDrawWindow window) gcontext + (XPoint x y) (XPoint (x+30*18) y) `thenIO` \() -> + drawhlines x (y+30) (z+1) + | otherwise + = returnIO () + + drawmarks :: IO () + drawmarks = + map2IO (\x y -> + xDrawArc (XDrawWindow window) gcontext + (XArc x y 6 6 (-1.0) 6.283) True) + (map (\x -> 30 + x*30-3) [3,9,15,3,9,15,3,9,15]) + (map (\x -> 30 + x*30-3) [3,3,3,9,9,9,15,15,15]) + `thenIO` \ _ -> returnIO () + +map2IO :: (a -> b -> IO c) -> [a] -> [b] -> IO [c] + +map2IO f [] [] = returnIO [] +map2IO f (x:xs) (z:zs) = f x z `thenIO` \ y -> + map2IO f xs zs `thenIO` \ ys -> + returnIO (y:ys) + +drawPieces 20 _ board xinfo = returnIO () +drawPieces x 20 board xinfo = drawPieces (x+1) 1 board xinfo +drawPieces x y board xinfo = + xMArrayLookup board ((x-1)*19 + y-1) `thenIO` \ piece -> + (if (piece == 1 || piece == 2) + then drawPiece x y xinfo (piece == 1) + else returnIO ()) `thenIO` \ _ -> + drawPieces x (y+1) board xinfo + +drawPiece x y (XInfo display window gcontext gcontext2 _ ) is_black = + (if is_black then returnIO () + else xDrawArc (XDrawWindow window) gcontext2 + (XArc (30*x-10) (30*y-10) 20 20 + (-1.0) 6.283) + True) `thenIO` \ _ -> + xDrawArc (XDrawWindow window) gcontext + (XArc (30*x-10) (30*y-10) 20 20 + (-1.0) 6.283) + is_black `thenIO` \ _ -> + xDisplayForceOutput display + diff --git a/progs/demo/X11/gobang/redraw.hu b/progs/demo/X11/gobang/redraw.hu new file mode 100644 index 0000000..7d5aa14 --- /dev/null +++ b/progs/demo/X11/gobang/redraw.hu @@ -0,0 +1,4 @@ +:o= all +$HASKELL_LIBRARY/X11/xlib.hu +redraw.hs +utilities.hs diff --git a/progs/demo/X11/gobang/utilities.hs b/progs/demo/X11/gobang/utilities.hs new file mode 100644 index 0000000..fe2483b --- /dev/null +++ b/progs/demo/X11/gobang/utilities.hs @@ -0,0 +1,305 @@ +module Utilities where + +import Xlib +import Weights +import Redraw +import Misc + +data XInfo = XInfo XDisplay XWindow XGcontext XGcontext XGcontext +data GameState = GameState (XMArray String) (XMArray String) (XMArray Int) + (XMArray Int) (XMArray Int) (XMArray Int) + (XMArray Integer) (XMArray Int) + (XMArray String) (XMArray Int) + +type GameCont = XInfo -> GameState -> IO () + +xMArrayToList :: XMArray a -> IO [a] +xMArrayToList a = + let la = xMArrayLength a + loop i a = if i == la then returnIO [] + else xMArrayLookup a i `thenIO` \ x -> + loop (i+1) a `thenIO` \ xs -> + returnIO (x:xs) + in + loop 0 a + + +readGameState str = + let + [(board_lst, r1)] = reads str + [(weight1_lst, r2)] = reads r1 + [(weight2_lst, r3)] = reads r2 + [(steps_lst, r4)] = reads r3 + [(player1_lst, r5)] = reads r4 + [(player2_lst, r6)] = reads r5 + [(time_lst, r7)] = reads r6 + [(numbersteps_lst, r8)] = reads r7 + [(promptString_lst, r9)] = reads r8 + [(next_player_lst, [])] = reads r9 + in + xMArrayCreate board_lst `thenIO` \ board -> + xMArrayCreate weight1_lst `thenIO` \ weight1 -> + xMArrayCreate weight2_lst `thenIO` \ weight2 -> + xMArrayCreate steps_lst `thenIO` \ steps -> + xMArrayCreate player1_lst `thenIO` \ player1 -> + xMArrayCreate player2_lst `thenIO` \ player2 -> + xMArrayCreate time_lst `thenIO` \ time -> + xMArrayCreate numbersteps_lst `thenIO` \ numbersteps -> + xMArrayCreate promptString_lst `thenIO` \ promptString -> + xMArrayCreate next_player_lst `thenIO` \ next_player -> + returnIO (GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) + +showGameState (GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) = + xMArrayToList board `thenIO` \ board_lst -> + xMArrayToList weight1 `thenIO` \ weight1_lst -> + xMArrayToList weight2 `thenIO` \ weight2_lst -> + xMArrayToList steps `thenIO` \ steps_lst -> + xMArrayToList player1 `thenIO` \ player1_lst -> + xMArrayToList player2 `thenIO` \ player2_lst -> + xMArrayToList time `thenIO` \ time_lst -> + xMArrayToList numbersteps `thenIO` \ numbersteps_lst -> + xMArrayToList promptString `thenIO` \ promptString_lst -> + xMArrayToList next_player `thenIO` \ next_player_lst -> + let + str =(shows board_lst . + shows weight1_lst . + shows weight2_lst . + shows steps_lst . + shows player1_lst . + shows player2_lst . + shows time_lst . + shows numbersteps_lst . + shows promptString_lst . + shows next_player_lst) [] + in + returnIO str + + +xMod :: Int -> Int -> Int +xMod x y | x >= y = xMod (x-y) y + | otherwise = x + +xRes :: Int -> Int -> Int -> Int +xRes x y z | x >= y = xRes (x-y) y (z+1) + | otherwise = z + +drawCmd :: String -> XInfo -> GameState -> IO () +drawCmd a (XInfo display window gcontext gcontext2 gcontextp) + (GameState _ _ _ _ _ _ _ _ str _) + = xDrawRectangle (XDrawWindow window) gcontext2 + (XRect 616 536 248 28) True `thenIO` \ () -> + xDrawGlyphs (XDrawWindow window) gcontext + (XPoint 620 550) a `thenIO` \ _ -> + xMArrayUpdate str 0 a `thenIO` \ _ -> + xDisplayForceOutput display + +clearCmd :: XInfo -> GameState -> IO () +clearCmd (XInfo display window gcontext gcontext2 gcontextp) + (GameState _ _ _ _ _ _ _ _ str _) + = xDrawRectangle (XDrawWindow window) gcontext2 + (XRect 616 536 248 28) True `thenIO` \() -> + xMArrayUpdate str 0 "" `thenIO` \ _ -> + xDisplayForceOutput display + +xPosition :: Int -> XPoint +xPosition a = (XPoint (xRes a 19 1) (1+ (xMod a 19))) + +initArray :: XMArray a -> Int -> Int -> a -> IO () +initArray mary x y z | x + initArray mary (x+1) y z + | otherwise = returnIO () + +getposition :: Int -> Int -> XMaybe (Int, Int) +getposition x y = let x1 = round ((fromIntegral x) / 30.0) + y1 = round ((fromIntegral y) / 30.0) + in + if (x1 < 1 || x1 > 19 || y1 < 1 || y1 > 19) then XNull + else XSome (x1, y1) + +addZero :: Int -> String +addZero a | a < 10 = "0" + | otherwise = "" + +printTime :: Int -> Int -> [Int] -> XInfo -> IO() +printTime x y zs (XInfo display window gcontext gcontext2 gcontextp) + = let s = head zs + m = head (tail zs) + h = head (tail (tail zs)) + in xDrawRectangle (XDrawWindow window) gcontext2 + (XRect (x-4) (y-24) 88 28) True `thenIO` \() -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint x y) + ((addZero h)++(show h)++":"++(addZero m)++(show m)++ + ":"++(addZero s)++(show s)) + `thenIO` \(trash) -> + xDisplayForceOutput display + +showtime :: Int -> Int -> Integer -> XInfo -> IO() +showtime x y z a = + let (curtm, c) = (decodeTime z (WestOfGMT 0)) + in printTime x y curtm a + +helpButton :: XInfo -> IO () +helpButton (XInfo display window gcontext gcontext2 gcontextp) = + xDrawRectangle (XDrawWindow window) gcontext (XRect 800 420 70 70) + False `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext (XRect 802 422 66 66) + False `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 810 450) "About" + `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 820 470) "Gobang" + `thenIO` \ _ -> + returnIO () + +ishelp :: Int -> Int -> Bool +ishelp x y = (x > 800 && x < 870 && y > 420 && y < 490) + +button :: Int -> Int -> String -> XInfo -> IO() +button x y a (XInfo display window gcontext gcontext2 gcontextp) = + xDrawArc (XDrawWindow window) gcontext + (XArc (x-40) (y-10) 20 20 1.5708 4.7124) True `thenIO` \() -> + xDrawRectangle (XDrawWindow window) gcontext + (XRect (x-30) (y-10) 60 20) True `thenIO` \() -> + xDrawArc (XDrawWindow window) gcontext + (XArc (x+20) (y-10) 20 20 (-1.0) 6.283) True `thenIO` \() -> + xDrawGlyphs (XDrawWindow window) gcontext2 + (XPoint (x-(length a * 3)) (y+4)) a `thenIO` \(trash) -> + xDisplayForceOutput display + +-- a b are the location of the button, c d are the point where we press the +-- button. + +buttonPress :: Int -> Int -> Int -> Int -> Bool +buttonPress a b c d | (abs (c-a))<=30 && (abs (d-b))<=10 = True + | (c-a+30)*(c-a+30)+(d-b)*(d-b)<=100 = True + | (c-a-30)*(c-a-30)+(d-b)*(d-b)<=100 = True + | otherwise = False + + + +randmax :: XMArray Int -> Int -> Int -> [Int] -> IO Int +randmax a ind max mi | ind > 360 = + let lmi = length mi + in case lmi of + 0 -> returnIO (-1) + 1 -> returnIO (head mi) + _ -> random lmi `thenIO` \ i -> + returnIO (mi !! i) + | otherwise = xMArrayLookup a ind `thenIO` \ tt3 -> + if (tt3 > max) + then randmax a (ind+1) tt3 [ind] + else if (tt3 == max) + then randmax a (ind+1) max (ind:mi) + else randmax a (ind+1) max mi + +robot :: XMArray Int -> XMArray Int -> XMArray Int -> IO XPoint +robot numbersteps weight1 weight2 + = xMArrayLookup numbersteps 0 `thenIO` \(tt5) -> + if (tt5 == 0) + then returnIO (XPoint 10 10) + else + randmax weight1 0 0 [] `thenIO` \ tmp1 -> + randmax weight2 0 0 [] `thenIO` \ tmp2 -> + xMArrayLookup weight1 tmp1 `thenIO` \ tmp3 -> + xMArrayLookup weight2 tmp2 `thenIO` \ tmp4 -> + if (tmp3 >= 200) + then returnIO (xPosition tmp1) + else if (tmp3 > tmp4) + then returnIO (xPosition tmp1) + else returnIO (xPosition tmp2) + + +promptFor prompt xinfo state = + let (GameState player1 player2 board steps weight1 weight2 time + numbersteps promptString next_player) = state + (XInfo display window gcontext gcontext2 gcontextp) = xinfo + in + xDrawRectangle (XDrawWindow window) gcontext2 + (XRect 616 536 248 28) True `thenIO` \() -> + xMArrayUpdate promptString 0 prompt `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint 620 550) prompt + `thenIO` \ _ -> + xDisplayForceOutput display `thenIO` \ _ -> + let h_base = (length prompt + 1) * 6 + 620 + getString :: Int -> String -> IO String + getString h_pos sofar = + xGetEvent display `thenIO` \event -> + case (xEventType event) of + XButtonPressEvent -> + let (XPoint x y) = xEventPos event + in + (if ishelp x y then helpGame xinfo state + else xBell display 0) + `thenIO` \ _ -> + getString h_pos sofar + XExposureEvent -> + may_redraw (xEventCount event == 0) xinfo state `thenIO` \ _ -> + xDrawGlyphs (XDrawWindow window) gcontext (XPoint h_base 550) sofar + `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext + (XRect (h_base + 6 * h_pos) (550-10) 6 13) True + `thenIO` \ _ -> getString h_pos sofar + XKeyPressEvent -> + let code = xEventCode event + state = xEventState event + bs = if (sofar == "") then getString h_pos sofar + else xDrawRectangle (XDrawWindow window) gcontext2 + (XRect (h_base + 6 * h_pos) + (550-10) 6 13) + True `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext + (XRect (h_base + 6 * (h_pos - 1)) + (550-10) 6 13) + True `thenIO` \ _ -> + getString (h_pos-1) (take (length sofar - 1) sofar) + in + xKeycodeCharacter display code state `thenIO` \ char -> + case char of + (XSome '\r') -> returnIO sofar + (XSome '\DEL') -> bs + (XSome '\BS') -> bs + XNull -> getString h_pos sofar + (XSome c) -> xDrawRectangle (XDrawWindow window) gcontext2 + (XRect (h_base + 6 * h_pos) + (550-10) 6 13) + True `thenIO` \ _ -> + xDrawGlyph (XDrawWindow window) gcontext + (XPoint (h_base + 6 * h_pos) 550) c + `thenIO` \ _ -> + xDrawRectangle (XDrawWindow window) gcontext + (XRect (h_base + 6 * (h_pos + 1)) + (550-10) 6 13) + True `thenIO` \ _ -> + getString (h_pos + 1) (sofar ++ [c]) + + in + xDrawRectangle (XDrawWindow window) gcontext + (XRect h_base (550-10) 6 13) True + `thenIO` \ _ -> + getString 0 "" + + +helpGame xinfo@(XInfo display window gcontext gcontext2 gcontextp) state = + drawHelp xinfo `thenIO` \ _ -> + let + loop xinfo state = + xGetEvent display `thenIO` \ event -> + case (xEventType event) of + XExposureEvent -> may_redraw (xEventCount event == 0) xinfo state + `thenIO` \ _ -> + drawHelp xinfo `thenIO` \ _ -> + loop xinfo state + XButtonPressEvent -> + let (XPoint x y) = xEventPos event + in + if (x > 200 && x < 300 && y > 230 && y < 290) + then redraw xinfo state `thenIO` \ _ -> + returnIO () + else loop xinfo state + _ -> xBell display 0 `thenIO` \ _ -> + loop xinfo state + in + loop xinfo state + + diff --git a/progs/demo/X11/gobang/utilities.hu b/progs/demo/X11/gobang/utilities.hu new file mode 100644 index 0000000..bfccbfe --- /dev/null +++ b/progs/demo/X11/gobang/utilities.hu @@ -0,0 +1,6 @@ +:o= all +$HASKELL_LIBRARY/X11/xlib.hu +utilities.hs +weights.hs +redraw.hs +misc.hi diff --git a/progs/demo/X11/gobang/weights.hs b/progs/demo/X11/gobang/weights.hs new file mode 100644 index 0000000..1b55553 --- /dev/null +++ b/progs/demo/X11/gobang/weights.hs @@ -0,0 +1,323 @@ +module Weights where + +import Xlib +import Utilities + +xlookup :: XMArray Int -> Int -> Int -> IO Int +xlookup keyboard x y = + if (x < 1 || x > 19 || y < 1 || y > 19) + then returnIO (-2) + else xMArrayLookup keyboard ((x-1)*19+(y-1)) + + +draw_unit :: XMArray Int -> XMArray Int -> XMArray Int -> Int -> Int -> IO() +draw_unit keyboard weight1 weight2 x y = + let + update_weight :: XMArray Int->Int->Int->Int->Int->Int->Int->IO() + update_weight weight counter player x y incr_x incr_y + | x>=1 && x<=19 && y>=1 && y<=19 && counter<=4 = + cpt_weight x y player `thenIO` \wt -> + xMArrayUpdate weight ((x-1)*19+(y-1)) wt `thenIO` \() -> + update_weight weight (counter+1) player (x+incr_x) (y+incr_y) + incr_x incr_y + | otherwise = returnIO () +---------------------------------------------------------------------------- + + pattern0 :: Int -> Int -> Int -> Int -> Int -> Int -> Bool + pattern0 a b c d e p | a==p && b==p && c==p && d==p && e==p = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern1 :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Bool + pattern1 a b c d e f p | (a==0) && (b==p) && (c==p) && (d==p) && (e==p) && + (f==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern2 :: Int -> Int -> Int -> Int -> Int -> Int -> Bool + pattern2 a b c d e p | (a==0 && b==p && c==p && d==p && e==p)|| + (a==p && b==p && c==p && d==p && e==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern3 :: Int -> Int -> Int -> Int -> Int -> Int -> Bool + pattern3 a b c d e p | (a==0 && b==p && c==p && d==p && e==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern4 :: Int -> Int -> Int -> Int -> Int -> Bool + pattern4 a b c d p | (a==0 && b==p && c==p && d==p) || + (a==p && b==p && c==p && d==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern5 :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Bool + pattern5 a b c d e f p | (a==0 && b==p && c==p && d==0 && e==p && + f==0) || + (a==0 && b==p && c==0 && d==p && e==p && + f==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern6 :: Int -> Int -> Int -> Int -> Int -> Int -> Bool + pattern6 a b c d e p | (a==0 && b==p && c==p && d==0 && e==p) || + (a==0 && b==p && c==0 && d==p && e==p) || + (a==p && b==p && c==0 && d==p && e==0) || + (a==p && b==0 && c==p && d==p && e==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern7 :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int-> Bool + pattern7 a b c d e f g p | (a==0 && b==p && c==0 && d==p && e==0 && + f==p && g==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern8 :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Bool + pattern8 a b c d e f p | (a==0 && b==p && c==0 && d==p && e==0 && + f==p) || + (a==p && b==0 && c==p && d==0 && e==p && + f==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern9 :: Int -> Int -> Int -> Int -> Int -> Bool + pattern9 a b c d p | (a==0 && b==p && c==p && d==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern10 :: Int -> Int -> Int -> Int -> Bool + pattern10 a b c p | (a==0 && b==p && c==p) || + (a==p && b==p && c==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern11 :: Int -> Int -> Int -> Int -> Int -> Int -> Bool + pattern11 a b c d e p | (a==0 && b==p && c==0 && d==p && e==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + pattern12 :: Int -> Int -> Int -> Int -> Int -> Bool + pattern12 a b c d p | (a==0 && b==p && c==0 && d==p) || + (a==p && b==0 && c==p && d==0) = True + | otherwise = False +---------------------------------------------------------------------------- + + direct1 :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> + Int -> Int -> Int -> Int -> Int -> Int + direct1 x y pl ptN1 ptN2 ptN3 ptN4 ptN5 pt ptP1 ptP2 ptP3 ptP4 ptP5 + | (pattern0 ptN4 ptN3 ptN2 ptN1 pt pl) || + (pattern0 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern0 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern0 ptN1 pt ptP1 ptP2 ptP3 pl) || + (pattern0 pt ptP1 ptP2 ptP3 ptP4 pl) = 200 + | (pattern1 ptN4 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern1 ptN3 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern1 ptN2 ptN1 pt ptP1 ptP2 ptP3 pl) || + (pattern1 ptN1 pt ptP1 ptP2 ptP3 ptP4 pl) = 40 + | (pattern2 ptN4 ptN3 ptN2 ptN1 pt pl) || + (pattern2 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern2 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern2 ptN1 pt ptP1 ptP2 ptP3 pl) = 13 + | (pattern3 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern3 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern3 ptN1 pt ptP1 ptP2 ptP3 pl) = 10 + | (pattern4 ptN3 ptN2 ptN1 pt pl) || + (pattern4 ptN2 ptN1 pt ptP1 pl) || + (pattern4 ptN1 pt ptP1 ptP2 pl) = 8 + | (pattern5 ptN4 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern5 ptN3 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern5 ptN2 ptN1 pt ptP1 ptP2 ptP3 pl) || + (pattern5 ptN1 pt ptP1 ptP2 ptP3 ptP4 pl) = 9 + | (pattern6 ptN4 ptN3 ptN2 ptN1 pt pl) || + (pattern6 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern6 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern6 ptN1 pt ptP1 ptP2 ptP3 pl) = 7 + | (pattern7 ptN5 ptN4 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern7 ptN4 ptN3 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern7 ptN3 ptN2 ptN1 pt ptP1 ptP2 ptP3 pl) || + (pattern7 ptN2 ptN1 pt ptP1 ptP2 ptP3 ptP4 pl) || + (pattern7 ptN1 pt ptP1 ptP2 ptP3 ptP4 ptP5 pl) = 6 + | (pattern8 ptN5 ptN4 ptN3 ptN2 ptN1 pt pl) || + (pattern8 ptN4 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern8 ptN3 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern8 ptN2 ptN1 pt ptP1 ptP2 ptP3 pl) || + (pattern8 ptN1 pt ptP1 ptP2 ptP3 ptP4 pl) || + (pattern8 pt ptP1 ptP2 ptP3 ptP4 ptP5 pl) = 5 + | (pattern9 ptN2 ptN1 pt ptP1 pl) || + (pattern9 ptN1 pt ptP1 ptP2 pl) = 4 + | (pattern10 ptN2 ptN1 pt pl) || + (pattern10 ptN1 pt ptP1 pl) || + (pattern10 pt ptP1 ptP2 pl) = 2 + | (pattern11 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern11 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern11 ptN1 pt ptP1 ptP2 ptP3 pl) = 3 + | (pattern12 ptN3 ptN2 ptN1 pt pl) || + (pattern12 ptN2 ptN1 pt ptP1 pl) || + (pattern12 ptN1 pt ptP1 ptP2 pl) || + (pattern12 pt ptP1 ptP2 ptP3 pl) = 1 + | otherwise = 0 +---------------------------------------------------------------------------- + + direct2 :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> + Int -> Int -> Int -> Int -> Int -> Int + direct2 x y pl ptN1 ptN2 ptN3 ptN4 ptN5 pt ptP1 ptP2 ptP3 ptP4 ptP5 + | (pattern0 ptN4 ptN3 ptN2 ptN1 pt pl) || + (pattern0 ptN3 ptN2 ptN1 pt ptP1 pl) || + (pattern0 ptN2 ptN1 pt ptP1 ptP2 pl) || + (pattern0 ptN1 pt ptP1 ptP2 ptP3 pl) || + (pattern0 pt ptP1 ptP2 ptP3 ptP4 pl) = 200 + | otherwise = 0 +----------------------------------------------------------------------------- + + cpt_weight :: Int -> Int -> Int -> IO Int + cpt_weight x y player = + xMArrayLookup keyboard ((x-1)*19+(y-1)) `thenIO` \(unit) -> + if (unit /= 0) + then returnIO (-1) + else xlookup keyboard x (y-1) `thenIO` \(xyN1) -> + xlookup keyboard x (y-2) `thenIO` \(xyN2) -> + xlookup keyboard x (y-3) `thenIO` \(xyN3) -> + xlookup keyboard x (y-4) `thenIO` \(xyN4) -> + xlookup keyboard x (y-5) `thenIO` \(xyN5) -> + xlookup keyboard x (y+1) `thenIO` \(xyP1) -> + xlookup keyboard x (y+2) `thenIO` \(xyP2) -> + xlookup keyboard x (y+3) `thenIO` \(xyP3) -> + xlookup keyboard x (y+4) `thenIO` \(xyP4) -> + xlookup keyboard x (y+5) `thenIO` \(xyP5) -> + xlookup keyboard (x-1) y `thenIO` \(xN1y) -> + xlookup keyboard (x-2) y `thenIO` \(xN2y) -> + xlookup keyboard (x-3) y `thenIO` \(xN3y) -> + xlookup keyboard (x-4) y `thenIO` \(xN4y) -> + xlookup keyboard (x-5) y `thenIO` \(xN5y) -> + xlookup keyboard (x+1) y `thenIO` \(xP1y) -> + xlookup keyboard (x+2) y `thenIO` \(xP2y) -> + xlookup keyboard (x+3) y `thenIO` \(xP3y) -> + xlookup keyboard (x+4) y `thenIO` \(xP4y) -> + xlookup keyboard (x+5) y `thenIO` \(xP5y) -> + xlookup keyboard (x-1) (y-1) `thenIO` \(xN1yN1)-> + xlookup keyboard (x-2) (y-2) `thenIO` \(xN2yN2) -> + xlookup keyboard (x-3) (y-3) `thenIO` \(xN3yN3) -> + xlookup keyboard (x-4) (y-4) `thenIO` \(xN4yN4) -> + xlookup keyboard (x-5) (y-5) `thenIO` \(xN5yN5) -> + xlookup keyboard (x+1) (y+1) `thenIO` \(xP1yP1) -> + xlookup keyboard (x+2) (y+2) `thenIO` \(xP2yP2) -> + xlookup keyboard (x+3) (y+3) `thenIO` \(xP3yP3) -> + xlookup keyboard (x+4) (y+4) `thenIO` \(xP4yP4) -> + xlookup keyboard (x+5) (y+5) `thenIO` \(xP5yP5) -> + xlookup keyboard (x-1) (y+1) `thenIO` \(xN1yP1) -> + xlookup keyboard (x-2) (y+2) `thenIO` \(xN2yP2) -> + xlookup keyboard (x-3) (y+3) `thenIO` \(xN3yP3) -> + xlookup keyboard (x-4) (y+4) `thenIO` \(xN4yP4) -> + xlookup keyboard (x-5) (y+5) `thenIO` \(xN5yP5) -> + xlookup keyboard (x+1) (y-1) `thenIO` \(xP1yN1) -> + xlookup keyboard (x+2) (y-2) `thenIO` \(xP2yN2) -> + xlookup keyboard (x+3) (y-3) `thenIO` \(xP3yN3) -> + xlookup keyboard (x+4) (y-4) `thenIO` \(xP4yN4) -> + xlookup keyboard (x+5) (y-5) `thenIO` \(xP5yN5) -> + returnIO ( (direct1 x y player xyN1 xyN2 xyN3 xyN4 xyN5 player + xyP1 xyP2 xyP3 xyP4 xyP5) + + (direct1 x y player xN1y xN2y xN3y xN4y xN5y player + xP1y xP2y xP3y xP4y xP5y) + + (direct1 x y player xN1yN1 xN2yN2 xN3yN3 xN4yN4 + xN5yN5 player xP1yP1 xP2yP2 xP3yP3 xP4yP4 + xP5yP5) + + (direct1 x y player xN1yP1 xN2yP2 xN3yP3 xN4yP4 + xN5yP5 player xP1yN1 xP2yN2 xP3yN3 xP4yN4 + xP5yN5) ) +----------------------------------------------------------------------------- + +-- | 1111 && no_block = 20 +-- | 1111 && one_block = 13 +-- | 111 && no_block = 10 +-- | 111 && one_block = 8 +-- | 11 1 or 1 11 && no_block = 9 +-- | 11 1 or 1 11 && one_block =7 +-- | 1 1 1 && no_block = 6 +-- | 1 1 1 && one_block= 5 +-- | 11 && no_block = 4 +-- | 11 && one_block =2 +-- | 1 1 && no_block =3 +-- | 1 1 && one_block=1 + + in + update_weight weight1 0 1 x y 1 1 `thenIO` \() -> + update_weight weight2 0 2 x y 1 1 `thenIO` \() -> + update_weight weight1 0 1 x y 1 (-1) `thenIO` \() -> + update_weight weight2 0 2 x y 1 (-1) `thenIO` \() -> + update_weight weight1 0 1 x y (-1) (-1) `thenIO` \() -> + update_weight weight2 0 2 x y (-1) (-1) `thenIO` \() -> + update_weight weight1 0 1 x y (-1) 1 `thenIO` \() -> + update_weight weight2 0 2 x y (-1) 1 `thenIO` \() -> + update_weight weight1 0 1 x y 0 1 `thenIO` \() -> + update_weight weight2 0 2 x y 0 1 `thenIO` \() -> + update_weight weight1 0 1 x y 0 (-1) `thenIO` \() -> + update_weight weight2 0 2 x y 0 (-1) `thenIO` \() -> + update_weight weight1 0 1 x y (-1) 0 `thenIO` \() -> + update_weight weight2 0 2 x y (-1) 0 `thenIO` \() -> + update_weight weight1 0 1 x y 1 0 `thenIO` \() -> + update_weight weight2 0 2 x y 1 0 `thenIO` \() -> + returnIO () + + +human_unit :: XMArray Int -> Int -> Int -> IO(Bool) +human_unit keyboard x y = + let + pattern0 :: Int -> Int -> Int -> Int -> Int -> Bool + pattern0 a b c d e | a==b && b==c && c==d && d==e = True + | otherwise = False + + direct3 :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> + Int + direct3 ptN1 ptN2 ptN3 ptN4 pt ptP1 ptP2 ptP3 ptP4 + | (pattern0 ptN4 ptN3 ptN2 ptN1 pt) || + (pattern0 ptN3 ptN2 ptN1 pt ptP1) || + (pattern0 ptN2 ptN1 pt ptP1 ptP2) || + (pattern0 ptN1 pt ptP1 ptP2 ptP3) || + (pattern0 pt ptP1 ptP2 ptP3 ptP4) = 200 + | otherwise = 0 + in + xlookup keyboard x y `thenIO` \(xy) -> + xlookup keyboard x (y-1) `thenIO` \(xyN1) -> + xlookup keyboard x (y-2) `thenIO` \(xyN2) -> + xlookup keyboard x (y-3) `thenIO` \(xyN3) -> + xlookup keyboard x (y-4) `thenIO` \(xyN4) -> + xlookup keyboard x (y+1) `thenIO` \(xyP1) -> + xlookup keyboard x (y+2) `thenIO` \(xyP2) -> + xlookup keyboard x (y+3) `thenIO` \(xyP3) -> + xlookup keyboard x (y+4) `thenIO` \(xyP4) -> + xlookup keyboard (x-1) y `thenIO` \(xN1y) -> + xlookup keyboard (x-2) y `thenIO` \(xN2y) -> + xlookup keyboard (x-3) y `thenIO` \(xN3y) -> + xlookup keyboard (x-4) y `thenIO` \(xN4y) -> + xlookup keyboard (x+1) y `thenIO` \(xP1y) -> + xlookup keyboard (x+2) y `thenIO` \(xP2y) -> + xlookup keyboard (x+3) y `thenIO` \(xP3y) -> + xlookup keyboard (x+4) y `thenIO` \(xP4y) -> + xlookup keyboard (x-1) (y-1) `thenIO` \(xN1yN1)-> + xlookup keyboard (x-2) (y-2) `thenIO` \(xN2yN2) -> + xlookup keyboard (x-3) (y-3) `thenIO` \(xN3yN3) -> + xlookup keyboard (x-4) (y-4) `thenIO` \(xN4yN4) -> + xlookup keyboard (x+1) (y+1) `thenIO` \(xP1yP1) -> + xlookup keyboard (x+2) (y+2) `thenIO` \(xP2yP2) -> + xlookup keyboard (x+3) (y+3) `thenIO` \(xP3yP3) -> + xlookup keyboard (x+4) (y+4) `thenIO` \(xP4yP4) -> + xlookup keyboard (x-1) (y+1) `thenIO` \(xN1yP1) -> + xlookup keyboard (x-2) (y+2) `thenIO` \(xN2yP2) -> + xlookup keyboard (x-3) (y+3) `thenIO` \(xN3yP3) -> + xlookup keyboard (x-4) (y+4) `thenIO` \(xN4yP4) -> + xlookup keyboard (x+1) (y-1) `thenIO` \(xP1yN1) -> + xlookup keyboard (x+2) (y-2) `thenIO` \(xP2yN2) -> + xlookup keyboard (x+3) (y-3) `thenIO` \(xP3yN3) -> + xlookup keyboard (x+4) (y-4) `thenIO` \(xP4yN4) -> + xlookup keyboard (x+1) y `thenIO` \(xP1y) -> + xlookup keyboard (x+2) y `thenIO` \(xP2y) -> + xlookup keyboard (x+3) y `thenIO` \(xP3y) -> + xlookup keyboard (x+4) y `thenIO` \(xP4y) -> + if ((direct3 xyN1 xyN2 xyN3 xyN4 xy xyP1 xyP2 xyP3 xyP4) + + (direct3 xN1y xN2y xN3y xN4y xy xP1y xP2y xP3y xP4y) + + (direct3 xN1yN1 xN2yN2 xN3yN3 xN4yN4 xy xP1yP1 xP2yP2 xP3yP3 xP4yP4) + + (direct3 xN1yP1 xN2yP2 xN3yP3 xN4yP4 xy xP1yN1 xP2yN2 xP3yN3 xP4yN4)) + >=200 + then returnIO (True) + else returnIO (False) \ No newline at end of file diff --git a/progs/demo/X11/gobang/weights.hu b/progs/demo/X11/gobang/weights.hu new file mode 100644 index 0000000..f13aba0 --- /dev/null +++ b/progs/demo/X11/gobang/weights.hu @@ -0,0 +1,4 @@ +:o= all +$HASKELL_LIBRARY/X11/xlib.hu +weights.hs +utilities.hs diff --git a/progs/demo/X11/graphics/README b/progs/demo/X11/graphics/README new file mode 100644 index 0000000..77a2d66 --- /dev/null +++ b/progs/demo/X11/graphics/README @@ -0,0 +1,31 @@ +HENDERSON GRAPHICS LIBRARY +by Syam Gadde +and Bo Whong + +------------------------------------------------- + +To use the Henderson Library, run emacs with a module that +imports HendersonLib, such as "sqrlmt.hs". For "sqrlmt.hs", +run the dialogue "final" or "skewedfinal". + +------------------------------------------------- + +henderson.hs - Haskell source for the Henderson library. +henderson.hu +sqrlmt.hs - Haskell source for dialogue that draws "Square Limit". +sqrlmt.hu +p.pic - First of four pictures used to construct "Square Limit". +q.pic - Second of four pictures used to construct "Square Limit". +r.pic - Third of four pictures used to construct "Square Limit". +s.pic - Four of four pictures used to construct "Square Limit". +new.pic - Hudak's house +stop.pic - A "hand-drawn" stop sign border +text.pic - The word "STOP!" (hand-drawn) +strange.pic - Overlays stop.pic and Flip of text.pic +squarebox.xwd - A window dump of a box-like structure made of four + square limits. Use "xwud -in squarebox.xwd" to view. +sksl.xwd - A window dump of "squarelimit" in a skewed bounding box. + ("skewedfinal" from sqrlmt.hs.) +sl.xwd - A window dump of Square Limit. + ("squarelimit" from sqrlmt.hs.) +manual - The manual in Island Write format. diff --git a/progs/demo/X11/graphics/henderson.hs b/progs/demo/X11/graphics/henderson.hs new file mode 100644 index 0000000..8b7e4ce --- /dev/null +++ b/progs/demo/X11/graphics/henderson.hs @@ -0,0 +1,465 @@ +-- Peter Henderson's Recursive Geometry +-- Syam Gadde and Bo Whong +-- full set of modules +-- CS429 Project +-- 4/30/93 + +module HendersonLib (Hostname(..), Filename(..), VTriple(..), HendQuartet(..), + Picture(..), sendToDraw, draw, create, modify, plot) where +import Xlib + +-- ADTs and Type Synonyms -------------------------------------------------- +data Picture = Nil + | Flip Picture + | Beside Float Picture Float Picture + | Above Float Picture Float Picture + | Rot Picture + | File String + | Overlay Picture Picture + | Grid Int Int SegList + deriving Text + +data Plot = Plot Picture VTriple + | Union Plot Plot + +type Hostname = String +type Filename = String +type IntPoint = (Int,Int) +type IntSegment = (IntPoint, IntPoint) +type IntSegList = [IntSegment] +type Point = (Float,Float) +type Segment = (Point, Point) +type SegList = [Segment] +type Vector = Point +type VTriple = (Vector, Vector, Vector) +type HendQuartet = (Int, Int, Int, Int) +type PEnv = [(Filename, Picture)] + +-- vector Functions -------------------------------------------------------- +-- for adding, negating, multiplying, and dividing vectors + +addV :: Vector -> Vector -> Vector +addV (x1,y1) (x2,y2) = (x1+x2, y1+y2) + +negateV :: Vector -> Vector +negateV (x,y) = (-x,-y) + +multV :: Float-> Vector -> Vector +multV a (x,y) = (a*x, a*y) + +divV :: Float -> Vector -> Vector +divV a (x,y) = (x/a, y/a) + +-- plot Function ----------------------------------------------------------- +-- picture manipulation function + +plot :: Picture -> VTriple -> PEnv -> ((Plot, PEnv) -> IO()) -> IO() + +-- the Nil Picture is just "nothingness" so choose an abritrary representation +-- of nothingness. +plot Nil (v1, v2, v3) env cont = + plot (Grid 1 1 []) (v1,v2,v3) env cont + +-- Flipping a Picture +plot (Flip p1) (v1, v2, v3) env cont = + plot p1 (addV v1 v2, negateV v2, v3) env cont + +-- Rotate a Picture 90 degrees counterclockwise +plot (Rot p1) (v1, v2, v3) env cont = + plot p1 (addV v1 v3, negateV v3, v2) env cont + +-- Overlay one Picture over another Picture +plot (Overlay p q) (a,b,c) env cont = + plot p (a,b,c) env $ \ (plot1, env1) -> + plot q (a,b,c) env1 $ \ (plot2, env2) -> + cont ((Union plot1 plot2), env2) + +-- Place p1 Beside p2 with width ratio m to n +plot (Beside m p1 n p2) (v1, v2, v3) env cont = + plot p1 (v1, multV (m/(m+n)) v2, v3) env $ \ (plot1, env1) -> + plot p2 ((addV (multV (m/(m+n)) v2) v1), + (multV (n/(m+n)) v2), + v3) env1 $ \ (plot2, env2) -> + cont ((Union plot1 plot2), env2) + +-- Place p Above q with height ratio m to n +plot (Above m p n q) (a,b,c) env cont = + plot q (addV a (multV (m/(n+m)) c), b, multV (n/(m+n)) c) env + $ \ (plot1, env1) -> + plot p (a, b, multV (m/(m+n)) c) env1 $ \ (plot2, env2) -> + cont ((Union plot1 plot2), env2) + +-- the 'real' Picture +plot (Grid x y s) (a,b,c) env cont = + cont ((Plot (Grid x y s) (a,b,c)), env) + +-- this picture is located in a File with name name +-- lookup table: thanks to Sheng +plot (File name) (a,b,c) env cont = + case (lookupEnv env name) of + ((_, pic):_) -> plot pic (a,b,c) env cont + [] -> + readFile name (\s -> appendChan stdout ("File "++name++" not able to be read\n") exit done) + $ \s -> + let + pic = read s + newenv = (name,pic):env + in + plot pic (a,b,c) newenv cont + +lookupEnv :: PEnv -> Filename -> PEnv +lookupEnv [] _ = [] +lookupEnv ((a,b):es) name | a==name = ((a,b):es) + | otherwise = lookupEnv es name + +-- Draw Function ----------------------------------------------------------- +-- user function to draw pictures + +draw :: Hostname -> Picture -> VTriple -> HendQuartet -> IO() + +-- opens a display, screen, and window (of size specified in HendQuartet) +-- and draws Picture in the window +draw host p (a,b,c) (hm,hn,ho,hp) = + xOpenDisplay host `thenIO` \display -> -- opens display + let (screen:_) = xDisplayRoots display + fg_color = xScreenBlackPixel screen + bg_color = xScreenWhitePixel screen + root = xScreenRoot screen + in + xCreateWindow root -- opens window + (XRect hm hn ho hp) + [XWinBackground bg_color, + XWinEventMask (XEventMask [XKeyPress, + XExposure, + XButtonPress])] + `thenIO` \window -> + xSetWmName window "Henderson Graphics" `thenIO` \() -> + xSetWmIconName window "Henderson Graphics" `thenIO` \() -> + xMapWindow window `thenIO` \() -> -- show window + xDisplayForceOutput display `thenIO` \ () -> -- show window NOW + xCreateGcontext (XDrawWindow (xScreenRoot screen)) -- open a GC + [XGCBackground bg_color, + XGCForeground fg_color] `thenIO` \ gcontext -> + plot p (a,b,c) [] $ \(plt,_) -> -- make pic easier to work with + let + handleEvent = + xGetEvent display `thenIO` \event -> + case (xEventType event) of + -- Has a part of the window been uncovered? + XExposureEvent -> sendToDraw window screen display gcontext plt + `thenIO` \() -> handleEvent + _ -> xCloseDisplay display + in + handleEvent + +-- SendToDraw Function ----------------------------------------------------- +-- called by draw to actually draw the lines onto the window + +sendToDraw :: XWindow -> XScreen -> XDisplay -> XGcontext -> Plot -> IO() + +-- have a Union. so do one, and then the other. simple. +sendToDraw win screen display gcontext (Union p1 p2) = + sendToDraw win screen display gcontext p1 `thenIO` \() -> + sendToDraw win screen display gcontext p2 + +-- have just a Plot. have to do some dirty work. +sendToDraw window screen display gcontext (Plot (Grid x y s) (a,b,c)) = + let + v2p :: Vector -> XPoint + v2p (e,f) = XPoint (round e) (round f) -- convert Vector to an XPoint + fx :: Float + fx = fromIntegral x + fy :: Float + fy = fromIntegral y + drawit :: SegList -> IO() + -- draw the Grid one line at a time + drawit [] = done + drawit (((x0,y0),(x1,y1)):ss) = + xDrawLine (XDrawWindow window) + gcontext + (v2p (addV (addV a (multV (x0/fx) b)) + (multV (y0/fy) c))) + (v2p (addV (addV a (multV (x1/fx) b)) + (multV (y1/fy) c))) `thenIO` \() -> + drawit ss + in + drawit s `thenIO` \ () -> + xDisplayForceOutput display + +-- create function --------------------------------------------------------- +-- opens up a window to allow the user to create a file +-- and save it onto a file + +create :: Hostname -> Filename -> Int -> Int -> IO() + +create host filename x y = + xOpenDisplay host `thenIO` \ display -> + let + (screen:_) = xDisplayRoots display + fg_color = xScreenWhitePixel screen + bg_color = xScreenBlackPixel screen + root = xScreenRoot screen + in + xCreateWindow root + (XRect 0 0 (x+1) (y+1)) + [XWinBackground bg_color, + XWinEventMask (XEventMask [XExposure, + XKeyPress, + XButtonPress, + XPointerMotion])] + `thenIO` \window -> + xSetWmName window filename `thenIO` \() -> + xSetWmIconName window filename `thenIO` \() -> + xCreateWindow root + (XRect 0 0 100 40) + [XWinBackground bg_color] `thenIO` \window2 -> + xSetWmName window2 "pos" `thenIO` \() -> + xSetWmIconName window2 "pos" `thenIO` \() -> + xMapWindow window `thenIO` \() -> + xMapWindow window2 `thenIO` \() -> + xListFonts display "*times*bold*r*normal*18*" `thenIO` \fontlist -> + xCreateGcontext (XDrawWindow root) + [XGCBackground bg_color, + XGCForeground fg_color, + XGCFont (head fontlist)] `thenIO` \gcontext -> + let + handleEvent :: IntSegList -> IO() + handleEvent list = + xGetEvent display `thenIO` \event -> + let + point = xEventPos event + XPoint pointx pointy = point + handleEvent' :: XPoint -> IO() + handleEvent' last = + xGetEvent display `thenIO` \event2 -> + let + pos = xEventPos event2 + XPoint posx posy = pos + in + case (xEventType event2) of + XKeyPressEvent -> + appendChan stdout ((show (tup pos))++ "\n") abort $ + xDrawLine (XDrawWindow window) gcontext point pos + `thenIO` \() -> handleEvent (store list point pos) + XExposureEvent -> + redraw window gcontext list `thenIO` \() -> handleEvent' last + XMotionNotifyEvent -> + xDrawImageGlyphs (XDrawWindow window2) + gcontext + (XPoint 2 18) + ((show posx)++", "++(show posy)++" ") + `thenIO` \dummy -> handleEvent' last + _ -> + handleEvent' last + in + case (xEventType event) of + XButtonPressEvent -> + putFile display filename list x y "create" + XKeyPressEvent -> + appendChan stdout (show (tup point)) abort $ + handleEvent' point + XExposureEvent -> + redraw window gcontext list `thenIO` \() -> handleEvent list + XMotionNotifyEvent -> + xDrawImageGlyphs (XDrawWindow window2) + gcontext + (XPoint 2 18) + ((show pointx)++", "++(show pointy)++" ") + `thenIO` \dummy -> handleEvent list + _ -> + handleEvent list + in + case (checkFile filename) of + True -> handleEvent [] + False -> appendChan stdout picTypeError abort $ + xCloseDisplay display + +-- modify function --------------------------------------------------------- +-- allows the user to add onto an already existing picture file + +modify :: Hostname -> Filename -> IO() + +modify host filename = + case (checkFile filename) of + False -> appendChan stdout picTypeError abort done + True -> + readFile filename (\s -> appendChan stdout + readError abort done) $ \s-> + let + dat = read s + origlist = fFloat (getlist dat) + x = getx dat + y = gety dat + in + xOpenDisplay host `thenIO` \ display -> + let + (screen:_) = xDisplayRoots display + fg_color = xScreenWhitePixel screen + bg_color = xScreenBlackPixel screen + root = xScreenRoot screen + in + xCreateWindow root + (XRect 0 0 (x + 1) (y + 1)) + [XWinBackground bg_color, + XWinEventMask (XEventMask [XExposure, XKeyPress, + XButtonPress, XPointerMotion])] + `thenIO` \window -> + xSetWmName window filename `thenIO` \() -> + xSetWmIconName window filename `thenIO` \() -> + xCreateWindow root (XRect 0 0 100 40) + [XWinBackground bg_color] `thenIO` \window2 -> + xSetWmName window2 "pos" `thenIO` \() -> + xSetWmIconName window2 "pos" `thenIO` \() -> + xMapWindow window `thenIO` \() -> + xMapWindow window2 `thenIO` \() -> + xListFonts display "*times*bold*r*normal*18*" `thenIO` \fontlist -> + xCreateGcontext (XDrawWindow root) [XGCBackground bg_color, + XGCForeground fg_color, + XGCFont (head fontlist)] + `thenIO` \ gcontext -> + let + handleEvent :: IntSegList -> IO() + handleEvent list = + xGetEvent display `thenIO` \event -> + let + point = xEventPos event + XPoint pointx pointy = point + handleEvent' :: XPoint -> IO() + handleEvent' last = xGetEvent display `thenIO` \event2 -> + let + pos = xEventPos event2 + XPoint posx posy = pos + in + case (xEventType event2) of + XExposureEvent -> + redraw window gcontext list `thenIO` \() -> + handleEvent' last + XKeyPressEvent -> + appendChan stdout ((show (tup pos))++ "\n") abort $ + xDrawLine (XDrawWindow window) gcontext point pos + `thenIO` \() -> handleEvent (store list point pos) + XMotionNotifyEvent -> + xDrawImageGlyphs (XDrawWindow window2) gcontext + (XPoint 2 18) ((show posx)++", "++(show posy)++" ") + `thenIO` \dummy -> handleEvent' last + _ -> handleEvent' last + in + case (xEventType event) of + XButtonPressEvent -> + putFile display filename list x y "modify" + XKeyPressEvent -> + appendChan stdout (show (tup point)) abort $ + handleEvent' point + XExposureEvent -> + redraw window gcontext list `thenIO` \() -> + handleEvent list + XMotionNotifyEvent -> + xDrawImageGlyphs (XDrawWindow window2) + gcontext (XPoint 2 18) + ((show pointx)++", "++(show pointy)++" ") + `thenIO` \dummy -> handleEvent list + _ -> + handleEvent list + in + redraw window gcontext origlist `thenIO` \() -> + handleEvent origlist + +-- Miscellaneous functions ------------------------------------------------- +-- shared by the create and modify functions + +checkFile :: Filename -> Bool +checkFile name = + case (take 4 (reverse name)) of + "cip." -> True + _ -> False + +store :: IntSegList -> XPoint -> XPoint -> IntSegList +store l a b = [((xof a,yof a),(xof b,yof b))] ++ l + +xof :: XPoint -> Int +xof (XPoint x y) = x + +yof :: XPoint -> Int +yof (XPoint x y) = y + +tup :: XPoint -> IntPoint +tup (XPoint a b) = (a,b) + +ll:: IntSegment -> Int +ll ((a1,a2),(b1,b2)) = a1 + +lr:: IntSegment -> Int +lr ((a1,a2),(b1,b2)) = a2 + +rl:: IntSegment -> Int +rl ((a1,a2),(b1,b2)) = b1 + +rr:: IntSegment -> Int +rr ((a1,a2),(b1,b2)) = b2 + +getx :: Picture -> Int +getx (Grid m n o) = m + +gety :: Picture -> Int +gety(Grid m n o) = n + +getlist :: Picture -> SegList +getlist (Grid m n o) = o + +fFloat :: SegList -> IntSegList +fFloat = map (\ ((ix,iy),(jx,jy)) -> + ((round ix,round iy), (round jx,round jy))) + +readError :: String +readError = "Error: reading an invalid file\n" + +picTypeError :: String +picTypeError = "Error: files need to be of .pic type\n" + +deleteError :: String +deleteError = "Error: file can not be deleted\n" + +writeError :: String +writeError = "Error: file can not be written\n" + +modError :: String +modError = "Error: file can not be modified\n" + +redraw :: XWindow-> XGcontext -> IntSegList -> IO() +redraw window gcontext [] = done +redraw window gcontext (l:ls) = + xDrawLine (XDrawWindow window) gcontext (XPoint (ll l) (lr l)) + (XPoint (rl l) (rr l)) + `thenIO` \() -> redraw window gcontext ls + +changeList :: IntSegList -> SegList +changeList = + map (\ ((ix,iy),(jx,jy)) -> ((fromIntegral ix,fromIntegral iy), + (fromIntegral jx,fromIntegral jy))) + +putFile :: XDisplay -> Filename -> IntSegList -> + Int -> Int -> String -> IO() +putFile display name list x y flag = + let + text = show (Grid x y (changeList list)) + finishMsg = name ++ ": Done...Process completed\n" + modMsg = name ++ ": Modifying file\n" + createMsg = name ++ ": Creating file\n" + continue = + deleteFile name (\s -> appendChan stdout deleteError abort done) $ + writeFile name text (\s -> appendChan stdout writeError abort done) $ + appendChan stdout finishMsg abort $ + xCloseDisplay display + in + case (flag == "create") of + False -> appendChan stdout modMsg + (\s -> appendChan stdout modError abort done) $ + continue + True -> readFile name (\s -> appendChan stdout createMsg abort $ + writeFile name text abort + (xCloseDisplay display)) $ \s -> + continue + + + + diff --git a/progs/demo/X11/graphics/henderson.hu b/progs/demo/X11/graphics/henderson.hu new file mode 100644 index 0000000..e92b66d --- /dev/null +++ b/progs/demo/X11/graphics/henderson.hu @@ -0,0 +1,3 @@ +:o= foldr inline constant +$HASKELL_LIBRARY/X11/xlib.hu +henderson.hs diff --git a/progs/demo/X11/graphics/manual b/progs/demo/X11/graphics/manual new file mode 100644 index 0000000..17772f1 --- /dev/null +++ b/progs/demo/X11/graphics/manual @@ -0,0 +1,454 @@ +104 pgscriptver + +100 DefSpaceEx 100 DefCharEx 1 DefNormalHyphenationOn 100 +DefTypeColor (Times-Roman) DefTypeFace ENGLISH DefLanguage 12 DefPointSize +USE_POINTSIZE DefSetSize (@default) DefTypeResource + +LEFT DefJustifyFlags 2 DefBeginParaLeadValue ABSOLUTE DefBeginParaLeadMode 2 +DefEndParaLeadValue ABSOLUTE DefEndParaLeadMode 120 DefLeadValue +PROPORTIONAL DefLeadMode 1 46 0 TAB_LEFT 720 DefTab 1 46 0 +TAB_LEFT 2160 DefTab 1 46 0 TAB_LEFT 3600 DefTab 1 46 0 +TAB_LEFT 5040 DefTab 1 46 0 TAB_LEFT 6480 DefTab 1 46 0 +TAB_LEFT 7920 DefTab 1 46 0 TAB_LEFT 9360 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 0 46 0 TAB_LEFT 24480 DefTab 0 46 0 +TAB_LEFT 24480 DefTab 80 DefWSMN 100 DefWSNM 150 DefWSMX 110 +DefLSMX 100 DefLeaderEx 46 DefLeaderChar 0 DefFirstIndent 0 +DefLeftIndent 0 DefRightIndent 0 DefNumberingOn 0 DefNumberingType 0 +DefNumberingRestart 1 DefNumberingLevel 0 DefNumberingStyle 0 +DefNumberingTabAfter 1 DefNumberingShowAllLevels 1 DefNumberingStart 1 +DefNumberingIncrement () DefNumberingPrefix () DefNumberingSuffix (.) +DefNumberingSeparator (*default) DefParaResource + +0 DefLineWidth TRANSPARENT DefPenColor TRANSPARENT DefFillColor 1 DefIG 300 +DefResolution 100 DefYScale 100 DefXScale (=default) DefPolyResource + +0 DefPageDimensions 12240 DefPageWidth 15840 DefPageHeight 1440 +DefInsideMargin 1080 DefOutsideMargin 1080 DefTopMargin 1080 +DefBottomMargin 0 DefOrientation 0 DefPageStyle 1 DefColumns 360 +DefGutter (%default) DefMasterPage ResDefEnd + +0 DefFirstLeft 0 DefDocSetup 1 DefNumPages 1 AutoPage 1 +DefStartPageNum () DefPageNumPrefix 1 DefGraphicLocation document + +1 DefAutoPage +0 (%default) 1 DefPage +1 DefAutoPage +0 (%default) 2 DefPage + +POLY_OBJECT POLY_EMPTY | DefPolyType + +0 DefLineWidth TRANSPARENT DefPenColor TRANSPARENT DefFillColor 1 DefIG 300 +DefResolution 100 DefYScale 100 DefXScale (=default) DefPolyResId 0 +DefMasterRef +MP_CPSUCC_LINK MP_CPPRED_LINK POLY_COLUMN | | DefSLinksFlags 0 DefStreamSucc 0 +DefStreamPred +1440 1080 11160 1080 11160 14760 1440 14760 4 +POLY_OBJECT POLY_EMPTY | (%default) 0 1 TextPolygon + +POLY_OBJECT POLY_TEXT | DefPolyType + +0 DefLineWidth TRANSPARENT DefPenColor TRANSPARENT DefFillColor 1 DefIG 300 +DefResolution 100 DefYScale 100 DefXScale (=default) DefPolyResId 1 +DefMasterRef + +MP_CPSUCC_LINK MP_CPPRED_LINK LINK_OVERFLOW MPREF_VALID POLY_COLUMN AUTO_STREAM | | | | | +DefSLinksFlags 4 DefStreamSucc 0 DefStreamPred 3 DefTextHandle +1440 1080 11160 1080 11160 14760 1440 14760 4 +POLY_OBJECT POLY_TEXT | (1) 0 2 TextPolygon + +3 asciitextstream +<(Courier) cf ><9 cs>The Henderson Library-- +by Syam Gadde +and Bo Whong + +The Henderson Library is a toolkit with which one can use Functional Geometry, +as proposed by Peter Henderson in his paper "Functional Geometry". This is a s +cheme by which "Picture"s can be described in an abstract data type, and a numb +er of functions can be applied to it. This results in a very elegant method to + produce complex pictures from simple ones. The example Henderson uses is "Squ +are Limit" by M. C. Escher, which can be constructed with four simple pictures +. + +------------------------ +ADTs and Type Synonyms + +The Picture data type is composed of eight different types of pictures. They a +re: + +data +Picture = Nil - empty picture + | Flip Picture - picture flipped on the y-axis + | Beside Float Picture Float Picture - two pictures placed side by sid +e + - in accordance to the ratio of t +he + - two floats + | Above Float Picture Float Picture - two pictures placed one on top +of + - another in accordance to the ra +tio + - of the two floats + | Rot Picture - picture is rotated 90 degrees < +eop> + - counterclockwise + | File String - picture is stored as an externa +l + - file + | Overlay Picture Picture - two pictures are drawn such tha +t + - one lays on top of the other + | Grid Int Int SegList - picture type that contains the +list + - of picture's line segments alon +g + - with the size of the inital pic +ture + +The type synonyms are pretty much self explanatory. + + Hostname- a string of the hostname + Filename - a string of the filename + IntPoint - a tuple of integers repres +enting + - the coordinates of a point + + IntSegment - a tuple of Intpoints repre +senting + - the endpoints of a line se +gment + IntSegList - a list of IntSegments + Point - same as IntPoint except in + place of + - intergers, they are floating points + Segment - same as IntSegment except +in place + - of intergers, they are floating + - points + SegList - same as IntsegList except +in place + - of intergers, they are floating + - points + Vector - a tuple of floating points + to + - to represent a vector + Vtriple - a 3-tuple of Vectors + HendQuartet - a 4-tuple of Integers for the s +ize + - of the Henderson window + PEnv - a tuple of a Filename and a Pic +ture + - for storing already opened file +s in + - in order to save time and memor +y + - when a file needs to be opened +more + - than once + +------------------------------------------------------------------------------- +---- +Function: create (an exported function from the HendersonLib) + +The purpose of the create function is to provide the user with a function to +draw a picture from a graphics interface. The user may choose to create a pict +ure +file by inputing the the lines and points manually into a file or (s)he may cho +ose +to use the create function. + +Functionality of create: + create :: Hostname - Filaname - Int - Int - IO() + +create takes as input a hostname, a filename, and two integers for the size of +the +window to be opened. Two windows should appear, one for the input of lines and + +another showing the current position of the mouse. These windows will be label +ed +accordingly. +To draw a line on the file window, move the cursor to the desired position, the +n +hit any key on the keybroad. This point will be the beginning of the line segme +nt. +Next move the cursor to the position of where the user wants the end of the lin +e +segment to be, then hit any key from the keyboard again. A line should appear. + +The coordinates of the endpoints of each line drawn will also be printed out o +nto +standard output. +To signal completion of a file, press any button on the mouse. The user must < +eop> +remember though that this is only applicable after a completed drawing of a lin +e. +For example, pressing the mouse button will not work if one of the endpoints of + a +line is drawn but the other endpoint is not. create will not recognize the mous +e +button press event until a second endpoint is drawn. + +Advantages of create: + provides a quick and fun way to create a picture file. + +Disadvantages of create: + If the file does not exist, create will create the file and then store the pic +ture + to it. However, if the file exists, create will automatically delete the cont +ents + of that file before storing the new picture. + +------------------------------------------------------------------------------- +---- +Function: modify (an exported function from the HendersonLib) + +The purpose of the modify function is to provide the user with a function make + +additions to an already existing picture file using a graphics interface. The +user +may choose to modify the picture file by adding the the lines and points manual +ly +into the file or (s)he may choose to use the modify function. + +Functionality of modify: + modify :: Hostname - Filaname - IO() + +modify takes as input a hostname and a filename. Tow windows should appear. Th +e +size of the draw window will be the same as the x and y coordinates already in +the +file. These windows will be labeled accordingly. The existing picture will app +ear +first before any input is allowed. +To draw a line on the file window, move the cursor to the desired position, the +n +hit any key on the keybroad. This point will be the beginning of the line segme +nt. +Next move the cursor to the position of where the user wants the end of the lin +e +segment to be, then hit any key from the keyboard again. A line should appear. + +The coordinates of the endpoints of each line drawn will also be printed out o +nto +standard output. +To signal completion of a file, press any button on the mouse. The user must < +eop> +remember though that this is only applicable after a completed drawing of a lin +e. +For example, pressing the mouse button will not work if one of the endpoints of + a +line is drawn but the other endpoint is not. modify will not recognize the mou +se +button press event until a second endpoint is drawn. + +Advantages of modify: + provides a quick and fun way to modify a picture file without having to go int +o + the file and manually add on the coordinates of the additional lines + +Disadvantages of modify: + Existing lines can not be deleted and any additional lines, whether intentiona +l or + unintentional, will be appended to the picture and stored in the file. + +-------------------------------------------------------- +Function: sendToDraw + +Type of sendToDraw: + sendToDraw :: XWindow - XScreen - XDisplay - + XPixel - XPixel - Plot - IO() + +Usage: + sendToDraw win scn dis fg_color bg_color plt + +'sendToDraw' is the most primitive function in the part of the Henderson +library that deals with X windows, and therefore, can be used as a very +powerful tool. It draws a Plot plt (see 'plot' function) in the given XWindow< +eop> +win, and on the given XScreen and XDisplay scn and dis, drawing the lines in +the foreground color. This function allows the programmer to draw more than +one Picture to the same window. + +Arguments: + win - the XWindow in which to draw plt + scn - the screen which contains win + dis - the display which contains scn + fg_color - an XPixel the color of which the plt will be drawn in. Note that< +eop> +this allows the programmer to draw different plt's in different colors. + bg_color - unused, but required. +-------------------------------------------------------- +Function: plot + +Type of 'plot': + plot :: Picture - VTriple - PEnv - ((Plot, PEnv) - IO()) - IO() + +Usage: + plot pic (a,b,c) env func + +The 'plot' function is needed to create a Plot which would be normally sent to< +eop> +a function such as sendToDraw. 'plot' converts a Picture pic into a format +that sendToDraw can deal with. +'plot' also takes three vectors which specify the bounding box in which the +Picture is to be drawn. The first vector (a) specifies the upper left corner +of the bounding box. The next two vectors specify the bounding box itself, +with respect to the first vector. This allows for non-rectangular bounding +boxes. For example, the vector triple ((50,50), (100,0), (0,100)) specifies +the following bounding box: + + (0,0)---------------------------------- + | + | (50,50) + | _______________ (150,0) + | | | + | | | + | | | + | | | + | | | + | |_____________| (150,150) + | (0,150) + + +A vector triple of ((0,0), (100,300), (0,100)) would specify: + + (0,0)------------------------------------- + ||\ + || \ + || \ + (0,100)|| \ + |\ \ + | \ \ + | \ \ + | \ \ (100,300) + | \ | + | \ | + | \ | + | \| (100,400) + +Arguments: + pic - the Picture to be converted + a - a vector specifying the upper left corner of the bounding box +of the picture. + b - a vector understood to start at 'a' and specifying the upper edge of + +the bounding box. + c - a vector understood to start at 'a' and specifying the left edge of +the bounding box. +-------------------------------------------------------- +Function: draw + +Type of draw: + draw :: Hostname - Picture - VTriple - HendQuartet - IO() + +Usage: + draw host pic (a,b,c) (m,n,p,q) + +'draw' is a higher-level function than sendToDraw, and is useful to use when +the programmer wishes only to draw one Picture on the screen. This function +does most of the work that the programmer would normally have to do when using< +eop> +sendToDraw. 'draw' opens a window at host with upper left coordinates m and n< +eop> +(on an X server that lets the user position any child window of the root +window, these coordinates mean nothing), and with width p and height q. +'draw' then calls 'plot' on pic and (a,b,c) and sends the result to sendToDraw, + +which finally draws the picture to the window. + +Arguments: + host - host on which to open a display, i.e. "tucan:0" + pic - the Picture to be drawn + (a,b,c) - the vector triple specifying the bounding box to be sent to +plot (see 'plot' function) + (m,n,p,q) - upper left corner x (m), upper left corner y (n), width (p), + +and height (q), of window to be opened. + +----------------------------------------------------------- + +Module: SquareLimit + +This module is a sample user module that can be used to draw Square Limit, a wo +odcut by M. C. Escher. To draw "SquareLimit" on your host, run the dialogue: +final host +where 'host' is the host running X, such as "turtle:0". + +To draw a slightly more interesting picture, tun the dialogue: +skewedfinal host +and it will draw "SquareLimit" in a bounding box shaped as a diamond. + + + + +POLY_OBJECT POLY_TEXT | DefPolyType + +0 DefLineWidth TRANSPARENT DefPenColor TRANSPARENT DefFillColor 1 DefIG 300 +DefResolution 100 DefYScale 100 DefXScale (=default) DefPolyResId 1 +DefMasterRef + +MP_CPSUCC_LINK MP_CPPRED_LINK LINK_OVERFLOW MPREF_VALID POLY_COLUMN AUTO_STREAM | | | | | +DefSLinksFlags 0 DefStreamSucc 2 DefStreamPred 3 DefTextHandle +1440 1080 11160 1080 11160 14760 1440 14760 4 +POLY_OBJECT POLY_TEXT | (2) 0 4 TextPolygon + +BeginProfile +(Number of Pages) (5) DefProfileString +(Language) (ENGLISH) DefProfileString +(Version) (IslandWrite Version 2.3) DefProfileString +(Creation Date) (gadde May 7, 1993 3:55 PM) DefProfileString +(Text Formats) (default) DefProfileString +(Container Formats) (default) DefProfileString +(Page Formats) (default) DefProfileString +(Fonts) (Courier) DefProfileString +(Fonts) (Times-Roman) DefProfileString +(File Path) () DefProfileString +(External Contents) () DefProfileString +(Title) () DefProfileString +(Status) () DefProfileString +(Distribution List) () DefProfileString +(Preparer) () DefProfileString +(Owner) () DefProfileString +(Author) () DefProfileString +(Superseded Documents) () DefProfileString +EndProfile + +pgscriptdone diff --git a/progs/demo/X11/graphics/p.pic b/progs/demo/X11/graphics/p.pic new file mode 100644 index 0000000..240b386 --- /dev/null +++ b/progs/demo/X11/graphics/p.pic @@ -0,0 +1 @@ +Grid 640 640 [((560.00000000000000,560.00000000000000),(440.00000000000000,640.00000000000000)), ((640.00000000000000,560.00000000000000),(560.00000000000000,560.00000000000000)), ((520.00000000000000,440.00000000000000),(640.00000000000000,480.00000000000000)), ((400.00000000000000,480.00000000000000),(520.00000000000000,440.00000000000000)), ((480.00000000000000,360.00000000000000),(360.00000000000000,400.00000000000000)), ((480.00000000000000,360.00000000000000),(640.00000000000000,400.00000000000000)), ((480.00000000000000,280.00000000000000),(640.00000000000000,320.00000000000000)), ((320.00000000000000,320.00000000000000),(480.00000000000000,280.00000000000000)), ((280.00000000000000,400.00000000000000),(160.00000000000000,440.00000000000000)), ((160.00000000000000,240.00000000000000),(280.00000000000000,400.00000000000000)), ((160.00000000000000,440.00000000000000),(160.00000000000000,240.00000000000000)), ((120.00000000000000,480.00000000000000),(0.0000000000000000,320.00000000000000)), ((0.0000000000000000,320.00000000000000),(0.0000000000000000,520.00000000000000)), ((120.00000000000000,480.00000000000000),(0.0000000000000000,520.00000000000000)), ((240.00000000000000,640.00000000000000),(160.00000000000000,480.00000000000000)), ((400.00000000000000,480.00000000000000),(440.00000000000000,640.00000000000000)), ((320.00000000000000,320.00000000000000),(400.00000000000000,480.00000000000000)), ((160.00000000000000,120.00000000000000),(320.00000000000000,320.00000000000000)), ((0.0000000000000000,0.0000000000000000),(160.00000000000000,120.00000000000000)), ((640.00000000000000,240.00000000000000),(320.00000000000000,160.00000000000000)), ((640.00000000000000,40.000000000000000),(560.00000000000000,0.0000000000000000)), ((520.00000000000000,40.000000000000000),(640.00000000000000,80.000000000000000)), ((480.00000000000000,0.0000000000000000),(520.00000000000000,40.000000000000000)), ((480.00000000000000,80.000000000000000),(400.00000000000000,0.0000000000000000)), ((640.00000000000000,120.00000000000000),(480.00000000000000,80.000000000000000)), ((480.00000000000000,160.00000000000000),(640.00000000000000,160.00000000000000)), ((320.00000000000000,0.0000000000000000),(480.00000000000000,160.00000000000000)), ((240.00000000000000,40.000000000000000),(320.00000000000000,0.0000000000000000)), ((0.0000000000000000,0.0000000000000000),(240.00000000000000,40.000000000000000))] \ No newline at end of file diff --git a/progs/demo/X11/graphics/q.pic b/progs/demo/X11/graphics/q.pic new file mode 100644 index 0000000..84e27a4 --- /dev/null +++ b/progs/demo/X11/graphics/q.pic @@ -0,0 +1,2 @@ +Grid 16 16 +[((10.000000000000000,6.0000000000000000),(9.0000000000000000,4.0000000000000000)), ((12.000000000000000,4.0000000000000000),(10.000000000000000,6.0000000000000000)), ((9.0000000000000000,4.0000000000000000),(12.000000000000000,4.0000000000000000)), ((0.0000000000000000,6.0000000000000000),(7.0000000000000000,5.0000000000000000)), ((0.0000000000000000,8.0000000000000000),(0.0000000000000000,16.000000000000000)), ((0.0000000000000000,0.0000000000000000),(0.0000000000000000,4.0000000000000000)), ((15.000000000000000,16.000000000000000),(16.000000000000000,14.000000000000000)), ((16.000000000000000,12.000000000000000),(14.000000000000000,16.000000000000000)), ((13.000000000000000,16.000000000000000),(16.000000000000000,10.000000000000000)), ((13.000000000000000,12.000000000000000),(12.000000000000000,16.000000000000000)), ((16.000000000000000,8.0000000000000000),(13.000000000000000,12.000000000000000)), ((15.000000000000000,6.0000000000000000),(16.000000000000000,8.0000000000000000)), ((16.000000000000000,0.0000000000000000),(15.000000000000000,6.0000000000000000)), ((10.000000000000000,16.000000000000000),(14.000000000000000,5.0000000000000000)), ((10.000000000000000,10.000000000000000),(10.000000000000000,7.0000000000000000)), ((8.0000000000000000,16.000000000000000),(10.000000000000000,10.000000000000000)), ((8.0000000000000000,11.000000000000000),(8.0000000000000000,8.0000000000000000)), ((6.0000000000000000,16.000000000000000),(8.0000000000000000,11.000000000000000)), ((6.0000000000000000,11.000000000000000),(4.0000000000000000,16.000000000000000)), ((6.0000000000000000,9.0000000000000000),(6.0000000000000000,11.000000000000000)), ((4.0000000000000000,11.000000000000000),(4.0000000000000000,9.0000000000000000)), ((2.0000000000000000,16.000000000000000),(4.0000000000000000,11.000000000000000)), ((4.0000000000000000,9.0000000000000000),(0.0000000000000000,8.0000000000000000)), ((6.0000000000000000,9.0000000000000000),(4.0000000000000000,9.0000000000000000)), ((12.000000000000000,6.0000000000000000),(6.0000000000000000,9.0000000000000000)), ((16.000000000000000,0.0000000000000000),(12.000000000000000,6.0000000000000000)), ((9.0000000000000000,3.0000000000000000),(8.0000000000000000,1.0000000000000000)), ((11.000000000000000,1.0000000000000000),(9.0000000000000000,3.0000000000000000)), ((8.0000000000000000,1.0000000000000000),(11.000000000000000,1.0000000000000000)), ((8.0000000000000000,0.0000000000000000),(7.0000000000000000,1.0000000000000000)), ((5.0000000000000000,2.0000000000000000),(7.0000000000000000,1.0000000000000000)), ((6.0000000000000000,0.0000000000000000),(7.0000000000000000,1.0000000000000000)), ((5.0000000000000000,2.0000000000000000),(4.0000000000000000,0.0000000000000000)), ((3.0000000000000000,3.0000000000000000),(5.0000000000000000,2.0000000000000000)), ((3.0000000000000000,3.0000000000000000),(0.0000000000000000,4.0000000000000000)), ((2.0000000000000000,0.0000000000000000),(3.0000000000000000,3.0000000000000000))] \ No newline at end of file diff --git a/progs/demo/X11/graphics/r.pic b/progs/demo/X11/graphics/r.pic new file mode 100644 index 0000000..6e37979 --- /dev/null +++ b/progs/demo/X11/graphics/r.pic @@ -0,0 +1,2 @@ +Grid 32 32 +[((32.000000000000000,0.0000000000000000),(24.000000000000000,8.0000000000000000)), ((32.000000000000000,4.0000000000000000),(30.000000000000000,2.0000000000000000)), ((28.000000000000000,4.0000000000000000),(32.000000000000000,8.0000000000000000)), ((32.000000000000000,12.000000000000000),(26.000000000000000,6.0000000000000000)), ((24.000000000000000,8.0000000000000000),(32.000000000000000,16.000000000000000)), ((22.000000000000000,0.0000000000000000),(24.000000000000000,8.0000000000000000)), ((22.000000000000000,12.000000000000000),(12.000000000000000,0.0000000000000000)), ((32.000000000000000,20.000000000000000),(22.000000000000000,12.000000000000000)), ((24.000000000000000,26.000000000000000),(10.000000000000000,22.000000000000000)), ((32.000000000000000,32.000000000000000),(24.000000000000000,26.000000000000000)), ((16.000000000000000,28.000000000000000),(24.000000000000000,32.000000000000000)), ((6.0000000000000000,26.000000000000000),(16.000000000000000,28.000000000000000)), ((16.000000000000000,32.000000000000000),(4.0000000000000000,28.000000000000000)), ((2.0000000000000000,30.000000000000000),(8.0000000000000000,32.000000000000000)), ((0.0000000000000000,32.000000000000000),(16.000000000000000,16.000000000000000)), ((0.0000000000000000,24.000000000000000),(10.000000000000000,12.000000000000000)), ((4.0000000000000000,8.0000000000000000),(0.0000000000000000,16.000000000000000)), ((28.000000000000000,20.000000000000000),(32.000000000000000,24.000000000000000)), ((16.000000000000000,16.000000000000000),(28.000000000000000,20.000000000000000)), ((4.0000000000000000,8.0000000000000000),(16.000000000000000,16.000000000000000)), ((2.0000000000000000,4.0000000000000000),(4.0000000000000000,8.0000000000000000)), ((2.0000000000000000,4.0000000000000000),(0.0000000000000000,8.0000000000000000)), ((0.0000000000000000,0.0000000000000000),(2.0000000000000000,4.0000000000000000))] \ No newline at end of file diff --git a/progs/demo/X11/graphics/s.pic b/progs/demo/X11/graphics/s.pic new file mode 100644 index 0000000..74659b7 --- /dev/null +++ b/progs/demo/X11/graphics/s.pic @@ -0,0 +1 @@ +Grid 32 32 [((24.000000000000000,0.0000000000000000),(32.000000000000000,0.0000000000000000)), ((0.0000000000000000,0.0000000000000000),(16.000000000000000,0.0000000000000000)), ((30.000000000000000,14.000000000000000),(32.000000000000000,12.000000000000000)), ((32.000000000000000,8.0000000000000000),(28.000000000000000,10.000000000000000)), ((26.000000000000000,6.0000000000000000),(32.000000000000000,4.0000000000000000)), ((26.000000000000000,6.0000000000000000),(24.000000000000000,0.0000000000000000)), ((30.000000000000000,14.000000000000000),(26.000000000000000,6.0000000000000000)), ((32.000000000000000,16.000000000000000),(30.000000000000000,14.000000000000000)), ((30.000000000000000,16.000000000000000),(26.000000000000000,18.000000000000000)), ((30.000000000000000,22.000000000000000),(30.000000000000000,16.000000000000000)), ((26.000000000000000,18.000000000000000),(30.000000000000000,22.000000000000000)), ((24.000000000000000,24.000000000000000),(20.000000000000000,20.000000000000000)), ((24.000000000000000,18.000000000000000),(24.000000000000000,24.000000000000000)), ((20.000000000000000,20.000000000000000),(24.000000000000000,18.000000000000000)), ((20.000000000000000,0.0000000000000000),(22.000000000000000,12.000000000000000)), ((14.000000000000000,6.0000000000000000),(16.000000000000000,0.0000000000000000)), ((14.000000000000000,16.000000000000000),(16.000000000000000,20.000000000000000)), ((14.000000000000000,6.0000000000000000),(14.000000000000000,16.000000000000000)), ((20.000000000000000,24.000000000000000),(16.000000000000000,20.000000000000000)), ((32.000000000000000,32.000000000000000),(20.000000000000000,24.000000000000000)), ((16.000000000000000,28.000000000000000),(32.000000000000000,32.000000000000000)), ((8.0000000000000000,28.000000000000000),(16.000000000000000,28.000000000000000)), ((0.0000000000000000,32.000000000000000),(8.0000000000000000,28.000000000000000)), ((0.0000000000000000,24.000000000000000),(4.0000000000000000,30.000000000000000)), ((0.0000000000000000,20.000000000000000),(14.000000000000000,24.000000000000000)), ((0.0000000000000000,16.000000000000000),(16.000000000000000,20.000000000000000)), ((0.0000000000000000,12.000000000000000),(14.000000000000000,16.000000000000000)), ((0.0000000000000000,8.0000000000000000),(14.000000000000000,12.000000000000000)), ((0.0000000000000000,4.0000000000000000),(14.000000000000000,6.0000000000000000))] \ No newline at end of file diff --git a/progs/demo/X11/graphics/sqrlmt.hs b/progs/demo/X11/graphics/sqrlmt.hs new file mode 100644 index 0000000..662cdfa --- /dev/null +++ b/progs/demo/X11/graphics/sqrlmt.hs @@ -0,0 +1,177 @@ +-- Peter Henderson's Recursive Geometry +-- Syam Gadde and Bo Whong +-- CS429 Project +-- SquareLimit User Program + +module SqrLimit where +import HendersonLib +import Xlib +{- +p = File "p.pic" + +q = File "q.pic" + +r = File "r.pic" + +s = File "s.pic" +-} +p = Grid 640 640 [((560.0,560.0),(440.0,640.0)), + ((640.0,560.0),(560.0,560.0)), + ((520.0,440.0),(640.0,480.0)), + ((400.0,480.0),(520.0,440.0)), + ((480.0,360.0),(360.0,400.0)), + ((480.0,360.0),(640.0,400.0)), + ((480.0,280.0),(640.0,320.0)), + ((320.0,320.0),(480.0,280.0)), + ((280.0,400.0),(160.0,440.0)), + ((160.0,240.0),(280.0,400.0)), + ((160.0,440.0),(160.0,240.0)), + ((120.0,480.0),(0.0,320.0)), + ((0.0,320.0),(0.0,520.0)), + ((120.0,480.0),(0.0,520.0)), + ((240.0,640.0),(160.0,480.0)), + ((400.0,480.0),(440.0,640.0)), + ((320.0,320.0),(400.0,480.0)), + ((160.0,120.0),(320.0,320.0)), + ((0.0,0.0),(160.0,120.0)), + ((640.0,240.0),(320.0,160.0)), + ((640.0,40.0),(560.0,0.0)), + ((520.0,40.0),(640.0,80.0)), + ((480.0,0.0),(520.0,40.0)), + ((480.0,80.0),(400.0,0.0)), + ((640.0,120.0),(480.0,80.0)), + ((480.0,160.0),(640.0,160.0)), + ((320.0,0.0),(480.0,160.0)), + ((240.0,40.0),(320.0,0.0)), + ((0.0,0.0),(240.0,40.0))] + +q = Grid 16 16 [((10.0,6.0),(9.0,4.0)), + ((12.0,4.0),(10.0,6.0)), + ((9.0,4.0),(12.0,4.0)), + ((0.0,6.0),(7.0,5.0)), + ((0.0,8.0),(0.0,16.0)), + ((0.0,0.0),(0.0,4.0)), + ((15.0,16.0),(16.0,14.0)), + ((16.0,12.0),(14.0,16.0)), + ((13.0,16.0),(16.0,10.0)), + ((13.0,12.0),(12.0,16.0)), + ((16.0,8.0),(13.0,12.0)), + ((15.0,6.0),(16.0,8.0)), + ((16.0,0.0),(15.0,6.0)), + ((10.0,16.0),(14.0,5.0)), + ((10.0,10.0),(10.0,7.0)), + ((8.0,16.0),(10.0,10.0)), + ((8.0,11.0),(8.0,8.0)), + ((6.0,16.0),(8.0,11.0)), + ((6.0,11.0),(4.0,16.0)), + ((6.0,9.0),(6.0,11.0)), + ((4.0,11.0),(4.0,9.0)), + ((2.0,16.0),(4.0,11.0)), + ((4.0,9.0),(0.0,8.0)), + ((6.0,9.0),(4.0,9.0)), + ((12.0,6.0),(6.0,9.0)), + ((16.0,0.0),(12.0,6.0)), + ((9.0,3.0),(8.0,1.0)), + ((11.0,1.0),(9.0,3.0)), + ((8.0,1.0),(11.0,1.0)), + ((8.0,0.0),(7.0,1.0)), + ((5.0,2.0),(7.0,1.0)), + ((6.0,0.0),(7.0,1.0)), + ((5.0,2.0),(4.0,0.0)), + ((3.0,3.0),(5.0,2.0)), + ((3.0,3.0),(0.0,4.0)), + ((2.0,0.0),(3.0,3.0))] + +r = Grid 32 32 [((32.0,0.0),(24.0,8.0)), + ((32.0,4.0),(30.0,2.0)), + ((28.0,4.0),(32.0,8.0)), + ((32.0,12.0),(26.0,6.0)), + ((24.0,8.0),(32.0,16.0)), + ((22.0,0.0),(24.0,8.0)), + ((22.0,12.0),(12.0,0.0)), + ((32.0,20.0),(22.0,12.0)), + ((24.0,26.0),(10.0,22.0)), + ((32.0,32.0),(24.0,26.0)), + ((16.0,28.0),(24.0,32.0)), + ((6.0,26.0),(16.0,28.0)), + ((16.0,32.0),(4.0,28.0)), + ((2.0,30.0),(8.0,32.0)), + ((0.0,32.0),(16.0,16.0)), + ((0.0,24.0),(10.0,12.0)), + ((4.0,8.0),(0.0,16.0)), + ((28.0,20.0),(32.0,24.0)), + ((16.0,16.0),(28.0,20.0)), + ((4.0,8.0),(16.0,16.0)), + ((2.0,4.0),(4.0,8.0)), + ((2.0,4.0),(0.0,8.0)), + ((0.0,0.0),(2.0,4.0))] + +s = Grid 32 32 [((24.0,0.0),(32.0,0.0)), + ((0.0,0.0),(16.0,0.0)), + ((30.0,14.0),(32.0,12.0)), + ((32.0,8.0),(28.0,10.0)), + ((26.0,6.0),(32.0,4.0)), + ((26.0,6.0),(24.0,0.0)), + ((30.0,14.0),(26.0,6.0)), + ((32.0,16.0),(30.0,14.0)), + ((30.0,16.0),(26.0,18.0)), + ((30.0,22.0),(30.0,16.0)), + ((26.0,18.0),(30.0,22.0)), + ((24.0,24.0),(20.0,20.0)), + ((24.0,18.0),(24.0,24.0)), + ((20.0,20.0),(24.0,18.0)), + ((20.0,0.0),(22.0,12.0)), + ((14.0,6.0),(16.0,0.0)), + ((14.0,16.0),(16.0,20.0)), + ((14.0,6.0),(14.0,16.0)), + ((20.0,24.0),(16.0,20.0)), + ((32.0,32.0),(20.0,24.0)), + ((16.0,28.0),(32.0,32.0)), + ((8.0,28.0),(16.0,28.0)), + ((0.0,32.0),(8.0,28.0)), + ((0.0,24.0),(4.0,30.0)), + ((0.0,20.0),(14.0,24.0)), + ((0.0,16.0),(16.0,20.0)), + ((0.0,12.0),(14.0,16.0)), + ((0.0,8.0),(14.0,12.0)), + ((0.0,4.0),(14.0,6.0))] + +quartet p1 p2 p3 p4 = + Above 1 (Beside 1 p1 1 p2) 1 (Beside 1 p3 1 p4) + +cyc p1 = + quartet p1 (Rot (Rot (Rot p1))) (Rot p1) (Rot (Rot p1)) + +t = quartet p q r s + +u = cyc (Rot q) + +side1 = quartet Nil Nil (Rot t) t + +side2 = quartet side1 side1 (Rot t) t + +corner1 = quartet Nil Nil Nil u + +corner2 = quartet corner1 side1 (Rot side1) u + +pseudocorner = quartet corner2 side2 (Rot side2) (Rot t) + +pseudolimit = cyc pseudocorner + +nonet p1 p2 p3 p4 p5 p6 p7 p8 p9 = + Above 1 (Beside 1 p1 2 (Beside 1 p2 1 p3)) + 2 (Above 1 (Beside 1 p4 2 (Beside 1 p5 1 p6)) + 1 (Beside 1 p7 2 (Beside 1 p8 1 p9))) + +corner = nonet corner2 side2 side2 + (Rot side2) u (Rot t) + (Rot side2) (Rot t) (Rot q) + +squarelimit = cyc corner + +final host = draw host corner ((0,0),(500,0),(0,500)) (0,0,500,500) +skewedfinal host = draw host squarelimit ((0,0),(600,200),(200,600)) (0,0,800,800) + +main = getEnv "DISPLAY" exit $ \ host -> + xHandleError ( \ (XError msg) -> appendChan stdout msg exit done) $ + final host diff --git a/progs/demo/X11/graphics/sqrlmt.hu b/progs/demo/X11/graphics/sqrlmt.hu new file mode 100644 index 0000000..7d46b0e --- /dev/null +++ b/progs/demo/X11/graphics/sqrlmt.hu @@ -0,0 +1,3 @@ +:o= foldr inline constant +henderson.hu +sqrlmt.hs diff --git a/progs/demo/X11/graphics/stop.pic b/progs/demo/X11/graphics/stop.pic new file mode 100644 index 0000000..01ed7d6 --- /dev/null +++ b/progs/demo/X11/graphics/stop.pic @@ -0,0 +1 @@ +Grid 200 200 [((110.00000000000000,28.000000000000000),(48.000000000000000,39.000000000000000)), ((143.00000000000000,45.000000000000000),(118.00000000000000,32.000000000000000)), ((165.00000000000000,97.000000000000000),(143.00000000000000,45.000000000000000)), ((149.00000000000000,142.00000000000000),(166.00000000000000,98.000000000000000)), ((80.000000000000000,155.00000000000000),(153.00000000000000,146.00000000000000)), ((31.000000000000000,124.00000000000000),(80.000000000000000,156.00000000000000)), ((24.000000000000000,64.000000000000000),(31.000000000000000,124.00000000000000)), ((52.000000000000000,34.000000000000000),(24.000000000000000,64.000000000000000))] \ No newline at end of file diff --git a/progs/demo/X11/graphics/strange.pic b/progs/demo/X11/graphics/strange.pic new file mode 100644 index 0000000..5484b0b --- /dev/null +++ b/progs/demo/X11/graphics/strange.pic @@ -0,0 +1,2 @@ +Overlay (Grid 200 200 [((110.00000000000000,28.000000000000000),(48.000000000000000,39.000000000000000)), ((143.00000000000000,45.000000000000000),(118.00000000000000,32.000000000000000)), ((165.00000000000000,97.000000000000000),(143.00000000000000,45.000000000000000)), ((149.00000000000000,142.00000000000000),(166.00000000000000,98.000000000000000)), ((80.000000000000000,155.00000000000000),(153.00000000000000,146.00000000000000)), ((31.000000000000000,124.00000000000000),(80.000000000000000,156.00000000000000)), ((24.000000000000000,64.000000000000000),(31.000000000000000,124.00000000000000)), ((52.000000000000000,34.000000000000000),(24.000000000000000,64.000000000000000))]) (Flip (File "text.pic")) + diff --git a/progs/demo/X11/graphics/text.pic b/progs/demo/X11/graphics/text.pic new file mode 100644 index 0000000..87fd55c --- /dev/null +++ b/progs/demo/X11/graphics/text.pic @@ -0,0 +1 @@ +Grid 200 200 [((177.00000000000000,91.000000000000000),(177.00000000000000,91.000000000000000)), ((172.00000000000000,63.000000000000000),(175.00000000000000,79.000000000000000)), ((164.00000000000000,73.000000000000000),(148.00000000000000,77.000000000000000)), ((159.00000000000000,63.000000000000000),(164.00000000000000,71.000000000000000)), ((148.00000000000000,63.000000000000000),(159.00000000000000,62.000000000000000)), ((146.00000000000000,61.000000000000000),(149.00000000000000,92.000000000000000)), ((122.00000000000000,61.000000000000000),(115.00000000000000,61.000000000000000)), ((130.00000000000000,62.000000000000000),(122.00000000000000,61.000000000000000)), ((133.00000000000000,75.000000000000000),(130.00000000000000,63.000000000000000)), ((124.00000000000000,89.000000000000000),(131.00000000000000,79.000000000000000)), ((111.00000000000000,81.000000000000000),(124.00000000000000,89.000000000000000)), ((114.00000000000000,61.000000000000000),(108.00000000000000,78.000000000000000)), ((88.000000000000000,64.000000000000000),(91.000000000000000,91.000000000000000)), ((73.000000000000000,62.000000000000000),(96.000000000000000,60.000000000000000)), ((65.000000000000000,97.000000000000000),(49.000000000000000,100.00000000000000)), ((61.000000000000000,80.000000000000000),(65.000000000000000,97.000000000000000)), ((46.000000000000000,79.000000000000000),(61.000000000000000,80.000000000000000)), ((45.000000000000000,61.000000000000000),(46.000000000000000,79.000000000000000)), ((61.000000000000000,63.000000000000000),(41.000000000000000,62.000000000000000))] \ No newline at end of file diff --git a/progs/demo/X11/logo/EXAMPLES.LOGO b/progs/demo/X11/logo/EXAMPLES.LOGO new file mode 100644 index 0000000..bb89632 --- /dev/null +++ b/progs/demo/X11/logo/EXAMPLES.LOGO @@ -0,0 +1,70 @@ +(to nth :index :lst + (if (equal :index 1) + then (first :lst) + else (nth (difference :index 1) (butfirst :lst)))) + +(to makelist :begin :end + (fput :begin (if (equal :begin :end) + then [[]] + else (makelist (sum :begin 1) :end)))) + +(to wheel :centerright + [(hideturtle) + (pendown) + (setangle 90) + (setxy :centerright 350) + (repeat 72 times + [(forward 2) + (left 5)])]) + +(to car + [(pendown) + (hideturtle) + (setxy 400 350) + (setangle 90) + (forward 70) + (left 90) + (forward 100) + (right 60) + (forward 80) + (left 60) + (forward 100) + (left 60) + (forward 80) + (right 60) + (forward 70) + (left 90) + (forward 70) + (left 90) + (forward 350) + (wheel 350) + (wheel 150)]) + +(to docar? + [(local "ans) + (print [do you want a car?]) + (make "ans (read)) + (if (equal (first ans) "yes) + then (car) + else [[oh well]])]) + +(to poly :size :angles + [(hideturtle) + (pendown) + (setangle 90) + (repeat :angles times + [(forward :size) + (right (div 360 :angles))])]) + +(make "x (makelist 3 12)) + +(while (less (first x) 12) + [(make "x (butfirst x)) + (print x)]) + +(clean) + +(car) + +(poly 100 5) + diff --git a/progs/demo/X11/logo/README b/progs/demo/X11/logo/README new file mode 100644 index 0000000..b483e2a --- /dev/null +++ b/progs/demo/X11/logo/README @@ -0,0 +1,104 @@ +Ki-Wing Ho and Eric Fox +Computer Science 429b +Professor Hudak +Final Project: User Manual + + +Control Commands: + + +(DO WHILE ) + + Loop, executing a list of commands, then checking a condition and +looping again if the condition is true. + + +(REPEAT n TIMES) + WHILE cn cl + IF cn THEN cl1 [ELSE cl2] + +Load a file: + USE "filename + +Environment Commands: + MAKE "nm v + LOCAL "nm + TO :nm1 :nm2 :nm3 ... cl + +Text I/O: + PRINT v + READ + +Graphics Commands: + FORWARD n + BACKWARD n + SETXY n1 n2 + LEFT n + RIGHT n + PENUP + PENDOWN + HIDETURTLE + SHOWTURTLE + CLEARSCREEN + CLEAN + +Graphics Functions: + XCOR + YCOR + GETANGLE + GETPEN + GETTURTLE + +Mathematical: + SUM n1 n2 + DIFFERENCE n1 n2 + PRODUCT n1 n2 + MOD n1 n2 + DIV n1 n2 + POWER n1 n2 + +Boolean: + AND b1 b2 + OR b1 b2 + NOT b + +Predicates: + WORDP v + LISTP v + NUMBERP v + GREATER n1 n2 + LESS n1 n2 + EQUAL v1 v2 + +Word/List: + FIRST t + LAST t + FPUT t l + BUTFIRST l + WORD w1 w2 w3 ... + LIST t1 t2 t3 ... + CONCAT l1 l2 + SENTENCE t1 t2 t3 ... + + +Our Logo interpreter will only support one of the three windowing +modes: window mode, where the turtle, if it walks off the end of the +screen, just continues going and does not wrap. The two (unsupported) +modes are fence mode where the turtle cannot walk off the end, and +wrap mode. The initial turtle state will be with the turtle hidden, +the pen down, and the turtle in the center of the screen facing +upwards. + +All input (both for commands as well as user-input) will be +case-insensitive, and the interpreter needs to handle lists, words, +integers, and boolean values. Also, typing "GoodBye" at the LOGO> +prompt exits the interpreter. + +All commands will be enclosed in parentheses, and all lists of +commands will be enclosed in square brackets, so that there is no +longer any need for the keyword "End". Also, all procedures will +return the value of their last command, so that there are no Stop or +Output commands. IF statements should return the value of the last +statement executed, but all looping constructs should return no value. + + diff --git a/progs/demo/X11/logo/logo.hs b/progs/demo/X11/logo/logo.hs new file mode 100644 index 0000000..458eca1 --- /dev/null +++ b/progs/demo/X11/logo/logo.hs @@ -0,0 +1,1345 @@ +{- + +Ki-Wing Ho and Eric Fox +Computer Science 429b +Professor Hudak +Final Project: LOGO Interpreter + +-} + + + +------------------------------------------------------------------------------- +module REPLoop where + +{- + +REPLoop has two main parts: the first part (function logo) sets up the +graphics window, prints a welcome message, initializes the variable +and procedure environments and the turtle, accepts and lines's the +user input, runs the read-eval-print loop (part two), and then closes +the graphics window and exists; the second part (function repLoop) +lexes and parses each command, prints an error message if there was a +syntax error and evaluates (or tries to) if there wasn't, and then +either prints the value or an error message or exits if the value +returnd by the evaluator is "GoodBye". + +-} + +import Lexer +import Parser +import Evaluator +import Xlib + +demo = main + +main = getEnv "DISPLAY" exit $ \ host -> + xHandleError ( \ (XError msg) -> appendChan stdout msg exit done) $ + logo host + +logo :: String -> IO () + +logo host = + xOpenDisplay host `thenIO` \ display -> + + let (screen:_) = xDisplayRoots display + fg_color = xScreenWhitePixel screen + bg_color = xScreenBlackPixel screen + root = xScreenRoot screen + in + xCreateWindow root + (XRect 100 100 500 500) + [XWinBackground bg_color, + XWinBackingStore XAlwaysBackStore] + `thenIO` \ graphWindow -> + xSetWmName graphWindow "Logo" `thenIO` \ () -> + xSetWmIconName graphWindow "Logo" `thenIO` \ () -> + xMapWindow graphWindow `thenIO` \ () -> + + xCreateGcontext (XDrawWindow root) + [XGCBackground bg_color, + XGCForeground fg_color] `thenIO` \ graphContext -> + + xDisplayForceOutput display `thenIO` \ () -> + + appendChan stdout ("Welcome to LOGO!\n" ++ prompt) exit $ + readChan stdin exit $ \userInput -> + repLoop + (varEnvsInit,procEnvsInit,turtleInit) + ((lines userInput,Lexer), + (graphWindow,display,graphContext,bg_color,fg_color)) $ + xCloseDisplay display + +-- Initial Environments -- + +varEnvsInit :: VarsType +varEnvsInit = [[("GOODBYE",GoodBye)]] + +-- all user-defined commands must have dummy entries +procEnvsInit :: ProcsType +procEnvsInit = (map (makeFakeProc) + [("XCOR",0),("YCOR",0),("GETANGLE",0),("GETPEN",0), + ("GETTURTLE",0), + ("SUM",2),("DIFFERENCE",2),("PRODUCT",2),("MOD",2), + ("DIV",2),("POWER",2), + ("AND",2),("OR",2),("NOT",1), + ("WORDP",1),("LISTP",1),("NUMBERP",1),("GREATER",2), + ("EQUAL",2),("LESS",2), + ("BUTFIRST",1),("FPUT",2),("CONCAT",2), + ("FIRST",1),("LAST",1),("WORD",-2),("LIST",-2), + ("SENTENCE",-2), ("USE",1)]):[] + +turtleInit :: TurtleType +turtleInit = (500 `div` 2,500 `div` 2,90,True,False) + +-- makes a dummy procedure +makeFakeProc :: (NameType , Int) -> (NameType , ProcType) +makeFakeProc (name,num) = (name,(makeArgs num,[])) + +makeArgs :: Int -> [NameType] +makeArgs n | n > 0 = "" : makeArgs (n-1) + | otherwise = [] + +-- keep running Read-Eval-Print Loop until user types GoodBye +-- repLoop keeps running until user types "GoodBye", alternately +-- lexing, parsing, and evaluating each command +-- after a syntax error, the lex state is reset +repLoop :: EnvsType -> StateType -> IO () -> IO () +repLoop e1 (inS1,gs1) end = + let fail1 msg (is1,ls1) = errorOutput msg $ + repLoop e1 ((is1,Lexer),gs1) end + -- parser fail continuation doesn't contain graphics state + fail2 msg ((is2,ls2),gs2) = errorOutput msg $ + repLoop e1 ((is2,Lexer),gs1) end + -- evaluator fail continuation does contain graphics state + in + parse [] inS1 fail1 $ \a ts inS2 -> + if (null ts) + then + evaluate e1 a (inS2,gs1) fail2 $ \v e2 ((is3,ls3),gs3) -> + output v end $ + repLoop e2 ((is3,Lexer),gs3) end + else + fail1 "Syntax error: expected end of line" inS2 + -- repLoop will still be rerun + +-- print error message +errorOutput :: String -> IO () -> IO () +errorOutput error = appendChan stdout (error ++ prompt) abort + +-- print expression value, exiting if GoodBye +output :: Value -> IO () -> IO () -> IO () +output GoodBye end succ + = appendChan stdout "\nGoodbye!\n"abort end +output v end succ + = appendChan stdout ((valueToString v) ++ prompt) abort succ + +prompt :: String +prompt = "\nLOGO> " + + + +------------------------------------------------------------------------------- +module Evaluator where + +{- + +Evaluator takes an Abstract Syntax Tree and evaluates it in the +current environment, returning both the resultant value and the new +environment (as well as the updated state, of which only the user +input can actually be changed in the evaluator). + +A value can be of one of six types: integer, string, list, and +boolean, as well as null (for commands which don't return anything and +newly-declared local variables), and goodbye, which allows logo to +quit. + +The environment consists of three parts. The variable environment and +the procedure environment are separate (so that a name can refer both +to a variable and a procedure: Logo syntax is such that there is +never any ambiguity) are both lists of name-value association lists. +Each association list representes a "local environment", with each +successive one being more "global", so that the last environment in +the list is the global environment. Local environments are produced +by user-function invocations and removed at the end of those +invocations. + +-} + +import Lexer +import Parser +import Xlib + +type NameType = [Char] +type WordType = [Char] +type Error = [Char] + +type StateType = (InputState , GraphicsState) +type GraphicsState = (XWindow , XDisplay , XGcontext , XPixel , XPixel) +type EnvsType = (VarsType,ProcsType,TurtleType) +type VarsType = [[(NameType , Value)]] +type ProcsType = [[(NameType , ProcType)]] +type TurtleType = (Int , Int , Int , Bool , Bool) +type ProcType = ([NameType] , ClauseType) + +data Value = Null + | Num Int + | Word WordType + | List ListType + | Boolean Bool + | GoodBye + deriving Text + +data ListType = NullList | Value :* ListType + deriving Text + + +type EvalFailType = Error -> StateType -> IO () +type EvalSuccType = Value -> EnvsType -> StateType -> IO () +type EvalResType = StateType -> EvalFailType -> EvalSuccType -> IO () +type EvaluateType = EnvsType -> AST -> EvalResType + + +evaluate :: EvaluateType + +evaluate (vs,p:ps,ttl) (To newName newProc) ss fail succ + = succ Null (vs,((newName,newProc):p):ps,ttl) ss + -- procedures + +evaluate e (Read) ((i:is,ls),gs) fail succ + = succ (List (makeReadList (lexerReadLine i))) e ((is,ls),gs) + -- user input + +evaluate e1 (Print [a]) ss fail succ + = evaluate e1 a ss fail $ \v e2 ss2 -> + appendChan stdout ((valueToString v)++"\n") abort $ + succ Null e2 ss2 + -- user output + +evaluate e (Argument (Val (Word n))) ss fail succ + = lookup e n ss fail $ \v -> + succ v e ss + -- variable reference + +evaluate e (Argument (Val v)) ss fail succ + = succ v e ss + -- constant + +evaluate e (Argument (QuotedWordArg n)) ss fail succ + = succ (Word n) e ss + -- string constant + +evaluate (v:vs,ps,ttl) (Local n) ss fail succ + = succ Null (((n,Null):v):vs,ps,ttl) ss + -- local variable declaraion + -- local returns null, and sets the new local variable to null also + +evaluate e (ParseList l) ss fail succ + = succ (List l) e ss + -- lists (also constant) + +evaluate e (Loop l cond insts) ss fail succ + = evalLoop l e cond insts ss fail succ + -- loops + +evaluate e (If cond thens elses) ss fail succ + = evalIf e cond thens elses ss fail succ + -- if-then[-eles] conditionals + +evaluate e1 (Command name as1) ss fail succ + | ((na == length as1) || (na == -2)) + = evalArgs e1 as1 ss fail $ \e2 as2 ss2 -> + apply name as2 e2 ss2 fail $ \v e3 ss3 -> + succ v e3 ss3 + | na == -1 + = fail ("Function does not exist: " ++ name) ss + | otherwise + = fail ("Wrong number of arguments to " ++ name) ss + where na = numArgs e1 name + -- function applications + +evaluate e1 (Make n a) ss fail succ + = evaluate e1 a ss fail $ \v e2 ss2 -> + update e2 n v $ \e3 -> + succ v e3 ss2 + -- assignment statements, which return the assigned value + +evaluate e1 (Graphics name as1) ss fail succ + = evalArgs e1 as1 ss fail $ \e2 as2 ss2 -> + doGraphics name as2 e2 ss2 fail $ \e3 ss3 -> + succ Null e3 ss3 + -- side-effecting graphics statements, which all return null +-- end evaluate + + +-- evaluate a list of actual parameters, returning the corresponding +-- list of values +evalArgs :: EnvsType -> ParseArgs -> StateType -> EvalFailType -> + (EnvsType -> EvalArgs -> StateType -> IO ()) -> IO () +evalArgs e [] ss fail succ + = succ e [] ss +evalArgs e1 (a:as1) ss fail succ + = evaluate e1 a ss fail $ \v e2 ss2 -> + evalArgs e2 as1 ss2 fail $ \e3 as2 ss3 -> + succ e3 (v:as2) ss3 + + +-- evaluate a list of commands, returning the value of the last one +evalClause :: EnvsType -> ClauseType -> EvalResType +evalClause e [] ss fail succ + = succ Null e ss +evalClause e (a:[]) ss fail succ + = evaluate e a ss fail succ +evalClause e1 (a:as) ss fail succ + = evaluate e1 a ss fail $ \v e2 ss2 -> + evalClause e2 as ss2 fail succ + +-- convert a lexed user-input list to a list constant +makeReadList :: [WordType] -> ListType +makeReadList [] = NullList +makeReadList (w:ws) = (Word w) :* (makeReadList ws) + + +-- Variable routines -- + +-- look up a variable reference in the variable environment +-- search the most-local environments first +-- return an error if not found +lookup :: EnvsType -> NameType -> StateType -> EvalFailType -> + (Value -> IO ()) -> IO () +lookup ([],ps,ttl) name ss fail succ + = fail ("Unbound variable: " ++ name) ss +lookup ([]:vss,ps,ttl) name ss fail succ + = lookup (vss,ps,ttl) name ss fail succ +lookup (((n,v):vs):vss,ps,ttl) name ss fail succ + | n == name = succ v + | otherwise = lookup (vs:vss,ps,ttl) name ss fail succ + +-- update the variable environment +-- replace the most-local occurrance first; if none are found, +-- create a new variable and place it in the most-global environment +update :: EnvsType -> NameType -> Value -> (EnvsType -> IO ()) -> IO () +update ([]:[],ps,ttl) name value succ + = succ (((name,value):[]):[],ps,ttl) +update ([]:vss,ps,ttl) name value succ + = update (vss,ps,ttl) name value $ \(vss2,ps2,ttl2) -> + succ ([]:vss2,ps2,ttl2) +update (((n,v):vs):vss,ps,ttl) name value succ + | n == name = succ (((n,value):vs):vss,ps,ttl) + | otherwise = update (vs:vss,ps,ttl) name value $ \(vs2:vss2,ps2,ttl2) -> + succ (((n,v):vs2):vss2,ps2,ttl2) + + +-- Control structures -- + +-- evaluate loops +evalLoop :: LoopType -> EnvsType -> ConditionType -> ClauseType -> + EvalResType +evalLoop Do = evalDo +evalLoop While = evalWhile +evalLoop Repeat = evalRepeat + +-- evaluate while statements +-- loop semantics: evaluate condition; if true, evaluate clause, then loop +-- while returns null +evalWhile :: EnvsType -> ConditionType -> ClauseType -> EvalResType +evalWhile e1 cond insts ss fail succ + = evalCond e1 cond ss fail $ \b e2 ss2 -> + if b + then + evalClause e2 insts ss2 fail $ \v e3 ss3 -> + evalWhile e3 cond insts ss3 fail succ + else + succ Null e2 ss2 + +-- evaluate do-while statements +-- loop semantics: evaluate clause then evaluate condition; if true, loop +evalDo :: EnvsType -> ConditionType -> ClauseType -> EvalResType +evalDo e1 cond insts ss fail succ + = evalClause e1 insts ss fail $ \v e2 ss2 -> + evalCond e2 cond ss2 fail $ \b e3 ss3 -> + if b + then + evalDo e3 cond insts ss3 fail succ + else + succ Null e3 ss3 + +-- evaluate repeat statements +-- loop semantics: evaluate loop number as n; evaluate clause n times +-- evaluate loop number and print error if it is negative or not an integer +evalRepeat :: EnvsType -> ConditionType -> ClauseType -> EvalResType +evalRepeat e1 cond insts ss fail succ + = evaluate e1 cond ss fail $ \v e2 ss2 -> + case v of + Num n -> if (n >= 0) + then doIterations e2 n insts ss2 fail succ + else fail "Repeat: Iteration count cannot be negative" ss2 + otherwise -> fail "Repeat: Invalid iteration count" ss2 + +-- perform loop interations: evaluate "insts" "n" times +doIterations :: EnvsType -> Int -> ClauseType -> EvalResType +doIterations e 0 insts ss fail succ + = succ Null e ss +doIterations e1 (n+1) insts ss fail succ + = evalClause e1 insts ss fail $ \v e2 ss2 -> + doIterations e2 n insts ss2 fail succ + +-- evaluates conditions and returns either true, false, or an error +evalCond :: EnvsType -> ConditionType -> StateType -> EvalFailType -> + (Bool -> EnvsType -> StateType -> IO ()) -> IO () +evalCond e1 cond ss fail succ + = evaluate e1 cond ss fail $ \v e2 ss2 -> + case v of + Boolean b -> succ b e2 ss2 + otherwise -> fail "Invalid condition" ss2 + +-- evaluate if-then[-else] statements +evalIf :: EnvsType -> ConditionType -> ClauseType -> ClauseType -> EvalResType +evalIf e1 cond thens elses ss fail succ + = evalCond e1 cond ss fail $ \b e2 ss2 -> + if b + then evalClause e2 thens ss2 fail succ + else evalClause e2 elses ss2 fail succ + + +-- Function application -- + +-- returns the number of arguments to a user-defined or built-in function +-- -1 means the function wasn't found +-- -2 means the function can take any number of arguments +numArgs :: EnvsType -> CommandName -> Int +numArgs (vs,[],ttl) name + = -1 +numArgs (vs,[]:pss,ttl) name + = numArgs (vs,pss,ttl) name +numArgs (vs,((n,(formals,body)):ps):pss,ttl) name + | inList ["WORD","SENTENCE","LIST"] name = -2 + | n == name = length formals + | otherwise = numArgs (vs,ps:pss,ttl) name + +-- apply a function to its arguments +-- mostly just decides if it's user-defined or built-in, then dispatches +apply :: CommandName -> EvalArgs -> EnvsType -> EvalResType +apply n as e ss fail succ + | isBuiltIn n = applyPrimProc n as e ss fail succ + | otherwise = applyUserProc (getProc e n) as e ss fail succ + + + +-- returns procedure "name" from the procedure environment +-- searches most-local environments first +-- precondition: procedure does exist somewhere +getProc :: EnvsType -> CommandName -> ProcType +getProc (vss,[]:pss,ttl) name + = getProc (vss,pss,ttl) name +getProc (vs,((n,p):ps):pss,ttl) name + | n == name = p + | otherwise = getProc (vs,ps:pss,ttl) name + +-- apply user function: +-- bind formal parameters +-- create local enviroments +-- evaluate body of function +-- destroy local environments +-- return value of body +applyUserProc :: ProcType -> EvalArgs -> EnvsType -> EvalResType +applyUserProc (formals,body) actuals e1 ss fail succ + = bind formals actuals e1 $ \e2 -> + evalClause e2 body ss fail $ \v (vs:vss,ps:pss,ts) ss2 -> + succ v (vss,pss,ts) ss2 + +-- bind formal parameters to actuals in local environment +bind :: [NameType] -> EvalArgs -> EnvsType -> (EnvsType -> IO ()) -> IO () +bind formals actuals (vss,pss,ttl) succ + = succ ((zip formals actuals):vss,[]:pss,ttl) + + +-- Built-in functions -- + +-- returns true for built-in functions +isBuiltIn :: CommandName -> Bool +isBuiltIn = inList ["XCOR","YCOR","GETANGLE","GETPEN","GETTURTLE", + "SUM","DIFFERENCE","PRODUCT","MOD","DIV","POWER", + "AND","OR","NOT", + "WORDP","LISTP","NUMBERP","GREATER","EQUAL","LESS", + "BUTFIRST","FPUT","CONCAT", + "FIRST","LAST","WORD","LIST","SENTENCE", "USE"] + + +-- applies a built-in function to its arguments +applyPrimProc :: CommandName -> [Value] -> EnvsType -> EvalResType + +applyPrimProc "XCOR" [] (vs,ps,(x,y,a,p,t)) ss fail succ + = succ (Num x) (vs,ps,(x,y,a,p,t)) ss +applyPrimProc "YCOR" [] (vs,ps,(x,y,a,p,t)) ss fail succ + = succ (Num y) (vs,ps,(x,y,a,p,t)) ss +applyPrimProc "GETANGLE" [] (vs,ps,(x,y,a,p,t)) ss fail succ + = succ (Num a) (vs,ps,(x,y,a,p,t)) ss +applyPrimProc "GETPEN" [] (vs,ps,(x,y,a,p,t)) ss fail succ + = succ (Boolean p) (vs,ps,(x,y,a,p,t)) ss +applyPrimProc "GETTURTLE" [] (vs,ps,(x,y,a,p,t)) ss fail succ + = succ (Boolean t) (vs,ps,(x,y,a,p,t)) ss + +applyPrimProc "SUM" [Num a , Num b] e ss fail succ + = succ (Num (a+b)) e ss +applyPrimProc "DIFFERENCE" [Num a , Num b] e ss fail succ + = succ (Num (a-b)) e ss +applyPrimProc "PRODUCT" [Num a , Num b] e ss fail succ + = succ (Num (a*b)) e ss +applyPrimProc "MOD" [Num a , Num b] e ss fail succ + = succ (Num (a `mod` b)) e ss +applyPrimProc "DIV" [Num a , Num b] e ss fail succ + = succ (Num (a `div` b)) e ss +applyPrimProc "POWER" [Num a , Num b] e ss fail succ + | b >= 0 = succ (Num (a^b)) e ss + | otherwise = fail ("Negative exponent: " ++ (show b)) ss + +applyPrimProc "AND" [Boolean a , Boolean b] e ss fail succ + = succ (Boolean (a && b)) e ss +applyPrimProc "OR" [Boolean a , Boolean b] e ss fail succ + = succ (Boolean (a || b)) e ss +applyPrimProc "NOT" [Boolean a] e ss fail succ + = succ (Boolean (not a)) e ss + +applyPrimProc "WORDP" [Word w] e ss fail succ + = succ (Boolean True) e ss +applyPrimProc "WORDP" [v] e ss fail succ + = succ (Boolean False) e ss +applyPrimProc "NUMBERP" [Num n] e ss fail succ + = succ (Boolean True) e ss +applyPrimProc "NUMBERP" [v] e ss fail succ + = succ (Boolean False) e ss +applyPrimProc "LISTP" [List l] e ss fail succ + = succ (Boolean True) e ss +applyPrimProc "LISTP" [v] e ss fail succ + = succ (Boolean False) e ss +applyPrimProc "GREATER" [Num a , Num b] e ss fail succ + = succ (Boolean (a > b)) e ss +applyPrimProc "EQUAL" [Num a , Num b] e ss fail succ + = succ (Boolean (a == b)) e ss +applyPrimProc "EQUAL" [Word a , Word b] e ss fail succ + = succ (Boolean (a == b)) e ss +applyPrimProc "EQUAL" [Boolean a , Boolean b] e ss fail succ + = succ (Boolean (a == b)) e ss +applyPrimProc "LESS" [Num a , Num b] e ss fail succ + = succ (Boolean (a < b)) e ss + +applyPrimProc "BUTFIRST" [Word ""] e ss fail succ + = succ (Word "") e ss +applyPrimProc "BUTFIRST" [Word (c:cs)] e ss fail succ + = succ (Word cs) e ss +applyPrimProc "BUTFIRST" [List NullList] e ss fail succ + = succ (List NullList) e ss +applyPrimProc "BUTFIRST" [List (v :* vs)] e ss fail succ + = succ (List vs) e ss +applyPrimProc "FPUT" [v , List l] e ss fail succ + = succ (List (v :* l)) e ss +applyPrimProc "CONCAT" [List l1 , List l2] e ss fail succ + = succ (List (listConcatenate l1 l2)) e ss +applyPrimProc "FIRST" [Word (c:cs)] e ss fail succ + = succ (Word (c:[])) e ss +applyPrimProc "FIRST" [List (v :* vs)] e ss fail succ + = succ v e ss +applyPrimProc "LAST" [Word (c:[])] e ss fail succ + = succ (Word (c:[])) e ss +applyPrimProc "LAST" [Word ""] e ss fail succ + = succ Null e ss +applyPrimProc "LAST" [Word (c:cs)] e ss fail succ + = applyPrimProc "LAST" [(Word cs)] e ss fail succ +applyPrimProc "LAST" [List (v :* NullList)] e ss fail succ + = succ v e ss +applyPrimProc "LAST" [List (v :* vs)] e ss fail succ + = applyPrimProc "LAST" [(List vs)] e ss fail succ +applyPrimProc "WORD" [] e ss fail succ + = succ (Word "") e ss +applyPrimProc "WORD" ((Word w):ws) e ss fail succ + = applyPrimProc "WORD" ws e ss fail $ \(Word wsc) e2 ss2 -> + succ (Word (w ++ wsc)) e2 ss2 +applyPrimProc "LIST" (v:vs) e ss fail succ + = applyPrimProc "LIST" vs e ss fail $ \(List l) e2 ss2 -> + succ (List (v :* l)) e2 ss2 +applyPrimProc "LIST" [] e ss fail succ + = succ (List NullList) e ss +applyPrimProc "SENTENCE" [] e ss fail succ + = succ (List NullList) e ss +applyPrimProc "SENTENCE" ((List l):[]) e ss fail succ + = succ (List l) e ss +applyPrimProc "SENTENCE" ((List l):vs) e ss fail succ + = applyPrimProc "SENTENCE" [List l] e ss fail $ \(List s1) e2 ss2 -> + applyPrimProc "SENTENCE" vs e2 ss2 fail $ \(List s2) e3 ss3 -> + succ (List (listConcatenate s1 s2)) e3 ss3 +applyPrimProc "SENTENCE" (v:vs) e ss fail succ + = applyPrimProc "SENTENCE" vs e ss fail $ \(List ws) e2 ss2 -> + succ (List (v :* ws)) e2 ss2 + +applyPrimProc "USE" [Word filename] + e + ss@((ins, ls), gs) + fail succ + = readFile filename (\ _ -> fail ("Can't read file: " ++ filename) ss) + $ \filecontents -> + useRepLoop e ((lines filecontents, Lexer), gs) + (\ msg s -> fail msg ss) $ \ v e s -> + succ v e ss + +applyPrimProc n _ _ ss fail _ + = fail ("Incorrect arguments: " ++ n) ss + +useRepLoop :: EnvsType -> EvalResType +useRepLoop e s@(([], ls), gs) fail succ = succ (Word "OK") e s +useRepLoop e1 s1@(inS1,gs1) fail succ = + parse [] inS1 (\ msg ins -> fail msg (ins, gs1)) $ \a ts inS2 -> + if (null ts) + then + evaluate e1 a (inS2,gs1) fail $ \v e2 s3 -> + useRepLoop e2 s3 fail succ + else + fail "Syntax error: expected end of line" (inS2, gs1) + + + +-- concatenates two lists +listConcatenate :: ListType -> ListType -> ListType +listConcatenate NullList l2 = l2 +listConcatenate (v :* l1) l2 = (v :* (listConcatenate l1 l2)) + + +-- Graphics -- + +type EvalArgs = [Value] +type GraphEnv = (Int,Int,Int,Bool) + +-- evaluates side-effecting graphics functions +-- note: none of them return values +doGraphics :: CommandName -> EvalArgs -> EnvsType -> StateType -> + EvalFailType -> (EnvsType -> StateType -> IO ()) -> IO () + +doGraphics "HIDETURTLE" [] (vs,ps,(x,y,a,p,t)) ss fail succ + = hideTurtle x y a ss $ + succ (vs,ps,(x,y,a,p,False)) ss + -- hide turtle, appropriately adjust environment + +doGraphics "SHOWTURTLE" [] (vs,ps,(x,y,a,p,t)) ss fail succ + = showTurtle x y a ss $ + succ (vs,ps,(x,y,a,p,True)) ss + -- show turtle, appropriately adjust environment + +doGraphics name as (vs,ps,(x,y,a,p,True)) ss fail succ + = hideTurtle x y a ss $ + moveTurtle name as (x,y,a,p) ss $ \(x2,y2,a2,p2) -> + showTurtle x2 y2 a2 ss $ + succ (vs,ps,(x2,y2,a2,p2,True)) ss + -- executes graphics commands if turtle is shownn + +doGraphics name as (vs,ps,(x,y,a,p,False)) ss fail succ + = moveTurtle name as (x,y,a,p) ss $ \(x2,y2,a2,p2) -> + succ (vs,ps,(x2,y2,a2,p2,False)) ss + -- executes graphics commands if turtle is not shown + +-- converts an integer to a float +toFloat :: Int -> Float +toFloat = fromInteger . toInteger + +newmod a b = let c = a `mod` b + in if (c < 0) then (c + b) else c + +-- shows the turtle, but returns nothing +showTurtle :: Int -> Int -> Int -> StateType -> IO () -> IO () +showTurtle x y a (is,(graphWindow,display,graphContext,bg,fg)) succ + = let dx1 = round (12 * cos (toFloat a * pi/180)) + dx2 = round (4 * sin (toFloat a * pi/180)) + dy1 = round (12 * sin (toFloat a * pi/180)) + dy2 = round (4 * cos (toFloat a * pi/180)) + in + xDrawLine (XDrawWindow graphWindow) + graphContext + (XPoint x y) + (XPoint (x-dx1-dx2) (y+dy1-dy2)) + `thenIO` \ () -> + xDrawLine (XDrawWindow graphWindow) + graphContext + (XPoint x y) + (XPoint (x-dx1+dx2) (y+dy1+dy2)) + `thenIO` \ () -> + xDrawLine (XDrawWindow graphWindow) + graphContext + (XPoint (x-dx1-dx2) (y+dy1-dy2)) + (XPoint (x-dx1+dx2) (y+dy1+dy2)) + `thenIO` \ () -> + xDisplayForceOutput display + `thenIO_` + succ + +-- hides the turtle, but returns nothing +hideTurtle :: Int -> Int -> Int -> StateType -> IO () -> IO () +hideTurtle x y a (is,(graphWindow,display,graphContext,bg,fg)) succ + = xUpdateGcontext graphContext [XGCForeground bg] + `thenIO_` + (showTurtle x y a (is,(graphWindow,display,graphContext,bg,fg)) $ + (xUpdateGcontext graphContext [XGCForeground fg] + `thenIO_` + succ)) + +-- performs all graphics commands that don't involve hiding/showing +-- the turtle +moveTurtle :: CommandName -> EvalArgs -> GraphEnv -> StateType -> + (GraphEnv -> IO ()) -> IO () +moveTurtle "SETXY" [Num xp,Num yp] (x,y,a,p) ss succ + = succ (xp,yp,a,p) + +-- move the turtle forward "d" times, drawing a line if pen is down +moveTurtle "FORWARD" [Num d] (x,y,a,p) + (is,(graphWindow,display,graphContext,fg,bg)) succ + = let xp = x + round (toFloat d * cos (toFloat a * pi/180)) + yp = y - round (toFloat d * sin (toFloat a * pi/180)) in + (if p + then (xDrawLine (XDrawWindow graphWindow) + graphContext + (XPoint x y) + (XPoint xp yp)) + else returnIO ()) `thenIO` \ () -> + xDisplayForceOutput display `thenIO` \ () -> + succ (xp,yp,a,p) + +-- move the turtle backward "d" pixels, drawing a line if pen is down +moveTurtle "BACKWARD" [Num d] (x,y,a,p) ss succ + = moveTurtle "FORWARD" [Num (-d)] (x,y,a,p) ss succ + +-- rotate turtle to "ap" degrees from facing due east +moveTurtle "SETANGLE" [Num ap] (x,y,a,p) ss succ + = succ (x,y,ap,p) + +-- rotate turtle counterclockwise "ap" degrees +moveTurtle "LEFT" [Num ap] (x,y,a,p) ss succ + = succ (x,y, (a + ap) `newmod` 360 ,p) + +-- rotate turtle clockwise "ap" degrees +moveTurtle "RIGHT" [Num ap] (x,y,a,p) ss succ + = succ (x,y, (a - ap) `newmod` 360 ,p) + +-- pick pen up +moveTurtle "PENUP" [] (x,y,a,p) ss succ + = succ (x,y,a,False) + +-- put pen down +moveTurtle "PENDOWN" [] (x,y,a,p) ss succ + = succ (x,y,a,True) + +-- clear screen but don't otherwise alter turtle state +moveTurtle "CLEARSCREEN" [] (x,y,a,p) + (is,(graphWindow,display,graphContext,bg,fg)) succ + = xClearArea graphWindow (XRect 0 0 500 500) True + `thenIO` \() -> + xDisplayForceOutput display + `thenIO` \() -> + succ (x,y,a,p) + +-- pick pen up and reset turtle +moveTurtle "CLEAN" [] (x,y,a,p) + (is,(graphWindow,display,graphContext,bg,fg)) succ + = xClearArea graphWindow (XRect 0 0 500 500) True + `thenIO` \() -> + xDisplayForceOutput display + `thenIO` \() -> + succ (500 `div` 2,500 `div` 2,90,True) + +-- do nothing if arguments are incorrect +moveTurtle _ _ e _ succ = succ e + + +-- valueToString, etc. -- + +-- convert a value to a string +valueToString :: Value -> String +valueToString (Word w) = w +valueToString (Num n) = show n +valueToString (Boolean True) = "TRUE" +valueToString (Boolean False) = "FALSE" +valueToString Null = "" +valueToString (List l) = "[" ++ (listToString l) ++ "]" +valueToString GoodBye = "Don't play around with this variable!" + +-- convert a list to a string +listToString :: ListType -> String +listToString NullList = "" +listToString (v :* NullList) = valueToString v +listToString (v :* l) = (valueToString v) ++ " " ++ (listToString l) + + + +------------------------------------------------------------------------------- +module Lexer where + +{- + +Lexer takes as input a line from standard input and returns an ordered +pair containing the translation of that list into tokens as well as +the current state of the lexer (how many parentheses and brackets are +still open). The state is necessary because some commands may take +multiple lines, so a bracket (say) may be left open on one line to be +closed later on. + +All unmatched close brackets and parentheses are treated as spaces +(and therefore ignored). + +The method for tokenizing commands is: + + All words are delimited by spaces, parenthesis, or brackets. + + All words beginning with a double quote are returned as quoted words + rather than normal words. + + Any character preceded by a backslash is taken as is, rather than + tokenized normally. + + All words are translated to upper case.. + +The method for tokenizing user input is: + + All words are delimited by spaces and translated to upper case. + +-} + +import Parser +import Evaluator + + +data LexState = Lexer | LexerBracket Int LexState | LexerParen Int LexState + deriving Text + +type LexerType = [Char] -> ([Token] , LexState) + +data Token = OpenBracket + | CloseBracket + | OpenParen + | CloseParen + | QuotedWord WordType + | Normal WordType deriving (Text,Eq) + + +-- call appropriate lex procedure depending upon the current lex state +lexDispatch :: LexState -> LexerType +lexDispatch (Lexer) = lexer +lexDispatch (LexerBracket n s) = lexerBracket n s +lexDispatch (LexerParen n s) = lexerParen n s + + +-- handle commands +lexer :: LexerType +lexer [] = ([] , Lexer) +lexer (' ':cs) = lexer cs +lexer ('[':cs) = let (ts , s) = lexerBracket 1 (Lexer) cs + in (OpenBracket : ts , s) +lexer ('(':cs) = let (ts , s) = lexerParen 1 (Lexer) cs + in (OpenParen : ts , s) +lexer (')':cs) = lexer cs +lexer (']':cs) = lexer cs +lexer ('"':cs) = let (t , cs2) = lexerWord (isDelimiter) cs + (ts , s) = lexer cs2 + in ((QuotedWord (upWord t)):ts , s) +lexer cs = let (t , cs2) = lexerWord (isDelimiter) cs + (ts , s) = lexer cs2 + in ((Normal (upWord t)):ts , s) + +lexerWord :: (Char -> Bool) -> [Char] -> (WordType , [Char]) +lexerWord endCond [] + = ([] , []) +lexerWord endCond (c:cs) + | c == '\\' = if cs == [] + then ("\\" , cs) + else + let (t , cs2) = lexerWord endCond (tail cs) + in ((head cs):t , cs2) + | endCond c = ([] , (c:cs)) + | otherwise = let (t , cs2) = lexerWord endCond cs + in ((toUpper c):t , cs2) + + +-- performs lexing inside brackets +lexerBracket :: Int -> LexState -> LexerType +lexerBracket n s [] + = ([] , LexerBracket n s) +lexerBracket n s (' ':cs) + = lexerBracket n s cs +lexerBracket 1 s (']':cs) + = let (ts , s2) = lexDispatch s cs + in (CloseBracket:ts , s2) +lexerBracket n s (']':cs) + = let (ts , s2) = lexerBracket (n-1) s cs + in (CloseBracket:ts , s2) +lexerBracket n s ('[':cs) + = let (ts , s2) = lexerBracket (n+1) s cs + in (OpenBracket:ts , s2) +lexerBracket n s ('(':cs) + = let (ts , s2) = lexerParen 1 (LexerBracket n s) cs + in (OpenParen:ts , s2) +lexerBracket n s (')':cs) + = lexerBracket n s cs +lexerBracket n s cs + = let (t , cs2) = lexerWord (isDelimiter) cs + (ts , s2) = lexerBracket n s cs2 + in ((Normal (upWord t)):ts , s2) + + +-- performs lexing inside parentheses +lexerParen :: Int -> LexState -> LexerType +lexerParen n s [] + = ([] , LexerParen n s) +lexerParen n s (' ':cs) + = lexerParen n s cs +lexerParen 1 s (')':cs) + = let (ts , s2) = lexDispatch s cs + in (CloseParen:ts , s2) +lexerParen n s (')':cs) + = let (ts , s2) = lexerParen (n-1) s cs + in (CloseParen:ts , s2) +lexerParen n s ('(':cs) + = let (ts , s2) = lexerParen (n+1) s cs + in (OpenParen:ts , s2) +lexerParen n s ('[':cs) + = let (ts , s2) = lexerBracket 1 (LexerParen n s) cs + in (OpenBracket:ts , s2) +lexerParen n s (']':cs) + = lexerParen n s cs +lexerParen n s ('"':cs) + = let (t , cs2) = lexerWord (isDelimiter) cs + (ts , s2) = lexerParen n s cs2 + in ((QuotedWord (upWord t)):ts , s2) +lexerParen n s cs + = let (t , cs2) = lexerWord (isDelimiter) cs + (ts , s2) = lexerParen n s cs2 + in ((Normal (upWord t)):ts , s2) + + +-- returns true for delimiters +isDelimiter :: Char -> Bool +isDelimiter = inList " []()" + +-- returns true of p is in cs +inList :: (Eq a) => [a] -> a -> Bool +inList [] p = False +inList (c:cs) p = (c == p) || (inList cs p) + + +-- handle user input +lexerReadLine :: [Char] -> [WordType] +lexerReadLine [] + = [] +lexerReadLine (' ':cs) + = lexerReadLine cs +lexerReadLine cs + = let (firstWord,restOfWords) = span (/= ' ') cs + in (upWord firstWord) : lexerReadLine restOfWords + +-- translate a word to upper case +upWord :: WordType -> WordType +upWord = map (toUpper) + + + +------------------------------------------------------------------------------- +module Parser where + +{- + +Parser takes a list of tokens, the input state, and fail and success +continuations and returns an Abstract Syntax Tree, the remaining +tokens (hopefully none), and the new input state. The input state +will be changed every time Parser runs out of tokens: it simply grabs +(and lexes) the next line of user-input. It therefore doesn't return +anything until the entire AST has been be read in, even if it spans +several lines, though parse may catch some errors before all lines +have been input. In this case, it ceases taking input and returns the +error. + +An Abstract Syntax Tree represents one command, and breaks those +commands into Ifs, Loops, Tos, Locals, Makes, Reads, Prints, +Constants, List constants, Graphics commands (which produce +side-effects), and function applications. All built-in commands that +don't fit into one of those categories are lumped into function +applications along with user-defined functions. Each type of AST is +parsed into subcommands, subclauses (lists of commands), command +arguments (also subcommands), and any other values that will be +immediately-evaluatable (such as function names). + +-} + + +import Lexer +import Evaluator + + +type CommandName = [Char] +type ClauseType = [AST] +type ConditionType = AST + +type ParseArgs = [AST] + +data ArgType = Val Value | QuotedWordArg WordType + deriving Text + +data AST = ParseList ListType + | If ConditionType ClauseType ClauseType + | Loop LoopType ConditionType ClauseType + | To NameType ProcType + | Make NameType AST + | Local NameType + | Read + | Print ParseArgs + | Argument ArgType + | Graphics CommandName ParseArgs + | Command CommandName ParseArgs deriving Text + +data LoopType = Do | While | Repeat + deriving Text + +type ParseFailType = Error -> InputState -> IO () +type ParseType = [Token] -> InputState -> ParseFailType -> + (AST -> [Token] -> InputState -> IO ()) -> IO () +type ParseClauseType = [Token] -> InputState -> ParseFailType -> + (ClauseType -> [Token] -> InputState -> IO ()) -> IO () + +type InputState = ([[Char]] , LexState) + +parse :: ParseType + +parse [] (i:is , ls) fail succ + = let (ts , ls2) = lexDispatch ls i + in parse ts (is , ls2) fail succ + +parse ((QuotedWord s) : ts) inS fail succ + = succ (Argument (QuotedWordArg s)) ts inS + +parse ((Normal s) : ts) inS fail succ + = succ (Argument (Val (process s))) ts inS + +parse (OpenParen : []) (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in parse (OpenParen:ts) (is,ls2) fail succ + +parse (OpenParen : (Normal t) : ts) inS fail succ + | t == "TO" = makeProc ts inS fail succ + | t == "MAKE" = makeMake ts inS fail succ + | t == "LOCAL" = makeLocal ts inS fail succ + | t == "READ" = makeRead ts inS fail succ + | t == "PRINT" = makePrint ts inS fail succ + | t == "IF" = makeIf ts inS fail succ + | isLoop t = makeLoop t ts inS fail succ + | isGraphics t = makeGraphics t ts inS fail succ + | otherwise = makeCommand t ts inS fail succ + +parse (OpenBracket : ts) inS fail succ + = parseList ts inS fail succ + +parse ts inS@([], _) _ succ = succ (Argument (Val (Word "GOODBYE"))) ts inS + +parse _ inS fail _ + = fail "Syntax error" inS + + +-- returns true for all loop names +isLoop :: CommandName -> Bool +isLoop = inList ["DO","WHILE","REPEAT"] + +-- returns true for all side-effecting graphics command names +isGraphics :: CommandName -> Bool +isGraphics = inList ["FORWARD","BACKWARD","LEFT","RIGHT", + "SETXY","SETANGLE","PENUP","PENDOWN", + "HIDETURTLE","SHOWTURTLE","CLEARSCREEN","CLEAN"] + +-- Parse lists -- + +-- parses a list constant +parseList :: ParseType +parseList [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in parseList ts (is,ls2) fail succ +parseList (CloseBracket:ts) inS fail succ + = succ (ParseList NullList) ts inS +parseList (OpenBracket:ts) inS fail succ + = parseList ts inS fail $ \(ParseList l1) ts2 inS2 -> + parseList ts2 inS2 fail $ \(ParseList l2) ts3 inS3 -> + succ (ParseList ((List l1) :* l2)) ts3 inS3 +parseList ((Normal w):ts) inS fail succ + = parseList ts inS fail $ \(ParseList l) ts2 inS2 -> + succ (ParseList ((process w) :* l)) ts2 inS2 +parseList (OpenParen:ts) inS fail succ + = parseList ts inS fail $ \(ParseList l) ts2 inS2 -> + succ (ParseList ((Word "(") :* l)) ts2 inS2 +parseList (CloseParen:ts) inS fail succ + = parseList ts inS fail $ \(ParseList l) ts2 inS2 -> + succ (ParseList ((Word ")") :* l)) ts2 inS2 +parseList ((QuotedWord w):ts) inS fail succ + = parseList ts inS fail $ \(ParseList l) ts2 inS2 -> + succ (ParseList ((Word w) :* l)) ts2 inS2 + + +-- parses constant values, distinguishing words from integers and booleans +process :: WordType -> Value +process "TRUE" = Boolean True +process "FALSE" = Boolean False +process ('-':w) + | all isDigit w = Num (- (stringToNum (reverse w))) + | otherwise = Word ('-':w) +process w + | all isDigit w = Num (stringToNum (reverse w)) + | otherwise = Word w + +-- converts a string to a positive integer +stringToNum :: String -> Int +stringToNum (d:[]) = charToDigit d +stringToNum (d:ds) = (charToDigit d) + 10 * stringToNum ds + +-- converts a character to a digit +charToDigit :: Char -> Int +charToDigit c = ord c - ord '0' + + +-- Parse command statements -- + +-- parses commands +-- format: ( ...) +makeCommand :: CommandName -> ParseType +makeCommand n ts inS fail succ + = parseArgs CloseParen ts inS fail $ \as ts2 inS2 -> + succ (Command n as) ts2 inS2 + + +-- parses a list of commands that are terminated by token "term"" +parseArgs :: Token -> ParseClauseType +parseArgs term [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in parseArgs term ts (is,ls2) fail succ +parseArgs term (t:ts) inS fail succ + | t == term = succ [] ts inS + | otherwise = parse (t:ts) inS fail $ \a ts2 inS2 -> + parseArgs term ts2 inS2 fail $ \as ts3 inS3 -> + succ (a:as) ts3 inS3 + + +-- Parse I/O statements -- + +-- parses read statements +-- format: (READ) +makeRead :: ParseType +makeRead (CloseParen:ts) inS fail succ + = succ Read ts inS +makeRead _ inS fail _ + = fail "Read: too many arguments" inS + +-- parses print statements +-- format: (PRINT ) +makePrint :: ParseType +makePrint ts inS fail succ + = parseArgs CloseParen ts inS fail $ \as ts2 inS2 -> + if (length as) == 1 + then succ (Print as) ts2 inS2 + else fail "Print: too many arguments" inS + + + +-- Parse TO statements -- + + +-- parses to statements +-- format: (TO ... ) +-- note: all formal parameter names must begin with a colon +makeProc :: ParseType +makeProc [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in makeProc ts (is,ls2) fail succ +makeProc ((Normal t):ts) inS fail succ + = parseFormals ts inS fail $ \p ts2 inS2 -> + getParen ts2 inS2 fail $ \ts3 inS3 -> + succ (To t p) ts3 inS3 +makeProc _ inS fail _ + = fail "Invalid procedure name" inS + +-- parses the formal parameters +-- takes all words beginning with a colon, and assumes everything +-- after that is part of the body +parseFormals :: [Token] -> InputState -> ParseFailType -> + (([NameType] , ClauseType) -> [Token] -> InputState -> IO ()) + -> IO () +parseFormals [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in parseFormals ts (is,ls2) fail succ +parseFormals (OpenBracket:ts) inS fail succ + = parseClause (OpenBracket:ts) inS fail $ \pb ts2 inS2 -> + succ ([],pb) ts2 inS2 +parseFormals ((Normal (':':c:cs)):ts) inS fail succ + = parseFormals ts inS fail $ \(formals,pb) ts2 inS2 -> + succ ((':':c:cs):formals , pb) ts2 inS2 +parseFormals ts inS fail succ + = parseClause ts inS fail $ \pb ts2 inS2 -> + succ ([],pb) ts2 inS2 + + +-- Parse MAKE statements -- + +-- parses make statements +-- format: (MAKE ) +-- note: must be quoted +makeMake :: ParseType +makeMake [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in makeMake ts (is,ls2) fail succ +makeMake ((QuotedWord s):ts) inS fail succ + = parse ts inS fail $ \a ts2 inS2 -> + getParen ts2 inS2 fail $ \ts3 inS3 -> + succ (Make s a) ts3 inS3 +makeMake _ inS fail _ + = fail "Make: Improper variable name" inS + + +-- Parse LOCAL statements -- + +-- parses local statements +-- format: (LOCAL ) +-- note: must be quoted +makeLocal :: ParseType +makeLocal [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in makeLocal ts (is,ls2) fail succ +makeLocal (t:[]) (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in makeLocal (t:ts) (is,ls2) fail succ +makeLocal ((QuotedWord s):CloseParen:ts) inS fail succ + = succ (Local s) ts inS +makeLocal _ inS fail _ + = fail "Local: improper variable name" inS + + +-- Parse IF statements -- + +-- parses if-then and if-then-else statements +-- format: (IF then [else ]) +makeIf :: ParseType +makeIf [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in makeIf ts (is,ls2) fail succ +makeIf ts inS fail succ + = parse ts inS fail $ \cond ts2 inS2 -> + parseThen ts2 inS2 fail $ \thens elses ts3 inS3 -> + getParen ts3 inS3 fail $ \ts4 inS4 -> + succ (If cond thens elses) ts4 inS4 + + +-- parses then clauses +parseThen :: [Token] -> InputState -> ParseFailType -> + (ClauseType -> ClauseType -> [Token] -> InputState -> IO ()) -> + IO () +parseThen [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in parseThen ts (is,ls2) fail succ +parseThen ((Normal "THEN"):ts) inS fail succ + = parseClause ts inS fail $ \thens ts2 inS2 -> + parseElse ts2 inS2 fail $ \elses ts3 inS3 -> + succ thens elses ts3 inS3 +parseThen _ inS fail _ + = fail "IF: improper THEN clause" inS + +-- parses (optional) else clauses +parseElse :: ParseClauseType +parseElse [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in parseElse ts (is,ls2) fail succ +parseElse (CloseParen:ts) inS fail succ + = succ [] (CloseParen:ts) inS +parseElse ((Normal "ELSE"):ts) inS fail succ + = parseClause ts inS fail succ +parseElse _ inS fail _ + = fail "IF: improper ELSE clause" inS + +-- parses clauses +-- a clause is either a list of commands enclosed in brackets, or a +-- single command +parseClause :: ParseClauseType +parseClause [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in parseClause ts (is,ls2) fail succ +parseClause (OpenBracket:ts) inS fail succ + = parseArgs CloseBracket ts inS fail succ +parseClause ts inS fail succ + = parse ts inS fail $ \a ts2 inS2 -> + succ [a] ts2 inS2 + + +-- Parse Loop Statements -- + +-- parses loop statements +-- basically a dispatcher for other parse functions +makeLoop :: NameType -> ParseType +makeLoop "DO" = makeDo +makeLoop "WHILE" = makeWhile +makeLoop "REPEAT" = makeRepeat + +-- parses do statements +-- format: (DO WHILE ) +makeDo :: ParseType +makeDo ts inS fail succ + = parseClause ts inS fail $ \insts ts2 inS2 -> + parseWhileCond ts2 inS2 fail $ \cond ts3 inS3 -> + getParen ts3 inS3 fail $ \ts4 inS4 -> + succ (Loop Do cond insts) ts4 inS4 + +-- parses while conditions (both in while and do-while loops) +-- a condition is simply a command that (hopefully) returns a boolean +parseWhileCond :: ParseType +parseWhileCond [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in parseWhileCond ts (is,ls2) fail succ +parseWhileCond ((Normal "WHILE"):ts) inS fail succ + = parse ts inS fail succ + +-- parses while statements +-- format: (WHILE ) +makeWhile :: ParseType +makeWhile ts inS fail succ + = parse ts inS fail $ \cond ts2 inS2 -> + parseClause ts2 inS fail $ \insts ts3 inS3 -> + getParen ts3 inS3 fail $ \ts4 inS4 -> + succ (Loop While cond insts) ts4 inS4 + +-- parses repeat statements +-- format: (REPEAT TIMES ) +-- note: is simply a command that (hopefully) returns an integer +makeRepeat :: ParseType +makeRepeat ts inS fail succ + = parse ts inS fail $ \num ts2 inS2 -> + parseRepeatBody ts2 inS fail $ \insts ts3 inS3 -> + getParen ts3 inS3 fail $ \ts4 inS4 -> + succ (Loop Repeat num insts) ts4 inS4 + +-- parses repeat body (just a clause) +parseRepeatBody :: ParseClauseType +parseRepeatBody [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in parseRepeatBody ts (is,ls2) fail succ +parseRepeatBody ((Normal "TIMES"):ts) inS fail succ + = parseClause ts inS fail succ +parseRepeatBody _ inS fail _ + = fail "Repeat: invalid format" inS + + +-- Parse Graphics Statements -- + +-- parses all side-effecting graphics statements +makeGraphics :: CommandName -> ParseType +makeGraphics n ts inS fail succ + = parseArgs CloseParen ts inS fail $ \as ts2 inS2 -> + succ (Graphics n as) ts2 inS2 + +-- Parse Trailing Parenthesis -- + +-- parses the closing paren terminating most commands +getParen :: [Token] -> InputState -> ParseFailType -> + ([Token] -> InputState -> IO ()) -> IO () +getParen [] (i:is,ls) fail succ + = let (ts,ls2) = lexDispatch ls i + in getParen ts (is,ls) fail succ +getParen (CloseParen:ts) inS fail succ + = succ ts inS +getParen _ inS fail _ + = fail "Expected )" inS + diff --git a/progs/demo/X11/logo/logo.hu b/progs/demo/X11/logo/logo.hu new file mode 100644 index 0000000..388e926 --- /dev/null +++ b/progs/demo/X11/logo/logo.hu @@ -0,0 +1,3 @@ +:o= foldr inline constant +$HASKELL_LIBRARY/X11/xlib.hu +logo.hs diff --git a/progs/demo/X11/mdraw/README b/progs/demo/X11/mdraw/README new file mode 100644 index 0000000..c78f7d9 --- /dev/null +++ b/progs/demo/X11/mdraw/README @@ -0,0 +1 @@ +This is a multiple screen version of the draw program. diff --git a/progs/demo/X11/mdraw/mdraw.hs b/progs/demo/X11/mdraw/mdraw.hs new file mode 100644 index 0000000..c4bb508 --- /dev/null +++ b/progs/demo/X11/mdraw/mdraw.hs @@ -0,0 +1,83 @@ +module MDraw where + +import Xlib + +mapIO :: (a -> IO b) -> [a] -> IO [b] + +mapIO f [] = returnIO [] +mapIO f (x:xs) = f x `thenIO` \ y -> + mapIO f xs `thenIO` \ ys -> + returnIO (y:ys) + +map2IO :: (a -> b -> IO c) -> [a] -> [b] -> IO [c] + +map2IO f [] [] = returnIO [] +map2IO f (x:xs) (z:zs) = f x z `thenIO` \ y -> + map2IO f xs zs `thenIO` \ ys -> + returnIO (y:ys) + +xGetEventMul :: XMArray XDisplay -> IO (Int, XEvent) +xGetEventMul displays = + let n_displays = xMArrayLength displays + loop :: Int -> IO (Int, XEvent) + loop i = if i == n_displays then loop 0 + else xMArrayLookup displays i `thenIO` \ display -> + xDisplayForceOutput display `thenIO` \ _ -> + xEventListen display `thenIO` \ n_events -> + if n_events == 0 then loop (i + 1) + else xGetEvent display `thenIO` \ event -> + returnIO (i, event) + in loop 0 + +-- takes a list of host names + +mdraw :: [String] -> IO () +mdraw hosts = + xHandleError (\ (XError msg) -> appendChan stdout msg exit done) $ + mapIO xOpenDisplay hosts `thenIO` \ displays -> + let screens = map (head . xDisplayRoots) displays + fg_colors = map xScreenBlackPixel screens + bg_colors = map xScreenWhitePixel screens + roots = map xScreenRoot screens + in + map2IO (\ root color -> + xCreateWindow root + (XRect 100 100 400 400) + [XWinBackground color, + XWinEventMask (XEventMask [XButtonMotion, + XButtonPress])]) + roots + bg_colors + `thenIO` \windows -> + mapIO xMapWindow windows `thenIO` \ _ -> + map2IO xCreateGcontext + (map XDrawWindow roots) + (map (\ color -> [XGCForeground color]) fg_colors) + `thenIO` \ gcontexts -> + xMArrayCreate displays `thenIO` \ displayArr -> + let + handleEvent lasts = + xGetEventMul displayArr `thenIO` \ (idx, event) -> + let pos = xEventPos event + in + case (xEventType event) of + XButtonPressEvent -> + xMArrayUpdate lasts idx pos `thenIO` \ () -> + handleEvent lasts + XMotionNotifyEvent -> + xMArrayLookup lasts idx `thenIO` \ last -> + map2IO (\ window gcontext -> xDrawLine (XDrawWindow window) + gcontext + last + pos) + windows + gcontexts + `thenIO` \ _ -> + xMArrayUpdate lasts idx pos `thenIO` \ () -> + handleEvent lasts + _ -> handleEvent lasts + in + xMArrayCreate (map (\ _ -> XPoint 0 0) hosts) `thenIO` \ lasts -> + handleEvent lasts `thenIO` \ _ -> + returnIO () + diff --git a/progs/demo/X11/mdraw/mdraw.hu b/progs/demo/X11/mdraw/mdraw.hu new file mode 100644 index 0000000..16296d5 --- /dev/null +++ b/progs/demo/X11/mdraw/mdraw.hu @@ -0,0 +1,3 @@ +:o= all +$HASKELL_LIBRARY/X11/xlib.hu +mdraw.hs diff --git a/progs/demo/X11/mdraw/t.hs b/progs/demo/X11/mdraw/t.hs new file mode 100644 index 0000000..77f2baf --- /dev/null +++ b/progs/demo/X11/mdraw/t.hs @@ -0,0 +1,16 @@ +module Test where +import Xlib + +xGetEventMul :: XMArray XDisplay -> IO (Int, XEvent) +xGetEventMul displays = + let n_displays = xMArrayLength displays + loop :: Int -> IO (Int, XEvent) + loop i = if i == n_displays then loop 0 + else xMArrayLookup displays i `thenIO` \ display -> + xDisplayForceOutput display `thenIO` \ _ -> + xEventListen display `thenIO` \ n_events -> + if n_events == 0 then loop (i + 1) + else xGetEvent display `thenIO` \ event -> + returnIO (i, event) + in loop 0 + diff --git a/progs/demo/X11/mdraw/t.hu b/progs/demo/X11/mdraw/t.hu new file mode 100644 index 0000000..657234c --- /dev/null +++ b/progs/demo/X11/mdraw/t.hu @@ -0,0 +1,3 @@ +:o= all +$HASKELL_LIBRARY/X11/xlib.hu +t.hs diff --git a/progs/demo/add.hs b/progs/demo/add.hs new file mode 100644 index 0000000..bdfcc2f --- /dev/null +++ b/progs/demo/add.hs @@ -0,0 +1,21 @@ +-- this is an interactive program to read in two numbers and print their sum. + +module Main where + +main = readChan stdin abort $ \userInput -> + let inputLines = lines userInput in + readInt "Enter first number: " inputLines $ \num1 inputLines1 -> + readInt "Enter second number: " inputLines1 $ \ num2 _ -> + appendChan stdout ("Their sum is: " ++ show (num1 + num2)) abort done + +readInt :: String -> [String] -> (Integer -> [String] -> Dialogue) -> Dialogue + +readInt prompt inputLines succ = + appendChan stdout prompt abort $ + case inputLines of + (l1 : rest) -> case (reads l1) of + [(x,"")] -> succ x rest + _ -> appendChan stdout + "Error - retype the number\n" abort $ + readInt prompt rest succ + _ -> appendChan stdout "Early EOF" abort done diff --git a/progs/demo/eliza.hs b/progs/demo/eliza.hs new file mode 100644 index 0000000..d7bf975 --- /dev/null +++ b/progs/demo/eliza.hs @@ -0,0 +1,267 @@ +-- Eliza: an implementation of the classic pseudo-psychoanalyst --------------- +-- +-- Gofer version by Mark P. Jones, January 12 1992 +-- +-- Adapted from a pascal implementation provided as part of an experimental +-- package from James Risner (risner@ms.uky.edu), Univ. of KY. with original +-- pascal code apparently provided by Robert Migliaccio (mig@ms.uky.edu). +------------------------------------------------------------------------------- + +import Prelude hiding (conjugate) + +main :: Dialogue +main = interact (("\n\ + \Hi! I'm Eliza. I am your personal therapy computer.\n\ + \Please tell me your problem.\n\ + \\n" ++) + . session initial [] + . filter (not.null) + . map (words . trim) + . lines) + +trim :: String -> String -- strip punctuation characters +trim = foldr cons "" . dropWhile (`elem` punct) + where x `cons` xs | x `elem` punct && null xs = [] + | otherwise = x : xs + punct = [' ', '.', '!', '?', ','] + +-- Read a line at a time, and produce some kind of response ------------------- + +session :: State -> Words -> [Words] -> String +session rs prev [] = [] +session rs prev (l:ls) = response ++ "\n\n" ++ session rs' l ls + where (response, rs') | prev == l = repeated rs + | otherwise = answer rs l + +answer :: State -> Words -> (String, State) +answer st l = (response, newKeyTab kt st) + where (response, kt) = ans (keyTabOf st) + e `cons` (r, es) = (r, e:es) + ans (e:es) | null rs = e `cons` ans es + | otherwise = (makeResponse a (head rs), (key,as):es) + where rs = replies key l + (key,(a:as)) = e + +-- Find all possible replies (without leading string for given key ------------ + +replies :: Words -> Words -> [String] +replies key l = ( map (conjugate l . drop (length key)) + . filter (prefix key . map ucase) + . tails) l + +prefix :: Eq a => [a] -> [a] -> Bool +[] `prefix` xs = True +(x:xs) `prefix` [] = False +(x:xs) `prefix` (y:ys) = x==y && (xs `prefix` ys) + +tails :: [a] -> [[a]] -- non-empty tails of list +tails [] = [] +tails xs = xs : tails (tail xs) + +ucase :: String -> String -- map string to upper case +ucase = map toUpper + +-- Replace keywords in a list of words with appropriate conjugations ---------- + +conjugate :: Words -> Words -> String +conjugate d = unwords . trailingI . map conj . maybe d -- d is default input + where maybe d xs = if null xs then d else xs + conj w = head ([m | (w',m)<-conjugates, uw==w'] ++ [w]) + where uw = ucase w + trailingI = foldr cons [] + where x `cons` xs | x=="I" && null xs = ["me"] + | otherwise = x:xs + +conjugates :: [(Word, Word)] +conjugates = prepare (oneways ++ concat [[(x,y), (y,x)] | (x,y) <- bothways]) + where oneways = [ ("me", "you") ] + bothways = [ ("are", "am"), ("we're", "was"), + ("you", "I"), ("your", "my"), + ("I've", "you've"), ("I'm", "you're") ] + prepare = map (\(w,r) -> (ucase w, r)) + +-- Response data -------------------------------------------------------------- + +type Word = String +type Words = [Word] +type KeyTable = [(Key, Replies)] +type Replies = [String] +type State = (KeyTable, Replies) +type Key = Words + +repeated :: State -> (String, State) +repeated (kt, (r:rp)) = (r, (kt, rp)) + +newKeyTab :: KeyTable -> State -> State +newKeyTab kt' (kt, rp) = (kt', rp) + +keyTabOf :: State -> KeyTable +keyTabOf (kt, rp) = kt + +makeResponse :: String -> String -> String +makeResponse ('?':cs) us = cs ++ " " ++ us ++ "?" +makeResponse ('.':cs) us = cs ++ " " ++ us ++ "." +makeResponse cs us = cs + +initial :: State +initial = ([(words k, cycle rs) | (k,rs) <-respMsgs], cycle repeatMsgs) + +respMsgs = [ ("CAN YOU", canYou), + ("CAN I", canI), + ("YOU ARE", youAre), + ("YOU'RE", youAre), + ("I DON'T", iDont), + ("I FEEL", iFeel), + ("WHY DON'T YOU", whyDont), + ("WHY CAN'T I", whyCant), + ("ARE YOU", areYou), + ("I CAN'T", iCant), + ("I AM", iAm), + ("I'M", iAm), + ("YOU", you), + ("YES", yes), + ("NO", no), + ("COMPUTER", computer), + ("COMPUTERS", computer), + ("I WANT", iWant), + ("WHAT", question), + ("HOW", question), + ("WHO", question), + ("WHERE", question), + ("WHEN", question), + ("WHY", question), + ("NAME", name), + ("BECAUSE", because), + ("CAUSE", because), + ("SORRY", sorry), + ("DREAM", dream), + ("DREAMS", dream), + ("HI", hello), + ("HELLO", hello), + ("MAYBE", maybe), + ("YOUR", your), + ("ALWAYS", always), + ("THINK", think), + ("ALIKE", alike), + ("FRIEND", friend), + ("FRIENDS", friend), + ("", nokeyMsgs) ] + +canYou = [ "?Don't you believe that I can", + "?Perhaps you would like to be able to", + "?You want me to be able to" ] +canI = [ "?Perhaps you don't want to", + "?Do you want to be able to" ] +youAre = [ "?What makes you think I am", + "?Does it please you to believe I am", + "?Perhaps you would like to be", + "?Do you sometimes wish you were" ] +iDont = [ "?Don't you really", + "?Why don't you", + "?Do you wish to be able to", + "Does that trouble you?" ] +iFeel = [ "Tell me more about such feelings.", + "?Do you often feel", + "?Do you enjoy feeling" ] +whyDont = [ "?Do you really believe I don't", + ".Perhaps in good time I will", + "?Do you want me to" ] +whyCant = [ "?Do you think you should be able to", + "?Why can't you" ] +areYou = [ "?Why are you interested in whether or not I am", + "?Would you prefer if I were not", + "?Perhaps in your fantasies I am" ] +iCant = [ "?How do you know you can't", + "Have you tried?", + "?Perhaps you can now" ] +iAm = [ "?Did you come to me because you are", + "?How long have you been", + "?Do you believe it is normal to be", + "?Do you enjoy being" ] +you = [ "We were discussing you --not me.", + "?Oh,", + "You're not really talking about me, are you?" ] +yes = [ "You seem quite positive.", + "Are you Sure?", + "I see.", + "I understand." ] +no = [ "Are you saying no just to be negative?", + "You are being a bit negative.", + "Why not?", + "Are you sure?", + "Why no?" ] +computer = [ "Do computers worry you?", + "Are you talking about me in particular?", + "Are you frightened by machines?", + "Why do you mention computers?", + "What do you think machines have to do with your problems?", + "Don't you think computers can help people?", + "What is it about machines that worries you?" ] +iWant = [ "?Why do you want", + "?What would it mean to you if you got", + "?Suppose you got", + "?What if you never got", + ".I sometimes also want" ] +question = [ "Why do you ask?", + "Does that question interest you?", + "What answer would please you the most?", + "What do you think?", + "Are such questions on your mind often?", + "What is it that you really want to know?", + "Have you asked anyone else?", + "Have you asked such questions before?", + "What else comes to mind when you ask that?" ] +name = [ "Names don't interest me.", + "I don't care about names --please go on." ] +because = [ "Is that the real reason?", + "Don't any other reasons come to mind?", + "Does that reason explain anything else?", + "What other reasons might there be?" ] +sorry = [ "Please don't apologise!", + "Apologies are not necessary.", + "What feelings do you have when you apologise?", + "Don't be so defensive!" ] +dream = [ "What does that dream suggest to you?", + "Do you dream often?", + "What persons appear in your dreams?", + "Are you disturbed by your dreams?" ] +hello = [ "How do you...please state your problem." ] +maybe = [ "You don't seem quite certain.", + "Why the uncertain tone?", + "Can't you be more positive?", + "You aren't sure?", + "Don't you know?" ] +your = [ "?Why are you concerned about my", + "?What about your own" ] +always = [ "Can you think of a specific example?", + "When?", + "What are you thinking of?", + "Really, always?" ] +think = [ "Do you really think so?", + "?But you are not sure you", + "?Do you doubt you" ] +alike = [ "In what way?", + "What resemblence do you see?", + "What does the similarity suggest to you?", + "What other connections do you see?", + "Cound there really be some connection?", + "How?" ] +friend = [ "Why do you bring up the topic of friends?", + "Do your friends worry you?", + "Do your friends pick on you?", + "Are you sure you have any friends?", + "Do you impose on your friends?", + "Perhaps your love for friends worries you." ] + +repeatMsgs = [ "Why did you repeat yourself?", + "Do you expect a different answer by repeating yourself?", + "Come, come, elucidate your thoughts.", + "Please don't repeat yourself!" ] + +nokeyMsgs = [ "I'm not sure I understand you fully.", + "What does that suggest to you?", + "I see.", + "Can you elaborate on that?", + "Say, do you have any psychological problems?" ] + +------------------------------------------------------------------------------- diff --git a/progs/demo/fact.hs b/progs/demo/fact.hs new file mode 100755 index 0000000..054183e --- /dev/null +++ b/progs/demo/fact.hs @@ -0,0 +1,14 @@ +{- This is a simple factorial program which uses the I/O system + to read the input and print the result -} + +module Main where + +fact :: Integer -> Integer +fact 0 = 1 +fact (n+1) = (n+1)*fact n +fact _ = error "Negative argument to factorial" + +main = appendChan stdout "Type in N: " abort $ + readChan stdin abort $ \ input -> + appendChan stdout (show (fact (read (head (lines input))))) abort done + diff --git a/progs/demo/improved-add.hs b/progs/demo/improved-add.hs new file mode 100644 index 0000000..bdfcc2f --- /dev/null +++ b/progs/demo/improved-add.hs @@ -0,0 +1,21 @@ +-- this is an interactive program to read in two numbers and print their sum. + +module Main where + +main = readChan stdin abort $ \userInput -> + let inputLines = lines userInput in + readInt "Enter first number: " inputLines $ \num1 inputLines1 -> + readInt "Enter second number: " inputLines1 $ \ num2 _ -> + appendChan stdout ("Their sum is: " ++ show (num1 + num2)) abort done + +readInt :: String -> [String] -> (Integer -> [String] -> Dialogue) -> Dialogue + +readInt prompt inputLines succ = + appendChan stdout prompt abort $ + case inputLines of + (l1 : rest) -> case (reads l1) of + [(x,"")] -> succ x rest + _ -> appendChan stdout + "Error - retype the number\n" abort $ + readInt prompt rest succ + _ -> appendChan stdout "Early EOF" abort done diff --git a/progs/demo/merge.hs b/progs/demo/merge.hs new file mode 100755 index 0000000..cf61f8f --- /dev/null +++ b/progs/demo/merge.hs @@ -0,0 +1,26 @@ +{- This is a simple merge sort -} + +module Merge where + +merge :: [Int] -> [Int] -> [Int] +merge [] x = x +merge x [] = x +merge l1@(a:b) l2@(c:d) | a < c = a:(merge b l2) + | otherwise = c:(merge l1 d) + +half [] = [] +half [x] = [x] +half (x:y:z) = x:r where r = half z + +sort [] = [] +sort [x] = [x] +sort l = merge (sort odds) (sort evens) where + odds = half l + evens = half (tail l) + +main = + appendChan stdout "Enter a list of integers separated by \",\"\n" abort $ + readChan stdin abort $ \ input -> + appendChan stdout + (show (sort (read ("[" ++ (head (lines input)) ++ "]")))) + abort done diff --git a/progs/demo/pascal.hs b/progs/demo/pascal.hs new file mode 100644 index 0000000..a26e9c9 --- /dev/null +++ b/progs/demo/pascal.hs @@ -0,0 +1,24 @@ +{- This uses lazy evaluation to define Pascals triangle -} + +module Main where + +pascal :: [[Int]] +pascal = [1] : [[x+y | (x,y) <- zip ([0]++r) (r++[0])] | r <- pascal] + +tab :: Int -> ShowS +tab 0 = id +tab (n+1) = showChar ' ' . tab n + +showRow :: [Int] -> ShowS +showRow [] = showChar '\n' +showRow (n:ns) = shows n . showChar ' ' . showRow ns + +showTriangle 1 (t:_) = showRow t +showTriangle (n+1) (t:ts) = tab n . showRow t . showTriangle n ts + +main = appendChan stdout "Number of rows: " abort $ + readChan stdin abort $ \input -> + appendChan stdout + (showTriangle (read (head (lines input))) pascal "") + abort done + diff --git a/progs/demo/pfac.hs b/progs/demo/pfac.hs new file mode 100644 index 0000000..516fc85 --- /dev/null +++ b/progs/demo/pfac.hs @@ -0,0 +1,21 @@ + +-- This is a parallel varient of factorial + +module Main where + +fac :: Int -> Int +fac 0 = 1 +fac n = pfac 1 n + +pfac :: Int -> Int -> Int +pfac low high | low == high = low + | low + 1 == high = (low * high) + | otherwise = pfac low mid * pfac (mid + 1) high + where + mid = (high + low) `div` 2 + +main = appendChan stdout "Type in N: " abort $ + readChan stdin abort $ \ input -> + appendChan stdout (show (fac (read (head (lines input))))) abort done + + diff --git a/progs/demo/primes.hs b/progs/demo/primes.hs new file mode 100755 index 0000000..6c8fe79 --- /dev/null +++ b/progs/demo/primes.hs @@ -0,0 +1,16 @@ +-- This program implements Eratosthenes Sieve +-- to generate prime numbers. + +module Main where + +primes :: [Int] +primes = map head (iterate sieve [2 ..]) + +sieve :: [Int] -> [Int] +sieve (p:ps) = [x | x <- ps, (x `mod` p) /= 0] + +main = appendChan stdout "How many primes? " abort $ + readChan stdin abort $ \ input -> + appendChan stdout (show (take (read (head (lines input))) primes)) + abort done + diff --git a/progs/demo/prolog/Engine.hs b/progs/demo/prolog/Engine.hs new file mode 100644 index 0000000..a269503 --- /dev/null +++ b/progs/demo/prolog/Engine.hs @@ -0,0 +1,61 @@ +-- +-- Stack based Prolog inference engine +-- Mark P. Jones November 1990 +-- +-- uses Haskell B. version 0.99.3 +-- +module Engine(prove) where + +import PrologData +import Subst + +--- Calculation of solutions: + +-- the stack based engine maintains a stack of triples (s,goal,alts) +-- corresponding to backtrack points, where s is the substitution at that +-- point, goal is the outstanding goal and alts is a list of possible ways +-- of extending the current proof to find a solution. Each member of alts +-- is a pair (tp,u) where tp is a new subgoal that must be proved and u is +-- a unifying substitution that must be combined with the substitution s. +-- +-- the list of relevant clauses at each step in the execution is produced +-- by attempting to unify the head of the current goal with a suitably +-- renamed clause from the database. + +type Stack = [ (Subst, [Term], [Alt]) ] +type Alt = ([Term], Subst) + +alts :: Database -> Int -> Term -> [Alt] +alts db n g = [ (tp,u) | (tm:*tp) <- renClauses db n g, u <- unify g tm ] + +-- The use of a stack enables backtracking to be described explicitly, +-- in the following `state-based' definition of prove: + +prove :: Database -> [Term] -> [Subst] +prove db gl = solve 1 nullSubst gl [] + where + solve :: Int -> Subst -> [Term] -> Stack -> [Subst] + solve n s [] ow = s : backtrack n ow + solve n s (g:gs) ow + | g==theCut = solve n s gs (cut ow) + | otherwise = choose n s gs (alts db n (apply s g)) ow + + choose :: Int -> Subst -> [Term] -> [Alt] -> Stack -> [Subst] + choose n s gs [] ow = backtrack n ow + choose n s gs ((tp,u):rs) ow = solve (n+1) (u@@s) (tp++gs) ((s,gs,rs):ow) + + backtrack :: Int -> Stack -> [Subst] + backtrack n [] = [] + backtrack n ((s,gs,rs):ow) = choose (n-1) s gs rs ow + + +--- Special definitions for the cut predicate: + +theCut :: Term +theCut = Struct "!" [] + +cut :: Stack -> Stack +cut (top:(s,gl,_):ss) = top:(s,gl,[]):ss +cut ss = ss + +--- End of Engine.hs diff --git a/progs/demo/prolog/Engine.hu b/progs/demo/prolog/Engine.hu new file mode 100644 index 0000000..5a64277 --- /dev/null +++ b/progs/demo/prolog/Engine.hu @@ -0,0 +1,3 @@ +Engine.hs +PrologData.hu +Subst.hu diff --git a/progs/demo/prolog/Interact.hs b/progs/demo/prolog/Interact.hs new file mode 100644 index 0000000..c8bf516 --- /dev/null +++ b/progs/demo/prolog/Interact.hs @@ -0,0 +1,76 @@ +-- +-- Interactive utility functions +-- Mark P. Jones November 1990 +-- +-- uses Haskell B. version 0.99.3 +-- +module Interact(Interactive(..), skip, end, readln, writeln, readch) where + +-- The functions defined in this module provide basic facilities for +-- writing line-oriented interactive programs (i.e. a function mapping +-- an input string to an appropriate output string). These definitions +-- are an enhancement of thos in B+W 7.8 +-- +-- skip p is an interactive program which consumes no input, produces +-- no output and then behaves like the interactive program p. +-- end is an interactive program which ignores the input and +-- produces no output. +-- writeln txt p is an interactive program which outputs the message txt +-- and then behaves like the interactive program p +-- readch act def is an interactive program which reads the first character c +-- from the input stream and behaves like the interactive +-- program act c. If the input character stream is empty, +-- readch act def prints the default string def and terminates. +-- +-- readln p g is an interactive program which prints the prompt p and +-- reads a line (upto the first carriage return, or end of +-- input) from the input stream. It then behaves like g line. +-- Backspace characters included in the input stream are +-- interpretted in the usual way. + +type Interactive = String -> String + +--- Interactive program combining forms: + +skip :: Interactive -> Interactive +skip p inn = p inn -- a dressed up identity function + +end :: Interactive +end inn = "" + +writeln :: String -> Interactive -> Interactive +writeln txt p inn = txt ++ p inn + +readch :: (Char -> Interactive) -> String -> Interactive +readch act def "" = def +readch act def (c:cs) = act c cs + +readln :: String -> (String -> Interactive) -> Interactive +readln prompt g inn = prompt ++ lineOut 0 line ++ "\n" + ++ g (noBackSpaces line) input' + where line = before '\n' inn + input' = after '\n' inn + after x = tail . dropWhile (x/=) + before x = takeWhile (x/=) + +--- Filter out backspaces etc: + +rubout :: Char -> Bool +rubout c = (c=='\DEL' || c=='\BS') + +lineOut :: Int -> String -> String +lineOut n "" = "" +lineOut n (c:cs) + | n>0 && rubout c = "\BS \BS" ++ lineOut (n-1) cs + | n==0 && rubout c = lineOut 0 cs + | otherwise = c:lineOut (n+1) cs + +noBackSpaces :: String -> String +noBackSpaces = reverse . delete 0 . reverse + where delete n "" = "" + delete n (c:cs) + | rubout c = delete (n+1) cs + | n>0 = delete (n-1) cs + | otherwise = c:delete 0 cs + +--- End of Interact.hs diff --git a/progs/demo/prolog/Interact.hu b/progs/demo/prolog/Interact.hu new file mode 100644 index 0000000..41ebb9d --- /dev/null +++ b/progs/demo/prolog/Interact.hu @@ -0,0 +1,2 @@ +Interact.hs + diff --git a/progs/demo/prolog/Main.hs b/progs/demo/prolog/Main.hs new file mode 100644 index 0000000..56d83a8 --- /dev/null +++ b/progs/demo/prolog/Main.hs @@ -0,0 +1,87 @@ +-- +-- Prolog interpreter top level module +-- Mark P. Jones November 1990 +-- +-- uses Haskell B. version 0.99.3 +-- +module Main(main) where + +import PrologData +import Parse +import Interact +import Subst +import Engine +import Version + +--- Command structure and parsing: + +data Command = Fact Clause | Query [Term] | Show | Error | Quit | NoChange + +command :: Parser Command +command = just (sptok "bye" `orelse` sptok "quit") `do` (\quit->Quit) + `orelse` + just (okay NoChange) + `orelse` + just (sptok "??") `do` (\show->Show) + `orelse` + just clause `do` Fact + `orelse` + just (sptok "?-" `seq` termlist) `do` (\(q,ts)->Query ts) + `orelse` + okay Error + +--- Main program read-solve-print loop: + +signOn :: String +signOn = "Mini Prolog Version 1.5 (" ++ version ++ ")\n\n" + +main :: Dialogue +main = --echo False abort + (appendChan stdout signOn abort + (appendChan stdout ("Reading " ++ stdlib ++ "...") abort + (readFile stdlib + (\fail -> appendChan stdout "not found\n" abort + (interpreter "")) + (\lib -> appendChan stdout "done\n" abort + (interpreter lib)) + ))) + +stdlib :: String +stdlib = "$HASKELL/progs/demo/prolog/stdlib" + +interpreter :: String -> Dialogue +interpreter lib = readChan stdin abort + (\inn -> appendChan stdout (loop startDb inn) abort done) + where startDb = foldl addClause emptyDb clauses + clauses = [r | ((r,""):_)<-map clause (lines lib)] + +loop :: Database -> String -> String +loop db = readln "> " (exec db . fst . head . command) + +exec :: Database -> Command -> String -> String +exec db (Fact r) = skip (loop (addClause db r)) +exec db (Query q) = demonstrate db q +exec db Show = writeln (show db) (loop db) +exec db Error = writeln "I don't understand\n" (loop db) +exec db Quit = writeln "Thank you and goodbye\n" end +exec db NoChange = skip (loop db) + +--- Handle printing of solutions etc... + +solution :: [Id] -> Subst -> [String] +solution vs s = [ show (Var i) ++ " = " ++ show v + | (i,v) <- [ (i,s i) | i<-vs ], v /= Var i ] + +demonstrate :: Database -> [Term] -> Interactive +demonstrate db q = printOut (map (solution vs) (prove db q)) + where vs = (nub . concat . map varsIn) q + printOut [] = writeln "no.\n" (loop db) + printOut ([]:bs) = writeln "yes.\n" (loop db) + printOut (b:bs) = writeln (doLines b) (nextReqd bs) + doLines = foldr1 (\xs ys -> xs ++ "\n" ++ ys) + nextReqd bs = writeln " " + (readch (\c->if c==';' + then writeln ";\n" (printOut bs) + else writeln "\n" (loop db)) "") + +--- End of Main.hs diff --git a/progs/demo/prolog/Main.hu b/progs/demo/prolog/Main.hu new file mode 100644 index 0000000..a936ca6 --- /dev/null +++ b/progs/demo/prolog/Main.hu @@ -0,0 +1,6 @@ +Main.hs +Parse.hu +PrologData.hu +Interact.hu +Engine.hu +Version.hu diff --git a/progs/demo/prolog/Parse.hs b/progs/demo/prolog/Parse.hs new file mode 100644 index 0000000..0487432 --- /dev/null +++ b/progs/demo/prolog/Parse.hs @@ -0,0 +1,116 @@ +-- +-- General parsing library, based on Richard Bird's parselib.orw for Orwell +-- (with a number of extensions) +-- Mark P. Jones November 1990 +-- +-- uses Haskell B. version 0.99.3 +-- +module Parse(Parser(..), fail, okay, tok, sat, orelse, seq, do, + sptok, just, listOf, many, sp, many1) where + +infixr 6 `seq` +infixl 5 `do` +infixr 4 `orelse` + +--- Type definition: + +type Parser a = [Char] -> [(a,[Char])] + +-- A parser is a function which maps an input stream of characters into +-- a list of pairs each containing a parsed value and the remainder of the +-- unused input stream. This approach allows us to use the list of +-- successes technique to detect errors (i.e. empty list ==> syntax error). +-- it also permits the use of ambiguous grammars in which there may be more +-- than one valid parse of an input string. + +--- Primitive parsers: + +-- fail is a parser which always fails. +-- okay v is a parser which always succeeds without consuming any characters +-- from the input string, with parsed value v. +-- tok w is a parser which succeeds if the input stream begins with the +-- string (token) w, returning the matching string and the following +-- input. If the input does not begin with w then the parser fails. +-- sat p is a parser which succeeds with value c if c is the first input +-- character and c satisfies the predicate p. + +fail :: Parser a +fail inn = [] + +okay :: a -> Parser a +okay v inn = [(v,inn)] + +tok :: [Char] -> Parser [Char] +tok w inn = [(w, drop n inn) | w == take n inn] + where n = length w + +sat :: (Char -> Bool) -> Parser Char +sat p [] = [] +sat p (c:inn) = [ (c,inn) | p c ] + +--- Parser combinators: + +-- p1 `orelse` p2 is a parser which returns all possible parses of the input +-- string, first using the parser p1, then using parser p2. +-- p1 `seq` p2 is a parser which returns pairs of values (v1,v2) where +-- v1 is the result of parsing the input string using p1 and +-- v2 is the result of parsing the remaining input using p2. +-- p `do` f is a parser which behaves like the parser p, but returns +-- the value f v wherever p would have returned the value v. +-- +-- just p is a parser which behaves like the parser p, but rejects any +-- parses in which the remaining input string is not blank. +-- sp p behaves like the parser p, but ignores leading spaces. +-- sptok w behaves like the parser tok w, but ignores leading spaces. +-- +-- many p returns a list of values, each parsed using the parser p. +-- many1 p parses a non-empty list of values, each parsed using p. +-- listOf p s parses a list of input values using the parser p, with +-- separators parsed using the parser s. + +orelse :: Parser a -> Parser a -> Parser a +p1 `orelse` p2 = \inn->p1 inn ++ p2 inn + +seq :: Parser a -> Parser b -> Parser (a,b) +p1 `seq` p2 = \inn->[((v1,v2),inn2) | (v1,inn1) <- p1 inn, (v2,inn2) <- p2 inn1] + +do :: Parser a -> (a -> b) -> Parser b +p `do` f = \inn->[(f v, inn1) | (v,inn1) <- p inn] + +just :: Parser a -> Parser a +just p inn = [ (v,"") | (v,inn')<- p inn, dropWhile (' '==) inn' == "" ] + +sp :: Parser a -> Parser a +sp p = p . dropWhile (' '==) + +sptok :: [Char] -> Parser [Char] +sptok = sp . tok + +many :: Parser a -> Parser [a] +many p = q + where q = ((p `seq` q) `do` makeList) `orelse` (okay []) + +many1 :: Parser a -> Parser [a] +many1 p = p `seq` many p `do` makeList + +listOf :: Parser a -> Parser b -> Parser [a] +listOf p s = p `seq` many (s `seq` p) `do` nonempty + `orelse` okay [] + where nonempty (x,xs) = x:(map snd xs) + +--- Internals: + +makeList :: (a,[a]) -> [a] +makeList (x,xs) = x:xs + +{- +-- an attempt to optimise the performance of the standard prelude function +-- `take' in Haskell B 0.99.3 gives the wrong semantics. The original +-- definition, given below works correctly and is used in the above. + +safetake :: (Integral a) => a -> [b] -> [b] +safetake _ [] = [] +safetake 0 _ = [] +safetake (n+1) (x:xs) = x : safetake n xs +-} +--- End of Parse.hs diff --git a/progs/demo/prolog/Parse.hu b/progs/demo/prolog/Parse.hu new file mode 100644 index 0000000..44cc302 --- /dev/null +++ b/progs/demo/prolog/Parse.hu @@ -0,0 +1 @@ +Parse.hs diff --git a/progs/demo/prolog/PrologData.hs b/progs/demo/prolog/PrologData.hs new file mode 100644 index 0000000..4ff3173 --- /dev/null +++ b/progs/demo/prolog/PrologData.hs @@ -0,0 +1,121 @@ +-- +-- Representation of Prolog Terms, Clauses and Databases +-- Mark P. Jones November 1990 +-- +-- uses Haskell B. version 0.99.3 +-- +module PrologData(Id(..), Atom(..), Term(..), term, termlist, varsIn, + Clause((:*)), clause, + Database, emptyDb, renClauses, addClause) where + +import Parse + +infix 6 :* + +--- Prolog Terms: + +type Id = (Int,String) +type Atom = String +data Term = Var Id | Struct Atom [Term] + deriving Eq +data Clause = Term :* [Term] +data Database = Db [(Atom,[Clause])] + +--- Determine the list of variables in a term: + +varsIn :: Term -> [Id] +varsIn (Var i) = [i] +varsIn (Struct i ts) = (nub . concat . map varsIn) ts + +renameVars :: Int -> Term -> Term +renameVars lev (Var (n,s)) = Var (lev,s) +renameVars lev (Struct s ts) = Struct s (map (renameVars lev) ts) + +--- Functions for manipulating databases (as an abstract datatype) + +emptyDb :: Database +emptyDb = Db [] + +renClauses :: Database -> Int -> Term -> [Clause] +renClauses db n (Var _) = [] +renClauses db n (Struct a _) = [ r tm:*map r tp | (tm:*tp)<-clausesFor a db ] + where r = renameVars n + +clausesFor :: Atom -> Database -> [Clause] +clausesFor a (Db rss) = case dropWhile (\(n,rs) -> n [] + ((n,rs):_) -> if a==n then rs else [] + +addClause :: Database -> Clause -> Database +addClause (Db rss) r@(Struct a _ :* _) + = Db (initialPart ++ + case lastPart of + [] -> [(a,[r])] + ((n,rs):rss') -> if a==n then (n,rs++[r]):rss' + else (a,[r]):lastPart) + where (initialPart,lastPart) = span (\(n,rs) -> n u . showChar '\n' . v) + [ showWithTerm "\n" rs | (i,rs)<-rss ] + +--- Local functions for use in defining instances of Text: + +showWithSep :: Text a => String -> [a] -> ShowS +showWithSep s [x] = shows x +showWithSep s (x:xs) = shows x . showString s . showWithSep s xs + +showWithTerm :: Text a => String -> [a] -> ShowS +showWithTerm s xs = foldr1 (.) [shows x . showString s | x<-xs] + +--- String parsing functions for Terms and Clauses: +--- Local definitions: + +letter :: Parser Char +letter = sat (\c -> isAlpha c || isDigit c || c `elem` ":;+=-*&%$#@?/.~!") + +variable :: Parser Term +variable = sat isUpper `seq` many letter `do` makeVar + where makeVar (initial,rest) = Var (0,(initial:rest)) + +struct :: Parser Term +struct = many letter `seq` (sptok "(" `seq` termlist `seq` sptok ")" + `do` (\(o,(ts,c))->ts) + `orelse` + okay []) + `do` (\(name,terms)->Struct name terms) + +--- Exports: + +term :: Parser Term +term = sp (variable `orelse` struct) + +termlist :: Parser [Term] +termlist = listOf term (sptok ",") + +clause :: Parser Clause +clause = sp struct `seq` (sptok ":-" `seq` listOf term (sptok ",") + `do` (\(from,body)->body) + `orelse` okay []) + `seq` sptok "." + `do` (\(head,(goals,dot))->head:*goals) + +--- End of PrologData.hs diff --git a/progs/demo/prolog/PrologData.hu b/progs/demo/prolog/PrologData.hu new file mode 100644 index 0000000..362d35f --- /dev/null +++ b/progs/demo/prolog/PrologData.hu @@ -0,0 +1,2 @@ +PrologData.hs +Parse.hu diff --git a/progs/demo/prolog/README b/progs/demo/prolog/README new file mode 100644 index 0000000..73dbc1b --- /dev/null +++ b/progs/demo/prolog/README @@ -0,0 +1,3 @@ +This is a mini prolog interpreter written my Mark Jones. It +was slightly adapted from version in the hbc release. + diff --git a/progs/demo/prolog/Subst.hs b/progs/demo/prolog/Subst.hs new file mode 100644 index 0000000..f96e462 --- /dev/null +++ b/progs/demo/prolog/Subst.hs @@ -0,0 +1,65 @@ +-- +-- Substitutions and Unification of Prolog Terms +-- Mark P. Jones November 1990 +-- +-- uses Haskell B. version 0.99.3 +-- +module Subst(Subst(..), nullSubst, (>!), (@@), apply, unify) where + +import PrologData + +infixr 3 @@ +infix 4 >! + +--- Substitutions: + +type Subst = Id -> Term + +-- substitutions are represented by functions mapping identifiers to terms. +-- +-- apply s extends the substitution s to a function mapping terms to terms +-- nullSubst is the empty substitution which maps every identifier to the +-- same identifier (as a term). +-- i >! t is the substitution which maps the identifier i to the term t, +-- but otherwise behaves like nullSubst. +-- s1 @@ s2 is the composition of substitutions s1 and s2 +-- N.B. apply is a monoid homomorphism from (Subst,nullSubst,(@@)) +-- to (Term -> Term, id, (.)) in the sense that: +-- apply (s1 @@ s2) = apply s1 . apply s2 +-- s @@ nullSubst = s = nullSubst @@ s + +apply :: Subst -> Term -> Term +apply s (Var i) = s i +apply s (Struct a ts) = Struct a (map (apply s) ts) + +nullSubst :: Subst +nullSubst i = Var i + +(>!) :: Id -> Term -> Subst +(>!) i t j | j==i = t + | otherwise = Var j + +(@@) :: Subst -> Subst -> Subst +s1 @@ s2 = apply s1 . s2 + +--- Unification: + +-- unify t1 t2 returns a list containing a single substitution s which is +-- the most general unifier of terms t1 t2. If no unifier +-- exists, the list returned is empty. + +unify :: Term -> Term -> [Subst] +unify (Var x) (Var y) = if x==y then [nullSubst] else [x>!Var y] +unify (Var x) t2 = [ x >! t2 | not (x `elem` varsIn t2) ] +unify t1 (Var y) = [ y >! t1 | not (y `elem` varsIn t1) ] +unify (Struct a ts) (Struct b ss) = [ u | a==b, u<-listUnify ts ss ] + +listUnify :: [Term] -> [Term] -> [Subst] +listUnify [] [] = [nullSubst] +listUnify [] (r:rs) = [] +listUnify (t:ts) [] = [] +listUnify (t:ts) (r:rs) = [ u2 @@ u1 | u1<-unify t r, + u2<-listUnify (map (apply u1) ts) + (map (apply u1) rs) ] + +--- End of Subst.hs diff --git a/progs/demo/prolog/Subst.hu b/progs/demo/prolog/Subst.hu new file mode 100644 index 0000000..1bb92fb --- /dev/null +++ b/progs/demo/prolog/Subst.hu @@ -0,0 +1,2 @@ +Subst.hs +PrologData.hu diff --git a/progs/demo/prolog/Version.hs b/progs/demo/prolog/Version.hs new file mode 100644 index 0000000..c580f4b --- /dev/null +++ b/progs/demo/prolog/Version.hs @@ -0,0 +1 @@ +module Version where version="tree based" diff --git a/progs/demo/prolog/Version.hu b/progs/demo/prolog/Version.hu new file mode 100644 index 0000000..244a511 --- /dev/null +++ b/progs/demo/prolog/Version.hu @@ -0,0 +1 @@ +Version.hs diff --git a/progs/demo/prolog/stdlib b/progs/demo/prolog/stdlib new file mode 100644 index 0000000..76d2b8c --- /dev/null +++ b/progs/demo/prolog/stdlib @@ -0,0 +1,38 @@ +This file contains a list of predicate definitions that will automatically +be read into Mini Prolog at the beginning of a session. Each clause in this +file must be entered on a single line and lines containing syntax errors are +always ignored. This includes the first few lines of this file and provides +a simple way to include comments. + +append(nil,X,X). +append(cons(X,Y),Z,cons(X,W)):-append(Y,Z,W). + +equals(X,X). + +not(X):-X,!,false. +not(X). + +or(X,Y):-X. +or(X,Y):-Y. + +and(X,Y):-X,Y. + +reverse(nil,nil). +reverse(cons(A,X),Y):-and(reverse(X,Z),append(Z,cons(A,nil),Y)). + +palindromes(X):-and(reverse(X,Y),equals(X,Y)). + +mul2(A,B):-append(A,A,B). +mul4(A,B):-and(mul2(A,C),mul2(C,B)). +mul8(A,B):-and(mul4(A,C),mul2(C,B)). +mul16(A,B):-and(mul8(A,C),mul2(C,B)). +mul32(A,B):-and(mul16(A,C),mul2(C,B)). +mul64(A,B):-and(mul32(A,C),mul2(C,B)). +mul128(A,B):-and(mul64(A,C),mul2(C,B)). +mul256(A,B):-and(mul128(A,C),mul2(C,B)). +mul512(A,B):-and(mul256(A,C),mul2(C,B)). +mul1024(A,B):-and(mul512(A,C),mul2(C,B)). + +true. + +End of stdlib diff --git a/progs/demo/queens.hs b/progs/demo/queens.hs new file mode 100755 index 0000000..0f8de59 --- /dev/null +++ b/progs/demo/queens.hs @@ -0,0 +1,40 @@ +{- This is the n Queens problem. -} + +module Main where + +queens :: Int -> [[Int]] +queens size = queens' size size + +queens' :: Int -> Int -> [[Int]] +queens' 0 _ = [[]] +queens' (n+1) size = [q:qs | qs <- queens' n size, q <- [1..size], + not (threatens q qs)] + +threatens :: Int -> [Int] -> Bool +threatens q qs = q `elem` qs || q `elem` (diagonals 1 qs) + +diagonals :: Int -> [Int] -> [Int] +diagonals _ [] = [] +diagonals n (q:qs) = (q+n) : (q-n) : diagonals (n+1) qs + +main = appendChan stdout "Enter board size: " abort $ + readChan stdin abort $ \input -> + let line1 : ~(line2 : _) = lines input + size = read line1 + solns = read line2 + in if size == 0 then done else -- This causes the size to actually read + appendChan stdout "Number of solutions: " abort $ + appendChan stdout (concat (map (\x -> showBoard size x) + (take solns (queens size)))) + abort done + +showBoard :: Int -> [Int] -> String + +showBoard size pos = + concat (map showRow pos) ++ "\n" + where + showRow n = concat [if i == n then "Q " else ". " | i <- [1..size]] + ++ "\n" + + + diff --git a/progs/demo/quicksort.hs b/progs/demo/quicksort.hs new file mode 100644 index 0000000..30b4ab4 --- /dev/null +++ b/progs/demo/quicksort.hs @@ -0,0 +1,13 @@ +-- Quick sort for Haskell. + +module Main where + +qs :: [Int] -> [Int] +qs [] = [] +qs (a:as) = qs [x | x <- as, x <= a] ++ [a] ++ qs [x | x <- as, x > a] + +main = + appendChan stdout "Enter a list of integers separated by \",\"\n" abort $ + readChan stdin abort $ \ input -> + appendChan stdout (show (qs (read ("[" ++ (head (lines input)) ++ "]")))) + abort done diff --git a/progs/lib/README b/progs/lib/README new file mode 100644 index 0000000..910be2d --- /dev/null +++ b/progs/lib/README @@ -0,0 +1 @@ +This directory contains supported libraries for Yale Haskell. diff --git a/progs/lib/X11/README b/progs/lib/X11/README new file mode 100644 index 0000000..db748e4 --- /dev/null +++ b/progs/lib/X11/README @@ -0,0 +1,11 @@ +This directory contains the Haskell->CLX support code. + +If you see errors like "ID 42 is a :WM_RESIZE_HINTS, not a window", +you can get rid of them by loading clx-patch.lisp. This seems to be a +bug where CLX is not consistent with the protocol in some way; we've +seen it on some machines and not others. The line + +(load "$HASKELL/progs/lib/X11/clx-patch.lisp") + +can be placed in your .yhaskell file to load the patch on startup. + diff --git a/progs/lib/X11/clx-patch.lisp b/progs/lib/X11/clx-patch.lisp new file mode 100644 index 0000000..fe2a5e3 --- /dev/null +++ b/progs/lib/X11/clx-patch.lisp @@ -0,0 +1,39 @@ +(lisp:in-package 'xlib) +(defmacro generate-lookup-functions (useless-name &body types) + `(within-definition (,useless-name generate-lookup-functions) + ,@(mapcar + #'(lambda (type) + `(defun ,(xintern 'lookup- type) + (display id) + (declare (type display display) + (type resource-id id)) + (declare (values ,type)) + ,(if (member type *clx-cached-types*) + `(let ((,type (lookup-resource-id display id))) + (cond ((null ,type) ;; Not found, create and s +ave it. + (setq ,type (,(xintern 'make- type) + :display display :id id)) + (save-id display id ,type)) + ;; Found. Check the type + ,(cond ((null '()) ;*type-check?*) + `(t ,type)) + ((member type '(window pixmap)) + `((type? ,type 'drawable) ,type) +) + (t `((type? ,type ',type) ,type)) +) + ,@(when '() ;*type-check?* + `((t (x-error 'lookup-error + :id id + :display display + :type ',type + :object ,type)))))) + ;; Not being cached. Create a new one each time. + `(,(xintern 'make- type) + :display display :id id)))) + types))) +(macroexpand + (generate-lookup-functions ignore + window)) + diff --git a/progs/lib/X11/xlib.hs b/progs/lib/X11/xlib.hs new file mode 100644 index 0000000..716cc8c --- /dev/null +++ b/progs/lib/X11/xlib.hs @@ -0,0 +1,877 @@ +module Xlib(XLibTypes..,XLibPrims..) where +import XLibTypes +import XLibPrims + +module XLibTypes(XDisplay, XScreen, XWindow, XGcontext, XPixmap, + XColormap, XCursor, XFont, XImage, XMaybe(..), XError(..), + XBitmap(..), XKeysymTable(..), XBitVec(..), + XPixarray(..), XByteVec(..), XAtom(..), XProperty(..), + XPixel(..), XDrawable(..), XTime(..), XSwitch(..), + XWindowPlace(..), XEventMode(..), XEventKind(..), + XWindowVisibility(..), XWindowStackMode(..), + XPropertyState(..), XMapReqType(..), XGraphFun(..), + XEvent(..), XEventType(..), XEventSlot(..), XEventMask(..), + XEventMaskKey(..), XStateMask(..), XStateMaskKey(..), + XWinAttribute(..),XGCAttribute(..), XImAttribute(..), + XGrabAttribute(..), XArcMode(..), XCapStyle(..), + XClipMask(..), XFillRule(..), XFillStyle(..), + XFunction(..), XJoinStyle(..), XLineStyle(..), + XSubwindowMode(..), XPoint(..), XSize(..), XRect(..), + XArc(..), XBitmapFormat(..), XByteOrder(..), + XPixmapFormat(..), XVisualInfo(..), XVisualClass(..), + XFillContent(..), XBackingStore(..), XGravity(..), + XWindowClass(..), XMapState(..), XImageData(..), + XImageFormat(..), XImageType(..), XDrawDirection(..), + XColor(..), XInputFocus(..), XGrabStatus(..), + XKeysym(..), XCloseDownMode(..), XScreenSaver(..)) + where + +data XMaybe a {-# STRICT #-} = XSome a + | XNull + --deriving (Printers) + +data XDisplay = XDisplay --deriving (Printers) +data XScreen = XScreen --deriving (Printers) +data XWindow = XWindow --deriving (Printers) +data XGcontext = XGcontext --deriving (Printers) +data XPixmap = XPixmap --deriving (Printers) +data XColormap = XColormap --deriving (Printers) +data XCursor = XCursor --deriving (Printers) +data XFont = XFont --deriving (Printers) +data XImage = XImage --deriving (Printers) + +data XError {-# STRICT #-} + = XError String + --deriving Printers +data XBitmap {-# STRICT #-} + = XBitmap [[Int]] +instance Text(XBitmap) where + showsPrec p x = showString "<>" + +data XKeysymTable {-# STRICT #-} + = XKeysymTable [[Integer]] +instance Text(XKeysymTable) where + showsPrec p x = showString "<>" + +data XBitVec {-# STRICT #-} + = XBitVec [Int] +instance Text(XBitVec) where + showsPrec p x = showString "<>" + +data XPixarray {-# STRICT #-} + = XPixarray [[Integer]] +instance Text(XPixarray) where + showsPrec p x = showString "<>" + +data XByteVec {-# STRICT #-} + = XByteVec [Int] +instance Text(XByteVec) where + showsPrec p x = showString "<>" + + +data XAtom {-# STRICT #-} + = XAtom String + --deriving (Printers) + +data XProperty {-#STRICT #-} + = XProperty [Integer] -- data + XAtom -- type + Int -- format + --deriving (Printers) + +data XPixel {-# STRICT #-} + = XPixel Integer + --deriving (Printers) + +data XDrawable {-# STRICT #-} + = XDrawWindow XWindow + | XDrawPixmap XPixmap + --deriving (Printers) + +data XTime {-# STRICT #-} + = XTime Integer + --deriving (Printers) + +data XSwitch = XOn + | XOff + --deriving (Printers) + +data XWindowPlace = XTopPlace + | XBottomPlace + --deriving (Printers) + +data XEventMode = XNormalMode + | XGrabMode + | XUngrabMode + | XWhileGrabbedMode + --deriving (Printers) + +data XEventKind = XAncestorKind + | XVirtualKind + | XInferiorKind + | XNonlinearKind + | XNonlinearVirtualKind + | XPointerKind + | XPointerRootKind + | XNoneKind + --deriving (Printers) + +data XWindowVisibility = XUnobscured + | XPartiallyObscured + | XFullyObscured + --deriving (Printers) + +data XWindowStackMode = XStackAbove + | XStackBelow + | XStackTopIf + | XStackBottomIf + | XStackOpposite + --deriving (Printers) + +data XPropertyState = XNewValueProperty + | XDeletedProperty + --deriving (Printers) + +data XMapReqType = XModifierMapping + | XKeyboardMapping + | XPointerMapping + --deriving (Printers) + +data XGraphFun {-# STRICT #-} + = XGraphFun Int -- major opcode + Int -- minor opcode + --deriving (Printers) + +data XEvent {-# STRICT #-} + = XEvent XEventType + [XEventSlot] + +data XEventType = XKeyPressEvent + | XKeyReleaseEvent + | XButtonPressEvent + | XButtonReleaseEvent + | XMotionNotifyEvent + | XEnterNotifyEvent + | XLeaveNotifyEvent + | XFocusInEvent + | XFocusOutEvent + | XKeymapNotifyEvent + | XMappingNotifyEvent + | XExposureEvent + | XGraphicsExposureEvent + | XNoExposureEvent + | XCirculateNotifyEvent + | XConfigureNotifyEvent + | XCreateNotifyEvent + | XDestroyNotifyEvent + | XGravityNotifyEvent + | XMapNotifyEvent + | XReparentNotifyEvent + | XUnmapNotifyEvent + | XVisibilityNotifyEvent + | XCirculateRequestEvent + | XColormapNotifyEvent + | XConfigureRequestEvent + | XMapRequestEvent + | XResizeRequestEvent + | XClientMessageEvent + | XPropertyNotifyEvent + | XSelectionClearEvent + | XSelectionNotifyEvent + | XSelectionRequestEvent + | XOtherEvents + --deriving Printers + +data XEventSlot {-# STRICT #-} + = XEventWindow XWindow + | XEventEventWindow XWindow + | XEventCode Int + | XEventPos XPoint + | XEventState XStateMask + | XEventTime XTime + | XEventRoot XWindow + | XEventRootPos XPoint + | XEventChild (XMaybe XWindow) + | XEventSameScreenP Bool + | XEventHintP Bool + | XEventMode XEventMode + | XEventKind XEventKind + | XEventFocusP Bool + | XEventKeymap XBitVec + | XEventRequest XMapReqType + | XEventStart Int + | XEventCount Int + | XEventRect XRect + | XEventDrawable XDrawable + | XEventXGraphFun XGraphFun + | XEventPlace XWindowPlace + | XEventBorderWidth Int + | XEventAboveSibling (XMaybe XWindow) + | XEventOverrideRedirectP Bool + | XEventParent XWindow + | XEventConfigureP Bool + | XEventVisibility XWindowVisibility + | XEventNewP Bool + | XEventInstalledP Bool + | XEventStackMode XWindowStackMode + | XEventValueMask Int + | XEventSize XSize + | XEventMessage XProperty + | XEventPropertyState XPropertyState + | XEventAtom XAtom + | XEventSelection XAtom + | XEventTarget XAtom + | XEventProperty (XMaybe XAtom) + | XEventRequestor XWindow + --deriving Printers + +data XEventMask {-# STRICT #-} + = XEventMask [XEventMaskKey] + --deriving (Printers) + +data XEventMaskKey + = XButton1Motion + | XButton2Motion + | XButton3Motion + | XButton4Motion + | XButton5Motion + | XButtonMotion + | XButtonPress + | XButtonRelease + | XColormapChange + | XEnterWindow + | XExposure + | XFocusChange + | XKeyPress + | XKeyRelease + | XKeymapState + | XLeaveWindow + | XOwnerGrabButton + | XPointerMotion + | XPointerMotionHint + | XPropertyChange + | XResizeRedirect + | XStructureNotify + | XSubstructureRedirect + | XVisibilityChange + --deriving (Printers) + +data XStateMask {-# STRICT #-} + = XStateMask [XStateMaskKey] + --deriving (Printers) + +data XStateMaskKey + = XShift + | XLock + | XControl + | XMod1 + | XMod2 + | XMod3 + | XMod4 + | XMod5 + | XButton1 + | XButton2 + | XButton3 + | XButton4 + | XButton5 + --deriving (Printers) + +data XWinAttribute {-# STRICT #-} + = XWinBackground XPixel + | XWinEventMask XEventMask + | XWinDepth Int + | XWinBorderWidth Int + | XWinClass XWindowClass + | XWinVisual Int + | XWinBorder XFillContent + | XWinBackingStore XBackingStore + | XWinBackingPlanes XPixel + | XWinBackingPixel XPixel + | XWinSaveUnder XSwitch + | XWinDoNotPropagateMask XEventMask + | XWinOverrideRedirect XSwitch + | XWinColormap XColormap + | XWinCursor XCursor + --deriving (Printers) + +data XGCAttribute {-# STRICT #-} + = XGCArcMode XArcMode + | XGCBackground XPixel + | XGCCapStyle XCapStyle + | XGCClipMask XClipMask + | XGCClipOrigin XPoint + | XGCDashOffset Int + | XGCDashes [Int] + | XGCExposures XSwitch + | XGCFillRule XFillRule + | XGCFillStyle XFillStyle + | XGCFont XFont + | XGCForeground XPixel + | XGCFunction XFunction + | XGCJoinStyle XJoinStyle + | XGCLineStyle XLineStyle + | XGCLineWidth Int + | XGCPlaneMask XPixel + | XGCStipple XPixmap + | XGCSubwindowMode XSubwindowMode + | XGCTile XPixmap + | XGCTileOrigin XPoint + --deriving (Printers) + +data XImAttribute {-# STRICT #-} + = XImBitLsbFirstP Bool + | XImBitsPerPixel Int + | XImBlueMask XPixel + | XImByteLsbFirstP Bool + | XImBytesPerLine Int + | XImData XImageData + | XImDepth Int + | XImFormat XImageFormat + | XImGreenMask XPixel + | XImSize XSize + | XImName String + | XImRedMask XPixel + | XImHotSpot XPoint + --deriving (Printers) + +data XGrabAttribute {-# STRICT #-} + = XGrabOwnerP Bool + | XGrabSyncPointerP Bool + | XGrabSyncKeyboardP Bool + | XGrabConfineTo XWindow + | XGrabCursor XCursor + --deriving (Printers) + +data XArcMode = XChord + | XPieSlice + --deriving (Printers) + +data XCapStyle = XButt + | XNotLast + | XProjecting + | XRound + --deriving (Printers) + +data XClipMask {-# STRICT #-} + = XClipMaskPixmap XPixmap + | XClipMaskRects [XRect] + | XClipMaskNone + --deriving (Printers) + +data XFillRule = XFillEvenOdd + | XFillWinding + --deriving (Printers) + +data XFillStyle = XFillOpaqueStippled + | XFillSolid + | XFillStippled + | XFillTiled + --deriving (Printers) + +data XFunction = XBoole1 + | XBoole2 + | XBooleAndC1 + | XBooleAndC2 + | XBooleAnd + | XBooleC1 + | XBooleC2 + | XBooleClr + | XBooleEqv + | XBooleIor + | XBooleNand + | XBooleNor + | XBooleOrc1 + | XBooleOrc2 + | XBooleSet + | XBooleXor + --deriving (Printers) + +data XJoinStyle = XJoinBevel + | XJoinMiter + | XJoinRound + --deriving (Printers) + +data XLineStyle = XLineSolid + | XLineDoubleDash + | XLineOnOffDash + --deriving (Printers) + +data XSubwindowMode = XClipByChildren + | XIncludeInferiors + --deriving (Printers) + +-- BASIC GEOMETRY + +data XPoint {-# STRICT #-} = XPoint Int Int -- x,y + --deriving (Printers) + +data XSize {-# STRICT #-} = XSize Int Int -- width, height + --deriving (Printers) + +data XRect {-# STRICT #-} = XRect Int Int Int Int -- x, y, width, height + --deriving (Printers) + +data XArc {-# STRICT #-} = XArc Int Int Int Int Float Float + --deriving (Printers) -- x, y, width, height, angle1, angle2 + +data XBitmapFormat {-# STRICT #-} = XBitmapFormat Int Int Bool + --deriving (Printers) -- unit, pad, lsb-first-p + +data XByteOrder = XLsbFirst + | XMsbFirst + --deriving (Printers) + +data XPixmapFormat {-# STRICT #-} = XPixmapFormat Int Int Int + --deriving (Printers) -- depth, bits-per-pixel, scanline-pad + +data XVisualInfo {-# STRICT #-} = XVisualInfo + Int -- id + XVisualClass -- class + XPixel -- red-mask + XPixel -- green-mask + XPixel -- blue-mask + Int -- bits-per-rgb + Int -- colormap-entries + --deriving (Printers) + +data XVisualClass = XDirectColor + | XGrayScale + | XPseudoColor + | XStaticColor + | XStaticGray + | XTrueColor + --deriving (Printers) + +data XFillContent {-# STRICT #-} + = XFillPixel XPixel + | XFillPixmap XPixmap + | XFillNone + | XFillParentRelative + | XFillCopy + --deriving (Printers) + +data XBackingStore = XAlwaysBackStore + | XNeverBackStore + | XBackStoreWhenMapped + | XBackStoreNotUseful + --deriving (Printers) + +data XGravity = XForget + | XStatic + | XCenter + | XEast + | XNorth + | XNorthEast + | XNorthWest + | XSouth + | XSouthEast + | XSouthWest + | XWest + --deriving (Printers) + +data XWindowClass = XInputOutput + | XInputOnly + --deriving (Printers) + +data XMapState = XUnmapped + | XUnviewable + | XViewable + --deriving (Printers) + +data XImageData {-# STRICT #-} + = XBitmapData [XBitmap] + | XPixarrayData XPixarray + | XByteVecData XByteVec + --deriving (Printers) + +data XImageFormat = XXyPixmapImage + | XZPixmapImage + | XBitmapImage + --deriving (Printers) + +data XImageType = XImageX + | XImageXy + | XImageZ + --deriving (Printers) + +data XDrawDirection = XLeftToRight + | XRightToLeft + --deriving (Printers) + +data XColor {-# STRICT #-} = XColor Float Float Float + --deriving (Printers) + +data XInputFocus {-# STRICT #-} + = XFocusWindow XWindow + | XFocusNone + | XFocusPointerRoot + | XFocusParent + --deriving (Printers) + +data XGrabStatus = XAlreadyGrabbed + | XFrozen + | XInvalidTime + | XNotViewable + | XSuccess + --deriving (Printers) + + +data XKeysym {-# STRICT #-} = XKeysym Integer + --deriving (Printers) + + +data XCloseDownMode = XDestroy + | XRetainPermanent + | XRetainTemporary + --deriving (Printers) + +data XScreenSaver {-# STRICT #-} = XScreenSaver Int Int Bool Bool + --deriving (Printers) + +{-# +ImportLispType ( + XMaybe (XSome ("not-null?", "identity", "identity"), + XNull ("null?", "'()")), + XError (XError ("cons-xerror", "x-error-string")), + XBitmap (XBitmap ("mk-bitmap", "sel-bitmap")), + XKeysymTable (XKeysymTable ("mk-keysym-table", "sel-keysym-table")), + XBitVec (XBitVec ("mk-bitvec", "sel-bitvec")), + XPixarray (XPixarray ("mk-pixarray", "sel-pixarray")), + XByteVec (XByteVec ("mk-bytevec", "sel-bytevec")), + XAtom (XAtom ("mk-atom", "sel-atom")), + XProperty (XProperty ("mk-xproperty", "sel-xproperty-data", + "sel-xproperty-type", "sel-xproperty-format")), + XDrawable (XDrawWindow ("xlib:window-p", "identity", "identity"), + XDrawPixmap ("xlib:pixmap-p", "identity", "identity")), + XSwitch ( XOn(":on"), XOff(":off")), + XWindowPlace (XTopPlace (":top"), XBottomPlace (":bottom")), + XEventMode (XNormalMode (":normal"), + XGrabMode (":grab"), + XUngrabMode (":ungrab"), + XWhileGrabbedMode (":while-grabbed")), + XEventKind (XAncestorKind (":ancestor"), + XVirtualKind (":virtual"), + XInferiorKind (":inferior"), + XNonlinearKind (":nonlinear"), + XNonlinearVirtualKind (":nonlinear-virtual"), + XPointerKind (":pointer"), + XPointerRootKind (":pointer-root"), + XNoneKind (":none")), + XWindowVisibility (XUnobscured (":unobscured"), + XPartiallyObscured (":partially-obscured"), + XFullyObscured (":fully-obscured")), + XWindowStackMode (XStackAbove (":above"), + XStackBelow (":below"), + XStackTopIf (":top-if"), + XStackBottomIf (":bottom-if"), + XStackOpposite (":opposite")), + XPropertyState (XNewValueProperty (":new-value"), + XDeletedProperty (":deleted")), + XMapReqType (XModifierMapping (":modifier"), + XKeyboardMapping (":keyboard"), + XPointerMapping (":pointer")), + XGraphFun (XGraphFun ("cons", "car", "cdr")), + XEvent (XEvent ("mk-event", "sel-event-type", "sel-event-slots")), + XEventType (XKeyPressEvent (":key-press"), + XKeyReleaseEvent (":key-release"), + XButtonPressEvent (":button-press"), + XButtonReleaseEvent (":button-release"), + XMotionNotifyEvent (":motion-notify"), + XEnterNotifyEvent (":enter-notify"), + XLeaveNotifyEvent (":leave-notify"), + XFocusInEvent (":focus-in"), + XFocusOutEvent (":focus-out"), + XKeymapNotifyEvent (":keymap-notify"), + XMappingNotifyEvent (":mapping-notify"), + XExposureEvent (":exposure"), + XGraphicsExposureEvent (":graphics-exposure"), + XNoExposureEvent (":no-exposure"), + XCirculateNotifyEvent (":circulate-notify"), + XConfigureNotifyEvent (":configure-notify"), + XCreateNotifyEvent (":create-notify"), + XDestroyNotifyEvent (":destroy-notify"), + XGravityNotifyEvent (":gravity-notify"), + XMapNotifyEvent (":map-notify"), + XReparentNotifyEvent (":reparent-notify"), + XUnmapNotifyEvent (":unmap-notify"), + XVisibilityNotifyEvent (":visibility-notify"), + XCirculateRequestEvent (":circulate-notify"), + XColormapNotifyEvent (":colormap-notify"), + XConfigureRequestEvent (":configure-request"), + XMapRequestEvent (":map-request"), + XResizeRequestEvent (":resize-request"), + XClientMessageEvent (":client-message"), + XPropertyNotifyEvent (":property-notify"), + XSelectionClearEvent (":selection-clear"), + XSelectionNotifyEvent (":selection-notify"), + XSelectionRequestEvent (":selection-request"), + XOtherEvents (":others")), + XEventSlot (XEventWindow ("is-window", "mk-window", "keyword-val"), + XEventEventWindow + ("is-event-window", "mk-event-window", "keyword-val"), + XEventCode ("is-code", "mk-code", "keyword-val"), + XEventPos ("is-pos", "mk-pos", "keyword-val"), + XEventState ("is-state", "mk-state", "keyword-val"), + XEventTime ("is-time", "mk-time", "keyword-val"), + XEventRoot ("is-root", "mk-root", "keyword-val"), + XEventRootPos ("is-root-pos", "mk-root-pos", "keyword-val"), + XEventChild ("is-child", "mk-child", "keyword-val"), + XEventSameScreenP + ("is-same-screen-p", "mk-same-screen-p", "keyword-val"), + XEventHintP ("is-hint-p", "mk-hint-p", "keyword-val"), + XEventMode ("is-mode", "mk-mode", "keyword-val"), + XEventKind ("is-kind", "mk-kind", "keyword-val"), + XEventFocusP ("is-focus-p", "mk-focus-p", "keyword-val"), + XEventKeymap ("is-keymap", "mk-keymap", "keyword-val"), + XEventRequest ("is-request", "mk-request", "keyword-val"), + XEventStart ("is-start", "mk-start", "keyword-val"), + XEventCount ("is-count", "mk-count", "keyword-val"), + XEventRect ("is-rect", "mk-rect", "keyword-val"), + XEventDrawable ("is-drawable", "mk-drawable", "keyword-val"), + XEventXGraphFun ("is-graph-fun", "mk-graph-fun", "keyword-val"), + XEventPlace ("is-place", "mk-place", "keyword-val"), + XEventBorderWidth + ("is-border-width", "mk-border-width", "keyword-val"), + XEventAboveSibling + ("is-above-sibling", "mk-above-sibling", "keyword-val"), + XEventOverrideRedirectP + ("is-override-redirect-p", "mk-override-redirect-p", "keyword-val"), + XEventParent ("is-parent", "mk-parent", "keyword-val"), + XEventConfigureP ("is-configure-p", "mk-configure-p", "keyword-val"), + XEventVisibility ("is-visibility", "mk-visibility", "keyword-val"), + XEventNewP ("is-new-p", "mk-new-p", "keyword-val"), + XEventInstalledP ("is-installed-p", "mk-installed-p", "keyword-val"), + XEventStackMode ("is-stack-mode", "mk-stack-mode", "keyword-val"), + XEventValueMask ("is-value-mask", "mk-value-mask", "keyword-val"), + XEventSize ("is-size", "mk-size", "keyword-val"), + XEventMessage ("is-message", "mk-message", "keyword-val"), + XEventPropertyState + ("is-property-state", "mk-property-state", "keyword-val"), + XEventAtom ("is-atom", "mk-atom", "keyword-val"), + XEventSelection ("is-selection", "mk-selection", "keyword-val"), + XEventTarget ("is-target", "mk-target", "keyword-val"), + XEventProperty ("is-property", "mk-property", "keyword-val"), + XEventRequestor ("is-requestor", "mk-requestor", "keyword-val")), + XEventMask (XEventMask ("x-make-event-mask", "x-event-mask-key-list")), + XEventMaskKey (XButton1Motion (":button-1-motion"), + XButton2Motion (":button-2-motion"), + XButton3Motion (":button-3-motion"), + XButton4Motion (":button-4-motion"), + XButton5Motion (":button-5-motion"), + XButtonMotion (":button-motion"), + XButtonPress (":button-press"), + XButtonRelease (":button-release"), + XColormapChange (":colormap-change"), + XEnterWindow (":enter-window"), + XExposure (":exposure"), + XFocusChange (":focus-change"), + XKeyPress (":key-press"), + XKeyRelease (":key-release"), + XKeymapState (":keymap-state"), + XLeaveWindow (":leave-window"), + XOwnerGrabButton (":owner-grab-button"), + XPointerMotion (":pointer-motion"), + XPointerMotionHint (":pointer-motion-hint"), + XPropertyChange (":property-change"), + XResizeRedirect (":resize-redirect"), + XStructureNotify (":structure-notify"), + XSubstructureRedirect (":substructure-notify"), + XVisibilityChange (":visibility-change")), + XStateMask (XStateMask ("x-make-state-mask", "x-state-mask-key-list")), + XStateMaskKey (XShift (":shift"), + XLock (":lock"), + XControl (":control"), + XMod1 (":mod-1"), + XMod2 (":mod-2"), + XMod3 (":mod-3"), + XMod4 (":mod-4"), + XMod5 (":mod-5"), + XButton1 (":button-1"), + XButton2 (":button-2"), + XButton3 (":button-3"), + XButton4 (":button-4"), + XButton5 (":button-5")), + XWinAttribute + (XWinBackground ("is-background","mk-background","keyword-val"), + XWinEventMask ("is-event-mask","mk-event-mask","keyword-val"), + XWinDepth ("is-depth","mk-depth","keyword-val"), + XWinBorderWidth ("is-border-width","mk-border-width","keyword-val"), + XWinClass ("is-class","mk-class","keyword-val"), + XWinVisual ("is-visual","mk-visual","keyword-val"), + XWinBorder ("is-border","mk-border","keyword-val"), + XWinBackingStore ("is-backing-store","mk-backing-store","keyword-val"), + XWinBackingPlanes ("is-backing-planes","mk-backing-planes","keyword-val"), + XWinBackingPixel ("is-backing-pixel","mk-backing-pixel","keyword-val"), + XWinSaveUnder ("is-save-under","mk-save-under","keyword-val"), + XWinDoNotPropagateMask ("is-do-not-propagate-mask", + "mk-do-not-propagate-mask","keyword-val"), + XWinOverrideRedirect("is-override-redirect", + "mk-override-redirect","keyword-val"), + XWinColormap ("is-colormap","mk-colormap","keyword-val"), + XWinCursor ("is-cursor","mk-cursor","keyword-val")), + XGCAttribute( + XGCArcMode ("is-arc-mode","mk-arc-mode","keyword-val"), + XGCBackground ("is-background","mk-background","keyword-val"), + XGCCapStyle ("is-cap-style","mk-cap-style","keyword-val"), + XGCClipMask ("is-clip-mask","mk-clip-mask","keyword-val"), + XGCClipOrigin ("is-clip-origin","mk-clip-origin","keyword-val"), + XGCDashOffset ("is-dash-offset","mk-dash-offset","keyword-val"), + XGCDashes ("is-dashes","mk-dashes","keyword-val"), + XGCExposures ("is-exposures","mk-exposures","keyword-val"), + XGCFillRule ("is-fill-rule","mk-fill-rule","keyword-val"), + XGCFillStyle ("is-fill-style","mk-fill-style","keyword-val"), + XGCFont ("is-font","mk-font","keyword-val"), + XGCForeground ("is-foreground","mk-foreground","keyword-val"), + XGCFunction ("is-function","mk-function","keyword-val"), + XGCJoinStyle ("is-join-style","mk-join-style","keyword-val"), + XGCLineStyle ("is-line-style","mk-line-style","keyword-val"), + XGCLineWidth ("is-line-width","mk-line-width","keyword-val"), + XGCPlaneMask ("is-plane-mask","mk-plane-mask","keyword-val"), + XGCStipple ("is-stipple","mk-stipple","keyword-val"), + XGCSubwindowMode ("is-subwindow-mode","mk-subwindow-mode","keyword-val"), + XGCTile ("is-tile","mk-tile","keyword-val"), + XGCTileOrigin ("is-tile-origin","mk-tile-origin","keyword-val")), + XImAttribute ( + XImBitLsbFirstP ("is-bit-lsb-first-p","mk-bit-lsb-first-p","keyword-val"), + XImBitsPerPixel ("is-bits-per-pixel","mk-bits-per-pixel","keyword-val"), + XImBlueMask ("is-blue-mask","mk-blue-mask","keyword-val"), + XImByteLsbFirstP ("is-byte-lsb-first-p","mk-byte-lsb-first-p","keyword-val"), + XImBytesPerLine ("is-bytes-per-line","mk-bytes-per-line","keyword-val"), + XImData ("is-data","mk-data","keyword-val"), + XImDepth ("is-depth","mk-depth","keyword-val"), + XImFormat ("is-format","mk-format","keyword-val"), + XImGreenMask ("is-green-mask","mk-green-mask","keyword-val"), + XImSize ("is-size","mk-size","keyword-val"), + XImName ("is-name","mk-name","keyword-val"), + XImRedMask ("is-red-mask","mk-red-mask","keyword-val"), + XImHotSpot ("is-hot-spot","mk-hot-spot","keyword-val")), + XGrabAttribute ( + XGrabOwnerP ("is-owner-p", "mk-owner-p", "keyword-val"), + XGrabSyncPointerP ("is-sync-pointer-p", "mk-sync-pointer-p", "keyword-val"), + XGrabSyncKeyboardP ("is-sync-keyboard-p", "mk-sync-keyboard-p", "keyword-val"), + XGrabConfineTo ("is-confine-to", "mk-confine-to", "keyword-val"), + XGrabCursor ("is-cursor", "mk-cursor", "keyword-val")), + XArcMode (XChord (":chord"), + XPieSlice (":pie-slice")), + XCapStyle (XButt (":butt"), + XNotLast (":not-last"), + XProjecting (":projecting"), + XRound (":round")), + XClipMask (XClipMaskPixmap ("xlib:pixmap-p","identity","identity"), + XClipMaskRects ("not-pixmap-and-list-p","mk-clip-mask-rects", + "sel-clip-mask-rects"), + XClipMaskNone ("null?", "()")), + XFillRule (XFillEvenOdd (":even-odd"), + XFillWinding (":winding")), + XFillStyle (XFillOpaqueStippled (":opaque-stippled"), + XFillSolid (":solid"), + XFillStippled (":stippled"), + XFillTiled (":tiled")), + XFunction (XBoole1 ("xlib::boole-1"), + XBoole2 ("xlib::boole-2"), + XBooleAndC1 ("xlib::boole-andc1"), + XBooleAndC2 ("xlib::boole-andc2"), + XBooleAnd ("xlib::boole-and"), + XBooleC1 ("xlib::boole-c1"), + XBooleC2 ("xlib::boole-c2"), + XBooleClr ("xlib::boole-clr"), + XBooleEqv ("xlib::boole-eqv"), + XBooleIor ("xlib::boole-ior"), + XBooleNand ("xlib::boole-nand"), + XBooleNor ("xlib::boole-nor"), + XBooleOrc1 ("xlib::boole-orc1"), + XBooleOrc2 ("xlib::boole-orc2"), + XBooleSet ("xlib::boole-set"), + XBooleXor ("xlib::boole-xor")), + XJoinStyle (XJoinBevel (":bevel"), + XJoinMiter (":miter"), + XJoinRound (":round")), + XLineStyle (XLineSolid (":solid"), + XLineDoubleDash (":double-dash"), + XLineOnOffDash (":on-off-dash")), + XSubwindowMode (XClipByChildren (":clip-by-children"), + XIncludeInferiors (":include-inferiors")), + XPoint(XPoint("mk-xpoint", "xpoint-x", "xpoint-y")), + XSize (XSize ("mk-xsize", "xsize-w", "xsize-h")), + XRect (XRect ("mk-xrect", "xrect-x", "xrect-y", "xrect-w", "xrect-h")), + XArc (XArc ("mk-xarc", "xarc-x", "xarc-y", "xarc-w", "xarc-h", + "xarc-a1", "xarc-a2")), + XBitmapFormat + (XBitmapFormat ("bitmap-format-p", "mk-bitmap-format", + "xlib:bitmap-format-unit", + "xlib:bitmap-format-pad", + "xlib:bitmap-format-lsb-first-p")), + XByteOrder (XLsbFirst (":lsbfirst"), + XMsbFirst (":msbfirst")), + XPixmapFormat (XPixmapFormat ("pixmap-format-p", "mk-pixmap-format", + "xlib:pixmap-format-depth", + "xlib:pixmap-format-bits-per-pixel", + "xlib:pixmap-format-scanline-pad")), + XVisualInfo + (XVisualInfo ( "visual-info-p", "mk-xvisual-info", + "xlib:visual-info-id", + "xlib:visual-info-class", + "xlib:visual-info-red-mask", + "xlib:visual-info-green-mask", + "xlib:visual-info-blue-mask", + "xlib:visual-info-bits-per-rgb", + "xlib:visual-info-colormap-entries")), + XVisualClass (XDirectColor (":direct-color"), + XGrayScale (":gray-scale"), + XPseudoColor (":pseudo-color"), + XStaticColor (":static-color"), + XStaticGray (":static-gray"), + XTrueColor (":true-color")), + XFillContent (XFillPixel ("is-fill-pixel", "identity","identity"), + XFillPixmap ("xlib:pixmap-p", "identity","identity"), + XFillNone (":none"), + XFillParentRelative (":parent-relative"), + XFillCopy (":copy")), + XBackingStore (XAlwaysBackStore (":always"), + XNeverBackStore (":never"), + XBackStoreWhenMapped (":when-mapped"), + XBackStoreNotUseful (":not-useful")), + XGravity (XForget (":forget"), + XStatic (":static"), + XCenter (":center"), + XEast (":east"), + XNorth (":north"), + XNorthEast (":north-east"), + XNorthWest (":north-west"), + XSouth (":south"), + XSouthEast (":south-east"), + XSouthWest (":south-west"), + XWest ("west")), + XWindowClass (XInputOutput (":input-output"), + XInputOnly (":input-only")), + XMapState (XUnmapped (":unmapped"), + XUnviewable (":unviewable"), + XViewable (":viewable")), + XImageData (XBitmapData ("bitmap-list-p", "haskell-list->list/identity", "list->haskell-list/identity"), + XPixarrayData ("pixarray-p", "identity", "identity"), + XByteVecData ("bytevec-p", "identity", "identity")), + XImageFormat (XXyPixmapImage (":xy-pixmap"), + XZPixmapImage (":z-pixmap"), + XBitmapImage (":bitmap")), + XImageType (XImageX ("'xlib:image-x"), + XImageXy ("'xlib:image-xy"), + XImageZ ("'xlib:image-z")), + XDrawDirection (XLeftToRight (":left-to-right"), + XRightToLeft (":right-to-left")), + XColor (XColor ("xlib:color-p", "mk-color", + "xlib:color-red", "xlib:color-green", "xlib:color-blue")), + XInputFocus (XFocusWindow ("xlib:window-p", "identity", "identity"), + XFocusNone (":none"), + XFocusPointerRoot (":pointer-root"), + XFocusParent (":parent")), + XGrabStatus (XAlreadyGrabbed (":already-grabbed"), + XFrozen (":frozen"), + XInvalidTime (":invalid-time"), + XSuccess (":success")), + XCloseDownMode (XDestroy (":destroy"), + XRetainPermanent (":retain-permanent"), + XRetainTemporary (":retain-temporary")), + XScreenSaver (XScreenSaver ("list", "car", "cadr", "caddr", "cadddr"))) + +#-} + diff --git a/progs/lib/X11/xlib.hu b/progs/lib/X11/xlib.hu new file mode 100644 index 0000000..b86b2ac --- /dev/null +++ b/progs/lib/X11/xlib.hu @@ -0,0 +1,5 @@ +:output $LIBRARYBIN/ +:stable +:o= all +xlib.hs +xlibprims.hu diff --git a/progs/lib/X11/xlibclx.scm b/progs/lib/X11/xlibclx.scm new file mode 100644 index 0000000..1f1fd6a --- /dev/null +++ b/progs/lib/X11/xlibclx.scm @@ -0,0 +1,1262 @@ +;;; xlibclx.scm -- Lisp support for Haskell/CLX interface + +;; general + +(define-syntax (nth-value n form) + (cond ((eqv? n 0) + `(values ,form)) + ((number? n) + (let ((temps '())) + (dotimes (i n) + (declare (ignorable i)) + (push (gensym) temps)) + `(multiple-value-bind ,(reverse temps) ,form + (declare (ignore ,@(reverse (cdr temps)))) + ,(car temps)))) + (else + `(lisp:nth ,n (lisp:multiple-value-list ,form))) + )) + + +(define-local-syntax (keywordify string) + `(lisp:intern ,string (lisp:find-package "KEYWORD"))) + +(define-local-syntax (xlibify string) + `(lisp:intern ,string (lisp:find-package "XLIB"))) + + + +;;; This is stuff to support slots that consist of a keyword/value +;;; pair. Note that the value is always unboxed. + +(define-syntax (make-keyword key value) + `(cons ,key ,value)) + +(define-syntax (is-keyword? x key) + `(eq? (car ,x) ,key)) + +(define-syntax (keyword-key x) `(car ,x)) +(define-syntax (keyword-val x) `(cdr ,x)) + +(define-syntax (define-keyword-constructor name) + (let* ((name-str (symbol->string name)) + (key (keywordify name-str)) + (is-name (string->symbol (string-append "IS-" name-str))) + (mk-name (string->symbol (string-append "MK-" name-str)))) + `(begin + (define (,mk-name x) (make-keyword ,key x)) + (define (,is-name x) (is-keyword? x ,key))) + )) + +(define-syntax (define-event-slot-finder slot) + (let* ((slot-str (symbol->string slot)) + (slot-key (keywordify slot-str)) + (fun (string->symbol (string-append "X-EVENT-" slot-str)))) + `(define (,fun event) (lookup-event-slot (cdr event) ,slot-key)))) + +(define (lookup-event-slot event key) + (if (null? event) + (error "non-existent event slot: ~A" key) + (if (eq? key (car event)) + (cadr event) + (lookup-event-slot (cddr event) key)))) + + +(define-syntax (define-attribute-setter entity attribute) + (let* ((entity-attr (string-append (symbol->string entity) + "-" + (symbol->string attribute))) + (fun-name (string->symbol (string-append "X-SET-" entity-attr))) + (xfun-name (xlibify entity-attr))) + `(define (,fun-name ,entity ,attribute) + (setf (,xfun-name ,entity) ,attribute)))) + +(define-syntax (make-h-tuple . args) + (let ((nargs (map (lambda (arg) `(box ,arg)) args))) + `(make-tuple ,@nargs))) + +;; type XError + +(define (cons-xerror x) + (declare (ignore x)) + (error "can't construct XError")) + +(define (x-error-string c) + (make-haskell-string (format '#f "~A" c))) + + +;;; The forces here are necessary because the thing being funcalled +;;; returns a data structure of type (IO a), and we need to do +;;; an IO a -> a transformation. + +#+lucid +(define (x-handle-error handler body) + (lisp:catch 'x-error-handle + (lcl:handler-bind ((lisp:error (mk-handler handler))) + (force (funcall body (box 'state)))))) + +#+(or cmu allegro lispworks) +(define (x-handle-error handler body) + (lisp:catch 'x-error-handle + (lisp:handler-bind ((lisp:error (mk-handler handler))) + (force (funcall body (box 'state)))))) + +#+akcl +(define (x-handle-error handler body) + (error "AKCL does not support HANDLER-BIND!")) + +(define (mk-handler handler) + (lambda (c) + (lisp:throw 'x-error-handle + (force (funcall handler + (box c) + (box 'state)))))) + +;; for type XMaybe + +(define (not-null? x) (not (null? x))) + + +;; For Bitmap, Pixarray, KeysymTable + +(define (array2->haskell-list a) + (let* ((dims (lisp:array-dimensions a)) + (i1max (car dims)) + (i2max (cadr dims))) + (declare (type fixnum i1max i2max)) + (do ((i1 (the fixnum (1- i1max)) (the fixnum (1- i1))) + (outer '())) + ((< i1 0) outer) + (declare (type fixnum i1)) + (setf outer + (cons + (box + (do ((i2 (the fixnum (1- i2max)) (the fixnum (1- i2))) + (inner '())) + ((< i2 0) inner) + (declare (type fixnum i2)) + (setf inner + (cons (box (lisp:aref a i1 i2)) + (box inner))))) + (box outer)))) + )) + + +;; Bitmap + +(define (mk-bitmap ll) + (let ((l (haskell-list->list #'haskell-list->list/identity ll))) + (lisp:make-array `(,(length l) , (length (car l))) + :element-type 'lisp:bit + :initial-contents l))) + +(define (sel-bitmap l) + (array2->haskell-list l)) + + +;; XKeysymTable + +(define (mk-keysym-table ll) + (let ((l (haskell-list->list #'haskell-list->list/identity ll))) + (lisp:make-array `(,(length l) , (length (car l))) + :element-type 'xlib:card32 + :initial-contents l))) + +(define (sel-keysym-table l) + (array2->haskell-list l)) + +;; XPixarray + +(define (mk-pixarray ll) + (let ((l (haskell-list->list #'haskell-list->list/identity ll))) + (let* ((max-num (find-max l)) + (pix-type (cond ((<= max-num 1) 'lisp:bit) + ((<= max-num 15) '(lisp:unsigned-byte 4)) + ((<= max-num 255) 'xlib:card8) + ((<= max-num 65535) 'xlib:card16) + (else 'xlib:card32)))) + (declare (type integer max-num)) + (lisp:make-array `(,(length l) , (length (car l))) + :element-type pix-type + :initial-contents l)))) + +(define (find-max l) + (let ((max 0)) + (dolist (ll l) + (dolist (lll ll) + (when (> (the integer lll) (the integer max)) + (setf max lll)))) + max)) + +(define (sel-pixarray l) + (array2->haskell-list l)) + + + + +;;; Can't use mumble vector primitives on arrays of specialized types! + +(define (array1->haskell-list a) + (declare (type lisp:vector a)) + (let ((imax (lisp:length a))) + (declare (type fixnum imax)) + (do ((i (the fixnum (1- imax)) (the fixnum (1- i))) + (result '())) + ((< i 0) result) + (declare (type fixnum i)) + (setf result + (cons (box (lisp:aref a i)) + (box result)))))) + +;; BitVec + +(define (mk-bitvec ll) + (let ((l (haskell-list->list/identity ll))) + (lisp:make-array `(,(length l)) :element-type 'lisp:bit + :initial-contents l))) + +(define (sel-bitvec l) + (array1->haskell-list l)) + +;; ByteVec + +(define (mk-bytevec ll) + (let ((l (haskell-list->list/identity ll))) + (lisp:make-array `(,(length l)) :element-type 'xlib:card8 + :initial-contents l))) + +(define (sel-bytevec l) + (array1->haskell-list l)) + + +;; XAtom +(define (mk-atom name) + (keywordify (haskell-string->string name))) + +(define (sel-atom atom) + (make-haskell-string (symbol->string atom))) + +;; XProperty +;;; watch out for name conflict with :property keyword stuff +(define (mk-xproperty d ty f) (list (haskell-list->list/identity d) ty f)) +(define (sel-xproperty-data p) (list->haskell-list/identity (car p))) +(define (sel-xproperty-type p) (cadr p)) +(define (sel-xproperty-format p) (caddr p)) + +(define (mk-event type slots) + (cons type (slots->keywords (haskell-list->list/identity slots)))) + +(define (sel-event-type event) (car event)) + +(define (sel-event-slots event) + (list->haskell-list/identity (keywords->slots (car event) (cdr event) event))) + +;; XEventSlot + +(define-keyword-constructor window) +(define-keyword-constructor event-window) +(define-keyword-constructor code) +(define-keyword-constructor pos) +(define-keyword-constructor state) +(define-keyword-constructor time) +(define-keyword-constructor root) +(define-keyword-constructor root-pos) +(define-keyword-constructor child) +(define-keyword-constructor same-screen-p) +(define-keyword-constructor hint-p) +(define-keyword-constructor mode) +(define-keyword-constructor kind) +(define-keyword-constructor focus-p) +(define-keyword-constructor keymap) +(define-keyword-constructor request) +(define-keyword-constructor start) +(define-keyword-constructor count) +(define-keyword-constructor rect) +(define-keyword-constructor drawable) +(define-keyword-constructor graph-fun) +(define-keyword-constructor place) +(define-keyword-constructor border-width) +(define-keyword-constructor above-sibling) +(define-keyword-constructor override-redirect-p) +(define-keyword-constructor parent) +(define-keyword-constructor configure-p) +(define-keyword-constructor visibility) +(define-keyword-constructor new-p) +(define-keyword-constructor installed-p) +(define-keyword-constructor stack-mode) +(define-keyword-constructor value-mask) +(define-keyword-constructor size) +(define-keyword-constructor message) +(define-keyword-constructor property-state) +(define-keyword-constructor atom) +(define-keyword-constructor selection) +(define-keyword-constructor target) +(define-keyword-constructor property) +(define-keyword-constructor requestor) + +(define-event-slot-finder window) +(define-event-slot-finder event-window) +(define-event-slot-finder code) +(define-event-slot-finder x) +(define-event-slot-finder y) +(define-event-slot-finder state) +(define-event-slot-finder time) +(define-event-slot-finder root) +(define-event-slot-finder root-x) +(define-event-slot-finder root-y) +(define-event-slot-finder child) +(define-event-slot-finder same-screen-p) +(define-event-slot-finder hint-p) +(define-event-slot-finder mode) +(define-event-slot-finder kind) +(define-event-slot-finder focus-p) +(define-event-slot-finder keymap) +(define-event-slot-finder request) +(define-event-slot-finder start) +(define-event-slot-finder count) +(define-event-slot-finder width) +(define-event-slot-finder height) +(define-event-slot-finder drawable) +(define-event-slot-finder major) +(define-event-slot-finder minor) +(define-event-slot-finder place) +(define-event-slot-finder border-width) +(define-event-slot-finder above-sibling) +(define-event-slot-finder override-redirect-p) +(define-event-slot-finder parent) +(define-event-slot-finder configure-p) +(define-event-slot-finder new-p) +(define-event-slot-finder installed-p) +(define-event-slot-finder stack-mode) +(define-event-slot-finder value-mask) +(define-event-slot-finder data) +(define-event-slot-finder type) +(define-event-slot-finder format) +(define-event-slot-finder atom) +(define-event-slot-finder selection) +(define-event-slot-finder target) +(define-event-slot-finder property) +(define-event-slot-finder requestor) + +(define (x-event-pos event) (mk-xpoint (x-event-x event) (x-event-y event))) + +(define (x-event-root-pos event) + (mk-xpoint (x-event-root-x event) (x-event-root-y event))) + +(define (x-event-size event) + (mk-xsize (x-event-width event) (x-event-height event))) + +(define (x-event-rect event) + (mk-xrect (x-event-x event) (x-event-y event) + (x-event-width event) (x-event-height event))) + +(define (x-event-graph-fun event) + (cons (x-event-major event) (x-event-minor event))) + +(define (x-event-message event) + (list (sequence->list (x-event-data event)) + (x-event-type event) + (x-event-format event))) + + +;; XEventMask + +(define (x-make-event-mask keys) + (apply (function xlib:make-event-mask) (haskell-list->list/identity keys))) + +(define (x-event-mask-key-list mask) + (list->haskell-list/identity (xlib:make-event-keys mask))) + +;; XStateMask + +(define (x-make-state-mask keys) + (apply (function xlib:make-state-mask) (haskell-list->list/identity keys))) + +(define (x-state-mask-key-list mask) + (list->haskell-list/identity (xlib:make-state-keys mask))) + + +(define-keyword-constructor background) +(define-keyword-constructor foreground) +(define-keyword-constructor event-mask) +(define-keyword-constructor depth) +(define-keyword-constructor border-width) +(define-keyword-constructor class) +(define-keyword-constructor visual) +(define-keyword-constructor border) +(define-keyword-constructor backing-store) +(define-keyword-constructor backing-planes) +(define-keyword-constructor backing-pixel) +(define-keyword-constructor save-under) +(define-keyword-constructor do-not-propagate-mask) +(define-keyword-constructor override-redirect) +(define-keyword-constructor colormap) +(define-keyword-constructor cursor) + +(define-keyword-constructor arc-mode) +(define-keyword-constructor cap-style) +(define-keyword-constructor clip-mask) +(define-keyword-constructor clip-origin) +(define-keyword-constructor dash-offset) +(define-keyword-constructor dashes) +(define-keyword-constructor exposures) +(define-keyword-constructor fill-rule) +(define-keyword-constructor fill-style) +(define-keyword-constructor font) +(define-keyword-constructor function) +(define-keyword-constructor join-style) +(define-keyword-constructor line-style) +(define-keyword-constructor line-width) +(define-keyword-constructor plane-mask) +(define-keyword-constructor stipple) +(define-keyword-constructor subwindow-mode) +(define-keyword-constructor tile) +(define-keyword-constructor tile-origin) + +(define-keyword-constructor bit-lsb-first-p) +(define-keyword-constructor bits-per-pixel) +(define-keyword-constructor blue-mask) +(define-keyword-constructor byte-lsb-first-p) +(define-keyword-constructor bytes-per-line) +(define-keyword-constructor data) +(define-keyword-constructor format) +(define-keyword-constructor green-mask) +(define-keyword-constructor size) +(define-keyword-constructor name) +(define-keyword-constructor red-mask) +(define-keyword-constructor hot-spot) + + +(define-keyword-constructor owner-p) +(define-keyword-constructor sync-pointer-p) +(define-keyword-constructor sync-keyboard-p) +(define-keyword-constructor confine-to) + + +;; XClipMask + +(define (not-pixmap-and-list-p x) + (and (pair? x) (not (xlib:pixmap-p x)))) +(define (mk-clip-mask-rects rects) + (rects->point-seq (haskell-list->list/identity rects))) +(define (sel-clip-mask-rects point-seq) + (list->haskell-list/identity (point-seq->rects point-seq))) + +;; XPoint + +(define (mk-xpoint x y) (cons x y)) +(define (xpoint-x x) (car x)) +(define (xpoint-y x) (cdr x)) + +;; XSize + +(define (mk-xsize x y) (cons x y)) +(define (xsize-w x) (car x)) +(define (xsize-h x) (cdr x)) + +;; XRect +(define (mk-xrect x y w h) (vector x y w h)) +(define (xrect-x x) (vector-ref x 0)) +(define (xrect-y x) (vector-ref x 1)) +(define (xrect-w x) (vector-ref x 2)) +(define (xrect-h x) (vector-ref x 3)) + +;; XArc + +(define (mk-xarc x y w h a1 a2) (vector x y w h a1 a2)) + +(define (xarc-x x) (vector-ref x 0)) +(define (xarc-y x) (vector-ref x 1)) +(define (xarc-w x) (vector-ref x 2)) +(define (xarc-h x) (vector-ref x 3)) +(define (xarc-a1 x) (vector-ref x 4)) +(define (xarc-a2 x) (vector-ref x 5)) + +;; BitmapFormat + +(define (mk-bitmap-format u p l) + (xlib::make-bitmap-format :unit u :pad p :lsb-first-p l)) + +;; PixmapFormat + +(define (mk-pixmap-format u p l) + (xlib::make-pixmap-format :depth u :bits-per-pixel p :scanline-pad l)) + +;; XVisualInfo + +(define (mk-xvisual-info id cl rm gm bm bs es) + (xlib::make-visual-info :id id :class cl :red-mask rm :green-mask gm + :blue-mask bm :bits-per-rgb bs :colormap-entries es)) + +;; XFillContent + +(define (is-fill-pixel x) (not (or (xlib:pixmap-p x) (symbol? x)))) + +;; XBackingStore + +;; XImageData + +(define (bitmap-list-p x) (pair? x)) +(define (pixarray-p x) (and (not (pair? x)) (eq? (lisp:array-rank x) 2))) +(define (bytevec-p x) (and (not (pair? x)) (eq? (lisp:array-rank x) 1))) + +;; XColor +(define (mk-color r g b) + (xlib:make-color :red r :green g :blue b)) + + +(define (x-print x) + (print x)) + +(define (x-set-event-mask-key mask key-sym) + (lisp:logior mask (xlib:make-event-mask key-sym))) + +(define (x-clear-event-mask-key mask key-sym) + (lisp:logand mask (lisp:lognot (xlib:make-event-mask key-sym)))) + + +(define (x-test-event-mask-key mask key-sym) + (if (eqv? 0 (lisp:logand mask (xlib:make-event-mask key-sym))) '#f '#t)) + +(define (x-set-state-mask-key mask key-sym) + (lisp:logior mask (xlib:make-state-mask key-sym))) + +(define (x-clear-state-mask-key mask key-sym) + (lisp:logand mask (lisp:lognot (xlib:make-state-mask key-sym)))) + +(define (x-test-state-mask-key mask key-sym) + (if (eqv? 0 (lisp:logand mask (xlib:make-state-mask key-sym))) '#f '#t)) + + +;;; Display is a string of the format name:d.s +;;; ignore s; if d is omitted, default it to zero. + +(define (x-open-display display) + (let* ((end (string-length display)) + (colon (or (string-position #\: display 0 end) end)) + (dot (or (string-position #\. display colon end) end))) + (declare (type fixnum end colon dot)) + (xlib:open-display + (substring display 0 colon) + :display (if (eqv? colon dot) + 0 + (string->number (substring display (1+ colon) dot)))))) + +(define (x-set-display-error-handler display error-fun) + (declare (ignore display error-fun)) + (error "not implemented")) + +(define (x-set-display-after-function display after-fun) + (declare (ignore display after-fun)) + (error "not implemented")) + +(define (x-screen-depths screen) + (let ((depths (xlib:screen-depths screen))) + (map (lambda (l) (make-h-tuple (car l) (list->haskell-list/identity (cdr l)))) + depths))) + +(define (x-screen-size screen) + (mk-xsize (xlib:screen-width screen) (xlib:screen-height screen))) + +(define (x-screen-mmsize screen) + (mk-xsize (xlib:screen-width-in-millimeters screen) + (xlib:screen-height-in-millimeters screen))) + +(define (x-create-window parent rect attrs) + (apply (function XLIB:CREATE-WINDOW) + `(:parent ,parent :x ,(xrect-x rect) :y ,(xrect-y rect) + :width ,(xrect-w rect) :height ,(xrect-h rect) + ,@(attrs->keywords attrs)))) + +(define-attribute-setter drawable border-width) + +(define (x-drawable-size drawable) + (mk-xsize (xlib:drawable-width drawable) (xlib:drawable-height drawable))) + +(define (x-drawable-resize drawable size) + (setf (xlib:drawable-width drawable) (xsize-w size)) + (setf (xlib:drawable-height drawable) (xsize-h size))) + +(define (x-window-pos window) + (mk-xpoint (xlib:drawable-x window) (xlib:drawable-y window))) + +(define (x-window-move window point) + (setf (xlib:drawable-x window) (xpoint-x point)) + (setf (xlib:drawable-y window) (xpoint-y point))) + +(define-attribute-setter window background) +(define-attribute-setter window backing-pixel) +(define-attribute-setter window backing-planes) +(define-attribute-setter window backing-store) +(define-attribute-setter window bit-gravity) +(define-attribute-setter window border) +(define-attribute-setter window colormap) + +(define (x-set-window-cursor window cursor) + (let ((val (if (null? cursor) :none cursor))) + (setf (xlib:window-cursor window) val))) + +(define-attribute-setter window do-not-propagate-mask) +(define-attribute-setter window event-mask) +(define-attribute-setter window gravity) +(define-attribute-setter window override-redirect) +(define-attribute-setter window priority) +(define-attribute-setter window save-under) + +(define (x-query-tree window) + (multiple-value-bind (children parent root) + (xlib:query-tree window) + (make-h-tuple (list->haskell-list/identity children) parent root))) + +(define (x-reparent-window window parent point) + (xlib:reparent-window window parent (xpoint-x point) (xpoint-y point))) + +(define (x-translate-coordinates source point dest) + (xlib:translate-coordinates source (xpoint-x point) (xpoint-y point) dest)) + +(define (x-create-pixmap size depth drawable) + (xlib:create-pixmap :width (xsize-w size) + :height (xsize-h size) + :depth depth + :drawable drawable)) + +(define (x-create-gcontext drawable attrs) + (apply (function XLIB:CREATE-GCONTEXT) + `(:drawable ,drawable ,@(attrs->keywords attrs)))) + +(define (x-update-gcontext gcontext attrs) + (do ((keys (attrs->keywords attrs) (cddr keys))) + ((null? keys)) + (x-update-gcontext-attr gcontext (car keys) (cadr keys)))) + +(define (x-update-gcontext-attr gcontext key attr) + (case key + (:arc-mode (setf (xlib:gcontext-arc-mode gcontext) attr)) + (:background (setf (xlib:gcontext-background gcontext) attr)) + (:cap-style (setf (xlib:gcontext-cap-style gcontext) attr)) + (:fill-style (setf (xlib:gcontext-fill-style gcontext) attr)) + (:clip-mask (setf (xlib:gcontext-clip-mask gcontext) attr)) + (:clip-x (setf (xlib:gcontext-clip-x gcontext) attr)) + (:clip-y (setf (xlib:gcontext-clip-y gcontext) attr)) + (:dash-offset (setf (xlib:gcontext-dash-offset gcontext) attr)) + (:dashes (setf (xlib:gcontext-dashes gcontext) attr)) + (:exposures (setf (xlib:gcontext-exposures gcontext) attr)) + (:fill-rule (setf (xlib:gcontext-fill-rule gcontext) attr)) + (:font (setf (xlib:gcontext-font gcontext) attr)) + (:foreground (setf (xlib:gcontext-foreground gcontext) attr)) +; (:function (setf (xlib:gcontext-function gcontext) attr)) + (:join-style (setf (xlib:gcontext-join-style gcontext) attr)) + (:line-style (setf (xlib:gcontext-line-style gcontext) attr)) +; (:line-width (setf (xlib:gcontext-line-width gcontext) attr)) +; (:plane-mask (setf (xlib:gcontext-plane-mask gcontext) attr)) +; (:stipple (setf (xlib:gcontext-stipple gcontext) attr)) + (:subwindow-mode (setf (xlib:gcontext-subwindow-mode gcontext) attr)) +; (:tile (setf (xlib:gcontext-tile gcontext) attr)) +; (:ts-x (setf (xlib:gcontext-ts-x gcontext) attr)) +; (:ts-y (setf (xlib:gcontext-ts-y gcontext) attr)) + (else (format '#t "Graphics context attribute ~A is not settable.~%" + key)))) + +(define (x-query-best-stipple dsize drawable) + (multiple-value-bind (w h) + (xlib:query-best-stipple (xsize-w dsize) (xsize-h dsize) drawable) + (mk-xsize w h))) + +(define (x-query-best-tile dsize drawable) + (multiple-value-bind (w h) + (xlib:query-best-tile (xsize-w dsize) (xsize-h dsize) drawable) + (mk-xsize w h))) + +(define (x-clear-area window rect exposures-p) + (xlib:clear-area window + :x (xrect-x rect) + :y (xrect-y rect) + :width (xrect-w rect) + :height (xrect-h rect) + :exposures-p exposures-p)) + +(define (x-copy-area src gcontext rect dest point) + (xlib:copy-area src + gcontext + (xrect-x rect) (xrect-y rect) + (xrect-w rect) (xrect-h rect) + dest + (xpoint-x point) (xpoint-y point))) + +(define (x-copy-plane src gcontext plane rect dest point) + (xlib:copy-plane src + gcontext + plane + (xrect-x rect) (xrect-y rect) + (xrect-w rect) (xrect-h rect) + dest + (xpoint-x point) (xpoint-y point))) + +(define (x-draw-point drawable gcontext point) + (xlib:draw-point drawable gcontext (xpoint-x point) (xpoint-y point))) + +(define (x-draw-points drawable gcontext points) + (xlib:draw-points drawable gcontext (points->point-seq points))) + +(define (points->point-seq points) + (if (null? points) + '() + (let ((point (car points))) + (lisp:list* (xpoint-x point) + (xpoint-y point) + (points->point-seq (cdr points)))))) + +(define (segments->point-seq segments) + (if (null? segments) + '() + (let* ((first-pair (car segments)) + (point-1 (force (tuple-select 2 0 first-pair))) + (point-2 (force (tuple-select 2 1 first-pair)))) + (lisp:list* (xpoint-x point-1) + (xpoint-y point-1) + (xpoint-x point-2) + (xpoint-y point-2) + (segments->point-seq (cdr segments)))))) + +(define (rects->point-seq rects) + (if (null? rects) + '() + (let ((rect (car rects))) + (lisp:list* (xrect-x rect) + (xrect-y rect) + (xrect-w rect) + (xrect-h rect) + (rects->point-seq (cdr rects)))))) + +(define (point-seq->rects point-seq) + (if (null? point-seq) + '() + (cons (mk-xrect (car point-seq) (cadr point-seq) + (caddr point-seq) (cadddr point-seq)) + (point-seq->rects (cddddr point-seq))))) + +(define (arcs->point-seq arcs) + (if (null? arcs) + '() + (let ((arc (car arcs))) + (lisp:list* (xarc-x arc) + (xarc-y arc) + (xarc-w arc) + (xarc-h arc) + (xarc-a1 arc) + (xarc-a2 arc) + (arcs->point-seq (cdr arcs)))))) + +(define (x-draw-line drawable gcontext point-1 point-2) + (xlib:draw-line drawable gcontext (xpoint-x point-1) (xpoint-y point-1) + (xpoint-x point-2) (xpoint-y point-2))) + +(define (x-draw-lines drawable gcontext points fill-p) + (xlib:draw-lines drawable gcontext + (points->point-seq points) :fill-p fill-p)) + +(define (x-draw-segments drawable gcontext segments) + (xlib:draw-segments drawable gcontext (segments->point-seq segments))) + +(define (x-draw-rectangle drawable gcontext rect fill-p) + (xlib:draw-rectangle drawable gcontext + (xrect-x rect) (xrect-y rect) + (xrect-w rect) (xrect-h rect) + fill-p)) + +(define (x-draw-rectangles drawable gcontext rects fill-p) + (xlib:draw-rectangles drawable gcontext + (rects->point-seq rects) + fill-p)) + +(define (x-draw-arc drawable gcontext arc fill-p) + (xlib:draw-arc drawable gcontext + (xarc-x arc) (xarc-y arc) + (xarc-w arc) (xarc-h arc) + (xarc-a1 arc) (xarc-a2 arc) + fill-p)) + +(define (x-draw-arcs drawable gcontext arcs fill-p) + (xlib:draw-arcs drawable gcontext + (arcs->point-seq arcs) + fill-p)) + +(define (x-draw-glyph drawable gcontext point element) + (nth-value 1 + (xlib:draw-glyph drawable gcontext (xpoint-x point) + (xpoint-y point) element))) + +(define (x-draw-glyphs drawable gcontext point element) + (nth-value 1 (xlib:draw-glyphs drawable gcontext (xpoint-x point) + (xpoint-y point) element))) + +(define (x-draw-image-glyph drawable gcontext point element) + (nth-value 1 (xlib:draw-image-glyph drawable gcontext (xpoint-x point) + (xpoint-y point) element))) + +(define (x-draw-image-glyphs drawable gcontext point element) + (nth-value 1 (xlib:draw-image-glyphs drawable gcontext (xpoint-x point) + (xpoint-y point) element))) + +(define (x-image-size image) + (mk-xsize (xlib:image-width image) (xlib:image-height image))) + +(define (x-image-name image) + (let ((lisp-name (xlib:image-name image))) + (cond ((null? lisp-name) "") + ((symbol? lisp-name) (symbol->string lisp-name)) + (else lisp-name)))) + +(define-attribute-setter image name) + +(define (x-image-hot-spot image) + (mk-xpoint (xlib:image-x-hot image) (xlib:image-y-hot image))) + +(define (x-set-image-hot-spot image point) + (setf (xlib:image-x-hot image) (xpoint-x point)) + (setf (xlib:image-y-hot image) (xpoint-y point))) + +(define-attribute-setter image xy-bitmap-list) +(define-attribute-setter image z-bits-per-pixel) +(define-attribute-setter image z-pixarray) + +(define (x-create-image attrs) + (apply (function xlib:create-image) (attrs->keywords attrs))) + +(define (x-copy-image image rect type) + (xlib:copy-image image :x (xrect-x rect) :y (xrect-y rect) + :width (xrect-w rect) :height (xrect-h rect) + :result-type type)) + +(define (x-get-image drawable rect pmask format type) + (xlib:get-image drawable :x (xrect-x rect) :y (xrect-y rect) + :width (xrect-w rect) :height (xrect-h rect) + :plane-mask pmask :format format :result-type type)) + +(define (x-put-image drawable gcontext image point rect) + (xlib:put-image drawable gcontext image + :src-x (xpoint-x point) :src-y (xpoint-y point) + :x (xrect-x rect) :y (xrect-y rect) + :width (xrect-w rect) :height (xrect-h rect))) + +(define (x-get-raw-image drawable rect pmask format) + (xlib:get-raw-image drawable + :x (xrect-x rect) :y (xrect-y rect) + :width (xrect-w rect) :height (xrect-h rect) + :plane-mask pmask :format format)) + +(define (x-put-raw-image drawable gcontext data depth rect left-pad format) + (xlib:put-raw-image drawable gcontext data + :depth depth + :x (xrect-x rect) :y (xrect-y rect) + :width (xrect-w rect) :height (xrect-h rect) + :left-pad left-pad :format format)) + +(define (x-font-name font) + (let ((lisp-name (xlib:font-name font))) + (cond ((null? lisp-name) "") + ((symbol? lisp-name) (symbol->string lisp-name)) + (else lisp-name)))) + +(define (x-alloc-color colormap color) + (multiple-value-bind (pixel screen-color exact-color) + (xlib:alloc-color colormap color) + (make-h-tuple pixel screen-color exact-color))) + +(define (x-alloc-color-cells colormap colors planes contiguous-p) + (multiple-value-bind (pixels mask) + (xlib:alloc-color-cells colormap colors :planes planes + :contiguous-p contiguous-p) + (make-h-tuple (list->haskell-list/identity pixels) (list->haskell-list/identity mask)))) + +(define (x-alloc-color-planes colormap colors reds greens blues contiguous-p) + (multiple-value-bind (pixels red-mask green-mask blue-mask) + (xlib:alloc-color-planes colormap colors :reds reds :greens greens + :blues blues :contiguous-p contiguous-p) + (make-h-tuple (list->haskell-list/identity pixels) + red-mask + green-mask + blue-mask))) + +(define (x-lookup-color colormap name) + (multiple-value-bind (screen-color exact-color) + (xlib:lookup-color colormap name) + (make-h-tuple screen-color exact-color))) + +(define (unzip l) + (if (null? l) + '() + (let ((h (car l))) + (lisp:list* (force (tuple-select 2 0 h)) + (force (tuple-select 2 1 h)) + (unzip (cdr l)))))) + +(define (x-store-colors colormap pixel-colors) + (xlib:store-colors colormap (unzip pixel-colors))) + +(define (x-create-cursor source mask point foreground background) + (apply (function xlib:create-cursor) + `(:source ,source + ,@(if mask `(:mask ,mask) '()) + :x ,(xpoint-x point) :y ,(xpoint-y point) + :foreground ,foreground :background ,background))) + +(define (x-create-glyph-cursor src mask foreground background) + (apply (function xlib:create-glyph-cursor) + `(:source-font ,(force (tuple-select 2 0 src)) + :source-char ,(integer->char (force (tuple-select 2 1 src))) + ,@(if mask + `(:mask-font ,(force (tuple-select 2 0 mask)) + :mask-char ,(integer->char (force (tuple-select 2 1 mask)))) + '()) + :foreground ,foreground :background ,background))) + +(define (x-query-best-cursor size display) + (multiple-value-bind (w h) + (xlib:query-best-cursor (xsize-w size) (xsize-h size) display) + (mk-xsize w h))) + +(define (x-change-property window property content) + (xlib:change-property window property + (car content) (cadr content) + (caddr content))) + +(define (x-get-property window property) + (lisp:multiple-value-bind (data type format) + (xlib:get-property window property) + (list (sequence->list data) type format))) + +(define (x-convert-selection selection type requestor property time) + (apply (function xlib:convert-selection) + `(,selection ,type ,requestor ,property ,@(if time `(,time) '())))) + +(define (x-set-selection-owner display selection time owner) + (if time + (setf (xlib:selection-owner display selection time) owner) + (setf (xlib:selection-owner display selection) owner))) + +(define (sequence->list seq) + (if (list? seq) seq + (do ((i (1- (lisp:length seq)) (1- i)) + (res '() (cons (lisp:elt seq i) res))) + ((< i 0) res)))) + +(define *this-event* '()) + +(define (translate-event lisp:&rest event-slots lisp:&key event-key + lisp:&allow-other-keys) + (setf *this-event* (cons event-key event-slots)) + '#t) + + +(define (x-get-event display) + (xlib:process-event display :handler #'translate-event :force-output-p '#t) + *this-event*) + +(define (x-queue-event display event append-p) + (apply (function xlib:queue-event) + `(,display ,(car event) ,@(cdr event) :append-p ,append-p))) + +(define (x-event-listen display) + (let ((res (xlib:event-listen display))) + (if (null? res) 0 res))) + +(define (x-send-event window event mask) + (apply (function xlib:send-event) + `(,window ,(car event) ,mask ,@(cdr event)))) + +(define (x-global-pointer-position display) + (multiple-value-bind (x y) (xlib:global-pointer-position display) + (mk-xpoint x y))) + +(define (x-pointer-position window) + (multiple-value-bind (x y same) (xlib:pointer-position window) + (if same (mk-xpoint x y) '()))) + +(define (x-motion-events window start stop) + (do ((npos '() (cons (mk-xpoint (car pos) (cadr pos)) npos)) + (pos (xlib:motion-events window :start start :stop stop) + (cdddr pos))) + ((null? pos) (nreverse npos)))) + +(define (x-warp-pointer dest-win point) + (xlib:warp-pointer dest-win (xpoint-x point) (xpoint-y point))) + +(define (x-set-input-focus display focus revert-to time) + (apply (function xlib:set-input-focus) + `(,display ,focus ,revert-to ,@(if time `(,time) '())))) + +(define (x-input-focus display) + (multiple-value-bind (focus revert-to) (xlib:input-focus display) + (make-h-tuple focus revert-to))) + +(define (x-grab-pointer window event-mask attrs time) + (apply (function xlib:grab-pointer) + `(,window ,event-mask + ,@(attrs->keywords attrs) + ,@(if time `(:time ,time) '())))) + +(define (x-ungrab-pointer display time) + (if time + (xlib:ungrab-pointer display :time time) + (xlib:ungrab-pointer display))) + +(define (x-change-active-pointer-grab display event-mask attrs time) + (apply (function xlib:change-active-pointer-grab) + `(,display ,event-mask + ,@(attrs->keywords attrs) + ,@(if time `(,time) '())))) + +(define (x-grab-button window button event-mask state-mask attrs) + (apply (function xlib:grab-button) + `(,window ,button ,event-mask :modifiers ,state-mask + ,@(attrs->keywords attrs)))) + +(define (x-ungrab-button window button modifiers) + (xlib:ungrab-button window button :modifiers modifiers)) + +(define (x-grab-keyboard window attrs time) + (apply (function xlib:grab-keyboard) + `(,window ,@(attrs->keywords attrs) + ,@(if time `(:time ,time) '())))) + +(define (x-ungrab-keyboard display time) + (if time + (xlib:ungrab-keyboard display :time time) + (xlib:ungrab-keyboard display))) + +(define (x-grab-key window key state-mask attrs) + (apply (function xlib:grab-key) + `(,window ,key :modifiers ,state-mask ,@(attrs->keywords attrs)))) + +(define (x-ungrab-key window key modifiers) + (xlib:ungrab-button window key :modifiers modifiers)) + +(define (x-set-pointer-acceleration display val) + (xlib:change-pointer-control display :acceleration val)) + +(define (x-set-pointer-threshold display val) + (xlib:change-pointer-control display :threshold val)) + +(define (x-pointer-acceleration display) + (lisp:coerce (nth-value 0 (xlib:pointer-control display)) + 'lisp:single-float)) + +(define (x-pointer-threshold display) + (lisp:coerce (nth-value 1 (xlib:pointer-control display)) + 'lisp:single-float)) + +(define-attribute-setter pointer mapping) + +(define (x-set-keyboard-key-click-percent display v) + (xlib:change-keyboard-control display :key-click-percent v)) + +(define (x-set-keyboard-bell-percent display v) + (xlib:change-keyboard-control display :bell-percent v)) + +(define (x-set-keyboard-bell-pitch display v) + (xlib:change-keyboard-control display :bell-pitch v)) + +(define (x-set-keyboard-bell-duration display v) + (xlib:change-keyboard-control display :bell-duration v)) + + +;;; Yes, leds are really counted from 1 rather than 0. + +(define (x-set-keyboard-led display v) + (declare (type integer v)) + (do ((led 1 (1+ led)) + (vv v (lisp:ash vv -1))) + ((> led 32)) + (declare (type fixnum led) (type integer vv)) + (xlib:change-keyboard-control display + :led led + :led-mode (if (lisp:logand vv 1) :on :off)))) + +(define (x-set-keyboard-auto-repeat-mode display v) + (do ((key 0 (1+ key))) + ((>= key (lisp:length v))) + (declare (type fixnum key)) + (xlib:change-keyboard-control display + :key key + :auto-repeat-mode (if (eqv? (the fixnum (lisp:aref v key)) 1) :on :off) + ))) + +(define (x-keyboard-key-click-percent display) + (nth-value 0 (xlib:keyboard-control display))) + +(define (x-keyboard-bell-percent display) + (nth-value 1 (xlib:keyboard-control display))) + +(define (x-keyboard-bell-pitch display) + (nth-value 2 (xlib:keyboard-control display))) + +(define (x-keyboard-bell-duration display) + (nth-value 3 (xlib:keyboard-control display))) + +(define (x-keyboard-led display) + (nth-value 4 (xlib:keyboard-control display))) + +(define (x-keyboard-auto-repeat-mode display) + (nth-value 6 (xlib:keyboard-control display))) + +(define (x-modifier-mapping display) + (lisp:multiple-value-list (xlib:modifier-mapping display))) + +(define (x-set-modifier-mapping display l) + (let ((l1 (cddddr l))) + (xlib:set-modifier-mapping display + :shift (car l) + :lock (cadr l) + :control (caddr l) + :mod1 (cadddr l) + :mod2 (car l1) + :mod3 (cadr l1) + :mod4 (caddr l1) + :mod5 (cadddr l1)))) + +(define (x-keysym-character display keysym state) + (let ((res (xlib:keysym->character display keysym state))) + (if (char? res) (char->integer res) '()))) + +(define (x-keycode-character display keycode state) + (let ((res (xlib:keycode->character display keycode state))) + (if (char? res) (char->integer res) '()))) + +(define-attribute-setter close-down mode) + +(define-attribute-setter access control) + +(define (x-screen-saver display) + (lisp:multiple-value-list (xlib:screen-saver display))) + +(define (x-set-screen-saver display ss) + (xlib:set-screen-saver display (car ss) (cadr ss) (caddr ss) (cadddr ss))) + +(define (slots->keywords slots) + (if (null slots) '() + `(,@(slot->keyword (car slots)) ,@(slots->keywords (cdr slots))))) + +(define (slot->keyword slot) + (let* ((tag (keyword-key slot)) + (val (keyword-val slot))) + (case tag + (:pos `(:x ,(xpoint-x val) :y ,(xpoint-y val))) + (:root-pos `(:root-x ,(xpoint-x val) :root-y ,(xpoint-y val))) + (:size `(:width ,(xsize-w val) :height ,(xsize-h val))) + (:rect `(:x ,(xrect-x val) :y ,(xrect-y val) + :width ,(xrect-w val) :height ,(xrect-h val))) + (:graph-fun `(:major ,(car val) :minor ,(cdr val))) + (:visibility `(:state ,val)) + (:property-state `(:state ,val)) + (:message `(:data ,(car val) :type ,(cadr val) :format ,(caddr val))) + (else `(,tag ,val))))) + +(define (keywords->slots type keywords event) + (let* ((slots (keywords->slots1 type keywords)) + (has-root-xy (memq type '(:key-press :key-release :button-press + :button-release :motion-notify + :enter-notify :leave-notify))) + (has-xy (or has-root-xy + (memq type '(:gravity-notify :reparent-notify)))) + (has-graph-fun (memq type '(:graphics-exposure :no-exposure))) + (has-rect (memq type '(:exposure :graphics-exposure + :configure-notify + :create-notify :configure-request))) + (has-size (memq type '(:resize-request))) + (has-message (memq type '(:client-message)))) + (when has-xy + (push (make-keyword :pos (x-event-pos event)) slots)) + (when has-root-xy + (push (make-keyword :root-pos (x-event-root-pos event)) slots)) + (when has-graph-fun + (push (make-keyword :graph-fun (x-event-graph-fun event)) slots)) + (when has-rect + (push (make-keyword :rect (x-event-rect event)) slots)) + (when has-size + (push (make-keyword :size (x-event-size event)) slots)) + (when has-message + (push (make-keyword :message (x-event-message event)) slots)) + slots)) + +(define (keywords->slots1 type keywords) + (if (null? keywords) + '() + (if (memq (car keywords) + '(:x :y :width :height :root-x :root-y + :major :minor :type :data :format)) + (keywords->slots1 type (cddr keywords)) + (cons (keyword->slot type (car keywords) (cadr keywords)) + (keywords->slots1 type (cddr keywords)))))) + +(define (keyword->slot type slot val) + (if (eq? slot :state) + (case type + (:property-state (make-keyword :property-state val)) + (:visibility (make-keyword :visibility val)) + (else (make-keyword :state val))) + (make-keyword slot val))) + +(define (attrs->keywords attrs) + (if (null attrs) + '() + (nconc (attr->keyword (car attrs)) + (attrs->keywords (cdr attrs))))) + +(define (attr->keyword attr) + (let* ((tag (keyword-key attr)) + (val (keyword-val attr))) + (case tag + (:clip-origin `(:clip-x ,(xpoint-x val) :clip-y ,(xpoint-y val))) + (:dashes `(,tag ,(haskell-list->list/identity val))) + (:tile-origin `(:ts-x ,(xpoint-x val) :ts-y ,(xpoint-y val))) + (:size `(:width ,(xsize-w val) :height ,(xsize-h val))) + (:name `(:name ,(haskell-string->string val))) + (:hot-spot `(:x-hot ,(xpoint-x val) :y-hot ,(xpoint-y val))) + (else `(,tag ,val))))) + +(define (x-mutable-array-create inits) + (list->vector inits)) + +(define (x-mutable-array-lookup a i) + (vector-ref a i)) + +(define (x-mutable-array-update a i x) + (setf (vector-ref a i) x)) + +(define (x-mutable-array-length a) + (vector-length a)) + +(define (get-time-zone) + (nth-value 8 (lisp:get-decoded-time))) + +(define (decode-time time zone) + (multiple-value-bind (sec min hour date mon year week ds-p) + (if zone + (lisp:decode-universal-time time zone) + (lisp:decode-universal-time time)) + (make-h-tuple + (list->haskell-list/identity (list sec min hour date mon year week)) + ds-p))) + +(define (encode-time time zone) + (apply (function lisp:encode-universal-time) + (if (null? zone) time (append time (list zone))))) + +(define (get-run-time) + (/ (lisp:coerce (lisp:get-internal-run-time) 'lisp:single-float) + (lisp:coerce lisp:internal-time-units-per-second 'lisp:single-float))) + +(define (get-elapsed-time) + (/ (lisp:coerce (lisp:get-internal-real-time) 'lisp:single-float) + (lisp:coerce lisp:internal-time-units-per-second 'lisp:single-float))) + +(define (prim.thenio---1 x fn) + (lambda (state) + (declare (ignore state)) + (let ((res (funcall x (box 'state)))) + (format '#t "~A~%" res) + (funcall fn res (box 'state))))) + +(define-attribute-setter wm name) +(define-attribute-setter wm icon-name) diff --git a/progs/lib/X11/xlibprims.hi b/progs/lib/X11/xlibprims.hi new file mode 100644 index 0000000..02d4163 --- /dev/null +++ b/progs/lib/X11/xlibprims.hi @@ -0,0 +1,1465 @@ +-- 4/13/93 add xTestEventMask, xTestStateMask +-- 4/14/93 add xMArrayLength, +-- xGetEventN +-- 4/15/93 change xKeycodeCharacter +-- add xKeysymCharacter +-- add xHandleError +-- add xError +-- 4/27/93 Change Bool to XSwitch in XWinAttribute, XGCAttribute + +interface XLibPrims where + +import XLibTypes( + XDisplay, XScreen, XWindow, XGcontext, XPixmap, + XColormap, XCursor, XFont, XImage, XMaybe, XError, + XBitmap, XKeysymTable, XBitVec, + XPixarray, XByteVec, XAtom, XProperty, + XPixel, XDrawable, XTime, XSwitch, + XWindowPlace, XEventMode, XEventKind, + XWindowVisibility, XWindowStackMode, + XPropertyState, XMapReqType, XGraphFun, + XEvent, XEventType, XEventSlot, XEventMask, + XEventMaskKey, XStateMask, XStateMaskKey, + XWinAttribute,XGCAttribute, XImAttribute, + XGrabAttribute, XArcMode, XCapStyle, + XClipMask, XFillRule, XFillStyle, + XFunction, XJoinStyle, XLineStyle, + XSubwindowMode, XPoint, XSize, XRect, + XArc, XBitmapFormat, XByteOrder, + XPixmapFormat, XVisualInfo, XVisualClass, + XFillContent, XBackingStore, XGravity, + XWindowClass, XMapState, XImageData, + XImageFormat, XImageType, XDrawDirection, + XColor, XInputFocus, XGrabStatus, + XKeysym, XCloseDownMode, XScreenSaver) + +xHandleError :: (XError -> IO a) -> IO a -> IO a +xError :: String -> IO a + +xEventType :: XEvent -> XEventType +xEventWindow :: XEvent -> XWindow +xEventEventWindow :: XEvent -> XWindow +xEventCode :: XEvent -> Int +xEventPos :: XEvent -> XPoint +xEventState :: XEvent -> XStateMask +xEventTime :: XEvent -> XTime +xEventRoot :: XEvent -> XWindow +xEventRootPos :: XEvent -> XPoint +xEventChild :: XEvent -> (XMaybe XWindow) +xEventSameScreenP :: XEvent -> Bool +xEventHintP :: XEvent -> Bool +xEventMode :: XEvent -> XEventMode +xEventKind :: XEvent -> XEventKind +xEventFocusP :: XEvent -> Bool +xEventKeymap :: XEvent -> XBitVec +xEventRequest :: XEvent -> XMapReqType +xEventStart :: XEvent -> Int +xEventCount :: XEvent -> Int +xEventRect :: XEvent -> XRect +xEventDrawable :: XEvent -> XDrawable +xEventXGraphFun :: XEvent -> XGraphFun +xEventPlace :: XEvent -> XWindowPlace +xEventBorderWidth :: XEvent -> Int +xEventAboveSibling :: XEvent -> (XMaybe XWindow) +xEventOverrideRedirectP :: XEvent -> Bool +xEventParent :: XEvent -> XWindow +xEventConfigureP :: XEvent -> Bool +xEventVisibility :: XEvent -> XWindowVisibility +xEventNewP :: XEvent -> Bool +xEventInstalledP :: XEvent -> Bool +xEventStackMode :: XEvent -> XWindowStackMode +xEventValueMask :: XEvent -> Int +xEventSize :: XEvent -> XSize +xEventMessage :: XEvent -> XProperty +xEventPropertyState :: XEvent -> XPropertyState +xEventAtom :: XEvent -> XAtom +xEventSelection :: XEvent -> XAtom +xEventTarget :: XEvent -> XAtom +xEventProperty :: XEvent -> (XMaybe XAtom) +xEventRequestor :: XEvent -> XWindow + +xSetEventMaskKey :: XEventMask -> XEventMaskKey -> XEventMask +xClearEventMaskKey :: XEventMask -> XEventMaskKey -> XEventMask +xTestEventMaskKey :: XEventMask -> XEventMaskKey -> Bool + +xSetStateMaskKey :: XStateMask -> XStateMaskKey -> XStateMask +xClearStateMaskKey :: XStateMask -> XStateMaskKey -> XStateMask +xTestStateMaskKey :: XStateMask -> XStateMaskKey -> Bool + + +-- DISPLAYS + +-- open + +xOpenDisplay :: String -- host:display + -> IO XDisplay + +-- display attributes + +xDisplayAuthorizationData :: XDisplay -> String +xDisplayAuthorizationName :: XDisplay -> String +xDisplayBitmapFormat :: XDisplay -> XBitmapFormat +xDisplayByteOrder :: XDisplay -> XByteOrder +xDisplayDisplay :: XDisplay -> Int +xSetDisplayErrorHandler :: XDisplay -> (XError -> IO ()) -> IO () +xDisplayImageLsbFirstP :: XDisplay -> Bool +xDisplayMaxKeycode :: XDisplay -> Int +xDisplayMaxRequestLength :: XDisplay -> Int +xDisplayMinKeycode :: XDisplay -> Int +xDisplayMotionBufferSize :: XDisplay -> Int +xDisplayPixmapFormats :: XDisplay -> [XPixmapFormat] +xDisplayProtocolMajorVersion :: XDisplay -> Int +xDisplayProtocolMinorVersion :: XDisplay -> Int +xDisplayResourceIdBase :: XDisplay -> Int +xDisplayResourceIdMask :: XDisplay -> Int +xDisplayRoots :: XDisplay -> [XScreen] +xDisplayVendorName :: XDisplay -> String +xDisplayReleaseNumber :: XDisplay -> Int + +-- output buffer + +xDisplayAfterFunction :: XDisplay -> XMaybe (IO ()) +xSetDisplayAfterFunction :: XDisplay -> XMaybe (IO ()) -> IO () +xDisplayForceOutput :: XDisplay -> IO () +xDisplayFinishOutput :: XDisplay -> IO () + +-- close + +xCloseDisplay :: XDisplay -> IO () + +-- SCREENS + +xScreenBackingStores :: XScreen -> XBackingStore +xScreenBlackPixel :: XScreen -> XPixel +xScreenDefaultColormap :: XScreen -> XColormap +xScreenDepths :: XScreen -> [(Int, [XVisualInfo])] +xScreenEventMaskAtOpen :: XScreen -> XEventMask +xScreenSize :: XScreen -> XSize +xScreenMMSize :: XScreen -> XSize +xScreenMaxInstalledMaps :: XScreen -> Int +xScreenMinInstalledMaps :: XScreen -> Int +xScreenRoot :: XScreen -> XWindow +xScreenRootDepth :: XScreen -> Int +xScreenRootVisual :: XScreen -> Int +xScreenSaveUndersP :: XScreen -> Bool +xScreenWhitePixel :: XScreen -> XPixel + +-- WINDOWS AND PIXMAPS + +-- drawables + +xDrawableDisplay :: XDrawable -> XDisplay +xDrawableEqual :: XDrawable -> XDrawable -> Bool +xDrawableId :: XDrawable -> Int + +-- creating windows + +xCreateWindow :: XWindow -- parent + -> XRect -- (x,y,width,height) + -> [XWinAttribute] -- optional arguments + -> IO XWindow + +-- window attributes + +xWindowBorderWidth :: XWindow -> IO Int +xSetWindowBorderWidth :: XWindow -> Int -> IO () + +xDrawableDepth :: XDrawable -> Int + +xDrawableSize :: XDrawable -> IO XSize +xDrawableResize :: XDrawable -> XSize -> IO () + +xWindowPos :: XWindow -> IO XPoint +xWindowMove :: XWindow -> XPoint -> IO () + +xWindowAllEventMasks :: XWindow -> IO XEventMask +xSetWindowBackground :: XWindow -> XFillContent -> IO () + +xWindowBackingPixel :: XWindow -> IO XPixel +xSetWindowBackingPixel :: XWindow -> XPixel -> IO () + +xWindowBackingPlanes :: XWindow -> IO XPixel +xSetWindowBackingPlanes :: XWindow -> XPixel -> IO () + +xWindowBackingStore :: XWindow -> IO XBackingStore +xSetWindowBackingStore :: XWindow -> XBackingStore -> IO () + +xWindowBitGravity :: XWindow -> IO XGravity +xSetWindowBitGravity :: XWindow -> XGravity -> IO () + +xSetWindowBorder :: XWindow -> XFillContent -> IO () + +xWindowClass :: XWindow -> XWindowClass + +xWindowColorMap :: XWindow -> IO (XMaybe XColormap) +xSetWindowColorMap :: XWindow -> XColormap -> IO () +xWindowColormapInstalledP :: XWindow -> IO Bool + +xSetWindowCursor :: XWindow -> (XMaybe XCursor) -> IO () + +xWindowDisplay :: XWindow -> XDisplay + +xWindowDoNotPropagateMask :: XWindow -> IO XEventMask +xSetWindowDoNotPropagateMask :: XWindow -> XEventMask -> IO () + +xWindowEqual :: XWindow -> XWindow -> Bool + +xWindowEventMask :: XWindow -> IO XEventMask +xSetWindowEventMask :: XWindow -> XEventMask -> IO () + +xWindowGravity :: XWindow -> IO XGravity +xSetWindowGravity :: XWindow -> XGravity -> IO () + +xWindowId :: XWindow -> Int + +xWindowMapState :: XWindow -> IO XMapState + +xWindowOverrideRedirect :: XWindow -> IO XSwitch +xSetWindowOverrideRedirect :: XWindow -> XSwitch -> IO () + +xSetWindowPriority :: XWindow -> XWindowStackMode -> IO () + +xWindowSaveUnder :: XWindow -> IO XSwitch +xSetWindowSaveUnder :: XWindow -> XSwitch -> IO () + +xWindowVisual :: XWindow -> Int + +-- stacking order + +xCirculateWindowDown :: XWindow -> IO () +xCirculateWindowUp :: XWindow -> IO () + +-- window hierarchy + +xDrawableRoot :: XDrawable -> IO XWindow +xQueryTree :: XWindow -> IO ([XWindow], -- children + XMaybe XWindow,-- parent + XWindow) -- root + +xReparentWindow :: XWindow -- window + -> XWindow -- parent + -> XPoint -- (x,y) + -> IO () + +xTranslateCoordinates :: XWindow -- source + -> XPoint -- (source-x,source-y) + -> XWindow -- destination + -> IO (XMaybe XPoint) -- (dest-x,dest-y) + +-- mapping windows + +xMapWindow :: XWindow -> IO () +xMapSubwindows :: XWindow -> IO () +xUnmapWindow :: XWindow -> IO () +xUnmapSubwindows :: XWindow -> IO () + +-- destroying windows + +xDestroyWindow :: XWindow -> IO () +xDestroySubwindows :: XWindow -> IO () + +-- pixmaps + +xCreatePixmap :: XSize -- (width,height) + -> Int -- depth + -> XDrawable -- drawable + -> IO XPixmap + +xFreePixmap :: XPixmap -> IO () + +xPixmapDisplay :: XPixmap -> XDisplay +xPixmapEqual :: XPixmap -> XPixmap -> Bool + +-- GRAPHICS CONTEXTS + +xCreateGcontext :: XDrawable -- drawable + -> [XGCAttribute] -- optional arguments + -> IO XGcontext + +xUpdateGcontext :: XGcontext -- old gcontext + -> [XGCAttribute] -- changes + -> IO () -- new gcontext + +xFreeGcontext :: XGcontext -> IO () + +xGcontextDisplay :: XGcontext -> XDisplay +xGcontextEqual :: XGcontext -> XGcontext -> Bool + +xGcontextId :: XGcontext -> Int + +xQueryBestStipple :: XSize -> XDrawable -> XSize +xQueryBestTile :: XSize -> XDrawable -> XSize + +xCopyGcontext :: XGcontext -- source + -> XGcontext -- destination + -> IO () + +-- GRAPHICS OPERATIONS + +xClearArea :: XWindow -- window + -> XRect -- (x,y,width,height) + -> Bool -- exposure-p + -> IO () + +xCopyArea :: XDrawable -- source + -> XGcontext -- gcontext + -> XRect -- (src-x,src-y,w,h) + -> XDrawable -- destination + -> XPoint -- (dest-x,dest-y) + -> IO () + +xCopyPlane :: XDrawable -- source + -> XGcontext -- gcontext + -> XPixel -- plane + -> XRect -- (src-x,src-y,w,h) + -> XDrawable -- destination + -> XPoint -- (dest-x,dest-y) + -> IO () + +xDrawPoint :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XPoint -- (x,y) + -> IO () + +xDrawPoints :: XDrawable -- drawable + -> XGcontext -- gcontext + -> [XPoint] -- points + -> IO () + +xDrawLine :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XPoint -- (x1,y1) + -> XPoint -- (x2,y2) + -> IO () + +xDrawLines :: XDrawable -- drawable + -> XGcontext -- gcontext + -> [XPoint] -- points + -> Bool -- fill-p + -> IO () + +xDrawSegments :: XDrawable -- drawable + -> XGcontext -- gcontext + -> [(XPoint,XPoint)] -- segments + -> IO () + +xDrawRectangle :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XRect -- (x,y,width,height) + -> Bool -- fill-p + -> IO () + +xDrawRectangles :: XDrawable -- drawable + -> XGcontext -- gcontext + -> [XRect] -- rectangles + -> Bool -- fill-p + -> IO () + +xDrawArc :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XArc -- (x,y,w,h,a1,a2) + -> Bool -- fill-p + -> IO () + +xDrawArcs :: XDrawable -- drawable + -> XGcontext -- gcontext + -> [XArc] -- arcs + -> Bool -- fill-p + -> IO () + +xDrawGlyph :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XPoint -- (x,y) + -> Char -- element + -> IO (XMaybe Int) -- width + +xDrawGlyphs :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XPoint -- (x,y) + -> String -- sequence + -> IO (XMaybe Int) -- width + +xDrawImageGlyph :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XPoint -- (x,y) + -> Char -- element + -> IO (XMaybe Int) -- width + +xDrawImageGlyphs :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XPoint -- (x,y) + -> String -- sequence + -> IO (XMaybe Int) -- width + +-- IMAGES + +xImageBlueMask :: XImage -> XMaybe XPixel +xImageDepth :: XImage -> Int +xImageGreenMask :: XImage -> XMaybe XPixel +xImageSize :: XImage -> XSize +xImageName :: XImage -> String +xSetImageName :: XImage -> String -> IO () +xImageRedMask :: XImage -> XMaybe XPixel +xImageHotSpot :: XImage -> XMaybe XPoint +xSetImageHotSpot :: XImage -> XPoint -> IO () + +-- XY-format images + +xImageXYBitmaps :: XImage -> IO [XBitmap] +xSetImageXYBitmaps :: XImage -> [XBitmap] -> IO () + +-- Z-format images + +xImageZBitsPerPixel :: XImage -> IO Int +xsetImageZBitsPerPixel :: XImage -> Int -> IO () +xImageZPixarray :: XImage -> IO XPixarray +xSetImageZPixarray :: XImage -> XPixarray -> IO () + +-- image functions + +xCreateImage :: [XImAttribute] -> IO XImage +xCopyImage :: XImage -- image + -> XRect -- (x,y,width,height) + -> XImageType -- result-type + -> XImage -- new-image + +xGetImage :: XDrawable -- drawable + -> XRect -- (x,y,width,height) + -> XPixel -- plane-mask + -> XImageFormat -- format + -> XImageType -- result-type + -> IO XImage -- image + +xPutImage :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XImage -- ximage + -> XPoint -- (src-x,src-y) + -> XRect -- (x,y,width,height) + -> IO () + +-- image files + +xReadBitmapFile :: String -- pathname + -> IO XImage + +xWriteBitmapFile :: String -- pathname + -> XImage -> IO () + +-- direct image transfer + +xGetRawImage :: XDrawable -- drawable + -> XRect -- (x,y,width,height) + -> XPixel -- plane-mask + -> XImageFormat -- format + -> IO XImageData -- data + +xPutRawImage :: XDrawable -- drawable + -> XGcontext -- gcontext + -> XImageData -- data + -> Int -- depth + -> XRect -- (x,y,width,height) + -> Int -- left-pad + -> XImageFormat -- format + -> IO () + +-- FONTS + +-- opening fonts + +xOpenFont :: XDisplay -> String -> IO XFont +xCloseFont :: XFont -> IO () +xDiscardFontInfo :: XFont -> IO () + +-- listing fonts + +xFontPath :: XDisplay -> IO [String] +xListFontNames :: XDisplay -> String -- pattern + -> IO [String] +xListFonts :: XDisplay -> String -- pattern + -> IO [XFont] + +-- font attriburtes + +xFontAllCharExistsP :: XFont -> Bool +xFontAscent :: XFont -> Int +xFontDefaultChar :: XFont -> Int +xFontDescent :: XFont -> Int +xFontDirection :: XFont -> XDrawDirection +xFontDisplay :: XFont -> XDisplay +xFontEqual :: XFont -> XFont -> Int +xFontId :: XFont -> Int + +xFontMaxByte1 :: XFont -> Int +xFontMaxByte2 :: XFont -> Int +xFontMaxChar :: XFont -> Int +xFontMinByte1 :: XFont -> Int +xFontMinByte2 :: XFont -> Int +xFontMinChar :: XFont -> Int + +xFontName :: XFont -> String + +xFontMaxCharAscent :: XFont -> Int +xFontMaxCharAttributes :: XFont -> Int +xFontMaxCharDescent :: XFont -> Int +xFontMaxCharLeftBearing :: XFont -> Int +xFontMaxCharRightBearing :: XFont -> Int +xFontMaxCharWidth :: XFont -> Int +xFontMinCharAscent :: XFont -> Int +xFontMinCharAttributes :: XFont -> Int +xFontMinCharDescent :: XFont -> Int +xFontMinCharLeftBearing :: XFont -> Int +xFontMinCharRightBearing :: XFont -> Int +xFontMinCharWidth :: XFont -> Int + +-- char attributes + +xCharAscent :: XFont -> Int -> XMaybe Int +xCharAttributes :: XFont -> Int -> XMaybe Int +xCharDescent :: XFont -> Int -> XMaybe Int +xCharLeftBearing :: XFont -> Int -> XMaybe Int +xCharRightBearing :: XFont -> Int -> XMaybe Int +xCharWidth :: XFont -> Int -> XMaybe Int + +-- querying text size + +xTextWidth :: XFont -- font + -> String -- sequence + -> Int -- width + +-- COLORS + +-- creating colormaps + +xCreateColormap :: XVisualInfo -- visual + -> XWindow -- window + -> Bool -- alloc-p + -> IO XColormap + +xCopyColormapAndFree :: XColormap -> IO XColormap +xFreeColormap :: XColormap -> IO () + +-- installing colormaps + +xInstallColormap :: XColormap -> IO () +xInstalledColormaps :: XWindow -> IO [XColormap] +xUnInstallColormap :: XColormap -> IO () + +-- allocating colors + +xAllocColor :: XColormap -> XColor + -> IO (XPixel, -- pixel + XColor, -- screen-color + XColor) -- exact-color + +xAllocColorCells :: XColormap -- pixel + -> Int -- colors + -> Int -- planes + -> Bool -- contiguous + -> IO ([XPixel], -- pixels + [XPixel]) -- mask + +xAllocColorPlanes :: XColormap -- colormap + -> Int -- colors + -> Int -- reds + -> Int -- greens + -> Int -- blues + -> Bool -- contiguous-p + -> IO ([XPixel], -- pixel + XPixel, -- red-mask + XPixel, -- green-mask + XPixel) -- blue-mask + +xFreeColors :: XColormap -> [XPixel] -- pixels + -> XPixel -- plane-mask + -> IO () + +-- finding colors + +xLookupColor :: XColormap -> String -- name + -> IO (XColor, -- screen-color + XColor) -- exact-color + +xQueryColors :: XColormap -> [XPixel] -- pixels + -> IO [XColor] + +-- changing colors + +xStoreColor :: XColormap -> XPixel -- pixel + -> XColor -- color + -> IO () + +xStoreColors :: XColormap -- colormap + -> [(XPixel, XColor)] -- pixel-colors + -> IO () + +-- colormap attributes + +xColormapDisplay :: XColormap -> XDisplay +xColormapEqual :: XColormap -> XColormap -> Bool + +-- CURSORS + +xCreateCursor :: XPixmap -- source + -> (XMaybe XPixmap) -- mask + -> XPoint -- (x,y) + -> XColor -- foreground + -> XColor -- background + -> IO XCursor + +xCreateGlyphCursor :: (XFont, char) -- (src-font,src-char) + -> (XMaybe (XFont, Char)) -- (mask-font,mask-char) + -> XColor -- foreground + -> XColor -- background + -> IO XCursor + +xFreeCursor :: XCursor -> IO () + +xQueryBestCursor :: XSize -- (width,height) + -> XDisplay -> IO XSize + +xRecolorCursor :: XCursor -> XColor -- foreground + -> XColor -- background + -> IO () + +xCursorDisplay :: XCursor -> XDisplay +xCursorEqual :: XCursor -> XCursor -> Bool + +-- ATOMS, PROPERTIES, AND SELECTIONS + +-- atoms + +xAtomName :: XDisplay -> Int -- atom-id + -> XAtom + +xFindAtom :: XDisplay -> XAtom -- atom-name + -> IO (XMaybe Int) + +xInternAtom :: XDisplay -> XAtom -- atom-name + -> IO (XMaybe Int) + +-- properties + +xChangeProperty :: XWindow -- window + -> XAtom -- property + -> XProperty -- (data,type,format) + -> IO () + +xDeleteProperty :: XWindow -> XAtom -> IO () +xGetProperty :: XWindow -- window + -> XAtom -- property + -> IO XProperty -- (data,type,format) + +xListProperties :: XWindow -> IO [XAtom] +xRotateProperties :: XWindow -- window + -> [XAtom] -- properties + -> Int -- delta + -> IO () + +-- selections + +xConvertSelection :: XAtom -- selection + -> XAtom -- type + -> XWindow -- requester + -> XAtom -- property + -> (XMaybe XTime) -- time + -> IO () + +xSelectionOwner :: XDisplay -- display + -> XAtom -- selection + -> IO (XMaybe XWindow) + +xSetSelectionOwner :: XDisplay -- display + -> XAtom -- selection + -> (XMaybe XTime) -- time + -> XWindow -- owner + -> IO () + +-- EVENT + +-- Wait for the next event + +xGetEvent :: XDisplay -> IO XEvent + +-- managing the event queue + +xQueueEvent :: XDisplay -> XEvent -> Bool -- append-p + -> IO () + +xEventListen :: XDisplay -> IO Int -- # of events in queue + +-- sending events + +xSendEvent :: XWindow -- window + -> XEvent -- event key and slots + -> XEventMask -- event-mask + -> IO () + +-- pointer position + +xGlobalPointerPosition :: XDisplay -> IO XPoint +xPointerPosition :: XWindow -> IO (XMaybe XPoint) +xMotionEvents :: XWindow -> XTime -> XTime -> IO [XPoint] +xWarpPointer :: XWindow -> XPoint -> IO () + +-- keyboard input focus + +xSetInputFocus :: XDisplay -- display + -> XInputFocus -- focus + -> XInputFocus -- revert-to + -> (XMaybe XTime) -- time + -> IO () + +xInputFucus :: XDisplay -> IO (XInputFocus, -- focus + XInputFocus) -- revert-to + +-- grabbing the pointer + +xGrabPointer :: XWindow -- window + -> XEventMask -- event-mask + -> [XGrabAttribute] -- optional attributes + -> XMaybe XTime -- time + -> IO XGrabStatus + +xUngrabPointer :: XDisplay -> XMaybe XTime -> IO () + +xChangeActivePointerGrab :: XDisplay -> XEventMask -- event-mask + -> [XGrabAttribute] -- cursor + -> XMaybe XTime -> IO () + +-- grabbing a button + +xGrabButton :: XWindow -- window + -> Int -- button + -> XEventMask -- event-mask + -> XStateMask -- modifiers + -> [XGrabAttribute] -- optional attributes + -> IO () + +xUngrabButton :: XWindow -> Int -- button + -> XStateMask -- modifiers + -> IO () + +-- grabbing the keyboard + +xGrabKeyboard :: XWindow -- window + -> [XGrabAttribute] -- optional attributes + -> XMaybe XTime -- time + -> IO XGrabStatus + +xUngrabkeyboard :: XDisplay -> XMaybe XTime -> IO () + +-- grabbing a key + +xGrabKey :: XWindow -- window + -> Int -- key + -> XStateMask -- modifiers + -> [XGrabAttribute] -- optional attributes + -> IO () + +xUngrabKey :: XWindow -> Int -> XStateMask -- modifiers + -> IO () + +-- CONTROL FUNCTIONS + +-- grabbing the server + +xGrabServer :: XDisplay -> IO () +xUngrabServer :: XDisplay -> IO () + +-- pointer control + +xSetPointerAcceleration :: XDisplay -> Float -> IO () +xSetPointerThreshold :: XDisplay -> Float -> IO () +xPointerAcceleration :: XDisplay -> IO Float +xPointerThreshold :: XDisplay -> IO Float +xSetPointerMapping :: XDisplay -> [Int] -> IO () +xPointerMapping :: XDisplay -> IO [Int] + +-- keyboard control + +xBell :: XDisplay -> Int -> IO () + +xSetKeyboardKeyClickPercent :: XDisplay -> Int -> IO () +xSetKeyboardBellPercent :: XDisplay -> Int -> IO () +xSetKeyboardBellPitch :: XDisplay -> Int -> IO () +xSetKeyboardBellDuration :: XDisplay -> Int -> IO () +xSetKeyboardLed :: XDisplay -> Integer -> IO () +xSetKeyboardAutoRepeatMode :: XDisplay -> XBitVec -> IO () + +xKeyboardKeyClickPercent :: XDisplay -> IO Int +xKeyboardBellPercent :: XDisplay -> IO Int +xKeyboardBellPitch :: XDisplay -> IO Int +xKeyboardBellDuration :: XDisplay -> IO Int + +xKeyboardLed :: XDisplay -> IO Integer +xKeyboardAutoRepeatMode :: XDisplay -> IO XBitVec + +xModifierMapping :: XDisplay -> IO [[Int]] +xSetModifierMapping :: XDisplay -> [[Int]] -> IO (XMaybe ()) +xQueryKeymap :: XDisplay -> IO XBitVec + +-- keyboard mapping + +xChangeKeyboardMapping :: XDisplay -- display + -> XKeysymTable -- keysyms + -> IO () + +xKeyboardMapping :: XDisplay -- display + -> IO XKeysymTable -- mappings + +xKeycodeKeysym :: XDisplay -- display + -> Int -- keycode + -> Int -- keysym-index + -> IO XKeysym + +xKeysymCharacter :: XDisplay -- display + -> XKeysym -- keysym + -> XStateMask -- state + -> IO (XMaybe Char) + +xKeycodeCharacter :: XDisplay -- display + -> Int -- keycode + -> XStateMask -- state + -> IO (XMaybe Char) + +-- client termination + +xAddToSaveSet :: XWindow -> IO () +xCloseDownMode :: XDisplay -> IO XCloseDownMode +xSetCloseDownMode :: XDisplay -> XCloseDownMode -> IO () +xKillClient :: XDisplay -> Int -> IO () +xKillTemporaryClients :: XDisplay -> IO () +xRemoveFromSaveSet :: XWindow -> IO () + +-- managing host access + +xAccessControl :: XDisplay -> IO Bool +xSetAccessControl :: XDisplay -> Bool -> IO () +xAccessHosts :: XDisplay -> IO [String] +xAddAccessHost :: XDisplay -> String -> IO () +xRemoveAccessHost :: XDisplay -> String -> IO () + +-- screen saver + +xActivateScreenSaver :: XDisplay -> IO () +xResetScreenSaver :: XDisplay -> IO () + +xScreenSaver :: XDisplay -> IO XScreenSaver +xSetScreenSaver :: XDisplay -> XScreenSaver -> IO () + +{-# + + +xHandleError :: LispName("x-handle-error") +xError :: LispName("xlib::x-error") + +xEventType :: LispName("sel-event-type") + +xEventWindow :: LispName ("x-event-window") +xEventEventWindow :: LispName ("x-event-event-window") +xEventCode :: LispName ("x-event-code") +xEventPos :: LispName ("x-event-pos") +xEventState :: LispName ("x-event-state") +xEventTime :: LispName ("x-event-time") +xEventRoot :: LispName ("x-event-root") +xEventRootPos :: LispName ("x-event-root-pos") +xEventChild :: LispName ("x-event-child") +xEventSameScreenP :: LispName ("x-event-same-screen-p") +xEventHintP :: LispName ("x-event-hint-p") +xEventMode :: LispName ("x-event-mode") +xEventKind :: LispName ("x-event-kind") +xEventFocusP :: LispName ("x-event-focus-p") +xEventKeymap :: LispName ("x-event-keymap") +xEventRequest :: LispName ("x-event-request") +xEventStart :: LispName ("x-event-start") +xEventCount :: LispName ("x-event-count") +xEventRect :: LispName ("x-event-rect") +xEventDrawable :: LispName ("x-event-drawable") +xEventXGraphFun :: LispName ("x-event-graph-fun") +xEventPlace :: LispName ("x-event-place") +xEventBorderWidth :: LispName ("x-event-border-width") +xEventAboveSibling :: LispName ("x-event-above-sibling") +xEventOverrideRedirectP :: LispName ("x-event-override-redirect-p") +xEventParent :: LispName ("x-event-parent") +xEventConfigureP :: LispName ("x-event-configure-p") +xEventVisibility :: LispName ("x-event-state") +xEventNewP :: LispName ("x-event-new-p") +xEventInstalledP :: LispName ("x-event-installed-p") +xEventStackMode :: LispName ("x-event-stack-mode") +xEventValueMask :: LispName ("x-event-value-mask") +xEventSize :: LispName ("x-event-size") +xEventMessage :: LispName ("x-event-message") +xEventPropertyState :: LispName ("x-event-state") +xEventAtom :: LispName ("x-event-atom") +xEventSelection :: LispName ("x-event-selection") +xEventTarget :: LispName ("x-event-target") +xEventProperty :: LispName ("x-event-property") +xEventRequestor :: LispName ("x-event-requestor") + + +xSetEventMaskKey :: LispName ("x-set-event-mask-key") +xClearEventMaskKey :: LispName ("x-clear-event-mask-key") +xTestEventMaskKey :: LispName ("x-test-event-mask-key") + +xSetStateMaskKey :: LispName ("x-set-state-mask-key") +xClearStateMaskKey :: LispName ("x-clear-state-mask-key") +xTestStateMaskKey :: LispName ("x-test-state-mask-key") + +-- DISPLAYS + +-- open + +xOpenDisplay :: LispName("x-open-display") + +-- display attributes + +xDisplayAuthorizationData :: LispName("xlib:display-authorization-data") +xDisplayAuthorizationName :: LispName("xlib:display-authorization-name") +xDisplayBitmapFormat :: LispName("xlib:display-bitmap-format") +xDisplayByteOrder :: LispName("xlib:display-byte-order") +xDisplayDisplay :: LispName("xlib:display-display") +xSetDisplayErrorHandler :: LispName("x-set-display-error-handler") +xDisplayImageLsbFirstP :: LispName("xlib:display-image-lsb-first-p") +xDisplayMaxKeycode :: LispName("xlib:display-max-keycode") +xDisplayMaxRequestLength :: LispName("xlib:display-max-request-length") +xDisplayMinKeycode :: LispName("xlib:display-min-keycode") +xDisplayMotionBufferSize :: LispName("xlib:display-motion-buffer-size") +xDisplayPixmapFormats :: LispName("xlib:display-pixmap-formats") +xDisplayProtocolMajorVersion :: LispName("xlib:display-protocol-major-version") +xDisplayProtocolMinorVersion :: LispName("xlib:display-protocol-minor-version") +xDisplayResourceIdBase :: LispName("xlib:display-resource-id-base") +xDisplayResourceIdMask :: LispName("xlib:display-resource-id-mask") +xDisplayRoots :: LispName("xlib:display-roots") +xDisplayVendorName :: LispName("xlib:display-vendor-name") +xDisplayReleaseNumber :: LispName("xlib:display-release-number") + +-- output buffer + +xDisplayAfterFunction :: LispName("xlib:display-after-function") +xSetDisplayAfterFunction :: LispName("x-set-display-after-function") +xDisplayForceOutput :: LispName("xlib:display-force-output") +xDisplayFinishOutput :: LispName("xlib:display-finish-output") + +-- close + +xCloseDisplay :: LispName("xlib:close-display") + +-- SCREENS + +xScreenBackingStores :: LispName("xlib:screen-backing-stores") +xScreenBlackPixel :: LispName("xlib:screen-black-pixel") +xScreenDefaultColormap :: LispName("xlib:screen-default-colormap") +xScreenDepths :: LispName("x-screen-depths") +xScreenEventMaskAtOpen :: LispName("xlib:screen-event-mask-at-open") +xScreenSize :: LispName("x-screen-size") +xScreenMMSize :: LispName("x-screen-mmsize") +xScreenMaxInstalledMaps :: LispName("xlib:screen-max-installed-maps") +xScreenMinInstalledMaps :: LispName("xlib:screen-min-installed-maps") +xScreenRoot :: LispName("xlib:screen-root") +xScreenRootDepth :: LispName("xlib:screen-root-depth") +xScreenRootVisual :: LispName("xlib:screen-root-visual") +xScreenSaveUndersP :: LispName("xlib:screen-save-unders-p") +xScreenWhitePixel :: LispName("xlib:screen-white-pixel") + +-- WINDOWS AND PIXMAPS + +-- drawables + +xDrawableDisplay :: LispName("xlib:drawable-display") +xDrawableEqual :: LispName("xlib:drawable-equal") +xDrawableId :: LispName("xlib:drawable-id") + +-- creating windows + +xCreateWindow :: LispName("x-create-window") + +-- window attributes + +xWindowBorderWidth :: LispName("xlib:drawable-border-width") +xSetWindowBorderWidth :: LispName("x-set-drawable-border-width") + +xDrawableDepth :: LispName("xlib:drawable-depth") + +xDrawableSize :: LispName("x-drawable-size") +xDrawableResize :: LispName("x-drawable-resize") + +xWindowPos :: LispName("x-window-pos") +xWindowMove :: LispName("x-window-move") + +xWindowAllEventMasks :: LispName("xlib:window-all-event-masks") + +xSetWindowBackground :: LispName("x-set-window-background") + +xWindowBackingPixel :: LispName("xlib:window-backing-pixel") +xSetWindowBackingPixel :: LispName("x-set-window-backing-pixel") + +xWindowBackingPlanes :: LispName("xlib:window-backing-planes") +xSetWindowBackingPlanes :: LispName("x-set-window-backing-planes") + +xWindowBackingStore :: LispName("xlib:window-backing-store") +xSetWindowBackingStore :: LispName("x-set-window-backing-store") + +xWindowBitGravity :: LispName("xlib:window-bit-gravity") +xSetWindowBitGravity :: LispName("x-set-window-bit-gravity") + +xSetWindowBorder :: LispName("x-set-window-border") + +xWindowClass :: LispName("xlib:window-class") + +xWindowColorMap :: LispName("xlib:window-colormap") +xSetWindowColorMap :: LispName("x-set-window-colormap") +xWindowColormapInstalledP :: LispName("xlib:window-colormap-installed-p") + +xSetWindowCursor :: LispName("x-set-window-cursor") + +xWindowDisplay :: LispName("xlib:window-display") + +xWindowDoNotPropagateMask :: LispName("xlib:window-do-not-propagate-mask") +xSetWindowDoNotPropagateMask :: LispName("x-set-window-do-not-propagate-mask") + +xWindowEqual :: LispName("xlib:window-equal") + +xWindowEventMask :: LispName("xlib:window-event-mask") +xSetWindowEventMask :: LispName("x-set-window-event-mask") + +xWindowGravity :: LispName("xlib:window-gravity") +xSetWindowGravity :: LispName("x-set-window-gravity") + +xWindowId :: LispName("xlib:window-id") + +xWindowMapState :: LispName("xlib:window-map-state") + +xWindowOverrideRedirect :: LispName("xlib:window-override-redirect") +xSetWindowOverrideRedirect :: LispName("x-set-window-override-redirect") + +xSetWindowPriority :: LispName("x-set-window-priority") + +xWindowSaveUnder :: LispName("xlib:window-save-under") +xSetWindowSaveUnder :: LispName("x-set-window-save-under") +xWindowVisual :: LispName("xlib:window-visual") + +-- stacking order + +xCirculateWindowDown :: LispName("xlib:circulate-window-down") +xCirculateWindowUp :: LispName("xlib:circulate-window-up") + +-- window hierarchy + +xDrawableRoot :: LispName("xlib:drawable-root") +xQueryTree :: LispName("x-query-tree") + +xReparentWindow :: LispName("x-reparent-window") + +xTranslateCoordinates :: LispName("x-translate-coordinates") + +-- mapping windows + +xMapWindow :: LispName("xlib:map-window") +xMapSubwindows :: LispName("xlib:map-subwindows") +xUnmapWindow :: LispName("xlib:unmap-window") +xUnmapSubwindows :: LispName("xlib:unmap-subwindows") + +-- destroying windows + +xDestroyWindow :: LispName("xlib:destroy-window") +xDestroySubwindows :: LispName("xlib:destroy-subwindows") + +-- pixmaps + +xCreatePixmap :: LispName("x-create-pixmap") +xFreePixmap :: LispName("xlib:free-pixmap") +xPixmapDisplay :: LispName("xlib:pixmap-display") +xPixmapEqual :: LispName("xlib:pixmap-equal") + +-- GRAPHICS CONTEXTS + +xCreateGcontext :: LispName("x-create-gcontext") +xUpdateGcontext :: LispName("x-update-gcontext") +xFreeGcontext :: LispName("xlib:free-gcontext") + +xGcontextDisplay :: LispName("xlib:gcontext-display") +xGcontextEqual :: LispName("xlib:gcontext-equal") + +xGcontextId :: LispName("xlib:gcontext-id") + +xQueryBestStipple :: LispName("x-query-best-stipple") +xQueryBestTile :: LispName("x-query-best-tile") + +xCopyGcontext :: LispName("xlib:copy-gcontext") + +-- GRAPHICS OPERATIONS + +xClearArea :: LispName("x-clear-area") +xCopyArea :: LispName("x-copy-area") +xCopyPlane :: LispName("x-copy-plane") +xDrawPoint :: LispName("x-draw-point") +xDrawPoints :: LispName("x-draw-points") +xDrawLine :: LispName("x-draw-line") +xDrawLines :: LispName("x-draw-lines") +xDrawSegments :: LispName("x-draw-segments") +xDrawRectangle :: LispName("x-draw-rectangle") +xDrawRectangles :: LispName("x-draw-rectangles") +xDrawArc :: LispName("x-draw-arc") +xDrawArcs :: LispName("x-draw-arcs") +xDrawGlyph :: LispName("x-draw-glyph") +xDrawGlyphs :: LispName("x-draw-glyphs") +xDrawImageGlyph :: LispName("x-draw-image-glyph") +xDrawImageGlyphs :: LispName("x-draw-image-glyphs") + +-- IMAGES + +xImageBlueMask :: LispName("xlib:image-blue-mask") +xImageDepth :: LispName("xlib:image-depth") +xImageGreenMask :: LispName("xlib:image-green-mask") +xImageSize :: LispName("x-image-size") +xImageName :: LispName("x-image-name") +xSetImageName :: LispName("x-set-image-name") +xImageRedMask :: LispName("xlib:image-red-mask") +xImageHotSpot :: LispName("x-image-hot-spot") +xSetImageHotSpot :: LispName("x-set-image-hot-spot") + +-- XY-format images + +xImageXYBitmaps :: LispName("xlib:image-xy-bitmap-list") +xSetImageXYBitmaps :: LispName("x-set-image-xy-bitmap-list") + +-- Z-format images + +xImageZBitsPerPixel :: LispName("xlib:image-z-bits-per-pixel") +xsetImageZBitsPerPixel :: LispName("x-set-image-z-bits-per-pixel") +xImageZPixarray :: LispName("xlib:image-z-pixarray") +xSetImageZPixarray :: LispName("x-set-image-z-pixarray") + +-- image functions + +xCreateImage :: LispName("x-create-image") +xCopyImage :: LispName("x-copy-image") +xGetImage :: LispName("x-get-image") +xPutImage :: LispName("x-put-image") + +-- image files + +xReadBitmapFile :: LispName("xlib:read-bitmap-file") +xWriteBitmapFile :: LispName("xlib:write-bitmap-file") + +-- direct image transfer + +xGetRawImage :: LispName("x-get-raw-image") +xPutRawImage :: LispName("x-put-raw-image") + +-- FONTS + +-- opening fonts + +xOpenFont :: LispName ("xlib:open-font") +xCloseFont :: LispName ("xlib:close-font") +xDiscardFontInfo :: LispName ("xlib:discard-font-info") + +-- listing fonts + +xFontPath :: LispName ("xlib:font-path") +xListFontNames :: LispName ("xlib:list-font-names") +xListFonts :: LispName ("xlib:list-fonts") + +-- font attriburtes + +xFontAllCharExistsP :: LispName ("xlib:font-all-chars-exist-p") +xFontAscent :: LispName ("xlib:font-ascent") +xFontDefaultChar :: LispName ("xlib:font-default-char") +xFontDescent :: LispName ("xlib:font-descent") +xFontDirection :: LispName ("xlib:font-direction") +xFontDisplay :: LispName ("xlib:font-display") +xFontEqual :: LispName ("xlib:font-equal") +xFontId :: LispName ("xlib:font-id") + +xFontMaxByte1 :: LispName ("xlib:font-max-byte1") +xFontMaxByte2 :: LispName ("xlib:font-max-byte2") +xFontMaxChar :: LispName ("xlib:font-max-char") +xFontMinByte1 :: LispName ("xlib:font-min-byte1") +xFontMinByte2 :: LispName ("xlib:font-min-byte2") +xFontMinChar :: LispName ("xlib:font-min-char") + +xFontName :: LispName ("x-font-name") + +xFontMaxCharAscent :: LispName ("xlib:max-char-ascent") +xFontMaxCharAttributes :: LispName ("xlib:max-char-attributes") +xFontMaxCharDescent :: LispName ("xlib:max-char-descent") +xFontMaxCharLeftBearing :: LispName ("xlib:max-char-left-bearing") +xFontMaxCharRightBearing :: LispName ("xlib:max-char-right-bearing") +xFontMaxCharWidth :: LispName ("xlib:max-char-width") +xFontMinCharAscent :: LispName ("xlib:min-char-ascent") +xFontMinCharAttributes :: LispName ("xlib:min-char-attributes") +xFontMinCharDescent :: LispName ("xlib:min-char-descent") +xFontMinCharLeftBearing :: LispName ("xlib:min-char-left-bearing") +xFontMinCharRightBearing :: LispName ("xlib:min-char-right-bearing") +xFontMinCharWidth :: LispName ("xlib:min-char-width") + +-- char attributes + +xCharAscent :: LispName ("xlib:char-ascent") +xCharAttributes :: LispName ("xlib:char-attributes") +xCharDescent :: LispName ("xlib:char-descent") +xCharLeftBearing :: LispName ("xlib:char-left-bearing") +xCharRightBearing :: LispName ("xlib:char-right-bearing") +xCharWidth :: LispName ("xlib:char-width") + +-- querying text size + +xTextWidth :: LispName ("xlib:text-width") + +-- COLORS + +-- creating colormaps + +xCreateColormap :: LispName ("xlib:create-colormap") +xCopyColormapAndFree :: LispName ("xlib:copy-colormap-and-free") +xFreeColormap :: LispName ("xlib:free-colormap") + +-- installing colormaps + +xInstallColormap :: LispName ("xlib:install-colormap") +xInstalledColormaps :: LispName ("xlib:installed-colormaps") +xUnInstallColormap :: LispName ("xlib:uninstall-colormap") + +-- allocating colors + +xAllocColor :: LispName ("x-alloc-color") +xAllocColorCells :: LispName ("x-alloc-color-cells") +xAllocColorPlanes :: LispName ("x-alloc-color-planes") + +xFreeColors :: LispName ("xlib:free-colors") + +-- finding colors + +xLookupColor :: LispName ("x-lookup-color") +xQueryColors :: LispName ("xlib:query-colors") + +-- changing colors + +xStoreColor :: LispName ("xlib:store-color") +xStoreColors :: LispName ("x-store-colors") + +-- colormap attributes + +xColormapDisplay :: LispName ("xlib:colormap-display") +xColormapEqual :: LispName ("xlib:colormap-equal") + +-- CURSORS + +xCreateCursor :: LispName ("x-create-cursor") +xCreateGlyphCursor :: LispName ("x-create-glyph-cursor") +xFreeCursor :: LispName ("xlib:free-cursor") + +xQueryBestCursor :: LispName ("x-query-best-cursor") +xRecolorCursor :: LispName ("xlib:recolor-cursor") + +xCursorDisplay :: LispName ("xlib:cursor-display") +xCursorEqual :: LispName ("xlib:cursor-equal") + +-- ATOMS, PROPERTIES, AND SELECTIONS + +-- atoms + +xAtomName :: LispName ("xlib:atom-name") +xFindAtom :: LispName ("xlib:find-atom") +xInternAtom :: LispName ("xlib:intern-atom") + +-- properties + +xChangeProperty :: LispName ("x-change-property") +xDeleteProperty :: LispName ("xlib:delete-property") +xGetProperty :: LispName ("x-get-property") +xListProperties :: LispName ("xlib:list-properties") +xRotateProperties :: LispName ("xlib:rotate-properties") + +-- selections + +xConvertSelection :: LispName ("x-convert-selection") +xSelectionOwner :: LispName ("xlib:selection-owner") +xSetSelectionOwner :: LispName ("x-set-selection-owner") + +-- EVENT + +-- Wait for the next event + +xGetEvent :: LispName ("x-get-event") + +-- managing the event queue + +xQueueEvent :: LispName ("x-queue-event") +xEventListen :: LispName ("x-event-listen") + +-- sending events + +xSendEvent :: LispName ("x-send-event") + +-- pointer position + +xGlobalPointerPosition :: LispName ("x-global-pointer-position") +xPointerPosition :: LispName ("x-pointer-position") +xMotionEvents :: LispName ("x-motion-events") +xWarpPointer :: LispName ("x-warp-pointer") + +-- keyboard input focus + +xSetInputFocus :: LispName ("x-set-input-focus") +xInputFucus :: LispName ("x-input-focus") + +-- grabbing the pointer + +xGrabPointer :: LispName ("x-grab-pointer") +xUngrabPointer :: LispName ("x-ungrab-pointer") +xChangeActivePointerGrab :: LispName ("x-change-active-pointer-grab") + +-- grabbing a button + +xGrabButton :: LispName ("x-grab-button") +xUngrabButton :: LispName ("x-ungrab-button") + +-- grabbing the keyboard + +xGrabKeyboard :: LispName ("x-grab-keyboard") +xUngrabkeyboard :: LispName ("x-ungrab-keyboard") + +-- grabbing a key + +xGrabKey :: LispName ("x-grab-key") +xUngrabKey :: LispName ("x-ungrab-key") + +-- CONTROL FUNCTIONS + +-- grabbing the server + +xGrabServer :: LispName ("xlib:grab-server") +xUngrabServer :: LispName ("xlib:ungrab-server") + +-- pointer control + +xSetPointerAcceleration :: LispName ("x-set-pointer-acceleration") +xSetPointerThreshold :: LispName ("x-set-pointer-threshold") +xPointerAcceleration :: LispName ("x-pointer-acceleration") +xPointerThreshold :: LispName ("x-pointer-threshold") +xSetPointerMapping :: LispName ("x-set-pointer-mapping") +xPointerMapping :: LispName ("xlib:pointer-mapping") + +-- keyboard control + +xBell :: LispName ("xlib:bell") + +xSetKeyboardKeyClickPercent :: LispName ("x-set-keyboard-key-click-percent") +xSetKeyboardBellPercent :: LispName ("x-set-keyboard-bell-percent") +xSetKeyboardBellPitch :: LispName ("x-set-keyboard-bell-pitch") +xSetKeyboardBellDuration :: LispName ("x-set-keyboard-bell-duration") +xSetKeyboardLed :: LispName ("x-set-keyboard-led") +xSetKeyboardAutoRepeatMode :: LispName ("x-set-keyboard-auto-repeat-mode") + +xKeyboardKeyClickPercent :: LispName ("x-keyboard-key-click-percent") +xKeyboardBellPercent :: LispName ("x-keyboard-bell-percent") +xKeyboardBellPitch :: LispName ("x-keyboard-bell-pitch") +xKeyboardBellDuration :: LispName ("x-keyboard-bell-duration") +xKeyboardLed :: LispName ("x-keyboard-led") +xKeyboardAutoRepeatMode :: LispName ("x-keyboard-auto-repeat-mode") + +xModifierMapping :: LispName ("x-modifier-mapping") +xSetModifierMapping :: LispName ("x-set-modifier-mapping") +xQueryKeymap :: LispName ("xlib:query-keymap") + +-- keyboard mapping + +xChangeKeyboardMapping :: LispName ("xlib:change-keyboard-mapping") +xKeyboardMapping :: LispName ("xlib:keyboard-mapping") + +xKeycodeKeysym :: LispName ("xlib:keycode->keysym") +xKeysymCharacter :: LispName ("x-keysym-character") +xKeycodeCharacter :: LispName ("x-keycode-character") + +-- client termination + +xAddToSaveSet :: LispName ("xlib:add-to-save-set") +xCloseDownMode :: LispName ("xlib:close-down-mode") +xSetCloseDownMode :: LispName ("x-set-close-down-mode") +xKillClient :: LispName ("xlib:kill-client") +xKillTemporaryClients :: LispName ("xlib:kill-temporary-clients") +xRemoveFromSaveSet :: LispName ("xlib:remove-from-save-set") + +-- managing host access + +xAccessControl :: LispName ("xlib:access-control") +xSetAccessControl :: LispName ("x-set-access-control") +xAccessHosts :: LispName ("xlib:access-hosts") +xAddAccessHost :: LispName ("xlib:add-access-host") +xRemoveAccessHost :: LispName ("xlib:remove-access-host") + +-- screen saver + +xActivateScreenSaver :: LispName ("xlib:activate-screen-saver") +xResetScreenSaver :: LispName ("xlib:reset-screen-saver") +xScreenSaver :: LispName ("x-screen-saver") +xSetScreenSaver :: LispName ("x-set-screen-saver") + +#-} + +data XMArray a + +xMArrayCreate :: [a] -> IO (XMArray a) +xMArrayLookup :: XMArray a -> Int -> IO a +xMArrayUpdate :: XMArray a -> Int -> a -> IO () +xMArrayLength :: XMArray a -> Int + +{-# +xMArrayCreate :: LispName("x-mutable-array-create") +xMArrayLookup :: LispName("x-mutable-array-lookup") +xMArrayUpdate :: LispName("x-mutable-array-update") +xMArrayLength :: LispName("x-mutable-array-length") +#-} + + +xprint :: a -> IO () +{-# +xprint :: LispName ("x-print") +#-} + +-- decoded time format: +-- ([second, minute, hour, date, month, year, day-of-week], +-- daylight-saving-time-p) +-- time format to encode: +-- [second, minute, hour, date, month, year] + +data TimeZone = WestOfGMT Int {-# STRICT #-} + | CurrentZone + +getTime :: IO Integer +getTimeZone :: IO Int +decodeTime :: Integer -> TimeZone -> ([Int], Bool) +encodeTime :: [Int] -> TimeZone -> Integer +getRunTime :: IO Float +getElapsedTime :: IO Float +sleep :: Int -> IO () + +{-# +ImportLispType (TimeZone (WestOfGMT ("number?", "identity", "identity"))) +ImportLispType (TimeZone (CurrentZone ("null?", "'()"))) + +getTime :: LispName("lisp:get-universal-time") +getTimeZone :: LispName("get-time-zone") +decodeTime :: LispName("decode-time") +encodeTime :: LispName("encode-time") +getRunTime :: LispName("get-run-time") +getElapsedTime :: LispName("get-elapsed-time") +sleep :: LispName("lisp:sleep") + +#-} + +xWmName :: XWindow -> IO String +xSetWmName :: XWindow -> String -> IO () + +xWmIconName :: XWindow -> IO String +xSetWmIconName :: XWindow -> String -> IO () + +{-# +xWmName :: LispName ("xlib:wm-name") +xSetWmName :: LispName ("x-set-wm-name") + +xWmIconName :: LispName ("xlib:wm-icon-name") +xSetWmIconName :: LispName ("x-set-wm-icon-name") +#-} diff --git a/progs/lib/X11/xlibprims.hu b/progs/lib/X11/xlibprims.hu new file mode 100644 index 0000000..38138d4 --- /dev/null +++ b/progs/lib/X11/xlibprims.hu @@ -0,0 +1,5 @@ +:output $LIBRARYBIN/ +:stable +:o= all +xlibclx.scm +xlibprims.hi diff --git a/progs/lib/cl/README b/progs/lib/cl/README new file mode 100644 index 0000000..8164257 --- /dev/null +++ b/progs/lib/cl/README @@ -0,0 +1,2 @@ +This directory contains some libraries which allow you to use various +Common Lisp primitives from Haskell. diff --git a/progs/lib/cl/logop-prims.hi b/progs/lib/cl/logop-prims.hi new file mode 100644 index 0000000..2b120bb --- /dev/null +++ b/progs/lib/cl/logop-prims.hi @@ -0,0 +1,78 @@ +-- logop-prims.hi -- interface to logical operations on numbers +-- +-- author : Sandra Loosemore +-- date : 19 June 1993 +-- + +interface LogOpPrims where + +logiorInteger :: Integer -> Integer -> Integer +logxorInteger :: Integer -> Integer -> Integer +logandInteger :: Integer -> Integer -> Integer +logeqvInteger :: Integer -> Integer -> Integer +lognandInteger :: Integer -> Integer -> Integer +lognorInteger :: Integer -> Integer -> Integer +logandc1Integer :: Integer -> Integer -> Integer +logandc2Integer :: Integer -> Integer -> Integer +logorc1Integer :: Integer -> Integer -> Integer +logorc2Integer :: Integer -> Integer -> Integer +lognotInteger :: Integer -> Integer +logtestInteger :: Integer -> Integer -> Integer +logbitpInteger :: Int -> Integer -> Integer +ashInteger :: Integer -> Int -> Integer +logcountInteger :: Integer -> Int +integerLengthInteger :: Integer -> Int + +logiorInt :: Int -> Int -> Int +logxorInt :: Int -> Int -> Int +logandInt :: Int -> Int -> Int +logeqvInt :: Int -> Int -> Int +lognandInt :: Int -> Int -> Int +lognorInt :: Int -> Int -> Int +logandc1Int :: Int -> Int -> Int +logandc2Int :: Int -> Int -> Int +logorc1Int :: Int -> Int -> Int +logorc2Int :: Int -> Int -> Int +lognotInt :: Int -> Int +logtestInt :: Int -> Int -> Int +logbitpInt :: Int -> Int -> Int +ashInt :: Int -> Int -> Int +logcountInt :: Int -> Int +integerLengthInt :: Int -> Int + +{-# +logiorInteger :: LispName("logop.logior-integer"), Complexity(4) +logxorInteger :: LispName("logop.logxor-integer"), Complexity(4) +logandInteger :: LispName("logop.logand-integer"), Complexity(4) +logeqvInteger :: LispName("logop.logeqv-integer"), Complexity(4) +lognandInteger :: LispName("logop.lognand-integer"), Complexity(4) +lognorInteger :: LispName("logop.lognor-integer"), Complexity(4) +logandc1Integer :: LispName("logop.logandc1-integer"), Complexity(4) +logandc2Integer :: LispName("logop.logandc2-integer"), Complexity(4) +logorc1Integer :: LispName("logop.logorc1-integer"), Complexity(4) +logorc2Integer :: LispName("logop.logorc2-integer"), Complexity(4) +lognotInteger :: LispName("logop.lognot-integer"), Complexity(4) +logtestInteger :: LispName("logop.logtest-integer"), Complexity(4) +logbitpInteger :: LispName("logop.logbitp-integer"), Complexity(4) +ashInteger :: LispName("logop.ash-integer"), Complexity(4) +logcountInteger :: LispName("logop.logcount-integer"), Complexity(4) +integerLengthInteger :: LispName("logop.integer-length-integer"), Complexity(4) + +logiorInt :: LispName("logop.logior-int"), Complexity(2) +logxorInt :: LispName("logop.logxor-int"), Complexity(2) +logandInt :: LispName("logop.logand-int"), Complexity(2) +logeqvInt :: LispName("logop.logeqv-int"), Complexity(2) +lognandInt :: LispName("logop.lognand-int"), Complexity(2) +lognorInt :: LispName("logop.lognor-int"), Complexity(2) +logandc1Int :: LispName("logop.logandc1-int"), Complexity(2) +logandc2Int :: LispName("logop.logandc2-int"), Complexity(2) +logorc1Int :: LispName("logop.logorc1-int"), Complexity(2) +logorc2Int :: LispName("logop.logorc2-int"), Complexity(2) +lognotInt :: LispName("logop.lognot-int"), Complexity(2) +logtestInt :: LispName("logop.logtest-int"), Complexity(2) +logbitpInt :: LispName("logop.logbitp-int"), Complexity(2) +ashInt :: LispName("logop.ash-int"), Complexity(2) +logcountInt :: LispName("logop.logcount-int"), Complexity(2) +integerLengthInt :: LispName("logop.integer-length-int"), Complexity(2) +#-} + diff --git a/progs/lib/cl/logop-prims.scm b/progs/lib/cl/logop-prims.scm new file mode 100644 index 0000000..b846836 --- /dev/null +++ b/progs/lib/cl/logop-prims.scm @@ -0,0 +1,81 @@ +;;; logop-prims.scm -- primitives for logical operations on numbers +;;; +;;; author : Sandra Loosemore +;;; date : 19 Jun 1993 +;;; + + +;;; Integer operations +;;; Note that bit counts are still guaranteed to be fixnums.... + +(define-syntax (logop.logior-integer i1 i2) + `(the integer (lisp:logior (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.logxor-integer i1 i2) + `(the integer (lisp:logxor (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.logand-integer i1 i2) + `(the integer (lisp:logand (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.logeqv-integer i1 i2) + `(the integer (lisp:logeqv (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.lognand-integer i1 i2) + `(the integer (lisp:lognand (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.lognor-integer i1 i2) + `(the integer (lisp:lognor (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.logandc1-integer i1 i2) + `(the integer (lisp:logandc1 (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.logandc2-integer i1 i2) + `(the integer (lisp:logandc2 (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.logorc1-integer i1 i2) + `(the integer (lisp:logorc1 (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.logorc2-integer i1 i2) + `(the integer (lisp:logorc2 (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.lognot-integer i1) + `(the integer (lisp:lognot (the integer ,i1)))) +(define-syntax (logop.logtest-integer i1 i2) + `(the integer (lisp:logtest (the integer ,i1) (the integer ,i2)))) +(define-syntax (logop.logbitp-integer i1 i2) + `(the integer (lisp:logbitp (the fixnum ,i1) (the integer ,i2)))) +(define-syntax (logop.ash-integer i1 i2) + `(the integer (lisp:ash (the integer ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logcount-integer i1) + `(the fixnum (lisp:logcount (the integer ,i1)))) +(define-syntax (logop.integer-length-integer i1) + `(the fixnum (lisp:integer-length (the integer ,i1)))) + + +;;; Fixnum operations + +(define-syntax (logop.logior-int i1 i2) + `(the fixnum (lisp:logior (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logxor-int i1 i2) + `(the fixnum (lisp:logxor (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logand-int i1 i2) + `(the fixnum (lisp:logand (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logeqv-int i1 i2) + `(the fixnum (lisp:logeqv (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.lognand-int i1 i2) + `(the fixnum (lisp:lognand (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.lognor-int i1 i2) + `(the fixnum (lisp:lognor (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logandc1-int i1 i2) + `(the fixnum (lisp:logandc1 (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logandc2-int i1 i2) + `(the fixnum (lisp:logandc2 (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logorc1-int i1 i2) + `(the fixnum (lisp:logorc1 (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logorc2-int i1 i2) + `(the fixnum (lisp:logorc2 (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.lognot-int i1) + `(the fixnum (lisp:lognot (the fixnum ,i1)))) +(define-syntax (logop.logtest-int i1 i2) + `(the fixnum (lisp:logtest (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logbitp-int i1 i2) + `(the fixnum (lisp:logbitp (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.ash-int i1 i2) + `(the fixnum (lisp:ash (the fixnum ,i1) (the fixnum ,i2)))) +(define-syntax (logop.logcount-int i1) + `(the fixnum (lisp:logcount (the fixnum ,i1)))) +(define-syntax (logop.integer-length-int i1) + `(the fixnum (lisp:integer-length (the fixnum ,i1)))) + + + diff --git a/progs/lib/cl/logop.hs b/progs/lib/cl/logop.hs new file mode 100644 index 0000000..1d0f9ba --- /dev/null +++ b/progs/lib/cl/logop.hs @@ -0,0 +1,63 @@ +-- logop.hs -- logical operations on numbers +-- +-- author : Sandra Loosemore +-- date : 19 June 1993 +-- + +module LogOp where + +import LogOpPrims -- from logop-prims.hi + +class LogOperand a where + logior :: a -> a -> a + logxor :: a -> a -> a + logand :: a -> a -> a + logeqv :: a -> a -> a + lognand :: a -> a -> a + lognor :: a -> a -> a + logandc1 :: a -> a -> a + logandc2 :: a -> a -> a + logorc1 :: a -> a -> a + logorc2 :: a -> a -> a + lognot :: a -> a + logtest :: a -> a -> a + logbitp :: Int -> a -> a + ash :: a -> Int -> a + logcount :: a -> Int + integerLength :: a -> Int + +instance LogOperand Integer where + logior = logiorInteger + logxor = logxorInteger + logand = logandInteger + logeqv = logeqvInteger + lognand = lognandInteger + lognor = lognorInteger + logandc1 = logandc1Integer + logandc2 = logandc2Integer + logorc1 = logorc1Integer + logorc2 = logorc2Integer + lognot = lognotInteger + logtest = logtestInteger + logbitp = logbitpInteger + ash = ashInteger + logcount = logcountInteger + integerLength = integerLengthInteger + +instance LogOperand Int where + logior = logiorInt + logxor = logxorInt + logand = logandInt + logeqv = logeqvInt + lognand = lognandInt + lognor = lognorInt + logandc1 = logandc1Int + logandc2 = logandc2Int + logorc1 = logorc1Int + logorc2 = logorc2Int + lognot = lognotInt + logtest = logtestInt + logbitp = logbitpInt + ash = ashInt + logcount = logcountInt + integerLength = integerLengthInt diff --git a/progs/lib/cl/logop.hu b/progs/lib/cl/logop.hu new file mode 100644 index 0000000..cfe8209 --- /dev/null +++ b/progs/lib/cl/logop.hu @@ -0,0 +1,5 @@ +:output $LIBRARYBIN/ +:o= all +logop.hs +logop-prims.scm +logop-prims.hi diff --git a/progs/lib/cl/maybe.hs b/progs/lib/cl/maybe.hs new file mode 100644 index 0000000..8ce01e5 --- /dev/null +++ b/progs/lib/cl/maybe.hs @@ -0,0 +1,12 @@ +-- maybe.hs -- "maybe" type +-- +-- author : Sandra Loosemore +-- date : 22 June 1993 +-- + +module Maybe where + +data Maybe a = Some a | Null + +{-# ImportLispType (Maybe(Some("identity", "identity", "identity"), + Null("not", "'#f"))) #-} diff --git a/progs/lib/cl/maybe.hu b/progs/lib/cl/maybe.hu new file mode 100644 index 0000000..2115c71 --- /dev/null +++ b/progs/lib/cl/maybe.hu @@ -0,0 +1,3 @@ +:output $LIBRARYBIN/ +:o= all +maybe.hs diff --git a/progs/lib/cl/random-prims.hi b/progs/lib/cl/random-prims.hi new file mode 100644 index 0000000..e66d802 --- /dev/null +++ b/progs/lib/cl/random-prims.hi @@ -0,0 +1,20 @@ +-- random-prims.hi -- interface file to random number primitives +-- +-- author : Sandra Loosemore +-- date : 22 June 1993 +-- + + +interface RandomPrims where + +randomInt :: Int -> IO Int +randomInteger :: Integer -> IO Integer +randomFloat :: Float -> IO Float +randomDouble :: Double -> IO Double + +{-# +randomInt :: LispName("lisp:random"), Complexity(5) +randomInteger :: LispName("lisp:random"), Complexity(5) +randomFloat :: LispName("lisp:random"), Complexity(5) +randomDouble :: LispName("lisp:random"), Complexity(5) +#-} diff --git a/progs/lib/cl/random.hs b/progs/lib/cl/random.hs new file mode 100644 index 0000000..93d26e4 --- /dev/null +++ b/progs/lib/cl/random.hs @@ -0,0 +1,21 @@ +-- random.hs -- random number functions +-- +-- author : Sandra Loosemore +-- date : 22 June 1993 +-- + +module Random where + +import RandomPrims -- from random-prims.hi + +class RandomOperand a where + random :: a -> IO a + +instance RandomOperand Int where + random = randomInt +instance RandomOperand Integer where + random = randomInteger +instance RandomOperand Float where + random = randomFloat +instance RandomOperand Double where + random = randomDouble diff --git a/progs/lib/cl/random.hu b/progs/lib/cl/random.hu new file mode 100644 index 0000000..4b8e286 --- /dev/null +++ b/progs/lib/cl/random.hu @@ -0,0 +1,4 @@ +:output $LIBRARYBIN/ +:o= all +random.hs +random-prims.hi diff --git a/progs/lib/hbc/Either.hs b/progs/lib/hbc/Either.hs new file mode 100644 index 0000000..fad5af8 --- /dev/null +++ b/progs/lib/hbc/Either.hs @@ -0,0 +1,2 @@ +module Either(Either(..)) where +data Either a b = Left a | Right b deriving (Eq, Ord, Text, Binary) diff --git a/progs/lib/hbc/Either.hu b/progs/lib/hbc/Either.hu new file mode 100644 index 0000000..3313235 --- /dev/null +++ b/progs/lib/hbc/Either.hu @@ -0,0 +1,3 @@ +:output $LIBRARYBIN/ +:o= all +Either.hs diff --git a/progs/lib/hbc/Hash.hs b/progs/lib/hbc/Hash.hs new file mode 100644 index 0000000..1f14c6f --- /dev/null +++ b/progs/lib/hbc/Hash.hs @@ -0,0 +1,79 @@ +module Hash where +-- +-- Hash a value. Hashing produces an Int of +-- unspecified range. +-- + +class Hashable a where + hash :: a -> Int + +instance Hashable Char where + hash x = ord x + +instance Hashable Int where + hash x = x + +instance Hashable Integer where + hash x = fromInteger x + +instance Hashable Float where + hash x = truncate x + +instance Hashable Double where + hash x = truncate x + +instance Hashable Bin where + hash x = 0 + +{-instance Hashable File where + hash x = 0 -} + +instance Hashable () where + hash x = 0 + +instance Hashable (a -> b) where + hash x = 0 + +instance Hashable a => Hashable [a] where + hash x = sum (map hash x) + +instance (Hashable a, Hashable b) => Hashable (a,b) where + hash (a,b) = hash a + 3 * hash b + +instance (Hashable a, Hashable b, Hashable c) => Hashable (a,b,c) where + hash (a,b,c) = hash a + 3 * hash b + 5 * hash c + +instance (Hashable a, Hashable b, Hashable c, Hashable d) => Hashable (a,b,c,d) where + hash (a,b,c,d) = hash a + 3 * hash b + 5 * hash c + 7 * hash d + +instance (Hashable a, Hashable b, Hashable c, Hashable d, Hashable e) => Hashable (a,b,c,d,e) where + hash (a,b,c,d,e) = hash a + hash b + hash c + hash d + hash e + +instance Hashable Bool where + hash False = 0 + hash True = 1 + +instance (Integral a, Hashable a) => Hashable (Ratio a) where + hash x = hash (denominator x) + hash (numerator x) + +instance (RealFloat a, Hashable a) => Hashable (Complex a) where + hash (x :+ y) = hash x + hash y + +instance (Hashable a, Hashable b) => Hashable (Assoc a b) where + hash (x := y) = hash x + hash y + +instance (Ix a) => Hashable (Array a b) where + hash x = 0 -- !!! + +instance Hashable Request where + hash x = 0 -- !! + +instance Hashable Response where + hash x = 0 -- !! + +instance Hashable IOError where + hash x = 0 -- !! + +hashToMax maxhash x = + let h = abs (hash x) + in if h < 0 then 0 else h `rem` maxhash diff --git a/progs/lib/hbc/Hash.hu b/progs/lib/hbc/Hash.hu new file mode 100644 index 0000000..2c23c72 --- /dev/null +++ b/progs/lib/hbc/Hash.hu @@ -0,0 +1,3 @@ +:output $LIBRARYBIN/ +:o= all +Hash.hs diff --git a/progs/lib/hbc/ListUtil.hs b/progs/lib/hbc/ListUtil.hs new file mode 100644 index 0000000..560920e --- /dev/null +++ b/progs/lib/hbc/ListUtil.hs @@ -0,0 +1,48 @@ +module ListUtil(assoc, concatMap, unfoldr, mapAccuml, union, intersection, chopList, assocDef, lookup, Maybe..) where +import Maybe + +-- Lookup an item in an association list. Apply a function to it if it is found, otherwise return a default value. +assoc :: (Eq c) => (a -> b) -> b -> [(c, a)] -> c -> b +assoc f d [] x = d +assoc f d ((x',y):xys) x | x' == x = f y + | otherwise = assoc f d xys x + +-- Map and concatename results. +concatMap :: (a -> [b]) -> [a] -> [b] +concatMap f [] = [] +concatMap f (x:xs) = + case f x of + [] -> concatMap f xs + ys -> ys ++ concatMap f xs + +-- Repeatedly extract (and transform) values until a predicate hold. Return the list of values. +unfoldr :: (a -> (b, a)) -> (a -> Bool) -> a -> [b] +unfoldr f p x | p x = [] + | otherwise = y:unfoldr f p x' + where (y, x') = f x + +-- Map, but plumb a state through the map operation. +mapAccuml :: (a -> b -> (a, c)) -> a -> [b] -> (a, [c]) +mapAccuml f s [] = (s, []) +mapAccuml f s (x:xs) = (s'', y:ys) + where (s', y) = f s x + (s'', ys) = mapAccuml f s' xs + +-- Union of sets as lists. +union :: (Eq a) => [a] -> [a] -> [a] +union xs ys = xs ++ (ys \\ xs) + +-- Intersection of sets as lists. +intersection :: (Eq a) => [a] -> [a] -> [a] +intersection xs ys = [x | x<-xs, x `elem` ys] + +--- Functions derived from those above + +chopList :: ([a] -> (b, [a])) -> [a] -> [b] +chopList f l = unfoldr f null l + +assocDef :: (Eq a) => [(a, b)] -> b -> a -> b +assocDef l d x = assoc id d l x + +lookup :: (Eq a) => [(a, b)] -> a -> Maybe b +lookup l x = assoc Just Nothing l x diff --git a/progs/lib/hbc/ListUtil.hu b/progs/lib/hbc/ListUtil.hu new file mode 100644 index 0000000..7402cb7 --- /dev/null +++ b/progs/lib/hbc/ListUtil.hu @@ -0,0 +1,4 @@ +:output $LIBRARYBIN/ +:o= all +ListUtil.hs +Maybe.hu diff --git a/progs/lib/hbc/Maybe.hs b/progs/lib/hbc/Maybe.hs new file mode 100644 index 0000000..f0ada70 --- /dev/null +++ b/progs/lib/hbc/Maybe.hs @@ -0,0 +1,6 @@ +module Maybe(Maybe(..), thenM) where +-- Maybe together with Just and thenM forms a monad, but is more +-- by accident than by design. +data Maybe a = Nothing | Just a deriving (Eq, Ord, Text, Binary) +Nothing `thenM` _ = Nothing +Just a `thenM` f = f a diff --git a/progs/lib/hbc/Maybe.hu b/progs/lib/hbc/Maybe.hu new file mode 100644 index 0000000..a55b652 --- /dev/null +++ b/progs/lib/hbc/Maybe.hu @@ -0,0 +1,3 @@ +:output $LIBRARYBIN/ +:o= all +Maybe.hs diff --git a/progs/lib/hbc/Miranda.hs b/progs/lib/hbc/Miranda.hs new file mode 100644 index 0000000..2d863ce --- /dev/null +++ b/progs/lib/hbc/Miranda.hs @@ -0,0 +1,90 @@ +module Miranda(cjustify, lay, layn, limit, ljustify, merge, rep, rjustify, spaces, + {-force,seq,-}sort) where +--import UnsafeDirty +import QSort + +cjustify :: Int -> String -> String +cjustify n s = spaces l ++ s ++ spaces r + where + m = n - length s + l = m `div` 2 + r = m - l + +{- +index :: [a] -> [Int] +index xs = f xs 0 + where f [] n = [] + f (_:xs) n = n : f xs (n+1) +-} + +lay :: [String] -> String +lay = concat . map (++"\n") + +layn :: [String] -> String +layn = concat . zipWith f [1..] + where + f :: Int -> String -> String + f n x = rjustify 4 (show n) ++ ") " ++ x ++ "\n" + +limit :: (Eq a) => [a] -> a +limit (x:y:ys) | x == y = x + | otherwise = limit (y:ys) +limit _ = error "Miranda.limit: bad use" + +ljustify :: Int -> String -> String +ljustify n s = s ++ spaces (n - length s) + +merge :: (Ord a) => [a] -> [a] -> [a] +merge [] ys = ys +merge xs [] = xs +merge xxs@(x:xs) yys@(y:ys) | x <= y = x : merge xs yys + | otherwise = y : merge xxs ys + +rep :: Int -> b -> [b] +rep n x = take n (repeat x) + +rjustify :: Int -> String -> String +rjustify n s = spaces (n - length s) ++ s + +spaces :: Int -> String +spaces 0 = "" +spaces n = ' ' : spaces (n-1) + +------------- + +arctan x = atan x +code c = ord c +converse f a b = flip f a b +decode n = chr n +digit c = isDigit c +e :: (Floating a) => a +e = exp 1 +entier x = floor x +filemode f = error "Miranda.filemode" +--getenv +hd xs = head xs +hugenum :: (Floating a) => a +hugenum = error "hugenum" --!!! +integer x = x == truncate x +letter c = isAlpha c +map2 f xs ys = zipWith f xs ys +--max +max2 x y = max x y +member xs x = x `elem` xs +--min +min2 x y = min x y +mkset xs = nub xs +neg x = negate x +numval :: (Num a) => String -> a +numval cs = read cs +postfix xs x = xs ++ [x] +--read +scan f z l = scanl f z l +--shownum !!! +--showfloat !!! +--showscaled !!! +tinynum :: (Floating a) => a +tinynum = error "tinynum" +undef = error "undefined" +zip2 xs ys = zip xs ys +--zip diff --git a/progs/lib/hbc/Miranda.hu b/progs/lib/hbc/Miranda.hu new file mode 100644 index 0000000..cfa86ed --- /dev/null +++ b/progs/lib/hbc/Miranda.hu @@ -0,0 +1,4 @@ +:output $LIBRARYBIN/ +:o= all +Miranda.hs +QSort.hu diff --git a/progs/lib/hbc/Option.hs b/progs/lib/hbc/Option.hs new file mode 100644 index 0000000..a4b2423 --- /dev/null +++ b/progs/lib/hbc/Option.hs @@ -0,0 +1,3 @@ +module Option(Option(..), thenO) where +import Maybe renaming (Maybe to Option, Nothing to None, Just to Some, thenM to thenO) + diff --git a/progs/lib/hbc/Option.hu b/progs/lib/hbc/Option.hu new file mode 100644 index 0000000..592a0cd --- /dev/null +++ b/progs/lib/hbc/Option.hu @@ -0,0 +1,3 @@ +:output $LIBRARYBIN/ +:o= all +Option.hs diff --git a/progs/lib/hbc/Pretty.hs b/progs/lib/hbc/Pretty.hs new file mode 100644 index 0000000..ad63dbe --- /dev/null +++ b/progs/lib/hbc/Pretty.hs @@ -0,0 +1,50 @@ +module Pretty(text, separate, nest, pretty, (~.), (^.), IText(..), Context(..)) where +infixr 8 ~. +infixr 8 ^. + +type IText = Context -> [String] +type Context = (Bool,Int,Int,Int) + +text :: String -> IText +text s (v,w,m,m') = [s] + +(~.) :: IText -> IText -> IText +(~.) d1 d2 (v,w,m,m') = + let t = d1 (False,w,m,m') + tn = last t + indent = length tn + sig = if length t == 1 + then m' + indent + else length (dropWhile (==' ') tn) + (l:ls) = d2 (False,w-indent,m,sig) + in init t ++ + [tn ++ l] ++ + map (space indent++) ls + +space :: Int -> String +space n = [' ' | i<-[1..n]] + +(^.) :: IText -> IText -> IText +(^.) d1 d2 (v,w,m,m') = d1 (True,w,m,m') ++ d2 (True,w,m,0) + +separate :: [IText] -> IText +separate [] _ = [""] +separate ds (v,w,m,m') = + let hor = foldr1 (\d1 d2 -> d1 ~. text " " ~. d2) ds + ver = foldr1 (^.) ds + t = hor (v,w,m,m') + in if fits 1 t && fits (w `min` m-m') (head t) + then t + else ver (v,w,m,m') + +fits n xs = length xs <= n `max` 0 --null (drop n xs) + +nest :: Int -> IText -> IText +nest n d (v,w,m,m') = + if v then + map (space n++) (d (v,w-n,m,if m'==0 then 0 else m'+n)) + else + d (v,w,m,m') + +pretty :: Int->Int->IText->String +pretty w m d = concat (map (++"\n") (d (False,w,m,0))) diff --git a/progs/lib/hbc/Printf.hs b/progs/lib/hbc/Printf.hs new file mode 100644 index 0000000..c8291bd --- /dev/null +++ b/progs/lib/hbc/Printf.hs @@ -0,0 +1,150 @@ +-- This code used a function in the lml library (fmtf) that I don't have. +-- If someone makes this work for floats let me know -- jcp +-- +-- A C printf like formatter. +-- Conversion specs: +-- - left adjust +-- num field width +-- . separates width from precision +-- Formatting characters: +-- c Char, Int, Integer +-- d Char, Int, Integer +-- o Char, Int, Integer +-- x Char, Int, Integer +-- u Char, Int, Integer +-- f Float, Double +-- g Float, Double +-- e Float, Double +-- s String +-- +module Printf(UPrintf(..), printf) where + +-- import LMLfmtf + +data UPrintf = UChar Char | + UString String | + UInt Int | + UInteger Integer | + UFloat Float | + UDouble Double + +printf :: String -> [UPrintf] -> String +printf "" [] = "" +printf "" (_:_) = fmterr +printf ('%':_) [] = argerr +printf ('%':cs) us@(_:_) = fmt cs us +printf (c:cs) us = c:printf cs us + +fmt :: String -> [UPrintf] -> String +fmt cs us = + let (width, prec, ladj, zero, cs', us') = getSpecs False False cs us + adjust (pre, str) = + let lstr = length str + lpre = length pre + fill = if lstr+lpre < width then take (width-(lstr+lpre)) (repeat (if zero then '0' else ' ')) else "" + in if ladj then pre ++ str ++ fill else pre ++ fill ++ str + in + case cs' of + [] -> fmterr + c:cs'' -> + case us' of + [] -> argerr + u:us'' -> + (case c of + 'c' -> adjust ("", [chr (toint u)]) + 'd' -> adjust (fmti u) + 'x' -> adjust ("", fmtu 16 u) + 'o' -> adjust ("", fmtu 8 u) + 'u' -> adjust ("", fmtu 10 u) + '%' -> "%" + 'e' -> adjust (dfmt c prec (todbl u)) + 'f' -> adjust (dfmt c prec (todbl u)) + 'g' -> adjust (dfmt c prec (todbl u)) + 's' -> adjust ("", tostr u) + c -> perror ("bad formatting char " ++ [c]) + ) ++ printf cs'' us'' +unimpl = perror "unimplemented" + +fmti (UInt i) = if i < 0 then + if i == -i then fmti (UInteger (toInteger i)) else ("-", itos (-i)) + else + ("", itos i) +fmti (UInteger i) = if i < 0 then ("-", itos (-i)) else ("", itos i) +fmti (UChar c) = fmti (UInt (ord c)) +fmti u = baderr + +fmtu b (UInt i) = if i < 0 then + if i == -i then itosb b (maxi - toInteger (i+1) - 1) else itosb b (maxi - toInteger (-i)) + else + itosb b (toInteger i) +fmtu b (UInteger i) = itosb b i +fmtu b (UChar c) = itosb b (toInteger (ord c)) +fmtu b u = baderr + +maxi :: Integer +maxi = (toInteger maxInt + 1) * 2 + +toint (UInt i) = i +toint (UInteger i) = toInt i +toint (UChar c) = ord c +toint u = baderr + +tostr (UString s) = s +tostr u = baderr + +todbl (UDouble d) = d +todbl (UFloat f) = fromRational (toRational f) +todbl u = baderr + +itos n = + if n < 10 then + [chr (ord '0' + toInt n)] + else + let (q, r) = quotRem n 10 in + itos q ++ [chr (ord '0' + toInt r)] + +chars = array (0,15) (zipWith (:=) [0..] "0123456789abcdef") +itosb :: Integer -> Integer -> String +itosb b n = + if n < b then + [chars!n] + else + let (q, r) = quotRem n b in + itosb b q ++ [chars!r] + +stoi :: Int -> String -> (Int, String) +stoi a (c:cs) | isDigit c = stoi (a*10 + ord c - ord '0') cs +stoi a cs = (a, cs) + +getSpecs :: Bool -> Bool -> String -> [UPrintf] -> (Int, Int, Bool, Bool, String, [UPrintf]) +getSpecs l z ('-':cs) us = getSpecs True z cs us +getSpecs l z ('0':cs) us = getSpecs l True cs us +getSpecs l z ('*':cs) us = unimpl +getSpecs l z cs@(c:_) us | isDigit c = + let (n, cs') = stoi 0 cs + (p, cs'') = case cs' of + '.':r -> stoi 0 r + _ -> (-1, cs') + in (n, p, l, z, cs'', us) +getSpecs l z cs us = (0, -1, l, z, cs, us) + +-- jcp: I don't know what the lml function fmtf does. Someone needs to +-- rewrite this. + +{- +dfmt c p d = + case fmtf ("1" ++ (if p < 0 then "" else '.':itos p) ++ [c]) d of + '-':cs -> ("-", cs) + cs -> ("" , cs) +-} +dfmt = error "fmtf not implemented" + +perror s = error ("Printf.printf: "++s) +fmterr = perror "formatting string ended prematurely" +argerr = perror "argument list ended prematurely" +baderr = perror "bad argument" + +-- This is needed because standard Haskell does not have toInt + +toInt :: Integral a => a -> Int +toInt x = fromIntegral x diff --git a/progs/lib/hbc/Printf.hu b/progs/lib/hbc/Printf.hu new file mode 100644 index 0000000..d94f5b1 --- /dev/null +++ b/progs/lib/hbc/Printf.hu @@ -0,0 +1,3 @@ +:output $LIBRARYBIN/ +:o= all +Printf.hs diff --git a/progs/lib/hbc/QSort.hs b/progs/lib/hbc/QSort.hs new file mode 100644 index 0000000..f19eb43 --- /dev/null +++ b/progs/lib/hbc/QSort.hs @@ -0,0 +1,47 @@ +{- + This module implements a sort function using a variation on + quicksort. It is stable, uses no concatenation and compares + only with <=. + + sortLe sorts with a given predicate + sort uses the <= method + + Author: Lennart Augustsson +-} + +module QSort(sortLe, sort) where +sortLe :: (a -> a -> Bool) -> [a] -> [a] +sortLe le l = qsort le l [] + +sort :: (Ord a) => [a] -> [a] +sort l = qsort (<=) l [] + +-- qsort is stable and does not concatenate. +qsort le [] r = r +qsort le [x] r = x:r +qsort le (x:xs) r = qpart le x xs [] [] r + +-- qpart partitions and sorts the sublists +qpart le x [] rlt rge r = + -- rlt and rge are in reverse order and must be sorted with an + -- anti-stable sorting + rqsort le rlt (x:rqsort le rge r) +qpart le x (y:ys) rlt rge r = + if le x y then + qpart le x ys rlt (y:rge) r + else + qpart le x ys (y:rlt) rge r + +-- rqsort is as qsort but anti-stable, i.e. reverses equal elements +rqsort le [] r = r +rqsort le [x] r = x:r +rqsort le (x:xs) r = rqpart le x xs [] [] r + +rqpart le x [] rle rgt r = + qsort le rle (x:qsort le rgt r) +rqpart le x (y:ys) rle rgt r = + if le y x then + rqpart le x ys (y:rle) rgt r + else + rqpart le x ys rle (y:rgt) r + diff --git a/progs/lib/hbc/QSort.hu b/progs/lib/hbc/QSort.hu new file mode 100644 index 0000000..9a07dd1 --- /dev/null +++ b/progs/lib/hbc/QSort.hu @@ -0,0 +1,3 @@ +:output $LIBRARYBIN/ +:o= all +QSort.hs diff --git a/progs/lib/hbc/README b/progs/lib/hbc/README new file mode 100644 index 0000000..c51452a --- /dev/null +++ b/progs/lib/hbc/README @@ -0,0 +1,97 @@ +These libraries are adapted from the lml library. Also included are a number +of Common Lisp functions. + +The hbc library contains the following modules and functions: + +* module Either + binary sum data type + data Either a b = Left a | Right b + constructor Left typically used for errors + +* module Option + type for success or failure + data Option a = None | Some a + thenO :: Option a -> (a -> Option b) -> Option b apply a function that may fail + + +* module ListUtil + Various useful functions involving lists that are missing from the Prelude + assoc :: (Eq c) => (a -> b) -> b -> [(c, a)] -> c -> b + assoc f d l k looks for k in the association list l, if it is found f is applied to the value, otherwise d is returned + concatMap :: (a -> [b]) -> [a] -> [b] + flattening map (LMLs concmap) + unfoldr :: (a -> (b, a)) -> (a -> Bool) -> a -> [b] + unfoldr f p x repeatedly applies f to x until (p x) holds. (f x) should give a list element and a new x + mapAccuml :: (a -> b -> (a, c)) -> a -> [b] -> (a, [c]) + mapAccuml f s l maps f over l, but also threads the state s though (LMLs mapstate) + union :: (Eq a) => [a] -> [a] -> [a] + unions of two lists + intersection :: (Eq a) => [a] -> [a] -> [a] + intersection of two lists + chopList :: ([a] -> (b, [a])) -> [a] -> [b] + LMLs choplist + assocDef :: (Eq a) => [(a, b)] -> b -> a -> b + LMLs assocdef + lookup :: (Eq a) => [(a, b)] -> a -> Option b + lookup l k looks for the key k in the association list l and returns an optional value + +* module Pretty + John Hughes pretty printing library. + type Context = (Bool, Int, Int, Int) + type IText = Context -> [String] + text :: String -> IText just text + (~.) :: IText -> IText -> IText horizontal composition + (^.) :: IText -> IText -> IText vertical composition + separate :: [IText] -> IText separate by spaces + nest :: Int -> IText -> IText indent + pretty :: Int -> Int -> IText -> String format it + +* module QSort + Sort function using quicksort. + sortLe :: (a -> a -> Bool) -> [a] -> [a] sort le l sorts l with le as less than predicate + sort :: (Ord a) => [a] -> [a] sort l sorts l using the Ord class + +* module Random + Random numbers. + randomInts :: Int -> Int -> [Int] given two seeds gives a list of random Int + randomDoubles :: Int -> Int -> [Double] given two seeds gives a list of random Double + +* module RunDialogue + Test run programs of type Dialogue. + Only a few Requests are implemented, unfortunately not ReadChannel. + run :: Dialogue -> String just run the program, showing the output + runTrace :: Dialogue -> String run the program, showing each Request and Response + +* module Miranda + Functions found in the Miranda(tm) library. + +* module Printf + C printf style formatting. Handles same types as printf in C, but requires the arguments + to be tagged. Useful for formatting of floating point values. + data UPrintf = UChar Char | UString String | UInt Int | UInteger Integer | UFloat Float | UDouble Double + printf :: String -> [UPrintf] -> String convert arguments in the list according to the formatting string + + +* module Time + Manipulate time values (a Double with seconds since 1970). + -- year mon day hour min sec dec-sec weekday + data Time = Time Int Int Int Int Int Int Double Int + dblToTime :: Double -> Time convert a Double to a Time + timeToDbl :: Time -> Double convert a Time to a Double + timeToString :: Time -> String convert a Time to a readable String + +----- To add: + +Bytes +IO Library +Word oprtations +Time clock stuff +Lisp stuff: symbols + hashtables + strings + + + + + + diff --git a/progs/lib/hbc/Random.hs b/progs/lib/hbc/Random.hs new file mode 100644 index 0000000..269d6af --- /dev/null +++ b/progs/lib/hbc/Random.hs @@ -0,0 +1,52 @@ +{- + This module implements a (good) random number generator. + + The June 1988 (v31 #6) issue of the Communications of the ACM has an + article by Pierre L'Ecuyer called, "Efficient and Portable Combined + Random Number Generators". Here is the Portable Combined Generator of + L'Ecuyer for 32-bit computers. It has a period of roughly 2.30584e18. + + Transliterator: Lennart Augustsson +-} + +module Random(randomInts, randomDoubles) where +-- Use seeds s1 in 1..2147483562 and s2 in 1..2147483398 to generate +-- an infinite list of random Ints. +randomInts :: Int -> Int -> [Int] +randomInts s1 s2 = + if 1 <= s1 && s1 <= 2147483562 then + if 1 <= s2 && s2 <= 2147483398 then + rands s1 s2 + else + error "randomInts: Bad second seed." + else + error "randomInts: Bad first seed." + +rands :: Int -> Int -> [Int] +rands s1 s2 = + let + k = s1 `div` 53668 + s1' = 40014 * (s1 - k * 53668) - k * 12211 + s1'' = if s1' < 0 then s1' + 2147483563 else s1' + + k' = s2 `div` 52774 + s2' = 40692 * (s2 - k' * 52774) - k' * 3791 + s2'' = if s2' < 0 then s2' + 2147483399 else s2' + + z = s1'' - s2'' +{- + z' = if z < 1 then z + 2147483562 else z + + in z' : rands s1'' s2'' +-} +-- Use this instead; it is a little stricter and generates much better code + in if z < 1 then z + 2147483562 : rands s1'' s2'' + else z : rands s1'' s2'' + +-- For those of you who don't have fromInt +fromInt = fromInteger . toInteger + +-- Same values for s1 and s2 as above, generates an infinite +-- list of Doubles uniformly distibuted in (0,1). +randomDoubles :: Int -> Int -> [Double] +randomDoubles s1 s2 = map (\x -> fromInt x * 4.6566130638969828e-10) (randomInts s1 s2) diff --git a/progs/lib/hbc/Random.hu b/progs/lib/hbc/Random.hu new file mode 100644 index 0000000..9fff34e --- /dev/null +++ b/progs/lib/hbc/Random.hu @@ -0,0 +1,3 @@ +:output $LIBRARYBIN/ +:o= all +Random.hs diff --git a/progs/lib/hbc/Time.hs b/progs/lib/hbc/Time.hs new file mode 100644 index 0000000..29f3441 --- /dev/null +++ b/progs/lib/hbc/Time.hs @@ -0,0 +1,51 @@ +module Time(Time(..), dblToTime, timeToDbl, timeToString) where +-- year mon day hour min sec ... wday +data Time = Time Int Int Int Int Int Int Double Int deriving (Eq, Ord, Text) + +isleap :: Int -> Bool +isleap n = n `rem` 4 == 0 -- good enough for the UNIX time span + +daysin :: Int -> Int +daysin n = if isleap n then 366 else 365 + +monthlen :: Array (Bool, Int) Int +monthlen = array ((False, 1), (True, 12)) (zipWith3 (\ a b c -> (a,b):=c) (repeat False) [1..] [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ++ + zipWith3 (\ a b c -> (a,b):=c) (repeat True) [1..] [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]) + +-- Time zone offset in minutes +tzOffset = 120 -- Swedish DST + +dblToTime :: Double -> Time +dblToTime d = + let t = truncate d :: Int + offset = tzOffset -- timezone + (days, rem) = (t+offset*60) `quotRem` (60*60*24) + (hour, rem') = rem `quotRem` (60*60) + (min, sec) = rem' `quotRem` 60 + wday = (days+3) `mod` 7 + (year, days')= until (\ (y, d) -> d < daysin y) (\ (y, d) -> (y+1, d - daysin y)) (1970, days) + (mon, day) = until (\ (m, d) -> d <= monthlen!(isleap year, m)) (\ (m, d) -> (m+1, d - monthlen!(isleap year, m))) (1, days') + in Time year mon (day+1) hour min sec (d - fromInt t) wday + +timeToDbl :: Time -> Double +timeToDbl (Time year mon day hour min sec sdec _) = + let year' = year - 1970 + offset = tzOffset -- timezone + days = year' * 365 + (year'+1) `div` 4 + + sum [monthlen!(isleap year, m) | m<-[1..mon-1]] + day - 1 + secs = ((days*24 + hour) * 60 + min - offset) * 60 + sec + in fromInt secs + sdec + +show2 :: Int -> String +show2 x = [chr (x `quot` 10 + ord '0'), chr (x `rem` 10 + ord '0')] + +weekdays = ["Mon","Tue","Wen","Thu","Fri","Sat","Sun"] + +timeToString :: Time -> String +timeToString (Time year mon day hour min sec sdec wday) = + show year ++ "-" ++ show2 mon ++ "-" ++ show2 day ++ " " ++ + show2 hour ++ ":" ++ show2 min ++ ":" ++ show2 sec ++ + tail (take 5 (show sdec)) ++ " " ++ weekdays!!wday + +-- For those of you who don't have fromInt +fromInt = fromInteger . toInteger diff --git a/progs/lib/hbc/Time.hu b/progs/lib/hbc/Time.hu new file mode 100644 index 0000000..01c8f64 --- /dev/null +++ b/progs/lib/hbc/Time.hu @@ -0,0 +1,3 @@ +:output $LIBRARYBIN/ +:o= all +Time.hs diff --git a/progs/prelude/Prelude.hs b/progs/prelude/Prelude.hs new file mode 100644 index 0000000..bf20849 --- /dev/null +++ b/progs/prelude/Prelude.hs @@ -0,0 +1,187 @@ +-- Standard value bindings + +module Prelude ( + PreludeCore.., PreludeRatio.., PreludeComplex.., PreludeList.., + PreludeArray.., PreludeText.., PreludeIO.., + nullBin, isNullBin, appendBin, + (&&), (||), not, otherwise, + minChar, maxChar, ord, chr, + isAscii, isControl, isPrint, isSpace, + isUpper, isLower, isAlpha, isDigit, isAlphanum, + toUpper, toLower, + minInt, maxInt, subtract, gcd, lcm, (^), (^^), + fromIntegral, fromRealFrac, atan2, + fst, snd, id, const, (.), flip, ($), until, asTypeOf, error ) where + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +import PreludePrims + +import PreludeCore +import PreludeList +import PreludeArray +import PreludeRatio +import PreludeComplex +import PreludeText +import PreludeIO + +infixr 9 . +infixr 8 ^, ^^ +infixr 3 && +infixr 2 || +infixr 0 $ + + +-- Binary functions + +nullBin :: Bin +nullBin = primNullBin + +isNullBin :: Bin -> Bool +isNullBin = primIsNullBin + +appendBin :: Bin -> Bin -> Bin +appendBin = primAppendBin + +-- Boolean functions + +(&&), (||) :: Bool -> Bool -> Bool +True && x = x +False && _ = False +True || _ = True +False || x = x + +not :: Bool -> Bool +not True = False +not False = True + +{-# (&&) :: Inline #-} +{-# (||) :: Inline #-} +{-# not :: Inline #-} + + +otherwise :: Bool +otherwise = True + +-- Character functions + +minChar, maxChar :: Char +minChar = '\0' +maxChar = '\255' + +ord :: Char -> Int +ord = primCharToInt + +chr :: Int -> Char +chr = primIntToChar + +isAscii, isControl, isPrint, isSpace :: Char -> Bool +isUpper, isLower, isAlpha, isDigit, isAlphanum :: Char -> Bool + +isAscii c = ord c < 128 +isControl c = c < ' ' || c == '\DEL' +isPrint c = c >= ' ' && c <= '~' +isSpace c = c == ' ' || c == '\t' || c == '\n' || + c == '\r' || c == '\f' || c == '\v' +isUpper c = c >= 'A' && c <= 'Z' +isLower c = c >= 'a' && c <= 'z' +isAlpha c = isUpper c || isLower c +isDigit c = c >= '0' && c <= '9' +isAlphanum c = isAlpha c || isDigit c + + +toUpper, toLower :: Char -> Char +toUpper c | isLower c = chr ((ord c - ord 'a') + ord 'A') + | otherwise = c + +toLower c | isUpper c = chr ((ord c - ord 'A') + ord 'a') + | otherwise = c + +-- Numeric functions + +minInt, maxInt :: Int +minInt = primMinInt +maxInt = primMaxInt + +subtract :: (Num a) => a -> a -> a +subtract = flip (-) + +gcd :: (Integral a) => a -> a -> a +gcd 0 0 = error "gcd{Prelude}: gcd 0 0 is undefined" +gcd x y = gcd' (abs x) (abs y) + where gcd' x 0 = x + gcd' x y = gcd' y (x `rem` y) + +lcm :: (Integral a) => a -> a -> a +lcm _ 0 = 0 +lcm 0 _ = 0 +lcm x y = abs ((x `quot` (gcd x y)) * y) + +(^) :: (Num a, Integral b) => a -> b -> a +x ^ 0 = 1 +x ^ (n+1) = f x n x + where f _ 0 y = y + f x n y = g x n where + g x n | even n = g (x*x) (n `quot` 2) + | otherwise = f x (n-1) (x*y) +_ ^ _ = error "(^){Prelude}: negative exponent" + +(^^) :: (Fractional a, Integral b) => a -> b -> a +x ^^ n = if n >= 0 then x^n else recip (x^(-n)) + +fromIntegral :: (Integral a, Num b) => a -> b +fromIntegral = fromInteger . toInteger + +fromRealFrac :: (RealFrac a, Fractional b) => a -> b +fromRealFrac = fromRational . toRational + +atan2 :: (RealFloat a) => a -> a -> a +atan2 y x = case (signum y, signum x) of + ( 0, 1) -> 0 + ( 1, 0) -> pi/2 + ( 0,-1) -> pi + (-1, 0) -> -pi/2 + ( _, 1) -> atan (y/x) + ( _,-1) -> atan (y/x) + pi + ( 0, 0) -> error "atan2{Prelude}: atan2 of origin" + + +-- Some standard functions: +-- component projections for pairs: +fst :: (a,b) -> a +fst (x,y) = x + +snd :: (a,b) -> b +snd (x,y) = y + +-- identity function +id :: a -> a +id x = x + +-- constant function +const :: a -> b -> a +const x _ = x + +-- function composition +(.) :: (b -> c) -> (a -> b) -> a -> c +f . g = \ x -> f (g x) + +-- flip f takes its (first) two arguments in the reverse order of f. +flip :: (a -> b -> c) -> b -> a -> c +flip f x y = f y x + +-- right-associating infix application operator (useful in continuation- +-- passing style) +($) :: (a -> b) -> a -> b +f $ x = f x + +-- until p f yields the result of applying f until p holds. +until :: (a -> Bool) -> (a -> a) -> a -> a +until p f x | p x = x + | otherwise = until p f (f x) + +-- asTypeOf is a type-restricted version of const. It is usually used +-- as an infix operator, and its typing forces its first argument +-- (which is usually overloaded) to have the same type as the second. +asTypeOf :: a -> a -> a +asTypeOf = const diff --git a/progs/prelude/Prelude.hu b/progs/prelude/Prelude.hu new file mode 100644 index 0000000..1ee32ca --- /dev/null +++ b/progs/prelude/Prelude.hu @@ -0,0 +1,16 @@ +:output $PRELUDEBIN/Prelude +:stable +:prelude +PreludePrims.hu +PreludeArrayPrims.hu +PreludeTuplePrims.hu +PreludeIOPrims.hu +Prelude.hs +PreludeArray.hs +PreludeComplex.hs +PreludeCore.hs +PreludeIO.hs +PreludeList.hs +PreludeRatio.hs +PreludeText.hs +PreludeTuple.hs diff --git a/progs/prelude/PreludeArray.hs b/progs/prelude/PreludeArray.hs new file mode 100644 index 0000000..a501631 --- /dev/null +++ b/progs/prelude/PreludeArray.hs @@ -0,0 +1,201 @@ +module PreludeArray ( Array, Assoc((:=)), array, listArray, (!), bounds, + indices, elems, assocs, accumArray, (//), accum, amap, + ixmap + ) where + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +-- This module uses some simple techniques with updatable vectors to +-- avoid vector copying in loops where single threading is obvious. +-- This is rather fragile and depends on the way the compiler handles +-- strictness. + +import PreludeBltinArray + +infixl 9 ! +infixl 9 // +infix 1 := + +data Assoc a b = a := b deriving (Eq, Ord, Ix, Text, Binary) +data (Ix a) => Array a b = MkArray (a,a) {-#STRICT#-} + (Vector (Box b)) {-#STRICT#-} + deriving () + +array :: (Ix a) => (a,a) -> [Assoc a b] -> Array a b +listArray :: (Ix a) => (a,a) -> [b] -> Array a b +(!) :: (Ix a) => Array a b -> a -> b +bounds :: (Ix a) => Array a b -> (a,a) +indices :: (Ix a) => Array a b -> [a] +elems :: (Ix a) => Array a b -> [b] +assocs :: (Ix a) => Array a b -> [Assoc a b] +accumArray :: (Ix a) => (b -> c -> b) -> b -> (a,a) -> [Assoc a c] + -> Array a b +(//) :: (Ix a) => Array a b -> [Assoc a b] -> Array a b +accum :: (Ix a) => (b -> c -> b) -> Array a b -> [Assoc a c] + -> Array a b +amap :: (Ix a) => (b -> c) -> Array a b -> Array a c +ixmap :: (Ix a, Ix b) => (a,a) -> (a -> b) -> Array b c + -> Array a c + +-- Arrays are a datatype containing a bounds pair and a vector of values. +-- Uninitialized array elements contain an error value. + +-- Primitive vectors now contain only unboxed values. This permits us to +-- treat array indexing as an atomic operation without forcing the element +-- being accessed. The boxing and unboxing of array elements happens +-- explicitly using these operations: + +data Box a = MkBox a +unBox (MkBox x) = x +{-# unBox :: Inline #-} + + +-- Array construction and update using index/value associations share +-- the same helper function. + +array b@(bmin, bmax) ivs = + let size = (index b bmax) + 1 + v = primMakeVector size uninitializedArrayError + in (MkArray b (updateArrayIvs b v ivs)) +{-# array :: Inline #-} + +a@(MkArray b v) // ivs = + let v' = primCopyVector v + in (MkArray b (updateArrayIvs b v' ivs)) +{-# (//) :: Inline #-} + +updateArrayIvs b v ivs = + let g (i := x) next = strict1 (primVectorUpdate v (index b i) (MkBox x)) + next + in foldr g v ivs +{-# updateArrayIvs :: Inline #-} + +uninitializedArrayError = + MkBox (error "(!){PreludeArray}: uninitialized array element.") + + +-- when mapping a list onto an array, be smart and don't do full index +-- computation + +listArray b@(bmin, bmax) vs = + let size = (index b bmax) + 1 + v = primMakeVector size uninitializedArrayError + in (MkArray b (updateArrayVs size v vs)) +{-# listArray :: Inline #-} + +updateArrayVs size v vs = + let g x next j = if (j == size) + then v + else strict1 (primVectorUpdate v j (MkBox x)) + (next (j + 1)) + in foldr g (\ _ -> v) vs 0 +{-# updateArrayVs :: Inline #-} + + +-- Array access + +a@(MkArray b v) ! i = unBox (primVectorSel v (index b i)) +{-# (!) :: Inline #-} + +bounds (MkArray b _) = b + +indices = range . bounds + + +-- Again, when mapping array elements into a list, be smart and don't do +-- the full index computation for every element. + +elems a@(MkArray b@(bmin, bmax) v) = + build (\ c n -> + let size = (index b bmax) + 1 + g j = if (j == size) + then n + else c (unBox (primVectorSel v j)) (g (j + 1)) + -- This strict1 is so size doesn't get inlined and recomputed + -- at every iteration. It should also force the array argument + -- to be strict. + in strict1 size (g 0)) +{-# elems :: Inline #-} + +assocs a@(MkArray b@(bmin, bmax) v) = + build (\ c n -> + let g i next j = let y = unBox (primVectorSel v j) + in c (i := y) (next (j + 1)) + in foldr g (\ _ -> n) (range b) 0) +{-# assocs :: Inline #-} + + +-- accum and accumArray share the same helper function. The difference is +-- that accum makes a copy of an existing array and accumArray creates +-- a new one with all elements initialized to the given value. + +accum f a@(MkArray b v) ivs = + let v' = primCopyVector v + in (MkArray b (accumArrayIvs f b v' ivs)) +{-# accum :: Inline #-} + +accumArray f z b@(bmin, bmax) ivs = + let size = (index b bmax) + 1 + v = primMakeVector size (MkBox z) + in (MkArray b (accumArrayIvs f b v ivs)) +{-# accumArray :: Inline #-} + + +-- This is a bit tricky. We need to force the access to the array element +-- before the update, but not force the thunk that is the value of the +-- array element unless f is strict. + +accumArrayIvs f b v ivs = + let g (i := x) next = + let j = index b i + y = primVectorSel v j + in strict1 + y + (strict1 (primVectorUpdate v j (MkBox (f (unBox y) x))) + next) + in foldr g v ivs +{-# accumArrayIvs :: Inline #-} + + +-- again, be smart and bypass full array indexing on array mapping + +amap f a@(MkArray b@(bmin, bmax) v) = + let size = (index b bmax) + 1 + v' = primMakeVector size uninitializedArrayError + g j = if (j == size) + then v' + else let y = primVectorSel v j + in strict1 (primVectorUpdate v' j (MkBox (f (unBox y)))) + (g (j + 1)) + in (MkArray b (g 0)) +{-# amap :: Inline #-} + + +-- can't bypass the index computation here since f needs it as an argument + +ixmap b f a = array b [i := a ! f i | i <- range b] +{-# ixmap :: Inline #-} + + +-- random other stuff + +instance (Ix a, Eq b) => Eq (Array a b) where + a == a' = assocs a == assocs a' + +instance (Ix a, Ord b) => Ord (Array a b) where + a <= a' = assocs a <= assocs a' + +instance (Ix a, Text a, Text b) => Text (Array a b) where + showsPrec p a = showParen (p > 9) ( + showString "array " . + shows (bounds a) . showChar ' ' . + shows (assocs a) ) + + readsPrec p = readParen (p > 9) + (\r -> [(array b as, u) | ("array",s) <- lex r, + (b,t) <- reads s, + (as,u) <- reads t ] + ++ + [(listArray b xs, u) | ("listArray",s) <- lex r, + (b,t) <- reads s, + (xs,u) <- reads t ]) diff --git a/progs/prelude/PreludeArrayPrims.hi b/progs/prelude/PreludeArrayPrims.hi new file mode 100644 index 0000000..a8529c0 --- /dev/null +++ b/progs/prelude/PreludeArrayPrims.hi @@ -0,0 +1,37 @@ +-- These primitives are used to implement arrays with constant time +-- access. There are destructive update routines for arrays for use +-- internally in functions such as array. These are impure but are +-- marked as pure to keep them out of the top level monad. This should +-- be redone using lambda-var someday. + +interface PreludeBltinArray where + + +data Vector a -- Used to represent vectors with delayed components +data Delay a -- An explicit represenation of a delayed object + + +-- Primitive vectors now always have strict components. This permits us +-- to treat array indexing as an atomic operation without the explicit +-- force on access. + +primVectorSel :: Vector a -> Int -> a +primVectorUpdate :: Vector a -> Int -> a -> a +primMakeVector :: Int -> a -> Vector a +primCopyVector :: Vector a -> Vector a + +-- These functions are used for explicit sequencing of destructive ops + +strict1 :: a -> b -> b +primForce :: Delay a -> a + +{-# +primVectorSel :: LispName("prim.vector-sel"), Complexity(1) +primVectorUpdate :: LispName("prim.vector-update"), Complexity(1) +primMakeVector :: LispName("prim.make-vector"), Complexity(4) +primCopyVector :: LispName("prim.copy-vector"), Complexity(5) +strict1 :: Strictness("S,N"), + LispName("prim.strict1") +primForce :: LispName("prim.force") +#-} + diff --git a/progs/prelude/PreludeArrayPrims.hu b/progs/prelude/PreludeArrayPrims.hu new file mode 100644 index 0000000..62ea8ac --- /dev/null +++ b/progs/prelude/PreludeArrayPrims.hu @@ -0,0 +1,4 @@ +:output $PRELUDEBIN/PreludeArrayPrims +:stable +:prelude +PreludeArrayPrims.hi diff --git a/progs/prelude/PreludeComplex.hs b/progs/prelude/PreludeComplex.hs new file mode 100644 index 0000000..2044129 --- /dev/null +++ b/progs/prelude/PreludeComplex.hs @@ -0,0 +1,94 @@ +-- Complex Numbers + +module PreludeComplex where + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +infixl 6 :+ + +data (RealFloat a) => Complex a = a {-#STRICT#-} :+ a {-#STRICT #-} + deriving (Eq,Binary,Text) + +instance (RealFloat a) => Num (Complex a) where + (x:+y) + (x':+y') = (x+x') :+ (y+y') + (x:+y) - (x':+y') = (x-x') :+ (y-y') + (x:+y) * (x':+y') = (x*x'-y*y') :+ (x*y'+y*x') + negate (x:+y) = negate x :+ negate y + abs z = magnitude z :+ 0 + signum 0 = 0 + signum z@(x:+y) = x/r :+ y/r where r = magnitude z + fromInteger n = fromInteger n :+ 0 + +instance (RealFloat a) => Fractional (Complex a) where + (x:+y) / (x':+y') = (x*x''+y*y'') / d :+ (y*x''-x*y'') / d + where x'' = scaleFloat k x' + y'' = scaleFloat k y' + k = - max (exponent x') (exponent y') + d = x'*x'' + y'*y'' + + fromRational a = fromRational a :+ 0 + +instance (RealFloat a) => Floating (Complex a) where + pi = pi :+ 0 + exp (x:+y) = expx * cos y :+ expx * sin y + where expx = exp x + log z = log (magnitude z) :+ phase z + + sqrt 0 = 0 + sqrt z@(x:+y) = u :+ (if y < 0 then -v else v) + where (u,v) = if x < 0 then (v',u') else (u',v') + v' = abs y / (u'*2) + u' = sqrt ((magnitude z + abs x) / 2) + + sin (x:+y) = sin x * cosh y :+ cos x * sinh y + cos (x:+y) = cos x * cosh y :+ (- sin x * sinh y) + tan (x:+y) = (sinx*coshy:+cosx*sinhy)/(cosx*coshy:+(-sinx*sinhy)) + where sinx = sin x + cosx = cos x + sinhy = sinh y + coshy = cosh y + + sinh (x:+y) = cos y * sinh x :+ sin y * cosh x + cosh (x:+y) = cos y * cosh x :+ sin y * sinh x + tanh (x:+y) = (cosy*sinhx:+siny*coshx)/(cosy*coshx:+siny*sinhx) + where siny = sin y + cosy = cos y + sinhx = sinh x + coshx = cosh x + + asin z@(x:+y) = y':+(-x') + where (x':+y') = log (((-y):+x) + sqrt (1 - z*z)) + acos z@(x:+y) = y'':+(-x'') + where (x'':+y'') = log (z + ((-y'):+x')) + (x':+y') = sqrt (1 - z*z) + atan z@(x:+y) = y':+(-x') + where (x':+y') = log (((1-y):+x) / sqrt (1+z*z)) + + asinh z = log (z + sqrt (1+z*z)) + acosh z = log (z + (z+1) * sqrt ((z-1)/(z+1))) + atanh z = log ((1+z) / sqrt (1-z*z)) + + +realPart, imagPart :: (RealFloat a) => Complex a -> a +realPart (x:+y) = x +imagPart (x:+y) = y + +conjugate :: (RealFloat a) => Complex a -> Complex a +conjugate (x:+y) = x :+ (-y) + +mkPolar :: (RealFloat a) => a -> a -> Complex a +mkPolar r theta = r * cos theta :+ r * sin theta + +cis :: (RealFloat a) => a -> Complex a +cis theta = cos theta :+ sin theta + +polar :: (RealFloat a) => Complex a -> (a,a) +polar z = (magnitude z, phase z) + +magnitude, phase :: (RealFloat a) => Complex a -> a +magnitude (x:+y) = scaleFloat k + (sqrt ((scaleFloat mk x)^2 + (scaleFloat mk y)^2)) + where k = max (exponent x) (exponent y) + mk = - k + +phase (x:+y) = atan2 y x diff --git a/progs/prelude/PreludeCore.hs b/progs/prelude/PreludeCore.hs new file mode 100644 index 0000000..f8a7be2 --- /dev/null +++ b/progs/prelude/PreludeCore.hs @@ -0,0 +1,817 @@ +-- Standard types, classes, and instances + +module PreludeCore ( + Eq((==), (/=)), + Ord((<), (<=), (>=), (>), max, min), + Num((+), (-), (*), negate, abs, signum, fromInteger), + Integral(quot, rem, div, mod, quotRem, divMod, even, odd, toInteger), + Fractional((/), recip, fromRational), + Floating(pi, exp, log, sqrt, (**), logBase, + sin, cos, tan, asin, acos, atan, + sinh, cosh, tanh, asinh, acosh, atanh), + Real(toRational), + RealFrac(properFraction, truncate, round, ceiling, floor), + RealFloat(floatRadix, floatDigits, floatRange, + encodeFloat, decodeFloat, exponent, significand, scaleFloat), + Ix(range, index, inRange), + Enum(enumFrom, enumFromThen, enumFromTo, enumFromThenTo), + Text(readsPrec, showsPrec, readList, showList), ReadS(..), ShowS(..), + Binary(readBin, showBin), +-- List type: [_]((:), []) +-- Tuple types: (_,_), (_,_,_), etc. +-- Trivial type: () + Bool(True, False), + Char, Int, Integer, Float, Double, Bin, + Ratio, Complex((:+)), Assoc((:=)), Array, + String(..), Rational(..) ) where + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +import PreludePrims +import PreludeText +import PreludeRatio(Ratio, Rational(..)) +import PreludeComplex(Complex((:+))) +import PreludeArray(Assoc((:=)), Array) +import PreludeIO({-Request, Response,-} IOError, + Dialogue(..), SuccCont(..), StrCont(..), + StrListCont(..), BinCont(..), FailCont(..)) + +infixr 8 ** +infixl 7 *, /, `quot`, `rem`, `div`, `mod` +infixl 6 +, - +infix 4 ==, /=, <, <=, >=, > + + +infixr 5 : + +data Int = MkInt +data Integer = MkInteger +data Float = MkFloat +data Double = MkDouble +data Char = MkChar +data Bin = MkBin +data List a = a : (List a) | Nil deriving (Eq, Ord) +data Arrow a b = MkArrow a b +data UnitType = UnitConstructor deriving (Eq, Ord, Ix, Enum, Binary) + +-- Equality and Ordered classes + +class Eq a where + (==), (/=) :: a -> a -> Bool + + x /= y = not (x == y) + +class (Eq a) => Ord a where + (<), (<=), (>=), (>):: a -> a -> Bool + max, min :: a -> a -> a + + x < y = x <= y && x /= y + x >= y = y <= x + x > y = y < x + + -- The following default methods are appropriate for partial orders. + -- Note that the second guards in each function can be replaced + -- by "otherwise" and the error cases, eliminated for total orders. + max x y | x >= y = x + | y >= x = y + |otherwise = error "max{PreludeCore}: no ordering relation" + min x y | x <= y = x + | y <= x = y + |otherwise = error "min{PreludeCore}: no ordering relation" + + +-- Numeric classes + +class (Eq a, Text a) => Num a where + (+), (-), (*) :: a -> a -> a + negate :: a -> a + abs, signum :: a -> a + fromInteger :: Integer -> a + + x - y = x + negate y + +class (Num a, Enum a) => Real a where + toRational :: a -> Rational + +class (Real a, Ix a) => Integral a where + quot, rem, div, mod :: a -> a -> a + quotRem, divMod :: a -> a -> (a,a) + even, odd :: a -> Bool + toInteger :: a -> Integer + + n `quot` d = q where (q,r) = quotRem n d + n `rem` d = r where (q,r) = quotRem n d + n `div` d = q where (q,r) = divMod n d + n `mod` d = r where (q,r) = divMod n d + divMod n d = if signum r == - signum d then (q-1, r+d) else qr + where qr@(q,r) = quotRem n d + even n = n `rem` 2 == 0 + odd = not . even + +class (Num a) => Fractional a where + (/) :: a -> a -> a + recip :: a -> a + fromRational :: Rational -> a + + recip x = 1 / x + +class (Fractional a) => Floating a where + pi :: a + exp, log, sqrt :: a -> a + (**), logBase :: a -> a -> a + sin, cos, tan :: a -> a + asin, acos, atan :: a -> a + sinh, cosh, tanh :: a -> a + asinh, acosh, atanh :: a -> a + + x ** y = exp (log x * y) + logBase x y = log y / log x + sqrt x = x ** 0.5 + tan x = sin x / cos x + tanh x = sinh x / cosh x + +class (Real a, Fractional a) => RealFrac a where + properFraction :: (Integral b) => a -> (b,a) + truncate, round :: (Integral b) => a -> b + ceiling, floor :: (Integral b) => a -> b + + truncate x = m where (m,_) = properFraction x + + round x = let (n,r) = properFraction x + m = if r < 0 then n - 1 else n + 1 + in case signum (abs r - 0.5) of + -1 -> n + 0 -> if even n then n else m + 1 -> m + + ceiling x = if r > 0 then n + 1 else n + where (n,r) = properFraction x + + floor x = if r < 0 then n - 1 else n + where (n,r) = properFraction x + +class (RealFrac a, Floating a) => RealFloat a where + floatRadix :: a -> Integer + floatDigits :: a -> Int + floatRange :: a -> (Int,Int) + decodeFloat :: a -> (Integer,Int) + encodeFloat :: Integer -> Int -> a + exponent :: a -> Int + significand :: a -> a + scaleFloat :: Int -> a -> a + + exponent x = if m == 0 then 0 else n + floatDigits x + where (m,n) = decodeFloat x + + significand x = encodeFloat m (- floatDigits x) + where (m,_) = decodeFloat x + + scaleFloat k x = encodeFloat m (n+k) + where (m,n) = decodeFloat x + + +-- Index and Enumeration classes + +class (Ord a, Text a) => Ix a where -- This is a Yale modification + range :: (a,a) -> [a] + index :: (a,a) -> a -> Int + inRange :: (a,a) -> a -> Bool + +class (Ord a) => Enum a where + enumFrom :: a -> [a] -- [n..] + enumFromThen :: a -> a -> [a] -- [n,n'..] + enumFromTo :: a -> a -> [a] -- [n..m] + enumFromThenTo :: a -> a -> a -> [a] -- [n,n'..m] + + enumFromTo = defaultEnumFromTo + enumFromThenTo = defaultEnumFromThenTo + +defaultEnumFromTo n m = takeWhile (<= m) (enumFrom n) +defaultEnumFromThenTo n n' m + = takeWhile (if n' >= n then (<= m) else (>= m)) + (enumFromThen n n') +{-# defaultEnumFromTo :: Inline #-} +{-# defaultEnumFromThenTo :: Inline #-} + +-- Text class + +type ReadS a = String -> [(a,String)] +type ShowS = String -> String + +class Text a where + readsPrec :: Int -> ReadS a + showsPrec :: Int -> a -> ShowS + readList :: ReadS [a] + showList :: [a] -> ShowS + + readList = readParen False (\r -> [pr | ("[",s) <- lex r, + pr <- readl s]) + where readl s = [([],t) | ("]",t) <- lex s] ++ + [(x:xs,u) | (x,t) <- reads s, + (xs,u) <- readl' t] + readl' s = [([],t) | ("]",t) <- lex s] ++ + [(x:xs,v) | (",",t) <- lex s, + (x,u) <- reads t, + (xs,v) <- readl' u] + showList [] = showString "[]" + showList (x:xs) + = showChar '[' . shows x . showl xs + where showl [] = showChar ']' + showl (x:xs) = showString ", " . shows x . showl xs + + + +-- Binary class + +class Binary a where + readBin :: Bin -> (a,Bin) + showBin :: a -> Bin -> Bin + + +-- Trivial type + +-- data () = () deriving (Eq, Ord, Ix, Enum, Binary) + +instance Text () where + readsPrec p = readParen False + (\r -> [((),t) | ("(",s) <- lex r, + (")",t) <- lex s ] ) + showsPrec p () = showString "()" + + +-- Binary type + +instance Text Bin where + readsPrec p s = error "readsPrec{PreludeText}: Cannot read Bin." + showsPrec p b = showString "<>" + + +-- Boolean type + +data Bool = False | True deriving (Eq, Ord, Ix, Enum, Text, Binary) + + +-- Character type + +instance Eq Char where + (==) = primEqChar + (/=) = primNeqChar + +instance Ord Char where + (<) = primLsChar + (<=) = primLeChar + (>) = primGtChar + (>=) = primGeChar + +instance Ix Char where + range (c,c') = [c..c'] + index b@(c,c') ci + | inRange b ci = ord ci - ord c + | otherwise = error "index{PreludeCore}: Index out of range." + inRange (c,c') ci = ord c <= i && i <= ord c' + where i = ord ci + {-# range :: Inline #-} + +instance Enum Char where + enumFrom = charEnumFrom + enumFromThen = charEnumFromThen + enumFromTo = defaultEnumFromTo + enumFromThenTo = defaultEnumFromThenTo + {-# enumFrom :: Inline #-} + {-# enumFromThen :: Inline #-} + {-# enumFromTo :: Inline #-} + {-# enumFromThenTo :: Inline #-} + +charEnumFrom c = map chr [ord c .. ord maxChar] +charEnumFromThen c c' = map chr [ord c, ord c' .. ord lastChar] + where lastChar = if c' < c then minChar else maxChar +{-# charEnumFrom :: Inline #-} +{-# charEnumFromThen :: Inline #-} + +instance Text Char where + readsPrec p = readParen False + (\r -> [(c,t) | ('\'':s,t)<- lex r, + (c,_) <- readLitChar s]) + + showsPrec p '\'' = showString "'\\''" + showsPrec p c = showChar '\'' . showLitChar c . showChar '\'' + + readList = readParen False (\r -> [(l,t) | ('"':s, t) <- lex r, + (l,_) <- readl s ]) + where readl ('"':s) = [("",s)] + readl ('\\':'&':s) = readl s + readl s = [(c:cs,u) | (c ,t) <- readLitChar s, + (cs,u) <- readl t ] + + showList cs = showChar '"' . showl cs + where showl "" = showChar '"' + showl ('"':cs) = showString "\\\"" . showl cs + showl (c:cs) = showLitChar c . showl cs + +type String = [Char] + + +-- Standard Integral types + +instance Eq Int where + (==) = primEqInt + (/=) = primNeqInt + +instance Eq Integer where + (==) = primEqInteger + (/=) = primNeqInteger + +instance Ord Int where + (<) = primLsInt + (<=) = primLeInt + (>) = primGtInt + (>=) = primGeInt + max = primIntMax + min = primIntMin + +instance Ord Integer where + (<) = primLsInteger + (<=) = primLeInteger + (>) = primGtInteger + (>=) = primGeInteger + max = primIntegerMax + min = primIntegerMin + +instance Num Int where + (+) = primPlusInt + (-) = primMinusInt + negate = primNegInt + (*) = primMulInt + abs = primAbsInt + signum = signumReal + fromInteger = primIntegerToInt + +instance Num Integer where + (+) = primPlusInteger + (-) = primMinusInteger + negate = primNegInteger + (*) = primMulInteger + abs = primAbsInteger + signum = signumReal + fromInteger x = x + +signumReal x | x == 0 = 0 + | x > 0 = 1 + | otherwise = -1 + +instance Real Int where + toRational x = toInteger x % 1 + +instance Real Integer where + toRational x = x % 1 + +instance Integral Int where + quotRem = primQuotRemInt + toInteger = primIntToInteger + +instance Integral Integer where + quotRem = primQuotRemInteger + toInteger x = x + +instance Ix Int where + range (m,n) = [m..n] + index b@(m,n) i + | inRange b i = i - m + | otherwise = error "index{PreludeCore}: Index out of range." + inRange (m,n) i = m <= i && i <= n + {-# range :: Inline #-} + +instance Ix Integer where + range (m,n) = [m..n] + index b@(m,n) i + | inRange b i = fromInteger (i - m) + | otherwise = error "index{PreludeCore}: Index out of range." + inRange (m,n) i = m <= i && i <= n + {-# range :: Inline #-} + +instance Enum Int where + enumFrom = numericEnumFrom + enumFromThen = numericEnumFromThen + enumFromTo = defaultEnumFromTo + enumFromThenTo = defaultEnumFromThenTo + {-# enumFrom :: Inline #-} + {-# enumFromThen :: Inline #-} + {-# enumFromTo :: Inline #-} + {-# enumFromThenTo :: Inline #-} + +instance Enum Integer where + enumFrom = numericEnumFrom + enumFromThen = numericEnumFromThen + enumFromTo = defaultEnumFromTo + enumFromThenTo = defaultEnumFromThenTo + {-# enumFrom :: Inline #-} + {-# enumFromThen :: Inline #-} + {-# enumFromTo :: Inline #-} + {-# enumFromThenTo :: Inline #-} + +numericEnumFrom :: (Real a) => a -> [a] +numericEnumFromThen :: (Real a) => a -> a -> [a] +numericEnumFrom = iterate (+1) +numericEnumFromThen n m = iterate (+(m-n)) n + +{-# numericEnumFrom :: Inline #-} +{-# numericEnumFromThen :: Inline #-} + + +instance Text Int where + readsPrec p = readSigned readDec + showsPrec = showSigned showInt + +instance Text Integer where + readsPrec p = readSigned readDec + showsPrec = showSigned showInt + + +-- Standard Floating types + +instance Eq Float where + (==) = primEqFloat + (/=) = primNeqFloat + +instance Eq Double where + (==) = primEqDouble + (/=) = primNeqDouble + +instance Ord Float where + (<) = primLsFloat + (<=) = primLeFloat + (>) = primGtFloat + (>=) = primGeFloat + max = primFloatMax + min = primFloatMin + +instance Ord Double where + (<) = primLsDouble + (<=) = primLeDouble + (>) = primGtDouble + (>=) = primGeDouble + max = primDoubleMax + min = primDoubleMax + +instance Num Float where + (+) = primPlusFloat + (-) = primMinusFloat + negate = primNegFloat + (*) = primMulFloat + abs = primAbsFloat + signum = signumReal + fromInteger n = encodeFloat n 0 + +instance Num Double where + (+) = primPlusDouble + (-) = primMinusDouble + negate = primNegDouble + (*) = primMulDouble + abs = primAbsDouble + signum = signumReal + fromInteger n = encodeFloat n 0 + +instance Real Float where + toRational = primFloatToRational + +instance Real Double where + toRational = primDoubleToRational + +-- realFloatToRational x = (m%1)*(b%1)^^n +-- where (m,n) = decodeFloat x +-- b = floatRadix x + +instance Fractional Float where + (/) = primDivFloat + fromRational = primRationalToFloat +-- fromRational = rationalToRealFloat + +instance Fractional Double where + (/) = primDivDouble + fromRational = primRationalToDouble +-- fromRational = rationalToRealFloat + +-- rationalToRealFloat x = x' +-- where x' = f e +-- f e = if e' == e then y else f e' +-- where y = encodeFloat (round (x * (1%b)^^e)) e +-- (_,e') = decodeFloat y +-- (_,e) = decodeFloat (fromInteger (numerator x) `asTypeOf` x' +-- / fromInteger (denominator x)) +-- b = floatRadix x' + +instance Floating Float where + pi = primPiFloat + exp = primExpFloat + log = primLogFloat + sqrt = primSqrtFloat + sin = primSinFloat + cos = primCosFloat + tan = primTanFloat + asin = primAsinFloat + acos = primAcosFloat + atan = primAtanFloat + sinh = primSinhFloat + cosh = primCoshFloat + tanh = primTanhFloat + asinh = primAsinhFloat + acosh = primAcoshFloat + atanh = primAtanhFloat + +instance Floating Double where + pi = primPiDouble + exp = primExpDouble + log = primLogDouble + sqrt = primSqrtDouble + sin = primSinDouble + cos = primCosDouble + tan = primTanDouble + asin = primAsinDouble + acos = primAcosDouble + atan = primAtanDouble + sinh = primSinhDouble + cosh = primCoshDouble + tanh = primTanhDouble + asinh = primAsinhDouble + acosh = primAcoshDouble + atanh = primAtanhDouble + + +instance RealFrac Float where + properFraction = floatProperFraction + +instance RealFrac Double where + properFraction = floatProperFraction + +floatProperFraction x + | n >= 0 = (fromInteger m * fromInteger b ^ n, 0) + | otherwise = (fromInteger w, encodeFloat r n) + where (m,n) = decodeFloat x + b = floatRadix x + (w,r) = quotRem m (b^(-n)) + +instance RealFloat Float where + floatRadix _ = primFloatRadix + floatDigits _ = primFloatDigits + floatRange _ = (primFloatMinExp,primFloatMaxExp) + decodeFloat = primDecodeFloat + encodeFloat = primEncodeFloat + +instance RealFloat Double where + floatRadix _ = primDoubleRadix + floatDigits _ = primDoubleDigits + floatRange _ = (primDoubleMinExp,primDoubleMaxExp) + decodeFloat = primDecodeDouble + encodeFloat = primEncodeDouble + +instance Enum Float where + enumFrom = numericEnumFrom + enumFromThen = numericEnumFromThen + enumFromTo = defaultEnumFromTo + enumFromThenTo = defaultEnumFromThenTo + {-# enumFrom :: Inline #-} + {-# enumFromThen :: Inline #-} + {-# enumFromTo :: Inline #-} + {-# enumFromThenTo :: Inline #-} + +instance Enum Double where + enumFrom = numericEnumFrom + enumFromThen = numericEnumFromThen + enumFromTo = defaultEnumFromTo + enumFromThenTo = defaultEnumFromThenTo + {-# enumFrom :: Inline #-} + {-# enumFromThen :: Inline #-} + {-# enumFromTo :: Inline #-} + {-# enumFromThenTo :: Inline #-} + +instance Text Float where + readsPrec p = readSigned readFloat + showsPrec = showSigned showFloat + +instance Text Double where + readsPrec p = readSigned readFloat + showsPrec = showSigned showFloat + + +-- Lists + +-- data [a] = [] | a : [a] deriving (Eq, Ord, Binary) + +instance (Text a) => Text [a] where + readsPrec p = readList + showsPrec p = showList + + +-- Tuples + +-- data (a,b) = (a,b) deriving (Eq, Ord, Ix, Binary) +{- +instance (Text a, Text b) => Text (a,b) where + readsPrec p = readParen False + (\r -> [((x,y), w) | ("(",s) <- lex r, + (x,t) <- reads s, + (",",u) <- lex t, + (y,v) <- reads u, + (")",w) <- lex v ] ) + + showsPrec p (x,y) = showChar '(' . shows x . showChar ',' . + shows y . showChar ')' +-- et cetera +-} + +-- Functions + +instance Text (a -> b) where + readsPrec p s = error "readsPrec{PreludeCore}: Cannot read functions." + showsPrec p f = showString "<>" + +-- Support for class Bin + +instance Binary Int where + showBin i b = primShowBinInt i b + readBin b = primReadBinInt b + +instance Binary Integer where + showBin i b = primShowBinInteger i b + readBin b = primReadBinInteger b + +instance Binary Float where + showBin f b = primShowBinFloat f b + readBin b = primReadBinFloat b + +instance Binary Double where + showBin d b = primShowBinDouble d b + readBin b = primReadBinDouble b + +instance Binary Char where + showBin c b = primShowBinInt (ord c) b + readBin b = (chr i,b') where + (i,b') = primReadBinSmallInt b primMaxChar + +instance (Binary a) => Binary [a] where + showBin l b = showBin (length l :: Int) (sb1 l b) where + sb1 [] b = b + sb1 (h:t) b = showBin h (sb1 t b) + readBin bin = rbl len bin' where + len :: Int + (len,bin') = readBin bin + rbl 0 b = ([],b) + rbl n b = (h:t,b'') where + (h,b') = readBin b + (t,b'') = rbl (n-1) b' + +instance (Ix a, Binary a, Binary b) => Binary (Array a b) where + showBin a = showBin (bounds a) . showBin (elems a) + readBin bin = (listArray b vs, bin'') + where (b,bin') = readBin bin + (vs,bin'') = readBin bin' + +{- +instance (Binary a, Binary b) => Binary (a,b) where + showBin (x,y) = (showBin x) . (showBin y) + readBin b = ((x,y),b'') where + (x,b') = readBin b + (y,b'') = readBin b' + +instance (Binary a, Binary b, Binary c) => Binary (a,b,c) where + showBin (x,y,z) = (showBin x) . (showBin y) . (showBin z) + readBin b = ((x,y,z),b3) where + (x,b1) = readBin b + (y,b2) = readBin b1 + (z,b3) = readBin b2 + +instance (Binary a, Binary b, Binary c, Binary d) => Binary (a,b,c,d) where + showBin (a,b,c,d) = (showBin a) . (showBin b) . (showBin c) . (showBin d) + readBin b = ((a1,a2,a3,a4),b4) where + (a1,b1) = readBin b + (a2,b2) = readBin b1 + (a3,b3) = readBin b2 + (a4,b4) = readBin b3 +-} +-- Instances for tuples + +-- This whole section should be handled in the support code. For now, +-- only tuple instances expliticly provided here are available. +-- Currently provided: + +-- 2,3 tuples: all classes (Eq, Ord, Ix, Bin, Text) +-- 4 tuples: Eq, Bin, Text +-- 5, 6 tuples: Text (printing only) + +{- +rangeSize :: (Ix a) => (a,a) -> Int +rangeSize (l,u) = index (l,u) u + 1 + +instance (Eq a1, Eq a2) => Eq (a1,a2) where + (a1,a2) == (z1,z2) = a1==z1 && a2==z2 + +instance (Ord a1, Ord a2) => Ord (a1,a2) where + (a1,a2) <= (z1,z2) = a1<=z1 || a1==z1 && a2<=z2 + (a1,a2) < (z1,z2) = a1 Ix (a1,a2) where + range ((l1,l2),(u1,u2)) = [(i1,i2) | i1 <- range(l1,u1), + i2 <- range(l2,u2)] + index ((l1,l2),(u1,u2)) (i1,i2) = + index (l1,u1) i1 * rangeSize (l2,u2) + + index (l2,u2) i2 + inRange ((l1,l2),(u1,u2)) (i1,i2) = + inRange (l1,u1) i1 && inRange (l2,u2) i2 + +{- Apprears in Joe's code. +instance (Text a1, Text a2) => Text (a1,a2) where + readsPrec p = readParen False + (\r0 -> [((a1,a2), w) | ("(",r1) <- lex r0, + (a1,r2) <- reads r1, + (",",r3) <- lex r2, + (a2,r4) <- reads r3, + (")",w) <- lex r4 ]) + + showsPrec p (a1,a2) = showChar '(' . shows a1 . showChar ',' . + shows a2 . showChar ')' +-} + +instance (Eq a1, Eq a2, Eq a3) => Eq (a1,a2,a3) where + (a1,a2,a3) == (z1,z2,z3) = a1==z1 && a2==z2 && a3==z3 + +instance (Ord a1, Ord a2, Ord a3) => Ord (a1,a2,a3) where + (a1,a2,a3) <= (z1,z2,z3) = a1<=z1 || a1==z1 && + (a2<=z2 || a2==z2 && + a3<=z3) + (a1,a2,a3) < (z1,z2,z3) = a1 Ix (a1,a2,a3) where + range ((l1,l2,l3),(u1,u2,u3)) = + [(i1,i2,i3) | i1 <- range(l1,u1), + i2 <- range(l2,u2), + i3 <- range(l3,u3)] + index ((l1,l2,l3),(u1,u2,u3)) (i1,i2,i3) = + (index (l1,u1) i1 * rangeSize (l2,u2) + + index (l2,u2) i2 ) * rangeSize (l3,u3) + + index (l3,u3) i3 + inRange ((l1,l2,l3),(u1,u2,u3)) (i1,i2,i3) = + inRange (l1,u1) i1 && inRange (l2,u2) i2 && inRange (l3,u3) i3 + + +instance (Text a1, Text a2, Text a3) => Text (a1,a2,a3) where + readsPrec p = readParen False + (\r0 -> [((a1,a2,a3), w) | + ("(",r1) <- lex r0, + (a1,r2) <- reads r1, + (",",r3) <- lex r2, + (a2,r4) <- reads r3, + (",",r5) <- lex r4, + (a3,r6) <- reads r5, + (")",w) <- lex r6 ]) + showsPrec p (a1,a2,a3) = + showChar '(' . shows a1 . showChar ',' . + shows a2 . showChar ',' . + shows a3 . showChar ')' + +instance (Eq a1, Eq a2, Eq a3, Eq a4) => Eq (a1,a2,a3,a4) where + (a1,a2,a3,a4) == (z1,z2,z3,z4) = a1==z1 && a2==z2 && a3==z3 && a4 == z4 + +instance (Text a1, Text a2, Text a3, Text a4) => Text (a1,a2,a3,a4) where + readsPrec p = readParen False + (\r0 -> [((a1,a2,a3,a4), w) | + ("(",r1) <- lex r0, + (a1,r2) <- reads r1, + (",",r3) <- lex r2, + (a2,r4) <- reads r3, + (",",r5) <- lex r4, + (a3,r6) <- reads r5, + (",",r7) <- lex r6, + (a4,r8) <- reads r7, + (")",w) <- lex r8 ]) + showsPrec p (a1,a2,a3,a4) = + showChar '(' . shows a1 . showChar ',' . + shows a2 . showChar ',' . + shows a3 . showChar ',' . + shows a4 . showChar ')' + +instance (Text a1, Text a2, Text a3, Text a4, Text a5) => + Text (a1,a2,a3,a4,a5) where + readsPrec p = error "Read of 5 tuples not implemented" + showsPrec p (a1,a2,a3,a4,a5) = + showChar '(' . shows a1 . showChar ',' . + shows a2 . showChar ',' . + shows a3 . showChar ',' . + shows a4 . showChar ',' . + shows a5 . showChar ')' + +instance (Text a1, Text a2, Text a3, Text a4, Text a5, Text a6) => + Text (a1,a2,a3,a4,a5,a6) where + readsPrec p = error "Read of 6 tuples not implemented" + showsPrec p (a1,a2,a3,a4,a5,a6) = + showChar '(' . shows a1 . showChar ',' . + shows a2 . showChar ',' . + shows a3 . showChar ',' . + shows a4 . showChar ',' . + shows a5 . showChar ',' . + shows a6 . showChar ')' + + +-} diff --git a/progs/prelude/PreludeIO.hs b/progs/prelude/PreludeIO.hs new file mode 100644 index 0000000..6173d8c --- /dev/null +++ b/progs/prelude/PreludeIO.hs @@ -0,0 +1,232 @@ +-- I/O functions and definitions + +module PreludeIO(stdin,stdout,stderr,stdecho,{-Request(..),Response(..),-} + IOError(..),Dialogue(..),IO(..),SystemState,IOResult, + SuccCont(..),StrCont(..), + StrListCont(..),BinCont(..),FailCont(..), + readFile, writeFile, appendFile, readBinFile, + writeBinFile, appendBinFile, deleteFile, statusFile, + readChan, appendChan, readBinChan, appendBinChan, + statusChan, echo, getArgs, getProgName, getEnv, setEnv, + done, exit, abort, print, prints, interact, + thenIO,thenIO_,seqIO,returnIO, doneIO) + where + +import PreludeBltinIO +import PreludeBltinArray(strict1) + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +-- These datatypes are used by the monad. + +type IO a = SystemState -> IOResult a + +data SystemState = SystemState +data IOResult a = IOResult a + +-- Operations in the monad + +-- This definition is needed to allow proper tail recursion of the Lisp +-- code. The use of strict1 forces f1 s (since getState is strict) before +-- the call to f2. The optimizer removed getState and getRes from the +-- generated code. + +{-# thenIO :: Inline #-} +thenIO f1 f2 s = + let g = f1 s + s' = getState g in + strict1 s' (f2 (getRes g) s') + +{-# thenIO_ :: Inline #-} +x `thenIO_` y = x `thenIO` \_ -> y +x `seqIO` y = x `thenIO` \_ -> y + +-- The returnIO function is implemented directly as a primitive. +doneIO = returnIO () + + +-- File and channel names: + +stdin = "stdin" +stdout = "stdout" +stderr = "stderr" +stdecho = "stdecho" + + +-- Requests and responses: + +{- Not used since streams are no longer supported: +data Request = -- file system requests: + ReadFile String + | WriteFile String String + | AppendFile String String + | ReadBinFile String + | WriteBinFile String Bin + | AppendBinFile String Bin + | DeleteFile String + | StatusFile String + -- channel system requests: + | ReadChan String + | AppendChan String String + | ReadBinChan String + | AppendBinChan String Bin + | StatusChan String + -- environment requests: + | Echo Bool + | GetArgs + | GetProgName + | GetEnv String + | SetEnv String String + deriving Text + +data Response = Success + | Str String + | StrList [String] + | Bn Bin + | Failure IOError + deriving Text + +-} + +data IOError = WriteError String + | ReadError String + | SearchError String + | FormatError String + | OtherError String + deriving Text + +-- Continuation-based I/O: + +type Dialogue = IO () +type SuccCont = Dialogue +type StrCont = String -> Dialogue +type StrListCont = [String] -> Dialogue +type BinCont = Bin -> Dialogue +type FailCont = IOError -> Dialogue + +done :: Dialogue +readFile :: String -> FailCont -> StrCont -> Dialogue +writeFile :: String -> String -> FailCont -> SuccCont -> Dialogue +appendFile :: String -> String -> FailCont -> SuccCont -> Dialogue +readBinFile :: String -> FailCont -> BinCont -> Dialogue +writeBinFile :: String -> Bin -> FailCont -> SuccCont -> Dialogue +appendBinFile :: String -> Bin -> FailCont -> SuccCont -> Dialogue +deleteFile :: String -> FailCont -> SuccCont -> Dialogue +statusFile :: String -> FailCont -> StrCont -> Dialogue +readChan :: String -> FailCont -> StrCont -> Dialogue +appendChan :: String -> String -> FailCont -> SuccCont -> Dialogue +readBinChan :: String -> FailCont -> BinCont -> Dialogue +appendBinChan :: String -> Bin -> FailCont -> SuccCont -> Dialogue +statusChan :: String -> FailCont -> StrCont -> Dialogue +echo :: Bool -> FailCont -> SuccCont -> Dialogue +getArgs :: FailCont -> StrListCont -> Dialogue +getProgName :: FailCont -> StrCont -> Dialogue +getEnv :: String -> FailCont -> StrCont -> Dialogue +setEnv :: String -> String -> FailCont -> SuccCont -> Dialogue + +done = returnIO () + +readFile name fail succ = + primReadStringFile name `thenIO` objDispatch fail succ + +writeFile name contents fail succ = + primWriteStringFile name contents `thenIO` succDispatch fail succ + +appendFile name contents fail succ = + primAppendStringFile name contents `thenIO` succDispatch fail succ + +readBinFile name fail succ = + primReadBinFile name `thenIO` objDispatch fail succ + +writeBinFile name contents fail succ = + primWriteBinFile name contents `thenIO` succDispatch fail succ + +appendBinFile name contents fail succ = + primAppendBinFile name contents `thenIO` succDispatch fail succ + +deleteFile name fail succ = + primDeleteFile name `thenIO` succDispatch fail succ + +statusFile name fail succ = + primStatusFile name `thenIO` + (\status -> case status of Succ s -> succ s + Fail msg -> fail (SearchError msg)) + +readChan name fail succ = + if name == stdin then + primReadStdin `thenIO` succ + else + badChan fail name + +appendChan name contents fail succ = + if name == stdout then + primWriteStdout contents `thenIO` succDispatch fail succ + else + badChan fail name + +readBinChan name fail succ = + if name == stdin then + primReadBinStdin `thenIO` objDispatch fail succ + else + badChan fail name + +appendBinChan name contents fail succ = + if name == stdout then + primWriteBinStdout contents `thenIO` succDispatch fail succ + else + badChan fail name + +statusChan name fail succ = + if name == stdin || name == stdout then + succ "0 0" + else + fail (SearchError "Channel not defined") + +echo bool fail succ = + if bool then + succ + else + fail (OtherError "Echo cannot be turned off") + +getArgs fail succ = + succ [""] + +getProgName fail succ = + succ "haskell" + +getEnv name fail succ = + primGetEnv name `thenIO` objDispatch fail succ + +setEnv name val fail succ = + fail (OtherError "setEnv not implemented") + +objDispatch fail succ r = + case r of Succ s -> succ s + Fail msg -> fail (OtherError msg) + +succDispatch fail succ r = + case r of Succ _ -> succ + Fail msg -> fail (OtherError msg) + +badChan f name = f (OtherError ("Improper IO Channel: " ++ name)) + +abort :: FailCont +abort err = done + +exit :: FailCont +exit err = appendChan stderr (msg ++ "\n") abort done + where msg = case err of ReadError s -> s + WriteError s -> s + SearchError s -> s + FormatError s -> s + OtherError s -> s + +print :: (Text a) => a -> Dialogue +print x = appendChan stdout (show x) exit done +prints :: (Text a) => a -> String -> Dialogue +prints x s = appendChan stdout (shows x s) exit done + +interact :: (String -> String) -> Dialogue +interact f = readChan stdin exit + (\x -> appendChan stdout (f x) exit done) + diff --git a/progs/prelude/PreludeIOMonad.hs b/progs/prelude/PreludeIOMonad.hs new file mode 100644 index 0000000..9a45606 --- /dev/null +++ b/progs/prelude/PreludeIOMonad.hs @@ -0,0 +1,60 @@ +module IOMonad (State, IO(..)) where + +import IOMonadPrims + +{- I use data instead of type so that IO can be abstract. For efficiency, + IO can be annotated as a strict constructor. +-} + +type IO a = State -> (State, a) + +data State = State + +-- The rest of this file is unnecessary at the moment since +-- unitIO & bindIO are primitives and we're not using the rest of this + +{- Implemented as a primitives: +bindIO :: IO a -> (a -> IO b) -> IO b +bindIO (IO m) (IO k) = IO (\s0 -> let (s1, a) = m s0 in k a s1) -} + +unitIO :: a -> IO a +unitIO x = IO (\s -> (s, x)) + +-} + +{- Not currently used: +pureIO :: IO a -> a +pureIO (IO m) = let (s, x) = m State in x + +-- execIO executes a program of type IO (). +execIO :: IO () -> State +execIO (IO m) = let (s, x) = m State in s + +infixr 1 =: +infixr 1 ? + +-- assignment +(=:) :: a -> Var a -> IO () +x =: v = IO (\s -> (update v x s, ())) + +-- reader +(?) :: Var a -> (a -> IO b) -> IO b +v ? k = IO (\s -> (s, readVar v s)) `bindIO` k + +-- new +newvar :: IO (Var a) +newvar = IO allocVar + +instance Eq (Var a) where + x == y = eqVar x y +-} + + + + + + + + + + diff --git a/progs/prelude/PreludeIOPrims.hi b/progs/prelude/PreludeIOPrims.hi new file mode 100644 index 0000000..e4c2e74 --- /dev/null +++ b/progs/prelude/PreludeIOPrims.hi @@ -0,0 +1,55 @@ +-- These lisp functions implement the standard Haskell requests + +interface PreludeBltinIO where + +import PreludeCore(String,Bin) +import PreludeIO(SystemState,IOResult,IO) +data IOResponse a = Succ a | Fail String + +{-# Prelude #-} + +primReadStringFile :: String -> IO (IOResponse String) +primWriteStringFile :: String -> String -> IO (IOResponse ()) +primAppendStringFile :: String -> String -> IO (IOResponse ()) +primReadBinFile :: String -> IO (IOResponse Bin) +primWriteBinFile :: String -> Bin -> IO (IOResponse ()) +primAppendBinFile :: String -> Bin -> IO (IOResponse ()) +primDeleteFile :: String -> IO (IOResponse ()) +primStatusFile :: String -> IO (IOResponse String) +primReadStdin :: IO String +primWriteStdout :: String -> IO (IOResponse ()) +primReadBinStdin :: IO (IOResponse Bin) +primWriteBinStdout :: Bin -> IO (IOResponse ()) +primGetEnv :: String -> IO (IOResponse String) + +{-# +primReadStringFile :: LispName("prim.read-string-file") +primWriteStringFile :: LispName("prim.write-string-file"), NoConversion +primAppendStringFile :: LispName("prim.append-string-file"), NoConversion +primReadBinFile :: LispName("prim.read-bin-file") +primWriteBinFile :: LispName("prim.write-bin-file") +primAppendBinFile :: LispName("prim.append-bin-file") +primDeleteFile :: LispName("prim.delete-file") +primStatusFile :: LispName("prim.status-file") +primReadStdin :: LispName("prim.read-string-stdin"), NoConversion +primWriteStdout :: LispName("prim.write-string-stdout"), NoConversion +primReadBinStdin :: LispName("prim.read-bin-stdin") +primWriteBinStdout :: LispName("prim.write-bin-stdout") +primGetEnv :: LispName("prim.getenv") +#-} + +-- Monad prims + +returnIO :: a -> IO a +getState :: IOResult a -> SystemState +getRes :: IOResult a -> a + +{-# +returnIO :: LispName("prim.returnio"), + Strictness("N,S"), NoConversion, Complexity(3) +getState :: LispName("prim.getstate"), + Strictness("S"), NoConversion, Complexity(3) +getRes :: LispName("prim.getres"), + Strictness("S"), NoConversion +#-} + diff --git a/progs/prelude/PreludeIOPrims.hu b/progs/prelude/PreludeIOPrims.hu new file mode 100644 index 0000000..66393c5 --- /dev/null +++ b/progs/prelude/PreludeIOPrims.hu @@ -0,0 +1,4 @@ +:output $PRELUDEBIN/PreludeIOPrims +:stable +:prelude +PreludeIOPrims.hi diff --git a/progs/prelude/PreludeList.hs b/progs/prelude/PreludeList.hs new file mode 100644 index 0000000..3e445c3 --- /dev/null +++ b/progs/prelude/PreludeList.hs @@ -0,0 +1,585 @@ +-- Standard list functions + +-- build really shouldn't be exported, but what the heck. +-- some of the helper functions in this file shouldn't be +-- exported either! + +module PreludeList (PreludeList.., foldr, build) where + +import PreludePrims(build, foldr) + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +infixl 9 !! +infix 5 \\ +infixr 5 ++ +infix 4 `elem`, `notElem` + + +-- These are primitives used by the deforestation stuff in the optimizer. +-- the optimizer will turn references to foldr and build into +-- inlineFoldr and inlineBuild, respectively, but doesn't want to +-- necessarily inline all references immediately. + +inlineFoldr :: (a -> b -> b) -> b -> [a] -> b +inlineFoldr f z l = + let foldr' [] = z + foldr' (x:xs) = f x (foldr' xs) + in foldr' l +{-# inlineFoldr :: Inline #-} + + +inlineBuild :: ((a -> [a] -> [a]) -> [b] -> [c]) -> [c] +inlineBuild g = g (:) [] +{-# inlineBuild :: Inline #-} + + +-- head and tail extract the first element and remaining elements, +-- respectively, of a list, which must be non-empty. last and init +-- are the dual functions working from the end of a finite list, +-- rather than the beginning. + +head :: [a] -> a +head (x:_) = x +head [] = error "head{PreludeList}: head []" + +last :: [a] -> a +last [x] = x +last (_:xs) = last xs +last [] = error "last{PreludeList}: last []" + +tail :: [a] -> [a] +tail (_:xs) = xs +tail [] = error "tail{PreludeList}: tail []" + +init :: [a] -> [a] +init [x] = [] +init (x:xs) = x : init xs +init [] = error "init{PreludeList}: init []" + +-- null determines if a list is empty. +null :: [a] -> Bool +null [] = True +null (_:_) = False + + +-- list concatenation (right-associative) + +(++) :: [a] -> [a] -> [a] +xs ++ ys = build (\ c n -> foldr c (foldr c n ys) xs) +{-# (++) :: Inline #-} + + +-- the first occurrence of each element of ys in turn (if any) +-- has been removed from xs. Thus, (xs ++ ys) \\ xs == ys. +(\\) :: (Eq a) => [a] -> [a] -> [a] +(\\) = foldl del + where [] `del` _ = [] + (x:xs) `del` y + | x == y = xs + | otherwise = x : xs `del` y + +-- length returns the length of a finite list as an Int; it is an instance +-- of the more general genericLength, the result type of which may be +-- any kind of number. + +genericLength :: (Num a) => [b] -> a +genericLength l = foldr (\ x n -> 1 + n) 0 l +--genericLength [] = 0 +--genericLength (x:xs) = 1 + genericLength xs +{-# genericLength :: Inline #-} + + +length :: [a] -> Int +length l = foldr (\ x n -> 1 + n) 0 l +--length [] = 0 +--length (x:xs) = 1 + length xs +{-# length :: Inline #-} + +-- List index (subscript) operator, 0-origin +(!!) :: (Integral a) => [b] -> a -> b +l !! i = nth l (fromIntegral i) +{-# (!!) :: Inline #-} + +nth :: [b] -> Int -> b +nth l m = let f x g 0 = x + f x g i = g (i - 1) + fail _ = error "(!!){PreludeList}: index too large" + in foldr f fail l m +{-# nth :: Inline #-} +--nth _ n | n < 0 = error "(!!){PreludeList}: negative index" +--nth [] n = error "(!!){PreludeList}: index too large" +--nth (x:xs) n +-- | n == 0 = x +-- | otherwise = nth xs (n - 1) +--{-# nth :: Strictness("S,S") #-} + +-- map f xs applies f to each element of xs; i.e., map f xs == [f x | x <- xs]. +map :: (a -> b) -> [a] -> [b] +map f xs = build (\ c n -> foldr (\ a b -> c (f a) b) n xs) +--map f [] = [] +--map f (x:xs) = f x : map f xs +{-# map :: Inline #-} + + +-- filter, applied to a predicate and a list, returns the list of those +-- elements that satisfy the predicate; i.e., +-- filter p xs == [x | x <- xs, p x]. +filter :: (a -> Bool) -> [a] -> [a] +filter f xs = build (\ c n -> + foldr (\ a b -> if f a then c a b else b) + n xs) +--filter p = foldr (\x xs -> if p x then x:xs else xs) [] +{-# filter :: Inline #-} + + +-- partition takes a predicate and a list and returns a pair of lists: +-- those elements of the argument list that do and do not satisfy the +-- predicate, respectively; i.e., +-- partition p xs == (filter p xs, filter (not . p) xs). +partition :: (a -> Bool) -> [a] -> ([a],[a]) +partition p = foldr select ([],[]) + where select x (ts,fs) | p x = (x:ts,fs) + | otherwise = (ts,x:fs) +{-# partition :: Inline #-} + + +-- foldl, applied to a binary operator, a starting value (typically the +-- left-identity of the operator), and a list, reduces the list using +-- the binary operator, from left to right: +-- foldl f z [x1, x2, ..., xn] == (...((z `f` x1) `f` x2) `f`...) `f` xn +-- foldl1 is a variant that has no starting value argument, and thus must +-- be applied to non-empty lists. scanl is similar to foldl, but returns +-- a list of successive reduced values from the left: +-- scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...] +-- Note that last (scanl f z xs) == foldl f z xs. +-- scanl1 is similar, again without the starting element: +-- scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...] + +foldl :: (a -> b -> a) -> a -> [b] -> a +foldl f z xs = foldr (\ b g a -> g (f a b)) id xs z +--foldl f z [] = z +--foldl f z (x:xs) = foldl f (f z x) xs +{-# foldl :: Inline #-} + +foldl1 :: (a -> a -> a) -> [a] -> a +foldl1 f (x:xs) = foldl f x xs +foldl1 _ [] = error "foldl1{PreludeList}: empty list" +{-# foldl1 :: Inline #-} + +scanl :: (a -> b -> a) -> a -> [b] -> [a] +scanl f q xs = q : (case xs of + [] -> [] + x:xs -> scanl f (f q x) xs) +{-# scanl :: Inline #-} + +scanl1 :: (a -> a -> a) -> [a] -> [a] +scanl1 f (x:xs) = scanl f x xs +scanl1 _ [] = error "scanl1{PreludeList}: empty list" +{-# scanl1 :: Inline #-} + + +-- foldr, foldr1, scanr, and scanr1 are the right-to-left duals of the +-- above functions. + +--foldr :: (a -> b -> b) -> b -> [a] -> b +--foldr f z [] = z +--foldr f z (x:xs) = f x (foldr f z xs) + + +foldr1 :: (a -> a -> a) -> [a] -> a +foldr1 f [x] = x +foldr1 f (x:xs) = f x (foldr1 f xs) +foldr1 _ [] = error "foldr1{PreludeList}: empty list" +{-# foldr1 :: Inline #-} + + +-- I'm not sure the build/foldr expansion wins. + +scanr :: (a -> b -> b) -> b -> [a] -> [b] +--scanr f q0 l = build (\ c n -> +-- let g x qs@(q:_) = c (f x q) qs +-- in foldr g (c q0 n) l) +scanr f q0 [] = [q0] +scanr f q0 (x:xs) = f x q : qs + where qs@(q:_) = scanr f q0 xs +{-# scanr :: Inline #-} + +scanr1 :: (a -> a -> a) -> [a] -> [a] +scanr1 f [x] = [x] +scanr1 f (x:xs) = f x q : qs + where qs@(q:_) = scanr1 f xs +scanr1 _ [] = error "scanr1{PreludeList}: empty list" +{-# scanr1 :: Inline #-} + + +-- iterate f x returns an infinite list of repeated applications of f to x: +-- iterate f x == [x, f x, f (f x), ...] +iterate :: (a -> a) -> a -> [a] +iterate f x = build (\ c n -> + let iterate' x' = c x' (iterate' (f x')) + in iterate' x) +--iterate f x = x : iterate f (f x) +{-# iterate :: Inline #-} + + +-- repeat x is an infinite list, with x the value of every element. +repeat :: a -> [a] +repeat x = build (\ c n -> let r = c x r in r) +--repeat x = xs where xs = x:xs +{-# repeat :: Inline #-} + +-- cycle ties a finite list into a circular one, or equivalently, +-- the infinite repetition of the original list. It is the identity +-- on infinite lists. + +cycle :: [a] -> [a] +cycle xs = xs' where xs' = xs ++ xs' + + +-- take n, applied to a list xs, returns the prefix of xs of length n, +-- or xs itself if n > length xs. drop n xs returns the suffix of xs +-- after the first n elements, or [] if n > length xs. splitAt n xs +-- is equivalent to (take n xs, drop n xs). + +take :: (Integral a) => a -> [b] -> [b] +take n l = takeInt (fromIntegral n) l +{-# take :: Inline #-} + +takeInt :: Int -> [b] -> [b] +takeInt m l = + build (\ c n -> + let f x g i | i <= 0 = n + | otherwise = c x (g (i - 1)) + in foldr f (\ _ -> n) l m) +--takeInt 0 _ = [] +--takeInt _ [] = [] +--takeInt n l | n > 0 = primTake n l +{-# takeInt :: Inline #-} + + + +-- Writing drop and friends in terms of build/foldr seems to lose +-- way big since they cause an extra traversal of the list tail +-- (except when the calls are being deforested). + +drop :: (Integral a) => a -> [b] -> [b] +drop n l = dropInt (fromIntegral n) l +{-# drop :: Inline #-} +{-# drop :: Strictness("S,S") #-} + + +dropInt :: Int -> [b] -> [b] +dropInt 0 xs = xs +dropInt _ [] = [] +dropInt (n+1) (_:xs) = dropInt n xs +{-# dropInt :: Inline #-} + +splitAt :: (Integral a) => a -> [b] -> ([b],[b]) +splitAt n l = splitAtInt (fromIntegral n) l +{-# splitAt :: Inline #-} + +splitAtInt :: Int -> [b] -> ([b],[b]) +splitAtInt 0 xs = ([],xs) +splitAtInt _ [] = ([],[]) +splitAtInt (n+1) (x:xs) = (x:xs',xs'') where (xs',xs'') = splitAtInt n xs +{-# splitAtInt :: Inline #-} + +-- takeWhile, applied to a predicate p and a list xs, returns the longest +-- prefix (possibly empty) of xs of elements that satisfy p. dropWhile p xs +-- returns the remaining suffix. Span p xs is equivalent to +-- (takeWhile p xs, dropWhile p xs), while break p uses the negation of p. + +takeWhile :: (a -> Bool) -> [a] -> [a] +takeWhile p l = build (\ c n -> foldr (\ a b -> if p a then c a b else n) n l) +--takeWhile p [] = [] +--takeWhile p (x:xs) +-- | p x = x : takeWhile p xs +-- | otherwise = [] +{-# takeWhile :: Inline #-} + + +dropWhile :: (a -> Bool) -> [a] -> [a] +dropWhile p [] = [] +dropWhile p xs@(x:xs') + | p x = dropWhile p xs' + | otherwise = xs +{-# dropWhile :: Inline #-} + +span, break :: (a -> Bool) -> [a] -> ([a],[a]) +span p [] = ([],[]) +span p xs@(x:xs') + | p x = let (ys,zs) = span p xs' in (x:ys,zs) + | otherwise = ([],xs) +break p = span (not . p) + +{-# span :: Inline #-} +{-# break :: Inline #-} + + +-- lines breaks a string up into a list of strings at newline characters. +-- The resulting strings do not contain newlines. Similary, words +-- breaks a string up into a list of words, which were delimited by +-- white space. unlines and unwords are the inverse operations. +-- unlines joins lines with terminating newlines, and unwords joins +-- words with separating spaces. + +lines :: String -> [String] +lines "" = [] +lines s = let (l, s') = break (== '\n') s + in l : case s' of + [] -> [] + (_:s'') -> lines s'' + +words :: String -> [String] +words s = case dropWhile isSpace s of + "" -> [] + s' -> w : words s'' + where (w, s'') = break isSpace s' + +unlines :: [String] -> String +unlines = concat . map (++ "\n") +{-# unlines :: Inline #-} + + +unwords :: [String] -> String +unwords [] = "" +unwords ws = foldr1 (\w s -> w ++ ' ':s) ws + +-- nub (meaning "essence") removes duplicate elements from its list argument. +nub :: (Eq a) => [a] -> [a] +nub l = build (\ c n -> + let f x g [] = c x (g [x]) + f x g xs = if elem x xs + then (g xs) + else c x (g (x:xs)) + in foldr f (\ _ -> n) l []) +{-# nub :: Inline #-} +--nub [] = [] +--nub (x:xs) = x : nub (filter (/= x) xs) + +-- reverse xs returns the elements of xs in reverse order. xs must be finite. +reverse :: [a] -> [a] +reverse l = build (\ c n -> + let f x g tail = g (c x tail) + in foldr f id l n) +{-# reverse :: Inline #-} +--reverse x = reverse1 x [] where +-- reverse1 [] a = a +-- reverse1 (x:xs) a = reverse1 xs (x:a) + +-- and returns the conjunction of a Boolean list. For the result to be +-- True, the list must be finite; False, however, results from a False +-- value at a finite index of a finite or infinite list. or is the +-- disjunctive dual of and. +and, or :: [Bool] -> Bool +and = foldr (&&) True +or = foldr (||) False +{-# and :: Inline #-} +{-# or :: Inline #-} + +-- Applied to a predicate and a list, any determines if any element +-- of the list satisfies the predicate. Similarly, for all. +any, all :: (a -> Bool) -> [a] -> Bool +any p = or . map p +all p = and . map p +{-# any :: Inline #-} +{-# all :: Inline #-} + +-- elem is the list membership predicate, usually written in infix form, +-- e.g., x `elem` xs. notElem is the negation. +elem, notElem :: (Eq a) => a -> [a] -> Bool + +elem x ys = foldr (\ y t -> (x == y) || t) False ys +--x `elem` [] = False +--x `elem` (y:ys) = x == y || x `elem` ys +{-# elem :: Inline #-} +notElem x y = not (x `elem` y) + +-- sum and product compute the sum or product of a finite list of numbers. +sum, product :: (Num a) => [a] -> a +sum = foldl (+) 0 +product = foldl (*) 1 +{-# sum :: Inline #-} +{-# product :: Inline #-} + +-- sums and products give a list of running sums or products from +-- a list of numbers. For example, sums [1,2,3] == [0,1,3,6]. +sums, products :: (Num a) => [a] -> [a] +sums = scanl (+) 0 +products = scanl (*) 1 + +-- maximum and minimum return the maximum or minimum value from a list, +-- which must be non-empty, finite, and of an ordered type. +maximum, minimum :: (Ord a) => [a] -> a +maximum = foldl1 max +minimum = foldl1 min +{-# maximum :: Inline #-} +{-# minimum :: Inline #-} + +-- concat, applied to a list of lists, returns their flattened concatenation. +concat :: [[a]] -> [a] +concat xs = build (\ c n -> foldr (\ x y -> foldr c y x) n xs) +--concat [] = [] +--concat (l:ls) = l ++ concat ls +{-# concat :: Inline #-} + + +-- transpose, applied to a list of lists, returns that list with the +-- "rows" and "columns" interchanged. The input need not be rectangular +-- (a list of equal-length lists) to be completely transposable, but can +-- be "triangular": Each successive component list must be not longer +-- than the previous one; any elements outside of the "triangular" +-- transposable region are lost. The input can be infinite in either +-- dimension or both. +transpose :: [[a]] -> [[a]] +transpose = foldr + (\xs xss -> zipWith (:) xs (xss ++ repeat [])) + [] +{-# transpose :: Inline #-} + +-- zip takes two lists and returns a list of corresponding pairs. If one +-- input list is short, excess elements of the longer list are discarded. +-- zip3 takes three lists and returns a list of triples, etc. Versions +-- of zip producing up to septuplets are defined here. + +zip :: [a] -> [b] -> [(a,b)] +zip = zipWith (\a b -> (a,b)) +{-# zip :: Inline #-} + +zip3 :: [a] -> [b] -> [c] -> [(a,b,c)] +zip3 = zipWith3 (\a b c -> (a,b,c)) +{-# zip3 :: Inline #-} + +zip4 :: [a] -> [b] -> [c] -> [d] -> [(a,b,c,d)] +zip4 = zipWith4 (\a b c d -> (a,b,c,d)) +{-# zip4 :: Inline #-} + +zip5 :: [a] -> [b] -> [c] -> [d] -> [e] -> [(a,b,c,d,e)] +zip5 = zipWith5 (\a b c d e -> (a,b,c,d,e)) +{-# zip5 :: Inline #-} + +zip6 :: [a] -> [b] -> [c] -> [d] -> [e] -> [f] + -> [(a,b,c,d,e,f)] +zip6 = zipWith6 (\a b c d e f -> (a,b,c,d,e,f)) +{-# zip6 :: Inline #-} + +zip7 :: [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] + -> [(a,b,c,d,e,f,g)] +zip7 = zipWith7 (\a b c d e f g -> (a,b,c,d,e,f,g)) +{-# zip7 :: Inline #-} + +-- The zipWith family generalises the zip family by zipping with the +-- function given as the first argument, instead of a tupling function. +-- For example, zipWith (+) is applied to two lists to produce the list +-- of corresponding sums. + +zipWith :: (a->b->c) -> [a]->[b]->[c] +zipWith z as bs = + build (\ c' n' -> + let f' a g' (b:bs) = c' (z a b) (g' bs) + f' a g' _ = n' + in foldr f' (\ _ -> n') as bs) +--zipWith z (a:as) (b:bs) = z a b : zipWith z as bs +--zipWith _ _ _ = [] +{-# zipWith :: Inline #-} + +zipWith3 :: (a->b->c->d) -> [a]->[b]->[c]->[d] +zipWith3 z as bs cs = + build (\ c' n' -> + let f' a g' (b:bs) (c:cs) = c' (z a b c) (g' bs cs) + f' a g' _ _ = n' + in foldr f' (\ _ _ -> n') as bs cs) +{-# zipWith3 :: Inline #-} +--zipWith3 z (a:as) (b:bs) (c:cs) +-- = z a b c : zipWith3 z as bs cs +--zipWith3 _ _ _ _ = [] + +zipWith4 :: (a->b->c->d->e) -> [a]->[b]->[c]->[d]->[e] +zipWith4 z as bs cs ds = + build (\ c' n' -> + let f' a g' (b:bs) (c:cs) (d:ds) = c' (z a b c d) (g' bs cs ds) + f' a g' _ _ _ = n' + in foldr f' (\ _ _ _ -> n') as bs cs ds) +{-# zipWith4 :: Inline #-} +--zipWith4 z (a:as) (b:bs) (c:cs) (d:ds) +-- = z a b c d : zipWith4 z as bs cs ds +--zipWith4 _ _ _ _ _ = [] + +zipWith5 :: (a->b->c->d->e->f) + -> [a]->[b]->[c]->[d]->[e]->[f] +zipWith5 z as bs cs ds es= + build (\ c' n' -> + let f' a g' (b:bs) (c:cs) (d:ds) (e:es) = + c' (z a b c d e) (g' bs cs ds es) + f' a g' _ _ _ _ = n' + in foldr f' (\ _ _ _ _ -> n') as bs cs ds es) +{-# zipWith5 :: Inline #-} +--zipWith5 z (a:as) (b:bs) (c:cs) (d:ds) (e:es) +-- = z a b c d e : zipWith5 z as bs cs ds es +--zipWith5 _ _ _ _ _ _ = [] + +zipWith6 :: (a->b->c->d->e->f->g) + -> [a]->[b]->[c]->[d]->[e]->[f]->[g] +zipWith6 z as bs cs ds es fs = + build (\ c' n' -> + let f' a g' (b:bs) (c:cs) (d:ds) (e:es) (f:fs) = + c' (z a b c d e f) (g' bs cs ds es fs) + f' a g' _ _ _ _ _ = n' + in foldr f' (\ _ _ _ _ _ -> n') as bs cs ds es fs) +{-# zipWith6 :: Inline #-} +--zipWith6 z (a:as) (b:bs) (c:cs) (d:ds) (e:es) (f:fs) +-- = z a b c d e f : zipWith6 z as bs cs ds es fs +--zipWith6 _ _ _ _ _ _ _ = [] + +zipWith7 :: (a->b->c->d->e->f->g->h) + -> [a]->[b]->[c]->[d]->[e]->[f]->[g]->[h] +zipWith7 z as bs cs ds es fs gs = + build (\ c' n' -> + let f' a g' (b:bs) (c:cs) (d:ds) (e:es) (f:fs) (g:gs) = + c' (z a b c d e f g) (g' bs cs ds es fs gs) + f' a g' _ _ _ _ _ _ = n' + in foldr f' (\ _ _ _ _ _ _ -> n') as bs cs ds es fs gs) +{-# zipWith7 :: Inline #-} +--zipWith7 z (a:as) (b:bs) (c:cs) (d:ds) (e:es) (f:fs) (g:gs) +-- = z a b c d e f g : zipWith7 z as bs cs ds es fs gs +--zipWith7 _ _ _ _ _ _ _ _ = [] + + +-- unzip transforms a list of pairs into a pair of lists. As with zip, +-- a family of such functions up to septuplets is provided. + +unzip :: [(a,b)] -> ([a],[b]) +unzip = foldr (\(a,b) ~(as,bs) -> (a:as,b:bs)) ([],[]) +{-# unzip :: Inline #-} + + +unzip3 :: [(a,b,c)] -> ([a],[b],[c]) +unzip3 = foldr (\(a,b,c) ~(as,bs,cs) -> (a:as,b:bs,c:cs)) + ([],[],[]) +{-# unzip3 :: Inline #-} + +unzip4 :: [(a,b,c,d)] -> ([a],[b],[c],[d]) +unzip4 = foldr (\(a,b,c,d) ~(as,bs,cs,ds) -> + (a:as,b:bs,c:cs,d:ds)) + ([],[],[],[]) +{-# unzip4 :: Inline #-} + +unzip5 :: [(a,b,c,d,e)] -> ([a],[b],[c],[d],[e]) +unzip5 = foldr (\(a,b,c,d,e) ~(as,bs,cs,ds,es) -> + (a:as,b:bs,c:cs,d:ds,e:es)) + ([],[],[],[],[]) +{-# unzip5 :: Inline #-} + +unzip6 :: [(a,b,c,d,e,f)] -> ([a],[b],[c],[d],[e],[f]) +unzip6 = foldr (\(a,b,c,d,e,f) ~(as,bs,cs,ds,es,fs) -> + (a:as,b:bs,c:cs,d:ds,e:es,f:fs)) + ([],[],[],[],[],[]) +{-# unzip6 :: Inline #-} + +unzip7 :: [(a,b,c,d,e,f,g)] -> ([a],[b],[c],[d],[e],[f],[g]) +unzip7 = foldr (\(a,b,c,d,e,f,g) ~(as,bs,cs,ds,es,fs,gs) -> + (a:as,b:bs,c:cs,d:ds,e:es,f:fs,g:gs)) + ([],[],[],[],[],[],[]) +{-# unzip7 :: Inline #-} + diff --git a/progs/prelude/PreludeLocal.hs b/progs/prelude/PreludeLocal.hs new file mode 100644 index 0000000..6e52bbf --- /dev/null +++ b/progs/prelude/PreludeLocal.hs @@ -0,0 +1,16 @@ +module PreludeLocal where + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +infixr 5 : + +data Int = MkInt +data Integer = MkInteger +data Float = MkFloat +data Double = MkDouble +data Char = MkChar +data Bin = MkBin +data List a = a : (List a) | Nil +data Arrow a b = MkArrow a b + +data Triv = MkTriv diff --git a/progs/prelude/PreludeLocalIO.hs b/progs/prelude/PreludeLocalIO.hs new file mode 100644 index 0000000..2753071 --- /dev/null +++ b/progs/prelude/PreludeLocalIO.hs @@ -0,0 +1,144 @@ +module PreludeLocalIO where + +import PreludeIOPrims +import PreludeIOMonad + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +data IOResponse a = Succ a | Fail String deriving Text + +exec :: ([Response] -> [Request]) -> IO () +{- +-- Sunderesh's original definition +exec p = case (p bottom) of + [] -> unitIO () + (q:qs) -> processRequest q `bindIO` \r -> + exec (\rs -> tail (p (r:rs))) + +bottom :: a +bottom = error "Should never be evaluated" +-} +-- modified from the existing compiler. no quadratic behavior +-- needs +-- pure :: IO a -> a +-- other alternatives: +-- 1. use reference cells +-- 2. implement exec in Lisp + +exec p = os requests `bindIO` \x -> unitIO () where + requests = p responses + responses = pureIO (os requests) + +os :: [Request] -> IO [Response] +os [] = unitIO [] +os (q:qs) = processRequest q `bindIO` \r -> + os qs `bindIO` \rs -> + unitIO (r:rs) + +processRequest :: Request -> IO Response + +-- This needs to be rewritten in terms of the continuation based defs + +processRequest request = + case request of + +-- File system requests + ReadFile name -> + primReadStringFile name `bindIO` \a -> + case a of + Succ s -> unitIO (Str s) + Fail e -> unitIO (Failure e) + WriteFile name contents -> + primWriteStringFile name contents `bindIO` \a -> + case a of + MaybeNot -> unitIO Success + Maybe e -> unitIO (Failure e) + AppendFile name contents -> + primAppendStringFile name contents `bindIO` \a -> + case a of + MaybeNot -> unitIO Success + Maybe e -> unitIO (Failure e) + ReadBinFile name -> + primReadBinFile name `bindIO` \a -> + case a of + Succ s -> unitIO (Bn s) + Fail e -> unitIO (Failure e) + WriteBinFile name bin -> + primWriteBinFile name bin `bindIO` \a -> + case a of + MaybeNot -> unitIO Success + Maybe e -> unitIO (Failure e) + AppendBinFile name bin -> + primAppendBinFile name bin `bindIO` \a -> + case a of + MaybeNot -> unitIO Success + Maybe e -> unitIO (Failure e) + DeleteFile name -> + primDeleteFile name `bindIO` \a -> + case a of + MaybeNot -> Success + Maybe e -> unitIO (Failure e) + StatusFile name -> + primStatusFile name `bindIO` \a -> + case a of + Succ s -> unitIO (Str s) + Fail e -> unitIO (Failure e) + +-- Channel system requests + ReadChan name -> + primReadChan name `bindIO` \a -> + case a of + Succ s -> unitIO (Str s) + Fail e -> unitIO (Failure e) + AppendChan name string -> + primAppendChan name string `bindIO` \a -> + case a of + MaybeNot -> unitIO Success + Maybe e -> unitIO (Failure e) + ReadBinChan name -> + primReadBinChan name `bindIO` \a -> + case a of + Succ s -> unitIO (Bn s) + Fail e -> unitIO (Failure e) + AppendBinChan name bin -> + primAppendBinChan name bin `bindIO` \a -> + case a of + MaybeNot -> unitIO Success + Maybe e -> unitIO (Failure e) + StatusChan name -> + primStatusChan name `bindIO` \a -> + case a of + Succ s -> unitIO (Str s) + Fail e -> unitIO (Failure e) + +-- Environment requests + Echo status -> + primEcho status `bindIO` \a -> + case a of + Succ s -> unitIO (Str s) + Fail e -> unitIO (Failure e) + GetArgs -> + primGetArgs `bindIO` \a -> + case a of + Succ s -> unitIO (Str s) + Fail e -> unitIO (Failure e) + GetProgName -> + primProgArgs `bindIO` \a -> + case a of + Succ s -> unitIO (Str s) + Fail e -> unitIO (Failure e) + GetEnv name -> + primGetEnv name `bindIO` \a -> + case a of + Succ s -> unitIO (Str s) + Fail e -> unitIO (Failure e) + SetEnv name string -> + primGetEnv name string `bindIO` \a -> + case a of + Succ s -> unitIO (Str s) + Fail e -> unitIO (Failure e) + _ -> unitIO (Failure (OtherError "Unrecognized IO Feature")) + +-- Monadic Style IO +-- Channel system requests + diff --git a/progs/prelude/PreludePrims.hi b/progs/prelude/PreludePrims.hi new file mode 100644 index 0000000..737a448 --- /dev/null +++ b/progs/prelude/PreludePrims.hi @@ -0,0 +1,252 @@ +-- interface.scm -- define interface to primitives +-- +-- author : Sandra & John +-- date : 24 Apr 1992 +-- +-- This file declares the interface to the runtime system primitives. +-- The actual definitions for the Lisp functions all appear elsewhere; +-- they all have names like prim.xxx. (They can actually be macros +-- instead of functions since they're never referenced by name.) + +interface PreludePrims where + +{-# Prelude #-} + +import PreludeCore(Int,Integer,Float,Double,Char,Bool) +import PreludeRational(Rational) + +error :: String -> a +primCharToInt :: Char -> Int +primIntToChar :: Int -> Char +primEqChar, primNeqChar, primLeChar, primGtChar, primLsChar, primGeChar + :: Char -> Char -> Bool +primMaxChar :: Int +primEqFloat, primNeqFloat, primLeFloat, primGtFloat, primLsFloat, primGeFloat + :: Float -> Float -> Bool +primFloatMax, primFloatMin :: Float -> Float -> Float +primEqDouble, primNeqDouble, primLeDouble, primGtDouble, + primLsDouble, primGeDouble + :: Double -> Double -> Bool +primDoubleMax, primDoubleMin :: Double -> Double -> Double +primPlusFloat, primMinusFloat, primMulFloat, primDivFloat + :: Float -> Float -> Float +primPlusDouble, primMinusDouble, primMulDouble, primDivDouble + :: Double -> Double -> Double +primNegFloat, primAbsFloat :: Float -> Float +primNegDouble, primAbsDouble :: Double -> Double +primExpFloat, primLogFloat, primSqrtFloat, primSinFloat, primCosFloat, + primTanFloat, primAsinFloat, primAcosFloat, primAtanFloat, primSinhFloat, + primCoshFloat, primTanhFloat, primAsinhFloat, primAcoshFloat, primAtanhFloat + :: Float -> Float +primExpDouble, primLogDouble, primSqrtDouble, primSinDouble, primCosDouble, + primTanDouble, primAsinDouble, primAcosDouble, primAtanDouble, primSinhDouble, + primCoshDouble, primTanhDouble, primAsinhDouble, primAcoshDouble, primAtanhDouble + :: Double -> Double +primPiFloat :: Float +primPiDouble :: Double +primRationalToFloat :: Rational -> Float +primRationalToDouble :: Rational -> Double +primFloatToRational :: Float -> Rational +primDoubleToRational :: Double -> Rational +primFloatDigits :: Int +primFloatRadix :: Integer +primFloatMinExp :: Int +primFloatMaxExp :: Int +primFloatRange :: Float -> (Int, Int) +primDecodeFloat :: Float -> (Integer, Int) +primEncodeFloat :: Integer -> Int -> Float +primDoubleDigits :: Int +primDoubleRadix :: Integer +primDoubleMinExp :: Int +primDoubleMaxExp :: Int +primDoubleRange :: Double -> (Int, Int) +primDecodeDouble :: Double -> (Integer, Int) +primEncodeDouble :: Integer -> Int -> Double +primEqInt, primNeqInt, primLeInt, primGtInt, primLsInt, primGeInt + :: Int -> Int -> Bool +primIntMax, primIntMin :: Int -> Int -> Int +primEqInteger, primNeqInteger, primLeInteger, primGtInteger, + primLsInteger, primGeInteger + :: Integer -> Integer -> Bool +primIntegerMax, primIntegerMin :: Integer -> Integer -> Integer +primPlusInt, primMinusInt, primMulInt :: Int -> Int -> Int +primMinInt,primMaxInt :: Int +primNegInt, primAbsInt :: Int -> Int +primPlusInteger, primMinusInteger, primMulInteger :: Integer -> Integer -> Integer +primNegInteger, primAbsInteger :: Integer -> Integer +primQuotRemInt :: Int -> Int -> (Int, Int) +primQuotRemInteger :: Integer -> Integer -> (Integer, Integer) +primIntegerToInt :: Integer -> Int +primIntToInteger :: Int -> Integer +primNullBin :: Bin +primIsNullBin :: Bin -> Bool +primShowBinInt :: Int -> Bin -> Bin +primShowBinInteger :: Integer -> Bin -> Bin +primShowBinFloat :: Float -> Bin -> Bin +primShowBinDouble :: Double -> Bin -> Bin +primReadBinInt :: Bin -> (Int,Bin) +primReadBinInteger :: Bin -> (Integer,Bin) +primReadBinFloat :: Bin -> (Float,Bin) +primReadBinDouble :: Bin -> (Double,Bin) +primReadBinSmallInt :: Bin -> Int -> (Int,Bin) +primAppendBin :: Bin -> Bin -> Bin + +primStringEq :: [Char] -> [Char] -> Bool + +primAppend :: [a] -> [a] -> [a] +primTake :: Int -> [a] -> [a] + +foldr :: (a -> b -> b) -> b -> [a] -> b +build :: ((a -> [a] -> [a]) -> [b] -> [c]) -> [c] + + + +-- I've assigned complexities for arithmetic primitives as follows: +-- Int and Char comparisons and arithmetic are very cheap (complexity 1). +-- Double and Float comparsions are also cheap, but most implementations +-- need to box the results of floating-point arithmetic so I have given +-- them a complexity of 3. +-- Integer operations need to do an extra bignum check that has a fixed +-- overhead. I assume that actual bignums will be rare and give them +-- all a complexity of 2. + +{-# +error :: LispName("prim.abort") +primCharToInt :: LispName("prim.char-to-int"), Complexity(0),NoConversion +primIntToChar :: LispName("prim.int-to-char"), Complexity(0),NoConversion +primEqChar :: LispName("prim.eq-char"), Complexity(1), NoConversion +primNeqChar:: LispName("prim.not-eq-char"), Complexity(1), NoConversion +primLeChar :: LispName("prim.le-char"), Complexity(1), NoConversion +primGtChar :: LispName("prim.not-le-char"), Complexity(1), NoConversion +primLsChar :: LispName("prim.lt-char"), Complexity(1), NoConversion +primGeChar :: LispName("prim.not-lt-char"), Complexity(1), NoConversion +primMaxChar :: LispName("prim.max-char"), NoConversion +primEqFloat :: LispName("prim.eq-float"), Complexity(1) +primNeqFloat :: LispName("prim.not-eq-float"), Complexity(1) +primLeFloat :: LispName("prim.le-float"), Complexity(1) +primGtFloat :: LispName("prim.not-le-float"), Complexity(1) +primLsFloat :: LispName("prim.lt-float"), Complexity(1) +primGeFloat :: LispName("prim.not-lt-float"), Complexity(1) +primFloatMax :: LispName("prim.float-max"), Complexity(3) +primFloatMin :: LispName("prim.float-min"), Complexity(3) +primEqDouble :: LispName("prim.eq-double"), Complexity(1) +primNeqDouble :: LispName("prim.not-eq-double"), Complexity(1) +primLeDouble :: LispName("prim.le-double"), Complexity(1) +primGtDouble :: LispName("prim.not-le-double"), Complexity(1) +primLsDouble :: LispName("prim.lt-double"), Complexity(1) +primGeDouble :: LispName("prim.not-lt-double"), Complexity(1) +primDoubleMax :: LispName("prim.double-max"), Complexity(3) +primDoubleMin :: LispName("prim.double-min"), Complexity(3) +primPlusFloat :: LispName("prim.plus-float"), Complexity(3) +primMinusFloat :: LispName("prim.minus-float"), Complexity(3) +primMulFloat :: LispName("prim.mul-float"), Complexity(3) +primDivFloat :: LispName("prim.div-float"), Complexity(3) +primPlusDouble :: LispName("prim.plus-double"), Complexity(3) +primMinusDouble :: LispName("prim.minus-double"), Complexity(3) +primMulDouble :: LispName("prim.mul-double"), Complexity(3) +primDivDouble :: LispName("prim.div-double"), Complexity(3) +primNegFloat :: LispName("prim.neg-float"), Complexity(3) +primAbsFloat :: LispName("prim.abs-float"), Complexity(3) +primNegDouble :: LispName("prim.neg-double"), Complexity(3) +primAbsDouble :: LispName("prim.abs-double"), Complexity(3) +primExpFloat :: LispName("prim.exp-float") +primLogFloat :: LispName("prim.log-float") +primSqrtFloat :: LispName("prim.sqrt-float") +primSinFloat :: LispName("prim.sin-float") +primCosFloat :: LispName("prim.cos-float") +primTanFloat :: LispName("prim.tan-float") +primAsinFloat :: LispName("prim.asin-float") +primAcosFloat :: LispName("prim.acos-float") +primAtanFloat :: LispName("prim.atan-float") +primSinhFloat :: LispName("prim.sinh-float") +primCoshFloat :: LispName("prim.cosh-float") +primTanhFloat :: LispName("prim.tanh-float") +primAsinhFloat :: LispName("prim.asinh-float") +primAcoshFloat :: LispName("prim.acosh-float") +primAtanhFloat :: LispName("prim.atanh-float") +primExpDouble :: LispName("prim.exp-double") +primLogDouble :: LispName("prim.log-double") +primSqrtDouble :: LispName("prim.sqrt-double") +primSinDouble :: LispName("prim.sin-double") +primCosDouble :: LispName("prim.cos-double") +primTanDouble :: LispName("prim.tan-double") +primAsinDouble :: LispName("prim.asin-double") +primAcosDouble :: LispName("prim.acos-double") +primAtanDouble :: LispName("prim.atan-double") +primSinhDouble :: LispName("prim.sinh-double") +primCoshDouble :: LispName("prim.cosh-double") +primTanhDouble :: LispName("prim.tanh-double") +primAsinhDouble :: LispName("prim.asinh-double") +primAcoshDouble :: LispName("prim.acosh-double") +primAtanhDouble :: LispName("prim.atanh-double") +primPiFloat :: LispName("prim.pi-float") +primPiDouble :: LispName("prim.pi-double") +primRationalToFloat :: LispName("prim.rational-to-float"), Complexity(3) +primRationalToDouble :: LispName("prim.rational-to-double"), Complexity(3) +primFloatToRational :: LispName("prim.float-to-rational"), Complexity(3) +primDoubleToRational :: LispName("prim.double-to-rational"), Complexity(3) +primFloatDigits :: LispName("prim.float-digits") +primFloatRadix :: LispName("prim.float-radix") +primFloatMinExp :: LispName("prim.float-min-exp") +primFloatMaxExp :: LispName("prim.float-max-exp") +primFloatRange :: LispName("prim.float-range") +primDecodeFloat :: LispName("prim.decode-float") +primEncodeFloat :: LispName("prim.encode-float") +primDoubleDigits :: LispName("prim.double-digits") +primDoubleRadix :: LispName("prim.double-radix") +primDoubleMinExp :: LispName("prim.double-min-exp") +primDoubleMaxExp :: LispName("prim.double-max-exp") +primDoubleRange :: LispName("prim.double-range") +primDecodeDouble :: LispName("prim.decode-double") +primEncodeDouble :: LispName("prim.encode-double") +primEqInt :: LispName("prim.eq-int"), Complexity(1) +primNeqInt:: LispName("prim.not-eq-int"), Complexity(1) +primLeInt :: LispName("prim.le-int"), Complexity(1) +primGtInt :: LispName("prim.not-le-int"), Complexity(1) +primLsInt :: LispName("prim.lt-int"), Complexity(1) +primGeInt :: LispName("prim.not-lt-int"), Complexity(1) +primIntMax :: LispName("prim.int-max"), Complexity(1) +primIntMin :: LispName("prim.int-min"), Complexity(1) +primEqInteger :: LispName("prim.eq-integer"), Complexity(2) +primNeqInteger:: LispName("prim.not-eq-integer"), Complexity(2) +primLeInteger :: LispName("prim.le-integer"), Complexity(2) +primGtInteger :: LispName("prim.not-le-integer"), Complexity(2) +primLsInteger :: LispName("prim.lt-integer"), Complexity(2) +primGeInteger :: LispName("prim.not-lt-integer"), Complexity(2) +primIntegerMax :: LispName("prim.integer-max"), Complexity(2) +primIntegerMin :: LispName("prim.integer-min"), Complexity(2) +primPlusInt :: LispName("prim.plus-int"), Complexity(1) +primMinusInt :: LispName("prim.minus-int"), Complexity(1) +primMulInt :: LispName("prim.mul-int"), Complexity(1) +primMinInt :: LispName("prim.minint") +primMaxInt :: LispName("prim.maxint") +primNegInt :: LispName("prim.neg-int"), Complexity(1) +primAbsInt :: LispName("prim.abs-int"), Complexity(1) +primPlusInteger :: LispName("prim.plus-integer"), Complexity(2) +primMinusInteger :: LispName("prim.minus-integer"), Complexity(2) +primMulInteger :: LispName("prim.mul-integer"), Complexity(2) +primNegInteger :: LispName("prim.neg-integer"), Complexity(2) +primAbsInteger :: LispName("prim.abs-integer"), Complexity(2) +primQuotRemInt :: LispName("prim.div-rem-int") +primQuotRemInteger :: LispName("prim.div-rem-integer") +primIntegerToInt :: LispName("prim.integer-to-int"), Complexity(1) +primIntToInteger :: LispName("prim.int-to-integer"), Complexity(0) +primNullBin :: LispName("prim.nullbin") +primIsNullBin :: LispName("prim.is-null-bin"), Complexity(1) +primShowBinInt :: LispName("prim.show-bin-int"), Complexity(2) +primShowBinInteger :: LispName("prim.show-bin-integer"), Complexity(2) +primShowBinFloat :: LispName("prim.show-bin-float"), Complexity(2) +primShowBinDouble :: LispName("prim.show-bin-double"), Complexity(2) +primReadBinInt :: LispName("prim.read-bin-int") +primReadBinInteger :: LispName("prim.read-bin-integer") +primReadBinFloat :: LispName("prim.read-bin-float") +primReadBinDouble :: LispName("prim.read-bin-double") +primReadBinSmallInt :: LispName("prim.read-bin-small-int") +primAppendBin :: LispName("prim.append-bin") +primStringEq :: LispName("prim.string-eq"), Strictness("S,S"), NoConversion +primAppend :: LispName("prim.append"), Strictness("S,N"), NoConversion +primTake :: LispName("prim.take"), Strictness("S,S"), NoConversion +foldr :: LispName("prim.foldr"), Strictness("N,N,S"), NoConversion +build :: LispName("prim.build"), Strictness("S"), NoConversion + +#-} diff --git a/progs/prelude/PreludePrims.hu b/progs/prelude/PreludePrims.hu new file mode 100644 index 0000000..fd2cdcc --- /dev/null +++ b/progs/prelude/PreludePrims.hu @@ -0,0 +1,4 @@ +:output $PRELUDEBIN/PreludePrims +:stable +:prelude +PreludePrims.hi diff --git a/progs/prelude/PreludeRatio.hs b/progs/prelude/PreludeRatio.hs new file mode 100644 index 0000000..564558e --- /dev/null +++ b/progs/prelude/PreludeRatio.hs @@ -0,0 +1,98 @@ +-- Standard functions on rational numbers + +module PreludeRatio ( + Ratio, Rational(..), (%), numerator, denominator, approxRational ) where + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +infixl 7 %, :% + +prec = 7 + +data (Integral a) => Ratio a = a {-# STRICT #-} :% a {-# STRICT #-} + deriving (Eq, Binary) + +type Rational = Ratio Integer + +(%) :: (Integral a) => a -> a -> Ratio a +numerator, denominator :: (Integral a) => Ratio a -> a +approxRational :: (RealFrac a) => a -> a -> Rational + + +reduce _ 0 = error "(%){PreludeRatio}: zero denominator" +reduce x y = (x `quot` d) :% (y `quot` d) + where d = gcd x y + + +x % y = reduce (x * signum y) (abs y) + +numerator (x:%y) = x + +denominator (x:%y) = y + + +instance (Integral a) => Ord (Ratio a) where + (x:%y) <= (x':%y') = x * y' <= x' * y + (x:%y) < (x':%y') = x * y' < x' * y + +instance (Integral a) => Num (Ratio a) where + (x:%y) + (x':%y') = reduce (x*y' + x'*y) (y*y') + (x:%y) * (x':%y') = reduce (x * x') (y * y') + negate (x:%y) = (-x) :% y + abs (x:%y) = abs x :% y + signum (x:%y) = signum x :% 1 + fromInteger x = fromInteger x :% 1 + +instance (Integral a) => Real (Ratio a) where + toRational (x:%y) = toInteger x :% toInteger y + +instance (Integral a) => Fractional (Ratio a) where + (x:%y) / (x':%y') = (x*y') % (y*x') + recip (x:%y) = if x < 0 then (-y) :% (-x) else y :% x + fromRational (x:%y) = fromInteger x :% fromInteger y + +instance (Integral a) => RealFrac (Ratio a) where + properFraction (x:%y) = (fromIntegral q, r:%y) + where (q,r) = quotRem x y + +instance (Integral a) => Enum (Ratio a) where + enumFrom = iterate ((+)1) + enumFromThen n m = iterate ((+)(m-n)) n + +instance (Integral a) => Text (Ratio a) where + readsPrec p = readParen (p > prec) + (\r -> [(x%y,u) | (x,s) <- reads r, + ("%",t) <- lex s, + (y,u) <- reads t ]) + + showsPrec p (x:%y) = showParen (p > prec) + (shows x . showString " % " . shows y) + + +-- approxRational, applied to two real fractional numbers x and epsilon, +-- returns the simplest rational number within epsilon of x. A rational +-- number n%d in reduced form is said to be simpler than another n'%d' if +-- abs n <= abs n' && d <= d'. Any real interval contains a unique +-- simplest rational; here, for simplicity, we assume a closed rational +-- interval. If such an interval includes at least one whole number, then +-- the simplest rational is the absolutely least whole number. Otherwise, +-- the bounds are of the form q%1 + r%d and q%1 + r'%d', where abs r < d +-- and abs r' < d', and the simplest rational is q%1 + the reciprocal of +-- the simplest rational between d'%r' and d%r. + +approxRational x eps = simplest (x-eps) (x+eps) + where simplest x y | y < x = simplest y x + | x == y = xr + | x > 0 = simplest' n d n' d' + | y < 0 = - simplest' (-n') d' (-n) d + | otherwise = 0 :% 1 + where xr@(n:%d) = toRational x + (n':%d') = toRational y + + simplest' n d n' d' -- assumes 0 < n%d < n'%d' + | r == 0 = q :% 1 + | q /= q' = (q+1) :% 1 + | otherwise = (q*n''+d'') :% n'' + where (q,r) = quotRem n d + (q',r') = quotRem n' d' + (n'':%d'') = simplest' d' r' d r diff --git a/progs/prelude/PreludeText.hs b/progs/prelude/PreludeText.hs new file mode 100644 index 0000000..9e4e353 --- /dev/null +++ b/progs/prelude/PreludeText.hs @@ -0,0 +1,260 @@ +module PreludeText ( + reads, shows, show, read, lex, + showChar, showString, readParen, showParen, readLitChar, showLitChar, + readSigned, showSigned, readDec, showInt, readFloat, showFloat ) where + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +reads :: (Text a) => ReadS a +reads = readsPrec 0 + +shows :: (Text a) => a -> ShowS +shows = showsPrec 0 + +read :: (Text a) => String -> a +read s = case [x | (x,t) <- reads s, ("","") <- lex t] of + [x] -> x + [] -> error "read{PreludeText}: no parse" + _ -> error "read{PreludeText}: ambiguous parse" + +show :: (Text a) => a -> String +show x = shows x "" + +showChar :: Char -> ShowS +showChar = (:) + +showString :: String -> ShowS +showString = (++) + +showParen :: Bool -> ShowS -> ShowS +showParen b p = if b then showChar '(' . p . showChar ')' else p + +readParen :: Bool -> ReadS a -> ReadS a +readParen b g = if b then mandatory else optional + where optional r = g r ++ mandatory r + mandatory r = [(x,u) | ("(",s) <- lex r, + (x,t) <- optional s, + (")",u) <- lex t ] + +lex :: ReadS String +lex "" = [("","")] +lex (c:s) | isSpace c = lex (dropWhile isSpace s) +lex ('-':'-':s) = case dropWhile (/= '\n') s of + '\n':t -> lex t + _ -> [] -- unterminated end-of-line + -- comment + +lex ('{':'-':s) = lexNest lex s + where + lexNest f ('-':'}':s) = f s + lexNest f ('{':'-':s) = lexNest (lexNest f) s + lexNest f (c:s) = lexNest f s + lexNest _ "" = [] -- unterminated + -- nested comment + +lex ('<':'-':s) = [("<-",s)] +lex ('\'':s) = [('\'':ch++"'", t) | (ch,'\'':t) <- lexLitChar s, + ch /= "'" ] +lex ('"':s) = [('"':str, t) | (str,t) <- lexString s] + where + lexString ('"':s) = [("\"",s)] + lexString s = [(ch++str, u) + | (ch,t) <- lexStrItem s, + (str,u) <- lexString t ] + + lexStrItem ('\\':'&':s) = [("\\&",s)] + lexStrItem ('\\':c:s) | isSpace c + = [("\\&",t) | '\\':t <- [dropWhile isSpace s]] + lexStrItem s = lexLitChar s + +lex (c:s) | isSingle c = [([c],s)] + | isSym1 c = [(c:sym,t) | (sym,t) <- [span isSym s]] + | isAlpha c = [(c:nam,t) | (nam,t) <- [span isIdChar s]] + | isDigit c = [(c:ds++fe,t) | (ds,s) <- [span isDigit s], + (fe,t) <- lexFracExp s ] + | otherwise = [] -- bad character + where + isSingle c = c `elem` ",;()[]{}_" + isSym1 c = c `elem` "-~" || isSym c + isSym c = c `elem` "!@#$%&*+./<=>?\\^|:" + isIdChar c = isAlphanum c || c `elem` "_'" + + lexFracExp ('.':s) = [('.':ds++e,u) | (ds,t) <- lexDigits s, + (e,u) <- lexExp t ] + lexFracExp s = [("",s)] + + lexExp (e:s) | e `elem` "eE" + = [(e:c:ds,u) | (c:t) <- [s], c `elem` "+-", + (ds,u) <- lexDigits t] ++ + [(e:ds,t) | (ds,t) <- lexDigits s] + lexExp s = [("",s)] + +lexDigits :: ReadS String +lexDigits = nonnull isDigit + +nonnull :: (Char -> Bool) -> ReadS String +nonnull p s = [(cs,t) | (cs@(_:_),t) <- [span p s]] + +lexLitChar :: ReadS String +lexLitChar ('\\':s) = [('\\':esc, t) | (esc,t) <- lexEsc s] + where + lexEsc (c:s) | c `elem` "abfnrtv\\\"'" = [([c],s)] + lexEsc ('^':c:s) | c >= '@' && c <= '_' = [(['^',c],s)] + lexEsc s@(d:_) | isDigit d = lexDigits s + lexEsc ('o':s) = [('o':os, t) | (os,t) <- nonnull isOctDigit s] + lexEsc ('x':s) = [('x':xs, t) | (xs,t) <- nonnull isHexDigit s] + lexEsc s@(c:_) | isUpper c + = case [(mne,s') | mne <- "DEL" : elems asciiTab, + ([],s') <- [match mne s] ] + of (pr:_) -> [pr] + [] -> [] + lexEsc _ = [] +lexLitChar (c:s) = [([c],s)] +lexLitChar "" = [] + +isOctDigit c = c >= '0' && c <= '7' +isHexDigit c = isDigit c || c >= 'A' && c <= 'F' + || c >= 'a' && c <= 'f' + +match :: (Eq a) => [a] -> [a] -> ([a],[a]) +match (x:xs) (y:ys) | x == y = match xs ys +match xs ys = (xs,ys) + +asciiTab = listArray ('\NUL', ' ') + ["NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", + "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", + "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US", + "SP"] + + + +readLitChar :: ReadS Char +readLitChar ('\\':s) = readEsc s + where + readEsc ('a':s) = [('\a',s)] + readEsc ('b':s) = [('\b',s)] + readEsc ('f':s) = [('\f',s)] + readEsc ('n':s) = [('\n',s)] + readEsc ('r':s) = [('\r',s)] + readEsc ('t':s) = [('\t',s)] + readEsc ('v':s) = [('\v',s)] + readEsc ('\\':s) = [('\\',s)] + readEsc ('"':s) = [('"',s)] + readEsc ('\'':s) = [('\'',s)] + readEsc ('^':c:s) | c >= '@' && c <= '_' + = [(chr (ord c - ord '@'), s)] + readEsc s@(d:_) | isDigit d + = [(chr n, t) | (n,t) <- readDec s] + readEsc ('o':s) = [(chr n, t) | (n,t) <- readOct s] + readEsc ('x':s) = [(chr n, t) | (n,t) <- readHex s] + readEsc s@(c:_) | isUpper c + = let table = ('\DEL' := "DEL") : assocs asciiTab + in case [(c,s') | (c := mne) <- table, + ([],s') <- [match mne s]] + of (pr:_) -> [pr] + [] -> [] + readEsc _ = [] +readLitChar (c:s) = [(c,s)] + +showLitChar :: Char -> ShowS +showLitChar c | c > '\DEL' = showChar '\\' . protectEsc isDigit (shows (ord c)) +showLitChar '\DEL' = showString "\\DEL" +showLitChar '\\' = showString "\\\\" +showLitChar c | c >= ' ' = showChar c +showLitChar '\a' = showString "\\a" +showLitChar '\b' = showString "\\b" +showLitChar '\f' = showString "\\f" +showLitChar '\n' = showString "\\n" +showLitChar '\r' = showString "\\r" +showLitChar '\t' = showString "\\t" +showLitChar '\v' = showString "\\v" +showLitChar '\SO' = protectEsc (== 'H') (showString "\\SO") +showLitChar c = showString ('\\' : asciiTab!c) + +protectEsc p f = f . cont + where cont s@(c:_) | p c = "\\&" ++ s + cont s = s + +readDec, readOct, readHex :: (Integral a) => ReadS a +readDec = readInt 10 isDigit (\d -> ord d - ord '0') +readOct = readInt 8 isOctDigit (\d -> ord d - ord '0') +readHex = readInt 16 isHexDigit hex + where hex d = ord d - (if isDigit d then ord '0' + else ord (if isUpper d then 'A' else 'a') + - 10) + +readInt :: (Integral a) => a -> (Char -> Bool) -> (Char -> Int) -> ReadS a +readInt radix isDig digToInt s = + [(foldl1 (\n d -> n * radix + d) (map (fromIntegral . digToInt) ds), r) + | (ds,r) <- nonnull isDig s ] + +showInt :: (Integral a) => a -> ShowS +showInt n r = let (n',d) = quotRem n 10 + r' = chr (ord '0' + fromIntegral d) : r + in if n' == 0 then r' else showInt n' r' + +readSigned:: (Real a) => ReadS a -> ReadS a +readSigned readPos = readParen False read' + where read' r = read'' r ++ + [(-x,t) | ("-",s) <- lex r, + (x,t) <- read'' s] + read'' r = [(n,s) | (str,s) <- lex r, + (n,"") <- readPos str] + +showSigned:: (Real a) => (a -> ShowS) -> Int -> a -> ShowS +showSigned showPos p x = if x < 0 then showParen (p > 6) + (showChar '-' . showPos (-x)) + else showPos x + + +-- The functions readFloat and showFloat below use rational arithmetic +-- to insure correct conversion between the floating-point radix and +-- decimal. It is often possible to use a higher-precision floating- +-- point type to obtain the same results. + +readFloat:: (RealFloat a) => ReadS a +readFloat r = [(fromRational ((n%1)*10^^(k-d)), t) | (n,d,s) <- readFix r, + (k,t) <- readExp s] + where readFix r = [(read (ds++ds'), length ds', t) + | (ds,'.':s) <- lexDigits r, + (ds',t) <- lexDigits s ] + + readExp (e:s) | e `elem` "eE" = readExp' s + readExp s = [(0,s)] + + readExp' ('-':s) = [(-k,t) | (k,t) <- readDec s] + readExp' ('+':s) = readDec s + readExp' s = readDec s + +-- The number of decimal digits m below is chosen to guarantee +-- read (show x) == x. See +-- Matula, D. W. A formalization of floating-point numeric base +-- conversion. IEEE Transactions on Computers C-19, 8 (1970 August), +-- 681-692. + +showFloat:: (RealFloat a) => a -> ShowS +showFloat x = + if x == 0 then showString ("0." ++ take (m-1) (repeat '0')) + else if e >= m-1 || e < 0 then showSci else showFix + where + showFix = showString whole . showChar '.' . showString frac + where (whole,frac) = splitAt (e+1) (show sig) + showSci = showChar d . showChar '.' . showString frac + . showChar 'e' . shows e + where (d:frac) = show sig + (m, sig, e) = if b == 10 then (w, s, n+w-1) + else (m', sig', e' ) + m' = ceiling + (fromIntegral w * log (fromInteger b) / log 10 :: Double) + + 1 + (sig', e') = if sig1 >= 10^m' then (round (t/10), e1+1) + else if sig1 < 10^(m'-1) then (round (t*10), e1-1) + else (sig1, e1 ) + sig1 :: Integer + sig1 = round t + t = s%1 * (b%1)^^n * 10^^(m'-e1-1) + e1 = floor (logBase 10 x) + (s, n) = decodeFloat x + b = floatRadix x + w = floatDigits x diff --git a/progs/prelude/PreludeTuple.hs b/progs/prelude/PreludeTuple.hs new file mode 100644 index 0000000..4f2637a --- /dev/null +++ b/progs/prelude/PreludeTuple.hs @@ -0,0 +1,213 @@ +module PreludeTuple where + +{-#Prelude#-} -- Indicates definitions of compiler prelude symbols + +import PreludeTuplePrims + +-- This module contains support routines which handle tuple instances. +-- These are based on a implementation level data type which represents +-- general tuples and a data type to hold the set of dictionaries which +-- are associated with the tuple. + +-- Each of these functions takes the tupledicts as the first argument. +-- Force all of these functions to take strict arguments because they'll +-- never be called with 0-length tuples anyway. + +-- The following primitives operate on tuples. + +-- tupleSize :: TupleDicts -> Int +-- tupleSel :: Tuple -> Int -> Int -> a +-- dictSel :: TupleDicts -> method -> Int -> a +-- listToTuple :: [a] -> Tuple + +-- Eq functions + +tupleEq :: TupleDicts -> Tuple -> Tuple -> Bool +{-# tupleEq :: Strictness("S,S,S") #-} +tupleEq dicts x y = tupleEq1 0 where + tupleEq1 i | i == size = True + | otherwise = + ((dictSel (cmpEq dicts i)) x' y') && tupleEq1 (i+1) + where + x' = tupleSel x i size + y' = tupleSel y i size + size = tupleSize dicts + +cmpEq x y = x == y + +tupleNeq dicts x y = not (tupleEq dicts x y) + +-- Ord functions + +tupleLe :: TupleDicts -> Tuple -> Tuple -> Bool +{-# tupleLe :: Strictness("S,S,S") #-} +tupleLe dicts x y = tupleLe1 0 where + tupleLe1 i | i == size = False + | (dictSel (cmpLs dicts i)) x' y' = True + | (dictSel (ordEq dicts i)) x' y' = tupleLe1 (i+1) + | otherwise = False + where + x' = tupleSel x i size + y' = tupleSel y i size + size = tupleSize dicts + +cmpLs x y = x < y + +ordEq :: Ord a => a -> a -> Bool +ordEq x y = x == y + +tupleLeq :: TupleDicts -> Tuple -> Tuple -> Bool +{-# tupleLeq :: Strictness("S,S,S") #-} +tupleLeq dicts x y = tupleLeq1 0 where + tupleLeq1 i | i == size = True + | (dictSel (cmpLs dicts i)) x' y' = True + | (dictSel (ordEq dicts i)) x' y' = tupleLeq1 (i+1) + | otherwise = False + where + x' = tupleSel x i size + y' = tupleSel y i size + size = tupleSize dicts + +tupleGe :: TupleDicts -> Tuple -> Tuple -> Bool +tupleGe d x y = tupleLe d y x + +tupleGeq :: TupleDicts -> Tuple -> Tuple -> Bool +tupleGeq d x y = tupleLeq d y x + +tupleMax,tupleMin :: TupleDicts -> Tuple -> Tuple -> Tuple +tupleMax d x y = if tupleGe d x y then x else y +tupleMin d x y = if tupleLe d x y then x else y + +-- Ix functions + +tupleRange :: TupleDicts -> (Tuple,Tuple) -> [Tuple] +{-# tupleRange :: Strictness("S,S") #-} + +tupleRange dicts (x,y) = map listToTuple (tupleRange' 0) where + tupleRange' i | i == size = [[]] + | otherwise = + [(i1 : i2) | i1 <- r, i2 <- tupleRange' (i+1)] + where + x' = tupleSel x i size + y' = tupleSel y i size + r = (dictSel (range' dicts i)) (x',y') + size = tupleSize dicts + +range' x = range x + +tupleIndex :: TupleDicts -> (Tuple,Tuple) -> Tuple -> Int +{-# tupleIndex :: Strictness("S,S,S") #-} + +tupleIndex dicts (low,high) n = tupleIndex' (size-1) where + size = tupleSize dicts + tupleIndex' i | i == 0 = i' + | otherwise = i' + r' * (tupleIndex' (i-1)) + where + low' = tupleSel low i size + high' = tupleSel high i size + n' = tupleSel n i size + i' = (dictSel (index' dicts i)) (low',high') n' + r' = (dictSel (rangeSize dicts i)) (low',high') + +index' x = index x + +rangeSize :: (Ix a) => (a,a) -> Int +rangeSize (l,u) = index (l,u) u + 1 + +tupleInRange :: TupleDicts -> (Tuple,Tuple) -> Tuple -> Bool +{-# tupleInRange :: Strictness("S,S,S") #-} +tupleInRange dicts (low,high) n = tupleInRange' 0 where + size = tupleSize dicts + tupleInRange' i | i == size = True + | otherwise = (dictSel (inRange' dicts i)) (low',high') n' + && tupleInRange' (i+1) + where + low' = tupleSel low i size + high' = tupleSel high i size + n' = tupleSel n i size + +inRange' x = inRange x + +-- Text functions + +tupleReadsPrec :: TupleDicts -> Int -> ReadS Tuple + +tupleReadsPrec dicts p = readParen False + (\s -> map ( \ (t,w) -> (listToTuple t,w)) + (tRP' s 0)) + where + size = tupleSize dicts + tRP' s i | i == 0 = [(t':t,w) | + ("(",s1) <- lex s, + (t',s2) <- nextItem s1, + (t,w) <- tRP' s2 (i+1)] + | i == size = [([],w) | (")",w) <- lex s] + | otherwise = + [(t':t,w) | + (",",s1) <- lex s, + (t',s2) <- nextItem s1, + (t,w) <- tRP' s2 (i+1)] + where + nextItem s = (dictSel (reads dicts i)) s + +tupleShowsPrec :: TupleDicts -> Int -> Tuple -> ShowS + +tupleShowsPrec dicts p tuple = + showChar '(' . tSP' 0 + where + size = tupleSize dicts + tSP' i | i == (size-1) = + showTup . showChar ')' + | otherwise = + showTup . showChar ',' . tSP' (i+1) + where + showTup = (dictSel (shows dicts i)) (tupleSel tuple i size) + +tupleReadList :: TupleDicts -> ReadS [Tuple] + +tupleReadList dicts = + readParen False (\r -> [pr | ("[",s) <- lex r, + pr <- readl s]) + where readl s = [([],t) | ("]",t) <- lex s] ++ + [(x:xs,u) | (x,t) <- tupleReads s, + (xs,u) <- readl' t] + readl' s = [([],t) | ("]",t) <- lex s] ++ + [(x:xs,v) | (",",t) <- lex s, + (x,u) <- tupleReads t, + (xs,v) <- readl' u] + tupleReads s = tupleReadsPrec dicts 0 s + +tupleShowList :: TupleDicts -> [Tuple] -> ShowS + +tupleShowList dicts [] = showString "[]" +tupleShowList dicts (x:xs) + = showChar '[' . showsTuple x . showl xs + where showl [] = showChar ']' + showl (x:xs) = showString ", " . showsTuple x + . showl xs + showsTuple x = tupleShowsPrec dicts 0 x + +-- Binary functions + +tupleShowBin :: TupleDicts -> Tuple -> Bin -> Bin + +tupleShowBin dicts t bin = tSB' 0 + where + size = tupleSize dicts + tSB' i | i == size = bin + tSB' i | otherwise = + (dictSel (showBin' dicts i)) (tupleSel t i size) (tSB' (i+1)) + +showBin' x = showBin x + +tupleReadBin :: TupleDicts -> Bin -> (Tuple,Bin) + +tupleReadBin dicts bin = (listToTuple t,b) where + size = tupleSize dicts + (t,b) = tRB' bin 0 + tRB' b i | i == size = ([],b) + | otherwise = (t':ts,b') where + (t',b'') = (dictSel (readBin' dicts i)) b + (ts,b') = tRB' b'' (i+1) + +readBin' x = readBin x diff --git a/progs/prelude/PreludeTuplePrims.hi b/progs/prelude/PreludeTuplePrims.hi new file mode 100644 index 0000000..6af0dfd --- /dev/null +++ b/progs/prelude/PreludeTuplePrims.hi @@ -0,0 +1,48 @@ + +-- This is the interface to the primitives used to implement arbitrary +-- sized tuples. + +interface PreludeTuplePrims where + +{-# Prelude #-} + +-- The type checker fiddles around with the call to dictSel to use the +-- dictionary to resolve the overloading of a subexpression. The call +-- dictSel (exp dict i) will typecheck exp and use the ith component of +-- the tupleDict dict to resolve the overloading. No check is made to ensure +-- that the type of the dictionary matches the overloaded class! Beware! + +import PreludeData(Int) + +data Tuple +data TupleDicts + + +tupleSize :: TupleDicts -> Int +tupleSel :: Tuple -> Int -> Int -> a +dictSel :: TupleDicts -> Int -> a +listToTuple :: [a] -> Tuple +-- These are not called by haskell code directly; these are introduced +-- during dictionary conversion by the type checker. +tupleEqDict :: a +tupleOrdDict :: a +tupleIxDict :: a +tupleTextDict :: a +tupleBinaryDict :: a + +{-# +tupleSize :: LispName("prim.tupleSize"), Complexity(1) +tupleSel :: LispName("prim.tupleSel") +dictSel :: LispName("prim.dict-sel") +listToTuple :: LispName("prim.list->tuple"), NoConversion +tupleEqDict :: LispName("prim.tupleEqDict") +tupleOrdDict :: LispName("prim.tupleOrdDict") +tupleIxDict :: LispName("prim.tupleIxDict") +tupleTextDict :: LispName("prim.tupleTextDict") +tupleBinaryDict :: LispName("prim.tupleBinaryDict") + +#-} + + + + diff --git a/progs/prelude/PreludeTuplePrims.hu b/progs/prelude/PreludeTuplePrims.hu new file mode 100644 index 0000000..eaa0385 --- /dev/null +++ b/progs/prelude/PreludeTuplePrims.hu @@ -0,0 +1,4 @@ +:output $PRELUDEBIN/PreludeTuplePrims +:stable +:prelude +PreludeTuplePrims.hi diff --git a/progs/prelude/README b/progs/prelude/README new file mode 100644 index 0000000..2decc21 --- /dev/null +++ b/progs/prelude/README @@ -0,0 +1,12 @@ + +This is the actual prelude used by the Yale system. This contains a many +small changes to the standard prelude, mostly optimizer annotations. +PreludeIO is totally different since we have flushed streams in favor +of the monad. Primitives are defined using the Haskell to Lisp interface. + +Arrays are implemented internally using destructive updates - no array +primitive involves more than one copy operation and lookup is constant +time. + +The data constructors for Complex and Rational are strict. + diff --git a/progs/tutorial/README b/progs/tutorial/README new file mode 100644 index 0000000..defe248 --- /dev/null +++ b/progs/tutorial/README @@ -0,0 +1,12 @@ + +This is the text of the online version of the tutorial. It is set up to +run under Emacs only. Form feeds divide the pages of the tutorial. Emacs +has a special mode just for the tutorial which makes a local copy of each +page for the user to scribble on without disturbing this source. + +It is possible that this could be adapted to the command interface by +breaking it up into one file per page. + +This is still preliminary - we need to work on the text and examples. +Please send comments to haskell-request@cs.yale.edu. + diff --git a/progs/tutorial/tutorial.hs b/progs/tutorial/tutorial.hs new file mode 100644 index 0000000..eb6a78d --- /dev/null +++ b/progs/tutorial/tutorial.hs @@ -0,0 +1,2143 @@ +-- Page 0 Introduction + +This is a programming supplement to `A Gentle Introduction to Haskell' +by Hudak and Fasel. This supplement augments the tutorial by +providing executable Haskell programs which you can run and +experiment with. All program fragments in the tutorial are +found here, as well as other examples not included in the tutorial. + + +Using This Tutorial + +You should have a copy of both the `Gentle Introduction' and the +report itself to make full use of this tutorial. Although the +`Gentle Introduction' is meant to stand by itself, it is often easier +to learn a language through actual use and experimentation than by +reading alone. Once you finish this introduction, we recommend that +you proceed section by section through the `Gentle Introduction' and +after having read each section go back to this online tutorial. You +should wait until you have finished the tutorial before attempting to +read the report. We assume that you are familiar with the basics of +Emacs and that Haskell has been installed at your site. + +This tutorial does not assume any familiarity with Haskell or other +functional languages. However, knowledge of almost-functional +languages such as ML or Scheme is very useful. Throughout the +online component of this tutorial, we try to relate Haskell to +other programming languages and clarify the written tutorial through +additional examples and text. + + +Organization of the Online Tutorial + +This online tutorial is divided into a series of pages. Each page +covers one or more sections in the written tutorial. You can use +special Emacs commands to move back and forth through the pages of the +online tutorial. Each page is a single Haskell program. Comments in +the program contain the text of the online tutorial. You can modify +the program freely (this will not change the underlying tutorial +file!) and ask the system to print the value of expressions defined in +the program. + +At the beginning of each page, the sections covered by the page are +listed. In addition, the start of each individual section is +marked within each page. Emacs commands can take you directly to a +specific page or section in the tutorial. + +To create useful, executable examples of Haskell code, some language +constructs need to be revealed well before they are explained in the +tutorial. We attempt to point these out when they occur. Some +small changes have been made to the examples in the written tutorial; +these are usually cosmetic and should be ignored. Don't feel you have +to understand everything on a page before you move on -- many times +concepts become clearer as you move on and can relate them to other +aspect of the language. + +Each page of the tutorial defines a set of variables. Some of +these are named e1, e2, and so on. These `e' variables are the ones +which are meant for you to evaluate as you go through the tutorial. +Of course you may evaluate any other expressions or variables you wish. + + +The Haskell Report + +While the report is not itself a tutorial on the Haskell language, it +can be an invaluable reference to even a novice user. A very +important feature of Haskell is the prelude. The prelude is a +rather large chunk of Haskell code which is implicitly a part of every +Haskell program. Whenever you see functions used which are not +defined in the current page, these come from the Prelude. Appendix A +of the report lists the entire Prelude; the index has an entry for +every function in the Prelude. Looking at the definitions in the +Prelude is sometimes necessary to fully understand the programs in +this tutorial. + +Another reason to look at the report is to understand the syntax of +Haskell. Appendix B contains the complete syntax for Haskell. The +tutorial treats the syntax very informally; the precise details are +found only in the report. + + +The Yale Haskell System + +This version of the tutorial runs under version Y2.0 of Yale Haskell. +The Yale Haskell system is an interactive programming environment for +the Haskell language. The system is best used in conjunction with the +Emacs editor. Yale Haskell is available free of change via ftp. + + +Using the Compiler + +Yale Haskell runs as a subprocess under Emacs. While many commands +are available to the Yale Haskell user, a single command is the +primary means of communicating with the compiler: C-c e. This command +evaluates and prints an expression in the context of the program on +the screen. Here is what this command does: + +a) You are prompted for an expression in the minibuffer. You can +use M-p or M-n to move through a ring of previous inputs. + +b) If an inferior Haskell process is not running, a buffer named *haskell* +is created and the Haskell compiler is started up. The *haskell* buffer +pops onto your screen. + +c) If the program in the current page of the tutorial has not yet been +compiled or the page has been modified after its most recent +compilation, the entire page is compiled. This may result in a short delay. + +d) If there are no errors in the program, the expression entered in +step a) is compiled in the context of the program. Any value defined +in the current page can be referenced. + +e) If there are no errors in the expression, its value is printed in +the *haskell* buffer. + +There are also a few other commands you can use. C-c i interrupts +the Haskell program. Some tight loops cannot be interrupted; in this +case you will have to kill the Haskell process. C-c q exits the Haskell +process. + + +Emacs Commands Used by the Tutorial + +These commands are specific to the tutorial. The tutorial is entered +using M-x haskell-tutorial and is exited with C-c q. To move among +the pages of the tutorial, use + +C-c C-f -- go forward 1 page +C-c C-b -- go back 1 page +M-x ht-goto-page - goto a specific page of the tutorial +M-x ht-goto-section - goto a specific section of the tutorial + +Each page of the tutorial can be modified without changing the +underlying text of the tutorial. Changes are not saved as you go +between pages. To revert a page to its original form use C-c C-l. + +You can get help regarding the Emacs commands with C-c ?. + +Summary of Emacs commands used by the tutorial: + M-x haskell-tutorial - start the tutorial + C-c C-f - Go to the next page of the tutorial program + C-c C-b - Go back to the previous page of the tutorial program + C-c C-l - Restore the current page to its original form + C-c e - Evaluate a Haskell expression + C-c i - Interrupt a running Haskell program + C-c ? - Shows a help message + M-x ht-goto-page - goto a specific page of the tutorial + M-x ht-goto-section - goto a specific section of the tutorial + + +You are now ready to start the tutorial. Start by reading the `Gentle +Introduction' section 1 then proceed through the online tutorial using +C-c C-f to advance to the next page. You should read about each topic +first before turning to the associated programming example in the +online tutorial. + + +-- Page 1 Section 2 + +-- Section 2 Values, Types, and Other Goodies + +-- Haskell uses `--' to designate end of line comments. We use these +-- throughout the tutorial to place explanatory text in the program. + +-- Remember to use C-c e to evaluate expressions, C-c ? for help. + +-- All Haskell programs must start with a module declaration. Ignore this +-- for now. + +module Test(Bool) where + +-- We will start by defining some identifiers (variables) using equations. +-- You can print out the value of an identifier by typing C-c e and +-- typing the name of the identifier you wish to evaluate. This will +-- compile the entire program, not just the line with the definition +-- you want to see. Not all definitions are very interesting to print out - +-- by convention, we will use variables e1, e2, ... to denote values that +-- are `interesting' to print. + +-- We'll start with some constants as well as their associated type. +-- There are two ways to associate a type with a value: a type declaration +-- and an expression type signature. Here is an equation and a type +-- declaration: + +e1 :: Int -- This is a type declaration for the identifier e1 +e1 = 5 -- This is an equation defining e1 + +-- You can evaluate the expression e1 and watch the system print `5'! Wow! + +-- Remember that C-c e is prompting for an expression. Expressions like +-- e1 or 5 or 1+1 are all valid. However, `e1 = 5' is a definition, +-- not an expression. Trying to evaluate it will result in a syntax error. + +-- The type declaration for e1 is not really necessary but we will try to +-- always provide type declarations for values to help document the program +-- and to ensure that the system infers the same type we do for an expression. +-- If you change the value for e1 to `True', the program will no longer +-- compile due to the type mismatch. + +-- We will briefly mention expression type signatures: these are attached to +-- expressions instead of identifiers. Here are equivalent ways to do +-- the previous definition: + +e2 = 5 :: Int +e3 = (2 :: Int) + (3 :: Int) + +-- The :: has very low precedence in expressions and should usually be placed +-- in parenthesis. + +-- Note that there are two completely separate languages: an expression +-- language for values and a type language for type signatures. The type +-- language is used only in the type declarations previously described and +-- declarations of new types, described later. Haskell uses a +-- uniform syntax so that values resemble their type signature as much as +-- possible. However, you must always be aware of the difference between +-- type expressions and value expressions. + +-- Here are some of the predefined types Haskell provides: +-- type Value Syntax Type Syntax +-- Small integers Int +e4 :: Int +e4 = 12345 +-- Characters '' Char +e5 :: Char +e5 = 'a' +-- Boolean True, False Bool +e6 :: Bool +e6 = True +-- Floating point Float +e7 :: Float +e7 = 123.456 +-- We will introduce these types now; there will be much more to say later. +-- Homogenous List [,,...] [] +e8 :: [Int] +e8 = [1,2,3] +-- Tuple (,,...) (,,...) +e9 :: (Char,Int) +e9 = ('b',4) +-- Functional described later domain type -> range type +succ :: Int -> Int -- a function which takes an Int argument and returns Int +succ x = x + 1 -- test this by evaluating `succ 4' + +-- Here's a few leftover examples from section 2: + +e10 = succ (succ 3) -- you could also evaluate `succ (succ 3)' directly + -- by entering the entire expression to the C-c e + +-- If you want to evaluate something more complex than the `e' variables +-- defined here, it is better to enter a complex expression, such as +-- succ (succ 3), directly than to edit a new definition like e10 into +-- the program. This is because any change to the program will require +-- recompilation of the entire page. The expressions entered to C-c e are +-- compiled separately (and very quickly!). + +-- Uncomment this next line to see a compile time type error. +-- e11 = 'a'+'b' +-- Don't worry about the error message - it will make more sense later. + +-- Proceed to the next page using C-c C-f + +-- Page 2 Section 2.1 + +-- Section 2.1 Polymorphic Types + +module Test(Bool) where + +-- The following line allows us to redefine functions in the standard +-- prelude. Ignore this for now. + +import Prelude hiding (length,head,tail,null) + +-- start with some sample lists to use in test cases + +list1 :: [Int] +list1 = [1,2,3] +list2 :: [Char] -- This is the really a string +list2 = ['a','b','c'] -- This is the same as "abc"; evaluate list2 and see. +list3 :: [[a]] -- The element type of the inner list is unknown +list3 = [[],[],[],[]] -- so this list can't be printed +list4 :: [Int] +list4 = 1:2:3:4:[] -- Exactly the same as [1,2,3,4]; print it and see. + +-- This is the length function. You can test it by evaluating expressions +-- such as `length list1'. Function application is written by +-- simple juxtaposition: `f(x)' in other languages would be `f x' in Haskell. + +length :: [a] -> Int +length [] = 0 +length (x:xs) = 1 + length xs + +-- Function application has the highest precedence, so 1 + length xs is +-- parsed as 1 + (length xs). In general, you have to surround +-- non-atomic arguments to a function with parens. This includes +-- arguments which are also function applications. For example, +-- f g x is the function f applied to arguments g and x, similar to +-- f(g,x) in other languages. However, f (g x) is f applied to (g x), or +-- f(g(x)), which means something quite different! Be especially +-- careful with infix operators: f x+1 y-2 would be parsed as (f x)+(1 y)-2. +-- This is also true on the left of the `=': the parens around (x:xs) are +-- absolutely necessary. length x:xs would be parsed as (length x):xs. + +-- Also be careful with prefix negation, -. The application `f -1' is +-- f-1, not f(-1). Add parens around negative numbers to avoid this +-- problem. + +-- Here are some other list functions: + +head :: [a] -> a -- returns the first element in a list (same as car in lisp) +head (x:xs) = x + +tail :: [a] -> [a] -- removes the first element from a list (same as cdr) +tail (x:xs) = xs + +null :: [a] -> Bool +null [] = True +null (x:xs) = False + +cons :: a -> [a] -> [a] +cons x xs = x:xs + +nil :: [a] +nil = [] + +-- Length could be defined using these functions too. This is +-- not good Haskell style but does illustrate these other list functions. +-- The if - then - else will be discussed later. Haskell programmers feel +-- that the pattern matching style, as used in the previous version of +-- length, is more natural and readable. + +length' :: [a] -> Int -- Note that ' can be part of a name +length' x = if null x then 0 else 1 + length' (tail x) + +-- A test case for length', cons, and nil + +e1 = length' (cons 1 (cons 2 nil)) + +-- We haven't said anything about errors yet. Each of the following +-- examples illustrates a potential runtime or compile time error. The +-- compile time error is commented out so that other examples will compile; +-- you can uncomment them and see what happens. + +-- e2 = cons True False -- Why is this not possible in Haskell? +e3 = tail (tail ['a']) -- What happens if you evaluate this? +e4 = [] -- This is especially mysterious! + +-- This last example, e4, is something hard to explain but is often +-- encountered early by novices. We haven't explained yet how the system +-- prints out the expressions you type in - this will wait until later. +-- However, the problem here is that e4 has the type [a]. The printer for +-- the list datatype is complaining that it needs to know a specific type +-- for the list elements even though the list has no elements! This can +-- be avoided by giving e4 a type such as [Int]. (To further confuse you, +-- try giving e4 the type [Char] and see what happens.) + +-- Page 3 Section 2.2 + +-- Section 2.2 User-Defined Types + +module Test(Bool) where + +-- The type Bool is already defined in the Prelude so there is no +-- need to define it here. + +data Color = Red | Green | Blue | Indigo | Violet deriving Text +-- The `deriving Text' is necessary if you want to print a Color value. + +-- You can now evaluate these expressions. +e1 :: Color +e1 = Red +e2 :: [Color] +e2 = [Red,Blue] + +-- It is very important to keep the expression language and the type +-- language in Haskell separated. The data declaration above defines +-- the type constructor Color. This is a nullary constructor: it takes no +-- arguments. Color is found ONLY in the type language - it can not be +-- part of an expression. e1 = Color is meaningless. (Actually, Color could +-- be both a data constructor and a type constructor but we'll ignore this +-- possibility for now). On the other hand, Red, Blue, and so on are +-- (nullary) data constructors. They can appear in expressions and +-- in patterns (described later). The declaration e1 :: Blue would also +-- be meaningless. Data constructors can be defined ONLY in a data +-- declaration. + +-- In the next example, Point is a type constructor and Pt is a data +-- constructor. Point takes one argument and Pt takes two. A data constructor +-- like Pt is really just an ordinary function except that it can be used in +-- a pattern. Type signatures can not be supplied directly for data +-- constructors; their typing is completely defined by the data declaration. +-- However, data constructors have a signature just like any variable: +-- Pt :: a -> a -> Point a -- Not valid Haskell syntax +-- That is, Pt is a function which takes two arguments with the same +-- arbitrary type and returns a value containing the two argument values. + +data Point a = Pt a a deriving Text + +e3 :: Point Float +e3 = Pt 2.0 3.0 +e4 :: Point Char +e4 = Pt 'a' 'b' +e5 :: Point (Point Int) +e5 = Pt (Pt 1 2) (Pt 3 4) +-- e6 = Pt 'a' True -- This is a typing error + +-- The individual components of a point do not have names. +-- Let's jump ahead a little so that we can write functions using these +-- data types. Data constructors (Red, Blue, ..., and Pt) can be used in +-- patterns. When more than one equation is used to define a function, +-- pattern matching occurs top down. + +-- A function to remove red from a list of colors. + +removeRed :: [Color] -> [Color] +removeRed [] = [] +removeRed (Red:cs) = removeRed cs +removeRed (c:cs) = c : removeRed cs -- c cannot be Red at this point + +e7 :: [Color] +e7 = removeRed [Blue,Red,Green,Red] + +-- Pattern matching is capable of testing equality with a specific color. + +-- All equations defining a function must share a common type. A +-- definition such as: +-- foo Red = 1 +-- foo (Pt x y) = x +-- would result in a type error since the argument to foo cannot be both a +-- Color and a Point. Similarly, the right hand sides must also share a +-- common type; a definition such as +-- foo Red = Blue +-- foo Blue = Pt Red Red +-- would also result in a type error. + +-- Here's a couple of functions defined on points. + +dist :: Point Float -> Point Float -> Float +dist (Pt x1 y1) (Pt x2 y2) = sqrt ((x1-x2)^2 + (y1-y2)^2) + +midpoint :: Point Float -> Point Float -> Point Float +midpoint (Pt x1 y1) (Pt x2 y2) = Pt ((x1+x2)/2) ((y1+y2)/2) + +p1 :: Point Float +p1 = Pt 1.0 1.0 +p2 :: Point Float +p2 = Pt 2.0 2.0 + +e8 :: Float +e8 = dist p1 p2 +e9 :: Point Float +e9 = midpoint p1 p2 + +-- The only way to take apart a point is to pattern match. +-- That is, the two values which constitute a point must be extracted +-- by matching a pattern containing the Pt data constructor. Much +-- more will be said about pattern matching later. + +-- Haskell prints values in the same syntax used in expressions. Thus +-- Pt 1 2 would print as Pt 1 2 (of course, Pt 1 (1+1) would also print +-- as Pt 1 2). + +-- Page 4 Section 2.3 + +-- Section 2.3 Recursive Types + +module Test where + +data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Text + +-- The following typings are implied by this declaration. As before, +-- this is not valid Haskell syntax. +-- Leaf :: a -> Tree a +-- Branch :: Tree a -> Tree a -> Tree a + +fringe :: Tree a -> [a] +fringe (Leaf x) = [x] +fringe (Branch left right) = fringe left ++ fringe right + +-- The following trees can be used to test functions: + +tree1 :: Tree Int +tree1 = Branch (Leaf 1) (Branch (Branch (Leaf 2) (Leaf 3)) (Leaf 4)) +tree2 :: Tree Int +tree2 = Branch (Branch (Leaf 3) (Leaf 1)) (Branch (Leaf 4) (Leaf 1)) +tree3 :: Tree Int +tree3 = Branch tree1 tree2 + +-- Try evaluating `fringe tree1' and others. + +-- Here's another tree function: + +twist :: Tree a -> Tree a +twist (Branch left right) = Branch right left +twist x = x -- This equation only applies to leaves + +-- Here's a function which compares two trees to see if they have the +-- same shape. Note the signature: the two trees need not contain the +-- same type of values. + +sameShape :: Tree a -> Tree b -> Bool +sameShape (Leaf x) (Leaf y) = True +sameShape (Branch l1 r1) (Branch l2 r2) = sameShape l1 l2 && sameShape r1 r2 +sameShape x y = False -- One is a branch, the other is a leaf + +-- The && function is a boolean AND function. + +-- The entire pattern on the left hand side must match in order for the +-- right hand side to be evaluated. The first clause requires both +-- arguments to be a leaf' otherwise the next equation is tested. +-- The last clause will always match: the final x and y match both +-- leaves and branches. + +-- This compares a tree of integers to a tree of booleans. +e1 = sameShape tree1 (Branch (Leaf True) (Leaf False)) + +-- Page 5 Sections 2.4, 2.5, 2.6 + +-- Section 2.4 Type Synonyms + +module Test(Bool) where + +-- Since type synonyms are part of the type language only, it's hard to +-- write a program which shows what they do. Essentially, they are like +-- macros for the type language. They can be used interchangably with their +-- definition: + +e1 :: String +e1 = "abc" +e2 :: [Char] -- No different than String +e2 = e1 + +-- In the written tutorial the declaration of `Addr' is a data type +-- declaration, not a synonym declaration. This shows that the data +-- type declaration as well as a signature can reference a synonym. + +-- Section 2.5 Built-in Types + +-- Tuples are an easy way of grouping a set of data values. Here are +-- a few tuples. Note the consistancy in notation between the values and +-- types. + +e3 :: (Bool,Int) +e3 = (True,4) +e4 :: (Char,[Int],Char) +e4 = ('a',[1,2,3],'b') + +-- Here's a function which returns the second component of a 3 tuple. +second :: (a,b,c) -> b +second (a,b,c) = b + +-- Try out `second e3' and `second e4' - what happens? +-- Each different size of tuple is a completely distinct type. There is +-- no general way to append two arbitrary tuples or randomly select the +-- i'th component of an arbitrary tuple. Here's a function built using +-- 2-tuples to represent intervals. + +-- Use a type synonym to represent homogenous 2 tuples +type Interval a = (a,a) + +containsInterval :: Interval Int -> Interval Int -> Bool +containsInterval (xmin,xmax) (ymin,ymax) = xmin <= ymin && xmax >= ymax + +p1 :: Interval Int +p1 = (2,3) +p2 :: Interval Int +p2 = (1,4) + +e5 = containsInterval p1 p2 +e6 = containsInterval p2 p1 + +-- Here's a type declaration for a type isomorphic to lists: + +data List a = Nil | Cons a (List a) deriving Text + +-- Except for the notation, this is completely equivalent to ordinary lists +-- in Haskell. + +length' :: List a -> Int +length' Nil = 0 +length' (Cons x y) = 1 + length' y + +e7 = length' (Cons 'a' (Cons 'b' (Cons 'c' Nil))) + +-- It's hard to demonstrate much about the `non-specialness' of built-in +-- types. However, here is a brief summary: + +-- Numbers and characters, such as 1, 2.2, or 'a', are the same as nullary +-- type constructors. + +-- Lists have a special type constructor, [a] instead of List a, and +-- an odd looking data constructor, []. The other data constructor, :, is +-- not `unusual', syntactically speaking. The notation [x,y] is just +-- syntax for x:y:[] and "abc" for 'a' : 'b' : 'c' : []. + +-- Tuples use a special syntax. In a type expression, a 2 tuple containing +-- types a and be would be written (a,b) instead of using a prefix type +-- constructor such as Tuple2 a b. This same notation is used to build +-- tuple values: (1,2) would construct a 2 tuple containing the values 1 and 2. + + +-- Page 6 Sections 2.5.1, 2.5.2 + +module Test(Bool) where + +-- Section 2.5.1 List Comprehensions and Arithmetic Sequences + +-- Warning: brackets in Haskell are used in three different types +-- of expressions: lists, as in [a,b,c], sequences (distinguished by +-- the ..), as in [1..2], and list comprehensions (distinguished by the +-- bar: |), as in [x+1 | x <- xs, x > 1]. + +-- Before list comprehensions, let's start out with sequences: + +e1 :: [Int] +e1 = [1..10] -- Step is 1 +e2 :: [Int] +e2 = [1,3..10] -- Step is 3 - 1 +e3 :: [Int] +e3 = [1,-1..-10] +e4 :: [Char] +e4 = ['a'..'z'] -- This works on chars too + +-- We'll avoid infinite sequences like [1..] for now. If you print one, +-- use C-c i to interrupt the Haskell program. + +-- List comprehensions are very similar to nested loops. They return a +-- list of values generated by the expression inside the loop. The filter +-- expressions are similar to conditionals in the loop. + +-- This function does nothing at all! It just scans through a list and +-- copies it into a new one. + +doNothing :: [a] -> [a] +doNothing l = [x | x <- l] + +-- Adding a filter to the previous function allows only selected elements to +-- be generated. This is similar to what is done in quicksort. + +positives :: [Int] -> [Int] +positives l = [x | x <- l, x > 0] + +e5 = positives [2,-4,5,6,-5,3] + +-- Now the full quicksort function. + +quicksort :: [Char] -> [Char] -- Use Char just to be different! +quicksort [] = [] +quicksort (x:xs) = quicksort [y | y <- xs, y <= x] ++ + [x] ++ + quicksort [y | y <- xs, y > x] + +e6 = quicksort "Why use Haskell?" + +-- Now for some nested loops. Each generator, <-, adds another level of +-- nesting to the loop. Note that the variable introduced by each generator +-- can be used in each following generator; all variables can be used in the +-- generated expression: + +e7 :: [(Int,Int)] +e7 = [(x,y) | x <- [1..5], y <- [x..5]] + +-- Now let's add some guards: (the /= function is `not equal') + +e8 :: [(Int,Int)] +e8 = [(x,y) | x <- [1..7], x /= 5, y <- [x..8] , x*y /= 12] + +-- This is the same as the loop: (going to a psuedo Algol notation) +-- for x := 1 to 7 do +-- if x <> 5 then +-- for y := x to 8 do +-- if x*y <> 12 +-- generate (x,y) + +-- Section 2.5.2 Strings + +e9 = "hello" ++ " world" + +-- Page 7 Sections 3, 3.1 + +module Test(Bool) where +import Prelude hiding (map) + +-- Section 3 Functions + +add :: Int -> Int -> Int +add x y = x+y + +e1 :: Int +e1 = add 1 2 + +-- This Int -> Int is the latter part of the signature of add: +-- add :: Int -> (Int -> Int) + +succ :: Int -> Int +succ = add 1 + +e2 :: Int +e2 = succ 3 + +map :: (a->b) -> [a] -> [b] +map f [] = [] +map f (x:xs) = f x : (map f xs) + +e3 :: [Int] +e3 = map (add 1) [1,2,3] +-- This next definition is the equivalent to e3 +e4 :: [Int] +e4 = map succ [1,2,3] + +-- Heres a more complex example. Define flist to be a list of functions: +flist :: [Int -> Int] +flist = map add [1,2,3] +-- This returns a list of functions which add 1, 2, or 3 to their input. +-- Warning: Haskell should print flist as something like +-- [<>,<>,<>] + + +-- Now, define a function which takes a function and returns its value +-- when applied to the constant 1: +applyTo1 :: (Int -> a) -> a +applyTo1 f = f 1 + +e5 :: [Int] +e5 = map applyTo1 flist -- Apply each function in flist to 1 + +-- If you want to look at how the type inference works, figure out how +-- the signatures of map, applyTo1, and flist combine to yield [Int]. + +-- Section 3.1 Lambda Abstractions + +-- The symbol \ is like `lambda' in lisp or scheme. + +-- Anonymous functions are written as \ arg1 arg2 ... argn -> body +-- Instead of naming every function, you can code it inline with this +-- notation: + +e6 = map (\f -> f 1) flist + +-- Be careful with the syntax here. \x->\y->x+y parses as +-- \ x ->\ y -> x + y. The ->\ is all one token. Use spaces!! + +-- This is identical to e5 except that the applyTo1 function has no name. + +-- Function arguments on the left of an = are the same as lambda on the +-- right: + +add' = \x y -> x+y -- identical to add +succ' = \x -> x+1 -- identical to succ + +-- As with ordinary function, the parameters to anonymous functions +-- can be patterns: + +e7 :: [Int] +e7 = map (\(x,y) -> x+y) [(1,2),(3,4),(5,6)] + +-- Functions defined by more than one equation, like map, cannot +-- be converted to anonymous lambda functions quite as easily - a case +-- statement is also required. This is discussed later. + +-- Page 8 Sections 3.2, 3.2.1, 3.2.2 + +module Test(Bool) where + +import Prelude hiding ((++),(.)) + +-- Section 3.2 Infix operators + +-- Haskell has both identifiers, like x, and operators, like +. +-- These are just two different types of syntax for variables. +-- However, operators are by default used in infix notation. + +-- Briefly, identifiers begin with a letter and may have numbers, _, and ' +-- in them: x, xyz123, x'', xYz'_12a. The case of the first letter +-- distinguishes variables from data constructors (or type variables from +-- type constructors). An operator is a string of symbols, where +-- :!#$%&*+./<=>?@\^| are all symbols. If the first character is : then +-- the operator is a data constructor; otherwise it is an ordinary +-- variable operator. The - and ~ characters may start a symbol but cannot +-- be used after the first character. This allows a*-b to parse as +-- a * - b instead of a *- b. + +-- Operators can be converted to identifiers by enclosing them in parens. +-- This is required in signature declarations. Operators can be defined +-- as well as used in the infix style: + +(++) :: [a] -> [a] -> [a] +[] ++ y = y +(x:xs) ++ y = x : (xs ++ y) + +-- Table 2 (Page 54) of the report is invaluable for sorting out the +-- precedences of the many predefined infix operators. + +e1 = "Foo" ++ "Bar" + +-- This is the same function without operator syntax +appendList :: [a] -> [a] -> [a] +appendList [] y = y +appendList (x:xs) y = x : appendList xs y + +(.) :: (b -> c) -> (a -> b) -> (a -> c) +f . g = \x -> f (g x) + +add1 :: Int -> Int +add1 x = x+1 + +e2 = (add1 . add1) 3 + +-- Section 3.2.1 Sections + +-- Sections are a way of creating unary functions from infix binary +-- functions. When a parenthesized expression starts or ends in an +-- operator, it is a section. Another definition of add1: + +add1' :: Int -> Int +add1' = (+ 1) + +e3 = add1' 4 + +-- Here are a few section examples: + +e4 = map (++ "abc") ["x","y","z"] + +e5 = map ("abc" ++) ["x","y","z"] + + +-- Section 3.2.2 Fixity Declarations + +-- We'll avoid any demonstration of fixity declarations. The Prelude +-- contains numerous examples. + +-- Page 9 Sections 3.3, 3.4, 3.5 +module Test(Bool) where + +import Prelude hiding (take,zip) + +-- Section 3.3 Functions are Non-strict + +-- Observing lazy evaluation can present difficulties. The essential +-- question is `does an expression get evaluated?'. While in theory using a +-- non-terminating computation is the way evaluation issues are examined, +-- we need a more practical approach. The `error' function serves as +-- a bottom value. Evaluating this function prints an error message and +-- halts execution. + +bot = error "Evaluating Bottom" + +e1 :: Bool -- This can be any type at all! +e1 = bot -- evaluate this and see what happens. + +const1 :: a -> Int +const1 x = 1 + +e2 :: Int +e2 = const1 bot -- The bottom is not needed and will thus not be evaluated. + +-- Section 3.4 "Infinite" Data Structures + +-- Data structures are constructed lazily. A constructor like : will not +-- evaluate its arguments until they are demanded. All demands arise from +-- the need to print the result of the computation -- components not needed +-- to compute the printed result will not be evaluated. + +list1 :: [Int] +list1 = (1:bot) + +e3 = head list1 -- doesnt evaluate bot +e4 = tail list1 -- does evaluate bot + +-- Some infinite data structures. Don't print these! If you do, you will +-- need to interrupt the system (C-c i) or kill the Haskell process. + +ones :: [Int] +ones = 1 : ones + +numsFrom :: Int -> [Int] +numsFrom n = n : numsFrom (n+1) + +-- An alternate numsFrom using series notation: + +numsFrom' :: Int -> [Int] +numsFrom' n = [n..] + +squares :: [Int] +squares = map (^2) (numsFrom 0) + +-- Before we start printing anything, we need a function to truncate these +-- infinite lists down to a more manageable size. The `take' function +-- extracts the first k elements of a list: + +take :: Int -> [a] -> [a] +take 0 x = [] -- two base cases: k = 0 +take k [] = [] -- or the list is empty +take k (x:xs) = x : take (k-1) xs + +-- now some printable lists: + +e5 :: [Int] +e5 = take 5 ones + +e6 :: [Int] +e6 = take 5 (numsFrom 10) + +e7 :: [Int] +e7 = take 5 (numsFrom' 0) + +e8 :: [Int] +e8 = take 5 squares + +-- zip is a function which turns two lists into a list of 2 tuples. If +-- the lists are of differing sizes, the result is as long as the +-- shortest list. + +zip (x:xs) (y:ys) = (x,y) : zip xs ys +zip xs ys = [] -- one of the lists is [] + +e9 :: [(Int,Int)] +e9 = zip [1,2,3] [4,5,6] + +e10 :: [(Int,Int)] +e10 = zip [1,2,3] ones + +fib :: [Int] +fib = 1 : 1 : [x+y | (x,y) <- zip fib (tail fib)] + +e11 = take 5 fib + +-- Let's do this without the list comprehension: + +fib' :: [Int] +fib' = 1 : 1 : map (\(x,y) -> x+y) (zip fib (tail fib)) + +-- This could be written even more cleanly using a map function which +-- maps a binary function over two lists at once. This is in the +-- Prelude and is called zipWith. + +fib'' :: [Int] +fib'' = 1 : 1 : zipWith (+) fib (tail fib) + +-- For more examples using infinite structures look in the demo files +-- that come with Yale Haskell. Both the pascal program and the +-- primes program use infinite lists. + +-- Section 3.5 The Error Function + +-- Too late - we already used it! + + +-- Page 10 Sections 4, 4.1, 4.2 + +module Test(Bool) where + +import Prelude hiding (take,(^)) + +-- Section 4 Case Expressions and Pattern Matching + +-- Now for details of pattern matching. We use [Int] instead of [a] +-- since the only value of type [a] is []. + +contrived :: ([Int], Char, (Int, Float), String, Bool) -> Bool +contrived ([], 'b', (1, 2.0), "hi", True) = False +contrived x = True -- add a second equation to avoid runtime errors + +e1 :: Bool +e1 = contrived ([], 'b', (1, 2.0), "hi", True) +e2 :: Bool +e2 = contrived ([1], 'b', (1, 2.0), "hi", True) + +-- Contrived just tests its input against a big constant. + +-- Linearity in pattern matching implies that patterns can only compare +-- values with constants. The following is not valid Haskell: + +-- member x [] = False +-- member x (x:ys) = True -- Invalid since x appears twice +-- member x (y:ys) = member x ys + +f :: [a] -> [a] +f s@(x:xs) = x:s +f _ = [] + +e3 = f "abc" + +-- Another use of _: + +middle :: (a,b,c) -> b +middle (_,x,_) = x + +e4 :: Char +e4 = middle (True, 'a', "123") + +(^) :: Int -> Int -> Int +x ^ 0 = 1 +x ^ (n+1) = x*(x^n) + +e5 :: Int +e5 = 3^3 +e6 :: Int +e6 = 4^(-2) -- Notice the behavior of the + pattern on this one + +-- Section 4.1 Pattern Matching Semantics + +-- Here's an extended example to illustrate the left -> right, top -> bottom +-- semantics of pattern matching. + +foo :: (Int,[Int],Int) -> Int +foo (1,[2],3) = 1 +foo (2,(3:_),3) = 2 +foo (1,_,3) = 3 +foo _ = 4 + +bot = error "Bottom Evaluated" + +e7 = foo (1,[],3) +e8 = foo (1,bot,3) +e9 = foo (1,1:bot,3) +e10 = foo (2,bot,2) +e11 = foo (3,bot,bot) + +-- Now add some guards: + +sign :: Int -> Int +sign x | x > 0 = 1 + | x == 0 = 0 + | x < 0 = -1 + +e12 = sign 3 + +-- The last guard is often `True' to catch all other cases. The identifier +-- `otherwise' is defined as True for use in guards: + +max' :: Int -> Int -> Int +max' x y | x > y = x + | otherwise = y + +-- Guards can refer to any variables bound by pattern matching. When +-- no guard is true, pattern matching resumes at the next equation. Guards +-- may also refer to values bound in an associated where declaration. + + +inOrder :: [Int] -> Bool +inOrder (x1:x2:xs) | x1 <= x2 = True +inOrder _ = False + +e13 = inOrder [1,2,3] +e14 = inOrder [2,1] + +-- Section 4.2 An Example + +take :: Int -> [a] -> [a] +take 0 _ = [] +take _ [] = [] +take (n+1) (x:xs) = x:take n xs + +take' :: Int -> [a] -> [a] +take' _ [] = [] +take' 0 _ = [] +take' (n+1) (x:xs) = x:take' n xs + +e15, e16, e17, e18 :: [Int] +e15 = take 0 bot +e16 = take' 0 bot +e17 = take bot [] +e18 = take' bot [] + +-- Page 11 Sections 4.3, 4.4, 4.5, 4.6 + +module Test(Bool) where + +-- import Prelude hiding (take,Request(..),Response(..)) -- Standard Haskell +import Prelude hiding (take) -- Y2.0-b4 only + +-- Section 4.3 Case Expressions + +-- The function take using a case statement instead of multiple equations + +take :: Int -> [a] -> [a] +take m ys = case (m,ys) of + (0 ,_) -> [] + (_ ,[]) -> [] + (n+1,x:xs) -> x : take n xs + +-- The function take using if then else. We can also eliminate the n+k +-- pattern just for fun. The original version of take is much easier to read! + +take' :: Int -> [a] -> [a] +take' m ys = if m == 0 then [] else + if null ys then [] else + if m > 0 then head ys : take (m-1) (tail ys) + else error "m < 0" + +-- Section 4.4 Lazy Patterns + +-- Before the client-server example, here is a contrived example of lazy +-- patterns. The first version will fail to pattern match whenever the +-- the first argument is []. The second version will always pattern +-- match initially but x will fail if used when the list is []. + +nonlazy :: [Int] -> Bool -> [Int] +nonlazy (x:xs) isNull = if isNull then [] else [x] + +e1 = nonlazy [1,2] False +e2 = nonlazy [] True +e3 = nonlazy [] False + +-- This version will never fail the initial pattern match +lazy :: [Int] -> Bool -> [Int] +lazy ~(x:xs) isNull = if isNull then [] else [x] + +e4 = lazy [1,2] False +e5 = lazy [] True +e6 = lazy [] False + +-- The server - client example is a little hard to demonstrate. We'll avoid +-- the initial version which loops. Here is the version with irrefutable +-- patterns. + +type Response = Int +type Request = Int + +client :: Request -> [Response] -> [Request] +client init ~(resp:resps) = init : client (next resp) resps + +server :: [Request] -> [Response] +server (req : reqs) = process req : server reqs + +-- Next maps the response from the previous request onto the next request +next :: Response -> Request +next resp = resp + +-- Process maps a request to a response +process :: Request -> Response +process req = req+1 + +requests :: [Request] +requests = client 0 responses + +responses :: [Response] +responses = server requests + +e7 = take 5 responses + +-- The lists of requests and responses are infinite - there is no need to +-- check for [] in this program. These lists correspond to streams in other +-- languages. + +-- Here is fib again: + +fib :: [Int] +fib@(1:tfib) = 1 : 1 : [ a+b | (a,b) <- zip fib tfib] + +e8 = take 10 fib + +-- Section 4.5 Lexical Scoping and Nested Forms + +-- One thing that is important to note is that the order of the +-- definitions in a program, let expression, or where clauses is +-- completely arbitrary. Definitions can be arranged 'top down' +-- or `bottom up' without changing the program. + +e9 = let y = 2 :: Float + f x = (x+y)/y + in f 1 + f 2 + +f :: Int -> Int -> String +f x y | y > z = "y > x^2" + | y == z = "y = x^2" + | y < z = "y < x^2" + where + z = x*x + +e10 = f 2 5 +e11 = f 2 4 + +-- Section 4.6 Layout + +-- There's nothing much to demonstrate here. We have been using layout all +-- through the tutorial. The main thing is to be careful line up the +-- first character of each definition. For example, if you +-- change the indentation of the definition of f in e9 you will get a +-- parse error. + +-- Page 12 Section 5 +module Test(Bool) where + +import Prelude hiding (elem) + +-- Section 5 Type Classes + +-- Names in the basic class structure of Haskell cannot be hidden (they are +-- in PreludeCore) so we have to modify the names used in the tutorial. + +-- Here is a new Eq class: + +class Eq' a where + eq :: a -> a -> Bool + +-- Now we can define elem using eq from above: + +elem :: (Eq' a) => a -> [a] -> Bool +x `elem` [] = False +x `elem` (y:ys) = x `eq` y || x `elem` ys + +-- Before this is of any use, we need to admit some types to Eq' + +instance Eq' Int where + x `eq` y = abs (x-y) < 3 -- Let's make this `nearly equal' just for fun + +instance Eq' Float where + x `eq` y = abs (x-y) < 0.1 + +list1 :: [Int] +list1 = [1,5,9,23] + +list2 :: [Float] +list2 = [0.2,5.6,33,12.34] + +e1 = 2 `elem` list1 +e2 = 100 `elem` list1 +e3 = 0.22 `elem` list2 + +-- Watch out! Integers in Haskell are overloaded - without a type signature +-- to designate an integer as an Int, expressions like 3 `eq` 3 will be +-- ambiguous. See 5.5.4 about this problem. + +-- Now to add the tree type: + +data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Text + +instance (Eq' a) => Eq' (Tree a) where + (Leaf a) `eq` (Leaf b) = a `eq` b + (Branch l1 r1) `eq` (Branch l2 r2) = (l1 `eq` l2) && (r1 `eq` r2) + _ `eq` _ = False + +tree1,tree2 :: Tree Int +tree1 = Branch (Leaf 1) (Leaf 2) +tree2 = Branch (Leaf 2) (Leaf 1) + +e4 = tree1 `eq` tree2 + +-- Now make a new class with Eq' as a super class: + +class (Eq' a) => Ord' a where + lt,le :: a -> a -> Bool -- lt and le are operators in Ord' + x `le` y = x `eq` y || x `lt` y -- This is a default for le + +-- The typing of lt & le is +-- le,lt :: (Ord' a) => a -> a -> Bool +-- This is identical to +-- le,lt :: (Eq' a,Ord' a) => a -> a -> Bool + +-- Make Int an instance of Ord +instance Ord' Int where + x `lt` y = x < y+1 + +i :: Int -- Avoid ambiguity +i = 3 +e5 :: Bool +e5 = i `lt` i + +-- Some constraints on instance declarations: +-- A program can never have more than one instance declaration for +-- a given combination of data type and class. +-- If a type is declared to be a member of a class, it must also be +-- declared in all superclasses of that class. +-- An instance declaration does not need to supply a method for every +-- operator in the class. When a method is not supplied in an +-- instance declaration and no default is present in the class +-- declaration, a runtime error occurs if the method is invoked. +-- You must supply the correct context for an instance declaration -- +-- this context is not inferred automatically. + +-- Section 5.1 Equality and Ordered Classes +-- Section 5.2 Enumeration and Index Classes + +-- No examples are provided for 5.1 or 5.2. The standard Prelude contains +-- many instance declarations which illustrate the Eq, Ord, and Enum classes. + +-- Page 13 Section 5.3 + +module Test(Bool) where + +-- Section 5.3 Text and Binary Classes + +-- This is the slow showTree. The `show' function is part of the +-- Text class and works with all the built-in types. The context `Text a' +-- arises from the call to show for leaf values. + +data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Text + +showTree :: (Text a) => Tree a -> String +showTree (Leaf x) = show x +showTree (Branch l r) = "<" ++ showTree l ++ "|" ++ showTree r ++ ">" + +tree1 :: Tree Int +tree1 = Branch (Leaf 1) (Branch (Leaf 3) (Leaf 6)) + +e1 = showTree tree1 + +-- Now the improved showTree; shows is already defined for all +-- built in types. + +showsTree :: Text a => Tree a -> String -> String +showsTree (Leaf x) s = shows x s +showsTree (Branch l r) s = '<' : showsTree l ('|' : showsTree r ('>' : s)) + +e2 = showsTree tree1 "" + +-- The final polished version. ShowS is predefined in the Prelude so we +-- don't need it here. + + +showsTree' :: Text a => Tree a -> ShowS +showsTree' (Leaf x) = shows x +showsTree' (Branch l r) = ('<' :) . showsTree' l . ('|' :) . + showsTree' r . ('>' :) + +e3 = showsTree' tree1 "" + + +-- Page 14 This page break is just to keep recompilation from getting too +-- long. The compiler takes a little longer to compile this +-- page than other pages. + +module Test(Bool) where + +data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Text + +-- Now for the reading function. Again, ReadS is predefined and reads works +-- for all built-in types. The generators in the list comprehensions are +-- patterns: p <- l binds pattern p to successive elements of l which match +-- p. Elements not matching p are skipped. + +readsTree :: (Text a) => ReadS (Tree a) +readsTree ('<':s) = [(Branch l r, u) | (l, '|':t) <- readsTree s, + (r, '>':u) <- readsTree t ] +readsTree s = [(Leaf x,t) | (x,t) <- reads s] + +e4 :: [(Int,String)] +e4 = reads "5 golden rings" + +e5 :: [(Tree Int,String)] +e5 = readsTree "<1|<2|3>>" +e6 :: [(Tree Int,String)] +e6 = readsTree "<1|2" +e7 :: [(Tree Int,String)] +e7 = readsTree "<1|<<2|3>|<4|5>>> junk at end" + +-- Before we do the next readTree, let's play with the lex function. + +e8 :: [(String,String)] +e8 = lex "foo bar bletch" + +-- Here's a function to completely lex a string. This does not handle +-- lexical ambiguity - lex would return more than one possible lexeme +-- when an ambiguity is encountered and the patterns used here would not +-- match. + +lexAll :: String -> [String] +lexAll s = case lex s of + [("",_)] -> [] -- lex returns an empty token if none is found + [(token,rest)] -> token : lexAll rest + +e9 = lexAll "lexAll :: String -> [String]" +e10 = lexAll "<1|>" + +-- Finally, the `hard core' reader. This is not sensitive to +-- white space as were the previous versions. + + +readsTree' :: (Text a) => ReadS (Tree a) +readsTree' s = [(Branch l r, x) | ("<", t) <- lex s, + (l, u) <- readsTree' t, + ("|", v) <- lex u, + (r, w) <- readsTree' v, + (">", x) <- lex w ] + ++ + [(Leaf x, t) | (x, t) <- reads s] + +-- When testing this program, you must make sure the input conforms to +-- Haskell lexical syntax. If you remove spaces between | and < or +-- > and > they will lex as a single token. + +e11 :: [(Tree Int,String)] +e11 = readsTree' "<1 | <2 | 3> >" +e12 :: [(Tree Bool,String)] +e12 = readsTree' "" + +-- Finally, here is a simple version of read for trees only: + +read' :: (Text a) => String -> (Tree a) +read' s = case (readsTree' s) of + [(tree,"")] -> tree -- Only one parse, no junk at end + [] -> error "Couldn't parse tree" + [_] -> error "Crud after the tree" -- unread chars at end + _ -> error "Ambiguous parse of tree" + +e13 :: Tree Int +e13 = read' "foo" +e14 :: Tree Int +e14 = read' "< 1 | < 2 | 3 > >" +e15 :: Tree Int +e15 = read' "3 xxx" + +-- Page 15 Section 5.4 + +module Test(Bool) where + +-- Section 5.4 Derived Instances + +-- We have actually been using the derived Text instances all along for +-- printing out trees and other structures we have defined. The code +-- in the tutorial for the Eq and Ord instance of Tree is created +-- implicitly by the deriving clause so there is no need to write it +-- here. + +data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Eq,Ord,Text) + +-- Now we can fire up both Eq and Ord functions for trees: + +tree1, tree2, tree3, tree4 :: Tree Int +tree1 = Branch (Leaf 1) (Leaf 3) +tree2 = Branch (Leaf 1) (Leaf 5) +tree3 = Leaf 4 +tree4 = Branch (Branch (Leaf 4) (Leaf 3)) (Leaf 5) + +e1 = tree1 == tree1 +e2 = tree1 == tree2 +e3 = tree1 < tree2 + +quicksort :: Ord a => [a] -> [a] +quicksort [] = [] +quicksort (x:xs) = quicksort [y | y <- xs, y <= x] ++ + [x] ++ + quicksort [y | y <- xs, y > x] + +e4 = quicksort [tree1,tree2,tree3,tree4] + +-- Now for Enum: + +data Day = Sunday | Monday | Tuesday | Wednesday | Thursday | + Friday | Saturday deriving (Text,Eq,Ord,Enum) + +e5 = quicksort [Monday,Saturday,Friday,Sunday] +e6 = [Wednesday .. Friday] +e7 = [Monday, Wednesday ..] +e8 = [Saturday, Friday ..] + + +-- Page 16 Sections 5.5, 5.5.1, 5.5.2, 5.5.3 + +module Test(Bool) where + +-- Section 5.5 Numbers +-- Section 5.5.1 Numeric Class Structure +-- Section 5.5.2 Constructed Numbers + +-- Here's a brief summary of Haskell numeric classes. + +-- Class Num +-- Most general numeric class. Has addition, subtraction, multiplication. +-- Integers can be coerced to any instance of Num with fromInteger. +-- All integer constants are in this class. +-- Instances: Int, Integer, Float, Double, Ratio a, Complex a + +-- Class Real +-- This class contains ordered numbers which can be converted to +-- rationals. +-- Instances: Int, Integer, Float, Double, Ratio a + +-- Class Integral +-- This class deals with integer division. All values in Integral can +-- be mapped onto Integer. +-- Instances: Int, Integer + +-- Class Fractional +-- These are numbers which can be divided. Any rational number can +-- be converted to a fractional. Floating point constants are in +-- this class: 1.2 would be 12/10. +-- Instances: Float, Double, Ratio a + +-- Class Floating +-- This class contains all the standard floating point functions such +-- as sqrt and sin. +-- Instances: Float, Double, Complex a + +-- Class RealFrac +-- These values can be rounded to integers and approximated by rationals. +-- Instances: Float, Double, Ratio a + +-- Class RealFloat +-- These are floating point numbers constructed from a fixed precision +-- mantissa and exponent. +-- Instances: Float, Double + +-- There are only a few sensible combinations of the constructed numerics +-- with built-in types: +-- Ratio Integer (same as Rational): arbitrary precision rationals +-- Ratio Int: limited precision rationals +-- Complex Float: complex numbers with standard precision components +-- Complex Double: complex numbers with double precision components + + +-- The following function works for arbitrary numerics: + +fact :: (Num a) => a -> a +fact 0 = 1 +fact n = n*(fact (n-1)) + +-- Note the behavior when applied to different types of numbers: + +e1 :: Int +e1 = fact 6 +e2 :: Int +e2 = fact 20 -- Yale Haskell may not handle overflow gracefully! +e3 :: Integer +e3 = fact 20 +e4 :: Rational +e4 = fact 6 +e5 :: Float +e5 = fact 6 +e6 :: Complex Float +e6 = fact 6 + +-- Be careful: values like `fact 1.5' will loop! + +-- As a practical matter, Int operations are much faster than Integer +-- operations. Also, overloaded functions can be much slower than non- +-- overloaded functions. Giving a function like fact a precise typing: + +-- fact :: Int -> Int + +-- will yield much faster code. + +-- In general, numeric expressions work as expected. Literals are +-- a little tricky - they are coerced to the appropriate value. A +-- constant like 1 can be used as ANY numeric type. + +e7 :: Float +e7 = sqrt 2 +e8 :: Rational +e8 = ((4%5) * (1%2)) / (3%4) +e9 :: Rational +e9 = 2.2 * (3%11) - 1 +e10 :: Complex Float +e10 = (2 * (3:+3)) / (1.1:+2.0 - 1) +e11 :: Complex Float +e11 = sqrt (-1) +e12 :: Integer +e12 = numerator (4%2) +e13 :: Complex Float +e13 = conjugate (4:+5.2) + +-- A function using pattern matching on complex numbers: + +mag :: (RealFloat a) => Complex a -> a +mag (a:+b) = sqrt (a^2 + b^2) + +e14 :: Float +e14 = mag (1:+1) + +-- Section 5.5.3 Numeric Coercions and Overloaded Literals + +-- The Haskell type system does NOT implicitly coerce values between +-- the different numeric types! Although overloaded constants are +-- coerced when the overloading is resolved, no implicit coercion goes +-- on when values of different types are mixed. For example: + +f :: Float +f = 1.1 +i1 :: Int +i1 = 1 +i2 :: Integer +i2 = 2 + +-- All of these expressions would result in a type error (try them!): + +-- g = i1 + f +-- h = i1 + i2 +-- i3 :: Int +-- i3 = i2 + +-- Appropriate coercions must be introduced by the user to allow +-- the mixing of types in arithmetic expressions. + +e15 :: Float +e15 = f + fromIntegral i1 +e16 :: Integer +e16 = fromIntegral i1 + i2 +e17 :: Int +e17 = i1 + fromInteger i2 -- fromIntegral would have worked too. + +-- Page 17 Section 5.5.4 +module Test(Bool) where + +-- Section 5.5.4 Default Numeric Types + +-- Ambiguous contexts arise frequently in numeric expressions. When an +-- expression which produces a value with a general type, such as +-- `1' (same as `fromInteger 1'; the type is (Num a) => a), with +-- another expression which `consumes' the type, such as `show' or +-- `toInteger', ambiguity arises. This ambiguity can be resolved +-- using expression type signatures, but this gets tedious fast! +-- Assigning a type to the top level of an ambiguous expression does +-- not help: the ambiguity does not propagate to the top level. + +e1 :: String -- This type does not influence the type of the argument to show +e1 = show 1 -- Does this mean to show an Int or a Float or ... +e2 :: String +e2 = show (1 :: Float) +e3 :: String +e3 = show (1 :: Complex Float) + +-- The reason the first example works is that ambiguous numeric types are +-- resolved using defaults. The defaults in effect here are Int and +-- Double. Since Int `fits' in the expression for e1, Int is used. +-- When Int is not valid (due to other context constraints), Double +-- will be tried. + +-- This function defaults the type of the 2's to be Int + +rms :: (Floating a) => a -> a -> a +rms x y = sqrt ((x^2 + y^2) * 0.5) + +-- The C-c e evaluation used to the Haskell system also makes use of +-- defaulting. When you type an expression, the system creates a +-- simple program to print the value of the expression using a function +-- like show. If no type signature for the printed expression is given, +-- defaulting may occur. + +-- One of the reasons for adding type signatures throughout these examples +-- is to avoid unexpected defaulting. Many of the top level signatures are +-- required to avoid ambiguity. + +-- Defaulting can lead to overflow problems when values exceed Int limits. +-- Evaluate a very large integer without a type signature to observe this +-- (unfortunately this may cause a core dump or other unpleasantness). + +-- Notice that defaulting applies only to numeric classes. The +-- show (read "xyz") -- Try this if you want! +-- example uses only class Text so no defaulting occurs. + +-- Ambiguity also arises with polymorphic types. As discussed previously, +-- expressions like [] have a similar problem. + +-- e4 = [] -- Won't work since [] has type [a] and `a' is not known. + +-- Note the difference: even though the lists have no components, the type +-- of component makes a difference in printing. + +e5 = ([] :: [Int]) +e6 = ([] :: [Char]) + +-- Page 18 Sections 6, 6.1, 6.2 + +-- Section 6 Modules + +module Tree ( Tree(Leaf,Branch), fringe ) where +-- Tree(..) would work also + +data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Text + +fringe :: Tree a -> [a] +fringe (Leaf x) = [x] +fringe (Branch left right) = fringe left ++ fringe right + +-- The Emacs interface to Haskell performs evaluation within the +-- module containing the cursor. To evaluate e1 you must place the +-- cursor in module Main. + +module Main (Tree) where +import Tree ( Tree(Leaf, Branch), fringe) +-- import Tree -- this would be the same thing +e1 :: [Int] +e1 = fringe (Branch (Leaf 1) (Leaf 2)) + +-- This interactive Haskell environment can evaluate expressions in +-- any module. The use of module Main is optional. + +-- Section 6.1 Original Names and Renaming + +module Renamed where +import Tree ( Tree(Leaf,Branch), fringe) + renaming (Leaf to Root, Branch to Twig) + +e2 :: Tree Int +e2 = Twig (Root 1) (Root 2) -- Printing always uses the original names + +-- Section 6.2 Interfaces and Implementations + +-- Yale Haskell allows separate compilation of modules using +-- unit files. These are described in the user's guide. + + +-- Page 19 Sections 6.3, 6.4 + +-- Section 6.3 Abstract Data Types + +-- Since TreeADT does not import Tree it can use the name Tree without +-- any conflict. Each module has its own separate namespace. + +module TreeADT (Tree, leaf, branch, cell, left, + right, isLeaf) where + +data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Text + +leaf = Leaf +branch = Branch +cell (Leaf a) = a +left (Branch l r) = l +right (Branch l r) = r +isLeaf (Leaf _) = True +isLeaf _ = False + +module Test where +import TreeADT + +-- Since the constructors for type Tree are hidden, pattern matching +-- cannot be used. + +fringe :: Tree a -> [a] +fringe x = if isLeaf x then [cell x] + else fringe (left x) ++ fringe (right x) + +e1 :: [Int] +e1 = fringe (branch (branch (leaf 3) (leaf 2)) (leaf 1)) + +-- Section 6.4 + + +-- Page 20 Sections 7, 7.1, 7.2, 7.3 + +-- Section 7 Typing Pitfalls + +-- Section 7.1 Let-Bound Polymorphism + +module Test(e2) where + +-- f g = (g 'a',g []) -- This won't typecheck. + +-- Section 7.2 Overloaded Numerals + +-- Overloaded numerics were covered previously - here is one more example. +-- sum is a prelude function which sums the elements of a list. + +average :: (Fractional a) => [a] -> a +average xs = sum xs / fromIntegral (length xs) + +e1 :: Float -- Note that e1 would default to Double instead of Int - + -- this is due to the Fractional context. +e1 = average [1,2,3] + +-- Section 7.3 The Monomorphism Restriction + +-- The monomorphism restriction is usually encountered when functions +-- are defined without parameters. If you remove the signature for sum' +-- the monomorphism restriction will apply. +-- This will generate an error if either: +-- sum' is added to the module export list at the start of this section +-- both sumInt and sumFloat remain in the program. +-- If sum' is not exported and all uses of sum' have the same overloading, +-- there is no type error. + +sum' :: (Num a) => [a] -> a +sum' = foldl (+) 0 -- foldl reduces a list with a binary function + -- 0 is the initial value. + +sumInt :: Int +sumInt = sum' [1,2,3] + +sumFloat :: Float +sumFloat = sum' [1,2,3] + +-- If you use overloaded constants you also may encounter monomorphism: + +x :: Num a => a +x = 1 -- The type of x is Num a => a +y :: Int +y = x -- Uses x as an Int +z :: Integer +z = x -- Uses x as an Integer. A monomorphism will occur of the + -- signature for x is removed. + -- comments to see an error. + +-- Finally, if a value is exported it must not be overloaded unless bound +-- by a function binding. e2 is the only value exported. + +e2 :: Int -- Remove this to get an error. Without this line e1 will + -- be overloaded. +e2 = 1 + +-- To prevent annoying error messages about exported monomorphic variables, +-- most modules in this tutorial do not implicitly export everything - they +-- only export a single value, Bool, which was chosen to keep the export +-- list non-empty (a syntactic restriction!). In Haskell systems without +-- the evaluator used here, a module which does not export any names would +-- be useless. + +-- module Test where -- this would export everything in the module +-- module Test(Bool) -- exports only Bool +-- module Test() -- this is what we really want to do but is not valid. + +-- Page 21 Sections 8, 8.1 + +module Test(Bool) where + +-- Section 8 Input/Output +-- Section 8.1 Introduction to Continuations + +-- Simplify f here to be 1/x. + +data Maybe a = Ok a | Oops String deriving Text + +f :: Float -> Maybe Float +f x = if x == 0 then Oops "Divide by 0" else Ok (1/x) + +-- g is a `safe' call to x. The call to error could be replaced by +-- some explicit value like Oops msg -> 0. + +g x = case f x of + Ok y -> y + Oops msg -> error msg + +e1 = f 0 +e2 = g 0 +e3 = g 1 + +-- Here is the same example using continuations: + +f' :: Float -> (String -> Float) -> Float +f' x c = if x == 0 then c "Divide by 0" + else 1/x + +g' x = f' x error -- calls error on divide by 0 +g'' x = f' x (\s -> 0) -- returns 0 on divide by 0 + +e4 = g' 0 +e5 = g'' 0 + +-- Page 22 Sections 8.2, 8.3 + +module Test where + +-- Section 8.2 Continuation Based I/O + +-- We will skip the program fragments at the start of this section and +-- move directly to the writeFile / readFile example. + +-- Before we can use Haskell I/O, we need to introduce a new Emacs command: +-- C-c r. This command runs a dialogue instead of printing a value. +-- (Actually C-c e creates a dialogue on the fly and runs it in the same +-- manner as C-c r). As with C-c e you are prompted for an expression. +-- In this case, the expression must be of type Dialogue and it is +-- executed by the I/O system. We use d1,d2,... for dialogues to be +-- executed by C-c r. + +-- We make the file name a parameter to allow for easier testing. +-- Don't expect much error handling in exit. + +s1 = "This is a test of Haskell" + +main file = writeFile file s1 exit ( + readFile file exit (\s2 -> + appendChan stdout (if s1==s2 then "contents match" + else "something intervened!") exit + done)) + +d1,d2 :: Dialogue +d1 = main "/tmp/ReadMe" +d2 = main "/dev/null" -- this will read back as the empty string + +-- A simple IO program using $ for readability: ($ is defined in the Prelude) + +d3 = appendChan "stdout" "Type something: " exit $ + readChan "stdin" exit $ \s2 -> + appendChan "stdout" ("You typed " ++ head (lines s2)) exit $ + done + +-- This program suffers from a strictness problem. Strictness deals +-- with when things get evaluated. In this program, the input is not +-- needed until after the "You typed " is printed. Fixing this would +-- require some operation to look at the string before the final +-- appendChan. Here is one possible fix: + +d4 = appendChan "stdout" "Type something: " exit $ + readChan "stdin" exit $ \s2 -> + let str = head (lines s2) in + if str == str then -- This evaluates str + appendChan "stdout" ("You typed " ++ head (lines s2)) exit $ + done + else done + + +-- Section 8.3 Terminal I/O + +-- Since this programming environment runs under Emacs, the issue of +-- echoing does not really apply. However, the synchronization between +-- input and output can be seen in the following example. Since the input +-- comes a line at a time, the X's come in groups between input lines. +-- The cursor will move into the haskell dialogue buffer when the program +-- requests input. Use a ^D to stop the program (^Q^D actually). [Warning: +-- some brain damaged lisps stop not only the Haskell program but also +-- the entire compiler on ^D] + +d5 = readChan stdin exit processInput where + processInput s = loop 1 s + loop n [] = done + loop n (x:xs) | n == 10 = appendChan stdout "X" exit (loop 1 xs) + | True = loop (n+1) xs + +-- For more examples using the I/O system look in the demo programs +-- that come with haskell (in $HASKELL/progs/demo) and the report. + +-- Page 23 Sections 9, 9.1, 9.2 + +module Test(Bool) where + +-- Section 9 Arrays +-- Section 9.1 Index Types + +-- Arrays are built on the class Ix. Here are some quick examples of Ix: + +e1 :: [Int] +e1 = range (0,4) +e2 :: Int +e2 = index (0,4) 2 +low,high :: (Int,Int) +low = (1,1) +high = (3,4) +e3 = range (low,high) +e4 = index (low,high) (3,2) +e5 = inRange (low,high) (4,3) + +-- Section 9.2 Array Creation + +squares :: Array Int Int +squares = array (1,100) [i := i*i | i <- [1..100]] + +-- We can also parameterize this a little: + +squares' :: Int -> Array Int Int +squares' n = array (1,n) [i := i*i | i <- [1..n]] + +e6 :: Int +e6 = squares!6 +e7 :: (Int,Int) +e7 = bounds squares +e8 :: Array Int Int +e8 = squares' 10 + +-- Here is a function which corresponds to `take' for lists. It takes +-- an arbitrary slice out of an array. + +atake :: (Ix a) => Array a b -> (a,a) -> Array a b +atake a (l,u) | inRange (bounds a) l && inRange (bounds a) u = + array (l,u) [i := a!i | i <- range (l,u)] + | otherwise = error "Subarray out of range" + +e9 = atake squares (4,8) + +mkArray :: Ix a => (a -> b) -> (a,a) -> Array a b +mkArray f bnds = array bnds [i := f i | i <- range bnds] + +e10 :: Array Int Int +e10 = mkArray (\i -> i*i) (1,10) + +fibs :: Int -> Array Int Int +fibs n = a where + a = array (0,n) ([0 := 1, 1 := 1] ++ + [i := a!(i-1) + a!(i-2) | i <- [2..n]]) + +e11 = atake (fibs 50) (3,10) + +wavefront :: Int -> Array (Int,Int) Int +wavefront n = a where + a = array ((1,1),(n,n)) + ([(1,j) := 1 | j <- [1..n]] ++ + [(i,1) := 1 | i <- [2..n]] ++ + [(i,j) := a!(i,j-1) + a!(i-1,j-1) + a!(i-1,j) + | i <- [2..n], j <- [2..n]]) + +wave = wavefront 20 +e12 = atake wave ((1,1),(3,3)) +e13 = atake wave ((3,3),(5,5)) + +-- Here are some errors in array operations: + +e14 :: Int +e14 = wave ! (0,0) -- Out of bounds +arr1 :: Array Int Int +arr1 = array (1,10) [1 := 1] -- No value provided for 2..10 +e15,e16 :: Int +e15 = arr1 ! 1 -- works OK +e16 = arr1 ! 2 -- undefined by array + +-- Page 24 Sections 9.3, 9.4 + +module Test(Bool) where + +-- Section 9.3 Accumulation + +hist :: (Ix a, Integral b) => (a,a) -> [a] -> Array a b +hist bnds is = accumArray (+) 0 bnds [i := 1 | i <- is, inRange bnds i] + +e1 :: Array Char Int +e1 = hist ('a','z') "This counts the frequencies of each lowercase letter" + +decades :: (RealFrac a) => a -> a -> [a] -> Array Int Int +decades a b = hist (0,9) . map decade + where + decade x = floor ((x-a) * s) + s = 10 / (b - a) + +test1 :: [Float] +test1 = map sin [0..100] -- take the sine of the 0 - 100 +e2 = decades 0 1 test1 + +-- Section 9.4 Incremental Updates + +swapRows :: (Ix a, Ix b, Enum b) => a -> a -> Array (a,b) c -> Array (a,b) c +swapRows i i' a = a // ([(i,j) := a!(i',j) | j <- [jLo..jHi]] ++ + [(i',j) := a!(i,j) | j <- [jLo..jHi]]) + where ((iLo,jLo),(iHi,jHi)) = bounds a + +arr1 :: Array (Int,Int) (Int,Int) +arr1 = array ((1,1),(5,5)) [(i,j) := (i,j) | (i,j) <- range ((1,1),(5,5))] + +e3 = swapRows 2 3 arr1 + +-- Printing the arrays in more readable form makes the results easier +-- to view. + +-- This is a printer for 2d arrays + +aprint a width = shows (bounds a) . showChar '\n' . showRows lx ly where + showRows r c | r > ux = showChar '\n' + showRows r c | c > uy = showChar '\n' . showRows (r+1) ly + showRows r c = showElt (a!(r,c)) . showRows r (c+1) + showElt e = showString (take width (show e ++ repeat ' ')) . showChar ' ' + ((lx,ly),(ux,uy)) = bounds a + +showArray a w = appendChan stdout (aprint a w "") abort done + +d1 = showArray e3 6 + +swapRows' :: (Ix a, Ix b, Enum b) => a -> a -> Array (a,b) c -> Array (a,b) c +swapRows' i i' a = a // [assoc | j <- [jLo..jHi], + assoc <- [(i,j) := a!(i',j), + (i',j) := a!(i,j)]] + where ((iLo,jLo),(iHi,jHi)) = bounds a + +d2 = showArray (swapRows' 1 5 arr1) 6 + +-- Page 25 Section 9.5 + +module Test(Bool) where + +-- Section 9.5 An example: Matrix Multiplication + +aprint a width = shows (bounds a) . showChar '\n' . showRows lx ly where + showRows r c | r > ux = showChar '\n' + showRows r c | c > uy = showChar '\n' . showRows (r+1) ly + showRows r c = showElt (a!(r,c)) . showRows r (c+1) + showElt e = showString (take width (show e ++ repeat ' ')) . showChar ' ' + ((lx,ly),(ux,uy)) = bounds a + +showArray a w = appendChan stdout (aprint a w "") abort done + +matMult :: (Ix a, Ix b, Ix c, Num d) => + Array (a,b) d -> Array (b,c) d -> Array (a,c) d +matMult x y = + array resultBounds + [(i,j) := sum [x!(i,k) * y!(k,j) | k <- range (lj,uj)] + | i <- range (li,ui), + j <- range (lj',uj')] + where + ((li,lj),(ui,uj)) = bounds x + ((li',lj'),(ui',uj')) = bounds y + resultBounds + | (lj,uj)==(li',ui') = ((li,lj'),(ui,uj')) + | otherwise = error "matMult: incompatible bounds" + +mat1,mat2,mat3,mat4 :: Array (Int,Int) Int +mat1 = array ((0,0),(1,1)) [(0,0) := 1,(0,1) := 0,(1,0) := 0,(1,1) := 1] +mat2 = array ((0,0),(1,1)) [(0,0) := 1,(0,1) := 1,(1,0) := 1,(1,1) := 1] +mat3 = array ((0,0),(1,1)) [(0,0) := 1,(0,1) := 2,(1,0) := 3,(1,1) := 4] +mat4 = array ((0,0),(1,2)) [(0,0) := 1,(0,1) := 2,(0,2) := 3, + (1,0) := 4,(1,1) := 5,(1,2) := 6] + +d1 = showArray (matMult mat1 mat2) 4 +d2 = showArray (matMult mat2 mat3) 4 +d3 = showArray (matMult mat1 mat4) 4 +d4 = showArray (matMult mat4 mat1) 4 + +matMult' :: (Ix a, Ix b, Ix c, Num d) => + Array (a,b) d -> Array (b,c) d -> Array (a,c) d +matMult' x y = + accumArray (+) 0 ((li,lj'),(ui,uj')) + [(i,j) := x!(i,k) * y!(k,j) + | i <- range (li,ui), + j <- range (lj',uj'), + k <- range (lj,uj)] + + where + ((li,lj),(ui,uj)) = bounds x + ((li',lj'),(ui',uj')) = bounds y + resultBounds + | (lj,uj)==(li',ui') = ((li,lj'),(ui,uj')) + | otherwise = error "matMult: incompatible bounds" + +d5 = showArray (matMult mat1 mat2) 4 +d6 = showArray (matMult mat2 mat3) 4 + +genMatMul :: (Ix a, Ix b, Ix c) => + ([f] -> g) -> (d -> e -> f) -> + Array (a,b) d -> Array (b,c) e -> Array (a,c) g +genMatMul f g x y = + array ((li,lj'),(ui,uj')) + [(i,j) := f [(x!(i,k)) `g` (y!(k,j)) | k <- range (lj,uj)] + | i <- range (li,ui), + j <- range (lj',uj')] + where + ((li,lj),(ui,uj)) = bounds x + ((li',lj'),(ui',uj')) = bounds y + resultBounds + | (lj,uj)==(li',ui') = ((li,lj'),(ui,uj')) + | otherwise = error "matMult: incompatible bounds" + +d7 = showArray (genMatMul maximum (-) mat2 mat1) 4 +d8 = showArray (genMatMul and (==) mat1 mat2) 6 +d9 = showArray (genMatMul and (==) mat1 mat1) 6 + +-- Page 26 More about Haskell + +This is the end of the tutorial. If you wish to see more examples of +Haskell programming, Yale Haskell comes with a set of demo programs. +These can be found in $HASKELL/progs/demo. Once you have mastered the +tutorial, both the report and the user manual for Yale Haskell should +be understandable. Many examples of Haskell programming can be found in +the Prelude. The directory $HASKELL/progs/prelude contains the sources +for the Prelude. + +We appreciate any comments you have on this tutorial. Send any comments +to haskell-requests@cs.yale.edu. + + The Yale Haskell Group diff --git a/runtime/README b/runtime/README new file mode 100644 index 0000000..99d113a --- /dev/null +++ b/runtime/README @@ -0,0 +1,8 @@ +This directory contains definitions of things that are used +exclusively in code generated by the Haskell compiler. It contains +implementations of some of the things declared in the prims files for +the prelude, as well as some more generic things that the code +generator knows about. + +Note that some of the files in this directory access some Common Lisp +features directly. diff --git a/runtime/array-prims.scm b/runtime/array-prims.scm new file mode 100644 index 0000000..6b553f5 --- /dev/null +++ b/runtime/array-prims.scm @@ -0,0 +1,55 @@ +;;; array-prims.scm -- array primitives +;;; +;;; author : John & Sandra +;;; date : 14 May 1993 + + +;;; Vector reference, returning unboxed value + +(define-syntax (prim.vector-sel vec i) + `(vector-ref ,vec ,i)) + + +;;; Destructive vector update. All arguments are unboxed. + +(define-syntax (prim.vector-update vec i newval) + `(setf (vector-ref ,vec ,i) ,newval)) + + +;;; Make a vector whose elements are initialized to val (which is boxed). + +(define-syntax (prim.make-vector size val) + `(make-vector ,size ,val)) + + +;;; Copy an existing vector. + +(define-syntax (prim.copy-vector vec) + `(vector-copy ,vec)) + + +;;; Explicit force operation + +(define-syntax (prim.force x) + `(force ,x)) + + +;;; The first parameter is forced first since this prim is declared to +;;; be strict in the first arg. + +(define-syntax (prim.strict1 force-this leave-this) + `(begin + ;; Can't ignore the first argument entirely since doing so + ;; might result in variable-bound-but-not-referenced errors. + ;; Hopefully the Lisp compiler will be smart enough to get + ;; rid of this when appropriate. + ,force-this + ;; Don't generate a stupid (force (delay x)) sequence here if + ;; we don't need to. + ,(if (and (pair? leave-this) + (or (eq? (car leave-this) 'delay) + (eq? (car leave-this) 'box))) + (cadr leave-this) + `(force ,leave-this)))) + + diff --git a/runtime/debug-utils.scm b/runtime/debug-utils.scm new file mode 100644 index 0000000..e5fa971 --- /dev/null +++ b/runtime/debug-utils.scm @@ -0,0 +1,33 @@ + +;;; This has some diagnostic stuff + +;;; This forces all delays in a structure + +(define (force-all x) + (cond ((delay? x) + (force-all (force x))) + ((pair? x) + (force-all (car x)) + (force-all (cdr x))) + ((vector? x) + (dotimes (i (vector-length x)) + (force-all (vector-ref x i))))) + x) + +;;; This forces & removes all delays in a structure. + +(define (remove-delays x) + (cond ((delay? x) + (remove-delays (force x))) + ((pair? x) + (cons (remove-delays (car x)) + (remove-delays (cdr x)))) + ((vector? x) + (list->vector (map (function remove-delays) (vector->list x)))) + (else x))) + +(define (delay? x) + (and (pair? x) + (or (eq? (car x) '#t) + (eq? (car x) '#f)))) + diff --git a/runtime/io-primitives.scm b/runtime/io-primitives.scm new file mode 100644 index 0000000..85bc51b --- /dev/null +++ b/runtime/io-primitives.scm @@ -0,0 +1,178 @@ + +;;; These are the IO primitives used by PreludeIOPrims + +;;; Note: the box in write-string-stdout, write-string-file, and +;;; append-string-file are due to the NoConversion in the .hi file. +;;; The problem is that NoConversion applies to everything, not just +;;; the input arg that the conversion is not needed or. + + +(predefine (notify-input-request)) + +(define *emacs-notified* '#f) +(define *stdin-read* '#f) + +(define (initialize-io-system) + (setf *emacs-notified* '#f) + (setf *stdin-read* '#f)) + +(define (io-success . res) + (make-tagged-data 0 + (if (null? res) + (box 0) + (box (make-haskell-string (car res)))))) + +(define (io-success/bin res) + (make-tagged-data 0 (box res))) + +(define (io-success/lazy res) + (make-tagged-data 0 res)) + +(define (io-failure string) + (make-tagged-data 1 (box (make-haskell-string string)))) + +; primReadStringFile +(define (prim.read-string-file filename) + (if (file-exists? filename) + (let ((str (call-with-input-file filename + (lambda (port) + (port->string port))))) + (io-success str)) + (io-failure (format '#f "File not found: ~A~%" filename)))) + +(define (port->string port) + (call-with-output-string + (lambda (string-port) + (copy-till-eof port string-port)))) + +(define (copy-till-eof in-port out-port) + (do ((ch (read-char in-port) (read-char in-port))) + ((eof-object? ch)) + (write-char ch out-port))) + +; primWriteStringFile +(define (prim.write-string-file filename contents state) + (declare (ignore state)) + (box + (let ((stream (lisp:open (haskell-string->string filename) + :direction :output + :if-exists :overwrite + :if-does-not-exist :create))) + (print-haskell-string contents stream) + (close-output-port stream) + (io-success)))) + +;primAppendStringFile +(define (prim.append-string-file filename contents state) + (declare (ignore state)) + (box + (let ((stream (lisp:open (haskell-string->string filename) + :direction :output + :if-exists :append + :if-does-not-exist '()))) + (cond ((not (eq? stream '())) + (print-haskell-string contents stream) + (close-output-port stream) + (io-success)) + (else + (io-failure "Can't open file")))))) + +; primReadBinFile +(define (prim.read-bin-file name) + (let ((bin (lisp-read name))) + (if (and (pair? bin) (eq? (car bin) ':binary)) + (io-success/bin bin) + (io-failure "Not a bin file")))) + +; primWriteBinFile +(define (prim.write-bin-file name contents) + (let ((stream (lisp:open name :direction :output + :if-exists :overwrite + :if-does-not-exist :create))) + (write (cons ':binary contents) stream) + (close-output-port stream) + (io-success))) + +; primAppendBinFile +(define (prim.append-bin-file name contents) + (let ((bin (lisp-read name))) + (if (and (pair? bin) (eq? (car bin) ':binary)) + (let ((stream (lisp:open name :direction :output :if-exists :overwrite))) + (write (append bin contents) stream) + (io-success)) + (io-failure "Can't open Bin file")))) + +; primDeleteFile +(define (prim.delete-file name) + (if (file-exists? name) + (if (lisp:delete-file name) + (io-success) + (io-failure "Can't delete file")) + (io-failure "File not found"))) + +; primStatusFile +(define (prim.status-file name) + (if (file-exists? name) + (io-success "frw") + (io-failure (format '#f "File ~A not found" name)))) + +;primReadStdin +(define (prim.read-string-stdin state) + (declare (ignore state)) + (cond (*stdin-read* + (haskell-runtime-error "Multiple ReadChan from stdin")) + (else + (setf *stdin-read* '#t) + (delay (read-next-char))))) + +(define (read-next-char) + (when (and *emacs-mode* (not *emacs-notified*)) + (setf *emacs-notified* '#t) + (notify-input-request)) + (let ((ch (read-char))) + (if (eof-object? ch) + '() + (cons (box (char->integer ch)) + (delay (read-next-char)))))) + +; primWriteStdout +(define (prim.write-string-stdout string state) + (declare (ignore state)) + (print-haskell-string string (current-output-port)) + (box (io-success))) + +; primReadBinStdin +(define (prim.read-bin-stdin) + (haskell-runtime-error "ReadBinChan not implemented")) + +; primWriteBinStdout +(define (prim.write-bin-stdout bin) + (declare (ignore bin)) + (haskell-runtime-error "WriteBinChan not implemented")) + +;;; %%% probably bogus +; primGetEnv +(define (prim.getenv name) + (io-success (getenv name))) + +(define (lisp-read file) + (if (not (file-exists? file)) + 'error + (call-with-input-file file + (lambda (port) + (lisp:read port '#f 'error '#f))))) + +(define-integrable (prim.returnio x s) + (declare (ignore s)) + x) + +(define-integrable (prim.getstate x) + (declare (ignore x)) + 'state) + +(define-integrable (prim.getres x) + (force x)) + + + + diff --git a/runtime/prims.scm b/runtime/prims.scm new file mode 100644 index 0000000..797f21b --- /dev/null +++ b/runtime/prims.scm @@ -0,0 +1,595 @@ +;;; prims.scm -- definitions for primitives +;;; +;;; author : Sandra Loosemore +;;; date : 9 Jun 1992 +;;; +;;; WARNING!!! This file contains Common-Lisp specific code. +;;; + + +;;; Helper stuff + +(define-integrable (is-fixnum? x) + (lisp:typep x 'lisp:fixnum)) + +(define-integrable (is-integer? x) + (lisp:typep x 'lisp:integer)) + +(define-integrable (is-single-float? x) + (lisp:typep x 'lisp:single-float)) + +(define-integrable (is-double-float? x) + (lisp:typep x 'lisp:double-float)) + +(define-syntax (the-fixnum x) + `(lisp:the lisp:fixnum ,x)) + +(define-syntax (the-integer x) + `(lisp:the lisp:integer ,x)) + +(define-syntax (the-single-float x) + `(lisp:the lisp:single-float ,x)) + +(define-syntax (the-double-float x) + `(lisp:the lisp:double-float ,x)) + +(define-syntax (make-haskell-tuple2 x y) + `(make-tuple (box ,x) (box ,y))) + +;;; Abort +;;; *** Should probably do something other than just signal an error. + +(define (prim.abort s) + (haskell-runtime-error s)) + +(define (haskell-string->list s) + (if (null? s) + '() + (cons (integer->char (force (car s))) + (haskell-string->list (force (cdr s)))))) + +;;; Char + +(define-syntax (prim.char-to-int c) + `(the-fixnum ,c)) + +(define-syntax (prim.int-to-char i) + `(the-fixnum ,i)) + +(define-syntax (prim.eq-char i1 i2) + `(= (the-fixnum ,i1) (the-fixnum ,i2))) +(define-syntax (prim.not-eq-char i1 i2) + `(not (= (the-fixnum ,i1) (the-fixnum ,i2)))) +(define-syntax (prim.le-char i1 i2) + `(<= (the-fixnum ,i1) (the-fixnum ,i2))) +(define-syntax (prim.not-le-char i1 i2) + `(> (the-fixnum ,i1) (the-fixnum ,i2))) +(define-syntax (prim.not-lt-char i1 i2) + `(>= (the-fixnum ,i1) (the-fixnum ,i2))) +(define-syntax (prim.lt-char i1 i2) + `(< (the-fixnum ,i1) (the-fixnum ,i2))) + +(define-integrable prim.max-char 255) + + +;;; Floating + +(define-syntax (prim.eq-float f1 f2) + `(= (the-single-float ,f1) (the-single-float ,f2))) +(define-syntax (prim.not-eq-float f1 f2) + `(not (= (the-single-float ,f1) (the-single-float ,f2)))) +(define-syntax (prim.le-float f1 f2) + `(<= (the-single-float ,f1) (the-single-float ,f2))) +(define-syntax (prim.not-le-float f1 f2) + `(> (the-single-float ,f1) (the-single-float ,f2))) +(define-syntax (prim.not-lt-float f1 f2) + `(>= (the-single-float ,f1) (the-single-float ,f2))) +(define-syntax (prim.lt-float f1 f2) + `(< (the-single-float ,f1) (the-single-float ,f2))) + +(define-syntax (prim.eq-double f1 f2) + `(= (the-double-float ,f1) (the-double-float ,f2))) +(define-syntax (prim.not-eq-double f1 f2) + `(not (= (the-double-float ,f1) (the-double-float ,f2)))) +(define-syntax (prim.le-double f1 f2) + `(<= (the-double-float ,f1) (the-double-float ,f2))) +(define-syntax (prim.not-le-double f1 f2) + `(> (the-double-float ,f1) (the-double-float ,f2))) +(define-syntax (prim.not-lt-double f1 f2) + `(>= (the-double-float ,f1) (the-double-float ,f2))) +(define-syntax (prim.lt-double f1 f2) + `(< (the-double-float ,f1) (the-double-float ,f2))) + +(define-syntax (prim.float-max f1 f2) + `(the-single-float (max (the-single-float ,f1) (the-single-float ,f2)))) +(define-syntax (prim.float-min f1 f2) + `(the-single-float (min (the-single-float ,f1) (the-single-float ,f2)))) + +(define-syntax (prim.double-max f1 f2) + `(the-double-float (max (the-double-float ,f1) (the-double-float ,f2)))) +(define-syntax (prim.double-min f1 f2) + `(the-double-float (min (the-double-float ,f1) (the-double-float ,f2)))) + +(define-syntax (prim.plus-float f1 f2) + `(the-single-float (+ (the-single-float ,f1) (the-single-float ,f2)))) +(define-syntax (prim.minus-float f1 f2) + `(the-single-float (- (the-single-float ,f1) (the-single-float ,f2)))) +(define-syntax (prim.mul-float f1 f2) + `(the-single-float (* (the-single-float ,f1) (the-single-float ,f2)))) +(define-syntax (prim.div-float f1 f2) + `(the-single-float (/ (the-single-float ,f1) (the-single-float ,f2)))) + +(define-syntax (prim.plus-double f1 f2) + `(the-double-float (+ (the-double-float ,f1) (the-double-float ,f2)))) +(define-syntax (prim.minus-double f1 f2) + `(the-double-float (- (the-double-float ,f1) (the-double-float ,f2)))) +(define-syntax (prim.mul-double f1 f2) + `(the-double-float (* (the-double-float ,f1) (the-double-float ,f2)))) +(define-syntax (prim.div-double f1 f2) + `(the-double-float (/ (the-double-float ,f1) (the-double-float ,f2)))) + + +(define-syntax (prim.neg-float f) + `(the-single-float (- (the-single-float ,f)))) + +(define-syntax (prim.neg-double f) + `(the-double-float (- (the-double-float ,f)))) + +(define-syntax (prim.abs-float f) + `(the-single-float (lisp:abs (the-single-float ,f)))) + +(define-syntax (prim.abs-double f) + `(the-double-float (lisp:abs (the-double-float ,f)))) + + +(define-syntax (prim.exp-float f) + `(the-single-float (lisp:exp (the-single-float ,f)))) +(define-syntax (prim.log-float f) + `(the-single-float (lisp:log (the-single-float ,f)))) +(define-syntax (prim.sqrt-float f) + `(the-single-float (lisp:sqrt (the-single-float ,f)))) +(define-syntax (prim.sin-float f) + `(the-single-float (lisp:sin (the-single-float ,f)))) +(define-syntax (prim.cos-float f) + `(the-single-float (lisp:cos (the-single-float ,f)))) +(define-syntax (prim.tan-float f) + `(the-single-float (lisp:tan (the-single-float ,f)))) +(define-syntax (prim.asin-float f) + `(the-single-float (lisp:asin (the-single-float ,f)))) +(define-syntax (prim.acos-float f) + `(the-single-float (lisp:acos (the-single-float ,f)))) +(define-syntax (prim.atan-float f) + `(the-single-float (lisp:atan (the-single-float ,f)))) +(define-syntax (prim.sinh-float f) + `(the-single-float (lisp:sinh (the-single-float ,f)))) +(define-syntax (prim.cosh-float f) + `(the-single-float (lisp:cosh (the-single-float ,f)))) +(define-syntax (prim.tanh-float f) + `(the-single-float (lisp:tanh (the-single-float ,f)))) +(define-syntax (prim.asinh-float f) + `(the-single-float (lisp:asinh (the-single-float ,f)))) +(define-syntax (prim.acosh-float f) + `(the-single-float (lisp:acosh (the-single-float ,f)))) +(define-syntax (prim.atanh-float f) + `(the-single-float (lisp:atanh (the-single-float ,f)))) + + +(define-syntax (prim.exp-double f) + `(the-double-float (lisp:exp (the-double-float ,f)))) +(define-syntax (prim.log-double f) + `(the-double-float (lisp:log (the-double-float ,f)))) +(define-syntax (prim.sqrt-double f) + `(the-double-float (lisp:sqrt (the-double-float ,f)))) +(define-syntax (prim.sin-double f) + `(the-double-float (lisp:sin (the-double-float ,f)))) +(define-syntax (prim.cos-double f) + `(the-double-float (lisp:cos (the-double-float ,f)))) +(define-syntax (prim.tan-double f) + `(the-double-float (lisp:tan (the-double-float ,f)))) +(define-syntax (prim.asin-double f) + `(the-double-float (lisp:asin (the-double-float ,f)))) +(define-syntax (prim.acos-double f) + `(the-double-float (lisp:acos (the-double-float ,f)))) +(define-syntax (prim.atan-double f) + `(the-double-float (lisp:atan (the-double-float ,f)))) +(define-syntax (prim.sinh-double f) + `(the-double-float (lisp:sinh (the-double-float ,f)))) +(define-syntax (prim.cosh-double f) + `(the-double-float (lisp:cosh (the-double-float ,f)))) +(define-syntax (prim.tanh-double f) + `(the-double-float (lisp:tanh (the-double-float ,f)))) +(define-syntax (prim.asinh-double f) + `(the-double-float (lisp:asinh (the-double-float ,f)))) +(define-syntax (prim.acosh-double f) + `(the-double-float (lisp:acosh (the-double-float ,f)))) +(define-syntax (prim.atanh-double f) + `(the-double-float (lisp:atanh (the-double-float ,f)))) + + +(define-integrable prim.pi-float (lisp:coerce lisp:pi 'lisp:single-float)) + +(define-integrable prim.pi-double (lisp:coerce lisp:pi 'lisp:double-float)) + + +;;; Assumes rationals are represented as a 2-tuple of integers + +(define (prim.rational-to-float x) + (let ((n (tuple-select 2 0 x)) + (d (tuple-select 2 1 x))) + (if (eqv? d 0) + (haskell-runtime-error "Divide by 0.") + (prim.rational-to-float-aux n d)))) + +(define (prim.rational-to-float-aux n d) + (declare (type integer n d)) + (/ (lisp:coerce n 'lisp:single-float) + (lisp:coerce d 'lisp:single-float))) + +(define (prim.rational-to-double x) + (let ((n (tuple-select 2 0 x)) + (d (tuple-select 2 1 x))) + (if (eqv? d 0) + (haskell-runtime-error "Divide by 0.") + (prim.rational-to-double-aux n d)))) + +(define (prim.rational-to-double-aux n d) + (declare (type integer n d)) + (/ (lisp:coerce n 'lisp:double-float) + (lisp:coerce d 'lisp:double-float))) + +(define (prim.float-to-rational x) + (let ((r (lisp:rational (the lisp:single-float x)))) + (declare (type rational r)) + (make-tuple (lisp:numerator r) (lisp:denominator r)))) + +(define (prim.double-to-rational x) + (let ((r (lisp:rational (the lisp:double-float x)))) + (declare (type rational r)) + (make-tuple (lisp:numerator r) (lisp:denominator r)))) + + +(define-integrable prim.float-1 (lisp:coerce 1.0 'lisp:single-float)) +(define-integrable prim.double-1 (lisp:coerce 1.0 'lisp:double-float)) + +(define-integrable prim.float-digits + (lisp:float-digits prim.float-1)) + +(define-integrable prim.double-digits + (lisp:float-digits prim.double-1)) + +(define-integrable prim.float-radix + (lisp:float-radix prim.float-1)) + +(define-integrable prim.double-radix + (lisp:float-radix prim.double-1)) + + +;;; Sometimes least-positive-xxx-float is denormalized. + +(define-integrable prim.float-min-exp + (multiple-value-bind (m e) + (lisp:decode-float + #+lucid lcl:least-positive-normalized-single-float + #-lucid lisp:least-positive-single-float) + (declare (ignore m)) + e)) + +(define-integrable prim.double-min-exp + (multiple-value-bind (m e) + (lisp:decode-float + #+lucid lcl:least-positive-normalized-double-float + #-lucid lisp:least-positive-double-float) + (declare (ignore m)) + e)) + +(define-integrable prim.float-max-exp + (multiple-value-bind (m e) + (lisp:decode-float lisp:most-positive-single-float) + (declare (ignore m)) + e)) + +(define-integrable prim.double-max-exp + (multiple-value-bind (m e) + (lisp:decode-float lisp:most-positive-double-float) + (declare (ignore m)) + e)) + +(define-integrable (prim.float-range x) + (declare (ignore x)) + (make-haskell-tuple2 prim.float-min-exp prim.float-max-exp)) + +(define-integrable (prim.double-range x) + (declare (ignore x)) + (make-haskell-tuple2 prim.double-min-exp prim.double-max-exp)) + + +;;; *** I'm not sure if these are correct. Should the exponent value +;;; *** be taken as the value that lisp:integer-decode-float returns, +;;; *** or as the value that lisp:decode-float returns? (They're +;;; *** not the same because the significand is scaled differently.) +;;; *** I'm guessing that Haskell's model is to use the actual numbers +;;; *** that are in the bit fields + +;;; jcp - I removed this since Haskell requires an integer instead of a +;;; fractional mantissa. My theory is that integer-decode-float returns +;;; what Haskell wants without fiddling (except sign reattachment) + +(define (exponent-adjustment m) + (if (eqv? prim.float-radix 2) + ;; the usual case -- e.g. IEEE floating point + (lisp:integer-length m) + (lisp:ceiling (lisp:log m prim.float-radix)))) + +(define (prim.decode-float f) + (multiple-value-bind (m e s) + (lisp:integer-decode-float (the-single-float f)) + (make-haskell-tuple2 (* (the-integer m) (the-fixnum s)) + (the-fixnum e)))) + +(define (prim.decode-double f) + (multiple-value-bind (m e s) + (lisp:integer-decode-float (the-double-float f)) + (make-haskell-tuple2 (* (the-integer m) (the-fixnum s)) + (the-fixnum e)))) + +(define (prim.encode-float m e) + (lisp:scale-float (lisp:coerce m 'lisp:single-float) (the-fixnum e))) + +(define (prim.encode-double m e) + (lisp:scale-float (lisp:coerce m 'lisp:double-float) (the-fixnum e))) + + +;;; Integral + +(define-syntax (prim.eq-int i1 i2) + `(= (the-fixnum ,i1) (the-fixnum ,i2))) +(define-syntax (prim.not-eq-int i1 i2) + `(not (= (the-fixnum ,i1) (the-fixnum ,i2)))) +(define-syntax (prim.le-int i1 i2) + `(<= (the-fixnum ,i1) (the-fixnum ,i2))) +(define-syntax (prim.not-le-int i1 i2) + `(> (the-fixnum ,i1) (the-fixnum ,i2))) +(define-syntax (prim.not-lt-int i1 i2) + `(>= (the-fixnum ,i1) (the-fixnum ,i2))) +(define-syntax (prim.lt-int i1 i2) + `(< (the-fixnum ,i1) (the-fixnum ,i2))) +(define-syntax (prim.int-max i1 i2) + `(the-fixnum (max (the-fixnum ,i1) (the-fixnum ,i2)))) +(define-syntax (prim.int-min i1 i2) + `(the-fixnum (min (the-fixnum ,i1) (the-fixnum ,i2)))) + +(define-syntax (prim.eq-integer i1 i2) + `(= (the-integer ,i1) (the-integer ,i2))) +(define-syntax (prim.not-eq-integer i1 i2) + `(not (= (the-integer ,i1) (the-integer ,i2)))) +(define-syntax (prim.le-integer i1 i2) + `(<= (the-integer ,i1) (the-integer ,i2))) +(define-syntax (prim.not-le-integer i1 i2) + `(> (the-integer ,i1) (the-integer ,i2))) +(define-syntax (prim.not-lt-integer i1 i2) + `(>= (the-integer ,i1) (the-integer ,i2))) +(define-syntax (prim.lt-integer i1 i2) + `(< (the-integer ,i1) (the-integer ,i2))) +(define-syntax (prim.integer-max i1 i2) + `(the-integer (max (the-integer ,i1) (the-integer ,i2)))) +(define-syntax (prim.integer-min i1 i2) + `(the-integer (min (the-integer ,i1) (the-integer ,i2)))) + + +(define-syntax (prim.plus-int i1 i2) + `(the-fixnum (+ (the-fixnum ,i1) (the-fixnum ,i2)))) +(define-syntax (prim.minus-int i1 i2) + `(the-fixnum (- (the-fixnum ,i1) (the-fixnum ,i2)))) +(define-syntax (prim.mul-int i1 i2) + `(the-fixnum (* (the-fixnum ,i1) (the-fixnum ,i2)))) +(define-syntax (prim.neg-int i) + `(the-fixnum (- (the-fixnum ,i)))) +(define-syntax (prim.abs-int i) + `(the-fixnum (lisp:abs (the-fixnum ,i)))) + +(define-integrable prim.minint lisp:most-negative-fixnum) +(define-integrable prim.maxint lisp:most-positive-fixnum) + +(define-syntax (prim.plus-integer i1 i2) + `(the-integer (+ (the-integer ,i1) (the-integer ,i2)))) +(define-syntax (prim.minus-integer i1 i2) + `(the-integer (- (the-integer ,i1) (the-integer ,i2)))) +(define-syntax (prim.mul-integer i1 i2) + `(the-integer (* (the-integer ,i1) (the-integer ,i2)))) +(define-syntax (prim.neg-integer i) + `(the-integer (- (the-integer ,i)))) +(define-syntax (prim.abs-integer i) + `(the-integer (lisp:abs (the-integer ,i)))) + + +(define (prim.div-rem-int i1 i2) + (multiple-value-bind (q r) + (lisp:truncate (the-fixnum i1) (the-fixnum i2)) + (make-tuple (box (the-fixnum q)) (box (the-fixnum r))))) + +(define (prim.div-rem-integer i1 i2) + (multiple-value-bind (q r) + (lisp:truncate (the-integer i1) (the-integer i2)) + (make-tuple (box (the-integer q)) (box (the-integer r))))) + +(define (prim.integer-to-int i) + (if (is-fixnum? i) + (the-fixnum i) + (haskell-runtime-error "Integer -> Int overflow."))) + +(define-syntax (prim.int-to-integer i) + i) + +;;; Binary + +(define prim.nullbin '()) + +(define (prim.is-null-bin x) + (null? x)) + +(define (prim.show-bin-int i b) + (cons i b)) + +(define (prim.show-bin-integer i b) + (cons i b)) + +(define (prim.show-bin-float f b) + (cons f b)) + +(define (prim.show-bin-double f b) + (cons f b)) + +(define (prim.bin-read-error) + (haskell-runtime-error "Error: attempt to read from an incompatible Bin.")) + +(define (prim.read-bin-int b) + (if (or (null? b) (not (is-fixnum? (car b)))) + (prim.bin-read-error) + (make-haskell-tuple2 (car b) (cdr b)))) + +(define (prim.read-bin-integer b) + (if (or (null? b) (not (is-integer? (car b)))) + (prim.bin-read-error) + (make-haskell-tuple2 (car b) (cdr b)))) + +(define (prim.read-bin-float b) + (if (or (null? b) (not (is-single-float? (car b)))) + (prim.bin-read-error) + (make-haskell-tuple2 (car b) (cdr b)))) + +(define (prim.read-bin-double b) + (if (or (null? b) (not (is-double-float? (car b)))) + (prim.bin-read-error) + (make-haskell-tuple2 (car b) (cdr b)))) + +(define (prim.read-bin-small-int b m) + (if (or (null? b) + (not (is-fixnum? (car b))) + (> (the-fixnum (car b)) (the-fixnum m))) + (prim.bin-read-error) + (make-haskell-tuple2 (car b) (cdr b)))) + +(define (prim.append-bin x y) + (append x y)) + + +;;; String primitives + +;;; Calls to prim.string-eq are generated by the CFN to pattern match +;;; against string constants. So normally one of the arguments will be +;;; a constant string. Treat this case specially to avoid consing up +;;; a haskell string whenever it's called. +;;; This function is strict in both its arguments. + +(define-syntax (prim.string-eq s1 s2) + (cond ((and (pair? s1) + (eq? (car s1) 'make-haskell-string)) + `(prim.string-eq-inline ,(cadr s1) 0 ,(string-length (cadr s1)) ,s2)) + ((and (pair? s2) + (eq? (car s2) 'make-haskell-string)) + `(prim.string-eq-inline ,(cadr s2) 0 ,(string-length (cadr s2)) ,s1)) + (else + `(prim.string-eq-notinline ,s1 ,s2)))) + +(define (prim.string-eq-inline lisp-string i n haskell-string) + (declare (type fixnum i n)) + (cond ((eqv? i n) + ;; Reached end of Lisp string constant -- better be at the end + ;; of the Haskell string, too. + (if (null? haskell-string) '#t '#f)) + ((null? haskell-string) + ;; The Haskell string is too short. + '#f) + ((eqv? (the fixnum (char->integer (string-ref lisp-string i))) + (the fixnum (force (car haskell-string)))) + ;; Next characters match, recurse + (prim.string-eq-inline + lisp-string (the fixnum (+ i 1)) n (force (cdr haskell-string)))) + (else + ;; No match + '#f))) + +(define (prim.string-eq-notinline s1 s2) + (cond ((null? s1) + ;; Reached end of first string. + (if (null? s2) '#t '#f)) + ((null? s2) + ;; Second string too short. + '#f) + ((eqv? (the fixnum (force (car s1))) (the fixnum (force (car s2)))) + (prim.string-eq-notinline (force (cdr s1)) (force (cdr s2)))) + (else + '#f))) + + +;;; List primitives + + +;;; The first argument is strict and the second is a delay. + +(define-syntax (prim.append l1 l2) + (cond ((and (pair? l1) + (eq? (car l1) 'make-haskell-string)) + `(make-haskell-string-tail ,(cadr l1) ,l2)) + ((equal? l1 ''()) + `(force ,l2)) + ((equal? l2 '(box '())) + l1) + ;; *** could also look for + ;; *** (append (cons x (box y)) z) => (cons x (box (append y z))) + ;; *** but I don't think this happens very often anyway + (else + `(prim.append-aux ,l1 ,l2)))) + +(define (prim.append-aux l1 l2) + (cond ((null? l1) + (force l2)) + ((and (forced? l2) (eq? (unbox l2) '())) + ;; Appending nil is identity. + l1) + ((forced? (cdr l1)) + ;; Append eagerly if the tail of the first list argument has + ;; already been forced. + (cons (car l1) + (if (null? (unbox (cdr l1))) + l2 ; don't force this!! + (box (prim.append-aux (unbox (cdr l1)) l2))))) + (else + (cons (car l1) (delay (prim.append-aux (force (cdr l1)) l2)))) + )) + + +;;; Both arguments are forced here. Have to be careful not to call +;;; recursively with an argument of 0. +;;; *** This is no longer used. + +(define (prim.take n l) + (declare (type fixnum n)) + (cond ((not (pair? l)) + '()) + ((eqv? n 1) + ;; Only one element to take. + (cons (car l) (box '()))) + ((forced? (cdr l)) + ;; Take eagerly if the tail of the list has already been forced. + (cons (car l) (box (prim.take (- n 1) (unbox (cdr l)))))) + (else + (cons (car l) (delay (prim.take (- n 1) (force (cdr l)))))) + )) + + +;;; The optimizer gets rid of all first-order calls to these functions. + +(define (prim.foldr k z l) + ;; k and z are nonstrict, l is strict + (if (null? l) + (force z) + (funcall (force k) + (car l) + (delay (prim.foldr k z (force (cdr l))))))) + +(define (prim.build g) + ;; g is strict + (funcall g + (box (function make-cons-constructor)) + (box '()))) diff --git a/runtime/runtime-utils.scm b/runtime/runtime-utils.scm new file mode 100644 index 0000000..f43c930 --- /dev/null +++ b/runtime/runtime-utils.scm @@ -0,0 +1,384 @@ +;;; runtime-utils.scm -- basic runtime support +;;; +;;; author : Sandra Loosemore +;;; date : 9 Jun 1992 +;;; +;;; This file contains definitions (beyond the normal mumble stuff) +;;; that is referenced directly in code built by the code generator. +;;; See backend/codegen.scm. +;;; + + + +;;; (delay form) +;;; returns a delay object with unevaluated "form". + +(define-syntax (delay form) + `(cons '#f (lambda () ,form))) + + +;;; (box form) +;;; returns a delay object with evaluated "form". + +(define-syntax (box form) + (cond ((number? form) + `(quote ,(cons '#t form))) + ((and (pair? form) (eq? (car form) 'quote)) + `(quote ,(cons '#t (cadr form)))) + (else + `(cons '#t ,form)))) + +(define-syntax (unbox form) + `(cdr ,form)) + +(define-syntax (forced? form) + `(car ,form)) + + +;;; (force delay) +;;; return the value of the delay object. + +(define (force delay-object) + (declare (type pair delay-object)) + (if (car delay-object) + (cdr delay-object) + (begin + (let ((result (funcall (cdr delay-object)))) + (setf (car delay-object) '#t) + (setf (cdr delay-object) result))))) + +;;; Inline version of the above. Not good to use everywhere because +;;; of code bloat problems, but handy for helper functions. + +(define-syntax (force-inline delay-object) + (let ((temp1 (gensym)) + (temp2 (gensym))) + `(let ((,temp1 ,delay-object)) + (declare (type pair ,temp1)) + (if (car ,temp1) + (cdr ,temp1) + (let ((,temp2 (funcall (cdr ,temp1)))) + (setf (car ,temp1) '#t) + (setf (cdr ,temp1) ,temp2)))))) + + +;;; (make-curried-fn opt-fn strictness) +;;; The basic idea is to compare the number of arguments received against +;;; the number expected. +;;; If the same, call the optimized entry point opt-fn. +;;; If more, apply the result of calling the optimized entry to the +;;; leftover arguments. +;;; If less, make a closure that accepts the additional arguments. + +(define (make-curried-fn opt-fn strictness) + (lambda args + (curried-fn-body '() args opt-fn strictness))) + +(define (curried-fn-body previous-args args opt-fn strictness) + (multiple-value-bind + (saturated? actual-args leftover-args leftover-strictness) + (process-curried-fn-args strictness args '()) + (setf actual-args (append previous-args actual-args)) + (if saturated? + (if (null? leftover-args) + (apply opt-fn actual-args) + (apply (apply opt-fn actual-args) leftover-args)) + (lambda more-args + (curried-fn-body actual-args more-args opt-fn leftover-strictness))) + )) + +(define (process-curried-fn-args strictness args actual-args) + (cond ((null? strictness) + ;; At least as many arguments as expected. + (values '#t (nreverse actual-args) args strictness)) + ((null? args) + ;; Not enough arguments supplied. + (values '#f (nreverse actual-args) args strictness)) + (else + ;; Process the next argument. + (if (car strictness) + (push (force-inline (car args)) actual-args) + (push (car args) actual-args)) + (process-curried-fn-args (cdr strictness) (cdr args) actual-args)) + )) + + +;;; Special cases of the above. + +(define (make-curried-fn-1-strict opt-fn) + (lambda (arg1 . moreargs) + (setf arg1 (force-inline arg1)) + (if (null? moreargs) + (funcall opt-fn arg1) + (apply (funcall opt-fn arg1) moreargs)))) + +(define (make-curried-fn-1-nonstrict opt-fn) + (lambda (arg1 . moreargs) + (if (null? moreargs) + (funcall opt-fn arg1) + (apply (funcall opt-fn arg1) moreargs)))) + + +;;; Here's a similar helper function used for making data constructors. + +(define (constructor-body previous-args args arity fn) + (declare (type fixnum arity)) + (let ((n (length args))) + (declare (type fixnum n)) + (setf args (append previous-args args)) + (cond ((eqv? n arity) + (apply fn args)) + ((< n arity) + (lambda more-args + (constructor-body args more-args (- arity n) fn))) + (else + (error "Too many arguments supplied to constructor."))))) + + +;;; Special case for cons constructor + +(define (make-cons-constructor . args) + (constructor-body '() args 2 (function cons))) + + +;;; (make-tuple-constructor arity) +;;; return a function that makes an untagged data structure with "arity" +;;; slots. "arity" is a constant. + +(define-integrable *max-predefined-tuple-arity* 10) + +(define (make-tuple-constructor-aux arity) + (cond ((eqv? arity 0) + ;; Actually, should never happen -- this is the unit constructor + 0) + ((eqv? arity 1) + (lambda args + (constructor-body '() args 2 (lambda (x) x)))) + ((eqv? arity 2) + (lambda args + (constructor-body '() args 2 (function cons)))) + (else + (lambda args + (constructor-body '() args arity (function vector)))))) + +(define *predefined-tuple-constructors* + (let ((result '())) + (dotimes (i *max-predefined-tuple-arity*) + (push (make-tuple-constructor-aux i) result)) + (list->vector (nreverse result)))) + +(define-syntax (make-tuple-constructor arity) + (declare (type fixnum arity)) + (if (< arity *max-predefined-tuple-arity*) + `(vector-ref *predefined-tuple-constructors* ,arity) + `(make-tuple-constructor-aux ,arity))) + + +;;; (make-tuple . args) +;;; uncurried version of the above + +(define-syntax (make-tuple . args) + (let ((arity (length args))) + (cond ((eqv? arity 0) + ;; Actually, should never happen -- this is the unit constructor + 0) + ((eqv? arity 1) + (car args)) + ((eqv? arity 2) + `(cons ,@args)) + (else + `(vector ,@args))))) + + +;;; (make-tagged-data-constructor n arity) +;;; return a function that makes a data structure with tag "n" and +;;; "arity" slots. + +(define-integrable *max-predefined-tagged-data-tag* 10) +(define-integrable *max-predefined-tagged-data-arity* 10) + +(define (make-tagged-data-constructor-aux n arity) + (if (eqv? arity 0) + (vector n) + (lambda args + (constructor-body (list n) args arity (function vector))))) + +(define *predefined-tagged-data-constructors* + (let ((result '())) + (dotimes (i *max-predefined-tagged-data-arity*) + (let ((inner-result '())) + (dotimes (j *max-predefined-tagged-data-tag*) + (push (make-tagged-data-constructor-aux j i) inner-result)) + (push (list->vector (nreverse inner-result)) result))) + (list->vector (nreverse result)))) + +(define-syntax (make-tagged-data-constructor n arity) + (declare (type fixnum arity n)) + (if (and (< arity *max-predefined-tagged-data-arity*) + (< n *max-predefined-tagged-data-tag*)) + `(vector-ref (vector-ref *predefined-tagged-data-constructors* ,arity) + ,n) + `(make-tagged-data-constructor-aux ,n ,arity))) + + +;;; (make-tagged-data n . args) +;;; uncurried version of the above + +(define-syntax (make-tagged-data n . args) + `(vector ,n ,@args)) + + +;;; (tuple-select arity i object) +;;; extract component "i" from untagged "object" + +(define-syntax (tuple-select arity i object) + (cond ((eqv? arity 1) + object) + ((eqv? arity 2) + (if (eqv? i 0) + `(car ,object) + `(cdr ,object))) + (else + `(vector-ref (the vector ,object) (the fixnum ,i))))) + + +;;; (tagged-data-select arity i object) +;;; extract component "i" from tagged "object" + +(define-syntax (tagged-data-select arity i object) + (declare (ignore arity)) + `(vector-ref (the vector ,object) (the fixnum ,(1+ i)))) + + +;;; (constructor-number object) +;;; return the tag from "object" + +(define-syntax (constructor-number object) + `(vector-ref (the vector ,object) 0)) + +(define-syntax (funcall-force fn . args) + (let* ((n (length args)) + (junk (assv n '((1 . funcall-force-1) + (2 . funcall-force-2) + (3 . funcall-force-3) + (4 . funcall-force-4))))) + `(,(if junk (cdr junk) 'funcall-force-n) ,fn ,@args))) + +(define (funcall-force-1 fn a1) + (funcall (force-inline fn) a1)) +(define (funcall-force-2 fn a1 a2) + (funcall (force-inline fn) a1 a2)) +(define (funcall-force-3 fn a1 a2 a3) + (funcall (force-inline fn) a1 a2 a3)) +(define (funcall-force-4 fn a1 a2 a3 a4) + (funcall (force-inline fn) a1 a2 a3 a4)) +(define-syntax (funcall-force-n fn . args) + `(funcall (force ,fn) ,@args)) + + +;;; (make-haskell-string string) +;;; Converts a Lisp string lazily to a boxed haskell string (makes +;;; a delay with a magic function). Returns an unboxed result. + +(define (make-haskell-string string) + (declare (type string string)) + (let ((index 1) + (size (string-length string))) + (declare (type fixnum index size)) + (cond ((eqv? size 0) + '()) + ((eqv? size 1) + (cons (box (char->integer (string-ref string 0))) + (box '()))) + (else + (letrec ((next-fn + (lambda () + (let ((ch (char->integer (string-ref string index)))) + (incf index) + (cons (box ch) + (if (eqv? index size) + (box '()) + (cons '#f next-fn))))))) + (cons (box (char->integer (string-ref string 0))) + (cons '#f next-fn)))) + ))) + + +;;; Similar, but accepts an arbitrary tail (which must be a delay object) + +(define (make-haskell-string-tail string tail-delay) + (declare (type string string)) + (let ((index 1) + (size (string-length string))) + (declare (type fixnum index size)) + (cond ((eqv? size 0) + (force-inline tail-delay)) + ((eqv? size 1) + (cons (box (char->integer (string-ref string 0))) + tail-delay)) + (else + (letrec ((next-fn + (lambda () + (let ((ch (char->integer (string-ref string index)))) + (incf index) + (cons (box ch) + (if (eqv? index size) + tail-delay + (cons '#f next-fn))))))) + (cons (box (char->integer (string-ref string 0))) + (cons '#f next-fn)))) + ))) + + +(define (haskell-string->string s) + (let ((length 0)) + (declare (type fixnum length)) + (do ((s s (force (cdr s)))) + ((null? s)) + (setf length (+ length 1))) + (let ((result (make-string length))) + (declare (type string result)) + (do ((s s (unbox (cdr s))) + (i 0 (+ i 1))) + ((null? s)) + (declare (type fixnum i)) + (setf (string-ref result i) (integer->char (force (car s))))) + result))) + + +(define (print-haskell-string s port) + (do ((s1 s (force (cdr s1)))) + ((null? s1)) + (write-char (integer->char (force (car s1))) port))) + +;;; This explicates the value returned by a proc (the IO () type). + +(define (insert-unit-value x) + (declare (ignore x)) + 0) + +;;; These handle list conversions + +(define (haskell-list->list fn l) + (if (null? l) + '() + (cons (funcall fn (force (car l))) + (haskell-list->list fn (force (cdr l)))))) + +(define (list->haskell-list fn l) + (if (null? l) + '() + (cons (box (funcall fn (car l))) + (box (list->haskell-list fn (cdr l)))))) + +(define (haskell-list->list/identity l) + (if (null? l) + '() + (cons (force (car l)) + (haskell-list->list/identity (force (cdr l)))))) + +(define (list->haskell-list/identity l) + (if (null? l) + '() + (cons (box (car l)) + (box (list->haskell-list/identity (cdr l)))))) diff --git a/runtime/runtime.scm b/runtime/runtime.scm new file mode 100644 index 0000000..bd5713e --- /dev/null +++ b/runtime/runtime.scm @@ -0,0 +1,26 @@ +;;; runtime.scm +;;; +;;; author : John +;;; + + +(define-compilation-unit runtime + (source-filename "$Y2/runtime/") + (require global) + (unit runtime-utils + (source-filename "runtime-utils.scm")) + (unit prims + (require runtime-utils) + (source-filename "prims.scm")) + (unit io-primitives + (require runtime-utils) + (source-filename "io-primitives.scm")) + (unit array-prims + (require runtime-utils) + (source-filename "array-prims.scm")) + (unit debug-utils + (require runtime-utils) + (source-filename "debug-utils.scm")) + (unit tuple-prims + (require runtime-utils) + (source-filename "tuple-prims.scm"))) diff --git a/runtime/tuple-prims.scm b/runtime/tuple-prims.scm new file mode 100644 index 0000000..6eb0cbf --- /dev/null +++ b/runtime/tuple-prims.scm @@ -0,0 +1,86 @@ +;; these primitives support arbitrary sized tuples. + +(define (prim.tupleSize x) + (vector-length x)) + +(define (prim.tupleSel tuple i n) + (force + (if (eqv? n 2) + (if (eqv? i 0) + (car tuple) + (cdr tuple)) + (vector-ref tuple i)))) + +(define (prim.list->tuple l) + (let ((l (haskell-list->list/non-strict l))) + (if (null? (cddr l)) + (cons (car l) (cadr l)) + (list->vector l)))) + +(define (haskell-list->list/non-strict l) + (if (null? l) + '() + (cons (car l) + (haskell-list->list/non-strict (force (cdr l)))))) + +(define (prim.dict-sel dicts i) + (force (vector-ref dicts i))) + +;;; These generate dictionaries. + +(define-local-syntax (create-dict dicts vars other-dicts) + `(let ((dict-vector (box (list->vector ,dicts)))) + (make-tuple + ,@(map (lambda (v) + `(delay (funcall (dynamic ,v) dict-vector))) + vars) + ,@(map (lambda (sd) + `(delay (,(car sd) + (map (lambda (d) + (tuple-select ,(cadr sd) ,(caddr sd) (force d))) + ,dicts)))) + other-dicts)))) + +(define prim.tupleEqdict + (lambda dicts + (tupleEqDict/l dicts))) + +(define (tupleEqDict/l dicts) + (create-dict dicts + (|PreludeTuple:tupleEq| |PreludeTuple:tupleNeq|) + ())) + +(define prim.tupleOrdDict + (lambda dicts + (tupleOrdDict/l dicts))) + +(define (tupleOrdDict/l d) + (create-dict d + (|PreludeTuple:tupleLe| |PreludeTuple:tupleLeq| + |PreludeTuple:tupleGe| |PreludeTuple:tupleGeq| + |PreludeTuple:tupleMax| |PreludeTuple:tupleMin|) + ((tupleEqDict/l 7 6)))) + +(define prim.tupleIxDict + (lambda dicts + (create-dict dicts + (|PreludeTuple:tupleRange| |PreludeTuple:tupleIndex| + |PreludeTuple:tupleInRange|) + ((tupleEqDict/l 6 3) (tupleTextDict/l 6 4) (tupleOrdDict/l 6 5))))) + +(define prim.tupleTextDict + (lambda dicts + (tupleTextDict/l dicts))) + +(define (tupleTextDict/l d) + (create-dict d + (|PreludeTuple:tupleReadsPrec| |PreludeTuple:tupleShowsPrec| + |PreludeTuple:tupleReadList| |PreludeTuple:tupleShowList|) + ())) + +(define prim.tupleBinaryDict + (lambda dicts + (create-dict dicts + (|PreludeTuple:tupleReadBin| |PreludeTuple:tupleShowBin|) + ()))) + diff --git a/support/README b/support/README new file mode 100644 index 0000000..6127bc6 --- /dev/null +++ b/support/README @@ -0,0 +1,4 @@ +This directory contains utilities that are layered on top of the basic +mumble support stuff. There should be no T-specific or CL-specific +code in this area. + diff --git a/support/compile.scm b/support/compile.scm new file mode 100644 index 0000000..77e222f --- /dev/null +++ b/support/compile.scm @@ -0,0 +1,447 @@ +;;; compile.scm -- compilation utilities +;;; +;;; author : Sandra Loosemore +;;; date : 24 Oct 1991 +;;; +;;; This file defines a makefile-like compilation system that supports +;;; a hierarchy of dependencies. +;;; The external entry points are define-compilation-unit, load-unit, and +;;; compile-and-load-unit. + + + +;;;===================================================================== +;;; Parsing +;;;===================================================================== + + +;;; Establish global defaults for filenames. + +(define compile.source-filename source-file-type) +(define compile.binary-filename binary-file-type) +(define compile.binary-subdir (string-append lisp-implementation-name "/")) +(define compile.delayed-loads '()) + + +;;; Top level units are stored in this table. +;;; This is really a slight wart on the whole scheme of things; this +;;; is done instead of storing the top-level units in variables because +;;; we were getting unintentional name collisions. + +(define compile.unit-table (make-table)) + +(define-syntax (compile.lookup-unit name) + `(table-entry compile.unit-table ,name)) + +(define (mung-global-units names lexical-units) + (map (lambda (n) + (if (memq n lexical-units) + n + `(compile.lookup-unit ',n))) + names)) + + +;;; Top-level compilation units are defined with define-compilation-unit. +;;; The body can consist of the following clauses: +;;; +;;; (source-filename ) +;;; (binary-filename ) +;;; Specify source and/or binary file names. For nested units, these +;;; are merged with defaults from outer units. If you don't specify +;;; an explicit binary filename, it's inherited from the source file +;;; name. +;;; (require ...) +;;; Specify compile/load dependencies. Arguments are names of other +;;; units/component files; these names have scoping like let*, so a unit +;;; can require previously listed units at the same or outer level. +;;; (unit name ....) +;;; Specifies a nested unit. This can appear multiple times. +;;; If a unit doesn't include any nested units, then it's a leaf +;;; consisting of a single source file. +;;; (load ) +;;; If supplied and false, the unit isn't loaded unless it is needed +;;; to satisfy a require clause. Used for files containing compilation +;;; support stuff. +;;; (compile ) +;;; If supplied and false, the unit isn't compiled. Only useful for +;;; leaf nodes. Typically used in combination with (load '#f) to suppress +;;; compilation of stuff only used at compile time. + +(define-syntax (define-compilation-unit name . clauses) + `(begin + (let ((unit ,(compile.process-unit-spec name clauses '#t '()))) + (setf (compile.lookup-unit ',name) unit) + (setf compilation-units (append compilation-units (list unit)))) + ',name)) + + +;;; The basic approach is to turn the compilation unit definition into +;;; a big LET*, and put calls to build the actual unit object inside +;;; of this. +;;; + +(define (compile.process-unit-spec name clauses top-level? lexical-units) + (multiple-value-bind + (source-filename binary-filename require nested-units + load? compile?) + (compile.parse-unit-spec clauses lexical-units) + `(let* ((compile.source-filename ,source-filename) + (compile.binary-filename ,binary-filename) + (compile.unit-require (list ,@require)) + (compile.delayed-loads (append compile.delayed-loads + (compile.select-delayed-loads + compile.unit-require))) + ,@nested-units) + (make compile.unit + (name ',name) + (source-filename compile.source-filename) + (binary-filename compile.binary-filename) + (components (list ,@(map (function car) nested-units))) + (require compile.unit-require) + (top-level? ',top-level?) + (load? ,load?) + (compile? ,compile?) + (delayed-loads compile.delayed-loads))))) + +(define (compile.parse-unit-spec clauses lexical-units) + (let ((source-filename '#f) + (binary-filename '#f) + (require '#f) + (nested-units '()) + (load? ''#t) + (compile? ''#t)) + (dolist (c clauses) + (cond ((not (pair? c)) + (compile.unit-syntax-error c)) + ((eq? (car c) 'source-filename) + (if source-filename + (compile.unit-duplicate-error c) + (setf source-filename (cadr c)))) + ((eq? (car c) 'binary-filename) + (if binary-filename + (compile.unit-duplicate-error c) + (setf binary-filename (cadr c)))) + ((eq? (car c) 'require) + (if require + (compile.unit-duplicate-error c) + (setf require (mung-global-units (cdr c) lexical-units)))) + ((eq? (car c) 'unit) + (push (list (cadr c) + (compile.process-unit-spec (cadr c) (cddr c) + '#f lexical-units)) + nested-units) + (push (cadr c) lexical-units)) + ((eq? (car c) 'load) + (setf load? (cadr c))) + ((eq? (car c) 'compile) + (setf compile? (cadr c))) + (else + (compile.unit-syntax-error c)))) + (values + (if source-filename + `(compile.merge-filenames ,source-filename + compile.source-filename '#f) + 'compile.source-filename) + (if binary-filename + `(compile.merge-filenames ,binary-filename + compile.binary-filename '#f) + (if source-filename + '(compile.merge-filenames compile.binary-filename + compile.source-filename + compile.binary-subdir) + 'compile.binary-filename)) + (or require '()) + (nreverse nested-units) + load? + compile?))) + + +(predefine (error format . args)) + +(define (compile.unit-syntax-error c) + (error "Invalid compilation unit clause ~s." c)) + +(define (compile.unit-duplicate-error c) + (error "Duplicate compilation unit clause ~s." c)) + + + +;;;===================================================================== +;;; Representation and utilities +;;;===================================================================== + +;;; Here are constructors and accessors for unit objects. +;;; Implementationally, the compilation unit has the following slots: +;;; +;;; * The unit name. +;;; * The source file name. +;;; * The binary file name. +;;; * A list of component file/units. +;;; * A list of units/files to require. +;;; * A load timestamp. +;;; * A timestamp to keep track of the newest source file. +;;; * Flags for compile and load. + +(define-struct compile.unit + (predicate compile.unit?) + (slots + (name (type symbol)) + (source-filename (type string)) + (binary-filename (type string)) + (components (type list)) + (require (type list)) + (top-level? (type bool)) + (load? (type bool)) + (compile? (type bool)) + (delayed-loads (type list)) + (load-time (type (maybe integer)) (default '#f)) + (source-time (type (maybe integer)) (default '#f)) + (last-update (type (maybe integer)) (default 0)) + )) + +(define (compile.newer? t1 t2) + (and t1 + t2 + (> t1 t2))) + +(define (compile.select-newest t1 t2) + (if (compile.newer? t1 t2) t1 t2)) + +(define (compile.get-source-time u) + (let ((source-file (compile.unit-source-filename u))) + (if (file-exists? source-file) + (file-write-date source-file) + '#f))) + +(define (compile.get-binary-time u) + (let ((binary-file (compile.unit-binary-filename u))) + (if (file-exists? binary-file) + (file-write-date binary-file) + '#f))) + +(define (compile.load-source u) + (load (compile.unit-source-filename u)) + (setf (compile.unit-load-time u) (current-date))) + +(define (compile.load-binary u) + (load (compile.unit-binary-filename u)) + (setf (compile.unit-load-time u) (current-date))) + +(define (compile.compile-and-load u) + (let ((source-file (compile.unit-source-filename u)) + (binary-file (compile.unit-binary-filename u))) + (compile-file source-file binary-file) + (load binary-file) + (setf (compile.unit-load-time u) (current-date)))) + +(define (compile.do-nothing u) + u) + + +;;;===================================================================== +;;; Runtime support for define-compilation-unit +;;;===================================================================== + +(define (compile.select-delayed-loads require) + (let ((result '())) + (dolist (r require) + (if (not (compile.unit-load? r)) + (push r result))) + (nreverse result))) + +(define (compile.merge-filenames fname1 fname2 add-subdir) + (let ((place1 (filename-place fname1)) + (name1 (filename-name fname1)) + (type1 (filename-type fname1))) + (assemble-filename + (if (string=? place1 "") + (if add-subdir + (string-append (filename-place fname2) add-subdir) + fname2) + place1) + (if (string=? name1 "") fname2 name1) + (if (string=? type1 "") fname2 type1)))) + + + +;;;===================================================================== +;;; Load operation +;;;===================================================================== + +;;; Load-unit and compile-and-load-unit are almost identical. The only +;;; difference is that load-unit will load source files as necessary, while +;;; compile-and-load-unit will compile them and load binaries instead. + +(define (load-unit u) + (compile.update-unit-source-times u '#f (current-date)) + (compile.load-unit-aux u)) + +(define (compile.load-unit-aux u) + (with-compilation-unit () + (compile.load-unit-recursive u '#f))) + +(define (compile-and-load-unit u) + (compile.update-unit-source-times u '#f (current-date)) + (compile.compile-and-load-unit-aux u)) + +(define (compile.compile-and-load-unit-aux u) + (with-compilation-unit () + (compile.load-unit-recursive u '#t))) + + +;;; Load a bunch of compilation units as a group. This is useful because +;;; it can prevent repeated lookups of file timestamps. Basically, the +;;; assumption is that none of the source files will change while the loading +;;; is in progress. +;;; In case of an error, store the units left to be compiled in a global +;;; variable. + +(define remaining-units '()) + +(define (load-unit-list l) + (let ((timestamp (current-date))) + (dolist (u l) + (compile.update-unit-source-times u '#f timestamp)) + (setf remaining-units l) + (dolist (u l) + (compile.load-unit-aux u) + (pop remaining-units)))) + +(define (compile-and-load-unit-list l) + (let ((timestamp (current-date))) + (dolist (u l) + (compile.update-unit-source-times u '#f timestamp)) + (setf remaining-units l) + (dolist (u l) + (compile.compile-and-load-unit-aux u) + (pop remaining-units)))) + + +;;; Walk the compilation unit, updating the source timestamps. + +(define (compile.update-unit-source-times u newest-require timestamp) + (unless (eqv? timestamp (compile.unit-last-update u)) + (setf (compile.unit-last-update u) timestamp) + (dolist (r (compile.unit-require u)) + (if (compile.unit-top-level? r) + (compile.update-unit-source-times r '#f timestamp)) + (setf newest-require + (compile.select-newest newest-require + (compile.unit-source-time r)))) + (let ((components (compile.unit-components u))) + (if (not (null? components)) + (let ((source-time newest-require)) + (dolist (c components) + (compile.update-unit-source-times c newest-require timestamp) + (setf source-time + (compile.select-newest source-time + (compile.unit-source-time c)))) + (setf (compile.unit-source-time u) source-time)) + (setf (compile.unit-source-time u) + (compile.select-newest + newest-require + (compile.get-source-time u))))))) + + +;;; Load a compilation unit. Do this by first loading its require list, +;;; then by recursively loading each of its components, in sequence. +;;; Note that because of the way scoping of units works and the +;;; sequential nature of the load operation, only top-level +;;; units in the require list have to be loaded explicitly. + +(define (compile.load-unit-recursive u compile?) + (let ((components (compile.unit-components u))) + ;; First recursively load dependencies. + ;; No need to update time stamps again here. + (dolist (r (compile.unit-require u)) + (if (compile.unit-top-level? r) + (compile.load-unit-aux r))) + (if (not (null? components)) + ;; Now recursively load subunits. + (dolist (c components) + (unless (not (compile.unit-load? c)) + (compile.load-unit-recursive c compile?))) + ;; For a leaf node, load either source or binary if necessary. + (let ((source-time (compile.unit-source-time u)) + (binary-time (compile.get-binary-time u)) + (load-time (compile.unit-load-time u))) + (cond ((compile.newer? load-time source-time) + ;; The module has been loaded since it was last changed, + ;; but maybe we want to compile it now. + (if (and compile? + (compile.unit-compile? u) + (compile.newer? source-time binary-time)) + (begin + (compile.do-delayed-loads + (compile.unit-delayed-loads u) + compile?) + (compile.compile-and-load u)) + (compile.do-nothing u))) + ((compile.newer? binary-time source-time) + ;; The binary is up-to-date, so load it. + (compile.load-binary u)) + (else + ;; The binary is out-of-date, so either load source or + ;; recompile the binary. + (compile.do-delayed-loads + (compile.unit-delayed-loads u) + compile?) + (if (and compile? (compile.unit-compile? u)) + (compile.compile-and-load u) + (compile.load-source u))) + ))))) + + +(define (compile.do-delayed-loads units compile?) + (dolist (u units) + (compile.load-unit-recursive u compile?))) + + + + +;;;===================================================================== +;;; Extra stuff +;;;===================================================================== + + +;;; Reload a unit without testing to see if any of its dependencies are +;;; out of date. + +(define (reload-unit-source u) + (let ((components (compile.unit-components u))) + (if (not (null? components)) + (dolist (c components) + (reload-unit-source c)) + (compile.load-source u)))) + +(define (reload-unit-binary u) + (let ((components (compile.unit-components u))) + (if (not (null? components)) + (dolist (c components) + (reload-unit-binary c)) + (compile.load-binary u)))) + + +;;; Find a (not necessarily top-level) compilation unit with the given +;;; name. + +(define (find-unit name) + (compile.find-unit-aux name compilation-units)) + +(define (compile.find-unit-aux name units) + (block find-unit-aux + (dolist (u units '#f) + (if (eq? name (compile.unit-name u)) + (return-from find-unit-aux u) + (let* ((components (compile.unit-components u)) + (result (compile.find-unit-aux name components))) + (if result + (return-from find-unit-aux result))))))) + + +;;; Combine the two above: reload a compilation unit. + +(define-syntax (reload name) + `(reload-unit-source + (or (find-unit ',name) + (error "Couldn't find unit named ~s." ',name)))) diff --git a/support/format.scm b/support/format.scm new file mode 100644 index 0000000..c9dbb38 --- /dev/null +++ b/support/format.scm @@ -0,0 +1,683 @@ +;;; format.scm -- format function for Scheme +;;; +;;; author : Sandra Loosemore +;;; date : 29 Oct 1991 +;;; +;;; +;;; This code is adapted from the XP pretty printer originally written +;;; in Common Lisp by Dick Waters. Here is the copyright notice attached +;;; to the original XP source file: +;;; +;;;------------------------------------------------------------------------ +;;; +;;; Copyright 1989,1990 by the Massachusetts Institute of Technology, +;;; Cambridge, Massachusetts. +;;; +;;; Permission to use, copy, modify, and distribute this software and its +;;; documentation for any purpose and without fee is hereby granted, +;;; provided that this copyright and permission notice appear in all +;;; copies and supporting documentation, and that the name of M.I.T. not +;;; be used in advertising or publicity pertaining to distribution of the +;;; software without specific, written prior permission. M.I.T. makes no +;;; representations about the suitability of this software for any +;;; purpose. It is provided "as is" without express or implied warranty. +;;; +;;; M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +;;; ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +;;; M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +;;; ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +;;; WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +;;; ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +;;; SOFTWARE. +;;; +;;;------------------------------------------------------------------------ +;;; + + +;;; The stream argument can be #f, in which case a string is returned. +;;; If the stream is #t, (current-output-port) is used. +;;; We compile a string argument into a function and call the function. +;;; The only exception is if the string doesn't contain any ~ escapes; +;;; then we can treat it as a literal and just write it to the stream. + +(define (format stream string-or-fn . args) + (cond ((not stream) + (call-with-output-string + (lambda (stream) + (apply (function format) stream string-or-fn args)))) + (else + (if (eq? stream '#t) + (setf stream (current-output-port))) + (when (string? string-or-fn) + (setf string-or-fn (xp.process-format-string string-or-fn))) + (if (string? string-or-fn) + (write-string string-or-fn stream) + (xp.maybe-initiate-xp-printing string-or-fn stream args)) + '#f))) + +(define xp.format-string-cache (make-table)) + +(define (xp.process-format-string string-or-fn) + (cond ((not (string? string-or-fn)) string-or-fn) + ((not xp.format-string-cache) + (xp.maybe-compile-format-string string-or-fn)) + (else + (when (not (table? xp.format-string-cache)) + (setf xp.format-string-cache (make-table))) + (let ((value + (table-entry xp.format-string-cache string-or-fn))) + (when (not value) + (setf value (xp.maybe-compile-format-string string-or-fn)) + (setf (table-entry xp.format-string-cache string-or-fn) + value)) + value)))) + + +(define (xp.maybe-compile-format-string string) + (let ((length (string-length string))) + (or (xp.simple-format-string? string 0 length) + (let ((fn (xp.parse-format-string string 0 length))) + (lambda (xp args) + (funcall fn xp args args)))))) + + +;;; Try to detect format strings without fancy directives, that can be +;;; written with a call to write-string. +;;; Can do simple transformations e.g. ~% => newline, ~~ => ~, etc. + +(define (xp.simple-format-string? s start end) + (let ((twiddle (string-position #\~ s start end))) + (if (not twiddle) + (if (eqv? start 0) + s + (substring s start end)) + (let ((char (string-ref s (1+ twiddle)))) + (cond ((eqv? char #\%) + (let ((tail (xp.simple-format-string? s (+ twiddle 2) end))) + (if tail + (string-append (substring s start twiddle) + (string #\newline) + tail) + '#f))) + ((eqv? char #\~) + (let ((tail (xp.simple-format-string? s (+ twiddle 2) end))) + (if tail + (string-append (substring s start (1+ twiddle)) + tail) + '#f))) + ((eqv? char #\newline) + (let ((tail (xp.simple-format-string? + s + (xp.skip-whitespace s (+ twiddle 2) end) + end))) + (if tail + (string-append (substring s start twiddle) + tail) + '#f))) + (else + '#f)))))) + +(define (warning string-or-fn . args) + (internal-warning (apply (function format) '#f string-or-fn args))) + +(define (error string-or-fn . args) + (internal-error (apply (function format) '#f string-or-fn args))) + + +;;;===================================================================== +;;; Compiled format +;;;===================================================================== + +;;; Note that compiled format strings always print through xp streams even if +;;; they don't have any xp directives in them. As a result, the compiled code +;;; can depend on the fact that the stream being operated on is an xp +;;; stream not an ordinary one. + + +;;; Parse a format string, returning a function to do the printing. +;;; The function is called with three arguments +;;; * the xp stream +;;; * the original argument list +;;; * the argument list tail +;;; It should return the list of leftover, unprocessed arguments. + +(define (xp.parse-format-string string start end) + (cond ((eqv? start end) + (function xp.format-finish)) + ((eqv? (string-ref string start) #\~) + (xp.parse-format-string-dispatch string start end)) + (else + (let* ((next (or (string-position #\~ string start end) end)) + (literal (substring string start next)) + (count (- next start)) + (continue (xp.parse-format-string string next end)) + (newline? (string-position #\newline literal 0 count))) + (if newline? + (lambda (xp args tail) + (xp.write-string+ literal xp 0 count) + (funcall continue xp args tail)) + (lambda (xp args tail) + (xp.write-string++ literal xp 0 count) + (funcall continue xp args tail))))) + )) + +(define (xp.format-finish xp args tail) + (declare (ignore xp args)) + tail) + + +;;; Functions for handling individual format specifiers are installed +;;; in this table. They are called with these arguments: +;;; * the format string +;;; * the index of the next character +;;; * the index of the end of the format string +;;; * the list of parameters for the format specification +;;; * a boolean indicating whether the colon modifier was present +;;; * a boolean indicating whether the atsign modifier was present +;;; The handler is responsible for calling xp.parse-format-string to parse +;;; the rest of the format string, and returning a function. (This has +;;; to be done by the individual handlers because some of them need to +;;; scan the format string for matching delimiters, etc.) + +;;; *** This probably isn't right, we assume characters can be compared +;;; *** with EQ? and used as table keys. + +(define xp.fn-table (make-table)) + +(define (define-format char function) + (setf (table-entry xp.fn-table (char-upcase char)) function) + (setf (table-entry xp.fn-table (char-downcase char)) function)) + +;;; Parse a ~ sequence from the format string and dispatch to the +;;; appropriate handler. + +(define (xp.parse-format-string-dispatch string start end) + (multiple-value-bind (next params colon? atsign? char) + (xp.parse-format-descriptor string start end) + (let ((fn (table-entry xp.fn-table char))) + (if fn + (funcall fn string next end params colon? atsign?) + (error "Unrecognized format escape ~~~a." char))))) + +(define (xp.parse-format-descriptor string start end) + (multiple-value-bind (params start) + (xp.parse-format-parameters string start end) + (let ((colon? '#f) + (atsign? '#f) + (char '#f)) + (block parse-format-descriptor + (do () + ((xp.check-for-incomplete-format-string string start end)) + (setf char (string-ref string start)) + (incf start) + (cond ((eqv? char #\:) + (setf colon? '#t)) + ((eqv? char #\@) + (setf atsign? '#t)) + (else + (return-from parse-format-descriptor + (values start params colon? atsign? char))) + )))))) + + +;;; *** The stuff for V and # format parameters is disabled because +;;; *** it makes the handler functions hairier. It's rarely used anyway, +;;; *** and you can get the same effect by consing up a format string +;;; *** on the fly if you really need to. + +(define (xp.parse-format-parameters string start end) + (let ((params '()) + (char '#f)) + (incf start) ; skip ~ + (block parse-format-parameters + (do () + ((xp.check-for-incomplete-format-string string start end)) + (setf char (string-ref string start)) + (cond ((char-numeric? char) + (multiple-value-bind (next value) + (xp.parse-format-number string start end 0) + (setf start next) + (push value params))) + ((eqv? char #\') + (push (string-ref string (1+ start)) params) + (setf start (+ start 2))) + ((or (eqv? char #\v) (eqv? char #\V)) + (error "V format parameter not supported.") ;*** + (push 'value params) + (setf start (+ start 1))) + ((eqv? char #\#) + (error "# format parameter not supported.") ;*** + (push 'count params) + (setf start (+ start 1))) + ((eqv? char #\,) + (push '#f params)) + (else + (return-from parse-format-parameters + (values (nreverse params) start)))) + (if (eqv? (string-ref string start) #\,) + (incf start)))))) + +(define (xp.parse-format-number string start end value) + (xp.check-for-incomplete-format-string string start end) + (let* ((char (string-ref string start)) + (weight (string-position char "0123456789" 0 10))) + (if weight + (xp.parse-format-number string (1+ start) end (+ (* value 10) weight)) + (values start value)))) + +(define (xp.check-for-incomplete-format-string string start end) + (if (eqv? start end) + (error "Incomplete format string ~s." string) + '#f)) + + +;;; *** All of these format handlers probably ought to do more checking +;;; *** for the right number of parameters and not having colon? and +;;; *** atsign? supplied when they are not allowed. + +;;; ~A and ~S are the basic format directives. + +(define (xp.format-a string start end params colon? atsign?) + (xp.format-a-s-helper string start end params colon? atsign? '#f)) +(define-format #\a (function xp.format-a)) + +(define (xp.format-s string start end params colon? atsign?) + (xp.format-a-s-helper string start end params colon? atsign? '#t)) +(define-format #\s (function xp.format-s)) + +(define (xp.format-a-s-helper string start end params colon? atsign? escape?) + (declare (ignore colon? atsign?)) ;*** + (let ((continuation (xp.parse-format-string string start end))) + (if (null? params) + ;; Do the simple, common case. + (lambda (xp args tail) + (dynamic-let ((*print-escape* escape?)) + (xp.write+ (car tail) xp)) + (funcall continuation xp args (cdr tail))) + ;; Do the hard case. + (let* ((mincol (or (and (not (null? params)) (pop params)) 0)) + (colinc (or (and (not (null? params)) (pop params)) 1)) + (minpad (or (and (not (null? params)) (pop params)) 0)) + (padchar (or (and (not (null? params)) (pop params)) #\space))) + (declare (ignore mincol colinc minpad padchar)) ;*** +;;; *** I'm confused. It seems like we have to print this to a string +;;; *** and then write the string to the XP stream along with the padding +;;; *** But won't switching to a new stream mess up circularity detection, +;;; *** indentation, etc? + (error "Unimplemented format option ~s!" string)) + ))) + + +;;; ~W -> write + +(define (xp.format-w string start end params colon? atsign?) + (declare (ignore params)) + (let ((continuation (xp.parse-format-string string start end))) + (cond ((and (not colon?) (not atsign?)) + (lambda (xp args tail) + (xp.write+ (car tail) xp) + (funcall continuation xp args (cdr tail)))) + ((and colon? (not atsign?)) + (lambda (xp args tail) + (dynamic-let ((*print-pretty* '#t)) + (xp.write+ (car tail) xp)) + (funcall continuation xp args (cdr tail)))) + ((and (not colon?) atsign?) + (lambda (xp args tail) + (dynamic-let ((*print-level* '#f) + (*print-length* '#f)) + (xp.write+ (car tail) xp)) + (funcall continuation xp args (cdr tail)))) + ((and colon? atsign?) + (lambda (xp args tail) + (dynamic-let ((*print-level* '#f) + (*print-length* '#f) + (*print-pretty* '#t)) + (xp.write+ (car tail) xp)) + (funcall continuation xp args (cdr tail)))) + ))) +(define-format #\w (function xp.format-w)) + + +;;; Here are the directives for printing integers, ~D and friends. + +(define (xp.format-d string start end params colon? atsign?) + (xp.format-d-b-o-x-helper string start end params colon? atsign? 10)) +(define-format #\d (function xp.format-d)) + +(define (xp.format-b string start end params colon? atsign?) + (xp.format-d-b-o-x-helper string start end params colon? atsign? 2)) +(define-format #\b (function xp.format-b)) + +(define (xp.format-o string start end params colon? atsign?) + (xp.format-d-b-o-x-helper string start end params colon? atsign? 8)) +(define-format #\o (function xp.format-o)) + +(define (xp.format-x string start end params colon? atsign?) + (xp.format-d-b-o-x-helper string start end params colon? atsign? 16)) +(define-format #\x (function xp.format-x)) + +(define (xp.format-d-b-o-x-helper string start end params colon? atsign? radix) + (let ((continuation (xp.parse-format-string string start end))) + (if (and (null? params) (not colon?) (not atsign?)) + ;; Do the simple, common case. + (lambda (xp args tail) + (dynamic-let ((*print-escape* '#f) + (*print-radix* '#f) + (*print-base* radix)) + (xp.write+ (car tail) xp)) + (funcall continuation xp args (cdr tail))) + ;; Do the hard case. + (let* ((mincol (or (and (not (null? params)) (pop params)) 0)) + (padchar (or (and (not (null? params)) (pop params)) #\space)) + (commachar (or (and (not (null? params)) (pop params)) #\,)) + (commaint (or (and (not (null? params)) (pop params)) 3))) + (declare (ignore mincol padchar commachar commaint)) ;*** + ;; *** I'm too lazy to do this right now. + (error "Unimplemented format option ~s!" string))))) + + +(define (xp.format-r string start end params colon? atsign?) + (if (not (null? params)) + (xp.format-d-b-o-x-helper string start end (cdr params) + colon? atsign? (car params)) + ;; *** The colon? and atsign? modifiers do weird things like + ;; *** printing roman numerals. I'm too lazy to do this until/unless + ;; *** we have a real need for it. + (error "Unimplemented format option ~s!" string))) +(define-format #\r (function xp.format-r)) + + +;;; ~P -> plurals + +(define (xp.format-p string start end params colon? atsign?) + (declare (ignore params)) + (let ((continuation (xp.parse-format-string string start end))) + (cond ((and (not colon?) (not atsign?)) + (lambda (xp args tail) + (if (not (eqv? (car tail) 1)) + (xp.write-char++ #\s xp)) + (funcall continuation xp args (cdr tail)))) + ((and colon? (not atsign?)) + (lambda (xp args tail) + (setf tail (xp.back-up 1 args tail)) + (if (not (eqv? (car tail) 1)) + (xp.write-char++ #\s xp)) + (funcall continuation xp args (cdr tail)))) + ((and (not colon?) atsign?) + (lambda (xp args tail) + (if (eqv? (car tail) 1) + (xp.write-char++ #\y xp) + (begin + (xp.write-char++ #\i xp) + (xp.write-char++ #\e xp) + (xp.write-char++ #\s xp))) + (funcall continuation xp args (cdr tail)))) + ((and colon? atsign?) + (lambda (xp args tail) + (setf tail (xp.back-up 1 args tail)) + (if (eqv? (car tail) 1) + (xp.write-char++ #\y xp) + (begin + (xp.write-char++ #\i xp) + (xp.write-char++ #\e xp) + (xp.write-char++ #\s xp))) + (funcall continuation xp args (cdr tail))))))) +(define-format #\p (function xp.format-p)) + + +;;; ~C -> character + +(define (xp.format-c string start end params colon? atsign?) + (declare (ignore params)) + (let ((continuation (xp.parse-format-string string start end))) + (cond ((and (not colon?) (not atsign?)) + (lambda (xp args tail) + (xp.write-char++ (car tail) xp) + (funcall continuation xp args (cdr tail)))) + ((and (not colon?) atsign?) + (lambda (xp args tail) + (dynamic-let ((*print-escape* '#t)) + (xp.write+ (car tail) xp) + (funcall continuation xp args (cdr tail))))) + (else + ;; *** I don't know how to get at the character names. + (error "Unimplemented format option ~s!" string))))) +(define-format #\c (function xp.format-c)) + + + +;;; Newline directives, ~% and ~& + +(define (xp.format-percent string start end params colon? atsign?) + (xp.format-newline-helper string start end params colon? atsign? + 'unconditional)) +(define-format #\% (function xp.format-percent)) + +(define (xp.format-ampersand string start end params colon? atsign?) + (xp.format-newline-helper string start end params colon? atsign? + 'fresh)) +(define-format #\& (function xp.format-ampersand)) + +(define (xp.format-newline-helper string start end params colon? atsign? kind) + (declare (ignore colon? atsign?)) + (let ((continuation (xp.parse-format-string string start end)) + (n (or (and (not (null? params)) (pop params)) 1))) + (if (eqv? n 1) + (lambda (xp args tail) + (xp.pprint-newline+ kind xp) + (funcall continuation xp args tail)) + (lambda (xp args tail) + (xp.pprint-newline+ kind xp) + (dotimes (i (1- n)) + (xp.pprint-newline+ 'unconditional xp)) + (funcall continuation xp args tail)) + ))) + + +;;; ~_, Conditional newline + +(define (xp.format-underbar string start end params colon? atsign?) + (declare (ignore params)) + (let ((continuation (xp.parse-format-string string start end)) + (kind (if colon? + (if atsign? 'mandatory 'fill) + (if atsign? 'miser 'linear)))) + (lambda (xp args tail) + (xp.pprint-newline+ kind xp) + (funcall continuation xp args tail)))) +(define-format #\_ (function xp.format-underbar)) + + +;;; Random character printing directives, ~| and ~~ + +;;; *** commented out because #\page is not standard scheme +; (define (xp.format-bar string start end params colon? atsign?) +; (xp.format-char-helper string start end params colon? atsign? #\page)) +; (define-format #\| (function xp.format-bar)) + +(define (xp.format-twiddle string start end params colon? atsign?) + (xp.format-char-helper string start end params colon? atsign? #\~)) +(define-format #\~ (function xp.format-twiddle)) + +(define (xp.format-char-helper string start end params colon? atsign? char) + (declare (ignore colon? atsign?)) + (let ((continuation (xp.parse-format-string string start end)) + (n (or (and (not (null? params)) (pop params)) 1))) + (if (eqv? n 1) + (lambda (xp args tail) + (xp.write-char++ char xp) + (funcall continuation xp args tail)) + (lambda (xp args tail) + (dotimes (i n) + (xp.write-char++ char xp)) + (funcall continuation xp args tail))))) + + + +;;; ~ directive (ignore whitespace in format string) + +(define (xp.format-newline string start end params colon? atsign?) + (declare (ignore params)) + (let ((newline? '#f) + (skip? '#f)) + (cond ((and (not colon?) (not atsign?)) ; skip both newline and whitespace + (setf skip? '#t)) + ((and colon? (not atsign?))) ; skip newline, leave whitespace + ((and (not colon?) atsign?) ; do newline, skip whitespace + (setf newline? '#t) + (setf skip? '#t)) + (else + (error "~:@ not allowed."))) + (if skip? + (setf start (xp.skip-whitespace string start end))) + (let ((continuation (xp.parse-format-string string start end))) + (if newline? + (lambda (xp args tail) + (xp.pprint-newline+ 'unconditional xp) + (funcall continuation xp args tail)) + continuation)))) +(define-format #\newline (function xp.format-newline)) + +(define (xp.skip-whitespace string start end) + (if (eqv? start end) + start + (let ((char (string-ref string start))) + (if (and (char-whitespace? char) + (not (eqv? char #\newline))) + (xp.skip-whitespace string (1+ start) end) + start)))) + + + +;;; ~T -> tab + +(define (xp.format-t string start end params colon? atsign?) + (let* ((continuation (xp.parse-format-string string start end)) + (colnum (or (and (not (null? params)) (pop params)) 1)) + (colinc (or (and (not (null? params)) (pop params)) 1)) + (kind (if colon? + (if atsign? 'section-relative 'section) + (if atsign? 'line-relative 'line)))) + (lambda (xp args tail) + (xp.pprint-tab+ kind colnum colinc xp) + (funcall continuation xp args tail)))) +(define-format #\t (function xp.format-t)) + + +;;; ~I -> indent + +(define (xp.format-i string start end params colon? atsign?) + (declare (ignore atsign?)) + (let ((continuation (xp.parse-format-string string start end)) + (kind (if colon? 'current 'block)) + (n (or (and (not (null? params)) (pop params)) 0))) + (lambda (xp args tail) + (pprint-indent kind n) + (funcall continuation xp args tail)))) +(define-format #\i (function xp.format-i)) + + +;;; ~* -> skip or back up over arguments + +(define (xp.format-star string start end params colon? atsign?) + (let ((continuation (xp.parse-format-string string start end)) + (n (or (and (not (null? params)) (pop params)) 1))) + (cond ((and (not colon?) (not atsign?)) + (lambda (xp args tail) + (funcall continuation xp args (list-tail tail n)))) + ((and colon? (not atsign?)) + (lambda (xp args tail) + (funcall continuation xp args (xp.back-up n args tail)))) + ((and (not colon?) atsign?) + (lambda (xp args tail) + (declare (ignore tail)) + (funcall continuation xp args (list-tail args n)))) + (else + (error "~:@* not allowed."))))) +(define-format #\* (function xp.format-star)) + +(define (xp.back-up n head tail) + (if (eq? (list-tail head n) tail) + head + (xp.back-up n (cdr head) tail))) + + +;;; ~? -> indirection +;;; Normally uses two arguments, a string and a list. +;;; With @, only uses a string, takes arguments from the tail. + +(define (xp.format-question string start end params colon? atsign?) + (declare (ignore params colon?)) + (let ((continuation (xp.parse-format-string string start end))) + (if atsign? + (lambda (xp args tail) + (setf tail (apply (function format) xp (car tail) (cdr tail))) + (funcall continuation xp args tail)) + (lambda (xp args tail) + (apply (function format) xp (car tail) (cadr tail)) + (funcall continuation xp args (cddr tail)))))) +(define-format #\? (function xp.format-question)) + + +;;; ~(...~) -> case conversion. + +(define *xp.format-paren-next* '#f) + +(define (xp.format-paren string start end params colon? atsign?) + (declare (ignore params)) + (let* ((handler (dynamic-let ((*xp.format-paren-next* '#t)) + (let ((result (xp.parse-format-string + string start end))) + (if (eq? (dynamic *xp.format-paren-next*) '#t) + (error "~( directive has no matching ~).")) + (setf start (dynamic *xp.format-paren-next*)) + result))) + (continuation (xp.parse-format-string string start end)) + (mode (if colon? + (if atsign? 'up 'cap1) + (if atsign? 'cap0 'down)))) + (lambda (xp args tail) + (xp.push-char-mode xp mode) + (setf tail (funcall handler xp args tail)) + (xp.pop-char-mode xp) + (funcall continuation xp args tail)))) +(define-format #\( (function xp.format-paren)) + +(define (xp.format-paren-end string start end params colon? atsign?) + (declare (ignore string end params colon? atsign?)) + (if (not (dynamic *xp.format-paren-next*)) + (error "~) directive has no matching ~(.")) + (setf (dynamic *xp.format-paren-next*) start) + (function xp.format-finish)) +(define-format #\) (function xp.format-paren-end)) + +;;; ~F -> fixed-width *** unimplemented +;;; ~E -> e-notation *** unimplemented +;;; ~G -> general float *** unimplemented +;;; ~$ -> dollars float *** unimplemented +;;; ~[...~] -> conditional *** unimplemented +;;; ~{...~} -> iteration *** unimplemented +;;; ~<...~> -> justification *** unimplemented +;;; ~; -> clause seperator *** unimplemented +;;; ~^ -> up and out *** unimplemented +;;; ~/.../ -> hook *** unimplemented + +(define (xp.unimplemented-format string start end params colon? atsign?) + (declare (ignore start end params colon? atsign?)) + (error "Unimplemented format directive in ~s." string)) + +(define-format #\f (function xp.unimplemented-format)) +(define-format #\e (function xp.unimplemented-format)) +(define-format #\g (function xp.unimplemented-format)) +(define-format #\$ (function xp.unimplemented-format)) +(define-format #\[ (function xp.unimplemented-format)) +(define-format #\] (function xp.unimplemented-format)) +(define-format #\{ (function xp.unimplemented-format)) +(define-format #\} (function xp.unimplemented-format)) +(define-format #\< (function xp.unimplemented-format)) +(define-format #\> (function xp.unimplemented-format)) +(define-format #\; (function xp.unimplemented-format)) +(define-format #\^ (function xp.unimplemented-format)) +(define-format #\/ (function xp.unimplemented-format)) + diff --git a/support/mumble.txt b/support/mumble.txt new file mode 100644 index 0000000..0ca2f40 --- /dev/null +++ b/support/mumble.txt @@ -0,0 +1,840 @@ +Syntax +------ + +(quote x) + +(function name) + You must use this to reference a global function, as in CL. (There + isn't a local function namespace.) + +(lambda lambda-list . body) + Equivalent to #'(lambda ...) in Common Lisp. + The lambda-list can be dotted, as in Scheme. CL lambda-list keywords + are not supported. + +function call + Order of evaluation is unspecified, as in Scheme. + You have to use FUNCALL if the function is bound with let. + +(funcall function . args) + As in Common Lisp, but might be a macro. (The function is guaranteed + to be a true function, not a symbol.) + +(apply procedure . args) + As in Common Lisp/Scheme. + +(map procedure . lists) + As in Scheme. Equivalent to MAPCAR in CL. + +(for-each procedure . lists) + As in Scheme. Equivalent to MAPC in CL. + +(every procedure . lists) +(some procedure . lists) +(notany procedure . lists) +(notevery procedure . lists) + As in CL, but only work on lists. + +(procedure? object) + As in Scheme, but can return an arbitrary truth value instead of just #t. + Note that we never use symbols or quoted lambda expressions as functions. + +(if test then . maybe-else) +(when test . body) +(unless test . body) + +(cond . tests) + As in Scheme, but the = syntax isn't supported. When no test is true, the + result is undefined. + +(case value . cases) + As in Scheme. + Stylistically, use this only when the case labels are symbols. + +(and . expressions) +(or . expressions) + +(not value) + As in Scheme but can return an arbitrary truth value instead of #t. + +(set! variable value) + As in Scheme; this doesn't return a useful value. Use setf instead. + +(setf place value) + Similar to SETF in Common Lisp. Returns value. + See define-setf below. Places that are macro calls are expanded + if they don't have their own setter. + Here is a list of the built-in setters: + dynamic + car + cdr + list-ref + string-ref + vector-ref + table-entry + +(let bindings . body) +(let* bindings . body) +(letrec bindings . body) + Note that each binding clause must be a list of the form (var init); + you can't just supply var or (var) as in Common Lisp. Also remember + that the order of evaluation for the init-forms is not specified for + let/letrec. + The Scheme named LET construct is not supported. + +(flet bindings . body) +(labels bindings . body) + As in Common Lisp. + +(dynamic-let bindings . body) +(dynamic name) + As in Eulisp. Dynamic-let is equivalent to bind in T, or LET in + Common Lisp with all of the variables declared special. As a matter + of style, use dynamic to reference the value rather than just the name. + +(begin . body) + Like PROGN in Common Lisp. + +(block name . body) +(return-from name result) + The intersection of the Eulisp and Common Lisp definitions. The "name" + may be bound as a lexical variable, but you should only refer to it + inside a return-from. + Don't depend on named functions (etc) establishing implicit blocks, + as they do in CL. + +(do bindings-and-steppers (end-test . results) . body) + As in Scheme. It doesn't necessarily establish an implicit BLOCK + as in CL so you can't RETURN from the loop. + +(dolist (variable init . maybe-result) . body) +(dotimes (variable init . maybe-result) . body) + As in CL, except you can't RETURN from the loop. + +(values . values) +(multiple-value-bind variables values-expression . body) + As in Common Lisp, except that the values-expression must explicitly + return multiple values. + +(let/cc variable . body) + As in EuLisp. This is the same as catch in T. The continuation + has dynamic extent within the body. + You call the continuation with an arbitrary number of arguments, which + are the multiple values to be returned. + +(unwind-protect protected-form . body) + +(declare ...) + Similar to Common Lisp declare. Declarations are allowed only in the + standard places that Common Lisp permits (in particular, at the + beginning of binding forms). For now, only the following declarations + are permitted: + + (ignore . variables) + (ignorable . variables) + (type type-spec . variables) -- see info on type-specs below. + + + + +Definitions +----------- + +(define pattern . value) + As in Scheme. + +(define-integrable pattern . value) + Like DEFINE, but also tells the compiler to try to inline the value. + +(define-syntax (name . lambda-list) . body) + Similar to the equivalent T functionality. The lambda-list does not + support destructuring, as does Common Lisp's DEFMACRO. + The macro definition is made both when the file is loaded and when it + is compiled. + +(define-local-syntax (name . lambda-list) . body) + Again, similar to the T functionality. In Common Lisp, equivalent to + a DEFMACRO wrapped in (eval-when (compile) ...). + +(define-setf getter-name setter-name) + Similar to the short form of DEFSETF in Common Lisp, except that the + calling convention for the setter differs: the value is passed as the + first argument rather than as the last. The setter must return this + value. + +(predefine pattern) + This is a forward definition for a function or variable. It doesn't + actually make a definition; its purpose is to try to get rid of compiler + warnings about calls to functions that haven't been defined yet. It can + be a no-op if the underlying Lisp system doesn't provide any way to do + this. + +(redefine pattern . value) + Like DEFINE, but hints to the compiler not to complain if this + function/variable was previously defined somewhere else. + +(redefine-syntax (name . lambda-list) . body) + Like DEFINE-SYNTAX, but hints to the compiler not to complain if this + macro was previously defined somewhere else. + + +Equivalence +----------- + +(eq? x1 x2) +(eqv? x1 x2) +(equal? x1 x2) + As in Scheme but can return an arbitrary truth value instead of #t. + Note that equal? is not the same as EQUAL in CL because it descends vectors. + eqv? is different from the T equiv? because it doesn't descent strings. + + +Lists +----- + +(pair? x) + As in Scheme but can return an arbitrary truth value instead of #t. + +(cons x y) +(list . values) +(make-list length . maybe-init) + +(cxxxxr x) + +(null? x) +(list? x) + As in Scheme but can return an arbitrary truth value instead of #t. + Note that this is a check for a proper (null-terminated) list, not + like LISTP in CL. + +(length x) +(append list . more-lists) +(nconc list . more-lists) + +(reverse x) +(nreverse x) + +(list-tail list n) + Like NTHCDR in Common Lisp. + +(list-ref list n) + Like NTH in Common Lisp. + +(last list) +(butlast list) + As in Common Lisp. + +(memq object list) +(memv object list) +(member object list) + +(assq object list) +(assv object list) +(assoc object list) + +(push item place) +(pop place) + As in Common Lisp. + +(list-copy list) + + +Symbols +------- + +(symbol? object) +(symbol->string object) +(string->symbol object) +(gensym . maybe-prefix) +(gensym? object) + +(symbol-append . symbols) + + +Characters +---------- + +(char? object) + As in Scheme, but can return an arbitrary truth value instead of just #t. + +(char=? c1 c2) +(char? c1 c2) +(char<=? c1 c2) +(char>=? c1 c2) + As in Scheme, except that they can return an arbitrary truth value + instead of just #t. + +(char-ci=? c1 c2) +(char-ci? c1 c2) +(char-ci<=? c1 c2) +(char-ci>=? c1 c2) + As in Scheme, except that they can return an arbitrary truth value + instead of just #t. + +(char-alphabetic? c) +(char-numeric? c) +(char-whitespace? c) +(char-upper-case? c) +(char-lower-case? c) + +(char->integer c) +(integer->char n) + +(char-upcase c) +(char-downcase c) + +(char-name c) + As in Common Lisp. + +(char->digit c . maybe-radix) + Returns nil or the "weight" of the character as a fixnum in the given + radix (defaults to 10). + + +Strings +------- + +(string? object) + As in Scheme, but can return an arbitrary truth value instead of just #t. + +(make-string length . maybe-init) + +(string char . more-chars) + +(string-length string) +(string-ref string index) + +(string=? s1 s2) +(string? s1 s2) +(string<=? s1 s2) +(string>=? s1 s2) + As in Scheme, but can return an arbitrary truth value instead of just #t. + +(string-ci=? s1 s2) +(string-ci? s1 s2) +(string-ci<=? s1 s2) +(string-ci>=? s1 s2) + As in Scheme, but can return an arbitrary truth value instead of just #t. + +(substring string start end) +(string-append string . more-strings) + +(string->list string) +(list->string list) + +(string-copy string) + +(string-upcase string) +(string-downcase string) + + +Vectors +------- + +(vector? object) + As in Scheme, but can return an arbitrary truth value instead of just #t. + +(make-vector length . maybe-init) +(vector object . more-objects) + +(vector-length vector) +(vector-ref vector index) +(vector->list vector) +(list->vector list) + +(vector-copy vector) + + +Numbers +------- + +(number? object) + As in Scheme, but can return an arbitrary truth value instead of just #t. + +(integer? object) +(rational? object) +(float? object) + These test the representation of a number, not its mathematical + properties. They're equivalent to the CL integerp, rationalp, and floatp + predicates. We ignore complex numbers for now. + +(exact->inexact number) + Convert an exact-rational to a float. + +(= x1 x2) +(< x1 x2) +(> x1 x2) +(<= x1 x2) +(>= x1 x2) + As in Scheme, except they can return an arbitrary truth value. + They're restricted to being binary operators because that's all + that's supported in T. + +(zero? x) +(positive? x) +(negative? x) + As in Scheme, except they can return an arbitrary truth value. + +(min number . more-numbers) +(max number . more-numbers) + +(+ . numbers) +(* . numbers) +(- n1 . more-numbers) +(/ n1 . more-numbers) + As in Scheme. + +(quotient n1 n2) +(remainder n1 n2) +(modulo n1 n2) + quotient rounds towards zero. + remainder has the sign of the second argument, modulo has the sign of + the first argument. + +(floor x) +(ceiling x) +(truncate x) +(round x) + As in Scheme. These return a number of the same type as the argument. + +(floor->exact x) +(ceiling->exact x) +(truncate->exact x) +(round->exact x) + Like the above, but return an exact-integer result. Borrowed from + MIT Scheme. + +(1+ n) +(1- n) +(incf place . maybe-delta) +(decf place . maybe-delta) + As in Common Lisp. + +(number->string number . maybe-radix) +(string->number string . maybe-radix) + As in Scheme. + +(expt base power) + As in Common Lisp. [our only use is when both args are integers] + + +Tables +------ + +(table? object) +(make-table) +(table-entry table key) +(table-for-each proc table) +(copy-table table) + More or less as in T. For now we only bother with tables that use + eq? as the comparison function -- mostly symbols are used as keys. + + +I/O +--- + +(call-with-input-file string proc) +(call-with-output-file string proc) + As in Scheme. The proc is called with one argument, the port. + +(call-with-input-string string proc) +(call-with-output-string proc) + Similar, but for reading/writing to a string stream string. + Call-with-output-string returns the string. + +(input-port? object) +(output-port? object) + As in Scheme, but can return an arbitrary truth value. + +(current-input-port) +(current-output-port) + +(open-input-file filename) +(open-output-file filename) + +(close-input-port port) +(close-output-port port) + +(read . maybe-port) +(read-char . maybe-port) +(peek-char . maybe-port) +(read-line . maybe-port) + +(eof-object? object) + + +Printer +------- + +(internal-write object port) +(internal-output-width port) +(internal-output-position port) +(internal-write-char char port) +(internal-write-string string port start end) +(internal-newline port) +(internal-fresh-line port) +(internal-finish-output port) +(internal-force-output port) +(internal-clear-output port) +(internal-write-to-string object) +(internal-warning string) +(internal-error string) + These are all internal hooks. Don't use them directly if you can + avoid it. + +(write object . maybe-stream) +(print object . maybe-stream) +(prin1 object . maybe-stream) +(princ object . maybe-stream) +(pprint object . maybe-stream) +(prin1-to-string object) +(princ-to-string object) +(write-char char . maybe-stream) +(write-string string . maybe-stream-start-end) +(write-line string . maybe-stream-start-end) +(terpri . maybe-stream) +(fresh-line . maybe-stream) +(finish-output . maybe-stream) +(force-output . maybe-stream) +(clear-output . maybe-stream) + These are the standard Common Lisp print functions. All of them + accept either a port or an XP stream as a stream argument. + +(display object . maybe-stream) + Same as princ; for Scheme compatibility. +(newline object . maybe-stream) + Same as terpri; for Scheme compatibility. + + +*print-escape* +*print-shared* +*print-circle* +*print-pretty* +*print-level* +*print-length* + These are the standard Common Lisp printer control variables. The + functions listed above obey them. + +*print-base* +*print-radix* +*print-case* +*print-readably* + These are more standard Common Lisp printer control variables, but + support for them hasn't been implemented yet. Maybe some day. + +*print-dispatch* + This is the hook for user customization of the printer. Its value is a + function that is passed an object as an argument, and returns another + function that takes a stream and the object as arguments. + +*print-structure* + If true, use standard structure printing syntax (overriding any special + print function for the structure type). + +*print-structure-slots* + If true, recursively print structure slots when using standard structure + printing syntax; otherwise just print the structure type name. + + +(standard-print-dispatch object) + This function is the initial value of *print-dispatch*. + +*print-right-margin* +*print-miser-width* +*print-lines* +*default-right-margin* +*last-abbreviated-printing* + These are the XP pretty-printer control variables. For more information + about the pretty-printer, read the XP document. + +(pprint-newline kind . maybe-stream) + The kind argument can be one of LINEAR, FILL, MISER, or MANDATORY. + +(pprint-logical-block (stream-symbol list . more-options) . body) + This is a macro. The body should contain code for printing a logical + block to the stream stream-symbol. + + The format of the options is (stream-symbol list prefix suffix per-line?). + + The list argument can be used with the pprint-pop macro. + + The prefix is a string that is printed as the initial prefix of the logical + block. If per-line? is true, then the prefix is printed on every line. + The suffix is a string that is printed at the end of the logical block. + + You can use this macro even when not pretty-printing, to get support + for *print-length* and *print-level*. In that case, you should have + the body forms put out only a minimal amount of whitespace. + +(pprint-pop) + Returns the next item from the list specified to an enclosing + pprint-logical-block. Checks for circular list tails and *print-length* + abbreviation. + +(pprint-exit-if-list-exhausted) + Can be used inside pprint-logical-block to see if the list is empty. + Causes the block to be exited if so. + +(pprint-indent relative-to n . maybe-stream) + Specify the indentation level to use for a logical block. + The relative-to argument can be either BLOCK or CURRENT. + +(pprint-tab kind colnum colinc . maybe-stream) + Specify tabbing. The kind argument can be one of LINE, SECTION, + LINE-RELATIVE, or SECTION-RELATIVE. + +(pprint-fill stream list . maybe-colon-atsign) +(pprint-linear stream list . maybe-colon-atsign) +(pprint-tabular stream list . maybe-colon-atsign-tabsize) + Pretty-print list to the stream in the given style. + + +(format stream string-or-fn . args) + The standard Common Lisp format, except that some of the more esoteric + directives are unimplemented. (Specifically, watch out for specifying + field widths or using # or V parameters; most of the numeric formatting + options are unimplemented, as are complicated directives like ~{...~}.) + + The stream parameter can be #f to output to a string, or #t to output + to the (current-output-port). + + The string-or-fn argument can be a function as well as a string containing + embedded directives. The function is applied to the stream and the args. + +(warning string-or-fn . args) +(error string-or-fn . args) + + + +System Interface +---------------- + +(macroexpand-1 form . maybe-env) +(macroexpand form . maybe-env) + As in Common Lisp. Since we don't have lexical macros and don't allow + syntax to be shadowed by local bindings, you can omit the environment + argument. These functions are provided mostly for debugging purposes. + +(eval form . maybe-compile) + As in Common Lisp. If the optional argument is supplied and is true, + try to compile the code in memory, not interpret it. + +(load filename) + +*code-quality* + A number between 0 and 3. 0 = minimal compilation, 1 = for debugging, + 2 = low safety, high speed, fast compilation, 3 = go all out. + +(compile-file source-filename . maybe-binary-filename) + +(with-compilation-unit options . forms) + This is the ANSI CL macro. We don't use any options. + +(filename-place filename) +(filename-name filename) +(filename-type filename) + We use a rather simplistic file system model. Filenames are strings + with place (or directory), name, and type components. These functions + pick apart filename strings. You shouldn't have to mess with string + operations on the components directly. + +(assemble-filename place-filename name-filename type-filename) + Build a new filename by combining the appropriate parts of the argument + filenames. + +source-file-type +binary-file-type + These constants hold appropriate default types for source and + compiled files. By convention, source-file-type is ".scm" but + the binary-file-type depends on the underlying Lisp system. + +(file-exists? filename) + Returns true if the file exists. + +(file-write-date filename) +(current-date) + Dates are represented as integers relative to an arbitrary base. These + functions are mostly useful for recording timestamps. + +(get-run-time) + Return run time as a floating-point number relative to an arbitrary base. + Useful for doing timings. + +(getenv name) + Explicitly expand an environment variable. (Environment variables that + appear as filename prefixes are expanded automagically by the functions + that open files.) + +(cd filename) + Change the current directory. + + +(exit) + Go away. + + +Reader Support +-------------- + +' => quote +` => backquote; also , and ,@ +#t and #f + + +Random Stuff +------------ + +lisp-implementation-name + returns a string identifying the underlying lisp implementation; e.g. + "lucid", "t", etc. + +(identify-system) + return a longer string indentifying the lisp version and machine type. + +left-to-right-evaluation + True if the underlying Lisp always evaluates function arguments + left-to-right; false otherwise. + +(gc-messages onoff) + Turn garbage collection messages on/off, if possible. + +(identity x) + The identity function. + + + +Type specifiers +--------------- + +t +procedure +pair +null +list, (list element-type) +symbol +char +string +vector +number +integer +rational +float +fixnum, int +table, (table key-type value-type) +(enum . values) +(tuple . component-types) +bool +alist, (alist key-type value-type) +(maybe type) +struct +type-descriptor +slot-descriptor + These are the standard type specifiers. + +the + As in Common Lisp. +subtype? + Equivalent to CL subtypep +is-type? + Equivalent to CL typep +typecase + As in Common Lisp, also recognizes "else" clause. + + + +Structures +---------- + +(struct? object) + Returns true if the object is a struct. +(struct-type-descriptor object) + Returns the type descriptor of a struct object. + +name, slots, parent-type, printer + Slots of type-descriptor object. + +(td-name td) +(td-slots td) +(td-parent-type td) +(td-printer td) + Accessors for type-descriptors. + +name, type, default, getter + Slots of slot-descriptor object. + +(sd-name sd) +(sd-type sd) +(sd-default sd) +(sd-getter sd) + Accessors for slot-descriptors. +(sd-getter-function sd) + Returns a function which can be used to access a slot (as opposed to + the symbol that names the function). + +(lookup-type-descriptor type-name) +(lookup-slot-descriptor type-name slot-name) + Name to descriptor mappings. + + +(make type . initializers) + The type must name a struct type; it is not evaluated. + The initializers are of the form (slot-name value-form). + +(struct-slot type slot object) + Generalized slot access. Type and slot are symbols. If both are + quoted, can be used with SETF. + +(with-slots type slot-names object . body) + Binds the specified slots of object to local variables with the + same names. Bindings are read-only. Type is not evaluated. + +(update-slots type object . initializers) + Modifies the slots of object. Syntax of initializers is as for make. + Type is not evaluated. + +(define-struct name + (include parent-type-name) + (type-template subtype-of-type-descriptor) + (prefix prefix-symbol) + (predicate predicate-name) + (slots + (slot-name + (type type) + (default init-form) + (bit #t) + (read-only? #t) + (uninitialized? #t)) + ...)) + + Defines name as a subtype of struct with the given slots. + All fields are optional. + + Include specifies the immediate supertype. All accessors on the supertype + work on the newly defined type. It defaults to struct. + + Type-template specifies the metaclass. It can be used to attach + additional information to the type descriptor. It defaults to + type-descriptor. + + Prefix can be used to specify an alternate prefix for accessors. The + default is name-. + + Predicate can be used to create a predicate function. The default is + not to create one. + + If no default is specified for a slot, it's expected to have an + explicit initializer supplied with MAKE. You'll get a compilation + warning otherwise, unless you specify the uninitialized? option instead. + + Bit is a hint for optimizing internal representation. + + Read-only? says not to create a SETFer for the slot. + + +(define-struct-printer struct-name printer-function) + Specifies a printer function to use when *print-structure* is false. + diff --git a/support/pprint.scm b/support/pprint.scm new file mode 100644 index 0000000..9b28ec8 --- /dev/null +++ b/support/pprint.scm @@ -0,0 +1,1788 @@ +;;; pprint.scm -- xp pretty-printer in Scheme +;;; +;;; author : Sandra Loosemore +;;; date : 29 Oct 1991 +;;; +;;; +;;; This code is adapted from the XP pretty printer originally written +;;; in Common Lisp by Dick Waters. Here is the copyright notice attached +;;; to the original XP source file: +;;; +;;;------------------------------------------------------------------------ +;;; +;;; Copyright 1989,1990 by the Massachusetts Institute of Technology, +;;; Cambridge, Massachusetts. +;;; +;;; Permission to use, copy, modify, and distribute this software and its +;;; documentation for any purpose and without fee is hereby granted, +;;; provided that this copyright and permission notice appear in all +;;; copies and supporting documentation, and that the name of M.I.T. not +;;; be used in advertising or publicity pertaining to distribution of the +;;; software without specific, written prior permission. M.I.T. makes no +;;; representations about the suitability of this software for any +;;; purpose. It is provided "as is" without express or implied warranty. +;;; +;;; M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +;;; ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +;;; M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +;;; ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +;;; WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +;;; ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +;;; SOFTWARE. +;;; +;;;------------------------------------------------------------------------ +;;; + + +;;;===================================================================== +;;; Variables +;;;===================================================================== + + +;;; External variables. These may be specially bound by user code. + +(define *print-escape* '#t) +(define *print-circle* '#f) +(define *print-level* '#f) +(define *print-length* '#f) +(define *print-base* 10) +(define *print-radix* '#f) + + +(define *print-shared* '#f) +(define *print-pretty* '#f) +(define *print-right-margin* '#f) +(define *print-miser-width* 40) +(define *print-lines* '#f) +(define *default-right-margin* 70) +(define *last-abbreviated-printing* + (lambda maybe-stream + (declare (ignore maybe-stream)) + '#f)) + +(define *print-dispatch* '#f) ; initialized later +(define *print-structure* '#f) +(define *print-structure-slots* '#t) + + +;;; *** These variables aren't really supported, but they should be. + +(define *print-readably* '#f) +(define *print-case* 'upcase) + + + +;;; Internal variables. These are all specially rebound when we initiate +;;; printing to an XP stream. + +(define *xp.current-level* 0) +(define *xp.current-length* 0) +(define *xp.abbreviation-happened* '#f) +(define *xp.locating-circularities* '#f) +(define *xp.parents* '()) +(define *xp.circularity-hash-table* '#f) +(define *xp.line-limit-abbreviation-exit* + (lambda values + (declare (ignore values)) + (error "No line limit abbreviation exit in this extent."))) + + + +;;;===================================================================== +;;; Dispatching +;;;===================================================================== + +;;; Since Scheme doesn't have type specifiers or named structures, +;;; the dispatch mechanism defined for the Common Lisp XP won't work +;;; very well. A more general alternative might be to maintain a +;;; sorted list of tuples, but having to +;;; try each of these in sequence could get very slow. +;;; +;;; What I've decided to to instead is to have the value of +;;; *print-dispatch* be a user-defined dispatcher +;;; function: given an object, it should return a function to print it, +;;; or #f. In the latter case, the object is printed in some default +;;; way. +;;; +;;; The standard dispatcher function is defined towards the bottom +;;; of this file. If you are writing your own dispatcher, you should +;;; probably call this function as the fall-through case. + +(define (xp.get-printer object) + (funcall (dynamic *print-dispatch*) object)) + + +;;;===================================================================== +;;; Internal data structures +;;;===================================================================== + +(define-integrable xp.block-stack-entry-size 1) +(define-integrable xp.prefix-stack-entry-size 5) +(define-integrable xp.queue-entry-size 7) +(define-integrable xp.buffer-entry-size 1) +(define-integrable xp.prefix-entry-size 1) +(define-integrable xp.suffix-entry-size 1) + +(define-integrable xp.block-stack-min-size (* 35 xp.block-stack-entry-size)) +(define-integrable xp.prefix-stack-min-size (* 30 xp.prefix-stack-entry-size)) +(define-integrable xp.queue-min-size (* 75 xp.queue-entry-size)) +(define-integrable xp.buffer-min-size 256) +(define-integrable xp.prefix-min-size 256) +(define-integrable xp.suffix-min-size 256) + + +;;; The xp stream structure. +;;; Fields without defaults are initialized by xp.initialize-xp, below. + +(define-struct xp + (prefix xp.) + (predicate xp.xp-structure-p) + (slots + (base-stream (type t) (default '#f)) + (linel (type fixnum) (default 0)) + (line-limit (type (maybe fixnum)) (default '#f)) + (line-no (type fixnum) (default 0)) + (char-mode (type (enum #f up down cap0 cap1 capw)) (default '#f)) + (char-mode-counter (type fixnum) (default 0)) + ;; number of logical blocks at qright that are started but not ended. + (depth-in-blocks (type fixnum) (default 0)) + ;; This stack is pushed and popped in accordance with the way blocks + ;; are nested at the moment they are entered into the queue. + (block-stack (type vector) (default (make-vector xp.block-stack-min-size))) + ;; Pointer into block-stack vector. + (block-stack-ptr (type fixnum) (default 0)) + ;; This is a string that builds up the line images that will be printed out. + (buffer (type string) (default (make-string xp.buffer-min-size))) + ;; The output character position of the first character in the buffer; + ;; nonzero only if a partial line has been output. + (charpos (type fixnum) (default 0)) + ;; The index in the buffer where the next character is to be inserted. + (buffer-ptr (type fixnum) (default 0)) + ;; This is used in computing total lengths. It is changed to reflect + ;; all shifting and insertion of prefixes so that total length computes + ;; things as they would be if they were all on one line. + (buffer-offset (type fixnum) (default 0)) + ;; The queue of action descriptors. The value is a vector. + (queue (type vector) (default (make-vector xp.queue-min-size))) + ;; Index of next queue entry to dequeue. + (qleft (type fixnum) (default 0)) + ;; Index of last entry queued; queue is empty when (> qleft qright). + (qright (type fixnum) (default 0)) + ;; This stores the prefix that should be used at the start of the line. + (prefix (type string) (default (make-string xp.buffer-min-size))) + ;; This stack is pushed and popped in accordance with the way blocks + ;; are nested at the moment things are taken off the queue and printed. + (prefix-stack (type vector) (default (make-vector xp.prefix-stack-min-size))) + ;; Index into prefix-stack. + (prefix-stack-ptr (type fixnum) (default 0)) + ;; This stores the suffixes that have to be pritned to close of the + ;; current open blocks. For convenience in popping, the whole suffix + ;; is stored in reverse order. + (suffix (type string) (default (make-string xp.buffer-min-size))) + )) + + +(define (xp.make-xp-structure) + (make xp)) + + +;;; Positions within the buffer are kept in three ways: +;;; * Buffer position (eg BUFFER-PTR) +;;; * Line position (eg (+ BUFFER-PTR CHARPOS)). +;;; Indentations are stored in this form. +;;; * Total position if all on one line (eg (+ BUFFER-PTR BUFFER-OFFSET)) +;;; Positions are stored in this form. + +(define-local-syntax (xp.lp<-bp xp . maybe-ptr) + (let ((ptr (if (not (null? maybe-ptr)) + (car maybe-ptr) + `(xp.buffer-ptr ,xp)))) + `(+ ,ptr (xp.charpos ,xp)))) + +(define-local-syntax (xp.tp<-bp xp) + `(+ (xp.buffer-ptr ,xp) (xp.buffer-offset ,xp))) + +(define-local-syntax (xp.bp<-lp xp ptr) + `(- ,ptr (xp.charpos ,xp))) + +(define-local-syntax (xp.bp<-tp xp ptr) + `(- ,ptr (xp.buffer-offset ,xp))) + +(define-local-syntax (xp.lp<-tp xp ptr) + `(xp.lp<-bp ,xp (xp.bp<-tp ,xp ,ptr))) + + +;;; Define some macros for growing the various stacks in the xp-structure. + +(define-local-syntax (xp.check-block-stack-size xp ptr) + `(setf (xp.block-stack ,xp) + (xp.grow-vector (xp.block-stack ,xp) ,ptr xp.block-stack-entry-size))) + +(define-local-syntax (xp.check-prefix-size xp ptr) + `(setf (xp.prefix ,xp) + (xp.grow-string (xp.prefix ,xp) ,ptr xp.prefix-entry-size))) + +(define-local-syntax (xp.check-prefix-stack-size xp ptr) + `(setf (xp.prefix-stack ,xp) + (xp.grow-vector (xp.prefix-stack ,xp) ,ptr xp.prefix-stack-entry-size))) + +(define-local-syntax (xp.check-queue-size xp ptr) + `(setf (xp.queue ,xp) + (xp.grow-vector (xp.queue ,xp) ,ptr xp.queue-entry-size))) + +(define-local-syntax (xp.check-buffer-size xp ptr) + `(setf (xp.buffer ,xp) + (xp.grow-string (xp.buffer ,xp) ,ptr xp.buffer-entry-size))) + +(define-local-syntax (xp.check-suffix-size xp ptr) + `(setf (xp.suffix ,xp) + (xp.grow-string (xp.suffix ,xp) ,ptr xp.suffix-entry-size))) + +(define (xp.grow-vector old ptr entry-size) + (let ((end (vector-length old))) + (if (> ptr (- end entry-size)) + (let ((new (make-vector (+ ptr 50)))) + (dotimes (i end) + (setf (vector-ref new i) (vector-ref old i))) + new) + old))) + +(define (xp.grow-string old ptr entry-size) + (let ((end (string-length old))) + (if (> ptr (- end entry-size)) + (let ((new (make-string (+ ptr 50)))) + (dotimes (i end) + (setf (string-ref new i) (string-ref old i))) + new) + old))) + + + +;;; Things for manipulating the block stack. + +(define-local-syntax (xp.section-start xp) + `(vector-ref (xp.block-stack ,xp) (xp.block-stack-ptr ,xp))) + +(define (xp.push-block-stack xp) + (incf (xp.block-stack-ptr xp) xp.block-stack-entry-size) + (xp.check-block-stack-size xp (xp.block-stack-ptr xp))) + +(define (xp.pop-block-stack xp) + (decf (xp.block-stack-ptr xp) xp.block-stack-entry-size)) + + +;;; Prefix stack manipulations + +(define-local-syntax (xp.prefix-ptr xp) + `(vector-ref (xp.prefix-stack ,xp) (xp.prefix-stack-ptr ,xp))) +(define-local-syntax (xp.suffix-ptr xp) + `(vector-ref (xp.prefix-stack ,xp) (+ (xp.prefix-stack-ptr ,xp) 1))) +(define-local-syntax (non-blank-prefix-ptr xp) + `(vector-ref (xp.prefix-stack ,xp) (+ (xp.prefix-stack-ptr ,xp) 2))) +(define-local-syntax (initial-prefix-ptr xp) + `(vector-ref (xp.prefix-stack ,xp) (+ (xp.prefix-stack-ptr ,xp) 3))) +(define-local-syntax (xp.section-start-line xp) + `(vector-ref (xp.prefix-stack ,xp) (+ (xp.prefix-stack-ptr ,xp) 4))) + +(define (xp.push-prefix-stack xp) + (let ((old-prefix 0) + (old-suffix 0) + (old-non-blank 0)) + (when (not (negative? (xp.prefix-stack-ptr xp))) + (setf old-prefix (xp.prefix-ptr xp)) + (setf old-suffix (xp.suffix-ptr xp)) + (setf old-non-blank (non-blank-prefix-ptr xp))) + (incf (xp.prefix-stack-ptr xp) xp.prefix-stack-entry-size) + (xp.check-prefix-stack-size xp (xp.prefix-stack-ptr xp)) + (setf (xp.prefix-ptr xp) old-prefix) + (setf (xp.suffix-ptr xp) old-suffix) + (setf (non-blank-prefix-ptr xp) old-non-blank))) + +(define (xp.pop-prefix-stack xp) + (decf (xp.prefix-stack-ptr xp) xp.prefix-stack-entry-size)) + + +;;; The queue entries have several parts: +;;; QTYPE one of :NEWLINE/:IND/:START-BLOCK/:END-BLOCK +;;; QKIND :LINEAR/:MISER/:FILL/:MANDATORY or :UNCONDITIONAL/:FRESH +;;; or :BLOCK/:CURRENT +;;; QPOS total position corresponding to this entry +;;; QDEPTH depth in blocks of this entry. +;;; QEND offset to entry marking end of section this entry starts. +;; (NIL until known.) +;;; Only :start-block and non-literal :newline entries can start sections. +;;; QOFFSET offset to :END-BLOCK for :START-BLOCK (NIL until known). +;;; QARG for :IND indentation delta +;;; for :START-BLOCK suffix in the block if any. +;;; or if per-line-prefix then cons of suffix and +;;; per-line-prefix. +;;; for :END-BLOCK suffix for the block if any. + +(define-local-syntax (xp.qtype xp index) + `(vector-ref (xp.queue ,xp) ,index)) +(define-local-syntax (xp.qkind xp index) + `(vector-ref (xp.queue ,xp) (1+ ,index))) +(define-local-syntax (xp.qpos xp index) + `(vector-ref (xp.queue ,xp) (+ ,index 2))) +(define-local-syntax (xp.qdepth xp index) + `(vector-ref (xp.queue ,xp) (+ ,index 3))) +(define-local-syntax (xp.qend xp index) + `(vector-ref (xp.queue ,xp) (+ ,index 4))) +(define-local-syntax (xp.qoffset xp index) + `(vector-ref (xp.queue ,xp) (+ ,index 5))) +(define-local-syntax (xp.qarg xp index) + `(vector-ref (xp.queue ,xp) (+ ,index 6))) + +;;; we shift the queue over rather than using a circular queue because +;;; that works out to be a lot faster in practice. Note, short printout +;;; does not ever cause a shift, and even in long printout, the queue is +;;; shifted left for free every time it happens to empty out. + +(define (xp.enqueue xp type kind . maybe-arg) + (incf (xp.qright xp) xp.queue-entry-size) + (when (> (xp.qright xp) (- xp.queue-min-size xp.queue-entry-size)) + (vector-replace (xp.queue xp) (xp.queue xp) 0 (xp.qleft xp) (xp.qright xp)) + (setf (xp.qright xp) (- (xp.qright xp) (xp.qleft xp))) + (setf (xp.qleft xp) 0)) + (xp.check-queue-size xp (xp.qright xp)) + (setf (xp.qtype xp (xp.qright xp)) type) + (setf (xp.qkind xp (xp.qright xp)) kind) + (setf (xp.qpos xp (xp.qright xp)) (xp.tp<-bp xp)) + (setf (xp.qdepth xp (xp.qright xp)) (xp.depth-in-blocks xp)) + (setf (xp.qend xp (xp.qright xp)) '#f) + (setf (xp.qoffset xp (xp.qright xp)) '#f) + (setf (xp.qarg xp (xp.qright xp)) (car maybe-arg))) + +(define-local-syntax (xp.qnext index) `(+ ,index xp.queue-entry-size)) + + + +;;; Print routine for xp structures +;;; *** this is broken, it uses unimplemented format options. + +(define *xp.describe-xp-streams-fully* '#f) + +(define (xp.describe-xp xp . maybe-stream) + (let ((s (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (format s "# (xp.qleft xp) (xp.qright xp)) + (format s "~&ptr type kind pos depth end offset arg") + (do ((p (xp.qleft xp) (xp.qnext p))) + ((> p (xp.qright xp))) + (format s "~&~4A~13A~15A~4A~6A~4A~7A~A" + (/ (- p (xp.qleft xp)) xp.queue-entry-size) + (xp.qtype xp p) + (if (memq (xp.qtype xp p) '(newline ind)) + (xp.qkind xp p) + "") + (xp.bp<-tp xp (xp.qpos xp p)) + (xp.qdepth xp p) + (if (not (memq (xp.qtype xp p) + '(newline start-block))) + "" + (and (xp.qend xp p) + (/ (- (+ p (xp.qend xp p)) (xp.qleft xp)) + xp.queue-entry-size))) + (if (not (eq? (xp.qtype xp p) 'start-block)) + "" + (and (xp.qoffset xp p) + (/ (- (+ p (xp.qoffset xp p)) (xp.qleft xp)) + xp.queue-entry-size))) + (if (not (memq (xp.qtype xp p) + '(ind start-block end-block))) + "" + (xp.qarg xp p))))) + (unless (negative? (xp.prefix-stack-ptr xp)) + (format s "~&initial-prefix-ptr prefix-ptr suffix-ptr non-blank start-line") + (do ((save (xp.prefix-stack-ptr xp))) + ((negative? (xp.prefix-stack-ptr xp)) + (setf (xp.prefix-stack-ptr xp) save)) + (format s "~& ~19A~11A~11A~10A~A" + (initial-prefix-ptr xp) + (xp.prefix-ptr xp) + (xp.suffix-ptr xp) + (non-blank-prefix-ptr xp) + (xp.section-start-line xp)) + (xp.pop-prefix-stack xp))))))) + (format s ">"))) + + + +;;; Allocation of XP structures + +;;; This maintains a list of XP structures. We save them +;;; so that we don't have to create new ones all of the time. +;;; We have separate objects so that many can be in use at once +;;; (e.g. for printing to multiple streams). + +(define xp.free-xps '()) + +(define (xp.get-pretty-print-stream stream) + (xp.initialize-xp + (if (not (null? xp.free-xps)) + (pop xp.free-xps) + (xp.make-xp-structure)) + stream)) + + +;;; If you call this, the xp-stream gets efficiently recycled. + +(define (xp.free-pretty-print-stream xp) + (setf (xp.base-stream xp) '#f) + (if (not (memq xp xp.free-xps)) + (push xp xp.free-xps))) + + +;;; This is called to initialize things when you start pretty printing. + +(define (xp.initialize-xp xp stream) + (setf (xp.base-stream xp) stream) + (setf (xp.linel xp) + (max 0 + (cond ((dynamic *print-right-margin*)) + ((internal-output-width stream)) + (else (dynamic *default-right-margin*))))) + (setf (xp.line-limit xp) (dynamic *print-lines*)) + (setf (xp.line-no xp) 1) + (setf (xp.char-mode xp) '#f) + (setf (xp.char-mode-counter xp) 0) + (setf (xp.depth-in-blocks xp) 0) + (setf (xp.block-stack-ptr xp) 0) + (setf (xp.charpos xp) (or (internal-output-position stream) 0)) + (setf (xp.section-start xp) 0) + (setf (xp.buffer-ptr xp) 0) + (setf (xp.buffer-offset xp) (xp.charpos xp)) + (setf (xp.qleft xp) 0) + (setf (xp.qright xp) (- xp.queue-entry-size)) + (setf (xp.prefix-stack-ptr xp) (- xp.prefix-stack-entry-size)) + xp) + + + +;;; The char-mode stuff is a bit tricky. +;;; one can be in one of the following modes: +;;; NIL no changes to characters output. +;;; :UP CHAR-UPCASE used. +;;; :DOWN CHAR-DOWNCASE used. +;;; :CAP0 capitalize next alphanumeric letter then switch to :DOWN. +;;; :CAP1 capitalize next alphanumeric letter then switch to :CAPW +;;; :CAPW downcase letters. When a word break letter found, switch to :CAP1. +;;; It is possible for ~(~) to be nested in a format string, but note that +;;; each mode specifies what should happen to every letter. Therefore, inner +;;; nested modes never have any effect. You can just ignore them. + +(define (xp.push-char-mode xp new-mode) + (if (zero? (xp.char-mode-counter xp)) + (setf (xp.char-mode xp) new-mode)) + (incf (xp.char-mode-counter xp))) + +(define (xp.pop-char-mode xp) + (decf (xp.char-mode-counter xp)) + (if (zero? (xp.char-mode-counter xp)) + (setf (xp.char-mode xp) '#f))) + + +;;; Assumes is only called when char-mode is non-nil + +(define (xp.handle-char-mode xp char) + (case (xp.char-mode xp) + ((CAP0) + (cond ((not (or (char-alphabetic? char) (char-numeric? char))) char) + (else (setf (xp.char-mode xp) 'DOWN) (char-upcase char)))) + ((CAP1) + (cond ((not (or (char-alphabetic? char) (char-numeric? char))) char) + (else (setf (xp.char-mode xp) 'CAPW) (char-upcase char)))) + ((CAPW) + (cond ((or (char-alphabetic? char) (char-numeric? char)) + (char-downcase char)) + (else (setf (xp.char-mode xp) 'CAP1) char))) + ((UP) + (char-upcase char)) + (else + (char-downcase char)))) ;DOWN + + +;;; All characters output are passed through the handler above. However, +;;; it must be noted that on-each-line prefixes are only processed in the +;;; context of the first place they appear. They stay the same later no +;;; matter what. Also non-literal newlines do not count as word breaks. + +;;; This handles the basic outputting of characters. note + suffix means that +;;; the stream is known to be an XP stream, all inputs are mandatory, and no +;;; error checking has to be done. Suffix ++ additionally means that the +;;; output is guaranteed not to contain a newline char. + +(define (xp.write-char+ char xp) + (if (eqv? char #\newline) + (xp.pprint-newline+ 'unconditional xp) + (xp.write-char++ char xp))) + +(define (xp.write-string+ mystring xp start end) + (let ((next-newline (string-position #\newline mystring start end))) + (if next-newline + (begin + (xp.write-string++ mystring xp start next-newline) + (xp.pprint-newline+ 'unconditional xp) + (xp.write-string+ mystring xp (1+ next-newline) end)) + (xp.write-string++ mystring xp start end)))) + + +;;; note this checks (> BUFFER-PTR LINEL) instead of (> (xp.lp<-bp) LINEL) +;;; this is important so that when things are longer than a line they +;;; end up getting printed in chunks of size LINEL. + +(define (xp.write-char++ char xp) + (when (> (xp.buffer-ptr xp) (xp.linel xp)) + (xp.force-some-output xp)) + (let ((new-buffer-end (1+ (xp.buffer-ptr xp)))) + (xp.check-buffer-size xp new-buffer-end) + (if (xp.char-mode xp) (setf char (xp.handle-char-mode xp char))) + (setf (string-ref (xp.buffer xp) (xp.buffer-ptr xp)) char) + (setf (xp.buffer-ptr xp) new-buffer-end))) + +(define (xp.force-some-output xp) + (xp.attempt-to-output xp '#f '#f) + (when (> (xp.buffer-ptr xp) (xp.linel xp)) ;only if printing off end of line + (xp.attempt-to-output xp '#t '#t))) + +(define (xp.write-string++ mystring xp start end) + (when (> (xp.buffer-ptr xp) (xp.linel xp)) + (xp.force-some-output xp)) + (xp.write-string+++ mystring xp start end)) + + +;;; never forces output; therefore safe to call from within xp.output-line. + +(define (xp.write-string+++ mystring xp start end) + (let ((new-buffer-end (+ (xp.buffer-ptr xp) (- end start)))) + (xp.check-buffer-size xp new-buffer-end) + (do ((buffer (xp.buffer xp)) + (i (xp.buffer-ptr xp) (1+ i)) + (j start (1+ j))) + ((= j end)) + (let ((char (string-ref mystring j))) + (if (xp.char-mode xp) (setf char (xp.handle-char-mode xp char))) + (setf (string-ref buffer i) char))) + (setf (xp.buffer-ptr xp) new-buffer-end))) + + +(define (xp.pprint-tab+ kind colnum colinc xp) + (let ((indented? '#f) + (relative? '#f)) + (case kind + ((section) (setf indented? '#t)) + ((line-relative) (setf relative? '#t)) + ((section-relative) (setf indented? '#t) (setf relative? '#t))) + (let* ((current + (if (not indented?) + (xp.lp<-bp xp) + (- (xp.tp<-bp xp) (xp.section-start xp)))) + (new + (if (zero? colinc) + (if relative? (+ current colnum) (max colnum current)) + (cond (relative? + (* colinc + (quotient (+ current colnum colinc -1) colinc))) + ((> colnum current) colnum) + (else + (+ colnum + (* colinc + (quotient (+ current (- colnum) colinc) + colinc))))))) + (end (- new current))) + (when (positive? end) + (if (xp.char-mode xp) (xp.handle-char-mode xp #\space)) + (let ((end (+ (xp.buffer-ptr xp) end))) + (xp.check-buffer-size xp end) + (string-fill (xp.buffer xp) #\space (xp.buffer-ptr xp) end) + (setf (xp.buffer-ptr xp) end)))))) + + +;;; note following is smallest number >= x that is a multiple of colinc +;;; (* colinc (quotient (+ x (1- colinc)) colinc)) + + +(define (xp.pprint-newline+ kind xp) + (xp.enqueue xp 'newline kind) + (do ((ptr (xp.qleft xp) (xp.qnext ptr))) ;find sections we are ending + ((not (< ptr (xp.qright xp)))) ;all but last + (when (and (not (xp.qend xp ptr)) + (not (> (xp.depth-in-blocks xp) (xp.qdepth xp ptr))) + (memq (xp.qtype xp ptr) '(newline start-block))) + (setf (xp.qend xp ptr) (- (xp.qright xp) ptr)))) + (setf (xp.section-start xp) (xp.tp<-bp xp)) + (when (and (memq kind '(fresh unconditional)) (xp.char-mode xp)) + (xp.handle-char-mode xp #\newline)) + (when (memq kind '(fresh unconditional mandatory)) + (xp.attempt-to-output xp '#t '#f))) + + +(define (xp.start-block xp prefix-string on-each-line? suffix-string) + (xp.write-prefix-suffix prefix-string xp) + (if (and (xp.char-mode xp) on-each-line?) + (setf prefix-string + (substring (xp.buffer xp) + (- (xp.buffer-ptr xp) (string-length prefix-string)) + (xp.buffer-ptr xp)))) + (xp.push-block-stack xp) + (xp.enqueue xp 'start-block '#f + (if on-each-line? (cons suffix-string prefix-string) suffix-string)) + (incf (xp.depth-in-blocks xp)) ;must be after enqueue + (setf (xp.section-start xp) (xp.tp<-bp xp))) + + +(define (xp.end-block xp suffix) + (unless (and (dynamic *xp.abbreviation-happened*) + (eqv? (dynamic *xp.abbreviation-happened*) + (dynamic *print-lines*))) + (xp.write-prefix-suffix suffix xp) + (decf (xp.depth-in-blocks xp)) + (xp.enqueue xp 'end-block '#f suffix) + (block foundit + (do ((ptr (xp.qleft xp) (xp.qnext ptr))) ;look for start of block we are ending + ((not (< ptr (xp.qright xp)))) ;all but last + (when (and (= (xp.depth-in-blocks xp) (xp.qdepth xp ptr)) + (eq? (xp.qtype xp ptr) 'start-block) + (not (xp.qoffset xp ptr))) + (setf (xp.qoffset xp ptr) (- (xp.qright xp) ptr)) + (return-from foundit '#f))) ;can only be 1 + ) + (xp.pop-block-stack xp))) + +(define (xp.write-prefix-suffix mystring xp) + (when mystring + (xp.write-string++ mystring xp 0 (string-length mystring)))) + +(define (xp.pprint-indent+ kind n xp) + (xp.enqueue xp 'ind kind n)) + + +;;; attempt-to-output scans the queue looking for things it can do. +;;; it keeps outputting things until the queue is empty, or it finds +;;; a place where it cannot make a decision yet. +;;; If flush-out? is T and force-newlines? is NIL then the buffer, +;;; prefix-stack, and queue will be in an inconsistent state after the call. +;;; You better not call it this way except as the last act of outputting. + + +(define-local-syntax (xp.maybe-too-large xp Qentry) + `(let ((limit (xp.linel ,xp))) + (when (eqv? (xp.line-limit ,xp) (xp.line-no ,xp)) ;prevents suffix overflow + (decf limit 2) ;3 for " .." minus 1 for space (heuristic) + (when (not (negative? (xp.prefix-stack-ptr ,xp))) + (decf limit (xp.suffix-ptr ,xp)))) + (cond ((xp.qend ,xp ,Qentry) + (> (xp.lp<-tp ,xp (xp.qpos ,xp (+ ,Qentry (xp.qend ,xp ,Qentry)))) limit)) + ((or force-newlines? (> (xp.lp<-bp ,xp) limit)) + '#t) + (else ;wait until later to decide. + (return-from attempt-to-output '#f))))) + +(define-local-syntax (xp.misering? xp) + `(and (dynamic *print-miser-width*) + (<= (- (xp.linel ,xp) (initial-prefix-ptr ,xp)) + (dynamic *print-miser-width*)))) + +(define (xp.attempt-to-output xp force-newlines? flush-out?) + (block attempt-to-output + (do () + ((> (xp.qleft xp) (xp.qright xp)) + (setf (xp.qleft xp) 0) + (setf (xp.qright xp) (- xp.queue-entry-size))) ;saves shifting + (case (xp.qtype xp (xp.qleft xp)) + ((ind) + (unless (xp.misering? xp) + (xp.set-indentation-prefix + xp + (case (xp.qkind xp (xp.qleft xp)) + ((block) + (+ (initial-prefix-ptr xp) (xp.qarg xp (xp.qleft xp)))) + (else ; current + (+ (xp.lp<-tp xp (xp.qpos xp (xp.qleft xp))) + (xp.qarg xp (xp.qleft xp))))))) + (setf (xp.qleft xp) (xp.qnext (xp.qleft xp)))) + ((start-block) + (cond ((xp.maybe-too-large xp (xp.qleft xp)) + (xp.push-prefix-stack xp) + (setf (initial-prefix-ptr xp) (xp.prefix-ptr xp)) + (xp.set-indentation-prefix + xp (xp.lp<-tp xp (xp.qpos xp (xp.qleft xp)))) + (let ((arg (xp.qarg xp (xp.qleft xp)))) + (when (pair? arg) (xp.set-prefix xp (cdr arg))) + (setf (initial-prefix-ptr xp) (xp.prefix-ptr xp)) + (cond ((not (list? arg)) (xp.set-suffix xp arg)) + ((car arg) (xp.set-suffix xp (car arg))))) + (setf (xp.section-start-line xp) (xp.line-no xp))) + (else (incf (xp.qleft xp) (xp.qoffset xp (xp.qleft xp))))) + (setf (xp.qleft xp) (xp.qnext (xp.qleft xp)))) + ((end-block) + (xp.pop-prefix-stack xp) + (setf (xp.qleft xp) (xp.qnext (xp.qleft xp)))) + (else ; newline + (when (case (xp.qkind xp (xp.qleft xp)) + ((fresh) (not (zero? (xp.lp<-bp xp)))) + ((miser) (xp.misering? xp)) + ((fill) (or (xp.misering? xp) + (> (xp.line-no xp) (xp.section-start-line xp)) + (xp.maybe-too-large xp (xp.qleft xp)))) + (else '#t)) ;(linear unconditional mandatory) + (xp.output-line xp (xp.qleft xp)) + (xp.setup-for-next-line xp (xp.qleft xp))) + (setf (xp.qleft xp) (xp.qnext (xp.qleft xp))))))) + (when flush-out? (xp.flush xp))) + + +;;; this can only be called last! + +(define (xp.flush xp) + (unless (dynamic *xp.locating-circularities*) + (internal-write-string (xp.buffer xp) (xp.base-stream xp) 0 (xp.buffer-ptr xp))) + (incf (xp.buffer-offset xp) (xp.buffer-ptr xp)) + (incf (xp.charpos xp) (xp.buffer-ptr xp)) + (setf (xp.buffer-ptr xp) 0)) + + +;;; This prints out a line of stuff. + +(define (xp.output-line xp Qentry) + (let* ((out-point (xp.bp<-tp xp (xp.qpos xp Qentry))) + (last-non-blank (string-position-not-from-end + #\space (xp.buffer xp) 0 out-point)) + (end (cond ((memq (xp.qkind xp Qentry) '(fresh unconditional)) + out-point) + (last-non-blank (1+ last-non-blank)) + (else 0))) + (line-limit-exit (and (xp.line-limit xp) + (not (> (xp.line-limit xp) (xp.line-no xp)))))) + (when line-limit-exit + (setf (xp.buffer-ptr xp) end) ;truncate pending output. + (xp.write-string+++ " .." xp 0 3) + (string-nreverse (xp.suffix xp) 0 (xp.suffix-ptr xp)) + (xp.write-string+++ (xp.suffix xp) xp 0 (xp.suffix-ptr xp)) + (setf (xp.qleft xp) (xp.qnext (xp.qright xp))) + (setf (dynamic *xp.abbreviation-happened*) (dynamic *print-lines*)) + (funcall (dynamic *xp.line-limit-abbreviation-exit*) '#t)) + (incf (xp.line-no xp)) + (unless (dynamic *xp.locating-circularities*) + (internal-write-string (xp.buffer xp) (xp.base-stream xp) 0 end) + (newline (xp.base-stream xp))))) + +(define (xp.setup-for-next-line xp Qentry) + (let* ((out-point (xp.bp<-tp xp (xp.qpos xp Qentry))) + (prefix-end + (cond ((memq (xp.qkind xp Qentry) '(unconditional fresh)) + (non-blank-prefix-ptr xp)) + (else (xp.prefix-ptr xp)))) + (change (- prefix-end out-point))) + (setf (xp.charpos xp) 0) + (when (positive? change) ;almost never happens + (xp.check-buffer-size xp (+ (xp.buffer-ptr xp) change))) + (string-replace (xp.buffer xp) (xp.buffer xp) + prefix-end out-point (xp.buffer-ptr xp)) + (string-replace (xp.buffer xp) (xp.prefix xp) 0 0 prefix-end) + (incf (xp.buffer-ptr xp) change) + (decf (xp.buffer-offset xp) change) + (when (not (memq (xp.qkind xp Qentry) '(unconditional fresh))) + (setf (xp.section-start-line xp) (xp.line-no xp))))) + +(define (xp.set-indentation-prefix xp new-position) + (let ((new-ind (max (non-blank-prefix-ptr xp) new-position))) + (setf (xp.prefix-ptr xp) (initial-prefix-ptr xp)) + (xp.check-prefix-size xp new-ind) + (when (> new-ind (xp.prefix-ptr xp)) + (string-fill (xp.prefix xp) #\space (xp.prefix-ptr xp) new-ind)) + (setf (xp.prefix-ptr xp) new-ind))) + +(define (xp.set-prefix xp prefix-string) + (let ((end (string-length prefix-string))) + (string-replace (xp.prefix xp) prefix-string + (- (xp.prefix-ptr xp) end) 0 end)) + (setf (non-blank-prefix-ptr xp) (xp.prefix-ptr xp))) + +(define (xp.set-suffix xp suffix-string) + (let* ((end (string-length suffix-string)) + (new-end (+ (xp.suffix-ptr xp) end))) + (xp.check-suffix-size xp new-end) + (do ((i (1- new-end) (1- i)) + (j 0 (1+ j))) + ((= j end)) + (setf (string-ref (xp.suffix xp) i) (string-ref suffix-string j))) + (setf (xp.suffix-ptr xp) new-end))) + + +;;;===================================================================== +;;; Basic interface functions +;;;===================================================================== + +;;; The internal functions in this file +;;; use the '+' forms of these functions directly (which is faster) because, +;;; they do not need error checking of fancy stream coercion. The '++' forms +;;; additionally assume the thing being output does not contain a newline. + +(define (write object . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (cond ((xp.xp-structure-p stream) + (xp.write+ object stream)) + ((xp.get-printer object) + (xp.initiate-xp-printing + (lambda (s o) (xp.write+ o s)) + stream + object)) + (else + (internal-write object stream))))) + +(define (xp.maybe-initiate-xp-printing fn stream . args) + (if (xp.xp-structure-p stream) + (apply fn stream args) + (apply (function xp.initiate-xp-printing) fn stream args))) + +(define (xp.initiate-xp-printing fn stream . args) + (dynamic-let ((*xp.abbreviation-happened* + '#f) + (*xp.locating-circularities* + (if (dynamic *print-circle*) + 0 + '#f)) + (*xp.circularity-hash-table* + (if (dynamic *print-circle*) + (make-table) + '#f)) + (*xp.parents* + (if (not (dynamic *print-shared*)) + (list '#f) + '())) ;*** is this right? + (*xp.current-level* + 0) + (*xp.current-length* + 0)) + (let ((result (xp.xp-print fn stream args))) + (when (dynamic *xp.abbreviation-happened*) + (setf args (list-copy args)) + (setf (dynamic *last-abbreviated-printing*) + (lambda maybe-stream + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + stream))) + (apply (function xp.maybe-initiate-xp-printing) + fn stream args))))) + result))) + +(define (xp.xp-print fn stream args) + (let ((result (xp.do-xp-printing fn stream args))) + (when (dynamic *xp.locating-circularities*) + (setf (dynamic *xp.locating-circularities*) '#f) + (setf (dynamic *xp.abbreviation-happened*) '#f) + (setf (dynamic *xp.parents*) '()) + (setf result (xp.do-xp-printing fn stream args))) + result)) + +(define (xp.do-xp-printing fn stream args) + (let ((xp (xp.get-pretty-print-stream stream)) + (result '#f)) + (dynamic-let ((*xp.current-level* 0)) + (let/cc catch + (dynamic-let ((*xp.line-limit-abbreviation-exit* catch)) + (xp.start-block xp '#f '#f '#f) + (setf result (apply fn xp args)) + (xp.end-block xp '#f))) + (when (and (dynamic *xp.locating-circularities*) + (zero? (dynamic *xp.locating-circularities*)) ;No circularities. + (= (xp.line-no xp) 1) ;Didn't suppress line. + (zero? (xp.buffer-offset xp))) ;Didn't suppress partial line. + (setf (dynamic *xp.locating-circularities*) '#f)) ;print what you have got. + (when (let/cc catch + (dynamic-let ((*xp.line-limit-abbreviation-exit* catch)) + (xp.attempt-to-output xp '#f '#t) + '#f)) + (xp.attempt-to-output xp '#t '#t)) + (xp.free-pretty-print-stream xp) + result))) + + +(define (xp.write+ object xp) + (dynamic-let ((*xp.parents* (dynamic *xp.parents*))) + (unless (and (dynamic *xp.circularity-hash-table*) + (eq? (xp.circularity-process xp object '#f) 'subsequent)) + (when (and (dynamic *xp.circularity-hash-table*) (pair? object)) + ;; Must do this to avoid additional circularity detection by + ;; pprint-logical-block; otherwise you get stuff like #1=#1#. + (setf object (cons (car object) (cdr object)))) + (funcall (or (xp.get-printer object) (function xp.print-default)) + object + xp)) + object)) + + + +(define (xp.print-default object xp) + (let ((stuff (internal-write-to-string object))) + (xp.write-string+ stuff xp 0 (string-length stuff)))) + + +;;; It is vital that this function be called EXACTLY once for each occurrence +;;; of each thing in something being printed. +;;; Returns nil if printing should just continue on. +;;; Either it is not a duplicate, or we are in the first pass and do not +;;; know. +;;; returns :FIRST if object is first occurrence of a DUPLICATE. +;;; (This can only be returned on a second pass.) +;;; After an initial code (printed by this routine on the second pass) +;;; printing should continue on for the object. +;;; returns :SUBSEQUENT if second or later occurrence. +;;; Printing is all taken care of by this routine. + +;;; Note many (maybe most) lisp implementations have characters and small +;;; numbers represented in a single word so that the are always eq when +;;; they are equal and the reader takes care of properly sharing them +;;; (just as it does with symbols). Therefore, we do not want circularity +;;; processing applied to them. However, some kinds of numbers +;;; (e.g., bignums) undoubtedly are complex structures that the reader +;;; does not share. However, they cannot have circular pointers in them +;;; and it is therefore probably a waste to do circularity checking on them. +;;; In any case, it is not clear that it easy to tell exactly what kinds of +;;; numbers a given implementation is going to have the reader +;;; automatically share. + +(define (xp.circularity-process xp object interior-cdr?) + (unless (or (number? object) + (char? object) + (and (symbol? object) (not (gensym? object)))) + (let ((id (table-entry (dynamic *xp.circularity-hash-table*) object))) + (if (dynamic *xp.locating-circularities*) + ;; This is the first pass. + (cond ((not id) ;never seen before + (when (not (null? (dynamic *xp.parents*))) + (push object (dynamic *xp.parents*))) + (setf (table-entry (dynamic *xp.circularity-hash-table*) object) + 0) + '#f) + ((zero? id) ;possible second occurrence + (cond ((or (null? (dynamic *xp.parents*)) + (memq object (dynamic *xp.parents*))) + (setf (table-entry + (dynamic *xp.circularity-hash-table*) object) + (incf (dynamic *xp.locating-circularities*))) + 'subsequent) + (else '#f))) + (else 'subsequent));third or later occurrence + ;; This is the second pass. + (cond ((or (not id) ;never seen before (note ~@* etc. conses) + (zero? id));no duplicates + '#f) + ((positive? id) ; first occurrence + (cond (interior-cdr? + (decf (dynamic *xp.current-level*)) + (xp.write-string++ ". #" xp 0 3)) + (else (xp.write-char++ #\# xp))) + (xp.print-integer id xp) + (xp.write-char++ #\= xp) + (setf (table-entry (dynamic *xp.circularity-hash-table*) object) + (- id)) + 'first) + (else + (if interior-cdr? + (xp.write-string++ ". #" xp 0 3) + (xp.write-char++ #\# xp)) + (xp.print-integer(- id) xp) + (xp.write-char++ #\# xp) + 'subsequent)))))) + + +;;; Here are all the standard Common Lisp printing functions. + +(define (print object . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (dynamic-let ((*print-escape* '#t)) + (terpri stream) + (write object stream) + (write-char #\space stream) + object))) + +(define (prin1 object . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (dynamic-let ((*print-escape* '#t)) + (write object stream) + object))) + +(define (princ object . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (dynamic-let ((*print-escape* '#f)) + (write object stream) + object))) + +(define (display object . maybe-stream) + (apply (function princ) object maybe-stream)) + + +(define (pprint object . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (dynamic-let ((*print-escape* '#t) + (*print-pretty* '#t)) + (terpri stream) + (write object stream) + (values)))) + +(define (prin1-to-string object) + (call-with-output-string + (lambda (stream) + (dynamic-let ((*print-escape* '#t)) + (write object stream))))) + +(define (princ-to-string object) + (call-with-output-string + (lambda (stream) + (dynamic-let ((*print-escape* '#f)) + (write object stream))))) + + + +(define (write-char char . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (if (xp.xp-structure-p stream) + (xp.write-char+ char stream) + (internal-write-char char stream)) + char)) + +(define (write-string mystring . maybe-stream-start-end) + (let* ((stream (if (not (null? maybe-stream-start-end)) + (car maybe-stream-start-end) + (current-output-port))) + (start (if (not (null? (cdr maybe-stream-start-end))) + (cadr maybe-stream-start-end) + 0)) + (end (if (not (null? (cddr maybe-stream-start-end))) + (caddr maybe-stream-start-end) + (string-length mystring)))) + (if (xp.xp-structure-p stream) + (xp.write-string+ mystring stream start end) + (internal-write-string mystring stream start end)) + mystring)) + +(define (write-line mystring . maybe-stream-start-end) + (let* ((stream (if (not (null? maybe-stream-start-end)) + (car maybe-stream-start-end) + (current-output-port))) + (start (if (not (null? (cdr maybe-stream-start-end))) + (cadr maybe-stream-start-end) + 0)) + (end (if (not (null? (cddr maybe-stream-start-end))) + (caddr maybe-stream-start-end) + (string-length mystring)))) + (if (xp.xp-structure-p stream) + (begin + (xp.write-string+ mystring stream start end) + (xp.pprint-newline+ 'unconditional stream)) + (begin + (internal-write-string mystring stream start end) + (internal-newline stream))) + mystring)) + +(define (terpri . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (if (xp.xp-structure-p stream) + (xp.pprint-newline+ 'unconditional stream) + (internal-newline stream)) + '#f)) + +(define (newline . maybe-stream) + (apply (function terpri) maybe-stream)) + + +;;; This has to violate the XP data abstraction and fool with internal +;;; stuff, in order to find out the right info to return as the result. + +(define (fresh-line . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (cond ((xp.xp-structure-p stream) + (xp.attempt-to-output stream '#t '#t) ;ok because we want newline + (when (not (zero? (xp.lp<-bp stream))) + (xp.pprint-newline+ 'fresh stream) + '#t)) + (else + (internal-fresh-line stream))))) + + +;;; Each of these causes the stream to be pessimistic and insert +;;; newlines wherever it might have to, when forcing the partial output +;;; out. This is so that things will be in a consistent state if +;;; output continues to the stream later. + +(define (finish-output . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (if (xp.xp-structure-p stream) + (xp.attempt-to-output stream '#t '#t) + (internal-finish-output stream)) + '#f)) + +(define (force-output . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (if (xp.xp-structure-p stream) + (xp.attempt-to-output stream '#t '#t) + (internal-force-output stream)) + '#f)) + +(define (clear-output . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (if (xp.xp-structure-p stream) + (dynamic-let ((*xp.locating-circularities* 0)) ;hack to prevent visible output + (xp.attempt-to-output stream '#t '#t) + (internal-clear-output stream))) + '#f)) + + + + +;;;===================================================================== +;;; Functional interface to dynamic formatting +;;;===================================================================== + +;;; The internal functions in this file, and the (formatter "...") expansions +;;; use the '+' forms of these functions directly (which is faster) because, +;;; they do not need error checking or fancy stream coercion. The '++' forms +;;; additionally assume the thing being output does not contain a newline. + +(define-syntax (pprint-logical-block stream-symbol-stuff . body) + (let* ((stream-symbol (car stream-symbol-stuff)) + (mylist (cadr stream-symbol-stuff)) + (rest (cddr stream-symbol-stuff)) + (prefix (if (not (null? rest)) (pop rest) "")) + (suffix (if (not (null? rest)) (pop rest) "")) + (per-line? (if (not (null? rest)) (pop rest) '#f))) + `(xp.maybe-initiate-xp-printing + (lambda (,stream-symbol) + (let ((+l ,mylist) + (+p ,prefix) + (+s ,suffix) + (+x ,stream-symbol)) + (xp.pprint-logical-block+ (+x +l +p +s ,per-line? '#t '#f) + ,@body + '#f))) + ,stream-symbol))) + + +;;; Assumes var and args must be variables. Other arguments must be literals +;;; or variables. + +(define-syntax (xp.pprint-logical-block+ stuff . body) + (let* ((var (pop stuff)) + (args (pop stuff)) + (prefix (pop stuff)) + (suffix (pop stuff)) + (per-line? (pop stuff))) + `(unless (xp.check-abbreviation ,var ,args) + (dynamic-let ((*xp.current-level* (1+ (dynamic *xp.current-level*))) + (*xp.current-length* -1) + (*xp.parents* (dynamic *xp.parents*))) + (block logical-block + (if (dynamic *print-pretty*) + (xp.start-block ,var ,prefix ,per-line? ,suffix) + (xp.write-prefix-suffix ,prefix ,var)) + (unwind-protect + (begin ,@body) + (if (dynamic *print-pretty*) + (xp.end-block ,var ,suffix) + (xp.write-prefix-suffix ,suffix ,var)))))) + )) + +(define (xp.check-abbreviation xp object) + (cond ((and (dynamic *print-level*) + (>= (dynamic *xp.current-level*) + (dynamic *print-level*))) + (xp.write-char++ #\# XP) + (setf (dynamic *xp.abbreviation-happened*) '#t) + '#t) + ((and (dynamic *xp.circularity-hash-table*) + (eq? (xp.circularity-process xp object '#f) 'subsequent)) + '#t) + (else '#f))) + + +(define-syntax (pprint-pop) + `(xp.pprint-pop+ +l +x)) + +(define-syntax (xp.pprint-pop+ args xp) + `(if (xp.pprint-pop-check+ ,args ,xp) + (return-from logical-block '#f) + (if (null? ,args) '() (pop ,args)))) + +(define (xp.pprint-pop-check+ args xp) + (incf (dynamic *xp.current-length*)) + (cond ((not (or (pair? args) (null? args))) + ;; must be first to supersede length abbreviation + (xp.write-string++ ". " xp 0 2) + (xp.write+ args xp) + '#t) + ((and (dynamic *print-length*) + (not (< *xp.current-length* (dynamic *print-length*)))) + ;; must supersede circularity check + (xp.write-string++ "..." xp 0 3) + (setf (dynamic *xp.abbreviation-happened*) '#t) + '#t) + ((and (dynamic *xp.circularity-hash-table*) + (not (zero? *xp.current-length*))) + (case (xp.circularity-process xp args '#t) + ((first) + (xp.write+ (cons (car args) (cdr args)) xp) '#t) + ((subsequent) + '#t) + (else + '#f))) + (else + '#f))) + +(define-syntax (pprint-exit-if-list-exhausted) + `(xp.pprint-exit-if-list-exhausted+ +l)) + +(define-syntax (xp.pprint-exit-if-list-exhausted+ mylist) + `(if (null? ,mylist) (return-from logical-block '#f))) + + +(define (pprint-newline kind . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (when (not (memq kind '(linear miser fill mandatory))) + (error "Invalid KIND argument ~A to PPRINT-NEWLINE" kind)) + (when (and (xp.xp-structure-p stream) (dynamic *print-pretty*)) + (xp.pprint-newline+ kind stream)) + '#f)) + +(define (pprint-indent relative-to n . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (when (not (memq relative-to '(block current))) + (error "Invalid KIND argument ~A to PPRINT-INDENT" relative-to)) + (when (and (xp.xp-structure-p stream) (dynamic *print-pretty*)) + (xp.pprint-indent+ relative-to n stream)) + '#f)) + +(define (pprint-tab kind colnum colinc . maybe-stream) + (let ((stream (if (not (null? maybe-stream)) + (car maybe-stream) + (current-output-port)))) + (when (not (memq kind '(line section line-relative section-relative))) + (error "Invalid KIND argument ~A to PPRINT-TAB" kind)) + (when (and (xp.xp-structure-p stream) (dynamic *print-pretty*)) + (xp.pprint-tab+ kind colnum colinc stream)) + '#f)) + + + + +;;;===================================================================== +;;; Standard print dispatch function +;;;===================================================================== + + +(define (xp.print-null object xp) + (declare (ignore object)) + (xp.write-string+ "()" xp 0 2)) + +(define (xp.print-true object xp) + (declare (ignore object)) + (xp.write-string+ "#t" xp 0 2)) + +(define (xp.print-false object xp) + (declare (ignore object)) + (xp.write-string+ "#f" xp 0 2)) + +(define (xp.print-symbol object xp) + (if (dynamic *print-escape*) + (xp.print-default object xp) + (let ((mystring (symbol->string object))) + (xp.write-string+ mystring xp 0 (string-length mystring))))) + +(define (xp.print-number object xp) + (if (and (integer? object) + (eqv? (dynamic *print-base*) 10) + (not (dynamic *print-radix*))) + (begin + (when (negative? object) + (xp.write-char++ #\- xp) + (setf object (- object))) + (xp.print-integer object xp)) + (xp.print-default object xp))) + +(define (xp.print-integer n xp) + (let ((quot (quotient n 10)) + (rem (remainder n 10))) + (unless (zero? quot) + (xp.print-integer quot xp)) + (xp.write-char++ (string-ref "0123456789" rem) xp))) + +(define (xp.print-string object xp) + (if (dynamic *print-escape*) + (begin + (xp.write-char++ #\" xp) + (do ((i 0 (1+ i)) + (n (string-length object))) + ((= i n)) + (let ((c (string-ref object i))) + (if (or (char=? c #\") (char=? c #\\)) + (xp.write-char++ #\\ xp)) + (xp.write-char++ c xp))) + (xp.write-char++ #\" xp)) + (xp.write-string+ object xp 0 (string-length object)))) + +(define (xp.print-character object xp) + (if (dynamic *print-escape*) + (let ((name (char-name object))) + (xp.write-char++ #\# xp) + (xp.write-char++ #\\ xp) + (if name + (xp.write-string++ name xp 0 (string-length name)) + (xp.write-char++ object xp))) + (xp.write-char+ object xp))) + +(define (xp.print-vector object xp) + (let* ((pretty? (dynamic *print-pretty*)) + (end (vector-length object))) + (pprint-logical-block (xp '() "#(" ")") + (do ((i 0 (1+ i))) + ((eqv? i end) '#f) + (when (not (eqv? i 0)) + (xp.write-char++ #\space xp) + (if pretty? + (xp.pprint-newline+ 'fill xp))) + (pprint-pop) + (xp.write+ (vector-ref object i) xp) + )))) + +(define (xp.print-table object xp) + (let ((pretty? (dynamic *print-pretty*))) + (pprint-logical-block (xp '() "#") + (table-for-each + (lambda (key value) + (xp.write-char++ #\space xp) + (if pretty? + (xp.pprint-newline+ 'fill xp)) + (pprint-pop) + (xp.write+ (cons key value) xp)) + object)))) + +(define (xp.print-pair object xp) + (if (dynamic *print-pretty*) + (xp.pretty-print-list object xp) + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (do () + ((null? object) '#f) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (when (not (null? object)) (xp.write-char++ #\space xp)))))) + +(define (xp.print-struct object xp) + (if (dynamic *print-structure*) + (print-structure-default object xp) + (funcall (get-structure-printer (struct-type-descriptor object)) + object xp))) + +(define (get-structure-printer td) + (or (td-printer td) + (if (eq? (td-name td) 'struct) + (function print-structure-default) + (get-structure-printer (td-parent-type td))))) + + + +(define (print-structure-default object xp) + (let* ((td (struct-type-descriptor object)) + (slots (td-slots td)) + (pretty? (dynamic *print-pretty*))) + (pprint-logical-block (xp '() "#") + (prin1 (td-name td) xp) + (when (dynamic *print-structure-slots*) + (dolist (s slots) + (write-char #\space xp) + (if pretty? (pprint-newline 'fill xp)) + (pprint-pop) + (prin1 (sd-name s) xp) + (write-char #\space xp) + (write (funcall (sd-getter-function s) object) xp))) + ))) + + +;;; This table can't be initialized until after all the functions +;;; have been defined. + +(define *standard-print-dispatch-table* + (list (cons (function null?) (function xp.print-null)) + (cons (lambda (x) (eq? x '#t)) (function xp.print-true)) + (cons (function not) (function xp.print-false)) + (cons (function symbol?) (function xp.print-symbol)) + (cons (function number?) (function xp.print-number)) + (cons (function pair?) (function xp.print-pair)) + (cons (function string?) (function xp.print-string)) + (cons (function char?) (function xp.print-character)) + (cons (function struct?) (function xp.print-struct)) + (cons (function vector?) (function xp.print-vector)) + (cons (function table?) (function xp.print-table)))) + +(define (standard-print-dispatch object) + (standard-print-dispatch-aux + object (dynamic *standard-print-dispatch-table*))) + +(define (standard-print-dispatch-aux object table) + (cond ((null? table) (function xp.print-default)) + ((funcall (car (car table)) object) + (cdr (car table))) + (else + (standard-print-dispatch-aux object (cdr table))))) + +(setf (dynamic *print-dispatch*) (function standard-print-dispatch)) + + + +;;;===================================================================== +;;; Pretty printing formats for code +;;;===================================================================== + + +;;; The standard prettyprinters for lists dispatch off the CAR of the list. + +(define *xp.pair-dispatch-table* (make-table)) + +(define (xp.pretty-print-list object xp) + (funcall (or (table-entry (dynamic *xp.pair-dispatch-table*) (car object)) + (if (symbol? (car object)) (function xp.fn-call) '#f) + (lambda (object xp) + (pprint-fill xp object))) + object + xp)) + + +;;; Must use pprint-logical-block (no +) in the following three, because they +;;; are exported functions. +;;; *** Note that the argument order on these is backwards; that's the +;;; *** way it is in Common Lisp.... + +(define (pprint-linear s object . moreargs) + (let* ((colon? (if (not (null? moreargs)) (pop moreargs) '#t)) + (atsign? (if (not (null? moreargs)) (pop moreargs) '#f))) + (declare (ignore atsign?)) + (pprint-logical-block (s object (if colon? "(" "") (if colon? ")" "")) + (pprint-exit-if-list-exhausted) + (do () ('#f) + (xp.write+ (pprint-pop) s) + (pprint-exit-if-list-exhausted) + (xp.write-char++ #\space s) + (xp.pprint-newline+ 'linear s))))) + +(define (pprint-fill s object . moreargs) + (let* ((colon? (if (not (null? moreargs)) (pop moreargs) '#t)) + (atsign? (if (not (null? moreargs)) (pop moreargs) '#f))) + (declare (ignore atsign?)) + (pprint-logical-block (s object (if colon? "(" "") (if colon? ")" "")) + (pprint-exit-if-list-exhausted) + (do () ('#f) + (xp.write+ (pprint-pop) s) + (pprint-exit-if-list-exhausted) + (xp.write-char++ #\space s) + (xp.pprint-newline+ 'fill s))))) + +(define (pprint-tabular s object . moreargs) + (let* ((colon? (if (not (null? moreargs)) (pop moreargs) '#t)) + (atsign? (if (not (null? moreargs)) (pop moreargs) '#f)) + (tabsize (or (and (not (null? moreargs)) (pop moreargs)) 16))) + (declare (ignore atsign?)) + (pprint-logical-block (s object (if colon? "(" "") (if colon? ")" "")) + (pprint-exit-if-list-exhausted) + (do () ('#f) + (xp.write+ (pprint-pop) s) + (pprint-exit-if-list-exhausted) + (xp.write-char++ #\space s) + (xp.pprint-tab+ 'section-relative 0 tabsize s) + (xp.pprint-newline+ 'fill s))))) + + +(define (xp.fn-call object xp) + ;; (formatter "~:<~W~^ ~:I~@_~@{~W~^ ~_~}~:>") + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (xp.pprint-exit-if-list-exhausted+ object) + (xp.write-char++ #\space xp) + (xp.pprint-indent+ 'current 0 xp) + (xp.pprint-newline+ 'miser xp) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (xp.write+ (xp.pprint-pop+ object xp) xp)))) + + +;;; Although idiosyncratic, I have found this very useful to avoid large +;;; indentations when printing out code. + +(define (xp.alternative-fn-call object xp) + (if (> (string-length (symbol->string (car object))) 12) + ;; (formatter "~:<~1I~@{~W~^ ~_~}~:>") + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (xp.pprint-indent+ 'block 1 xp) + (when (not (null? object)) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (xp.write+ (xp.pprint-pop+ object xp) xp)))) + (xp.fn-call object xp))) + + +(define (xp.bind-list object xp . args) + (declare (ignore args)) + ;; (formatter "~:<~@{~:/xp:pprint-fill/~^ ~_~}~:>") + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (when (not (null? object)) + (pprint-fill xp (xp.pprint-pop+ object xp) '#t '#f) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (pprint-fill xp (xp.pprint-pop+ object xp) '#t '#f))))) + +(define (xp.fbind-list object xp . args) + (declare (ignore args)) + ;; (formatter "~:<~@{~:/xp:pprint-fill/~^ ~_~}~:>") + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (when (not (null? object)) + (pprint-fill xp (xp.pprint-pop+ object xp) '#t '#f) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (xp.block-like (xp.pprint-pop+ object xp) xp))))) + + +(define (xp.block-like object xp . args) + (declare (ignore args)) + ;; (formatter "~:<~1I~^~W~^ ~@_~W~^~@{ ~_~W~^~}~:>") + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (xp.pprint-indent+ 'block 1 xp) + (xp.pprint-exit-if-list-exhausted+ object) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (xp.pprint-exit-if-list-exhausted+ object) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'miser xp) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (xp.pprint-exit-if-list-exhausted+ object) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (xp.write+ (xp.pprint-pop+ object xp) xp)))) + + +(define (xp.print-fancy-fn-call object xp template) + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (xp.pprint-indent+ 'current 1 xp) + (do ((i 0 (1+ i)) + (in-first-section '#t)) + ((null? object) '#f) + (xp.write-char++ #\space xp) + (when (eqv? i (car template)) + (xp.pprint-indent+ 'block (cadr template) xp) + (setf template (cddr template)) + (setf in-first-section '#f)) + (pprint-newline (cond ((zero? i) 'miser) + (in-first-section 'fill) + (else 'linear)) + xp) + (xp.write+ (xp.pprint-pop+ object xp) xp)))) + +(define (xp.let-print object xp) + ;; (formatter "~:<~1I~W~^ ~@_~/xp:xp.bind-list/~^~@{ ~_~W~^~}~:>") + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (xp.pprint-indent+ 'block 1 xp) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (xp.pprint-exit-if-list-exhausted+ object) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'miser xp) + (xp.bind-list (xp.pprint-pop+ object xp) xp '#f '#f) + (xp.pprint-exit-if-list-exhausted+ object) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (xp.write+ (xp.pprint-pop+ object xp) xp)))) + +(define (xp.flet-print object xp) + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (xp.pprint-indent+ 'block 1 xp) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (xp.pprint-exit-if-list-exhausted+ object) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'miser xp) + (xp.fbind-list (xp.pprint-pop+ object xp) xp '#f '#f) + (xp.pprint-exit-if-list-exhausted+ object) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (xp.write+ (xp.pprint-pop+ object xp) xp)))) + +(define (xp.cond-print object xp) + ;; (formatter "~:<~W~^ ~:I~@_~@{~:/xp:pprint-linear/~^ ~_~}~:>") + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (xp.pprint-exit-if-list-exhausted+ object) + (xp.write-char++ #\space xp) + (xp.pprint-indent+ 'current 0 xp) + (xp.pprint-newline+ 'miser xp) + (pprint-linear xp (xp.pprint-pop+ object xp) '#t '#f) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (pprint-linear xp (xp.pprint-pop+ object xp) '#t '#f)))) + +(define (xp.do-print object xp) + ;; (formatter "~:<~W~^ ~:I~@_~/xp:xp.bind-list/~^ ~_~:/xp:pprint-linear/ ~1I~^~@{ ~_~W~^~}~:>") + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (xp.pprint-exit-if-list-exhausted+ object) + (xp.write-char++ #\space xp) + (xp.pprint-indent+ 'current 0 xp) + (xp.pprint-newline+ 'miser xp) + (xp.bind-list (xp.pprint-pop+ object xp) xp '#f '#f) + (xp.pprint-exit-if-list-exhausted+ object) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (pprint-linear xp (xp.pprint-pop+ object xp) '#t '#f) + (xp.write-char++ #\space xp) + (xp.pprint-indent+ 'block 1 xp) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (xp.write+ (xp.pprint-pop+ object xp) xp)))) + +(define (xp.mvb-print object xp) + (xp.print-fancy-fn-call object xp '(1 3 2 1))) + +(define (xp.setf-print object xp) + ;; (formatter "~:<~W~^ ~:I~@_~@{~W~^ ~:_~W~^ ~_~}~:>") + (xp.pprint-logical-block+ (xp object "(" ")" '#f '#t '#f) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (xp.pprint-exit-if-list-exhausted+ object) + (xp.write-char++ #\space xp) + (xp.pprint-indent+ 'current 0 xp) + (xp.pprint-newline+ 'miser xp) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (do () + ((null? object) '#f) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'fill xp) + (xp.write+ (xp.pprint-pop+ object xp) xp) + (when (not (null? object)) + (xp.write-char++ #\space xp) + (xp.pprint-newline+ 'linear xp) + (xp.write+ (xp.pprint-pop+ object xp) xp))))) + +(define (xp.quote-print object xp) + (if (and (pair? (cdr object)) (null? (cddr object))) + (begin + (xp.write-char++ #\' xp) + (xp.write+ (cadr object) xp)) + (pprint-fill xp object))) + +(define (xp.up-print object xp) + (xp.print-fancy-fn-call object xp '(0 3 1 1))) + + +;;; Install printers for built-in macros and special forms into the +;;; standard dispatch table. + +(define-local-syntax (define-printer symbol function) + `(setf (table-entry (dynamic *xp.pair-dispatch-table*) ',symbol) + (function ,function))) + + +;;; *** Missing support for backquote here. + +(define-printer quote xp.quote-print) +(define-printer lambda xp.block-like) +(define-printer when xp.block-like) +(define-printer unless xp.block-like) +(define-printer cond xp.cond-print) +(define-printer case xp.block-like) +(define-printer setf xp.setf-print) +(define-printer set! xp.setf-print) +(define-printer let xp.let-print) +(define-printer let* xp.let-print) +(define-printer letrec xp.let-print) +(define-printer flet xp.flet-print) +(define-printer labels xp.flet-print) +(define-printer dynamic-let xp.let-print) +(define-printer block xp.block-like) +(define-printer do xp.do-print) +(define-printer dolist xp.block-like) +(define-printer dotimes xp.block-like) +(define-printer multiple-value-bind xp.mvb-print) +(define-printer let/cc xp.block-like) +(define-printer unwind-protect xp.up-print) +(define-printer define xp.block-like) +(define-printer define-syntax xp.block-like) +(define-printer define-local-syntax xp.block-like) +(define-printer pprint-logical-block xp.block-like) +(define-printer xp.pprint-logical-block+ xp.block-like) + +;;; Here are some hacks for struct macros. + +(define-printer update-slots xp.mvb-print) +(define-printer make xp.block-like) diff --git a/support/support.scm b/support/support.scm new file mode 100644 index 0000000..bdecc4f --- /dev/null +++ b/support/support.scm @@ -0,0 +1,35 @@ +;;; support.scm -- load support files shared by all systems +;;; +;;; author : Sandra Loosemore +;;; date : 28 Oct 1991 +;;; +;;; + + +;;; Keep track of all compilation units defined. +;;; This has to go here and not in compile.scm because we don't want this +;;; list reinitialized every time that file is loaded. + +(define compilation-units '()) + + +;;; Load this file first; it defines the basic compilation system support. +;;; It doesn't matter if this ends up loading source because we'll compile +;;; and reload it below. + +(load "$Y2/support/compile.scm") + + +;;; Define a real compilation unit for shared support files. + +(define-compilation-unit support + (source-filename "$Y2/support/") + (unit compile (source-filename "compile.scm")) + (unit utils (source-filename "utils.scm")) + (unit xp + (unit pprint (source-filename "pprint.scm")) + (unit format (source-filename "format.scm") + (require pprint))) + ) + + diff --git a/support/system.scm b/support/system.scm new file mode 100644 index 0000000..ac03b10 --- /dev/null +++ b/support/system.scm @@ -0,0 +1,51 @@ +;;; system.scm -- haskell system setup +;;; +;;; author : Sandra Loosemore +;;; date : 22 Nov 1991 +;;; +;;; This file loads in the compilation unit definition files for all +;;; of the components of the haskell system. +;;; +;;; (The compilation unit facility is defined in support/shared/compile.scm.) + + +;;; First load the files containing module definitions. +;;; *** Add more files to the end of this list. + +(load "$Y2/support/support") +(load "$Y2/ast/ast") +(load "$Y2/top/top") +(load "$Y2/util/haskell-utils") +(load "$Y2/printers/printers") +(load "$Y2/parser/parser") +(load "$Y2/import-export/ie.scm") +(load "$Y2/tdecl/tdecl.scm") +(load "$Y2/derived/derived.scm") +(load "$Y2/prec/prec.scm") +(load "$Y2/depend/depend.scm") +(load "$Y2/type/type.scm") +(load "$Y2/cfn/cfn.scm") +(load "$Y2/flic/flic.scm") +(load "$Y2/backend/backend.scm") +(load "$Y2/runtime/runtime.scm") +(load "$Y2/csys/csys") +(load "$Y2/command-interface/command-interface") + +;;; Define some functions to actually do the work. The compilation unit +;;; facility has conveniently kept a list of all of the unit definitions, +;;; so we can just rip through them in sequence. + +(define (compile-haskell) + (compile-and-load-unit-list compilation-units)) + +(define (recompile-haskell) + (unless (null? remaining-units) + (compile-and-load-unit-list remaining-units))) + + +(define (load-haskell) + (load-unit-list compilation-units)) + +(define (reload-haskell) + (unless (null? remaining-units) + (load-unit-list remaining-units))) diff --git a/support/utils.scm b/support/utils.scm new file mode 100644 index 0000000..ab93f6b --- /dev/null +++ b/support/utils.scm @@ -0,0 +1,408 @@ +;;; utils.scm -- utility functions +;;; +;;; author : Sandra Loosemore +;;; date : 18 Nov 1991 +;;; +;;; This file contains miscellaneous functions that are generally useful. +;;; If you find some missing feature from the base language, this is +;;; a good place to put it. Common Lisp-style sequence functions are +;;; an example of the sort of thing found here. + + +;;;===================================================================== +;;; Sequence functions +;;;===================================================================== + +(define (vector-replace to-vec from-vec to start end) + (declare (type fixnum to start end) + (type vector to-vec from-vec)) + (if (and (eq? to-vec from-vec) + (> to start)) + ;; Right shift in place + (do ((from (1- end) (1- from)) + (to (1- (+ to (- end start))))) + ((< from start) to-vec) + (declare (type fixnum from to)) + (setf (vector-ref to-vec to) (vector-ref from-vec from)) + (decf to)) + ;; Normal case, left-to-right + (do ((from start (1+ from))) + ((= from end) to-vec) + (declare (type fixnum from)) + (setf (vector-ref to-vec to) (vector-ref from-vec from)) + (incf to)))) + +(define (string-replace to-vec from-vec to start end) + (declare (type fixnum to start end) + (type string to-vec from-vec)) + (if (and (eq? to-vec from-vec) + (> to start)) + ;; Right shift in place + (do ((from (1- end) (1- from)) + (to (1- (+ to (- end start))))) + ((< from start) to-vec) + (declare (type fixnum from to)) + (setf (string-ref to-vec to) (string-ref from-vec from)) + (decf to)) + ;; Normal case, left-to-right + (do ((from start (1+ from))) + ((= from end) to-vec) + (declare (type fixnum from)) + (setf (string-ref to-vec to) (string-ref from-vec from)) + (incf to)))) + +(define (string-fill string c start end) + (declare (type fixnum start end) + (type string string) + (type char c)) + (do ((i start (1+ i))) + ((= i end) string) + (declare (type fixnum i)) + (setf (string-ref string i) c))) + +(define (string-position c string start end) + (declare (type fixnum start end) + (type string string) + (type char c)) + (cond ((= start end) '#f) + ((char=? (string-ref string start) c) start) + (else + (string-position c string (1+ start) end)))) + +(define (string-position-not-from-end c string start end) + (declare (type fixnum start end) + (type string string) + (type char c)) + (cond ((= start end) '#f) + ((not (char=? (string-ref string (setf end (1- end))) c)) + end) + (else + (string-position-not-from-end c string start end)))) + +(define (string-nreverse string start end) + (declare (type fixnum start end) + (type string string)) + (do ((i start (1+ i)) + (j (1- end) (1- j))) + ((not (< i j)) string) + (declare (type fixnum i j)) + (let ((c (string-ref string i))) + (setf (string-ref string i) (string-ref string j)) + (setf (string-ref string j) c)))) + + +(define (string-starts? s1 s2) ; true is s1 begins s2 + (and (>= (string-length s2) (string-length s1)) + (string=? s1 (substring s2 0 (string-length s1))))) + + +;;;===================================================================== +;;; Table utilities +;;;===================================================================== + + +(define (table->list table) + (let ((l '())) + (table-for-each + (lambda (key val) (push (cons key val) l)) table) + l)) + +(define (list->table l) + (let ((table (make-table))) + (dolist (p l) + (setf (table-entry table (car p)) (cdr p))) + table)) + + + +;;;===================================================================== +;;; Tuple utilities +;;;===================================================================== + +;;; For future compatibility with a typed language, define 2 tuples with +;;; a few functions: (maybe add 3 tuples someday!) + +(define-integrable (tuple x y) + (cons x y)) + +(define-integrable (tuple-2-1 x) (car x)) ; Flic-like notation +(define-integrable (tuple-2-2 x) (cdr x)) + +(define (map-tuple-2-1 f l) + (map (lambda (x) (tuple (funcall f (tuple-2-1 x)) (tuple-2-2 x))) l)) + +(define (map-tuple-2-2 f l) + (map (lambda (x) (tuple (tuple-2-1 x) (funcall f (tuple-2-2 x)))) l)) + + +;;;===================================================================== +;;; List utilities +;;;===================================================================== + +;;; This does an assq using the second half of the tuple as the key. + +(define (rassq x l) + (if (null? l) + '#f + (if (eq? x (tuple-2-2 (car l))) + (car l) + (rassq x (cdr l))))) + +;;; This is an assoc with an explicit test + +(define (assoc/test test-fn x l) + (if (null? l) + '#f + (if (funcall test-fn x (tuple-2-1 (car l))) + (car l) + (assoc/test test-fn x (cdr l))))) + + + + +;;; Stupid position function works only on lists, uses eqv? + +(define (position item list) + (position-aux item list 0)) + +(define (position-aux item list index) + (declare (type fixnum index)) + (cond ((null? list) + '#f) + ((eqv? item (car list)) + index) + (else + (position-aux item (cdr list) (1+ index))) + )) + + +;;; Destructive delete-if function + +(define (list-delete-if f l) + (list-delete-if-aux f l l '#f)) + +(define (list-delete-if-aux f head next last) + (cond ((null? next) + ;; No more elements. + head) + ((not (funcall f (car next))) + ;; Leave this element and do the next. + (list-delete-if-aux f head (cdr next) next)) + (last + ;; Delete element from middle of list. + (setf (cdr last) (cdr next)) + (list-delete-if-aux f head (cdr next) last)) + (else + ;; Delete element from head of list. + (list-delete-if-aux f (cdr next) (cdr next) last)))) + + +;;; Same as the haskell function + +(define (concat lists) + (if (null? lists) + '() + (append (car lists) (concat (cdr lists))))) + + +;;; This is a quick & dirty list sort function. + +(define (sort-list l compare-fn) + (if (or (null? l) (null? (cdr l))) + l + (insert-sorted compare-fn (car l) (sort-list (cdr l) compare-fn)))) + +(define (insert-sorted compare-fn e l) + (if (null? l) + (list e) + (if (funcall compare-fn e (car l)) + (cons e l) + (cons (car l) (insert-sorted compare-fn e (cdr l)))))) + +(define (find-duplicates l) + (cond ((null? l) + '()) + ((memq (car l) (cdr l)) + (cons (car l) + (find-duplicates (cdr l)))) + (else (find-duplicates (cdr l))))) + +;;; A simple & slow topsort routine. +;;; Input: A list of lists. Each list is a object consed onto the +;;; list of objects it preceeds. +;;; Output: Two values: SORTED / CYCLIC & a list of either sorted objects +;;; or a set of components containing the cycle. + +(define (topsort l) + (let ((changed? '#t) + (sorted '()) + (next '())) + (do () ((not changed?) + (if (null? next) + (values 'sorted (nreverse sorted)) + (values 'cyclic (map (function car) next)))) + (setf changed? '#f) + (setf next '()) + (dolist (x l) + (cond ((topsort-aux (cdr x) sorted) + (push (car x) sorted) + (setf changed? '#t)) + (else + (push x next)))) + (setf l next)))) + + +;;; Returns true if x doesn't contain any elements that aren't in sorted. +;;; equivalent to (null? (set-intersection x sorted)), but doesn't cons +;;; and doesn't traverse the whole list in the failure case. + +(define (topsort-aux x sorted) + (cond ((null? x) + '#t) + ((memq (car x) sorted) + (topsort-aux (cdr x) sorted)) + (else + '#f))) + +(define (set-intersection s1 s2) + (if (null? s1) + '() + (let ((rest (set-intersection (cdr s1) s2))) + (if (memq (car s1) s2) + (cons (car s1) rest) + rest)))) + +;;; remove s2 elements from s1 + +(define (set-difference s1 s2) + (if (null? s1) + '() + (let ((rest (set-difference (cdr s1) s2))) + (if (memq (car s1) s2) + rest + (cons (car s1) rest))))) + + +(define (set-union s1 s2) + (if (null? s2) + s1 + (if (memq (car s2) s1) + (set-union s1 (cdr s2)) + (cons (car s2) (set-union s1 (cdr s2)))))) + + +;;; Destructive list splitter + +(define (split-list list n) + (declare (type fixnum n)) + (let ((tail1 (list-tail list (1- n)))) + (if (null? tail1) + (values list '()) + (let ((tail2 (cdr tail1))) + (setf (cdr tail1) '()) + (values list tail2))))) + + +;;; Some string utils + +(define (mem-string s l) + (and (not (null? l)) (or (string=? s (car l)) + (mem-string s (cdr l))))) + +(define (ass-string k l) + (cond ((null? l) + '#f) + ((string=? k (caar l)) + (car l)) + (else + (ass-string k (cdr l))))) + + +;;;===================================================================== +;;; Syntax extensions +;;;===================================================================== + +;;; The mlet macro combines let* and multiple-value-bind into a single +;;; syntax. + +(define-syntax (mlet binders . body) + (mlet-body binders body)) + +(define (mlet-body binders body) + (if (null? binders) + `(begin ,@body) + (let* ((b (car binders)) + (var (car b)) + (init (cadr b)) + (inner-body (mlet-body (cdr binders) body))) + (if (pair? var) + (multiple-value-bind (new-vars ignore-decl) + (remove-underlines var) + `(multiple-value-bind ,new-vars + ,init ,@ignore-decl ,inner-body)) + `(let ((,var ,init)) ,inner-body))))) + +(define (remove-underlines vars) + (if (null? vars) + (values '() '()) + (multiple-value-bind (rest ignore-decl) (remove-underlines (cdr vars)) + (if (not (eq? (car vars) '_)) + (values (cons (car vars) rest) ignore-decl) + (let ((var (gensym))) + (values (cons var rest) + `((declare (ignore ,var)) ,@ignore-decl))))))) + + + + +;;;===================================================================== +;;; Other utilities +;;;===================================================================== + +(define (add-extension name ext) + (assemble-filename (filename-place name) (filename-name name) ext)) + +(define (time-execution thunk) + (let* ((start-time (get-run-time)) + (res (funcall thunk)) + (end-time (get-run-time))) + (values res (- end-time start-time)))) + +(define (pprint-flatten code . maybe-port) + (pprint-flatten-aux + code + (if (null? maybe-port) (current-output-port) (car maybe-port)))) + +(define (pprint-flatten-aux code port) + (if (and (pair? code) + (eq? (car code) 'begin)) + (dolist (c (cdr code)) + (pprint-flatten-aux c port)) + (pprint*-aux code port))) + +(define (print-flatten code port) + (if (and (pair? code) + (eq? (car code) 'begin)) + (dolist (c (cdr code)) + (print-flatten c port)) + (begin + (internal-write code port) + (internal-newline port)))) + + +;;; Like pprint, but print newline after instead of before. + +(define (pprint* object . maybe-port) + (pprint*-aux + object + (if (null? maybe-port) (current-output-port) (car maybe-port)))) + +(define (pprint*-aux object port) + (dynamic-let ((*print-pretty* '#t)) + (prin1 object port)) + (terpri port)) + +;;; This reads stuff from a string. (Better error checks needed!) + +(define (read-lisp-object str) + (call-with-input-string str (lambda (port) (read port)))) diff --git a/tdecl/README b/tdecl/README new file mode 100644 index 0000000..62e6b0a --- /dev/null +++ b/tdecl/README @@ -0,0 +1,2 @@ +This directory contains code to convert type-related declarations to +definition form. diff --git a/tdecl/alg-syn.scm b/tdecl/alg-syn.scm new file mode 100644 index 0000000..b128486 --- /dev/null +++ b/tdecl/alg-syn.scm @@ -0,0 +1,228 @@ + +;;; Description: Convert algdata & synonym from ast to definition form. +;;; Lots of error checking. + +;;; Algdata: +;;; Errors detected: +;;; Types & classes (deriving & context) resolved +;;; context tyvars must be parameters +;;; all parameter tyvars must be referenced +;;; only parameter tyvars must be referenced + +(define (algdata->def data-decl) + (remember-context data-decl + (with-slots data-decl (context simple constrs deriving annotations) data-decl + (let* ((def (tycon-def simple)) + (tyvars (simple-tyvar-list simple)) + (enum? '#t) + (tag 0) + (derived-classes '()) + (tyvars-referenced '()) + (all-con-vars '()) + (all-strict? (process-alg-strictness-annotation annotations)) + (constr-defs + (map (lambda (constr) + (with-slots constr (constructor types) constr + (let ((constr-def (con-ref-con constructor)) + (c-arity (length types)) + (con-vars '()) + (all-types '()) + (strictness '())) + (when (not (eqv? c-arity 0)) + (setf enum? '#f)) + (dolist (type types) + (let* ((ty (tuple-2-1 type)) + (anns (tuple-2-2 type)) + (tyvars1 (resolve-type ty))) + (push ty all-types) + (push (get-constr-strictness anns all-strict?) + strictness) + (dolist (v tyvars1) + (if (not (memq v tyvars)) + (signal-bad-algdata-tyvar v))) + (setf con-vars (append tyvars1 tyvars-referenced)) + (setf tyvars-referenced + (append tyvars1 tyvars-referenced)))) + (push (tuple constr con-vars) all-con-vars) + (update-slots con constr-def + (arity c-arity) + (types (reverse all-types)) + (tag tag) + (alg def) + (infix? (con-ref-infix? constructor)) + (slot-strict? (reverse strictness))) + (incf tag) + constr-def))) + constrs))) + (dolist (class deriving) + (if (eq? (class-ref-name class) '|Printers|) + (setf (class-ref-class class) *printer-class*) + (resolve-class class)) + (when (not (eq? (class-ref-class class) *undefined-def*)) + (push (class-ref-class class) derived-classes))) + (when (not (null? constrs)) + (dolist (tyvar tyvars) + (when (not (memq tyvar tyvars-referenced)) + (signal-unreferenced-tyvar-arg tyvar)))) + (resolve-signature-aux tyvars context) + ;; This computes a signature for the datatype as a whole. + (let ((gtype (ast->gtype context simple))) + ;; This sets the signatures for the constructors + (dolist (con constr-defs) + (let* ((con-type (**arrow-type/l (append (con-types con) + (list simple)))) + (con-context (restrict-context + context (tuple-2-2 (assq con all-con-vars)))) + (con-signature (ast->gtype con-context con-type))) + (setf (con-signature con) con-signature))) + (update-slots algdata def + (n-constr (length constrs)) + (constrs constr-defs) + (context context) + (tyvars tyvars) + (signature gtype) + (classes '()) + (enum? enum?) + (tuple? (and (not (null? constrs)) (null? (cdr constrs)))) + (real-tuple? '#f) + (deriving derived-classes) + )) + (process-alg-annotations def) + def)))) + + +(define (process-alg-strictness-annotation anns) + (let ((res '#f)) + (dolist (a anns) + (if (and (annotation-value? a) + (eq? (annotation-value-name a) '|STRICT|) + (null? (annotation-value-args a))) + (setf res '#t) + (signal-unknown-annotation a))) + res)) + +(define (get-constr-strictness anns all-strict?) + (let ((res all-strict?)) + (dolist (a anns) + (cond ((annotation-value? a) + (if (and (eq? (annotation-value-name a) '|STRICT|) + (null? (annotation-value-args a))) + (setf res '#t) + (signal-unknown-annotation a))) + (else (signal-unknown-annotation a)))) + res)) + +(define (process-alg-annotations alg) + (dolist (a (module-annotations *module*)) + (when (and (annotation-value? a) + (or (eq? (annotation-value-name a) '|ImportLispType|) + (eq? (annotation-value-name a) '|ExportLispType|)) + (assq (def-name alg) (car (annotation-value-args a)))) + (if (eq? (annotation-value-name a) '|ImportLispType|) + (setf (algdata-implemented-by-lisp? alg) '#t) + (setf (algdata-export-to-lisp? alg) '#t)) + (let ((constrs (tuple-2-2 (assq (def-name alg) + (car (annotation-value-args a)))))) + (dolist (c constrs) + (process-annotated-constr + alg + (lookup-alg-constr (tuple-2-1 c) (algdata-constrs alg)) + (tuple-2-2 c))))))) + +(define (lookup-alg-constr name constrs) + (if (null? constrs) + (fatal-error 'bad-constr-name "Constructor ~A not in algdata~%" + name) + (if (eq? name (def-name (car constrs))) + (car constrs) + (lookup-alg-constr name (cdr constrs))))) + +(define (process-annotated-constr alg con lisp-fns) + ;; For nullary tuples, allow a single annotation to represent a constant + ;; and generate the test function by default. + (when (and (eqv? (con-arity con) 0) + lisp-fns + (null? (cdr lisp-fns))) + (push `(lambda (x) (eq? x ,(car lisp-fns))) lisp-fns)) + ;; Insert an implicit test function for tuples (never used anyway!) + (when (and (algdata-tuple? alg) + (eqv? (+ 1 (con-arity con)) (length lisp-fns))) + (push '(lambda (x) '#t) lisp-fns)) + (when (or (not (null? (con-lisp-fns con))) + (not (eqv? (length lisp-fns) (+ 2 (con-arity con))))) + (fatal-error 'bad-constr-annotation + "Bad annotation for ~A in ~A~%" con alg)) + (setf (con-lisp-fns con) lisp-fns)) + +(define (signal-unknown-annotation a) + (recoverable-error 'bad-annotation "Bad or misplaced annotation: ~A%" + a)) + +(define (restrict-context context vars) + (if (null? context) + '() + (let ((rest (restrict-context (cdr context) vars))) + (if (memq (context-tyvar (car context)) vars) + (cons (car context) rest) + rest)))) + +(define (signal-bad-algdata-tyvar tyvar) + (phase-error 'bad-algdata-tyvar + "~a is referenced on the right-hand side of a data type declaration,~%~ + but is not bound as a type variable." + tyvar)) + +(define (signal-unreferenced-tyvar-arg tyvar) + (phase-error 'unreferenced-tyvar-arg + "~a is bound as a type variable in a data type declaration,~%~ + but is not referenced on the right-hand side." + tyvar)) + +;;; Synonyms + +;;; Errors detected: + +(define (synonym->def synonym-decl) + (remember-context synonym-decl + (with-slots synonym-decl (simple body) synonym-decl + (let* ((def (tycon-def simple)) + (tyvars (simple-tyvar-list simple)) + (tyvars-referenced (resolve-type body))) + (dolist (v tyvars) + (if (not (memq v tyvars-referenced)) + (signal-unreferenced-synonym-arg v))) + (dolist (v tyvars-referenced) + (if (not (memq v tyvars)) + (signal-bad-synonym-tyvar v))) + (update-slots synonym def + (args tyvars) + (body body)) + (push (cons def (gather-synonyms body '())) *synonym-refs*) + def)))) + +(define (signal-bad-synonym-tyvar tyvar) + (phase-error 'bad-synonym-tyvar + "~a is referenced on the right-hand side of a type synonym declaration,~%~ + but is not bound as a type variable." + tyvar)) + +(define (signal-unreferenced-synonym-arg tyvar) + (haskell-warning 'unreferenced-synonym-arg + "~a is bound as a type variable in a type synonym declaration,~%~ + but is not referenced on the right-hand side." + tyvar)) + +(define (gather-synonyms type acc) + (cond ((tyvar? type) + acc) + ((and (synonym? (tycon-def type)) + (eq? *unit* (def-unit (tycon-def type)))) + (gather-synonyms/list (tycon-args type) + (cons (tycon-def type) acc))) + (else + (gather-synonyms/list (tycon-args type) acc)))) + +(define (gather-synonyms/list types acc) + (if (null? types) + acc + (gather-synonyms/list (cdr types) (gather-synonyms (car types) acc)))) diff --git a/tdecl/class.scm b/tdecl/class.scm new file mode 100644 index 0000000..c95bbc2 --- /dev/null +++ b/tdecl/class.scm @@ -0,0 +1,258 @@ +;;; Before classes are converted, the super class relation is computed. +;;; This sets up the super and super* field of each class and +;;; checks for the following errors: +;;; Wrong tyvar in context +;;; cyclic class structure +;;; Non-class in context + +(define (compute-super-classes modules) + (let ((all-classes '())) + (walk-modules modules + (lambda () + (dolist (c (module-classes *module*)) + (remember-context c + (with-slots class-decl (super-classes class class-var) c + (let* ((def (class-ref-class class)) + (local-ctxts '()) + (super '())) + (dolist (context super-classes) + (with-slots context (class tyvar) context + (when (not (eq? class-var tyvar)) + (signal-super-class-tyvar-error class class-var tyvar)) + (resolve-class class) + (let ((super-def (class-ref-class class))) + (when (not (eq? super-def *undefined-def*)) + (push super-def super) + (when (eq? *unit* (def-unit super-def)) + (push super-def local-ctxts)))))) + (update-slots class def + (super super) + (tyvar class-var)) + (push (cons def local-ctxts) all-classes))))))) + (multiple-value-bind (status sorted) (topsort all-classes) + (when (eq? status 'cyclic) + (signal-cyclic-class-structure sorted)) + (dolist (c sorted) + (let* ((super (class-super c)) + (super* super)) + (dolist (s super) + (setf super* (set-union super* (class-super* s))) + (setf (class-super* c) super*))))))) + +(define (signal-super-class-tyvar-error class class-var tyvar) + (recoverable-error 'super-class-tyvar-error + "The context for class ~A must only refer to type variable ~A.~%~ + Type variable ~A cannot be used here." + (class-ref-name class) class-var tyvar)) + +(define (signal-cyclic-class-structure classes) + (fatal-error 'cyclic-class-structure + "There is a cycle in the superclass relation involving these classes:~%~a" + classes)) + + +;;; This sets up the following fields in the class entry: +;;; instances '() +;;; defaults = ast for defaults +;;; kind +;;; methods +;;; signatures +;;; method-vars +;;; selectors +;;; Each method is initialized with +;;; class +;;; signature +;;; type +;;; Errors detected: +;;; signature doesnt reference class + +(define (class->def class-decl) + (remember-context class-decl + (let* ((class (class-ref-class (class-decl-class class-decl))) + (decls (class-decl-decls class-decl))) + (setf (class-instances class) '()) + (setf (class-kind class) (find-class-kind class)) + (init-methods class decls) ; sets up defaults, method signatures + (setf (class-n-methods class) (length (class-method-vars class))) + (setf (class-dict-size class) + (+ (class-n-methods class) (length (class-super* class)))) + class))) + +(define (find-class-kind class) + (cond ((not (module-prelude? *module*)) + 'other) + ((memq class + (list (core-symbol "Eq") (core-symbol "Ord") + (core-symbol "Text") (core-symbol "Binary") + (core-symbol "Ix") (core-symbol "Enum"))) + 'Standard) + ((memq class + (list (core-symbol "Num") (core-symbol "Real") + (core-symbol "Integral") (core-symbol "Fractional") + (core-symbol "Floating") (core-symbol "RealFrac") + (core-symbol "RealFloat"))) + 'Numeric) + (else + 'other))) + +(define (init-methods class decls) + (let* ((tyvar (class-tyvar class)) + (class-context (**context (**class/def class) tyvar))) + (dolist (decl decls) + (remember-context decl + (cond ((is-type? 'signdecl decl) + (let* ((signature (signdecl-signature decl)) + (vars (resolve-signature signature))) + (when (not (memq tyvar vars)) + (signal-class-sig-ignores-type signature)) + ;; Note: signature does not include defined class yet + (dolist (context (signature-context signature)) + (when (eq? tyvar (context-tyvar context)) + (signal-method-constrains-class-tyvar context))) + (setf signature (rename-class-sig-vars signature tyvar)) + (let ((gtype (ast->gtype (cons class-context + (signature-context signature)) + (signature-type signature)))) + (dolist (var-ref (signdecl-vars decl)) + (let ((var (var-ref-var var-ref))) + (setf (var-type var) gtype) + (setf (method-var-method-signature var) signature)))))) + (else ; decl must be a default definition + (let ((vars (collect-pattern-vars (valdef-lhs decl)))) + (dolist (var-ref vars) + (resolve-var var-ref) + (let* ((method-name (var-ref-name var-ref)) + (method-var (var-ref-var var-ref))) + (when (not (eq? method-var *undefined-def*)) + (if (and (method-var? method-var) + (eq? (method-var-class method-var) class)) + (let ((default-var + (make-new-var + (string-append + "default-" + (symbol->string (def-name method-var)))))) + (setf (var-ref-var var-ref) default-var) + (setf (var-ref-name var-ref) (def-name default-var)) + (when (not (eq? (method-var-default method-var) '#f)) + (signal-multiple-definition-of-default method-name)) + (setf (method-var-default method-var) default-var) + (let* ((sig (method-var-method-signature method-var)) + (context (cons class-context + (signature-context sig))) + (new-sig (**signature context + (signature-type sig)))) + (add-new-module-signature default-var new-sig))) + (signal-default-not-in-class method-var class))))) + (add-new-module-decl decl)))))))) + +(define (signal-class-sig-ignores-type signature) + (phase-error 'class-sig-ignores-type + "The method signature ~a does not reference the overloaded type." + signature)) + + +;;; *** I don't understand this message. + +(define (signal-method-constrains-class-tyvar context) + (phase-error 'method-constrains-class-tyvar + "Individual methods may not further constrain a class: ~A" context)) + + +;;; *** I don't understand this message. + +(define (signal-multiple-definition-of-default method-name) + (phase-error 'multiple-definition-of-default + "More that one default for ~A." + method-name)) + + +;;; *** I don't understand this message. + +(define (signal-default-not-in-class method-var class) + (phase-error 'default-not-in-class + "~A is not a method in class ~A." + method-var class)) + + +(define (create-selector-functions class) + (let ((res '())) + (dolist (c (cons class (class-super* class))) + (dolist (m (class-method-vars c)) + (let* ((var (make-new-var + (string-append "sel-" + (symbol->string (def-name class)) + "/" + (symbol->string (def-name m))))) + (sel-body (create-selector-code class m))) + (setf (var-selector-fn? var) '#t) + (push (tuple m var) res) + (when (not (eq? (module-type *module*) 'interface)) + (add-new-module-def var sel-body))))) + res)) + +(define (create-selector-code c m) + (let ((var (create-local-definition '|d|))) + (setf (var-force-strict? var) '#t) + (let ((body (create-selector-code-1 c m (**var/def var)))) + (**lambda/pat (list (**var-pat/def var)) body)))) + +(define (create-selector-code-1 class method d) + (let ((mcl (method-var-class method))) + (cond ((eq? mcl class) + (**dsel/method class method d)) + (else + (**dsel/method mcl method (**dsel/dict class mcl d)))))) + +;;; The following code is for the alpha conversion of method +;;; signatures. The class tyvar is unchanged; all others are renamed. +;;; This is needed because all method types are combined to form the +;;; dictionary signature and aliasing among different tyvars should be +;;; prevented. + +(define (rename-class-sig-vars signature tyvar) + (mlet (((new-context env1) + (rename-context-vars (signature-context signature) + (list (tuple tyvar tyvar)))) + ((new-type _) + (rename-type-vars (signature-type signature) env1))) + (**signature new-context new-type))) + +(define (rename-context-vars contexts env) + (if (null? contexts) + (values '() env) + (mlet (((new-tyvar env1) + (rename-sig-tyvar (context-tyvar (car contexts)) env)) + ((rest env2) + (rename-context-vars (cdr contexts) env1))) + (values (cons (**context (context-class (car contexts)) new-tyvar) rest) + env2)))) + +(define (rename-type-vars type env) + (if (tyvar? type) + (mlet (((tyvar env1) + (rename-sig-tyvar (tyvar-name type) env))) + (values (**tyvar tyvar) env1)) + (mlet (((new-types env1) (rename-type-vars/l (tycon-args type) env))) + (values (**tycon/def (tycon-def type) new-types) env1)))) + +(define (rename-type-vars/l types env) + (if (null? types) + (values '() env) + (mlet (((type1 env1) (rename-type-vars (car types) env)) + ((new-types env2) (rename-type-vars/l (cdr types) env1))) + (values (cons type1 new-types) env2)))) + +(define (rename-sig-tyvar tyvar env) + (let ((res (assq tyvar env))) + (if (eq? res '#f) + (let ((new-tyvar (gentyvar (symbol->string tyvar)))) + (values new-tyvar (cons (tuple tyvar new-tyvar) env))) + (values (tuple-2-2 res) env)))) + +(define *tyvar-counter* 0) + +;;; This generates a new interned tyvar name + +(define (gentyvar root) + (incf *tyvar-counter*) + (string->symbol (format '#f "~A-~A" root *tyvar-counter*))) diff --git a/tdecl/instance.scm b/tdecl/instance.scm new file mode 100644 index 0000000..1866339 --- /dev/null +++ b/tdecl/instance.scm @@ -0,0 +1,296 @@ +;;; tdecl/instance.scm + +;;; Convert an instance decl to a definition + +;;; The treatment of instances is more complex than the treatment of other +;;; type definitions due to the possibility of derived instances. +;;; Here's the plan: +;;; a) instance-decls are converted to instance structures. The type +;;; information is verified but the decls are unchanged. +;;; b) All instances are linked into the associated classes. +;;; c) Derived instances are generated. +;;; d) Instance dictionaries are generated from the decls in the instances. +;;; + +;;; Instances-decl to instance definition conversion +;;; Errors detected: +;;; Class must be a class +;;; Data type must be an alg +;;; Tyvars must be distinct +;;; Correct number of tyvars +;;; Context applies only to tyvars in simple +;;; C-T restriction + +;;; Needs work for interface files. + +(define (instance->def inst-decl) + (recover-errors '#f + (remember-context inst-decl + (with-slots instance-decl (context class simple decls) inst-decl + (resolve-type simple) + (resolve-class class) + (let ((alg-def (tycon-def simple)) + (class-def (class-ref-class class))) + (when (not (algdata? (tycon-def simple))) + (signal-datatype-required (tycon-def simple))) + (let ((tyvars (simple-tyvar-list simple))) + (resolve-signature-aux tyvars context) + (when (and (not (eq? *module-name* (def-module alg-def))) + (not (eq? *module-name* (def-module class-def)))) + (signal-c-t-rule-violation class-def alg-def)) + (let ((old-inst (lookup-instance alg-def class-def))) + (when (and (not (eq? old-inst '#f)) + (not (instance-special? old-inst))) + (signal-multiple-instance class-def alg-def)) + (let ((inst (new-instance class-def alg-def tyvars))) + (setf (instance-context inst) context) + (setf (instance-decls inst) decls) + (setf (instance-ok? inst) '#t) + inst)))))))) + +(define (signal-datatype-required def) + (phase-error 'datatype-required + "The synonym type ~a cannot be declared as an instance." + (def-name def))) + +(define (signal-c-t-rule-violation class-def alg-def) + (phase-error 'c-t-rule-violation + "Instance declaration does not appear in the same module as either~%~ + the class ~a or type ~a." + class-def alg-def)) + +(define (signal-multiple-instance class-def alg-def) + (phase-error 'multiple-instance + "The type ~a has already been declared to be an instance of class ~a." + alg-def class-def)) + +;;; This generates the dictionary for each instance and makes a few final +;;; integrity checks in the instance context. This happens after derived +;;; instances are inserted. + +(define (expand-instance-decls inst) + (when (instance-ok? inst) + (check-inst-type inst) + (with-slots instance (class algdata dictionary decls context tyvars) inst + (let ((simple (**tycon/def algdata (map (function **tyvar) tyvars)))) + (setf (instance-gcontext inst) + (gtype-context (ast->gtype/inst context simple))) + (with-slots class (super* method-vars) class + ;; Before computing signatures uniquify tyvar names to prevent + ;; collision with method tyvar names + (let ((new-tyvars (map (lambda (tyvar) (tuple tyvar (gentyvar "tv"))) + (instance-tyvars inst)))) + (setf (instance-tyvars inst) (map (function tuple-2-2) new-tyvars)) + (setf (instance-context inst) + (map (lambda (c) + (**context (context-class c) + (tuple-2-2 (assq (context-tyvar c) new-tyvars)))) + (instance-context inst)))) + ;; Now walk over the decls & rename each method with a unique name + ;; generated by combining the class, type, and method. Watch for + ;; multiple defs of methods and add defaults after all decls have + ;; been scanned. + (let ((methods-used '()) + (new-instance-vars (map (lambda (m) + (tuple m (method-def-var m inst))) + method-vars))) + (dolist (decl decls) + (setf methods-used + (process-instance-decl decl new-instance-vars methods-used))) + ;; now add defaults when needed + (dolist (m-v new-instance-vars) + (let* ((method-var (tuple-2-1 m-v)) + (definition-var (tuple-2-2 m-v)) + (signature (generate-method-signature inst method-var '#t))) + (if (memq method-var methods-used) + (add-new-module-signature definition-var signature) + (let ((method-body + (if (eq? (method-var-default method-var) '#f) + (**abort (format '#f + "No method declared for method ~A in instance ~A(~A)." + method-var class algdata)) + (**var/def (method-var-default method-var))))) + (add-new-module-def definition-var method-body) + (add-new-module-signature definition-var signature))))) + (setf (instance-methods inst) new-instance-vars) + (add-new-module-def dictionary + (**tuple/l (append (map (lambda (m-v) + (dict-method-ref + (tuple-2-1 m-v) (tuple-2-2 m-v) inst)) + new-instance-vars) + (map (lambda (c) + (get-class-dict algdata c)) + super*)))) + (let ((dict-sig (generate-dictionary-signature inst))) + (add-new-module-signature dictionary dict-sig)) + (setf (instance-decls inst) '()))))))) + +(define (dict-method-ref method-var inst-var inst) + (if (null? (signature-context (method-var-method-signature method-var))) + (**var/def inst-var) + (let* ((sig (generate-method-signature inst method-var '#f)) + (ctxt (signature-context sig)) + (ty (signature-type sig))) + (make overloaded-var-ref + (sig (ast->gtype ctxt ty)) + (var inst-var))))) + +(define (get-class-dict algdata class) + (let ((inst (lookup-instance algdata class))) + (if (eq? inst '#f) + (**abort "Missing super class") + (**var/def (instance-dictionary inst))))) + +(define (process-instance-decl decl new-instance-vars methods-used) + (if (valdef? decl) + (rename-instance-decl decl new-instance-vars methods-used) + (begin + (dolist (a (annotation-decls-annotations decl)) + (cond ((annotation-value? a) + (recoverable-error 'misplaced-annotation + "Misplaced annotation: ~A~%" a)) + (else + (dolist (name (annotation-decl-names a)) + (attach-method-annotation + name (annotation-decl-annotations a) new-instance-vars))))) + methods-used))) + +(define (attach-method-annotation name annotations vars) + (cond ((null? vars) + (signal-no-method name)) + ((eq? name (def-name (tuple-2-1 (car vars)))) + (setf (var-annotations (tuple-2-2 (car vars))) + (append annotations (var-annotations (tuple-2-2 (car vars)))))) + (else (attach-method-annotation name annotations (cdr vars))))) + +(define (signal-no-method name) + (recoverable-error 'no-method "~A is not a method in this class.~%" + name)) + +(define (rename-instance-decl decl new-instance-vars methods-used) + (let ((decl-vars (collect-pattern-vars (valdef-lhs decl)))) + (dolist (var decl-vars) + (resolve-var var) + (let ((method (var-ref-var var))) + (when (not (eq? method *undefined-def*)) + (let ((m-v (assq method new-instance-vars))) + (cond ((memq method methods-used) + (signal-multiple-instance-def method)) + ((eq? m-v '#f) + (signal-not-in-class method)) + (else + (setf (var-ref-name var) (def-name (tuple-2-2 m-v))) + (setf (var-ref-var var) (tuple-2-2 m-v)) + (push (tuple-2-1 m-v) methods-used))))))) + (add-new-module-decl decl) + methods-used)) + +(define (signal-multiple-instance-def method) + (phase-error 'multiple-instance-def + "The instance declaration has multiple definitions of the method ~a." + method)) + +(define (signal-not-in-class method) + (phase-error 'not-in-class + "The instance declaration includes a definition for ~a,~%~ + which is not one of the methods for this class." + method)) + + +(define (method-def-var method-var inst) + (make-new-var + (string-append "i-" + (symbol->string (print-name (instance-class inst))) "-" + (symbol->string (print-name (instance-algdata inst))) "-" + (symbol->string (def-name method-var))))) + +(define (generate-method-signature inst method-var keep-method-context?) + (let* ((simple-type (make-instance-type inst)) + (class-context (instance-context inst)) + (class-tyvar (class-tyvar (instance-class inst))) + (signature (method-var-method-signature method-var))) + (make signature + (context (if keep-method-context? + (append class-context (signature-context signature)) + class-context)) + (type (substitute-tyvar (signature-type signature) class-tyvar + simple-type))))) + +(define (make-instance-type inst) + (**tycon/def (instance-algdata inst) + (map (function **tyvar) (instance-tyvars inst)))) + +(define (generate-dictionary-signature inst) + (**signature (sort-inst-context-by-tyvar + (instance-context inst) (instance-tyvars inst)) + (generate-dictionary-type inst (make-instance-type inst)))) + +(define (sort-inst-context-by-tyvar ctxt tyvars) + (concat (map (lambda (tyvar) + (extract-single-context tyvar ctxt)) tyvars))) + +(define (extract-single-context tyvar ctxt) + (if (null? ctxt) + '() + (let ((rest (extract-single-context tyvar (cdr ctxt)))) + (if (eq? tyvar (context-tyvar (car ctxt))) + (cons (car ctxt) rest) + rest)))) + +(define (generate-dictionary-type inst simple) + (let* ((class (instance-class inst)) + (algdata (instance-algdata inst)) + (tyvar (class-tyvar class))) + (**tuple-type/l (append (map (lambda (method-var) + ;; This ignores the context associated + ;; with a method + (let ((sig (method-var-method-signature + method-var))) + (substitute-tyvar (signature-type sig) + tyvar + simple))) + (class-method-vars class)) + (map (lambda (super-class) + (generate-dictionary-type + (lookup-instance algdata super-class) + simple)) + (class-super* class)))))) + +;;; Checks performed here: +;;; Instance context must include the following: +;;; Context associated with data type +;;; Context associated with instances for each super class +;;; All super class instances must exist + +(define (check-inst-type inst) + (let* ((class (instance-class inst)) + (algdata (instance-algdata inst)) + (inst-context (instance-gcontext inst)) + (alg-context (gtype-context (algdata-signature algdata)))) + (when (not (full-context-implies? inst-context alg-context)) + (signal-instance-context-needs-alg-context algdata)) + (dolist (super-c (class-super class)) + (let ((super-inst (lookup-instance algdata super-c))) + (cond ((eq? super-inst '#f) + (signal-no-super-class-instance class algdata super-c)) + (else + (when (not (full-context-implies? + inst-context (instance-context super-inst))) + (signal-instance-context-insufficient-for-super + class algdata super-c)))))) + )) + +(define (signal-instance-context-needs-alg-context algdata) + (phase-error 'instance-context-needs-alg-context + "The instance context needs to include context defined for data type ~A." + algdata)) + +(define (signal-no-super-class-instance class algdata super-c) + (fatal-error 'no-super-class-instance + "The instance ~A(~A) requires that the instance ~A(~A) be provided." + class algdata super-c algdata)) + +(define (signal-instance-context-insufficient-for-super class algdata super-c) + (phase-error 'instance-context-insufficient-for-super + "Instance ~A(~A) does not imply super class ~A instance context." + class algdata super-c)) diff --git a/tdecl/tdecl-utils.scm b/tdecl/tdecl-utils.scm new file mode 100644 index 0000000..0009eeb --- /dev/null +++ b/tdecl/tdecl-utils.scm @@ -0,0 +1,16 @@ +;;; This file contains routines which generate the code for the +;;; dictionaries used in the class system. + +(define (make-sel-node size i) + (**lambda '(x) + (if (eqv? size 1) + (**var 'x) + (**sel (tuple-constructor size) (**var 'x) i)))) + +(define (make-compose f1 f2) + (**lambda '(x) + (**app f1 (**app f2 (**var 'x))))) + +(define (make-new-var name) ; name is a string + (create-definition *module* (string->symbol name) 'var)) + diff --git a/tdecl/tdecl.scm b/tdecl/tdecl.scm new file mode 100644 index 0000000..227171e --- /dev/null +++ b/tdecl/tdecl.scm @@ -0,0 +1,18 @@ +;;; -- compilation unit definition for type declaration analysis +;;; +;;; author : John +;;; + +(define-compilation-unit tdecl + (source-filename "$Y2/tdecl/") + (require global) + (unit type-declaration-analysis + (source-filename "type-declaration-analysis.scm")) + (unit tdecl-utils + (source-filename "tdecl-utils.scm")) + (unit alg-syn + (source-filename "alg-syn.scm")) + (unit class + (source-filename "class.scm")) + (unit instance + (source-filename "instance.scm"))) diff --git a/tdecl/type-declaration-analysis.scm b/tdecl/type-declaration-analysis.scm new file mode 100644 index 0000000..bffcb23 --- /dev/null +++ b/tdecl/type-declaration-analysis.scm @@ -0,0 +1,72 @@ +;;; This processes type declarations (data, type, instance, class) +;;; Static errors in type declarations are detected and type decls +;;; are replaced by type definitions. All code (class and instance +;;; definitions) is moved to the module decls. + +(define *synonym-refs* '()) + +(predefine (add-derived-instances modules)) ; in derived/derived-instances.scm + +(define (process-type-declarations modules) +;;; Convert data & type decls to definitions + (let ((interface? (eq? (module-type (car modules)) 'interface))) + (setf *synonym-refs* '()) + (walk-modules modules + (lambda () + (setf (module-alg-defs *module*) + (map (function algdata->def) (module-algdatas *module*))) + (setf (module-synonym-defs *module*) + (map (function synonym->def) (module-synonyms *module*))) + (when (not interface?) + (dolist (ty (default-decl-types (module-default *module*))) + (resolve-type ty)))) + ;; A test to see that ty is in Num and is a monotype is needed here. + ) + (multiple-value-bind (ty vals) (topsort *synonym-refs*) + (when (eq? ty 'cyclic) (signal-recursive-synonyms vals))) + ;; Build the class heirarchy + (compute-super-classes modules) + ;; Convert class declarations and instance declarations to definitions. + (walk-modules modules + (lambda () + (setf (module-class-defs *module*) + (map (function class->def) (module-classes *module*))))) + (walk-modules modules + (lambda () + (dolist (class (module-class-defs *module*)) + (setf (class-selectors class) (create-selector-functions class))))) + (walk-modules modules + (lambda () + (setf (module-instance-defs *module*) '()) + (dolist (inst-decl (module-instances *module*)) + (let ((inst (instance->def inst-decl))) + (when (not (eq? inst '#f)) + (push inst (module-instance-defs *module*))))))) + (add-derived-instances modules) + (walk-modules modules + (lambda () + (dolist (inst (module-instance-defs *module*)) + (expand-instance-decls inst)))) + (when (not interface?) + (walk-modules modules + (lambda () + (dolist (ty (default-decl-types (module-default *module*))) + (resolve-type ty))))) + )) + + +(define (signal-recursive-synonyms vals) + (fatal-error 'recursive-synonyms + "There is a cycle in type synonym definitions involving these types:~%~a" + vals)) + +(define (add-new-module-decl decl) + (setf (module-decls *module*) (cons decl (module-decls *module*)))) + +(define (add-new-module-def var value) + (add-new-module-decl + (**define var '() value))) + +(define (add-new-module-signature var signature) + (add-new-module-decl + (**signdecl/def (list var) signature))) diff --git a/top/README b/top/README new file mode 100644 index 0000000..6657292 --- /dev/null +++ b/top/README @@ -0,0 +1,12 @@ +This directory contains the top level of the compiler. +Files found here: + +phases - the top level calls to the compiler phases; compilation init code +errors - general error handlers +globals - global variable definitions +core-symbols - defines core symbols +system-init - code to run once after the compiler is loaded. +driver - top level functions which drive the compiler. There are called + from the command interface or directly from the user. + + diff --git a/top/core-definitions.scm b/top/core-definitions.scm new file mode 100644 index 0000000..e86b355 --- /dev/null +++ b/top/core-definitions.scm @@ -0,0 +1,149 @@ +;;; This file defines core symbols - those in PreludeCore and +;;; other Prelude symbols used in compilation. + +;;; This part is constructed from the export table of PreludeCore +;;; by 'top/prelude-core-syms' and has been pasted in here. + + +(DEFINE *haskell-prelude-vars* + '((CLASSES "Num" + "Integral" + "Eq" + "Text" + "Fractional" + "RealFloat" + "RealFrac" + "Enum" + "Ix" + "Floating" + "Ord" + "Real" + "Binary") + (METHODS "fromInteger" + "signum" + "abs" + "negate" + "*" + "-" + "+" + "toInteger" + "odd" + "even" + "divMod" + "quotRem" + "mod" + "div" + "rem" + "quot" + "/=" + "==" + "showList" + "readList" + "showsPrec" + "readsPrec" + "fromRational" + "recip" + "/" + "scaleFloat" + "significand" + "exponent" + "encodeFloat" + "decodeFloat" + "floatRange" + "floatDigits" + "floatRadix" + "floor" + "ceiling" + "round" + "truncate" + "properFraction" + "enumFromThenTo" + "enumFromTo" + "enumFromThen" + "enumFrom" + "inRange" + "index" + "range" + "atanh" + "acosh" + "asinh" + "tanh" + "cosh" + "sinh" + "atan" + "acos" + "asin" + "tan" + "cos" + "sin" + "logBase" + "**" + "sqrt" + "log" + "exp" + "pi" + "min" + "max" + ">" + ">=" + "<=" + "<" + "toRational" + "showBin" + "readBin") + (TYPES "Char" + "Complex" + "Integer" + "Double" + "Bin" + "Array" + "Float" + "Bool" + "Int" + "Assoc" + "Ratio" + "SystemState" + "IOResult") + (CONSTRUCTORS ":+" "True" "False" ":=" ":") + (SYNONYMS "ShowS" "ReadS" "String" "Rational" "IO") + (VALUES))) + +;;; Non PreludeCore stuff + +;;; This table defines all symbols in the core used internally by the +;;; compiler. + +(define *haskell-noncore-vars* '( + (types + "List" + "Arrow" + "Request" + "Response" + "UnitType" + "TupleDicts") + (constructors + "MkFloat" + "MkDouble" + "MkChar" + "MkInteger" + "MkInt" + "Nil" + "UnitConstructor") + (values + "&&" "||" "primPlusInt" + "++" "take" "drop" "." "showChar" "shows" "showString" + "showParen" "lex" "readParen" "reads" + "primShowBinInt" "primReadBinSmallInt" + "error" + "primIntegerToInt" "primIntToInteger" + "primRationalToFloat" "primRationalToDouble" + "primNegInt" "primNegInteger" "primNegFloat" "primNegDouble" + "foldr" "build" "inlineFoldr" "inlineBuild" + "primAppend" "primStringEq" + "dictSel" "tupleEqDict" "tupleOrdDict" "tupleIxDict" + "tupleTextDict" "tupleBinaryDict"))) + + + + + diff --git a/top/core-init.scm b/top/core-init.scm new file mode 100644 index 0000000..7ba9fa1 --- /dev/null +++ b/top/core-init.scm @@ -0,0 +1,14 @@ + + +(define *core-symbols* '()) +(define *prelude-core-symbols* '()) + +; expands into lots of (define *core-??* '()) + +(define-core-variables) + +(define (init-core-symbols) + (setf *core-symbols* (make-table)) + (setf *prelude-core-symbols* (make-table)) + (create-core-globals)) + diff --git a/top/core-symbols.scm b/top/core-symbols.scm new file mode 100644 index 0000000..f43de93 --- /dev/null +++ b/top/core-symbols.scm @@ -0,0 +1,126 @@ +;;; This defines all core symbols. + +;;; Core symbols are stored in global variables. The core-symbol +;;; macro just turns a string into a variable name. + +(define-syntax (core-symbol str) + (make-core-symbol-name str)) + +(define (make-core-symbol-name str) + (string->symbol (string-append "*core-" str "*"))) + +(define (symbol->core-var name) + (make-core-symbol-name (symbol->string name))) + +(define (get-core-var-names vars type) + (let ((res (assq type vars))) + (if (eq? res '#f) + '() + (map (function string->symbol) (tuple-2-2 res))))) + +;;; This is just used to create a define for each var without a +;;; value. + +(define-syntax (define-core-variables) + `(begin + ,@(define-core-variables-1 *haskell-prelude-vars*) + ,@(define-core-variables-1 *haskell-noncore-vars*))) + +(define (define-core-variables-1 vars) + (concat (map (lambda (ty) + (map (function init-core-symbol) + (get-core-var-names vars ty))) + '(classes methods types constructors synonyms values)))) + +(define (init-core-symbol sym) + `(define ,(symbol->core-var sym) '())) + +(define-syntax (create-core-globals) + `(begin + (begin ,@(create-core-defs *haskell-prelude-vars* '#t)) + (begin ,@(create-core-defs *haskell-noncore-vars* '#f)))) + +(define (create-core-defs defs prelude-core?) + `(,@(map (lambda (x) (define-core-value x prelude-core?)) + (get-core-var-names defs 'values)) + ,@(map (lambda (x) (define-core-method x prelude-core?)) + (get-core-var-names defs 'methods)) + ,@(map (lambda (x) (define-core-synonym x prelude-core?)) + (get-core-var-names defs 'synonyms)) + ,@(map (lambda (x) (define-core-class x prelude-core?)) + (get-core-var-names defs 'classes)) + ,@(map (lambda (x) (define-core-type x prelude-core?)) + (get-core-var-names defs 'types)) + ,@(map (lambda (x) (define-core-constr x prelude-core?)) + (get-core-var-names defs 'constructors)))) + + +(define (define-core-value name pc?) + `(setf ,(symbol->core-var name) + (make-core-value-definition ',name ',pc?))) + +(define (make-core-value-definition name pc?) + (install-core-sym + (make var (name name) (module '|*Core|) (unit '|*Core|)) + name + pc?)) + +(define (define-core-method name pc?) + `(setf ,(symbol->core-var name) + (make-core-method-definition ',name ',pc?))) + +(define (make-core-method-definition name pc?) + (install-core-sym + (make method-var (name name) (module '|*Core|) (unit '|*Core|)) + name + pc?)) + +(define (define-core-class name pc?) + `(setf ,(symbol->core-var name) + (make-core-class-definition ',name ',pc?))) + +(define (make-core-class-definition name pc?) + (install-core-sym + (make class (name name) (module '|*Core|) (unit '|*Core|)) + name + pc?)) + +(define (define-core-synonym name pc?) + `(setf ,(symbol->core-var name) + (make-core-synonym-definition ',name ',pc?))) + +(define (make-core-synonym-definition name pc?) + (install-core-sym + (make synonym (name name) (module '|*Core|) (unit '|*Core|)) + name + pc?)) + +(define (define-core-type name pc?) + `(setf ,(symbol->core-var name) + (make-core-type-definition ',name ',pc?))) + +(define (make-core-type-definition name pc?) + (install-core-sym + (make algdata (name name) (module '|*Core|) (unit '|*Core|)) + name + pc?)) + +(define (define-core-constr name pc?) + `(setf ,(symbol->core-var name) + (make-core-constr-definition ',name ',pc?))) + +(define (make-core-constr-definition name pc?) + (setf name (add-con-prefix/symbol name)) + (install-core-sym + (make con (name name) (module '|*Core|) (unit '|*Core|)) + name + pc?)) + +(define (install-core-sym def name preludecore?) + (setf (def-core? def) '#t) + (when preludecore? + (setf (def-prelude? def) '#t)) + (setf (table-entry (dynamic *core-symbols*) name) def) + (when preludecore? + (setf (table-entry (dynamic *prelude-core-symbols*) name) def)) + def) diff --git a/top/errors.scm b/top/errors.scm new file mode 100644 index 0000000..06a2f66 --- /dev/null +++ b/top/errors.scm @@ -0,0 +1,119 @@ +;;; This file contains general error handling routines. + +;;; This is the general error handler. It has three arguments: an +;;; id, error type, and an error message. The message is a list of +;;; format, arglist combinations. + +;;; The error types are: +;;; warning -> control returns and compilation proceeds +;;; The message may be suppressed +;;; recoverable -> control returns and compilation proceeds +;;; phase -> control returns but compilation is aborted +;;; after the phase in *abort-point*. +;;; fatal -> control goes back to the top level +;;; internal -> enters the break loop or does a fatal error + +;;; Two globals control error behavior: +;;; *break-on-error?* enter the break loop on any error +;;; *never-break?* never enter the break loop, even for internal errors. + +;;; The global *error-output-port* controls where errors are printer. + +;;; The strategy here is to first write a banner message based on the id and +;;; type, write out the messages, and then take action depending on the type. + +(define *in-error-handler?* '#f) + +(define (haskell-error id type messages) + (format *error-output-port* "~&[~A] ~A in phase ~A:~%" + id (err-type->banner type) (dynamic *phase*)) + (dolist (m messages) + (apply (function format) *error-output-port* m) + (fresh-line *error-output-port*)) + (maybe-show-context (dynamic *context*)) + (if (dynamic *in-error-handler?*) + (error "Recursive error in haskell-error.") + (begin + (dynamic-let ((*in-error-handler?* '#t)) + (cond (*break-on-error?* + (haskell-breakpoint)) + ((eq? type 'internal) + (if *never-break?* + (abort-compilation) + (haskell-breakpoint))) + ((eq? type 'fatal) + (abort-compilation)) + ((eq? type 'phase) + (halt-compilation)))) + (when (and (memq type '(recoverable phase)) + (dynamic *recoverable-error-handler*)) + (funcall (dynamic *recoverable-error-handler*))) + 'ok))) + +(define (err-type->banner err-type) + (cond ((eq? err-type 'warning) + "Warning") + ((eq? err-type 'recoverable) + "Recoverable error") + ((eq? err-type 'phase) + "Phase error") + ((eq? err-type 'fatal) + "Fatal error") + ((eq? err-type 'internal) + "Internal-error") + (else "???"))) + +(define (maybe-show-context context) + (when context + (with-slots source-pointer (line file) (ast-node-line-number context) + (fresh-line *error-output-port*) + (format *error-output-port* "Error occurred at line ~A in file ~A.~%" + line (filename-name file))))) + +;;; A few entry points into the error system. +;;; As a matter of convention, there should be a signaling function defined +;;; for each specific error condition that calls one of these functions. +;;; Error messages should be complete sentences with proper punctuation +;;; and capitalization. The signaling function should use the message +;;; to report the error and not do any printing of its own. + +(define (fatal-error id . msg) + (haskell-error id 'fatal (list msg))) + +(define (haskell-warning id . msg) + (haskell-error id 'warning (list msg))) + +(define (recoverable-error id . msg) + (haskell-error id 'recoverable (list msg))) + +(define (compiler-error id . msg) + (haskell-error id 'internal (list msg))) + +(define (phase-error id . msg) + (haskell-error id 'phase (list msg))) + +;;; This function puts the compiler into the lisp breakloop. this may +;;; want to fiddle the programming envoronment someday. + +(define (haskell-breakpoint) + (error "Haskell breakpoint.")) + + +;;; This deals with error at runtime + +(define (haskell-runtime-error msg) + (format '#t "~&Haskell runtime abort.~%~A~%" msg) + (funcall (dynamic *runtime-abort*))) + +;; Some common error handlers + +(define (signal-unknown-file-type filename) + (fatal-error 'unknown-file-type + "The filename ~a has an unknown file type." + filename)) + +(define (signal-file-not-found filename) + (fatal-error 'file-not-found + "The file ~a doesn't exist." + filename)) + diff --git a/top/globals.scm b/top/globals.scm new file mode 100644 index 0000000..eba139b --- /dev/null +++ b/top/globals.scm @@ -0,0 +1,75 @@ +;;; These are global variables used throughout the compiler. + +;;; Configuration stuff + +(define *prelude-unit-filename* "$PRELUDE/Prelude.hu") + +(define *haskell-compiler-version* "Y2.0.5") +(define *haskell-compiler-update* "") + + +;;; Control over the init process +(define *haskell-initialized?* '#f) + +;;; Error control +(define *break-on-error?* '#f) +(define *never-break?* '#f) + +(define *runtime-abort* '()) + +(define *recoverable-error-handler* '()) +(define *error-output-port* '()) ; initialized later + +(define *context* '#f) ; ast node being compiled. + +(define *unit* '()) + +(define *standard-module-default* '()) + +(define *undefined-def* '()) +(define *printer-class* '()) +(define *printers* '(phase-time)) + +(define *all-printers* + '(phase-time time compiling loading reading extension + parse import type-decl scope depend + type cfn depend2 + flic optimize optimize-extra strictness codegen codegen-flic + dumper dump-stat)) + +;;; Global context stuff +;;; ***This variable is actually only used by the parser. + +(define *current-file* '()) + +(define *printed-tyvars* '()) + + +;;; Used by the symbol table routines + +(define *modules* '()) ; maps module name -> module structure +(define *module* '()) ; current module +(define *module-name* '()) +(define *symbol-table* '()) ; part of the current module +(define *inverted-symbol-table* '()) ; maps def -> localname +(define *fixity-table* '()) ; name -> fixity +(define *suffix-table* '()) ; name -> int (for uniquifying names) + +(define *special-parse-for-type-macros* '#f) + +;;; These are for diagnostic purposes only + +(define *big-let* '()) + +(define *show-end-of-phase* '#f) + +;;; This is used to configure error messages & responses. + +(define *emacs-mode* '#f) + +;;; This is used to stash the Prelude symbol environment + +(define *prelude-symbol-table* '()) +(define *prelude-fixity-table* '()) +(define *prelude-inverted-symbol-table* '()) + diff --git a/top/has-macros.scm b/top/has-macros.scm new file mode 100644 index 0000000..2c75730 --- /dev/null +++ b/top/has-macros.scm @@ -0,0 +1,57 @@ +;;; General macros for the Haskell compiler + +(define-syntax (remember-context exp . body) + (let ((temp (gensym))) + `(let ((,temp ,exp)) + (dynamic-let ((*context* (if (ast-node-line-number ,temp) + ,temp + (dynamic *context*)))) + ,@body)))) + +(define-syntax (maybe-remember-context exp . body) + (let ((temp (gensym))) + `(let ((,temp ,exp)) + (if (ast-node-line-number ,temp) + (dynamic-let ((*context* ,temp)) ,@body) + (begin ,@body))))) + +(define-syntax (recover-errors error-value . body) + (let ((local-handler (gensym))) + `(let/cc ,local-handler + (dynamic-let ((*recoverable-error-handler* + (lambda () (funcall ,local-handler ,error-value)))) + ,@body)))) + +;;; This is for iterating a list of contexts over a list of types. + +(define-syntax (do-contexts cbinder tbinder . body) + (let ((cvar (car cbinder)) + (cinit (cadr cbinder)) + (tvar (car tbinder)) + (tinit (cadr tbinder)) + (cv (gensym)) + (tv (gensym))) + `(do ((,cv ,cinit (cdr ,cv)) + (,tv ,tinit (cdr ,tv))) + ((null? ,cv)) + (let ((,tvar (car ,tv))) + (dolist (,cvar (car ,cv)) + ,@body))))) + +;; dolist for 2 lists at once. + +(define-syntax (dolist2 a1 a2 . body) + (let ((a1var (car a1)) + (a1init (cadr a1)) + (a2var (car a2)) + (a2init (cadr a2)) + (a1l (gensym)) + (a2l (gensym))) + `(do ((,a1l ,a1init (cdr ,a1l)) + (,a2l ,a2init (cdr ,a2l))) + ((null? ,a1l)) + (let ((,a1var (car ,a1l)) + (,a2var (car ,a2l))) + ,@body)))) + + \ No newline at end of file diff --git a/top/has-utils.scm b/top/has-utils.scm new file mode 100644 index 0000000..62a0c3f --- /dev/null +++ b/top/has-utils.scm @@ -0,0 +1,21 @@ +;;; These utilities are specific to the Haskell language. + +(define (add-con-prefix str) ; should be in some utility file + (string-append ";" str)) + +(define (remove-con-prefix string) + (substring string 1 (string-length string))) + +(define (has-con-prefix? string) + (char=? (string-ref string 0) '#\;)) + +(define (add-con-prefix/symbol sym) + (string->symbol (add-con-prefix (symbol->string sym)))) + +(define (remove-con-prefix/symbol sym) + (string->symbol (remove-con-prefix (symbol->string sym)))) + +(define (has-con-prefix/symbol? sym) + (has-con-prefix? (symbol->string sym))) + + diff --git a/top/phases.scm b/top/phases.scm new file mode 100644 index 0000000..706c541 --- /dev/null +++ b/top/phases.scm @@ -0,0 +1,226 @@ + +;;; This is the top-level phase structure of the compiler. + +;;; Compilation phase support + +(define *phase* '#f) +(define *abort-phase* '#f) ; abort when this phase completes +(define *abort-compilation* + (lambda () + (error "No error continuation defined here!"))) + +(define *module-asts* '()) ; a global only for debugging purposes + +;;; Later add the printing and timing stuff here + +(define-local-syntax (phase-body phase-name body printer) + `(dynamic-let ((*phase* ',phase-name)) + (when (memq ',phase-name (dynamic *printers*)) + (format '#t "~%Phase ~a:~%" ',phase-name) + (force-output)) + (let* ((phase-start-time (get-run-time)) + (result ,body) + (current-time (get-run-time))) + (when (eq? (dynamic *abort-phase*) ',phase-name) + (abort-compilation)) + ,@(if (eq? printer '#f) + '() + `((when (memq ',phase-name (dynamic *printers*)) + (funcall ,printer result) + (force-output)))) + (when (memq 'phase-time *printers*) + (let ((elapsed-time (- current-time phase-start-time))) + (format '#t "~&~A complete: ~A seconds~%" + ',phase-name elapsed-time) + (force-output))) + result))) + + + +;;; Returns 2 values: module ast's and lisp code. + +(define (compile-haskell-files files) + (dynamic-let ((*abort-phase* '#f)) + (let ((all-mods (haskell-parse-files files)) + (interface-mods '()) + (regular-mods '())) + (dolist (m all-mods) + (if (eq? (module-type m) 'interface) + (push m interface-mods) + (push m regular-mods))) + (dynamic-let ((*unit* (module-name (car all-mods)))) + (values + all-mods + `(begin + ,(if interface-mods + (compile-interface-modules (nreverse interface-mods)) + '#f) + ,(if regular-mods + (compile-modules (nreverse regular-mods)) + '#f)) + ))))) + + + +(define (compile-modules mods) + (dynamic-let ((*context* '#f) + (*recoverable-error-handler* '#f) + (*abort-phase* '#f) + (*unique-name-counter* 1) + (*suffix-table* (make-table))) + (haskell-import-export mods '#f) + (haskell-process-type-declarations mods) + (haskell-scope mods) + (let ((big-let (haskell-dependency-analysis mods))) + (cond ((not (void? big-let)) + (haskell-type-check big-let mods) + (setf big-let (haskell-cfn big-let)) + (setf big-let (haskell-dependency-reanalysis big-let)) + (setf big-let (haskell-ast-to-flic big-let)) + (setf big-let (haskell-optimize big-let)) + (setf big-let (haskell-strictness big-let)) + (haskell-codegen big-let mods)) + (else + ''#f) + )))) + + +(define (modules->lisp-code modules) + (dynamic-let ((*unit* (module-name (car modules)))) + (compile-modules modules))) + + +(predefine (notify-error)) ; in command-interface/command-utils.scm + +(define (abort-compilation) + (notify-error) + (funcall (dynamic *abort-compilation*))) + +(define (halt-compilation) + (setf (dynamic *abort-phase*) (dynamic *phase*))) + + +;;; Here are the actual phase bodies + +(predefine (parse-files files)) + +(define (haskell-parse-files filenames) + (phase-body parse + (let ((mods (parse-files filenames))) + mods) + #f)) + +(predefine (import-export modules)) ; in import-export/import-export.scm +(predefine (import-export/interface modules)) + +(define (haskell-import-export modules interface?) + (phase-body import + (if interface? + (import-export/interface modules) + (import-export modules)) + #f)) + + +(predefine (process-type-declarations modules)) + ; in tdecl/type-declaration-analysis.scm + +(define (haskell-process-type-declarations modules) + (phase-body type-decl + (begin + (process-type-declarations modules)) + #f)) + + +(predefine (scope-modules x)) ; in prec/scope.scm +(predefine (print-full-module x . maybe-stream)) ; in the printers + +(define (haskell-scope modules) + (phase-body scope + (scope-modules modules) + (lambda (result) + (declare (ignore result)) + (dolist (m modules) (print-full-module m))) + )) + + +(predefine (do-dependency-analysis x)) ; in depend/dependency-analysis.scm + +(define (haskell-dependency-analysis modules) + (phase-body depend + (do-dependency-analysis modules) + (function pprint*))) + + +(predefine (do-haskell-type-check big-let mods)) + +(define (haskell-type-check big-let modules) + (phase-body type + (do-haskell-type-check big-let modules) + #f)) + +(predefine (cfn-ast x)) ; in cfn/main.scm + +(define (haskell-cfn big-let) + (phase-body cfn + (cfn-ast big-let) + (function pprint*))) + + +(predefine (analyze-dependency-top x)) ; in depend/dependency-analysis.scm + +(define (haskell-dependency-reanalysis big-let) + (phase-body depend2 + (begin + (analyze-dependency-top big-let) + big-let) + (function pprint*))) + + +(predefine (ast-to-flic x)) ; in flic/ast-to-flic.scm + +(define (haskell-ast-to-flic big-let) + (phase-body flic + (ast-to-flic big-let) + (function pprint*))) + + +(predefine (optimize-top x)) ; in backend/optimize.scm + +(define (haskell-optimize big-let) + (phase-body optimize + (optimize-top big-let) + (function pprint*))) + +(predefine (strictness-analysis-top x)) ; in backend/strictness.scm +(predefine (strictness-analysis-printer x)) + +(define (haskell-strictness big-let) + (phase-body strictness + (strictness-analysis-top big-let) + (function strictness-analysis-printer))) + + +(predefine (codegen-top x)) ; in backend/codegen.scm +(predefine (codegen-exported-types x)) ; " +(predefine (codegen-prim-entries x)) ; ditto + +(define (haskell-codegen big-let mods) + (phase-body codegen + `(begin + ,(codegen-exported-types mods) + ,(codegen-top big-let)) + #f)) + + +;;; This is for interface modules. + +(predefine (haskell-codegen/interface mods)) + +(define (compile-interface-modules mods) + (dynamic-let ((*context* '#f) + (*recoverable-error-handler* '#f) + (*abort-phase* '#f)) + (haskell-import-export mods '#t) + (haskell-process-type-declarations mods) + (haskell-scope mods) + (haskell-codegen/interface mods))) diff --git a/top/prelude-core-syms.scm b/top/prelude-core-syms.scm new file mode 100644 index 0000000..ddae21f --- /dev/null +++ b/top/prelude-core-syms.scm @@ -0,0 +1,57 @@ +;;; This should be used to create core symbols for every name exported +;;; by PreludeCore. This only needs to run when the Prelude definition +;;; changes. + +(define (def->name-string x) + (symbol->string (def-name x))) + + +(define (generate-prelude-core-symbols) + (initialize-compilation) + (load-compilation-unit *prelude-unit-filename* '#t '#f '#f '#f) + (let* ((core (table-entry *modules* '|PreludeCore|)) + (export-table (module-export-table core)) + (vars '()) + (classes '()) + (types '()) + (constrs '()) + (syns '()) + (methods '())) + (table-for-each + (lambda (k v) + (declare (ignore k)) + (let ((def (tuple-2-2 (car v)))) + (cond ((var? def) + (push (def->name-string def) vars)) + ((synonym? def) + (push (def->name-string def) syns)) + ((algdata? def) + (push (def->name-string def) types) + (dolist (x (cdr v)) + (push (remove-con-prefix (def->name-string (tuple-2-2 x))) + constrs))) + ((class? def) + (push (def->name-string def) classes) + (dolist (x (cdr v)) + (push (def->name-string (tuple-2-2 x)) + methods))) + (else (error "? strange def"))))) + export-table) + (call-with-output-file "/tmp/prelude-syms" + (lambda (port) + (pprint `(define *haskell-prelude-vars* + '((classes ,@classes) + (methods ,@methods) + (types ,@types) + (constructors ,@constrs) + (synonyms ,@syns) + (values ,@vars))) + port))))) + + + +(define (create-prelude-init-code defs) + (let* ((name (def-name def)) + (sym-name (make-core-symbol-name name))) + `(define sym-name '()))) + diff --git a/top/symbol-table.scm b/top/symbol-table.scm new file mode 100644 index 0000000..499bfb8 --- /dev/null +++ b/top/symbol-table.scm @@ -0,0 +1,412 @@ +;;; These routines deal with the global symbol table. The symbol table +;;; is represented in two stages: a module table which maps module names +;;; onto module structures and local tables within each module which +;;; map names (symbols) to definitions. + +;;; The following functions deal with the module table (*modules*): + +;;; (initialize-module-table) - this clears out all modules from the +;;; symbol table. Every compilation should start with this. +;;; (add-module-to-module-table module) - this takes a module ast, +;;; either from a .exp file or previous compilation with the same +;;; incarnation of the compiler and adds it to the set of `known' +;;; modules. Incomplete module ast's in the process of compilation +;;; are also added to this table. + + +(define (initialize-module-table) + (setf *modules* (make-table))) + +(define (add-module-to-symbol-table module) + (let* ((name (module-name module)) + (old-module (table-entry *modules* name))) + (when (not (eq? old-module '#f)) + (if (eq? *unit* (module-unit old-module)) + (signal-module-double-definition name) + (signal-module-already-defined name))) + (setf (table-entry *modules* name) module))) + +(define (remove-module-from-symbol-table module) + (let ((name (module-name module))) + (setf (table-entry *modules* name) '#f))) + +(define (locate-module name) + (table-entry *modules* name)) + +;;; (walk-modules fn mod-list) - this calls fn for each module in the +;;; mod-list. It also binds the global variable *module* to the +;;; current module, *symbol-table* to the local symbol +;;; table. The fixity table is also placed in a global. + +(define (walk-modules mods fn) + (dolist (mod mods) + (dynamic-let ((*module* mod) + (*module-name* (module-name mod)) + (*symbol-table* (module-symbol-table mod)) + (*fixity-table* (module-fixity-table mod)) + (*inverted-symbol-table* (module-inverted-symbol-table mod))) + (funcall fn)))) + +;;; create-definition makes a new definition object + +(define (create-definition module name type) + (cond ((module-prelude? module) + (let ((def (table-entry *core-symbols* name))) + (cond ((eq? def '#f) + (create-definition/non-core module name type)) + (else + (setf (def-unit def) *unit*) + (setf (def-module def) (module-name module)) + ;; *** Should any other properties be reinitialized here? + (cond ((or (eq? type 'var) (eq? type 'method-var)) + (setf (var-fixity def) '#f) + (setf (var-signature def) '#f)) + ((eq? type 'con) + (setf (con-fixity def) '#f))) + def)))) + (else (create-definition/non-core module name type)))) + +;(define (create-definition/non-core module name type) +; (create-definition/new module name type) +; (let* ((interface (module-interface-module module)) +; (old-def (table-entry (module-symbol-table interface) name))) +; (if (eq? old-def '#f) +; (create-definition/new module name type) +; (cond ((eq? type 'var) +; (unless (var? old-def) +; (def-conflict module name type old-def)) +; (setf (var-interface-type old-def) (var-type old-def))) +; ((eq? type 'con) +; (unless (con? old-def) +; (def-conflict module name type old-def))) +; ((eq? type 'synonym) +; (unless (synonym? old-def) +; (def-conflict module name type old-def))) +; ((eq? type 'algdata) +; (unless (algdata? old-def) +; (def-conflict module name type old-def))) +; ((eq? type 'class) +; (unless (class? old-def) +; (def-conflict module name type old-def))) +; ((eq? type 'method-var) +; (unless (method-var? old-def) +; (def-conflict module name type old-def))))) +; (setf (def-unit old-def) *unit*) +; old-def))) +; +;(define (def-conflict module name type def) +; (phase-error 'interface-conflict +; "The ~A ~A in module ~A was defined as a ~A in an interface." +; (cond ((var? def) "variable") +; ((class? def) "class") +; ((algdata? def) "data type") +; ((synonym? def) "synonym") +; ((con? def) "constructor") +; (else "widgit")) +; name (module-name module) type)) + +(define (create-definition/non-core module name type) + (let ((mname (module-name module))) + (when (eq? (module-type *module*) 'interface) + (mlet (((mod name1) (rename-interface-symbol name))) + (setf mname mod) + (setf name name1))) + (create-definition/inner mname name type))) + +(define (create-definition/inner mname name type) + (cond ((eq? type 'var) + (make var (name name) (module mname) (unit *unit*))) + ((eq? type 'con) + (make con (name name) (module mname) (unit *unit*))) + ((eq? type 'synonym) + (make synonym (name name) (module mname) (unit *unit*))) + ((eq? type 'algdata) + (make algdata (name name) (module mname) (unit *unit*))) + ((eq? type 'class) + (make class (name name) (module mname) (unit *unit*))) + ((eq? type 'method-var) + (make method-var (name name) (module mname) (unit *unit*))) + (else + (error "Bad type argument ~s." type)))) + + +(define (create-top-definition name type) + (let ((def (create-definition *module* name type))) + (insert-top-definition name def) + def)) + +;;; Interfaces have a special table which resolves imports in the +;;; interface. Given a name in an interface module this returns the +;;; corresponding full name: a (module,original-name) pair. Symbols not +;;; imported are assumed to be defined in the interface. + +(define (rename-interface-symbol name) + (let ((res (assq name (module-interface-imports *module*)))) + (if (eq? res '#f) + (values *module-name* name) + (values (tuple-2-1 (tuple-2-2 res)) + (tuple-2-2 (tuple-2-2 res)))))) + +;;; This creates a locally defined var node. + +(define (create-local-definition name) + (let ((var (make var (name name) (module *module-name*) (unit *unit*)))) + (setf (var-fixity var) (table-entry *fixity-table* name)) + var)) + + +;;; This function creates a new variable. +;;; The "root" may be either a symbol or a string. +;;; *unit* defines the home module of the variable. + +;;; *** Maybe it would be possible to hack this so that it doesn't +;;; *** create any symbol at all until the name is demanded by something, +;;; *** but that seems like a rather sweeping change. + +(define (create-temp-var root) + (let* ((name (gensym (if (symbol? root) (symbol->string root) root))) + (module *unit*)) + (make var (name name) (module module) (unit *unit*)))) + + +;;; The following routines install top level definitions into the symbol +;;; table. + +(predefine (signal-multiple-name-conflict name old-local-name def)) + ; in import-export/ie-errors.scm + +(define (insert-top-definition name def) + (let ((old-definition (resolve-toplevel-name name))) + (cond ((eq? old-definition '#f) + (when (not (def-prelude? def)) + (setf (table-entry *symbol-table* name) def)) + (when (and (var? def) (not (eq? (var-fixity def) '#f))) + (setf (table-entry *fixity-table* name) + (var-fixity def))) + (when (and (con? def) (not (eq? (con-fixity def) '#f))) + (setf (table-entry *fixity-table* name) + (con-fixity def))) + (when (not (def-prelude? def)) + (if (eq? (local-name def) '#f) + (setf (table-entry *inverted-symbol-table* def) name) + (signal-multiple-name-conflict name (local-name def) def)))) + ((eq? old-definition def) + 'OK) + ((def-prelude? old-definition) + (signal-core-redefinition name)) + ((and (module-uses-standard-prelude? *module*) + (table-entry *prelude-symbol-table* name)) + (if (eq? (def-module def) *module-name*) + (signal-prelude-redefinition name) + (signal-prelude-reimport name (def-module def)))) + ((eq? (def-module def) *module-name*) + (signal-multiple-definition-in-module name *module-name*)) + ((eq? (def-module old-definition) *module-name*) + (signal-redefinition-by-imported-symbol name *module-name*)) + (else + (signal-multiple-import name *module-name*))))) + +;;; Gets the fixity of a name. + +(define (get-local-fixity name) + (table-entry *fixity-table* name)) + +;;; These routines support general scoping issues. Only vars have local +;;; definitions - all other names are resolved from the global symbol table. + +;;; This is used when the name must be in the top symbols. + +(define (fetch-top-def name type) + (let ((def (resolve-toplevel-name name))) + (cond ((eq? def '#f) + (cond ((eq? (module-type *module*) 'interface) + (mlet (((mod name1) (rename-interface-symbol name))) + (if (eq? mod *module-name*) + (undefined-topsym name) + (let ((new-def (create-definition/inner + mod name1 type))) + (insert-top-definition name1 new-def) + (cond ((algdata? new-def) + (setf (algdata-n-constr new-def) 0) + (setf (algdata-constrs new-def) '()) + (setf (algdata-context new-def) '()) + (setf (algdata-tyvars new-def) '()) + (setf (algdata-classes new-def) '#f) + (setf (algdata-enum? new-def) '#f) + (setf (algdata-tuple? new-def) '#f) + (setf (algdata-real-tuple? new-def) '#f) + (setf (algdata-deriving new-def) '())) + ((class? new-def) + (setf (class-method-vars new-def) '()) + (setf (class-super new-def) '()) + (setf (class-super* new-def) '()) + (setf (class-tyvar new-def) '|a|) + (setf (class-instances new-def) '()) + (setf (class-kind new-def) 'other) + (setf (class-n-methods new-def) 0) + (setf (class-dict-size new-def) 0) + (setf (class-selectors new-def) '()))) + new-def)))) + (else + (undefined-topsym name)))) + (else def)))) + +(define (undefined-topsym name) + (signal-undefined-symbol name) + *undefined-def*) + + +(define (resolve-toplevel-name name) + (let ((pc (table-entry *prelude-core-symbols* name))) + (cond ((not (eq? pc '#f)) + pc) + ((module-uses-standard-prelude? *module*) + (let ((res (table-entry *prelude-symbol-table* name))) + (if (eq? res '#f) + (resolve-toplevel-name-1 name) + res))) + (else + (resolve-toplevel-name-1 name))))) + +(define (resolve-toplevel-name-1 name) + (cond ((eq? (module-inherited-env *module*) '#f) + (table-entry *symbol-table* name)) + (else + (let ((res (search-inherited-tables + name (module-inherited-env *module*)))) + (if (eq? res '#f) + (table-entry *symbol-table* name) + res))))) + +(define (search-inherited-tables name mod) + (if (eq? mod '#f) + '#f + (let ((res (table-entry (module-symbol-table mod) name))) + (if (eq? res '#f) + (search-inherited-tables name (module-inherited-env mod)) + res)))) + +;;; Con-ref's are special in that the naming convention (;Name) ensures +;;; that if a def is found it must be a con. + +(define (resolve-con con-ref) + (when (eq? (con-ref-con con-ref) *undefined-def*) + (remember-context con-ref + (let ((def (fetch-top-def (con-ref-name con-ref) 'con))) + (setf (con-ref-con con-ref) def))))) + +(define (resolve-class class-ref) + (when (eq? (class-ref-class class-ref) *undefined-def*) + (remember-context class-ref + (let ((def (fetch-top-def (class-ref-name class-ref) 'class))) + (when (not (class? def)) + (signal-class-name-required def (class-ref-name class-ref))) + (setf (class-ref-class class-ref) def))))) + + +(define (resolve-tycon tycon) + (when (eq? (tycon-def tycon) *undefined-def*) + (remember-context tycon + (let ((def (fetch-top-def (tycon-name tycon) 'algdata))) + (when (class? def) + (signal-tycon-name-required (tycon-name tycon))) + (setf (tycon-def tycon) def))))) + + +;;; This should be used after the local environment has been searched. +;;; Other routines dealing with variable scoping are elsewhere. + +(define (resolve-var var-ref) + (when (eq? (var-ref-var var-ref) *undefined-def*) + (remember-context var-ref + (let ((def (fetch-top-def (var-ref-name var-ref) 'var))) + (setf (var-ref-var var-ref) def))))) + + +;;; *** The inverted-symbol-table is the only table in the whole +;;; *** system that is not keyed off of symbols. If this is a problem, +;;; *** things that use it could probably be rewritten to do something +;;; *** else, like store an a-list on the def itself. + +;;; This does not need to consult the inherited-env flag because when this +;;; is used in extensions only new symbols get inserted. + +(define (local-name def) + (cond ((def-prelude? def) + (def-name def)) + ((module-uses-standard-prelude? *module*) + (let ((res (table-entry *prelude-inverted-symbol-table* def))) + (if (eq? res '#f) + (table-entry *inverted-symbol-table* def) + res))) + (else + (table-entry *inverted-symbol-table* def)))) + +(define (print-name x) + (let ((res (local-name x))) + (if (eq? res '#f) + (def-name x) + res))) + + +;;; Error signalling routines. + +(define (signal-module-double-definition name) + (fatal-error 'module-double-definition + "Module ~s is defined more than once." + name)) + +(define (signal-module-already-defined name) + (fatal-error 'module-already-defined + "Module ~a is defined more than once in the current unit." + name)) + +(define (signal-multiple-definition-in-module name modname) + (if (eq? (module-type *module*) 'extension) + (phase-error 'cant-redefine-in-extension + "An extension for module ~A cannot redefine the symbol ~A" + modname name) + (phase-error 'multiple-definition-in-module + "There is more than one definition for the name ~a in module ~a." + name modname))) + +(define (signal-redefinition-by-imported-symbol name modname) + (phase-error 'redefinition-by-imported-symbol + "The name ~a is defined in module ~a, and cannot be imported." + name modname)) + +(define (signal-core-redefinition name) + (phase-error 'prelude-redefinition + "The name ~a is defined in the prelude core and cannot be redefined." + name)) + +(define (signal-prelude-redefinition name) + (phase-error 'prelude-redefinition + "The name ~a is defined in the prelude.~%You must hide it if you wish to use this name." + name)) + +(define (signal-prelude-reimport name modname) + (phase-error 'prelude-redefinition + "The name ~a is both imported from ~A and defined in the prelude.~%" + name modname)) + +(define (signal-multiple-import name modname) + (phase-error 'multiple-import + "The name ~a is imported into module ~a multiple times." + name modname)) + +(define (signal-undefined-symbol name) + (phase-error 'undefined-symbol + "The name ~A is undefined." + name)) + +(define (signal-class-name-required name def) + (phase-error 'class-name-required + "The name ~A defines a ~A, but a class name is required." + name + (if (synonym? def) "synonym" "data type"))) + +(define (signal-tycon-name-required name) + (phase-error 'tycon-required + "The name ~A defines a class, but a type constructor name is required." + name)) diff --git a/top/system-init.scm b/top/system-init.scm new file mode 100644 index 0000000..4c06cb5 --- /dev/null +++ b/top/system-init.scm @@ -0,0 +1,41 @@ + +(define (initialize-haskell-system) + (when (not *haskell-initialized?*) + (initialize-haskell-system/forced)) + 'haskell-ready) + +(predefine (**tycon/def def args)) ; in util/constructors.scm +(predefine (init-cse-structs)) ; in csys/dump-cse.scm + +(define (initialize-haskell-system/forced) + (setf *haskell-initialized?* '#t) + (setf *error-output-port* (current-output-port)) + (init-core-symbols) + (init-tuples) + (setf *standard-module-default* + (make default-decl + (types (list + (**tycon/def (core-symbol "Int") '()) + (**tycon/def (core-symbol "Double") '()))))) + (setf *undefined-def* + (make def + (name '*undefined*) + (unit '*undefined*) + (module '*undefined*))) + (setf *printer-class* + (make class + (name '|Printers|) + (module '|*Core|) (unit '|*Core|))) + (init-cse-structs)) + +;;; This should be called in the system restart code generated by a +;;; disk save + +(define (load-init-files) + (load-init-file "$HASKELL/.yhaskell") + (load-init-file "~/.yhaskell")) + +(define (load-init-file name) + (when (file-exists? name) + (load name))) + diff --git a/top/top.scm b/top/top.scm new file mode 100644 index 0000000..1a63923 --- /dev/null +++ b/top/top.scm @@ -0,0 +1,46 @@ +;;; top.scm -- compilation unit definition for the top level + +;;; Global includes the ast definitions and all global data structures +;;; used in the compiler. + +(define-compilation-unit global + (source-filename "$Y2/top/") + (require ast) + (unit has-utils + (source-filename "has-utils.scm")) + (unit core-definitions + (require has-utils) + (source-filename "core-definitions.scm")) + (unit core-symbols + (require core-definitions) + (source-filename "core-symbols.scm")) + (unit core-init + (require core-symbols) + (source-filename "core-init.scm")) + (unit globals + (require core-init) + (source-filename "globals.scm")) + (unit has-macros + (source-filename "has-macros.scm")) + ) + + +;;; These files do not need to be required by other units + +(define-compilation-unit top-level + (source-filename "$Y2/top/") + (require global) + (unit phases + (source-filename "phases.scm")) + (unit system-init + (source-filename "system-init.scm")) + (unit errors + (source-filename "errors.scm")) + (unit tuple + (source-filename "tuple.scm")) + (unit symbol-table + (source-filename "symbol-table.scm")) + ) + + + diff --git a/top/tuple.scm b/top/tuple.scm new file mode 100644 index 0000000..b736ee2 --- /dev/null +++ b/top/tuple.scm @@ -0,0 +1,87 @@ +;;; This file creates type definitions for tuples of arbitrary size. + +(define *tuple-definitions* '()) + +(define (init-tuples) + (setf *tuple-definitions* '())) + +(define (tuple-tycon k) + (let ((tycon (assq k *tuple-definitions*))) + (if (eq? tycon '#f) + (new-tuple-tycon k) + (tuple-2-2 tycon)))) + +(define (tuple-constructor k) + (car (algdata-constrs (tuple-tycon k)))) + +(define (is-tuple-constructor? x) + (and (con? x) (is-tuple-tycon? (con-alg x)))) + +(define (is-tuple-tycon? x) + (and (algdata? x) (algdata-real-tuple? x))) + +(define (tuple-constructor-arity x) + (con-arity x)) + +(predefine (ast->gtype c t)) ; in util/type-utils.scm +(predefine (**arrow-type/l args)) ; in util/constructors.scm +(predefine (**tyvar x)) ; in util/constructors.scm + +(define (new-tuple-tycon k) + (cond ((eqv? k 0) + (core-symbol "UnitType")) + (else + (let* ((name (string->symbol (format '#f "Tuple~A" k))) + (cname (string->symbol (format '#f ";MkTuple~A" k))) + (dummy-vars (gen-dummy-names k)) + (algdata (make algdata + (name name) + (module '*core*) + (unit '*core*) + (exported? '#t) + (arity k) + (n-constr 1) + (context '()) + (tyvars dummy-vars) + (classes '()) ;; filled in later + (enum? '#f) + (tuple? '#t) + (real-tuple? '#t) + (deriving '()))) + (constr (make con + (name cname) + (module '*core*) + (unit '*core*) + (exported? '#t) + (arity k) + (types (map (function **tyvar) dummy-vars)) + (tag 0) + (alg algdata) + (slot-strict? '()) + (infix? '#f))) + (tyvars (map (function **tyvar) dummy-vars)) + (tuple-type (**tycon/def algdata tyvars))) + (dotimes (i k) + (push '#f (con-slot-strict? constr))) + (setf (algdata-signature algdata) + (ast->gtype '() tuple-type)) + (setf (con-signature constr) + (ast->gtype '() (**arrow-type/l + (append tyvars (list tuple-type))))) + (setf (algdata-constrs algdata) + (list constr)) + (push (tuple k algdata) *tuple-definitions*) + algdata)))) + +(define (gen-dummy-names n) + (gen-dummy-names-1 n '())) + +(define (gen-dummy-names-1 n l) + (if (eqv? n 0) + l + (gen-dummy-names-1 (1- n) + (cons (string->symbol (format '#f "a~A" n)) l)))) + + + + diff --git a/type/README b/type/README new file mode 100644 index 0000000..dc40c55 --- /dev/null +++ b/type/README @@ -0,0 +1 @@ +This directory contains the type inference phase. diff --git a/type/default.scm b/type/default.scm new file mode 100644 index 0000000..529f4f8 --- /dev/null +++ b/type/default.scm @@ -0,0 +1,47 @@ +;;; This handles the default rule. + +(define (maybe-default-ambiguous-tyvar type def module) + (let ((classes (ntyvar-context type))) + (and (not (null? classes)) ; this happens only during cleanup after an error + (let ((non-standard? '#f) + (numeric? '#f)) + (dolist (class classes) + (cond ((eq? (class-kind class) 'numeric) + (setf numeric? '#t)) + ((not (eq? (class-kind class) 'standard)) + (setf non-standard? '#t)))) + (cond ((or non-standard? (not numeric?)) + (remember-context def + (phase-error 'Non-defaultable-ambiguous-context +"An ambiguous context, ~A, cannot be defaulted.~%Ambiguity in call to ~A~%" + classes def)) + '#f) + (else + (find-default-type type classes classes + (tuple-2-2 (assq module *default-decls*))))))))) + +(define (find-default-type tyvar classes all-classes defaults) + (cond ((null? defaults) + (phase-error 'no-default-applies + "Ambiguous context: ~A~%No default applies.~%" + all-classes) + '#f) + ((null? classes) + (instantiate-tyvar tyvar (car defaults)) + '#t) + ((type-in-class? (car defaults) (car classes)) + (find-default-type tyvar (cdr classes) all-classes defaults)) + (else + (find-default-type tyvar all-classes all-classes (cdr defaults))))) + +(define (type-in-class? ntype class) + (let* ((ntype (expand-ntype-synonym ntype)) + (alg (ntycon-tycon ntype)) + (inst (lookup-instance alg class))) + (if (eq? inst '#f) + '#f + (let ((res '#t)) + (do-contexts (c (instance-context inst)) (ty (ntycon-args ntype)) + (when (not (type-in-class? ty c)) + (setf res '#f))) + res)))) diff --git a/type/dictionary.scm b/type/dictionary.scm new file mode 100644 index 0000000..0a0260e --- /dev/null +++ b/type/dictionary.scm @@ -0,0 +1,229 @@ + +;;; type/dictionary.scm + +;;; This function supports dictionary conversion. It creates lambda +;;; variables to bind to the dictionary args needed by the context. +;;; The actual conversion to lambda is done in the cfn. Each tyvar in +;;; the context has an associated mapping from class to dictionary +;;; variable. This mapping depends on the decl containing the placeholder +;;; since different recursive decls share common tyvars. The mapping is +;;; two levels: decl -> class -> var. + +;;; Due to language restrictions this valdef must be a simple variable +;;; definition. + +(define (dictionary-conversion/definition valdef tyvars) + (let* ((var (decl-var valdef)) + (type (var-type var)) + (context (gtype-context type)) + (dict-param-vars '())) + (dolist (c context) + (let ((tyvar (car tyvars)) + (dparams '())) + (when (not (null? c)) + (dolist (class c) + (let ((var (create-temp-var + (string-append "d_" + (symbol->string (def-name class)))))) + (setf (var-force-strict? var) '#t) + (push (tuple class var) dparams) + (push var dict-param-vars))) + (push (tuple valdef dparams) (ntyvar-dict-params tyvar))) + (setf tyvars (cdr tyvars)))) + (setf (valdef-dictionary-args valdef) (nreverse dict-param-vars)))) + +;;; These routines deal with dict-var processing. + +;;; This discharges the tyvars associated with dictionaries. The dict-vars +;;; to be processed at the next level are returned. + +(define (process-placeholders placeholders deferred decls) + (if (null? placeholders) + deferred + (let ((d1 (process-placeholder (car placeholders) deferred decls))) + (process-placeholders (cdr placeholders) d1 decls)))) + +;;; This processes a placeholder. The following cases arise: +;;; a) the variable has already been processed (no placeholders remain) - +;;; ignore it. placeholders may contain duplicates so this is likely. +;;; b) the type variable is from an outer type environment (in ng-list) +;;; and should just be passed up to the next level (added to old-placeholders) +;;; c) the type variable is associated with a dictionary parameter +;;; d) the type variable is instantiated to a type constructor +;;; e) the type variable is ambiguous (none of the above) + +(define (process-placeholder p deferred decls) + (let* ((tyvar (placeholder-tyvar p)) + (type (prune tyvar))) + (cond ((ntycon? type) + (process-instantiated-tyvar + (expand-ntype-synonym type) p deferred decls)) + ((non-generic? type) + (cons p deferred)) + ((not (null? (ntyvar-dict-params type))) + (if (dict-placeholder? p) + (placeholder->dict-param p (ntyvar-dict-params type) decls) + (placeholder->method p (ntyvar-dict-params type) decls)) + deferred) + (else + ;; Since default types are monotypes, no new vars will + ;; be added to old-placeholders + (when (maybe-default-ambiguous-tyvar + type (placeholder-overloaded-var p) + (valdef-module (car (placeholder-enclosing-decls p)))) + (process-placeholder p deferred decls)) + deferred)))) + +;;; The type variable is associated with a dictionary parameter. The only +;;; complication here is that the class needed may not be directly available - +;;; it may need to be obtained from the super classes of the parameter +;;; dictionaries. + +(define (placeholder->dict-param p param-vars decls) + (let ((class (dict-placeholder-class p)) + (edecls (dict-placeholder-enclosing-decls p))) + (setf (placeholder-exp p) + (dict-reference-code class (locate-params param-vars edecls decls))))) + +(define (dict-reference-code class param-vars) + (let ((var (assq class param-vars))) + (if (not (eq? var '#f)) + (**var/def (tuple-2-2 var)) + (search-superclasses class param-vars)))) + +(define (locate-params param-vars enclosing-decls decls) + (if (null? (cdr param-vars)) + (tuple-2-2 (car param-vars)) + (let ((decl (search-enclosing-decls enclosing-decls decls))) + (tuple-2-2 (assq decl param-vars))))) + +;;; This finds the first dictionary containing the needed class in its +;;; super classes and generates a selector to get the needed dictionary. + +(define (search-superclasses class param-vars) + (let ((pclass (tuple-2-1 (car param-vars)))) + (if (memq class (class-super* pclass)) + (**dsel/dict pclass class (**var/def (tuple-2-2 (car param-vars)))) + (search-superclasses class (cdr param-vars))))) + +(define (placeholder->method p param-vars decls) + (let* ((method (method-placeholder-method p)) + (class (method-var-class method)) + (edecls (placeholder-enclosing-decls p)) + (params (locate-params param-vars edecls decls))) + (setf (placeholder-exp p) + (method-reference-code method class params)))) + +(define (method-reference-code m c param-vars) + (let ((pclass (tuple-2-1 (car param-vars)))) + (if (or (eq? c pclass) + (memq c (class-super* pclass))) + (let* ((msel (assq m (class-selectors pclass))) + (mvar (tuple-2-2 msel))) + (**app (**var/def mvar) (**var/def (tuple-2-2 (car param-vars))))) + (method-reference-code m c (cdr param-vars))))) + +;;; This is for tyvars instantiated to a tycon. A reference to the +;;; appropriate dictionary is generated. This reference must be recursively +;;; dictionary converted since dictionaries may need subdictionaries +;;; when referenced. + +(define (process-instantiated-tyvar tycon p deferred decls) + (let* ((alg (ntycon-tycon tycon)) + (edecls (placeholder-enclosing-decls p)) + (var (placeholder-overloaded-var p)) + (class (if (dict-placeholder? p) + (dict-placeholder-class p) + (method-var-class (method-placeholder-method p)))) + (instance (lookup-instance alg class))) + (if (dict-placeholder? p) + (mlet (((code def1) + (generate-dict-ref instance tycon deferred decls edecls var))) + (setf (placeholder-exp p) code) + (setf deferred def1)) + (let ((method (method-placeholder-method p))) + (if (every (function null?) (instance-gcontext instance)) + (let ((mvar (tuple-2-2 + (assq method (instance-methods instance))))) + (setf (placeholder-exp p) (**var/def mvar))) + (mlet (((code def1) + (generate-dict-ref + instance tycon deferred decls edecls var)) + (sel (tuple-2-2 (assq method (class-selectors class))))) + (setf (method-placeholder-exp p) (**app (**var/def sel) code)) + (setf deferred def1))))) + deferred)) + +;;; This generates a reference to a specific dictionary and binds +;;; needed subdictionaries. Since subdictionaries may be part of the outer +;;; type environment new placeholders may be generated for later resolution. + +(define (generate-dict-ref instance type deferred decls edecls var) + (let* ((ctxt (instance-gcontext instance)) + (dict (dict-ref-code instance))) + (do-contexts (class ctxt) (ty (ntycon-args type)) + (let ((ntype (prune ty))) + (cond + ((ntycon? ntype) + (mlet ((ntype (expand-ntype-synonym ntype)) + (alg (ntycon-tycon ntype)) + (instance (lookup-instance alg class)) + ((code dv1) + (generate-dict-ref + instance ntype deferred decls edecls var))) + (setf dict (**app dict code)) + (setf deferred dv1))) + ((non-generic? ntype) + (let ((p (**dict-placeholder + class ntype edecls var))) + (setf dict (**app dict p)) + (push p deferred))) + ((null? (ntyvar-dict-params ntype)) + (let ((ref-code (**dict-placeholder + class ntype edecls var))) + (when (maybe-default-ambiguous-tyvar + ntype var (valdef-module (car edecls))) + (process-placeholder ref-code '() decls)) + (setf dict (**app dict ref-code)))) + (else + (let ((p (locate-params (ntyvar-dict-params ntype) edecls decls))) + (setf dict (**app dict (dict-reference-code class p)))))))) + (values dict deferred))) + +;;; The following routines deal with recursive placeholders. The basic +;;; strategy is to pass the entire context as a parameter with each +;;; recursive call (this could be optimized later to make use of an +;;; internal entry point). The basic complication is that the context +;;; of each function in a letrec may be arranged differently. + +;;; This generates a call inside decl 'from' to the var 'to'. Vmap is an +;;; alist from vars to a list of vars corresponding to the gtyvars of +;;; the decl signature. + +(define (recursive-call-code from to vmap) + (let ((exp (**var/def to)) + (tyvars (tuple-2-2 (assq to vmap))) + (contexts (gtype-context (var-type to)))) + (do-contexts (class contexts) (tyvar tyvars) + (setf exp (**app exp (locate-param-var tyvar class from)))) + exp)) + +(define (locate-param-var tyvar class decl) + (let ((vmap (tuple-2-2 (assq decl (ntyvar-dict-params tyvar))))) + (**var/def (tuple-2-2 (assq class vmap))))) + +;;; This is used to get the code for a specific dictionary reference. + +(define (dict-ref-code instance) + (**var/def (instance-dictionary instance))) + +;;; This is used to locate the correct enclosing decl. + +(define (search-enclosing-decls decl-list decls) + (cond ((null? decl-list) + (error "Lost decl in search-enclosing-decls!")) + ((memq (car decl-list) decls) + (car decl-list)) + (else + (search-enclosing-decls (cdr decl-list) decls)))) + diff --git a/type/expression-typechecking.scm b/type/expression-typechecking.scm new file mode 100644 index 0000000..f4606d1 --- /dev/null +++ b/type/expression-typechecking.scm @@ -0,0 +1,364 @@ +;;; This file contains typecheckers for all expressions except vars and +;;; declarations. + +;;; From valdef-structs: +;;; valdef, single-fun-def are in type-decls + +(define-type-checker guarded-rhs + (type-check guarded-rhs rhs rhs-type + (type-check guarded-rhs guard guard-type + (type-unify guard-type *bool-type* + (type-mismatch/fixed (guarded-rhs-guard object) + "Guards must be of type Bool" guard-type)) + (return-type object rhs-type)))) + +;;; These type checkers deal with patterns. + +(define-type-checker as-pat + (type-check as-pat pattern as-type + (setf (var-type (var-ref-var (as-pat-var object))) as-type) + (return-type object as-type))) + +(define-type-checker irr-pat + (type-check irr-pat pattern pattern-type + (return-type object pattern-type))) + +(define-type-checker var-pat + (fresh-type var-type + (setf (var-type (var-ref-var (var-pat-var object))) var-type) + (return-type object var-type))) + +(define-type-checker wildcard-pat + (fresh-type pat-type + (return-type object pat-type))) + +;;; Constant patterns create a piece of code to actually to the +;;; match: ((==) k), where k is the constant. This code is placed in the +;;; match-fn slot of the const-pat and is used by the cfn. + +(define-type-checker const-pat + (let* ((val (const-pat-value object)) + (match-fn (**app (**var/def (core-symbol "==")) val))) + (setf (const-pat-match-fn object) match-fn) + (type-check const-pat match-fn match-type + (fresh-type res-type + (type-unify match-type (**arrow res-type *bool-type*) #f) + (return-type object res-type))))) + +(define-type-checker plus-pat + (let* ((kp (**int (plus-pat-k object))) + (km (**int (- (plus-pat-k object)))) + (match-fn (**app (**var/def (core-symbol "<=")) kp)) + (bind-fn (**app (**var/def (core-symbol "+")) km))) + (setf (plus-pat-match-fn object) match-fn) + (setf (plus-pat-bind-fn object) bind-fn) + (fresh-type res-type + (setf (ntyvar-context res-type) (list (core-symbol "Integral"))) + (type-check plus-pat match-fn match-type + (type-check plus-pat bind-fn bind-type + (type-check plus-pat pattern pat-type + (type-unify match-type (**arrow pat-type *bool-type*) #f) + (type-unify bind-type (**arrow pat-type pat-type) #f) + (type-unify res-type pat-type #f) + (return-type object res-type))))))) + +(define-type-checker pcon + (type-check/list pcon pats arg-types + (fresh-type res-type + (let ((con-type (instantiate-gtype (con-signature (pcon-con object))))) + (type-unify con-type (**arrow/l-2 arg-types res-type) #f) + (return-type object res-type))))) + +(define-type-checker list-pat + (if (null? (list-pat-pats object)) + (return-type object (instantiate-gtype + (algdata-signature (core-symbol "List")))) + (type-check/unify-list list-pat pats element-type + (type-mismatch/list object + "List elements have different types") + (return-type object (**list-of element-type))))) + +;;; These are in the order defined in exp-structs.scm + +(define-type-checker lambda + (with-new-tyvars + (fresh-monomorphic-types (length (lambda-pats object)) arg-vars + (type-check/list lambda pats arg-types + (unify-list arg-types arg-vars) + (type-check lambda body body-type + (return-type object (**arrow/l-2 arg-vars body-type))))))) + +(define-type-checker let + (type-check/decls let decls + (type-check let body let-type + (return-type object let-type)))) + +(define-type-checker if + (type-check if test-exp test-type + (type-unify test-type *bool-type* + (type-mismatch/fixed object + "The test in an if statement must be of type Bool" + test-type)) + (type-check if then-exp then-type + (type-check if else-exp else-type + (type-unify then-type else-type + (type-mismatch object + "then and else clauses have different types" + then-type else-type)) + (return-type object then-type))))) + +(define-type-checker case + (with-new-tyvars + (let ((case-exp object)) ; needed since object is rebound later + (fresh-monomorphic-type arg-type + (type-check case exp exp-type + (type-unify arg-type exp-type #f) ; just to make it monomorphic + (fresh-type res-type + (dolist (object (case-alts object)) + (recover-type-error ;;; %%% Needs work + (type-check alt pat pat-type + (type-unify pat-type arg-type + (type-mismatch case-exp + "Case patterns type conflict." + pat-type arg-type)) + (type-check/decls alt where-decls + (type-check/unify-list alt rhs-list rhs-type + (type-mismatch/list case-exp + "Guarded expressions must have the same type") + (type-unify rhs-type res-type + (type-mismatch case-exp + "Case expression alternatives must have the same type" + rhs-type res-type))))))) + (return-type case-exp res-type))))))) + +;;; Expressions with signatures are transformed into let expressions +;;; with signatures. + +;;; exp :: type is rewritten as +;;; let temp = exp +;;; temp :: type +;;; in temp + +(define-type-checker exp-sign + (type-rewrite + (let* ((temp-var (create-temp-var "TC")) + (decl (**valdef (**var-pat/def temp-var) '() (exp-sign-exp object))) + (let-exp (**let (list decl) (**var/def temp-var))) + (signature (exp-sign-signature object))) + (setf (var-signature temp-var) + (ast->gtype (signature-context signature) + (signature-type signature))) + let-exp))) + +;;; Rather than complicate the ast structure with a new node for dictSel +;;; we recognize the dictSel primitive as an application and treat it +;;; specially. + +(define-type-checker app + (if (and (var-ref? (app-fn object)) + (eq? (var-ref-var (app-fn object)) (core-symbol "dictSel"))) + (type-check-dict-sel (app-arg object)) + (type-check app fn fn-type + (type-check app arg arg-type + (fresh-type res-type + (fresh-type arg-type-1 + (type-unify fn-type (**arrow arg-type-1 res-type) + (type-mismatch/fixed object + "Attempt to call a non-function" + fn-type)) + (type-unify arg-type-1 arg-type + (type-mismatch object + "Argument type mismatch" arg-type-1 arg-type)) + (return-type object res-type))))))) + +;;; This is a special hack for typing dictionary selection as used in +;;; generic tuple functions. This extracts a dictionary from a TupleDict +;;; object and uses is to resolve the overloading of a designated +;;; expression. The expresion must generate exactly one new context. + +(define (type-check-dict-sel arg) + (when (or (not (app? arg)) + (not (app? (app-fn arg)))) + (dict-sel-error)) + (let* ((exp (app-fn (app-fn arg))) + (dict-var (app-arg (app-fn arg))) + (i (app-arg arg)) + (p (dynamic *placeholders*))) + (mlet (((object exp-type) (dispatch-type-check exp))) + ; check for exactly one new context + (when (or (eq? (dynamic *placeholders*) p) + (not (eq? (cdr (dynamic *placeholders*)) p))) + (dict-sel-error)) + (mlet ((placeholder (car (dynamic *placeholders*))) + (tyvar (placeholder-tyvar placeholder)) + ((dict-var-ast dict-var-type) (dispatch-type-check dict-var)) + ((index-ast index-type) (dispatch-type-check i))) + (setf (ntyvar-context tyvar) '()) ; prevent context from leaking out + (setf (dynamic *placeholders*) p) + (type-unify dict-var-type + (**ntycon (core-symbol "TupleDicts") '()) #f) + (type-unify index-type *int-type* #f) + (cond ((method-placeholder? placeholder) + (dict-sel-error)) ; I am lazy. This means that + ; dictSel must not be passed a method + (else + (setf (placeholder-exp placeholder) + (**app (**var/def (core-symbol "dictSel")) + dict-var-ast index-ast)))) + (return-type object exp-type))))) + +(define (dict-sel-error) + (fatal-error 'dict-sel-error "Bad dictSel usage.")) + +(define-type-checker con-ref + (return-type object (instantiate-gtype (con-signature (con-ref-con object))))) + +(define-type-checker integer-const + (cond ((const-overloaded? object) + (setf (const-overloaded? object) '#f) + (type-rewrite (**fromInteger object))) + (else + (return-type object *Integer-type*)))) + +(define-type-checker float-const + (cond ((const-overloaded? object) + (setf (const-overloaded? object) '#f) + (type-rewrite (**fromRational object))) + (else + (return-type object *Rational-type*)))) + +(define-type-checker char-const + (return-type object *char-type*)) + +(define-type-checker string-const + (return-type object *string-type*)) + +(define-type-checker list-exp + (if (null? (list-exp-exps object)) + (return-type object (instantiate-gtype + (algdata-signature (core-symbol "List")))) + (type-check/unify-list list-exp exps element-type + (type-mismatch/list object + "List elements do not share a common type") + (return-type object (**list-of element-type))))) + +(define-type-checker sequence + (type-rewrite (**enumFrom (sequence-from object)))) + +(define-type-checker sequence-to + (type-rewrite (**enumFromTo (sequence-to-from object) + (sequence-to-to object)))) + +(define-type-checker sequence-then + (type-rewrite (**enumFromThen (sequence-then-from object) + (sequence-then-then object)))) + +(define-type-checker sequence-then-to + (type-rewrite (**enumFromThenTo (sequence-then-to-from object) + (sequence-then-to-then object) + (sequence-then-to-to object)))) + +(define-type-checker list-comp + (with-new-tyvars + (dolist (object (list-comp-quals object)) + (if (is-type? 'qual-generator object) + (fresh-type pat-type + (push pat-type (dynamic *non-generic-tyvars*)) + (type-check qual-generator pat pat-type-1 + (type-unify pat-type pat-type-1 #f) + (type-check qual-generator exp qual-exp-type + (type-unify (**list-of pat-type) qual-exp-type + (type-mismatch/fixed object + "Generator expression is not a list" qual-exp-type))))) + (type-check qual-filter exp filter-type + (type-unify filter-type *bool-type* + (type-mismatch/fixed object + "Filter must have type Bool" filter-type))))) + (type-check list-comp exp exp-type + (return-type object (**list-of exp-type))))) + +(define-type-checker section-l + (type-check section-l op op-type + (type-check section-l exp exp-type + (fresh-type a-type + (fresh-type b-type + (fresh-type c-type + (type-unify op-type (**arrow a-type b-type c-type) + (type-mismatch/fixed object + "Binary function required in section" op-type)) + (type-unify b-type exp-type + (type-mismatch object + "Argument type mismatch" b-type exp-type)) + (return-type object (**arrow a-type c-type)))))))) + +(define-type-checker section-r + (type-check section-r op op-type + (type-check section-r exp exp-type + (fresh-type a-type + (fresh-type b-type + (fresh-type c-type + (type-unify op-type (**arrow a-type b-type c-type) + (type-mismatch/fixed object + "Binary function required" op-type)) + (type-unify exp-type a-type + (type-mismatch object + "Argument type mismatch" a-type exp-type)) + (return-type object (**arrow b-type c-type)))))))) + +(define-type-checker omitted-guard + (return-type object *bool-type*)) + +(define-type-checker con-number + (let ((arg-type (instantiate-gtype + (algdata-signature (con-number-type object))))) + (type-check con-number value arg-type1 + (type-unify arg-type arg-type1 #f) + (return-type object *int-type*)))) + +(define-type-checker sel + (let ((con-type (instantiate-gtype + (con-signature (sel-constructor object))))) + (mlet (((res-type exp-type1) (get-ith-type con-type (sel-slot object)))) + (type-check sel value exp-type + (type-unify exp-type exp-type1 #f) + (return-type object res-type))))) + +(define (get-ith-type type i) + (let ((args (ntycon-args type))) ; must be an arrow + (if (eq? i 0) + (values (car args) (get-ith-type/last (cadr args))) + (get-ith-type (cadr args) (1- i))))) + +(define (get-ith-type/last type) + (if (eq? (ntycon-tycon type) (core-symbol "Arrow")) + (get-ith-type/last (cadr (ntycon-args type))) + type)) + +(define-type-checker is-constructor + (let ((alg-type (instantiate-gtype + (algdata-signature + (con-alg (is-constructor-constructor object)))))) + (type-check is-constructor value arg-type + (type-unify arg-type alg-type #f) + (return-type object *bool-type*)))) + +(define-type-checker cast + (type-check cast exp _ + (fresh-type res + (return-type object res)))) + +;;; This is used for overloaded methods. The theory is to avoid supplying +;;; the context at the class level. This type checks the variable as if it had +;;; the supplied signature. + +(define-type-checker overloaded-var-ref + (let* ((var (overloaded-var-ref-var object)) + (gtype (overloaded-var-ref-sig object)) + (ovar-type (var-type var))) + (when (recursive-type? ovar-type) + (error + "Implementation error: overloaded method found a recursive type")) + (mlet (((ntype new-vars) (instantiate-gtype/newvars gtype)) + (object1 (insert-dict-placeholders + (**var/def var) new-vars object))) + (return-type object1 ntype)))) diff --git a/type/pattern-binding.scm b/type/pattern-binding.scm new file mode 100644 index 0000000..769e155 --- /dev/null +++ b/type/pattern-binding.scm @@ -0,0 +1,38 @@ +;;; This implements the pattern binding rule. + +(define (apply-pattern-binding-rule? decls) + (not + (every (lambda (decl) + (or (function-binding? decl) + (simple-pattern-binding-with-signature? decl))) + decls))) + +(define (function-binding? decl) + (let ((defs (valdef-definitions decl))) + (not (null? (single-fun-def-args (car defs)))))) + +(define (simple-pattern-binding-with-signature? decl) + (let ((lhs (valdef-lhs decl)) + (defs (valdef-definitions decl))) + (and (is-type? 'var-pat lhs) + (null? (single-fun-def-args (car defs))) + (not (eq? (var-signature (var-ref-var (var-pat-var lhs))) '#f))))) + +(define (do-pattern-binding-rule decls necessary-tyvars ng-list) + (setf ng-list (append necessary-tyvars ng-list)) + (find-exported-pattern-bindings decls) + ng-list) + +(define (find-exported-pattern-bindings decls) + (dolist (decl decls) + (dolist (var-ref (collect-pattern-vars (valdef-lhs decl))) + (let ((var (var-ref-var var-ref))) + (when (def-exported? var) + (recoverable-error 'exported-pattern-binding + "Can't export pattern binding of ~A~%" var-ref)) + (when (not (eq? (var-signature var) '#f)) + (recoverable-error 'entire-group-needs-signature + "Variable ~A signature declaration ignored~%" var-ref)))))) + + + diff --git a/type/type-decl.scm b/type/type-decl.scm new file mode 100644 index 0000000..790c0ca --- /dev/null +++ b/type/type-decl.scm @@ -0,0 +1,337 @@ +;;; This deals with declarations (let & letrec). The input is a list of +;;; declarations (valdefs) which may contain recursive-decl-groups, as +;;; introduced in dependency analysis. This function alters the list +;;; of non-generic type variables. Expressions containing declarations +;;; need to rebind the non-generic list around the decls and all expressions +;;; within their scope. + +;;; This returns an updated decl list with recursive decl groups removed. + +(define (type-decls decls) + (cond ((null? decls) + '()) + ((is-type? 'recursive-decl-group (car decls)) + (let ((d (recursive-decl-group-decls (car decls)))) + (type-recursive d) + (append d (type-decls (cdr decls))))) + (else + (type-non-recursive (car decls)) + (cons (car decls) + (type-decls (cdr decls)))))) + +;;; This typechecks a mutually recursive group of declarations (valdefs). +;;; Generate a monomorphic variable for each declaration and unify it with +;;; the lhs of the decl. The variable all-vars collects all variables defined +;;; by the declaration group. Save the values of placeholders and ng-list +;;; before recursing. + +;;; The type of each variable is marked as recursive. + +(define (type-recursive decls) + (let ((old-ng (dynamic *non-generic-tyvars*)) + (old-placeholders (dynamic *placeholders*)) + (all-vars '()) + (new-tyvars '()) + (decls+tyvars '())) + ;; on a type error set all types to `a' and give up. + (setf (dynamic *placeholders*) '()) + (recover-type-error + (lambda (r) + (make-dummy-sigs decls) + (setf (dynamic *dict-placeholders*) old-placeholders) + (funcall r)) + ;; Type the lhs of each decl and then mark each variable bound + ;; in the decl as recursive. + (dolist (d decls) + (fresh-type lhs-type + (push lhs-type (dynamic *non-generic-tyvars*)) + (push lhs-type new-tyvars) + (type-decl-lhs d lhs-type) + (push (tuple d lhs-type) decls+tyvars)) + (dolist (var-ref (collect-pattern-vars (valdef-lhs d))) + (let ((var (var-ref-var var-ref))) + (push var all-vars) + (setf (var-type var) + (make recursive-type (type (var-type var)) + (placeholders '())))))) + +;;; This types the decl right hand sides. Each rhs type is unified with the +;;; tyvar corresponding to the lhs. Before checking the signatures, the +;;; ng-list is restored. + + (dolist (d decls+tyvars) + (let ((rhs-type (type-decl-rhs (tuple-2-1 d))) + (lhs-type (tuple-2-2 d))) + (type-unify lhs-type rhs-type + (type-mismatch (tuple-2-1 d) + "Decl type mismatch" lhs-type rhs-type)))) + (setf (dynamic *non-generic-tyvars*) old-ng) + (let ((sig-contexts (check-user-signatures all-vars))) + +;;; This generalizes the signatures of recursive decls. First, the +;;; context of the declaration group is computed. Any tyvar in the +;;; bodies with a non-empty context must appear in all signatures that +;;; are non-ambiguous. + + (let* ((all-tyvars (collect-tyvars/l new-tyvars)) + (overloaded-tyvars '())) + (dolist (tyvar all-tyvars) + (when (and (ntyvar-context tyvar) (not (non-generic? tyvar))) + (push tyvar overloaded-tyvars))) + (reconcile-sig-contexts overloaded-tyvars sig-contexts) + ;; We should probably also emit a warning about inherently + ;; ambiguous decls. + (when (and overloaded-tyvars + (apply-pattern-binding-rule? decls)) + (setf (dynamic *non-generic-tyvars*) + (do-pattern-binding-rule + decls overloaded-tyvars old-ng)) + (setf overloaded-tyvars '())) + ;; The next step is to compute the signatures of the defined + ;; variables and to define all recursive placeholders. When + ;; there is no context the placeholders become simple var refs. + ;; and the types are simply converted. + (cond ((null? overloaded-tyvars) + (dolist (var all-vars) + (let ((r (var-type var))) + (setf (var-type var) (recursive-type-type (var-type var))) + (dolist (p (recursive-type-placeholders r)) + (setf (recursive-placeholder-exp p) + (**var/def var))) + (generalize-type var)))) + ;; When the declaration has a context things get very hairy. + ;; First, grap the recursive placeholders before generalizing the + ;; types. + (else + ;; Mark the overloaded tyvars as read-only. This prevents + ;; signature unification from changing the set of tyvars + ;; defined in the mapping. + (dolist (tyvar overloaded-tyvars) + (setf (ntyvar-read-only? tyvar) '#t)) + (let ((r-placeholders '())) + (dolist (var all-vars) + (let ((rt (var-type var))) + (dolist (p (recursive-type-placeholders rt)) + (push p r-placeholders)) + (setf (var-type var) (recursive-type-type rt)))) + ;; Now compute a signature for each definition and do dictionary + ;; conversion. The var-map defines the actual parameter associated + ;; with each of the overloaded tyvars. + (let ((var-map (map (lambda (decl) + (tuple (decl-var decl) + (generalize-overloaded-type + decl overloaded-tyvars))) + decls))) + ;; Finally discharge each recursive placeholder. + (dolist (p r-placeholders) + (let ((ref-to (recursive-placeholder-var p)) + (decl-from + (search-enclosing-decls + (recursive-placeholder-enclosing-decls p) + decls))) + (setf (recursive-placeholder-exp p) + (recursive-call-code decl-from ref-to var-map))) + ))))) + (setf (dynamic *placeholders*) + (process-placeholders + (dynamic *placeholders*) old-placeholders decls))))))) + +;;; Non-recursive decls are easier. Save the placeholders, use a fresh type +;;; for the left hand side, check signatures, and generalize. + +(define (type-non-recursive decl) + (remember-context decl + (fresh-type lhs-type + (let ((old-placeholders (dynamic *placeholders*)) + (all-vars (map (lambda (x) (var-ref-var x)) + (collect-pattern-vars (valdef-lhs decl))))) + (setf (dynamic *placeholders*) '()) + (recover-type-error + (lambda (r) + (make-dummy-sigs (list decl)) + (setf (dynamic *placeholders*) old-placeholders) + (funcall r)) + (type-decl-lhs decl lhs-type) + (let ((rhs-type (type-decl-rhs decl))) + (type-unify lhs-type rhs-type + (type-mismatch decl + "Decl type mismatch" lhs-type rhs-type))) + (check-user-signatures all-vars) + (let ((all-tyvars (collect-tyvars lhs-type)) + (overloaded-tyvars '())) + (dolist (tyvar all-tyvars) + (when (ntyvar-context tyvar) + (push tyvar overloaded-tyvars))) + (when (and overloaded-tyvars + (apply-pattern-binding-rule? (list decl))) + (setf (dynamic *non-generic-tyvars*) + (do-pattern-binding-rule + (list decl) overloaded-tyvars (dynamic *non-generic-tyvars*))) + (setf overloaded-tyvars '())) + (if (null? overloaded-tyvars) + (dolist (var all-vars) + (generalize-type var)) + (generalize-overloaded-type decl '())) + (setf (dynamic *placeholders*) + (process-placeholders + (dynamic *placeholders*) old-placeholders (list decl))))))))) + +;;; These functions type check definition components. + +;;; This unifies the type of the lhs pattern with a type variable. + +(define (type-decl-lhs object type) + (dynamic-let ((*enclosing-decls* (cons object (dynamic *enclosing-decls*)))) + (remember-context object + (type-check valdef lhs pat-type + (type-unify type pat-type #f))))) + + +;;; This types the right hand side. The *enclosing-decls* variable is +;;; used to keep track of which decl the type checker is inside. This +;;; is needed for both defaulting (to find which module defaults apply) +;;; and recursive types to keep track of the dictionary parameter variables +;;; for recursive references. + +(define (type-decl-rhs object) + (dynamic-let ((*enclosing-decls* (cons object (dynamic *enclosing-decls*)))) + (remember-context object + (type-check/unify-list valdef definitions res-type + (type-mismatch/list object + "Right hand sides have different types") + res-type)))) + + +;;; This is similar to typing lambda. + +(define-type-checker single-fun-def + (fresh-monomorphic-types (length (single-fun-def-args object)) tyvars + (type-check/list single-fun-def args arg-types + (unify-list tyvars arg-types) + (type-check/decls single-fun-def where-decls + (type-check/unify-list single-fun-def rhs-list rhs-type + (type-mismatch/list object + "Bodies have incompatible types") + (return-type object (**arrow/l-2 arg-types rhs-type))))))) + + +;;; These functions are part of the generalization process. + +;;; This function processes user signature declarations for the set of +;;; variables defined in a declaration. Since unification of one signature +;;; may change the type associated with a previously verified signature, +;;; signature unification is done twice unless only one variable is +;;; involved. The context of the signatures is returned to compare +;;; with the overall context of the declaration group. + +(define (check-user-signatures vars) + (cond ((null? (cdr vars)) + (let* ((var (car vars)) + (sig (var-signature var))) + (if (eq? sig '#f) + '() + (list (tuple var (check-var-signature var sig)))))) + (else + (let ((sigs '())) + (dolist (var vars) + (let ((sig (var-signature var))) + (unless (eq? sig '#f) + (check-var-signature var sig)))) + (dolist (var vars) + (let ((sig (var-signature var))) + (unless (eq? sig '#f) + (push (tuple var (check-var-signature var sig)) sigs)))) + sigs)))) + + +(define (check-var-signature var sig) + (mlet (((sig-type sig-vars) (instantiate-gtype/newvars sig))) + (dolist (tyvar sig-vars) + (setf (ntyvar-read-only? tyvar) '#t)) + (type-unify (remove-recursive-type (var-type var)) sig-type + (signature-mismatch var)) + (dolist (tyvar sig-vars) + (setf (ntyvar-read-only? tyvar) '#f)) + sig-vars)) + +;;; Once the declaration context is computed, it must be compared to the +;;; contexts given by the user. All we need to check is that all tyvars +;;; constrained in the user signatures are also in the decl-context. +;;; All user supplied contexts are correct at this point - we just need +;;; to see if some ambiguous portion of the context exists. + +;;; This error message needs work. We need to present the contexts. + +(define (reconcile-sig-contexts overloaded-tyvars sig-contexts) + (dolist (sig sig-contexts) + (let ((sig-vars (tuple-2-2 sig))) + (dolist (d overloaded-tyvars) + (when (not (memq d sig-vars)) + (type-error +"Declaration signature has insufficiant context in declaration~%~A~%" + (tuple-2-1 sig))))))) + +;;; This is used for noisy type inference + +(define (report-typing var) + (when (memq 'type (dynamic *printers*)) + (let* ((name (symbol->string (def-name var)))) + (when (not (or (string-starts? "sel-" name) + (string-starts? "i-" name) + (string-starts? "default-" name) + (string-starts? "dict-" name))) + (format '#t "~A :: ~A~%" var (var-type var)))))) + +;;; This is used during error recovery. When a type error occurs, all +;;; variables defined in the enclosing declaration are set to type `a' +;;; and typing is resumed. + +(define (make-dummy-sigs decls) + (let ((dummy-type (make gtype (context '(())) + (type (**gtyvar 0))))) + (dolist (d decls) + (dolist (var-ref (collect-pattern-vars (valdef-lhs d))) + (let ((var (var-ref-var var-ref))) + (setf (var-type var) dummy-type)))))) + + +;;; This is used to generalize the variable signatures. If there is +;;; an attached signature, the signature is used. Otherwise the ntype +;;; is converted to a gtype. + +(define (generalize-type var) + (if (eq? (var-signature var) '#f) + (setf (var-type var) (ntype->gtype (var-type var))) + (setf (var-type var) (var-signature var))) + (report-typing var)) + +;;; For overloaded types, it is necessary to map the declaration context +;;; onto the generalized type. User signatures may provide different but +;;; equivilant contexts for different declarations in a decl goup. + +;;; The overloaded-vars argument allows ambiguous contexts. This is not +;;; needed for non-recursive vars since the context cannot be ambiguous. + +(define (generalize-overloaded-type decl overloaded-vars) + (let* ((var (decl-var decl)) + (sig (var-signature var)) + (new-tyvars '())) + (cond ((eq? sig '#f) + (mlet (((gtype tyvars) + (ntype->gtype/env (var-type var) overloaded-vars))) + (setf (var-type var) gtype) + (setf new-tyvars tyvars))) + (else + (mlet (((ntype tyvars) (instantiate-gtype/newvars sig))) + (unify ntype (var-type var)) + (setf (var-type var) sig) + (setf new-tyvars (prune/l tyvars))))) + (report-typing var) + (dictionary-conversion/definition decl new-tyvars) + new-tyvars)) + +(define (remove-recursive-type ty) + (if (recursive-type? ty) + (recursive-type-type ty) + ty)) + diff --git a/type/type-error-handlers.scm b/type/type-error-handlers.scm new file mode 100644 index 0000000..ac7af9c --- /dev/null +++ b/type/type-error-handlers.scm @@ -0,0 +1,40 @@ +;;; This file contains error handlers for the type checker. + +(define (type-error msg . args) + (apply (function phase-error) `(type-error ,msg ,@args)) + (report-non-local-type-error) + (continue-from-type-error)) + +(define (report-non-local-type-error) + (when (pair? (dynamic *type-error-handlers*)) + (funcall (car (dynamic *type-error-handlers*))))) + +(define (continue-from-type-error) + (funcall (car (dynamic *type-error-recovery*)))) + +(define (type-mismatch/fixed object msg type) + (format '#t "While typing ~A:~%~A~%Type: ~A~%" object msg type)) + +(define (type-mismatch object msg type1 type2) + (format '#t "While type checking~%~A~%~A~%Types: ~A~% ~A~%" + object msg type1 type2)) + +(define (type-mismatch/list types object msg) + (format '#t "While typing ~A:~%~A~%Types: ~%" object msg) + (dolist (type types) + (format '#t "~A~%" type))) + +;;; Error handlers + +(define (signature-mismatch var) + (format '#t + "Signature mismatch for ~A~%Inferred type: ~A~%Declared type: ~A~%" + var + (remove-type-wrapper (ntype->gtype (var-type var))) + (var-signature var))) + +(define (remove-type-wrapper ty) + (if (recursive-type? ty) (recursive-type-type ty) ty)) + + + \ No newline at end of file diff --git a/type/type-macros.scm b/type/type-macros.scm new file mode 100644 index 0000000..c6dc168 --- /dev/null +++ b/type/type-macros.scm @@ -0,0 +1,159 @@ + +;;; This file also contains some random globals for the type checker: + +(define-walker type ast-td-type-walker) + +;;; Some pre-defined types +(define *bool-type* '()) +(define *char-type* '()) +(define *string-type* '()) +(define *int-type* '()) +(define *integer-type* '()) +(define *rational-type* '()) + +;;; These two globals are used throughout the typechecker to avoid +;;; passing lots of stuff in each function call. + +(define *placeholders* '()) +(define *non-generic-tyvars* '()) +(define *enclosing-decls* '()) + +;;; Used by the defaulting mechanism + +(define *default-decls* '()) + +;;; Used in error handling & recovery + +(define *type-error-handlers* '()) +(define *type-error-recovery* '()) + + +;;; This associates a type checker function with an ast type. The variable +;;; `object' is bound to the value being types. + +(define-syntax (define-type-checker ast-type . cont) + `(define-walker-method type ,ast-type (object) + ,@cont)) + +;;; This recursively type checks a structure slot in the current object. +;;; This updates the ast in the slot (since type checking rewrites the ast) +;;; and binds the computed type to a variable. The slot must contain an +;;; expression. + +(define-syntax (type-check struct slot var . cont) + `(mlet ((($$$ast$$$ ,var) + (dispatch-type-check (struct-slot ',struct ',slot object)))) + (setf (struct-slot ',struct ',slot object) $$$ast$$$) + ,@cont)) + +;;; This is used to scope decls. + +(define-syntax (with-new-tyvars . cont) + `(dynamic-let ((*non-generic-tyvars* (dynamic *non-generic-tyvars*))) + ,@cont)) + + +;;; Similar to type-check, the slot must contain a list of decls. +;;; This must be done before any reference to a variable defined in the +;;; decls is typechecked. + +(define-syntax (type-check/decls struct slot . cont) + `(with-new-tyvars + (let (($$$decls$$$ + (type-decls (struct-slot ',struct ',slot object)))) + (setf (struct-slot ',struct ',slot object) $$$decls$$$) + ,@cont))) + +;;; The type checker returns an expression / type pair. This +;;; abstracts the returned value. + +(define-syntax (return-type object type) + `(values ,object ,type)) + +;;; When an ast slot contains a list of expressions, there are two +;;; possibilities: the expressions all share the same type or each has +;;; an independant type. In the first case, a single type (computed +;;; by unifying all types in the list) is bound to a variable. + +(define-syntax (type-check/unify-list struct slot var error-handler . cont) + `(mlet ((($$$ast$$$ $$$types$$$) + (do-type-check/list (struct-slot ',struct ',slot object)))) + (setf (struct-slot ',struct ',slot object) $$$ast$$$) + (with-type-error-handler ,error-handler ($$$types$$$) + (unify-list/single-type $$$types$$$) + (let ((,var (car $$$types$$$))) + ,@cont)))) + +;;; When a list of expressions does not share a common type, the result is +;;; a list of types. + +(define-syntax (type-check/list struct slot var . cont) + `(mlet ((($$$ast$$$ ,var) + (do-type-check/list (struct-slot ',struct ',slot object)))) + (setf (struct-slot ',struct ',slot object) $$$ast$$$) + ,@cont)) + +;;; This creates a fresh tyvar and binds it to a variable. + +(define-syntax (fresh-type var . cont) + `(let ((,var (**ntyvar))) + ,@cont)) + +;;; This drives the unification routine. Two types are unified and the +;;; context is updated. Currently no error handling is implemented to +;;; deal with unification errors. + +(define-syntax (type-unify type1 type2 error-handler) + `(with-type-error-handler ,error-handler () + (unify ,type1 ,type2))) + +;;; This generates a fresh set of monomorphic type variables. + +(define-syntax (fresh-monomorphic-types n vars . cont) + `(with-new-tyvars + (let ((,vars '())) + (dotimes (i ,n) + (let ((tv (**ntyvar))) + (push tv ,vars) + (push tv (dynamic *non-generic-tyvars*)))) + ,@cont))) + +;;; This creates a single monomorphic type variable. + +(define-syntax (fresh-monomorphic-type var . cont) + `(let* ((,var (**ntyvar))) + (with-new-tyvars + (push ,var (dynamic *non-generic-tyvars*)) + ,@cont))) + +;;; This is used to rewrite the current ast as a new ast and then +;;; recursively type check the new ast. The original ast is saved for +;;; error message printouts. + +(define-syntax (type-rewrite ast) + `(mlet (((res-ast type) (dispatch-type-check ,ast)) + (res (**save-old-exp object res-ast))) + (return-type res type))) + +;;; These are the type error handlers + +(define-syntax (recover-type-error error-handler . body) + (let ((temp (gensym)) + (err-fn (gensym))) + `(let/cc ,temp + (let ((,err-fn ,error-handler)) + (dynamic-let ((*type-error-recovery* + (cons (lambda () + (funcall ,err-fn ,temp)) + (dynamic *type-error-recovery*)))) + ,@body))))) + +(define-syntax (with-type-error-handler handler extra-args . body) + (if (eq? handler '#f) + `(begin ,@body) + `(dynamic-let ((*type-error-handlers* + (cons (lambda () + (,(car handler) ,@extra-args ,@(cdr handler))) + (dynamic *type-error-handlers*)))) + ,@body))) + diff --git a/type/type-main.scm b/type/type-main.scm new file mode 100644 index 0000000..c5ffe14 --- /dev/null +++ b/type/type-main.scm @@ -0,0 +1,56 @@ + +;;; This is the main entry point to the type checker. + + +(define (do-haskell-type-check object modules) + (type-init modules) + (when (is-type? 'let object) ; may be void + (dynamic-let ((*non-generic-tyvars* '()) + (*placeholders* '()) + (*enclosing-decls* '())) + (type-check/decls let decls + (setf (dynamic *non-generic-tyvars*) '()) + (process-placeholders (dynamic *placeholders*) '() '())))) + 'done) + +;;; This is the main recursive entry to the type checker. + +(define (dispatch-type-check exp) + (remember-context exp + (call-walker type exp))) + +(define (do-type-check/list exps) + (if (null? exps) + (values '() '()) + (mlet (((obj1 type1) (dispatch-type-check (car exps))) + ((objs types) (do-type-check/list (cdr exps)))) + (values (cons obj1 objs) (cons type1 types))))) + +(define (type-init modules) + ;; Built in types + (setf *char-type* (**ntycon (core-symbol "Char") '())) + (setf *string-type* (**ntycon (core-symbol "List") + (list *char-type*))) + (setf *bool-type* (**ntycon (core-symbol "Bool") '())) + (setf *int-type* (**ntycon (core-symbol "Int") '())) + (setf *integer-type* (**ntycon (core-symbol "Integer") '())) + (setf *rational-type* (**ntycon (core-symbol "Ratio") + (list *integer-type*))) + (setf *default-decls* '()) + (dolist (m modules) + (let ((default-types '())) + (dolist (d (default-decl-types (module-default m))) + (let* ((ty (ast->gtype '() d)) + (ntype (gtype-type ty))) + (cond ((not (null? (gtype-context ty))) + (recoverable-error 'not-monotype + "~A is not a monotype in default decl" ty)) + ((not (type-in-class? ntype (core-symbol "Num"))) + (recoverable-error 'not-Num-class + "~A is not in class Num" ty)) + (else + (push ntype default-types))))) + (push (tuple (module-name m) (reverse default-types)) *default-decls*)))) + +(define (remember-placeholder placeholder) + (push placeholder (dynamic *placeholders*))) diff --git a/type/type-vars.scm b/type/type-vars.scm new file mode 100644 index 0000000..4091ce4 --- /dev/null +++ b/type/type-vars.scm @@ -0,0 +1,60 @@ +;;; This type checks a variable. Possible cases: +;;; a) recursive variables +;;; b) method variables +;;; c) generalized variables +;;; d) other variables + +(define-type-checker var-ref + (let* ((var (var-ref-var object)) + (type (var-type var))) + (cond ((method-var? var) +;;; The context of a method variable always has the carrier class +;;; first. + (mlet (((ntype new-tyvars) (instantiate-gtype/newvars type)) + (carrier-tyvar (car new-tyvars)) + (extra-context (cdr new-tyvars)) + (p (**method-placeholder + var carrier-tyvar (dynamic *enclosing-decls*) object)) + (new-object (insert-dict-placeholders p extra-context object))) + (remember-placeholder p) + (return-type (**save-old-exp object new-object) ntype))) + ((recursive-type? type) + (let ((placeholder (**recursive-placeholder + var (dynamic *enclosing-decls*)))) + (push placeholder (recursive-type-placeholders type)) + (return-type placeholder (recursive-type-type type)))) + ((gtype? type) + (mlet (((ntype new-vars) (instantiate-gtype/newvars type)) + (object1 (insert-dict-placeholders object new-vars object))) + (return-type (if (eq? object1 object) + object + (**save-old-exp object object1)) + ntype))) + (else + (return-type object type))))) + +;;; This takes an expression and a context and returns an updated +;;; expression containing placeholders for the context information +;;; implied by the context. Tyvars in the context are added to dict-vars. + +(define (insert-dict-placeholders object tyvars var) + (cond ((null? tyvars) + object) + ((null? (ntyvar-context (car tyvars))) + (insert-dict-placeholders object (cdr tyvars) var)) + (else + (let ((tyvar (car tyvars))) + (insert-dict-placeholders + (insert-dict-placeholders/tyvar + tyvar (ntyvar-context tyvar) object var) + (cdr tyvars) + var))))) + +(define (insert-dict-placeholders/tyvar tyvar classes object var) + (if (null? classes) + object + (let ((p (**dict-placeholder + (car classes) tyvar (dynamic *enclosing-decls*) var))) + (remember-placeholder p) + (insert-dict-placeholders/tyvar tyvar (cdr classes) + (**app object p) var)))) diff --git a/type/type.scm b/type/type.scm new file mode 100644 index 0000000..8a3a82f --- /dev/null +++ b/type/type.scm @@ -0,0 +1,32 @@ +(define-compilation-unit type + (source-filename "$Y2/type/") + (require ast haskell-utils) + (unit type-macros + (source-filename "type-macros.scm")) + (unit unify + (require type-macros) + (source-filename "unify.scm")) + (unit type-main + (require type-macros) + (source-filename "type-main.scm")) + (unit type-decl + (require type-macros) + (source-filename "type-decl.scm")) + (unit dictionary + (require type-macros) + (source-filename "dictionary.scm")) + (unit default + (require type-macros) + (source-filename "default.scm")) + (unit pattern-binding + (require type-macros) + (source-filename "pattern-binding.scm")) + (unit type-vars + (require type-macros) + (source-filename "type-vars.scm")) + (unit expression-typechecking + (require type-macros) + (source-filename "expression-typechecking.scm")) + (unit type-error-handlers + (require type-macros) + (source-filename "type-error-handlers.scm"))) diff --git a/type/unify.scm b/type/unify.scm new file mode 100644 index 0000000..59248c9 --- /dev/null +++ b/type/unify.scm @@ -0,0 +1,154 @@ + +;;; File: type/unify.scm Author: John + +;;; This is the basic unification algorithm used in type checking. + +;;; Unification failure invokes the current type error handler + +;;; Start by removing instantiated type variables from the type. + +(define (unify type1 type2) + (unify-1 (prune type1) (prune type2))) + +;;; The only real tweak here is the read-only bit on type variables. +;;; The rule is that a RO tyvar can be unified only with a generic +;;; non-RO tyvar which has the same or more general context. + +;;; Aside from this, this is standard unification except that context +;;; propagation is needed when a tyvar with a non-empty context is +;;; instantiated. + +;;; If type2 is a tyvar and type1 is not they are switched. + +(define (unify-1 type1 type2) + (cond ((eq? type1 type2) ;; this catches variable to variable unify + 'OK) + ((ntyvar? type1) + (cond ((occurs-in-type type1 type2) + (type-error "Circular type: cannot unify ~A with ~A" + type1 type2)) + ((ntyvar-read-only? type1) + (cond ((or (not (ntyvar? type2)) (ntyvar-read-only? type2)) + (type-error + "Signature too general: cannot unify ~A with ~A" + type1 type2)) + (else + (unify-1 type2 type1)))) + ((and (ntyvar? type2) + (ntyvar-read-only? type2) + (non-generic? type1)) + (type-error + "Type signature cannot be used: monomorphic type variables present.")) + (else + (instantiate-tyvar type1 type2) + (let ((classes (ntyvar-context type1))) + (if (null? classes) + 'OK + (propagate-contexts/ntype type1 type2 classes)))))) + ((ntyvar? type2) + (unify-1 type2 type1)) + ((eq? (ntycon-tycon type1) (ntycon-tycon type2)) + (unify-list (ntycon-args type1) (ntycon-args type2))) + (else + (let ((etype1 (expand-ntype-synonym type1)) + (etype2 (expand-ntype-synonym type2))) + (if (same-tycon? (ntycon-tycon etype1) (ntycon-tycon etype2)) + (unify-list (ntycon-args etype1) (ntycon-args etype2)) + ;; This error message should probably show both the original + ;; and the expanded types for clarity. + (type-error + "Type conflict: type ~A does not match ~A" + etype1 etype2)))))) + + +(define-integrable (instantiate-tyvar tyvar val) + (setf (ntyvar-value tyvar) val)) + +;;; This is needed since interface files may leave multiple def's +;;; for the same tycon sitting around. + +(define (same-tycon? ty1 ty2) + (or (eq? ty1 ty2) + (and (eq? (def-name ty1) (def-name ty2)) + (eq? (def-module ty1) (def-module ty2))))) + + +;;; unifies two lists of types pairwise. Used for tycon args. + +(define (unify-list args1 args2) + (if (null? args1) + 'OK + (begin (unify-list (cdr args1) (cdr args2)) + (unify (car args1) (car args2))))) + +;;; combines a list of types into a single type. Used in constructs +;;; such as [x,y,z] and case expressions. + +(define (unify-list/single-type types) + (when (not (null? types)) + (let ((type (car types))) + (dolist (type2 (cdr types)) + (unify type type2))))) + +;;; This propagates the context from a just instantiated tyvar to the +;;; instantiated value. If the value is a tycon, instances must be +;;; looked up. If the value is a tyvar, the context is added to that of +;;; other tyvar. + +;;; This is used to back out of the unification on errors. This is a +;;; poor mans trail stack! Without this, error messages get very +;;; obscure. + +(define *instantiated-tyvar* '()) + +(define (propagate-contexts/ntype tyvar type classes) + (dynamic-let ((*instantiated-tyvar* tyvar)) + (propagate-contexts/inner type classes))) + +(define (propagate-contexts/inner type classes) + (let ((type (prune type))) + (if (ntyvar? type) + (if (ntyvar-read-only? type) + (if (context-implies? (ntyvar-context type) classes) + 'OK ; no need for context propagation here + (begin + (setf (ntyvar-value (dynamic *instantiated-tyvar*)) '#f) + (type-error "Signature context is too general"))) + (if (null? (ntyvar-context type)) + (setf (ntyvar-context type) classes) + (setf (ntyvar-context type) + (merge-contexts classes (ntyvar-context type))))) + (propagate-contexts-1 (expand-ntype-synonym type) classes)))) + +;;; The type has now been expanded. This propagates each class constraint +;;; in turn. + +(define (propagate-contexts-1 type classes) + (dolist (class classes) + (propagate-single-class type class))) + +;;; Now we have a single class & data type. Either an instance decl can +;;; be found or a type error should be signalled. Once the instance +;;; decl is found, contexts are propagated to the component types. + +(define (propagate-single-class type class) + (let ((instance (lookup-instance (ntycon-tycon type) class))) + (cond ((eq? instance '#f) + ;; This remove the instantiation which caused the type + ;; error - perhaps stop error propagation & make + ;; error message better. + (setf (ntyvar-value (dynamic *instantiated-tyvar*)) '#f) + (type-error "Type ~A is not in class ~A" type class)) + (else + ;; The instance contains a list of class constraints for + ;; each argument. This loop pairs the argument to the + ;; type constructor with the context required by the instance + ;; decl. + (dolist2 (classes (instance-gcontext instance)) + (arg (ntycon-args type)) + (propagate-contexts/inner arg classes))))) + 'OK) + +;;; The routines which handle contexts (merge-contexts and context-implies?) +;;; are in type-utils. The occurs check is also there. + diff --git a/util/README b/util/README new file mode 100644 index 0000000..a39e4bc --- /dev/null +++ b/util/README @@ -0,0 +1,2 @@ +This directory contains random utilities that are used in various places +around the compiler. diff --git a/util/annotation-utils.scm b/util/annotation-utils.scm new file mode 100644 index 0000000..8e2baf2 --- /dev/null +++ b/util/annotation-utils.scm @@ -0,0 +1,41 @@ + +;;; Some general utilities for dealing with annotations + +;;; Lookup an annotation on a var + +(define (lookup-annotation var aname) + (lookup-annotation-1 (var-annotations var) aname)) + +(define (lookup-annotation-1 a aname) + (if (null? a) + '#f + (if (eq? aname (annotation-value-name (car a))) + (car a) + (lookup-annotation-1 (cdr a) aname)))) + +;;; This parses a string denoting a strictness property into a list +;;; of booleans. "S,N,S" -> (#t #f #t) + +(define (parse-strictness str) + (parse-strictness-1 str 0)) + +(define (parse-strictness-1 str i) + (if (>= i (string-length str)) + (signal-bad-strictness-annotation str) + (let* ((ch (char-downcase (string-ref str i))) + (s (cond ((char=? ch '#\s) + '#t) + ((char=? ch '#\n) + '#f) + (else + (signal-bad-strictness-annotation str))))) + (cond ((eqv? (1+ i) (string-length str)) + (list s)) + ((char=? (string-ref str (1+ i)) '#\,) + (cons s (parse-strictness-1 str (+ i 2)))) + (else + (signal-bad-strictness-annotation str)))))) + +(define (signal-bad-strictness-annotation str) + (fatal-error 'bad-strictness "Bad strictness annotation: ~A~%" str)) + diff --git a/util/constructors.scm b/util/constructors.scm new file mode 100644 index 0000000..2fa8bd3 --- /dev/null +++ b/util/constructors.scm @@ -0,0 +1,339 @@ +;;; This file contains ast construction functions. These +;;; functions are supplied for commonly used ast structures to +;;; avoid the longer `make' normally required. + +;;; Function names are the type names with a `**' prefix. For reference +;;; nodes, the /def for builds the node from a definition instead of a name. + +;;; Note: maybe these should be made automagicly someday. + +;;; from exp-structs: + +(define (**lambda args body) + (**lambda/pat (map (function **pat) args) body)) + +(define (**lambda/pat pats body) + (if (null? pats) + body + (make lambda (pats pats) (body body)))) + + + +;;; Make a case expression. + +(define (**case exp alts) + (make case (exp exp) (alts alts))) + +(define (**alt/simple pat exp) + (**alt pat + (list (make guarded-rhs + (guard (make omitted-guard)) + (rhs exp))) + '())) + +(define (**alt pat rhs-list where-decls) + (make alt (pat pat) (rhs-list rhs-list) (where-decls where-decls))) + + + + +(define (**let decls body) + (if decls + (make let (decls decls) (body body)) + body)) + +(define (**if test then-exp else-exp) + (make if (test-exp test) (then-exp then-exp) (else-exp else-exp))) + +(define (**app fn . args) ; any number of args + (**app/l fn args)) + +(define (**app/l fn args) ; second args is a list + (if (null? args) + fn + (**app/l (make app (fn fn) (arg (car args))) + (cdr args)))) + +(define (**var name) + (make var-ref (name name) (var (dynamic *undefined-def*)) (infix? '#f))) + +(define (**var/def def) ; arg is an entry + (make var-ref (var def) (name (def-name def)) (infix? '#f))) + +(define (**con/def def) + (make con-ref (name (def-name def)) (con def) (infix? '#f))) + +(define (**int x) + (make integer-const (value x))) + +(define (**char x) + (make char-const (value x))) + +(define (**string x) + (make string-const (value x))) + +(define (**listcomp exp quals) + (make list-comp (exp exp) (quals quals))) + +(define (**gen pat exp) + (make qual-generator (pat (**pat pat)) (exp exp))) + +(define (**omitted-guard) + (make omitted-guard)) + +(define (**con-number exp algdata) + (make con-number (type algdata) (value exp))) + +(define (**sel con exp i) + (make sel (constructor con) (value exp) (slot i))) + +(define (**is-constructor exp con) + (make is-constructor (value exp) (constructor con))) + +;;; From valdef-structs + +(define (**signdecl vars type) + (make signdecl (vars (map (function **var) vars)) (signature type))) + +(define (**signdecl/def vars type) + (make signdecl (vars (map (function **var/def) vars)) (signature type))) + +(define (**define name args val) + (**valdef (**pat name) (map (function **pat) args) val)) + +(define (**valdef/def var exp) + (**valdef/pat (**var-pat/def var) exp)) + +(define (**valdef/pat pat exp) + (**valdef pat '() exp)) + +(define (**valdef lhs args rhs) + (make valdef + (lhs lhs) + (definitions + (list (make single-fun-def + (args args) + (rhs-list + (list (make guarded-rhs + (guard (**omitted-guard)) + (rhs rhs)))) + (where-decls '()) + (infix? '#f)))))) + + +;;; Patterns (still in valdef-structs) + +;;; The **pat function converts a very simple lisp-style pattern representation +;;; into corresponding ast structure. The conversion: +;;; a) _ => wildcard +;;; b) a symbol => Var pattern +;;; c) an integer / string => const pattern +;;; d) a list of pats starting with 'tuple => Pcon +;;; e) a list of pats starting with a con definition => Pcon + +(define (**pat v) + (cond ((eq? v '_) (**wildcard-pat)) + ((symbol? v) + (make var-pat (var (**var v)))) + ((var? v) + (make var-pat (var (**var/def v)))) + ((integer? v) + (make const-pat (value (**int v)))) + ((string? v) + (make const-pat (value (**string v)))) + ((and (pair? v) (eq? (car v) 'tuple)) + (**pcon/tuple (map (function **pat) (cdr v)))) + ((and (pair? v) (con? (car v))) + (**pcon/def (car v) (map (function **pat) (cdr v)))) + (else + (error "Bad pattern in **pat: ~A~%" v)))) + +(define (**pcon name pats) + (make pcon (name (add-con-prefix/symbol name)) + (con (dynamic *undefined-def*)) (pats pats) (infix? '#f))) + +(define (**pcon/def def pats) + (make pcon (name (def-name def)) (con def) (pats pats) (infix? '#f))) + +(define (**pcon/tuple pats) + (**pcon/def (tuple-constructor (length pats)) pats)) + +;;; Make a variable pattern from the var + +(define (**var-pat/def var) + (make var-pat + (var (**var/def var)))) + +(define (**wildcard-pat) + (make wildcard-pat)) + + +;;; Either make a tuple, or return the single element of a list. + +(define (**tuple-pat pats) + (cond ((null? pats) + (**pcon/def (core-symbol "UnitConstructor") '())) + ((null? (cdr pats)) + (car pats)) + (else + (**pcon/tuple pats)))) + + +;;; From type-structs.scm + +(define (**tycon name args) + (make tycon (name name) (args args) (def (dynamic *undefined-def*)))) + +(define (**tycon/def def args) + (make tycon (name (def-name def)) (def def) (args args))) + +(define (**tyvar name) + (make tyvar (name name))) + +(define (**signature context type) + (make signature (context context) (type type))) + +(define (**class/def def) + (make class-ref (name (def-name def)) (class def))) + +(define (**context tycls tyvar) + (make context (class tycls) (tyvar tyvar))) + +;;; From tc-structs + +(define (**ntyvar) + (make ntyvar (value '#f) (context '()) (dict-params '()))) + +(define (**ntycon tycon args) + (make ntycon (tycon tycon) (args args))) + +(define (**arrow . args) + (**arrow/l args)) + +(define (**arrow/l args) + (if (null? (cdr args)) + (car args) + (**ntycon (core-symbol "Arrow") + (list (car args) (**arrow/l (cdr args)))))) + +(define (**arrow/l-2 args final-val) + (if (null? args) + final-val + (**ntycon (core-symbol "Arrow") + (list (car args) (**arrow/l-2 (cdr args) final-val))))) + +(define (**list-of arg) + (**ntycon (core-symbol "List") (list arg))) + +(define (**recursive-placeholder var edecls) + (make recursive-placeholder (var var) (exp '#f) + (enclosing-decls edecls))) + +(define (**dict-placeholder class tyvar edecls var) + (make dict-placeholder + (class class) (exp '#f) (overloaded-var var) + (tyvar tyvar) (enclosing-decls edecls))) + +(define (**method-placeholder method tyvar edecls var) + (make method-placeholder + (method method) (exp '#f) (overloaded-var var) + (tyvar tyvar) (enclosing-decls edecls))) + +;;; Some less primitive stuff + +(define (**tuple-sel n i exp) ;; 0 <= i < n + (if (eqv? n 1) + exp + (**sel (tuple-constructor n) exp i))) + +(define (**abort msg) + (**app (**var/def (core-symbol "error")) + (**string msg))) + +(define (**tuple/l args) + (cond ((null? args) + (**con/def (core-symbol "UnitConstructor"))) + ((null? (cdr args)) + (car args)) + (else + (**app/l (**con/def (tuple-constructor (length args))) + args)))) + +(define (**tuple . args) + (**tuple/l args)) + +(define (**tuple-type/l args) + (cond ((null? args) + (**tycon/def (core-symbol "UnitConstructor") '())) + ((null? (cdr args)) + (car args)) + (else + (**tycon/def (tuple-tycon (length args)) args)))) + +(define (**tuple-type . args) + (**tuple-type/l args)) + +(define (**arrow-type . args) + (**arrow-type/l args)) + +(define (**arrow-type/l args) + (if (null? (cdr args)) + (car args) + (**tycon/def (core-symbol "Arrow") (list (car args) + (**arrow-type/l (cdr args)))))) + +(define (**fromInteger x) + (**app (**var/def (core-symbol "fromInteger")) x)) + +(define (**fromRational x) + (**app (**var/def (core-symbol "fromRational")) x)) + +(define (**gtyvar n) + (make gtyvar (varnum n))) + +(define (**gtype context type) + (make gtype (context context) (type type))) + +(define (**fixity a p) + (make fixity (associativity a) (precedence p))) + +(define (**ntycon/tuple . args) + (let ((arity (length args))) + (**ntycon (tuple-tycon arity) args))) + +(define (**ntycon/arrow . args) + (**ntycon/arrow-l args)) + +(define (**ntycon/arrow-l args) + (let ((arg (if (integer? (car args)) + (**gtyvar (car args)) + (car args)))) + (if (null? (cdr args)) + arg + (**arrow arg (**ntycon/arrow-l (cdr args)))))) + +(define (**save-old-exp old new) + (make save-old-exp (old-exp old) (new-exp new))) + + + +;;; These are used by the CFN. + +(define (**case-block block-name exps) + (make case-block + (block-name block-name) + (exps exps))) + +(define (**return-from block-name exp) + (make return-from + (block-name block-name) + (exp exp))) + +(define (**and-exp . exps) + (cond ((null? exps) + (**con/def (core-symbol "True"))) + ((null? (cdr exps)) + (car exps)) + (else + (make and-exp (exps exps))))) + diff --git a/util/haskell-utils.scm b/util/haskell-utils.scm new file mode 100644 index 0000000..c851cda --- /dev/null +++ b/util/haskell-utils.scm @@ -0,0 +1,22 @@ +(define-compilation-unit haskell-utils + (source-filename "$Y2/util/") + (require global) + (unit constructors + (source-filename "constructors.scm")) + (unit prec-utils + (source-filename "prec-utils.scm")) + (unit walk-ast + (source-filename "walk-ast.scm")) + (unit pattern-vars + (source-filename "pattern-vars.scm") + (require walk-ast)) + (unit instance-manager + (source-filename "instance-manager.scm")) + (unit signature + (source-filename "signature.scm")) + (unit type-utils + (source-filename "type-utils.scm")) + (unit annotation-utils + (source-filename "annotation-utils.scm")) + ) + diff --git a/util/instance-manager.scm b/util/instance-manager.scm new file mode 100644 index 0000000..231e27d --- /dev/null +++ b/util/instance-manager.scm @@ -0,0 +1,161 @@ + +;;; This file has some random utilities dealing with instances. + +;;; Right now, this is a linear search off the class. + +(define (lookup-instance alg-def class-def) + (let ((res (lookup-instance-1 alg-def (class-instances class-def)))) + (if (and (eq? res '#f) (algdata-real-tuple? alg-def)) + (lookup-possible-tuple-instances alg-def class-def) + res))) + +(define (lookup-instance-1 alg-def instances) + (cond ((null? instances) + '#f) + ((eq? (instance-algdata (car instances)) alg-def) + (if (instance-ok? (car instances)) + (car instances) + '#f)) + (else + (lookup-instance-1 alg-def (cdr instances))))) + +(define (lookup-possible-tuple-instances alg-def class-def) + (cond ((eq? class-def (core-symbol "Eq")) + (get-tuple-eq-instance alg-def)) + ((eq? class-def (core-symbol "Ord")) + (get-tuple-ord-instance alg-def)) + ((eq? class-def (core-symbol "Ix")) + (get-tuple-ix-instance alg-def)) + ((eq? class-def (core-symbol "Text")) + (get-tuple-text-instance alg-def)) + ((eq? class-def (core-symbol "Binary")) + (get-tuple-binary-instance alg-def)) + (else '#f))) + +(define *saved-eq-instances* '()) +(define *saved-ord-instances* '()) +(define *saved-ix-instances* '()) +(define *saved-text-instances* '()) +(define *saved-binary-instances* '()) + +(define (get-tuple-eq-instance tpl) + (let ((res (assq tpl *saved-eq-instances*))) + (if (not (eq? res '#f)) + (tuple-2-2 res) + (let ((inst (make-tuple-instance + tpl (core-symbol "Eq") (core-symbol "tupleEqDict")))) + (push (tuple tpl inst) *saved-eq-instances*) + inst)))) + +(define (get-tuple-ord-instance tpl) + (let ((res (assq tpl *saved-ord-instances*))) + (if (not (eq? res '#f)) + (tuple-2-2 res) + (let ((inst (make-tuple-instance + tpl (core-symbol "Ord") (core-symbol "tupleOrdDict")))) + (push (tuple tpl inst) *saved-ord-instances*) + inst)))) + +(define (get-tuple-ix-instance tpl) + (let ((res (assq tpl *saved-ix-instances*))) + (if (not (eq? res '#f)) + (tuple-2-2 res) + (let ((inst (make-tuple-instance + tpl (core-symbol "Ix") (core-symbol "tupleIxDict")))) + (push (tuple tpl inst) *saved-ix-instances*) + inst)))) + +(define (get-tuple-text-instance tpl) + (let ((res (assq tpl *saved-text-instances*))) + (if (not (eq? res '#f)) + (tuple-2-2 res) + (let ((inst (make-tuple-instance + tpl (core-symbol "Text") (core-symbol "tupleTextDict")))) + (push (tuple tpl inst) *saved-text-instances*) + inst)))) + +(define (get-tuple-binary-instance tpl) + (let ((res (assq tpl *saved-binary-instances*))) + (if (not (eq? res '#f)) + (tuple-2-2 res) + (let ((inst (make-tuple-instance + tpl (core-symbol "Binary") + (core-symbol "tupleBinaryDict")))) + (push (tuple tpl inst) *saved-binary-instances*) + inst)))) + +(define (make-tuple-instance algdata class dict) + (let* ((size (tuple-size algdata)) + (tyvars (gen-symbols size)) + (context (map (lambda (tyvar) + (**context (**class/def class) tyvar)) + tyvars)) + (sig (**tycon/def algdata (map (lambda (x) (**tyvar x)) tyvars))) + (gcontext (gtype-context (ast->gtype context sig)))) + (make instance + (algdata algdata) + (tyvars tyvars) + (class class) + (context context) + (gcontext gcontext) + (methods '()) + (dictionary dict) + (ok? '#t) + (special? '#t)))) + +;;; I know these are somewhere else too ... + +(define (tuple-size alg) + (con-arity (car (algdata-constrs alg)))) + +(define (gen-symbols n) + (gen-symbols-1 n '(|a| |b| |c| |d| |e| |f| |g| |h| |i| |j| |k| |l| |m| + |n| |o| |p| |q| |r| |s| |t| |u| |v| |w| |x| |y| |z|))) + +(define (gen-symbols-1 n vars) + (if (eqv? n 0) + '() + (if (null? vars) + (cons (string->symbol (format '#f "x~A" n)) + (gen-symbols-1 (1- n) '())) + (cons (car vars) (gen-symbols-1 (1- n) (cdr vars)))))) + +;;; This handles the dynamic linking of instances into classes + +(define (link-instances modules) + (dolist (m modules) + ;; clear out any instances sitting around from old compiles + (dolist (class (module-class-defs m)) + (setf (class-instances class) '()))) + (dolist (m modules) + (dolist (inst (module-instance-defs m)) + (link-instance inst))) + ) + +(define (link-instance inst) ; links an instance into the associated class + (push inst (class-instances (instance-class inst)))) + +;;; This creates a new instance object and installs it. + +(predefine (make-new-var name)) ; in tdecl/tdecl-utils.scm + +(define (new-instance class algdata tyvars) + (let* ((dict-name + (string-append "dict-" + (symbol->string (print-name class)) "-" + (symbol->string (print-name algdata)))) + (inst (make instance (algdata algdata) + (tyvars tyvars) + (class class) + (gcontext '()) + (context '()) + (dictionary (make-new-var dict-name))))) + (link-instance inst) + inst)) + + + + + + + diff --git a/util/pattern-vars.scm b/util/pattern-vars.scm new file mode 100644 index 0000000..78cb361 --- /dev/null +++ b/util/pattern-vars.scm @@ -0,0 +1,40 @@ +;;; This collects the vars bound in a pattern. + +(define-walker collect-pattern-vars ast-td-collect-pattern-vars-walker) + +(define (collect-pattern-vars x) + (collect-pattern-vars-1 x '())) + +(define (collect-pattern-vars-1 x vars-so-far) + (call-walker collect-pattern-vars x vars-so-far)) + +(define (collect-pattern-vars/list l vars-so-far) + (if (null? l) + vars-so-far + (collect-pattern-vars/list (cdr l) + (collect-pattern-vars-1 (car l) vars-so-far)))) + +(define-local-syntax (collect-pattern-vars-processor + slot type object-form accum-form) + (let ((stype (sd-type slot)) + (sname (sd-name slot))) + (cond ((eq? stype 'var-ref) + `(cons (struct-slot ',type ',sname ,object-form) ,accum-form)) + ((eq? stype 'pattern) + `(collect-pattern-vars-1 + (struct-slot ',type ',sname ,object-form) + ,accum-form)) + ((equal? stype '(list pattern)) + `(collect-pattern-vars/list + (struct-slot ',type ',sname ,object-form) ,accum-form)) + (else +; (format '#t "Collect-pattern-vars: skipping slot ~A in ~A~%" +; sname +; type) + accum-form) + ))) + +(define-collecting-walker-methods collect-pattern-vars + (as-pat irr-pat var-pat wildcard-pat const-pat plus-pat pcon list-pat + pp-pat-list pp-pat-plus pp-pat-negated) + collect-pattern-vars-processor) diff --git a/util/prec-utils.scm b/util/prec-utils.scm new file mode 100644 index 0000000..6ff7a1a --- /dev/null +++ b/util/prec-utils.scm @@ -0,0 +1,115 @@ +;;; prec-util.scm -- utilities for precedence parsing and printing of +;;; expressions +;;; +;;; author : Sandra Loosemore +;;; date : 15 Feb 1992 +;;; +;;; The functions in this file are used by the expression printers +;;; and by precedence parsing. + + +;;; Uncurry the function application, looking for a con-ref as the +;;; actual function being applied. Return the con-ref-con and a list +;;; of the arguments. + +(define (extract-constructor fn args) + (cond ((is-type? 'con-ref fn) + (values (con-ref-con fn) args)) + ((is-type? 'app fn) + (extract-constructor (app-fn fn) (cons (app-arg fn) args))) + (else + (values '#f '())))) + + +;;; If this is an infix operator application, there are really two nested +;;; applications that we handle at once. The "fn" on the outer app +;;; points to a nested app which is a var-ref or con-ref with the infix? +;;; slot set to T. +;;; Returns three values: the fixity info, the operator, and the first +;;; argument (the arg to the outer application is the second argument). + +(define (extract-infix-operator fn) + (if (is-type? 'app fn) + (let* ((new-fn (app-fn fn)) + (arg (app-arg fn)) + (fixity (operator-fixity new-fn))) + (if fixity + (values fixity new-fn arg) + (values '#f '#f '#f))) + (values '#f '#f '#f))) + + +;;; Return the fixity info for a reference to a var or con. +;;; If it doesn't have an explicit fixity, use the default of +;;; left associativity and precedence 9. + +(define default-fixity + (make fixity (associativity 'l) (precedence 9))) + +(define (operator-fixity fn) + (if (is-type? 'save-old-exp fn) + (operator-fixity (save-old-exp-old-exp fn)) + (or (and (is-type? 'var-ref fn) + (var-ref-infix? fn) + (or (and (var-ref-var fn) + (not (eq? (var-ref-var fn) *undefined-def*)) + (var-fixity (var-ref-var fn))) + default-fixity)) + (and (is-type? 'con-ref fn) + (con-ref-infix? fn) + (or (and (con-ref-con fn) + (not (eq? (con-ref-con fn) *undefined-def*)) + (con-fixity (con-ref-con fn))) + default-fixity)) + (and (is-type? 'pcon fn) + (pcon-infix? fn) + (or (and (pcon-con fn) + (not (eq? (pcon-con fn) *undefined-def*)) + (con-fixity (pcon-con fn))) + default-fixity)) + '#f))) + + + +;;; Determine the precedence of an expression. +;;; *** What about unary -? + +(define (precedence-of-exp exp associativity) + (cond ((is-type? 'save-old-exp exp) + (precedence-of-exp (save-old-exp-old-exp exp) associativity)) + ((is-type? 'aexp exp) 10) + ((is-type? 'app exp) + (multiple-value-bind (fixity op arg1) + (extract-infix-operator (app-fn exp)) + (declare (ignore op arg1)) + (if fixity + (if (eq? associativity (fixity-associativity fixity)) + (1+ (fixity-precedence fixity)) + (fixity-precedence fixity)) + 10))) + ((is-type? 'lambda exp) 10) + ((is-type? 'let exp) 10) + ((is-type? 'if exp) 10) + ((is-type? 'case exp) 10) + ((pp-exp-list-section? exp) 10) + ((is-type? 'negate exp) 10) ; hack, hack + (else + 0))) + + +;;; Determine whether a pp-exp-list is really a section -- the +;;; first or last exp in the list is really an infix op. + +(define (pp-exp-list-section? object) + (if (is-type? 'pp-exp-list object) + (let ((exps (pp-exp-list-exps object))) + (or (infix-var-or-con? (car exps)) + (infix-var-or-con? (list-ref exps (1- (length exps)))))) + '#f)) + +(define (infix-var-or-con? object) + (or (and (is-type? 'var-ref object) + (var-ref-infix? object)) + (and (is-type? 'con-ref object) + (con-ref-infix? object)))) + diff --git a/util/signature.scm b/util/signature.scm new file mode 100644 index 0000000..aea41eb --- /dev/null +++ b/util/signature.scm @@ -0,0 +1,90 @@ +;;; This file handles the scoping and error checking of signatures. + +;;; Possible errors: +;;; Wrong arity in a tycon +;;; Ambiguous context + +;;; Other errors may be present; these are detected at a higher level. +;;; The list of variables used in the signature is returned. + +(define (resolve-signature signature) + (with-slots signature (context type) signature + (let ((tyvars (resolve-type type))) + (resolve-signature-aux tyvars context) + tyvars))) + +(define (resolve-signature-aux tyvars context) + (dolist (ctxt context) + (with-slots context (class tyvar) ctxt + (when (not (memq tyvar tyvars)) + (signal-ambiguous-context tyvar)) + (resolve-class class)))) + +(define (resolve-type type) + (resolve-type-1 type '())) + +(define (resolve-type-1 type vars) + (cond ((tyvar? type) + (cons (tyvar-name type) vars)) + (else + (resolve-tycon type) + (with-slots tycon (name def args) type + (when (not (eq? def *undefined-def*)) + (if (eqv? (tycon-def-arity def) -1) + (setf (tycon-def-arity def) (length args)) + (when (not (eqv? (length args) (tycon-def-arity def))) + (signal-tycon-arity name type)))) + (resolve-type/list args vars))))) + +(define (resolve-type/list args vars) + (if (null? args) + vars + (resolve-type/list (cdr args) (resolve-type-1 (car args) vars)))) + +;;; This returns the names of the tyvars in a simple tycon + +(define (simple-tyvar-list simple) + (remember-context simple + (let* ((res (map (lambda (x) (tyvar-name x)) (tycon-args simple))) + (dups (find-duplicates res))) + (when (not (null? dups)) + (signal-non-linear-type-vars simple)) + res))) + +;;; This is used to build the class dictionary signature. + +(define (substitute-tyvar type tyvar new) + (cond ((tyvar? type) + (if (eq? (tyvar-name type) tyvar) + new + (**tyvar (tyvar-name type)))) + ((tycon? type) + (with-slots tycon (name def args) type + (make tycon (name name) (def def) + (args (map (lambda (x) (substitute-tyvar x tyvar new)) + args))))) + (else + (**signature (signature-context type) + (substitute-tyvar (signature-type type) tyvar new))))) + + + +;;; Error signalling routines + +(define (signal-ambiguous-context tyvar) + (phase-error 'ambiguous-context + "~a is referenced in a context, but is not bound as a type variable." + tyvar)) + +(define (signal-tycon-arity name type) + (phase-error 'tycon-arity + "The wrong number of arguments are supplied to the constructor ~a~%~ + in the type ~a." + name type)) + + +(define (signal-non-linear-type-vars simple) + (phase-error 'non-linear-type-vars + "There are duplicate type variables in ~s." + simple)) + diff --git a/util/type-utils.scm b/util/type-utils.scm new file mode 100644 index 0000000..c9b4504 --- /dev/null +++ b/util/type-utils.scm @@ -0,0 +1,308 @@ + +;;; The `prune' function removes instantiated type variables at the +;;; top level of a type. + +;;; It returns an uninstantiated type variable or a type constructor. + +(define-integrable (prune ntype) + (if (ntyvar? ntype) + (if (instantiated? ntype) + (prune-1 (ntyvar-value ntype)) + ntype) + ntype)) + +;;; This is because lucid can't hack inlining recursive fns. + +(define (prune-1 x) (prune x)) + +(define-integrable (instantiated? ntyvar) + (ntyvar-value ntyvar)) +; (not (eq? (ntyvar-value ntyvar) '#f))) ;*** Lucid compiler bug? + +(define (prune/l l) + (map (function prune) l)) + + +;;; These functions convert between AST types and gtypes. Care is taken to +;;; ensure that the gtyvars are in the same order that they appear in the +;;; context. This is needed to make dictionary conversion work right. + +(define (ast->gtype context type) + (mlet (((gcontext env) (context->gcontext context '() '())) + ((type env1) (type->gtype type env)) + (gcontext-classes (arrange-gtype-classes env1 gcontext))) + (**gtype gcontext-classes type))) + +;;; This is similar except that the ordering of the tyvars is as defined in +;;; the data type. This is used only for instance declarations and allows +;;; for simple context implication checks. It also used by the signature +;;; of the dictionary variable. + +(define (ast->gtype/inst context type) + (mlet (((type env) (type->gtype type '())) + ((gcontext env1) (context->gcontext context '() env)) + (gcontext-classes (arrange-gtype-classes env1 gcontext))) + (**gtype gcontext-classes type))) + +;;; This converts a context into gtype form [[class]]: a list of classes +;;; for each gtyvar. This returns the context and the gtyvar environment. + +(define (context->gcontext context gcontext env) + (if (null? context) + (values gcontext env) + (mlet ((sym (context-tyvar (car context))) + (class (class-ref-class (context-class (car context)))) + ((n new-env) (ast->gtyvar sym env)) + (old-context (get-gtyvar-context n gcontext)) + (new-context (merge-single-class class old-context)) + (new-gcontext (cons (tuple n new-context) gcontext))) + (context->gcontext (cdr context) new-gcontext new-env)))) + +;;; This assigns a gtyvar number to a tyvar name. + +(define (ast->gtyvar sym env) + (let ((res (assq sym env))) + (if (eq? res '#f) + (let ((n (length env))) + (values n (cons (tuple sym n) env))) + (values (tuple-2-2 res) env)))) + +(define (get-gtyvar-context n gcontext) + (cond ((null? gcontext) + '()) + ((eqv? n (tuple-2-1 (car gcontext))) + (tuple-2-2 (car gcontext))) + (else (get-gtyvar-context n (cdr gcontext))))) + +(define (type->gtype type env) + (if (tyvar? type) + (mlet (((n env1) (ast->gtyvar (tyvar-name type) env))) + (values (**gtyvar n) env1)) + (mlet (((types env1) (type->gtype/l (tycon-args type) env))) + (values (**ntycon (tycon-def type) types) env1)))) + +(define (type->gtype/l types env) + (if (null? types) + (values '() env) + (mlet (((type env1) (type->gtype (car types) env)) + ((other-types env2) (type->gtype/l (cdr types) env1))) + (values (cons type other-types) env2)))) + +(define (arrange-gtype-classes env gcontext) + (arrange-gtype-classes-1 0 (length env) env gcontext)) + +(define (arrange-gtype-classes-1 m n env gcontext) + (if (equal? m n) + '() + (cons (get-gtyvar-context m gcontext) + (arrange-gtype-classes-1 (1+ m) n env gcontext)))) + +;;; These routines convert gtypes back to ordinary types. + +(define (instantiate-gtype g) + (mlet (((gtype _) (instantiate-gtype/newvars g))) + gtype)) + +(define (instantiate-gtype/newvars g) + (if (null? (gtype-context g)) + (values (gtype-type g) '()) + (let ((new-tyvars (create-new-tyvars (gtype-context g)))) + (values (copy-gtype (gtype-type g) new-tyvars) new-tyvars)))) + +(define (create-new-tyvars ctxts) + (if (null? ctxts) + '() + (let ((tyvar (**ntyvar))) + (setf (ntyvar-context tyvar) (car ctxts)) + (cons tyvar (create-new-tyvars (cdr ctxts)))))) + +(define (copy-gtype g env) + (cond ((ntycon? g) + (**ntycon (ntycon-tycon g) + (map (lambda (g1) (copy-gtype g1 env)) + (ntycon-args g)))) + ((ntyvar? g) + g) + ((gtyvar? g) + (list-ref env (gtyvar-varnum g))) + ((const-type? g) + (const-type-type g)))) + +;;; ntypes may contain synonyms. These are expanded here. Only the +;;; top level synonym is expanded. + +(define (expand-ntype-synonym type) + (if (and (ntycon? type) + (synonym? (ntycon-tycon type))) + (let ((syn (ntycon-tycon type))) + (expand-ntype-synonym + (expand-ntype-synonym-1 (synonym-body syn) + (map (lambda (var val) + (tuple var val)) + (synonym-args syn) + (ntycon-args type))))) + type)) + +(define (expand-ntype-synonym-1 type env) + (if (tyvar? type) + (tuple-2-2 (assq (tyvar-name type) env)) + (**ntycon (tycon-def type) + (map (lambda (ty) (expand-ntype-synonym-1 ty env)) + (tycon-args type))))) + +;;; This is used in generalization. Note that ntyvars will remain when +;;; non-generic tyvars are encountered. + +(define (ntype->gtype ntype) + (mlet (((res _) (ntype->gtype/env ntype '()))) + res)) + +(define (ntype->gtype/env ntype required-vars) + (mlet (((gtype env) (ntype->gtype-1 ntype required-vars))) + (values + (make gtype (type gtype) (context (map (lambda (x) (ntyvar-context x)) + env))) + env))) + +(define (ntype->gtype-1 ntype env) + (let ((ntype (prune ntype))) + (cond ((ntycon? ntype) + (mlet (((args env1) (ntype->gtype/l (ntycon-args ntype) env))) + (values (**ntycon (ntycon-tycon ntype) args) env1))) + (else + (ntyvar->gtyvar ntype env))))) + +(define (ntype->gtype/l types env) + (if (null? types) + (values '() env) + (mlet (((type env1) (ntype->gtype-1 (car types) env)) + ((types2 env2) (ntype->gtype/l (cdr types) env1))) + (values (cons type types2) env2)))) + +(define (ntyvar->gtyvar ntyvar env) + (if (non-generic? ntyvar) + (values ntyvar env) + (let ((l (list-pos ntyvar env))) + (if (eq? l '#f) + (values (**gtyvar (length env)) (append env (list ntyvar))) + (values (**gtyvar l) env))))) + +(define (list-pos x l) + (list-pos-1 x l 0)) + +(define (list-pos-1 x l n) + (cond ((null? l) + '#f) + ((eq? x (car l)) + n) + (else + (list-pos-1 x (cdr l) (1+ n))))) + + +;;; These utils are used in dictionary conversion. + +(define (**dsel/method class method dict-code) + (let ((pos (locate-in-list method (class-method-vars class) 0))) + (**tuple-sel (class-dict-size class) pos dict-code))) + +(define (**dsel/dict class dict-class dict-code) + (let ((pos (locate-in-list + dict-class (class-super* class) (class-n-methods class)))) + (**tuple-sel (class-dict-size class) pos dict-code))) + +(define (locate-in-list var l pos) + (if (null? l) + (error "Locate in list failed") + (if (eq? var (car l)) + pos + (locate-in-list var (cdr l) (1+ pos))))) + +;;; These routines deal with contexts. A context is a list classes. + +;;; A context is normalized whenever class is a superclass of another. + +(define (merge-contexts ctxt1 ctxt2) + (if (null? ctxt1) + ctxt2 + (merge-single-class (car ctxt1) (merge-contexts (cdr ctxt1) ctxt2)))) + +;;; This could perhaps avoid some consing but I don't imagine it would +;;; make much difference. + +(define (merge-single-class class ctxt) + (cond ((null? ctxt) + (list class)) + ((eq? class (car ctxt)) + ctxt) + ((memq class (class-super* (car ctxt))) + ctxt) + ((memq (car ctxt) (class-super* class)) + (merge-single-class class (cdr ctxt))) + (else + (cons (car ctxt) (merge-single-class class (cdr ctxt)))))) + +;;; This determines if ctxt2 is contained in ctxt1. + +(define (context-implies? ctxt1 ctxt2) + (or (null? ctxt2) + (and (single-class-implies? ctxt1 (car ctxt2)) + (context-implies? ctxt1 (cdr ctxt2))))) + +(define (single-class-implies? ctxt class) + (and (not (null? ctxt)) + (or (memq class ctxt) + (super-class-implies? ctxt class)))) + +(define (super-class-implies? ctxt class) + (and (not (null? ctxt)) + (or (memq class (class-super* (car ctxt))) + (super-class-implies? (cdr ctxt) class)))) + +;;; This looks at the context of a full signature. + +(define (full-context-implies? ctxt1 ctxt2) + (or (null? ctxt1) + (and (context-implies? (car ctxt1) (car ctxt2)) + (full-context-implies? (cdr ctxt1) (cdr ctxt2))))) + +;;; This is used to avoid type circularity on unification. + +(define (occurs-in-type tyvar type) ; Cardelli algorithm + (let ((type (prune type))) + (if (ntyvar? type) + (eq? type tyvar) + (occurs-in-type/l tyvar (ntycon-args type))))) + +; Does a tyvar occur in a list of types? +(define (occurs-in-type/l tyvar types) + (if (null? types) + '#f + (or (occurs-in-type tyvar (car types)) + (occurs-in-type/l tyvar (cdr types))))) + +(define-integrable (non-generic? tyvar) + (occurs-in-type/l tyvar (dynamic *non-generic-tyvars*))) + +(define (collect-tyvars ntype) + (collect-tyvars-1 ntype '())) + +(define (collect-tyvars-1 ntype vars) + (let ((ntype (prune ntype))) + (if (ntyvar? ntype) + (if (or (memq ntype vars) (non-generic? ntype)) + vars + (cons ntype vars)) + (collect-tyvars/l-1 (ntycon-args ntype) vars)))) + +(define (collect-tyvars/l types) + (collect-tyvars/l-1 types '())) + +(define (collect-tyvars/l-1 types vars) + (if (null? types) + vars + (collect-tyvars/l-1 (cdr types) (collect-tyvars-1 (car types) vars)))) + +;;; Random utilities + +(define (decl-var decl) + (var-ref-var (var-pat-var (valdef-lhs decl)))) diff --git a/util/walk-ast.scm b/util/walk-ast.scm new file mode 100644 index 0000000..aecffc6 --- /dev/null +++ b/util/walk-ast.scm @@ -0,0 +1,156 @@ +;;; walk-ast.scm -- general-purpose walkers for AST structures. +;;; +;;; author : Sandra & John +;;; date : 30 Jan 1992 +;;; +;;; + +;;;===================================================================== +;;; Basic support, macros +;;;===================================================================== + + +;;; Here is a macro for accessing the walker function for a particular +;;; type. +;;; The walk-type names the walker. +;;; If an accessor argument is provided, it must name a SETF'able function +;;; or macro that takes a type descriptor as an argument. This is used to +;;; do the lookup of the walker function for the given type. +;;; If no explicit accessor is provided, one will be created. It will +;;; use a hash table keyed off the type names to store the walker functions. +;;; In either case, the mapping between the walker name and accessor is +;;; stored in the hash table ast-walker-table. + +(define ast-walker-table (make-table)) + +(define-syntax (define-walker walk-type . maybe-accessor) + (let ((accessor-name (if (null? maybe-accessor) + (symbol-append walk-type '-walker) + (car maybe-accessor)))) + (setf (table-entry ast-walker-table walk-type) accessor-name) + `(begin + ,@(if (null? maybe-accessor) + (let ((accessor-table (symbol-append '* walk-type '-table*))) + `((define ,accessor-table (make-table)) + (define-syntax (,accessor-name td) + (list 'table-entry + ',accessor-table + (list 'td-name td))))) + '()) + (setf (table-entry ast-walker-table ',walk-type) + ',accessor-name) + ',walk-type))) + +(define-syntax (ast-walker walk-type td) + (let ((accessor (table-entry ast-walker-table walk-type))) + `(,accessor ,td))) + + +;;; This macro dispatches a walker on an object of type ast-node. + +(define-syntax (call-walker walk-type object . args) + (let ((temp (gensym "OBJ"))) + `(let ((,temp ,object)) + (funcall (or (ast-walker ,walk-type (struct-type-descriptor ,temp)) + (walker-not-found-error ',walk-type ,temp)) + ,temp + ,@args)) + )) + +(define (walker-not-found-error walk-type object) + (error "There is no ~a walker for structure ~A defined." + walk-type (td-name (struct-type-descriptor object)))) + + + +;;; Define an individual walker for a particular type. The body should +;;; return either the original object or a replacement for it. + +(define-syntax (define-walker-method walk-type type args . body) + (let ((function-name (symbol-append walk-type '- type))) + `(begin + (define (,function-name ,@args) ,@body) + (setf (ast-walker ,walk-type (lookup-type-descriptor ',type)) + (function ,function-name)) + ',function-name))) + + + +;;;===================================================================== +;;; Support for default walker methods +;;;===================================================================== + +;;; Two kinds of walkers are supported: a collecting walker, which +;;; walks over a tree collecting some sort of returned result while +;;; not changing the tree itself, and a rewriting walker which maps +;;; ast to ast. + +;;; The basic template for a collecting walk is: +;;; (define-walker-method walk-type type (object accum) +;;; (sf1 (sf2 object ... (sfn accum))) +;;; where sfi = slot function for the ith slot. +;;; +;;; The slot-processor should be the name of a macro that is called with four +;;; arguments: a slot descriptor, the object type name, a form +;;; representing the object being traversed, and a form representing the +;;; accumulated value. +;;; If the slot does not participate in the walk, this last argument should +;;; be returned unchanged as the expansion of the macro. + +(define-syntax (define-collecting-walker-methods walk-type types + slot-processor) + `(begin + ,@(map (lambda (type) + (make-collecting-walker-method walk-type type slot-processor)) + types))) + +(define (make-collecting-walker-method walk-type type slot-processor) + `(define-walker-method ,walk-type ,type (object accum) + object ; prevent possible unreferenced variable warning + ,(make-collecting-walker-method-body + 'accum + type + (td-slots (lookup-type-descriptor type)) + slot-processor))) + +(define (make-collecting-walker-method-body base type slots slot-processor) + (if (null? slots) + base + `(,slot-processor ,(car slots) ,type object + ,(make-collecting-walker-method-body + base type (cdr slots) slot-processor)))) + + + +;;; A rewriting walker traverses the ast modifying various subtrees. +;;; The basic template here is: +;;; (define-walker-method walker type (object . args) +;;; (setf (slot1 object) (walk (slot1 object))) +;;; (setf (slot2 object) (walk (slot2 object))) +;;; ... +;;; object) + +;;; The basic macro to generate default walkers is as above except +;;; that the slot-processor macro is called with only +;;; two arguments, the slot and object type. +;;; The `args' is the actual lambda-list for the methods, and bindings +;;; can be referenced inside the code returned by the macro. +;;; If a slot participates in the walk, the macro should return code +;;; to SETF the slot, as in the template above. Otherwise, the macro +;;; should just return #f. + +(define-syntax (define-modify-walker-methods walk-type types args + slot-processor) + `(begin + ,@(map (lambda (type) + (make-modify-walker-method walk-type type args + slot-processor)) + types))) + +(define (make-modify-walker-method walk-type type args slot-processor) + `(define-walker-method ,walk-type ,type ,args + ,@(cdr args) ; prevent possible unreferenced variable warnings + ,@(map (lambda (slot) + `(,slot-processor ,slot ,type)) + (td-slots (lookup-type-descriptor type))) + ,(car args))) -- cgit v1.2.3