Zulip Chat Archive

Stream: lean4

Topic: Unexpected macro rule firing?


Siddharth Bhat (Oct 08 2021 at 10:22):

Consider the use of macros:

import Lean.Parser
import Init.Data.List
open Lean.Parser
open Std

declare_syntax_cat mlir_type
syntax "i" numLit : mlir_type -- this rule causes problems!

inductive MLIRTy : Type where
| int : Int -> MLIRTy

syntax "[mlir_type|" mlir_type "]": term

macro_rules
  | `([mlir_type| i $x:numLit ]) => `(MLIRTy.int $x)

def tyfoo : MLIRTy := [mlir_type| i 42]
#print tyfoo

def foo (xs: List Int) : IO Unit :=
  xs.forM $ fun i => do
    if i > 0 then IO.println ", "
    IO.println i

Surprisingly to me, the line xs.forM $ fun i => do creates a compile error:

foo.lean:22:16: error: expected '[', '_', '{', '|', '⦃', identifier or term

I don't understand why the elaborator attempts to elaborate the i at fun i => ... As far as I can tell, the i is not followed b a numLit, and thus the rule should not fire?

I'm on a recent nightly:

/tmp$ ~/work/lean4-contrib/build/stage1/bin/lean --version
Lean (version 4.0.0, commit 988e43d2b4a2, Release)

Gabriel Ebner (Oct 08 2021 at 10:28):

Writing "i" in a syntax declaration will register it as a keyword, i.e., it will no longer be parsed as an identifier. In this case however, there's no need to make it a keyword, you can also match on identifiers: https://github.com/leanprover/lean4/blob/master/tests/lean/nonReserved.lean

Gabriel Ebner (Oct 08 2021 at 10:31):

Mmmh, apparently writing &"i" numLit doesn't help here either. But ident numLit works.

Siddharth Bhat (Oct 08 2021 at 10:35):

Thank you for the pointer! Why doesn't &"i" work, though? I'm trying to build a mental model of how the macro system works!

Jannis Limperg (Oct 08 2021 at 10:59):

Syntax categories declared with declare_syntax_cat do not dispatch rules starting with &"stuff" correctly. To do this, a syntax category must have its LeadingIdentBehavior set to LeadingIdentBehavior.both or LeadingIdentBehavior.symbol, but declare_syntax_cat doesn't allow you to do this (at least last I checked). Here's a variant of declare_syntax_cat that exposes the option:

namespace Lean.Elab.Command

syntax (name := syntaxCatWithUnreservedTokens)
  "declare_syntax_cat' " ident
    (&"allow_leading_unreserved_tokens" <|> &"force_leading_unreserved_tokens")? : command

-- Copied from Lean/Elab/Syntax.lean
private def declareSyntaxCatQuotParser (catName : Name) : CommandElabM Unit := do
  if let Name.str _ suffix _ := catName then
    let quotSymbol := "`(" ++ suffix ++ "|"
    let name := catName ++ `quot
    -- TODO(Sebastian): this might confuse the pretty printer, but it lets us reuse the elaborator
    let kind := ``Lean.Parser.Term.quot
    let cmd  `(
      @[termParser] def $(mkIdent name) : Lean.ParserDescr :=
        Lean.ParserDescr.node $(quote kind) $(quote Lean.Parser.maxPrec)
          (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol $(quote quotSymbol))
            (Lean.ParserDescr.binary `andthen
              (Lean.ParserDescr.unary `incQuotDepth (Lean.ParserDescr.cat $(quote catName) 0))
              (Lean.ParserDescr.symbol ")"))))
    elabCommand cmd

open Lean.Parser (LeadingIdentBehavior) in
@[commandElab syntaxCatWithUnreservedTokens]
def elabDeclareSyntaxCatWithUnreservedTokens : CommandElab := fun stx => do
  let catName  := stx[1].getId
  let leadingIdentBehavior :=
    match stx[2].getOptional? with
    | none => LeadingIdentBehavior.default
    | some b =>
      match b.getAtomVal! with
      | "allow_leading_unreserved_tokens" => LeadingIdentBehavior.both
      | "force_leading_unreserved_tokens" => LeadingIdentBehavior.symbol
      | _ => unreachable!
  let attrName := catName.appendAfter "Parser"
  let env  getEnv
  let env 
    liftIO $ Parser.registerParserCategory env attrName catName
      leadingIdentBehavior
  setEnv env
  declareSyntaxCatQuotParser catName

end Lean.Elab.Command

Sebastian Ullrich (Oct 08 2021 at 14:26):

declare_syntax_cat conv (behavior := both)

from Init.Conv

Jannis Limperg (Oct 08 2021 at 17:51):

Oh nice so I can delete that code. :)


Last updated: Dec 20 2023 at 11:08 UTC