From 18b825bfdc19969a31dab0207fbf78b9a4e31022 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Tue, 11 Jan 2022 22:48:14 +0100 Subject: [PATCH] add syntax for empty n-dimensional arrays (#41618) --- NEWS.md | 1 + src/ast.scm | 2 +- src/julia-parser.scm | 50 +++++++++++++++++++++++++++++--------------- test/syntax.jl | 34 ++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/NEWS.md b/NEWS.md index ca66132891685..6347bc45ca75c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -19,6 +19,7 @@ New language features * Mutable struct fields may now be annotated as `const` to prevent changing them after construction, providing for greater clarity and optimization ability of these objects ([#43305]). +* Empty n-dimensional arrays can now be created using multiple semicolons inside square brackets, i.e. `[;;;]` creates a 0×0×0 `Array`. ([#41618]) Language changes ---------------- diff --git a/src/ast.scm b/src/ast.scm index 7056189764876..688b4e852e7c4 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -141,7 +141,7 @@ ((typed_hcat) (string (deparse (cadr e)) (deparse (cons 'hcat (cddr e))))) ((ncat) (string #\[ (deparse-arglist (cddr e) (string (deparse-semicolons (cadr e)) " ")) - (if (= (length (cddr e)) 1) + (if (<= (length (cddr e)) 1) (deparse-semicolons (cadr e)) "") #\])) ((typed_ncat) (string (deparse (cadr e)) diff --git a/src/julia-parser.scm b/src/julia-parser.scm index ef8019722f50d..97a11df701a37 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -2016,24 +2016,40 @@ (where-enabled #t) (whitespace-newline #f) (for-generator #t)) - (if (eqv? (require-token s) closer) - (begin (take-token s) - '()) - (let* ((first (parse-eq* s)) - (t (peek-token s))) - (cond ((or (eqv? t #\,) (eqv? t closer)) - (parse-vect s first closer)) - ((eq? t 'for) - (expect-space-before s 'for) - (take-token s) - (parse-comprehension s first closer)) - ((eqv? t #\newline) + (let ((t (require-token s))) + (cond ((eqv? t closer) + (take-token s) + '()) + ((eqv? t #\;) + (take-token s) + (define (loop (n 1)) + (let ((t (with-whitespace-newline (require-token s)))) (take-token s) - (if (memv (peek-token s) (list #\, closer)) - (parse-vect s first closer) - (parse-array s first closer #t last-end-symbol))) - (else - (parse-array s first closer #f last-end-symbol))))))) + (cond ((eqv? t #\;) + (if (ts:space? s) + (error (string "unexpected space inside " + (deparse `(ncat ,n)) " expression"))) + (loop (+ n 1))) + ((eqv? t closer) `(ncat ,n)) + (else (error (string "unexpected \"" t "\" inside " + (deparse `(ncat ,n)) " expression")))))) + (loop)) + (else + (let* ((first (parse-eq* s)) + (t (peek-token s))) + (cond ((or (eqv? t #\,) (eqv? t closer)) + (parse-vect s first closer)) + ((eq? t 'for) + (expect-space-before s 'for) + (take-token s) + (parse-comprehension s first closer)) + ((eqv? t #\newline) + (take-token s) + (if (memv (peek-token s) (list #\, closer)) + (parse-vect s first closer) + (parse-array s first closer #t last-end-symbol))) + (else + (parse-array s first closer #f last-end-symbol))))))))) (define (kw-to-= e) (if (kwarg? e) (cons '= (cdr e)) e)) (define (=-to-kw e) (if (assignment? e) (cons 'kw (cdr e)) e)) diff --git a/test/syntax.jl b/test/syntax.jl index beff019d72e80..354f014733c7e 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3088,3 +3088,37 @@ function checkUserAccess(u::User) return false end """) + +@testset "empty nd arrays" begin + @test :([]) == Expr(:vect) + @test :([;]) == Expr(:ncat, 1) + @test :([;;]) == Expr(:ncat, 2) + @test :([;;;]) == Expr(:ncat, 3) + + @test [] == Array{Any}(undef, 0) + @test [;] == Array{Any}(undef, 0) + @test [;;] == Array{Any}(undef, 0, 0) + @test [;;;] == Array{Any}(undef, 0, 0, 0) + + @test :(T[]) == Expr(:ref, :T) + @test :(T[;]) == Expr(:typed_ncat, :T, 1) + @test :(T[;;]) == Expr(:typed_ncat, :T, 2) + @test :(T[;;;]) == Expr(:typed_ncat, :T, 3) + + @test Int[] == Array{Int}(undef, 0) + @test Int[;] == Array{Int}(undef, 0) + @test Int[;;] == Array{Int}(undef, 0, 0) + @test Int[;;;] == Array{Int}(undef, 0, 0, 0) + + @test :([ ]) == Expr(:vect) + @test :([ + ]) == Expr(:vect) + @test :([ ;; ]) == Expr(:ncat, 2) + @test :([ + ;; + ]) == Expr(:ncat, 2) + + @test_throws ParseError Meta.parse("[; ;]") + @test_throws ParseError Meta.parse("[;; ;]") + @test_throws ParseError Meta.parse("[;\n;]") +end