repos / temple.lean4.git


temple.lean4.git / Temple
Evgenii Akentev  ·  2025-08-19

DeBruijnEval.lean

  1import Std.Data.HashMap
  2
  3import Temple.DeBruijn
  4
  5open TempleDeBruijn
  6
  7namespace TempleDeBruijnEval
  8
  9inductive Value where
 10| text : String -> Value
 11| list : List Value -> Value
 12deriving BEq, Repr, Inhabited
 13
 14partial instance : ToString Value where
 15  toString :=
 16    let rec toStr x :=
 17      have : ToString Value := toStr
 18      match x with
 19      | .text s => s
 20      | .list s => "[" ++ ",".intercalate (s.map toStr) ++ "]"
 21    toStr
 22
 23structure UserContext where
 24  data : Std.HashMap String Value := Std.HashMap.ofList []
 25  operators : Std.HashMap String (Value -> Except String Value) := Std.HashMap.ofList []
 26
 27def defaultOperators : List (Prod String (Value -> Except String Value)) :=
 28  let toUpper
 29    | .text t => return .text (String.toUpper t)
 30    | _ => throw "toUpper: list is not supported"
 31  let toLower
 32    | .text t => return .text (String.toLower t)
 33    | _ => throw "toLower: list is not supported"
 34  [ ("toUpper", toUpper), ("toLower", toLower) ]
 35
 36structure Context where
 37  data : Std.HashMap String Value
 38  operators : Std.HashMap String (Value -> Except String Value)
 39  binds : List Value := []
 40
 41private partial def eval' (ctx : Context) (t : Term) : IO String :=
 42  match t with
 43  | .text s => return s
 44  | .op name args =>
 45    match (Std.HashMap.get? ctx.operators name) with
 46    | .some operator => do
 47      let resolvedArgs <- args.mapM $ fun (Arg.var v) => eval' ctx $ .var v
 48      let operatorResult := operator $ match resolvedArgs with
 49        | [x] => .text x
 50        | _ => .list $ resolvedArgs.map .text
 51      match operatorResult with
 52        | .ok r => return toString r
 53        | .error e => throw $ IO.userError s!"operator {name} execution result: {e}"
 54    | _ => throw $ IO.userError s!"\"{name}\" operator is not provided"
 55  | .forloop v ts => do
 56    let processVariable name o := match o with
 57      | .some (.text t) => return t.toList.map $ fun c => .text ([c].asString)
 58      | .some (.list vs) => return vs
 59      | .none => throw $ IO.userError s!"\"{name}\" is not provided"
 60    let vals <- match v with
 61      | .free n => processVariable n $ Std.HashMap.get? ctx.data n
 62      | .bound (i, n) => processVariable n ctx.binds[i]?
 63    let processVal val := ts.mapM $ eval' {ctx with binds := List.cons val ctx.binds }
 64    let results <- vals.mapM processVal
 65    return String.join (results.map String.join)
 66  | .var (.free n) => match (Std.HashMap.get? ctx.data n) with
 67    | .some v => return toString v
 68    | .none => throw $ IO.userError s!"\"{n}\" is not provided"
 69  | .var (.bound (i, n)) => match ctx.binds[i]? with
 70    | .some v => return toString v
 71    | .none => throw $ IO.userError s!"\"{n}\" is not defined in this scope"
 72
 73partial def eval (userContext : UserContext) : Term -> IO String :=
 74  TempleDeBruijnEval.eval'
 75    { data := userContext.data
 76    , operators := Std.HashMap.ofList $ defaultOperators ++ userContext.operators.toList
 77    }
 78
 79section Test
 80
 81#eval show IO Bool from do
 82  let t <- eval {} (.text "hello")
 83  return (t == "hello")
 84
 85#eval show IO Bool from do
 86  let t <- eval ({operators := Std.HashMap.ofList [ ( "hello", fun _ => return (Value.text "world") ) ]}) (.op "hello" [])
 87  return (t == "world")
 88
 89#eval show IO Bool from do
 90  let t <- eval ({data := Std.HashMap.ofList [ ( "fruits", .list [ .text "world", .text "land" ] ) ]})
 91                (.forloop (Var.free "fruits") [ .text "hello ", .var (.bound (0, "0")) ])
 92  return (t == "hello worldhello land")
 93
 94#eval show IO Bool from do
 95  let t <- eval ({data := Std.HashMap.ofList [ ("fruits", .list [ .text "apple", .text "orange" ])
 96                                                            , ("vegetables", .list [ .text "salad", .text "onion" ] ) ]})
 97                (.forloop (Var.free "fruits")
 98                  [ .text "got an ", .var (.bound (0, "0")), .text "\n"
 99                  , .forloop (Var.free "vegetables") [ .var (.bound (0, "0")) ] ])
100  return (t == "got an apple\nsaladoniongot an orange\nsaladonion")
101
102#eval show IO String from
103  eval ({data := Std.HashMap.ofList [ ("fruits", .list [ .text "apple", .text "orange" ])
104                                                            , ("vegetables", .list [ .text "salad", .text "onion" ] ) ]})
105                (.forloop (Var.free "fruits")
106                  [ .text "got an ", .var (.bound (0, "0")), .text "\n"
107                  , .forloop (Var.free "vegetables") [ .var (.bound (1, "1")) ] ])
108
109#eval show IO String from
110  try
111    eval ({data := Std.HashMap.ofList [ ( "fruits", .list [ .text "world", .text "land" ] ) ]})
112                  (.forloop (Var.free "fruits") [ .text "hello ", .var (.bound (1, "x")) ])
113  catch e => pure s!"Caught exception: {e}"
114
115#eval show IO String from
116  try
117    eval ({data := Std.HashMap.ofList [ ]})
118                  (.forloop (Var.free "fruits") [ .text "hello ", .var (.bound (0, "x")) ])
119  catch e => pure s!"Caught exception: {e}"
120
121#eval show IO String from
122  try
123    eval ({data := Std.HashMap.ofList [ ( "fruits", .list [ .text "world", .text "land" ] ) ]})
124                  (.forloop (Var.free "fruits") [ .text "hello ", .var (.free "free"), .var (.bound (0, "x")) ])
125  catch e => pure s!"Caught exception: {e}"
126
127end Test
128
129end TempleDeBruijnEval