resurrect builtins: WIP
This commit is contained in:
parent
f3c8c2c7f0
commit
4d3751f654
|
@ -37,6 +37,7 @@ set -e
|
||||||
|
|
||||||
SCM_FILES="
|
SCM_FILES="
|
||||||
gash/bournish-commands.scm
|
gash/bournish-commands.scm
|
||||||
|
gash/builtins.scm
|
||||||
gash/guix-build-utils.scm
|
gash/guix-build-utils.scm
|
||||||
gash/gash.scm
|
gash/gash.scm
|
||||||
gash/io.scm
|
gash/io.scm
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
(define-module (gash builtins)
|
||||||
|
#:use-module (ice-9 match)
|
||||||
|
|
||||||
|
#:use-module (srfi srfi-1)
|
||||||
|
#:use-module (srfi srfi-26)
|
||||||
|
|
||||||
|
#:use-module (gash bournish-commands)
|
||||||
|
#:use-module (gash gash)
|
||||||
|
#:use-module (gash job)
|
||||||
|
#:use-module (gash peg)
|
||||||
|
|
||||||
|
#:export (
|
||||||
|
%builtin-commands
|
||||||
|
PATH-search-path
|
||||||
|
;; cd-command
|
||||||
|
;; ("bg" . ,bg-command)
|
||||||
|
;; ("cat" . ,cat-command)
|
||||||
|
;; ("cd" . ,cd-command)
|
||||||
|
;; ("cp" . ,cp-command)
|
||||||
|
;; ("echo" . ,echo-command)
|
||||||
|
;; ("exit" . ,exit-command)
|
||||||
|
;; ("fg" . ,fg-command)
|
||||||
|
;; ("help" . ,help-command)
|
||||||
|
;; ("jobs" . ,jobs-command)
|
||||||
|
;; ("ls" . ,ls-command)
|
||||||
|
;; ("pwd" . ,pwd-command)
|
||||||
|
;; ("reboot" . ,reboot-command)
|
||||||
|
;; ("rm" . ,rm-command)
|
||||||
|
;; ("set" . ,set-command)
|
||||||
|
;; ("wc" . ,wc-command)
|
||||||
|
;; ("which" . ,which-command)
|
||||||
|
|
||||||
|
))
|
||||||
|
|
||||||
|
(define (PATH-search-path program)
|
||||||
|
(search-path (string-split (getenv "PATH") #\:) program))
|
||||||
|
|
||||||
|
(define (cd-command . args)
|
||||||
|
(match args
|
||||||
|
(() (chdir (getenv "HOME")))
|
||||||
|
((dir)
|
||||||
|
(chdir dir))
|
||||||
|
((args ...)
|
||||||
|
(format (current-error-port) "cd: too many arguments: ~a\n" (string-join args)))))
|
||||||
|
|
||||||
|
(define (echo-command . args)
|
||||||
|
(match args
|
||||||
|
(() (newline))
|
||||||
|
(("-n" args ...) (map display args))
|
||||||
|
(_ (map display args) (newline))))
|
||||||
|
|
||||||
|
(define (bg-command . args)
|
||||||
|
(match args
|
||||||
|
(() (bg 1))
|
||||||
|
((job x ...) (bg (string->number (car job))))))
|
||||||
|
|
||||||
|
(define (fg-command . args)
|
||||||
|
(match args
|
||||||
|
(() (fg 1))
|
||||||
|
((job x ...) (fg (string->number (car job))))))
|
||||||
|
|
||||||
|
(define pwd-command (lambda _ (stdout (getcwd))))
|
||||||
|
|
||||||
|
(define (set-command . args) ;; TODO export; env vs set
|
||||||
|
(define (display-var o)
|
||||||
|
(format #t "~a=~a\n" (car o) (cdr o)))
|
||||||
|
(match args
|
||||||
|
(() (for-each display-var global-variables))
|
||||||
|
(("-e") (set-shell-opt! "errexit" #t))
|
||||||
|
(("+e") (set-shell-opt! "errexit" #f))
|
||||||
|
(("-x") (set-shell-opt! "xtrace" #t))
|
||||||
|
(("+x") (set-shell-opt! "xtrace" #f))))
|
||||||
|
|
||||||
|
(define (exit-command . args)
|
||||||
|
(match args
|
||||||
|
(() (exit 0))
|
||||||
|
((status)
|
||||||
|
(exit (string->number status)))
|
||||||
|
((args ...)
|
||||||
|
(format (current-error-port) "exit: too many arguments: ~a\n" (string-join args)))))
|
||||||
|
|
||||||
|
(define (help-command . _)
|
||||||
|
(display "\
|
||||||
|
Hello, this is gash, Guile As SHell.
|
||||||
|
|
||||||
|
Gash is work in progress; many language constructs work, globbing
|
||||||
|
mostly works, pipes work, some redirections work.
|
||||||
|
")
|
||||||
|
(when (or %prefer-builtins? (not (PATH-search-path "ls")))
|
||||||
|
(display "\nIt features the following, somewhat naive builtin commands\n")
|
||||||
|
(display-tabulated (map car %commands))))
|
||||||
|
|
||||||
|
(define (cp-command-implementation source dest . rest)
|
||||||
|
(copy-file source dest))
|
||||||
|
|
||||||
|
(define cp-command (wrap-command cp-command-implementation "cp"))
|
||||||
|
|
||||||
|
(define %builtin-commands
|
||||||
|
`(
|
||||||
|
("bg" . ,bg-command)
|
||||||
|
("cat" . ,cat-command)
|
||||||
|
("cd" . ,cd-command)
|
||||||
|
("cp" . ,cp-command)
|
||||||
|
;;("echo" . ,echo-command) BROKEN wrt variables for now
|
||||||
|
("exit" . ,exit-command)
|
||||||
|
("fg" . ,fg-command)
|
||||||
|
("help" . ,help-command)
|
||||||
|
("jobs" . ,jobs-command)
|
||||||
|
("ls" . ,ls-command)
|
||||||
|
("pwd" . ,pwd-command)
|
||||||
|
("reboot" . ,reboot-command)
|
||||||
|
("rm" . ,rm-command)
|
||||||
|
("set" . ,set-command)
|
||||||
|
("wc" . ,wc-command)
|
||||||
|
("which" . ,which-command)
|
||||||
|
))
|
125
gash/gash.scm
125
gash/gash.scm
|
@ -19,13 +19,14 @@
|
||||||
#:use-module (gash peg)
|
#:use-module (gash peg)
|
||||||
#:use-module (gash io)
|
#:use-module (gash io)
|
||||||
#:use-module (gash util)
|
#:use-module (gash util)
|
||||||
#:use-module (gash bournish-commands)
|
|
||||||
|
|
||||||
#:export (main
|
#:export (main
|
||||||
%debug-level
|
%debug-level
|
||||||
|
%prefer-builtins?
|
||||||
shell-opt?))
|
shell-opt?))
|
||||||
|
|
||||||
(define %debug-level 0)
|
(define %debug-level 0) ; 1 informational, 2 verbose, 3 peg tracing
|
||||||
|
(define %prefer-builtins? #f) ; use builtin, even if COMMAND is available in PATH?
|
||||||
|
|
||||||
(define (remove-shell-comments s)
|
(define (remove-shell-comments s)
|
||||||
(string-join (map
|
(string-join (map
|
||||||
|
@ -177,132 +178,14 @@ copyleft.
|
||||||
(loop (cdr patterns) (glob- (car patterns) paths))))))
|
(loop (cdr patterns) (glob- (car patterns) paths))))))
|
||||||
(#t (list pattern))))
|
(#t (list pattern))))
|
||||||
|
|
||||||
(define (background ast)
|
(define (DEAD-background ast)
|
||||||
(match ast
|
(match ast
|
||||||
(('pipeline fg rest ...) `(pipeline #f ,@rest))
|
(('pipeline fg rest ...) `(pipeline #f ,@rest))
|
||||||
(_ ast)))
|
(_ ast)))
|
||||||
|
|
||||||
(define (PATH-search-path program)
|
|
||||||
(search-path (string-split (getenv "PATH") #\:) program))
|
|
||||||
|
|
||||||
(define (cd-command . args)
|
|
||||||
(match args
|
|
||||||
(() (chdir (getenv "HOME")))
|
|
||||||
((dir)
|
|
||||||
(chdir dir))
|
|
||||||
((args ...)
|
|
||||||
(format (current-error-port) "cd: too many arguments: ~a\n" (string-join args)))))
|
|
||||||
|
|
||||||
(define (echo-command . args)
|
|
||||||
(match args
|
|
||||||
(() (newline))
|
|
||||||
(("-n" args ...) (map display args))
|
|
||||||
(_ (map display args) (newline))))
|
|
||||||
|
|
||||||
(define (bg-command . args)
|
|
||||||
(match args
|
|
||||||
(() (bg 1))
|
|
||||||
((job x ...) (bg (string->number (car job))))))
|
|
||||||
|
|
||||||
(define (fg-command . args)
|
|
||||||
(match args
|
|
||||||
(() (fg 1))
|
|
||||||
((job x ...) (fg (string->number (car job))))))
|
|
||||||
|
|
||||||
(define pwd-command (lambda _ (stdout (getcwd))))
|
|
||||||
|
|
||||||
(define (set-command . args) ;; TODO export; env vs set
|
|
||||||
(define (display-var o)
|
|
||||||
(format #t "~a=~a\n" (car o) (cdr o)))
|
|
||||||
(match args
|
|
||||||
(() (for-each display-var global-variables))
|
|
||||||
(("-e") (set-shell-opt! "errexit" #t))
|
|
||||||
(("+e") (set-shell-opt! "errexit" #f))
|
|
||||||
(("-x") (set-shell-opt! "xtrace" #t))
|
|
||||||
(("+x") (set-shell-opt! "xtrace" #f))))
|
|
||||||
|
|
||||||
(define (exit-command . args)
|
|
||||||
(match args
|
|
||||||
(() (exit 0))
|
|
||||||
((status)
|
|
||||||
(exit (string->number status)))
|
|
||||||
((args ...)
|
|
||||||
(format (current-error-port) "exit: too many arguments: ~a\n" (string-join args)))))
|
|
||||||
|
|
||||||
(define (help-command . _)
|
|
||||||
(display "\
|
|
||||||
Hello, this is gash, Guile As SHell.
|
|
||||||
|
|
||||||
Gash is work in progress; many language constructs work, globbing
|
|
||||||
mostly works, pipes work, some redirections work.
|
|
||||||
")
|
|
||||||
(when (or %prefer-builtins? (not (PATH-search-path "ls")))
|
|
||||||
(display "\nIt features the following, somewhat naive builtin commands\n")
|
|
||||||
(display-tabulated (map car %commands))))
|
|
||||||
|
|
||||||
(define (cp-command-implementation source dest . rest)
|
|
||||||
(copy-file source dest))
|
|
||||||
|
|
||||||
(define cp-command (wrap-command cp-command-implementation "cp"))
|
|
||||||
|
|
||||||
(define (set-shell-opt! name set?)
|
|
||||||
(let* ((shell-opts (assoc-ref %global-variables "SHELLOPTS"))
|
|
||||||
(options (if (string-null? shell-opts) '()
|
|
||||||
(string-split shell-opts #\:)))
|
|
||||||
(new-options (if set? (delete-duplicates (sort (cons name options) string<))
|
|
||||||
(filter (negate (cut equal? <> name)) options)))
|
|
||||||
(new-shell-opts (string-join new-options ":")))
|
|
||||||
(assignment "SHELLOPTS" new-shell-opts)))
|
|
||||||
|
|
||||||
(define (shell-opt? name)
|
(define (shell-opt? name)
|
||||||
(member name (string-split (assoc-ref %global-variables "SHELLOPTS") #\:)))
|
(member name (string-split (assoc-ref %global-variables "SHELLOPTS") #\:)))
|
||||||
|
|
||||||
(define %commands
|
|
||||||
;; Built-in commands.
|
|
||||||
`(
|
|
||||||
("bg" . ,bg-command)
|
|
||||||
("cat" . ,cat-command)
|
|
||||||
("cd" . ,cd-command)
|
|
||||||
("cp" . ,cp-command)
|
|
||||||
("echo" . ,echo-command)
|
|
||||||
("exit" . ,exit-command)
|
|
||||||
("fg" . ,fg-command)
|
|
||||||
("help" . ,help-command)
|
|
||||||
("jobs" . ,jobs-command)
|
|
||||||
("ls" . ,ls-command)
|
|
||||||
("pwd" . ,pwd-command)
|
|
||||||
("reboot" . ,reboot-command)
|
|
||||||
("rm" . ,rm-command)
|
|
||||||
("set" . ,set-command)
|
|
||||||
("wc" . ,wc-command)
|
|
||||||
("which" . ,which-command)
|
|
||||||
))
|
|
||||||
|
|
||||||
(define %prefer-builtins? #t) ; use builtin, even if COMMAND is available in PATH?
|
|
||||||
(define (builtin ast)
|
|
||||||
(receive (command args)
|
|
||||||
(match ast
|
|
||||||
((('append ('glob command) args ...)) (values command args))
|
|
||||||
((('glob command)) (values command #f))
|
|
||||||
(_ (values #f #f)))
|
|
||||||
(let ((program (and command (PATH-search-path command))))
|
|
||||||
(when (> %debug-level 0)
|
|
||||||
(format (current-error-port) "command ~a => ~s ~s\n" program command args))
|
|
||||||
(cond ((and program (not %prefer-builtins?))
|
|
||||||
#f)
|
|
||||||
((and command (assoc-ref %commands command))
|
|
||||||
=>
|
|
||||||
(lambda (command)
|
|
||||||
(if args
|
|
||||||
`(,apply ,command ,@args)
|
|
||||||
`(,command))))
|
|
||||||
(else
|
|
||||||
(match ast
|
|
||||||
(('for-each rest ...) ast)
|
|
||||||
(('if rest ...) ast)
|
|
||||||
(#t #t)
|
|
||||||
(_ #f)))))))
|
|
||||||
|
|
||||||
(define (tostring . args)
|
(define (tostring . args)
|
||||||
(with-output-to-string (cut map display args)))
|
(with-output-to-string (cut map display args)))
|
||||||
|
|
||||||
|
|
64
gash/peg.scm
64
gash/peg.scm
|
@ -5,11 +5,13 @@
|
||||||
#:use-module (ice-9 pretty-print)
|
#:use-module (ice-9 pretty-print)
|
||||||
#:use-module (ice-9 peg)
|
#:use-module (ice-9 peg)
|
||||||
#:use-module (ice-9 peg codegen)
|
#:use-module (ice-9 peg codegen)
|
||||||
|
#:use-module (ice-9 receive)
|
||||||
#:use-module (ice-9 regex)
|
#:use-module (ice-9 regex)
|
||||||
|
|
||||||
#:use-module (srfi srfi-1)
|
#:use-module (srfi srfi-1)
|
||||||
#:use-module (srfi srfi-26)
|
#:use-module (srfi srfi-26)
|
||||||
|
|
||||||
|
#:use-module (gash builtins)
|
||||||
#:use-module (gash gash)
|
#:use-module (gash gash)
|
||||||
#:use-module (gash io)
|
#:use-module (gash io)
|
||||||
#:use-module (gash job)
|
#:use-module (gash job)
|
||||||
|
@ -20,6 +22,7 @@
|
||||||
%global-variables
|
%global-variables
|
||||||
parse
|
parse
|
||||||
peg-trace?
|
peg-trace?
|
||||||
|
set-shell-opt!
|
||||||
))
|
))
|
||||||
|
|
||||||
(define (wrap-parser-for-users for-syntax parser accumsym s-syn)
|
(define (wrap-parser-for-users for-syntax parser accumsym s-syn)
|
||||||
|
@ -220,7 +223,16 @@
|
||||||
(format (current-error-port) "sh-exec:exec ast=~s\n" ast))
|
(format (current-error-port) "sh-exec:exec ast=~s\n" ast))
|
||||||
(match ast
|
(match ast
|
||||||
('script #t) ;; skip
|
('script #t) ;; skip
|
||||||
(('pipeline command ...) (exec ast))
|
(('pipeline commands ...)
|
||||||
|
(when (shell-opt? "xtrace")
|
||||||
|
(for-each
|
||||||
|
(lambda (o)
|
||||||
|
(match o
|
||||||
|
(('command command ...)
|
||||||
|
(format (current-error-port) "+ ~a\n" (string-join command)))
|
||||||
|
(_ (format (current-error-port) "FIXME trace:~s" o))))
|
||||||
|
(reverse commands)))
|
||||||
|
(exec ast))
|
||||||
(_ (for-each exec ast))))
|
(_ (for-each exec ast))))
|
||||||
|
|
||||||
|
|
||||||
|
@ -237,10 +249,12 @@
|
||||||
ast))))
|
ast))))
|
||||||
|
|
||||||
(define (transform ast)
|
(define (transform ast)
|
||||||
|
(when (> %debug-level 1)
|
||||||
|
(format (current-error-port) "transform ast=~s\n" ast))
|
||||||
(match ast
|
(match ast
|
||||||
(('script o ...) (map transform o))
|
(('script o ...) (map transform o))
|
||||||
(('substitution o) `(substitution ,@(transform o)))
|
(('substitution o) `(substitution ,@(transform o)))
|
||||||
(('pipeline o) (pk `(pipeline ,(transform o))))
|
(('pipeline o) (pk `(pipeline ,(let ((c (warn 'transform (transform o)))) (or (builtin c) c)))))
|
||||||
(('pipeline h t) (pk `(pipeline ,(transform h) ,@(map transform t))))
|
(('pipeline h t) (pk `(pipeline ,(transform h) ,@(map transform t))))
|
||||||
(('command o ...) `(command ,@(map transform o)))
|
(('command o ...) `(command ,@(map transform o)))
|
||||||
(('literal o) (transform o))
|
(('literal o) (transform o))
|
||||||
|
@ -255,12 +269,46 @@
|
||||||
(('else-part o ...) `(begin ,@(map transform o)))
|
(('else-part o ...) `(begin ,@(map transform o)))
|
||||||
(_ ast)))
|
(_ ast)))
|
||||||
|
|
||||||
|
(define (set-shell-opt! name set?)
|
||||||
|
(let* ((shell-opts (assoc-ref %global-variables "SHELLOPTS"))
|
||||||
|
(options (if (string-null? shell-opts) '()
|
||||||
|
(string-split shell-opts #\:)))
|
||||||
|
(new-options (if set? (delete-duplicates (sort (cons name options) string<))
|
||||||
|
(filter (negate (cut equal? <> name)) options)))
|
||||||
|
(new-shell-opts (string-join new-options ":")))
|
||||||
|
;; HMM
|
||||||
|
(assignment "SHELLOPTS" new-shell-opts)
|
||||||
|
(lambda _ (format (current-error-port) "hiero\n") "daro")
|
||||||
|
'("hiero2")))
|
||||||
|
|
||||||
|
(define (builtin ast)
|
||||||
|
(when (> %debug-level 0)
|
||||||
|
(format (current-error-port) "builtin ast=~s\n" ast))
|
||||||
|
(receive (command args)
|
||||||
|
(match ast
|
||||||
|
(('command (and (? string?) command) args ...) (values command args))
|
||||||
|
;; ((('append ('glob command) args ...)) (values command args))
|
||||||
|
;; ((('glob command)) (values command #f))
|
||||||
|
(_ (values #f #f)))
|
||||||
|
(let ((program (and command (PATH-search-path command))))
|
||||||
|
(when (> %debug-level 0)
|
||||||
|
(format (current-error-port) "command ~a => ~s ~s\n" program command args))
|
||||||
|
(cond ((and program (not %prefer-builtins?))
|
||||||
|
#f)
|
||||||
|
((and command (assoc-ref %builtin-commands command))
|
||||||
|
=>
|
||||||
|
(lambda (command)
|
||||||
|
(if args
|
||||||
|
`(,apply ,command ',args)
|
||||||
|
command)))
|
||||||
|
(else #f)))))
|
||||||
|
|
||||||
;; FIXME: export/env vs set
|
;; FIXME: export/env vs set
|
||||||
(define %global-variables
|
(define %global-variables
|
||||||
(map identity ;; FIXME: make mutable
|
(map identity ;; FIXME: make mutable
|
||||||
`(("SHELLOPTS" . "")
|
`(,(cons "SHELLOPTS" "")
|
||||||
("PIPESTATUS" . "([0]=\"0\"")
|
,(cons "PIPESTATUS" "([0]=\"0\"")
|
||||||
("?" . "")
|
,(cons "?" "")
|
||||||
,@(map (lambda (key-value)
|
,@(map (lambda (key-value)
|
||||||
(let* ((key-value (string-split key-value #\=))
|
(let* ((key-value (string-split key-value #\=))
|
||||||
(key (car key-value))
|
(key (car key-value))
|
||||||
|
@ -333,12 +381,10 @@
|
||||||
(cut warn 'status <>)
|
(cut warn 'status <>)
|
||||||
system*) command))
|
system*) command))
|
||||||
(else (lambda () #t))))
|
(else (lambda () #t))))
|
||||||
(warn 'command=> (exec (append-map glob args))))
|
(exec (append-map glob args)))
|
||||||
|
|
||||||
(define (substitution . commands)
|
(define (substitution . commands)
|
||||||
(apply (@ (gash pipe) pipeline->string) (map cdr commands)))
|
(apply (@ (gash pipe) pipeline->string) (map cdr commands)))
|
||||||
|
|
||||||
(define (pipeline . commands)
|
(define (pipeline . commands)
|
||||||
(apply (@ (gash pipe) pipeline) #t commands)
|
(apply (@ (gash pipe) pipeline) #t commands))
|
||||||
;;(map (lambda (command) (command)) commands)
|
|
||||||
)
|
|
||||||
|
|
|
@ -73,8 +73,8 @@
|
||||||
(when (pair? w)
|
(when (pair? w)
|
||||||
(close-port (current-output-port))
|
(close-port (current-output-port))
|
||||||
(set-current-output-port (car w)))
|
(set-current-output-port (car w)))
|
||||||
(let ((status (warn 'spawn-status (if (thunk? command) (command)
|
(let ((status (if (thunk? command) (command)
|
||||||
(command input w)))))
|
(command input w))))
|
||||||
(exit (cond ((number? status) status)
|
(exit (cond ((number? status) status)
|
||||||
((boolean? status) status)
|
((boolean? status) status)
|
||||||
(else 0)))))
|
(else 0)))))
|
||||||
|
@ -90,11 +90,11 @@
|
||||||
(define (pipeline fg? . commands)
|
(define (pipeline fg? . commands)
|
||||||
(when (> %debug-level 0)
|
(when (> %debug-level 0)
|
||||||
(format (current-error-port) "pipeline[~a]: COMMANDS: ~s\n" fg? commands))
|
(format (current-error-port) "pipeline[~a]: COMMANDS: ~s\n" fg? commands))
|
||||||
(when (shell-opt? "xtrace")
|
;; (when (shell-opt? "xtrace")
|
||||||
(for-each
|
;; (for-each
|
||||||
(lambda (o)
|
;; (lambda (o)
|
||||||
(format (current-error-port) "+ ~a\n" (string-join o)))
|
;; (format (current-error-port) "+ ~a\n" (string-join o)))
|
||||||
(reverse commands)))
|
;; (reverse commands)))
|
||||||
(receive (r w)
|
(receive (r w)
|
||||||
(pipe*)
|
(pipe*)
|
||||||
(move->fdes w 2)
|
(move->fdes w 2)
|
||||||
|
|
Loading…
Reference in New Issue