7

Étant donné un LL (1) grammaire qu'est-ce qu'une structure de données ou un algorithme approprié pour produire un arbre de syntaxe concret immuable d'une manière fonctionnellement pure? N'hésitez pas à écrire un exemple de code dans la langue que vous préférez.Qu'est-ce qu'une structure de données ou un algorithme approprié pour produire un arbre de syntaxe concret immuable d'une manière fonctionnellement pure?

Mon idée

 
symbol : either a token or a node 

result : success or failure 

token : a lexical token from source text 
    value -> string : the value of the token 
    type -> integer : the named type code of the token 
    next -> token : reads the next token and keeps position of the previous token 
    back -> token : moves back to the previous position and re-reads the token 

node : a node in the syntax tree 
    type -> integer : the named type code of the node 
    symbols -> linkedList : the symbols found at this node 
    append -> symbol -> node : appends the new symbol to a new copy of the node 

Voici une idée que j'ai pensé à. Le problème principal ici est de gérer les erreurs de syntaxe. Je veux dire que je pourrais arrêter à la première erreur mais cela ne semble pas correct.

let program token = 
    sourceElements (node nodeType.program) token   

let sourceElements node token = 
    let (n, r) = sourceElement (node.append (node nodeType.sourceElements)) token 
    match r with 
    | success -> (n, r) 
    | failure -> // ???  

let sourceElement node token = 
    match token.value with 
    | "function" -> 
     functionDeclaration (node.append (node nodeType.sourceElement)) token 
    | _ -> 
     statement (node.append (node nodeType.sourceElement)) token 

S'il vous plaît noter

Je va offrir une belle prime à la meilleure réponse, alors ne se sentent pas bousculés. Les réponses qui affichent simplement un lien auront moins de poids sur les réponses qui affichent du code ou contiennent des explications détaillées.

Note finale

Je suis vraiment nouveau à ce genre de choses alors ne pas avoir peur de me appeler un crétin.

+1

Qu'est-ce qui ne va pas avec les arbres composés de bas en haut? –

+0

@Ira - Vous me dites, je suis nouveau à tout cela. – ChaosPandion

+0

Puisque deviner les connaissances antérieures est difficile, vous pouvez commenter les réponses et demander des clarifications. –

Répondre

10

Vous souhaitez analyser quelque chose dans un arbre de syntaxe abstraite.

Dans le langage de programmation purement fonctionnel Haskell, vous pouvez utiliser combinateurs d'analyseurs pour exprimer votre grammaire. Voici un exemple qui parse une langue minuscule d'expression:

EDIT Utilisez le style monadique pour correspondre le livre de Graham Hutton

-- import a library of *parser combinators* 
import Parsimony 
import Parsimony.Char 
import Parsimony.Error 
(+++) = (<|>) 

    -- abstract syntax tree 
data Expr = I Int 
      | Add Expr Expr 
      | Mul Expr Expr 
      deriving (Eq,Show) 

    -- parse an expression 
parseExpr :: String -> Either ParseError Expr 
parseExpr = Parsimony.parse pExpr 
    where 

     -- grammar 
    pExpr :: Parser String Expr 
    pExpr = do 
     a <- pNumber +++ parentheses pExpr -- first argument 
     do 
      f <- pOp      -- operation symbol 
      b <- pExpr      -- second argument 
      return (f a b) 
     +++ return a      -- or just the first argument 

    parentheses parser = do     -- parse inside parentheses 
     string "(" 
     x <- parser 
     string ")" 
     return x 

    pNumber = do       -- parse an integer 
     digits <- many1 digit 
     return . I . read $ digits 

    pOp =         -- parse an operation symbol 
     do string "+" 
      return Add 
     +++ 
     do string "*" 
      return Mul 

Voici un exemple exécuté:

*Main> parseExpr "(5*3)+1" 
Right (Add (Mul (I 5) (I 3)) (I 1)) 

Pour en savoir plus sur combinateurs analyseur , voir par exemple le chapitre 8 de Graham Hutton's book "Programming in Haskell" ou chapter 16 de "Real World Haskell".

De nombreuses bibliothèques de combinateurs d'analyseurs peuvent être utilisées avec différents types de jetons, comme vous avez l'intention de le faire. Les flux de jetons sont généralement représentés sous la forme de listes de jetons [Token].

+0

Je dois admettre que c'est difficile à comprendre pour moi.Dans mon esprit, j'imagine une machine d'état qui transmet des suites, mais il me faudra certainement du temps pour comprendre cela. – ChaosPandion

+1

La clé ici est que vous n'avez plus besoin d'implémenter une machine d'état ou quelque chose, c'est déjà fait pour vous dans la bibliothèque des combinateurs d'analyseurs et son type 'Parser tokens a'. Vous pouvez simplement spécifier la grammaire de manière abstraite et laisser la bibliothèque déterminer ce qu'il faut faire avec. –

+0

@Heinrich - Il y a deux façons de comprendre un sujet (grammaires/analyseur/compilateur dans ce cas). La première est de simplement être assez intelligent pour le comprendre avec peu d'effort (c'est ainsi que j'ai travaillé avant de décider d'apprendre l'informatique avancée). Une fois que j'ai découvert que je ne suis pas si intelligent, j'ai décidé que la seule façon dont je serai capable d'apprendre ceci est à travers la deuxième méthode. La deuxième méthode est la dédicace simple. Depuis, j'ai écrit 4 parseurs ECMAScript, chacun progressant progressivement au fur et à mesure que j'appris de nouvelles techniques. Cependant, ils ont tous été impératifs. – ChaosPandion

2

Vérifiez définitivement l'approche du combinateur d'analyseur monadique; J'ai blogué à ce sujet in C# et in F#.