├── .deepsource.toml ├── .github ├── FUNDING.yml └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── a.txt ├── another_if.txt ├── another_loop.txt ├── as.txt ├── b.txt ├── bit.txt ├── fib.txt ├── float.txt ├── hello_world.txt ├── if.txt ├── input.txt ├── l.txt ├── large_op.txt ├── logic.txt ├── no_join.txt ├── should_fail.txt ├── simple_calculations.txt ├── syn.txt └── zero.txt ├── media └── mewl_banner.png ├── run ├── src ├── lib.rs ├── main.rs └── mewl │ ├── errors.rs │ ├── eval_helpers │ ├── atomic.rs │ ├── mewcheck.rs │ ├── mod.rs │ └── operations │ │ ├── binary.rs │ │ ├── comparison.rs │ │ └── mod.rs │ ├── evaluator.rs │ ├── mod.rs │ ├── parser.rs │ └── types.rs └── tests ├── addition_test.rs └── common └── mod.rs /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "rust" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | msrv = "1.30.0" -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: bauripalash 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: palash 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "atty" 7 | version = "0.2.14" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 10 | dependencies = [ 11 | "hermit-abi", 12 | "libc", 13 | "winapi", 14 | ] 15 | 16 | [[package]] 17 | name = "autocfg" 18 | version = "1.1.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 21 | 22 | [[package]] 23 | name = "bitflags" 24 | version = "1.3.2" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 27 | 28 | [[package]] 29 | name = "clap" 30 | version = "3.1.12" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "7c167e37342afc5f33fd87bbc870cedd020d2a6dffa05d45ccd9241fbdd146db" 33 | dependencies = [ 34 | "atty", 35 | "bitflags", 36 | "clap_lex", 37 | "indexmap", 38 | "lazy_static", 39 | "strsim", 40 | "termcolor", 41 | "textwrap", 42 | ] 43 | 44 | [[package]] 45 | name = "clap_lex" 46 | version = "0.1.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "189ddd3b5d32a70b35e7686054371742a937b0d99128e76dde6340210e966669" 49 | dependencies = [ 50 | "os_str_bytes", 51 | ] 52 | 53 | [[package]] 54 | name = "console" 55 | version = "0.15.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" 58 | dependencies = [ 59 | "encode_unicode", 60 | "libc", 61 | "once_cell", 62 | "regex", 63 | "terminal_size", 64 | "unicode-width", 65 | "winapi", 66 | ] 67 | 68 | [[package]] 69 | name = "encode_unicode" 70 | version = "0.3.6" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 73 | 74 | [[package]] 75 | name = "hashbrown" 76 | version = "0.11.2" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 79 | 80 | [[package]] 81 | name = "hermit-abi" 82 | version = "0.1.19" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 85 | dependencies = [ 86 | "libc", 87 | ] 88 | 89 | [[package]] 90 | name = "indexmap" 91 | version = "1.8.1" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" 94 | dependencies = [ 95 | "autocfg", 96 | "hashbrown", 97 | ] 98 | 99 | [[package]] 100 | name = "lazy_static" 101 | version = "1.4.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 104 | 105 | [[package]] 106 | name = "libc" 107 | version = "0.2.125" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" 110 | 111 | [[package]] 112 | name = "mewl" 113 | version = "0.1.0" 114 | dependencies = [ 115 | "clap", 116 | "console", 117 | ] 118 | 119 | [[package]] 120 | name = "once_cell" 121 | version = "1.10.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" 124 | 125 | [[package]] 126 | name = "os_str_bytes" 127 | version = "6.0.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" 130 | 131 | [[package]] 132 | name = "regex" 133 | version = "1.5.5" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" 136 | dependencies = [ 137 | "regex-syntax", 138 | ] 139 | 140 | [[package]] 141 | name = "regex-syntax" 142 | version = "0.6.25" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 145 | 146 | [[package]] 147 | name = "strsim" 148 | version = "0.10.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 151 | 152 | [[package]] 153 | name = "termcolor" 154 | version = "1.1.3" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 157 | dependencies = [ 158 | "winapi-util", 159 | ] 160 | 161 | [[package]] 162 | name = "terminal_size" 163 | version = "0.1.17" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" 166 | dependencies = [ 167 | "libc", 168 | "winapi", 169 | ] 170 | 171 | [[package]] 172 | name = "textwrap" 173 | version = "0.15.0" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" 176 | 177 | [[package]] 178 | name = "unicode-width" 179 | version = "0.1.9" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 182 | 183 | [[package]] 184 | name = "winapi" 185 | version = "0.3.9" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 188 | dependencies = [ 189 | "winapi-i686-pc-windows-gnu", 190 | "winapi-x86_64-pc-windows-gnu", 191 | ] 192 | 193 | [[package]] 194 | name = "winapi-i686-pc-windows-gnu" 195 | version = "0.4.0" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 198 | 199 | [[package]] 200 | name = "winapi-util" 201 | version = "0.1.5" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 204 | dependencies = [ 205 | "winapi", 206 | ] 207 | 208 | [[package]] 209 | name = "winapi-x86_64-pc-windows-gnu" 210 | version = "0.4.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 213 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mewl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Palash Bauri "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | console = "0.15.0" 11 | clap = {version = "3.1.12" , features=["cargo"]} 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Palash Bauri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![](media/mewl_banner.png) 2 | 3 | # Mewl 4 | 5 | ##### The programming language of cats' with the taste of lisp 6 | 7 | ## 🤔 What,Why? 8 | 9 | Well, 2 years ago in 2020, I created a esoteric programming language called [Mewmew](https://github.com/bauripalash/mewmew). It was just a fun and experimental language I made while tinkering with ANTLR, cause I was (am still) fascinated about programming languages, interpreter/compilers; how they work; how they basically become the medium of interaction between humans and computers and/or machines. But with ANTLR, I didn't actually learn much about compiler/interpreter construction. It just worked! No fun! 10 | 11 | So, In the October of 2021, I started re-writing the whole thing from scratch with Rust. I was able to build a lexer; a basic parser; But then I realized, syntax of 'mewmew' is ugly. I hurts my eyes to look at a 'mewmew' source code. 12 | 13 | Again in, March 2022, I started experimenting with mewmew lanugage but with lisp style syntax, 'cause lisp is awesome. So, here is the result, **Mewl** , mewmew reborn with the syntax similar to lisp. Most important thing, I am having fun while developing mewl, and the syntax doesn't hurt my eyes that much. 14 | 15 | ## 🎉 Get Started 16 | 17 | ### Read Documention 18 | 19 | [The Mewl Book](https://bauripalash.github.io/mewlbook) 20 | 21 | ### Hello World 22 | 23 | ```lisp 24 | [=mew [[+[* mewmewmew mewmew] mew] mewmew] ] //H 25 | [=mewmew [mew [- mew mew] mew]] //e 26 | [=mewmewmew [mew [- mew mew] [* mewmewmewmew mewmew]]] //l 27 | [=mewmewmewmew [mew mew mew]] //o 28 | [=mewmewmewmewmew [mewmewmew mewmew]] //SPACE 29 | [=mewmewmewmewmewmew [[* mewmewmewmew mewmew] mewmewmewmewmewmewmew]] //W 30 | [=mewmewmewmewmewmewmew [mew mew mewmewmewmew]] //r 31 | [=mewmewmewmewmewmewmewmew [mew [- mew mew] [- mew mew]]] //d 32 | [=mewmewmewmewmewmewmewmewmew [mewmewmew mewmewmew]] //! 33 | 34 | [::: ~mew //H 35 | ~mewmew //e 36 | ~mewmewmew //l 37 | ~mewmewmew //l 38 | ~mewmewmewmew //o 39 | ~mewmewmewmewmew //SPACE 40 | ~mewmewmewmewmewmew //W 41 | ~mewmewmewmew //o 42 | ~mewmewmewmewmewmewmew //r 43 | ~mewmewmew //l 44 | ~mewmewmewmewmewmewmewmew //d 45 | ~mewmewmewmewmewmewmewmewmew //! 46 | ] 47 | 48 | ``` 49 | 50 | ### Basic Syntax 51 | 52 | ```lisp 53 | ;; mew is equivalent to 1 54 | ;; mewmew is equivalent to 2 55 | ;; mewmewmewmewmew is equivalent to 5 56 | ;; [- mew mew] is equivalent to 0 57 | 58 | 59 | (:: [+ mew mew]) ;; prints 2 60 | ;; :: -> is a symbol/function to print the next atoms/expressions to stdout 61 | 62 | For more, Read Docs 63 | 64 | 65 | 66 | ``` 67 | 68 | ### Some operations 69 | 70 | ```lisp 71 | + -> Addition 72 | - -> Substraction 73 | * -> Multiplication 74 | / -> Division 75 | :: -> Print to stdout 76 | ::: -> Assumes next expressions/atom as byte value. convert them to string, including invalid chars and prints to stdout 77 | 78 | 79 | Learn more on Docs. 80 | ``` 81 | 82 | 83 | 84 | [![Test](https://github.com/bauripalash/mewl/actions/workflows/rust.yml/badge.svg)](https://github.com/bauripalash/mewl/actions/workflows/rust.yml) 85 | -------------------------------------------------------------------------------- /examples/a.txt: -------------------------------------------------------------------------------- 1 | [:: [- mewmewmew mewmew ]] 2 | [:: [ + [+ mew mew][- mew mewmew mewmew] ]] 3 | -------------------------------------------------------------------------------- /examples/another_if.txt: -------------------------------------------------------------------------------- 1 | // numbers 2 | 3 | [=mew mewmew] 4 | [@ [!= ~mew [' mew mew]] 5 | [[? [== [% ~mew mewmew] [- mew mew]] [:: ~mew]][=mew [+ ~mew mew]]] 6 | ] 7 | -------------------------------------------------------------------------------- /examples/another_loop.txt: -------------------------------------------------------------------------------- 1 | [=mew mew] //start //1 2 | [=mewmew [mewmewmewmewmew [- mew mew]]] //target //50 3 | 4 | [@ [< ~mew ~mewmew] [ [? [== [% ~mew mewmew] [- mew mew]] [:: ~mew]] [=mew [+ ~mew mew]]] ] 5 | -------------------------------------------------------------------------------- /examples/as.txt: -------------------------------------------------------------------------------- 1 | [:: mewmew] 2 | [=mew [+ mew mew] [- mew mew] [+ mew mew ] [+ mew mew]] 3 | [:: ~mew] 4 | [:: mewmewmew] 5 | -------------------------------------------------------------------------------- /examples/b.txt: -------------------------------------------------------------------------------- 1 | [:: [+ [mew mewmew mewmew mewmewmew]]] 2 | 3 | 4 | [:: 5 | mew 6 | [mewmew] 7 | [[mewmewmew]] 8 | [[[mewmewmewmew]]] 9 | [[[[mewmewmewmewmew]]]] 10 | [[[[[mewmewmewmewmewmew]]]]] 11 | [[[[[[mewmewmewmewmewmewmew]]]]]] 12 | [[[[[[[mewmewmewmewmewmewmewmew]]]]]]] 13 | [[[[[[[[mewmewmewmewmewmewmewmewmew]]]]]]]] 14 | [[[[[[[[[ - mew mew]]]]]]]]] 15 | 16 | ] 17 | -------------------------------------------------------------------------------- /examples/bit.txt: -------------------------------------------------------------------------------- 1 | [:: [&& mewmew mewmewmew ]] 2 | [:: [## mewmew mewmewmew]] 3 | [:: [^ mewmew mewmewmew ]] 4 | [:: [!! mewmewmew]] 5 | [:: [<< mewmew mewmewmew]] 6 | [:: [>> mewmew mewmewmew]] 7 | -------------------------------------------------------------------------------- /examples/fib.txt: -------------------------------------------------------------------------------- 1 | // Simple Fibonacci 2 | 3 | [=mewmew [- mew mew]] //a 4 | [=mewmewmew mew] //b 5 | [=mewmewmewmewmew mew] //c 6 | [=mew [+ mew mew]] //i 7 | 8 | [=mewmewmewmew [mewmew [- mew mew]]] //n => target `N`th 9 | [:: ~mewmew] 10 | [:: ~mewmewmew] 11 | 12 | //loop while n>=i 13 | 14 | [@ [>= ~mewmewmewmew ~mew] 15 | [ [=mewmewmewmewmew [+ ~mewmew ~mewmewmew]] //c=a+b 16 | [:: ~mewmewmewmewmew] //print c 17 | [=mewmew ~mewmewmew] //a=b 18 | [=mewmewmew ~mewmewmewmewmew] //b=c 19 | [=mew [+ ~mew mew]] ] //i += 1 20 | 21 | 22 | ] 23 | -------------------------------------------------------------------------------- /examples/float.txt: -------------------------------------------------------------------------------- 1 | [:: [/ mew.mew mewmew]] 2 | 3 | -------------------------------------------------------------------------------- /examples/hello_world.txt: -------------------------------------------------------------------------------- 1 | [=mew [[+[* mewmewmew mewmew] mew] mewmew] ] //H 2 | [=mewmew [mew [- mew mew] mew]] //e 3 | [=mewmewmew [mew [- mew mew] [* mewmewmewmew mewmew]]] //l 4 | [=mewmewmewmew [mew mew mew]] //o 5 | [=mewmewmewmewmew [mewmewmew mewmew]] //SPACE 6 | [=mewmewmewmewmewmew [[* mewmewmewmew mewmew] mewmewmewmewmewmewmew]] //W 7 | [=mewmewmewmewmewmewmew [mew mew mewmewmewmew]] //r 8 | [=mewmewmewmewmewmewmewmew [mew [- mew mew] [- mew mew]]] //d 9 | [=mewmewmewmewmewmewmewmewmew [mewmewmew mewmewmew]] //! 10 | 11 | [::: ~mew //H 12 | ~mewmew //e 13 | ~mewmewmew //l 14 | ~mewmewmew //l 15 | ~mewmewmewmew //o 16 | ~mewmewmewmewmew //SPACE 17 | ~mewmewmewmewmewmew //W 18 | ~mewmewmewmew //o 19 | ~mewmewmewmewmewmewmew //r 20 | ~mewmewmew //l 21 | ~mewmewmewmewmewmewmewmew //d 22 | ~mewmewmewmewmewmewmewmewmew //! 23 | ] 24 | 25 | -------------------------------------------------------------------------------- /examples/if.txt: -------------------------------------------------------------------------------- 1 | [:: [? [< mewmew mew] [:: mew] [:: mewmew]]] 2 | -------------------------------------------------------------------------------- /examples/input.txt: -------------------------------------------------------------------------------- 1 | [|> =mewmew] 2 | [:: ~mewmew] 3 | 4 | [||> =mew] 5 | [:: ~mew] 6 | -------------------------------------------------------------------------------- /examples/l.txt: -------------------------------------------------------------------------------- 1 | [=mewmew mew] 2 | [=mewmewmew [@ [!= ~mewmew [* mewmewmew mewmewmew] ] 3 | [ [:: ~mewmew] [=mewmew [+ ~mewmew mew]]] 4 | [+ ~mewmew mew] 5 | ]] 6 | [:: ~mewmewmew] 7 | -------------------------------------------------------------------------------- /examples/large_op.txt: -------------------------------------------------------------------------------- 1 | [::: [' [+[* mewmewmew mewmew] mew] mewmew]] 2 | -------------------------------------------------------------------------------- /examples/logic.txt: -------------------------------------------------------------------------------- 1 | [:: [!! [ mewmew mew mew ]]] 2 | -------------------------------------------------------------------------------- /examples/no_join.txt: -------------------------------------------------------------------------------- 1 | [::[mew mew [+ mew mew]]] 2 | -------------------------------------------------------------------------------- /examples/should_fail.txt: -------------------------------------------------------------------------------- 1 | [:: mewmew.mew.mew.mewmewmw] 2 | -------------------------------------------------------------------------------- /examples/simple_calculations.txt: -------------------------------------------------------------------------------- 1 | [:: [+ mew mew]] //print 2 2 | [:: [+ mewmewmew [+ mew mew]]] //print 5 3 | 4 | [:: [+ mew mewmew [ - mew mewmew mewmew mewmew] ]] //print -2 [2 + [1 - 2 - 2 - 2]] 5 | [:: [- mew mew]] //print 0 6 | 7 | [:: [+ [' mew mew mew] [' mew mewmew]]] //111+12; print 123 8 | [:: [- mew mewmew [- [' mewmew mewmew] mewmew ]]] // print -21 9 | 10 | [:: [+ mewmew.mew mew.mewmew.mew]] // 2.1 + 1.21 11 | [:: [* [' mewmew mewmew mewmew] [' mewmewmew mewmewmew mewmewmew ]]] //222*333 12 | [:: [/ mewmew mewmewmew]] //1.5 13 | -------------------------------------------------------------------------------- /examples/syn.txt: -------------------------------------------------------------------------------- 1 | [=mewmew [ [* mewmew mewmewmew] [+ mewmewmew mewmew ] ]] 2 | // hello world 3 | [::: ~mewmew] 4 | //[::: ~mewmew [+ ~mewmew mew] [+ ~mewmew mewmew] [- [* ~mewmew mewmew] [ * mewmewmew mewmewmew ] ] ] 5 | [:: mew] 6 | -------------------------------------------------------------------------------- /examples/zero.txt: -------------------------------------------------------------------------------- 1 | [:: [- mew mew]] 2 | 3 | [=mew [- [- mew mew] mew]] 4 | [:: ~mew] 5 | -------------------------------------------------------------------------------- /media/mewl_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bauripalash/mewl/6f02d87907bb91f48d0afa71b28fa231d92ee297/media/mewl_banner.png -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | for filename in examples/*.txt; do 4 | cargo run -- "$filename" 5 | done; 6 | 7 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod mewl; 2 | 3 | use std::{fs::File, io::Read}; 4 | 5 | pub fn mewlrun(filename: String) { 6 | let mut source_file = match File::open(filename.as_str()) { 7 | Ok(f) => f, 8 | Err(_) => { 9 | eprintln!("[Err!] The source file '{}' can not open/found!", filename); 10 | std::process::exit(1); 11 | } 12 | }; 13 | 14 | let mut source_code = String::new(); 15 | 16 | match source_file.read_to_string(&mut source_code) { 17 | Ok(_) => {} 18 | Err(_) => { 19 | eprintln!("[Err!] The source file cannot be read!"); 20 | std::process::exit(1); 21 | } 22 | }; 23 | 24 | let _ = mewl::parser::MewlParser::new(source_code).parse(true); 25 | } 26 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use clap::{crate_authors, crate_version, Arg, Command}; 4 | use console::style; 5 | 6 | use mewl::mewlrun; 7 | 8 | fn main() { 9 | let mewl_args = Command::new("Mewl") 10 | .version(crate_version!()) 11 | .author(crate_authors!()) 12 | .about("Mewl Programmming Language") 13 | .arg( 14 | Arg::new("script") 15 | .takes_value(false) 16 | .required(true) 17 | .help("Mewl script to run"), 18 | ) 19 | .get_matches(); 20 | 21 | if let Some(a) = mewl_args.value_of("script") { 22 | if Path::new(a).exists() { 23 | mewlrun(a.to_string()) 24 | } else { 25 | eprintln!( 26 | "Err , script file `{}` is invalid or can not be found!", 27 | style(a).magenta().bold().for_stderr() 28 | ); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/mewl/errors.rs: -------------------------------------------------------------------------------- 1 | use crate::mewl::types::*; 2 | use console::Style; 3 | use std::process::exit; 4 | const ERROR_LIST: [&str; 12] = [ 5 | "Sorry! I don't know the value of this variable!", // [0] //Undefined variable 6 | "Uh! I can't recognize this symbol! What to do with this?", // [1] //Unexpected symbol/char/atom 7 | "Please provide correct number of expression for this *loop*", // [2] // loop statement arguments wrong 8 | "Can not find correct number of arguments for this *if* expression", // [3] // if statement arguments wrong 9 | "No *expression(s)* provided after this Identifier to assign to it.", // [4] // no expression after identifier 10 | "Uh! I was not expecting a assignment here!", //[5] // unexpected assignment statement 11 | "I cannot combine the *expressions* for this operation", // [6] //failed to combine expression for assignment 12 | "Please provide a assignment mew symbol after this read input function", // [7] //Read MewNum/Number from stdin 13 | "I was not expecting a number as argument for this read input function", // [8] Got a number argument for stdin function 14 | "Please only provide a single assignment expression as argument to stdin function", //[9] //got multiple expressions after stdin function 15 | "I was not expecting a closing bracket here!", 16 | "Uh! Cannot find a closing bracket for this opening bracket", 17 | ]; 18 | 19 | const LOOP_EXAMPLE: &str = 20 | "Do something like this => \n [ @ [ Condition ] [ Body ] [ Return ] ]! (Return is optional)"; 21 | const IF_EXAMPLE : &str = "Do something like this => \n [ ? [ Condition ] [ Body ] [ False/Else Body ] ]! (False/Else is optional)"; 22 | 23 | pub fn no_closing_bracket(token: &MewToken, code: &str, do_exit: bool) { 24 | show_nice_error(token, code, ERROR_LIST[11].to_string()); 25 | 26 | if do_exit { 27 | exit(1); 28 | } 29 | } 30 | pub fn unexpected_closing_bracket(token: &MewToken, code: &str, do_exit: bool) { 31 | show_nice_error(token, code, ERROR_LIST[10].to_string()); 32 | 33 | if do_exit { 34 | exit(1); 35 | } 36 | } 37 | pub fn multiple_exp_after_stdin(token: &MewToken, code: &str, do_exit: bool) { 38 | show_nice_error(token, code, ERROR_LIST[9].to_string()); 39 | 40 | if do_exit { 41 | exit(1); 42 | } 43 | } 44 | 45 | pub fn no_assignment_symbol_after_stdin(token: &MewToken, code: &str, do_exit: bool) { 46 | show_nice_error(token, code, ERROR_LIST[7].to_string()); 47 | 48 | if do_exit { 49 | exit(1); 50 | } 51 | } 52 | 53 | pub fn number_after_stdin(token: &MewToken, code: &str, do_exit: bool) { 54 | show_nice_error(token, code, ERROR_LIST[8].to_string()); 55 | 56 | if do_exit { 57 | exit(1); 58 | } 59 | } 60 | 61 | /// When combining of expression fails 62 | /// For example 63 | /// ```[` mew mew]``` should be equal to 11.0 64 | /// use this function when the evaluator fails to combine atoms 65 | pub fn expresion_combine_failed(token: &MewToken, code: &str, do_exit: bool) { 66 | show_nice_error(token, code, ERROR_LIST[6].to_string()); 67 | if do_exit { 68 | exit(1); 69 | } 70 | } 71 | 72 | pub fn undefined_var(token: &MewToken, code: &str, do_exit: bool) { 73 | show_nice_error(token, code, ERROR_LIST[0].to_string()); 74 | 75 | if do_exit { 76 | exit(1); 77 | } 78 | } 79 | 80 | pub fn no_expression_after_id(token: &MewToken, code: &str, do_exit: bool) { 81 | show_nice_error(token, code, ERROR_LIST[4].to_string()); 82 | 83 | if do_exit { 84 | exit(1); 85 | } 86 | } 87 | 88 | pub fn unexpected_assignment(token: &MewToken, code: &str, do_exit: bool) { 89 | show_nice_error(token, code, ERROR_LIST[5].to_string()); 90 | 91 | if do_exit { 92 | exit(1); 93 | } 94 | } 95 | 96 | pub fn unknown_atom(token: &MewToken, code: &str, do_exit: bool) { 97 | show_nice_error(token, code, ERROR_LIST[1].to_string()); 98 | if do_exit { 99 | exit(1); 100 | } 101 | } 102 | 103 | pub fn loop_arg_wrong(token: &MewToken, code: &str, do_exit: bool) { 104 | show_nice_error(token, code, format!("{}\n{}", ERROR_LIST[2], LOOP_EXAMPLE)); 105 | if do_exit { 106 | exit(1); 107 | } 108 | } 109 | 110 | pub fn if_arg_wrong(token: &MewToken, code: &str, do_exit: bool) { 111 | show_nice_error(token, code, format!("{}\n{}", ERROR_LIST[3], IF_EXAMPLE)); 112 | if do_exit { 113 | exit(1); 114 | } 115 | } 116 | 117 | //TODO 118 | pub fn nice_error_atom_list(atom_list: &[Atom], source_code: &str, err_msg: String, do_exit: bool) { 119 | if atom_list.len() == 1 { 120 | if let Atom::Sym(s) = &atom_list[0] { 121 | show_nice_error(s, source_code, err_msg); 122 | if do_exit { 123 | exit(1); 124 | } 125 | } 126 | } 127 | let mut mewtok_list: Vec = vec![]; 128 | for atom in atom_list { 129 | if let Atom::Sym(s) = atom { 130 | mewtok_list.push(s.to_owned()); 131 | } 132 | } 133 | println!("{:?}", mewtok_list); 134 | } 135 | 136 | fn show_nice_error(tok: &MewToken, source_code: &str, err_msg: String) { 137 | let error_msg_color = Style::new().magenta().bold().for_stderr(); 138 | let error_token_color = Style::new().yellow().for_stderr(); 139 | let mut xx = source_code.to_string(); //cloning the source cause I don't want to mess up the origin source; 140 | // the parser maybe able to catch other error; so source should not be mutated; I guess; 141 | 142 | //checks if the next char a linefeed char `\n` for below bug 143 | //BUG: If there is a linefeed char after the error token - 144 | //the token highlight is also including the `\n` 145 | let newline_next = 146 | xx.chars().map(|s| s.to_string()).collect::>()[tok.position.1 .1 - 1] == "\n"; 147 | //xx = format!("{}" , style(xx).cyan()); 148 | // 149 | xx.insert_str( 150 | if newline_next { 151 | tok.position.1 .1 - 1 152 | } else { 153 | tok.position.1 .1 154 | }, 155 | format!(" {}", error_token_color.apply_to(" <== ")).as_str(), 156 | ); 157 | xx.insert_str( 158 | tok.position.1 .0 - 1, 159 | format!("{}", error_token_color.apply_to(" ==> ")).as_str(), 160 | ); 161 | 162 | let o: Vec = xx 163 | .split_terminator('\n') 164 | .into_iter() 165 | .map(|i| i.trim().to_string().replace('\n', "")) 166 | .collect(); 167 | 168 | let mut line_index = tok.position.0; 169 | if newline_next { 170 | line_index -= 2 171 | } else { 172 | line_index -= 1 173 | } 174 | 175 | if !err_msg.is_empty() { 176 | eprintln!("{}\n", error_msg_color.apply_to(err_msg)); 177 | } 178 | 179 | //line before the error line 180 | if line_index != 0 && o.len() > line_index { 181 | println!("|{}| {}", line_index, o[line_index - 1]) 182 | } 183 | //error token's line 184 | println!("|{}| {}", line_index + 1, o[line_index]); 185 | 186 | //next line after error 187 | if line_index < o.len() { 188 | println!("|{}| {}", line_index + 2, o[line_index + 1]) 189 | } 190 | 191 | //exit(1); 192 | } 193 | -------------------------------------------------------------------------------- /src/mewl/eval_helpers/atomic.rs: -------------------------------------------------------------------------------- 1 | use crate::mewl::errors::*; 2 | use crate::mewl::eval_helpers::mewcheck::*; 3 | use crate::mewl::types::*; 4 | use std::process::exit; 5 | 6 | pub fn extract_atom(atom: &Atom, source: &str) -> Option { 7 | match atom { 8 | Atom::Number(atm) => Some(*atm), 9 | Atom::Sym(atm) => { 10 | if is_this_identifier(atm) { 11 | //self.show_nice_error(atm, "Undefined variable!".to_string()); 12 | undefined_var(atm, source, false); 13 | //None 14 | } else if is_this_assignment(atm) { 15 | unexpected_assignment(atm, source, false); 16 | //None 17 | //self.show_nice_error(atm, "Unexpected assignment!".to_string()); 18 | } else { 19 | unknown_atom(atm, source, false); 20 | //None 21 | //self.show_nice_error(atm, "Unexpected symbol!".to_string()); 22 | } 23 | exit(1) 24 | //return None; 25 | } 26 | } 27 | } 28 | 29 | pub fn convert_from_mewnum(lexeme: &str) -> f64 { 30 | //let lexeme = &token.lexeme; 31 | 32 | if lexeme.contains('.') { 33 | let mut raw_mews: Vec<&str> = lexeme.trim().split('.').collect(); 34 | let first_part = raw_mews[0]; 35 | let sec_part = raw_mews[1]; 36 | raw_mews.drain(..2); 37 | let mut output = format!( 38 | "{}.{}", 39 | (first_part.len() as f64 / 3.0), 40 | (sec_part.len() as f64 / 3.0) 41 | ); 42 | 43 | if !raw_mews.is_empty() { 44 | for rm in raw_mews { 45 | output.push_str(&convert_from_mewnum(rm).to_string()) 46 | } 47 | } 48 | 49 | let x = match output.parse::() { 50 | Ok(n) => n, 51 | Err(_) => { 52 | eprintln!("Failed to parse this expresssion -> {} as float", lexeme); 53 | exit(1); 54 | } 55 | }; 56 | return x; 57 | } 58 | lexeme.len() as f64 / 3.0 59 | } 60 | -------------------------------------------------------------------------------- /src/mewl/eval_helpers/mewcheck.rs: -------------------------------------------------------------------------------- 1 | use crate::mewl::types::*; 2 | 3 | fn is_this_mewnum_string(lexeme: &str) -> bool { 4 | let temp_token = MewToken { 5 | lexeme: lexeme.to_string(), 6 | position: (0, (0, 0)), 7 | }; 8 | is_this_mewnum(&temp_token) 9 | } 10 | 11 | pub fn is_this_mewnum(token: &MewToken) -> bool { 12 | let mut token_lexeme = token.lexeme.chars(); 13 | if token.lexeme.contains('.') { 14 | let mut raw_mews: Vec<&str> = token.lexeme.split('.').collect(); 15 | if raw_mews.len() < 2 { 16 | return false; 17 | } 18 | if raw_mews[0].is_empty() { 19 | raw_mews.drain(..1); 20 | } 21 | 22 | for rm in &raw_mews { 23 | if !is_this_mewnum_string(rm) { 24 | return false; 25 | } 26 | } 27 | return true; 28 | } else { 29 | if token_lexeme.as_str().len() < 3 { 30 | return false; 31 | } 32 | 33 | while !token_lexeme.as_str().is_empty() { 34 | if token_lexeme.next() != Some('m') { 35 | return false; 36 | } 37 | if token_lexeme.next() != Some('e') { 38 | return false; 39 | } 40 | 41 | if token_lexeme.next() != Some('w') { 42 | return false; 43 | } 44 | } 45 | } 46 | 47 | true 48 | } 49 | 50 | #[allow(dead_code)] 51 | pub fn is_this_assignment(token: &MewToken) -> bool { 52 | let mut token_lexeme = token.lexeme.chars(); 53 | if token_lexeme.as_str().len() < 4 { 54 | return false; 55 | } 56 | 57 | if token_lexeme.next() != Some('=') { 58 | return false; 59 | } 60 | 61 | while !token_lexeme.as_str().is_empty() { 62 | if token_lexeme.next() != Some('m') { 63 | return false; 64 | } 65 | if token_lexeme.next() != Some('e') { 66 | return false; 67 | } 68 | 69 | if token_lexeme.next() != Some('w') { 70 | return false; 71 | } 72 | } 73 | 74 | true 75 | } 76 | 77 | pub fn is_this_identifier(token: &MewToken) -> bool { 78 | //println!("IS_ID=> {:?}" , token); 79 | let mut token_lexeme = token.lexeme.chars(); 80 | //let mut result = false; 81 | if token_lexeme.as_str().len() < 4 { 82 | return false; 83 | } 84 | if token_lexeme.next() != Some('~') { 85 | return false; 86 | } 87 | 88 | while !token_lexeme.as_str().is_empty() { 89 | if token_lexeme.next() != Some('m') { 90 | return false; 91 | } 92 | if token_lexeme.next() != Some('e') { 93 | return false; 94 | } 95 | 96 | if token_lexeme.next() != Some('w') { 97 | return false; 98 | } 99 | } 100 | 101 | true 102 | } 103 | -------------------------------------------------------------------------------- /src/mewl/eval_helpers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod atomic; 2 | pub mod mewcheck; 3 | pub mod operations; 4 | -------------------------------------------------------------------------------- /src/mewl/eval_helpers/operations/binary.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | use crate::mewl::errors::expresion_combine_failed; 4 | use crate::mewl::eval_helpers::atomic::*; 5 | use crate::mewl::eval_helpers::operations::comparison::do_comparison; 6 | use crate::mewl::types::{Atom, MewToken}; 7 | 8 | pub fn do_binary_operation(op_token: &MewToken, exp_args: Vec, source: &str) -> Atom { 9 | //println!("{:?}" , exp_args); 10 | let op = op_token.lexeme.as_str(); 11 | let extracted_atom_list: Vec> = exp_args 12 | .into_iter() 13 | .map(|a| extract_atom(&a, source)) 14 | .collect(); 15 | let mut result: f64 = 0.0; 16 | match op { 17 | "+" => { 18 | result = extracted_atom_list 19 | .into_iter() 20 | .flatten() 21 | .into_iter() 22 | .fold(0.0, |a, b| a + b); 23 | } 24 | 25 | "-" => { 26 | result = extracted_atom_list 27 | .into_iter() 28 | .flatten() 29 | .reduce(|a, b| a - b) 30 | .unwrap(); 31 | } 32 | 33 | "%" => { 34 | result = extracted_atom_list 35 | .into_iter() 36 | .flatten() 37 | .reduce(|a, b| a % b) 38 | .unwrap(); 39 | } 40 | 41 | "*" => { 42 | result = extracted_atom_list 43 | .into_iter() 44 | .flatten() 45 | .into_iter() 46 | .fold(1.0, |a, b| a * b); 47 | } 48 | 49 | "/" => { 50 | result = extracted_atom_list 51 | .into_iter() 52 | .flatten() 53 | .into_iter() 54 | .reduce(|a, b| b / a) 55 | .unwrap(); 56 | } 57 | 58 | "**" => { 59 | result = extracted_atom_list 60 | .into_iter() 61 | .flatten() 62 | .into_iter() 63 | .reduce(|a, b| a.powf(b)) 64 | .unwrap(); 65 | } 66 | 67 | ">>" => { 68 | result = extracted_atom_list 69 | .into_iter() 70 | .flatten() 71 | .into_iter() 72 | .reduce(|a, b| ((a as i64) >> (b as i64)) as f64) 73 | .unwrap(); 74 | } 75 | 76 | "<<" => { 77 | result = extracted_atom_list 78 | .into_iter() 79 | .flatten() 80 | .into_iter() 81 | .reduce(|a, b| ((a as i64) << (b as i64)) as f64) 82 | .unwrap(); 83 | } 84 | 85 | "&&" => { 86 | result = extracted_atom_list 87 | .into_iter() 88 | .flatten() 89 | .into_iter() 90 | .reduce(|a, b| ((a as i64) & (b as i64)) as f64) 91 | .unwrap(); 92 | } 93 | 94 | "^" => { 95 | result = extracted_atom_list 96 | .into_iter() 97 | .flatten() 98 | .into_iter() 99 | .reduce(|a, b| ((a as i64) ^ (b as i64)) as f64) 100 | .unwrap(); 101 | } 102 | 103 | "##" => { 104 | result = extracted_atom_list 105 | .into_iter() 106 | .flatten() 107 | .into_iter() 108 | .reduce(|a, b| ((a as i64) | (b as i64)) as f64) 109 | .unwrap(); 110 | } 111 | 112 | "!!" => { 113 | let temp = match extracted_atom_list.into_iter().flatten().into_iter().next() { 114 | Some(v) => v, 115 | None => { 116 | expresion_combine_failed(op_token, source, false); 117 | exit(1); 118 | } 119 | }; 120 | 121 | result = !(temp as i64) as f64; 122 | } 123 | 124 | ">" | "<" | "==" | "!=" | "<=" | ">=" | "@" | "#" | "!" => { 125 | let flat_list: Vec = extracted_atom_list.into_iter().flatten().collect(); 126 | 127 | result = match flat_list.is_empty() { 128 | true => 0.0, 129 | false => do_comparison(op, flat_list), 130 | }; 131 | } 132 | 133 | "'" => { 134 | let x: Vec = extracted_atom_list 135 | .iter() 136 | .flatten() 137 | .map(|item| item.abs().floor().to_string()) 138 | .collect(); 139 | match x.join("").parse::() { 140 | Ok(v) => result = v, 141 | Err(_) => expresion_combine_failed(op_token, source, false), 142 | } 143 | } 144 | 145 | "::" => { 146 | //println!("{:?}" , extracted_atom_list); 147 | println!( 148 | "{}", 149 | extracted_atom_list 150 | .into_iter() 151 | .flatten() 152 | .map(|s| s.to_string()) 153 | .collect::>() 154 | .join(" ") 155 | ); 156 | } 157 | 158 | ":::" => { 159 | println!( 160 | "{}", 161 | String::from_utf8_lossy( 162 | &extracted_atom_list 163 | .into_iter() 164 | .flatten() 165 | .map(|a| a as u8) 166 | .collect::>() 167 | ) 168 | ) 169 | } 170 | 171 | _ => {} 172 | } 173 | Atom::Number(result) 174 | } 175 | -------------------------------------------------------------------------------- /src/mewl/eval_helpers/operations/comparison.rs: -------------------------------------------------------------------------------- 1 | pub fn do_comparison(op: &str, exp_args: Vec) -> f64 { 2 | //println!("=> ->{}<- COMP => {:?}" , op ,exp_args); 3 | let temp_res: Option<&f64> = match op { 4 | "==" => exp_args.windows(2).all(|a| a[0] == a[1]).then(|| &1_f64), 5 | "!=" => exp_args 6 | .windows(2) 7 | .all(|a| a[0] != a[1]) 8 | .then(|| &exp_args[0]), 9 | "<" => exp_args 10 | .windows(2) 11 | .all(|a| a[0] < a[1]) 12 | .then(|| &exp_args[0]), 13 | ">" => exp_args 14 | .windows(2) 15 | .all(|a| a[0] > a[1]) 16 | .then(|| &exp_args[0]), 17 | ">=" => exp_args 18 | .windows(2) 19 | .all(|a| a[0] >= a[1]) 20 | .then(|| &exp_args[0]), 21 | "<=" => exp_args 22 | .windows(2) 23 | .all(|a| a[0] <= a[1]) 24 | .then(|| &exp_args[0]), 25 | "#" => { 26 | if exp_args.iter().any(|a| a == &(1.0_f64)) { 27 | Some(&1.0) 28 | } else { 29 | None 30 | } 31 | } 32 | "&" => { 33 | if exp_args.iter().all(|a| a == &(1.0_f64)) { 34 | Some(&1.0) 35 | } else { 36 | None 37 | } 38 | } 39 | "!" => { 40 | if exp_args[0] == 1.0 { 41 | None 42 | } else { 43 | Some(&1.0) 44 | } 45 | } 46 | _ => None, 47 | }; 48 | //println!("{:?}" , temp_res); 49 | if temp_res.is_some() { 50 | 1.0 51 | } else { 52 | 0.0 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/mewl/eval_helpers/operations/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod binary; 2 | pub mod comparison; 3 | -------------------------------------------------------------------------------- /src/mewl/evaluator.rs: -------------------------------------------------------------------------------- 1 | use crate::mewl::errors::*; 2 | use crate::mewl::eval_helpers::atomic::{convert_from_mewnum, extract_atom}; 3 | use crate::mewl::eval_helpers::mewcheck::*; 4 | use crate::mewl::eval_helpers::operations::binary::do_binary_operation; 5 | use crate::mewl::types::*; 6 | use std::collections::HashMap; 7 | use std::io; 8 | use std::process::exit; 9 | 10 | pub struct MewlEvaluator { 11 | pub expression: Expr, 12 | pub source: String, 13 | pub symbol_table: HashMap, 14 | } 15 | 16 | impl MewlEvaluator { 17 | pub fn new(expression: Expr, source: String) -> Self { 18 | Self { 19 | expression, 20 | source, 21 | symbol_table: HashMap::new(), 22 | } 23 | } 24 | 25 | pub fn do_eval(&mut self) -> (Option, Option>) { 26 | 27 | self.evaluate(&mut self.expression.clone(), &mut self.symbol_table.clone()) 28 | //println!("{:?}" , res); 29 | //res 30 | } 31 | 32 | fn evaluate_atom_expr( 33 | &mut self, 34 | atom: &mut Atom, 35 | symbol_table: &mut HashMap, 36 | ) -> (Option, Option>) { 37 | match atom { 38 | Atom::Number(_) => (Some(atom.to_owned()), None), 39 | Atom::Sym(atom_symbol) => { 40 | if OPERATORS.contains(&atom_symbol.lexeme.as_str()) { 41 | (Some(atom.to_owned()), None) 42 | } else if is_this_identifier(atom_symbol) { 43 | //[^ref-1] //see below 44 | //check if the symbol is identifer; basically if mew number starts with a `~` char 45 | 46 | let var_value = symbol_table.get(&atom_symbol.lexeme); //get value from symbol table 47 | 48 | if let Some(..) = var_value { 49 | (Some(Atom::Number(*var_value.unwrap())), None) 50 | //if the id has value assigned to it; create a new Atom with the value 51 | } else { 52 | undefined_var(atom_symbol, &self.source, true); 53 | //self.show_nice_error(atom_symbol, "Undefined variable!".to_string()); //variable has no value; show error 54 | //exit(1); 55 | (None, None) 56 | } 57 | } else if is_this_assignment(atom_symbol) { 58 | //check if the symbol is assignment; if mew number starts with `=` 59 | 60 | (Some(atom.to_owned()), None) // return as is; so we can use it later for assignment 61 | } else { 62 | unknown_atom(atom_symbol, &self.source, false); 63 | //self.show_nice_error( 64 | // atom_symbol, 65 | // "Sorry! I dont know what to do with this symbol!".to_string(), 66 | //); 67 | //exit(1); 68 | (None, None) 69 | } 70 | } 71 | } 72 | } 73 | 74 | fn evaluate_stdin_expr( 75 | &mut self, 76 | s: &MewToken, 77 | expr_list: &mut Vec, 78 | symbol_table: &mut HashMap, 79 | ) { 80 | if expr_list.len() != 2 { 81 | //println!("{:?}", expr_list); 82 | //eprintln!("Please provide a identifier to store the input"); 83 | //exit(1); 84 | // 85 | no_assignment_symbol_after_stdin(s, &self.source, true); 86 | } 87 | 88 | let identifer = expr_list.drain(1..2).collect::>(); 89 | let mut identifier_name: String = String::new(); 90 | let mut input_buffer: String = String::new(); 91 | 92 | match &identifer[0] { 93 | Expr::Atom(atm) => match atm { 94 | Atom::Sym(id) => { 95 | if is_this_assignment(id) { 96 | identifier_name = id.lexeme.clone(); 97 | } else if is_this_identifier(id) { 98 | let mut x = id.lexeme.chars(); 99 | x.next(); 100 | let temp_wrong_id = x.collect::(); 101 | eprintln!("Did you meant to write ={}", temp_wrong_id); 102 | exit(1); 103 | } else { 104 | //eprintln!() 105 | unknown_atom(id, &self.source, true); 106 | } 107 | } 108 | Atom::Number(_) => { 109 | number_after_stdin(s, &self.source, true); 110 | } 111 | }, 112 | Expr::List(l) => { 113 | //println!("{:?}" , expr_list) 114 | if l.len() != 1 { 115 | //println!("Please provide a single assignment mew symbol for stdin input operation"); 116 | 117 | //exit(1); 118 | // 119 | multiple_exp_after_stdin(s, &self.source, true); 120 | } else if let Expr::Atom(Atom::Sym(id)) = &l[0] { 121 | identifier_name = id.lexeme.clone(); 122 | } else if let Expr::Atom(Atom::Number(_)) = &l[0] { 123 | //eprintln!("I was not expecting a number here!"); 124 | //exit(1); 125 | 126 | number_after_stdin(s, &self.source, true); 127 | } 128 | } 129 | } 130 | 131 | if !identifier_name.is_empty() { 132 | match io::stdin().read_line(&mut input_buffer) { 133 | Ok(_) => {} 134 | Err(_) => { 135 | eprintln!("Failed to read the input!"); 136 | exit(1); 137 | } 138 | } 139 | } 140 | 141 | input_buffer = input_buffer.trim_end().to_owned(); 142 | let temp_token = MewToken { 143 | lexeme: input_buffer.clone(), 144 | position: (0, (0, 0)), 145 | }; 146 | let temp_atom: Atom = if is_this_mewnum(&temp_token) { 147 | Atom::Number(convert_from_mewnum(&temp_token.lexeme)) 148 | } else { 149 | let t = match input_buffer.parse::() { 150 | Ok(v) => v, 151 | Err(_) => { 152 | eprintln!( 153 | "Failed to parse the input; it is not a mewnum or traditional number!" 154 | ); 155 | exit(1); 156 | } 157 | }; 158 | 159 | Atom::Number(t) 160 | }; 161 | let temp_id_token = MewToken { 162 | lexeme: identifier_name, 163 | position: (0, (0, 0)), 164 | }; 165 | self.do_assignment(&temp_id_token, &[temp_atom], symbol_table); 166 | } 167 | 168 | fn evaluate_stdin_len_expr( 169 | &mut self, 170 | s: &MewToken, 171 | expr_list: &mut Vec, 172 | symbol_table: &mut HashMap, 173 | ) { 174 | if expr_list.len() != 2 { 175 | //println!("{:?}", expr_list); 176 | //eprintln!("Please provide a identifier to store the input"); 177 | //exit(1); 178 | no_assignment_symbol_after_stdin(s, &self.source, true); 179 | } 180 | 181 | let identifer = expr_list.drain(1..2).collect::>(); 182 | let mut identifier_name: String = String::new(); 183 | let mut input_buffer: String = String::new(); 184 | 185 | match &identifer[0] { 186 | Expr::Atom(atm) => match atm { 187 | Atom::Sym(id) => { 188 | if is_this_assignment(id) { 189 | identifier_name = id.lexeme.clone(); 190 | } else if is_this_identifier(id) { 191 | let mut x = id.lexeme.chars(); 192 | x.next(); 193 | let temp_wrong_id = x.collect::(); 194 | eprintln!("Did you meant to write ={}", temp_wrong_id); 195 | exit(1); 196 | } else { 197 | //eprintln!() 198 | unknown_atom(id, &self.source, true); 199 | } 200 | } 201 | Atom::Number(_) => number_after_stdin(s, &self.source, true), 202 | }, 203 | Expr::List(l) => { 204 | //println!("{:?}" , expr_list) 205 | if l.len() != 1 { 206 | multiple_exp_after_stdin(s, &self.source, true); 207 | //println!("Please provide a single assignment mew symbol for stdin input operation"); 208 | //exit(1); 209 | } else if let Expr::Atom(Atom::Sym(id)) = &l[0] { 210 | identifier_name = id.lexeme.clone(); 211 | } else if let Expr::Atom(Atom::Number(_)) = &l[0] { 212 | //eprintln!("I was not expecting a number here!"); 213 | //exit(1); 214 | // 215 | number_after_stdin(s, &self.source, true); 216 | } 217 | } 218 | } 219 | 220 | if !identifier_name.is_empty() { 221 | match io::stdin().read_line(&mut input_buffer) { 222 | Ok(_) => {} 223 | Err(_) => { 224 | eprintln!("Failed to read the input from stdin!"); 225 | exit(1); 226 | } 227 | } 228 | } 229 | let temp_atom = Atom::Number((input_buffer.trim_end().len()) as f64); 230 | let temp_id_token = MewToken { 231 | lexeme: identifier_name, 232 | position: (0, (0, 0)), 233 | }; 234 | self.do_assignment(&temp_id_token, &[temp_atom], symbol_table); 235 | } 236 | 237 | fn evaluate_while_loop( 238 | &mut self, 239 | s: &MewToken, 240 | expr_list: &mut Vec, 241 | symbol_table: &mut HashMap, 242 | ) -> (Option, Option>) { 243 | if expr_list.len() < 3 { 244 | loop_arg_wrong(s, &self.source, true); 245 | 246 | //exit(1); 247 | } 248 | let mut con_expr = expr_list.drain(..2).collect::>(); 249 | let mut condition_temp = self.evaluate(&mut con_expr[1], symbol_table).0; 250 | let mut condition: f64 = if let Some(Atom::Number(n)) = condition_temp { 251 | n 252 | } else { 253 | 0.0 254 | }; 255 | 256 | let body = expr_list.drain(..1).collect::>(); 257 | 258 | if condition >= 1.0 { 259 | loop { 260 | self.evaluate(&mut body.clone()[0], symbol_table); 261 | 262 | condition_temp = self.evaluate(&mut con_expr[1], symbol_table).0; 263 | condition = if let Some(Atom::Number(n)) = condition_temp { 264 | n 265 | } else { 266 | 0.0 267 | }; 268 | 269 | if condition == 0.0 { 270 | if expr_list.is_empty() { 271 | return (None, None); 272 | } else { 273 | let mut else_body = expr_list.drain(..1).collect::>(); 274 | return self.evaluate(&mut else_body[0], symbol_table); 275 | } 276 | 277 | //break; 278 | } 279 | } 280 | } 281 | 282 | (None, None) 283 | } 284 | 285 | fn evaluate_if_expr( 286 | &mut self, 287 | s: &MewToken, 288 | expr_list: &mut Vec, 289 | symbol_table: &mut HashMap, 290 | ) -> (Option, Option>) { 291 | if expr_list.len() < 3 { 292 | if_arg_wrong(s, &self.source, true); 293 | //exit(1); 294 | } 295 | 296 | let mut con_expr = expr_list.drain(..2).collect::>(); 297 | //println!("=> condition => {:?}" , con_expr[1]); 298 | let condition_temp = self.evaluate(&mut con_expr[1], symbol_table).0; 299 | //println!("{:?}" , self.evaluate(&mut con_expr.clone()[1], symbol_table)); 300 | let condition: f64 = if let Some(Atom::Number(n)) = condition_temp { 301 | n 302 | } else { 303 | 0.0 304 | }; 305 | //println!("=> {}" , condition); 306 | let mut body = expr_list.drain(..1).collect::>(); 307 | //println!("{:?}" , body); 308 | if condition >= 1.0 { 309 | return self.evaluate(&mut body[0], symbol_table); 310 | 311 | //break; 312 | } else if !expr_list.is_empty() { 313 | let mut else_body = expr_list.drain(..1).collect::>(); 314 | return self.evaluate(&mut else_body[0], symbol_table); 315 | } 316 | (None, None) 317 | } 318 | 319 | fn evaluate_list_expr( 320 | &mut self, 321 | expr_list: &mut Vec, 322 | symbol_table: &mut HashMap, 323 | ) -> (Option, Option>) { 324 | { 325 | if expr_list.is_empty() { 326 | (None, None) 327 | } else { 328 | let mut atom_list: Vec = vec![]; 329 | if let Expr::Atom(Atom::Sym(s)) = &expr_list.clone()[0] { 330 | if s.lexeme == *"|>" { 331 | self.evaluate_stdin_expr(s, expr_list, symbol_table); 332 | } else if s.lexeme == *"||>" { 333 | self.evaluate_stdin_len_expr(s, expr_list, symbol_table); 334 | } else if s.lexeme == *"@" { 335 | return self.evaluate_while_loop(s, expr_list, symbol_table); 336 | } else if s.lexeme == *"?" { 337 | return self.evaluate_if_expr(s, expr_list, symbol_table); 338 | } 339 | } 340 | for item in expr_list.iter_mut() { 341 | let evaluted_res: (Option, Option>) = 342 | self.evaluate(item, symbol_table); 343 | 344 | if evaluted_res.0.is_some() && evaluted_res.1.is_none() { 345 | atom_list.push(evaluted_res.0.unwrap()); 346 | } else if evaluted_res.1.is_some() && evaluted_res.0.is_none() { 347 | atom_list.append(&mut evaluted_res.1.unwrap()); 348 | } 349 | } 350 | 351 | if atom_list.is_empty() { 352 | (None, None) 353 | } else { 354 | let clone_of_atom_list = atom_list.clone(); 355 | let current_operator: Vec = atom_list.drain(..1).collect(); 356 | 357 | match ¤t_operator[0] { 358 | Atom::Number(_) => { 359 | return (None, Some(clone_of_atom_list)); 360 | } 361 | Atom::Sym(symbol) => { 362 | if OPERATORS.contains(&symbol.lexeme.as_str()) { 363 | return ( 364 | Some(do_binary_operation(symbol, atom_list, &self.source)), 365 | None, 366 | ); 367 | //we only need to check if it is a assignment expression or not; 368 | //because the value has already been extracted above [^ref-1] 369 | //or an error has been thrown 370 | } else if is_this_assignment(symbol) { 371 | //check if assignment; mew number with `=` 372 | if atom_list.is_empty() { 373 | no_expression_after_id(symbol, &self.source, true); 374 | /* 375 | self.show_nice_error( 376 | symbol, 377 | "No expression provided after identifier to assign to it." 378 | .to_string(), 379 | ) 380 | ;*/ 381 | //exit(1); 382 | } else { 383 | //self.do_assignment(symbol, &atom_list, symbol_table); 384 | return ( 385 | Some(Atom::Number(self.do_assignment( 386 | symbol, 387 | &atom_list, 388 | symbol_table, 389 | ))), 390 | None, 391 | ); //return zero as like lisp; everything is an expression 392 | } 393 | } else { 394 | unknown_atom(symbol, &self.source, true); 395 | /* 396 | self.show_nice_error( 397 | symbol, 398 | "Unexpected Atom; I don't know, what to do with this!" 399 | .to_string(), 400 | ); 401 | */ 402 | } 403 | } 404 | } 405 | 406 | self.evaluate(&mut expr_list[0], symbol_table) 407 | } 408 | } 409 | } 410 | } 411 | 412 | #[allow(unused_assignments)] 413 | fn evaluate( 414 | &mut self, 415 | exp: &mut Expr, 416 | symbol_table: &mut HashMap, 417 | ) -> (Option, Option>) { 418 | //let p = exp.clone(); 419 | match exp { 420 | Expr::Atom(atom) => self.evaluate_atom_expr(atom, symbol_table), 421 | 422 | Expr::List(expr_list) => self.evaluate_list_expr(expr_list, symbol_table), 423 | } 424 | } 425 | 426 | fn do_assignment( 427 | &self, 428 | identifer: &MewToken, 429 | atom: &[Atom], 430 | symbol_table: &mut HashMap, 431 | ) -> f64 { 432 | // the argument we got will be something like `=mewmew` so, what we have to is convert it 433 | // to something like `~mewmew` , so it can be found on the symbol table later; 434 | let mut p_id: Vec = identifer.lexeme.chars().map(|c| c.to_string()).collect(); 435 | p_id[0] = "~".to_string(); 436 | let id = p_id.join(""); 437 | 438 | //nice_error_atom_list(&atom, &self.source, "error list".to_string(), false); 439 | let mut value: f64 = 0.0; 440 | if atom.len() > 1 { 441 | // What is happening here is => 442 | // [=mew [mew mew mewmew]] 443 | // we have to assign `[mew mew mew]` to `~mew` 444 | // but the expression list has no function/operator 445 | // so first, we convert the expression to something like this `[1 2 3]` 446 | // then convert it to a string "123" then parse it as float; 447 | // finally assign it to `~mew` 448 | 449 | //let extracted_atom_list: Vec> = 450 | // atom.into_iter().map(|a| self.extract_atom(a)).collect(); 451 | 452 | let x = atom 453 | .iter() 454 | .map(|a| extract_atom(a, &self.source)) 455 | .into_iter() 456 | .flatten() 457 | .map(|a| a.to_string()) 458 | .collect::>(); 459 | let temp_value = x.join("").parse::(); 460 | 461 | match temp_value { 462 | Ok(v) => value = v, 463 | Err(_) => { 464 | //eprintln!( 465 | // "Failed to join expression list and create a single value for assignment" 466 | //); 467 | // 468 | // 469 | expresion_combine_failed(identifer, &self.source, true); 470 | //exit(1); 471 | } 472 | } 473 | } else if let Atom::Number(n) = atom[0].to_owned() { 474 | value = n; 475 | } 476 | 477 | symbol_table.insert(id, value); 478 | value 479 | } 480 | } 481 | -------------------------------------------------------------------------------- /src/mewl/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod errors; 2 | pub mod eval_helpers; 3 | pub mod evaluator; 4 | pub mod parser; 5 | pub mod types; 6 | -------------------------------------------------------------------------------- /src/mewl/parser.rs: -------------------------------------------------------------------------------- 1 | use std::process::exit; 2 | 3 | use crate::mewl::evaluator::MewlEvaluator; 4 | use crate::mewl::types::*; 5 | 6 | use super::errors::{no_closing_bracket, unexpected_closing_bracket}; 7 | use super::eval_helpers::atomic::convert_from_mewnum; 8 | use super::eval_helpers::mewcheck::is_this_mewnum; 9 | 10 | #[allow(dead_code)] 11 | pub struct MewlParser { 12 | source: String, //Source string as is; could've used a simple vector but this is String for now for the show_nice_error() function to work properly 13 | tokens: Vec, // Raw tokens as strings; 14 | current_atom: Atom, // not used for anything as of now; 15 | } 16 | 17 | impl MewlParser { 18 | pub fn new(mut source: String) -> Self { 19 | //let source_code = source.clone(); 20 | source.push('\n'); 21 | source.push(' '); // without these last statements are not being parser; TODO: Fix this, if possible 22 | 23 | Self { 24 | source: source.replace('[', " [ ").replace(']', " ] "), // adding space between parens to make parsing easier 25 | //TODO: Support all bracket types -> () {} [] 26 | tokens: Vec::new(), 27 | current_atom: Atom::Number(0.0), 28 | } 29 | } 30 | 31 | fn poktoken(&self, tokens: &mut Vec) -> MewToken { 32 | tokens.drain(..1).next().unwrap() 33 | } 34 | 35 | fn get_tokens(&self) -> Vec { 36 | let raw_toks: Vec = self.source.chars().collect(); 37 | // println!("{:?}" , raw_toks); 38 | let mut output: Vec = vec![]; 39 | let mut curp: usize = 0; //current position of of reader 40 | let mut curtok: String = String::new(); //current token; blank at first and later filled 41 | let mut line_no: usize = 1; //current line number; for the function show_nice_error() 42 | while curp < raw_toks.len() { 43 | // The below hack feels a little complex; 44 | // Skip whitespaces and tabs 45 | // as soon as we find a non-space char; we start pushing the next chars 46 | // to the `curtok` variable [continued...] 47 | 48 | // WHITESPACE skipping loop 49 | while raw_toks[curp] != ' ' && raw_toks[curp] != '\t' { 50 | if raw_toks[curp] == '\n' { 51 | line_no += 1; 52 | curp += 1; 53 | continue; 54 | } 55 | 56 | // if we find `//` anywhere, all the tokens are skipped unless we find a newline 57 | if raw_toks[curp] == '/' && raw_toks[curp + 1] == '/' { 58 | line_no += 1; //increment the line index; 59 | curp += 2; //skip the comment chars 60 | while raw_toks[curp] != '\n' { 61 | //skip everything until we find a newline 62 | curp += 1; 63 | } 64 | curp += 1; //skip the newline; 65 | continue; 66 | } 67 | 68 | curtok.push(raw_toks[curp]); //if the current char is not; the char is pushed to `curtok` 69 | curp += 1; 70 | } 71 | 72 | // END of WHITESPACE skipping loop 73 | 74 | // if we reached here; that means we have found a whitespace or tab char; 75 | // so now we should a filled `curtok` 76 | // we prepare a `Token` with position data and push the final token to the `output` variable 77 | if !curtok.is_empty() { 78 | let temp_token = MewToken { 79 | lexeme: curtok.clone(), 80 | position: (line_no, (curp - curtok.len(), curp)), 81 | }; 82 | output.push(temp_token); 83 | curtok = String::new(); 84 | } 85 | 86 | curp += 1; 87 | //we now have to advance; 88 | //I am not sure about this; 89 | } 90 | 91 | output 92 | } 93 | 94 | pub fn parse(&mut self, evaluate: bool) -> Option { 95 | let mut mytoks = self.get_tokens(); 96 | let mut token_list: Vec = vec![]; 97 | while !mytoks.is_empty() { 98 | token_list.push(self.parse_raw_tokens(&mut mytoks)); 99 | } 100 | //let mut st: HashMap = HashMap::new(); 101 | //st.insert("~mew".to_string(), 3.0); 102 | // 103 | if evaluate { 104 | let mut evaluator = 105 | MewlEvaluator::new(Expr::List(token_list.clone()), self.source.clone()); 106 | evaluator.do_eval(); 107 | } 108 | 109 | Some(Expr::List(token_list)) 110 | //let _ = self.evaluate(&mut Expr::List(token_list), &mut st); 111 | //println!("{:?}" , a); 112 | //println!("{:#?}" , token_list); 113 | //token_list 114 | } 115 | 116 | fn parse_raw_tokens(&mut self, raw_tokens: &mut Vec) -> Expr { 117 | //println!("{:?}" , raw_tokens); 118 | let current_token = self.poktoken(raw_tokens); 119 | if raw_tokens.is_empty() { 120 | eprintln!("No expression to parse"); 121 | } 122 | 123 | match current_token.lexeme.as_str() { 124 | "[" => { 125 | //`println!("{:?}" , raw_tokens); 126 | let mut output_tokens: Vec = vec![]; 127 | while raw_tokens[0].lexeme != *"]" { 128 | // if raw_tokens.first().unwrap().lexeme != *"]"{ 129 | 130 | output_tokens.push(self.parse_raw_tokens(raw_tokens)); 131 | /* } 132 | else if current_token.lexeme == *"]" { 133 | self.show_nice_error(¤t_token, "[ found".to_string()); 134 | exit(1); 135 | } 136 | 137 | //self.poktoken(raw_tokens); 138 | */ 139 | if raw_tokens.is_empty() { 140 | // if tokens are empty; that might suggest that some brackets are still open; so error! 141 | no_closing_bracket(¤t_token, &self.source, false); 142 | exit(1); 143 | } 144 | } 145 | 146 | //println!("{:?}" , raw_tokens); 147 | if !raw_tokens.is_empty() { 148 | self.poktoken(raw_tokens); 149 | } 150 | let output: Expr = Expr::List(output_tokens); 151 | output 152 | } 153 | "]" => { 154 | //self.show_nice_error(¤t_token, "Unexpected Closing bracket!".to_string()); 155 | unexpected_closing_bracket(¤t_token, &self.source, false); 156 | exit(1); 157 | } 158 | _ => { 159 | let out: Expr = Expr::Atom(self.parse_raw_atom(¤t_token)); 160 | out 161 | } 162 | } 163 | } 164 | 165 | fn parse_raw_atom(&self, token: &MewToken) -> Atom { 166 | //println!("<<<<<<<|{}|=>>>>>>{:?}" , token.lexeme , self.is_mewnum(token)); 167 | if is_this_mewnum(token) { 168 | //TODO: Fix 169 | return Atom::Number(convert_from_mewnum(token.lexeme.as_str())); 170 | } 171 | 172 | Atom::Sym(token.to_owned()) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/mewl/types.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub enum Atom { 3 | Sym(MewToken), 4 | Number(f64), 5 | } 6 | 7 | #[derive(Debug, Clone)] 8 | pub enum Expr { 9 | Atom(Atom), 10 | List(Vec), 11 | } 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct MewToken { 15 | pub lexeme: String, 16 | pub position: (usize, (usize, usize)), //[line number, [start position, end position ]] 17 | } 18 | 19 | pub const OPERATORS: [&str; 28] = [ 20 | "+", //Addition 21 | "-", //Substraction 22 | "*", //Multiplication 23 | "/", //Division 24 | "%", //mod 25 | "::", //Print as is (Array or just single element) 26 | ":::", //print as char 27 | ">", // A > B ==> A is greater than B 28 | "<", // A < B ==> A is less than B 29 | "==", // A == B ==> A is equal to B 30 | "!=", // A != B ==> A is not equal to B 31 | "<=", // A < = B ==> A is less than equal to B 32 | ">=", // A > = B ==> A is greater than equal to B 33 | "@", // Loop / While Loop 34 | "?", // If statemet 35 | "&", // Simple True/False AND Operation 36 | "#", // Simple True/False OR Operation 37 | "!", // Simple True/False NOT Operation 38 | "^", // Bitwise XOR 39 | "**", // Power 40 | ">>", // Bitwise Right Shift 41 | "<<", // Bitwise Left Shift 42 | "!!", // Bitwise NOT 43 | "##", // Bitwise OR 44 | "&&", //Bitwise AND 45 | "|>", // take input 46 | "||>", // take input; return the length 47 | "'", // Join atoms 48 | ]; 49 | -------------------------------------------------------------------------------- /tests/addition_test.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | #[test] 4 | fn with_multiplication_test() { 5 | assert_eq!(2.0, common::get_return_value("[+ mew [* mew mew]]")); 6 | assert_eq!( 7 | 10.0, 8 | common::get_return_value("[* mewmew [+ mew [* mewmew mewmew] ]]") 9 | ); 10 | assert_eq!(5.0, common::get_return_value("[+ mew [* mewmew mewmew]]")); 11 | } 12 | 13 | #[test] 14 | fn addition_test() { 15 | assert_eq!( 16 | 13.0, 17 | common::get_return_value("[+ mewmew.mew.mewmew [' mew mew]]").floor() 18 | ); 19 | assert_eq!( 20 | 112.0, 21 | common::get_return_value("[+ mew [' mew [' mew mew]]]") 22 | ); 23 | assert_eq!(112.0, common::get_return_value("[+ mew [' mew mew mew]]")); 24 | assert_eq!(3.0, common::get_return_value("[+ [+ [mew mew]] mew]")); 25 | assert_eq!(3.0, common::get_return_value("[+ mew mew mew]")); 26 | assert_eq!(2.0, common::get_return_value("[+ mew mew]")) 27 | } 28 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | use mewl::mewl::evaluator::MewlEvaluator; 2 | use mewl::mewl::parser::MewlParser; 3 | use mewl::mewl::types::Atom; 4 | 5 | pub fn get_return_value(source: &str) -> f64 { 6 | let parser = MewlParser::new(source.to_string()).parse(false); 7 | 8 | let m = MewlEvaluator::new(parser.unwrap(), source.to_string()).do_eval(); 9 | let result = if m.0.is_some() && m.1.is_none() { 10 | match m.0.unwrap() { 11 | Atom::Number(n) => n, 12 | _ => panic!(), 13 | } 14 | } else if m.1.is_some() && m.0.is_none() { 15 | println!("{:?}", m.1); 16 | match m.1.unwrap()[0] { 17 | Atom::Number(n) => n, 18 | _ => panic!(), 19 | } 20 | } else { 21 | 0.0 22 | }; 23 | 24 | result 25 | 26 | //println!("`{}` should be => {}" , source , 2.0); 27 | //assert_eq!(2.0_f64 , result); 28 | } 29 | --------------------------------------------------------------------------------