-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProjectParser.fs
756 lines (651 loc) · 22.4 KB
/
ProjectParser.fs
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
745
746
747
748
749
750
751
752
753
754
755
756
module ProjectParser
open System
open System.Text.RegularExpressions
open Parser
type BinaryOp =
// Numerical operations
| Add
| Sub
| Mult
| Div
| Mod
// Boolean operations
| And
| Or
// Comparison operations
| Eq
| Neq
| Leq
| Geq
| Lt
| Gt
| Dot
let binaryOps = [
Add; Sub; Mult; Div; Mod; And; Or; Eq; Neq; Leq; Geq; Lt; Gt; Dot
]
let assignmentOps = [
Add; Sub; Mult; Div; Mod; And; Or
]
let precedences = [
[Dot];
[Mult; Div; Mod];
[Add; Sub];
[Leq; Geq; Lt; Gt];
[Eq; Neq];
[And];
[Or]
]
let optostr op =
match op with
| Add -> "+"
| Sub -> "-"
| Mult -> "*"
| Div -> "/"
| Mod -> "%"
| Eq -> "=="
| Neq -> "!="
| Leq -> "<="
| Geq -> ">="
| Lt -> "<"
| Gt -> ">"
| And -> "&"
| Or -> "|"
| Dot -> "."
type Expr =
| FunctionCallExpr of string * Expr list
| NumLiteral of int
| StringLiteral of string
| BoolLiteral of bool
| ListLiteral of Expr list
| ThisLiteral
| ParensExpr of Expr
| BinaryExpr of BinaryOp * Expr * Expr
| UnaryMinus of Expr
| UnaryPlus of Expr
| UnaryNot of Expr
| PropertyFunctionCall of Expr * string * Expr list
| LValueExpr of LValue
// Value that can appear at the left side of an assignment
and LValue =
| PropertyAccessor of Expr * string
| PropertyArrayAccessor of Expr * string * Expr list
| ArrayAccessor of Expr * Expr list
| Identifier of string
type Stmt =
| FunctionCallStmt of string * Expr list
| PropertyFunctionCallStmt of Expr * string * Expr list
| AssignmentStmt of LValue * BinaryOp option * Expr
| LetStmt of string * Expr
| ReturnStmt of Expr
| IfElseStmt of
(Expr * Stmt list) *
((Expr * Stmt list) list) *
Stmt list option
| WhileStmt of Expr * Stmt list
| ForStmt of string * Expr * Stmt list
type Defn =
// Function with (name, arg_names[], body)
| FunctionDefn of string * string list * Stmt list
| ScopeDefn of string * Defn list
| AssignmentDefn of string * Expr
let prettyprintcommasep stringify exprs =
(List.fold (fun a b ->
(if a <> "" then a + ", " else "") + stringify b
) "" exprs)
let prettyprintfunc stringify exprs =
"(" + (prettyprintcommasep stringify exprs) + ")"
let rec prettyprintexpr expr =
match expr with
| FunctionCallExpr(name, exprs) -> prettyprintcall name exprs
| NumLiteral(num) -> sprintf "%i" num
| StringLiteral(str) -> sprintf "\"%s\"" str
| BoolLiteral(b) -> if b then "true" else "false"
| ListLiteral(exprs) ->
"[" + (prettyprintcommasep prettyprintexpr exprs) + "]"
| ThisLiteral -> "this"
| ParensExpr(e) -> sprintf "(%s)" (prettyprintexpr e)
| BinaryExpr(op, e1, e2) -> prettyprintinfix (optostr op) e1 e2
| UnaryMinus(e) -> sprintf "-%s" (prettyprintexpr e)
| UnaryPlus(e) -> sprintf "+%s" (prettyprintexpr e)
| UnaryNot(e) -> sprintf "!%s" (prettyprintexpr e)
| PropertyFunctionCall(expr, name, exprs) ->
sprintf "%s.%s"
(prettyprintexpr expr)
(prettyprintcall name exprs)
| LValueExpr lv ->
prettyprintlvalue lv
and prettyprintlvalue lv =
match lv with
| Identifier name -> name
| PropertyAccessor(expr, name) ->
sprintf "%s.%s"
(prettyprintexpr expr)
name
| PropertyArrayAccessor(expr, name, exprs) ->
sprintf "%s.%s%s"
(prettyprintexpr expr)
name
(prettyprintarrayaccess exprs)
| ArrayAccessor(expr, exprs) ->
sprintf "%s%s"
(prettyprintexpr expr)
(prettyprintarrayaccess exprs)
and prettyprintarrayaccess exprs =
let wrapWithArray str =
sprintf "[%s]" str
exprs
|> List.map prettyprintexpr
|> List.map wrapWithArray
|> String.concat ""
and prettyprintcall name exprs =
name + ((prettyprintfunc prettyprintexpr) exprs)
and prettyprintinfix op e1 e2 =
sprintf "%s %s %s"
(prettyprintexpr e1) op (prettyprintexpr e2)
let prettyprintassignment lv oop expr =
let opPart =
match oop with
| None -> ""
| Some op -> optostr op
sprintf "%s %s= %s" (prettyprintlvalue lv) opPart (prettyprintexpr expr)
let prettyprintlet name expr =
"let " + (prettyprintassignment (Identifier name) None expr)
// Currently auto-detects indentation size
let defaultIndentationSize = 4
let indentation = String.replicate defaultIndentationSize " "
let indent xs =
List.map (fun s -> indentation + s) xs
let rec prettyprintgroup header innerPrinter block =
header + ":"
:: (indent (block |> List.collect innerPrinter))
let rec prettyprintstmt stmt =
match stmt with
| FunctionCallStmt(name, exprs) -> [prettyprintcall name exprs]
| PropertyFunctionCallStmt(expr, name, exprs) ->
[sprintf "%s.%s"
(prettyprintexpr expr)
(prettyprintcall name exprs)]
| AssignmentStmt(lv, op, expr) ->
[prettyprintassignment lv op expr]
| LetStmt(name, expr) -> [prettyprintlet name expr]
| ReturnStmt(expr) -> ["return " + prettyprintexpr expr]
| IfElseStmt((cond, block), condBlockList, optionBlock) ->
let elseLines =
match optionBlock with
| None -> []
| Some v -> prettyprintgroup "else" prettyprintstmt v
List.concat
[
prettyprintgroup ("if " + prettyprintexpr cond)
prettyprintstmt block
List.collect (fun (innerCond, innerBlock) ->
(prettyprintgroup ("elif " + prettyprintexpr innerCond)
prettyprintstmt innerBlock)
) condBlockList
elseLines
]
| WhileStmt(cond, block) ->
prettyprintgroup ("while " + prettyprintexpr cond)
prettyprintstmt block
| ForStmt(iden, expr, block) ->
prettyprintgroup
("for " + iden + " in " + (prettyprintexpr expr))
prettyprintstmt block
let prettyprintlist printer xs =
xs
|> List.map (printer >> (List.fold (fun a b -> a + "\n" + b) ""))
|> List.fold (+) ""
let rec prettyprintdef def =
match def with
| FunctionDefn(name, args, body) ->
prettyprintgroup (name + (prettyprintfunc id args))
prettyprintstmt body
| ScopeDefn(name, body) ->
prettyprintgroup name prettyprintdef body
| AssignmentDefn(name, expr) ->
[prettyprintassignment (Identifier name) None expr]
let prettyprint =
prettyprintlist prettyprintdef
let poption (parser: Parser<'a>) input : Outcome<'a Option> =
match parser input with
| Success (res, rem) -> Success (Some res, rem)
| Failure -> Success (None, input)
let pmapoption (parser: Parser<'a option>) input : Outcome<'a> =
match parser input with
| Failure -> Failure
| Success (resv, rem) ->
match resv with
| None -> Failure
| Some s -> Success(s, rem)
let isSymb c = is_regexp (c.ToString()) @"[_$]"
let isLetterSymb c = is_letter c || isSymb c
let isValidChar c = isLetterSymb c || is_digit c
let isNotQuote c = c <> '\"'
let psymb = psat isSymb
let plettersymb = psat isLetterSymb
let pvalidchar = psat isValidChar
let charListToString str =
str |> List.map (fun x -> x.ToString()) |> String.concat ""
let pidentifier =
pseq plettersymb (pmany0 pvalidchar)
(fun (x,xs) ->
(x :: xs)
|> charListToString
)
let pidsep = (poption (pchar '?'))
let pid = pidentifier |>> Identifier
let plistsep sepParser innerParser =
poption (pseq (pmany0 (pleft innerParser sepParser)) innerParser
(fun (xs, x) -> List.append xs [x])) |>>
(fun x ->
match x with
| Some y -> y
| None -> List.empty
)
let pFuncHelper innerParser =
pseq (pleft pidentifier (pchar '('))
(pleft
(plistsep (pchar ',') innerParser)
(pchar ')')
)
(fun (name, args) -> (name, args))
let (pExpr : Parser<Expr>), pExprImpl = recparser()
let (pInnerExpr : Parser<Expr>), pInnerExprImpl = recparser()
let pFuncCall = pFuncHelper pExpr
let pFuncCallExpr = pFuncCall |>> FunctionCallExpr
let pFuncCallStmt = pFuncCall |>> FunctionCallStmt
let pPropertyFuncCallStmt input =
match pExpr input with
| Failure -> Failure
| Success (resV, rem) ->
match resV with
| PropertyFunctionCall (a, b, c) ->
Success (PropertyFunctionCallStmt (a, b, c), rem)
| _ -> Failure
let tryParseInt (s : string) =
match System.Int32.TryParse(s) with
| (true, v) -> Some v
| (false, _) -> None
let pPosInt = pmapoption (pmany1 pdigit |>> charListToString |>> tryParseInt)
let pNumLiteral =
pseq (poption (pchar '-'))
pPosInt
(fun (oc, num) ->
match oc with
| Some _ -> -num
| None -> num
) |>> NumLiteral
//String parsing is handled by the upper parser
//let pStrInner = (pright (pchar '\\') pitem) <|> (psat isNotQuote)
//let pStrLiteral =
// pleft (pright (pchar '"') (pmany0 pStrInner)) (pchar '"')
// |>> charListToString
// |>> StringLiteral
let pThisLiteral = pfresult (pstr "this") (ThisLiteral)
let pTrueLiteral = pfresult (pstr "true") (BoolLiteral true)
let pFalseLiteral = pfresult (pstr "false") (BoolLiteral false)
let pBoolLiteral = pTrueLiteral <|> pFalseLiteral
let pListLiteral =
pbetween
(pchar '[')
(pchar ']')
(plistsep (pchar ',') pExpr)
|>> ListLiteral
let pParens = pbetween (pchar '(') (pchar ')') pExpr |>> ParensExpr
let pUnaryMinus = pright (pchar '-') pInnerExpr |>> UnaryMinus
let pUnaryPlus = pright (pchar '+') pInnerExpr |>> UnaryPlus
let pUnaryNot = pright (pchar '!') pInnerExpr |>> UnaryNot
let pUnaryOp = pUnaryMinus <|> pUnaryPlus <|> pUnaryNot
let pConsumingExpr =
pUnaryOp
<|> pFuncCallExpr
<|> pThisLiteral
<|> pBoolLiteral
<|> (pid |>> LValueExpr)
<|> pNumLiteral
<|> pParens
<|> pListLiteral
// <|> pStrLiteral
let makebin bop (a, b) =
if bop = Dot then
match b with
| LValueExpr lv ->
match lv with
| Identifier i -> Some ((PropertyAccessor(a, i)) |> LValueExpr)
| ArrayAccessor (expr, args) ->
match expr with
| LValueExpr lv ->
match lv with
| Identifier i ->
Some
((PropertyArrayAccessor(a, i, args))
|> LValueExpr)
| _ -> None
| _ -> None
| _ -> None
| FunctionCallExpr (name, args) ->
Some (PropertyFunctionCall(a, name, args))
| _ -> None
else
Some (BinaryExpr (bop, a, b))
let pbinop op =
let str = (optostr op)
let exprgen = makebin op
pfresult (pstr str) (op, exprgen)
let pinfixop opList =
List.fold (fun parser op -> (parser <|> (pbinop op))) pzero opList
let rec combineSinglePrecOp right cexpr xs predicate =
match xs with
| [] -> Some (cexpr, [])
| ((name, op), term) :: remaining ->
match combineSinglePrecOp right term remaining predicate with
| None -> None
| Some (expr, newRemaining) ->
if predicate name then
let opin = if right then (cexpr, expr) else (expr, cexpr)
match (op opin) with
| None -> None
| Some v -> Some (v, newRemaining)
else
Some (cexpr, ((name, op), expr) :: newRemaining)
let rec rightToLeftAssoc cexpr xs ys =
match xs with
| [] -> cexpr, ys
| (opn, expr) :: remaining ->
rightToLeftAssoc expr remaining ((opn, cexpr) :: ys)
let rec combineOps right precs (cexpr, xs) =
match precs with
| [] ->
match combineSinglePrecOp right cexpr xs (fun _ -> true) with
| None -> None
| Some (newExpr, _) -> Some newExpr
| singlePrec :: remaining ->
let res =
combineSinglePrecOp right cexpr xs
(fun x -> List.contains x singlePrec)
match res with
| None -> None
| Some (newExpr, nxs) -> combineOps right remaining (newExpr, nxs)
let combineOpsRight precs (cexpr, xs) =
combineOps true precs (cexpr, xs)
let combineOpsLeft precs (cexpr, xs) =
combineOps false precs (rightToLeftAssoc cexpr xs [])
let pArrayAccessSingle =
pbetween (pchar '[') (pchar ']') pExpr
let pArrayAccessExpr =
pseq pConsumingExpr (pmany1 pArrayAccessSingle) ArrayAccessor
pInnerExprImpl :=
(pArrayAccessExpr |>> LValueExpr) <|> pConsumingExpr
pExprImpl :=
pmapoption
(pseq pInnerExpr
(pmany0
(pseq (pinfixop binaryOps)
pInnerExpr
id
)
)
(combineOpsLeft precedences))
let pAssignmentExpr input =
match pExpr input with
| Failure -> Failure
| Success (expr, rem) ->
match expr with
| LValueExpr lv ->
Success (lv, rem)
| _ -> Failure
let pOpEq =
pleft (poption (pinfixop assignmentOps)) (pchar '=') |>> Option.map fst
let pComplexAssignment =
pseq (pseq pAssignmentExpr pOpEq id) pExpr id
let pSimpleAssignment =
pseq (pleft pidentifier (pchar '=')) pExpr id
let pAssignmentStmt =
pComplexAssignment
|>> (fun ((a, b), c) -> a, b, c)
|>> AssignmentStmt
let pAssignmentGlobal = pSimpleAssignment |>> AssignmentDefn
let pIdAssign =
pseq (pleft pidentifier pidsep) pSimpleAssignment id
let pIdExpr =
pseq (pleft pidentifier pidsep) pExpr id
let pIdId =
pseq (pleft pidentifier pidsep) pidentifier id
let pWordStmt pidx str outputProcessor input =
let outcome = pidx input
match outcome with
| Success ((iden, parsed), remaining) ->
if iden = str then
Success (outputProcessor parsed, remaining)
else
Failure
| Failure -> Failure
let pWordAssignStmt = pWordStmt pIdAssign "let" LetStmt
let pWordExprStmt = pWordStmt pIdExpr "return" ReturnStmt
let pStmt =
pAssignmentStmt
<|> pWordExprStmt
<|> pWordAssignStmt
<|> pFuncCallStmt
<|> pPropertyFuncCallStmt
// Use mutable to keep track of max line reached
// For program debugging purposes
let mutable maxLine = 0
let pPosIntMut input =
let retVal = pPosInt input
match retVal with
| Success (v, _) ->
maxLine <- if v > maxLine then v else maxLine
retVal
| Failure -> retVal
let pMaxLine parser = pright pPosIntMut (pright (pstr "{") parser)
let pLine parser = pbetween (pstr "{l") (pstr "}l}") (pMaxLine parser)
let pBlock parser = pbetween (pstr "{b{") (pstr "}b}") (pmany1 parser)
let pFuncHeader = pFuncHelper pidentifier
let pGroup header blockElem converter =
pbetween (pstr "{s") (pstr "}s}") (pMaxLine
(
pseq header (pBlock blockElem)
converter
))
let pIfHeader = pWordStmt pIdExpr "if" id
let pElifHeader = pWordStmt pIdExpr "elif" id
let pElseHeader = pWordStmt (pidentifier |>> (fun a -> a, ())) "else" id
//IfElseStmt((name, block), List.empty, None))
let pWhileHeader = pWordStmt pIdExpr "while" id
let pForHeader =
pseq
(pleft (pWordStmt pIdId "for" id) pidsep)
(pWordStmt pIdExpr "in" id)
id
let rec pFuncScope header =
let b : Parser<Stmt> = ((pLine pStmt) <|> pInnerScope)
pGroup header b
and pIfGroup a =
a |>
(pseq
(pseq (pFuncScope pIfHeader id)
(pmany0 (pFuncScope pElifHeader id))
id
) (poption (pFuncScope pElseHeader (fun (_, stmts) -> stmts)))
(fun (((cond, block), condBlockList), optionBlock) ->
IfElseStmt((cond, block), condBlockList, optionBlock))
)
and pWhileGroup a =
a |> pFuncScope pWhileHeader WhileStmt
and pForGroup a =
a |> pFuncScope pForHeader (fun ((a, b), c) -> ForStmt(a, b, c))
and pInnerScope a = a |> (pIfGroup <|> pWhileGroup <|> pForGroup)
let (pDefn : Parser<Defn>), pDefnImpl = recparser()
let pScope =
pGroup pidentifier pDefn
(fun (name, defns) -> ScopeDefn(name, defns))
let pFunc =
pFuncScope pFuncHeader
(fun ((name, args), block) -> FunctionDefn(name, args, block))
pDefnImpl := pFunc <|> pScope <|> (pLine pAssignmentGlobal)
let pProgram = pmany1 pDefn
let grammar = pleft pProgram peof
let parseLower input : Defn list option =
maxLine <- 0
match grammar (prepare input) with
| Success(e,_) -> Some e
| Failure -> None
let cleanLower input =
let clean0 = Regex.Replace(input, @"[\n\r]+", "")
let clean1 = Regex.Replace(clean0, @"[\s]{2,}", " ")
let rec cleanLowerInner input =
let cleanInner =
Regex.Replace(input, @"([A-Za-z0-9_$]) ([A-Za-z0-9_$])", "$1?$2")
if cleanInner = input then
cleanInner
else
cleanLowerInner cleanInner
let clean2 = cleanLowerInner clean1
let clean3 = Regex.Replace(clean2, @" ", "")
clean3
type Block =
| NoBlock
| Line of int * string
| SubBlock of int * string * Block list
let linewithnum num l =
sprintf "%04i:%s" num l
let rec prettyprintblock block =
match block with
| NoBlock -> []
| Line (_, l) -> [l]
| SubBlock(_, title, sub) ->
title + ":" :: (indent (sub |> List.collect prettyprintblock))
let initialSpacing str =
let rec initialSpacingInner clist =
match clist with
| [] -> 0
| ' ' :: xs -> 1 + initialSpacingInner xs
| _ -> 0
initialSpacingInner (Seq.toList str)
let spacesOf count =
String.replicate count " "
let rec findIndented (lineList : (int * string) list) indentationAmount =
match lineList with
| [] -> [], []
| (num, line) :: remaining ->
let actualAmount =
if indentationAmount = 0 then
initialSpacing line
else
indentationAmount
if actualAmount > 0 &&
line.Length >= actualAmount &&
line.Substring(0, actualAmount) = spacesOf actualAmount then
let unindented = line.Substring(actualAmount)
let remainingIndented, rest = findIndented remaining actualAmount
(num, unindented) :: remainingIndented, rest
else
[], lineList
let rec parseUpperNum lineList =
match lineList with
| [] -> []
| (num, line) :: remaining ->
let cleanLine = Regex.Replace(line, @":\s+$", ":")
if cleanLine.Chars(cleanLine.Length - 1) = ':' then
let cleanLine2 = cleanLine.Substring(0, cleanLine.Length - 1)
let indented, rest = findIndented remaining 0
(SubBlock(num, cleanLine2, parseUpperNum indented))
:: (parseUpperNum rest)
else
Line (num, line) :: (parseUpperNum remaining)
let parseUpper (stringList : string list) =
let zipped = List.zip [1..stringList.Length] stringList
let newLines = List.filter (fun x -> snd x <> "") zipped
parseUpperNum newLines
let stringExtract i =
sprintf "$str_%i$" i
let stringExtractRegex i =
sprintf "$$str_%i$$" i
let stringReplace (str : string) index length replacement =
String.concat "" [str.[0 .. index - 1];
replacement;
str.[index + length .. str.Length - 1]]
let extractQuotes line (strings : string[]) =
let quoteRegex = new Regex("(\"([^\"\\\\]|\\\\\\\\|\\\\\")*\")")
let m = quoteRegex.Match(line)
if m.Success then
let firstMatch = List.head (Seq.toList m.Groups)
let text = firstMatch.Value
let text2 = Regex.Replace(text, "\\\\\\\\", "\\\\")
let text3 = Regex.Replace(text2, "\\\\\"", "\"")
let outLine =
quoteRegex.Replace(line, stringExtract strings.Length, 1)
//Text contains quotes, so remove first and last char
outLine, Array.append strings [|text3.[1 .. text3.Length - 2]|]
else
line, strings
let cleanUpperLine line strings =
//Extract string literals - do this before other cleanup
let newLine, newStrings = extractQuotes line strings
//Replace illegal chars
let clean0 = Regex.Replace(newLine, @"[\{\}]", "")
//Replace comments
let clean1 = Regex.Replace(clean0, @"\#.*", "")
if Regex.Replace(clean1, @"\s", "") = "" then
"", newStrings
else
clean1, newStrings
let cleanUpper stringList =
let cleanUpperInner str (xs, assigns) =
let cleaned, newAssigns = cleanUpperLine str assigns
cleaned :: xs, newAssigns
let newLines, strings = List.foldBack cleanUpperInner stringList ([], [||])
newLines, strings
type UpperToLowerResult =
| UTLSuccess of string
| UTLFailure of int
let rec upperToLower blockList =
let upperToLowerInner state block =
match state with
| UTLFailure num -> UTLFailure num
| UTLSuccess s ->
match upperToLowerSingle block with
| UTLFailure num -> UTLFailure num
| UTLSuccess s2 -> UTLSuccess (s + s2)
List.fold upperToLowerInner (UTLSuccess "") blockList
and upperToLowerSingle block =
match block with
| NoBlock -> UTLSuccess ""
| Line (num, line) ->
//Make sure line does not start with spaces
if line.StartsWith " " then
UTLFailure num
else
UTLSuccess ((sprintf "{l%i{" num) + line + "}l}")
| SubBlock(num, title, sub) ->
if title.StartsWith " " then
UTLFailure num
else
match upperToLower sub with
| UTLFailure num -> UTLFailure num
| UTLSuccess v -> UTLSuccess ((sprintf "{s%i{" num) + title + "{b{" + v + "}b}}s}")
let assignStrings assigns =
let xs = Array.toList assigns
let xsz = List.zip [0..(xs.Length-1)] xs
List.map (fun (index, elem) ->
AssignmentDefn(stringExtract index, StringLiteral(elem))) xsz
type ParseResult =
| ParseSuccess of Defn list
//Line failed on
| ParseFailure of int option
let parseComplete lines =
let cleanLines, assigns = cleanUpper lines
let parsed = parseUpper cleanLines
match upperToLower parsed with
| UTLFailure num -> ParseFailure (Some num)
| UTLSuccess lower ->
let cleaned = cleanLower lower
match parseLower cleaned with
| None ->
let res = (Some maxLine)
maxLine <- 0
ParseFailure res
| Some s ->
maxLine <- 0
ParseSuccess (List.append (assignStrings assigns) s)