Zulip Chat Archive
Stream: new members
Topic: Parsing JSON pairs in JSON obj
Andrew Wells (Mar 31 2023 at 18:23):
I'm trying to parse JSON to an AST. I don't want to auto generate the parser with FromJson
because I expect I'll need to tweak the parser and to give specific error messages to make sure it matches another implementation. Currently I have:
import Lean.Data.Json.Parser
import Lean.Data.Json.Basic
inductive Primitive where
| Bool (b: Bool): Primitive
| Int (i: Int): Primitive
| String (s: String): Primitive
deriving Repr
inductive Expr where
| PrimitiveLit (l:Primitive) : Expr
| If(g : Expr) (t:Expr) (e:Expr) : Expr
| And(lhs:Expr) (rhs:Expr): Expr
| Or(lhs:Expr) (rhs:Expr) : Expr
| GetAttr(e:Expr) (a:Attr) : Expr
| HasAttr(e:Expr) (a:Attr) : Expr
| Record(data: Record) : Expr
| Wrong : Expr
def my_json_expr_str := "{ \"And\" : { \"left\": {\"Lit\": {\"Bool\": true}}, {\"right\": {\"Lit\" : {\"Bool\": true} } } } }"
def my_json_expr := Lean.Json.parse my_json_expr_str
def json_to_expr (json : Lean.Json) : Expr := match json with
| Lean.Json.null => Expr.Wrong
| Lean.Json.bool b => Expr.PrimitiveLit (Primitive.Bool b)
| Lean.Json.num n => match n.exponent with
| 0 => Expr.PrimitiveLit (Primitive.Int n.mantissa)
| _ => Expr.Wrong
| Lean.Json.str s => Expr.PrimitiveLit (Primitive.String s)
| Lean.Json.arr _ => Expr.Wrong
| Lean.Json.obj o => match o with -- Can't figure out how to process this case
| Lean.RBNode.leaf => Expr.Wrong
| _ => Expr.Wrong
I'd like to do something like iterate through all <key, value>
pairs in the Json.obj
to make sure the keys are expected and then assuming they are, call different functions processing the value depending on the key. If I knew what all the keys were I see I could call getObjVal
for each key, but I can't seem to find a way to iterate through the keys.
Andrew Wells (Mar 31 2023 at 21:04):
I have a work around, but it looks like it will lead to pretty awful code:
Fix my JSON and define some helpers for unwrapping Except
def my_json_expr_str := "{ \"And\" : { \"left\": {\"Lit\": {\"Bool\": true}}, \"right\": {\"Lit\" : {\"Bool\": true} } } } "
def my_json_expr := Lean.Json.parse my_json_expr_str
structure ErrorStruct where
error : Bool
deriving Repr, Lean.ToJson
def unwrap_except (e: Except String Lean.Json) : Lean.Json := match e.isOk with
| true => Option.get! e.toOption
| false => Lean.Json.bool false
Define new funcs to check that all and any keys in the map match one of my key words:
def is_and_helper (s: String) (f: Lean.Json) : Bool := match s with
| "And" => true
| _ => false
def is_and (node : Lean.RBNode String fun x => Lean.Json) : Bool :=
let contains_and := Lean.RBNode.any is_and_helper node
let contains_nothing_but_and := Lean.RBNode.all is_and_helper node
contains_and && contains_nothing_but_and
Use a massive match statement:
def json_to_expr (json : Lean.Json) : Expr := match json with
| Lean.Json.null => Expr.Wrong
| Lean.Json.bool b => Expr.PrimitiveLit (Primitive.Bool b)
| Lean.Json.num n => match n.exponent with
| 0 => Expr.PrimitiveLit (Primitive.Int n.mantissa)
| _ => Expr.Wrong
| Lean.Json.str s => Expr.PrimitiveLit (Primitive.String s)
| Lean.Json.arr _ => Expr.Wrong
| Lean.Json.obj o => match is_and o with
| true =>
let lhs := unwrap_except ((unwrap_except (json.getObjVal? "And")).getObjVal? "left")
let rhs := unwrap_except ((unwrap_except (json.getObjVal? "And")).getObjVal? "right")
Expr.And (json_to_expr lhs) (json_to_expr rhs)
| false => Expr.Wrong -- Continue for every keyword
Any pointers on how I can do this more cleanly?
Eric Wieser (Mar 31 2023 at 21:08):
I doubt unwrap_expect
is a sensible approach here
Eric Wieser (Mar 31 2023 at 21:08):
Do you really want invalid json to be treated as bool literals for false?
Eric Wieser (Mar 31 2023 at 21:09):
You would do much better to propagate the exception, and declare json_to_expr (json : Lean.Json) : Except String Expr
Eric Wieser (Mar 31 2023 at 21:10):
let lhs := unwrap_except ((unwrap_except (json.getObjVal? "And")).getObjVal? "left")
let rhs := unwrap_except ((unwrap_except (json.getObjVal? "And")).getObjVal? "right")
Expr.And (json_to_expr lhs) (json_to_expr rhs)
would become something like
let lhs := (<-(<-json.getObjVal? "And").getObjVal? "left")
let rhs := (<-(<-json.getObjVal? "And").getObjVal? "right")
return Expr.And (json_to_expr lhs) (json_to_expr rhs)
Eric Wieser (Mar 31 2023 at 21:10):
(assuming docs4#Lean.Json.getObjVal? is the type I think it is)
Andrew Wells (Mar 31 2023 at 21:11):
Thanks, I'll clean that up! Is there a better solution to the contains_and && contains_nothing_but_and
pattern? Seems like this will get very verbose for more than a few key words
Andrew Wells (Mar 31 2023 at 21:15):
Hmm, I'm getting invalid use of
(<- ...), must be nested inside a 'do' expression
Eric Wieser (Mar 31 2023 at 21:33):
You need a do
after the =>
Eric Wieser (Mar 31 2023 at 21:33):
You probably need a pure
or return
before Expr.and
too
Eric Wieser (Mar 31 2023 at 21:34):
Andrew Wells said:
Thanks, I'll clean that up! Is there a better solution to the
contains_and && contains_nothing_but_and
pattern? Seems like this will get very verbose for more than a few key words
One answer would be to choose a more natural json representation, such as {"_kind": "and", "left": ..., "right":... }
Last updated: Dec 20 2023 at 11:08 UTC