Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions PRIMITIVES.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ Note that functions have their names mangled a little bit ("-" is converted to "
* Return the ASCII character corresponding to the given integer.
* `cons`
* Add the element to the start of the given (potentially empty) list.
* `entries`
* Return the names of all files in the given directory.
* See [test/entries.lisp](test/entries.lisp) for an example
* `environment`
* Return a list of all environmental variables.
* `exit`
Expand Down Expand Up @@ -134,6 +137,8 @@ Note that functions have their names mangled a little bit ("-" is converted to "
* `split-all`
* Return a list of all parts of string, split by the character.
* e.g. `(split-all (getenv "PATH") #\:)` to find all directories on the PATH.
* `stat`
* Returns file information as a list (TYPE SIZE MODE), or nil on failure.
* `strcat`
* Join two strings together and return them.
* `strcmp`
Expand All @@ -158,8 +163,14 @@ The implementation of these primitives can be found in the file [stdlib.slisp](s
* Test if every item in a list is true.
* `append`
* Append the given value to the specified list. If the list is empty just return the specified item.
* `dir?`
* Does the given path exist as a directory?
* `even?`
* Return 1 if the given number is even, nil otherwise.
* `exists?`
* Does the given filename exist?
* `file?`
* Does the given path exist as a file?
* `filter`
* Return a list consisting of all members of the input list for which the given predicate returns non-nil.
* `find`
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ It should be noted that we prepend a standard library of functions to all user p
* File I/O operations:
* `fopen`, `fclose`, `fread`, and `fwrite`.
* Filesystem primitive:
* `entries`.
* `entries`, `stat`, `dir?`, `exists?`, and `file?`.
* Comparison operations:
* `=`, `<`, `<=`, `>=`, `>`, and `!` to invert a result.
* Special forms
Expand Down
109 changes: 109 additions & 0 deletions compiler/template.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ SYS_read equ 0
SYS_write equ 1
SYS_open equ 2
SYS_close equ 3
SYS_stat equ 4
SYS_lseek equ 8
SYS_exit equ 60
SYS_getdents64 equ 217
Expand All @@ -44,6 +45,16 @@ SYS_getrandom equ 318
O_RDONLY equ 0
O_DIRECTORY equ 0200000o

;; (stat)
S_IFMT equ 0170000o
S_IFSOCK equ 0140000o
S_IFLNK equ 0120000o
S_IFREG equ 0100000o
S_IFBLK equ 0060000o
S_IFDIR equ 0040000o
S_IFCHR equ 0020000o
S_IFIFO equ 0010000o




Expand Down Expand Up @@ -1683,6 +1694,104 @@ FUNC fn_split
ret



;;
;; stat(path)
;;
;; Returns:
;;
;; (type size mode)
;;
;; type:
;; 0 = regular file
;; 1 = directory
;; 2 = symlink
;; 3 = character device
;; 4 = block device
;; 5 = fifo
;; 6 = socket
;;
;; Returns NIL on failure.
;;

FUNC fn_stat
mov rbx,rdi ; type-check arg1: string
GET_TAG_BITS rbx
cmp rbx,TAG_ID_STRING
jne type_error
UNTAG_REG rdi

sub rsp, 160 ; Reserve space for struct stat.

mov eax,SYS_stat
mov rsi,rsp ; stack buffer
syscall
test rax,rax
js .failure

movzx eax,word [rsp+24] ; Get the mode
mov ecx,eax ; save it

and eax,S_IFMT
xor edx,edx ; default = regular file

cmp eax,S_IFREG ; compare against other explicitly known types
je .type_done
mov edx,1
cmp eax,S_IFDIR
je .type_done
mov edx,2
cmp eax,S_IFLNK
je .type_done
mov edx,3
cmp eax,S_IFCHR
je .type_done
mov edx,4
cmp eax,S_IFBLK
je .type_done
mov edx,5
cmp eax,S_IFIFO
je .type_done
mov edx,6
cmp eax,S_IFSOCK
je .type_done

xor edx,edx ; unknown -> regular file

.type_done:
xor r13,r13 ; create nil for end of list
TAG_NIL_REG r13

mov eax,ecx ; add mode
and eax,0777o
TAG_INTEGER_REG rax
mov rdi,rax
mov rsi,r13
call fn_cons
mov r13,rax

mov rax,[rsp+48] ; add size
TAG_INTEGER_REG rax
mov rdi,rax
mov rsi,r13
call fn_cons
mov r13,rax

mov eax,edx ; add type
TAG_INTEGER_REG rax
mov rdi,rax
mov rsi,r13
call fn_cons
add rsp, 160 ; restore stack
ret

.failure:
add rsp, 160 ; restore stack
xor rax,rax
TAG_NIL_REG rax
ret


;; join the two given strings and return a new one.
FUNC fn_strcat
mov rbx, rdi ; type-check arg1: str
Expand Down
22 changes: 22 additions & 0 deletions stdlib.slisp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,28 @@ do the right thing for each entry in the given list."



;;; filesystem primitives

(defun exists? (path)
"Does the named path exist?"
(cons? (stat path)))

(defun file? (path)
"Is the given path a file?"
(let ((res (stat path)))
(if res
(if (= 0 (car res))
t))))

(defun dir? (path)
"Is the given path a directory?"
(let ((res (stat path)))
(if res
(if (= 1 (car res))
t))))




;;; logical functions

Expand Down
22 changes: 22 additions & 0 deletions test/stat.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(defun main ()

;; basic tests
(println "/etc exists as directory:" (dir? "/etc"))
(println "/etc/fstab exists as directory:" (dir? "/etc/fstab"))
(println "/etc/fstab exists as file:" (file? "/etc/fstab"))

(let ((s (stat "stat.lisp")))

;; If stat succeeds it returns a three-element list
(let ((type (nth s 0))
(size (nth s 1))
(mode (nth s 2)))

;; Show what we got
(println "file type for stat.lisp: " type)
(println "file size for stat.lisp: " size)
(println "file mode for stat.lisp: " mode)))

;; Returns nil on failure
(println "stat of a missing file:")
(println (stat "/the/file/does'nt/exist")))
8 changes: 8 additions & 0 deletions test/stat.test.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/etc exists as directory:1
/etc/fstab exists as directory:<nil>
/etc/fstab exists as file:1
file type for stat.lisp: 0
file size for stat.lisp: 676
file mode for stat.lisp: 420
stat of a missing file:
<nil>
Loading