diff --git a/examples/ex4.lean b/examples/ex4.lean new file mode 100644 index 000000000..74ff3a4b9 --- /dev/null +++ b/examples/ex4.lean @@ -0,0 +1,15 @@ +Show (fun x : Bool, (fun y : Bool, x /\ y)) +Show let x := true, + y := true + in (let z := x /\ y, + f := (fun arg1 arg2 : Bool, arg1 /\ arg2 <=> + arg2 /\ arg1 <=> + arg1 \/ arg2 \/ arg2) + in (f x y) \/ z) +Eval let x := true, + y := true, + z := x /\ y, + f := (fun arg1 arg2 : Bool, arg1 /\ arg2 <=> + arg2 /\ arg1 <=> + arg1 \/ arg2 \/ arg2) + in (f x y) \/ z diff --git a/src/frontend/builtin_notation.cpp b/src/frontend/builtin_notation.cpp index f36e3c1b5..aaa9f58e7 100644 --- a/src/frontend/builtin_notation.cpp +++ b/src/frontend/builtin_notation.cpp @@ -21,5 +21,7 @@ void init_builtin_notation(frontend & f) { f.add_infixr("\u2228", 30, const_name(mk_or_fn())); f.add_infixr("=>", 25, const_name(mk_implies_fn())); f.add_infixr("\u21D2", 25, const_name(mk_implies_fn())); + f.add_infixr("<=>", 25, const_name(mk_iff_fn())); + f.add_infixr("\u21D4", 25, const_name(mk_iff_fn())); } } diff --git a/src/frontend/parser.cpp b/src/frontend/parser.cpp index 771810f08..1aa9b4610 100644 --- a/src/frontend/parser.cpp +++ b/src/frontend/parser.cpp @@ -13,9 +13,10 @@ Author: Leonardo de Moura #include "type_check.h" #include "free_vars.h" #include "builtin_notation.h" -#include "pp.h" #include "builtin.h" #include "arith.h" +#include "pp.h" +#include "printer.h" namespace lean { static name g_definition_kwd("Definition"); @@ -81,6 +82,8 @@ struct parser_fn { bool curr_is_identifier() const { return curr() == scanner::token::Id; } bool curr_is_lparen() const { return curr() == scanner::token::LeftParen; } bool curr_is_colon() const { return curr() == scanner::token::Colon; } + bool curr_is_comma() const { return curr() == scanner::token::Comma; } + bool curr_is_in() const { return curr() == scanner::token::In; } void check_identifier(char const * msg) { if (!curr_is_identifier()) throw parser_error(msg); } name check_identifier_next(char const * msg) { check_identifier(msg); name r = curr_name(); next(); return r; } @@ -348,6 +351,31 @@ struct parser_fn { return parse_abstraction(false); } + expr parse_let() { + next(); + local_decls::mk_scope scope(m_local_decls); + buffer> bindings; + while (true) { + name id = check_identifier_next("invalid let expression, identifier expected"); + check_assign_next("invalid let expression, ':=' expected"); + expr val = parse_expr(); + register_binding(id); + bindings.push_back(mk_pair(id, val)); + if (curr_is_in()) { + next(); + expr r = parse_expr(); + unsigned i = bindings.size(); + while (i > 0) { + --i; + r = mk_let(bindings[i].first, bindings[i].second, r); + } + return r; + } else { + check_comma_next("invalid let expression, ',' or 'in' expected"); + } + } + } + expr parse_int() { expr r = mk_int_value(m_scanner.get_num_val().get_numerator()); next(); @@ -374,6 +402,7 @@ struct parser_fn { case scanner::token::LeftParen: return parse_lparen(); case scanner::token::Lambda: return parse_lambda(); case scanner::token::Pi: return parse_pi(); + case scanner::token::Let: return parse_let(); case scanner::token::IntVal: return parse_int(); case scanner::token::DecimalVal: return parse_decimal(); case scanner::token::StringVal: return parse_string(); diff --git a/src/frontend/scanner.cpp b/src/frontend/scanner.cpp index 9099c4f1a..5b63e07ed 100644 --- a/src/frontend/scanner.cpp +++ b/src/frontend/scanner.cpp @@ -19,6 +19,8 @@ static name g_arrow_unicode("\u2192"); static name g_lambda_name("fun"); static name g_type_name("Type"); static name g_pi_name("Pi"); +static name g_let_name("let"); +static name g_in_name("in"); static name g_arrow_name("->"); static name g_eq_name("="); @@ -193,6 +195,10 @@ scanner::token scanner::read_a_symbol() { return token::Pi; else if (m_name_val == g_type_name) return token::Type; + else if (m_name_val == g_let_name) + return token::Let; + else if (m_name_val == g_in_name) + return token::In; else return is_command(m_name_val) ? token::CommandId : token::Id; } @@ -347,6 +353,8 @@ std::ostream & operator<<(std::ostream & out, scanner::token const & t) { case scanner::token::Lambda: out << g_lambda_unicode; break; case scanner::token::Pi: out << g_pi_unicode; break; case scanner::token::Arrow: out << g_arrow_unicode; break; + case scanner::token::Let: out << "let"; break; + case scanner::token::In: out << "in"; break; case scanner::token::Id: out << "Id"; break; case scanner::token::CommandId: out << "CId"; break; case scanner::token::IntVal: out << "Int"; break; diff --git a/src/frontend/scanner.h b/src/frontend/scanner.h index cf7743b7c..c10a3e984 100644 --- a/src/frontend/scanner.h +++ b/src/frontend/scanner.h @@ -19,7 +19,7 @@ class scanner { public: enum class token { LeftParen, RightParen, LeftCurlyBracket, RightCurlyBracket, Colon, Comma, Period, Lambda, Pi, Arrow, - Id, CommandId, IntVal, DecimalVal, StringVal, Eq, Assign, Type, Eof + Let, In, Id, CommandId, IntVal, DecimalVal, StringVal, Eq, Assign, Type, Eof }; protected: int m_spos; // position in the current line of the stream diff --git a/src/kernel/builtin.cpp b/src/kernel/builtin.cpp index 4ceacc704..cecdd0ee2 100644 --- a/src/kernel/builtin.cpp +++ b/src/kernel/builtin.cpp @@ -167,6 +167,7 @@ MK_BUILTIN(ite_fn, ite_fn_value); MK_CONSTANT(if_fn, name("if")); MK_CONSTANT(implies_fn, name("implies")); +MK_CONSTANT(iff_fn, name("iff")); MK_CONSTANT(and_fn, name("and")); MK_CONSTANT(or_fn, name("or")); MK_CONSTANT(not_fn, name("not")); @@ -210,6 +211,9 @@ void add_basic_theory(environment & env) { // implies(x, y) := ite x y True env.add_definition(implies_fn_name, p2, Fun({{x, Bool}, {y, Bool}}, bIf(x, y, True))); + // iff(x, y) := x = y + env.add_definition(iff_fn_name, p2, Fun({{x, Bool}, {y, Bool}}, Eq(x, y))); + // not(x) := ite x False True env.add_definition(not_fn_name, p1, Fun({x, Bool}, bIf(x, False, True))); diff --git a/src/kernel/builtin.h b/src/kernel/builtin.h index c2ec682a0..3616230c0 100644 --- a/src/kernel/builtin.h +++ b/src/kernel/builtin.h @@ -67,6 +67,14 @@ inline expr mk_implies(unsigned num_args, expr const * args) { lean_assert(num_a inline expr Implies(expr const & e1, expr const & e2) { return mk_implies(e1, e2); } inline expr Implies(std::initializer_list const & l) { return mk_implies(l.size(), l.begin()); } +/** \brief Return the Lean Iff operator */ +expr mk_iff_fn(); +/** \brief Return (e1 iff e2) */ +inline expr mk_iff(expr const & e1, expr const & e2) { return mk_app(mk_iff_fn(), e1, e2); } +inline expr mk_iff(unsigned num_args, expr const * args) { return mk_bin_rop(mk_iff_fn(), True, num_args, args); } +inline expr Iff(expr const & e1, expr const & e2) { return mk_iff(e1, e2); } +inline expr Iff(std::initializer_list const & l) { return mk_iff(l.size(), l.begin()); } + /** \brief Return the Lean And operator */ expr mk_and_fn(); /** \brief Return (e1 and e2) */