Подтвердить что ты не робот

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

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

Lexer.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)
}

Parser.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
4b9b3361

Ответ 1

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

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

Сделав это, вы также можете нести вокруг AlexState свой токен, но это может быть ненужным.

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