Skip to content

Commit 952361f

Browse files
committed
improve rule for distinguishing tuples (arg lists) from parenthesized blocks
a parenthesized expression is a tuple when at least one of these is true: - it is empty - there is a comma - it begins with `x...` and isn't just `(x...)` - it begins with `(;` (corresponding to the positional part being empty) add line numbers to blocks written as `(a; b; c)`
1 parent d6b75d1 commit 952361f

File tree

4 files changed

+98
-53
lines changed

4 files changed

+98
-53
lines changed

src/ast.scm

+1
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@
406406
(eq? (cadr (caddr x)) 'Vararg)))))
407407
(define (trans? x) (and (pair? x) (eq? (car x) '|.'|)))
408408
(define (ctrans? x) (and (pair? x) (eq? (car x) '|'|)))
409+
(define (linenum? x) (and (pair? x) (eq? (car x) 'line)))
409410

410411
(define (make-assignment l r) `(= ,l ,r))
411412
(define (assignment? e) (and (pair? e) (eq? (car e) '=)))

src/julia-parser.scm

+62-35
Original file line numberDiff line numberDiff line change
@@ -675,8 +675,7 @@
675675
(memv (peek-token s) ops))
676676
(loop ex #f (peek-token s))
677677
(if (and add-linenums
678-
(not (and (pair? (car ex))
679-
(eq? (caar ex) 'line))))
678+
(not (linenum? (car ex))))
680679
(let ((loc (line-number-node s)))
681680
(loop (list* (down s) loc ex) #f (peek-token s)))
682681
(loop (cons (down s) ex) #f (peek-token s))))))))))
@@ -1350,7 +1349,7 @@
13501349
(blk (parse-block s (lambda (s) (parse-docstring s parse-eq)))))
13511350
(expect-end s word)
13521351
(let ((blk (if (and (length> blk 1)
1353-
(pair? (cadr blk)) (eq? (caadr blk) 'line))
1352+
(linenum? (cadr blk)))
13541353
(list* 'block loc (cddr blk))
13551354
blk)))
13561355
(if (eq? word 'quote)
@@ -1374,7 +1373,7 @@
13741373
(error "let variables should end in \";\" or newline"))
13751374
(let* ((ex (begin0 (parse-block s)
13761375
(expect-end s word)))
1377-
(ex (if (and (length= ex 2) (pair? (cadr ex)) (eq? (caadr ex) 'line))
1376+
(ex (if (and (length= ex 2) (linenum? (cadr ex)))
13781377
`(block) ;; don't need line info in an empty let block
13791378
ex)))
13801379
`(let ,(if (and (length= binds 1) (or (assignment? (car binds)) (decl? (car binds))
@@ -1527,8 +1526,7 @@
15271526
catch-block
15281527
`(block ,loc ,var
15291528
,@(if (and (length= catch-block 2)
1530-
(pair? (cadr catch-block))
1531-
(eq? (caadr catch-block) 'line))
1529+
(linenum? (cadr catch-block)))
15321530
'()
15331531
(cdr catch-block))))
15341532
(if var? var 'false)
@@ -1725,7 +1723,7 @@
17251723
;; . an extra comma at the end is allowed
17261724
;; . expressions after a ; are enclosed in (parameters ...)
17271725
;; . an expression followed by ... becomes (... x)
1728-
(define (parse-arglist s closer)
1726+
(define (parse-arglist s closer (add-linenums #f))
17291727
(with-bindings ((range-colon-enabled #t)
17301728
(space-sensitive #f)
17311729
(where-enabled #t)
@@ -1739,12 +1737,17 @@
17391737
(to-kws (reverse! lst))
17401738
(reverse! lst)))
17411739
(if (eqv? t #\;)
1742-
(begin (take-token s)
1743-
(let ((params (loop '()))
1740+
(begin (take-token s) (require-token s)
1741+
(let ((loc (line-number-node s))
1742+
(params (loop '()))
17441743
(lst (if (eqv? closer #\) )
17451744
(to-kws (reverse lst))
17461745
(reverse lst))))
1747-
(cons (cons 'parameters params)
1746+
(cons `(parameters
1747+
,@(if add-linenums
1748+
(list loc)
1749+
'())
1750+
,@params)
17481751
lst)))
17491752
(let* ((nxt (parse-eq* s))
17501753
(c (require-token s)))
@@ -1910,20 +1913,36 @@
19101913
;; this allows us to first parse tuples using parse-arglist
19111914
(define (parameters-to-block e)
19121915
(if (and (pair? e) (eq? (car e) 'parameters))
1913-
(cond ((length= e 1) '())
1914-
((length= e 2) (parameters-to-block (cadr e)))
1915-
((length= e 3)
1916-
(let ((fst (cadr e))
1917-
(snd (caddr e)))
1918-
(if (and (pair? fst) (eq? (car fst) 'parameters))
1919-
(let ((rec (parameters-to-block fst))
1920-
(snd (parameters-to-block snd)))
1921-
(and rec snd
1922-
(cons (car snd) rec)))
1923-
#f)))
1924-
(else #f))
1916+
(let ((e2 (filter (lambda (x) (not (linenum? x))) e))
1917+
(lnum (if (and (pair? (cdr e))
1918+
(linenum? (cadr e)))
1919+
(cadr e)
1920+
#f)))
1921+
(cond ((length= e2 1) '())
1922+
((length= e2 2)
1923+
(let ((rec (parameters-to-block (cadr e2))))
1924+
(if (null? rec)
1925+
rec
1926+
(cons lnum rec))))
1927+
((length= e2 3)
1928+
(let ((fst (cadr e2))
1929+
(snd (caddr e2)))
1930+
(if (and (pair? fst) (eq? (car fst) 'parameters))
1931+
(let ((rec (parameters-to-block fst))
1932+
(snd (parameters-to-block snd)))
1933+
(and rec snd
1934+
(append (if lnum (list lnum) '()) (cons (car snd) rec))))
1935+
#f)))
1936+
(else #f)))
19251937
(list (kw-to-= e))))
19261938

1939+
(define (rm-linenums e)
1940+
(if (atom? e) e
1941+
(map rm-linenums
1942+
(if (eq? (car e) 'parameters)
1943+
(filter (lambda (x) (not (linenum? x))) e)
1944+
e))))
1945+
19271946
;; convert an arglist to a tuple or block expr
19281947
;; leading-semi? means we saw (; ...)
19291948
;; comma? means there was a comma after the first expression
@@ -1938,9 +1957,10 @@
19381957
`(block)))))) ;; all semicolons inside ()
19391958
(and (null? first) (null? args) (not comma?)
19401959
`(block)) ;; this case is (;)
1941-
(if (and (pair? args) (pair? (car args)) (eq? (caar args) 'parameters))
1942-
`(tuple ,(car args) ,@first ,@(map kw-to-= (cdr args)))
1943-
`(tuple ,@first ,@(map kw-to-= args))))))
1960+
(rm-linenums
1961+
(if (and (pair? args) (pair? (car args)) (eq? (caar args) 'parameters))
1962+
`(tuple ,(car args) ,@first ,@(map kw-to-= (cdr args)))
1963+
`(tuple ,@first ,@(map kw-to-= args)))))))
19441964

19451965
(define (tuple-to-arglist e)
19461966
(cond ((eq? (car e) 'tuple) (map =-to-kw (cdr e)))
@@ -1986,8 +2006,8 @@
19862006
(take-token s) ;; take #\)
19872007
'(|::| . #f))
19882008
((eqv? nxt #\;)
1989-
(cons (arglist-to-tuple #t #f (parse-arglist s #\) ))
1990-
#t))
2009+
(let ((ex (arglist-to-tuple #t #f (parse-arglist s #\) ))))
2010+
(cons ex (eq? (car ex) 'tuple))))
19912011
(else
19922012
;; here we parse the first subexpression separately, so
19932013
;; we can look for a comma to see if it's a tuple.
@@ -1997,7 +2017,7 @@
19972017
(cond ((eqv? t #\) )
19982018
(take-token s)
19992019
;; value in parentheses (x)
2000-
(if (and (pair? ex) (eq? (car ex) '...))
2020+
(if (vararg? ex)
20012021
(let ((lineno (input-port-line (ts:port s))))
20022022
(if (or accept-dots-without-comma (eq? (with-bindings ((whitespace-newline #f))
20032023
(peek-token s))
@@ -2008,6 +2028,19 @@
20082028
(string "(" (deparse (cadr ex)) "...,)"))
20092029
(cons `(tuple ,ex) #t))))
20102030
(cons ex #f)))
2031+
((eqv? t #\,)
2032+
;; tuple (x,) (x,y) etc.
2033+
(take-token s)
2034+
(cons (arglist-to-tuple #f #t (parse-arglist s #\) ) ex)
2035+
#t))
2036+
((eqv? t #\;)
2037+
(cons (arglist-to-tuple
2038+
#f
2039+
;; consider `(x...; ` the start of an arglist, since it's not useful as a block
2040+
(vararg? ex)
2041+
(parse-arglist s #\) #t)
2042+
ex)
2043+
(vararg? ex)))
20112044
((eq? t 'for)
20122045
(expect-space-before s 'for)
20132046
(take-token s)
@@ -2017,13 +2050,7 @@
20172050
(error "expected \")\""))
20182051
(cons gen #f)))
20192052
(else
2020-
;; tuple (x,) (x,y) (x...) etc.
2021-
(if (eqv? t #\, )
2022-
(take-token s)
2023-
(if (not (eqv? t #\;))
2024-
(error "missing comma or ) in argument list")))
2025-
(cons (arglist-to-tuple #f (eqv? t #\,) (parse-arglist s #\) ) ex)
2026-
#t)))))))))
2053+
(error "missing comma or ) in argument list")))))))))
20272054

20282055
(define (not-eof-for delim c)
20292056
(if (eof-object? c)

src/julia-syntax.scm

+7-9
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@
766766
(if (and (pair? body) (eq? (car body) 'block))
767767
(cond ((atom? (cdr body))
768768
`(block ,stmt (null)))
769-
((and (pair? (cadr body)) (eq? (caadr body) 'line))
769+
((linenum? (cadr body))
770770
`(block ,(cadr body) ,stmt ,@(cddr body)))
771771
(else
772772
`(block ,stmt ,@(cdr body))))
@@ -805,8 +805,7 @@
805805
,(ctor-body body curlyargs))))))
806806

807807
(define (function-body-lineno body)
808-
(let ((lnos (filter (lambda (e) (and (pair? e) (eq? (car e) 'line)))
809-
body)))
808+
(let ((lnos (filter linenum? body)))
810809
(if (null? lnos) '() (car lnos))))
811810

812811
;; rewrite calls to `new( ... )` to `new` expressions on the appropriate
@@ -863,7 +862,7 @@
863862
(fields defs) (separate (lambda (x) (or (symbol? x) (decl? x)))
864863
fields0)
865864
(let* ((defs (filter (lambda (x) (not (effect-free? x))) defs))
866-
(locs (if (and (pair? fields0) (pair? (car fields0)) (eq? (caar fields0) 'line))
865+
(locs (if (and (pair? fields0) (linenum? (car fields0)))
867866
(list (car fields0))
868867
'()))
869868
(field-names (map decl-var fields))
@@ -1103,7 +1102,7 @@
11031102
(set! a (cadr w))))
11041103
#f))
11051104
(argl (if (pair? a)
1106-
(tuple-to-arglist a)
1105+
(tuple-to-arglist (filter (lambda (x) (not (linenum? x))) a))
11071106
(list a)))
11081107
;; TODO: always use a specific special name like #anon# or _, then ignore
11091108
;; this as a local variable name.
@@ -1238,7 +1237,7 @@
12381237
(if (null? f)
12391238
'()
12401239
(let ((x (car f)))
1241-
(cond ((or (symbol? x) (decl? x) (and (pair? x) (eq? (car x) 'line)))
1240+
(cond ((or (symbol? x) (decl? x) (linenum? x))
12421241
(loop (cdr f)))
12431242
((and (assignment? x) (or (symbol? (cadr x)) (decl? (cadr x))))
12441243
(error (string "\"" (deparse x) "\" inside type definition is reserved")))
@@ -1893,8 +1892,7 @@
18931892
(lambda (e)
18941893
(cond ((null? (cdr e)) '(null))
18951894
((and (null? (cddr e))
1896-
(not (and (pair? (cadr e))
1897-
(eq? (car (cadr e)) 'line))))
1895+
(not (linenum? (cadr e))))
18981896
(expand-forms (cadr e)))
18991897
(else
19001898
(cons 'block
@@ -3608,7 +3606,7 @@ f(x) = yt(x)
36083606
((block body)
36093607
(let* ((last-fname filename)
36103608
(fnm (first-non-meta e))
3611-
(fname (if (and (length> e 1) (pair? fnm) (eq? (car fnm) 'line)
3609+
(fname (if (and (length> e 1) (linenum? fnm)
36123610
(length> fnm 2))
36133611
(caddr fnm)
36143612
filename))

test/syntax.jl

+28-9
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,21 @@ macro test999_str(args...); args; end
176176
@test_throws ParseError Meta.parse("(,)")
177177
@test_throws ParseError Meta.parse("(;,)")
178178
@test_throws ParseError Meta.parse("(,;)")
179+
# TODO: would be nice to make these errors, but needed to parse e.g. `(x;y,)->x`
180+
#@test_throws ParseError Meta.parse("(1;2,)")
181+
#@test_throws ParseError Meta.parse("(1;2,;)")
182+
#@test_throws ParseError Meta.parse("(1;2,;3)")
179183
@test Meta.parse("(x;)") == Expr(:block, :x)
180184
@test Meta.parse("(;x)") == Expr(:tuple, Expr(:parameters, :x))
181185
@test Meta.parse("(;x,)") == Expr(:tuple, Expr(:parameters, :x))
182186
@test Meta.parse("(x,)") == Expr(:tuple, :x)
183187
@test Meta.parse("(x,;)") == Expr(:tuple, Expr(:parameters), :x)
184-
@test Meta.parse("(x;y)") == Expr(:block, :x, :y)
185-
@test Meta.parse("(x=1;y=2)") == Expr(:block, Expr(:(=), :x, 1), Expr(:(=), :y, 2))
188+
@test Meta.parse("(x;y)") == Expr(:block, :x, LineNumberNode(1,:none), :y)
189+
@test Meta.parse("(x...;)") == Expr(:tuple, Expr(:parameters), Expr(:(...), :x))
190+
@test Meta.parse("(;x...)") == Expr(:tuple, Expr(:parameters, Expr(:(...), :x)))
191+
@test Meta.parse("(x...;y)") == Expr(:tuple, Expr(:parameters, :y), Expr(:(...), :x))
192+
@test Meta.parse("(x;y...)") == Expr(:block, :x, LineNumberNode(1,:none), Expr(:(...), :y))
193+
@test Meta.parse("(x=1;y=2)") == Expr(:block, Expr(:(=), :x, 1), LineNumberNode(1,:none), Expr(:(=), :y, 2))
186194
@test Meta.parse("(x,;y)") == Expr(:tuple, Expr(:parameters, :y), :x)
187195
@test Meta.parse("(x,;y=1)") == Expr(:tuple, Expr(:parameters, Expr(:kw, :y, 1)), :x)
188196
@test Meta.parse("(x,a;y=1)") == Expr(:tuple, Expr(:parameters, Expr(:kw, :y, 1)), :x, :a)
@@ -1310,13 +1318,24 @@ end
13101318
# f(x) = x", 1)[1] === "x"
13111319

13121320
# issue #26137
1313-
@test Meta.parse("-()^2") == Expr(:call, :^, Expr(:call, :-), 2)
1314-
@test Meta.parse("-(x)^2") == Expr(:call, :-, Expr(:call, :^, :x, 2))
1315-
@test Meta.parse("-(x,)^2") == Expr(:call, :^, Expr(:call, :-, :x), 2)
1316-
@test Meta.parse("-(x,y)^2") == Expr(:call, :^, Expr(:call, :-, :x, :y), 2)
1317-
@test Meta.parse("+((1,2))") == Expr(:call, :+, Expr(:tuple, 1, 2))
1318-
@test Meta.parse("-(x;y)^2") == Expr(:call, :^, Expr(:call, :-, Expr(:parameters, :y), :x), 2)
1319-
@test Meta.parse("-(x...)^2") == Expr(:call, :^, Expr(:call, :-, Expr(:(...), :x)), 2)
1321+
# cases where parens enclose argument lists
1322+
@test Meta.parse("-()^2") == Expr(:call, :^, Expr(:call, :-), 2)
1323+
@test Meta.parse("-(x,)^2") == Expr(:call, :^, Expr(:call, :-, :x), 2)
1324+
@test Meta.parse("-(x,;)^2") == Expr(:call, :^, Expr(:call, :-, Expr(:parameters), :x), 2)
1325+
@test Meta.parse("-(;x)^2") == Expr(:call, :^, Expr(:call, :-, Expr(:parameters, :x)), 2)
1326+
@test Meta.parse("-(x,y)^2") == Expr(:call, :^, Expr(:call, :-, :x, :y), 2)
1327+
@test Meta.parse("-(x...)^2") == Expr(:call, :^, Expr(:call, :-, Expr(:(...), :x)), 2)
1328+
@test Meta.parse("-(x...;)^2") == Expr(:call, :^, Expr(:call, :-, Expr(:parameters), Expr(:(...), :x)), 2)
1329+
@test Meta.parse("-(x...;)") == Expr(:call, :-, Expr(:parameters), Expr(:(...), :x))
1330+
1331+
# cases where parens are just grouping
1332+
@test Meta.parse("-(x)^2") == Expr(:call, :-, Expr(:call, :^, :x, 2))
1333+
@test Meta.parse("-(a=1)^2") == Expr(:call, :-, Expr(:call, :^, Expr(:(=), :a, 1), 2))
1334+
@test Meta.parse("-(x;y)^2") == Expr(:call, :-, Expr(:call, :^, Expr(:block, :x, LineNumberNode(1,:none), :y), 2))
1335+
@test Meta.parse("-(;)^2") == Expr(:call, :-, Expr(:call, :^, Expr(:block), 2))
1336+
@test Meta.parse("-(;;;;)^2") == Expr(:call, :-, Expr(:call, :^, Expr(:block), 2))
1337+
@test Meta.parse("-(x;;;)^2") == Expr(:call, :-, Expr(:call, :^, Expr(:block, :x), 2))
1338+
@test Meta.parse("+((1,2))") == Expr(:call, :+, Expr(:tuple, 1, 2))
13201339

13211340
@test_throws ParseError("space before \"(\" not allowed in \"+ (\"") Meta.parse("1 -+ (a=1, b=2)")
13221341

0 commit comments

Comments
 (0)