File: l3-to-acl2.lisp

package info (click to toggle)
acl2 8.5dfsg-5
  • links: PTS
  • area: main
  • in suites: bookworm
  • size: 991,452 kB
  • sloc: lisp: 15,567,759; javascript: 22,820; cpp: 13,929; ansic: 12,092; perl: 7,150; java: 4,405; xml: 3,884; makefile: 3,507; sh: 3,187; ruby: 2,633; ml: 763; python: 746; yacc: 723; awk: 295; csh: 186; php: 171; lex: 154; tcl: 49; asm: 23; haskell: 17
file content (1298 lines) | stat: -rw-r--r-- 49,701 bytes parent folder | download | duplicates (4)
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
(in-package "ACL2")

; General comments:

; We translate objects of option type to nil, for NONE, and (SOME . x), for
; SOME x.  We considered translating (SOME . x) to x in the case that x cannot
; be nil, but we worried that case-matching would be awkward to work out in
; that case.

; We start by including l3, so that various functions are defined, e.g.: BITS
; and BL are defined, for use in term-stobj-out below; type-expr, for use in
; l3-trans-formal below.
(include-book "l3")

(defun trans-breaker ()
; To trace errors, e.g.:
; (set-iprint t)
; (trace$ (trans-breaker :entry (break$)))
  (declare (xargs :guard t))
  nil)

(defmacro trans-err (&rest args)
  `(prog2$ (trans-breaker) ; to break: (trace$ (trans-breaker :entry (break$)))
           (trans-er ,@args)))

(defstobj bindings
  str-to-const    ; alist mapping strings to constant symbols
  const-to-str    ; alist mapping constant symbols to strings
  sym-to-str      ; alist mapping symbols seen to strings they came from
  str-to-sym      ; inverse of sym-to-str
  st$-fields      ; alist binding each field to nil or (for array) dimension
  construct-alist ; alist mapping constructor types to field names
  bw              ; binding world, extended with some stobjs-out for defs
  )

(defun str-to-sym-alistp (x)
  (declare (xargs :guard (alistp x)))
  (cond ((atom x) (null x))
        (t (and (consp (car x))
                (stringp (caar x))
                (symbolp (cdar x))
                (not (assoc-equal (caar x) (cdr x)))
                (not (rassoc-eq (cdar x) (cdr x)))
                (str-to-sym-alistp (cdr x))))))

(defthm str-to-sym-alistp-forward-to-alistp
  (implies (str-to-sym-alistp x)
           (alistp x))
  :rule-classes :forward-chaining)

(defun invert-alist (x)
  (declare (xargs :guard (alistp x)))
  (cond ((endp x) nil)
        (t (acons (cdar x)
                  (caar x)
                  (invert-alist (cdr x))))))

(defconst *initial-bindings*
  '(("t" . T_VAR)
    ("nil" . NIL_VAR)))

(defun initialize-bindings (str-to-sym wrld ctx bindings)
  (declare (xargs :stobjs bindings))
  (let ((str-to-sym (append *initial-bindings* str-to-sym)))
    (cond
     ((not (and (alistp str-to-sym)
                (str-to-sym-alistp str-to-sym)))
      (trans-err ctx
                 "STR-TO-SYM should be, after extending by ~x0, an alist ~
                  mapping strings to symbols, without duplicate CARs or CDRs; ~
                  but that extension is:~|~x1"
                 *initial-bindings* str-to-sym))
     (t (let* ((bindings (update-str-to-const nil bindings))
               (bindings (update-const-to-str nil bindings))
               (bindings (update-str-to-sym str-to-sym bindings))
               (bindings (update-sym-to-str (invert-alist str-to-sym)
                                            bindings))
               (bindings (update-st$-fields nil bindings))
               (bindings (update-construct-alist nil bindings))
               (bindings (update-bw wrld bindings)))
          (trans-value nil))))))

(defun l3-reparse (x)

; Replaces subexpressions
;   qvar"s"
;   qvar"state"
; and for that matter, any
;   qvar x
; by just qvar.
; Also, replaces subexpressions
;   bvar"name"
; by
;   (var "name" bty)

  (cond ((atom x) x)
        ((atom (cdr x))
         (cons (l3-reparse (car x))
               (cdr x)))
        (t (case (car x)
             (bvar (cons (list 'var (cadr x) 'bty)
                         (l3-reparse (cddr x))))
             (nvar (cons (list 'var (cadr x) 'nty)
                         (l3-reparse (cddr x))))
             (ivar (cons (list 'var (cadr x) 'ity)
                         (l3-reparse (cddr x))))
             (qvar (cons 'qvar
                         (l3-reparse (cddr x))))
             (svar (cons (list 'var (cadr x) 'sty)
                         (l3-reparse (cddr x))))
             (uvar (cons (list 'var (cadr x) 'uty)
                         (l3-reparse (cddr x))))
             (vvar (cons (list 'var (cadr x) 'vty)
                         (l3-reparse (cddr x))))
             (cty  (cons (list 'cty (cadr x))
                         (l3-reparse (cddr x))))
             (otherwise (cons (l3-reparse (car x))
                              (l3-reparse (cdr x))))))))

; At this point we get lazy.  If we want to convert to guard-verified :logic
; mode then at a minimum, we'll need to define a "good-bindings-p" predicate
; that says that sym-to-str and str-to-sym are alists mapping symbols to/from
; strings.
(program)

(defun l3-trans-constant (s ctx bindings)

; S is expected to be a string (we cause an error if not).  We translate s to a
; constant symbol or nil.

  (declare (xargs :stobjs bindings))
  (cond
   ((not (stringp s))
    (trans-err ctx
               "String expected, but got:~|~x0"
               s))
   (t (trans-value (cdr (assoc-equal s (str-to-const bindings)))))))

(defun l3-trans-sym (s ctx bindings)

; S is expected to be a string (we cause an error if not).  We translate s to a
; symbol, using bindings and avoiding the returning of symbol ST$.

  (declare (xargs :stobjs bindings))
  (cond
   ((not (stringp s))
    (trans-err ctx
               "String expected, but got:~|~x0"
               s))
   ((string-equal s "ST$")
    (trans-err ctx
               "The name ST$ is reserved."))
   (t (let* ((str-to-sym (str-to-sym bindings))
             (pair (assoc-equal s str-to-sym)))
        (cond
         (pair (trans-value (cdr pair)))
         (t
          (let* ((sym (intern (string-upcase (if (position #\' s)
                                                 (substitute #\- #\' s)
                                               s))
                              "ACL2"))
                 (sym-to-str (sym-to-str bindings))
                 (pair (assoc-eq sym sym-to-str)))
            (cond
             ((null pair)
              (let* ((bindings (update-str-to-sym (acons s sym str-to-sym)
                                                  bindings))
                     (bindings (update-sym-to-str (acons sym s sym-to-str)
                                                  bindings)))
                (trans-value sym)))
             (t (trans-err ctx
                           "Strings ~x0 and ~x1 both map to symbol '~x2.  ~
                            Re-run with keyword argument :STR-TO-SYM set to ~
                            an alist that binds at least one of these strings ~
                            to some other symbol."
                           (cdr pair) s sym))))))))))

(defun l3-trans-type-expr (texpr ctx bindings)

; Texpr is a type-expression, which we want to translate to a corresponding
; symbolic type expression or a list of such.  For example, the type-expression

; (PTy (CTy "funcT")
;      (PTy (CTy "shiftT")
;           (PTy (FTy 7)
;                (PTy (FTy 7)
;                     (FTy 7)))))

; translates to the list:

; (funct shiftt (unsigned-byte 7) (unsigned-byte 7) (unsigned-byte 7))

; Also, for example, (lty (CTy "instruction")) translates to instruction-list.

  (declare (xargs :stobjs bindings))
  (case-match texpr
    (('cty s)
     (l3-trans-sym s ctx bindings))
    (('pty t1 t2)
     (trans-er-let*
      ((x1 (l3-trans-type-expr t1 ctx bindings))
       (x2 (l3-trans-type-expr t2 ctx bindings)))
      (trans-value (if (and (consp t2)
                            (eq (car t2) 'pty))
                       (cons x1 x2)
                     (list x1 x2)))))
    (('fty n)
     (cond ((posp n)
            (trans-value (list 'unsigned-byte n)))
           (t (trans-err ctx
                         "Unexpected type expression:~|~x0"
                         texpr))))
    (('lty t1)
     (trans-er-let*
      ((x1 (l3-trans-type-expr t1 ctx bindings)))
      (trans-value (type-list-name x1))))
    (('oty t1)
     (trans-er-let*
      ((x1 (l3-trans-type-expr t1 ctx bindings)))
      (trans-value (list 'oty x1))))
    (& (case texpr
         (F1 (trans-value (list 'unsigned-byte 1)))
         (F4 (trans-value (list 'unsigned-byte 4)))
         (F8 (trans-value (list 'unsigned-byte 8)))
         (F16 (trans-value (list 'unsigned-byte 16)))
         (F32 (trans-value (list 'unsigned-byte 32)))
         (F64 (trans-value (list 'unsigned-byte 64)))
         (bTy (trans-value 'bty))
         (sTy (trans-value 'sty))
         (uTy (trans-value 'uty))
         (qTy (trans-value 'qty))
         (otherwise
          (trans-err ctx
                     "Unexpected type expression:~|~x0"
                     texpr))))))

(defun l3-trans-construct-1 (cl ctx bindings)
  (declare (xargs :stobjs bindings))
  (case-match cl
    ((s '[])
     (l3-trans-sym s ctx bindings))
    ((s ('sqbkt texpr))
     (trans-er-let*
      ((sym (l3-trans-sym s ctx bindings))
       (texpr (l3-trans-type-expr texpr ctx bindings)))
      (trans-value (list sym texpr))))
    (& (trans-err ctx
                  "Illegal CONSTRUCT clause:~|~x0"
                  cl))))

(defun l3-trans-construct-rec (clauses ctx bindings)
  (declare (xargs :stobjs bindings))
  (cond ((endp clauses)
         (trans-value nil))
        (t (trans-er-let*
            ((cl (l3-trans-construct-1 (car clauses) ctx bindings))
             (x (l3-trans-construct-rec (cdr clauses) ctx bindings)))
            (trans-value (cons cl x))))))

(defun construct-constructors (clauses)

; Clauses is the second argument of a construct form.  We skip over atomic
; members of clauses, but for each cons (constructor-name types), we collect up
; constructor-name.

  (cond ((endp clauses) nil)
        ((atom (car clauses))
         (construct-constructors (cdr clauses)))
        (t (cons (caar clauses)
                 (construct-constructors (cdr clauses))))))

(defun l3-trans-construct (name clauses ctx bindings)
  (declare (xargs :stobjs bindings))
  (trans-er-let*
   ((sym (l3-trans-sym name ctx bindings))
    (x (l3-trans-construct-rec clauses ctx bindings)))
   (let* ((field-names (construct-constructors x))
          (old-construct-alist (construct-alist bindings))
          (bindings (if field-names
                        (update-construct-alist
                         (put-assoc-eq sym field-names old-construct-alist)
                         bindings)
                      bindings)))
     (trans-value (list 'construct sym x)))))

(defmacro extend-st$-fields (sym-expr val)

; Sym-expr is an expression whose value is a tuple (trans-value sym), where sym
; is the (translated) name of an st$ field.  Val is to be associated with that
; name in the st$-fields of bindings.

  `(trans-er-let*
    ((sym ,sym-expr))
    (let ((bindings
           (update-st$-fields (put-assoc-eq sym ,val (st$-fields bindings))
                              bindings)))
      (trans-value sym))))

(defun l3-trans-st$-field (field ctx bindings)
  (declare (xargs :stobjs bindings))
  (case-match field
    ((name ('CTy &))
     (extend-st$-fields (l3-trans-sym name ctx bindings) nil))
    ((name ('ATy t1 t2))
     (trans-er-let*
      ((x1 (l3-trans-type-expr t1 ctx bindings))
       (x2 (l3-trans-type-expr t2 ctx bindings)))
      (case-match x1
        (('unsigned-byte n)
         (let ((expt-2-n (expt 2 n)))
           (trans-er-let*
            ((sym (extend-st$-fields (l3-trans-sym name ctx bindings)
                                     expt-2-n)))
            (trans-value
             (case-match x2
               (('unsigned-byte &)
                `(,sym :type (array ,x2 (,expt-2-n)) :initially 0))
               (&
                `(,sym :type (array t (,expt-2-n)))))))))
        (& (trans-err ctx
                      "Illegal defstobj array field spec:~|~x0"
                      field)))))
    ((name t1)
     (trans-er-let*
      ((sym (extend-st$-fields (l3-trans-sym name ctx bindings) nil))
       (x1 (l3-trans-type-expr t1 ctx bindings)))
      (cond
       ((and (consp x1)
             (eq (car x1) 'unsigned-byte))
        (trans-value `(,sym :type ,x1 :initially 0)))
       ((symbolp x1)
        (trans-value `(,sym :type (satisfies ,(make-type x1)))))
       (t
        (trans-value `(,sym)))

; If x1 is an option type expression, or for that matter any other type
; expression not handled above, we simply make no constraints on the type of
; the field.  This seems harmless enough for now, though we may want to revisit
; it when we start doing proofs.

;       (trans-err ctx
;                  "Unexpected type for stobj field, ~x0"
;                  x1)
       )))
    (& (trans-err ctx
                  "Illegal defstobj field spec:~|~x0"
                  field))))

(defun l3-trans-st$-fields (fields ctx bindings)
  (declare (xargs :stobjs bindings))
  (cond ((endp fields) (trans-value nil))
        (t (trans-er-let*
            ((field (l3-trans-st$-field (car fields) ctx bindings))
             (fields (l3-trans-st$-fields (cdr fields) ctx bindings)))
            (trans-value (cons field fields))))))

(defun l3-trans-st$ (fields ctx bindings)
  (declare (xargs :stobjs bindings))
  (trans-er-let*
   ((tfields (l3-trans-st$-fields fields ctx bindings)))
   (trans-value `(defstobj+ st$ ,@tfields))))

(defun l3-trans-record (name fields ctx bindings)
; !! Needs to be written.
; Example:
; ("CauseRegister" (sqbkt ("'rst" (FTy 27)) ("ExcCode" (FTy 5))))
; so name is "CauseRegister"
  (declare (xargs :stobjs bindings)
           (ignore fields))
  (trans-err ctx
             "Sorry: l3-trans-record is not yet implemented (attempted to ~
              translate a record definition for name ~x0)."
             name))

(mutual-recursion

(defun l3-get-type-mop (mop expr ctx bindings)
; Keep in sync with l3-trans-map.
; !! Need to support fst and snd.
  (declare (xargs :stobjs bindings))
  (case-match mop
    ('length (trans-value '(integer 0 *)))
    ('bnot (l3-get-type expr ctx bindings))
    ('not (trans-value 'bty))
    (('cast x)
     (l3-trans-type-expr x ctx bindings))
    (& (trans-err ctx
                  "Unimplemented mop for l3-get-type-mop: ~x0"
                  mop))))

(defun l3-get-type (l3-expr ctx bindings)

; Warning: keep in sync with l3-trans-expr and expr-st$-out-p.

; Return the translated type of expr.

  (declare (xargs :stobjs bindings))
  (case-match l3-expr
    ('lu (trans-value 'uTy))
    ('qvar (trans-value 'qTy))
    ('lt (trans-value 'bty))
    ('lf (trans-value 'bty))
    (('lnl typ)
     (l3-trans-type-expr typ ctx bindings))
    (('llc ('sqbkt &) expr)
     (l3-get-type expr ctx bindings))
    (('ll ('sqbkt . exprs))
     (cond ((null exprs) ; maybe impossible?
            (trans-value 'null))
           (t (trans-er-let*
               ((x (l3-get-type (car exprs) ctx bindings)))
               (cond ((symbolp x)
                      (trans-value (type-list-name x)))
                     (t (trans-err ctx
                                   "~x0 doesn't know how to get the type for ~
                                     expression~|  ~x1~|because it doesn't ~
                                     know how to get the list type ~
                                     corresponding to the type ~x2."
                                   'l3-get-type
                                   l3-expr
                                   x)))))))
    (('var & typ)
     (l3-trans-type-expr typ ctx bindings))
    (('avar typ)
     (l3-trans-type-expr typ ctx bindings))
    (('lc & typ)
     (l3-trans-type-expr typ ctx bindings))
    (('const & typ)
     (l3-trans-type-expr typ ctx bindings))
    (('lw & n)
     (trans-value `(unsigned-byte ,n)))
    (('tp ('sqbkt . args))
     (l3-get-type-lst args ctx bindings))
    (('ite & expr1 expr2) ; same as for bop (except for error message)
     (trans-er-let*
      ((x1 (l3-get-type expr1 ctx bindings))
       (x2 (l3-get-type expr2 ctx bindings)))
      (cond ((equal x1 x2)
             (trans-value x1))
            (t (trans-er
                "Obtained different types for second and third arguments of ~
                 expression~|~x0~|, as follow.~|  True-branch: ~
                 ~x1~|  False-branch: ~x2"
                l3-expr x1 x2)))))
    (('bop & expr1 expr2) ; same as for ite (except for error message)
     (trans-er-let*
      ((x1 (l3-get-type expr1 ctx bindings))
       (x2 (l3-get-type expr2 ctx bindings)))
      (cond ((equal x1 x2)
             (trans-value x1))
            (t (trans-er
                "Obtained different types for second and third arguments of ~
                 expression~|~x0~|, as follow.~|  True-branch: ~
                 ~x1~|  False-branch: ~x2"
                l3-expr x1 x2)))))
    (('mop mop expr1)
     (l3-get-type-mop mop expr1 ctx bindings))
    (('apply ('const & ('ATy 'qTy t1)) 'qvar)
     (l3-trans-type-expr t1 ctx bindings))
    (('apply ('call & ('ATy 'qTy typ) &) &)
     (l3-trans-type-expr typ ctx bindings))
    (('apply ('dest & ('ATy & typ) 'qvar) &)
; !! Need to consider records other than the state record.
     (l3-trans-type-expr typ ctx bindings))
    (('dest & typ 'qvar)
; !! Need to consider records other than the state record.
     (l3-trans-type-expr typ ctx bindings))
    (('rupd & &)
; !! Need to consider records other than the state record.
     (trans-value 'qTy))
    (('call & typ &)
     (l3-trans-type-expr typ ctx bindings))
    (('cs & ('sqbkt (& x) . &))
; We could check that all clauses give the same type, but that seems
; unnecessary at this point.
     (l3-get-type x ctx bindings))
    (('let & & x)
     (l3-get-type x ctx bindings))
    (('cc ('sqbkt . lst))
     (trans-er-let*
      ((n (l3-trans-width-lst lst ctx bindings)))
      (trans-value `(unsigned-byte ,n))))
    (('bl n x)
     (trans-er-let*
      ((typ (l3-get-type x ctx bindings)))
      (let ((expected `(unsigned-byte ,n)))
        (cond ((equal typ expected)
               (trans-value typ))
              (t (trans-err ctx
                            "Unexpected type, ~x0, for ~x1 (expected type to ~
                             be ~x2), in expression:~|~x3."
                           typ x expected l3-expr))))))
    (('ex & & & typ)
     (l3-trans-type-expr typ ctx bindings))
    (('eq & &)
     (trans-value `bty))
    (& (trans-err ctx
                  "Unrecognized type expression: ~x0"
                  l3-expr))))

(defun l3-get-type-lst (lst ctx bindings)
  (declare (xargs :stobjs bindings))
  (cond ((endp lst) (trans-value nil))
        (t (trans-er-let*
            ((x1 (l3-get-type (car lst) ctx bindings))
             (x2 (l3-get-type-lst (cdr lst) ctx bindings)))
            (trans-value (cons x1 x2))))))

(defun l3-trans-width (x ctx bindings)
  (declare (xargs :stobjs bindings))
  (trans-er-let*
   ((typ (l3-get-type x ctx bindings)))
   (case-match typ
     (('unsigned-byte n)
      (trans-value n))
     (& (trans-err ctx
                   "Unable to compute bit width for ~x0."
                   x)))))

(defun l3-trans-width-lst (lst ctx bindings)
  (declare (xargs :stobjs bindings))
  (cond ((endp lst) (trans-value 0))
        (t (trans-er-let*
            ((n1 (l3-trans-width (car lst) ctx bindings))
             (n2 (l3-trans-width-lst (cdr lst) ctx bindings)))
            (trans-value (+ n1 n2))))))
)

(defun l3-trans-st$-field-name (field array-p update-p ctx bindings)
  (declare (xargs :stobjs bindings))
  (trans-er-let*
   ((sym (l3-trans-sym field ctx bindings)))
   (let ((pair (assoc-eq sym (st$-fields bindings)))
         (key2 (if array-p :array :non-array)))
     (cond ((null pair)
            (trans-err ctx
                       "Implementation error: Failed to find symbol ~x0 (from ~
                        field ~x1) in ~x2"
                       sym field '(st$-fields bindings)))
           ((not (iff (cdr pair) array-p))
            (trans-err ctx
                       "Implementation error: symbol ~x0 (from field ~x1) is ~
                        associated in ~x2 with ~x3, but array-p is ~x4 in ~
                        call:~|~x5"
                       sym field '(st$-fields bindings) (cdr pair) array-p
                       `(l3-trans-st$-field-name
                         ,field ,array-p ,update-p ,ctx bindings)))
           ((eq update-p 'map)
            (trans-value (stobj-mapper-name
                          (defstobj-fnname sym :updater key2 nil)
                          nil)))
           (update-p (trans-value (defstobj-fnname sym :updater key2 nil)))
           (t (trans-value (defstobj-fnname sym :accessor key2 nil)))))))

(defun type-st$-out-p (x n)

; This function is called at the top level with n = nil.

; X is an untranslated type.  We return a non-nil result iff x represents
; state.  If x is a pair type, then in that case we return n+k where state is
; the kth component of the resulting tuple; otherwise we return t.

  (case-match x
    ('qty (or n t))
    (('pty 'qty &) n)
    (('pty & x2)
     (type-st$-out-p x2
                     (if (null n) 1 (1+ n))))
    (& nil)))

(mutual-recursion

(defun expr-st$-out-p (x)

; Warning: keep in sync with l3-get-type and l3-trans-expr.

; X is an untranslated expression.  We return t if x represents a value of st$,
; n if x represents an mv for which component number n (0-based) represents a
; value of st$, and nil otherwise.

  (case-match x
    ('lu nil)
    ('qvar t)
    ('lt nil)
    ('lf nil)
    (('lnl &) nil)
    (('llc . &) nil)
    (('ll . &) nil)
    (('var & &) nil)
    (('avar typ)
     (not (eq typ 'qty)))
    (('lc & &) nil)
    (('const & &) nil)
    (('lw & &) nil)
    (('tp ('sqbkt . args))
     (expr-st$-out-listp args 0))
    (('ite & tbr fbr)
     (let ((x1 (expr-st$-out-p tbr))
           (x2 (expr-st$-out-p fbr)))
       (cond ((not (eq x1 x2))
              (cond ((iff x1 x2)
                     (er hard 'expr-st$-out-p
                         "Surprise!  Expr-st$-out-p returns different state ~
                          results, ~x0 and ~x1, for the two branches of an ~
                          ITE expression:~|~x2"
                         x1 x2 x))
                    (t
                     (er hard 'expr-st$-out-p
                         "Surprise!  The following ITE expression seems to ~
                          return state in its ~s0 branch but not in its ~s1 ~
                          branch:~|~x2"
                         (if x1 "true" "false")
                         (if x1 "false" "true")
                         x))))
             (t (or x1 x2)))))
    (('bop . &) nil)
    (('mop . &) nil)
    (('apply ('const & ('ATy 'qTy t1)) 'qvar)
     (type-st$-out-p t1 nil))
    (('apply ('call & ('ATy & t1) &) &)
     (type-st$-out-p t1 nil))
    (('apply ('dest & & &) &) nil)
    (('dest & & &) nil)
    (('rupd & &) t)
; !! Need to consider records other than the state record.
    (('call & t1 &)
     (type-st$-out-p t1 nil))
    (('cs & ('sqbkt (& x1) . &))
     (expr-st$-out-p x1))
    (('let ('tp ('sqbkt . lst)) & &)
     (expr-st$-out-listp lst 0))
    (('let var & &)
     (eq var 'st$))
    (('cc ('sqbkt . &)) nil)
    (('bl & &) nil)
    (('ex & & & &) nil)
    (('ln &) nil)
    (('eq & &) nil)
    (& (er hard 'expr-st$-out-p
           "Unrecognized expression: ~x0"
           x))))

(defun expr-st$-out-listp (x n)
  (cond ((endp x) nil)
        ((expr-st$-out-p (car x)) n)
        (t (expr-st$-out-listp (cdr x) (1+ n)))))
)

(defun lift-st$-lst (args lst)

; !! Consider redoing to better match lift-st$, or maybe even eliminate this
; function if possible and only use lift-st$.

; Keep in sync with lift-st$.

; Args is a list of l3 expressions and lst is the corresponding list of
; translations.  We return (mv let-sym bs new-lst), where let-sym is nil, t,
; let, or let*; bs is a list of bindings for let or let* (according to
; let-sym); and new-lst is the result of replacing an element e of lst that
; represents st$, by st$ itself.  The intention is that either let-sym is nil
; or t, in which case lst is returned unchanged, or else e is equal to (let-sym
; bs st).  Also, lst contains an expression representing st$ if and only if
; let-sym is non-nil.

  (let ((posn (expr-st$-out-listp args 0)))
    (cond
     (posn (let ((st$-expr (nth posn lst)))
             (case-match st$-expr
               ('st$ (mv t nil lst))
               (('let (('st$ e)) body) ; assume single-threadedness:
                (mv 'let
                    `((st$ ,e))
                    (update-nth posn body lst)))
               (('let* bindings body) ; assume single-threadedness:
                (cond ((equal (remove-duplicates-eq (strip-cars bindings))
                              '(st$))
                       (mv 'let*
                           bindings
                           (update-nth posn body lst)))
                      (t (mv 'let
                             `((st$ ,st$-expr))
                             (update-nth posn 'st$ lst)))))
               (&
                (mv 'let
                    `((st$ ,st$-expr))
                    (update-nth posn 'st$ lst))))))
     (t (mv nil nil lst)))))

(defun lift-st$ (call posn)

; Keep in sync with lift-st$-lst.

; Call is (fn arg1 ... argk) and posn is the position (0-based in the call,
; 1-based in (arg1 ... argk)) of the st$ argument, which might be an expression
; other than 'st$ itself.  We rewrite call to make ACL2's single-threadedness
; check happy.

  (let ((st$-expr (nth posn call)))
    (case-match st$-expr
      ('st$ call)
      (('let (('st$ e)) body) ; assume single-threadedness:
       `(let* ((st$ ,e)
               (st$ ,body))
          ,(update-nth posn 'st$ call)))
      (('let* bindings body) ; assume single-threadedness:
       (cond ((equal (remove-duplicates-eq (strip-cars bindings))
                     '(st$))
              `(let* ,(append bindings `((st$ ,body)))
                 ,(update-nth posn 'st$ call)))
             (t `(let ((st$ ,st$-expr))
                   ,(update-nth posn 'st$ call)))))
      (&
       `(let ((st$ ,st$-expr))
          ,(update-nth posn 'st$ call))))))

(mutual-recursion

(defun l3-trans-expr (patp l3-expr ctx bindings)

; Warning: keep in sync with l3-get-type and expr-st$-out-p.

  (declare (xargs :stobjs bindings))
  (case-match l3-expr
    ('lu (trans-value '(unit-value)))
    ('qvar (trans-value 'st$))
    ('lt (trans-value '(true)))
    ('lf (trans-value '(false)))
    (('lnl &) (trans-value (if patp ''nil nil)))
    (('llc ('sqbkt expr1) expr2)
     (trans-er-let*
      ((x1 (l3-trans-expr patp expr1 ctx bindings))
       (x2 (l3-trans-expr patp expr2 ctx bindings)))
      (trans-value (if patp
                       `(,x1 . ,x2)
                     `(cons ,x1 ,x2)))))
    (('ll ('sqbkt . lst))
     (trans-er-let*
      ((x (l3-trans-expr-lst patp lst ctx bindings)))
      (trans-value (cond (patp ; (not (equal patp nil))
                          lst)
                         (t (cons 'list x))))))
    (('var s &)
     (l3-trans-sym s ctx bindings))
    (('avar &)
; Anthony expects that user code will only generate avar terms within case
; expressions.
     (trans-value '&))
    (('lc s &) ; enumerated type constant
     (trans-er-let*
      ((sym (l3-trans-sym s ctx bindings)))
      (trans-value (kwote sym))))
    (('const s &) ; non-enumerated type constant, or a def0-defined constant
     (trans-er-let*
      ((const (l3-trans-constant s ctx bindings)))
      (cond (const ; def0 case
             (trans-value const))
            (t (trans-er-let*
                ((sym (l3-trans-sym s ctx bindings)))
                (trans-value (kwote sym)))))))
    (('lw i &)
     (trans-value i))
    (('tp ('sqbkt . args))
     (trans-er-let*
      ((lst (l3-trans-expr-lst patp args ctx bindings)))
      (trans-value
       (cond
        (patp lst)
        (t (mv-let (let-sym bs new-lst)
                   (lift-st$-lst args lst)
                   (cond ((null let-sym) (cons 'tuple lst))
                         ((eq let-sym t) (cons 'mv lst))
                         (t `(,let-sym ,bs (mv ,@new-lst))))))))))
    (('ite . args)
     (trans-er-let*
      ((lst (l3-trans-expr-lst nil args ctx bindings)))
      (trans-value (cons 'if lst))))
    (('bop sym expr1 expr2)
; !! Should really cause an error here, and in many other analogous places, if
; patp is true, because we don't have a way of matching a function call (other
; than cons).
     (l3-trans-bop sym expr1 expr2 ctx bindings))
    (('mop mop expr)
     (l3-trans-mop mop expr ctx bindings))
    (('apply ('const s &) 'qvar)
     (trans-er-let*
      ((sym (l3-trans-sym s ctx bindings)))
      (trans-value `(,sym st$))))
    (('apply ('call s typ expr1) expr2)
     (trans-er-let*
      ((sym (l3-trans-sym s ctx bindings))
       (x1 (l3-trans-expr patp expr1 ctx bindings))
       (x2 (l3-trans-expr patp expr2 ctx bindings)))
      (cond ((eq sym 'raise-exception)
             (case-match typ
               (('ATy 'qTy ('PTy typ1 'qTy))
                (trans-er-let*
                 ((t1 (l3-trans-type-expr typ1 ctx bindings)))
                 (trans-value
                  (let ((type-term `(arb ,t1)))
                    (cond
                     ((eq x2 'st$)
                      `(,sym ,x1 ,type-term ,x2))
                     (t `(let ((st$ ,x2))
                           (,sym ,x1 ,type-term st$))))))))
               (& (trans-err ctx
                             "Unexpected type encountered for raise-exception ~
                              term:~|~x0"
                             l3-expr))))
            ((eq x2 'st$)
             (trans-value `(,sym ,x1 ,x2)))
            (t (trans-value `(let ((st$ ,x2))
                               (,sym ,x1 st$)))))))
    (('apply ('dest field & 'qvar) expr)
; !! Need to consider records other than the state record.
     (trans-er-let*
      ((sym (l3-trans-st$-field-name field t nil ctx bindings))
       (x (l3-trans-expr patp expr ctx bindings)))
      (trans-value `(,sym ,x st$))))
    (('dest field & 'qvar)
; !! Need to consider records other than the state record.
     (trans-er-let*
      ((sym (l3-trans-st$-field-name field nil nil ctx bindings)))
      (trans-value `(,sym st$))))
    (('rupd field ('tp ('sqbkt st$-expr
                               ('mop ('k1 &) init-expr))))

; !! Need to consider records other than the state record.  To elaborate:

; The above needs to be modified for the case that we are updating other than
; the state.  Probably we can just test, using expr-st$-out-p, whether st$-expr
; is a state expression.  If true, then use what's below.  If false, then think
; about what to use for the non-state case of record update.

; E.g.:
; (RUPD "R" (TP (SQBKT QVAR (MOP (K1 (FTY 7)) (LW 0 32)))))
; translates to:
; (map-update-r 0 st$)

     (trans-er-let*
      ((sym (l3-trans-st$-field-name field t 'map ctx bindings))
       (trans-st$-expr (l3-trans-expr patp st$-expr ctx bindings))
       (trans-init-expr (l3-trans-expr patp init-expr ctx bindings)))
      (trans-value
       (lift-st$ `(,sym ,trans-init-expr ,trans-st$-expr) 2))))
    (('rupd field ('tp ('sqbkt st$-expr
                               ('Fupd ('Dest field & 'qVar)
                                      index
                                      value))))
; !! Need to consider records other than the state record.
; See comment for first rupd case above.
     (trans-er-let*
      ((sym (l3-trans-st$-field-name field t t ctx bindings))
       (trans-st$-expr (l3-trans-expr patp st$-expr ctx bindings))
       (i (l3-trans-expr patp index ctx bindings))
       (v (l3-trans-expr patp value ctx bindings)))
      (trans-value (lift-st$ `(,sym ,i ,v ,trans-st$-expr) 3))))
    (('rupd field ('tp ('sqbkt st$-expr expr)))
; !! Need to consider records other than the state record.
; See comment for first rupd case above.
     (trans-er-let*
      ((sym (l3-trans-st$-field-name field nil t ctx bindings))
       (trans-st$-expr (l3-trans-expr patp st$-expr ctx bindings))
       (v (l3-trans-expr patp expr ctx bindings)))
      (trans-value (lift-st$ `(,sym ,v ,trans-st$-expr) 2))))
    (('call s typ expr)
     (trans-er-let*
      ((sym (l3-trans-sym s ctx bindings))
       (x (l3-trans-expr patp expr ctx bindings))
       (trans-typ (l3-trans-type-expr typ ctx bindings)))
      (case-match typ
        (('cty &)
         (cond ((member-eq sym
                           (cdr (assoc-eq trans-typ
                                          (construct-alist bindings))))
                (trans-value
                 (cond (patp `(',sym ,x))
                       (t `(call-constructor ,trans-typ ,sym ,x)))))
               (patp (trans-err ctx
                                "We do not know how to translate this ~
                                 function call into a case-match+ ~
                                 pattern:~|~x0"
                                l3-expr))
               (t (trans-value `(,sym ,x)))))
        (& (cond (patp (trans-err ctx
                                  "We do not know how to translate this ~
                                   function call into a case-match+ ~
                                   pattern:~|~x0"
                                  l3-expr))
                 (t (trans-value `(,sym ,x))))))))
    (('cs case-expr ('sqbkt . clauses))
     (trans-er-let*
      ((typ (l3-get-type l3-expr ctx bindings))
       (x (l3-trans-expr patp case-expr ctx bindings))
       (c (l3-trans-cs-clauses clauses typ ctx bindings)))
      (trans-value `(case-match+ ,x ,@c))))
    (('let expr0 expr1 expr2)
     (trans-er-let*
      ((x1 (l3-trans-expr patp expr1 ctx bindings))
       (x2 (l3-trans-expr patp expr2 ctx bindings)))
      (let ((stobjs-out (term-stobjs-out x1 nil (bw bindings))))
        (case-match expr0
          (('tp ('sqbkt . lst))
           (let ((let-macro (cond
                             ((and (consp x1)
                                   (eq (car x1) 'bl))
                              'mv-let-ignorable)
                             ((or (and (consp stobjs-out)
                                       (consp (cdr stobjs-out)))
                                  (member-eq 'qvar lst))
                              'mv-let)
                             (t 'slet))))
             (trans-er-let*
              ((x0 (l3-trans-expr-lst nil lst ctx bindings)))
              (trans-value `(,let-macro ,x0 ,x1 ,x2)))))
          (& (cond ((and (consp stobjs-out)
                         (consp (cdr stobjs-out)))
                    (trans-err ctx
                               "Attempted to bind non-tuple expression,~|  ~
                                ~x0,~|to expression generating stobjs-out = ~
                                ~x1:~|~x2"
                               expr0 stobjs-out expr1))
                   (t (trans-er-let*
                       ((x0 (l3-trans-expr patp expr0 ctx bindings)))
                       (trans-value
                        (case-match x2
                          (('let (b) body)
                           `(let* ((,x0 ,x1) ,b) ,body))
                          (('let* bs body)
                           `(let* ((,x0 ,x1) ,@bs) ,body))
                          (& `(let ((,x0 ,x1)) ,x2))))))))))))
    (('cc ('sqbkt . lst))
     (trans-er-let*
      ((x (l3-trans-cc-exprs lst ctx bindings)))
      (trans-value (cons 'cat x))))
    (('bl n expr)
     (cond ((natp n)
            (trans-er-let*
             ((x (l3-trans-expr patp expr ctx bindings)))
             (trans-value `(bl ,n ,x))))
           (t (trans-err ctx
                         "The first argument of BL is expected to be a natp, ~
                          which is not the case for:~|~x0."
                         l3-expr))))
    (('ex expr0 expr1 expr2 &)
     (trans-er-let*
      ((x0 (l3-trans-expr patp expr0 ctx bindings))
       (x1 (l3-trans-expr patp expr1 ctx bindings))
       (x2 (l3-trans-expr patp expr2 ctx bindings)))
      (trans-value `(bits ,x0 ,x1 ,x2))))
    (('ln n) ; only for l3-trans-expr (not for l3-get-type)
     (trans-value n))
    (('eq expr1 expr2)
     (trans-er-let*
      ((x1 (l3-trans-expr patp expr1 ctx bindings))
       (x2 (l3-trans-expr patp expr2 ctx bindings)))
      (let ((eq-sym (cond ((or (case-match x1
                                 (('quote sym)
                                  (symbolp sym)))
                               (case-match x2
                                 (('quote sym)
                                  (symbolp sym))))
                           'eq)
                          ((or (acl2-numberp x1)
                               (acl2-numberp x2))
                           'eql)
                          (t 'equal))))
        (trans-value `(,eq-sym ,x1 ,x2)))))
    (& (trans-err ctx
                  "Unrecognized expression: ~x0"
                  l3-expr))))

(defun l3-trans-cs-clauses (clauses typ ctx bindings)

; Note that typ is a translated type.

  (declare (xargs :stobjs bindings))
  (trans-er-let*
   ((trans-clauses (l3-trans-cs-clauses-rec clauses ctx bindings)))
   (let* ((cl (car (last trans-clauses)))
          (tst (car cl)))
     (cond ((eq tst '&)
            (trans-value trans-clauses))
           (t (trans-value
               (append trans-clauses
                       `((& (impossible

; Here we assume that if the processor state st$ is returned, then it is
; returned as st$ or (mv .. st$).

                             ,(case-match typ
                                ('qty 'st$)
                                ((t0 'qty)
                                 `(mv (arb ,t0) st$))
                                (&
                                 `(arb ,typ)))))))))))))

(defun l3-trans-cs-clauses-rec (clauses ctx bindings)
  (declare (xargs :stobjs bindings))
  (cond ((endp clauses)
         (trans-value nil))
        (t (trans-er-let*
            ((tst (l3-trans-expr t (caar clauses) ctx bindings))
             (val (l3-trans-expr nil (cadar clauses) ctx bindings))
             (rst (l3-trans-cs-clauses-rec (cdr clauses) ctx bindings)))
            (trans-value (cons (list tst val) rst))))))

(defun l3-trans-bop (bop expr1 expr2 ctx bindings)
  (declare (xargs :stobjs bindings))
  (trans-er-let*
   ((type (l3-get-type expr1 ctx bindings))
    (texpr1 (l3-trans-expr nil expr1 ctx bindings))
    (texpr2 (l3-trans-expr nil expr2 ctx bindings)))
   (case bop
     ((Add Sub Mul Bit)
      (case-match type
        (('unsigned-byte n) ; ((quote unsigned-byte) n)
         (let ((fn (cdr (or (assoc-eq bop
                                      '((add . n+)
                                        (sub . n-)
                                        (mul . n*)
                                        (bit . logbitp)))
                            (er hard ctx
                                "Implementation error for bop: ~x0"
                                bop)))))
           (trans-value `(,fn ,n ,texpr1 ,texpr2))))
        (& (trans-err ctx
                      "Illegal ~x0 type:~|~x1"
                      type))))
     ((BAnd BOr BXor
            Lt Gt Le Ge
            And Or)
      (let ((fn (cdr (or (assoc-eq bop
                                   '((band . logand)
                                     (bor . logior)
                                     (bxor . logxor)
                                     (lt . <)
                                     (gt . >)
                                     (le . <=)
                                     (ge . >=)
                                     (and . and)
                                     (or . or)))
                         (er hard ctx
                             "Implementation error for bop: ~x0"
                             bop)))))
        (trans-value `(,fn ,texpr1 ,texpr2))))
     (Ror (trans-value `(ash ,texpr1
                             ,(cond ((natp texpr2)
                                     (- texpr2))
                                    (t (list '- texpr2))))))
     (otherwise (trans-err ctx
                           "Unimplemented bop: ~x0"
                           bop)))))

(defun l3-trans-mop (mop expr ctx bindings)
; Keep in sync with l3-get-type-mop.
  (declare (xargs :stobjs bindings))
  (trans-er-let*
   ((from-type (l3-get-type expr ctx bindings))
    (trans-expr (l3-trans-expr nil expr ctx bindings)))
   (case-match mop
     ('length (trans-value (list 'len trans-expr)))
     ('bnot (trans-value (list 'lognot trans-expr)))
     ('not (trans-value (list 'not trans-expr)))
     ('fst (trans-value (list 'car trans-expr)))
     ('snd (trans-er-let*
            ((t1 (l3-get-type trans-expr ctx bindings)))
            (trans-value (list (case-match t1
                                 ((& &)
; second arg of input pair isn't itself a pair
                                  'cadr)
                                 (& 'cdr))
                               trans-expr))))
     (('cast to-type)
      (trans-er-let*
       ((to-type (l3-trans-type-expr to-type ctx bindings)))
       (trans-value `(cast (,from-type ,to-type) ,trans-expr))))
     (& (trans-err ctx
                   "Unimplemented mop for l3-trans-mop: ~x0"
                   mop)))))

(defun l3-trans-cc-exprs (lst ctx bindings)
  (declare (xargs :stobjs bindings))
  (cond ((endp lst) (trans-value nil))
        (t (trans-er-let*
            ((x (l3-trans-expr nil (car lst) ctx bindings))
             (n (l3-trans-width (car lst) ctx bindings))
             (rest (l3-trans-cc-exprs (cdr lst) ctx bindings)))
            (trans-value (list* x n rest))))))

(defun l3-trans-expr-lst (patp lst ctx bindings)
  (declare (xargs :stobjs bindings))
  (cond ((endp lst) (trans-value nil))
        (t (trans-er-let*
            ((x1 (l3-trans-expr patp (car lst) ctx bindings))
             (x2 (l3-trans-expr-lst patp (cdr lst) ctx bindings)))
            (trans-value (cons x1 x2))))))
)

(mutual-recursion

(defun l3-trans-formal (formal ctx bindings)
  (declare (xargs :stobjs bindings))
  (case-match formal
    (('tp ('sqbkt . formals))
     (l3-trans-formal-lst formals ctx bindings))
    (('var name typ)
     (trans-er-let*
      ((x (l3-trans-sym name ctx bindings))
       (typ (l3-trans-type-expr typ ctx bindings)))
      (trans-value (list x (type-expr x typ)))))
    ('qvar (trans-value 'st$))
    (& (trans-err ctx
                  "Unexpected formal passed to l3-trans-formal:~|~x0"
                  formal))))

(defun l3-trans-formal-lst (formals ctx bindings)
  (declare (xargs :stobjs bindings))
  (cond ((endp formals) (trans-value nil))
        (t (trans-er-let*
            ((x (l3-trans-formal (car formals) ctx bindings))
             (lst (l3-trans-formal-lst (cdr formals) ctx bindings)))
            (trans-value (cons x lst))))))
)

(defun l3-trans-def (name formal expr closep measure-expr ctx bindings)
  (declare (xargs :stobjs bindings))
  (trans-er-let*
   ((sym (l3-trans-sym name ctx bindings))
    (trans-formal (l3-trans-formal formal ctx bindings))
    (trans-expr (l3-trans-expr nil expr ctx bindings))
    (trans-measure-expr ; only used if measure-expr is non-nil
     (if (null measure-expr)
         (trans-value nil) ; arbitrary
       (l3-trans-expr nil measure-expr ctx bindings))))
   (trans-value `(defun-struct ,sym
                   ,(if closep
                        `(,trans-formal st$)
                      `(,trans-formal))
                   ,@(and measure-expr
                          `(:measure ,trans-measure-expr))
                   ,@(and (or closep (eq 'st$ trans-formal))
                          '((declare (xargs :stobjs st$))))
                   ,trans-expr))))

(defun sym-to-const (str)
  (intern
   (concatenate 'string "*" (string-upcase str) "*")
   "ACL2"))

(defun l3-trans-def0 (name expr ctx bindings)
  (declare (xargs :stobjs bindings))
  (let ((sym (sym-to-const name)))
    (cond ((assoc-eq sym (const-to-str bindings))
           (trans-err ctx
                      "Apparently def0 was applied to two strings with the ~
                       same upper-case, ~x0 and ~x1.  A workaround is not yet ~
                       implemented, but probably could be."
                      (cdr (assoc-eq sym (const-to-str bindings)))
                      name))
          (t (let* ((bindings (update-str-to-const
                               (acons name sym (str-to-const bindings))
                               bindings))
                    (bindings (update-const-to-str
                               (acons sym name (const-to-str bindings))
                               bindings)))
               (trans-er-let*
                ((trans-expr (l3-trans-expr nil expr ctx bindings)))
                (trans-value `(defconst ,sym
                                ,trans-expr))))))))

(defmacro chk-pass1p (when-pass1-p-true form)
  (declare (xargs :guard (booleanp when-pass1-p-true)))
  `(cond ((eq pass1p ,when-pass1-p-true) ,form)
         (t (trans-value nil))))

(defun main-state-name-p (name)
  (equal name "state"))

(defun l3-to-acl2-fn3 (pass1p input-list ctx bindings state acc)
  (declare (xargs :stobjs (bindings state)))
  (cond
   ((endp input-list)
    (mv nil
        (if pass1p acc (reverse acc))
        bindings
        state))
   (t
    (let ((x (l3-reparse (car input-list))))
      (mv-let
       (erp form bindings)
       (case-match x
         (('val '_ '= 'Construct
                ('sqbkt (name ('sqbkt . clauses))))
          (chk-pass1p t (l3-trans-construct name clauses ctx bindings)))
         (('val '_ '= 'Record
                (name ('sqbkt . fields)))
          (chk-pass1p t
                      (if (main-state-name-p name)
                          (l3-trans-st$ fields ctx bindings)
                        (l3-trans-record name fields ctx bindings))))
         (('Def "raise'exception" . &)
          (chk-pass1p
           t
           (trans-value
            '(value-triple
              "See l3.lisp for the definition of raise-exception"))))
         (('def name formal
                ('Close 'qVar expr))
          (chk-pass1p nil
                      (l3-trans-def name formal expr t nil ctx bindings)))
         (('def name formal expr)
          (chk-pass1p nil
                      (l3-trans-def name formal expr nil nil ctx bindings)))
         (('tdef name formal
                 ('close 'qvar expr)
                 ('close ('var var-name &)
                         ('cs ('var var-name &)
                              ('sqbkt (('tp ('sqbkt . &))
                                       measure-expr)))))
          (declare (ignore var-name))
          (chk-pass1p nil
                      (l3-trans-def name formal expr t measure-expr ctx
                                    bindings)))
         (('def0 name expr)
          (chk-pass1p nil
                      (l3-trans-def0 name expr ctx bindings)))
         (& (trans-err ctx
                       "Unexpected input form:~|~x0"
                       x)))
       (cond
        (erp (mv-let
              (erp2 val2 state)
              (er soft erp
                  "Translation problem for ~x0: ~@1"
                  x form)
              (declare (ignore erp2 val2))
              (l3-to-acl2-fn3 pass1p (cdr input-list) ctx bindings state
                              (cons `(value-triple (list :error ,x))
                                    acc))))
        (t (l3-to-acl2-fn3 pass1p (cdr input-list) ctx bindings state
                           (if form
                               (cons form acc)
                             acc)))))))))

(defun l3-to-acl2-fn2 (input-list str-to-sym logic-p ctx bindings state)
  (declare (xargs :stobjs (bindings state)))
  (mv-let
   (erp initial-acc bindings)
   (initialize-bindings str-to-sym (w state) ctx bindings)
   (cond
    (erp (mv-let (erp2 val2 state)
                 (cmp-to-error-triple (mv erp initial-acc))
                 (declare (ignore erp2 val2))
                 (mv erp initial-acc bindings state)))
    (t (mv-let
        (erp1 new-acc bindings state)
        (l3-to-acl2-fn3 t input-list ctx bindings state initial-acc)
        (mv-let
         (erp2 result bindings state)
         (l3-to-acl2-fn3 nil input-list ctx bindings state
                         (cond ((null logic-p)
                                (cons '(program) new-acc))
                               ((eq logic-p :logic-only) ; not guard-verified
                                (cons '(set-verify-guards-eagerness 0)
                                      new-acc))
                               (t new-acc)))
         (mv (or erp1 erp2) result bindings state)))))))

(defun l3-to-acl2-fn1 (input-list str-to-sym logic-p ctx state)
  (declare (xargs :stobjs state))
  (with-local-stobj
   bindings
   (mv-let
    (erp val bindings state)
    (l3-to-acl2-fn2 input-list str-to-sym logic-p ctx bindings state)
    (mv erp val state))))

(include-book "misc/file-io" :dir :system)

(defconst *l3-book-path*
  "projects/translators/l3-to-acl2/translator/l3")

(defun l3-to-acl2-fn (infile outfile logic-p str-to-sym form state)
  (declare (xargs :stobjs state))
  (er-let* ((ctx (value 'l3-to-acl2)) ; (ctx (mv nil 'l3-to-acl2 state))
            (input-list (read-file infile state))
            (output-list (l3-to-acl2-fn1 input-list str-to-sym logic-p ctx
                                         state)))
    (write-list (list* '(in-package "ACL2")
                       `(value-triple '(:generated-by ,form))
                       `(include-book ,*l3-book-path* :dir :system)
                       output-list)
                outfile
                ctx
                state)))

(defmacro l3-to-acl2 (&whole form infile outfile &key logic str-to-sym)
  `(l3-to-acl2-fn ,infile ,outfile ,logic ,str-to-sym ',form state))