1

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 是一种函数式语言,我强烈怀疑我遗漏了一些微不足道的东西,但我没有看到它......任何人都可以提供一个将函数传递给快乐生成的解析器的示例吗?提前致谢!

皮埃尔

4

2 回答 2

2

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

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

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

  1. 部分适用lexer于您在.y文件中的功能,如下所示:

    %lexer { lexer fixedFunction }

    并且您的lexer函数将具有type of 的T -> [Token] -> a类型。TfixedFunction

  2. 在某些上下文中传递函数,例如Readermonad。我使用Statemonad 来跟踪令牌位置。你可以在这里看到我的例子:我的 monad我的 lexer

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

于 2017-07-18T10:46:55.947 回答
0

此示例基于标准快乐示例(参见例如https://www.haskell.org/happy/doc/html/sec-using.html)此示例不使用单子和属性。

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

}

而使用解析器的主要是

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"))))

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

于 2017-07-19T07:21:55.567 回答