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