Compare commits

..

394 commits

Author SHA1 Message Date
Erin a21fe4de57 Update Cargo lock 2023-11-30 16:55:40 +01:00
Erin 7aad9b83f0 Release bump 2023-05-19 00:25:14 +02:00
Erin 1bcd5ce9b0 Fixed BF code parsing 2023-05-18 23:49:54 +02:00
Erin 89c4fe68d2 Updated dependencies. 2023-05-18 20:33:58 +02:00
Erin 1d91e68b05 shorten file 2023-01-04 15:30:03 +01:00
Erin acd7cad29e Fix clippy complaining when used instead of cargo check with RA 2022-11-04 00:58:23 +01:00
Erin 4dfddc2cdb initial vars - &mut self 2022-09-21 00:28:57 +02:00
Erin e936585112 fmt??? 2022-09-19 20:58:50 +02:00
Erin 61fbd3ee28 Host Interface initial vars support 2022-09-19 20:58:32 +02:00
Erin 2690c65c21 stuff 2022-09-14 21:57:02 +02:00
Erin fd4845ac8d Bumped version 2022-09-14 21:52:17 +02:00
Erin 9803a393d4 Fixed #13 2022-09-14 21:50:04 +02:00
Erin a9fdff0663 fmt + clippy 2022-09-14 21:46:24 +02:00
Erin d004955c4e Fixed issue #12 2022-09-14 21:46:17 +02:00
Erin e674052942 thing 2022-08-18 14:58:46 +02:00
Erin cbb7152890 No need for the associated type :D 2022-07-02 12:55:10 +02:00
Erin 1781a66c3a Revert "Added emoji support for identifiers"
This reverts commit 167a5077df.
2022-07-02 12:47:16 +02:00
Erin 18943f184d cycle detecion on cart print - no longer stack overflow 2022-07-02 01:17:31 +02:00
Erin 167a5077df Added emoji support for identifiers 2022-07-02 00:47:09 +02:00
Erin 5c0e89e38e Invalid Token error + fixed forgotten refactor 2022-07-02 00:30:51 +02:00
Erin f2d3561508 Error comments + renaming 2022-07-02 00:27:38 +02:00
Erin 63af15cc94 EOF -> EOI 2022-07-02 00:22:40 +02:00
Erin 24c5ada994 Bumped AbleScript version
+ chore: update
+ clap minor update
+ change repo URLs
2022-07-02 00:19:44 +02:00
Erin 83b25b2b89 Implemented host interface 2022-07-02 00:17:29 +02:00
Erin e883010580 Well, Able and Eval functios can now change loops flow! 2022-07-01 23:12:59 +02:00
Erin 472b41cf16 Implemented finally 2022-07-01 22:56:22 +02:00
Erin 844ccd731a chore: cargo update 2022-06-23 19:00:11 +02:00
Erin 49e36b0a21 Value Refactoring 2022-06-17 20:58:37 +02:00
Erin 43c9293e4f Dependencies update 2022-06-07 23:32:25 +02:00
Erin 7140082ff6 Parser cleanup 2022-06-06 23:30:08 +02:00
Erin 84f54f12d6 Changed dim syntax 2022-06-03 00:10:19 +02:00
Erin 2c7596c871 ADDED VERY IMPORTANT CHANGE 2022-06-02 23:56:32 +02:00
Erin c0a2857122 Changed hopback to and again (credits: Evrey#6086) 2022-05-17 19:03:02 +02:00
Erin bb3de6db14 chore: cargo update 2022-05-17 18:04:09 +02:00
Alex Bethel 507f700c44 Merge branch 'v0.4.0-fixes' 2022-05-09 17:58:36 -06:00
Alex Bethel ea802fbdf9 Fix AbleScript lib doc comment 2022-05-09 17:56:28 -06:00
Erin 7ec0b0b39d Replaced break with enough (credits: Evrey#6086) 2022-05-06 23:52:43 +02:00
Erin 3d745e6082 Bumped version to 0.4.0 2022-05-06 21:22:57 +02:00
Erin e4114ed2e8 changed undefined -> cart coercion 2022-05-06 21:12:55 +02:00
Erin ed1b3bfb43 Implemented all coercions for undefined 2022-05-06 21:08:16 +02:00
Erin d97d940d23 Evaluating nonextant variable results into Undefined 2022-05-06 20:20:52 +02:00
Erin bda92cc2f7 Added Undefined type 2022-05-06 20:19:35 +02:00
Erin aa29a1fc86 {} -> () 2022-05-06 17:30:41 +02:00
Erin 86dfc6d1f7 Removed arity checking and arity error 2022-05-06 17:28:25 +02:00
Erin 28a5415207 Renamed few errors 2022-05-06 17:25:50 +02:00
Erin 766eb799c9 Changed error variant name to go by conventions 2022-05-05 00:39:13 +02:00
Erin edb03dec94 get_bit returns error kind which makes the error spanned 2022-05-05 00:36:13 +02:00
Erin 7b5a9aed89 Fixed zero division and zero assertion errors when [] / 0 2022-05-04 23:15:12 +02:00
Erin 947c2552b1 fmt + clippy 2022-05-03 23:19:10 +02:00
Erin 6ad0ab5e29 Added non-newline print. 2022-05-03 23:13:17 +02:00
Erin fd97655e04 Merge pull request 'T-Dark letter cases implemented' (#10) from feature/tdark-cases into master
Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/10
2022-05-03 21:03:42 +00:00
Erin 1a61c66e10 T-Dark letter cases implemented 2022-05-02 23:40:42 +02:00
Erin ab76516624 extracted t-dark substitution to separate function 2022-05-02 23:12:32 +02:00
Erin b76e890fb7 Modified Str -> Int coercion 2022-04-26 00:18:11 +02:00
Erin 85226fbfbb Implemented key extraction 2022-04-25 14:56:35 +02:00
able e895ef775f Merge pull request 'Added String character escaping by dozenal " delimited unicode values' (#9) from feature/string-unicode-escapes into master
Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/9
2022-04-25 12:48:07 +00:00
Erin 19744aa63a Added escapes test 2022-04-24 23:47:51 +02:00
Erin c6f4aaef24 Added some comments 2022-04-24 23:33:11 +02:00
Erin 90f8137b0d Implemented string unicode escapes 2022-04-24 23:29:15 +02:00
Erin 842c121901 sus 2022-04-24 22:01:31 +02:00
Erin c51a332f60 Merge pull request 'Changed the way how Melo-ing variables works' (#8) from improvements/melo into master
Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/8
2022-04-23 11:54:22 +00:00
Erin db3b56d798 Double Melo causes variable deletion 2022-04-22 21:14:53 +02:00
Erin 5ccc181f47 Rc is got by reference by default 2022-04-22 21:09:03 +02:00
Erin 95e790d8e8 Reimplemented Melo
- Variable made into enum of `ValueRef or Melo`
- Melo-ing variable causes dropping of the Rc, decreasing reference count
2022-04-22 21:06:34 +02:00
Erin 3916b20b59 clearer comment 2022-04-18 22:09:56 +02:00
Erin 5aada3c09e Made Parser private and made parse function
Parser had only consturctor and parse functions public.

Likely, there would be just a creation of parser followed by .parse(),
and now this action is performed in `parse` function.
2022-04-18 22:09:41 +02:00
Erin 98b72b14e2 Renamed init function in parser 2022-04-18 22:01:35 +02:00
Erin 60fb95cb13 Added crate-level docs for ablescript 2022-04-18 21:59:57 +02:00
Erin ee6105dd91 chore: fmt 2022-04-18 21:43:32 +02:00
Erin f2340dd493 fixed comments and naming 2022-04-18 21:43:19 +02:00
Erin ea13c79fc4 replaced if with unless 2022-04-18 21:42:26 +02:00
Erin 6dd32793bb Chars are no longer special case of identifiers 2022-04-18 21:04:19 +02:00
Erin 0eb2698242 Expr::Literal contains Literal type which contains only possible types instead of any Value 2022-04-18 20:56:11 +02:00
Erin eac86b5e0b Changed variable declaration / assignment and equals syntax:
- `dim <ident> [value];` is now used for declaration
- `<value> =: <assignable>` is used for assignments
- As token `=` doesn't cause any ambiguity now, it can be used for equals operation
2022-04-18 20:34:08 +02:00
Erin ba019d3b7e Release binaries are now stripped 2022-04-16 22:11:13 +02:00
Erin c1bc0e8a96 No underscore! 2022-04-16 22:06:37 +02:00
Erin 35caa085f8 fmt 2022-04-12 22:15:53 +02:00
Erin de3b6dc048 version bump 2022-04-12 14:17:20 +02:00
Erin cee4a80584 dependency update + fmt 2022-04-12 14:16:34 +02:00
Erin 60505735c9 removed != in favour of ain't 2022-04-08 17:44:35 +02:00
Erin 89326fd344 Removed ! for ain't 2022-04-08 17:39:14 +02:00
Alex Bethel 19fa04f979 cargo fmt 2022-04-02 12:42:40 -06:00
Erin 45007e7d2b removed \ from idents 2022-04-02 20:39:48 +02:00
Erin ec48508ba7 changed line order 2022-04-02 14:13:49 +02:00
Erin 0b4db65ca3 This isn't AbleScript, we can just negate expressions :D 2022-04-02 13:44:29 +02:00
Erin eae118a78e Small change 2022-04-02 01:42:13 +02:00
Erin 3b829d7e55 Merge branch 'refactoring/ast-spans' 2022-04-02 01:37:33 +02:00
Erin b46c178692 Removed Booleans
We have Abooleans (where Always = true, Never = false), so why to duplicate stuff?

This PR was lying around unmerged for quite some time, merged without review.
2022-04-01 23:37:10 +00:00
Erin 38fe23d2b5 Removed ident type, fixed tests 2022-04-02 01:34:25 +02:00
Erin db7fc4d592 Generalised Spanned items 2022-04-02 01:22:46 +02:00
Erin d890d7cfff Simplified base 55 2022-04-02 01:06:47 +02:00
Erin a3d2922f7e fixed tests 2022-03-30 21:03:17 +02:00
Erin c1c8f17c8d Improved type coercions 2022-03-30 20:56:59 +02:00
Erin 6f663839a7 Removed booleans 2022-03-30 20:55:05 +02:00
Erin 86e1391ea2 Used checked next in require 2022-03-20 02:20:55 +01:00
Erin 5a5403a8df Changed way of String lexing 2022-03-13 13:18:51 +01:00
Erin c567341775 Added support for idents starting with _ 2022-03-12 23:26:39 +01:00
Erin 070f3b2f0e fixed spelling of ain't 2022-03-02 11:36:08 +01:00
Erin 9d6f5740b1 \ is valid part of identifiers 2022-03-01 23:07:23 +01:00
Erin 4f56411375 Renamed dir 2022-03-01 22:17:01 +01:00
Erin 5f842dc8d9 There, we are now, we do not need those. 2022-03-01 22:15:27 +01:00
Erin 0ec54ecc51 Introduced newtype for Rc<RefCell<Value>> 2022-03-01 22:13:49 +01:00
Erin 3f4cc58b87 Satisfied most of THE ALLMIGHTY CLIPPY's requests. 2022-03-01 21:53:58 +01:00
Erin b975bc52d0 Bool, Abool and Nil values are no longer tokens 2022-03-01 18:57:03 +01:00
Erin 567665a314 fixed strings 2022-02-24 21:53:43 +01:00
Erin a23dac3ce0 Rust 2021
- Consts map generated using iterators
2022-02-23 21:47:07 +01:00
able 81f713a5e2 Merge pull request 'You know what could be cursed? Using Rust's block comment syntax as String delimiters!' (#5) from change/string-delimiters into master
Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/5
2022-02-23 14:11:14 +00:00
Erin cb6fcceb79 Fixed tests and examples 2022-02-22 22:49:56 +01:00
Erin f55f9e0512 Changed string delimiters
You know what could be cursed? Using Rust's block comment syntax as String delimiters!
2022-02-22 22:39:03 +01:00
Erin d1f18be279 Removed unused tokens from Lexer
Uh, they were lying there with no use for 9 months!
2022-02-22 22:31:05 +01:00
Erin 0615091b3c added simple history support 2022-02-14 00:17:55 +01:00
Erin 43ae772894 Exposed variables to public API 2022-02-14 00:12:03 +01:00
Erin edce9dcc91 Add from_value variable constructor 2022-02-14 00:11:37 +01:00
Erin ca4a9aba4c added new_with_vars 2022-02-14 00:08:48 +01:00
Erin 0134448472 Merge pull request 'change/isize-as-int' (#4) from change/isize-as-int into master
Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/4
2022-02-13 22:17:13 +00:00
Erin 43c51f2c96 Merge branch 'master' into change/isize-as-int 2022-02-13 22:17:00 +00:00
Erin 0d00aba6ce Merge pull request 'Implemented built-in functios' (#3) from feature/builtins into master
Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/3
2022-02-13 22:16:43 +00:00
Erin e79c98f2f6 Fixed all errors 2022-02-13 00:58:50 +01:00
Erin 5d29c8cf87 fixed base 55 2022-02-13 00:55:51 +01:00
Erin 8c867c7899 Value::Int is isize 2022-02-13 00:55:19 +01:00
Erin 5b320259f6 Changed Built-in functio -> integer coercion 2022-02-13 00:37:17 +01:00
Erin a9a9cc71e1 Implemented builtin functio division 2022-02-13 00:30:40 +01:00
Erin 3338ec94c2 implemented subtraction for built-in functios 2022-02-13 00:25:56 +01:00
Erin d634692f12 Implemented not for built-in functios 2022-02-13 00:07:23 +01:00
Erin a1f6d1ae5f Implemented Display for built-ins 2022-02-12 22:52:14 +01:00
Erin 06ec25e717 Implemented coercions for built-in functios 2022-02-12 22:10:30 +01:00
Erin 405f88e394 Implemented built-in functios
- Missing coercions and operations
2022-02-12 21:14:55 +01:00
Erin 09617b2a89 Length by empty square braces
Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/2
2022-01-28 20:03:31 +00:00
Erin 68e5e7cf41 Functio -> Int more cursed 2022-01-22 21:02:10 +01:00
Erin 7cf9433462 Cart -> Int more cursed 2022-01-22 20:52:09 +01:00
Erin 2191e126f0 Implemented [] as length operator 2022-01-22 20:48:21 +01:00
Erin 8e55872f10 Implemented Len in parser 2022-01-22 20:37:44 +01:00
Alex Bethel 1b84093eab Merge branch 'master' of ssh://git.ablecorp.us:20/AbleScript/able-script 2021-12-29 17:08:05 -06:00
Alex Bethel 912437f10c Add martens owo 2021-12-29 17:06:46 -06:00
Erin 88ef782cea Toggled Fat LTO on 2021-12-14 23:57:03 +01:00
Alex Bethel 7a34105c3e Add version number to ablescript_cli deps 2021-12-14 16:53:18 -06:00
Alex Bethel 0451e71104 Release 0.2
Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/1
2021-12-14 22:31:52 +00:00
Erin 4b5ad3c462 Merge branch 'master' into feature/coercions 2021-12-14 23:30:07 +01:00
Alex Bethel b6413f6219 Merge branch 'feature/coercions' of ssh://git.ablecorp.us:20/AbleScript/able-script into feature/coercions 2021-12-14 16:16:44 -06:00
Alex Bethel 20b1408179 Implement function chain division 2021-12-14 16:16:28 -06:00
Erin 573df02ba6 Fixed T-Dark blocks in blocks 2021-12-14 23:16:19 +01:00
Erin c1471bcdb7 Removed clone 2021-12-14 23:05:21 +01:00
Erin 8302324d0f Merge branch 'feature/coercions' of https://git.ablecorp.us/AbleScript/able-script into feature/coercions 2021-12-14 23:02:57 +01:00
Erin 6c836125b3 Renamed stuff 2021-12-14 23:02:55 +01:00
Alex Bethel cf289a82d7 Fix carts.able formatting 2021-12-14 15:57:04 -06:00
Alex Bethel 57939a87b0 Arity-based functio deinterlacing 2021-12-14 15:56:40 -06:00
Erin db95d2f718 🚗 Implemented ordered functio chaining 2021-12-14 21:45:44 +01:00
Erin 872a16578f Removed a feature which disallowed comments in functios 2021-12-14 21:10:58 +01:00
Erin a9d9c7aa27 Implemented subtraction for functio chains... maybe? 2021-12-09 17:51:03 +01:00
Erin 6430387119 Implemented coercions when subtracting able functios 2021-12-09 17:18:37 +01:00
Erin c92cb7e818 Who cares about the content, we have 256 commits!
And I readded a thing
2021-12-09 00:38:36 +01:00
Erin 6d48dca547 sync 2021-12-09 00:35:43 +01:00
Erin 61ee6b3597 Git things beyond your comprehension mortals 2021-12-09 00:34:41 +01:00
Erin 3bfe600103 Implemented halfway of functio sub 2021-12-09 00:33:45 +01:00
Alex Bethel 0b703173f8 Function division 2021-12-08 16:33:06 -07:00
Erin 3c98caf2d6 Made Clippy happy (so he will not kill us in sleep) 2021-12-08 22:56:12 +01:00
Alex Bethel 3cdca1666a Use b-string instead of array of b-chars 2021-12-08 14:39:04 -07:00
Erin 20c6b4c7cf Implemented Abool -> Functio 2021-12-07 22:58:41 +01:00
Alex Bethel 3044ef91e3 Better BF function conversions 2021-12-07 14:27:45 -07:00
Alex Bethel ffcd0000c2 I made a cursed thing 😂 2021-12-07 14:24:58 -07:00
Erin f5e4be4d67 Implemented Functio to Aboolean 2021-12-07 21:57:37 +01:00
Erin 904d647031 huh??? 2021-12-07 21:20:24 +01:00
Erin 6889c3f3b2 Fmt! 2021-12-07 21:20:16 +01:00
Erin 65bb29d401 Function negation 2021-12-07 21:18:45 +01:00
Erin 3a59501217 Revert "Merge branch 'fix/unused-code'"
This reverts commit 6ee12a7c91, reversing
changes made to 706d39e860.
2021-12-07 20:54:23 +01:00
Alex Bethel 6ee12a7c91 Merge branch 'fix/unused-code' 2021-11-27 17:20:54 -06:00
Alex Bethel 2f31e7c3d5 Fix unused code warnings 2021-11-27 11:15:29 -06:00
Alex Bethel 6cd2b8bd44 Avoid trailing comma in cart printout 2021-11-27 11:11:03 -06:00
Alex Bethel 711b8e1c03 Slightly better ExecEnv::assign
A little more idiomatic, avoid some borrow checker clumsiness, add
comments.
2021-11-27 11:02:41 -06:00
Alex Bethel 9727a9b577 Assign read syntax 2021-11-05 17:09:53 -06:00
Alex Bethel 6849f7f296 Coerce indexing assignments into non-carts 2021-11-05 16:18:07 -06:00
Erin 5d6ac150ab Renamed semi_terminated to semicolon_terminated 2021-10-23 23:20:45 +02:00
Alex Bethel 5634a8ad2b Get cart assignments working 2021-10-23 15:08:10 -06:00
Alex Bethel 5db6eebdc0 Almost get cart assignments working 2021-10-23 14:17:17 -06:00
Erin ae1416e329 Read in AST takes Assignable instead of Ident 2021-10-23 21:53:21 +02:00
Alex Bethel 706d39e860 Merge branch 'feature/line_continuations' 2021-10-23 12:46:05 -06:00
Erin bcd446c5d1 Moved Assignable creation functions 2021-10-21 20:51:24 +02:00
Erin 0f198a2554 Merge pull request #43 from T-Dark0/T-Dark_fix_brainfuck_overflows
Fixed integer overflows. Why did I even write `checked_add` in the first place?
2021-10-19 23:37:47 +02:00
Erin 3e8b80af2a Removed in_the_past_this_used_to_crash_but_not_anymore test 2021-10-19 23:35:46 +02:00
T-Dark 6779d7c898 Fixed integer overflows. Why did I even write in the first place? 2021-10-19 17:29:53 +01:00
Erin 610ba7fc40 Change Cart AssignableKind to Index. 2021-10-13 13:20:23 +02:00
Alex Bethel 31fe3f53be Sort of fix cart assignments 2021-10-12 14:33:23 -06:00
Erin cdf4c5a308 improvement 2021-10-12 22:22:33 +02:00
Erin 4c6dd311a7 Added Assignable support in parser / AST 2021-10-12 22:14:20 +02:00
Alex Bethel 6eb97c6032 Add statement continuation in REPL in CLI
Typing ```
var foo = [
``` into the REPL now causes it to prompt you to complete the
statement rather than just printing an "unexpected EOF" error.
2021-10-09 22:31:14 -06:00
Erin e47e739e02 Generalised some lexer functions 2021-10-04 23:03:23 +02:00
Erin 71c5738a6e Renamed Iden to Ident 2021-10-04 23:00:18 +02:00
Alex Bethel e8349bf0d9 Improve consistency of code & comments
Changed all `&str.to_string()` into `&str.to_owned()`, and made the
grammar of the `Value::into_*` comments uniform.
2021-09-04 10:54:53 -06:00
Erin a288db985d Variable name shortened 2021-09-02 18:36:25 +02:00
Erin 4d1e85a295 Fixed division
x/0 is now x/42 instead of 42 which makes it more confusing
2021-09-02 18:35:33 +02:00
Erin 113554428f Clippy 2021-09-01 17:46:17 +02:00
Erin 82c09e5b1b All operations except functio ones are implemented
- Aboolean logic
- Cart division/multiplication
2021-08-31 00:41:37 +02:00
Erin 14e9eb5c6b Implemented negation for most types 2021-08-30 23:19:25 +02:00
Alex Bethel 6f8a49b711 Add parsing negative numbers 2021-08-30 15:03:29 -06:00
Erin 82239be160 Not is now lexing and parsing correctly 2021-08-30 22:55:31 +02:00
Erin 92f2c62d59 Removed And and Or ops because they aren't cursed enough 2021-08-30 22:18:09 +02:00
Erin 49ba66de07 Most of operations are implemented 2021-08-30 22:14:13 +02:00
Erin 58f0645b54 fmt + docs 2021-08-29 00:34:08 +02:00
Erin 31296ec0b8 Used body length for AbleFunctio to i32 instead of AST length 2021-08-29 00:28:45 +02:00
Erin 7a275556c7 Revert "Eval errors are now correctly spanned"
This reverts commit 2f5b954b7c.
2021-08-29 00:26:40 +02:00
Erin 975655a96e Merge branch 'feature/coercions-and-ops' of https://github.com/ablecorp/able-script into feature/coercions-and-ops 2021-08-29 00:24:24 +02:00
Erin 2f5b954b7c Eval errors are now correctly spanned 2021-08-29 00:23:59 +02:00
Erin 3ce85a11ee Eval errors are now correctly spanned 2021-08-29 00:16:15 +02:00
Erin a8c603164c AbleFunctio's body length is now by it's string representation 2021-08-29 00:10:44 +02:00
Erin e30eef32b5 Eval's integer representation is now its length
- consistency with other types
2021-08-29 00:07:26 +02:00
Erin 1c8722ba99 Added placeholders for And + Or
and used placeholders for -, * and / in interpret
2021-08-28 23:59:04 +02:00
Erin c66616ee2c Fixed add overflows 2021-08-28 23:55:36 +02:00
Erin e8f08a90d5 Display carts sorted 2021-08-28 23:52:58 +02:00
Erin 4c74e3bef3 Implement custom PartialEq for Value 2021-08-28 23:43:59 +02:00
Erin 8b57cbe5a5 Most coercions implemented 2021-08-28 23:27:35 +02:00
Erin 2c36b2a5f7 Require space after owo for making comments. 2021-08-23 18:58:28 +02:00
Alex Bethel 4c3d44dfd8 Fix panics on invalid indexes 2021-08-16 15:06:40 -06:00
Able 92134d68d5 Merge pull request #39 from AbleCorp/0.2.0 2021-08-15 19:48:37 -05:00
Erin 3297605bf2 Changed license file to MIT from Github template 2021-08-15 00:59:44 +02:00
Able b3b2d1a8ef Merge pull request #38 from AbleCorp/enhancement/relicensing 2021-08-11 13:36:03 -05:00
Erin c70e77f717 Relicensed to MIT 2021-08-11 20:32:12 +02:00
Erin aa854a067b Restructured project (#37)
* Separation to two crates
- `ablescript`: library, language
- `ablescript_cli` - cli: repl and executor for files

* Added lints (back) to library
- unsafe_code and unwrap_used are forbidden
2021-08-10 21:32:12 +02:00
Alex Bethel a2d240f471 Format .able test files
Unified mixed 2-space and 4-space indentation into uniform 3-space
indentation.
2021-08-09 14:41:02 -06:00
Alex Bethel fd9054d3d3 Functio & cart comparisons and hashing 2021-08-07 16:33:28 -06:00
Erin 3686b3b608 Added carts example 2021-08-01 18:47:13 +02:00
Erin 83a4f67121 Implemented callable expressions in parser 2021-08-01 18:36:09 +02:00
Erin 3df3cbbf6d Implemented callable expressions in AST and Interpret 2021-08-01 18:30:07 +02:00
Erin 0c000337d2 fmt 2021-08-01 18:28:59 +02:00
Erin 25a3c14145 Changed to correct naming conventions 2021-07-29 23:26:43 +02:00
Erin b451503a27 Obeyed clippy 2021-07-29 23:22:38 +02:00
Erin e3cfaaf32c removed unused enum variant 2021-07-29 23:04:04 +02:00
Erin 241f2261d1 Added history (session based) 2021-07-29 22:48:36 +02:00
Erin eb91aa96fa Added carts tests 2021-07-29 22:39:28 +02:00
Alex Bethel 3cd1115f5d Merge branch 'feature/carts' of github.com:AbleCorp/able-script into feature/carts 2021-07-29 13:30:11 -05:00
Alex Bethel 0bca9ff79c Get carts sort of working
still a bunch of `todo`s and needs a lot of work, but I'm hungry and
going on lunch break :)
2021-07-29 13:29:23 -05:00
Erin a6f86b7e26 Bumped version to 0.2.0 2021-07-29 19:18:06 +02:00
Erin 603a244b78 Nest the loop inside a match arm 2021-07-29 19:17:10 +02:00
Erin 0d497bccdd Added cart parsing 2021-07-29 18:58:11 +02:00
Erin b53cc1e768 Not fails on cart contruction parsing, does nothing. 2021-07-29 18:17:08 +02:00
Erin bf2e73a5d1 Extracted construction of carts to separate function 2021-07-27 12:14:11 +02:00
Erin e7bd81e12a Implemented cart indexing parsing 2021-07-27 12:09:36 +02:00
Erin ab1515cdb0 Added arrow token 2021-07-27 11:52:43 +02:00
Erin e847a76c6d Added cart support into AST 2021-07-27 11:51:05 +02:00
Alex Bethel 282a9a6a07 Fix broken type_errors unit test 2021-07-22 22:27:17 -05:00
Alex Bethel 0b0a8942de Remove latent debug println for BFF input 2021-07-22 22:11:55 -05:00
Alex Bethel dcc0c0b00f Merge branch 'master' of github.com:AbleCorp/able-script 2021-07-22 22:09:40 -05:00
Alex Bethel 501d182fb4 Use coercion in BFF argument passing 2021-07-22 22:08:04 -05:00
Alex Bethel 50f9bfa4c6 Eliminate type errors
Hooray, we've just made a whole class of errors impossible! :D
2021-07-16 19:03:27 -05:00
Alex Bethel 821eeae322 Silently fail when calling a non-function
The most cursed possible way to deal with this error.
2021-07-16 19:02:51 -05:00
Alex Bethel d3242484fc Make integer coercion infallible 2021-07-16 18:56:45 -05:00
Erin 0c795741e8 Reduced parser boilerplate 2021-07-15 21:39:01 +02:00
Alex Bethel 141220fb38 Fix function call type error message 2021-07-15 14:24:07 -05:00
Alex Bethel 3d9f847862 Make consts accessible from AbleScript code 2021-07-13 14:22:06 -05:00
Alex Bethel a225105f09 Prettier error handling 2021-07-13 13:54:27 -05:00
Alex Bethel 8818536717 Revert "Update README.md"
This reverts commit e02edbbd16.

It is, in fact, still better than Javascript.
2021-07-01 17:17:48 -05:00
Alex Bethel bc7427014d Add more info to Cargo.toml 2021-06-30 16:45:44 -05:00
Alex Bethel 1e8314b1a1 Merge branch 'feature/read' 2021-06-19 17:34:19 -05:00
Alex Bethel c1cc54be75 Merge 'feature/forty-two' into master 2021-06-19 17:32:12 -05:00
Alex Bethel eee173060c Implement read semantics in interpreter
Also added a test file `iotest.able` to just read data and print
it (the `cat` of AbleScript I guess).
2021-06-18 16:05:50 -05:00
Erin 3edbca51d5 Implemented read in Parser 2021-06-18 20:28:53 +02:00
Alex Bethel 430f0c2b90 Forbid use of unwrap w/ clippy 2021-06-16 10:36:52 -05:00
Alex Bethel 4011997e60 Clippy conformance 2021-06-16 10:35:06 -05:00
Alex Bethel cdb4a22443 Add --debug option for printing AST 2021-06-16 10:29:27 -05:00
Alex Bethel 098137082e Better error messages, remove debug AST printing 2021-06-16 10:25:55 -05:00
Alex Bethel 2e5799d9e6 Remove quotes around rickroll string
They're not necessary, because the `rickroll` file is already included
as a string.
2021-06-15 11:47:39 -05:00
Alex Bethel 9965d97713 Fix ignored unit tests in interpret.rs 2021-06-15 11:46:01 -05:00
Alex Bethel d1064802ec Fix unit tests 2021-06-15 11:29:52 -05:00
Alex Bethel 8aa6fc502a rustfmt: whoops, let's put a semicolon there
maybe I should have actually looked at what rustfmt produced before I
just blindly accepted it and pushed it onto master.
2021-06-15 10:26:44 -05:00
Alex Bethel 7bf3cc6f5d rustfmt: break long line 2021-06-15 10:24:50 -05:00
Alex Bethel ae0955209c Use pass-by-reference for variables, pass-by-value for expressions 2021-06-15 09:51:00 -05:00
Alex Bethel 66d4f4f82d Implement most of pass-by-reference
We need some parser changes to make it work fully: function arguments
are now identifiers rather than expressions.
2021-06-15 09:51:00 -05:00
Alex Bethel 4b0aa1602b Lack of else statements is intentional 2021-06-15 09:51:00 -05:00
Alex Bethel d80c138735 Improve pass-by-reference test 2021-06-15 09:51:00 -05:00
Alex Bethel d59b75fdb3 Add pass-by-reference test (doesn't work yet)
Also rename the "hello_world.able" test to "hello-world.able" for
consistency.
2021-06-15 09:51:00 -05:00
Alex Bethel 75938f27c0 Implement functio declaration & calling 2021-06-15 09:51:00 -05:00
Able c2de1f6411 Merge pull request #33 from HTG-YT/master 2021-06-15 04:39:07 -05:00
HTG-YT aa72945010 comply with the move of rickroll 2021-06-14 20:53:27 +08:00
HTG-YT 26df745dee move rickroll to src/ folder 2021-06-14 20:52:38 +08:00
HTG-YT 953128887b Update rickroll 2021-06-14 09:42:38 +08:00
HTG-YT b526d5c575 move rickroll content into a file 2021-06-14 09:42:23 +08:00
HTG-YT 712e6cef67 Create rickroll 2021-06-14 09:41:20 +08:00
HTG-YT 72e48d1b86 rustfmt: add trailing comma 2021-06-14 09:40:42 +08:00
HTG-YT 327466cddc Merge branch 'AbleCorp:master' into master 2021-06-14 09:40:00 +08:00
Erin 93289e02f1 Added build staus badge 2021-06-13 18:43:09 +02:00
Erin 232595c1be Added CI config
Rust Template
2021-06-13 18:14:41 +02:00
Erin f62bab110f Update README.md 2021-06-13 18:12:21 +02:00
HTG-YT 227fa8e35b handle Token::Rickroll in parser 2021-06-13 13:24:09 +08:00
HTG-YT fa0d3968f5 Update interpret.rs 2021-06-13 13:22:30 +08:00
HTG-YT 3646c5e32e handle StmtKind::Rickroll 2021-06-13 13:22:01 +08:00
HTG-YT 286a12b28e add StmtKind::Rickroll 2021-06-13 13:11:51 +08:00
HTG-YT 855383faac add rickroll lexeme in lexer 2021-06-13 13:11:02 +08:00
Alex Bethel da9ff64486 Make arithmetic errors evaluate to 42 2021-06-12 10:48:44 -05:00
Alex Bethel 92e0a22e24 Integrate const.rs as consts 2021-06-12 10:45:25 -05:00
Alex Bethel 976518a251 Reimplement BF function declaration semantics 2021-06-12 09:26:21 -05:00
Erin ff04c34cf6 Better comment 2021-06-11 19:44:53 +02:00
Erin 183c584e96 Removed todo when unknown token 2021-06-11 18:34:34 +02:00
Erin 3d7c89634e Fixed invalid spanning of UnexpectedToken in parse_expr 2021-06-11 18:31:25 +02:00
Erin 3bcef28843 BF code is now Vec<u8> 2021-06-11 18:10:11 +02:00
Erin 711ad27e00 Basic BfFunctio support 2021-06-11 17:52:47 +02:00
Alex Bethel 41316f5696 Re-enable assignment semantics 2021-06-11 10:05:48 -05:00
Erin 6f16d2594b Added variable assignment to parser
- Fixed parse error, that `rlyeh` returned HopBack
2021-06-11 16:59:40 +02:00
Erin 851f04c653 Merge pull request #29 from HTG-YT/interpreter-fixes
add `interessant`, `nevergonnagiveyouup` and `funny` constants
2021-06-11 09:33:48 +02:00
HTG-YT dd28ff985f add nevergonnagiveyouup constant 2021-06-11 15:26:30 +08:00
HTG-YT 77749bc32b add interessant and funny constants 2021-06-11 15:19:27 +08:00
Alex Bethel 7d7c1e4a1b Fix interpreter variable persistence bug 2021-06-08 19:21:33 -05:00
Alex Bethel dc5ef63fef Better error handling in REPL
We have much better spanned expression support, so now we get to show
it off!
2021-06-07 20:08:38 -05:00
Alex Bethel a0c1f93392 Remove 0..0 placeholder from unexpected_eof() 2021-06-07 20:03:26 -05:00
Alex Bethel ce20611c07 Fix all 0..0 span placeholders in interpret.rs 2021-06-07 19:57:44 -05:00
Alex Bethel d8f462e0b5 Fix unit tests 2021-06-07 17:35:49 -05:00
Alex Bethel addda8fc87 Remove giant comment block left in by mistake 2021-06-07 16:28:27 -05:00
Alex Bethel 426aee6a2b Repair interpreter after parser changes 2021-06-07 16:20:20 -05:00
Erin 903b1a3809 Added T-Dark test 2021-06-07 23:06:13 +02:00
Erin 8bd61a4a65 Added tests, Bugfix
- Originally, it spanned from operator (bug)
2021-06-07 22:58:28 +02:00
Erin 1f78cb0ee1 Added missing mut 2021-06-07 22:24:43 +02:00
Erin 902d8b914c Unified span terminology
- Add more derivations, because it's required by another parts of project
2021-06-07 22:21:21 +02:00
Erin f893e24aed Changed position terminology 2021-06-07 21:28:24 +02:00
Erin 2084ebeed1 Removed unwraps, added Rlyeh 2021-06-07 11:07:50 +02:00
Erin b7464f6ad3 Implement function calls
- And printing
- Revised terminology in Lexer
- Control flow
2021-06-07 11:00:06 +02:00
Erin b6bce72b94 Added logical operators 2021-06-07 09:17:30 +02:00
Erin 97f0271534 Binary operator boilerplate reduction 2021-06-07 09:17:18 +02:00
Erin a5a048728c Added basic parsing
- Expressions
- If, Functio
2021-06-07 00:09:45 +02:00
Erin b04bd77672 Initial chthulic error impl in Lexer
- See discussions #17
2021-06-06 23:15:11 +02:00
Erin 93065e7dc9 AST revamp
- Unified terminology of span in error.rs
2021-06-06 23:13:48 +02:00
Erin 6fd95f3cc2 Updated AST 2021-06-06 21:09:18 +02:00
Erin afe6588c05 Removed custom Lexer, reorganised Token definition
- As peeking will not be involved in parsing, it was removed
2021-06-06 20:28:13 +02:00
Erin 2c15f3dc17 Deleted old parser 2021-06-06 20:05:18 +02:00
Alex Bethel 4bda25c9f3 Correct spelling of "occurred" 2021-06-05 08:50:20 -05:00
Alex Bethel 6c2d5cc84a Prettier error handling 2021-06-04 18:56:26 -05:00
Able 5d230431e0 Merge pull request #28 from AlexBethel/bf-functio 2021-06-02 20:02:53 -05:00
Alex Bethel 528de718dc Clean up eval_expr some more
I keep going back and forth on how I want this block to look :P
Anyway, this is *probably* its final-ish form, until the parser gets
re-written.
2021-06-02 18:41:20 -05:00
Alex Bethel ce02aebd91 Use impl Value rather than trait BfWriter
It probably makes more sense to be writing values with
`Value::bf_write` than to go for the complexity of using a trait to
allow `Writer::write_value` (which also looks ambiguous because it's
not immediately clear that a generic name like "write_value" relates
to BF or AbleScript).
2021-06-02 18:20:30 -05:00
Alex Bethel 326d0511e7 Add Brainfuck functio interpretation
BF functios can now be declared and called from AbleScript code.
Function parameters supplied from AbleScript are serialized manually
into byte sequences using the `BfWriter` trait, and are available from
BF code using the input operator, `,`.

At the moment, BF functios simply write output to stdout, and are
therefore incapable of communicating results to the rest of the
program; this might change in the future if we can get something close
to pass-by-reference working.
2021-06-02 15:29:31 -05:00
Alex Bethel 07195d4cf6 Fix panic on arithmetic error
Divide by zero and add, subtract, or multiply with overflow are all
caught now and reported as an ArithmeticError, rather than causing the
interpreter to panic.
2021-05-30 13:32:29 -05:00
Alex Bethel acfd81ead2 More thorough unit tests 2021-05-30 13:32:29 -05:00
Alex Bethel 344a11084e Add some unit tests to interpret.rs
Currently `overflow_should_not_panic` and `variable_persistence` both
fail due to bugs in the interpreter.
2021-05-30 13:32:29 -05:00
Able 90773eea3d Merge pull request #27 from AlexBethel/master 2021-05-29 17:14:25 -05:00
Alex Bethel 099b9e23d6 Make base-55 encoding & decoding match
Changed "U"'s encoding to -210, so now when the README claims "U =
-210", it's actually accurate :)
2021-05-29 14:24:46 -05:00
Alex Bethel 585bf2e19f Use single-letter identifiers as base-55 digits 2021-05-29 10:45:39 -05:00
Able 7b21eaf48a Merge pull request #26 from Seppel3210/patch-1 2021-05-28 05:10:43 -05:00
Seppel3210 d52c775ed1 Change "*" versions to explicit versions
Change "*" versions to explicit versions in Cargo.toml
2021-05-28 09:30:16 +02:00
Able e02edbbd16 Update README.md 2021-05-27 17:51:08 -05:00
Able 18973333b0 Merge pull request #24 from AlexBethel/master 2021-05-25 23:06:08 -05:00
Alex Bethel c906366e3f Improve name & documentation accuracy
Renamed ControlFlow -> HaltStatus because that's what the enum really
is -- a status on why something halted. Also reviewed `interpret.rs`'s
documentation and fixed a few things that were out of date.
2021-05-25 21:55:02 -05:00
Alexander Bethel ec81ead3ea Implement break and hopback statements 2021-05-25 21:22:38 -05:00
Alexander Bethel 669f379700 Better abstractions, implement scoping rules 2021-05-25 13:26:01 -05:00
Alexander Bethel 109c77eeb2 Implement more statements
Added variable declaration, `if` statements, `loop` statements,
variable assignment, and variable banning to go along with
printing (which was already implemented). We still need function
declarations, brainfuck declarations, function calls, and the control
flow operators "break" and "hopback".
2021-05-24 10:50:26 -05:00
Able c27837a807 Slightly more curse 2021-05-24 01:18:36 -05:00
Able 84016d3dcd Merge pull request #22 from AlexBethel/master 2021-05-21 12:29:03 -05:00
Alexander Bethel b625a71711 Allow abool -> bool coercion
The expression `sometimes & true` now evaluates to `true` 50% of the
time and false 50% of the time, rather than throwing a type error
because `sometimes` is not a bool.
2021-05-21 12:25:37 -05:00
Alexander Bethel ea211fc3b0 Make ablescript -f run interpreter
`ablescript -f foo.able` will now both parse and interpret `foo.able`,
rather than just parsing it.
2021-05-20 18:24:18 -05:00
Alexander Bethel eccc00ff81 Implement basic interpreter
Added code for interpreting parsed AbleScript expressions and
statements, and hooked it up to the REPL.
2021-05-20 18:18:01 -05:00
Able 0ad680cadd Merge pull request #18 from T-Dark0/tdark-brainfuck 2021-05-15 20:34:29 -05:00
tdark 1b195dc955 Added support for specifying a tape size limit 2021-05-15 18:39:49 +02:00
tdark 7b5ae34bfd Implemented brainfuck interpreter 2021-05-15 18:10:50 +02:00
Able c0b7bd2f34 Update README.md 2021-05-14 10:52:16 -05:00
Able 396650ae95 Update README.md 2021-05-11 13:39:41 -05:00
able 87139c4448 consider using able brand products 2021-05-05 07:33:40 -05:00
able 9b7367353d Brain fuck work 2021-05-04 22:23:17 -05:00
able 57d86b9f92 README changes 2021-05-04 21:29:51 -05:00
able bdf27c83da Minor Changes 2021-05-03 19:33:21 -05:00
Able 143ae613d8 Merge pull request #15 from erindesu/master
Fixed some incompatible old code
2021-05-03 18:41:23 -05:00
Erin 72cd540728 Tidy up 2021-05-03 23:02:19 +02:00
Erin 798c1807db Obeyed our paperclip overlord + fmt 2021-05-03 21:36:32 +02:00
Erin 30eedbc854 Fixed some incompatible old code
- Added assignment support
- Reduced boilerplate
- Removed `else`
2021-05-03 21:35:43 +02:00
Erin 29bf01935c Variable assignment implemented 2021-05-03 09:54:27 +02:00
Able e8a2b87c6e Merge pull request #14 from erindesu/master 2021-05-03 02:17:38 -05:00
Erin 8c6f415ac9 Fixed #13 2021-05-03 08:47:52 +02:00
Able e080fc65e3 Merge pull request #12 from erindesu/master 2021-05-02 12:49:30 -05:00
Erin 051f6e781f Added tests and loop flow 2021-05-02 18:12:51 +02:00
Erin ecce080378 Added T-Dark block
- obeyed clippy
2021-05-02 17:38:12 +02:00
Erin 5a8dd5051f Parser production ready
- TODO: T-Dark block
- cargo fmt
- Obeyed our clippy overlord
2021-05-02 16:48:33 +02:00
Erin c90d242b0f Parser implements examples
- Function call is now stmt (muhehe)
2021-05-02 15:43:25 +02:00
Erin 18a1343e11 Added basic math operations
- cargo fmt
2021-05-02 00:39:08 +02:00
Erin 3794fd3c8f Divided token types
- Item = Expr | Stmt
- Added REPL (AST dump)
- Removed operator parsing because it was horrible and I need to redo it
2021-05-01 13:44:58 +02:00
Erin eef7ec16fa Tidy up 2021-04-29 19:19:35 +02:00
Erin ca60f818eb Implemented function calls 2021-04-29 18:50:51 +02:00
Erin 17a32a8df7 Bugfix: Peeking
- Fixed `PeekableLexer` next to not-be passtrough to iterator
- Made error handling depend on state of Option
2021-04-29 09:47:29 +02:00
Erin e45afeac5e Added Peekable Lexer
- Added wrapper for Lexer with `peek()` method
- Renamed `token` module to `lexer` as it more describe it's function
- Started work on operator flow
2021-04-28 22:52:19 +02:00
Able b4d3f0c9b6 Merge pull request #10 from erindesu/master
Reimplemented parser
2021-04-27 09:26:29 -05:00
Erin f3779deeb5 Added parsing of conditionals 2021-04-27 13:48:56 +02:00
Erin 3f9e6b72cc Made parser to throw error when unexpected EOF 2021-04-27 11:57:11 +02:00
Erin 39a8bf6a54 Redone original parser
= implemented original features
2021-04-27 11:49:07 +02:00
Erin d2160a3a4a Added testing for base55, new identifier lexing 2021-04-27 11:09:19 +02:00
Erin 3b8ce34c2b Continue work on parser, improved lexer
- Added literal parsing (improved lexing)
- Revised error handling
2021-04-27 10:51:39 +02:00
Erin cc4ec803c4 Starting work on parser improvements
- Parser should parse single expressions
2021-04-26 10:44:42 +02:00
Able 19762e940a Merge pull request #9 from erindesu/master 2021-04-20 10:28:52 -05:00
Erin e020fc4267 Implemented abool
- Tidied up code
2021-04-18 23:40:41 +02:00
Erin 7e0c881130 Added function/variable parsing
- Added block support
- TODO: Tidy it up
2021-04-18 22:33:55 +02:00
Erin 48dd930872 Initial parser work 2021-04-18 16:39:43 +02:00
able bc1eb42af4 variable things added 2021-04-13 18:01:19 -05:00
Able 83c8595a8f Merge pull request #8 from erindesu/master 2021-04-13 11:04:17 -05:00
Erin 1db4bd74cf clippy + fmt 2021-04-13 17:43:54 +02:00
Erin 81ff84ad73 Improved Scanner 2021-04-13 17:40:20 +02:00
able d13d72ad89 Adding a melo example 2021-04-12 22:19:44 -05:00
Able a26058587a Merge pull request #6 from erindesu/master
Added tokenization
2021-04-12 20:11:41 -05:00
Erin 968a65b1fb Added tokenization
- Added tokenization
- Modified `Token` definition for make it compatible with Logos
- And also obeyed our paperclip overlord and changed all names to be complaint with Rust conventions
2021-04-12 20:20:45 +02:00
able 1abd802b58 Merge branch 'master' of https://github.com/AbleTheAbove/able-lang
merge
2021-04-11 22:40:32 -05:00
Able 4235106434 Merge pull request #2 from erindesu/patch-1 2021-04-11 18:03:34 -05:00
Erin 0d35f7f0a5 Add ANSWER const 2021-04-12 01:02:08 +02:00
26 changed files with 2044 additions and 1844 deletions

521
Cargo.lock generated
View file

@ -4,7 +4,7 @@ version = 3
[[package]]
name = "ablescript"
version = "0.3.0"
version = "0.5.4"
dependencies = [
"logos",
"rand",
@ -12,7 +12,7 @@ dependencies = [
[[package]]
name = "ablescript_cli"
version = "0.3.0"
version = "0.5.4"
dependencies = [
"ablescript",
"clap",
@ -20,27 +20,58 @@ dependencies = [
]
[[package]]
name = "atty"
version = "0.2.14"
name = "anstream"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [
"hermit-abi",
"libc",
"winapi",
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "autocfg"
version = "1.1.0"
name = "anstyle"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
dependencies = [
"anstyle",
"windows-sys 0.48.0",
]
[[package]]
name = "beef"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "bitflags"
@ -49,10 +80,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.73"
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "cfg-if"
@ -62,30 +93,61 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.1.8"
version = "4.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272"
dependencies = [
"atty",
"bitflags",
"indexmap",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
"clap_builder",
"clap_derive",
]
[[package]]
name = "clipboard-win"
version = "4.4.1"
name = "clap_builder"
version = "4.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3e1238132dc01f081e1cbb9dace14e5ef4c3a51ee244bd982275fb514605db"
checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "clipboard-win"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
dependencies = [
"error-code",
"str-buf",
"winapi",
]
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "dirs-next"
version = "2.0.0"
@ -115,23 +177,12 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "errno"
version = "0.2.8"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
"windows-sys 0.52.0",
]
[[package]]
@ -146,13 +197,13 @@ dependencies = [
[[package]]
name = "fd-lock"
version = "3.0.5"
version = "3.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca"
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
dependencies = [
"cfg-if",
"rustix",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
@ -163,9 +214,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "getrandom"
version = "0.2.6"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
@ -173,71 +224,54 @@ dependencies = [
]
[[package]]
name = "hashbrown"
version = "0.11.2"
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "io-lifetimes"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "libc"
version = "0.2.122"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
version = "0.0.42"
name = "libredox"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7"
[[package]]
name = "log"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
dependencies = [
"cfg-if",
"bitflags 2.4.1",
"libc",
"redox_syscall",
]
[[package]]
name = "logos"
version = "0.12.0"
name = "linux-raw-sys"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427e2abca5be13136da9afdbf874e6b34ad9001dd70f2b103b083a85daa7b345"
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "logos"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1"
dependencies = [
"logos-derive",
]
[[package]]
name = "logos-derive"
version = "0.12.0"
name = "logos-codegen"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56a7d287fd2ac3f75b11f19a1c8a874a7d55744bd91f7a1b3e7cf87d4343c36d"
checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68"
dependencies = [
"beef",
"fnv",
@ -245,23 +279,22 @@ dependencies = [
"quote",
"regex-syntax",
"syn",
"utf8-ranges",
]
[[package]]
name = "logos-derive"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e"
dependencies = [
"logos-codegen",
]
[[package]]
name = "memchr"
version = "2.4.1"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "nibble_vec"
@ -274,46 +307,35 @@ dependencies = [
[[package]]
name = "nix"
version = "0.23.1"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags",
"cc",
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset",
]
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.37"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@ -351,60 +373,59 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.6.3"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.2.13"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags",
"bitflags 1.3.2",
]
[[package]]
name = "redox_users"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
dependencies = [
"getrandom",
"redox_syscall",
"libredox",
"thiserror",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "rustix"
version = "0.34.2"
version = "0.38.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96619609a54d638872db136f56941d34e2a00bb0acf3fa783a90d6b96a093ba2"
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
dependencies = [
"bitflags",
"bitflags 2.4.1",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"winapi",
"windows-sys 0.48.0",
]
[[package]]
name = "rustyline"
version = "9.1.2"
version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039"
checksum = "5dfc8644681285d1fb67a467fb3021bfea306b99b4146b166a1fe3ada965eece"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"cfg-if",
"clipboard-win",
"dirs-next",
@ -415,7 +436,6 @@ dependencies = [
"nix",
"radix_trie",
"scopeguard",
"smallvec",
"unicode-segmentation",
"unicode-width",
"utf8parse",
@ -424,21 +444,21 @@ dependencies = [
[[package]]
name = "scopeguard"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "smallvec"
version = "1.8.0"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "str-buf"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a"
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
[[package]]
name = "strsim"
@ -448,44 +468,29 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.91"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.30"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
@ -493,40 +498,34 @@ dependencies = [
]
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.1.9"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "utf8-ranges"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "utf8parse"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
@ -544,15 +543,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
@ -561,43 +551,132 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.30.0"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "030b7ff91626e57a05ca64a07c481973cbb2db774e4852c9c7ca342408c6a99a"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
"windows-targets 0.48.5",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.30.0"
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.30.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.30.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.30.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.30.0"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"

View file

@ -1,16 +1,14 @@
[package]
name = "ablescript"
version = "0.3.0"
authors = ["able <abl3theabove@gmail.com>"]
name = "ablescript"
version = "0.5.4"
authors = ["AbleScript Developers"]
edition = "2021"
description = "The best programming language"
license = "MIT"
description = "The best programming language"
license = "MIT"
documentation = "https://ablecorp.us/able-script-the-book/"
repository = "https://github.com/AbleCorp/able-script"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
repository = "https://git.ablecorp.us/AbleScript/able-script"
[dependencies]
logos = "0.12"
rand = "0.8"
logos = "0.13"
rand = "0.8"

View file

@ -8,7 +8,7 @@
//! just plain subroutines and they do not return any value,
//! so their calls are statements.
use crate::{base_55::char2num, variables::Value};
use crate::{base_55::char2num, value::Value};
use std::{fmt::Debug, hash::Hash};
type Span = std::ops::Range<usize>;
@ -110,8 +110,8 @@ pub enum Stmt {
Loop {
body: Block,
},
Break,
HopBack,
Enough,
AndAgain,
Dim {
ident: Spanned<String>,
@ -136,9 +136,13 @@ pub enum Stmt {
expr: Spanned<Expr>,
args: Vec<Spanned<Expr>>,
},
Print(Spanned<Expr>),
Print {
expr: Spanned<Expr>,
newline: bool,
},
Read(Assignable),
Melo(Spanned<String>),
Finally(Block),
Rlyeh,
Rickroll,
}
@ -160,10 +164,11 @@ pub enum Expr {
index: Box<Spanned<Expr>>,
},
Len(Box<Spanned<Expr>>),
Keys(Box<Spanned<Expr>>),
Variable(String),
}
#[derive(Debug, PartialEq, Clone, Hash)]
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum Literal {
Char(char),
Int(isize),
@ -180,7 +185,7 @@ impl From<Literal> for Value {
}
}
#[derive(Debug, PartialEq, Clone, Hash)]
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum BinOpKind {
Add,
Subtract,

View file

@ -5,7 +5,7 @@ pub const fn char2num(c: char) -> isize {
'/' => 53,
'\\' => 54,
'.' => 55,
'U' => -210,
'U' => -210, // Backwards compatibility
'A'..='Z' => -(c as isize) + 64,
'a'..='z' => (c as isize) - 96,
_ => 0,

View file

@ -221,15 +221,7 @@ impl<'a, I: BootlegRead> Interpreter<'a, I> {
};
Some((*counter, index))
})
.find_map(
|(counter, index)| {
if counter == 0 {
Some(index)
} else {
None
}
},
)
.find_map(|(counter, index)| (counter == 0).then_some(index))
}
fn get_matching_opening_bracket(&mut self, closing: usize) -> Option<usize> {
@ -245,15 +237,7 @@ impl<'a, I: BootlegRead> Interpreter<'a, I> {
};
Some((*counter, index))
})
.find_map(
|(counter, index)| {
if counter == 0 {
Some(index)
} else {
None
}
},
)
.find_map(|(counter, index)| (counter == 0).then_some(index))
}
}

View file

@ -1,6 +1,6 @@
//! Number constants.
use crate::variables::{Value, Variable};
use crate::value::{Value, Variable};
use std::collections::HashMap;
pub const ANSWER: isize = 42;
@ -30,11 +30,12 @@ pub fn ablescript_consts() -> HashMap<String, Variable> {
Str("1452251871514141792252515212116".to_owned()),
),
("OCTOTHORPE", Str("#".to_owned())), // It's an octothorpe
("AMOGUS", Str("".to_owned())), // Amogus
("ANSWER", Int(ANSWER)),
("nul", Nul),
("always", Abool(crate::variables::Abool::Always)),
("sometimes", Abool(crate::variables::Abool::Sometimes)),
("never", Abool(crate::variables::Abool::Never)),
("always", Abool(crate::value::Abool::Always)),
("sometimes", Abool(crate::value::Abool::Sometimes)),
("never", Abool(crate::value::Abool::Never)),
]
.into_iter()
.map(|(name, value)| (name.to_owned(), Variable::from_value(value)))

View file

@ -9,15 +9,36 @@ pub struct Error {
#[derive(Debug)]
pub enum ErrorKind {
UnexpectedEof,
/// Parser expected token, but none was available
UnexpectedEoi,
/// Parser encountered unknown token
InvalidToken,
/// Parser expected certain token, but other one appeared
UnexpectedToken(Token),
/// Attempted to assign to undefined variable
UnknownVariable(String),
/// Attempted to access banned variable
MeloVariable(String),
TopLevelBreak,
BfInterpretError(InterpretError),
MismatchedArgumentError,
/// Breaking / re-starting loop outside loop
LoopOpOutsideLoop,
/// Rlyeh was executed but host interface's exit
/// doesn't exit the program
NonExitingRlyeh(i32),
/// Missing left-hand side expression in binary expression
MissingLhs,
IOError(io::Error),
/// Error when executing BF code
Brian(InterpretError),
/// IO Error
Io(io::Error),
}
impl Error {
@ -25,10 +46,10 @@ impl Error {
Self { kind, span }
}
/// Create an UnexpectedEof error, where the EOF occurs at the
/// given index in the file.
pub fn unexpected_eof(index: usize) -> Self {
Self::new(ErrorKind::UnexpectedEof, index..index)
/// Create an UnexpectedEoi error, where the EOI occurs at the
/// given index in the input.
pub fn unexpected_eoi(index: usize) -> Self {
Self::new(ErrorKind::UnexpectedEoi, index..index)
}
}
@ -46,26 +67,27 @@ impl std::error::Error for Error {}
impl Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::UnexpectedEof => write!(f, "unexpected end of file"),
ErrorKind::UnexpectedEoi => write!(f, "unexpected end of input"),
ErrorKind::InvalidToken => write!(f, "invalid token"),
ErrorKind::UnexpectedToken(Token::Melo) => write!(f, "unexpected marten"),
ErrorKind::UnexpectedToken(token) => write!(f, "unexpected token {:?}", token),
ErrorKind::UnknownVariable(name) => write!(f, "unknown identifier \"{}\"", name),
ErrorKind::MeloVariable(name) => write!(f, "banned variable \"{}\"", name),
ErrorKind::TopLevelBreak => write!(f, "can only `break` out of a loop"),
ErrorKind::BfInterpretError(err) => write!(f, "brainfuck error: {}", err),
ErrorKind::LoopOpOutsideLoop => write!(
f,
"unable to perform loop operation (enough or and enough) outside a loop"
),
&ErrorKind::NonExitingRlyeh(code) => write!(f, "program exited with code {code}"),
ErrorKind::Brian(err) => write!(f, "brainfuck error: {}", err),
// TODO: give concrete numbers here.
ErrorKind::MismatchedArgumentError => write!(f, "wrong number of function arguments"),
ErrorKind::MissingLhs => write!(f, "missing expression before binary operation"),
ErrorKind::IOError(err) => write!(f, "I/O error: {}", err),
ErrorKind::Io(err) => write!(f, "I/O error: {}", err),
}
}
}
impl From<io::Error> for Error {
impl From<io::Error> for ErrorKind {
fn from(e: io::Error) -> Self {
Self {
kind: ErrorKind::IOError(e),
span: 0..0,
}
Self::Io(e)
}
}

View file

@ -0,0 +1,53 @@
use crate::value::Variable;
use std::collections::HashMap;
/// Host Environment Interface
pub trait HostInterface {
/// Initial variables for a stack frame
fn initial_vars(&mut self) -> HashMap<String, Variable>;
/// Print a string
fn print(&mut self, string: &str, new_line: bool) -> std::io::Result<()>;
/// Read a byte
fn read_byte(&mut self) -> std::io::Result<u8>;
/// This function should exit the program with specified code.
///
/// For cases where exit is not desired, just let the function return
/// and interpreter will terminate with an error.
fn exit(&mut self, code: i32);
}
/// Standard [HostInterface] implementation
#[derive(Clone, Copy, Default)]
pub struct Standard;
impl HostInterface for Standard {
fn initial_vars(&mut self) -> HashMap<String, Variable> {
HashMap::default()
}
fn print(&mut self, string: &str, new_line: bool) -> std::io::Result<()> {
use std::io::Write;
let mut stdout = std::io::stdout();
stdout.write_all(string.as_bytes())?;
if new_line {
stdout.write_all(b"\n")?;
}
Ok(())
}
fn read_byte(&mut self) -> std::io::Result<u8> {
use std::io::Read;
let mut buf = [0];
std::io::stdin().read_exact(&mut buf)?;
Ok(buf[0])
}
fn exit(&mut self, code: i32) {
std::process::exit(code);
}
}

View file

@ -9,23 +9,22 @@
#![deny(missing_docs)]
use crate::{
ast::{Assignable, AssignableKind, Expr, Spanned, Stmt},
ast::{Assignable, AssignableKind, Block, Expr, Spanned, Stmt},
consts::ablescript_consts,
error::{Error, ErrorKind},
variables::{Functio, Value, ValueRef, Variable},
host_interface::HostInterface,
value::{Functio, Value, ValueRef, Variable},
};
use rand::random;
use std::{
cmp::Ordering,
collections::{HashMap, VecDeque},
io::{stdin, stdout, Read, Write},
mem::take,
ops::Range,
process::exit,
};
/// An environment for executing AbleScript code.
pub struct ExecEnv {
pub struct ExecEnv<H> {
/// The stack, ordered such that `stack[stack.len() - 1]` is the
/// top-most (newest) stack frame, and `stack[0]` is the
/// bottom-most (oldest) stack frame.
@ -37,6 +36,12 @@ pub struct ExecEnv {
/// (via the `read` statement). We store each of those bits as
/// booleans to facilitate easy manipulation.
read_buf: VecDeque<bool>,
/// Interface to interact with the host interface
host_interface: H,
/// Vector of blocks to be executed at the end of the program
finalisers: Vec<Block>,
}
/// A set of visible variable and function definitions in a single
@ -46,12 +51,12 @@ struct Scope {
variables: HashMap<String, Variable>,
}
impl Default for ExecEnv {
impl<H> Default for ExecEnv<H>
where
H: Default + HostInterface,
{
fn default() -> Self {
Self {
stack: vec![Default::default()],
read_buf: Default::default(),
}
Self::with_host_interface(H::default())
}
}
@ -68,57 +73,80 @@ enum HaltStatus {
/// We ran out of statements to execute.
Finished,
/// A `break` statement occurred at the given span, and was not
/// An `enough` statement occurred at the given span, and was not
/// caught by a `loop` statement up to this point.
Break(Range<usize>),
Enough(Range<usize>),
/// A `hopback` statement occurred at the given span, and was not
/// A `and again` statement occurred at the given span, and was not
/// caught by a `loop` statement up to this point.
Hopback(Range<usize>),
AndAgain(Range<usize>),
}
/// The number of bits the `read` statement reads at once from
/// standard input.
pub const READ_BITS: u8 = 3;
impl ExecEnv {
impl<H: HostInterface> ExecEnv<H> {
/// Create a new Scope with no predefined variable definitions or
/// other information.
pub fn new() -> Self {
Self::default()
pub fn with_host_interface(mut host_interface: H) -> Self {
Self {
stack: vec![
Default::default(),
Scope {
variables: host_interface.initial_vars(),
},
],
read_buf: Default::default(),
finalisers: vec![],
host_interface,
}
}
/// Create a new Scope with predefined variables
pub fn new_with_vars<I>(vars: I) -> Self
pub fn new_with_vars<I>(mut host_interface: H, vars: I) -> Self
where
I: IntoIterator<Item = (String, Variable)>,
{
let scope = Scope {
variables: ablescript_consts().into_iter().chain(vars).collect(),
};
Self {
stack: vec![scope],
stack: vec![
Scope {
variables: ablescript_consts().into_iter().chain(vars).collect(),
},
Scope {
variables: host_interface.initial_vars(),
},
],
read_buf: Default::default(),
finalisers: vec![],
host_interface,
}
}
/// Execute a set of Statements in the root stack frame. Return an
/// error if one or more of the Stmts failed to evaluate, or if a
/// `break` or `hopback` statement occurred at the top level.
/// `enough` or `and again` statement occurred at the top level.
pub fn eval_stmts(&mut self, stmts: &[Spanned<Stmt>]) -> Result<(), Error> {
match self.eval_stmts_hs(stmts, false)? {
HaltStatus::Finished => Ok(()),
HaltStatus::Break(span) | HaltStatus::Hopback(span) => Err(Error {
// It's an error to issue a `break` outside of a
HaltStatus::Enough(span) | HaltStatus::AndAgain(span) => Err(Error {
// It's an error to issue a `enough` outside of a
// `loop` statement.
kind: ErrorKind::TopLevelBreak,
kind: ErrorKind::LoopOpOutsideLoop,
span,
}),
}?;
while !self.finalisers.is_empty() {
for block in std::mem::take(&mut self.finalisers) {
self.eval_stmts_hs(&block, true)?;
}
}
Ok(())
}
/// The same as `eval_stmts`, but report "break" and "hopback"
/// The same as `eval_stmts`, but report "enough" and "and again"
/// exit codes as normal conditions in a HaltStatus enum, and
/// create a new stack frame if `stackframe` is true.
///
@ -195,18 +223,30 @@ impl ExecEnv {
.unwrap_or(Value::Nul)
}
Len(expr) => Value::Int(self.eval_expr(expr)?.length()),
Keys(expr) => Value::Cart(
self.eval_expr(expr)?
.into_cart()
.into_keys()
.enumerate()
.map(|(i, k)| (Value::Int(i as isize + 1), ValueRef::new(k)))
.collect(),
),
// TODO: not too happy with constructing an artificial
// Ident here.
Variable(name) => self.get_var(&Spanned::new(name.to_owned(), expr.span.clone()))?,
Variable(name) => {
self.get_var_value(&Spanned::new(name.to_owned(), expr.span.clone()))?
}
})
}
/// Perform the action indicated by a statement.
fn eval_stmt(&mut self, stmt: &Spanned<Stmt>) -> Result<HaltStatus, Error> {
match &stmt.item {
Stmt::Print(expr) => {
println!("{}", self.eval_expr(expr)?);
Stmt::Print { expr, newline } => {
let value = self.eval_expr(expr)?;
self.host_interface
.print(&value.to_string(), *newline)
.map_err(|e| Error::new(e.into(), stmt.span.clone()))?;
}
Stmt::Dim { ident, init } => {
let init = match init {
@ -254,44 +294,59 @@ impl ExecEnv {
}
Stmt::Call { expr, args } => {
let func = self.eval_expr(expr)?.into_functio();
self.fn_call(func, args, &stmt.span)?;
return self.fn_call(func, args, &stmt.span);
}
Stmt::Loop { body } => loop {
let res = self.eval_stmts_hs(body, true)?;
match res {
HaltStatus::Finished => {}
HaltStatus::Break(_) => break,
HaltStatus::Hopback(_) => continue,
HaltStatus::Finished => (),
HaltStatus::Enough(_) => break,
HaltStatus::AndAgain(_) => continue,
}
},
Stmt::Assign { assignable, value } => {
self.assign(assignable, self.eval_expr(value)?)?;
}
Stmt::Break => {
return Ok(HaltStatus::Break(stmt.span.clone()));
Stmt::Enough => {
return Ok(HaltStatus::Enough(stmt.span.clone()));
}
Stmt::HopBack => {
return Ok(HaltStatus::Hopback(stmt.span.clone()));
}
Stmt::Melo(ident) => {
self.get_var_mut(ident)?.melo = true;
Stmt::AndAgain => {
return Ok(HaltStatus::AndAgain(stmt.span.clone()));
}
Stmt::Melo(ident) => match self.get_var_mut(ident)? {
var @ Variable::Ref(_) => *var = Variable::Melo,
Variable::Melo => {
for s in &mut self.stack {
if s.variables.remove(&ident.item).is_some() {
break;
}
}
}
},
Stmt::Finally(block) => self.finalisers.push(block.clone()),
Stmt::Rlyeh => {
// Maybe print a creepy error message or something
// here at some point. ~~Alex
exit(random());
let code = random();
self.host_interface.exit(code);
return Err(Error::new(
ErrorKind::NonExitingRlyeh(code),
stmt.span.clone(),
));
}
Stmt::Rickroll => {
stdout()
.write_all(include_str!("rickroll").as_bytes())
.expect("Failed to write to stdout");
self.host_interface
.print(include_str!("rickroll"), false)
.map_err(|e| Error::new(e.into(), stmt.span.clone()))?;
}
Stmt::Read(assignable) => {
let mut value = 0;
for _ in 0..READ_BITS {
value <<= 1;
value += self.get_bit()? as isize;
value += self
.get_bit()
.map_err(|e| Error::new(e, stmt.span.clone()))?
as isize;
}
self.assign(assignable, Value::Int(value))?;
@ -305,10 +360,10 @@ impl ExecEnv {
fn assign(&mut self, dest: &Assignable, value: Value) -> Result<(), Error> {
match dest.kind {
AssignableKind::Variable => {
self.get_var_mut(&dest.ident)?.value.replace(value);
self.get_var_rc_mut(&dest.ident)?.replace(value);
}
AssignableKind::Index { ref indices } => {
let mut cell = self.get_var_rc(&dest.ident)?;
let mut cell = self.get_var_rc_mut(&dest.ident)?.clone();
for index in indices {
let index = self.eval_expr(index)?;
@ -357,14 +412,15 @@ impl ExecEnv {
func: Functio,
args: &[Spanned<Expr>],
span: &Range<usize>,
) -> Result<(), Error> {
) -> Result<HaltStatus, Error> {
// Arguments that are ExprKind::Variable are pass by
// reference; all other expressions are pass by value.
let args = args
.iter()
.map(|arg| {
if let Expr::Variable(name) = &arg.item {
self.get_var_rc(&Spanned::new(name.to_owned(), arg.span.clone()))
self.get_var_rc_mut(&Spanned::new(name.to_owned(), arg.span.clone()))
.cloned()
} else {
self.eval_expr(arg).map(ValueRef::new)
}
@ -379,7 +435,7 @@ impl ExecEnv {
func: Functio,
args: &[ValueRef],
span: &Range<usize>,
) -> Result<(), Error> {
) -> Result<HaltStatus, Error> {
match func {
Functio::Bf {
instructions,
@ -399,22 +455,21 @@ impl ExecEnv {
)
.interpret_with_output(&mut output)
.map_err(|e| Error {
kind: ErrorKind::BfInterpretError(e),
kind: ErrorKind::Brian(e),
span: span.to_owned(),
})?;
stdout()
.write_all(&output)
.expect("Failed to write to stdout");
match String::from_utf8(output) {
Ok(string) => self.host_interface.print(&string, false),
Err(e) => self
.host_interface
.print(&format!("{:?}", e.as_bytes()), true),
}
.map_err(|e| Error::new(e.into(), span.clone()))?;
Ok(HaltStatus::Finished)
}
Functio::Able { params, body } => {
if params.len() != args.len() {
return Err(Error {
kind: ErrorKind::MismatchedArgumentError,
span: span.to_owned(),
});
}
self.stack.push(Default::default());
for (param, arg) in params.iter().zip(args.iter()) {
@ -424,40 +479,45 @@ impl ExecEnv {
let res = self.eval_stmts_hs(&body, false);
self.stack.pop();
res?;
res
}
Functio::Builtin(b) => {
b.call(args).map_err(|e| Error::new(e, span.clone()))?;
Ok(HaltStatus::Finished)
}
Functio::Builtin(b) => b.call(args).map_err(|e| Error::new(e, span.clone()))?,
Functio::Chain { functios, kind } => {
use crate::value::functio::FunctioChainKind;
let (left_functio, right_functio) = *functios;
match kind {
crate::variables::FunctioChainKind::Equal => {
let (l, r) = args.split_at(args.len() / 2);
Ok(
match match kind {
FunctioChainKind::Equal => {
let (l, r) = args.split_at(args.len() / 2);
self.fn_call_with_values(left_functio, l, span)?;
self.fn_call_with_values(right_functio, r, span)?;
}
crate::variables::FunctioChainKind::ByArity => {
let (l, r) =
Self::deinterlace(args, (left_functio.arity(), right_functio.arity()));
(
self.fn_call_with_values(left_functio, l, span)?,
self.fn_call_with_values(right_functio, r, span)?,
)
}
FunctioChainKind::ByArity => {
let (l, r) = Self::deinterlace(
args,
(left_functio.arity(), right_functio.arity()),
);
self.fn_call_with_values(left_functio, &l, span)?;
self.fn_call_with_values(right_functio, &r, span)?;
}
};
}
Functio::Eval(code) => {
if !args.is_empty() {
return Err(Error {
kind: ErrorKind::MismatchedArgumentError,
span: span.to_owned(),
});
}
let stmts = crate::parser::parse(&code)?;
self.eval_stmts(&stmts)?;
(
self.fn_call_with_values(left_functio, &l, span)?,
self.fn_call_with_values(right_functio, &r, span)?,
)
}
} {
(s, HaltStatus::Finished) => s,
(HaltStatus::Finished, s) => s,
(_, r) => r,
},
)
}
Functio::Eval(code) => self.eval_stmts_hs(&crate::parser::parse(&code)?, false),
}
Ok(())
}
fn deinterlace(args: &[ValueRef], arities: (usize, usize)) -> (Vec<ValueRef>, Vec<ValueRef>) {
@ -473,18 +533,23 @@ impl ExecEnv {
.take(n_alternations)
.map(|chunk| ValueRef::clone(&chunk[0]))
.chain(
args[2 * n_alternations..]
args.get(2 * n_alternations..)
.iter()
.copied()
.flatten()
.map(ValueRef::clone)
.take(extra_l),
)
.collect(),
args.chunks(2)
.take(n_alternations)
.map(|chunk| ValueRef::clone(&chunk[1]))
.flat_map(|chunk| chunk.get(1))
.map(ValueRef::clone)
.chain(
args[2 * n_alternations..]
args.get(2 * n_alternations..)
.iter()
.copied()
.flatten()
.map(ValueRef::clone)
.take(extra_r),
)
@ -494,15 +559,14 @@ impl ExecEnv {
/// Get a single bit from the bit buffer, or refill it from
/// standard input if it is empty.
fn get_bit(&mut self) -> Result<bool, Error> {
fn get_bit(&mut self) -> Result<bool, ErrorKind> {
const BITS_PER_BYTE: u8 = 8;
if self.read_buf.is_empty() {
let mut data = [0];
stdin().read_exact(&mut data)?;
let byte = self.host_interface.read_byte()?;
for n in (0..BITS_PER_BYTE).rev() {
self.read_buf.push_back(((data[0] >> n) & 1) != 0);
self.read_buf.push_back(((byte >> n) & 1) != 0);
}
}
@ -514,7 +578,7 @@ impl ExecEnv {
/// Get the value of a variable. Throw an error if the variable is
/// inaccessible or banned.
fn get_var(&self, name: &Spanned<String>) -> Result<Value, Error> {
fn get_var_value(&self, name: &Spanned<String>) -> Result<Value, Error> {
// Search for the name in the stack from top to bottom.
match self
.stack
@ -522,25 +586,16 @@ impl ExecEnv {
.rev()
.find_map(|scope| scope.variables.get(&name.item))
{
Some(var) => {
if !var.melo {
Ok(var.value.borrow().clone())
} else {
Err(Error {
kind: ErrorKind::MeloVariable(name.item.to_owned()),
span: name.span.clone(),
})
}
}
None => Err(Error {
kind: ErrorKind::UnknownVariable(name.item.to_owned()),
Some(Variable::Ref(r)) => Ok(r.borrow().clone()),
Some(Variable::Melo) => Err(Error {
kind: ErrorKind::MeloVariable(name.item.to_owned()),
span: name.span.clone(),
}),
None => Ok(Value::Undefined),
}
}
/// Get a mutable reference to a variable. Throw an error if the
/// variable is inaccessible or banned.
/// Get a mutable reference to a variable.
fn get_var_mut(&mut self, name: &Spanned<String>) -> Result<&mut Variable, Error> {
// This function has a lot of duplicated code with `get_var`,
// which I feel like is a bad sign...
@ -550,16 +605,7 @@ impl ExecEnv {
.rev()
.find_map(|scope| scope.variables.get_mut(&name.item))
{
Some(var) => {
if !var.melo {
Ok(var)
} else {
Err(Error {
kind: ErrorKind::MeloVariable(name.item.to_owned()),
span: name.span.clone(),
})
}
}
Some(var) => Ok(var),
None => Err(Error {
kind: ErrorKind::UnknownVariable(name.item.to_owned()),
span: name.span.clone(),
@ -567,10 +613,16 @@ impl ExecEnv {
}
}
/// Get an Rc'd pointer to the value of a variable. Throw an error
/// Get an reference to an Rc'd pointer to the value of a variable. Throw an error
/// if the variable is inaccessible or banned.
fn get_var_rc(&mut self, name: &Spanned<String>) -> Result<ValueRef, Error> {
Ok(self.get_var_mut(name)?.value.clone())
fn get_var_rc_mut(&mut self, name: &Spanned<String>) -> Result<&mut ValueRef, Error> {
match self.get_var_mut(name)? {
Variable::Ref(r) => Ok(r),
Variable::Melo => Err(Error {
kind: ErrorKind::MeloVariable(name.item.to_owned()),
span: name.span.clone(),
}),
}
}
/// Declare a new variable, with the given initial value.
@ -585,19 +637,22 @@ impl ExecEnv {
.last()
.expect("Declaring variable on empty stack")
.variables
.insert(name.to_owned(), Variable { melo: false, value });
.insert(name.to_owned(), Variable::Ref(value));
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{Expr, Literal};
use crate::{
ast::{Expr, Literal},
host_interface::Standard,
};
#[test]
fn basic_expression_test() {
// Check that 2 + 2 = 4.
let env = ExecEnv::new();
let env = ExecEnv::<Standard>::default();
assert_eq!(
env.eval_expr(&Spanned {
item: Expr::BinOp {
@ -623,7 +678,7 @@ mod tests {
// The sum of an integer and an aboolean causes an aboolean
// coercion.
let env = ExecEnv::new();
let env = ExecEnv::<Standard>::default();
assert_eq!(
env.eval_expr(&Spanned {
item: Expr::BinOp {
@ -648,7 +703,7 @@ mod tests {
fn overflow_should_not_panic() {
// Integer overflow should throw a recoverable error instead
// of panicking.
let env = ExecEnv::new();
let env = ExecEnv::<Standard>::default();
assert_eq!(
env.eval_expr(&Spanned {
item: Expr::BinOp {
@ -692,7 +747,7 @@ mod tests {
// From here on out, I'll use this function to parse and run
// expressions, because writing out abstract syntax trees by hand
// takes forever and is error-prone.
fn eval(env: &mut ExecEnv, src: &str) -> Result<Value, Error> {
fn eval(env: &mut ExecEnv<Standard>, src: &str) -> Result<Value, Error> {
// We can assume there won't be any syntax errors in the
// interpreter tests.
let ast = crate::parser::parse(src).unwrap();
@ -704,12 +759,12 @@ mod tests {
// Functions have no return values, so use some
// pass-by-reference hacks to detect the correct
// functionality.
let mut env = ExecEnv::new();
let mut env = ExecEnv::<Standard>::default();
// Declaring and reading from a variable.
eval(&mut env, "dim foo 32; dim bar foo + 1;").unwrap();
eval(&mut env, "foo dim 32; bar dim foo + 1;").unwrap();
assert_eq!(
env.get_var(&Spanned {
env.get_var_value(&Spanned {
item: "bar".to_owned(),
span: 1..1,
})
@ -720,7 +775,7 @@ mod tests {
// Assigning an existing variable.
eval(&mut env, "/*hi*/ =: foo;").unwrap();
assert_eq!(
env.get_var(&Spanned {
env.get_var_value(&Spanned {
item: "foo".to_owned(),
span: 1..1,
})
@ -738,15 +793,15 @@ mod tests {
// Declaration and assignment of variables declared in an `if`
// statement should have no effect on those declared outside
// of it.
let mut env = ExecEnv::new();
let mut env = ExecEnv::<Standard>::default();
eval(
&mut env,
"dim foo 1; 2 =: foo; unless (never) { dim foo 3; 4 =: foo; }",
"foo dim 1; 2 =: foo; unless (never) { foo dim 3; 4 =: foo; }",
)
.unwrap();
assert_eq!(
env.get_var(&Spanned {
env.get_var_value(&Spanned {
item: "foo".to_owned(),
span: 1..1,
})

View file

@ -1,135 +1,60 @@
use logos::{Lexer, Logos};
#[derive(Logos, Debug, PartialEq, Clone)]
#[derive(Logos, Debug, PartialEq, Eq, Clone)]
#[logos(skip r"[ \t\n\f]+")]
#[logos(skip r"owo .*")]
#[rustfmt::skip]
pub enum Token {
// Symbols
#[token("(")]
LeftParen,
#[token(")")]
RightParen,
#[token("[")]
LeftBracket,
#[token("]")]
RightBracket,
#[token("{")]
LeftCurly,
#[token("}")]
RightCurly,
#[token(";")]
Semicolon,
#[token(",")]
Comma,
#[token("(")] LeftParen,
#[token(")")] RightParen,
#[token("[")] LeftBracket,
#[token("]")] RightBracket,
#[token("{")] LeftCurly,
#[token("}")] RightCurly,
#[token(";")] Semicolon,
#[token(",")] Comma,
// Operators
#[token("+")]
Plus,
#[token("-")]
Minus,
#[token("*")]
Star,
#[token("/")]
FwdSlash,
#[token("=:")]
Assign,
#[token("<=")]
Arrow,
#[token("+")] Plus,
#[token("-")] Minus,
#[token("*")] Star,
#[token("/")] FwdSlash,
#[token("=:")] Assign,
#[token("<=")] Arrow,
// Logical operators
#[token("<")]
LessThan,
#[token(">")]
GreaterThan,
#[token("=")]
Equals,
#[token("ain't")]
Aint,
#[token("<")] LessThan,
#[token(">")] GreaterThan,
#[token("=")] Equals,
#[token("ain't")] Aint,
// Keywords
#[token("functio")]
Functio,
/// Brain fuck FFI
#[token("bff")]
Bff,
/// Variable bro
#[token("dim")]
Dim,
/// Prints the preceding things
#[token("print")]
Print,
/// Read input into preceding variable
#[token("read")]
Read,
/// Ban the following variable from ever being used again
#[token("melo")]
Melo,
#[token("T-Dark")]
TDark,
#[token("functio")] Functio,
#[token("bff")] Bff,
#[token("dim")] Dim,
#[token("print")] Print,
#[token("read")] Read,
#[token("melo")] Melo,
#[token("T-Dark")] TDark,
// Control flow keywords
#[token("unless")]
Unless,
#[token("unless")] Unless,
#[token("loop")] Loop,
#[token("enough")] Enough,
#[token("and again")] AndAgain,
#[token("finally")] Finally,
#[token("rlyeh")] Rlyeh,
#[token("loop")]
Loop,
#[token("break")]
Break,
/// HopBack hops on the back of loop - like `continue`
#[token("hopback")]
HopBack,
/// Crash with random error (see discussion #17)
#[token("rlyeh")]
Rlyeh,
#[token("rickroll")]
Rickroll,
#[token("rickroll")] Rickroll,
// Literals
/// String
#[token("/*", get_string)]
String(String),
/// Integer
#[regex(r"-?[0-9]+", get_value)]
Integer(isize),
// A character (to be base-55 converted)
#[regex(r"\p{XID_Start}", get_value)]
Char(char),
/// An identifier
#[token("/*", get_string)] String(String),
#[regex(r"-?[0-9]+", get_value)] Integer(isize),
#[regex(r"\p{XID_Start}", get_value)] Char(char),
#[regex(r"\p{XID_Start}[\p{XID_Continue}]+", get_ident)]
#[token("and ", |_| "and".to_owned())]
Identifier(String),
#[regex(r"owo .*")]
Comment,
#[regex(r"[ \t\n\f]+", logos::skip)]
#[error]
Error,
}
fn get_value<T: std::str::FromStr>(lexer: &mut Lexer<Token>) -> Option<T> {
@ -138,7 +63,31 @@ fn get_value<T: std::str::FromStr>(lexer: &mut Lexer<Token>) -> Option<T> {
fn get_string(lexer: &mut Lexer<Token>) -> Option<String> {
lexer.bump(lexer.remainder().find("*/")?);
let string = lexer.slice()[2..].to_owned();
let mut string = String::new();
let mut slice = &lexer.slice()[2..];
while let Some(escape_start) = slice.find('"') {
// Push predeceasing string
string.push_str(slice.get(..escape_start)?);
// Move slice behind escape start delimiter
slice = slice.get(escape_start + 1..)?;
// Get escape end delimiter position and parse string before it to
// a character from it's unicode value (base-12) and push it to string
let escape_end = slice.find('"')?;
string.push(
u32::from_str_radix(slice.get(..escape_end)?, 12)
.ok()
.and_then(char::from_u32)?,
);
// Move slice behind escape end delimiter
slice = slice.get(escape_end + 1..)?;
}
// Push remaining string
string.push_str(slice);
lexer.bump(2);
Some(string)
@ -179,8 +128,17 @@ mod tests {
RightCurly,
RightCurly,
];
let lexer = Token::lexer(code);
let result: Vec<Token> = lexer.collect();
let result: Vec<_> = Token::lexer(code).collect::<Result<_, _>>().unwrap();
assert_eq!(result, expected);
}
#[test]
fn escapes() {
let code = r#"/*»"720B""722B""7195"«*/"#;
let expected = &[Token::String("»にゃぁ«".to_owned())];
let result: Vec<_> = Token::lexer(code).collect::<Result<_, _>>().unwrap();
assert_eq!(result, expected);
}
}

View file

@ -1,11 +1,16 @@
#![doc = include_str!("../../README.md")]
#![forbid(unsafe_code, clippy::unwrap_used)]
//! The AbleScript language reference implementation. See
//! <https://git.ablecorp.us/AbleScript/able-script> for more
//! information.
#![forbid(unsafe_code)]
#![cfg_attr(not(test), forbid(clippy::unwrap_used))]
pub mod ast;
pub mod error;
pub mod host_interface;
pub mod interpret;
pub mod parser;
pub mod variables;
pub mod value;
mod base_55;
mod brian;

View file

@ -31,12 +31,12 @@ impl<'source> Parser<'source> {
let mut ast = vec![];
while let Some(token) = self.lexer.next() {
match token {
// Ignore comments
Token::Comment => continue,
// T-Dark block (replace `lang` with `script`)
Token::TDark => ast.extend(self.tdark_flow()?),
token => ast.push(self.parse_stmt(token)?),
Ok(Token::TDark) => ast.extend(self.tdark_flow()?),
Ok(token) => ast.push(self.parse_stmt(token)?),
// Invalid token
Err(()) => return Err(Error::new(ErrorKind::InvalidToken, self.lexer.span())),
}
}
Ok(ast)
@ -46,15 +46,10 @@ impl<'source> Parser<'source> {
///
/// If EOF, return Error instead of None
fn checked_next(&mut self) -> Result<Token, Error> {
loop {
match self
.lexer
.next()
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))?
{
Token::Comment => (),
token => break Ok(token),
}
match self.lexer.next() {
Some(Ok(t)) => Ok(t),
Some(Err(())) => Err(Error::new(ErrorKind::InvalidToken, self.lexer.span())),
None => Err(Error::unexpected_eoi(self.lexer.span().start)),
}
}
@ -66,40 +61,16 @@ impl<'source> Parser<'source> {
let start = self.lexer.span().start;
match token {
Token::Unless => Ok(Spanned::new(
self.unless_flow()?,
start..self.lexer.span().end,
)),
Token::Functio => Ok(Spanned::new(
self.functio_flow()?,
start..self.lexer.span().end,
)),
Token::Bff => Ok(Spanned::new(self.bff_flow()?, start..self.lexer.span().end)),
Token::Dim => Ok(Spanned::new(self.dim_flow()?, start..self.lexer.span().end)),
Token::Melo => Ok(Spanned::new(
self.melo_flow()?,
start..self.lexer.span().end,
)),
Token::Loop => Ok(Spanned::new(
self.loop_flow()?,
start..self.lexer.span().end,
)),
Token::Break => Ok(Spanned::new(
self.semicolon_terminated(Stmt::Break)?,
start..self.lexer.span().end,
)),
Token::HopBack => Ok(Spanned::new(
self.semicolon_terminated(Stmt::HopBack)?,
start..self.lexer.span().end,
)),
Token::Rlyeh => Ok(Spanned::new(
self.semicolon_terminated(Stmt::Rlyeh)?,
start..self.lexer.span().end,
)),
Token::Rickroll => Ok(Spanned::new(
self.semicolon_terminated(Stmt::Rickroll)?,
start..self.lexer.span().end,
)),
Token::Unless => self.unless_flow(),
Token::Functio => self.functio_flow(),
Token::Bff => self.bff_flow(),
Token::Melo => self.melo_flow(),
Token::Loop => self.get_block().map(|body| Stmt::Loop { body }),
Token::Enough => self.semicolon_terminated(Stmt::Enough),
Token::AndAgain => self.semicolon_terminated(Stmt::AndAgain),
Token::Finally => self.get_block().map(Stmt::Finally),
Token::Rlyeh => self.semicolon_terminated(Stmt::Rlyeh),
Token::Rickroll => self.semicolon_terminated(Stmt::Rickroll),
Token::Identifier(_)
| Token::String(_)
@ -107,16 +78,14 @@ impl<'source> Parser<'source> {
| Token::Char(_)
| Token::Aint
| Token::LeftBracket
| Token::LeftParen => Ok(Spanned::new(
self.value_flow(token)?,
start..self.lexer.span().end,
)),
| Token::LeftParen => self.value_flow(token),
t => Err(Error {
kind: ErrorKind::UnexpectedToken(t),
span: start..self.lexer.span().end,
}),
}
.map(|stmt| Spanned::new(stmt, start..self.lexer.span().end))
}
/// Require statement to be semicolon terminated
@ -138,14 +107,9 @@ impl<'source> Parser<'source> {
/// Get an Identifier
fn get_ident(&mut self) -> Result<Spanned<String>, Error> {
match self.checked_next()? {
Token::Identifier(ident) => Ok(Spanned::new(
if self.tdark {
ident.replace("lang", "script")
} else {
ident
},
self.lexer.span(),
)),
Token::Identifier(ident) => {
Ok(Spanned::new(self.tdark_subst(ident), self.lexer.span()))
}
t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
}
}
@ -167,50 +131,20 @@ impl<'source> Parser<'source> {
match token {
// Values
Token::Identifier(i) => Ok(Spanned::new(
Expr::Variable(if self.tdark {
i.replace("lang", "script")
} else {
i
}),
start..self.lexer.span().end,
)),
Token::Integer(i) => Ok(Spanned::new(
Expr::Literal(Literal::Int(i)),
start..self.lexer.span().end,
)),
Token::String(s) => Ok(Spanned::new(
Expr::Literal(Literal::Str(if self.tdark {
s.replace("lang", "script")
} else {
s
})),
start..self.lexer.span().end,
)),
Token::Char(c) => Ok(Spanned::new(
Expr::Literal(Literal::Char(c)),
start..self.lexer.span().end,
)),
Token::Identifier(i) => Ok(Expr::Variable(self.tdark_subst(i))),
Token::Integer(i) => Ok(Expr::Literal(Literal::Int(i))),
Token::String(s) => Ok(Expr::Literal(Literal::Str(self.tdark_subst(s)))),
Token::Char(c) => Ok(Expr::Literal(Literal::Char(c))),
Token::LeftBracket => match buf.take() {
Some(buf) => Ok(Spanned::new(
self.index_flow(buf)?,
start..self.lexer.span().end,
)),
None => Ok(Spanned::new(
self.cart_flow()?,
start..self.lexer.span().end,
)),
Some(buf) => self.index_flow(buf),
None => self.cart_flow(),
},
// Operations
Token::Aint if buf.is_none() => Ok(Spanned::new(
{
let next = self.checked_next()?;
Expr::Aint(Box::new(self.parse_expr(next, buf)?))
},
start..self.lexer.span().end,
)),
Token::Aint if buf.is_none() => {
let next = self.checked_next()?;
Ok(Expr::Aint(Box::new(self.parse_expr(next, buf)?)))
}
Token::Plus
| Token::Minus
@ -219,17 +153,15 @@ impl<'source> Parser<'source> {
| Token::Equals
| Token::LessThan
| Token::GreaterThan
| Token::Aint => Ok(Spanned::new(
self.binop_flow(
BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?,
buf,
)?,
start..self.lexer.span().end,
)),
| Token::Aint => self.binop_flow(
BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?,
buf,
),
Token::LeftParen => self.expr_flow(Token::RightParen),
Token::LeftParen => return self.expr_flow(Token::RightParen),
t => Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
}
.map(|expr| Spanned::new(expr, start..self.lexer.span().end))
}
/// Flow for creating carts
@ -258,7 +190,7 @@ impl<'source> Parser<'source> {
cart.push((
value,
buf.take().ok_or_else(|| {
Error::unexpected_eof(self.lexer.span().start)
Error::unexpected_eoi(self.lexer.span().start)
})?,
));
@ -268,7 +200,7 @@ impl<'source> Parser<'source> {
t => buf = Some(self.parse_expr(t, &mut buf)?),
}
}
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))?;
.ok_or_else(|| Error::unexpected_eoi(self.lexer.span().start))?;
cart.push((value, key));
}
@ -294,6 +226,10 @@ impl<'source> Parser<'source> {
}
None => break Expr::Len(Box::new(expr)),
},
Token::GreaterThan if buf.is_none() => {
self.require(Token::RightBracket)?;
break Expr::Keys(Box::new(expr));
}
token => buf = Some(self.parse_expr(token, &mut buf)?),
}
})
@ -315,10 +251,7 @@ impl<'source> Parser<'source> {
.ok_or_else(|| Error::new(ErrorKind::MissingLhs, self.lexer.span()))?,
),
rhs: {
let next = self
.lexer
.next()
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))?;
let next = self.checked_next()?;
Box::new(self.parse_expr(next, &mut None)?)
},
kind,
@ -368,14 +301,28 @@ impl<'source> Parser<'source> {
/// will parse it to function call or print statement.
fn value_flow(&mut self, init: Token) -> Result<Stmt, Error> {
let mut buf = Some(self.parse_expr(init, &mut None)?);
let r = loop {
Ok(loop {
match self.checked_next()? {
// Print to stdout
Token::Print => {
let stmt = Stmt::Print(buf.take().ok_or_else(|| {
Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span())
})?);
break self.semicolon_terminated(stmt)?;
break Stmt::Print {
expr: buf.take().ok_or_else(|| {
Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span())
})?,
newline: match self.checked_next()? {
Token::Semicolon => true,
Token::Minus => {
self.require(Token::Semicolon)?;
false
}
token => {
return Err(Error::new(
ErrorKind::UnexpectedToken(token),
self.lexer.span(),
));
}
},
};
}
// Functio call
@ -388,7 +335,32 @@ impl<'source> Parser<'source> {
})?)?;
}
// Variable Assignment
// Variable declaration
Token::Dim => {
return match buf.take() {
Some(Spanned {
item: Expr::Variable(ident),
span,
}) => Ok(Stmt::Dim {
ident: Spanned::new(ident, span),
init: {
let mut init = None;
loop {
match self.checked_next()? {
Token::Semicolon => break init,
token => init = Some(self.parse_expr(token, &mut init)?),
}
}
},
}),
_ => Err(Error::new(
ErrorKind::UnexpectedToken(Token::Dim),
self.lexer.span(),
)),
}
}
// Variable assignment
Token::Assign => {
return match buf.take() {
Some(expr) => self.assignment_flow(expr),
@ -414,9 +386,7 @@ impl<'source> Parser<'source> {
t => buf = Some(self.parse_expr(t, &mut buf)?),
}
};
Ok(r)
})
}
/// Parse Unless flow
@ -492,16 +462,10 @@ impl<'source> Parser<'source> {
let mut code: Vec<u8> = vec![];
loop {
match self.checked_next()? {
Token::Plus
| Token::Minus
| Token::Comma
| Token::LeftBracket
| Token::RightBracket
| Token::LessThan
| Token::GreaterThan => code.push(self.lexer.slice().as_bytes()[0]),
Token::RightCurly => break,
_ => (),
match self.lexer.next() {
Some(Ok(Token::RightCurly)) => break,
Some(_) => code.push(self.lexer.slice().as_bytes()[0]),
None => return Err(Error::unexpected_eoi(self.lexer.span().start)),
}
}
@ -545,20 +509,6 @@ impl<'source> Parser<'source> {
Ok(Stmt::Call { expr, args })
}
/// Parse variable declaration
fn dim_flow(&mut self) -> Result<Stmt, Error> {
let ident = self.get_ident()?;
let mut init = None;
loop {
match self.checked_next()? {
Token::Semicolon => break,
t => init = Some(self.parse_expr(t, &mut init)?),
}
}
Ok(Stmt::Dim { ident, init })
}
/// Parse assignment to assignable
fn assignment_flow(&mut self, value: Spanned<Expr>) -> Result<Stmt, Error> {
let ident = self.get_ident()?;
@ -595,13 +545,37 @@ impl<'source> Parser<'source> {
self.semicolon_terminated(Stmt::Melo(ident))
}
/// Parse loop flow
///
/// `loop` is an infinite loop, no condition, only body
fn loop_flow(&mut self) -> Result<Stmt, Error> {
Ok(Stmt::Loop {
body: self.get_block()?,
})
/// Perform lang -> script substitution if in T-Dark block
fn tdark_subst(&self, mut string: String) -> String {
if self.tdark {
if let Some(pos) = string.to_lowercase().find("lang") {
let range = pos..pos + 4;
let mut count_upper = 0_u8;
string.replace_range(
range.clone(),
&(string[range]
.chars()
.zip("scri".chars())
.map(|(lc, sc)| {
if lc.is_uppercase() {
count_upper += 1;
sc.to_ascii_uppercase()
} else {
sc.to_ascii_lowercase()
}
})
.collect::<String>()
+ match count_upper {
0 | 1 => "pt",
2 if rand::random() => "Pt",
2 => "pT",
_ => "PT",
}),
)
}
}
string
}
}
@ -618,40 +592,43 @@ mod tests {
fn simple_math() {
let code = "1 * (num + 3) / 666 print;";
let expected = &[Spanned {
item: Stmt::Print(Spanned {
item: Expr::BinOp {
lhs: Box::new(Spanned {
item: Expr::BinOp {
lhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(1)),
span: 0..1,
}),
rhs: Box::new(Spanned {
item: Expr::BinOp {
lhs: Box::new(Spanned {
item: Expr::Variable("num".to_owned()),
span: 5..6,
}),
rhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(3)),
span: 9..10,
}),
kind: BinOpKind::Add,
},
span: 5..10,
}),
kind: BinOpKind::Multiply,
},
span: 0..11,
}),
rhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(666)),
span: 14..17,
}),
kind: BinOpKind::Divide,
item: Stmt::Print {
expr: Spanned {
item: Expr::BinOp {
lhs: Box::new(Spanned {
item: Expr::BinOp {
lhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(1)),
span: 0..1,
}),
rhs: Box::new(Spanned {
item: Expr::BinOp {
lhs: Box::new(Spanned {
item: Expr::Variable("num".to_owned()),
span: 5..6,
}),
rhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(3)),
span: 9..10,
}),
kind: BinOpKind::Add,
},
span: 5..10,
}),
kind: BinOpKind::Multiply,
},
span: 0..11,
}),
rhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(666)),
span: 14..17,
}),
kind: BinOpKind::Divide,
},
span: 0..17,
},
span: 0..17,
}),
newline: true,
},
span: 0..24,
}];
@ -661,16 +638,16 @@ mod tests {
#[test]
fn variable_declaration() {
let code = "dim var 42;";
let code = "var dim 42;";
let expected = &[Spanned {
item: Stmt::Dim {
ident: Spanned {
item: "var".to_owned(),
span: 4..5,
span: 0..3,
},
init: Some(Spanned {
item: Expr::Literal(Literal::Int(42)),
span: 8..10,
span: 4..6,
}),
},
span: 0..11,
@ -700,10 +677,13 @@ mod tests {
span: 8..21,
},
body: vec![Spanned {
item: Stmt::Print(Spanned {
item: Expr::Literal(Literal::Str("Buy Able products!".to_owned())),
span: 25..47,
}),
item: Stmt::Print {
expr: Spanned {
item: Expr::Literal(Literal::Str("Buy Able products!".to_owned())),
span: 25..47,
},
newline: true,
},
span: 25..54,
}],
},
@ -716,12 +696,12 @@ mod tests {
#[test]
fn tdark() {
let code = "T-Dark { dim lang /*lang*/ + lang; }";
let code = "T-Dark { lang dim /*lang*/ + lang; }";
let expected = &[Spanned {
item: Stmt::Dim {
ident: Spanned {
item: "script".to_owned(),
span: 13..17,
span: 9..15,
},
init: Some(Spanned {
item: Expr::BinOp {
@ -749,41 +729,44 @@ mod tests {
fn cart_construction() {
let code = "[/*able*/ <= 1, /*script*/ <= 3 - 1] print;";
let expected = &[Spanned {
item: Stmt::Print(Spanned {
item: Expr::Cart(vec![
(
Spanned {
item: Expr::Literal(Literal::Str("able".to_owned())),
span: 1..7,
},
Spanned {
item: Expr::Literal(Literal::Int(1)),
span: 11..12,
},
),
(
Spanned {
item: Expr::Literal(Literal::Str("script".to_owned())),
span: 14..22,
},
Spanned {
item: Expr::BinOp {
kind: BinOpKind::Subtract,
lhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(3)),
span: 26..27,
}),
rhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(1)),
span: 30..31,
}),
item: Stmt::Print {
expr: Spanned {
item: Expr::Cart(vec![
(
Spanned {
item: Expr::Literal(Literal::Str("able".to_owned())),
span: 1..7,
},
span: 26..31,
},
),
]),
span: 0..32,
}),
Spanned {
item: Expr::Literal(Literal::Int(1)),
span: 11..12,
},
),
(
Spanned {
item: Expr::Literal(Literal::Str("script".to_owned())),
span: 14..22,
},
Spanned {
item: Expr::BinOp {
kind: BinOpKind::Subtract,
lhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(3)),
span: 26..27,
}),
rhs: Box::new(Spanned {
item: Expr::Literal(Literal::Int(1)),
span: 30..31,
}),
},
span: 26..31,
},
),
]),
span: 0..32,
},
newline: true,
},
span: 0..39,
}];
@ -795,28 +778,31 @@ mod tests {
fn cart_index() {
let code = "[/*able*/ <= /*ablecorp*/][/*ablecorp*/] print;";
let expected = &[Spanned {
item: Stmt::Print(Spanned {
item: Expr::Index {
expr: Box::new(Spanned {
item: Expr::Cart(vec![(
Spanned {
item: Expr::Literal(Literal::Str("able".to_owned())),
span: 1..7,
},
Spanned {
item: Expr::Literal(Literal::Str("ablecorp".to_owned())),
span: 11..21,
},
)]),
span: 0..22,
}),
index: Box::new(Spanned {
item: Expr::Literal(Literal::Str("ablecorp".to_owned())),
span: 23..33,
}),
item: Stmt::Print {
expr: Spanned {
item: Expr::Index {
expr: Box::new(Spanned {
item: Expr::Cart(vec![(
Spanned {
item: Expr::Literal(Literal::Str("able".to_owned())),
span: 1..7,
},
Spanned {
item: Expr::Literal(Literal::Str("ablecorp".to_owned())),
span: 11..21,
},
)]),
span: 0..22,
}),
index: Box::new(Spanned {
item: Expr::Literal(Literal::Str("ablecorp".to_owned())),
span: 23..33,
}),
},
span: 0..34,
},
span: 0..34,
}),
newline: true,
},
span: 0..41,
}];

View file

@ -0,0 +1,279 @@
use super::{functio::FunctioChainKind, Abool, Cart, Functio, Value, ValueRef};
use crate::{brian::INSTRUCTION_MAPPINGS, consts};
use std::collections::HashMap;
impl Value {
/// Coerce a value to an integer.
pub fn into_isize(self) -> isize {
match self {
Value::Nul => consts::ANSWER,
Value::Undefined => rand::random(),
Value::Str(text) => text
.parse()
.unwrap_or_else(|_| text.chars().map(|cr| cr as isize).sum()),
Value::Int(i) => i,
Value::Abool(a) => a as _,
Value::Functio(f) => match f {
Functio::Bf {
instructions,
tape_len,
} => {
instructions.into_iter().map(|x| x as isize).sum::<isize>() * tape_len as isize
}
Functio::Able { params, body } => {
params
.into_iter()
.map(|x| x.bytes().map(|x| x as isize).sum::<isize>())
.sum::<isize>()
+ body.len() as isize
}
Functio::Builtin(b) => (b.fn_addr() + b.arity) as _,
Functio::Chain { functios, kind } => {
let (lf, rf) = *functios;
Value::Functio(lf).into_isize()
+ Value::Functio(rf).into_isize()
* match kind {
FunctioChainKind::Equal => -1,
FunctioChainKind::ByArity => 1,
}
}
Functio::Eval(code) => code.bytes().map(|x| x as isize).sum(),
},
Value::Cart(c) => c
.into_iter()
.map(|(i, v)| i.into_isize() * v.borrow().clone().into_isize())
.sum(),
}
}
/// Coerce a value to an aboolean.
pub fn into_abool(self) -> Abool {
match self {
Value::Nul => Abool::Never,
Value::Undefined => Abool::Sometimes,
Value::Str(s) => match s.to_lowercase().as_str() {
"never" | "no" | "🇳🇴" => Abool::Never,
"sometimes" => Abool::Sometimes,
"always" | "yes" => Abool::Always,
s => (!s.is_empty()).into(),
},
Value::Int(x) => match x.cmp(&0) {
std::cmp::Ordering::Less => Abool::Never,
std::cmp::Ordering::Equal => Abool::Sometimes,
std::cmp::Ordering::Greater => Abool::Always,
},
Value::Abool(a) => a,
Value::Functio(f) => match f {
Functio::Bf {
instructions,
tape_len,
} => Value::Int(
(instructions.iter().map(|x| *x as usize).sum::<usize>() * tape_len) as _,
)
.into_abool(),
Functio::Able { params, body } => {
let str_to_isize =
|x: String| -> isize { x.as_bytes().iter().map(|x| *x as isize).sum() };
let params: isize = params.into_iter().map(str_to_isize).sum();
let body: isize = body
.into_iter()
.map(|x| format!("{:?}", x))
.map(str_to_isize)
.sum();
Value::Int((params + body) % 3 - 1).into_abool()
}
Functio::Builtin(b) => (b.fn_addr() % b.arity == 0).into(),
Functio::Chain { functios, kind } => {
let (lhs, rhs) = *functios;
match kind {
FunctioChainKind::Equal => {
Value::Abool(Value::Functio(lhs).into_abool())
+ Value::Abool(Value::Functio(rhs).into_abool())
}
FunctioChainKind::ByArity => {
Value::Abool(Value::Functio(lhs).into_abool())
* Value::Abool(Value::Functio(rhs).into_abool())
}
}
.into_abool()
}
Functio::Eval(code) => Value::Str(code).into_abool(),
},
Value::Cart(c) => {
if c.is_empty() {
Abool::Never
} else {
Abool::Always
}
}
}
}
/// Coerce a value to a functio.
pub fn into_functio(self) -> Functio {
match self {
Value::Nul | Value::Undefined => Functio::Able {
body: vec![],
params: vec![],
},
Value::Str(s) => Functio::Eval(s),
Value::Int(i) => Functio::Bf {
instructions: {
std::iter::successors(Some(i as usize), |i| {
Some(i / INSTRUCTION_MAPPINGS.len())
})
.take_while(|&i| i != 0)
.map(|i| INSTRUCTION_MAPPINGS[i % INSTRUCTION_MAPPINGS.len()])
.collect()
},
tape_len: crate::brian::DEFAULT_TAPE_SIZE_LIMIT,
},
Value::Abool(a) => Functio::Eval(match a {
Abool::Never => "".to_owned(),
Abool::Sometimes => {
use rand::seq::SliceRandom;
let mut str_chars: Vec<_> = "Buy Able Products!".chars().collect();
str_chars.shuffle(&mut rand::thread_rng());
format!(r#""{}"print;"#, str_chars.iter().collect::<String>())
}
Abool::Always => r#"loop{"Buy Able products!"print;}"#.to_owned(),
}),
Value::Functio(f) => f,
Value::Cart(c) => {
let kind = if let Some(114514) = c
.get(&Value::Str("1452251871514141792252515212116".to_owned()))
.map(|x| x.borrow().to_owned().into_isize())
{
FunctioChainKind::Equal
} else {
FunctioChainKind::ByArity
};
let mut cart_vec = c.iter().collect::<Vec<_>>();
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
cart_vec
.into_iter()
.map(|(_, x)| x.borrow().to_owned().into_functio())
.reduce(|acc, x| Functio::Chain {
functios: Box::new((acc, x)),
kind,
})
.unwrap_or_else(|| Functio::Eval(r#""Buy Able Products!"print;"#.to_owned()))
}
}
}
/// Coerce a value into a cart.
pub fn into_cart(self) -> Cart {
match self {
Value::Nul => HashMap::new(),
Value::Undefined => [(Value::Undefined, ValueRef::new(Value::Undefined))]
.into_iter()
.collect(),
Value::Str(s) => s
.chars()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as isize + 1),
ValueRef::new(Value::Str(x.to_string())),
)
})
.collect(),
Value::Int(i) => Value::Str(i.to_string()).into_cart(),
Value::Abool(a) => Value::Str(a.to_string()).into_cart(),
Value::Functio(f) => match f {
Functio::Able { params, body } => {
let params: Cart = params
.into_iter()
.enumerate()
.map(|(i, x)| (Value::Int(i as isize + 1), ValueRef::new(Value::Str(x))))
.collect();
let body: Cart = body
.into_iter()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as isize + 1),
ValueRef::new(Value::Str(format!("{:?}", x))),
)
})
.collect();
let mut cart = HashMap::new();
cart.insert(
Value::Str("params".to_owned()),
ValueRef::new(Value::Cart(params)),
);
cart.insert(
Value::Str("body".to_owned()),
ValueRef::new(Value::Cart(body)),
);
cart
}
Functio::Bf {
instructions,
tape_len,
} => {
let mut cart: Cart = instructions
.into_iter()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as isize + 1),
ValueRef::new(
char::from_u32(x as u32)
.map(|x| Value::Str(x.to_string()))
.unwrap_or(Value::Nul),
),
)
})
.collect();
cart.insert(
Value::Str("tapelen".to_owned()),
ValueRef::new(Value::Int(tape_len as _)),
);
cart
}
Functio::Builtin(b) => {
let mut cart = HashMap::new();
cart.insert(
Value::Str("addr".to_owned()),
ValueRef::new(Value::Cart(Value::Int(b.fn_addr() as _).into_cart())),
);
cart.insert(
Value::Str("arity".to_owned()),
ValueRef::new(Value::Int(b.arity as _)),
);
cart
}
Functio::Chain { functios, kind } => {
let (lhs, rhs) = *functios;
match kind {
FunctioChainKind::Equal => {
Value::Cart(Value::Functio(lhs).into_cart())
+ Value::Cart(Value::Functio(rhs).into_cart())
}
FunctioChainKind::ByArity => {
Value::Cart(Value::Functio(lhs).into_cart())
* Value::Cart(Value::Functio(rhs).into_cart())
}
}
.into_cart()
}
Functio::Eval(s) => Value::Str(s).into_cart(),
},
Value::Cart(c) => c,
}
}
}

View file

@ -0,0 +1,113 @@
use super::ValueRef;
use crate::ast::Block;
use std::{hash::Hash, rc::Rc};
type BuiltinRc = Rc<dyn Fn(&[ValueRef]) -> Result<(), crate::error::ErrorKind>>;
/// AbleScript Function
#[derive(Debug, PartialEq, Clone, Hash)]
pub enum Functio {
/// BF instructions and a length of the type
///
/// Takes input bytes as parameters
Bf {
instructions: Vec<u8>,
tape_len: usize,
},
/// Regular AbleScript functio
///
/// Consists of parameter name mapping and AST
Able { params: Vec<String>, body: Block },
/// Builtin Rust functio
Builtin(BuiltinFunctio),
/// Chained functio pair
Chain {
functios: Box<(Functio, Functio)>,
kind: FunctioChainKind,
},
/// Code to be parsed and then executed in current scope
Eval(String),
}
impl Functio {
pub fn arity(&self) -> usize {
match self {
Functio::Bf {
instructions: _,
tape_len: _,
} => 0,
Functio::Able { params, body: _ } => params.len(),
Functio::Builtin(b) => b.arity,
Functio::Chain { functios, kind: _ } => functios.0.arity() + functios.1.arity(),
Functio::Eval(_) => 0,
}
}
}
/// Built-in Rust functio
#[derive(Clone)]
pub struct BuiltinFunctio {
pub(super) function: BuiltinRc,
pub(super) arity: usize,
}
impl BuiltinFunctio {
/// Wrap a Rust function into AbleScript's built-in functio
///
/// Arity used for functio chaining, recommend value for variadic
/// functions is the accepted minimum.
pub fn new<F>(f: F, arity: usize) -> Self
where
F: Fn(&[ValueRef]) -> Result<(), crate::error::ErrorKind> + 'static,
{
Self {
function: Rc::new(f),
arity,
}
}
pub fn call(&self, args: &[ValueRef]) -> Result<(), crate::error::ErrorKind> {
(self.function)(args)
}
pub fn fn_addr(&self) -> usize {
Rc::as_ptr(&self.function) as *const () as _
}
}
impl std::fmt::Debug for BuiltinFunctio {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BuiltinFunctio")
.field("function", &"built-in")
.field("arity", &self.arity)
.finish()
}
}
impl PartialEq for BuiltinFunctio {
fn eq(&self, other: &Self) -> bool {
self.fn_addr() == other.fn_addr() && self.arity == other.arity
}
}
impl Hash for BuiltinFunctio {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.fn_addr().hash(state);
self.arity.hash(state);
}
}
/// A method of distributting parameters across functio chain
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum FunctioChainKind {
/// All parameters are equally distributed
Equal,
/// Parameters are distributed to members of chain
/// by their arity
ByArity,
}

202
ablescript/src/value/mod.rs Normal file
View file

@ -0,0 +1,202 @@
pub mod functio;
mod coercions;
mod ops;
pub use functio::Functio;
use std::{
cell::{Ref, RefCell, RefMut},
collections::HashMap,
fmt::Display,
hash::Hash,
io::Write,
mem::discriminant,
rc::Rc,
};
pub type Cart = HashMap<Value, ValueRef>;
/// AbleScript Value
#[derive(Debug, Default, Clone)]
pub enum Value {
#[default]
Nul,
Undefined,
Str(String),
Int(isize),
Abool(Abool),
Functio(Functio),
Cart(Cart),
}
impl Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
discriminant(self).hash(state);
match self {
Value::Nul | Value::Undefined => (),
Value::Str(v) => v.hash(state),
Value::Int(v) => v.hash(state),
Value::Abool(v) => v.to_string().hash(state),
Value::Functio(statements) => statements.hash(state),
Value::Cart(_) => self.to_string().hash(state),
}
}
}
impl Value {
/// Write an AbleScript value to a Brainfuck input stream by
/// coercing the value to an integer, then truncating that integer
/// to a single byte, then writing that byte. This should only be
/// called on `Write`rs that cannot fail, e.g., `Vec<u8>`, because
/// any IO errors will cause a panic.
pub fn bf_write(&self, stream: &mut impl Write) {
stream
.write_all(&[self.clone().into_isize() as u8])
.expect("Failed to write to Brainfuck input");
}
}
/// Three-state logic value
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
pub enum Abool {
Never = -1,
Sometimes = 0,
Always = 1,
}
impl Abool {
pub fn to_bool(&self) -> bool {
match self {
Self::Always => true,
Self::Sometimes if rand::random() => true,
_ => false,
}
}
}
impl Display for Abool {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Abool::Never => write!(f, "never"),
Abool::Sometimes => write!(f, "sometimes"),
Abool::Always => write!(f, "always"),
}
}
}
impl From<bool> for Abool {
fn from(b: bool) -> Self {
if b {
Abool::Always
} else {
Abool::Never
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Nul => write!(f, "nul"),
Value::Undefined => write!(f, "undefined"),
Value::Str(v) => write!(f, "{}", v),
Value::Int(v) => write!(f, "{}", v),
Value::Abool(v) => write!(f, "{}", v),
Value::Functio(v) => match v {
Functio::Bf {
instructions,
tape_len,
} => {
write!(
f,
"({}) {}",
tape_len,
String::from_utf8(instructions.to_owned())
.expect("Brainfuck functio source should be UTF-8")
)
}
Functio::Able { params, body } => {
write!(
f,
"({}) -> {:?}",
params.join(", "),
// Maybe we should have a pretty-printer for
// statement blocks at some point?
body,
)
}
Functio::Builtin(b) => write!(f, "builtin @ {}", b.fn_addr()),
Functio::Chain { functios, kind } => {
let (a, b) = *functios.clone();
write!(
f,
"{} {} {} ",
Value::Functio(a),
match kind {
functio::FunctioChainKind::Equal => '+',
functio::FunctioChainKind::ByArity => '*',
},
Value::Functio(b)
)
}
Functio::Eval(s) => write!(f, "{}", s),
},
Value::Cart(c) => {
write!(f, "[")?;
let mut cart_vec = c.iter().collect::<Vec<_>>();
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
for (idx, (key, value)) in cart_vec.into_iter().enumerate() {
write!(f, "{}", if idx != 0 { ", " } else { "" },)?;
match &*value.borrow() {
x if std::ptr::eq(x, self) => write!(f, "<cycle>"),
x => write!(f, "{x}"),
}?;
write!(f, " <= {key}")?;
}
write!(f, "]")
}
}
}
}
/// Runtime borrow-checked, counted reference to a [Value]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValueRef(Rc<RefCell<Value>>);
impl ValueRef {
pub fn new(v: Value) -> Self {
Self(Rc::new(RefCell::new(v)))
}
pub fn borrow(&self) -> Ref<Value> {
self.0.borrow()
}
pub fn borrow_mut(&self) -> RefMut<Value> {
self.0.borrow_mut()
}
pub fn replace(&self, v: Value) -> Value {
self.0.replace(v)
}
}
/// AbleScript variable either holding a reference
/// or being banned
#[derive(Debug)]
pub enum Variable {
/// Reference to a value
Ref(ValueRef),
/// Banned variable
Melo,
}
impl Variable {
pub fn from_value(value: Value) -> Self {
Self::Ref(ValueRef::new(value))
}
}

455
ablescript/src/value/ops.rs Normal file
View file

@ -0,0 +1,455 @@
use super::{
functio::{BuiltinFunctio, FunctioChainKind},
Abool, Functio, Value, ValueRef,
};
use crate::consts;
use rand::Rng;
use std::{
collections::HashMap,
ops::{Add, Div, Mul, Not, Sub},
};
impl Add for Value {
type Output = Value;
fn add(self, rhs: Self) -> Self::Output {
match self {
Value::Nul | Value::Undefined => match rhs {
Value::Nul => Value::Nul,
Value::Undefined => Value::Undefined,
Value::Str(_) => Value::Str(self.to_string()) + rhs,
Value::Int(_) => Value::Int(self.into_isize()) + rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) + rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) + rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) + rhs,
},
Value::Str(s) => Value::Str(format!("{s}{rhs}")),
Value::Int(i) => Value::Int(i.wrapping_add(rhs.into_isize())),
Value::Abool(_) => {
Value::Abool(Value::Int(self.into_isize().max(rhs.into_isize())).into_abool())
}
Value::Functio(f) => Value::Functio(Functio::Chain {
functios: Box::new((f, rhs.into_functio())),
kind: FunctioChainKind::Equal,
}),
Value::Cart(c) => {
Value::Cart(c.into_iter().chain(rhs.into_cart().into_iter()).collect())
}
}
}
}
impl Sub for Value {
type Output = Value;
fn sub(self, rhs: Self) -> Self::Output {
match self {
Value::Nul | Value::Undefined => match rhs {
Value::Nul => Value::Nul,
Value::Undefined => Value::Undefined,
Value::Str(_) => Value::Str(self.to_string()) - rhs,
Value::Int(_) => Value::Int(self.into_isize()) - rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) - rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) - rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) - rhs,
},
Value::Str(s) => Value::Str(s.replace(&rhs.to_string(), "")),
Value::Int(i) => Value::Int(i.wrapping_sub(rhs.into_isize())),
Value::Abool(_) => (self.clone() + rhs.clone()) * !(self * rhs),
Value::Functio(f) => Value::Functio(match f {
Functio::Bf {
instructions: lhs_ins,
tape_len: lhs_tl,
} => match rhs.into_functio() {
Functio::Bf {
instructions: rhs_ins,
tape_len: rhs_tl,
} => Functio::Bf {
instructions: lhs_ins
.into_iter()
.zip(rhs_ins.into_iter())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
tape_len: lhs_tl - rhs_tl,
},
rhs => Functio::Bf {
instructions: lhs_ins
.into_iter()
.zip(Value::Functio(rhs).to_string().bytes())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
tape_len: lhs_tl,
},
},
Functio::Able {
params: lhs_params,
body: lhs_body,
} => match rhs.into_functio() {
Functio::Able {
params: rhs_params,
body: rhs_body,
} => Functio::Able {
params: lhs_params
.into_iter()
.zip(rhs_params.into_iter())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
body: lhs_body
.into_iter()
.zip(rhs_body.into_iter())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
},
rhs => Value::Int(
Value::Functio(Functio::Able {
params: lhs_params,
body: lhs_body,
})
.into_isize()
- Value::Functio(rhs).into_isize(),
)
.into_functio(),
},
Functio::Builtin(b) => {
let arity = b.arity;
let resulting_arity = arity.saturating_sub(rhs.into_isize() as usize);
Functio::Builtin(BuiltinFunctio::new(
move |args| {
b.call(
&args
.iter()
.take(resulting_arity)
.cloned()
.chain(std::iter::repeat_with(|| ValueRef::new(Value::Nul)))
.take(arity)
.collect::<Vec<_>>(),
)
},
resulting_arity,
))
}
Functio::Chain { functios, .. } => {
let rhs = rhs.into_functio();
let (a, b) = *functios;
match (a == rhs, b == rhs) {
(_, true) => a,
(true, _) => b,
(_, _) => (Value::Functio(a) - Value::Functio(rhs)).into_functio(),
}
}
Functio::Eval(lhs_code) => Functio::Eval(lhs_code.replace(
&match rhs.into_functio() {
Functio::Eval(code) => code,
rhs => Value::Functio(rhs).to_string(),
},
"",
)),
}),
Value::Cart(c) => Value::Cart({
let rhs_cart = rhs.into_cart();
c.into_iter()
.filter(|(k, v)| rhs_cart.get(k) != Some(v))
.collect()
}),
}
}
}
impl Mul for Value {
type Output = Value;
fn mul(self, rhs: Self) -> Self::Output {
match self {
Value::Nul | Value::Undefined => match rhs {
Value::Nul => Value::Nul,
Value::Undefined => Value::Undefined,
Value::Str(_) => Value::Str(self.to_string()) * rhs,
Value::Int(_) => Value::Int(self.into_isize()) * rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) * rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) * rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) * rhs,
},
Value::Str(s) => Value::Str(s.repeat(rhs.into_isize() as usize)),
Value::Int(i) => Value::Int(i.wrapping_mul(rhs.into_isize())),
Value::Abool(_) => {
Value::Abool(Value::Int(self.into_isize().min(rhs.into_isize())).into_abool())
}
Value::Functio(f) => Value::Functio(Functio::Chain {
functios: Box::new((f, rhs.into_functio())),
kind: FunctioChainKind::ByArity,
}),
Value::Cart(c) => {
let rhsc = rhs.into_cart();
Value::Cart(
c.into_iter()
.map(|(k, v)| {
if let Some(k) = rhsc.get(&k) {
(k.borrow().clone(), v)
} else {
(k, v)
}
})
.collect(),
)
}
}
}
}
impl Div for Value {
type Output = Value;
fn div(self, rhs: Self) -> Self::Output {
match self {
Value::Nul | Value::Undefined => match rhs {
Value::Nul => Value::Nul,
Value::Undefined => Value::Undefined,
Value::Str(_) => Value::Str(self.to_string()) / rhs,
Value::Int(_) => Value::Int(self.into_isize()) / rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) / rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) / rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) / rhs,
},
Value::Str(s) => Value::Cart(
s.split(&rhs.to_string())
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as isize + 1),
ValueRef::new(Value::Str(x.to_owned())),
)
})
.collect(),
),
Value::Int(i) => Value::Int(i.wrapping_div(match rhs.into_isize() {
0 => consts::ANSWER,
x => x,
})),
Value::Abool(_) => !self + rhs,
Value::Functio(f) => Value::Functio(match f {
Functio::Bf {
instructions,
tape_len,
} => {
let fraction = 1.0 / rhs.into_isize() as f64;
let len = instructions.len();
Functio::Bf {
instructions: instructions
.into_iter()
.take((len as f64 * fraction) as usize)
.collect(),
tape_len,
}
}
Functio::Able { params, body } => {
let fraction = 1.0 / rhs.into_isize() as f64;
let len = body.len();
Functio::Able {
params,
body: body
.into_iter()
.take((len as f64 * fraction) as usize)
.collect(),
}
}
Functio::Builtin(b) => Functio::Builtin(BuiltinFunctio {
arity: b.arity + rhs.into_isize() as usize,
..b
}),
Functio::Chain { functios, kind } => {
let functios = *functios;
Functio::Chain {
functios: Box::new((
(Value::Functio(functios.0) / rhs.clone()).into_functio(),
(Value::Functio(functios.1) / rhs).into_functio(),
)),
kind,
}
}
Functio::Eval(s) => {
let fraction = 1.0 / rhs.into_isize() as f64;
let len = s.len();
Functio::Eval(s.chars().take((len as f64 * fraction) as usize).collect())
}
}),
Value::Cart(c) => {
let cart_len = match c.len() {
0 => return Value::Cart(HashMap::new()),
l => l,
};
let chunk_len = match rhs.into_isize() as usize {
0 => rand::thread_rng().gen_range(1..=cart_len),
l => l,
};
Value::Cart(
c.into_iter()
.collect::<Vec<_>>()
.chunks(cart_len / chunk_len + (cart_len % chunk_len != 0) as usize)
.enumerate()
.map(|(k, v)| {
(
Value::Int(k as isize + 1),
ValueRef::new(Value::Cart(v.iter().cloned().collect())),
)
})
.collect(),
)
}
}
}
}
impl Not for Value {
type Output = Value;
fn not(self) -> Self::Output {
match self {
Value::Nul => Value::Undefined,
Value::Undefined => Value::Nul,
Value::Str(s) => Value::Str(s.chars().rev().collect()),
Value::Int(i) => Value::Int(i.swap_bytes()),
Value::Abool(a) => Value::Abool(match a {
Abool::Never => Abool::Always,
Abool::Sometimes => Abool::Sometimes,
Abool::Always => Abool::Never,
}),
Value::Functio(f) => Value::Functio(match f {
Functio::Bf {
mut instructions,
tape_len,
} => {
instructions.reverse();
Functio::Bf {
instructions,
tape_len,
}
}
Functio::Able {
mut params,
mut body,
} => {
params.reverse();
body.reverse();
Functio::Able { params, body }
}
Functio::Builtin(b) => {
let arity = b.arity;
Functio::Builtin(BuiltinFunctio::new(
move |args| b.call(&args.iter().cloned().rev().collect::<Vec<_>>()),
arity,
))
}
Functio::Chain { functios, kind } => {
let (a, b) = *functios;
Functio::Chain {
functios: Box::new((
(!Value::Functio(b)).into_functio(),
(!Value::Functio(a)).into_functio(),
)),
kind,
}
}
Functio::Eval(code) => Functio::Eval(code.chars().rev().collect()),
}),
Value::Cart(c) => Value::Cart(
c.into_iter()
.map(|(k, v)| (v.borrow().clone(), ValueRef::new(k)))
.collect(),
),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
let other = other.clone();
match self {
Value::Nul => matches!(other, Value::Nul),
Value::Undefined => matches!(other, Value::Undefined),
Value::Str(s) => *s == other.to_string(),
Value::Int(i) => *i == other.into_isize(),
Value::Abool(a) => *a == other.into_abool(),
Value::Functio(f) => *f == other.into_functio(),
Value::Cart(c) => *c == other.into_cart(),
}
}
}
impl Eq for Value {}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering::*;
let other = other.clone();
match self {
Value::Nul if other == Value::Nul => Some(Equal),
Value::Undefined if other == Value::Undefined => Some(Equal),
Value::Str(s) => Some(s.cmp(&other.to_string())),
Value::Int(i) => Some(i.cmp(&other.into_isize())),
Value::Abool(a) => a.partial_cmp(&other.into_abool()),
Value::Functio(_) => self.clone().into_isize().partial_cmp(&other.into_isize()),
Value::Cart(c) => Some(c.len().cmp(&other.into_cart().len())),
Value::Nul | Value::Undefined => None,
}
}
}
impl Value {
/// Get a length of a value
pub fn length(&self) -> isize {
match self {
Value::Nul => 0,
Value::Undefined => -1,
Value::Str(s) => s.len() as _,
Value::Int(i) => i.count_zeros() as _,
Value::Abool(a) => match a {
Abool::Never => -3,
Abool::Sometimes => {
if rand::thread_rng().gen() {
3
} else {
-3
}
}
Abool::Always => 3,
},
Value::Functio(f) => match f {
// Compares lengths of functions:
// BfFunctio - Sum of lengths of instructions and length of tape
// AbleFunctio - Sum of argument count and body length
// Eval - Length of input code
Functio::Bf {
instructions,
tape_len,
} => (instructions.len() + tape_len) as _,
Functio::Able { params, body } => (params.len() + format!("{:?}", body).len()) as _,
Functio::Builtin(b) => (std::mem::size_of_val(b.function.as_ref()) + b.arity) as _,
Functio::Chain { functios, kind } => {
let (lhs, rhs) = *functios.clone();
match kind {
FunctioChainKind::Equal => {
Value::Int(Value::Functio(lhs).into_isize())
+ Value::Int(Value::Functio(rhs).into_isize())
}
FunctioChainKind::ByArity => {
Value::Int(Value::Functio(lhs).into_isize())
* Value::Int(Value::Functio(rhs).into_isize())
}
}
.into_isize()
}
Functio::Eval(s) => s.len() as _,
},
Value::Cart(c) => c.len() as _,
}
}
}

View file

@ -1,989 +0,0 @@
use std::{
cell::{Ref, RefCell, RefMut},
collections::HashMap,
fmt::Display,
hash::Hash,
io::Write,
mem::discriminant,
ops,
rc::Rc,
vec,
};
use rand::Rng;
use crate::{ast::Block, brian::INSTRUCTION_MAPPINGS, consts};
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Abool {
Never = -1,
Sometimes = 0,
Always = 1,
}
impl Abool {
pub fn to_bool(&self) -> bool {
match self {
Self::Always => true,
Self::Sometimes if rand::random() => true,
_ => false,
}
}
}
impl Display for Abool {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Abool::Never => write!(f, "never"),
Abool::Sometimes => write!(f, "sometimes"),
Abool::Always => write!(f, "always"),
}
}
}
impl From<bool> for Abool {
fn from(b: bool) -> Self {
if b {
Abool::Always
} else {
Abool::Never
}
}
}
#[derive(Debug, PartialEq, Clone, Hash)]
pub enum Functio {
Bf {
instructions: Vec<u8>,
tape_len: usize,
},
Able {
params: Vec<String>,
body: Block,
},
Builtin(BuiltinFunctio),
Chain {
functios: Box<(Functio, Functio)>,
kind: FunctioChainKind,
},
Eval(String),
}
impl Functio {
pub fn arity(&self) -> usize {
match self {
Functio::Bf {
instructions: _,
tape_len: _,
} => 0,
Functio::Able { params, body: _ } => params.len(),
Functio::Builtin(b) => b.arity,
Functio::Chain { functios, kind: _ } => functios.0.arity() + functios.1.arity(),
Functio::Eval(_) => 0,
}
}
}
#[derive(Clone)]
pub struct BuiltinFunctio {
function: Rc<dyn Fn(&[ValueRef]) -> Result<(), crate::error::ErrorKind>>,
arity: usize,
}
impl BuiltinFunctio {
pub fn new<F>(f: F, arity: usize) -> Self
where
F: Fn(&[ValueRef]) -> Result<(), crate::error::ErrorKind> + 'static,
{
Self {
function: Rc::new(f),
arity,
}
}
pub fn call(&self, args: &[ValueRef]) -> Result<(), crate::error::ErrorKind> {
(self.function)(args)
}
pub fn fn_addr(&self) -> usize {
Rc::as_ptr(&self.function) as *const () as _
}
}
impl std::fmt::Debug for BuiltinFunctio {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BuiltinFunctio")
.field("function", &"built-in")
.field("arity", &self.arity)
.finish()
}
}
impl PartialEq for BuiltinFunctio {
fn eq(&self, other: &Self) -> bool {
self.fn_addr() == other.fn_addr() && self.arity == other.arity
}
}
impl Hash for BuiltinFunctio {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.fn_addr().hash(state);
self.arity.hash(state);
}
}
#[derive(Debug, PartialEq, Copy, Clone, Hash)]
pub enum FunctioChainKind {
Equal,
ByArity,
}
pub type Cart = HashMap<Value, ValueRef>;
#[derive(Debug, Clone)]
pub enum Value {
Nul,
Str(String),
Int(isize),
Abool(Abool),
Functio(Functio),
Cart(Cart),
}
impl Default for Value {
fn default() -> Self {
Self::Nul
}
}
impl Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
discriminant(self).hash(state);
match self {
Value::Nul => (),
Value::Str(v) => v.hash(state),
Value::Int(v) => v.hash(state),
Value::Abool(v) => v.to_string().hash(state),
Value::Functio(statements) => statements.hash(state),
Value::Cart(_) => self.to_string().hash(state),
}
}
}
impl Value {
/// Write an AbleScript value to a Brainfuck input stream by
/// coercing the value to an integer, then truncating that integer
/// to a single byte, then writing that byte. This should only be
/// called on `Write`rs that cannot fail, e.g., `Vec<u8>`, because
/// any IO errors will cause a panic.
pub fn bf_write(&self, stream: &mut impl Write) {
stream
.write_all(&[self.clone().into_isize() as u8])
.expect("Failed to write to Brainfuck input");
}
/// Coerce a value to an integer.
pub fn into_isize(self) -> isize {
match self {
Value::Abool(a) => a as _,
Value::Functio(f) => match f {
Functio::Bf {
instructions,
tape_len,
} => {
instructions.into_iter().map(|x| x as isize).sum::<isize>() * tape_len as isize
}
Functio::Able { params, body } => {
params
.into_iter()
.map(|x| x.bytes().map(|x| x as isize).sum::<isize>())
.sum::<isize>()
+ body.len() as isize
}
Functio::Builtin(b) => (b.fn_addr() + b.arity) as _,
Functio::Chain { functios, kind } => {
let (lf, rf) = *functios;
Value::Functio(lf).into_isize()
+ Value::Functio(rf).into_isize()
* match kind {
FunctioChainKind::Equal => -1,
FunctioChainKind::ByArity => 1,
}
}
Functio::Eval(code) => code.bytes().map(|x| x as isize).sum(),
},
Value::Int(i) => i,
Value::Nul => consts::ANSWER,
Value::Str(text) => text.parse().unwrap_or(consts::ANSWER),
Value::Cart(c) => c
.into_iter()
.map(|(i, v)| i.into_isize() * v.borrow().clone().into_isize())
.sum(),
}
}
/// Coerce a value to an aboolean.
pub fn into_abool(self) -> Abool {
match self {
Value::Nul => Abool::Never,
Value::Str(s) => match s.to_lowercase().as_str() {
"never" | "no" | "🇳🇴" => Abool::Never,
"sometimes" => Abool::Sometimes,
"always" | "yes" => Abool::Always,
s => (!s.is_empty()).into(),
},
Value::Int(x) => match x.cmp(&0) {
std::cmp::Ordering::Less => Abool::Never,
std::cmp::Ordering::Equal => Abool::Sometimes,
std::cmp::Ordering::Greater => Abool::Always,
},
Value::Abool(a) => a,
Value::Functio(f) => match f {
Functio::Bf {
instructions,
tape_len,
} => Value::Int(
(instructions.iter().map(|x| *x as usize).sum::<usize>() * tape_len) as _,
)
.into_abool(),
Functio::Able { params, body } => {
let str_to_isize =
|x: String| -> isize { x.as_bytes().iter().map(|x| *x as isize).sum() };
let params: isize = params.into_iter().map(str_to_isize).sum();
let body: isize = body
.into_iter()
.map(|x| format!("{:?}", x))
.map(str_to_isize)
.sum();
Value::Int((params + body) % 3 - 1).into_abool()
}
Functio::Builtin(b) => (b.fn_addr() % b.arity == 0).into(),
Functio::Chain { functios, kind } => {
let (lhs, rhs) = *functios;
match kind {
FunctioChainKind::Equal => {
Value::Abool(Value::Functio(lhs).into_abool())
+ Value::Abool(Value::Functio(rhs).into_abool())
}
FunctioChainKind::ByArity => {
Value::Abool(Value::Functio(lhs).into_abool())
* Value::Abool(Value::Functio(rhs).into_abool())
}
}
.into_abool()
}
Functio::Eval(code) => Value::Str(code).into_abool(),
},
Value::Cart(c) => {
if c.is_empty() {
Abool::Never
} else {
Abool::Always
}
}
}
}
/// Coerce a value to a functio.
pub fn into_functio(self) -> Functio {
match self {
Value::Nul => Functio::Able {
body: vec![],
params: vec![],
},
Value::Str(s) => Functio::Eval(s),
Value::Int(i) => Functio::Bf {
instructions: {
std::iter::successors(Some(i as usize), |i| {
Some(i / INSTRUCTION_MAPPINGS.len())
})
.take_while(|&i| i != 0)
.map(|i| INSTRUCTION_MAPPINGS[i % INSTRUCTION_MAPPINGS.len()])
.collect()
},
tape_len: crate::brian::DEFAULT_TAPE_SIZE_LIMIT,
},
Value::Abool(a) => Functio::Eval(match a {
Abool::Never => "".to_owned(),
Abool::Sometimes => {
use rand::seq::SliceRandom;
let mut str_chars: Vec<_> = "Buy Able Products!".chars().collect();
str_chars.shuffle(&mut rand::thread_rng());
format!(r#""{}"print;"#, str_chars.iter().collect::<String>())
}
Abool::Always => r#"loop{"Buy Able products!"print;}"#.to_owned(),
}),
Value::Functio(f) => f,
Value::Cart(c) => {
let kind = if let Some(114514) = c
.get(&Value::Str("1452251871514141792252515212116".to_owned()))
.map(|x| x.borrow().to_owned().into_isize())
{
FunctioChainKind::Equal
} else {
FunctioChainKind::ByArity
};
let mut cart_vec = c.iter().collect::<Vec<_>>();
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
cart_vec
.into_iter()
.map(|(_, x)| x.borrow().to_owned().into_functio())
.reduce(|acc, x| Functio::Chain {
functios: Box::new((acc, x)),
kind,
})
.unwrap_or_else(|| Functio::Eval(r#""Buy Able Products!"print;"#.to_owned()))
}
}
}
/// Coerce a value into a cart.
pub fn into_cart(self) -> Cart {
match self {
Value::Nul => HashMap::new(),
Value::Str(s) => s
.chars()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as isize + 1),
ValueRef::new(Value::Str(x.to_string())),
)
})
.collect(),
Value::Int(i) => Value::Str(i.to_string()).into_cart(),
Value::Abool(a) => Value::Str(a.to_string()).into_cart(),
Value::Functio(f) => match f {
Functio::Able { params, body } => {
let params: Cart = params
.into_iter()
.enumerate()
.map(|(i, x)| (Value::Int(i as isize + 1), ValueRef::new(Value::Str(x))))
.collect();
let body: Cart = body
.into_iter()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as isize + 1),
ValueRef::new(Value::Str(format!("{:?}", x))),
)
})
.collect();
let mut cart = HashMap::new();
cart.insert(
Value::Str("params".to_owned()),
ValueRef::new(Value::Cart(params)),
);
cart.insert(
Value::Str("body".to_owned()),
ValueRef::new(Value::Cart(body)),
);
cart
}
Functio::Bf {
instructions,
tape_len,
} => {
let mut cart: Cart = instructions
.into_iter()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as isize + 1),
ValueRef::new(
char::from_u32(x as u32)
.map(|x| Value::Str(x.to_string()))
.unwrap_or(Value::Nul),
),
)
})
.collect();
cart.insert(
Value::Str("tapelen".to_owned()),
ValueRef::new(Value::Int(tape_len as _)),
);
cart
}
Functio::Builtin(b) => {
let mut cart = HashMap::new();
cart.insert(
Value::Str("addr".to_owned()),
ValueRef::new(Value::Cart(Value::Int(b.fn_addr() as _).into_cart())),
);
cart.insert(
Value::Str("arity".to_owned()),
ValueRef::new(Value::Int(b.arity as _)),
);
cart
}
Functio::Chain { functios, kind } => {
let (lhs, rhs) = *functios;
match kind {
FunctioChainKind::Equal => {
Value::Cart(Value::Functio(lhs).into_cart())
+ Value::Cart(Value::Functio(rhs).into_cart())
}
FunctioChainKind::ByArity => {
Value::Cart(Value::Functio(lhs).into_cart())
* Value::Cart(Value::Functio(rhs).into_cart())
}
}
.into_cart()
}
Functio::Eval(s) => Value::Str(s).into_cart(),
},
Value::Cart(c) => c,
}
}
/// Get a length of a value
pub fn length(&self) -> isize {
match self {
Value::Nul => 0,
Value::Str(s) => s.len() as _,
Value::Int(i) => i.count_zeros() as _,
Value::Abool(a) => match a {
Abool::Never => -3,
Abool::Sometimes => {
if rand::thread_rng().gen() {
3
} else {
-3
}
}
Abool::Always => 3,
},
Value::Functio(f) => match f {
// Compares lengths of functions:
// BfFunctio - Sum of lengths of instructions and length of tape
// AbleFunctio - Sum of argument count and body length
// Eval - Length of input code
Functio::Bf {
instructions,
tape_len,
} => (instructions.len() + tape_len) as _,
Functio::Able { params, body } => (params.len() + format!("{:?}", body).len()) as _,
Functio::Builtin(b) => (std::mem::size_of_val(b.function.as_ref()) + b.arity) as _,
Functio::Chain { functios, kind } => {
let (lhs, rhs) = *functios.clone();
match kind {
FunctioChainKind::Equal => {
Value::Int(Value::Functio(lhs).into_isize())
+ Value::Int(Value::Functio(rhs).into_isize())
}
FunctioChainKind::ByArity => {
Value::Int(Value::Functio(lhs).into_isize())
* Value::Int(Value::Functio(rhs).into_isize())
}
}
.into_isize()
}
Functio::Eval(s) => s.len() as _,
},
Value::Cart(c) => c.len() as _,
}
}
}
impl ops::Add for Value {
type Output = Value;
fn add(self, rhs: Self) -> Self::Output {
match self {
Value::Nul => match rhs {
Value::Nul => Value::Nul,
Value::Str(_) => Value::Str(self.to_string()) + rhs,
Value::Int(_) => Value::Int(self.into_isize()) + rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) + rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) + rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) + rhs,
},
Value::Str(s) => Value::Str(format!("{s}{rhs}")),
Value::Int(i) => Value::Int(i.wrapping_add(rhs.into_isize())),
Value::Abool(_) => {
Value::Abool(Value::Int(self.into_isize().max(rhs.into_isize())).into_abool())
}
Value::Functio(f) => Value::Functio(Functio::Chain {
functios: Box::new((f, rhs.into_functio())),
kind: FunctioChainKind::Equal,
}),
Value::Cart(c) => {
Value::Cart(c.into_iter().chain(rhs.into_cart().into_iter()).collect())
}
}
}
}
impl ops::Sub for Value {
type Output = Value;
fn sub(self, rhs: Self) -> Self::Output {
match self {
Value::Nul => match rhs {
Value::Nul => Value::Nul,
Value::Str(_) => Value::Str(self.to_string()) - rhs,
Value::Int(_) => Value::Int(self.into_isize()) - rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) - rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) - rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) - rhs,
},
Value::Str(s) => Value::Str(s.replace(&rhs.to_string(), "")),
Value::Int(i) => Value::Int(i.wrapping_sub(rhs.into_isize())),
Value::Abool(_) => (self.clone() + rhs.clone()) * !(self * rhs),
Value::Functio(f) => Value::Functio(match f {
Functio::Bf {
instructions: lhs_ins,
tape_len: lhs_tl,
} => match rhs.into_functio() {
Functio::Bf {
instructions: rhs_ins,
tape_len: rhs_tl,
} => Functio::Bf {
instructions: lhs_ins
.into_iter()
.zip(rhs_ins.into_iter())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
tape_len: lhs_tl - rhs_tl,
},
rhs => Functio::Bf {
instructions: lhs_ins
.into_iter()
.zip(Value::Functio(rhs).to_string().bytes())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
tape_len: lhs_tl,
},
},
Functio::Able {
params: lhs_params,
body: lhs_body,
} => match rhs.into_functio() {
Functio::Able {
params: rhs_params,
body: rhs_body,
} => Functio::Able {
params: lhs_params
.into_iter()
.zip(rhs_params.into_iter())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
body: lhs_body
.into_iter()
.zip(rhs_body.into_iter())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
},
rhs => Value::Int(
Value::Functio(Functio::Able {
params: lhs_params,
body: lhs_body,
})
.into_isize()
- Value::Functio(rhs).into_isize(),
)
.into_functio(),
},
Functio::Builtin(b) => {
let arity = b.arity;
let resulting_arity = arity.saturating_sub(rhs.into_isize() as usize);
Functio::Builtin(BuiltinFunctio::new(
move |args| {
b.call(
&args
.iter()
.take(resulting_arity)
.cloned()
.chain(std::iter::repeat_with(|| ValueRef::new(Value::Nul)))
.take(arity)
.collect::<Vec<_>>(),
)
},
resulting_arity,
))
}
Functio::Chain { functios, .. } => {
let rhs = rhs.into_functio();
let (a, b) = *functios;
match (a == rhs, b == rhs) {
(_, true) => a,
(true, _) => b,
(_, _) => (Value::Functio(a) - Value::Functio(rhs)).into_functio(),
}
}
Functio::Eval(lhs_code) => Functio::Eval(lhs_code.replace(
&match rhs.into_functio() {
Functio::Eval(code) => code,
rhs => Value::Functio(rhs).to_string(),
},
"",
)),
}),
Value::Cart(c) => Value::Cart({
let rhs_cart = rhs.into_cart();
c.into_iter()
.filter(|(k, v)| rhs_cart.get(k) != Some(v))
.collect()
}),
}
}
}
impl ops::Mul for Value {
type Output = Value;
fn mul(self, rhs: Self) -> Self::Output {
match self {
Value::Nul => match rhs {
Value::Nul => Value::Nul,
Value::Str(_) => Value::Str(self.to_string()) * rhs,
Value::Int(_) => Value::Int(self.into_isize()) * rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) * rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) * rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) * rhs,
},
Value::Str(s) => Value::Str(s.repeat(rhs.into_isize() as usize)),
Value::Int(i) => Value::Int(i.wrapping_mul(rhs.into_isize())),
Value::Abool(_) => {
Value::Abool(Value::Int(self.into_isize().min(rhs.into_isize())).into_abool())
}
Value::Functio(f) => Value::Functio(Functio::Chain {
functios: Box::new((f, rhs.into_functio())),
kind: FunctioChainKind::ByArity,
}),
Value::Cart(c) => {
let rhsc = rhs.into_cart();
Value::Cart(
c.into_iter()
.map(|(k, v)| {
if let Some(k) = rhsc.get(&k) {
(k.borrow().clone(), v)
} else {
(k, v)
}
})
.collect(),
)
}
}
}
}
impl ops::Div for Value {
type Output = Value;
fn div(self, rhs: Self) -> Self::Output {
match self {
Value::Nul => match rhs {
Value::Nul => Value::Nul,
Value::Str(_) => Value::Str(self.to_string()) / rhs,
Value::Int(_) => Value::Int(self.into_isize()) / rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) / rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) / rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) / rhs,
},
Value::Str(s) => Value::Cart(
s.split(&rhs.to_string())
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as isize + 1),
ValueRef::new(Value::Str(x.to_owned())),
)
})
.collect(),
),
Value::Int(i) => Value::Int(i.wrapping_div(match rhs.into_isize() {
0 => consts::ANSWER,
x => x,
})),
Value::Abool(_) => !self + rhs,
Value::Functio(f) => Value::Functio(match f {
Functio::Bf {
instructions,
tape_len,
} => {
let fraction = 1.0 / rhs.into_isize() as f64;
let len = instructions.len();
Functio::Bf {
instructions: instructions
.into_iter()
.take((len as f64 * fraction) as usize)
.collect(),
tape_len,
}
}
Functio::Able { params, body } => {
let fraction = 1.0 / rhs.into_isize() as f64;
let len = body.len();
Functio::Able {
params,
body: body
.into_iter()
.take((len as f64 * fraction) as usize)
.collect(),
}
}
Functio::Builtin(b) => Functio::Builtin(BuiltinFunctio {
arity: b.arity + rhs.into_isize() as usize,
..b
}),
Functio::Chain { functios, kind } => {
let functios = *functios;
Functio::Chain {
functios: Box::new((
(Value::Functio(functios.0) / rhs.clone()).into_functio(),
(Value::Functio(functios.1) / rhs).into_functio(),
)),
kind,
}
}
Functio::Eval(s) => {
let fraction = 1.0 / rhs.into_isize() as f64;
let len = s.len();
Functio::Eval(s.chars().take((len as f64 * fraction) as usize).collect())
}
}),
Value::Cart(c) => {
let cart_len = c.len();
let chunk_len = rhs.into_isize() as usize;
Value::Cart(
c.into_iter()
.collect::<Vec<_>>()
.chunks(cart_len / chunk_len + (cart_len % chunk_len != 0) as usize)
.enumerate()
.map(|(k, v)| {
(
Value::Int(k as isize + 1),
ValueRef::new(Value::Cart(v.iter().cloned().collect())),
)
})
.collect(),
)
}
}
}
}
impl ops::Not for Value {
type Output = Value;
fn not(self) -> Self::Output {
match self {
Value::Nul => Value::Nul,
Value::Str(s) => Value::Str(s.chars().rev().collect()),
Value::Int(i) => Value::Int(i.swap_bytes()),
Value::Abool(a) => Value::Abool(match a {
Abool::Never => Abool::Always,
Abool::Sometimes => Abool::Sometimes,
Abool::Always => Abool::Never,
}),
Value::Functio(f) => Value::Functio(match f {
Functio::Bf {
mut instructions,
tape_len,
} => {
instructions.reverse();
Functio::Bf {
instructions,
tape_len,
}
}
Functio::Able {
mut params,
mut body,
} => {
params.reverse();
body.reverse();
Functio::Able { params, body }
}
Functio::Builtin(b) => {
let arity = b.arity;
Functio::Builtin(BuiltinFunctio::new(
move |args| b.call(&args.iter().cloned().rev().collect::<Vec<_>>()),
arity,
))
}
Functio::Chain { functios, kind } => {
let (a, b) = *functios;
Functio::Chain {
functios: Box::new((
(!Value::Functio(b)).into_functio(),
(!Value::Functio(a)).into_functio(),
)),
kind,
}
}
Functio::Eval(code) => Functio::Eval(code.chars().rev().collect()),
}),
Value::Cart(c) => Value::Cart(
c.into_iter()
.map(|(k, v)| (v.borrow().clone(), ValueRef::new(k)))
.collect(),
),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
let other = other.clone();
match self {
Value::Nul => matches!(other, Value::Nul),
Value::Str(s) => *s == other.to_string(),
Value::Int(i) => *i == other.into_isize(),
Value::Abool(a) => *a == other.into_abool(),
Value::Functio(f) => *f == other.into_functio(),
Value::Cart(c) => *c == other.into_cart(),
}
}
}
impl Eq for Value {}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering::*;
let other = other.clone();
match self {
Value::Nul => {
if other == Value::Nul {
Some(Equal)
} else {
None
}
}
Value::Str(s) => Some(s.cmp(&other.to_string())),
Value::Int(i) => Some(i.cmp(&other.into_isize())),
Value::Abool(a) => a.partial_cmp(&other.into_abool()),
Value::Functio(_) => self.clone().into_isize().partial_cmp(&other.into_isize()),
Value::Cart(c) => Some(c.len().cmp(&other.into_cart().len())),
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Nul => write!(f, "nul"),
Value::Str(v) => write!(f, "{}", v),
Value::Int(v) => write!(f, "{}", v),
Value::Abool(v) => write!(f, "{}", v),
Value::Functio(v) => match v {
Functio::Bf {
instructions,
tape_len,
} => {
write!(
f,
"({}) {}",
tape_len,
String::from_utf8(instructions.to_owned())
.expect("Brainfuck functio source should be UTF-8")
)
}
Functio::Able { params, body } => {
write!(
f,
"({}) -> {:?}",
params.join(", "),
// Maybe we should have a pretty-printer for
// statement blocks at some point?
body,
)
}
Functio::Builtin(b) => write!(f, "builtin @ {}", b.fn_addr()),
Functio::Chain { functios, kind } => {
let (a, b) = *functios.clone();
write!(
f,
"{} {} {} ",
Value::Functio(a),
match kind {
FunctioChainKind::Equal => '+',
FunctioChainKind::ByArity => '*',
},
Value::Functio(b)
)
}
Functio::Eval(s) => write!(f, "{}", s),
},
Value::Cart(c) => {
write!(f, "[")?;
let mut cart_vec = c.iter().collect::<Vec<_>>();
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
for (idx, (key, value)) in cart_vec.into_iter().enumerate() {
write!(
f,
"{}{} <= {}",
if idx != 0 { ", " } else { "" },
value.borrow(),
key
)?;
}
write!(f, "]")
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ValueRef(Rc<RefCell<Value>>);
impl ValueRef {
pub fn new(v: Value) -> Self {
Self(Rc::new(RefCell::new(v)))
}
pub fn borrow(&self) -> Ref<Value> {
self.0.borrow()
}
pub fn borrow_mut(&self) -> RefMut<Value> {
self.0.borrow_mut()
}
pub fn replace(&self, v: Value) -> Value {
self.0.replace(v)
}
}
#[derive(Debug)]
pub struct Variable {
pub melo: bool,
// Multiple Variables can reference the same underlying Value when
// pass-by-reference is used, therefore we use Rc here.
pub value: ValueRef,
}
impl Variable {
pub fn from_value(value: Value) -> Self {
Self {
melo: false,
value: ValueRef::new(value),
}
}
}

View file

@ -1,18 +1,15 @@
[package]
name = "ablescript_cli"
version = "0.3.0"
authors = ["able <abl3theabove@gmail.com>"]
version = "0.5.4"
authors = ["AbleScript Developers"]
edition = "2021"
description = "The best programming language"
license = "MIT"
documentation = "https://ablecorp.us/able-script-the-book/"
repository = "https://github.com/AbleCorp/able-script"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
description = "The best programming language"
license = "MIT"
documentation = "https://gblecorp.github.io/able-script-the-book"
repository = "https://git.ablecorp.us/AbleScript/able-script"
[dependencies]
ablescript = { version = "0.3.0", path = "../ablescript" }
clap = "3.1"
rustyline = "9.1"
ablescript = { version = "0.5.3", path = "../ablescript" }
clap = { version = "4.2", features = ["derive"] }
rustyline = "11.0"

View file

@ -1,52 +1,31 @@
#![forbid(unsafe_code, clippy::unwrap_used)]
#![forbid(unsafe_code)]
mod repl;
use ablescript::interpret::ExecEnv;
use ablescript::parser::parse;
use clap::{Arg, Command};
use std::process::exit;
use ablescript::{interpret::ExecEnv, parser::parse};
use clap::Parser;
use std::{path::PathBuf, process::exit};
fn main() {
// variables::test(); // NOTE(Able): Add this as a test case
let matches = Command::new("AbleScript")
.version(env!("CARGO_PKG_VERSION"))
.author("Able <abl3theabove@gmail.com>")
.about("AbleScript interpreter")
.arg(
Arg::new("file")
.short('f')
.long("file")
.value_name("FILE")
.help("Set the path to interpret from")
.takes_value(true),
)
.arg(
Arg::new("debug")
.long("debug")
.help("Enable debug AST printing"),
)
.get_matches();
let ast_print = matches.is_present("debug");
match matches.value_of("file") {
let args = Args::parse();
match args.file {
Some(file_path) => {
// Read file
let source = match std::fs::read_to_string(file_path) {
let source = match std::fs::read_to_string(&file_path) {
Ok(s) => s,
Err(e) => {
println!("Failed to read file \"{}\": {}", file_path, e);
println!("Failed to read file \"{:?}\": {}", file_path, e);
exit(1)
}
};
// Parse & evaluate
if let Err(e) = parse(&source).and_then(|ast| {
if ast_print {
println!("{:#?}", ast);
if args.debug {
eprintln!("{:#?}", ast);
}
ExecEnv::new().eval_stmts(&ast)
ExecEnv::<ablescript::host_interface::Standard>::default().eval_stmts(&ast)
}) {
println!(
"Error `{:?}` occurred at span: {:?} = `{:?}`",
@ -58,7 +37,19 @@ fn main() {
}
None => {
println!("Hi [AbleScript {}]", env!("CARGO_PKG_VERSION"));
repl::repl(ast_print);
repl::repl(args.debug);
}
}
}
#[derive(Parser, Debug)]
#[command(name = "AbleScript", version, about)]
struct Args {
/// File to execute
#[arg(short, long)]
file: Option<PathBuf>,
/// Dump AST to console
#[arg(short, long)]
debug: bool,
}

View file

@ -1,10 +1,16 @@
use ablescript::interpret::ExecEnv;
use ablescript::parser::parse;
use rustyline::Editor;
use ablescript::{interpret::ExecEnv, parser::parse};
use rustyline::DefaultEditor;
pub fn repl(ast_print: bool) {
let mut rl = Editor::<()>::new();
let mut env = ExecEnv::new();
let mut rl = match DefaultEditor::new() {
Ok(rl) => rl,
Err(e) => {
eprintln!("Failed to create editor: {e}");
std::process::exit(-1);
}
};
let mut env = ExecEnv::<ablescript::host_interface::Standard>::default();
// If this is `Some`, the user has previously entered an
// incomplete statement and is now completing it; otherwise, the
@ -14,7 +20,7 @@ pub fn repl(ast_print: bool) {
match rl.readline(if partial.is_some() { ">> " } else { ":: " }) {
Ok(readline) => {
let readline = readline.trim_end();
rl.add_history_entry(readline);
let _ = rl.add_history_entry(readline);
let partial_data = match partial {
Some(line) => line + readline,
@ -23,7 +29,7 @@ pub fn repl(ast_print: bool) {
partial = match parse(&partial_data).and_then(|ast| {
if ast_print {
println!("{:#?}", &ast);
eprintln!("{:#?}", &ast);
}
env.eval_stmts(&ast)
}) {
@ -31,7 +37,7 @@ pub fn repl(ast_print: bool) {
Err(ablescript::error::Error {
// Treat "Unexpected EOF" errors as "we need
// more data".
kind: ablescript::error::ErrorKind::UnexpectedEof,
kind: ablescript::error::ErrorKind::UnexpectedEoi,
..
}) => Some(partial_data),
Err(e) => {

View file

@ -25,20 +25,20 @@ owo arity_1(/*foo*/);
owo arity_2(/*foo*/, /*bar*/);
owo arity_3(/*foo*/, /*bar*/, /*baz*/);
dim i1 arity_0 * arity_1;
i1 dim arity_0 * arity_1;
i1(/*second*/);
/*----*/ print;
dim i2 arity_1 * arity_0;
i2 dim arity_1 * arity_0;
i2(/*first*/);
/*----*/ print;
dim ifancy arity_3 * arity_3;
ifancy dim arity_3 * arity_3;
ifancy(/*left1*/, /*right1*/, /*left2*/, /*right2*/, /*left3*/, /*right3*/);
/*---*/ print;
dim another arity_0 * arity_3;
another dim arity_0 * arity_3;
another(/*right1*/, /*right2*/, /*right3*/);

View file

@ -2,7 +2,7 @@ functio helloable() {
/*Hello, Able!*/ print;
}
dim cart [/*able*/ <= 42, helloable <= /*hello*/];
cart dim [/*able*/ <= 42, helloable <= /*hello*/];
cart[42] print;
cart[/*hello*/]();

View file

@ -1,2 +1,2 @@
dim hello /*world*/;
hello dim /*world*/;
hello print;

View file

@ -1,4 +1,4 @@
dim data;
data dim;
loop {
data read;
data print;

View file

@ -1,3 +1,3 @@
dim hi /*wonk*/;
hi dim /*wonk*/;
melo hi;
hi print; owo Should error out

View file

@ -2,13 +2,13 @@ owo Pass-by-reference test
owo Swap two variables.
functio swap(left, right) {
dim tmp left;
tmp dim left;
right =: left;
tmp =: right;
}
dim foo /*hello*/;
dim bar /*world*/;
foo dim /*hello*/;
bar dim /*world*/;
swap(foo, bar);