PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/detail/common/error.sml

https://bitbucket.org/mb0/gdsl
Standard ML (SML) | 244 lines | 180 code | 37 blank | 27 comment | 14 complexity | 18a2df118bdb389a146f5b3bf26ac4ed MD5 | raw file
  1. (* error.sml
  2. *
  3. * COPYRIGHT (c) 2007 The Manticore Project (http://manticore.cs.uchicago.edu)
  4. * All rights reserved.
  5. *
  6. * Common infrastructure for error reporting in the Manticore compiler.
  7. *)
  8. structure CurrentSourcemap = struct
  9. val sourcemap = ref (AntlrStreamPos.mkSourcemap())
  10. end
  11. structure Error :> sig
  12. (* logical positions in the input stream *)
  13. type pos = AntlrStreamPos.pos
  14. type span = {file: AntlrStreamPos.sourcemap, span: AntlrStreamPos.span}
  15. type err_stream
  16. (* make an error stream. *)
  17. val mkErrStream : string -> err_stream
  18. val mkErrStream' : unit -> err_stream
  19. val anyErrors : err_stream -> bool
  20. val sourceFile : err_stream -> string
  21. val sourceMap : err_stream -> AntlrStreamPos.sourcemap
  22. (* add error messages to the error stream *)
  23. val error : err_stream * string list -> unit
  24. val errorAt : err_stream * span * string list -> unit
  25. (* add warning messages to the error stream *)
  26. val warning : err_stream * string list -> unit
  27. val warningAt : err_stream * span * string list -> unit
  28. (* add an ml-antlr parse error to the error stream *)
  29. val parseError : ('tok -> string)
  30. -> err_stream
  31. -> (pos * 'tok AntlrRepair.repair_action)
  32. -> unit
  33. (* print the errors to an output stream *)
  34. val report : TextIO.outstream * err_stream -> unit
  35. (* source-code locations *)
  36. datatype location
  37. = UNKNOWN
  38. | LOC of {file : string, l1 : int, c1 : int, l2 : int, c2 : int}
  39. val location : err_stream * span -> location
  40. val position : err_stream * pos -> location
  41. val locToString : location -> string
  42. (* a term marked with a source-map span *)
  43. type 'a mark = {span : span, tree : 'a}
  44. end = struct
  45. structure SP = AntlrStreamPos
  46. structure Repair = AntlrRepair
  47. structure F = Format
  48. type pos = SP.pos
  49. type span = {file: SP.sourcemap, span: SP.span}
  50. datatype severity = WARN | ERR
  51. type error = {
  52. kind : severity,
  53. pos : span option,
  54. msg : string
  55. }
  56. (* an error stream collects the errors and warnings generated for
  57. * a compilation unit.
  58. *)
  59. datatype err_stream = ES of {
  60. srcFile : string,
  61. sm : SP.sourcemap, (* the source map for mapping positions to *)
  62. (* source-file positions *)
  63. errors : error list ref,
  64. numErrors : int ref,
  65. numWarnings : int ref
  66. }
  67. (* make an error stream. *)
  68. fun mkErrStream filename = ES{
  69. srcFile = filename,
  70. sm = SP.mkSourcemap' filename,
  71. errors = ref [],
  72. numErrors = ref 0,
  73. numWarnings = ref 0
  74. }
  75. fun mkErrStream' filename = ES{
  76. srcFile = "<unkown>",
  77. sm = SP.mkSourcemap (),
  78. errors = ref [],
  79. numErrors = ref 0,
  80. numWarnings = ref 0
  81. }
  82. fun anyErrors (ES{numErrors, ...}) = (!numErrors > 0)
  83. fun sourceFile (ES{srcFile, ...}) = srcFile
  84. fun sourceMap (ES{sm, ...}) = sm
  85. fun addErr (ES{errors, numErrors, ...}, pos, msg) = (
  86. numErrors := !numErrors + 1;
  87. errors := {kind=ERR, pos=pos, msg=msg} :: !errors)
  88. fun addWarn (ES{errors, numWarnings, ...}, pos, msg) = (
  89. numWarnings := !numWarnings + 1;
  90. errors := {kind=WARN, pos=pos, msg=msg} :: !errors)
  91. fun parseError tok2str es (pos, repair) = let
  92. val toksToStr = (String.concatWith " ") o (List.map tok2str)
  93. val msg = (case repair
  94. of Repair.Insert toks => ["syntax error; try inserting \"", toksToStr toks, "\""]
  95. | Repair.Delete toks => ["syntax error; try deleting \"", toksToStr toks, "\""]
  96. | Repair.Subst{old, new} => [
  97. "syntax error; try substituting \"", toksToStr new, "\" for \"",
  98. toksToStr old, "\""
  99. ]
  100. | Repair.FailureAt tok => ["syntax error at ", tok2str tok]
  101. (* end case *))
  102. in
  103. addErr (es, SOME{file=sourceMap es,span=(pos,pos)}, String.concat msg)
  104. end
  105. (* add error messages to the error stream *)
  106. fun error (es, msg) = addErr (es, NONE, String.concat msg)
  107. fun errorAt (es, span, msg) = addErr (es, SOME span, String.concat msg)
  108. (* add warning messages to the error stream *)
  109. fun warning (es, msg) = addWarn (es, NONE, String.concat msg)
  110. fun warningAt (es, span, msg) = addWarn (es, SOME span, String.concat msg)
  111. (* sort a list of errors by position in the source file *)
  112. val sort = let
  113. fun fname sm = Option.getOpt (SP.fileName sm 0, "")
  114. fun lt (NONE, NONE) = false
  115. | lt (NONE, _) = true
  116. | lt (_, NONE) = false
  117. | lt (SOME{file=f1,span=(l1, r1)}, SOME{file=f2,span=(l2, r2)}) =
  118. (case String.compare (fname f1, fname f2)
  119. of LESS => true
  120. | GREATER => false
  121. | EQUAL =>
  122. (case Position.compare(l1, l2)
  123. of LESS => true
  124. | EQUAL => (Position.compare(r1, r2) = LESS)
  125. | GREATER => false))
  126. fun cmp (e1 : error, e2 : error) = not (lt(#pos e1, #pos e2))
  127. in
  128. ListMergeSort.sort cmp
  129. end
  130. (* source-code locations *)
  131. datatype location
  132. = UNKNOWN
  133. | LOC of {file : string, l1 : int, c1 : int, l2 : int, c2 : int}
  134. (* FIXME *)
  135. fun location (ES{sm, ...}, {span=(p1, p2),...}: span) =
  136. if (p1 = p2)
  137. then let
  138. val {fileName=SOME f, lineNo, colNo} = SP.sourceLoc sm p1
  139. in
  140. LOC{file=f, l1=lineNo, c1=colNo, l2=lineNo, c2=colNo}
  141. end
  142. else let
  143. val {fileName=SOME f1, lineNo=l1, colNo=c1} = SP.sourceLoc sm p1
  144. val {fileName=SOME f2, lineNo=l2, colNo=c2} = SP.sourceLoc sm p2
  145. in
  146. if (f1 <> f2)
  147. then LOC{file=f1, l1=l1, c1=c1, l2=l1, c2=c1}
  148. else LOC{file=f1, l1=l1, c1=c1, l2=l2, c2=c2}
  149. end
  150. (* FIXME *)
  151. fun position (ES{sm, ...}, p : pos) = let
  152. val {fileName=SOME f, lineNo, colNo} = SP.sourceLoc sm p
  153. in
  154. LOC{file=f, l1=lineNo, c1=colNo, l2=lineNo, c2=colNo}
  155. end
  156. fun locToString UNKNOWN = "<unknown>"
  157. | locToString (LOC{file, l1, l2, c1, c2}) =
  158. if (l1 = l2)
  159. then if (c1 = c2)
  160. then F.format "[%s:%d.%d] " [F.STR file, F.INT l1, F.INT c1]
  161. else F.format "[%s:%d.%d-%d] " [F.STR file, F.INT l1, F.INT c1, F.INT c2]
  162. else F.format "[%s:%d.%d-%d.%d] " [
  163. F.STR file, F.INT l1, F.INT c1, F.INT l2, F.INT c2
  164. ]
  165. fun printError (outStrm, _) = let
  166. fun pr {kind, pos, msg} = let
  167. val kind = (case kind of ERR => "Error" | Warn => "Warning")
  168. val pos = (case pos of
  169. NONE => "[no position] "
  170. | SOME{file=sm,span=(p1, p2)} => if (p1 = p2)
  171. then let
  172. val {fileName=SOME f, lineNo, colNo} = SP.sourceLoc sm p1
  173. in
  174. F.format "[%s:%d.%d] " [
  175. F.STR f, F.INT lineNo, F.INT colNo
  176. ]
  177. end
  178. else let
  179. val {fileName=SOME f1, lineNo=l1, colNo=c1} = SP.sourceLoc sm p1
  180. val {fileName=SOME f2, lineNo=l2, colNo=c2} = SP.sourceLoc sm p2
  181. in
  182. if (f1 <> f2)
  183. then F.format "[%s:%d.%d-%s:%d.%d] " [
  184. F.STR f1, F.INT l1, F.INT c1,
  185. F.STR f2, F.INT l2, F.INT c2
  186. ]
  187. else if (l1 <> l2)
  188. then F.format "[%s:%d.%d-%d.%d] " [
  189. F.STR f1, F.INT l1, F.INT c1,
  190. F.INT l2, F.INT c2
  191. ]
  192. else F.format "[%s:%d.%d-%d] " [
  193. F.STR f1, F.INT l1, F.INT c1, F.INT c2
  194. ]
  195. end
  196. (* end case *))
  197. in
  198. TextIO.output (outStrm, String.concat [pos, kind, ": ", msg, "\n"])
  199. end
  200. in
  201. pr
  202. end
  203. fun report (outStrm, es as ES{errors, numErrors, ...}) =
  204. List.app (printError (outStrm, es)) (sort (!errors))
  205. (* a term marked with a source-map span *)
  206. type 'a mark = {span : span, tree : 'a}
  207. end