213 lines
5.9 KiB
Text
213 lines
5.9 KiB
Text
/-
|
||
Copyright (c) 2019 Gabriel Ebner. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
|
||
Authors: Gabriel Ebner, Marc Huisinga
|
||
-/
|
||
import Std.Data.RBTree
|
||
namespace Lean
|
||
|
||
-- mantissa * 10^-exponent
|
||
structure JsonNumber where
|
||
mantissa : Int
|
||
exponent : Nat
|
||
deriving DecidableEq
|
||
|
||
namespace JsonNumber
|
||
|
||
protected def fromNat (n : Nat) : JsonNumber := ⟨n, 0⟩
|
||
protected def fromInt (n : Int) : JsonNumber := ⟨n, 0⟩
|
||
|
||
instance : Coe Nat JsonNumber := ⟨JsonNumber.fromNat⟩
|
||
instance : Coe Int JsonNumber := ⟨JsonNumber.fromInt⟩
|
||
|
||
private partial def countDigits (n : Nat) : Nat :=
|
||
let rec loop (n digits : Nat) : Nat :=
|
||
if n ≤ 9 then
|
||
digits
|
||
else
|
||
loop (n/10) (digits+1)
|
||
loop n 1
|
||
|
||
-- convert mantissa * 10^-exponent to 0.mantissa * 10^exponent
|
||
protected def normalize : JsonNumber → Int × Nat × Int
|
||
| ⟨m, e⟩ => do
|
||
if m = 0 then (0, 0, 0)
|
||
else
|
||
let sign : Int := if m > 0 then 1 else -1
|
||
let mut mAbs := m.natAbs
|
||
let nDigits := countDigits mAbs
|
||
-- eliminate trailing zeros
|
||
for _ in [0:nDigits] do
|
||
if mAbs % 10 = 0 then
|
||
mAbs := mAbs / 10
|
||
else
|
||
break
|
||
(sign, mAbs, -(e : Int) + nDigits)
|
||
|
||
-- todo (Dany): We should have an Ordering version of this.
|
||
def lt (a b : JsonNumber) : Bool :=
|
||
let (as, am, ae) := a.normalize
|
||
let (bs, bm, be) := b.normalize
|
||
match (as, bs) with
|
||
| (-1, 1) => true
|
||
| (1, -1) => false
|
||
| _ =>
|
||
let ((am, ae), (bm, be)) :=
|
||
if as = -1 && bs = -1 then
|
||
((bm, be), (am, ae))
|
||
else
|
||
((am, ae), (bm, be))
|
||
let amDigits := countDigits am
|
||
let bmDigits := countDigits bm
|
||
-- align the mantissas
|
||
let (am, bm) :=
|
||
if amDigits < bmDigits then
|
||
(am * 10^(bmDigits - amDigits), bm)
|
||
else
|
||
(am, bm * 10^(amDigits - bmDigits))
|
||
if ae < be then true
|
||
else if ae > be then false
|
||
else am < bm
|
||
|
||
def ltProp : LT JsonNumber :=
|
||
⟨fun a b => lt a b = true⟩
|
||
|
||
instance : LT JsonNumber :=
|
||
ltProp
|
||
|
||
instance (a b : JsonNumber) : Decidable (a < b) :=
|
||
inferInstanceAs (Decidable (lt a b = true))
|
||
|
||
instance : Ord JsonNumber where
|
||
compare x y :=
|
||
if x < y then Ordering.lt
|
||
else if x > y then Ordering.gt
|
||
else Ordering.eq
|
||
|
||
protected def toString : JsonNumber → String
|
||
| ⟨m, 0⟩ => m.repr
|
||
| ⟨m, e⟩ =>
|
||
let sign := if m ≥ 0 then "" else "-"
|
||
let m := m.natAbs
|
||
-- if there are too many zeroes after the decimal, we
|
||
-- use exponents to compress the representation.
|
||
-- this is mostly done for memory usage reasons:
|
||
-- the size of the representation would otherwise
|
||
-- grow exponentially in the value of exponent.
|
||
let exp : Int := 9 + countDigits m - (e : Int)
|
||
let exp := if exp < 0 then exp else 0
|
||
let e' := (10 : Int) ^ (e - exp.natAbs)
|
||
let left := (m / e').repr
|
||
let right := e' + m % e'
|
||
|>.repr.toSubstring.drop 1
|
||
|>.dropRightWhile (fun c => c = '0')
|
||
|>.toString
|
||
let exp := if exp = 0 then "" else "e" ++ exp.repr
|
||
s!"{sign}{left}.{right}{exp}"
|
||
|
||
-- shift a JsonNumber by a specified amount of places to the left
|
||
protected def shiftl : JsonNumber → Nat → JsonNumber
|
||
-- if s ≤ e, then 10 ^ (s - e) = 1, and hence the mantissa remains unchanged.
|
||
-- otherwise, the expression pads the mantissa with zeroes
|
||
-- to accomodate for the remaining places to shift.
|
||
| ⟨m, e⟩, s => ⟨m * (10 ^ (s - e) : Nat), e - s⟩
|
||
|
||
-- shift a JsonNumber by a specified amount of places to the right
|
||
protected def shiftr : JsonNumber → Nat → JsonNumber
|
||
| ⟨m, e⟩, s => ⟨m, e + s⟩
|
||
|
||
instance : ToString JsonNumber := ⟨JsonNumber.toString⟩
|
||
|
||
instance : Repr JsonNumber where
|
||
reprPrec | ⟨m, e⟩, _ => Std.Format.bracket "⟨" (repr m ++ "," ++ repr e) "⟩"
|
||
|
||
end JsonNumber
|
||
|
||
def strLt (a b : String) := Decidable.decide (a < b)
|
||
|
||
open Std (RBNode RBNode.leaf)
|
||
|
||
inductive Json where
|
||
| null
|
||
| bool (b : Bool)
|
||
| num (n : JsonNumber)
|
||
| str (s : String)
|
||
| arr (elems : Array Json)
|
||
-- uses RBNode instead of RBMap because RBMap is a def
|
||
-- and thus currently cannot be used to define a type that
|
||
-- is recursive in one of its parameters
|
||
| obj (kvPairs : RBNode String (fun _ => Json))
|
||
deriving Inhabited
|
||
|
||
namespace Json
|
||
|
||
-- HACK(Marc): temporary ugliness until we can use RBMap for JSON objects
|
||
def mkObj (o : List (String × Json)) : Json :=
|
||
obj $ do
|
||
let mut kvPairs := RBNode.leaf
|
||
for ⟨k, v⟩ in o do
|
||
kvPairs := kvPairs.insert compare k v
|
||
kvPairs
|
||
|
||
instance : Coe Nat Json := ⟨fun n => Json.num n⟩
|
||
instance : Coe Int Json := ⟨fun n => Json.num n⟩
|
||
instance : Coe String Json := ⟨Json.str⟩
|
||
instance : Coe Bool Json := ⟨Json.bool⟩
|
||
|
||
def isNull : Json -> Bool
|
||
| null => true
|
||
| _ => false
|
||
|
||
def getObj? : Json → Option (RBNode String (fun _ => Json))
|
||
| obj kvs => kvs
|
||
| _ => none
|
||
|
||
def getArr? : Json → Option (Array Json)
|
||
| arr a => a
|
||
| _ => none
|
||
|
||
def getStr? : Json → Option String
|
||
| str s => some s
|
||
| _ => none
|
||
|
||
def getNat? : Json → Option Nat
|
||
| (n : Nat) => some n
|
||
| _ => none
|
||
|
||
def getInt? : Json → Option Int
|
||
| (i : Int) => some i
|
||
| _ => none
|
||
|
||
def getBool? : Json → Option Bool
|
||
| (b : Bool) => some b
|
||
| _ => none
|
||
|
||
def getNum? : Json → Option JsonNumber
|
||
| num n => n
|
||
| _ => none
|
||
|
||
def getObjVal? : Json → String → Option Json
|
||
| obj kvs, k => kvs.find compare k
|
||
| _ , _ => none
|
||
|
||
def getArrVal? : Json → Nat → Option Json
|
||
| arr a, i => a.get? i
|
||
| _ , _ => none
|
||
|
||
def getObjValD (j : Json) (k : String) : Json :=
|
||
(j.getObjVal? k).getD null
|
||
|
||
def setObjVal! : Json → String → Json → Json
|
||
| obj kvs, k, v => obj <| kvs.insert compare k v
|
||
| j , _, _ => panic! "Json.setObjVal!: not an object: {j}"
|
||
|
||
inductive Structured where
|
||
| arr (elems : Array Json)
|
||
| obj (kvPairs : RBNode String (fun _ => Json))
|
||
|
||
instance : Coe (Array Json) Structured := ⟨Structured.arr⟩
|
||
instance : Coe (RBNode String (fun _ => Json)) Structured := ⟨Structured.obj⟩
|
||
|
||
end Json
|
||
end Lean
|