Skip to content

Commit 97a7e37

Browse files
add char streams
1 parent a43bac8 commit 97a7e37

File tree

7 files changed

+249
-3
lines changed

7 files changed

+249
-3
lines changed

build/instructions_template.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ enum SystemClauseType {
247247
CurrentHostname,
248248
#[strum_discriminants(strum(props(Arity = "1", Name = "$current_input")))]
249249
CurrentInput,
250+
#[strum_discriminants(strum(props(Arity = "1", Name = "$memory_stream")))]
251+
MemoryStream,
250252
#[strum_discriminants(strum(props(Arity = "1", Name = "$current_output")))]
251253
CurrentOutput,
252254
#[strum_discriminants(strum(props(Arity = "2", Name = "$directory_files")))]
@@ -1672,6 +1674,7 @@ fn generate_instruction_preface() -> TokenStream {
16721674
&Instruction::CallCreatePartialString |
16731675
&Instruction::CallCurrentHostname |
16741676
&Instruction::CallCurrentInput |
1677+
&Instruction::CallMemoryStream |
16751678
&Instruction::CallCurrentOutput |
16761679
&Instruction::CallDirectoryFiles |
16771680
&Instruction::CallFileSize |
@@ -1915,6 +1918,7 @@ fn generate_instruction_preface() -> TokenStream {
19151918
&Instruction::ExecuteCreatePartialString |
19161919
&Instruction::ExecuteCurrentHostname |
19171920
&Instruction::ExecuteCurrentInput |
1921+
&Instruction::ExecuteMemoryStream |
19181922
&Instruction::ExecuteCurrentOutput |
19191923
&Instruction::ExecuteDirectoryFiles |
19201924
&Instruction::ExecuteFileSize |

src/lib/charsio.pl

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,18 @@
1414
read_from_chars/2,
1515
read_term_from_chars/3,
1616
write_term_to_chars/3,
17-
chars_base64/3]).
17+
chars_base64/3,
18+
chars_stream/1,
19+
chars_to_stream/2,
20+
chars_to_stream/3]).
1821

1922
:- use_module(library(dcgs)).
2023
:- use_module(library(iso_ext)).
2124
:- use_module(library(error)).
2225
:- use_module(library(lists)).
2326
:- use_module(library(between)).
2427
:- use_module(library(iso_ext), [partial_string/1,partial_string/3]).
28+
:- use_module(library(charsio/memory_stream_utils)).
2529

2630
fabricate_var_name(VarType, VarName, N) :-
2731
char_code('A', AC),
@@ -305,7 +309,7 @@
305309

306310
% invalid continuation byte
307311
% each remaining continuation byte (if any) will raise 0xFFFD too
308-
continuation(_, ['\xFFFD\'|T], _) --> [_], decode_utf8(T).
312+
continuation(_, ['\xFFFD\'|T], _) --> [_], decode_utf8(T). %'
309313

310314
%% get_line_to_chars(+Stream, -Chars, +InitialChars).
311315
%
@@ -393,3 +397,40 @@
393397
; '$chars_base64'(Cs, Bs, Padding, Charset)
394398
)
395399
).
400+
401+
%% chars_stream(-Stream)
402+
% Stream is a character stream.
403+
404+
chars_stream(Stream) :-
405+
'$memory_stream'(Stream).
406+
407+
%% chars_to_stream(+Chars, -Stream) :-
408+
% Convert a list of characters into a character stream.
409+
410+
chars_to_stream(Chars, Stream) :-
411+
chars_to_stream(Chars, Stream, []).
412+
413+
%% chars_to_stream(+Chars, -Stream, +Options) :-
414+
% Creates a character stream from a list of characters.
415+
%
416+
% Chars is the list of characters to write to the stream.
417+
% Stream is the created character stream (a memory stream).
418+
% Options are currently ignored.
419+
%
420+
% Example:
421+
%
422+
% ```
423+
% ?- chars_to_stream("hello", Stream, []).
424+
% Stream = stream('$memory_stream'(2048)).
425+
% ```
426+
427+
chars_to_stream(Chars, Stream, StreamOpts) :-
428+
parse_stream_options_list(StreamOpts, [Alias, EOFAction, Reposition, Type]),
429+
validate_chars(Chars, Type),
430+
'$memory_stream'(Stream),
431+
'$set_stream_options'(Stream, Alias ,EOFAction, Reposition, Type),
432+
( Type=binary
433+
-> maplist(put_byte(Stream), Chars)
434+
; maplist(put_char(Stream), Chars)
435+
).
436+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2+
Internal utilities supporting charsio:chars_to_stream/3.
3+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
4+
5+
:- module(memory_stream_utils, [parse_stream_options_list/2, validate_chars/2]).
6+
7+
:- use_module(library(lists)).
8+
:- use_module(library(error)).
9+
10+
11+
option_default(type, text).
12+
option_default(reposition, false).
13+
option_default(alias, []).
14+
option_default(eof_action, eof_code).
15+
16+
parse_stream_options_list(Options, [Alias, EOFAction, Reposition, Type]) :-
17+
maplist(parse_option, Options),
18+
option_default(type, Type, Options),
19+
option_default(reposition, Reposition, Options),
20+
option_default(alias, Alias, Options),
21+
option_default(eof_action, EOFAction, Options).
22+
23+
option_default(Option, Resolved, Options) :-
24+
option_default(Option, Default),
25+
MaybeOption =.. [Option,Answer],
26+
( member(MaybeOption, Options) ->
27+
Resolved=Answer
28+
; Resolved=Default
29+
).
30+
31+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
32+
?- option_default(type, Resolved, []).
33+
?- option_default(type, Resolved, [type(binary)]).
34+
?- option_default(type, Resolved, [type(text)]).
35+
?- option_default(type, Resolved, [alias]).
36+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
37+
38+
parse_option(type(Type)) :-
39+
( memberchk(Type, [binary, text]), !
40+
; throw(error(instantiation_error, choose_for(type, one_of(binary, text))))
41+
).
42+
43+
parse_option(reposition(Bool)) :-
44+
catch(must_be(boolean, Bool),
45+
error(_,_),
46+
throw(error(instantiation_error, choose_for(binary, one_of(true,false))))
47+
).
48+
49+
parse_option(alias(A)) :-
50+
( var(A)
51+
-> throw(error(instantiation_error, must_satisfy(alias, var)))
52+
; true
53+
),
54+
( atom(A), dif(A, []), !
55+
; throw(error(instantiation_error, must_satisfy(alias, (atom, dif([])))))
56+
)
57+
.
58+
59+
60+
parse_option(eof_action(Action)) :-
61+
( nonvar(Action), memberchk(Action, [eof_code, error, reset]), !
62+
; throw(error(domain_error(stream_option), choose_one(eof_action, [eof_code, error, reset])))
63+
).
64+
65+
validate_chars(Chars, Type) :-
66+
validate_chars_(Chars, Type).
67+
68+
valid_byte_(Byte) :-
69+
must_be(integer, Byte),
70+
Byte >= 0,
71+
Byte < 256.
72+
73+
validate_chars_(Chars, binary) :-
74+
maplist(valid_byte_, Chars).
75+
76+
validate_chars_(Chars, text) :-
77+
must_be(chars, Chars).

src/machine/dispatch.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,14 @@ impl Machine {
35213521
try_or_throw!(self.machine_st, self.current_input());
35223522
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
35233523
}
3524+
&Instruction::CallMemoryStream => {
3525+
try_or_throw!(self.machine_st, self.memory_stream());
3526+
step_or_fail!(self, self.machine_st.p += 1);
3527+
}
3528+
&Instruction::ExecuteMemoryStream => {
3529+
try_or_throw!(self.machine_st, self.memory_stream());
3530+
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
3531+
}
35243532
&Instruction::CallCurrentOutput => {
35253533
try_or_throw!(self.machine_st, self.current_output());
35263534
step_or_fail!(self, self.machine_st.p += 1);

src/machine/system_calls.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::heap_print::*;
1414
#[cfg(feature = "http")]
1515
use crate::http::{HttpListener, HttpRequest, HttpRequestData, HttpResponse};
1616
use crate::instructions::*;
17-
use crate::machine;
17+
use crate::{machine};
1818
use crate::machine::code_walker::*;
1919
use crate::machine::copier::*;
2020
use crate::machine::heap::*;
@@ -1952,6 +1952,41 @@ impl Machine {
19521952
Ok(())
19531953
}
19541954

1955+
#[inline(always)]
1956+
pub(crate) fn memory_stream(&mut self) -> CallResult {
1957+
let addr = self.deref_register(1);
1958+
let stream = Stream::from_owned_string("".to_string(), &mut self.machine_st.arena);
1959+
1960+
if let Some(var) = addr.as_var() {
1961+
self.machine_st.bind(var, stream.into());
1962+
return Ok(());
1963+
}
1964+
1965+
read_heap_cell!(addr,
1966+
(HeapCellValueTag::Cons, cons_ptr) => {
1967+
match_untyped_arena_ptr!(cons_ptr,
1968+
(ArenaHeaderTag::Stream, other_stream) => {
1969+
self.machine_st.fail = stream != other_stream;
1970+
}
1971+
_ => {
1972+
let stub = functor_stub(atom!("memory_stream"), 1);
1973+
let err = self.machine_st.domain_error(DomainErrorType::Stream, addr);
1974+
1975+
return Err(self.machine_st.error_form(err, stub));
1976+
}
1977+
);
1978+
}
1979+
_ => {
1980+
let stub = functor_stub(atom!("memory_stream"), 1);
1981+
let err = self.machine_st.domain_error(DomainErrorType::Stream, addr);
1982+
1983+
return Err(self.machine_st.error_form(err, stub));
1984+
}
1985+
);
1986+
1987+
Ok(())
1988+
}
1989+
19551990
#[inline(always)]
19561991
pub(crate) fn current_output(&mut self) -> CallResult {
19571992
let addr = self.deref_register(1);

src/tests/charsio.pl

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
:- module(charsio_tests, []).
2+
:- use_module(library(lists)).
3+
:- use_module(library(charsio)).
4+
5+
:- use_module(test_framework).
6+
7+
8+
9+
test("can create string char stream",
10+
( chars_stream(Stream),
11+
put_char(Stream, a),
12+
get_char(Stream, C),
13+
C=a
14+
)).
15+
16+
17+
test("can spell simple word with char stream",
18+
(
19+
chars_stream(Stream),
20+
put_char(Stream, c),
21+
put_char(Stream, a),
22+
put_char(Stream, t),
23+
get_n_chars(Stream, 3, Chars),
24+
Chars=[c,a,t]
25+
)).
26+
27+
test("can read from and write to char stream",
28+
(
29+
chars_stream(Stream),
30+
put_char(Stream, c),
31+
put_char(Stream, a),
32+
get_char(Stream, _C),
33+
put_char(Stream, b),
34+
get_n_chars(Stream, 2, Chars),
35+
Chars=[a,b]
36+
)
37+
).
38+
39+
40+
test("can convert string to char stream",
41+
(
42+
Phrase="can convert string to char stream",
43+
length(Phrase, N),
44+
chars_to_stream(Phrase, Stream),
45+
get_n_chars(Stream, N, Chars),
46+
Phrase=Chars
47+
)
48+
).
49+
50+
test("can convert string to char stream with options",
51+
(
52+
Phrase="can convert string to char stream",
53+
length(Phrase, N),
54+
chars_to_stream(Phrase, Stream, []),
55+
get_n_chars(Stream, N, Chars),
56+
Phrase=Chars
57+
)).
58+
59+
60+
test("can read/write bytes",
61+
(
62+
A=97,
63+
B=98,
64+
C=99,
65+
chars_to_stream([A,B,C], Stream, [type(binary)]),
66+
get_byte(Stream, A),
67+
get_byte(Stream, B),
68+
get_byte(Stream, C),
69+
put_byte(Stream, A),
70+
put_byte(Stream, B),
71+
put_byte(Stream, C),
72+
get_byte(Stream, A),
73+
get_byte(Stream, B),
74+
get_byte(Stream, C)
75+
)).
76+
77+
78+
79+
% ?- test_framework:main(charsio_tests).
80+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
args = ["-f", "--no-add-history", "src/tests/charsio.pl", "-f", "-g", "use_module(library(charsio_tests)), charsio_tests:main_quiet(charsio_tests)"]

0 commit comments

Comments
 (0)