Compare commits

..

No commits in common. "75d765c9055987a6a8f37d4c503ecd3a5b73a9da" and "87be0c0c33dcdf8d53a65200901a232c1f9a94ed" have entirely different histories.

17 changed files with 157 additions and 2933 deletions

View file

@ -1,23 +1,20 @@
# test.bin: lisp.rs lisp.o test.bin: lisp.rs lisp.o
# rustc -Clink-arg=-fuse-ld=mold -Clink-arg=lisp.o --edition=2024 --test -g $< -o $@ rustc -Clink-arg=-fuse-ld=mold -Clink-arg=lisp.o --edition=2024 --test -g $< -o $@
# lisp.o: lisp.asm lisp.o: lisp.asm
# nasm -g -f elf64 -o lisp.o lisp.asm nasm -g -f elf64 -o lisp.o lisp.asm
# test: test.bin test: test.bin
# ./test.bin ./test.bin
test.bin: test.rs lisp1.o test1.bin: test.rs lisp1.o
rustc -Clink-arg=-fuse-ld=mold -Clink-arg=lisp1.o --edition=2024 --test -g $< -o $@ rustc -Clink-arg=-fuse-ld=mold -Clink-arg=lisp1.o --edition=2024 --test -g $< -o $@
lisp1.o: lisp1.asm lisp1.o: lisp1.asm
nasm -g -f elf64 -o lisp1.o lisp1.asm nasm -g -f elf64 -o lisp1.o lisp1.asm
test: test.bin test1: test1.bin
./test.bin --nocapture --test-threads=1 ./test1.bin
lisp1: lisp1.o
ld -m elf_x86_64 -e _interp_entry -o lisp1 lisp1.o
clean: clean:
rm -f lisp.o test.bin lisp1.o test1.bin lisp1 rm -f lisp.o test.bin lisp1.o test1.bin

File diff suppressed because it is too large Load diff

View file

@ -8,17 +8,12 @@ unsafe extern "C" {
fn getc() -> SourceIterResult; fn getc() -> SourceIterResult;
fn peekc() -> SourceIterResult; fn peekc() -> SourceIterResult;
fn eval(expr: Object, env: Object) -> Object;
fn init_env() -> Object;
fn parse_next_token() -> Object; fn parse_next_token() -> Object;
#[link_name = "ifile"] #[link_name = "ifile"]
static mut IFILE: Source; static mut IFILE: Source;
#[link_name = "nil"] #[link_name = "nil"]
static NIL: (); static NIL: ();
#[link_name = "env"]
static GENV: ();
} }
use std::ptr::NonNull; use std::ptr::NonNull;
@ -41,59 +36,14 @@ impl Object {
fn is_nil(&self) -> bool { fn is_nil(&self) -> bool {
self.0 as *const () == &raw const NIL self.0 as *const () == &raw const NIL
} }
fn tag(&self) -> u8 {
self.0.addr() as u8 & 0b111
}
fn bits(&self) -> usize {
self.0.addr()
}
fn ptr(&self) -> *mut () {
self.0.map_addr(|addr| addr & !0b111)
}
fn is_g_env(&self) -> bool {
self.0 as *const () == unsafe { *ENV_INIT }.0 as *const ()
}
}
#[derive(Copy, Clone)]
struct DebugObjectExplicitGenv(Object);
impl std::fmt::Debug for DebugObjectExplicitGenv {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#[repr(C)]
struct Cons {
refc: u64,
car: Object,
cdr: Object,
}
if self.0.is_g_env() {
let mut proxy = unsafe {
Cons {
refc: 1,
car: self.0.ptr().byte_add(8).cast::<Object>().read(),
cdr: self.0.ptr().byte_add(16).cast::<Object>().read(),
}
};
let obj =
Object((&raw const proxy as *mut ()).map_addr(|addr| addr | Object::CONS as usize));
write!(f, "{:?}", obj)
} else {
return write!(f, "{:?}", self.0);
}
}
} }
impl std::fmt::Debug for Object { impl std::fmt::Debug for Object {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_nil() { if self.0 as *const () == &raw const NIL {
return write!(f, "nil"); return write!(f, "nil");
} }
if self.is_g_env() {
return write!(f, "#<g_env>");
}
fn fmt_byte(byte: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt_byte(byte: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let c = unsafe { byte.cast::<u8>().read() }; let c = unsafe { byte.cast::<u8>().read() };
if c.is_ascii_graphic() || c == b' ' { if c.is_ascii_graphic() || c == b' ' {
@ -109,8 +59,8 @@ impl std::fmt::Debug for Object {
} }
fn fmt_prim(prim: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt_prim(prim: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let prim_ptr = unsafe { prim.cast::<*const ()>().read() }; let prim_ptr = unsafe { prim.cast::<usize>().read() };
write!(f, "#<prim {:?}>", prim_ptr) write!(f, "#<prim {}>", prim_ptr)
} }
fn fmt_cons(cons: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt_cons(cons: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -140,7 +90,7 @@ impl std::fmt::Debug for Object {
let (car, env) = unsafe { clos.cast::<(Object, Object)>().read() }; let (car, env) = unsafe { clos.cast::<(Object, Object)>().read() };
assert_eq!( assert_eq!(
car.tag(), car.tag(),
Object::CONS, 1,
"Expected a pair for lambda cdr, got tag {}", "Expected a pair for lambda cdr, got tag {}",
car.tag() car.tag()
); );
@ -160,7 +110,6 @@ impl std::fmt::Debug for Object {
atom.byte_add(8).cast::<usize>().read(), atom.byte_add(8).cast::<usize>().read(),
) )
}; };
let slice = unsafe { core::slice::from_raw_parts(ptr, len) }; let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>"); let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>");
write!(f, "'{string}") write!(f, "'{string}")
@ -233,6 +182,18 @@ impl std::fmt::Debug for Object {
} }
} }
impl Object {
fn tag(&self) -> u8 {
self.0.addr() as u8 & 0b111
}
fn bits(&self) -> usize {
self.0.addr()
}
fn ptr(&self) -> *mut () {
self.0.map_addr(|addr| addr & !0b111)
}
}
#[repr(C)] #[repr(C)]
struct Source { struct Source {
fd: i32, fd: i32,
@ -282,9 +243,6 @@ unsafe impl std::alloc::Allocator for HeapAlloc {
} }
} }
static mut ENV_INIT: std::cell::LazyCell<Object> =
std::cell::LazyCell::new(|| unsafe { init_env() });
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -292,71 +250,61 @@ mod tests {
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
#[test] // #[test]
fn test_heap_alloc_dealloc() { // fn test_heap_alloc_dealloc() {
let small = Box::new_in(42u8, HeapAlloc); // let small = Box::new_in(42u8, HeapAlloc);
let ptr_sized = Box::new_in(42usize, HeapAlloc); // let ptr_sized = Box::new_in(42usize, HeapAlloc);
let big = Box::new_in([0u8; 48], HeapAlloc); // let big = Box::new_in([0u8; 48], HeapAlloc);
let large = Box::new_in([0u8; 1024], HeapAlloc); // let large = Box::new_in([0u8; 1024], HeapAlloc);
let page_sized = Box::new_in([0u8; 4096], HeapAlloc); // let page_sized = Box::new_in([0u8; 4096], HeapAlloc);
assert_eq!(*small, 42); // assert_eq!(*small, 42);
assert_ne!( // assert_ne!(
Box::as_ptr(&small) as *const (), // Box::as_ptr(&small) as *const (),
Box::as_ptr(&ptr_sized) as *const () // Box::as_ptr(&ptr_sized) as *const ()
); // );
let small_ptr = Box::as_ptr(&small) as *const (); // let small_ptr = Box::as_ptr(&small) as *const ();
drop(small); // drop(small);
let small2 = Box::new_in(43u8, HeapAlloc); // let small2 = Box::new_in(43u8, HeapAlloc);
assert_eq!(*small2, 43); // assert_eq!(*small2, 43);
assert_eq!(small_ptr, Box::as_ptr(&small2) as *const ()); // assert_eq!(small_ptr, Box::as_ptr(&small2) as *const ());
} // }
#[test] // #[test]
fn source() { // fn source() {
let mut file = std::fs::File::open("lisp1.asm").unwrap(); // let mut file = std::fs::File::open("lisp1.asm").unwrap();
unsafe { // unsafe {
init_source(&raw mut IFILE, file.as_raw_fd()); // init_source(&raw mut IFILE, file.as_raw_fd());
let SourceIterResult { c, some } = getc(); // let SourceIterResult { c, some } = getc();
assert!(*some); // assert!(*some);
assert_eq!(c, b'd'); // assert_eq!(c, b'd');
let SourceIterResult { c, some } = peekc(); // let SourceIterResult { c, some } = peekc();
assert!(*some); // assert!(*some);
assert_eq!(c, b'e'); // assert_eq!(c, b'e');
let SourceIterResult { c, some } = getc(); // let SourceIterResult { c, some } = getc();
assert!(*some); // assert!(*some);
assert_eq!(c, b'e'); // assert_eq!(c, b'e');
} // }
} // }
#[test] // #[test]
fn parse_list() { // fn parse_list() {
let file = ManuallyDrop::new(File::open("tests/list.l").unwrap()); // let file = ManuallyDrop::new(File::open("tests/list.l").unwrap());
unsafe { // unsafe {
init_source(&raw mut IFILE, file.as_raw_fd()); // init_source(&raw mut IFILE, file.as_raw_fd());
let obj = parse_next_token(); // let obj = parse_next_token();
println!("{:?}", obj); // println!("{:?}", obj);
} // }
} // }
#[test] // #[test]
fn parse_quoted_list() { // fn parse_quoted_list() {
let file = ManuallyDrop::new(File::open("tests/quoted-list.l").unwrap()); // let file = ManuallyDrop::new(File::open("tests/quoted-list.l").unwrap());
unsafe { // unsafe {
init_source(&raw mut IFILE, file.as_raw_fd()); // init_source(&raw mut IFILE, file.as_raw_fd());
let obj = parse_next_token(); // let obj = parse_next_token();
println!("{:?}", obj); // println!("{:?}", obj);
} // }
} // }
#[test]
fn parse_string() {
let file = ManuallyDrop::new(File::open("tests/string.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let obj = parse_next_token();
println!("{:?}", obj);
}
}
#[test] #[test]
fn parse_atom() { fn parse_atom() {
@ -368,201 +316,23 @@ mod tests {
} }
} }
#[test] // #[test]
fn parse_pair() { // fn parse_pair() {
let file = ManuallyDrop::new(File::open("tests/pair.l").unwrap()); // let file = ManuallyDrop::new(File::open("tests/pair.l").unwrap());
unsafe { // unsafe {
init_source(&raw mut IFILE, file.as_raw_fd()); // init_source(&raw mut IFILE, file.as_raw_fd());
let obj = parse_next_token(); // let obj = parse_next_token();
println!("{:?}", obj); // println!("{:?}", obj);
} // }
} // }
#[test] // #[test]
fn parse_big_number() { // fn parse_big_number() {
let file = ManuallyDrop::new(File::open("tests/big-number.l").unwrap()); // let file = ManuallyDrop::new(File::open("tests/big-number.l").unwrap());
unsafe { // unsafe {
init_source(&raw mut IFILE, file.as_raw_fd()); // init_source(&raw mut IFILE, file.as_raw_fd());
let obj = parse_next_token(); // let obj = parse_next_token();
println!("{:?}", obj); // println!("{:?}", obj);
} // }
} // }
#[test]
fn global_env() {
unsafe {
let env = unsafe { *ENV_INIT };
println!("{:?}", DebugObjectExplicitGenv(env));
}
}
#[test]
fn eval_add() {
let file = ManuallyDrop::new(File::open("tests/add.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_let() {
let file = ManuallyDrop::new(File::open("tests/let.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_letstar() {
let file = ManuallyDrop::new(File::open("tests/letstar.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_print() {
let file = ManuallyDrop::new(File::open("tests/print.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_if() {
let file = ManuallyDrop::new(File::open("tests/if.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_define() {
let file = ManuallyDrop::new(File::open("tests/define.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_typeof() {
let file = ManuallyDrop::new(File::open("tests/typeof.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_eval() {
let file = ManuallyDrop::new(File::open("tests/eval.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_comment() {
let file = ManuallyDrop::new(File::open("tests/comment.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
eprintln!("parsed: {:?}", expr);
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_mapcar() {
let file = ManuallyDrop::new(File::open("tests/mapcar.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_setq() {
let file = ManuallyDrop::new(File::open("tests/setq.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn eval_equal() {
let file = ManuallyDrop::new(File::open("tests/equal.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
#[test]
fn complex() {
let file = ManuallyDrop::new(File::open("tests/print-args.l").unwrap());
unsafe {
init_source(&raw mut IFILE, file.as_raw_fd());
let expr = parse_next_token();
let env = unsafe { *ENV_INIT };
let result = eval(expr, env);
eprint!("done: ");
eprintln!("{result:?}");
}
}
} }

View file

@ -1 +0,0 @@
(+ 1 2 3)

View file

@ -1,2 +0,0 @@
;; this is a comment
(define x 10) ; this is another comment

View file

@ -1,6 +0,0 @@
(progn
(define gt (lambda (a b)
(if (< b a)
't ())))
(gt 1 2)
)

View file

@ -1 +0,0 @@
(= 1 1 1 1)

View file

@ -1,3 +0,0 @@
(let ((x 10)
(y 20))
(eval '(cons x y)))

View file

@ -1 +0,0 @@
(if () 1 2)

View file

@ -1,3 +0,0 @@
(let ((x 10)
(y 20))
(+ x y))

View file

@ -1,3 +0,0 @@
(let* ((x 10)
(y (+ x 10)))
(+ x y))

View file

@ -1,4 +0,0 @@
(progn
(define inc (lambda (x) (+ x 1)))
(mapcar 'inc '(1 2 3 4 5))
)

View file

@ -1,27 +0,0 @@
(define print
(lambda (msg)
(let*
((msg-parts (str-parts msg))
(ptr (car msg-parts))
(len (cdr msg-parts))
(fd 1))
(syscall 1 fd ptr len)
)))
(define println
(lambda (msg)
(progn
(print msg)
(print "\n"))))
(define print-list (lambda (args)
(if (nil? args)
()
(mapcar 'println args))))
;; (print-list argv)
(println "Arguments: ")
(progn
(print-list argv)
)

View file

@ -1,7 +0,0 @@
(let*
((msg "Hello, World!\n")
(msg-parts (str-parts msg))
(ptr (car msg-parts))
(len (cdr msg-parts))
(fd 1))
(syscall 1 fd ptr len))

View file

@ -1,5 +0,0 @@
(progn
(define x 10)
(setq x 20)
(eval 'x)
)

View file

@ -1 +0,0 @@
("hello, World!" \a \b \NL \Space)

View file

@ -1,12 +0,0 @@
(progn
(define exit (lambda (code)
(syscall 60 code)))
(define assert (lambda (cond)
(if (nil? cond)
(exit 1) ())))
(assert (= (typeof 1) 'NUMBER))
(assert (= (typeof 'symbol) 'ATOM))
(assert (= (typeof '()) 'NIL))
(assert (= (typeof '(1 2 3)) 'CONS))
(assert (= (typeof exit) 'CLOSURE))
)