2023-11-22-declare-prove-use.lua (3673B)
1 local pprint = require("pprint").pprint 2 -- Possible actions: 3 -- * start subcontext 4 -- * declare judgment 5 -- * use declared judgment 6 7 function die(err, msg, ...) 8 msg = tostring(msg or err) 9 if err then 10 io.stderr:write(string.format(msg .. "\n", ...)) 11 os.exit(1) 12 end 13 end 14 15 local test = { 16 { 17 method = "down" 18 }, 19 { 20 method = "declare", 21 params = { 22 name = "cv", 23 judgment = { 24 head = "var", 25 body = { 26 { 27 head = "x" 28 } 29 } 30 } 31 } 32 }, 33 { 34 method = "up" 35 }, 36 { 37 method = "declare", 38 params = { 39 name = "var-wff", 40 judgment = { 41 head = "wff", 42 body = { 43 { 44 head = "x" 45 } 46 } 47 } 48 } 49 } 50 } 51 52 local actions = {} 53 54 function actions.down(state) 55 die(state.subcontext, "Unresolved subcontext") 56 table.insert(state.theories, {}) 57 return state 58 end 59 60 function actions.up(state) 61 state.subcontext = state.theories[#state.theories] 62 table.remove(state.theories) 63 return state 64 end 65 66 function pressupositions(state, judgment) 67 -- This is a rabbit hole. Where do pressupositions come from? Two 68 -- options: 1) we fix the forms of judgments and hard-code the 69 -- pressupositions; 2) we find a way for the user to create 70 -- *declaration rules* that work just like assumptions but are 71 -- only used at top-level declarations. 72 return {} 73 end 74 75 function actions.declare(state, params) 76 die(state.focus, "Proof in progress") 77 local assumption = { 78 name = params.name, 79 conclusion = params.judgment, 80 subcontext = state.subcontext 81 } 82 state.subcontext = nil 83 table.insert(state.theories[#state.theories], assumption) 84 state.goals = pressupositions(state, params.judgment) 85 return state 86 end 87 88 function prune(state) 89 while #state.focus.subgoals == 0 do 90 die(not match(state.focus.goal, state.focus.candidate), "Used assumption does not prove the goal") 91 if state.focus.goal.head == "var" then 92 -- oh boy 93 -- perform unifications 94 end 95 if state.focus.parent then 96 state.focus = state.focus.parent 97 table.remove(state.focus.subgoals) 98 else 99 table.remove(state.goals) 100 if #state.goals > 0 then 101 state.focus = state.goals[#state.goals] 102 else 103 state.focus = nil 104 end 105 return state 106 end 107 end 108 return state 109 end 110 111 function use(state, assumption) 112 state.focus.candidate = assumption.conclusion 113 if #assumption.subcontext > 0 then 114 for i = #candidate.subcontext, 1, -1 do 115 table.insert(state.focus.subgoals, candidate.subcontext[i]) 116 state.focus.subgoals[i].parent = state.focus 117 end 118 state.focus = state.focus.subgoals[#state.focus.subgoals] 119 end 120 end 121 122 function actions.use(state, name) 123 die(not state.goals.focus, "Nothing to prove") 124 125 local assumption = state.assets[name] 126 die(not assumption, "No assumption under that name") 127 state = use(state, assumption) 128 state = prune(state) 129 return state 130 end 131 132 function interpret(program, state) 133 for _,c in ipairs(program) do 134 die(not actions[c.method], "No such action '%s'", c.method) 135 state = actions[c.method](state, c.params) 136 end 137 return state 138 end 139 140 function judgment2str(judg) 141 local res = judg.head 142 if judg.body then 143 res = res .. "(" 144 for i = 1, #judg.body do 145 res = res .. judgment2str(judg.body[i]) 146 if i < #judg.body then 147 res = res .. "," 148 end 149 end 150 res = res .. ")" 151 end 152 return res 153 end 154 155 function printassumption(assump, level) 156 if assump.subcontext then 157 printtheory(assump.subcontext, level + 1) 158 end 159 io.write(string.rep(" ", level) .. assump.name .. " " .. judgment2str(assump.conclusion) .. "\n") 160 end 161 162 function printtheory(th, level) 163 level = level or 0 164 for _,a in ipairs(th) do 165 printassumption(a, level) 166 end 167 end 168 169 local init = { 170 goals = {}, 171 theories = {{}} 172 } 173 174 local bigres = interpret(test, init) 175 printtheory(bigres.theories[1])