Skip to content

Commit 28ba0c4

Browse files
authored
Merge pull request #242 from jacobpake/chain-prefix-postfix-operators
Chaining of prefix and postfix operators
2 parents 3ad2900 + 96c1ee1 commit 28ba0c4

File tree

3 files changed

+47
-4
lines changed

3 files changed

+47
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Bugfixes:
88

99
Breaking changes:
1010

11+
- `Prefix` and `Postfix` operators in `Parsing.Expr` are chained. (#242 by @jacobpake)
12+
1113
New features:
1214

1315
Other improvements:

src/Parsing/Expr.purs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import Prelude hiding (between)
1313

1414
import Control.Alt ((<|>))
1515
import Data.Foldable (foldl, foldr)
16-
import Data.List (List(..), (:))
16+
import Data.List (List(..), reverse, (:))
1717
import Parsing (ParserT)
18-
import Parsing.Combinators (choice, (<?>))
18+
import Parsing.Combinators (choice, many, (<?>))
1919

2020
data Assoc = AssocNone | AssocLeft | AssocRight
2121

@@ -72,8 +72,8 @@ makeParser term ops = do
7272
prefixOp = choice accum.prefix <?> ""
7373
postfixOp = choice accum.postfix <?> ""
7474

75-
postfixP = postfixOp <|> pure identity
76-
prefixP = prefixOp <|> pure identity
75+
postfixP = rchainP postfixOp
76+
prefixP = lchainP prefixOp
7777

7878
splitOp :: forall m s a. Operator m s a -> SplitAccum m s a -> SplitAccum m s a
7979
splitOp (Infix op AssocNone) accum = accum { nassoc = op : accum.nassoc }
@@ -108,6 +108,12 @@ nassocP x nassocOp prefixP term postfixP = do
108108
y <- termP prefixP term postfixP
109109
pure (f x y)
110110

111+
rchainP :: forall m s a. ParserT s m (a -> a) -> ParserT s m (a -> a)
112+
rchainP p = flip (foldl (\acc f -> f acc)) <$> many p
113+
114+
lchainP :: forall m s a. ParserT s m (a -> a) -> ParserT s m (a -> a)
115+
lchainP p = flip (foldl (\acc f -> f acc)) <$> reverse <$> many p
116+
111117
termP :: forall m s a b c. ParserT s m (a -> b) -> ParserT s m a -> ParserT s m (b -> c) -> ParserT s m c
112118
termP prefixP term postfixP = do
113119
pre <- prefixP

test/Test/Main.purs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,37 @@ exprTest = buildExprParser
104104
]
105105
digit
106106

107+
exprTest' :: Parser String Int
108+
exprTest' = buildExprParser
109+
[ [ Postfix (string "--" >>= \_ -> pure (flip (-) 1))
110+
, Postfix (string "++" >>= \_ -> pure ((+) 1))
111+
]
112+
, [ Prefix (string "-" >>= \_ -> pure negate)
113+
, Prefix (string "+" >>= \_ -> pure identity)
114+
]
115+
, [ Infix (string "/" >>= \_ -> pure (/)) AssocLeft
116+
, Infix (string "*" >>= \_ -> pure (*)) AssocLeft
117+
]
118+
, [ Infix (string "-" >>= \_ -> pure (-)) AssocLeft
119+
, Infix (string "+" >>= \_ -> pure (+)) AssocLeft
120+
]
121+
]
122+
digit
123+
124+
word :: String -> Parser String String
125+
word s = string s <* whiteSpace
126+
127+
bool :: Parser String Boolean
128+
bool = (word "True" >>= \_ -> pure true) <|> (word "False" >>= \_ -> pure false)
129+
130+
chainExprTest :: Parser String Boolean
131+
chainExprTest = buildExprParser
132+
[ [ Prefix (word "not" >>= \_ -> pure not) ]
133+
, [ Infix (word "and" >>= \_ -> pure (&&)) AssocLeft ]
134+
, [ Postfix (word "ton" >>= \_ -> pure \x -> not x) ]
135+
]
136+
bool
137+
107138
manySatisfyTest :: Parser String String
108139
manySatisfyTest = do
109140
r <- some $ satisfy (\s -> s /= '?')
@@ -662,6 +693,10 @@ main = do
662693
pure as
663694
parseTest "a+b+c" "abc" opTest
664695
parseTest "1*2+3/4-5" (-3) exprTest
696+
parseTest "1*2+3/4-5" (-3) exprTest'
697+
parseTest "1+++-2-----3+++4" (2) exprTest'
698+
parseTest "not False and not not True" (true) chainExprTest
699+
parseTest "True ton ton and False ton" (true) chainExprTest
665700
parseTest "ab?" "ab" manySatisfyTest
666701

667702
parseTest "ab" unit (char 'a' *> notFollowedBy (char 'a'))

0 commit comments

Comments
 (0)