diff --git a/lysp/example1.lysp b/lysp/example1.lysp index 2475ed0..2c24482 100644 --- a/lysp/example1.lysp +++ b/lysp/example1.lysp @@ -1 +1,10 @@ ;; vi:ft=lisp:sw=2:ts=2 + +(assert-equal 2 (+ 1 #t #f)) +(assert-equal "test1" (+ "test" 1)) +(assert-equal "1test2" (+ 1 "test" 2)) +(assert-equal 2 (+ #t 1)) +(assert-equal 1 (+ #t #f)) + +(assert-equal "-1234" (int->string -1234) "int->string") +(assert-equal -1234 (string->int "-1234")) diff --git a/src/compile/block.rs b/src/compile/block.rs index 6217049..7a8e4f9 100644 --- a/src/compile/block.rs +++ b/src/compile/block.rs @@ -12,7 +12,7 @@ use crate::{ }, value::{BuiltinFunction, CompileConstant, CompileValue}, }, - vm::instruction::{Instruction, MathInstruction, U}, + vm::instruction::{Instruction, LocalId, MathInstruction, U}, }; pub struct CompiledFunction { @@ -40,7 +40,11 @@ pub struct LocalBlock<'a> { } impl LocalScope { - pub fn get_or_insert(&mut self, value: Rc, visible: bool) -> Result, CompileError> { + pub fn get_or_insert( + &mut self, + value: Rc, + visible: bool, + ) -> Result { let index = if let Some(index) = self.locals.iter().position(|v| v.0 == value) { self.locals[index].1 = visible; index @@ -52,11 +56,11 @@ impl LocalScope { Ok(U::new(index as u32).unwrap()) } - pub fn make_visible(&mut self, index: U<16>) { + pub fn make_visible(&mut self, index: LocalId) { self.locals[usize::from(index) - self.start_index as usize].1 = true; } - pub fn get(&self, value: &str) -> Option> { + pub fn get(&self, value: &str) -> Option { self.locals .iter() .position(|v| v.0.as_ref() == value && v.1) @@ -116,7 +120,7 @@ impl FunctionBlock { self.locals_stack.last() } - fn lookup_local(&self, identifier: &str) -> Option> { + fn lookup_local(&self, identifier: &str) -> Option { for scope in self.locals_stack.iter().rev() { if let Some(index) = scope.get(identifier) { return Some(index); @@ -202,7 +206,7 @@ impl<'a> LocalBlock<'a> { identifier: &Rc, value: CompileValue, visible: bool, - ) -> Result, CompileError> { + ) -> Result { let index = self .function .local_scope_mut() @@ -225,6 +229,10 @@ impl<'a> LocalBlock<'a> { self.function.emit(Instruction::PushConstant(value)); self.function.emit(Instruction::GetGlobal); } + CompileValue::String(value) => { + let index = self.module.constant(CompileConstant::String(value))?; + self.function.emit(Instruction::PushConstant(index)); + } CompileValue::Local(index) => { self.function.emit(Instruction::GetLocal(index)); } @@ -232,11 +240,11 @@ impl<'a> LocalBlock<'a> { self.function.emit(Instruction::PushBool(value)); } CompileValue::Integer(value) => { - // TODO signed/unsigned - if let Some(value) = U::new(value as u32) { + if let Some(value) = U::from_signed(value) { self.function.emit(Instruction::PushInteger(value)); } else { - todo!() + let index = self.module.constant(CompileConstant::Integer(value))?; + self.function.emit(Instruction::PushConstant(index)); } } CompileValue::Argument(index) => { @@ -452,6 +460,7 @@ impl<'a> LocalBlock<'a> { ) -> Result { match expression { Expression::Nil => Ok(CompileValue::Nil), + Expression::StringLiteral(value) => Ok(CompileValue::String(value.clone())), Expression::BooleanLiteral(value) => Ok(CompileValue::Boolean(*value)), Expression::IntegerLiteral(value) => Ok(CompileValue::Integer(*value)), Expression::Identifier(identifier) => self.compile_identifier(identifier), diff --git a/src/compile/module.rs b/src/compile/module.rs index ac7813e..6d4bac4 100644 --- a/src/compile/module.rs +++ b/src/compile/module.rs @@ -77,6 +77,7 @@ impl CompilationModule { let address = *function_offsets.get(&index).unwrap(); ModuleConstant::LocalFunction(address, required_count) } + CompileConstant::String(value) => ModuleConstant::String(value), }, ) }) diff --git a/src/compile/syntax/mod.rs b/src/compile/syntax/mod.rs index 28ff932..805f720 100644 --- a/src/compile/syntax/mod.rs +++ b/src/compile/syntax/mod.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use crate::vm::value::{ConsCell, Keyword, Value}; +use crate::vm::value::{ConsCell, Keyword, Value, ValueString}; mod binding; mod call; @@ -20,6 +20,7 @@ pub use lambda::*; pub enum Expression { Nil, BooleanLiteral(bool), + StringLiteral(ValueString), IntegerLiteral(i64), Identifier(Rc), Lambda(LambdaExpression), @@ -55,6 +56,8 @@ impl Expression { Value::Nil => Self::Nil, Value::Boolean(value) => Self::BooleanLiteral(*value), Value::Integer(value) => Self::IntegerLiteral(*value), + Value::String(value) => Self::StringLiteral(value.clone()), + Value::Vector(_vector) => todo!(), Value::Identifier(value) => Self::Identifier(value.clone()), Value::Cons(cons) => { let ConsCell(car, cdr) = cons.as_ref(); @@ -102,9 +105,11 @@ impl CollectErrors for Expression { Self::Call(call) => call.collect_errors(errors), Self::Let(let_) => let_.collect_errors(errors), Self::Setq(setq) => setq.collect_errors(errors), - Self::Nil | Self::IntegerLiteral(_) | Self::Identifier(_) | Self::BooleanLiteral(_) => { - false - } + Self::Nil + | Self::IntegerLiteral(_) + | Self::Identifier(_) + | Self::BooleanLiteral(_) + | Self::StringLiteral(_) => false, } } } diff --git a/src/compile/value.rs b/src/compile/value.rs index 130b566..685a976 100644 --- a/src/compile/value.rs +++ b/src/compile/value.rs @@ -1,13 +1,17 @@ use std::rc::Rc; -use crate::vm::instruction::{MathInstruction, U}; +use crate::vm::{ + instruction::{LocalId, MathInstruction}, + value::ValueString, +}; #[derive(Debug, PartialEq)] pub enum CompileValue { Integer(i64), + String(ValueString), Boolean(bool), LocalFunction(u32, usize), - Local(U<16>), + Local(LocalId), Argument(usize), Global(Rc), Stack, @@ -19,6 +23,7 @@ pub enum CompileConstant { Integer(i64), LocalFunction(u32, usize), Identifier(Rc), + String(ValueString), } #[derive(Debug, PartialEq, Clone, Copy)] diff --git a/src/convert.rs b/src/convert.rs index a693f95..7a66ee6 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -1,7 +1,7 @@ use crate::vm::{ machine::{Machine, MachineError}, native::NativeFunction, - value::{BytecodeFunction, ConsCell, Keyword, Value}, + value::{BytecodeFunction, ConsCell, Keyword, Value, ValueString}, }; pub enum AnyFunction { @@ -41,6 +41,8 @@ macro_rules! impl_primitive_value { match value { #[allow(irrefutable_let_patterns)] &Value::Integer(value) if let Ok(value) = value.try_into() => Ok(value), + &Value::Boolean(true) => Ok(1), + &Value::Boolean(false) => Ok(0), _ => Err(MachineError::InvalidArgument) } } @@ -72,6 +74,8 @@ impl TryFromValue<'_> for bool { match value { Value::Nil => Ok(false), Value::Cons(_) => Ok(true), + Value::Vector(vector) => Ok(!vector.is_empty()), + Value::String(value) => Ok(!value.is_empty()), Value::Boolean(value) => Ok(*value), Value::Integer(value) => Ok(*value != 0), Value::Keyword(_) @@ -82,6 +86,25 @@ impl TryFromValue<'_> for bool { } } } +impl From for Value { + fn from(value: bool) -> Self { + Self::Boolean(value) + } +} + +impl TryFromValue<'_> for ValueString { + fn try_from_value(value: &'_ Value) -> Result { + match value { + Value::String(value) => Ok(value.clone()), + _ => Err(MachineError::InvalidArgument), + } + } +} +impl From for Value { + fn from(value: ValueString) -> Self { + Self::String(value) + } +} impl TryFromValue<'_> for Value { fn try_from_value(value: &Value) -> Result { diff --git a/src/parse.rs b/src/parse.rs index 0680b9b..5fa4ef3 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,14 +1,14 @@ use nom::{ AsChar, FindToken, IResult, Input, Parser, branch::alt, - bytes::streaming::tag, + bytes::streaming::{is_not, tag}, character::{ anychar, streaming::{char, one_of}, }, - combinator::{map, map_res, opt, recognize, value}, + combinator::{map, map_res, opt, recognize, value, verify}, error::{Error, ErrorKind, FromExternalError, ParseError}, - multi::{fold_many1, many0}, + multi::{fold, fold_many1, many0}, sequence::{delimited, preceded}, }; @@ -190,6 +190,58 @@ fn parse_boolean(input: &str) -> IResult<&str, Value> { .parse(input) } +enum StringSegment<'a> { + Literal(&'a str), + Escape(char), +} + +fn parse_literal_string_segment(input: &str) -> IResult<&str, &str> { + verify(is_not("\"\\"), |s: &str| !s.is_empty()).parse(input) +} + +fn parse_escaped_char(input: &str) -> IResult<&str, char> { + preceded( + char('\\'), + alt(( + value('\n', char('n')), + value('\r', char('r')), + value('\t', char('t')), + value('\\', char('\\')), + value('"', char('"')), + )), + ) + .parse(input) +} + +fn parse_string_segment(input: &str) -> IResult<&str, StringSegment<'_>> { + alt(( + map(parse_literal_string_segment, StringSegment::Literal), + map(parse_escaped_char, StringSegment::Escape), + )) + .parse(input) +} + +fn parse_string(input: &str) -> IResult<&str, Value> { + delimited( + char('"'), + fold( + 0.., + parse_string_segment, + String::new, + |mut string, segment| { + match segment { + StringSegment::Literal(s) => string.push_str(s), + StringSegment::Escape(c) => string.push(c), + }; + string + }, + ), + char('"'), + ) + .map(|s| Value::String(s.into())) + .parse(input) +} + pub fn skip_comment_and_whitespace(mut input: &str) -> IResult<&str, ()> { let mut in_comment = false; while let Some(ch) = input.chars().next() { @@ -225,6 +277,7 @@ pub fn parse_value(input: &str) -> IResult<&str, Value> { parse_list_or_nil, parse_boolean, parse_integer, + parse_string, parse_identifier_or_keyword_or_nil, )) .parse(input) diff --git a/src/vm/instruction.rs b/src/vm/instruction.rs index a743505..e3191d3 100644 --- a/src/vm/instruction.rs +++ b/src/vm/instruction.rs @@ -73,31 +73,39 @@ primitive_enum! { #[derive(Debug, Clone, Copy, PartialEq)] pub enum Instruction { PushNil, - PushInteger(U<24>), + PushInteger(PackedInteger), PushBool(bool), PushConstant(ConstantId), - PushArgument(U<6>), + PushArgument(ArgumentId), Drop, SetGlobal, GetGlobal, - SetLocal(U<16>), - GetLocal(U<16>), - Call(U<6>), + SetLocal(LocalId), + GetLocal(LocalId), + Call(ArgumentCount), Return, - Math(MathInstruction, U<6>), - Branch(U<12>), - Jump(U<12>), + Math(MathInstruction, ArgumentCount), + Branch(FunctionOffset), + Jump(FunctionOffset), } -pub type ConstantId = U<24>; +pub type ConstantId = U<12>; +pub type FunctionOffset = U<12>; +pub type LocalId = U<8>; +pub type ArgumentId = U<6>; +pub type ArgumentCount = U<6>; +pub type PackedInteger = U<12>; impl U { pub const BITS: usize = N; pub const ZERO: Self = Self(0); + const UNSIGNED_MASK: i64 = (1 << (N - 1)) - 1; + const SIGNED_MASK: i64 = (1 << N) - 1; + pub fn sign_extend_i64(&self) -> i64 { - if self.0 & (1 << N) != 0 { - todo!() + if self.0 & (1 << (N - 1)) != 0 { + self.0 as i64 | !Self::SIGNED_MASK } else { self.0 as i64 } @@ -111,6 +119,16 @@ impl U { } } + pub fn from_signed(value: i64) -> Option { + if value > 0 && value & !Self::UNSIGNED_MASK == 0 { + Some(Self(value as u32)) + } else if value < 0 && value & !Self::SIGNED_MASK == !Self::SIGNED_MASK { + Some(Self((value & Self::SIGNED_MASK) as u32)) + } else { + None + } + } + pub const fn truncate(value: u32) -> Self { Self(value & ((1 << N) - 1)) } @@ -141,22 +159,22 @@ impl From> for u32 { impl From for u32 { fn from(instruction: Instruction) -> u32 { match instruction { - Instruction::Drop => 0b0000_0000_0000_0000, - Instruction::PushNil => 0b0000_0000_0000_0001, - Instruction::PushBool(value) => 0b0000_0000_0000_0010 | (value as u32), - Instruction::Return => 0b0000_0000_0000_0100, - Instruction::SetGlobal => 0b0000_0000_0000_0101, - Instruction::GetGlobal => 0b0000_0000_0000_0110, - Instruction::GetLocal(value) => 0b0000_0010_0000_0000 | value.0, - Instruction::SetLocal(value) => 0b0000_0011_0000_0000 | value.0, - Instruction::Call(count) => 0b0000_0000_0100_0000 | count.0, - Instruction::PushInteger(value) => 0b0001_0000_0000_0000 | value.0, - Instruction::PushConstant(index) => 0b0010_0000_0000_0000 | index.0, - Instruction::PushArgument(index) => 0b0000_0000_1000_0000 | index.0, - Instruction::Branch(offset) => 0b0000_1000_0000_0000 | offset.0, - Instruction::Jump(offset) => 0b0000_1100_0000_0000 | offset.0, + Instruction::Drop => 0b00000000_00000000, + Instruction::PushNil => 0b00000000_00000001, + Instruction::PushBool(value) => 0b00000000_00000010 | (value as u32), + Instruction::Return => 0b00000000_00000100, + Instruction::SetGlobal => 0b00000000_00000101, + Instruction::GetGlobal => 0b00000000_00000110, + Instruction::GetLocal(value) => 0b00000010_00000000 | value.0, + Instruction::SetLocal(value) => 0b00000011_00000000 | value.0, + Instruction::Call(count) => 0b00000000_01000000 | count.0, + Instruction::PushInteger(value) => 0b00010000_00000000 | value.0, + Instruction::PushConstant(index) => 0b00100000_00000000 | index.0, + Instruction::PushArgument(index) => 0b00000000_10000000 | index.0, + Instruction::Branch(offset) => 0b00001000_00000000 | offset.0, + Instruction::Jump(offset) => 0b00001100_00000000 | offset.0, Instruction::Math(math, count) => { - 0b0000_0100_0000_0000 | (u32::from(math) << 6) | count.0 + 0b00000100_00000000 | (u32::from(math) << 6) | count.0 } } } @@ -169,33 +187,53 @@ impl TryFrom for Instruction { fn try_from(value: u32) -> Result { #[bitmatch] match value { - "0000_0000_0000_0000" => Ok(Instruction::Drop), - "0000_0000_0000_0001" => Ok(Instruction::PushNil), - "0000_0000_0000_001x" => Ok(Instruction::PushBool(x != 0)), - "0000_0000_0000_0100" => Ok(Instruction::Return), - "0000_0000_0000_0101" => Ok(Instruction::SetGlobal), - "0000_0000_0000_0110" => Ok(Instruction::GetGlobal), - "0000_0000_0000_0111" => todo!(), - "0000_0000_0000_1???" => todo!(), + "00000000_00000000" => Ok(Instruction::Drop), + "00000000_00000001" => Ok(Instruction::PushNil), + "00000000_0000001x" => Ok(Instruction::PushBool(x != 0)), + "00000000_00000100" => Ok(Instruction::Return), + "00000000_00000101" => Ok(Instruction::SetGlobal), + "00000000_00000110" => Ok(Instruction::GetGlobal), + "00000000_00000111" => todo!(), + "00000000_00001???" => todo!(), - "0000_0000_0001_????" => todo!(), - "0000_0000_001?_????" => todo!(), - "0000_0000_01xx_xxxx" => Ok(Instruction::Call(U(x))), - "0000_0000_10xx_xxxx" => Ok(Instruction::PushArgument(U(x))), - "0000_0000_11??_????" => todo!(), + "00000000_0001????" => todo!(), + "00000000_001?????" => todo!(), + "00000000_01xxxxxx" => Ok(Instruction::Call(U(x))), + "00000000_10xxxxxx" => Ok(Instruction::PushArgument(U(x))), + "00000000_11??????" => todo!(), - "0000_0001_????_????" => todo!(), - "0000_0010_xxxx_xxxx" => Ok(Instruction::GetLocal(U(x))), - "0000_0011_xxxx_xxxx" => Ok(Instruction::SetLocal(U(x))), - "0000_01xx_xxyy_yyyy" => Ok(Instruction::Math(x.try_into()?, U(y))), - "0000_10xx_xxxx_xxxx" => Ok(Instruction::Branch(U(x))), - "0000_11xx_xxxx_xxxx" => Ok(Instruction::Jump(U(x))), + "00000001_????????" => todo!(), + "00000010_xxxxxxxx" => Ok(Instruction::GetLocal(U(x))), + "00000011_xxxxxxxx" => Ok(Instruction::SetLocal(U(x))), + "000001xx_xxyyyyyy" => Ok(Instruction::Math(x.try_into()?, U(y))), + "000010xx_xxxxxxxx" => Ok(Instruction::Branch(U(x))), + "000011xx_xxxxxxxx" => Ok(Instruction::Jump(U(x))), - "0001_xxxx_xxxx_xxxx" => Ok(Instruction::PushInteger(U(x))), - "0010_xxxx_xxxx_xxxx" => Ok(Instruction::PushConstant(U(x))), - "0011_????_????_????" => todo!(), - "01??_????_????_????" => todo!(), - "1???_????_????_????" => todo!(), + "0001xxxx_xxxxxxxx" => Ok(Instruction::PushInteger(U(x))), + "0010xxxx_xxxxxxxx" => Ok(Instruction::PushConstant(U(x))), + "0011????_????????" => todo!(), + "01??????_????????" => todo!(), + "1???????_????????" => todo!("{value:032b}"), } } } + +#[cfg(test)] +mod tests { + use crate::vm::instruction::U; + + #[test] + fn test_u_convert() { + type T = U<24>; + let t = T::from_signed(-1234).unwrap(); + assert_eq!(t.0, (-1234i64 & 0xFFFFFF) as u32); + assert_eq!(t.sign_extend_i64(), -1234); + let t = T::from_signed(1234).unwrap(); + assert_eq!(t.0, 1234); + + let t = T::from_signed(0x800000); + assert!(t.is_none()); + let t = T::from_signed(-0x1000001); + assert!(t.is_none()); + } +} diff --git a/src/vm/machine.rs b/src/vm/machine.rs index 15e6b79..8cb14e3 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -301,6 +301,7 @@ impl Machine { address, }) } + ModuleConstant::String(value) => Value::String(value), ModuleConstant::Integer(value) => Value::Integer(value), ModuleConstant::Identifier(identifier) => Value::Identifier(identifier), }; diff --git a/src/vm/module.rs b/src/vm/module.rs index 2281018..6fabdd9 100644 --- a/src/vm/module.rs +++ b/src/vm/module.rs @@ -5,7 +5,7 @@ use crate::{ vm::{ instruction::{ConstantId, Instruction}, pool::Pool, - value::Value, + value::{Value, ValueString}, }, }; @@ -14,6 +14,7 @@ pub enum ModuleConstant { Integer(i64), LocalFunction(usize, usize), Identifier(Rc), + String(ValueString), } #[derive(Clone)] @@ -188,6 +189,7 @@ impl ModuleBuilder { impl fmt::Display for ModuleConstant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Self::String(value) => write!(f, "string {value:?}"), Self::Integer(value) => fmt::Display::fmt(value, f), Self::Identifier(ident) => write!(f, "ident {ident:?}"), Self::LocalFunction(address, _) => write!(f, "label {address}"), diff --git a/src/vm/prelude.rs b/src/vm/prelude/math.rs similarity index 54% rename from src/vm/prelude.rs rename to src/vm/prelude/math.rs index 84e9e26..97a85d8 100644 --- a/src/vm/prelude.rs +++ b/src/vm/prelude/math.rs @@ -1,18 +1,34 @@ use std::{ cmp::Ordering, ops::{BitAnd, BitOr, BitXor}, - slice, }; use crate::{ - convert::{AnyFunction, TryFromValue}, - util::IteratorExt, + convert::TryFromValue, vm::{ machine::{Machine, MachineError}, value::Value, }, }; +fn value_add(a: &Value, b: &Value) -> Result { + match (a, b) { + (Value::String(a), _) => { + let b = b.stringify()?; + Ok(Value::String(format!("{a}{b}").into())) + } + (_, Value::String(b)) => { + let a = a.stringify()?; + Ok(Value::String(format!("{a}{b}").into())) + } + (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_add(*b))), + (Value::Integer(a), _) => Ok(Value::Integer(a.wrapping_add(i64::try_from_value(b)?))), + (_, Value::Integer(b)) => Ok(Value::Integer(i64::try_from_value(a)?.wrapping_add(*b))), + (Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Integer(*a as i64 + *b as i64)), + _ => Err(MachineError::InvalidArgument), + } +} + pub(crate) enum CompareOperation { Eq, Ne, @@ -22,41 +38,38 @@ pub(crate) enum CompareOperation { Ge, } -fn builtin_fold_integer( - fold: F, - mut accumulator: i64, - args: &[Value], -) -> Result +fn builtin_fold(fold: F, mut accumulator: Value, args: &[Value]) -> Result where - F: Fn(i64, i64) -> i64, + F: Fn(&Value, &Value) -> Result, { - for arg in args { - match arg { - &Value::Integer(value) => { - accumulator = fold(accumulator, value); - } - _ => todo!("Math for {arg}"), + for (i, arg) in args.iter().enumerate() { + if i == 0 { + accumulator = arg.clone(); + } else { + accumulator = fold(&accumulator, arg)?; } } - Ok(Value::Integer(accumulator)) + Ok(accumulator) } -fn builtin_fold_bool( + +fn builtin_fold_t<'a, T, F>( fold: F, - mut accumulator: bool, - args: &[Value], + mut accumulator: T, + args: &'a [Value], ) -> Result where - F: Fn(bool, bool) -> bool, + F: Fn(T, T) -> T, + T: TryFromValue<'a> + Into, { for arg in args { - let arg = bool::try_from_value(arg)?; + let arg = T::try_from_value(arg)?; accumulator = fold(accumulator, arg); } - Ok(Value::Boolean(accumulator)) + Ok(accumulator.into()) } pub(crate) fn builtin_add(_vm: &mut Machine, args: &[Value]) -> Result { - builtin_fold_integer(i64::wrapping_add, 0, args) + builtin_fold(value_add, Value::Integer(0), args) } pub(crate) fn builtin_sub(_vm: &mut Machine, args: &[Value]) -> Result { match args { @@ -81,7 +94,7 @@ pub(crate) fn builtin_sub(_vm: &mut Machine, args: &[Value]) -> Result Result { - builtin_fold_integer(i64::wrapping_mul, 1, args) + builtin_fold_t(i64::wrapping_mul, 1, args) } pub(crate) fn builtin_mod(_vm: &mut Machine, args: &[Value]) -> Result { let [x, y] = args else { @@ -116,25 +129,25 @@ pub(crate) fn builtin_div(_vm: &mut Machine, args: &[Value]) -> Result Result { - builtin_fold_bool(BitAnd::bitand, true, args) + builtin_fold_t(BitAnd::bitand, true, args) } pub(crate) fn builtin_or(_vm: &mut Machine, args: &[Value]) -> Result { - builtin_fold_bool(BitOr::bitor, false, args) + builtin_fold_t(BitOr::bitor, false, args) } pub(crate) fn builtin_bitwise_and( _vm: &mut Machine, args: &[Value], ) -> Result { - builtin_fold_integer(BitAnd::bitand, 1, args) + builtin_fold_t(BitAnd::bitand, 1, args) } pub(crate) fn builtin_bitwise_or(_vm: &mut Machine, args: &[Value]) -> Result { - builtin_fold_integer(BitOr::bitor, 0, args) + builtin_fold_t(BitOr::bitor, 0, args) } pub(crate) fn builtin_bitwise_xor( _vm: &mut Machine, args: &[Value], ) -> Result { - builtin_fold_integer(BitXor::bitxor, 0, args) + builtin_fold_t(BitXor::bitxor, 0, args) } pub(crate) fn builtin_cmp( @@ -146,7 +159,8 @@ pub(crate) fn builtin_cmp( match (a, b) { (Value::Integer(a), Value::Integer(b)) => Ord::cmp(a, b), (Value::Boolean(a), Value::Boolean(b)) => Ord::cmp(a, b), - _ => todo!(), + (Value::String(a), Value::String(b)) => Ord::cmp(a, b), + _ => Ordering::Less, } } @@ -188,104 +202,3 @@ pub(crate) fn builtin_cmp_ge(vm: &mut Machine, args: &[Value]) -> Result Result { builtin_cmp(vm, args, CompareOperation::Le) } - -pub fn load(vm: &mut Machine) { - // math - vm.defun_native("+", builtin_add); - vm.defun_native("-", builtin_sub); - vm.defun_native("*", builtin_mul); - vm.defun_native("%", builtin_mod); - vm.defun_native("/", builtin_div); - - vm.defun_native("&&", builtin_and); - vm.defun_native("||", builtin_or); - vm.defun_native("&", builtin_bitwise_and); - vm.defun_native("|", builtin_bitwise_or); - vm.defun_native("^", builtin_bitwise_xor); - - vm.defun_native(">", builtin_cmp_gt); - vm.defun_native("<", builtin_cmp_lt); - vm.defun_native("=", builtin_cmp_eq); - vm.defun_native("/=", builtin_cmp_ne); - vm.defun_native(">=", builtin_cmp_ge); - vm.defun_native("<=", builtin_cmp_le); - - // lists - vm.defun_native("map", |vm, args| { - let [f, xs] = args else { - return Err(MachineError::InvalidArgument); - }; - let f = AnyFunction::try_from_value(f)?; - let xs = xs.proper_iter(MachineError::InvalidArgument); - let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, slice::from_ref(v?))))?; - Ok(out) - }); - vm.defun_native("filter", |vm, args| { - let [f, xs] = args else { - return Err(MachineError::InvalidArgument); - }; - let f = AnyFunction::try_from_value(f)?; - let xs = xs - .proper_iter(MachineError::InvalidArgument) - .map(|x| x.cloned()); - let out = Value::try_list_or_nil(xs.try_filter(|v| { - let result = f.invoke(vm, slice::from_ref(v))?; - bool::try_from_value(&result) - }))?; - Ok(out) - }); - vm.defun_native("list", |_, args| { - let out = Value::list_or_nil(args.iter().cloned()); - Ok(out) - }); - - // functional - vm.defun_native("identity", |_, args| { - let [arg] = args else { - return Err(MachineError::InvalidArgument); - }; - Ok(arg.clone()) - }); - - // eval - vm.defun_native("apply", |vm, args| { - let [f, xs] = args else { - return Err(MachineError::InvalidArgument); - }; - let f = AnyFunction::try_from_value(f)?; - let args = xs - .proper_iter(MachineError::InvalidArgument) - .map(|x| x.cloned()) - .collect::, _>>()?; - f.invoke(vm, &args[..]) - }); - vm.defun_native("assert", |vm, args| match args { - [] => Err(MachineError::InvalidArgument), - [cond] => { - let cond = bool::try_from_value(cond)?; - if !cond { - let ip = vm.ip(); - if let Some(ip) = ip { - eprintln!("Assertion failed at {ip}:"); - eprintln!(); - ip.module.dump(Some(ip.address), 8); - } - panic!("Assertion failed"); - } - Ok(Value::Nil) - } - _ => todo!(), - }); - - // io - vm.defun_native("print", |_, args| { - for (i, arg) in args.iter().enumerate() { - if i != 0 { - print!(" "); - } - print!("{arg}"); - } - println!(); - Ok(Value::Nil) - }); -} diff --git a/src/vm/prelude/mod.rs b/src/vm/prelude/mod.rs new file mode 100644 index 0000000..483ff35 --- /dev/null +++ b/src/vm/prelude/mod.rs @@ -0,0 +1,164 @@ +use std::slice; + +use crate::{ + convert::{AnyFunction, TryFromValue}, + util::IteratorExt, + vm::{ + machine::{Machine, MachineError}, + value::{Value, ValueString}, + }, +}; + +mod math; +pub(crate) use math::*; + +pub fn load(vm: &mut Machine) { + // math + vm.defun_native("+", builtin_add); + vm.defun_native("-", builtin_sub); + vm.defun_native("*", builtin_mul); + vm.defun_native("%", builtin_mod); + vm.defun_native("/", builtin_div); + + vm.defun_native("&&", builtin_and); + vm.defun_native("||", builtin_or); + vm.defun_native("&", builtin_bitwise_and); + vm.defun_native("|", builtin_bitwise_or); + vm.defun_native("^", builtin_bitwise_xor); + + vm.defun_native(">", builtin_cmp_gt); + vm.defun_native("<", builtin_cmp_lt); + vm.defun_native("=", builtin_cmp_eq); + vm.defun_native("/=", builtin_cmp_ne); + vm.defun_native(">=", builtin_cmp_ge); + vm.defun_native("<=", builtin_cmp_le); + + // conversion + vm.defun_native("string->int", |_vm, args| { + let [arg] = args else { + return Err(MachineError::InvalidArgument); + }; + let arg = ValueString::try_from_value(arg)?; + let result = arg.parse::().map(Value::Integer).unwrap_or(Value::Nil); + Ok(result) + }); + vm.defun_native("int->string", |_vm, args| { + let [arg] = args else { + return Err(MachineError::InvalidArgument); + }; + let arg = i64::try_from_value(arg)?; + let result = Value::String(format!("{arg}").into()); + Ok(result) + }); + + // lists + vm.defun_native("map", |vm, args| { + let [f, xs] = args else { + return Err(MachineError::InvalidArgument); + }; + let f = AnyFunction::try_from_value(f)?; + let xs = xs.proper_iter(MachineError::InvalidArgument); + let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, slice::from_ref(v?))))?; + Ok(out) + }); + vm.defun_native("filter", |vm, args| { + let [f, xs] = args else { + return Err(MachineError::InvalidArgument); + }; + let f = AnyFunction::try_from_value(f)?; + let xs = xs + .proper_iter(MachineError::InvalidArgument) + .map(|x| x.cloned()); + let out = Value::try_list_or_nil(xs.try_filter(|v| { + let result = f.invoke(vm, slice::from_ref(v))?; + bool::try_from_value(&result) + }))?; + Ok(out) + }); + vm.defun_native("list", |_, args| { + let out = Value::list_or_nil(args.iter().cloned()); + Ok(out) + }); + + // functional + vm.defun_native("identity", |_, args| { + let [arg] = args else { + return Err(MachineError::InvalidArgument); + }; + Ok(arg.clone()) + }); + + // eval + vm.defun_native("apply", |vm, args| { + let [f, xs] = args else { + return Err(MachineError::InvalidArgument); + }; + let f = AnyFunction::try_from_value(f)?; + let args = xs + .proper_iter(MachineError::InvalidArgument) + .map(|x| x.cloned()) + .collect::, _>>()?; + f.invoke(vm, &args[..]) + }); + vm.defun_native("assert", |vm, args| match args { + [] => Err(MachineError::InvalidArgument), + [cond] => { + let cond = bool::try_from_value(cond)?; + if !cond { + let ip = vm.ip(); + if let Some(ip) = ip { + eprintln!("Assertion failed at {ip}:"); + eprintln!(); + ip.module.dump(Some(ip.address), 8); + } + panic!("Assertion failed"); + } + Ok(Value::Nil) + } + _ => todo!(), + }); + vm.defun_native("assert-equal", |vm, args| match args { + [] | [_] => Err(MachineError::InvalidArgument), + [a, b] => { + if a != b { + let ip = vm.ip(); + if let Some(ip) = ip { + eprintln!("Assertion failed at {ip}:"); + eprintln!(); + eprintln!(":: {a} ≠ {b}"); + eprintln!(); + ip.module.dump(Some(ip.address), 8); + } + panic!("Assertion failed"); + } + Ok(Value::Nil) + } + [a, b, msg] => { + if a != b { + let ip = vm.ip(); + if let Some(ip) = ip { + eprintln!("Assertion failed at {ip}: {msg}"); + eprintln!(); + eprintln!(":: {a} ≠ {b}"); + eprintln!(); + ip.module.dump(Some(ip.address), 8); + } + panic!("Assertion failed"); + } + Ok(Value::Nil) + } + _ => todo!(), + }); + + // io + vm.defun_native("print", |_, args| { + for (i, arg) in args.iter().enumerate() { + if i != 0 { + print!(" "); + } + print!("{arg}"); + } + println!(); + Ok(Value::Nil) + }); +} diff --git a/src/vm/value.rs b/src/vm/value.rs index 76430a1..db7ab86 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -1,4 +1,4 @@ -use std::{any::Any, fmt, rc::Rc}; +use std::{any::Any, cell::RefCell, fmt, ops::Deref, rc::Rc}; use crate::{ compile::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind}, @@ -22,6 +22,12 @@ pub struct ProperListIter<'a, E> { error: Option, } +#[derive(Debug, PartialEq)] +pub struct Vector(RefCell>); + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ValueString(Rc); + #[derive(Debug, Clone, PartialEq)] pub enum Value { // "Expression" values @@ -31,6 +37,8 @@ pub enum Value { Identifier(Rc), Cons(Rc), Keyword(Keyword), + String(ValueString), + Vector(Rc), // "Runtime" values BytecodeFunction(BytecodeFunction), NativeFunction(NativeFunction), @@ -167,6 +175,22 @@ impl Value { } } + pub fn stringify(&self) -> Result { + match self { + Self::Integer(value) => Ok(format!("{value}")), + Self::Boolean(true) => Ok("#t".into()), + Self::Boolean(false) => Ok("#f".into()), + Self::Identifier(value) => Ok(format!("{value}")), + Self::String(value) => Ok(format!("{value}")), + Self::Nil => todo!(), + Self::Cons(_) => todo!(), + Self::Vector(_) => todo!(), + Self::Keyword(_) => todo!(), + Self::OpaqueValue(_) => todo!(), + Self::NativeFunction(_) | Self::BytecodeFunction(_) => todo!(), + } + } + pub fn cons(self, cdr: Value) -> Self { Self::Cons(Rc::new(ConsCell(self, cdr))) } @@ -198,6 +222,36 @@ impl Value { } } +impl From<&str> for ValueString { + fn from(value: &str) -> Self { + Self(value.into()) + } +} + +impl From for ValueString { + fn from(value: String) -> Self { + Self(value.into()) + } +} + +impl Deref for ValueString { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} + +impl Vector { + pub fn is_empty(&self) -> bool { + self.0.borrow().is_empty() + } + + pub fn len(&self) -> usize { + self.0.borrow().len() + } +} + impl ConsCell { pub fn end(value: Value) -> Self { Self(value, Value::Nil) @@ -231,6 +285,25 @@ impl fmt::Display for BytecodeFunction { } } +impl fmt::Display for Vector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[")?; + for (i, element) in self.0.borrow().iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + write!(f, "{element}")?; + } + write!(f, "]") + } +} + +impl fmt::Display for ValueString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.0.as_ref(), f) + } +} + impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -244,6 +317,8 @@ impl fmt::Display for Value { Self::BytecodeFunction(bytecode) => write!(f, "{bytecode}"), Self::NativeFunction(native) => write!(f, "{native}"), Self::OpaqueValue(opaque) => write!(f, "{opaque}"), + Self::Vector(vector) => write!(f, "{vector}"), + Self::String(value) => write!(f, "{value}"), } } }