Add defun and math prelude
This commit is contained in:
@@ -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
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ pub mod compile;
|
||||
pub mod convert;
|
||||
pub mod error;
|
||||
pub mod parse;
|
||||
pub mod util;
|
||||
pub mod vm;
|
||||
|
||||
+158
-8
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user