diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ec3a051..786defb1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,7 @@ jobs: build-test: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: include: - { os: windows-latest, rust-version: stable, shell: 'msys2 {0}', target: 'x86_64-pc-windows-gnu'} diff --git a/src/lib/pio.pl b/src/lib/pio.pl index fdc5bcb1..f13c9b85 100644 --- a/src/lib/pio.pl +++ b/src/lib/pio.pl @@ -9,6 +9,7 @@ :- module(pio, [phrase_from_file/2, phrase_from_file/3, + phrase_from_stream/2, phrase_to_file/2, phrase_to_file/3, phrase_to_stream/2 @@ -17,16 +18,30 @@ :- use_module(library(dcgs)). :- use_module(library(error)). :- use_module(library(freeze)). -:- use_module(library(iso_ext), [setup_call_cleanup/3, partial_string/3]). -:- use_module(library(lists), [member/2, maplist/2]). +:- use_module(library(gensym)). +:- use_module(library(iso_ext), [ + bb_get/2, bb_put/2, setup_call_cleanup/3, partial_string/3, partial_string_tail/2 +]). +:- use_module(library(lists), [append/3, length/2, member/2, maplist/2]). :- use_module(library(charsio), [get_n_chars/3]). :- meta_predicate(phrase_from_file(2, ?)). :- meta_predicate(phrase_from_file(2, ?, ?)). +:- meta_predicate(phrase_from_stream(2, ?)). :- meta_predicate(phrase_to_file(2, ?)). :- meta_predicate(phrase_to_file(2, ?, ?)). :- meta_predicate(phrase_to_stream(2, ?)). + +%% phrase_from_stream(+GRBody, +Stream) +% +% True if grammar rule body GRBody covers the contents of the stream, +% represented as a list of characters. + +phrase_from_stream(GRBody, Stream) :- + stream_to_lazy_list(Stream, Ls), + phrase(GRBody, Ls). + %% phrase_from_file(+GRBody, +File) % % True if grammar rule body GRBody covers the contents of File, @@ -48,25 +63,121 @@ phrase_from_file(NT, File, Options) :- member(Type, [text,binary]) ; Type = text ), - setup_call_cleanup(open(File, read, Stream, [reposition(true)|Options]), - ( stream_to_lazy_list(Stream, Xs), - phrase(NT, Xs) ), - close(Stream)) - ). + setup_call_cleanup( + open(File, read, Stream, Options), + phrase_from_stream(NT, Stream), + close(Stream) + ) + ). +% How many chars to read from stream and buffer in each step +chars_to_read(4096). -stream_to_lazy_list(Stream, Xs) :- - stream_property(Stream, position(Pos)), - freeze(Xs, reader_step(Stream, Pos, Xs)). +stream_to_lazy_list(Stream, Ls) :- + get_stream_buffer_position(Stream, Pos), + freeze(Ls, render_step(Stream, Pos, Ls)). -reader_step(Stream, Pos, Xs0) :- - set_stream_position(Stream, Pos), - ( at_end_of_stream(Stream) - -> Xs0 = [] - ; get_n_chars(Stream, 4096, Cs), - partial_string(Cs, Xs0, Xs), - stream_to_lazy_list(Stream, Xs) - ). +render_step(Stream, Pos, Ls) :- + set_stream_buffer_position(Stream, Pos), + ( buffer_at_end_of_stream(Stream) -> + Ls = [] + ; chars_to_read(CharsToRead), + buffer_get_n_chars(Stream, CharsToRead, Chars), + partial_string(Chars, Ls, Ls0), + stream_to_lazy_list(Stream, Ls0) + ). + +buffer_at_end_of_stream(Stream) :- + stream_bufferids(Stream, _, BufferPosId, _), + bb_get(BufferPosId, Pos), + Pos = eof. + +get_stream_buffer_position(Stream, Pos) :- + stream_bufferids(Stream, _, BufferPosId, _), + bb_get(BufferPosId, Pos). + +set_stream_buffer_position(Stream, Pos) :- + stream_bufferids(Stream, _, BufferPosId, _), + bb_put(BufferPosId, Pos). + +buffer_get_n_chars(Stream, N, Chars) :- + stream_bufferids(Stream, BufferId, BufferPosId, BufferLenId), + buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N), + bb_get(BufferId, Buffer), + bb_get(BufferPosId, BufferPos), + ( BufferPos = eof -> + Chars = [] + ; string_get_n_chars(Buffer, BufferPos, N, Chars), + length(Chars, NChars), + ( NChars = 0 -> + BufferPos1 = eof + ; BufferPos1 is BufferPos + NChars + ), + bb_put(BufferPosId, BufferPos1) + ). + +buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N) :- + bb_get(BufferPosId, BufferPos), + bb_get(BufferLenId, BufferLen), + ( BufferLen < BufferPos + N -> + bb_get(BufferId, Buffer), + ( + ( var(Buffer) -> + BufferTail = Buffer + ; partial_string_last_tail(Buffer, BufferTail) + ) -> + ( at_end_of_stream(Stream) -> + BufferTail = [], + bb_put(BufferId, Buffer) + ; chars_to_read(CharsToRead), + get_n_chars(Stream, CharsToRead, Chars), + length(Chars, NChars), + partial_string(Chars, BufferTail, _), + bb_put(BufferId, Buffer), + BufferLen1 is BufferLen + NChars, + bb_put(BufferLenId, BufferLen1), + buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N) + ) + ; true + ) + ; true + ). + +partial_string_last_tail(PartialString, PartialStringTail) :- + partial_string_tail(PartialString, PartialStringTail0), + ( var(PartialStringTail0) -> + PartialStringTail = PartialStringTail0 + ; partial_string_last_tail(PartialStringTail0, PartialStringTail) + ). + +string_get_n_chars(String, Pos, N, Chars) :- + '$skip_max_list'(_, Pos, String, String1), + '$skip_max_list'(N1, N, String1, _), + length(Chars, N1), + append(Chars, _, String1). + +stream_bufferids(Stream, BufferId, BufferPosId, BufferLenId) :- + ( bb_get(streams_buffers, _) -> + true + ; bb_put(streams_buffers, []) + ), + bb_get(streams_buffers, StreamsBuffers), + ( member( + stream_buffer(Stream, BufferId, BufferPosId, BufferLenId), + StreamsBuffers + ) -> + true + ; gensym(buffer, BufferId), + gensym(buffer_pos, BufferPosId), + gensym(buffer_len, BufferLenId), + bb_put( + streams_buffers, + [stream_buffer(Stream, BufferId, BufferPosId, BufferLenId)|StreamsBuffers] + ), + bb_put(BufferId, _), + bb_put(BufferPosId, 0), + bb_put(BufferLenId, 0) + ). %% phrase_to_stream(+GRBody, +Stream) % diff --git a/src/parser/macros.rs b/src/parser/macros.rs index 702f6ae1..26bd0b2b 100644 --- a/src/parser/macros.rs +++ b/src/parser/macros.rs @@ -132,7 +132,7 @@ macro_rules! hexadecimal_digit_char { #[macro_export] macro_rules! layout_char { ($c: expr) => { - $crate::char_class!($c, [' ', '\n', '\t', '\u{0B}', '\u{0C}']) + $crate::char_class!($c, [' ', '\r', '\n', '\t', '\u{0B}', '\u{0C}']) }; }