【问题标题】:How to pass a function as argument to a generated Happy parser?如何将函数作为参数传递给生成的 Happy 解析器?
【发布时间】:2017-07-18 08:28:36
【问题描述】:

Happy 生成带有签名的解析器 :: [Token] -> a

我想生成一个参数化解析器,即需要一个函数作为参数来传递解析器的函数。 所以我想要签名:::(x->y) -> [Token] -> a。 但是,我也可以使用签名 ::[Token] -> (x->y) -> a

功能固定后,我可以解决 通过导入和分配函数。

import Functions (fixedFunction)

Root : Production Rule
       { $$.argument = fixedFunction
       }

当参数是Show的实例时,我可以如下解决

Alex:
   data Token = ...
              |  Carg            ArgType
Happy:
    %token 
        ...
        Argument { Carg $$ }

    Root : Argument Production Rule
           {  $$.argument = $1
           }

参见例如我的项目 TorXakis 了解更多详细信息,特别是文件夹 https://github.com/TorXakis/TorXakis/tree/develop/sys/front/src

但是,我无法传递作为函数的变量参数,因为函数不是从 Show 派生的! 由于 Haskell 是一种函数式语言,我强烈怀疑我遗漏了一些微不足道的东西,但我没有看到它...... 任何人都可以提供一个将函数传递给快乐生成的解析器的例子吗? 提前致谢!

皮埃尔

【问题讨论】:

    标签: function haskell functional-programming happy


    【解决方案1】:

    happy 允许你在 Monad 上工作。它可以使用具有以下两个签名之一的 lexer 函数:

    1. [Token] -> a
    2. Monad m => (Token -> m a) -> m a

    第一个选项是上下文无关的,第二个是上下文感知的。如果您需要将额外的参数传递给 lexer 函数,您可以执行以下两种操作之一:

    1. lexer 部分应用于.y 文件中的函数,如下所示:

      %lexer { lexer fixedFunction }

      您的lexer 函数将具有T -> [Token] -> a 类型,其中TfixedFunction 的类型。

    2. 在某些上下文中传递函数,例如Reader monad。我使用State monad 来跟踪令牌位置。您可以在此处查看我的示例:my monadmy lexer

    使用任何解决方案,您都可以向 lexer 添加额外的参数和一些额外的上下文。

    【讨论】:

    • 据我所知,在您的一元解析器中,您不使用属性语法。你知道在 Happy 中是否可以混合使用一元解析器和属性语法吗?
    • @DamianNadales 似乎有可能。只有条件规则的一些语法差异:haskell.org/happy/doc/html/sec-AtrributeGrammarsInHappy.html 我无法想象为什么不能混合一元解析器和属性。
    • 我问是因为使用monadic parser需要特殊语法{%...} ,不知道能不能做到。
    • @DamianNadales 我想这不是问题。在 {%...} 中,您可以在 monad 中编写任何 monadic 操作。我没有使用属性,我对它们没有太多经验。但似乎有可能。只是尝试进行实验。如果你坚持一些小例子,那么你可以就这两个问题提出具体问题。
    • 我尝试混合属性语法和单子解析器,但到目前为止我失败了。我猜 Haskell 解析器不使用属性语法是有原因的:github.com/ghc/ghc/blob/master/compiler/parser/Parser.y
    【解决方案2】:

    这个例子是基于标准的快乐例子( 参见例如https://www.haskell.org/happy/doc/html/sec-using.html) 这个例子没有使用 monad 也没有使用属性。

    表达式解析器需要一个函数来“标准化”变量名。 例如,使它们不区分大小写,或者像在旧的编程语言中一样,只考虑前 8 个字符。

    解析器是:

    {
    module Calc
    ( calc
    , lexer
    )
    where
    import Data.Char
    }
    %name calc
    %tokentype { Token }
    %error { parseError }
    
    %token 
          let             { TokenLet }
          in              { TokenIn }
          int             { TokenInt $$ }
          var             { TokenVar $$ }
          '='             { TokenEq }
          '+'             { TokenPlus }
          '-'             { TokenMinus }
          '*'             { TokenTimes }
          '/'             { TokenDiv }
          '('             { TokenOB }
          ')'             { TokenCB }
    
    %%
    
    Exp   :: { (String -> String) -> Exp } 
          : let var '=' Exp in Exp  { \p -> Let (p $2) ($4 p) ($6 p) }
          | Exp1                    { \p -> Exp1 ($1 p) }
    
    Exp1  :: { (String -> String) -> Exp1 } 
          : Exp1 '+' Term           { \p -> Plus ($1 p) ($3 p) }
          | Exp1 '-' Term           { \p -> Minus ($1 p) ($3 p) }
          | Term                    { \p -> Term ($1 p) }
    
    Term  :: { (String -> String) -> Term } 
          : Term '*' Factor         { \p -> Times ($1 p) ($3 p) }
          | Term '/' Factor         { \p -> Div ($1 p) ($3 p) }
          | Factor                  { \p -> Factor ($1 p) }
    
    Factor:: { (String -> String) -> Factor }
          : int                     { \p -> Int $1 }
          | var                     { \p -> Var (p $1) }
          | '(' Exp ')'             { \p -> Brack ($2 p) }
    
    {
    parseError :: [Token] -> a
    parseError _ = error "Parse error"
    
    data Exp  
          = Let String Exp Exp
          | Exp1 Exp1
          deriving Show
    
    data Exp1 
          = Plus Exp1 Term 
          | Minus Exp1 Term 
          | Term Term
          deriving Show
    
    data Term 
          = Times Term Factor 
          | Div Term Factor 
          | Factor Factor
          deriving Show
    
    data Factor 
          = Int Int 
          | Var String 
          | Brack Exp
          deriving Show
    
    data Token
          = TokenLet
          | TokenIn
          | TokenInt Int
          | TokenVar String
          | TokenEq
          | TokenPlus
          | TokenMinus
          | TokenTimes
          | TokenDiv
          | TokenOB
          | TokenCB
     deriving Show
    
    
    lexer :: String -> [Token]
    lexer [] = []
    lexer (c:cs) 
          | isSpace c = lexer cs
          | isAlpha c = lexVar (c:cs)
          | isDigit c = lexNum (c:cs)
    lexer ('=':cs) = TokenEq : lexer cs
    lexer ('+':cs) = TokenPlus : lexer cs
    lexer ('-':cs) = TokenMinus : lexer cs
    lexer ('*':cs) = TokenTimes : lexer cs
    lexer ('/':cs) = TokenDiv : lexer cs
    lexer ('(':cs) = TokenOB : lexer cs
    lexer (')':cs) = TokenCB : lexer cs
    
    lexNum cs = TokenInt (read num) : lexer rest
          where (num,rest) = span isDigit cs
    
    lexVar cs =
       case span isAlpha cs of
          ("let",rest) -> TokenLet : lexer rest
          ("in",rest)  -> TokenIn : lexer rest
          (var,rest)   -> TokenVar var : lexer rest
    
    }
    

    而使用解析器的Main是

    module Main
    
    where 
    import Data.Char
    import Calc
    
    caseSensitive :: String -> String
    caseSensitive = id
    
    caseInsensitive :: String -> String
    caseInsensitive = map toUpper
    
    firstEight :: String -> String
    firstEight = take 8
    
    main :: IO ()
    main = getContents >>= (\a -> print (calc (lexer a) caseInsensitive) )
    

    将表达式解析器与不区分大小写的函数和输入一起使用

    let aap = 7 in Aap + AAP
    

    输出结果

    Let "AAP" (Exp1 (Term (Factor (Int 7)))) (Exp1 (Plus (Term (Factor (Var "AAP"))) (Factor (Var "AAP"))))
    

    这部分回答了我自己的问题:我想使用属性而不是像本例中那样明确地传递函数...

    【讨论】:

    • 好的,现在我更了解你的问题了。是的,happy 的 AST 需要 Show 实例。您可以为所有类型的函数添加假的 Show 实例,并对此感到满意。但可能只使用 parser combinators 而不是 parser generators 会简单得多。
    猜你喜欢
    • 2023-03-14
    • 2017-05-07
    • 2019-09-27
    • 2017-08-13
    • 2013-01-27
    • 2016-01-07
    • 2014-09-23
    • 1970-01-01
    • 2021-11-22
    相关资源
    最近更新 更多