feat: add natural language demo

This commit is contained in:
Sebastian Ullrich 2020-06-13 16:20:23 +02:00
parent 46065a9b3b
commit e6b988a10a
3 changed files with 162 additions and 0 deletions

View file

@ -0,0 +1 @@
import ForTheLean.Demo

View file

@ -0,0 +1,82 @@
-- -*- origami-fold-style: triple-braces -*-
import ForTheLean.Prelim
new_frontend
open Lean
open Lean.Elab.Command
open Prelim
-- {{{ [synonym]
syntax variant := "-"? wlexem
syntax [synonym] "[synonym " wlexem ("/" (wlexem <|> variant))+ "]" : command
@[commandElab synonym]
def elabSynonym : CommandElab :=
fun stx => match_syntax stx with
| `([synonym $w:ident/ -$w':ident]) => modifyEnv $ fun env => addSynonym env w.getId (w.getId.appendAfter w'.getId.toString)
| `([synonym $w:ident/$w':ident]) => modifyEnv $ fun env => addSynonym env w.getId w'.getId
| _ => throwUnsupportedSyntax
-- }}}
[synonym number/numbers]
-- {{{
syntax indef := "A" <|> "a" <|> "An" <|> "an"
syntax art := "The" <|> "the" <|> indef
syntax notionPattern := art wlexem+
-- all class nouns are added dynamically
declare_syntax_cat classNoun
syntax "Signature." notionPattern "is" indef (classNoun <|> "notion") "." : command
macro_rules
| `(Signature. The $words:wlexem* is a notion.) =>
let words := words.map Syntax.getId;
let parsers := words.map mkSyntaxAtom;
let desc := mkIdent $ mkNameSimple $ "_".intercalate $ words.toList.map toString;
`(axiom $desc:ident : Type
syntax_synonyms [$desc] $parsers:syntax* : classNoun
@[macro $desc] def expandSig : Macro := fun _ => `($desc))
| `(Signature. The $words:wlexem* is a $n.) =>
let words := words.map Syntax.getId;
let parsers := words.map mkSyntaxAtom;
let desc := mkIdent $ mkNameSimple $ "_".intercalate $ words.toList.map toString;
`(axiom $desc:ident : $n
syntax_synonyms [$desc] $parsers:syntax* : classNoun
@[macro $desc] def expandSig : Macro := fun _ => `($desc))
-- }}}
Signature. A real number is a notion.
-- {{{
-- TODO: should be single character
syntax newVar := ident
syntax standFor := "stand" "for"
syntax standForDenote := standFor <|> "denote"
syntax "Let" (sepBy newVar ",") standForDenote (indef)? classNoun "." : command
macro_rules
| `(Let $vs* denote $indef* $noun.) =>
`(variables ($(vs.getSepElems.map (fun v => v.getArg 0)):ident* : $noun))
-- }}}
Let x,y stand for real numbers.
-- {{{
syntax var := ident -- TODO: should be single character
syntax uniPredPattern := var "is" wlexem+ var
syntax predPattern := uniPredPattern
syntax "Signature." predPattern "is" (indef)? "atom" "." : command
macro_rules
| `(Signature. $x:var is $words:wlexem* $y:var is an atom.) =>
let words := words.toList.map Syntax.getId;
let desc := mkNameSimple $ "_".intercalate $ words.map toString;
`(axiom $(mkIdent desc):ident : type_of $(x.getArg 0) → type_of $(y.getArg 0) → Prop)
-- }}}
Signature. x is greater than y is an atom.
Signature. A packing of congruent balls in Euclidean three space is a notion.
Signature. The face centered cubic packing is a packing of congruent balls in Euclidean three space.
Let P denote a packing of congruent balls in Euclidean three space.
-- incomplete from here on
Signature. The density of P is a real number.
Theorem The_Kepler_conjecture. No packing of congruent balls in Euclidean three space has density greater than the density of the face centered cubic packing.

View file

@ -0,0 +1,79 @@
import Lean
namespace Lean.Elab.Command
open Lean
-- to get around missing structure notation support
def modifyEnv (f : Environment → Environment) : CommandElabM Unit :=
modify $ fun s => { s with env := f s.env }
end Lean.Elab.Command
namespace Prelim
open Lean
open Lean.Parser
-- for declaring simple parsers I can still use within other `syntax`
@[commandParser] def syntaxAbbrev := parser! "syntax " >> ident >> " := " >> many1 syntaxParser
@[macro syntaxAbbrev] def elabSyntaxAbbrev : Macro :=
fun stx => match_syntax stx with
| `(syntax $id := $p*) => `(declare_syntax_cat $id syntax:0 $p* : $id)
| _ => Macro.throwUnsupported
def mkSyntaxAtom (n : Name) : Syntax :=
Syntax.node `Lean.Parser.Syntax.atom #[Lean.mkStxStrLit n.toString, mkNullNode]
-- store known synonyms of a word
-- HACK: can't define new environment extension at runtime, so I'm reusing this matching one...
def addSynonym (env : Environment) (a : Name) (e : Name) : Environment :=
addAlias env (`_subst ++ a) (e)
def getSynonyms (env : Environment) (a : Name) : List Name :=
match (aliasExtension.getState env).find? (`_subst ++ a) with
| none => []
| some es => es
def checkPrev (p : Syntax → Bool) (errorMsg : String) : Parser :=
{ fn := fun c s =>
let prev := s.stxStack.back;
if p prev then s else s.mkError errorMsg }
-- a word lexem is any identifier/keyword except for variables and "is"
def wlexem : Parser :=
try (rawIdent >> checkPrev (fun stx => stx.getId.toString.length > 1 && not ([`is].contains stx.getId)) "")
end Prelim
new_frontend
open Lean
open Lean.Elab
open Lean.Elab.Command
open Prelim
syntax [type_of] "type_of" term:max : term
@[termElab «type_of»]
def elabTypeOf : Term.TermElab :=
fun stx _ => match_syntax stx with
| `(type_of $e) =>
Term.elabTerm e none >>= Term.inferType e
| _ => Term.throwUnsupportedSyntax
syntax [syntax_synonyms] "syntax_synonyms" "[" ident "]" «syntax»+ ":" ident : command
@[commandElab «syntax_synonyms»] def elabSyntaxSynonyms : CommandElab :=
fun stx => match_syntax stx with
| `(syntax_synonyms [$kind] $stxs* : $cat) =>
-- TODO: do notation
getEnv >>= fun env =>
-- map word w with synonyms w1... to syntax ("w" <|> "w1" <|> ...)
let stxs := stxs.map (fun stx =>
if stx.isOfKind `Lean.Parser.Syntax.atom then
let word := (Lean.Syntax.isStrLit? (stx.getArg 0)).getD "";
let substs := getSynonyms env (mkNameSimple word);
-- TODO: `(syntax|` antiquotation
-- TODO: `foldl1` would be nice
substs.foldl (fun stx word => Syntax.node `Lean.Parser.Syntax.orelse #[stx, mkAtom "<|>", mkSyntaxAtom word])
stx
else stx);
`(syntax [$kind] $stxs:syntax* : $cat) >>= elabCommand
| _ => throwUnsupportedSyntax
-- HACK: get `wlexem` into `syntax` world
declare_syntax_cat wlexem
@[wlexemParser] def wlexem := Prelim.wlexem