我想解析典型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是特殊的,它们只是原子。
但现在,我完全迷失了。如何将原子流转换为树?有人可以指点我一个算法或帮我找一个。
答案 0 :(得分:5)
简而言之,即使你有一个令牌列表,你仍然需要一个解析器。
Parsec可以处理替代令牌流,但您可能需要参考手册 - 在Daan Leijen的“遗留”主页 - http://legacy.cs.uu.nl/daan/download/parsec/parsec.pdf中提供的PDF。您可以在不使用组合器库的情况下滚动自己的解析器,但是您将重新实现Parsec的某些部分。据我所知,UU_parsing希望使用单独的扫描仪,这是另一种选择。
虽然它没有处理解析,但你可能会发现Lennart Augustsson的“Lambda calculus以四种方式烹饪”对其他事情有帮助 - http://www.augustsson.net/Darcs/Lambda/top.pdf
编辑 - 这是一个部分制定的计划,说明如何使用Parsec,有关详细信息,请参阅手册第2.11节。
假设您拥有具体“内部”令牌的数据类型:
data InternalTok = Ident String
| BinOpPlus
| BinOpMinus
| UnaryOpNegate
| IntLiteral Int
deriving (Show)
然后你得到Parsec令牌的这些类型并解析:
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
对于每个优先级,你需要一个大致相同的解析器,你必须自己解决非关联。你也可能需要在chainl / chainr上做一些工作 - 我只用UU_Parsing编写了这种风格的解析器,Parsec可能略有不同。注意Apply通常位于优先级最高级别。
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
...
您必须扩展语法以处理优先级为0的IntLiterals和Identifiers:
expression0 :: Parser Expression
expression0 =
IntLit <$> intLiteral
<|> Ident <$> identifier
<|> ...
编辑 - 无限优先 - 也许如果您只有应用程序和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 = ??
答案 1 :(得分:0)
在网上搜索另一个主题后,我发现这段很好的代码可以完全按照我的意愿行事。看看:
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"
(我从这个页面上看到了它:http://hackage.haskell.org/trac/haskell-prime/attachment/wiki/FixityResolution/resolve.hs)