1
1
Fork 0
mirror of https://github.com/azur1s/bobbylisp.git synced 2024-10-16 02:37:40 -05:00

refactor: rewrote lol

This commit is contained in:
Natapat Samutpong 2022-02-10 03:08:54 +07:00
parent 841f007b44
commit 252a8aae33
31 changed files with 346 additions and 1880 deletions

253
Cargo.lock generated
View file

@ -3,256 +3,5 @@
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0005d08a8f7b65fb8073cb697aa0b12b631ed251ce73d862ce50eeb52ce3b50"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vyc"
name = "vy"
version = "0.1.0"
dependencies = [
"regex",
"structopt",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View file

@ -1,4 +1,8 @@
[workspace]
members = [
"vyc",
]
[package]
name = "vy"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -1,12 +0,0 @@
all: build
debug: build-debug
build:
cd ./vyc; cargo build --release
rm ~/bin/vyc -f
mv ./target/release/vyc ~/bin/vyc
build-debug:
cd ./vyc; cargo build
rm ~/bin/vyc -f
mv ./target/debug/vyc ~/bin/vyc

View file

@ -1,47 +0,0 @@
# vy
another lisp dialect
## Installation
```console
$ bash <(curl -s https://raw.githubusercontent.com/azur1s/vy/master/install.sh)
```
The binary will be installed in `~/bin/vyc` run it with:
```console
$ vyc help
```
### Example
```console
$ vyc compile ./example/hello.vy
$ vyc run ./hello.bsm
Hello, World!
```
## Progress:
DONE:
- Parsing, Compiling, Running(VM)
- Intrinsic:
- Function definition: `fun` (no arguments yet)
- Variable definition: `def`
- Do blocks: `do`
- User input: `read`
- Printing: `print`
- Condition: `if`
- Loops: `while`
- Erroring: `throw`
- Math:
- `+` , `add`
- `-` , `sub`
- `*` , `mul`
- `/` , `div`
- Comparison:
- `=` , `equal`
- `!` , `not`
TODO:
- Prove turing complete
- Do the intrinsic left
- Quote, Quasiquote, etc.
- Linter, for stuff like undefined variables, etc.
- Optimizing
- Remove unnecessary copying in the entire codebase (also with `.unwrap()`)

View file

@ -1,3 +0,0 @@
(fun main (do
(def str "Hello, world!")
(print (str))))

View file

@ -1,4 +0,0 @@
(fun print_true (print "True"))
(fun print_false (print "False"))
(fun main
(if true (print_true) (print_false)))

View file

@ -1,4 +0,0 @@
(fun main (do
(def in (read))
(print "Your input was: ")
(print in)))

View file

@ -1,3 +0,0 @@
(fun main (do
(def file (slurp "quine.blsp"))
(print file)))

View file

@ -1,6 +0,0 @@
(fun main (do
(print (quote (a b c)))
(print (quote (+ 1 (+ 2 "3"))))
(print (quote a))
(print (quote "a"))
))

View file

@ -1,2 +0,0 @@
(fun main
(throw "error :("))

View file

@ -1,5 +0,0 @@
; https://esolangs.org/wiki/Truth-machine
(fun main (do
(def x (read))
(while (equal x 1) (print x))
(print "Done!")))

View file

@ -1,7 +0,0 @@
(fun return_true true)
(fun main (do
(def name "John")
(if (return_true)
(print name)
(print "no"))))

View file

@ -1,154 +0,0 @@
#!/bin/bash
# --- Initialization ---
set +x
path=$(pwd)
tput smcup # Switch to alternate screen so we preserve the terminal history
tput civis
trap clean_up_fail INT
clean_up() {
tput rmcup
tput cnorm
echo "${1:-Goodbye! o/}"
exit 0
}
clean_up_fail() {
tput rmcup
tput cnorm
echo "${1:-Installation failed! :(}"
exit 1
}
# --- Displaying ---
print_menu() {
local function_arguments=($@)
local selected_item="$1"
local menu_items=(${function_arguments[@]:1})
local menu_size="${#menu_items[@]}"
for (( i = 0; i < $menu_size; ++i )) do
if [ "$i" = "$selected_item" ]
then echo -e "\033[2K\e[1m>\e[0m \e[1;33m${menu_items[i]}\e[0m"
else echo -e "\033[2K ${menu_items[i]}"
fi
done
}
run_menu() {
local function_arguments=($@)
local selected_item="$1"
local menu_items=(${function_arguments[@]:1})
local menu_size="${#menu_items[@]}"
local menu_limit=$((menu_size - 1))
clear
print_menu "$selected_item" "${menu_items[@]}"
while read -rsn1 input
do
case "$input" in
$'\x1B')
read -rsn1 -t 0.1 input
if [ "$input" = "[" ]
then
read -rsn1 -t 0.1 input
case "$input"
in
A) # Arrow up
if [ "$selected_item" -ge 1 ]
then
selected_item=$((selected_item - 1))
clear
print_menu "$selected_item" "${menu_items[@]}"
fi;;
B) # Arrow down
if [ "$selected_item" -lt "$menu_limit" ]
then
selected_item=$((selected_item + 1))
clear
print_menu "$selected_item" "${menu_items[@]}"
fi;;
esac
fi
# stdin flush
read -rsn5 -t 0.1;;
"") # Enter
return "$selected_item";;
esac
done
}
# --- Installation ---
check_installed() {
if ! [ -x "$(command -v $1)" ]
then clean_up_fail "Please check that you have $1 installed!"
fi
}
install() {
local selected_install_item=0
local install_opts=("Download" "Compile" "Compile(Debug)" "Exit")
run_menu "$selected_install_item" "${install_opts[@]}"
local install_chosen="$?"
case "$install_chosen" in
0) echo "There is no release yet, please hold tight!";;
1)
echo "Testing dependencies..."
check_installed git
check_installed cargo
echo "Setting up folders..."
mkdir -p ~/.cache/
rm -rf ~/.cache/vy/
echo "Cloning repository..."
cd ~/.cache/
git clone https://github.com/azur1s/vy
cd ~/.cache/vy/
echo "Compiling..."
cargo build --release
mv ~/.cache/vy/target/release/vyc ~/bin/vyc
clean_up "Done! Thanks a lot for trying out vy!";;
2)
echo "Testing dependencies..."
check_installed git
check_installed cargo
echo "Setting up folders..."
mkdir -p ~/.cache/
rm -rf ~/.cache/vy/
echo "Cloning repository..."
cd ~/.cache/
git clone https://github.com/azur1s/vy
cd ~/.cache/vy/
echo "Compiling..."
cargo build
mv ~/.cache/vy/target/debug/vyc ~/bin/vyc
clean_up "Done! Thanks a lot for trying out vy!";;
3) clean_up;;
esac
}
uninstall() {
echo "Uninstalling vyc..."
rm ~/bin/vyc -f
rm /usr/bin/vyc -f
sleep 1s
clean_up "Sad to see you go! Goodbye! o/"
}
selected_item=0
menu_opts=("Install" "Uninstall" "Exit")
run_menu "$selected_item" "${menu_opts[@]}"
menu_chosen="$?"
case "$menu_chosen" in
0) install;;
1) uninstall;;
2) clean_up;;
esac

13
src/main.rs Normal file
View file

@ -0,0 +1,13 @@
pub mod parser;
fn main() {
let src = r#"
(print "Hello, World!")
(print '(hello, world!))
"#;
let parsed = parser::parse(src);
for result in parsed {
println!("{:?}", result);
}
}

323
src/parser.rs Normal file
View file

@ -0,0 +1,323 @@
// This implementation of parser is heavily inspired by
// brundonsmith/rust_lisp (https://github.com/brundonsmith/rust_lisp)
// go check them out!
use std::{ rc::Rc, cell::RefCell };
/// A List in Sexpr
#[derive(Debug, Clone)]
pub struct List {
pub value: Option<Rc<RefCell<Cons>>>,
}
/// Cons -> (car, [cdr])
#[derive(Debug, Clone)]
pub struct Cons {
pub car: Value,
pub cdr: Option<Rc<RefCell<Cons>>>,
}
#[derive(Debug, Clone)]
pub enum Value {
// Boolean types
True, False,
// Numbers
Int(i64), Float(f64),
String(String), Symbol(String),
List(List),
Nil,
}
#[derive(Debug, Clone)]
pub enum Tree {
Atom { atom: Value, quote: bool },
List { vec: Vec<Tree>, quote: bool },
}
impl Tree {
fn into_expr(self) -> Value {
match self {
Tree::Atom { atom, quote } => {
if quote {
Value::List(List {
value: Some(Rc::new(RefCell::new(Cons {
car: Value::Symbol(String::from("quote")),
cdr: Some(Rc::new(RefCell::new(Cons {
car: atom, // car vroom vroom 🚗
cdr: None,
}))),
}))),
})
} else {
atom
}
},
Tree::List { vec, quote } => {
let list = Value::List(
vec.into_iter()
.map(|tree| tree.into_expr())
.collect::<List>(),
);
if quote {
Value::List(List {
value: Some(Rc::new(RefCell::new(Cons {
car: Value::Symbol(String::from("quote")),
cdr: Some(Rc::new(RefCell::new(Cons {
car: list, // car vroom vroom 🚗
cdr: None,
}))),
}))),
})
} else {
list
}
}
}
}
}
impl FromIterator<Value> for List {
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
let mut list = List { value: None };
let mut tail: Option<Rc<RefCell<Cons>>> = None;
for value in iter {
let new_cons = Rc::new(RefCell::new(Cons {
car: value,
cdr: None,
}));
if list.value.is_none() {
list.value = Some(new_cons.clone());
} else if let Some(ref tail_cons) = tail {
tail_cons.as_ref().borrow_mut().cdr = Some(new_cons.clone());
}
tail = Some(new_cons);
}
list
}
}
// --- Start tokenizer ---
const SPECIAL_CHARS: [&str; 4] = ["(", ")", "'", "..."];
/// Match the characters from `with` with the characters from `from`
/// Example: match_front("123", "12") -> true
fn match_front(from: &str, with: &str) -> bool { with.chars().zip(from.chars()).all(|(a, b)| a == b) }
/// Get length from `from` until `predicate`
/// Example: match_pred("abcdef", |c| c != 'f') -> Some(5)
fn match_predicate<F: Fn(char) -> bool>(from: &str, predicate: F) -> Option<usize> {
from.char_indices().take_while(|(_, c)| predicate(*c)).last().map(|(i, _)| i)
}
/// Check if characters is in a special characters list or not
fn is_symbolic(char: char) -> bool {
!char.is_whitespace() && !SPECIAL_CHARS.iter().any(|t| t.chars().any(|other| other == char))
}
/// Return type: (token, (start, end))
pub fn tokenize(src: &str) -> impl Iterator<Item = (&str, (usize, usize))> {
let mut skip: Option<usize> = None;
src.char_indices().filter_map(move |(i, char)| {
if skip.map(|dest| dest > i).unwrap_or(false) { return None; }
else { skip = None; }
// Whitespaces
if char.is_whitespace() { return None; }
// Special characters
for special in &SPECIAL_CHARS {
if match_front(&src[i..], special) {
skip = Some(i + special.len());
return Some((*special, (i, i + special.len())));
}
}
// Strings
if char == '"' {
let match_end = match_predicate(&src[i + 1..], |c| c != '"');
if let Some(end) = match_end {
let string_end = i + end + 3;
skip = Some(string_end);
return Some((&src[i..string_end], (i, string_end)));
}
}
// Comments
// Check if the current char is a semicolon and
if char == ';' && src[i + 1..].chars().next().map_or(false, |c| c == ';') {
// Get length until end of line
let end = i + 2 + match_predicate(&src[i + 2..], |c| c!= '\n').unwrap_or(0);
skip = Some(end + 1);
return None;
}
// Numbers
if char.is_numeric() {
let front = i + match_predicate(&src[i..], |c| c.is_numeric()).unwrap() + 1;
// Check if its a float (by checking if its contain a dot)
if front < src.len() - 1 && &src[front..front + 1] == "." {
let back = front + match_predicate(&src[front + 1..], |c| c.is_numeric()).unwrap() + 2;
skip = Some(back);
return Some((&src[i..back], (i, back)));
} else {
skip = Some(front);
return Some((&src[i..front], (i, front)));
}
}
// Symbols
if !char.is_numeric() && is_symbolic(char) {
let end = match_predicate(&src[i..], is_symbolic);
if let Some(last) = end {
let symbol_end = i + last + 1;
skip = Some(symbol_end);
return Some((&src[i..symbol_end], (i, symbol_end)));
}
}
None
})
}
// --- End tokenizer & Start parser ---
#[derive(Debug)]
pub enum ParseErrorKind {
UnexpectedParenOpen,
UnexpectedParenClose,
}
#[derive(Debug)]
pub struct ParseError {
pub kind: ParseErrorKind,
pub pos: (usize, usize),
}
impl ParseError {
fn new(kind: ParseErrorKind, pos: (usize, usize)) -> Self {
ParseError { kind, pos }
}
}
fn read<'a>(
tokens: impl Iterator<Item = (&'a str, (usize, usize))> + 'a
) -> impl Iterator<Item = Result<(Value, (usize, usize)), ParseError>> + 'a {
let mut stack: Vec<Tree> = Vec::new();
let mut parenths = 0;
let mut quote_next = false;
let mut block_start = 0;
tokens.filter_map(move |(token, (start, end))| {
match token {
"(" => {
parenths += 1;
block_start = start;
stack.push(Tree::List {
vec: Vec::new(),
quote: quote_next,
});
quote_next = false;
None
},
")" => {
parenths -= 1;
if stack.is_empty() {
Some(Err(ParseError::new(
ParseErrorKind::UnexpectedParenClose,
(start, end)
)))
} else {
let mut finished = stack.pop().unwrap();
if parenths == 0 {
stack = Vec::new();
let r = Some(Ok((finished.into_expr(), (block_start, end))));
block_start = 0;
r
} else {
let destination = stack.last_mut().unwrap();
if let Tree::List { vec, quote } = &finished {
if vec.is_empty() {
finished = Tree::Atom {
atom: Value::Nil,
quote: *quote,
};
}
}
if let Tree::List { vec, quote: _ } = destination { vec.push(finished); }
None
}
}
},
"'" => { quote_next = true; None },
_ => {
let expr = Tree::Atom {
atom: read_atom(token),
quote: quote_next,
};
quote_next = false;
if let Some(last) = stack.last_mut() {
if let Tree::List { vec, quote: _ } = last {
vec.push(expr);
}
None
} else {
Some(Ok((expr.into_expr(), (start, end))))
}
}
}
})
}
fn read_atom(token: &str) -> Value {
let lower = token.to_lowercase();
match lower.as_str() {
"true" => Value::True,
"false" => Value::False,
"nil" => Value::Nil,
_ => {
// Parse number
if let Ok(int) = token.parse::<i64>() { Value::Int(int) }
// Parse float
else if let Ok(float) = token.parse::<f64>() { Value::Float(float) }
// Parse string
else if token.chars().next().map_or(false, |c| c == '"') && token.chars().nth_back(0).map_or(false, |c| c == '"') {
Value::String(String::from(&token[1..token.chars().count() - 1]))
} else {
Value::Symbol(String::from(token))
}
}
}
}
// --- End parser ---
pub fn parse(src: &str) -> impl Iterator<Item = Result<(Value, (usize, usize)), ParseError>> + '_ {
read(tokenize(src))
}

22
test.sh
View file

@ -1,22 +0,0 @@
in=$1 # Get first's file name
noext=${in%.*} # Remove extension
name=${noext##*/} # Remove path
echo $2
# if $2 equal to "noecho"
if [ $2 = "noecho" ];
then
make debug; echo ""; vyc compile $noext.vy; echo ""
cat $noext.vy; echo -e "\n"; cat $name.vyir; echo ""
vyc run $name.vyir
else
make debug
vyc compile $noext.vy
echo -e "------------------------------------------- SOURCE"
cat $noext.vy
echo -e "\n----------------------------------------- COMPILED"
cat $name.vyir
echo -e "------------------------------------------- OUTPUT"
blspc run $name.vyir
echo -e "--------------------------------------------------"
fi

2
vyc/.gitignore vendored
View file

@ -1,2 +0,0 @@
/target
/proto

258
vyc/Cargo.lock generated
View file

@ -1,258 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blspc"
version = "0.1.0"
dependencies = [
"regex",
"structopt",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View file

@ -1,15 +0,0 @@
[package]
name = "vyc"
description = "vy language compiler."
version = "0.1.0"
edition = "2021"
homepage = "https://github.com/azur1s/vy"
repository = "https://github.com/azur1s/vy"
license = "AGPL-3.0"
readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
regex = "1.5.4"
structopt = "0.3.26"

View file

@ -1,41 +0,0 @@
use std::path::PathBuf;
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
#[structopt(name = "vyc")]
pub struct Opts {
#[structopt(subcommand)]
pub commands: Args,
}
#[derive(StructOpt, Debug)]
#[structopt(name = "args")]
pub enum Args {
#[structopt(name = "compile")]
Compile (CompileOpts),
#[structopt(name = "run")]
Run (RunOpts),
}
#[derive(StructOpt, Debug)]
#[structopt(name = "compile", about = "Compile Options")]
pub struct CompileOpts {
#[structopt(name = "FILE", parse(from_os_str))]
pub file: PathBuf,
#[structopt(name = "OUTPUT", parse(from_os_str))]
pub output: Option<PathBuf>,
#[structopt(short, long)]
pub debug: bool,
#[structopt(short = "b", long)]
pub with_comment: bool,
}
#[derive(StructOpt, Debug)]
#[structopt(name = "run", about = "Run Options")]
pub struct RunOpts {
#[structopt(name = "FILE", parse(from_os_str))]
pub file: PathBuf,
#[structopt(short, long)]
pub debug: bool,
}

View file

@ -1,249 +0,0 @@
use crate::{vm::{instr::*, types::Type}, compiler::parser::Sexpr::{self, *}};
pub struct Compiler {
// Compiled instructions
pub instructions: Vec<Instr>,
// Compiled variables's register address
pub variables: Vec<(String, Register)>,
// Current register index
pub register_pointer: usize,
}
impl Compiler {
pub fn new() -> Compiler {
Compiler {
instructions: Vec::new(),
variables: Vec::new(),
register_pointer: 1,
}
}
fn next_register(&mut self) -> Register {
let r = Register { value: self.register_pointer };
self.register_pointer += 1;
r
}
pub fn compile(&mut self, src: Sexpr) -> Result<Vec<Instr>, String> {
let mut result = Vec::new();
'tco: loop {
match src {
Cons(car, cdr) => {
match *car {
Symbol(ref call) => {
match call.as_str() {
"do" => {
for c in cdr {
result.append(&mut self.compile(c)?);
}
},
"fun" => {
let function_name = match &cdr[0] {
Symbol(ref name) => format!("function_{}", name.clone()),
_ => return Err(format!("Expected function name, got {}", cdr[0])),
};
let body = &cdr[1];
result.push(Instr::Label { name: function_name });
result.append(&mut self.compile(body.clone())?);
result.push(Instr::Return);
},
"if" => {
let mut cond = self.compile(cdr[0].clone())?;
result.append(&mut cond);
let mut then = self.compile(cdr[1].clone())?;
let mut else_ = self.compile(cdr[2].clone())?;
result.push(Instr::JumpIfFalse { to: len(&then) as isize + 1 }); // +1 for the jump instr
result.append(&mut then);
result.push(Instr::Jump { to: len(&else_) as isize });
result.append(&mut else_);
},
"def" => {
let var_name = match &cdr[0] {
Symbol(ref name) => name.clone(),
_ => return Err(format!("Expected variable name, got {}", cdr[0])),
};
if let Some(v) = self.variables.iter().find(|v| v.0 == var_name) {
let r = v.1;
self.variables.retain(|v| v.0 != var_name);
self.variables.push((var_name, r));
result.append(&mut self.compile(cdr[1].clone())?);
result.push(Instr::Store { address: r });
} else {
let var_pointer = self.next_register();
self.variables.push((var_name, var_pointer));
result.append(&mut self.compile(cdr[1].clone())?);
result.push(Instr::Store { address: var_pointer });
}
},
"while" => {
let mut cond = self.compile(cdr[0].clone())?;
let mut body = self.compile(cdr[1].clone())?;
let jump_length = (len(&body) as isize) + (len(&cond) as isize);
result.append(&mut cond.clone());
result.push(Instr::JumpIfFalse { to: jump_length + 1 });
result.append(&mut body);
result.append(&mut cond);
result.push(Instr::Not);
result.push(Instr::JumpIfFalse { to: -jump_length });
},
"quote" => {
let value = quoted_sexpr(&cdr[0])?;
result.push(Instr::Push { value });
}
_ => {
result.append(&mut self.compile_intrinsic(call, &cdr)?);
}
} // End `match call`
}, // End `Symbol(call)`
_ => { dbg!(car); unimplemented!() },
} // End `match car`
}, // End `Cons(car, cdr)`
_ => { result.append(&mut self.compile_atom(&src)?); },
} // End `match src`
break 'tco;
} // End `loop`
Ok(result)
}
fn compile_atom(&mut self, atom: &Sexpr) -> Result<Vec<Instr>, String> {
let mut result = Vec::new();
match atom {
Int(i) => { result.push(Instr::Push { value: Type::Int(*i) }); },
Float(f) => { result.push(Instr::Push { value: Type::Float(*f) }); },
Str(s) => { result.push(Instr::Push { value: Type::String(s.to_string()) }); },
Boolean(b) => { result.push(Instr::Push { value: Type::Boolean(*b) }); },
Symbol(s) => {
let var_pointer = match self.variables.iter().find(|&(ref name, _)| name == s) {
Some((_, pointer)) => *pointer,
None => return Err(format!("Undefined variable {}", s)),
};
result.push(Instr::Comment { text: format!("`{}` variable", s) });
result.push(Instr::Load { address: var_pointer });
},
_ => { result.append(&mut self.compile(atom.clone())?); }
}
Ok(result)
}
fn compile_intrinsic(&mut self, intrinsic: &String, args: &[Sexpr]) -> Result<Vec<Instr>, String> {
let mut result = Vec::new();
match intrinsic.as_str() {
"print" => {
result.append(&mut self.compile(args[0].clone())?);
result.push(Instr::Call { function: "print".to_string() });
},
"read" => { result.push(Instr::Call { function: "read".to_string() }); },
"slurp" => {
result.append(&mut self.compile(args[0].clone())?);
result.push(Instr::Call { function: "slurp".to_string() });
},
"throw" => {
result.append(&mut self.compile(args[0].clone())?);
result.push(Instr::Call { function: "throw".to_string() });
}
"add" | "+" => {
let mut lhs = self.compile_atom(&args[0])?;
result.append(&mut lhs);
let mut rhs = self.compile_atom(&args[1])?;
result.append(&mut rhs);
result.push(Instr::Add);
},
"sub" | "-" => {
let mut lhs = self.compile_atom(&args[0])?;
result.append(&mut lhs);
let mut rhs = self.compile_atom(&args[1])?;
result.append(&mut rhs);
result.push(Instr::Swap);
result.push(Instr::Sub);
},
"mul" | "*" => {
let mut lhs = self.compile_atom(&args[0])?;
result.append(&mut lhs);
let mut rhs = self.compile_atom(&args[1])?;
result.append(&mut rhs);
result.push(Instr::Mul);
},
"div" | "/" => {
let mut lhs = self.compile_atom(&args[0])?;
result.append(&mut lhs);
let mut rhs = self.compile_atom(&args[1])?;
result.append(&mut rhs);
result.push(Instr::Swap);
result.push(Instr::Div);
},
"equal" | "=" => {
let mut lhs = self.compile_atom(&args[0])?;
result.append(&mut lhs);
let mut rhs = self.compile_atom(&args[1])?;
result.append(&mut rhs);
result.push(Instr::Equal);
},
"not" | "!" => {
let mut lhs = self.compile_atom(&args[0])?;
result.append(&mut lhs);
result.push(Instr::Not);
},
_ => {
result.push(Instr::Comment { text: format!("`{}` function", intrinsic) });
result.push(Instr::JumpLabel { to: format!("function_{}", intrinsic), });
}
}
Ok(result)
}
}
fn quoted_sexpr(cdr: &Sexpr) -> Result<Type, String> {
match cdr {
Cons(car, cdr) => {
let mut vec = Vec::new();
vec.push(quoted_sexpr(car)?);
for item in cdr {
vec.push(quoted_sexpr(item)?);
}
Ok(Type::Cons(vec))
},
Symbol(ref s) => Ok(Type::String(s.to_string())),
Str(ref s) => Ok(Type::String(s.to_string())),
Int(ref i) => Ok(Type::Int(*i)),
Float(ref f) => Ok(Type::Float(*f)),
Boolean(ref b) => Ok(Type::Boolean(*b)),
Nil => Ok(Type::Null),
}
}
fn len(vec: &Vec<Instr>) -> usize {
let mut result = 0;
for i in vec {
match i {
Instr::Comment { .. } => {},
Instr::Label { .. } => {},
_ => { result += 1; },
}
}
result
}

View file

@ -1,4 +0,0 @@
/// Definition of the compiler.
pub mod compile;
/// Definition of the parser.
pub mod parser;

View file

@ -1,121 +0,0 @@
use regex::Regex;
use crate::compiler::parser::Sexpr::*;
#[derive(Debug, Clone)]
pub enum Sexpr {
Int(i64), Float(f64), Str(String), Boolean(bool),
Symbol(String),
Cons(Box<Sexpr>, Vec<Sexpr>),
Nil,
}
// Used for error displaying
impl std::fmt::Display for Sexpr {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Int(i) => write!(f, "{}", i),
Float(fl) => write!(f, "{}", fl),
Str(s) => write!(f, "\"{}\"", s),
Boolean(b) => write!(f, "{}", b),
Symbol(s) => write!(f, "{}", s),
Cons(car, cdr) => {
write!(f, "(")?;
write!(f, "{}", car)?;
for c in cdr {
write!(f, " {}", c)?;
}
write!(f, ")")
},
Nil => write!(f, "nil"),
}
}
}
pub type ParseResult = Result<Sexpr, String>;
pub struct Parser {
unparsed: Vec<String>,
position: usize,
}
impl Parser {
pub fn new(src: Vec<String>) -> Parser {
Parser {
unparsed: src,
position: 0,
}
}
fn peek(&mut self) -> Option<String> {
self.unparsed.get(self.position).cloned()
}
fn next(&mut self) -> Option<String> {
self.position += 1;
self.unparsed.get(self.position - 1).cloned()
}
pub fn parse(&mut self) -> ParseResult {
match self.peek() {
Some(s) => match s.as_str() {
")" => Err(format!("Unexpected ')' at position {}", self.position)),
"'" => {
self.next();
Ok(Cons(Box::new(Symbol("quote".to_string())), vec![self.parse()?]))
},
"(" => self.parse_sequence(")"),
_ => self.parse_atom(),
}
None => return Err("Unexpected EOF".to_string()),
}
}
fn parse_sequence(&mut self, end: &str) -> ParseResult {
self.next();
let car = self.parse()?;
let mut cdr = Vec::new();
loop {
let token = match self.peek() {
Some(token) => token,
None => return Err(format!("Unexpected end of input, expected '{}'", end)),
};
if token == end { break; }
cdr.push(self.parse()?)
}
self.next();
Ok(Sexpr::Cons(Box::new(car), cdr))
}
fn parse_atom(&mut self) -> ParseResult {
let token = self.next().unwrap();
match token.as_str() {
"null" => Ok(Nil),
"true" | "True" => Ok(Boolean(true)),
"false" | "False" => Ok(Boolean(false)),
_ => {
if Regex::new(r#""(?:\\.|[^\\"])*""#).unwrap().is_match(&token) {
Ok(Str(token[1..token.len() - 1].to_string()))
} else if Regex::new(r#"[+-]?([0-9]*[.])?[0-9]+"#).unwrap().is_match(&token) {
Ok(Int(token.parse().unwrap()))
} else if Regex::new(r#"[+-]?([0-9]*[.])?[0-9]+"#).unwrap().is_match(&token) {
Ok(Float(token.parse().unwrap()))
} else {
Ok(Symbol(token))
}
}
}
}
}
pub fn tokenize(str: &str) -> Vec<String> {
let regex = Regex::new(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]+)"###).unwrap();
let mut res = vec![];
for cap in regex.captures_iter(str) {
if cap[1].starts_with(";") { continue; }
res.push(String::from(&cap[1]));
}
res
}

View file

@ -1,87 +0,0 @@
use std::{fs::{read_to_string, File}, path::{Path, PathBuf}, io::{Write, BufWriter}, time::Instant, process::exit};
use structopt::StructOpt;
mod args;
use args::Opts;
mod util;
use util::cover_paren;
mod compiler;
use compiler::{compile::Compiler, parser::{tokenize, Parser}};
mod vm;
use vm::{vm::VM, parser::parse_instr};
fn main() {
let args = Opts::from_args();
match args.commands {
args::Args::Compile(args) => {
let src = read_to_string(&args.file).unwrap();
let debug = args.debug;
compile_src(src, args.output, args.file, debug);
},
args::Args::Run(args) => {
let src = read_to_string(&args.file).unwrap();
let debug = args.debug;
run_src(src, debug);
},
}
}
fn compile_src(src: String, path: Option<PathBuf>, file: PathBuf, debug: bool) {
let file_name = match path {
Some(path) => path,
None => Path::new(&file).to_path_buf(),
}.file_stem().unwrap().to_str().unwrap().to_string(); // what
let start = Instant::now();
let tokens = tokenize(&cover_paren(src));
let mut parser = Parser::new(tokens.clone());
let result = parser.parse();
if debug { println!("{:#?}", &result); }
match result {
Ok(ast) => {
let mut compiler = Compiler::new();
let code = compiler.compile(ast);
match code {
Ok(code) => {
let file = File::create(format!("{}.bsm", file_name)).unwrap();
let mut buffer = BufWriter::new(file);
for line in code {
writeln!(buffer, "{}", line).unwrap();
}
buffer.flush().unwrap();
let elapsed = start.elapsed();
println!("Compiled in {}.{}s", elapsed.as_secs(), elapsed.subsec_millis());
exit(0);
},
Err(err) => {
eprintln!("{}", err);
exit(1);
}
}
},
Err(e) => {
eprintln!("{}", e);
exit(1);
}
}
}
fn run_src(src: String, debug: bool) {
let instrs = parse_instr(&src);
let mut vm = VM::new();
match vm.run(instrs, debug) {
Ok(()) => {
exit(0);
},
Err(e) => {
eprintln!("{}", e);
exit(1);
}
}
}

View file

@ -1,3 +0,0 @@
pub fn cover_paren(s: String) -> String {
format!("(do {})", s)
}

View file

@ -1,86 +0,0 @@
use std::{fmt::Display, str::FromStr};
use crate::vm::types::Type;
#[derive(Clone, Copy, Debug)]
pub struct Register { pub value: usize }
impl Register {
pub fn value(&self) -> usize { self.value }
}
impl Display for Register {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "%{}", self.value)
}
}
impl FromStr for Register {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if !s.starts_with("%") { return Err(format!("Invalid register: {}", s)); }
let value = s[1..].parse::<usize>().map_err(|_| (format!("Invalid register: {}", s)))?;
Ok(Register { value })
}
}
/// Instructions for the assembler.
#[derive(Clone, Debug)]
pub enum Instr {
Label { name: String }, Comment { text: String },
// Variable declaration
Load { address: Register }, Store { address: Register },
// Call intrinsic function.
Call { function: String },
// Stack operations.
Push { value: Type }, Pop { address: Register }, Swap,
// Stack arithmetic operations.
Add, Sub,
Mul, Div,
Not,
// Jumping.
JumpLabel { to: String }, // Jump to (function) label.
Jump { to: isize }, // Jump with offset.
JumpIfFalse { to: isize },
// Comparison (with stack values).
Equal,
Return,
}
impl Display for Instr {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
// --4-- Padding
// ----------20--------- Parameter start
Instr::Label { name } => write!(f, ".{}:", name),
Instr::Comment { text } => write!(f, " ; {}", text),
Instr::Load { address } => write!(f, " LOAD {}", address),
Instr::Store { address } => write!(f, " STORE {}", address),
Instr::Call { function } => write!(f, " CALL {}", function),
Instr::Push { value } => write!(f, " PUSH {}", value),
Instr::Pop { address } => write!(f, " POP {}", address),
Instr::Swap => write!(f, " SWAP"),
Instr::Add => write!(f, " ADD"),
Instr::Sub => write!(f, " SUB"),
Instr::Mul => write!(f, " MUL"),
Instr::Div => write!(f, " DIV"),
Instr::Not => write!(f, " NOT"),
Instr::JumpLabel { to } => write!(f, " JMPL {}", to),
Instr::Jump { to } => write!(f, " JMP {}", to),
Instr::JumpIfFalse { to } => write!(f, " JMPF {}", to),
Instr::Equal => write!(f, " EQ"),
Instr::Return => write!(f, " RET"),
}
}
}

View file

@ -1,8 +0,0 @@
/// Type for instrs.
pub mod types;
/// Definition of the VM instructions.
pub mod instr;
/// Definition of the instruction parser.
pub mod parser;
/// Definition of the virtual machine.
pub mod vm;

View file

@ -1,52 +0,0 @@
use regex::Regex;
use crate::vm::{instr::*, types::Type};
const REGEX: &str = r###"\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)|[^\s\$";]+|"[^"]*"|;.*"###;
macro_rules! value { ($s:expr) => { $s.parse::<Type>().unwrap() }; }
macro_rules! register { ($s:expr) => { $s.parse::<Register>().unwrap() }; }
pub fn parse_instr(src: &str) -> Vec<Instr> {
let regex = Regex::new(REGEX).unwrap();
let mut result = Vec::new();
for line in src.lines() {
let tokens = regex.find_iter(line).map(|m| m.as_str()).collect::<Vec<_>>();
if tokens[0].starts_with(";") { continue; }
match tokens[0] {
"LOAD" => { result.push(Instr::Load { address: register!(tokens[1].to_string()) }); },
"STORE" => { result.push(Instr::Store { address: register!(tokens[1].to_string()) }); },
"CALL" => { result.push(Instr::Call { function: tokens[1].to_string() }); },
"PUSH" => { result.push(Instr::Push { value: value!(tokens[1]) }); },
"POP" => { result.push(Instr::Pop { address: register!(tokens[1]) }); },
"SWAP" => { result.push(Instr::Swap); },
"ADD" => { result.push(Instr::Add); },
"SUB" => { result.push(Instr::Sub); },
"MUL" => { result.push(Instr::Mul); },
"DIV" => { result.push(Instr::Div); },
"NOT" => { result.push(Instr::Not); },
"JMPL" => { result.push(Instr::JumpLabel { to: tokens[1].to_string() }); },
"JMP" => { result.push(Instr::Jump { to: tokens[1].parse::<isize>().unwrap() }); },
"JMPF" => { result.push(Instr::JumpIfFalse { to: tokens[1].parse::<isize>().unwrap() }); },
"EQ" => { result.push(Instr::Equal); },
"RET" => { result.push(Instr::Return); },
_ => {
if tokens[0].starts_with(".") {
let name = &tokens[0][1..tokens[0].len() - 1];
result.push(Instr::Label { name: name.to_string() });
}
},
}
}
result
}

View file

@ -1,181 +0,0 @@
use std::{fmt::Display, str::FromStr, ops::{Add, Sub, Mul, Div, Not}};
use crate::vm::vm::Error::{self, InvalidAriphmeticOperation};
/// Literal types for the assembler.
#[derive(Clone, Debug, PartialEq)]
pub enum Type {
Null,
Int(i64),
Float(f64),
Boolean(bool),
String(String),
Cons(Vec<Type>),
}
impl Type {
pub fn is_null(&self) -> bool {
match self {
Type::Null => true,
_ => false,
}
}
pub fn trim(&self) -> Type {
match self {
Type::String(s) => Type::String(s[1..s.len() - 1].to_string()),
_ => self.clone(),
}
}
pub fn print(&self) -> String {
match self {
Type::Cons(v) => {
let mut s = String::new();
s.push('(');
for (i, t) in v.iter().enumerate() {
if i != 0 { s.push(' '); }
s.push_str(&t.print());
}
s.push(')');
s
},
Type::String(s) => s.trim().to_string(),
_ => self.clone().to_string(),
}
}
}
impl Add for Type {
type Output = Result<Type, Error>;
fn add(self, other: Type) -> Result<Type, Error> {
match (self, other) {
(Type::Int(lhs), Type::Int(rhs)) => Ok(Type::Int(lhs + rhs)),
(Type::Int(lhs), Type::Float(rhs)) => Ok(Type::Float(lhs as f64 + rhs)),
(Type::Float(lhs), Type::Int(rhs)) => Ok(Type::Float(lhs + rhs as f64)),
(Type::Float(lhs), Type::Float(rhs)) => Ok(Type::Float(lhs + rhs)),
(Type::String(lhs), Type::String(rhs)) => Ok(Type::String(format!("{}{}", lhs, rhs))),
_ => Err(InvalidAriphmeticOperation),
}
}
}
impl Sub for Type {
type Output = Result<Type, Error>;
fn sub(self, other: Type) -> Result<Type, Error> {
match (self, other) {
(Type::Int(lhs), Type::Int(rhs)) => Ok(Type::Int(lhs - rhs)),
(Type::Int(lhs), Type::Float(rhs)) => Ok(Type::Float(lhs as f64 - rhs)),
(Type::Float(lhs), Type::Int(rhs)) => Ok(Type::Float(lhs - rhs as f64)),
(Type::Float(lhs), Type::Float(rhs)) => Ok(Type::Float(lhs - rhs)),
_ => Err(InvalidAriphmeticOperation),
}
}
}
impl Mul for Type {
type Output = Result<Type, Error>;
fn mul(self, other: Type) -> Result<Type, Error> {
match (self, other) {
(Type::Int(lhs), Type::Int(rhs)) => Ok(Type::Int(lhs * rhs)),
(Type::Int(lhs), Type::Float(rhs)) => Ok(Type::Float(lhs as f64 * rhs)),
(Type::Float(lhs), Type::Int(rhs)) => Ok(Type::Float(lhs * rhs as f64)),
(Type::Float(lhs), Type::Float(rhs)) => Ok(Type::Float(lhs * rhs)),
_ => Err(InvalidAriphmeticOperation),
}
}
}
impl Div for Type {
type Output = Result<Type, Error>;
fn div(self, other: Type) -> Result<Type, Error> {
match (self, other) {
(Type::Int(lhs), Type::Int(rhs)) => Ok(Type::Int(lhs / rhs)),
(Type::Int(lhs), Type::Float(rhs)) => Ok(Type::Float(lhs as f64 / rhs)),
(Type::Float(lhs), Type::Int(rhs)) => Ok(Type::Float(lhs / rhs as f64)),
(Type::Float(lhs), Type::Float(rhs)) => Ok(Type::Float(lhs / rhs)),
_ => Err(InvalidAriphmeticOperation),
}
}
}
impl Not for Type {
type Output = Result<Type, Error>;
fn not(self) -> Result<Type, Error> {
match self {
Type::Boolean(b) => Ok(Type::Boolean(!b)),
_ => Err(InvalidAriphmeticOperation),
}
}
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Type::Int(i) => write!(f, "{}", i),
Type::Float(fl) => write!(f, "{}", fl),
Type::Boolean(b) => write!(f, "{}", b),
Type::String(s) => write!(f, "\"{}\"", s),
Type::Cons(v) => {
let mut s = String::new();
s.push('(');
for (i, t) in v.iter().enumerate() {
if i != 0 { s.push(' '); }
s.push_str(&t.to_string());
}
s.push(')');
write!(f, "{}", s)
},
_ => unreachable!(),
}
}
}
impl FromStr for Type {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"true" => Ok(Type::Boolean(true)),
"false" => Ok(Type::Boolean(false)),
_ => {
if s.starts_with("(") && s.ends_with(")") {
let mut v = Vec::new();
let mut s = s[1..s.len() - 1].to_string();
// TODO: This is pretty messy :(
while !s.is_empty() {
let mut i = 0;
while i < s.len() && s.chars().nth(i).unwrap().is_whitespace() { i += 1; }
s = s[i..s.len()].to_string();
if s.is_empty() { break; }
let mut j = 0;
while j < s.len() && !s.chars().nth(j).unwrap().is_whitespace() { j += 1; }
let t = &s[0..j];
v.push(t.parse::<Type>()?);
s = s[j..s.len()].to_string();
}
Ok(Type::Cons(v))
} else {
let i = s.parse::<i64>();
if i.is_ok() {
Ok(Type::Int(i.unwrap()))
} else {
let fl = s.parse::<f64>();
if fl.is_ok() {
Ok(Type::Float(fl.unwrap()))
} else {
Ok(Type::String(s.to_string()))
}
}
}
}
}
}
}

View file

@ -1,247 +0,0 @@
use std::{io::{self, Read}, fmt::Display, fs::File};
use crate::vm::{instr::{Instr::{self, *}, Register}, types::Type};
pub enum Error {
NoMainFunction,
StackOverflow,
UnknownFunction(String),
UnknownFunctionCall(String),
InvalidAriphmeticOperation,
FileError(String),
Throw(String),
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::NoMainFunction => write!(f, "Main function not found"),
Error::StackOverflow => write!(f, "Stack overflow"),
Error::UnknownFunction(name) => write!(f, "Unknown function: {}", name),
Error::UnknownFunctionCall(function) => write!(f, "Unknown function call: {}", function),
Error::InvalidAriphmeticOperation => write!(f, "Invalid ariphmetic operation"),
Error::FileError(msg) => write!(f, "Could not open file: {}", msg),
Error::Throw(msg) => write!(f, "{}", msg),
}
}
}
#[derive(Debug)]
pub struct VM {
instr_pointer: isize,
jumped_from: isize,
registers: Vec<Type>,
stack: Vec<Type>,
function_pointer: Vec<(String, isize)>, // (name, index)
}
pub type VMReturn = Result<(), Error>;
impl VM {
pub fn new() -> Self {
VM {
instr_pointer: 0,
jumped_from: 0,
registers: vec![Type::Null; 1024],
stack: Vec::new(),
function_pointer: Vec::new(),
}
}
pub fn run(&mut self, instrs: Vec<Instr>, debug: bool) -> VMReturn {
let result: VMReturn;
let mut found = false;
for (idx, instr) in instrs.iter().enumerate() {
match instr {
Label { name } => {
if name == "function_main" { self.instr_pointer = idx as isize; found = true; }
self.function_pointer.push((name.clone(), idx as isize));
},
_ => {}
}
}
if !found { return Err(Error::NoMainFunction); }
'tco: loop {
// std::thread::sleep(std::time::Duration::from_millis(1000));
self.instr_pointer += 1;
if self.instr_pointer - 1 == instrs.len() as isize {
result = Ok(());
break 'tco;
}
let instr = &instrs[(self.instr_pointer - 1) as usize];
if debug { print_debug(self, &instr); }
match instr {
Load { address } => {
self.load(address)?;
continue 'tco;
},
Store { address } => {
let value = &self.stack.pop().unwrap();
self.store(address, value)?;
continue 'tco;
},
Call { function } => {
self.call(function)?;
continue 'tco;
},
Push { value } => {
self.push(value.trim().clone())?;
continue 'tco;
},
Pop { address } => {
let value = self.stack.pop();
self.pop(&address, &value.unwrap())?;
continue 'tco;
},
Swap => {
let top = self.stack.pop().unwrap();
let bottom = self.stack.pop().unwrap();
self.stack.push(top);
self.stack.push(bottom);
continue 'tco;
},
Add => {
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
self.push((lhs + rhs)?)?;
continue 'tco;
},
Sub => {
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
self.push((lhs - rhs)?)?;
continue 'tco;
},
Mul => {
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
self.push((lhs * rhs)?)?;
continue 'tco;
},
Div => {
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
self.push((lhs / rhs)?)?;
continue 'tco;
},
Not => {
let value = self.stack.pop().unwrap();
self.push((!value)?)?;
continue 'tco;
},
JumpLabel { to } => {
let pointer = self.get_function_pointer(to.to_string())?;
self.jumped_from = self.instr_pointer;
self.instr_pointer = pointer;
continue 'tco;
},
Jump { to } => {
self.instr_pointer += *to as isize + 1;
continue 'tco;
},
JumpIfFalse { to } => {
let cond = self.stack.pop().unwrap();
if cond == Type::Boolean(false) {
if *to < 0 { self.instr_pointer += *to as isize - 2; }
else { self.instr_pointer += *to as isize + 1; }
continue 'tco;
}
},
Equal => {
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
self.push(Type::Boolean(lhs == rhs))?;
continue 'tco;
},
Return => {
if self.jumped_from == 0 { return Ok(()); }
self.instr_pointer = self.jumped_from;
self.jumped_from = 0;
continue 'tco;
},
Label { .. } => {},
_ => { dbg!(instr); unimplemented!()},
}
}
result
}
fn push(&mut self, value: Type) -> Result<(), Error> {
if self.stack.len() >= 1024 {
return Err(Error::StackOverflow);
}
Ok(self.stack.push(value))
}
fn pop(&mut self, address: &Register, value: &Type) -> Result<(), Error> {
// TODO: Remove .clone()
Ok(self.registers[address.value()] = value.clone())
}
fn store(&mut self, address: &Register, value: &Type) -> Result<(), Error> {
Ok(self.registers[address.value()] = value.clone())
}
fn load(&mut self, address: &Register) -> Result<(), Error> {
Ok(self.stack.push(self.registers[address.value()].clone()))
}
fn get_function_pointer(&mut self, name: String) -> Result<isize, Error> {
for (idx, (n, _)) in self.function_pointer.iter().enumerate() {
if n == &name {
return Ok(idx as isize);
}
}
Err(Error::UnknownFunction(name))
}
fn call(&mut self, function: &String) -> Result<(), Error> {
match function.as_str() {
"print" => {
let value = self.stack.pop().unwrap();
println!("{}", value.print());
return Ok(());
},
"read" => {
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let input = input.trim().parse::<Type>().unwrap();
self.stack.push(input);
Ok(())
},
"slurp" => {
let file_name = self.stack.pop().unwrap().to_string();
let mut result = String::new();
match File::open(file_name).and_then(|mut f| f.read_to_string(&mut result)) {
Ok(_) => Ok(self.stack.push(Type::String(result))),
Err(e) => Err(Error::FileError(e.to_string())),
}
},
"throw" => {
let value = self.stack.pop().unwrap();
return Err(Error::Throw(value.print()));
},
_ => { dbg!(function); Err(Error::UnknownFunctionCall(function.to_string())) },
}
}
}
fn print_debug(vm: &VM, curr_instr: &Instr) {
// get all register that are not null
let regs = vm.registers.iter().enumerate().filter(|(_, v)| !v.is_null()).collect::<Vec<_>>();
println!("regis: {:?}", regs);
println!("stack: {:?}", vm.stack);
println!("currn: {} {}", vm.instr_pointer, curr_instr);
}