monte-language / typhon

A virtual machine for Monte.
Other
67 stars 10 forks source link

lib/json fails on docs_examples.json #174

Closed dckc closed 7 years ago

dckc commented 7 years ago

One might ask for a smaller example, but JSON.decode() doesn't tell me where it thinks the error is, so it's hard to narrow it down.

https://gist.github.com/dckc/3204a8bb40aa43a3b5843c744cb865ef

I get this stacktrace:

TRACE: From vat pa
 ~ Problem: Nonempty stack (unclosed object/array)
 ~   throw.run("Nonempty stack (unclosed object/array)")
 ~ File 'typhon/objects/ejectors.py', in object Throw:
 ~   throw.eject(throw, "Nonempty stack (unclosed object/array)")
 ~ File 'typhon/objects/ejectors.py', in object Throw:
 ~   <parse>.run(['[', '{', "source", ':', "if (2 < 3) {…, throw)
 ~ File '<eval>', in object _$parse:
 ~   <JSON>.decode("[\n  {\n    \"source\": \"if (2 < 3) {…, throw)
 ~ File '<eval>', in object _$JSON:
 ~   <go>.run(b`[$\n  {$\n    "source": "if (2 < 3) {…)
 ~ File '<eval>', in object _$main$go:
Total recorded time: 0.759183

And this is the code I was trying to use to read the JSON data:

import "lib/json" =~ [=> JSON :DeepFrozen]
import "lib/codec/utf8" =~ [=> UTF8 :DeepFrozen]

exports (main)

def main(_argv, => stdio) as DeepFrozen:
    def go(bs):
        def text := UTF8.decode(bs, throw)
        def suite := JSON.decode(text, throw)
        trace(suite.size())

    return when (stdio.stdin()(go)) -> {
        0
    }
MostAwesomeDude commented 7 years ago

This is an API error; go only receives the first segment of the input, which is going to be incomplete JSON data. Buffering the entire file in memory yields a valid parse.

dckc commented 7 years ago

oops. thanks for debugging

dckc commented 7 years ago

win!

# import "lib/monte/mast" =~ [=> makeMASTContext :DeepFrozen]
import "lib/json" =~ [=> JSON :DeepFrozen]
import "lib/streams" =~ [=> flow :DeepFrozen]
import "lib/codec/utf8" =~ [=> UTF8 :DeepFrozen]

exports (main)

def main(_argv, => stdio) as DeepFrozen:
    var buf := b``

    object inputSink:
        to run(more):
            buf += more
            trace(`got ${more.size()} bytes of input`)
        to complete():
            trace("complete")
            def text := UTF8.decode(buf, throw)
            def suite := JSON.decode(text, throw)
            trace(`suite size: ${suite.size()}`)
        to abort(problem):
            throw(problem)

    flow(stdio.stdin(), inputSink)
dckc commented 7 years ago

perhaps for a rosetta stone section or maybe even a library function, consider makeTextReader:

# import "lib/monte/mast" =~ [=> makeMASTContext :DeepFrozen]
import "lib/json" =~ [=> JSON :DeepFrozen]
import "lib/streams" =~ [=> flow :DeepFrozen]
import "lib/codec/utf8" =~ [=> UTF8 :DeepFrozen]

exports (main)

def makeTextReader(source) as DeepFrozen:
    var buf := b``
    def [iouText, gotText] := Ref.promise()
    object textReader:
        to read():
            return iouText
        to run(more):
            buf += more
        to complete():
            gotText.resolve(UTF8.decode(buf, throw))
        to abort(problem):
            throw(problem)
    flow(source, textReader)
    return textReader

def main(_argv, => stdio) as DeepFrozen:
    when(def text := makeTextReader(stdio.stdin()).read()) ->
        def suite := JSON.decode(text, throw)
        trace(`suite size: ${suite.size()}`)
dckc commented 7 years ago

simpson suggests using makeSink.asList() to avoid quadratic-time append... How's this?

# import "lib/monte/mast" =~ [=> makeMASTContext :DeepFrozen]
import "lib/json" =~ [=> JSON :DeepFrozen]
import "lib/streams" =~ [=> flow :DeepFrozen, => makeSink :DeepFrozen]
import "lib/codec/utf8" =~ [=> UTF8 :DeepFrozen]

exports (main)

def makeTextReader(source) as DeepFrozen:
    def [chunkVow, collector] := makeSink.asList()
    flow(source, collector)

    return object textReader:
        to read():
            return when (def chunks := chunkVow) ->
                UTF8.decode(b``.join(chunks), throw)

def main(_argv, => stdio) as DeepFrozen:
    when (def text := makeTextReader(stdio.stdin()).read()) ->
        def suite := JSON.decode(text, throw)
        trace(`suite size: ${suite.size()}`)