readline scribblings; set GC alignment to 8-byte by default

svn: r8320
This commit is contained in:
Matthew Flatt 2008-01-14 13:06:53 +00:00
parent 707416a13f
commit 62f38e2a40
5 changed files with 257 additions and 160 deletions

View File

@ -1,140 +0,0 @@
The _readline_ collection (not to be confused with MzScheme's
`read-line' procedure) provides glue for using GNU's readline library
with the MzScheme read-eval-print-loop.
Normal use of readline
----------------------
The _rep.ss_ library installs a readline-based input port, and hooks
the prompt-and-read part of MzScheme's read-eval-print loop to
interact with it.
You can start MzScheme with
mzscheme -L rep.ss readline
or you can put the following in your ~/.mzschemerc so that MzScheme
starts with readline support when appropriate:
(when (regexp-match? #rx"xterm" (getenv "TERM"))
(dynamic-require '(lib "rep.ss" "readline") #f))
The "rep.ss" module is actually a wrapper around "rep-start.ss", it
will *not* invoke it if the input port is not a terminal port (eg,
when the input is redirected from a file). Still the TERM condition
above is useful for starting MzScheme in dumb terminals, eg, inside
Emacs.
Completion is set to use the visible bindings in the current
namespace; this is far from ideal, but it's better than readline's
default filename completion which is rarely useful. In addition, the
readline history is stored across invocations in MzScheme's
preferences file, assuming MzScheme exits normally.
Interacting with the readline-enabled input port
------------------------------------------------
The _pread.ss_ library provides customization, and support for
prompt-reading after "rep.ss" installs the new input port.
The reading facility that the new input port provides can be
customized with these parameters:
> currnet-prompt
The prompt that is used, as a byte string. Defaults to #"> ".
> show-all-prompts
If #f, no prompt is shown until you write input that is completely
readable. For example, when you type
(foo bar) (+ 1
2)
you will see a single prompt in the beginning.
The problem is that the first expression can be `(read-line)' which
normally consumes the rest of the text on the *same* line. The
default value of this parameter is therefore #t, making it mimic
plain I/O interactions.
> max-history
The number of history entries to save. Defaults to 100.
> keep-duplicates
If this is #f (the default), then lines that are equal to the
previous one are not added as new history items.
> keep-blanks
If #f (the default), blank input lines are not kept in history.
The new input port that you get when you require "rep.ss" is a custom
port that uses readline for all inputs. The problem is when you want
to display a prompt and then read some input: readline will get
confused if it's not used when the cursor is at the beginning of the
line, which is why it has a `prompt' argument. To use this prompt:
(parameterize ([readline-prompt some-byte-string])
...code-that-reads...)
This will make the first call to readline use the prompt, and
subsequent calls will use an all-spaces prompt of the same length (for
example, when you're reading an s-expression). The normal value of
`readline-prompt' is #f for an empty prompt (and 'spaces after the
prompt is used, which is why you should use `parameterize' to restore
it to #f).
A proper solution would be to install a custom output port too which
keeps track of text that is displayed without a trailing newline. As
a cheaper solution, if line-counting is enabled for the terminal's
output-port, then a newline is printed before reading if the column is
not 0. ("rep.ss" enables line-counting for the output port.)
Warning
-------
The readline library uses the output port directly. You should not
use it when `current-input-port' has been modified, or when it was not
a terminal port when MzScheme was started (eg, when reading input from
a pipe). Expect problems if you ignore this warning (not too bad,
mostly problems with detecting an EOF).
Direct bindings for readline hackers
------------------------------------
The _readline.ss_ library provides these functions:
> (readline prompt-string)
prints the given prompt string and reads a line
> (readline-bytes prompt-bytes)
same as above, but using raw byte-strings for the prompt and
returning a byte string
> (add-history s)
adds the given string to the readline history, which is accessible
to the user via the up-arrow key
> (add-history-bytes s)
adds the given byte string to the readline history, which is
accessible to the user via the up-arrow key
> (set-completion-function! proc [type])
sets readline's `rl_completion_entry_function' function according to
proc, which is expected to be a `string -> (list-of string/bytes)'
procedure; the `type' argument defaults to `_string' but you can use
it with `_bytes' instead to have your function receive a byte
string.
License Issues
--------------
GNU's readline library is covered by the GPL, and that applies to code
that links with it. PLT Scheme is LGPL, so this code is not used by
default -- you should explicitly enable it if you want to. Also, be
aware that if you write code that uses this library, it will make your
code link to the readline library when invoked -- with the usual GPL
implications.

View File

@ -2,6 +2,7 @@
(module info setup/infotab
(define doc.txt "doc.txt")
(define name "readline")
(define scribblings '(("readline.scrbl")))
(define blurb
`("The readline collection provides glue for using GNU's readline library"
" with the MzScheme read-eval-print-loop.")))

View File

@ -0,0 +1,185 @@
#lang scribble/doc
@(require scribble/manual
(for-label scheme/base
readline/pread
readline/readline))
@(define readline "Readline")
@(define Readline "Readline")
@title{@bold{Readline}: Terminal Interaction}
The @filepath{readline} collection (not to be confused with MzScheme's
@scheme[read-line] function) provides glue for using GNU's @|readline|
library with the MzScheme @scheme[read-eval-print-loop].
@section{Normal Use of @|Readline|}
@defmodule*[(readline/rep readline/rep-start)]
The @schememodname[readline/rep] library installs a @|readline|-based
input port, and hooks the prompt-and-read part of MzScheme's
@scheme[read-eval-print-loop] to interact with it
You can start MzScheme with
@commandline{mzscheme -il readline/rep}
or you can put the following in your @filepath{~/.mzschemerc} so that
MzScheme starts with @|readline| support when appropriate:
@schemeblock[
(when (regexp-match? #rx"xterm"
(getenv "TERM"))
(dynamic-require 'readline/rep #f))
]
The @schememodname[readline/rep] module is actually a wrapper around
@schememodname[readline/rep-start]; it will @emph{not} invoke
@schememodname[readline/rep-start] if the input port is not a terminal
port (e.g., when the input is redirected from a file); see
@scheme[terminal-port?]. Still, the @envvar{TERM} condition
above is useful for starting MzScheme in dumb terminals (e.g., inside
Emacs.)
Completion is set to use the visible bindings in the current
namespace; this is far from ideal, but it's better than @|readline|'s
default filename completion which is rarely useful. In addition, the
@|readline| history is stored across invocations in MzScheme's
preferences file, assuming that MzScheme exits normally.
@section{Interacting with the @|Readline|-Enabled Input Port }
@defmodule[readline/pread]{ The @schememodname[readline/pread] library
provides customization, and support for prompt-reading after
@schememodname[readline/rep] installs the new input port.}
The reading facility that the new input port provides can be
customized with the following parameters.
@defparam[current-prompt bstr bytes?]{
A parameter that determines the prompt that is used, as a byte string.
Defaults to @scheme[#"> "].}
@defboolparam[show-all-prompts on?]{
A parameter. If @scheme[#f], no prompt is shown until you write input
that is completely readable. For example, when you type
@schemeblock[
(foo bar) (+ 1
2)
]
you will see a single prompt in the beginning.
The problem is that the first expression can be @scheme[(read-line)],
which normally consumes the rest of the text on the @emph{same} line.
The default value of this parameter is therefore @scheme[#t], making
it mimic plain I/O interactions.}
@defparam[max-history n nonnegative-exact-integer?]{
A parameter that determines the number of history entries to save,
defaults to @scheme[100].}
@defboolparam[keep-duplicates keep?]{
A parameter. If @scheme[#f] (the default), then lines that are equal
to the previous one are not added as new history items.}
@defboolparam[keep-blanks keep?]{
A parameter. If @scheme[#f] (the default), blank input lines are not
kept in history.}
@defparam[readline-prompt status (or/c false/c bytes? (one-of/c 'space))]{
The new input port that you get when you require
@schememodname[readline/rep] is a custom port that uses @|readline| for
all inputs. The problem is when you want to display a prompt and then
read some input, @|readline| will get confused if it is not used when the
cursor is at the beginning of the line (which is why it has a
@scheme[_prompt] argument.) To use this prompt:
@schemeblock[
(parameterize ([readline-prompt some-byte-string])
...code-that-reads...)
]
This expression makes the first call to @|readline| use the prompt, and
subsequent calls will use an all-spaces prompt of the same length (for
example, when you're reading an S-expression). The normal value of
@scheme[readline-prompt] is @scheme[#f] for an empty prompt (and
spaces after the prompt is used, which is why you should use
@scheme[parameterize] to restore it to @scheme[#f]).
A proper solution would be to install a custom output port, too, which
keeps track of text that is displayed without a trailing newline. As
a cheaper solution, if line-counting is enabled for the terminal's
output-port, then a newline is printed before reading if the column is
not 0. (The @schememodname[readline/rep] library enables line-counting
for the output port.)
@bold{Warning:} The @|readline| library uses the output port directly.
You should not use it when @scheme[current-input-port] has been
modified, or when it was not a terminal port when MzScheme was started
(eg, when reading input from a pipe). Expect some problems if you
ignore this warning (not too bad, mostly problems with detecting an
EOF).}
@section{Direct Bindings for @|Readline| Hackers}
@defmodule[readline/readline]
@defproc[(readline [prompt string?]) string?]{
Prints the given prompt string and reads a line.}
@defproc[(readline-bytes [prompt bytes?]) bytes?]{
Like @scheme[readline], but using raw byte-strings for the prompt and
returning a byte string.}
@defproc[(add-history [str string?]) void?]{
Adds the given string to the @|readline| history, which is accessible to
the user via the up-arrow key.}
@defproc[(add-history-bytes [str string?]) void?]{
Adds the given byte string to the @|readline| history, which is
accessible to the user via the up-arrow key.}
@defproc[(set-completion-function! [proc ((or/c string? bytes?)
. -> . (listof (or/c string? bytes?)))]
[type (one-of/c 'string 'bytes) 'string])
void?]{
Sets @|readline|'s @tt{rl_completion_entry_function} to
@scheme[proc]. The @scheme[type] argument determines the type of value
upplied to the @scheme[proc].}
@section{License Issues}
GNU's @|readline| library is covered by the GPL, and that applies to code
that links with it. PLT Scheme is LGPL, so this code is not used by
default; you should explicitly enable it if you want to. Also, be
aware that if you write code that uses this library, it will make your
code link to the @|readline| library when invoked, with the usual GPL
implications.

View File

@ -57,7 +57,11 @@ by @scheme[kind], which must be one of the following:
@item{@indexed-scheme['temp-dir] --- the standard directory for
storing temporary files. Under @|AllUnix|, this is the directory
specified by the @indexed-envvar{TMPDIR} environment variable, if it
is defined.}
is defined, otherwise it is the first path that exists among
@filepath{/var/tmp}, @filepath{/usr/tmp}, and @filepath{/tmp}. Under
Windows, the result is the directory specified by the
@indexed-envvar{TMP} or @indexed-envvar{TEMP} environment variable,
if it is defined, otherwise it is the current directory.}
@item{@indexed-scheme['init-dir] --- the directory containing the
initialization file used by stand-alone @exec{mzscheme} executable.

View File

@ -42,7 +42,21 @@
#endif
#if defined(sparc) || defined(__sparc) || defined(__sparc__)
# define ALIGN_DOUBLES
/* Required for `double' operations: */
# define GC_ALIGN_EIGHT
#endif
/* Even when 8-byte alginment is not required by the processor, it's
better for floating-point performance (PowerPC) and may be required
for some libraries (VecLib in Mac OS X, including x86).
Under Windows, Mac OS X, and Linux x86_64, malloc() returns 16-byte
aligned data. And, actually, VecLib says that it requires
16-byte-aligned data. So, in those cases, GC_ALIGN_SIXTEEN might be
better --- but that's a lot more expensive, increasing DrScheme's
initial footprint by almost 10%. */
#ifndef GC_ALIGN_EIGHT
# define GC_ALIGN_EIGHT
#endif
#include "msgprint.c"
@ -229,14 +243,24 @@ struct mpage {
void **backtrace;
};
#ifdef ALIGN_DOUBLES
/* Make sure alloction starts out double-word aligned: */
# define PREFIX_SIZE WORD_SIZE
# define PREFIX_WSIZE 1
/* Make sure alloction starts out double-word aligned.
The header on each allocated object is one word, so to make
the content double-word aligned, we deeper. */
#ifdef GC_ALIGN_SIXTEEN
# ifdef SIXTY_FOUR_BIT_INTEGERS
# define PREFIX_WSIZE 1
# else
# define PREFIX_WSIZE 3
# endif
#else
# define PREFIX_SIZE 0
# define PREFIX_WSIZE 0
# if defined(GC_ALIGN_EIGHT) && !defined(SIXTY_FOUR_BIT_INTEGERS)
# define PREFIX_WSIZE 1
# else
# define PREFIX_WSIZE 0
# endif
#endif
#define PREFIX_SIZE (PREFIX_WSIZE * WORD_SIZE)
/* this is the maximum size of an object that will fit on a page, in words.
the "- 3" is basically used as a fudge/safety factor, and has no real,
@ -441,12 +465,35 @@ static void *allocate_big(size_t sizeb, int type)
return PTR(NUM(addr) + PREFIX_SIZE + WORD_SIZE);
}
#ifdef ALIGN_DOUBLES
# define ALIGN_SIZE(sizew) (((sizew) & 0x1) ? ((sizew) + 1) : (sizew))
# define ALIGN_BYTES_SIZE(sizeb) (((sizeb) & WORD_SIZE) ? ((sizeb) + WORD_SIZE) : (sizeb))
/* ALIGN_BYTES_SIZE can assume that the argument is already word-aligned. */
/* INSET_WORDS is how many words in a tagged array can be padding, plus one; it
must also be no more than the minimum size of a tagged element. */
#ifdef GC_ALIGN_SIXTEEN
# ifdef SIXTY_FOUR_BIT_INTEGERS
# define ALIGN_SIZE(sizew) (((sizew) & 0x1) ? ((sizew) + 1) : (sizew))
# define ALIGN_BYTES_SIZE(sizeb) (((sizeb) & WORD_SIZE) ? ((sizeb) + WORD_SIZE) : (sizeb))
# define INSET_WORDS 1
# else
# define ALIGN_SIZE(sizew) (((sizew) & 0x3) ? ((sizew) + (4 - ((sizew) & 0x3))) : (sizew))
# define ALIGN_BYTES_SIZE(sizeb) (((sizeb) & (3 * WORD_SIZE)) ? ((sizeb) + ((4 * WORD_SIZE) - ((sizeb) & (3 * WORD_SIZE)))) : (sizeb))
# define INSET_WORDS 3
# endif
#else
# define ALIGN_SIZE(sizew) (sizew)
# define ALIGN_BYTES_SIZE(sizeb) (sizeb)
# ifdef GC_ALIGN_EIGHT
# ifdef SIXTY_FOUR_BIT_INTEGERS
# define ALIGN_SIZE(sizew) (sizew)
# define ALIGN_BYTES_SIZE(sizeb) (sizeb)
# define INSET_WORDS 0
# else
# define ALIGN_SIZE(sizew) (((sizew) & 0x1) ? ((sizew) + 1) : (sizew))
# define ALIGN_BYTES_SIZE(sizeb) (((sizeb) & WORD_SIZE) ? ((sizeb) + WORD_SIZE) : (sizeb))
# define INSET_WORDS 1
# endif
# else
# define ALIGN_SIZE(sizew) (sizew)
# define ALIGN_BYTES_SIZE(sizeb) (sizeb)
# define INSET_WORDS 0
# endif
#endif
inline static void *allocate(size_t sizeb, int type)
@ -901,7 +948,7 @@ static void *get_backtrace(struct mpage *page, void *ptr)
unsigned long delta;
if (page->big_page)
ptr = PTR(page->addr + PREFIX_SIZE);
ptr = PTR(page->addr + PREFIX_SIZE + WORD_SIZE);
delta = PPTR(ptr) - PPTR(page->addr);
return page->backtrace[delta - 1];
@ -1690,7 +1737,7 @@ inline static void mark_normal_obj(struct mpage *page, void *ptr)
case PAGE_TARRAY: {
struct objhead *info = (struct objhead *)((char*)ptr - WORD_SIZE);
unsigned short tag = *(unsigned short*)ptr;
void **temp = ptr, **end = PPTR(info) + (info->size - 1);
void **temp = ptr, **end = PPTR(info) + (info->size - INSET_WORDS);
while(temp < end) temp += mark_table[tag](temp);
break;
@ -1718,7 +1765,7 @@ inline static void mark_acc_big_page(struct mpage *page)
case PAGE_XTAGGED: GC_mark_xtagged(start); break;
case PAGE_TARRAY: {
unsigned short tag = *(unsigned short *)start;
end -= 1;
end -= INSET_WORDS;
while(start < end) start += mark_table[tag](start);
break;
}
@ -2295,7 +2342,7 @@ inline static void internal_mark(void *p)
case PAGE_XTAGGED: GC_mark_xtagged(start); break;
case PAGE_TARRAY: {
unsigned short tag = *(unsigned short *)start;
end -= 1;
end -= INSET_WORDS;
while(start < end) start += mark_table[tag](start);
break;
}
@ -2316,7 +2363,7 @@ inline static void internal_mark(void *p)
}
case PAGE_TARRAY: {
void **start = p;
void **end = PPTR(info) + (info->size - 1);
void **end = PPTR(info) + (info->size - INSET_WORDS);
unsigned short tag = *(unsigned short *)start;
while(start < end) start += mark_table[tag](start);
break;
@ -2798,7 +2845,7 @@ static void repair_heap(void)
break;
case PAGE_TARRAY: {
unsigned short tag = *(unsigned short *)start;
end -= 1;
end -= INSET_WORDS;
while(start < end) start += fixup_table[tag](start);
break;
}
@ -2851,7 +2898,7 @@ static void repair_heap(void)
struct objhead *info = (struct objhead *)start;
size_t size = info->size;
if(info->mark) {
void **tempend = (start++) + (size - 1);
void **tempend = (start++) + (size - INSET_WORDS);
unsigned short tag = *(unsigned short*)start;
while(start < tempend)
start += fixup_table[tag](start);