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:
parent
841f007b44
commit
252a8aae33
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1 @@
|
|||
/target
|
||||
/target
|
||||
|
|
253
Cargo.lock
generated
253
Cargo.lock
generated
|
@ -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"
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -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]
|
||||
|
|
12
Makefile
12
Makefile
|
@ -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
|
47
README.md
47
README.md
|
@ -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()`)
|
|
@ -1,3 +0,0 @@
|
|||
(fun main (do
|
||||
(def str "Hello, world!")
|
||||
(print (str))))
|
|
@ -1,4 +0,0 @@
|
|||
(fun print_true (print "True"))
|
||||
(fun print_false (print "False"))
|
||||
(fun main
|
||||
(if true (print_true) (print_false)))
|
|
@ -1,4 +0,0 @@
|
|||
(fun main (do
|
||||
(def in (read))
|
||||
(print "Your input was: ")
|
||||
(print in)))
|
|
@ -1,3 +0,0 @@
|
|||
(fun main (do
|
||||
(def file (slurp "quine.blsp"))
|
||||
(print file)))
|
|
@ -1,6 +0,0 @@
|
|||
(fun main (do
|
||||
(print (quote (a b c)))
|
||||
(print (quote (+ 1 (+ 2 "3"))))
|
||||
(print (quote a))
|
||||
(print (quote "a"))
|
||||
))
|
|
@ -1,2 +0,0 @@
|
|||
(fun main
|
||||
(throw "error :("))
|
|
@ -1,5 +0,0 @@
|
|||
; https://esolangs.org/wiki/Truth-machine
|
||||
(fun main (do
|
||||
(def x (read))
|
||||
(while (equal x 1) (print x))
|
||||
(print "Done!")))
|
|
@ -1,7 +0,0 @@
|
|||
(fun return_true true)
|
||||
|
||||
(fun main (do
|
||||
(def name "John")
|
||||
(if (return_true)
|
||||
(print name)
|
||||
(print "no"))))
|
154
install.sh
154
install.sh
|
@ -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
13
src/main.rs
Normal 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
323
src/parser.rs
Normal 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
22
test.sh
|
@ -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
2
vyc/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
/target
|
||||
/proto
|
258
vyc/Cargo.lock
generated
258
vyc/Cargo.lock
generated
|
@ -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"
|
|
@ -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"
|
|
@ -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,
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
/// Definition of the compiler.
|
||||
pub mod compile;
|
||||
/// Definition of the parser.
|
||||
pub mod parser;
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
pub fn cover_paren(s: String) -> String {
|
||||
format!("(do {})", s)
|
||||
}
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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
|
||||
}
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
247
vyc/src/vm/vm.rs
247
vyc/src/vm/vm.rs
|
@ -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);
|
||||
}
|
Loading…
Reference in a new issue