1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
|
-- Copyright 2022 Google LLC
--
-- Use of this source code is governed by a BSD-style
-- license that can be found in the LICENSE file or at
-- https://developers.google.com/open-source/licenses/bsd
module ConcreteSyntax (
mustParseit, sourceBlocks, sourceBlock,
keyWordStrs, showPrimName,
parseUModule, parseUModuleDeps,
finishUModuleParse, preludeImportBlock, mustParseSourceBlock,
pattern Identifier) where
import Control.Monad.Combinators.Expr qualified as Expr
import Control.Monad.Reader
import Data.Char
import Data.Either
import Data.Functor
import Data.List.NonEmpty (NonEmpty (..))
import Data.String (fromString)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Text.Encoding qualified as T
import Data.Void
import Text.Megaparsec hiding (Label, State)
import Text.Megaparsec.Char hiding (space, eol)
import Err
import Lexing
import Types.Core
import Types.Source
import Types.Primitives
import Util
-- TODO: implement this more efficiently rather than just parsing the whole
-- thing and then extracting the deps.
parseUModuleDeps :: ModuleSourceName -> File -> [ModuleSourceName]
parseUModuleDeps name file = deps
where UModule _ deps _ = parseUModule name $ T.decodeUtf8 $ fContents file
{-# SCC parseUModuleDeps #-}
finishUModuleParse :: UModulePartialParse -> UModule
finishUModuleParse (UModulePartialParse name _ file) =
parseUModule name (T.decodeUtf8 $ fContents file)
parseUModule :: ModuleSourceName -> Text -> UModule
parseUModule name s = do
let blocks = mustParseit s sourceBlocks
let preamble = case name of
Prelude -> []
_ -> [preludeImportBlock]
let blocks' = preamble ++ blocks
let imports = flip foldMap blocks' \b -> case sbContents b of
Misc (ImportModule moduleName) -> [moduleName]
_ -> []
UModule name imports blocks'
{-# SCC parseUModule #-}
preludeImportBlock :: SourceBlock
preludeImportBlock = SourceBlock 0 0 "" mempty (Misc $ ImportModule Prelude)
sourceBlocks :: Parser [SourceBlock]
sourceBlocks = manyTill (sourceBlock <* outputLines) eof
{-# SCC sourceBlocks #-}
mustParseSourceBlock :: Text -> SourceBlock
mustParseSourceBlock s = mustParseit s sourceBlock
-- === helpers for target ADT ===
interpOperator :: SrcId -> String -> ([SrcId], Bin)
interpOperator sid = \case
"&>" -> atomic DepAmpersand
"." -> atomic Dot
",>" -> atomic DepComma
":" -> atomic Colon
"|" -> atomic Pipe
"::" -> atomic DoubleColon
"$" -> atomic Dollar
"->>" -> atomic ImplicitArrow
"=>" -> atomic FatArrow
"=" -> atomic CSEqual
name -> ([], EvalBinOp $ WithSrc sid $ fromString $ "(" <> name <> ")")
where
atomic :: Bin -> ([SrcId], Bin)
atomic b = ([sid], b)
pattern Identifier :: SourceName -> GroupW
pattern Identifier name <- (WithSrcs _ _ (CLeaf (CIdentifier name)))
-- === Parser (top-level structure) ===
sourceBlock :: Parser SourceBlock
sourceBlock = do
offset <- getOffset
pos <- getSourcePos
(src, (lexInfo, b)) <- withSource $ withLexemeInfo $ withRecovery recover $ sourceBlock'
let lexInfo' = lexInfo { lexemeInfo = lexemeInfo lexInfo <&> \(t, (l, r)) -> (t, (l-offset, r-offset))}
return $ SourceBlock (unPos (sourceLine pos)) offset src lexInfo' b
recover :: ParseError Text Void -> Parser SourceBlock'
recover e = do
pos <- liftM statePosState getParserState
reachedEOF <- try (mayBreak sc >> eof >> return True)
<|> return False
consumeTillBreak
let errmsg = errorBundlePretty (ParseErrorBundle (e :| []) pos)
return $ UnParseable reachedEOF errmsg
importModule :: Parser SourceBlock'
importModule = Misc . ImportModule . OrdinaryModule <$> do
keyWord ImportKW
WithSrc _ s <- anyCaseName
eol
return s
declareForeign :: Parser SourceBlock'
declareForeign = do
keyWord ForeignKW
foreignName <- strLit
b <- anyName
void $ label "type annotation" $ sym ":"
ty <- cGroup
eol
return $ DeclareForeign (fmap fromString foreignName) b ty
declareCustomLinearization :: Parser SourceBlock'
declareCustomLinearization = do
zeros <- (keyWord CustomLinearizationSymbolicKW $> SymbolicZeros)
<|> (keyWord CustomLinearizationKW $> InstantiateZeros)
fun <- anyCaseName
linearization <- cGroup
eol
return $ DeclareCustomLinearization fun zeros linearization
consumeTillBreak :: Parser ()
consumeTillBreak = void $ manyTill anySingle $ eof <|> void (try (eol >> eol))
sourceBlock' :: Parser SourceBlock'
sourceBlock' =
proseBlock
<|> topLevelCommand
<|> liftM TopDecl topDecl
<|> topLetOrExpr <* eolf
<|> hidden (some eol >> return (Misc EmptyLines))
<|> hidden (sc >> eol >> return (Misc CommentLine))
topDecl :: Parser CTopDeclW
topDecl = withSrcs topDecl' <* eolf
topDecl' :: Parser CTopDecl
topDecl' =
dataDef
<|> structDef
<|> interfaceDef
<|> (CInstanceDecl <$> instanceDef True)
<|> (CInstanceDecl <$> instanceDef False)
proseBlock :: Parser SourceBlock'
proseBlock = label "prose block" $
char '\'' >> fmap (Misc . ProseBlock . fst) (withSource consumeTillBreak)
topLevelCommand :: Parser SourceBlock'
topLevelCommand =
importModule
<|> declareForeign
<|> declareCustomLinearization
-- <|> (Misc . QueryEnv <$> envQuery)
<|> explicitCommand
<?> "top-level command"
_envQuery :: Parser EnvQuery
_envQuery = error "not implemented"
-- string ":debug" >> sc >> (
-- (DumpSubst <$ (string "env" >> sc))
-- <|> (InternalNameInfo <$> (string "iname" >> sc >> rawName))
-- <|> (SourceNameInfo <$> (string "sname" >> sc >> anyName)))
-- <* eol
-- where
-- rawName :: Parser RawName
-- rawName = RawName <$> (fromString <$> anyName) <*> intLit
explicitCommand :: Parser SourceBlock'
explicitCommand = do
cmdName <- char ':' >> nameString
cmd <- case cmdName of
"p" -> return $ EvalExpr (Printed Nothing)
"pp" -> return $ EvalExpr (Printed (Just PrintHaskell))
"pcodegen"-> return $ EvalExpr (Printed (Just PrintCodegen))
"t" -> return $ GetType
"html" -> return $ EvalExpr RenderHtml
"export" -> ExportFun <$> nameString
_ -> fail $ "unrecognized command: " ++ show cmdName
b <- cBlock <* eolf
e <- case b of
ExprBlock e -> return e
IndentedBlock sid decls -> withSrcs $ return $ CDo $ IndentedBlock sid decls
return $ case (e, cmd) of
(WithSrcs sid _ (CLeaf (CIdentifier v)), GetType) -> Misc $ GetNameType (WithSrc sid v)
_ -> Command cmd e
type CDefBody = ([(SourceNameW, GroupW)], [(LetAnn, CDef)])
structDef :: Parser CTopDecl
structDef = do
keyWord StructKW
tyName <- anyName
(params, givens) <- typeParams
(fields, defs) <- oneLiner <|> multiLiner
return $ CStruct tyName params givens fields defs
where
oneLiner :: Parser CDefBody
oneLiner = do
field <- nameAndType
return ([field], [])
multiLiner :: Parser CDefBody
multiLiner = partitionEithers <$> onePerLine do
( (Left <$> nameAndType)
<|> (Right <$> funDefLetWithAnn))
funDefLetWithAnn :: Parser (LetAnn, CDef)
funDefLetWithAnn = do
ann <- topLetAnn <|> return PlainLet
def <- funDefLet
return (ann, def)
dataDef :: Parser CTopDecl
dataDef = do
keyWord DataKW
tyName <- anyName
(params, givens) <- typeParams
dataCons <- onePerLine do
dataConName <- anyName
dataConArgs <- optional explicitParams
return (dataConName, dataConArgs)
return $ CData tyName params givens dataCons
interfaceDef :: Parser CTopDecl
interfaceDef = do
keyWord InterfaceKW
className <- anyName
params <- explicitParams
methodDecls <- try (withIndent (keyWord PassKW) >> return [])
<|> onePerLine do
methodName <- anyName
void $ label "type annotation" $ sym ":"
ty <- cGroup
return (methodName, ty)
return $ CInterface className params methodDecls
nameAndType :: Parser (SourceNameW, GroupW)
nameAndType = do
n <- anyName
sym ":"
arg <- cGroup
return (n, arg)
topLetOrExpr :: Parser SourceBlock'
topLetOrExpr = topLet >>= \case
WithSrcs _ _ (CSDecl ann (CExpr e)) -> do
when (ann /= PlainLet) $ fail "Cannot annotate expressions"
return $ Command (EvalExpr (Printed Nothing)) e
d -> return $ TopDecl d
topLet :: Parser CTopDeclW
topLet = withSrcs do
lAnn <- topLetAnn <|> return PlainLet
decl <- cDecl
return $ CSDecl lAnn decl
topLetAnn :: Parser LetAnn
topLetAnn = do
void $ char '@'
ann <- (string "inline" $> InlineLet)
<|> (string "noinline" $> NoInlineLet)
nextLine
return ann
onePerLine :: Parser a -> Parser [a]
onePerLine p = liftM (:[]) p
<|> (withIndent $ p `sepBy1` try nextLine)
{-# INLINE onePerLine #-}
-- === Groups ===
cBlock :: Parser CSBlock
cBlock = indentedBlock <|> ExprBlock <$> cGroup
indentedBlock :: Parser CSBlock
indentedBlock = withIndent do
WithSrcs sid _ decls <- withSrcs $ withSrcs cDecl `sepBy1` (void semicolon <|> try nextLine)
return $ IndentedBlock sid decls
cDecl :: Parser CSDecl
cDecl = (CDefDecl <$> funDefLet)
<|> simpleLet
<|> (keyWord PassKW >> return CPass)
simpleLet :: Parser CSDecl
simpleLet = do
lhs <- cGroupNoEqual
next <- nextChar
case next of
'=' -> sym "=" >> CLet lhs <$> cBlock
'<' -> sym "<-" >> CBind lhs <$> cBlock
_ -> return $ CExpr lhs
instanceDef :: Bool -> Parser CInstanceDef
instanceDef isNamed = do
optNameAndArgs <- case isNamed of
False -> keyWord InstanceKW $> Nothing
True -> keyWord NamedInstanceKW >> do
name <- anyName
args <- (sym ":" >> return Nothing)
<|> ((Just <$> parenList cParenGroup) <* sym "->")
return $ Just (name, args)
className <- anyName
args <- argList
givens <- optional givenClause
methods <- withIndent $ (withSrcs cDecl) `sepBy1` try nextLine
return $ CInstanceDef className args givens methods optNameAndArgs
funDefLet :: Parser CDef
funDefLet = label "function definition" do
keyWord DefKW
mayBreak do
name <- anyName
params <- explicitParams
rhs <- optional do
expl <- explicitness
effs <- optional cEffs
resultTy <- cGroupNoEqual
return (expl, effs, resultTy)
givens <- optional givenClause
mayNotBreak do
sym "="
body <- cBlock
return $ CDef name params rhs givens body
explicitness :: Parser AppExplicitness
explicitness = (sym "->" $> ExplicitApp)
<|> (sym "->>" $> ImplicitApp)
-- Intended for occurrences, like `foo(x, y, z)` (cf. defParamsList).
argList :: Parser [GroupW]
argList = do
WithSrcs _ _ gs <- withSrcs $ bracketedGroup immediateLParen rParen cParenGroup
return gs
immediateLParen :: Parser ()
immediateLParen = label "'(' (without preceding whitespace)" do
nextChar >>= \case
'(' -> precededByWhitespace >>= \case
True -> empty
False -> lParen
_ -> empty
-- Putting `sym =` inside the cases gives better errors.
typeParams :: Parser (Maybe ExplicitParams, Maybe GivenClause)
typeParams =
(explicitParamsAndGivens <* sym "=")
<|> (return (Nothing, Nothing) <* sym "=")
explicitParamsAndGivens :: Parser (Maybe ExplicitParams, Maybe GivenClause)
explicitParamsAndGivens = (,) <$> (Just <$> explicitParams) <*> optional givenClause
explicitParams :: Parser ExplicitParams
explicitParams = label "parameter list in parentheses (without preceding whitespace)" $
withSrcs $ bracketedGroup immediateLParen rParen cParenGroup
parenList :: Parser GroupW -> Parser BracketedGroup
parenList p = withSrcs $ bracketedGroup lParen rParen p
bracketedGroup :: Parser () -> Parser () -> Parser GroupW -> Parser [GroupW]
bracketedGroup l r p = bracketed l r $ commaSep p
noGap :: Parser ()
noGap = precededByWhitespace >>= \case
True -> fail "Unexpected whitespace"
False -> return ()
givenClause :: Parser GivenClause
givenClause = do
keyWord GivenKW
(,) <$> parenList cGroup <*> optional (parenList cGroup)
withClause :: Parser WithClause
withClause = keyWord WithKW >> parenList cGroup
cEffs :: Parser CEffs
cEffs = withSrcs $ braces do
effs <- commaSep cGroupNoPipe
effTail <- optional $ sym "|" >> cGroup
return (effs, effTail)
commaSep :: Parser a -> Parser [a]
commaSep p = sepBy p (sym ",")
cParenGroup :: Parser GroupW
cParenGroup = (withSrcs (CGivens <$> givenClause)) <|> cGroup
cGroup :: Parser GroupW
cGroup = makeExprParser leafGroup ops
cGroupNoJuxt :: Parser GroupW
cGroupNoJuxt = makeExprParser leafGroup $
withoutOp "space" $ withoutOp "." ops
cGroupNoEqual :: Parser GroupW
cGroupNoEqual = makeExprParser leafGroup $
withoutOp "=" ops
cGroupNoPipe :: Parser GroupW
cGroupNoPipe = makeExprParser leafGroup $
withoutOp "|" ops
cGroupNoArrow :: Parser GroupW
cGroupNoArrow = makeExprParser leafGroup $
withoutOp "->" ops
cNullaryLam :: Parser Group
cNullaryLam = do
void $ sym "\\."
body <- cBlock
return $ CLambda [] body
cLam :: Parser Group
cLam = do
void $ sym "\\"
bs <- many cGroupNoJuxt
void $ mayNotBreak $ sym "."
body <- cBlock
return $ CLambda bs body
cFor :: Parser Group
cFor = do
kw <- forKW
indices <- many cGroupNoJuxt
void $ mayNotBreak $ sym "."
body <- cBlock
return $ CFor kw indices body
where forKW = keyWord ForKW $> KFor
<|> keyWord For_KW $> KFor_
<|> keyWord RofKW $> KRof
<|> keyWord Rof_KW $> KRof_
cDo :: Parser Group
cDo = do
keyWord DoKW
CDo <$> cBlock
cCase :: Parser Group
cCase = do
keyWord CaseKW
scrut <- cGroup
keyWord OfKW
alts <- onePerLine cAlt
return $ CCase scrut alts
cAlt :: Parser CaseAlt
cAlt = do
pat <- cGroupNoArrow
sym "->"
body <- cBlock
return (pat, body)
-- see [Note if-syntax]
cIf :: Parser Group
cIf = mayNotBreak do
keyWord IfKW
predicate <- cGroup
(cons, alt) <- thenSameLine <|> thenNewLine
return $ CIf predicate cons alt
thenSameLine :: Parser (CSBlock, Maybe CSBlock)
thenSameLine = do
void $ keyWord ThenKW
cBlock >>= \case
IndentedBlock sid blk -> do
let msg = ("No `else` may follow same-line `then` and indented consequent"
++ "; indent and align both `then` and `else`, or write the "
++ "whole `if` on one line.")
mayBreak $ noElse msg
return (IndentedBlock sid blk, Nothing)
ExprBlock ex -> do
alt <- optional
$ (keyWord ElseKW >> cBlock)
<|> (lookAhead (try $ withIndent (keyWord ElseKW))
>> withIndent (keyWord ElseKW >> cBlock))
return (ExprBlock ex, alt)
thenNewLine :: Parser (CSBlock, Maybe CSBlock)
thenNewLine = withIndent $ do
void $ keyWord ThenKW
cBlock >>= \case
IndentedBlock sid blk -> do
alt <- do
-- With `mayNotBreak`, this just forbids inline else
noElse ("Same-line `else` may not follow indented consequent;"
++ " put the `else` on the next line.")
optional $ do
void $ try $ nextLine >> keyWord ElseKW
cBlock
return (IndentedBlock sid blk, alt)
ExprBlock ex -> do
void $ optional $ try nextLine
alt <- optional $ keyWord ElseKW >> cBlock
return (ExprBlock ex, alt)
noElse :: String -> Parser ()
noElse msg = (optional $ try $ sc >> keyWord ElseKW) >>= \case
Just _ -> fail msg
Nothing -> return ()
leafGroup :: Parser GroupW
leafGroup = leafGroup' >>= appendPostfixGroups
where
leafGroup' :: Parser GroupW
leafGroup' = do
next <- nextChar
case next of
'_' -> withSrcs $ CLeaf <$> (underscore >> pure CHole)
'(' -> toCLeaf CIdentifier <$> symName
<|> cParens
'[' -> cBrackets
'\"' -> toCLeaf CString <$> strLit
'\'' -> toCLeaf CChar <$> charLit
'%' -> do
WithSrc sid name <- primName
case strToPrimName name of
Just prim -> WithSrcs sid [] <$> CPrim prim <$> argList
Nothing -> fail $ "Unrecognized primitive: " ++ name
_ | isDigit next -> ( toCLeaf CNat <$> natLit
<|> toCLeaf CFloat <$> doubleLit)
'\\' -> withSrcs (cNullaryLam <|> cLam)
-- For exprs include for, rof, for_, rof_
'f' -> (withSrcs cFor) <|> cIdentifier
'd' -> (withSrcs cDo) <|> cIdentifier
'r' -> (withSrcs cFor) <|> cIdentifier
'c' -> (withSrcs cCase) <|> cIdentifier
'i' -> (withSrcs cIf) <|> cIdentifier
_ -> cIdentifier
appendPostfixGroups :: GroupW -> Parser GroupW
appendPostfixGroups g =
(noGap >> appendPostfixGroup g >>= appendPostfixGroups)
<|> return g
appendPostfixGroup :: GroupW -> Parser GroupW
appendPostfixGroup g = withSrcs $
(CJuxtapose False g <$> cParens)
<|> (CJuxtapose False g <$> cBrackets)
<|> appendFieldAccess g
appendFieldAccess :: GroupW -> Parser Group
appendFieldAccess g = try do
dot
field <- cFieldName
return $ CBin Dot g field
cFieldName :: Parser GroupW
cFieldName = cIdentifier <|> (toCLeaf CNat <$> natLit)
cIdentifier :: Parser GroupW
cIdentifier = toCLeaf CIdentifier <$> anyName
toCLeaf :: (a -> CLeaf) -> WithSrc a -> GroupW
toCLeaf toLeaf (WithSrc sid leaf) = WithSrcs sid [] $ CLeaf $ toLeaf leaf
cParens :: Parser GroupW
cParens = withSrcs $ CParens <$> bracketedGroup lParen rParen cParenGroup
cBrackets :: Parser GroupW
cBrackets = withSrcs $ CBrackets <$> bracketedGroup lBracket rBracket cGroup
-- A `PrecTable` is enough information to (i) remove or replace
-- operators for special contexts, and (ii) build the input structure
-- for Expr.makeExprParser.
type PrecTable a = [[(SourceName, Expr.Operator Parser a)]]
makeExprParser :: Parser a -> PrecTable a -> Parser a
makeExprParser p tbl = Expr.makeExprParser p tbl' where
tbl' = map (map snd) tbl
withoutOp :: SourceName -> PrecTable a -> PrecTable a
withoutOp op tbl = map (filter ((/= op) . fst)) tbl
ops :: PrecTable GroupW
ops =
[ [symOpL "!"]
, [juxtaposition]
, [unOpPre "-", unOpPre "+"]
, [backquote]
-- Other ops with default fixity
, [other]
, [symOpL "*", symOpL "/"]
, [symOpL ".*", symOpL "*."]
, [symOpL ".**", symOpL "**."]
, [symOpL "**"]
, [symOpL "+", symOpL "-"]
, [symOpL "-|"]
, [symOpL "+>>"]
, [symOpL "<>"]
, [symOpN "~~"]
, [symOpN "<", symOpN "<=", symOpN ">", symOpN ">="]
, [symOpN "==", symOpN "!="]
, [symOpL "&&"]
, [symOpL "||"]
, [symOpR "=>"]
, [arrow, symOpR "->>"]
, [symOpL ">>>"]
, [symOpL "<<<"]
, [symOpL "@"]
, [symOpN "::"]
, [symOpR "$"]
, [symOpN "+=", symOpN ":="]
-- Associate right so the mistaken utterance foo : i:Fin 4 => (..i)
-- groups as a bad pi type rather than a bad binder
, [symOpR ":"]
, [symOpL "|"]
, [symOpR ",>"]
, [symOpR "&>"]
, [withClausePostfix]
, [symOpL "="]
] where
other = ("other", anySymOp)
backquote = ("backquote", Expr.InfixL backquoteOp)
juxtaposition = ("space", Expr.InfixL $ sc >> addSrcIdToBinOp (return \x y -> ([], CJuxtapose True x y)))
withClausePostfix = ("with", Expr.Postfix withClausePostfixOp)
arrow = ("->", Expr.InfixR arrowOp)
addSrcIdToBinOp :: Parser (GroupW -> GroupW -> ([SrcId], Group)) -> Parser (GroupW -> GroupW -> GroupW)
addSrcIdToBinOp op = do
f <- op
sid <- freshSrcId
return \x y -> do
let (atomicSids, g) = f x y
WithSrcs sid atomicSids g
{-# INLINE addSrcIdToBinOp #-}
addSrcIdToUnOp :: Parser (GroupW -> Group) -> Parser (GroupW -> GroupW)
addSrcIdToUnOp op = do
f <- op
sid <- freshSrcId
return \x -> WithSrcs sid [] $ f x
{-# INLINE addSrcIdToUnOp #-}
backquoteOp :: Parser (GroupW -> GroupW -> GroupW)
backquoteOp = binApp do
fname <- backquoteName
return ([], EvalBinOp fname)
anySymOp :: Expr.Operator Parser GroupW
anySymOp = Expr.InfixL $ binApp do
WithSrc sid s <- label "infix operator" (mayBreak anySym)
return $ interpOperator sid s
symOpN :: String -> (SourceName, Expr.Operator Parser GroupW)
symOpN s = (fromString s, Expr.InfixN $ symOp s)
symOpL :: String -> (SourceName, Expr.Operator Parser GroupW)
symOpL s = (fromString s, Expr.InfixL $ symOp s)
symOpR :: String -> (SourceName, Expr.Operator Parser GroupW)
symOpR s = (fromString s, Expr.InfixR $ symOp s)
symOp :: String -> Parser (GroupW -> GroupW -> GroupW)
symOp s = binApp do
sid <- label "infix operator" (mayBreak $ symWithId $ T.pack s)
return $ interpOperator sid s
arrowOp :: Parser (GroupW -> GroupW -> GroupW)
arrowOp = addSrcIdToBinOp do
sid <- symWithId "->"
optEffs <- optional cEffs
return \lhs rhs -> ([sid], CArrow lhs optEffs rhs)
unOpPre :: String -> (SourceName, Expr.Operator Parser GroupW)
unOpPre s = (fromString s, Expr.Prefix $ prefixOp s)
prefixOp :: String -> Parser (GroupW -> GroupW)
prefixOp s = addSrcIdToUnOp do
symId <- symWithId (fromString s)
return $ CPrefix (WithSrc symId $ fromString s)
binApp :: Parser ([SrcId], Bin) -> Parser (GroupW -> GroupW -> GroupW)
binApp f = addSrcIdToBinOp do
(sids, op) <- f
return \x y -> (sids, CBin op x y)
withClausePostfixOp :: Parser (GroupW -> GroupW)
withClausePostfixOp = addSrcIdToUnOp do
rhs <- withClause
return \lhs -> CWith lhs rhs
withSrcs :: Parser a -> Parser (WithSrcs a)
withSrcs p = do
sid <- freshSrcId
(sids, result) <- collectAtomicLexemeIds p
return $ WithSrcs sid sids result
-- === notes ===
-- note [if-syntax]
-- We support the following syntaxes for `if`:
-- - 1-armed then-newline
-- if predicate
-- then consequent
-- if predicate
-- then
-- consequent1
-- consequent2
-- - 2-armed then-newline else-newline
-- if predicate
-- then consequent
-- else alternate
-- and the three other versions where the consequent or the
-- alternate are themselves blocks
-- - 1-armed then-inline
-- if predicate then consequent
-- if predicate then
-- consequent1
-- consequent2
-- - 2-armed then-inline else-inline
-- if predicate then consequent else alternate
-- if predicate then consequent else
-- alternate1
-- alternate2
-- - Notably, an imagined 2-armed then-inline else-newline
-- if predicate then
-- consequent1
-- consequent2
-- else alternate
-- is not an option, because the indentation lines up badly. To wit,
-- one would want the `else` to be indented relative to the `if`,
-- but outdented relative to the consequent block, and if the `then` is
-- inline there is no indentation level that does that.
-- - Last candiate is
-- if predicate
-- then consequent else alternate
-- if predicate
-- then consequent else
-- alternate1
-- alternate2
|