Algorithm 将标记列表解析为表达式树

Algorithm 将标记列表解析为表达式树,algorithm,haskell,expression-trees,Algorithm,Haskell,Expression Trees,我想解析典型Haskell源代码中的表达式。我得到一个输入流,它已经用固定性和优先级标记和注释了。运算符集在编译时未知,可能是任意的。输出应该是表示表达式的树。以下是我尝试过的一点: -- A single token of the input stream data Token a = Prefix a | Infix a Int Fixity -- The Int parameter represents the precedence | LBrace | RBrace

我想解析典型Haskell源代码中的表达式。我得到一个输入流,它已经用固定性和优先级标记和注释了。运算符集在编译时未知,可能是任意的。输出应该是表示表达式的树。以下是我尝试过的一点:

-- A single token of the input stream
data Token a
  = Prefix a
  | Infix a Int Fixity -- The Int parameter represents the precedence
  | LBrace
  | RBrace
  deriving (Show,Eq)

data Fixity
  = InfixL
  | InfixR
  | InfixC
  deriving (Show,Eq)

data Expression a
  = Atom a
  | Apply a a
  deriving Show

-- Wrapped into either, if expression is malformed.
exprToTree :: [Token a] -> Either String (Expression a)
exprToTree = undefined
为了简单起见,我不把lambda视为特殊的,它们只是原子


但是现在,我完全迷路了。我怎样才能把原子流转换成一棵树呢?谁能给我指一个算法或者帮我找到一个

简而言之,即使您有一个令牌列表,您仍然需要一个解析器

Parsec可以处理其他令牌流,但您可能需要参考手册-一份PDF文件,可在Daan Leijen的“遗留”主页上找到。您可以在不使用combinator库的情况下运行自己的解析器,但您将重新实现部分Parsec。据我所知,UU_解析希望使用单独的扫描仪,所以这是另一种选择

虽然它不能处理解析,但你可能会发现Lennart Augustsson的“Lambda演算四种方法”对其他事情很有帮助-

编辑-这是一个关于如何使用Parsec的部分计划,有关详细信息,请参阅手册第2.11节

假设您有具体的“内部”标记的数据类型:

然后,您将为Parsec令牌和parse获取以下类型:

type MyToken = Token InternalTok

type MyParser a = GenParser MyToken () a
按照Parsec手册定义一个助手函数-该函数处理show和pos,因此单个定义较短,参见第19页的
mytoken
函数

mytoken :: (MyToken -> Maybe a) -> MyParser a
mytoken test = token showToken posToken testToken
  where
    showToken tok = show tok
    posToken tok = no_pos
    testToken tok = test tok
目前,您的令牌类型不跟踪源位置,因此:

no_pos :: SourcePos
no_pos = newPos "" 0 0 0 
对于每个终端,您必须定义一个令牌函数:

identifier :: MyParser MyToken
identifier =  mytoken (\tok -> case tok of
                         a@(Prefix (Ident _)) -> Just a
                         _                    -> Nothing)

intLiteral :: MyParser MyToken
intLiteral =  mytoken (\tok -> case tok of
                         a@(Prefix (IntLiteral _)) -> Just a
                         _                         -> Nothing)

binPlus :: MyParser MyToken
binPlus =  mytoken (\tok -> case tok of
                      a@(Infix BinOpPlus _ _) -> Just a
                      _                       -> Nothing)


binMinus :: MyParser MyToken
binMinus =  mytoken (\tok -> case tok of
                      a@(Infix BinOpMinus _ _) -> Just a
                      _                        -> Nothing)

unaryNegate :: MyParser MyToken
unaryNegate =  mytoken (\tok -> case tok of
                        a@(Prefix UnaryNegate _ _) -> Just a
                        _                          -> Nothing)
编辑-要处理自定义中缀运算符,您需要以下令牌解析器:

tokInfixL :: Int -> MyParser MyToken
tokInfixL n = mytoken $ \tok -> case tok of
    a@(Infix _ i InfixL) | i == n -> Just a
    _                             -> Nothing)


tokInfixR :: Int -> MyParser MyToken
tokInfixR n = mytoken $ \tok -> case tok of
    a@(Infix _ i InfixR) | i == n -> Just a
    _                             -> Nothing)

tokInfixC :: Int -> MyParser MyToken
tokInfixC n = mytoken $ \tok -> case tok of
    a@(Infix _ i InfixC) | i == n -> Just a
    _                             -> Nothing)


tokPrefix :: MyParser MyToken
tokPrefix = mytoken (\tok -> case tok of
                       a@(Prefix _) -> Just a
                       _            -> Nothing)
现在您可以定义解析器了——您需要事先确定优先级级别的数量,因为您需要为每个级别编写一个解析器,所以无法绕过这一事实

顶级表达式解析只是调用最高优先级的解析器

pExpression :: Parser Expersion
pExpression = expression10
对于每个前置级别,您需要一个大致如下的解析器,您必须自己计算出非assoc。另外,您可能需要在chainl/chainr上做一些工作-我只编写了一个具有UU_解析的这种风格的解析器,对于Parsec来说可能略有不同。注:应用通常处于优先级最高的级别

expression10 :: Parser Expression
expression10 = 
        Apply   <$> identifier <*> pExpression
    <|> Prefix  <$> tokPrefix  <*> pExpression
    <|> chainl (Infix <$> tokInfixL 10) expression9
    <|> chainr (Infix <$> tokInfixR 10) expression9

expression9 :: Parser Expression
expression9 = 
        Prefix  <$> tokPrefix  <*> pExpression
    <|> chainl (Infix <$> tokInfixL 9) expression8
    <|> chainr (Infix <$> tokInfixR 9) expression8

...
expression10::解析器表达式
表达式10=
应用标识符pExpression
前缀前缀表达式
chainl(中缀10)表达式9
chainr(中缀tokInfixR 10)表达式9
表达式9::解析器表达式
表达式9=
前缀前缀表达式
chainl(中缀tokInfixL 9)表达式8
chainr(中缀tokInfixR 9)表达式8
...
您必须扩展语法以处理优先级别为0的内部文本和标识符:

expression0 :: Parser Expression
expression0 = 
        IntLit  <$> intLiteral 
    <|> Ident   <$> identifier
    <|> ...
expression0::解析器表达式
表达式0=
IntLit intLiteral
识别标识符
...
编辑-无限制的优先级-如果您只有应用程序和Atom,可能类似的东西可以工作。注意,您必须更改tokInfixL和tokInfixR解析器,使其不再匹配assoc级别,并且您可能必须尝试替代顺序

expression :: Parser Expression
expression = 
        Apply   <$> identifier <*> expression
    <|> Prefix  <$> tokPrefix  <*> expression
    <|> chainl (Infix <$> tokInfixL) expression
    <|> chainr (Infix <$> tokInfixR) expression
    <|> intLiteral
    <|> identifier

intLiteral :: Parser Expression
intLiteral = Atom . convert <$> intLiteral
  where
    convert = ??

identifier :: Parser Expression
identifier = Atom . convert <$> intLiteral
  where
    convert = ??
expression::解析器表达式
表达式=
应用标识符表达式
前缀表达式
chainl(中缀)表达式
chainr(中缀tokInfixR)表达式
内部文字
标识符
intLiteral::解析器表达式
intLiteral=Atom。转换整型文字
哪里
转换=??
标识符::解析器表达式
标识符=原子。转换整型文字
哪里
转换=??

在网上搜索另一个主题后,我发现这段代码很好,完全符合我的要求。看看:

data Op     = Op String Prec Fixity          deriving Eq
data Fixity = Leftfix | Rightfix | Nonfix    deriving Eq
data Exp    = Var Var | OpApp Exp Op Exp     deriving Eq
type Prec   = Int
type Var    = String

data Tok = TVar Var | TOp Op

parse :: [Tok] -> Exp
parse (TVar x : rest) = fst (parse1 (Var x) (-1) Nonfix rest)

parse1 :: Exp -> Int -> Fixity -> [Tok] -> (Exp, [Tok])
parse1 e p f [] = (e, [])
parse1 e p f inp@(TOp op@(Op _ p' f') : TVar x : rest) 
  | p' == p && (f /= f' || f == Nonfix)
  = error "ambiguous infix expression"
  | p' < p  ||  p' == p && (f == Leftfix || f' == Nonfix)
  = (e, inp)
  | otherwise
  = let (r,rest') = parse1 (Var x) p' f' rest in
    parse1 (OpApp e op r) p f rest'

-- Printing

instance Show Exp where
  showsPrec _ (Var x) = showString x
  showsPrec p e@(OpApp l (Op op _ _) r) = 
        showParen (p > 0) $ showsPrec 9 l . showString op . showsPrec 9 r

-- Testing

plus   = TOp (Op "+" 6 Leftfix)
times  = TOp (Op "*" 7 Leftfix)
divide = TOp (Op "/" 7 Leftfix)
gt     = TOp (Op ">" 4 Nonfix)
ex     = TOp (Op "^" 8 Rightfix)

lookupop '+' = plus
lookupop '*' = times
lookupop '/' = divide
lookupop '>' = gt
lookupop '^' = ex

fromstr [x]     = [TVar [x]]
fromstr (x:y:z) = TVar [x] : lookupop y : fromstr z

test1 = fromstr "a+b+c"
test2 = fromstr "a+b+c*d"
test3 = fromstr "a/b/c"
test4 = fromstr "a/b+c"
test5 = fromstr "a/b*c"
test6 = fromstr "1^2^3+4"
test7 = fromstr "a/1^2^3"
test8 = fromstr "a*b/c"
data Op=Op字符串预固定性推导公式
数据固定性=左固定|右固定|非固定派生等式
数据Exp=Var Var | OpApp Exp Op Exp导出公式
类型Prec=Int
类型Var=String
数据Tok=TVar |顶部Op
解析::[Tok]->Exp
parse(TVar x:rest)=fst(parse1(Var x)(-1)非fix rest)
parse1::Exp->Int->Fixity->[Tok]->(Exp[Tok])
parse1 e p f[]=(e,[])
parse1 e p f inp@(TOp op@(op'p'f'):TVar x:rest)
|p'==p&&(f/=f'| | f==Nonfix)
=错误“不明确的中缀表达式”
|p'

0)$showsPrec 9升。显示字符串操作。showsPrec 9 r --测试 加号=顶部(Op“+”6左固定) 时间=顶部(Op“*”7 Leftfix) 除法=顶部(Op“/”7左固定) gt=顶部(Op“>”4非FIX) ex=顶部(Op“^”8右键固定) lookupop'+'=plus lookupop'*'=次 lookupop'/'=除法 lookupop'>'=gt lookupop'^'=ex fromstr[x]=[TVar[x]] fromstr(x:y:z)=TVar[x]:lookupop y:fromstr z test1=fromstr“a+b+c” test2=fromstr“a+b+c*d” test3=fromstr“a/b/c” test4=fromstr“a/b+c” test5=fromstr“a/b*c” test6=fromstr“1^2^3+4” test7=fromstr“a/1^2^3” test8=fromstr“a*b/c”


(我从这页上取的:)

Hi FuzzXL-您知道目前没有数字文字和运算符吗?也许这就是类型参数
a
所抽象的,但我想自己将它们建模为具体的数据类型。@Stephen Tetley:我选择
a
,因为我想能够在以后对树应用一些操作(比如键入它)。开始时,
a
是一种代数数据类型,可以是名称,也可以是
data Op     = Op String Prec Fixity          deriving Eq
data Fixity = Leftfix | Rightfix | Nonfix    deriving Eq
data Exp    = Var Var | OpApp Exp Op Exp     deriving Eq
type Prec   = Int
type Var    = String

data Tok = TVar Var | TOp Op

parse :: [Tok] -> Exp
parse (TVar x : rest) = fst (parse1 (Var x) (-1) Nonfix rest)

parse1 :: Exp -> Int -> Fixity -> [Tok] -> (Exp, [Tok])
parse1 e p f [] = (e, [])
parse1 e p f inp@(TOp op@(Op _ p' f') : TVar x : rest) 
  | p' == p && (f /= f' || f == Nonfix)
  = error "ambiguous infix expression"
  | p' < p  ||  p' == p && (f == Leftfix || f' == Nonfix)
  = (e, inp)
  | otherwise
  = let (r,rest') = parse1 (Var x) p' f' rest in
    parse1 (OpApp e op r) p f rest'

-- Printing

instance Show Exp where
  showsPrec _ (Var x) = showString x
  showsPrec p e@(OpApp l (Op op _ _) r) = 
        showParen (p > 0) $ showsPrec 9 l . showString op . showsPrec 9 r

-- Testing

plus   = TOp (Op "+" 6 Leftfix)
times  = TOp (Op "*" 7 Leftfix)
divide = TOp (Op "/" 7 Leftfix)
gt     = TOp (Op ">" 4 Nonfix)
ex     = TOp (Op "^" 8 Rightfix)

lookupop '+' = plus
lookupop '*' = times
lookupop '/' = divide
lookupop '>' = gt
lookupop '^' = ex

fromstr [x]     = [TVar [x]]
fromstr (x:y:z) = TVar [x] : lookupop y : fromstr z

test1 = fromstr "a+b+c"
test2 = fromstr "a+b+c*d"
test3 = fromstr "a/b/c"
test4 = fromstr "a/b+c"
test5 = fromstr "a/b*c"
test6 = fromstr "1^2^3+4"
test7 = fromstr "a/1^2^3"
test8 = fromstr "a*b/c"