aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2022-06-29 14:30:35 +0200
committerHugo Hörnquist <hugo@lysator.liu.se>2022-07-07 21:17:22 +0200
commit561c7a3ea6c5153cde6be6d1792cda5af1202881 (patch)
tree406d99680a1d4ec2f532434a2fa3e1c4a4511490
parentAdd split-by-one-of. (diff)
downloadcalp-561c7a3ea6c5153cde6be6d1792cda5af1202881.tar.gz
calp-561c7a3ea6c5153cde6be6d1792cda5af1202881.tar.xz
Fix C order of operations.
-rw-r--r--module/c/parse.scm148
-rw-r--r--tests/test/c-parse.scm69
-rw-r--r--tests/test/cpp.scm23
3 files changed, 174 insertions, 66 deletions
diff --git a/module/c/parse.scm b/module/c/parse.scm
index d407f5ac..d923e5b1 100644
--- a/module/c/parse.scm
+++ b/module/c/parse.scm
@@ -1,6 +1,7 @@
(define-module (c parse)
:use-module (hnh util)
:use-module (srfi srfi-1)
+ :use-module (srfi srfi-71)
:use-module (ice-9 match)
:export (parse-lexeme-tree))
@@ -138,15 +139,21 @@
`(,(parse-lexeme-tree op)
,(parse-lexeme-tree arg))]
+ ;; resolved-operator and ternary are the return "types"
+ ;; of resolve-order-of-operations
+ [(('resolved-operator op) args ...)
+ `(,op ,@(map parse-lexeme-tree args))]
+
+ [('ternary a b c)
+ `(ternary ,(parse-lexeme-tree a)
+ ,(parse-lexeme-tree b)
+ ,(parse-lexeme-tree c))]
+
[('infix args ...)
- (let ((r
- (resolve-order-of-operations
- (flatten-infix (cons 'infix args)))))
- (match r
- (`(: (? ,op ,true)
- ,false)
- `(ternary ,op ,true ,false))
- (_ r)))]
+ (let ((r (resolve-order-of-operations
+ (flatten-infix (cons 'infix args)))))
+ (parse-lexeme-tree r))]
+
[('funcall function ('group arguments))
`(funcall ,(parse-lexeme-tree function)
@@ -220,70 +227,95 @@
(define order-of-operations
(reverse
- (concatenate
- ;; This is only for binary operations
- `((-> ,(symbol #\.))
- ;; All unary procedures go here, incnluding typecasts, and sizeof
- (* / %)
- (+ -)
- (<< >>)
- (< <= > >=)
- (== != not_eq)
- (& bitand)
- (^ xorg)
- (,(symbol #\|) bitor)
- (&& and)
- (,(symbol #\| #\|) or)
- (? :)
- (= += -= *= /= %= <<= >>= &= ^= ,(symbol #\| #\=)
- and_eq or_eq xor_eq)
- (,(symbol #\,))
- ))))
-
-(define (mark-other form)
- (if (list? form) (cons '*other* form) form))
+ ;; This is only for binary operations
+ `((-> ,(symbol #\.))
+ ;; All unary procedures go here, incnluding typecasts, and sizeof
+ (* / %)
+ (+ -)
+ (<< >>)
+ (< <= > >=)
+ (== != not_eq)
+ (& bitand)
+ (^ xorg)
+ (,(symbol #\|) bitor)
+ (&& and)
+ (,(symbol #\| #\|) or)
+ (? :)
+ (= += -= *= /= %= <<= >>= &= ^= ,(symbol #\| #\=)
+ and_eq or_eq xor_eq)
+ (,(symbol #\,))
+ )))
+
+;; a.b->c.d (. (-> (. a b) c) d)
+;; 2 * 3 / 4 * 5 => (* (/ (* 2 3) 4) 5)
+;; eller => (* 2 (/ 3 4) 5)
(define* (resolve-order-of-operations
tree optional: (order order-of-operations))
- (format (current-error-port) "~s~%" tree)
(if (null? order)
(scm-error 'c-parse-error
"resolve-order-of-operations"
"Out of operations to resolve when resolving expression ~s"
(list tree) #f)
(match tree
- [('*other* body ...) body]
- [(form) (resolve-order-of-operations form order)]
- [(forms ...)
- (match (split-by forms (car order))
- [(group) (resolve-order-of-operations group (cdr order))]
- [groups
- (cons (car order)
- (map (lambda (form) (resolve-order-of-operations
- form order-of-operations))
- groups))])]
- [a a])))
+ [('fixed-infix form) form]
+ [('fixed-infix forms ...)
+ (match (split-by-one-of forms (car order))
+ [(group)
+ (resolve-order-of-operations (cons 'fixed-infix group)
+ (cdr order))]
+ [(a ('? b ...) (': c ...))
+ `(ternary ,(resolve-order-of-operations (cons 'fixed-infix a) (cdr order))
+ ,(resolve-order-of-operations (cons 'fixed-infix b) (cdr order))
+ ,(resolve-order-of-operations (cons 'fixed-infix c) (cdr order)))]
+ [(first rest ...)
+ (if (apply eq? (map car rest))
+ (let ((op (caar rest)))
+ `((resolved-operator ,op)
+ ,@(map (lambda (x) (resolve-order-of-operations (cons 'fixed-infix x)
+ (cdr order)))
+ (cons first (map cdr rest)))))
+ (fold (lambda (item done)
+ (let ((operator args (car+cdr item)))
+ `((resolved-operator ,operator)
+ ,done ,(resolve-order-of-operations
+ (cons 'fixed-infix args)
+ (cdr order)))))
+ (resolve-order-of-operations (cons 'fixed-infix first)
+ (cdr order))
+ rest))])])))
+
+;; 1 * 2 / 3 * 4
+;; ⇒ ((1) (* 2) (/ 3) (* 4))
+;; (1)
+;; (* (1) 2)
+;; (/ (* (1) 2) 3)
+;; (* (/ (* (1) 2) 3) 4)
;; Flatens a tree of infix triples. Stops when it should.
;; (parenthesis, function calls, ...)
(define (flatten-infix form)
- (match form
- [('infix left op ('infix right ...))
- (cons* (parse-lexeme-tree left)
- (parse-lexeme-tree op)
- (flatten-infix (cons 'infix right)))]
-
- [('infix left op right)
- (list (mark-other (parse-lexeme-tree left))
- (parse-lexeme-tree op)
- (mark-other (parse-lexeme-tree right)))]
-
- [other (scm-error 'c-parse-error
- "flatten-infix"
- "Not an infix tree ~a"
- (list other)
- #f)]))
+ (cons 'fixed-infix
+ (let loop ((form form))
+ (match form
+ [('infix left op ('infix right ...))
+ (cons* left
+ (parse-lexeme-tree op)
+ (loop (cons 'infix right)))]
+
+ [('infix left op right)
+ (list left
+ (parse-lexeme-tree op)
+ right)]
+
+ [('infix form) form]
+
+ [other (scm-error 'c-parse-error
+ "flatten-infix"
+ "Not an infix tree ~a"
+ (list other)
+ #f)]))))
diff --git a/tests/test/c-parse.scm b/tests/test/c-parse.scm
new file mode 100644
index 00000000..c16958de
--- /dev/null
+++ b/tests/test/c-parse.scm
@@ -0,0 +1,69 @@
+;;; Commentary
+;; Test implementation details of C parser
+;; TODO Should be ran before (test cpp)
+;;; Code
+
+(define-module (test cpp)
+ :use-module (srfi srfi-64)
+ :use-module (srfi srfi-88)
+ :use-module ((c lex) :select (lex))
+ :use-module (c parse))
+
+(define flatten-infix (@@ (c parse) flatten-infix))
+(define resolve-order-of-operations (@@ (c parse) resolve-order-of-operations))
+
+(test-group "Flatten infix"
+ (test-equal "Simple binary operator"
+ '(fixed-infix (integer (base-10 "1"))
+ +
+ (integer (base-10 "2")))
+ (flatten-infix (lex "1 + 2")))
+
+ (test-equal "Simple binary operator, with compound structure in on branch"
+ '(fixed-infix (integer (base-10 "1"))
+ +
+ (funcall (variable "f")
+ (group (integer (base-10 "2")))))
+ (flatten-infix (lex "1 + f(2)"))))
+
+(test-group "Order of operations"
+ (test-equal "Basic binary operator"
+ '((resolved-operator +)
+ (integer (base-10 "1"))
+ (integer (base-10 "2")))
+ (resolve-order-of-operations (flatten-infix (lex "1 + 2"))))
+
+ (test-equal "Multiple operators, with non-left-associative application"
+ '((resolved-operator +)
+ (integer (base-10 "1"))
+ ((resolved-operator *)
+ (integer (base-10 "2"))
+ (integer (base-10 "3"))))
+ (resolve-order-of-operations (flatten-infix (lex "1 + 2 * 3"))))
+
+ (test-equal "Multiple of the same operation gets clumed together"
+ '((resolved-operator +)
+ (integer (base-10 "1"))
+ (integer (base-10 "2"))
+ (integer (base-10 "3")))
+ (resolve-order-of-operations (flatten-infix (lex "1 + 2 + 3"))))
+
+ (test-equal "Simple Ternary"
+ '(ternary
+ (integer (base-10 "1"))
+ (integer (base-10 "2"))
+ (integer (base-10 "3")))
+ (resolve-order-of-operations (flatten-infix (lex "1 ? 2 : 3"))))
+
+ (test-equal "ternary with further infix operators"
+ '(ternary ((resolved-operator +)
+ (integer (base-10 "1"))
+ (integer (base-10 "2")))
+ ((resolved-operator %)
+ (integer (base-10 "3"))
+ (integer (base-10 "4")))
+ ((resolved-operator *)
+ (integer (base-10 "5"))
+ (integer (base-10 "6"))))
+ (resolve-order-of-operations (flatten-infix (lex "1 + 2? 3 % 4 : 5 * 6")))))
+
diff --git a/tests/test/cpp.scm b/tests/test/cpp.scm
index f3b9ff72..8a53ecce 100644
--- a/tests/test/cpp.scm
+++ b/tests/test/cpp.scm
@@ -13,6 +13,17 @@
;; So changing the lexer test cases isn't a problem
;; but don't change the parser test cases
+;; Strings aren't yet implemented
+(test-skip "Strings")
+;; __asm__ always has strings as arguments
+(test-skip "__asm__")
+;; not implemented
+(test-skip "Token concatenation")
+;; not implemented
+(test-skip "Floating point numbers")
+;; order of operation is wrong, leading to an incorrect result
+(test-skip "Cast with operation")
+
(define run (compose parse-lexeme-tree lex))
(define-syntax let-group
@@ -108,11 +119,6 @@
-(test-skip "Strings")
-(test-skip "__asm__")
-(test-skip "Token concatenation")
-(test-skip "Floating point numbers")
-
;; Hand picked forms from output of `cpp -dM /usr/include/termios.h` on
;; FreeBSD 13.1-RELEASE releng/13.1-n250148-fc952ac2212 GENERIC amd64
;; 2022-06-28
@@ -175,8 +181,8 @@
(test-equal '(ternary
(and (>= x #x61)
(<= x #x7A))
- (- x (+ #x61 1))
- (bitand (- x (+ #x61 1)) 127))
+ (+ (- x #x61) 1)
+ (bitand (+ (- x #x61) 1) 127))
(run form)))
(let ((form "((x) & ~(IOCPARM_MASK << 16))"))
@@ -350,7 +356,8 @@
(group (variable "num")))))))
(lex form))
(test-equal '(as-type (unsigned long)
- (bitor (<< (bitand len IOCPARM_MASK) 16)
+ (bitor inout
+ (<< (bitand len IOCPARM_MASK) 16)
(<< group 8)
num))
(run form))))