Coverage report: /home/ellis/comp/core/lib/syn/gen/c/ast.lisp
Kind | Covered | All | % |
expression | 47 | 712 | 6.6 |
branch | 3 | 104 | 2.9 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; ast.lisp --- SYN/GEN/C AST Nodes
6
(in-package :syn/gen/c)
8
(defnode function-definition () (item parameter body))
9
(defmethod ast ((self function-definition))
11
;; params are before the result type (item) in the AST
12
(slot-value self 'item)
13
(slot-value self 'parameter)
14
(slot-value self 'body)))
15
(defnode parameter-list () (parameters))
16
(defmethod ast ((self parameter-list))
17
(ast (slot-value self 'parameters)))
19
(defnode struct-definition (id) (members))
21
(defnode union-definition (id) (members))
23
(defnode enum-definition (id) (members))
24
(defmethod ast ((self enum-definition)) (list (id self) (slot-value self 'members)))
25
;; variable declaration
26
(defnode declaration-list () (braces bindings body))
27
(defmethod ast ((self declaration-list))
28
(list (slot-value self 'bindings)
29
(slot-value self 'body)))
30
(defnode declaration-item () (specifier type identifier value))
31
(defmethod ast ((self declaration-item))
32
(list (slot-value self 'type)
33
(slot-value self 'specifier)
34
(slot-value self 'identifier)
35
(slot-value self 'value)))
36
(defnode declaration-value () (value))
37
(defmethod ast ((self declaration-value))
38
(list (slot-value self 'value)))
39
(defmethod val ((self declaration-value))
40
(slot-value self 'value))
41
;; essential bulding blocks
42
(defnode clist () (items))
43
(defnode array-reference () (array indizes))
44
(defnode object-reference () (object component))
45
(defnode pointer-reference () (pointer component))
46
(defnode c-type () (type))
47
(defmethod ast ((self c-type)) (list (slot-value self 'type)))
48
(defnode float-type () (number))
49
(defnode specifier () (specifier))
50
(defmethod ast ((self specifier)) (list (slot-value self 'specifier)))
51
(defnode function-pointer () (identifier parameters))
54
;; = *= /= %= += -= <<= >>= &= ^= \|=
55
(defexpr assignment-expression () (op variable value))
56
(defmethod ast ((self assignment-expression))
57
(list (slot-value self 'variable) (slot-value self 'value)))
58
;; + - / * > < == != += -= >= <= \| \|\| & &= && % << >> or and
59
(defexpr infix-expression () (op members))
60
(defmethod ast ((self infix-expression))
61
(slot-value self 'members))
63
(defexpr prefix-expression () (op object))
65
(defexpr postfix-expression () (op object))
66
;; not ('!' defined as prefix)
67
(defexpr not-expression () (value))
69
(defexpr conditional-expression () (test then else))
71
(defexpr cast-expression () (type object))
74
;; goto, continue, break return
75
(defstmt jump-statement () (kind members))
76
(defmethod ast ((self jump-statement))
77
(ast (slot-value self 'members)))
78
(defstmt label-statement () (name))
79
(defstmt expression-statement () (force-semicolon expression))
80
(defstmt compound-statement () (braces statements))
81
(defstmt if-statement () (test if-body else-body))
84
(defstmt for-statement () (init test step body))
85
(defstmt while-statement () (test body))
86
(defstmt do-statement () (body test))
89
(defstmt comment () (chars comment linebreak))
92
(defstmt switch-case-statement () (switch cases))
93
(defnode switch-case-item () (constant body))
96
(defexpr attribute-expression () (arguments))
99
(defstmt typedef () (declaration))
101
(defnode include () (file))
102
(defmethod ast ((self include)) (list (slot-value self 'file)))
104
(defnode preprocessor-macro () (name function body))
107
(defmacro c-syntax (tags lambda-list &body body)
108
`(defsyntax ,tags (:c) ,lambda-list ,@body))
110
(defmacro make-exprs (list)
112
;; :prepend (make-instance 'expression-statement)
115
(defmacro make-block (list)
116
"Code block with curly braces and indentation."
117
`(make-instance 'compound-statement
119
:statements (make-exprs ,list)))
121
(defmacro make-simple-block (list)
122
"Code block without underlying AST.
123
Used for 'bodys' where implicit progn is required"
124
`(make-instance 'compound-statement
126
:statements (make-instance 'expression-statement
128
:expression (quoty ,list))))
130
(c-syntax block (&body body)
131
"Code block with curly braces and indentation."
134
(c-syntax progn (&body body)
135
"Code block without curly braces nor intendation"
136
;; make expressions with ';' delimiter
139
(c-syntax set (&rest rest)
140
"Assigment operator for multiple inputs"
141
(when (oddp (length rest))
142
(error "Set operator with odd number of elements: ~a" rest))
143
(if (eql (length rest) 2)
145
`(make-instance 'assignment-expression
147
:variable (make-node ,(pop rest))
148
:value (make-node ,(pop rest)))
149
;; muliple assignments
151
;; collect item pairwise and emmit sigle assignments
152
,(loop while rest collect
153
`(make-instance 'assignment-expression
155
:variable (make-node ,(pop rest))
156
:value (make-node ,(pop rest)))))))
158
(c-syntax (= *= /= %= += -= <<= >>= &= ^= \|=) (variable value)
159
"Assignment operators for single inputs"
160
`(make-instance 'assignment-expression :op ',syn/gen::tag :var (make-node ,variable) :val (make-node ,value)))
162
(c-syntax (/ > < == != >= <= \| \|\| % << >> or and ^ &&) (&rest rest)
163
"Infix expressions for multiple inputs"
164
`(make-instance 'infix-expression :op ',syn/gen::tag :members (make-nodes ,rest)))
166
(c-syntax (- + * &) (&rest rest)
167
"Infix or prefix version"
168
(if (eql (length rest) 1)
169
`(make-instance 'prefix-expression :op ',syn/gen::tag :object (make-node ,@rest))
170
`(make-instance 'infix-expression :op ',syn/gen::tag :members (make-nodes ,rest))))
172
(c-syntax (~ !) (item)
174
`(make-instance 'prefix-expression :op ',syn/gen::tag :object (make-node ,item)))
176
(c-syntax (addr) (item)
177
"Address-of function (&)"
178
`(make-instance 'prefix-expression :op '& :object (make-node ,item)))
180
(c-syntax (deref) (item)
181
"Taget-of or dereferencing pointer"
182
`(make-instance 'prefix-expression :op '* :object (make-node ,item)))
184
(c-syntax prefix++ (item)
186
`(make-instance 'prefix-expression :op '++ :object (make-node ,item)))
188
(c-syntax prefix-- (item)
190
`(make-instance 'prefix-expression :op '-- :object (make-node ,item)))
192
(c-syntax postfix-- (item)
193
"Postfix operator --"
194
`(make-instance 'postfix-expression :op '-- :object (make-node ,item)))
196
(c-syntax postfix++ (item)
197
"Postfix operator ++"
198
`(make-instance 'postfix-expression :op '++ :object (make-node ,item)))
200
(c-syntax postfix* (item)
202
`(make-instance 'postfix-expression :op '* :object (make-node ,item)))
204
(c-syntax struct (name &body body)
206
`(make-instance 'struct-definition
208
:id (make-node ,name)
212
`(make-instance 'compound-statement
215
:statements (make-nodes ,body)))))
217
(c-syntax union (name &body body)
219
`(make-instance 'union-definition
221
:id (make-node ,name)
223
:members (make-instance 'compound-statement
225
:statements (make-nodes ,body))))
227
(c-syntax enum (name &rest enum-list)
229
(setf enum-list (mapcar #'(lambda (x)
234
`(make-instance 'enum-definition
238
(make-nodes ,enum-list :prepend decompose-enum)))
240
(c-syntax (aref array) (array &rest indizes &environment env)
243
(setf indizes '(nil)))
244
;; make array referende
245
`(make-instance 'array-reference
246
;; check if identifier / type / macro
249
;; check if macro/function or list
250
(let ((first (first array)))
251
(if (and (not (listp first)) (std:fboundp! first env))
252
;; type is macro or function
254
;; type is list with type information
255
`(make-declaration-node (,@array nil))))
256
;; type is single symbol
259
(make-nodes ,indizes)))
261
(c-syntax oref (&rest rest)
263
(let* ((items (reverse rest))
264
(last-item (pop items))
265
(butlast-item (pop items))
266
(oref `(make-instance 'object-reference
267
:component (make-node ,butlast-item)
268
:object (make-node ,last-item))))
269
(loop for item in items do
270
(setf oref `(make-instance 'object-reference
271
:component (make-node ,item)
275
(c-syntax pref (pointer component)
277
`(make-instance 'pointer-reference :pointer (make-node ,pointer) :component (make-node ,component)))
279
(c-syntax type (type)
281
`(make-instance 'c-type :type (make-node ,type)))
283
(c-syntax specifier (specifier)
284
"Type specifier/qualifier"
285
`(make-instance 'specifier :specifier (make-node ,specifier)))
287
(c-syntax include (file)
288
"Include for c files"
289
`(make-instance 'include :file (quoty ,file)))
291
(c-syntax comment (comment &key (prefix nil) (linebreak t))
292
"Comment with default ('//') or user defined delimiter."
293
`(make-instance 'comment
294
:chars (quoty ,(if prefix prefix "//"))
295
:comment (quoty ,comment)
296
:linebreak ,linebreak))
298
(defun decompose-declaration (item)
299
"Decompose declaration item into its SPECIFIERS, TYPE, NAME and INITIALIZER"
300
(if (< 2 (length item))
301
;; decompose arg list with init
302
(let ((specifier (butlast item 3))
303
(type+id+val (last item 3)))
304
(let ((type (second type+id+val))
305
(id (first type+id+val))
306
(init (third type+id+val)))
307
(values specifier type id init)))
308
;; decompose arg list without init
309
(let ((specifier (butlast item 2))
310
(type+id (last item 2)))
311
(let ((type (second type+id))
312
(id (first type+id)))
313
(values specifier type id nil)))))
315
(defmacro make-declaration-node (item)
316
"Decompose declaration item and instantiate nodes"
317
(if (eql item '&rest)
319
(multiple-value-bind (specifier type id init) (decompose-declaration item)
320
`(make-instance 'declaration-item
324
`(make-instance 'specifier
325
:specifier (make-nodes ,specifier)))
326
:type (make-instance 'c-type :type (make-node ,type))
327
:identifier (make-node ,id)
329
`(make-instance 'declaration-value
330
:value (make-node ,init))
333
(defmacro decompose-type (item)
334
"Decompose type like declaration but without name"
335
`(make-declaration-node (,@item nil)))
337
(defmacro decompose-enum (item)
338
"Decompose enum like declaration but without type"
339
`(make-instance 'declaration-item
345
:identifier (make-node ,(first item))
347
:value ,(when (second item)
348
`(make-instance 'declaration-value
349
:value (make-node ,(second item))))))
351
(c-syntax decl (bindings &body body)
353
`(make-instance 'declaration-list
354
;; braces t, adjusted later by traverser
356
;; make single declarations/bindings
359
,(remove nil bindings) :prepend make-declaration-node)
362
;; make single expression statements
363
`(make-exprs ,body))))
365
(c-syntax function (name type parameters &body body &environment env)
366
"Define a C function"
367
`(make-instance 'function-definition
368
;; function name + type
371
;; check if macro/function or list
372
(let ((first (first type)))
373
(if (and (not (listp first)) (std:fboundp! first env))
374
;; type is macro or function
375
`(make-declaration-node (,type ,name))
376
;; type is list with type information
377
`(make-declaration-node (,@type ,name))))
378
;; type is single symbol
379
`(make-declaration-node (,name ,type)))
381
(make-instance 'parameter-list
383
(make-nodes ,parameters :prepend make-declaration-node))
386
`(make-block ,body))))
388
(c-syntax fpointer (name &optional parameters)
389
"Define a function pointer"
390
`(make-instance 'function-pointer
391
;; function pointer identifier
392
:identifier (make-node ,name)
393
;; function pointer parameters
394
:parameters (make-instance 'parameter-list
395
:parameters (make-nodes ,parameters :prepend make-declaration-node))))
397
(c-syntax for (init &body body)
399
`(make-instance 'for-statement
400
;; check if initialization present
404
`(make-declaration-node ,(first init)))
406
(make-node ,(second init))
408
(make-node ,(third init))
412
(c-syntax if (test if-body &optional else-body)
413
"The c if expression"
414
`(make-instance 'if-statement
416
:test (make-node ,test)
419
:if-body (make-simple-block ,(when if-body if-body))
420
;; if else and present
421
:else-body ,(when else-body
422
`(make-simple-block ,else-body))))
424
(c-syntax ? (test then else)
425
"The conditinal expression 'test ? then : else'"
426
`(make-instance 'conditional-expression
427
:test (make-node ,test)
428
:then (make-node ,then)
429
:else (make-node ,else)))
431
(defmacro make-switch-case-item (item)
432
"switch case item helper"
433
`(make-instance 'switch-case-item
435
;; list of trigger values
436
,(if (eql (first item) t)
437
;; identify default case
440
`(make-nodes ,(if (listp (first item))
445
(make-exprs ,(rest item))))
447
(c-syntax switch (expression &rest cases)
449
`(make-instance 'switch-case-statement
452
(make-node ,expression)
454
(make-instance 'compound-statement
456
:statements (make-nodes ,cases :prepend make-switch-case-item))))
458
(c-syntax while (test &body body)
460
`(make-instance 'while-statement
466
(c-syntax do-while (test &body body)
467
"The c do-whiel loop"
468
`(make-instance 'do-statement
474
(c-syntax typedef (&rest rest)
475
"Typedef for c types"
476
`(make-instance 'typedef
478
(make-declaration-node ,rest)))
480
(c-syntax cast (&rest rest)
482
`(make-instance 'cast-expression
484
(decompose-type ,(butlast rest))
486
(make-node ,(first (last rest)))))
488
(c-syntax sizeof (&rest type)
490
`(make-instance 'function-call
494
(decompose-type ,type)))
496
(c-syntax float-type (item)
497
"Generate 'f' suffixes"
498
`(make-instance 'float-type :number (make-node ,item)))
500
(c-syntax (goto continue break return) (&optional item)
501
"Jump statements with optional item"
502
`(make-instance 'jump-statement
504
(make-node ,syn/gen::tag)
506
,(when item `(make-node ,item))))
508
(c-syntax (label) (name)
510
`(make-instance 'label-statement :name (make-node ,name)))
514
`(make-instance 'not-expression :value (make-node ,item)))
516
(c-syntax clist (&rest rest)
518
`(make-instance 'clist :items (make-nodes ,rest)))
520
(c-syntax funcall (function &rest args)
522
`(make-instance 'function-call
523
:function (make-node ,function)
524
:arguments (make-nodes ,args)))
526
(c-syntax attribute (&rest args)
527
"GCC attribute extension"
528
`(make-instance 'attribute-expression
529
:arguments (make-nodes ,args)))
531
(build-context-switches
532
:package :syn/gen/c/sym
533
:symbols *c-symbols*)
536
:package :syn/gen/c/sym
537
:swap-package :syn/gen/c/swap
541
;;; A traverser which checks the identifier for c-conformity
542
;;; and automatically solves naming problems.
544
((used-names :initform (make-hash-table :test 'equal))
545
(name-map :initform (make-hash-table :test 'equal))))
546
(defgeneric check-and-get-name (renamer check-name))
548
;;; Check if identifier is OK.
549
;;; Store in hash table and correct if necessary.
550
(defmethod check-and-get-name ((item renamer) check-name)
551
(with-slots (used-names name-map) item
552
(if (eql check-name '|...|)
555
;; treat hyphen and underscore equally / map hyphen to underscore
556
(let* ((name-string (symbol-name check-name))
557
(identifier (substitute #\_ #\- name-string)))
558
(when (and (not (equal identifier name-string))
559
(find :hyphen *gen-warnings*))
560
(warn "Possible ambiguity through hyphen override of ~s" check-name))
561
(let ((alr-checked (gethash identifier name-map)))
564
(labels ((check-char (x) (alpha-char-p x))
565
(check-underscore (x) (eql #\_ x))
566
(check-tilde (x) (eql #\~ x))
567
(check-num (x) (digit-char-p x))
568
(check-hex (x) (and (eql #\0 (first x))
569
(or (eql #\x (second x))
570
(eql #\X (second x)))))
577
(not (check-all x))))
578
(let* ((identifier-l (concatenate 'list identifier))
579
(changed-l (if (check-tilde (car identifier-l))
582
(substitute-if #\_ #'check-nall (rest identifier-l)))
583
(substitute-if #\_ #'check-nall identifier-l)))
584
(changed (concatenate 'string changed-l)))
586
(when (and (check-num (first changed-l))
587
(not (check-hex changed-l)))
588
(setf (first changed-l) #\_)
589
(setf changed (concatenate 'string changed-l)))
591
(loop while (gethash changed used-names) do
592
(setf changed (format nil "_~a" changed)))
593
(setf (gethash changed used-names) t)
594
(setf changed (intern changed))
595
(setf (gethash identifier name-map) changed)
598
;;; Traverses the tree but checks only the identifier nodes.
599
(defmethod traverse ((rn renamer) (item ident) level)
600
(declare (ignore level))
602
(check-and-get-name rn (val item))))
604
;;; This Traverser checks whether braces really are necessary.
605
(defclass decl-blocker ()
606
((names :initform `(,(make-hash-table)))
607
(delta-names :initform '(nil))
608
(in-decl :initform '(nil))
609
(in-decl-item :initform '(nil))
610
(make-block :initform '(nil))))
612
(defmethod traverse ((db decl-blocker) (item ident) level)
613
"find names, check if in decl-item, save infos on stack in decl-blocker"
614
(declare (ignore level))
615
(with-slots (val) item
616
(with-slots (names delta-names in-decl-item make-block) db
617
(when (first in-decl-item)
618
(if (gethash val (first names))
619
(setf (first make-block) t)
620
(progn (push val (first delta-names))
621
(setf (gethash val (first names)) t)))))))
623
(defmethod traverse :before ((db decl-blocker) (item declaration-list) level)
624
"prepare empty lists and a nil-value for further traversing"
625
(declare (ignore level))
626
(with-slots (delta-names make-block in-decl in-decl-item) db
627
(push nil delta-names)
628
(push nil make-block)
630
(push nil in-decl-item)))
632
(defmethod traverse :after ((db decl-blocker) (item declaration-list) level)
633
"check values in decl-blocker and set braces to 'true' or 'nil'"
634
(declare (ignore level))
635
(with-slots (names delta-names make-block in-decl in-decl-item) db
636
(if (first make-block)
638
(setf (slot-value item 'braces) t)
639
(loop for i in (first delta-names) do
640
(setf (gethash i (first names)) nil)))
641
(if (> (list-length delta-names) 1)
643
(setf (slot-value item 'braces) nil)
644
(loop for i in (first delta-names) do
645
(push i (second delta-names))))))
651
(defmacro prepare-blocker-stacks (node-class)
652
"create method which prepares decl-blocker stacks"
653
`(defmethod traverse :before ((db decl-blocker) (item ,node-class) level)
654
"prepare empty decl-blocker stacks and values"
655
(declare (ignore level))
656
(with-slots (names) db
657
(push (make-hash-table) names))))
659
(defmacro clean-blocker-stacks (node-class)
660
"creates method which cleans decl-blocker stacks"
661
`(defmethod traverse :after ((db decl-blocker) (item ,node-class) level)
662
"clean up decl-blocker stack and values"
663
(declare (ignore level))
664
(with-slots (names) db
667
(defmacro decl-blocker-extra-nodes (&rest nodes)
668
`(progn .,(loop for i in nodes collect
669
`(progn (eval (prepare-blocker-stacks ,i))
670
(eval (clean-blocker-stacks ,i))))))
672
(decl-blocker-extra-nodes function-definition struct-definition for-statement compound-statement)
674
;;; This traverser hides "{}" in ifs where possible
675
(defclass if-blocker ()
676
((parent-node :initform '())
677
(statement-count :initform '(0))
678
(first-statement :initform '(nil))
679
(self-else :initform '(nil))
680
(child-else :initform '(nil))
681
(force-braces :initform '(nil))
682
(curr-level :initform '())))
684
(defmethod traverse :before ((ib if-blocker) (item compound-statement) level)
685
"prepare stacks, count statements"
686
(with-slots (parent-node statement-count first-statement force-braces curr-level) ib
687
(with-slots (statements) item
688
(push level curr-level)
689
(push t first-statement)
690
(push 'compound-statement parent-node)
691
(push nil force-braces)
692
(push 0 statement-count))))
694
(defmethod traverse :after ((ib if-blocker) (item compound-statement) level)
695
"decide wheter to print braces or not"
696
(with-slots (parent-node statement-count first-statement
697
self-else child-else force-braces curr-level)
699
(with-slots (statement braces) item
703
(cond ((eql (first parent-node) 'if-body)
705
(cond ((and (< (first statement-count) 2)
706
(not (first self-else)))
708
((and (< (first statement-count) 2)
713
((eql (first parent-node) 'else-body)
714
(if (< (first statement-count) 2)
717
(if (first force-braces)
719
(pop statement-count)
720
(pop first-statement)
721
(pop force-braces))))
723
(defmethod traverse :after ((ib if-blocker) (item comment) level)
724
"force braces if comments are present / important for solitary comments"
725
(declare (ignore level))
726
(with-slots (force-braces) ib
727
(setf (first force-braces) t)))
729
(defmethod traverse :before ((ib if-blocker) (item declaration-list) level)
730
"set force-braces (to t) if declartion-list found"
731
(declare (ignore level))
732
(with-slots (force-braces) ib
734
(setf (first force-braces) t))))
736
(defmethod traverse :before ((ib if-blocker) (item ast) level)
737
"check nodelists that belong to a compound-statement"
738
(with-slots (statement-count first-statement parent-node curr-level) ib
739
(with-slots (ast) item
740
(when (and (first first-statement)
741
(eql (first parent-node) 'compound-statement)
742
(eql (- level (first curr-level)) 2))
744
(let ((count (length ast)))
745
(setf (first first-statement) nil)
746
(setf (first statement-count)
747
(max count (first statement-count))))))))
750
(defmethod traverse :after ((ib if-blocker) (item expression-statement) level)
751
"place semicolon at empty branches"
752
(with-slots (statement-count curr-level force-braces) ib
753
(with-slots (force-semicolon expression) item
755
;; subnode that can contain no further statements
756
(typep expression 'ast)
757
;; do nothing if a comment is present, see above (if-blocker comment)
758
(not (eql (first force-braces) t))
759
;; specific position in ast, 1st expr-statement in body.
760
(and (first curr-level) ;; curr-level must be set
761
(eql (- level (first curr-level)) 1))
762
;; subtree has no expressions
763
;; this is a :after method, statement-cound already filled
764
(eql (first statement-count) 0))
765
(setf force-semicolon t)))))
767
;;; This traveser removes ambiguous nested compound-statements in else-if
768
;;; to reduce indentation.
769
(defclass else-if-traverser ()())
771
;;; Remove nested ast (progn (progn (progn ...)))
772
;;; Required for proper placement of curly braces (esp. for if-else)
773
(defclass nested-ast-remover () ())
774
(defmethod traverse :after ((nar nested-ast-remover) (item ast) level)
775
(with-slots (ast) item
776
(when (and (eql (length ast) 1)
777
(typep (first ast) 'expression-statement)
778
(typep (slot-value (first ast) 'expression) 'ast))
779
(setf ast (slot-value (slot-value (first ast) 'expression) 'ast)))))