summaryrefslogtreecommitdiff
path: root/stream.sml
blob: eb2cea18c616a7c80a979ecd7e9ee165e305c68c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
structure Stream :> STREAM = struct 
  type fileId = int
  type fileOffset = int
  type pos = fileId * fileOffset
  type ppos = string * int * int option
  type fileInfo = fileId * string * string

  exception UngetcError

  fun ppos2str (pos, line, col) =
  let
    val % = Int.toString
  in
    case col of
      SOME col => pos ^ ":" ^ %line ^ ":" ^ %col
    | NONE => pos ^ ":" ^ %line
  end

  type t = fileId * string * fileOffset * string

  fun convert (fid, fname, _, contents) = (fid, fname, contents)

  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 (_, fname, contents) (_, pos) =
  let
    val (line, col) = calcFilePos contents pos
    val line = Int.toString line
    val col = Int.toString col
  in
    print $ fname ^ ":" ^ line ^ ":" ^ col ^ ": "
  end

  fun readFile fname =
  let
    open TextIO
    val h = openIn fname
    val s = inputAll h
    val () = closeIn h
  in
    s
  end

  fun getchar (S as (fid, fname, off, contents)) =
    if off < String.size contents then
      (SOME $ String.sub (contents, off), (fid, fname, off + 1, contents))
    else
      (NONE, S)

  fun ungetc (fid, fname, off, contents) =
    if off = 0 then
      raise UngetcError
    else
      (fid, fname, off - 1, contents)

  fun getPosAfterCharRead (fid, _, off, _) = (fid, off - 1)

  fun pos2ppos (_, pos) (_, fname, _, contents) =
  let
    val (line, col) = calcFilePos contents pos
  in
    (fname, line, SOME col)
  end

  fun pposWithoutCol (fname, line, SOME _) = (fname, line, NONE)
    | pposWithoutCol (_, _, NONE) = raise Unreachable

  fun getPos (id, _, off, _) = (id, off)

  fun getSubstr startOff endOff (_, _, _, contents) =
      String.substring (contents, startOff, endOff - startOff)

  fun streamInit fname =
  let
    val contents = readFile fname
  in
    (0, fname, 0, contents)
  end

  fun isFirstOnLine (_, offset) ((_, _, _, contents) : t) =
    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