no longer try using the same port for reading and writing

cvs
Slava Pestov 2004-08-16 23:29:07 +00:00
parent 7d583b43d1
commit 6165c935d3
40 changed files with 157 additions and 107 deletions

View File

@ -1,13 +1,11 @@
0.62:
- stdout hangs
- read-line: handle \r\n
- flush output buffer before reading line
- print: only flush in stdio stream
- ignore errors from flush
- can-read-line?
- EOF in client connections
- vector-each/map examples
- unparse examples
- finish second practical
- sbuf-hashcode
- vector-hashcode
- listener backspace overzealous
@ -52,7 +50,6 @@
- read#
- to_fixnum and friends: error on float
ERROR: I/O error: [ "primitive_read_line_fd_8" "Resource temporarily unavailable" ]
- parsing should be parsing
- describe-word
- contains ==> contains?

View File

@ -1167,7 +1167,7 @@ Vectors are applicable to a different class of problems than lists.
Compare the relative performance of common operations on vectors and
lists:
\begin{tabular}{|c|c|c|}
\begin{tabular}{|r|l|l|}
\hline
&
Lists&
@ -1298,7 +1298,7 @@ can write:
Other special characters, such as quotes and tabs can be input in
a similar manner. Here is the full list of supported character escapes:
\begin{tabular}{|c|c|}
\begin{tabular}{|r|l|}
\hline
Character&
Escape\tabularnewline
@ -1773,6 +1773,25 @@ Reading a number, showing a menu
\subsection{The name stack}
So far, we have seen what we called ``the stack'' store intermediate values between computations. In fact Factor maintains a number of other stacks, and the formal name for the stack we've been dealing with so far is the \emph{data stack}.
Another stack is the \emph{call stack}. When a colon definition is invoked, the position within the current colon definition is pushed on the stack. This ensures that calling words return to the caller, just as in any other language with subroutines.\footnote{Factor supports a variety of structures for implementing non-local word exits, such as exceptions, co-routines, continuations, and so on. They all rely on manipulating the call stack and are described in later sections.}
The \emph{name stack} is the focus of this section. The \texttt{bind} combinator creates dynamic scope by pushing and popping namespaces on the name stack. Its definition is simpler than one would expect:
\begin{alltt}
: bind ( namespace quot -- )
swap >n call n> drop ;
\end{alltt}
The words \texttt{>n} and \texttt{n>} push and pop the name stack, respectively. Observe the stack flow in the definition of \texttt{bind}; the namespace goes on the name stack, the quotation is called, and the name space is popped and discarded.
The name stack is really just a vector. The words \texttt{>n} and \texttt{n>} are implemented as follows:
\begin{alltt}
: >n ( namespace -- n:namespace ) namestack* vector-push ;
: n> ( n:namespace -- namespace ) namestack* vector-pop ;
\end{alltt}
\subsection{The inspector}
@ -1780,15 +1799,75 @@ Reading a number, showing a menu
\section{PRACTICAL: Music player}
\section{Deeper in the beast}
\section{Metaprogramming}
Text -> objects - parser, objects -> text - unparser for atoms, prettyprinter
for collections.
Recall that code quotations are in fact just linked lists. Factor code is data, and vice versa. Essentially, the interpreter iterates through code quotations, pushing literals and executing words. When a word is executed, one of two things happen -- either the word has a colon definition, and the interpreter is invoked recursively on the definition, or the word is primitive, and it is executed by the underlying virtual machine. A word is itself a first-class object.
What really is a word -- primitive, parameter, property list.
It is the job of the parser to transform source code denoting literals and words into their internal representations. This is done using a vocabulary of \emph{parsing words}. The prettyprinter does the converse, by printing out data structures in a parsable form (both to humans and Factor). Because code is data, text representation of source code doubles as a way to serialize almost any Factor object.
Call stack how it works and >r/r>
\subsection{Looking at words}
Try pushing a list of words on the stack, and take its first element:
\begin{alltt}
{[} * + {]} car .s
\emph{\{ * \}}
\end{alltt}
What happened here? Instead of being executed, a ``naked'', unquoted word was pushed on the stack. The predicate \texttt{word? ( obj -{}- ? )} from the \texttt{words} vocabulary tests if the top of the stack is a word. Another way to get a word on the stack is to do a vocabulary search using a word name and a list of vocabularies to search in:
\begin{alltt}
"car" {[} "lists" {]} search .s
\emph{\{ car \}}
\end{alltt}
The \texttt{search} word will push \texttt{f} if the word is not defined. A new word can be created in a specified vocabulary explicitly:
\begin{alltt}
"start-server" "user" create .s
\emph{\{ start-server \}}
\end{alltt}
Two words are only ever equal under the \texttt{=} operator if they identify the same underlying object. Word objects are composed of three slots, named as follows.
\begin{tabular}{|r|l|}
\hline
Slot&
Description\tabularnewline
\hline
\hline
Primitive&
A number identifying a virtual machine operation.\tabularnewline
\hline
Parameter&
An object parameter for the virtual machine operation.\tabularnewline
\hline
Property list&
An association list of name/value pairs.\tabularnewline
\hline
\end{tabular}
If the primitive number is set to 1, the word is a colon definition and the parameter must be a quotation. Any other primitive number denotes a function of the virtual machine, and the parameter is ignored. Do not rely on primitive numbers in your code, instead use the \texttt{compound? ( obj -{}- ? )} and \texttt{primitive? ( obj -{}- ? )} predicates.
The word \texttt{define ( word quot -{}- )} defines a word to have the specified colon definition. Note that \texttt{create} and \texttt{define} perform an action somewhat analagous to the \texttt{: ... ;} notation for colon definitions, except at parse time rather than run time.
\subsection{The prettyprinter}
We've already seen the word \texttt{.} which prints the top of the stack in a form that may be read back in. The word \texttt{prettyprint} is similar, except the output is in an indented, multiple-line format. Both words are in the \texttt{prettyprint} vocabulary. Here is an example:
\begin{alltt}
{[} 1 {[} 2 3 4 {]} 5 {]} .
\emph{{[} 1 {[} 2 3 4 {]} 5 {]}}
{[} 1 {[} 2 3 4 {]} 5 {]} prettyprint
\emph{{[}
1 {[}
2 3 4
{]} 5
{]}}
\end{alltt}
\subsection{The parser}
\subsection{Parsing words}
@ -1864,6 +1943,8 @@ types of objects. Strings are covered in great detail later.
\section{Continuations}
Call stack how it works and >r/r>
Generators, co-routines, multitasking, exception handling

View File

@ -38,7 +38,6 @@ USE: streams
USE: strings
USE: vectors
USE: vectors
USE: vocabularies
USE: words
IN: arithmetic

View File

@ -42,7 +42,6 @@ USE: streams
USE: strings
USE: test
USE: vectors
USE: vocabularies
USE: unparser
USE: words

View File

@ -42,7 +42,6 @@ USE: stdio
USE: streams
USE: strings
USE: words
USE: vocabularies
! This file is run as the last stage of boot.factor; it relies
! on all other words already being defined.

View File

@ -25,7 +25,7 @@
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
IN: vocabularies
IN: words
USE: combinators
USE: inspector
USE: lists
@ -36,7 +36,6 @@ USE: stack
USE: stdio
USE: strings
USE: unparser
USE: words
: word-uses? ( of in -- ? )
2dup = [

View File

@ -40,7 +40,6 @@ USE: words
USE: prettyprint
USE: unparser
USE: vectors
USE: vocabularies
: relative>absolute-object-path ( string -- string )
"object-path" get [ "'" rot cat3 ] when* ;

View File

@ -31,7 +31,7 @@ USE: combinators
USE: namespaces
USE: stack
USE: strings
USE: vocabularies
USE: words
: view ( -- view )
[ ] "org.gjt.sp.jedit.jEdit"

View File

@ -32,7 +32,6 @@ USE: namespaces
USE: stack
USE: stdio
USE: words
USE: vocabularies
: class-name ( class -- name )
[ ] "java.lang.Class" "getName" jinvoke ;

View File

@ -36,7 +36,6 @@ USE: streams
USE: strings
USE: vectors
USE: vectors
USE: vocabularies
USE: words
: worddef, ( word -- )

View File

@ -33,7 +33,6 @@ USE: prettyprint
USE: stack
USE: stdio
USE: unparser
USE: vocabularies
USE: words
: prettyprint-~<< ( indent -- indent )

View File

@ -25,7 +25,7 @@
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
IN: vocabularies
IN: words
USE: combinators
USE: kernel
USE: lists

View File

@ -32,7 +32,6 @@ USE: lists
USE: logic
USE: namespaces
USE: stack
USE: vocabularies
: worddef? ( obj -- boolean )
"factor.FactorWordDefinition" is ;

View File

@ -48,7 +48,6 @@ USE: streams
USE: strings
USE: styles
USE: vectors
USE: vocabularies
USE: words
USE: unparser

View File

@ -60,6 +60,6 @@ USE: strings
: wait-to-accept ( socket -- )
[ swap add-accept-io-task next-io-task drop ( call ) ] callcc0 ;
: blocking-accept ( socket -- host port socket )
: blocking-accept ( socket -- host port in out )
dup wait-to-accept accept-fd ;

View File

@ -36,7 +36,6 @@ USE: namespaces
USE: stack
USE: strings
USE: words
USE: vocabularies
USE: unparser
! Number parsing

View File

@ -42,7 +42,6 @@ USE: stack
USE: strings
USE: words
USE: vectors
USE: vocabularies
USE: unparser
! Constants

View File

@ -36,7 +36,6 @@ USE: namespaces
USE: stack
USE: strings
USE: words
USE: vocabularies
USE: unparser
! The parser uses a number of variables:

View File

@ -32,7 +32,6 @@ USE: prettyprint
USE: stack
USE: stdio
USE: unparser
USE: vocabularies
USE: words
: see-compound ( word -- )

View File

@ -61,10 +61,10 @@ USE: namespaces
] extend ;
: <filecr> ( path -- stream )
t f open-file f <fd-stream> ;
t f open-file <fd-stream> ;
: <filecw> ( path -- stream )
f t open-file f swap <fd-stream> ;
f t open-file <fd-stream> ;
: <filebr> ( path -- stream )
<filecr> ;
@ -83,8 +83,8 @@ USE: namespaces
[ "socket" get close-fd ] "fclose" set
] extend ;
: <client-stream> ( host port socket -- stream )
dup <fd-stream> [ "port" set "client" set ] extend ;
: <client-stream> ( host port in out -- stream )
<fd-stream> [ "port" set "client" set ] extend ;
: accept ( server -- client )
#! Accept a connection from a server socket.

View File

@ -38,7 +38,6 @@ USE: stack
USE: stdio
USE: strings
USE: words
USE: vocabularies
: integer% ( num -- )
"base" get /mod swap dup 0 > [

View File

@ -25,11 +25,10 @@
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
IN: vocabularies
IN: words
USE: lists
USE: namespaces
USE: stack
USE: words
: (search) ( name vocab -- word )
vocab dup [ get* ] [ 2drop f ] ifte ;

View File

@ -32,7 +32,6 @@ USE: lists
USE: logic
USE: namespaces
USE: stack
USE: vocabularies
: word-property ( pname word -- pvalue )
word-plist assoc ;

View File

@ -41,7 +41,6 @@ USE: strings
USE: styles
USE: unparser
USE: vectors
USE: vocabularies
USE: words
: tab-size

View File

@ -35,7 +35,16 @@ USE: streams
: <stdio-stream> ( stream -- stream )
#! We disable fclose on stdio so that various tricks like
#! with-stream can work.
clone [ [ ] "fclose" set ] extend ;
clone [
( string -- )
[
namespace fwrite
"\n" namespace fwrite
namespace fflush
] "fprint" set
[ ] "fclose" set
] extend ;
: flush ( -- )
"stdio" get fflush ;

View File

@ -81,7 +81,6 @@ USE: strings
[
namespace fwrite
"\n" namespace fwrite
namespace fflush
] "fprint" set
] extend ;

View File

@ -1,7 +1,7 @@
IN: scratchpad
USE: inspector
USE: namespaces
USE: vocabularies
USE: words
"httpd" apropos.
"car" usages.

View File

@ -12,7 +12,6 @@ USE: stack
USE: stdio
USE: strings
USE: test
USE: vocabularies
USE: words
"Checking dictionary words." print

View File

@ -6,7 +6,6 @@ USE: namespaces
USE: stack
USE: test
USE: words
USE: vocabularies
[ [ 1 0 0 0 ] ] [ [ >n ] ] [ balance>list ] test-word
[ [ 1 1 0 0 ] ] [ [ get ] ] [ balance>list ] test-word

View File

@ -4,7 +4,6 @@ USE: namespaces
USE: test
USE: stack
USE: words
USE: vocabularies
<namespace> "test-namespace" set

View File

@ -2,6 +2,6 @@ IN: scratchpad
USE: lists
USE: prettyprint
USE: test
USE: vocabularies
USE: words
[ vocabs [ words [ see ] each ] each ] time

View File

@ -17,7 +17,6 @@ USE: stdio
USE: strings
USE: words
USE: unparser
USE: vocabularies
: assert ( t -- )
[ "Assertion failed!" throw ] unless ;

View File

@ -25,7 +25,7 @@
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
IN: vocabularies
IN: words
USE: combinators
USE: kernel
USE: lists
@ -57,7 +57,8 @@ USE: strings
: words ( vocab -- list )
#! Push a list of all words in a vocabulary.
vocab [ values ] bind ;
#! Filter empty slots.
vocab [ values ] bind [ ] subset ;
: intern ( "word" -- word )
#! Returns the top of the stack if it already been interned.

View File

@ -25,15 +25,13 @@
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
IN: vocabularies
IN: words
USE: combinators
USE: lists
USE: kernel
USE: namespaces
USE: stack
USE: styles
USE: vocabularies
USE: words
: get-vocab-style ( vocab -- style )
#! Each vocab has a style object specifying how words are

View File

@ -2,9 +2,8 @@
void init_io(void)
{
env.user[STDIN_ENV] = tag_object(port(0));
env.user[STDOUT_ENV] = tag_object(port(1));
env.user[STDERR_ENV] = tag_object(port(2));
env.user[STDIN_ENV] = tag_object(port(PORT_READ,0));
env.user[STDOUT_ENV] = tag_object(port(PORT_WRITE,1));
}
bool can_read_line(PORT* port)
@ -98,13 +97,11 @@ bool can_write(PORT* port, FIXNUM len)
{
CELL buf_capacity;
switch(port->buf_mode)
switch(port->type)
{
case B_NONE:
return true;
case B_READ_LINE:
case PORT_READ:
return false;
case B_WRITE:
case PORT_WRITE:
buf_capacity = port->buffer->capacity * CHARS;
/* Is the string longer than the buffer? */
if(port->buf_fill == 0 && len > buf_capacity)
@ -116,7 +113,7 @@ bool can_write(PORT* port, FIXNUM len)
else
return (port->buf_fill + len <= buf_capacity);
default:
critical_error("Bad buf_mode",port->buf_mode);
critical_error("Bad port->type",port->type);
return false;
}
}
@ -135,8 +132,6 @@ void write_fd_char_8(PORT* port, FIXNUM ch)
if(!can_write(port,1))
io_error(port,__FUNCTION__);
init_buffer(port,B_WRITE);
bput((CELL)port->buffer + sizeof(STRING) + port->buf_fill,c);
port->buf_fill++;
}
@ -149,8 +144,6 @@ void write_fd_string_8(PORT* port, STRING* str)
if(!can_write(port,str->capacity))
io_error(port,__FUNCTION__);
init_buffer(port,B_WRITE);
c_str = to_c_string(str);
/* Append string to buffer */

View File

@ -21,5 +21,6 @@ void primitive_open_file(void)
if(fd < 0)
io_error(NULL,__FUNCTION__);
dpush(tag_object(port(fd)));
dpush(read ? tag_object(port(PORT_READ,fd)) : F);
dpush(write ? tag_object(port(PORT_WRITE,fd)) : F);
}

View File

@ -111,7 +111,11 @@ bool set_up_fd_set(fd_set* fdset, int fd_count, IO_TASK* io_tasks)
bool perform_read_line_io_task(PORT* port)
{
init_buffer(port,B_READ_LINE);
if(port->line == F)
port->line = tag_object(sbuf(LINE_SIZE));
else
untag_sbuf(port->line)->top = 0;
if(port->buf_pos >= port->buf_fill)
{
if(!read_step(port))
@ -130,7 +134,6 @@ bool perform_read_line_io_task(PORT* port)
bool perform_write_io_task(PORT* port)
{
init_buffer(port,B_WRITE);
if(write_step(port))
{
if(port->buf_pos == port->buf_fill)
@ -243,7 +246,15 @@ CELL next_io_task(void)
void primitive_next_io_task(void)
{
dpush(next_io_task());
CELL callback;
for(;;)
{
callback = next_io_task();
if(callback != F)
break;
}
dpush(callback);
}
void collect_io_tasks(void)

View File

@ -11,19 +11,25 @@ PORT* untag_port(CELL tagged)
return p;
}
PORT* port(CELL fd)
PORT* port(PORT_MODE type, CELL fd)
{
PORT* port = allot_object(PORT_TYPE,sizeof(PORT));
port->type = type;
port->fd = fd;
port->buffer = NULL;
port->line = F;
port->client_host = F;
port->client_port = F;
port->client_socket = F;
port->buf_mode = B_NONE;
port->line = F;
port->buf_fill = 0;
port->buf_pos = 0;
if(type == PORT_SPECIAL)
port->buffer = NULL;
else
port->buffer = string(BUF_SIZE,'\0');
if(fcntl(port->fd,F_SETFL,O_NONBLOCK,1) == -1)
io_error(port,__FUNCTION__);
@ -35,28 +41,6 @@ void primitive_portp(void)
drepl(tag_boolean(typep(PORT_TYPE,dpeek())));
}
void init_buffer(PORT* port, int mode)
{
if(port->buf_mode == B_NONE)
port->buffer = string(BUF_SIZE,'\0');
if(port->buf_mode != mode)
{
port->buf_fill = port->buf_pos = 0;
port->buf_mode = mode;
if(mode == B_READ_LINE)
port->line = tag_object(sbuf(LINE_SIZE));
}
else if(port->buf_mode == B_READ_LINE)
{
if(port->line == F)
port->line = tag_object(sbuf(LINE_SIZE));
else
untag_sbuf(port->line)->top = 0;
}
}
void fixup_port(PORT* port)
{
port->fd = -1;
@ -65,7 +49,6 @@ void fixup_port(PORT* port)
fixup(&port->line);
fixup(&port->client_host);
fixup(&port->client_port);
fixup(&port->client_socket);
}
void collect_port(PORT* port)
@ -75,5 +58,4 @@ void collect_port(PORT* port)
copy_object(&port->line);
copy_object(&port->client_host);
copy_object(&port->client_port);
copy_object(&port->client_socket);
}

View File

@ -1,8 +1,9 @@
/* Buffer mode */
typedef enum { B_READ_LINE, B_WRITE, B_NONE } B_MODE;
typedef enum { PORT_READ, PORT_WRITE, PORT_SPECIAL } PORT_MODE;
typedef struct {
CELL header;
/* one of PORT_READ or PORT_WRITE */
PORT_MODE type;
FIXNUM fd;
STRING* buffer;
/* tagged partial line used by read_line_fd */
@ -10,9 +11,8 @@ typedef struct {
/* tagged client info used by accept_fd */
CELL client_host;
CELL client_port;
/* untagged fd of accepted connection */
CELL client_socket;
/* one of B_READ, B_WRITE or B_NONE */
B_MODE buf_mode;
/* top of buffer */
CELL buf_fill;
/* current read/write position */
@ -20,8 +20,7 @@ typedef struct {
} PORT;
PORT* untag_port(CELL tagged);
PORT* port(CELL fd);
void init_buffer(PORT* port, int mode);
PORT* port(PORT_MODE type, CELL fd);
void primitive_portp(void);
void fixup_port(PORT* port);
void collect_port(PORT* port);

View File

@ -40,7 +40,7 @@ int make_server_socket(CHAR port)
void primitive_server_socket(void)
{
CHAR p = (CHAR)to_fixnum(dpop());
dpush(tag_object(port(make_server_socket(p))));
dpush(tag_object(port(PORT_SPECIAL,make_server_socket(p))));
}
CELL accept_connection(PORT* p)
@ -60,15 +60,16 @@ CELL accept_connection(PORT* p)
p->client_host = tag_object(from_c_string(inet_ntoa(
clientname.sin_addr)));
p->client_port = tag_fixnum(ntohs(clientname.sin_port));
p->client_socket = tag_object(port(new));
p->client_socket = new;
return true;
}
void primitive_accept_fd(void)
{
PORT* port = untag_port(dpop());
dpush(port->client_host);
dpush(port->client_port);
dpush(port->client_socket);
PORT* p = untag_port(dpop());
dpush(p->client_host);
dpush(p->client_port);
dpush(tag_object(port(PORT_READ,p->client_socket)));
dpush(tag_object(port(PORT_WRITE,p->client_socket)));
}