|
@@ -4,9 +4,24 @@
|
|
|
* All rights reserved.
|
|
|
*)
|
|
|
|
|
|
-structure JSONParser : sig
|
|
|
+structure JSONParser :> sig
|
|
|
|
|
|
- val parse : TextIO.instream -> JSON.value
|
|
|
+ (* abstract type of JSON input *)
|
|
|
+ type source = JSONSource.source
|
|
|
+
|
|
|
+ (* open a text input stream as a source *)
|
|
|
+ val openStream : TextIO.instream -> source
|
|
|
+
|
|
|
+ (* open a text file as a source *)
|
|
|
+ val openFile : string -> source
|
|
|
+
|
|
|
+ (* open a string as a source *)
|
|
|
+ val openString : string -> source
|
|
|
+
|
|
|
+ (* close a source *)
|
|
|
+ val close : source -> unit
|
|
|
+
|
|
|
+ val parse : source -> JSON.value
|
|
|
|
|
|
val parseFile : string -> JSON.value
|
|
|
|
|
@@ -16,34 +31,20 @@ structure JSONParser : sig
|
|
|
structure T = JSONTokens
|
|
|
structure J = JSON
|
|
|
|
|
|
- fun parse' (srcMap, inStrm) = let
|
|
|
- fun error (pos, msg, tok) = raise Fail(concat[
|
|
|
- "error ", AntlrStreamPos.spanToString srcMap pos, ": ",
|
|
|
- msg, ", found '", JSONTokens.toString tok, "'"
|
|
|
- ])
|
|
|
+ datatype source = datatype JSONSource.source
|
|
|
+
|
|
|
+ val openStream = JSONSource.openStream
|
|
|
+ val openFile = JSONSource.openFile
|
|
|
+ val openString = JSONSource.openString
|
|
|
+ val close = JSONSource.close
|
|
|
+
|
|
|
+ fun parse (Src{closed = ref true, ...}) = raise Fail "closed JSON source"
|
|
|
+ | parse (src as Src{srcMap, strm, ...}) = let
|
|
|
+ val errorMsg = JSONSource.errorMsg src
|
|
|
+ fun error arg = raise Fail(errorMsg arg)
|
|
|
val lexer = Lex.lex srcMap
|
|
|
- fun parse_sequence (is_tok_end, parse_item) (strm : Lex.strm, itms) = let
|
|
|
- fun is_tok_sep tok = case tok of T.COMMA => true | _ => false
|
|
|
- val (tok, pos, strm') = lexer strm
|
|
|
- in if is_tok_sep tok
|
|
|
- then error (pos, "parsing sequence", tok)
|
|
|
- else if is_tok_end tok
|
|
|
- then (strm', itms)
|
|
|
- else let val (strm'', itm) = parse_item strm
|
|
|
- val (tok', pos', strm''') = lexer strm''
|
|
|
- in if is_tok_end tok'
|
|
|
- then (strm''', itm :: itms)
|
|
|
- else if is_tok_sep tok'
|
|
|
- then let val (tok'', pos'', _) = lexer strm'''
|
|
|
- in if is_tok_end tok''
|
|
|
- then error (pos'', "parsing sequence", tok'')
|
|
|
- else parse_sequence (is_tok_end, parse_item) (strm''', itm :: itms)
|
|
|
- end
|
|
|
- else error (pos', "parsing sequence", tok')
|
|
|
- end
|
|
|
- end
|
|
|
fun parseValue (strm : Lex.strm) = let
|
|
|
- val (tok, pos, strm) = lexer strm
|
|
|
+ val (tok, span, strm) = lexer strm
|
|
|
in
|
|
|
case tok
|
|
|
of T.LB => parseArray strm
|
|
@@ -54,47 +55,62 @@ structure JSONParser : sig
|
|
|
| T.INT n => (strm, J.INT n)
|
|
|
| T.FLOAT f => (strm, J.FLOAT f)
|
|
|
| T.STRING s => (strm, J.STRING s)
|
|
|
- | _ => error (pos, "parsing value", tok)
|
|
|
+ | _ => error (span, "parsing value", tok)
|
|
|
(* end case *)
|
|
|
end
|
|
|
- and parseArray (strm : Lex.strm) = let
|
|
|
- fun is_RB tok = case tok of T.RB => true | _ => false
|
|
|
- val (strm', elmnts) = parse_sequence (is_RB, parseValue) (strm, [])
|
|
|
- in (strm', J.ARRAY(List.rev elmnts))
|
|
|
- end
|
|
|
+ and parseArray (strm : Lex.strm) = (case lexer strm
|
|
|
+ of (T.RB, _, strm) => (strm, J.ARRAY[])
|
|
|
+ | _ => let
|
|
|
+ fun loop (strm, items) = let
|
|
|
+ val (strm, v) = parseValue strm
|
|
|
+ (* expect either a "," or a "]" *)
|
|
|
+ val (tok, span, strm) = lexer strm
|
|
|
+ in
|
|
|
+ case tok
|
|
|
+ of T.RB => (strm, v::items)
|
|
|
+ | T.COMMA => loop (strm, v::items)
|
|
|
+ | _ => error (span, "parsing array", tok)
|
|
|
+ (* end case *)
|
|
|
+ end
|
|
|
+ val (strm, items) = loop (strm, [])
|
|
|
+ in
|
|
|
+ (strm, J.ARRAY(List.rev items))
|
|
|
+ end
|
|
|
+ (* end case *))
|
|
|
and parseObject (strm : Lex.strm) = let
|
|
|
- fun is_RCB tok = case tok of T.RCB => true | _ => false
|
|
|
- fun parse_field strm = let
|
|
|
- val (tok, pos, strm') = lexer strm
|
|
|
- in case tok
|
|
|
- of T.STRING s =>
|
|
|
- (case lexer strm'
|
|
|
- of (T.COLON, _, strm'') => let
|
|
|
- val (strm''', v) = parseValue strm''
|
|
|
- in (strm''', (s, v))
|
|
|
- end
|
|
|
- | (tok', pos', _) => error (pos', "parsing field", tok')
|
|
|
- (* end case *))
|
|
|
- | _ => error (pos, "parsing field", tok)
|
|
|
- (* end case *)
|
|
|
- end
|
|
|
- val (strm', flds) = parse_sequence (is_RCB, parse_field) (strm, [])
|
|
|
- in (strm', J.OBJECT(List.rev flds))
|
|
|
+ fun parseField ((T.STRING s, _, strm), flds) = (case lexer strm
|
|
|
+ of (T.COLON, _, strm) => let
|
|
|
+ val (strm, v) = parseValue strm
|
|
|
+ in
|
|
|
+ parseFields (strm, (s, v)::flds)
|
|
|
+ end
|
|
|
+ | (tok, span, _) => error (span, "parsing field", tok)
|
|
|
+ (* end case *))
|
|
|
+ | parseField ((tok, span, _), _) = error (span, "parsing field", tok)
|
|
|
+ and parseFields (strm, flds) = (case lexer strm
|
|
|
+ of (T.RCB, span, strm) => (strm, J.OBJECT(List.rev flds))
|
|
|
+ | (T.COMMA, span, strm) => parseField (lexer strm, flds)
|
|
|
+ | (tok, span, _) => error (span, "parsing object", tok)
|
|
|
+ (* end case *))
|
|
|
+ in
|
|
|
+ case lexer strm
|
|
|
+ of (T.RCB, span, strm) => (strm, J.OBJECT[])
|
|
|
+ | tokEtc => parseField (tokEtc, [])
|
|
|
+ (* end case *)
|
|
|
end
|
|
|
+ val (inStrm, value) = parseValue (!strm)
|
|
|
in
|
|
|
- #2 (parseValue (Lex.streamifyInstream inStrm))
|
|
|
+ strm := inStrm;
|
|
|
+ value
|
|
|
end
|
|
|
|
|
|
- fun parse inStrm = parse' (AntlrStreamPos.mkSourcemap (), inStrm)
|
|
|
-
|
|
|
fun parseFile fileName = let
|
|
|
- val inStrm = TextIO.openIn fileName
|
|
|
- val v = parse' (AntlrStreamPos.mkSourcemap' fileName, inStrm)
|
|
|
- handle ex => (TextIO.closeIn inStrm; raise ex)
|
|
|
+ val inStrm = openFile fileName
|
|
|
+ val v = parse inStrm
|
|
|
+ handle ex => (close inStrm; raise ex)
|
|
|
in
|
|
|
- TextIO.closeIn inStrm;
|
|
|
+ close inStrm;
|
|
|
v
|
|
|
end
|
|
|
|
|
|
end
|
|
|
-
|