diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2024-05-27 16:04:31 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2024-05-27 16:04:31 -0700 |
| commit | 4debd88807c538aa430ce69b83fdb291f687b1b1 (patch) | |
| tree | 5def99861585367826937c2bdbbfba7f23e07f0b | |
| download | qc-4debd88807c538aa430ce69b83fdb291f687b1b1.tar.zst | |
Rewrite in Rust.
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Cargo.lock | 840 | ||||
| -rw-r--r-- | Cargo.toml | 13 | ||||
| -rw-r--r-- | flake.lock | 95 | ||||
| -rw-r--r-- | flake.nix | 26 | ||||
| -rw-r--r-- | src/lexer.rs | 74 | ||||
| -rw-r--r-- | src/main.rs | 89 | ||||
| -rw-r--r-- | src/num.rs | 183 | ||||
| -rw-r--r-- | src/parser.rs | 202 |
9 files changed, 1524 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d787b70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/result diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..187dd2c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,840 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cairo-rs" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ac2a4d0e69036cf0062976f6efcba1aaee3e448594e6514bb2ddf87acce562" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3bb3119664efbd78b5e6c93957447944f16bdbced84c17a9f41c7829b81e64" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "event-listener" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6a23f8a0b5090494fd04924662d463f8386cc678dd3915015a838c1a3679b92" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fdbf021f8b9d19e30fb9ea6d6e5f2b6a712fe4645417c69f86f6ff1e1444a8f" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk4" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db265c9dd42d6a371e09e52deab3a84808427198b86ac792d75fd35c07990a07" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk4-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9418fb4e8a67074919fe7604429c45aa74eb9df82e7ca529767c6d4e9dc66dd" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gio" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be548be810e45dd31d3bbb89c6210980bb7af9bca3ea1292b5f16b75f8e394a7" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4bdbef451b0f0361e7f762987cc6bebd5facab1d535e85a3cf1115dfb08db40" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys", +] + +[[package]] +name = "glib" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52355166df21c7ed16b6a01f615669c7911ed74e27ef60eba339c0d2da12490" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70025dbfa1275cf7d0531c3317ba6270dae15d87e63342229d638246ff45202e" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767d23ead9bbdfcbb1c2242c155c8128a7d13dde7bf69c176f809546135e2282" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3787b0bfacca12bb25f8f822b0dbee9f7e4a86e6469a29976d332d2c14c945b" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "graphene-rs" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e4d388e96c5f29e2b2f67045d229ddf826d0a8d6d282f94ed3b34452222c91" +dependencies = [ + "glib", + "graphene-sys", + "libc", +] + +[[package]] +name = "graphene-sys" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60e7381afdd7be43bd10a89d3b6741d162aabbca3a8db73505afb6a3aea59d" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gsk4" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7563884bf6939f4468e5d94654945bdd9afcaf8c3ba4c5dd17b5342b747221be" +dependencies = [ + "cairo-rs", + "gdk4", + "glib", + "graphene-rs", + "gsk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gsk4-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23024bf2636c38bbd1f822f58acc9d1c25b28da896ff0f291a1a232d4272b3dc" +dependencies = [ + "cairo-sys-rs", + "gdk4-sys", + "glib-sys", + "gobject-sys", + "graphene-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk4" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b04e11319b08af11358ab543105a9e49b0c491faca35e2b8e7e36bfba8b671ab" +dependencies = [ + "cairo-rs", + "field-offset", + "futures-channel", + "gdk-pixbuf", + "gdk4", + "gio", + "glib", + "graphene-rs", + "gsk4", + "gtk4-macros", + "gtk4-sys", + "libc", + "pango", +] + +[[package]] +name = "gtk4-macros" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec655a7ef88d8ce9592899deb8b2d0fa50bab1e6dd69182deb764e643c522408" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "gtk4-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c8aa86b7f85ea71d66ea88c1d4bae1cfacf51ca4856274565133838d77e57b5" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk4-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "graphene-sys", + "gsk4-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-bigint" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "pango" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504ce6e805439ea2c6791168fe7ef8e3da0c1b2ef82c44bc450dbc330592920d" +dependencies = [ + "gio", + "glib", + "libc", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4829555bdbb83692ddeaf5a6927fb2d025c8131e5ecaa4f7619fff6985d3505" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro2" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qc" +version = "0.1.0" +dependencies = [ + "async-channel", + "gdk4", + "gtk4", + "num-bigint", + "num-traits", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.13", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.8", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..094829c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "qc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-channel = "2.3.1" +gdk4 = "0.8.2" +gtk4 = "0.8.2" +num-bigint = "0.4.5" +num-traits = "0.2.19" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..4579b3b --- /dev/null +++ b/flake.lock @@ -0,0 +1,95 @@ +{ + "nodes": { + "naersk": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1713520724, + "narHash": "sha256-CO8MmVDmqZX2FovL75pu5BvwhW+Vugc7Q6ze7Hj8heI=", + "owner": "nix-community", + "repo": "naersk", + "rev": "c5037590290c6c7dae2e42e7da1e247e54ed2d49", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "naersk", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1716715802, + "narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1716715802, + "narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "naersk": "naersk", + "nixpkgs": "nixpkgs_2", + "utils": "utils" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..d96ac0b --- /dev/null +++ b/flake.nix @@ -0,0 +1,26 @@ +{ + inputs = { + naersk.url = "github:nix-community/naersk/master"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, utils, naersk }: + utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + deps = with pkgs; [ pkg-config graphene cairo gtk4 ]; + naersk-lib = pkgs.callPackage naersk {}; + in + { + defaultPackage = naersk-lib.buildPackage { + src = ./.; + nativeBuildInputs = deps; + }; + devShell = with pkgs; mkShell { + buildInputs = [ cargo rustc rustfmt pre-commit rustPackages.clippy ] ++ deps; + RUST_SRC_PATH = rustPlatform.rustLibSrc; + }; + } + ); +} diff --git a/src/lexer.rs b/src/lexer.rs new file mode 100644 index 0000000..49d2d04 --- /dev/null +++ b/src/lexer.rs @@ -0,0 +1,74 @@ +pub struct Lexer<'a> { + pub buf: &'a str, +} + +impl<'a> Lexer<'a> { + fn skip_whitespace(&mut self) { + while let Some(b' ') = self.buf.as_bytes().first() { + self.buf = &self.buf[1..] + } + } + + fn parse_num(&mut self) -> Option<&'a str> { + let mut i = 0; + while matches!(self.buf.as_bytes().get(i), Some(&b) if b.is_ascii_digit()) { + i += 1; + } + if let Some(b'.') = self.buf.as_bytes().get(i) { + i += 1; + } + while matches!(self.buf.as_bytes().get(i), Some(&b) if b.is_ascii_digit()) { + i += 1; + } + let (Some(b'e') | Some(b'E')) = self.buf.as_bytes().get(i) else { + let t = &self.buf[..i]; + self.buf = &self.buf[i..]; + return Some(t); + }; + i += 1; + if let Some(b'-') = self.buf.as_bytes().get(i) { + i += 1; + } + while matches!(self.buf.as_bytes().get(i), Some(&b) if b.is_ascii_digit()) { + i += 1; + } + let t = &self.buf[..i]; + self.buf = &self.buf[i..]; + Some(t) + } +} + +impl<'a> Iterator for Lexer<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option<&'a str> { + self.skip_whitespace(); + let &first_byte = self.buf.as_bytes().first()?; + if first_byte.is_ascii_digit() || first_byte == b'.' { + return self.parse_num(); + } + match first_byte { + b'/' => { + if let Some(b'/') = self.buf.as_bytes().get(1) { + self.buf = &self.buf[2..]; + return Some("//"); + } + self.buf = &self.buf[1..]; + return Some("/"); + } + b'+' | b'-' | b'*' | b'^' | b'(' | b')' => { + let t = &self.buf[..1]; + self.buf = &self.buf[1..]; + return Some(t); + } + _ => (), + } + let mut i = 0; + while let Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9') = self.buf.as_bytes().get(i) { + i += 1; + } + let t = &self.buf[..i]; + self.buf = &self.buf[i..]; + Some(t) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1f4291e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,89 @@ +mod lexer; +mod num; +mod parser; + +use gdk4::{Key, ModifierType}; +use gtk4::glib::{clone, ExitCode}; +use gtk4::prelude::{ + ApplicationExt, ApplicationExtManual, EditableExt, GridExt, GtkWindowExt, WidgetExt, +}; +use gtk4::{glib, Application, ApplicationWindow, Entry, EventControllerKey, Grid}; +use std::sync::{Arc, Mutex}; + +fn send_mail<T>(m: &Mutex<Option<T>>, msg: T) { + let mut guard = m.lock().unwrap(); + *guard = Some(msg); +} + +fn get_mail<T>(m: &Mutex<Option<T>>) -> Option<T> { + let mut guard = m.lock().unwrap(); + std::mem::take(&mut guard) +} + +fn main() -> ExitCode { + let app = Application::builder() + .application_id("com.github.rhogenson.qc") + .build(); + app.connect_activate(|app| { + let window = ApplicationWindow::builder() + .application(app) + .decorated(false) + .resizable(false) + .build(); + + let grid = Grid::new(); + window.set_child(Some(&grid)); + + let input = Entry::new(); + grid.attach(&input, 0, 0, 1, 1); + + let output = Entry::builder().editable(false).build(); + grid.attach(&output, 0, 1, 1, 1); + + let (ping_send, ping_recv) = std::sync::mpsc::sync_channel(1); + let mailbox = Arc::new(Mutex::new(None)); + + let key_controller = EventControllerKey::new(); + key_controller.connect_key_released( + clone!(@weak window, @weak input, @strong mailbox => move |_, k, _, modifiers| { + if k == Key::Escape + || k == Key::Return + || k == Key::bracketleft && modifiers.contains(ModifierType::CONTROL_MASK) + { + window.destroy(); + } + send_mail(mailbox.as_ref(), format!("{}", input.text())); + let _ = ping_send.try_send(()); + }), + ); + window.add_controller(key_controller); + + let (output_send, output_recv) = async_channel::bounded(1); + let (confirm_send, confirm_recv) = async_channel::bounded(1); + + gtk4::gio::spawn_blocking(move || loop { + let Ok(_) = ping_recv.recv() else { + return; + }; + let Some(input) = get_mail(mailbox.as_ref()) else { + continue; + }; + let Some(result) = parser::parse(&input) else { + continue; + }; + output_send.send_blocking(result).unwrap(); + confirm_recv.recv_blocking().unwrap(); + }); + + glib::spawn_future_local(clone!(@weak output => async move { + while let Ok(result) = output_recv.recv().await { + output.set_text(&format!("{}", result)); + confirm_send.send(()).await.unwrap(); + } + })); + + window.present(); + }); + + app.run() +} diff --git a/src/num.rs b/src/num.rs new file mode 100644 index 0000000..f17c076 --- /dev/null +++ b/src/num.rs @@ -0,0 +1,183 @@ +use num_bigint::BigInt; +use num_traits::cast::ToPrimitive; +use num_traits::pow::Pow; +use num_traits::FromPrimitive; +use std::fmt::{Display, Formatter}; + +pub enum Num { + Int(BigInt), + Float(f64), +} + +pub fn int(i: BigInt) -> Num { + Num::Int(i) +} + +pub fn float(f: f64) -> Num { + Num::Float(f) +} + +fn float_from_int(i: &BigInt) -> f64 { + if let Some(f) = i.to_f64() { + return f; + } + if i > &BigInt::ZERO { + std::f64::INFINITY + } else { + std::f64::NEG_INFINITY + } +} + +impl Num { + fn as_float(&self) -> f64 { + match self { + Num::Int(i) => float_from_int(i), + Num::Float(f) => *f, + } + } + + pub fn pow(self, other: Num) -> Num { + match (self, other) { + (Num::Int(i1), Num::Int(i2)) => { + if i2 < BigInt::ZERO { + return float(float_from_int(&i1).powf(float_from_int(&i2))); + } + let (_, u2) = i2.into_parts(); + int(i1.pow(u2)) + } + (n1, n2) => float(n1.as_float().powf(n2.as_float())), + } + } + + pub fn mul(self, other: Num) -> Num { + match (self, other) { + (Num::Int(i1), Num::Int(i2)) => int(i1 * i2), + (n1, n2) => float(n1.as_float() * n2.as_float()), + } + } + + pub fn div(self, other: Num) -> Num { + float(self.as_float() / other.as_float()) + } + + pub fn int_div(self, other: Num) -> Num { + match (self, other) { + (Num::Int(i1), Num::Int(i2)) => int(i1 / i2), + (Num::Float(f1), Num::Float(f2)) => { + let r = f1 / f2; + if let Some(i) = BigInt::from_f64(r) { + return int(i); + } + float(r) + } + (Num::Int(i), Num::Float(f)) => { + if let Some(fi) = BigInt::from_f64(f) { + return int(i / fi); + } + float(float_from_int(&i) / f) + } + (Num::Float(f), Num::Int(i)) => { + if let Some(fi) = BigInt::from_f64(f) { + return int(fi / i); + } + float(f / float_from_int(&i)) + } + } + } + + pub fn add(self, other: Num) -> Num { + match (self, other) { + (Num::Int(i1), Num::Int(i2)) => int(i1 + i2), + (n1, n2) => float(n1.as_float() + n2.as_float()), + } + } + + pub fn sub(self, other: Num) -> Num { + match (self, other) { + (Num::Int(i1), Num::Int(i2)) => int(i1 - i2), + (n1, n2) => Num::Float(n1.as_float() - n2.as_float()), + } + } + + pub fn sin(self) -> Num { + float(self.as_float().sin()) + } + + pub fn cos(self) -> Num { + float(self.as_float().cos()) + } + + pub fn tan(self) -> Num { + float(self.as_float().tan()) + } + + pub fn sqrt(self) -> Num { + match self { + Num::Int(i) if i >= BigInt::ZERO => { + let s = i.sqrt(); + if i != &s * &s { + return float(float_from_int(&i).sqrt()); + } + int(s) + } + n => float(n.as_float().sqrt()), + } + } + + pub fn log(self) -> Num { + float(self.as_float().ln()) + } + + pub fn floor(self) -> Num { + match self { + Num::Int(i) => int(i), + Num::Float(f) => { + if let Some(i) = BigInt::from_f64(f.floor()) { + return int(i); + } + float(f) + } + } + } + + pub fn ceil(self) -> Num { + match self { + Num::Int(i) => int(i), + Num::Float(f) => { + if let Some(i) = BigInt::from_f64(f.ceil()) { + return int(i); + } + float(f) + } + } + } + + pub fn round(self) -> Num { + match self { + Num::Int(i) => int(i), + Num::Float(f) => { + if let Some(i) = BigInt::from_f64(f.round_ties_even()) { + return int(i); + } + float(f) + } + } + } +} + +fn format_float(f: f64) -> String { + let mut s = format!("{f:.7}"); + while let Some(b'0') = s.as_bytes().get(s.len() - 1) { + s.truncate(s.len() - 1); + } + s +} + +impl Display for Num { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + Num::Int(n) => write!(f, "{n}"), + Num::Float(n) => write!(f, "{}", format_float(*n)), + } + } +} diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..fda744d --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,202 @@ +use crate::lexer::Lexer; +use crate::num; +use crate::num::Num; +use num_bigint::BigInt; +use std::str::FromStr; + +struct Parser<'a> { + r: Lexer<'a>, + first_token: Option<&'a str>, +} + +impl<'a> Parser<'a> { + fn peek(&mut self) -> Option<&'a str> { + if let Some(t) = self.first_token { + return Some(t); + } + self.first_token = self.r.next(); + self.first_token + } + + fn next(&mut self) -> Option<&'a str> { + if let Some(t) = std::mem::take(&mut self.first_token) { + return Some(t); + } + self.r.next() + } + + fn symbol(&mut self, s: &str) -> bool { + let Some(t) = self.peek() else { + return false; + }; + if t != s { + return false; + } + self.next(); + true + } + + fn parse_num(&mut self) -> Option<Num> { + let negative = self.symbol("-"); + let t = self.next()?; + if t.contains('.') || t.contains('e') || t.contains('E') { + let f = f64::from_str(t).ok()?; + if negative { + return Some(num::float(-f)); + } + return Some(num::float(f)); + } + let i = BigInt::from_str(t).ok()?; + if negative { + return Some(num::int(-i)); + } + Some(num::int(i)) + } + + fn paren_expr(&mut self) -> Option<Num> { + if !self.symbol("(") { + return None; + } + let n = self.expr()?; + if !self.symbol(")") { + return None; + } + Some(n) + } + + fn parse_const(&mut self) -> Option<Num> { + if self.symbol("e") { + return Some(num::float(std::f64::consts::E)); + } + if self.symbol("pi") { + return Some(num::float(std::f64::consts::PI)); + } + None + } + + fn atom(&mut self) -> Option<Num> { + if let Some(n) = self.paren_expr() { + return Some(n); + } + if let Some(n) = self.parse_const() { + return Some(n); + } + self.parse_num() + } + + fn parse_fun(&mut self) -> Option<Num> { + if self.symbol("sin") { + return Some(self.atom()?.sin()); + } + if self.symbol("cos") { + return Some(self.atom()?.cos()); + } + if self.symbol("tan") { + return Some(self.atom()?.tan()); + } + if self.symbol("sqrt") { + return Some(self.atom()?.sqrt()); + } + if self.symbol("log") || self.symbol("ln") { + return Some(self.atom()?.log()); + } + if self.symbol("floor") { + return Some(self.atom()?.floor()); + } + if self.symbol("ceil") || self.symbol("ceiling") { + return Some(self.atom()?.ceil()); + } + if self.symbol("round") { + return Some(self.atom()?.round()); + } + None + } + + fn app_expr_fold(&mut self, e1: Num) -> Option<Num> { + if let Some(e2) = self.paren_expr() { + return self.app_expr_fold(e1.mul(e2)); + } + if let Some(e2) = self.parse_const() { + return self.app_expr_fold(e1.mul(e2)); + } + if let Some(e2) = self.parse_fun() { + return Some(e1.mul(e2)); + } + Some(e1) + } + + fn app_expr(&mut self) -> Option<Num> { + if let Some(e) = self.parse_fun() { + return Some(e); + } + let e1 = self.atom()?; + self.app_expr_fold(e1) + } + + fn expt_expr_fold(&mut self, e1: Num) -> Option<Num> { + if !self.symbol("^") { + return Some(e1); + } + let e2 = self.app_expr()?; + if let Some(e2) = self.expt_expr_fold(e2) { + return Some(e1.pow(e2)); + } + None + } + + fn expt_expr(&mut self) -> Option<Num> { + let n = self.app_expr()?; + self.expt_expr_fold(n) + } + + fn mul_expr_fold(&mut self, e1: Num) -> Option<Num> { + if self.symbol("*") { + let e2 = self.expt_expr()?; + let e = e1.mul(e2); + return self.mul_expr_fold(e); + } + if self.symbol("/") { + let e2 = self.expt_expr()?; + let e = e1.div(e2); + return self.mul_expr_fold(e); + } + if self.symbol("//") { + let e2 = self.expt_expr()?; + let e = e1.int_div(e2); + return self.mul_expr_fold(e); + } + Some(e1) + } + + fn mul_expr(&mut self) -> Option<Num> { + let n = self.expt_expr()?; + self.mul_expr_fold(n) + } + + fn add_expr_fold(&mut self, e1: Num) -> Option<Num> { + if self.symbol("+") { + let e2 = self.mul_expr()?; + let e = e1.add(e2); + return self.add_expr_fold(e); + } + if self.symbol("-") { + let e2 = self.mul_expr()?; + let e = e1.sub(e2); + return self.add_expr_fold(e); + } + Some(e1) + } + + fn expr(&mut self) -> Option<Num> { + let n = self.mul_expr()?; + self.add_expr_fold(n) + } +} + +pub fn parse(expr: &str) -> Option<Num> { + let mut p = Parser { + r: Lexer { buf: expr }, + first_token: None, + }; + p.expr() +} |
