From 9e6c0a92dbedb5b8801772802e2e5d2e56cb9bcf Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Tue, 14 Sep 2021 00:19:40 +0200 Subject: ch3: added LLVM IR code gen - Added safe wrapper around LLVM C API - Added codegen module to emit LLVM IR for the AST - Update the main repl loop to codegen LLVM IR --- src/lib.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/lib.rs (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..64721a7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,109 @@ +use std::convert::TryFrom; + +pub mod codegen; +pub mod lexer; +pub mod llvm; +pub mod parser; + +/// Fixed size of [`SmallCStr`] including the trailing `\0` byte. +pub const SMALL_STR_SIZE: usize = 16; + +/// Small C string on the stack with fixed size [`SMALL_STR_SIZE`]. +#[derive(Debug, PartialEq)] +pub struct SmallCStr([u8; SMALL_STR_SIZE]); + +impl SmallCStr { + /// Create a new C string from `src`. + /// Returns [`None`] if `src` exceeds the fixed size or contains any `\0` bytes. + pub fn new>(src: &T) -> Option { + let src = src.as_ref(); + let len = src.len(); + + // Check for \0 bytes. + let contains_null = unsafe { !libc::memchr(src.as_ptr().cast(), 0, len).is_null() }; + + if contains_null || len > SMALL_STR_SIZE - 1 { + None + } else { + let mut dest = [0; SMALL_STR_SIZE]; + dest[..len].copy_from_slice(src); + Some(SmallCStr(dest)) + } + } + + /// Return pointer to C string. + pub const fn as_ptr(&self) -> *const libc::c_char { + self.0.as_ptr().cast() + } +} + +impl TryFrom<&str> for SmallCStr { + type Error = (); + + fn try_from(value: &str) -> Result { + SmallCStr::new(&value).ok_or(()) + } +} + +/// Either type, for APIs accepting two types. +pub enum Either { + A(A), + B(B), +} + +#[cfg(test)] +mod test { + use super::{SmallCStr, SMALL_STR_SIZE}; + use std::convert::TryInto; + + #[test] + fn test_create() { + let src = "\x30\x31\x32\x33"; + let scs = SmallCStr::new(&src).unwrap(); + assert_eq!(&scs.0[..5], &[0x30, 0x31, 0x32, 0x33, 0x00]); + + let src = b"abcd1234"; + let scs = SmallCStr::new(&src).unwrap(); + assert_eq!( + &scs.0[..9], + &[0x61, 0x62, 0x63, 0x64, 0x31, 0x32, 0x33, 0x34, 0x00] + ); + } + + #[test] + fn test_contain_null() { + let src = "\x30\x00\x32\x33"; + let scs = SmallCStr::new(&src); + assert_eq!(scs, None); + + let src = "\x30\x31\x32\x33\x00"; + let scs = SmallCStr::new(&src); + assert_eq!(scs, None); + } + + #[test] + fn test_too_large() { + let src = (0..SMALL_STR_SIZE).map(|_| 'a').collect::(); + let scs = SmallCStr::new(&src); + assert_eq!(scs, None); + + let src = (0..SMALL_STR_SIZE + 10).map(|_| 'a').collect::(); + let scs = SmallCStr::new(&src); + assert_eq!(scs, None); + } + + #[test] + fn test_try_into() { + let src = "\x30\x31\x32\x33"; + let scs: Result = src.try_into(); + assert!(scs.is_ok()); + + let src = (0..SMALL_STR_SIZE).map(|_| 'a').collect::(); + let scs: Result = src.as_str().try_into(); + assert!(scs.is_err()); + + let src = (0..SMALL_STR_SIZE + 10).map(|_| 'a').collect::(); + let scs: Result = src.as_str().try_into(); + assert!(scs.is_err()); + } +} -- cgit v1.2.3