/** * read.c * * Read basic Lisp objects..This is :bootstrap layer print; it needs to be * able to read characters, symbols, integers, lists and dotted pairs. I * don't think it needs to be able to read anything else. It must, however, * take a readtable as argument and expand reader macros. * * * (c) 2017 Simon Brooke * Licensed under GPL version 2.0, or, at your option, any later version. */ #include #include #include #include /* * wide characters */ #include #include #include "debug.h" #include "io/io.h" #include "io/read.h" #include "memory/node.h" #include "memory/pointer.h" #include "memory/pso.h" #include "memory/pso2.h" #include "memory/tags.h" #include "payloads/character.h" #include "payloads/cons.h" #include "payloads/exception.h" #include "payloads/function.h" #include "payloads/integer.h" #include "payloads/read_stream.h" #include "payloads/symbol.h" #include "ops/assoc.h" #include "ops/reverse.h" #include "payloads/stack.h" #include "ops/string_ops.h" #include "ops/truth.h" // TODO: what I've copied from 0.0.6 is *weirdly* over-complex for just now. // I think I'm going to essentially delete all this and start again. We need // to be able to despatch on readttables, and the initial readtable functions // don't need to be written in Lisp. // // In the long run a readtable ought to be a hashtable, but for now an assoc // list will do. // // A readtable function is a Lisp function so needs the stackframe and the // environment. Other arguments (including the output stream) should be passed // in the argument, so I think the first arg in the frame is the character read; // the next is the input stream; the next is the readtable, if any. /* * for the time being things which may be read are: * * integers * * lists * * atoms * * dotted pairs */ /** * An example wrapper function while I work out how I'm going to do this. * * For this and all other `read` functions unless documented otherwise, the * arguments in the frame are expected to be: * * 0. The input stream to read from; * 1. The read table currently in use; * 2. The character most recently read from that stream. */ struct pso_pointer read_example( struct pso_pointer frame_pointer ) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer stream = fetch_arg( frame, 0 ); struct pso_pointer readtable = fetch_arg( frame, 1 ); struct pso_pointer character = fetch_arg( frame, 2 ); struct pso_pointer result = nil; return result; } struct pso_pointer make_eof_exception( struct pso_pointer frame_pointer ) { return make_exception( make_frame( 1, frame_pointer, c_string_to_lisp_string ( frame_pointer, L"Read: end of input while reading" ) ) ); } /** * Function: return the next character from the stream indicated by arg 0; * further arguments are ignored. * * * (read-char stream) * * @param frame my stack frame. * @param frame_pointer a pointer to my stack frame. * @param env my environment. * @return a string of one character, namely the next available character * on my stream, if any, else nil. */ struct pso_pointer read_character( struct pso_pointer frame_pointer ) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer result = nil; struct pso_pointer stream_pointer = fetch_arg( frame, 0 ); if ( readp( stream_pointer ) ) { wint_t chr = url_fgetwc( stream_get_url_file( stream_pointer ) ); result = make_character( frame_pointer, chr ); #ifdef DEBUG debug_printf( DEBUG_IO, 0, L"\nRead character %lc\n", chr ); #endif } return result; } /** * @brief advance the `stream` indicated in arg[0] of this stack frame over any * whitespace characters. The character indicated by arg[2] will be treated as * potentially the first such character. Returns the first non-space character * encountered, or an exception. */ struct pso_pointer skip_whitespace( struct pso_pointer frame_pointer ) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer stream = fetch_arg( frame, 0 ); struct pso_pointer readtable = fetch_arg( frame, 1 ); struct pso_pointer character = fetch_arg( frame, 2 ); struct pso_pointer result = nil; if ( characterp( character ) ) { wchar_t wc = pointer_to_object( character )->payload.character.character; if ( !iswspace( wc ) && wc != L',' ) { result = character; } } if ( c_nilp( result ) && readp( stream ) ) { URL_FILE *input = pointer_to_object( stream )->payload.stream.stream; wint_t wc = url_fgetwc( input ); while ( iswspace( wc ) || wc == L',' ) { wc = url_fgetwc( input ); } result = ( wc == WEOF ) ? make_eof_exception( frame_pointer ) : make_character( frame_pointer, wc ); } return result; } struct pso_pointer read_list( struct pso_pointer frame_pointer ) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer stream = fetch_arg( frame, 0 ); struct pso_pointer readtable = fetch_arg( frame, 1 ); struct pso_pointer character = fetch_arg( frame, 2 ); struct pso_pointer result = nil; if ( !c_nilp( character ) && characterp( character ) && pointer_to_object( character )->payload.character.character == SYNTAX_LPAR ) { // it's OK if an LPAR is passed in, but we don't want it now. character = nil; } if ( !c_nilp( character ) ) { // if anything other than LPAR is passed in as character, TODO: throw exception. } while ( c_nilp( character ) || ( characterp( character ) && pointer_to_object( character )-> payload.character.character != SYNTAX_RPAR ) ) { character = skip_whitespace( make_frame ( 3, frame_pointer, stream, readtable, character ) ); struct pso_pointer r = read( make_frame ( 3, frame_pointer, stream, readtable, character ) ); if ( exceptionp( r ) ) { result = r; break; } else { result = make_cons( frame_pointer, r, result ); character = skip_whitespace( make_frame ( 3, frame_pointer, stream, readtable, character ) ); } } return consp( result ) ? c_reverse( frame_pointer, result ) : result; } /** * @brief Read one integer from the stream and return it. * * For this and all other `read` functions unless documented otherwise, the * arguments in the frame are expected to be: * * 0. The input stream to read from; * 1. The read table currently in use; * 2. The character most recently read from that stream. */ struct pso_pointer read_number( struct pso_pointer frame_pointer ) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer stream = fetch_arg( frame, 0 ); struct pso_pointer readtable = fetch_arg( frame, 1 ); struct pso_pointer character = fetch_arg( frame, 2 ); struct pso_pointer result = nil; int base = 10; // TODO: should check for *read-base* in the environment int64_t value = 0; if ( readp( stream ) ) { if ( c_nilp( character ) ) { character = read_character( make_frame( 1, frame_pointer, stream ) ); } wchar_t c = c_nilp( character ) ? 0 : pointer_to_object( character )->payload.character.character; URL_FILE *input = pointer_to_object( stream )->payload.stream.stream; for ( ; iswdigit( c ) || c == L','; c = url_fgetwc( input ) ) { if ( iswdigit( c ) ) { value = ( value * base ) + ( ( int ) c - ( int ) L'0' ); } } url_ungetwc( c, input ); result = make_integer( frame_pointer, value ); } // else exception? #ifdef DEBUG debug_printf( DEBUG_IO, 0, L"\nRead number %ld\n", value ); debug_dump_object( result, DEBUG_IO, 1 ); #endif return result; } struct pso_pointer read_symbol( struct pso_pointer frame_pointer ) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer stream = fetch_arg( frame, 0 ); struct pso_pointer readtable = fetch_arg( frame, 1 ); struct pso_pointer character = fetch_arg( frame, 2 ); struct pso_pointer result = nil; if ( readp( stream ) ) { if ( c_nilp( character ) ) { character = read_character( make_frame( 1, frame_pointer, stream ) ); } wchar_t c = c_nilp( character ) ? 0 : pointer_to_object( character )->payload.character.character; URL_FILE *input = pointer_to_object( stream )->payload.stream.stream; for ( ; symbol_char_p( c ); c = url_fgetwc( input ) ) { result = make_string_like_thing( frame_pointer, c, result, SYMBOLTAG ); } url_ungetwc( c, input ); result = c_reverse( frame_pointer, result ); } #ifdef DEBUG debug_print( L"\nRead symbol `", DEBUG_IO, 0 ); debug_print_object( result, DEBUG_IO, 0 ); debug_print( L"`\n\t", DEBUG_IO, 0 ); debug_dump_object( result, DEBUG_IO, 1 ); #endif return result; } /** * @brief Read the next object on the input stream indicated by this stack * frame, and return a pso_pointer to the object read. * * For this and all other `read` functions unless documented otherwise, the * arguments in the frame are expected to be: * * 0. The input stream to read from; * 1. The read table currently in use; * 2. The character most recently read from that stream. */ struct pso_pointer read( struct pso_pointer frame_pointer ) { struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso_pointer stream = fetch_arg( frame, 0 ); struct pso_pointer readtable = fetch_arg( frame, 1 ); struct pso_pointer character = fetch_arg( frame, 2 ); struct pso_pointer result = nil; if ( c_nilp( stream ) ) { stream = make_read_stream( frame_pointer, file_to_url_file( stdin ), nil ); } if ( c_nilp( readtable ) ) { readtable = c_assoc( lisp_io_read_table, fetch_env( frame_pointer ) ); } if ( c_nilp( character ) ) { character = skip_whitespace( make_frame( 1, frame_pointer, stream ) ); } struct pso_pointer readmacro = c_assoc( character, readtable ); if ( !c_nilp( readmacro ) ) { // invoke the read macro on the stream } else if ( readp( stream ) && characterp( character ) ) { wchar_t c = pointer_to_object( character )->payload.character.character; URL_FILE *input = pointer_to_object( stream )->payload.stream.stream; switch ( c ) { case SYNTAX_SEMICOLON: for ( c = url_fgetwc( input ); c != '\n'; c = url_fgetwc( input ) ); /* skip all characters from semi-colon to the end of the line */ break; case SYNTAX_LPAR: result = read_list( make_frame( 3, stream, readtable, character ) ); break; case EOF: result = make_eof_exception( frame_pointer ); break; default: struct pso_pointer next = make_frame( 3, frame_pointer, stream, readtable, make_character ( frame_pointer, c ) ); inc_ref( next ); if ( iswdigit( c ) ) { result = push_local( frame_pointer, read_number( next ) ); } else if ( symbol_char_p( c ) ) { result = push_local( frame_pointer, read_symbol( next ) ); } else { // result = // throw_exception( // c_string_to_lisp_symbol( L"read" ), // make_cons( // c_string_to_lisp_string // ( // L"Unrecognised // start of // input // character" // ), // make_string( // c, NIL ) // ), // frame_pointer ); } // dec_ref( next ); break; } } #ifdef DEBUG debug_print( L"Read expression: `", DEBUG_IO, 0 ); debug_print_object( result, DEBUG_IO, 0 ); debug_print( L"`\n", DEBUG_IO, 0 ); debug_dump_object( result, DEBUG_IO, 1 ); #endif return result; }