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
16 changes: 10 additions & 6 deletions PRIMITIVES.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ added to them too.
* (Integer) comparison operations
* `<`, `<=`, `>=`, `>`, and `=`.
* Other functions implemented in assembly:
* `alloc`
* Allocate N bytes of string memory.
* This is only supposed to be used in conjunction with the `syscall` primitive.
* NOTE: There is no `free`, we rely upon our [garbage collector](GC.md) to free allocated memory.
* `car`
* Return the first item of a list.
* `cdr`
Expand All @@ -118,12 +122,6 @@ added to them too.
* Terminate execution.
* `explode`
* Convert the supplied string to a list of characters.
* `fread`
* Read ALL available data from the given handle.
* To simplify usage `fread` will accept a nil-filehandle, and return nil.
* `fwrite`
* Write the given data, with length, to the open file handle.
* To simplify usage `fwrite` will accept a nil-filehandle, and return nil.
* `implode`
* Convert the given list of characters to a string.
* `int`
Expand Down Expand Up @@ -242,6 +240,12 @@ The implementation of these primitives can be found in the file [stdlib.slisp](s
* To simplify usage `fclose` will accept a nil-filehandle.
* `fopen`
* Open the given filename, for read/write, and return a handle.
* `fread`
* Read ALL available data from the given handle.
* To simplify usage `fread` will accept a nil-filehandle, and return nil.
* `fwrite`
* Write the given data, with length, to the open file handle.
* To simplify usage `fwrite` will accept a nil-filehandle, and return nil.
* `getc`
* Read a single character from STDIN, return NIL on failure.
* `getenv`
Expand Down
158 changes: 30 additions & 128 deletions compiler/template.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,36 @@ fn_printstr:
;;


;; Allocate (string) memory and return it.
FUNC fn_sys_alloc
mov rbx, rdi
GET_TAG_BITS rbx
cmp rbx, TAG_ID_INTEGER
jne type_error
UNTAG_REG rdi
cmp rdi, 0 ; N must be >= 1
jle .nil
push rdi
mov rax, rdi
mov rbx, TAG_ID_STRING
call alloc
pop rbx ; restore requested size
push rax ; save new region

.loop:
mov byte [rax], 42 ; *
inc rax
dec rbx
jnz .loop

pop rax
TAG_STRING_REG rax
ret
.nil:
STORE_NIL_REG rax
ret


;; Get the first item in the cons cell.
;; Note: calling (car nil) will return nil.
FUNC fn_sys_car
Expand Down Expand Up @@ -1578,134 +1608,6 @@ FUNC fn_sys_explode



;; Read ALL data from the given file handle.
;;
;; As a special case reading from a nil-filehandle
;; will return nil.
FUNC fn_sys_fread
mov rbx, rdi ; type-check arg1
GET_TAG_BITS rbx
cmp rbx, TAG_ID_NIL ; special case the nil-handle
je .fread_return_nil
cmp rbx, TAG_ID_INTEGER
jne type_error

UNTAG_REG rdi
mov [random_buffer], rdi ; save file handle

mov rax, SYS_lseek
xor rsi, rsi ; offset = 0
mov rdx, 2 ; SEEK_END
syscall

test rax, rax
js .fread_seek_end_fail

mov rbx, rax ; file size
push rbx ; save size

; allocate size+1 bytes
mov rdi, rbx
inc rdi

mov rax, rdi ; size to allocate
mov rbx, TAG_ID_STRING
call alloc ; Allocate memory, result in RAX

pop rbx ; restore size
mov r12, rax ; data pointer in r12.

mov rax, 8 ; seek to start of file
mov rdi,[random_buffer] ; saved file handle
xor rsi, rsi
xor rdx, rdx ; SEEK_SET
syscall

test rax, rax
js .fread_seek_start_fail

; Read until we've consumed the entire file.
xor r13, r13 ; bytes_read_so_far = 0

.read_loop:

cmp r13, rbx ; got all the file? Yay!
je .read_complete

mov rdi, [random_buffer] ; file handle
lea rsi, [r12+r13] ; destination offset

mov rdx, rbx
sub rdx, r13 ; remaining bytes

mov rax, SYS_read
syscall

test rax, rax ; failed to read
js .fread_read_fail

test rax, rax
je .fread_short_read ; unexpected EOF

add r13, rax
jmp .read_loop

.read_complete:
mov byte [r12+r13], 0 ; null-terminate
mov rax, r12
TAG_STRING_REG rax
ret

.fread_short_read:
.fread_read_fail:
.fread_seek_start_fail:
.fread_seek_end_fail:
.fread_return_nil:
STORE_NIL_REG rax
ret





;; Write data, with a size, to the given file handle
;; e.g. (fwrite handle "Steve" 5)
;;
;; As a special case writing to a nil-filehandle
;; will return nil.
FUNC fn_sys_fwrite
mov rbx, rdi ; type-check arg1: int
GET_TAG_BITS rbx
cmp rbx, TAG_ID_NIL ; special case the nil-handle
je .fwrite_return_nil
cmp rbx, TAG_ID_INTEGER
jne type_error

mov rbx, rsi ; type-check arg2: str
GET_TAG_BITS rbx
cmp rbx, TAG_ID_STRING
jne type_error

mov rbx, rdx ; type-check arg3: int
GET_TAG_BITS rbx
cmp rbx, TAG_ID_INTEGER
jne type_error

UNTAG_REG rsi
UNTAG_REG rdi
UNTAG_REG rdx

mov rax, SYS_write
syscall
TAG_INTEGER_REG rax
ret

.fwrite_return_nil:
STORE_NIL_REG rax
ret



;; join a character list to a string.
FUNC fn_sys_implode
mov rbx,rdi
Expand Down
3 changes: 1 addition & 2 deletions inception.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@
(register-builtin "sys_=" (lambda (args) (sys_= (car args) (cadr args))))
(register-builtin "sys_>" (lambda (args) (sys_> (car args) (cadr args))))
(register-builtin "sys_>=" (lambda (args) (sys_>= (car args) (cadr args))))
(register-builtin "sys_alloc" (lambda (args) (sys_alloc (car args))))
(register-builtin "sys_car" (lambda (args) (sys_car (car args))))
(register-builtin "sys_cdr" (lambda (args) (sys_cdr (car args))))
(register-builtin "sys_chr" (lambda (args) (sys_chr (car args))))
Expand All @@ -309,8 +310,6 @@
(register-builtin "sys_environment" (lambda (args) (sys_environment)))
(register-builtin "sys_exit" (lambda (args) (sys_exit (car args))))
(register-builtin "sys_explode" (lambda (args) (sys_explode (car args))))
(register-builtin "sys_fread" (lambda (args) (sys_fread (car args))))
(register-builtin "sys_fwrite" (lambda (args) (sys_fwrite (car args) (cadr args) (caddr args))))
(register-builtin "sys_implode" (lambda (args) (sys_implode (car args))))
(register-builtin "sys_int" (lambda (args) (sys_int (car args))))
(register-builtin "sys_isqrt" (lambda (args) (sys_isqrt (car args))))
Expand Down
62 changes: 50 additions & 12 deletions stdlib.slisp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@
"Perform A modulus B"
(sys_% a b))

(defun alloc (n)
"Allocate N bytes of string memory, and fill it with ASCII.

This is only supposed to be used alongside the `syscall` function.

NOTE There is no corresponding free function, the garbage collector will
reap orphaned memory."
(sys_alloc n))

(defun car (xs)
"Return the first item from the list"
(sys_car xs))
Expand Down Expand Up @@ -98,18 +107,6 @@
"Convert a string to a list of characters."
(sys_explode str))

(defun fread (handle)
"Read all available data from the given file-handle.

It is assumed the filehandle came from fopen, but nil is also accepted."
(sys_fread handle))

(defun fwrite (handle data len)
"Write the given data, of length LEN, to the specified file-handle.

It is assumed the filehandle came from fopen, but nil is also accepted."
(sys_fwrite handle data len))

(defun implode (xs)
"Convert a list of characters to a string."
(sys_implode xs))
Expand Down Expand Up @@ -431,6 +428,47 @@ Mode is \"r\" for read, \"w\" for write, and \"a\" for append."
handle
nil)))

(defconst SYS_lseek 8)
(defconst SEEK_END 2)

(defconst SEEK_START 0)

(defun fread (handle)
"Read all available data from the given file-handle.

It is assumed the filehandle came from fopen, but nil is also accepted."
(if (nil? handle)
nil
(let ((size 0))
; seek to end of handle to get size
(set! size (syscall SYS_lseek handle 0 SEEK_END))
(if (< size 0)
nil
(do
; seek back to the start
(syscall SYS_lseek handle 0 SEEK_START)

; allocate memory and read into it
(let ((mem (alloc size))
(res (syscall SYS_read handle mem size)))
(if (< res 0)
nil
mem)))))))

(defconst SYS_write 1)

(defun fwrite (handle data len)
"Write the given data, of length LEN, to the specified file-handle.

It is assumed the filehandle came from fopen, but nil is also accepted."
(if (nil? handle)
nil
(let ((res (syscall SYS_write handle data len)))
(if (> res 0)
res
nil))))


(defun file? (path)
"Is the given path a file?"
(let ((res (stat path)))
Expand Down
Loading