2017-07-18 1 views
1

Heureux génère un analyseur avec la signature :: [Token] -> aComment passer une fonction en argument à un parseur Happy généré?

Je voudrais générer un analyseur paramétrés, à savoir, une fonction qui a besoin d'une fonction comme argument pour fournir un analyseur. Donc je voudrais la signature :: (x->y) -> [Token] -> a. Pourtant, je peux aussi travailler avec la signature :: [Token] -> (x->y) -> a.

Lorsque la fonction est fixe, je peux la résoudre en important et en affectant la fonction.

import Functions (fixedFunction) 

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

Lorsque l'argument est une instance de Show, je peux le résoudre comme suit

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

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

Voir par exemple mon projet TorXakis pour plus de détails, en particulier le dossier https://github.com/TorXakis/TorXakis/tree/develop/sys/front/src

Cependant, je suis incapable de passer un argument variable qui est une fonction puisqu'une fonction ne dérive pas de Show! Puisque Haskell est un langage fonctionnel, j'ai la forte suspicion que je manque quelque chose de trivial, mais je ne le vois pas ... Quelqu'un peut-il donner un exemple de passage d'une fonction à un analyseur heureusement généré? Merci d'avance!

Pierre

Répondre

2

happy vous permet de travailler sur Monad. Il peut consommer lexer fonctions avec l'une des deux signatures:

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

La première option est hors-contexte et le second est sensible au contexte. Si vous avez besoin de passer des arguments supplémentaires pour lexer fonction que vous pouvez faire l'une des deux choses:

  1. appliquent partiellement lexer vous fonctionnez dans .y fichier comme ceci:

    %lexer { lexer fixedFunction }

    Et votre lexer fonction aura le type T -> [Token] -> aT est le type de fixedFunction.

  2. Passez la fonction dans un contexte, par exemple Reader monad. J'ai utilisé la monade State pour suivre les positions des jetons. Vous pouvez voir mes exemples ici: my monad et my lexer.

Avec une solution, vous pouvez ajouter des arguments supplémentaires et un contexte supplémentaire à votre lexer.

+0

Dans votre analyseur monadique vous n'utilisez pas grammaires attribu es pour autant que je peut voir. Savez-vous si, dans Happy, vous pouvez mélanger des parseurs monadiques et attribuer des grammaires? –

+1

@DamianNadales semble comme c'est possible. Seule une différence syntaxique dans les règles conditionnelles: https://www.haskell.org/happy/doc/html/sec-AributeGrammarsInHappy.html Je ne peux pas imaginer pourquoi il ne devrait pas être possible de mélanger les analyseurs et les attributs monadiques. – Shersh

+0

J'ai demandé parce que l'utilisation de l'analyseur monadique nécessite une syntaxe spéciale '{% ...}' et je ne sais pas si cela peut être fait. –

0

Cet exemple est basé sur l'exemple standard standard ( voir par exemple https://www.haskell.org/happy/doc/html/sec-using.html) Cet exemple n'utilise pas de monades et pas d'attributs.

L'analyseur d'expression a besoin d'une fonction pour «standardiser» les noms de variables. Par exemple, les rendre insensibles à la casse ou, comme dans les anciens langages de programmation, ne considèrent que les 8 premiers caractères.

L'analyseur est:

{ 
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 

} 

et le principal en utilisant l'analyseur syntaxique est

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

Utilisation de l'analyseur d'expression avec la fonction caseInsensitive et l'entrée

let aap = 7 in Aap + AAP 

résultat dans la sortie

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

Cela répond à ma question en partie: Je voudrais passer la fonction autour de l'utilisation des attributs et non explicitement comme dans cet exemple ...

+0

Bon, maintenant je comprends mieux votre problème. Oui, AST pour 'happy' a besoin de l'instance' Show'. Vous pouvez simplement ajouter de fausses instances 'Show' pour tous les types de fonctions et en être satisfait. Mais il sera probablement beaucoup plus simple d'utiliser _parser combinators_ au lieu de _parser generators_. – Shersh