diff --git a/src/bindings/lua/CMakeLists.txt b/src/bindings/lua/CMakeLists.txt index d36c4a406..b5e2b3e86 100644 --- a/src/bindings/lua/CMakeLists.txt +++ b/src/bindings/lua/CMakeLists.txt @@ -2,6 +2,6 @@ add_library(lua util.cpp lua_exception.cpp name.cpp numerics.cpp lref.cpp splay_map.cpp options.cpp sexpr.cpp format.cpp level.cpp local_context.cpp expr.cpp context.cpp object.cpp environment.cpp formatter.cpp justification.cpp metavar_env.cpp type_inferer.cpp -io_state.cpp leanlua_state.cpp frontend_lean.cpp) +io_state.cpp goal.cpp leanlua_state.cpp frontend_lean.cpp) target_link_libraries(lua ${LEAN_LIBS}) diff --git a/src/bindings/lua/environment.cpp b/src/bindings/lua/environment.cpp index 5bc364415..8939a9a4b 100644 --- a/src/bindings/lua/environment.cpp +++ b/src/bindings/lua/environment.cpp @@ -185,7 +185,7 @@ static int environment_objects_core(lua_State * L, bool local) { lua_pushinteger(L, 0); // upvalue(2): index lua_pushinteger(L, env->get_num_objects(local)); // upvalue(3): size lua_pushboolean(L, local); // upvalue(4): local flag - lua_pushcclosure(L, &environment_next_object, 4); // create closure with 4 upvalues + lua_pushcclosure(L, &safe_function, 4); // create closure with 4 upvalues return 1; } diff --git a/src/bindings/lua/expr.cpp b/src/bindings/lua/expr.cpp index dffe4faa8..543699d42 100644 --- a/src/bindings/lua/expr.cpp +++ b/src/bindings/lua/expr.cpp @@ -242,7 +242,7 @@ static int expr_args(lua_State * L) { expr & e = to_app(L, 1); push_expr(L, e); // upvalue(1): expr lua_pushinteger(L, 0); // upvalue(2): index - lua_pushcclosure(L, &expr_next_arg, 2); // create closure with 2 upvalues + lua_pushcclosure(L, &safe_function, 2); // create closure with 2 upvalues return 1; } diff --git a/src/bindings/lua/goal.cpp b/src/bindings/lua/goal.cpp new file mode 100644 index 000000000..5346100b0 --- /dev/null +++ b/src/bindings/lua/goal.cpp @@ -0,0 +1,176 @@ +/* +Copyright (c) 2013 Microsoft Corporation. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. + +Author: Leonardo de Moura +*/ +#include +#include +#include "library/tactic/goal.h" +#include "bindings/lua/util.h" +#include "bindings/lua/name.h" +#include "bindings/lua/expr.h" +#include "bindings/lua/formatter.h" +#include "bindings/lua/format.h" +#include "bindings/lua/options.h" + +namespace lean { +DECL_UDATA(hypotheses) + +static int mk_hypotheses(lua_State * L) { + int nargs = lua_gettop(L); + if (nargs == 0) { + return push_hypotheses(L, hypotheses()); + } else if (nargs == 2) { + return push_hypotheses(L, hypotheses(mk_pair(to_name_ext(L, 1), to_expr(L, 2)), hypotheses())); + } else if (nargs == 3) { + return push_hypotheses(L, hypotheses(mk_pair(to_name_ext(L, 1), to_expr(L, 2)), to_hypotheses(L, 3))); + } else { + throw exception("hypotheses functions expects 0 (empty list), 2 (name & expr for singleton hypotheses list), or 3 (name & expr & hypotheses list) arguments"); + } +} + +static int hypotheses_is_nil(lua_State * L) { + lua_pushboolean(L, !to_hypotheses(L, 1)); + return 1; +} + +static int hypotheses_head(lua_State * L) { + hypotheses const & hs = to_hypotheses(L, 1); + if (!hs) + throw exception("head method expects a non-empty hypotheses list"); + push_name(L, head(hs).first); + push_expr(L, head(hs).second); + return 2; +} + +static int hypotheses_tail(lua_State * L) { + hypotheses const & hs = to_hypotheses(L, 1); + if (!hs) + throw exception("tail method expects a non-empty hypotheses list"); + push_hypotheses(L, tail(hs)); + return 1; +} + +static int hypotheses_next(lua_State * L) { + hypotheses & hs = to_hypotheses(L, lua_upvalueindex(1)); + if (hs) { + push_hypotheses(L, tail(hs)); + lua_replace(L, lua_upvalueindex(1)); + push_name(L, head(hs).first); + push_expr(L, head(hs).second); + return 2; + } else { + lua_pushnil(L); + return 1; + } +} + +static int hypotheses_items(lua_State * L) { + hypotheses & hs = to_hypotheses(L, 1); + push_hypotheses(L, hs); // upvalue(1): hypotheses + lua_pushcclosure(L, &safe_function, 1); // create closure with 1 upvalue + return 1; +} + +static int hypotheses_len(lua_State * L) { + lua_pushinteger(L, length(to_hypotheses(L, 1))); + return 1; +} + +static const struct luaL_Reg hypotheses_m[] = { + {"__gc", hypotheses_gc}, // never throws + {"__len", safe_function}, + {"size", safe_function}, + {"pairs", safe_function}, + {"is_nil", safe_function}, + {"empty", safe_function}, + {"head", safe_function}, + {"tail", safe_function}, + {0, 0} +}; + +DECL_UDATA(goal) + +static int mk_goal(lua_State * L) { + return push_goal(L, goal(to_hypotheses(L, 1), to_expr(L, 2))); +} + +static int goal_is_null(lua_State * L) { + lua_pushboolean(L, !to_goal(L, 1)); + return 1; +} + +static int goal_hypotheses(lua_State * L) { + return push_hypotheses(L, to_goal(L, 1).get_hypotheses()); +} + +static int goal_conclusion(lua_State * L) { + return push_expr(L, to_goal(L, 1).get_conclusion()); +} + +static int goal_unique_name(lua_State * L) { + return push_name(L, to_goal(L, 1).mk_unique_hypothesis_name(to_name_ext(L, 2))); +} + +static int goal_tostring(lua_State * L) { + std::ostringstream out; + goal & g = to_goal(L, 1); + if (g) { + formatter fmt = get_global_formatter(L); + options opts = get_global_options(L); + out << mk_pair(g.pp(fmt, opts), opts); + } else { + out << ""; + } + lua_pushstring(L, out.str().c_str()); + return 1; +} + +static int goal_pp(lua_State * L) { + int nargs = lua_gettop(L); + goal & g = to_goal(L, 1); + if (!g) { + return push_format(L, format()); + } else if (nargs == 1) { + return push_format(L, g.pp(get_global_formatter(L), get_global_options(L))); + } else if (nargs == 2) { + if (is_formatter(L, 2)) + return push_format(L, g.pp(to_formatter(L, 2), get_global_options(L))); + else + return push_format(L, g.pp(get_global_formatter(L), to_options(L, 2))); + } else { + return push_format(L, g.pp(to_formatter(L, 2), to_options(L, 3))); + } +} + +static const struct luaL_Reg goal_m[] = { + {"__gc", goal_gc}, // never throws + {"__tostring", safe_function}, + {"is_null", safe_function}, + {"hypotheses", safe_function}, + {"hyps", safe_function}, + {"conclusion", safe_function}, + {"unique_name", safe_function}, + {"pp", safe_function}, + {0, 0} +}; + +void open_goal(lua_State * L) { + luaL_newmetatable(L, hypotheses_mt); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + setfuncs(L, hypotheses_m, 0); + + SET_GLOBAL_FUN(hypotheses_pred, "is_hypotheses"); + SET_GLOBAL_FUN(mk_hypotheses, "hypotheses"); + + luaL_newmetatable(L, goal_mt); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + setfuncs(L, goal_m, 0); + + SET_GLOBAL_FUN(goal_pred, "is_goal"); + SET_GLOBAL_FUN(mk_goal, "goal"); +} +} diff --git a/src/bindings/lua/goal.h b/src/bindings/lua/goal.h new file mode 100644 index 000000000..00a4a7e96 --- /dev/null +++ b/src/bindings/lua/goal.h @@ -0,0 +1,13 @@ +/* +Copyright (c) 2013 Microsoft Corporation. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. + +Author: Leonardo de Moura +*/ +#pragma once +#include +namespace lean { +UDATA_DEFS(hypotheses) +UDATA_DEFS(goal) +void open_goal(lua_State * L); +} diff --git a/src/bindings/lua/justification.cpp b/src/bindings/lua/justification.cpp index 341a2ef2a..d4561672f 100644 --- a/src/bindings/lua/justification.cpp +++ b/src/bindings/lua/justification.cpp @@ -67,7 +67,7 @@ static int justification_children(lua_State * L) { i = i + 1; } lua_pushinteger(L, 1); - lua_pushcclosure(L, &justification_next_child, 2); // create closure with 2 upvalues + lua_pushcclosure(L, &safe_function, 2); // create closure with 2 upvalues return 1; } diff --git a/src/bindings/lua/leanlua_state.cpp b/src/bindings/lua/leanlua_state.cpp index 144e67e06..1949c7aed 100644 --- a/src/bindings/lua/leanlua_state.cpp +++ b/src/bindings/lua/leanlua_state.cpp @@ -33,6 +33,7 @@ Author: Leonardo de Moura #include "bindings/lua/environment.h" #include "bindings/lua/justification.h" #include "bindings/lua/metavar_env.h" +#include "bindings/lua/goal.h" #include "bindings/lua/io_state.h" #include "bindings/lua/type_inferer.h" #include "bindings/lua/frontend_lean.h" @@ -185,6 +186,7 @@ struct leanlua_state::imp { open_metavar_env(m_state); open_state(m_state); open_type_inferer(m_state); + open_goal(m_state); open_frontend_lean(m_state); open_thread(m_state); open_interrupt(m_state); diff --git a/tests/lua/goal1.lua b/tests/lua/goal1.lua new file mode 100644 index 000000000..4160baff7 --- /dev/null +++ b/tests/lua/goal1.lua @@ -0,0 +1,31 @@ +local hs = hypotheses() +assert(is_hypotheses(hs)) +assert(hs:empty()) +assert(hs:is_nil()) +local hs = hypotheses("H1", Const("q"), hs) +local hs = hypotheses("H2", Const("p"), hs) +local n, p = hs:head() +assert(n == name("H2")) +assert(p == Const("p")) +assert(not hs:empty()) +assert(hs:tail():tail():empty()) +for n, p in hs:pairs() do + print(n, p) +end +assert(not pcall(function() hypotheses("H1", Const("q"), "H2", Const("p"), hs) end)) +assert(#hs == 2) +assert(hs:size() == 2) + +local g = goal(hs, Const("p1")) +assert(is_goal(g)) +print(g) +assert(#(g:hypotheses()) == 2) +assert(g:conclusion() == Const("p1")) +assert(not g:is_null()) +assert(g:unique_name("H") == name("H")) +assert(g:unique_name("H1") == name("H1", 1)) +print(g:pp()) +local opts = options() +opts = opts:update(name("pp", "unicode"), false) +print(opts) +print(g:pp(opts))