├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── arduino ├── __init__.py ├── constants.py ├── math.py └── uno_pins.py ├── compiler ├── Cargo.toml ├── error │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── macros.rs │ │ └── types.rs ├── mangler │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── src │ ├── flags.rs │ ├── lib.rs │ └── llvm_prototypes.rs └── value │ ├── Cargo.toml │ └── src │ ├── convert.rs │ ├── lib.rs │ └── value.rs ├── examples ├── AnalogReadSerial.py ├── Blink.py ├── Button.py ├── MoodLight.py ├── SerialPrint.py └── UltrasonicSensor.py ├── include ├── Builtins.cc ├── Builtins.hh ├── LLVMArduinoBuiltins.cc ├── LLVMArduinoBuiltins.hh ├── Serial.cc └── Serial.hh ├── python ├── Cargo.toml ├── codegen │ ├── Cargo.toml │ └── src │ │ ├── cgexpr.rs │ │ ├── cgstmt.rs │ │ ├── lib.rs │ │ └── scope.rs ├── macros │ ├── Cargo.toml │ └── src │ │ ├── condition_value.rs │ │ └── lib.rs ├── parser │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── src │ └── lib.rs ├── src ├── arduino │ ├── avrdude.rs │ ├── avrgcc.rs │ └── mod.rs ├── bin │ └── dspython.rs ├── lib.rs └── utils.rs └── tests ├── fib.py └── x.py /.gitignore: -------------------------------------------------------------------------------- 1 | ### Intellij ### 2 | /.idea/ 3 | 4 | ### VSCode ### 5 | /.vscode 6 | 7 | ### Rust ### 8 | /target/ 9 | 10 | ### DSPython and compiler outputs ### 11 | *.o 12 | *.d 13 | *.eep 14 | *.elf 15 | *.hex 16 | core.a 17 | 18 | ### Example tests ### 19 | /examples/*.py.* 20 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.13" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" 8 | dependencies = [ 9 | "memchr", 10 | ] 11 | 12 | [[package]] 13 | name = "ansi_term" 14 | version = "0.11.0" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 17 | dependencies = [ 18 | "winapi", 19 | ] 20 | 21 | [[package]] 22 | name = "arrayref" 23 | version = "0.3.6" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 26 | 27 | [[package]] 28 | name = "arrayvec" 29 | version = "0.5.1" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 32 | 33 | [[package]] 34 | name = "ascii-canvas" 35 | version = "2.0.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "ff8eb72df928aafb99fe5d37b383f2fe25bd2a765e3e5f7c365916b6f2463a29" 38 | dependencies = [ 39 | "term", 40 | ] 41 | 42 | [[package]] 43 | name = "atty" 44 | version = "0.2.14" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 47 | dependencies = [ 48 | "hermit-abi", 49 | "libc", 50 | "winapi", 51 | ] 52 | 53 | [[package]] 54 | name = "autocfg" 55 | version = "1.0.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 58 | 59 | [[package]] 60 | name = "base64" 61 | version = "0.11.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 64 | 65 | [[package]] 66 | name = "bincode" 67 | version = "1.3.1" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" 70 | dependencies = [ 71 | "byteorder", 72 | "serde", 73 | ] 74 | 75 | [[package]] 76 | name = "bit-set" 77 | version = "0.5.2" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" 80 | dependencies = [ 81 | "bit-vec", 82 | ] 83 | 84 | [[package]] 85 | name = "bit-vec" 86 | version = "0.6.2" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" 89 | 90 | [[package]] 91 | name = "bitflags" 92 | version = "1.2.1" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 95 | 96 | [[package]] 97 | name = "blake2b_simd" 98 | version = "0.5.10" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" 101 | dependencies = [ 102 | "arrayref", 103 | "arrayvec", 104 | "constant_time_eq", 105 | ] 106 | 107 | [[package]] 108 | name = "block-buffer" 109 | version = "0.7.3" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" 112 | dependencies = [ 113 | "block-padding", 114 | "byte-tools", 115 | "byteorder", 116 | "generic-array", 117 | ] 118 | 119 | [[package]] 120 | name = "block-padding" 121 | version = "0.1.5" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" 124 | dependencies = [ 125 | "byte-tools", 126 | ] 127 | 128 | [[package]] 129 | name = "byte-tools" 130 | version = "0.3.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" 133 | 134 | [[package]] 135 | name = "byteorder" 136 | version = "1.3.4" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 139 | 140 | [[package]] 141 | name = "cc" 142 | version = "1.0.58" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" 145 | 146 | [[package]] 147 | name = "cfg-if" 148 | version = "0.1.10" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 151 | 152 | [[package]] 153 | name = "clap" 154 | version = "2.33.0" 155 | source = "git+https://github.com/clap-rs/clap?tag=v2.33.0#c50deae6bcec44714368f739dfbe2919d740d7b6" 156 | dependencies = [ 157 | "ansi_term", 158 | "atty", 159 | "bitflags", 160 | "strsim 0.8.0", 161 | "textwrap", 162 | "unicode-width", 163 | "vec_map", 164 | ] 165 | 166 | [[package]] 167 | name = "cloudabi" 168 | version = "0.1.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" 171 | dependencies = [ 172 | "bitflags", 173 | ] 174 | 175 | [[package]] 176 | name = "constant_time_eq" 177 | version = "0.1.5" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 180 | 181 | [[package]] 182 | name = "crossbeam-utils" 183 | version = "0.7.2" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 186 | dependencies = [ 187 | "autocfg", 188 | "cfg-if", 189 | "lazy_static", 190 | ] 191 | 192 | [[package]] 193 | name = "diff" 194 | version = "0.1.12" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" 197 | 198 | [[package]] 199 | name = "digest" 200 | version = "0.8.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 203 | dependencies = [ 204 | "generic-array", 205 | ] 206 | 207 | [[package]] 208 | name = "dirs" 209 | version = "1.0.5" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" 212 | dependencies = [ 213 | "libc", 214 | "redox_users", 215 | "winapi", 216 | ] 217 | 218 | [[package]] 219 | name = "docopt" 220 | version = "1.1.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "7f525a586d310c87df72ebcd98009e57f1cc030c8c268305287a476beb653969" 223 | dependencies = [ 224 | "lazy_static", 225 | "regex", 226 | "serde", 227 | "strsim 0.9.3", 228 | ] 229 | 230 | [[package]] 231 | name = "dsp_compiler" 232 | version = "0.1.0" 233 | dependencies = [ 234 | "dsp_compiler_error", 235 | "dsp_compiler_value", 236 | "dsp_python_codegen", 237 | "dsp_python_parser", 238 | "inkwell", 239 | ] 240 | 241 | [[package]] 242 | name = "dsp_compiler_error" 243 | version = "0.1.0" 244 | dependencies = [ 245 | "dsp_python_parser", 246 | "inkwell", 247 | ] 248 | 249 | [[package]] 250 | name = "dsp_compiler_mangler" 251 | version = "0.1.0" 252 | dependencies = [ 253 | "dsp_compiler_value", 254 | ] 255 | 256 | [[package]] 257 | name = "dsp_compiler_value" 258 | version = "0.1.0" 259 | dependencies = [ 260 | "dsp_python_parser", 261 | "inkwell", 262 | "num-bigint", 263 | "num-complex", 264 | "num-traits", 265 | ] 266 | 267 | [[package]] 268 | name = "dsp_python" 269 | version = "0.1.0" 270 | 271 | [[package]] 272 | name = "dsp_python_codegen" 273 | version = "0.1.0" 274 | dependencies = [ 275 | "dsp_compiler_error", 276 | "dsp_compiler_mangler", 277 | "dsp_compiler_value", 278 | "dsp_python_macros", 279 | "dsp_python_parser", 280 | "either", 281 | "inkwell", 282 | "num-bigint", 283 | "num-complex", 284 | "num-traits", 285 | ] 286 | 287 | [[package]] 288 | name = "dsp_python_macros" 289 | version = "0.1.0" 290 | 291 | [[package]] 292 | name = "dsp_python_parser" 293 | version = "0.1.0" 294 | dependencies = [ 295 | "rustpython-compiler", 296 | "rustpython-parser", 297 | ] 298 | 299 | [[package]] 300 | name = "dspython" 301 | version = "0.1.0" 302 | dependencies = [ 303 | "clap", 304 | "dsp_compiler", 305 | "dsp_python_codegen", 306 | "dsp_python_parser", 307 | ] 308 | 309 | [[package]] 310 | name = "either" 311 | version = "1.6.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" 314 | 315 | [[package]] 316 | name = "ena" 317 | version = "0.14.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" 320 | dependencies = [ 321 | "log", 322 | ] 323 | 324 | [[package]] 325 | name = "fake-simd" 326 | version = "0.1.2" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 329 | 330 | [[package]] 331 | name = "fixedbitset" 332 | version = "0.2.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" 335 | 336 | [[package]] 337 | name = "generic-array" 338 | version = "0.12.3" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" 341 | dependencies = [ 342 | "typenum", 343 | ] 344 | 345 | [[package]] 346 | name = "getrandom" 347 | version = "0.1.14" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 350 | dependencies = [ 351 | "cfg-if", 352 | "libc", 353 | "wasi", 354 | ] 355 | 356 | [[package]] 357 | name = "hashbrown" 358 | version = "0.8.2" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" 361 | dependencies = [ 362 | "autocfg", 363 | ] 364 | 365 | [[package]] 366 | name = "hermit-abi" 367 | version = "0.1.15" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" 370 | dependencies = [ 371 | "libc", 372 | ] 373 | 374 | [[package]] 375 | name = "indexmap" 376 | version = "1.5.1" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9" 379 | dependencies = [ 380 | "autocfg", 381 | "hashbrown", 382 | ] 383 | 384 | [[package]] 385 | name = "inkwell" 386 | version = "0.1.0" 387 | source = "git+https://github.com/TheDan64/inkwell?branch=llvm10-0#79070d9fbb0e7ed1c96c7133273770dc2e000a88" 388 | dependencies = [ 389 | "either", 390 | "inkwell_internals", 391 | "libc", 392 | "llvm-sys", 393 | "once_cell", 394 | "parking_lot", 395 | "regex", 396 | ] 397 | 398 | [[package]] 399 | name = "inkwell_internals" 400 | version = "0.2.0" 401 | source = "git+https://github.com/TheDan64/inkwell?branch=llvm10-0#79070d9fbb0e7ed1c96c7133273770dc2e000a88" 402 | dependencies = [ 403 | "proc-macro2", 404 | "quote", 405 | "syn", 406 | ] 407 | 408 | [[package]] 409 | name = "instant" 410 | version = "0.1.6" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" 413 | 414 | [[package]] 415 | name = "itertools" 416 | version = "0.9.0" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" 419 | dependencies = [ 420 | "either", 421 | ] 422 | 423 | [[package]] 424 | name = "lalrpop" 425 | version = "0.19.0" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "d6f55673d283313791404be21209bb433f128f7e5c451986df107eb5fdbd68d2" 428 | dependencies = [ 429 | "ascii-canvas", 430 | "atty", 431 | "bit-set", 432 | "diff", 433 | "docopt", 434 | "ena", 435 | "itertools", 436 | "lalrpop-util", 437 | "petgraph", 438 | "regex", 439 | "regex-syntax", 440 | "serde", 441 | "serde_derive", 442 | "sha2", 443 | "string_cache", 444 | "term", 445 | "unicode-xid", 446 | ] 447 | 448 | [[package]] 449 | name = "lalrpop-util" 450 | version = "0.19.0" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "f7e88f15a7d31dfa8fb607986819039127f0161058a3b248a146142d276cbd28" 453 | 454 | [[package]] 455 | name = "lazy_static" 456 | version = "1.4.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 459 | 460 | [[package]] 461 | name = "libc" 462 | version = "0.2.74" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" 465 | 466 | [[package]] 467 | name = "llvm-sys" 468 | version = "100.1.0" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "5545bf9a09267c644e4d0ac68f37ac200af6579ae2e82aebce382654eb4abab1" 471 | dependencies = [ 472 | "cc", 473 | "lazy_static", 474 | "libc", 475 | "regex", 476 | "semver", 477 | ] 478 | 479 | [[package]] 480 | name = "lock_api" 481 | version = "0.4.1" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" 484 | dependencies = [ 485 | "scopeguard", 486 | ] 487 | 488 | [[package]] 489 | name = "log" 490 | version = "0.4.11" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 493 | dependencies = [ 494 | "cfg-if", 495 | ] 496 | 497 | [[package]] 498 | name = "lz4-compression" 499 | version = "0.7.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "761104bf97f13a3caf47d822498a0760a10d00d220148bac2669f63fc3bb8270" 502 | 503 | [[package]] 504 | name = "memchr" 505 | version = "2.3.3" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 508 | 509 | [[package]] 510 | name = "new_debug_unreachable" 511 | version = "1.0.4" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" 514 | 515 | [[package]] 516 | name = "num-bigint" 517 | version = "0.3.0" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "b7f3fc75e3697059fb1bc465e3d8cca6cf92f56854f201158b3f9c77d5a3cfa0" 520 | dependencies = [ 521 | "autocfg", 522 | "num-integer", 523 | "num-traits", 524 | "serde", 525 | ] 526 | 527 | [[package]] 528 | name = "num-complex" 529 | version = "0.3.0" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "b05ad05bd8977050b171b3f6b48175fea6e0565b7981059b486075e1026a9fb5" 532 | dependencies = [ 533 | "num-traits", 534 | "serde", 535 | ] 536 | 537 | [[package]] 538 | name = "num-integer" 539 | version = "0.1.43" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 542 | dependencies = [ 543 | "autocfg", 544 | "num-traits", 545 | ] 546 | 547 | [[package]] 548 | name = "num-traits" 549 | version = "0.2.12" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 552 | dependencies = [ 553 | "autocfg", 554 | ] 555 | 556 | [[package]] 557 | name = "once_cell" 558 | version = "1.4.0" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" 561 | 562 | [[package]] 563 | name = "opaque-debug" 564 | version = "0.2.3" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" 567 | 568 | [[package]] 569 | name = "parking_lot" 570 | version = "0.11.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" 573 | dependencies = [ 574 | "instant", 575 | "lock_api", 576 | "parking_lot_core", 577 | ] 578 | 579 | [[package]] 580 | name = "parking_lot_core" 581 | version = "0.8.0" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" 584 | dependencies = [ 585 | "cfg-if", 586 | "cloudabi", 587 | "instant", 588 | "libc", 589 | "redox_syscall", 590 | "smallvec", 591 | "winapi", 592 | ] 593 | 594 | [[package]] 595 | name = "petgraph" 596 | version = "0.5.1" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" 599 | dependencies = [ 600 | "fixedbitset", 601 | "indexmap", 602 | ] 603 | 604 | [[package]] 605 | name = "phf_shared" 606 | version = "0.8.0" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" 609 | dependencies = [ 610 | "siphasher", 611 | ] 612 | 613 | [[package]] 614 | name = "precomputed-hash" 615 | version = "0.1.1" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 618 | 619 | [[package]] 620 | name = "proc-macro2" 621 | version = "1.0.19" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" 624 | dependencies = [ 625 | "unicode-xid", 626 | ] 627 | 628 | [[package]] 629 | name = "quote" 630 | version = "1.0.7" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 633 | dependencies = [ 634 | "proc-macro2", 635 | ] 636 | 637 | [[package]] 638 | name = "redox_syscall" 639 | version = "0.1.57" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 642 | 643 | [[package]] 644 | name = "redox_users" 645 | version = "0.3.4" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" 648 | dependencies = [ 649 | "getrandom", 650 | "redox_syscall", 651 | "rust-argon2", 652 | ] 653 | 654 | [[package]] 655 | name = "regex" 656 | version = "1.3.9" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" 659 | dependencies = [ 660 | "aho-corasick", 661 | "memchr", 662 | "regex-syntax", 663 | "thread_local", 664 | ] 665 | 666 | [[package]] 667 | name = "regex-syntax" 668 | version = "0.6.18" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" 671 | 672 | [[package]] 673 | name = "rust-argon2" 674 | version = "0.7.0" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" 677 | dependencies = [ 678 | "base64", 679 | "blake2b_simd", 680 | "constant_time_eq", 681 | "crossbeam-utils", 682 | ] 683 | 684 | [[package]] 685 | name = "rustpython-bytecode" 686 | version = "0.1.2" 687 | source = "git+https://github.com/RustPython/RustPython#a35408a058bbf4dd546b875866be25ea39e07382" 688 | dependencies = [ 689 | "bincode", 690 | "bitflags", 691 | "itertools", 692 | "lz4-compression", 693 | "num-bigint", 694 | "num-complex", 695 | "serde", 696 | ] 697 | 698 | [[package]] 699 | name = "rustpython-compiler" 700 | version = "0.1.2" 701 | source = "git+https://github.com/RustPython/RustPython#a35408a058bbf4dd546b875866be25ea39e07382" 702 | dependencies = [ 703 | "arrayvec", 704 | "indexmap", 705 | "itertools", 706 | "log", 707 | "num-complex", 708 | "rustpython-bytecode", 709 | "rustpython-parser", 710 | ] 711 | 712 | [[package]] 713 | name = "rustpython-parser" 714 | version = "0.1.2" 715 | source = "git+https://github.com/RustPython/RustPython#a35408a058bbf4dd546b875866be25ea39e07382" 716 | dependencies = [ 717 | "lalrpop", 718 | "lalrpop-util", 719 | "log", 720 | "num-bigint", 721 | "num-traits", 722 | "unic-emoji-char", 723 | "unic-ucd-ident", 724 | "unicode_names2", 725 | ] 726 | 727 | [[package]] 728 | name = "scopeguard" 729 | version = "1.1.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 732 | 733 | [[package]] 734 | name = "semver" 735 | version = "0.9.0" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 738 | dependencies = [ 739 | "semver-parser", 740 | ] 741 | 742 | [[package]] 743 | name = "semver-parser" 744 | version = "0.7.0" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 747 | 748 | [[package]] 749 | name = "serde" 750 | version = "1.0.115" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" 753 | dependencies = [ 754 | "serde_derive", 755 | ] 756 | 757 | [[package]] 758 | name = "serde_derive" 759 | version = "1.0.115" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" 762 | dependencies = [ 763 | "proc-macro2", 764 | "quote", 765 | "syn", 766 | ] 767 | 768 | [[package]] 769 | name = "sha2" 770 | version = "0.8.2" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" 773 | dependencies = [ 774 | "block-buffer", 775 | "digest", 776 | "fake-simd", 777 | "opaque-debug", 778 | ] 779 | 780 | [[package]] 781 | name = "siphasher" 782 | version = "0.3.3" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" 785 | 786 | [[package]] 787 | name = "smallvec" 788 | version = "1.4.2" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" 791 | 792 | [[package]] 793 | name = "string_cache" 794 | version = "0.8.0" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a" 797 | dependencies = [ 798 | "lazy_static", 799 | "new_debug_unreachable", 800 | "phf_shared", 801 | "precomputed-hash", 802 | "serde", 803 | ] 804 | 805 | [[package]] 806 | name = "strsim" 807 | version = "0.8.0" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 810 | 811 | [[package]] 812 | name = "strsim" 813 | version = "0.9.3" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 816 | 817 | [[package]] 818 | name = "syn" 819 | version = "1.0.38" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" 822 | dependencies = [ 823 | "proc-macro2", 824 | "quote", 825 | "unicode-xid", 826 | ] 827 | 828 | [[package]] 829 | name = "term" 830 | version = "0.5.2" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" 833 | dependencies = [ 834 | "byteorder", 835 | "dirs", 836 | "winapi", 837 | ] 838 | 839 | [[package]] 840 | name = "textwrap" 841 | version = "0.11.0" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 844 | dependencies = [ 845 | "unicode-width", 846 | ] 847 | 848 | [[package]] 849 | name = "thread_local" 850 | version = "1.0.1" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 853 | dependencies = [ 854 | "lazy_static", 855 | ] 856 | 857 | [[package]] 858 | name = "typenum" 859 | version = "1.12.0" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 862 | 863 | [[package]] 864 | name = "unic-char-property" 865 | version = "0.9.0" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" 868 | dependencies = [ 869 | "unic-char-range", 870 | ] 871 | 872 | [[package]] 873 | name = "unic-char-range" 874 | version = "0.9.0" 875 | source = "registry+https://github.com/rust-lang/crates.io-index" 876 | checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" 877 | 878 | [[package]] 879 | name = "unic-common" 880 | version = "0.9.0" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" 883 | 884 | [[package]] 885 | name = "unic-emoji-char" 886 | version = "0.9.0" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" 889 | dependencies = [ 890 | "unic-char-property", 891 | "unic-char-range", 892 | "unic-ucd-version", 893 | ] 894 | 895 | [[package]] 896 | name = "unic-ucd-ident" 897 | version = "0.9.0" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" 900 | dependencies = [ 901 | "unic-char-property", 902 | "unic-char-range", 903 | "unic-ucd-version", 904 | ] 905 | 906 | [[package]] 907 | name = "unic-ucd-version" 908 | version = "0.9.0" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" 911 | dependencies = [ 912 | "unic-common", 913 | ] 914 | 915 | [[package]] 916 | name = "unicode-width" 917 | version = "0.1.8" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 920 | 921 | [[package]] 922 | name = "unicode-xid" 923 | version = "0.2.1" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 926 | 927 | [[package]] 928 | name = "unicode_names2" 929 | version = "0.4.0" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "87d6678d7916394abad0d4b19df4d3802e1fd84abd7d701f39b75ee71b9e8cf1" 932 | 933 | [[package]] 934 | name = "vec_map" 935 | version = "0.8.2" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 938 | 939 | [[package]] 940 | name = "wasi" 941 | version = "0.9.0+wasi-snapshot-preview1" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 944 | 945 | [[package]] 946 | name = "winapi" 947 | version = "0.3.9" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 950 | dependencies = [ 951 | "winapi-i686-pc-windows-gnu", 952 | "winapi-x86_64-pc-windows-gnu", 953 | ] 954 | 955 | [[package]] 956 | name = "winapi-i686-pc-windows-gnu" 957 | version = "0.4.0" 958 | source = "registry+https://github.com/rust-lang/crates.io-index" 959 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 960 | 961 | [[package]] 962 | name = "winapi-x86_64-pc-windows-gnu" 963 | version = "0.4.0" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 966 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["compiler", "python"] 3 | 4 | [package] 5 | name = "dspython" 6 | version = "0.1.0" 7 | authors = ["탁동혁 "] 8 | edition = "2018" 9 | description = "Damn Small Python compiler" 10 | license = "MIT" 11 | repository = "https://github.com/tdh8316/dspython" 12 | 13 | [dependencies] 14 | clap = { git = "https://github.com/clap-rs/clap", tag = "v2.33.0" } 15 | dsp_compiler = { path = "./compiler" } 16 | dsp_python_codegen = { path = "./python/codegen" } 17 | dsp_python_parser = { path = "./python/parser" } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Donghyeok Tak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notice 2 | 3 | The new version of DSPython is now under the development in [dev branch](https://github.com/tdh8316/dspython/tree/dev)! 4 | 5 | # Warning 6 | 7 | DSPython is in the very initial stage of a development phase and should not be used in a production environment. 8 | 9 | You can browse the [examples](https://github.com/tdh8316/dspython/tree/master/examples) directory to learn how DSPython interacts with Arduino. 10 | 11 | > Are you disappointed? Please consider contributing! 12 | 13 | # DSPython - Damn Small Python 14 | > 🐍 Python compiler intended to use in Arduino. 15 | 16 | The [Micropython](https://github.com/micropython/micropython) project aims to put an implementation of Python 3 on microcontrollers, but it is not for Arduino. 17 | 18 | DSPython uses [LLVM](http://llvm.org/) to provide a way to compile programs written in the [Python programming language](https://www.python.org/). 19 | The generated LLVM bytecode is intended to be similar to C++'s. 20 | 21 | Accordingly, **DSPython is internally not a Python** at all. 22 | 23 | Here is an example program that blinks the built-in LED of Arduino Uno: 24 | ```python 25 | # Blink the built-in LED of Arduino Uno! 26 | from arduino import * 27 | 28 | def setup(): 29 | pin_mode(13, 1) 30 | 31 | def loop(): 32 | digital_write(13, 1) 33 | delay(1000) 34 | digital_write(13, 0) 35 | delay(1000) 36 | ``` 37 | 38 | To compile and upload this source, you can specify the serial port to upload by providing the `--upload-to` option. 39 | For example, this compiles and uploads the [blink example](https://github.com/tdh8316/dsp/tree/master/examples/Blink.py) to the Arduino: 40 | 41 | ``` 42 | dspython examples/Blink.py --upload-to YOUR_PORT 43 | ``` 44 | 45 | ## Supported boards 46 | Currently, All examples have been tested only on Arduino Uno. 47 | 48 | - [Arduino Uno](https://store.arduino.cc/usa/arduino-uno-rev3) 49 | 50 | ## Usage 51 | 52 | # Installation 53 | ## Requirements 54 | - LLVM 10 (include llvm-config) 55 | 56 | On Windows, the official LLVM releases do not contain many important components. 57 | You have to use [pre-built LLVM binary](https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz) built for [Ziglang](https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows) 58 | 59 | - Arduino IDE 60 | 61 | You have to set the environment variable named `ARDUINO_DIR` to your arduino IDE location. 62 | This is because DSPython requires Arduino standard headers, avr-gcc compiler, and avrdude. 63 | 64 | ## Building from source 65 | ## Installer packages 66 | 67 | # Contributing 68 | Contributions are more than welcome! 69 | 70 | This is my first project using LLVM and Rust language. 71 | Please share your opinions. Any ideas would be highly appreciated! 72 | 73 | ### Project goals 74 | - Damn small binary size 75 | - Support Arduino or microcontrollers 76 | - Programming Arduino with seemingly Python-like language 77 | ### Neutral 78 | These are not impossible, but currently not our goals. 79 | - Compile to other platforms 80 | - Garbage collector 81 | - Class and inheritance 82 | ### Never 83 | - Complete Python implementation 84 | - Compile all python standard libraries 85 | - Support threading or asynchronous functions 86 | 87 | ## The reason this project exists 88 | I wanted to program Arduino in other languages as well as C++ and thought the Python language would be a good choice. 89 | That's why I decided to make a Python compiler that is available to upload directly to the Arduino. 90 | 91 | The distinctive feature of DSP is that it uses LLVM internally instead of emitting [C++](https://arduino.github.io/arduino-cli/sketch-build-process/). 92 | 93 | # License 94 | Licensed under the MIT License 95 | 96 | Copyright 2022 `Donghyeok Tak` 97 | 98 | # Credit 99 | - The python parser is based on [RustPython](https://github.com/RustPython/RustPython) 100 | - Value handler from [testlang](https://github.com/AcrylicShrimp/testlang-rust/) 101 | - LLVM binding for rust is [Inkwell](https://github.com/TheDan64/inkwell) 102 | 103 | -------------------------------------------------------------------------------- /arduino/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides access to built-in Arduino functions. 3 | 4 | This file contains the signature of the built-in functions 5 | so that the IDEs can provide intelligent code completion, and 6 | DSPython compiler will not compile this file itself 7 | while it compiles the others. 8 | 9 | The real implementation of these function can be found 10 | in the rest files located in this directory. 11 | Especially, the low-level functions are defined in the wrapper file, 12 | which is located in the `include` directory. 13 | """ 14 | 15 | from typing import Union 16 | 17 | from arduino.constants import * 18 | from arduino.uno_pins import * 19 | 20 | 21 | # noinspection PyShadowingBuiltins 22 | def print(_str: object) -> None: 23 | ... 24 | 25 | 26 | def println(_str: object) -> None: 27 | ... 28 | 29 | 30 | def pin_mode(_pin: int, _mode: int) -> None: 31 | ... 32 | 33 | 34 | def pulse_in(_pin: int, _state: int) -> float: 35 | ... 36 | 37 | 38 | def delay(_milliseconds: int) -> None: 39 | ... 40 | 41 | 42 | def is_serial_available() -> bool: 43 | ... 44 | 45 | 46 | def serial_begin(_baudrate: int) -> None: 47 | ... 48 | 49 | 50 | # noinspection PyShadowingBuiltins 51 | def input() -> int: 52 | ... 53 | 54 | 55 | def flush() -> None: 56 | ... 57 | 58 | 59 | def digital_write(_pin: int, _level: int) -> None: 60 | ... 61 | 62 | 63 | def analog_write(_pin: int, _level: int) -> None: 64 | ... 65 | 66 | 67 | def digital_read(_pin: int) -> int: 68 | ... 69 | 70 | 71 | def analog_read(_pin: int) -> int: 72 | ... 73 | 74 | 75 | def radians(_deg: Union[int, float]) -> float: 76 | ... 77 | 78 | 79 | def degrees(_rad: Union[int, float]) -> float: 80 | ... 81 | 82 | 83 | def sin(_rad: Union[int, float]) -> float: 84 | ... 85 | 86 | 87 | def cos(_rad: Union[int, float]) -> float: 88 | ... 89 | 90 | 91 | def tan(_rad: Union[int, float]) -> float: 92 | ... 93 | -------------------------------------------------------------------------------- /arduino/constants.py: -------------------------------------------------------------------------------- 1 | HIGH = 0x1 2 | LOW = 0x0 3 | 4 | INPUT = 0x0 5 | OUTPUT = 0x1 6 | INPUT_PULLUP = 0x2 7 | 8 | PI = 3.1415926535897932384626433832795 9 | HALF_PI = 1.5707963267948966192313216916398 10 | TWO_PI = 6.283185307179586476925286766559 11 | DEG_TO_RAD = 0.017453292519943295769236907684886 12 | RAD_TO_DEG = 57.295779513082320876798154814105 13 | EULER = 2.718281828459045235360287471352 14 | 15 | SERIAL = 0x0 16 | DISPLAY = 0x1 17 | 18 | LSBFIRST = 0 19 | MSBFIRST = 1 20 | 21 | CHANGE = 1 22 | FALLING = 2 23 | RISING = 3 24 | -------------------------------------------------------------------------------- /arduino/math.py: -------------------------------------------------------------------------------- 1 | from arduino.constants import DEG_TO_RAD, RAD_TO_DEG 2 | 3 | 4 | def abs__i__(x: int) -> int: 5 | if x > 0: 6 | return x 7 | else: 8 | return 0-x 9 | 10 | 11 | def abs__f__(x: float) -> float: 12 | if x > 0.0: 13 | return x 14 | else: 15 | return 0.0-x 16 | 17 | 18 | def min__i__(a: int, b: int) -> int: 19 | if a < b: 20 | return a 21 | else: 22 | return b 23 | 24 | 25 | def min__f__(a: float, b: float) -> float: 26 | if a < b: 27 | return a 28 | else: 29 | return b 30 | 31 | 32 | def max__i__(a: int, b: int) -> int: 33 | if a > b: 34 | return a 35 | else: 36 | return b 37 | 38 | 39 | def max__f__(a: float, b: float) -> float: 40 | if a > b: 41 | return a 42 | else: 43 | return b 44 | 45 | 46 | def radians__i__(deg: int) -> float: 47 | return deg * DEG_TO_RAD 48 | 49 | 50 | def radians__f__(deg: float) -> float: 51 | return deg * DEG_TO_RAD 52 | 53 | 54 | def degrees__i__(rad: int) -> float: 55 | return rad * RAD_TO_DEG 56 | 57 | 58 | def degrees__f__(rad: float) -> float: 59 | return rad * RAD_TO_DEG 60 | -------------------------------------------------------------------------------- /arduino/uno_pins.py: -------------------------------------------------------------------------------- 1 | def analog_input_to_digital_pin(p: int) -> int: 2 | if p < 6: 3 | return p + 14 4 | else: 5 | return -1 6 | 7 | 8 | def digital_pin_to_interrupt(p: int) -> int: 9 | if p == 2: 10 | return 0 11 | elif p == 3: 12 | return 1 13 | 14 | # Not an interrupt 15 | return -1 16 | 17 | 18 | SS = 10 19 | MOSI = 11 20 | MISO = 12 21 | SCK = 13 22 | 23 | SDA = 18 24 | SCL = 19 25 | 26 | LED_BUILTIN = 13 27 | 28 | A0 = 14 29 | A1 = 15 30 | A2 = 16 31 | A3 = 17 32 | A4 = 18 33 | A5 = 19 34 | A6 = 20 35 | A7 = 21 36 | -------------------------------------------------------------------------------- /compiler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsp_compiler" 3 | version = "0.1.0" 4 | authors = ["탁동혁 "] 5 | edition = "2018" 6 | description = "DSPython compiler" 7 | license = "MIT" 8 | repository = "https://github.com/tdh8316/dspython" 9 | 10 | [dependencies] 11 | dsp_compiler_error = { path = "./error" } 12 | dsp_compiler_value = { path = "./value" } 13 | dsp_python_codegen = { path = "../python/codegen" } 14 | dsp_python_parser = { path = "../python/parser" } 15 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm10-0" } 16 | -------------------------------------------------------------------------------- /compiler/error/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsp_compiler_error" 3 | version = "0.1.0" 4 | authors = ["탁동혁 "] 5 | edition = "2018" 6 | description = "Compiler error implementation" 7 | license = "MIT" 8 | repository = "https://github.com/tdh8316/dspython" 9 | 10 | [dependencies] 11 | dsp_python_parser = { path = "../../python/parser" } 12 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm10-0" } 13 | -------------------------------------------------------------------------------- /compiler/error/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt; 3 | 4 | use dsp_python_parser::ast; 5 | 6 | pub mod types; 7 | pub use crate::types::LLVMCompileErrorType; 8 | 9 | pub mod macros; 10 | pub use crate::macros::*; 11 | 12 | // These errors are not compatible with the parsing errors 13 | #[derive(Debug)] 14 | pub struct LLVMCompileError { 15 | pub error: LLVMCompileErrorType, 16 | pub location: Option, 17 | pub file: Option, 18 | } 19 | 20 | impl LLVMCompileError { 21 | pub fn new(location: Option, exception: LLVMCompileErrorType) -> Self { 22 | LLVMCompileError { 23 | error: exception, 24 | location, 25 | file: None, 26 | } 27 | } 28 | } 29 | 30 | impl Error for LLVMCompileError {} 31 | 32 | impl fmt::Display for LLVMCompileError { 33 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 34 | let error_desc = match &self.error { 35 | LLVMCompileErrorType::NameError(target) => format!("name '{}' is not defined", target), 36 | LLVMCompileErrorType::SyntaxError(desc) => format!("{}", desc), 37 | LLVMCompileErrorType::TypeError(expected, but) => { 38 | format!("Expected '{}', but found '{}'", expected, but) 39 | } 40 | LLVMCompileErrorType::NotImplemented(desc) => format!("{}", desc), 41 | }; 42 | 43 | let loc_string = if let Some(loc) = self.location { 44 | format!( 45 | "File '{}', line {}:{}", 46 | self.file.as_ref().unwrap_or(&"".to_string()), 47 | loc.row(), 48 | loc.column() 49 | ) 50 | } else { 51 | format!( 52 | "File '{}', line :", 53 | self.file.as_ref().unwrap_or(&"".to_string()) 54 | ) 55 | }; 56 | 57 | eprintln!("Traceback (most recent call last):"); 58 | eprintln!(" {}", loc_string); 59 | eprintln!("{}: {}", &self.error.to_string(), error_desc); 60 | 61 | write!( 62 | f, 63 | "LLVMCompileError: at {} -> {}: {}", 64 | loc_string, 65 | &self.error.to_string(), 66 | error_desc 67 | ) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /compiler/error/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! err { 3 | ($self:ident, $e:expr, $desc:expr) => {{ 4 | Err(LLVMCompileError::new( 5 | Some($self.get_loc()), 6 | $e($desc.to_string()), 7 | )) 8 | }}; 9 | ($self:ident, $e:expr, $s1:expr, $s2:expr) => {{ 10 | Err(LLVMCompileError::new( 11 | Some($self.get_loc()), 12 | $e($s1.to_string(), $s2.to_string()), 13 | )) 14 | }}; 15 | } 16 | -------------------------------------------------------------------------------- /compiler/error/src/types.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | // Errors that can occur during emitting LLVM IR 4 | #[derive(Debug, PartialEq)] 5 | pub enum LLVMCompileErrorType { 6 | // name '{}' is not defined 7 | NameError(String), 8 | 9 | SyntaxError(String), 10 | 11 | // Expected '{}', but found '{}' 12 | TypeError(String, String), 13 | 14 | NotImplemented(String), 15 | } 16 | 17 | impl fmt::Display for LLVMCompileErrorType { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 19 | let this = format!("{:?}", self); 20 | write!(f, "{}", this.split("(").collect::>()[0]) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compiler/mangler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsp_compiler_mangler" 3 | version = "0.1.0" 4 | authors = ["탁동혁 "] 5 | edition = "2018" 6 | description = "Name mangler" 7 | license = "MIT" 8 | repository = "https://github.com/tdh8316/dspython" 9 | 10 | [dependencies] 11 | dsp_compiler_value = { path = "../value" } 12 | -------------------------------------------------------------------------------- /compiler/mangler/src/lib.rs: -------------------------------------------------------------------------------- 1 | use dsp_compiler_value::value::ValueType; 2 | 3 | pub fn get_mangled_name(origin: &String, at: ValueType) -> String { 4 | let origin = &mut origin.clone(); 5 | 6 | origin.push_str(match at { 7 | ValueType::Str => "__s__", 8 | ValueType::I8 | ValueType::I16 | ValueType::Bool => "__i__", 9 | ValueType::F32 => "__f__", 10 | _ => "", 11 | }); 12 | 13 | { 14 | origin.clone() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /compiler/src/flags.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct CompilerFlags { 3 | pub optimization_level: u8, 4 | } 5 | 6 | impl CompilerFlags { 7 | pub fn new(optimization_level: u8) -> Self { 8 | CompilerFlags { optimization_level } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /compiler/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{read_dir, read_to_string}; 2 | use std::io::Write; 3 | 4 | use inkwell::builder::Builder; 5 | use inkwell::context::Context; 6 | use inkwell::module::Module; 7 | use inkwell::passes::{PassManager, PassManagerBuilder}; 8 | use inkwell::support::LLVMString; 9 | use inkwell::targets::{TargetData, TargetTriple}; 10 | use inkwell::OptimizationLevel; 11 | 12 | use dsp_compiler_error::{LLVMCompileError, LLVMCompileErrorType}; 13 | use dsp_python_codegen::{get_doc, CodeGen}; 14 | use dsp_python_parser::parser::parse_program; 15 | use dsp_python_parser::{ast, CompileError}; 16 | 17 | pub use crate::flags::*; 18 | use crate::llvm_prototypes::generate_prototypes; 19 | 20 | pub mod flags; 21 | mod llvm_prototypes; 22 | 23 | type CompileResult = Result; 24 | 25 | pub struct Compiler<'a, 'ctx> { 26 | pub source_path: String, 27 | pub compiler_flags: CompilerFlags, 28 | 29 | codegen: CodeGen<'a, 'ctx>, 30 | pass_manager: PassManager>, 31 | program: ast::Program, 32 | } 33 | 34 | impl<'a, 'ctx> Compiler<'a, 'ctx> { 35 | pub fn new( 36 | source_path: String, 37 | compiler_flags: CompilerFlags, 38 | context: &'ctx Context, 39 | builder: &'a Builder<'ctx>, 40 | module: &'a Module<'ctx>, 41 | pass_manager: PassManager>, 42 | program: ast::Program, 43 | ) -> Self { 44 | Compiler { 45 | source_path, 46 | compiler_flags, 47 | codegen: CodeGen::new(context, builder, module), 48 | pass_manager, 49 | program, 50 | } 51 | } 52 | 53 | /// Compile main program 54 | pub fn compile(&mut self) -> CompileResult<()> { 55 | let (statements, _doc_string) = get_doc(&self.program.statements); 56 | 57 | for statement in statements.iter() { 58 | if let ast::StatementType::Expression { ref expression } = statement.node { 59 | self.codegen.emit_expr(&expression)?; 60 | } else { 61 | self.codegen.emit_stmt(&statement)?; 62 | } 63 | } 64 | Ok(()) 65 | } 66 | 67 | /// Compile the given module 68 | pub fn compile_module(&mut self, module: ast::Program) -> CompileResult<()> { 69 | let (statements, _doc_string) = get_doc(&module.statements); 70 | 71 | for statement in statements.iter() { 72 | if let ast::StatementType::Expression { ref expression } = statement.node { 73 | self.codegen.emit_expr(&expression)?; 74 | } else { 75 | self.codegen.emit_stmt(&statement)?; 76 | } 77 | } 78 | Ok(()) 79 | } 80 | 81 | /// Include and compile standard DSPython Arduino libraries 82 | pub fn include_stdlib(&mut self) -> CompileResult<()> { 83 | let libs = read_dir("./arduino/") 84 | .expect("Failed to load DSPython Arduino core libraries") 85 | .map(|res| res.map(|entry| entry.path())) 86 | .collect::, std::io::Error>>() 87 | .unwrap(); 88 | 89 | for lib in libs.iter() { 90 | let lib = lib.to_str().unwrap(); 91 | // Skip `__init__.py` which is for linter 92 | if lib.contains("__init__.py") { 93 | continue; 94 | } 95 | 96 | let to_compile_error = 97 | |parse_error| CompileError::from_parse_error(parse_error, lib.to_string()); 98 | 99 | let source = 100 | read_to_string(&lib).expect(&format!("dspython: can't open file '{}'", lib)); 101 | let module_ast = match parse_program(&source).map_err(to_compile_error) { 102 | Err(e) => { 103 | eprintln!("fatal: Could not include standard libraries"); 104 | eprintln!( 105 | "An unhandled exception occurred during parsing '{}'", 106 | std::path::PathBuf::from(&e.source_path) 107 | .canonicalize() 108 | .unwrap() 109 | .display() 110 | ); 111 | panic!("ParseError: {}", e); 112 | } 113 | Ok(module) => module, 114 | }; 115 | if let Err(mut e) = self.compile_module(module_ast) { 116 | // Enrich error 117 | e.file = Some(lib.to_string()); 118 | 119 | return Err(e); 120 | } 121 | } 122 | 123 | Ok(()) 124 | } 125 | 126 | pub fn run_pm(&self) { 127 | self.pass_manager.run_on(&self.codegen.module); 128 | } 129 | 130 | #[inline] 131 | pub fn emit(&self) -> LLVMString { 132 | self.codegen.module.print_to_string() 133 | } 134 | } 135 | 136 | /// Compile given source and return the LLVM assembly object 137 | pub fn get_assembly(source_path: String, flags: CompilerFlags) -> CompileResult { 138 | std::io::stdout().flush().unwrap_or_default(); 139 | 140 | // Yield parse error to compile error 141 | let to_compile_error = 142 | |parse_error| CompileError::from_parse_error(parse_error, source_path.clone()); 143 | 144 | let context = Context::create(); 145 | let module = context.create_module(&source_path); 146 | 147 | // Create target data structure for Arduino 148 | let target_data = TargetData::create("e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"); 149 | module.set_data_layout(&target_data.get_data_layout()); 150 | 151 | // LLVM triple 152 | module.set_triple(&TargetTriple::create("avr")); 153 | 154 | // Create a root builder context 155 | let builder = context.create_builder(); 156 | 157 | // Initialize pass manager 158 | let pass_manager: PassManager = PassManager::create(()); 159 | let pm_builder = PassManagerBuilder::create(); 160 | pm_builder.set_optimization_level(match flags.optimization_level { 161 | 0 => OptimizationLevel::None, 162 | 1 => OptimizationLevel::Less, 163 | 2 => OptimizationLevel::Default, 164 | 3 => OptimizationLevel::Aggressive, 165 | _ => { 166 | return Err(LLVMCompileError::new( 167 | None, 168 | LLVMCompileErrorType::NotImplemented( 169 | "The optimization level must be an integer from 0 to 3.".to_string(), 170 | ), 171 | )); 172 | } 173 | }); 174 | 175 | // -Os 176 | pm_builder.set_size_level(1); 177 | pm_builder.populate_module_pass_manager(&pass_manager); 178 | 179 | let source = read_to_string(&source_path) 180 | .expect(&format!("dspython: can't open file '{}'", source_path)); 181 | let ast = match parse_program(&source).map_err(to_compile_error) { 182 | Err(e) => { 183 | eprintln!( 184 | "An unhandled exception occurred during parsing '{}'", 185 | std::path::PathBuf::from(&e.source_path) 186 | .canonicalize() 187 | .unwrap() 188 | .display() 189 | ); 190 | panic!("ParseError: {}", e); 191 | } 192 | Ok(program) => program, 193 | }; 194 | 195 | // Create Compiler instance 196 | let mut compiler = Compiler::new( 197 | source_path.clone(), 198 | flags, 199 | &context, 200 | &builder, 201 | &module, 202 | pass_manager, 203 | ast, 204 | ); 205 | 206 | generate_prototypes(compiler.codegen.module, compiler.codegen.context); 207 | compiler.include_stdlib()?; 208 | if let Err(mut e) = compiler.compile() { 209 | // Enrich error 210 | e.file = Some(compiler.source_path); 211 | 212 | return Err(e); 213 | } 214 | 215 | compiler.run_pm(); 216 | // println!("[Done]"); 217 | { 218 | Ok(compiler.emit()) 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /compiler/src/llvm_prototypes.rs: -------------------------------------------------------------------------------- 1 | use inkwell::context::Context; 2 | use inkwell::module::Module; 3 | use inkwell::AddressSpace; 4 | 5 | // TODO: Generate prototypes using included files 6 | pub fn generate_prototypes<'a, 'ctx>(module: &'a Module<'ctx>, context: &'ctx Context) { 7 | // Arduino builtins 8 | module.add_function( 9 | "pin_mode", 10 | context 11 | .void_type() 12 | .fn_type(&[context.i8_type().into(), context.i8_type().into()], false), 13 | None, 14 | ); 15 | module.add_function( 16 | "pulse_in", 17 | context 18 | .f32_type() 19 | .fn_type(&[context.i8_type().into(), context.i8_type().into()], false), 20 | None, 21 | ); 22 | module.add_function( 23 | "is_serial_available", 24 | context.bool_type().fn_type(&[], false), 25 | None, 26 | ); 27 | module.add_function( 28 | "serial_begin", 29 | context 30 | .void_type() 31 | .fn_type(&[context.i16_type().into()], false), 32 | None, 33 | ); 34 | module.add_function("input", context.i16_type().fn_type(&[], false), None); 35 | module.add_function("flush", context.void_type().fn_type(&[], false), None); 36 | module.add_function( 37 | "delay", 38 | context 39 | .void_type() 40 | .fn_type(&[context.i32_type().into()], false), 41 | None, 42 | ); 43 | module.add_function( 44 | "digital_write", 45 | context 46 | .void_type() 47 | .fn_type(&[context.i8_type().into(), context.i8_type().into()], false), 48 | None, 49 | ); 50 | module.add_function( 51 | "analog_write", 52 | context 53 | .void_type() 54 | .fn_type(&[context.i8_type().into(), context.i8_type().into()], false), 55 | None, 56 | ); 57 | module.add_function( 58 | "digital_read", 59 | context 60 | .i16_type() 61 | .fn_type(&[context.i8_type().into()], false), 62 | None, 63 | ); 64 | module.add_function( 65 | "analog_read", 66 | context 67 | .i16_type() 68 | .fn_type(&[context.i8_type().into()], false), 69 | None, 70 | ); 71 | 72 | // Math 73 | module.add_function( 74 | "sin", 75 | context 76 | .f32_type() 77 | .fn_type(&[context.f32_type().into()], false), 78 | None, 79 | ); 80 | module.add_function( 81 | "cos", 82 | context 83 | .f32_type() 84 | .fn_type(&[context.f32_type().into()], false), 85 | None, 86 | ); 87 | module.add_function( 88 | "tan", 89 | context 90 | .f32_type() 91 | .fn_type(&[context.f32_type().into()], false), 92 | None, 93 | ); 94 | 95 | // Python builtins 96 | module.add_function( 97 | "print__i__", 98 | context 99 | .void_type() 100 | .fn_type(&[context.i16_type().into()], false), 101 | None, 102 | ); 103 | module.add_function( 104 | "print__f__", 105 | context 106 | .void_type() 107 | .fn_type(&[context.f32_type().into()], false), 108 | None, 109 | ); 110 | module.add_function( 111 | "print__s__", 112 | context.void_type().fn_type( 113 | &[context.i8_type().ptr_type(AddressSpace::Generic).into()], 114 | false, 115 | ), 116 | None, 117 | ); 118 | module.add_function( 119 | "println__i__", 120 | context 121 | .void_type() 122 | .fn_type(&[context.i16_type().into()], false), 123 | None, 124 | ); 125 | module.add_function( 126 | "println__f__", 127 | context 128 | .void_type() 129 | .fn_type(&[context.f32_type().into()], false), 130 | None, 131 | ); 132 | module.add_function( 133 | "println__s__", 134 | context.void_type().fn_type( 135 | &[context.i8_type().ptr_type(AddressSpace::Generic).into()], 136 | false, 137 | ), 138 | None, 139 | ); 140 | module.add_function( 141 | "int__f__", 142 | context 143 | .i16_type() 144 | .fn_type(&[context.f32_type().into()], false), 145 | None, 146 | ); 147 | module.add_function( 148 | "int__i__", 149 | context 150 | .i16_type() 151 | .fn_type(&[context.i16_type().into()], false), 152 | None, 153 | ); 154 | module.add_function( 155 | "float__f__", 156 | context 157 | .f32_type() 158 | .fn_type(&[context.f32_type().into()], false), 159 | None, 160 | ); 161 | module.add_function( 162 | "float__i__", 163 | context 164 | .f32_type() 165 | .fn_type(&[context.i16_type().into()], false), 166 | None, 167 | ); 168 | } 169 | -------------------------------------------------------------------------------- /compiler/value/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsp_compiler_value" 3 | version = "0.1.0" 4 | authors = ["탁동혁 "] 5 | edition = "2018" 6 | description = "DSPython compiler value" 7 | license = "MIT" 8 | repository = "https://github.com/tdh8316/dspython" 9 | 10 | [dependencies] 11 | dsp_python_parser = { path = "../../python/parser" } 12 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm10-0" } 13 | num-bigint = "0.3" 14 | num-complex = { version = "0.3", features = ["serde"] } 15 | num-traits = "0.2" 16 | -------------------------------------------------------------------------------- /compiler/value/src/convert.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | 3 | use num_bigint::{BigInt, BigUint}; 4 | use num_traits::{Signed, ToPrimitive}; 5 | 6 | use dsp_python_parser::ast; 7 | 8 | pub fn truncate_bigint_to_u64(a: &BigInt) -> u64 { 9 | fn truncate_biguint_to_u64(a: &BigUint) -> u64 { 10 | use std::u64; 11 | let mask = BigUint::from(u64::MAX); 12 | (a & mask.borrow()).to_u64().unwrap() 13 | } 14 | let was_negative = a.is_negative(); 15 | let abs = a.abs().to_biguint().unwrap(); 16 | let truncated = truncate_biguint_to_u64(&abs); 17 | if was_negative { 18 | truncated.wrapping_neg() 19 | } else { 20 | truncated 21 | } 22 | } 23 | 24 | pub fn try_get_constant_string(string: &ast::StringGroup) -> Option { 25 | fn get_constant_string_inner(out_string: &mut String, string: &ast::StringGroup) -> bool { 26 | match string { 27 | ast::StringGroup::Constant { value } => { 28 | out_string.push_str(&value); 29 | true 30 | } 31 | ast::StringGroup::Joined { values } => values 32 | .iter() 33 | .all(|value| get_constant_string_inner(out_string, value)), 34 | ast::StringGroup::FormattedValue { .. } => false, 35 | } 36 | } 37 | let mut out_string = String::new(); 38 | if get_constant_string_inner(&mut out_string, string) { 39 | Some(out_string) 40 | } else { 41 | None 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /compiler/value/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod convert; 2 | pub mod value; 3 | -------------------------------------------------------------------------------- /compiler/value/src/value.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | 3 | use inkwell::context::Context; 4 | use inkwell::types::{AnyTypeEnum, BasicTypeEnum, FloatType, IntType, PointerType, VoidType}; 5 | use inkwell::values::{ 6 | AnyValueEnum, ArrayValue, BasicValueEnum, FloatValue, IntValue, PointerValue, 7 | }; 8 | use inkwell::AddressSpace; 9 | 10 | #[derive(Debug, PartialEq, Clone, Copy)] 11 | pub enum Value<'ctx> { 12 | Void, 13 | Array { value: ArrayValue<'ctx> }, 14 | Bool { value: IntValue<'ctx> }, 15 | I8 { value: IntValue<'ctx> }, 16 | I16 { value: IntValue<'ctx> }, 17 | I32 { value: IntValue<'ctx> }, 18 | I64 { value: IntValue<'ctx> }, 19 | I128 { value: IntValue<'ctx> }, 20 | U8 { value: IntValue<'ctx> }, 21 | U16 { value: IntValue<'ctx> }, 22 | U32 { value: IntValue<'ctx> }, 23 | U64 { value: IntValue<'ctx> }, 24 | U128 { value: IntValue<'ctx> }, 25 | F16 { value: FloatValue<'ctx> }, 26 | F32 { value: FloatValue<'ctx> }, 27 | F64 { value: FloatValue<'ctx> }, 28 | Str { value: PointerValue<'ctx> }, 29 | } 30 | 31 | #[derive(Debug, PartialEq, Copy, Clone)] 32 | pub enum ValueType { 33 | Void, 34 | Array, 35 | Bool, 36 | I8, 37 | I16, 38 | I32, 39 | I64, 40 | I128, 41 | U8, 42 | U16, 43 | U32, 44 | U64, 45 | U128, 46 | F16, 47 | F32, 48 | F64, 49 | Str, 50 | } 51 | 52 | #[derive(Debug, PartialEq)] 53 | pub enum ValueTypeGroup { 54 | Void, 55 | Array, 56 | Bool, 57 | Int, 58 | UInt, 59 | Float, 60 | Str, 61 | } 62 | 63 | pub struct ValueHandler<'cb, 'ctx: 'cb, T> { 64 | void_handler: &'cb dyn Fn(&Value<'ctx>) -> T, 65 | array_handler: &'cb dyn Fn(&Value<'ctx>, ArrayValue<'ctx>) -> T, 66 | bool_handler: &'cb dyn Fn(&Value<'ctx>, IntValue<'ctx>) -> T, 67 | int_handler: &'cb dyn Fn(&Value<'ctx>, IntValue<'ctx>) -> T, 68 | unsigned_int_handler: &'cb dyn Fn(&Value<'ctx>, IntValue<'ctx>) -> T, 69 | float_handler: &'cb dyn Fn(&Value<'ctx>, FloatValue<'ctx>) -> T, 70 | str_handler: &'cb dyn Fn(&Value<'ctx>, PointerValue<'ctx>) -> T, 71 | } 72 | 73 | pub struct ValueTypeHandler<'cb, 'ctx: 'cb, T> { 74 | void_handler: &'cb dyn Fn(&ValueType, VoidType<'ctx>) -> T, 75 | array_handler: &'cb dyn Fn(&ValueType, ArrayValue<'ctx>) -> T, 76 | bool_handler: &'cb dyn Fn(&ValueType, IntType<'ctx>) -> T, 77 | int_handler: &'cb dyn Fn(&ValueType, IntType<'ctx>) -> T, 78 | unsigned_int_handler: &'cb dyn Fn(&ValueType, IntType<'ctx>) -> T, 79 | float_handler: &'cb dyn Fn(&ValueType, FloatType<'ctx>) -> T, 80 | str_handler: &'cb dyn Fn(&ValueType, PointerType<'ctx>) -> T, 81 | } 82 | 83 | impl<'cb, 'ctx: 'cb, T> ValueHandler<'cb, 'ctx, T> { 84 | pub fn new() -> ValueHandler<'cb, 'ctx, T> { 85 | ValueHandler { 86 | void_handler: &|_| panic!("wrong type; void type is not allowed."), 87 | array_handler: &|_, _| panic!("wrong type; array type is not allowed."), 88 | bool_handler: &|_, _| panic!("wrong type; bool type is not allowed."), 89 | int_handler: &|_, _| panic!("wrong type; int type is not allowed."), 90 | unsigned_int_handler: &|_, _| panic!("wrong type; unsigned int type is not allowed."), 91 | float_handler: &|_, _| panic!("wrong type; float type is not allowed."), 92 | str_handler: &|_, _| panic!("wrong type; str type is not allowed."), 93 | } 94 | } 95 | 96 | pub fn handle_void(&mut self, handler: &'cb dyn Fn(&Value<'ctx>) -> T) -> &mut Self { 97 | self.void_handler = handler; 98 | self 99 | } 100 | 101 | pub fn handle_bool( 102 | &mut self, 103 | handler: &'cb dyn Fn(&Value<'ctx>, IntValue<'ctx>) -> T, 104 | ) -> &mut Self { 105 | self.bool_handler = handler; 106 | self 107 | } 108 | 109 | pub fn handle_int( 110 | &mut self, 111 | handler: &'cb dyn Fn(&Value<'ctx>, IntValue<'ctx>) -> T, 112 | ) -> &mut Self { 113 | self.int_handler = handler; 114 | self 115 | } 116 | 117 | pub fn handle_unsigned_int( 118 | &mut self, 119 | handler: &'cb dyn Fn(&Value<'ctx>, IntValue<'ctx>) -> T, 120 | ) -> &mut Self { 121 | self.unsigned_int_handler = handler; 122 | self 123 | } 124 | 125 | pub fn handle_float( 126 | &mut self, 127 | handler: &'cb dyn Fn(&Value<'ctx>, FloatValue<'ctx>) -> T, 128 | ) -> &mut Self { 129 | self.float_handler = handler; 130 | self 131 | } 132 | 133 | pub fn handle_str( 134 | &mut self, 135 | handler: &'cb dyn Fn(&Value<'ctx>, PointerValue<'ctx>) -> T, 136 | ) -> &mut Self { 137 | self.str_handler = handler; 138 | self 139 | } 140 | } 141 | 142 | impl<'cb, 'ctx: 'cb, T> ValueTypeHandler<'cb, 'ctx, T> { 143 | pub fn new() -> ValueTypeHandler<'cb, 'ctx, T> { 144 | ValueTypeHandler { 145 | void_handler: &|_, _| panic!("wrong type; void type is not allowed."), 146 | array_handler: &|_, _| panic!("wrong type; array type is not allowed."), 147 | bool_handler: &|_, _| panic!("wrong type; bool type is not allowed."), 148 | int_handler: &|_, _| panic!("wrong type; int type is not allowed."), 149 | unsigned_int_handler: &|_, _| panic!("wrong type; unsigned int type is not allowed."), 150 | float_handler: &|_, _| panic!("wrong type; float type is not allowed."), 151 | str_handler: &|_, _| panic!("wrong type; str type is not allowed."), 152 | } 153 | } 154 | 155 | pub fn handle_void( 156 | &mut self, 157 | handler: &'cb dyn Fn(&ValueType, VoidType<'ctx>) -> T, 158 | ) -> &mut Self { 159 | self.void_handler = handler; 160 | self 161 | } 162 | 163 | pub fn handle_bool( 164 | &mut self, 165 | handler: &'cb dyn Fn(&ValueType, IntType<'ctx>) -> T, 166 | ) -> &mut Self { 167 | self.bool_handler = handler; 168 | self 169 | } 170 | 171 | pub fn handle_int( 172 | &mut self, 173 | handler: &'cb dyn Fn(&ValueType, IntType<'ctx>) -> T, 174 | ) -> &mut Self { 175 | self.int_handler = handler; 176 | self 177 | } 178 | 179 | pub fn handle_unsigned_int( 180 | &mut self, 181 | handler: &'cb dyn Fn(&ValueType, IntType<'ctx>) -> T, 182 | ) -> &mut Self { 183 | self.unsigned_int_handler = handler; 184 | self 185 | } 186 | 187 | pub fn handle_float( 188 | &mut self, 189 | handler: &'cb dyn Fn(&ValueType, FloatType<'ctx>) -> T, 190 | ) -> &mut Self { 191 | self.float_handler = handler; 192 | self 193 | } 194 | 195 | pub fn handle_str( 196 | &mut self, 197 | handler: &'cb dyn Fn(&ValueType, PointerType<'ctx>) -> T, 198 | ) -> &mut Self { 199 | self.str_handler = handler; 200 | self 201 | } 202 | } 203 | 204 | impl<'ctx> Value<'ctx> { 205 | pub fn get_type(&self) -> ValueType { 206 | match self { 207 | Value::Void => ValueType::Void, 208 | Value::Array { value: _ } => ValueType::Array, 209 | Value::Bool { value: _ } => ValueType::Bool, 210 | Value::I8 { value: _ } => ValueType::I8, 211 | Value::I16 { value: _ } => ValueType::I16, 212 | Value::I32 { value: _ } => ValueType::I32, 213 | Value::I64 { value: _ } => ValueType::I64, 214 | Value::I128 { value: _ } => ValueType::I128, 215 | Value::U8 { value: _ } => ValueType::U8, 216 | Value::U16 { value: _ } => ValueType::U16, 217 | Value::U32 { value: _ } => ValueType::U32, 218 | Value::U64 { value: _ } => ValueType::U64, 219 | Value::U128 { value: _ } => ValueType::U128, 220 | Value::F16 { value: _ } => ValueType::F16, 221 | Value::F32 { value: _ } => ValueType::F32, 222 | Value::F64 { value: _ } => ValueType::F64, 223 | Value::Str { value: _ } => ValueType::Str, 224 | } 225 | } 226 | 227 | pub fn from_int_value(bitwidth: usize, value: IntValue<'ctx>) -> Value<'ctx> { 228 | match bitwidth { 229 | 8 => Value::I8 { value }, 230 | 16 => Value::I16 { value }, 231 | 32 => Value::I32 { value }, 232 | 64 => Value::I64 { value }, 233 | 128 => Value::I128 { value }, 234 | _ => unreachable!(), 235 | } 236 | } 237 | 238 | pub fn from_unsigned_int_value(bitwidth: usize, value: IntValue<'ctx>) -> Value<'ctx> { 239 | match bitwidth { 240 | 8 => Value::U8 { value }, 241 | 16 => Value::U16 { value }, 242 | 32 => Value::U32 { value }, 243 | 64 => Value::U64 { value }, 244 | 128 => Value::U128 { value }, 245 | _ => unreachable!(), 246 | } 247 | } 248 | 249 | pub fn from_float_value(bitwidth: usize, value: FloatValue<'ctx>) -> Value<'ctx> { 250 | match bitwidth { 251 | 16 => Value::F16 { value }, 252 | 32 => Value::F32 { value }, 253 | 64 => Value::F64 { value }, 254 | _ => unreachable!(), 255 | } 256 | } 257 | 258 | pub fn from_any_value(value_type: ValueType, any_value: AnyValueEnum<'ctx>) -> Value<'ctx> { 259 | match any_value { 260 | AnyValueEnum::IntValue(value) => match value_type { 261 | ValueType::Bool => Value::Bool { value }, 262 | ValueType::I8 => Value::I8 { value }, 263 | ValueType::I16 => Value::I16 { value }, 264 | ValueType::I32 => Value::I32 { value }, 265 | ValueType::I64 => Value::I64 { value }, 266 | ValueType::I128 => Value::I128 { value }, 267 | ValueType::U8 => Value::U8 { value }, 268 | ValueType::U16 => Value::U16 { value }, 269 | ValueType::U32 => Value::U32 { value }, 270 | ValueType::U64 => Value::U64 { value }, 271 | ValueType::U128 => Value::U128 { value }, 272 | _ => panic!( 273 | "value type mismatch; given value is not instance of {:?}", 274 | value_type 275 | ), 276 | }, 277 | AnyValueEnum::FloatValue(value) => match value_type { 278 | ValueType::F16 => Value::F16 { value }, 279 | ValueType::F32 => Value::F32 { value }, 280 | ValueType::F64 => Value::F64 { value }, 281 | _ => panic!( 282 | "value type mismatch; given value is not instance of {:?}", 283 | value_type 284 | ), 285 | }, 286 | AnyValueEnum::PointerValue(value) => match value_type { 287 | ValueType::Str => Value::Str { value }, 288 | _ => panic!( 289 | "value type mismatch; given value is not instance of {:?}", 290 | value_type 291 | ), 292 | }, 293 | AnyValueEnum::PhiValue(value) => { 294 | Value::from_basic_value(value_type, value.as_basic_value()) 295 | } 296 | _ => panic!("unexpected type encountered"), 297 | } 298 | } 299 | 300 | pub fn from_basic_value( 301 | value_type: ValueType, 302 | basic_value: BasicValueEnum<'ctx>, 303 | ) -> Value<'ctx> { 304 | match basic_value { 305 | BasicValueEnum::IntValue(value) => match value_type { 306 | ValueType::Bool => Value::Bool { value }, 307 | ValueType::I8 => Value::I8 { value }, 308 | ValueType::I16 => Value::I16 { value }, 309 | ValueType::I32 => Value::I32 { value }, 310 | ValueType::I64 => Value::I64 { value }, 311 | ValueType::I128 => Value::I128 { value }, 312 | ValueType::U8 => Value::U8 { value }, 313 | ValueType::U16 => Value::U16 { value }, 314 | ValueType::U32 => Value::U32 { value }, 315 | ValueType::U64 => Value::U64 { value }, 316 | ValueType::U128 => Value::U128 { value }, 317 | _ => panic!( 318 | "value type mismatch; given value is not instance of {:?}", 319 | value_type 320 | ), 321 | }, 322 | BasicValueEnum::FloatValue(value) => match value_type { 323 | ValueType::F16 => Value::F16 { value }, 324 | ValueType::F32 => Value::F32 { value }, 325 | ValueType::F64 => Value::F64 { value }, 326 | _ => panic!( 327 | "value type mismatch; given value is not instance of {:?}", 328 | value_type 329 | ), 330 | }, 331 | BasicValueEnum::PointerValue(value) => match value_type { 332 | ValueType::Str => Value::Str { value }, 333 | _ => panic!( 334 | "value type mismatch; given value is not instance of {:?}", 335 | value_type 336 | ), 337 | }, 338 | _ => panic!("unexpected type encountered"), 339 | } 340 | } 341 | 342 | pub fn to_any_value(&self) -> AnyValueEnum<'ctx> { 343 | match self { 344 | Value::Void => panic!("void is not acceptible"), 345 | Value::Array { value } => AnyValueEnum::ArrayValue(*value), 346 | Value::Bool { value } => AnyValueEnum::IntValue(*value), 347 | Value::I8 { value } => AnyValueEnum::IntValue(*value), 348 | Value::I16 { value } => AnyValueEnum::IntValue(*value), 349 | Value::I32 { value } => AnyValueEnum::IntValue(*value), 350 | Value::I64 { value } => AnyValueEnum::IntValue(*value), 351 | Value::I128 { value } => AnyValueEnum::IntValue(*value), 352 | Value::U8 { value } => AnyValueEnum::IntValue(*value), 353 | Value::U16 { value } => AnyValueEnum::IntValue(*value), 354 | Value::U32 { value } => AnyValueEnum::IntValue(*value), 355 | Value::U64 { value } => AnyValueEnum::IntValue(*value), 356 | Value::U128 { value } => AnyValueEnum::IntValue(*value), 357 | Value::F16 { value } => AnyValueEnum::FloatValue(*value), 358 | Value::F32 { value } => AnyValueEnum::FloatValue(*value), 359 | Value::F64 { value } => AnyValueEnum::FloatValue(*value), 360 | Value::Str { value } => AnyValueEnum::PointerValue(*value), 361 | } 362 | } 363 | 364 | pub fn to_basic_value(&self) -> BasicValueEnum<'ctx> { 365 | match self { 366 | Value::Void => panic!("void is not acceptible"), 367 | Value::Array { value } => BasicValueEnum::ArrayValue(*value), 368 | Value::Bool { value } => BasicValueEnum::IntValue(*value), 369 | Value::I8 { value } => BasicValueEnum::IntValue(*value), 370 | Value::I16 { value } => BasicValueEnum::IntValue(*value), 371 | Value::I32 { value } => BasicValueEnum::IntValue(*value), 372 | Value::I64 { value } => BasicValueEnum::IntValue(*value), 373 | Value::I128 { value } => BasicValueEnum::IntValue(*value), 374 | Value::U8 { value } => BasicValueEnum::IntValue(*value), 375 | Value::U16 { value } => BasicValueEnum::IntValue(*value), 376 | Value::U32 { value } => BasicValueEnum::IntValue(*value), 377 | Value::U64 { value } => BasicValueEnum::IntValue(*value), 378 | Value::U128 { value } => BasicValueEnum::IntValue(*value), 379 | Value::F16 { value } => BasicValueEnum::FloatValue(*value), 380 | Value::F32 { value } => BasicValueEnum::FloatValue(*value), 381 | Value::F64 { value } => BasicValueEnum::FloatValue(*value), 382 | Value::Str { value } => BasicValueEnum::PointerValue(*value), 383 | } 384 | } 385 | 386 | pub fn invoke_handler<'cb, T>(&self, value_handler: &mut ValueHandler<'cb, 'ctx, T>) -> T { 387 | match self { 388 | Value::Void => (*value_handler.void_handler)(self), 389 | Value::Array { value } => (*value_handler.array_handler)(self, *value), 390 | Value::Bool { value } => (*value_handler.bool_handler)(self, *value), 391 | Value::I8 { value } => (*value_handler.int_handler)(self, *value), 392 | Value::I16 { value } => (*value_handler.int_handler)(self, *value), 393 | Value::I32 { value } => (*value_handler.int_handler)(self, *value), 394 | Value::I64 { value } => (*value_handler.int_handler)(self, *value), 395 | Value::I128 { value } => (*value_handler.int_handler)(self, *value), 396 | Value::U8 { value } => (*value_handler.unsigned_int_handler)(self, *value), 397 | Value::U16 { value } => (*value_handler.unsigned_int_handler)(self, *value), 398 | Value::U32 { value } => (*value_handler.unsigned_int_handler)(self, *value), 399 | Value::U64 { value } => (*value_handler.unsigned_int_handler)(self, *value), 400 | Value::U128 { value } => (*value_handler.unsigned_int_handler)(self, *value), 401 | Value::F16 { value } => (*value_handler.float_handler)(self, *value), 402 | Value::F32 { value } => (*value_handler.float_handler)(self, *value), 403 | Value::F64 { value } => (*value_handler.float_handler)(self, *value), 404 | Value::Str { value } => (*value_handler.str_handler)(self, *value), 405 | } 406 | } 407 | } 408 | 409 | impl ValueType { 410 | pub fn is_void(&self) -> bool { 411 | match self { 412 | ValueType::Void => true, 413 | _ => false, 414 | } 415 | } 416 | 417 | pub fn to_any_type<'ctx>(&self, context: &'ctx Context) -> AnyTypeEnum<'ctx> { 418 | match self { 419 | ValueType::Void => AnyTypeEnum::VoidType(context.void_type()), 420 | ValueType::Array => unimplemented!(), 421 | ValueType::Bool => AnyTypeEnum::IntType(context.bool_type()), 422 | ValueType::I8 => AnyTypeEnum::IntType(context.i8_type()), 423 | ValueType::I16 => AnyTypeEnum::IntType(context.i16_type()), 424 | ValueType::I32 => AnyTypeEnum::IntType(context.i32_type()), 425 | ValueType::I64 => AnyTypeEnum::IntType(context.i64_type()), 426 | ValueType::I128 => AnyTypeEnum::IntType(context.i128_type()), 427 | ValueType::U8 => AnyTypeEnum::IntType(context.i8_type()), 428 | ValueType::U16 => AnyTypeEnum::IntType(context.i16_type()), 429 | ValueType::U32 => AnyTypeEnum::IntType(context.i32_type()), 430 | ValueType::U64 => AnyTypeEnum::IntType(context.i64_type()), 431 | ValueType::U128 => AnyTypeEnum::IntType(context.i128_type()), 432 | ValueType::F16 => AnyTypeEnum::FloatType(context.f16_type()), 433 | ValueType::F32 => AnyTypeEnum::FloatType(context.f32_type()), 434 | ValueType::F64 => AnyTypeEnum::FloatType(context.f64_type()), 435 | ValueType::Str => { 436 | AnyTypeEnum::PointerType(context.i8_type().ptr_type(AddressSpace::Generic)) 437 | } 438 | } 439 | } 440 | 441 | pub fn to_basic_type<'ctx>(&self, context: &'ctx Context) -> BasicTypeEnum<'ctx> { 442 | match self { 443 | ValueType::Void => panic!("void is not acceptible"), 444 | ValueType::Array => unimplemented!(), 445 | ValueType::Bool => BasicTypeEnum::IntType(context.bool_type()), 446 | ValueType::I8 => BasicTypeEnum::IntType(context.i8_type()), 447 | ValueType::I16 => BasicTypeEnum::IntType(context.i16_type()), 448 | ValueType::I32 => BasicTypeEnum::IntType(context.i32_type()), 449 | ValueType::I64 => BasicTypeEnum::IntType(context.i64_type()), 450 | ValueType::I128 => BasicTypeEnum::IntType(context.i128_type()), 451 | ValueType::U8 => BasicTypeEnum::IntType(context.i8_type()), 452 | ValueType::U16 => BasicTypeEnum::IntType(context.i16_type()), 453 | ValueType::U32 => BasicTypeEnum::IntType(context.i32_type()), 454 | ValueType::U64 => BasicTypeEnum::IntType(context.i64_type()), 455 | ValueType::U128 => BasicTypeEnum::IntType(context.i128_type()), 456 | ValueType::F16 => BasicTypeEnum::FloatType(context.f16_type()), 457 | ValueType::F32 => BasicTypeEnum::FloatType(context.f32_type()), 458 | ValueType::F64 => BasicTypeEnum::FloatType(context.f64_type()), 459 | ValueType::Str => { 460 | BasicTypeEnum::PointerType(context.i8_type().ptr_type(AddressSpace::Generic)) 461 | } 462 | } 463 | } 464 | 465 | pub fn get_group(&self) -> ValueTypeGroup { 466 | match self { 467 | ValueType::Void => ValueTypeGroup::Void, 468 | ValueType::Array => ValueTypeGroup::Array, 469 | ValueType::Bool => ValueTypeGroup::Bool, 470 | ValueType::I8 => ValueTypeGroup::Int, 471 | ValueType::I16 => ValueTypeGroup::Int, 472 | ValueType::I32 => ValueTypeGroup::Int, 473 | ValueType::I64 => ValueTypeGroup::Int, 474 | ValueType::I128 => ValueTypeGroup::Int, 475 | ValueType::U8 => ValueTypeGroup::UInt, 476 | ValueType::U16 => ValueTypeGroup::UInt, 477 | ValueType::U32 => ValueTypeGroup::UInt, 478 | ValueType::U64 => ValueTypeGroup::UInt, 479 | ValueType::U128 => ValueTypeGroup::UInt, 480 | ValueType::F16 => ValueTypeGroup::Float, 481 | ValueType::F32 => ValueTypeGroup::Float, 482 | ValueType::F64 => ValueTypeGroup::Float, 483 | ValueType::Str => ValueTypeGroup::Str, 484 | } 485 | } 486 | 487 | pub fn get_bitwidth(&self) -> usize { 488 | match self { 489 | ValueType::Void => 0, 490 | ValueType::Array => 0, // Unknown 491 | ValueType::Bool => 1, 492 | ValueType::I8 => 8, 493 | ValueType::I16 => 16, 494 | ValueType::I32 => 32, 495 | ValueType::I64 => 64, 496 | ValueType::I128 => 128, 497 | ValueType::U8 => 8, 498 | ValueType::U16 => 16, 499 | ValueType::U32 => 32, 500 | ValueType::U64 => 64, 501 | ValueType::U128 => 128, 502 | ValueType::F16 => 16, 503 | ValueType::F32 => 32, 504 | ValueType::F64 => 64, 505 | ValueType::Str => 0, //Unknown 506 | } 507 | } 508 | 509 | pub fn invoke_handler<'cb, 'ctx: 'cb, T>( 510 | &self, 511 | context: &'ctx Context, 512 | value_type_handler: &mut ValueTypeHandler<'cb, 'ctx, T>, 513 | ) -> T { 514 | match self { 515 | ValueType::Void => (*value_type_handler.void_handler)(self, context.void_type()), 516 | ValueType::Array => unimplemented!(), 517 | ValueType::Bool => (*value_type_handler.bool_handler)(self, context.bool_type()), 518 | ValueType::I8 => (*value_type_handler.int_handler)(self, context.i8_type()), 519 | ValueType::I16 => (*value_type_handler.int_handler)(self, context.i16_type()), 520 | ValueType::I32 => (*value_type_handler.int_handler)(self, context.i32_type()), 521 | ValueType::I64 => (*value_type_handler.int_handler)(self, context.i64_type()), 522 | ValueType::I128 => (*value_type_handler.int_handler)(self, context.i128_type()), 523 | ValueType::U8 => (*value_type_handler.unsigned_int_handler)(self, context.i8_type()), 524 | ValueType::U16 => (*value_type_handler.unsigned_int_handler)(self, context.i16_type()), 525 | ValueType::U32 => (*value_type_handler.unsigned_int_handler)(self, context.i32_type()), 526 | ValueType::U64 => (*value_type_handler.unsigned_int_handler)(self, context.i64_type()), 527 | ValueType::U128 => { 528 | (*value_type_handler.unsigned_int_handler)(self, context.i128_type()) 529 | } 530 | ValueType::F16 => (*value_type_handler.float_handler)(self, context.f16_type()), 531 | ValueType::F32 => (*value_type_handler.float_handler)(self, context.f32_type()), 532 | ValueType::F64 => (*value_type_handler.float_handler)(self, context.f64_type()), 533 | ValueType::Str => (*value_type_handler.str_handler)( 534 | self, 535 | context.i8_type().ptr_type(AddressSpace::Generic), 536 | ), 537 | } 538 | } 539 | 540 | pub fn merge_group<'a>(lhs: &'a ValueType, rhs: &'a ValueType) -> Option<&'a ValueType> { 541 | if lhs.get_group() != rhs.get_group() { 542 | Option::None 543 | } else { 544 | Option::Some(if lhs.get_bitwidth() < rhs.get_bitwidth() { 545 | rhs 546 | } else { 547 | lhs 548 | }) 549 | } 550 | } 551 | } 552 | -------------------------------------------------------------------------------- /examples/AnalogReadSerial.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is the DSPython implementation of the Arduino example. 3 | 4 | Modified for DSPython by Donghyeok Tak 5 | 6 | The original code is: 7 | 8 | /* 9 | AnalogReadSerial 10 | 11 | Reads an analog input on pin 0, prints the result to the Serial Monitor. 12 | Graphical representation is available using Serial Plotter (Tools > Serial Plotter menu). 13 | Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground. 14 | 15 | This example code is in the public domain. 16 | 17 | http://www.arduino.cc/en/Tutorial/AnalogReadSerial 18 | */ 19 | 20 | // the setup routine runs once when you press reset: 21 | void setup() { 22 | // initialize serial communication at 9600 bits per second: 23 | Serial.begin(9600); 24 | } 25 | 26 | // the loop routine runs over and over again forever: 27 | void loop() { 28 | // read the input on analog pin 0: 29 | int sensorValue = analogRead(A0); 30 | // print out the value you read: 31 | Serial.println(sensorValue); 32 | delay(1); // delay in between reads for stability 33 | } 34 | """ 35 | 36 | from arduino import * 37 | 38 | 39 | def setup(): 40 | serial_begin(9600) 41 | 42 | 43 | def loop(): 44 | sensor_value = analog_read(A0) 45 | println(sensor_value) 46 | delay(1) 47 | -------------------------------------------------------------------------------- /examples/Blink.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is the DSPython implementation of the Arduino example. 3 | 4 | Modified for DSPython by Donghyeok Tak 5 | 6 | The original code is: 7 | 8 | /* 9 | Blink 10 | 11 | Turns an LED on for one second, then off for one second, repeatedly. 12 | 13 | Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO 14 | it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to 15 | the correct LED pin independent of which board is used. 16 | If you want to know what pin the on-board LED is connected to on your Arduino 17 | model, check the Technical Specs of your board at: 18 | https://www.arduino.cc/en/Main/Products 19 | 20 | modified 8 May 2014 21 | by Scott Fitzgerald 22 | modified 2 Sep 2016 23 | by Arturo Guadalupi 24 | modified 8 Sep 2016 25 | by Colby Newman 26 | 27 | This example code is in the public domain. 28 | 29 | http://www.arduino.cc/en/Tutorial/Blink 30 | */ 31 | 32 | // the setup function runs once when you press reset or power the board 33 | void setup() { 34 | // initialize digital pin LED_BUILTIN as an output. 35 | pinMode(LED_BUILTIN, OUTPUT); 36 | } 37 | 38 | // the loop function runs over and over again forever 39 | void loop() { 40 | digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) 41 | delay(1000); // wait for a second 42 | digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW 43 | delay(1000); // wait for a second 44 | } 45 | """ 46 | 47 | from arduino import * 48 | 49 | 50 | def setup(): 51 | pin_mode(LED_BUILTIN, OUTPUT) 52 | 53 | 54 | def loop(): 55 | digital_write(LED_BUILTIN, HIGH) 56 | delay(1000) 57 | digital_write(LED_BUILTIN, LOW) 58 | delay(1000) 59 | -------------------------------------------------------------------------------- /examples/Button.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is the DSPython implementation of the Arduino example. 3 | 4 | Modified for DSPython by Donghyeok Tak 5 | 6 | The original code is: 7 | 8 | /* 9 | Button 10 | 11 | Turns on and off a light emitting diode(LED) connected to digital pin 13, 12 | when pressing a pushbutton attached to pin 2. 13 | 14 | The circuit: 15 | - LED attached from pin 13 to ground 16 | - pushbutton attached to pin 2 from +5V 17 | - 10K resistor attached to pin 2 from ground 18 | 19 | - Note: on most Arduinos there is already an LED on the board 20 | attached to pin 13. 21 | 22 | created 2005 23 | by DojoDave 24 | modified 30 Aug 2011 25 | by Tom Igoe 26 | 27 | This example code is in the public domain. 28 | 29 | http://www.arduino.cc/en/Tutorial/Button 30 | */ 31 | 32 | // constants won't change. They're used here to set pin numbers: 33 | const int buttonPin = 2; // the number of the pushbutton pin 34 | const int ledPin = 13; // the number of the LED pin 35 | 36 | // variables will change: 37 | int buttonState = 0; // variable for reading the pushbutton status 38 | 39 | void setup() { 40 | // initialize the LED pin as an output: 41 | pinMode(ledPin, OUTPUT); 42 | // initialize the pushbutton pin as an input: 43 | pinMode(buttonPin, INPUT); 44 | } 45 | 46 | void loop() { 47 | // read the state of the pushbutton value: 48 | buttonState = digitalRead(buttonPin); 49 | 50 | // check if the pushbutton is pressed. If it is, the buttonState is HIGH: 51 | if (buttonState == HIGH) { 52 | // turn LED on: 53 | digitalWrite(ledPin, HIGH); 54 | } else { 55 | // turn LED off: 56 | digitalWrite(ledPin, LOW); 57 | } 58 | } 59 | """ 60 | 61 | from arduino import * 62 | 63 | button_pin = 2 64 | led_pin = 13 65 | 66 | 67 | def setup(): 68 | pin_mode(button_pin, INPUT) 69 | pin_mode(led_pin, OUTPUT) 70 | 71 | 72 | def loop(): 73 | button_state = digital_read(button_pin) 74 | 75 | if button_state == HIGH: 76 | digital_write(led_pin, HIGH) 77 | else: 78 | digital_write(led_pin, LOW) 79 | -------------------------------------------------------------------------------- /examples/MoodLight.py: -------------------------------------------------------------------------------- 1 | """ 2 | DSPython test 3 | """ 4 | 5 | from arduino import * 6 | 7 | pin_red = 9 8 | pin_green = 10 9 | pin_blue = 11 10 | 11 | 12 | def setup(): 13 | serial_begin(9600) 14 | 15 | pin_mode(pin_red, OUTPUT) 16 | pin_mode(pin_green, OUTPUT) 17 | pin_mode(pin_blue, OUTPUT) 18 | 19 | 20 | def loop(): 21 | x = 0. 22 | while x < PI: 23 | x = x + 0.00003 24 | r = 255 * abs(sin(x * 180 / PI)) 25 | g = 255 * abs(sin((x + PI / 3) * 180 / PI)) 26 | b = 255 * abs(sin((x + (2 * PI) / 3) * 180 / PI)) 27 | 28 | analog_write(pin_red, int(r)) 29 | analog_write(pin_green, int(g)) 30 | analog_write(pin_blue, int(b)) 31 | 32 | print(int(r)); print(",") 33 | print(int(g)); print(",") 34 | println(int(b)) 35 | -------------------------------------------------------------------------------- /examples/SerialPrint.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is the DSPython implementation of the Arduino example. 3 | 4 | Created for DSPython by Donghyeok Tak 5 | """ 6 | 7 | from arduino import * 8 | 9 | 10 | def setup(): 11 | serial_begin(9600) 12 | print("Hello, Arduino!") 13 | println("...from Damn Small Python") 14 | 15 | 16 | def loop(): 17 | return 18 | -------------------------------------------------------------------------------- /examples/UltrasonicSensor.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is the DSPython implementation of the Arduino example. 3 | 4 | Created for DSPython by Donghyeok Tak 5 | """ 6 | 7 | from arduino import * 8 | 9 | trig = 13 10 | echo = 12 11 | 12 | 13 | def setup(): 14 | serial_begin(9600) 15 | 16 | pin_mode(trig, OUTPUT) 17 | pin_mode(echo, INPUT) 18 | 19 | print("DSPython - Measure distance using an ultrasonic sensor") 20 | 21 | 22 | def loop(): 23 | digital_write(trig, HIGH) 24 | delay(10) 25 | digital_write(trig, LOW) 26 | 27 | duration: float = pulse_in(echo, HIGH) 28 | distance: float = float(340 * duration / 10000) / 2 29 | 30 | print(distance); println("cm") 31 | 32 | delay(1000) 33 | -------------------------------------------------------------------------------- /include/Builtins.cc: -------------------------------------------------------------------------------- 1 | #include "Builtins.hh" 2 | 3 | extern "C" int int__f__(float n) { 4 | return (int)n; 5 | } 6 | 7 | extern "C" int int__i__(int n) { 8 | return (int)n; 9 | } 10 | 11 | extern "C" float float__f__(float n) { 12 | return (float)n; 13 | } 14 | 15 | extern "C" float float__i__(int n) { 16 | return (float)n; 17 | } 18 | -------------------------------------------------------------------------------- /include/Builtins.hh: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" int int__f__(float n); 4 | extern "C" int int__i__(int n); 5 | extern "C" float float__f__(float n); 6 | extern "C" float float__i__(int n); 7 | -------------------------------------------------------------------------------- /include/LLVMArduinoBuiltins.cc: -------------------------------------------------------------------------------- 1 | #include "LLVMArduinoBuiltins.hh" 2 | 3 | extern "C" void pin_mode(uint8_t pin, uint8_t mode) { 4 | return pinMode(pin, mode); 5 | } 6 | 7 | extern "C" float pulse_in(uint8_t pin, uint8_t mode) { 8 | return pulseIn(pin, mode); 9 | } 10 | 11 | extern "C" void digital_write(uint8_t pin, uint8_t val) { 12 | return digitalWrite(pin, val); 13 | } 14 | 15 | extern "C" void analog_write(uint8_t pin, uint8_t val) { 16 | return analogWrite(pin, val); 17 | } 18 | 19 | extern "C" int digital_read(uint8_t pin) { 20 | return digitalRead(pin); 21 | } 22 | 23 | extern "C" int analog_read(uint8_t pin) { 24 | return analogRead(pin); 25 | } 26 | -------------------------------------------------------------------------------- /include/LLVMArduinoBuiltins.hh: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" void pin_mode(uint8_t pin, uint8_t mode); 5 | extern "C" float pulse_in(uint8_t pin, uint8_t mode); 6 | extern "C" void digital_write(uint8_t pin, uint8_t val); 7 | extern "C" void analog_write(uint8_t pin, uint8_t val); 8 | extern "C" int digital_read(uint8_t pin); 9 | extern "C" int analog_read(uint8_t pin); 10 | -------------------------------------------------------------------------------- /include/Serial.cc: -------------------------------------------------------------------------------- 1 | #include "Serial.hh" 2 | 3 | extern "C" void print__i__(int n) { Serial.print(n); } 4 | extern "C" void print__f__(float n) { Serial.print(n); } 5 | extern "C" void print__s__(char c[]) { Serial.print(c); } 6 | extern "C" void println__i__(int n) { Serial.println(n); } 7 | extern "C" void println__f__(float n) { Serial.println(n); } 8 | extern "C" void println__s__(char c[]) { Serial.println(c); } 9 | extern "C" int is_serial_available() { return Serial.available(); } 10 | extern "C" void serial_begin(int b) { Serial.begin(b); } 11 | extern "C" int input() { return Serial.read(); } 12 | extern "C" void flush() { return Serial.flush(); } -------------------------------------------------------------------------------- /include/Serial.hh: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Print.h" 3 | #include 4 | 5 | extern "C" void print__i__(int n); 6 | extern "C" void print__f__(float n); 7 | extern "C" void print__s__(char c[]); 8 | extern "C" void println__i__(int n); 9 | extern "C" void println__f__(float n); 10 | extern "C" void println__s__(char c[]); 11 | extern "C" int is_serial_available(); 12 | extern "C" void serial_begin(int b); 13 | extern "C" int input(); 14 | extern "C" void flush(); 15 | -------------------------------------------------------------------------------- /python/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsp_python" 3 | version = "0.1.0" 4 | authors = ["탁동혁 "] 5 | edition = "2018" 6 | description = "Python" 7 | license = "MIT" 8 | repository = "https://github.com/tdh8316/dspython" 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /python/codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsp_python_codegen" 3 | version = "0.1.0" 4 | authors = ["탁동혁 "] 5 | edition = "2018" 6 | description = "LLVM IR generator for the dspython project." 7 | license = "MIT" 8 | repository = "https://github.com/tdh8316/dspython" 9 | 10 | [dependencies] 11 | dsp_compiler_error = { path = "../../compiler/error" } 12 | dsp_compiler_mangler = { path = "../../compiler/mangler" } 13 | dsp_compiler_value = { path = "../../compiler/value" } 14 | dsp_python_macros = { path = "../macros" } 15 | dsp_python_parser = { path = "../parser" } 16 | either = "1.6.0" 17 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm10-0" } 18 | num-bigint = "0.3" 19 | num-complex = { version = "0.3", features = ["serde"] } 20 | num-traits = "0.2" 21 | -------------------------------------------------------------------------------- /python/codegen/src/cgexpr.rs: -------------------------------------------------------------------------------- 1 | use either::Either; 2 | use inkwell::values::{BasicValueEnum, InstructionOpcode}; 3 | use inkwell::{FloatPredicate, IntPredicate}; 4 | 5 | use dsp_compiler_error::{err, LLVMCompileError, LLVMCompileErrorType}; 6 | use dsp_compiler_mangler::get_mangled_name; 7 | use dsp_compiler_value::convert::{truncate_bigint_to_u64, try_get_constant_string}; 8 | use dsp_compiler_value::value::{Value, ValueHandler, ValueType}; 9 | use dsp_python_parser::ast; 10 | 11 | use crate::CodeGen; 12 | 13 | impl<'a, 'ctx> CodeGen<'a, 'ctx> { 14 | pub fn emit_expr(&mut self, expr: &ast::Expression) -> Result, LLVMCompileError> { 15 | self.set_loc(expr.location); 16 | 17 | use dsp_python_parser::ast::ExpressionType; 18 | match &expr.node { 19 | ExpressionType::Number { value } => match value { 20 | ast::Number::Integer { value } => { 21 | let value = Value::I16 { 22 | value: self 23 | .context 24 | .i16_type() 25 | .const_int(truncate_bigint_to_u64(value), true), 26 | }; 27 | Ok(value) 28 | } 29 | ast::Number::Float { value } => { 30 | let value = Value::F32 { 31 | value: self.context.f32_type().const_float(*value), 32 | }; 33 | Ok(value) 34 | } 35 | ast::Number::Complex { real: _, imag: _ } => err!( 36 | self, 37 | LLVMCompileErrorType::NotImplemented, 38 | "Imaginary numbers are not supported." 39 | ), 40 | }, 41 | ExpressionType::String { value } => { 42 | let v = try_get_constant_string(value).unwrap(); 43 | if self._fn_value.is_some() { 44 | let value = Value::Str { 45 | value: self 46 | .builder 47 | .build_global_string_ptr(&v, ".str") 48 | .as_pointer_value(), 49 | }; 50 | Ok(value) 51 | } else { 52 | err!( 53 | self, 54 | LLVMCompileErrorType::NotImplemented, 55 | "String expression in global scope is not implemented." 56 | ) 57 | } 58 | } 59 | ExpressionType::Call { 60 | function, 61 | args, 62 | keywords, 63 | } => { 64 | // TODO: kwargs 65 | let _keywords = keywords; 66 | self.compile_expr_call(function, args) 67 | } 68 | ExpressionType::Identifier { name } => { 69 | let (value_type, pointer_value) = if let Some(fn_value) = self._fn_value { 70 | let llvm_variable = self.locals.load(&fn_value, name); 71 | if let Some(llvm_variable) = llvm_variable { 72 | llvm_variable 73 | } else { 74 | let llvm_variable = self.globals.load(name); 75 | if let Some(llvm_variable) = llvm_variable { 76 | llvm_variable 77 | } else { 78 | return err!(self, LLVMCompileErrorType::NameError, name); 79 | } 80 | } 81 | } else { 82 | let llvm_variable = self.globals.load(name); 83 | if let Some(llvm_variable) = llvm_variable { 84 | llvm_variable 85 | } else { 86 | return err!(self, LLVMCompileErrorType::NameError, name); 87 | } 88 | }; 89 | let value = Value::from_basic_value( 90 | value_type.to_owned(), 91 | self.builder.build_load(*pointer_value, name), 92 | ); 93 | Ok(value) 94 | } 95 | ExpressionType::Compare { vals, ops } => self.compile_comparison(vals, ops), 96 | ExpressionType::Binop { a, op, b } => self.compile_bin_op(a, op, b), 97 | ExpressionType::BoolOp { op, values } => self.compile_bool_op(op, values), 98 | ExpressionType::Unop { op, a } => match &a.node { 99 | ExpressionType::Number { value } => match value { 100 | ast::Number::Integer { value } => match op { 101 | ast::UnaryOperator::Neg => { 102 | let value = Value::I16 { 103 | value: self 104 | .context 105 | .i16_type() 106 | .const_int(truncate_bigint_to_u64(&-value), true), 107 | }; 108 | Ok(value) 109 | } 110 | _ => { 111 | return err!( 112 | self, 113 | LLVMCompileErrorType::NotImplemented, 114 | format!("Unimplemented unop {:?} for integer", op) 115 | ); 116 | } 117 | }, 118 | ast::Number::Float { value } => match op { 119 | ast::UnaryOperator::Neg => { 120 | let value = Value::F32 { 121 | value: self.context.f32_type().const_float(-value.clone()), 122 | }; 123 | Ok(value) 124 | } 125 | _ => { 126 | return err!( 127 | self, 128 | LLVMCompileErrorType::NotImplemented, 129 | format!("Unimplemented unop {:?} for floating number", op) 130 | ); 131 | } 132 | }, 133 | ast::Number::Complex { real: _, imag: _ } => { 134 | return err!( 135 | self, 136 | LLVMCompileErrorType::NotImplemented, 137 | format!("Complex numbers are not implemented.") 138 | ); 139 | } 140 | }, 141 | _ => { 142 | return err!( 143 | self, 144 | LLVMCompileErrorType::NotImplemented, 145 | format!("unary operator for {:?} is not implemented.", a.node) 146 | ); 147 | } 148 | }, 149 | ExpressionType::List { elements } | ExpressionType::Tuple { elements } => { 150 | let mut elements_value = vec![]; 151 | for (_i, element) in elements.iter().enumerate() { 152 | elements_value.push(self.emit_expr(element)?.to_basic_value()); 153 | } 154 | let arr_element_type = (&elements_value).first().unwrap().get_type(); 155 | // let array_type = (&arr_element_type).array_type(elements.len() as u32); 156 | let mut vec_array_value = vec![]; 157 | for value in elements_value.iter() { 158 | vec_array_value.push(value.into_int_value()); 159 | } 160 | let array_value = arr_element_type 161 | .into_int_type() 162 | .const_array(vec_array_value.as_slice()); 163 | 164 | // let array = self.builder.build_array_alloca(array_type, self.context.i8_type().const_int(3, false), "arr"); 165 | Ok(Value::Array { value: array_value }) 166 | } 167 | ExpressionType::True => Ok(Value::Bool { 168 | value: self.context.bool_type().const_int(1, false), 169 | }), 170 | ExpressionType::False => Ok(Value::Bool { 171 | value: self.context.bool_type().const_int(0, false), 172 | }), 173 | ExpressionType::None => Ok(Value::Void), 174 | _ => err!( 175 | self, 176 | LLVMCompileErrorType::NotImplemented, 177 | format!("{:?}", expr) 178 | ), 179 | } 180 | } 181 | 182 | fn compile_expr_call( 183 | &mut self, 184 | func: &Box, 185 | args: &Vec, 186 | ) -> Result, LLVMCompileError> { 187 | let func_name = match &func.node { 188 | ast::ExpressionType::Identifier { name } => name.to_string(), 189 | _ => { 190 | return err!( 191 | self, 192 | LLVMCompileErrorType::NotImplemented, 193 | "Calling method is not implemented." 194 | ); 195 | } 196 | }; 197 | 198 | // Compile the first argument to get type signature 199 | let first_arg = if let Some(f_arg) = args.first() { 200 | self.emit_expr(f_arg)? 201 | } else { 202 | Value::Void 203 | }; 204 | 205 | let func = match self.get_function(&func_name) { 206 | Some(f) => f, 207 | None => { 208 | // Simple mangling from the type of the first argument 209 | let func_name_mangled = get_mangled_name(&func_name, first_arg.get_type()); 210 | if let Some(f) = self.get_function(&func_name_mangled) { 211 | f 212 | } else { 213 | return err!(self, LLVMCompileErrorType::NameError, &func_name); 214 | } 215 | } 216 | }; 217 | 218 | let args_proto = func.get_params(); 219 | let mut args_value: Vec = vec![]; 220 | 221 | for (i, (expr, proto)) in args.iter().zip(args_proto.iter()).enumerate() { 222 | // Prevent compile the same argument twice 223 | let value = if i == 0 { 224 | first_arg 225 | } else { 226 | self.emit_expr(expr)? 227 | }; 228 | 229 | // Convert the type of argument according to the signature 230 | match value { 231 | Value::Bool { value } => { 232 | let cast = self.builder.build_int_truncate( 233 | value, 234 | proto.get_type().into_int_type(), 235 | "itrunc", 236 | ); 237 | args_value.push(BasicValueEnum::IntValue(cast)) 238 | } 239 | Value::I8 { value } => { 240 | let cast = self.builder.build_int_cast( 241 | value, 242 | proto.get_type().into_int_type(), 243 | "icast", 244 | ); 245 | args_value.push(BasicValueEnum::IntValue(cast)) 246 | } 247 | Value::I16 { value } => { 248 | let cast = self.builder.build_int_truncate( 249 | value, 250 | proto.get_type().into_int_type(), 251 | "itrunc", 252 | ); 253 | args_value.push(BasicValueEnum::IntValue(cast)) 254 | } 255 | Value::F32 { value } => args_value.push(BasicValueEnum::FloatValue(value)), 256 | Value::Str { value } => args_value.push(BasicValueEnum::PointerValue(value)), 257 | _ => { 258 | return err!( 259 | self, 260 | LLVMCompileErrorType::NotImplemented, 261 | format!("Unimplemented argument type '{:?}'", value.get_type()) 262 | ); 263 | } 264 | } 265 | } 266 | 267 | let res = self.builder.build_call(func, args_value.as_slice(), "call"); 268 | res.set_tail_call(true); 269 | 270 | // Returned value 271 | let value = match res.try_as_basic_value() { 272 | // Return type 273 | Either::Left(bv) => { 274 | let vt = if bv.is_int_value() { 275 | let iv = bv.into_int_value(); 276 | 277 | match iv.get_type().get_bit_width() { 278 | 8 => ValueType::I8, 279 | 16 => ValueType::I16, 280 | _ => unreachable!(), 281 | } 282 | } else if bv.is_float_value() { 283 | ValueType::F32 284 | } else { 285 | unreachable!() 286 | }; 287 | Value::from_basic_value(vt, bv) 288 | } 289 | Either::Right(_) => Value::Void, 290 | }; 291 | Ok(value) 292 | } 293 | 294 | fn compile_comparison( 295 | &mut self, 296 | vals: &Vec, 297 | ops: &Vec, 298 | ) -> Result, LLVMCompileError> { 299 | if ops.len() > 1 || vals.len() > 2 { 300 | return err!( 301 | self, 302 | LLVMCompileErrorType::NotImplemented, 303 | "Chained comparison is not implemented." 304 | ); 305 | } 306 | 307 | let a = self.emit_expr(vals.first().unwrap())?; 308 | let b = self.emit_expr(vals.last().unwrap())?; 309 | 310 | Ok(a.invoke_handler( 311 | ValueHandler::new() 312 | .handle_int(&|_, lhs_value| { 313 | b.invoke_handler(ValueHandler::new().handle_int(&|_, rhs_value| { 314 | let int_predicate = match ops.first().unwrap() { 315 | ast::Comparison::Equal => IntPredicate::EQ, 316 | ast::Comparison::NotEqual => IntPredicate::NE, 317 | ast::Comparison::Greater => IntPredicate::SGT, 318 | ast::Comparison::Less => IntPredicate::SLT, 319 | ast::Comparison::GreaterOrEqual => IntPredicate::SGE, 320 | ast::Comparison::LessOrEqual => IntPredicate::SLE, 321 | _ => panic!( 322 | "Unsupported {:?} comparison operator for integer", 323 | ops.first().unwrap() 324 | ), 325 | }; 326 | Value::Bool { 327 | value: self.builder.build_int_compare( 328 | int_predicate, 329 | lhs_value, 330 | rhs_value, 331 | "a", 332 | ), 333 | } 334 | })) 335 | }) 336 | .handle_float(&|_, lhs_value| { 337 | b.invoke_handler(ValueHandler::new().handle_float(&|_, rhs_value| { 338 | let float_predicate = match ops.first().unwrap() { 339 | ast::Comparison::Equal => FloatPredicate::OEQ, 340 | ast::Comparison::NotEqual => FloatPredicate::ONE, 341 | ast::Comparison::Greater => FloatPredicate::OGT, 342 | ast::Comparison::Less => FloatPredicate::OLT, 343 | ast::Comparison::GreaterOrEqual => FloatPredicate::OGE, 344 | ast::Comparison::LessOrEqual => FloatPredicate::OLE, 345 | _ => panic!( 346 | "Unsupported {:?} comparison operator for floating number", 347 | ops.first().unwrap() 348 | ), 349 | }; 350 | Value::Bool { 351 | value: self.builder.build_float_compare( 352 | float_predicate, 353 | lhs_value, 354 | rhs_value, 355 | "a", 356 | ), 357 | } 358 | })) 359 | }), 360 | )) 361 | } 362 | 363 | fn compile_bin_op( 364 | &mut self, 365 | a: &ast::Expression, 366 | op: &ast::Operator, 367 | b: &ast::Expression, 368 | ) -> Result, LLVMCompileError> { 369 | use dsp_python_parser::ast::Operator; 370 | let a = self.emit_expr(a)?; 371 | let b = self.emit_expr(b)?; 372 | Ok(a.invoke_handler( 373 | ValueHandler::new() 374 | .handle_int(&|_, lhs_value| { 375 | macro_rules! emit_cast { 376 | () => {{ 377 | self.builder 378 | .build_cast( 379 | InstructionOpcode::SIToFP, 380 | lhs_value, 381 | self.context.f32_type(), 382 | "sitofp", 383 | ) 384 | .into_float_value() 385 | }}; 386 | }; 387 | b.invoke_handler( 388 | ValueHandler::new() 389 | // Between int and int 390 | .handle_int(&|_, rhs_value| { 391 | // Div operator to int returns a float. 392 | if op == &Operator::Div { 393 | return Value::F32 { 394 | value: self.builder.build_float_div( 395 | lhs_value 396 | .const_signed_to_float(self.context.f32_type()), 397 | rhs_value 398 | .const_signed_to_float(self.context.f32_type()), 399 | "div", 400 | ), 401 | }; 402 | } 403 | let value = match op { 404 | Operator::Add => { 405 | self.builder.build_int_add(lhs_value, rhs_value, "add") 406 | } 407 | Operator::Sub => { 408 | self.builder.build_int_sub(lhs_value, rhs_value, "sub") 409 | } 410 | Operator::Mult => { 411 | self.builder.build_int_mul(lhs_value, rhs_value, "mul") 412 | } 413 | Operator::Div => { 414 | // In Python, dividing int by int returns a float, 415 | // which is implemented above. 416 | unimplemented!() 417 | } 418 | Operator::FloorDiv => self 419 | .builder 420 | .build_int_signed_div(lhs_value, rhs_value, "fld"), 421 | Operator::Mod => self 422 | .builder 423 | .build_int_signed_rem(lhs_value, rhs_value, "mod"), 424 | _ => panic!("Unimplemented {:?} operator for i16", op), 425 | }; 426 | Value::I16 { value } 427 | }) 428 | // Between int and float 429 | .handle_float(&|_, rhs_value| { 430 | let cast = emit_cast!(); 431 | let value = match op { 432 | Operator::Add => { 433 | self.builder.build_float_add(cast, rhs_value, "add") 434 | } 435 | Operator::Sub => { 436 | self.builder.build_float_sub(cast, rhs_value, "sub") 437 | } 438 | Operator::Mult => { 439 | self.builder.build_float_mul(cast, rhs_value, "mul") 440 | } 441 | Operator::Div => { 442 | self.builder.build_float_div(cast, rhs_value, "div") 443 | } 444 | Operator::FloorDiv => unimplemented!(), 445 | Operator::Mod => { 446 | self.builder.build_float_rem(cast, rhs_value, "mod") 447 | } 448 | _ => panic!("Unimplemented {:?} operator for i16 and f32", op), 449 | }; 450 | Value::F32 { value } 451 | }), 452 | ) 453 | }) 454 | .handle_float(&|_, lhs_value| { 455 | b.invoke_handler( 456 | ValueHandler::new() 457 | // Between float and float 458 | .handle_float(&|_, rhs_value| { 459 | let value = match op { 460 | Operator::Add => { 461 | self.builder.build_float_add(lhs_value, rhs_value, "add") 462 | } 463 | Operator::Sub => { 464 | self.builder.build_float_sub(lhs_value, rhs_value, "sub") 465 | } 466 | Operator::Mult => { 467 | self.builder.build_float_mul(lhs_value, rhs_value, "mul") 468 | } 469 | Operator::Div => { 470 | self.builder.build_float_div(lhs_value, rhs_value, "div") 471 | } 472 | Operator::FloorDiv => unimplemented!(), 473 | Operator::Mod => { 474 | self.builder.build_float_rem(lhs_value, rhs_value, "mod") 475 | } 476 | _ => panic!("Unimplemented {:?} operator for f32", op), 477 | }; 478 | Value::F32 { value } 479 | }) 480 | // Between float and int 481 | .handle_int(&|_, rhs_value| { 482 | macro_rules! emit_cast { 483 | () => {{ 484 | self.builder 485 | .build_cast( 486 | InstructionOpcode::SIToFP, 487 | rhs_value, 488 | self.context.f32_type(), 489 | "sitofp", 490 | ) 491 | .into_float_value() 492 | }}; 493 | }; 494 | let cast = emit_cast!(); 495 | let value = match op { 496 | Operator::Add => { 497 | self.builder.build_float_add(lhs_value, cast, "add") 498 | } 499 | Operator::Sub => { 500 | self.builder.build_float_sub(lhs_value, cast, "sub") 501 | } 502 | Operator::Mult => { 503 | self.builder.build_float_mul(lhs_value, cast, "mul") 504 | } 505 | Operator::Div => { 506 | self.builder.build_float_div(lhs_value, cast, "div") 507 | } 508 | Operator::FloorDiv => unimplemented!(), 509 | Operator::Mod => { 510 | self.builder.build_float_rem(lhs_value, cast, "mod") 511 | } 512 | _ => panic!("Unimplemented {:?} operator for f32 and i16", op), 513 | }; 514 | Value::F32 { value } 515 | }), 516 | ) 517 | }), 518 | )) 519 | } 520 | 521 | fn compile_bool_op( 522 | &mut self, 523 | _op: &ast::BooleanOperator, 524 | _values: &Vec, 525 | ) -> Result, LLVMCompileError> { 526 | unimplemented!() 527 | } 528 | } 529 | -------------------------------------------------------------------------------- /python/codegen/src/cgstmt.rs: -------------------------------------------------------------------------------- 1 | use std::option::Option::Some; 2 | 3 | use inkwell::types::BasicTypeEnum; 4 | use inkwell::values::BasicValue; 5 | use inkwell::{FloatPredicate, IntPredicate}; 6 | 7 | use dsp_compiler_error::{err, LLVMCompileError, LLVMCompileErrorType}; 8 | use dsp_compiler_value::value::{Value, ValueHandler, ValueType}; 9 | use dsp_python_macros::*; 10 | use dsp_python_parser::ast; 11 | 12 | use crate::scope::LLVMVariableAccessor; 13 | use crate::{get_doc, CodeGen}; 14 | use inkwell::module::Linkage; 15 | 16 | impl<'a, 'ctx> CodeGen<'a, 'ctx> { 17 | pub fn emit_stmt(&mut self, stmt: &ast::Statement) -> Result<(), LLVMCompileError> { 18 | self.set_loc(stmt.location); 19 | use dsp_python_parser::ast::StatementType; 20 | match &stmt.node { 21 | StatementType::Expression { expression } => { 22 | self.emit_expr(expression)?; 23 | Ok(()) 24 | } 25 | StatementType::FunctionDef { 26 | is_async, 27 | name, 28 | args, 29 | body, 30 | decorator_list, 31 | returns, 32 | } => { 33 | if *is_async { 34 | return err!( 35 | self, 36 | LLVMCompileErrorType::NotImplemented, 37 | "Async functions are not supported." 38 | ); 39 | } 40 | if decorator_list.len() > 0 { 41 | return err!( 42 | self, 43 | LLVMCompileErrorType::NotImplemented, 44 | "Decorators are not implemented." 45 | ); 46 | }; 47 | self.compile_stmt_function_def(name, args, body, returns) 48 | } 49 | StatementType::AnnAssign { 50 | target, 51 | annotation: _, // Do not check type; It will be done in compile time 52 | value, 53 | } => { 54 | if let Some(value) = value { 55 | self.compile_stmt_ann_assign(target, value)?; 56 | } 57 | Ok(()) 58 | } 59 | StatementType::Assign { targets, value } => { 60 | if targets.len() > 1 { 61 | return err!( 62 | self, 63 | LLVMCompileErrorType::NotImplemented, 64 | "Variable unpacking is not implemented." 65 | ); 66 | } 67 | self.compile_stmt_assign(targets.first().unwrap(), value) 68 | } 69 | StatementType::Return { value } => self.compile_stmt_return(value), 70 | StatementType::ImportFrom { 71 | level, 72 | module, 73 | names, 74 | } => { 75 | let _level = level; 76 | let target = module.as_ref().expect("Unknown module name"); 77 | let _names = names; 78 | if target.contains("arduino") { 79 | // Builtin 80 | } else { 81 | return err!( 82 | self, 83 | LLVMCompileErrorType::NotImplemented, 84 | "Import is not implemented." 85 | ); 86 | } 87 | Ok(()) 88 | } 89 | StatementType::If { test, body, orelse } => { 90 | match orelse { 91 | None /*Only if:*/ => { 92 | self.compile_stmt_conditional(test, body, None) 93 | } 94 | Some(statements) => { 95 | self.compile_stmt_conditional(test, body, Some(statements)) 96 | } 97 | } 98 | } 99 | StatementType::While { test, body, orelse } => { 100 | self.compile_stmt_while(test, body, orelse) 101 | } 102 | StatementType::Pass => Ok(()), 103 | _ => err!( 104 | self, 105 | LLVMCompileErrorType::NotImplemented, 106 | format!("{:?}", stmt) 107 | ), 108 | } 109 | } 110 | 111 | fn compile_stmt_assign( 112 | &mut self, 113 | target: &ast::Expression, 114 | value: &ast::Expression, 115 | ) -> Result<(), LLVMCompileError> { 116 | let name = match &target.node { 117 | ast::ExpressionType::Identifier { name } => name, 118 | _ => { 119 | return err!( 120 | self, 121 | LLVMCompileErrorType::NotImplemented, 122 | "Failed to get assignee." 123 | ); 124 | } 125 | }; 126 | let value = self.emit_expr(value)?; 127 | let value_type = value.get_type(); 128 | 129 | if let Some(fn_value) = &self._fn_value { 130 | // Define the local 131 | let llvm_var = self.locals.load(fn_value, name); 132 | let pointer = if let Some(llvm_var) = llvm_var { 133 | llvm_var.pointer_value() 134 | } else { 135 | self.builder 136 | .build_alloca(value_type.to_basic_type(self.context), name) 137 | }; 138 | self.builder.build_store(pointer, value.to_basic_value()); 139 | self.locals.set(fn_value, name, (value_type, pointer)); 140 | } else { 141 | // Define the global 142 | let global = self 143 | .module 144 | .add_global(value_type.to_basic_type(self.context), None, name); 145 | global.set_linkage(Linkage::Internal); 146 | global.set_unnamed_addr(true); 147 | global.set_initializer(&value.to_basic_value()); 148 | let pointer = global.as_pointer_value(); 149 | self.globals.set(name, (value_type, pointer)); 150 | } 151 | 152 | Ok(()) 153 | } 154 | 155 | fn compile_stmt_ann_assign( 156 | &mut self, 157 | target: &ast::Expression, 158 | value: &ast::Expression, 159 | ) -> Result<(), LLVMCompileError> { 160 | self.compile_stmt_assign(target, value) 161 | } 162 | 163 | fn compile_stmt_function_def( 164 | &mut self, 165 | name: &String, 166 | args: &Box, 167 | body: &ast::Suite, 168 | returns: &Option, 169 | ) -> Result<(), LLVMCompileError> { 170 | // The types and names of arguments 171 | let mut args_vec: Vec = vec![]; 172 | let mut arg_names: Vec<&String> = vec![]; 173 | for arg in args.args.iter() { 174 | arg_names.push(&arg.arg); 175 | if arg.annotation.is_none() { 176 | return err!( 177 | self, 178 | LLVMCompileErrorType::SyntaxError, 179 | "You must provide type hint for arguments" 180 | ); 181 | } 182 | let arg_type; 183 | match &arg.annotation.as_ref().unwrap().node { 184 | ast::ExpressionType::Identifier { name } => { 185 | arg_type = name; 186 | } 187 | _ => { 188 | return err!( 189 | self, 190 | LLVMCompileErrorType::NotImplemented, 191 | "Unrecognizable type" 192 | ); 193 | } 194 | } 195 | match arg_type.as_str() { 196 | "int" => args_vec.push(self.context.i16_type().into()), 197 | "float" => args_vec.push(self.context.f32_type().into()), 198 | _ => { 199 | return err!( 200 | self, 201 | LLVMCompileErrorType::NotImplemented, 202 | format!("Unimplemented argument type {}", arg_type) 203 | ); 204 | } 205 | } 206 | } 207 | 208 | // The type to return value of this function 209 | let return_type = if let Some(annotation) = returns { 210 | match &annotation.node { 211 | ast::ExpressionType::Identifier { name } => name.to_owned(), 212 | ast::ExpressionType::None => "None".to_string(), 213 | _ => { 214 | return err!( 215 | self, 216 | LLVMCompileErrorType::NotImplemented, 217 | "Unknown return annotation node" 218 | ); 219 | } 220 | } 221 | } else { 222 | "None".to_string() 223 | }; 224 | 225 | let f = self.module.add_function( 226 | name, 227 | match return_type.as_str() { 228 | // i8 for arduino builtins 229 | "int8" => self.context.i8_type().fn_type(&args_vec, false), 230 | 231 | "int" => self.context.i16_type().fn_type(&args_vec, false), 232 | "float" => self.context.f32_type().fn_type(&args_vec, false), 233 | "None" => self.context.void_type().fn_type(&args_vec, false), 234 | _ => { 235 | return err!( 236 | self, 237 | LLVMCompileErrorType::NotImplemented, 238 | format!("Unknown return type {}", return_type) 239 | ); 240 | } 241 | }, 242 | None, 243 | ); 244 | if !vec!["setup", "loop"].contains(&name.as_str()) { 245 | f.set_linkage(Linkage::Internal); 246 | } 247 | 248 | // Create an entry block 249 | let bb = self.context.append_basic_block(f, ""); 250 | self.builder.position_at_end(bb); 251 | self.compile_context.returned = false; 252 | 253 | // Create local scope 254 | self.set_fn_value(f); 255 | self.locals.create(self.get_fn_value()?); 256 | 257 | // Assign arguments 258 | for (i, bv) in f.get_param_iter().enumerate() { 259 | let arg_name = arg_names[i]; 260 | let v = if bv.is_int_value() { 261 | bv.into_int_value().set_name(arg_name); 262 | Value::I16 { 263 | value: bv.into_int_value(), 264 | } 265 | } else if bv.is_float_value() { 266 | bv.into_float_value().set_name(arg_name); 267 | Value::F32 { 268 | value: bv.into_float_value(), 269 | } 270 | } else { 271 | return err!( 272 | self, 273 | LLVMCompileErrorType::NotImplemented, 274 | "Unimplemented function argument type" 275 | ); 276 | }; 277 | let pointer = self 278 | .builder 279 | .build_alloca(v.get_type().to_basic_type(self.context), arg_name); 280 | self.builder.build_store(pointer, bv); 281 | self.locals.set( 282 | &self.get_fn_value().unwrap(), 283 | arg_name, 284 | (v.get_type(), pointer), 285 | ); 286 | } 287 | 288 | let (body, _doc_string) = get_doc(body); 289 | 290 | for statement in body.iter() { 291 | self.emit_stmt(statement)?; 292 | } 293 | 294 | if !self.compile_context.returned { 295 | let frt = self.get_fn_value().unwrap().get_type().get_return_type(); 296 | if let Some(frt) = frt { 297 | if frt.is_int_type() { 298 | match frt.into_int_type().get_bit_width() { 299 | 8 => { 300 | self.builder 301 | .build_return(Some(&self.context.i8_type().const_zero())); 302 | } 303 | 16 => { 304 | self.builder 305 | .build_return(Some(&self.context.i16_type().const_zero())); 306 | } 307 | _ => unimplemented!(), 308 | } 309 | } else if frt.is_float_type() { 310 | self.builder 311 | .build_return(Some(&self.context.f32_type().const_zero())); 312 | } 313 | } else { 314 | self.builder.build_return(None); 315 | } 316 | } 317 | 318 | self._fn_value = None; 319 | Ok(()) 320 | } 321 | 322 | fn compile_stmt_conditional( 323 | &mut self, 324 | test: &ast::Expression, 325 | body: &Vec, 326 | orelse: Option<&Vec>, 327 | ) -> Result<(), LLVMCompileError> { 328 | let parent = self.get_fn_value()?; 329 | 330 | let then_bb = self.context.append_basic_block(parent, "if.then"); 331 | let else_bb = self.context.append_basic_block(parent, "if.else"); 332 | let end_bb = self.context.append_basic_block(parent, "if.end"); 333 | 334 | // Compile the condition as i1. 335 | let test = self.emit_expr(test)?; 336 | let cond = test.invoke_handler(cvhandler!(self)); 337 | 338 | // Build the conditional branch. 339 | self.builder 340 | .build_conditional_branch(cond, then_bb, else_bb); 341 | 342 | // Emit at if.then. 343 | self.builder.position_at_end(then_bb); 344 | // TODO: Return block 345 | self.compile_context.returned = false; 346 | for statement in body.iter() { 347 | if self.compile_context.returned { 348 | break; 349 | } 350 | self.emit_stmt(statement)?; 351 | } 352 | // Then, unconditionally jump to the end block. 353 | if !self.compile_context.returned { 354 | self.builder.build_unconditional_branch(end_bb); 355 | } 356 | 357 | // Emit at if.else if present. 358 | self.builder.position_at_end(else_bb); 359 | self.compile_context.returned = false; 360 | if let Some(statements) = orelse { 361 | for statement in statements.iter() { 362 | if self.compile_context.returned { 363 | break; 364 | } 365 | self.emit_stmt(statement)?; 366 | } 367 | } 368 | 369 | // Then, unconditionally jump to the end block. 370 | if !self.compile_context.returned { 371 | self.builder.build_unconditional_branch(end_bb); 372 | } 373 | 374 | // Set the cursor at the end 375 | self.compile_context.returned = false; 376 | self.builder.position_at_end(end_bb); 377 | Ok(()) 378 | } 379 | 380 | fn compile_stmt_while( 381 | &mut self, 382 | test: &ast::Expression, 383 | body: &ast::Suite, 384 | orelse: &Option, 385 | ) -> Result<(), LLVMCompileError> { 386 | let parent = self.get_fn_value()?; 387 | 388 | let while_bb = self.context.append_basic_block(parent, "while"); 389 | let loop_bb = self.context.append_basic_block(parent, "while.body"); 390 | let else_bb = self.context.append_basic_block(parent, "while.else"); 391 | let end_bb = self.context.append_basic_block(parent, "while.end"); 392 | 393 | // Switch to the loop block. 394 | self.builder.build_unconditional_branch(while_bb); 395 | self.builder.position_at_end(while_bb); 396 | 397 | // Declare the variable in condition. 398 | let start = self.emit_expr(test)?; 399 | let cond = start.invoke_handler(cvhandler!(self)); 400 | 401 | // At first, Check whether or not the condition in the header of the loop is true. 402 | self.builder 403 | .build_conditional_branch(cond, loop_bb, else_bb); 404 | 405 | // Emit the loop body. 406 | self.builder.position_at_end(loop_bb); 407 | for statement in body.iter() { 408 | self.emit_stmt(statement)?; 409 | } 410 | 411 | // Emit the conditional branch at the end of the loop body. 412 | // self.builder 413 | // .build_conditional_branch(cond, while_bb, else_bb); 414 | // It is not needed to check the condition. 415 | // Return to the while header block and check it in there. 416 | self.builder.build_unconditional_branch(while_bb); 417 | 418 | // Emit the 'else' code if present. 419 | self.builder.position_at_end(else_bb); 420 | if let Some(statements) = orelse { 421 | for statement in statements.iter() { 422 | self.emit_stmt(statement)?; 423 | } 424 | } 425 | 426 | // Then, unconditionally jump to the end block. 427 | self.builder.build_unconditional_branch(end_bb); 428 | 429 | // Set the cursor at the end 430 | self.builder.position_at_end(end_bb); 431 | 432 | Ok(()) 433 | } 434 | 435 | fn compile_stmt_return( 436 | &mut self, 437 | value: &Option, 438 | ) -> Result<(), LLVMCompileError> { 439 | // Outside function 440 | if self._fn_value.is_none() { 441 | return err!( 442 | self, 443 | LLVMCompileErrorType::SyntaxError, 444 | "'return' outside function" 445 | ); 446 | } 447 | if let Some(value) = value { 448 | let return_value = self.emit_expr(value)?; 449 | 450 | if return_value.get_type() == ValueType::Void { 451 | self.builder.build_return(None); 452 | } else { 453 | // Type check 454 | let fn_type = self 455 | .get_fn_value()? 456 | .get_type() 457 | .get_return_type() 458 | .expect("No return type"); 459 | let value_type = return_value.to_basic_value().get_type(); 460 | if fn_type != value_type { 461 | return err!( 462 | self, 463 | LLVMCompileErrorType::TypeError, 464 | format!("{:?}", fn_type), 465 | format!("{:?}", value_type) 466 | ); 467 | } 468 | 469 | return_value.invoke_handler( 470 | ValueHandler::new() 471 | .handle_int(&|_, value| self.builder.build_return(Some(&value))) 472 | .handle_float(&|_, value| self.builder.build_return(Some(&value))), 473 | ); 474 | } 475 | } else { 476 | self.builder.build_return(None); 477 | } 478 | self.compile_context.returned = true; 479 | Ok(()) 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /python/codegen/src/lib.rs: -------------------------------------------------------------------------------- 1 | use inkwell::builder::Builder; 2 | use inkwell::context::Context; 3 | use inkwell::module::Module; 4 | use inkwell::values::FunctionValue; 5 | 6 | use dsp_compiler_error::{err, LLVMCompileError, LLVMCompileErrorType}; 7 | use dsp_compiler_value::convert::try_get_constant_string; 8 | use dsp_python_parser::ast; 9 | 10 | use crate::scope::{Locals, VariableMap}; 11 | 12 | pub mod scope; 13 | 14 | pub mod cgexpr; 15 | pub mod cgstmt; 16 | 17 | pub struct CompileContext { 18 | returned: bool, 19 | } 20 | 21 | impl CompileContext { 22 | pub fn new() -> Self { 23 | CompileContext { returned: false } 24 | } 25 | } 26 | 27 | pub struct CodeGen<'a, 'ctx> { 28 | pub context: &'ctx Context, 29 | pub builder: &'a Builder<'ctx>, 30 | pub module: &'a Module<'ctx>, 31 | pub compile_context: CompileContext, 32 | 33 | _fn_value: Option>, 34 | _current_source_location: ast::Location, 35 | globals: VariableMap<'ctx>, 36 | locals: Locals<'ctx>, 37 | } 38 | 39 | impl<'a, 'ctx> CodeGen<'a, 'ctx> { 40 | pub fn new( 41 | context: &'ctx Context, 42 | builder: &'a Builder<'ctx>, 43 | module: &'a Module<'ctx>, 44 | ) -> Self { 45 | CodeGen { 46 | context, 47 | builder, 48 | module, 49 | _fn_value: None, 50 | _current_source_location: ast::Location::default(), 51 | globals: VariableMap::new(), 52 | locals: Locals::new(), 53 | compile_context: CompileContext { returned: false }, 54 | } 55 | } 56 | 57 | #[inline] 58 | pub fn get_function(&self, name: &str) -> Option> { 59 | self.module.get_function(name) 60 | } 61 | 62 | pub fn set_fn_value(&mut self, fn_value: FunctionValue<'ctx>) { 63 | self._fn_value = Some(fn_value); 64 | } 65 | 66 | pub fn get_fn_value(&self) -> Result, LLVMCompileError> { 67 | match self._fn_value { 68 | Some(func) => Ok(func), 69 | None => err!( 70 | self, 71 | LLVMCompileErrorType::NotImplemented, 72 | "Attempted to get a function value outside function. Some features must be in the function." 73 | ) 74 | } 75 | } 76 | 77 | pub fn set_loc(&mut self, location: ast::Location) { 78 | self._current_source_location = location; 79 | } 80 | 81 | pub fn get_loc(&self) -> ast::Location { 82 | self._current_source_location 83 | } 84 | } 85 | 86 | pub fn get_doc(body: &[ast::Statement]) -> (&[ast::Statement], Option) { 87 | if let Some((val, body_rest)) = body.split_first() { 88 | if let ast::StatementType::Expression { ref expression } = val.node { 89 | if let ast::ExpressionType::String { value } = &expression.node { 90 | if let Some(value) = try_get_constant_string(value) { 91 | return (body_rest, Some(value)); 92 | } 93 | } 94 | } 95 | } 96 | (body, None) 97 | } 98 | -------------------------------------------------------------------------------- /python/codegen/src/scope.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use inkwell::values::{FunctionValue, PointerValue}; 4 | 5 | use dsp_compiler_value::value::ValueType; 6 | 7 | /* 8 | Fuck you inappropriate lifetime error 9 | _ 10 | _._ _..._ .-', _.._(`)) 11 | '-. ` ' /-._.-' ',/ 12 | ) \ '. 13 | / _ _ | \ 14 | | a a / | 15 | \ .-. ; 16 | '-('' ).-' ,' ; 17 | '-; | .' 18 | \ \ / 19 | | 7 .__ _.-\ \ 20 | | | | ``/ /` / 21 | /,_| | /,_/ / 22 | /,_/ '`-' 23 | */ 24 | 25 | pub type LLVMVariable<'ctx> = (ValueType, PointerValue<'ctx>); 26 | 27 | pub trait LLVMVariableAccessor<'ctx> { 28 | fn value_type(&self) -> ValueType; 29 | fn pointer_value(&self) -> PointerValue<'ctx>; 30 | } 31 | 32 | impl<'ctx> LLVMVariableAccessor<'ctx> for LLVMVariable<'ctx> { 33 | fn value_type(&self) -> ValueType { 34 | self.0 35 | } 36 | 37 | fn pointer_value(&self) -> PointerValue<'ctx> { 38 | self.1 39 | } 40 | } 41 | 42 | pub struct VariableMap<'ctx> { 43 | variables: HashMap>, 44 | } 45 | 46 | impl<'ctx> VariableMap<'ctx> { 47 | pub fn new() -> Self { 48 | VariableMap { 49 | variables: HashMap::new(), 50 | } 51 | } 52 | 53 | pub fn load(&self, name: &str) -> Option<&LLVMVariable<'ctx>> { 54 | self.variables.get(name) 55 | } 56 | 57 | pub fn set(&mut self, name: &str, value: LLVMVariable<'ctx>) { 58 | self.variables.insert(name.to_string(), value); 59 | } 60 | } 61 | 62 | pub struct Locals<'ctx> { 63 | local_variables: HashMap, VariableMap<'ctx>>, 64 | } 65 | 66 | impl<'ctx> Locals<'ctx> { 67 | pub fn new() -> Self { 68 | Locals { 69 | local_variables: HashMap::new(), 70 | } 71 | } 72 | 73 | pub fn load(&self, fn_value: &FunctionValue<'ctx>, name: &str) -> Option<&LLVMVariable<'ctx>> { 74 | self.local_variables.get(fn_value).unwrap().load(name) 75 | } 76 | 77 | pub fn set(&mut self, fn_value: &FunctionValue<'ctx>, name: &str, value: LLVMVariable<'ctx>) { 78 | self.local_variables 79 | .get_mut(fn_value) 80 | .unwrap() 81 | .set(name, value); 82 | } 83 | 84 | pub fn create(&mut self, fn_value: FunctionValue<'ctx>) { 85 | self.local_variables.insert(fn_value, VariableMap::new()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /python/macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsp_python_macros" 3 | version = "0.1.0" 4 | authors = ["탁동혁 "] 5 | edition = "2018" 6 | description = "Macros for CodeGen" 7 | license = "MIT" 8 | repository = "https://github.com/tdh8316/dspython" 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /python/macros/src/condition_value.rs: -------------------------------------------------------------------------------- 1 | /// Condition value handler 2 | #[macro_export] 3 | macro_rules! cvhandler { 4 | ($self:ident) => {{ 5 | ValueHandler::new() 6 | .handle_bool(&|_, value| value) 7 | // In Python, all integers except 0 are considered true. 8 | .handle_int(&|value, _| { 9 | let a = value; 10 | let b = Value::I16 { 11 | value: $self.context.i16_type().const_zero(), 12 | }; 13 | 14 | // This LLVM expression is same as `lhs_value != 0` 15 | // Therefore all integers except 0 are considered true. 16 | let c = a.invoke_handler( 17 | ValueHandler::new() 18 | .handle_int(&|_, lhs_value| { 19 | b.invoke_handler(ValueHandler::new().handle_int(&|_, rhs_value| { 20 | Value::Bool { 21 | value: $self.builder.build_int_compare( 22 | IntPredicate::NE, 23 | lhs_value, 24 | rhs_value, 25 | "a", 26 | ), 27 | } 28 | })) 29 | }) 30 | .handle_float(&|_, lhs_value| { 31 | b.invoke_handler(ValueHandler::new().handle_float(&|_, rhs_value| { 32 | Value::Bool { 33 | value: $self.builder.build_float_compare( 34 | FloatPredicate::ONE, 35 | lhs_value, 36 | rhs_value, 37 | "a", 38 | ), 39 | } 40 | })) 41 | }), 42 | ); 43 | 44 | c.invoke_handler(ValueHandler::new().handle_bool(&|_, value| value)) 45 | }) 46 | // In Python, all float numbers except 0.0 are considered true. 47 | .handle_float(&|value, _| { 48 | let a = value; 49 | let b = Value::F32 { 50 | value: $self.context.f32_type().const_zero(), 51 | }; 52 | 53 | // This LLVM expression is same as `lhs_value != 0.0` 54 | // Therefore all float numbers except 0 are considered true. 55 | let c = a.invoke_handler(ValueHandler::new().handle_float(&|_, lhs_value| { 56 | b.invoke_handler(ValueHandler::new().handle_float(&|_, rhs_value| { 57 | Value::Bool { 58 | value: $self.builder.build_float_compare( 59 | FloatPredicate::ONE, 60 | lhs_value, 61 | rhs_value, 62 | "a", 63 | ), 64 | } 65 | })) 66 | })); 67 | 68 | c.invoke_handler(ValueHandler::new().handle_bool(&|_, value| value)) 69 | }) 70 | }}; 71 | } 72 | -------------------------------------------------------------------------------- /python/macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod condition_value; 3 | -------------------------------------------------------------------------------- /python/parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsp_python_parser" 3 | version = "0.1.0" 4 | authors = ["탁동혁 "] 5 | edition = "2018" 6 | description = "RustPython wrapper library for the dspython project." 7 | license = "MIT" 8 | repository = "https://github.com/tdh8316/dspython" 9 | 10 | [dependencies] 11 | rustpython-parser = { git = "https://github.com/RustPython/RustPython" } 12 | rustpython-compiler = { git = "https://github.com/RustPython/RustPython" } 13 | -------------------------------------------------------------------------------- /python/parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use rustpython_compiler::error::CompileError; 2 | pub use rustpython_parser::*; 3 | -------------------------------------------------------------------------------- /python/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/arduino/avrdude.rs: -------------------------------------------------------------------------------- 1 | use std::process::{Command, Stdio}; 2 | 3 | use crate::utils::get_arduino_dir; 4 | 5 | #[derive(Clone)] 6 | pub struct AVRDudeFlags { 7 | pub mcu: String, 8 | pub port: String, 9 | pub baudrate: u64, 10 | } 11 | 12 | impl AVRDudeFlags { 13 | pub fn new(mcu: String, port: &str, baudrate: u64) -> Self { 14 | AVRDudeFlags { 15 | mcu, 16 | port: port.to_string(), 17 | baudrate, 18 | } 19 | } 20 | } 21 | 22 | /// Use avrdude to flash hex 23 | pub fn avrdude(target_path: &str, flags: AVRDudeFlags) { 24 | // Load the environmental variable: `ARDUINO_DIR` 25 | let arduino_dir = get_arduino_dir(); 26 | 27 | // avrdude from Arduino IDE 28 | let avrdude_executable = format!("{}/{}", arduino_dir, "hardware/tools/avr/bin/avrdude"); 29 | 30 | let config_file = format!("-C{}/{}", arduino_dir, "/hardware/tools/avr/etc/avrdude.conf"); 31 | let mcu = format!("-p{}", flags.mcu); 32 | let port = format!("-P{}", flags.port); 33 | let memtype = format!("-Uflash:w:{}:i", target_path); 34 | 35 | let mut args = vec![ 36 | // avrdude executable path 37 | avrdude_executable.as_str(), 38 | // Arduino configuration file for the avrdude 39 | &config_file, 40 | // verbose output 41 | "-v", 42 | // micro controller unit 43 | &mcu, 44 | // programmer type 45 | "-carduino", 46 | // serial port 47 | &port, 48 | // baudrate 49 | "-b115200", 50 | // Disable auto erase for flash memory 51 | "-D", 52 | // memory operation specification 53 | &memtype, 54 | ]; 55 | 56 | let mut process = if cfg!(target_os = "windows") { 57 | println!("{}", args.join(" ")); 58 | args.insert(0, "/C"); 59 | Command::new("cmd") 60 | .args(args.as_slice()) 61 | .stdout(Stdio::inherit()) 62 | .stdout(Stdio::inherit()) 63 | .spawn() 64 | .expect("Failed to execute avrdude!") 65 | } else { 66 | println!("{}", args.join(" ")); 67 | args.insert(0, "-c"); 68 | Command::new("sh") 69 | .args(args.as_slice()) 70 | .stdout(Stdio::inherit()) 71 | .stdout(Stdio::inherit()) 72 | .spawn() 73 | .expect("Failed to execute avrdude!") 74 | }; 75 | let status = process.wait().unwrap(); 76 | if !status.success() { 77 | panic!( 78 | "ERROR: avrdude returned non-zero status {}", 79 | status.code().unwrap_or(-1), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/arduino/avrgcc.rs: -------------------------------------------------------------------------------- 1 | use std::fs::create_dir_all; 2 | use std::process::{Command, Stdio}; 3 | 4 | use crate::get_arduino_dir; 5 | 6 | #[derive(Clone)] 7 | pub struct AVRCompilerFlags { 8 | pub cpu_f: u64, 9 | pub mcu: String, 10 | } 11 | 12 | impl AVRCompilerFlags { 13 | pub fn new(cpu_f: u64, mcu: String) -> Self { 14 | AVRCompilerFlags { cpu_f, mcu } 15 | } 16 | } 17 | 18 | /// Generate full-linked hex file from an object and return the file path 19 | pub fn avrgcc(object: &str, flags: AVRCompilerFlags) -> String { 20 | // Load the environmental variable: `ARDUINO_DIR` 21 | let arduino_dir = get_arduino_dir(); 22 | 23 | // avr-gcc from Arduino IDE 24 | let gcc_executable = format!("{}/{}", arduino_dir, "/hardware/tools/avr/bin/avr-gcc"); 25 | // avr-g++ from Arduino IDE 26 | let gpp_executable = format!("{}/{}", arduino_dir, "/hardware/tools/avr/bin/avr-g++"); 27 | // avr-ar from Arduino IDE 28 | let ar_executable = format!("{}/{}", arduino_dir, "/hardware/tools/avr/bin/avr-ar"); 29 | // avr-objcopy from Arduino IDE 30 | let objcopy_executable = format!("{}/{}", arduino_dir, "/hardware/tools/avr/bin/avr-objcopy"); 31 | 32 | let mcu = format!("-mmcu={}", flags.mcu); 33 | let cpu_f = format!("-DF_CPU={}L", flags.cpu_f); 34 | let gcc_flags = vec![ 35 | "-c", 36 | "-g", 37 | "-Os", 38 | "-Wall", 39 | "-ffunction-sections", 40 | "-fdata-sections", 41 | &mcu, 42 | &cpu_f, 43 | "-MMD", 44 | "-DUSB_VID=null", 45 | "-DUSB_PID=null", 46 | "-DARDUINO=106", 47 | ]; 48 | let mut gpp_flags = gcc_flags.clone(); 49 | gpp_flags.push("-fno-exceptions"); 50 | 51 | // Headers 52 | let include_files = vec![ 53 | format!("-I{}/hardware/arduino/avr/cores/arduino", arduino_dir), 54 | format!("-I{}/hardware/arduino/avr/variants/standard", arduino_dir), 55 | format!("-I{}/hardware/tools/avr/avr/include", arduino_dir), 56 | ]; 57 | 58 | // Arduino headers 59 | let arduino_libs = format!("{}/hardware/arduino/avr/cores/arduino/", arduino_dir); 60 | 61 | let out_prefix = "arduino_build/"; 62 | 63 | // Create a directory contains build files 64 | create_dir_all(out_prefix).unwrap(); 65 | 66 | let compile_commands = vec![ 67 | // Compile standard Arduino cores 68 | "{GCC} {GCC_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}WInterrupts.c -o {OUT_PREFIX}WInterrupts.c.o", 69 | "{GCC} {GCC_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}wiring.c -o {OUT_PREFIX}wiring.c.o", 70 | "{GCC} {GCC_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}wiring_analog.c -o {OUT_PREFIX}wiring_analog.c.o", 71 | "{GCC} {GCC_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}wiring_digital.c -o {OUT_PREFIX}wiring_digital.c.o", 72 | "{GCC} {GCC_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}wiring_pulse.c -o {OUT_PREFIX}wiring_pulse.c.o", 73 | "{GCC} {GCC_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}wiring_pulse.S -o {OUT_PREFIX}wiring_pulse.S.o", 74 | "{GCC} {GCC_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}wiring_shift.c -o {OUT_PREFIX}wiring_shift.c.o", 75 | "{GCC} {GCC_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}hooks.c -o {OUT_PREFIX}hooks.c.o", 76 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}CDC.cpp -o {OUT_PREFIX}CDC.cpp.o", 77 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}HardwareSerial.cpp -o {OUT_PREFIX}HardwareSerial.cpp.o", 78 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}IPAddress.cpp -o {OUT_PREFIX}IPAddress.cpp.o", 79 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}main.cpp -o {OUT_PREFIX}main.cpp.o", 80 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}new.cpp -o {OUT_PREFIX}new.cpp.o", 81 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}Print.cpp -o {OUT_PREFIX}Print.cpp.o", 82 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}Stream.cpp -o {OUT_PREFIX}Stream.cpp.o", 83 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}Tone.cpp -o {OUT_PREFIX}Tone.cpp.o", 84 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}USBCore.cpp -o {OUT_PREFIX}USBCore.cpp.o", 85 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}WMath.cpp -o {OUT_PREFIX}WMath.cpp.o", 86 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}WString.cpp -o {OUT_PREFIX}WString.cpp.o", 87 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}PluggableUSB.cpp -o {OUT_PREFIX}PluggableUSB.cpp.o", 88 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}HardwareSerial0.cpp -o {OUT_PREFIX}HardwareSerial0.cpp.o", 89 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}HardwareSerial1.cpp -o {OUT_PREFIX}HardwareSerial1.cpp.o", 90 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} {ARDUINO_LIBS}abi.cpp -o {OUT_PREFIX}abi.cpp.o", 91 | 92 | // Compile DSPython wrapper 93 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} include/Serial.cc -o {OUT_PREFIX}Serial.cc.o", 94 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} include/Builtins.cc -o {OUT_PREFIX}Builtins.cc.o", 95 | "{GPP} {GPP_FLAGS} {INCLUDE_FILES} include/LLVMArduinoBuiltins.cc -o {OUT_PREFIX}LLVMArduinoBuiltins.cc.o", 96 | 97 | // Archiver 98 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}WInterrupts.c.o", 99 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}wiring.c.o", 100 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}wiring_analog.c.o", 101 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}wiring_digital.c.o", 102 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}wiring_pulse.c.o", 103 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}wiring_pulse.S.o", 104 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}wiring_shift.c.o", 105 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}hooks.c.o", 106 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}CDC.cpp.o", 107 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}HardwareSerial.cpp.o", 108 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}IPAddress.cpp.o", 109 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}main.cpp.o", 110 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}new.cpp.o", 111 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}Print.cpp.o", 112 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}Stream.cpp.o", 113 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}Tone.cpp.o", 114 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}USBCore.cpp.o", 115 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}WMath.cpp.o", 116 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}WString.cpp.o", 117 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}PluggableUSB.cpp.o", 118 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}HardwareSerial0.cpp.o", 119 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}HardwareSerial1.cpp.o", 120 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}abi.cpp.o", 121 | 122 | // Link DSPython wrapper library 123 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}Serial.cc.o", 124 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}Builtins.cc.o", 125 | "{AR} rcs {OUT_PREFIX}core.a {OUT_PREFIX}LLVMArduinoBuiltins.cc.o", 126 | 127 | // Compile everything together 128 | "{GCC} -w -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -mmcu={MCU} -o {INPUT}.elf {INPUT} {OUT_PREFIX}core.a -lm", 129 | "{OBJCOPY} -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 {INPUT}.elf {INPUT}.eep", 130 | "{OBJCOPY} -O ihex -R .eeprom {INPUT}.elf {INPUT}.hex", 131 | ]; 132 | 133 | for command in compile_commands { 134 | let command_string = command 135 | .replace("{GCC}", &gcc_executable) 136 | .replace("{GCC_FLAGS}", &gcc_flags.join(" ")) 137 | .replace("{INCLUDE_FILES}", &include_files.join(" ")) 138 | .replace("{ARDUINO_LIBS}", &arduino_libs) 139 | .replace("{OUT_PREFIX}", out_prefix) 140 | .replace("{GPP}", &gpp_executable) 141 | .replace("{GPP_FLAGS}", &gpp_flags.join(" ")) 142 | .replace("{AR}", &ar_executable) 143 | .replace("{MCU}", &flags.mcu) 144 | .replace("{OBJCOPY}", &objcopy_executable) 145 | .replace("{INPUT}", object); 146 | let mut args = command_string.as_str().split(" ").collect::>(); 147 | 148 | let mut process = if cfg!(target_os = "windows") { 149 | args.insert(0, "/C"); 150 | Command::new("cmd") 151 | .args(args.as_slice()) 152 | .stdout(Stdio::inherit()) 153 | .stdout(Stdio::inherit()) 154 | .spawn() 155 | .expect(&format!( 156 | "Failed to execute command '{}'", 157 | command_string.as_str() 158 | )) 159 | } else { 160 | args.insert(0, "-c"); 161 | Command::new("sh") 162 | .args(args.as_slice()) 163 | .stdout(Stdio::inherit()) 164 | .stdout(Stdio::inherit()) 165 | .spawn() 166 | .expect(&format!( 167 | "Failed to execute command '{}'", 168 | command_string.as_str() 169 | )) 170 | }; 171 | let status = process.wait().unwrap(); 172 | if !status.success() { 173 | eprintln!( 174 | "'{}' returned non-zero status {}", 175 | command_string.as_str(), 176 | status.code().unwrap_or(-1) 177 | ); 178 | panic!("ERROR: avrgcc failed"); 179 | } 180 | } 181 | 182 | return format!("{}.hex", object); 183 | } 184 | -------------------------------------------------------------------------------- /src/arduino/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod avrdude; 2 | pub mod avrgcc; 3 | -------------------------------------------------------------------------------- /src/bin/dspython.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fs::{remove_file, write, File}; 3 | 4 | use clap::{App, Arg, ArgMatches}; 5 | 6 | use dsp_compiler::{get_assembly, CompilerFlags}; 7 | use dspython::{avrdude, avrgcc, static_compiler, AVRCompilerFlags, AVRDudeFlags}; 8 | 9 | const VERSION: &'static str = env!("CARGO_PKG_VERSION"); 10 | const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS"); 11 | 12 | fn parse_arguments<'a>(app: App<'a, '_>) -> ArgMatches<'a> { 13 | let arg_file = Arg::with_name("file") 14 | .required(true) 15 | .help("The source file"); 16 | let arg_port = Arg::with_name("port") 17 | .help("Serial port of an Arduino to upload") 18 | .long("--upload-to") 19 | .short("u") 20 | .takes_value(true); 21 | let arg_cpu = Arg::with_name("cpu") 22 | .long("--cpu") 23 | .short("c") 24 | .takes_value(true) 25 | .default_value("atmega328p"); 26 | let arg_opt = Arg::with_name("opt_level") 27 | .help("LLVM Optimization level. Must be in the range of 0 to 3") 28 | .long("--opt-level") 29 | .short("o") 30 | .takes_value(true) 31 | .default_value("2"); 32 | let arg_baudrate = Arg::with_name("baudrate") 33 | .help("Serial communication speed") 34 | .long("--baudrate") 35 | .short("b") 36 | .takes_value(true) 37 | .default_value("9600"); 38 | let arg_remove_hex = Arg::with_name("remove_hex") 39 | .help("Remove generated hex file") 40 | .long("--remove-hex") 41 | .takes_value(false); 42 | let arg_emit_llvm = Arg::with_name("emit_llvm") 43 | .help("Emit LLVM IR") 44 | .long("--emit-llvm") 45 | .takes_value(false); 46 | 47 | app.arg(arg_file) 48 | .arg(arg_opt) 49 | .arg(arg_baudrate) 50 | .arg(arg_port) 51 | .arg(arg_cpu) 52 | .arg(arg_remove_hex) 53 | .arg(arg_emit_llvm) 54 | .get_matches() 55 | } 56 | 57 | fn main() -> Result<(), Box> { 58 | let app = App::new("dspython") 59 | .version(VERSION) 60 | .author(AUTHORS) 61 | .about("DSPython is a damn small Python compiler intended to use in Arduino."); 62 | 63 | // Parse command-line arguments 64 | let matches = parse_arguments(app); 65 | let file = matches.value_of("file").expect("no input files"); 66 | let port = matches.value_of("port"); 67 | let cpu = matches.value_of("cpu").unwrap(); 68 | let optimization_level = matches.value_of("opt_level").unwrap().parse::()?; 69 | 70 | let compiler_flags = CompilerFlags::new(optimization_level); 71 | 72 | // Generate assembly from given file 73 | let ir_path = format!("{}.ll", file); 74 | let assembly = match get_assembly(file.to_string(), compiler_flags) { 75 | Ok(llvm_string) => llvm_string, 76 | Err(e) => panic!("{}", e), 77 | }; 78 | write(&ir_path, assembly.to_string())?; 79 | 80 | // Generate object 81 | let object = static_compiler(&ir_path, cpu, optimization_level); 82 | 83 | // Run avr-gcc to create hex file 84 | let avr_compiler_flags = AVRCompilerFlags::new(16000000, cpu.to_owned()); 85 | let hex = avrgcc(&object, avr_compiler_flags); 86 | 87 | // Check the size of hex 88 | let hex_file = File::open(&hex)?; 89 | let file_size = hex_file.metadata()?.len(); 90 | if file_size > 30 * 1024 { 91 | eprintln!( 92 | "WARNING: The size of the result file ({}KB) is larger than 30KB.", 93 | file_size / 1024 94 | ); 95 | } 96 | 97 | // Run avrdude to flash memory if a serial port is presented 98 | if let Some(port) = port { 99 | let avrdude_flags = AVRDudeFlags::new( 100 | cpu.to_owned(), 101 | port, 102 | matches.value_of("baudrate").unwrap().parse::()?, 103 | ); 104 | avrdude(&hex, avrdude_flags); 105 | } 106 | 107 | // Remove intermediate files 108 | remove_file(&object)?; 109 | remove_file(format!("{}.eep", &object))?; 110 | remove_file(format!("{}.elf", &object))?; 111 | 112 | // Remove the hex file if --remove-hex is presented after finishing upload 113 | if matches.is_present("remove_hex") { 114 | remove_file(hex)?; 115 | } 116 | 117 | // Remove the llvm ir if --emit-llvm is not presented 118 | if !matches.is_present("emit_llvm") { 119 | remove_file(&ir_path)?; 120 | } 121 | 122 | Ok(()) 123 | } 124 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use arduino::avrdude::{avrdude, AVRDudeFlags}; 2 | pub use arduino::avrgcc::{avrgcc, AVRCompilerFlags}; 3 | pub use utils::{get_arduino_dir, static_compiler}; 4 | 5 | mod arduino; 6 | mod utils; 7 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::process::{Command, Stdio}; 3 | 4 | pub fn get_arduino_dir() -> String { 5 | let _arduino_dir = env::var("ARDUINO_DIR").expect( 6 | "You must set the environment variable 'ARDUINO_DIR' as your arduino software location!", 7 | ); 8 | let arduino_dir = _arduino_dir.to_string(); 9 | 10 | return arduino_dir; 11 | } 12 | 13 | /// Generate non-linked object file from llvm ir 14 | pub fn static_compiler(ir_path: &str, cpu: &str, optimization_level: u8) -> String { 15 | let out = format!("{}.o", ir_path); 16 | let output = format!("-o={}", &out); 17 | let cpu = format!("-mcpu={}", cpu); 18 | let optimization_level = format!("-O{}", optimization_level); 19 | let mut args = vec![ 20 | "llc", 21 | "-filetype=obj", 22 | ir_path, 23 | &output, 24 | &optimization_level, 25 | "--march=avr", 26 | &cpu, 27 | "--thread-model=single", 28 | ]; 29 | let mut process = if cfg!(target_os = "windows") { 30 | // println!("{}", args.join(" ")); 31 | args.insert(0, "/C"); 32 | Command::new("cmd") 33 | .args(args.as_slice()) 34 | .stdout(Stdio::inherit()) 35 | .stdout(Stdio::inherit()) 36 | .spawn() 37 | .expect("Failed to execute llc!") 38 | } else { 39 | // println!("{}", args.join(" ")); 40 | args.insert(0, "-c"); 41 | Command::new("sh") 42 | .args(args.as_slice()) 43 | .stdout(Stdio::inherit()) 44 | .stdout(Stdio::inherit()) 45 | .spawn() 46 | .expect("Failed to execute llc!") 47 | }; 48 | let status = process.wait().unwrap(); 49 | if !status.success() { 50 | panic!("ERROR: llc failed"); 51 | } 52 | 53 | return out; 54 | } 55 | -------------------------------------------------------------------------------- /tests/fib.py: -------------------------------------------------------------------------------- 1 | """ 2 | Fibonacci series 3 | """ 4 | 5 | from arduino import * 6 | 7 | 8 | def fib_while(n: int) -> int: 9 | a = 0 10 | b = 1 11 | res = 0 12 | count = 1 13 | 14 | while count < n: 15 | count = count + 1 16 | a = b 17 | b = res 18 | res = a + b 19 | 20 | return res 21 | 22 | 23 | def fib_recursion(n: int) -> int: 24 | if n <= 0: 25 | print("n must be an integer greater than zero!") 26 | # If the function ends without a return statement 27 | # DSPython automatically adds `return 0` that fits the type of this function 28 | elif n == 1: 29 | return 0 30 | elif n == 2: 31 | return 1 32 | else: 33 | return fib_recursion(n - 1) + fib_recursion(n - 2) 34 | 35 | 36 | def setup(): 37 | serial_begin(9600) 38 | 39 | print("fib(10) Using loop:") 40 | println(fib_while(10)) 41 | 42 | print("fib(10) Using recursion:") 43 | println(fib_recursion(10)) 44 | 45 | 46 | def loop(): 47 | return None 48 | -------------------------------------------------------------------------------- /tests/x.py: -------------------------------------------------------------------------------- 1 | """ 2 | DSPython test 3 | """ 4 | 5 | from arduino import * 6 | 7 | 8 | def setup(): 9 | """This is a test""" 10 | serial_begin(9600) 11 | 12 | print("Hello, world!") 13 | 14 | # arr: list = [0, 1, 2] 15 | 16 | # print(arr[0]) 17 | # print(arr[1]) 18 | # print(arr[2]) 19 | 20 | 21 | def loop(): 22 | return 23 | --------------------------------------------------------------------------------