diff --git a/check.sh b/check.sh index 44cdfe9..d4d9c95 100755 --- a/check.sh +++ b/check.sh @@ -69,6 +69,12 @@ tests=" 60-function 60-subst +70-hash.sh +70-hash-hash.sh +70-percent.sh +70-percent-percent.sh +70-percent-space.sh + 100-sed 100-sed-once 100-sed-global diff --git a/gash/peg.scm b/gash/peg.scm index a65dcb2..603a033 100644 --- a/gash/peg.scm +++ b/gash/peg.scm @@ -225,9 +225,13 @@ dollar < '$' literal <-- backslash? (!ws !amp !tick !dollar !pipe !semi !par !nl !sp !rbrace !io-op !dq !sq .)+ variable <-- dollar ('$' / '#' / '*' / '?' / '@' / [0-9] / identifier / lbrace identifier rbrace) - variable-and-or <- dollar lbrace (variable-or / variable-and ) rbrace + variable-and-or <- dollar lbrace (variable-or / variable-and / variable-hash-hash / variable-hash / variable-percent-percent / variable-percent ) rbrace variable-and <-- identifier plus rhs variable-or <-- identifier minus rhs + variable-hash <-- identifier hash rhs + variable-hash-hash <-- identifier hash hash rhs + variable-percent <-- identifier percent rhs + variable-percent-percent <-- identifier percent percent rhs delim <- singlequotes / doublequotes / substitution sq < ['] dq < [\"] @@ -248,6 +252,8 @@ rbrace < [}] plus < [+] minus < '-' + hash < '#' + percent < '%' par < lpar / rpar nl < '\n' sp < '\t' / ' ' / (escaped-nl sp*) @@ -351,19 +357,20 @@ (('brace-group o) `(brace-group ,(transform o))) (('file-name o) `(file-name ,(transform o))) + (_ ast))) -(define (remove-shell-comments s) +(define (remove-line-comments s) (string-join (map (lambda (s) - (let* ((n (string-index s #\#))) - (if n (string-pad-right s (string-length s) #\space 0 n) + (let ((n (string-index s #\#))) + (if (and n (zero? n)) (string-pad-right s (string-length s) #\space 0 n) s))) (string-split s #\newline)) "\n")) (define (parse-string string) - (let* ((pt ((compose parse- remove-shell-comments) string)) + (let* ((pt ((compose parse- remove-line-comments) string)) (foo (when (> %debug-level 1) (display "tree:\n") (pretty-print pt))) (flat (flatten pt)) (foo (when (> %debug-level 0) (display "flat:\n") (pretty-print flat))) diff --git a/gash/script.scm b/gash/script.scm index 763ca6d..b1859ef 100644 --- a/gash/script.scm +++ b/gash/script.scm @@ -91,14 +91,16 @@ (else (lambda () #t)))) (exec (append-map glob args))) -(define (glob pattern) - (define (glob? pattern) - (and (string? pattern) (string-match "\\?|\\*" pattern))) - (define (glob2regex pattern) +(define (glob? pattern) + (and (string? pattern) (string-match "\\?|\\*" pattern))) + +(define* (glob->regex pattern #:key (begin "^") (end "$")) (let* ((pattern (regexp-substitute/global #f "\\." pattern 'pre "\\." 'post)) (pattern (regexp-substitute/global #f "\\?" pattern 'pre "." 'post)) (pattern (regexp-substitute/global #f "\\*" pattern 'pre ".*" 'post))) - (make-regexp (string-append "^" pattern "$")))) + (make-regexp (string-append begin pattern end)))) + +(define (glob pattern) (define (glob-match regex path) ;; pattern path -> bool (regexp-match? (regexp-exec regex path))) (define (glob- pattern file-names) @@ -107,7 +109,7 @@ (append-map (lambda (file-name) (map (cut string-append (if (string=? "/" file-name) "" file-name) "/" <>) (filter (conjoin (negate (cut string-prefix? "." <>)) - (cute glob-match (glob2regex pattern) <>)) + (cute glob-match (glob->regex pattern) <>)) (or (scandir file-name) '())))) file-names))) (cond @@ -285,3 +287,57 @@ (define (file-name o) o) + +(define (regexp-exec-non-greedy regexp string) + (let ((max (string-length string))) + (let loop ((size 1)) + (and (<= size max) + (or (regexp-exec regexp (substring string 0 size)) + (loop (1+ size))))))) + +(define (regexp-exec-non-greedy-reverse regexp string) + (let ((max (string-length string))) + (let loop ((start (1- max))) + (and (>= start 0) + (or (regexp-exec regexp (substring string start)) + (loop (1- start))))))) + +(define (variable-hash name pattern) + (let ((value (variable name)) + (glob? (glob? pattern))) + (if glob? (let* ((regexp (glob->regex pattern #:end "")) + (match (regexp-exec-non-greedy regexp value))) + (if match (string-drop value (match:end match)) + value)) + (if (string-prefix? pattern value) (string-drop value (string-length pattern)) + value)))) + +(define (variable-hash-hash name pattern) + (let ((value (variable name)) + (glob? (glob? pattern))) + (if glob? (let* ((regexp (glob->regex pattern #:end "")) + (match (regexp-exec regexp value))) + (if match (string-drop value (match:end match)) + value)) + (if (string-prefix? pattern value) (string-drop value (string-length pattern)) + value)))) + +(define (variable-percent name pattern) + (let ((value (variable name)) + (glob? (glob? pattern))) + (if glob? (let* ((regexp (glob->regex pattern #:begin "")) + (match (regexp-exec-non-greedy-reverse regexp value))) + (if match (substring value 0 (- (string-length value) (match:end match))) + value)) + (if (string-suffix? pattern value) (substring value 0 (string-length pattern)) + value)))) + +(define (variable-percent-percent name pattern) + (let ((value (variable name)) + (glob? (glob? pattern))) + (if glob? (let* ((regexp (glob->regex pattern #:begin "")) + (match (regexp-exec regexp value))) + (if match (substring value 0 (match:start match)) + value)) + (if (string-suffix? pattern value) (substring value 0 (string-length pattern)) + value)))) diff --git a/test/70-hash-hash.sh b/test/70-hash-hash.sh new file mode 100644 index 0000000..bdbec5c --- /dev/null +++ b/test/70-hash-hash.sh @@ -0,0 +1,2 @@ +file=dir/sub/name.ext +echo ${file##*/} diff --git a/test/70-hash-hash.stdout b/test/70-hash-hash.stdout new file mode 100644 index 0000000..a6726fb --- /dev/null +++ b/test/70-hash-hash.stdout @@ -0,0 +1 @@ +name.ext diff --git a/test/70-hash.sh b/test/70-hash.sh new file mode 100644 index 0000000..b4b9c02 --- /dev/null +++ b/test/70-hash.sh @@ -0,0 +1,2 @@ +file=dir/sub/name.ext +echo ${file#*/} diff --git a/test/70-hash.stdout b/test/70-hash.stdout new file mode 100644 index 0000000..2d658b6 --- /dev/null +++ b/test/70-hash.stdout @@ -0,0 +1 @@ +sub/name.ext diff --git a/test/70-percent-percent.sh b/test/70-percent-percent.sh new file mode 100644 index 0000000..47dcc3c --- /dev/null +++ b/test/70-percent-percent.sh @@ -0,0 +1,2 @@ +file=dir/sub/name.ext +echo ${file%%/*} diff --git a/test/70-percent-percent.stdout b/test/70-percent-percent.stdout new file mode 100644 index 0000000..0d2ecd7 --- /dev/null +++ b/test/70-percent-percent.stdout @@ -0,0 +1 @@ +dir diff --git a/test/70-percent-space.sh b/test/70-percent-space.sh new file mode 100644 index 0000000..512072d --- /dev/null +++ b/test/70-percent-space.sh @@ -0,0 +1,2 @@ +args="--prefix=/usr " +echo ${args% *}/ diff --git a/test/70-percent-space.stdout b/test/70-percent-space.stdout new file mode 100644 index 0000000..bc89cc9 --- /dev/null +++ b/test/70-percent-space.stdout @@ -0,0 +1 @@ +--prefix=/usr/ diff --git a/test/70-percent.sh b/test/70-percent.sh new file mode 100644 index 0000000..af50281 --- /dev/null +++ b/test/70-percent.sh @@ -0,0 +1,2 @@ +file=dir/sub/name.ext +echo ${file%/*} diff --git a/test/70-percent.stdout b/test/70-percent.stdout new file mode 100644 index 0000000..86da852 --- /dev/null +++ b/test/70-percent.stdout @@ -0,0 +1 @@ +dir/sub