@@ -1384,33 +1384,96 @@ meta def eval_prime : expr → tactic (expr × expr)
1384
1384
| _ := failed
1385
1385
1386
1386
/-- This version of `derive` does not fail when the input is already a numeral -/
1387
- meta def derive' (e : expr) : tactic (expr × expr) :=
1387
+ meta def derive.step (e : expr) : tactic (expr × expr) :=
1388
1388
eval_field e <|> eval_nat_int_ext e <|>
1389
1389
eval_pow e <|> eval_ineq e <|> eval_prime e
1390
1390
1391
- meta def derive : expr → tactic (expr × expr) | e :=
1391
+ /-- An attribute for adding additional extensions to `norm_num`. To use this attribute, put
1392
+ `@[norm_num]` on a tactic of type `expr → tactic (expr × expr)`; the tactic will be called on
1393
+ subterms by `norm_num`, and it is responsible for identifying that the expression is a numerical
1394
+ function applied to numerals, for example `nat.fib 17`, and should return the reduced numerical
1395
+ expression (which must be in `norm_num`-normal form: a natural or rational numeral, i.e. `37`,
1396
+ `12 / 7` or `-(2 / 3)`, although this can be an expression in any type), and the proof that the
1397
+ original expression is equal to the rewritten expression.
1398
+
1399
+ Failure is used to indicate that this tactic does not apply to the term. For performance reasons,
1400
+ it is best to detect non-applicability as soon as possible so that the next tactic can have a go,
1401
+ so generally it will start with a pattern match and then checking that the arguments to the term
1402
+ are numerals or of the appropriate form, followed by proof construction, which should not fail.
1403
+
1404
+ Propositions are treated like any other term. The normal form for propositions is `true` or
1405
+ `false`, so it should produce a proof of the form `p = true` or `p = false`. `eq_true_intro` can be
1406
+ used to help here.
1407
+ -/
1408
+ @[user_attribute]
1409
+ protected meta def attr : user_attribute (expr → tactic (expr × expr)) unit :=
1410
+ { name := `norm_num ,
1411
+ descr := " Add norm_num derivers" ,
1412
+ cache_cfg :=
1413
+ { mk_cache := λ ns, do {
1414
+ t ← ns.mfoldl
1415
+ (λ (t : expr → tactic (expr × expr)) n, do
1416
+ t' ← eval_expr (expr → tactic (expr × expr)) (expr.const n []),
1417
+ pure (λ e, t' e <|> t e))
1418
+ (λ _, failed),
1419
+ pure (λ e, derive.step e <|> t e) },
1420
+ dependencies := [] } }
1421
+
1422
+ add_tactic_doc
1423
+ { name := " norm_num" ,
1424
+ category := doc_category.attr,
1425
+ decl_names := [`norm_num .attr],
1426
+ tags := [" arithmetic" , " decision_procedure" ] }
1427
+
1428
+ /-- Look up the `norm_num` extensions in the cache and return a tactic extending `derive.step` with
1429
+ additional reduction procedures. -/
1430
+ meta def get_step : tactic (expr → tactic (expr × expr)) := norm_num.attr.get_cache
1431
+
1432
+ /-- Simplify an expression bottom-up using `step` to simplify the subexpressions. -/
1433
+ meta def derive' (step : expr → tactic (expr × expr))
1434
+ : expr → tactic (expr × expr) | e :=
1392
1435
do e ← instantiate_mvars e,
1393
1436
(_, e', pr) ←
1394
1437
ext_simplify_core () {} simp_lemmas.mk (λ _, failed) (λ _ _ _ _ _, failed)
1395
1438
(λ _ _ _ _ e,
1396
- do (new_e, pr) ← derive' e,
1439
+ do (new_e, pr) ← step e,
1397
1440
guard (¬ new_e =ₐ e),
1398
1441
return ((), new_e, some pr, tt))
1399
1442
`eq e,
1400
1443
return (e', pr)
1401
1444
1445
+ /-- Simplify an expression bottom-up using the default `norm_num` set to simplify the
1446
+ subexpressions. -/
1447
+ meta def derive (e : expr) : tactic (expr × expr) := do f ← get_step, derive' f e
1448
+
1402
1449
end norm_num
1403
1450
1451
+ /-- Basic version of `norm_num` that does not call `simp`. It uses the provided `step` tactic
1452
+ to simplify the expression; use `get_step` to get the default `norm_num` set and `derive.step` for
1453
+ the basic builtin set of simplifications. -/
1454
+ meta def tactic.norm_num1 (step : expr → tactic (expr × expr))
1455
+ (loc : interactive.loc) : tactic unit :=
1456
+ do ns ← loc.get_locals,
1457
+ tt ← tactic.replace_at (norm_num.derive' step) ns loc.include_goal
1458
+ | fail " norm_num failed to simplify" ,
1459
+ when loc.include_goal $ try tactic.triv,
1460
+ when (¬ ns.empty) $ try tactic.contradiction
1461
+
1462
+ /-- Normalize numerical expressions. It uses the provided `step` tactic to simplify the expression;
1463
+ use `get_step` to get the default `norm_num` set and `derive.step` for the basic builtin set of
1464
+ simplifications. -/
1465
+ meta def tactic.norm_num (step : expr → tactic (expr × expr))
1466
+ (hs : list simp_arg_type) (l : interactive.loc) : tactic unit :=
1467
+ repeat1 $ orelse' (tactic.norm_num1 step l) $
1468
+ interactive.simp_core {} (tactic.norm_num1 step (interactive.loc.ns [none]))
1469
+ ff (simp_arg_type.except ``one_div :: hs) [] l
1470
+
1404
1471
namespace tactic.interactive
1405
1472
open norm_num interactive interactive.types
1406
1473
1407
1474
/-- Basic version of `norm_num` that does not call `simp`. -/
1408
1475
meta def norm_num1 (loc : parse location) : tactic unit :=
1409
- do ns ← loc.get_locals,
1410
- tt ← tactic.replace_at derive ns loc.include_goal
1411
- | fail " norm_num failed to simplify" ,
1412
- when loc.include_goal $ try tactic.triv,
1413
- when (¬ ns.empty) $ try tactic.contradiction
1476
+ do f ← get_step, tactic.norm_num1 f loc
1414
1477
1415
1478
/-- Normalize numerical expressions. Supports the operations
1416
1479
`+` `-` `*` `/` `^` and `%` over numerical types such as
@@ -1419,8 +1482,7 @@ and can prove goals of the form `A = B`, `A ≠ B`, `A < B` and `A ≤ B`,
1419
1482
where `A` and `B` are numerical expressions.
1420
1483
It also has a relatively simple primality prover. -/
1421
1484
meta def norm_num (hs : parse simp_arg_list) (l : parse location) : tactic unit :=
1422
- repeat1 $ orelse' (norm_num1 l) $
1423
- simp_core {} (norm_num1 (loc.ns [none])) ff (simp_arg_type.except ``one_div :: hs) [] l
1485
+ do f ← get_step, tactic.norm_num f hs l
1424
1486
1425
1487
add_hint_tactic " norm_num"
1426
1488
0 commit comments