Skip to content

Commit 524d538

Browse files
author
Daniel Patterson
committed
added desugaring for binary operators, without well formedness checking, made the tokenizer handle parens adjacent to operators and other parens correctly (by caching tokens, instead of modifying the input port)
1 parent ee545ea commit 524d538

File tree

7 files changed

+114
-28
lines changed

7 files changed

+114
-28
lines changed

src/lang/ast.rkt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ these metadata purposes.
7575
;; s-op: srcloc op Expr Expr -> s-op
7676
(struct s-op (syntax op left right) #:transparent)
7777

78+
;; s-paren: srcloc Expr -> s-paren
79+
(struct s-paren (syntax expr) #:transparent)
80+
7881
;; An Expr is a
7982
;; (U s-obj s-onion s-list s-app s-left-app s-id
8083
;; s-assign s-num s-bool s-str

src/lang/desugar.rkt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,21 @@
127127
(s-bind s1 id (a-any)) bind)]
128128
[_ bind])))
129129

130+
(define op-method-table
131+
(make-immutable-hash
132+
`((,op+ . "plus")
133+
(,op- . "minus")
134+
(,op* . "times")
135+
(,op/ . "divide")
136+
(,op<= . "lessequal")
137+
(,op< . "lessthan")
138+
(,op>= . "greaterequal")
139+
(,op> . "greaterthan")
140+
(,op== . "equals")
141+
;; NOTE(dbp): we deal with not specially, since it is .equals(...).not()
142+
;(,op<> . "")
143+
)))
144+
130145
(define (desugar-internal ast)
131146
(define ds desugar-internal)
132147
(define (ds-args binds)
@@ -219,7 +234,7 @@
219234
[(s-try s try exn catch)
220235
;; NOTE(joe & dbp): The identifier in the exn binding of the try is carefully
221236
;; shadowed here to avoid capturing any names in Pyret. It is both
222-
;; the name that the compiler will use for the exception, and the name
237+
;; the name that the compiler will use for the exc, and the name
223238
;; that desugaring uses to provide the wrapped exception from the error
224239
;; library.
225240
(define make-error (s-app s (s-bracket s (s-id s 'error)
@@ -256,6 +271,20 @@
256271

257272
[(s-bracket-method s obj field) (s-bracket-method s (ds obj) (ds field))]
258273

274+
[(s-paren _ e) (ds e)]
275+
276+
;; NOTE(dbp): notequals is special because it requires two method
277+
[(s-op s 'op<> e1 e2)
278+
(s-app s (s-bracket s
279+
(s-app s (s-bracket s (ds e1) (s-str s "equals"))
280+
(list (ds e2)))
281+
(s-str s "not"))
282+
(list))]
283+
284+
[(s-op s op e1 e2)
285+
(s-app s (s-bracket s (ds e1) (s-str s (hash-ref op-method-table op)))
286+
(list (ds e2)))]
287+
259288
[(or (s-num _ _)
260289
(s-bool _ _)
261290
(s-str _ _)

src/lang/parser.rkt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,14 @@
156156
(syntax-parse stx
157157
#:datum-literals (binop-expr expr paren-expr)
158158
[(binop-expr _ _ _) (parse-stmt stx)]
159-
[(paren-expr "(" e ")") (parse-binop-expr #'e)]
159+
[(paren-expr "(" e ")") (s-paren (loc stx) (parse-binop-expr #'e))]
160160
[(expr e) (parse-expr #'e)]
161161
[(binop-expr e) (parse-binop-expr #'e)]))
162162

163163
(define (parse-op stx)
164164
(syntax-parse stx
165-
[(op str) (hash-ref op-lookup-table (syntax->datum #'str))]))
165+
#:datum-literals (binop)
166+
[(binop str) (hash-ref op-lookup-table (syntax->datum #'str))]))
166167

167168
(define (parse-return-ann stx)
168169
(syntax-parse stx

src/lang/runtime.rkt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ And the object was:
583583
(set! meta-str-store
584584
(make-immutable-hash
585585
`(("append" . ,(mk-prim-fun string-append 'append mk-str p-str-s (s1 s2) (p-str? p-str?)))
586+
("plus" . ,(mk-prim-fun string-append 'plus mk-str p-str-s (s1 s2) (p-str? p-str?)))
586587
("contains" . ,(mk-prim-fun string-contains 'contains mk-bool p-str-s (s1 s2) (p-str? p-str?)))
587588
("length" . ,(mk-prim-fun string-length 'length mk-num p-str-s (s) (p-str?)))
588589
("tonumber" . ,(mk-prim-fun string->number 'tonumber mk-num p-str-s (s) (p-str?)))

src/lang/tokenizer.rkt

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
ragg/support
55
racket/set
66
racket/port
7+
racket/list
78
"grammar.rkt")
89
(provide tokenize)
910

@@ -27,12 +28,14 @@
2728
operator-chars
2829
(union "+" "-" "*" "/" "<=" ">=" "==" "<>" "<" ">"))
2930

30-
31+
(define (get-middle-pos n pos)
32+
(position (+ n (position-offset pos))
33+
(position-line pos)
34+
(+ n (position-col pos))))
35+
3136
(define (tokenize ip)
3237
(port-count-lines! ip)
33-
(define (push-back-paren)
34-
(set! ip (input-port-append #t (open-input-string " (")
35-
ip)))
38+
(define extra-token #f)
3639
(define my-lexer
3740
(lexer-src-pos
3841
;; open parenthesis: preceded by space and not
@@ -41,14 +44,18 @@
4144
;; doing this at the tokenizer level, we don't need to deal
4245
;; with whitespace in the grammar.
4346
["(("
44-
(begin
45-
(push-back-paren)
46-
(token PARENNOSPACE "("))]
47+
(let [(middle-pos (get-middle-pos 1 start-pos))]
48+
(return-without-pos
49+
(list (position-token (token PARENNOSPACE "(") start-pos middle-pos)
50+
(position-token (token PARENSPACE "(") middle-pos end-pos))))]
4751
[(concatenation operator-chars "(")
48-
(let [(op (substring lexeme 0
49-
(- (string-length lexeme) 1)))]
50-
(push-back-paren)
51-
(token op op))]
52+
(let* [(op (substring lexeme 0
53+
(- (string-length lexeme) 1)))
54+
(op-len (string-length op))
55+
(middle-pos (get-middle-pos op-len start-pos))]
56+
(return-without-pos
57+
(list (position-token (token op op) start-pos middle-pos)
58+
(position-token (token PARENSPACE "(") middle-pos end-pos))))]
5259
[(concatenation whitespace "(")
5360
(token PARENSPACE "(")]
5461
["("
@@ -112,5 +119,14 @@
112119
[(eof)
113120
(void)]
114121
))
115-
(define (next-token) (my-lexer ip))
122+
(define (next-token)
123+
(if extra-token
124+
(let [(rv extra-token)]
125+
(set! extra-token #f)
126+
rv)
127+
(let [(tokens (my-lexer ip))]
128+
(cond
129+
[(list? tokens) (set! extra-token (second tokens))
130+
(first tokens)]
131+
[else tokens]))))
116132
next-token)

src/tests/compile-tests.rkt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,34 @@
656656

657657
))
658658

659+
(define binary-operators (test-suite "binary-operators"
660+
(check-pyret "6 - 4 - 1" (p:mk-num 1))
661+
(check-pyret "6 - (4 - 1)" (p:mk-num 3))
662+
(check-pyret "5 + 5" (p:mk-num 10))
663+
(check-pyret "5 * 5" (p:mk-num 25))
664+
(check-pyret "5 / 5" (p:mk-num 1))
665+
(check-pyret "4 <= 5" (p:mk-bool #t))
666+
(check-pyret "4 < 5" (p:mk-bool #t))
667+
(check-pyret "4 >= 5" (p:mk-bool #f))
668+
(check-pyret "4 > 5" (p:mk-bool #f))
669+
(check-pyret "4 <> 5" (p:mk-bool #t))
670+
(check-pyret "4 == 6" (p:mk-bool #f))
671+
(check-pyret "fun f(y):
672+
y
673+
end
674+
f(1+2)" (p:mk-num 3))
675+
(check-pyret "fun f(y):
676+
y
677+
end
678+
f((1+2))" (p:mk-num 3))
679+
(check-pyret "'hello' + ' world'" (p:mk-str "hello world"))
680+
(check-pyret-exn "5 + 'foo'" "Bad args to prim")
681+
(check-pyret "x = {lessequal(s,o): 3 end} x <= 5" (p:mk-num 3))
682+
(check-pyret-exn "x = {lessthan: \\s,o: 3 end} x < 5" "Arity")
683+
(check-pyret-exn "x = {greaterthan: 3} x > 5" "expected function")
684+
(check-pyret-exn "x = {} x <= 5" "lessequal was not found")
685+
))
686+
659687
(define all (test-suite "all"
660688
constants
661689
functions
@@ -668,7 +696,8 @@
668696
for-block
669697
methods
670698
exceptions
671-
ids-and-vars))
699+
ids-and-vars
700+
binary-operators))
672701

673702
(run-tests all 'normal)
674703

src/tests/parse-tests.rkt

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -519,17 +519,19 @@
519519
(list (s-num _ 3))))
520520
(s-num _ 4)))
521521
(check/block "(1 - 2) + 3" (s-op _ op+
522-
(s-op _ op-
522+
(s-paren _
523+
(s-op _ op-
523524
(s-num _ 1)
524-
(s-num _ 2))
525+
(s-num _ 2)))
525526
(s-num _ 3)))
526527
(check/block "(3 * (1 - 2)) / 3"
527528
(s-op _ op/
528-
(s-op _ op*
529+
(s-paren _ (s-op _ op*
529530
(s-num _ 3)
530-
(s-op _ op-
531+
(s-paren _
532+
(s-op _ op-
531533
(s-num _ 1)
532-
(s-num _ 2)))
534+
(s-num _ 2)))))
533535
(s-num _ 3)))
534536
(check/block "x = 3 + 4"
535537
(s-let _ (s-bind _ 'x _) (s-op _ op+ (s-num _ 3) (s-num _ 4))))
@@ -543,34 +545,39 @@
543545
(check/block "1+(2*3)"
544546
(s-op _ op+
545547
(s-num _ 1)
546-
(s-op _ op* (s-num _ 2) (s-num _ 3))))
548+
(s-paren _ (s-op _ op* (s-num _ 2) (s-num _ 3)))))
547549

548550
(check/block "1*(2-3)"
549551
(s-op _ op*
550552
(s-num _ 1)
551-
(s-op _ op- (s-num _ 2) (s-num _ 3))))
553+
(s-paren _ (s-op _ op- (s-num _ 2) (s-num _ 3)))))
552554

553555
(check/block "1/(2*3)"
554556
(s-op _ op/
555557
(s-num _ 1)
556-
(s-op _ op* (s-num _ 2) (s-num _ 3))))
558+
(s-paren _ (s-op _ op* (s-num _ 2) (s-num _ 3)))))
557559

558560
(check/block "1-(2*3)"
559561
(s-op _ op-
560562
(s-num _ 1)
561-
(s-op _ op* (s-num _ 2) (s-num _ 3))))
563+
(s-paren _ (s-op _ op* (s-num _ 2) (s-num _ 3)))))
562564

563565
(check/block "foo((2+3))"
564566
(s-app _
565567
(s-id _ 'foo)
566568
(list
567-
(s-op _ op* (s-num _ 2) (s-num _ 3)))))
569+
(s-paren _ (s-op _ op* (s-num _ 2) (s-num _ 3))))))
570+
571+
(check/block "fun f(y):
572+
y
573+
end
574+
f((1+2))" _ _)
568575

569576
(check/block "foo((2+3)*2)"
570577
(s-app _
571578
(s-id _ 'foo)
572579
(list
573-
(s-op _ op* (s-op _ op+ (s-num _ 2) (s-num _ 3))
580+
(s-op _ op* (s-paren _ (s-op _ op+ (s-num _ 2) (s-num _ 3)))
574581
(s-num _ 2)))))
575582

576583
(check/block "1 < 2"
@@ -593,7 +600,7 @@
593600

594601
(check/block "1 <= (1+2)"
595602
(s-op _ op<= (s-num _ 1)
596-
(s-op _ op+ (s-num _ 1) (s-num _ 2))))
603+
(s-paren _ (s-op _ op+ (s-num _ 1) (s-num _ 2)))))
597604

598605
(check-parse/fail "when(1 < 2): 3" "parsing error")
599606
))

0 commit comments

Comments
 (0)