Add defun and math prelude

This commit is contained in:
2026-05-05 16:06:36 +03:00
parent 1392801e9f
commit 2c84f1fbb2
17 changed files with 802 additions and 172 deletions
+14
View File
@@ -0,0 +1,14 @@
(defun return-1 () 1)
(assert (= 1 (return-1)))
(assert (< 1 2 3 4))
(assert (<= 1 2 2 3))
(assert (= 6 (+ 1 2 3) (apply + (list 1 2 3))))
(assert (= (- 4) (- 1 2 3) (apply - (list 1 2 3))))
(assert (= 6 (* 1 2 3) (apply * (list 1 2 3))))
(assert (= 2 (/ 6 3 1) (apply / (list 6 3 1))))
(assert (= 2 (% 6 4) (apply % (list 6 4))))
(assert (= 7 (| 1 2 4) (apply | (list 1 2 4))))
(assert (= 1 (& 1 3 5 7) (apply & (list 1 3 5 7))))
(assert (= 0 (^ 1 3 5 7) (apply ^ (list 1 3 5 7))))
(assert (= #t (&& #t #t) (apply && (list #t #t))))
(assert (= #f (|| #f #f) (apply || (list #f #f))))
+37 -33
View File
@@ -7,12 +7,12 @@ use crate::{
instruction::Emitted,
module::CompilationModule,
syntax::{
CallExpression, CondExpression, Expression, FunctionBody, IfExpression,
LambdaExpression,
CallExpression, CondExpression, DefunExpression, Expression, FunctionBody,
IfExpression, LambdaExpression,
},
value::{BuiltinFunction, CompileConstant, CompileValue},
},
vm::instruction::{Comparison, Instruction, U},
vm::instruction::{Instruction, MathInstruction, U},
};
pub struct CompiledFunction {
@@ -112,6 +112,20 @@ impl<'a> LocalBlock<'a> {
}
}
fn compile_set_global(
&mut self,
identifier: &Rc<str>,
value: CompileValue,
) -> Result<(), CompileError> {
self.compile_push(value)?;
let index = self
.module
.constant(CompileConstant::Identifier(identifier.clone()))?;
self.function.emit(Instruction::PushConstant(index));
self.function.emit(Instruction::SetGlobal);
Ok(())
}
fn compile_push(&mut self, value: CompileValue) -> Result<(), CompileError> {
match value {
CompileValue::Nil => {
@@ -141,10 +155,10 @@ impl<'a> LocalBlock<'a> {
};
self.function.emit(Instruction::PushArgument(index));
}
CompileValue::LocalFunction(index) => {
CompileValue::LocalFunction(index, required_count) => {
let value = self
.module
.constant(CompileConstant::LocalFunction(index))?;
.constant(CompileConstant::LocalFunction(index, required_count))?;
self.function.emit(Instruction::PushConstant(value));
}
// Already on stack
@@ -174,9 +188,6 @@ impl<'a> LocalBlock<'a> {
}
fn compile_identifier(&mut self, identifier: &Rc<str>) -> Result<CompileValue, CompileError> {
if let Some(_builtin) = BuiltinFunction::from_identifier(identifier) {
todo!("Illegal");
}
if let Some(argument) = self.function.signature.argument(identifier) {
return Ok(CompileValue::Argument(argument));
}
@@ -188,33 +199,27 @@ impl<'a> LocalBlock<'a> {
let index = self
.module
.compile_function(lambda.signature.clone(), &lambda.body, false)?;
Ok(CompileValue::LocalFunction(index))
Ok(CompileValue::LocalFunction(
index,
lambda.signature.required_arguments.len(),
))
}
fn compile_builtin_add(&mut self, args: &[Expression]) -> Result<CompileValue, CompileError> {
// TODO optimize literals
for arg in args.iter().rev() {
let arg = self.compile_expression(arg)?;
self.compile_push(arg)?;
}
let Some(count) = U::new(args.len() as u32) else {
todo!()
};
self.function.emit(Instruction::Add(count));
Ok(CompileValue::Stack)
fn compile_defun(&mut self, defun: &DefunExpression) -> Result<CompileValue, CompileError> {
let index = self
.module
.compile_function(defun.signature.clone(), &defun.body, false)?;
let value = CompileValue::LocalFunction(index, defun.signature.required_arguments.len());
self.compile_set_global(&defun.name, value)?;
Ok(CompileValue::Nil)
}
fn compile_builtin_sub(&mut self, _args: &[Expression]) -> Result<CompileValue, CompileError> {
todo!()
}
fn compile_builtin_cmp(
fn compile_builtin_math_generic(
&mut self,
not: bool,
cmp: Comparison,
math: MathInstruction,
args: &[Expression],
) -> Result<CompileValue, CompileError> {
// TODO optimize literals
// TODO optimize
for arg in args.iter().rev() {
let arg = self.compile_expression(arg)?;
self.compile_push(arg)?;
@@ -222,7 +227,7 @@ impl<'a> LocalBlock<'a> {
let Some(count) = U::new(args.len() as u32) else {
todo!()
};
self.function.emit(Instruction::Compare(not, cmp, count));
self.function.emit(Instruction::Math(math, count));
Ok(CompileValue::Stack)
}
@@ -232,9 +237,7 @@ impl<'a> LocalBlock<'a> {
args: &[Expression],
) -> Result<CompileValue, CompileError> {
match builtin {
BuiltinFunction::Add => self.compile_builtin_add(args),
BuiltinFunction::Sub => self.compile_builtin_sub(args),
BuiltinFunction::Cmp(not, cmp) => self.compile_builtin_cmp(not, cmp, args),
BuiltinFunction::Math(math) => self.compile_builtin_math_generic(math, args),
}
}
@@ -323,6 +326,7 @@ impl<'a> LocalBlock<'a> {
Expression::IntegerLiteral(value) => Ok(CompileValue::Integer(*value)),
Expression::Identifier(identifier) => self.compile_identifier(identifier),
Expression::Lambda(lambda) => self.compile_lambda(lambda),
Expression::Defun(defun) => self.compile_defun(defun),
Expression::Call(call) => self.compile_call(call),
Expression::If(condition) => self.compile_if(condition),
Expression::Cond(condition) => self.compile_cond(condition),
@@ -347,7 +351,7 @@ mod tests {
},
value::{CompileConstant, CompileValue},
},
vm::instruction::{Comparison, Instruction, U},
vm::instruction::{Instruction, U},
};
fn test_compile(
+2 -2
View File
@@ -73,9 +73,9 @@ impl CompilationModule {
CompileConstant::Identifier(identifier) => {
ModuleConstant::Identifier(identifier)
}
CompileConstant::LocalFunction(index) => {
CompileConstant::LocalFunction(index, required_count) => {
let address = *function_offsets.get(&index).unwrap();
ModuleConstant::LocalFunction(address)
ModuleConstant::LocalFunction(address, required_count)
}
},
)
+4
View File
@@ -40,6 +40,8 @@ pub enum ExpectedWhat {
ArgumentSpec,
#[error("a proper list")]
ProperList,
#[error("an identifier")]
Identifier,
}
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
@@ -58,6 +60,8 @@ pub enum ExpectedWhere {
InConditionList,
#[error("in the condition arm")]
InConditionArm,
#[error("in the function definition")]
InFunctionDefinition,
}
pub(super) trait CollectErrors<E> {
+57
View File
@@ -14,6 +14,13 @@ pub struct FunctionBody {
pub tail: Rc<Expression>,
}
#[derive(Debug, PartialEq)]
pub struct DefunExpression {
pub name: Rc<str>,
pub signature: FunctionSignature,
pub body: FunctionBody,
}
impl FunctionSignature {
pub(super) fn parse(mut value: &Value, input: &Value) -> Result<Self, ParseError> {
enum Mode {
@@ -164,6 +171,50 @@ impl FunctionBody {
}
}
impl DefunExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let Value::Cons(value) = value else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::ProperList,
ExpectedWhere::InFunctionDefinition,
),
});
};
let ConsCell(identifier, cdr) = value.as_ref();
let Value::Identifier(identifier) = identifier else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::Identifier,
ExpectedWhere::AfterKeyword(Keyword::Defun),
),
});
};
let Value::Cons(cdr) = cdr else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::ProperList,
ExpectedWhere::InFunctionDefinition,
),
});
};
let ConsCell(car, cdr) = cdr.as_ref();
let signature = FunctionSignature::parse(car, input)?;
let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?;
Ok(Self {
name: identifier.clone(),
signature,
body,
})
}
}
impl CollectErrors<ParseError> for FunctionBody {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
let mut r = false;
@@ -175,6 +226,12 @@ impl CollectErrors<ParseError> for FunctionBody {
}
}
impl CollectErrors<ParseError> for DefunExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.body.collect_errors(errors)
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
+6 -1
View File
@@ -21,6 +21,7 @@ pub enum Expression {
IntegerLiteral(i64),
Identifier(Rc<str>),
Lambda(LambdaExpression),
Defun(DefunExpression),
Call(CallExpression),
If(IfExpression),
Cond(CondExpression),
@@ -63,6 +64,9 @@ impl Expression {
Value::Keyword(Keyword::Cond) => {
Self::map_or(CondExpression::parse(cdr, value), Expression::Cond)
}
Value::Keyword(Keyword::Defun) => {
Self::map_or(DefunExpression::parse(cdr, value), Expression::Defun)
}
_ => Self::map_or(CallExpression::parse(cons, value), Expression::Call),
}
}
@@ -83,7 +87,8 @@ impl CollectErrors<ParseError> for Expression {
}
Self::If(condition) => condition.collect_errors(errors),
Self::Cond(condition) => condition.collect_errors(errors),
Self::Lambda(condition) => condition.collect_errors(errors),
Self::Lambda(lambda) => lambda.collect_errors(errors),
Self::Defun(defun) => defun.collect_errors(errors),
Self::Call(call) => call.collect_errors(errors),
Self::Nil | Self::IntegerLiteral(_) | Self::Identifier(_) | Self::BooleanLiteral(_) => {
false
+20 -14
View File
@@ -1,12 +1,12 @@
use std::rc::Rc;
use crate::vm::instruction::Comparison;
use crate::vm::instruction::MathInstruction;
#[derive(Debug, PartialEq)]
pub enum CompileValue {
Integer(i64),
Boolean(bool),
LocalFunction(u32),
LocalFunction(u32, usize),
Argument(usize),
Global(Rc<str>),
Stack,
@@ -16,28 +16,34 @@ pub enum CompileValue {
#[derive(Debug, Hash, PartialEq, Eq)]
pub enum CompileConstant {
Integer(i64),
LocalFunction(u32),
LocalFunction(u32, usize),
Identifier(Rc<str>),
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum BuiltinFunction {
Add,
Sub,
Cmp(bool, Comparison),
Math(MathInstruction),
}
impl BuiltinFunction {
pub fn from_identifier(identifier: &str) -> Option<Self> {
match identifier {
"+" => Some(Self::Add),
"-" => Some(Self::Sub),
">" => Some(Self::Cmp(false, Comparison::Gt)),
">=" => Some(Self::Cmp(true, Comparison::Lt)),
"=" => Some(Self::Cmp(false, Comparison::Eq)),
"/=" => Some(Self::Cmp(true, Comparison::Eq)),
"<=" => Some(Self::Cmp(true, Comparison::Gt)),
"<" => Some(Self::Cmp(false, Comparison::Lt)),
"+" => Some(Self::Math(MathInstruction::Add)),
"-" => Some(Self::Math(MathInstruction::Sub)),
"*" => Some(Self::Math(MathInstruction::Mul)),
"/" => Some(Self::Math(MathInstruction::Div)),
"%" => Some(Self::Math(MathInstruction::Mod)),
"&" => Some(Self::Math(MathInstruction::BitwiseAnd)),
"|" => Some(Self::Math(MathInstruction::BitwiseOr)),
"^" => Some(Self::Math(MathInstruction::BitwiseXor)),
"&&" => Some(Self::Math(MathInstruction::And)),
"||" => Some(Self::Math(MathInstruction::Or)),
">" => Some(Self::Math(MathInstruction::Gt)),
">=" => Some(Self::Math(MathInstruction::Ge)),
"=" => Some(Self::Math(MathInstruction::Eq)),
"/=" => Some(Self::Math(MathInstruction::Ne)),
"<=" => Some(Self::Math(MathInstruction::Le)),
"<" => Some(Self::Math(MathInstruction::Lt)),
_ => None,
}
}
+1
View File
@@ -4,4 +4,5 @@ pub mod compile;
pub mod convert;
pub mod error;
pub mod parse;
pub mod util;
pub mod vm;
+158 -8
View File
@@ -1,23 +1,56 @@
use std::io::stdin;
use std::{
fs::File,
io::{self, BufRead, BufReader, Write, stdin, stdout},
path::{Path, PathBuf},
process::ExitCode,
rc::Rc,
};
use clap::Parser;
use lysp::{
compile::{CompileError, ParseError},
compile::{
CompilationModule, CompileError, Expression, FunctionBody, FunctionSignature, ParseError,
},
error::EvalError,
parse::parse_value,
vm::{
machine::{EvalResult, Machine},
machine::{EvalResult, Machine, MachineError},
prelude,
},
};
fn main() {
let mut vm = Machine::default();
prelude::load(&mut vm);
#[derive(Debug, thiserror::Error)]
#[error("{0}")]
enum Error {
Machine(#[from] MachineError),
Eval(#[from] EvalError),
Io(#[from] io::Error),
Compile(#[from] CompileError),
#[error("Compilation errors ocurred")]
CompilationErrors,
#[error("Error already printed")]
Printed,
}
#[derive(Debug, Parser)]
struct Args {
module: Option<PathBuf>,
}
fn run_interactive(vm: &mut Machine) -> Result<(), Error> {
let mut input = String::new();
let stdin = stdin();
let mut stdout = stdout();
loop {
let len = stdin.read_line(&mut input).unwrap();
if input.is_empty() {
print!("> ");
} else {
print!(">>> ");
}
stdout.flush().ok();
let len = stdin.read_line(&mut input)?;
if len == 0 {
break;
}
@@ -48,7 +81,7 @@ fn main() {
eprintln!();
eprintln!(":: {error}");
eprintln!();
module.dump(ip_address);
module.dump(ip_address, 8);
i = "";
break;
}
@@ -90,4 +123,121 @@ fn main() {
input = i.into();
}
Ok(())
}
fn run_module<P: AsRef<Path>>(vm: &mut Machine, path: P) -> Result<(), Error> {
let path = path.as_ref();
let mut input = String::new();
let mut reader = BufReader::new(File::open(path)?);
let mut module = CompilationModule::default();
let mut body = FunctionBody {
head: vec![],
tail: Rc::new(Expression::Nil),
};
let mut fail = false;
loop {
let len = reader.read_line(&mut input)?;
if len == 0 {
break;
}
let mut i = input.trim_start();
while !i.is_empty() {
let result = parse_value(i);
let (o, value) = match result {
Ok(r) => r,
Err(nom::Err::Incomplete(_)) => {
break;
}
Err(error) => {
eprintln!("Syntax error: {error}");
i = "";
fail = true;
break;
}
};
let expression = match Expression::parse(&value) {
Ok(expression) => expression,
Err(errors) => {
if errors.len() > 1 {
eprintln!("Syntax errors:");
} else {
eprintln!("Syntax error:");
}
eprintln!();
for error in errors {
let ParseError { input, error } = error;
eprintln!(" * In (sub)expression:");
eprintln!();
eprintln!(" {input}");
eprintln!();
eprintln!(" :: {error}");
}
fail = true;
continue;
}
};
body.head.push(expression);
i = o.trim_start();
}
input = i.into();
}
if fail {
return Err(Error::CompilationErrors);
}
module.compile_function(
FunctionSignature {
required_arguments: vec![],
optional_arguments: vec![],
rest_argument: None,
},
&body,
true,
)?;
let module = module.compile_module()?;
match vm.eval_module(module.into()) {
EvalResult::Ok(_) => Ok(()),
EvalResult::Err(module, ip, error) => {
let ip_address = ip.map(|ip| ip.address);
eprintln!("Error in module");
eprintln!();
eprintln!(" {}:", path.display());
eprintln!();
eprintln!(":: {error}");
eprintln!();
module.dump(ip_address, 8);
Err(Error::Printed)
}
EvalResult::LoadErr(error) => Err(error.into()),
}
}
fn main() -> ExitCode {
let args = Args::parse();
let mut vm = Machine::default();
prelude::load(&mut vm);
let result = match args.module.as_ref() {
Some(module) => run_module(&mut vm, module),
None => run_interactive(&mut vm),
};
match result {
Ok(()) => ExitCode::SUCCESS,
Err(Error::Printed) => ExitCode::FAILURE,
Err(error) => {
eprintln!("{error}");
ExitCode::FAILURE
}
}
}
+2 -2
View File
@@ -19,13 +19,13 @@ struct IdentifierTail;
impl FindToken<char> for IdentifierHead {
fn find_token(&self, token: char) -> bool {
token.is_alphabetic() || "~!@$%^&*-=_+<>?/".contains(token)
token.is_alphabetic() || "~!@$%^&*-=_+<>?/|".contains(token)
}
}
impl FindToken<char> for IdentifierTail {
fn find_token(&self, token: char) -> bool {
token.is_alphanumeric() || "~!@$%^&*-=_+<>?/".contains(token)
token.is_alphanumeric() || "~!@$%^&*-=_+<>?/|".contains(token)
}
}
+60
View File
@@ -0,0 +1,60 @@
pub struct TryFilter<I, E1, E2, T, F>
where
I: Iterator<Item = Result<T, E1>>,
F: FnMut(&T) -> Result<bool, E2>,
E2: From<E1>,
{
iterator: I,
filter: F,
}
pub trait IteratorExt<I, E1, T> {
fn try_filter<E2, F>(self, filter: F) -> impl Iterator<Item = Result<T, E2>>
where
F: FnMut(&T) -> Result<bool, E2>,
E2: From<E1>;
}
impl<I, E1, T> IteratorExt<I, E1, T> for I
where
I: Iterator<Item = Result<T, E1>>,
{
fn try_filter<E2, F>(self, filter: F) -> impl Iterator<Item = Result<T, E2>>
where
F: FnMut(&T) -> Result<bool, E2>,
E2: From<E1>,
{
TryFilter {
iterator: self,
filter,
}
}
}
impl<I, E1, E2, T, F> Iterator for TryFilter<I, E1, E2, T, F>
where
I: Iterator<Item = Result<T, E1>>,
F: FnMut(&T) -> Result<bool, E2>,
E2: From<E1>,
{
type Item = Result<T, E2>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let value = self.iterator.next()?;
let value = match value {
Ok(value) => value,
Err(error) => return Some(Err(error.into())),
};
let cond = match (self.filter)(&value) {
Ok(value) => value,
Err(error) => return Some(Err(error)),
};
match cond {
true => return Some(Ok(value)),
false => continue,
}
}
}
}
+74 -33
View File
@@ -14,24 +14,59 @@ pub enum InstructionEncodeError {}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct U<const N: usize>(u32);
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u32)]
pub enum Comparison {
Eq = 0,
Lt = 1,
Gt = 2,
macro_rules! primitive_enum {
(
$(#[$meta:meta])*
$vis:vis enum $ident:ident: $repr:ty {
$($variant:ident = $value:literal),* $(,)?
}
) => {
$(#[$meta])*
#[repr($repr)]
$vis enum $ident {
$($variant = $value),*
}
impl TryFrom<$repr> for $ident {
type Error = InstructionError;
fn try_from(value: $repr) -> Result<Self, Self::Error> {
match value {
$($value => Ok(Self::$variant),)*
_ => Err(InstructionError::Invalid)
}
}
}
impl From<$ident> for $repr {
fn from(value: $ident) -> $repr {
match value {
$($ident::$variant => $value,)*
}
}
}
};
}
impl TryFrom<u32> for Comparison {
type Error = InstructionError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Eq),
1 => Ok(Self::Lt),
2 => Ok(Self::Gt),
_ => Err(InstructionError::Invalid),
}
primitive_enum! {
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MathInstruction: u32 {
Add = 0,
Sub = 1,
Mul = 2,
Div = 3,
Mod = 4,
And = 5,
Or = 6,
BitwiseAnd = 7,
BitwiseOr = 8,
BitwiseXor = 9,
Gt = 10,
Lt = 11,
Eq = 12,
Ne = 13,
Ge = 14,
Le = 15,
}
}
@@ -47,11 +82,9 @@ pub enum Instruction {
GetGlobal,
Call(U<6>),
Return,
Add(U<6>),
Sub(U<6>),
Math(MathInstruction, U<6>),
Branch(U<12>),
Jump(U<12>),
Compare(bool, Comparison, U<6>),
}
pub type ConstantId = U<24>;
@@ -91,15 +124,15 @@ impl<const N: usize> fmt::Debug for U<N> {
}
}
impl<const N: usize> Into<usize> for U<N> {
fn into(self) -> usize {
self.0 as usize
impl<const N: usize> From<U<N>> for usize {
fn from(value: U<N>) -> Self {
value.0 as usize
}
}
impl<const N: usize> Into<u32> for U<N> {
fn into(self) -> u32 {
self.0
impl<const N: usize> From<U<N>> for u32 {
fn from(value: U<N>) -> Self {
value.0
}
}
@@ -112,16 +145,14 @@ impl From<Instruction> for u32 {
Instruction::Return => 0b0000_0000_0000_0100,
Instruction::SetGlobal => 0b0000_0000_0000_0101,
Instruction::GetGlobal => 0b0000_0000_0000_0110,
Instruction::Add(count) => 0b0000_0001_0000_0000 | count.0,
Instruction::Sub(count) => 0b0000_0001_0100_0000 | count.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::Compare(not, cmp, count) => {
0b0000_0010_0000_0000 | ((not as u32) << 8) | ((cmp as u32) << 6) | count.0
Instruction::Math(math, count) => {
0b0000_0100_0000_0000 | (u32::from(math) << 6) | count.0
}
}
}
@@ -140,16 +171,26 @@ impl TryFrom<u32> for Instruction {
"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!(),
"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_0001_00xx_xxxx" => Ok(Instruction::Add(U(x))),
"0000_0001_01xx_xxxx" => Ok(Instruction::Sub(U(x))),
"0000_001x_yyzz_zzzz" => Ok(Instruction::Compare(x != 0, y.try_into()?, U(z))),
"0000_0000_11??_????" => todo!(),
"0000_0001_????_????" => todo!(),
"0000_001?_????_????" => todo!(),
"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))),
"0001_xxxx_xxxx_xxxx" => Ok(Instruction::PushInteger(U(x))),
"0010_xxxx_xxxx_xxxx" => Ok(Instruction::PushConstant(U(x))),
_ => todo!(),
"0011_????_????_????" => todo!(),
"01??_????_????_????" => todo!(),
"1???_????_????_????" => todo!(),
}
}
}
+85 -68
View File
@@ -1,11 +1,12 @@
use std::{cmp::Ordering, collections::HashMap, fmt, rc::Rc};
use std::{collections::HashMap, fmt, rc::Rc};
use crate::{
error::EvalError,
vm::{
instruction::{Comparison, ConstantId, Instruction, InstructionError},
instruction::{ConstantId, Instruction, InstructionError, MathInstruction},
module::{Module, ModuleConstant, ModuleRef},
native::NativeFunction,
prelude,
stack::Stack,
value::{BytecodeFunction, Value},
},
@@ -29,6 +30,8 @@ pub enum MachineError {
CallStackOverflow,
#[error("Unbound identifier: {0:?}")]
UnboundIdentifier(Rc<str>),
#[error("Expected at least {0} arguments, got {1}")]
ArgumentCountMismatch(usize, usize),
#[error("Invalid argument provided in a call")]
InvalidArgument,
}
@@ -79,6 +82,10 @@ impl Default for Machine {
}
impl Machine {
pub fn ip(&self) -> Option<InstructionPointer> {
self.ip.clone()
}
pub fn set_global<S: Into<Rc<str>>>(&mut self, identifier: S, value: Value) {
self.globals.insert(identifier.into(), value);
}
@@ -111,15 +118,25 @@ impl Machine {
arguments: Vec<Value>,
) -> Result<(), MachineError> {
let source_ip = self.ip.clone().unwrap();
let BytecodeFunction {
module,
required_count,
address,
} = bytecode.clone();
if required_count > arguments.len() {
return Err(MachineError::ArgumentCountMismatch(
required_count,
arguments.len(),
));
}
let frame = CallFrame {
arguments,
event: ExecutionEvent::BytecodeFunctionExit(bytecode.clone()),
event: ExecutionEvent::BytecodeFunctionExit(bytecode),
return_address: InstructionPointer {
module: source_ip.module,
address: source_ip.address + 1,
},
};
let BytecodeFunction { module, address } = bytecode;
if self.call_stack.push(frame).is_err() {
return Err(MachineError::CallStackOverflow);
}
@@ -160,6 +177,19 @@ impl Machine {
Ok(())
}
fn execute_builtin_native<F>(&mut self, function: F, count: usize) -> Result<(), MachineError>
where
F: Fn(&mut Self, &[Value]) -> Result<Value, MachineError>,
{
let mut args = vec![];
for _ in 0..count {
args.push(self.pop()?);
}
let value = function(self, &args)?;
self.push(value)?;
Ok(())
}
fn execute_return(&mut self) -> Result<ExecutionEvent, MachineError> {
let ip = self.ip.clone().unwrap();
if let Some(frame) = self.call_stack.pop() {
@@ -171,59 +201,49 @@ impl Machine {
}
}
fn execute_add(&mut self, count: usize) -> Result<(), MachineError> {
let mut accumulator = 0i64;
for _ in 0..count {
let arg = self.pop()?;
match arg {
Value::Integer(value) => {
accumulator = accumulator.wrapping_add(value);
}
_ => todo!("{arg:?}"),
}
}
self.push(Value::Integer(accumulator))?;
Ok(())
}
//fn execute_add(&mut self, count: usize) -> Result<(), MachineError> {
//}
fn execute_sub(&mut self, _count: usize) -> Result<(), MachineError> {
todo!()
}
// fn execute_sub(&mut self, _count: usize) -> Result<(), MachineError> {
// todo!()
// }
fn execute_compare(
&mut self,
not: bool,
cmp: Comparison,
count: usize,
) -> Result<(), MachineError> {
fn ordering(a: &Value, b: &Value) -> Ordering {
match (a, b) {
(Value::Integer(a), Value::Integer(b)) => Ord::cmp(a, b),
_ => todo!(),
}
}
// TODO checks
let values = (0..count)
.map(|_| self.pop())
.collect::<Result<Vec<_>, _>>()?;
let mut accumulator = true;
for i in 0..values.len() - 1 {
let a = &values[i];
let b = &values[i + 1];
let ord = ordering(a, b);
let check = match (not, cmp) {
(true, Comparison::Gt) => ord.is_le(),
(false, Comparison::Gt) => ord.is_gt(),
(true, Comparison::Eq) => ord.is_ne(),
(false, Comparison::Eq) => ord.is_eq(),
(true, Comparison::Lt) => ord.is_ge(),
(false, Comparison::Lt) => ord.is_lt(),
};
accumulator &= check;
}
self.push(Value::Boolean(accumulator))?;
Ok(())
// fn execute_compare(
// &mut self,
// not: bool,
// cmp: Comparison,
// count: usize,
// ) -> Result<(), MachineError> {
// let f = match (not, cmp) {
// (true, Comparison::Gt) => prelude::builtin_cmp_le,
// (false, Comparison::Gt) => prelude::builtin_cmp_gt,
// (true, Comparison::Eq) => prelude::builtin_cmp_eq,
// (false, Comparison::Eq) => prelude::builtin_cmp_ne,
// (true, Comparison::Lt) => prelude::builtin_cmp_ge,
// (false, Comparison::Lt) => prelude::builtin_cmp_lt,
// };
// self.execute_builtin_native(f, count)
// }
fn execute_math(&mut self, math: MathInstruction, count: usize) -> Result<(), MachineError> {
let function = match math {
MathInstruction::Add => prelude::builtin_add,
MathInstruction::Sub => prelude::builtin_sub,
MathInstruction::Mul => prelude::builtin_mul,
MathInstruction::Div => prelude::builtin_div,
MathInstruction::Mod => prelude::builtin_mod,
MathInstruction::And => prelude::builtin_and,
MathInstruction::Or => prelude::builtin_or,
MathInstruction::BitwiseAnd => prelude::builtin_bitwise_and,
MathInstruction::BitwiseOr => prelude::builtin_bitwise_or,
MathInstruction::BitwiseXor => prelude::builtin_bitwise_xor,
MathInstruction::Gt => prelude::builtin_cmp_gt,
MathInstruction::Lt => prelude::builtin_cmp_lt,
MathInstruction::Eq => prelude::builtin_cmp_eq,
MathInstruction::Ne => prelude::builtin_cmp_ne,
MathInstruction::Ge => prelude::builtin_cmp_ge,
MathInstruction::Le => prelude::builtin_cmp_le,
};
self.execute_builtin_native(function, count)
}
fn execute_branch(&mut self, offset: usize) -> Result<bool, MachineError> {
@@ -253,10 +273,13 @@ impl Machine {
let ip = self.ip.as_ref().unwrap();
let constant = ip.module.constant(index).expect("TODO");
let value = match constant {
ModuleConstant::LocalFunction(address) => Value::BytecodeFunction(BytecodeFunction {
module: ip.module.clone(),
address,
}),
ModuleConstant::LocalFunction(address, required_count) => {
Value::BytecodeFunction(BytecodeFunction {
module: ip.module.clone(),
required_count,
address,
})
}
ModuleConstant::Integer(value) => Value::Integer(value),
ModuleConstant::Identifier(identifier) => Value::Identifier(identifier),
};
@@ -281,7 +304,7 @@ impl Machine {
.globals
.get(&ident)
.cloned()
.ok_or_else(|| MachineError::UnboundIdentifier(ident))?;
.ok_or(MachineError::UnboundIdentifier(ident))?;
self.push(value)
}
_ => todo!(),
@@ -341,11 +364,8 @@ impl Machine {
advance = false;
self.execute_call(count.into())?;
}
Instruction::Add(count) => {
self.execute_add(count.into())?;
}
Instruction::Sub(count) => {
self.execute_sub(count.into())?;
Instruction::Math(math, count) => {
self.execute_math(math, count.into())?;
}
Instruction::Branch(offset) => {
advance = self.execute_branch(offset.into())?;
@@ -354,9 +374,6 @@ impl Machine {
advance = false;
self.execute_jump(offset.into())?;
}
Instruction::Compare(not, cmp, count) => {
self.execute_compare(not, cmp, count.into())?;
}
}
if advance {
self.ip = Some(InstructionPointer {
+13 -6
View File
@@ -12,7 +12,7 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ModuleConstant {
Integer(i64),
LocalFunction(usize),
LocalFunction(usize, usize),
Identifier(Rc<str>),
}
@@ -120,15 +120,22 @@ impl Module {
module.compile_module()
}
pub fn dump(&self, highlight: Option<usize>) {
for (i, instruction) in self.instructions.iter().enumerate() {
if highlight.is_some_and(|a| a == i) {
pub fn dump(&self, highlight: Option<usize>, context: usize) {
let window = highlight
.map(|end| (end + 1).min(self.instructions.len()))
.map(|end| end.saturating_sub(context)..end)
.unwrap_or(0..self.instructions.len());
let start = window.start;
for (i, instruction) in self.instructions[window].iter().enumerate() {
let offset = i + start;
if highlight.is_some_and(|a| a == offset) {
print!(">");
} else {
print!(" ");
}
print!("{i:4}: {instruction:08x}");
print!("{offset:4}: {instruction:08x}");
if let Ok(instruction) = Instruction::try_from(*instruction) {
print!(" {instruction:?}");
@@ -183,7 +190,7 @@ impl fmt::Display for ModuleConstant {
match self {
Self::Integer(value) => fmt::Display::fmt(value, f),
Self::Identifier(ident) => write!(f, "ident {ident:?}"),
Self::LocalFunction(address) => write!(f, "label {address}"),
Self::LocalFunction(address, _) => write!(f, "label {address}"),
}
}
}
+4 -1
View File
@@ -5,10 +5,13 @@ use crate::vm::{
value::Value,
};
pub type NativeFunctionImpl =
Rc<dyn Fn(&mut Machine, &[Value]) -> Result<Value, MachineError> + 'static>;
#[derive(Clone)]
pub struct NativeFunction {
name: Rc<str>,
inner: Rc<dyn Fn(&mut Machine, &[Value]) -> Result<Value, MachineError> + 'static>,
inner: NativeFunctionImpl,
}
impl NativeFunction {
+264 -4
View File
@@ -1,29 +1,289 @@
use std::{
cmp::Ordering,
ops::{BitAnd, BitOr, BitXor},
slice,
};
use crate::{
convert::{AnyFunction, TryFromValue},
util::IteratorExt,
vm::{
machine::{Machine, MachineError},
value::Value,
},
};
pub(crate) enum CompareOperation {
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
}
fn builtin_fold_integer<F>(
fold: F,
mut accumulator: i64,
args: &[Value],
) -> Result<Value, MachineError>
where
F: Fn(i64, i64) -> i64,
{
for arg in args {
match arg {
&Value::Integer(value) => {
accumulator = fold(accumulator, value);
}
_ => todo!("Math for {arg}"),
}
}
Ok(Value::Integer(accumulator))
}
fn builtin_fold_bool<F>(
fold: F,
mut accumulator: bool,
args: &[Value],
) -> Result<Value, MachineError>
where
F: Fn(bool, bool) -> bool,
{
for arg in args {
let arg = bool::try_from_value(arg)?;
accumulator = fold(accumulator, arg);
}
Ok(Value::Boolean(accumulator))
}
pub(crate) fn builtin_add(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_fold_integer(i64::wrapping_add, 0, args)
}
pub(crate) fn builtin_sub(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
match args {
[] => Err(MachineError::InvalidArgument),
[x] if let &Value::Integer(x) = x => Ok(Value::Integer(-x)),
[x] => todo!("Math for {x}"),
[x, rest @ ..] => {
let &Value::Integer(x) = x else {
todo!("Math for {x}");
};
let mut accumulator = x;
for arg in rest {
match arg {
&Value::Integer(arg) => {
accumulator = accumulator.saturating_sub(arg);
}
_ => todo!("Math for {arg}"),
}
}
Ok(Value::Integer(accumulator))
}
}
}
pub(crate) fn builtin_mul(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_fold_integer(i64::wrapping_mul, 1, args)
}
pub(crate) fn builtin_mod(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
let [x, y] = args else {
return Err(MachineError::InvalidArgument);
};
let (&Value::Integer(x), &Value::Integer(y)) = (x, y) else {
return Err(MachineError::InvalidArgument);
};
Ok(Value::Integer(x % y))
}
pub(crate) fn builtin_div(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
match args {
[] => Err(MachineError::InvalidArgument),
[x] if let &Value::Integer(_x) = x => todo!("Fractionals"),
[x] => todo!("Math for {x}"),
[x, rest @ ..] => {
let &Value::Integer(x) = x else {
todo!("Math for {x}");
};
let mut accumulator = x;
for arg in rest {
match arg {
&Value::Integer(arg) => {
accumulator = accumulator.saturating_div(arg);
}
_ => todo!("Math for {arg}"),
}
}
Ok(Value::Integer(accumulator))
}
}
}
pub(crate) fn builtin_and(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_fold_bool(BitAnd::bitand, true, args)
}
pub(crate) fn builtin_or(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_fold_bool(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)
}
pub(crate) fn builtin_bitwise_or(_vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_fold_integer(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)
}
pub(crate) fn builtin_cmp(
_vm: &mut Machine,
args: &[Value],
op: CompareOperation,
) -> Result<Value, MachineError> {
fn ordering(a: &Value, b: &Value) -> Ordering {
match (a, b) {
(Value::Integer(a), Value::Integer(b)) => Ord::cmp(a, b),
(Value::Boolean(a), Value::Boolean(b)) => Ord::cmp(a, b),
_ => todo!(),
}
}
let mut accumulator = true;
if !args.is_empty() {
for i in 0..args.len() - 1 {
let a = &args[i];
let b = &args[i + 1];
let ord = ordering(a, b);
let check = match op {
CompareOperation::Eq => ord.is_eq(),
CompareOperation::Ne => ord.is_ne(),
CompareOperation::Lt => ord.is_lt(),
CompareOperation::Gt => ord.is_gt(),
CompareOperation::Le => ord.is_le(),
CompareOperation::Ge => ord.is_ge(),
};
accumulator &= check;
}
}
Ok(Value::Boolean(accumulator))
}
pub(crate) fn builtin_cmp_eq(vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Eq)
}
pub(crate) fn builtin_cmp_ne(vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Ne)
}
pub(crate) fn builtin_cmp_gt(vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Gt)
}
pub(crate) fn builtin_cmp_lt(vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Lt)
}
pub(crate) fn builtin_cmp_ge(vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Ge)
}
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();
let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, &[v?.clone()])))?;
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().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())
});
vm.defun_native("list", |_, args| {
let out = Value::list_or_nil(args.iter().cloned());
Ok(out)
// 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()
.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)
});
}
+1
View File
@@ -5,6 +5,7 @@ use crate::vm::{machine::MachineError, module::ModuleRef, native::NativeFunction
#[derive(Debug, Clone, PartialEq)]
pub struct BytecodeFunction {
pub module: ModuleRef,
pub required_count: usize,
pub address: usize,
}