Skip to content
This repository was archived by the owner on Jun 15, 2023. It is now read-only.

Commit edb6a20

Browse files
committed
Add more tests. Fix bugs. Add error printer
1 parent c21bfa0 commit edb6a20

File tree

5 files changed

+883
-62
lines changed

5 files changed

+883
-62
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
/output/
55
/.psc*
66
/.psa*
7+
/.purs-repl

src/SqlSquared/Parser.purs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module SqlSquared.Parser
22
( parse
33
, parseQuery
44
, parseModule
5+
, prettyParse
56
, module SqlSquared.Parser.Tokenizer
67
) where
78

@@ -14,12 +15,13 @@ import Control.Monad.State (get, put)
1415
import Control.MonadZero (guard)
1516

1617
import Data.Array as A
17-
import Data.NonEmpty ((:|))
18+
import Data.Bifunctor (lmap)
1819
import Data.Either as E
20+
import Data.Foldable as F
1921
import Data.List ((:))
2022
import Data.List as L
21-
import Data.Foldable as F
22-
import Data.Maybe (Maybe(..), isNothing, fromMaybe, isJust, maybe)
23+
import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe)
24+
import Data.NonEmpty ((:|))
2325
import Data.Json.Extended as EJ
2426
import Data.Tuple (Tuple(..), uncurry)
2527
import Data.Path.Pathy as Pt
@@ -55,15 +57,39 @@ asErrorMessage ∷ ∀ m a. Monad m ⇒ String → P.ParserT TokenStream m a →
5557
asErrorMessage err = flip (<|>) do
5658
P.ParseState input _ _ ← get
5759
case A.head input of
58-
NothingP.fail $ "Expected " <> err <> ", got end of input"
59-
Just tk → P.failWithPosition ("Expected " <> err <> ", got " <> printToken tk.token) tk.position
60+
NothingP.fail $ "Expected " <> err <> ", but got end of input"
61+
Just tk → P.failWithPosition ("Expected " <> err <> ", but got " <> printToken tk.token) tk.position
6062

6163
withToken m a. Monad m String (Token P.ParserT TokenStream m a) P.ParserT TokenStream m a
6264
withToken err k =
6365
PC.try
64-
$ withErrorMessage (append $ err <> ", got ")
66+
$ withErrorMessage (append $ err <> ", but got ")
6567
((withErrorMessage' (const "end of input") token) >>= k)
6668

69+
prettyParse
70+
a
71+
. (String E.Either P.ParseError a)
72+
String
73+
E.Either String a
74+
prettyParse parser input =
75+
lmap printError (parser input)
76+
where
77+
padLeft n s =
78+
S.fromCharArray (A.replicate (n - S.length s) ' ') <> s
79+
80+
printError parseError =
81+
let
82+
message = P.parseErrorMessage parseError
83+
PP.Position pos = P.parseErrorPosition parseError
84+
lines = S.split (S.Pattern "\n") input
85+
pre = A.drop (pos.line - 3) $ A.take (pos.line - 1) lines
86+
line = A.take 1 $ A.drop (pos.line - 1) lines
87+
post = A.take 3 $ A.drop pos.line lines
88+
nums = A.mapWithIndex (\n l → padLeft 4 (show (n + pos.line - (A.length pre))) <> " | " <> l) (pre <> line <> post)
89+
pointer = pure $ S.fromCharArray (A.replicate (pos.column - 1 + 7) '-') <> "^ " <> message
90+
in
91+
S.joinWith "\n" $ A.take 3 nums <> pointer <> A.drop 3 nums
92+
6793
parse
6894
t
6995
. Corecursive t (Sig.SqlF EJ.EJsonF)
@@ -434,12 +460,12 @@ keyValuePair = do
434460

435461
negatableSuffix m t. SqlParser m t (t t)
436462
negatableSuffix = do
437-
mbInv ← PC.optionMaybe do
438-
_ ← keyword "is"
463+
inv ← do
464+
_ ← PC.optionMaybe $ keyword "is"
439465
n ← PC.optionMaybe $ keyword "not"
440-
pure $ isNothing n
441-
let inv = fromMaybe false mbInv
442-
suffix ← betweenSuffix <|> inSuffix <|> likeSuffix
466+
pure $ isJust n
467+
suffix ← asErrorMessage "`LIKE`, `IN`, or `BETWEEN`" do
468+
betweenSuffix <|> inSuffix <|> likeSuffix
443469
pure \e → (if inv then _NOT else id) $ suffix e
444470

445471
betweenSuffix m t. SqlParser m t (t t)

src/SqlSquared/Parser/Tokenizer.purs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ operators =
9797
, "~"
9898
, "??"
9999
, "="
100+
, ">="
100101
, ">"
102+
, "<="
101103
, "<"
102104
, "["
103105
, "]"

test/src/Parse.purs

Lines changed: 160 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,62 +3,171 @@ module Test.Parse where
33
import Prelude
44

55
import Data.Either as E
6-
import Data.Foldable as F
76
import SqlSquared (parseQuery, SqlQuery)
7+
import SqlSquared.Parser (prettyParse)
88

9+
import Test.Queries as Q
910
import Test.Unit (suite, test, TestSuite)
1011
import Test.Unit.Assert as Assert
1112
import Test.Unit.Console as Console
1213

13-
testPrintParse e. E.Either String String TestSuite (testOutput Console.TESTOUTPUT | e)
14-
testPrintParse = case _ of
15-
E.Left s → do
16-
test s case parseQuery s of
17-
E.Left err → pure unit
18-
E.Right (sql SqlQuery) → Assert.assert "Parse succeeded" false
19-
E.Right s → do
20-
test s case parseQuery s of
21-
E.Left err → Assert.assert (show err) false
22-
E.Right (sql SqlQuery) → pure unit
23-
24-
inputs Array (E.Either String String)
25-
inputs =
26-
[ E.Right """select a from `/f` where b is not between 1 and (2..3)"""
27-
, E.Right """select foo, bar from (select foo, bar from (select foo, bar from `/baz`)as t) as d"""
28-
, E.Right """select (1 + 2) * 3 + 2"""
29-
, E.Right """select 1 + 2 * 3 ^ 1.2 / 14 + Minimum(12, 23)"""
30-
, E.Right """select ("12" || "12" || "12")"""
31-
, E.Right """select date("12-12-12") from `/fo` cross join `/bar`"""
32-
, E.Right """Select foo as bar from `/test/db/collection`"""
33-
, E.Right """Select `foo`, `bar`[*] from `/test` join `/test2` on baz where doo = (12 + 23)"""
34-
, E.Right """foo := 12; select * from `/test` group by baz"""
35-
, E.Right """select 1"""
36-
, E.Right """select (1, 2)"""
37-
, E.Right """foo := [1, 2]; select 1"""
38-
, E.Right """foo := 1; bar := 2; select [] """
39-
, E.Right """select foo from `/bar` order by zoo desc"""
40-
, E.Right """select distinct a from `/f`"""
41-
, E.Right """select a from /* trololo */ `/db`"""
42-
, E.Right """-- comment
43-
select 12
44-
"""
45-
, E.Right """import foo; select * from `/test`"""
46-
, E.Right """create function foo(:bar) begin :bar + 2 end; select * from `/test` where foo = foo(42)"""
47-
, E.Right """select :where"""
48-
, E.Right """foo.`_id`"""
49-
, E.Left """foo._id"""
50-
, E.Right """select * from foo JOIN bar on baz"""
51-
, E.Right """select * from foo FULL JOIN bar on baz"""
52-
, E.Right """select * from foo FULL OUTER JOIN bar on baz"""
53-
, E.Right """select * from foo INNER JOIN bar on baz"""
54-
, E.Right """select * from foo LEFT OUTER JOIN bar on baz"""
55-
, E.Right """select * from foo LEFT JOIN bar on baz"""
56-
, E.Right """select * from foo RIGHT OUTER JOIN bar on baz"""
57-
, E.Right """select * from foo RIGHT JOIN bar on baz"""
58-
, E.Right """industry"""
59-
]
14+
parseSucc e. String String TestSuite (testOutput Console.TESTOUTPUT | e)
15+
parseSucc n s =
16+
test n case prettyParse parseQuery s of
17+
E.Left err → Assert.assert ("\n" <> err) false
18+
E.Right (sql SqlQuery) → pure unit
19+
20+
parseFail e. String String TestSuite (testOutput Console.TESTOUTPUT | e)
21+
parseFail n s =
22+
test n case parseQuery s of
23+
E.Left err → pure unit
24+
E.Right (sql SqlQuery) → Assert.assert s false
6025

6126
testSuite e. TestSuite (testOutput Console.TESTOUTPUT | e)
62-
testSuite = do
63-
suite "parsers" do
64-
F.traverse_ testPrintParse inputs
27+
testSuite = suite "parsers" do
28+
parseSucc "t1" """
29+
select a from `/f` where b is not between 1 and (2..3)
30+
"""
31+
32+
parseSucc "t2" """
33+
select foo, bar from (select foo, bar from (select foo, bar from `/baz`)as t) as d
34+
"""
35+
36+
parseSucc "t3" """
37+
select (1 + 2) * 3 + 2
38+
"""
39+
40+
parseSucc "t4" """
41+
select 1 + 2 * 3 ^ 1.2 / 14 + Minimum(12, 23)
42+
"""
43+
44+
parseSucc "t5" """
45+
select ("12" || "12" || "12")
46+
"""
47+
48+
parseSucc "t6" """
49+
select date("12-12-12") from `/fo` cross join `/bar`
50+
"""
51+
52+
parseSucc "t7" """
53+
Select foo as bar from `/test/db/collection`
54+
"""
55+
56+
parseSucc "t8" """
57+
Select `foo`, `bar`[*] from `/test` join `/test2` on baz where doo = (12 + 23)
58+
"""
59+
60+
parseSucc "t9" """
61+
foo := 12; select * from `/test` group by baz
62+
"""
63+
64+
parseSucc "t10" """
65+
select 1
66+
"""
67+
68+
parseSucc "t11" """
69+
select (1, 2)
70+
"""
71+
72+
parseSucc "t12" """
73+
foo := [1, 2]; select 1
74+
"""
75+
76+
parseSucc "t13" """
77+
foo := 1; bar := 2; select []
78+
"""
79+
80+
parseSucc "t14" """
81+
select foo from `/bar` order by zoo desc
82+
"""
83+
84+
parseSucc "t15" """
85+
select distinct a from `/f`
86+
"""
87+
88+
parseSucc "t16" """
89+
select a from /* trololo */ `/db`
90+
"""
91+
92+
parseSucc "t17" """
93+
-- comment
94+
select 12
95+
"""
96+
97+
parseSucc "t18" """
98+
import foo; select * from `/test`
99+
"""
100+
101+
parseSucc "t19" """
102+
create function foo(:bar) begin :bar + 2 end; select * from `/test` where foo = foo(42)
103+
"""
104+
105+
parseSucc "t20" """
106+
select :where
107+
"""
108+
109+
parseSucc "t21" """
110+
foo.`_id`
111+
"""
112+
113+
parseFail "t22" """
114+
foo._id
115+
"""
116+
117+
parseSucc "t23" """
118+
select * from foo JOIN bar on baz
119+
"""
120+
121+
parseSucc "t24" """
122+
select * from foo FULL JOIN bar on baz
123+
"""
124+
125+
parseSucc "t25" """
126+
select * from foo FULL OUTER JOIN bar on baz
127+
"""
128+
129+
parseSucc "t26" """
130+
select * from foo INNER JOIN bar on baz
131+
"""
132+
133+
parseSucc "t27" """
134+
select * from foo LEFT OUTER JOIN bar on baz
135+
"""
136+
137+
parseSucc "t28" """
138+
select * from foo LEFT JOIN bar on baz
139+
"""
140+
141+
parseSucc "t29" """
142+
select * from foo RIGHT OUTER JOIN bar on baz
143+
"""
144+
145+
parseSucc "t30" """
146+
select * from foo RIGHT JOIN bar on baz
147+
"""
148+
149+
parseSucc "t31" """
150+
industry
151+
"""
152+
153+
parseSucc "q1" Q.q1
154+
parseSucc "q2" Q.q2
155+
parseSucc "q3" Q.q3
156+
parseSucc "q4" Q.q4
157+
parseSucc "q5" Q.q5
158+
parseSucc "q6" Q.q6
159+
parseSucc "q7" Q.q7
160+
parseSucc "q8" Q.q8
161+
parseSucc "q9" Q.q9
162+
parseSucc "q10" Q.q10
163+
parseSucc "q11" Q.q11
164+
parseSucc "q12" Q.q12
165+
parseSucc "q13" Q.q13
166+
parseSucc "q14" Q.q14
167+
parseSucc "q15" Q.q15
168+
parseSucc "q16" Q.q16
169+
parseSucc "q17" Q.q17
170+
parseSucc "q18" Q.q18
171+
parseSucc "q19" Q.q19
172+
parseSucc "q20" Q.q20
173+
parseSucc "q21" Q.q21

0 commit comments

Comments
 (0)