forked from mit-plv/koika
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lv.ml
1707 lines (1558 loc) · 69.9 KB
/
lv.ml
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
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(*! Lispy Verilog frontend !*)
open Common
let lcnt x = x.lcnt
let sprintf = Printf.sprintf
type nonrec 'a locd = (Pos.t, 'a) locd
let pos_of_sexp_pos ({ line; col; _ }: Parsexp.Positions.pos) =
Pos.{ line; col }
let range_of_sexp_range ({ start_pos; end_pos }: Parsexp.Positions.range) =
Pos.{ rbeg = pos_of_sexp_pos start_pos;
rend = pos_of_sexp_pos end_pos }
module UnresolvedAST = struct
type unresolved_enumerator = {
enum: string;
constructor: string
}
type unresolved_value =
| UBits of bool array
| UEnum of unresolved_enumerator
type unresolved_typedecl =
| Enum_u of { name: string locd; bitsize: int locd; members: (string locd * bits_value locd) list }
| Struct_u of { name: string locd; fields: (string locd * unresolved_type locd) list }
and unresolved_type =
| Bits_u of int
| String_u
| Array_u of unresolved_type locd * int option
| Unknown_u of string
type unresolved_literal =
| Var of var_t
| Fail of unresolved_type
| Num of (string * int)
| Type of unresolved_type
| String of string
| Keyword of string
| Enumerator of { enum: string; constructor: string }
| Const of unresolved_value
type unresolved_action =
| Fail of typ
| Assign of (var_t locd * unresolved_action locd)
| If of unresolved_action locd * unresolved_action locd * unresolved_action locd list
| Read of port_t * unresolved_reg_name locd
| Write of port_t * unresolved_reg_name locd * unresolved_action locd
(* Sugar on Coq side *)
| AstError
| Skip
| Progn of unresolved_action locd list
| Let of (var_t locd * unresolved_action locd) list * unresolved_action locd list
| When of unresolved_action locd * unresolved_action locd list
| Switch of { operand: unresolved_action locd;
default: unresolved_action locd;
branches: (unresolved_action locd * unresolved_action locd) list }
(* Not in Coq-side AST *)
| Lit of unresolved_literal
| Call of { fn: string locd; args: unresolved_action locd list }
and unresolved_reg_name = string
type unresolved_scheduler =
| Done
| Cons of rule_name_t locd * unresolved_scheduler locd
| Try of rule_name_t locd * unresolved_scheduler locd * unresolved_scheduler locd
end
module ResolvedAST = struct
open Cuttlebone
open Compilation
type uaction =
| Fail of typ
| Var of var_t
| Const of value
| Assign of (var_t locd * uaction locd)
| If of uaction locd * uaction locd * uaction locd
| Read of port_t * reg_signature locd
| Write of port_t * reg_signature locd * uaction locd
| Unop of { fn: (Extr.PrimUntyped.ufn1) locd; arg: uaction locd }
| Binop of { fn: (Extr.PrimUntyped.ufn2) locd; a1: uaction locd; a2: uaction locd }
| ExternalCall of { fn: ffi_signature locd; arg: uaction locd }
| InternalCall of { fn: uaction locd internal_function; args: uaction locd list }
| Sugar of usugar
and usugar =
| AstError
| Skip
| ConstString of string
| Progn of uaction locd list
| Let of (var_t locd * uaction locd) list * uaction locd
| When of uaction locd * uaction locd
| Switch of { operand: uaction locd;
default: uaction locd;
branches: (uaction locd * uaction locd) list }
| StructInit of { sg: struct_sig; fields: (string locd * uaction locd) list }
| ArrayInit of { sg: array_sig; elements: uaction locd list }
type uscheduler = UnresolvedAST.unresolved_scheduler
let rec translate_action ({ lpos; lcnt }: uaction locd) : Pos.t extr_uaction =
Extr.UAPos
(lpos,
match lcnt with
| Fail tau -> UFail (Util.extr_type_of_typ tau)
| Var v -> UVar v
| Const v -> let tau, v = Util.extr_value_of_value v in UConst (tau, v)
| Assign (v, expr) -> Extr.UAssign (v.lcnt, translate_action expr)
| If (e, l, r) -> Extr.UIf (translate_action e, translate_action l, translate_action r)
| Read (port, reg) -> Extr.URead (translate_port port, reg.lcnt)
| Write (port, reg, v) -> Extr.UWrite (translate_port port, reg.lcnt, translate_action v)
| Unop { fn; arg } -> UUnop (fn.lcnt, translate_action arg)
| Binop { fn; a1; a2 } -> UBinop (fn.lcnt, translate_action a1, translate_action a2)
| ExternalCall { fn; arg } -> UExternalCall (fn.lcnt, translate_action arg)
| InternalCall { fn; args } ->
UInternalCall
(Util.extr_intfun_of_intfun translate_action fn,
List.map translate_action args)
| Sugar u ->
Extr.USugar
(match u with
| AstError -> UErrorInAst
| Skip -> USkip
| ConstString str -> UConstString (Cuttlebone.Util.coq_string_of_string str)
| Progn rs ->
UProgn (List.map translate_action rs)
| Let (bs, body) ->
let bindings = List.map (fun (var, a) -> var.lcnt, translate_action a) bs in
ULet (bindings, translate_action body)
| When (e, body) ->
UWhen (translate_action e, translate_action body)
| Switch { operand; default; branches } ->
let branches = List.map (fun (lbl, br) -> translate_action lbl, translate_action br) branches in
USwitch (translate_action operand, translate_action default, branches)
| StructInit { sg; fields } ->
let fields = List.map (fun (nm, v) -> Util.coq_string_of_string nm.lcnt, translate_action v) fields in
UStructInit (Util.extr_struct_sig_of_struct_sig sg, fields)
| ArrayInit { sg; elements } ->
let elements = List.map translate_action elements in
UArrayInit (Util.extr_type_of_typ sg.array_type, elements)))
let rec translate_scheduler ({ lpos; lcnt }: uscheduler locd) =
Extr.SPos
(lpos,
match lcnt with
| Done -> Extr.Done
| Cons (r, s) ->
Extr.Cons (r.lcnt, translate_scheduler s)
| Try (r, s1, s2) ->
Extr.Try (r.lcnt, translate_scheduler s1, translate_scheduler s2))
let typecheck_rule (raw_ast: uaction locd) : (Pos.t extr_action, (Pos.t * _)) result =
typecheck_rule raw_ast.lpos (translate_action raw_ast)
type debug_printer = { debug_print: uaction -> unit }
let debug_printer : debug_printer ref =
ref { debug_print = (fun _ -> Printf.eprintf "No printer installed\n%!") }
end
open UnresolvedAST
type unresolved_rule = unresolved_action
type unresolved_register = unresolved_value
type unresolved_module = {
m_name: string locd;
m_registers: (string locd * unresolved_register locd) list;
m_rules: (string locd * unresolved_rule locd) list;
m_schedulers: (string locd * unresolved_scheduler locd) list;
m_cpp_preamble: string list;
}
type unresolved_fn_body =
| ExternalUfn
| InternalUfn of unresolved_action locd
type unresolved_fn =
{ ufn_name: string locd;
ufn_signature: (string locd * unresolved_type locd) list;
ufn_rettype: unresolved_type locd;
ufn_body: unresolved_fn_body }
type typedecls = {
td_enums: enum_sig StringMap.t;
td_structs: struct_sig StringMap.t;
td_enumerators: enum_sig StringMap.t;
td_all: typ StringMap.t
}
type unresolved_unit = {
types: unresolved_typedecl locd list;
fns: unresolved_fn list;
mods: unresolved_module locd list;
}
type resolved_extfun = ffi_signature
type resolved_defun =
ResolvedAST.uaction locd internal_function
type resolved_fn =
| FnExternal of ffi_signature
| FnInternal of resolved_defun
| FnUnop of Cuttlebone.Extr.PrimUntyped.ufn1
| FnBinop of Cuttlebone.Extr.PrimUntyped.ufn2
| FnStructInit of { sg: struct_sig; field_names: string locd list }
| FnArrayInit of { sg: array_sig }
| FnStringInit of string
type resolved_fndecl =
| ExternalDecl of resolved_extfun
| InternalDecl of resolved_defun
type resolved_module = {
name: string locd;
registers: reg_signature list;
rules: (string * ResolvedAST.uaction locd) list;
schedulers: (string * ResolvedAST.uscheduler locd) list;
cpp_preamble: string list
}
type fndecls = {
fn_ordered: (string * resolved_fndecl) list;
fn_all: resolved_fndecl StringMap.t
}
type resolved_unit = {
r_types: typedecls;
r_fns: fndecls;
r_mods: resolved_module list;
}
let quote x = "‘" ^ x ^ "’"
let fquote () = quote
let one_of_str candidates =
match candidates with
| [] -> "" | [x] -> quote x
| _ -> let candidates = candidates |> List.map quote |> String.concat ", " in
sprintf "one of %s" candidates
let in_scope_str candidates =
match candidates with
| [] -> "none in scope"
| _ -> let candidates = candidates |> List.map quote |> String.concat ", " in
sprintf "in scope: %s" candidates
type 'f sexp =
| Atom of { loc: 'f; atom: string }
| List of { loc: 'f; elements: 'f sexp list }
let sexp_pos = function
| Atom { loc; _ } | List { loc; _ } -> loc
module Errors = struct
module ParseErrors = struct
type t =
| BadPosAnnot
| SexpError of { msg: string }
| BadBitsSize of { size: string }
| Overflow of { numstr: string; bits: string; size: int }
let to_string = function
| BadPosAnnot ->
"Bad use of <>"
| SexpError { msg } ->
String.capitalize_ascii msg
| BadBitsSize { size } ->
sprintf "Unparseable size annotation: %s" (quote size)
| Overflow { numstr; bits; size } ->
sprintf "Number %a (%d'b%s) does not fit in %d bit(s)"
fquote numstr (String.length bits) bits size
end
module SyntaxErrors = struct
type t =
| MissingSize of { number: string }
| MissingElement of { kind: string }
| MissingElementIn of { kind: string; where: string }
| MissingPairElement of { kind2: string }
| TooManyElementsIn of { kind: string; where: string }
| ExpectingNil of { kind: string; prev: string }
| UnexpectedList of { expected: string }
| UnexpectedAtom of { expected: string; atom: string }
| UnexpectedType of { typ: typ }
| UnexpectedString of { str: string }
| UnexpectedKeyword of { keyword: string }
| BadChoice of { atom: string; expected: string list }
| BadLiteral of { atom: string }
| BadBitsLiteral of { atom: string }
| ReservedIdentifier of { kind: string; atom: string }
| BadIdentifier of { kind: string; atom: string }
| BadConst of { atom: string }
| BadKeyword of { kind: string; atom: string }
| BadEnumerator of { atom: string }
| BadType of { atom: string }
| BadSizeInType of { atom: string }
| BadIntParam of { obj: string; kind: string option }
| BadKeywordParam of { obj: string; kind: string }
| BadStringParam of { obj: string }
| BadTypeParam of { obj: string; kind: string }
| EmptySwitch
| EarlyDefaultInSwitch
| MissingDefaultInSwitch
| DuplicateDefaultInSwitch
| QualifiedEnumeratorInDecl of { enum: string; constructor: string }
| TooManyArgumentsInExtfunDecl
let to_string = function
| MissingSize { number } ->
sprintf "Missing size annotation on number %s" (quote number)
| MissingElement { kind } ->
sprintf "Missing %s" kind
| MissingElementIn { kind; where } ->
sprintf "No %s found in %s" kind where
| MissingPairElement { kind2 } ->
sprintf "Missing %s after this element" kind2
| TooManyElementsIn { kind; where } ->
sprintf "More than one %s found in %s" kind where
| ExpectingNil { kind; prev } ->
sprintf "Unexpected %s after %s" kind prev
| UnexpectedList { expected } ->
sprintf "Expecting %s, but got a list" expected
| UnexpectedAtom { expected; atom } ->
sprintf "Expecting a list (%s), got %a" expected fquote atom
| UnexpectedType { typ } ->
sprintf "Unexpected type %s" (typ_name typ)
| UnexpectedString { str } ->
sprintf "Unexpected string %a" fquote str
| UnexpectedKeyword { keyword } ->
sprintf "Unexpected keyword %a" fquote keyword
| BadChoice { atom; expected } ->
sprintf "Expecting %s, got %a" (one_of_str expected) fquote atom
| BadLiteral { atom } ->
sprintf "Expecting a literal (a number, variable, symbol or keyword), got %a" fquote atom
| BadBitsLiteral { atom } ->
sprintf "Expecting a sized literal (e.g. 2'b01 or 8'42), got %a" fquote atom
| BadIdentifier { kind; atom } ->
sprintf "Expecting an identifier (%s), got %a" kind fquote atom
| ReservedIdentifier { kind; atom } ->
sprintf "%a is a reserved; it cannot be used as %s" fquote atom kind
| BadConst { atom } ->
sprintf "Expecting a sized literal (e.g. 8'hff) or an enumerator (eg proto::ipv4), got %a" fquote atom
| BadKeyword { kind; atom } ->
sprintf "Expecting a keyword (%s, starting with a colon), got %a" kind fquote atom
| BadEnumerator { atom } ->
sprintf "Expecting an enumerator (format: abc::xyz or ::xyz), got %a" fquote atom
| BadType { atom } ->
sprintf "Expecting a type name (e.g. (bits 16) or 'xyz) got %a" fquote atom
| BadSizeInType { atom } ->
sprintf "Expecting a size (e.g. 32), got %a" fquote atom
| BadIntParam { obj; kind } ->
sprintf "This %s should be an integer%s" obj
(match kind with Some k -> sprintf " (%s)" k | None -> "")
| BadKeywordParam { obj; kind } ->
sprintf "This %s should be a keyword (%s, starting with a colon)" obj kind
| BadStringParam { obj } ->
sprintf "This %s should be a string" obj
| BadTypeParam { obj; kind } ->
sprintf "This %s should be a type (%s, such as bool, (array 5 (bits 8)), or a type name starting with a quote)" obj kind
| EmptySwitch ->
"No valid branch in switch: not sure what to return"
| EarlyDefaultInSwitch ->
"Default case (_) in switch precedes other branches; move it to the end"
| MissingDefaultInSwitch ->
"Missing default case (_) in switch"
| DuplicateDefaultInSwitch ->
"Duplicated default case (_) in switch"
| QualifiedEnumeratorInDecl { enum; constructor } ->
sprintf "Enumerator declarations should not be qualified: try %a instead of %a"
fquote ("::" ^ constructor) fquote (enum ^ "::" ^ constructor)
| TooManyArgumentsInExtfunDecl ->
"External fns must take a single argument (use a struct to pass multiple arguments)"
end
module NameErrors = struct
type t =
| Unbound of { kind: string; prefix: string; name: string; candidates: string list }
| Duplicate of { kind: string; name: string }
| DuplicateTypeName of { name: string; kind: string; previous: typ }
| FnShadowsPrimitive of { ext_name: string }
| MissingScheduler of { modname: string }
| MissingModule
let to_string = function
| Unbound { kind; prefix; name; candidates } ->
let candidates =
if candidates = [] then ""
else sprintf " (%s)" (in_scope_str (List.map (fun c -> prefix ^ c) candidates)) in
sprintf "Unbound %s: %a%s" kind fquote (prefix ^ name) candidates
| Duplicate { kind; name } ->
sprintf "Duplicate %s: %a" kind fquote name
| DuplicateTypeName { name; kind; previous } ->
sprintf "Duplicate type name: %s %a previously declared (as %s)" kind fquote name (kind_to_str previous)
| FnShadowsPrimitive { ext_name } ->
sprintf "Function name %a conflicts with existing primitive" fquote ext_name
| MissingScheduler { modname } ->
sprintf "Missing scheduler in module %a" fquote modname
| MissingModule ->
"No modules declared"
end
module TypeErrors = struct
type t =
| BadArgumentCount of { fn: string; expected: int; given: int }
| InconsistentEnumeratorSizes of { expected: int; actual: int }
| BadKind of { expected: string; actual_type: typ }
| ArrayLengthMismatch of { expected: int; actual: int }
| MissingArraySize
| MissingStringSize
let to_string = function
| BadArgumentCount { fn; expected; given } ->
sprintf "Function %s takes %d arguments (%d given)" fn expected given
| InconsistentEnumeratorSizes { expected; actual } ->
sprintf "Inconsistent sizes in enum: expecting %a, got %a"
fquote (sprintf "bits %d" expected) fquote (sprintf "bits %d" actual)
| BadKind { expected: string; actual_type } ->
sprintf "Got type %s but expected %s" (typ_name actual_type) expected
| ArrayLengthMismatch { expected; actual } ->
sprintf "This array has %d element(s) instead of the expected %d" actual expected
| MissingArraySize ->
sprintf "Missing size in array type"
| MissingStringSize ->
sprintf "Missing size in string type; use (array char ...) instead"
end
module TypeInferenceErrors = struct
type t = string Cuttlebone.Util.extr_error_message
let classify (msg: t) =
match msg with
| ExplicitErrorInAst -> `TypeError
| SugaredConstructorInAst -> `SyntaxError
| UnboundVariable _ -> `NameError
| OutOfBounds _ -> `TypeError
| UnboundField _ -> `NameError
| UnboundEnumMember _ -> `NameError
| TooManyArguments _ -> `SyntaxError
| TooFewArguments _ -> `SyntaxError
| TypeMismatch _ -> `TypeError
| KindMismatch _ -> `TypeError
let to_string (msg: t) =
match msg with
| ExplicitErrorInAst ->
"Untypeable term (likely due to an ill-typed subterm)"
| SugaredConstructorInAst ->
"Improper desugaring (this is a bug; please report it)"
| UnboundVariable { var } ->
sprintf "Unbound variable %a" fquote var
| OutOfBounds { pos; sg } ->
sprintf "Index %d is not in range [0 .. %d)" pos sg.array_len
| UnboundField { field; sg } ->
sprintf "Unbound field %a in %s" fquote field (struct_sig_to_string sg)
| UnboundEnumMember { name; sg } ->
sprintf "Enumerator %a is not a member of %s" fquote name (enum_sig_to_string sg)
| TooManyArguments { name; actual; expected } ->
sprintf "Too many arguments in call to %a: expected %d, got %d"
fquote name expected actual
| TooFewArguments { name; actual; expected } ->
sprintf "Too few arguments in call to %a: expected %d, got %d"
fquote name expected actual
| TypeMismatch { expected; actual } ->
sprintf "This term has type %a, but %a was expected"
fquote (typ_to_string actual) fquote (typ_to_string expected)
| KindMismatch { actual; expected } ->
sprintf "This term has type %a, but kind %a was expected"
fquote actual fquote expected
end
module Warnings = struct
type t = NoWarning
let to_string _ = ""
end
type err =
| EParse of ParseErrors.t
| ESyntax of SyntaxErrors.t
| EName of NameErrors.t
| EType of TypeErrors.t
| ETypeInference of TypeInferenceErrors.t
| EWarn of Warnings.t
let classify = function
| EParse _ -> `ParseError
| ESyntax _ -> `SyntaxError
| EName _ -> `NameError
| EType _ -> `TypeError
| ETypeInference err -> TypeInferenceErrors.classify err
| EWarn _ -> `Warning
let err_to_string = function
| EParse err -> ParseErrors.to_string err
| ESyntax err -> SyntaxErrors.to_string err
| EName err -> NameErrors.to_string err
| EType err -> TypeErrors.to_string err
| ETypeInference err -> TypeInferenceErrors.to_string err
| EWarn wrn -> Warnings.to_string wrn
type error = { epos: Pos.t; emsg: err }
let compare e1 e2 =
match Pos.compare e1.epos e2.epos with
| 0 -> compare e1.emsg e2.emsg
| n -> n
let to_string { epos; emsg } =
sprintf "%s: %s: %s"
(Pos.to_string epos)
(match (classify emsg) with
| `Warning -> "Warning"
| `ParseError -> "Parse error"
| `SyntaxError -> "Syntax error"
| `NameError -> "Name error"
| `TypeError -> "Type error")
(err_to_string emsg)
let collected_warnings : error list ref = ref []
let fetch_warnings () =
let warnings = !collected_warnings in
collected_warnings := [];
warnings
exception Errors of error list
let warning epos emsg = collected_warnings := { epos; emsg = EWarn emsg } :: !collected_warnings
end
open Errors
(* open Warnings *)
module Delay = struct
let buffer = ref []
let delay_errors = ref 0
let handle_exn = function
| Errors errs when !delay_errors > 0 -> buffer := errs :: !buffer
| exn -> raise exn
let reset_buffered_errors () =
let buffered = List.flatten (List.rev !buffer) in
buffer := [];
buffered
let raise_buffered_errors () =
let buffered = reset_buffered_errors () in
if buffered <> [] then raise (Errors buffered)
let with_delayed_errors f =
incr delay_errors;
Base.Exn.protect ~f:(fun () ->
try let result = f () in
raise_buffered_errors ();
result
with (Errors _) as exn ->
handle_exn exn;
raise (Errors (reset_buffered_errors ())))
~finally:(fun () -> decr delay_errors)
let with_exn_handler f x =
try f x with exn -> handle_exn exn
let apply1_default default f x1 = try f x1 with exn -> handle_exn exn; default
let apply2_default default f x1 x2 = try f x1 x2 with exn -> handle_exn exn; default
let apply3_default default f x1 x2 x3 = try f x1 x2 x3 with exn -> handle_exn exn; default
let apply4_default default f x1 x2 x3 x4 = try f x1 x2 x3 x4 with exn -> handle_exn exn; default
let apply1 f x1 = apply1_default () f x1
let apply2 f x1 x2 = apply2_default () f x1 x2
let apply3 f x1 x2 x3 = apply3_default () f x1 x2 x3
let apply4 f x1 x2 x3 x4 = apply4_default () f x1 x2 x3 x4
let rec iter f = function
| [] -> ()
| x :: l -> apply1 f x; iter f l
let rec map f = function
| [] -> []
| x :: xs ->
let fx = try [f x] with exn -> handle_exn exn; [] in
fx @ (map f xs)
let rec fold_left f acc l =
match l with
| [] -> acc
| x :: l ->
let acc = try f acc x with exn -> handle_exn exn; acc in
fold_left f acc l
let rec fold_right f l acc =
match l with
| [] -> acc
| x :: l ->
let acc = fold_right f l acc in
try f x acc with exn -> handle_exn exn; acc
let maybe f x =
apply1_default None (fun x -> Some (f x)) x
end
let error ?default epos emsg =
let exn = Errors [{ epos; emsg }] in
match default with
| Some v -> Delay.handle_exn exn; v
| None -> raise exn
let parse_error ?default epos emsg = error ?default epos (EParse emsg)
let syntax_error ?default epos emsg = error ?default epos (ESyntax emsg)
let name_error ?default epos msg = error ?default epos (EName msg)
let type_error ?default epos msg = error ?default epos (EType msg)
let type_inference_error ?default epos emsg = error ?default epos (ETypeInference emsg)
module Dups(OT: Map.OrderedType) = struct
module M = Map.Make(OT)
let multimap_add k v m =
let vs = match M.find_opt k m with Some vs -> vs | None -> [] in
M.add k (v :: vs) m
let multimap_of_locds keyfn xs =
List.fold_left (fun map x ->
let { lcnt = k; lpos } = keyfn x in multimap_add k (x, lpos) map)
M.empty xs
let check kind (keyfn: 'a -> OT.t locd) strfn xs =
M.iter (fun _ positions ->
Delay.iter (fun (x, lpos) ->
name_error lpos @@ Duplicate { kind; name = (strfn x) })
(List.tl (List.rev positions)))
(multimap_of_locds keyfn xs)
end
module StringDuplicates = Dups(OrderedString)
module BitsDuplicates = Dups(struct type t = bool array let compare = poly_cmp end)
let expect_cons loc kind = function
| [] -> syntax_error loc @@ MissingElement { kind }
| hd :: tl -> hd, tl
let expect_single loc kind where = function
| [] -> syntax_error loc (MissingElementIn { kind; where })
| _ :: _ :: _ -> syntax_error loc (TooManyElementsIn { kind; where })
| [x] -> x
let rec gather_pairs = function
| [] -> []
| [x1] -> [`Single x1]
| x1 :: x2 :: tl -> `Pair (x1, x2) :: gather_pairs tl
let rec list_const n x =
if n = 0 then [] else x :: list_const (n - 1) x
let read_all fname =
if fname = "-" then Stdio.In_channel.input_all Stdio.stdin
else Stdio.In_channel.read_all fname
module DelayedReader (P: Parsexp.Eager_parser) = struct
exception GotSexp of P.parsed_value * Parsexp.Positions.pos
let parse_string fname source =
let open Parsexp in
let got_sexp state parsed_value =
raise_notrace (GotSexp (parsed_value, P.State.Read_only.position state)) in
let state =
P.State.create ~no_sexp_is_error:false got_sexp in
let feed pos =
try
let len = String.length source - pos in
P.feed_substring state ~pos ~len source P.Stack.empty |> P.feed_eoi state
with Parse_error err ->
let pos = Parse_error.position err in
let range = Positions.{ start_pos = pos; end_pos = pos } in
let msg = Parse_error.message err in
parse_error (Pos.Range (fname, range_of_sexp_range range)) @@ SexpError { msg } in
let rec read_sexps pos =
P.State.reset ~pos state;
try Delay.apply1 feed pos.offset; []
with GotSexp (sexp, last_pos) -> sexp :: read_sexps last_pos in
read_sexps (P.State.position state)
end
module DelayedReader_plain = DelayedReader (Parsexp.Eager)
module DelayedReader_cst = DelayedReader (Parsexp.Eager_cst)
let read_sexps fname =
let open Parsexp in
let wrap_loc loc =
Pos.Range (fname, range_of_sexp_range loc) in
let rec translate_ast (s: Cst.t_or_comment) =
match s with
| Comment _ -> None
| Sexp (Atom { loc; atom; _ }) ->
Some (Atom { loc = wrap_loc loc; atom })
| Sexp (List { loc; elements }) ->
Some (List { loc = wrap_loc loc;
elements = Base.List.filter_map ~f:translate_ast elements }) in
let commit_annotation annot (sexp: Pos.t sexp) =
match annot with
| None -> sexp
| Some loc ->
match sexp with
| Atom { atom; _ } -> Atom { loc; atom }
| List { elements; _ } -> List { loc; elements } in
let rec commit_annotations ?(annot: Pos.t option) (sexp: Pos.t sexp) =
commit_annotation annot
(match sexp with
| Atom _ -> sexp
| List { elements = [Atom { atom = "<>"; _ }; Atom { atom = annot; _ }; body]; _ } ->
commit_annotations ~annot:(Pos.StrPos annot) body
| List { elements = (Atom { atom = "<>"; _ } :: _); loc } ->
parse_error loc @@ BadPosAnnot
| List { loc; elements } ->
List { loc; elements = List.map (commit_annotations ?annot) elements })
in
DelayedReader_cst.parse_string fname (read_all fname)
|> Base.List.filter_map ~f:translate_ast
|> Base.List.map ~f:commit_annotations
let keys s =
StringMap.fold (fun k _ acc -> k :: acc) s [] |> List.rev
let gensym_prefix = "_lvs_"
let gensym, gensym_reset = make_gensym gensym_prefix
let mangling_prefix = "_lv_"
let needs_mangling_re = Str.regexp (sprintf "^\\(%s\\|%s\\)" gensym_prefix mangling_prefix)
let mangle name =
if Str.string_match needs_mangling_re name 0 then
mangling_prefix ^ name
else name
let name_re_str = "_\\|_?[a-zA-Z][a-zA-Z0-9_]*"
let ident_re = Str.regexp (sprintf "^%s$" name_re_str)
let forbidden_vars = StringSet.of_list ["true"; "false"]
let try_variable var =
if not (Str.string_match ident_re var 0) then
`InvalidIdentifier
else if StringSet.mem var forbidden_vars then
`ReservedIdentifier
else `ValidIdentifier (mangle var)
let bits_const_re = Str.regexp "^\\([0-9]+\\)'\\(b[01]*\\|h[0-9a-fA-F]*\\|[0-9]+\\)$"
let underscore_re = Str.regexp "_"
let leading_h_re = Str.regexp "^h"
let try_plain_int n =
int_of_string_opt n
let try_number' loc a =
let a = Str.global_replace underscore_re "" a in
if Str.string_match bits_const_re a 0 then
let size = Str.matched_group 1 a in
let size = try int_of_string size
with Failure _ ->
parse_error loc @@ BadBitsSize { size } in
let numstr = Str.matched_group 2 a in
let num = Z.of_string ("0" ^ (Str.replace_first leading_h_re "x" numstr)) in
let bits = if size = 0 && num = Z.zero then ""
else Z.format "%b" num in
let nbits = String.length bits in
if nbits > size then
parse_error loc @@ Overflow { numstr; bits; size }
else
let padding = list_const (size - nbits) false in
let char2bool = function '0' -> false | '1' -> true | _ -> assert false in
let bits = List.of_seq (String.to_seq bits) in
let bools = List.append (List.rev_map char2bool bits) padding in
Some (`Const (Array.of_list bools))
else match try_plain_int a with
| Some n -> Some (`Num n)
| None -> None
let try_number loc = function
| "true" -> Some (`Const [|true|])
| "false" -> Some (`Const [|false|])
| a -> try_number' loc a
let keyword_re = Str.regexp (sprintf "^:\\(%s\\)$" name_re_str)
let try_keyword nm =
if Str.string_match keyword_re nm 0 then Some (Str.matched_group 1 nm)
else None
let enumerator_re = Str.regexp (sprintf "^\\(\\|%s\\)::\\(%s\\)$" name_re_str name_re_str)
let try_enumerator nm =
if Str.string_match enumerator_re nm 0 then
Some { enum = Str.matched_group 1 nm; constructor = Str.matched_group 2 nm }
else None
let symbol_re = Str.regexp (sprintf "^'\\(%s\\)$" name_re_str)
let try_symbol nm =
if Str.string_match symbol_re nm 0 then Some (Str.matched_group 1 nm)
else None
let language_constructs =
[("fail", `Fail);
("setq", `Setq);
("progn", `Progn);
("let", `Let);
("if", `If);
("when", `When);
("write.0" , `Write P0);
("write.1", `Write P1);
("read.0" , `Read P0);
("read.1", `Read P1);
("switch", `Switch)]
|> StringMap.of_list
let type_names =
[("unit", Bits_u 0);
("bool", Bits_u 1);
("char", Bits_u 8);
("string", String_u)]
|> StringMap.of_list
let type_constructors =
[("bits", `Bits); ("array", `Array)]
let parse (sexps: Pos.t sexp list) =
let expect_atom expected = function
| List { loc; _ } ->
syntax_error loc @@ UnexpectedList { expected }
| Atom { loc; atom } ->
(loc, atom) in
let expect_list expected = function
| Atom { loc; atom } ->
syntax_error loc @@ UnexpectedAtom { atom; expected }
| List { loc; elements } ->
(loc, elements) in
let expect_nil prev = function
| [] -> ()
| List { loc; _ } :: _ -> syntax_error loc @@ ExpectingNil { prev; kind = "list" }
| Atom { loc; _ } :: _ -> syntax_error loc @@ ExpectingNil { prev; kind = "atom" } in
let expect_pair loc kind1 kind2 lst =
let x1, lst = expect_cons loc kind1 lst in
let x2, lst = expect_cons loc kind2 lst in
expect_nil (sprintf "%s and %s" kind1 kind2) lst;
(x1, x2) in
let expect_pairs kind2 f1 f2 xs =
Delay.map (function
| `Pair (x1, x2) -> (f1 x1, f2 x2)
| `Single x1 -> ignore (f1 x1); syntax_error (sexp_pos x1) @@ MissingPairElement { kind2 })
(gather_pairs xs) in
let expect_constant loc csts atom =
match List.assoc_opt atom csts with
| None -> syntax_error loc @@ BadChoice { atom; expected = List.map fst csts }
| Some x -> x in
let expect_constant_atom csts c =
let candidates = List.map fst csts in
let loc, s = expect_atom (one_of_str candidates) c in
loc, expect_constant loc csts s in
let expect_identifier kind v =
let loc, atom = expect_atom kind v in
match try_variable atom with
| `ValidIdentifier v -> locd_make loc v
| `ReservedIdentifier -> syntax_error loc @@ ReservedIdentifier { kind; atom }
| `InvalidIdentifier -> syntax_error loc @@ BadIdentifier { kind; atom } in
let try_bits loc v =
match try_number loc v with
| Some (`Const c) -> Some c
| _ -> None in
let expect_bits msg v =
let loc, atom = expect_atom msg v in
match try_number loc atom with
| Some (`Const c) -> loc, (atom, c)
| Some (`Num _) -> syntax_error loc @@ MissingSize { number = atom }
| _ -> syntax_error loc @@ BadBitsLiteral { atom } in
let expect_const msg v =
let loc, atom = expect_atom msg v in
(loc,
match try_bits loc atom with
| Some c -> UBits c
| None ->
match try_enumerator atom with
| Some { enum; constructor } -> UEnum { enum; constructor }
| None -> syntax_error loc @@ BadConst { atom }) in
let expect_keyword loc kind atom =
match try_keyword atom with
| Some k -> k
| None -> syntax_error loc @@ BadKeyword { kind; atom } in
let expect_enumerator loc atom =
match try_enumerator atom with
| Some ev -> ev
| None -> syntax_error loc @@ BadEnumerator { atom } in
let rec expect_type ?(bits_raw=false) = function (* (bit 16), (array 3 (bit 15)), 'typename *)
| Atom { loc; atom } ->
locd_make loc
(match StringMap.find_opt atom type_names with
| Some tau -> tau
| None ->
match try_symbol atom with
| Some s -> Unknown_u s
| None ->
match try_plain_int atom with
| Some n when bits_raw -> Bits_u n
| _ -> syntax_error loc @@ BadType { atom })
| List { loc; elements } ->
let expect_size_arg loc args =
let sz, args = expect_cons loc "size" args in
let loc, szstr = expect_atom "a size" sz in
match try_plain_int szstr with
| Some size -> size, args
| _ -> syntax_error loc @@ BadSizeInType { atom = szstr } in
let hd, args = expect_cons loc "type" elements in
let loc, kind = expect_constant_atom type_constructors hd in
locd_make loc
(match kind with
| `Bits -> let sz, args = expect_size_arg loc args in
expect_nil "argument" args;
Bits_u sz
| `Array -> let tau, args = expect_cons loc "element type" args in
let tau = expect_type ~bits_raw:false tau in
if args = [] then Array_u (tau, None)
else let sz, args = expect_size_arg loc args in
expect_nil "argument" args;
Array_u (tau, Some sz)) in
let try_type ?(bits_raw=false) sexp =
try Some (expect_type ~bits_raw sexp)
with Errors _ -> None in
let expect_literal loc a =
match try_enumerator a with
| Some { enum; constructor } -> Enumerator { enum; constructor }
| None ->
match try_keyword a with
| Some name -> Keyword name
| None ->
match try_number loc a with
| Some (`Num n) -> Num (a, n)
| Some (`Const bs) -> Const (UBits bs)
| None ->
match try_variable a with
| `ValidIdentifier var -> Var var
| `InvalidIdentifier -> String a (* FIXME use a parser that distinguishes strings and atoms *)
| `ReservedIdentifier ->
syntax_error loc @@ ReservedIdentifier { atom = a; kind = "a string or variable name" } in
let expect_funapp loc kind elements =
let hd, args = expect_cons loc kind elements in
let loc_hd, hd = expect_atom (sprintf "a %s name" kind) hd in
loc_hd, hd, args in
let rec expect_action_nodelay action =
match action with
| Atom { loc; atom } ->
locd_make loc
(match atom with (* FIXME disallow these var names *)
| "skip" -> Skip
| "fail" -> Fail (Bits_t 0)
| atom -> match try_type action with
| Some tau -> Lit (Type tau.lcnt)
| None -> Lit (expect_literal loc atom))
| List { loc; elements } ->
let loc_hd, hd, args = expect_funapp loc "constructor or function" (elements) in
locd_make loc
(match StringMap.find_opt hd language_constructs with
| Some fn ->
(match fn with
| `Fail ->
(match args with
| [] -> Fail (Bits_t 0)
| [arg] -> Lit (Fail (expect_type ~bits_raw:true arg).lcnt)
| _ -> type_error loc @@ BadArgumentCount { fn = "fail"; expected = 1; given = List.length args })
| `Setq ->
let var, body = expect_cons loc "variable name" args in
let value = expect_action (expect_single loc "value" "assignment" body) in
Assign (expect_identifier "a variable name" var, value)
| `Progn ->
Progn (List.map expect_action args)
| `Let ->
let bindings, body = expect_cons loc "let bindings" args in
let bindings = expect_let_bindings bindings in
let body = List.map expect_action body in
Let (bindings, body)
| `If ->
let cond, body = expect_cons loc "condition of conditional expression" args in
let tbranch, fbranches = expect_cons loc "true branch of conditional expression" body in
If (expect_action cond, expect_action tbranch,
List.map expect_action fbranches)
| `When ->
let cond, body = expect_cons loc "condition of conditional expression" args in
When (expect_action cond, List.map expect_action body)
| `Write port ->
let reg, body = expect_cons loc "register name" args in
Write (port, expect_identifier "a register name" reg,
expect_action (expect_single loc "value" "call to write" body))
| `Read port ->
let reg = expect_single loc "register name" "call to write" args in
Read (port, expect_identifier "a register name" reg)
| `Switch ->
let inspected, branches = expect_cons loc "switch operand" args in
let branches = List.map expect_switch_branch branches in
let inspected = expect_action inspected in
let binder = gensym "switch_operand" in
let operand = locd_make inspected.lpos (Lit (Var binder)) in
let switch = match build_switch_body branches with
| (Some default, branches) ->
Switch { operand; default; branches }
| None, [] -> syntax_error loc @@ EmptySwitch
| None, branches ->