aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/sloth/Cargo.toml14
-rw-r--r--crates/sloth/src/compiler/mod.rs75
-rw-r--r--crates/sloth/src/compiler/symbol.rs79
-rw-r--r--crates/sloth/src/lexer.rs559
-rw-r--r--crates/sloth/src/main.rs76
-rw-r--r--crates/sloth/src/parser/ast.rs115
-rw-r--r--crates/sloth/src/parser/expr.rs261
-rw-r--r--crates/sloth/src/parser/mod.rs57
-rw-r--r--crates/sloth/src/parser/stmt.rs646
-rw-r--r--crates/sloth_asm/Cargo.toml8
-rw-r--r--crates/sloth_asm/src/lib.rs1
-rw-r--r--crates/sloth_bytecode/Cargo.toml9
-rw-r--r--crates/sloth_bytecode/src/lib.rs76
-rw-r--r--crates/sloth_vm/Cargo.toml12
-rw-r--r--crates/sloth_vm/src/lib.rs155
-rw-r--r--crates/sloth_vm/src/native.rs19
-rw-r--r--crates/sloth_vm/src/sloth_std/file.rs83
-rw-r--r--crates/sloth_vm/src/sloth_std/misc.rs39
-rw-r--r--crates/sloth_vm/src/sloth_std/mod.rs43
-rw-r--r--crates/sloth_vm/src/sloth_std/rand.rs48
-rw-r--r--crates/sloth_vm/src/sloth_std/stdio.rs91
-rw-r--r--crates/sloth_vm/src/sloth_std/term.rs41
-rw-r--r--crates/sloth_vm/src/sloth_std/time.rs29
-rw-r--r--crates/sloth_vm/src/value.rs53
-rw-r--r--crates/sloth_vm/src/vm.rs610
25 files changed, 0 insertions, 3199 deletions
diff --git a/crates/sloth/Cargo.toml b/crates/sloth/Cargo.toml
deleted file mode 100644
index 8f49802..0000000
--- a/crates/sloth/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-[package]
-name = "sloth"
-
-license.workspace = true
-version.workspace = true
-edition.workspace = true
-
-[dependencies]
-sloth_vm = { path = "../sloth_vm" }
-sloth_bytecode = { path = "../sloth_bytecode" }
-
-itertools = "0.10.5"
-libc = "0.2.142"
-thiserror = "1.0.40"
diff --git a/crates/sloth/src/compiler/mod.rs b/crates/sloth/src/compiler/mod.rs
deleted file mode 100644
index 873f1a2..0000000
--- a/crates/sloth/src/compiler/mod.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-#![allow(unused)]
-
-pub mod symbol;
-
-use std::collections::HashMap;
-
-use sloth_bytecode::Opcode;
-
-use self::symbol::{Function, Symbol, SymbolTable, SymbolTableStack, SymbolType};
-use crate::parser::ast::{BinaryOp, Expr, Literal, Stmt, UnaryOp};
-
-// Modules:
-// Symbols (Functions, Constants)
-//
-// Functions:
-// Symbols (Functions, Variables, Constants)
-
-pub enum CompilerMode {
- Module,
- Function,
-}
-
-pub struct Compiler {
- symbols: SymbolTableStack,
- mode: CompilerMode,
-}
-
-pub struct CompileOrder {
- code: Vec<Stmt>,
-}
-
-impl Compiler {
- fn new() -> Self {
- Self {
- symbols: SymbolTableStack::default(),
- mode: CompilerMode::Module,
- }
- }
-
- fn compile(&mut self, code: Vec<Stmt>) {
- let mut queue = Vec::<CompileOrder>::new();
-
- for stmt in code {
- match stmt {
- Stmt::DefineFunction {
- ident,
- args,
- body,
- return_type,
- } => {
- self.symbols.push_symbol(ident, Symbol {
- typ: SymbolType::Function(Function {
- arity: args.len() as u8,
- returns_value: return_type.is_some(),
- }),
- });
-
- todo!()
- }
-
- _ => panic!("Failed to compile module due to unexpected statement"),
- }
- }
- }
-
- fn compile_function(&mut self, code: Vec<Stmt>) -> Function {
- unimplemented!()
- }
-}
-
-pub fn generate_symbols() -> SymbolTable {
- let mut table = SymbolTable::default();
- //
- todo!()
-}
diff --git a/crates/sloth/src/compiler/symbol.rs b/crates/sloth/src/compiler/symbol.rs
deleted file mode 100644
index b10918f..0000000
--- a/crates/sloth/src/compiler/symbol.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-use std::collections::HashMap;
-
-pub struct SymbolTableStack {
- inner: Vec<SymbolTable>,
-}
-
-impl SymbolTableStack {
- pub fn push_scope(&mut self) {
- self.inner.push(SymbolTable::default());
- }
-
- pub fn pop_scope(&mut self) -> bool {
- if self.inner.len() == 1 {
- return false;
- }
-
- self.inner.pop();
- true
- }
-
- pub fn get_symbol(&self, identifier: &str) -> Option<&Symbol> {
- for table in self.inner.iter().rev() {
- if let Some(symbol) = table.get(identifier) {
- return Some(symbol);
- }
- }
-
- None
- }
-
- pub fn push_symbol(&mut self, identifier: impl Into<String>, symbol: Symbol) {
- let table = self
- .inner
- .last_mut()
- .expect("Symbol table stack should always have at least 1 table");
- table.insert(identifier.into(), symbol);
- }
-}
-
-impl Default for SymbolTableStack {
- fn default() -> Self {
- Self {
- inner: vec![SymbolTable::default()],
- }
- }
-}
-
-// x 0x00
-// - x 0x01
-// - y 0x02
-// y 0x01
-
-pub type SymbolTable = HashMap<String, Symbol>;
-
-pub struct Symbol {
- pub typ: SymbolType,
-}
-
-pub enum SymbolType {
- Function(Function),
- Variable(Variable),
- Constant(Constant),
-}
-
-pub struct Function {
- pub arity: u8,
- pub returns_value: bool,
- // TODO: Types
-}
-
-pub struct Variable {
- pub idx: u16,
- // TODO: Types
-}
-
-pub struct Constant {
- pub idx: u16,
- // TODO: Types
-}
diff --git a/crates/sloth/src/lexer.rs b/crates/sloth/src/lexer.rs
deleted file mode 100644
index 0afaf1c..0000000
--- a/crates/sloth/src/lexer.rs
+++ /dev/null
@@ -1,559 +0,0 @@
-#![allow(dead_code)]
-
-//! TODO: Lexing Regex Literals
-
-use std::str::Chars;
-
-use thiserror::Error;
-
-#[derive(Debug, Clone, PartialEq, Error)]
-pub enum LexerError {
- #[error("Unexpected token")]
- UnexpectedToken,
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum TokenType {
- // Meta
- DocComment,
- Comment,
-
- // Brackets
- OpeningParen, // (
- ClosingParen, // )
- OpeningBracket, // [
- ClosingBracket, // ]
- OpeningBrace, // {
- ClosingBrace, // }
-
- // Operators
- Plus, // +
- PlusPlus, // ++
- Minus, // -
- Star, // *
- StarStar, // **
- Slash, // /
- Perc, // %
- Tilde, // ~
-
- PlusEq, // +=
- PlusPlusEq, // ++=
- MinusEq, // -=
- StarEq, // *=
- StarStarEq, // **=
- SlashEq, // /=
- PercEq, // %=
- TildeEq, // ~=
-
- Amp, // &
- AmpAmp, // &&
- Pipe, // |
- PipePipe, // ||
- Caret, // ^
-
- Eq, // =
- EqEq, // ==
- Bang, // !
- BangBang, // !!
- BangEq, // !=
-
- Lt, // <
- LtLt, // <<
- LtEq, // <=
- LtLtEq, // <<=
- Gt, // >
- GtGt, // >>
- GtEq, // >=
- GtGtEq, // >>=
-
- Comma,
-
- Question, // ?
- QuestionDot, // ?.
- QuestionQuestion, // ??
- Dot, // .
- DotDot, // ..
-
- Colon, // :
- ColonColon, // ::
- SemiColon, // ;
-
- Arrow, // ->
- FatArrow, // =>
-
- // Keywords
- Val,
- Var,
-
- Fn,
- Return,
-
- If,
- Else,
-
- While,
- For,
- In,
-
- Loop,
- Break,
- Continue,
-
- As,
-
- // Literals
- Integer(i128),
- Float(f64),
- Boolean(bool),
- Character(char),
- String(String),
- Regex(String),
-
- Identifier(String),
-
- // Utility
- Error(LexerError),
-}
-
-#[derive(Debug, Default, Clone, Copy)]
-pub struct Location {
- index: usize,
- pub row: u32,
- pub col: u32,
-}
-
-impl Location {
- fn advance(&mut self, len: usize, newline: bool) {
- if newline {
- self.row += 1;
- self.col = 0;
- } else {
- self.col += 1;
- }
- self.index += len;
- }
-}
-
-#[derive(Debug)]
-pub struct Token<'a> {
- pub tt: TokenType,
- pub lexeme: &'a str,
-
- start: Location,
- end: Location,
-}
-
-pub struct Lexer<'a> {
- source: &'a [u8],
- window: [char; 3],
- chars: Chars<'a>,
-
- start: Location,
- current: Location,
-
- // Keep track if the lexer has encountered an error to stop lexing asap
- errored: bool,
-}
-
-impl<'a> Lexer<'a> {
- pub(crate) fn new(source: &'a str) -> Self {
- let mut chars = source.chars();
- let window = [
- chars.next().unwrap_or('\0'),
- chars.next().unwrap_or('\0'),
- chars.next().unwrap_or('\0'),
- ];
-
- Self {
- source: source.as_bytes(),
- window,
- chars,
- start: Default::default(),
- current: Default::default(),
- errored: false,
- }
- }
-}
-
-impl<'a> Lexer<'a> {
- fn pos(&self) -> usize {
- self.current.index
- }
-
- fn peek(&self) -> char {
- self.window[0]
- }
-
- fn eof(&self) -> bool {
- self.peek() == '\0'
- }
-
- fn advance(&mut self) -> char {
- let current = self.window[0];
- self.window = [
- self.window[1],
- self.window[2],
- self.chars.next().unwrap_or('\0'),
- ];
- self.current.advance(current.len_utf8(), current == '\n');
- current
- }
-
- fn advance_with(&mut self, with: TokenType) -> TokenType {
- self.advance();
- with
- }
-
- fn advance_by(&mut self, amount: usize) {
- for _ in 0..amount {
- self.advance();
- }
- }
-
- fn advance_by_with(&mut self, amount: usize, with: TokenType) -> TokenType {
- self.advance_by(amount);
- with
- }
-
- fn advance_while(&mut self, predicate: impl Fn([char; 3]) -> bool) {
- while !self.eof() && predicate(self.window) {
- self.advance();
- }
- }
-}
-
-impl<'a> Lexer<'a> {
- fn lex_number(&mut self) -> TokenType {
- let mut value = self.advance().to_string();
-
- while self.peek().is_ascii_digit() {
- value.push(self.advance());
- }
-
- if self.peek() == '.' {
- value.push(self.advance());
-
- while self.peek().is_ascii_digit() {
- value.push(self.advance());
- }
-
- TokenType::Float(value.parse::<f64>().expect("Expected float"))
- } else {
- TokenType::Integer(value.parse::<i128>().expect("Expected integer"))
- }
- }
-
- fn lex_string(&mut self) -> TokenType {
- let mut value = String::new();
-
- self.advance();
- loop {
- match self.window {
- ['\\', '"', ..] => {
- self.advance_by(2);
- value.push('"');
- }
- ['\\', 't', ..] => {
- self.advance_by(2);
- value.push('\t');
- }
- ['\\', 'n', ..] => {
- self.advance_by(2);
- value.push('\n');
- }
- ['"', ..] => {
- self.advance();
- break;
- }
- _ => {
- value.push(self.advance());
- continue;
- }
- }
- }
-
- TokenType::String(value)
- }
-}
-
-impl<'a> Iterator for Lexer<'a> {
- type Item = Token<'a>;
-
- fn next(&mut self) -> Option<Self::Item> {
- // Skipping whitespace
- self.advance_while(|it| it[0].is_whitespace());
- self.start = self.current;
-
- // If were at the end of the file or an error has occurred return nothing
- if self.eof() || self.errored {
- return None;
- }
-
- // Figuring out the token type
- let tt = match self.window {
- ['#', '#', ..] => {
- self.advance_while(|it| it[0] != '\n');
- // TODO: TokenType::DocComment
- return self.next();
- }
-
- ['#', ..] => {
- self.advance_while(|it| it[0] != '\n');
- // TODO: okenType::Comment
- return self.next();
- }
-
- // Blocks
- ['(', ..] => self.advance_with(TokenType::OpeningParen),
- [')', ..] => self.advance_with(TokenType::ClosingParen),
- ['[', ..] => self.advance_with(TokenType::OpeningBracket),
- [']', ..] => self.advance_with(TokenType::ClosingBracket),
- ['{', ..] => self.advance_with(TokenType::OpeningBrace),
- ['}', ..] => self.advance_with(TokenType::ClosingBrace),
-
- // Operators
- ['-', '>', ..] => self.advance_by_with(2, TokenType::Arrow),
- ['=', '>', ..] => self.advance_by_with(2, TokenType::FatArrow),
-
- ['+', '+', '='] => self.advance_by_with(3, TokenType::PlusPlusEq),
- ['*', '*', '='] => self.advance_by_with(3, TokenType::StarStarEq),
- ['+', '+', ..] => self.advance_by_with(2, TokenType::PlusPlus),
- ['*', '*', ..] => self.advance_by_with(2, TokenType::StarStar),
-
- ['+', '=', ..] => self.advance_by_with(2, TokenType::PlusEq),
- ['-', '=', ..] => self.advance_by_with(2, TokenType::MinusEq),
- ['*', '=', ..] => self.advance_by_with(2, TokenType::StarEq),
- ['/', '=', ..] => self.advance_by_with(2, TokenType::SlashEq),
- ['%', '=', ..] => self.advance_by_with(2, TokenType::PercEq),
- ['~', '=', ..] => self.advance_by_with(2, TokenType::TildeEq),
-
- ['+', ..] => self.advance_with(TokenType::Plus),
- ['-', ..] => self.advance_with(TokenType::Minus),
- ['*', ..] => self.advance_with(TokenType::Star),
- ['/', ..] => self.advance_with(TokenType::Slash), // TODO: Check for regex literals
- ['%', ..] => self.advance_with(TokenType::Perc),
- ['~', ..] => self.advance_with(TokenType::Tilde),
-
- ['&', '&', ..] => self.advance_by_with(2, TokenType::AmpAmp),
- ['&', ..] => self.advance_with(TokenType::Amp),
-
- ['|', '|', ..] => self.advance_by_with(2, TokenType::PipePipe),
- ['|', ..] => self.advance_with(TokenType::Pipe),
-
- ['^', ..] => self.advance_by_with(2, TokenType::Caret),
-
- ['=', '=', ..] => self.advance_by_with(2, TokenType::EqEq),
- ['!', '=', ..] => self.advance_by_with(2, TokenType::BangEq),
- ['!', '!', ..] => self.advance_by_with(2, TokenType::BangBang),
- ['=', ..] => self.advance_with(TokenType::Eq),
- ['!', ..] => self.advance_with(TokenType::Bang),
-
- ['<', '<', '='] => self.advance_by_with(3, TokenType::LtLtEq),
- ['<', '<', ..] => self.advance_by_with(2, TokenType::LtLt),
- ['<', '=', ..] => self.advance_by_with(2, TokenType::LtEq),
- ['<', ..] => self.advance_with(TokenType::Lt),
-
- ['>', '>', '='] => self.advance_by_with(3, TokenType::GtGtEq),
- ['>', '>', ..] => self.advance_by_with(2, TokenType::GtGt),
- ['>', '=', ..] => self.advance_by_with(2, TokenType::GtEq),
- ['>', ..] => self.advance_with(TokenType::Gt),
-
- [',', ..] => self.advance_with(TokenType::Comma),
-
- ['.', '.', ..] => self.advance_by_with(2, TokenType::DotDot),
- ['.', ..] => self.advance_with(TokenType::Dot),
- ['?', '?', ..] => self.advance_by_with(2, TokenType::QuestionQuestion),
- ['?', '.', ..] => self.advance_by_with(2, TokenType::QuestionDot),
- ['?', ..] => self.advance_with(TokenType::Question),
-
- [';', ..] => self.advance_with(TokenType::SemiColon),
- [':', ':', ..] => self.advance_by_with(2, TokenType::ColonColon),
- [':', ..] => self.advance_with(TokenType::Colon),
-
- // Literals
- ['\'', c, '\''] => self.advance_by_with(3, TokenType::Character(c)),
- ['0'..='9', ..] => self.lex_number(),
- ['"', ..] => self.lex_string(),
-
- ['a'..='z' | 'A'..='Z' | '_' | '$', ..] => {
- let mut value = String::new();
- while matches!(self.peek(), 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '$') {
- value.push(self.advance());
- }
-
- match value.as_str() {
- "val" => TokenType::Val,
- "var" => TokenType::Var,
- "fn" => TokenType::Fn,
- "return" => TokenType::Return,
- "if" => TokenType::If,
- "else" => TokenType::Else,
- "while" => TokenType::While,
- "for" => TokenType::For,
- "in" => TokenType::In,
- "loop" => TokenType::Loop,
- "break" => TokenType::Break,
- "continue" => TokenType::Continue,
- "as" => TokenType::As,
- "true" => TokenType::Boolean(true),
- "false" => TokenType::Boolean(false),
- _ => TokenType::Identifier(value),
- }
- }
-
- _ => {
- self.errored = true;
- TokenType::Error(LexerError::UnexpectedToken)
- }
- };
-
- let lexeme = unsafe {
- // At this point it is already known that the string is valid UTF-8, might
- // aswell not check again
- std::str::from_utf8_unchecked(&self.source[self.start.index..self.pos()])
- };
-
- let token = Token {
- tt,
- lexeme,
- start: self.start,
- end: self.current,
- };
-
- Some(token)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use itertools::Itertools;
-
- use super::{Lexer, TokenType};
- use crate::lexer::LexerError;
-
- #[test]
- fn lex_operators() {
- let source = "+ ++ - * ** / % ~ += ++= -= *= **= /= %= ~= & && | || ^ = == ! !! != < << \
- <<= <= > >> >>= >= , ? ?. ?? . .. : :: ; -> =>";
- let tokens = Lexer::new(source).map(|it| it.tt).collect_vec();
-
- assert_eq!(&tokens, &[
- TokenType::Plus,
- TokenType::PlusPlus,
- TokenType::Minus,
- TokenType::Star,
- TokenType::StarStar,
- TokenType::Slash,
- TokenType::Perc,
- TokenType::Tilde,
- TokenType::PlusEq,
- TokenType::PlusPlusEq,
- TokenType::MinusEq,
- TokenType::StarEq,
- TokenType::StarStarEq,
- TokenType::SlashEq,
- TokenType::PercEq,
- TokenType::TildeEq,
- TokenType::Amp,
- TokenType::AmpAmp,
- TokenType::Pipe,
- TokenType::PipePipe,
- TokenType::Caret,
- TokenType::Eq,
- TokenType::EqEq,
- TokenType::Bang,
- TokenType::BangBang,
- TokenType::BangEq,
- TokenType::Lt,
- TokenType::LtLt,
- TokenType::LtLtEq,
- TokenType::LtEq,
- TokenType::Gt,
- TokenType::GtGt,
- TokenType::GtGtEq,
- TokenType::GtEq,
- TokenType::Comma,
- TokenType::Question,
- TokenType::QuestionDot,
- TokenType::QuestionQuestion,
- TokenType::Dot,
- TokenType::DotDot,
- TokenType::Colon,
- TokenType::ColonColon,
- TokenType::SemiColon,
- TokenType::Arrow,
- TokenType::FatArrow,
- ]);
- }
-
- #[test]
- fn lex_keywords() {
- let source = "val var fn if else while for in loop break continue as true false";
- let tokens = Lexer::new(source).map(|it| it.tt).collect_vec();
-
- assert_eq!(&tokens, &[
- TokenType::Val,
- TokenType::Var,
- TokenType::Fn,
- TokenType::If,
- TokenType::Else,
- TokenType::While,
- TokenType::For,
- TokenType::In,
- TokenType::Loop,
- TokenType::Break,
- TokenType::Continue,
- TokenType::As,
- TokenType::Boolean(true),
- TokenType::Boolean(false),
- ]);
- }
-
- #[test]
- fn lex_literals_a() {
- let source = "foo bar _foo __bar $0 $$1 \"foo\" \"bar\" \"baz\" \"\\\"\" \"\\n\" \"\\t\" \
- 'a' 'b' '\"' 93 3252 238 -382 -832 83 -25 52.9 83.7 12.4 35.2 3.3";
- let tokens = Lexer::new(source).map(|it| it.tt).collect_vec();
-
- assert_eq!(&tokens, &[
- TokenType::Identifier("foo".to_owned()),
- TokenType::Identifier("bar".to_owned()),
- TokenType::Identifier("_foo".to_owned()),
- TokenType::Identifier("__bar".to_owned()),
- TokenType::Identifier("$0".to_owned()),
- TokenType::Identifier("$$1".to_owned()),
- TokenType::String("foo".to_owned()),
- TokenType::String("bar".to_owned()),
- TokenType::String("baz".to_owned()),
- TokenType::String("\"".to_owned()),
- TokenType::String("\n".to_owned()),
- TokenType::String("\t".to_owned()),
- TokenType::Character('a'),
- TokenType::Character('b'),
- TokenType::Character('"'),
- TokenType::Integer(93),
- TokenType::Integer(3252),
- TokenType::Integer(238),
- TokenType::Minus,
- TokenType::Integer(382),
- TokenType::Minus,
- TokenType::Integer(832),
- TokenType::Integer(83),
- TokenType::Minus,
- TokenType::Integer(25),
- TokenType::Float(52.9),
- TokenType::Float(83.7),
- TokenType::Float(12.4),
- TokenType::Float(35.2),
- TokenType::Float(3.3),
- ]);
- }
-
- #[test]
- fn lex_errors() {
- let source = "`";
- let tokens = Lexer::new(source).map(|it| it.tt).collect_vec();
-
- assert_eq!(&tokens, &[TokenType::Error(LexerError::UnexpectedToken)]);
- }
-}
diff --git a/crates/sloth/src/main.rs b/crates/sloth/src/main.rs
deleted file mode 100644
index b770684..0000000
--- a/crates/sloth/src/main.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-#![warn(
- clippy::wildcard_imports,
- clippy::string_add,
- clippy::string_add_assign,
- clippy::manual_ok_or,
- unused_lifetimes
-)]
-
-pub mod compiler;
-pub mod lexer;
-pub mod parser;
-
-use std::collections::HashMap;
-
-// use std::{env, fs};
-use itertools::Itertools;
-use lexer::Lexer;
-use parser::AstParser;
-use sloth_vm::value::Function;
-use sloth_vm::{ObjectMap, VM};
-
-fn main() {
- // let args = env::args().collect_vec();
- //
- // if args.len() < 2 {
- // println!("Sloth programming language interpreter\n");
- // println!("Usage: sloth <file>");
- // return;
- // }
- //
- // let source_path = &args[1];
- // let Ok(source) = fs::read_to_string(source_path) else {
- // println!("Error while reading '{source_path}'");
- // return;
- // };
- // let source = " 3 + 7 ;";
- // let source = r#"
-
- // fn hello() -> int {
- // return 3 + 7;
- // }
-
- // hello();
- // hello();
- // hello();
- // hello();
- // hello();
- // hello();
-
- // "#;
- let source = r#"
-
- fn hello() -> int {
- var x = 5;
- x = 7;
- return x;
- }
-
- hello();
-
- "#;
-
- let tokens = Lexer::new(source).collect_vec();
- let ast = AstParser::new(tokens).parse();
- let mut object_map = ObjectMap::default();
- // let code = Compiler::compile(&mut object_map, HashMap::default(),
- // ast.clone());
-
- // println!("{ast:?}\n\n");
- // println!("{:?}\n\n", code.constants);
- // println!("{:?}\n\n", code.code);
- //
- // let mut vm = VM::new(object_map, Function::root(code));
- // vm.run();
- // println!("{:?}", vm.stack.peek());
-}
diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs
deleted file mode 100644
index 543ea3a..0000000
--- a/crates/sloth/src/parser/ast.rs
+++ /dev/null
@@ -1,115 +0,0 @@
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum BinaryOp {
- Add,
- Con,
- Sub,
- Mul,
- Pow,
- Div,
- Mod,
-
- BWSftRight,
- BWSftLeft,
- BWAnd,
- BWOr,
- BWXor,
-
- Lt,
- Gt,
- LtEq,
- GtEq,
- EqEq,
- NotEq,
- LogAnd,
- LogOr,
- Range,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum UnaryOp {
- Not,
- Neg,
-
- BWComp,
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Literal {
- Integer(i128),
- Float(f64),
- Bool(bool),
- Char(char),
- String(String),
- Regex(String),
- List(Vec<Expr>),
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Expr {
- Grouping(Box<Expr>),
- BinaryOp {
- op: BinaryOp,
- lhs: Box<Expr>,
- rhs: Box<Expr>,
- },
- UnaryOp {
- op: UnaryOp,
- value: Box<Expr>,
- },
- Call {
- ident: Box<Expr>,
- args: Vec<Expr>,
- },
- Variable(String),
- Literal(Literal),
- Lambda, // TODO: Lambda
-}
-
-#[derive(PartialEq, Clone, Debug)]
-pub struct FuncArgs {
- pub name: String,
- pub typ: Option<String>,
-}
-
-#[derive(PartialEq, Clone, Debug)]
-pub enum Stmt {
- ExprStmt(Expr),
- DefineFunction {
- ident: String,
- args: Vec<FuncArgs>,
- body: Vec<Stmt>,
- return_type: Option<String>,
- },
- DefineVariable {
- name: String,
- value: Expr,
- typ: Option<String>,
- },
- DefineValue {
- name: String,
- value: Expr,
- typ: Option<String>,
- },
- AssignVariable {
- name: String,
- value: Expr,
- },
- If {
- expr: Expr,
- body: Vec<Stmt>,
- else_if: Vec<(Expr, Stmt)>,
- els: Option<Box<Stmt>>,
- },
- For {
- name: String,
- iter: Expr,
- body: Vec<Stmt>,
- },
- While {
- condition: Expr,
- body: Vec<Stmt>,
- },
- Return {
- value: Expr,
- },
-}
diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs
deleted file mode 100644
index 9e81f7f..0000000
--- a/crates/sloth/src/parser/expr.rs
+++ /dev/null
@@ -1,261 +0,0 @@
-use super::ast::{BinaryOp, Expr, Literal, UnaryOp};
-use super::AstParser;
-use crate::lexer::TokenType;
-
-/// Implementation containing parsers internal components related to expressions
-impl<'a> AstParser<'a> {
- // FIXME: Should probably avoid cloning token types
-
- pub fn expression(&mut self) -> Expr {
- self.logical_or()
- }
-
- fn unary(&mut self) -> Expr {
- if !self.eof()
- && matches!(
- self.peek().tt,
- TokenType::Bang | TokenType::Plus | TokenType::Minus
- )
- {
- let operator = match self.advance().unwrap().tt.clone() {
- TokenType::Bang => UnaryOp::Not,
- TokenType::Tilde => UnaryOp::BWComp,
- TokenType::Minus => UnaryOp::Neg,
- _ => panic!(),
- };
-
- let rhs = self.unary();
- return Expr::UnaryOp {
- op: (operator),
- value: (Box::new(rhs)),
- };
- }
-
- self.call()
- }
-
- fn call(&mut self) -> Expr {
- let mut expr = self.primary();
-
- if self.advance_if_eq(&TokenType::OpeningParen) {
- let mut arguments = Vec::<Expr>::new();
-
- if self.peek().tt != TokenType::ClosingParen {
- loop {
- arguments.push(self.expression());
- if !self.advance_if_eq(&TokenType::Comma) {
- break;
- }
- }
- }
-
- self.consume(
- TokenType::ClosingParen,
- "Expected ')' to close off function call",
- );
-
- // let Expr::Variable(_ident) = expr else { panic!("uh oh spaghettio"); };
-
- expr = Expr::Call {
- ident: (Box::new(expr)),
- args: (arguments),
- }
- }
-
- expr
- }
-
- fn primary(&mut self) -> Expr {
- match self.advance().unwrap().tt.clone() {
- TokenType::Integer(literal) => Expr::Literal(Literal::Integer(literal)),
- TokenType::Float(literal) => Expr::Literal(Literal::Float(literal)),
- TokenType::Boolean(literal) => Expr::Literal(Literal::Bool(literal)),
- TokenType::Character(literal) => Expr::Literal(Literal::Char(literal)),
- TokenType::String(literal) => Expr::Literal(Literal::String(literal)),
- TokenType::Regex(literal) => Expr::Literal(Literal::Regex(literal)),
- TokenType::Identifier(ident) => Expr::Variable(ident),
- TokenType::OpeningParen => {
- let expr = self.expression();
- self.consume(TokenType::ClosingParen, "Must end expression with ')'");
- Expr::Grouping(Box::new(expr))
- }
- TokenType::OpeningBracket => {
- let mut expr: Vec<Expr> = Vec::new();
-
- while !self.eof() && self.peek().tt != TokenType::ClosingBracket {
- let exp = self.expression();
- expr.push(exp);
-
- self.advance_if_eq(&TokenType::Comma);
- }
- self.consume(TokenType::ClosingBracket, "Expected ']' at end of list");
- Expr::Literal(Literal::List(expr))
- }
- _ => unimplemented!("{:?}", self.peek()),
- }
- }
-}
-
-// Macro to generate repetitive binary expressions. Things like addition,
-// multiplication, exc.
-macro_rules! binary_expr {
- ($name:ident, $parent:ident, $pattern:pat) => {
- fn $name(&mut self) -> Expr {
- let mut expr = self.$parent();
-
- while !self.eof() && matches!(self.peek().tt, $pattern) {
- let operator = match self.advance().unwrap().tt.clone() {
- TokenType::Plus => BinaryOp::Add,
- TokenType::PlusPlus => BinaryOp::Con,
- TokenType::Minus => BinaryOp::Sub,
- TokenType::Star => BinaryOp::Mul,
- TokenType::StarStar => BinaryOp::Pow,
- TokenType::Slash => BinaryOp::Div,
- TokenType::Perc => BinaryOp::Mod,
- TokenType::DotDot => BinaryOp::Range,
-
- TokenType::LtLt => BinaryOp::BWSftRight,
- TokenType::GtGt => BinaryOp::BWSftLeft,
- TokenType::Amp => BinaryOp::BWAnd,
- TokenType::Pipe => BinaryOp::BWOr,
- TokenType::Caret => BinaryOp::BWXor,
-
- TokenType::Lt => BinaryOp::Lt,
- TokenType::Gt => BinaryOp::Gt,
- TokenType::LtEq => BinaryOp::LtEq,
- TokenType::GtEq => BinaryOp::GtEq,
- TokenType::EqEq => BinaryOp::EqEq,
- TokenType::BangEq => BinaryOp::NotEq,
- TokenType::AmpAmp => BinaryOp::LogAnd,
- TokenType::PipePipe => BinaryOp::LogOr,
- _ => panic!("uh oh spagghetio"),
- };
-
- let rhs = self.$parent();
- expr = Expr::BinaryOp {
- op: (operator),
- lhs: (Box::new(expr)),
- rhs: (Box::new(rhs)),
- }
- }
-
- expr
- }
- };
-}
-
-#[rustfmt::skip]
-#[allow(unused_parens)]
-impl<'a> AstParser<'a> {
- // Binary expressions in order of precedence from lowest to highest.
- binary_expr!(logical_or , logical_and , (TokenType::PipePipe));
- binary_expr!(logical_and , range , (TokenType::AmpAmp));
- binary_expr!(range , equality , (TokenType::DotDot));
- binary_expr!(equality , comparison , (TokenType::BangEq | TokenType::EqEq));
- binary_expr!(comparison , bitwise_shifting, (TokenType::Lt | TokenType::Gt | TokenType::LtEq | TokenType::GtEq));
- binary_expr!(bitwise_shifting, additive , (TokenType::LtLt | TokenType::GtGt));
- binary_expr!(additive , multiplicative , (TokenType::Plus | TokenType::Minus));
- binary_expr!(multiplicative , unary , (TokenType::Star | TokenType::Slash | TokenType::Perc));
-}
-
-#[cfg(test)]
-mod tests {
- use itertools::Itertools;
-
- use super::{AstParser, BinaryOp, Expr, Literal};
- use crate::lexer::Lexer;
- use crate::parser::ast::UnaryOp;
-
- #[test]
- fn basic_expression_a() {
- let lexer = Lexer::new("3 + 5 * 4");
- let tokens = lexer.collect_vec();
-
- let expected_ast = Expr::BinaryOp {
- op: BinaryOp::Add,
- lhs: Box::new(Expr::Literal(Literal::Integer(3))),
- rhs: Box::new(Expr::BinaryOp {
- op: BinaryOp::Mul,
- lhs: Box::new(Expr::Literal(Literal::Integer(5))),
- rhs: Box::new(Expr::Literal(Literal::Integer(4))),
- }),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.expression();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
-
- #[test]
- fn basic_expression_b() {
- let lexer = Lexer::new("17 - (-5 + 5) / 6");
- let tokens = lexer.collect_vec();
-
- let expected_ast = Expr::BinaryOp {
- op: BinaryOp::Sub,
- lhs: Box::new(Expr::Literal(Literal::Integer(17))),
- rhs: Box::new(Expr::BinaryOp {
- op: BinaryOp::Div,
- lhs: Box::new(Expr::Grouping(Box::new(Expr::BinaryOp {
- op: BinaryOp::Add,
- lhs: Box::new(Expr::UnaryOp {
- op: UnaryOp::Neg,
- value: Box::new(Expr::Literal(Literal::Integer(5))),
- }),
- rhs: Box::new(Expr::Literal(Literal::Integer(5))),
- }))),
- rhs: Box::new(Expr::Literal(Literal::Integer(6))),
- }),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.expression();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
- #[test]
- fn basic_expression_c() {
- let lexer = Lexer::new("[1, 2, 3]");
- let tokens = lexer.collect_vec();
-
- let expected_ast = Expr::Literal(Literal::List(vec![
- Expr::Literal(Literal::Integer(1)),
- Expr::Literal(Literal::Integer(2)),
- Expr::Literal(Literal::Integer(3)),
- ]));
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.expression();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
- #[test]
- fn basic_expression_d() {
- let lexer = Lexer::new("1 .. 17");
- let tokens = lexer.collect_vec();
-
- let expected_ast = Expr::BinaryOp {
- op: (BinaryOp::Range),
- lhs: (Box::new(Expr::Literal(Literal::Integer(1)))),
- rhs: (Box::new(Expr::Literal(Literal::Integer(17)))),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.expression();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
-}
diff --git a/crates/sloth/src/parser/mod.rs b/crates/sloth/src/parser/mod.rs
deleted file mode 100644
index 9d77acc..0000000
--- a/crates/sloth/src/parser/mod.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-pub mod ast;
-pub mod expr;
-pub mod stmt;
-
-use crate::lexer::{Token, TokenType};
-#[derive(Debug)]
-pub struct AstParser<'a> {
- tokens: Vec<Token<'a>>,
- index: usize,
-}
-
-/// Implementation containing utilities used by the parsers internal components
-impl<'a> AstParser<'a> {
- pub fn new(tokens: Vec<Token<'a>>) -> Self {
- Self { tokens, index: 0 }
- }
- pub fn peek(&self) -> &Token {
- &self.tokens[self.index]
- }
-
- pub fn advance(&mut self) -> Option<&Token> {
- if self.eof() {
- return None;
- }
-
- self.index += 1;
- Some(&self.tokens[self.index - 1])
- }
-
- pub fn advance_if(&mut self, next: impl FnOnce(&Token) -> bool) -> bool {
- if self.eof() {
- return false;
- }
-
- if next(self.peek()) {
- self.advance();
- return true;
- }
-
- false
- }
-
- pub fn advance_if_eq(&mut self, next: &TokenType) -> bool {
- self.advance_if(|it| it.tt == *next)
- }
-
- pub fn consume(&mut self, next: TokenType, error: &str) {
- if std::mem::discriminant(&self.peek().tt) != std::mem::discriminant(&next) {
- panic!("{error} at index {:?}", self.index);
- }
- self.advance();
- }
-
- pub fn eof(&self) -> bool {
- self.index >= self.tokens.len()
- }
-}
diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs
deleted file mode 100644
index 1a961b1..0000000
--- a/crates/sloth/src/parser/stmt.rs
+++ /dev/null
@@ -1,646 +0,0 @@
-use super::ast::{Expr, FuncArgs, Stmt};
-use super::AstParser;
-use crate::lexer::TokenType;
-
-impl<'a> AstParser<'a> {
- pub fn parse(&mut self) -> Vec<Stmt> {
- let mut statements = Vec::new();
-
- while !self.eof() {
- statements.push(self.statement());
- }
-
- statements
- }
-
- fn statement(&mut self) -> Stmt {
- if self.advance_if_eq(&TokenType::Var) {
- return self.var_statement();
- }
-
- if self.advance_if_eq(&TokenType::Val) {
- return self.val_statement();
- }
-
- if self.advance_if_eq(&TokenType::If) {
- return self.if_statement();
- }
-
- if self.advance_if_eq(&TokenType::For) {
- return self.for_statement();
- }
-
- if self.advance_if_eq(&TokenType::While) {
- return self.while_statement();
- }
-
- if self.advance_if_eq(&TokenType::Fn) {
- return self.function_statement();
- }
-
- if self.advance_if_eq(&TokenType::Return) {
- return self.return_statement();
- }
-
- self.mut_statement()
-
- // If we couldn't parse a statement return an expression statement
- // self.expression_statement()
- }
-
- fn mut_statement(&mut self) -> Stmt {
- let TokenType::Identifier(ident) = self.peek().tt.clone() else {
- panic!("Identifier error {:?}", self.peek());
- };
-
- self.advance();
- let next = self.advance().unwrap().tt.clone();
- if next == TokenType::Eq {
- let value = self.expression();
- self.consume(TokenType::SemiColon, "No semi colon for me i guess");
- return Stmt::AssignVariable {
- name: (ident),
- value: (value),
- };
- } else if next == TokenType::OpeningParen {
- let mut arguments = Vec::<Expr>::new();
-
- if self.peek().tt != TokenType::ClosingParen {
- loop {
- arguments.push(self.expression());
- if !self.advance_if_eq(&TokenType::Comma) {
- break;
- }
- }
- }
-
- self.consume(
- TokenType::ClosingParen,
- "Expected ')' to close off function call",
- );
-
- self.consume(TokenType::SemiColon, "No semi colon for me i guess");
- return Stmt::ExprStmt(Expr::Call {
- ident: Box::new(Expr::Variable(ident)),
- args: (arguments),
- });
- }
- self.expression_statement()
- }
-
- fn var_statement(&mut self) -> Stmt {
- let TokenType::Identifier(ident) = self.peek().tt.clone() else {
- panic!("Identifier expected after 'var', not {:?}", self.peek());
- };
-
- self.advance();
-
- let mut typ: Option<String> = None;
- if self.peek().tt.clone() == TokenType::Colon {
- self.consume(TokenType::Colon, "How did you even get this error?");
- let TokenType::Identifier(name) = self.peek().tt.clone() else {
- panic!("Type expected after identifier, not {:?}", self.peek());
- };
- self.advance();
- typ = Some(name);
- }
-
- self.consume(TokenType::Eq, "Expected '=' after identifier at ");
-
- let value = self.expression();
-
- self.consume(TokenType::SemiColon, "Expected ';' at end of statement");
-
- Stmt::DefineVariable {
- name: (ident),
- value: (value),
- typ: (typ),
- }
- }
-
- fn val_statement(&mut self) -> Stmt {
- let TokenType::Identifier(ident) = self.peek().tt.clone() else {
- panic!("Identifier expected after 'val'");
- };
-
- self.advance(); // Advancing from the identifier
-
- let mut typ: Option<String> = None;
- if self.peek().tt.clone() == TokenType::Colon {
- self.consume(TokenType::Colon, "How did you even get this error?");
- let TokenType::Identifier(name) = self.peek().tt.clone() else {
- panic!("Type expected after identifier, not {:?}", self.peek());
- };
- self.advance();
- typ = Some(name);
- }
-
- self.consume(TokenType::Eq, "Expected '=' after identifier");
-
- let value = self.expression();
-
- self.consume(TokenType::SemiColon, "Expected ';' at end of statement");
-
- Stmt::DefineValue {
- name: (ident),
- value: (value),
- typ: (typ),
- }
- }
-
- fn if_statement(&mut self) -> Stmt {
- let condition = self.expression();
-
- self.consume(
- TokenType::OpeningBrace,
- "Expected '{' at beggining of block",
- );
- let mut body = Vec::new();
- while !self.eof() && self.peek().tt != TokenType::ClosingBrace {
- body.push(self.statement());
- }
- self.advance();
- Stmt::If {
- expr: (condition),
- body: (body),
- else_if: (Vec::new()),
- els: (None),
- } // TODO: implement else if and else
- }
-
- fn for_statement(&mut self) -> Stmt {
- let binding = self.expression();
- let Expr::Variable(binding) = binding else {
- panic!("Left side of for statement must be identifier");
- };
-
- self.consume(
- TokenType::In,
- "Expected 'in' in between identifier and range",
- );
-
- // let range_start = self.expression();
- // self.consume(
- // TokenType::DotDot,
- // "Expected '..' denoting min and max of range",
- // );
- // let range_end = self.expression();
-
- let expr = self.expression();
-
- self.consume(TokenType::OpeningBrace, "Expected '{' after iterator");
-
- let mut body = Vec::new();
- while !self.eof() && self.peek().tt != TokenType::ClosingBrace {
- body.push(self.statement());
- }
- self.advance();
-
- Stmt::For {
- name: (binding),
- iter: (expr),
- body: (body),
- }
- } // TODO: Fix this garbage
-
- fn while_statement(&mut self) -> Stmt {
- let condition = self.expression();
-
- self.consume(
- TokenType::OpeningBrace,
- "Expected '{' at beggining of block",
- );
- let mut body = Vec::new();
- while !self.eof() && self.peek().tt != TokenType::ClosingBrace {
- println!("{:?}", self.peek().tt);
- body.push(self.statement());
- }
- self.consume(
- TokenType::ClosingBrace,
- "Expected '}' after block on while loop",
- );
-
- self.advance();
- Stmt::While { condition, body }
- }
-
- fn expression_statement(&mut self) -> Stmt {
- let expr = self.expression();
-
- // FIXME: Move assignment handling
- // if self.advance_if_eq(&TokenType::Eq) {
- // if let Expr::Literal(_ident) = &expr {
- // let value = self.expression();
-
- // self.consume(
- // TokenType::SemiColon,
- // "Expected ';' at end of
- // statement",
- // ); // return Stmt::DefineVariable {
- // // name: (ident.clone()),
- // // value: (value),
- // // typ: (None),
- // // };
- // return Stmt::ExprStmt(expr);
- // }
- // }
-
- self.consume(
- TokenType::SemiColon,
- "Expected ';' at end of expr statement",
- );
- Stmt::ExprStmt(expr)
- }
-
- fn function_statement(&mut self) -> Stmt {
- let TokenType::Identifier(ident) = self.advance().unwrap().tt.clone() else {
- panic!("Identifier expected after 'fn'");
- };
-
- self.consume(TokenType::OpeningParen, "Expected '(' after identifier");
- let mut args: Vec<FuncArgs> = Vec::new();
- while !self.eof() && self.peek().tt != TokenType::ClosingParen {
- let TokenType::Identifier(name) = self.advance().unwrap().tt.clone() else {
- panic!("parameter expected after '('");
- };
-
- let mut typ: Option<String> = None;
-
- if self.peek().tt.clone() == TokenType::Colon {
- self.consume(TokenType::Colon, "How did you even get this error?");
- let TokenType::Identifier(name) = self.peek().tt.clone() else {
- panic!("Type expected after ':', not {:?}", self.peek());
- };
- self.advance();
- typ = Some(name);
- }
-
- self.advance_if_eq(&TokenType::Comma);
-
- let arg = FuncArgs {
- name: (name),
- typ: (typ),
- };
- args.push(arg);
- }
- self.advance();
- let mut typ: Option<String> = None;
- if self.peek().tt.clone() == TokenType::Arrow {
- self.advance();
- let TokenType::Identifier(name) = self.peek().tt.clone() else {
- panic!("Type expected after ':', not {:?}", self.peek());
- };
- typ = Some(name);
- self.advance();
- }
- self.consume(TokenType::OpeningBrace, "Expected '{' after parameters");
- let mut body = Vec::new();
- while !self.eof() && self.peek().tt != TokenType::ClosingBrace {
- body.push(self.statement());
- }
- self.consume(TokenType::ClosingBrace, "Expected '}' after body");
-
- Stmt::DefineFunction {
- ident: (ident),
- args: (args),
- body: (body),
- return_type: (typ),
- }
- }
-
- fn return_statement(&mut self) -> Stmt {
- let expr = self.expression();
- self.consume(TokenType::SemiColon, "Expected ';' after return statement");
- Stmt::Return { value: (expr) }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use itertools::Itertools;
-
- use super::{AstParser, Stmt};
- use crate::lexer::Lexer;
- use crate::parser::ast::{BinaryOp, Expr, FuncArgs, Literal, UnaryOp};
-
- #[test]
- fn basic_statement_a() {
- let lexer = Lexer::new("var test_a: int = 5 + 3;");
- let tokens = lexer.collect_vec();
-
- let expected_ast = Stmt::DefineVariable {
- name: ("test_a".to_string()),
- value: (Expr::BinaryOp {
- op: (BinaryOp::Add),
- lhs: (Box::new(Expr::Literal(Literal::Integer(5)))),
- rhs: (Box::new(Expr::Literal(Literal::Integer(3)))),
- }),
- typ: Some("int".to_string()),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.statement();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
-
- #[test]
- fn basic_statement_b() {
- let lexer = Lexer::new("val test_b = \"Hello World\";");
- let tokens = lexer.collect_vec();
-
- let expected_ast = Stmt::DefineValue {
- name: ("test_b".to_string()),
- value: (Expr::Literal(Literal::String("Hello World".to_string()))),
- typ: (None),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.statement();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
-
- #[test]
- fn basic_statement_c() {
- let lexer = Lexer::new(
- "\
- fn test_c (a, b, c) {\nreturn (a + b * c);\n}",
- );
- let tokens = lexer.collect_vec();
- println!("{tokens:?}");
-
- let expected_ast = Stmt::DefineFunction {
- ident: ("test_c".to_string()),
- args: (vec![
- FuncArgs {
- name: ("a".to_string()),
- typ: None,
- },
- FuncArgs {
- name: ("b".to_string()),
- typ: None,
- },
- FuncArgs {
- name: ("c".to_string()),
- typ: None,
- },
- ]),
- body: (vec![Stmt::Return {
- value: (Expr::Grouping(Box::new(Expr::BinaryOp {
- op: BinaryOp::Add,
- lhs: Box::new(Expr::Variable("a".to_string())),
- rhs: Box::new(Expr::BinaryOp {
- op: BinaryOp::Mul,
- lhs: Box::new(Expr::Variable("b".to_string())),
- rhs: Box::new(Expr::Variable("c".to_string())),
- }),
- }))),
- }]),
- return_type: (None),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.statement();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
- #[test]
- fn basic_statement_d() {
- let lexer = Lexer::new(
- "\
- while true {\nprint(\"Hello World\");\nprintln(5 + 7/-3);\n}",
- );
- let tokens = lexer.collect_vec();
- println!("{tokens:?}");
-
- let expected_ast = Stmt::While {
- condition: (Expr::Literal(Literal::Bool(true))),
- body: (vec![
- Stmt::ExprStmt(Expr::Call {
- ident: Box::new(Expr::Variable("print".to_string())),
- args: (vec![Expr::Literal(Literal::String("Hello World".to_string()))]),
- }),
- Stmt::ExprStmt(Expr::Call {
- ident: Box::new(Expr::Variable("println".to_string())),
- args: (vec![Expr::BinaryOp {
- op: (BinaryOp::Add),
- lhs: (Box::new(Expr::Literal(Literal::Integer(5)))),
- rhs: (Box::new(Expr::BinaryOp {
- op: (BinaryOp::Div),
- lhs: (Box::new(Expr::Literal(Literal::Integer(7)))),
- rhs: (Box::new(Expr::UnaryOp {
- op: (UnaryOp::Neg),
- value: (Box::new(Expr::Literal(Literal::Integer(3)))),
- })),
- })),
- }]),
- }),
- ]),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.statement();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
- #[test]
- fn basic_statement_e() {
- let lexer = Lexer::new(
- "\
- if a+5 > 10 {\nprint(a);\n}\nif a+5 < 10 {\nprintln(10);\n}\nif a+5 == 10 \
- {\nprint(toString(10));\na = true;\n}",
- );
- let tokens = lexer.collect_vec();
- // println!("{tokens:?}");
-
- let expected_ast = vec![
- Stmt::If {
- expr: (Expr::BinaryOp {
- op: (BinaryOp::Gt),
- lhs: (Box::new(Expr::BinaryOp {
- op: (BinaryOp::Add),
- lhs: (Box::new(Expr::Variable("a".to_string()))),
- rhs: (Box::new(Expr::Literal(Literal::Integer(5)))),
- })),
- rhs: (Box::new(Expr::Literal(Literal::Integer(10)))),
- }),
- body: (vec![Stmt::ExprStmt(Expr::Call {
- ident: (Box::new(Expr::Variable("print".to_string()))),
- args: (vec![Expr::Variable("a".to_string())]),
- })]),
- else_if: (Vec::new()),
- els: (None),
- },
- Stmt::If {
- expr: (Expr::BinaryOp {
- op: (BinaryOp::Lt),
- lhs: (Box::new(Expr::BinaryOp {
- op: (BinaryOp::Add),
- lhs: (Box::new(Expr::Variable("a".to_string()))),
- rhs: (Box::new(Expr::Literal(Literal::Integer(5)))),
- })),
- rhs: (Box::new(Expr::Literal(Literal::Integer(10)))),
- }),
- body: (vec![Stmt::ExprStmt(Expr::Call {
- ident: (Box::new(Expr::Variable("println".to_string()))),
- args: (vec![Expr::Literal(Literal::Integer(10))]),
- })]),
- else_if: (Vec::new()),
- els: (None),
- },
- Stmt::If {
- expr: (Expr::BinaryOp {
- op: (BinaryOp::EqEq),
- lhs: (Box::new(Expr::BinaryOp {
- op: (BinaryOp::Add),
- lhs: (Box::new(Expr::Variable("a".to_string()))),
- rhs: (Box::new(Expr::Literal(Literal::Integer(5)))),
- })),
- rhs: (Box::new(Expr::Literal(Literal::Integer(10)))),
- }),
- body: (vec![
- Stmt::ExprStmt(Expr::Call {
- ident: (Box::new(Expr::Variable("print".to_string()))),
- // ident: (Box::new(Expr::Literal(Literal::String("print".to_string())))),
- args: (vec![Expr::Call {
- ident: (Box::new(Expr::Variable("toString".to_string()))),
- // ident: Box::new(Expr::Literal(Literal::String("toString".
- // to_string()))),
- args: vec![Expr::Literal(Literal::Integer(10))],
- }]),
- }),
- Stmt::AssignVariable {
- name: ("a".to_string()),
- value: (Expr::Literal(Literal::Bool(true))),
- },
- ]),
-
- else_if: (Vec::new()),
- els: (None),
- },
- ];
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.parse();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
-
- #[test]
- fn basic_statement_f() {
- let lexer = Lexer::new("test_a = 5 + 3;");
- let tokens = lexer.collect_vec();
-
- let expected_ast = Stmt::AssignVariable {
- name: ("test_a".to_string()),
- value: (Expr::BinaryOp {
- op: (BinaryOp::Add),
- lhs: (Box::new(Expr::Literal(Literal::Integer(5)))),
- rhs: (Box::new(Expr::Literal(Literal::Integer(3)))),
- }),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.statement();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
- #[test]
- fn basic_statement_g() {
- let lexer = Lexer::new(
- "\
- fn times_two(x: int) -> int {\nval y: int = x*2;\nreturn y;\n}",
- );
- let tokens = lexer.collect_vec();
-
- let expected_ast = Stmt::DefineFunction {
- ident: ("times_two".to_string()),
- args: (vec![FuncArgs {
- name: ("x".to_string()),
- typ: (Some("int".to_string())),
- }]),
- body: (vec![
- Stmt::DefineValue {
- name: "y".to_string(),
- value: (Expr::BinaryOp {
- op: (BinaryOp::Mul),
- lhs: (Box::new(Expr::Variable("x".to_string()))),
- rhs: (Box::new(Expr::Literal(Literal::Integer(2)))),
- }),
- typ: Some("int".to_string()),
- },
- Stmt::Return {
- value: (Expr::Variable("y".to_string())),
- },
- ]),
-
- return_type: Some("int".to_string()),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.statement();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
-
- #[test]
- fn basic_statement_h() {
- let lexer = Lexer::new("for i in 1 .. 3 {\nfor j in [1, 2, 3] {\nprint(j*i);}}");
- let tokens = lexer.collect_vec();
-
- let expected_ast = Stmt::For {
- name: ("i".to_string()),
- iter: (Expr::BinaryOp {
- op: (BinaryOp::Range),
- lhs: (Box::new(Expr::Literal(Literal::Integer(1)))),
- rhs: (Box::new(Expr::Literal(Literal::Integer(3)))),
- }),
- body: (vec![Stmt::For {
- name: ("j".to_string()),
- iter: (Expr::Literal(Literal::List(vec![
- Expr::Literal(Literal::Integer(1)),
- Expr::Literal(Literal::Integer(2)),
- Expr::Literal(Literal::Integer(3)),
- ]))),
- body: (vec![Stmt::ExprStmt(Expr::Call {
- ident: Box::new(Expr::Variable("print".to_string())),
- args: (vec![Expr::BinaryOp {
- op: (BinaryOp::Mul),
- lhs: (Box::new(Expr::Variable("j".to_string()))),
- rhs: (Box::new(Expr::Variable("i".to_string()))),
- }]),
- })]),
- }]),
- };
-
- let mut parser = AstParser::new(tokens);
- let generated_ast = parser.statement();
-
- println!("Expected AST:\n{expected_ast:#?}\n\n");
- println!("Generated AST:\n{generated_ast:#?}\n\n");
-
- assert_eq!(expected_ast, generated_ast);
- }
-}
diff --git a/crates/sloth_asm/Cargo.toml b/crates/sloth_asm/Cargo.toml
deleted file mode 100644
index b3ae934..0000000
--- a/crates/sloth_asm/Cargo.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-[package]
-name = "sloth_asm"
-
-license.workspace = true
-version.workspace = true
-edition.workspace = true
-
-[dependencies]
diff --git a/crates/sloth_asm/src/lib.rs b/crates/sloth_asm/src/lib.rs
deleted file mode 100644
index 8b13789..0000000
--- a/crates/sloth_asm/src/lib.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/crates/sloth_bytecode/Cargo.toml b/crates/sloth_bytecode/Cargo.toml
deleted file mode 100644
index 0de211f..0000000
--- a/crates/sloth_bytecode/Cargo.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-[package]
-name = "sloth_bytecode"
-
-license.workspace = true
-version.workspace = true
-edition.workspace = true
-
-[dependencies]
-byteorder = "1.4.3"
diff --git a/crates/sloth_bytecode/src/lib.rs b/crates/sloth_bytecode/src/lib.rs
deleted file mode 100644
index c8152f6..0000000
--- a/crates/sloth_bytecode/src/lib.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-#![allow(dead_code)]
-#![warn(
- clippy::wildcard_imports,
- clippy::string_add,
- clippy::string_add_assign,
- clippy::manual_ok_or,
- unused_lifetimes
-)]
-
-pub enum Error {
- UnknownOpcode(u8),
- InvalidArguments,
- Eof,
-}
-
-macro_rules! opcodes {
- ( $( $code:literal $name:ident $docs:literal ),* ) => {
- #[repr(u8)]
- #[derive(Debug, Clone, Copy, Eq, PartialEq)]
- pub enum Opcode {
- $(
- #[doc = $docs]
- $name = $code
- ),*
- }
-
- impl Opcode {
- pub fn into_u8(self) -> u8 {
- self as u8
- }
-
- pub fn from_u8(value: u8) -> Opcode {
- match value {
- $( $code => Self:: $name , )*
- _ => panic!("Invalid opcode"),
- }
- }
- }
- };
-}
-
-opcodes! {
- 0x00 Constant "Push a constant value onto the stack",
- 0x01 Load "Load a value from a variable",
- 0x02 Push "Push a value to a variable",
-
- 0x10 Dup "Duplicate a value on the stack",
- 0x11 Pop "Pop a value from the stack",
-
- 0x12 GetGlobal "Get a global value",
- 0x13 SetGlobal "Set a global value",
- 0x14 GetLocal "Get a local value",
- 0x15 SetLocal "Set a local value",
- 0x16 Box "Box a value on the stack",
-
- 0x20 Add "Add the last 2 values on the stack",
- 0x21 Sub "Subtract the last 2 values on the stack",
- 0x22 Mul "Multiply the last 2 values on the stack",
- 0x23 Div "Divide the last 2 values on the stack",
- 0x24 Mod "Modulo the last 2 values on the stack",
-
- 0x30 Eq "Check if the last 2 values on the stack are equal",
- 0x31 Ne "Check if the last 2 values on the stack are not equal",
-
- 0x40 Jump "Jump to a specific point in the program",
- 0x41 JumpIf "Jump to a specific point in the program if true is on the stack",
-
- 0x50 Call "Call function on stack",
- 0x51 CallNative "Call native function",
- 0x52 Return "Return from function on stack",
-
- 0xE0 Halt "Halt the program",
-
- 0xF0 VMReturn "[DEBUG] Pop value from stack and return it fromthe program",
- 0xF1 VMPrint "[DEBUG] Print value to console"
-}
diff --git a/crates/sloth_vm/Cargo.toml b/crates/sloth_vm/Cargo.toml
deleted file mode 100644
index f484402..0000000
--- a/crates/sloth_vm/Cargo.toml
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "sloth_vm"
-
-license.workspace = true
-version.workspace = true
-edition.workspace = true
-
-[dependencies]
-sloth_bytecode = { path = "../sloth_bytecode" }
-
-once_cell = "1.17.1"
-rand = "0.8.5"
diff --git a/crates/sloth_vm/src/lib.rs b/crates/sloth_vm/src/lib.rs
deleted file mode 100644
index 9cf552b..0000000
--- a/crates/sloth_vm/src/lib.rs
+++ /dev/null
@@ -1,155 +0,0 @@
-#![allow(dead_code)]
-#![warn(
- clippy::wildcard_imports,
- clippy::string_add,
- clippy::string_add_assign,
- clippy::manual_ok_or,
- unused_lifetimes
-)]
-
-pub mod native;
-pub mod sloth_std;
-pub mod value;
-pub mod vm;
-
-use std::ops::{Index, IndexMut};
-
-use value::{Object, ObjectType};
-
-use crate::value::Primitive;
-pub use crate::vm::VM;
-
-#[derive(Default)]
-pub struct Chunk {
- pub constants: Vec<Primitive>,
- pub code: Vec<u8>,
-}
-
-const STACK_SIZE: usize = 1024;
-
-#[derive(Debug)]
-pub struct Stack {
- stack: [Primitive; STACK_SIZE],
- top: usize,
-}
-
-impl Default for Stack {
- fn default() -> Self {
- Self {
- top: Default::default(),
- stack: [Primitive::Empty; STACK_SIZE],
- }
- }
-}
-
-impl Stack {
- #[inline(always)]
- pub fn push(&mut self, value: Primitive) {
- if self.top >= STACK_SIZE {
- panic!("Stack overflow");
- }
-
- self.stack[self.top] = value;
- self.top += 1;
- }
-
- #[inline(always)]
- pub fn pop(&mut self) -> Primitive {
- if self.top == 0 {
- panic!("Stack underflow");
- }
-
- self.top -= 1;
- self.stack[self.top]
- }
-
- #[inline(always)]
- pub fn pop2(&mut self) -> (Primitive, Primitive) {
- (self.pop(), self.pop())
- }
-
- #[inline(always)]
- pub fn peek(&self) -> Primitive {
- self.stack[self.top - 1]
- }
-
- #[inline(always)]
- pub fn peek_nth(&self, nth: usize) -> Primitive {
- self.stack[self.top - 1 - nth]
- }
-}
-
-impl Index<usize> for Stack {
- type Output = Primitive;
-
- fn index(&self, index: usize) -> &Self::Output {
- &self.stack[index]
- }
-}
-
-impl IndexMut<usize> for Stack {
- fn index_mut(&mut self, index: usize) -> &mut Self::Output {
- &mut self.stack[index]
- }
-}
-
-pub struct ObjectMap {
- free: usize,
- heap: Vec<Object>,
-}
-
-impl Default for ObjectMap {
- fn default() -> Self {
- Self::with_capacity(32)
- }
-}
-
-impl From<Vec<Object>> for ObjectMap {
- fn from(heap: Vec<Object>) -> Self {
- let mut free = heap.len();
- for (idx, obj) in heap.iter().enumerate() {
- if let ObjectType::Free { .. } = obj.typ {
- free = idx;
- break;
- }
- }
-
- Self { free, heap }
- }
-}
-
-impl ObjectMap {
- pub fn with_capacity(capacity: usize) -> Self {
- let mut heap = Vec::with_capacity(capacity);
- for i in 0..capacity {
- heap.push(Object::new(ObjectType::Free { next: i + 1 }));
- }
-
- Self { free: 0, heap }
- }
-
- pub fn allocate(&mut self, object: Object) -> usize {
- let current = self.free;
- if current >= self.heap.len() {
- self.heap
- .push(Object::new(ObjectType::Free { next: current + 1 }))
- }
-
- let ObjectType::Free { next } = self.heap[current].typ else {
- panic!("Allocation failed: Expected free location wasn't free");
- };
-
- self.heap[current] = object;
- self.free = next;
-
- current
- }
-
- pub fn get(&self, idx: usize) -> Option<&Object> {
- self.heap.get(idx)
- }
-
- pub fn get_mut(&mut self, idx: usize) -> Option<&mut Object> {
- self.heap.get_mut(idx)
- }
-}
diff --git a/crates/sloth_vm/src/native.rs b/crates/sloth_vm/src/native.rs
deleted file mode 100644
index fbd2626..0000000
--- a/crates/sloth_vm/src/native.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use crate::value::Primitive;
-use crate::VM;
-
-pub type NativeFunctionResult = Result<Primitive, Error>;
-pub type NativeFunctionInput = fn(&mut VM, &[Primitive]) -> NativeFunctionResult;
-
-pub enum Error {
- InvalidArgument,
- Unknown(String),
-}
-
-#[allow(clippy::type_complexity)]
-pub struct NativeFunction {
- pub name: &'static str,
- pub function: NativeFunctionInput,
- pub arity: u8,
- pub returns_value: bool,
- pub doc: Option<&'static str>,
-}
diff --git a/crates/sloth_vm/src/sloth_std/file.rs b/crates/sloth_vm/src/sloth_std/file.rs
deleted file mode 100644
index b0b476a..0000000
--- a/crates/sloth_vm/src/sloth_std/file.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use std::fs;
-
-use crate::native::{self, NativeFunction, NativeFunctionResult};
-use crate::value::{Object, ObjectType, Primitive};
-use crate::VM;
-
-fn file_read(vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult {
- let Some(Primitive::Object(ptr)) = args.get(0).cloned() else {
- return Err(native::Error::InvalidArgument);
- };
-
- let object = vm
- .objects()
- .get(ptr as usize)
- .ok_or(native::Error::InvalidArgument)?;
-
- let ObjectType::String(str) = &object.typ else {
- return Err(native::Error::InvalidArgument);
- };
-
- let contents = fs::read_to_string(str).expect("IO Error: Failed to read file!");
-
- let object = Object::new(ObjectType::String(contents));
- let ptr = vm.objects_mut().allocate(object);
-
- Ok(Primitive::Object(ptr as u32))
-}
-
-pub const FILE_READ: NativeFunction = NativeFunction {
- name: "file$read",
- function: file_read,
- arity: 1,
- returns_value: true,
- doc: Some(
- "NativeFunction file$read: \n\targs: path (str)\n\tdesc: Returns the contents of a file \
- at <path>\n\tExample: `var todo = file$read('/home/sloth/todo.txt'); # Assuming the \
- contents of todo.txt are 'Take a nap' then todo = 'Take a nap'`",
- ),
-};
-
-fn file_write(vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult {
- let Some(Primitive::Object(path_ptr)) = args.get(0).cloned() else {
- return Err(native::Error::InvalidArgument);
- };
-
- let path_object = vm
- .objects()
- .get(path_ptr as usize)
- .ok_or(native::Error::InvalidArgument)?;
-
- let ObjectType::String(path) = &path_object.typ else {
- return Err(native::Error::InvalidArgument);
- };
-
- let Some(Primitive::Object(content_ptr)) = args.get(1).cloned() else {
- return Err(native::Error::InvalidArgument);
- };
-
- let content_object = vm
- .objects()
- .get(content_ptr as usize)
- .ok_or(native::Error::InvalidArgument)?;
-
- let ObjectType::String(content) = &content_object.typ else {
- return Err(native::Error::InvalidArgument);
- };
-
- let _ = fs::write(path, content);
-
- Ok(Primitive::Empty)
-}
-
-pub const FILE_WRITE: NativeFunction = NativeFunction {
- name: "file$write",
- function: file_write,
- arity: 2,
- returns_value: false,
- doc: Some(
- "NativeFunction file$write: \n\targs: path (str), content (str)\n\tdesc: Writes <content> \
- to file at <path>\n\tExample: `file$write('/home/sloth/todo.txt', 'Take a nap'); # \
- todo.txt now contains the string 'Take a nap'`",
- ),
-};
diff --git a/crates/sloth_vm/src/sloth_std/misc.rs b/crates/sloth_vm/src/sloth_std/misc.rs
deleted file mode 100644
index ca08d1d..0000000
--- a/crates/sloth_vm/src/sloth_std/misc.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use crate::native::{self, NativeFunction, NativeFunctionResult};
-use crate::value::{Object, ObjectType, Primitive};
-use crate::VM;
-
-fn get_doc(vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult {
- let Some(Primitive::Object(ptr)) = args.get(0).cloned() else {
- return Err(native::Error::InvalidArgument);
- };
-
- let object = vm
- .objects()
- .get(ptr as usize)
- .ok_or(native::Error::InvalidArgument)?;
-
- let ObjectType::NativeFunction(fnc) = &object.typ else {
- return Err(native::Error::InvalidArgument);
- };
-
- let docs = fnc
- .doc
- .expect("Oopsie Poopsie the stringy no worky")
- .to_string();
- let object = Object::new(ObjectType::String(docs));
- let ptr = vm.objects_mut().allocate(object);
-
- Ok(Primitive::Object(ptr as u32))
-}
-
-pub const DOCS: NativeFunction = NativeFunction {
- name: "docs",
- function: get_doc,
- arity: 1,
- returns_value: true,
- doc: Some(
- "NativeFunction docs: \n\targs: name (str)\n\tdesc: Returns documentaiton on a function \
- with name <str>\n\tExample: `var doc = docs('wait'); # Returns the documentation of the \
- 'wait' function to doc`",
- ),
-};
diff --git a/crates/sloth_vm/src/sloth_std/mod.rs b/crates/sloth_vm/src/sloth_std/mod.rs
deleted file mode 100644
index ff761a6..0000000
--- a/crates/sloth_vm/src/sloth_std/mod.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-use std::collections::HashMap;
-
-use once_cell::sync::Lazy;
-
-use crate::native::NativeFunction;
-
-pub mod file;
-pub mod misc;
-pub mod rand;
-pub mod stdio;
-pub mod term;
-pub mod time;
-
-pub static NATIVE_LIBRARY: Lazy<HashMap<&'static str, NativeFunction>> = Lazy::new(|| {
- let mut map = HashMap::new();
-
- // rand
- map.insert("rand$gen", rand::GEN_FUNCTION);
- map.insert("rand$gen_range", rand::GEN_RANGE_FUNCTION);
-
- // stdio
- map.insert("write", stdio::WRITE_FUNCTION);
- map.insert("writeln", stdio::WRITELN_FUNCTION);
- map.insert("read", stdio::READ_FUNCTION);
-
- // term
- map.insert("term$clear", term::TERM_CLEAR);
- map.insert("term$setpos", term::TERM_SETPOS);
-
- // filesystem
- // TODO: Make the files commands work by making a global file variable with
- // certain permissions created by 'file.open' instead of just reading the file.
- map.insert("file$read", file::FILE_READ);
- map.insert("file$write", file::FILE_WRITE);
-
- // time
- map.insert("wait", time::WAIT);
-
- // doc
- map.insert("docs", misc::DOCS);
-
- map
-});
diff --git a/crates/sloth_vm/src/sloth_std/rand.rs b/crates/sloth_vm/src/sloth_std/rand.rs
deleted file mode 100644
index 870cca1..0000000
--- a/crates/sloth_vm/src/sloth_std/rand.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use rand::Rng;
-
-use crate::native::{self, NativeFunction, NativeFunctionResult};
-use crate::value::Primitive;
-use crate::value::Primitive::{Float, Integer};
-use crate::VM;
-
-fn gen(_vm: &mut VM, _args: &[Primitive]) -> NativeFunctionResult {
- let value = rand::thread_rng().gen_range(0.0..1.0);
-
- Ok(Float(value))
-}
-
-pub const GEN_FUNCTION: NativeFunction = NativeFunction {
- name: "rand$gen",
- function: gen,
- arity: 0,
- returns_value: true,
- doc: Some(
- "NativeFunction rand$gen:\n\tdesc: Returns a random number in the range `0.0 .. \
- 1.0`\n\tExample: `var num = rand$gen(); # num could be any number from 0.0 to 1.0`",
- ),
-};
-
-fn gen_range(_vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult {
- let min = args.get(0).cloned();
- let max = args.get(1).cloned();
-
- let (Some(Integer(min)), Some(Integer(max))) = (min, max) else {
- return Err(native::Error::InvalidArgument);
- };
-
- let value = rand::thread_rng().gen_range(min..max);
-
- Ok(Integer(value))
-}
-
-pub const GEN_RANGE_FUNCTION: NativeFunction = NativeFunction {
- name: "rand$gen_range",
- function: gen_range,
- arity: 2,
- returns_value: true,
- doc: Some(
- "NativeFunction rand$gen_range: \n\targs: min (int), max (int)\n\tdesc: Returns a random \
- numnber in the range <min> .. <max>\n\tExample: `var num = rand$gen_range(20, 76); # num \
- could be any number from 20 to 76`",
- ),
-};
diff --git a/crates/sloth_vm/src/sloth_std/stdio.rs b/crates/sloth_vm/src/sloth_std/stdio.rs
deleted file mode 100644
index f56b604..0000000
--- a/crates/sloth_vm/src/sloth_std/stdio.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-use std::io::{stdin, BufRead};
-
-use crate::native::{self, NativeFunction, NativeFunctionResult};
-use crate::value::{Object, ObjectType, Primitive};
-use crate::VM;
-
-fn write(vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult {
- let Some(Primitive::Object(ptr)) = args.get(0).cloned() else {
- return Err(native::Error::InvalidArgument);
- };
-
- let object = vm
- .objects()
- .get(ptr as usize)
- .ok_or(native::Error::InvalidArgument)?;
-
- let ObjectType::String(str) = &object.typ else {
- return Err(native::Error::InvalidArgument);
- };
-
- print!("{str}");
-
- Ok(Primitive::Empty)
-}
-
-pub const WRITE_FUNCTION: NativeFunction = NativeFunction {
- name: "write",
- function: write,
- arity: 1,
- returns_value: false,
- doc: Some(
- "NativeFunction write: \n\targs: string (str)\n\tdesc: Writes <string> to the \
- terminal.\n\tExample: `write(\"I'm sleepy...\"); # Output: I'm sleepy...`",
- ),
-};
-
-fn writeln(vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult {
- let Some(Primitive::Object(ptr)) = args.get(0).cloned() else {
- return Err(native::Error::InvalidArgument);
- };
-
- let object = vm
- .objects()
- .get(ptr as usize)
- .ok_or(native::Error::InvalidArgument)?;
-
- let ObjectType::String(str) = &object.typ else {
- return Err(native::Error::InvalidArgument);
- };
-
- println!("{str}");
-
- Ok(Primitive::Empty)
-}
-
-pub const WRITELN_FUNCTION: NativeFunction = NativeFunction {
- name: "writeln",
- function: writeln,
- arity: 1,
- returns_value: false,
- doc: Some(
- "NativeFunction writeln: \n\targs: string (str)\n\tdesc: Writes <string> to the terminal \
- and starts a new line.\n\tExample: `writeln(\"I'm sleepy...\"); # Output: I'm \
- sleepy...\n # This is a new line`",
- ),
-};
-
-fn read(vm: &mut VM, _args: &[Primitive]) -> NativeFunctionResult {
- let mut line = String::new();
- stdin()
- .lock()
- .read_line(&mut line)
- .map_err(|it| native::Error::Unknown(it.to_string()))?;
-
- let object = Object::new(ObjectType::String(line));
- let ptr = vm.objects_mut().allocate(object);
-
- Ok(Primitive::Object(ptr as u32))
-}
-
-pub const READ_FUNCTION: NativeFunction = NativeFunction {
- name: "read",
- function: read,
- arity: 0,
- returns_value: true,
- doc: Some(
- "NativeFunction read:\n\tdesc: Reads input from the terminal and returns what was \
- read.\n\tExample: `var input = read(); # Hello World <execute code> input = 'Hello \
- World'`",
- ),
-};
diff --git a/crates/sloth_vm/src/sloth_std/term.rs b/crates/sloth_vm/src/sloth_std/term.rs
deleted file mode 100644
index f61321c..0000000
--- a/crates/sloth_vm/src/sloth_std/term.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use crate::native::{self, NativeFunction, NativeFunctionResult};
-use crate::value::Primitive;
-use crate::value::Primitive::Integer;
-use crate::VM;
-
-pub const TERM_CLEAR: NativeFunction = NativeFunction {
- name: "term$clear",
- function: |_vm, _args| {
- print!("\x1b[2J\x1b[H");
- Ok(Primitive::Empty)
- },
- arity: 0,
- returns_value: false,
- doc: Some(
- "NativeFunction term$clear: \n\tdesc: Clears the terminal\n\tExample: `term$clear(); # \
- Clears the terminal`",
- ),
-};
-
-fn term_setpos(_vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult {
- let x = args.get(0).cloned();
- let y = args.get(1).cloned();
-
- let (Some(Integer(x)), Some(Integer(y))) = (x, y) else {
- return Err(native::Error::InvalidArgument);
- };
- print!("\x1b[{x};{y}H");
- Ok(Primitive::Empty)
-}
-
-pub const TERM_SETPOS: NativeFunction = NativeFunction {
- name: "term$setpos",
- function: term_setpos,
- arity: 2,
- returns_value: false,
- doc: Some(
- "NativeFunction term$setpos: \n\targs: x (int), y (int)\n\tdesc: Sets the cursors \
- position to (<x>, <y>)\n\tExample: `term$setpos(5, 17); # Sets the position of the \
- cursor to (5, 17)`",
- ),
-};
diff --git a/crates/sloth_vm/src/sloth_std/time.rs b/crates/sloth_vm/src/sloth_std/time.rs
deleted file mode 100644
index b27e0b5..0000000
--- a/crates/sloth_vm/src/sloth_std/time.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use std::{thread, time};
-
-use crate::native::{self, NativeFunction, NativeFunctionResult};
-use crate::value::Primitive;
-use crate::value::Primitive::Integer;
-use crate::VM;
-
-fn wait(_vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult {
- let sec = args.get(0).cloned();
-
- let Some(Integer(sec)) = sec else {
- return Err(native::Error::InvalidArgument);
- };
-
- thread::sleep(time::Duration::from_secs(sec.try_into().unwrap()));
-
- Ok(Primitive::Empty)
-}
-
-pub const WAIT: NativeFunction = NativeFunction {
- name: "wait",
- function: wait,
- arity: 1,
- returns_value: false,
- doc: Some(
- "NativeFunction wait: \n\targs: sec (int)\n\tdesc: Waits for <sec> seconds.\n\tExample: \
- `wait(10); # Waits 10 seconds`",
- ),
-};
diff --git a/crates/sloth_vm/src/value.rs b/crates/sloth_vm/src/value.rs
deleted file mode 100644
index 4450b5a..0000000
--- a/crates/sloth_vm/src/value.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use crate::native::NativeFunction;
-use crate::Chunk;
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum Primitive {
- Integer(i128),
- Float(f64),
- Bool(bool),
- /// Pointer to an object living on heap
- Object(u32),
- Empty,
-}
-
-pub struct Object {
- /// If the object has been marked by the VM or not
- pub(crate) marked: bool,
- pub(crate) typ: ObjectType,
-}
-
-impl Object {
- pub fn new(typ: ObjectType) -> Self {
- Self { marked: false, typ }
- }
-}
-
-pub enum ObjectType {
- Box(Primitive),
- String(String),
- List(Vec<Primitive>),
-
- Function(Function),
- NativeFunction(NativeFunction),
-
- Free { next: usize },
-}
-
-pub struct Function {
- pub name: Option<String>,
- pub chunk: Chunk,
- pub arity: u8,
- pub returns_value: bool,
-}
-
-impl Function {
- pub fn root(chunk: Chunk) -> Self {
- Self {
- name: None,
- chunk,
- arity: 0,
- returns_value: false,
- }
- }
-}
diff --git a/crates/sloth_vm/src/vm.rs b/crates/sloth_vm/src/vm.rs
deleted file mode 100644
index 3600719..0000000
--- a/crates/sloth_vm/src/vm.rs
+++ /dev/null
@@ -1,610 +0,0 @@
-use std::mem::MaybeUninit;
-
-use sloth_bytecode::Opcode;
-
-use crate::value::{Function, Object, ObjectType, Primitive};
-use crate::{native, vm, ObjectMap, Stack};
-
-#[derive(Clone, Copy)]
-pub struct CallFrame {
- pointer: usize,
- stack_offset: usize,
- function: *const Function, // TODO: Safety
-}
-
-impl CallFrame {
- fn new(stack_offset: usize, function: &Function) -> Self {
- Self {
- pointer: 0,
- stack_offset,
- function: function as *const _,
- }
- }
-
- #[inline]
- fn function(&self) -> &Function {
- unsafe { &*self.function }
- }
-}
-
-const CALL_STACK_SIZE: usize = 1024;
-
-pub struct CallStack {
- top: usize,
- frames: [MaybeUninit<CallFrame>; CALL_STACK_SIZE],
-}
-
-impl Default for CallStack {
- fn default() -> Self {
- Self {
- top: 0,
- frames: [MaybeUninit::uninit(); CALL_STACK_SIZE],
- }
- }
-}
-
-impl CallStack {
- fn push(&mut self, frame: CallFrame) {
- self.frames[self.top].write(frame);
- self.top += 1;
- }
-
- fn pop(&mut self) {
- self.top -= 1;
- }
-
- fn peek(&self) -> &CallFrame {
- unsafe { self.frames[self.top - 1].assume_init_ref() }
- }
-
- fn peek_mut(&mut self) -> &mut CallFrame {
- unsafe { self.frames[self.top - 1].assume_init_mut() }
- }
-}
-
-// TODO: Fix visibility
-pub struct VM {
- pub stack: Stack,
- call_stack: CallStack,
- pub objects: ObjectMap,
-}
-
-impl Default for VM {
- fn default() -> Self {
- Self::init(ObjectMap::default())
- }
-}
-
-impl VM {
- pub fn init(objects: ObjectMap) -> Self {
- Self {
- stack: Stack::default(),
- call_stack: CallStack::default(),
- objects,
- }
- }
-
- pub fn new(objects: ObjectMap, mut root: Function) -> Self {
- let mut this = Self::init(objects);
-
- // Allocating the root function
- root.chunk.code.push(Opcode::Halt as u8);
- this.call_stack.push(CallFrame::new(0, &root));
- this.objects
- .allocate(Object::new(ObjectType::Function(root)));
-
- this
- }
-
- pub fn step(&mut self) -> bool {
- use Primitive::*;
-
- let opcode = self.read_u8();
-
- match Opcode::from_u8(opcode) {
- Opcode::Constant => {
- let idx = self.read_u16() as usize;
- let value = self.call_stack.peek().function().chunk.constants[idx];
-
- self.stack.push(value);
- }
-
- Opcode::Dup => {
- let value = self.stack.pop();
- self.stack.push(value);
- self.stack.push(value);
- }
- Opcode::Pop => {
- self.stack.pop();
- }
- Opcode::GetLocal => {
- let idx = self.read_u16() as usize;
- let value = self.stack[self.call_stack.peek().stack_offset + idx];
-
- self.stack.push(value);
- }
- Opcode::SetLocal => {
- let idx = self.read_u16() as usize;
- let value = self.stack.pop();
-
- self.stack[self.call_stack.peek().stack_offset + idx] = value;
- }
- Opcode::Box => {
- // FIXME: TODO: MEGA CURSED
- let pos = self.read_u16() as usize;
- let value = self.stack.pop();
-
- let object = vm::Object::new(ObjectType::Box(value));
-
- self.objects.heap[pos] = object;
- self.stack.push(Object(pos as u32));
- }
-
- Opcode::Add => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Integer(lhs + rhs),
- (Float(lhs), Float(rhs)) => Float(lhs + rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
- Opcode::Sub => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Integer(lhs - rhs),
- (Float(lhs), Float(rhs)) => Float(lhs - rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
- Opcode::Mul => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Integer(lhs * rhs),
- (Float(lhs), Float(rhs)) => Float(lhs * rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
- Opcode::Div => {
- let value = match self.stack.pop2() {
- (Integer(_), Integer(0)) => panic!("Divide by 0"),
- (Integer(lhs), Integer(rhs)) => Integer(lhs / rhs),
- (Float(lhs), Float(rhs)) => Float(lhs / rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
- Opcode::Mod => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Integer(lhs % rhs),
- (Float(lhs), Float(rhs)) => Float(lhs % rhs),
- _ => panic!(),
- };
-
- self.stack.push(value);
- }
-
- Opcode::Eq => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Bool(lhs == rhs),
- (Float(lhs), Float(rhs)) => Bool(lhs == rhs),
- (Bool(lhs), Bool(rhs)) => Bool(lhs == rhs),
- (Object(lhs), Object(rhs)) => Bool(lhs == rhs),
- (Empty, Empty) => Bool(true),
- _ => Bool(false),
- };
-
- self.stack.push(value);
- }
- Opcode::Ne => {
- let value = match self.stack.pop2() {
- (Integer(lhs), Integer(rhs)) => Bool(lhs != rhs),
- (Float(lhs), Float(rhs)) => Bool(lhs != rhs),
- (Bool(lhs), Bool(rhs)) => Bool(lhs != rhs),
- (Object(lhs), Object(rhs)) => Bool(lhs != rhs),
- (Empty, Empty) => Bool(false),
- _ => Bool(false),
- };
-
- self.stack.push(value);
- }
-
- Opcode::Jump => {
- let to = self.read_u16();
- self.call_stack.peek_mut().pointer = to as usize;
- }
- Opcode::JumpIf => {
- let to = self.read_u16();
- let value = self.stack.pop();
-
- if let Bool(true) = value {
- self.call_stack.peek_mut().pointer = to as usize;
- }
- }
-
- Opcode::Call => {
- let Primitive::Object(ptr) = self.stack.pop() else {
- panic!("Last element on stack was not an object");
- };
-
- self.call(ptr as usize);
- }
-
- Opcode::Return => {
- self.call_return();
- }
-
- Opcode::Halt => return false,
-
- opcode => unimplemented!("Opcode {:?} unimplemented", opcode),
- }
-
- true
- }
-
- pub fn run(&mut self) {
- while self.step() {}
- }
-
- pub fn call(&mut self, ptr: usize) {
- let Some(obj) = self.objects.get(ptr) else {
- panic!("Pointer referenced nothing");
- };
-
- match &obj.typ {
- ObjectType::Function(function) => {
- // Add a callstack entry for the function
- let offset = self.stack.top - (function.arity as usize);
- self.call_stack.push(CallFrame::new(offset, function));
- }
- ObjectType::NativeFunction(function) => {
- let mut args = Vec::with_capacity(function.arity as usize);
- for _ in 0..function.arity {
- args.push(self.stack.pop());
- }
-
- let name = function.name;
- let returns_value = function.returns_value;
-
- let internal = function.function;
- let result = internal(self, &args);
-
- match result {
- Ok(value) => {
- if returns_value {
- self.stack.push(value);
- }
- }
- Err(error) => match error {
- native::Error::InvalidArgument => {
- panic!("Invalid argument provided to '{name}'");
- }
- native::Error::Unknown(msg) => {
- panic!("Native function '{name}' failed due to '{msg}'");
- }
- },
- }
- }
- _ => panic!("Object was not a function"),
- }
- }
-
- fn call_return(&mut self) {
- let function = self.call_stack.peek().function();
- let stack_offset = self.call_stack.peek().stack_offset;
-
- let return_value = if function.returns_value {
- Some(self.stack.pop())
- } else {
- None
- };
-
- self.stack.top = stack_offset;
-
- if let Some(return_value) = return_value {
- self.stack.push(return_value);
- }
-
- self.call_stack.pop();
- }
-
- fn unwind(&mut self) {
- unimplemented!("Implement unwinding for error handling");
- }
-
- #[inline(always)]
- fn read_u8(&mut self) -> u8 {
- let frame = self.call_stack.peek_mut();
- let function = frame.function();
- let byte = function.chunk.code[frame.pointer];
- frame.pointer += 1;
- byte
- }
-
- #[inline(always)]
- fn read_u16(&mut self) -> u16 {
- let frame = self.call_stack.peek_mut();
- let chunk = &frame.function().chunk;
-
- let bytes = (chunk.code[frame.pointer], chunk.code[frame.pointer + 1]);
-
- frame.pointer += 2;
-
- ((bytes.0 as u16) << 8) + (bytes.1 as u16)
- }
-
- #[inline(always)]
- pub fn objects(&self) -> &ObjectMap {
- &self.objects
- }
-
- #[inline(always)]
- pub fn objects_mut(&mut self) -> &mut ObjectMap {
- &mut self.objects
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::value::{Function, Object, ObjectType, Primitive};
- use crate::{sloth_std, Chunk, ObjectMap, VM};
-
- #[test]
- fn arithmetic_ops() {
- // Addition
- let mut vm = VM::new(
- ObjectMap::default(),
- Function::root(Chunk {
- constants: vec![Primitive::Integer(7)],
- code: vec![
- 0x00, 0, 0, // Load constant from 0
- 0x10, // Duplicate
- 0x20, // Add
- 0xE0,
- ],
- }),
- );
-
- vm.run();
- assert_eq!(vm.stack.peek(), Primitive::Integer(14));
-
- let mut vm = VM::new(
- ObjectMap::default(),
- Function::root(Chunk {
- constants: vec![Primitive::Integer(2), Primitive::Integer(11)],
- code: vec![
- 0x00, 0, 0, // Load constant from 0
- 0x00, 0, 1, // Load constant from 1
- 0x20, // Add
- 0xE0,
- ],
- }),
- );
-
- vm.run();
- assert_eq!(vm.stack.peek(), Primitive::Integer(13));
- }
-
- #[test]
- fn basic_function() {
- let mut vm = VM::new(
- ObjectMap::from(vec![Object::new(ObjectType::Function(Function {
- name: Some("add".to_string()),
- chunk: Chunk {
- constants: vec![],
- code: vec![0x14, 0, 0, 0x14, 0, 1, 0x20, 0x52],
- },
- arity: 2,
- returns_value: true,
- }))]),
- Function::root(Chunk {
- constants: vec![
- Primitive::Integer(6),
- Primitive::Integer(3),
- Primitive::Object(0),
- Primitive::Object(1),
- Primitive::Object(2),
- ],
- code: vec![
- 0x00, 0, 0, // Load first function parameter from 0
- 0x00, 0, 1, // Load second function parameter from 1
- 0x00, 0, 2, // Load function constant from 2
- 0x50, // Call function
- ],
- }),
- );
-
- vm.run();
-
- assert_eq!(vm.stack.peek(), Primitive::Integer(9));
- }
-
- #[test]
- fn native_function() {
- let mut vm = VM::new(
- ObjectMap::from(vec![
- Object::new(ObjectType::NativeFunction(sloth_std::rand::GEN_FUNCTION)),
- Object::new(ObjectType::NativeFunction(
- sloth_std::rand::GEN_RANGE_FUNCTION,
- )),
- ]),
- Function::root(Chunk {
- constants: vec![
- Primitive::Object(0),
- Primitive::Object(1),
- Primitive::Integer(5),
- Primitive::Integer(10),
- ],
- code: vec![
- // First part
- 0x00, 0, 0, //
- 0x50, //
- 0xE0, //
- // Second part
- 0x00, 0, 3, //
- 0x00, 0, 2, //
- 0x00, 0, 1, //
- 0x50, //
- ],
- }),
- );
-
- vm.run();
-
- assert!({
- let Primitive::Float(i) = vm.stack.peek() else { panic!(); };
- (0.0..=1.0).contains(&i)
- });
-
- vm.run();
-
- assert!({
- let Primitive::Integer(i) = vm.stack.peek() else { panic!(); };
- (5..10).contains(&i)
- });
- }
-
- #[test]
- fn fibonacci() {
- #[rustfmt::skip]
- let mut vm = VM::new(
- ObjectMap::default(),
- Function::root(Chunk {
- constants: vec![
- Primitive::Integer(0),
- Primitive::Integer(1),
- Primitive::Integer(10),
- ],
- code: vec![
- // Load variables
- 0x00, 0, 0, // 0 Index
- 0x00, 0, 0, // 3 Me
- 0x00, 0, 0, // 6 Parent
- 0x00, 0, 1, // 9 Grandparent
-
- // Load parent and grandparent, sum them and put the value in me
- 0x14, 0, 2, // 12
- 0x14, 0, 3, // 15
- 0x20, // 16
- 0x15, 0, 1, // 19
-
- // Set grandparent to parent
- 0x14, 0, 2, // 22
- 0x15, 0, 3, // 25
-
- // Set parent to me
- 0x14, 0, 1, // 28
- 0x15, 0, 2, // 31
-
- // Increment Index by 1
- 0x00, 0, 1, // 34
- 0x14, 0, 0, // 37 Index
- 0x20, // 40
- 0x15, 0, 0, // 41 Index
-
- // Load me
- 0x14, 0, 1, // 44
- 0xE0, // 47
- 0x11, // 48
-
- // Repeat until Index is 9
- 0x00, 0, 2, // 49
- 0x14, 0, 0, // 52 Index
- 0x31, // 55
- 0x41, 0, 12, // 56
- ],
- }),
- );
-
- let mut values = Vec::new();
- for _ in 0..10 {
- vm.run();
- values.push(vm.stack.peek());
- }
-
- assert_eq!(&values, &[
- Primitive::Integer(1),
- Primitive::Integer(1),
- Primitive::Integer(2),
- Primitive::Integer(3),
- Primitive::Integer(5),
- Primitive::Integer(8),
- Primitive::Integer(13),
- Primitive::Integer(21),
- Primitive::Integer(34),
- Primitive::Integer(55),
- ]);
- }
-
- #[test]
- fn fibonacci_recursive() {
- #[rustfmt::skip]
- let mut vm = VM::new(
- ObjectMap::from(vec![Object::new(ObjectType::Function(Function {
- name: Some("fib".to_owned()),
- chunk: Chunk {
- constants: vec![
- Primitive::Object(0),
- Primitive::Integer(0),
- Primitive::Integer(1),
- Primitive::Integer(2),
- ],
- code: vec![
- 0x14, 0, 0, // 0
- 0x00, 0, 1, // 3
- 0x31, // 6
- 0x41, 0, 14, // 7
- 0x00, 0, 1, // 10
- 0x52, // 13
-
- 0x14, 0, 0, // 14
- 0x00, 0, 2, // 17
- 0x31, // 20
- 0x41, 0, 28, // 21
- 0x00, 0, 2, // 24
- 0x52, // 27
-
- // fib(n - 1)
- 0x00, 0, 2, // 28
- 0x14, 0, 0, // 31
- 0x21, // 34
- 0x00, 0, 0, // 35
- 0x50, // 38
-
- // fib(n - 2)
- 0x00, 0, 3, // 39
- 0x14, 0, 0, // 42
- 0x21, // 45
- 0x00, 0, 0, // 46
- 0x50, // 49
-
- // add & return
- 0x20, // 50
- 0x52, // 51
- ],
- },
- arity: 1,
- returns_value: true,
- }))]),
- Function::root(Chunk {
- constants: vec![
- Primitive::Object(0),
- Primitive::Integer(10),
- ],
- code: vec![
- // Load n and the function and call it
- 0x00, 0, 1, // 0
- 0x00, 0, 0, // 3
- 0x50, // 6
- ],
- }),
- );
-
- vm.run();
-
- assert_eq!(Primitive::Integer(55), vm.stack.peek());
- }
-}