├── .github └── workflows │ ├── book_cd.yml │ └── rust_ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── examples ├── 1_project │ ├── ham.yml │ └── src │ │ └── main.ham ├── demo.ham ├── errors.ham ├── external.ham ├── loop.ham ├── module.ham └── pointer.ham ├── ham_book ├── Cargo.toml ├── book.toml └── src │ ├── SUMMARY.md │ ├── contributing.md │ ├── introduction.md │ ├── introduction │ ├── installing.md │ └── usage.md │ └── primitive_types.md ├── ham_core ├── Cargo.lock ├── Cargo.toml ├── readme.md └── src │ ├── ast_types.rs │ ├── ast_types │ ├── ast_base.rs │ ├── boxed_val.rs │ ├── break_ast.rs │ ├── expression.rs │ ├── fn_call.rs │ ├── fn_def.rs │ ├── if_ast.rs │ ├── module.rs │ ├── reference.rs │ ├── result.rs │ ├── return_ast.rs │ ├── var_assign.rs │ ├── var_def.rs │ └── while_block.rs │ ├── lib.rs │ ├── primitive_values.rs │ ├── primitive_values │ ├── boolean.rs │ ├── number.rs │ ├── pointer.rs │ ├── primitive_base.rs │ └── string.rs │ ├── runtime.rs │ ├── stack.rs │ ├── types.rs │ └── utils.rs ├── ham_manager ├── Cargo.toml ├── readme.md └── src │ └── lib.rs ├── license.md ├── readme.md ├── rustfmt.toml ├── src └── main.rs └── tests ├── manifest.rs └── tokenizer.rs /.github/workflows/book_cd.yml: -------------------------------------------------------------------------------- 1 | name: book cd 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Setup mdBook 16 | uses: peaceiris/actions-mdbook@v1 17 | with: 18 | mdbook-version: '0.4.10' 19 | 20 | - run: mdbook build ham_book 21 | 22 | - name: Deploy 23 | uses: peaceiris/actions-gh-pages@v3 24 | with: 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | publish_dir: ./ham_book/book -------------------------------------------------------------------------------- /.github/workflows/rust_ci.yml: -------------------------------------------------------------------------------- 1 | name: rust_ci 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@v2 19 | - name: Install latest nightly 20 | uses: actions-rs/toolchain@v1 21 | with: 22 | toolchain: nightly 23 | override: true 24 | 25 | - name: Build 26 | uses: actions-rs/cargo@v1 27 | with: 28 | command: build 29 | 30 | - name: Run tests 31 | uses: actions-rs/cargo@v1 32 | with: 33 | command: test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | *.iml 4 | .vscode 5 | ham_book/book -------------------------------------------------------------------------------- /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 = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "atty" 16 | version = "0.2.14" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 19 | dependencies = [ 20 | "hermit-abi", 21 | "libc", 22 | "winapi", 23 | ] 24 | 25 | [[package]] 26 | name = "autocfg" 27 | version = "1.0.1" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 30 | 31 | [[package]] 32 | name = "bitflags" 33 | version = "1.2.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 36 | 37 | [[package]] 38 | name = "cfg-if" 39 | version = "1.0.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 42 | 43 | [[package]] 44 | name = "clap" 45 | version = "3.0.0-beta.2" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142" 48 | dependencies = [ 49 | "atty", 50 | "bitflags", 51 | "clap_derive", 52 | "indexmap", 53 | "lazy_static", 54 | "os_str_bytes", 55 | "strsim", 56 | "termcolor", 57 | "textwrap", 58 | "unicode-width", 59 | "vec_map", 60 | ] 61 | 62 | [[package]] 63 | name = "clap_derive" 64 | version = "3.0.0-beta.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1" 67 | dependencies = [ 68 | "heck", 69 | "proc-macro-error", 70 | "proc-macro2", 71 | "quote", 72 | "syn", 73 | ] 74 | 75 | [[package]] 76 | name = "colored" 77 | version = "2.0.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" 80 | dependencies = [ 81 | "atty", 82 | "lazy_static", 83 | "winapi", 84 | ] 85 | 86 | [[package]] 87 | name = "dyn-clone" 88 | version = "1.0.4" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" 91 | 92 | [[package]] 93 | name = "erased-serde" 94 | version = "0.3.16" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "3de9ad4541d99dc22b59134e7ff8dc3d6c988c89ecd7324bf10a8362b07a2afa" 97 | dependencies = [ 98 | "serde", 99 | ] 100 | 101 | [[package]] 102 | name = "getrandom" 103 | version = "0.2.3" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 106 | dependencies = [ 107 | "cfg-if", 108 | "libc", 109 | "wasi", 110 | ] 111 | 112 | [[package]] 113 | name = "ham" 114 | version = "0.0.2" 115 | dependencies = [ 116 | "clap", 117 | "ham_core", 118 | "ham_manager", 119 | "question", 120 | "serde_json", 121 | ] 122 | 123 | [[package]] 124 | name = "ham_core" 125 | version = "0.0.2" 126 | dependencies = [ 127 | "colored", 128 | "dyn-clone", 129 | "erased-serde", 130 | "regex", 131 | "serde", 132 | "serde_json", 133 | "uuid", 134 | ] 135 | 136 | [[package]] 137 | name = "ham_manager" 138 | version = "0.0.1" 139 | dependencies = [ 140 | "yaml-rust", 141 | ] 142 | 143 | [[package]] 144 | name = "hashbrown" 145 | version = "0.9.1" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 148 | 149 | [[package]] 150 | name = "heck" 151 | version = "0.3.3" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 154 | dependencies = [ 155 | "unicode-segmentation", 156 | ] 157 | 158 | [[package]] 159 | name = "hermit-abi" 160 | version = "0.1.18" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 163 | dependencies = [ 164 | "libc", 165 | ] 166 | 167 | [[package]] 168 | name = "indexmap" 169 | version = "1.6.2" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" 172 | dependencies = [ 173 | "autocfg", 174 | "hashbrown", 175 | ] 176 | 177 | [[package]] 178 | name = "itoa" 179 | version = "0.4.7" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 182 | 183 | [[package]] 184 | name = "lazy_static" 185 | version = "1.4.0" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 188 | 189 | [[package]] 190 | name = "libc" 191 | version = "0.2.97" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" 194 | 195 | [[package]] 196 | name = "linked-hash-map" 197 | version = "0.5.4" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 200 | 201 | [[package]] 202 | name = "memchr" 203 | version = "2.4.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" 206 | 207 | [[package]] 208 | name = "os_str_bytes" 209 | version = "2.4.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" 212 | 213 | [[package]] 214 | name = "proc-macro-error" 215 | version = "1.0.4" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 218 | dependencies = [ 219 | "proc-macro-error-attr", 220 | "proc-macro2", 221 | "quote", 222 | "syn", 223 | "version_check", 224 | ] 225 | 226 | [[package]] 227 | name = "proc-macro-error-attr" 228 | version = "1.0.4" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 231 | dependencies = [ 232 | "proc-macro2", 233 | "quote", 234 | "version_check", 235 | ] 236 | 237 | [[package]] 238 | name = "proc-macro2" 239 | version = "1.0.27" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" 242 | dependencies = [ 243 | "unicode-xid", 244 | ] 245 | 246 | [[package]] 247 | name = "question" 248 | version = "0.2.2" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "acbb3ede7a8f9a8ab89e714637f2cf40001b58f21340d4242b2f11533e65fa8d" 251 | 252 | [[package]] 253 | name = "quote" 254 | version = "1.0.9" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 257 | dependencies = [ 258 | "proc-macro2", 259 | ] 260 | 261 | [[package]] 262 | name = "regex" 263 | version = "1.5.4" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 266 | dependencies = [ 267 | "aho-corasick", 268 | "memchr", 269 | "regex-syntax", 270 | ] 271 | 272 | [[package]] 273 | name = "regex-syntax" 274 | version = "0.6.25" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 277 | 278 | [[package]] 279 | name = "ryu" 280 | version = "1.0.5" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 283 | 284 | [[package]] 285 | name = "serde" 286 | version = "1.0.126" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" 289 | dependencies = [ 290 | "serde_derive", 291 | ] 292 | 293 | [[package]] 294 | name = "serde_derive" 295 | version = "1.0.126" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" 298 | dependencies = [ 299 | "proc-macro2", 300 | "quote", 301 | "syn", 302 | ] 303 | 304 | [[package]] 305 | name = "serde_json" 306 | version = "1.0.64" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" 309 | dependencies = [ 310 | "itoa", 311 | "ryu", 312 | "serde", 313 | ] 314 | 315 | [[package]] 316 | name = "strsim" 317 | version = "0.10.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 320 | 321 | [[package]] 322 | name = "syn" 323 | version = "1.0.73" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" 326 | dependencies = [ 327 | "proc-macro2", 328 | "quote", 329 | "unicode-xid", 330 | ] 331 | 332 | [[package]] 333 | name = "termcolor" 334 | version = "1.1.2" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 337 | dependencies = [ 338 | "winapi-util", 339 | ] 340 | 341 | [[package]] 342 | name = "textwrap" 343 | version = "0.12.1" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789" 346 | dependencies = [ 347 | "unicode-width", 348 | ] 349 | 350 | [[package]] 351 | name = "unicode-segmentation" 352 | version = "1.7.1" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 355 | 356 | [[package]] 357 | name = "unicode-width" 358 | version = "0.1.8" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 361 | 362 | [[package]] 363 | name = "unicode-xid" 364 | version = "0.2.2" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 367 | 368 | [[package]] 369 | name = "uuid" 370 | version = "0.8.2" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" 373 | dependencies = [ 374 | "getrandom", 375 | ] 376 | 377 | [[package]] 378 | name = "vec_map" 379 | version = "0.8.2" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 382 | 383 | [[package]] 384 | name = "version_check" 385 | version = "0.9.3" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 388 | 389 | [[package]] 390 | name = "wasi" 391 | version = "0.10.2+wasi-snapshot-preview1" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 394 | 395 | [[package]] 396 | name = "winapi" 397 | version = "0.3.9" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 400 | dependencies = [ 401 | "winapi-i686-pc-windows-gnu", 402 | "winapi-x86_64-pc-windows-gnu", 403 | ] 404 | 405 | [[package]] 406 | name = "winapi-i686-pc-windows-gnu" 407 | version = "0.4.0" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 410 | 411 | [[package]] 412 | name = "winapi-util" 413 | version = "0.1.5" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 416 | dependencies = [ 417 | "winapi", 418 | ] 419 | 420 | [[package]] 421 | name = "winapi-x86_64-pc-windows-gnu" 422 | version = "0.4.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 425 | 426 | [[package]] 427 | name = "yaml-rust" 428 | version = "0.4.5" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 431 | dependencies = [ 432 | "linked-hash-map", 433 | ] 434 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ham" 3 | version = "0.0.2" 4 | edition = "2018" 5 | authors = ["Marc Espín Sanz"] 6 | 7 | [workspace] 8 | members = [ 9 | "ham_core", 10 | "ham_manager" 11 | ] 12 | excludes = [ 13 | "ham_book" 14 | ] 15 | 16 | [dependencies] 17 | clap = "3.0.0-beta.2" 18 | serde_json = "1.0.64" 19 | question = "0.2.2" 20 | ham_core = { path = "ham_core"} 21 | ham_manager = { path = "ham_manager"} -------------------------------------------------------------------------------- /examples/1_project/ham.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.0 -------------------------------------------------------------------------------- /examples/1_project/src/main.ham: -------------------------------------------------------------------------------- 1 | let a = 5 2 | let b = &a 3 | 4 | if a == b { 5 | print(1) 6 | } 7 | 8 | a = 1 9 | 10 | if a == b { 11 | print(2) 12 | } 13 | 14 | &b = 7 15 | 16 | if a == b { 17 | print(3) 18 | } -------------------------------------------------------------------------------- /examples/demo.ham: -------------------------------------------------------------------------------- 1 | fn until(x){ 2 | let c = 0 3 | while c != x { 4 | println(c) 5 | if c == 2 { 6 | break 7 | } 8 | c.mut_sum(1) 9 | } 10 | return c 11 | } 12 | 13 | println(format("result is -> {}", until(5))) 14 | 15 | fn mod(x) { 16 | if x == 5 { 17 | return 0 18 | } 19 | x.mut_sum(1) 20 | println(format("Value is -> {}", x)) 21 | return mod(x) 22 | } 23 | 24 | let val = mod(0) 25 | 26 | fn o() { 27 | return 10 28 | } 29 | 30 | let val = o().sum(1).sum(1).sum(3) 31 | 32 | // 15 33 | println(val) 34 | 35 | // 20 36 | println(val.sum(3).sum(2)) 37 | 38 | 39 | 40 | 41 | let n = 0 42 | 43 | fn lol(arg1 arg2){ 44 | println(format("A: {}, B: {}", arg1, arg2)) 45 | } 46 | 47 | lol( 48 | n.sum(1), 49 | n.sum(2) 50 | ) 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /examples/errors.ham: -------------------------------------------------------------------------------- 1 | 2 | // 5. Module not found 3 | import e from "./wow.ham" 4 | 5 | // 6. Unexpected keyword 6 | import c blup "./module.ham" 7 | 8 | fn invalid(){ 9 | let val = 6 10 | return &val 11 | } 12 | 13 | // 3. Broken argument (Variable not found) 14 | fn foo(x){ 15 | 16 | // 4. Broken pointer 17 | invalid(x) 18 | fee() 19 | return 5 20 | } 21 | 22 | // 2. Unused returned value 23 | foo() 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/external.ham: -------------------------------------------------------------------------------- 1 | 2 | fn test(){ 3 | import module from "./module.ham" 4 | 5 | module.x("World") 6 | } 7 | 8 | test() 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/loop.ham: -------------------------------------------------------------------------------- 1 | fn test(n){ 2 | println("") 3 | println(format("Starting from {}...", n)) 4 | while n != 5 { 5 | println(format("Value is {}", n)) 6 | n.mut_sum(1) 7 | } 8 | } 9 | 10 | test(0) 11 | test(4) 12 | -------------------------------------------------------------------------------- /examples/module.ham: -------------------------------------------------------------------------------- 1 | 2 | fn x(text){ 3 | print(format("Hello {}", text)) 4 | } -------------------------------------------------------------------------------- /examples/pointer.ham: -------------------------------------------------------------------------------- 1 | 2 | let pointer = 0 3 | 4 | fn mod_pointer(val){ 5 | val = 9 6 | } 7 | 8 | // Pass it as pointer 9 | mod_pointer(&pointer) 10 | 11 | // This prints 9 12 | println(pointer) 13 | -------------------------------------------------------------------------------- /ham_book/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ham_book" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /ham_book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Marc Espín Sanz"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Ham's book" 7 | -------------------------------------------------------------------------------- /ham_book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](./introduction.md) 4 | - [Install](./introduction/installing.md) 5 | - [Usage](./introduction/usage.md) 6 | - [Primitive Types](./primitive_types.md) 7 | - [Contributing](./contributing.md) 8 | -------------------------------------------------------------------------------- /ham_book/src/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Compiling: 4 | ```shell 5 | cargo build --release 6 | ``` 7 | 8 | Linting: 9 | ```shell 10 | cargo clippy 11 | ``` 12 | 13 | Formatting: 14 | ```shell 15 | cargo fmt 16 | ``` 17 | 18 | Testing: 19 | ```shell 20 | cargo test 21 | ``` 22 | 23 | Install mdbook: 24 | ```shell 25 | cargo install mdbook 26 | ``` 27 | 28 | Build the book: 29 | ```shell 30 | mdbook build 31 | ``` 32 | 33 | Watch for changes on the book: 34 | ```shell 35 | mdbook watch 36 | ``` 37 | 38 | Running directly: 39 | ```shell 40 | cargo run -- run examples/demo.ham 41 | ``` -------------------------------------------------------------------------------- /ham_book/src/introduction.md: -------------------------------------------------------------------------------- 1 | # 🍖 ham 2 | 3 | ham is a general purpose language. It is heavily inspired by Rust and TypeScript. 4 | 5 | Wanna try it out? [Install it](./introduction/installing.md) 6 | 7 | ### Goals 8 | - Speed 9 | - Security 10 | - Comfort 11 | 12 | ### Example 13 | ```rust 14 | fn calc(value){ 15 | // If the value is 5 end the function 16 | if value == 5 { 17 | return 0 18 | } 19 | 20 | // Add 1 21 | value.mut_sum(1) 22 | 23 | // Print it's value 24 | println(format("Value is {}", value)) 25 | 26 | // Call the function again with the latest value 27 | return calc(value) 28 | } 29 | 30 | // This will print from `Value is 1` to `Value is 5` 31 | let _ = calc(0) 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /ham_book/src/introduction/installing.md: -------------------------------------------------------------------------------- 1 | 2 | You can download `ham`'s interpreter (around 2mb or 5mb) from it's repository on GitHub [here](https://github.com/marc2332/ham/releases). 3 | 4 | Then, you should add it to your PATH environment varible. -------------------------------------------------------------------------------- /ham_book/src/introduction/usage.md: -------------------------------------------------------------------------------- 1 | Ham's interpreter has a built-in [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) 2 | 3 | Open a terminal and type: 4 | ```shell 5 | ham repl 6 | ``` 7 | 8 | You can also run standalone files: 9 | Run files: 10 | ```shell 11 | ham run examples/demo.ham 12 | ``` 13 | 14 | Or simply run a project (This will run `1_project/src/main.ham` automatically): 15 | ```shell 16 | ham run examples/1_project 17 | ``` -------------------------------------------------------------------------------- /ham_book/src/primitive_types.md: -------------------------------------------------------------------------------- 1 | Ham comes has a few primitive types: 2 | 3 | - String 4 | ```ts 5 | let text = "Hello World" 6 | ``` 7 | - Number 8 | ```ts 9 | let number = 1234 10 | ``` 11 | - Boolean 12 | ```ts 13 | let is_cool = true 14 | ``` -------------------------------------------------------------------------------- /ham_core/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 = "ham_core" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /ham_core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ham_core" 3 | version = "0.0.2" 4 | edition = "2018" 5 | authors = ["Marc Espín Sanz"] 6 | 7 | [dependencies] 8 | regex = { version = "1", features = ["pattern"] } 9 | dyn-clone = "1.0.4" 10 | erased-serde = "0.3" 11 | uuid = { version = "0.8", features = ["v4"] } 12 | serde = { version = "1.0.126", features = ["derive"] } 13 | serde_json = "1.0.64" 14 | colored = "2.0.0" -------------------------------------------------------------------------------- /ham_core/readme.md: -------------------------------------------------------------------------------- 1 | This crate includes: 2 | - Tokenizer 3 | - Ast tree generator 4 | - Runtime core 5 | - Runtime APIs (println(), wait(), etc..) -------------------------------------------------------------------------------- /ham_core/src/ast_types.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | primitive_values::{ 3 | boolean::{ 4 | Boolean, 5 | BooleanValueBase, 6 | }, 7 | number::{ 8 | Number, 9 | NumberValueBase, 10 | }, 11 | string::{ 12 | StringVal, 13 | StringValueBase, 14 | }, 15 | }, 16 | types::{ 17 | IndexedTokenList, 18 | Token, 19 | TokensList, 20 | }, 21 | utils::{ 22 | Directions, 23 | Ops, 24 | }, 25 | }; 26 | 27 | use self::{ 28 | boxed_val::BoxedValue, 29 | expression::{ 30 | Expression, 31 | ExpressionBase, 32 | }, 33 | fn_call::{ 34 | FnCall, 35 | FnCallBase, 36 | }, 37 | reference::{ 38 | Reference, 39 | ReferenceValueBase, 40 | }, 41 | result::{ 42 | ResultExpression, 43 | ResultExpressionBase, 44 | }, 45 | }; 46 | 47 | pub mod ast_base; 48 | pub mod boxed_val; 49 | pub mod break_ast; 50 | pub mod expression; 51 | pub mod fn_call; 52 | pub mod fn_def; 53 | pub mod if_ast; 54 | pub mod module; 55 | pub mod reference; 56 | pub mod result; 57 | pub mod return_ast; 58 | pub mod var_assign; 59 | pub mod var_def; 60 | pub mod while_block; 61 | 62 | use return_ast::ReturnStatement; 63 | 64 | /* 65 | * Get all the tokens with index starting on `from` until a token matches its type to `to` 66 | */ 67 | pub fn get_tokens_from_to_fn( 68 | from: usize, 69 | to: Ops, 70 | tokens: TokensList, 71 | direction: Directions, 72 | ) -> IndexedTokenList { 73 | let mut found_tokens = Vec::new(); 74 | 75 | // Init token position 76 | let mut token_n = from; 77 | 78 | match direction { 79 | // Get tokens from left to right 80 | Directions::LeftToRight => { 81 | while token_n < tokens.len() { 82 | if tokens[token_n].ast_type == to { 83 | break; 84 | } else { 85 | found_tokens.push((token_n, tokens[token_n].clone())) 86 | } 87 | token_n += 1; 88 | } 89 | } 90 | 91 | // Get tokens from right to left 92 | Directions::RightToLeft => { 93 | while token_n > 0 { 94 | if tokens[token_n - 1].ast_type == to { 95 | break; 96 | } else { 97 | found_tokens.push((token_n - 1, tokens[token_n - 1].clone())) 98 | } 99 | token_n -= 1 100 | } 101 | 102 | found_tokens.reverse(); 103 | } 104 | } 105 | 106 | found_tokens 107 | } 108 | 109 | /* 110 | * Get the right AST token given a simple token 111 | */ 112 | pub fn get_assignment_token_fn( 113 | val: String, 114 | token_n: usize, 115 | tokens: TokensList, 116 | direction: Directions, 117 | ) -> (usize, BoxedValue) { 118 | match val.as_str() { 119 | // True boolean 120 | "true" => ( 121 | 1, 122 | BoxedValue { 123 | interface: Ops::Boolean, 124 | value: Box::new(Boolean::new(true)), 125 | }, 126 | ), 127 | // False boolean 128 | "false" => ( 129 | 1, 130 | BoxedValue { 131 | interface: Ops::Boolean, 132 | value: Box::new(Boolean::new(false)), 133 | }, 134 | ), 135 | // Numeric values 136 | val if val.parse::().is_ok() => ( 137 | 1, 138 | BoxedValue { 139 | interface: Ops::Number, 140 | value: Box::new(Number::new(val.parse::().unwrap())), 141 | }, 142 | ), 143 | // String values 144 | val if val.starts_with('"') && val.ends_with('"') => ( 145 | 1, 146 | BoxedValue { 147 | interface: Ops::String, 148 | value: Box::new(StringVal::new(val.replace('"', ""))), 149 | }, 150 | ), 151 | // References to other values (ej: referencing to a variable) 152 | val => { 153 | if token_n < tokens.len() - 1 { 154 | let next_token = { 155 | match direction { 156 | // Get the next token when reading from left to right 157 | Directions::LeftToRight => tokens[token_n + 1].clone(), 158 | // Get the previous token when reading from right to left 159 | Directions::RightToLeft => tokens[token_n - 1].clone(), 160 | } 161 | }; 162 | 163 | let reference_type = match next_token.ast_type { 164 | // Having a ( or ) means there is a function call 165 | Ops::OpenParent => Ops::FnCall, 166 | Ops::CloseParent => Ops::FnCall, 167 | // Having a . means it's referencing to another value 168 | Ops::PropAccess => Ops::PropAccess, 169 | _ => Ops::Invalid, 170 | }; 171 | 172 | match reference_type { 173 | Ops::PropAccess => { 174 | let after_next_token = tokens[token_n + 2].clone(); 175 | let (size, val) = get_assignment_token_fn( 176 | after_next_token.value, 177 | token_n + 2, 178 | tokens, 179 | Directions::LeftToRight, 180 | ); 181 | 182 | (size + 2, val) 183 | } 184 | /* 185 | * Handle function calls used as assignment value 186 | */ 187 | Ops::FnCall => { 188 | let mut group = Expression::new(); 189 | 190 | // Position where it will be starting getting the argument tokens 191 | let starting_token: usize = { 192 | match direction { 193 | Directions::LeftToRight => token_n + 2, 194 | _ => token_n, 195 | } 196 | }; 197 | 198 | // Get argument tokens 199 | let mut arguments_tokens: Vec<(usize, Token)> = { 200 | match direction { 201 | /* 202 | * Get all the next tokens from the starting token to the first CloseParent token 203 | */ 204 | Directions::LeftToRight => get_tokens_from_to_fn( 205 | starting_token, 206 | Ops::CloseParent, 207 | tokens.clone(), 208 | direction.clone(), 209 | ), 210 | /* 211 | * get all previous the tokens the starting token until an IfConditional token 212 | * [WIP] 213 | */ 214 | Directions::RightToLeft => get_tokens_from_to_fn( 215 | starting_token, 216 | Ops::IfConditional, 217 | tokens.clone(), 218 | direction.clone(), 219 | ), 220 | } 221 | }; 222 | 223 | let mut ast_token = FnCall::new( 224 | { 225 | match direction { 226 | // When reading from left to right, we know current token.value is it's name 227 | Directions::LeftToRight => String::from(val), 228 | 229 | // But when reading from right to left we need to first get all the tokens which are part of the function 230 | Directions::RightToLeft => { 231 | let fn_name = arguments_tokens[0].1.value.clone(); 232 | 233 | // Now we can remove the function name from the arguments token 234 | arguments_tokens.remove(0); 235 | fn_name 236 | } 237 | } 238 | }, 239 | { 240 | if token_n > 0 { 241 | let previous_token = tokens[token_n - 1].clone(); 242 | match previous_token.ast_type { 243 | // Is referencing to a value on the left side 244 | Ops::PropAccess => { 245 | let before_previous_token = tokens[token_n - 2].clone(); 246 | match before_previous_token.ast_type { 247 | // Reference to a variable 248 | Ops::Reference => Some(before_previous_token.value), 249 | // Reference to the result of a function call 250 | Ops::CloseParent => Some("_".to_string()), 251 | _ => None, 252 | } 253 | } 254 | _ => None, 255 | } 256 | } else { 257 | None 258 | } 259 | }, 260 | ); 261 | 262 | // Transfrom the tokens into arguments 263 | ast_token.arguments = convert_tokens_into_arguments( 264 | arguments_tokens 265 | .iter() 266 | .map(|(_, token)| token.clone()) 267 | .collect(), 268 | ); 269 | 270 | let mut left_length = arguments_tokens.len() + 3; 271 | 272 | // Assign a expression to the variable 273 | group.body.push(Box::new(var_def::VarDefinition { 274 | def_name: "_".to_string(), 275 | assignment: BoxedValue { 276 | interface: Ops::FnCall, 277 | value: Box::new(ast_token), 278 | }, 279 | })); 280 | 281 | let mut next_token_n = token_n + left_length; 282 | 283 | if next_token_n < tokens.len() - 1 { 284 | let next_token = &tokens[next_token_n]; 285 | 286 | /* 287 | * If the next token is also referencing to the value on the left 288 | * then add it to the expression block 289 | */ 290 | if next_token.ast_type == Ops::PropAccess { 291 | next_token_n += 1; 292 | let next_token = &tokens[next_token_n]; 293 | 294 | /* 295 | * Get the token next to the PropAccess token 296 | */ 297 | let (length, val) = get_assignment_token_fn( 298 | next_token.value.clone(), 299 | next_token_n, 300 | tokens, 301 | Directions::LeftToRight, 302 | ); 303 | 304 | /* 305 | * Return the token from the expression 306 | */ 307 | group.body.push(Box::new(ReturnStatement { value: val })); 308 | left_length += length; 309 | } else { 310 | /* 311 | * If the next token is no longer a PropAccess 312 | * it means it is not referencing to the function 313 | * call's result 314 | */ 315 | group.body.push(Box::new(ReturnStatement { 316 | value: BoxedValue { 317 | interface: Ops::Reference, 318 | value: Box::new(Reference::new("_".to_string())), 319 | }, 320 | })); 321 | } 322 | } else { 323 | /* 324 | * If there aren't any more tokens then just return a reference to the variable 325 | */ 326 | group.body.push(Box::new(ReturnStatement { 327 | value: BoxedValue { 328 | interface: Ops::Reference, 329 | value: Box::new(Reference::new("_".to_string())), 330 | }, 331 | })); 332 | } 333 | 334 | ( 335 | left_length, 336 | BoxedValue { 337 | interface: Ops::Expression, 338 | value: Box::new(group), 339 | }, 340 | ) 341 | } 342 | _ => ( 343 | 1, 344 | BoxedValue { 345 | interface: Ops::Reference, 346 | value: Box::new(Reference::new(String::from(val))), 347 | }, 348 | ), 349 | } 350 | } else { 351 | ( 352 | 1, 353 | BoxedValue { 354 | interface: Ops::Reference, 355 | value: Box::new(Reference::new(String::from(val))), 356 | }, 357 | ) 358 | } 359 | } 360 | } 361 | } 362 | 363 | /* 364 | * Convert some tokens into function arguments 365 | */ 366 | pub fn convert_tokens_into_arguments(tokens: TokensList) -> Vec { 367 | let mut args = Vec::new(); 368 | 369 | let mut token_n = 0; 370 | 371 | while token_n < tokens.len() { 372 | let token = tokens[token_n].clone(); 373 | 374 | match token.ast_type { 375 | // Ignore ( ) and , 376 | Ops::OpenParent => token_n += 1, 377 | Ops::CloseParent => token_n += 1, 378 | Ops::CommaDelimiter => token_n += 1, 379 | _ => { 380 | let (size, val) = get_assignment_token_fn( 381 | token.value.clone(), 382 | token_n, 383 | tokens.clone(), 384 | Directions::LeftToRight, 385 | ); 386 | 387 | token_n += size + 1; 388 | 389 | args.push(val); 390 | } 391 | } 392 | } 393 | 394 | args 395 | } 396 | 397 | /* 398 | * Convert some tokens into a list of boolean expressions 399 | */ 400 | pub fn convert_tokens_into_res_expressions(tokens: TokensList) -> Vec { 401 | let mut exprs = Vec::new(); 402 | 403 | let mut token_n = 1; 404 | 405 | while token_n < tokens.len() { 406 | let left_token = tokens[token_n - 1].clone(); 407 | let token = tokens[token_n].clone(); 408 | 409 | match token.ast_type { 410 | Ops::EqualCondition | Ops::NotEqualCondition => { 411 | let right_token = tokens[token_n + 1].clone(); 412 | 413 | let left_token = get_assignment_token_fn( 414 | left_token.value.clone(), 415 | token_n, 416 | tokens.clone(), 417 | Directions::RightToLeft, 418 | ); 419 | 420 | let right_token = get_assignment_token_fn( 421 | right_token.value.clone(), 422 | token_n + 1, 423 | tokens.clone(), 424 | Directions::LeftToRight, 425 | ); 426 | 427 | exprs.push(ResultExpression::new( 428 | token.ast_type, 429 | left_token.1.clone(), 430 | right_token.1.clone(), 431 | )); 432 | 433 | token_n += 2; 434 | } 435 | _ => { 436 | token_n += 1; 437 | } 438 | } 439 | } 440 | 441 | exprs 442 | } 443 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/ast_base.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::Ops; 2 | use erased_serde::serialize_trait_object; 3 | use std::any::Any; 4 | 5 | /* 6 | * This is the base for every AST type 7 | */ 8 | pub trait AstBase: dyn_clone::DynClone + erased_serde::Serialize + std::fmt::Debug { 9 | fn get_type(&self) -> Ops; 10 | fn as_self(&self) -> &dyn Any; 11 | } 12 | 13 | dyn_clone::clone_trait_object!(AstBase); 14 | serialize_trait_object!(AstBase); 15 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/boxed_val.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::ast_base::AstBase, 3 | primitive_values::primitive_base::PrimitiveValueBase, 4 | utils::Ops, 5 | }; 6 | use serde::Serialize; 7 | use std::any::Any; 8 | 9 | /* BOXED VALUE */ 10 | 11 | #[derive(Clone, Debug, Serialize)] 12 | pub struct BoxedValue { 13 | pub interface: Ops, 14 | pub value: Box, 15 | } 16 | 17 | impl AstBase for BoxedValue { 18 | fn get_type(&self) -> Ops { 19 | Ops::LeftAssign 20 | } 21 | fn as_self(&self) -> &dyn Any { 22 | self 23 | } 24 | } 25 | 26 | impl PrimitiveValueBase for BoxedValue { 27 | fn as_self(&self) -> &dyn Any { 28 | self 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/break_ast.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::ast_base::AstBase, 3 | utils::Ops, 4 | }; 5 | use serde::Serialize; 6 | use std::any::Any; 7 | 8 | /* 9 | * Break statement 10 | * 11 | * Used to break from while loops 12 | * 13 | */ 14 | 15 | #[derive(Clone, Debug, Serialize)] 16 | pub struct Break(); 17 | 18 | impl AstBase for Break { 19 | fn get_type(&self) -> Ops { 20 | Ops::Break 21 | } 22 | fn as_self(&self) -> &dyn Any { 23 | self 24 | } 25 | } 26 | 27 | pub trait BreakBase { 28 | fn new() -> Self; 29 | } 30 | 31 | impl BreakBase for Break { 32 | fn new() -> Self { 33 | Self() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/expression.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::ast_base::AstBase, 3 | primitive_values::primitive_base::PrimitiveValueBase, 4 | utils::Ops, 5 | }; 6 | use serde::Serialize; 7 | use std::any::Any; 8 | use uuid::Uuid; 9 | 10 | /* EXPRESSION */ 11 | 12 | #[derive(Clone, Debug, Serialize)] 13 | pub struct Expression { 14 | pub body: Vec>, 15 | pub token_type: Ops, 16 | pub expr_id: String, 17 | } 18 | 19 | impl PrimitiveValueBase for Expression { 20 | fn as_self(&self) -> &dyn Any { 21 | self 22 | } 23 | } 24 | 25 | impl AstBase for Expression { 26 | fn get_type(&self) -> Ops { 27 | Ops::Expression 28 | } 29 | fn as_self(&self) -> &dyn Any { 30 | self 31 | } 32 | } 33 | 34 | pub trait ExpressionBase { 35 | fn new() -> Self; 36 | fn from_body(body: Vec>) -> Self; 37 | } 38 | 39 | impl ExpressionBase for Expression { 40 | fn new() -> Self { 41 | Self { 42 | token_type: Ops::Expression, 43 | body: Vec::new(), 44 | expr_id: Uuid::new_v4().to_string(), 45 | } 46 | } 47 | /* 48 | * Create an expression statement from an existing AST body 49 | */ 50 | fn from_body(body: Vec>) -> Self { 51 | Self { 52 | token_type: Ops::Expression, 53 | body, 54 | // TODO: Move away from Uuid 55 | expr_id: Uuid::new_v4().to_string(), 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/fn_call.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | boxed_val::BoxedValue, 5 | }, 6 | primitive_values::primitive_base::PrimitiveValueBase, 7 | utils::Ops, 8 | }; 9 | use serde::Serialize; 10 | use std::any::Any; 11 | 12 | /* FUNCTION CALL */ 13 | 14 | #[derive(Clone, Serialize, Debug)] 15 | pub struct FnCall { 16 | pub token_type: Ops, 17 | pub fn_name: String, 18 | pub arguments: Vec, 19 | pub reference_to: Option, 20 | } 21 | 22 | impl AstBase for FnCall { 23 | fn get_type(&self) -> Ops { 24 | Ops::FnCall 25 | } 26 | fn as_self(&self) -> &dyn Any { 27 | self 28 | } 29 | } 30 | 31 | pub trait FnCallBase { 32 | fn new(fn_name: String, reference_to: Option) -> Self; 33 | } 34 | 35 | impl FnCallBase for FnCall { 36 | fn new(fn_name: String, reference_to: Option) -> Self { 37 | Self { 38 | token_type: Ops::FnCall, 39 | fn_name, 40 | arguments: Vec::new(), 41 | reference_to, 42 | } 43 | } 44 | } 45 | 46 | impl PrimitiveValueBase for FnCall { 47 | fn as_self(&self) -> &dyn Any { 48 | self 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/fn_def.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::ast_base::AstBase, 3 | utils::Ops, 4 | }; 5 | use serde::Serialize; 6 | use std::any::Any; 7 | 8 | /* FUNCTION DEFINITION */ 9 | pub trait FnDefinitionBase { 10 | fn get_def_name(&self) -> String; 11 | fn new(def_name: String, body: Vec>, arguments: Vec) -> Self; 12 | } 13 | 14 | #[derive(Clone, Debug, Serialize)] 15 | pub struct FnDefinition { 16 | pub def_name: String, 17 | pub body: Vec>, 18 | pub arguments: Vec, 19 | } 20 | 21 | impl FnDefinitionBase for FnDefinition { 22 | fn get_def_name(&self) -> String { 23 | self.def_name.clone() 24 | } 25 | fn new(def_name: String, body: Vec>, arguments: Vec) -> Self { 26 | Self { 27 | def_name, 28 | body, 29 | arguments, 30 | } 31 | } 32 | } 33 | 34 | impl AstBase for FnDefinition { 35 | fn get_type(&self) -> Ops { 36 | Ops::FnDef 37 | } 38 | fn as_self(&self) -> &dyn Any { 39 | self 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/if_ast.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | result::ResultExpression, 5 | }, 6 | utils::Ops, 7 | }; 8 | use serde::Serialize; 9 | use std::any::Any; 10 | 11 | /* IF STATEMENT */ 12 | pub trait IfConditionalBase { 13 | fn new(conditions: Vec, body: Vec>) -> Self; 14 | } 15 | 16 | #[derive(Clone, Debug, Serialize)] 17 | pub struct IfConditional { 18 | pub conditions: Vec, 19 | pub body: Vec>, 20 | } 21 | 22 | impl IfConditionalBase for IfConditional { 23 | fn new(conditions: Vec, body: Vec>) -> Self { 24 | Self { conditions, body } 25 | } 26 | } 27 | 28 | impl AstBase for IfConditional { 29 | fn get_type(&self) -> Ops { 30 | Ops::IfConditional 31 | } 32 | fn as_self(&self) -> &dyn Any { 33 | self 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/module.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | fn_def::FnDefinition, 5 | }, 6 | utils::Ops, 7 | }; 8 | use serde::Serialize; 9 | use std::any::Any; 10 | 11 | /* MODULE STATEMENT */ 12 | 13 | #[derive(Clone, Debug, Serialize)] 14 | pub struct Module { 15 | pub name: String, 16 | pub functions: Vec, 17 | } 18 | 19 | impl AstBase for Module { 20 | fn get_type(&self) -> Ops { 21 | Ops::Module 22 | } 23 | fn as_self(&self) -> &dyn Any { 24 | self 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/reference.rs: -------------------------------------------------------------------------------- 1 | use crate::primitive_values::primitive_base::PrimitiveValueBase; 2 | use serde::Serialize; 3 | use std::any::Any; 4 | 5 | /* 6 | * Reference by name 7 | * 8 | * This happens when a variable instead of having an static value, 9 | * it references to another variable by it's name 10 | * 11 | */ 12 | 13 | #[derive(Clone, Debug, Serialize)] 14 | pub struct Reference(pub String); 15 | 16 | // Implement base methods for REFERENCE 17 | impl PrimitiveValueBase for Reference { 18 | fn as_self(&self) -> &dyn Any { 19 | self 20 | } 21 | } 22 | 23 | pub trait ReferenceValueBase { 24 | fn new(val: String) -> Self; 25 | } 26 | 27 | impl ReferenceValueBase for Reference { 28 | fn new(val: String) -> Self { 29 | Self(val) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/result.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | boxed_val::BoxedValue, 5 | }, 6 | utils::Ops, 7 | }; 8 | use serde::Serialize; 9 | use std::any::Any; 10 | 11 | /* RESULT EXPRESSION */ 12 | pub trait ResultExpressionBase { 13 | fn new(relation: Ops, left: BoxedValue, right: BoxedValue) -> Self; 14 | } 15 | 16 | #[derive(Clone, Debug, Serialize)] 17 | pub struct ResultExpression { 18 | pub left: BoxedValue, 19 | pub relation: Ops, 20 | pub right: BoxedValue, 21 | } 22 | 23 | impl ResultExpressionBase for ResultExpression { 24 | fn new(relation: Ops, left: BoxedValue, right: BoxedValue) -> Self { 25 | Self { 26 | left, 27 | relation, 28 | right, 29 | } 30 | } 31 | } 32 | 33 | impl AstBase for ResultExpression { 34 | fn get_type(&self) -> Ops { 35 | Ops::ResExpression 36 | } 37 | fn as_self(&self) -> &dyn Any { 38 | self 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/return_ast.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | boxed_val::BoxedValue, 5 | }, 6 | utils::Ops, 7 | }; 8 | use serde::Serialize; 9 | use std::any::Any; 10 | 11 | /* RETURN STATEMENT */ 12 | 13 | #[derive(Clone, Debug, Serialize)] 14 | pub struct ReturnStatement { 15 | pub value: BoxedValue, 16 | } 17 | 18 | impl AstBase for ReturnStatement { 19 | fn get_type(&self) -> Ops { 20 | Ops::Return 21 | } 22 | fn as_self(&self) -> &dyn Any { 23 | self 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/var_assign.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | boxed_val::BoxedValue, 5 | }, 6 | utils::Ops, 7 | }; 8 | use serde::Serialize; 9 | use std::any::Any; 10 | 11 | /* VARIABLE ASSIGNMENT */ 12 | pub trait VarAssignmentBase { 13 | fn get_def_name(&self) -> String; 14 | fn new(var_name: String, assignment: BoxedValue) -> Self; 15 | } 16 | 17 | #[derive(Clone, Debug, Serialize)] 18 | pub struct VarAssignment { 19 | pub var_name: String, 20 | pub assignment: BoxedValue, 21 | } 22 | 23 | impl VarAssignmentBase for VarAssignment { 24 | fn get_def_name(&self) -> String { 25 | self.var_name.clone() 26 | } 27 | fn new(var_name: String, assignment: BoxedValue) -> Self { 28 | Self { 29 | var_name, 30 | assignment, 31 | } 32 | } 33 | } 34 | 35 | impl AstBase for VarAssignment { 36 | fn get_type(&self) -> Ops { 37 | Ops::VarAssign 38 | } 39 | fn as_self(&self) -> &dyn Any { 40 | self 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/var_def.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | boxed_val::BoxedValue, 5 | }, 6 | utils::Ops, 7 | }; 8 | use serde::Serialize; 9 | use std::any::Any; 10 | 11 | /* VARIABLE DEFINITION */ 12 | pub trait VarDefinitionBase { 13 | fn get_def_name(&self) -> String; 14 | fn new(def_name: String, assignment: BoxedValue) -> Self; 15 | } 16 | 17 | #[derive(Clone, Debug, Serialize)] 18 | pub struct VarDefinition { 19 | pub def_name: String, 20 | pub assignment: BoxedValue, 21 | } 22 | 23 | impl VarDefinitionBase for VarDefinition { 24 | fn get_def_name(&self) -> String { 25 | self.def_name.clone() 26 | } 27 | fn new(def_name: String, assignment: BoxedValue) -> Self { 28 | Self { 29 | def_name, 30 | assignment, 31 | } 32 | } 33 | } 34 | 35 | impl AstBase for VarDefinition { 36 | fn get_type(&self) -> Ops { 37 | Ops::VarDef 38 | } 39 | fn as_self(&self) -> &dyn Any { 40 | self 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ham_core/src/ast_types/while_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | result::ResultExpression, 5 | }, 6 | utils::Ops, 7 | }; 8 | use serde::Serialize; 9 | use std::any::Any; 10 | 11 | /* WHILE BLOCK */ 12 | 13 | #[derive(Clone, Debug, Serialize)] 14 | pub struct While { 15 | pub body: Vec>, 16 | pub conditions: Vec, 17 | } 18 | 19 | impl AstBase for While { 20 | fn get_type(&self) -> Ops { 21 | Ops::WhileDef 22 | } 23 | fn as_self(&self) -> &dyn Any { 24 | self 25 | } 26 | } 27 | 28 | pub trait WhileBase { 29 | fn new(conditions: Vec, body: Vec>) -> Self; 30 | } 31 | 32 | impl WhileBase for While { 33 | fn new(conditions: Vec, body: Vec>) -> Self { 34 | Self { body, conditions } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ham_core/src/lib.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | boxed_val::BoxedValue, 4 | break_ast::{ 5 | Break, 6 | BreakBase, 7 | }, 8 | convert_tokens_into_arguments, 9 | convert_tokens_into_res_expressions, 10 | expression::{ 11 | Expression, 12 | ExpressionBase, 13 | }, 14 | fn_call::{ 15 | FnCall, 16 | FnCallBase, 17 | }, 18 | fn_def::{ 19 | FnDefinition, 20 | FnDefinitionBase, 21 | }, 22 | get_assignment_token_fn, 23 | get_tokens_from_to_fn, 24 | if_ast::{ 25 | IfConditional, 26 | IfConditionalBase, 27 | }, 28 | module::Module, 29 | return_ast::ReturnStatement, 30 | var_assign::{ 31 | VarAssignment, 32 | VarAssignmentBase, 33 | }, 34 | var_def::{ 35 | VarDefinition, 36 | VarDefinitionBase, 37 | }, 38 | while_block::{ 39 | While, 40 | WhileBase, 41 | }, 42 | }, 43 | runtime::{ 44 | downcast_val, 45 | get_methods_in_type, 46 | resolve_reference, 47 | value_to_string, 48 | values_to_strings, 49 | }, 50 | stack::{ 51 | FunctionDef, 52 | FunctionsContainer, 53 | Stack, 54 | VariableDef, 55 | }, 56 | types::{ 57 | BoxedPrimitiveValue, 58 | IndexedTokenList, 59 | LinesList, 60 | Token, 61 | TokensList, 62 | }, 63 | utils::{ 64 | errors, 65 | Directions, 66 | Ops, 67 | }, 68 | }; 69 | use regex::Regex; 70 | use std::{ 71 | collections::HashMap, 72 | fs, 73 | sync::Mutex, 74 | }; 75 | use uuid::Uuid; 76 | 77 | pub mod ast_types; 78 | pub mod primitive_values; 79 | pub mod runtime; 80 | pub mod stack; 81 | pub mod types; 82 | pub mod utils; 83 | 84 | use primitive_values::string::StringVal; 85 | 86 | /* 87 | * Split the text by the passed regex but also keep these words which are removed when splitting 88 | */ 89 | fn split<'a>(r: &'a Regex, text: &'a str) -> Vec<&'a str> { 90 | let mut result = Vec::new(); 91 | let mut last = 0; 92 | for (index, matched) in text.match_indices(r) { 93 | if last != index { 94 | result.push(&text[last..index]); 95 | } 96 | result.push(matched); 97 | 98 | last = index + matched.len(); 99 | } 100 | if last < text.len() { 101 | result.push(&text[last..]); 102 | } 103 | result 104 | } 105 | 106 | /* 107 | * Transform the code into lines 108 | */ 109 | fn get_lines(code: String) -> LinesList { 110 | let mut lines = Vec::new(); 111 | 112 | // Every line 113 | for line in code.split('\n') { 114 | // Ignore // comments 115 | if line.starts_with("//") { 116 | continue; 117 | } 118 | 119 | let mut line_ast = Vec::new(); 120 | 121 | let re = Regex::new(r#"([\s+,.:])|("(.*?)")|([()])"#).unwrap(); 122 | 123 | // Every detected word 124 | for word in split(&re, line) { 125 | // Prevent empty words 126 | if word.trim() != "" { 127 | line_ast.push(String::from(word.trim())); 128 | } 129 | } 130 | lines.push(line_ast); 131 | } 132 | 133 | lines 134 | } 135 | 136 | /* 137 | * Trasnform a list of lines into a tokens list 138 | */ 139 | fn transform_into_tokens(lines: LinesList) -> TokensList { 140 | let mut tokens = Vec::new(); 141 | 142 | for (i, line) in lines.iter().enumerate() { 143 | for word in line { 144 | let token_type: Ops = match word.as_str() { 145 | "let" => Ops::VarDef, 146 | "=" => Ops::LeftAssign, 147 | "(" => Ops::OpenParent, 148 | ")" => Ops::CloseParent, 149 | "fn" => Ops::FnDef, 150 | "{" => Ops::OpenBlock, 151 | "}" => Ops::CloseBlock, 152 | "if" => Ops::IfConditional, 153 | "==" => Ops::EqualCondition, 154 | "return" => Ops::Return, 155 | "." => Ops::PropAccess, 156 | "," => Ops::CommaDelimiter, 157 | "while" => Ops::WhileDef, 158 | "!=" => Ops::NotEqualCondition, 159 | "import" => Ops::Import, 160 | "from" => Ops::FromModule, 161 | "break" => Ops::Break, 162 | _ => Ops::Reference, 163 | }; 164 | 165 | let ast_token = Token { 166 | ast_type: token_type, 167 | value: word.clone(), 168 | line: i + 1, 169 | }; 170 | 171 | tokens.push(ast_token); 172 | } 173 | } 174 | 175 | tokens 176 | } 177 | 178 | /* 179 | * Transform the code into a list of tokens 180 | */ 181 | pub fn get_tokens(code: String) -> TokensList { 182 | let lines = self::get_lines(code); 183 | self::transform_into_tokens(lines) 184 | } 185 | 186 | /* 187 | * Create a ast tree from some tokens 188 | */ 189 | pub fn move_tokens_into_ast(tokens: TokensList, ast_tree: &Mutex, filedir: String) { 190 | let mut ast_tree = ast_tree.lock().unwrap(); 191 | 192 | // Closure version of above 193 | let get_tokens_from_to = |from: usize, to: Ops| -> IndexedTokenList { 194 | get_tokens_from_to_fn(from, to, tokens.clone(), Directions::LeftToRight) 195 | }; 196 | 197 | // Get all the tokens in a group (expression blocks, arguments) 198 | let get_tokens_in_group_of = |from: usize, open_tok: Ops, close_tok: Ops| -> TokensList { 199 | let mut found_tokens = Vec::new(); 200 | 201 | let mut count = 0; 202 | 203 | let mut token_n = from; 204 | 205 | while token_n < tokens.len() { 206 | let token = tokens[token_n].clone(); 207 | 208 | if token.ast_type == open_tok { 209 | count += 1; 210 | } else if token.ast_type == close_tok { 211 | count -= 1; 212 | } 213 | 214 | if count == 0 { 215 | break; 216 | } else if token_n > from { 217 | found_tokens.push(token.clone()); 218 | } 219 | token_n += 1; 220 | } 221 | 222 | found_tokens 223 | }; 224 | 225 | let get_assignment_token = |val: String, token_n: usize| -> (usize, BoxedValue) { 226 | get_assignment_token_fn(val, token_n, tokens.clone(), Directions::LeftToRight) 227 | }; 228 | 229 | let mut token_n = 0; 230 | 231 | while token_n < tokens.len() { 232 | let current_token = &tokens[token_n]; 233 | match current_token.ast_type { 234 | // Break statement 235 | Ops::Break => { 236 | let break_ast = Break::new(); 237 | ast_tree.body.push(Box::new(break_ast)); 238 | token_n += 1; 239 | } 240 | 241 | // Import statement 242 | Ops::Import => { 243 | let module_name = &tokens[token_n + 1].value; 244 | let module_direction = &tokens[token_n + 2]; 245 | let module_origin = &tokens[token_n + 3].value; 246 | /* 247 | * import x from "./x.ham" 248 | * 249 | * module_name: x 250 | * module_direction: from 251 | * module_origin= "./x.ham" 252 | */ 253 | 254 | if module_direction.ast_type != Ops::FromModule { 255 | errors::raise_error( 256 | errors::CODES::UnexpectedKeyword, 257 | vec![module_direction.value.clone()], 258 | ) 259 | } 260 | // Module's path 261 | let filepath = format!("{}/{}", filedir, module_origin.replace('"', "")); 262 | 263 | // Module's code 264 | let filecontent = fs::read_to_string(filepath.as_str()); 265 | 266 | if let Ok(filecontent) = filecontent { 267 | let tokens = get_tokens(filecontent); 268 | 269 | // Move all the tokens into a expression 270 | let scope_tree = Mutex::new(Expression::new()); 271 | move_tokens_into_ast(tokens.clone(), &scope_tree, filedir.clone()); 272 | 273 | // Copy all root-functions (public by default) from the expression body to the vector 274 | let mut public_functions = Vec::new(); 275 | 276 | for op in scope_tree.lock().unwrap().body.iter() { 277 | if op.get_type() == Ops::FnDef { 278 | public_functions 279 | .push(downcast_val::(op.as_self()).clone()); 280 | } 281 | } 282 | 283 | let module = Module { 284 | name: module_name.to_string(), 285 | functions: public_functions, 286 | }; 287 | 288 | ast_tree.body.push(Box::new(module)); 289 | } else { 290 | errors::raise_error(errors::CODES::ModuleNotFound, vec![filepath]) 291 | } 292 | 293 | token_n += 4 294 | } 295 | 296 | // While block 297 | Ops::WhileDef => { 298 | // Get the if condition tokens 299 | let condition_tokens = get_tokens_from_to(token_n + 1, Ops::OpenBlock); 300 | 301 | // Transform those tokens into result expressions 302 | let exprs = convert_tokens_into_res_expressions( 303 | condition_tokens 304 | .clone() 305 | .iter() 306 | .map(|(_, token)| token.clone()) 307 | .collect(), 308 | ); 309 | 310 | // Scope tree 311 | let scope_tree = Mutex::new(Expression::new()); 312 | 313 | // Ignore the if conditions and { 314 | let open_block_index = token_n + condition_tokens.len() + 1; 315 | 316 | // Get all tokens inside the if block 317 | let block_tokens = 318 | get_tokens_in_group_of(open_block_index, Ops::OpenBlock, Ops::CloseBlock); 319 | 320 | // Move the tokens into the tree 321 | move_tokens_into_ast(block_tokens.clone(), &scope_tree, filedir.clone()); 322 | 323 | // Ignore the whilte body 324 | token_n = block_tokens.len() + open_block_index + 1; 325 | 326 | // Create a while definition 327 | let body = &scope_tree.lock().unwrap().body.clone(); 328 | let ast_token = While::new(exprs.clone(), body.to_vec()); 329 | ast_tree.body.push(Box::new(ast_token)); 330 | } 331 | 332 | // Return statement 333 | Ops::Return => { 334 | let next_token = tokens[token_n + 1].clone(); 335 | 336 | let (size, return_val) = 337 | get_assignment_token(next_token.value.clone(), token_n + 1); 338 | 339 | let ast_token = ReturnStatement { value: return_val }; 340 | ast_tree.body.push(Box::new(ast_token)); 341 | 342 | token_n += 2 + size; 343 | } 344 | 345 | // If statement 346 | Ops::IfConditional => { 347 | // Get the if condition tokens 348 | let condition_tokens = get_tokens_from_to(token_n + 1, Ops::OpenBlock); 349 | 350 | // Transform those tokens into result expressions 351 | let exprs = convert_tokens_into_res_expressions( 352 | condition_tokens 353 | .clone() 354 | .iter() 355 | .map(|(_, token)| token.clone()) 356 | .collect(), 357 | ); 358 | 359 | // Scope tree 360 | let scope_tree = Mutex::new(Expression::new()); 361 | 362 | // Ignore the if conditions and { 363 | let open_block_index = token_n + condition_tokens.len() + 1; 364 | 365 | // Get all tokens inside the if block 366 | let block_tokens = 367 | get_tokens_in_group_of(open_block_index, Ops::OpenBlock, Ops::CloseBlock); 368 | 369 | // Move the tokens into the tree 370 | move_tokens_into_ast(block_tokens.clone(), &scope_tree, filedir.clone()); 371 | 372 | // Ignore the block body 373 | token_n = block_tokens.len() + open_block_index + 1; 374 | 375 | // Create a if block definition 376 | let body = &scope_tree.lock().unwrap().body.clone(); 377 | let ast_token = IfConditional::new(exprs.clone(), body.to_vec()); 378 | ast_tree.body.push(Box::new(ast_token)); 379 | } 380 | 381 | // Property access 382 | Ops::PropAccess => { 383 | let previous_token = tokens[token_n - 1].clone(); 384 | let next_token = tokens[token_n + 1].clone(); 385 | 386 | if next_token.ast_type == Ops::Reference { 387 | let after_next_token = tokens[token_n + 2].clone(); 388 | 389 | let reference_type = match after_next_token.ast_type { 390 | Ops::OpenParent => Ops::FnCall, 391 | _ => Ops::Invalid, 392 | }; 393 | 394 | match reference_type { 395 | /* 396 | * Call to functions from variables 397 | */ 398 | Ops::FnCall => { 399 | let mut ast_token = 400 | FnCall::new(next_token.value.clone(), Some(previous_token.value)); 401 | 402 | // Ignore itself and the ( 403 | let starting_token = token_n + 2; 404 | 405 | let arguments_tokens = get_tokens_in_group_of( 406 | starting_token, 407 | Ops::OpenParent, 408 | Ops::CloseParent, 409 | ); 410 | let arguments = convert_tokens_into_arguments(arguments_tokens.clone()); 411 | 412 | token_n += 2 + arguments_tokens.len(); 413 | 414 | ast_token.arguments = arguments; 415 | ast_tree.body.push(Box::new(ast_token)); 416 | } 417 | /* 418 | * TODO: Access properties from varibles 419 | */ 420 | Ops::Reference => {} 421 | _ => (), 422 | }; 423 | } 424 | } 425 | 426 | // Function definition 427 | Ops::FnDef => { 428 | let def_name = String::from(&tokens[token_n + 1].value.clone()); 429 | 430 | // Scope tree 431 | let scope_tree = Mutex::new(Expression::new()); 432 | 433 | // Ignore function name and the ( 434 | let starting_token = token_n + 2; 435 | 436 | // Get function arguments, WIP 437 | let arguments: Vec = 438 | get_tokens_in_group_of(starting_token, Ops::OpenParent, Ops::CloseParent) 439 | .iter() 440 | .map(|token| token.value.clone()) 441 | .collect(); 442 | 443 | // Ignore function name, (, arguments and ) 444 | let open_block_index = starting_token + arguments.len() + 2; 445 | 446 | // Get all tokens inside the function block 447 | 448 | let block_tokens = 449 | get_tokens_in_group_of(open_block_index, Ops::OpenBlock, Ops::CloseBlock); 450 | 451 | // Move the tokens into the tree 452 | move_tokens_into_ast(block_tokens.clone(), &scope_tree, filedir.clone()); 453 | 454 | // Ignore the function body 455 | token_n = block_tokens.len() + open_block_index + 1; 456 | 457 | // Create a function definition 458 | let body = &scope_tree.lock().unwrap().body.clone(); 459 | let ast_token = FnDefinition::new(def_name, body.to_vec(), arguments); 460 | ast_tree.body.push(Box::new(ast_token)); 461 | } 462 | 463 | // Variable definition 464 | Ops::VarDef => { 465 | let next_token = tokens[token_n + 1].clone(); 466 | 467 | // Variable name 468 | let def_name = next_token.value.clone(); 469 | 470 | // Value token position 471 | let val_index = token_n + 3; 472 | 473 | // Stringified value 474 | let def_value = String::from(&tokens[val_index].value.clone()); 475 | 476 | let (size, assignment) = get_assignment_token(def_value, val_index); 477 | 478 | let ast_token = VarDefinition::new(def_name, assignment); 479 | ast_tree.body.push(Box::new(ast_token)); 480 | 481 | token_n += size + 3; 482 | } 483 | 484 | // References (fn calls, variable reassignation...) 485 | Ops::Reference => { 486 | let reference_type = { 487 | if token_n < tokens.len() - 1 { 488 | let next_token = &tokens[token_n + 1]; 489 | match next_token.ast_type { 490 | Ops::OpenParent => Ops::FnCall, 491 | Ops::LeftAssign => Ops::VarAssign, 492 | _ => Ops::Invalid, 493 | } 494 | } else { 495 | Ops::Invalid 496 | } 497 | }; 498 | 499 | match reference_type { 500 | Ops::VarAssign => { 501 | let token_after_equal = tokens[token_n + 2].clone(); 502 | 503 | let (size, assignment) = 504 | get_assignment_token(token_after_equal.value.clone(), token_n + 2); 505 | 506 | let ast_token = VarAssignment::new(current_token.value.clone(), assignment); 507 | 508 | ast_tree.body.push(Box::new(ast_token)); 509 | 510 | token_n += 2 + size; 511 | } 512 | Ops::FnCall => { 513 | let mut ast_token = FnCall::new(current_token.value.clone(), None); 514 | 515 | // Ignore itself and the ( 516 | let starting_token = token_n + 1; 517 | 518 | let arguments_tokens = get_tokens_in_group_of( 519 | starting_token, 520 | Ops::OpenParent, 521 | Ops::CloseParent, 522 | ); 523 | 524 | let arguments = convert_tokens_into_arguments(arguments_tokens.clone()); 525 | 526 | token_n += 3 + arguments_tokens.len(); 527 | 528 | ast_token.arguments = arguments; 529 | 530 | ast_tree.body.push(Box::new(ast_token)); 531 | } 532 | _ => { 533 | token_n += 1; 534 | } 535 | } 536 | } 537 | _ => { 538 | token_n += 1; 539 | } 540 | } 541 | } 542 | } 543 | 544 | /* 545 | * Shorthand to create a function definition 546 | */ 547 | fn get_function_from_def(function: &FnDefinition) -> FunctionDef { 548 | FunctionDef { 549 | name: function.def_name.clone(), 550 | body: function.body.clone(), 551 | arguments: function.arguments.clone(), 552 | cb: |args, args_vals, body, stack, ast| { 553 | let expr = Expression::from_body(body.clone()); 554 | let expr_id = expr.expr_id.clone(); 555 | 556 | for (i, arg) in args_vals.iter().enumerate() { 557 | let arg_name = args[i].clone(); 558 | let var_id = stack.lock().unwrap().reseve_index(); 559 | stack.lock().unwrap().push_variable(VariableDef { 560 | name: arg_name, 561 | value: arg.value.clone(), 562 | val_type: arg.interface, 563 | expr_id: expr_id.clone(), 564 | functions: get_methods_in_type(arg.interface), 565 | var_id, 566 | }) 567 | } 568 | 569 | let return_val = run_ast(&Mutex::new(expr), stack); 570 | 571 | stack.lock().unwrap().drop_ops_from_id(expr_id); 572 | 573 | if let Some(return_val) = return_val { 574 | resolve_reference(stack, return_val.interface, return_val.value, &ast) 575 | } else { 576 | return_val 577 | } 578 | }, 579 | // TODO: Move away from Uuid 580 | expr_id: Uuid::new_v4().to_string(), 581 | } 582 | } 583 | 584 | /* 585 | * Execute a AST tree 586 | */ 587 | pub fn run_ast(ast: &Mutex, stack: &Mutex) -> Option { 588 | let ast = ast.lock().unwrap(); 589 | 590 | // Closure version of resolve_reference 591 | let resolve_ref = |val_type: Ops, ref_val: BoxedPrimitiveValue| -> Option { 592 | resolve_reference(stack, val_type, ref_val, &ast) 593 | }; 594 | 595 | // Check if a conditional is true or not 596 | let eval_condition = 597 | |condition_code: Ops, left_val: BoxedValue, right_val: BoxedValue| -> bool { 598 | let left_val = resolve_ref(left_val.interface, left_val.value.clone()); 599 | let right_val = resolve_ref(right_val.interface, right_val.value.clone()); 600 | 601 | if let (Some(left_val), Some(right_val)) = (left_val, right_val) { 602 | match condition_code { 603 | // Handle != 604 | Ops::NotEqualCondition => { 605 | let left_val = value_to_string(left_val, stack).unwrap(); 606 | let right_val = value_to_string(right_val, stack).unwrap(); 607 | 608 | left_val != right_val 609 | } 610 | // Handle == 611 | Ops::EqualCondition => { 612 | let left_val = value_to_string(left_val, stack).unwrap(); 613 | let right_val = value_to_string(right_val, stack).unwrap(); 614 | 615 | left_val == right_val 616 | } 617 | _ => false, 618 | } 619 | } else { 620 | false 621 | } 622 | }; 623 | 624 | for operation in &ast.body { 625 | match operation.get_type() { 626 | /* 627 | * Handle breaks 628 | */ 629 | Ops::Break => { 630 | return Some(BoxedValue { 631 | interface: Ops::Break, 632 | value: Box::new(StringVal("break".to_string())), 633 | }) 634 | } 635 | 636 | /* 637 | * Handle module definitions 638 | */ 639 | Ops::Module => { 640 | let module = downcast_val::(operation.as_self()); 641 | 642 | let var_id = stack.lock().unwrap().reseve_index(); 643 | 644 | let mut functions = HashMap::new(); 645 | 646 | for function in &module.functions { 647 | let mut function = function.clone(); 648 | function.arguments.insert(0, "_".to_string()); 649 | functions.insert(function.def_name.clone(), get_function_from_def(&function)); 650 | } 651 | 652 | // Push the variable into the stack 653 | stack.lock().unwrap().push_variable(VariableDef { 654 | name: module.name.clone(), 655 | val_type: Ops::String, 656 | value: Box::new(StringVal(module.name.clone())), 657 | expr_id: ast.expr_id.clone(), 658 | functions, 659 | var_id, 660 | }); 661 | } 662 | 663 | /* 664 | * Handle if block 665 | */ 666 | Ops::WhileDef => { 667 | let while_block = downcast_val::(operation.as_self()); 668 | 669 | let check_while = |while_block: &While| -> Option { 670 | /* 671 | * Evaluate all conditions, 672 | * If all they return true then execute the IF's expression block 673 | */ 674 | let mut true_count = 0; 675 | 676 | for condition in while_block.conditions.clone() { 677 | let res = 678 | eval_condition(condition.relation, condition.left, condition.right); 679 | 680 | if res { 681 | true_count += 1; 682 | } 683 | } 684 | 685 | if true_count == while_block.conditions.len() { 686 | let expr = Expression::from_body(while_block.body.clone()); 687 | let expr_id = expr.expr_id.clone(); 688 | 689 | // Execute the expression block 690 | let if_block_return = run_ast(&Mutex::new(expr), stack); 691 | 692 | /* 693 | * While's loop will stop when something is returned forcefully 694 | */ 695 | if let Some(if_block_return) = if_block_return { 696 | return Some(if_block_return); 697 | } 698 | 699 | // Clean the expression definitions from the stack 700 | stack.lock().unwrap().drop_ops_from_id(expr_id); 701 | Some(BoxedValue { 702 | value: Box::new(StringVal("while".to_string())), 703 | interface: Ops::WhileDef, 704 | }) 705 | } else { 706 | None 707 | } 708 | }; 709 | 710 | let mut stopped = false; 711 | 712 | while !stopped { 713 | let res = check_while(while_block); 714 | 715 | if let Some(res) = res { 716 | match res.interface { 717 | Ops::WhileDef => { 718 | // Ignore non-returning whiles 719 | } 720 | Ops::Break => { 721 | // Simply stop the while 722 | stopped = true; 723 | } 724 | _ => { 725 | // Stop and return the value 726 | return Some(res); 727 | } 728 | } 729 | } else { 730 | stopped = true; 731 | } 732 | } 733 | } 734 | 735 | /* 736 | * Handle return statements 737 | */ 738 | Ops::Return => { 739 | let statement = downcast_val::(operation.as_self()); 740 | 741 | // Type of return 742 | let return_type = statement.value.interface; 743 | 744 | // Value returning 745 | let return_val = statement.value.value.clone(); 746 | 747 | // Pimitive value to return 748 | let return_val = resolve_ref(return_type, return_val); 749 | 750 | return return_val; 751 | } 752 | 753 | /* 754 | * Handle if statements 755 | */ 756 | Ops::IfConditional => { 757 | let if_statement = downcast_val::(operation.as_self()); 758 | 759 | /* 760 | * Evaluate all conditions, 761 | * If all they return true then execute the IF's expression block 762 | */ 763 | let mut true_count = 0; 764 | 765 | for condition in if_statement.conditions.clone() { 766 | let res = eval_condition(condition.relation, condition.left, condition.right); 767 | 768 | if res { 769 | true_count += 1; 770 | } 771 | } 772 | if true_count == if_statement.conditions.len() { 773 | let expr = Expression::from_body(if_statement.body.clone()); 774 | let expr_id = expr.expr_id.clone(); 775 | 776 | // Execute the expression block 777 | let if_block_return = run_ast(&Mutex::new(expr), stack); 778 | 779 | if let Some(if_block_return) = if_block_return { 780 | return Some(if_block_return); 781 | } 782 | 783 | // Clean the expression definitions from the stack 784 | stack.lock().unwrap().drop_ops_from_id(expr_id.clone()); 785 | } 786 | } 787 | 788 | /* 789 | * Handle function definitions 790 | */ 791 | Ops::FnDef => { 792 | let function = downcast_val::(operation.as_self()); 793 | 794 | stack 795 | .lock() 796 | .unwrap() 797 | .push_function(get_function_from_def(function)); 798 | } 799 | 800 | /* 801 | * Handle variables definitions 802 | */ 803 | Ops::VarDef => { 804 | let variable = downcast_val::(operation.as_self()); 805 | 806 | let val_type = variable.assignment.interface; 807 | let ref_val = variable.assignment.value.clone(); 808 | 809 | let var_ref = resolve_ref(val_type, ref_val); 810 | 811 | if let Some(var_ref) = var_ref { 812 | // Take a id for the stack 813 | let var_id = stack.lock().unwrap().reseve_index(); 814 | 815 | // Push the variable into the stack 816 | stack.lock().unwrap().push_variable(VariableDef { 817 | name: variable.def_name.clone(), 818 | val_type: var_ref.interface, 819 | value: var_ref.value, 820 | expr_id: ast.expr_id.clone(), 821 | functions: get_methods_in_type(var_ref.interface), 822 | var_id, 823 | }); 824 | } 825 | } 826 | 827 | /* 828 | * Handle variable assignments 829 | */ 830 | Ops::VarAssign => { 831 | let variable = downcast_val::(operation.as_self()); 832 | 833 | let is_pointer = variable.var_name.starts_with('&'); 834 | 835 | let variable_name = if is_pointer { 836 | // Remove & from it's name 837 | let mut variable_name = variable.var_name.clone(); 838 | variable_name.remove(0); 839 | variable_name 840 | } else { 841 | variable.var_name.clone() 842 | }; 843 | 844 | let ref_val = resolve_ref( 845 | variable.assignment.interface, 846 | variable.assignment.value.clone(), 847 | ); 848 | 849 | if let Some(ref_val) = ref_val { 850 | stack.lock().unwrap().modify_var(variable_name, ref_val); 851 | } 852 | } 853 | 854 | /* 855 | * Handle function calls 856 | */ 857 | Ops::FnCall => { 858 | let fn_call = downcast_val::(operation.as_self()); 859 | 860 | let is_referenced = fn_call.reference_to.is_some(); 861 | 862 | let function = if is_referenced { 863 | let reference_to = fn_call.reference_to.as_ref().unwrap(); 864 | let variable = stack 865 | .lock() 866 | .unwrap() 867 | .get_variable_by_name(reference_to.as_str()); 868 | variable 869 | .unwrap() 870 | .get_function_by_name(fn_call.fn_name.as_str()) 871 | } else { 872 | stack 873 | .lock() 874 | .unwrap() 875 | .get_function_by_name(fn_call.fn_name.as_str()) 876 | }; 877 | 878 | // If the calling function is found 879 | if let Some(function) = function { 880 | let mut arguments = Vec::new(); 881 | 882 | if is_referenced { 883 | let reference_to = fn_call.reference_to.as_ref().unwrap(); 884 | arguments.push(BoxedValue { 885 | interface: Ops::String, 886 | value: Box::new(StringVal(reference_to.to_string())), 887 | }); 888 | } 889 | 890 | for argument in &fn_call.arguments { 891 | let arg_ref = resolve_ref(argument.interface, argument.value.clone()); 892 | if let Some(arg_ref) = arg_ref { 893 | arguments.push(arg_ref); 894 | } else { 895 | // Broken argument 896 | } 897 | } 898 | 899 | let res_func = (function.cb)( 900 | function.arguments, 901 | arguments.clone(), 902 | function.body, 903 | &stack, 904 | &ast, 905 | ); 906 | 907 | if let Some(ret_val) = res_func { 908 | let val_stringified = value_to_string(ret_val, stack); 909 | 910 | if let Ok(val_stringified) = val_stringified { 911 | // The function returned something that ends up not being used, throw error 912 | 913 | errors::raise_error( 914 | errors::CODES::ReturnedValueNotUsed, 915 | vec![ 916 | val_stringified, 917 | fn_call.fn_name.clone(), 918 | values_to_strings(arguments, stack).join(" "), 919 | ], 920 | ) 921 | } 922 | } else { 923 | // No value returned, OK 924 | } 925 | } 926 | } 927 | _ => { 928 | panic!("Unhandled code operation") 929 | } 930 | } 931 | } 932 | None 933 | } 934 | -------------------------------------------------------------------------------- /ham_core/src/primitive_values.rs: -------------------------------------------------------------------------------- 1 | pub mod boolean; 2 | pub mod number; 3 | pub mod pointer; 4 | pub mod primitive_base; 5 | pub mod string; 6 | -------------------------------------------------------------------------------- /ham_core/src/primitive_values/boolean.rs: -------------------------------------------------------------------------------- 1 | use crate::primitive_values::primitive_base::PrimitiveValueBase; 2 | use serde::Serialize; 3 | use std::any::Any; 4 | 5 | /* 6 | * Boolean 7 | */ 8 | 9 | #[derive(Clone, Debug, Serialize)] 10 | pub struct Boolean(pub bool); 11 | 12 | // Implement base methods for Boolean 13 | impl PrimitiveValueBase for Boolean { 14 | fn as_self(&self) -> &dyn Any { 15 | self 16 | } 17 | } 18 | 19 | /* 20 | * Boolean base 21 | */ 22 | pub trait BooleanValueBase { 23 | fn new(val: bool) -> Self; 24 | fn get_state(&self) -> bool; 25 | } 26 | 27 | impl BooleanValueBase for Boolean { 28 | fn new(val: bool) -> Self { 29 | Self(val) 30 | } 31 | 32 | fn get_state(&self) -> bool { 33 | self.0 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ham_core/src/primitive_values/number.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | boxed_val::BoxedValue, 5 | expression::Expression, 6 | }, 7 | primitive_values::primitive_base::PrimitiveValueBase, 8 | runtime::{ 9 | downcast_val, 10 | value_to_string, 11 | }, 12 | stack::Stack, 13 | utils::Ops, 14 | }; 15 | use serde::Serialize; 16 | use std::{ 17 | any::Any, 18 | sync::{ 19 | Mutex, 20 | MutexGuard, 21 | }, 22 | }; 23 | 24 | /* 25 | * Number 26 | */ 27 | 28 | #[derive(Clone, Debug, Serialize)] 29 | pub struct Number(pub usize); 30 | 31 | impl PrimitiveValueBase for Number { 32 | fn as_self(&self) -> &dyn Any { 33 | self 34 | } 35 | } 36 | 37 | /* 38 | * Number base 39 | */ 40 | pub trait NumberValueBase { 41 | fn new(value: usize) -> Self; 42 | fn get_state(&self) -> usize; 43 | 44 | fn mut_sum( 45 | args: Vec, 46 | args_vals: Vec, 47 | body: Vec>, 48 | stack: &Mutex, 49 | ast: &MutexGuard, 50 | ) -> Option; 51 | 52 | fn sum( 53 | args: Vec, 54 | args_vals: Vec, 55 | body: Vec>, 56 | stack: &Mutex, 57 | ast: &MutexGuard, 58 | ) -> Option; 59 | } 60 | 61 | impl NumberValueBase for Number { 62 | fn new(value: usize) -> Self { 63 | Self(value) 64 | } 65 | 66 | fn get_state(&self) -> usize { 67 | self.0 68 | } 69 | 70 | /* 71 | * function: sum() 72 | * 73 | * Returns the variable's value plus the argument 74 | */ 75 | fn sum( 76 | _: Vec, 77 | args_vals: Vec, 78 | _: Vec>, 79 | stack: &Mutex, 80 | _: &MutexGuard, 81 | ) -> Option { 82 | let var_name = value_to_string(args_vals[0].clone(), stack).unwrap(); 83 | let new_val = downcast_val::(args_vals[1].value.as_self()).0; 84 | 85 | // Get the variable from the stack 86 | let variable = stack 87 | .lock() 88 | .unwrap() 89 | .get_variable_by_name(var_name.as_str()); 90 | 91 | match variable { 92 | Some(current_var) => { 93 | let current_var = downcast_val::(current_var.value.as_self()); 94 | let current_val = current_var.get_state(); 95 | 96 | let new_val = Number::new(current_val + new_val); 97 | 98 | Some(BoxedValue { 99 | interface: Ops::Number, 100 | value: Box::new(new_val), 101 | }) 102 | } 103 | _ => None, 104 | } 105 | } 106 | 107 | /* 108 | * function: mut_sum() 109 | * 110 | * Assigns to the variable value it's value plus the argument 111 | */ 112 | fn mut_sum( 113 | _: Vec, 114 | args_vals: Vec, 115 | _: Vec>, 116 | stack: &Mutex, 117 | _: &MutexGuard, 118 | ) -> Option { 119 | let var_name = value_to_string(args_vals[0].clone(), stack).unwrap(); 120 | let new_val = downcast_val::(args_vals[1].value.as_self()).0; 121 | 122 | // Get the variable from the stack 123 | let var_ref = stack 124 | .lock() 125 | .unwrap() 126 | .get_variable_by_name(var_name.as_str()); 127 | 128 | if let Some(current_var) = var_ref { 129 | let current_val = downcast_val::(current_var.value.as_self()); 130 | let current_num = current_val.get_state(); 131 | 132 | let new_val = Number::new(current_num + new_val); 133 | 134 | stack.lock().unwrap().modify_var( 135 | var_name, 136 | BoxedValue { 137 | interface: Ops::Number, 138 | value: Box::new(new_val), 139 | }, 140 | ); 141 | } 142 | 143 | None 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /ham_core/src/primitive_values/pointer.rs: -------------------------------------------------------------------------------- 1 | use crate::primitive_values::primitive_base::PrimitiveValueBase; 2 | use serde::Serialize; 3 | use std::any::Any; 4 | 5 | #[derive(Clone, Debug, Serialize)] 6 | pub struct Pointer(pub u64); 7 | 8 | // Implement base methods for Pointer 9 | impl PrimitiveValueBase for Pointer { 10 | fn as_self(&self) -> &dyn Any { 11 | self 12 | } 13 | } 14 | 15 | /* 16 | * Pointer base 17 | */ 18 | pub trait PointerBase { 19 | fn get_state(&self) -> u64; 20 | } 21 | 22 | impl PointerBase for Pointer { 23 | fn get_state(&self) -> u64 { 24 | self.0 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ham_core/src/primitive_values/primitive_base.rs: -------------------------------------------------------------------------------- 1 | use erased_serde::serialize_trait_object; 2 | use std::any::Any; 3 | 4 | /* 5 | * Primitive value base 6 | */ 7 | pub trait PrimitiveValueBase: 8 | dyn_clone::DynClone + erased_serde::Serialize + std::fmt::Debug 9 | { 10 | fn as_self(&self) -> &dyn Any; 11 | } 12 | 13 | dyn_clone::clone_trait_object!(PrimitiveValueBase); 14 | serialize_trait_object!(PrimitiveValueBase); 15 | -------------------------------------------------------------------------------- /ham_core/src/primitive_values/string.rs: -------------------------------------------------------------------------------- 1 | use crate::primitive_values::primitive_base::PrimitiveValueBase; 2 | use serde::Serialize; 3 | use std::any::Any; 4 | 5 | /* 6 | * String 7 | */ 8 | 9 | #[derive(Clone, Debug, Serialize)] 10 | pub struct StringVal(pub String); 11 | 12 | // Implement base methods for String 13 | impl PrimitiveValueBase for StringVal { 14 | fn as_self(&self) -> &dyn Any { 15 | self 16 | } 17 | } 18 | 19 | /* 20 | * String base 21 | */ 22 | pub trait StringValueBase { 23 | fn new(val: String) -> Self; 24 | fn get_state(&self) -> String; 25 | } 26 | 27 | impl StringValueBase for StringVal { 28 | fn new(val: String) -> Self { 29 | Self(val) 30 | } 31 | 32 | fn get_state(&self) -> String { 33 | self.0.clone() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ham_core/src/runtime.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | boxed_val::BoxedValue, 4 | expression::Expression, 5 | fn_call::FnCall, 6 | reference::Reference, 7 | }, 8 | primitive_values::{ 9 | boolean::Boolean, 10 | number::{ 11 | Number, 12 | NumberValueBase, 13 | }, 14 | pointer::Pointer, 15 | string::StringVal, 16 | }, 17 | run_ast, 18 | stack::{ 19 | FunctionDef, 20 | FunctionsContainer, 21 | Stack, 22 | }, 23 | types::BoxedPrimitiveValue, 24 | utils::{ 25 | errors, 26 | errors::raise_error, 27 | Ops, 28 | }, 29 | }; 30 | use std::{ 31 | any::Any, 32 | collections::HashMap, 33 | sync::{ 34 | Mutex, 35 | MutexGuard, 36 | }, 37 | }; 38 | 39 | /* 40 | * Shorthand and *unsafe* way to downcast values 41 | */ 42 | pub fn downcast_val(val: &dyn Any) -> &T { 43 | val.downcast_ref::().unwrap() 44 | } 45 | 46 | /* 47 | * Force the transformation from a primitive type into a String 48 | */ 49 | pub fn value_to_string(value: BoxedValue, stack: &Mutex) -> Result { 50 | match value.interface { 51 | Ops::Boolean => Ok(downcast_val::(value.value.as_self()).0.to_string()), 52 | Ops::String => Ok(downcast_val::(value.value.as_self()).0.clone()), 53 | Ops::Number => Ok(downcast_val::(value.value.as_self()).0.to_string()), 54 | Ops::Pointer => { 55 | let pointer = downcast_val::(value.value.as_self()).0; 56 | let variable = stack.lock().unwrap().get_variable_by_id(pointer).unwrap(); 57 | 58 | value_to_string( 59 | BoxedValue { 60 | value: variable.value, 61 | interface: variable.val_type, 62 | }, 63 | stack, 64 | ) 65 | } 66 | _ => Err(value.interface), 67 | } 68 | } 69 | 70 | /* 71 | * Transform a group of boxed values into strings 72 | */ 73 | pub fn values_to_strings(values: Vec, stack: &Mutex) -> Vec { 74 | return values 75 | .iter() 76 | .map(|arg| value_to_string(arg.clone(), stack).unwrap()) 77 | .collect(); 78 | } 79 | 80 | /* 81 | * Returns the methods for the specified primitive type 82 | */ 83 | pub fn get_methods_in_type(val_type: Ops) -> HashMap { 84 | let mut res = HashMap::new(); 85 | 86 | /* 87 | * map a value type to it's implemented functions 88 | */ 89 | match val_type { 90 | Ops::Number => { 91 | res.insert( 92 | "sum".to_string(), 93 | FunctionDef { 94 | name: String::from("sum"), 95 | body: vec![], 96 | cb: Number::sum, 97 | expr_id: "".to_string(), 98 | arguments: vec![], 99 | }, 100 | ); 101 | 102 | res.insert( 103 | "mut_sum".to_string(), 104 | FunctionDef { 105 | name: String::from("mut_sum"), 106 | body: vec![], 107 | cb: Number::mut_sum, 108 | expr_id: "".to_string(), 109 | arguments: vec![], 110 | }, 111 | ); 112 | } 113 | 114 | /* 115 | * TODO: Methods for strings 116 | */ 117 | Ops::String => {} 118 | 119 | /* 120 | * TODO: Methods for booleans 121 | */ 122 | Ops::Boolean => {} 123 | _ => (), 124 | } 125 | 126 | res 127 | } 128 | 129 | /* 130 | * For static values it will just return the input but for references it will resolve its value 131 | * and then return it 132 | */ 133 | pub fn resolve_reference( 134 | stack: &Mutex, 135 | val_type: Ops, 136 | ref_val: BoxedPrimitiveValue, 137 | ast: &MutexGuard, 138 | ) -> Option { 139 | match val_type { 140 | Ops::Expression => { 141 | let expr = downcast_val::(ref_val.as_self()); 142 | let res = run_ast(&Mutex::new(expr.clone()), stack); 143 | 144 | stack.lock().unwrap().drop_ops_from_id(expr.expr_id.clone()); 145 | 146 | res 147 | } 148 | 149 | Ops::Pointer => { 150 | let pointer = downcast_val::(ref_val.as_self()).0; 151 | let variable = stack.lock().unwrap().get_variable_by_id(pointer); 152 | 153 | if let Some(variable) = variable { 154 | Some(BoxedValue { 155 | value: variable.value, 156 | interface: variable.val_type, 157 | }) 158 | } else { 159 | raise_error(errors::CODES::BrokenPointer, vec![pointer.to_string()]); 160 | None 161 | } 162 | } 163 | Ops::String => Some(BoxedValue { 164 | interface: val_type, 165 | value: ref_val, 166 | }), 167 | Ops::Boolean => Some(BoxedValue { 168 | interface: val_type, 169 | value: ref_val, 170 | }), 171 | Ops::Number => Some(BoxedValue { 172 | interface: val_type, 173 | value: ref_val, 174 | }), 175 | Ops::Reference => { 176 | let mut referenced_variable = downcast_val::(ref_val.as_self()).clone(); 177 | 178 | let is_pointer = referenced_variable.0.starts_with('&'); 179 | 180 | if is_pointer { 181 | // Remove & from it's name 182 | referenced_variable.0.remove(0); 183 | } 184 | 185 | let variable = stack 186 | .lock() 187 | .unwrap() 188 | .get_variable_by_name(referenced_variable.0.as_str()); 189 | 190 | if let Some(variable) = variable { 191 | if is_pointer { 192 | // Return a pointer 193 | Some(BoxedValue { 194 | interface: Ops::Pointer, 195 | value: Box::new(Pointer(variable.var_id)), 196 | }) 197 | } else { 198 | // Return a copy of it's value 199 | Some(BoxedValue { 200 | interface: variable.val_type, 201 | value: variable.value, 202 | }) 203 | } 204 | } else { 205 | raise_error(errors::CODES::VariableNotFound, vec![referenced_variable.0]); 206 | None 207 | } 208 | } 209 | Ops::FnCall => { 210 | let fn_call = downcast_val::(ref_val.as_self()); 211 | 212 | let is_referenced = fn_call.reference_to.is_some(); 213 | 214 | let function = if is_referenced { 215 | let reference_to = fn_call.reference_to.as_ref().unwrap(); 216 | let variable = stack 217 | .lock() 218 | .unwrap() 219 | .get_variable_by_name(reference_to.as_str()); 220 | 221 | variable 222 | .unwrap() 223 | .get_function_by_name(fn_call.fn_name.as_str()) 224 | } else { 225 | stack 226 | .lock() 227 | .unwrap() 228 | .get_function_by_name(fn_call.fn_name.as_str()) 229 | }; 230 | 231 | // If the calling function is found 232 | if let Some(function) = function { 233 | let mut arguments = Vec::new(); 234 | 235 | // Pass the reference name as first argument 236 | if is_referenced { 237 | let reference_to = fn_call.reference_to.as_ref().unwrap(); 238 | arguments.push(BoxedValue { 239 | interface: Ops::String, 240 | value: Box::new(StringVal(reference_to.to_string())), 241 | }); 242 | } 243 | 244 | for argument in &fn_call.arguments { 245 | let arg_ref = 246 | resolve_reference(stack, argument.interface, argument.value.clone(), &ast); 247 | 248 | if let Some(arg_ref) = arg_ref { 249 | arguments.push(arg_ref); 250 | } else { 251 | // Broken argument 252 | } 253 | } 254 | 255 | // Call the function and return it's result 256 | (function.cb)(function.arguments, arguments, function.body, &stack, &ast) 257 | } else { 258 | None 259 | } 260 | } 261 | _ => None, 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /ham_core/src/stack.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ast_types::{ 3 | ast_base::AstBase, 4 | boxed_val::BoxedValue, 5 | expression::Expression, 6 | }, 7 | primitive_values::{ 8 | pointer::Pointer, 9 | primitive_base::PrimitiveValueBase, 10 | string::StringVal, 11 | }, 12 | runtime::{ 13 | downcast_val, 14 | value_to_string, 15 | values_to_strings, 16 | }, 17 | utils::{ 18 | errors, 19 | Ops, 20 | }, 21 | }; 22 | use std::{ 23 | collections::HashMap, 24 | sync::{ 25 | Mutex, 26 | MutexGuard, 27 | }, 28 | thread, 29 | time, 30 | }; 31 | 32 | /* 33 | * Variable definition stored on the memory stack 34 | */ 35 | #[derive(Clone)] 36 | pub struct VariableDef { 37 | pub name: String, 38 | pub val_type: Ops, 39 | pub value: Box, 40 | pub expr_id: String, 41 | pub functions: HashMap, 42 | pub var_id: u64, 43 | } 44 | 45 | impl FunctionsContainer for VariableDef { 46 | fn get_function_by_name(&self, fn_name: &str) -> Option { 47 | let op_fn: Option<&FunctionDef> = self.functions.get(fn_name); 48 | 49 | if let Some(op_fn) = op_fn { 50 | Some(op_fn.clone()) 51 | } else { 52 | errors::raise_error(errors::CODES::FunctionNotFound, vec![fn_name.to_string()]); 53 | None 54 | } 55 | } 56 | fn push_function(&mut self, function: FunctionDef) { 57 | self.functions.insert(function.name.clone(), function); 58 | } 59 | } 60 | 61 | type FunctionAction = fn( 62 | args: Vec, 63 | args_vals: Vec, 64 | body: Vec>, 65 | stack: &Mutex, 66 | ast: &MutexGuard, 67 | ) -> Option; 68 | 69 | /* 70 | * Function definition stored on the memory stack 71 | */ 72 | #[derive(Clone)] 73 | pub struct FunctionDef { 74 | pub name: String, 75 | pub body: Vec>, 76 | pub cb: FunctionAction, 77 | pub expr_id: String, 78 | pub arguments: Vec, 79 | } 80 | 81 | /* 82 | * Common layer for functions container (ex: variables, memory stack) 83 | */ 84 | pub trait FunctionsContainer { 85 | /* 86 | * Return the requested function if found 87 | */ 88 | fn get_function_by_name(&self, fn_name: &str) -> Option; 89 | /* 90 | * Push a function into the container 91 | */ 92 | fn push_function(&mut self, function: FunctionDef); 93 | } 94 | 95 | /* 96 | * Implementation of the stack 97 | */ 98 | #[derive(Clone)] 99 | pub struct Stack { 100 | pub functions: HashMap, 101 | pub variables: Vec, 102 | pub item_index: u64, 103 | } 104 | 105 | impl FunctionsContainer for Stack { 106 | fn get_function_by_name(&self, fn_name: &str) -> Option { 107 | for function in self.functions.values() { 108 | if function.name == *fn_name { 109 | return Some(function.clone()); 110 | } 111 | } 112 | errors::raise_error(errors::CODES::FunctionNotFound, vec![fn_name.to_string()]); 113 | None 114 | } 115 | fn push_function(&mut self, function: FunctionDef) { 116 | self.functions.insert(function.expr_id.clone(), function); 117 | } 118 | } 119 | 120 | impl Stack { 121 | pub fn reseve_index(&mut self) -> u64 { 122 | self.item_index += 1; 123 | self.item_index 124 | } 125 | 126 | pub fn new(expr_id: String) -> Stack { 127 | let mut functions = HashMap::new(); 128 | 129 | functions.insert( 130 | "clear".to_string(), 131 | FunctionDef { 132 | name: "clear".to_string(), 133 | body: vec![], 134 | arguments: vec![], 135 | cb: |_, _, _, _, _| { 136 | print!("{esc}[2J{esc}[1;1H", esc = 27 as char); 137 | None 138 | }, 139 | expr_id: expr_id.clone(), 140 | }, 141 | ); 142 | 143 | /* 144 | * format() function 145 | * 146 | * Example: 147 | * 148 | * let msg = format("Hello {} from {}", "people", "world") 149 | * 150 | */ 151 | functions.insert( 152 | "format".to_string(), 153 | FunctionDef { 154 | name: "format".to_string(), 155 | body: vec![], 156 | arguments: vec![], 157 | cb: |_, args, _, stack, _| { 158 | let mut args = values_to_strings(args, stack); 159 | 160 | let mut template = args[0].clone(); 161 | 162 | args.remove(0); 163 | 164 | for arg in args { 165 | template = template.replacen("{}", arg.as_str(), 1); 166 | } 167 | 168 | Some(BoxedValue { 169 | interface: Ops::String, 170 | value: Box::new(StringVal(template)), 171 | }) 172 | }, 173 | expr_id: expr_id.clone(), 174 | }, 175 | ); 176 | 177 | /* 178 | * print() function 179 | */ 180 | functions.insert( 181 | "print".to_string(), 182 | FunctionDef { 183 | name: "print".to_string(), 184 | body: vec![], 185 | arguments: vec![], 186 | cb: |_, args, _, stack, _| { 187 | print!("{}", values_to_strings(args, stack).join(" ")); 188 | None 189 | }, 190 | expr_id: expr_id.clone(), 191 | }, 192 | ); 193 | 194 | /* 195 | * println() function 196 | */ 197 | functions.insert( 198 | "println".to_string(), 199 | FunctionDef { 200 | name: "println".to_string(), 201 | body: vec![], 202 | arguments: vec![], 203 | cb: |_, args, _, stack, _| { 204 | println!("{}", values_to_strings(args, stack).join("")); 205 | None 206 | }, 207 | expr_id: expr_id.clone(), 208 | }, 209 | ); 210 | 211 | /* 212 | * wait() function 213 | */ 214 | functions.insert( 215 | "wait".to_string(), 216 | FunctionDef { 217 | name: "wait".to_string(), 218 | body: vec![], 219 | arguments: vec![], 220 | cb: |_, args, _, stack, _| { 221 | let time = value_to_string(args[0].clone(), stack) 222 | .unwrap() 223 | .parse::() 224 | .unwrap(); 225 | let time = time::Duration::from_millis(time); 226 | thread::sleep(time); 227 | None 228 | }, 229 | expr_id, 230 | }, 231 | ); 232 | 233 | Stack { 234 | variables: Vec::new(), 235 | functions, 236 | item_index: 0, 237 | } 238 | } 239 | 240 | /* 241 | * Print variables and functions stored on stack 242 | */ 243 | #[allow(dead_code)] 244 | pub fn debug_print(&self) { 245 | let functions: String = self 246 | .functions 247 | .iter() 248 | .map(|func| { 249 | format!( 250 | "fn {}({}); is {} \n", 251 | func.0, 252 | func.1.arguments.join(", "), 253 | func.1.expr_id 254 | ) 255 | }) 256 | .collect(); 257 | let variables: String = self 258 | .variables 259 | .iter() 260 | .map(|var| { 261 | format!( 262 | "let {} = {}; in {} \n", 263 | var.name, 264 | value_to_string( 265 | BoxedValue { 266 | value: var.value.clone(), 267 | interface: var.val_type 268 | }, 269 | &Mutex::new(self.clone()) 270 | ) 271 | .unwrap(), 272 | var.expr_id 273 | ) 274 | }) 275 | .collect(); 276 | println!( 277 | "DEBUG:: \n | functions | \n{} \n | variables | \n{}", 278 | functions, variables 279 | ); 280 | } 281 | 282 | /* 283 | * Drop from the stack all variables and functions which are port 284 | * of an specified expression ID 285 | * 286 | * This is mainly used in block expression (functions, if...) 287 | */ 288 | pub fn drop_ops_from_id(&mut self, id: String) { 289 | self.variables.retain(|var| var.expr_id != id); 290 | self.functions.retain(|_, func| func.expr_id != id); 291 | } 292 | 293 | pub fn push_variable(&mut self, var: VariableDef) { 294 | self.variables.push(var); 295 | } 296 | 297 | /* 298 | * Get a variable from the stack by its ID 299 | */ 300 | pub fn get_variable_by_id(&self, var_id: u64) -> Option { 301 | for variable in &self.variables { 302 | if variable.var_id == var_id { 303 | return Some(variable.clone()); 304 | } 305 | } 306 | None 307 | } 308 | 309 | /* 310 | * Get a variable from the stack by its name 311 | */ 312 | pub fn get_variable_by_name(&self, var_name: &str) -> Option { 313 | for variable in self.variables.iter().rev() { 314 | if variable.name == *var_name { 315 | return Some(variable.clone()); 316 | } 317 | } 318 | None 319 | } 320 | 321 | /* 322 | * Get a mutable variable from the stack by its ID 323 | */ 324 | pub fn get_mut_variable_by_id(&mut self, var_id: u64) -> Option<&mut VariableDef> { 325 | for variable in &mut self.variables { 326 | if variable.var_id == var_id { 327 | return Some(variable); 328 | } 329 | } 330 | None 331 | } 332 | 333 | /* 334 | * Get a mutable variable from the stack by its name 335 | */ 336 | pub fn get_mut_variable_by_name(&mut self, var_name: &str) -> Option<&mut VariableDef> { 337 | for variable in &mut self.variables.iter_mut().rev() { 338 | if variable.name == *var_name { 339 | return Some(variable); 340 | } 341 | } 342 | None 343 | } 344 | 345 | /* 346 | * Modify a variable value 347 | */ 348 | pub fn modify_var(&mut self, var_name: String, value: BoxedValue) { 349 | let variable = self.get_mut_variable_by_name(var_name.as_str()); 350 | 351 | // If variable exists 352 | if let Some(variable) = variable { 353 | // Is pointer 354 | if variable.val_type == Ops::Pointer { 355 | let variable = variable.clone(); 356 | 357 | let pointer = downcast_val::(variable.value.as_self()); 358 | 359 | let variable_origin = self.get_mut_variable_by_id(pointer.0); 360 | 361 | if let Some(variable_origin) = variable_origin { 362 | variable_origin.value = value.value; 363 | variable_origin.val_type = value.interface; 364 | } else { 365 | // Broken pointer 366 | } 367 | } else { 368 | variable.value = value.value; 369 | } 370 | } else { 371 | errors::raise_error(errors::CODES::VariableNotFound, vec![var_name.clone()]); 372 | } 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /ham_core/src/types.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | primitive_values::primitive_base::PrimitiveValueBase, 3 | utils::Ops, 4 | }; 5 | 6 | #[derive(Clone, Debug, PartialEq)] 7 | pub struct Token { 8 | pub ast_type: Ops, 9 | pub value: String, 10 | pub line: usize, 11 | } 12 | 13 | impl Token { 14 | pub fn new(ast_type: Ops, value: String, line: usize) -> Self { 15 | Self { 16 | ast_type, 17 | value, 18 | line, 19 | } 20 | } 21 | } 22 | 23 | pub type LinesList = Vec>; 24 | pub type TokensList = Vec; 25 | pub type IndexedTokenList = Vec<(usize, Token)>; 26 | 27 | // Boxed primitive value 28 | pub type BoxedPrimitiveValue = Box; 29 | -------------------------------------------------------------------------------- /ham_core/src/utils.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | #[derive(Clone)] 4 | pub enum Directions { 5 | LeftToRight, 6 | RightToLeft, 7 | } 8 | 9 | #[derive(Clone, Debug, Serialize, PartialEq, Copy)] 10 | pub enum Ops { 11 | Invalid, 12 | Reference, 13 | VarDef, 14 | LeftAssign, 15 | Expression, 16 | FnCall, 17 | OpenParent, 18 | CloseParent, 19 | Boolean, 20 | Number, 21 | String, 22 | VarAssign, 23 | FnDef, 24 | OpenBlock, 25 | CloseBlock, 26 | IfConditional, 27 | ResExpression, 28 | EqualCondition, 29 | Return, 30 | PropAccess, 31 | CommaDelimiter, 32 | WhileDef, 33 | NotEqualCondition, 34 | Pointer, 35 | Import, 36 | Module, 37 | FromModule, 38 | Break, 39 | } 40 | 41 | pub mod errors { 42 | 43 | use colored::*; 44 | 45 | pub enum CODES { 46 | // Function wasn't found in the current scope 47 | FunctionNotFound, 48 | 49 | // Variable wasn't found in the current scope 50 | VariableNotFound, 51 | 52 | // Not used returned value 53 | ReturnedValueNotUsed, 54 | 55 | // Pointer points to an invalid reference 56 | BrokenPointer, 57 | 58 | // Module is not found (ex, file's path is not correct) 59 | ModuleNotFound, 60 | 61 | // Got a wrong keyword 62 | UnexpectedKeyword, 63 | } 64 | 65 | pub fn raise_error(kind: CODES, args: Vec) { 66 | let msg = match kind { 67 | CODES::FunctionNotFound => format!("Function '{}' was not found", args[0]), 68 | CODES::VariableNotFound => format!("Variable '{}' was not found", args[0].blue()), 69 | CODES::ReturnedValueNotUsed => { 70 | // Function's name length + arguments length + () 71 | let lines = "¯".repeat(args[1].len() + args[2].len() + 2); 72 | format!( 73 | "Returned value '{value}' by function '{fn_name}' is not used\n 74 | let value = {fn_name}({call_args}); 75 | ¯¯¯¯¯¯¯¯¯ 76 | ↑ Help: Assign the return value to a variable. 77 | 78 | println({fn_name}({call_args})); 79 | {lines} 80 | ↑ Help: Wrap it as a function argument.", 81 | value = args[0].blue(), 82 | fn_name = args[1].blue(), 83 | call_args = args[2], 84 | lines = lines 85 | ) 86 | } 87 | CODES::BrokenPointer => { 88 | format!( 89 | "Pointer points to variable by id '{}' which does no longer exist.", 90 | args[0].blue() 91 | ) 92 | } 93 | CODES::ModuleNotFound => { 94 | format!("There is no module in path '{}'", args[0].blue()) 95 | } 96 | CODES::UnexpectedKeyword => { 97 | format!("Unexpected keyword '{}'", args[0].blue()) 98 | } 99 | }; 100 | 101 | println!("{}: {}", "Error".red(), msg); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /ham_manager/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ham_manager" 3 | version = "0.0.1" 4 | edition = "2018" 5 | authors = ["Marc Espín Sanz"] 6 | 7 | [dependencies] 8 | yaml-rust = "0.4.5" -------------------------------------------------------------------------------- /ham_manager/readme.md: -------------------------------------------------------------------------------- 1 | This crate manages manifest files. -------------------------------------------------------------------------------- /ham_manager/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use yaml_rust::YamlLoader; 3 | 4 | #[derive(Debug)] 5 | pub enum Errors { 6 | Reading, 7 | Parsing, 8 | } 9 | 10 | pub struct Manifest { 11 | pub version: Option, 12 | } 13 | 14 | impl Manifest { 15 | pub fn from_file(file: &str) -> Result { 16 | match fs::read_to_string(file) { 17 | Ok(content) => { 18 | let yaml = YamlLoader::load_from_str(content.as_str()); 19 | 20 | if let Ok(yaml) = yaml { 21 | // Manifest document 22 | let manifest = yaml[0].clone(); 23 | 24 | // Manifest version 25 | let version = manifest["version"] 26 | .as_str() 27 | .map(|version| version.to_string()); 28 | 29 | Ok(Manifest { version }) 30 | } else { 31 | Err(Errors::Parsing) 32 | } 33 | } 34 | Err(_) => Err(Errors::Reading), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Marc Espín Sanz 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. -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 🍖 ham, a programming language made in Rust 2 | 3 | **NOTE: I'm learning rust, I am still a noob** 4 | 5 | status: **alpha** 6 | 7 | You can **download** it from [here](https://github.com/marc2332/ham/releases). 8 | 9 | ### Goals 10 | - Speed 11 | - Security 12 | - Comfort 13 | 14 | ### Language Ideas 15 | - Rust interoperability 16 | - Manifest file 17 | - Ability to import files from: 18 | - Local file system 19 | - HTTP urls 20 | - By name (this would use some kind of remote registry to translate it to HTTP urls) 21 | - std library 22 | - Low-level (networking, file system...) APIs 23 | 24 | ### Project ideas 25 | - CD integration to release a new version in each commit 26 | - More unit tests 27 | 28 | ### Example 29 | 30 | ```rust 31 | 32 | fn calc(value){ 33 | // If the value is 5 end the function 34 | if value == 5 { 35 | return 0 36 | } 37 | 38 | // Add 1 39 | value.mut_sum(1) 40 | 41 | // Print it's value 42 | println(format("Value is {}", value)) 43 | 44 | // Call the function again with the latest value 45 | return calc(value) 46 | } 47 | 48 | // This will print from `Value is 1` to `Value is 5` 49 | let _ = calc(0) 50 | ``` 51 | 52 | There are more examples in /examples. 53 | 54 | ### About 55 | ham is a general purpose language. It is heavily inspired by Rust and TypeScript. 56 | 57 | ### Usage 58 | 59 | Built-in repl: 60 | ```shell 61 | ham repl 62 | ``` 63 | 64 | Run files: 65 | ```shell 66 | ham run examples/demo.ham 67 | ``` 68 | 69 | Run a project (This will run `1_project/src/main.ham` automatically): 70 | ```shell 71 | ham run examples/1_project 72 | ``` 73 | 74 | ### Contribuding 75 | 76 | Compiling: 77 | ```shell 78 | cargo build --release 79 | ``` 80 | 81 | Linting: 82 | ```shell 83 | cargo clippy 84 | ``` 85 | 86 | Formatting: 87 | ```shell 88 | cargo fmt 89 | ``` 90 | 91 | Testing: 92 | ```shell 93 | cargo test 94 | ``` 95 | 96 | Install mdbook: 97 | ```shell 98 | cargo install mdbook 99 | ``` 100 | 101 | Build the book: 102 | ```shell 103 | mdbook build 104 | ``` 105 | 106 | Watch for changes on the book: 107 | ```shell 108 | mdbook watch 109 | ``` 110 | 111 | Running directly: 112 | ```shell 113 | cargo run -- run examples/demo.ham 114 | ``` 115 | 116 | Made by Marc Espín Sanz 117 | 118 | MIT License -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | imports_layout = "Vertical" 2 | imports_granularity = "Crate" 3 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{ 2 | App, 3 | Arg, 4 | ArgMatches, 5 | }; 6 | use ham_core::{ 7 | ast_types::expression::{ 8 | Expression, 9 | ExpressionBase, 10 | }, 11 | stack::Stack, 12 | }; 13 | use ham_manager::Manifest; 14 | use question::Question; 15 | use std::{ 16 | fs, 17 | path::Path, 18 | sync::Mutex, 19 | }; 20 | 21 | fn commands() -> ArgMatches { 22 | App::new("ham") 23 | .version("0.0.2") 24 | .author("Marc E. ") 25 | .subcommand(App::new("repl")) 26 | .subcommand( 27 | App::new("run") 28 | .arg(Arg::new("file").about("Live code interpreter.")) 29 | .arg( 30 | Arg::new("show_ast_tree") 31 | .about("Displays the AST Tree of the code.") 32 | .takes_value(false) 33 | .short('t') 34 | .long("show-ast-tree"), 35 | ), 36 | ) 37 | .get_matches() 38 | } 39 | 40 | fn run_repl() { 41 | let cli_welcome = format!(":: ham REPL ({}) ::", env!("CARGO_PKG_VERSION")); 42 | let cli_tip = "Use Ctrl+C to exit."; 43 | 44 | println!("{}\n{}", cli_welcome, cli_tip); 45 | 46 | // CWD 47 | let cwd = std::env::current_dir().unwrap().display().to_string(); 48 | 49 | // Global context 50 | let global_context = Expression::new(); 51 | 52 | // Memory stack 53 | let stack = Mutex::new(Stack::new(global_context.expr_id)); 54 | 55 | loop { 56 | let answer = Question::new(">").ask().unwrap(); 57 | 58 | match answer { 59 | question::Answer::RESPONSE(line) => { 60 | // Tokens 61 | let tokens = ham_core::get_tokens(line); 62 | 63 | // Ast tree root 64 | let tree = Mutex::new(Expression::new()); 65 | 66 | // Tree 67 | ham_core::move_tokens_into_ast(tokens, &tree, cwd.clone()); 68 | 69 | // Run the code 70 | ham_core::run_ast(&tree, &stack); 71 | 72 | println!(" <-"); 73 | } 74 | question::Answer::NO | question::Answer::YES => {} 75 | } 76 | } 77 | } 78 | 79 | fn main() { 80 | let matches = commands(); 81 | 82 | match matches.subcommand() { 83 | Some(("run", run_matches)) => { 84 | // CWD 85 | let cwd = std::env::current_dir().unwrap().display().to_string(); 86 | 87 | let filename = run_matches.value_of("file"); 88 | 89 | // Manifest file 90 | let _manifest = { 91 | let manifest_file = format!("{}/ham.yml", cwd); 92 | 93 | Manifest::from_file(manifest_file.as_str()) 94 | }; 95 | 96 | // Main file 97 | let filename = if let Some(filename) = filename { 98 | format!("{}/{}", cwd, filename.to_string()) 99 | } else { 100 | format!("{}/src/main.ham", cwd) 101 | }; 102 | 103 | // Main file content 104 | let filecontent = fs::read_to_string(filename.as_str()) 105 | .expect("Something went wrong reading the file"); 106 | 107 | // Tokens 108 | let tokens = ham_core::get_tokens(filecontent); 109 | 110 | // Global context 111 | let global_context = Expression::new(); 112 | 113 | // Memory stack 114 | let stack = Mutex::new(Stack::new(global_context.expr_id.clone())); 115 | 116 | // Ast tree root 117 | let tree = Mutex::new(global_context); 118 | 119 | // File's folder 120 | let mut filedir = Path::new(filename.as_str()).ancestors(); 121 | filedir.next().unwrap(); 122 | 123 | let filedir = filedir.next().unwrap(); 124 | let filedir = filedir.to_str().unwrap().to_string(); 125 | 126 | // Tree 127 | ham_core::move_tokens_into_ast(tokens, &tree, filedir); 128 | 129 | if run_matches.is_present("show_ast_tree") { 130 | println!( 131 | "{}", 132 | serde_json::to_string_pretty(&tree.lock().unwrap().clone()).unwrap() 133 | ); 134 | } 135 | 136 | ham_core::run_ast(&tree, &stack); 137 | } 138 | Some(("repl", _)) => { 139 | run_repl(); 140 | } 141 | _ => { 142 | // Default to repl 143 | run_repl(); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /tests/manifest.rs: -------------------------------------------------------------------------------- 1 | use ham_manager::Manifest; 2 | 3 | #[test] 4 | pub fn manifest_manager() { 5 | // Manifest file 6 | let filename = format!( 7 | "{}/examples/1_project/ham.yml", 8 | std::env::current_dir().unwrap().display() 9 | ); 10 | 11 | // Manifest instance 12 | let manifest = Manifest::from_file(filename.as_str()); 13 | 14 | // It was found 15 | assert_eq!(true, manifest.is_ok()); 16 | 17 | let manifest = manifest.unwrap(); 18 | 19 | // Expect the same version 20 | assert_eq!("1.0.0", manifest.version.unwrap()); 21 | } 22 | -------------------------------------------------------------------------------- /tests/tokenizer.rs: -------------------------------------------------------------------------------- 1 | use ham_core::{ 2 | get_tokens, 3 | types::{ 4 | Token, 5 | TokensList, 6 | }, 7 | utils::Ops, 8 | }; 9 | 10 | /* 11 | * Make sure a sample code is properly tokenized 12 | */ 13 | #[test] 14 | pub fn tokenizer_works() { 15 | // Sample code 16 | const CODE: &str = "fn x(b){ let c = b return c } x(4)"; 17 | 18 | // Generated tokens 19 | let created_tokens: TokensList = get_tokens(CODE.to_string()); 20 | 21 | // Expected tokens 22 | let tokens: TokensList = vec![ 23 | Token::new(Ops::FnDef, "fn".to_string(), 1), 24 | Token::new(Ops::Reference, "x".to_string(), 1), 25 | Token::new(Ops::OpenParent, "(".to_string(), 1), 26 | Token::new(Ops::Reference, "b".to_string(), 1), 27 | Token::new(Ops::CloseParent, ")".to_string(), 1), 28 | Token::new(Ops::OpenBlock, "{".to_string(), 1), 29 | Token::new(Ops::VarDef, "let".to_string(), 1), 30 | Token::new(Ops::Reference, "c".to_string(), 1), 31 | Token::new(Ops::LeftAssign, "=".to_string(), 1), 32 | Token::new(Ops::Reference, "b".to_string(), 1), 33 | Token::new(Ops::Return, "return".to_string(), 1), 34 | Token::new(Ops::Reference, "c".to_string(), 1), 35 | Token::new(Ops::CloseBlock, "}".to_string(), 1), 36 | Token::new(Ops::Reference, "x".to_string(), 1), 37 | Token::new(Ops::OpenParent, "(".to_string(), 1), 38 | Token::new(Ops::Reference, "4".to_string(), 1), 39 | Token::new(Ops::CloseParent, ")".to_string(), 1), 40 | ]; 41 | 42 | let mut all_tokens_are_ok = true; 43 | 44 | for (i, token) in created_tokens.iter().enumerate() { 45 | if *token != tokens[i] { 46 | all_tokens_are_ok = false; 47 | } 48 | } 49 | 50 | assert_eq!(true, all_tokens_are_ok); 51 | } 52 | --------------------------------------------------------------------------------