304
.cvskeywords
|
@ -1,107 +1,151 @@
|
||||||
./library/vocabulary-style.factor:! $Id: vocabulary-style.factor,v 1.3 2004/07/22 23:48:49 spestov Exp $
|
./contrib/irc.factor:! $Id: irc.factor,v 1.3 2004/08/23 01:56:04 spestov Exp $
|
||||||
./library/prettyprint.factor:! $Id: prettyprint.factor,v 1.6 2004/07/23 05:27:54 spestov Exp $
|
./library/compiler/assembler.factor:! $Id: assembler.factor,v 1.5 2004/10/01 01:49:49 spestov Exp $
|
||||||
./library/ansi.factor:! $Id: ansi.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/compiler/generic.factor:! $Id: generic.factor,v 1.3 2004/10/07 01:04:01 spestov Exp $
|
||||||
./library/inspect-vocabularies.factor:! $Id: inspect-vocabularies.factor,v 1.6 2004/07/23 05:38:36 spestov Exp $
|
./library/compiler/assembly-x86.factor:! $Id: assembly-x86.factor,v 1.9 2004/10/02 02:25:19 spestov Exp $
|
||||||
./library/vectors.factor:! $Id: vectors.factor,v 1.3 2004/07/22 23:48:49 spestov Exp $
|
./library/compiler/alien-types.factor:! $Id: alien-types.factor,v 1.4 2004/09/27 00:16:01 spestov Exp $
|
||||||
./library/httpd/file-responder.factor:! $Id: file-responder.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/compiler/alien-macros.factor:! $Id: alien-macros.factor,v 1.6 2004/10/01 01:49:49 spestov Exp $
|
||||||
./library/httpd/http-common.factor:! $Id: http-common.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/compiler/compile-all.factor:! $Id: compile-all.factor,v 1.4 2004/10/10 01:58:16 spestov Exp $
|
||||||
./library/httpd/inspect-responder.factor:! $Id: inspect-responder.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/compiler/alien.factor:! $Id: alien.factor,v 1.3 2004/10/10 01:43:14 spestov Exp $
|
||||||
./library/httpd/quit-responder.factor:! $Id: quit-responder.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/compiler/compiler.factor:! $Id: compiler.factor,v 1.12 2004/10/09 19:14:49 spestov Exp $
|
||||||
./library/httpd/responder.factor:! $Id: responder.factor,v 1.3 2004/07/22 23:48:50 spestov Exp $
|
./library/compiler/compiler-macros.factor:! $Id: compiler-macros.factor,v 1.4 2004/10/02 02:46:12 spestov Exp $
|
||||||
./library/httpd/default-responders.factor:! $Id: default-responders.factor,v 1.3 2004/07/22 23:48:50 spestov Exp $
|
./library/compiler/interpret-only.factor:! $Id: interpret-only.factor,v 1.3 2004/10/07 01:04:01 spestov Exp $
|
||||||
./library/httpd/wiki-responder.factor:! $Id: wiki-responder.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/compiler/ifte.factor:! $Id: ifte.factor,v 1.3 2004/10/03 20:07:48 spestov Exp $
|
||||||
./library/httpd/html.factor:! $Id: html.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/vocabulary-style.factor:! $Id: vocabulary-style.factor,v 1.9 2004/09/02 23:38:04 spestov Exp $
|
||||||
./library/httpd/test-responder.factor:! $Id: test-responder.factor,v 1.3 2004/07/22 23:48:50 spestov Exp $
|
./library/prettyprint.factor:! $Id: prettyprint.factor,v 1.18 2004/09/28 04:24:35 spestov Exp $
|
||||||
./library/httpd/httpd.factor:! $Id: httpd.factor,v 1.4 2004/07/23 05:21:46 spestov Exp $
|
./library/ansi.factor:! $Id: ansi.factor,v 1.4 2004/08/22 05:46:25 spestov Exp $
|
||||||
./library/httpd/url-encoding.factor:! $Id: url-encoding.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/inspect-vocabularies.factor:! $Id: inspect-vocabularies.factor,v 1.11 2004/10/02 02:25:19 spestov Exp $
|
||||||
./library/math/namespace-math.factor:! $Id: namespace-math.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/vectors.factor:! $Id: vectors.factor,v 1.7 2004/08/27 02:21:03 spestov Exp $
|
||||||
./library/math/arc-trig-hyp.factor:! $Id: arc-trig-hyp.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/httpd/file-responder.factor:! $Id: file-responder.factor,v 1.11 2004/10/05 01:51:57 spestov Exp $
|
||||||
./library/math/quadratic.factor:! $Id: quadratic.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/httpd/http-common.factor:! $Id: http-common.factor,v 1.12 2004/09/02 23:38:04 spestov Exp $
|
||||||
./library/math/list-math.factor:! $Id: list-math.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/httpd/inspect-responder.factor:! $Id: inspect-responder.factor,v 1.3 2004/08/18 01:57:45 spestov Exp $
|
||||||
./library/math/math-combinators.factor:! $Id: math-combinators.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/httpd/quit-responder.factor:! $Id: quit-responder.factor,v 1.4 2004/08/28 20:43:42 spestov Exp $
|
||||||
./library/math/pow.factor:! $Id: pow.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/httpd/responder.factor:! $Id: responder.factor,v 1.16 2004/09/23 03:42:45 spestov Exp $
|
||||||
./library/math/math.factor:! $Id: math.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/httpd/resource-responder.factor:! $Id: resource-responder.factor,v 1.1 2004/09/02 23:38:04 spestov Exp $
|
||||||
./library/math/arithmetic.factor:! $Id: arithmetic.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/httpd/default-responders.factor:! $Id: default-responders.factor,v 1.9 2004/09/23 03:42:45 spestov Exp $
|
||||||
./library/math/trig-hyp.factor:! $Id: trig-hyp.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/httpd/wiki-responder.factor:! $Id: wiki-responder.factor,v 1.6 2004/09/15 03:23:05 spestov Exp $
|
||||||
./library/errors.factor:! $Id: errors.factor,v 1.6 2004/07/22 23:48:49 spestov Exp $
|
./library/httpd/html.factor:! $Id: html.factor,v 1.14 2004/10/05 01:51:57 spestov Exp $
|
||||||
./library/random.factor:! $Id: random.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/httpd/test-responder.factor:! $Id: test-responder.factor,v 1.4 2004/08/11 03:48:07 spestov Exp $
|
||||||
./library/styles.factor:! $Id: styles.factor,v 1.3 2004/07/22 23:48:49 spestov Exp $
|
./library/httpd/httpd.factor:! $Id: httpd.factor,v 1.16 2004/09/18 22:15:00 spestov Exp $
|
||||||
./library/combinators.factor:! $Id: combinators.factor,v 1.3 2004/07/22 23:48:49 spestov Exp $
|
./library/httpd/url-encoding.factor:! $Id: url-encoding.factor,v 1.6 2004/10/07 03:34:22 spestov Exp $
|
||||||
./library/inspector.factor:! $Id: inspector.factor,v 1.7 2004/07/23 05:21:46 spestov Exp $
|
./library/math/namespace-math.factor:! $Id: namespace-math.factor,v 1.4 2004/10/07 03:34:22 spestov Exp $
|
||||||
./library/words.factor:! $Id: words.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/math/arc-trig-hyp.factor:! $Id: arc-trig-hyp.factor,v 1.4 2004/08/27 02:21:03 spestov Exp $
|
||||||
./library/continuations.factor:! $Id: continuations.factor,v 1.3 2004/07/22 23:48:49 spestov Exp $
|
./library/math/constants.factor:! $Id: constants.factor,v 1.2 2004/09/19 02:29:28 spestov Exp $
|
||||||
./library/assoc.factor:! $Id: assoc.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/math/quadratic.factor:! $Id: quadratic.factor,v 1.4 2004/10/07 03:34:22 spestov Exp $
|
||||||
./library/logic.factor:! $Id: logic.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/math/simpson.factor:! $Id: simpson.factor,v 1.3 2004/08/27 02:21:16 spestov Exp $
|
||||||
./library/list-namespaces.factor:! $Id: list-namespaces.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/math/list-math.factor:! $Id: list-math.factor,v 1.4 2004/08/27 02:21:16 spestov Exp $
|
||||||
./library/vocabularies.factor:! $Id: vocabularies.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/math/math-combinators.factor:! $Id: math-combinators.factor,v 1.5 2004/09/25 03:22:43 spestov Exp $
|
||||||
./library/lists.factor:! $Id: lists.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/math/pow.factor:! $Id: pow.factor,v 1.6 2004/10/07 03:34:22 spestov Exp $
|
||||||
./library/debugger.factor:! $Id: debugger.factor,v 1.6 2004/07/23 05:38:36 spestov Exp $
|
./library/math/math.factor:! $Id: math.factor,v 1.5 2004/09/22 02:58:53 spestov Exp $
|
||||||
|
./library/math/arithmetic.factor:! $Id: arithmetic.factor,v 1.9 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./library/math/trig-hyp.factor:! $Id: trig-hyp.factor,v 1.7 2004/09/19 02:29:28 spestov Exp $
|
||||||
|
./library/errors.factor:! $Id: errors.factor,v 1.13 2004/10/03 20:07:47 spestov Exp $
|
||||||
|
./library/random.factor:! $Id: random.factor,v 1.4 2004/10/07 03:34:19 spestov Exp $
|
||||||
|
./library/styles.factor:! $Id: styles.factor,v 1.7 2004/09/02 23:38:04 spestov Exp $
|
||||||
|
./library/combinators.factor:! $Id: combinators.factor,v 1.10 2004/10/10 02:36:41 spestov Exp $
|
||||||
|
./library/inspector.factor:! $Id: inspector.factor,v 1.17 2004/08/31 00:24:18 spestov Exp $
|
||||||
|
./library/words.factor:! $Id: words.factor,v 1.5 2004/09/28 04:24:35 spestov Exp $
|
||||||
|
./library/continuations.factor:! $Id: continuations.factor,v 1.5 2004/08/23 06:15:10 spestov Exp $
|
||||||
|
./library/assoc.factor:! $Id: assoc.factor,v 1.5 2004/08/21 07:30:49 spestov Exp $
|
||||||
|
./library/logic.factor:! $Id: logic.factor,v 1.4 2004/08/28 20:43:42 spestov Exp $
|
||||||
|
./library/cross-compiler.factor:! $Id: cross-compiler.factor,v 1.51 2004/10/05 03:58:50 spestov Exp $
|
||||||
|
./library/image.factor:! $Id: image.factor,v 1.30 2004/10/01 01:49:49 spestov Exp $
|
||||||
|
./library/list-namespaces.factor:! $Id: list-namespaces.factor,v 1.4 2004/09/28 04:24:35 spestov Exp $
|
||||||
|
./library/stdio-binary.factor:! $Id: stdio-binary.factor,v 1.5 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./library/vocabularies.factor:! $Id: vocabularies.factor,v 1.10 2004/09/19 02:29:28 spestov Exp $
|
||||||
|
./library/lists.factor:! $Id: lists.factor,v 1.15 2004/10/01 01:49:49 spestov Exp $
|
||||||
|
./library/extend-stream.factor:! $Id: extend-stream.factor,v 1.1 2004/08/22 05:46:25 spestov Exp $
|
||||||
|
./library/debugger.factor:! $Id: debugger.factor,v 1.11 2004/08/18 23:22:14 spestov Exp $
|
||||||
./library/cons.factor:! $Id: cons.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/cons.factor:! $Id: cons.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
||||||
./library/format.factor:! $Id: format.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/sdl/sdl-video.factor:! $Id: sdl-video.factor,v 1.2 2004/10/10 01:43:14 spestov Exp $
|
||||||
./library/hashtables.factor:! $Id: hashtables.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/sdl/sdl.factor:! $Id: sdl.factor,v 1.2 2004/10/10 01:43:14 spestov Exp $
|
||||||
./library/stream.factor:! $Id: stream.factor,v 1.3 2004/07/22 23:48:49 spestov Exp $
|
./library/sdl/sdl-event.factor:! $Id: sdl-event.factor,v 1.2 2004/10/10 01:43:14 spestov Exp $
|
||||||
./library/strings.factor:! $Id: strings.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/inferior.factor:! $Id: inferior.factor,v 1.2 2004/08/24 22:01:35 spestov Exp $
|
||||||
./library/logging.factor:! $Id: logging.factor,v 1.3 2004/07/24 00:35:12 spestov Exp $
|
./library/format.factor:! $Id: format.factor,v 1.3 2004/08/27 02:21:03 spestov Exp $
|
||||||
./library/init.factor:! $Id: init.factor,v 1.6 2004/07/24 19:11:54 spestov Exp $
|
./library/hashtables.factor:! $Id: hashtables.factor,v 1.6 2004/08/31 04:27:09 spestov Exp $
|
||||||
./library/platform/jvm/prettyprint.factor:! $Id: prettyprint.factor,v 1.4 2004/07/22 23:48:50 spestov Exp $
|
./library/stream.factor:! $Id: stream.factor,v 1.9 2004/08/24 22:01:35 spestov Exp $
|
||||||
./library/platform/jvm/vectors.factor:! $Id: vectors.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/files.factor:! $Id: files.factor,v 1.4 2004/09/04 07:06:53 spestov Exp $
|
||||||
./library/platform/jvm/kernel.factor:! $Id: kernel.factor,v 1.3 2004/07/22 23:48:50 spestov Exp $
|
./library/strings.factor:! $Id: strings.factor,v 1.14 2004/10/07 03:34:19 spestov Exp $
|
||||||
./library/platform/jvm/errors.factor:! $Id: errors.factor,v 1.5 2004/07/23 05:38:36 spestov Exp $
|
./library/logging.factor:! $Id: logging.factor,v 1.6 2004/08/18 03:49:48 spestov Exp $
|
||||||
|
./library/init.factor:! $Id: init.factor,v 1.23 2004/10/10 01:43:13 spestov Exp $
|
||||||
|
./library/platform/jvm/prettyprint.factor:! $Id: prettyprint.factor,v 1.10 2004/10/05 03:06:18 spestov Exp $
|
||||||
|
./library/platform/jvm/vectors.factor:! $Id: vectors.factor,v 1.3 2004/07/31 18:58:16 spestov Exp $
|
||||||
|
./library/platform/jvm/kernel.factor:! $Id: kernel.factor,v 1.11 2004/09/04 07:06:53 spestov Exp $
|
||||||
|
./library/platform/jvm/errors.factor:! $Id: errors.factor,v 1.6 2004/10/03 20:07:48 spestov Exp $
|
||||||
./library/platform/jvm/random.factor:! $Id: random.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/random.factor:! $Id: random.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
||||||
./library/platform/jvm/combinators.factor:! $Id: combinators.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/combinators.factor:! $Id: combinators.factor,v 1.5 2004/08/22 21:28:31 spestov Exp $
|
||||||
./library/platform/jvm/words.factor:! $Id: words.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/words.factor:! $Id: words.factor,v 1.8 2004/10/05 03:06:18 spestov Exp $
|
||||||
./library/platform/jvm/math-types.factor:! $Id: math-types.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/processes.factor:! $Id: processes.factor,v 1.2 2004/09/15 03:23:05 spestov Exp $
|
||||||
./library/platform/jvm/stack2.factor:! $Id: stack2.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/cross-compiler.factor:! $Id: cross-compiler.factor,v 1.5 2004/09/11 19:26:17 spestov Exp $
|
||||||
./library/platform/jvm/vocabularies.factor:! $Id: vocabularies.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/math-types.factor:! $Id: math-types.factor,v 1.4 2004/08/27 02:21:16 spestov Exp $
|
||||||
|
./library/platform/jvm/stack2.factor:! $Id: stack2.factor,v 1.5 2004/08/28 20:43:43 spestov Exp $
|
||||||
|
./library/platform/jvm/vocabularies.factor:! $Id: vocabularies.factor,v 1.3 2004/08/16 23:28:54 spestov Exp $
|
||||||
./library/platform/jvm/debugger.factor:! $Id: debugger.factor,v 1.5 2004/07/23 05:38:36 spestov Exp $
|
./library/platform/jvm/debugger.factor:! $Id: debugger.factor,v 1.5 2004/07/23 05:38:36 spestov Exp $
|
||||||
./library/platform/jvm/regexp.factor:! $Id: regexp.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/regexp.factor:! $Id: regexp.factor,v 1.4 2004/08/27 02:21:16 spestov Exp $
|
||||||
./library/platform/jvm/stack.factor:! $Id: stack.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/stack.factor:! $Id: stack.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
||||||
./library/platform/jvm/cons.factor:! $Id: cons.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/cons.factor:! $Id: cons.factor,v 1.3 2004/08/03 06:08:11 spestov Exp $
|
||||||
./library/platform/jvm/compiler.factor:! $Id: compiler.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/compiler.factor:! $Id: compiler.factor,v 1.5 2004/08/24 19:27:37 spestov Exp $
|
||||||
./library/platform/jvm/parser.factor:! $Id: parser.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/parser.factor:! $Id: parser.factor,v 1.9 2004/09/06 00:14:37 spestov Exp $
|
||||||
./library/platform/jvm/stream.factor:! $Id: stream.factor,v 1.3 2004/07/23 22:52:08 spestov Exp $
|
./library/platform/jvm/stream.factor:! $Id: stream.factor,v 1.15 2004/09/18 22:15:00 spestov Exp $
|
||||||
./library/platform/jvm/boot-mini.factor:! $Id: boot-mini.factor,v 1.5 2004/07/23 05:38:36 spestov Exp $
|
./library/platform/jvm/files.factor:! $Id: files.factor,v 1.3 2004/09/04 07:06:53 spestov Exp $
|
||||||
./library/platform/jvm/strings.factor:! $Id: strings.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/boot-mini.factor:! $Id: boot-mini.factor,v 1.9 2004/09/19 02:29:28 spestov Exp $
|
||||||
./library/platform/jvm/listener.factor:! $Id: listener.factor,v 1.4 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/strings.factor:! $Id: strings.factor,v 1.3 2004/08/28 20:43:43 spestov Exp $
|
||||||
./library/platform/jvm/init.factor:! $Id: init.factor,v 1.4 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/listener.factor:! $Id: listener.factor,v 1.16 2004/09/25 03:22:43 spestov Exp $
|
||||||
./library/platform/jvm/arithmetic.factor:! $Id: arithmetic.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/init.factor:! $Id: init.factor,v 1.16 2004/09/02 23:38:05 spestov Exp $
|
||||||
./library/platform/jvm/unparser.factor:! $Id: unparser.factor,v 1.3 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/arithmetic.factor:! $Id: arithmetic.factor,v 1.8 2004/08/27 02:21:16 spestov Exp $
|
||||||
./library/platform/jvm/threads.factor:! $Id: threads.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/unparser.factor:! $Id: unparser.factor,v 1.6 2004/09/05 02:29:07 spestov Exp $
|
||||||
./library/platform/jvm/sbuf.factor:! $Id: sbuf.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/threads.factor:! $Id: threads.factor,v 1.3 2004/07/28 00:23:08 spestov Exp $
|
||||||
./library/platform/jvm/real-math.factor:! $Id: real-math.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/sbuf.factor:! $Id: sbuf.factor,v 1.4 2004/09/07 02:39:11 spestov Exp $
|
||||||
./library/platform/jvm/boot.factor:! $Id: boot.factor,v 1.3 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/real-math.factor:! $Id: real-math.factor,v 1.4 2004/08/27 02:21:16 spestov Exp $
|
||||||
|
./library/platform/jvm/boot.factor:! $Id: boot.factor,v 1.5 2004/08/28 20:43:43 spestov Exp $
|
||||||
./library/platform/jvm/namespaces.factor:! $Id: namespaces.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/jvm/namespaces.factor:! $Id: namespaces.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
||||||
./library/platform/jvm/boot-sumo.factor:! $Id: boot-sumo.factor,v 1.5 2004/07/23 05:38:36 spestov Exp $
|
./library/platform/jvm/boot-sumo.factor:! $Id: boot-sumo.factor,v 1.18 2004/09/04 07:06:53 spestov Exp $
|
||||||
./library/platform/native/prettyprint.factor:! $Id: prettyprint.factor,v 1.4 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/prettyprint.factor:! $Id: prettyprint.factor,v 1.9 2004/10/02 02:25:19 spestov Exp $
|
||||||
./library/platform/native/vectors.factor:! $Id: vectors.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/vectors.factor:! $Id: vectors.factor,v 1.7 2004/09/28 04:24:35 spestov Exp $
|
||||||
./library/platform/native/kernel.factor:! $Id: kernel.factor,v 1.6 2004/07/24 04:54:57 spestov Exp $
|
./library/platform/native/kernel.factor:! $Id: kernel.factor,v 1.34 2004/10/09 19:14:49 spestov Exp $
|
||||||
./library/platform/native/errors.factor:! $Id: errors.factor,v 1.9 2004/07/24 21:37:42 spestov Exp $
|
./library/platform/native/errors.factor:! $Id: errors.factor,v 1.19 2004/08/23 06:15:10 spestov Exp $
|
||||||
./library/platform/native/words.factor:! $Id: words.factor,v 1.3 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/heap-stats.factor:! $Id: heap-stats.factor,v 1.1 2004/09/21 16:41:57 spestov Exp $
|
||||||
./library/platform/native/cross-compiler.factor:! $Id: cross-compiler.factor,v 1.10 2004/07/24 21:37:42 spestov Exp $
|
./library/platform/native/random.factor:! $Id: random.factor,v 1.4 2004/08/28 20:43:43 spestov Exp $
|
||||||
./library/platform/native/image.factor:! $Id: image.factor,v 1.5 2004/07/24 19:11:54 spestov Exp $
|
./library/platform/native/boot-stage2.factor:! $Id: boot-stage2.factor,v 1.27 2004/10/10 01:43:14 spestov Exp $
|
||||||
./library/platform/native/vocabularies.factor:! $Id: vocabularies.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/words.factor:! $Id: words.factor,v 1.11 2004/10/02 02:25:19 spestov Exp $
|
||||||
./library/platform/native/io-internals.factor:! $Id: io-internals.factor,v 1.3 2004/07/24 00:35:13 spestov Exp $
|
./library/platform/native/network.factor:! $Id: network.factor,v 1.2 2004/08/29 02:25:58 spestov Exp $
|
||||||
./library/platform/native/parse-syntax.factor:! $Id: parse-syntax.factor,v 1.1 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/cross-compiler.factor:! $Id: cross-compiler.factor,v 1.18 2004/09/11 19:26:18 spestov Exp $
|
||||||
./library/platform/native/parse-numbers.factor:! $Id: parse-numbers.factor,v 1.1 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/init-stage2.factor:! $Id: init-stage2.factor,v 1.7 2004/10/09 19:14:49 spestov Exp $
|
||||||
./library/platform/native/stack.factor:! $Id: stack.factor,v 1.2 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/vocabularies.factor:! $Id: vocabularies.factor,v 1.4 2004/08/28 20:43:43 spestov Exp $
|
||||||
./library/platform/native/parse-stream.factor:! $Id: parse-stream.factor,v 1.7 2004/07/24 19:11:54 spestov Exp $
|
./library/platform/native/io-internals.factor:! $Id: io-internals.factor,v 1.14 2004/09/03 01:51:19 spestov Exp $
|
||||||
./library/platform/native/parser.factor:! $Id: parser.factor,v 1.7 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/parse-syntax.factor:! $Id: parse-syntax.factor,v 1.22 2004/10/07 03:40:46 spestov Exp $
|
||||||
./library/platform/native/stream.factor:! $Id: stream.factor,v 1.8 2004/07/24 21:37:42 spestov Exp $
|
./library/platform/native/debugger.factor:! $Id: debugger.factor,v 1.16 2004/09/27 00:16:01 spestov Exp $
|
||||||
./library/platform/native/init.factor:! $Id: init.factor,v 1.6 2004/07/24 04:54:57 spestov Exp $
|
./library/platform/native/parse-numbers.factor:! $Id: parse-numbers.factor,v 1.11 2004/09/15 03:23:05 spestov Exp $
|
||||||
./library/platform/native/unparser.factor:! $Id: unparser.factor,v 1.4 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/stack.factor:! $Id: stack.factor,v 1.5 2004/10/01 01:49:49 spestov Exp $
|
||||||
./library/platform/native/boot.factor:! $Id: boot.factor,v 1.10 2004/07/24 00:35:13 spestov Exp $
|
./library/platform/native/primitives.factor:! $Id: primitives.factor,v 1.14 2004/10/03 20:07:48 spestov Exp $
|
||||||
./library/platform/native/namespaces.factor:! $Id: namespaces.factor,v 1.4 2004/07/22 23:48:50 spestov Exp $
|
./library/platform/native/profiler.factor:! $Id: profiler.factor,v 1.4 2004/08/29 07:20:18 spestov Exp $
|
||||||
./library/vector-combinators.factor:! $Id: vector-combinators.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/platform/native/parse-stream.factor:! $Id: parse-stream.factor,v 1.17 2004/09/06 00:14:37 spestov Exp $
|
||||||
./library/stdio.factor:! $Id: stdio.factor,v 1.5 2004/07/22 23:48:49 spestov Exp $
|
./library/platform/native/parser.factor:! $Id: parser.factor,v 1.20 2004/09/28 04:24:35 spestov Exp $
|
||||||
./library/interpreter.factor:! $Id: interpreter.factor,v 1.3 2004/07/22 23:48:49 spestov Exp $
|
./library/platform/native/stream.factor:! $Id: stream.factor,v 1.27 2004/09/06 00:14:37 spestov Exp $
|
||||||
./library/sbuf.factor:! $Id: sbuf.factor,v 1.2 2004/07/22 23:48:49 spestov Exp $
|
./library/platform/native/files.factor:! $Id: files.factor,v 1.5 2004/09/02 20:40:19 spestov Exp $
|
||||||
./library/telnetd.factor:! $Id: telnetd.factor,v 1.4 2004/07/22 23:48:49 spestov Exp $
|
./library/platform/native/strings.factor:! $Id: strings.factor,v 1.4 2004/08/27 02:21:16 spestov Exp $
|
||||||
./library/namespaces.factor:! $Id: namespaces.factor,v 1.4 2004/07/22 23:48:49 spestov Exp $
|
./library/platform/native/init.factor:! $Id: init.factor,v 1.22 2004/09/07 02:39:11 spestov Exp $
|
||||||
|
./library/platform/native/in-thread.factor:! $Id: in-thread.factor,v 1.1 2004/08/22 05:46:26 spestov Exp $
|
||||||
|
./library/platform/native/math.factor:! $Id: math.factor,v 1.8 2004/10/02 02:25:19 spestov Exp $
|
||||||
|
./library/platform/native/unparser.factor:! $Id: unparser.factor,v 1.21 2004/09/27 00:16:01 spestov Exp $
|
||||||
|
./library/platform/native/threads.factor:! $Id: threads.factor,v 1.3 2004/08/29 02:25:58 spestov Exp $
|
||||||
|
./library/platform/native/types.factor:! $Id: types.factor,v 1.5 2004/10/02 02:02:54 spestov Exp $
|
||||||
|
./library/platform/native/boot.factor:! $Id: boot.factor,v 1.36 2004/09/22 02:58:53 spestov Exp $
|
||||||
|
./library/platform/native/namespaces.factor:! $Id: namespaces.factor,v 1.10 2004/08/27 02:21:16 spestov Exp $
|
||||||
|
./library/jedit/jedit-remote.factor:! $Id: jedit-remote.factor,v 1.3 2004/08/22 05:46:25 spestov Exp $
|
||||||
|
./library/jedit/jedit-local.factor:! $Id: jedit-local.factor,v 1.2 2004/08/27 02:21:03 spestov Exp $
|
||||||
|
./library/jedit/jedit.factor:! $Id: jedit.factor,v 1.8 2004/09/28 04:24:35 spestov Exp $
|
||||||
|
./library/vector-combinators.factor:! $Id: vector-combinators.factor,v 1.4 2004/10/07 03:34:19 spestov Exp $
|
||||||
|
./library/stdio.factor:! $Id: stdio.factor,v 1.16 2004/09/25 03:22:43 spestov Exp $
|
||||||
|
./library/interpreter.factor:! $Id: interpreter.factor,v 1.18 2004/10/05 01:51:53 spestov Exp $
|
||||||
|
./library/sbuf.factor:! $Id: sbuf.factor,v 1.10 2004/10/07 03:34:19 spestov Exp $
|
||||||
|
./library/telnetd.factor:! $Id: telnetd.factor,v 1.8 2004/09/18 22:15:00 spestov Exp $
|
||||||
|
./library/namespaces.factor:! $Id: namespaces.factor,v 1.8 2004/10/10 01:43:13 spestov Exp $
|
||||||
./factor/compiler/CompiledList.java: * $Id: CompiledList.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
./factor/compiler/CompiledList.java: * $Id: CompiledList.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
||||||
./factor/compiler/TypeInferenceException.java: * $Id: TypeInferenceException.java,v 1.1.1.1 2004/07/16 06:26:10 spestov Exp $
|
./factor/compiler/TypeInferenceException.java: * $Id: TypeInferenceException.java,v 1.1.1.1 2004/07/16 06:26:10 spestov Exp $
|
||||||
./factor/compiler/StackEffect.java: * $Id: StackEffect.java,v 1.1.1.1 2004/07/16 06:26:10 spestov Exp $
|
./factor/compiler/StackEffect.java: * $Id: StackEffect.java,v 1.1.1.1 2004/07/16 06:26:10 spestov Exp $
|
||||||
./factor/compiler/Literal.java: * $Id: Literal.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
./factor/compiler/Literal.java: * $Id: Literal.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
||||||
./factor/compiler/FactorClassLoader.java:* $Id: FactorClassLoader.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/compiler/FactorClassLoader.java:* $Id: FactorClassLoader.java,v 1.3 2004/08/06 22:40:42 spestov Exp $
|
||||||
|
./factor/compiler/FactorCompiler.java.new: * $Id: FactorCompiler.java.new,v 1.1.1.1 2004/07/16 06:26:10 spestov Exp $
|
||||||
./factor/compiler/FactorCompilerException.java: * $Id: FactorCompilerException.java,v 1.1.1.1 2004/07/16 06:26:10 spestov Exp $
|
./factor/compiler/FactorCompilerException.java: * $Id: FactorCompilerException.java,v 1.1.1.1 2004/07/16 06:26:10 spestov Exp $
|
||||||
./factor/compiler/CompiledDefinition.java:* $Id: CompiledDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/compiler/CompiledDefinition.java:* $Id: CompiledDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
||||||
./factor/compiler/Result.java: * $Id: Result.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
./factor/compiler/Result.java: * $Id: Result.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
||||||
|
@ -113,57 +157,72 @@
|
||||||
./factor/compiler/RecursiveForm.java: * $Id: RecursiveForm.java,v 1.1.1.1 2004/07/16 06:26:08 spestov Exp $
|
./factor/compiler/RecursiveForm.java: * $Id: RecursiveForm.java,v 1.1.1.1 2004/07/16 06:26:08 spestov Exp $
|
||||||
./factor/compiler/AuxiliaryQuotation.java: * $Id: AuxiliaryQuotation.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
./factor/compiler/AuxiliaryQuotation.java: * $Id: AuxiliaryQuotation.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
||||||
./factor/compiler/CompilerState.java: * $Id: CompilerState.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
./factor/compiler/CompilerState.java: * $Id: CompilerState.java,v 1.1.1.1 2004/07/16 06:26:09 spestov Exp $
|
||||||
./factor/compiler/FlowObject.java: * $Id: FlowObject.java,v 1.1.1.1 2004/07/16 06:26:10 spestov Exp $
|
./factor/compiler/FlowObject.java: * $Id: FlowObject.java,v 1.2 2004/09/07 02:39:10 spestov Exp $
|
||||||
./factor/FactorPrimitiveDefinition.java: * $Id: FactorPrimitiveDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/FactorPrimitiveDefinition.java: * $Id: FactorPrimitiveDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
||||||
./factor/math/Complex.java: * $Id: Complex.java,v 1.1.1.1 2004/07/16 06:26:13 spestov Exp $
|
./factor/math/Complex.java: * $Id: Complex.java,v 1.1.1.1 2004/07/16 06:26:13 spestov Exp $
|
||||||
./factor/math/FactorNumber.java: * $Id: FactorNumber.java,v 1.1.1.1 2004/07/16 06:26:12 spestov Exp $
|
./factor/math/FactorNumber.java: * $Id: FactorNumber.java,v 1.1.1.1 2004/07/16 06:26:12 spestov Exp $
|
||||||
./factor/math/NumberParser.java: * $Id: NumberParser.java,v 1.1.1.1 2004/07/16 06:26:12 spestov Exp $
|
./factor/math/NumberParser.java: * $Id: NumberParser.java,v 1.2 2004/08/07 22:45:47 spestov Exp $
|
||||||
./factor/math/Ratio.java: * $Id: Ratio.java,v 1.1.1.1 2004/07/16 06:26:13 spestov Exp $
|
./factor/math/Ratio.java: * $Id: Ratio.java,v 1.1.1.1 2004/07/16 06:26:13 spestov Exp $
|
||||||
./factor/math/FactorMath.java: * $Id: FactorMath.java,v 1.1.1.1 2004/07/16 06:26:13 spestov Exp $
|
./factor/math/FactorMath.java: * $Id: FactorMath.java,v 1.2 2004/08/26 23:37:16 spestov Exp $
|
||||||
./factor/FactorExternalizable.java: * $Id: FactorExternalizable.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
./factor/FactorExternalizable.java: * $Id: FactorExternalizable.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
||||||
./factor/FactorJava.java: * $Id: FactorJava.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
./factor/FactorJava.java: * $Id: FactorJava.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
||||||
./factor/FactorDocComment.java: * $Id: FactorDocComment.java,v 1.1.1.1 2004/07/16 06:26:05 spestov Exp $
|
./factor/FactorDocComment.java: * $Id: FactorDocComment.java,v 1.2 2004/08/18 02:08:35 spestov Exp $
|
||||||
./factor/FactorArray.java: * $Id: FactorArray.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/FactorArray.java: * $Id: FactorArray.java,v 1.5 2004/09/27 01:34:24 spestov Exp $
|
||||||
./factor/FactorLib.java: * $Id: FactorLib.java,v 1.1.1.1 2004/07/16 06:26:04 spestov Exp $
|
./factor/FactorCompoundDefinition.java.new:* $Id: FactorCompoundDefinition.java.new,v 1.1.1.1 2004/07/16 06:26:04 spestov Exp $
|
||||||
|
./factor/FactorSymbolDefinition.java:* $Id: FactorSymbolDefinition.java,v 1.1 2004/10/05 03:06:14 spestov Exp $
|
||||||
|
./factor/FactorLib.java: * $Id: FactorLib.java,v 1.7 2004/09/07 02:39:10 spestov Exp $
|
||||||
./factor/FactorRuntimeException.java: * $Id: FactorRuntimeException.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
./factor/FactorRuntimeException.java: * $Id: FactorRuntimeException.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
||||||
./factor/ReadTable.java: * $Id: ReadTable.java,v 1.1.1.1 2004/07/16 06:26:03 spestov Exp $
|
./factor/ReadTable.java: * $Id: ReadTable.java,v 1.4 2004/09/06 00:14:36 spestov Exp $
|
||||||
./factor/FactorReader.java: * $Id: FactorReader.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/FactorReader.java: * $Id: FactorReader.java,v 1.9 2004/09/04 07:06:53 spestov Exp $
|
||||||
./factor/FactorStackException.java: * $Id: FactorStackException.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
./factor/FactorStackException.java: * $Id: FactorStackException.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
||||||
./factor/FactorUndefinedWordException.java: * $Id: FactorUndefinedWordException.java,v 1.1.1.1 2004/07/16 06:26:05 spestov Exp $
|
./factor/FactorUndefinedWordException.java: * $Id: FactorUndefinedWordException.java,v 1.1.1.1 2004/07/16 06:26:05 spestov Exp $
|
||||||
./factor/FactorParsingDefinition.java: * $Id: FactorParsingDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/FactorParsingDefinition.java: * $Id: FactorParsingDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
||||||
./factor/FactorNamespace.java: * $Id: FactorNamespace.java,v 1.1.1.1 2004/07/16 06:26:05 spestov Exp $
|
./factor/FactorNamespace.java: * $Id: FactorNamespace.java,v 1.3 2004/09/25 03:22:43 spestov Exp $
|
||||||
./factor/PublicCloneable.java: * $Id: PublicCloneable.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
./factor/PublicCloneable.java: * $Id: PublicCloneable.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
||||||
./factor/Cons.java: * $Id: Cons.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/Cons.java: * $Id: Cons.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
||||||
./factor/FactorCompoundDefinition.java:* $Id: FactorCompoundDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/FactorCompoundDefinition.java:* $Id: FactorCompoundDefinition.java,v 1.3 2004/08/06 22:40:41 spestov Exp $
|
||||||
./factor/FactorObject.java: * $Id: FactorObject.java,v 1.1.1.1 2004/07/16 06:26:03 spestov Exp $
|
./factor/FactorObject.java: * $Id: FactorObject.java,v 1.1.1.1 2004/07/16 06:26:03 spestov Exp $
|
||||||
./factor/parser/PassThrough.java: * $Id: PassThrough.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/ComplexLiteral.java: * $Id: ComplexLiteral.java,v 1.3 2004/08/16 02:45:08 spestov Exp $
|
||||||
./factor/parser/ComplexLiteral.java: * $Id: ComplexLiteral.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
|
||||||
./factor/parser/Base.java: * $Id: Base.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/Base.java: * $Id: Base.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
|
./factor/parser/Symbol.java: * $Id: Symbol.java,v 1.1 2004/10/05 03:06:18 spestov Exp $
|
||||||
./factor/parser/NoParsing.java: * $Id: NoParsing.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/NoParsing.java: * $Id: NoParsing.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/StringLiteral.java: * $Id: StringLiteral.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/StringLiteral.java: * $Id: StringLiteral.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/Bar.java: * $Id: Bar.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/Bar.java: * $Id: Bar.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/Def.java: * $Id: Def.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/Def.java: * $Id: Def.java,v 1.6 2004/08/20 06:08:19 spestov Exp $
|
||||||
./factor/parser/F.java: * $Id: F.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/F.java: * $Id: F.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/CharLiteral.java: * $Id: CharLiteral.java,v 1.3 2004/07/22 23:48:49 spestov Exp $
|
./factor/parser/CharLiteral.java: * $Id: CharLiteral.java,v 1.5 2004/10/03 20:07:47 spestov Exp $
|
||||||
./factor/parser/Shuffle.java: * $Id: Shuffle.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/Shuffle.java: * $Id: Shuffle.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/LineComment.java: * $Id: LineComment.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/LineComment.java: * $Id: LineComment.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/T.java: * $Id: T.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/T.java: * $Id: T.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/StackComment.java: * $Id: StackComment.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/StackComment.java: * $Id: StackComment.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/Dispatch.java: * $Id: Dispatch.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/Use.java: * $Id: Use.java,v 1.3 2004/08/16 02:45:08 spestov Exp $
|
||||||
./factor/parser/Use.java: * $Id: Use.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/EndVector.java: * $Id: EndVector.java,v 1.1 2004/08/10 23:53:52 spestov Exp $
|
||||||
./factor/parser/Ket.java: * $Id: Ket.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/Ket.java: * $Id: Ket.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/Defer.java: * $Id: Defer.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/Defer.java: * $Id: Defer.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/Ine.java: * $Id: Ine.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/Ine.java: * $Id: Ine.java,v 1.3 2004/08/17 03:52:49 spestov Exp $
|
||||||
./factor/parser/Unreadable.java: * $Id: Unreadable.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
|
||||||
./factor/parser/Bra.java: * $Id: Bra.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/Bra.java: * $Id: Bra.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/parser/In.java: * $Id: In.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/parser/In.java: * $Id: In.java,v 1.3 2004/08/16 02:45:08 spestov Exp $
|
||||||
|
./factor/parser/BeginVector.java: * $Id: BeginVector.java,v 1.1 2004/08/10 23:53:52 spestov Exp $
|
||||||
./factor/listener/EvalListener.java: * $Id: EvalListener.java,v 1.1.1.1 2004/07/16 06:26:11 spestov Exp $
|
./factor/listener/EvalListener.java: * $Id: EvalListener.java,v 1.1.1.1 2004/07/16 06:26:11 spestov Exp $
|
||||||
./factor/listener/FactorListener.java: * $Id: FactorListener.java,v 1.1.1.1 2004/07/16 06:26:11 spestov Exp $
|
./factor/listener/FactorListenerPanel.java: * $Id: FactorListenerPanel.java,v 1.3 2004/08/22 23:01:40 spestov Exp $
|
||||||
./factor/listener/FactorDesktop.java: * $Id: FactorDesktop.java,v 1.1.1.1 2004/07/16 06:26:11 spestov Exp $
|
./factor/listener/FactorListener.java: * $Id: FactorListener.java,v 1.8 2004/09/03 20:54:58 spestov Exp $
|
||||||
./factor/FactorInterpreter.java: * $Id: FactorInterpreter.java,v 1.6 2004/07/22 23:48:49 spestov Exp $
|
./factor/listener/FactorDesktop.java: * $Id: FactorDesktop.java,v 1.3 2004/08/08 21:20:53 spestov Exp $
|
||||||
|
./factor/FactorInterpreter.java: * $Id: FactorInterpreter.java,v 1.20 2004/10/05 03:06:14 spestov Exp $
|
||||||
./factor/FactorDomainException.java: * $Id: FactorDomainException.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
./factor/FactorDomainException.java: * $Id: FactorDomainException.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
||||||
./factor/FactorWord.java: * $Id: FactorWord.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/FactorWord.java: * $Id: FactorWord.java,v 1.6 2004/10/03 20:07:47 spestov Exp $
|
||||||
./factor/FactorWordDefinition.java: * $Id: FactorWordDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/FactorWordDefinition.java.new: * $Id: FactorWordDefinition.java.new,v 1.1.1.1 2004/07/16 06:26:04 spestov Exp $
|
||||||
|
./factor/jedit/FactorAsset.java: * $Id: FactorAsset.java,v 1.3 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./factor/jedit/FactorParsedData.java: * $Id: FactorParsedData.java,v 1.3 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./factor/jedit/RestartableFactorScanner.java: * $Id: RestartableFactorScanner.java,v 1.1 2004/08/17 03:52:49 spestov Exp $
|
||||||
|
./factor/jedit/FactorSideKickParser.java: * $Id: FactorSideKickParser.java,v 1.12 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./factor/jedit/EditWordDialog.java: * $Id: EditWordDialog.java,v 1.2 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./factor/jedit/FactorPlugin.java: * $Id: FactorPlugin.java,v 1.15 2004/09/27 00:16:01 spestov Exp $
|
||||||
|
./factor/jedit/FactorCompletion.java: * $Id: FactorCompletion.java,v 1.6 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./factor/jedit/WordPreview.java: * $Id: WordPreview.java,v 1.2 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./factor/jedit/WordListDialog.java: * $Id: WordListDialog.java,v 1.2 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./factor/jedit/FactorWordRenderer.java: * $Id: FactorWordRenderer.java,v 1.9 2004/10/07 01:03:54 spestov Exp $
|
||||||
|
./factor/jedit/InsertUseDialog.java: * $Id: InsertUseDialog.java,v 1.3 2004/09/04 05:05:49 spestov Exp $
|
||||||
|
./factor/FactorWordDefinition.java: * $Id: FactorWordDefinition.java,v 1.3 2004/08/08 06:32:56 spestov Exp $
|
||||||
./factor/primitives/JVarGetStatic.java: * $Id: JVarGetStatic.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/primitives/JVarGetStatic.java: * $Id: JVarGetStatic.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/primitives/Unstack.java: * $Id: Unstack.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/primitives/Unstack.java: * $Id: Unstack.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/primitives/JInvoke.java: * $Id: JInvoke.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/primitives/JInvoke.java: * $Id: JInvoke.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
|
@ -178,7 +237,10 @@
|
||||||
./factor/primitives/Coerce.java: * $Id: Coerce.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/primitives/Coerce.java: * $Id: Coerce.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/primitives/JVarSetStatic.java: * $Id: JVarSetStatic.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/primitives/JVarSetStatic.java: * $Id: JVarSetStatic.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/primitives/JVarGet.java: * $Id: JVarGet.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
./factor/primitives/JVarGet.java: * $Id: JVarGet.java,v 1.2 2004/07/19 20:10:18 spestov Exp $
|
||||||
./factor/FactorScanner.java: * $Id: FactorScanner.java,v 1.1.1.1 2004/07/16 06:26:03 spestov Exp $
|
./factor/FactorScanner.java: * $Id: FactorScanner.java,v 1.6 2004/10/03 20:07:47 spestov Exp $
|
||||||
./factor/FactorParseException.java: * $Id: FactorParseException.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
./factor/FactorParseException.java: * $Id: FactorParseException.java,v 1.3 2004/08/13 22:43:03 spestov Exp $
|
||||||
./factor/FactorException.java: * $Id: FactorException.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
./factor/FactorException.java: * $Id: FactorException.java,v 1.1.1.1 2004/07/16 06:26:06 spestov Exp $
|
||||||
./factor/FactorShuffleDefinition.java: * $Id: FactorShuffleDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
./factor/FactorShuffleDefinition.java: * $Id: FactorShuffleDefinition.java,v 1.2 2004/07/19 20:10:17 spestov Exp $
|
||||||
|
./native/s48_bignum.c:$Id: s48_bignum.c,v 1.6 2004/08/29 19:56:30 spestov Exp $
|
||||||
|
./native/s48_bignumint.h:$Id: s48_bignumint.h,v 1.7 2004/08/29 05:04:42 spestov Exp $
|
||||||
|
./native/s48_bignum.h:$Id: s48_bignum.h,v 1.5 2004/08/28 03:20:10 spestov Exp $
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Main-Class: factor.listener.FactorDesktop
|
|
@ -1,5 +0,0 @@
|
||||||
Manifest-Version: 1.0
|
|
||||||
Created-By: 1.4.2-p6-slava_18_jan_2004_13_00 (Sun Microsystems Inc.)
|
|
||||||
Ant-Version: Apache Ant 1.5.4
|
|
||||||
Main-Class: factor.listener.FactorDesktop
|
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
CC = gcc
|
||||||
|
DEFAULT_CFLAGS = -Os -Wall -export-dynamic -fomit-frame-pointer
|
||||||
|
DEFAULT_LIBS = -lm
|
||||||
|
|
||||||
|
STRIP = strip
|
||||||
|
|
||||||
|
OBJS = native/arithmetic.o native/array.o native/bignum.o \
|
||||||
|
native/s48_bignum.o \
|
||||||
|
native/complex.o native/cons.o native/error.o \
|
||||||
|
native/factor.o native/file.o native/fixnum.o \
|
||||||
|
native/float.o native/gc.o \
|
||||||
|
native/image.o native/io.o native/memory.o \
|
||||||
|
native/misc.o native/port.o native/primitives.o \
|
||||||
|
native/ratio.o native/read.o native/relocate.o \
|
||||||
|
native/run.o \
|
||||||
|
native/sbuf.o native/socket.o native/stack.o \
|
||||||
|
native/string.o native/types.o native/vector.o \
|
||||||
|
native/write.o native/word.o native/compiler.o \
|
||||||
|
native/ffi.o
|
||||||
|
|
||||||
|
default:
|
||||||
|
@echo "Run 'make' with one of the following parameters:"
|
||||||
|
@echo ""
|
||||||
|
@echo "bsd"
|
||||||
|
@echo "linux"
|
||||||
|
@echo "solaris"
|
||||||
|
@echo ""
|
||||||
|
@echo "Also, you might want to set the SITE_CFLAGS environment"
|
||||||
|
@echo "variable to enable some CPU-specific optimizations; this"
|
||||||
|
@echo "can make a huge difference. Eg:"
|
||||||
|
@echo ""
|
||||||
|
@echo "export SITE_CFLAGS=\"-march=pentium4 -ffast-math\""
|
||||||
|
|
||||||
|
bsd:
|
||||||
|
$(MAKE) f \
|
||||||
|
CFLAGS="$(DEFAULT_CFLAGS) -DFFI -pthread" \
|
||||||
|
LIBS="$(DEFAULT_LIBS)"
|
||||||
|
|
||||||
|
linux:
|
||||||
|
$(MAKE) f \
|
||||||
|
CFLAGS="$(DEFAULT_CFLAGS) -DFFI" \
|
||||||
|
LIBS="$(DEFAULT_LIBS) -ldl"
|
||||||
|
|
||||||
|
solaris:
|
||||||
|
$(MAKE) f \
|
||||||
|
CFLAGS="$(DEFAULT_CFLAGS)" \
|
||||||
|
LIBS="$(DEFAULT_LIBS) -lsocket -lnsl -lm"
|
||||||
|
|
||||||
|
f: $(OBJS)
|
||||||
|
$(CC) $(LIBS) $(CFLAGS) -o $@ $(OBJS)
|
||||||
|
$(STRIP) $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJS)
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
$(CC) -c $(CFLAGS) -o $@ $<
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
FACTOR
|
||||||
|
|
||||||
|
This source archive contains sources for two distinct
|
||||||
|
bodies of code -- a Factor interpreter written in Java,
|
||||||
|
and a Factor interpreter written in C. The C interpreter
|
||||||
|
is a more recent development than the Java interpreter.
|
||||||
|
They both share a large body of library code written in
|
||||||
|
Factor.
|
||||||
|
|
||||||
|
Java interpreter
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The Java interpreter includes a slick GUI with hyperlinked
|
||||||
|
inspection of source code, as well as stack effect checking.
|
||||||
|
|
||||||
|
build.xml - Ant buildfile for Java interpreter.
|
||||||
|
factor/ - source code for Factor interpreter written in Java.
|
||||||
|
org/objectweb/asm/ - helper library for Java interpreter.
|
||||||
|
library/platform/jvm - JVM-specific Factor code
|
||||||
|
Factor.jar - compiled, stand-alone Java interpreter
|
||||||
|
|
||||||
|
C interpreter
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The C interpreter is a minimal implementation, with the goal
|
||||||
|
of achieving the highest possible flexibility/lines of code
|
||||||
|
ratio. It runs faster than the Java interpreter, and uses
|
||||||
|
far less memory.
|
||||||
|
|
||||||
|
Makefile - Makefile for building C interpreter.
|
||||||
|
native/ - source code for Factor interpreter written in C.
|
||||||
|
library/platform/native - C interpreter-specific code
|
||||||
|
f - compiled C interpreter - needs image to run
|
||||||
|
boot.image.le - image for x86
|
||||||
|
boot.image.be - image for 32-bit SPARC and 32-bit PowerPC
|
||||||
|
|
||||||
|
Notes on the C interpreter
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
When you run the interpreter with a boot image, it loads a
|
||||||
|
bunch of files and saves a 'factor.image'. Run the
|
||||||
|
interpreter again with this image.
|
||||||
|
|
||||||
|
At the moment it assumes a 32-bit architecture. Your C
|
||||||
|
compiler's types must be as follows:
|
||||||
|
|
||||||
|
short - signed 16 bits
|
||||||
|
long - signed 32 bits
|
||||||
|
long long - signed 64 bits
|
||||||
|
double -IEEE double precision 64-bit float
|
||||||
|
|
||||||
|
Moving to 64-bits would require a few changes in the image
|
||||||
|
cross-compiler, namely in the way it packs strings.
|
||||||
|
|
||||||
|
Not everything has been implemented yet. However, a lot
|
||||||
|
already works. Compare the output of this in the C and
|
||||||
|
Java interpreters to see how they differ:
|
||||||
|
|
||||||
|
"vocabularies" get describe
|
|
@ -0,0 +1,225 @@
|
||||||
|
THE CONCATENATIVE LANGUAGE FACTOR
|
||||||
|
|
||||||
|
* Introduction
|
||||||
|
|
||||||
|
Factor supports various data types; atomic types include numbers of
|
||||||
|
various kinds, strings of characters, and booleans. Compound data types
|
||||||
|
include lists consisting of cons cells, vectors, and string buffers.
|
||||||
|
|
||||||
|
Factor encourages programming in a functional style where new objects
|
||||||
|
are returned and input parameters remain unmodified, but does not
|
||||||
|
enforce this. No manifest type declarations are necessary, and all data
|
||||||
|
types use exactly one slot each on the stack (unlike, say, FORTH).
|
||||||
|
|
||||||
|
The internal representation of a Factor program is a linked list. Linked
|
||||||
|
lists that are to be executed are referred to as ``quotations.'' The
|
||||||
|
interpreter iterates the list, executing words, and pushing all other
|
||||||
|
types of objects on the data stack. A word is a unique data type because
|
||||||
|
it can be executed. Words come in two varieties: primitive and compound.
|
||||||
|
Primitive words have an implementation coded in the host language (C or
|
||||||
|
Java). Compound words are executed by invoking the interpreter
|
||||||
|
recursively on their definition, which is also a linked list.
|
||||||
|
|
||||||
|
* A note about code examples
|
||||||
|
|
||||||
|
Factor words are separated out into multiple ``vocabularies''. Each code
|
||||||
|
example given here is preceeded with a series of declarations, such as
|
||||||
|
the following:
|
||||||
|
|
||||||
|
USE: math
|
||||||
|
USE: streams
|
||||||
|
|
||||||
|
When entering code at the interactive interpreter loop, most
|
||||||
|
vocabularies are already in the search path, and the USE: declarations
|
||||||
|
can be omitted. However, in a source file they must all be specified, by convention at the beginning of the file.
|
||||||
|
|
||||||
|
* Control flow
|
||||||
|
|
||||||
|
Control flow rests on two basic concepts: recursion, and branching.
|
||||||
|
Words with compound definitions may refer to themselves, and there is
|
||||||
|
exactly one primitive for performing conditional execution:
|
||||||
|
|
||||||
|
USE: combinators
|
||||||
|
|
||||||
|
1 10 < [ "1 is less than 10." print ] [ "whoa!" print ] ifte
|
||||||
|
==> 1 is less than 10.
|
||||||
|
|
||||||
|
Here is an example of a word that uses these two concepts:
|
||||||
|
|
||||||
|
: contains? ( element list -- remainder )
|
||||||
|
#! If the proper list contains the element, push the
|
||||||
|
#! remainder of the list, starting from the cell whose car
|
||||||
|
#! is elem. Otherwise push f.
|
||||||
|
dup [
|
||||||
|
2dup car = [ nip ] [ cdr contains? ] ifte
|
||||||
|
] [
|
||||||
|
2drop f
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
An example:
|
||||||
|
|
||||||
|
USE: lists
|
||||||
|
|
||||||
|
3 [ 1 2 3 4 ] contains?
|
||||||
|
==> [ 3 4 ]
|
||||||
|
5 [ 1 2 3 4 ] contains?
|
||||||
|
==> f
|
||||||
|
|
||||||
|
It recurses down the list, until it reaches the end, in which case the
|
||||||
|
outer ifte's 'false' branch is executed.
|
||||||
|
|
||||||
|
A quick overview of the words used here, along with their stack effects:
|
||||||
|
|
||||||
|
Shuffle words:
|
||||||
|
|
||||||
|
dup ( x -- x x )
|
||||||
|
nip ( x y -- y )
|
||||||
|
2dup ( x y -- x y x y )
|
||||||
|
2drop ( x y -- )
|
||||||
|
|
||||||
|
Linked list deconstruction:
|
||||||
|
|
||||||
|
car ( [ x | y ] -- x )
|
||||||
|
cdr ( [ x | y ] -- y ) - push the "tail" of a list.
|
||||||
|
|
||||||
|
Equality:
|
||||||
|
|
||||||
|
= ( x y -- ? )
|
||||||
|
|
||||||
|
More complicated control flow constructs, such as loops and higher order
|
||||||
|
functions, are usually built with the help of another primitive that
|
||||||
|
simply executes a quotation at the top of the stack, removing it from
|
||||||
|
the stack:
|
||||||
|
|
||||||
|
USE: math
|
||||||
|
USE: prettyprint
|
||||||
|
|
||||||
|
[ 2 2 + . ] call
|
||||||
|
==> 4
|
||||||
|
|
||||||
|
Here is an example of a word that applies a quotation to each element of
|
||||||
|
a list. Note that it uses 'call' to execute the given quotation:
|
||||||
|
|
||||||
|
: each ( list quotation -- )
|
||||||
|
#! Push each element of a proper list in turn, and apply a
|
||||||
|
#! quotation to each element.
|
||||||
|
#!
|
||||||
|
#! In order to compile, the quotation must consume one more
|
||||||
|
#! value than it produces.
|
||||||
|
over [
|
||||||
|
>r uncons r> tuck >r >r call r> r> each
|
||||||
|
] [
|
||||||
|
2drop
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
An example:
|
||||||
|
|
||||||
|
USE: lists
|
||||||
|
USE: math
|
||||||
|
USE: stack
|
||||||
|
|
||||||
|
[ 1 2 3 4 ] [ dup * . ] each
|
||||||
|
==> 1
|
||||||
|
4
|
||||||
|
9
|
||||||
|
16
|
||||||
|
|
||||||
|
A quick overview of the words used here:
|
||||||
|
|
||||||
|
Printing top of stack:
|
||||||
|
|
||||||
|
. ( x -- ) print top of stack in a form that is valid Factor syntax.
|
||||||
|
|
||||||
|
Shuffle words:
|
||||||
|
|
||||||
|
over ( x y -- x y x )
|
||||||
|
tuck ( x y -- y x y )
|
||||||
|
>r ( x -- r:x ) - move top of data stack to/from 'extra hand'.
|
||||||
|
r> ( r:x -- x )
|
||||||
|
|
||||||
|
Writing >r foo r> is analogous to '[ foo ] dip' in Joy. Occurrences of
|
||||||
|
>r and r> must be balanced within a single word definition.
|
||||||
|
|
||||||
|
Linked list deconstruction:
|
||||||
|
|
||||||
|
uncons ( [ x | y ] -- x y )
|
||||||
|
|
||||||
|
* Variables
|
||||||
|
|
||||||
|
Factor supports a notion of ``variables''. Whereas the stack is used for
|
||||||
|
transient, intermediate values, variables are used for more permanent
|
||||||
|
data.
|
||||||
|
|
||||||
|
Variables are retreived and stored using the 'get' and 'set' words. For
|
||||||
|
example:
|
||||||
|
|
||||||
|
USE: math
|
||||||
|
USE: namespaces
|
||||||
|
USE: prettyprint
|
||||||
|
|
||||||
|
"~" get .
|
||||||
|
==> "/home/slava"
|
||||||
|
|
||||||
|
5 "x" set
|
||||||
|
"x" get 2 * .
|
||||||
|
==> 10
|
||||||
|
|
||||||
|
The set of available variables is determined using ``dynamic scope''.
|
||||||
|
A ``namespace'' is a set of variable name/value pairs. Namespaces can be
|
||||||
|
pushed onto the ``name stack'', and later popped. The 'get' word
|
||||||
|
searches all namespaces on the namestack in turn. The 'set' word stores
|
||||||
|
a variable value into the namespace at the top of the name stack.
|
||||||
|
|
||||||
|
While it is possible to push/pop the namestack directly using the words
|
||||||
|
>n and n>, most of the time using the 'bind' combinator is more
|
||||||
|
desirable.
|
||||||
|
|
||||||
|
Good examples of namespace use are found in the I/O system.
|
||||||
|
|
||||||
|
Factor provides two sets of words for working with I/O streams: words
|
||||||
|
whose stream operand is specified on the stack (freadln, fwrite,
|
||||||
|
fprint...) and words that use the standard input/output stream (read,
|
||||||
|
write, print...).
|
||||||
|
|
||||||
|
An I/O stream is a namespace with a slot for each I/O operation. I/O
|
||||||
|
operations taking an explicit stream operand are all defined as follows:
|
||||||
|
|
||||||
|
: freadln ( stream -- string )
|
||||||
|
[ "freadln" get call ] bind ;
|
||||||
|
|
||||||
|
: fwrite ( string stream -- )
|
||||||
|
[ "fwrite" get call ] bind ;
|
||||||
|
|
||||||
|
: fclose ( stream -- )
|
||||||
|
[ "fclose" get call ] bind ;
|
||||||
|
|
||||||
|
( ... et cetera )
|
||||||
|
|
||||||
|
The second set of I/O operations, whose stream is the implicit 'standard
|
||||||
|
input/output' stream, are defined as follows:
|
||||||
|
|
||||||
|
: read ( -- string )
|
||||||
|
"stdio" get freadln ;
|
||||||
|
|
||||||
|
: write ( string -- )
|
||||||
|
"stdio" get fwrite ;
|
||||||
|
|
||||||
|
( ... et cetera )
|
||||||
|
|
||||||
|
In the global namespace, the 'stdio' variable corresponds to a stream
|
||||||
|
whose operations read/write from the standard file descriptors 0 and 1.
|
||||||
|
|
||||||
|
However, the 'with-stream' combinator provides a way to rebind the
|
||||||
|
standard input/output stream for the duration of the execution of a
|
||||||
|
single quotation. The following example writes the source of a word
|
||||||
|
definition to a file named 'definition.txt':
|
||||||
|
|
||||||
|
USE: prettyprint
|
||||||
|
USE: streams
|
||||||
|
|
||||||
|
"definition.txt" <filebw> [ "with-stream" see ] with-stream
|
||||||
|
|
||||||
|
The 'with-stream' word is implemented by pushing a new namespace on the
|
||||||
|
namestack, setting the 'stdio' variable therein, and execution the given
|
||||||
|
quotation.
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
FFI:
|
||||||
|
- is signed -vs- unsigned pointers an issue?
|
||||||
|
|
||||||
|
- command line parsing cleanup
|
||||||
|
|
||||||
|
- BIN: 2: bad
|
||||||
|
|
||||||
|
- compile word twice; no more 'cannot compile' error!
|
||||||
|
- doc comments in assoc, image, inferior
|
||||||
|
- styles - could use some cleanup
|
||||||
|
- list - trim down
|
||||||
|
- move quadratic and simpson to contrib
|
||||||
|
- init-assembler called twice
|
||||||
|
- compiler: drop literal peephole optimization
|
||||||
|
- compiling when*
|
||||||
|
- compiling unless*
|
||||||
|
- getenv/setenv: if literal arg, compile as a load/store
|
||||||
|
- inline words
|
||||||
|
|
||||||
|
[error] SideKick$BufferChangeHandler: We have cplusplus.xml (/home/slava/jEdit/modes/) but got event for DefaultInputHandler.java (/home/slava/jEdit/org/gjt/sp/jedit/gui/)
|
||||||
|
[error] SideKick$BufferChangeHandler: We have cplusplus.xml (/home/slava/jEdit/modes/) but got event for DefaultInputHandler.java (/home/slava/jEdit/org/gjt/sp/jedit/gui/)
|
||||||
|
|
||||||
|
[error] AWT-EventQueue-0: java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 98
|
||||||
|
[error] AWT-EventQueue-0: at org.gjt.sp.jedit.Buffer.getLineOfOffset(Buffer.java:882)
|
||||||
|
[error] AWT-EventQueue-0: at errorlist.DefaultErrorSource$DefaultError.getLineNumber(Unknown Source)
|
||||||
|
[error] AWT-EventQueue-0: at errorlist.DefaultErrorSource.getLineErrors(Unknown Source)
|
||||||
|
[error] AWT-EventQueue-0: at errorlist.ErrorOverview.paintComponent(Unknown Source)
|
||||||
|
|
||||||
|
[error] EditBus: Exception while sending message on EditBus:
|
||||||
|
[error] EditBus: java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 98
|
||||||
|
[error] EditBus: at org.gjt.sp.jedit.Buffer.getLineOfOffset(Buffer.java:882)
|
||||||
|
[error] EditBus: at errorlist.DefaultErrorSource$DefaultError.getLineNumber(Unknown Source)
|
||||||
|
[error] EditBus: at errorlist.ErrorList$ErrorCellRenderer.getTreeCellRendererComponent(Unknown Source)
|
||||||
|
[error] EditBus: at javax.swing.plaf.basic.BasicTreeUI$NodeDimensionsHandler.getNodeDimensions(BasicTreeUI.java:2751)
|
||||||
|
|
||||||
|
[error] EditBus: Exception while sending message on EditBus:
|
||||||
|
[error] EditBus: java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 98
|
||||||
|
[error] EditBus: at org.gjt.sp.jedit.Buffer.getLineOfOffset(Buffer.java:882)
|
||||||
|
[error] EditBus: at errorlist.DefaultErrorSource$DefaultError.getLineNumber(Unknown Source)
|
||||||
|
[error] EditBus: at errorlist.ErrorList$ErrorCellRenderer.getTreeCellRendererComponent(Unknown Source)
|
||||||
|
[error] EditBus: at javax.swing.plaf.basic.BasicTreeUI$NodeDimensionsHandler.getNodeDimensions(BasicTreeUI.java:2751)
|
||||||
|
[error] EditBus: at javax.swing.tree.AbstractLayoutCache.getNodeDimensions(AbstractLayoutCache.java:475)
|
||||||
|
|
||||||
|
- perhaps /i should work with all numbers
|
||||||
|
- profiler is inaccurate: wrong word on cs
|
||||||
|
- buffer change handler in sidekick is screwed
|
||||||
|
- dec> bin> oct> hex> throw errors
|
||||||
|
- parse-number doesn't
|
||||||
|
- eval with multilien strings and #!
|
||||||
|
- quit responder breaks with multithreading
|
||||||
|
- nicer way to combine two paths
|
||||||
|
- don't show listener on certain commands
|
||||||
|
- plugin should not exit jEdit on fatal errors
|
||||||
|
- wordpreview: don't show for string literals and comments
|
||||||
|
- alist -vs- assoc terminology
|
||||||
|
- file responder: don't show full path in title
|
||||||
|
|
||||||
|
- clean up listener's action popups
|
||||||
|
- jedit ==> jedit-word, jedit takes a file name
|
||||||
|
- add a socket timeout
|
||||||
|
- fix error postoning -- not all errors thrown by i/o code are
|
||||||
|
postponed
|
||||||
|
- some way to run httpd from command line
|
||||||
|
|
||||||
|
+ bignums:
|
||||||
|
|
||||||
|
- move some s48_ functions into bignum.c
|
||||||
|
- remove unused functions
|
||||||
|
|
||||||
|
- >lower, >upper for strings
|
||||||
|
- accept multi-line input in listener
|
||||||
|
|
||||||
|
+ docs:
|
||||||
|
|
||||||
|
- explain how log uses >polar and rect>
|
||||||
|
- when* unless*
|
||||||
|
- simple i/o section
|
||||||
|
- unparse examples, and difference from prettyprint
|
||||||
|
- review doc formatting with latex2html
|
||||||
|
- recursion -vs- iteration in vectors chapter, and combinator
|
||||||
|
construction
|
||||||
|
- [, , ,] -- mention that , are usually in nested words
|
||||||
|
- finish namespaces docs
|
||||||
|
- mention word accessors/mutators
|
||||||
|
- to document:
|
||||||
|
continuations
|
||||||
|
streams
|
||||||
|
multitasking
|
||||||
|
unit testing
|
||||||
|
|
||||||
|
+ tests:
|
||||||
|
|
||||||
|
- java factor: equal numbers have non-equal hashcodes!
|
||||||
|
- FactorLib.equal() not very good
|
||||||
|
- investigate mandel.factor
|
||||||
|
|
||||||
|
+ listener/plugin:
|
||||||
|
|
||||||
|
- NPE in ErrorHighlight
|
||||||
|
- some way to not have previous definitions from a source file
|
||||||
|
clutter the namespace
|
||||||
|
- use inferior.factor for everything not just listener
|
||||||
|
- fedit broken with listener
|
||||||
|
- maple-like: press enter at old commands to evaluate there
|
||||||
|
- input style after clicking link
|
||||||
|
- listener backspace overzealous
|
||||||
|
- completion in the listener
|
||||||
|
- special completion for USE:/IN:
|
||||||
|
- inspector links when describe called without object path
|
||||||
|
|
||||||
|
+ native:
|
||||||
|
|
||||||
|
- better i/o scheduler
|
||||||
|
|
||||||
|
+ JVM compiler:
|
||||||
|
|
||||||
|
- compiled stack traces broken
|
||||||
|
- save classes to disk
|
||||||
|
- tail call optimization broken again
|
||||||
|
- don't compile inline words
|
||||||
|
- recursive words with code after ifte
|
||||||
|
- less unnecessary args to auxiliary methods
|
||||||
|
- inlining tail-recursive immediates
|
||||||
|
- direct stack access leaks memory on stack
|
||||||
|
- unnecessary local allocation: max is instance var, but several methods
|
||||||
|
get compiled.
|
||||||
|
|
||||||
|
+ misc:
|
||||||
|
|
||||||
|
- don't rehash strings on every startup
|
||||||
|
- 'cascading' styles
|
||||||
|
- ditch expand
|
||||||
|
|
||||||
|
+ httpd:
|
||||||
|
|
||||||
|
- wiki responder:
|
||||||
|
- port to native
|
||||||
|
- text styles
|
||||||
|
- log with date
|
||||||
|
basic authentication, using httpdAuth function from a config file
|
||||||
|
- file responder; last-modified field
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<!DOCTYPE ACTIONS SYSTEM "actions.dtd">
|
||||||
|
|
||||||
|
<ACTIONS>
|
||||||
|
<ACTION NAME="factor-eval-selection">
|
||||||
|
<CODE>
|
||||||
|
sel = textArea.selectedText;
|
||||||
|
if(sel == null)
|
||||||
|
view.toolkit.beep();
|
||||||
|
else
|
||||||
|
FactorPlugin.eval(view,sel);
|
||||||
|
</CODE>
|
||||||
|
</ACTION>
|
||||||
|
<ACTION NAME="factor-run-file">
|
||||||
|
<CODE>
|
||||||
|
buffer.save(view,null);
|
||||||
|
VFSManager.waitForRequests();
|
||||||
|
FactorPlugin.eval(view,
|
||||||
|
"\""
|
||||||
|
+ FactorReader.charsToEscapes(buffer.path)
|
||||||
|
+ "\" run-file");
|
||||||
|
</CODE>
|
||||||
|
</ACTION>
|
||||||
|
<ACTION NAME="factor-apropos">
|
||||||
|
<CODE>
|
||||||
|
word = FactorPlugin.getWordAtCaret(textArea);
|
||||||
|
if(word == null)
|
||||||
|
view.toolkit.beep();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FactorPlugin.eval(view,
|
||||||
|
"\""
|
||||||
|
+ FactorReader.charsToEscapes(word)
|
||||||
|
+ "\" apropos.");
|
||||||
|
}
|
||||||
|
</CODE>
|
||||||
|
</ACTION>
|
||||||
|
<ACTION NAME="factor-see">
|
||||||
|
<CODE>
|
||||||
|
FactorPlugin.factorWordOperation(view,"see");
|
||||||
|
</CODE>
|
||||||
|
</ACTION>
|
||||||
|
<ACTION NAME="factor-edit">
|
||||||
|
<CODE>
|
||||||
|
FactorPlugin.factorWordOperation(view,"jedit");
|
||||||
|
</CODE>
|
||||||
|
</ACTION>
|
||||||
|
<ACTION NAME="factor-edit-dialog">
|
||||||
|
<CODE>
|
||||||
|
new EditWordDialog(view,FactorPlugin
|
||||||
|
.getSideKickParser());
|
||||||
|
</CODE>
|
||||||
|
</ACTION>
|
||||||
|
<ACTION NAME="factor-usages">
|
||||||
|
<CODE>
|
||||||
|
FactorPlugin.factorWordOperation(view,"usages.");
|
||||||
|
</CODE>
|
||||||
|
</ACTION>
|
||||||
|
<ACTION NAME="factor-insert-use">
|
||||||
|
<CODE>
|
||||||
|
word = FactorPlugin.getWordAtCaret(textArea);
|
||||||
|
if(word == null)
|
||||||
|
view.toolkit.beep();
|
||||||
|
else
|
||||||
|
FactorPlugin.insertUseDialog(view,word);
|
||||||
|
</CODE>
|
||||||
|
</ACTION>
|
||||||
|
<ACTION NAME="factor-extract-word">
|
||||||
|
<CODE>
|
||||||
|
FactorPlugin.extractWord(view);
|
||||||
|
</CODE>
|
||||||
|
</ACTION>
|
||||||
|
</ACTIONS>
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<project name=" Factor" default="dist" basedir=".">
|
||||||
|
|
||||||
|
<target name="init">
|
||||||
|
<available property="jedit" classname="org.gjt.sp.jedit.jEdit" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="compile" depends="init">
|
||||||
|
<javac
|
||||||
|
srcdir="."
|
||||||
|
destdir="."
|
||||||
|
deprecation="on"
|
||||||
|
includeJavaRuntime="yes"
|
||||||
|
debug="true"
|
||||||
|
optimize="true"
|
||||||
|
>
|
||||||
|
<include name="**/*.java"/>
|
||||||
|
<exclude name="factor/jedit/*.java"/>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<path id="jedit-classpath">
|
||||||
|
<pathelement location="${user.home}/.jedit/jars/ErrorList.jar" />
|
||||||
|
<pathelement location="${user.home}/.jedit/jars/SideKick.jar" />
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<target name="compile-jedit" depends="init" if="jedit">
|
||||||
|
<javac
|
||||||
|
classpathref="jedit-classpath"
|
||||||
|
debug="true"
|
||||||
|
deprecation="on"
|
||||||
|
destdir="."
|
||||||
|
optimize="true"
|
||||||
|
srcdir=".">
|
||||||
|
<include name="factor/jedit/*.java"/>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="dist" depends="compile,compile-jedit">
|
||||||
|
<jar
|
||||||
|
jarfile="Factor.jar"
|
||||||
|
manifest="Factor.manifest"
|
||||||
|
compress="true"
|
||||||
|
>
|
||||||
|
<fileset dir=".">
|
||||||
|
<include name="factor/*.class"/>
|
||||||
|
<include name="factor/**/*.class"/>
|
||||||
|
<include name="factor/**/*.props"/>
|
||||||
|
<include name="factor/**/*.bsh"/>
|
||||||
|
<include name="factor/**/*.txt"/>
|
||||||
|
<include name="*.xml"/>
|
||||||
|
<include name="library/**/*.factor"/>
|
||||||
|
<include name="library/**/*.txt"/>
|
||||||
|
<include name="library/**/*.png"/>
|
||||||
|
<include name="org/**/*.class"/>
|
||||||
|
<include name="*.factor"/>
|
||||||
|
<include name="doc/**/*.html"/>
|
||||||
|
<include name="doc/**/*.png"/>
|
||||||
|
<include name="doc/*.html"/>
|
||||||
|
<include name="Factor.manifest"/>
|
||||||
|
</fileset>
|
||||||
|
</jar>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="dist-jedit" depends="dist">
|
||||||
|
<copy file="Factor.jar" tofile="../Factor.jar" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="clean" description="Clean old stuff.">
|
||||||
|
<delete>
|
||||||
|
<fileset dir="." includes="**/*.class"/>
|
||||||
|
<fileset dir="." includes="**/*~" defaultexcludes="no"/>
|
||||||
|
<fileset dir="." includes="**/#*#" defaultexcludes="no"/>
|
||||||
|
<fileset dir="." includes="**/*.rej"/>
|
||||||
|
<fileset dir="." includes="**/*.orig"/>
|
||||||
|
<fileset dir="." includes="**/.*.swp"/>
|
||||||
|
<fileset dir="." includes="**/.#*"/>
|
||||||
|
<fileset dir="." includes="**/.new*"/>
|
||||||
|
<fileset dir="." includes="**/.directory"/>
|
||||||
|
</delete>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="docs" description="Build PDF and HTML docs.">
|
||||||
|
<delete>
|
||||||
|
<fileset dir="." includes="doc/devel-guide/*.html"/>
|
||||||
|
<fileset dir="." includes="doc/devel-guide.aux"/>
|
||||||
|
<fileset dir="." includes="doc/devel-guide.log"/>
|
||||||
|
<fileset dir="." includes="doc/devel-guide.pdf"/>
|
||||||
|
</delete>
|
||||||
|
<exec executable="latex" dir="doc">
|
||||||
|
<arg value="devel-guide.tex" />
|
||||||
|
</exec>
|
||||||
|
<exec executable="latex2html" dir="doc">
|
||||||
|
<arg value="-local_icons"/>
|
||||||
|
<arg value="devel-guide.tex" />
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
</project>
|
|
@ -0,0 +1,236 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
!
|
||||||
|
! An Smalltalk-link browser that runs in the httpd server using
|
||||||
|
! cont-responder facilities.
|
||||||
|
!
|
||||||
|
IN: browser
|
||||||
|
USE: cont-html
|
||||||
|
USE: cont-responder
|
||||||
|
USE: cont-utils
|
||||||
|
USE: stack
|
||||||
|
USE: stdio
|
||||||
|
USE: combinators
|
||||||
|
USE: namespaces
|
||||||
|
USE: words
|
||||||
|
USE: lists
|
||||||
|
USE: streams
|
||||||
|
USE: strings
|
||||||
|
USE: inspector
|
||||||
|
USE: kernel
|
||||||
|
USE: prettyprint
|
||||||
|
USE: words
|
||||||
|
USE: html
|
||||||
|
USE: parser
|
||||||
|
USE: errors
|
||||||
|
USE: unparser
|
||||||
|
USE: logging
|
||||||
|
|
||||||
|
: <browser> ( allow-edit? vocab word -- )
|
||||||
|
#! An object for storing the current browser
|
||||||
|
#! user interface state.
|
||||||
|
<namespace> [
|
||||||
|
"current-word" set
|
||||||
|
"current-vocab" set
|
||||||
|
"allow-edit?" set
|
||||||
|
] extend ;
|
||||||
|
|
||||||
|
: write-vocab-list ( -- )
|
||||||
|
#! Write out the HTML for the list of vocabularies
|
||||||
|
<select name= "vocabs" style= "width: 200" size= "20" onchange= "document.forms.main.submit()" select>
|
||||||
|
vocabs [
|
||||||
|
dup "current-vocab" get [ "" ] unless* = [
|
||||||
|
"<option selected>" write
|
||||||
|
] [
|
||||||
|
"<option>" write
|
||||||
|
] ifte
|
||||||
|
chars>entities write
|
||||||
|
"</option>\n" write
|
||||||
|
] each
|
||||||
|
</select> ;
|
||||||
|
|
||||||
|
: write-word-list ( vocab -- )
|
||||||
|
#! Write out the HTML for the list of words in a vocabulary.
|
||||||
|
<select name= "words" style= "width: 200" size= "20" onchange= "document.forms.main.submit()" select>
|
||||||
|
words [
|
||||||
|
word-name dup "current-word" get [ "" ] unless* str-compare 0 = [
|
||||||
|
"<option selected>" write
|
||||||
|
] [
|
||||||
|
"<option>" write
|
||||||
|
] ifte
|
||||||
|
chars>entities write
|
||||||
|
"</option>\n" write
|
||||||
|
] each
|
||||||
|
</select> ;
|
||||||
|
|
||||||
|
: write-editable-word-source ( vocab word -- )
|
||||||
|
#! Write the source in a manner allowing it to be edited.
|
||||||
|
<textarea name= "eval" rows= "30" cols= "80" textarea>
|
||||||
|
1024 <string-output-stream> dup >r [
|
||||||
|
>r words r> swap [ over swap dup word-name rot = [ see ] [ drop ] ifte ] each drop
|
||||||
|
] with-stream r> stream>str chars>entities write
|
||||||
|
</textarea> <br/>
|
||||||
|
"Accept" button ;
|
||||||
|
|
||||||
|
: write-word-source ( vocab word -- )
|
||||||
|
#! Write the source for the given word from the vocab as HTML.
|
||||||
|
<namespace> [
|
||||||
|
"responder" "inspect" put
|
||||||
|
"allow-edit?" get [ "Edit" [ "edit-state" t put ] quot-href <br/> ] when
|
||||||
|
"edit-state" get [
|
||||||
|
write-editable-word-source
|
||||||
|
] [
|
||||||
|
[
|
||||||
|
>r words r> swap [ over swap dup word-name rot = [ see ] [ drop ] ifte ] each drop
|
||||||
|
] with-simple-html-output
|
||||||
|
] ifte
|
||||||
|
] bind drop ;
|
||||||
|
|
||||||
|
: write-vm-statistics ( -- )
|
||||||
|
#! Display statistics about the JVM in use.
|
||||||
|
room swap unparse >r unparse r>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td> "Free Memory" write </td>
|
||||||
|
<td> write </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> "Total Memory" write </td>
|
||||||
|
<td> write </td>
|
||||||
|
</tr>
|
||||||
|
</table> ;
|
||||||
|
|
||||||
|
: write-browser-body ( -- )
|
||||||
|
#! Write out the HTML for the body of the main browser page.
|
||||||
|
<table width= "100%" table>
|
||||||
|
<tr>
|
||||||
|
<td> "<b>Vocabularies</b>" write </td>
|
||||||
|
<td> "<b>Words</b>" write </td>
|
||||||
|
<td> "<b>Source</b>" write </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td valign= "top" style= "width: 200" td> write-vocab-list </td>
|
||||||
|
<td valign= "top" style= "width: 200" td> "current-vocab" get write-word-list </td>
|
||||||
|
<td valign= "top" td> "current-vocab" get "current-word" get write-word-source </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
write-vm-statistics ;
|
||||||
|
|
||||||
|
: flatten ( tree - list )
|
||||||
|
#! Flatten a tree into a list.
|
||||||
|
dup f = [
|
||||||
|
] [
|
||||||
|
dup cons? [
|
||||||
|
dup car flatten swap cdr flatten append
|
||||||
|
] [
|
||||||
|
[ ] cons
|
||||||
|
] ifte
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: word-uses ( word -- list )
|
||||||
|
#! Return a list of vocabularies that the given word uses.
|
||||||
|
word-parameter flatten [ word? ] subset [
|
||||||
|
word-vocabulary
|
||||||
|
] map ;
|
||||||
|
|
||||||
|
: vocabulary-uses ( vocab -- list )
|
||||||
|
#! Return a list of vocabularies that all words in a vocabulary
|
||||||
|
#! uses.
|
||||||
|
<namespace> [
|
||||||
|
"result" f put
|
||||||
|
words [
|
||||||
|
word-uses [
|
||||||
|
"result" unique@
|
||||||
|
] each
|
||||||
|
] each
|
||||||
|
"result" get
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: build-eval-string ( vocab to-eval -- string )
|
||||||
|
#! Build a string that can evaluate the string 'to-eval'
|
||||||
|
#! by first doing an 'IN: vocab' and a 'USE:' of all
|
||||||
|
#! necessary vocabs for existing words in that vocab.
|
||||||
|
<% >r "IN: " % dup % "\n" %
|
||||||
|
vocabulary-uses [ "USE: " % % "\n" % ] each
|
||||||
|
r> % "\n" % %> ;
|
||||||
|
|
||||||
|
: show-parse-error ( error -- )
|
||||||
|
#! Show an error page describing the parse error.
|
||||||
|
[
|
||||||
|
<html>
|
||||||
|
<head> <title> "Parse error" write </title> </head>
|
||||||
|
<body>
|
||||||
|
swap [ write ] with-simple-html-output
|
||||||
|
<a href= a> "Ok" write </a>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
] show drop drop ;
|
||||||
|
|
||||||
|
: eval-string ( vocab to-eval -- )
|
||||||
|
#! Evaluate the 'to-eval' within the given vocabulary.
|
||||||
|
build-eval-string [
|
||||||
|
parse call
|
||||||
|
] [
|
||||||
|
[
|
||||||
|
show-parse-error
|
||||||
|
drop
|
||||||
|
] when*
|
||||||
|
] catch ;
|
||||||
|
|
||||||
|
: browse ( <browser> -- )
|
||||||
|
#! Display a Smalltalk like browser for exploring/modifying words.
|
||||||
|
[
|
||||||
|
[
|
||||||
|
[
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> "Factor Browser" write </title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form name= "main" action= method= "post" form>
|
||||||
|
write-browser-body
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
] show [
|
||||||
|
"allow-edit?" get [
|
||||||
|
"eval" get [
|
||||||
|
"eval" f put
|
||||||
|
"Editing has been disabled." show-message-page
|
||||||
|
] when
|
||||||
|
] unless
|
||||||
|
"allow-edit?" get "allow-edit?" set
|
||||||
|
] extend
|
||||||
|
] bind [
|
||||||
|
"allow-edit?" get
|
||||||
|
"vocabs" get
|
||||||
|
"words" get
|
||||||
|
"eval" get dup [ "vocabs" get swap eval-string ] [ drop ] ifte
|
||||||
|
] bind <browser>
|
||||||
|
] forever ;
|
||||||
|
|
||||||
|
: browser-responder ( allow-edit? -- )
|
||||||
|
#! Start the Smalltalk-like browser.
|
||||||
|
"browser" f <browser> browse ;
|
||||||
|
|
||||||
|
"browser" [ f browser-responder ] install-cont-responder
|
|
@ -0,0 +1,123 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
!
|
||||||
|
! Simple test applications
|
||||||
|
IN: cont-examples
|
||||||
|
USE: cont-responder
|
||||||
|
USE: cont-html
|
||||||
|
USE: stack
|
||||||
|
USE: stdio
|
||||||
|
USE: html
|
||||||
|
USE: lists
|
||||||
|
USE: strings
|
||||||
|
USE: math
|
||||||
|
USE: namespaces
|
||||||
|
USE: prettyprint
|
||||||
|
USE: unparser
|
||||||
|
|
||||||
|
: display-page ( title -- )
|
||||||
|
#! Display a page with some text to test the cont-responder.
|
||||||
|
#! The page has a link to the 'next' continuation.
|
||||||
|
[
|
||||||
|
swap [
|
||||||
|
<a href= a> "Next" write </a>
|
||||||
|
] html-document
|
||||||
|
] show drop drop ;
|
||||||
|
|
||||||
|
: display-get-name-page ( -- name )
|
||||||
|
#! Display a page prompting for input of a name and return that name.
|
||||||
|
[
|
||||||
|
"Enter your name" [
|
||||||
|
<form method= "post" action= form>
|
||||||
|
"Name: " write
|
||||||
|
<input type= "text" name= "name" size= "20" input/>
|
||||||
|
<input type= "submit" value= "Ok" input/>
|
||||||
|
</form>
|
||||||
|
] html-document
|
||||||
|
] show [
|
||||||
|
"name" get
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: test-cont-responder ( - )
|
||||||
|
#! Test the cont-responder responder by displaying a few pages in a row.
|
||||||
|
"Page one" display-page
|
||||||
|
"Hello " display-get-name-page cat2 display-page
|
||||||
|
"Page three" display-page ;
|
||||||
|
|
||||||
|
: test-cont-responder2 ( - )
|
||||||
|
#! Test the cont-responder responder by displaying a few pages in a loop.
|
||||||
|
[ "one" "two" "three" "four" ] [ display-page [ .s ] with-string-stream display-page ] each
|
||||||
|
"Done!" display-page ;
|
||||||
|
|
||||||
|
: test-cont-responder3 ( - )
|
||||||
|
#! Test the quot-href word by displaying a menu of the current
|
||||||
|
#! test words. Note that we drop the 'url' argument to the show
|
||||||
|
#! quotation as we don't link to a 'next' page.
|
||||||
|
[
|
||||||
|
drop
|
||||||
|
"Menu" [
|
||||||
|
<ol>
|
||||||
|
<li> "Test responder1" [ test-cont-responder ] quot-href </li>
|
||||||
|
<li> "Test responder2" [ [ .s ] with-string-stream display-page test-cont-responder2 [ .s ] with-string-stream display-page ] quot-href </li>
|
||||||
|
</ol>
|
||||||
|
] html-document
|
||||||
|
] show drop ;
|
||||||
|
|
||||||
|
: counter-example ( count - )
|
||||||
|
#! Display a counter which can be incremented or decremented
|
||||||
|
#! using anchors.
|
||||||
|
#!
|
||||||
|
#! Don't need the original alist
|
||||||
|
[
|
||||||
|
#! And we don't need the 'url' argument
|
||||||
|
drop
|
||||||
|
"Counter: " over unparse cat2 [
|
||||||
|
dup <h2> unparse write </h2>
|
||||||
|
"++" over unit [ f ] swap append [ 1 + counter-example ] append quot-href
|
||||||
|
"--" over unit [ f ] swap append [ 1 - counter-example ] append quot-href
|
||||||
|
drop
|
||||||
|
] html-document
|
||||||
|
] show drop ;
|
||||||
|
|
||||||
|
: counter-example2 ( - )
|
||||||
|
#! Display a counter which can be incremented or decremented
|
||||||
|
#! using anchors.
|
||||||
|
#!
|
||||||
|
0 "counter" set
|
||||||
|
[
|
||||||
|
#! We don't need the 'url' argument
|
||||||
|
drop
|
||||||
|
"Counter: " "counter" get unparse cat2 [
|
||||||
|
<h2> "counter" get unparse write </h2>
|
||||||
|
"++" [ "counter" get 1 + "counter" set ] quot-href
|
||||||
|
"--" [ "counter" get 1 - "counter" set ] quot-href
|
||||||
|
] html-document
|
||||||
|
] show
|
||||||
|
drop ;
|
||||||
|
|
||||||
|
! Install the examples
|
||||||
|
"counter1" [ drop 0 counter-example ] install-cont-responder
|
||||||
|
"counter2" [ drop counter-example2 ] install-cont-responder
|
||||||
|
"test1" [ drop test-cont-responder ] install-cont-responder
|
||||||
|
"test2" [ drop test-cont-responder2 ] install-cont-responder
|
||||||
|
"test3" [ drop test-cont-responder3 ] install-cont-responder
|
|
@ -0,0 +1,217 @@
|
||||||
|
! cont-html v0.6
|
||||||
|
!
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
IN: cont-html
|
||||||
|
USE: strings
|
||||||
|
USE: lists
|
||||||
|
USE: format
|
||||||
|
USE: stack
|
||||||
|
USE: combinators
|
||||||
|
USE: stdio
|
||||||
|
USE: namespaces
|
||||||
|
USE: words
|
||||||
|
USE: logic
|
||||||
|
|
||||||
|
! These words in cont-html are used to provide a means of writing
|
||||||
|
! formatted HTML to standard output with a familiar 'html' look
|
||||||
|
! and feel in the code.
|
||||||
|
!
|
||||||
|
! HTML tags can be used in a number of different ways. The highest
|
||||||
|
! level involves a similar syntax to HTML:
|
||||||
|
!
|
||||||
|
! <p> "someoutput" write </p>
|
||||||
|
!
|
||||||
|
! <p> will outupt the opening tag and </p> will output the closing
|
||||||
|
! tag with no attributes.
|
||||||
|
!
|
||||||
|
! <p class= "red" p> "someoutput" write </p>
|
||||||
|
!
|
||||||
|
! This time the opening tag does not have the '>'. It pushes
|
||||||
|
! a namespace on the stack to hold the attributes and values.
|
||||||
|
! Any attribute words used will store the attribute and values
|
||||||
|
! in that namespace. After the attribute word should come the
|
||||||
|
! value of that attribute. The next attribute word or
|
||||||
|
! finishing word (which is the html word followed by '>')
|
||||||
|
! will actually set the attribute to that value in the namespace.
|
||||||
|
! The finishing word will print out the operning tag including
|
||||||
|
! attributes.
|
||||||
|
! Any writes after this will appear after the opening tag.
|
||||||
|
!
|
||||||
|
! Values for attributes can be used directly without any stack
|
||||||
|
! operations:
|
||||||
|
!
|
||||||
|
! (url -- )
|
||||||
|
! <a href= a> "Click me" write </a>
|
||||||
|
!
|
||||||
|
! (url -- )
|
||||||
|
! <a href= "http://" swap cat2 a> "click" write </a>
|
||||||
|
!
|
||||||
|
! (url -- )
|
||||||
|
! <a href= <% "http://" % % %> a> "click" write </a>
|
||||||
|
!
|
||||||
|
! Tags that have no 'closing' equivalent have a trailing tag/> form:
|
||||||
|
!
|
||||||
|
! <input type= "text" name= "name" size= "20" input/>
|
||||||
|
|
||||||
|
: attrs>string ( alist -- string )
|
||||||
|
#! Convert the attrs alist to a string
|
||||||
|
#! suitable for embedding in an html tag.
|
||||||
|
nreverse <% [ dup car % "='" % cdr % "'" % ] each %> ;
|
||||||
|
|
||||||
|
: write-attributes ( n: namespace -- )
|
||||||
|
#! With the attribute namespace on the stack, get the attributes
|
||||||
|
#! and write them to standard output. If no attributes exist, write
|
||||||
|
#! nothing.
|
||||||
|
"attrs" get [ " " write attrs>string write ] when* ;
|
||||||
|
|
||||||
|
: store-prev-attribute ( n: tag value -- )
|
||||||
|
#! Assumes an attribute namespace is on the stack.
|
||||||
|
#! Gets the previous attribute that was used (if any)
|
||||||
|
#! and sets it's value to the current value on the stack.
|
||||||
|
#! If there is no previous attribute, no value is expected
|
||||||
|
#! on the stack.
|
||||||
|
"current-attribute" get [ swons "attrs" cons@ ] when* ;
|
||||||
|
|
||||||
|
! HTML tag words
|
||||||
|
!
|
||||||
|
! Each closable HTML tag has four words defined. The example below is for
|
||||||
|
! <p>:
|
||||||
|
!
|
||||||
|
! : <p> ( -- )
|
||||||
|
! #! Writes the opening tag to standard output.
|
||||||
|
! "<p>" write ;
|
||||||
|
|
||||||
|
! : <p ( -- n: <namespace> )
|
||||||
|
! #! Used for setting inline attributes. Prints out
|
||||||
|
! #! an unclosed opening tag.
|
||||||
|
! "<p" write <namespace> >n ;
|
||||||
|
!
|
||||||
|
! : p> ( n: <namespace> -- )
|
||||||
|
! #! Used to close off inline attribute version of word.
|
||||||
|
! #! Prints out attributes and closes opening tag.
|
||||||
|
! store-prev-attribute write-attributes n> drop ">" write ;
|
||||||
|
!
|
||||||
|
! : </p> ( -- )
|
||||||
|
! #! Write out the closing tag.
|
||||||
|
! "</foo>" write ;
|
||||||
|
!
|
||||||
|
! Each open only HTML tag has only three words:
|
||||||
|
!
|
||||||
|
! : <input/> ( -- )
|
||||||
|
! #! Used for printing the tag with no attributes.
|
||||||
|
! "<input>" write ;
|
||||||
|
!
|
||||||
|
! : <input ( -- n: <namespace> )
|
||||||
|
! #! Used for setting inline attributes.
|
||||||
|
! "<input" write <namespace> >n ;
|
||||||
|
!
|
||||||
|
! : input/> ( n: <namespace> -- )
|
||||||
|
! #! Used to close off inline attribute version of word
|
||||||
|
! #! and print the tag/
|
||||||
|
! store-prev-attribute write-attributes n> drop ">" write ;
|
||||||
|
!
|
||||||
|
! Each attribute word has the form xxxx= where 'xxxx' is the attribute
|
||||||
|
! name. The example below is for href:
|
||||||
|
!
|
||||||
|
! : href= ( n: <namespace> optional-value -- )
|
||||||
|
! store-prev-attribute "href" "current-attribute" set ;
|
||||||
|
|
||||||
|
: create-word ( vocab name def -- )
|
||||||
|
#! Define 'word creating' word to allow
|
||||||
|
#! dynamically creating words.
|
||||||
|
>r swap create r> define-compound ;
|
||||||
|
|
||||||
|
: def-for-html-word-<foo> ( name -- name quot )
|
||||||
|
#! Return the name and code for the <foo> patterned
|
||||||
|
#! word.
|
||||||
|
<% "<" % % ">" % %> dup [ write ] cons ;
|
||||||
|
|
||||||
|
: def-for-html-word-<foo ( name -- name quot )
|
||||||
|
#! Return the name and code for the <foo patterned
|
||||||
|
#! word.
|
||||||
|
<% "<" % % %> dup [ write <namespace> >n ] cons ;
|
||||||
|
|
||||||
|
: def-for-html-word-foo> ( name -- name quot )
|
||||||
|
#! Return the name and code for the foo> patterned
|
||||||
|
#! word.
|
||||||
|
<% % ">" % %> [ store-prev-attribute write-attributes n> drop ">" write ] ;
|
||||||
|
|
||||||
|
: def-for-html-word-</foo> ( name -- name quot )
|
||||||
|
#! Return the name and code for the </foo> patterned
|
||||||
|
#! word.
|
||||||
|
<% "</" % % ">" % %> dup [ write ] cons ;
|
||||||
|
|
||||||
|
: def-for-html-word-<foo/> ( name -- name quot )
|
||||||
|
#! Return the name and code for the <foo/> patterned
|
||||||
|
#! word.
|
||||||
|
<% "<" % dup % "/>" % %> swap <% "<" % % ">" % %> [ write ] cons ;
|
||||||
|
|
||||||
|
: def-for-html-word-foo/> ( name -- name quot )
|
||||||
|
#! Return the name and code for the foo/> patterned
|
||||||
|
#! word.
|
||||||
|
<% % "/>" % %> [ store-prev-attribute write-attributes n> drop ">" write ] ;
|
||||||
|
|
||||||
|
: define-closed-html-word ( name -- )
|
||||||
|
#! Given an HTML tag name, define the words for
|
||||||
|
#! that closable HTML tag.
|
||||||
|
"cont-html" swap
|
||||||
|
2dup def-for-html-word-<foo> create-word
|
||||||
|
2dup def-for-html-word-<foo create-word
|
||||||
|
2dup def-for-html-word-foo> create-word
|
||||||
|
def-for-html-word-</foo> create-word ;
|
||||||
|
|
||||||
|
: define-open-html-word ( name -- )
|
||||||
|
#! Given an HTML tag name, define the words for
|
||||||
|
#! that open HTML tag.
|
||||||
|
"cont-html" swap
|
||||||
|
2dup def-for-html-word-<foo/> create-word
|
||||||
|
2dup def-for-html-word-<foo create-word
|
||||||
|
def-for-html-word-foo/> create-word ;
|
||||||
|
|
||||||
|
: define-attribute-word ( name -- )
|
||||||
|
"cont-html" swap dup "=" cat2 swap
|
||||||
|
[ store-prev-attribute ] cons reverse [ "current-attribute" set ] append create-word ;
|
||||||
|
|
||||||
|
! Define some closed HTML tags
|
||||||
|
[ "h1" "h2" "h3" "h4" "h5" "h6" "h7" "h8" "h9"
|
||||||
|
"ol" "li" "form" "a" "p" "html" "head" "body" "title"
|
||||||
|
"b" "i" "ul" "table" "tr" "td" "th" "pre" "textarea"
|
||||||
|
"script" "div" "span" "select" "option"
|
||||||
|
] [ define-closed-html-word ] each
|
||||||
|
|
||||||
|
! Define some open HTML tags
|
||||||
|
[
|
||||||
|
"input"
|
||||||
|
"br"
|
||||||
|
"link"
|
||||||
|
] [ define-open-html-word ] each
|
||||||
|
|
||||||
|
! Define some attributes
|
||||||
|
[
|
||||||
|
"method" "action" "type" "value" "name"
|
||||||
|
"size" "href" "class" "border" "rows" "cols"
|
||||||
|
"id" "onclick" "style" "valign" "accesskey"
|
||||||
|
"src" "language" "colspan" "onchange" "rel"
|
||||||
|
"width"
|
||||||
|
] [ define-attribute-word ] each
|
|
@ -0,0 +1,319 @@
|
||||||
|
! cont-responder v0.6
|
||||||
|
!
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
IN: cont-responder
|
||||||
|
USE: stdio
|
||||||
|
USE: httpd
|
||||||
|
USE: httpd-responder
|
||||||
|
USE: math
|
||||||
|
USE: random
|
||||||
|
USE: continuations
|
||||||
|
USE: format
|
||||||
|
USE: namespaces
|
||||||
|
USE: stack
|
||||||
|
USE: combinators
|
||||||
|
USE: streams
|
||||||
|
USE: lists
|
||||||
|
USE: strings
|
||||||
|
USE: html
|
||||||
|
USE: kernel
|
||||||
|
USE: logic
|
||||||
|
USE: cont-html
|
||||||
|
USE: logging
|
||||||
|
USE: url-encoding
|
||||||
|
USE: unparser
|
||||||
|
|
||||||
|
: expiry-timeout ( -- timeout-seconds )
|
||||||
|
#! Number of seconds to timeout continuations in
|
||||||
|
#! continuation table. This value will need to be
|
||||||
|
#! tuned. I leave it at 24 hours but it can be
|
||||||
|
#! higher/lower as needed. Default to 1 hour for
|
||||||
|
#! testing.
|
||||||
|
3600 ;
|
||||||
|
|
||||||
|
: redirect-enabled?
|
||||||
|
#! Set to true if you want the post-redirect-get pattern
|
||||||
|
#! implemented. See the redirect-to-here word for details.
|
||||||
|
t ;
|
||||||
|
|
||||||
|
: get-random-id ( -- id )
|
||||||
|
#! Generate a random id to use for continuation URL's
|
||||||
|
<% 16 [ random-digit unparse % ] times %> ;
|
||||||
|
|
||||||
|
: continuation-table ( -- <namespace> )
|
||||||
|
#! Return the global table of continuations
|
||||||
|
"continuation-table" get ;
|
||||||
|
|
||||||
|
: reset-continuation-table ( -- )
|
||||||
|
#! Create the initial global table
|
||||||
|
<namespace> "continuation-table" set ;
|
||||||
|
|
||||||
|
: continuation-item ( expire? quot id -- <item> )
|
||||||
|
#! A continuation item is the actual item stored
|
||||||
|
#! in the continuation table. It contains the id,
|
||||||
|
#! quotation/continuation, time added, etc. If
|
||||||
|
#! expire? is true then the continuation will
|
||||||
|
#! be expired after a certain amount of time.
|
||||||
|
<namespace> [
|
||||||
|
"id" set
|
||||||
|
"quot" set
|
||||||
|
"expire?" set
|
||||||
|
millis "time-added" set
|
||||||
|
] extend ;
|
||||||
|
|
||||||
|
: seconds>millis ( seconds -- millis )
|
||||||
|
#! Convert a number of seconds to milliseconds
|
||||||
|
1000 * ;
|
||||||
|
|
||||||
|
: expired? ( timeout-seconds <item> -- bool )
|
||||||
|
#! Return true if the continuation item is expirable
|
||||||
|
#! and has expired (ie. was added to the table more than
|
||||||
|
#! timeout milliseconds ago).
|
||||||
|
[ seconds>millis "time-added" get + millis - 0 <
|
||||||
|
"expire?" get and
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: continuation-items ( -- alist )
|
||||||
|
#! Return an alist of all continuation items in the continuation
|
||||||
|
#! table with the car as the id and the cdr as the item.
|
||||||
|
continuation-table [ vars-values ] bind ;
|
||||||
|
|
||||||
|
: expire-continuations ( timeout-seconds -- )
|
||||||
|
#! Expire all continuations in the continuation table
|
||||||
|
#! if they are 'timeout-seconds' old (ie. were added
|
||||||
|
#! more than 'timeout-seconds' ago.
|
||||||
|
continuation-items [ cdr dupd expired? not ] subset nip
|
||||||
|
alist>namespace "continuation-table" set ;
|
||||||
|
|
||||||
|
: register-continuation ( expire? quot -- id )
|
||||||
|
#! Store a continuation in the table and associate it with
|
||||||
|
#! a random id. That continuation will be expired after
|
||||||
|
#! a certain period of time if 'expire?' is true.
|
||||||
|
continuation-table [
|
||||||
|
get-random-id -rot pick continuation-item over set
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: append* ( lists -- list )
|
||||||
|
#! Given a list of lists, append the lists together
|
||||||
|
#! and return the concatenated list.
|
||||||
|
f swap [ append ] each ;
|
||||||
|
|
||||||
|
: register-continuation* ( expire? quots -- id )
|
||||||
|
#! Like register-continuation but registers a quotation
|
||||||
|
#! that will call all quotations in the list, in the order given.
|
||||||
|
append* register-continuation ;
|
||||||
|
|
||||||
|
: get-continuation-item ( id -- <item> )
|
||||||
|
#! Get the continuation item associated with the id.
|
||||||
|
continuation-table [ get ] bind ;
|
||||||
|
|
||||||
|
DEFER: show
|
||||||
|
|
||||||
|
: expired-page-handler ( alist -- )
|
||||||
|
#! Display a page has expired message.
|
||||||
|
#! TODO: Need to handle this better to enable
|
||||||
|
#! returning back to root continuation.
|
||||||
|
drop
|
||||||
|
[
|
||||||
|
drop
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p> "This page has expired." write </p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
] show drop ;
|
||||||
|
|
||||||
|
: get-registered-continuation ( id -- cont )
|
||||||
|
#! Return the continuation or quotation
|
||||||
|
#! associated with the given id.
|
||||||
|
#! TODO: handle expired pages better.
|
||||||
|
expiry-timeout expire-continuations
|
||||||
|
get-continuation-item dup [
|
||||||
|
[ "quot" get ] bind
|
||||||
|
] [
|
||||||
|
drop [ expired-page-handler ]
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: resume-continuation ( value id -- )
|
||||||
|
#! Call the continuation associated with the given id,
|
||||||
|
#! with 'value' on the top of the stack.
|
||||||
|
get-registered-continuation call ;
|
||||||
|
|
||||||
|
: exit-continuation ( -- exit )
|
||||||
|
#! Get the current exit continuation
|
||||||
|
"exit" get ;
|
||||||
|
|
||||||
|
: call-exit-continuation ( value -- )
|
||||||
|
#! Call the exit continuation, passing it the given value on the
|
||||||
|
#! top of the stack.
|
||||||
|
"exit" get call ;
|
||||||
|
|
||||||
|
: with-exit-continuation ( quot -- )
|
||||||
|
#! Call the quotation with the variable "exit" bound such that when
|
||||||
|
#! the exit continuation is called, computation will resume from the
|
||||||
|
#! end of this 'with-exit-continuation' call, with the value passed
|
||||||
|
#! to the exit continuation on the top of the stack.
|
||||||
|
[ "exit" set call f call-exit-continuation ] callcc1 nip ;
|
||||||
|
|
||||||
|
: store-callback-cc ( -- )
|
||||||
|
#! Store the current continuation in the variable 'callback-cc'
|
||||||
|
#! so it can be returned to later by callbacks. Note that it
|
||||||
|
#! recalls itself when the continuation is called to ensure that
|
||||||
|
#! it resets its value back to the most recent show call.
|
||||||
|
[ ( 0 -- )
|
||||||
|
[ ( 0 1 -- )
|
||||||
|
"callback-cc" set ( 0 -- )
|
||||||
|
call
|
||||||
|
] callcc1 ( 0 [ ] == )
|
||||||
|
nip
|
||||||
|
call
|
||||||
|
store-callback-cc
|
||||||
|
] callcc0 ;
|
||||||
|
|
||||||
|
: with-string-stream ( quot -- string )
|
||||||
|
#! Call the quotation with standard output bound to a string output
|
||||||
|
#! stream. Return the string on exit.
|
||||||
|
<namespace> [
|
||||||
|
"stdio" 1024 <string-output-stream> put call "stdio" get stream>str
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: redirect-to-here ( -- )
|
||||||
|
#! Force a redirect to the client browser so that the browser
|
||||||
|
#! goes to the current point in the code. This forces an URL
|
||||||
|
#! change on the browser so that refreshing that URL will
|
||||||
|
#! immediately run from this code point. This prevents the
|
||||||
|
#! "this request will issue a POST" warning from the browser
|
||||||
|
#! and prevents re-running the previous POST logic. This is
|
||||||
|
#! known as the 'post-refresh-get' pattern.
|
||||||
|
"disable-initial-redirect?" get [
|
||||||
|
"disable-initial-redirect?" f put
|
||||||
|
] [
|
||||||
|
[
|
||||||
|
t swap register-continuation
|
||||||
|
<% "HTTP/1.1 302 Document Moved\nLocation: " % % "\n" %
|
||||||
|
"Content-Length: 0\nContent-Type: text/plain\n\n" % %>
|
||||||
|
call-exit-continuation
|
||||||
|
] callcc1 drop
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: show ( quot -- namespace )
|
||||||
|
#! Call the quotation with the URL associated with the current
|
||||||
|
#! continuation. Return the HTML string generated by that code
|
||||||
|
#! to the exit continuation. When the URL is later referenced then
|
||||||
|
#! computation will resume from this 'show' call with a namespace on
|
||||||
|
#! the stack containing any query or post parameters.
|
||||||
|
#! NOTE: On return from 'show' the stack is exactly the same as
|
||||||
|
#! initial entry with 'quot' popped off an <namespace> put on. Even
|
||||||
|
#! if the quotation consumes items on the stack.
|
||||||
|
store-callback-cc
|
||||||
|
redirect-enabled? [ redirect-to-here ] when
|
||||||
|
[
|
||||||
|
t swap register-continuation swap
|
||||||
|
[ serving-html ] car swons with-string-stream
|
||||||
|
call-exit-continuation
|
||||||
|
] callcc1
|
||||||
|
nip ;
|
||||||
|
USE: prettyprint
|
||||||
|
USE: inspector
|
||||||
|
|
||||||
|
: cont-get-responder ( id-or-f -- )
|
||||||
|
#! httpd responder that retrieves a continuation and calls it.
|
||||||
|
dup f-or-"" [
|
||||||
|
#! No continuation id given
|
||||||
|
drop "root-continuation" get dup [
|
||||||
|
#! Use the root continuation
|
||||||
|
[ f swap resume-continuation ] with-exit-continuation
|
||||||
|
] [
|
||||||
|
#! No root continuation either
|
||||||
|
drop [ f expired-page-handler ] with-exit-continuation
|
||||||
|
] ifte
|
||||||
|
] [
|
||||||
|
#! Use the given continuation
|
||||||
|
[ f swap resume-continuation ] with-exit-continuation
|
||||||
|
] ifte
|
||||||
|
[ write flush ] when* drop ;
|
||||||
|
|
||||||
|
: post-request>namespace ( post-request -- namespace )
|
||||||
|
#! Return a namespace containing the name/value's from the
|
||||||
|
#! post data.
|
||||||
|
alist>namespace ;
|
||||||
|
|
||||||
|
: cont-post-responder ( id -- )
|
||||||
|
#! httpd responder that retrieves a continuation for the given
|
||||||
|
#! id and calls it with the POST data as an alist on the top
|
||||||
|
#! of the stack.
|
||||||
|
[
|
||||||
|
"response" get post-request>namespace swap resume-continuation
|
||||||
|
] with-exit-continuation
|
||||||
|
print drop ;
|
||||||
|
|
||||||
|
: callback-quot ( quot -- quot )
|
||||||
|
#! Convert the given quotation so it works as a callback
|
||||||
|
#! by returning a quotation that will pass the original
|
||||||
|
#! quotation to the callback continuation.
|
||||||
|
unit "callback-cc" get [ call ] cons append ;
|
||||||
|
|
||||||
|
: quot-href ( text quot -- )
|
||||||
|
#! Write to standard output an HTML HREF where the href,
|
||||||
|
#! when referenced, will call the quotation and then return
|
||||||
|
#! back to the most recent 'show' call (via the callback-cc).
|
||||||
|
#! The text of the link will be the 'text' argument on the
|
||||||
|
#! stack.
|
||||||
|
<a href= callback-quot t swap register-continuation a> write </a> ;
|
||||||
|
|
||||||
|
: with-new-session ( quot -- )
|
||||||
|
#! Each cont-responder is bound inside their own
|
||||||
|
#! namespace for storing session state. Run the given
|
||||||
|
#! quotation inside a new namespace for this purpose.
|
||||||
|
<namespace> swap bind ;
|
||||||
|
|
||||||
|
: init-session-namespace ( -- )
|
||||||
|
#! Setup the initial session namespace. Currently this only
|
||||||
|
#! copies the global value of whether the initial redirect
|
||||||
|
#! will be disabled. It assumes the session namespace is
|
||||||
|
#! topmost on the namespace stack.
|
||||||
|
"disable-initial-redirect?" get "disable-initial-redirect?" set ;
|
||||||
|
|
||||||
|
: install-cont-responder ( name quot -- )
|
||||||
|
#! Install a cont-responder with the given name
|
||||||
|
#! that will initially run the given quotation.
|
||||||
|
#!
|
||||||
|
#! Convert the quotation so it is run within a session namespace
|
||||||
|
#! and that namespace is initialized first.
|
||||||
|
[ init-session-namespace ] swap append unit [ with-new-session ] append
|
||||||
|
"httpd-responders" get [
|
||||||
|
<responder> [
|
||||||
|
[ cont-get-responder ] "get" set
|
||||||
|
[ cont-post-responder ] "post" set
|
||||||
|
over "responder-name" set
|
||||||
|
over "responder" set
|
||||||
|
reset-continuation-table
|
||||||
|
"disable-initial-redirect?" t put
|
||||||
|
] extend dup >r rot set
|
||||||
|
r> [
|
||||||
|
f swap register-continuation "root-continuation" set
|
||||||
|
] bind
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
!
|
||||||
|
! General purpose words for display pages using the continuation
|
||||||
|
! based responder.
|
||||||
|
IN: cont-utils
|
||||||
|
USE: cont-html
|
||||||
|
USE: cont-responder
|
||||||
|
USE: lists
|
||||||
|
USE: stdio
|
||||||
|
USE: stack
|
||||||
|
USE: namespaces
|
||||||
|
USE: html
|
||||||
|
USE: combinators
|
||||||
|
|
||||||
|
: simple-page ( title quot -- )
|
||||||
|
#! Call the quotation, with all output going to the
|
||||||
|
#! body of an html page with the given title.
|
||||||
|
<html>
|
||||||
|
<head> <title> swap write </title> </head>
|
||||||
|
<body> call </body>
|
||||||
|
</html> ;
|
||||||
|
|
||||||
|
: styled-page ( title stylesheet-quot quot -- )
|
||||||
|
#! Call the quotation, with all output going to the
|
||||||
|
#! body of an html page with the given title. stylesheet-quot
|
||||||
|
#! is called to generate the required stylesheet.
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> rot write </title>
|
||||||
|
swap call
|
||||||
|
</head>
|
||||||
|
<body> call </body>
|
||||||
|
</html> ;
|
||||||
|
|
||||||
|
: paragraph ( str -- )
|
||||||
|
#! Output the string as an html paragraph
|
||||||
|
<p> write </p> ;
|
||||||
|
|
||||||
|
: show-message-page ( message -- )
|
||||||
|
#! Display the message in an HTML page with an OK button.
|
||||||
|
[
|
||||||
|
"Press OK to Continue" [
|
||||||
|
swap paragraph
|
||||||
|
<a href= a> "OK" write </a>
|
||||||
|
] simple-page
|
||||||
|
] show 2drop ;
|
||||||
|
|
||||||
|
: vertical-layout ( list -- )
|
||||||
|
#! Given a list of HTML components, arrange them vertically.
|
||||||
|
<table>
|
||||||
|
[ <tr> <td> call </td> </tr> ] each
|
||||||
|
</table> ;
|
||||||
|
|
||||||
|
: horizontal-layout ( list -- )
|
||||||
|
#! Given a list of HTML components, arrange them horizontally.
|
||||||
|
<table>
|
||||||
|
<tr valign= "top" tr> [ <td> call </td> ] each </tr>
|
||||||
|
</table> ;
|
||||||
|
|
||||||
|
: button ( label -- )
|
||||||
|
#! Output an HTML submit button with the given label.
|
||||||
|
<input type= "submit" value= input/> ;
|
||||||
|
|
||||||
|
: with-simple-html-output ( quot -- )
|
||||||
|
#! Run the quotation inside an HTML stream wrapped
|
||||||
|
#! around stdio.
|
||||||
|
<pre>
|
||||||
|
"stdio" get <html-stream> [
|
||||||
|
call
|
||||||
|
] with-stream
|
||||||
|
</pre> ;
|
|
@ -0,0 +1,375 @@
|
||||||
|
cont-responder v0.3
|
||||||
|
===================
|
||||||
|
|
||||||
|
NOTE: This documentation is slightly out of date with respect to the
|
||||||
|
current code but contains the basic idea.
|
||||||
|
|
||||||
|
In a continuation based web application the current position within
|
||||||
|
the web application is identified by a random URL. In this example the
|
||||||
|
URL is generated as a string of random digits:
|
||||||
|
|
||||||
|
: get-random-id ( -- id )
|
||||||
|
#! Generate a random id to use for continuation URL's
|
||||||
|
<% 16 [ random-digit % ] times %> ;
|
||||||
|
|
||||||
|
Each URL is associated with a quotation or continuation. When that URL
|
||||||
|
is accessed, that quotation is executed. The quotation will receive an
|
||||||
|
alist on the top of the stack which holds any POST or query
|
||||||
|
parameters. A global table is maintained that holds the association of
|
||||||
|
quotations to id's.
|
||||||
|
|
||||||
|
: continuation-table ( -- <namespace> )
|
||||||
|
#! Return the global table of continuations
|
||||||
|
"cont" get ;
|
||||||
|
|
||||||
|
: reset-continuation-table ( -- )
|
||||||
|
#! Create the initial global table
|
||||||
|
<namespace> "cont" set ;
|
||||||
|
|
||||||
|
: register-continuation ( quot -- id )
|
||||||
|
#! Store a continuation in the table and associate it with
|
||||||
|
#! a random id.
|
||||||
|
continuation-table [ get-random-id dup [ set ] dip ] bind ;
|
||||||
|
|
||||||
|
: get-registered-continuation ( id -- cont )
|
||||||
|
#! Return the continuation associated with the given id.
|
||||||
|
continuation-table [ get ] bind ;
|
||||||
|
|
||||||
|
: resume-continuation ( value id -- )
|
||||||
|
#! Call the continuation associated with the given id,
|
||||||
|
#! with 'value' on the top of the stack.
|
||||||
|
get-registered-continuation call ;
|
||||||
|
|
||||||
|
When a an URL is accessed, the continuation for the specific URL is
|
||||||
|
obtained and called. That continuation needs to exit back to the
|
||||||
|
caller when it has some HTML that it needs to display. returning that
|
||||||
|
HTML to the caller. To exit back to the caller it calls an 'exit
|
||||||
|
continuation' that is stored in an "exit" variable:
|
||||||
|
|
||||||
|
: exit-continuation ( -- exit )
|
||||||
|
#! Get the current exit continuation
|
||||||
|
"exit" get ;
|
||||||
|
|
||||||
|
: call-exit-continuation ( value -- )
|
||||||
|
#! Call the exit continuation, passing it the given value on the
|
||||||
|
#! top of the stack.
|
||||||
|
"exit" get call ;
|
||||||
|
|
||||||
|
: with-exit-continuation ( quot -- )
|
||||||
|
#! Call the quotation with the variable "exit" bound such that when
|
||||||
|
#! the exit continuation is called, computation will resume from the
|
||||||
|
#! end of this 'with-exit-continuation' call, with the value passed
|
||||||
|
#! to the exit continuation on the top of the stack.
|
||||||
|
[ "exit" set call call-exit-continuation ] callcc1 nip ;
|
||||||
|
|
||||||
|
All this calling of continuations is hidden behind a single 'show'
|
||||||
|
call. 'show' will take a quotation on the stack. That quotation should
|
||||||
|
return an HTML string. 'show' will call it to generate the HTML and
|
||||||
|
call the exit continuation with this string on the stack so it gets
|
||||||
|
returned to the httpd responder. The quotation receives a 'url' on the
|
||||||
|
top of the stack which is the 'id' of the continuation to resume when
|
||||||
|
that URL is accessed.
|
||||||
|
|
||||||
|
The HTML page that 'show' displays can contain links to
|
||||||
|
'callbacks'. These are links to other quotations that when called
|
||||||
|
will perform some action and return back to the calling page. To
|
||||||
|
return back to the calling page 'show' must capture and store the
|
||||||
|
current continuation before 'show' does anything so it can be later
|
||||||
|
return to by the callback. The following words store the current
|
||||||
|
continuation and retrieve it:
|
||||||
|
|
||||||
|
: store-callback-cc ( -- )
|
||||||
|
#! Store the current continuation in the variable 'callback-cc'
|
||||||
|
#! so it can be returned to later by callbacks. Note that it
|
||||||
|
#! recalls itself when the continuation is called to ensure that
|
||||||
|
#! it resets it's value back to the most recent show call.
|
||||||
|
[
|
||||||
|
[ "callback-cc" set call ] callcc0 drop store-callback-cc
|
||||||
|
] callcc0 ;
|
||||||
|
|
||||||
|
To generate the string of HTML I use 'with-string-stream' which calls
|
||||||
|
a quotation and all output inside that call gets appended to a string:
|
||||||
|
|
||||||
|
: with-string-stream ( quot -- string )
|
||||||
|
#! Call the quotation with standard output bound to a string output
|
||||||
|
#! stream. Return the string on exit.
|
||||||
|
<namespace> [
|
||||||
|
"stdio" <string-output-stream> put call "stdio" get stream>str
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: show ( quot -- alist )
|
||||||
|
#! Call the quotation with the URL associated with the current
|
||||||
|
#! continuation. Return the HTML string generated by that code
|
||||||
|
#! to the exit continuation. When the URL is later referenced then
|
||||||
|
#! computation will resume from this 'show' call with a alist on
|
||||||
|
#! the stack containing any query or post parameters.
|
||||||
|
store-callback-cc
|
||||||
|
[
|
||||||
|
register-continuation swap with-string-stream
|
||||||
|
call-exit-continuation
|
||||||
|
] callcc1
|
||||||
|
nip ;
|
||||||
|
|
||||||
|
An httpd get responder is used that takes the ID as an argument, retrieves
|
||||||
|
the continuation associated with it and calls it. For the post
|
||||||
|
responder the post data is converted into an alist and it is put on
|
||||||
|
the top of the stack when calling the continuation.
|
||||||
|
|
||||||
|
: cont-get-responder ( id -- )
|
||||||
|
#! httpd responder that retrieves a continuation and calls it.
|
||||||
|
[ f swap resume-continuation ] with-exit-continuation
|
||||||
|
serving-html print drop ;
|
||||||
|
|
||||||
|
: post-request>alist ( post-request -- alist )
|
||||||
|
#! Return an alist containing name/value pairs from the
|
||||||
|
#! post data.
|
||||||
|
dup "&" swap str-contains [
|
||||||
|
"(.+)&(.+)" groups [ "(.+)=(.+)" groups uncons car cons ] inject
|
||||||
|
] [
|
||||||
|
"(.+)=(.+)" groups uncons car cons unit
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: cont-post-responder ( id -- )
|
||||||
|
#! httpd responder that retrieves a continuation for the given
|
||||||
|
#! id and calls it with the POST data as an alist on the top
|
||||||
|
#! of the stack.
|
||||||
|
[
|
||||||
|
read-post-request post-request>alist swap resume-continuation
|
||||||
|
] with-exit-continuation
|
||||||
|
serving-html print drop ;
|
||||||
|
|
||||||
|
Some code to install the responder:
|
||||||
|
|
||||||
|
: install-cont-responder ( -- )
|
||||||
|
#! Install the cont-responder in the table of httpd responders
|
||||||
|
"httpd-responders" get [
|
||||||
|
<responder> [
|
||||||
|
[ cont-get-responder ] "get" set
|
||||||
|
[ cont-post-responder ] "post" set
|
||||||
|
reset-continuation-table
|
||||||
|
] extend "cont" set
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
Now to test it. Here's a function that displays some text on an HTML page:
|
||||||
|
|
||||||
|
: display-page ( title -- )
|
||||||
|
#! Display a page with some text to test the cont-responder.
|
||||||
|
#! The page has a link to the 'next' continuation.
|
||||||
|
[
|
||||||
|
swap [
|
||||||
|
"<a href='" write write "'>Next</a>" write
|
||||||
|
] html-document
|
||||||
|
] show drop ;
|
||||||
|
|
||||||
|
Note that it contains an A HREF link to the URL that resumes the
|
||||||
|
computation (The quotation passed to show has this URL passed to it by
|
||||||
|
show).
|
||||||
|
|
||||||
|
An example of a POST request is a page that requests input of a
|
||||||
|
name. The following function calls show to display it and returns the
|
||||||
|
result of the 'name' field by retrieving it from the alist. Notice how
|
||||||
|
the 'action' of the post request is set to the URL passed in to the
|
||||||
|
quotation passed to show. So the 'next' that happens here is not an A
|
||||||
|
HREF but the action field of the POST.
|
||||||
|
|
||||||
|
: display-get-name-page ( -- name )
|
||||||
|
#! Display a page prompting for input of a name and return that name.
|
||||||
|
[
|
||||||
|
"Enter your name" [
|
||||||
|
"<form method='post' action='" write write "'>" write
|
||||||
|
"Name: <input type='text' name='name' size='20'>" write
|
||||||
|
"<input type='submit' value='Ok'>" write
|
||||||
|
"</form>" write
|
||||||
|
] html-document
|
||||||
|
] show
|
||||||
|
"name" swap assoc ;
|
||||||
|
|
||||||
|
A word that displays a sequence of these pages would be:
|
||||||
|
|
||||||
|
: test-cont-responder ( alist - )
|
||||||
|
#! Test the cont-responder responder by displaying a few pages in a row.
|
||||||
|
drop
|
||||||
|
"Page one" display-page
|
||||||
|
"Hello " display-get-name-page cat2 display-page
|
||||||
|
"Page three" display-page ;
|
||||||
|
|
||||||
|
This displays the first page, then a page prompting for the
|
||||||
|
name. "Hello " is concatenated to the result of the page and a third
|
||||||
|
page is displayed. A fourth page is available as well.
|
||||||
|
|
||||||
|
The following registers this word with the continuation system:
|
||||||
|
|
||||||
|
: register-test-cont-responder ( -- id )
|
||||||
|
#! Register the test-cont-responder word so that accessing the
|
||||||
|
#! URL with the returned ID will call it.
|
||||||
|
"httpd-responders" get [
|
||||||
|
"cont" get [
|
||||||
|
[ test-cont-responder ] register-continuation
|
||||||
|
] bind
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
This returns an ID which can be used from the web server to resume
|
||||||
|
it. Start the web server:
|
||||||
|
|
||||||
|
8888 httpd
|
||||||
|
|
||||||
|
Now access the URL with that id:
|
||||||
|
|
||||||
|
http://localhost:8888/cont/1234567890
|
||||||
|
|
||||||
|
(replace 1234567890 with the ID returned by register-continuation above)
|
||||||
|
|
||||||
|
You'll see the first page and a link. Click the link and you'll see
|
||||||
|
the second page requesting a name. Enter the name and press 'Ok'. A
|
||||||
|
third page will display a message using that name. You can book mark,
|
||||||
|
refresh, go back, enter a new name, etc as expected.
|
||||||
|
|
||||||
|
You can do any form of computation inside the handlers. Here's an
|
||||||
|
example of looping a set number of times:
|
||||||
|
|
||||||
|
: test-cont-responder2 ( alist - )
|
||||||
|
#! Test the cont-responder responder by displaying a few pages in a loop.
|
||||||
|
[ "one" "two" "three" "four" ] [ display-page ] each
|
||||||
|
"Done!" display-page ;
|
||||||
|
|
||||||
|
: register-test-cont-responder2 ( -- id )
|
||||||
|
#! Register the test-cont-responder2 word so that accessing the
|
||||||
|
#! URL with the returned ID will call it.
|
||||||
|
"httpd-responders" get [
|
||||||
|
"cont" get [
|
||||||
|
[ test-cont-responder2 ] register-continuation
|
||||||
|
] bind
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
There is currently a limited ability to do 'callbacks'. You can
|
||||||
|
register a quotation as an HTML A HREF anchor thar when accessed by
|
||||||
|
the browser will run the quotation and then return to the most recent
|
||||||
|
'show' call. This has the effect of allowing 'subroutine' calls as
|
||||||
|
page links that can do anything (including display other pages and
|
||||||
|
complicated action) and will return back to the originating page. The
|
||||||
|
word to generate this link is:
|
||||||
|
|
||||||
|
: quot-href ( text quot -- )
|
||||||
|
#! Write to standard output an HTML HREF where the href,
|
||||||
|
#! when referenced, will call the quotation and then return
|
||||||
|
#! back to the most recent 'show' call (via the callback-cc).
|
||||||
|
#! The text of the link will be the 'text' argument on the
|
||||||
|
#! stack.
|
||||||
|
"<a href='" write
|
||||||
|
"callback-cc" get [ call ] cons append register-continuation write
|
||||||
|
"'>" write
|
||||||
|
write
|
||||||
|
"</a>" write ;
|
||||||
|
|
||||||
|
An example of use is a simple menu page that displays links to the
|
||||||
|
words written previously:
|
||||||
|
|
||||||
|
: test-cont-responder3 ( alist - )
|
||||||
|
#! Test the quot-href word by displaying a menu of the current
|
||||||
|
#! test words. Note that we drop the 'url' argument to the show
|
||||||
|
#! quotation as we don't link to a 'next' page.
|
||||||
|
drop
|
||||||
|
[
|
||||||
|
drop
|
||||||
|
"Menu" [
|
||||||
|
"<ol>" write
|
||||||
|
"<li>" write
|
||||||
|
"Test responder1" [ test-cont-responder ] quot-href
|
||||||
|
"</li>" write
|
||||||
|
"<li>" write
|
||||||
|
"Test responder2" [ test-cont-responder2 ] quot-href
|
||||||
|
"</li>" write
|
||||||
|
"</ol>" write
|
||||||
|
] html-document
|
||||||
|
] show drop ;
|
||||||
|
|
||||||
|
: register-test-cont-responder3 ( -- id )
|
||||||
|
#! Register the test-cont-responder3 word so that accessing the
|
||||||
|
#! URL with the returned ID will call it.
|
||||||
|
"httpd-responders" get [
|
||||||
|
"cont" get [
|
||||||
|
[ test-cont-responder3 ] register-continuation
|
||||||
|
] bind
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
You should now be able to click on the menu items, navigate through
|
||||||
|
those pages and when the sequence of pages ends, return back to the
|
||||||
|
original menu page.
|
||||||
|
|
||||||
|
Note that this is just a proof of concept. In a real framework the
|
||||||
|
continuations need to be expired after time. It would also enable
|
||||||
|
generating links to other pages, etc rather than just a sequence of
|
||||||
|
pages. I plan to flesh this out over the next few days and present
|
||||||
|
some more useful examples. My main point was to see if it was possible
|
||||||
|
to do this type of thing in Factor.
|
||||||
|
|
||||||
|
The number of words required to get things going is amazingly small
|
||||||
|
and it was very easy to develop this far.
|
||||||
|
|
||||||
|
The code is contained in cont-responder.factor. Here are the steps to
|
||||||
|
run all the examples starting with a default Factor 0.60 install:
|
||||||
|
|
||||||
|
1) java -cp Factor.jar factor.FactorInterpreter
|
||||||
|
-db:factor.db.FileStore:factor.db
|
||||||
|
|
||||||
|
At the prompt enter:
|
||||||
|
|
||||||
|
---8<---
|
||||||
|
USE: httpd-responder
|
||||||
|
default-responders
|
||||||
|
exit
|
||||||
|
---8<---
|
||||||
|
|
||||||
|
2) java -cp Factor.jar factor.FactorInterpreter
|
||||||
|
-db:factor.db.FileStore:factor.db
|
||||||
|
-no-compile
|
||||||
|
|
||||||
|
At the prompt enter:
|
||||||
|
|
||||||
|
---8<---
|
||||||
|
USE: httpd
|
||||||
|
"cont-responder.factor" run-file
|
||||||
|
USE: cont-responder
|
||||||
|
init-cont-responder
|
||||||
|
register-test-cont-responder .
|
||||||
|
---8<---
|
||||||
|
|
||||||
|
Make note of the number returned by the last line call this [1].
|
||||||
|
|
||||||
|
---8<---
|
||||||
|
register-test-cont-responder2 .
|
||||||
|
---8<---
|
||||||
|
|
||||||
|
Make note of the number returned by the last line call this [2].
|
||||||
|
|
||||||
|
---8<---
|
||||||
|
register-test-cont-responder3 .
|
||||||
|
---8<---
|
||||||
|
|
||||||
|
Make note of the number returned by the last line call this [3].
|
||||||
|
|
||||||
|
---8<---
|
||||||
|
8888 httpd
|
||||||
|
---8<---
|
||||||
|
|
||||||
|
For the first example go to http://localhost:8888/cont/[1]
|
||||||
|
where you replace [1] with the number from [1] above. In my system
|
||||||
|
it was:
|
||||||
|
|
||||||
|
http://localhost:8888/cont/0406763029866672
|
||||||
|
|
||||||
|
For the second example go to http://localhost:8888/cont/[2]
|
||||||
|
where you replace [2] with the number from [2] above. In my system
|
||||||
|
it was:
|
||||||
|
|
||||||
|
http://localhost:8888/cont/6018237533813007
|
||||||
|
|
||||||
|
For the menu example go to http://localhost:8888/cont/[3]
|
||||||
|
where you replace [3] with the number from [3] above. In my system
|
||||||
|
it was:
|
||||||
|
|
||||||
|
http://localhost:8888/cont/3568874223456634
|
||||||
|
|
||||||
|
3) You can use the inspector to look at the continuation table:
|
||||||
|
|
||||||
|
http://localhost:8888/inspect/global'httpd-responders'cont'cont
|
|
@ -0,0 +1,210 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
!
|
||||||
|
! An httpd responder that allows executing simple definitions.
|
||||||
|
!
|
||||||
|
IN: eval-responder
|
||||||
|
USE: cont-html
|
||||||
|
USE: html
|
||||||
|
USE: cont-responder
|
||||||
|
USE: cont-utils
|
||||||
|
USE: stack
|
||||||
|
USE: stdio
|
||||||
|
USE: namespaces
|
||||||
|
USE: streams
|
||||||
|
USE: parser
|
||||||
|
USE: lists
|
||||||
|
USE: errors
|
||||||
|
USE: strings
|
||||||
|
USE: logic
|
||||||
|
USE: combinators
|
||||||
|
USE: live-updater
|
||||||
|
USE: prettyprint
|
||||||
|
USE: words
|
||||||
|
|
||||||
|
: <evaluator> ( stack msg history -- )
|
||||||
|
#! Create an 'evaluator' object that holds
|
||||||
|
#! the current stack, output and history for
|
||||||
|
#! do-eval.
|
||||||
|
<namespace> [
|
||||||
|
"history" set
|
||||||
|
"output" set
|
||||||
|
"stack" set
|
||||||
|
] extend ;
|
||||||
|
|
||||||
|
: display-eval-form ( url -- )
|
||||||
|
#! Display the components for allowing entry of
|
||||||
|
#! factor words to be evaluated.
|
||||||
|
<form name= "main" method= "post" action= form>
|
||||||
|
[
|
||||||
|
[
|
||||||
|
<textarea name= "eval" rows= "10" cols= "40" textarea>
|
||||||
|
"" write
|
||||||
|
</textarea>
|
||||||
|
]
|
||||||
|
[
|
||||||
|
<input type= "submit" value= "Evaluate" accesskey= "e" input/>
|
||||||
|
]
|
||||||
|
] vertical-layout
|
||||||
|
</form>
|
||||||
|
"<script language='javascript'>document.forms.main.eval.focus()</script>" write ;
|
||||||
|
|
||||||
|
: escape-quotes ( string -- string )
|
||||||
|
#! Replace occurrences of single quotes with
|
||||||
|
#! backslash quote.
|
||||||
|
[ dup [ [ "'" | "\\'" ] [ "\"" | "\\\"" ] ] assoc dup rot ? ] str-map ;
|
||||||
|
|
||||||
|
: make-eval-javascript ( string -- string )
|
||||||
|
#! Give a string return some javascript that when
|
||||||
|
#! executed will set the eval textarea to that string.
|
||||||
|
<% "document.forms.main.eval.value=\"" % escape-quotes % "\"" % %> ;
|
||||||
|
|
||||||
|
: write-eval-link ( string -- )
|
||||||
|
#! Given text to evaluate, create an A HREF link which when
|
||||||
|
#! clicked sets the eval textarea to that value.
|
||||||
|
<a href= "#" onclick= dup make-eval-javascript a> write </a> ;
|
||||||
|
|
||||||
|
: display-stack ( list -- )
|
||||||
|
#! Write out html to display the stack.
|
||||||
|
<table border= "1" table>
|
||||||
|
<tr> <th> "Callstack" write </th> </tr>
|
||||||
|
[ <tr> <td> write-eval-link </td> </tr> ] each
|
||||||
|
</table> ;
|
||||||
|
|
||||||
|
: display-clear-history-link ( -- )
|
||||||
|
#! Write out html to display a link that will clear
|
||||||
|
#! the history list.
|
||||||
|
" (" write
|
||||||
|
"Clear" [ [ ] "history" set ] quot-href
|
||||||
|
")" write ;
|
||||||
|
|
||||||
|
: display-history ( list -- )
|
||||||
|
#! Write out html to display the history.
|
||||||
|
<table border= "1" table>
|
||||||
|
<tr> <th> "History" write display-clear-history-link </th> </tr>
|
||||||
|
[ <tr> <td> write-eval-link </td> </tr> ] each
|
||||||
|
</table> ;
|
||||||
|
|
||||||
|
: html-for-word-source ( word-string -- )
|
||||||
|
#! Return an html fragment dispaying the source
|
||||||
|
#! of the given word.
|
||||||
|
dup dup
|
||||||
|
<namespace> [
|
||||||
|
"responder" "inspect" put
|
||||||
|
<table border= "1" table>
|
||||||
|
<tr> <th colspan= "2" th> "Source" write </th> </tr>
|
||||||
|
<tr> <td colspan= "2" td> [ see ] with-simple-html-output </td> </tr>
|
||||||
|
<tr> <th> "Apropos" write </th> <th> "Usages" write </th> </tr>
|
||||||
|
<tr> <td valign= "top" td> [ apropos. ] with-simple-html-output </td>
|
||||||
|
<td valign= "top" td> [ usages. ] with-simple-html-output </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: display-word-see-form ( url -- )
|
||||||
|
#! Write out the html for code that accepts
|
||||||
|
#! the name of a word, and displays the source
|
||||||
|
#! code of that word.
|
||||||
|
<form method= "post" action= "." form>
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"Enter the name of a word: " write
|
||||||
|
"see" [ html-for-word-source ] live-search
|
||||||
|
]
|
||||||
|
[
|
||||||
|
<div id= "see" div> "" write </div>
|
||||||
|
]
|
||||||
|
] vertical-layout
|
||||||
|
</form> ;
|
||||||
|
|
||||||
|
: display-last-output ( string -- )
|
||||||
|
#! Write out html to display the last output.
|
||||||
|
<table border= "1" table>
|
||||||
|
<tr> <th> "Last Output" write </th> </tr>
|
||||||
|
<tr> <td> <pre> write </pre> </td> </tr>
|
||||||
|
</table> ;
|
||||||
|
|
||||||
|
|
||||||
|
: get-expr-to-eval ( -- string )
|
||||||
|
#! Show a page to the user requesting the form to be
|
||||||
|
#! evaluated. Return the form as a string. Assumes
|
||||||
|
#! an evaluator is on the namestack.
|
||||||
|
[
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> "Factor Evaluator" write </title>
|
||||||
|
include-live-updater-js
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
"Use Alt+E to evaluate, or press 'Evaluate'" paragraph
|
||||||
|
[
|
||||||
|
[ display-eval-form ]
|
||||||
|
[ "stack" get display-stack ]
|
||||||
|
[ "history" get display-history ]
|
||||||
|
] horizontal-layout
|
||||||
|
display-word-see-form
|
||||||
|
"output" get display-last-output
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
] show [
|
||||||
|
"eval" get
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: do-eval ( list string -- list )
|
||||||
|
#! Evaluate the expression in 'string' using 'list' as
|
||||||
|
#! the datastack. Return the resulting stack as a list.
|
||||||
|
parse unit append restack call unstack ;
|
||||||
|
|
||||||
|
: do-eval-to-string ( list string -- list string )
|
||||||
|
#! Evaluate expression using 'list' as the current callstack.
|
||||||
|
#! All output should go to a string which is returned on the
|
||||||
|
#! callstack along with the resulting datastack as a list.
|
||||||
|
<namespace> [
|
||||||
|
"inspect" "responder" set
|
||||||
|
1024 <string-output-stream> dup >r <html-stream> [
|
||||||
|
do-eval
|
||||||
|
] with-stream r> stream>str
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: run-eval-requester ( evaluator -- )
|
||||||
|
#! Enter a loop request an expression to
|
||||||
|
#! evaluate, and displaying the results.
|
||||||
|
[
|
||||||
|
[
|
||||||
|
get-expr-to-eval dup "history" cons@
|
||||||
|
"stack" get swap do-eval-to-string
|
||||||
|
"output" set "stack" set
|
||||||
|
] forever
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: eval-responder ( evaluator -- )
|
||||||
|
#! Run an eval-responder using the given evaluation details.
|
||||||
|
[
|
||||||
|
[
|
||||||
|
run-eval-requester
|
||||||
|
] [
|
||||||
|
show-message-page
|
||||||
|
] catch
|
||||||
|
] forever ;
|
||||||
|
|
||||||
|
"eval" [ [ ] "None" [ ] <evaluator> eval-responder ] install-cont-responder
|
|
@ -0,0 +1 @@
|
||||||
|
/*
liveUpdater.js originally written by Avi Bryant, author of
Seaside (http://www.beta4.com/seaside2)
Modifed by Chris Double to add LiveUpdaterPost and use '
instead of " for the id.
*/
function liveUpdaterUri(uri)
{
return liveUpdater(function() { return uri; });
}
function liveUpdater(uriFunc)
{
var request = false;
var regex = /<(\w+).*?id='(\w+)'.*?>((.|\n)*)<\/\1>/;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
}
function update()
{
if(request && request.readyState < 4)
request.abort();
if(!window.XMLHttpRequest)
request = new ActiveXObject("Microsoft.XMLHTTP");
request.onreadystatechange = processRequestChange;
request.open("GET", uriFunc());
request.send(null);
return false;
}
function processRequestChange()
{
if(request.readyState == 4)
{
var results = regex.exec(request.responseText);
if(results)
document.getElementById(results[2]).innerHTML = results[3];
}
}
return update;
}
function liveUpdaterPost(uriFunc)
{
var request = false;
var regex = /<(\w+).*?id='(\w+)'.*?>((.|\n)*)<\/\1>/;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
}
function update(data)
{
if(request && request.readyState < 4)
request.abort();
if(!window.XMLHttpRequest)
request = new ActiveXObject("Microsoft.XMLHTTP");
request.onreadystatechange = processRequestChange;
request.open("POST", uriFunc());
request.send(data);
return false;
}
function processRequestChange()
{
if(request.readyState == 4)
{
var results = regex.exec(request.responseText);
if(results)
document.getElementById(results[2]).innerHTML = results[3];
}
}
return update;
}
function liveSearch(id, uri)
{
var updater = liveUpdaterPost((function() { return uri; }));
var last = "";
var timeout = false;
function update()
{
if (last != document.getElementById(id).value)
updater("s=" + escape(document.getElementById(id).value));
}
function start() {
if (timeout)
window.clearTimeout(timeout);
timeout = window.setTimeout(update, 300);
}
if (navigator.userAgent.indexOf("Safari") > 0)
document.getElementById(id).addEventListener("keydown",start,false);
else if (navigator.product == "Gecko")
document.getElementById(id).addEventListener("keypress",start,false);
else
document.getElementById(id).attachEvent("onkeydown",start);
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
!
|
||||||
|
! An httpd responder that demonstrates using XMLHttpRequest to send
|
||||||
|
! asynchronous requests back to the server.
|
||||||
|
!
|
||||||
|
IN: live-updater-responder
|
||||||
|
USE: live-updater
|
||||||
|
USE: namespaces
|
||||||
|
USE: cont-html
|
||||||
|
USE: html
|
||||||
|
USE: words
|
||||||
|
USE: stdio
|
||||||
|
USE: stack
|
||||||
|
USE: kernel
|
||||||
|
USE: cont-utils
|
||||||
|
USE: cont-responder
|
||||||
|
USE: prettyprint
|
||||||
|
|
||||||
|
: live-search-apropos-word ( string -- )
|
||||||
|
#! Given a string that is a factor word, show the
|
||||||
|
#! aporpos of that word.
|
||||||
|
<namespace> [
|
||||||
|
"responder" "inspect" put
|
||||||
|
<pre>
|
||||||
|
"stdio" get <html-stream> [
|
||||||
|
apropos.
|
||||||
|
] with-stream
|
||||||
|
</pre>
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: live-updater-responder ( -- )
|
||||||
|
[
|
||||||
|
drop
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> "Live Updater Example" write </title>
|
||||||
|
include-live-updater-js
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"millis" [ millis prettyprint ] "Display Server millis" live-anchor
|
||||||
|
<div id= "millis" div>
|
||||||
|
"The millisecond time from the server will appear here" write
|
||||||
|
</div>
|
||||||
|
]
|
||||||
|
[
|
||||||
|
"Enter a word to apropos:" paragraph
|
||||||
|
"apropos" [ live-search-apropos-word ] live-search
|
||||||
|
]
|
||||||
|
[
|
||||||
|
<div id= "apropos" div>
|
||||||
|
"" write
|
||||||
|
</div>
|
||||||
|
]
|
||||||
|
] vertical-layout
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
] show ;
|
||||||
|
|
||||||
|
"live-updater" [ live-updater-responder ] install-cont-responder
|
|
@ -0,0 +1,168 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
!
|
||||||
|
! cont-responder code for display forms and anchors that use XMLHttpRequest
|
||||||
|
! and the 'liveUpdater.js' code.
|
||||||
|
IN: live-updater
|
||||||
|
USE: stack
|
||||||
|
USE: streams
|
||||||
|
USE: strings
|
||||||
|
USE: cont-html
|
||||||
|
USE: cont-responder
|
||||||
|
USE: stdio
|
||||||
|
USE: namespaces
|
||||||
|
USE: lists
|
||||||
|
USE: combinators
|
||||||
|
|
||||||
|
: get-live-updater-js* ( stream -- string )
|
||||||
|
#! Read all lines from the stream, creating a string of the result.
|
||||||
|
dup freadln dup [ % "\n" % get-live-updater-js* ] [ drop fclose ] ifte ;
|
||||||
|
|
||||||
|
: get-live-updater-js ( filename -- string )
|
||||||
|
#! Return the liveUpdater javascript code as a string.
|
||||||
|
<filecr> <% get-live-updater-js* %> ;
|
||||||
|
|
||||||
|
: live-updater-url ( -- url )
|
||||||
|
#! Generate an URL to the liveUpdater.js code.
|
||||||
|
t [
|
||||||
|
[
|
||||||
|
"js/liveUpdater.js" get-live-updater-js write
|
||||||
|
] show
|
||||||
|
] register-continuation ;
|
||||||
|
|
||||||
|
: include-live-updater-js ( -- )
|
||||||
|
#! Write out the HTML script to include the live updater
|
||||||
|
#! javascript code.
|
||||||
|
<script language= "JavaScript" src= live-updater-url script>
|
||||||
|
"" write
|
||||||
|
</script> ;
|
||||||
|
|
||||||
|
: write-live-anchor-tag ( text -- id )
|
||||||
|
#! Write out the HTML for the clickable anchor. This
|
||||||
|
#! will have no actionable HREF assigned to it. Instead
|
||||||
|
#! an onclick is set via DHTML later to make it run a
|
||||||
|
#! quotation on the server. The randomly generated id
|
||||||
|
#! for the anchor is returned.
|
||||||
|
<a id= get-random-id dup href= "#" a>
|
||||||
|
swap write
|
||||||
|
</a> ;
|
||||||
|
|
||||||
|
: register-live-anchor-quot ( div-id div-quot -- kid )
|
||||||
|
#! Register the 'quot' with the cont-responder so
|
||||||
|
#! that when it is run it will produce an HTML
|
||||||
|
#! fragment which is the output generated by calling
|
||||||
|
#! 'quot'. That HTML fragment will be wrapped in a
|
||||||
|
#! 'div' with the given id.
|
||||||
|
<namespace> [
|
||||||
|
"div-quot" set
|
||||||
|
"div-id" set
|
||||||
|
] extend [
|
||||||
|
[
|
||||||
|
t "disable-initial-redirect?" set
|
||||||
|
[
|
||||||
|
<div id= "div-id" get div> "div-quot" get call </div>
|
||||||
|
] show
|
||||||
|
] bind
|
||||||
|
] cons t swap register-continuation ;
|
||||||
|
|
||||||
|
: write-live-anchor-script ( div-id div-quot anchor-id -- )
|
||||||
|
#! Write the javascript that will attach the onclick
|
||||||
|
#! event handler to the anchor with the 'anchor-id'. The
|
||||||
|
#! onclick, when clicked, will retrieve from the server
|
||||||
|
#! the HTML generated by the output of 'div-quot' wrapped
|
||||||
|
#! in a 'div' tag with the 'div-id'. That 'div' tag will
|
||||||
|
#! replace whatever HTML DOM object currently has that same
|
||||||
|
#! id.
|
||||||
|
<script language= "JavaScript" script>
|
||||||
|
"document.getElementById('" write
|
||||||
|
write
|
||||||
|
"').onclick=liveUpdaterUri('" write
|
||||||
|
register-live-anchor-quot write
|
||||||
|
"');" write
|
||||||
|
</script> ;
|
||||||
|
|
||||||
|
: live-anchor ( id quot text -- )
|
||||||
|
#! Write out the HTML for an anchor that when clicked
|
||||||
|
#! will replace the DOM object on the current page with
|
||||||
|
#! the given 'id' with the result of the output of calling
|
||||||
|
#! 'quot'.
|
||||||
|
write-live-anchor-tag
|
||||||
|
write-live-anchor-script ;
|
||||||
|
|
||||||
|
: write-live-search-tag ( -- id )
|
||||||
|
#! Write out the HTML for the input box. This
|
||||||
|
#! will have no actionable keydown assigned to it. Instead
|
||||||
|
#! a keydown is set via DHTML later to make it run a
|
||||||
|
#! quotation on the server. The randomly generated id
|
||||||
|
#! for the input box is returned.
|
||||||
|
<input id= get-random-id dup type= "text" input/> ;
|
||||||
|
|
||||||
|
: register-live-search-quot ( div-id div-quot -- kid )
|
||||||
|
#! Register the 'quot' with the cont-responder so
|
||||||
|
#! that when it is run it will produce an HTML
|
||||||
|
#! fragment which is the output generated by calling
|
||||||
|
#! 'quot'. That HTML fragment will be wrapped in a
|
||||||
|
#! 'div' with the given id. The 'quot' is called with
|
||||||
|
#! a string on top of the stack. This is the input string
|
||||||
|
#! entered in the live search input box.
|
||||||
|
<namespace> [
|
||||||
|
"div-quot" set
|
||||||
|
"div-id" set
|
||||||
|
] extend [
|
||||||
|
[
|
||||||
|
t "disable-initial-redirect?" set
|
||||||
|
#! Retrieve the search query value from the POST parameters.
|
||||||
|
[ "s" get ] bind
|
||||||
|
[
|
||||||
|
#! Don't need the URL as the 'show' won't be resumed.
|
||||||
|
drop
|
||||||
|
<div id= "div-id" get div> "div-quot" get call </div>
|
||||||
|
] show
|
||||||
|
] bind
|
||||||
|
] cons t swap register-continuation ;
|
||||||
|
|
||||||
|
: write-live-search-script ( div-id div-quot id-id -- )
|
||||||
|
#! Write the javascript that will attach the keydown handler
|
||||||
|
#! to the input box with the give id. Whenever a keydown is
|
||||||
|
#! received the 'div-quot' will be executed on the server,
|
||||||
|
#! with the input boxes text on top of the stack. The
|
||||||
|
#! output of the quot will be an HTML fragment, it is wrapped in
|
||||||
|
#! a 'div' with the id 'div-id' and will
|
||||||
|
#! replace whatever HTML DOM object currently has that same
|
||||||
|
#! id.
|
||||||
|
<script language= "JavaScript" script>
|
||||||
|
"liveSearch('" write
|
||||||
|
write
|
||||||
|
"', '" write
|
||||||
|
register-live-search-quot write
|
||||||
|
"');" write
|
||||||
|
</script> ;
|
||||||
|
|
||||||
|
: live-search ( div-id div-quot -- )
|
||||||
|
#! Write an input text field. The keydown of this
|
||||||
|
#! text field will run 'div-quot' on the server with
|
||||||
|
#! the value of the text field on the stack. The output
|
||||||
|
#! of div-quot will replace the HTML DOM object with the
|
||||||
|
#! given id.
|
||||||
|
write-live-search-tag
|
||||||
|
write-live-search-script ;
|
|
@ -0,0 +1,53 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
!
|
||||||
|
! Start an httpd server and some words to re-load the continuation
|
||||||
|
! server files.
|
||||||
|
USE: httpd-responder
|
||||||
|
USE: httpd
|
||||||
|
USE: threads
|
||||||
|
USE: stack
|
||||||
|
USE: prettyprint
|
||||||
|
USE: combinators
|
||||||
|
USE: errors
|
||||||
|
USE: stdio
|
||||||
|
|
||||||
|
USE: parser
|
||||||
|
|
||||||
|
: l1
|
||||||
|
"../parser-combinators/lazy.factor" run-file
|
||||||
|
"../parser-combinators/parser-combinators.factor" run-file
|
||||||
|
"cont-html.factor" run-file
|
||||||
|
"cont-responder.factor" run-file
|
||||||
|
"cont-utils.factor" run-file ;
|
||||||
|
: l2
|
||||||
|
"cont-examples.factor" run-file ;
|
||||||
|
: l3 "todo.factor" run-file ;
|
||||||
|
: l4 "todo-example.factor" run-file ;
|
||||||
|
: l5 "live-updater.factor" run-file ;
|
||||||
|
: l6 "eval-responder.factor" run-file ;
|
||||||
|
: l7 "live-updater-responder.factor" run-file ;
|
||||||
|
: l8 "browser.factor" run-file ;
|
||||||
|
: la ;
|
||||||
|
: la [ 8888 httpd ] [ dup . flush [ la ] when* ] catch ;
|
||||||
|
! : lb [ la "httpd thread exited.\n" write flush ] in-thread ;
|
|
@ -0,0 +1,482 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
!
|
||||||
|
! A simple 'to-do list' web application.
|
||||||
|
!
|
||||||
|
! Users can register with the system and from there manage a simple
|
||||||
|
! list of things to do. All data is stored in a directory in the
|
||||||
|
! filesystem with the users name.
|
||||||
|
IN: todo-example
|
||||||
|
USE: cont-responder
|
||||||
|
USE: cont-html
|
||||||
|
USE: cont-utils
|
||||||
|
USE: html
|
||||||
|
USE: stdio
|
||||||
|
USE: stack
|
||||||
|
USE: strings
|
||||||
|
USE: namespaces
|
||||||
|
USE: inspector
|
||||||
|
USE: lists
|
||||||
|
USE: combinators
|
||||||
|
USE: cont-examples
|
||||||
|
USE: regexp
|
||||||
|
USE: prettyprint
|
||||||
|
USE: todo
|
||||||
|
USE: math
|
||||||
|
USE: logic
|
||||||
|
USE: kernel
|
||||||
|
USE: lazy
|
||||||
|
USE: parser-combinators
|
||||||
|
|
||||||
|
: todo-stylesheet ( -- string )
|
||||||
|
#! Return the stylesheet for the todo list
|
||||||
|
<%
|
||||||
|
"table.list {" %
|
||||||
|
" text-align:center;" %
|
||||||
|
" font-family: Verdana;" %
|
||||||
|
" font-weight: normal;" %
|
||||||
|
" font-size: 11px;" %
|
||||||
|
" color: #404040;" %
|
||||||
|
" background-color: #fafafa;" %
|
||||||
|
" border: 1px #6699cc solid;" %
|
||||||
|
" border-collapse: collapse;" %
|
||||||
|
" boder-spacing: 0px;" %
|
||||||
|
"}" %
|
||||||
|
"tr.heading {" %
|
||||||
|
" border-bottom: 2px solid #6699cc;" %
|
||||||
|
" border-left: 1px solix #6699cc;" %
|
||||||
|
" background-color: #BEC8D1;" %
|
||||||
|
" text-align: left;" %
|
||||||
|
" text-indent: 0px;" %
|
||||||
|
" font-family: verdana;" %
|
||||||
|
" font-weight: bold;" %
|
||||||
|
" color: #404040;" %
|
||||||
|
"}" %
|
||||||
|
"tr.item {" %
|
||||||
|
" border-bottom: 1px solid #9cf;" %
|
||||||
|
" border-top: 0px;" %
|
||||||
|
" border-left: 1px solid #9cf;" %
|
||||||
|
" border-right: 0px;" %
|
||||||
|
" text-align: left;" %
|
||||||
|
" text-indent: 2px;" %
|
||||||
|
" font-family: verdana, sans-serif, arial;" %
|
||||||
|
" font-weight: normal;" %
|
||||||
|
" color: #404040;" %
|
||||||
|
" background-color: #fafafa;" %
|
||||||
|
"}" %
|
||||||
|
"tr.complete {" %
|
||||||
|
" border-bottom: 1px solid #9cf;" %
|
||||||
|
" border-top: 0px;" %
|
||||||
|
" border-left: 1px solid #9cf;" %
|
||||||
|
" border-right: 0px;" %
|
||||||
|
" text-align: left;" %
|
||||||
|
" text-indent: 2px;" %
|
||||||
|
" font-family: verdana, sans-serif, arial;" %
|
||||||
|
" font-weight: normal;" %
|
||||||
|
" color: #404040;" %
|
||||||
|
" background-color: #ccc;" %
|
||||||
|
"}" %
|
||||||
|
"td.lbl {" %
|
||||||
|
" font-weight: bold; text-align: right;" %
|
||||||
|
"}" %
|
||||||
|
"tr.required {" %
|
||||||
|
" background: #FCC;" %
|
||||||
|
"}" %
|
||||||
|
"input:focus {" %
|
||||||
|
" background: yellow;" %
|
||||||
|
"}" %
|
||||||
|
"textarea:focus {" %
|
||||||
|
" background: yellow;" %
|
||||||
|
"}" %
|
||||||
|
%> ;
|
||||||
|
|
||||||
|
: todo-stylesheet-url ( -- url )
|
||||||
|
#! Generate an URL for the stylesheet.
|
||||||
|
t [ [ drop todo-stylesheet write ] show ] register-continuation ;
|
||||||
|
|
||||||
|
: include-todo-stylesheet ( -- )
|
||||||
|
#! Generate HTML to include the todo stylesheet
|
||||||
|
<link rel= "stylesheet" href= todo-stylesheet-url link/> ;
|
||||||
|
|
||||||
|
: show-stack-page ( -- )
|
||||||
|
#! Debug function to show a page containing the current call stack.
|
||||||
|
[ .s ] with-string-stream chars>entities show-message-page ;
|
||||||
|
|
||||||
|
: row ( list -- )
|
||||||
|
#! Output an html TR row with each element of the list
|
||||||
|
#! being called to produce the output for each TD.
|
||||||
|
<tr>
|
||||||
|
[ <td> call </td> ] each
|
||||||
|
</tr> ;
|
||||||
|
|
||||||
|
: styled-row ( class list -- )
|
||||||
|
#! Output an html TR row with each element of the list
|
||||||
|
#! being called to produce the output for each TD.
|
||||||
|
<tr class= swap tr>
|
||||||
|
[ <td> call </td> ] each
|
||||||
|
</tr> ;
|
||||||
|
|
||||||
|
: simple-input ( name -- )
|
||||||
|
#! Output a simple HTML input field which will have the
|
||||||
|
#! specified name.
|
||||||
|
<input type= "text" size= "20" name= input/> ;
|
||||||
|
|
||||||
|
: simple-input-with-value ( name value -- )
|
||||||
|
#! Output a simple HTML input field which will have the
|
||||||
|
#! specified name and value.
|
||||||
|
<input type= "text" size= "20" value= name= input/> ;
|
||||||
|
|
||||||
|
: textarea-input ( name -- )
|
||||||
|
#! Output a simple HTML textarea field which will have the
|
||||||
|
#! specified name.
|
||||||
|
<textarea name= rows= "10" cols= "40" textarea> "Enter description here." write </textarea> ;
|
||||||
|
|
||||||
|
: textarea-input-with-value ( name value -- )
|
||||||
|
#! Output a simple HTML textarea field which will have the
|
||||||
|
#! specified name and value.
|
||||||
|
<textarea name= swap rows= "10" cols= "40" textarea> write </textarea> ;
|
||||||
|
|
||||||
|
: password-input ( name -- )
|
||||||
|
#! Output an HTML password input field which will have the
|
||||||
|
#! specified name.
|
||||||
|
<input type= "password" size= "20" name= input/> ;
|
||||||
|
|
||||||
|
: form ( action quot -- )
|
||||||
|
#! Call quot with any output appearing inside an HTML form.
|
||||||
|
#! The form is a POST form where the action is as specified.
|
||||||
|
<form method= "post" action= swap form> call </form> ;
|
||||||
|
|
||||||
|
: input-value ( name -- value )
|
||||||
|
#! Get the value of the variable "name". If it is f
|
||||||
|
#! return "" else return the value.
|
||||||
|
get [ "" ] unless* ;
|
||||||
|
|
||||||
|
: login-form ( url button-text -- )
|
||||||
|
#! Write the HTML for an HTML form requesting a username
|
||||||
|
#! and password. The 'accept' button has the text given
|
||||||
|
#! in 'button-text'. The form will go to the given URL on
|
||||||
|
#! submission.
|
||||||
|
swap [
|
||||||
|
<table>
|
||||||
|
[ [ "Name:" write ] [ "name" simple-input ] ] row
|
||||||
|
[ [ "Password:" write ] [ "password" password-input ] ] row
|
||||||
|
</table>
|
||||||
|
button
|
||||||
|
] form ;
|
||||||
|
|
||||||
|
: registration-page ( submit-url -- )
|
||||||
|
#! Write the HTML for the registration page to std output.
|
||||||
|
"Register New TODO List" [
|
||||||
|
"Enter the username and password for your new todo list:" paragraph
|
||||||
|
"Register" login-form
|
||||||
|
] simple-page ;
|
||||||
|
|
||||||
|
: username-parser ( -- parser )
|
||||||
|
#! Return a parser which parses a valid todo username.
|
||||||
|
#! That is, it contains only lowercase, uppercase and digits.
|
||||||
|
[ letter? ] satisfy
|
||||||
|
[ LETTER? ] satisfy <|>
|
||||||
|
[ digit? ] satisfy <|> <!+> just ;
|
||||||
|
|
||||||
|
: is-valid-username? ( password -- bool )
|
||||||
|
#! Return true if the username parses correctly
|
||||||
|
username-parser call ;
|
||||||
|
|
||||||
|
: login-details-valid? ( name password -- )
|
||||||
|
#! Ensure that a valid username and password were
|
||||||
|
#! entered. In particular, ensure that only alphanumeric
|
||||||
|
#! data was entered to prevent security problems by
|
||||||
|
#! using .., etc in the name.
|
||||||
|
drop is-valid-username? ;
|
||||||
|
|
||||||
|
: get-registration-details ( -- name password )
|
||||||
|
#! Get the registration details from the user putting
|
||||||
|
#! the name and password on the stack.
|
||||||
|
[ registration-page ] show [
|
||||||
|
"name" get "password" get
|
||||||
|
] bind 2dup login-details-valid? [
|
||||||
|
2drop
|
||||||
|
"Please ensure you enter a username containing letters and numbers only." show-message-page
|
||||||
|
get-registration-details
|
||||||
|
] unless ;
|
||||||
|
|
||||||
|
: get-todo-filename ( database-path <todo> -- filename )
|
||||||
|
#! Get the filename containing the todo list details.
|
||||||
|
<% swap % todo-username % ".todo" % %> ;
|
||||||
|
|
||||||
|
: add-default-todo-item ( <todo> -- )
|
||||||
|
#! Add a default todo item. This is a workaround for the
|
||||||
|
#! currently hackish method of saving todo lists which can't
|
||||||
|
#! handle empty lists.
|
||||||
|
"1" "Set up todo list" <todo-item> add-todo-item ;
|
||||||
|
|
||||||
|
: init-new-todo ( <todo> -- )
|
||||||
|
#! Add the default todo item and store the todo list to
|
||||||
|
#! persistent storage.
|
||||||
|
dup add-default-todo-item
|
||||||
|
dup "database-path" get swap get-todo-filename store-todo ;
|
||||||
|
|
||||||
|
: register-new-user ( -- )
|
||||||
|
#! Get registration details for a new user and add a
|
||||||
|
#! todo list for them.
|
||||||
|
get-registration-details
|
||||||
|
2dup "database-path" get -rot user-exists? [
|
||||||
|
2drop
|
||||||
|
"That user already exists in the system, sorry. Please use another name."
|
||||||
|
show-message-page
|
||||||
|
register-new-user
|
||||||
|
] [
|
||||||
|
<todo> init-new-todo
|
||||||
|
"You have successfully registered your todo list." show-message-page
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: login-request-paragraph ( -- )
|
||||||
|
#! Display the paragraph requesting the user to login or register.
|
||||||
|
<p>
|
||||||
|
"Please enter your username and password (" write
|
||||||
|
"Click to Register" [ register-new-user ] quot-href
|
||||||
|
"):" write
|
||||||
|
</p> ;
|
||||||
|
|
||||||
|
: get-login-information ( -- user password )
|
||||||
|
[
|
||||||
|
"Login" [
|
||||||
|
login-request-paragraph
|
||||||
|
"Login" login-form
|
||||||
|
] simple-page
|
||||||
|
] show [
|
||||||
|
"name" get "password" get
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: ensure-login-valid ( user password -- user password )
|
||||||
|
2dup login-details-valid? [
|
||||||
|
"Please ensure you enter a username containing letters and numbers only." show-message-page
|
||||||
|
get-login-information
|
||||||
|
] unless ;
|
||||||
|
|
||||||
|
: get-todo-list ( -- <todo> )
|
||||||
|
#! Prompts for a username or password until a valid combination
|
||||||
|
#! is entered then returns the <todo> list for that user.
|
||||||
|
get-login-information ensure-login-valid
|
||||||
|
"database-path" get -rot user-exists? [
|
||||||
|
"Sorry, your username or password was incorrect." show-message-page
|
||||||
|
get-todo-list
|
||||||
|
] unless* ;
|
||||||
|
|
||||||
|
: write-new-todo-item-form ( url -- )
|
||||||
|
#! Display the HTML for a form allowing entry of a
|
||||||
|
#! todo item details.
|
||||||
|
[
|
||||||
|
<table>
|
||||||
|
<tr class= "required" tr>
|
||||||
|
<td class= "lbl" td> "Priority" write </td>
|
||||||
|
<td> "priority" simple-input </td>
|
||||||
|
</tr>
|
||||||
|
<tr class= "required" tr>
|
||||||
|
<td class= "lbl" td> "Description" write </td>
|
||||||
|
<td> "description" textarea-input </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
"Add" button
|
||||||
|
] form ;
|
||||||
|
|
||||||
|
: write-edit-todo-item-form ( item url -- )
|
||||||
|
#! Display the HTML for a form allowing editing of a
|
||||||
|
#! todo item details.
|
||||||
|
swap [
|
||||||
|
[
|
||||||
|
<table>
|
||||||
|
<tr class= "required" tr>
|
||||||
|
<td class= "lbl" td> "Priority" write </td>
|
||||||
|
<td> "priority" dup get simple-input-with-value </td>
|
||||||
|
</tr>
|
||||||
|
<tr class= "required" tr>
|
||||||
|
<td class= "lbl" td> "Description" write </td>
|
||||||
|
<td> "description" dup get textarea-input-with-value </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
"Save" button
|
||||||
|
] form
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: priority-parser ( -- parser )
|
||||||
|
#! Return a parser for parsing priorities
|
||||||
|
[ digit? ] satisfy just ;
|
||||||
|
|
||||||
|
: todo-details-valid? ( priority description -- bool )
|
||||||
|
#! Return true if a valid priority and description were entered.
|
||||||
|
str-length 0 > swap priority-parser call and ;
|
||||||
|
|
||||||
|
: get-new-todo-item ( -- <todo-item> )
|
||||||
|
#! Enter a new item to the current todo list.
|
||||||
|
[
|
||||||
|
"Enter New Todo Item" [ include-todo-stylesheet ] [ write-new-todo-item-form ] styled-page
|
||||||
|
] show [
|
||||||
|
"priority" get "description" get
|
||||||
|
] bind 2dup todo-details-valid? [
|
||||||
|
<todo-item>
|
||||||
|
] [
|
||||||
|
2drop
|
||||||
|
"Please ensure you enter a Priority from 0-9 and a description." show-message-page
|
||||||
|
get-new-todo-item
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: write-get-password-form ( url -- )
|
||||||
|
#! Display the HTML for a form allowing entry of a
|
||||||
|
#! new password.
|
||||||
|
[
|
||||||
|
<table>
|
||||||
|
<tr class= "required" tr>
|
||||||
|
<td class= "lbl" td> "Old Password" write </td>
|
||||||
|
<td> "old-password" password-input </td>
|
||||||
|
</tr>
|
||||||
|
<tr class= "required" tr>
|
||||||
|
<td class= "lbl" td> "New Password" write </td>
|
||||||
|
<td> "new-password" password-input </td>
|
||||||
|
</tr>
|
||||||
|
<tr class= "required" tr>
|
||||||
|
<td class= "lbl" td> "Verify Password" write </td>
|
||||||
|
<td> "verify-password" password-input </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
"Change Password" button
|
||||||
|
] form ;
|
||||||
|
|
||||||
|
: get-new-password ( <todo> -- password )
|
||||||
|
#! Get a new password for the todo list.
|
||||||
|
[
|
||||||
|
"Enter New Password" [ include-todo-stylesheet ] [ write-get-password-form ] styled-page
|
||||||
|
] show [
|
||||||
|
"old-password" get
|
||||||
|
swap password-matches? [
|
||||||
|
"new-password" get
|
||||||
|
"verify-password" get = [
|
||||||
|
"new-password" get
|
||||||
|
] [
|
||||||
|
"Your new password did not match. The password was NOT changed." show-message-page
|
||||||
|
f
|
||||||
|
] ifte
|
||||||
|
] [
|
||||||
|
"You entered an incorrect old password. The password was NOT changed." show-message-page
|
||||||
|
f
|
||||||
|
] ifte
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: edit-item-details ( item -- )
|
||||||
|
#! Allow editing of an existing items details.
|
||||||
|
[
|
||||||
|
"Edit Item" [ include-todo-stylesheet ] [ write-edit-todo-item-form ] styled-page
|
||||||
|
] show [
|
||||||
|
"priority" get "description" get
|
||||||
|
] bind 2dup todo-details-valid? [
|
||||||
|
rot [ "description" set "priority" set ] bind
|
||||||
|
] [
|
||||||
|
drop drop
|
||||||
|
"Please ensure you enter a Priority from 0-9 and a description." show-message-page
|
||||||
|
edit-item-details
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: save-current-todo ( -- )
|
||||||
|
#! Save the current todo list
|
||||||
|
"database-path" get "todo" get get-todo-filename "todo" get swap store-todo ;
|
||||||
|
|
||||||
|
: lcurry1 ( value quot -- quot )
|
||||||
|
#! Return a quotation that when called will have 'value'
|
||||||
|
#! as the first item on the stack.
|
||||||
|
cons ;
|
||||||
|
|
||||||
|
: write-mark-complete-action ( item -- )
|
||||||
|
#! Write out HTML to perform a mark complete
|
||||||
|
#! action on an item (or other appropriate
|
||||||
|
#! action if already complete).
|
||||||
|
dup item-complete? [
|
||||||
|
"Delete" swap [ "todo" get swap delete-item save-current-todo ] lcurry1 quot-href
|
||||||
|
] [
|
||||||
|
"Complete" swap [ set-item-completed save-current-todo ] lcurry1 quot-href
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: write-edit-action ( item -- )
|
||||||
|
#! Write out html to allow editing an item.
|
||||||
|
"Edit" swap [ edit-item-details save-current-todo ] lcurry1 quot-href ;
|
||||||
|
|
||||||
|
: item-class ( <todo-item> -- string )
|
||||||
|
#! Return the class to use for displaying the row for the
|
||||||
|
#! item.
|
||||||
|
item-complete? [ "complete" ] [ "item" ] ifte ;
|
||||||
|
|
||||||
|
: write-item-row ( <todo-item> -- )
|
||||||
|
#! Write the todo list item as an HTML row.
|
||||||
|
dup dup dup dup
|
||||||
|
dup item-class [
|
||||||
|
[ item-priority write ]
|
||||||
|
[ item-complete? [ "Yes" ] [ "No" ] ifte write ]
|
||||||
|
[ item-description write ]
|
||||||
|
[ write-mark-complete-action ]
|
||||||
|
[ write-edit-action ]
|
||||||
|
] styled-row ;
|
||||||
|
|
||||||
|
: write-item-table ( <todo> -- )
|
||||||
|
#! Write the table of items for the todo list.
|
||||||
|
<table>
|
||||||
|
"heading" [
|
||||||
|
[ "Priority" write ] [ "Complete?" write ] [ "Description" write ] [ "Action" write ] [ " " write ]
|
||||||
|
] styled-row
|
||||||
|
todo-items [ write-item-row ] each
|
||||||
|
</table> ;
|
||||||
|
|
||||||
|
: do-add-new-item ( -- )
|
||||||
|
#! Request a new item from the user and add it to the current todo list.
|
||||||
|
"todo" get get-new-todo-item add-todo-item save-current-todo ;
|
||||||
|
|
||||||
|
: do-change-password ( -- )
|
||||||
|
#! Allow changing the password for the todo list.
|
||||||
|
"todo" get get-new-password dup [
|
||||||
|
"todo" get [ "password" set ] bind save-current-todo
|
||||||
|
"Your password has been changed." show-message-page
|
||||||
|
] [
|
||||||
|
drop
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: show-todo-list ( -- )
|
||||||
|
#! Show the current todo list.
|
||||||
|
[
|
||||||
|
<% "todo" get todo-username % "'s To Do list" % %>
|
||||||
|
[ include-todo-stylesheet ]
|
||||||
|
[
|
||||||
|
drop
|
||||||
|
"todo" get write-item-table
|
||||||
|
[
|
||||||
|
[ "Add Item" [ do-add-new-item ] quot-href ]
|
||||||
|
[ "Change Password" [ do-change-password ] quot-href ]
|
||||||
|
] horizontal-layout
|
||||||
|
] styled-page
|
||||||
|
] show drop ;
|
||||||
|
|
||||||
|
: todo-example ( path -- )
|
||||||
|
#! Startup the todo list example using the given path as the
|
||||||
|
#! directory holding the todo files.
|
||||||
|
"database-path" set
|
||||||
|
get-todo-list "todo" set
|
||||||
|
show-todo-list ;
|
||||||
|
|
||||||
|
"todo" [ drop "todo/" todo-example ] install-cont-responder
|
|
@ -0,0 +1,169 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
!
|
||||||
|
! Routines for managing a simple "To Do list". A todo list has a 'user', 'password'
|
||||||
|
! and list of items. Each item has a priority, description, and indication if it is
|
||||||
|
! complete.
|
||||||
|
IN: todo
|
||||||
|
USE: parser
|
||||||
|
USE: stack
|
||||||
|
USE: strings
|
||||||
|
USE: streams
|
||||||
|
USE: namespaces
|
||||||
|
USE: lists
|
||||||
|
USE: math
|
||||||
|
USE: stdio
|
||||||
|
USE: kernel
|
||||||
|
USE: prettyprint
|
||||||
|
USE: unparser
|
||||||
|
USE: url-encoding
|
||||||
|
USE: combinators
|
||||||
|
USE: files
|
||||||
|
|
||||||
|
|
||||||
|
: <todo> ( user password -- <todo> )
|
||||||
|
#! Create an empty todo list
|
||||||
|
<namespace> [
|
||||||
|
"password" set
|
||||||
|
"user" set
|
||||||
|
f "items" set
|
||||||
|
] extend ;
|
||||||
|
|
||||||
|
: <todo-item> ( priority description -- )
|
||||||
|
#! Create a todo item
|
||||||
|
<namespace> [
|
||||||
|
"description" set
|
||||||
|
"priority" set
|
||||||
|
f "complete?" set
|
||||||
|
] extend ;
|
||||||
|
|
||||||
|
: add-todo-item ( <todo> <item> -- )
|
||||||
|
#! Add the item to the todo list
|
||||||
|
swap [
|
||||||
|
"items" add@
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: namespace>alist ( namespace -- alist )
|
||||||
|
#! Convert a namespace to an alist
|
||||||
|
[ vars-values ] bind ;
|
||||||
|
|
||||||
|
: print-quoted ( str -- )
|
||||||
|
#! Print the string with quotes around it
|
||||||
|
"\"" write write "\"" print ;
|
||||||
|
|
||||||
|
: write-item ( <todo-item> -- )
|
||||||
|
#! write the item in a manner that can be later re-read
|
||||||
|
[
|
||||||
|
"complete?" get [ "yes" url-encode print ] [ "no" url-encode print ] ifte
|
||||||
|
"priority" get url-encode print
|
||||||
|
"description" get url-encode print
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: write-items ( list -- )
|
||||||
|
#! write the todo list items
|
||||||
|
dup length unparse print
|
||||||
|
[ write-item ] each ;
|
||||||
|
|
||||||
|
: write-todo ( <todo> -- )
|
||||||
|
#! Write the todo list to the current output stream
|
||||||
|
#! in a format that if loaded by the parser will result
|
||||||
|
#! in a <todo> again.
|
||||||
|
[
|
||||||
|
"user" get url-encode print
|
||||||
|
"password" get url-encode print
|
||||||
|
"items" get write-items
|
||||||
|
] bind ;
|
||||||
|
|
||||||
|
: store-todo ( <todo> filename -- )
|
||||||
|
#! store the todo list in the given file.
|
||||||
|
<filecw> [ write-todo ] with-stream ;
|
||||||
|
|
||||||
|
: read-todo ( -- <todo> )
|
||||||
|
#! Read a todo list from the current input stream.
|
||||||
|
read url-decode read url-decode <todo>
|
||||||
|
read str>number [
|
||||||
|
dup
|
||||||
|
<namespace> [
|
||||||
|
read url-decode "yes" = "complete?" set
|
||||||
|
read url-decode "priority" set
|
||||||
|
read url-decode "description" set
|
||||||
|
] extend add-todo-item
|
||||||
|
] times ;
|
||||||
|
|
||||||
|
: load-todo ( filename -- <todo> )
|
||||||
|
<filecr> [ read-todo ] with-stream ;
|
||||||
|
|
||||||
|
: password-matches? ( password <todo> -- <todo> )
|
||||||
|
#! Returns the <todo> if the password matches otherwise
|
||||||
|
#! returns false.
|
||||||
|
dup -rot [ "password" get ] bind = [ ] [ drop f ] ifte ;
|
||||||
|
|
||||||
|
: user-exists? ( db-path name password -- <todo> )
|
||||||
|
#! Returns a <todo> if a user with the given name exists
|
||||||
|
#! otherwise returns false.
|
||||||
|
-rot ".todo" cat3 dup exists? [
|
||||||
|
load-todo password-matches?
|
||||||
|
] [
|
||||||
|
2drop f
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: items-each-bind ( quot -- )
|
||||||
|
#! For each item in the currently bound todo list, call the quotation
|
||||||
|
#! with that item bound.
|
||||||
|
unit [ bind ] append "items" get swap each ;
|
||||||
|
|
||||||
|
: todo-username ( <todo> -- username )
|
||||||
|
#! return the username for the todo list item.
|
||||||
|
[ "user" get ] bind ;
|
||||||
|
|
||||||
|
: item-priority ( <todo-item> -- priority )
|
||||||
|
#! return the priority for the todo list item.
|
||||||
|
[ "priority" get ] bind ;
|
||||||
|
|
||||||
|
: item-complete? ( <todo-item> -- boolean )
|
||||||
|
#! return true if the todo list item is completed.
|
||||||
|
[ "complete?" get ] bind ;
|
||||||
|
|
||||||
|
: set-item-completed ( <todo-item> -- )
|
||||||
|
[ t "complete?" set ] bind ;
|
||||||
|
|
||||||
|
: item-description ( <todo-item> -- description )
|
||||||
|
#! return the description for the todo list item.
|
||||||
|
[ "description" get ] bind ;
|
||||||
|
|
||||||
|
: priority-comparator ( item1 item2 -- bool )
|
||||||
|
#! Return true if item1 is a higher priority than item2
|
||||||
|
>r item-priority r> item-priority str-lexi> ;
|
||||||
|
|
||||||
|
: todo-items ( <todo> -- alist )
|
||||||
|
#! Return a list of items for the given todo list.
|
||||||
|
[ "items" get ] bind [ priority-comparator ] sort ;
|
||||||
|
|
||||||
|
: delete-item ( <todo> <todo-item> -- )
|
||||||
|
#! Delete the item from the todo list
|
||||||
|
swap dup >r todo-items remove r> [ "items" set ] bind ;
|
||||||
|
|
||||||
|
: test-todo
|
||||||
|
"user" "password" <todo>
|
||||||
|
dup "1" "item1" <todo-item> add-todo-item
|
||||||
|
dup "2" "item2" <todo-item> add-todo-item ;
|
|
@ -0,0 +1,152 @@
|
||||||
|
! :folding=indent:collapseFolds=1:
|
||||||
|
|
||||||
|
! $Id$
|
||||||
|
!
|
||||||
|
! Copyright (C) 2004 Slava Pestov.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
IN: irc
|
||||||
|
USE: arithmetic
|
||||||
|
USE: combinators
|
||||||
|
USE: errors
|
||||||
|
USE: inspector
|
||||||
|
USE: interpreter
|
||||||
|
USE: kernel
|
||||||
|
USE: lists
|
||||||
|
USE: logic
|
||||||
|
USE: math
|
||||||
|
USE: namespaces
|
||||||
|
USE: parser
|
||||||
|
USE: prettyprint
|
||||||
|
USE: stack
|
||||||
|
USE: stdio
|
||||||
|
USE: streams
|
||||||
|
USE: strings
|
||||||
|
USE: words
|
||||||
|
USE: unparser
|
||||||
|
|
||||||
|
: irc-register ( -- )
|
||||||
|
"USER " write
|
||||||
|
"user" get write " " write
|
||||||
|
"host" get write " " write
|
||||||
|
"server" get write " " write
|
||||||
|
"realname" get write " " print
|
||||||
|
|
||||||
|
"NICK " write
|
||||||
|
"nick" get print ;
|
||||||
|
|
||||||
|
: irc-join ( channel -- )
|
||||||
|
"JOIN " write print ;
|
||||||
|
|
||||||
|
: irc-message ( message recepients -- )
|
||||||
|
"PRIVMSG " write write " :" write print ;
|
||||||
|
|
||||||
|
: irc-action ( message recepients -- )
|
||||||
|
"ACTION " write write " :" write print ;
|
||||||
|
|
||||||
|
: keep-datastack ( quot -- )
|
||||||
|
datastack [ call ] dip set-datastack drop ;
|
||||||
|
|
||||||
|
: irc-stream-write ( string -- )
|
||||||
|
dup "buf" get sbuf-append
|
||||||
|
ends-with-newline? [
|
||||||
|
"buf" get sbuf>str
|
||||||
|
0 "buf" get set-sbuf-length
|
||||||
|
"\n" split [ dup f-or-"" [ drop ] [ "recepient" get irc-message ] ifte ] each
|
||||||
|
] when ;
|
||||||
|
|
||||||
|
: <irc-stream> ( stream recepient -- stream )
|
||||||
|
<stream> [
|
||||||
|
"recepient" set
|
||||||
|
"stdio" set
|
||||||
|
100 <sbuf> "buf" set
|
||||||
|
[
|
||||||
|
irc-stream-write
|
||||||
|
] "fwrite" set
|
||||||
|
] extend ;
|
||||||
|
|
||||||
|
: irc-eval ( line -- )
|
||||||
|
[
|
||||||
|
[
|
||||||
|
eval
|
||||||
|
] [
|
||||||
|
default-error-handler
|
||||||
|
] catch
|
||||||
|
] keep-datastack drop ;
|
||||||
|
|
||||||
|
: with-irc-stream ( recepient quot -- )
|
||||||
|
[
|
||||||
|
[ "stdio" get swap <irc-stream> "stdio" set ] dip
|
||||||
|
call
|
||||||
|
] with-scope ;
|
||||||
|
|
||||||
|
: irc-action-quot ( action -- quot )
|
||||||
|
[
|
||||||
|
[ "eval" swap [ irc-eval ] with-irc-stream ]
|
||||||
|
[ "see" swap [ see terpri ] with-irc-stream ]
|
||||||
|
[ "join" nip irc-join ]
|
||||||
|
[ "quit" 2drop global [ "irc-quit-flag" on ] bind ]
|
||||||
|
] assoc [ [ 2drop ] ] unless* ;
|
||||||
|
|
||||||
|
: irc-action-handler ( recepient message -- )
|
||||||
|
" " split1 swap irc-action-quot call ;
|
||||||
|
|
||||||
|
: irc-input ( line -- )
|
||||||
|
#! Handle a line of IRC input.
|
||||||
|
dup
|
||||||
|
" PRIVMSG " split1 nip [
|
||||||
|
":" split1 dup [
|
||||||
|
irc-action-handler
|
||||||
|
] [
|
||||||
|
drop
|
||||||
|
] ifte
|
||||||
|
] when*
|
||||||
|
|
||||||
|
global [ print ] bind ;
|
||||||
|
|
||||||
|
: irc-quit-flag ( -- ? )
|
||||||
|
global [ "irc-quit-flag" get ] bind ;
|
||||||
|
|
||||||
|
: clear-irc-quit-flag ( -- ? )
|
||||||
|
global [ "irc-quit-flag" off ] bind ;
|
||||||
|
|
||||||
|
: irc-loop ( -- )
|
||||||
|
irc-quit-flag [
|
||||||
|
read [ irc-input irc-loop ] when*
|
||||||
|
] unless clear-irc-quit-flag ;
|
||||||
|
|
||||||
|
: irc ( channels -- )
|
||||||
|
irc-register
|
||||||
|
"identify foobar" "NickServ" irc-message
|
||||||
|
[ irc-join ] each
|
||||||
|
irc-loop ;
|
||||||
|
|
||||||
|
: irc-test
|
||||||
|
"factorbot" "user" set
|
||||||
|
"emu" "host" set
|
||||||
|
"irc.freenode.net" "server" set
|
||||||
|
"Factor" "realname" set
|
||||||
|
"factorbot" "nick" set
|
||||||
|
"irc.freenode.net" 6667 <client> [
|
||||||
|
[ "#factor" ] irc
|
||||||
|
] with-stream ;
|
|
@ -0,0 +1,40 @@
|
||||||
|
! Based on lisp code from newsgroup discussion in
|
||||||
|
! comp.lang.lisp
|
||||||
|
|
||||||
|
! (loop for y from -1 to 1.1 by 0.1 do
|
||||||
|
! (loop for x from -2 to 1 by 0.04 do
|
||||||
|
! (let* ((c 126)
|
||||||
|
! (z (complex x y))
|
||||||
|
! (a z))
|
||||||
|
! (loop while (< (abs
|
||||||
|
! (setq z (+ (* z z) a)))
|
||||||
|
! 2)
|
||||||
|
! while (> (decf c) 32))
|
||||||
|
! (princ (code-char c))))
|
||||||
|
! (format t "~%"))
|
||||||
|
|
||||||
|
USE: combinators
|
||||||
|
USE: math
|
||||||
|
USE: prettyprint
|
||||||
|
USE: stack
|
||||||
|
USE: stdio
|
||||||
|
USE: strings
|
||||||
|
|
||||||
|
: ?mandel-step ( a z c -- a z c ? )
|
||||||
|
>r dupd sq + dup abs 2 < [
|
||||||
|
r> pred dup CHAR: \s >
|
||||||
|
] [
|
||||||
|
r> f
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: mandel-step ( a z c -- )
|
||||||
|
[ ?mandel-step ] [ ] while >char write 2drop ;
|
||||||
|
|
||||||
|
: mandel-x ( x y -- )
|
||||||
|
rect> dup CHAR: ~ mandel-step ;
|
||||||
|
|
||||||
|
: mandel-y ( y -- )
|
||||||
|
150 [ dupd 50 / 2 - >float swap mandel-x ] times* drop ;
|
||||||
|
|
||||||
|
: mandel ( -- )
|
||||||
|
42 [ 20 / 1 - >float mandel-y terpri ] times* ;
|
|
@ -0,0 +1,63 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
IN: lazy-examples
|
||||||
|
USE: lazy
|
||||||
|
USE: stack
|
||||||
|
USE: math
|
||||||
|
USE: lists
|
||||||
|
USE: combinators
|
||||||
|
USE: kernel
|
||||||
|
USE: logic
|
||||||
|
|
||||||
|
: lfrom ( n -- llist )
|
||||||
|
#! Return a lazy list of increasing numbers starting
|
||||||
|
#! from the initial value 'n'.
|
||||||
|
dup [ succ lfrom ] curry1 lcons ;
|
||||||
|
|
||||||
|
: lfrom-by ( n quot -- llist )
|
||||||
|
#! Return a lazy list of values starting from n, with
|
||||||
|
#! each successive value being the result of applying quot to
|
||||||
|
#! n.
|
||||||
|
dupd [ dup [ call ] dip lfrom-by ] curry2 lcons ;
|
||||||
|
|
||||||
|
: lnaturals 0 lfrom ;
|
||||||
|
: lpositves 1 lfrom ;
|
||||||
|
: levens 0 [ 2 + ] lfrom-by ;
|
||||||
|
: lodds 1 lfrom [ 2 mod 1 = ] lsubset ;
|
||||||
|
: lpowers-of-2 1 [ 2 * ] lfrom-by ;
|
||||||
|
: lones 1 [ ] lfrom-by ;
|
||||||
|
: lsquares lnaturals [ dup * ] lmap ;
|
||||||
|
: first-five-squares 5 lsquares ltake ;
|
||||||
|
|
||||||
|
: divisible-by? ( a b -- bool )
|
||||||
|
#! Return true if a is divisible by b
|
||||||
|
mod 0 = ;
|
||||||
|
|
||||||
|
: sieve ( llist - llist )
|
||||||
|
#! Given a lazy list of numbers, use the sieve of eratosthenes
|
||||||
|
#! algorithm to return a lazy list of primes.
|
||||||
|
luncons over [ divisible-by? not ] curry1 lsubset [ sieve ] curry1 lcons ;
|
||||||
|
|
||||||
|
: lprimes 2 lfrom sieve ;
|
||||||
|
|
||||||
|
: first-ten-primes 10 lprimes ltake ;
|
|
@ -0,0 +1,172 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
IN: lazy
|
||||||
|
USE: lists
|
||||||
|
USE: stack
|
||||||
|
USE: math
|
||||||
|
USE: stdio
|
||||||
|
USE: prettyprint
|
||||||
|
USE: kernel
|
||||||
|
USE: combinators
|
||||||
|
USE: logic
|
||||||
|
|
||||||
|
: curry1 ( n quot -- quot )
|
||||||
|
#! Return a quotation that when called will initially
|
||||||
|
#! have 'n' pushed on the stack.
|
||||||
|
cons ;
|
||||||
|
|
||||||
|
: curry2 ( n1 n2 quot -- quot )
|
||||||
|
#! Return a quotation that when called will initially
|
||||||
|
#! have 'n1' and 'n2' pushed on the stack.
|
||||||
|
cons cons ;
|
||||||
|
|
||||||
|
: delay ( value -- promise )
|
||||||
|
#! Return a promise that when 'forced' returns the original value.
|
||||||
|
unit ;
|
||||||
|
|
||||||
|
: force ( promise -- value )
|
||||||
|
#! Return the value associated with the promise.
|
||||||
|
call ;
|
||||||
|
|
||||||
|
: lcons ( car promise -- lcons )
|
||||||
|
#! Return a lazy pair, where the cdr is a promise and must
|
||||||
|
#! be forced to return the value.
|
||||||
|
cons ;
|
||||||
|
|
||||||
|
: lunit ( a -- llist )
|
||||||
|
#! Construct a lazy list of one element.
|
||||||
|
[ ] delay lcons ;
|
||||||
|
|
||||||
|
: lcar ( lcons -- car )
|
||||||
|
#! Return the car of a lazy pair.
|
||||||
|
car ;
|
||||||
|
|
||||||
|
: lcdr ( lcons -- cdr )
|
||||||
|
#! Return the cdr of a lazy pair, implicitly forcing it.
|
||||||
|
cdr force ;
|
||||||
|
|
||||||
|
: lnth ( n llist -- value )
|
||||||
|
#! Return the nth item in a lazy list
|
||||||
|
swap [ lcdr ] times lcar ;
|
||||||
|
|
||||||
|
: luncons ( lcons -- car cdr )
|
||||||
|
#! Return the car and forced cdr of the lazy cons.
|
||||||
|
uncons force ;
|
||||||
|
|
||||||
|
: (ltake) ( n llist accum -- list )
|
||||||
|
>r >r pred dup 0 < [
|
||||||
|
drop r> drop r> nreverse
|
||||||
|
] [
|
||||||
|
r> luncons swap r> cons (ltake)
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: ltake ( n llist -- list )
|
||||||
|
#! Return a list containing the first n items from
|
||||||
|
#! the lazy list.
|
||||||
|
[ ] (ltake) ;
|
||||||
|
|
||||||
|
: lmap ( llist quot -- llist )
|
||||||
|
#! Return a lazy list containing the collected result of calling
|
||||||
|
#! quot on the original lazy list.
|
||||||
|
over [ ] = [
|
||||||
|
2drop [ ]
|
||||||
|
] [
|
||||||
|
[ luncons ] dip
|
||||||
|
dup swapd
|
||||||
|
[ lmap ] curry2
|
||||||
|
[ call ] dip
|
||||||
|
lcons
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: lsubset ( llist pred -- llist )
|
||||||
|
#! Return a lazy list containing only the items from the original
|
||||||
|
#! lazy list for which the predicate returns a value other than f.
|
||||||
|
over [ ] = [
|
||||||
|
2drop [ ]
|
||||||
|
] [
|
||||||
|
[ luncons ] dip
|
||||||
|
dup swapd
|
||||||
|
[ lsubset ] curry2
|
||||||
|
-rot dupd call [
|
||||||
|
swap lcons
|
||||||
|
] [
|
||||||
|
drop call
|
||||||
|
] ifte
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: lappend* ;
|
||||||
|
: (lappend*) ;
|
||||||
|
: lappend-list* ;
|
||||||
|
|
||||||
|
: lappend-item* ( llists list item -- llist )
|
||||||
|
-rot [ lappend-list* ] curry2 lcons ;
|
||||||
|
|
||||||
|
: lappend-list* ( llists list -- llist )
|
||||||
|
dup [
|
||||||
|
#! non-empty list
|
||||||
|
luncons swap lappend-item*
|
||||||
|
] [
|
||||||
|
#! empty list
|
||||||
|
drop lappend*
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
|
||||||
|
: (lappend*) ( llists -- llist )
|
||||||
|
dup lcar [ ( llists )
|
||||||
|
#! Yes, the first item in the list is a valid llist
|
||||||
|
luncons swap lappend-list*
|
||||||
|
] [
|
||||||
|
#! The first item in the list is an empty list.
|
||||||
|
#! Resume passing the next list.
|
||||||
|
lcdr lappend*
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: lappend* ( llists -- llist )
|
||||||
|
#! Given a lazy list of lazy lists, return a lazy list that
|
||||||
|
#! works through all of the sub-lists in sequence.
|
||||||
|
dup [
|
||||||
|
(lappend*)
|
||||||
|
] [
|
||||||
|
#! Leave empty list on the stack
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: list>llist ( list -- llist )
|
||||||
|
#! Convert a list to a lazy list.
|
||||||
|
dup [
|
||||||
|
uncons [ list>llist ] curry1 lcons
|
||||||
|
] when ;
|
||||||
|
|
||||||
|
: lappend ( llist1 llist2 -- llist )
|
||||||
|
#! Concatenate two lazy lists such that they appear to be one big lazy list.
|
||||||
|
2list list>llist lappend* ;
|
||||||
|
|
||||||
|
: leach ( llist quot -- )
|
||||||
|
#! Call the quotation on each item in the lazy list.
|
||||||
|
#! Warning: If the list is infinite then this will
|
||||||
|
#! never return.
|
||||||
|
over [
|
||||||
|
>r luncons r> tuck >r >r call r> r> leach
|
||||||
|
] [
|
||||||
|
2drop
|
||||||
|
] ifte ;
|
||||||
|
|
|
@ -0,0 +1,280 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Lazy Evaluation</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Lazy Evaluation</h1>
|
||||||
|
<p>The 'lazy' vocabulary adds lazy lists to Factor. This provides the
|
||||||
|
ability to describe infinite structures, and to delay execution of
|
||||||
|
expressions until they are actually used.</p>
|
||||||
|
<p>Lazy lists, like normal lists, are composed of a head and tail. In
|
||||||
|
a lazy list the tail is something called a 'promise'. To convert a
|
||||||
|
'promise' into its actual value a word called 'force' is used. To
|
||||||
|
convert a value into a 'promise' the word to use is 'delay'.</p>
|
||||||
|
<p>Many of the lazy list words are named similar to the standard list
|
||||||
|
words but with an 'l' suffixed to it. Here are the commonly used
|
||||||
|
words and their equivalent list operation:</p>
|
||||||
|
<table border="1">
|
||||||
|
<tr><th>Lazy List</th><th>Normal List</th></tr>
|
||||||
|
<tr><td><a href="#lcons">lcons</a></td><td>cons</td></tr>
|
||||||
|
<tr><td><a href="#lunit">lunit</a></td><td>unit</td></tr>
|
||||||
|
<tr><td><a href="#lcar">lcar</a></td><td>car</td></tr>
|
||||||
|
<tr><td><a href="#lcdr">lcdr</a></td><td>cdr</td></tr>
|
||||||
|
<tr><td><a href="#lnth">lnth</a></td><td>nth</td></tr>
|
||||||
|
<tr><td><a href="#luncons">luncons</a></td><td>uncons</td></tr>
|
||||||
|
<tr><td><a href="#lmap">lmap</a></td><td>map</td></tr>
|
||||||
|
<tr><td><a href="#lsubset">lsubset</a></td><td>subset</td></tr>
|
||||||
|
<tr><td><a href="#leach">leach</a></td><td>each</td></tr>
|
||||||
|
<tr><td><a href="#lappend">lappend</a></td><td>append</td></tr>
|
||||||
|
</table>
|
||||||
|
<p>A few additional words specific to lazy lists are:</p>
|
||||||
|
<table border="1">
|
||||||
|
<tr><td><a href="#ltake">ltake</a></td><td>Returns a normal list containing a specified
|
||||||
|
number of items from the lazy list.</td></tr>
|
||||||
|
<tr><td><a href="#lappendstar">lappend*</a></td><td>Given a lazy list of lazy lists,
|
||||||
|
concatenate them together in a lazy manner, returning a single lazy
|
||||||
|
list.</td></tr>
|
||||||
|
<tr><td><a href="#list>llist">list>llist</a></td><td>Given a normal list, return a lazy list
|
||||||
|
that contains the same elements as the normal list.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p>A couple of helper functions are also provided by the lazy
|
||||||
|
vocabulary.</p>
|
||||||
|
<table border="1">
|
||||||
|
<tr><td>curry1</td><td>Given a value and a quotation, returns a new
|
||||||
|
quotation that when called will have the value on the stack.</td></tr>
|
||||||
|
<tr><td>curry2</td><td>Given two values and a quotation, returns a new
|
||||||
|
quotation that when called will have the two values on the
|
||||||
|
stack.</td></tr>
|
||||||
|
</table>
|
||||||
|
<h2>Reference</h2>
|
||||||
|
<!-- lcons description -->
|
||||||
|
<a name="lcons">
|
||||||
|
<h3>lcons ( value promise -- lcons )</h3>
|
||||||
|
<p>Provides the same effect as 'cons' does for normal lists. It
|
||||||
|
creates a cons cell where the first element is the value given and the
|
||||||
|
second element is a promise.</p>
|
||||||
|
<a name="promise">
|
||||||
|
<p>A promise is either a value that has had 'force' called on it, or
|
||||||
|
a quotation that when 'call' is applied to it, returns the actual
|
||||||
|
value.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 5 6 delay <a href="#lcons">lcons</a> dup .
|
||||||
|
=> [ 5 6 ]
|
||||||
|
( 2 ) dup <a href="#lcar">lcar</a> .
|
||||||
|
=> 5
|
||||||
|
( 3 ) dup <a href="#lcdr">lcdr</a> .
|
||||||
|
=> 6
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- lunit description -->
|
||||||
|
<a name="lunit">
|
||||||
|
<h3>lunit ( value -- llist )</h3>
|
||||||
|
<p>Provides the same effect as 'unit' does for normal lists. It
|
||||||
|
creates a lazy list where the first element is the value given.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 42 <a href="#lunit">lunit</a> dup .
|
||||||
|
=> [ 42 f ]
|
||||||
|
( 2 ) dup <a href="#lcar">lcar</a> .
|
||||||
|
=> 42
|
||||||
|
( 3 ) dup <a href="#lcdr">lcdr</a> .
|
||||||
|
=> f
|
||||||
|
( 4 ) [ . ] <a href="#leach">leach</a>
|
||||||
|
=> 42
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- lcar description -->
|
||||||
|
<a name="lcar">
|
||||||
|
<h3>lcar ( lcons -- value )</h3>
|
||||||
|
<p>Provides the same effect as 'car' does for normal lists. It
|
||||||
|
returns the first element in a lazy cons cell.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 42 <a href="#lunit">lunit</a> dup .
|
||||||
|
=> [ 42 f ]
|
||||||
|
( 2 ) <a href="#lcar">lcar</a> .
|
||||||
|
=> 42
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- lcdr description -->
|
||||||
|
<a name="lcdr">
|
||||||
|
<h3>lcdr ( lcons -- value )</h3>
|
||||||
|
<p>Provides the same effect as 'cdr' does for normal lists. It
|
||||||
|
returns the second element in a lazy cons cell and forces it. This
|
||||||
|
causes that element to be evaluated immediately.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 5 [ 5 6 + ] <a href="#lcons">lcons</a> dup .
|
||||||
|
=> [ 5 5 6 + ]
|
||||||
|
( 2 ) <a href="#lcdr">lcdr</a> .
|
||||||
|
=> 11
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 5 lfrom dup .
|
||||||
|
=> [ 5 5 succ lfrom ]
|
||||||
|
( 2 ) <a href="#lcdr">lcdr</a> dup <a href="#lcar">lcar</a> .
|
||||||
|
=> 6
|
||||||
|
( 3 ) <a href="#lcdr">lcdr</a> dup <a href="#lcar">lcar</a> .
|
||||||
|
=> 7
|
||||||
|
( 4 ) <a href="#lcdr">lcdr</a> dup <a href="#lcar">lcar</a> .
|
||||||
|
=> 8
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- lnth description -->
|
||||||
|
<a name="lnth">
|
||||||
|
<h3>lnth ( n llist -- value )</h3>
|
||||||
|
<p>Provides the same effect as 'nth' does for normal lists. It
|
||||||
|
returns the nth value in the lazy list. It causes all the values up to
|
||||||
|
'n' to be evaluated.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 1 lfrom
|
||||||
|
=> [ 1 1 succ lfrom ]
|
||||||
|
( 2 ) 5 swap <a href="#lnth">lnth</a> .
|
||||||
|
=> 6
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- luncons description -->
|
||||||
|
<a name="luncons">
|
||||||
|
<h3>luncons ( lcons -- car cdr )</h3>
|
||||||
|
<p>Provides the same effect as 'uncons' does for normal lists. It
|
||||||
|
returns the car and cdr of the lazy list. Note that cdr is forced
|
||||||
|
resulting in it being evaluated.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 5 [ 6 ] <a href="#lcons">lcons</a> dup .
|
||||||
|
=> [ 5 6 ]
|
||||||
|
( 2 ) <a href="#luncons">luncons</a> .s
|
||||||
|
=> { 5 6 }
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- lmap description -->
|
||||||
|
<a name="lmap">
|
||||||
|
<h3>lmap ( llist quot -- llist )</h3>
|
||||||
|
<p>Provides the same effect as 'map' does for normal lists. It
|
||||||
|
lazily maps over a lazy list applying the quotation to each element.
|
||||||
|
A new lazy list is returned which contains the results of the
|
||||||
|
quotation.</p>
|
||||||
|
<p>When initially called <a href="#lmap">lmap</a> will only call quot on the first element
|
||||||
|
of the list. It then constructs a lazy list that performs the
|
||||||
|
next '<a href="#lmap">lmap</a>' operation on the next element when it is evaluated. This
|
||||||
|
allows mapping over infinite lists.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 1 lfrom
|
||||||
|
=> < infinite list of incrementing numbers >
|
||||||
|
( 2 ) [ 2 * ] <a href="#lmap">lmap</a>
|
||||||
|
=> < infinite list of numbers incrementing by 2 >
|
||||||
|
( 3 ) 5 swap <a href="#ltake">ltake</a> .
|
||||||
|
=> [ 2 4 6 8 10 ]
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- lsubset description -->
|
||||||
|
<a name="lsubset">
|
||||||
|
<h3>lsubset ( llist pred -- llist )</h3>
|
||||||
|
<p>Provides the same effect as 'subset' does for normal lists. It
|
||||||
|
lazily iterates over a lazy list applying the predicate quotation to each
|
||||||
|
element. If that quotation returns true, the element will be included
|
||||||
|
in the resulting lazy list. If it is false, the element will be skipped.
|
||||||
|
A new lazy list is returned which contains all elements where the
|
||||||
|
predicate returned true.</p>
|
||||||
|
<p>When initially called <a href="#lsubset">lsubset</a> will only call
|
||||||
|
the predicate quotation on the first element
|
||||||
|
of the list. It then constructs a lazy list that performs the
|
||||||
|
next '<a href="#lsubset">lsubset</a>' operation on the next element when it is evaluated. This
|
||||||
|
allows subsetting over infinite lists.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 1 lfrom
|
||||||
|
=> < infinite list of incrementing numbers >
|
||||||
|
( 2 ) [ prime? ] <a href="#lsubset">lsubset</a>
|
||||||
|
=> < infinite list of prime numbers >
|
||||||
|
( 3 ) 5 swap <a href="#ltake">ltake</a> .
|
||||||
|
=> [ 2 3 5 7 11 ]
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- leach description -->
|
||||||
|
<a name="leach">
|
||||||
|
<h3>leach ( llist quot -- )</h3>
|
||||||
|
<p>Provides the same effect as 'each' does for normal lists. It
|
||||||
|
lazily iterates over a lazy list applying the quotation to each
|
||||||
|
element. If this operation is applied to an infinite list it will
|
||||||
|
never return unless the quotation escapes out by calling a continuation.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) 1 lfrom
|
||||||
|
=> < infinite list of incrementing numbers >
|
||||||
|
( 2 ) [ 2 mod 1 = ] <a href="#lsubset">lsubset</a>
|
||||||
|
=> < infinite list of odd numbers >
|
||||||
|
( 3 ) [ . ] <a href="#leach">leach</a>
|
||||||
|
=> 1
|
||||||
|
3
|
||||||
|
5
|
||||||
|
7
|
||||||
|
... for ever ...
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- ltake description -->
|
||||||
|
<a name="ltake">
|
||||||
|
<h3>ltake ( n llist -- list )</h3>
|
||||||
|
<p>Iterates over the lazy list 'n' times, appending each element to a
|
||||||
|
normal list. The normal list is returned. This provides a convenient
|
||||||
|
way of getting elements out of a lazy list.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) : ones 1 [ ones ] <a href="#lcons">lcons</a> ;
|
||||||
|
( 2 ) 5 ones <a href="#ltake">ltake</a>
|
||||||
|
=> [ 1 1 1 1 1 ]
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- lappend description -->
|
||||||
|
<a name="lappend">
|
||||||
|
<h3>lappend ( llist1 llist2 -- llist )</h3>
|
||||||
|
<p>Lazily appends two lists together. The actual appending is done
|
||||||
|
lazily on iteration rather than immediately so it works very fast no
|
||||||
|
matter how large the list.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) [ 1 2 3 ] <a href="#list>llist">list>llist</a> [ 4 5 6 ] <a href="#list>llist">list>llist</a> <a href="#lappend">lappend</a>
|
||||||
|
( 2 ) [ . ] <a href="#leach">leach</a>
|
||||||
|
=> 1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- lappend* description -->
|
||||||
|
<a name="lappendstar">
|
||||||
|
<h3>lappend* ( llists -- llist )</h3>
|
||||||
|
<p>Given a lazy list of lazy lists, concatenate them together in a
|
||||||
|
lazy fashion. The actual appending is done lazily on iteration rather
|
||||||
|
than immediately so it works very fast no matter how large the lists.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) [ 1 2 3 ] <a href="#list>llist">list>llist</a>
|
||||||
|
( 2 ) [ 4 5 6 ] <a href="#list>llist">list>llist</a>
|
||||||
|
( 3 ) [ 7 8 9 ] <a href="#list>llist">list>llist</a>
|
||||||
|
( 4 ) 3list <a href="#list>llist">list>llist</a> <a href="#lappendstar">lappend*</a>
|
||||||
|
( 5 ) [ . ] <a href="#leach">leach</a>
|
||||||
|
=> 1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7
|
||||||
|
8
|
||||||
|
9
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<!-- list>llist description -->
|
||||||
|
<a name="list>llist">
|
||||||
|
<h3>list>llist ( list -- llist )</h3>
|
||||||
|
<p>Converts a normal list into a lazy list. This is done lazily so the
|
||||||
|
initial list is not iterated through immediately.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) [ 1 2 3 ] <a href="#list>llist">list>llist</a>
|
||||||
|
( 2 ) [ . ] <a href="#leach">leach</a>
|
||||||
|
=> 1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p class="footer">
|
||||||
|
News and updates to this software can be obtained from the authors
|
||||||
|
weblog: <a href="http://radio.weblogs.com/0102385">Chris Double</a>.</p>
|
||||||
|
<p id="copyright">Copyright (c) 2004, Chris Double. All Rights Reserved.</p>
|
||||||
|
</body> </html>
|
|
@ -0,0 +1,433 @@
|
||||||
|
! Copyright (C) 2004 Chris Double.
|
||||||
|
!
|
||||||
|
! Redistribution and use in source and binary forms, with or without
|
||||||
|
! modification, are permitted provided that the following conditions are met:
|
||||||
|
!
|
||||||
|
! 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer.
|
||||||
|
!
|
||||||
|
! 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
! this list of conditions and the following disclaimer in the documentation
|
||||||
|
! and/or other materials provided with the distribution.
|
||||||
|
!
|
||||||
|
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
IN: parser-combinators
|
||||||
|
USE: lazy
|
||||||
|
USE: stack
|
||||||
|
USE: lists
|
||||||
|
USE: strings
|
||||||
|
USE: math
|
||||||
|
USE: logic
|
||||||
|
USE: kernel
|
||||||
|
USE: combinators
|
||||||
|
USE: parser
|
||||||
|
|
||||||
|
: phead ( object -- head )
|
||||||
|
#! Polymorphic head. Return the head item of the object.
|
||||||
|
#! For a string this is the first character.
|
||||||
|
#! For a list this is the car.
|
||||||
|
[
|
||||||
|
[ string? ] [ 0 swap str-nth ]
|
||||||
|
[ list? ] [ car ]
|
||||||
|
] cond ;
|
||||||
|
|
||||||
|
: ptail ( object -- tail )
|
||||||
|
#! Polymorphic tail. Return the tail of the object.
|
||||||
|
#! For a string this is everything but the first character.
|
||||||
|
#! For a list this is the cdr.
|
||||||
|
[
|
||||||
|
[ string? ] [ 1 str-tail ]
|
||||||
|
[ list? ] [ cdr ]
|
||||||
|
] cond ;
|
||||||
|
|
||||||
|
: pfirst ( object -- first )
|
||||||
|
#! Polymorphic first
|
||||||
|
phead ;
|
||||||
|
|
||||||
|
: psecond ( object -- second )
|
||||||
|
#! Polymorphic second
|
||||||
|
[
|
||||||
|
[ string? ] [ 1 swap str-nth ]
|
||||||
|
[ list? ] [ cdr car ]
|
||||||
|
] cond ;
|
||||||
|
|
||||||
|
: ph:t ( object -- head tail )
|
||||||
|
#! Return the head and tail of the object.
|
||||||
|
dup phead swap ptail ;
|
||||||
|
|
||||||
|
: pempty? ( object -- bool )
|
||||||
|
#! Polymorphic empty test.
|
||||||
|
[
|
||||||
|
[ string? ] [ "" = ]
|
||||||
|
[ list? ] [ not ]
|
||||||
|
] cond ;
|
||||||
|
|
||||||
|
: string-take ( n string -- string )
|
||||||
|
#! Return a string with the first 'n' characters
|
||||||
|
#! of the original string.
|
||||||
|
dup str-length pick < [
|
||||||
|
2drop ""
|
||||||
|
] [
|
||||||
|
swap str-head
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: (list-take) ( n list accum -- list )
|
||||||
|
>r >r pred dup 0 < [
|
||||||
|
drop r> drop r> nreverse
|
||||||
|
] [
|
||||||
|
r> uncons swap r> cons (list-take)
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: list-take ( n list -- list )
|
||||||
|
#! Return a list with the first 'n' characters
|
||||||
|
#! of the original list.
|
||||||
|
[ ] (list-take) ;
|
||||||
|
|
||||||
|
: ptake ( n object -- object )
|
||||||
|
#! Polymorphic take.
|
||||||
|
#! Return a collection of the first 'n'
|
||||||
|
#! characters from the original collection.
|
||||||
|
[
|
||||||
|
[ string? ] [ string-take ]
|
||||||
|
[ list? ] [ list-take ]
|
||||||
|
] cond ;
|
||||||
|
|
||||||
|
: string-drop ( n string -- string )
|
||||||
|
#! Return a string with the first 'n' characters
|
||||||
|
#! of the original string removed.
|
||||||
|
dup str-length pick < [
|
||||||
|
2drop ""
|
||||||
|
] [
|
||||||
|
swap str-tail
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: list-drop ( n list -- list )
|
||||||
|
#! Return a list with the first 'n' items
|
||||||
|
#! of the original list removed.
|
||||||
|
>r pred dup 0 < [
|
||||||
|
drop r>
|
||||||
|
] [
|
||||||
|
r> cdr list-drop
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: pdrop ( n object -- object )
|
||||||
|
#! Polymorphic drop.
|
||||||
|
#! Return a collection the same as 'object'
|
||||||
|
#! but with the first n items removed.
|
||||||
|
[
|
||||||
|
[ string? ] [ string-drop ]
|
||||||
|
[ list? ] [ list-drop ]
|
||||||
|
] cond ;
|
||||||
|
|
||||||
|
: ifte-head= ( string-or-list ch [ quot1 ] [ quot2 ] -- )
|
||||||
|
#! When the character 'ch' is equal to the head
|
||||||
|
#! of the string or list, run the quot1 otherwise run quot2.
|
||||||
|
[ swap phead = ] 2dip ifte ;
|
||||||
|
|
||||||
|
: symbol ( ch -- parser )
|
||||||
|
#! Return a parser that parses the given symbol.
|
||||||
|
[ ( inp ch -- result )
|
||||||
|
2dup [
|
||||||
|
swap ptail cons lunit
|
||||||
|
] [
|
||||||
|
2drop [ ]
|
||||||
|
] ifte-head=
|
||||||
|
] curry1 ;
|
||||||
|
|
||||||
|
: token ( string -- parser )
|
||||||
|
#! Return a parser that parses the given string.
|
||||||
|
[ ( inp string -- result )
|
||||||
|
2dup str-length swap ptake over = [
|
||||||
|
swap over str-length swap pdrop cons lunit
|
||||||
|
] [
|
||||||
|
2drop [ ]
|
||||||
|
] ifte
|
||||||
|
] curry1 ;
|
||||||
|
|
||||||
|
: satisfy ( p -- parser )
|
||||||
|
#! Return a parser that succeeds if the predicate 'p',
|
||||||
|
#! when passed the first character in the input, returns
|
||||||
|
#! true.
|
||||||
|
[ ( inp p -- result )
|
||||||
|
over pempty? [
|
||||||
|
2drop [ ]
|
||||||
|
] [
|
||||||
|
over phead swap call [
|
||||||
|
ph:t cons lunit
|
||||||
|
] [
|
||||||
|
drop [ ]
|
||||||
|
] ifte
|
||||||
|
] ifte
|
||||||
|
] curry1 ;
|
||||||
|
|
||||||
|
: satisfy2 ( p r -- parser )
|
||||||
|
#! Return a parser that succeeds if the predicate 'p',
|
||||||
|
#! when passed the first character in the input, returns
|
||||||
|
#! true. On success the word 'r' is called with the
|
||||||
|
#! successfully parser character on the stack. The result
|
||||||
|
#! of this is returned as the result of the parser.
|
||||||
|
[ ( inp p r -- result )
|
||||||
|
>r over phead swap call [
|
||||||
|
ph:t swap r> call swons lunit
|
||||||
|
] [
|
||||||
|
r> 2drop [ ]
|
||||||
|
] ifte
|
||||||
|
] curry2 ;
|
||||||
|
|
||||||
|
: epsilon ( -- parser )
|
||||||
|
#! A parser that parses the empty string.
|
||||||
|
[ ( inp -- result )
|
||||||
|
"" swap cons lunit
|
||||||
|
] ;
|
||||||
|
|
||||||
|
: succeed ( r -- parser )
|
||||||
|
#! A parser that always returns 'r' and consumes no input.
|
||||||
|
[ ( inp r -- result )
|
||||||
|
swap cons lunit
|
||||||
|
] curry1 ;
|
||||||
|
|
||||||
|
: fail ( -- parser )
|
||||||
|
#! A parser that always fails
|
||||||
|
[
|
||||||
|
drop [ ]
|
||||||
|
] ;
|
||||||
|
|
||||||
|
USE: prettyprint
|
||||||
|
USE: unparser
|
||||||
|
|
||||||
|
: ensure-list ( a -- [ a ] )
|
||||||
|
#! If 'a' is not a list, make it one.
|
||||||
|
dup list? [ unit ] unless ;
|
||||||
|
|
||||||
|
: ++ ( a b -- [ a b ] )
|
||||||
|
#! Join two items into a list.
|
||||||
|
>r ensure-list r> ensure-list append ;
|
||||||
|
|
||||||
|
: <&> ( p1 p2 -- parser )
|
||||||
|
#! Sequentially combine two parsers, returning a parser
|
||||||
|
#! that first calls p1, then p2 all remaining results from
|
||||||
|
#! p1.
|
||||||
|
[ ( inp p1 p2 -- result )
|
||||||
|
>r call r> [ ( [ x | xs ] p2 -- result )
|
||||||
|
>r uncons r> call swap [ ( [ x2 | xs2 ] x -- result )
|
||||||
|
>r uncons swap r> swap ++ swons
|
||||||
|
] curry1 lmap
|
||||||
|
] curry1 lmap lappend*
|
||||||
|
] curry2 ;
|
||||||
|
|
||||||
|
|
||||||
|
: <|> ( p1 p2 -- parser )
|
||||||
|
#! Choice operator for parsers. Return a parser that does
|
||||||
|
#! p1 or p2 depending on which will succeed.
|
||||||
|
[ ( inp p1 p2 -- result )
|
||||||
|
rot tuck swap call >r swap call r> lappend
|
||||||
|
] curry2 ;
|
||||||
|
|
||||||
|
: p-abc ( -- parser )
|
||||||
|
#! Test Parser. Parses the string "abc"
|
||||||
|
"a" token "b" token "c" token <&> <&> ;
|
||||||
|
|
||||||
|
: parse-skipwhite ( string -- string )
|
||||||
|
dup phead blank? [
|
||||||
|
ptail parse-skipwhite
|
||||||
|
] [
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: sp ( parser -- parser )
|
||||||
|
#! Return a parser that first skips all whitespace before
|
||||||
|
#! parsing.
|
||||||
|
[ ( inp parser -- result )
|
||||||
|
[ parse-skipwhite ] dip call
|
||||||
|
] curry1 ;
|
||||||
|
|
||||||
|
: just ( parser -- parser )
|
||||||
|
#! Return a parser that works exactly like the input parser
|
||||||
|
#! but guarantees that the rest string is empty.
|
||||||
|
[ ( inp parser -- result )
|
||||||
|
call [ ( [ x | xs ] -- )
|
||||||
|
cdr str-length 0 =
|
||||||
|
] lsubset
|
||||||
|
] curry1 ;
|
||||||
|
|
||||||
|
: <@ ( p f -- parser )
|
||||||
|
#! Given a parser p and a quotation f return a parser
|
||||||
|
#! that does the same as p but in addition applies f
|
||||||
|
#! to the resulting parse tree.
|
||||||
|
[ ( inp p f -- result )
|
||||||
|
>r call r> [ ( [ x | xs ] f -- [ fx | xs ] )
|
||||||
|
swap uncons [ swap over [ call ] [ drop ] ifte ] dip cons
|
||||||
|
] curry1 lmap
|
||||||
|
] curry2 ;
|
||||||
|
|
||||||
|
: p-1 ( -- parser )
|
||||||
|
"1" token "123" swap call lcar ;
|
||||||
|
|
||||||
|
: p-2 ( -- parser )
|
||||||
|
"1" token [ str>number ] <@ "123" swap call lcar ;
|
||||||
|
|
||||||
|
: some ( parser -- det-parser )
|
||||||
|
#! Given a parser, return a parser that only produces the
|
||||||
|
#! resulting parse tree of the first successful complete parse.
|
||||||
|
[ ( inp parser -- result )
|
||||||
|
just call lcar car
|
||||||
|
] curry1 ;
|
||||||
|
|
||||||
|
: delayed-parser ( [ parser ] -- parser )
|
||||||
|
[ ( inp [ parser ] -- result )
|
||||||
|
call call
|
||||||
|
] curry1 ;
|
||||||
|
|
||||||
|
: parens ;
|
||||||
|
: parens ( -- parser )
|
||||||
|
#! Parse nested parentheses
|
||||||
|
"(" token [ parens ] delayed-parser <&>
|
||||||
|
")" token <&> [ parens ] delayed-parser <&>
|
||||||
|
epsilon <|> ;
|
||||||
|
|
||||||
|
: nesting ( -- parser )
|
||||||
|
#! Count the maximum depth of nested parentheses.
|
||||||
|
"(" token [ nesting ] delayed-parser <&> ")" token <&>
|
||||||
|
[ nesting ] delayed-parser <&> [ .s drop "a" ] <@ epsilon <|> ;
|
||||||
|
|
||||||
|
: <& ( parser1 parser2 -- parser )
|
||||||
|
#! Same as <&> except only return the first item in the parse tree.
|
||||||
|
<&> [ pfirst ] <@ ;
|
||||||
|
|
||||||
|
: &> ( parser1 parser2 -- parser )
|
||||||
|
#! Same as <&> except only return the second item in the parse tree.
|
||||||
|
<&> [ psecond ] <@ ;
|
||||||
|
|
||||||
|
: lst ( [ x [ xs ] ] -- [x:xs] )
|
||||||
|
#! I need a good name for this word...
|
||||||
|
dup cdr [ uncons car cons ] when unit ;
|
||||||
|
|
||||||
|
: <*> ( parser -- parser )
|
||||||
|
#! Return a parser that accepts zero or more occurences of the original
|
||||||
|
#! parser.
|
||||||
|
dup [ <*> ] curry1 delayed-parser <&> [ lst ] <@ [ ] succeed <|> ;
|
||||||
|
|
||||||
|
: <+> ( parser -- parser )
|
||||||
|
#! Return a parser that accepts one or more occurences of the original
|
||||||
|
#! parser.
|
||||||
|
dup [ <*> ] curry1 delayed-parser <&> [ lst ] <@ ;
|
||||||
|
|
||||||
|
: <?> ( parser -- parser )
|
||||||
|
#! Return a parser where its construct is optional. It may or may not occur.
|
||||||
|
[ ] succeed <|> ;
|
||||||
|
|
||||||
|
: <first> ( parser -- parser )
|
||||||
|
#! Transform a parser into a parser that only returns the first success.
|
||||||
|
[
|
||||||
|
call dup [ lcar lunit ] when
|
||||||
|
] curry1 ;
|
||||||
|
|
||||||
|
: <!*> ( parser -- parser )
|
||||||
|
#! Version of <*> that only returns the first success.
|
||||||
|
<*> <first> ;
|
||||||
|
|
||||||
|
: <!+> ( parser -- parser )
|
||||||
|
#! Version of <+> that only returns the first success.
|
||||||
|
<+> <first> ;
|
||||||
|
|
||||||
|
: ab-test
|
||||||
|
"a" token <*> "b" token <&> "aaaaab" swap call [ . ] leach ;
|
||||||
|
|
||||||
|
: ab-test2
|
||||||
|
"a" token <*> "b" token <&> [ "a" "a" "a" "b" ] swap call [ . ] leach ;
|
||||||
|
|
||||||
|
: a "a" token "a" token <&> epsilon <|> ;
|
||||||
|
: b "b" token epsilon <|> ;
|
||||||
|
: c "c" token "c" token <&> ;
|
||||||
|
: d "d" token "d" token <&> ;
|
||||||
|
: count-a "a" token [ count-a ] delayed-parser &> "b" token <& [ 1 + ] <@ 0 succeed <|> ;
|
||||||
|
: tca "aaabbb" count-a call [ . ] leach ;
|
||||||
|
|
||||||
|
: parse-digit ( -- parser )
|
||||||
|
#! Return a parser for digits
|
||||||
|
[ digit? ] satisfy [ CHAR: 0 - ] <@ ;
|
||||||
|
|
||||||
|
: (reduce) ( start quot list -- value )
|
||||||
|
#! Call quot with start and the first value in the list.
|
||||||
|
#! quot is then called with the result of quot and the
|
||||||
|
#! next item in the list until the list is exhausted.
|
||||||
|
uncons >r swap dup dip r> dup [
|
||||||
|
(reduce)
|
||||||
|
] [
|
||||||
|
2drop
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: reduce ( list start quot -- value )
|
||||||
|
#! Call quot with start and the first value in the list.
|
||||||
|
#! quot is then called with the result of quot and the
|
||||||
|
#! next item in the list until the list is exhausted.
|
||||||
|
rot (reduce) ;
|
||||||
|
|
||||||
|
: natural ( -- parser )
|
||||||
|
#! a parser for natural numbers.
|
||||||
|
parse-digit <*> [ car 0 [ swap 10 * + ] reduce unit ] <@ ;
|
||||||
|
|
||||||
|
: natural2 ( -- parser )
|
||||||
|
#! a parser for natural numbers.
|
||||||
|
parse-digit <!+> [ car 0 [ swap 10 * + ] reduce unit ] <@ ;
|
||||||
|
|
||||||
|
: integer ( -- parser )
|
||||||
|
#! A parser that can parser possible negative numbers.
|
||||||
|
"-" token <?> [ drop -1 ] <@ natural2 <&> [ 1 [ * ] reduce ] <@ ;
|
||||||
|
|
||||||
|
: identifier ( -- parser )
|
||||||
|
#! Parse identifiers
|
||||||
|
[ letter? ] satisfy <+> [ car cat ] <@ ;
|
||||||
|
|
||||||
|
: identifier2 ( -- parser )
|
||||||
|
#! Parse identifiers
|
||||||
|
[ letter? ] satisfy <!+> [ car cat ] <@ ;
|
||||||
|
|
||||||
|
: ints ( -- parser )
|
||||||
|
integer "+" token [ drop [ [ + ] ] ] <@ <&>
|
||||||
|
integer <&> [ call swap call ] <@ ;
|
||||||
|
|
||||||
|
: url-quotable ( -- parser )
|
||||||
|
! [a-zA-Z0-9/_?] re-matches
|
||||||
|
[ letter? ] satisfy
|
||||||
|
[ LETTER? ] satisfy <|>
|
||||||
|
[ digit? ] satisfy <|>
|
||||||
|
CHAR: / symbol <|>
|
||||||
|
CHAR: _ symbol <|>
|
||||||
|
CHAR: ? symbol <|> just ;
|
||||||
|
|
||||||
|
: http-header ( -- parser )
|
||||||
|
[ CHAR: : = not ] satisfy <!+> [ car cat ] <@
|
||||||
|
": " token [ drop f ] <@ <&>
|
||||||
|
[ drop t ] satisfy <!+> [ car cat ] <@ <&> just ;
|
||||||
|
|
||||||
|
: parse-http-header ( string -- [ name value ] )
|
||||||
|
http-header call lcar car ;
|
||||||
|
|
||||||
|
: get-request ( -- parser )
|
||||||
|
"GET" token
|
||||||
|
[ drop t ] satisfy <!+> sp [ car cat ] <@ <&> ;
|
||||||
|
|
||||||
|
: post-request ( -- parser )
|
||||||
|
"POST" token
|
||||||
|
[ drop t ] satisfy <!+> sp [ car cat ] <@ <&> ;
|
||||||
|
|
||||||
|
: all-request ( -- parser )
|
||||||
|
"POST" token
|
||||||
|
[ 32 = not ] satisfy <!+> sp [ car cat ] <@ <&>
|
||||||
|
"HTTP/1.0" token sp <&> ;
|
||||||
|
|
||||||
|
: split-url ( -- parser )
|
||||||
|
"http://" token
|
||||||
|
[ CHAR: / = not ] satisfy <!*> [ car cat ] <@ <&>
|
||||||
|
"/" token <&>
|
||||||
|
[ drop t ] satisfy <!*> [ car cat ] <@ <&> ;
|
||||||
|
|
|
@ -0,0 +1,263 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Parser Combinators</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Parsers</h1>
|
||||||
|
<p class="note">The parser combinator library described here is based
|
||||||
|
on a library written for the Clean pure functional programming language and
|
||||||
|
described in chapter 5 of the 'Clean Book'. Based on the description
|
||||||
|
in that chapter I developed a version for Factor, a concatenative
|
||||||
|
language.</p>
|
||||||
|
<p>A parser is a word or quotation that, when called, processes
|
||||||
|
an input string on the stack, performs some parsing operation on
|
||||||
|
it, and returns a result indicating the success of the parsing
|
||||||
|
operation.</p>
|
||||||
|
<p>The result returned by a parser is known as a 'list of
|
||||||
|
successes'. It is a lazy list of standard Factor cons cells. Each cons
|
||||||
|
cell is a result of a parse. The car of the cell is the result of the
|
||||||
|
parse operation and the cdr of the cell is the remaining input left to
|
||||||
|
be parsed.</p>
|
||||||
|
<p>A list is used for the result as a parse operation can potentially
|
||||||
|
return many successful results. For example, a parser that parses one
|
||||||
|
or more digits will return more than one result for the input "123". A
|
||||||
|
successful parse could be "1", "12" or "123".</p>
|
||||||
|
<p>The list is lazy so if only one parse result is required the
|
||||||
|
remaining results won't actually be processed if they are not
|
||||||
|
requested. This improves efficiency.</p>
|
||||||
|
<p>The car of the result pair can be any value that the parser wishes
|
||||||
|
to return. It could be the successful portion of the input string
|
||||||
|
parsed, an abstract syntax tree representing the parsed input, or even
|
||||||
|
a quotation that should get called for later processing.</p>
|
||||||
|
<p>A Parser Combinator is a word that takes one or more parsers and
|
||||||
|
returns a parser that when called uses the original parsers in some
|
||||||
|
manner.</p>
|
||||||
|
<h1>Example Parsers</h1>
|
||||||
|
<p>The following are some very simple parsers that demonstrate how
|
||||||
|
general parsers work and the 'list of sucesses' that are returned as a
|
||||||
|
result.</p>
|
||||||
|
<pre class="code">
|
||||||
|
(1) : char-a ( inp -- result )
|
||||||
|
0 over str-nth CHAR: a = [
|
||||||
|
1 str-tail CHAR: a swons lunit
|
||||||
|
] [
|
||||||
|
drop f
|
||||||
|
] ifte ;
|
||||||
|
(2) "atest" char-a [ [ . ] leach ] when*
|
||||||
|
=> [ 97 | "test" ]
|
||||||
|
(3) "test" char-a [ [ . ] leach ] when*
|
||||||
|
=>
|
||||||
|
</pre>
|
||||||
|
<p>'char-a' is a parser that only accepts the character 'a' in the
|
||||||
|
input string. When passed an input string with a string with a leading
|
||||||
|
'a' then the 'list of successes' has 1 result value. The car of that
|
||||||
|
result value is the character 'a' successfully parsed, and the cdr is
|
||||||
|
the remaining input string. On failure of the parse an empty list is
|
||||||
|
returned.</p>
|
||||||
|
<p>The parser combinator library provides a combinator, <&>, that takes
|
||||||
|
two parsers off the stack and returns a parser that calls the original
|
||||||
|
two in sequence. An example of use would be calling 'char-a' twice,
|
||||||
|
which would then result in an input string expected with two 'a'
|
||||||
|
characters leading:</p>
|
||||||
|
<pre class="code">
|
||||||
|
(1) "aatest" [ char-a ] [ char-a ] <&> call
|
||||||
|
=> < list of successes >
|
||||||
|
(2) [ . ] leach
|
||||||
|
=> [ [ 97 97 ] | "test" ]
|
||||||
|
</pre>
|
||||||
|
<h2>Tokens</h2>
|
||||||
|
<p>Creating parsers for specfic characters and tokens can be a chore
|
||||||
|
so there is a word that, given a string token on the stack, returns
|
||||||
|
a parser that parses that particular token:</p>
|
||||||
|
<pre class="code">
|
||||||
|
(1) "begin" token
|
||||||
|
=> < a parser that parses the token "begin" >
|
||||||
|
(2) dup "this should fail" swap call .
|
||||||
|
=> f
|
||||||
|
(3) "begin a successfull parse" swap call
|
||||||
|
=> < lazy list >
|
||||||
|
(4) [ . ] leach
|
||||||
|
=> [ "begin" | " a successfull parse" ]
|
||||||
|
</pre>
|
||||||
|
<h2>Predicate matching</h2>
|
||||||
|
<p>The word 'satisfy' takes a quotation from the top of the stack and
|
||||||
|
returns a parser than when called will call the quotation with the
|
||||||
|
first item in the input string on the stack. If the quotation returns
|
||||||
|
true then the parse is successful, otherwise it fails:</p>
|
||||||
|
<pre class="code">
|
||||||
|
(1) : digit-parser ( -- parser )
|
||||||
|
[ digit? ] satisfy ;
|
||||||
|
(2) "5" digit-parser call [ . ] leach
|
||||||
|
=> [ 53 | "" ]
|
||||||
|
(3) "a" digit-parser call
|
||||||
|
=> f
|
||||||
|
</pre>
|
||||||
|
<p>Note that 'digit-parser' returns a parser, it is not the parser
|
||||||
|
itself. It is really a parser generating word like 'token'. Whereas
|
||||||
|
our 'char-a' word defined originally was a parser itself.</p>
|
||||||
|
<h2>Zero or more matches</h2>
|
||||||
|
<p>Now that we can parse single digits it would be nice to easily
|
||||||
|
parse a string of them. The '<*>' parser combinator word will do
|
||||||
|
this. It accepts a parser on the top of the stack and produces a
|
||||||
|
parser that parses zero or more of the constructs that the original
|
||||||
|
parser parsed. The result of the '<*>' generated parser will be a list
|
||||||
|
of the successful results returned by the original parser.</p>
|
||||||
|
<pre class="code">
|
||||||
|
(1) digit-parser <*>
|
||||||
|
=> < parser >
|
||||||
|
(2) "123" swap call
|
||||||
|
=> < lazy list >
|
||||||
|
(3) [ . ] leach
|
||||||
|
=> [ [ [ 49 50 51 ] ] | "" ]
|
||||||
|
[ [ [ 49 50 ] ] | "3" ]
|
||||||
|
[ [ [ 49 ] ] | "23" ]
|
||||||
|
[ f | "123" ]
|
||||||
|
</pre>
|
||||||
|
<p>In this case there are multiple successful parses. This is because
|
||||||
|
the occurrence of zero or more digits happens more than once. There is
|
||||||
|
also the 'f' case where zero digits is parsed. If only the 'longest
|
||||||
|
match' is required then the lcar of the lazy list can be used and the
|
||||||
|
remaining parse results are never produced.</p>
|
||||||
|
<h2>Manipulating parse trees</h2>
|
||||||
|
<p>The result of the previous parse was the list of characters
|
||||||
|
parsed. Sometimes you want this to be something else, like an abstract
|
||||||
|
syntax tree, or some calculation. For the digit case we may want the
|
||||||
|
actual integer number.</p>
|
||||||
|
<p>For this we can use the '<@' parser
|
||||||
|
combinator. This combinator takes a parser and a quotation on the
|
||||||
|
stack and returns a new parser. When the new parser is called it will
|
||||||
|
call the original parser to produce the results, then it will call the
|
||||||
|
quotation on each successfull result, and the result of that quotation
|
||||||
|
will be the result of the parse:</p>
|
||||||
|
<pre class="code">
|
||||||
|
(1) : digit-parser2 ( -- parser )
|
||||||
|
[ digit? ] satisfy [ CHAR: 0 - ] <@ ;
|
||||||
|
(2) "5" digit-parser2 call [ . ] leach
|
||||||
|
=> [ 5 | "" ]
|
||||||
|
</pre>
|
||||||
|
<p>Notice that now the result is the actual integer '5' rather than
|
||||||
|
character code '53'.</p>
|
||||||
|
<pre class="code">
|
||||||
|
(1) : natural-parser ( -- parser )
|
||||||
|
digit-parser2 <*> [ car 0 [ swap 10 * + ] reduce unit ] <@ ;
|
||||||
|
(2) "123" natural-parser call
|
||||||
|
=> < lazy list >
|
||||||
|
(3) [ . ] leach
|
||||||
|
=> [ [ 123 ] | "" ]
|
||||||
|
[ [ 12 ] | "3" ]
|
||||||
|
[ [ 1 ] | "23" ]
|
||||||
|
[ f | "123" ]
|
||||||
|
</pre>
|
||||||
|
<p>The number parsed is the actual integer number due to the operation
|
||||||
|
of the '<@' word. This allows parsers to not only parse the input
|
||||||
|
string but perform operations and transformations on the syntax tree
|
||||||
|
returned.</p>
|
||||||
|
<h2>Sequential combinator</h2>
|
||||||
|
<p>To create a full grammar we need a parser combinator that does
|
||||||
|
sequential compositions. That is, given two parsers, the sequential
|
||||||
|
combinator will first run the first parser, and then run the second on
|
||||||
|
the remaining text to be parsed. As the first parser returns a lazy
|
||||||
|
list, the second parser will be run on each item of the lazy list. Of
|
||||||
|
course this is done lazily so it only ends up being done when those
|
||||||
|
list items are requested. The sequential combinator word is <&>.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) "number:" token
|
||||||
|
=> < parser that parses the text 'number:' >
|
||||||
|
( 2 ) natural
|
||||||
|
=> < parser that parses natural numbers >
|
||||||
|
( 3 ) <&>
|
||||||
|
=> < parser that parses 'number:' followed by a natural >
|
||||||
|
( 4 ) "number:1000" swap call
|
||||||
|
=> < list of successes >
|
||||||
|
( 5 ) [ . ] leach
|
||||||
|
=> [ [ "number:" 1000 ] | "" ]
|
||||||
|
[ [ "number:" 100 ] | "0" ]
|
||||||
|
[ [ "number:" 10 ] | "00" ]
|
||||||
|
[ [ "number:" 1 ] | "000" ]
|
||||||
|
[ [ "number:" ] | "1000" ]
|
||||||
|
</pre>
|
||||||
|
<h2>Choice combinator</h2>
|
||||||
|
<p>As well as a sequential combinator we need an alternative
|
||||||
|
combinator. The word for this is <|>. It takes two parsers from the
|
||||||
|
stack and returns a parser that will first try the first parser. If it
|
||||||
|
succeeds then the result for that is returned. If it fails then the
|
||||||
|
second parser is tried and its result returned.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) "one" token
|
||||||
|
=> < parser that parses the text 'one' >
|
||||||
|
( 2 ) "two" token
|
||||||
|
=> < parser that parses the text 'two' >
|
||||||
|
( 3 ) <|>
|
||||||
|
=> < parser that parses 'one' or 'two' >
|
||||||
|
( 4 ) "one" over call [ . ] leach
|
||||||
|
=> [ "one" | "" ]
|
||||||
|
( 5 ) "two" swap call [ . ] leach
|
||||||
|
=> [ "two" | "" ]
|
||||||
|
</pre>
|
||||||
|
<h2>Skipping Whitespace</h2>
|
||||||
|
<p>A parser transformer exists, the word 'sp', that takes an existing
|
||||||
|
parser and returns a new one that will first skip any whitespace
|
||||||
|
before calling the original parser. This makes it easy to write
|
||||||
|
grammers that avoid whitespace without having to explicitly code it
|
||||||
|
into the grammar.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) natural
|
||||||
|
=> < a parser for natural numbers >
|
||||||
|
( 2 ) "+" token sp
|
||||||
|
=> < parser for '+' which ignores leading whitespace >
|
||||||
|
( 3 ) over sp
|
||||||
|
=> < a parser for natural numbers skipping leading whitespace >
|
||||||
|
( 4 ) <&> <&>
|
||||||
|
=> < a parser for natural + natural >
|
||||||
|
( 5 ) "1 + 2" over call lcar .
|
||||||
|
=> [ [ 1 "+" 2 ] | "" ]
|
||||||
|
( 6 ) "3+4" over call lcar .
|
||||||
|
=> [ [ 3 "+" 4 ] | "" ]
|
||||||
|
</pre>
|
||||||
|
<h2>Eval grammar example</h2>
|
||||||
|
<p>This example presents a simple grammar that will parse a number
|
||||||
|
followed by an operator and another number. A factor expression that
|
||||||
|
computes the entered value will be executed.</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) natural
|
||||||
|
=> < a parser for natural numbers >
|
||||||
|
( 2 ) "/" token "*" token "+" token "-" token <|> <|> <|>
|
||||||
|
=> < a parser for the operator >
|
||||||
|
( 3 ) sp [ unit [ eval ] append unit ] <@
|
||||||
|
=> < operator parser that skips whitespace and converts to a
|
||||||
|
factor expression >
|
||||||
|
( 4 ) natural sp
|
||||||
|
=> < a whitespace skipping natural parser >
|
||||||
|
( 5 ) <&> <&> [ call swap call ] <@
|
||||||
|
=> < a parser that parsers the expression, converts it to
|
||||||
|
factor, calls it and puts the result in the parse tree >
|
||||||
|
( 6 ) "123 + 456" over call lcar .
|
||||||
|
=> [ 579 | "" ]
|
||||||
|
( 7 ) "300-100" over call lcar .
|
||||||
|
=> [ 200 | "" ]
|
||||||
|
( 8 ) "200/2" over call lcar .
|
||||||
|
=> [ 100 | "" ]
|
||||||
|
</pre>
|
||||||
|
<p>It looks complicated when expanded as above but the entire parser,
|
||||||
|
factored a little, looks quite readable:</p>
|
||||||
|
<pre class="code">
|
||||||
|
( 1 ) : operator ( -- parser )
|
||||||
|
"/" token
|
||||||
|
"*" token <|>
|
||||||
|
"+" token <|>
|
||||||
|
"-" token <|>
|
||||||
|
[ unit [ eval ] append unit ] <@ ;
|
||||||
|
( 2 ) : expression ( -- parser )
|
||||||
|
natural
|
||||||
|
operator sp <&>
|
||||||
|
natural sp <&>
|
||||||
|
[ call swap call ] <@ ;
|
||||||
|
( 3 ) "40+2" expression call lcar .
|
||||||
|
=> [ 42 | "" ]
|
||||||
|
</pre>
|
||||||
|
<p class="footer">
|
||||||
|
News and updates to this software can be obtained from the authors
|
||||||
|
weblog: <a href="http://radio.weblogs.com/0102385">Chris Double</a>.</p>
|
||||||
|
<p id="copyright">Copyright (c) 2004, Chris Double. All Rights Reserved.</p>
|
||||||
|
</body> </html>
|
|
@ -0,0 +1,28 @@
|
||||||
|
body { background: white; color: black; }
|
||||||
|
p { margin-left: 10%; margin-right: 10%;
|
||||||
|
font: normal 100% Verdana, Arial, Helvetica; }
|
||||||
|
td { margin-left: 10%; margin-right: 10%;
|
||||||
|
font: normal 100% Verdana, Arial, Helvetica; }
|
||||||
|
table { margin-left: 10%; margin-right: 10%; }
|
||||||
|
ul { margin-left: 10%; margin-right: 10%;
|
||||||
|
font: normal 100% Verdana, Arial, Helvetica; }
|
||||||
|
ol { margin-left: 10%; margin-right: 10%;
|
||||||
|
font: normal 100% Verdana, Arial, Helvetica; }
|
||||||
|
h1 { text-align: center; margin-bottom: 0; margin-top: 1em; }
|
||||||
|
h2 { margin: 0 5% 0 7.5%; font-size: 120%; font-style: italic; }
|
||||||
|
h3 { border: 2px solid blue; border-width: 2px 0.5em 2px 0.5em;
|
||||||
|
padding: 0.2em 0.2em 0.2em 0.5em; background: #fafafa;
|
||||||
|
margin-left: 10%; margin-right: 10%; margin-top: 2em;
|
||||||
|
font-size: 100%; }
|
||||||
|
.note { border: 2px solid blue; border-width: 2px 2px 2px 2em;
|
||||||
|
padding: 0.5em 0.5em 0.5em 1em; background: #ffe; }
|
||||||
|
.code { border: 1px solid black; border-width: 1px;
|
||||||
|
padding: 0.5em; background: #ffe;
|
||||||
|
margin-left: 10%; margin-right: 10%; }
|
||||||
|
blockquote { margin-left: 25%; margin-right: 25%;
|
||||||
|
font-style: italic; }
|
||||||
|
.highlite { color: red; }
|
||||||
|
.footer { margin-top: 2.5em; border-top: 1px solid gray; color:
|
||||||
|
#AAA; font-size: 85%; padding-top: 0.33em; }
|
||||||
|
#copyright { text-align: center; color: #AAA;
|
||||||
|
font-size: 65%; }
|
|
@ -0,0 +1,56 @@
|
||||||
|
SOME NOTES ON FACTOR'S FFI
|
||||||
|
|
||||||
|
The FFI is quite a neat design and I think it is better than JNI and
|
||||||
|
similar approaches. Also, it offers better performance than libffi et
|
||||||
|
al. Of course, both of those technologies are great and Factor FFI has
|
||||||
|
its drawbacks -- namely, its not portable.
|
||||||
|
|
||||||
|
All FFI words are in the "alien" vocabulary.
|
||||||
|
|
||||||
|
The basic principle is generating machine stubs from C function
|
||||||
|
prototypes. The main entry point is the 'alien-call' word, which is
|
||||||
|
defined as simply throwing an error. However, it is given special
|
||||||
|
compilation behavior. This means it can only be used in compiled words.
|
||||||
|
|
||||||
|
Here is an example from sdl-video.factor:
|
||||||
|
|
||||||
|
: SDL_LockSurface ( surface -- )
|
||||||
|
"int" "sdl" "SDL_LockSurface" [ "surface*" ] alien-call ; compiled
|
||||||
|
|
||||||
|
The parameters are:
|
||||||
|
|
||||||
|
"int" - return type. later it will be surface*
|
||||||
|
"sdl" - library
|
||||||
|
"SDL_LockSurface" - function
|
||||||
|
[ "surface*" ] - parameters
|
||||||
|
|
||||||
|
Note the word ends with 'compiled'. This is a hack and won't be needed
|
||||||
|
later.
|
||||||
|
|
||||||
|
Parameters and return values are C type names. C types include the
|
||||||
|
following:
|
||||||
|
|
||||||
|
- char - 1 byte signed
|
||||||
|
- short - 2 bytes signed
|
||||||
|
- int - 4 bytes signed
|
||||||
|
- void* - word-size width field, can only be used as a parameter
|
||||||
|
|
||||||
|
Structs can be defined in this fashion:
|
||||||
|
|
||||||
|
BEGIN-STRUCT: point
|
||||||
|
FIELD: int x
|
||||||
|
FIELD: int y
|
||||||
|
END-STRUCT
|
||||||
|
|
||||||
|
And then referred to in parameter type specifiers as "point*". Struct
|
||||||
|
return values are not yet supported.
|
||||||
|
|
||||||
|
Enumerations can be defined; they simply become words that push
|
||||||
|
integers:
|
||||||
|
|
||||||
|
BEGIN-ENUM: 0
|
||||||
|
ENUM: int xuzzy
|
||||||
|
ENUM: int bax
|
||||||
|
END-ENUM
|
||||||
|
|
||||||
|
The parameter to BEGIN-ENUM specifies the starting index.
|
|
@ -0,0 +1,898 @@
|
||||||
|
IMPLEMENTATION OF THE FACTOR COMPILER
|
||||||
|
|
||||||
|
Compilation of Factor is a messy business, driven by heuristics and not
|
||||||
|
formal theory. The compiler is inherently limited -- some expressions
|
||||||
|
cannot be compiled by definition. The programmer must take care to
|
||||||
|
ensure that performance-critical sections of code are written such that
|
||||||
|
they can be compiled.
|
||||||
|
|
||||||
|
=== Introduction
|
||||||
|
|
||||||
|
==== The problem
|
||||||
|
|
||||||
|
The Factor interpreter introduces a lot of overhead:
|
||||||
|
|
||||||
|
- Execution of a quotation involves iteration down a linked list.
|
||||||
|
|
||||||
|
- Stack access is not as fast as local variables, since Java
|
||||||
|
bound-checks all array accesses.
|
||||||
|
|
||||||
|
- At the lowest level, everything is expressed as Java reflection calls
|
||||||
|
to the Factor and Java platform libraries. Java reflection is not as
|
||||||
|
fast as statically-compiled Java calls.
|
||||||
|
|
||||||
|
- Since Factor is dynamically-typed, intermediate values on the stack
|
||||||
|
are all stored as java.lang.Object types, so type checks and
|
||||||
|
possibly coercions must be done at each step of the computation.
|
||||||
|
|
||||||
|
==== The solution
|
||||||
|
|
||||||
|
The following optimizations naturally suggest themselves, and lead to
|
||||||
|
the implementation of the Factor compiler:
|
||||||
|
|
||||||
|
- Compiling Factor code down to Java platform bytecode.
|
||||||
|
|
||||||
|
- Using virtual machine local variables instead of an array stack to
|
||||||
|
store intermediate values.
|
||||||
|
|
||||||
|
- Statically compiling in Java calls where the class, method and
|
||||||
|
variable names are known ahead of time.
|
||||||
|
|
||||||
|
- Type inference and soft typing to eliminate unnecessary type checks.
|
||||||
|
(At the time of writing, this is in progress and is not documented in
|
||||||
|
this paper.)
|
||||||
|
|
||||||
|
=== Preliminaries: interpreter internals
|
||||||
|
|
||||||
|
A word object is essentially a property list. The one property we are
|
||||||
|
concerned with here is "def", which holds a FactorWordDefinition object.
|
||||||
|
|
||||||
|
The accessor word "worddef" pushes the "def" slot of a given word name
|
||||||
|
or word object:
|
||||||
|
|
||||||
|
0] "+" worddef .
|
||||||
|
#<factor.FactorCompoundDefinition: +>
|
||||||
|
|
||||||
|
Generally, the word definition is an opaque object, however there are
|
||||||
|
various ways to deconstruct it, which will not be convered here (see the
|
||||||
|
worddef>list word if you are interested).
|
||||||
|
|
||||||
|
When a word object is being executed, the eval() method of its
|
||||||
|
definition is invoked. The eval() method takes one parameter, which is
|
||||||
|
the FactorInterpreter instance. The interpreter instance provides access
|
||||||
|
to the stacks, global namespace, vocabularies, and so on.
|
||||||
|
|
||||||
|
(In this article, we will use the term "word" and "word definition"
|
||||||
|
somewhat interchangably; this does not cause any confusion. If a "word"
|
||||||
|
is mentioned where one would expect a definition, simply assume the
|
||||||
|
"def" slot of the word is being accessed.)
|
||||||
|
|
||||||
|
The class FactorWordDefinition is abstract; a number of subclasses
|
||||||
|
exist:
|
||||||
|
|
||||||
|
- FactorCompoundDefinition: a standard colon definition consisting of
|
||||||
|
a quotation; for example, : sq dup * ; is syntax for a compound
|
||||||
|
definition named "sq" with quotation [ dup * ].
|
||||||
|
|
||||||
|
Of course, its eval() method simply pushes the quotation on the
|
||||||
|
interpreter's callstack.
|
||||||
|
|
||||||
|
- FactorShuffleDefinition: a stack rearrangement word, whose syntax is
|
||||||
|
described in detail in parser.txt. For example,
|
||||||
|
~<< swap a b -- b a >>~ is syntax for a shuffle definition named
|
||||||
|
"swap" that exchanges the top two values on the data stack.
|
||||||
|
|
||||||
|
- FactorPrimitiveDefinition: primitive word definitions are written in
|
||||||
|
Java. Various concrete subclasses of this class in the
|
||||||
|
factor.primitives package provide implementations of eval().
|
||||||
|
|
||||||
|
When a word definition is compiled, the compiler dynamically generates a
|
||||||
|
new class, creates a new instance, and replaces the "def" slot of the
|
||||||
|
word in question with the instance of the compiled class.
|
||||||
|
|
||||||
|
So the compiler's primary job is to generate appropriate Java bytecode
|
||||||
|
for the eval() method.
|
||||||
|
|
||||||
|
=== Preliminaries: the specimen
|
||||||
|
|
||||||
|
Consider the following (naive) implementation of the Fibonacci sequence:
|
||||||
|
|
||||||
|
: fib ( n -- nth fibonacci number )
|
||||||
|
dup 1 <= [
|
||||||
|
drop 1
|
||||||
|
] [
|
||||||
|
pred dup fib swap pred fib +
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
A quick overview of the words used here:
|
||||||
|
|
||||||
|
- dup: a shuffle word that duplicates the top of the stack.
|
||||||
|
|
||||||
|
- <=: compare the top two numbers on the stack.
|
||||||
|
|
||||||
|
- drop: remove the top of the stack.
|
||||||
|
|
||||||
|
- pred: decrement the top of the stack by one. Indeed, it is defined as
|
||||||
|
simply : pred 1 - ;.
|
||||||
|
|
||||||
|
- swap: exchange the top two stack elements.
|
||||||
|
|
||||||
|
- +: add the top two stack elements.
|
||||||
|
|
||||||
|
- ifte: execute one of two given quotations, depending on the condition
|
||||||
|
on the stack.
|
||||||
|
|
||||||
|
=== Java reflection
|
||||||
|
|
||||||
|
The biggest performance improvement comes from the transformation of
|
||||||
|
Java reflection calls into static bytecode.
|
||||||
|
|
||||||
|
Indeed, when the compiler was first written, the only type of word it
|
||||||
|
could compile were such simple expressions that interfaced with Java and
|
||||||
|
nothing else.
|
||||||
|
|
||||||
|
In the above definition of "fib", the three key words <= - and + (note
|
||||||
|
that - is not referenced directly, but rather is a factor of the word
|
||||||
|
pred). All three of these words are implemented as Java calls into the
|
||||||
|
Factor math library:
|
||||||
|
|
||||||
|
: <= ( a b -- boolean )
|
||||||
|
[
|
||||||
|
"java.lang.Number" "java.lang.Number"
|
||||||
|
] "factor.math.FactorMath" "lessEqual" jinvoke-static ;
|
||||||
|
|
||||||
|
: - ( a b -- a-b )
|
||||||
|
[
|
||||||
|
"java.lang.Number" "java.lang.Number"
|
||||||
|
] "factor.math.FactorMath" "subtract" jinvoke-static ;
|
||||||
|
|
||||||
|
: + ( a b -- a+b )
|
||||||
|
[
|
||||||
|
"java.lang.Number" "java.lang.Number"
|
||||||
|
] "factor.math.FactorMath" "add" jinvoke-static ;
|
||||||
|
|
||||||
|
During interpretation, the execution of one of these words involves a
|
||||||
|
lot of overhead. First, the argument list is transformed into a Java
|
||||||
|
Class[] array; then the Class object corresponding to the containing
|
||||||
|
class is looked up; then the appropriate Method object defined in this
|
||||||
|
class is looked up; then the method is invoked, by passing it an
|
||||||
|
Object[] array consisting of arguments from the stack.
|
||||||
|
|
||||||
|
As one might guess, this is horribly inefficient. Indeed, look at the
|
||||||
|
time taken to compute the 25th Fibonacci number using pure
|
||||||
|
interpretation (of course depending on your hardware, results might
|
||||||
|
vary):
|
||||||
|
|
||||||
|
0] [ 25 fib ] time
|
||||||
|
24538
|
||||||
|
|
||||||
|
One quickly notices that in fact, all the overhead from the reflection
|
||||||
|
API is unnecessary; the containing class, method name and argument types
|
||||||
|
are, after all, known ahead of time.
|
||||||
|
|
||||||
|
For instance, the word "<=" might be compiled into the following
|
||||||
|
pseudo-bytecode (the details are a bit more complex in reality; we'll
|
||||||
|
get to it later):
|
||||||
|
|
||||||
|
MOVE datastack[top - 2] to JVM stack // get operands in right order
|
||||||
|
CHECKCAST java/lang/Number
|
||||||
|
MOVE datastack[top - 1] to JVM stack
|
||||||
|
CHECKCAST java/lang/Number
|
||||||
|
DECREMENT datastack.top 2 // pop the operands
|
||||||
|
INVOKESTATIC // invoke the method
|
||||||
|
"factor/FactorMath"
|
||||||
|
"lessEqual"
|
||||||
|
"(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;"
|
||||||
|
MOVE JVM stack top to datastack // push return value
|
||||||
|
|
||||||
|
Notice that no dynamic class or method lookups are done, and no arrays
|
||||||
|
are constructed; in fact, a modern Java virtual machine with a native
|
||||||
|
code compiler should be able to transform an INVOKESTATIC into a simple
|
||||||
|
subroutine call.
|
||||||
|
|
||||||
|
So what how much overhead is eliminated in practice? It is easy to find
|
||||||
|
out:
|
||||||
|
|
||||||
|
5] [ + - <= ] [ compile ] each
|
||||||
|
1] [ 25 fib ] time
|
||||||
|
937
|
||||||
|
|
||||||
|
This is still quite slow -- however, already we've gained a 26x speed
|
||||||
|
improvement!
|
||||||
|
|
||||||
|
Words consisting entirely of literal parameters to Java primitives such
|
||||||
|
as jinvoke, jnew, jvar-get/set, or jvar-get/set-static are compiled in a
|
||||||
|
similar manner; there is nothing new there.
|
||||||
|
|
||||||
|
=== First attempt at compiling compound definitions
|
||||||
|
|
||||||
|
Now consider the problem of compiling a word that does not directly call
|
||||||
|
Java primitives, but instead calls other words, which are already been
|
||||||
|
compiled.
|
||||||
|
|
||||||
|
For instance, consider the following word (recall that (...) is a comment!):
|
||||||
|
|
||||||
|
: mag2 ( x y -- sqrt[x*x+y*y] )
|
||||||
|
swap dup * swap dup * + sqrt ;
|
||||||
|
|
||||||
|
Lets assume that 'swap', 'dup', '*' and '+' are defined as before, and
|
||||||
|
that 'sqrt' is an already-compiled word that calls into the math
|
||||||
|
library.
|
||||||
|
|
||||||
|
Assume that the pseudo-bytecode INVOKEWORD <word> invokes the "eval"
|
||||||
|
method of a FactorWordDefinition instance.
|
||||||
|
|
||||||
|
(In reality, it is a bit more complex:
|
||||||
|
|
||||||
|
GETFIELD ... some field that stores a FactorWordDefinition instance ...
|
||||||
|
ALOAD 0 // push interpreter parameter to eval() on the stack
|
||||||
|
INVOKEVIRTUAL
|
||||||
|
"factor/FactorWordDefinition"
|
||||||
|
"eval"
|
||||||
|
"(Lfactor/FactorInterpreter;)V"
|
||||||
|
|
||||||
|
However the above takes up more space and adds no extra information over
|
||||||
|
the INVOKE notation.)
|
||||||
|
|
||||||
|
Now, we have the tools necessary to try compiling "mag2" as follows:
|
||||||
|
|
||||||
|
INVOKEWORD swap
|
||||||
|
INVOKEWORD dup
|
||||||
|
INVOKEWORD *
|
||||||
|
INVOKEWORD swap
|
||||||
|
INVOKEWORD dup
|
||||||
|
INVOKEWORD *
|
||||||
|
INVOKEWORD +
|
||||||
|
INVOKEWORD sqrt
|
||||||
|
|
||||||
|
In other words, the words still shuffle values back and forth on the
|
||||||
|
interpreter data stack as before; however, instead of the interpreter
|
||||||
|
iterating down a word thread, compiled bytecode invokes words directly.
|
||||||
|
|
||||||
|
This might seem like the obvious approach; however, it turns out it
|
||||||
|
brings very little performance benefit over simply iterating down a
|
||||||
|
linked list representing a quotation!
|
||||||
|
|
||||||
|
What we would like to do is just eliminate use of the interpreter's
|
||||||
|
stack for intermediate values altogether, and just loading the inputs at
|
||||||
|
the beginning and storing them at the end.
|
||||||
|
|
||||||
|
=== Avoiding the interpreter stack
|
||||||
|
|
||||||
|
The JVM is a stack machine, however its semantics are so different that
|
||||||
|
a direct mapping of interpreter stack use to stack bytecode would not
|
||||||
|
be feasable:
|
||||||
|
|
||||||
|
- No arbitrary stack access is allowed in Java; only a few, fixed stack
|
||||||
|
bytecodes like POP, DUP, SWAP are provided.
|
||||||
|
|
||||||
|
- A Java function receives input parameters in local variables, not in
|
||||||
|
the JVM stack.
|
||||||
|
|
||||||
|
In fact, the second point suggests that it is a better idea is to use
|
||||||
|
JVM *local variables* for temporary storage in compiled definitions.
|
||||||
|
|
||||||
|
Since no indirect addressing of locals is permitted, stack positions
|
||||||
|
used in computations must be known ahead of time. This process is known
|
||||||
|
as "stack effect deduction", and is the key concept of the Factor
|
||||||
|
compiler.
|
||||||
|
|
||||||
|
=== Fundamental idea: eval/core split
|
||||||
|
|
||||||
|
Earlier, we showed pseudo-bytecode for the word <=, however it was noted
|
||||||
|
that the reality is a bit more complicated.
|
||||||
|
|
||||||
|
Recall that FactorWordDefinition.eval() takes an interpreter instance.
|
||||||
|
It is the responsibility of this method to marshall and unmarshall
|
||||||
|
values on the interpreter stack before and after the word performs any
|
||||||
|
computation on the values.
|
||||||
|
|
||||||
|
In actual fact, compiled word definitions have a second method named
|
||||||
|
core(). Instead of accessing the interpreter data stack directly, this
|
||||||
|
method takes inputs from formal parameters passed to the method, in the
|
||||||
|
natural stack order.
|
||||||
|
|
||||||
|
So, lets look at possible disassembly for the eval() and core() methods
|
||||||
|
of the word <=:
|
||||||
|
|
||||||
|
void eval(FactorInterpreter interp)
|
||||||
|
|
||||||
|
ALOAD 0 // push interpreter instance on JVM stack
|
||||||
|
MOVE datastack[top - 2] to JVM stack // get operands in right order
|
||||||
|
CHECKCAST java/lang/Number
|
||||||
|
MOVE datastack[top - 1] to JVM stack
|
||||||
|
CHECKCAST java/lang/Number
|
||||||
|
DECREMENT datastack.top 2 // pop the operands
|
||||||
|
INVOKESTATIC // invoke the method
|
||||||
|
... compiled definition class name ...
|
||||||
|
"core"
|
||||||
|
"(Lfactor/FactorInterpreter;Ljava/lang/Object;Ljava/lang/Object;)
|
||||||
|
Ljava/lang/Object;"
|
||||||
|
MOVE JVM stack top to datastack // push return value
|
||||||
|
|
||||||
|
Object core(FactorInterpreter interp, Object x, Object y)
|
||||||
|
|
||||||
|
ALOAD 0 // push formal parameters
|
||||||
|
ALOAD 1
|
||||||
|
ALOAD 2
|
||||||
|
INVOKESTATIC // invoke the actual method
|
||||||
|
"factor/FactorMath"
|
||||||
|
"lessEqual"
|
||||||
|
"(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;"
|
||||||
|
ARETURN // pass return value up to eval()
|
||||||
|
|
||||||
|
==== Using the JVM stack and locals for intermediates
|
||||||
|
|
||||||
|
At first glance it seems nothing was achieved with the eval/core split,
|
||||||
|
excepting an extra layer of overhead.
|
||||||
|
|
||||||
|
However, the new revalation here is that compiled word definitions can
|
||||||
|
call each other's core methods *directly*, passing in the parameters
|
||||||
|
through JVM local variables, without the interpreter data stack being
|
||||||
|
involved!
|
||||||
|
|
||||||
|
Instead of pseudo-bytecode, from now on we will consider a very
|
||||||
|
abstract, high level "register transfer language". The extra verbosity
|
||||||
|
of bytecode will only distract from the key ideas.
|
||||||
|
|
||||||
|
Tentatively, we would like to compile the word 'mag2' as follows:
|
||||||
|
|
||||||
|
r0 * r0 -> r0
|
||||||
|
r1 * r1 -> r1
|
||||||
|
r0 + r1 -> r0
|
||||||
|
sqrt r0 -> r0
|
||||||
|
return r0
|
||||||
|
|
||||||
|
However this looks very different from the original, RPN definition; in
|
||||||
|
particular, we have named values, and the stack operations are gone!
|
||||||
|
|
||||||
|
As it turns out, there is a automatic way to transform the stack program
|
||||||
|
'mag2' into the register transfer program above (the reverse is also
|
||||||
|
possible, but will not be discussed here).
|
||||||
|
|
||||||
|
==== Stack effect deduction
|
||||||
|
|
||||||
|
Consider the following quotation:
|
||||||
|
|
||||||
|
[ swap dup * swap dup * + sqrt ]
|
||||||
|
|
||||||
|
The transformation of the above stack code into register code consists
|
||||||
|
of two passes.
|
||||||
|
|
||||||
|
(A one-pass approach is also possible; however because of the design of
|
||||||
|
the assembler used by the compiler, an extra pass will be required
|
||||||
|
elsewhere if this transformation described here is single-pass).
|
||||||
|
|
||||||
|
The first pass is simply to determine the total number of input and
|
||||||
|
output parameters of the quotation (its "stack effect"). We proceed as
|
||||||
|
follows.
|
||||||
|
|
||||||
|
1. Create a 'simulated' datastack. It does not contain actual values,
|
||||||
|
but rather markers.
|
||||||
|
|
||||||
|
Set the input parameter count to zero.
|
||||||
|
|
||||||
|
2. Iterate through each element of the quotation, and act as follows:
|
||||||
|
|
||||||
|
- If the element is a literal, allocate a simulated stack entry.
|
||||||
|
|
||||||
|
- If the element is a word, ensure that the stack has at least as
|
||||||
|
many items as the word's input parameter count.
|
||||||
|
|
||||||
|
If the stack does not have enough items, increment the input
|
||||||
|
parameter count by the difference between the stack item count and
|
||||||
|
the word's expected input parameter count, and fill the stack with
|
||||||
|
the difference.
|
||||||
|
|
||||||
|
Decrement the stack pointer by the word's input parameter count.
|
||||||
|
|
||||||
|
Increment the stack pointer by the word's output parameter count,
|
||||||
|
filling the new entries with markers.
|
||||||
|
|
||||||
|
3. When the end of the quotation is reached, the output parameter count
|
||||||
|
is the number of items on the simulated stack. The input parameter
|
||||||
|
count is the value of the intermediate parameter created in step 1.
|
||||||
|
|
||||||
|
Note that this algorithm is recursive -- to determine the stack effect
|
||||||
|
of a word, the stack effects of all its factors must be known. For now,
|
||||||
|
assume the stack effects of words that use the Java primitives are
|
||||||
|
"trivially" known.
|
||||||
|
|
||||||
|
A brief walkthrough of the above algorithm for the quotation
|
||||||
|
[ swap dup * swap dup * + sqrt ]:
|
||||||
|
|
||||||
|
swap - the simulated stack is empty but swap expects two parameters,
|
||||||
|
so the input parameter count becomes 2.
|
||||||
|
|
||||||
|
two empty markers are pushed on the simulated stack:
|
||||||
|
# #
|
||||||
|
|
||||||
|
dup - requires one parameter, which is already present.
|
||||||
|
another empty marker is pushed on the simulated stack:
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
* - requires two parameters, and returns one parameter, so the
|
||||||
|
simulated stack is now:
|
||||||
|
|
||||||
|
# #
|
||||||
|
|
||||||
|
swap - requires and returns two parameters.
|
||||||
|
|
||||||
|
# #
|
||||||
|
|
||||||
|
dup - requires one, returns two parameters.
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
* - requires two, and returns one parameter.
|
||||||
|
|
||||||
|
# #
|
||||||
|
|
||||||
|
+ - requires two, and returns one parameter.
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
sqrt - requires one, and returns one parameter.
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
So the input parameter count is two, and the output parameter count is
|
||||||
|
one (since at the end of the quotation the simulated datastack contains
|
||||||
|
one item marker).
|
||||||
|
|
||||||
|
==== The dataflow algorithm
|
||||||
|
|
||||||
|
The second pass of the compiler algorithm relies on the stack effect
|
||||||
|
already being known. It consists of these steps:
|
||||||
|
|
||||||
|
1. Create a new simulated stack. For each input parameter, a new entry
|
||||||
|
is allocated. This time, entries are not blank markers, but rather
|
||||||
|
register numbers.
|
||||||
|
|
||||||
|
2. Iterate through each element of the quotation, and act as follows:
|
||||||
|
|
||||||
|
- If the element is a literal, allocate a simulated stack entry.
|
||||||
|
This time, allocation finds an unused register number by checking
|
||||||
|
each stack entry.
|
||||||
|
|
||||||
|
- If the element is a shuffle word, apply the shuffle to the
|
||||||
|
simulated stack *and do not emit any code!*
|
||||||
|
|
||||||
|
- If the element is another word, pop the appropriate number of
|
||||||
|
register numbers from the simulated stack, and emit assembly code
|
||||||
|
for invoking the word with parameters stored in these registers.
|
||||||
|
|
||||||
|
Decrement the simulated stack pointer by the word's input parameter
|
||||||
|
count.
|
||||||
|
|
||||||
|
Increment the simulated stack pointer by the word's output
|
||||||
|
parameter count, filling the new entries with newly-allocated
|
||||||
|
register numbers.
|
||||||
|
|
||||||
|
Emit assembly code for moving the return values of the word into
|
||||||
|
the newly allocated registers.
|
||||||
|
|
||||||
|
Voila! The 'simulated stack' is a compile time only notion, and the
|
||||||
|
resulting emitted code does not explicitly reference any stacks at all;
|
||||||
|
in fact, applying this algorithm to the following quotation:
|
||||||
|
|
||||||
|
[ swap dup * swap dup * + sqrt ]
|
||||||
|
|
||||||
|
Yields the following output:
|
||||||
|
|
||||||
|
r0 * r0 -> r0
|
||||||
|
r1 * r1 -> r1
|
||||||
|
r0 + r1 -> r0
|
||||||
|
sqrt r0 -> r0
|
||||||
|
return r0
|
||||||
|
|
||||||
|
==== Multiple return values
|
||||||
|
|
||||||
|
A minor implementation detail is multiple return values. Java does not
|
||||||
|
support them directly, but a Factor word can return any number of
|
||||||
|
values. This is implemented by temporarily using the interpreter data
|
||||||
|
stack to return multiple values. This is the only time the interpreter
|
||||||
|
data stack is used.
|
||||||
|
|
||||||
|
==== The call stack
|
||||||
|
|
||||||
|
Sometimes Factor code uses the call stack as an 'extra hand' for
|
||||||
|
temporary storage:
|
||||||
|
|
||||||
|
dup >r + r> *
|
||||||
|
|
||||||
|
The dataflow algorithm can be trivially generalized with two simulated
|
||||||
|
stacks; there is nothing more to be said about this.
|
||||||
|
|
||||||
|
=== Questioning assumptions
|
||||||
|
|
||||||
|
The dataflow compilation algorithm gives us another nice performance
|
||||||
|
improvement. However, the algorithm assumes that the stack effect of
|
||||||
|
each word is known a priori, or can be deduced using the algorithm.
|
||||||
|
|
||||||
|
The algorithm falls down when faced with the following more complicated
|
||||||
|
expressions:
|
||||||
|
|
||||||
|
- Combinators calling the 'call' and 'ifte' primitives
|
||||||
|
|
||||||
|
- Recursive words
|
||||||
|
|
||||||
|
So ironically, this algorithm is unsuitable for code where it would help
|
||||||
|
the most -- complex code with a lot of branching, and tight loops and
|
||||||
|
recursions.
|
||||||
|
|
||||||
|
=== Eliminating explicit 'call':
|
||||||
|
|
||||||
|
As described above, the dataflow algorithm would break when it
|
||||||
|
encountered the 'call' primitive:
|
||||||
|
|
||||||
|
[ 2 + ] 5 swap call
|
||||||
|
|
||||||
|
The 'call' primitive executes the quotation at the top of the stack. So
|
||||||
|
its stack effect depends on its input parameter!
|
||||||
|
|
||||||
|
The first problem we faced was compilation of Java reflection
|
||||||
|
primitives. A critical observation was that all the information to
|
||||||
|
compile them efficiently was 'already there' in the source.
|
||||||
|
|
||||||
|
Our intuitition tells us that in the above code, the occurrence of
|
||||||
|
'call' *always* receives the parameter of [ 2 + ]; so somehow, the
|
||||||
|
quotation can be transformed into the following, which we can already
|
||||||
|
compile:
|
||||||
|
|
||||||
|
[ 2 + ] 5 swap drop 2 +
|
||||||
|
^^^^^^^^
|
||||||
|
"immediate instantiation" of 'call'
|
||||||
|
|
||||||
|
Or indeed, once the unused literal [ 2 + ] is factored out, simply:
|
||||||
|
|
||||||
|
5 2 +
|
||||||
|
|
||||||
|
==== Generalizing the 'simulated stack'
|
||||||
|
|
||||||
|
It might seem surprising that such expressions can be easily compiled,
|
||||||
|
once the 'simulated stack' is generalized such that it can hold literal
|
||||||
|
values!
|
||||||
|
|
||||||
|
The only change that needs to be made, is that in both passes, when a
|
||||||
|
literal is encountered, it is pushed directly on the simulated stack.
|
||||||
|
|
||||||
|
Also, when the primitive 'call' is encountered, its stack effect is
|
||||||
|
assumed to be the stack effect of the literal quotation at the top of
|
||||||
|
the simulated stack.
|
||||||
|
|
||||||
|
(What if the top of the simulated stack is a register number? The word
|
||||||
|
cannot be compiled, since the stack effect can potentially be
|
||||||
|
arbitrary!)
|
||||||
|
|
||||||
|
Being able to compile 'call' whose parameters are literals from the
|
||||||
|
same word definition doesn't really add nothing new.
|
||||||
|
|
||||||
|
A real breakthrough would be compiling "combinators"; words that take
|
||||||
|
parameters that are themselves quotations.
|
||||||
|
|
||||||
|
As it turns out, combinators themselves are not compiled -- however,
|
||||||
|
specific *instances* of combinators in other word definitions are.
|
||||||
|
|
||||||
|
For example, we can rewrite our word 'mag2' as follows:
|
||||||
|
|
||||||
|
: mag2 ( x y -- sqrt[x*x+y*y] )
|
||||||
|
[ sq ] 2apply + sqrt ;
|
||||||
|
|
||||||
|
Where 2apply is defined as follows:
|
||||||
|
|
||||||
|
: 2apply ( x y [ code ] -- )
|
||||||
|
2dup 2>r nip call 2r> call ;
|
||||||
|
|
||||||
|
How can we compile this new, equivalent, form of 'mag2'?
|
||||||
|
|
||||||
|
==== Inline words
|
||||||
|
|
||||||
|
Normally, when the dataflow algorithm encounters a word as an element
|
||||||
|
of a quotation, a call to that word's core() method is emitted. However,
|
||||||
|
if the word is compiled 'immediately', its definition is substituted in.
|
||||||
|
|
||||||
|
Assume for a second that in the new form of 'mag2', the word '2apply' is
|
||||||
|
compiled inline (ignoring the specifics of how this decision is made).
|
||||||
|
In other words, it is as if 'mag2' was defined as follows:
|
||||||
|
|
||||||
|
: mag2 ( x y -- sqrt[x*x+y*y] )
|
||||||
|
[ sq ] 2dup 2>r nip call 2r> call + sqrt ;
|
||||||
|
|
||||||
|
However, we already have a way of compiling the above code; in fact it
|
||||||
|
is compiled into the equivalent of:
|
||||||
|
|
||||||
|
: mag2 ( x y -- sqrt[x*x+y*y] )
|
||||||
|
[ sq ] 2dup 2>r nip drop sq 2r> drop sq + sqrt ;
|
||||||
|
^^^^^^^ ^^^^^^^
|
||||||
|
immediate instantiation of 'call'
|
||||||
|
|
||||||
|
As an aside, recall that the stack words 2dup, 2>r, nip, drop, and 2r>
|
||||||
|
do not emit any code, and the 'drop' of the literal [ sq ] ensures that
|
||||||
|
it never makes it to the compiled definition. The end-result is that the
|
||||||
|
register-transfer code is identical to the earlier definition of 'mag2'
|
||||||
|
which did not involve 2apply:
|
||||||
|
|
||||||
|
r0 * r0 -> r0
|
||||||
|
r1 * r1 -> r1
|
||||||
|
r0 + r1 -> r0
|
||||||
|
sqrt r0 -> r0
|
||||||
|
return r0
|
||||||
|
|
||||||
|
So, how is the decision made to compile a word inline, or not? It is
|
||||||
|
quite simple. If the word has a deducable stack effect on the simulated
|
||||||
|
stack of the current compilation, but it does *not* have a deducable
|
||||||
|
stack effect on an empty simulated stack, it is compiled immediate.
|
||||||
|
|
||||||
|
For example, the following word has a deducable stack effect, regardless
|
||||||
|
of the values of any literals on the simulated stack:
|
||||||
|
|
||||||
|
: sq ( x -- x^2 )
|
||||||
|
dup * ;
|
||||||
|
|
||||||
|
So the word 'sq' is always compiled normally.
|
||||||
|
|
||||||
|
However, the '2apply' word we saw earlier does not have a deducable
|
||||||
|
stack effect unless there is a literal quotation at the top of the
|
||||||
|
simulated stack:
|
||||||
|
|
||||||
|
: 2apply ( x y [ code ] -- )
|
||||||
|
2dup 2>r nip call 2r> call ;
|
||||||
|
|
||||||
|
So it is compiled inline.
|
||||||
|
|
||||||
|
Sometimes it is desirable to have short non-combinator words inlined.
|
||||||
|
While this is not necessary (whereas non-inlined combinators do not
|
||||||
|
compile), it can increase performance, especially if the word returns
|
||||||
|
multiple values (and without inlining, the interpreter datastack will
|
||||||
|
need to be used).
|
||||||
|
|
||||||
|
To mark a word for inline compilation, use the word 'inline' like so:
|
||||||
|
|
||||||
|
: sq ( x -- x^2 )
|
||||||
|
dup * ; inline
|
||||||
|
|
||||||
|
The word 'inline' sets the inline slot of the most recently defined word
|
||||||
|
object.
|
||||||
|
|
||||||
|
(Indeed, to push a reference to the most recently defined word object,
|
||||||
|
use the word 'word').
|
||||||
|
|
||||||
|
=== Branching
|
||||||
|
|
||||||
|
The only branching primitive supported by factor is 'ifte'. The syntax
|
||||||
|
is as follows:
|
||||||
|
|
||||||
|
2 2 + 4 = ( condition that leaves boolean on the stack )
|
||||||
|
[
|
||||||
|
( code to execute if condition is true )
|
||||||
|
] [
|
||||||
|
( code to execute if condition is false )
|
||||||
|
] ifte
|
||||||
|
|
||||||
|
Note that the different components might be spread between words, and
|
||||||
|
affected by stack operations in transit. Due to the dataflow algorithm
|
||||||
|
and inlining, all useful cases can be handled correctly.
|
||||||
|
|
||||||
|
==== Not all branching forms have a deducable stack effect
|
||||||
|
|
||||||
|
The first observation we gain is that if the two branches leave the
|
||||||
|
stack in inconsistent states, then stack positions used by subsequent
|
||||||
|
code will depend on the outcome of the branch.
|
||||||
|
|
||||||
|
This practice is discouraged anyway -- it leads to hard-to-understand
|
||||||
|
code -- so it is not supported by the compiler. If you must do it, the
|
||||||
|
words will always run in the interpreter.
|
||||||
|
|
||||||
|
Attempting to compile or balance an expression with such a branch raises
|
||||||
|
an error:
|
||||||
|
|
||||||
|
9] : bad-ifte 3 = [ 1 2 3 ] [ 2 2 + ] ifte ;
|
||||||
|
10] word effect .
|
||||||
|
break called.
|
||||||
|
|
||||||
|
:r prints the callstack.
|
||||||
|
:j prints the Java stack.
|
||||||
|
:x returns to top level.
|
||||||
|
:s returns to top level, retaining the data stack.
|
||||||
|
:g continues execution (but expect another error).
|
||||||
|
|
||||||
|
ERROR: Stack effect of [ 1 2 3 ] ( java.lang.Object -- java.lang.Object
|
||||||
|
java.lang.Object java.lang.Object ) is inconsistent with [ 2 2 + ] (
|
||||||
|
java.lang.Object -- java.lang.Object )
|
||||||
|
Head is ( java.lang.Object -- )
|
||||||
|
Recursive state:
|
||||||
|
[ #<ifte,base=null,effect=( java.lang.Object -- boolean java.lang.Object
|
||||||
|
java.lang.Object ); null.null()> #<bad-ifte,base=null,effect=( -- );
|
||||||
|
null.null()> ]
|
||||||
|
|
||||||
|
==== Merging
|
||||||
|
|
||||||
|
Lets return to our register transfer language, and add a branching
|
||||||
|
notation:
|
||||||
|
|
||||||
|
- two-instruction sequence to branch to <label> if <register> is null
|
||||||
|
ALOAD <register>
|
||||||
|
IFNULL <label>
|
||||||
|
|
||||||
|
- unconditional goto to <label>
|
||||||
|
GOTO <label>
|
||||||
|
|
||||||
|
So a simple conditional
|
||||||
|
|
||||||
|
rot [
|
||||||
|
(true)
|
||||||
|
] [
|
||||||
|
(false)
|
||||||
|
] ifte
|
||||||
|
|
||||||
|
Will be compiled as follows, where the inputs are in registers 1, 2, 3
|
||||||
|
|
||||||
|
1 ALOAD 1
|
||||||
|
2 IFNULL 5
|
||||||
|
3 (true)
|
||||||
|
4 GOTO 6
|
||||||
|
5 (false)
|
||||||
|
6 RETURN
|
||||||
|
|
||||||
|
However the question arises, what becomes of the simulated stack after
|
||||||
|
the branches are done.
|
||||||
|
|
||||||
|
For example, consider this snippet:
|
||||||
|
|
||||||
|
random-int random-int random-boolean [
|
||||||
|
swap
|
||||||
|
] [
|
||||||
|
|
||||||
|
] ifte
|
||||||
|
|
||||||
|
The first three words followed by the branch itself are compiled like
|
||||||
|
so:
|
||||||
|
|
||||||
|
1 1 <- random-int
|
||||||
|
2 2 <- random-int
|
||||||
|
3 3 <- random-boolean
|
||||||
|
4 ALOAD 3
|
||||||
|
5 IFNULL 8
|
||||||
|
|
||||||
|
However, a problem arises because if the true branch is taken, the
|
||||||
|
simulated stack contains register 1 at the top, and register 2 below;
|
||||||
|
but if the false branch is taken, it is the opposite!
|
||||||
|
|
||||||
|
The solution is to "merge" the stacks at the end of each branch. So
|
||||||
|
the remainder of our code might be compiled as follows:
|
||||||
|
|
||||||
|
6 1 <-> 2 // new notation: exchange registers 1 and 2
|
||||||
|
7 GOTO 8
|
||||||
|
8 RETURN
|
||||||
|
|
||||||
|
=== Recursion
|
||||||
|
|
||||||
|
Consider our old friend 'fib':
|
||||||
|
|
||||||
|
: fib ( n -- nth fibonacci number )
|
||||||
|
dup 1 <= [
|
||||||
|
drop 1
|
||||||
|
] [
|
||||||
|
pred dup fib swap pred fib +
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
Using the tools we have, we cannot deduce its stack effect yet, since
|
||||||
|
the false branch of the 'ifte' refers to the word 'fib' itself.
|
||||||
|
|
||||||
|
A critical observation is if the word is to complete, eventually, the
|
||||||
|
test will fail and 'drop 1' will be executed.
|
||||||
|
|
||||||
|
Note that this implies that when given a parameter of 0 or 1, the
|
||||||
|
stack effect of 'fib' is ( X -- X ).
|
||||||
|
|
||||||
|
==== What is the stack effect?
|
||||||
|
|
||||||
|
To see how to deduce the stack effect of the recursive case, it is
|
||||||
|
necessary to make a mental leap. Consider the case where the parameter
|
||||||
|
to fib is 2. The word recurses twice, and in each case, the parameter
|
||||||
|
to the recursive call is <= 1, so 'drop 1' is executed.
|
||||||
|
|
||||||
|
So when the parameter is 2, the stack effect is also ( X -- X )!
|
||||||
|
|
||||||
|
In fact it is not hard to usee that if the stack effect of 'fib' with
|
||||||
|
parameter n-1 and n-2 is ( X -- X ), then the stack effect of 'fib' with
|
||||||
|
parameter n is also ( X -- X ).
|
||||||
|
|
||||||
|
Therefore by induction, for any input, 'fib' has stack effect
|
||||||
|
( X -- X ).
|
||||||
|
|
||||||
|
Once the stack effect is known, it is easy enough to compile; just treat
|
||||||
|
the two recursive calls like calls to any other word with stack effect
|
||||||
|
( X -- X ).
|
||||||
|
|
||||||
|
==== Not all recursive forms have a deducable stack effect
|
||||||
|
|
||||||
|
Consider the following word:
|
||||||
|
|
||||||
|
: push ( list -- ... )
|
||||||
|
dup [
|
||||||
|
uncons push
|
||||||
|
] unless ;
|
||||||
|
|
||||||
|
If the top of the stack is null, the word returns. So the base case is (
|
||||||
|
X -- X ).
|
||||||
|
|
||||||
|
However if the top of the stack is a list of one element, the word has
|
||||||
|
stack effect ( X -- X X ), since 'uncons' has stack effect ( X -- X X )
|
||||||
|
and the base case is ( X -- X ).
|
||||||
|
|
||||||
|
If we proceed, we find that if the top of the stack is a list of two
|
||||||
|
elements, the stack effect of the word is ( X -- X X X ).
|
||||||
|
|
||||||
|
The stack positions used for intermediate values can no longer be
|
||||||
|
determined ahead of time.
|
||||||
|
|
||||||
|
A word whose stack effect depends on input is said to 'diverge'. Since
|
||||||
|
it is generally good practice to only write converging recursive words,
|
||||||
|
it is not a big loss that the compiler does not support them. Of course,
|
||||||
|
such words still work in the interpreter.
|
||||||
|
|
||||||
|
==== Auxiliary methods
|
||||||
|
|
||||||
|
So far, we can compile recursive words such as 'fib' and tail-recursive
|
||||||
|
words such as 'list?'. Now, lets try applying our techniques to a word
|
||||||
|
that calls a recursive combinator:
|
||||||
|
|
||||||
|
: reverse ( list -- list )
|
||||||
|
[ ] swap [ swons ] each ;
|
||||||
|
|
||||||
|
Recall that 'swons' creates a cons cell with stack effect
|
||||||
|
( cdr car -- [ car , cdr ] ) -- the opposite order of 'cons', which has stack effect ( car cdr -- [ car , cdr ] ).
|
||||||
|
|
||||||
|
The combinator 'each' is defined as follows:
|
||||||
|
|
||||||
|
: each ( [ list ] [ quotation ] -- )
|
||||||
|
over [
|
||||||
|
>r uncons r> tuck 2>r call 2r> each
|
||||||
|
] [
|
||||||
|
2drop
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
If we apply our previous inling technique, however, the end result is
|
||||||
|
absurd, since the recursive call to 'each' remains:
|
||||||
|
|
||||||
|
: reverse ( list -- list )
|
||||||
|
f swap [ swons ] over [
|
||||||
|
>r uncons r> tuck 2>r call 2r> each
|
||||||
|
] [
|
||||||
|
2drop
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
However, if the recursive call is changed to 'reverse', then the result
|
||||||
|
is also incorrect, since '[ ] swap' would be executed on each iteration.
|
||||||
|
|
||||||
|
The solution is to place instances of recursive combinators in an
|
||||||
|
'auxiliary method' in the same class as the definition being compiled.
|
||||||
|
|
||||||
|
So in fact, 'reverse' is compiled as three methods, eval(), core(), and
|
||||||
|
aux_each_0().
|
||||||
|
|
||||||
|
==== Wrapping up
|
||||||
|
|
||||||
|
There are two implementation details not covered here; they are not
|
||||||
|
really 'interesting' and best described by the source code anyway:
|
||||||
|
|
||||||
|
- tail-recursive words are compiled with a GOTO not a method invocation
|
||||||
|
at the end of the recursive case.
|
||||||
|
|
||||||
|
- some extra steps are needed to normalize the stack after recursive
|
||||||
|
calls, and when auxiliary methods are being generated.
|
||||||
|
|
||||||
|
=== Conclusion
|
||||||
|
|
||||||
|
Finally, lets see what kind of improvement we get over naive
|
||||||
|
interpretation when our old friend the 'fib' word is compiled using all
|
||||||
|
the techniques mentioned above:
|
||||||
|
|
||||||
|
3] "fib" compile
|
||||||
|
4] [ 25 fib ] time
|
||||||
|
123
|
||||||
|
|
||||||
|
That's right -- a 200x improvement over pure interpretation.
|
After Width: | Height: | Size: 333 B |
After Width: | Height: | Size: 165 B |
After Width: | Height: | Size: 174 B |
After Width: | Height: | Size: 288 B |
After Width: | Height: | Size: 288 B |
After Width: | Height: | Size: 171 B |
After Width: | Height: | Size: 155 B |
After Width: | Height: | Size: 278 B |
After Width: | Height: | Size: 147 B |
After Width: | Height: | Size: 190 B |
After Width: | Height: | Size: 333 B |
After Width: | Height: | Size: 244 B |
After Width: | Height: | Size: 246 B |
After Width: | Height: | Size: 245 B |
After Width: | Height: | Size: 272 B |
After Width: | Height: | Size: 314 B |
After Width: | Height: | Size: 386 B |
After Width: | Height: | Size: 333 B |
After Width: | Height: | Size: 332 B |
After Width: | Height: | Size: 279 B |
After Width: | Height: | Size: 327 B |
After Width: | Height: | Size: 332 B |
After Width: | Height: | Size: 352 B |
After Width: | Height: | Size: 430 B |
After Width: | Height: | Size: 332 B |
After Width: | Height: | Size: 211 B |
After Width: | Height: | Size: 231 B |
After Width: | Height: | Size: 229 B |
After Width: | Height: | Size: 333 B |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,173 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head><title>Factor plugin</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.fancy-heading { background: #c0d0ff; border: 1px solid #2040ff; }
|
||||||
|
div.fancy-heading { padding: 1px; margin: 0px 0px 3px 0px; }
|
||||||
|
.nice-box {
|
||||||
|
padding: 4px;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
.syntax0 {
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
.syntax1 {
|
||||||
|
color: #cc0000;
|
||||||
|
}
|
||||||
|
.syntax2 {
|
||||||
|
color: #ff8400;
|
||||||
|
}
|
||||||
|
.syntax3 {
|
||||||
|
color: #6600cc;
|
||||||
|
}
|
||||||
|
.syntax4 {
|
||||||
|
color: #cc6600;
|
||||||
|
}
|
||||||
|
.syntax5 {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
.syntax6 {
|
||||||
|
color: #9966ff;
|
||||||
|
}
|
||||||
|
.syntax7 {
|
||||||
|
background: #ffffcc;
|
||||||
|
color: #ff0066;
|
||||||
|
}
|
||||||
|
.syntax8 {
|
||||||
|
color: #006699;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.syntax9 {
|
||||||
|
color: #009966;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.syntax10 {
|
||||||
|
color: #0099ff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.syntax11 {
|
||||||
|
color: #66ccff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.syntax12 {
|
||||||
|
color: #02b902;
|
||||||
|
}
|
||||||
|
.syntax13 {
|
||||||
|
color: #ff00cc;
|
||||||
|
}
|
||||||
|
.syntax14 {
|
||||||
|
color: #cc00cc;
|
||||||
|
}
|
||||||
|
.syntax15 {
|
||||||
|
color: #9900cc;
|
||||||
|
}
|
||||||
|
.syntax16 {
|
||||||
|
color: #6600cc;
|
||||||
|
}
|
||||||
|
.syntax17 {
|
||||||
|
color: #0000ff;
|
||||||
|
}
|
||||||
|
.syntax18 {
|
||||||
|
color: #000000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.gutter {
|
||||||
|
background: #dbdbdb;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
.gutterH {
|
||||||
|
background: #dbdbdb;
|
||||||
|
color: #666699;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
|
||||||
|
<h1 align="center">Factor plugin</h1>
|
||||||
|
|
||||||
|
<p><a href="http://factor.sourceforge.net">Factor</a> is a programming language with postfix syntax. The Factor plugin for <a href="http://www.jedit.org">jEdit</a> provides many nifty time-saving features for working with Factor code.<p>
|
||||||
|
|
||||||
|
<h2 class="fancy-heading">Introduction</h2>
|
||||||
|
|
||||||
|
If Factor is compiled with the jEdit classes in the class path, the resulting <code>Factor.jar</code> can then be placed in <code>$HOME/.jedit/jars/</code> and loaded by jEdit.<p>
|
||||||
|
|
||||||
|
Note that while the Factor plugin requires jEdit 4.2pre15, you will need to download and install the Factor edit mode separately from the <a href="http://factor.sf.net">Factor home page</a>. If you are running jEdit 4.2final, this edit mode is already included.
|
||||||
|
|
||||||
|
<h2 class="fancy-heading">Embedded interpreter</h2>
|
||||||
|
|
||||||
|
<p>The plugin embeds a Factor interpreter inside jEdit that gets lazily loaded when first used. The interpreter communicates with the plugin and vice versa to perform various useful tasks.</p>
|
||||||
|
|
||||||
|
<p>The <b>Run current file</b> and <b>Evaluate selection</b> commands can be used to send text from jEdit to the interpreter.<p>
|
||||||
|
|
||||||
|
<img class="nice-box" src="listener.png">
|
||||||
|
|
||||||
|
<p>If you have CFactor installed, it is possible to embed it in jEdit using the <code>inferior.factor</code> socket protocol. Add the following to your <code>$HOME/.factor-rc</code>:
|
||||||
|
|
||||||
|
<PRE class="nice-box"><SPAN CLASS="syntax17">USE:</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">telnetd</SPAN>
|
||||||
|
<SPAN CLASS="syntax17">:</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">inf</SPAN> <SPAN CLASS="syntax5">9999</SPAN> telnetd <SPAN CLASS="syntax17">;</SPAN>
|
||||||
|
<SPAN CLASS="syntax17">:</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">cfactor</SPAN> <SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">localhost</SPAN><SPAN CLASS="syntax13">"</SPAN> <SPAN CLASS="syntax5">9999</SPAN> <client> inferior-client <SPAN CLASS="syntax17">;</SPAN>
|
||||||
|
</PRE>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>Now, start CFactor and type the following phrase:</p>
|
||||||
|
|
||||||
|
<pre class="nice-box">inf</pre>
|
||||||
|
|
||||||
|
<p>Then open the Factor listener window in jEdit, and type the following phrase:</p>
|
||||||
|
|
||||||
|
<pre class="nice-box">cfactor</pre>
|
||||||
|
|
||||||
|
<p>You will now be talking to the CFactor interpreter prompt. Styled text output and hyperlinks will be transmitted using the <code>inferior.factor</code> socket protocol.</p>
|
||||||
|
|
||||||
|
<h2 class="fancy-heading">Cross-referencing</h2>
|
||||||
|
|
||||||
|
<p>The <b>Edit word at caret</b> command opens the source file containing the definition of the word at the caret. <b>See word at caret</b> shows the definition of the word at the caret in the Factor listener window.</p>
|
||||||
|
|
||||||
|
<p>The <b>Edit word</b> command opens a dialog box where the name of a word can be typed -- while the word is being typed, the possible completions is instantly updated, and selecting one opens the source file containing the definition of that word:</p>
|
||||||
|
|
||||||
|
<img class="nice-box" src="edit-word.png">
|
||||||
|
|
||||||
|
<p>To be able to edit definitions of standard library words, add a phrase like the following to your <code>$HOME/.factor-rc</code>:</p>
|
||||||
|
|
||||||
|
<pre class="nice-box"><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">/home/slava/Factor/</SPAN><SPAN CLASS="syntax13">"</SPAN> <SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">resource-path</SPAN><SPAN CLASS="syntax13">"</SPAN> set
|
||||||
|
</PRE>
|
||||||
|
|
||||||
|
<p>The <b>Word usages at caret</b> command displays a list of words that refer to the word at the caret in the Factor listener window. Clicking on words in the listener shows a popup menu with various useful actions.</p>
|
||||||
|
|
||||||
|
<img class="nice-box" src="usages.png">
|
||||||
|
|
||||||
|
<p>Here we see the result of selecting <b>Describe</b>.</p>
|
||||||
|
|
||||||
|
<img class="nice-box" src="describe.png">
|
||||||
|
|
||||||
|
<h2 class="fancy-heading">Error checking</h2>
|
||||||
|
|
||||||
|
<p>Factor files are parsed in a background thread and checked for errors, using the framework provided by the SideKick plugin. Errors are underlined in the text area (and listed in the <b>ErrorList</b> plugin window).</p>
|
||||||
|
|
||||||
|
<img class="nice-box" src="error.png">
|
||||||
|
|
||||||
|
<p>A common error is a missing <code>USE:</code> declaration. The <b>Use word at caret</b> command searches for the word at the caret in all vocabularies, and adds a <code>USE:</code> declaration for the vocabulary to the start of the source file -- in this case, <code>ifte</code> is found in the <code>combinators</code> vocabulary, and the parse error instantly goes away:</p>
|
||||||
|
|
||||||
|
<img class="nice-box" src="word-use.png">
|
||||||
|
|
||||||
|
<h2 class="fancy-heading">Completion and browsing</h2>
|
||||||
|
|
||||||
|
<p>The stack effect of the word at the caret is shown in the status bar.</p>
|
||||||
|
|
||||||
|
<img class="nice-box" src="status.png">
|
||||||
|
|
||||||
|
<p>Invoking <b>Plugins</b>><b>SideKick</b>><b>Show Completion Popup</b> displays a popup of possible completions for the word at the caret -- bind this to <code>C+SPACE</code> for quick access:</p>
|
||||||
|
|
||||||
|
<img class="nice-box" src="complete.png">
|
||||||
|
|
||||||
|
<p>The <b>Plugins</b>><b>SideKick</b>><b>Structure Browser</b> displays a list of all words defined in the current buffer:</p>
|
||||||
|
|
||||||
|
<img class="nice-box" src="word-list.png">
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 5.7 KiB |
|
@ -0,0 +1,307 @@
|
||||||
|
FACTOR MATH WORDS
|
||||||
|
|
||||||
|
=== Basics
|
||||||
|
|
||||||
|
The following expressions demonstrate basic arithmetic in Factor.
|
||||||
|
|
||||||
|
0] 2 2 + .
|
||||||
|
4
|
||||||
|
1] 10 4.5 - .
|
||||||
|
5.5
|
||||||
|
2] 12.5 3 * .
|
||||||
|
37.5
|
||||||
|
3] 6 20 / .
|
||||||
|
3/10
|
||||||
|
4] 6 20 /f .
|
||||||
|
0.3
|
||||||
|
5] 0.354 neg .
|
||||||
|
-0.354
|
||||||
|
6] 5 recip .
|
||||||
|
0.2
|
||||||
|
|
||||||
|
Arithmetic operators appear after their oprands, and intermediate values
|
||||||
|
are stored on a stack -- this is called postfix syntax.
|
||||||
|
|
||||||
|
The word . prints the value at the top of the stack.
|
||||||
|
|
||||||
|
There are no operator precedence levels, and expressions can always be
|
||||||
|
reprepsented unambiguously without parantheses, unlike traditional
|
||||||
|
algebraic syntax.
|
||||||
|
|
||||||
|
For example, (3 + 2) * (1 - 6) is written as:
|
||||||
|
|
||||||
|
3 2 + 1 6 - *
|
||||||
|
|
||||||
|
However, 3 + (2 * 1) - 6 is written as:
|
||||||
|
|
||||||
|
3 2 1 * + 6 -
|
||||||
|
|
||||||
|
=== The number tower
|
||||||
|
|
||||||
|
Factor supports operations with many types of numbers, transparently
|
||||||
|
converting results from one type to another. The following informal
|
||||||
|
diagram can be helpful in understanding the various types of numbers.
|
||||||
|
|
||||||
|
+--------+ +--------+
|
||||||
|
|=fixnum=| |=bignum=|
|
||||||
|
+--------+ +--------+
|
||||||
|
+-------------------+ +-------+
|
||||||
|
| integer | |=ratio=|
|
||||||
|
+-------------------+ +-------+
|
||||||
|
+-----------------------------+ +-------+
|
||||||
|
| rational | |=float=|
|
||||||
|
+-----------------------------+ +-------+
|
||||||
|
+---------------------------------------+ +---------+
|
||||||
|
| real | |=complex=|
|
||||||
|
+---------------------------------------+ +---------+
|
||||||
|
+---------------------------------------------------+
|
||||||
|
| number |
|
||||||
|
+---------------------------------------------------+
|
||||||
|
|
||||||
|
Types on the same row are disjoint.
|
||||||
|
|
||||||
|
Each type is a subtype of all types directly below.
|
||||||
|
|
||||||
|
Types whose boxes are marked with '=' are disjoint concrete types.
|
||||||
|
|
||||||
|
Type upgrades are performed through the concrete types, from the top
|
||||||
|
left down to the bottom right.
|
||||||
|
|
||||||
|
Ratios and complex numbers are compound types; ratios consist of a pair
|
||||||
|
of integers, complex numbers consist of a pair of real numbers.
|
||||||
|
|
||||||
|
=== Types of numbers
|
||||||
|
|
||||||
|
All number entry is in base 10.
|
||||||
|
|
||||||
|
The predicate word number? tests if the top of the stack is a number.
|
||||||
|
|
||||||
|
Numbers are partitioned into two disjoint subsets; real numbers and
|
||||||
|
complex numbers. (In math, the reals are a subset of the complex
|
||||||
|
numbers. In Factor, a number whose imaginary part is zero is *not* a
|
||||||
|
complex number).
|
||||||
|
|
||||||
|
Real numbers are partitioned into three disjoint subsets: integers,
|
||||||
|
ratios and floats.
|
||||||
|
|
||||||
|
==== Integers: 12 -100 340282366920938463463374607431768211456
|
||||||
|
|
||||||
|
The predicate word integer? tests if the top of the stack is an integer.
|
||||||
|
|
||||||
|
The integers are partitioned into two disjoint types:
|
||||||
|
|
||||||
|
- signed 32-bit fixnums (predicate: fixnum?)
|
||||||
|
- signed arbitrary precision bignums (predicate: bignum?)
|
||||||
|
|
||||||
|
Fixnums are automatically upgraded as necessary to bignums.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
8] 1073741824 fixnum? .
|
||||||
|
t
|
||||||
|
9] 128 fixnum? .
|
||||||
|
t
|
||||||
|
10] 1073741824 128 * .
|
||||||
|
137438953472
|
||||||
|
11] 1073741824 128 * bignum? .
|
||||||
|
t
|
||||||
|
|
||||||
|
In the above example, the result of multiply those two fixnums exceeds
|
||||||
|
2^31-1, and the result is upgraded to a bignum.
|
||||||
|
|
||||||
|
When given integer operands, + - and * always return integers.
|
||||||
|
|
||||||
|
==== Ratios: 1/10 -37/78 10/3
|
||||||
|
|
||||||
|
A ratio is the result of a division of two integers where the
|
||||||
|
denimonator is not a multiple of the numerator.
|
||||||
|
|
||||||
|
The predicate word ratio? tests if the top of the stack is a ratio.
|
||||||
|
|
||||||
|
Ratios are always reduced to lowest terms, and the denominator is always
|
||||||
|
positive. The numerator never equals zero since dividing zero by a
|
||||||
|
non-zero integer always results in the integer zero.
|
||||||
|
|
||||||
|
The accessor words numerator and denominator deconstruct a ratio. Given
|
||||||
|
an integer, numerator is a no-op and denominator always returns 1.
|
||||||
|
|
||||||
|
14] 100 -30 / numerator .
|
||||||
|
-10
|
||||||
|
15] 100 -30 / denominator .
|
||||||
|
3
|
||||||
|
16] 12 numerator .
|
||||||
|
12
|
||||||
|
|
||||||
|
The numerator and denominator are integers, and hence either fixnums or
|
||||||
|
bignums (there is no requirement for them to be of the same type).
|
||||||
|
|
||||||
|
The result of dividing two integers as a floating point number can be
|
||||||
|
obtained using the word /f. For example:
|
||||||
|
|
||||||
|
17] 1 3 /f .
|
||||||
|
0.3333333333333333
|
||||||
|
|
||||||
|
When arithmetic operators are given a ratio and an integer as
|
||||||
|
parameters, the result is also a ratio or an integer.
|
||||||
|
|
||||||
|
==== Floats: -1.3 1.5e-6 0.003
|
||||||
|
|
||||||
|
Floats are entered as double-precision. Single-precision floats can be
|
||||||
|
constructed via coercion. They are converted to double-precision by
|
||||||
|
arithmetic operands.
|
||||||
|
|
||||||
|
The predicate word float? tests if the top of the stack is a single or
|
||||||
|
double precision float.
|
||||||
|
|
||||||
|
When at least one of the parameters to an arithmetic operator is a
|
||||||
|
float, the result is always a (double precision) float.
|
||||||
|
|
||||||
|
==== Complex numbers: #{ 2 2.5 } #{ 1/2 1/3 }
|
||||||
|
|
||||||
|
A complex number has a real and imaginary part. The syntax is to write
|
||||||
|
#{ followed by the real part, followed by the imaginary part, and
|
||||||
|
finally terminated with }. Each token must be separted with whitespace.
|
||||||
|
|
||||||
|
The predicate word complex? tests if the top of the stack is a single or
|
||||||
|
double precision float.
|
||||||
|
|
||||||
|
For example, what is commonly written as 2-3.5i in textbooks is
|
||||||
|
expressed as #{ 2 2.5 } in Factor.
|
||||||
|
|
||||||
|
The real and imaginary parts can be either integers, ratios or floats.
|
||||||
|
There is no requirement for them to be of the same type.
|
||||||
|
|
||||||
|
The accessor words real and imaginary deconstruct a complex number. The
|
||||||
|
real part followed by the imaginary part can both be pushed at once
|
||||||
|
using the word >rect, and a new complex number can be constructed from a
|
||||||
|
real and imaginary part using the word rect>.
|
||||||
|
|
||||||
|
4] -i sqrt >rect .s
|
||||||
|
-0.7071067811865475
|
||||||
|
0.7071067811865476
|
||||||
|
5] 1 2 rect> .
|
||||||
|
#{ 1 2 }
|
||||||
|
|
||||||
|
A complex number with an imaginary component of zero is automatically
|
||||||
|
downgraded to an integer, a ratio or a float (depending on the type of
|
||||||
|
its real component.)
|
||||||
|
|
||||||
|
6] 10 0 rect> .
|
||||||
|
10
|
||||||
|
7] #{ 5 -10 } #{ 2 10 } + .
|
||||||
|
7
|
||||||
|
|
||||||
|
Complex numbers never arise as results of arithmetic operators with real
|
||||||
|
operands. However, various irrational functions return complex values
|
||||||
|
for some real inputs.
|
||||||
|
|
||||||
|
=== Mathematical functions
|
||||||
|
|
||||||
|
==== Square root, squaring, arbitrary powers
|
||||||
|
|
||||||
|
These are pretty much self-explanatory.
|
||||||
|
|
||||||
|
10] 36 sq sqrt .
|
||||||
|
36.0
|
||||||
|
11] -2 sqrt sq .
|
||||||
|
-2.0000000000000004
|
||||||
|
12] 10 15 ^ .
|
||||||
|
1.0E15
|
||||||
|
13] e pi i * ^ .
|
||||||
|
#{ -1.0 1.2246467991473532E-16 }
|
||||||
|
|
||||||
|
==== Exponential, logarithm
|
||||||
|
|
||||||
|
The function e^x and its inverse.
|
||||||
|
|
||||||
|
15] e .
|
||||||
|
2.718281828459045
|
||||||
|
16] 2 exp .
|
||||||
|
7.38905609893065
|
||||||
|
17] 5 log 2 log - exp .
|
||||||
|
2.5
|
||||||
|
|
||||||
|
Note that the complex logarithm is infinitely-valued. The principle
|
||||||
|
value is chosen such that the complex part is in the interval (-pi,pi].
|
||||||
|
|
||||||
|
18] -10 log .
|
||||||
|
#{ 2.302585092994046 3.141592653589793 }
|
||||||
|
|
||||||
|
==== Trigonometric and hyperbolic functions
|
||||||
|
|
||||||
|
The full complement of trigonometric and hyperbolic functions and their
|
||||||
|
inverses is provided:
|
||||||
|
|
||||||
|
sin cos tan
|
||||||
|
asin acos atan
|
||||||
|
cosec sec cot
|
||||||
|
acosec asec acot
|
||||||
|
|
||||||
|
sinh cosh tanh
|
||||||
|
asinh acosh atanh
|
||||||
|
cosech sech coth
|
||||||
|
acosech asech acoth
|
||||||
|
|
||||||
|
Complex arguments are supported. The specific branch cuts used by the
|
||||||
|
inverse functions are undocumented by can be deduced from the
|
||||||
|
definitions of those functions, and the branch cuts taken by 'log' and
|
||||||
|
'sqrt'.
|
||||||
|
|
||||||
|
==== Polar co-ordinates
|
||||||
|
|
||||||
|
Complex numbers can be converted to/from polar co-ordinate
|
||||||
|
representations using the words >polar and polar>.
|
||||||
|
|
||||||
|
41] #{ 1 1 } >polar .s
|
||||||
|
0.7853981633974483
|
||||||
|
1.4142135623730951
|
||||||
|
42] -5 pi 3 / polar> .
|
||||||
|
#{ -2.5000000000000004 -4.330127018922193 }
|
||||||
|
|
||||||
|
==== Miscellaneous integer functions
|
||||||
|
|
||||||
|
Factorial, fibonacci sequence, harmonic numbers.
|
||||||
|
|
||||||
|
33] 128 2^ .
|
||||||
|
340282366920938463463374607431768211456
|
||||||
|
34] 30 fib .
|
||||||
|
1346269
|
||||||
|
35] 100 harmonic >float 100 log - .
|
||||||
|
0.5822073316515288
|
||||||
|
36] 1000 fac .
|
||||||
|
402387260077093773543702433923003985719374864210714632543799910429938512
|
||||||
|
398629020592044208486969404800479988610197196058631666872994808558901323
|
||||||
|
829669944590997424504087073759918823627727188732519779505950995276120874
|
||||||
|
975462497043601418278094646496291056393887437886487337119181045825783647
|
||||||
|
849977012476632889835955735432513185323958463075557409114262417474349347
|
||||||
|
553428646576611667797396668820291207379143853719588249808126867838374559
|
||||||
|
731746136085379534524221586593201928090878297308431392844403281231558611
|
||||||
|
036976801357304216168747609675871348312025478589320767169132448426236131
|
||||||
|
412508780208000261683151027341827977704784635868170164365024153691398281
|
||||||
|
264810213092761244896359928705114964975419909342221566832572080821333186
|
||||||
|
116811553615836546984046708975602900950537616475847728421889679646244945
|
||||||
|
160765353408198901385442487984959953319101723355556602139450399736280750
|
||||||
|
137837615307127761926849034352625200015888535147331611702103968175921510
|
||||||
|
907788019393178114194545257223865541461062892187960223838971476088506276
|
||||||
|
862967146674697562911234082439208160153780889893964518263243671616762179
|
||||||
|
168909779911903754031274622289988005195444414282012187361745992642956581
|
||||||
|
746628302955570299024324153181617210465832036786906117260158783520751516
|
||||||
|
284225540265170483304226143974286933061690897968482590125458327168226458
|
||||||
|
066526769958652682272807075781391858178889652208164348344825993266043367
|
||||||
|
660176999612831860788386150279465955131156552036093988180612138558600301
|
||||||
|
435694527224206344631797460594682573103790084024432438465657245014402821
|
||||||
|
885252470935190620929023136493273497565513958720559654228749774011413346
|
||||||
|
962715422845862377387538230483865688976461927383814900140767310446640259
|
||||||
|
899490222221765904339901886018566526485061799702356193897017860040811889
|
||||||
|
729918311021171229845901641921068884387121855646124960798722908519296819
|
||||||
|
372388642614839657382291123125024186649353143970137428531926649875337218
|
||||||
|
940694281434118520158014123344828015051399694290153483077644569099073152
|
||||||
|
433278288269864602789864321139083506217095002597389863554277196742822248
|
||||||
|
757586765752344220207573630569498825087968928162753848863396909959826280
|
||||||
|
956121450994871701244516461260379029309120889086942028510640182154399457
|
||||||
|
156805941872748998094254742173582401063677404595741785160829230135358081
|
||||||
|
840096996372524230560855903700624271243416909004153690105933983835777939
|
||||||
|
410970027753472000000000000000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000000
|
|
@ -0,0 +1,78 @@
|
||||||
|
FACTOR CODING CONVENTIONS.
|
||||||
|
|
||||||
|
=== Naming words
|
||||||
|
|
||||||
|
foo. - perform "foo", but instead of pushing the result on the
|
||||||
|
stack, print it in a human-readable form suitable for
|
||||||
|
interactive use.
|
||||||
|
|
||||||
|
Eg: words. vocabs.
|
||||||
|
|
||||||
|
.X - four words to print the contents of the stacks:
|
||||||
|
.s - data stack
|
||||||
|
.r - call stack
|
||||||
|
.n - name stack
|
||||||
|
.c - catch stack
|
||||||
|
|
||||||
|
foo* - a variation of "foo" that takes more parameters.
|
||||||
|
|
||||||
|
Eg: index-of* parse* random-element*
|
||||||
|
|
||||||
|
- a lower-level word used in the implementation of "foo".
|
||||||
|
|
||||||
|
Eg: compile* prettyprint*
|
||||||
|
|
||||||
|
- a word that is a variation on "foo", but is more specialized
|
||||||
|
and less frequently used.
|
||||||
|
|
||||||
|
Eg: last* get*
|
||||||
|
|
||||||
|
(foo) - a word that is only useful in the implementation of "foo".
|
||||||
|
|
||||||
|
Eg: (vector=) (split)
|
||||||
|
|
||||||
|
>to - convert object to type "to".
|
||||||
|
|
||||||
|
Eg: >str >lower >upper >fixnum >realnum
|
||||||
|
|
||||||
|
- move top of data stack "to" stack.
|
||||||
|
|
||||||
|
Eg: >r >n >c
|
||||||
|
|
||||||
|
from> - convert object from type "from".
|
||||||
|
|
||||||
|
Eg: dec> oct> hex>
|
||||||
|
|
||||||
|
- move top of "from" stack to data stack.
|
||||||
|
|
||||||
|
Eg: r> n> c>
|
||||||
|
|
||||||
|
one>two - convert object of type "one" to "two".
|
||||||
|
|
||||||
|
Eg: stream>str stack>list worddef>list
|
||||||
|
|
||||||
|
- transfer values between stacks.
|
||||||
|
|
||||||
|
Eg: >r r> 2>r 2r> >n
|
||||||
|
|
||||||
|
<type> - create an object of "type".
|
||||||
|
|
||||||
|
Eg: <namespace> <sbuf> <stream>
|
||||||
|
|
||||||
|
foo@ - get the value of a variable at the top of the stack;
|
||||||
|
operate on the value with "foo"; store the value back in the
|
||||||
|
variable.
|
||||||
|
|
||||||
|
Eg: +@ *@ -@ /@ cons@ append@
|
||||||
|
|
||||||
|
foo-iter - a tail-recursive word used in the implementatin of "foo".
|
||||||
|
|
||||||
|
Eg: nreverse-iter partition-iter
|
||||||
|
|
||||||
|
nfoo - on lists, a destructive (non-consing) version of "foo".
|
||||||
|
|
||||||
|
Eg: nappend nreverse
|
||||||
|
|
||||||
|
2foo - like foo but with two operands taken from stack.
|
||||||
|
|
||||||
|
Eg: 2drop 2dup 2each
|
|
@ -0,0 +1,40 @@
|
||||||
|
! Numbers game example
|
||||||
|
|
||||||
|
IN: numbers-game
|
||||||
|
USE: combinators
|
||||||
|
USE: kernel
|
||||||
|
USE: math
|
||||||
|
USE: parser
|
||||||
|
USE: random
|
||||||
|
USE: stdio
|
||||||
|
USE: stack
|
||||||
|
|
||||||
|
: read-number ( -- n ) read parse-number ;
|
||||||
|
|
||||||
|
: guess-banner
|
||||||
|
"I'm thinking of a number between 0 and 100." print ;
|
||||||
|
: guess-prompt "Enter your guess: " write ;
|
||||||
|
: too-high "Too high" print ;
|
||||||
|
: too-low "Too low" print ;
|
||||||
|
: correct "Correct - you win!" print ;
|
||||||
|
|
||||||
|
: inexact-guess ( actual guess -- )
|
||||||
|
< [ too-high ] [ too-low ] ifte ;
|
||||||
|
|
||||||
|
: judge-guess ( actual guess -- ? )
|
||||||
|
2dup = [
|
||||||
|
2drop correct f
|
||||||
|
] [
|
||||||
|
inexact-guess t
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: number-to-guess ( -- n ) 0 100 random-int ;
|
||||||
|
|
||||||
|
: numbers-game-loop ( actual -- )
|
||||||
|
dup guess-prompt read-number judge-guess [
|
||||||
|
numbers-game-loop
|
||||||
|
] [
|
||||||
|
drop
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: numbers-game number-to-guess numbers-game-loop ;
|
|
@ -0,0 +1,74 @@
|
||||||
|
! Contractor timesheet example
|
||||||
|
|
||||||
|
IN: timesheet
|
||||||
|
USE: combinators
|
||||||
|
USE: errors
|
||||||
|
USE: format
|
||||||
|
USE: kernel
|
||||||
|
USE: lists
|
||||||
|
USE: math
|
||||||
|
USE: parser
|
||||||
|
USE: stack
|
||||||
|
USE: stdio
|
||||||
|
USE: strings
|
||||||
|
USE: unparser
|
||||||
|
USE: vectors
|
||||||
|
|
||||||
|
! Adding a new entry to the time sheet.
|
||||||
|
|
||||||
|
: measure-duration ( -- duration )
|
||||||
|
millis
|
||||||
|
read drop
|
||||||
|
millis swap - 1000 /i 60 /i ;
|
||||||
|
|
||||||
|
: add-entry-prompt ( -- duration description )
|
||||||
|
"Start work on the task now. Press ENTER when done." print
|
||||||
|
measure-duration
|
||||||
|
"Please enter a description:" print
|
||||||
|
read ;
|
||||||
|
|
||||||
|
: add-entry ( timesheet -- )
|
||||||
|
add-entry-prompt cons swap vector-push ;
|
||||||
|
|
||||||
|
! Printing the timesheet.
|
||||||
|
|
||||||
|
: hh ( duration -- str ) 60 /i ;
|
||||||
|
: mm ( duration -- str ) 60 mod unparse 2 digits ;
|
||||||
|
: hh:mm ( millis -- str ) <% dup hh % ":" % mm % %> ;
|
||||||
|
|
||||||
|
: print-entry ( duration description -- )
|
||||||
|
dup write
|
||||||
|
60 swap pad-string write
|
||||||
|
hh:mm print ;
|
||||||
|
|
||||||
|
: print-timesheet ( timesheet -- )
|
||||||
|
"TIMESHEET:" print
|
||||||
|
[ uncons print-entry ] vector-each ;
|
||||||
|
|
||||||
|
! Displaying a menu
|
||||||
|
|
||||||
|
: print-menu ( menu -- )
|
||||||
|
terpri [ cdr car print ] each terpri
|
||||||
|
"Enter a letter between ( ) to execute that action." print ;
|
||||||
|
|
||||||
|
: menu-prompt ( menu -- )
|
||||||
|
read swap assoc dup [
|
||||||
|
cdr call
|
||||||
|
] [
|
||||||
|
"Invalid input: " swap unparse cat2 throw
|
||||||
|
] ifte ;
|
||||||
|
|
||||||
|
: menu ( menu -- )
|
||||||
|
dup print-menu menu-prompt ;
|
||||||
|
|
||||||
|
! Main menu
|
||||||
|
|
||||||
|
: main-menu ( timesheet -- )
|
||||||
|
[
|
||||||
|
[ "e" "(E)xit" drop ]
|
||||||
|
[ "a" "(A)dd entry" dup add-entry main-menu ]
|
||||||
|
[ "p" "(P)rint timesheet" dup print-timesheet main-menu ]
|
||||||
|
] menu ;
|
||||||
|
|
||||||
|
: timesheet-app ( -- )
|
||||||
|
10 <vector> main-menu ;
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<!-- For jEdit plugin -->
|
||||||
|
|
||||||
|
<!DOCTYPE DOCKABLES SYSTEM "dockables.dtd">
|
||||||
|
|
||||||
|
<DOCKABLES>
|
||||||
|
<DOCKABLE NAME="factor">
|
||||||
|
new factor.listener.FactorListenerPanel(
|
||||||
|
factor.jedit.FactorPlugin.getInterpreter());
|
||||||
|
</DOCKABLE>
|
||||||
|
</DOCKABLES>
|
|
@ -198,8 +198,43 @@ public class FactorArray implements FactorExternalizable, PublicCloneable
|
||||||
return new FactorArray();
|
return new FactorArray();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new FactorArray(
|
Object[] newArray = new Object[stack.length];
|
||||||
FactorLib.cloneArray(stack),top);
|
System.arraycopy(stack,0,newArray,0,top);
|
||||||
|
return new FactorArray(newArray,top);
|
||||||
}
|
}
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ hashCode() method
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
int hashCode = 0;
|
||||||
|
for(int i = 0; i < Math.min(top,4); i++)
|
||||||
|
{
|
||||||
|
Object obj = stack[i];
|
||||||
|
if(obj != null)
|
||||||
|
hashCode ^= obj.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashCode;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ equals() method
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if(obj instanceof FactorArray)
|
||||||
|
{
|
||||||
|
FactorArray a = (FactorArray)obj;
|
||||||
|
if(a.top != top)
|
||||||
|
return false;
|
||||||
|
for(int i = 0; i < top; i++)
|
||||||
|
{
|
||||||
|
if(!FactorLib.equal(stack[i],a.stack[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} //}}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,8 @@ public class FactorCompoundDefinition extends FactorWordDefinition
|
||||||
RecursiveState recursiveCheck) throws Exception
|
RecursiveState recursiveCheck) throws Exception
|
||||||
{
|
{
|
||||||
// Each word has its own class loader
|
// Each word has its own class loader
|
||||||
FactorClassLoader loader = new FactorClassLoader();
|
FactorClassLoader loader = new FactorClassLoader(
|
||||||
|
getClass().getClassLoader());
|
||||||
|
|
||||||
StackEffect effect = getStackEffect(interp);
|
StackEffect effect = getStackEffect(interp);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,347 @@
|
||||||
|
/* :folding=explicit:collapseFolds=1: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2003, 2004 Slava Pestov.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package factor;
|
||||||
|
|
||||||
|
import factor.compiler.*;
|
||||||
|
import factor.db.Workspace;
|
||||||
|
import factor.db.PersistenceException;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.*;
|
||||||
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* : name ... ;
|
||||||
|
*/
|
||||||
|
public class FactorCompoundDefinition extends FactorWordDefinition
|
||||||
|
{
|
||||||
|
private static int compileCount;
|
||||||
|
|
||||||
|
public Cons definition;
|
||||||
|
private Cons endOfDocs;
|
||||||
|
|
||||||
|
//{{{ FactorCompoundDefinition constructor
|
||||||
|
/**
|
||||||
|
* A new definition.
|
||||||
|
*/
|
||||||
|
public FactorCompoundDefinition(FactorWord word, Cons definition,
|
||||||
|
FactorInterpreter interp) throws PersistenceException
|
||||||
|
{
|
||||||
|
super(word,interp.workspace);
|
||||||
|
fromList(definition,interp);
|
||||||
|
|
||||||
|
if(interp.workspace != null)
|
||||||
|
interp.workspace.put(this);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ FactorCompoundDefinition constructor
|
||||||
|
/**
|
||||||
|
* A blank definition, about to be unpickled.
|
||||||
|
*/
|
||||||
|
public FactorCompoundDefinition(Workspace workspace, long id)
|
||||||
|
{
|
||||||
|
super(workspace,id);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ eval() method
|
||||||
|
public void eval(FactorInterpreter interp)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
interp.call(word,endOfDocs);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getClassName() method
|
||||||
|
private static String getClassName(String name)
|
||||||
|
{
|
||||||
|
return FactorJava.getSanitizedName(name)
|
||||||
|
+ "_" + (compileCount++);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compile() method
|
||||||
|
/**
|
||||||
|
* Compile the given word, returning a new word definition.
|
||||||
|
*/
|
||||||
|
FactorWordDefinition compile(FactorInterpreter interp,
|
||||||
|
RecursiveState recursiveCheck) throws Exception
|
||||||
|
{
|
||||||
|
// Each word has its own class loader
|
||||||
|
FactorClassLoader loader = new FactorClassLoader(
|
||||||
|
interp.workspace);
|
||||||
|
|
||||||
|
StackEffect effect = getStackEffect(interp,
|
||||||
|
new RecursiveState());
|
||||||
|
|
||||||
|
if(effect.inR != 0 || effect.outR != 0)
|
||||||
|
throw new FactorCompilerException("Compiled code cannot manipulate call stack frames");
|
||||||
|
|
||||||
|
String className = getClassName(word.name);
|
||||||
|
|
||||||
|
ClassWriter cw = new ClassWriter(true);
|
||||||
|
cw.visit(ACC_PUBLIC, className,
|
||||||
|
"factor/compiler/CompiledDefinition",
|
||||||
|
null, null);
|
||||||
|
|
||||||
|
compileConstructor(cw,className);
|
||||||
|
|
||||||
|
FactorCompiler compiler = compileEval(interp,cw,loader,
|
||||||
|
className,effect,recursiveCheck);
|
||||||
|
|
||||||
|
// Generate auxiliary methods
|
||||||
|
compiler.generateAuxiliary(cw);
|
||||||
|
|
||||||
|
// Generate fields for storing literals and
|
||||||
|
// word references
|
||||||
|
compiler.generateFields(cw);
|
||||||
|
|
||||||
|
compileToList(interp,compiler,cw);
|
||||||
|
|
||||||
|
compileGetStackEffect(cw,effect);
|
||||||
|
|
||||||
|
// gets the bytecode of the class, and loads it
|
||||||
|
// dynamically
|
||||||
|
byte[] code = cw.toByteArray();
|
||||||
|
|
||||||
|
if(interp.dump)
|
||||||
|
{
|
||||||
|
FileOutputStream fos = new FileOutputStream(
|
||||||
|
className + ".class");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fos.write(code);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String javaClassName = className.replace('/','.');
|
||||||
|
word.setCompiledInfo(compiler.loader,javaClassName);
|
||||||
|
|
||||||
|
Class compiledWordClass = loader.addClass(
|
||||||
|
javaClassName,code,0,code.length);
|
||||||
|
|
||||||
|
return CompiledDefinition.create(interp,word,compiledWordClass);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileConstructor() method
|
||||||
|
private void compileConstructor(ClassVisitor cw, String className)
|
||||||
|
{
|
||||||
|
// creates a MethodWriter for the constructor
|
||||||
|
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
|
||||||
|
"<init>",
|
||||||
|
"(Lfactor/FactorWord;)V",
|
||||||
|
null, null);
|
||||||
|
// pushes the 'this' variable
|
||||||
|
mw.visitVarInsn(ALOAD, 0);
|
||||||
|
// pushes the word parameter
|
||||||
|
mw.visitVarInsn(ALOAD, 1);
|
||||||
|
// invokes the super class constructor
|
||||||
|
mw.visitMethodInsn(INVOKESPECIAL,
|
||||||
|
"factor/compiler/CompiledDefinition", "<init>",
|
||||||
|
"(Lfactor/FactorWord;)V");
|
||||||
|
mw.visitInsn(RETURN);
|
||||||
|
mw.visitMaxs(0,0);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileToList() method
|
||||||
|
private void compileToList(FactorInterpreter interp,
|
||||||
|
FactorCompiler compiler, ClassVisitor cw)
|
||||||
|
{
|
||||||
|
// creates a MethodWriter for the toList() method
|
||||||
|
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
|
||||||
|
"toList",
|
||||||
|
"(Lfactor/FactorInterpreter;)Lfactor/Cons;",
|
||||||
|
null, null);
|
||||||
|
// push unparsed string representation of this word and parse it
|
||||||
|
compiler.generateParse(mw,toList(interp),1);
|
||||||
|
mw.visitTypeInsn(CHECKCAST,"factor/Cons");
|
||||||
|
mw.visitInsn(ARETURN);
|
||||||
|
mw.visitMaxs(0,0);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileGetStackEffect() method
|
||||||
|
private void compileGetStackEffect(ClassVisitor cw, StackEffect effect)
|
||||||
|
{
|
||||||
|
// creates a MethodWriter for the getStackEffect() method
|
||||||
|
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
|
||||||
|
"getStackEffect",
|
||||||
|
"(Lfactor/compiler/RecursiveState;"
|
||||||
|
+ "Lfactor/compiler/FactorCompiler;)V",
|
||||||
|
null, null);
|
||||||
|
|
||||||
|
mw.visitVarInsn(ALOAD,2);
|
||||||
|
mw.visitTypeInsn(NEW,"factor/compiler/StackEffect");
|
||||||
|
mw.visitInsn(DUP);
|
||||||
|
mw.visitLdcInsn(new Integer(effect.inD));
|
||||||
|
mw.visitLdcInsn(new Integer(effect.outD));
|
||||||
|
mw.visitLdcInsn(new Integer(effect.inR));
|
||||||
|
mw.visitLdcInsn(new Integer(effect.outR));
|
||||||
|
mw.visitMethodInsn(INVOKESPECIAL,"factor/compiler/StackEffect",
|
||||||
|
"<init>","(IIII)V");
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL,"factor/compiler/FactorCompiler",
|
||||||
|
"apply","(Lfactor/compiler/StackEffect;)V");
|
||||||
|
mw.visitInsn(RETURN);
|
||||||
|
mw.visitMaxs(0,0);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileEval() method
|
||||||
|
/**
|
||||||
|
* Write the definition of the eval() method in the compiled word.
|
||||||
|
* Local 0 -- this
|
||||||
|
* Local 1 -- interpreter
|
||||||
|
*/
|
||||||
|
protected FactorCompiler compileEval(FactorInterpreter interp,
|
||||||
|
ClassWriter cw, FactorClassLoader loader,
|
||||||
|
String className, StackEffect effect,
|
||||||
|
RecursiveState recursiveCheck)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
cw.visitField(ACC_PRIVATE | ACC_STATIC, "initialized", "Z",
|
||||||
|
null, null);
|
||||||
|
|
||||||
|
// creates a MethodWriter for the 'eval' method
|
||||||
|
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC,
|
||||||
|
"eval", "(Lfactor/FactorInterpreter;)V",
|
||||||
|
null, null);
|
||||||
|
|
||||||
|
// eval() method calls core
|
||||||
|
mw.visitVarInsn(ALOAD,1);
|
||||||
|
|
||||||
|
compileDataStackToJVMStack(effect,mw);
|
||||||
|
|
||||||
|
mw.visitMethodInsn(INVOKESTATIC,className,"core",
|
||||||
|
effect.getCorePrototype());
|
||||||
|
|
||||||
|
compileJVMStackToDataStack(effect,mw);
|
||||||
|
|
||||||
|
mw.visitInsn(RETURN);
|
||||||
|
mw.visitMaxs(0,0);
|
||||||
|
|
||||||
|
// generate core
|
||||||
|
FactorCompiler compiler = new FactorCompiler(interp,word,
|
||||||
|
className,loader);
|
||||||
|
compiler.init(1,effect.inD,effect.inR,"core");
|
||||||
|
compiler.compileCore(endOfDocs,cw,effect,recursiveCheck);
|
||||||
|
|
||||||
|
return compiler;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileDataStackToJVMStack() method
|
||||||
|
private void compileDataStackToJVMStack(StackEffect effect,
|
||||||
|
CodeVisitor mw)
|
||||||
|
{
|
||||||
|
if(effect.inD != 0)
|
||||||
|
{
|
||||||
|
mw.visitVarInsn(ALOAD,1);
|
||||||
|
mw.visitFieldInsn(GETFIELD,
|
||||||
|
"factor/FactorInterpreter", "datastack",
|
||||||
|
"Lfactor/FactorArrayStack;");
|
||||||
|
|
||||||
|
// ensure the stack has enough elements
|
||||||
|
mw.visitInsn(DUP);
|
||||||
|
mw.visitIntInsn(BIPUSH,effect.inD);
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL,
|
||||||
|
"factor/FactorArrayStack", "ensurePop",
|
||||||
|
"(I)V");
|
||||||
|
|
||||||
|
// datastack.stack -> 2
|
||||||
|
mw.visitInsn(DUP);
|
||||||
|
mw.visitFieldInsn(GETFIELD,
|
||||||
|
"factor/FactorArrayStack", "stack",
|
||||||
|
"[Ljava/lang/Object;");
|
||||||
|
mw.visitVarInsn(ASTORE,2);
|
||||||
|
// datastack.top-args.length -> 3
|
||||||
|
mw.visitInsn(DUP);
|
||||||
|
mw.visitFieldInsn(GETFIELD,
|
||||||
|
"factor/FactorArrayStack", "top",
|
||||||
|
"I");
|
||||||
|
mw.visitIntInsn(BIPUSH,effect.inD);
|
||||||
|
mw.visitInsn(ISUB);
|
||||||
|
|
||||||
|
// datastack.top -= args.length
|
||||||
|
mw.visitInsn(DUP_X1);
|
||||||
|
mw.visitFieldInsn(PUTFIELD,
|
||||||
|
"factor/FactorArrayStack", "top",
|
||||||
|
"I");
|
||||||
|
|
||||||
|
mw.visitVarInsn(ISTORE,3);
|
||||||
|
|
||||||
|
for(int i = 0; i < effect.inD; i++)
|
||||||
|
{
|
||||||
|
mw.visitVarInsn(ALOAD,2);
|
||||||
|
mw.visitVarInsn(ILOAD,3);
|
||||||
|
mw.visitInsn(AALOAD);
|
||||||
|
if(i != effect.inD - 1)
|
||||||
|
mw.visitIincInsn(3,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileJVMStackToDataStack() method
|
||||||
|
private void compileJVMStackToDataStack(StackEffect effect,
|
||||||
|
CodeVisitor mw)
|
||||||
|
{
|
||||||
|
if(effect.outD == 1)
|
||||||
|
{
|
||||||
|
// ( datastack )
|
||||||
|
mw.visitVarInsn(ALOAD,1);
|
||||||
|
mw.visitFieldInsn(GETFIELD,
|
||||||
|
"factor/FactorInterpreter", "datastack",
|
||||||
|
"Lfactor/FactorArrayStack;");
|
||||||
|
|
||||||
|
mw.visitInsn(SWAP);
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL,
|
||||||
|
"factor/FactorArrayStack", "push",
|
||||||
|
"(Ljava/lang/Object;)V");
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ fromList() method
|
||||||
|
public void fromList(Cons definition, FactorInterpreter interp)
|
||||||
|
{
|
||||||
|
this.definition = definition;
|
||||||
|
if(definition == null)
|
||||||
|
endOfDocs = null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
endOfDocs = definition;
|
||||||
|
while(endOfDocs != null
|
||||||
|
&& endOfDocs.car instanceof FactorDocComment)
|
||||||
|
endOfDocs = endOfDocs.next();
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ toList() method
|
||||||
|
public Cons toList(FactorInterpreter interp)
|
||||||
|
{
|
||||||
|
return definition;
|
||||||
|
} //}}}
|
||||||
|
}
|
|
@ -49,7 +49,7 @@ public class FactorDocComment implements FactorExternalizable
|
||||||
if(stack)
|
if(stack)
|
||||||
return "( " + msg + " )\n";
|
return "( " + msg + " )\n";
|
||||||
else
|
else
|
||||||
return "#!" + msg + "\n";
|
return "#! " + msg + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStackComment()
|
public boolean isStackComment()
|
||||||
|
|
|
@ -35,7 +35,11 @@ import java.io.*;
|
||||||
|
|
||||||
public class FactorInterpreter implements FactorObject, Runnable
|
public class FactorInterpreter implements FactorObject, Runnable
|
||||||
{
|
{
|
||||||
public static final String VERSION = "0.60.8";
|
public static final String VERSION = "0.66";
|
||||||
|
|
||||||
|
public static final Cons DEFAULT_USE = new Cons("builtins",
|
||||||
|
new Cons("syntax",new Cons("scratchpad",null)));
|
||||||
|
public static final String DEFAULT_IN = "scratchpad";
|
||||||
|
|
||||||
// command line arguments are stored here.
|
// command line arguments are stored here.
|
||||||
public Cons args;
|
public Cons args;
|
||||||
|
@ -63,25 +67,19 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
/**
|
/**
|
||||||
* Vocabulary search path for interactive parser.
|
* Vocabulary search path for interactive parser.
|
||||||
*/
|
*/
|
||||||
public Cons use;
|
public Cons use = DEFAULT_USE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vocabulary to define new words in.
|
* Vocabulary to define new words in.
|
||||||
*/
|
*/
|
||||||
public String in;
|
public String in = DEFAULT_IN;
|
||||||
|
|
||||||
/**
|
|
||||||
* Kernel vocabulary. Re-created on each startup, contains
|
|
||||||
* primitives and parsing words.
|
|
||||||
*/
|
|
||||||
public FactorNamespace builtins;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Most recently defined word.
|
* Most recently defined word.
|
||||||
*/
|
*/
|
||||||
public FactorWord last;
|
public FactorWord last;
|
||||||
|
|
||||||
public FactorNamespace global;
|
public FactorNamespace global = new FactorNamespace();
|
||||||
|
|
||||||
private FactorNamespace interpNamespace;
|
private FactorNamespace interpNamespace;
|
||||||
|
|
||||||
|
@ -91,7 +89,7 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
public static void main(String[] args) throws Exception
|
public static void main(String[] args) throws Exception
|
||||||
{
|
{
|
||||||
FactorInterpreter interp = new FactorInterpreter();
|
FactorInterpreter interp = new FactorInterpreter();
|
||||||
interp.init(args,null);
|
interp.init(args);
|
||||||
interp.run();
|
interp.run();
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
@ -110,13 +108,13 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
this.vocabularies = interp.vocabularies;
|
this.vocabularies = interp.vocabularies;
|
||||||
this.use = interp.use;
|
this.use = interp.use;
|
||||||
this.in = interp.in;
|
this.in = interp.in;
|
||||||
this.builtins = interp.builtins;
|
|
||||||
this.last = interp.last;
|
this.last = interp.last;
|
||||||
this.global = interp.global;
|
this.global = interp.global;
|
||||||
|
this.startupDone = true;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ init() method
|
//{{{ init() method
|
||||||
public void init(String[] args, Object root) throws Exception
|
public void init(String[] args) throws Exception
|
||||||
{
|
{
|
||||||
for(int i = 0; i < args.length; i++)
|
for(int i = 0; i < args.length; i++)
|
||||||
{
|
{
|
||||||
|
@ -138,7 +136,7 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
|
|
||||||
vocabularies = new FactorNamespace();
|
vocabularies = new FactorNamespace();
|
||||||
initBuiltinDictionary();
|
initBuiltinDictionary();
|
||||||
initNamespace(root);
|
initNamespace();
|
||||||
topLevel();
|
topLevel();
|
||||||
|
|
||||||
runBootstrap();
|
runBootstrap();
|
||||||
|
@ -147,81 +145,73 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
//{{{ initBuiltinDictionary() method
|
//{{{ initBuiltinDictionary() method
|
||||||
private void initBuiltinDictionary() throws Exception
|
private void initBuiltinDictionary() throws Exception
|
||||||
{
|
{
|
||||||
builtins = new FactorNamespace();
|
vocabularies.setVariable("builtins",new FactorNamespace());
|
||||||
vocabularies.setVariable("builtins",builtins);
|
vocabularies.setVariable("combinators",new FactorNamespace());
|
||||||
|
vocabularies.setVariable("syntax",new FactorNamespace());
|
||||||
in = "builtins";
|
|
||||||
use = new Cons(in,null);
|
|
||||||
|
|
||||||
/* comments */
|
/* comments */
|
||||||
FactorWord lineComment = define("builtins","!");
|
FactorWord lineComment = define("syntax","!");
|
||||||
lineComment.parsing = new LineComment(lineComment,false);
|
lineComment.parsing = new LineComment(lineComment,false);
|
||||||
FactorWord stackComment = define("builtins","(");
|
FactorWord stackComment = define("syntax","(");
|
||||||
stackComment.parsing = new StackComment(stackComment);
|
stackComment.parsing = new StackComment(stackComment);
|
||||||
FactorWord docComment = define("builtins","#!");
|
FactorWord docComment = define("syntax","#!");
|
||||||
docComment.parsing = new LineComment(docComment,true);
|
docComment.parsing = new LineComment(docComment,true);
|
||||||
|
|
||||||
/* strings */
|
/* strings */
|
||||||
FactorWord str = define("builtins","\"");
|
FactorWord str = define("syntax","\"");
|
||||||
str.parsing = new StringLiteral(str,true);
|
str.parsing = new StringLiteral(str,true);
|
||||||
FactorWord ch = define("builtins","CHAR:");
|
FactorWord ch = define("syntax","CHAR:");
|
||||||
ch.parsing = new CharLiteral(ch);
|
ch.parsing = new CharLiteral(ch);
|
||||||
FactorWord raw = define("builtins","#\"");
|
|
||||||
raw.parsing = new StringLiteral(raw,false);
|
|
||||||
|
|
||||||
/* constants */
|
/* constants */
|
||||||
FactorWord t = define("builtins","t");
|
FactorWord t = define("syntax","t");
|
||||||
t.parsing = new T(t);
|
t.parsing = new T(t);
|
||||||
FactorWord f = define("builtins","f");
|
FactorWord f = define("syntax","f");
|
||||||
f.parsing = new F(f);
|
f.parsing = new F(f);
|
||||||
FactorWord complex = define("builtins","#{");
|
FactorWord complex = define("syntax","#{");
|
||||||
complex.parsing = new ComplexLiteral(complex,"}");
|
complex.parsing = new ComplexLiteral(complex,"}");
|
||||||
|
|
||||||
/* lists */
|
/* lists */
|
||||||
FactorWord bra = define("builtins","[");
|
FactorWord bra = define("syntax","[");
|
||||||
bra.parsing = new Bra(bra);
|
bra.parsing = new Bra(bra);
|
||||||
FactorWord ket = define("builtins","]");
|
FactorWord ket = define("syntax","]");
|
||||||
ket.parsing = new Ket(bra,ket);
|
ket.parsing = new Ket(bra,ket);
|
||||||
FactorWord bar = define("builtins","|");
|
FactorWord bar = define("syntax","|");
|
||||||
bar.parsing = new Bar(bar);
|
bar.parsing = new Bar(bar);
|
||||||
|
|
||||||
|
/* vectors */
|
||||||
|
FactorWord beginVector = define("syntax","{");
|
||||||
|
beginVector.parsing = new BeginVector(beginVector);
|
||||||
|
FactorWord endVector = define("syntax","}");
|
||||||
|
endVector.parsing = new EndVector(beginVector,endVector);
|
||||||
|
|
||||||
/* word defs */
|
/* word defs */
|
||||||
FactorWord def = define("builtins",":");
|
FactorWord def = define("syntax",":");
|
||||||
def.parsing = new Def(def);
|
def.parsing = new Def(def);
|
||||||
def.getNamespace().setVariable("doc-comments",Boolean.TRUE);
|
def.getNamespace().setVariable("doc-comments",Boolean.TRUE);
|
||||||
FactorWord ine = define("builtins",";");
|
FactorWord ine = define("syntax",";");
|
||||||
ine.parsing = new Ine(def,ine);
|
ine.parsing = new Ine(def,ine);
|
||||||
FactorWord shuffle = define("builtins","~<<");
|
FactorWord shuffle = define("syntax","~<<");
|
||||||
shuffle.parsing = new Shuffle(shuffle,">>~");
|
shuffle.parsing = new Shuffle(shuffle,">>~");
|
||||||
|
FactorWord symbol = define("syntax","SYMBOL:");
|
||||||
|
symbol.parsing = new Symbol(symbol);
|
||||||
|
|
||||||
/* reading numbers with another base */
|
/* reading numbers with another base */
|
||||||
FactorWord bin = define("builtins","BIN:");
|
FactorWord bin = define("syntax","BIN:");
|
||||||
bin.parsing = new Base(bin,2);
|
bin.parsing = new Base(bin,2);
|
||||||
FactorWord oct = define("builtins","OCT:");
|
FactorWord oct = define("syntax","OCT:");
|
||||||
oct.parsing = new Base(oct,8);
|
oct.parsing = new Base(oct,8);
|
||||||
FactorWord hex = define("builtins","HEX:");
|
FactorWord hex = define("syntax","HEX:");
|
||||||
hex.parsing = new Base(hex,16);
|
hex.parsing = new Base(hex,16);
|
||||||
|
|
||||||
/* specials */
|
|
||||||
FactorWord dispatch = define("builtins","#");
|
|
||||||
dispatch.parsing = new Dispatch(dispatch);
|
|
||||||
FactorWord unreadable = define("builtins","#<");
|
|
||||||
unreadable.parsing = new Unreadable(unreadable);
|
|
||||||
|
|
||||||
// #: is not handled with a special dispatch. instead, when
|
|
||||||
// a word starting with #: is passed to intern(), it creates
|
|
||||||
// a new symbol
|
|
||||||
FactorWord passthru = define("builtins","#:");
|
|
||||||
passthru.parsing = new PassThrough(passthru);
|
|
||||||
|
|
||||||
/* vocabulary parsing words */
|
/* vocabulary parsing words */
|
||||||
FactorWord noParsing = define("builtins","POSTPONE:");
|
FactorWord noParsing = define("syntax","POSTPONE:");
|
||||||
noParsing.parsing = new NoParsing(noParsing);
|
noParsing.parsing = new NoParsing(noParsing);
|
||||||
FactorWord defer = define("builtins","DEFER:");
|
FactorWord defer = define("syntax","DEFER:");
|
||||||
defer.parsing = new Defer(defer);
|
defer.parsing = new Defer(defer);
|
||||||
FactorWord in = define("builtins","IN:");
|
FactorWord in = define("syntax","IN:");
|
||||||
in.parsing = new In(in);
|
in.parsing = new In(in);
|
||||||
FactorWord use = define("builtins","USE:");
|
FactorWord use = define("syntax","USE:");
|
||||||
use.parsing = new Use(use);
|
use.parsing = new Use(use);
|
||||||
|
|
||||||
FactorWord interpreterGet = define("builtins","interpreter");
|
FactorWord interpreterGet = define("builtins","interpreter");
|
||||||
|
@ -265,21 +255,19 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
define.def = new Define(define);
|
define.def = new Define(define);
|
||||||
|
|
||||||
// combinators
|
// combinators
|
||||||
FactorWord execute = define("builtins","execute");
|
FactorWord execute = define("words","execute");
|
||||||
execute.def = new Execute(execute);
|
execute.def = new Execute(execute);
|
||||||
FactorWord call = define("builtins","call");
|
FactorWord call = define("combinators","call");
|
||||||
call.def = new Call(call);
|
call.def = new Call(call);
|
||||||
call.inline = true;
|
call.inline = true;
|
||||||
FactorWord ifte = define("builtins","ifte");
|
FactorWord ifte = define("combinators","ifte");
|
||||||
ifte.def = new Ifte(ifte);
|
ifte.def = new Ifte(ifte);
|
||||||
ifte.inline = true;
|
ifte.inline = true;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ initNamespace() method
|
//{{{ initNamespace() method
|
||||||
private void initNamespace(Object root) throws Exception
|
private void initNamespace() throws Exception
|
||||||
{
|
{
|
||||||
global = new FactorNamespace(null,root);
|
|
||||||
|
|
||||||
global.setVariable("interpreter",this);
|
global.setVariable("interpreter",this);
|
||||||
|
|
||||||
global.setVariable("verbose-compile",
|
global.setVariable("verbose-compile",
|
||||||
|
@ -298,7 +286,6 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
"args",
|
"args",
|
||||||
"dump",
|
"dump",
|
||||||
"interactive",
|
"interactive",
|
||||||
"builtins",
|
|
||||||
"in",
|
"in",
|
||||||
"last",
|
"last",
|
||||||
"use"
|
"use"
|
||||||
|
@ -502,7 +489,6 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
|
|
||||||
//{{{ getVocabulary() method
|
//{{{ getVocabulary() method
|
||||||
public FactorNamespace getVocabulary(String name)
|
public FactorNamespace getVocabulary(String name)
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
Object value = vocabularies.getVariable(name);
|
Object value = vocabularies.getVariable(name);
|
||||||
if(value instanceof FactorNamespace)
|
if(value instanceof FactorNamespace)
|
||||||
|
@ -513,7 +499,6 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
|
|
||||||
//{{{ defineVocabulary() method
|
//{{{ defineVocabulary() method
|
||||||
public void defineVocabulary(String name)
|
public void defineVocabulary(String name)
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
Object value = vocabularies.getVariable(name);
|
Object value = vocabularies.getVariable(name);
|
||||||
if(value == null)
|
if(value == null)
|
||||||
|
@ -584,31 +569,23 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
if(isUninterned(name))
|
if(isUninterned(name))
|
||||||
return new FactorWord(null,name);
|
return new FactorWord(null,name);
|
||||||
|
|
||||||
try
|
FactorNamespace v = getVocabulary(vocabulary);
|
||||||
|
if(v == null)
|
||||||
{
|
{
|
||||||
FactorNamespace v = getVocabulary(vocabulary);
|
v = new FactorNamespace();
|
||||||
if(v == null)
|
vocabularies.setVariable(vocabulary,v);
|
||||||
{
|
|
||||||
v = new FactorNamespace();
|
|
||||||
vocabularies.setVariable(vocabulary,v);
|
|
||||||
}
|
|
||||||
Object value = v.getVariable(name);
|
|
||||||
if(value instanceof FactorWord)
|
|
||||||
return (FactorWord)value;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// save to same workspace as vocabulary,
|
|
||||||
// or no workspace if vocabulary is builtins
|
|
||||||
FactorWord word = new FactorWord(
|
|
||||||
vocabulary,name,null);
|
|
||||||
v.setVariable(name,word);
|
|
||||||
return word;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
Object value = v.getVariable(name);
|
||||||
|
if(value instanceof FactorWord)
|
||||||
|
return (FactorWord)value;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// should not happen!
|
// save to same workspace as vocabulary,
|
||||||
throw new RuntimeException(e);
|
// or no workspace if vocabulary is builtins
|
||||||
|
FactorWord word = new FactorWord(
|
||||||
|
vocabulary,name,null);
|
||||||
|
v.setVariable(name,word);
|
||||||
|
return word;
|
||||||
}
|
}
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
@ -627,9 +604,12 @@ public class FactorInterpreter implements FactorObject, Runnable
|
||||||
define("kernel","exit*");
|
define("kernel","exit*");
|
||||||
catchstack.push(new Cons(new Integer(1),
|
catchstack.push(new Cons(new Integer(1),
|
||||||
new Cons(searchVocabulary("kernel","exit*"),null)));
|
new Cons(searchVocabulary("kernel","exit*"),null)));
|
||||||
|
define("continuations","suspend");
|
||||||
define("errors","default-error-handler");
|
define("errors","default-error-handler");
|
||||||
catchstack.push(new Cons(searchVocabulary("errors",
|
catchstack.push(new Cons(searchVocabulary("errors",
|
||||||
"default-error-handler"),null));
|
"default-error-handler"),
|
||||||
|
new Cons(searchVocabulary("continuations","suspend"),
|
||||||
|
null)));
|
||||||
callframe = null;
|
callframe = null;
|
||||||
} //}}}
|
} //}}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,29 +49,6 @@ public class FactorLib
|
||||||
return o3;
|
return o3;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ cloneArray() method
|
|
||||||
public static Object[] cloneArray(Object[] array)
|
|
||||||
{
|
|
||||||
Object[] newArray = new Object[array.length];
|
|
||||||
System.arraycopy(array,0,newArray,0,array.length);
|
|
||||||
return newArray;
|
|
||||||
} //}}}
|
|
||||||
|
|
||||||
//{{{ deepCloneArray() method
|
|
||||||
public static Object[] deepCloneArray(Object[] array)
|
|
||||||
{
|
|
||||||
Object[] newArray = new Object[array.length];
|
|
||||||
for(int i = 0; i < array.length; i++)
|
|
||||||
{
|
|
||||||
Object o = array[i];
|
|
||||||
if(o instanceof PublicCloneable)
|
|
||||||
newArray[i] = ((PublicCloneable)o).clone();
|
|
||||||
else
|
|
||||||
newArray[i] = o;
|
|
||||||
}
|
|
||||||
return newArray;
|
|
||||||
} //}}}
|
|
||||||
|
|
||||||
//{{{ error() method
|
//{{{ error() method
|
||||||
public static void error(Object obj) throws Throwable
|
public static void error(Object obj) throws Throwable
|
||||||
{
|
{
|
||||||
|
@ -151,13 +128,14 @@ public class FactorLib
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ exec() method
|
//{{{ exec() method
|
||||||
public static int exec(String[] args) throws Exception
|
public static int exec(String[] args, String dir) throws Exception
|
||||||
{
|
{
|
||||||
int exitCode = -1;
|
int exitCode = -1;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process process = Runtime.getRuntime().exec(args);
|
Process process = Runtime.getRuntime().exec(args,
|
||||||
|
null,new File(dir));
|
||||||
process.getInputStream().close();
|
process.getInputStream().close();
|
||||||
process.getOutputStream().close();
|
process.getOutputStream().close();
|
||||||
process.getErrorStream().close();
|
process.getErrorStream().close();
|
||||||
|
@ -181,17 +159,7 @@ public class FactorLib
|
||||||
*/
|
*/
|
||||||
public static boolean objectsEqual(Object o1, Object o2)
|
public static boolean objectsEqual(Object o1, Object o2)
|
||||||
{
|
{
|
||||||
if(o1 == null)
|
return (o1 == null ? o2 == null : o1.equals(o2));
|
||||||
{
|
|
||||||
if(o2 == null)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if(o2 == null)
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
return o1.equals(o2);
|
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ copy() method
|
//{{{ copy() method
|
||||||
|
@ -201,21 +169,26 @@ public class FactorLib
|
||||||
public static void copy(InputStream in, OutputStream out)
|
public static void copy(InputStream in, OutputStream out)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
byte[] buf = new byte[4096];
|
try
|
||||||
|
|
||||||
int count;
|
|
||||||
|
|
||||||
for(;;)
|
|
||||||
{
|
{
|
||||||
count = in.read(buf,0,buf.length);
|
byte[] buf = new byte[4096];
|
||||||
if(count == -1 || count == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
out.write(buf,0,count);
|
int count;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
count = in.read(buf,0,buf.length);
|
||||||
|
if(count == -1 || count == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
out.write(buf,0,count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
in.close();
|
||||||
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
in.close();
|
|
||||||
out.close();
|
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ readLine() method
|
//{{{ readLine() method
|
||||||
|
@ -243,7 +216,12 @@ public class FactorLib
|
||||||
break;
|
break;
|
||||||
buf.append((char)b);
|
buf.append((char)b);
|
||||||
}
|
}
|
||||||
return buf.toString();
|
|
||||||
|
/* EOF? */
|
||||||
|
if(b == -1 && buf.length() == 0)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return buf.toString();
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ readCount() method
|
//{{{ readCount() method
|
||||||
|
@ -255,7 +233,7 @@ public class FactorLib
|
||||||
int read = 0;
|
int read = 0;
|
||||||
while((read = in.read(bytes,offset,count - offset)) > 0)
|
while((read = in.read(bytes,offset,count - offset)) > 0)
|
||||||
offset += read;
|
offset += read;
|
||||||
return new String(bytes,"ASCII");
|
return new String(bytes,0,offset,"ASCII");
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ readCount() method
|
//{{{ readCount() method
|
||||||
|
@ -267,6 +245,6 @@ public class FactorLib
|
||||||
int read = 0;
|
int read = 0;
|
||||||
while((read = in.read(chars,offset,count - offset)) > 0)
|
while((read = in.read(chars,offset,count - offset)) > 0)
|
||||||
offset += read;
|
offset += read;
|
||||||
return new String(chars);
|
return new String(chars,0,offset);
|
||||||
} //}}}
|
} //}}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,6 @@ public class FactorNamespace implements PublicCloneable, FactorObject
|
||||||
* Cloning constructor.
|
* Cloning constructor.
|
||||||
*/
|
*/
|
||||||
public FactorNamespace(Map words, Object obj)
|
public FactorNamespace(Map words, Object obj)
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
this.words = new TreeMap();
|
this.words = new TreeMap();
|
||||||
|
|
||||||
|
@ -94,8 +93,14 @@ public class FactorNamespace implements PublicCloneable, FactorObject
|
||||||
Map.Entry entry = (Map.Entry)iter.next();
|
Map.Entry entry = (Map.Entry)iter.next();
|
||||||
Object key = entry.getKey();
|
Object key = entry.getKey();
|
||||||
Object value = entry.getValue();
|
Object value = entry.getValue();
|
||||||
if(!(value instanceof VarBinding))
|
if(value instanceof VarBinding)
|
||||||
this.words.put(key,value);
|
{
|
||||||
|
VarBinding b = (VarBinding)value;
|
||||||
|
if(b.instance != null)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.words.put(key,value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +145,7 @@ public class FactorNamespace implements PublicCloneable, FactorObject
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ isDefined() method
|
//{{{ isDefined() method
|
||||||
public synchronized boolean isDefined(String name) throws Exception
|
public synchronized boolean isDefined(String name)
|
||||||
{
|
{
|
||||||
Object o = words.get(name);
|
Object o = words.get(name);
|
||||||
if(o instanceof VarBinding)
|
if(o instanceof VarBinding)
|
||||||
|
@ -165,7 +170,7 @@ public class FactorNamespace implements PublicCloneable, FactorObject
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ getVariable() method
|
//{{{ getVariable() method
|
||||||
public synchronized Object getVariable(String name) throws Exception
|
public synchronized Object getVariable(String name)
|
||||||
{
|
{
|
||||||
Object o = words.get(name);
|
Object o = words.get(name);
|
||||||
if(o instanceof VarBinding)
|
if(o instanceof VarBinding)
|
||||||
|
@ -194,7 +199,6 @@ public class FactorNamespace implements PublicCloneable, FactorObject
|
||||||
|
|
||||||
//{{{ setVariable() method
|
//{{{ setVariable() method
|
||||||
public synchronized void setVariable(String name, Object value)
|
public synchronized void setVariable(String name, Object value)
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
if(name == null)
|
if(name == null)
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
|
@ -227,7 +231,7 @@ public class FactorNamespace implements PublicCloneable, FactorObject
|
||||||
if(!constraint.isAssignableFrom(
|
if(!constraint.isAssignableFrom(
|
||||||
value.getClass()))
|
value.getClass()))
|
||||||
{
|
{
|
||||||
throw new FactorRuntimeException(
|
throw new RuntimeException(
|
||||||
"Can only store "
|
"Can only store "
|
||||||
+ constraint
|
+ constraint
|
||||||
+ " in " + this);
|
+ " in " + this);
|
||||||
|
@ -314,7 +318,7 @@ public class FactorNamespace implements PublicCloneable, FactorObject
|
||||||
/**
|
/**
|
||||||
* Returns a list of variable values.
|
* Returns a list of variable values.
|
||||||
*/
|
*/
|
||||||
public synchronized Cons toValueList() throws Exception
|
public synchronized Cons toValueList()
|
||||||
{
|
{
|
||||||
initAllFields();
|
initAllFields();
|
||||||
|
|
||||||
|
@ -342,7 +346,7 @@ public class FactorNamespace implements PublicCloneable, FactorObject
|
||||||
/**
|
/**
|
||||||
* Returns a list of pairs of variable names, and their values.
|
* Returns a list of pairs of variable names, and their values.
|
||||||
*/
|
*/
|
||||||
public synchronized Cons toVarValueList() throws Exception
|
public synchronized Cons toVarValueList()
|
||||||
{
|
{
|
||||||
initAllFields();
|
initAllFields();
|
||||||
|
|
||||||
|
@ -384,16 +388,30 @@ public class FactorNamespace implements PublicCloneable, FactorObject
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object get() throws Exception
|
public Object get()
|
||||||
{
|
{
|
||||||
return FactorJava.convertFromJavaType(
|
try
|
||||||
field.get(instance));
|
{
|
||||||
|
return FactorJava.convertFromJavaType(
|
||||||
|
field.get(instance));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(Object value) throws Exception
|
public void set(Object value)
|
||||||
{
|
{
|
||||||
field.set(instance,FactorJava.convertToJavaType(
|
try
|
||||||
value,field.getType()));
|
{
|
||||||
|
field.set(instance,FactorJava.convertToJavaType(
|
||||||
|
value,field.getType()));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,20 @@ package factor;
|
||||||
|
|
||||||
public class FactorParseException extends FactorException
|
public class FactorParseException extends FactorException
|
||||||
{
|
{
|
||||||
|
private String filename;
|
||||||
|
private int lineno;
|
||||||
|
private int position;
|
||||||
|
private String msg;
|
||||||
|
|
||||||
public FactorParseException(
|
public FactorParseException(
|
||||||
String filename,
|
String filename,
|
||||||
int lineno,
|
int lineno,
|
||||||
String str)
|
String str)
|
||||||
{
|
{
|
||||||
super(filename + ":" + lineno + ": " + str);
|
super(filename + ":" + lineno + ": " + str);
|
||||||
|
this.filename = filename;
|
||||||
|
this.lineno = lineno;
|
||||||
|
this.msg = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FactorParseException(
|
public FactorParseException(
|
||||||
|
@ -48,6 +56,30 @@ public class FactorParseException extends FactorException
|
||||||
{
|
{
|
||||||
super(filename + ":" + lineno + ": " + str
|
super(filename + ":" + lineno + ": " + str
|
||||||
+ "\n" + getDetailMessage(line,position));
|
+ "\n" + getDetailMessage(line,position));
|
||||||
|
this.filename = filename;
|
||||||
|
this.lineno = lineno;
|
||||||
|
this.position = position;
|
||||||
|
this.msg = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName()
|
||||||
|
{
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLineNumber()
|
||||||
|
{
|
||||||
|
return lineno;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPosition()
|
||||||
|
{
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParserMessage()
|
||||||
|
{
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getDetailMessage(String line, int position)
|
private static String getDetailMessage(String line, int position)
|
||||||
|
|
|
@ -38,36 +38,6 @@ import java.util.*;
|
||||||
*/
|
*/
|
||||||
public class FactorReader
|
public class FactorReader
|
||||||
{
|
{
|
||||||
public static final Cons DEFAULT_USE = new Cons("builtins",
|
|
||||||
new Cons("scratchpad",null));
|
|
||||||
public static final String DEFAULT_IN = "scratchpad";
|
|
||||||
public static final ReadTable DEFAULT_READTABLE;
|
|
||||||
|
|
||||||
//{{{ Class initializer
|
|
||||||
static
|
|
||||||
{
|
|
||||||
DEFAULT_READTABLE = new ReadTable();
|
|
||||||
|
|
||||||
DEFAULT_READTABLE.setCharacterType('\t',ReadTable.WHITESPACE);
|
|
||||||
DEFAULT_READTABLE.setCharacterType('\n',ReadTable.WHITESPACE);
|
|
||||||
|
|
||||||
// ^L
|
|
||||||
DEFAULT_READTABLE.setCharacterType((char)12,ReadTable.WHITESPACE);
|
|
||||||
|
|
||||||
DEFAULT_READTABLE.setCharacterType('\r',ReadTable.WHITESPACE);
|
|
||||||
DEFAULT_READTABLE.setCharacterType(' ',ReadTable.WHITESPACE);
|
|
||||||
|
|
||||||
DEFAULT_READTABLE.setCharacterType('!',ReadTable.CONSTITUENT);
|
|
||||||
DEFAULT_READTABLE.setCharacterType('"',ReadTable.DISPATCH);
|
|
||||||
DEFAULT_READTABLE.setCharacterType('#',ReadTable.DISPATCH);
|
|
||||||
DEFAULT_READTABLE.setCharacterRange('$','[',ReadTable.CONSTITUENT);
|
|
||||||
DEFAULT_READTABLE.setCharacterType('\\',ReadTable.SINGLE_ESCAPE);
|
|
||||||
DEFAULT_READTABLE.setCharacterRange(']','~',ReadTable.CONSTITUENT);
|
|
||||||
|
|
||||||
DEFAULT_READTABLE.setCharacterType('!',ReadTable.DISPATCH);
|
|
||||||
DEFAULT_READTABLE.setCharacterType('(',ReadTable.CONSTITUENT);
|
|
||||||
} //}}}
|
|
||||||
|
|
||||||
private FactorInterpreter interp;
|
private FactorInterpreter interp;
|
||||||
private FactorScanner scanner;
|
private FactorScanner scanner;
|
||||||
private Cons states;
|
private Cons states;
|
||||||
|
@ -153,7 +123,7 @@ public class FactorReader
|
||||||
buf.append("\\0");
|
buf.append("\\0");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if(DEFAULT_READTABLE.getCharacterType(ch)
|
if(ReadTable.DEFAULT_READTABLE.getCharacterType(ch)
|
||||||
== ReadTable.INVALID)
|
== ReadTable.INVALID)
|
||||||
{
|
{
|
||||||
buf.append("\\u");
|
buf.append("\\u");
|
||||||
|
@ -248,7 +218,12 @@ public class FactorReader
|
||||||
|| obj instanceof FactorExternalizable)
|
|| obj instanceof FactorExternalizable)
|
||||||
return obj.toString();
|
return obj.toString();
|
||||||
else if(obj instanceof Character)
|
else if(obj instanceof Character)
|
||||||
return "\"" + charsToEscapes(obj.toString()) + "\"";
|
{
|
||||||
|
if(((Character)obj).charValue() == ' ')
|
||||||
|
return "CHAR: \\s";
|
||||||
|
else
|
||||||
|
return "CHAR: " + charsToEscapes(obj.toString());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return getUnreadableString(obj.toString());
|
return getUnreadableString(obj.toString());
|
||||||
} //}}}
|
} //}}}
|
||||||
|
@ -269,15 +244,25 @@ public class FactorReader
|
||||||
boolean alwaysDocComments,
|
boolean alwaysDocComments,
|
||||||
boolean interactive,
|
boolean interactive,
|
||||||
FactorInterpreter interp)
|
FactorInterpreter interp)
|
||||||
|
{
|
||||||
|
this(new FactorScanner(filename,in),alwaysDocComments,
|
||||||
|
interactive,interp);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ FactorReader constructor
|
||||||
|
public FactorReader(
|
||||||
|
FactorScanner scanner,
|
||||||
|
boolean alwaysDocComments,
|
||||||
|
boolean interactive,
|
||||||
|
FactorInterpreter interp)
|
||||||
{
|
{
|
||||||
this.interp = interp;
|
this.interp = interp;
|
||||||
scanner = new FactorScanner(filename,in);
|
this.scanner = scanner;
|
||||||
scanner.setReadTable(DEFAULT_READTABLE);
|
|
||||||
pushState(toplevel,null);
|
pushState(toplevel,null);
|
||||||
this.alwaysDocComments = alwaysDocComments;
|
this.alwaysDocComments = alwaysDocComments;
|
||||||
this.interactive = interactive;
|
this.interactive = interactive;
|
||||||
this.in = DEFAULT_IN;
|
this.in = FactorInterpreter.DEFAULT_IN;
|
||||||
this.use = DEFAULT_USE;
|
this.use = FactorInterpreter.DEFAULT_USE;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ getScanner() method
|
//{{{ getScanner() method
|
||||||
|
@ -331,12 +316,7 @@ public class FactorReader
|
||||||
if(interp.getVocabulary(name) == null)
|
if(interp.getVocabulary(name) == null)
|
||||||
error("Undefined vocabulary: " + name);
|
error("Undefined vocabulary: " + name);
|
||||||
|
|
||||||
Cons use = getUse();
|
setUse(new Cons(name,getUse()));
|
||||||
|
|
||||||
if(!Cons.contains(use,name))
|
|
||||||
use = new Cons(name,use);
|
|
||||||
|
|
||||||
setUse(use);
|
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ parse() method
|
//{{{ parse() method
|
||||||
|
@ -346,6 +326,8 @@ public class FactorReader
|
||||||
*/
|
*/
|
||||||
public Cons parse() throws Exception
|
public Cons parse() throws Exception
|
||||||
{
|
{
|
||||||
|
scanner.nextLine();
|
||||||
|
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
if(next())
|
if(next())
|
||||||
|
@ -378,21 +360,30 @@ public class FactorReader
|
||||||
*/
|
*/
|
||||||
public FactorWord nextWord(boolean define) throws Exception
|
public FactorWord nextWord(boolean define) throws Exception
|
||||||
{
|
{
|
||||||
Object next = next(true,false);
|
// remember the position before the word name
|
||||||
if(next == FactorScanner.EOF)
|
int line = scanner.getLineNumber();
|
||||||
{
|
int col = scanner.getColumnNumber();
|
||||||
scanner.error("Unexpected EOF");
|
|
||||||
// can't happen
|
Object next = nextNonEOL(true,false);
|
||||||
return null;
|
if(next instanceof Number)
|
||||||
}
|
|
||||||
else if(next instanceof Number)
|
|
||||||
{
|
{
|
||||||
scanner.error("Unexpected " + next);
|
scanner.error("Unexpected " + next);
|
||||||
// can't happen
|
// can't happen
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
else if(next instanceof String)
|
||||||
|
{
|
||||||
|
FactorWord w = intern((String)next,define);
|
||||||
|
if(define && w != null)
|
||||||
|
{
|
||||||
|
w.line = line;
|
||||||
|
w.col = col;
|
||||||
|
w.file = scanner.getFileName();
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return intern((String)next,define);
|
return null;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ next() method
|
//{{{ next() method
|
||||||
|
@ -401,16 +392,23 @@ public class FactorReader
|
||||||
boolean start)
|
boolean start)
|
||||||
throws IOException, FactorParseException
|
throws IOException, FactorParseException
|
||||||
{
|
{
|
||||||
return scanner.next(readNumbers,start,base);
|
Object next = scanner.next(readNumbers,start,base);
|
||||||
|
if(next == FactorScanner.EOL)
|
||||||
|
{
|
||||||
|
scanner.nextLine();
|
||||||
|
return next(readNumbers,start);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return next;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ nextNonEOF() method
|
//{{{ nextNonEOL() method
|
||||||
public Object nextNonEOF(
|
public Object nextNonEOL(
|
||||||
boolean readNumbers,
|
boolean readNumbers,
|
||||||
boolean start)
|
boolean start)
|
||||||
throws IOException, FactorParseException
|
throws IOException, FactorParseException
|
||||||
{
|
{
|
||||||
return scanner.nextNonEOF(readNumbers,start,base);
|
return scanner.nextNonEOL(readNumbers,start,base);
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ next() method
|
//{{{ next() method
|
||||||
|
@ -427,6 +425,12 @@ public class FactorReader
|
||||||
{
|
{
|
||||||
FactorWord word = intern((String)next,
|
FactorWord word = intern((String)next,
|
||||||
!getCurrentState().warnUndefined);
|
!getCurrentState().warnUndefined);
|
||||||
|
if(word == null)
|
||||||
|
{
|
||||||
|
/* We're ignoring errors */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(word.parsing != null)
|
if(word.parsing != null)
|
||||||
{
|
{
|
||||||
word.parsing.eval(interp,this);
|
word.parsing.eval(interp,this);
|
||||||
|
@ -477,10 +481,9 @@ public class FactorReader
|
||||||
{
|
{
|
||||||
ParseState state = getCurrentState();
|
ParseState state = getCurrentState();
|
||||||
if(state.start != start)
|
if(state.start != start)
|
||||||
{
|
|
||||||
scanner.error(end + " does not close " + state.start);
|
scanner.error(end + " does not close " + state.start);
|
||||||
}
|
else
|
||||||
states = states.next();
|
states = states.next();
|
||||||
return state;
|
return state;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
@ -564,7 +567,7 @@ public class FactorReader
|
||||||
if(comma)
|
if(comma)
|
||||||
{
|
{
|
||||||
if(last.cdr != null)
|
if(last.cdr != null)
|
||||||
scanner.error("Only one token allowed after ,");
|
scanner.error("Only one token allowed after |");
|
||||||
last.cdr = obj;
|
last.cdr = obj;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -42,6 +42,11 @@ public class FactorScanner
|
||||||
*/
|
*/
|
||||||
public static final Object EOF = new Object();
|
public static final Object EOF = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special object returned on EOL.
|
||||||
|
*/
|
||||||
|
public static final Object EOL = new Object();
|
||||||
|
|
||||||
private String filename;
|
private String filename;
|
||||||
private BufferedReader in;
|
private BufferedReader in;
|
||||||
|
|
||||||
|
@ -73,6 +78,7 @@ public class FactorScanner
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.in = in;
|
this.in = in;
|
||||||
buf = new StringBuffer();
|
buf = new StringBuffer();
|
||||||
|
setReadTable(ReadTable.DEFAULT_READTABLE);
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ getReadTable() method
|
//{{{ getReadTable() method
|
||||||
|
@ -87,8 +93,26 @@ public class FactorScanner
|
||||||
this.readtable = readtable;
|
this.readtable = readtable;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getLineNumber() method
|
||||||
|
public int getLineNumber()
|
||||||
|
{
|
||||||
|
return lineNo;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getColumnNumber() method
|
||||||
|
public int getColumnNumber()
|
||||||
|
{
|
||||||
|
return position;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getFileName() method
|
||||||
|
public String getFileName()
|
||||||
|
{
|
||||||
|
return filename;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
//{{{ nextLine() method
|
//{{{ nextLine() method
|
||||||
private void nextLine() throws IOException
|
public void nextLine() throws IOException
|
||||||
{
|
{
|
||||||
lineNo++;
|
lineNo++;
|
||||||
line = in.readLine();
|
line = in.readLine();
|
||||||
|
@ -97,8 +121,45 @@ public class FactorScanner
|
||||||
nextLine();
|
nextLine();
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ isEOL() method
|
||||||
|
private boolean isEOL()
|
||||||
|
{
|
||||||
|
return position >= line.length();
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ skipWhitespace() method
|
||||||
|
/**
|
||||||
|
* The Factor parser is so much nicer in Factor than Java!
|
||||||
|
*/
|
||||||
|
public void skipWhitespace() throws FactorParseException
|
||||||
|
{
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(isEOL())
|
||||||
|
return;
|
||||||
|
|
||||||
|
char ch = line.charAt(position++);
|
||||||
|
|
||||||
|
int type = readtable.getCharacterType(ch);
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case ReadTable.INVALID:
|
||||||
|
error("Invalid character in input: " + ch);
|
||||||
|
break;
|
||||||
|
case ReadTable.WHITESPACE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
position--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
//{{{ next() method
|
//{{{ next() method
|
||||||
/**
|
/**
|
||||||
|
* Read a word name. Note that no escaping of characters is done.
|
||||||
|
*
|
||||||
* @param readNumbers If true, will return either a Number or a
|
* @param readNumbers If true, will return either a Number or a
|
||||||
* String. Otherwise, only Strings are returned.
|
* String. Otherwise, only Strings are returned.
|
||||||
* @param start If true, dispatches will be handled by their parsing
|
* @param start If true, dispatches will be handled by their parsing
|
||||||
|
@ -112,21 +173,20 @@ public class FactorScanner
|
||||||
int base)
|
int base)
|
||||||
throws IOException, FactorParseException
|
throws IOException, FactorParseException
|
||||||
{
|
{
|
||||||
if(line == null || position == line.length())
|
|
||||||
nextLine();
|
|
||||||
if(line == null)
|
if(line == null)
|
||||||
return EOF;
|
return EOF;
|
||||||
|
if(position == line.length())
|
||||||
|
return EOL;
|
||||||
|
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
if(position == line.length())
|
if(position >= line.length())
|
||||||
{
|
{
|
||||||
// EOL
|
// EOL
|
||||||
if(buf.length() != 0)
|
if(buf.length() != 0)
|
||||||
return word(readNumbers,base);
|
return word(readNumbers,base);
|
||||||
nextLine();
|
else
|
||||||
if(line == null)
|
return EOL;
|
||||||
return EOF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char ch = line.charAt(position++);
|
char ch = line.charAt(position++);
|
||||||
|
@ -151,29 +211,32 @@ public class FactorScanner
|
||||||
return word(readNumbers,base);
|
return word(readNumbers,base);
|
||||||
}
|
}
|
||||||
case ReadTable.CONSTITUENT:
|
case ReadTable.CONSTITUENT:
|
||||||
buf.append(ch);
|
|
||||||
break;
|
|
||||||
case ReadTable.SINGLE_ESCAPE:
|
case ReadTable.SINGLE_ESCAPE:
|
||||||
buf.append(escape());
|
buf.append(ch);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ nextNonEOF() method
|
//{{{ nextNonEOL() method
|
||||||
public Object nextNonEOF(
|
public Object nextNonEOL(
|
||||||
boolean readNumbers,
|
boolean readNumbers,
|
||||||
boolean start,
|
boolean start,
|
||||||
int base)
|
int base)
|
||||||
throws IOException, FactorParseException
|
throws IOException, FactorParseException
|
||||||
{
|
{
|
||||||
Object next = next(readNumbers,start,base);
|
Object next = next(readNumbers,start,base);
|
||||||
|
if(next == EOL)
|
||||||
|
error("Unexpected EOL");
|
||||||
if(next == EOF)
|
if(next == EOF)
|
||||||
error("Unexpected EOF");
|
error("Unexpected EOF");
|
||||||
return next;
|
return next;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ readUntil() method
|
//{{{ readUntil() method
|
||||||
|
/**
|
||||||
|
* Characters are escaped.
|
||||||
|
*/
|
||||||
public String readUntil(char start, char end, boolean escapesAllowed)
|
public String readUntil(char start, char end, boolean escapesAllowed)
|
||||||
throws IOException, FactorParseException
|
throws IOException, FactorParseException
|
||||||
{
|
{
|
||||||
|
@ -181,11 +244,17 @@ public class FactorScanner
|
||||||
|
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
if(position == line.length())
|
if(isEOL())
|
||||||
|
{
|
||||||
error("Expected " + end + " before EOL");
|
error("Expected " + end + " before EOL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(line == null)
|
if(line == null)
|
||||||
|
{
|
||||||
error("Expected " + end + " before EOF");
|
error("Expected " + end + " before EOF");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
char ch = line.charAt(position++);
|
char ch = line.charAt(position++);
|
||||||
|
|
||||||
|
@ -221,10 +290,16 @@ public class FactorScanner
|
||||||
//{{{ readNonEOF() method
|
//{{{ readNonEOF() method
|
||||||
public char readNonEOF() throws FactorParseException, IOException
|
public char readNonEOF() throws FactorParseException, IOException
|
||||||
{
|
{
|
||||||
if(position == line.length())
|
if(isEOL())
|
||||||
|
{
|
||||||
error("Unexpected EOL");
|
error("Unexpected EOL");
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
if(line == null)
|
if(line == null)
|
||||||
|
{
|
||||||
error("Unexpected EOF");
|
error("Unexpected EOF");
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
|
||||||
return line.charAt(position++);
|
return line.charAt(position++);
|
||||||
} //}}}
|
} //}}}
|
||||||
|
@ -242,7 +317,7 @@ public class FactorScanner
|
||||||
//{{{ atEndOfWord() method
|
//{{{ atEndOfWord() method
|
||||||
public boolean atEndOfWord() throws IOException
|
public boolean atEndOfWord() throws IOException
|
||||||
{
|
{
|
||||||
if(position == line.length())
|
if(isEOL())
|
||||||
return true;
|
return true;
|
||||||
if(line == null)
|
if(line == null)
|
||||||
return true;
|
return true;
|
||||||
|
@ -278,7 +353,10 @@ public class FactorScanner
|
||||||
return '\0';
|
return '\0';
|
||||||
case 'u':
|
case 'u':
|
||||||
if(line.length() - position < 4)
|
if(line.length() - position < 4)
|
||||||
|
{
|
||||||
error("Unexpected EOL");
|
error("Unexpected EOL");
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
|
||||||
String hex = line.substring(position,position + 4);
|
String hex = line.substring(position,position + 4);
|
||||||
|
|
||||||
|
@ -295,7 +373,6 @@ public class FactorScanner
|
||||||
return '\0';
|
return '\0';
|
||||||
default:
|
default:
|
||||||
error("Unknown escape: " + ch);
|
error("Unknown escape: " + ch);
|
||||||
// can't happen
|
|
||||||
return '\0';
|
return '\0';
|
||||||
}
|
}
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/* :folding=explicit:collapseFolds=1: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2004 Slava Pestov.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package factor;
|
||||||
|
|
||||||
|
import factor.compiler.*;
|
||||||
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SYMBOL: name
|
||||||
|
*
|
||||||
|
* Pushes word named.
|
||||||
|
*/
|
||||||
|
public class FactorSymbolDefinition extends FactorWordDefinition
|
||||||
|
{
|
||||||
|
public Object symbol;
|
||||||
|
|
||||||
|
//{{{ FactorSymbolDefinition constructor
|
||||||
|
/**
|
||||||
|
* A new definition.
|
||||||
|
*/
|
||||||
|
public FactorSymbolDefinition(FactorWord word, Object symbol)
|
||||||
|
{
|
||||||
|
super(word);
|
||||||
|
this.symbol = symbol;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ eval() method
|
||||||
|
public void eval(FactorInterpreter interp)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
interp.datastack.push(symbol);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getStackEffect() method
|
||||||
|
public void getStackEffect(RecursiveState recursiveCheck,
|
||||||
|
FactorCompiler compiler) throws Exception
|
||||||
|
{
|
||||||
|
compiler.pushLiteral(symbol,recursiveCheck);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compile() method
|
||||||
|
/**
|
||||||
|
* Compile the given word, returning a new word definition.
|
||||||
|
*/
|
||||||
|
FactorWordDefinition compile(FactorInterpreter interp,
|
||||||
|
RecursiveState recursiveCheck) throws Exception
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileCallTo() method
|
||||||
|
public void compileCallTo(CodeVisitor mw, FactorCompiler compiler,
|
||||||
|
RecursiveState recursiveCheck) throws FactorStackException
|
||||||
|
{
|
||||||
|
compiler.pushLiteral(symbol,recursiveCheck);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ fromList() method
|
||||||
|
public void fromList(Cons definition, FactorInterpreter interp)
|
||||||
|
{
|
||||||
|
this.symbol = definition.car;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ toList() method
|
||||||
|
public Cons toList(FactorInterpreter interp)
|
||||||
|
{
|
||||||
|
return new Cons(symbol,null);
|
||||||
|
} //}}}
|
||||||
|
}
|
|
@ -73,8 +73,15 @@ public class FactorWord implements FactorExternalizable, FactorObject
|
||||||
public FactorClassLoader loader;
|
public FactorClassLoader loader;
|
||||||
public String className;
|
public String className;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For text editor integration.
|
||||||
|
*/
|
||||||
|
public String file;
|
||||||
|
public int line;
|
||||||
|
public int col;
|
||||||
|
|
||||||
private FactorNamespace namespace;
|
private FactorNamespace namespace;
|
||||||
|
|
||||||
//{{{ FactorWord constructor
|
//{{{ FactorWord constructor
|
||||||
/**
|
/**
|
||||||
* Do not use this constructor unless you're writing a packages
|
* Do not use this constructor unless you're writing a packages
|
||||||
|
@ -82,7 +89,7 @@ public class FactorWord implements FactorExternalizable, FactorObject
|
||||||
* intern() method instead.
|
* intern() method instead.
|
||||||
*/
|
*/
|
||||||
public FactorWord(String vocabulary, String name,
|
public FactorWord(String vocabulary, String name,
|
||||||
FactorWordDefinition def) throws Exception
|
FactorWordDefinition def)
|
||||||
{
|
{
|
||||||
this.vocabulary = vocabulary;
|
this.vocabulary = vocabulary;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -191,7 +198,6 @@ public class FactorWord implements FactorExternalizable, FactorObject
|
||||||
//{{{ toString() method
|
//{{{ toString() method
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return name == null ? "#<unnamed>"
|
return name == null ? "#<unnamed>" : name;
|
||||||
: FactorReader.charsToEscapes(name);
|
|
||||||
} //}}}
|
} //}}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,7 @@ import org.objectweb.asm.*;
|
||||||
*/
|
*/
|
||||||
public abstract class FactorWordDefinition implements Constants
|
public abstract class FactorWordDefinition implements Constants
|
||||||
{
|
{
|
||||||
protected FactorWord word;
|
public FactorWord word;
|
||||||
|
|
||||||
public boolean compileFailed;
|
public boolean compileFailed;
|
||||||
|
|
||||||
//{{{ FactorWordDefinition constructor
|
//{{{ FactorWordDefinition constructor
|
||||||
|
@ -55,12 +54,6 @@ public abstract class FactorWordDefinition implements Constants
|
||||||
public abstract void eval(FactorInterpreter interp)
|
public abstract void eval(FactorInterpreter interp)
|
||||||
throws Exception;
|
throws Exception;
|
||||||
|
|
||||||
//{{{ getWord() method
|
|
||||||
public FactorWord getWord(FactorInterpreter interp)
|
|
||||||
{
|
|
||||||
return word;
|
|
||||||
} //}}}
|
|
||||||
|
|
||||||
//{{{ fromList() method
|
//{{{ fromList() method
|
||||||
public void fromList(Cons cons, FactorInterpreter interp)
|
public void fromList(Cons cons, FactorInterpreter interp)
|
||||||
throws FactorRuntimeException
|
throws FactorRuntimeException
|
||||||
|
|
|
@ -0,0 +1,444 @@
|
||||||
|
/* :folding=explicit:collapseFolds=1: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2003, 2004 Slava Pestov.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package factor;
|
||||||
|
|
||||||
|
import factor.db.*;
|
||||||
|
import factor.compiler.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A word definition.
|
||||||
|
*
|
||||||
|
* The pickled form is an unparsed list. The car of the list is the word,
|
||||||
|
* the cdr is toList().
|
||||||
|
*/
|
||||||
|
public abstract class FactorWordDefinition
|
||||||
|
implements Constants, PersistentObject
|
||||||
|
{
|
||||||
|
public static final String ENCODING = "UTF8";
|
||||||
|
|
||||||
|
private Workspace workspace;
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
public FactorWord word;
|
||||||
|
|
||||||
|
public boolean compileFailed;
|
||||||
|
|
||||||
|
//{{{ FactorWordDefinition constructor
|
||||||
|
/**
|
||||||
|
* A new definition.
|
||||||
|
*/
|
||||||
|
public FactorWordDefinition(FactorWord word, Workspace workspace)
|
||||||
|
{
|
||||||
|
this(workspace,workspace == null
|
||||||
|
? 0L : workspace.nextID());
|
||||||
|
this.word = word;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ FactorWordDefinition constructor
|
||||||
|
/**
|
||||||
|
* A blank definition, about to be unpickled.
|
||||||
|
*/
|
||||||
|
public FactorWordDefinition(Workspace workspace, long id)
|
||||||
|
{
|
||||||
|
this.workspace = workspace;
|
||||||
|
this.id = id;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ FactorWordDefinition constructor
|
||||||
|
/**
|
||||||
|
* A definition that is not saved in the current workspace.
|
||||||
|
*/
|
||||||
|
public FactorWordDefinition(FactorWord word)
|
||||||
|
{
|
||||||
|
this.word = word;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
public abstract void eval(FactorInterpreter interp)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
//{{{ fromList() method
|
||||||
|
public void fromList(Cons cons, FactorInterpreter interp)
|
||||||
|
throws FactorRuntimeException, PersistenceException
|
||||||
|
{
|
||||||
|
throw new PersistenceException("Cannot unpickle " + this);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ toList() method
|
||||||
|
public Cons toList(FactorInterpreter interp)
|
||||||
|
{
|
||||||
|
return new Cons(new FactorWord(null,getClass().getName()),null);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getStackEffect() method
|
||||||
|
public final StackEffect getStackEffect(FactorInterpreter interp)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
return getStackEffect(interp,new RecursiveState());
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getStackEffect() method
|
||||||
|
public final StackEffect getStackEffect(FactorInterpreter interp,
|
||||||
|
RecursiveState recursiveCheck)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
FactorCompiler compiler = new FactorCompiler(interp);
|
||||||
|
recursiveCheck.add(word,new StackEffect(),null,null,null);
|
||||||
|
compileCallTo(null,compiler,recursiveCheck);
|
||||||
|
recursiveCheck.remove(word);
|
||||||
|
return compiler.getStackEffect();
|
||||||
|
} //}}}
|
||||||
|
/*
|
||||||
|
//{{{ getStackEffect() method
|
||||||
|
public void getStackEffect(RecursiveState recursiveCheck,
|
||||||
|
FactorCompiler compiler) throws Exception
|
||||||
|
{
|
||||||
|
compileCallTo(null,compiler,recursiveCheck);
|
||||||
|
} //}}}
|
||||||
|
*/
|
||||||
|
//{{{ compile() method
|
||||||
|
FactorWordDefinition compile(FactorInterpreter interp,
|
||||||
|
RecursiveState recursiveCheck) throws Exception
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getDefinition() method
|
||||||
|
protected Cons getDefinition(FactorInterpreter interp)
|
||||||
|
throws FactorCompilerException
|
||||||
|
{
|
||||||
|
Cons definition = toList(interp);
|
||||||
|
|
||||||
|
while(definition != null
|
||||||
|
&& definition.car instanceof FactorDocComment)
|
||||||
|
definition = definition.next();
|
||||||
|
|
||||||
|
return definition;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileCallTo() method
|
||||||
|
/**
|
||||||
|
* Compile a call to this word. Returns maximum JVM stack use.
|
||||||
|
*/
|
||||||
|
public void compileCallTo(CodeVisitor mw, FactorCompiler compiler,
|
||||||
|
RecursiveState recursiveCheck) throws Exception
|
||||||
|
{
|
||||||
|
// normal word
|
||||||
|
String defclass;
|
||||||
|
String defmethod;
|
||||||
|
StackEffect effect;
|
||||||
|
|
||||||
|
FactorClassLoader loader;
|
||||||
|
|
||||||
|
RecursiveForm rec = recursiveCheck.get(word);
|
||||||
|
if(rec != null && rec.active)
|
||||||
|
{
|
||||||
|
if(compiler.interp.verboseCompile)
|
||||||
|
System.err.println("Recursive call to " + rec);
|
||||||
|
effect = StackEffect.decompose(rec.effect,rec.baseCase);
|
||||||
|
|
||||||
|
// are we recursing back on a form inside the current
|
||||||
|
// method?
|
||||||
|
RecursiveForm last = recursiveCheck.last();
|
||||||
|
if(mw != null
|
||||||
|
&& recursiveCheck.allTails(rec)
|
||||||
|
&& last.className.equals(rec.className)
|
||||||
|
&& last.method.equals(rec.method))
|
||||||
|
{
|
||||||
|
if(compiler.interp.verboseCompile)
|
||||||
|
System.err.println(word + " is tail recursive");
|
||||||
|
// GOTO instad of INVOKEVIRTUAL; ie a loop!
|
||||||
|
compiler.normalizeStacks(mw);
|
||||||
|
mw.visitJumpInsn(GOTO,rec.label);
|
||||||
|
compiler.apply(effect);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* recursive method call! */
|
||||||
|
defclass = rec.className;
|
||||||
|
defmethod = rec.method;
|
||||||
|
loader = rec.loader;
|
||||||
|
|
||||||
|
if(mw != null && !defclass.equals(compiler.className))
|
||||||
|
compiler.loader.addDependency(defclass,loader);
|
||||||
|
}
|
||||||
|
else if(mw == null)
|
||||||
|
{
|
||||||
|
Cons definition = getDefinition(compiler.interp);
|
||||||
|
compiler.getStackEffect(definition,recursiveCheck);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// not a recursive call but we're still not compiled
|
||||||
|
// its a bug in the compiler.
|
||||||
|
else if(this instanceof FactorCompoundDefinition)
|
||||||
|
{
|
||||||
|
throw new FactorCompilerException("You are an idiot!");
|
||||||
|
}
|
||||||
|
/* ordinary method call! */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
defclass = getClass().getName().replace('.','/');
|
||||||
|
defmethod = "core";
|
||||||
|
effect = getStackEffect(compiler.interp,
|
||||||
|
new RecursiveState());
|
||||||
|
ClassLoader l = getClass().getClassLoader();
|
||||||
|
if(l instanceof FactorClassLoader)
|
||||||
|
{
|
||||||
|
loader = (FactorClassLoader)l;
|
||||||
|
compiler.loader.addDependency(
|
||||||
|
getClass().getName(),loader);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
loader = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mw == null)
|
||||||
|
compiler.apply(effect);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mw.visitVarInsn(ALOAD,0);
|
||||||
|
compiler.generateArgs(mw,effect.inD,effect.inR,null);
|
||||||
|
String signature = effect.getCorePrototype();
|
||||||
|
mw.visitMethodInsn(INVOKESTATIC,defclass,defmethod,signature);
|
||||||
|
compiler.generateReturn(mw,effect.outD,effect.outR);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileNonRecursiveImmediate() method
|
||||||
|
/**
|
||||||
|
* Non-recursive immediate words are inlined.
|
||||||
|
*/
|
||||||
|
protected void compileNonRecursiveImmediate(CodeVisitor mw,
|
||||||
|
FactorCompiler compiler,
|
||||||
|
RecursiveState recursiveCheck,
|
||||||
|
StackEffect immediateEffect) throws Exception
|
||||||
|
{
|
||||||
|
Cons definition = toList(compiler.getInterpreter());
|
||||||
|
|
||||||
|
Cons endOfDocs = definition;
|
||||||
|
while(endOfDocs != null
|
||||||
|
&& endOfDocs.car instanceof FactorDocComment)
|
||||||
|
endOfDocs = endOfDocs.next();
|
||||||
|
|
||||||
|
compiler.compile(endOfDocs,mw,recursiveCheck);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileRecursiveImmediate() method
|
||||||
|
/**
|
||||||
|
* Recursive immediate words are compiled to an auxiliary method
|
||||||
|
* inside the compiled class definition.
|
||||||
|
*
|
||||||
|
* This must be done so that recursion has something to jump to.
|
||||||
|
*/
|
||||||
|
protected void compileRecursiveImmediate(CodeVisitor mw,
|
||||||
|
FactorCompiler compiler,
|
||||||
|
RecursiveState recursiveCheck,
|
||||||
|
StackEffect immediateEffect) throws Exception
|
||||||
|
{
|
||||||
|
Cons definition = toList(compiler.getInterpreter());
|
||||||
|
|
||||||
|
Cons endOfDocs = definition;
|
||||||
|
while(endOfDocs != null
|
||||||
|
&& endOfDocs.car instanceof FactorDocComment)
|
||||||
|
endOfDocs = endOfDocs.next();
|
||||||
|
|
||||||
|
String method = compiler.auxiliary(word,
|
||||||
|
endOfDocs,immediateEffect,recursiveCheck);
|
||||||
|
|
||||||
|
mw.visitVarInsn(ALOAD,0);
|
||||||
|
|
||||||
|
compiler.generateArgs(mw,immediateEffect.inD,
|
||||||
|
immediateEffect.inR,null);
|
||||||
|
|
||||||
|
String signature = immediateEffect.getCorePrototype();
|
||||||
|
|
||||||
|
mw.visitMethodInsn(INVOKESTATIC,compiler.className,
|
||||||
|
method,signature);
|
||||||
|
|
||||||
|
compiler.generateReturn(mw,
|
||||||
|
immediateEffect.outD,
|
||||||
|
immediateEffect.outR);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileImmediate() method
|
||||||
|
/**
|
||||||
|
* Compile a call to this word. Returns maximum JVM stack use.
|
||||||
|
*/
|
||||||
|
public void compileImmediate(CodeVisitor mw, FactorCompiler compiler,
|
||||||
|
RecursiveState recursiveCheck) throws Exception
|
||||||
|
{
|
||||||
|
Cons definition = getDefinition(compiler.interp);
|
||||||
|
|
||||||
|
if(mw == null)
|
||||||
|
{
|
||||||
|
compiler.compile(definition,null,recursiveCheck);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine stack effect of this instantiation, and if its
|
||||||
|
// recursive.
|
||||||
|
|
||||||
|
FactorArrayStack savedDatastack = (FactorArrayStack)
|
||||||
|
compiler.datastack.clone();
|
||||||
|
FactorCallStack savedCallstack = (FactorCallStack)
|
||||||
|
compiler.callstack.clone();
|
||||||
|
StackEffect savedEffect = compiler.getStackEffect();
|
||||||
|
|
||||||
|
RecursiveState _recursiveCheck = (RecursiveState)
|
||||||
|
recursiveCheck.clone();
|
||||||
|
_recursiveCheck.last().effect = compiler.getStackEffect();
|
||||||
|
compileImmediate(null,compiler,_recursiveCheck);
|
||||||
|
|
||||||
|
boolean recursive = (_recursiveCheck.last().baseCase != null);
|
||||||
|
|
||||||
|
StackEffect effect = compiler.getStackEffect();
|
||||||
|
|
||||||
|
StackEffect immediateEffect = StackEffect.decompose(
|
||||||
|
savedEffect,compiler.getStackEffect());
|
||||||
|
|
||||||
|
// restore previous state.
|
||||||
|
|
||||||
|
FactorArrayStack afterDatastack = (FactorArrayStack)
|
||||||
|
compiler.datastack.clone();
|
||||||
|
FactorCallStack afterCallstack = (FactorCallStack)
|
||||||
|
compiler.callstack.clone();
|
||||||
|
|
||||||
|
compiler.datastack = (FactorArrayStack)savedDatastack.clone();
|
||||||
|
compiler.callstack = (FactorCallStack)savedCallstack.clone();
|
||||||
|
compiler.effect = savedEffect;
|
||||||
|
|
||||||
|
if(!recursive)
|
||||||
|
{
|
||||||
|
// not recursive; inline.
|
||||||
|
compileNonRecursiveImmediate(mw,compiler,recursiveCheck,
|
||||||
|
immediateEffect);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// recursive; must generate auxiliary method.
|
||||||
|
compileRecursiveImmediate(mw,compiler,recursiveCheck,
|
||||||
|
immediateEffect);
|
||||||
|
|
||||||
|
mergeStacks(savedDatastack,afterDatastack,compiler.datastack);
|
||||||
|
mergeStacks(savedCallstack,afterCallstack,compiler.callstack);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ mergeStacks() method
|
||||||
|
private void mergeStacks(FactorArrayStack s1, FactorArrayStack s2,
|
||||||
|
FactorArrayStack into)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < s2.top; i++)
|
||||||
|
{
|
||||||
|
if(s1.top <= i)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(FactorLib.objectsEqual(s1.stack[i],
|
||||||
|
s2.stack[i]))
|
||||||
|
{
|
||||||
|
into.stack[i] = s1.stack[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getWorkspace() method
|
||||||
|
/**
|
||||||
|
* Each persistent object is stored in one workspace only.
|
||||||
|
*/
|
||||||
|
public Workspace getWorkspace()
|
||||||
|
{
|
||||||
|
return workspace;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getID() method
|
||||||
|
/**
|
||||||
|
* Each persistent object has an associated ID.
|
||||||
|
*/
|
||||||
|
public long getID()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ pickle() method
|
||||||
|
/**
|
||||||
|
* Each persistent object can turn itself into a byte array.
|
||||||
|
*/
|
||||||
|
public byte[] pickle(FactorInterpreter interp)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
Cons pickle = new Cons(word,toList(interp));
|
||||||
|
bytes.write((FactorReader.getVocabularyDeclaration(pickle)
|
||||||
|
+ FactorReader.unparseDBObject(pickle))
|
||||||
|
.getBytes(ENCODING));
|
||||||
|
|
||||||
|
return bytes.toByteArray();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
// should not happen with byte array stream
|
||||||
|
throw new PersistenceException("Unexpected error",e);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ unpickle() method
|
||||||
|
/**
|
||||||
|
* Each persistent object can set its state to that in a byte array.
|
||||||
|
*/
|
||||||
|
public void unpickle(byte[] bytes, int offset, FactorInterpreter interp)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String unparsed = new String(bytes,offset,
|
||||||
|
bytes.length - offset,ENCODING);
|
||||||
|
Cons pickle = (Cons)FactorReader.parseObject(unparsed,
|
||||||
|
interp);
|
||||||
|
word = (FactorWord)pickle.car;
|
||||||
|
fromList(pickle.next(),interp);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
// should not happen with byte array stream
|
||||||
|
throw new PersistenceException("Unexpected error",e);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ toString() method
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return getClass().getName() + ": " + word;
|
||||||
|
} //}}}
|
||||||
|
}
|
|
@ -34,6 +34,28 @@ package factor;
|
||||||
*/
|
*/
|
||||||
public class ReadTable
|
public class ReadTable
|
||||||
{
|
{
|
||||||
|
public static final ReadTable DEFAULT_READTABLE;
|
||||||
|
|
||||||
|
//{{{ Class initializer
|
||||||
|
static
|
||||||
|
{
|
||||||
|
DEFAULT_READTABLE = new ReadTable();
|
||||||
|
|
||||||
|
DEFAULT_READTABLE.setCharacterType('\t',ReadTable.WHITESPACE);
|
||||||
|
DEFAULT_READTABLE.setCharacterType('\n',ReadTable.WHITESPACE);
|
||||||
|
DEFAULT_READTABLE.setCharacterType('\r',ReadTable.WHITESPACE);
|
||||||
|
DEFAULT_READTABLE.setCharacterType(' ',ReadTable.WHITESPACE);
|
||||||
|
|
||||||
|
DEFAULT_READTABLE.setCharacterType('!',ReadTable.CONSTITUENT);
|
||||||
|
DEFAULT_READTABLE.setCharacterType('"',ReadTable.DISPATCH);
|
||||||
|
DEFAULT_READTABLE.setCharacterRange('#','[',ReadTable.CONSTITUENT);
|
||||||
|
DEFAULT_READTABLE.setCharacterType('\\',ReadTable.SINGLE_ESCAPE);
|
||||||
|
DEFAULT_READTABLE.setCharacterRange(']','~',ReadTable.CONSTITUENT);
|
||||||
|
|
||||||
|
DEFAULT_READTABLE.setCharacterType('!',ReadTable.DISPATCH);
|
||||||
|
DEFAULT_READTABLE.setCharacterType('(',ReadTable.CONSTITUENT);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalid character.
|
* Invalid character.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -42,6 +42,13 @@ public class FactorClassLoader extends ClassLoader
|
||||||
{
|
{
|
||||||
private long id;
|
private long id;
|
||||||
private FactorNamespace table = new FactorNamespace();
|
private FactorNamespace table = new FactorNamespace();
|
||||||
|
private ClassLoader delegate;
|
||||||
|
|
||||||
|
//{{{ FactorClassLoader constructor
|
||||||
|
public FactorClassLoader(ClassLoader delegate)
|
||||||
|
{
|
||||||
|
this.delegate = delegate;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
//{{{ addDependency() method
|
//{{{ addDependency() method
|
||||||
public void addDependency(String name, FactorClassLoader loader)
|
public void addDependency(String name, FactorClassLoader loader)
|
||||||
|
@ -88,7 +95,15 @@ public class FactorClassLoader extends ClassLoader
|
||||||
System.err.println("WARNING: unknown object in class loader table for " + this + ": " + obj);
|
System.err.println("WARNING: unknown object in class loader table for " + this + ": " + obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.loadClass(name,resolve);
|
if(delegate == null)
|
||||||
|
return super.loadClass(name,resolve);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c = delegate.loadClass(name);
|
||||||
|
if(resolve)
|
||||||
|
resolveClass(c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(ClassNotFoundException e)
|
catch(ClassNotFoundException e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,983 @@
|
||||||
|
/* :folding=explicit:collapseFolds=1: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2004 Slava Pestov.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package factor.compiler;
|
||||||
|
|
||||||
|
import factor.*;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.*;
|
||||||
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
|
public class FactorCompiler implements Constants
|
||||||
|
{
|
||||||
|
public final FactorInterpreter interp;
|
||||||
|
|
||||||
|
public final FactorWord word;
|
||||||
|
public final String className;
|
||||||
|
public final FactorClassLoader loader;
|
||||||
|
public String method;
|
||||||
|
|
||||||
|
private int base;
|
||||||
|
private int allotD;
|
||||||
|
private int allotR;
|
||||||
|
|
||||||
|
public FactorArrayStack datastack;
|
||||||
|
public FactorCallStack callstack;
|
||||||
|
|
||||||
|
private int literalCount;
|
||||||
|
|
||||||
|
private Map literals;
|
||||||
|
|
||||||
|
public StackEffect effect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getStackEffect() turns these into arrays and places them in the
|
||||||
|
* returned object.
|
||||||
|
*/
|
||||||
|
private Cons inDtypes, inRtypes;
|
||||||
|
|
||||||
|
private Cons aux;
|
||||||
|
private int auxCount;
|
||||||
|
|
||||||
|
//{{{ FactorCompiler constructor
|
||||||
|
/**
|
||||||
|
* For balancing.
|
||||||
|
*/
|
||||||
|
public FactorCompiler(FactorInterpreter interp)
|
||||||
|
{
|
||||||
|
this(interp,null,null,null);
|
||||||
|
init(0,0,0,null);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ FactorCompiler constructor
|
||||||
|
/**
|
||||||
|
* For compiling.
|
||||||
|
*/
|
||||||
|
public FactorCompiler(FactorInterpreter interp,
|
||||||
|
FactorWord word, String className,
|
||||||
|
FactorClassLoader loader)
|
||||||
|
{
|
||||||
|
this.interp = interp;
|
||||||
|
this.word = word;
|
||||||
|
this.className = className;
|
||||||
|
this.loader = loader;
|
||||||
|
|
||||||
|
literals = new HashMap();
|
||||||
|
|
||||||
|
datastack = new FactorArrayStack();
|
||||||
|
callstack = new FactorCallStack();
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getInterpreter() method
|
||||||
|
public FactorInterpreter getInterpreter()
|
||||||
|
{
|
||||||
|
return interp;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ init() method
|
||||||
|
public void init(int base, int allotD, int allotR, String method)
|
||||||
|
{
|
||||||
|
effect = new StackEffect();
|
||||||
|
|
||||||
|
this.base = base;
|
||||||
|
|
||||||
|
datastack.top = 0;
|
||||||
|
callstack.top = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < allotD; i++)
|
||||||
|
{
|
||||||
|
Result r = new Result(base + i,this,null,
|
||||||
|
Object.class);
|
||||||
|
datastack.push(r);
|
||||||
|
inDtypes = new Cons(r,inDtypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < allotR; i++)
|
||||||
|
{
|
||||||
|
Result r = new Result(base + allotD + i,this,null,
|
||||||
|
Object.class);
|
||||||
|
callstack.push(r);
|
||||||
|
inRtypes = new Cons(r,inRtypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.allotD = allotD;
|
||||||
|
this.allotR = allotR;
|
||||||
|
effect.inD = allotD;
|
||||||
|
effect.inR = allotR;
|
||||||
|
|
||||||
|
this.method = method;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getAllotedEffect() method
|
||||||
|
public StackEffect getAllotedEffect()
|
||||||
|
{
|
||||||
|
return new StackEffect(allotD,allotR,0,0);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ ensure() method
|
||||||
|
public void ensure(FactorArrayStack stack, Class type)
|
||||||
|
{
|
||||||
|
if(stack.top == 0)
|
||||||
|
{
|
||||||
|
Result r = new Result(allocate(),this,null,type);
|
||||||
|
if(stack == datastack)
|
||||||
|
{
|
||||||
|
inDtypes = new Cons(r,inDtypes);
|
||||||
|
effect.inD++;
|
||||||
|
}
|
||||||
|
else if(stack == callstack)
|
||||||
|
{
|
||||||
|
inRtypes = new Cons(r,inRtypes);
|
||||||
|
effect.inR++;
|
||||||
|
}
|
||||||
|
stack.push(r);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ ensure() method
|
||||||
|
/**
|
||||||
|
* Ensure stack has at least 'count' elements.
|
||||||
|
* Eg, if count is 4 and stack is A B,
|
||||||
|
* stack will become RESULT RESULT A B.
|
||||||
|
* Used when deducing stack effects.
|
||||||
|
*/
|
||||||
|
public void ensure(FactorArrayStack stack, int count)
|
||||||
|
{
|
||||||
|
Class[] types = new Class[count];
|
||||||
|
for(int i = 0; i < types.length; i++)
|
||||||
|
types[i] = Object.class;
|
||||||
|
ensure(stack,types);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ ensure() method
|
||||||
|
/**
|
||||||
|
* Ensure stack has at least 'count' elements.
|
||||||
|
* Eg, if count is 4 and stack is A B,
|
||||||
|
* stack will become RESULT RESULT A B.
|
||||||
|
* Used when deducing stack effects.
|
||||||
|
*/
|
||||||
|
public void ensure(FactorArrayStack stack, Class[] types)
|
||||||
|
{
|
||||||
|
int top = stack.top;
|
||||||
|
if(top < types.length)
|
||||||
|
{
|
||||||
|
Cons typespec = null;
|
||||||
|
|
||||||
|
if(stack == datastack)
|
||||||
|
effect.inD += (types.length - top);
|
||||||
|
else if(stack == callstack)
|
||||||
|
effect.inR += (types.length - top);
|
||||||
|
|
||||||
|
stack.ensurePush(types.length - top);
|
||||||
|
System.arraycopy(stack.stack,0,stack.stack,
|
||||||
|
types.length - top,top);
|
||||||
|
for(int i = 0; i < types.length - top; i++)
|
||||||
|
{
|
||||||
|
int local = allocate();
|
||||||
|
Result r = new Result(
|
||||||
|
local,this,null,types[i]);
|
||||||
|
stack.stack[i] = r;
|
||||||
|
typespec = new Cons(r,typespec);
|
||||||
|
}
|
||||||
|
stack.top = types.length;
|
||||||
|
|
||||||
|
if(stack == datastack)
|
||||||
|
inDtypes = Cons.nappend(inDtypes,typespec);
|
||||||
|
else if(stack == callstack)
|
||||||
|
inRtypes = Cons.nappend(inRtypes,typespec);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ consume() method
|
||||||
|
public void consume(FactorArrayStack stack, int count)
|
||||||
|
{
|
||||||
|
ensure(stack,count);
|
||||||
|
stack.top -= count;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ produce() method
|
||||||
|
public void produce(FactorArrayStack stack, int count)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int local = allocate();
|
||||||
|
stack.push(new Result(local,this,null,Object.class));
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ apply() method
|
||||||
|
public void apply(StackEffect se)
|
||||||
|
{
|
||||||
|
consume(datastack,se.inD);
|
||||||
|
produce(datastack,se.outD);
|
||||||
|
consume(callstack,se.inR);
|
||||||
|
produce(callstack,se.outR);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getTypeSpec() method
|
||||||
|
private Class[] getTypeSpec(Cons list)
|
||||||
|
{
|
||||||
|
if(list == null)
|
||||||
|
return new Class[0];
|
||||||
|
|
||||||
|
int length = list.length();
|
||||||
|
Class[] typespec = new Class[length];
|
||||||
|
int i = 0;
|
||||||
|
while(list != null)
|
||||||
|
{
|
||||||
|
typespec[length - i - 1]
|
||||||
|
= ((FlowObject)list.car).getType();
|
||||||
|
i++;
|
||||||
|
list = list.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return typespec;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getStackEffect() method
|
||||||
|
public StackEffect getStackEffect()
|
||||||
|
{
|
||||||
|
effect.inDtypes = getTypeSpec(inDtypes);
|
||||||
|
|
||||||
|
effect.outD = datastack.top;
|
||||||
|
|
||||||
|
effect.outDtypes = new Class[datastack.top];
|
||||||
|
for(int i = 0; i < datastack.top; i++)
|
||||||
|
{
|
||||||
|
effect.outDtypes[i] = ((FlowObject)datastack.stack[i])
|
||||||
|
.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
effect.inRtypes = getTypeSpec(inRtypes);
|
||||||
|
|
||||||
|
effect.outR = callstack.top;
|
||||||
|
|
||||||
|
effect.outRtypes = new Class[callstack.top];
|
||||||
|
for(int i = 0; i < callstack.top; i++)
|
||||||
|
{
|
||||||
|
effect.outRtypes[i] = ((FlowObject)callstack.stack[i])
|
||||||
|
.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (StackEffect)effect.clone();
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getStackEffect() method
|
||||||
|
public void getStackEffect(Cons definition,
|
||||||
|
RecursiveState recursiveCheck)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
while(definition != null)
|
||||||
|
{
|
||||||
|
Object obj = definition.car;
|
||||||
|
if(obj instanceof FactorWord)
|
||||||
|
getStackEffectOfWord((FactorWord)obj,recursiveCheck);
|
||||||
|
else
|
||||||
|
pushLiteral(obj,recursiveCheck);
|
||||||
|
|
||||||
|
definition = definition.next();
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getStackEffectOfWord() method
|
||||||
|
private void getStackEffectOfWord(FactorWord word,
|
||||||
|
RecursiveState recursiveCheck)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
RecursiveForm rec = recursiveCheck.get(word);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
boolean recursiveCall;
|
||||||
|
if(rec == null)
|
||||||
|
{
|
||||||
|
recursiveCall = false;
|
||||||
|
recursiveCheck.add(word,
|
||||||
|
getStackEffect(),
|
||||||
|
className,loader,
|
||||||
|
"core");
|
||||||
|
/* recursiveCheck.last().tail
|
||||||
|
= (word.def instanceof FactorPrimitiveDefinition); */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
recursiveCall = true;
|
||||||
|
rec.active = true;
|
||||||
|
}/*
|
||||||
|
if(rec == null)
|
||||||
|
recursiveCheck.add(word,getStackEffect(),null,null,null);
|
||||||
|
else
|
||||||
|
rec.active = true; */
|
||||||
|
|
||||||
|
compileCallTo(word,null,recursiveCheck,false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if(rec == null)
|
||||||
|
recursiveCheck.remove(word);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rec.active = false;
|
||||||
|
rec.tail = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileCore() method
|
||||||
|
public void compileCore(Cons definition, ClassWriter cw,
|
||||||
|
StackEffect effect, RecursiveState recursiveCheck)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
RecursiveForm last = recursiveCheck.last();
|
||||||
|
last.method = "core";
|
||||||
|
last.className = className;
|
||||||
|
last.loader = loader;
|
||||||
|
|
||||||
|
compileMethod(definition,cw,"core",effect,word,recursiveCheck,true);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileFieldInit() method
|
||||||
|
private void compileFieldInit(CodeVisitor mw, Label start)
|
||||||
|
{
|
||||||
|
mw.visitFieldInsn(GETSTATIC,className,"initialized","Z");
|
||||||
|
mw.visitJumpInsn(IFNE,start);
|
||||||
|
mw.visitInsn(ICONST_1);
|
||||||
|
mw.visitFieldInsn(PUTSTATIC,className,"initialized","Z");
|
||||||
|
mw.visitVarInsn(ALOAD,0);
|
||||||
|
mw.visitMethodInsn(INVOKESTATIC,className,"setFields",
|
||||||
|
"(Lfactor/FactorInterpreter;)V");
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileReturn() method
|
||||||
|
/**
|
||||||
|
* Once the word finishes executing, any return values need to be
|
||||||
|
* passed up.
|
||||||
|
*/
|
||||||
|
private void compileReturn(CodeVisitor mw, Label end,
|
||||||
|
StackEffect effect) throws Exception
|
||||||
|
{
|
||||||
|
// special case where return value is passed on
|
||||||
|
// JVM operand stack
|
||||||
|
|
||||||
|
// note: in each branch, must visit end label before RETURN!
|
||||||
|
if(effect.outD == 0 && effect.outR == 0)
|
||||||
|
{
|
||||||
|
mw.visitLabel(end);
|
||||||
|
mw.visitInsn(RETURN);
|
||||||
|
}
|
||||||
|
else if(effect.outD == 1 && effect.outR == 0)
|
||||||
|
{
|
||||||
|
pop(datastack,mw,Object.class);
|
||||||
|
mw.visitLabel(end);
|
||||||
|
mw.visitInsn(ARETURN);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// store datastack in a local
|
||||||
|
mw.visitVarInsn(ALOAD,0);
|
||||||
|
mw.visitFieldInsn(GETFIELD,
|
||||||
|
"factor/FactorInterpreter",
|
||||||
|
"datastack",
|
||||||
|
"Lfactor/FactorArrayStack;");
|
||||||
|
int datastackLocal = allocate();
|
||||||
|
mw.visitVarInsn(ASTORE,datastackLocal);
|
||||||
|
|
||||||
|
for(int i = 0; i < datastack.top; i++)
|
||||||
|
{
|
||||||
|
mw.visitVarInsn(ALOAD,datastackLocal);
|
||||||
|
((FlowObject)datastack.stack[i])
|
||||||
|
.pop(mw,Object.class);
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL,
|
||||||
|
"factor/FactorArrayStack",
|
||||||
|
"push",
|
||||||
|
"(Ljava/lang/Object;)V");
|
||||||
|
}
|
||||||
|
|
||||||
|
datastack.top = 0;
|
||||||
|
|
||||||
|
// store callstack in a local
|
||||||
|
mw.visitVarInsn(ALOAD,0);
|
||||||
|
mw.visitFieldInsn(GETFIELD,
|
||||||
|
"factor/FactorInterpreter",
|
||||||
|
"callstack",
|
||||||
|
"Lfactor/FactorCallStack;");
|
||||||
|
int callstackLocal = allocate();
|
||||||
|
mw.visitVarInsn(ASTORE,callstackLocal);
|
||||||
|
|
||||||
|
for(int i = 0; i < callstack.top; i++)
|
||||||
|
{
|
||||||
|
mw.visitVarInsn(ALOAD,callstackLocal);
|
||||||
|
((FlowObject)callstack.stack[i])
|
||||||
|
.pop(mw,Object.class);
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL,
|
||||||
|
"factor/FactorCallStack",
|
||||||
|
"push",
|
||||||
|
"(Ljava/lang/Object;)V");
|
||||||
|
}
|
||||||
|
|
||||||
|
callstack.top = 0;
|
||||||
|
|
||||||
|
mw.visitLabel(end);
|
||||||
|
mw.visitInsn(RETURN);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileMethod() method
|
||||||
|
/**
|
||||||
|
* Compiles a method.
|
||||||
|
*/
|
||||||
|
public void compileMethod(Cons definition, ClassWriter cw,
|
||||||
|
String methodName, StackEffect effect, FactorWord word,
|
||||||
|
RecursiveState recursiveCheck, boolean fieldInit)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
String signature = effect.getCorePrototype();
|
||||||
|
|
||||||
|
CodeVisitor mw = cw.visitMethod(ACC_PUBLIC | ACC_STATIC,
|
||||||
|
methodName,signature,null,null);
|
||||||
|
|
||||||
|
Label start = recursiveCheck.get(word).label;
|
||||||
|
|
||||||
|
if(fieldInit)
|
||||||
|
compileFieldInit(mw,start);
|
||||||
|
|
||||||
|
mw.visitLabel(start);
|
||||||
|
|
||||||
|
compile(definition,mw,recursiveCheck);
|
||||||
|
|
||||||
|
Label end = new Label();
|
||||||
|
|
||||||
|
compileReturn(mw,end,effect);
|
||||||
|
|
||||||
|
compileExceptionHandler(mw,start,end,word);
|
||||||
|
|
||||||
|
mw.visitMaxs(0,0);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileExceptionHandler() method
|
||||||
|
private void compileExceptionHandler(CodeVisitor mw,
|
||||||
|
Label start, Label end, FactorWord word)
|
||||||
|
{
|
||||||
|
// otherwise no code can throw exception etc
|
||||||
|
if(start.getOffset() != end.getOffset())
|
||||||
|
{
|
||||||
|
// Now compile exception handler.
|
||||||
|
|
||||||
|
Label target = new Label();
|
||||||
|
mw.visitLabel(target);
|
||||||
|
|
||||||
|
mw.visitVarInsn(ASTORE,1);
|
||||||
|
mw.visitVarInsn(ALOAD,0);
|
||||||
|
mw.visitFieldInsn(GETSTATIC,className,literal(word),
|
||||||
|
"Ljava/lang/Object;");
|
||||||
|
mw.visitTypeInsn(CHECKCAST,"factor/FactorWord");
|
||||||
|
mw.visitVarInsn(ALOAD,1);
|
||||||
|
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL,"factor/FactorInterpreter",
|
||||||
|
"compiledException",
|
||||||
|
"(Lfactor/FactorWord;Ljava/lang/Throwable;)V");
|
||||||
|
|
||||||
|
mw.visitVarInsn(ALOAD,1);
|
||||||
|
mw.visitInsn(ATHROW);
|
||||||
|
|
||||||
|
mw.visitTryCatchBlock(start,end,target,"java/lang/Throwable");
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compile() method
|
||||||
|
/**
|
||||||
|
* Compiles a quotation.
|
||||||
|
*/
|
||||||
|
public void compile(Cons definition, CodeVisitor mw,
|
||||||
|
RecursiveState recursiveCheck) throws Exception
|
||||||
|
{
|
||||||
|
while(definition != null)
|
||||||
|
{
|
||||||
|
Object obj = definition.car;
|
||||||
|
if(obj instanceof FactorWord)
|
||||||
|
{
|
||||||
|
compileWord((FactorWord)obj,mw,recursiveCheck,
|
||||||
|
definition.cdr == null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pushLiteral(obj,recursiveCheck);
|
||||||
|
|
||||||
|
definition = definition.next();
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileWord() method
|
||||||
|
private void compileWord(FactorWord w, CodeVisitor mw,
|
||||||
|
RecursiveState recursiveCheck,
|
||||||
|
boolean tail) throws Exception
|
||||||
|
{
|
||||||
|
if(tail && interp.verboseCompile)
|
||||||
|
System.err.println("Tail: " + recursiveCheck.last());
|
||||||
|
recursiveCheck.last().tail = tail;
|
||||||
|
|
||||||
|
RecursiveForm rec = recursiveCheck.get(w);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
boolean recursiveCall;
|
||||||
|
if(rec == null)
|
||||||
|
{
|
||||||
|
recursiveCall = false;
|
||||||
|
recursiveCheck.add(w,
|
||||||
|
new StackEffect(),
|
||||||
|
className,loader,
|
||||||
|
"core");
|
||||||
|
recursiveCheck.last().tail
|
||||||
|
= (w.def instanceof FactorPrimitiveDefinition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
recursiveCall = true;
|
||||||
|
rec.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
compileCallTo(w,mw,recursiveCheck,recursiveCall);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if(rec == null)
|
||||||
|
recursiveCheck.remove(w);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rec.active = false;
|
||||||
|
rec.tail = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ compileCallTo() method
|
||||||
|
private void compileCallTo(FactorWord w, CodeVisitor mw,
|
||||||
|
RecursiveState recursiveCheck,
|
||||||
|
boolean recursiveCall) throws Exception
|
||||||
|
{
|
||||||
|
if(w.def == null)
|
||||||
|
throw new FactorUndefinedWordException(w);
|
||||||
|
|
||||||
|
FactorWordDefinition d = w.def;
|
||||||
|
|
||||||
|
if(!recursiveCall)
|
||||||
|
{
|
||||||
|
StackEffect effect = getStackEffectOrNull(d);
|
||||||
|
if(w.inline)
|
||||||
|
{
|
||||||
|
d.compileImmediate(mw,this,recursiveCheck);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(d instanceof FactorCompoundDefinition
|
||||||
|
&& mw != null)
|
||||||
|
{
|
||||||
|
w.compile(interp,recursiveCheck);
|
||||||
|
if(d == w.def)
|
||||||
|
{
|
||||||
|
throw new FactorCompilerException(word + " depends on " + w + " which cannot be compiled");
|
||||||
|
}
|
||||||
|
d = w.def;
|
||||||
|
}
|
||||||
|
|
||||||
|
w.compileRef = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
d.compileCallTo(mw,this,recursiveCheck);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ push() method
|
||||||
|
/**
|
||||||
|
* Generates code for pushing the top of the JVM stack onto the
|
||||||
|
* data stack. Also generates code for converting this value to
|
||||||
|
* the given type.
|
||||||
|
*/
|
||||||
|
public void push(FactorArrayStack stack, CodeVisitor mw, Class type)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
int local = allocate();
|
||||||
|
Result r = new Result(local,this,null,type);
|
||||||
|
stack.push(r);
|
||||||
|
r.push(mw,type);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ pushLiteral() method
|
||||||
|
public void pushLiteral(Object literal, RecursiveState recursiveCheck)
|
||||||
|
{
|
||||||
|
if(literal == null)
|
||||||
|
datastack.push(new Null(this,recursiveCheck));
|
||||||
|
else if(literal instanceof Cons)
|
||||||
|
{
|
||||||
|
datastack.push(new CompiledList((Cons)literal,this,
|
||||||
|
recursiveCheck));
|
||||||
|
}
|
||||||
|
else if(literal instanceof String)
|
||||||
|
{
|
||||||
|
datastack.push(new ConstantPoolString((String)literal,
|
||||||
|
this,recursiveCheck));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
datastack.push(new Literal(literal,this,
|
||||||
|
recursiveCheck));
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ pop() method
|
||||||
|
/**
|
||||||
|
* Generates code for popping the top of the data stack onto
|
||||||
|
* the JVM stack. Also generates code for converting this value to
|
||||||
|
* the given type.
|
||||||
|
*/
|
||||||
|
public void pop(FactorArrayStack stack, CodeVisitor mw, Class type)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
FlowObject obj = (FlowObject)datastack.pop();
|
||||||
|
obj.pop(mw,type);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ popLiteral() method
|
||||||
|
/**
|
||||||
|
* Pops a literal off the datastack or throws an exception.
|
||||||
|
*/
|
||||||
|
public Object popLiteral() throws FactorException
|
||||||
|
{
|
||||||
|
FlowObject obj = (FlowObject)datastack.pop();
|
||||||
|
return obj.getLiteral();
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ allocate() method
|
||||||
|
/**
|
||||||
|
* Allocate a local variable.
|
||||||
|
*/
|
||||||
|
public int allocate()
|
||||||
|
{
|
||||||
|
// inefficient!
|
||||||
|
int i = base;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(allocate(i,datastack) && allocate(i,callstack))
|
||||||
|
return i;
|
||||||
|
else
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ allocate() method
|
||||||
|
/**
|
||||||
|
* Return true if not in use, false if in use.
|
||||||
|
*/
|
||||||
|
private boolean allocate(int local, FactorArrayStack stack)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < stack.top; i++)
|
||||||
|
{
|
||||||
|
FlowObject obj = (FlowObject)stack.stack[i];
|
||||||
|
if(obj.usingLocal(local))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ literal() method
|
||||||
|
public String literal(Object obj)
|
||||||
|
{
|
||||||
|
Integer i = (Integer)literals.get(obj);
|
||||||
|
int literal;
|
||||||
|
if(i == null)
|
||||||
|
{
|
||||||
|
literal = literalCount++;
|
||||||
|
literals.put(obj,new Integer(literal));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
literal = i.intValue();
|
||||||
|
|
||||||
|
return "literal_" + literal;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ auxiliary() method
|
||||||
|
public String auxiliary(FactorWord word, Cons code, StackEffect effect,
|
||||||
|
RecursiveState recursiveCheck) throws Exception
|
||||||
|
{
|
||||||
|
FactorArrayStack savedDatastack = (FactorArrayStack)
|
||||||
|
datastack.clone();
|
||||||
|
FactorCallStack savedCallstack = (FactorCallStack)
|
||||||
|
callstack.clone();
|
||||||
|
|
||||||
|
String method = "aux_" + FactorJava.getSanitizedName(word.name)
|
||||||
|
+ "_" + (auxCount++);
|
||||||
|
|
||||||
|
recursiveCheck.last().method = method;
|
||||||
|
aux = new Cons(new AuxiliaryQuotation(
|
||||||
|
method,savedDatastack,savedCallstack,
|
||||||
|
code,effect,word,this,recursiveCheck),aux);
|
||||||
|
|
||||||
|
return method;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ generateAuxiliary() method
|
||||||
|
public void generateAuxiliary(ClassWriter cw) throws Exception
|
||||||
|
{
|
||||||
|
while(aux != null)
|
||||||
|
{
|
||||||
|
AuxiliaryQuotation q = (AuxiliaryQuotation)aux.car;
|
||||||
|
// order of these two important, in case
|
||||||
|
// compilation of q adds more quotations to aux list
|
||||||
|
aux = aux.next();
|
||||||
|
q.compile(this,cw);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ normalizeStacks() method
|
||||||
|
public void normalizeStacks(CodeVisitor mw)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
int datastackTop = datastack.top;
|
||||||
|
datastack.top = 0;
|
||||||
|
int callstackTop = callstack.top;
|
||||||
|
callstack.top = 0;
|
||||||
|
|
||||||
|
localsToStack(callstack,callstackTop,mw);
|
||||||
|
localsToStack(datastack,datastackTop,mw);
|
||||||
|
stackToLocals(datastack,datastackTop,mw);
|
||||||
|
stackToLocals(callstack,callstackTop,mw);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ localsToStack() method
|
||||||
|
private void localsToStack(FactorArrayStack stack, int top,
|
||||||
|
CodeVisitor mw)
|
||||||
|
{
|
||||||
|
for(int i = top - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
FlowObject obj = (FlowObject)stack.stack[i];
|
||||||
|
obj.pop(mw);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ stackToLocals() method
|
||||||
|
private void stackToLocals(FactorArrayStack stack, int top,
|
||||||
|
CodeVisitor mw) throws Exception
|
||||||
|
{
|
||||||
|
for(int i = 0; i < top; i++)
|
||||||
|
push(stack,mw,Object.class);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ generateArgs() method
|
||||||
|
/**
|
||||||
|
* Generate instructions for copying arguments from the allocated
|
||||||
|
* local variables to the JVM stack, doing type conversion in the
|
||||||
|
* process.
|
||||||
|
*/
|
||||||
|
public void generateArgs(CodeVisitor mw, int inD, int inR, Class[] args)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
for(int i = 0; i < inD; i++)
|
||||||
|
{
|
||||||
|
FlowObject obj = (FlowObject)datastack.stack[
|
||||||
|
datastack.top - inD + i];
|
||||||
|
obj.pop(mw,args == null ? Object.class : args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
datastack.top -= inD;
|
||||||
|
|
||||||
|
for(int i = 0; i < inR; i++)
|
||||||
|
{
|
||||||
|
FlowObject obj = (FlowObject)callstack.stack[
|
||||||
|
callstack.top - inR + i];
|
||||||
|
obj.pop(mw,args == null ? Object.class : args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
callstack.top -= inR;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ generateReturn() method
|
||||||
|
public void generateReturn(CodeVisitor mw, int outD, int outR)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
if(outD == 0 && outR == 0)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
else if(outD == 1 && outR == 0)
|
||||||
|
{
|
||||||
|
push(datastack,mw,Object.class);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// transfer from data stack to JVM locals
|
||||||
|
|
||||||
|
// allocate the appropriate number of locals
|
||||||
|
|
||||||
|
if(outD != 0)
|
||||||
|
{
|
||||||
|
produce(datastack,outD);
|
||||||
|
|
||||||
|
// store the datastack instance somewhere
|
||||||
|
mw.visitVarInsn(ALOAD,0);
|
||||||
|
mw.visitFieldInsn(GETFIELD,
|
||||||
|
"factor/FactorInterpreter",
|
||||||
|
"datastack",
|
||||||
|
"Lfactor/FactorArrayStack;");
|
||||||
|
int datastackLocal = allocate();
|
||||||
|
mw.visitVarInsn(ASTORE,datastackLocal);
|
||||||
|
|
||||||
|
// put all elements from the real datastack
|
||||||
|
// into locals
|
||||||
|
for(int i = 0; i < outD; i++)
|
||||||
|
{
|
||||||
|
mw.visitVarInsn(ALOAD,datastackLocal);
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL,
|
||||||
|
"factor/FactorArrayStack",
|
||||||
|
"pop",
|
||||||
|
"()Ljava/lang/Object;");
|
||||||
|
|
||||||
|
Result destination = (Result)
|
||||||
|
datastack.stack[
|
||||||
|
datastack.top - i - 1];
|
||||||
|
|
||||||
|
destination.push(mw,Object.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(outR != 0)
|
||||||
|
{
|
||||||
|
produce(callstack,outR);
|
||||||
|
|
||||||
|
mw.visitVarInsn(ALOAD,0);
|
||||||
|
mw.visitFieldInsn(GETFIELD,
|
||||||
|
"factor/FactorInterpreter",
|
||||||
|
"callstack",
|
||||||
|
"Lfactor/FactorCallStack;");
|
||||||
|
int callstackLocal = allocate();
|
||||||
|
mw.visitVarInsn(ASTORE,callstackLocal);
|
||||||
|
|
||||||
|
// put all elements from the real callstack
|
||||||
|
// into locals
|
||||||
|
for(int i = 0; i < outR; i++)
|
||||||
|
{
|
||||||
|
mw.visitVarInsn(ALOAD,callstackLocal);
|
||||||
|
mw.visitMethodInsn(INVOKEVIRTUAL,
|
||||||
|
"factor/FactorCallStack",
|
||||||
|
"pop",
|
||||||
|
"()Ljava/lang/Object;");
|
||||||
|
|
||||||
|
Result destination = (Result)
|
||||||
|
callstack.stack[
|
||||||
|
callstack.top - i - 1];
|
||||||
|
|
||||||
|
destination.push(mw,Object.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ generateFields() method
|
||||||
|
public void generateFields(ClassWriter cw)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
for(int i = 0; i < literalCount; i++)
|
||||||
|
{
|
||||||
|
cw.visitField(ACC_PRIVATE | ACC_STATIC,"literal_" + i,
|
||||||
|
"Ljava/lang/Object;",null,null);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeVisitor mw = cw.visitMethod(ACC_PRIVATE | ACC_STATIC,
|
||||||
|
"setFields","(Lfactor/FactorInterpreter;)V",null,null);
|
||||||
|
|
||||||
|
Iterator entries = literals.entrySet().iterator();
|
||||||
|
while(entries.hasNext())
|
||||||
|
{
|
||||||
|
Map.Entry entry = (Map.Entry)entries.next();
|
||||||
|
Object literal = entry.getKey();
|
||||||
|
int index = ((Integer)entry.getValue()).intValue();
|
||||||
|
|
||||||
|
generateParse(mw,literal,0);
|
||||||
|
mw.visitFieldInsn(PUTSTATIC,
|
||||||
|
className,
|
||||||
|
"literal_" + index,
|
||||||
|
"Ljava/lang/Object;");
|
||||||
|
}
|
||||||
|
|
||||||
|
mw.visitInsn(RETURN);
|
||||||
|
|
||||||
|
mw.visitMaxs(0,0);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ generateParse() method
|
||||||
|
public void generateParse(CodeVisitor mw, Object obj, int interpLocal)
|
||||||
|
{
|
||||||
|
mw.visitLdcInsn(FactorReader.getVocabularyDeclaration(obj)
|
||||||
|
+ FactorReader.unparseObject(obj));
|
||||||
|
mw.visitVarInsn(ALOAD,interpLocal);
|
||||||
|
mw.visitMethodInsn(INVOKESTATIC,
|
||||||
|
"factor/FactorReader",
|
||||||
|
"parseObject",
|
||||||
|
"(Ljava/lang/String;Lfactor/FactorInterpreter;)"
|
||||||
|
+ "Ljava/lang/Object;");
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getStackEffectOrNull() method
|
||||||
|
public StackEffect getStackEffectOrNull(FactorWordDefinition def)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return def.getStackEffect(interp,
|
||||||
|
new RecursiveState());
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
//System.err.println("WARNING: " + e);
|
||||||
|
//System.err.println(def);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getStackEffectOrNull() method
|
||||||
|
public StackEffect getStackEffectOrNull(FlowObject obj,
|
||||||
|
RecursiveState recursiveCheck,
|
||||||
|
boolean decompose)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
obj.getStackEffect(recursiveCheck);
|
||||||
|
StackEffect effect = getStackEffect();
|
||||||
|
if(decompose)
|
||||||
|
{
|
||||||
|
effect = StackEffect.decompose(
|
||||||
|
recursiveCheck.last().effect,
|
||||||
|
effect);
|
||||||
|
}
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
//System.err.println("WARNING: " + e);
|
||||||
|
//System.err.println(obj);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
}
|
|
@ -173,11 +173,25 @@ implements Constants, FactorExternalizable, PublicCloneable
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mw.visitMethodInsn(INVOKESTATIC,
|
String signature;
|
||||||
"factor/FactorJava",
|
if(type.isArray())
|
||||||
methodName,
|
{
|
||||||
"(Ljava/lang/Object;)"
|
signature = "(Ljava/lang/Object;)"
|
||||||
+ FactorJava.javaClassToVMClass(type));
|
+ "[Ljava/lang/Object;";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
signature = "(Ljava/lang/Object;)"
|
||||||
|
+ FactorJava.javaClassToVMClass(type);
|
||||||
|
}
|
||||||
|
mw.visitMethodInsn(INVOKESTATIC,"factor/FactorJava",
|
||||||
|
methodName,signature);
|
||||||
|
/* if(type.isArray())
|
||||||
|
{
|
||||||
|
mw.visitTypeInsn(CHECKCAST,
|
||||||
|
type.getName()
|
||||||
|
.replace('.','/'));
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
/* :folding=explicit:collapseFolds=1: */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2004 Slava Pestov.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package factor.jedit;
|
||||||
|
|
||||||
|
import factor.*;
|
||||||
|
import javax.swing.border.*;
|
||||||
|
import javax.swing.event.*;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.List;
|
||||||
|
import org.gjt.sp.jedit.gui.EnhancedDialog;
|
||||||
|
import org.gjt.sp.jedit.*;
|
||||||
|
|
||||||
|
public class EditWordDialog extends WordListDialog
|
||||||
|
{
|
||||||
|
private JTextField field;
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
|
//{{{ EditWordDialog constructor
|
||||||
|
public EditWordDialog(View view, FactorSideKickParser parser)
|
||||||
|
{
|
||||||
|
super(view,parser,jEdit.getProperty("factor.edit-word.title"));
|
||||||
|
|
||||||
|
Box top = new Box(BoxLayout.X_AXIS);
|
||||||
|
top.add(new JLabel(jEdit.getProperty(
|
||||||
|
"factor.edit-word.caption")));
|
||||||
|
top.add(Box.createHorizontalStrut(12));
|
||||||
|
top.add(field = new JTextField(16));
|
||||||
|
field.getDocument().addDocumentListener(new DocumentHandler());
|
||||||
|
field.addKeyListener(new KeyHandler());
|
||||||
|
getContentPane().add(BorderLayout.NORTH,top);
|
||||||
|
|
||||||
|
list.setFixedCellHeight(list.getFontMetrics(list.getFont())
|
||||||
|
.getHeight());
|
||||||
|
list.addKeyListener(new KeyHandler());
|
||||||
|
|
||||||
|
timer = new Timer(0,new UpdateTimer());
|
||||||
|
|
||||||
|
pack();
|
||||||
|
setLocationRelativeTo(view);
|
||||||
|
setVisible(true);
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ ok() method
|
||||||
|
public void ok()
|
||||||
|
{
|
||||||
|
FactorWord word = (FactorWord)list.getSelectedValue();
|
||||||
|
if(word == null)
|
||||||
|
{
|
||||||
|
getToolkit().beep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String code = FactorPlugin.factorWord(word);
|
||||||
|
FactorPlugin.eval(view,code + " jedit");
|
||||||
|
dispose();
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ cancel() method
|
||||||
|
public void cancel()
|
||||||
|
{
|
||||||
|
dispose();
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ updateListWithDelay() method
|
||||||
|
private void updateListWithDelay()
|
||||||
|
{
|
||||||
|
timer.stop();
|
||||||
|
String text = field.getText();
|
||||||
|
if(text.length() <= 1)
|
||||||
|
list.setListData(new Object[0]);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timer.setInitialDelay(100);
|
||||||
|
timer.setRepeats(false);
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ updateList() method
|
||||||
|
private void updateList()
|
||||||
|
{
|
||||||
|
List completions = FactorPlugin.getCompletions(
|
||||||
|
field.getText(),true);
|
||||||
|
FactorWord[] completionArray
|
||||||
|
= (FactorWord[])completions.toArray(
|
||||||
|
new FactorWord[completions.size()]);
|
||||||
|
list.setListData(completionArray);
|
||||||
|
if(completionArray.length != 0)
|
||||||
|
{
|
||||||
|
list.setSelectedIndex(0);
|
||||||
|
list.ensureIndexIsVisible(0);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ UpdateTimer class
|
||||||
|
class UpdateTimer implements ActionListener
|
||||||
|
{
|
||||||
|
public void actionPerformed(ActionEvent evt)
|
||||||
|
{
|
||||||
|
updateList();
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ KeyHandler class
|
||||||
|
class KeyHandler extends KeyAdapter
|
||||||
|
{
|
||||||
|
public void keyPressed(KeyEvent evt)
|
||||||
|
{
|
||||||
|
switch(evt.getKeyCode())
|
||||||
|
{
|
||||||
|
case KeyEvent.VK_UP:
|
||||||
|
int selected = list.getSelectedIndex();
|
||||||
|
|
||||||
|
if(selected == 0)
|
||||||
|
selected = list.getModel().getSize() - 1;
|
||||||
|
else if(getFocusOwner() == list)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
selected = selected - 1;
|
||||||
|
|
||||||
|
list.setSelectedIndex(selected);
|
||||||
|
list.ensureIndexIsVisible(selected);
|
||||||
|
|
||||||
|
evt.consume();
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_DOWN:
|
||||||
|
/* int */ selected = list.getSelectedIndex();
|
||||||
|
|
||||||
|
if(selected == list.getModel().getSize() - 1)
|
||||||
|
selected = 0;
|
||||||
|
else if(getFocusOwner() == list)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
selected = selected + 1;
|
||||||
|
|
||||||
|
list.setSelectedIndex(selected);
|
||||||
|
list.ensureIndexIsVisible(selected);
|
||||||
|
|
||||||
|
evt.consume();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ DocumentHandler class
|
||||||
|
class DocumentHandler implements DocumentListener
|
||||||
|
{
|
||||||
|
public void insertUpdate(DocumentEvent evt)
|
||||||
|
{
|
||||||
|
updateListWithDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeUpdate(DocumentEvent evt)
|
||||||
|
{
|
||||||
|
updateListWithDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changedUpdate(DocumentEvent evt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
}
|