From e26362ea1b82c47206161b475bf15c9787ff07ae Mon Sep 17 00:00:00 2001 From: Matthew Fluet Date: Wed, 22 Nov 2023 11:21:46 -0500 Subject: [PATCH 1/2] New regression test demonstrating bug with `TextIO.getInstream` --- regression/textio.3.ok | 0 regression/textio.3.sml | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 regression/textio.3.ok create mode 100644 regression/textio.3.sml diff --git a/regression/textio.3.ok b/regression/textio.3.ok new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression/textio.3.sml b/regression/textio.3.sml new file mode 100644 index 0000000000..226b39218d --- /dev/null +++ b/regression/textio.3.sml @@ -0,0 +1,43 @@ +(* See https://github.com/MLton/mlton/issues/535. *) + +datatype tree = Leaf of char | Node of tree * tree + +fun nextis c cs = + case TextIO.StreamIO.input1 (TextIO.getInstream cs) of + NONE => false + | SOME (c', cs') => c' = c + +(* This version works. *) +(* +fun nextis c cs = + case TextIO.lookahead cs of + NONE => false + | SOME c' => c' = c +*) + +fun discard c cs = + let val c' = valOf (TextIO.input1 cs) in + if c = c' + then () + else raise Fail ("unexpected character: " ^ Char.toString c' ^ ", expecting: " ^ Char.toString c) + end + +fun parse cs = + case valOf (TextIO.input1 cs) of + #"(" => + let + fun loop l r = + if nextis #")" cs + then (discard #")" cs; Node (l, r)) + else loop (Node (l, r)) (parse cs) + in + loop (parse cs) (parse cs) + end + | c => Leaf c + +val cs = TextIO.openString "(a(bc)d)" + +(* with this it works too *) +(* val true = nextis #"(" cs *) + +val t = (parse cs) From 3d19b9368bf88c8aab69f26fdf270dd2c2c9f97d Mon Sep 17 00:00:00 2001 From: Matthew Fluet Date: Wed, 22 Nov 2023 11:22:50 -0500 Subject: [PATCH 2/2] Fix bug in `ImperativeIO.getInstream` Properly flush the "imperative" ImperativeIO buffer when realizing the underlying stream via `getInstream`. Failing to reset the `first` and `last` refs when realizing the `StreamIO.instream` from an `ImperativeIO.instream` that is in "imperative" mode allows subsequent `ImperativeIO.input*` functions to advance the stream with respect to the "imperative" mode but not advance the `StreamIO.instream`. After realizing the `StreamIO.instream` from an `ImpearativeIO.instream`, the stream should be in "stream" mode and always advance the `StreamIO.instream`. Fixes MLton/mlton#535 --- basis-library/io/imperative-io.fun | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/basis-library/io/imperative-io.fun b/basis-library/io/imperative-io.fun index 231e640b80..41cf3987db 100644 --- a/basis-library/io/imperative-io.fun +++ b/basis-library/io/imperative-io.fun @@ -1,4 +1,4 @@ -(* Copyright (C) 2013,2017 Matthew Fluet. +(* Copyright (C) 2013,2017,2023 Matthew Fluet. * Copyright (C) 2002-2007 Henry Cejtin, Matthew Fluet, Suresh * Jagannathan, and Stephen Weeks. * @@ -681,7 +681,7 @@ fun getInstream (ib as In {state, ...}) = AS.vector (AS.slice (buf, f, SOME (l - f))))) else doit (false, NONE) - val () = state := Stream s + val () = setInstream (ib, s) in s end