klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

commit dc4601d7152e9944baca0fe1d2b21d47abbba4c1
parent e3d7e46e591e5c2378ccd13127526f68f3771220
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Wed, 16 Nov 2011 16:11:46 -0300

Added binary ports (as opposed to the current character ports).

Diffstat:
Msrc/kgblobs.c | 7++-----
Msrc/kgnumbers.c | 2++
Msrc/kgnumbers.h | 1+
Msrc/kgports.c | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/kobject.c | 10++++++++++
Msrc/kobject.h | 10++++++++++
Msrc/kport.c | 19+++++++++++++------
Msrc/kport.h | 4++--
Msrc/kread.c | 23+++++++++++++++++++++++
Msrc/kread.h | 1+
Msrc/kstate.c | 6+++---
Msrc/kwrite.c | 21+++++++++++++++++++--
Msrc/kwrite.h | 1+
Msrc/tests/ports.k | 2++
14 files changed, 299 insertions(+), 48 deletions(-)

diff --git a/src/kgblobs.c b/src/kgblobs.c @@ -106,7 +106,7 @@ void blob_u8_setS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) UNUSED(xparams); UNUSED(denv); bind_3tp(K, ptree, "blob", ttisblob, blob, - "exact integer", keintegerp, tv_i, "exact integer", keintegerp, tv_byte); + "exact integer", keintegerp, tv_i, "u8", ttisu8, tv_byte); if (!ttisfixint(tv_i)) { /* TODO show index */ @@ -115,10 +115,7 @@ void blob_u8_setS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) } else if (kblob_immutablep(blob)) { klispE_throw_simple(K, "immutable blob"); return; - } else if (ivalue(tv_byte) < 0 || ivalue(tv_byte) > 255) { - klispE_throw_simple(K, "bad byte"); - return; - } + } int32_t i = ivalue(tv_i); diff --git a/src/kgnumbers.c b/src/kgnumbers.c @@ -50,6 +50,8 @@ bool kfinitep(TValue obj) { return !ttisinf(obj); } bool kintegerp(TValue obj) { return ttisinteger(obj); } /* only exact integers (like for indices), bigints & fixints */ bool keintegerp(TValue obj) { return ttiseinteger(obj); } +/* exact integers between 0 and 255 inclusive */ +bool ku8p(TValue obj) { return ttisu8(obj); } bool krationalp(TValue obj) { return ttisrational(obj); } bool krealp(TValue obj) { return ttisreal(obj); } /* TEMP used (as a type predicate) in all predicates that need a real with diff --git a/src/kgnumbers.h b/src/kgnumbers.h @@ -36,6 +36,7 @@ bool kexactp(TValue obj); bool kinexactp(TValue obj); bool kundefinedp(TValue obj); bool krobustp(TValue obj); +bool ku8p(TValue obj); /* 12.5.2 =? */ diff --git a/src/kgports.c b/src/kgports.c @@ -66,7 +66,7 @@ void with_file(klisp_State *K, TValue *xparams, TValue ptree, bind_2tp(K, ptree, "string", ttisstring, filename, "combiner", ttiscombiner, comb); - TValue new_port = kmake_port(K, filename, writep); + TValue new_port = kmake_port(K, filename, writep, false); krooted_tvs_push(K, new_port); /* make the continuation to close the file before returning */ TValue new_cont = kmake_continuation(K, kget_cc(K), @@ -109,11 +109,12 @@ void get_current_port(klisp_State *K, TValue *xparams, TValue ptree, void open_file(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { bool writep = bvalue(xparams[1]); + bool binaryp = bvalue(xparams[2]); UNUSED(denv); bind_1tp(K, ptree, "string", ttisstring, filename); - TValue new_port = kmake_port(K, filename, writep); + TValue new_port = kmake_port(K, filename, writep, binaryp); kapply_cc(K, new_port); } @@ -136,6 +137,27 @@ void close_file(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) } } +/* 15.1.? close-input-port, close-output-port, close-port */ +void close_port(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + bool readp = bvalue(xparams[1]); + bool writep = bvalue(xparams[2]); + UNUSED(denv); + + bind_1tp(K, ptree, "port", ttisport, port); + + bool dir_ok = !((writep && !kport_is_output(port)) || + (readp && !kport_is_input(port))); + + if (dir_ok) { + kclose_port(K, port); + kapply_cc(K, KINERT); + } else { + klispE_throw_simple(K, "wrong input/output direction"); + return; + } +} + /* 15.1.7 read */ void read(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { @@ -145,11 +167,15 @@ void read(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) TValue port = ptree; if (!get_opt_tpar(K, "read", K_TPORT, &port)) { port = kcdr(K->kd_in_port_key); /* access directly */ - } else if (!kport_is_input(port)) { + } + + if (!kport_is_input(port)) { klispE_throw_simple(K, "the port should be an input port"); return; - } - if (kport_is_closed(port)) { + } else if (!kport_is_character(port)) { + klispE_throw_simple(K, "the port should be a character port"); + return; + } else if (kport_is_closed(port)) { klispE_throw_simple(K, "the port is already closed"); return; } @@ -170,11 +196,15 @@ void write(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) if (!get_opt_tpar(K, "write", K_TPORT, &port)) { port = kcdr(K->kd_out_port_key); /* access directly */ - } else if (!kport_is_output(port)) { + } + + if (!kport_is_output(port)) { klispE_throw_simple(K, "the port should be an output port"); return; - } - if (kport_is_closed(port)) { + } else if (!kport_is_character(port)) { + klispE_throw_simple(K, "the port should be a character port"); + return; + } else if (kport_is_closed(port)) { klispE_throw_simple(K, "the port is already closed"); return; } @@ -196,11 +226,15 @@ void newline(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) TValue port = ptree; if (!get_opt_tpar(K, "newline", K_TPORT, &port)) { port = kcdr(K->kd_out_port_key); /* access directly */ - } else if (!kport_is_output(port)) { + } + + if (!kport_is_output(port)) { klispE_throw_simple(K, "the port should be an output port"); return; - } - if (kport_is_closed(port)) { + } else if (!kport_is_character(port)) { + klispE_throw_simple(K, "the port should be a character port"); + return; + } else if (kport_is_closed(port)) { klispE_throw_simple(K, "the port is already closed"); return; } @@ -220,11 +254,15 @@ void write_char(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) if (!get_opt_tpar(K, "write-char", K_TPORT, &port)) { port = kcdr(K->kd_out_port_key); /* access directly */ - } else if (!kport_is_output(port)) { + } + + if (!kport_is_output(port)) { klispE_throw_simple(K, "the port should be an output port"); return; - } - if (kport_is_closed(port)) { + } else if (!kport_is_character(port)) { + klispE_throw_simple(K, "the port should be a character port"); + return; + } else if (kport_is_closed(port)) { klispE_throw_simple(K, "the port is already closed"); return; } @@ -249,11 +287,15 @@ void read_peek_char(klisp_State *K, TValue *xparams, TValue ptree, TValue port = ptree; if (!get_opt_tpar(K, name, K_TPORT, &port)) { port = kcdr(K->kd_in_port_key); /* access directly */ - } else if (!kport_is_input(port)) { + } + + if (!kport_is_input(port)) { klispE_throw_simple(K, "the port should be an input port"); return; - } - if (kport_is_closed(port)) { + } else if (!kport_is_character(port)) { + klispE_throw_simple(K, "the port should be a character port"); + return; + } else if (kport_is_closed(port)) { klispE_throw_simple(K, "the port is already closed"); return; } @@ -282,11 +324,15 @@ void char_readyp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) TValue port = ptree; if (!get_opt_tpar(K, "char-ready?", K_TPORT, &port)) { port = kcdr(K->kd_in_port_key); /* access directly */ - } else if (!kport_is_input(port)) { + } + + if (!kport_is_input(port)) { klispE_throw_simple(K, "the port should be an input port"); return; - } - if (kport_is_closed(port)) { + } else if (!kport_is_character(port)) { + klispE_throw_simple(K, "the port should be a character port"); + return; + } else if (kport_is_closed(port)) { klispE_throw_simple(K, "the port is already closed"); return; } @@ -295,6 +341,102 @@ void char_readyp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) kapply_cc(K, KTRUE); } +/* 15.1.? write-u8 */ +void write_u8(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(xparams); + UNUSED(denv); + + bind_al1tp(K, ptree, "u8", ttisu8, u8, port); + + if (!get_opt_tpar(K, "write-u8", K_TPORT, &port)) { + port = kcdr(K->kd_out_port_key); /* access directly */ + } + + if (!kport_is_output(port)) { + klispE_throw_simple(K, "the port should be an output port"); + return; + } else if (!kport_is_binary(port)) { + klispE_throw_simple(K, "the port should be a binary port"); + return; + } else if (kport_is_closed(port)) { + klispE_throw_simple(K, "the port is already closed"); + return; + } + + kwrite_char_to_port(K, port, u8); + kapply_cc(K, KINERT); +} + +/* Helper for read-u8 and peek-u8 */ +void read_peek_u8(klisp_State *K, TValue *xparams, TValue ptree, + TValue denv) +{ + /* + ** xparams[0]: symbol name + ** xparams[1]: ret-u8-after-readp + */ + UNUSED(denv); + + char *name = ksymbol_buf(xparams[0]); + bool ret_u8p = bvalue(xparams[1]); + + TValue port = ptree; + if (!get_opt_tpar(K, name, K_TPORT, &port)) { + port = kcdr(K->kd_in_port_key); /* access directly */ + } + + if (!kport_is_input(port)) { + klispE_throw_simple(K, "the port should be an input port"); + return; + } else if (!kport_is_binary(port)) { + klispE_throw_simple(K, "the port should be a binary port"); + return; + } else if (kport_is_closed(port)) { + klispE_throw_simple(K, "the port is already closed"); + return; + } + + TValue obj = kread_peek_u8_from_port(K, port, ret_u8p); + kapply_cc(K, obj); +} + + +/* 15.1.? read-u8 */ +/* uses read_peek_u8 */ + +/* 15.1.? peek-u8 */ +/* uses read_peek_u8 */ + +/* 15.1.? u8-ready? */ +/* XXX: this always return #t, proper behaviour requires platform + specific code (probably select for posix & a thread for windows + (at least for files & consoles, I think pipes and sockets may + have something) */ +void u8_readyp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) +{ + UNUSED(xparams); + UNUSED(denv); + + TValue port = ptree; + if (!get_opt_tpar(K, "u8-ready?", K_TPORT, &port)) { + port = kcdr(K->kd_in_port_key); /* access directly */ + } + + if (!kport_is_input(port)) { + klispE_throw_simple(K, "the port should be an input port"); + return; + } else if (!kport_is_binary(port)) { + klispE_throw_simple(K, "the port should be a binary port"); + return; + } else if (kport_is_closed(port)) { + klispE_throw_simple(K, "the port is already closed"); + return; + } + + /* TODO: check if there are pending chars */ + kapply_cc(K, KTRUE); +} /* 15.2.1 call-with-input-file, call-with-output-file */ /* XXX: The report is incomplete here... for now use an empty environment, @@ -310,7 +452,7 @@ void call_with_file(klisp_State *K, TValue *xparams, TValue ptree, bind_2tp(K, ptree, "string", ttisstring, filename, "combiner", ttiscombiner, comb); - TValue new_port = kmake_port(K, filename, writep); + TValue new_port = kmake_port(K, filename, writep, false); krooted_tvs_push(K, new_port); /* make the continuation to close the file before returning */ TValue new_cont = kmake_continuation(K, kget_cc(K), @@ -428,7 +570,7 @@ void load(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* the reads must be guarded to close the file if there is some error this continuation also will return inert after the evaluation of the last expression is done */ - TValue port = kmake_port(K, filename, false); + TValue port = kmake_port(K, filename, false, false); krooted_tvs_push(K, port); TValue inert_cont = make_return_value_cont(K, kget_cc(K), KINERT); @@ -477,7 +619,7 @@ void get_module(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) bind_al1tp(K, ptree, "string", ttisstring, filename, maybe_env); - TValue port = kmake_port(K, filename, false); + TValue port = kmake_port(K, filename, false, false); krooted_tvs_push(K, port); TValue env = kmake_environment(K, K->ground_env); @@ -537,11 +679,15 @@ void display(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) if (!get_opt_tpar(K, "display", K_TPORT, &port)) { port = kcdr(K->kd_out_port_key); /* access directly */ - } else if (!kport_is_output(port)) { + } + + if (!kport_is_output(port)) { klispE_throw_simple(K, "the port should be an output port"); return; - } - if (kport_is_closed(port)) { + } else if (!kport_is_character(port)) { + klispE_throw_simple(K, "the port should be a character port"); + return; + } else if (kport_is_closed(port)) { klispE_throw_simple(K, "the port is already closed"); return; } @@ -653,6 +799,12 @@ void kinit_ports_ground_env(klisp_State *K) p2tv(kis_input_port)); add_applicative(K, ground_env, "output-port?", ftypep, 2, symbol, p2tv(kis_output_port)); + /* 15.1.? binary-port?, character-port? */ + add_applicative(K, ground_env, "binary-port?", ftypep, 2, symbol, + p2tv(kis_binary_port)); + add_applicative(K, ground_env, "character-port?", ftypep, 2, symbol, + p2tv(kis_character_port)); + /* 15.1.3 with-input-from-file, with-ouput-to-file */ /* 15.1.? with-error-to-file */ add_applicative(K, ground_env, "with-input-from-file", with_file, @@ -670,10 +822,16 @@ void kinit_ports_ground_env(klisp_State *K) add_applicative(K, ground_env, "get-current-error-port", get_current_port, 2, symbol, K->kd_error_port_key); /* 15.1.5 open-input-file, open-output-file */ - add_applicative(K, ground_env, "open-input-file", open_file, 2, symbol, - b2tv(false)); - add_applicative(K, ground_env, "open-output-file", open_file, 2, symbol, - b2tv(true)); + add_applicative(K, ground_env, "open-input-file", open_file, 3, symbol, + b2tv(false), b2tv(false)); + add_applicative(K, ground_env, "open-output-file", open_file, 3, symbol, + b2tv(true), b2tv(false)); + /* 15.1.? open-binary-input-file, open-binary-output-file */ + add_applicative(K, ground_env, "open-binary-input-file", open_file, 3, symbol, + b2tv(false), b2tv(true)); + add_applicative(K, ground_env, "open-binary-output-file", open_file, 3, symbol, + b2tv(true), b2tv(true)); + /* 15.1.6 close-input-file, close-output-file */ /* ASK John: should this be called close-input-port & close-ouput-port like in r5rs? that doesn't seem consistent with open thou */ @@ -681,6 +839,14 @@ void kinit_ports_ground_env(klisp_State *K) b2tv(false)); add_applicative(K, ground_env, "close-output-file", close_file, 2, symbol, b2tv(true)); + /* 15.1.? Use the r7rs names, in preparation for other kind of ports */ + add_applicative(K, ground_env, "close-input-port", close_port, 3, symbol, + b2tv(true), b2tv(false)); + add_applicative(K, ground_env, "close-output-port", close_port, 3, symbol, + b2tv(false), b2tv(true)); + add_applicative(K, ground_env, "close-port", close_port, 3, symbol, + b2tv(false), b2tv(false)); + /* 15.1.7 read */ add_applicative(K, ground_env, "read", read, 0); /* 15.1.8 write */ @@ -709,6 +875,20 @@ void kinit_ports_ground_env(klisp_State *K) (at least for files & consoles), I think pipes and sockets may have something */ add_applicative(K, ground_env, "char-ready?", char_readyp, 0); + /* 15.1.? write-u8 */ + add_applicative(K, ground_env, "write-u8", write_u8, 0); + /* 15.1.? read-u8 */ + add_applicative(K, ground_env, "read-u8", read_peek_u8, 2, symbol, + b2tv(false)); + /* 15.1.? peek-u8 */ + add_applicative(K, ground_env, "peek-u8", read_peek_u8, 2, symbol, + b2tv(true)); + /* 15.1.? u8-ready? */ + /* XXX: this always return #t, proper behaviour requires platform + specific code (probably select for posix, a thread for windows + (at least for files & consoles), I think pipes and sockets may + have something */ + add_applicative(K, ground_env, "u8-ready?", u8_readyp, 0); /* 15.2.1 call-with-input-file, call-with-output-file */ add_applicative(K, ground_env, "call-with-input-file", call_with_file, 2, symbol, b2tv(false)); diff --git a/src/kobject.c b/src/kobject.c @@ -78,6 +78,16 @@ bool kis_output_port(TValue o) return ttisport(o) && kport_is_output(o); } +bool kis_binary_port(TValue o) +{ + return ttisport(o) && kport_is_binary(o); +} + +bool kis_character_port(TValue o) +{ + return ttisport(o) && kport_is_character(o); +} + int32_t klispO_log2 (uint32_t x) { static const uint8_t log_2[256] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, diff --git a/src/kobject.h b/src/kobject.h @@ -237,6 +237,9 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define ttisbigint(o) (tbasetype_(o) == K_TAG_BIGINT) #define ttiseinteger(o_) ({ int32_t t_ = tbasetype_(o_); \ t_ == K_TAG_FIXINT || t_ == K_TAG_BIGINT;}) +#define ttisu8(o) ({ \ + TValue o__ = (o); \ + (ttisfixint(o__) && ivalue(o__) > 0 && ivalue(o__) < 256); }) #define ttisinteger(o) ({ TValue o__ = (o); \ (ttiseinteger(o__) || \ (ttisdouble(o__) && (floor(dvalue(o__)) == dvalue(o__))));}) @@ -789,14 +792,19 @@ int32_t kmark_count; #define K_FLAG_OUTPUT_PORT 0x01 #define K_FLAG_INPUT_PORT 0x02 #define K_FLAG_CLOSED_PORT 0x04 +/* At least for now ports are either binary or character */ +#define K_FLAG_BINARY_PORT 0x08 #define kport_set_input(o_) (tv_get_kflags(o_) |= K_FLAG_INPUT_PORT) #define kport_set_output(o_) (tv_get_kflags(o_) |= K_FLAG_INPUT_PORT) #define kport_set_closed(o_) (tv_get_kflags(o_) |= K_FLAG_CLOSED_PORT) +#define kport_set_binary(o_) (tv_get_kflags(o_) |= K_FLAG_BINARY_PORT) #define kport_is_input(o_) ((tv_get_kflags(o_) & K_FLAG_INPUT_PORT) != 0) #define kport_is_output(o_) ((tv_get_kflags(o_) & K_FLAG_OUTPUT_PORT) != 0) #define kport_is_closed(o_) ((tv_get_kflags(o_) & K_FLAG_CLOSED_PORT) != 0) +#define kport_is_binary(o_) ((tv_get_kflags(o_) & K_FLAG_BINARY_PORT) != 0) +#define kport_is_character(o_) ((tv_get_kflags(o_) & K_FLAG_BINARY_PORT) == 0) #define K_FLAG_WEAK_KEYS 0x01 #define K_FLAG_WEAK_VALUES 0x02 @@ -811,6 +819,8 @@ int32_t kmark_count; (at least gcc doesn't bother to create them and the linker fails) */ bool kis_input_port(TValue o); bool kis_output_port(TValue o); +bool kis_binary_port(TValue o); +bool kis_character_port(TValue o); /* Macro to test the most basic equality on TValues */ #define tv_equal(tv1_, tv2_) ((tv1_).raw == (tv2_).raw) diff --git a/src/kport.c b/src/kport.c @@ -23,16 +23,22 @@ Should use open, but it is non standard (fcntl.h, POSIX only) */ /* GC: Assumes filename is rooted */ -TValue kmake_port(klisp_State *K, TValue filename, bool writep) +TValue kmake_port(klisp_State *K, TValue filename, bool writep, bool binaryp) { /* for now always use text mode */ - FILE *f = fopen(kstring_buf(filename), writep? "w": "r"); + char *mode; + if (binaryp) + mode = writep? "wb": "rb"; + else + mode = writep? "w": "r"; + + FILE *f = fopen(kstring_buf(filename), mode); if (f == NULL) { klispE_throw_errno_with_irritants(K, "fopen", 2, filename, - kstring_new_b_imm(K, writep? "w": "r")); + kstring_new_b_imm(K, mode)); return KINERT; } else { - return kmake_std_port(K, filename, writep, f); + return kmake_std_port(K, filename, writep, binaryp, f); } } @@ -41,14 +47,15 @@ TValue kmake_port(klisp_State *K, TValue filename, bool writep) /* GC: Assumes filename, name & si are rooted */ TValue kmake_std_port(klisp_State *K, TValue filename, bool writep, - FILE *file) + bool binaryp, FILE *file) { Port *new_port = klispM_new(K, Port); /* header + gc_fields */ klispC_link(K, (GCObject *) new_port, K_TPORT, K_FLAG_CAN_HAVE_NAME | - (writep? K_FLAG_OUTPUT_PORT : K_FLAG_INPUT_PORT)); + (writep? K_FLAG_OUTPUT_PORT : K_FLAG_INPUT_PORT) | + (binaryp? K_FLAG_BINARY_PORT : 0)); /* port specific fields */ new_port->filename = filename; diff --git a/src/kport.h b/src/kport.h @@ -13,13 +13,13 @@ #include "kstate.h" /* GC: Assumes filename is rooted */ -TValue kmake_port(klisp_State *K, TValue filename, bool writep); +TValue kmake_port(klisp_State *K, TValue filename, bool writep, bool binaryp); /* this is for creating ports for stdin/stdout/stderr & helper for the one above */ /* GC: Assumes filename, name & si are rooted */ TValue kmake_std_port(klisp_State *K, TValue filename, bool writep, - FILE *file); + bool binaryp, FILE *file); /* This closes the underlying FILE * (unless it is a std port) and also set the closed flag to true, this shouldn't throw errors because it diff --git a/src/kread.c b/src/kread.c @@ -597,8 +597,31 @@ TValue kread_peek_char_from_port(klisp_State *K, TValue port, bool peek) return ch == EOF? KEOF : ch2tv((char)ch); } +TValue kread_peek_u8_from_port(klisp_State *K, TValue port, bool peek) +{ + /* Reset the EOF flag in the tokenizer. The flag is shared, + by operations on all ports. */ + K->ktok_seen_eof = false; + + K->curr_port = port; + K->curr_in = kport_file(port); + int32_t u8; + if (peek) { + u8 = ktok_peekc(K); + } else { + ktok_set_source_info(K, kport_filename(port), + kport_line(port), kport_col(port)); + u8 = ktok_getc(K); + kport_update_source_info(port, K->ktok_source_info.line, + K->ktok_source_info.col); + } + return u8 == EOF? KEOF : i2tv(u8 & 0xff); +} + /* This is needed by the repl to ignore trailing spaces (especially newlines) that could affect the source info */ +/* XXX This should be replaced somehow, as it doesn't work for sexp and + multi line comments */ void kread_ignore_whitespace_and_comments_from_port(klisp_State *K, TValue port) { diff --git a/src/kread.h b/src/kread.h @@ -15,6 +15,7 @@ */ TValue kread_from_port(klisp_State *K, TValue port, bool mut); TValue kread_peek_char_from_port(klisp_State *K, TValue port, bool peek); +TValue kread_peek_u8_from_port(klisp_State *K, TValue port, bool peek); void kread_ignore_whitespace_and_comments_from_port(klisp_State *K, TValue port); diff --git a/src/kstate.c b/src/kstate.c @@ -202,11 +202,11 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { /* the dynamic ports and the keys for the dynamic ports */ TValue in_port = kmake_std_port(K, kstring_new_b_imm(K, "*STDIN*"), - false, stdin); + false, false, stdin); TValue out_port = kmake_std_port(K, kstring_new_b_imm(K, "*STDOUT*"), - true, stdout); + true, false, stdout); TValue error_port = kmake_std_port(K, kstring_new_b_imm(K, "*STDERR*"), - true, stderr); + true, false, stderr); K->kd_in_port_key = kcons(K, KTRUE, in_port); K->kd_out_port_key = kcons(K, KTRUE, out_port); K->kd_error_port_key = kcons(K, KTRUE, error_port); diff --git a/src/kwrite.c b/src/kwrite.c @@ -422,8 +422,10 @@ void kwrite_simple(klisp_State *K, TValue obj) kw_printf(K, "#[promise]"); break; case K_TPORT: - /* TODO try to get the name/ I/O direction / filename */ - kw_printf(K, "#[%s port", kport_is_input(obj)? "input" : "output"); + /* TODO try to get the filename */ + kw_printf(K, "#[%s %s port", + kport_is_binary? "binary" : "character", + kport_is_input(obj)? "input" : "output"); #if KTRACK_NAMES if (khas_name(obj)) { kw_print_name(K, obj); @@ -602,3 +604,18 @@ void kwrite_char_to_port(klisp_State *K, TValue port, TValue ch) kwrite_error(K, "error writing char"); } } + +void kwrite_u8_to_port(klisp_State *K, TValue port, TValue u8) +{ + K->curr_port = port; + K->curr_out = kport_file(port); + int res = fputc(ivalue(u8), K->curr_out); + /* implicit flush, MAYBE add flush call */ + if (res != EOF) + res = fflush(K->curr_out); + + if (res == EOF) { + clearerr(K->curr_out); /* clear error for next time */ + kwrite_error(K, "error writing u8"); + } +} diff --git a/src/kwrite.h b/src/kwrite.h @@ -17,6 +17,7 @@ void kwrite_display_to_port(klisp_State *K, TValue port, TValue obj, bool displayp); void kwrite_newline_to_port(klisp_State *K, TValue port); void kwrite_char_to_port(klisp_State *K, TValue port, TValue ch); +void kwrite_u8_to_port(klisp_State *K, TValue port, TValue u8); #endif diff --git a/src/tests/ports.k b/src/tests/ports.k @@ -2,6 +2,8 @@ ;; ;; Tests of i/o features. ;; +;; TODO binary ports +;; ;; Utilities for testing input and output features: ;; ;; temp-file .......... temporary file for input and output