From 252a8aae3351cc7d1b9d5ad99eda64ae27334643 Mon Sep 17 00:00:00 2001 From: Natapat Samutpong Date: Thu, 10 Feb 2022 03:08:54 +0700 Subject: [PATCH] refactor: rewrote lol --- .gitignore | 2 +- Cargo.lock | 253 +--------------------------- Cargo.toml | 12 +- Makefile | 12 -- README.md | 47 ------ example/hello.vy | 3 - example/if.vy | 4 - example/input.vy | 4 - example/quine.vy | 3 - example/quote.vy | 6 - example/throw.vy | 2 - example/truth_machine.vy | 5 - example/var.vy | 7 - install.sh | 154 ----------------- src/main.rs | 13 ++ src/parser.rs | 323 ++++++++++++++++++++++++++++++++++++ test.sh | 22 --- vyc/.gitignore | 2 - vyc/Cargo.lock | 258 ---------------------------- vyc/Cargo.toml | 15 -- vyc/src/args.rs | 41 ----- vyc/src/compiler/compile.rs | 249 --------------------------- vyc/src/compiler/mod.rs | 4 - vyc/src/compiler/parser.rs | 121 -------------- vyc/src/main.rs | 87 ---------- vyc/src/util.rs | 3 - vyc/src/vm/instr.rs | 86 ---------- vyc/src/vm/mod.rs | 8 - vyc/src/vm/parser.rs | 52 ------ vyc/src/vm/types.rs | 181 -------------------- vyc/src/vm/vm.rs | 247 --------------------------- 31 files changed, 346 insertions(+), 1880 deletions(-) delete mode 100644 Makefile delete mode 100644 README.md delete mode 100644 example/hello.vy delete mode 100644 example/if.vy delete mode 100644 example/input.vy delete mode 100644 example/quine.vy delete mode 100644 example/quote.vy delete mode 100644 example/throw.vy delete mode 100644 example/truth_machine.vy delete mode 100644 example/var.vy delete mode 100755 install.sh create mode 100644 src/main.rs create mode 100644 src/parser.rs delete mode 100755 test.sh delete mode 100644 vyc/.gitignore delete mode 100644 vyc/Cargo.lock delete mode 100644 vyc/Cargo.toml delete mode 100644 vyc/src/args.rs delete mode 100644 vyc/src/compiler/compile.rs delete mode 100644 vyc/src/compiler/mod.rs delete mode 100644 vyc/src/compiler/parser.rs delete mode 100644 vyc/src/main.rs delete mode 100644 vyc/src/util.rs delete mode 100644 vyc/src/vm/instr.rs delete mode 100644 vyc/src/vm/mod.rs delete mode 100644 vyc/src/vm/parser.rs delete mode 100644 vyc/src/vm/types.rs delete mode 100644 vyc/src/vm/vm.rs diff --git a/.gitignore b/.gitignore index c41cc9e..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/target \ No newline at end of file +/target diff --git a/Cargo.lock b/Cargo.lock index 29a117b..8deab14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index b60f76f..35b327f 100644 --- a/Cargo.toml +++ b/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] diff --git a/Makefile b/Makefile deleted file mode 100644 index 0a2d703..0000000 --- a/Makefile +++ /dev/null @@ -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 diff --git a/README.md b/README.md deleted file mode 100644 index b1e012e..0000000 --- a/README.md +++ /dev/null @@ -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()`) diff --git a/example/hello.vy b/example/hello.vy deleted file mode 100644 index f97d065..0000000 --- a/example/hello.vy +++ /dev/null @@ -1,3 +0,0 @@ -(fun main (do - (def str "Hello, world!") - (print (str)))) \ No newline at end of file diff --git a/example/if.vy b/example/if.vy deleted file mode 100644 index 6a957a9..0000000 --- a/example/if.vy +++ /dev/null @@ -1,4 +0,0 @@ -(fun print_true (print "True")) -(fun print_false (print "False")) -(fun main - (if true (print_true) (print_false))) \ No newline at end of file diff --git a/example/input.vy b/example/input.vy deleted file mode 100644 index d197bc2..0000000 --- a/example/input.vy +++ /dev/null @@ -1,4 +0,0 @@ -(fun main (do - (def in (read)) - (print "Your input was: ") - (print in))) diff --git a/example/quine.vy b/example/quine.vy deleted file mode 100644 index a0a6fd5..0000000 --- a/example/quine.vy +++ /dev/null @@ -1,3 +0,0 @@ -(fun main (do - (def file (slurp "quine.blsp")) - (print file))) \ No newline at end of file diff --git a/example/quote.vy b/example/quote.vy deleted file mode 100644 index 2e27c45..0000000 --- a/example/quote.vy +++ /dev/null @@ -1,6 +0,0 @@ -(fun main (do - (print (quote (a b c))) - (print (quote (+ 1 (+ 2 "3")))) - (print (quote a)) - (print (quote "a")) - )) \ No newline at end of file diff --git a/example/throw.vy b/example/throw.vy deleted file mode 100644 index 8a3dabd..0000000 --- a/example/throw.vy +++ /dev/null @@ -1,2 +0,0 @@ -(fun main - (throw "error :(")) \ No newline at end of file diff --git a/example/truth_machine.vy b/example/truth_machine.vy deleted file mode 100644 index 5baddb2..0000000 --- a/example/truth_machine.vy +++ /dev/null @@ -1,5 +0,0 @@ -; https://esolangs.org/wiki/Truth-machine -(fun main (do - (def x (read)) - (while (equal x 1) (print x)) - (print "Done!"))) \ No newline at end of file diff --git a/example/var.vy b/example/var.vy deleted file mode 100644 index eb931f6..0000000 --- a/example/var.vy +++ /dev/null @@ -1,7 +0,0 @@ -(fun return_true true) - -(fun main (do - (def name "John") - (if (return_true) - (print name) - (print "no")))) \ No newline at end of file diff --git a/install.sh b/install.sh deleted file mode 100755 index a3764e6..0000000 --- a/install.sh +++ /dev/null @@ -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 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c67ba18 --- /dev/null +++ b/src/main.rs @@ -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); + } +} diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..1fddcab --- /dev/null +++ b/src/parser.rs @@ -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>>, +} + +/// Cons -> (car, [cdr]) +#[derive(Debug, Clone)] +pub struct Cons { + pub car: Value, + pub cdr: Option>>, +} + + +#[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, 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::(), + ); + + 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 for List { + fn from_iter>(iter: I) -> Self { + let mut list = List { value: None }; + let mut tail: Option>> = 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 bool>(from: &str, predicate: F) -> Option { + 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 { + let mut skip: Option = 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 + 'a + ) -> impl Iterator> + 'a { + let mut stack: Vec = 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::() { Value::Int(int) } + // Parse float + else if let Ok(float) = token.parse::() { 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> + '_ { + read(tokenize(src)) +} diff --git a/test.sh b/test.sh deleted file mode 100755 index 5805d0f..0000000 --- a/test.sh +++ /dev/null @@ -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 diff --git a/vyc/.gitignore b/vyc/.gitignore deleted file mode 100644 index a0e30ff..0000000 --- a/vyc/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -/proto diff --git a/vyc/Cargo.lock b/vyc/Cargo.lock deleted file mode 100644 index 7c0a2aa..0000000 --- a/vyc/Cargo.lock +++ /dev/null @@ -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" diff --git a/vyc/Cargo.toml b/vyc/Cargo.toml deleted file mode 100644 index fdf94b3..0000000 --- a/vyc/Cargo.toml +++ /dev/null @@ -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" diff --git a/vyc/src/args.rs b/vyc/src/args.rs deleted file mode 100644 index 741e6ea..0000000 --- a/vyc/src/args.rs +++ /dev/null @@ -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, - #[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, -} \ No newline at end of file diff --git a/vyc/src/compiler/compile.rs b/vyc/src/compiler/compile.rs deleted file mode 100644 index ccc00d7..0000000 --- a/vyc/src/compiler/compile.rs +++ /dev/null @@ -1,249 +0,0 @@ -use crate::{vm::{instr::*, types::Type}, compiler::parser::Sexpr::{self, *}}; - -pub struct Compiler { - // Compiled instructions - pub instructions: Vec, - // 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, 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, 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, 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 { - 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) -> usize { - let mut result = 0; - for i in vec { - match i { - Instr::Comment { .. } => {}, - Instr::Label { .. } => {}, - _ => { result += 1; }, - } - } - result -} \ No newline at end of file diff --git a/vyc/src/compiler/mod.rs b/vyc/src/compiler/mod.rs deleted file mode 100644 index 9ab42a6..0000000 --- a/vyc/src/compiler/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -/// Definition of the compiler. -pub mod compile; -/// Definition of the parser. -pub mod parser; \ No newline at end of file diff --git a/vyc/src/compiler/parser.rs b/vyc/src/compiler/parser.rs deleted file mode 100644 index a17065a..0000000 --- a/vyc/src/compiler/parser.rs +++ /dev/null @@ -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, Vec), - 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; - -pub struct Parser { - unparsed: Vec, - position: usize, -} - -impl Parser { - pub fn new(src: Vec) -> Parser { - Parser { - unparsed: src, - position: 0, - } - } - - fn peek(&mut self) -> Option { - self.unparsed.get(self.position).cloned() - } - - fn next(&mut self) -> Option { - 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 { - 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 -} \ No newline at end of file diff --git a/vyc/src/main.rs b/vyc/src/main.rs deleted file mode 100644 index 001d53b..0000000 --- a/vyc/src/main.rs +++ /dev/null @@ -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, 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); - } - } -} diff --git a/vyc/src/util.rs b/vyc/src/util.rs deleted file mode 100644 index f6605a6..0000000 --- a/vyc/src/util.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn cover_paren(s: String) -> String { - format!("(do {})", s) -} \ No newline at end of file diff --git a/vyc/src/vm/instr.rs b/vyc/src/vm/instr.rs deleted file mode 100644 index 9dacec4..0000000 --- a/vyc/src/vm/instr.rs +++ /dev/null @@ -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 { - if !s.starts_with("%") { return Err(format!("Invalid register: {}", s)); } - - let value = s[1..].parse::().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"), - } - } -} \ No newline at end of file diff --git a/vyc/src/vm/mod.rs b/vyc/src/vm/mod.rs deleted file mode 100644 index 1fe3b5d..0000000 --- a/vyc/src/vm/mod.rs +++ /dev/null @@ -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; \ No newline at end of file diff --git a/vyc/src/vm/parser.rs b/vyc/src/vm/parser.rs deleted file mode 100644 index 7d9045c..0000000 --- a/vyc/src/vm/parser.rs +++ /dev/null @@ -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::().unwrap() }; } -macro_rules! register { ($s:expr) => { $s.parse::().unwrap() }; } - -pub fn parse_instr(src: &str) -> Vec { - 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::>(); - 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::().unwrap() }); }, - "JMPF" => { result.push(Instr::JumpIfFalse { to: tokens[1].parse::().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 -} \ No newline at end of file diff --git a/vyc/src/vm/types.rs b/vyc/src/vm/types.rs deleted file mode 100644 index 447dcf1..0000000 --- a/vyc/src/vm/types.rs +++ /dev/null @@ -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), -} - -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; - - fn add(self, other: Type) -> Result { - 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; - - fn sub(self, other: Type) -> Result { - 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; - - fn mul(self, other: Type) -> Result { - 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; - - fn div(self, other: Type) -> Result { - 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; - - fn not(self) -> Result { - 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 { - 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::()?); - s = s[j..s.len()].to_string(); - } - Ok(Type::Cons(v)) - } else { - let i = s.parse::(); - if i.is_ok() { - Ok(Type::Int(i.unwrap())) - } else { - let fl = s.parse::(); - if fl.is_ok() { - Ok(Type::Float(fl.unwrap())) - } else { - Ok(Type::String(s.to_string())) - } - } - } - } - } - } -} diff --git a/vyc/src/vm/vm.rs b/vyc/src/vm/vm.rs deleted file mode 100644 index 8a6da0b..0000000 --- a/vyc/src/vm/vm.rs +++ /dev/null @@ -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, - stack: Vec, - 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, 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 { - 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::().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::>(); - println!("regis: {:?}", regs); - println!("stack: {:?}", vm.stack); - println!("currn: {} {}", vm.instr_pointer, curr_instr); -} \ No newline at end of file