Add string literals, fix instruction encoding

This commit is contained in:
2026-05-06 14:33:23 +03:00
parent a9a67acdce
commit 43938dee3c
13 changed files with 500 additions and 202 deletions
+9
View File
@@ -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"))
+18 -9
View File
@@ -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<str>, visible: bool) -> Result<U<16>, CompileError> {
pub fn get_or_insert(
&mut self,
value: Rc<str>,
visible: bool,
) -> Result<LocalId, CompileError> {
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<U<16>> {
pub fn get(&self, value: &str) -> Option<LocalId> {
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<U<16>> {
fn lookup_local(&self, identifier: &str) -> Option<LocalId> {
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<str>,
value: CompileValue,
visible: bool,
) -> Result<U<16>, CompileError> {
) -> Result<LocalId, CompileError> {
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<CompileValue, CompileError> {
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),
+1
View File
@@ -77,6 +77,7 @@ impl CompilationModule {
let address = *function_offsets.get(&index).unwrap();
ModuleConstant::LocalFunction(address, required_count)
}
CompileConstant::String(value) => ModuleConstant::String(value),
},
)
})
+9 -4
View File
@@ -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<str>),
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<ParseError> 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,
}
}
}
+7 -2
View File
@@ -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<str>),
Stack,
@@ -19,6 +23,7 @@ pub enum CompileConstant {
Integer(i64),
LocalFunction(u32, usize),
Identifier(Rc<str>),
String(ValueString),
}
#[derive(Debug, PartialEq, Clone, Copy)]
+24 -1
View File
@@ -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<bool> for Value {
fn from(value: bool) -> Self {
Self::Boolean(value)
}
}
impl TryFromValue<'_> for ValueString {
fn try_from_value(value: &'_ Value) -> Result<Self, MachineError> {
match value {
Value::String(value) => Ok(value.clone()),
_ => Err(MachineError::InvalidArgument),
}
}
}
impl From<ValueString> for Value {
fn from(value: ValueString) -> Self {
Self::String(value)
}
}
impl TryFromValue<'_> for Value {
fn try_from_value(value: &Value) -> Result<Self, MachineError> {
+56 -3
View File
@@ -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)
+88 -50
View File
@@ -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<const N: usize> U<N> {
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<const N: usize> U<N> {
}
}
pub fn from_signed(value: i64) -> Option<Self> {
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<const N: usize> From<U<N>> for u32 {
impl From<Instruction> 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<u32> for Instruction {
fn try_from(value: u32) -> Result<Self, Self::Error> {
#[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());
}
}
+1
View File
@@ -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),
};
+3 -1
View File
@@ -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<str>),
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}"),
+44 -131
View File
@@ -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<Value, MachineError> {
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<F>(
fold: F,
mut accumulator: i64,
args: &[Value],
) -> Result<Value, MachineError>
fn builtin_fold<F>(fold: F, mut accumulator: Value, args: &[Value]) -> Result<Value, MachineError>
where
F: Fn(i64, i64) -> i64,
F: Fn(&Value, &Value) -> Result<Value, MachineError>,
{
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<F>(
fn builtin_fold_t<'a, T, F>(
fold: F,
mut accumulator: bool,
args: &[Value],
mut accumulator: T,
args: &'a [Value],
) -> Result<Value, MachineError>
where
F: Fn(bool, bool) -> bool,
F: Fn(T, T) -> T,
T: TryFromValue<'a> + Into<Value>,
{
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<Value, MachineError> {
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<Value, MachineError> {
match args {
@@ -81,7 +94,7 @@ pub(crate) fn builtin_sub(_vm: &mut Machine, args: &[Value]) -> Result<Value, Ma
}
}
pub(crate) fn builtin_mul(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
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<Value, MachineError> {
let [x, y] = args else {
@@ -116,25 +129,25 @@ pub(crate) fn builtin_div(_vm: &mut Machine, args: &[Value]) -> Result<Value, Ma
}
pub(crate) fn builtin_and(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
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<Value, MachineError> {
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<Value, MachineError> {
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<Value, MachineError> {
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<Value, MachineError> {
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<Value,
pub(crate) fn builtin_cmp_le(vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
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::<Result<Vec<_>, _>>()?;
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)
});
}
+164
View File
@@ -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::<i64>().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::<Result<Vec<_>, _>>()?;
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)
});
}
+76 -1
View File
@@ -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<E>,
}
#[derive(Debug, PartialEq)]
pub struct Vector(RefCell<Vec<Value>>);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ValueString(Rc<str>);
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
// "Expression" values
@@ -31,6 +37,8 @@ pub enum Value {
Identifier(Rc<str>),
Cons(Rc<ConsCell>),
Keyword(Keyword),
String(ValueString),
Vector(Rc<Vector>),
// "Runtime" values
BytecodeFunction(BytecodeFunction),
NativeFunction(NativeFunction),
@@ -167,6 +175,22 @@ impl Value {
}
}
pub fn stringify(&self) -> Result<String, MachineError> {
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<String> 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}"),
}
}
}