Compare commits
10 commits
87be0c0c33
...
75d765c905
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75d765c905 | ||
|
|
dd8d005e5a | ||
|
|
26c219a2f7 | ||
|
|
e287ace328 | ||
|
|
02551fe706 | ||
|
|
224174a418 | ||
|
|
4e0719f891 | ||
|
|
52950c255a | ||
|
|
2b447b5517 | ||
|
|
8d4769ad2b |
|
|
@ -1,20 +1,23 @@
|
||||||
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
|
||||||
|
|
||||||
test1.bin: test.rs lisp1.o
|
test.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
|
||||||
|
|
||||||
test1: test1.bin
|
test: test.bin
|
||||||
./test1.bin
|
./test.bin --nocapture --test-threads=1
|
||||||
|
|
||||||
|
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
|
rm -f lisp.o test.bin lisp1.o test1.bin lisp1
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -8,12 +8,17 @@ 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;
|
||||||
|
|
@ -36,14 +41,59 @@ 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.0 as *const () == &raw const NIL {
|
if self.is_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' ' {
|
||||||
|
|
@ -59,8 +109,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::<usize>().read() };
|
let prim_ptr = unsafe { prim.cast::<*const ()>().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 {
|
||||||
|
|
@ -90,7 +140,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(),
|
||||||
1,
|
Object::CONS,
|
||||||
"Expected a pair for lambda cdr, got tag {}",
|
"Expected a pair for lambda cdr, got tag {}",
|
||||||
car.tag()
|
car.tag()
|
||||||
);
|
);
|
||||||
|
|
@ -110,6 +160,7 @@ 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}")
|
||||||
|
|
@ -182,18 +233,6 @@ 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,
|
||||||
|
|
@ -243,6 +282,9 @@ 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::*;
|
||||||
|
|
@ -250,61 +292,71 @@ 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() {
|
||||||
|
|
@ -316,23 +368,201 @@ 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:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
stages/lisp0/tests/add.l
Normal file
1
stages/lisp0/tests/add.l
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(+ 1 2 3)
|
||||||
2
stages/lisp0/tests/comment.l
Normal file
2
stages/lisp0/tests/comment.l
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
;; this is a comment
|
||||||
|
(define x 10) ; this is another comment
|
||||||
6
stages/lisp0/tests/define.l
Normal file
6
stages/lisp0/tests/define.l
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
(progn
|
||||||
|
(define gt (lambda (a b)
|
||||||
|
(if (< b a)
|
||||||
|
't ())))
|
||||||
|
(gt 1 2)
|
||||||
|
)
|
||||||
1
stages/lisp0/tests/equal.l
Normal file
1
stages/lisp0/tests/equal.l
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(= 1 1 1 1)
|
||||||
3
stages/lisp0/tests/eval.l
Normal file
3
stages/lisp0/tests/eval.l
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
(let ((x 10)
|
||||||
|
(y 20))
|
||||||
|
(eval '(cons x y)))
|
||||||
1
stages/lisp0/tests/if.l
Normal file
1
stages/lisp0/tests/if.l
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(if () 1 2)
|
||||||
3
stages/lisp0/tests/let.l
Normal file
3
stages/lisp0/tests/let.l
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
(let ((x 10)
|
||||||
|
(y 20))
|
||||||
|
(+ x y))
|
||||||
3
stages/lisp0/tests/letstar.l
Normal file
3
stages/lisp0/tests/letstar.l
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
(let* ((x 10)
|
||||||
|
(y (+ x 10)))
|
||||||
|
(+ x y))
|
||||||
4
stages/lisp0/tests/mapcar.l
Normal file
4
stages/lisp0/tests/mapcar.l
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
(progn
|
||||||
|
(define inc (lambda (x) (+ x 1)))
|
||||||
|
(mapcar 'inc '(1 2 3 4 5))
|
||||||
|
)
|
||||||
27
stages/lisp0/tests/print-args.l
Normal file
27
stages/lisp0/tests/print-args.l
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
(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)
|
||||||
|
)
|
||||||
|
|
||||||
7
stages/lisp0/tests/print.l
Normal file
7
stages/lisp0/tests/print.l
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
(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))
|
||||||
5
stages/lisp0/tests/setq.l
Normal file
5
stages/lisp0/tests/setq.l
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
(progn
|
||||||
|
(define x 10)
|
||||||
|
(setq x 20)
|
||||||
|
(eval 'x)
|
||||||
|
)
|
||||||
1
stages/lisp0/tests/string.l
Normal file
1
stages/lisp0/tests/string.l
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
("hello, World!" \a \b \NL \Space)
|
||||||
12
stages/lisp0/tests/typeof.l
Normal file
12
stages/lisp0/tests/typeof.l
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
(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))
|
||||||
|
)
|
||||||
Loading…
Reference in a new issue