diff options
author | Vladimir Azarov <avm@intermediate-node.net> | 2025-03-24 21:51:16 +0100 |
---|---|---|
committer | Vladimir Azarov <avm@intermediate-node.net> | 2025-03-24 21:51:16 +0100 |
commit | 87217fe5ba58f5199d30586b5d9bec104dece445 (patch) | |
tree | 00a43d11ebdfbb65750e80758ce1925e4c6a1a3e /stream.sml | |
parent | e40727b58e357f123256557af50666aa42c2caa4 (diff) |
Partition into structures
Diffstat (limited to 'stream.sml')
-rw-r--r-- | stream.sml | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/stream.sml b/stream.sml new file mode 100644 index 0000000..10b02cc --- /dev/null +++ b/stream.sml @@ -0,0 +1,185 @@ +structure Stream :> STREAM = struct + type fileId = int + type fileOffset = int + type pos = fileId * fileOffset + type convPos = string * int * int option + + type filesInfo = (fileId * string * string) list + + exception EndOfStream + exception EndOfFile + + (* unreachable *) + exception InvalidStream + exception InvalidStreamAdvance + + exception LineWithoutNl + + fun pos2str (pos, line, col) = + let + val % = Int.toString + in + case col of + SOME col => pos ^ ":" ^ %line ^ ":" ^ %col + | NONE => pos ^ ":" ^ %line + end + + type t = { + (* stack of file ids, file offsets and file contents *) + stack: (fileId * fileOffset * string) list, + (* list of file ids, file names and file contents *) + allFiles: filesInfo + } + + fun extractFilesInfo (s: t) = #allFiles s + + fun calcFilePos s offset = + let + fun calc s cur offset (line, col) = + if cur = offset then + (line, col) + else + calc s (cur + 1) offset + (if String.sub (s, cur) = #"\n" then (line + 1, 1) + else (line, col + 1)) + in + calc s 0 offset (1, 1) + end + + fun printPos fileList (id, pos) = + let + val triple = List.find (fn (fid, _, _) => fid = id) fileList + in + case triple of + NONE => raise InvalidStream + | SOME (_, fname, contents) => + let + val (line, col) = calcFilePos contents pos + val line = Int.toString line + val col = Int.toString col + in + print $ fname ^ ":" ^ line ^ ":" ^ col ^ ": " + end + end + + fun readFile fname = + let + open TextIO + val h = openIn fname + val s = inputAll h + val () = closeIn h + in + s + end + + fun getchar + ({ stack = (id, off, contents) :: rest, allFiles }: t) + : (char * t) = + if off < String.size contents then + (String.sub (contents, off), + { stack = (id, off + 1, contents) :: rest, allFiles }) + else + raise EndOfFile + | getchar _ = raise EndOfStream + + fun ungetc + ({ stack = (id, off, contents) :: rest, allFiles }) = + if off = 0 then + raise InvalidStream + else + { stack = (id, off - 1, contents) :: rest, allFiles } + | ungetc _ = raise InvalidStream + + fun readline { stack = (fid, off, contents) :: rest, allFiles } = + let + val prevIsSlash = + off > 0 andalso String.sub (contents, off - 1) = #"\\" + + open String + fun read prevIsSlash offset acc = + let + val c = sub (contents, offset) + in + if offset = size contents then + raise LineWithoutNl + else if c = #"\n" then + if prevIsSlash then + read (c = #"\\") (offset + 1) (#" " :: tl acc) + else + (implode $ rev acc, offset + 1) + else + read (c = #"\\") (offset + 1) (c :: acc) + end + + val (arg, newOffset) = read prevIsSlash off [] + in + (arg, { stack = (fid, newOffset, contents) :: rest, allFiles }) + end + | readline _ = raise InvalidStream + + fun getOffset ({ stack = (_, off, _) :: _, ... }: t) = off + | getOffset _ = raise InvalidStream + + fun getPosAfterCharRead + ({ stack = (id, off, _) :: _, ... }: t) = (id, off - 1) + | getPosAfterCharRead _ = raise InvalidStream + + fun getPposFromPos (id, pos) + { stack = (_, _, _) :: _, allFiles} = + let + val (fname, contents) = + case List.find (fn (fid, _, _) => fid = id) allFiles of + NONE => raise InvalidStream + | SOME (_, fname, contents) => (fname, contents) + + val (line, col) = calcFilePos contents pos + in + (fname, line, SOME col) + end + | getPposFromPos _ _ = raise InvalidStream + + fun getPos ({ stack = (id, off, _) :: _, ... }: t) = (id, off) + | getPos _ = raise InvalidStream + + fun getSubstr startOff endOff + ({ stack = (_, _, contents) :: _, ... }: t) = + String.substring (contents, startOff, endOff - startOff) + | getSubstr _ _ _ = raise InvalidStream + + fun advanceToNewFile + ({ stack = (_, off, contents) :: rest, allFiles }: t) = + if off = String.size contents then + { stack = rest, allFiles } + else + raise InvalidStreamAdvance + | advanceToNewFile _ = raise InvalidStreamAdvance + + fun streamInit fname = + let + val contents = readFile fname + in + { allFiles = [(0, fname, contents)], stack = [(0, 0, contents)] } + end + + fun isFirstOnLine (id, offset) (stream: t) = + case List.find (fn (fid, _, _) => id = fid) (#allFiles stream) of + NONE => raise InvalidStream + | SOME (_, _, contents) => + let + fun returnToNL ~1 = true + | returnToNL offset = + let + val chr = String.sub (contents, offset) + in + if chr = #"\n" then + true + else if Char.isSpace chr then + returnToNL (offset - 1) + else + false + end + in + returnToNL (offset - 1) + end + +end |