Управление информацией о местоположении с Alex and Happy

Я учусь использовать Алекса и Хэппи для написания небольшого компилятора. Я хочу сохранить информацию о строках и столбцах для своих узлов AST, чтобы я мог предоставлять пользователю содержательные сообщения об ошибках. Чтобы проиллюстрировать, как я планирую это сделать, я написал небольшой пример (см. код ниже), и я хотел бы знать, подходит ли мой подход к проблеме (присоединение AlexPosn к токенам, присоединение поля полиморфного атрибута к узлам AST). , используя tkPos и ​​astAttr) является хорошим стилем или если есть лучшие способы обработки информации о позиции.

Лексер.x:

{
module Lexer where
}

%wrapper "posn"

$white = [\ \t\n]

tokens :-

$white+ ;
[xX] { \pos s -> MkToken pos X }
"+"  { \pos s -> MkToken pos Plus }
"*"  { \pos s -> MkToken pos Times }
"("  { \pos s -> MkToken pos LParen }
")"  { \pos s -> MkToken pos RParen }

{
data Token = MkToken AlexPosn TokenClass
           deriving (Show, Eq)

data TokenClass = X
                | Plus
                | Times
                | LParen
                | RParen
                  deriving (Show, Eq)

tkPos :: Token -> (Int, Int)
tkPos (MkToken (AlexPn _ line col) _) = (line, col)
}

Парсер.y:

{
module Parser where

import Lexer
}

%name simple
%tokentype { Token }
%token
    '(' { MkToken _ LParen }
    ')' { MkToken _ RParen }
    '+' { MkToken _ Plus }
    '*' { MkToken _ Times }
    x   { MkToken _ X }

%%

Expr : Term '+' Expr     { NAdd $1 $3 (astAttr $1) }
     | Term              { $1 }

Term : Factor '*' Term   { NMul $1 $3 (astAttr $1) }
     | Factor            { $1 }

Factor : x               { NX (tkPos $1) }
       | '(' Expr ')'    { $2 }


{
data AST a = NX a
           | NMul (AST a) (AST a) a
           | NAdd (AST a) (AST a) a
             deriving (Show, Eq)

astAttr :: AST a -> a
astAttr (NX a)       = a
astAttr (NMul _ _ a) = a
astAttr (NAdd _ _ a) = a

happyError :: [Token] -> a
happyError _ = error "parse error"
}

Main.hs:

module Main where

import Lexer
import Parser

main :: IO ()
main = do
  s <- getContents
  let toks = alexScanTokens s
  print $ simple toks

person gnuvince    schedule 15.12.2013    source источник
comment
Нашли решение, которым хотите поделиться? Интересно точно так же   -  person mfaerevaag    schedule 09.03.2014


Ответы (1)


Меня лично вполне устроил бы стиль, который вы описали. Тем не менее, он очень ручной, и я надеялся предоставить хотя бы одну альтернативу, которой было бы легче управлять.

Если вы посмотрите немного дальше документацию по оберткам alex , вы заметите, что обе оболочки монады и состояния монады содержат информацию о позиции. Недостатком является то, что теперь у вас все это завернуто в монаду, и это немного усложняет синтаксический анализатор. Однако, обернув его в монаду, результатом синтаксического анализа будет Alex a, что означает, что у вас есть полный доступ к информации о строке и столбце при создании узлов ast. Теперь это просто удаляет часть шаблона из лексера и не делает ничего большего.

Делая это, вы также можете носить AlexState со своим токеном, но это может быть ненужным.

Если вам нужна помощь в том, чтобы на самом деле исправить синтаксический анализатор для обработки оболочки монады/монадсостояния, я написал ответ о том, как мне удалось заставить его работать здесь: Как использовать монадический лексер Alex с Happy?

person Mezuzza    schedule 25.07.2014