This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

mlang / Mlang / Interpreter.lean
65 kB 1709 lines
1import Mlang.Nickel 2import Mlang.Http 3import Mlang.NickelHost 4 5namespace Mlang 6 7open Nickel 8 9abbrev Name := String 10structure Rational where 11 num : Int 12 den : Nat 13 deriving Repr, Inhabited, BEq 14 15abbrev Decimal := Rational 16 17mutual 18 inductive Generator where 19 | exactRatio : Rational Generator 20 | exactDecimal : Decimal Generator 21 | rationalRefiner : LazyReal Generator 22 | decimalRefiner : LazyReal Generator 23 | piChudnovsky : Generator 24 | sqrt : Decimal Generator 25 | add : Generator Generator Generator 26 | sub : Generator Generator Generator 27 | mul : Generator Generator Generator 28 | div : Generator Generator Generator 29 deriving Repr, Inhabited, BEq 30 31 inductive LazyReal where 32 | exact : Decimal LazyReal 33 | ratioProcess : Rational LazyReal 34 | decimalProcess : Decimal LazyReal 35 | pi : LazyReal 36 | sqrt : Decimal LazyReal 37 | add : LazyReal LazyReal LazyReal 38 | sub : LazyReal LazyReal LazyReal 39 | mul : LazyReal LazyReal LazyReal 40 | div : LazyReal LazyReal LazyReal 41 | generated : Generator LazyReal 42 deriving Repr, Inhabited, BEq 43end 44 45def normalizeDecimalParts (negative : Bool) (whole frac : String) : String := 46 let wholeChars := whole.toList.dropWhile (· = '0') 47 let whole' := if wholeChars.isEmpty then "0" else String.ofList wholeChars 48 let fracChars := frac.toList.reverse.dropWhile (· = '0') |>.reverse 49 let frac' := if fracChars.isEmpty then "0" else String.ofList fracChars 50 let sign := if negative && !(whole' = "0" && frac' = "0") then "-" else "" 51 sign ++ whole' ++ "." ++ frac' 52 53def parseDecimalParts (repr : String) : Bool × String × String := 54 let chars := repr.toList 55 let (negative, chars) := 56 match chars with 57 | '-' :: rest => (true, rest) 58 | _ => (false, chars) 59 let rec split (acc : List Char) : List Char List Char × List Char 60 | [] => (acc.reverse, []) 61 | '.' :: rest => (acc.reverse, rest) 62 | c :: rest => split (c :: acc) rest 63 let (wholeChars, fracChars) := split [] chars 64 (negative, String.ofList wholeChars, String.ofList fracChars) 65 66def pow10 : Nat Int 67 | 0 => 1 68 | n + 1 => 10 * pow10 n 69 70def powNat (base : Nat) : Nat Nat 71 | 0 => 1 72 | n + 1 => base * powNat base n 73 74def mkRational (num : Int) (den : Nat) : Rational := 75 if den = 0 then 76 { num := 0, den := 1 } 77 else if num = 0 then 78 { num := 0, den := 1 } 79 else 80 let g := Nat.gcd num.natAbs den 81 { num := num / Int.ofNat g, den := den / g } 82 83def parseDecimal (repr : String) : Decimal := 84 let (negative, whole, frac) := parseDecimalParts repr 85 let digits := whole ++ frac 86 let magnitude := String.toInt? digits |>.getD 0 87 let signed := if negative then -magnitude else magnitude 88 mkRational signed (powNat 10 frac.length) 89 90def renderDecimalScaled (scaled : Int) (scale : Nat) : String := 91 let negative := scaled < 0 92 let mag := scaled.natAbs 93 let digits := toString mag 94 if scale = 0 then 95 normalizeDecimalParts negative digits "0" 96 else if digits.length <= scale then 97 let zeros := String.ofList (List.replicate (scale - digits.length + 1) '0') 98 let padded := zeros ++ digits 99 let wholeLen := padded.length - scale 100 normalizeDecimalParts negative (padded.take wholeLen |>.toString) (padded.drop wholeLen |>.toString) 101 else 102 let wholeLen := digits.length - scale 103 normalizeDecimalParts negative (digits.take wholeLen |>.toString) (digits.drop wholeLen |>.toString) 104 105def decimalOfInt (n : Int) : Decimal := 106 mkRational n 1 107 108def addDecimals (lhs rhs : Decimal) : Decimal := 109 mkRational (lhs.num * Int.ofNat rhs.den + rhs.num * Int.ofNat lhs.den) (lhs.den * rhs.den) 110 111def subDecimals (lhs rhs : Decimal) : Decimal := 112 mkRational (lhs.num * Int.ofNat rhs.den - rhs.num * Int.ofNat lhs.den) (lhs.den * rhs.den) 113 114def mulDecimals (lhs rhs : Decimal) : Decimal := 115 mkRational (lhs.num * rhs.num) (lhs.den * rhs.den) 116 117def rationalEq (lhs rhs : Decimal) : Bool := 118 lhs.num == rhs.num && lhs.den == rhs.den 119 120partial def countFactor (factor : Nat) (n : Nat) : Nat := 121 let rec loop (count current : Nat) := 122 if current = 0 then count 123 else if current % factor = 0 then loop (count + 1) (current / factor) 124 else count 125 loop 0 n 126 127partial def stripFactor (factor : Nat) (n : Nat) : Nat := 128 let rec loop (current : Nat) := 129 if current = 0 then 0 130 else if current % factor = 0 then loop (current / factor) 131 else current 132 loop n 133 134def renderDecimal (d : Decimal) : String := 135 if d.den = 1 then 136 renderDecimalScaled d.num 0 137 else 138 let twos := countFactor 2 d.den 139 let withoutTwos := stripFactor 2 d.den 140 let fives := countFactor 5 withoutTwos 141 let rest := stripFactor 5 withoutTwos 142 if rest = 1 then 143 let scale := max twos fives 144 let mult2 := powNat 2 (scale - twos) 145 let mult5 := powNat 5 (scale - fives) 146 let scaledNum := d.num * Int.ofNat (mult2 * mult5) 147 renderDecimalScaled scaledNum scale 148 else 149 s!"{d.num}/{d.den}" 150 151def isTerminatingDecimal (q : Rational) : Bool := 152 if q.den = 1 then 153 true 154 else 155 let withoutTwos := stripFactor 2 q.den 156 let withoutFives := stripFactor 5 withoutTwos 157 withoutFives = 1 158 159inductive Effect where 160 | error : Effect 161 | io : Effect 162 deriving Repr, Inhabited, BEq 163 164abbrev Effects := List Effect 165 166def Effects.contains (effects : Effects) (effect : Effect) : Bool := 167 effects.any (· == effect) 168 169def Effects.insert (effects : Effects) (effect : Effect) : Effects := 170 if effects.contains effect then effects else effect :: effects 171 172def Effects.union (lhs rhs : Effects) : Effects := 173 rhs.foldl Effects.insert lhs 174 175def Effects.erase (effects : Effects) (target : Effect) : Effects := 176 effects.filter (· != target) 177 178def Effects.render : Effects String 179 | [] => "{}" 180 | effects => 181 let names := effects.reverse.map (fun 182 | .error => "Error" 183 | .io => "IO") 184 "{" ++ String.intercalate ", " names ++ "}" 185 186inductive Ty where 187 | number : Ty 188 | int : Ty 189 | decimal : Ty 190 | rational : Ty 191 | lazyReal : Ty 192 | stability : Ty 193 | bool : Ty 194 | string : Ty 195 | data : Ty 196 | dataInt : Ty 197 | dataDecimal : Ty 198 | dataRational : Ty 199 | dataBool : Ty 200 | dataString : Ty 201 | dataNull : Ty 202 | dataArray : Ty Ty 203 | dataRecord : List (String × Ty) Ty 204 | list : Ty 205 | result : Ty 206 | error : Ty 207 | funTy : Ty Effects Ty Ty 208 deriving Repr, Inhabited, BEq 209 210inductive Data where 211 | null : Data 212 | bool : Bool Data 213 | int : Int Data 214 | decimal : Decimal Data 215 | string : String Data 216 | array : List Data Data 217 | record : List (String × Data) Data 218 deriving Repr, Inhabited, BEq 219 220inductive Expr where 221 | int : Int Expr 222 | decimal : Decimal Expr 223 | bool : Bool Expr 224 | string : String Expr 225 | null : Expr 226 | var : Name Expr 227 | arrayE : List Expr Expr 228 | recordE : List (String × Expr) Expr 229 | add : Expr Expr Expr 230 | sub : Expr Expr Expr 231 | mul : Expr Expr Expr 232 | div : Expr Expr Expr 233 | eq : Expr Expr Expr 234 | letE : Name Expr Expr Expr 235 | ifE : Expr Expr Expr Expr 236 | tryE : Expr Name Expr Expr 237 | parMapE : Name Expr Expr Expr 238 | lam : Name Ty Expr Expr 239 | app : Expr Expr Expr 240 deriving Repr, Inhabited 241 242inductive Numeric where 243 | exact : Rational Numeric 244 | composite : LazyReal Numeric 245 deriving Repr, Inhabited, BEq 246 247inductive Value where 248 | number : Numeric Value 249 | stability : String Value 250 | bool : Bool Value 251 | string : String Value 252 | data : Data Value 253 | list : List Value Value 254 | resultOk : Value Value 255 | resultErr : String Value 256 | error : String Value 257 | builtinReadFile : Value 258 | builtinHttpGet : Value 259 | builtinReadFileNickel : Value 260 | builtinParseNickel : Value 261 | builtinParseJson : Value 262 | builtinGetAturi : Value 263 | builtinDataGet : Value 264 | builtinDataGetField : Value Value 265 | builtinDataAt : Value 266 | builtinDataAtIndex : Value Value 267 | builtinDataAsInt : Value 268 | builtinDataAsDecimal : Value 269 | builtinRationalToInt : Value 270 | builtinRationalToDecimal : Value 271 | builtinDataAsString : Value 272 | builtinDataAsBool : Value 273 | builtinDataToJson : Value 274 | builtinDataToNickel : Value 275 | builtinSqrt : Value 276 | builtinProcess : Value 277 | builtinSynthesize : Value 278 | builtinFormula : Value 279 | builtinApprox : Value 280 | builtinApproxStep : Value Value 281 | builtinStabilize : Value 282 | builtinStabilizeStep : Value Value 283 | builtinBounds : Value 284 | builtinBoundsStep : Value Value 285 | builtinRationalStability : Value 286 | builtinDecimalStability : Value 287 | closure : Name Expr List (Name × Value) Value 288 deriving Repr, Inhabited 289 290abbrev Env := List (Name × Value) 291 292inductive Observation where 293 | asRational : Observation 294 | asDecimal : Observation 295 | asInt : Observation 296 | rationalStability : Observation 297 | decimalStability : Observation 298 | getField : String Observation 299 | atIndex : Nat Observation 300 | approx : Nat Observation 301 | bounds : Nat Observation 302 deriving Repr, Inhabited 303 304inductive ObservationResult where 305 | rational : Rational ObservationResult 306 | decimal : Decimal ObservationResult 307 | int : Int ObservationResult 308 | stability : String ObservationResult 309 | data : Data ObservationResult 310 | bounds : Decimal Decimal ObservationResult 311 deriving Repr, Inhabited 312 313inductive RuntimeError where 314 | unboundVariable : Name RuntimeError 315 | typeError : String RuntimeError 316 | divisionByZero : RuntimeError 317 | ioError : String RuntimeError 318 deriving Repr, Inhabited 319 320abbrev EvalM := EIO RuntimeError 321 322def numericOfInt (n : Int) : Numeric := 323 .exact (decimalOfInt n) 324 325def numericOfDecimal (d : Decimal) : Numeric := 326 .exact d 327 328def valueToDecimal : Value EvalM Decimal 329 | .number (.exact q) => pure q 330 | _ => throw (.typeError "expected a numeric value") 331 332def divDecimals (lhs rhs : Decimal) : EvalM Decimal := do 333 if rhs.num = 0 then 334 throw .divisionByZero 335 else 336 pure (mkRational (lhs.num * Int.ofNat rhs.den) (lhs.den * rhs.num.natAbs) |> 337 fun q => if rhs.num < 0 then { q with num := -q.num } else q) 338 339def valueToLazyReal : Value EvalM LazyReal 340 | .number (.composite r) => pure r 341 | .number (.exact q) => pure (.exact q) 342 | _ => throw (.typeError "expected a numeric or LazyReal value") 343 344def generatorComplexity : Generator Nat 345 | .exactRatio _ => 1 346 | .exactDecimal _ => 1 347 | .rationalRefiner _ => 2 348 | .decimalRefiner _ => 2 349 | .piChudnovsky => 3 350 | .sqrt _ => 2 351 | .add lhs rhs => 1 + generatorComplexity lhs + generatorComplexity rhs 352 | .sub lhs rhs => 1 + generatorComplexity lhs + generatorComplexity rhs 353 | .mul lhs rhs => 1 + generatorComplexity lhs + generatorComplexity rhs 354 | .div lhs rhs => 1 + generatorComplexity lhs + generatorComplexity rhs 355 356def selectFirstGoodEnough [Inhabited α] (candidates : List α) (goodEnough : α Bool) : α := 357 match candidates.find? goodEnough with 358 | some candidate => candidate 359 | none => 360 match candidates with 361 | candidate :: _ => candidate 362 | [] => default 363 364def generatorGoodEnough : Generator Bool 365 | .exactRatio _ => true 366 | .exactDecimal _ => true 367 | .rationalRefiner _ => true 368 | .decimalRefiner _ => true 369 | .piChudnovsky => true 370 | .sqrt _ => true 371 | .add lhs rhs => generatorGoodEnough lhs && generatorGoodEnough rhs 372 | .sub lhs rhs => generatorGoodEnough lhs && generatorGoodEnough rhs 373 | .mul lhs rhs => generatorGoodEnough lhs && generatorGoodEnough rhs 374 | .div lhs rhs => generatorGoodEnough lhs && generatorGoodEnough rhs 375 376partial def synthesizedCandidates (r : LazyReal) : List Generator := 377 match r with 378 | .exact q => 379 if isTerminatingDecimal q then 380 [.exactDecimal q, .exactRatio q] 381 else 382 [.exactRatio q, .decimalRefiner (.exact q)] 383 | .ratioProcess q => 384 if isTerminatingDecimal q then 385 [.exactDecimal q, .exactRatio q] 386 else 387 [.exactRatio q, .decimalRefiner (.ratioProcess q)] 388 | .decimalProcess d => 389 [.exactDecimal d, .exactRatio d] 390 | .generated g => 391 [g, .decimalRefiner (.generated g), .rationalRefiner (.generated g)] 392 | .pi => 393 [.decimalRefiner .pi, .rationalRefiner .pi, .piChudnovsky] 394 | .sqrt d => 395 [.decimalRefiner (.sqrt d), .rationalRefiner (.sqrt d), .sqrt d] 396 | .add lhs rhs => 397 let lhsGen := selectFirstGoodEnough (synthesizedCandidates lhs) generatorGoodEnough 398 let rhsGen := selectFirstGoodEnough (synthesizedCandidates rhs) generatorGoodEnough 399 [.add lhsGen rhsGen, .decimalRefiner (.add lhs rhs), .rationalRefiner (.add lhs rhs)] 400 | .sub lhs rhs => 401 let lhsGen := selectFirstGoodEnough (synthesizedCandidates lhs) generatorGoodEnough 402 let rhsGen := selectFirstGoodEnough (synthesizedCandidates rhs) generatorGoodEnough 403 [.sub lhsGen rhsGen, .decimalRefiner (.sub lhs rhs), .rationalRefiner (.sub lhs rhs)] 404 | .mul lhs rhs => 405 let lhsGen := selectFirstGoodEnough (synthesizedCandidates lhs) generatorGoodEnough 406 let rhsGen := selectFirstGoodEnough (synthesizedCandidates rhs) generatorGoodEnough 407 [.mul lhsGen rhsGen, .decimalRefiner (.mul lhs rhs), .rationalRefiner (.mul lhs rhs)] 408 | .div lhs rhs => 409 let lhsGen := selectFirstGoodEnough (synthesizedCandidates lhs) generatorGoodEnough 410 let rhsGen := selectFirstGoodEnough (synthesizedCandidates rhs) generatorGoodEnough 411 [.div lhsGen rhsGen, .decimalRefiner (.div lhs rhs), .rationalRefiner (.div lhs rhs)] 412 413def chooseSynthesizedGenerator (r : LazyReal) : Generator := 414 selectFirstGoodEnough (synthesizedCandidates r) generatorGoodEnough 415 416mutual 417 partial def renderGeneratorFormula : Generator String 418 | .exactRatio q => s!"{q.num}/{q.den}" 419 | .exactDecimal d => renderDecimal d 420 | .rationalRefiner r => s!"rationalRefine({renderLazyFormula r})" 421 | .decimalRefiner r => s!"decimalRefine({renderLazyFormula r})" 422 | .piChudnovsky => 423 "426880 * sqrt(10005) / sum(k => ((-1)^k * (6k)! * (13591409 + 545140134k)) / ((3k)! * (k!)^3 * 640320^(3k)), k = 0..inf)" 424 | .sqrt d => s!"sqrt({renderDecimal d})" 425 | .add lhs rhs => s!"({renderGeneratorFormula lhs}) + ({renderGeneratorFormula rhs})" 426 | .sub lhs rhs => s!"({renderGeneratorFormula lhs}) - ({renderGeneratorFormula rhs})" 427 | .mul lhs rhs => s!"({renderGeneratorFormula lhs}) * ({renderGeneratorFormula rhs})" 428 | .div lhs rhs => s!"({renderGeneratorFormula lhs}) / ({renderGeneratorFormula rhs})" 429 430 partial def renderLazyFormula : LazyReal String 431 | .exact q => renderDecimal q 432 | .ratioProcess q => s!"ratio({q.num}/{q.den})" 433 | .decimalProcess d => s!"decimal({renderDecimal d})" 434 | .pi => "pi" 435 | .sqrt d => s!"sqrt({renderDecimal d})" 436 | .add lhs rhs => s!"({renderLazyFormula lhs}) + ({renderLazyFormula rhs})" 437 | .sub lhs rhs => s!"({renderLazyFormula lhs}) - ({renderLazyFormula rhs})" 438 | .mul lhs rhs => s!"({renderLazyFormula lhs}) * ({renderLazyFormula rhs})" 439 | .div lhs rhs => s!"({renderLazyFormula lhs}) / ({renderLazyFormula rhs})" 440 | .generated g => renderGeneratorFormula g 441end 442 443def formulaOfValue (value : Value) : EvalM String := do 444 match value with 445 | .number (.exact q) => 446 pure (renderGeneratorFormula (chooseSynthesizedGenerator (.exact q))) 447 | .number (.composite r) => 448 pure (renderGeneratorFormula (chooseSynthesizedGenerator r)) 449 | _ => 450 let q valueToDecimal value 451 pure (renderGeneratorFormula (chooseSynthesizedGenerator (.exact q))) 452 453def synthesizeNumeric (value : Value) : EvalM Numeric := do 454 match value with 455 | .number (.exact q) => 456 pure (.composite (.generated (chooseSynthesizedGenerator (.exact q)))) 457 | .number (.composite r) => 458 pure (.composite (.generated (chooseSynthesizedGenerator r))) 459 | _ => 460 let q valueToDecimal value 461 pure (.composite (.generated (chooseSynthesizedGenerator (.exact q)))) 462 463partial def natSqrtSearch (target lo hi : Nat) : Nat := 464 if lo + 1 >= hi then 465 lo 466 else 467 let mid := lo + (hi - lo) / 2 468 if mid * mid target then 469 natSqrtSearch target mid hi 470 else 471 natSqrtSearch target lo mid 472 473def natSqrtFloor (target : Nat) : Nat := 474 natSqrtSearch target 0 (target + 1) 475 476def sqrtBoundsOfDecimal (d : Decimal) (steps : Nat) : EvalM (Decimal × Decimal) := do 477 if d.num < 0 then 478 throw (.typeError "sqrt expects a non-negative decimal") 479 else 480 let scale := steps 481 let scaledTarget := d.num.natAbs * powNat 10 (2 * scale) / d.den 482 let loNat := natSqrtFloor scaledTarget 483 let lo := mkRational (Int.ofNat loNat) (powNat 10 scale) 484 let step := mkRational 1 (powNat 10 scale) 485 pure (lo, addDecimals lo step) 486 487def natFactorial : Nat Nat 488 | 0 => 1 489 | n + 1 => (n + 1) * natFactorial n 490 491def chudnovskyTermMag (k : Nat) : Decimal := 492 let sixFact := natFactorial (6 * k) 493 let threeFact := natFactorial (3 * k) 494 let kFact := natFactorial k 495 let coeff : Int := 13591409 + 545140134 * Int.ofNat k 496 let num := Int.ofNat sixFact * coeff 497 let den := threeFact * kFact * kFact * kFact * powNat 640320 (3 * k) 498 mkRational num den 499 500def chudnovskySignedTerm (k : Nat) : Decimal := 501 let mag := chudnovskyTermMag k 502 if k % 2 = 0 then mag else { mag with num := -mag.num } 503 504def chudnovskySum (terms : Nat) : Decimal := 505 let rec loop (k : Nat) (acc : Decimal) := 506 if k < terms then 507 loop (k + 1) (addDecimals acc (chudnovskySignedTerm k)) 508 else 509 acc 510 loop 0 (decimalOfInt 0) 511 512def piBounds (steps : Nat) : EvalM (Decimal × Decimal) := do 513 let terms := steps / 14 + 1 514 let sum := chudnovskySum terms 515 let tail := chudnovskyTermMag terms 516 let sumLo := subDecimals sum tail 517 let sumHi := addDecimals sum tail 518 let (sqrtLo, sqrtHi) sqrtBoundsOfDecimal (decimalOfInt 10005) (steps + 2) 519 let coeff := decimalOfInt 426880 520 let cLo := mulDecimals coeff sqrtLo 521 let cHi := mulDecimals coeff sqrtHi 522 let lo divDecimals cLo sumHi 523 let hi divDecimals cHi sumLo 524 pure (lo, hi) 525 526def minDecimal (a b : Decimal) : Decimal := 527 if a.num * Int.ofNat b.den <= b.num * Int.ofNat a.den then a else b 528 529def maxDecimal (a b : Decimal) : Decimal := 530 if a.num * Int.ofNat b.den >= b.num * Int.ofNat a.den then a else b 531 532def roundRationalToScale (q : Rational) (scale : Nat) : Rational := 533 let factor := powNat 10 scale 534 let scaledNum := q.num * Int.ofNat factor 535 let denInt := Int.ofNat q.den 536 let adjust := Int.ofNat (q.den / 2) 537 let rounded := 538 if scaledNum < 0 then 539 (scaledNum - adjust) / denInt 540 else 541 (scaledNum + adjust) / denInt 542 mkRational rounded factor 543 544def mulBounds (lo1 hi1 lo2 hi2 : Decimal) : Decimal × Decimal := 545 let p1 := mulDecimals lo1 lo2 546 let p2 := mulDecimals lo1 hi2 547 let p3 := mulDecimals hi1 lo2 548 let p4 := mulDecimals hi1 hi2 549 let lo := minDecimal (minDecimal p1 p2) (minDecimal p3 p4) 550 let hi := maxDecimal (maxDecimal p1 p2) (maxDecimal p3 p4) 551 (lo, hi) 552 553mutual 554 partial def boundsGenerator (g : Generator) (steps : Nat) : EvalM (Decimal × Decimal) := do 555 match g with 556 | .exactRatio q => pure (q, q) 557 | .exactDecimal d => pure (d, d) 558 | .rationalRefiner r => 559 let q approxLazyReal r steps 560 pure (q, q) 561 | .decimalRefiner r => 562 let q approxLazyReal r steps 563 pure (roundRationalToScale q steps, roundRationalToScale q steps) 564 | .piChudnovsky => piBounds steps 565 | .sqrt d => sqrtBoundsOfDecimal d steps 566 | .add lhs rhs => 567 let (llo, lhi) boundsGenerator lhs steps 568 let (rlo, rhi) boundsGenerator rhs steps 569 pure (addDecimals llo rlo, addDecimals lhi rhi) 570 | .sub lhs rhs => 571 let (llo, lhi) boundsGenerator lhs steps 572 let (rlo, rhi) boundsGenerator rhs steps 573 pure (subDecimals llo rhi, subDecimals lhi rlo) 574 | .mul lhs rhs => 575 let (llo, lhi) boundsGenerator lhs steps 576 let (rlo, rhi) boundsGenerator rhs steps 577 pure (mulBounds llo lhi rlo rhi) 578 | .div lhs rhs => 579 let (llo, lhi) boundsGenerator lhs steps 580 let (rlo, rhi) boundsGenerator rhs steps 581 if rlo.num <= 0 && 0 <= rhi.num then 582 throw (.typeError "generated division interval crosses zero") 583 else 584 let invLo divDecimals (decimalOfInt 1) rhi 585 let invHi divDecimals (decimalOfInt 1) rlo 586 pure (mulBounds llo lhi invLo invHi) 587 588 partial def approxGenerator (g : Generator) (steps : Nat) : EvalM Decimal := do 589 match g with 590 | .exactRatio q => pure q 591 | .exactDecimal d => pure d 592 | .rationalRefiner r => approxLazyReal r steps 593 | .decimalRefiner r => 594 let q approxLazyReal r steps 595 pure (roundRationalToScale q steps) 596 | .piChudnovsky => 597 let (lo, hi) piBounds steps 598 divDecimals (addDecimals lo hi) (decimalOfInt 2) 599 | .sqrt d => 600 let (lo, hi) sqrtBoundsOfDecimal d steps 601 divDecimals (addDecimals lo hi) (decimalOfInt 2) 602 | .add _ _ | .sub _ _ | .mul _ _ | .div _ _ => 603 let (lo, hi) boundsGenerator g steps 604 divDecimals (addDecimals lo hi) (decimalOfInt 2) 605 606 partial def boundsLazyReal (r : LazyReal) (steps : Nat) : EvalM (Decimal × Decimal) := do 607 match r with 608 | .exact d => pure (d, d) 609 | .ratioProcess q => pure (q, q) 610 | .decimalProcess d => pure (d, d) 611 | .generated g => boundsGenerator g steps 612 | .pi => piBounds steps 613 | .sqrt d => sqrtBoundsOfDecimal d steps 614 | .add lhs rhs => 615 let (llo, lhi) boundsLazyReal lhs steps 616 let (rlo, rhi) boundsLazyReal rhs steps 617 pure (addDecimals llo rlo, addDecimals lhi rhi) 618 | .sub lhs rhs => 619 let (llo, lhi) boundsLazyReal lhs steps 620 let (rlo, rhi) boundsLazyReal rhs steps 621 pure (subDecimals llo rhi, subDecimals lhi rlo) 622 | .mul lhs rhs => 623 let (llo, lhi) boundsLazyReal lhs steps 624 let (rlo, rhi) boundsLazyReal rhs steps 625 pure (mulBounds llo lhi rlo rhi) 626 | .div lhs rhs => 627 let (llo, lhi) boundsLazyReal lhs steps 628 let (rlo, rhi) boundsLazyReal rhs steps 629 if rlo.num <= 0 && 0 <= rhi.num then 630 throw (.typeError "lazy division interval crosses zero") 631 else 632 let invLo divDecimals (decimalOfInt 1) rhi 633 let invHi divDecimals (decimalOfInt 1) rlo 634 pure (mulBounds llo lhi invLo invHi) 635 636 partial def approxLazyReal (r : LazyReal) (steps : Nat) : EvalM Decimal := do 637 match r with 638 | .exact d => pure d 639 | .ratioProcess q => pure q 640 | .decimalProcess d => pure d 641 | .generated g => approxGenerator g steps 642 | .pi => 643 let (lo, hi) piBounds steps 644 divDecimals (addDecimals lo hi) (decimalOfInt 2) 645 | .sqrt d => 646 let (lo, hi) sqrtBoundsOfDecimal d steps 647 divDecimals (addDecimals lo hi) (decimalOfInt 2) 648 | .add _ _ | .sub _ _ | .mul _ _ | .div _ _ => 649 let (lo, hi) boundsLazyReal r steps 650 divDecimals (addDecimals lo hi) (decimalOfInt 2) 651end 652 653def stabilizeNumeric (value : Value) (steps : Nat) : EvalM Rational := do 654 match value with 655 | .number (.exact q) => pure (roundRationalToScale q steps) 656 | .number (.composite r) => 657 let q approxLazyReal r steps 658 pure (roundRationalToScale q steps) 659 | _ => 660 let q valueToDecimal value 661 pure (roundRationalToScale q steps) 662 663def Env.lookup (env : Env) (name : Name) : Option Value := 664 match env with 665 | [] => none 666 | (n, v) :: rest => if n = name then some v else rest.lookup name 667 668def expectInt : Value EvalM Int 669 | .number (.exact q) => 670 if q.den = 1 then pure q.num else throw (.typeError "expected an integer") 671 | _ => throw (.typeError "expected an integer") 672 673def expectDecimal : Value EvalM Decimal 674 | .number (.exact q) => pure q 675 | _ => throw (.typeError "expected a decimal") 676 677def expectLazyReal : Value EvalM LazyReal 678 | .number (.composite r) => pure r 679 | _ => throw (.typeError "expected a LazyReal") 680 681def expectBool : Value EvalM Bool 682 | .bool b => pure b 683 | _ => throw (.typeError "expected a boolean") 684 685def expectString : Value EvalM String 686 | .string s => pure s 687 | _ => throw (.typeError "expected a string") 688 689def expectData : Value EvalM Data 690 | .data term => pure term 691 | _ => throw (.typeError "expected a data value") 692 693def expectWholeNat (value : Value) : EvalM Nat := do 694 let q valueToDecimal value 695 if q.den 1 then 696 throw (.typeError "expected a whole rational") 697 else if q.num < 0 then 698 throw (.typeError "expected a non-negative whole rational") 699 else 700 pure q.num.toNat 701 702def rationalStabilityOfValue (value : Value) : String := 703 match value with 704 | .number (.composite (.ratioProcess _)) => "Immediate" 705 | .number (.composite (.decimalProcess _)) => "Immediate" 706 | .number (.composite (.generated (.exactRatio _))) => "Immediate" 707 | .number (.composite (.generated (.exactDecimal _))) => "Immediate" 708 | .number (.exact _) => "Immediate" 709 | .number (.composite _) => "Refining" 710 | _ => "Unavailable" 711 712def decimalStabilityOfRational (q : Rational) : String := 713 if isTerminatingDecimal q then "Terminating" else "Periodic" 714 715def decimalStabilityOfValue (value : Value) : String := 716 match value with 717 | .number (.composite (.ratioProcess q)) => decimalStabilityOfRational q 718 | .number (.composite (.decimalProcess d)) => decimalStabilityOfRational d 719 | .number (.composite (.generated (.exactRatio q))) => decimalStabilityOfRational q 720 | .number (.composite (.generated (.exactDecimal d))) => decimalStabilityOfRational d 721 | .number (.exact q) => decimalStabilityOfRational q 722 | .number (.composite _) => "Refining" 723 | _ => "Unavailable" 724 725def observeValue (value : Value) (obs : Observation) : EvalM ObservationResult := do 726 match obs with 727 | .asRational => 728 match value with 729 | .number (.exact q) => pure (.rational q) 730 | .number (.composite _) => 731 throw (.typeError s!"rational observation failed: number is {rationalStabilityOfValue value}, not Immediate") 732 | _ => 733 pure (.rational ( valueToDecimal value)) 734 | .asDecimal => 735 match value with 736 | .number (.exact q) => 737 if isTerminatingDecimal q then 738 pure (.decimal q) 739 else 740 throw (.typeError s!"decimal observation failed: number is {decimalStabilityOfValue value}, not Terminating") 741 | .number (.composite _) => 742 throw (.typeError s!"decimal observation failed: number is {decimalStabilityOfValue value}, not Terminating") 743 | _ => 744 let q valueToDecimal value 745 if isTerminatingDecimal q then 746 pure (.decimal q) 747 else 748 throw (.typeError s!"decimal observation failed: number is {decimalStabilityOfValue value}, not Terminating") 749 | .asInt => 750 match value with 751 | .number (.exact q) => 752 if q.den = 1 then 753 pure (.int q.num) 754 else 755 throw (.typeError "integer observation failed: number is not whole") 756 | .number (.composite _) => 757 throw (.typeError s!"integer observation failed: number is {rationalStabilityOfValue value}, not Immediate") 758 | _ => 759 let q valueToDecimal value 760 if q.den = 1 then 761 pure (.int q.num) 762 else 763 throw (.typeError "integer observation failed: number is not whole") 764 | .rationalStability => 765 pure (.stability (rationalStabilityOfValue value)) 766 | .decimalStability => 767 pure (.stability (decimalStabilityOfValue value)) 768 | .getField key => 769 match value with 770 | .data (.record fields) => 771 match fields.find? (fun (name, _) => name = key) with 772 | some (_, field) => pure (.data field) 773 | none => throw (.typeError s!"missing field: {key}") 774 | _ => throw (.typeError "get expects a record") 775 | .atIndex idx => 776 let rec arrayGet? : List Data Nat Option Data 777 | [], _ => none 778 | x :: _, 0 => some x 779 | _ :: xs, n + 1 => arrayGet? xs n 780 match value with 781 | .data (.array items) => 782 match arrayGet? items idx with 783 | some item => pure (.data item) 784 | none => throw (RuntimeError.typeError s!"index out of bounds: {idx}") 785 | _ => throw (RuntimeError.typeError "at expects an array") 786 | .approx steps => 787 let r valueToLazyReal value 788 pure (.rational ( approxLazyReal r steps)) 789 | .bounds steps => 790 let r valueToLazyReal value 791 let (lo, hi) boundsLazyReal r steps 792 pure (.bounds lo hi) 793 794def valueToData : Value EvalM Data 795 | .data term => pure term 796 | .number (.exact q) => pure (.decimal q) 797 | .bool b => pure (.bool b) 798 | .string s => pure (.string s) 799 | _ => throw (.typeError "data literals only support Int, Decimal, Bool, String, and Data values") 800 801partial def dataType : Data Ty 802 | .null => .dataNull 803 | .bool _ => .dataBool 804 | .int _ => .dataInt 805 | .decimal _ => .dataRational 806 | .string _ => .dataString 807 | .array [] => .dataArray .data 808 | .array (x :: xs) => 809 let itemTy := dataType x 810 if xs.all (fun item => dataType item == itemTy) then 811 .dataArray itemTy 812 else 813 .dataArray .data 814 | .record fields => 815 .dataRecord (fields.map (fun (name, value) => (name, dataType value))) 816 817def dataRecordField? : List (String × Ty) String Option Ty 818 | [], _ => none 819 | (name, ty) :: rest, target => 820 if name = target then some ty else dataRecordField? rest target 821 822def liftScalarDataTy : Ty Ty 823 | .dataInt => .int 824 | .dataDecimal => .decimal 825 | .dataRational => .rational 826 | .dataBool => .bool 827 | .dataString => .string 828 | ty => ty 829 830partial def refineKnownDataTy (ty : Ty) : Ty := 831 match ty with 832 | .dataInt => .int 833 | .dataDecimal => .decimal 834 | .dataRational => .rational 835 | .dataBool => .bool 836 | .dataString => .string 837 | .dataNull => .data 838 | .dataArray itemTy => .dataArray (refineKnownDataTy itemTy) 839 | .dataRecord fields => 840 .dataRecord (fields.map (fun (name, fieldTy) => (name, refineKnownDataTy fieldTy))) 841 | other => other 842 843def dataArrayGet? : List Data Nat Option Data 844 | [], _ => none 845 | x :: _, 0 => some x 846 | _ :: xs, n + 1 => dataArrayGet? xs n 847 848def runtimeErrorToValue : RuntimeError Value 849 | .unboundVariable name => .error s!"unbound variable: {name}" 850 | .typeError msg => .error s!"type error: {msg}" 851 | .divisionByZero => .error "division by zero" 852 | .ioError msg => .error s!"io error: {msg}" 853 854def runtimeErrorToMessage : RuntimeError String 855 | .unboundVariable name => s!"unbound variable: {name}" 856 | .typeError msg => s!"type error: {msg}" 857 | .divisionByZero => "division by zero" 858 | .ioError msg => s!"io error: {msg}" 859 860partial def Data.ofNickel : Nickel.Term Data 861 | .null => .null 862 | .bool b => .bool b 863 | .int n => .int n 864 | .decimal d => .decimal (parseDecimal d) 865 | .string s => .string s 866 | .array xs => .array (xs.map Data.ofNickel) 867 | .record fields => .record (fields.map (fun (k, v) => (k, Data.ofNickel v))) 868 869def parseRenderedData (rendered : String) : EvalM Data := do 870 match Nickel.parse rendered with 871 | .ok term => pure (Data.ofNickel term) 872 | .error err => throw (.typeError s!"host Nickel render parse error: {err}") 873 874def encodeMapResult : Except RuntimeError Value Value 875 | .ok value => .resultOk value 876 | .error err => .resultErr (runtimeErrorToMessage err) 877 878def escapeJsonChar : Char String 879 | '"' => "\\\"" 880 | '\\' => "\\\\" 881 | '\n' => "\\n" 882 | '\r' => "\\r" 883 | '\t' => "\\t" 884 | c => c.toString 885 886def renderJsonString (s : String) : String := 887 "\"" ++ String.join (s.toList.map escapeJsonChar) ++ "\"" 888 889def isNickelKeyStart (c : Char) : Bool := 890 c.isAlpha || c = '_' 891 892def isNickelKeyContinue (c : Char) : Bool := 893 c.isAlpha || c.isDigit || c = '_' || c = '-' 894 895def renderNickelKey (s : String) : String := 896 match s.toList with 897 | [] => renderJsonString s 898 | c :: cs => 899 if isNickelKeyStart c && cs.all isNickelKeyContinue then 900 s 901 else 902 renderJsonString s 903 904partial def renderNickelData : Data String 905 | .null => "null" 906 | .bool b => toString b 907 | .int n => toString n 908 | .decimal d => renderDecimal d 909 | .string s => renderJsonString s 910 | .array xs => "[" ++ String.intercalate ", " (xs.map renderNickelData) ++ "]" 911 | .record fields => 912 let rendered := fields.map (fun (k, v) => s!"{renderNickelKey k} = {renderNickelData v}") 913 "{ " ++ String.intercalate ", " rendered ++ " }" 914 915partial def renderJsonData : Data String 916 | .null => "null" 917 | .bool b => if b then "true" else "false" 918 | .int n => toString n 919 | .decimal d => renderDecimal d 920 | .string s => renderJsonString s 921 | .array xs => "[" ++ String.intercalate ", " (xs.map renderJsonData) ++ "]" 922 | .record fields => 923 let rendered := fields.map (fun (k, v) => s!"{renderJsonString k}: {renderJsonData v}") 924 "{" ++ String.intercalate ", " rendered ++ "}" 925 926def builtinType? (name : Name) : Option Ty := 927 match name with 928 | "readFile" => some (.funTy .string [.error, .io] .string) 929 | "httpGet" => some (.funTy .string [.error, .io] .string) 930 | "readFileNickel" => some (.funTy .string [.error, .io] .data) 931 | "parseNickel" => some (.funTy .string [.error] .data) 932 | "parseJson" => some (.funTy .string [.error] .data) 933 | "getAturi" => some (.funTy .string [.error, .io] .data) 934 | "get" => some (.funTy .data [] (.funTy .string [.error] .data)) 935 | "at" => some (.funTy .data [] (.funTy .number [.error] .data)) 936 | "asInt" => some (.funTy .data [.error] .int) 937 | "asDecimal" => some (.funTy .data [.error] .decimal) 938 | "toInt" => some (.funTy .number [.error] .int) 939 | "toDecimal" => some (.funTy .number [.error] .decimal) 940 | "asString" => some (.funTy .data [.error] .string) 941 | "asBool" => some (.funTy .data [.error] .bool) 942 | "toJson" => some (.funTy .data [] .string) 943 | "toNickel" => some (.funTy .data [] .string) 944 | "rationalStability" => some (.funTy .number [] .stability) 945 | "decimalStability" => some (.funTy .number [] .stability) 946 | "pi" => some .number 947 | "sqrt" => some (.funTy .number [.error] .number) 948 | "process" => some (.funTy .number [] .number) 949 | "synthesize" => some (.funTy .number [.error] .number) 950 | "formula" => some (.funTy .number [.error] .string) 951 | "lazyRationalStability" => some (.funTy .number [] .stability) 952 | "lazyDecimalStability" => some (.funTy .number [] .stability) 953 | "stabilize" => some (.funTy .number [] (.funTy .number [.error] .number)) 954 | "approx" => some (.funTy .number [] (.funTy .number [.error] .number)) 955 | "bounds" => some (.funTy .number [] (.funTy .number [.error] .data)) 956 | _ => none 957 958def builtinValue? (name : Name) : Option Value := 959 match name with 960 | "readFile" => some .builtinReadFile 961 | "httpGet" => some .builtinHttpGet 962 | "readFileNickel" => some .builtinReadFileNickel 963 | "parseNickel" => some .builtinParseNickel 964 | "parseJson" => some .builtinParseJson 965 | "getAturi" => some .builtinGetAturi 966 | "get" => some .builtinDataGet 967 | "at" => some .builtinDataAt 968 | "asInt" => some .builtinDataAsInt 969 | "asDecimal" => some .builtinDataAsDecimal 970 | "toInt" => some .builtinRationalToInt 971 | "toDecimal" => some .builtinRationalToDecimal 972 | "asString" => some .builtinDataAsString 973 | "asBool" => some .builtinDataAsBool 974 | "toJson" => some .builtinDataToJson 975 | "toNickel" => some .builtinDataToNickel 976 | "rationalStability" => some .builtinRationalStability 977 | "decimalStability" => some .builtinDecimalStability 978 | "pi" => some (.number (.composite .pi)) 979 | "sqrt" => some .builtinSqrt 980 | "process" => some .builtinProcess 981 | "synthesize" => some .builtinSynthesize 982 | "formula" => some .builtinFormula 983 | "lazyRationalStability" => some .builtinRationalStability 984 | "lazyDecimalStability" => some .builtinDecimalStability 985 | "stabilize" => some .builtinStabilize 986 | "approx" => some .builtinApprox 987 | "bounds" => some .builtinBounds 988 | _ => none 989 990mutual 991 partial def applyValue (fnVal argVal : Value) : EvalM Value := do 992 match fnVal with 993 | .closure param body closureEnv => 994 eval ((param, argVal) :: closureEnv) body 995 | .builtinReadFile => do 996 let path expectString argVal 997 let contents IO.toEIO (fun err => .ioError (toString err)) (IO.FS.readFile path) 998 pure (.string contents) 999 | .builtinHttpGet => do 1000 let url expectString argVal 1001 let body IO.toEIO (fun err => .ioError (toString err)) (Http.httpGet url) 1002 pure (.string body) 1003 | .builtinReadFileNickel => do 1004 let path expectString argVal 1005 let rendered IO.toEIO (fun err => .ioError (toString err)) (NickelHost.evalFile path) 1006 pure (.data ( parseRenderedData rendered)) 1007 | .builtinParseNickel => do 1008 let source expectString argVal 1009 let rendered IO.toEIO (fun err => .ioError (toString err)) (NickelHost.evalString source) 1010 pure (.data ( parseRenderedData rendered)) 1011 | .builtinParseJson => do 1012 let source expectString argVal 1013 let rendered IO.toEIO (fun err => .ioError (toString err)) (NickelHost.evalJsonString source) 1014 pure (.data ( parseRenderedData rendered)) 1015 | .builtinGetAturi => do 1016 let source expectString argVal 1017 let rendered IO.toEIO (fun err => .ioError (toString err)) (Http.parseAturi source) 1018 pure (.data ( parseRenderedData rendered)) 1019 | .builtinDataGet => pure (.builtinDataGetField argVal) 1020 | .builtinDataGetField target => do 1021 let key expectString argVal 1022 match ( observeValue target (.getField key)) with 1023 | .data value => pure (.data value) 1024 | _ => throw (.typeError "get observation returned non-data") 1025 | .builtinDataAt => pure (.builtinDataAtIndex argVal) 1026 | .builtinDataAtIndex target => do 1027 let idx expectWholeNat argVal 1028 match ( observeValue target (.atIndex idx)) with 1029 | .data value => pure (.data value) 1030 | _ => throw (.typeError "at observation returned non-data") 1031 | .builtinDataAsInt => do 1032 let term expectData argVal 1033 match term with 1034 | .int n => pure (.number (numericOfInt n)) 1035 | _ => throw (.typeError "asInt expects an integer") 1036 | .builtinDataAsDecimal => do 1037 let term expectData argVal 1038 match term with 1039 | .decimal d => pure (.number (numericOfDecimal d)) 1040 | _ => throw (.typeError "asDecimal expects a decimal") 1041 | .builtinRationalToInt => do 1042 match ( observeValue argVal .asInt) with 1043 | .int n => pure (.number (numericOfInt n)) 1044 | _ => throw (.typeError "toInt observation returned non-int") 1045 | .builtinRationalToDecimal => do 1046 match ( observeValue argVal .asDecimal) with 1047 | .decimal d => pure (.number (numericOfDecimal d)) 1048 | _ => throw (.typeError "toDecimal observation returned non-decimal") 1049 | .builtinDataAsString => do 1050 let term expectData argVal 1051 match term with 1052 | .string s => pure (.string s) 1053 | _ => throw (.typeError "asString expects a string") 1054 | .builtinDataAsBool => do 1055 let term expectData argVal 1056 match term with 1057 | .bool b => pure (.bool b) 1058 | _ => throw (.typeError "asBool expects a boolean") 1059 | .builtinDataToJson => do 1060 let term expectData argVal 1061 pure (.string (renderJsonData term)) 1062 | .builtinDataToNickel => do 1063 let term expectData argVal 1064 pure (.string (renderNickelData term)) 1065 | .builtinRationalStability => do 1066 match ( observeValue argVal .rationalStability) with 1067 | .stability label => pure (.stability label) 1068 | _ => throw (.typeError "rationalStability observation returned non-stability") 1069 | .builtinDecimalStability => do 1070 match ( observeValue argVal .decimalStability) with 1071 | .stability label => pure (.stability label) 1072 | _ => throw (.typeError "decimalStability observation returned non-stability") 1073 | .builtinSqrt => do 1074 let d valueToDecimal argVal 1075 if d.num < 0 then 1076 throw (.typeError "sqrt expects a non-negative decimal") 1077 else 1078 pure (.number (.composite (.sqrt d))) 1079 | .builtinProcess => do 1080 match argVal with 1081 | .number (.exact q) => pure (.number (.composite (.exact q))) 1082 | .number (.composite r) => pure (.number (.composite r)) 1083 | _ => throw (.typeError "process expects a number") 1084 | .builtinSynthesize => do 1085 pure (.number ( synthesizeNumeric argVal)) 1086 | .builtinFormula => do 1087 pure (.string ( formulaOfValue argVal)) 1088 | .builtinStabilize => pure (.builtinStabilizeStep argVal) 1089 | .builtinStabilizeStep target => do 1090 let steps expectWholeNat argVal 1091 pure (.number (.exact ( stabilizeNumeric target steps))) 1092 | .builtinApprox => pure (.builtinApproxStep argVal) 1093 | .builtinApproxStep target => do 1094 let steps expectWholeNat argVal 1095 match ( observeValue target (.approx steps)) with 1096 | .rational q => pure (.number (.exact q)) 1097 | _ => throw (.typeError "approx observation returned non-rational") 1098 | .builtinBounds => pure (.builtinBoundsStep argVal) 1099 | .builtinBoundsStep target => do 1100 let steps expectWholeNat argVal 1101 match ( observeValue target (.bounds steps)) with 1102 | .bounds lo hi => pure (.data (.record [("lo", .decimal lo), ("hi", .decimal hi)])) 1103 | _ => throw (.typeError "bounds observation returned non-bounds") 1104 | .data (.record fields) => 1105 let key expectString argVal 1106 match fields.find? (fun (name, _) => name = key) with 1107 | some (_, value) => pure (.data value) 1108 | none => throw (.typeError s!"missing field: {key}") 1109 | .data (.array items) => 1110 let idx expectWholeNat argVal 1111 match dataArrayGet? items idx with 1112 | some value => pure (.data value) 1113 | none => throw (RuntimeError.typeError s!"index out of bounds: {idx}") 1114 | .data _ => 1115 throw (.typeError "data value is not callable with this argument") 1116 | _ => 1117 throw (.typeError "expected a function") 1118 1119 partial def eval (env : Env) : Expr EvalM Value 1120 | .int n => pure (.number (numericOfInt n)) 1121 | .decimal d => pure (.number (numericOfDecimal d)) 1122 | .bool b => pure (.bool b) 1123 | .string s => pure (.string s) 1124 | .null => pure (.data .null) 1125 | .var name => 1126 match env.lookup name with 1127 | some v => pure v 1128 | none => 1129 match builtinValue? name with 1130 | some v => pure v 1131 | none => throw (.unboundVariable name) 1132 | .arrayE items => do 1133 let values items.mapM (eval env) 1134 pure (.data (.array ( values.mapM valueToData))) 1135 | .recordE fields => do 1136 let fields' fields.mapM (fun (name, value) => do 1137 let value' eval env value 1138 pure (name, ( valueToData value'))) 1139 pure (.data (.record fields')) 1140 | .add lhs rhs => do 1141 let lVal eval env lhs 1142 let rVal eval env rhs 1143 match lVal, rVal with 1144 | .number (.composite _), _ | _, .number (.composite _) => 1145 pure (.number (.composite (.add ( valueToLazyReal lVal) ( valueToLazyReal rVal)))) 1146 | _, _ => 1147 pure (.number (.exact (addDecimals ( valueToDecimal lVal) ( valueToDecimal rVal)))) 1148 | .sub lhs rhs => do 1149 let lVal eval env lhs 1150 let rVal eval env rhs 1151 match lVal, rVal with 1152 | .number (.composite _), _ | _, .number (.composite _) => 1153 pure (.number (.composite (.sub ( valueToLazyReal lVal) ( valueToLazyReal rVal)))) 1154 | _, _ => 1155 pure (.number (.exact (subDecimals ( valueToDecimal lVal) ( valueToDecimal rVal)))) 1156 | .mul lhs rhs => do 1157 let lVal eval env lhs 1158 let rVal eval env rhs 1159 match lVal, rVal with 1160 | .number (.composite _), _ | _, .number (.composite _) => 1161 pure (.number (.composite (.mul ( valueToLazyReal lVal) ( valueToLazyReal rVal)))) 1162 | _, _ => 1163 pure (.number (.exact (mulDecimals ( valueToDecimal lVal) ( valueToDecimal rVal)))) 1164 | .div lhs rhs => do 1165 let lVal eval env lhs 1166 let rVal eval env rhs 1167 match lVal, rVal with 1168 | .number (.composite _), _ | _, .number (.composite _) => 1169 pure (.number (.composite (.div ( valueToLazyReal lVal) ( valueToLazyReal rVal)))) 1170 | _, _ => 1171 pure (.number (.exact ( divDecimals ( valueToDecimal lVal) ( valueToDecimal rVal)))) 1172 | .eq lhs rhs => do 1173 let l eval env lhs 1174 let r eval env rhs 1175 match l, r with 1176 | .number _, .number _ => pure (.bool (rationalEq ( valueToDecimal l) ( valueToDecimal r))) 1177 | _, _ => throw (.typeError "equality expects matching Int or Decimal operands") 1178 | .letE name value body => do 1179 let value' eval env value 1180 eval ((name, value') :: env) body 1181 | .ifE cond thenBranch elseBranch => do 1182 let c expectBool ( eval env cond) 1183 if c then 1184 eval env thenBranch 1185 else 1186 eval env elseBranch 1187 | .tryE body errName handler => do 1188 try 1189 eval env body 1190 catch err => 1191 eval ((errName, runtimeErrorToValue err) :: env) handler 1192 | .parMapE itemName collection body => do 1193 let source expectData ( eval env collection) 1194 match source with 1195 | .array items => 1196 let tasks items.mapM (fun item => 1197 EIO.asTask (prio := .dedicated) (eval ((itemName, .data item) :: env) body)) 1198 let results : List (Except RuntimeError Value) := List.map Task.get tasks 1199 pure (.list (results.map encodeMapResult)) 1200 | _ => 1201 throw (.typeError "pmap expects an array value") 1202 | .lam param _ body => 1203 pure (.closure param body env) 1204 | .app fn arg => do 1205 let fnVal eval env fn 1206 let argVal eval env arg 1207 applyValue fnVal argVal 1208end 1209 1210def renderData : Data String := renderNickelData 1211 1212def renderObservedScalar (value : Value) : Option String := 1213 match value with 1214 | .number (.exact q) => some s!"{q.num}/{q.den}" 1215 | _ => none 1216 1217def renderValue : Value String 1218 | value@(.number (.exact _)) => 1219 match renderObservedScalar value with 1220 | some rendered => rendered 1221 | none => "<number>" 1222 | .stability label => label 1223 | .number (.composite (.ratioProcess q)) => s!"<process:ratio({q.num}/{q.den})>" 1224 | .number (.composite (.decimalProcess d)) => s!"<process:decimal({renderDecimal d})>" 1225 | .number (.composite (.generated (.exactRatio q))) => s!"<generator:ratio({q.num}/{q.den})>" 1226 | .number (.composite (.generated (.exactDecimal d))) => s!"<generator:decimal({renderDecimal d})>" 1227 | .number (.composite (.generated (.rationalRefiner _))) => "<generator:rational-refiner>" 1228 | .number (.composite (.generated (.decimalRefiner _))) => "<generator:decimal-refiner>" 1229 | .number (.composite (.generated .piChudnovsky)) => "<generator:pi[chudnovsky]>" 1230 | .number (.composite (.generated (.sqrt d))) => s!"<generator:sqrt({renderDecimal d})>" 1231 | .number (.composite (.generated (.add _ _))) => "<generator:add>" 1232 | .number (.composite (.generated (.sub _ _))) => "<generator:sub>" 1233 | .number (.composite (.generated (.mul _ _))) => "<generator:mul>" 1234 | .number (.composite (.generated (.div _ _))) => "<generator:div>" 1235 | .number (.composite (.exact d)) => s!"<lazy:{renderDecimal d}>" 1236 | .number (.composite .pi) => "<lazy:pi>" 1237 | .number (.composite (.sqrt d)) => s!"<lazy:sqrt({renderDecimal d})>" 1238 | .number (.composite (.add _ _)) => "<lazy:add>" 1239 | .number (.composite (.sub _ _)) => "<lazy:sub>" 1240 | .number (.composite (.mul _ _)) => "<lazy:mul>" 1241 | .number (.composite (.div _ _)) => "<lazy:div>" 1242 | .bool b => toString b 1243 | .string s => s!"\"{s}\"" 1244 | .data data => renderData data 1245 | .list items => "[" ++ String.intercalate ", " (items.map renderValue) ++ "]" 1246 | .resultOk value => s!"ok({renderValue value})" 1247 | .resultErr msg => s!"err(\"{msg}\")" 1248 | .error msg => s!"<error:{msg}>" 1249 | .builtinReadFile => "<builtin:readFile>" 1250 | .builtinHttpGet => "<builtin:httpGet>" 1251 | .builtinReadFileNickel => "<builtin:readFileNickel>" 1252 | .builtinParseNickel => "<builtin:parseNickel>" 1253 | .builtinParseJson => "<builtin:parseJson>" 1254 | .builtinGetAturi => "<builtin:getAturi>" 1255 | .builtinDataGet => "<builtin:get>" 1256 | .builtinDataGetField _ => "<builtin:getField>" 1257 | .builtinDataAt => "<builtin:at>" 1258 | .builtinDataAtIndex _ => "<builtin:atIndex>" 1259 | .builtinDataAsInt => "<builtin:asInt>" 1260 | .builtinDataAsDecimal => "<builtin:asDecimal>" 1261 | .builtinRationalToInt => "<builtin:toInt>" 1262 | .builtinRationalToDecimal => "<builtin:toDecimal>" 1263 | .builtinDataAsString => "<builtin:asString>" 1264 | .builtinDataAsBool => "<builtin:asBool>" 1265 | .builtinDataToJson => "<builtin:toJson>" 1266 | .builtinDataToNickel => "<builtin:toNickel>" 1267 | .builtinRationalStability => "<builtin:rationalStability>" 1268 | .builtinDecimalStability => "<builtin:decimalStability>" 1269 | .builtinSqrt => "<builtin:sqrt>" 1270 | .builtinProcess => "<builtin:process>" 1271 | .builtinSynthesize => "<builtin:synthesize>" 1272 | .builtinFormula => "<builtin:formula>" 1273 | .builtinStabilize => "<builtin:stabilize>" 1274 | .builtinStabilizeStep _ => "<builtin:stabilizeStep>" 1275 | .builtinApprox => "<builtin:approx>" 1276 | .builtinApproxStep _ => "<builtin:approxStep>" 1277 | .builtinBounds => "<builtin:bounds>" 1278 | .builtinBoundsStep _ => "<builtin:boundsStep>" 1279 | .closure _ _ _ => "<closure>" 1280 1281abbrev TyEnv := List (Name × Ty) 1282 1283structure Judgment where 1284 ty : Ty 1285 effects : Effects 1286 deriving Repr, Inhabited 1287 1288inductive TypeError where 1289 | unboundVariable : Name TypeError 1290 | mismatch : Ty Ty TypeError 1291 | expectedFunction : Ty TypeError 1292 deriving Repr, Inhabited 1293 1294abbrev CheckM := Except TypeError 1295 1296def TyEnv.lookup (env : TyEnv) (name : Name) : Option Ty := 1297 match env with 1298 | [] => none 1299 | (n, ty) :: rest => if n = name then some ty else rest.lookup name 1300 1301def ensureType (expected actual : Ty) : CheckM Unit := 1302 if expected == actual then 1303 pure () 1304 else 1305 throw (.mismatch expected actual) 1306 1307def isNumericTy : Ty Bool 1308 | .number | .int | .decimal | .rational | .lazyReal => true 1309 | _ => false 1310 1311def pureJudgment (ty : Ty) : Judgment := 1312 { ty := ty, effects := [] } 1313 1314def eraseDataRefinement : Ty Ty 1315 | .dataInt | .dataDecimal | .dataRational | .dataBool | .dataString | .dataNull | .dataArray _ | .dataRecord _ => .data 1316 | ty => ty 1317 1318def canFlowTo (actual expected : Ty) : Bool := 1319 if actual == expected then 1320 true 1321 else 1322 match actual, expected with 1323 | .number, .number => true 1324 | .int, .number => true 1325 | .decimal, .number => true 1326 | .rational, .number => true 1327 | .lazyReal, .number => true 1328 | .number, .decimal => true 1329 | .number, .rational => true 1330 | .int, .decimal => true 1331 | .int, .rational => true 1332 | .decimal, .rational => true 1333 | .rational, .decimal => true 1334 | .dataInt, .data => true 1335 | .dataDecimal, .data => true 1336 | .dataRational, .data => true 1337 | .dataBool, .data => true 1338 | .dataString, .data => true 1339 | .dataNull, .data => true 1340 | .dataArray _, .data => true 1341 | .dataRecord _, .data => true 1342 | .dataArray _, .dataArray .data => true 1343 | _, _ => false 1344 1345partial def constString? : Expr Option String 1346 | .string s => some s 1347 | _ => none 1348 1349unsafe def evalConstData? (env : List (Name × Data)) : Expr Option Data 1350 | .null => some .null 1351 | .string s => some (.string s) 1352 | .int n => some (.int n) 1353 | .decimal d => some (.decimal d) 1354 | .bool b => some (.bool b) 1355 | .var name => env.lookup name 1356 | .arrayE items => do 1357 some (.array ( items.mapM (evalConstData? env))) 1358 | .recordE fields => do 1359 some (.record ( fields.mapM (fun (name, value) => do 1360 let value' evalConstData? env value 1361 pure (name, value')))) 1362 | .letE name value body => do 1363 let value' evalConstData? env value 1364 evalConstData? ((name, value') :: env) body 1365 | .ifE cond thenBranch elseBranch => do 1366 match ( evalConstData? env cond) with 1367 | .bool true => evalConstData? env thenBranch 1368 | .bool false => evalConstData? env elseBranch 1369 | _ => none 1370 | .app fn arg => do 1371 match fn with 1372 | .var "parseNickel" => 1373 let source evalConstData? env arg 1374 match source with 1375 | .string s => 1376 match unsafeIO (NickelHost.evalString s) with 1377 | .ok rendered => 1378 match Nickel.parse rendered with 1379 | .ok term => some (Data.ofNickel term) 1380 | .error _ => none 1381 | .error _ => none 1382 | _ => none 1383 | .var "parseJson" => 1384 let source evalConstData? env arg 1385 match source with 1386 | .string s => 1387 match unsafeIO (NickelHost.evalJsonString s) with 1388 | .ok rendered => 1389 match Nickel.parse rendered with 1390 | .ok term => some (Data.ofNickel term) 1391 | .error _ => none 1392 | .error _ => none 1393 | _ => none 1394 | .var "httpGet" => 1395 let url evalConstData? env arg 1396 match url with 1397 | .string s => 1398 match unsafeIO (Http.httpGet s) with 1399 | .ok body => some (.string body) 1400 | .error _ => none 1401 | _ => none 1402 | .var "getAturi" => 1403 let source evalConstData? env arg 1404 match source with 1405 | .string s => 1406 match unsafeIO (Http.parseAturi s) with 1407 | .ok rendered => 1408 match Nickel.parse rendered with 1409 | .ok term => some (Data.ofNickel term) 1410 | .error _ => none 1411 | .error _ => none 1412 | _ => none 1413 | .var "readFileNickel" => 1414 let path evalConstData? env arg 1415 match path with 1416 | .string p => 1417 match unsafeIO (NickelHost.evalFile p) with 1418 | .ok rendered => 1419 match Nickel.parse rendered with 1420 | .ok term => some (Data.ofNickel term) 1421 | .error _ => none 1422 | .error _ => none 1423 | _ => none 1424 | .app (.var "get") base => do 1425 let container evalConstData? env base 1426 let key evalConstData? env arg 1427 match container, key with 1428 | .record fields, .string field => 1429 match fields.find? (fun (name, _) => name = field) with 1430 | some (_, value) => some value 1431 | none => none 1432 | _, _ => none 1433 | .app (.var "at") base => do 1434 let container evalConstData? env base 1435 let index evalConstData? env arg 1436 match container, index with 1437 | .array items, .int idx => 1438 if idx < 0 then none else dataArrayGet? items idx.toNat 1439 | _, _ => none 1440 | _ => none 1441 | _ => none 1442 1443unsafe def inferType (env : TyEnv) : Expr CheckM Judgment 1444 | .int _ => pure (pureJudgment .number) 1445 | .decimal _ => pure (pureJudgment .number) 1446 | .bool _ => pure (pureJudgment .bool) 1447 | .string _ => pure (pureJudgment .string) 1448 | .null => pure (pureJudgment .dataNull) 1449 | .var name => 1450 match env.lookup name with 1451 | some ty => pure (pureJudgment ty) 1452 | none => 1453 match builtinType? name with 1454 | some ty => pure (pureJudgment ty) 1455 | none => throw (.unboundVariable name) 1456 | .arrayE items => do 1457 let itemJs items.mapM (inferType env) 1458 let dataTys itemJs.mapM (fun j => 1459 match j.ty with 1460 | .number => pure .dataRational 1461 | .int => pure .dataInt 1462 | .decimal => pure .dataDecimal 1463 | .rational => pure .dataRational 1464 | .bool => pure .dataBool 1465 | .string => pure .dataString 1466 | .data => pure .data 1467 | .dataInt => pure .dataInt 1468 | .dataDecimal => pure .dataDecimal 1469 | .dataRational => pure .dataRational 1470 | .dataBool => pure .dataBool 1471 | .dataString => pure .dataString 1472 | .dataNull => pure .dataNull 1473 | .dataArray ty => pure (.dataArray ty) 1474 | .dataRecord fields => pure (.dataRecord fields) 1475 | ty => throw (.mismatch .data ty)) 1476 let itemTy := 1477 match dataTys with 1478 | [] => .data 1479 | first :: rest => if rest.all (· == first) then first else .data 1480 pure { 1481 ty := .dataArray itemTy 1482 effects := itemJs.foldl (fun acc j => acc.union j.effects) [] 1483 } 1484 | .recordE fields => do 1485 let fieldJs fields.mapM (fun (name, value) => do pure (name, inferType env value)) 1486 let fieldTys fieldJs.mapM (fun (name, j) => do 1487 let ty match j.ty with 1488 | .number => pure .dataRational 1489 | .int => pure .dataInt 1490 | .decimal => pure .dataDecimal 1491 | .rational => pure .dataRational 1492 | .bool => pure .dataBool 1493 | .string => pure .dataString 1494 | .data => pure .data 1495 | .dataInt => pure .dataInt 1496 | .dataDecimal => pure .dataDecimal 1497 | .dataRational => pure .dataRational 1498 | .dataBool => pure .dataBool 1499 | .dataString => pure .dataString 1500 | .dataNull => pure .dataNull 1501 | .dataArray ty => pure (.dataArray ty) 1502 | .dataRecord fields => pure (.dataRecord fields) 1503 | ty => throw (.mismatch .data ty) 1504 pure (name, ty, j.effects)) 1505 pure { 1506 ty := .dataRecord (fieldTys.map (fun (name, ty, _) => (name, ty))) 1507 effects := fieldTys.foldl (fun acc (_, _, effects) => acc.union effects) [] 1508 } 1509 | .add lhs rhs 1510 | .sub lhs rhs 1511 | .mul lhs rhs => do 1512 let lhsJ inferType env lhs 1513 let rhsJ inferType env rhs 1514 if isNumericTy lhsJ.ty && isNumericTy rhsJ.ty then 1515 pure { 1516 ty := .number 1517 effects := lhsJ.effects.union rhsJ.effects 1518 } 1519 else 1520 throw (.mismatch lhsJ.ty rhsJ.ty) 1521 | .div lhs rhs => do 1522 let lhsJ inferType env lhs 1523 let rhsJ inferType env rhs 1524 if isNumericTy lhsJ.ty && isNumericTy rhsJ.ty then 1525 pure { 1526 ty := .number 1527 effects := (lhsJ.effects.union rhsJ.effects).insert .error 1528 } 1529 else 1530 throw (.mismatch lhsJ.ty rhsJ.ty) 1531 | .eq lhs rhs => do 1532 let lhsJ inferType env lhs 1533 let rhsJ inferType env rhs 1534 if isNumericTy lhsJ.ty && isNumericTy rhsJ.ty then 1535 pure { ty := .bool, effects := lhsJ.effects.union rhsJ.effects } 1536 else 1537 throw (.mismatch lhsJ.ty rhsJ.ty) 1538 | .letE name value body => do 1539 let valueJ inferType env value 1540 let bodyJ inferType ((name, valueJ.ty) :: env) body 1541 pure { 1542 ty := bodyJ.ty 1543 effects := valueJ.effects.union bodyJ.effects 1544 } 1545 | .ifE cond thenBranch elseBranch => do 1546 let condJ inferType env cond 1547 ensureType .bool condJ.ty 1548 let thenJ inferType env thenBranch 1549 let elseJ inferType env elseBranch 1550 ensureType thenJ.ty elseJ.ty 1551 pure { 1552 ty := thenJ.ty 1553 effects := condJ.effects.union (thenJ.effects.union elseJ.effects) 1554 } 1555 | .tryE body errName handler => do 1556 let bodyJ inferType env body 1557 let handlerJ inferType ((errName, .error) :: env) handler 1558 ensureType bodyJ.ty handlerJ.ty 1559 pure { 1560 ty := bodyJ.ty 1561 effects := (bodyJ.effects.erase .error).union handlerJ.effects 1562 } 1563 | .parMapE itemName collection body => do 1564 let collectionJ inferType env collection 1565 match collectionJ.ty with 1566 | .data 1567 | .dataArray _ => 1568 let itemTy := 1569 match collectionJ.ty with 1570 | .dataArray ty => ty 1571 | _ => .data 1572 let bodyJ inferType ((itemName, itemTy) :: env) body 1573 pure { 1574 ty := .list 1575 effects := collectionJ.effects.union bodyJ.effects 1576 } 1577 | _ => 1578 throw (.mismatch .data collectionJ.ty) 1579 | .lam param paramTy body => do 1580 let bodyJ inferType ((param, paramTy) :: env) body 1581 pure (pureJudgment (.funTy paramTy bodyJ.effects bodyJ.ty)) 1582 | .app fn arg => do 1583 let fnJ inferType env fn 1584 let argJ inferType env arg 1585 match fnJ.ty with 1586 | .funTy paramTy latentEffects resultTy => 1587 if canFlowTo argJ.ty paramTy then 1588 let refinedResultTy := 1589 match fn, arg, resultTy with 1590 | .app (.var "get") base, .string key, .data => 1591 match inferType env base with 1592 | .ok baseJ => 1593 match baseJ.ty with 1594 | .dataRecord fields => 1595 match dataRecordField? fields key with 1596 | some ty => liftScalarDataTy ty 1597 | none => .data 1598 | _ => 1599 match evalConstData? [] (.app fn arg) with 1600 | some data => liftScalarDataTy (dataType data) 1601 | none => .data 1602 | .error _ => .data 1603 | .app (.var "at") base, _, .data => 1604 match evalConstData? [] (.app fn arg) with 1605 | some data => liftScalarDataTy (dataType data) 1606 | none => 1607 match inferType env base with 1608 | .ok baseJ => 1609 match baseJ.ty with 1610 | .dataArray itemTy => liftScalarDataTy itemTy 1611 | _ => .data 1612 | .error _ => .data 1613 | .var "parseNickel", _, .data => 1614 match evalConstData? [] (.app fn arg) with 1615 | some data => refineKnownDataTy (dataType data) 1616 | none => .data 1617 | .var "parseJson", _, .data => 1618 match evalConstData? [] (.app fn arg) with 1619 | some data => refineKnownDataTy (dataType data) 1620 | none => .data 1621 | .var "readFileNickel", _, .data => 1622 match evalConstData? [] (.app fn arg) with 1623 | some data => refineKnownDataTy (dataType data) 1624 | none => .data 1625 | .var "asInt", _, .int => 1626 match argJ.ty with 1627 | .dataInt => .int 1628 | _ => .int 1629 | .var "asDecimal", _, .decimal => 1630 match argJ.ty with 1631 | .dataDecimal => .decimal 1632 | _ => .decimal 1633 | .var "toInt", _, .int => 1634 .int 1635 | .var "toDecimal", _, .decimal => 1636 .decimal 1637 | .var "asString", _, .string => 1638 match argJ.ty with 1639 | .dataString => .string 1640 | _ => .string 1641 | .var "asBool", _, .bool => 1642 match argJ.ty with 1643 | .dataBool => .bool 1644 | _ => .bool 1645 | .app (.var "approx") _, _, .number => 1646 .number 1647 | .app (.var "stabilize") _, _, .number => 1648 .number 1649 | .app (.var "bounds") _, _, .data => 1650 .data 1651 | _, _, _ => resultTy 1652 pure { 1653 ty := refinedResultTy 1654 effects := fnJ.effects.union (argJ.effects.union latentEffects) 1655 } 1656 else 1657 throw (.mismatch paramTy argJ.ty) 1658 | _ => 1659 throw (.expectedFunction fnJ.ty) 1660 1661partial def renderType : Ty String 1662 | .number => "Number" 1663 | .int => "Int" 1664 | .decimal => "Decimal" 1665 | .rational => "Number" 1666 | .lazyReal => "Number" 1667 | .stability => "Stability" 1668 | .bool => "Bool" 1669 | .string => "String" 1670 | .data => "Data" 1671 | .dataInt => "Data:Int" 1672 | .dataDecimal => "Data:Decimal" 1673 | .dataRational => "Data:Rational" 1674 | .dataBool => "Data:Bool" 1675 | .dataString => "Data:String" 1676 | .dataNull => "Data:Null" 1677 | .dataArray itemTy => s!"Data:[{renderType itemTy}]" 1678 | .dataRecord fields => 1679 let rendered := fields.map (fun (name, ty) => s!"{name}: {renderType ty}") 1680 "Data:{" ++ String.intercalate ", " rendered ++ "}" 1681 | .list => "List" 1682 | .result => "Result" 1683 | .error => "Error" 1684 | .funTy lhs effects rhs => s!"({renderType lhs} -> {renderType rhs} ! {effects.render})" 1685 1686def renderTypeError : TypeError String 1687 | .unboundVariable name => s!"unbound variable {name}" 1688 | .mismatch expected actual => s!"expected {renderType expected}, got {renderType actual}" 1689 | .expectedFunction actual => s!"expected function, got {renderType actual}" 1690 1691def renderJudgment (judgment : Judgment) : String := 1692 s!"{renderType judgment.ty} ! {judgment.effects.render}" 1693 1694def sampleProgram : Expr := 1695 .letE "inc" (.lam "x" .int (.add (.var "x") (.int 1))) 1696 (.app (.var "inc") (.int 41)) 1697 1698def lexicalScopeProgram : Expr := 1699 .letE "x" (.int 10) 1700 (.letE "f" (.lam "y" .int (.add (.var "x") (.var "y"))) 1701 (.letE "x" (.int 100) 1702 (.app (.var "f") (.int 5)))) 1703 1704def conditionalProgram : Expr := 1705 .ifE (.eq (.mul (.int 6) (.int 7)) (.int 42)) 1706 (.int 1) 1707 (.int 0) 1708 1709end Mlang