123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- (* json-stream-parser.sml
- *
- * COPYRIGHT (c) 2020 The Fellowship of SML/NJ (http://www.smlnj.org)
- * All rights reserved.
- *
- * TODO: use the same "source" abstraction supported by the `JSONParser`
- * structure.
- *)
- structure JSONStreamParser :> sig
- (* 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
- (* callback functions for the different parsing events *)
- type 'ctx callbacks = {
- null : 'ctx -> 'ctx,
- boolean : 'ctx * bool -> 'ctx,
- integer : 'ctx * IntInf.int -> 'ctx,
- float : 'ctx * real -> 'ctx,
- string : 'ctx * string -> 'ctx,
- startObject : 'ctx -> 'ctx,
- objectKey : 'ctx * string -> 'ctx,
- endObject : 'ctx -> 'ctx,
- startArray : 'ctx -> 'ctx,
- endArray : 'ctx -> 'ctx,
- error : 'ctx * string -> unit
- }
- val parse : 'ctx callbacks -> (source * 'ctx) -> 'ctx
- val parseFile : 'ctx callbacks -> (string * 'ctx) -> 'ctx
- end = struct
- structure Lex = JSONLexer
- structure T = JSONTokens
- datatype source = datatype JSONSource.source
- val openStream = JSONSource.openStream
- val openFile = JSONSource.openFile
- val openString = JSONSource.openString
- val close = JSONSource.close
- (* callback functions for the different parsing events *)
- type 'ctx callbacks = {
- null : 'ctx -> 'ctx,
- boolean : 'ctx * bool -> 'ctx,
- integer : 'ctx * IntInf.int -> 'ctx,
- float : 'ctx * real -> 'ctx,
- string : 'ctx * string -> 'ctx,
- startObject : 'ctx -> 'ctx,
- objectKey : 'ctx * string -> 'ctx,
- endObject : 'ctx -> 'ctx,
- startArray : 'ctx -> 'ctx,
- endArray : 'ctx -> 'ctx,
- error : 'ctx * string -> unit
- }
- fun error (cb : 'a callbacks, ctx, msg) = (
- #error cb (ctx, msg);
- raise Fail "error")
- fun parse (cb : 'a callbacks) (src as Src{srcMap, strm, ...}, ctx) = let
- val lexer = Lex.lex srcMap
- val errorMsg = JSONSource.errorMsg src
- fun err (ctx, span, msg, tok) = error (cb, ctx, errorMsg (span, msg, tok))
- fun parseValue (strm : Lex.strm, ctx) = let
- val (tok, span, strm) = lexer strm
- in
- case tok
- of T.LB => parseArray (strm, ctx)
- | T.LCB => parseObject (strm, ctx)
- | T.KW_null => (strm, #null cb ctx)
- | T.KW_true => (strm, #boolean cb (ctx, true))
- | T.KW_false => (strm, #boolean cb (ctx, false))
- | T.INT n => (strm, #integer cb (ctx, n))
- | T.FLOAT f => (strm, #float cb (ctx, f))
- | T.STRING s => (strm, #string cb (ctx, s))
- | _ => err (ctx, span, "error parsing value", tok)
- (* end case *)
- end
- and parseArray (strm : Lex.strm, ctx) = (case lexer strm
- of (T.RB, _, strm) => (strm, #endArray cb (#startArray cb ctx))
- | _ => let
- fun loop (strm, ctx) = let
- val (strm, ctx) = parseValue (strm, ctx)
- (* expect either a "," or a "]" *)
- val (tok, span, strm) = lexer strm
- in
- case tok
- of T.RB => (strm, ctx)
- | T.COMMA => loop (strm, ctx)
- | _ => err (ctx, span, "error parsing array", tok)
- (* end case *)
- end
- val ctx = #startArray cb ctx
- val (strm, ctx) = loop (strm, #startArray cb ctx)
- in
- (strm, #endArray cb ctx)
- end
- (* end case *))
- and parseObject (strm : Lex.strm, ctx) = let
- fun parseField (strm, ctx) = (case lexer strm
- of (T.STRING s, span, strm) => let
- val ctx = #objectKey cb (ctx, s)
- in
- case lexer strm
- of (T.COLON, _, strm) => parseValue (strm, ctx)
- | (tok, span, _) =>
- err (ctx, span, "error parsing field", tok)
- (* end case *)
- end
- | _ => (strm, ctx)
- (* end case *))
- fun loop (strm, ctx) = let
- val (strm, ctx) = parseField (strm, ctx)
- in
- (* expect either "," or "}" *)
- case lexer strm
- of (T.RCB, span, strm) => (strm, ctx)
- | (T.COMMA, span, strm) => loop (strm, ctx)
- | (tok, span, _) =>
- err (ctx, span, "error parsing object", tok)
- (* end case *)
- end
- val ctx = #startObject cb ctx
- val (strm, ctx) = loop (strm, #startObject cb ctx)
- in
- (strm, #endObject cb ctx)
- end
- val (inStrm, cxt) = parseValue (!strm, ctx)
- in
- strm := inStrm;
- ctx
- end
- fun parseFile cb = let
- val parse = parse cb
- fun parser (fileName, ctx) = let
- val inStrm = openFile fileName
- val ctx = parse (inStrm, ctx)
- handle ex => (close inStrm; raise ex)
- in
- close inStrm;
- ctx
- end
- in
- parser
- end
- end
|