212 Commits

Author SHA1 Message Date
55e02fd919 conlang: updated rustfmt 2025-10-24 05:23:53 -04:00
4e4c61ee4f repline: Goodbye, old friend. You have been promoted. 2025-10-24 03:44:11 -04:00
8d641d0060 cl-repl: Flatten menu structure so new friendo won't have to ^C^C
Plus it was more annoying than this is.
2025-10-23 06:06:56 -04:00
06ed0eae54 repline: Make begin and again post-customizable, and once again fall out of sync 2025-10-23 06:05:24 -04:00
e0eb0d5a02 cl-interpret: add type metadata to tuple variants 2025-10-23 05:05:34 -04:00
d62656c615 interpret: Properly scope enum variants! 2025-10-22 14:55:49 -04:00
acc3ed12b3 fstring.cl: update for conlang-ng 2025-10-22 13:13:09 -04:00
722036dfbb sample-code: link-the-docs using Modern™️ terminal escape sequences 2025-10-22 13:12:55 -04:00
4a2cd9303e repline: stop clobbering the input buffer every time you can't figure out an escape sequence 2025-10-22 13:11:41 -04:00
35edfdbb17 Add config.toml so other people can build 2025-10-19 19:29:24 -04:00
b54826cdd5 sample-code: update for new WIP syntax (it's more persnickety) 2025-10-19 19:27:09 -04:00
6b24980fc7 engine: borrow the bset and rset instead of RCing them 2025-10-19 18:56:32 -04:00
55324af358 populate: use visitor children, and don't mark generics in impls yet. TODO: this is nonsense. 2025-10-19 18:55:50 -04:00
df9973b119 cl-interpret: Change struct layout, add rudimentary operator overloading 2025-09-29 11:48:30 -04:00
f41e5fc49a conlang: add lang items, remove Empty, and shuffle typeck 2025-09-15 10:45:14 -04:00
ead1f351a7 conlang-run: Add Display-formatted errors here too. 2025-09-15 03:54:24 -04:00
986bac9e6b sample-code/calculator: Bit-shifts and better formatting 2025-09-15 00:24:02 -04:00
02239c5ce4 sample-code/ascii: fix reference depth for postfix-call in_range function 2025-09-15 00:22:38 -04:00
1f9d32f972 cl-lexer: Make strings curly-brace-aware, for future format string work 2025-09-15 00:21:14 -04:00
8dd2920fca cl-repl/cli: Pretty-print errors in non-repl mode 2025-09-15 00:20:05 -04:00
df6089f84a cl-interpret: Print function call backtrace on panic 2025-09-15 00:18:51 -04:00
62940b3d24 conlang: More clippy 2025-09-15 00:17:50 -04:00
1fe796dda7 cl-lexer: Bring over new lexer
- Different flow, similar action
- Consolidated all the single-purpose di- and trigraph functions
- Gave the lexer explicit access to its entire string, which is can slice.
2025-09-14 23:02:04 -04:00
f0c871711c compiler: updated to rust 1.84, now we have let chains! 2025-09-14 19:08:59 -04:00
fcab20579a cl-structures/intern: Debug-print interned objects with custom sigil 2025-09-14 19:02:55 -04:00
239785b322 cl-interpret: Literal[String] a la python 2025-07-20 15:57:11 -04:00
259c9f8bb6 repline: update Crossterm dependency to 0.29.0 2025-07-18 05:40:20 -04:00
c9ffeaddce conlang: bump version number to 0.0.10 2025-07-18 05:37:23 -04:00
12daf35c07 .gitignore: Ignore cl-typeck table dumpfile 2025-07-18 05:37:09 -04:00
6a0607b93a sample-code: Add a super hacky format-string implementation using eval and fmt builtins 2025-07-18 05:36:38 -04:00
4f40bd4f99 sample-code: Remove "Student" function from match_test 2025-07-18 05:35:57 -04:00
2f94ddd23f repline: QoL improvement: inserting newline works! 2025-07-18 05:35:00 -04:00
0f9044bb3e stdlib: Inference engine caught some type errors! Also added some hot garbage. 2025-07-18 05:34:03 -04:00
8732cca3f9 cl-typeck: Get some serious type inference going! 2025-07-18 05:30:23 -04:00
74220d3bff cl-lexer: Add base-36 literals, lmao 2025-07-18 05:29:31 -04:00
8b0a122dfc cl-interpret: Environment/stack overhaul + Ref patterns 2025-07-18 05:29:10 -04:00
e165e029dc cl-embed: Calculator example update! 2025-07-18 05:26:39 -04:00
148ef34a01 ast: add gens for ty and impl, raw ptr types, make fn return value non-optional 2025-07-18 05:25:35 -04:00
6ba62ac1c4 repline: Refactor for fewer redraws and more user control.
Definitely has bugs, but eh!
2025-06-17 00:43:21 -04:00
ae026420f1 conlang: Fix rustdoc warnings 2025-06-17 00:43:21 -04:00
d80f2f6315 cl-embed/examples: rename conculator back to calculator, since MI searches by path 2025-05-19 01:23:23 +00:00
89ed9b2a39 test.cl: Never-like enums tempermanently(?) removed 2025-05-18 11:51:22 -04:00
47608668fa cl-embed: Add an example, and a new sample-code (same file) 2025-05-18 11:50:33 -04:00
6ce27c522f cl-embed: Create an (unstable) API for embedding Conlang in your projects! 2025-05-18 11:49:43 -04:00
233e4dab4e visit: docs cleanup 2025-05-18 11:48:22 -04:00
f95c6ee239 cl-interpret: Add convenience function for binding variables 2025-05-18 11:48:04 -04:00
964917d2f0 cl-interpret: let now returns bool
This makes debugging monumentally harder, but it's SO NEAT and instantly adds `if let`/`while let` and `let chaining`
2025-05-18 11:47:13 -04:00
6bb855cff7 cl-parser: semantics changes
allow let in conditionals (restricts init of let to non-assignment)

allow semicolons after items at file scope
2025-05-18 11:44:44 -04:00
124bb2f680 cl-interpret: Builtins! builtins(), chars(str), and panic(msg) 2025-05-18 11:42:33 -04:00
08b5937fc2 cl-typeck: Twiddle with infer (maybe this will help fix the weird behavior?) 2025-05-18 11:39:54 -04:00
ccfa4c7723 cl-interpret: copy-capture closures 2025-05-18 11:30:17 -04:00
d3e20e53d0 cl-interpret: Unit tests for while-else control flow 2025-05-18 04:01:50 -04:00
e08bf57dc1 cl-typeck: Give inference its own result type 2025-05-18 04:01:13 -04:00
a5590168ee cl-parser: Misc cleanup 2025-05-18 04:00:43 -04:00
3e2063835b cl-parser: Dedicated parsing logic for patterns! 2025-05-18 04:00:00 -04:00
e6156343c3 cl-ast: Add inline closure expressions 2025-05-18 03:57:20 -04:00
6c6d2d04a7 cl-ast: Remove variantkind, as it was redundant 2025-05-17 21:28:12 -04:00
a023551d9f repline: Change newline behavior when not at end of input
TODO: This screws with the naming convention of home()/end()
2025-05-17 20:32:24 -04:00
dc1c9bdd6d stdlib: Add map* for Option and Result 2025-05-05 05:32:23 -04:00
c5e817f1e5 cl-ast: Rearrange 2025-05-05 05:26:43 -04:00
6108d66b0a stdlib: Add Option and Result types 2025-05-05 04:55:17 -04:00
09fdb14d79 cl-typeck: Progress population and categorization 2025-05-05 04:54:33 -04:00
4228324ab3 cl-typeck: With super semantics redone, search within self for items 2025-05-05 04:20:40 -04:00
f5f905cd70 cl-ast: more generic impls for weight_of
TODO: get rid of weight_of
2025-05-05 04:19:23 -04:00
883387aaf1 yaml: formatting 2025-05-05 04:18:49 -04:00
2d706ff582 desugar: Add primitive constant folding 2025-05-05 04:18:16 -04:00
cd2e3c3e32 cl-parser: change match parse slightly 2025-05-05 04:16:37 -04:00
8c23aea4af to_c: oops 2025-05-05 04:14:30 -04:00
d6c0a6cf1b cl-ast: Allow c-like enums to take an expr 2025-05-05 02:22:50 -04:00
fc80be5fcc conlang: Remove "self" keyword 2025-05-05 02:20:47 -04:00
7c2dd1468b cl-ast: Finally figure out how visit and walk are supposed to work 2025-05-05 00:24:52 -04:00
4747b65414 stdlib: Add Result and Option types
Since the type checker sucks less now, we can think about maybe
adding some features to the language.

...At some point I'd like to have the type checker clean up
the index map.
2025-04-22 08:00:59 -04:00
8ff17fd475 cl-ast: Add syntax support for generics 2025-04-22 07:22:44 -04:00
681fbc88d3 cl-typeck: More type inference
- Renamed "intrinsic" -> "primitive"
  - I thought it was funny, but it's just annoying.
- Rename "Uninferred" -> "Inferred"
  - It's a concrete type, but an inferred one.
- Add lifetimes to the Entry API
- Categorize does not care about recursive types. You can't have a recursive AST.
- Added helpful constructors to the inference engine
- Added some overloadable operators to the inference engine
  - These are a MAJOR work in progress
- Reorganized the Inference implementation file by functionality.

stdlib:
- Updated standard library.
2025-04-22 06:33:57 -04:00
7cf485fade cl-typeck/infer: Fix some inference errors
yay yippee type checking and inference woohoo
i am very tired
2025-04-21 05:37:34 -04:00
3b96833fcb cl-typeck: Early type inference for let 2025-04-21 04:52:59 -04:00
65b75f95ce cl-repl: Oops, add dependencies. 2025-04-21 04:27:17 -04:00
4c4b49ce00 cl-typeck: Implement a framework for type inference
...And also implement a bunch of the inference rules, too.
2025-04-21 04:26:07 -04:00
7ba808594c cl-ast: Cleanup
- Function bind is now one Pattern
- TyRef now allows &Ty (i.e. &[i32], &(char, bool)
- Range patterns (they cannot bind, only check whether a value is in range
- ArrayRep repeat has been reverted to usize, for now, until early consteval is implemented.
2025-04-21 04:17:45 -04:00
ef92d8b798 examples: Adapt examples/yaml to a broken C generator! woo! 2025-04-16 04:13:39 -04:00
8c8f1254e0 examples/yaml: Remove unnecessary uses of todo!() 2025-04-16 04:13:04 -04:00
82e62ab4ac cl-parser: Dereference first(?) 2025-04-15 23:44:56 -04:00
b09a610c6c interpreter: Include location in error type 2025-04-15 23:42:21 -04:00
fa5244dcf9 cl-interpret: References, part 3
MODS ARE ASLEEP
POST FAT BEAGLE
2025-03-14 08:33:46 -05:00
9b460afed4 cl-interpret/pattern: Fail on mismatch again 2025-03-14 07:00:10 -05:00
27d1d07ed8 conlang: Update to Rust Edition 2024 2025-03-14 06:09:39 -05:00
68e676eda4 repline: Clippy lints unbuffered readers now 2025-03-14 06:09:00 -05:00
c988193049 cl-structures/stack: constify all (most of) the things! 2025-03-14 06:07:48 -05:00
2ecb2efc09 cl-structures: IndexMap::get_disjoint_mut to match Rust 1.86 🎉 2025-03-14 04:35:47 -05:00
a4176c710e cl-ast: Add filename to File
- Better error reporting
- Better pizza
- Papa Cow's
2025-03-14 04:11:22 -05:00
cdb9ec49fe cl-ast: Estimate the "weight" of an AST, for debugging? 2025-03-14 04:02:14 -05:00
6d33c4baa9 cl-ast: Break ast-impl into submodules 2025-03-14 03:58:40 -05:00
33e13425a9 cl-ast: Clean up "to", "extents", Module."kind" 2025-03-14 00:52:43 -05:00
11c8daaed0 cl-ast: Re-add(?) the Infer type-pattern 2025-03-12 01:20:58 -05:00
584207fc8c cl-structures::Interned: Change to_ref() from assoc. function to member function
(it's so much nicer)
2025-03-12 01:16:51 -05:00
dcdb100a8a cl-interpret: Try having separate globals again? 2025-03-11 23:33:49 -05:00
fdf076c272 cl-ast: Remove Option-like "*Kind"s 2025-03-11 23:32:58 -05:00
2fc847fff2 Listen to the truthmachine!
Scream your falsehoods.
2025-03-11 05:03:52 -05:00
4bc088f277 cl-interpret: Pure value stack v1, references v2
References actually work! :D
They can also be stale :(
2025-03-11 05:01:49 -05:00
06bcb6b7c6 interpret: try out Ref == RcRefCell. Subject to change! 2025-03-11 01:31:02 -05:00
7e311cb0ef conlang: RIP THE EXPRKIND BANDAGE OFF
cl-ast: No more bare ExprKind: every Expr has a Span
cl-interpret: Give errors a span
cl-repl: Print eval errors in load_file, instead of returning them. These changes are relevant.
2025-03-11 00:36:42 -05:00
c0ad544486 cl-interpret/convalue: Destructure with (Rust) pattern matching 2025-02-28 20:36:24 -06:00
fd54b513be cl-interpret/pattern: Doc changes, minor format 2025-02-28 20:35:20 -06:00
adbabc66c5 cl-interpret: impls v1 2025-02-23 04:06:14 -06:00
5c99bf09ab stdlib: Make stdlib not error out w/ undefined symbols, add ranges. 2025-02-23 03:29:15 -06:00
4d9b13f7a1 cl-interpret: Enums, pt 1: C was right the whole time!!1 2025-02-23 03:22:48 -06:00
d9ac9e628d cl-interpret: Stage items within a file in resolution order.
TODO: Does this even help???
2025-02-23 03:21:34 -06:00
632ddf0eab cl-interpret: cleanup 2025-02-23 03:00:00 -06:00
e39b390441 cl-parser, cl-repl: Add ./[mod].cl to module search path 2025-02-23 02:44:26 -06:00
2fd08193fd cl-parser: Promote match scrutinee to position 2025-02-23 02:43:22 -06:00
7d3f189100 conlang: Introduce ..rest Patterns, refactor Ranges 2025-02-23 02:41:41 -06:00
cc6168b55e cl-ast: Remove Param, replace with flat Pattern 2025-02-23 02:01:38 -06:00
e3d94d8949 conlang: Unify binding operations!
This breaks out the pattern matching/unification algorithm from cl-interpret/interpret.rs to cl-interpret/pattern.rs

TODO: pattern destructuring in const, static :^)
2025-02-22 05:16:37 -06:00
7a8da33de9 cl-interpret: Tuple structs + fix tuple member access 2025-02-22 03:31:27 -06:00
697d139cfd conlang: Add Tuple-Struct Patterns
- Patterns are no longer parsed with the highest precedence
- Function calls with just a path and a tuple of args can now be transformed into a Pattern
2025-02-22 01:37:08 -06:00
5d2c714bc1 conlang: Patterns...2!
- Deny arbitrary paths in patterns (only one non-keyword identifier allowed!)
- Allow patterns in for-loop binders (literally useless atm, but it's a step toward making patterns the only way to bind names.)

Next: Functions, Tuple Struct Patterns... And solving the stupid syntactic ambiguity of structors.
2025-02-22 01:00:29 -06:00
b115fea71b cl-interpret: fix import in builtin! doctest 2025-02-21 22:49:14 -06:00
eebabf02fb repline: change error formatting 2025-02-20 22:05:43 -06:00
088cd4d1e4 cl-interpret: Change format of todo for Impl 2025-02-20 22:05:23 -06:00
0fd9c002fc cl-interpret: Interpret items in a file in a particular order
1) mod
2) use
3) enum, struct, type
4) fn
5) impl
6) const | static

This is a stopgap until names are statically resolved
2025-02-20 22:04:27 -06:00
772286eefa conlang: Single-expression functions 2025-02-20 21:59:42 -06:00
3b14186b70 sample-code: Add match_test.cl
Demonstrates pattern matching
2025-02-18 21:53:32 -06:00
a6ad20911d builtins: Add temp builtin for dumping the global string pool 2025-02-18 21:49:59 -06:00
01cf9d93e2 cl-repl: Usability improvements
- Don't print empty
- Don't needlessly append newline to cleared screen
2025-02-18 21:48:36 -06:00
edabbe1655 cl-interpret: process use items and imports in the interpreter 2025-02-18 21:44:52 -06:00
af9c293907 cl-interpret, cl-repl:
Move IO builtins into the CLI, so get_line can use repline keybinds.
2025-02-06 21:35:17 -06:00
0e3ba342c4 cl-interpret: make the error type smaller (at the cost of a heap allocation) 2025-01-31 03:35:35 -06:00
d95d35268e cl-interpret: Builtin refactor
- Everything is different now
  - Builtins are now built on top of Rust functions, so they can be recursive!
  - TODO: allow macro-defined builtins to call each other?
- The builtins! macro is a lot nicer to work with
  - No redundant return value
  - Maps the result over Into::into, allowing for type inference!
  - Uses explicit pattern syntax instead of weird binding, where possible
  - Does not #[allow(unused)], so you'll get unused variable warnings now!
2025-01-31 03:34:45 -06:00
0c2b0002ce conlang: Docs! 2025-01-29 05:07:08 -06:00
3534be5fbc conlang: bump version number for pattern matching and destructured bindings 2025-01-29 04:16:53 -06:00
026681787a cl-interpret: Add a new pretty-debug-printer builtin 2025-01-29 04:16:28 -06:00
80e1219808 cl-interpret: Tests for new pattern matching behavior
TODO: Expand control flow tests
2025-01-29 04:15:57 -06:00
6ee9bbd72e conlang: PATTERN MATCHING AND DESTRUCTURED BINDINGS WOOOOO
- Integrate the Match and Pattern nodes into the AST
  - TODO: `let x: T` is ambiguous with `let x: {}`. Currently the latter takes precedence in the parser.

- Implement pattern matching through unification in the interpreter.
  - It's not fast, but it works!

- Refactor destructuring assignments to use the new pattern functionality
2025-01-29 04:15:33 -06:00
6e94b702c9 cl-ast: Add pattern and match nodes, and associated behaviors 2025-01-29 04:05:11 -06:00
d21683ad61 conlang: Add Quote expression as a hack for testing
Possibly removed later, or replaced with something that turns Conlang AST nodes into Conlang data structures.
2025-01-29 03:56:19 -06:00
518fbe74a1 cl-ast: Fix AddrOf misbehavior 2025-01-29 03:31:24 -06:00
bc955c6409 conlang: Bump version number :D 2025-01-28 06:56:48 -06:00
678c0f952c sample-code: Add demo for get_line() 2025-01-28 06:56:30 -06:00
86c4da0689 cl-repl: Don't print ConValue::Empty return values 2025-01-28 06:55:03 -06:00
5db77db6b8 cl-interpret: Use dyn dispatch for iterators.
- This is a hack because the language has the syntax but no concept of iterators
2025-01-28 06:25:04 -06:00
145a24c5ff cl-interpret: Assignment 2.0, now with more destructuring!
TODO: Destructuring `let`, destructuring patterns in function args..?
2025-01-28 06:23:37 -06:00
485afb7843 cl-interpret: Make ConValues act like value types
(Aside from the fact that they're smothered in heap allocations)
2025-01-28 06:20:10 -06:00
01871bf455 cl-interpret: get_line builtin! 2025-01-28 06:14:05 -06:00
fd361f2bea cl-interpret: Upvars 2.0
- Only captures locals
2025-01-28 06:13:38 -06:00
0eef6b910c cl-interpret/collect_upvars: elide lifetime 2025-01-23 18:53:07 -06:00
c50940a44c cl-interpret: Make an attempt at closures
(It kinda sucks, but it emulates closures half decently)
2025-01-23 16:23:42 -06:00
3cda3d83d9 typeck: Replace unsafe static mut with tree interning,
I used tree interning here, because the interner already contains the necessary locks to make it Sync, and I was lazy. Be my guest if you want to do something else.

The computational overhead of interning the ASTs here is negligible, since this is all CLI code anyway.
2025-01-16 21:16:46 -06:00
e5a51ba6c2 cl-structures: add to_ref associated function on Interned type, for non-borrowing conversion 2025-01-16 21:01:28 -06:00
883fd31d38 conlang: Elide lifetimes (fixes clippy lint) 2025-01-16 20:57:33 -06:00
d71276b477 cl-structures: Update error type in unstable get_many_mut feature for IndexMap 2025-01-16 20:32:09 -06:00
d8e32ee263 cl-arena: promote to its own independent repository 2025-01-16 20:30:23 -06:00
e419c23769 repline: fix operator precedence bug, clippy lints 2025-01-16 20:29:43 -06:00
1bd9c021dd sample-code: Fix typo in sqrt.cl 2024-11-21 21:57:10 -06:00
df68d6f2e6 cl-interpret/tests: Fix broken test 2024-09-19 14:29:21 -05:00
ae11d87d68 Merge pull request 'Basic floating point support (WIP)' (#18) from floats into main
Reviewed-on: #18
2024-09-19 19:27:51 +00:00
96be5aba6c repline: Add a code sample demonstrating the use of prebaked::read_and 2024-09-19 14:08:50 -05:00
b9f4994930 sample-code: update sqrt.cl to use new float syntax 2024-09-19 14:02:58 -05:00
f4fe07a08b cl-lexer: Hack around ambiguity between 1.0 and 1..0
This requires more than one token lookahead, but is already part of a hack itself, so... /shrug
2024-09-19 14:02:02 -05:00
94be5d787f cl-ast: always pretty-print decimal for floats 2024-09-19 14:00:22 -05:00
5deb585054 cl-ast: Add float support
- Smuggle floats as integers to maintain `eq`
- This is bad, but not terrible for spec-compliant floats. Might have issues with NaN.

cl_parser: Smuggle floats

cl_interpret: unpack smuggled floats in float literal node
2024-09-19 13:20:19 -05:00
56e71d6782 cl-lexer: Add a hacky workaround for float support.
It's disgusting, but better than nothing!
2024-09-19 13:16:27 -05:00
c62df3d8b3 sample-code: Add square root demo 2024-09-18 01:53:04 -05:00
fad28beb05 interpreter: Add float machinery
- operators
- type casting
2024-09-18 01:02:09 -05:00
0f8b0824ac cl-parser: Fix precedence of comparison operators 2024-09-18 00:57:44 -05:00
99a00875a8 cl-intern: Derive Default for StringInterner and TypedInterner 2024-08-24 18:18:22 -05:00
8675f91aca cl-ast: Remove tail from let (it caused more problems that it could've solved) 2024-07-31 03:19:20 -05:00
de63a8c123 cl-parser: Outline precedence parser 2024-07-31 02:55:01 -05:00
533436afc1 cl-parser: Move precedence parser into its own module 2024-07-31 02:48:39 -05:00
1eb0516baf cl-parser: Rearrange to match cl-ast
Also reorder `Let` in the AST
2024-07-31 02:35:41 -05:00
97808fd855 cl-parser: Transliterate to a trait-based parsing implementation
Bump version number.
2024-07-31 01:39:00 -05:00
388a69948e Revert "cl-ast: Unify break, return, and unary expressions"
This reverts commit adb0fd229c.
2024-07-30 22:31:39 -05:00
5e7ba6de24 cl-ast: Improve formatting of blocks and groups 2024-07-30 20:40:22 -05:00
adb0fd229c cl-ast: Unify break, return, and unary expressions 2024-07-30 20:16:07 -05:00
0e545077c6 cl-ast: Remove "Continue" struct 2024-07-30 19:42:28 -05:00
b64cc232f9 cl-ast: Move loop expression into unary exprs (with lowest priority) 2024-07-30 18:21:25 -05:00
b0341f06fd cl-ast: Move let into Expr 2024-07-30 18:02:09 -05:00
a3e383b53f cl-token: Flatten TokenKind into a single enum (wow!) 2024-07-30 16:47:09 -05:00
1b217b2e75 typeck: Add a query for all strings 2024-07-29 15:55:53 -05:00
5662bd8524 cl-structures: (ab)use the Display trait to print a numbered, sorted list of interned strings. 2024-07-29 15:55:12 -05:00
28f9048087 cl-typeck: Fix infer.rs doctests 2024-07-29 15:42:35 -05:00
b17164b68b cl-interpret: Write an example for driving the interpreter 2024-07-29 15:42:05 -05:00
ecebefe218 cl-interpret: Knock those modules free! 2024-07-27 22:47:46 -05:00
fc374e0108 ascii.cl: TODO: throw out the interpreter (EVIL) 2024-07-27 20:00:22 -05:00
4295982876 ascii.cl: Cleanup on aisle "bitwise" 2024-07-27 19:59:35 -05:00
729155d3a4 ascii.cl: Fix type annotations (though they're not yet evaluated in the interpreter) 2024-07-27 19:38:41 -05:00
8c0ae02a71 sample-code/ascii.cl: Make it cooler
- Compute char value of digit
- Substitute C0 control codes for Unicode C0 Control Pictures
- Extend through Unicode Latin-1 Supplement
- Blank out C1 control code range
2024-07-27 19:34:37 -05:00
7f7836877e sample-code: Add shebang comments to samples with a main() function 2024-07-27 18:56:36 -05:00
b2733aa171 cl-interpret/builtin: Add len builtin as a quick hack to write more interesting programs.
This is temporary, I just want a way to get the length of a thing atm.
2024-07-27 18:43:03 -05:00
a233bb18bc cl-lexer: Record the contents of comments 2024-07-27 18:41:50 -05:00
e06a27a5b1 cl-lexer: Treat #!/ | #!\ as a comment 2024-07-27 18:41:18 -05:00
3f5c5480ae cl-interpret: [NOT FINAL] Add unicode-aware O(n) string indexing 2024-07-27 18:04:39 -05:00
53cf71608a sample-code/hex.cl: Fix casting TODO, add to_string_radix function 2024-07-27 17:46:27 -05:00
883c2677d9 cl-parser: Index is NOT a low precedence operator!!! 2024-07-27 17:37:29 -05:00
7d98ef87d5 sample-code: proper type annotations on HEX_LUT, add FIXME for min and max 2024-07-26 06:22:29 -05:00
a188c5b65e hex.cl: make the lut square 2024-07-26 06:17:00 -05:00
872818fe7c sample-code/fib.cl: rename fib-iterative -> fibit (easier to type) 2024-07-26 06:13:59 -05:00
3aef055739 sample-code/ascii: Use as casting to print the entire printable ASCII range 2024-07-26 06:10:59 -05:00
38a5d31b08 cl-ast: Escape string and char literals when pretty-printing 2024-07-26 05:51:20 -05:00
e43847bbd4 conlang: Introduce as casting
Arbitrary primitive type conversion

Currently implemented as jankily as possible in the interpreter, but it works alright™️
2024-07-26 05:26:08 -05:00
a8b8a91c79 sample-code: print->println to match interpreter behavior 2024-07-26 05:13:52 -05:00
695c812bf5 cl-repl: increase jank: first positional arg is main file, remainder are imports 2024-07-26 05:13:06 -05:00
524c84be9e cl_typeck: Add new primitive types (including joking-point numbers) 2024-07-26 03:24:34 -05:00
4096442f75 cl-typeck: Turn ref into a linked list.
This should be fine, since the only thing you can do with a ref is dereference it.
2024-07-26 02:14:41 -05:00
03a4e76292 cl-typeck: rustfmt implement.rs 2024-07-26 00:15:00 -05:00
46a1639990 sample-code: Have fun with random number generators 2024-07-25 23:59:41 -05:00
5ea8039a8a typeck.rs: Update for new stdlib layout; don't hardcode the root stdlib module. 2024-07-25 07:09:12 -05:00
479efbad73 typeck.rs: Add file-loading mode 2024-07-25 07:08:07 -05:00
a462dd2be3 stdlib: Use Conlang module layout 2024-07-25 07:05:57 -05:00
4d6b94b570 conlang: Bump version to v0.0.6
- Major milestone: cl-typeck doesn't suck as much!
2024-07-25 05:56:05 -05:00
fe2b816f27 cl-typeck: Crate-spanning refactor part 2
- Removed all unreferenced files
- Reimplemented missing/nonfunctional behavior
- Added module documentation for most things
  - TODO: item-level docs on Entry(Mut)
- Reparented the stages of Table population into the `stage` module.
  - TODO: rewrite type inference to use only the tools provided by Table.
2024-07-25 05:55:11 -05:00
e19127facc cl-typeck: Crate-spanning refactor
TODO: remove all unreferenced files
TODO: Finish resolving statically known types of values
TODO: Type inference
2024-07-24 18:22:42 -05:00
b7ad285a11 cl-typeck: give Handle accessors for useful attributes 2024-07-24 14:29:27 -05:00
131 changed files with 14905 additions and 8101 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[registries.soft-fish]
index = "https://git.soft.fish/j/_cargo-index.git"

3
.gitignore vendored
View File

@@ -6,5 +6,8 @@
**/Cargo.lock
target
# Symbol table dump?
typeck-table.ron
# Pest files generated by Grammatical
*.p*st

View File

@@ -2,22 +2,21 @@
members = [
"compiler/cl-repl",
"compiler/cl-typeck",
"compiler/cl-embed",
"compiler/cl-interpret",
"compiler/cl-structures",
"compiler/cl-token",
"compiler/cl-ast",
"compiler/cl-parser",
"compiler/cl-lexer",
"compiler/cl-arena",
"repline",
]
resolver = "2"
[workspace.package]
repository = "https://git.soft.fish/j/Conlang"
version = "0.0.5"
version = "0.0.10"
authors = ["John Breaux <j@soft.fish>"]
edition = "2021"
edition = "2024"
license = "MIT"
publish = ["soft-fish"]

View File

@@ -1,10 +0,0 @@
[package]
name = "cl-arena"
repository.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]

View File

@@ -1,42 +0,0 @@
use super::DroplessArena;
extern crate std;
use core::alloc::Layout;
use std::{prelude::rust_2021::*, vec};
#[test]
fn alloc_raw() {
let arena = DroplessArena::new();
let bytes = arena.alloc_raw(Layout::for_value(&0u128));
let byte2 = arena.alloc_raw(Layout::for_value(&0u128));
assert_ne!(bytes, byte2);
}
#[test]
fn alloc() {
let arena = DroplessArena::new();
let mut allocations = vec![];
for i in 0..0x400 {
allocations.push(arena.alloc(i));
}
}
#[test]
fn alloc_strings() {
const KW: &[&str] = &["pub", "mut", "fn", "mod", "conlang", "sidon", "🦈"];
let arena = DroplessArena::new();
let mut allocations = vec![];
for _ in 0..100 {
for kw in KW {
allocations.push(arena.alloc_str(kw));
}
}
}
#[test]
#[should_panic]
fn alloc_zsts() {
struct Zst;
let arena = DroplessArena::new();
arena.alloc(Zst);
}

View File

@@ -1,396 +0,0 @@
//! Typed and dropless arena allocation, paraphrased from [the Rust Compiler's `rustc_arena`](https://github.com/rust-lang/rust/blob/master/compiler/rustc_arena/src/lib.rs). See [LICENSE][1].
//!
//! An Arena Allocator is a type of allocator which provides stable locations for allocations within
//! itself for the entire duration of its lifetime.
//!
//! [1]: https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT
#![feature(dropck_eyepatch, new_uninit, strict_provenance)]
#![no_std]
extern crate alloc;
pub(crate) mod constants {
//! Size constants for arena chunk growth
pub(crate) const MIN_CHUNK: usize = 4096;
pub(crate) const MAX_CHUNK: usize = 2 * 1024 * 1024;
}
mod chunk {
//! An [ArenaChunk] contains a block of raw memory for use in arena allocators.
use alloc::boxed::Box;
use core::{
mem::{self, MaybeUninit},
ptr::{self, NonNull},
};
pub struct ArenaChunk<T> {
pub(crate) mem: NonNull<[MaybeUninit<T>]>,
pub(crate) filled: usize,
}
impl<T: Sized> ArenaChunk<T> {
pub fn new(cap: usize) -> Self {
let slice = Box::new_uninit_slice(cap);
Self { mem: NonNull::from(Box::leak(slice)), filled: 0 }
}
/// Drops all elements inside self, and resets the filled count to 0
///
/// # Safety
///
/// The caller must ensure that `self.filled` elements of self are currently initialized
pub unsafe fn drop_elements(&mut self) {
if mem::needs_drop::<T>() {
// Safety: the caller has ensured that `filled` elements are initialized
unsafe {
let slice = self.mem.as_mut();
for t in slice[..self.filled].iter_mut() {
t.assume_init_drop();
}
}
self.filled = 0;
}
}
/// Gets a pointer to the start of the arena
pub fn start(&mut self) -> *mut T {
self.mem.as_ptr() as _
}
/// Gets a pointer to the end of the arena
pub fn end(&mut self) -> *mut T {
if mem::size_of::<T>() == 0 {
ptr::without_provenance_mut(usize::MAX) // pointers to ZSTs must be unique
} else {
unsafe { self.start().add(self.mem.len()) }
}
}
}
impl<T> Drop for ArenaChunk<T> {
fn drop(&mut self) {
let _ = unsafe { Box::from_raw(self.mem.as_ptr()) };
}
}
}
pub mod typed_arena {
//! A [TypedArena] can hold many instances of a single type, and will properly [Drop] them.
#![allow(clippy::mut_from_ref)]
use crate::{chunk::ArenaChunk, constants::*};
use alloc::vec::Vec;
use core::{
cell::{Cell, RefCell},
marker::PhantomData,
mem, ptr, slice,
};
/// A [TypedArena] can hold many instances of a single type, and will properly [Drop] them when
/// it falls out of scope.
pub struct TypedArena<'arena, T> {
_lives: PhantomData<&'arena T>,
_drops: PhantomData<T>,
chunks: RefCell<Vec<ArenaChunk<T>>>,
head: Cell<*mut T>,
tail: Cell<*mut T>,
}
impl<'arena, T> Default for TypedArena<'arena, T> {
fn default() -> Self {
Self::new()
}
}
impl<'arena, T> TypedArena<'arena, T> {
pub const fn new() -> Self {
Self {
_lives: PhantomData,
_drops: PhantomData,
chunks: RefCell::new(Vec::new()),
head: Cell::new(ptr::null_mut()),
tail: Cell::new(ptr::null_mut()),
}
}
pub fn alloc(&'arena self, value: T) -> &'arena mut T {
if self.head == self.tail {
self.grow(1);
}
let out = if mem::size_of::<T>() == 0 {
self.head
.set(ptr::without_provenance_mut(self.head.get().addr() + 1));
ptr::NonNull::<T>::dangling().as_ptr()
} else {
let out = self.head.get();
self.head.set(unsafe { out.add(1) });
out
};
unsafe {
ptr::write(out, value);
&mut *out
}
}
fn can_allocate(&self, len: usize) -> bool {
len <= unsafe { self.tail.get().offset_from(self.head.get()) as usize }
}
/// # Panics
/// Panics if size_of::<T> == 0 || len == 0
#[inline]
fn alloc_raw_slice(&self, len: usize) -> *mut T {
assert!(mem::size_of::<T>() != 0);
assert!(len != 0);
if !self.can_allocate(len) {
self.grow(len)
}
let out = self.head.get();
unsafe { self.head.set(out.add(len)) };
out
}
pub fn alloc_from_iter<I>(&'arena self, iter: I) -> &'arena mut [T]
where I: IntoIterator<Item = T> {
// Collect them all into a buffer so they're allocated contiguously
let mut buf = iter.into_iter().collect::<Vec<_>>();
if buf.is_empty() {
return &mut [];
}
let len = buf.len();
// If T is a ZST, calling alloc_raw_slice will panic
let slice = if mem::size_of::<T>() == 0 {
self.head
.set(ptr::without_provenance_mut(self.head.get().addr() + len));
ptr::NonNull::dangling().as_ptr()
} else {
self.alloc_raw_slice(len)
};
unsafe {
buf.as_ptr().copy_to_nonoverlapping(slice, len);
buf.set_len(0);
slice::from_raw_parts_mut(slice, len)
}
}
#[cold]
#[inline(never)]
fn grow(&self, len: usize) {
let size = mem::size_of::<T>().max(1);
let mut chunks = self.chunks.borrow_mut();
let capacity = if let Some(last) = chunks.last_mut() {
last.filled = self.get_filled_of_chunk(last);
last.mem.len().min(MAX_CHUNK / size) * 2
} else {
MIN_CHUNK / size
}
.max(len);
let mut chunk = ArenaChunk::<T>::new(capacity);
self.head.set(chunk.start());
self.tail.set(chunk.end());
chunks.push(chunk);
}
fn get_filled_of_chunk(&self, chunk: &mut ArenaChunk<T>) -> usize {
let Self { head: tail, .. } = self;
let head = chunk.start();
if mem::size_of::<T>() == 0 {
tail.get().addr() - head.addr()
} else {
unsafe { tail.get().offset_from(head) as usize }
}
}
}
unsafe impl<'arena, T: Send> Send for TypedArena<'arena, T> {}
unsafe impl<'arena, #[may_dangle] T> Drop for TypedArena<'arena, T> {
fn drop(&mut self) {
let mut chunks = self.chunks.borrow_mut();
if let Some(last) = chunks.last_mut() {
last.filled = self.get_filled_of_chunk(last);
self.tail.set(self.head.get());
}
for chunk in chunks.iter_mut() {
unsafe { chunk.drop_elements() }
}
}
}
#[cfg(test)]
mod tests;
}
pub mod dropless_arena {
//! A [DroplessArena] can hold *any* combination of types as long as they don't implement
//! [Drop].
use crate::{chunk::ArenaChunk, constants::*};
use alloc::vec::Vec;
use core::{
alloc::Layout,
cell::{Cell, RefCell},
marker::PhantomData,
mem, ptr, slice,
};
pub struct DroplessArena<'arena> {
_lives: PhantomData<&'arena u8>,
chunks: RefCell<Vec<ArenaChunk<u8>>>,
head: Cell<*mut u8>,
tail: Cell<*mut u8>,
}
impl Default for DroplessArena<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'arena> DroplessArena<'arena> {
pub const fn new() -> Self {
Self {
_lives: PhantomData,
chunks: RefCell::new(Vec::new()),
head: Cell::new(ptr::null_mut()),
tail: Cell::new(ptr::null_mut()),
}
}
/// Allocates a `T` in the [DroplessArena], and returns a mutable reference to it.
///
/// # Panics
/// - Panics if T implements [Drop]
/// - Panics if T is zero-sized
#[allow(clippy::mut_from_ref)]
pub fn alloc<T>(&'arena self, value: T) -> &'arena mut T {
assert!(!mem::needs_drop::<T>());
assert!(mem::size_of::<T>() != 0);
let out = self.alloc_raw(Layout::new::<T>()) as *mut T;
unsafe {
ptr::write(out, value);
&mut *out
}
}
/// Allocates a slice of `T`s`, copied from the given slice, returning a mutable reference
/// to it.
///
/// # Panics
/// - Panics if T implements [Drop]
/// - Panics if T is zero-sized
/// - Panics if the slice is empty
#[allow(clippy::mut_from_ref)]
pub fn alloc_slice<T: Copy>(&'arena self, slice: &[T]) -> &'arena mut [T] {
assert!(!mem::needs_drop::<T>());
assert!(mem::size_of::<T>() != 0);
assert!(!slice.is_empty());
let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T;
unsafe {
mem.copy_from_nonoverlapping(slice.as_ptr(), slice.len());
slice::from_raw_parts_mut(mem, slice.len())
}
}
/// Allocates a copy of the given [`&str`](str), returning a reference to the allocation.
///
/// # Panics
/// Panics if the string is empty.
pub fn alloc_str(&'arena self, string: &str) -> &'arena str {
let slice = self.alloc_slice(string.as_bytes());
// Safety: This is a clone of the input string, which was valid
unsafe { core::str::from_utf8_unchecked(slice) }
}
/// Allocates some [bytes](u8) based on the given [Layout].
///
/// # Panics
/// Panics if the provided [Layout] has size 0
pub fn alloc_raw(&'arena self, layout: Layout) -> *mut u8 {
/// Rounds the given size (or pointer value) *up* to the given alignment
fn align_up(size: usize, align: usize) -> usize {
(size + align - 1) & !(align - 1)
}
/// Rounds the given size (or pointer value) *down* to the given alignment
fn align_down(size: usize, align: usize) -> usize {
size & !(align - 1)
}
assert!(layout.size() != 0);
loop {
let Self { head, tail, .. } = self;
let start = head.get().addr();
let end = tail.get().addr();
let align = 8.max(layout.align());
let bytes = align_up(layout.size(), align);
if let Some(end) = end.checked_sub(bytes) {
let end = align_down(end, layout.align());
if start <= end {
tail.set(tail.get().with_addr(end));
return tail.get();
}
}
self.grow(layout.size());
}
}
/// Grows the allocator, doubling the chunk size until it reaches [MAX_CHUNK].
#[cold]
#[inline(never)]
fn grow(&self, len: usize) {
let mut chunks = self.chunks.borrow_mut();
let capacity = if let Some(last) = chunks.last_mut() {
last.mem.len().min(MAX_CHUNK / 2) * 2
} else {
MIN_CHUNK
}
.max(len);
let mut chunk = ArenaChunk::<u8>::new(capacity);
self.head.set(chunk.start());
self.tail.set(chunk.end());
chunks.push(chunk);
}
/// Checks whether the given slice is allocated in this arena
pub fn contains_slice<T>(&self, slice: &[T]) -> bool {
let ptr = slice.as_ptr().cast::<u8>().cast_mut();
for chunk in self.chunks.borrow_mut().iter_mut() {
if chunk.start() <= ptr && ptr <= chunk.end() {
return true;
}
}
false
}
}
unsafe impl<'arena> Send for DroplessArena<'arena> {}
#[cfg(test)]
mod tests;
}

View File

@@ -1,61 +0,0 @@
use super::TypedArena;
extern crate std;
use std::{prelude::rust_2021::*, print, vec};
#[test]
fn pushing_to_arena() {
let arena = TypedArena::new();
let foo = arena.alloc("foo");
let bar = arena.alloc("bar");
let baz = arena.alloc("baz");
assert_eq!("foo", *foo);
assert_eq!("bar", *bar);
assert_eq!("baz", *baz);
}
#[test]
fn pushing_vecs_to_arena() {
let arena = TypedArena::new();
let foo = arena.alloc(vec!["foo"]);
let bar = arena.alloc(vec!["bar"]);
let baz = arena.alloc(vec!["baz"]);
assert_eq!("foo", foo[0]);
assert_eq!("bar", bar[0]);
assert_eq!("baz", baz[0]);
}
#[test]
fn pushing_zsts() {
struct ZeroSized;
impl Drop for ZeroSized {
fn drop(&mut self) {
print!("")
}
}
let arena = TypedArena::new();
for _ in 0..0x100 {
arena.alloc(ZeroSized);
}
}
#[test]
fn pushing_nodrop_zsts() {
struct ZeroSized;
let arena = TypedArena::new();
for _ in 0..0x1000 {
arena.alloc(ZeroSized);
}
}
#[test]
fn resize() {
let arena = TypedArena::new();
for _ in 0..0x780 {
arena.alloc(0u128);
}
}

View File

@@ -8,6 +8,7 @@
//! - [Assign], [Modify], [Binary], and [Unary] expressions
//! - [ModifyKind], [BinaryKind], and [UnaryKind] operators
//! - [Ty] and [TyKind]: Type qualifiers
//! - [Pattern]: Pattern matching operators
//! - [Path]: Path expressions
use cl_structures::{intern::interned::Interned, span::*};
@@ -30,18 +31,10 @@ pub enum Visibility {
Public,
}
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Literal {
Bool(bool),
Char(char),
Int(u128),
String(String),
}
/// A list of [Item]s
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct File {
pub name: &'static str,
pub items: Vec<Item>,
}
@@ -70,7 +63,7 @@ pub enum MetaKind {
/// Anything that can appear at the top level of a [File]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Item {
pub extents: Span,
pub span: Span,
pub attrs: Attrs,
pub vis: Visibility,
pub kind: ItemKind,
@@ -79,7 +72,6 @@ pub struct Item {
/// What kind of [Item] is this?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ItemKind {
// TODO: Import declaration ("use") item
// TODO: Trait declaration ("trait") item?
/// A [module](Module)
Module(Module),
@@ -101,10 +93,23 @@ pub enum ItemKind {
Use(Use),
}
/// A list of type variables to introduce
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Generics {
pub vars: Vec<Sym>,
}
/// An ordered collection of [Items](Item)
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Module {
pub name: Sym,
pub file: Option<File>,
}
/// An alias to another [Ty]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Alias {
pub to: Sym,
pub name: Sym,
pub from: Option<Box<Ty>>,
}
@@ -125,40 +130,21 @@ pub struct Static {
pub init: Box<Expr>,
}
/// An ordered collection of [Items](Item)
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Module {
pub name: Sym,
pub kind: ModuleKind,
}
/// The contents of a [Module], if they're in the same file
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ModuleKind {
Inline(File),
Outline,
}
/// Code, and the interface to that code
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Function {
pub name: Sym,
pub gens: Generics,
pub sign: TyFn,
pub bind: Vec<Param>,
pub body: Option<Block>,
}
/// A single parameter for a [Function]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Param {
pub mutability: Mutability,
pub name: Sym,
pub bind: Pattern,
pub body: Option<Expr>,
}
/// A user-defined product type
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Struct {
pub name: Sym,
pub gens: Generics,
pub kind: StructKind,
}
@@ -182,36 +168,22 @@ pub struct StructMember {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Enum {
pub name: Sym,
pub kind: EnumKind,
}
/// An [Enum]'s [Variant]s, if it has a variant block
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum EnumKind {
/// Represents an enum with no variants
NoVariants,
Variants(Vec<Variant>),
pub gens: Generics,
pub variants: Vec<Variant>,
}
/// A single [Enum] variant
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Variant {
pub name: Sym,
pub kind: VariantKind,
}
/// Whether the [Variant] has a C-like constant value, a tuple, or [StructMember]s
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum VariantKind {
Plain,
CLike(u128),
Tuple(Ty),
Struct(Vec<StructMember>),
pub kind: StructKind,
pub body: Option<Box<Expr>>,
}
/// Sub-[items](Item) (associated functions, etc.) for a [Ty]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Impl {
pub gens: Generics,
pub target: ImplKind,
pub body: File,
}
@@ -243,41 +215,42 @@ pub enum UseTree {
/// A type expression
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Ty {
pub extents: Span,
pub span: Span,
pub kind: TyKind,
pub gens: Generics,
}
/// Information about a [Ty]pe expression
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TyKind {
Never,
Empty,
Infer,
Path(Path),
Array(TyArray),
Slice(TySlice),
Tuple(TyTuple),
Ref(TyRef),
Ptr(TyPtr),
Fn(TyFn),
// TODO: slice, array types
}
/// An array of [`T`](Ty)
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TyArray {
pub ty: Box<TyKind>,
pub ty: Box<Ty>,
pub count: usize,
}
/// A [Ty]pe slice expression: `[T]`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TySlice {
pub ty: Box<TyKind>,
pub ty: Box<Ty>,
}
/// A tuple of [Ty]pes
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TyTuple {
pub types: Vec<TyKind>,
pub types: Vec<Ty>,
}
/// A [Ty]pe-reference expression as (number of `&`, [Path])
@@ -285,14 +258,20 @@ pub struct TyTuple {
pub struct TyRef {
pub mutable: Mutability,
pub count: u16,
pub to: Path,
pub to: Box<Ty>,
}
/// A [Ty]pe-reference expression as (number of `&`, [Path])
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TyPtr {
pub to: Box<Ty>,
}
/// The args and return value for a function pointer [Ty]pe
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TyFn {
pub args: Box<TyKind>,
pub rety: Option<Box<Ty>>,
pub args: Box<Ty>,
pub rety: Box<Ty>,
}
/// A path to an [Item] in the [Module] tree
@@ -303,10 +282,9 @@ pub struct Path {
}
/// A single component of a [Path]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PathPart {
SuperKw,
SelfKw,
SelfTy,
Ident(Sym),
}
@@ -314,7 +292,7 @@ pub enum PathPart {
/// An abstract statement, and associated metadata
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Stmt {
pub extents: Span,
pub span: Span,
pub kind: StmtKind,
pub semi: Semi,
}
@@ -323,7 +301,6 @@ pub struct Stmt {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum StmtKind {
Empty,
Local(Let),
Item(Box<Item>),
Expr(Box<Expr>),
}
@@ -335,19 +312,10 @@ pub enum Semi {
Unterminated,
}
/// A local variable declaration [Stmt]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Let {
pub mutable: Mutability,
pub name: Sym,
pub ty: Option<Box<Ty>>,
pub init: Option<Box<Expr>>,
}
/// An expression, the beating heart of the language
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Expr {
pub extents: Span,
pub span: Span,
pub kind: ExprKind,
}
@@ -357,6 +325,28 @@ pub enum ExprKind {
/// An empty expression: `(` `)`
#[default]
Empty,
/// A [Closure] expression: `|` [`Expr`] `|` ( -> [`Ty`])? [`Expr`]
Closure(Closure),
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
Tuple(Tuple),
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}`
Structor(Structor),
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
Array(Array),
/// An Array literal constructed with [repeat syntax](ArrayRep)
/// `[` [Expr] `;` [Literal] `]`
ArrayRep(ArrayRep),
/// An address-of expression: `&` `mut`? [`Expr`]
AddrOf(AddrOf),
/// A backtick-quoted expression
Quote(Quote),
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
Literal(Literal),
/// A [Grouping](Group) expression `(` [`Expr`] `)`
Group(Group),
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
Block(Block),
/// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+
Assign(Assign),
/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
@@ -369,52 +359,122 @@ pub enum ExprKind {
Member(Member),
/// An Array [Index] expression: a[10, 20, 30]
Index(Index),
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}`
Structor(Structor),
/// A [Cast] expression: [`Expr`] `as` [`Ty`]
Cast(Cast),
/// A [path expression](Path): `::`? [PathPart] (`::` [PathPart])*
Path(Path),
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
Literal(Literal),
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
Array(Array),
/// An Array literal constructed with [repeat syntax](ArrayRep)
/// `[` [Expr] `;` [Literal] `]`
ArrayRep(ArrayRep),
/// An address-of expression: `&` `mut`? [`Expr`]
AddrOf(AddrOf),
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
Block(Block),
/// A [Grouping](Group) expression `(` [`Expr`] `)`
Group(Group),
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
Tuple(Tuple),
/// A [Loop] expression: `loop` [`Block`]
Loop(Loop),
/// A local bind instruction, `let` [`Sym`] `=` [`Expr`]
Let(Let),
/// A [Match] expression: `match` [Expr] `{` ([MatchArm] `,`)* [MatchArm]? `}`
Match(Match),
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
While(While),
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
If(If),
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]?
/// A [For] expression: `for` [`Pattern`] `in` [`Expr`] [`Block`] [`Else`]?
For(For),
/// A [Break] expression: `break` [`Expr`]?
Break(Break),
/// A [Return] expression `return` [`Expr`]?
Return(Return),
/// A continue expression: `continue`
Continue(Continue),
Continue,
}
/// A Closure [expression](Expr): `|` [`Expr`] `|` ( -> [`Ty`])? [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Closure {
pub arg: Box<Pattern>,
pub body: Box<Expr>,
}
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Tuple {
pub exprs: Vec<Expr>,
}
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Structor {
pub to: Path,
pub init: Vec<Fielder>,
}
/// A [Struct field initializer] expression: [Sym] (`=` [Expr])?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Fielder {
pub name: Sym,
pub init: Option<Box<Expr>>,
}
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Array {
pub values: Vec<Expr>,
}
/// An Array literal constructed with [repeat syntax](ArrayRep)
/// `[` [Expr] `;` [Literal] `]`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ArrayRep {
pub value: Box<Expr>,
pub repeat: Box<Expr>,
}
/// An address-of expression: `&` `mut`? [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AddrOf {
pub mutable: Mutability,
pub expr: Box<Expr>,
}
/// A cast expression: [`Expr`] `as` [`Ty`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Cast {
pub head: Box<Expr>,
pub ty: Ty,
}
/// A backtick-quoted subexpression-literal
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Quote {
pub quote: Box<Expr>,
}
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Literal {
Bool(bool),
Char(char),
Int(u128),
Float(u64),
String(String),
}
/// A [Grouping](Group) expression `(` [`Expr`] `)`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Group {
pub expr: Box<Expr>,
}
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Block {
pub stmts: Vec<Stmt>,
}
/// An [Assign]ment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Assign {
pub parts: Box<(ExprKind, ExprKind)>,
pub parts: Box<(Expr, Expr)>,
}
/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Modify {
pub kind: ModifyKind,
pub parts: Box<(ExprKind, ExprKind)>,
pub parts: Box<(Expr, Expr)>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -435,7 +495,7 @@ pub enum ModifyKind {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Binary {
pub kind: BinaryKind,
pub parts: Box<(ExprKind, ExprKind)>,
pub parts: Box<(Expr, Expr)>,
}
/// A [Binary] operator
@@ -469,7 +529,7 @@ pub enum BinaryKind {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Unary {
pub kind: UnaryKind,
pub tail: Box<ExprKind>,
pub tail: Box<Expr>,
}
/// A [Unary] operator
@@ -478,6 +538,10 @@ pub enum UnaryKind {
Deref,
Neg,
Not,
RangeInc,
RangeExc,
/// A Loop expression: `loop` [`Block`]
Loop,
/// Unused
At,
/// Unused
@@ -487,7 +551,7 @@ pub enum UnaryKind {
/// A [Member] access expression: [`Expr`] [`MemberKind`]\*
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Member {
pub head: Box<ExprKind>,
pub head: Box<Expr>,
pub kind: MemberKind,
}
@@ -502,68 +566,44 @@ pub enum MemberKind {
/// A repeated [Index] expression: a[10, 20, 30][40, 50, 60]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Index {
pub head: Box<ExprKind>,
pub head: Box<Expr>,
pub indices: Vec<Expr>,
}
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}`
/// A local variable declaration [Stmt]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Structor {
pub to: Path,
pub init: Vec<Fielder>,
}
/// A [Struct field initializer] expression: [Sym] (`=` [Expr])?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Fielder {
pub name: Sym,
pub struct Let {
pub mutable: Mutability,
pub name: Pattern,
pub ty: Option<Box<Ty>>,
pub init: Option<Box<Expr>>,
}
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
/// A `match` expression: `match` `{` ([MatchArm] `,`)* [MatchArm]? `}`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Array {
pub values: Vec<Expr>,
pub struct Match {
pub scrutinee: Box<Expr>,
pub arms: Vec<MatchArm>,
}
/// An Array literal constructed with [repeat syntax](ArrayRep)
/// `[` [Expr] `;` [Literal] `]`
/// A single arm of a [Match] expression: [`Pattern`] `=>` [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ArrayRep {
pub value: Box<ExprKind>,
pub repeat: Box<ExprKind>,
}
pub struct MatchArm(pub Pattern, pub Expr);
/// An address-of expression: `&` `mut`? [`Expr`]
/// A [Pattern] meta-expression (any [`ExprKind`] that fits pattern rules)
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AddrOf {
pub count: usize,
pub mutable: Mutability,
pub expr: Box<ExprKind>,
}
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Block {
pub stmts: Vec<Stmt>,
}
/// A [Grouping](Group) expression `(` [`Expr`] `)`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Group {
pub expr: Box<ExprKind>,
}
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Tuple {
pub exprs: Vec<Expr>,
}
/// A [Loop] expression: `loop` [`Block`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Loop {
pub body: Box<Expr>,
pub enum Pattern {
Name(Sym),
Path(Path),
Literal(Literal),
Rest(Option<Box<Pattern>>),
Ref(Mutability, Box<Pattern>),
RangeExc(Box<Pattern>, Box<Pattern>),
RangeInc(Box<Pattern>, Box<Pattern>),
Tuple(Vec<Pattern>),
Array(Vec<Pattern>),
Struct(Path, Vec<(Sym, Option<Pattern>)>),
TupleStruct(Path, Vec<Pattern>),
}
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
@@ -585,7 +625,7 @@ pub struct If {
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct For {
pub bind: Sym, // TODO: Patterns?
pub bind: Pattern,
pub cond: Box<Expr>,
pub pass: Box<Block>,
pub fail: Else,
@@ -608,7 +648,3 @@ pub struct Break {
pub struct Return {
pub body: Option<Box<Expr>>,
}
/// A continue expression: `continue`
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Continue;

View File

@@ -1,834 +1,8 @@
//! Implementations of AST nodes and traits
use super::*;
mod display {
//! Implements [Display] for [AST](super::super) Types
mod convert;
mod display;
mod path;
use super::*;
use format::{delimiters::*, *};
use std::{
borrow::Borrow,
fmt::{Display, Write},
};
fn separate<I: Display, W: Write>(
iterable: impl IntoIterator<Item = I>,
sep: &'static str,
) -> impl FnOnce(W) -> std::fmt::Result {
move |mut f| {
for (idx, item) in iterable.into_iter().enumerate() {
if idx > 0 {
f.write_str(sep)?;
}
write!(f, "{item}")?;
}
Ok(())
}
}
impl Display for Mutability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Mutability::Not => Ok(()),
Mutability::Mut => "mut ".fmt(f),
}
}
}
impl Display for Visibility {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Visibility::Private => Ok(()),
Visibility::Public => "pub ".fmt(f),
}
}
}
impl Display for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Literal::Bool(v) => v.fmt(f),
Literal::Char(v) => write!(f, "'{v}'"),
Literal::Int(v) => v.fmt(f),
Literal::String(v) => write!(f, "\"{v}\""),
}
}
}
impl Display for File {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.items, "\n\n")(f)
}
}
impl Display for Attrs {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { meta } = self;
if meta.is_empty() {
return Ok(());
}
"#".fmt(f)?;
separate(meta, ", ")(&mut f.delimit(INLINE_SQUARE))?;
"\n".fmt(f)
}
}
impl Display for Meta {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self;
write!(f, "{name}{kind}")
}
}
impl Display for MetaKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MetaKind::Plain => Ok(()),
MetaKind::Equals(v) => write!(f, " = {v}"),
MetaKind::Func(args) => separate(args, ", ")(f.delimit(INLINE_PARENS)),
}
}
}
impl Display for Item {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { extents: _, attrs, vis, kind } = self;
attrs.fmt(f)?;
vis.fmt(f)?;
kind.fmt(f)
}
}
impl Display for ItemKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ItemKind::Alias(v) => v.fmt(f),
ItemKind::Const(v) => v.fmt(f),
ItemKind::Static(v) => v.fmt(f),
ItemKind::Module(v) => v.fmt(f),
ItemKind::Function(v) => v.fmt(f),
ItemKind::Struct(v) => v.fmt(f),
ItemKind::Enum(v) => v.fmt(f),
ItemKind::Impl(v) => v.fmt(f),
ItemKind::Use(v) => v.fmt(f),
}
}
}
impl Display for Alias {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { to, from } = self;
match from {
Some(from) => write!(f, "type {to} = {from};"),
None => write!(f, "type {to};"),
}
}
}
impl Display for Const {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, ty, init } = self;
write!(f, "const {name}: {ty} = {init}")
}
}
impl Display for Static {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutable, name, ty, init } = self;
write!(f, "static {mutable}{name}: {ty} = {init}")
}
}
impl Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self;
write!(f, "mod {name}{kind}")
}
}
impl Display for ModuleKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ModuleKind::Inline(items) => {
' '.fmt(f)?;
write!(f.delimit(BRACES), "{items}")
}
ModuleKind::Outline => ';'.fmt(f),
}
}
}
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, sign: sign @ TyFn { args, rety }, bind, body } = self;
let types = match **args {
TyKind::Tuple(TyTuple { ref types }) => types.as_slice(),
TyKind::Empty => Default::default(),
_ => {
write!(f, "Invalid function signature: {sign}")?;
Default::default()
}
};
debug_assert_eq!(bind.len(), types.len());
write!(f, "fn {name} ")?;
{
let mut f = f.delimit(INLINE_PARENS);
for (idx, (arg, ty)) in bind.iter().zip(types.iter()).enumerate() {
if idx != 0 {
f.write_str(", ")?;
}
write!(f, "{arg}: {ty}")?;
}
}
if let Some(rety) = rety {
write!(f, " -> {rety}")?;
}
match body {
Some(body) => write!(f, " {body}"),
None => ';'.fmt(f),
}
}
}
impl Display for Param {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutability, name } = self;
write!(f, "{mutability}{name}")
}
}
impl Display for Struct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self;
write!(f, "struct {name}{kind}")
}
}
impl Display for StructKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StructKind::Empty => ';'.fmt(f),
StructKind::Tuple(v) => separate(v, ", ")(f.delimit(INLINE_PARENS)),
StructKind::Struct(v) => separate(v, ",\n")(f.delimit(SPACED_BRACES)),
}
}
}
impl Display for StructMember {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { vis, name, ty } = self;
write!(f, "{vis}{name}: {ty}")
}
}
impl Display for Enum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self;
write!(f, "enum {name}{kind}")
}
}
impl Display for EnumKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EnumKind::NoVariants => ';'.fmt(f),
EnumKind::Variants(v) => separate(v, ",\n")(f.delimit(SPACED_BRACES)),
}
}
}
impl Display for Variant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self;
write!(f, "{name}{kind}")
}
}
impl Display for VariantKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
VariantKind::Plain => Ok(()),
VariantKind::CLike(n) => write!(f, " = {n}"),
VariantKind::Tuple(v) => v.fmt(f),
VariantKind::Struct(v) => separate(v, ", ")(f.delimit(INLINE_BRACES)),
}
}
}
impl Display for Impl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { target, body } = self;
write!(f, "impl {target} ")?;
write!(f.delimit(BRACES), "{body}")
}
}
impl Display for ImplKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ImplKind::Type(t) => t.fmt(f),
ImplKind::Trait { impl_trait, for_type } => {
write!(f, "{impl_trait} for {for_type}")
}
}
}
}
impl Display for Use {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { absolute, tree } = self;
f.write_str(if *absolute { "use ::" } else { "use " })?;
write!(f, "{tree};")
}
}
impl Display for UseTree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UseTree::Tree(tree) => separate(tree, ", ")(f.delimit(INLINE_BRACES)),
UseTree::Path(path, rest) => write!(f, "{path}::{rest}"),
UseTree::Alias(path, name) => write!(f, "{path} as {name}"),
UseTree::Name(name) => write!(f, "{name}"),
UseTree::Glob => write!(f, "*"),
}
}
}
impl Display for Ty {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)
}
}
impl Display for TyKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TyKind::Never => "!".fmt(f),
TyKind::Empty => "()".fmt(f),
TyKind::Path(v) => v.fmt(f),
TyKind::Array(v) => v.fmt(f),
TyKind::Slice(v) => v.fmt(f),
TyKind::Tuple(v) => v.fmt(f),
TyKind::Ref(v) => v.fmt(f),
TyKind::Fn(v) => v.fmt(f),
}
}
}
impl Display for TyArray {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { ty, count } = self;
write!(f, "[{ty}; {count}]")
}
}
impl Display for TySlice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { ty } = self;
write!(f, "[{ty}]")
}
}
impl Display for TyTuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.types, ", ")(f.delimit(INLINE_PARENS))
}
}
impl Display for TyRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let &Self { count, mutable, ref to } = self;
for _ in 0..count {
f.write_char('&')?;
}
write!(f, "{mutable}{to}")
}
}
impl Display for TyFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { args, rety } = self;
write!(f, "fn {args}")?;
match rety {
Some(v) => write!(f, " -> {v}"),
None => Ok(()),
}
}
}
impl Display for Path {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { absolute, parts } = self;
if *absolute {
"::".fmt(f)?;
}
separate(parts, "::")(f)
}
}
impl Display for PathPart {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PathPart::SuperKw => "super".fmt(f),
PathPart::SelfKw => "self".fmt(f),
PathPart::SelfTy => "Self".fmt(f),
PathPart::Ident(id) => id.fmt(f),
}
}
}
impl Display for Stmt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Stmt { extents: _, kind, semi } = self;
write!(f, "{kind}{semi}")
}
}
impl Display for StmtKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StmtKind::Empty => Ok(()),
StmtKind::Local(v) => v.fmt(f),
StmtKind::Item(v) => v.fmt(f),
StmtKind::Expr(v) => v.fmt(f),
}
}
}
impl Display for Semi {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Semi::Terminated => ';'.fmt(f),
Semi::Unterminated => Ok(()),
}
}
}
impl Display for Let {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutable, name, ty, init } = self;
write!(f, "let {mutable}{name}")?;
if let Some(value) = ty {
write!(f, ": {value}")?;
}
if let Some(value) = init {
write!(f, " = {value}")?;
}
Ok(())
}
}
impl Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)
}
}
impl Display for ExprKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ExprKind::Empty => "()".fmt(f),
ExprKind::Assign(v) => v.fmt(f),
ExprKind::Modify(v) => v.fmt(f),
ExprKind::Binary(v) => v.fmt(f),
ExprKind::Unary(v) => v.fmt(f),
ExprKind::Member(v) => v.fmt(f),
ExprKind::Index(v) => v.fmt(f),
ExprKind::Structor(v) => v.fmt(f),
ExprKind::Path(v) => v.fmt(f),
ExprKind::Literal(v) => v.fmt(f),
ExprKind::Array(v) => v.fmt(f),
ExprKind::ArrayRep(v) => v.fmt(f),
ExprKind::AddrOf(v) => v.fmt(f),
ExprKind::Block(v) => v.fmt(f),
ExprKind::Group(v) => v.fmt(f),
ExprKind::Tuple(v) => v.fmt(f),
ExprKind::Loop(v) => v.fmt(f),
ExprKind::While(v) => v.fmt(f),
ExprKind::If(v) => v.fmt(f),
ExprKind::For(v) => v.fmt(f),
ExprKind::Break(v) => v.fmt(f),
ExprKind::Return(v) => v.fmt(f),
ExprKind::Continue(_) => "continue".fmt(f),
}
}
}
impl Display for Assign {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { parts } = self;
write!(f, "{} = {}", parts.0, parts.1)
}
}
impl Display for Modify {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { kind, parts } = self;
write!(f, "{} {kind} {}", parts.0, parts.1)
}
}
impl Display for ModifyKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ModifyKind::Mul => "*=",
ModifyKind::Div => "/=",
ModifyKind::Rem => "%=",
ModifyKind::Add => "+=",
ModifyKind::Sub => "-=",
ModifyKind::And => "&=",
ModifyKind::Or => "|=",
ModifyKind::Xor => "^=",
ModifyKind::Shl => "<<=",
ModifyKind::Shr => ">>=",
}
.fmt(f)
}
}
impl Display for Binary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { kind, parts } = self;
let (head, tail) = parts.borrow();
match kind {
BinaryKind::Call => write!(f, "{head}{tail}"),
_ => write!(f, "{head} {kind} {tail}"),
}
}
}
impl Display for BinaryKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BinaryKind::Lt => "<",
BinaryKind::LtEq => "<=",
BinaryKind::Equal => "==",
BinaryKind::NotEq => "!=",
BinaryKind::GtEq => ">=",
BinaryKind::Gt => ">",
BinaryKind::RangeExc => "..",
BinaryKind::RangeInc => "..=",
BinaryKind::LogAnd => "&&",
BinaryKind::LogOr => "||",
BinaryKind::LogXor => "^^",
BinaryKind::BitAnd => "&",
BinaryKind::BitOr => "|",
BinaryKind::BitXor => "^",
BinaryKind::Shl => "<<",
BinaryKind::Shr => ">>",
BinaryKind::Add => "+",
BinaryKind::Sub => "-",
BinaryKind::Mul => "*",
BinaryKind::Div => "/",
BinaryKind::Rem => "%",
BinaryKind::Call => "()",
}
.fmt(f)
}
}
impl Display for Unary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { kind, tail } = self;
write!(f, "{kind}{tail}")
}
}
impl Display for UnaryKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UnaryKind::Deref => "*",
UnaryKind::Neg => "-",
UnaryKind::Not => "!",
UnaryKind::At => "@",
UnaryKind::Tilde => "~",
}
.fmt(f)
}
}
impl Display for Member {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, kind } = self;
write!(f, "{head}.{kind}")
}
}
impl Display for MemberKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MemberKind::Call(name, args) => write!(f, "{name}{args}"),
MemberKind::Struct(name) => write!(f, "{name}"),
MemberKind::Tuple(name) => write!(f, "{name}"),
}
}
}
impl Display for Index {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, indices } = self;
write!(f, "{head}")?;
separate(indices, ", ")(f.delimit(INLINE_SQUARE))
}
}
impl Display for Structor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { to, init } = self;
write!(f, "{to}: ")?;
separate(init, ", ")(f.delimit(INLINE_BRACES))
}
}
impl Display for Fielder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, init } = self;
write!(f, "{name}")?;
if let Some(init) = init {
write!(f, ": {init}")?;
}
Ok(())
}
}
impl Display for Array {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.values, ", ")(f.delimit(INLINE_SQUARE))
}
}
impl Display for ArrayRep {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { value, repeat } = self;
write!(f, "[{value}; {repeat}]")
}
}
impl Display for AddrOf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { count, mutable, expr } = self;
for _ in 0..*count {
f.write_char('&')?;
}
write!(f, "{mutable}{expr}")
}
}
impl Display for Block {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.stmts, "\n")(f.delimit(BRACES))
}
}
impl Display for Group {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({})", self.expr)
}
}
impl Display for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.exprs, ", ")(f.delimit(INLINE_PARENS))
}
}
impl Display for Loop {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { body } = self;
write!(f, "loop {body}")
}
}
impl Display for While {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { cond, pass, fail } = self;
write!(f, "while {cond} {pass}{fail}")
}
}
impl Display for If {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { cond, pass, fail } = self;
write!(f, "if {cond} {pass}{fail}")
}
}
impl Display for For {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { bind, cond, pass, fail } = self;
write!(f, "for {bind} in {cond} {pass}{fail}")
}
}
impl Display for Else {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.body {
Some(body) => write!(f, " else {body}"),
_ => Ok(()),
}
}
}
impl Display for Break {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "break")?;
match &self.body {
Some(body) => write!(f, " {body}"),
_ => Ok(()),
}
}
}
impl Display for Return {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "return")?;
match &self.body {
Some(body) => write!(f, " {body}"),
_ => Ok(()),
}
}
}
impl Display for Continue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
"continue".fmt(f)
}
}
}
mod convert {
//! Converts between major enums and enum variants
use super::*;
impl<T: AsRef<str>> From<T> for PathPart {
fn from(value: T) -> Self {
match value.as_ref() {
"self" => PathPart::SelfKw,
"super" => PathPart::SuperKw,
ident => PathPart::Ident(ident.into()),
}
}
}
macro impl_from ($(impl From for $T:ty {$($from:ty => $to:expr),*$(,)?})*) {$($(
impl From<$from> for $T {
fn from(value: $from) -> Self {
$to(value.into()) // Uses *tuple constructor*
}
}
impl From<Box<$from>> for $T {
fn from(value: Box<$from>) -> Self {
$to((*value).into())
}
}
)*)*}
impl_from! {
impl From for ItemKind {
Alias => ItemKind::Alias,
Const => ItemKind::Const,
Static => ItemKind::Static,
Module => ItemKind::Module,
Function => ItemKind::Function,
Struct => ItemKind::Struct,
Enum => ItemKind::Enum,
Impl => ItemKind::Impl,
Use => ItemKind::Use,
}
impl From for StructKind {
Vec<Ty> => StructKind::Tuple,
// TODO: Struct members in struct
}
impl From for EnumKind {
Vec<Variant> => EnumKind::Variants,
}
impl From for VariantKind {
u128 => VariantKind::CLike,
Ty => VariantKind::Tuple,
// TODO: enum struct variants
}
impl From for TyKind {
Path => TyKind::Path,
TyTuple => TyKind::Tuple,
TyRef => TyKind::Ref,
TyFn => TyKind::Fn,
}
impl From for StmtKind {
Let => StmtKind::Local,
Item => StmtKind::Item,
Expr => StmtKind::Expr,
}
impl From for ExprKind {
Assign => ExprKind::Assign,
Modify => ExprKind::Modify,
Binary => ExprKind::Binary,
Unary => ExprKind::Unary,
Member => ExprKind::Member,
Index => ExprKind::Index,
Path => ExprKind::Path,
Literal => ExprKind::Literal,
Array => ExprKind::Array,
ArrayRep => ExprKind::ArrayRep,
AddrOf => ExprKind::AddrOf,
Block => ExprKind::Block,
Group => ExprKind::Group,
Tuple => ExprKind::Tuple,
Loop => ExprKind::Loop,
While => ExprKind::While,
If => ExprKind::If,
For => ExprKind::For,
Break => ExprKind::Break,
Return => ExprKind::Return,
Continue => ExprKind::Continue,
}
impl From for Literal {
bool => Literal::Bool,
char => Literal::Char,
u128 => Literal::Int,
String => Literal::String,
}
}
impl From<Option<Expr>> for Else {
fn from(value: Option<Expr>) -> Self {
Self { body: value.map(Into::into) }
}
}
impl From<Expr> for Else {
fn from(value: Expr) -> Self {
Self { body: Some(value.into()) }
}
}
}
mod path {
//! Utils for [Path]
use crate::{ast::Path, PathPart, Sym};
impl Path {
/// Appends a [PathPart] to this [Path]
pub fn push(&mut self, part: PathPart) {
self.parts.push(part);
}
/// Removes a [PathPart] from this [Path]
pub fn pop(&mut self) -> Option<PathPart> {
self.parts.pop()
}
/// Concatenates `self::other`. If `other` is an absolute [Path],
/// this replaces `self` with `other`
pub fn concat(mut self, other: &Self) -> Self {
if other.absolute {
other.clone()
} else {
self.parts.extend(other.parts.iter().cloned());
self
}
}
}
impl PathPart {
pub fn from_sym(ident: Sym) -> Self {
Self::Ident(ident)
}
}
}
pub(crate) mod weight_of;

View File

@@ -0,0 +1,162 @@
//! Converts between major enums and enum variants
use super::*;
impl<T: AsRef<str>> From<T> for PathPart {
fn from(value: T) -> Self {
match value.as_ref() {
"super" => PathPart::SuperKw,
ident => PathPart::Ident(ident.into()),
}
}
}
macro impl_from ($(impl From for $T:ty {$($from:ty => $to:expr),*$(,)?})*) {$($(
impl From<$from> for $T {
fn from(value: $from) -> Self {
$to(value.into()) // Uses *tuple constructor*
}
}
impl From<Box<$from>> for $T {
fn from(value: Box<$from>) -> Self {
$to((*value).into())
}
}
)*)*}
impl_from! {
impl From for ItemKind {
Alias => ItemKind::Alias,
Const => ItemKind::Const,
Static => ItemKind::Static,
Module => ItemKind::Module,
Function => ItemKind::Function,
Struct => ItemKind::Struct,
Enum => ItemKind::Enum,
Impl => ItemKind::Impl,
Use => ItemKind::Use,
}
impl From for StructKind {
Vec<Ty> => StructKind::Tuple,
// TODO: Struct members in struct
}
impl From for TyKind {
Path => TyKind::Path,
TyTuple => TyKind::Tuple,
TyRef => TyKind::Ref,
TyPtr => TyKind::Ptr,
TyFn => TyKind::Fn,
}
impl From for StmtKind {
Item => StmtKind::Item,
Expr => StmtKind::Expr,
}
impl From for ExprKind {
Let => ExprKind::Let,
Closure => ExprKind::Closure,
Quote => ExprKind::Quote,
Match => ExprKind::Match,
Assign => ExprKind::Assign,
Modify => ExprKind::Modify,
Binary => ExprKind::Binary,
Unary => ExprKind::Unary,
Cast => ExprKind::Cast,
Member => ExprKind::Member,
Index => ExprKind::Index,
Path => ExprKind::Path,
Literal => ExprKind::Literal,
Array => ExprKind::Array,
ArrayRep => ExprKind::ArrayRep,
AddrOf => ExprKind::AddrOf,
Block => ExprKind::Block,
Group => ExprKind::Group,
Tuple => ExprKind::Tuple,
While => ExprKind::While,
If => ExprKind::If,
For => ExprKind::For,
Break => ExprKind::Break,
Return => ExprKind::Return,
}
impl From for Literal {
bool => Literal::Bool,
char => Literal::Char,
u128 => Literal::Int,
String => Literal::String,
}
}
impl From<Option<Expr>> for Else {
fn from(value: Option<Expr>) -> Self {
Self { body: value.map(Into::into) }
}
}
impl From<Expr> for Else {
fn from(value: Expr) -> Self {
Self { body: Some(value.into()) }
}
}
impl TryFrom<Expr> for Pattern {
type Error = Expr;
/// Performs the conversion. On failure, returns the *first* non-pattern subexpression.
fn try_from(value: Expr) -> Result<Self, Self::Error> {
Ok(match value.kind {
ExprKind::Literal(literal) => Pattern::Literal(literal),
ExprKind::Path(Path { absolute: false, ref parts }) => match parts.as_slice() {
[PathPart::Ident(name)] => Pattern::Name(*name),
_ => Err(value)?,
},
ExprKind::Empty => Pattern::Tuple(vec![]),
ExprKind::Group(Group { expr }) => Pattern::Tuple(vec![Pattern::try_from(*expr)?]),
ExprKind::Tuple(Tuple { exprs }) => Pattern::Tuple(
exprs
.into_iter()
.map(Pattern::try_from)
.collect::<Result<_, _>>()?,
),
ExprKind::AddrOf(AddrOf { mutable, expr }) => {
Pattern::Ref(mutable, Box::new(Pattern::try_from(*expr)?))
}
ExprKind::Array(Array { values }) => Pattern::Array(
values
.into_iter()
.map(Pattern::try_from)
.collect::<Result<_, _>>()?,
),
ExprKind::Binary(Binary { kind: BinaryKind::Call, parts }) => {
let (Expr { kind: ExprKind::Path(path), .. }, args) = *parts else {
return Err(parts.0);
};
match args.kind {
ExprKind::Empty | ExprKind::Tuple(_) => {}
_ => return Err(args),
}
let Pattern::Tuple(args) = Pattern::try_from(args)? else {
unreachable!("Arguments should be convertible to pattern!")
};
Pattern::TupleStruct(path, args)
}
ExprKind::Binary(Binary { kind: BinaryKind::RangeExc, parts }) => {
let (head, tail) = (Pattern::try_from(parts.0)?, Pattern::try_from(parts.1)?);
Pattern::RangeExc(head.into(), tail.into())
}
ExprKind::Binary(Binary { kind: BinaryKind::RangeInc, parts }) => {
let (head, tail) = (Pattern::try_from(parts.0)?, Pattern::try_from(parts.1)?);
Pattern::RangeInc(head.into(), tail.into())
}
ExprKind::Unary(Unary { kind: UnaryKind::RangeExc, tail }) => {
Pattern::Rest(Some(Pattern::try_from(*tail)?.into()))
}
ExprKind::Structor(Structor { to, init }) => {
let fields = init
.into_iter()
.map(|Fielder { name, init }| {
Ok((name, init.map(|i| Pattern::try_from(*i)).transpose()?))
})
.collect::<Result<_, Self::Error>>()?;
Pattern::Struct(to, fields)
}
_ => Err(value)?,
})
}
}

View File

@@ -0,0 +1,773 @@
//! Implements [Display] for [AST](super::super) Types
use super::*;
use format::{delimiters::*, *};
use std::{
borrow::Borrow,
fmt::{Display, Write},
};
fn separate<I: Display, W: Write>(
iterable: impl IntoIterator<Item = I>,
sep: &'static str,
) -> impl FnOnce(W) -> std::fmt::Result {
move |mut f| {
for (idx, item) in iterable.into_iter().enumerate() {
if idx > 0 {
f.write_str(sep)?;
}
write!(f, "{item}")?;
}
Ok(())
}
}
impl Display for Mutability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Mutability::Not => Ok(()),
Mutability::Mut => "mut ".fmt(f),
}
}
}
impl Display for Visibility {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Visibility::Private => Ok(()),
Visibility::Public => "pub ".fmt(f),
}
}
}
impl Display for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Literal::Bool(v) => v.fmt(f),
Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
Literal::Int(v) => v.fmt(f),
Literal::Float(v) => write!(f, "{:?}", f64::from_bits(*v)),
Literal::String(v) => write!(f, "\"{}\"", v.escape_debug()),
}
}
}
impl Display for File {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.items, "\n\n")(f)
}
}
impl Display for Attrs {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { meta } = self;
if meta.is_empty() {
return Ok(());
}
"#".fmt(f)?;
separate(meta, ", ")(&mut f.delimit(INLINE_SQUARE))?;
"\n".fmt(f)
}
}
impl Display for Meta {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind } = self;
write!(f, "{name}{kind}")
}
}
impl Display for MetaKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MetaKind::Plain => Ok(()),
MetaKind::Equals(v) => write!(f, " = {v}"),
MetaKind::Func(args) => separate(args, ", ")(f.delimit(INLINE_PARENS)),
}
}
}
impl Display for Item {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { span: _, attrs, vis, kind } = self;
attrs.fmt(f)?;
vis.fmt(f)?;
kind.fmt(f)
}
}
impl Display for ItemKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ItemKind::Alias(v) => v.fmt(f),
ItemKind::Const(v) => v.fmt(f),
ItemKind::Static(v) => v.fmt(f),
ItemKind::Module(v) => v.fmt(f),
ItemKind::Function(v) => v.fmt(f),
ItemKind::Struct(v) => v.fmt(f),
ItemKind::Enum(v) => v.fmt(f),
ItemKind::Impl(v) => v.fmt(f),
ItemKind::Use(v) => v.fmt(f),
}
}
}
impl Display for Generics {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Generics { vars } = self;
if !vars.is_empty() {
separate(vars, ", ")(f.delimit_with("<", ">"))?
}
Ok(())
}
}
impl Display for Alias {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, from } = self;
match from {
Some(from) => write!(f, "type {name} = {from};"),
None => write!(f, "type {name};"),
}
}
}
impl Display for Const {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, ty, init } = self;
write!(f, "const {name}: {ty} = {init}")
}
}
impl Display for Static {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutable, name, ty, init } = self;
write!(f, "static {mutable}{name}: {ty} = {init}")
}
}
impl Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, file } = self;
write!(f, "mod {name}")?;
match file {
Some(items) => {
' '.fmt(f)?;
write!(f.delimit(BRACES), "{items}")
}
None => Ok(()),
}
}
}
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, gens, sign: sign @ TyFn { args, rety }, bind, body } = self;
let types = match args.kind {
TyKind::Tuple(TyTuple { ref types }) => types.as_slice(),
_ => {
write!(f, "Invalid function signature: {sign}")?;
Default::default()
}
};
let bind = match bind {
Pattern::Tuple(patterns) => patterns.as_slice(),
_ => {
write!(f, "Invalid argument binder: {bind}")?;
Default::default()
}
};
debug_assert_eq!(bind.len(), types.len());
write!(f, "fn {name}{gens} ")?;
{
let mut f = f.delimit(INLINE_PARENS);
for (idx, (arg, ty)) in bind.iter().zip(types.iter()).enumerate() {
if idx != 0 {
f.write_str(", ")?;
}
write!(f, "{arg}: {ty}")?;
}
}
if let TyKind::Tuple(TyTuple { types }) = &rety.kind
&& !types.as_slice().is_empty()
{
write!(f, " -> {rety}")?
}
match body {
Some(body) => write!(f, " {body}"),
None => ';'.fmt(f),
}
}
}
impl Display for Struct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, gens, kind } = self;
write!(f, "struct {name}{gens}{kind}")
}
}
impl Display for StructKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StructKind::Empty => ';'.fmt(f),
StructKind::Tuple(v) => separate(v, ", ")(f.delimit(INLINE_PARENS)),
StructKind::Struct(v) => separate(v, ",\n")(f.delimit(SPACED_BRACES)),
}
}
}
impl Display for StructMember {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { vis, name, ty } = self;
write!(f, "{vis}{name}: {ty}")
}
}
impl Display for Enum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, gens, variants } = self;
write!(f, "enum {name}{gens}")?;
separate(variants, ",\n")(f.delimit(SPACED_BRACES))
}
}
impl Display for Variant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, kind, body } = self;
write!(f, "{name}{kind}")?;
match body {
Some(body) => write!(f, " {body}"),
None => Ok(()),
}
}
}
impl Display for Impl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { gens, target, body } = self;
write!(f, "impl{gens} {target} ")?;
write!(f.delimit(BRACES), "{body}")
}
}
impl Display for ImplKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ImplKind::Type(t) => t.fmt(f),
ImplKind::Trait { impl_trait, for_type } => {
write!(f, "{impl_trait} for {for_type}")
}
}
}
}
impl Display for Use {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { absolute, tree } = self;
f.write_str(if *absolute { "use ::" } else { "use " })?;
write!(f, "{tree};")
}
}
impl Display for UseTree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UseTree::Tree(tree) => separate(tree, ", ")(f.delimit(INLINE_BRACES)),
UseTree::Path(path, rest) => write!(f, "{path}::{rest}"),
UseTree::Alias(path, name) => write!(f, "{path} as {name}"),
UseTree::Name(name) => write!(f, "{name}"),
UseTree::Glob => write!(f, "*"),
}
}
}
impl Display for Ty {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { span: _, kind, gens } = self;
write!(f, "{kind}{gens}")
}
}
impl Display for TyKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TyKind::Never => "!".fmt(f),
TyKind::Infer => "_".fmt(f),
TyKind::Path(v) => v.fmt(f),
TyKind::Array(v) => v.fmt(f),
TyKind::Slice(v) => v.fmt(f),
TyKind::Tuple(v) => v.fmt(f),
TyKind::Ref(v) => v.fmt(f),
TyKind::Ptr(v) => v.fmt(f),
TyKind::Fn(v) => v.fmt(f),
}
}
}
impl Display for TyArray {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { ty, count } = self;
write!(f, "[{ty}; {count}]")
}
}
impl Display for TySlice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { ty } = self;
write!(f, "[{ty}]")
}
}
impl Display for TyTuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.types, ", ")(f.delimit(INLINE_PARENS))
}
}
impl Display for TyRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let &Self { count, mutable, ref to } = self;
for _ in 0..count {
f.write_char('&')?;
}
write!(f, "{mutable}{to}")
}
}
impl Display for TyPtr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { to } = self;
write!(f, "*{to}")
}
}
impl Display for TyFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { args, rety } = self;
write!(f, "fn {args}")?;
if let TyKind::Tuple(TyTuple { types }) = &rety.kind
&& !types.as_slice().is_empty()
{
write!(f, " -> {rety}")?
}
Ok(())
}
}
impl Display for Path {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { absolute, parts } = self;
if *absolute {
"::".fmt(f)?;
}
separate(parts, "::")(f)
}
}
impl Display for PathPart {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PathPart::SuperKw => "super".fmt(f),
PathPart::SelfTy => "Self".fmt(f),
PathPart::Ident(id) => id.fmt(f),
}
}
}
impl Display for Stmt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Stmt { span: _, kind, semi } = self;
write!(f, "{kind}{semi}")
}
}
impl Display for StmtKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StmtKind::Empty => Ok(()),
StmtKind::Item(v) => v.fmt(f),
StmtKind::Expr(v) => v.fmt(f),
}
}
}
impl Display for Semi {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Semi::Terminated => ';'.fmt(f),
Semi::Unterminated => Ok(()),
}
}
}
impl Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)
}
}
impl Display for ExprKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ExprKind::Empty => "()".fmt(f),
ExprKind::Closure(v) => v.fmt(f),
ExprKind::Quote(v) => v.fmt(f),
ExprKind::Let(v) => v.fmt(f),
ExprKind::Match(v) => v.fmt(f),
ExprKind::Assign(v) => v.fmt(f),
ExprKind::Modify(v) => v.fmt(f),
ExprKind::Binary(v) => v.fmt(f),
ExprKind::Unary(v) => v.fmt(f),
ExprKind::Cast(v) => v.fmt(f),
ExprKind::Member(v) => v.fmt(f),
ExprKind::Index(v) => v.fmt(f),
ExprKind::Structor(v) => v.fmt(f),
ExprKind::Path(v) => v.fmt(f),
ExprKind::Literal(v) => v.fmt(f),
ExprKind::Array(v) => v.fmt(f),
ExprKind::ArrayRep(v) => v.fmt(f),
ExprKind::AddrOf(v) => v.fmt(f),
ExprKind::Block(v) => v.fmt(f),
ExprKind::Group(v) => v.fmt(f),
ExprKind::Tuple(v) => v.fmt(f),
ExprKind::While(v) => v.fmt(f),
ExprKind::If(v) => v.fmt(f),
ExprKind::For(v) => v.fmt(f),
ExprKind::Break(v) => v.fmt(f),
ExprKind::Return(v) => v.fmt(f),
ExprKind::Continue => "continue".fmt(f),
}
}
}
impl Display for Closure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { arg, body } = self;
match arg.as_ref() {
Pattern::Tuple(args) => separate(args, ", ")(f.delimit_with("|", "|")),
_ => arg.fmt(f),
}?;
write!(f, " {body}")
}
}
impl Display for Quote {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { quote } = self;
write!(f, "`{quote}`")
}
}
impl Display for Let {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutable, name, ty, init } = self;
write!(f, "let {mutable}{name}")?;
if let Some(value) = ty {
write!(f, ": {value}")?;
}
if let Some(value) = init {
write!(f, " = {value}")?;
}
Ok(())
}
}
impl Display for Pattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Pattern::Name(sym) => sym.fmt(f),
Pattern::Path(path) => path.fmt(f),
Pattern::Literal(literal) => literal.fmt(f),
Pattern::Rest(Some(name)) => write!(f, "..{name}"),
Pattern::Rest(None) => "..".fmt(f),
Pattern::Ref(mutability, pattern) => write!(f, "&{mutability}{pattern}"),
Pattern::RangeExc(head, tail) => write!(f, "{head}..{tail}"),
Pattern::RangeInc(head, tail) => write!(f, "{head}..={tail}"),
Pattern::Tuple(patterns) => separate(patterns, ", ")(f.delimit(INLINE_PARENS)),
Pattern::Array(patterns) => separate(patterns, ", ")(f.delimit(INLINE_SQUARE)),
Pattern::Struct(path, items) => {
write!(f, "{path} ")?;
let f = &mut f.delimit(INLINE_BRACES);
for (idx, (name, item)) in items.iter().enumerate() {
if idx != 0 {
f.write_str(", ")?;
}
write!(f, "{name}")?;
if let Some(pattern) = item {
write!(f, ": {pattern}")?
}
}
Ok(())
}
Pattern::TupleStruct(path, items) => {
write!(f, "{path}")?;
separate(items, ", ")(f.delimit(INLINE_PARENS))
}
}
}
}
impl Display for Match {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { scrutinee, arms } = self;
write!(f, "match {scrutinee} ")?;
separate(arms, ",\n")(f.delimit(BRACES))
}
}
impl Display for MatchArm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self(pat, expr) = self;
write!(f, "{pat} => {expr}")
}
}
impl Display for Assign {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { parts } = self;
write!(f, "{} = {}", parts.0, parts.1)
}
}
impl Display for Modify {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { kind, parts } = self;
write!(f, "{} {kind} {}", parts.0, parts.1)
}
}
impl Display for ModifyKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ModifyKind::Mul => "*=",
ModifyKind::Div => "/=",
ModifyKind::Rem => "%=",
ModifyKind::Add => "+=",
ModifyKind::Sub => "-=",
ModifyKind::And => "&=",
ModifyKind::Or => "|=",
ModifyKind::Xor => "^=",
ModifyKind::Shl => "<<=",
ModifyKind::Shr => ">>=",
}
.fmt(f)
}
}
impl Display for Binary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { kind, parts } = self;
let (head, tail) = parts.borrow();
match kind {
BinaryKind::Call => write!(f, "{head}{tail}"),
_ => write!(f, "{head} {kind} {tail}"),
}
}
}
impl Display for BinaryKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BinaryKind::Lt => "<",
BinaryKind::LtEq => "<=",
BinaryKind::Equal => "==",
BinaryKind::NotEq => "!=",
BinaryKind::GtEq => ">=",
BinaryKind::Gt => ">",
BinaryKind::RangeExc => "..",
BinaryKind::RangeInc => "..=",
BinaryKind::LogAnd => "&&",
BinaryKind::LogOr => "||",
BinaryKind::LogXor => "^^",
BinaryKind::BitAnd => "&",
BinaryKind::BitOr => "|",
BinaryKind::BitXor => "^",
BinaryKind::Shl => "<<",
BinaryKind::Shr => ">>",
BinaryKind::Add => "+",
BinaryKind::Sub => "-",
BinaryKind::Mul => "*",
BinaryKind::Div => "/",
BinaryKind::Rem => "%",
BinaryKind::Call => "()",
}
.fmt(f)
}
}
impl Display for Unary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { kind, tail } = self;
write!(f, "{kind}{tail}")
}
}
impl Display for UnaryKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UnaryKind::Loop => "loop ",
UnaryKind::Deref => "*",
UnaryKind::Neg => "-",
UnaryKind::Not => "!",
UnaryKind::RangeExc => "..",
UnaryKind::RangeInc => "..=",
UnaryKind::At => "@",
UnaryKind::Tilde => "~",
}
.fmt(f)
}
}
impl Display for Cast {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, ty } = self;
write!(f, "{head} as {ty}")
}
}
impl Display for Member {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, kind } = self;
write!(f, "{head}.{kind}")
}
}
impl Display for MemberKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MemberKind::Call(name, args) => write!(f, "{name}{args}"),
MemberKind::Struct(name) => write!(f, "{name}"),
MemberKind::Tuple(name) => write!(f, "{name}"),
}
}
}
impl Display for Index {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, indices } = self;
write!(f, "{head}")?;
separate(indices, ", ")(f.delimit(INLINE_SQUARE))
}
}
impl Display for Structor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { to, init } = self;
write!(f, "{to} ")?;
separate(init, ", ")(f.delimit(INLINE_BRACES))
}
}
impl Display for Fielder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, init } = self;
write!(f, "{name}")?;
if let Some(init) = init {
write!(f, ": {init}")?;
}
Ok(())
}
}
impl Display for Array {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.values, ", ")(f.delimit(INLINE_SQUARE))
}
}
impl Display for ArrayRep {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { value, repeat } = self;
write!(f, "[{value}; {repeat}]")
}
}
impl Display for AddrOf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { mutable, expr } = self;
write!(f, "&{mutable}{expr}")
}
}
impl Display for Block {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { stmts } = self;
match stmts.as_slice() {
[] => "{}".fmt(f),
stmts => separate(stmts, "\n")(f.delimit(BRACES)),
}
}
}
impl Display for Group {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({})", self.expr)
}
}
impl Display for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { exprs } = self;
match exprs.as_slice() {
[] => write!(f, "()"),
[expr] => write!(f, "({expr},)"),
exprs => separate(exprs, ", ")(f.delimit(INLINE_PARENS)),
}
}
}
impl Display for While {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { cond, pass, fail } = self;
write!(f, "while {cond} {pass}{fail}")
}
}
impl Display for If {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { cond, pass, fail } = self;
write!(f, "if {cond} {pass}{fail}")
}
}
impl Display for For {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { bind, cond, pass, fail } = self;
write!(f, "for {bind} in {cond} {pass}{fail}")
}
}
impl Display for Else {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.body {
Some(body) => write!(f, " else {body}"),
_ => Ok(()),
}
}
}
impl Display for Break {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "break")?;
match &self.body {
Some(body) => write!(f, " {body}"),
_ => Ok(()),
}
}
}
impl Display for Return {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "return")?;
match &self.body {
Some(body) => write!(f, " {body}"),
_ => Ok(()),
}
}
}

View File

@@ -0,0 +1,60 @@
//! Utils for [Path]
use crate::{PathPart, Sym, ast::Path};
impl Path {
/// Appends a [PathPart] to this [Path]
pub fn push(&mut self, part: PathPart) {
self.parts.push(part);
}
/// Removes a [PathPart] from this [Path]
pub fn pop(&mut self) -> Option<PathPart> {
self.parts.pop()
}
/// Concatenates `self::other`. If `other` is an absolute [Path],
/// this replaces `self` with `other`
pub fn concat(mut self, other: &Self) -> Self {
if other.absolute {
other.clone()
} else {
self.parts.extend(other.parts.iter().cloned());
self
}
}
/// Gets the defining [Sym] of this path
pub fn as_sym(&self) -> Option<Sym> {
match self.parts.as_slice() {
[.., PathPart::Ident(name)] => Some(*name),
_ => None,
}
}
/// Checks whether this path ends in the given [Sym]
pub fn ends_with(&self, name: &str) -> bool {
match self.parts.as_slice() {
[.., PathPart::Ident(last)] => name == &**last,
_ => false,
}
}
/// Checks whether this path refers to the sinkhole identifier, `_`
pub fn is_sinkhole(&self) -> bool {
if let [PathPart::Ident(id)] = self.parts.as_slice()
&& let "_" = id.to_ref()
{
return true;
}
false
}
}
impl PathPart {
pub fn from_sym(ident: Sym) -> Self {
Self::Ident(ident)
}
}
impl From<Sym> for Path {
fn from(value: Sym) -> Self {
Self { parts: vec![PathPart::Ident(value)], absolute: false }
}
}

View File

@@ -0,0 +1,617 @@
//! Approximates the size of an AST
use std::mem::size_of_val;
use crate::ast::*;
use cl_structures::{intern::interned::Interned, span::Span};
/// Approximates the size of an AST without including indirection (pointers) or padding
pub trait WeightOf {
/// Approximates the size of a syntax tree without including pointer/indirection or padding.
fn weight_of(&self) -> usize;
}
impl WeightOf for File {
fn weight_of(&self) -> usize {
let Self { name, items } = self;
name.weight_of() + items.weight_of()
}
}
impl WeightOf for Attrs {
fn weight_of(&self) -> usize {
let Self { meta } = self;
meta.weight_of()
}
}
impl WeightOf for Meta {
fn weight_of(&self) -> usize {
let Self { name, kind } = self;
name.weight_of() + kind.weight_of()
}
}
impl WeightOf for MetaKind {
fn weight_of(&self) -> usize {
match self {
MetaKind::Plain => size_of_val(self),
MetaKind::Equals(v) => v.weight_of(),
MetaKind::Func(v) => v.weight_of(),
}
}
}
impl WeightOf for Item {
fn weight_of(&self) -> usize {
let Self { span, attrs, vis, kind } = self;
span.weight_of() + attrs.weight_of() + vis.weight_of() + kind.weight_of()
}
}
impl WeightOf for ItemKind {
fn weight_of(&self) -> usize {
match self {
ItemKind::Module(v) => v.weight_of(),
ItemKind::Alias(v) => v.weight_of(),
ItemKind::Enum(v) => v.weight_of(),
ItemKind::Struct(v) => v.weight_of(),
ItemKind::Const(v) => v.weight_of(),
ItemKind::Static(v) => v.weight_of(),
ItemKind::Function(v) => v.weight_of(),
ItemKind::Impl(v) => v.weight_of(),
ItemKind::Use(v) => v.weight_of(),
}
}
}
impl WeightOf for Generics {
fn weight_of(&self) -> usize {
let Self { vars } = self;
vars.iter().map(|v| v.weight_of()).sum()
}
}
impl WeightOf for Module {
fn weight_of(&self) -> usize {
let Self { name, file } = self;
name.weight_of() + file.weight_of()
}
}
impl WeightOf for Alias {
fn weight_of(&self) -> usize {
let Self { name, from } = self;
name.weight_of() + from.weight_of()
}
}
impl WeightOf for Const {
fn weight_of(&self) -> usize {
let Self { name, ty, init } = self;
name.weight_of() + ty.weight_of() + init.weight_of()
}
}
impl WeightOf for Static {
fn weight_of(&self) -> usize {
let Self { mutable, name, ty, init } = self;
mutable.weight_of() + name.weight_of() + ty.weight_of() + init.weight_of()
}
}
impl WeightOf for Function {
fn weight_of(&self) -> usize {
let Self { name, gens, sign, bind, body } = self;
name.weight_of() + gens.weight_of() + sign.weight_of() + bind.weight_of() + body.weight_of()
}
}
impl WeightOf for Struct {
fn weight_of(&self) -> usize {
let Self { name, gens, kind } = self;
name.weight_of() + gens.weight_of() + kind.weight_of()
}
}
impl WeightOf for StructKind {
fn weight_of(&self) -> usize {
match self {
StructKind::Empty => size_of_val(self),
StructKind::Tuple(items) => items.weight_of(),
StructKind::Struct(sm) => sm.weight_of(),
}
}
}
impl WeightOf for StructMember {
fn weight_of(&self) -> usize {
let Self { vis, name, ty } = self;
vis.weight_of() + name.weight_of() + ty.weight_of()
}
}
impl WeightOf for Enum {
fn weight_of(&self) -> usize {
let Self { name, gens, variants } = self;
name.weight_of() + gens.weight_of() + variants.weight_of()
}
}
impl WeightOf for Variant {
fn weight_of(&self) -> usize {
let Self { name, kind, body } = self;
name.weight_of() + kind.weight_of() + body.weight_of()
}
}
impl WeightOf for Impl {
fn weight_of(&self) -> usize {
let Self { gens, target, body } = self;
gens.weight_of() + target.weight_of() + body.weight_of()
}
}
impl WeightOf for ImplKind {
fn weight_of(&self) -> usize {
match self {
ImplKind::Type(ty) => ty.weight_of(),
ImplKind::Trait { impl_trait, for_type } => {
impl_trait.weight_of() + for_type.weight_of()
}
}
}
}
impl WeightOf for Use {
fn weight_of(&self) -> usize {
let Self { absolute, tree } = self;
absolute.weight_of() + tree.weight_of()
}
}
impl WeightOf for UseTree {
fn weight_of(&self) -> usize {
match self {
UseTree::Tree(tr) => tr.weight_of(),
UseTree::Path(pa, tr) => pa.weight_of() + tr.weight_of(),
UseTree::Alias(src, dst) => src.weight_of() + dst.weight_of(),
UseTree::Name(src) => src.weight_of(),
UseTree::Glob => size_of_val(self),
}
}
}
impl WeightOf for Ty {
fn weight_of(&self) -> usize {
let Self { span, kind, gens } = self;
span.weight_of() + kind.weight_of() + gens.weight_of()
}
}
impl WeightOf for TyKind {
fn weight_of(&self) -> usize {
match self {
TyKind::Never | TyKind::Infer => size_of_val(self),
TyKind::Path(v) => v.weight_of(),
TyKind::Array(v) => v.weight_of(),
TyKind::Slice(v) => v.weight_of(),
TyKind::Tuple(v) => v.weight_of(),
TyKind::Ref(v) => v.weight_of(),
TyKind::Ptr(v) => v.weight_of(),
TyKind::Fn(v) => v.weight_of(),
}
}
}
impl WeightOf for TyArray {
fn weight_of(&self) -> usize {
let Self { ty, count } = self;
ty.weight_of() + count.weight_of()
}
}
impl WeightOf for TySlice {
fn weight_of(&self) -> usize {
let Self { ty } = self;
ty.weight_of()
}
}
impl WeightOf for TyTuple {
fn weight_of(&self) -> usize {
let Self { types } = self;
types.weight_of()
}
}
impl WeightOf for TyRef {
fn weight_of(&self) -> usize {
let Self { mutable, count, to } = self;
mutable.weight_of() + count.weight_of() + to.weight_of()
}
}
impl WeightOf for TyPtr {
fn weight_of(&self) -> usize {
let Self { to } = self;
to.weight_of()
}
}
impl WeightOf for TyFn {
fn weight_of(&self) -> usize {
let Self { args, rety } = self;
args.weight_of() + rety.weight_of()
}
}
impl WeightOf for Path {
fn weight_of(&self) -> usize {
let Self { absolute, parts } = self;
absolute.weight_of() + parts.weight_of()
}
}
impl WeightOf for PathPart {
fn weight_of(&self) -> usize {
match self {
PathPart::SuperKw => size_of_val(self),
PathPart::SelfTy => size_of_val(self),
PathPart::Ident(interned) => interned.weight_of(),
}
}
}
impl WeightOf for Stmt {
fn weight_of(&self) -> usize {
let Self { span, kind, semi } = self;
span.weight_of() + kind.weight_of() + semi.weight_of()
}
}
impl WeightOf for StmtKind {
fn weight_of(&self) -> usize {
match self {
StmtKind::Empty => size_of_val(self),
StmtKind::Item(item) => item.weight_of(),
StmtKind::Expr(expr) => expr.weight_of(),
}
}
}
impl WeightOf for Expr {
fn weight_of(&self) -> usize {
let Self { span, kind } = self;
span.weight_of() + kind.weight_of()
}
}
impl WeightOf for ExprKind {
fn weight_of(&self) -> usize {
match self {
ExprKind::Empty => size_of_val(self),
ExprKind::Closure(v) => v.weight_of(),
ExprKind::Quote(v) => v.weight_of(),
ExprKind::Let(v) => v.weight_of(),
ExprKind::Match(v) => v.weight_of(),
ExprKind::Assign(v) => v.weight_of(),
ExprKind::Modify(v) => v.weight_of(),
ExprKind::Binary(v) => v.weight_of(),
ExprKind::Unary(v) => v.weight_of(),
ExprKind::Cast(v) => v.weight_of(),
ExprKind::Member(v) => v.weight_of(),
ExprKind::Index(v) => v.weight_of(),
ExprKind::Structor(v) => v.weight_of(),
ExprKind::Path(v) => v.weight_of(),
ExprKind::Literal(v) => v.weight_of(),
ExprKind::Array(v) => v.weight_of(),
ExprKind::ArrayRep(v) => v.weight_of(),
ExprKind::AddrOf(v) => v.weight_of(),
ExprKind::Block(v) => v.weight_of(),
ExprKind::Group(v) => v.weight_of(),
ExprKind::Tuple(v) => v.weight_of(),
ExprKind::While(v) => v.weight_of(),
ExprKind::If(v) => v.weight_of(),
ExprKind::For(v) => v.weight_of(),
ExprKind::Break(v) => v.weight_of(),
ExprKind::Return(v) => v.weight_of(),
ExprKind::Continue => size_of_val(self),
}
}
}
impl WeightOf for Closure {
fn weight_of(&self) -> usize {
let Self { arg, body } = self;
arg.weight_of() + body.weight_of()
}
}
impl WeightOf for Quote {
fn weight_of(&self) -> usize {
let Self { quote } = self;
quote.weight_of()
}
}
impl WeightOf for Let {
fn weight_of(&self) -> usize {
let Self { mutable, name, ty, init } = self;
mutable.weight_of() + name.weight_of() + ty.weight_of() + init.weight_of()
}
}
impl WeightOf for Pattern {
fn weight_of(&self) -> usize {
match self {
Pattern::Name(s) => size_of_val(s),
Pattern::Path(p) => p.weight_of(),
Pattern::Literal(literal) => literal.weight_of(),
Pattern::Rest(Some(pattern)) => pattern.weight_of(),
Pattern::Rest(None) => 0,
Pattern::Ref(mutability, pattern) => mutability.weight_of() + pattern.weight_of(),
Pattern::RangeExc(head, tail) => head.weight_of() + tail.weight_of(),
Pattern::RangeInc(head, tail) => head.weight_of() + tail.weight_of(),
Pattern::Tuple(patterns) | Pattern::Array(patterns) => patterns.weight_of(),
Pattern::Struct(path, items) => {
let sitems: usize = items
.iter()
.map(|(name, opt)| name.weight_of() + opt.weight_of())
.sum();
path.weight_of() + sitems
}
Pattern::TupleStruct(path, patterns) => path.weight_of() + patterns.weight_of(),
}
}
}
impl WeightOf for Match {
fn weight_of(&self) -> usize {
let Self { scrutinee, arms } = self;
scrutinee.weight_of() + arms.weight_of()
}
}
impl WeightOf for MatchArm {
fn weight_of(&self) -> usize {
let Self(pattern, expr) = self;
pattern.weight_of() + expr.weight_of()
}
}
impl WeightOf for Assign {
fn weight_of(&self) -> usize {
let Self { parts } = self;
parts.0.weight_of() + parts.1.weight_of()
}
}
impl WeightOf for Modify {
#[rustfmt::skip]
fn weight_of(&self) -> usize {
let Self { kind, parts } = self;
kind.weight_of()
+ parts.0.weight_of()
+ parts.1.weight_of()
}
}
impl WeightOf for Binary {
fn weight_of(&self) -> usize {
let Self { kind, parts } = self;
kind.weight_of() + parts.0.weight_of() + parts.1.weight_of()
}
}
impl WeightOf for Unary {
#[rustfmt::skip]
fn weight_of(&self) -> usize {
let Self { kind, tail } = self;
kind.weight_of() + tail.weight_of()
}
}
impl WeightOf for Cast {
fn weight_of(&self) -> usize {
let Self { head, ty } = self;
head.weight_of() + ty.weight_of()
}
}
impl WeightOf for Member {
fn weight_of(&self) -> usize {
let Self { head, kind } = self;
head.weight_of() + kind.weight_of() // accounting
}
}
impl WeightOf for MemberKind {
fn weight_of(&self) -> usize {
match self {
MemberKind::Call(_, tuple) => tuple.weight_of(),
MemberKind::Struct(_) => 0,
MemberKind::Tuple(literal) => literal.weight_of(),
}
}
}
impl WeightOf for Index {
fn weight_of(&self) -> usize {
let Self { head, indices } = self;
head.weight_of() + indices.weight_of()
}
}
impl WeightOf for Literal {
fn weight_of(&self) -> usize {
match self {
Literal::Bool(v) => v.weight_of(),
Literal::Char(v) => v.weight_of(),
Literal::Int(v) => v.weight_of(),
Literal::Float(v) => v.weight_of(),
Literal::String(v) => v.weight_of(),
}
}
}
impl WeightOf for Structor {
fn weight_of(&self) -> usize {
let Self { to, init } = self;
to.weight_of() + init.weight_of()
}
}
impl WeightOf for Fielder {
fn weight_of(&self) -> usize {
let Self { name, init } = self;
name.weight_of() + init.weight_of()
}
}
impl WeightOf for Array {
fn weight_of(&self) -> usize {
let Self { values } = self;
values.weight_of()
}
}
impl WeightOf for ArrayRep {
fn weight_of(&self) -> usize {
let Self { value, repeat } = self;
value.weight_of() + repeat.weight_of()
}
}
impl WeightOf for AddrOf {
fn weight_of(&self) -> usize {
let Self { mutable, expr } = self;
mutable.weight_of() + expr.weight_of()
}
}
impl WeightOf for Block {
fn weight_of(&self) -> usize {
let Self { stmts } = self;
stmts.weight_of()
}
}
impl WeightOf for Group {
fn weight_of(&self) -> usize {
let Self { expr } = self;
expr.weight_of()
}
}
impl WeightOf for Tuple {
fn weight_of(&self) -> usize {
let Self { exprs } = self;
exprs.weight_of()
}
}
impl WeightOf for While {
fn weight_of(&self) -> usize {
let Self { cond, pass, fail } = self;
cond.weight_of() + pass.weight_of() + fail.weight_of()
}
}
impl WeightOf for If {
fn weight_of(&self) -> usize {
let Self { cond, pass, fail } = self;
cond.weight_of() + pass.weight_of() + fail.weight_of()
}
}
impl WeightOf for For {
fn weight_of(&self) -> usize {
let Self { bind, cond, pass, fail } = self;
bind.weight_of() + cond.weight_of() + pass.weight_of() + fail.weight_of()
}
}
impl WeightOf for Else {
fn weight_of(&self) -> usize {
let Self { body } = self;
body.weight_of()
}
}
impl WeightOf for Break {
fn weight_of(&self) -> usize {
let Self { body } = self;
body.weight_of()
}
}
impl WeightOf for Return {
fn weight_of(&self) -> usize {
let Self { body } = self;
body.weight_of()
}
}
// ------------ SizeOf Blanket Implementations
impl<T: WeightOf> WeightOf for Option<T> {
fn weight_of(&self) -> usize {
match self {
Some(t) => t.weight_of().max(size_of_val(t)),
None => size_of_val(self),
}
}
}
impl<T: WeightOf> WeightOf for [T] {
fn weight_of(&self) -> usize {
self.iter().map(WeightOf::weight_of).sum()
}
}
impl<T: WeightOf> WeightOf for Vec<T> {
fn weight_of(&self) -> usize {
size_of::<Self>() + self.iter().map(WeightOf::weight_of).sum::<usize>()
}
}
impl<T: WeightOf> WeightOf for Box<T> {
fn weight_of(&self) -> usize {
(**self).weight_of() + size_of::<Self>()
}
}
impl WeightOf for str {
fn weight_of(&self) -> usize {
self.len()
}
}
impl_size_of! {
// primitives
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
f32, f64, bool, char,
// cl-structures
Span,
// cl-ast
Visibility, Mutability, Semi, ModifyKind, BinaryKind, UnaryKind
}
impl<T> WeightOf for Interned<'_, T> {
fn weight_of(&self) -> usize {
size_of_val(self) // interned values are opaque to SizeOF
}
}
macro impl_size_of($($T:ty),*$(,)?) {
$(impl WeightOf for $T {
fn weight_of(&self) -> usize {
::std::mem::size_of_val(self)
}
})*
}

View File

@@ -2,7 +2,11 @@
//! with default implementations across the entire AST
pub mod fold;
pub mod visit;
pub mod walk;
pub use fold::Fold;
pub use visit::Visit;
pub use walk::Walk;

View File

@@ -13,8 +13,8 @@ use cl_structures::span::Span;
///
/// For all other nodes, traversal is *explicit*.
pub trait Fold {
fn fold_span(&mut self, extents: Span) -> Span {
extents
fn fold_span(&mut self, span: Span) -> Span {
span
}
fn fold_mutability(&mut self, mutability: Mutability) -> Mutability {
mutability
@@ -37,12 +37,15 @@ pub trait Fold {
fn fold_int(&mut self, i: u128) -> u128 {
i
}
fn fold_smuggled_float(&mut self, f: u64) -> u64 {
f
}
fn fold_string(&mut self, s: String) -> String {
s
}
fn fold_file(&mut self, f: File) -> File {
let File { items } = f;
File { items: items.into_iter().map(|i| self.fold_item(i)).collect() }
let File { name, items } = f;
File { name, items: items.into_iter().map(|i| self.fold_item(i)).collect() }
}
fn fold_attrs(&mut self, a: Attrs) -> Attrs {
let Attrs { meta } = a;
@@ -56,9 +59,9 @@ pub trait Fold {
or_fold_meta_kind(self, kind)
}
fn fold_item(&mut self, i: Item) -> Item {
let Item { extents, attrs, vis, kind } = i;
let Item { span, attrs, vis, kind } = i;
Item {
extents: self.fold_span(extents),
span: self.fold_span(span),
attrs: self.fold_attrs(attrs),
vis: self.fold_visibility(vis),
kind: self.fold_item_kind(kind),
@@ -67,9 +70,13 @@ pub trait Fold {
fn fold_item_kind(&mut self, kind: ItemKind) -> ItemKind {
or_fold_item_kind(self, kind)
}
fn fold_generics(&mut self, gens: Generics) -> Generics {
let Generics { vars } = gens;
Generics { vars: vars.into_iter().map(|sym| self.fold_sym(sym)).collect() }
}
fn fold_alias(&mut self, a: Alias) -> Alias {
let Alias { to, from } = a;
Alias { to: self.fold_sym(to), from: from.map(|from| Box::new(self.fold_ty(*from))) }
let Alias { name, from } = a;
Alias { name: self.fold_sym(name), from: from.map(|from| Box::new(self.fold_ty(*from))) }
}
fn fold_const(&mut self, c: Const) -> Const {
let Const { name, ty, init } = c;
@@ -89,31 +96,26 @@ pub trait Fold {
}
}
fn fold_module(&mut self, m: Module) -> Module {
let Module { name, kind } = m;
Module { name: self.fold_sym(name), kind: self.fold_module_kind(kind) }
}
fn fold_module_kind(&mut self, m: ModuleKind) -> ModuleKind {
match m {
ModuleKind::Inline(f) => ModuleKind::Inline(self.fold_file(f)),
ModuleKind::Outline => ModuleKind::Outline,
}
let Module { name, file } = m;
Module { name: self.fold_sym(name), file: file.map(|v| self.fold_file(v)) }
}
fn fold_function(&mut self, f: Function) -> Function {
let Function { name, sign, bind, body } = f;
let Function { name, gens, sign, bind, body } = f;
Function {
name: self.fold_sym(name),
gens: self.fold_generics(gens),
sign: self.fold_ty_fn(sign),
bind: bind.into_iter().map(|p| self.fold_param(p)).collect(),
body: body.map(|b| self.fold_block(b)),
bind: self.fold_pattern(bind),
body: body.map(|b| self.fold_expr(b)),
}
}
fn fold_param(&mut self, p: Param) -> Param {
let Param { mutability, name } = p;
Param { mutability: self.fold_mutability(mutability), name: self.fold_sym(name) }
}
fn fold_struct(&mut self, s: Struct) -> Struct {
let Struct { name, kind } = s;
Struct { name: self.fold_sym(name), kind: self.fold_struct_kind(kind) }
let Struct { name, gens, kind } = s;
Struct {
name: self.fold_sym(name),
gens: self.fold_generics(gens),
kind: self.fold_struct_kind(kind),
}
}
fn fold_struct_kind(&mut self, kind: StructKind) -> StructKind {
match kind {
@@ -137,23 +139,29 @@ pub trait Fold {
}
}
fn fold_enum(&mut self, e: Enum) -> Enum {
let Enum { name, kind } = e;
Enum { name: self.fold_sym(name), kind: self.fold_enum_kind(kind) }
let Enum { name, gens, variants: kind } = e;
Enum {
name: self.fold_sym(name),
gens: self.fold_generics(gens),
variants: kind.into_iter().map(|v| self.fold_variant(v)).collect(),
}
fn fold_enum_kind(&mut self, kind: EnumKind) -> EnumKind {
or_fold_enum_kind(self, kind)
}
fn fold_variant(&mut self, v: Variant) -> Variant {
let Variant { name, kind } = v;
let Variant { name, kind, body } = v;
Variant { name: self.fold_sym(name), kind: self.fold_variant_kind(kind) }
Variant {
name: self.fold_sym(name),
kind: self.fold_struct_kind(kind),
body: body.map(|e| Box::new(self.fold_expr(*e))),
}
fn fold_variant_kind(&mut self, kind: VariantKind) -> VariantKind {
or_fold_variant_kind(self, kind)
}
fn fold_impl(&mut self, i: Impl) -> Impl {
let Impl { target, body } = i;
Impl { target: self.fold_impl_kind(target), body: self.fold_file(body) }
let Impl { gens, target, body } = i;
Impl {
gens: self.fold_generics(gens),
target: self.fold_impl_kind(target),
body: self.fold_file(body),
}
}
fn fold_impl_kind(&mut self, kind: ImplKind) -> ImplKind {
or_fold_impl_kind(self, kind)
@@ -166,39 +174,39 @@ pub trait Fold {
or_fold_use_tree(self, tree)
}
fn fold_ty(&mut self, t: Ty) -> Ty {
let Ty { extents, kind } = t;
Ty { extents: self.fold_span(extents), kind: self.fold_ty_kind(kind) }
let Ty { span, kind, gens } = t;
Ty {
span: self.fold_span(span),
kind: self.fold_ty_kind(kind),
gens: self.fold_generics(gens),
}
}
fn fold_ty_kind(&mut self, kind: TyKind) -> TyKind {
or_fold_ty_kind(self, kind)
}
fn fold_ty_array(&mut self, a: TyArray) -> TyArray {
let TyArray { ty, count } = a;
TyArray { ty: Box::new(self.fold_ty_kind(*ty)), count }
TyArray { ty: Box::new(self.fold_ty(*ty)), count }
}
fn fold_ty_slice(&mut self, s: TySlice) -> TySlice {
let TySlice { ty } = s;
TySlice { ty: Box::new(self.fold_ty_kind(*ty)) }
TySlice { ty: Box::new(self.fold_ty(*ty)) }
}
fn fold_ty_tuple(&mut self, t: TyTuple) -> TyTuple {
let TyTuple { types } = t;
TyTuple {
types: types
.into_iter()
.map(|kind| self.fold_ty_kind(kind))
.collect(),
}
TyTuple { types: types.into_iter().map(|kind| self.fold_ty(kind)).collect() }
}
fn fold_ty_ref(&mut self, t: TyRef) -> TyRef {
let TyRef { mutable, count, to } = t;
TyRef { mutable: self.fold_mutability(mutable), count, to: self.fold_path(to) }
TyRef { mutable: self.fold_mutability(mutable), count, to: Box::new(self.fold_ty(*to)) }
}
fn fold_ty_ptr(&mut self, t: TyPtr) -> TyPtr {
let TyPtr { to } = t;
TyPtr { to: Box::new(self.fold_ty(*to)) }
}
fn fold_ty_fn(&mut self, t: TyFn) -> TyFn {
let TyFn { args, rety } = t;
TyFn {
args: Box::new(self.fold_ty_kind(*args)),
rety: rety.map(|t| Box::new(self.fold_ty(*t))),
}
TyFn { args: Box::new(self.fold_ty(*args)), rety: Box::new(self.fold_ty(*rety)) }
}
fn fold_path(&mut self, p: Path) -> Path {
let Path { absolute, parts } = p;
@@ -207,15 +215,14 @@ pub trait Fold {
fn fold_path_part(&mut self, p: PathPart) -> PathPart {
match p {
PathPart::SuperKw => PathPart::SuperKw,
PathPart::SelfKw => PathPart::SelfKw,
PathPart::SelfTy => PathPart::SelfTy,
PathPart::Ident(i) => PathPart::Ident(self.fold_sym(i)),
}
}
fn fold_stmt(&mut self, s: Stmt) -> Stmt {
let Stmt { extents, kind, semi } = s;
let Stmt { span, kind, semi } = s;
Stmt {
extents: self.fold_span(extents),
span: self.fold_span(span),
kind: self.fold_stmt_kind(kind),
semi: self.fold_semi(semi),
}
@@ -226,33 +233,96 @@ pub trait Fold {
fn fold_semi(&mut self, s: Semi) -> Semi {
s
}
fn fold_let(&mut self, l: Let) -> Let {
let Let { mutable, name, ty, init } = l;
Let {
mutable: self.fold_mutability(mutable),
name: self.fold_sym(name),
ty: ty.map(|t| Box::new(self.fold_ty(*t))),
init: init.map(|e| Box::new(self.fold_expr(*e))),
}
}
fn fold_expr(&mut self, e: Expr) -> Expr {
let Expr { extents, kind } = e;
Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) }
let Expr { span, kind } = e;
Expr { span: self.fold_span(span), kind: self.fold_expr_kind(kind) }
}
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
or_fold_expr_kind(self, kind)
}
fn fold_closure(&mut self, value: Closure) -> Closure {
let Closure { arg, body } = value;
Closure { arg: Box::new(self.fold_pattern(*arg)), body: Box::new(self.fold_expr(*body)) }
}
fn fold_let(&mut self, l: Let) -> Let {
let Let { mutable, name, ty, init } = l;
Let {
mutable: self.fold_mutability(mutable),
name: self.fold_pattern(name),
ty: ty.map(|t| Box::new(self.fold_ty(*t))),
init: init.map(|e| Box::new(self.fold_expr(*e))),
}
}
fn fold_pattern(&mut self, p: Pattern) -> Pattern {
match p {
Pattern::Name(sym) => Pattern::Name(self.fold_sym(sym)),
Pattern::Path(path) => Pattern::Path(self.fold_path(path)),
Pattern::Literal(literal) => Pattern::Literal(self.fold_literal(literal)),
Pattern::Rest(Some(name)) => Pattern::Rest(Some(self.fold_pattern(*name).into())),
Pattern::Rest(None) => Pattern::Rest(None),
Pattern::Ref(mutability, pattern) => Pattern::Ref(
self.fold_mutability(mutability),
Box::new(self.fold_pattern(*pattern)),
),
Pattern::RangeExc(head, tail) => Pattern::RangeInc(
Box::new(self.fold_pattern(*head)),
Box::new(self.fold_pattern(*tail)),
),
Pattern::RangeInc(head, tail) => Pattern::RangeInc(
Box::new(self.fold_pattern(*head)),
Box::new(self.fold_pattern(*tail)),
),
Pattern::Tuple(patterns) => {
Pattern::Tuple(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
}
Pattern::Array(patterns) => {
Pattern::Array(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
}
Pattern::Struct(path, items) => Pattern::Struct(
self.fold_path(path),
items
.into_iter()
.map(|(name, bind)| (name, bind.map(|p| self.fold_pattern(p))))
.collect(),
),
Pattern::TupleStruct(path, items) => Pattern::TupleStruct(
self.fold_path(path),
items
.into_iter()
.map(|bind| self.fold_pattern(bind))
.collect(),
),
}
}
fn fold_match(&mut self, m: Match) -> Match {
let Match { scrutinee, arms } = m;
Match {
scrutinee: self.fold_expr(*scrutinee).into(),
arms: arms
.into_iter()
.map(|arm| self.fold_match_arm(arm))
.collect(),
}
}
fn fold_match_arm(&mut self, a: MatchArm) -> MatchArm {
let MatchArm(pat, expr) = a;
MatchArm(self.fold_pattern(pat), self.fold_expr(expr))
}
fn fold_assign(&mut self, a: Assign) -> Assign {
let Assign { parts } = a;
let (head, tail) = *parts;
Assign { parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))) }
Assign { parts: Box::new((self.fold_expr(head), self.fold_expr(tail))) }
}
fn fold_modify(&mut self, m: Modify) -> Modify {
let Modify { kind, parts } = m;
let (head, tail) = *parts;
Modify {
kind: self.fold_modify_kind(kind),
parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))),
parts: Box::new((self.fold_expr(head), self.fold_expr(tail))),
}
}
fn fold_modify_kind(&mut self, kind: ModifyKind) -> ModifyKind {
@@ -263,7 +333,7 @@ pub trait Fold {
let (head, tail) = *parts;
Binary {
kind: self.fold_binary_kind(kind),
parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))),
parts: Box::new((self.fold_expr(head), self.fold_expr(tail))),
}
}
fn fold_binary_kind(&mut self, kind: BinaryKind) -> BinaryKind {
@@ -271,14 +341,18 @@ pub trait Fold {
}
fn fold_unary(&mut self, u: Unary) -> Unary {
let Unary { kind, tail } = u;
Unary { kind: self.fold_unary_kind(kind), tail: Box::new(self.fold_expr_kind(*tail)) }
Unary { kind: self.fold_unary_kind(kind), tail: Box::new(self.fold_expr(*tail)) }
}
fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind {
kind
}
fn fold_cast(&mut self, cast: Cast) -> Cast {
let Cast { head, ty } = cast;
Cast { head: Box::new(self.fold_expr(*head)), ty: self.fold_ty(ty) }
}
fn fold_member(&mut self, m: Member) -> Member {
let Member { head, kind } = m;
Member { head: Box::new(self.fold_expr_kind(*head)), kind: self.fold_member_kind(kind) }
Member { head: Box::new(self.fold_expr(*head)), kind: self.fold_member_kind(kind) }
}
fn fold_member_kind(&mut self, kind: MemberKind) -> MemberKind {
or_fold_member_kind(self, kind)
@@ -286,7 +360,7 @@ pub trait Fold {
fn fold_index(&mut self, i: Index) -> Index {
let Index { head, indices } = i;
Index {
head: Box::new(self.fold_expr_kind(*head)),
head: Box::new(self.fold_expr(*head)),
indices: indices.into_iter().map(|e| self.fold_expr(e)).collect(),
}
}
@@ -309,18 +383,11 @@ pub trait Fold {
}
fn fold_array_rep(&mut self, a: ArrayRep) -> ArrayRep {
let ArrayRep { value, repeat } = a;
ArrayRep {
value: Box::new(self.fold_expr_kind(*value)),
repeat: Box::new(self.fold_expr_kind(*repeat)),
}
ArrayRep { value: Box::new(self.fold_expr(*value)), repeat }
}
fn fold_addrof(&mut self, a: AddrOf) -> AddrOf {
let AddrOf { count, mutable, expr } = a;
AddrOf {
count,
mutable: self.fold_mutability(mutable),
expr: Box::new(self.fold_expr_kind(*expr)),
}
let AddrOf { mutable, expr } = a;
AddrOf { mutable: self.fold_mutability(mutable), expr: Box::new(self.fold_expr(*expr)) }
}
fn fold_block(&mut self, b: Block) -> Block {
let Block { stmts } = b;
@@ -328,16 +395,12 @@ pub trait Fold {
}
fn fold_group(&mut self, g: Group) -> Group {
let Group { expr } = g;
Group { expr: Box::new(self.fold_expr_kind(*expr)) }
Group { expr: Box::new(self.fold_expr(*expr)) }
}
fn fold_tuple(&mut self, t: Tuple) -> Tuple {
let Tuple { exprs } = t;
Tuple { exprs: exprs.into_iter().map(|e| self.fold_expr(e)).collect() }
}
fn fold_loop(&mut self, l: Loop) -> Loop {
let Loop { body } = l;
Loop { body: Box::new(self.fold_expr(*body)) }
}
fn fold_while(&mut self, w: While) -> While {
let While { cond, pass, fail } = w;
While {
@@ -357,7 +420,7 @@ pub trait Fold {
fn fold_for(&mut self, f: For) -> For {
let For { bind, cond, pass, fail } = f;
For {
bind: self.fold_sym(bind),
bind: self.fold_pattern(bind),
cond: Box::new(self.fold_expr(*cond)),
pass: Box::new(self.fold_block(*pass)),
fail: self.fold_else(fail),
@@ -375,10 +438,6 @@ pub trait Fold {
let Return { body } = r;
Return { body: body.map(|e| Box::new(self.fold_expr(*e))) }
}
fn fold_continue(&mut self, c: Continue) -> Continue {
let Continue = c;
Continue
}
}
#[inline]
@@ -388,6 +447,7 @@ pub fn or_fold_literal<F: Fold + ?Sized>(folder: &mut F, lit: Literal) -> Litera
Literal::Bool(b) => Literal::Bool(folder.fold_bool(b)),
Literal::Char(c) => Literal::Char(folder.fold_char(c)),
Literal::Int(i) => Literal::Int(folder.fold_int(i)),
Literal::Float(f) => Literal::Float(folder.fold_smuggled_float(f)),
Literal::String(s) => Literal::String(folder.fold_string(s)),
}
}
@@ -420,15 +480,6 @@ pub fn or_fold_item_kind<F: Fold + ?Sized>(folder: &mut F, kind: ItemKind) -> It
}
}
#[inline]
/// Folds a [ModuleKind] in the default way
pub fn or_fold_module_kind<F: Fold + ?Sized>(folder: &mut F, kind: ModuleKind) -> ModuleKind {
match kind {
ModuleKind::Inline(f) => ModuleKind::Inline(folder.fold_file(f)),
ModuleKind::Outline => ModuleKind::Outline,
}
}
#[inline]
/// Folds a [StructKind] in the default way
pub fn or_fold_struct_kind<F: Fold + ?Sized>(folder: &mut F, kind: StructKind) -> StructKind {
@@ -445,32 +496,6 @@ pub fn or_fold_struct_kind<F: Fold + ?Sized>(folder: &mut F, kind: StructKind) -
}
}
#[inline]
/// Folds an [EnumKind] in the default way
pub fn or_fold_enum_kind<F: Fold + ?Sized>(folder: &mut F, kind: EnumKind) -> EnumKind {
match kind {
EnumKind::NoVariants => EnumKind::NoVariants,
EnumKind::Variants(v) => {
EnumKind::Variants(v.into_iter().map(|v| folder.fold_variant(v)).collect())
}
}
}
#[inline]
/// Folds a [VariantKind] in the default way
pub fn or_fold_variant_kind<F: Fold + ?Sized>(folder: &mut F, kind: VariantKind) -> VariantKind {
match kind {
VariantKind::Plain => VariantKind::Plain,
VariantKind::CLike(n) => VariantKind::CLike(n),
VariantKind::Tuple(t) => VariantKind::Tuple(folder.fold_ty(t)),
VariantKind::Struct(mem) => VariantKind::Struct(
mem.into_iter()
.map(|m| folder.fold_struct_member(m))
.collect(),
),
}
}
#[inline]
/// Folds an [ImplKind] in the default way
pub fn or_fold_impl_kind<F: Fold + ?Sized>(folder: &mut F, kind: ImplKind) -> ImplKind {
@@ -506,12 +531,13 @@ pub fn or_fold_use_tree<F: Fold + ?Sized>(folder: &mut F, tree: UseTree) -> UseT
pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind {
match kind {
TyKind::Never => TyKind::Never,
TyKind::Empty => TyKind::Empty,
TyKind::Infer => TyKind::Infer,
TyKind::Path(p) => TyKind::Path(folder.fold_path(p)),
TyKind::Array(a) => TyKind::Array(folder.fold_ty_array(a)),
TyKind::Slice(s) => TyKind::Slice(folder.fold_ty_slice(s)),
TyKind::Tuple(t) => TyKind::Tuple(folder.fold_ty_tuple(t)),
TyKind::Ref(t) => TyKind::Ref(folder.fold_ty_ref(t)),
TyKind::Ptr(t) => TyKind::Ptr(folder.fold_ty_ptr(t)),
TyKind::Fn(t) => TyKind::Fn(folder.fold_ty_fn(t)),
}
}
@@ -521,7 +547,6 @@ pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind
pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> StmtKind {
match kind {
StmtKind::Empty => StmtKind::Empty,
StmtKind::Local(l) => StmtKind::Local(folder.fold_let(l)),
StmtKind::Item(i) => StmtKind::Item(Box::new(folder.fold_item(*i))),
StmtKind::Expr(e) => StmtKind::Expr(Box::new(folder.fold_expr(*e))),
}
@@ -531,10 +556,15 @@ pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> St
pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> ExprKind {
match kind {
ExprKind::Empty => ExprKind::Empty,
ExprKind::Closure(c) => ExprKind::Closure(folder.fold_closure(c)),
ExprKind::Quote(q) => ExprKind::Quote(q), // quoted expressions are left unmodified
ExprKind::Let(l) => ExprKind::Let(folder.fold_let(l)),
ExprKind::Match(m) => ExprKind::Match(folder.fold_match(m)),
ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)),
ExprKind::Modify(m) => ExprKind::Modify(folder.fold_modify(m)),
ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)),
ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)),
ExprKind::Cast(c) => ExprKind::Cast(folder.fold_cast(c)),
ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)),
ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)),
ExprKind::Structor(s) => ExprKind::Structor(folder.fold_structor(s)),
@@ -546,13 +576,12 @@ pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> Ex
ExprKind::Block(b) => ExprKind::Block(folder.fold_block(b)),
ExprKind::Group(g) => ExprKind::Group(folder.fold_group(g)),
ExprKind::Tuple(t) => ExprKind::Tuple(folder.fold_tuple(t)),
ExprKind::Loop(l) => ExprKind::Loop(folder.fold_loop(l)),
ExprKind::While(w) => ExprKind::While(folder.fold_while(w)),
ExprKind::If(i) => ExprKind::If(folder.fold_if(i)),
ExprKind::For(f) => ExprKind::For(folder.fold_for(f)),
ExprKind::Break(b) => ExprKind::Break(folder.fold_break(b)),
ExprKind::Return(r) => ExprKind::Return(folder.fold_return(r)),
ExprKind::Continue(c) => ExprKind::Continue(folder.fold_continue(c)),
ExprKind::Continue => ExprKind::Continue,
}
}
pub fn or_fold_member_kind<F: Fold + ?Sized>(folder: &mut F, kind: MemberKind) -> MemberKind {

View File

@@ -3,486 +3,258 @@
use crate::ast::*;
use cl_structures::span::Span;
use super::walk::Walk;
/// Immutably walks the entire AST
///
/// Each method acts as a customization point.
///
/// There are a set of default implementations for enums
/// under the name [`or_visit_`*](or_visit_expr_kind),
/// provided for ease of use.
///
/// For all other nodes, traversal is *explicit*.
pub trait Visit<'a>: Sized {
fn visit_span(&mut self, _extents: &'a Span) {}
fn visit_mutability(&mut self, _mutable: &'a Mutability) {}
fn visit_visibility(&mut self, _vis: &'a Visibility) {}
fn visit_sym(&mut self, _name: &'a Sym) {}
fn visit_literal(&mut self, l: &'a Literal) {
or_visit_literal(self, l)
/// Visits a [Walker](Walk)
#[inline]
fn visit<W: Walk>(&mut self, walker: &'a W) -> &mut Self {
walker.visit_in(self);
self
}
fn visit_bool(&mut self, _b: &'a bool) {}
fn visit_char(&mut self, _c: &'a char) {}
fn visit_int(&mut self, _i: &'a u128) {}
fn visit_string(&mut self, _s: &'a str) {}
fn visit_file(&mut self, f: &'a File) {
let File { items } = f;
items.iter().for_each(|i| self.visit_item(i));
/// Visits the children of a [Walker](Walk)
fn visit_children<W: Walk>(&mut self, walker: &'a W) {
walker.children(self)
}
fn visit_attrs(&mut self, a: &'a Attrs) {
let Attrs { meta } = a;
meta.iter().for_each(|m| self.visit_meta(m));
}
fn visit_meta(&mut self, m: &'a Meta) {
let Meta { name, kind } = m;
self.visit_sym(name);
self.visit_meta_kind(kind);
}
fn visit_meta_kind(&mut self, kind: &'a MetaKind) {
or_visit_meta_kind(self, kind)
}
fn visit_item(&mut self, i: &'a Item) {
let Item { extents, attrs, vis, kind } = i;
self.visit_span(extents);
self.visit_attrs(attrs);
self.visit_visibility(vis);
self.visit_item_kind(kind);
}
fn visit_item_kind(&mut self, kind: &'a ItemKind) {
or_visit_item_kind(self, kind)
}
fn visit_alias(&mut self, a: &'a Alias) {
let Alias { to, from } = a;
self.visit_sym(to);
if let Some(t) = from {
self.visit_ty(t)
}
}
fn visit_const(&mut self, c: &'a Const) {
let Const { name, ty, init } = c;
self.visit_sym(name);
self.visit_ty(ty);
self.visit_expr(init);
}
fn visit_static(&mut self, s: &'a Static) {
let Static { mutable, name, ty, init } = s;
self.visit_mutability(mutable);
self.visit_sym(name);
self.visit_ty(ty);
self.visit_expr(init);
}
fn visit_module(&mut self, m: &'a Module) {
let Module { name, kind } = m;
self.visit_sym(name);
self.visit_module_kind(kind);
}
fn visit_module_kind(&mut self, kind: &'a ModuleKind) {
or_visit_module_kind(self, kind)
}
fn visit_function(&mut self, f: &'a Function) {
let Function { name, sign, bind, body } = f;
self.visit_sym(name);
self.visit_ty_fn(sign);
bind.iter().for_each(|p| self.visit_param(p));
if let Some(b) = body {
self.visit_block(b)
}
}
fn visit_param(&mut self, p: &'a Param) {
let Param { mutability, name } = p;
self.visit_mutability(mutability);
self.visit_sym(name);
}
fn visit_struct(&mut self, s: &'a Struct) {
let Struct { name, kind } = s;
self.visit_sym(name);
self.visit_struct_kind(kind);
}
fn visit_struct_kind(&mut self, kind: &'a StructKind) {
or_visit_struct_kind(self, kind)
}
fn visit_struct_member(&mut self, m: &'a StructMember) {
let StructMember { vis, name, ty } = m;
self.visit_visibility(vis);
self.visit_sym(name);
self.visit_ty(ty);
}
fn visit_enum(&mut self, e: &'a Enum) {
let Enum { name, kind } = e;
self.visit_sym(name);
self.visit_enum_kind(kind);
}
fn visit_enum_kind(&mut self, kind: &'a EnumKind) {
or_visit_enum_kind(self, kind)
}
fn visit_variant(&mut self, v: &'a Variant) {
let Variant { name, kind } = v;
self.visit_sym(name);
self.visit_variant_kind(kind);
}
fn visit_variant_kind(&mut self, kind: &'a VariantKind) {
or_visit_variant_kind(self, kind)
}
fn visit_impl(&mut self, i: &'a Impl) {
let Impl { target, body } = i;
self.visit_impl_kind(target);
self.visit_file(body);
}
fn visit_impl_kind(&mut self, target: &'a ImplKind) {
or_visit_impl_kind(self, target)
}
fn visit_use(&mut self, u: &'a Use) {
let Use { absolute: _, tree } = u;
self.visit_use_tree(tree);
}
fn visit_use_tree(&mut self, tree: &'a UseTree) {
or_visit_use_tree(self, tree)
}
fn visit_ty(&mut self, t: &'a Ty) {
let Ty { extents, kind } = t;
self.visit_span(extents);
self.visit_ty_kind(kind);
}
fn visit_ty_kind(&mut self, kind: &'a TyKind) {
or_visit_ty_kind(self, kind)
}
fn visit_ty_array(&mut self, a: &'a TyArray) {
let TyArray { ty, count: _ } = a;
self.visit_ty_kind(ty);
}
fn visit_ty_slice(&mut self, s: &'a TySlice) {
let TySlice { ty } = s;
self.visit_ty_kind(ty)
}
fn visit_ty_tuple(&mut self, t: &'a TyTuple) {
let TyTuple { types } = t;
types.iter().for_each(|kind| self.visit_ty_kind(kind))
}
fn visit_ty_ref(&mut self, t: &'a TyRef) {
let TyRef { mutable, count: _, to } = t;
self.visit_mutability(mutable);
self.visit_path(to);
}
fn visit_ty_fn(&mut self, t: &'a TyFn) {
let TyFn { args, rety } = t;
self.visit_ty_kind(args);
if let Some(rety) = rety {
self.visit_ty(rety);
}
}
fn visit_path(&mut self, p: &'a Path) {
let Path { absolute: _, parts } = p;
parts.iter().for_each(|p| self.visit_path_part(p))
}
fn visit_path_part(&mut self, p: &'a PathPart) {
match p {
PathPart::SuperKw => {}
PathPart::SelfKw => {}
PathPart::SelfTy => {}
PathPart::Ident(i) => self.visit_sym(i),
}
}
fn visit_stmt(&mut self, s: &'a Stmt) {
let Stmt { extents, kind, semi } = s;
self.visit_span(extents);
self.visit_stmt_kind(kind);
self.visit_semi(semi);
}
fn visit_stmt_kind(&mut self, kind: &'a StmtKind) {
or_visit_stmt_kind(self, kind)
}
fn visit_semi(&mut self, _s: &'a Semi) {}
fn visit_let(&mut self, l: &'a Let) {
let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable);
self.visit_sym(name);
if let Some(ty) = ty {
self.visit_ty(ty);
}
if let Some(init) = init {
self.visit_expr(init)
}
}
fn visit_expr(&mut self, e: &'a Expr) {
let Expr { extents, kind } = e;
self.visit_span(extents);
self.visit_expr_kind(kind)
}
fn visit_expr_kind(&mut self, e: &'a ExprKind) {
or_visit_expr_kind(self, e)
}
fn visit_assign(&mut self, a: &'a Assign) {
let Assign { parts } = a;
let (head, tail) = parts.as_ref();
self.visit_expr_kind(head);
self.visit_expr_kind(tail);
}
fn visit_modify(&mut self, m: &'a Modify) {
let Modify { kind, parts } = m;
let (head, tail) = parts.as_ref();
self.visit_modify_kind(kind);
self.visit_expr_kind(head);
self.visit_expr_kind(tail);
}
fn visit_modify_kind(&mut self, _kind: &'a ModifyKind) {}
fn visit_binary(&mut self, b: &'a Binary) {
let Binary { kind, parts } = b;
let (head, tail) = parts.as_ref();
self.visit_binary_kind(kind);
self.visit_expr_kind(head);
self.visit_expr_kind(tail);
}
fn visit_binary_kind(&mut self, _kind: &'a BinaryKind) {}
fn visit_unary(&mut self, u: &'a Unary) {
let Unary { kind, tail } = u;
self.visit_unary_kind(kind);
self.visit_expr_kind(tail);
}
fn visit_unary_kind(&mut self, _kind: &'a UnaryKind) {}
fn visit_member(&mut self, m: &'a Member) {
let Member { head, kind } = m;
self.visit_expr_kind(head);
self.visit_member_kind(kind);
}
fn visit_member_kind(&mut self, kind: &'a MemberKind) {
or_visit_member_kind(self, kind)
}
fn visit_index(&mut self, i: &'a Index) {
let Index { head, indices } = i;
self.visit_expr_kind(head);
indices.iter().for_each(|e| self.visit_expr(e));
}
fn visit_structor(&mut self, s: &'a Structor) {
let Structor { to, init } = s;
self.visit_path(to);
init.iter().for_each(|e| self.visit_fielder(e))
}
fn visit_fielder(&mut self, f: &'a Fielder) {
let Fielder { name, init } = f;
self.visit_sym(name);
if let Some(init) = init {
self.visit_expr(init);
}
}
fn visit_array(&mut self, a: &'a Array) {
let Array { values } = a;
values.iter().for_each(|e| self.visit_expr(e))
}
fn visit_array_rep(&mut self, a: &'a ArrayRep) {
let ArrayRep { value, repeat } = a;
self.visit_expr_kind(value);
self.visit_expr_kind(repeat);
}
fn visit_addrof(&mut self, a: &'a AddrOf) {
let AddrOf { count: _, mutable, expr } = a;
self.visit_mutability(mutable);
self.visit_expr_kind(expr);
}
fn visit_block(&mut self, b: &'a Block) {
let Block { stmts } = b;
stmts.iter().for_each(|s| self.visit_stmt(s));
}
fn visit_group(&mut self, g: &'a Group) {
let Group { expr } = g;
self.visit_expr_kind(expr)
}
fn visit_tuple(&mut self, t: &'a Tuple) {
let Tuple { exprs } = t;
exprs.iter().for_each(|e| self.visit_expr(e))
}
fn visit_loop(&mut self, l: &'a Loop) {
let Loop { body } = l;
self.visit_expr(body)
}
fn visit_while(&mut self, w: &'a While) {
let While { cond, pass, fail } = w;
self.visit_expr(cond);
self.visit_block(pass);
self.visit_else(fail);
}
fn visit_if(&mut self, i: &'a If) {
let If { cond, pass, fail } = i;
self.visit_expr(cond);
self.visit_block(pass);
self.visit_else(fail);
}
fn visit_for(&mut self, f: &'a For) {
let For { bind, cond, pass, fail } = f;
self.visit_sym(bind);
self.visit_expr(cond);
self.visit_block(pass);
self.visit_else(fail);
}
fn visit_else(&mut self, e: &'a Else) {
let Else { body } = e;
if let Some(body) = body {
self.visit_expr(body)
}
}
fn visit_break(&mut self, b: &'a Break) {
let Break { body } = b;
if let Some(body) = body {
self.visit_expr(body)
}
}
fn visit_return(&mut self, r: &'a Return) {
let Return { body } = r;
if let Some(body) = body {
self.visit_expr(body)
}
}
fn visit_continue(&mut self, c: &'a Continue) {
let Continue = c;
}
}
pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) {
match l {
Literal::Bool(b) => visitor.visit_bool(b),
Literal::Char(c) => visitor.visit_char(c),
Literal::Int(i) => visitor.visit_int(i),
Literal::String(s) => visitor.visit_string(s),
fn visit_span(&mut self, value: &'a Span) {
value.children(self)
}
fn visit_mutability(&mut self, value: &'a Mutability) {
value.children(self)
}
fn visit_visibility(&mut self, value: &'a Visibility) {
value.children(self)
}
fn visit_sym(&mut self, value: &'a Sym) {
value.children(self)
}
fn visit_literal(&mut self, value: &'a Literal) {
value.children(self)
}
fn visit_bool(&mut self, value: &'a bool) {
value.children(self)
}
fn visit_char(&mut self, value: &'a char) {
value.children(self)
}
fn visit_int(&mut self, value: &'a u128) {
value.children(self)
}
fn visit_smuggled_float(&mut self, value: &'a u64) {
value.children(self)
}
fn visit_string(&mut self, value: &'a str) {
value.children(self)
}
fn visit_file(&mut self, value: &'a File) {
value.children(self)
}
fn visit_attrs(&mut self, value: &'a Attrs) {
value.children(self)
}
fn visit_meta(&mut self, value: &'a Meta) {
value.children(self)
}
fn visit_meta_kind(&mut self, value: &'a MetaKind) {
value.children(self)
}
fn visit_item(&mut self, value: &'a Item) {
value.children(self)
}
fn visit_item_kind(&mut self, value: &'a ItemKind) {
value.children(self)
}
fn visit_generics(&mut self, value: &'a Generics) {
value.children(self)
}
fn visit_alias(&mut self, value: &'a Alias) {
value.children(self)
}
fn visit_const(&mut self, value: &'a Const) {
value.children(self)
}
fn visit_static(&mut self, value: &'a Static) {
value.children(self)
}
fn visit_module(&mut self, value: &'a Module) {
value.children(self)
}
fn visit_function(&mut self, value: &'a Function) {
value.children(self)
}
fn visit_struct(&mut self, value: &'a Struct) {
value.children(self)
}
fn visit_struct_kind(&mut self, value: &'a StructKind) {
value.children(self)
}
fn visit_struct_member(&mut self, value: &'a StructMember) {
value.children(self)
}
fn visit_enum(&mut self, value: &'a Enum) {
value.children(self)
}
fn visit_variant(&mut self, value: &'a Variant) {
value.children(self)
}
fn visit_impl(&mut self, value: &'a Impl) {
value.children(self)
}
fn visit_impl_kind(&mut self, value: &'a ImplKind) {
value.children(self)
}
fn visit_use(&mut self, value: &'a Use) {
value.children(self)
}
fn visit_use_tree(&mut self, value: &'a UseTree) {
value.children(self)
}
fn visit_ty(&mut self, value: &'a Ty) {
value.children(self)
}
fn visit_ty_kind(&mut self, value: &'a TyKind) {
value.children(self)
}
fn visit_ty_array(&mut self, value: &'a TyArray) {
value.children(self)
}
fn visit_ty_slice(&mut self, value: &'a TySlice) {
value.children(self)
}
fn visit_ty_tuple(&mut self, value: &'a TyTuple) {
value.children(self)
}
fn visit_ty_ref(&mut self, value: &'a TyRef) {
value.children(self)
}
fn visit_ty_ptr(&mut self, value: &'a TyPtr) {
value.children(self)
}
fn visit_ty_fn(&mut self, value: &'a TyFn) {
value.children(self)
}
fn visit_path(&mut self, value: &'a Path) {
value.children(self)
}
fn visit_path_part(&mut self, value: &'a PathPart) {
value.children(self)
}
fn visit_stmt(&mut self, value: &'a Stmt) {
value.children(self)
}
fn visit_stmt_kind(&mut self, value: &'a StmtKind) {
value.children(self)
}
fn visit_semi(&mut self, value: &'a Semi) {
value.children(self)
}
fn visit_expr(&mut self, value: &'a Expr) {
value.children(self)
}
fn visit_expr_kind(&mut self, value: &'a ExprKind) {
value.children(self)
}
fn visit_closure(&mut self, value: &'a Closure) {
value.children(self)
}
fn visit_quote(&mut self, value: &'a Quote) {
value.children(self)
}
fn visit_let(&mut self, value: &'a Let) {
value.children(self)
}
}
pub fn or_visit_meta_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a MetaKind) {
match kind {
MetaKind::Plain => {}
MetaKind::Equals(l) => visitor.visit_literal(l),
MetaKind::Func(lits) => lits.iter().for_each(|l| visitor.visit_literal(l)),
fn visit_pattern(&mut self, value: &'a Pattern) {
value.children(self)
}
}
pub fn or_visit_item_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a ItemKind) {
match kind {
ItemKind::Module(m) => visitor.visit_module(m),
ItemKind::Alias(a) => visitor.visit_alias(a),
ItemKind::Enum(e) => visitor.visit_enum(e),
ItemKind::Struct(s) => visitor.visit_struct(s),
ItemKind::Const(c) => visitor.visit_const(c),
ItemKind::Static(s) => visitor.visit_static(s),
ItemKind::Function(f) => visitor.visit_function(f),
ItemKind::Impl(i) => visitor.visit_impl(i),
ItemKind::Use(u) => visitor.visit_use(u),
fn visit_match(&mut self, value: &'a Match) {
value.children(self)
}
}
pub fn or_visit_module_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a ModuleKind) {
match kind {
ModuleKind::Inline(f) => visitor.visit_file(f),
ModuleKind::Outline => {}
fn visit_match_arm(&mut self, value: &'a MatchArm) {
value.children(self)
}
}
pub fn or_visit_struct_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StructKind) {
match kind {
StructKind::Empty => {}
StructKind::Tuple(ty) => ty.iter().for_each(|t| visitor.visit_ty(t)),
StructKind::Struct(m) => m.iter().for_each(|m| visitor.visit_struct_member(m)),
fn visit_assign(&mut self, value: &'a Assign) {
value.children(self)
}
fn visit_modify(&mut self, value: &'a Modify) {
value.children(self)
}
fn visit_modify_kind(&mut self, value: &'a ModifyKind) {
value.children(self)
}
fn visit_binary(&mut self, value: &'a Binary) {
value.children(self)
}
fn visit_binary_kind(&mut self, value: &'a BinaryKind) {
value.children(self)
}
fn visit_unary(&mut self, value: &'a Unary) {
value.children(self)
}
fn visit_unary_kind(&mut self, value: &'a UnaryKind) {
value.children(self)
}
}
pub fn or_visit_enum_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a EnumKind) {
match kind {
EnumKind::NoVariants => {}
EnumKind::Variants(variants) => variants.iter().for_each(|v| visitor.visit_variant(v)),
}
}
pub fn or_visit_variant_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a VariantKind) {
match kind {
VariantKind::Plain => {}
VariantKind::CLike(_) => {}
VariantKind::Tuple(t) => visitor.visit_ty(t),
VariantKind::Struct(m) => m.iter().for_each(|m| visitor.visit_struct_member(m)),
}
}
pub fn or_visit_impl_kind<'a, V: Visit<'a>>(visitor: &mut V, target: &'a ImplKind) {
match target {
ImplKind::Type(t) => visitor.visit_ty(t),
ImplKind::Trait { impl_trait, for_type } => {
visitor.visit_path(impl_trait);
visitor.visit_ty(for_type)
}
}
}
pub fn or_visit_use_tree<'a, V: Visit<'a>>(visitor: &mut V, tree: &'a UseTree) {
match tree {
UseTree::Tree(tree) => {
tree.iter().for_each(|tree| visitor.visit_use_tree(tree));
}
UseTree::Path(path, rest) => {
visitor.visit_path_part(path);
visitor.visit_use_tree(rest)
}
UseTree::Alias(path, name) => {
visitor.visit_sym(path);
visitor.visit_sym(name);
}
UseTree::Name(name) => {
visitor.visit_sym(name);
}
UseTree::Glob => {}
}
}
pub fn or_visit_ty_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a TyKind) {
match kind {
TyKind::Never => {}
TyKind::Empty => {}
TyKind::Path(p) => visitor.visit_path(p),
TyKind::Array(t) => visitor.visit_ty_array(t),
TyKind::Slice(t) => visitor.visit_ty_slice(t),
TyKind::Tuple(t) => visitor.visit_ty_tuple(t),
TyKind::Ref(t) => visitor.visit_ty_ref(t),
TyKind::Fn(t) => visitor.visit_ty_fn(t),
}
}
pub fn or_visit_stmt_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StmtKind) {
match kind {
StmtKind::Empty => {}
StmtKind::Local(l) => visitor.visit_let(l),
StmtKind::Item(i) => visitor.visit_item(i),
StmtKind::Expr(e) => visitor.visit_expr(e),
}
}
pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) {
match e {
ExprKind::Empty => {}
ExprKind::Assign(a) => visitor.visit_assign(a),
ExprKind::Modify(m) => visitor.visit_modify(m),
ExprKind::Binary(b) => visitor.visit_binary(b),
ExprKind::Unary(u) => visitor.visit_unary(u),
ExprKind::Member(m) => visitor.visit_member(m),
ExprKind::Index(i) => visitor.visit_index(i),
ExprKind::Structor(s) => visitor.visit_structor(s),
ExprKind::Path(p) => visitor.visit_path(p),
ExprKind::Literal(l) => visitor.visit_literal(l),
ExprKind::Array(a) => visitor.visit_array(a),
ExprKind::ArrayRep(a) => visitor.visit_array_rep(a),
ExprKind::AddrOf(a) => visitor.visit_addrof(a),
ExprKind::Block(b) => visitor.visit_block(b),
ExprKind::Group(g) => visitor.visit_group(g),
ExprKind::Tuple(t) => visitor.visit_tuple(t),
ExprKind::Loop(l) => visitor.visit_loop(l),
ExprKind::While(w) => visitor.visit_while(w),
ExprKind::If(i) => visitor.visit_if(i),
ExprKind::For(f) => visitor.visit_for(f),
ExprKind::Break(b) => visitor.visit_break(b),
ExprKind::Return(r) => visitor.visit_return(r),
ExprKind::Continue(c) => visitor.visit_continue(c),
}
}
pub fn or_visit_member_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a MemberKind) {
match kind {
MemberKind::Call(field, args) => {
visitor.visit_sym(field);
visitor.visit_tuple(args);
}
MemberKind::Struct(field) => visitor.visit_sym(field),
MemberKind::Tuple(field) => visitor.visit_literal(field),
fn visit_cast(&mut self, value: &'a Cast) {
value.children(self)
}
fn visit_member(&mut self, value: &'a Member) {
value.children(self)
}
fn visit_member_kind(&mut self, value: &'a MemberKind) {
value.children(self)
}
fn visit_index(&mut self, value: &'a Index) {
value.children(self)
}
fn visit_structor(&mut self, value: &'a Structor) {
value.children(self)
}
fn visit_fielder(&mut self, value: &'a Fielder) {
value.children(self)
}
fn visit_array(&mut self, value: &'a Array) {
value.children(self)
}
fn visit_array_rep(&mut self, value: &'a ArrayRep) {
value.children(self)
}
fn visit_addrof(&mut self, value: &'a AddrOf) {
value.children(self)
}
fn visit_block(&mut self, value: &'a Block) {
value.children(self)
}
fn visit_group(&mut self, value: &'a Group) {
value.children(self)
}
fn visit_tuple(&mut self, value: &'a Tuple) {
value.children(self)
}
fn visit_while(&mut self, value: &'a While) {
value.children(self)
}
fn visit_if(&mut self, value: &'a If) {
value.children(self)
}
fn visit_for(&mut self, value: &'a For) {
value.children(self)
}
fn visit_else(&mut self, value: &'a Else) {
value.children(self)
}
fn visit_break(&mut self, value: &'a Break) {
value.children(self)
}
fn visit_return(&mut self, value: &'a Return) {
value.children(self)
}
fn visit_continue(&mut self) {}
}

View File

@@ -0,0 +1,963 @@
//! Accepts an AST Visitor. Walks the AST, calling the visitor on each step.
use super::visit::Visit;
use crate::ast::*;
use cl_structures::span::Span;
/// Helps a [Visitor](Visit) walk through `Self`.
pub trait Walk {
/// Calls the respective `visit_*` function in V
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V);
#[allow(unused)]
/// Walks the children of self, visiting them in V
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {}
}
impl Walk for Span {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_span(self);
}
}
impl Walk for Sym {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_sym(self);
}
}
impl Walk for Mutability {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_mutability(self);
}
}
impl Walk for Visibility {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_visibility(self);
}
}
impl Walk for bool {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_bool(self);
}
}
impl Walk for char {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_char(self);
}
}
impl Walk for u128 {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_int(self);
}
}
impl Walk for u64 {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_smuggled_float(self);
}
}
impl Walk for str {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_string(self);
}
}
impl Walk for Literal {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_literal(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
Literal::Bool(value) => value.children(v),
Literal::Char(value) => value.children(v),
Literal::Int(value) => value.children(v),
Literal::Float(value) => value.children(v),
Literal::String(value) => value.children(v),
};
}
}
impl Walk for File {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_file(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let File { name: _, items } = self;
items.iter().for_each(|i| v.visit_item(i));
}
}
impl Walk for Attrs {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_attrs(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Attrs { meta } = self;
meta.children(v);
}
}
impl Walk for Meta {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_meta(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Meta { name, kind } = self;
name.visit_in(v);
kind.visit_in(v);
}
}
impl Walk for MetaKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_meta_kind(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
MetaKind::Plain => {}
MetaKind::Equals(lit) => lit.visit_in(v),
MetaKind::Func(lits) => lits.visit_in(v),
}
}
}
impl Walk for Item {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_item(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Item { span, attrs, vis, kind } = self;
span.visit_in(v);
attrs.visit_in(v);
vis.visit_in(v);
kind.visit_in(v);
}
}
impl Walk for ItemKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_item_kind(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
ItemKind::Module(value) => value.visit_in(v),
ItemKind::Alias(value) => value.visit_in(v),
ItemKind::Enum(value) => value.visit_in(v),
ItemKind::Struct(value) => value.visit_in(v),
ItemKind::Const(value) => value.visit_in(v),
ItemKind::Static(value) => value.visit_in(v),
ItemKind::Function(value) => value.visit_in(v),
ItemKind::Impl(value) => value.visit_in(v),
ItemKind::Use(value) => value.visit_in(v),
}
}
}
impl Walk for Generics {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_generics(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Self { vars } = self;
vars.visit_in(v);
}
}
impl Walk for Module {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_module(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Module { name, file } = self;
name.visit_in(v);
file.visit_in(v);
}
}
impl Walk for Alias {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_alias(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Alias { name, from } = self;
name.visit_in(v);
from.visit_in(v);
}
}
impl Walk for Const {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_const(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Const { name, ty, init } = self;
name.visit_in(v);
ty.visit_in(v);
init.visit_in(v);
}
}
impl Walk for Static {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_static(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Static { mutable, name, ty, init } = self;
mutable.visit_in(v);
name.visit_in(v);
ty.visit_in(v);
init.visit_in(v);
}
}
impl Walk for Function {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_function(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Function { name, gens, sign, bind, body } = self;
name.visit_in(v);
gens.visit_in(v);
sign.visit_in(v);
bind.visit_in(v);
body.visit_in(v);
}
}
impl Walk for Struct {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_struct(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Struct { name, gens, kind } = self;
name.visit_in(v);
gens.visit_in(v);
kind.visit_in(v);
}
}
impl Walk for StructKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_struct_kind(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
StructKind::Empty => {}
StructKind::Tuple(tys) => tys.visit_in(v),
StructKind::Struct(ms) => ms.visit_in(v),
}
}
}
impl Walk for StructMember {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_struct_member(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let StructMember { vis, name, ty } = self;
vis.visit_in(v);
name.visit_in(v);
ty.visit_in(v);
}
}
impl Walk for Enum {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_enum(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Enum { name, gens, variants } = self;
name.visit_in(v);
gens.visit_in(v);
variants.visit_in(v);
}
}
impl Walk for Variant {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_variant(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Variant { name, kind, body } = self;
name.visit_in(v);
kind.visit_in(v);
body.visit_in(v);
}
}
impl Walk for Impl {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_impl(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Impl { gens, target, body } = self;
gens.visit_in(v);
target.visit_in(v);
body.visit_in(v);
}
}
impl Walk for ImplKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_impl_kind(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
ImplKind::Type(t) => t.visit_in(v),
ImplKind::Trait { impl_trait, for_type } => {
impl_trait.visit_in(v);
for_type.visit_in(v);
}
}
}
}
impl Walk for Use {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_use(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Use { absolute: _, tree } = self;
tree.visit_in(v);
}
}
impl Walk for UseTree {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_use_tree(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
UseTree::Tree(tree) => tree.iter().for_each(|t| t.visit_in(v)),
UseTree::Path(part, tree) => {
part.visit_in(v);
tree.visit_in(v);
}
UseTree::Alias(from, to) => {
from.visit_in(v);
to.visit_in(v);
}
UseTree::Name(name) => name.visit_in(v),
UseTree::Glob => {}
}
}
}
impl Walk for Ty {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_ty(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Ty { span, kind, gens } = self;
span.visit_in(v);
kind.visit_in(v);
gens.visit_in(v);
}
}
impl Walk for TyKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_ty_kind(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
TyKind::Never => {}
TyKind::Infer => {}
TyKind::Path(value) => value.visit_in(v),
TyKind::Array(value) => value.visit_in(v),
TyKind::Slice(value) => value.visit_in(v),
TyKind::Tuple(value) => value.visit_in(v),
TyKind::Ref(value) => value.visit_in(v),
TyKind::Ptr(value) => value.visit_in(v),
TyKind::Fn(value) => value.visit_in(v),
}
}
}
impl Walk for TyArray {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_ty_array(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let TyArray { ty, count: _ } = self;
ty.visit_in(v);
// count.walk(v); // not available
}
}
impl Walk for TySlice {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_ty_slice(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let TySlice { ty } = self;
ty.visit_in(v);
}
}
impl Walk for TyTuple {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_ty_tuple(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let TyTuple { types } = self;
types.visit_in(v);
}
}
impl Walk for TyRef {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_ty_ref(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let TyRef { mutable, count: _, to } = self;
mutable.children(v);
to.children(v);
}
}
impl Walk for TyPtr {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_ty_ptr(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let TyPtr { to } = self;
to.children(v);
}
}
impl Walk for TyFn {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_ty_fn(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let TyFn { args, rety } = self;
args.visit_in(v);
rety.visit_in(v);
}
}
impl Walk for Path {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_path(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Path { absolute: _, parts } = self;
parts.visit_in(v);
}
}
impl Walk for PathPart {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_path_part(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
PathPart::SuperKw => {}
PathPart::SelfTy => {}
PathPart::Ident(sym) => sym.visit_in(v),
}
}
}
impl Walk for Stmt {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_stmt(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Stmt { span, kind, semi } = self;
span.visit_in(v);
kind.visit_in(v);
semi.visit_in(v);
}
}
impl Walk for StmtKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_stmt_kind(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
StmtKind::Empty => {}
StmtKind::Item(value) => value.visit_in(v),
StmtKind::Expr(value) => value.visit_in(v),
}
}
}
impl Walk for Semi {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_semi(self);
}
}
impl Walk for Expr {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_expr(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Expr { span, kind } = self;
span.visit_in(v);
kind.visit_in(v);
}
}
impl Walk for ExprKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_expr_kind(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
ExprKind::Empty => {}
ExprKind::Closure(value) => value.visit_in(v),
ExprKind::Tuple(value) => value.visit_in(v),
ExprKind::Structor(value) => value.visit_in(v),
ExprKind::Array(value) => value.visit_in(v),
ExprKind::ArrayRep(value) => value.visit_in(v),
ExprKind::AddrOf(value) => value.visit_in(v),
ExprKind::Quote(value) => value.visit_in(v),
ExprKind::Literal(value) => value.visit_in(v),
ExprKind::Group(value) => value.visit_in(v),
ExprKind::Block(value) => value.visit_in(v),
ExprKind::Assign(value) => value.visit_in(v),
ExprKind::Modify(value) => value.visit_in(v),
ExprKind::Binary(value) => value.visit_in(v),
ExprKind::Unary(value) => value.visit_in(v),
ExprKind::Member(value) => value.visit_in(v),
ExprKind::Index(value) => value.visit_in(v),
ExprKind::Cast(value) => value.visit_in(v),
ExprKind::Path(value) => value.visit_in(v),
ExprKind::Let(value) => value.visit_in(v),
ExprKind::Match(value) => value.visit_in(v),
ExprKind::While(value) => value.visit_in(v),
ExprKind::If(value) => value.visit_in(v),
ExprKind::For(value) => value.visit_in(v),
ExprKind::Break(value) => value.visit_in(v),
ExprKind::Return(value) => value.visit_in(v),
ExprKind::Continue => v.visit_continue(),
}
}
}
impl Walk for Closure {
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_closure(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Self { arg, body } = self;
v.visit_pattern(arg);
v.visit_expr(body);
}
}
impl Walk for Tuple {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_tuple(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Tuple { exprs } = self;
exprs.visit_in(v);
}
}
impl Walk for Structor {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_structor(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Structor { to, init } = self;
to.visit_in(v);
init.visit_in(v);
}
}
impl Walk for Fielder {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_fielder(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Fielder { name, init } = self;
name.visit_in(v);
init.visit_in(v);
}
}
impl Walk for Array {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_array(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Array { values } = self;
values.visit_in(v);
}
}
impl Walk for ArrayRep {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_array_rep(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let ArrayRep { value, repeat: _ } = self;
value.visit_in(v);
// repeat.visit_in(v) // TODO
}
}
impl Walk for AddrOf {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_addrof(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let AddrOf { mutable, expr } = self;
mutable.visit_in(v);
expr.visit_in(v);
}
}
impl Walk for Cast {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_cast(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Cast { head, ty } = self;
head.visit_in(v);
ty.visit_in(v);
}
}
impl Walk for Quote {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_quote(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Quote { quote } = self;
quote.visit_in(v);
}
}
impl Walk for Group {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_group(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Group { expr } = self;
expr.visit_in(v);
}
}
impl Walk for Block {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_block(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Block { stmts } = self;
stmts.visit_in(v);
}
}
impl Walk for Assign {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_assign(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Assign { parts } = self;
parts.visit_in(v);
}
}
impl Walk for Modify {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_modify(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Modify { kind, parts } = self;
kind.visit_in(v);
parts.visit_in(v);
}
}
impl Walk for ModifyKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_modify_kind(self);
}
}
impl Walk for Binary {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_binary(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Binary { kind, parts } = self;
kind.visit_in(v);
parts.visit_in(v);
}
}
impl Walk for BinaryKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_binary_kind(self);
}
}
impl Walk for Unary {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_unary(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Unary { kind, tail } = self;
kind.visit_in(v);
tail.visit_in(v);
}
}
impl Walk for UnaryKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_unary_kind(self);
}
}
impl Walk for Member {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_member(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Member { head, kind } = self;
head.visit_in(v);
kind.visit_in(v);
}
}
impl Walk for MemberKind {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_member_kind(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
MemberKind::Call(sym, tuple) => {
sym.visit_in(v);
tuple.visit_in(v);
}
MemberKind::Struct(sym) => sym.visit_in(v),
MemberKind::Tuple(literal) => literal.visit_in(v),
}
}
}
impl Walk for Index {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_index(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Index { head, indices } = self;
head.visit_in(v);
indices.visit_in(v);
}
}
impl Walk for Let {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_let(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Let { mutable, name, ty, init } = self;
mutable.visit_in(v);
name.visit_in(v);
ty.visit_in(v);
init.visit_in(v);
}
}
impl Walk for Match {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_match(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Match { scrutinee, arms } = self;
scrutinee.visit_in(v);
arms.visit_in(v);
}
}
impl Walk for MatchArm {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_match_arm(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let MatchArm(pat, expr) = self;
pat.visit_in(v);
expr.visit_in(v);
}
}
impl Walk for Pattern {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_pattern(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
match self {
Pattern::Name(sym) => sym.visit_in(v),
Pattern::Path(path) => path.visit_in(v),
Pattern::Literal(literal) => literal.visit_in(v),
Pattern::Rest(pattern) => pattern.visit_in(v),
Pattern::Ref(mutability, pattern) => {
mutability.visit_in(v);
pattern.visit_in(v);
}
Pattern::RangeExc(from, to) => {
from.visit_in(v);
to.visit_in(v);
}
Pattern::RangeInc(from, to) => {
from.visit_in(v);
to.visit_in(v);
}
Pattern::Tuple(patterns) => patterns.visit_in(v),
Pattern::Array(patterns) => patterns.visit_in(v),
Pattern::Struct(path, items) => {
path.visit_in(v);
items.visit_in(v);
}
Pattern::TupleStruct(path, patterns) => {
path.visit_in(v);
patterns.visit_in(v);
}
}
}
}
impl Walk for While {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_while(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let While { cond, pass, fail } = self;
cond.visit_in(v);
pass.visit_in(v);
fail.visit_in(v);
}
}
impl Walk for If {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_if(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let If { cond, pass, fail } = self;
cond.visit_in(v);
pass.visit_in(v);
fail.visit_in(v);
}
}
impl Walk for For {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_for(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let For { bind, cond, pass, fail } = self;
cond.visit_in(v);
fail.visit_in(v);
bind.visit_in(v);
pass.visit_in(v);
}
}
impl Walk for Else {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_else(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Else { body } = self;
body.visit_in(v);
}
}
impl Walk for Break {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_break(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Break { body } = self;
body.visit_in(v);
}
}
impl Walk for Return {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
v.visit_return(self);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let Return { body } = self;
body.visit_in(v);
}
}
// --- BLANKET IMPLEMENTATIONS
impl<T: Walk> Walk for [T] {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
self.iter().for_each(|value| value.visit_in(v));
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
self.iter().for_each(|value| value.children(v));
}
}
impl<T: Walk> Walk for Vec<T> {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
self.as_slice().visit_in(v);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
self.as_slice().children(v);
}
}
impl<A: Walk, B: Walk> Walk for (A, B) {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let (a, b) = self;
a.visit_in(v);
b.visit_in(v);
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
let (a, b) = self;
a.children(v);
b.children(v);
}
}
impl<T: Walk> Walk for Option<T> {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
if let Some(value) = self.as_ref() {
value.visit_in(v)
}
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
if let Some(value) = self {
value.children(v)
}
}
}
impl<T: Walk> Walk for Box<T> {
#[inline]
fn visit_in<'a, V: Visit<'a>>(&'a self, v: &mut V) {
self.as_ref().visit_in(v)
}
fn children<'a, V: Visit<'a>>(&'a self, v: &mut V) {
self.as_ref().children(v)
}
}

View File

@@ -1,9 +1,11 @@
//! Desugaring passes for Conlang
pub mod constant_folder;
pub mod path_absoluter;
pub mod squash_groups;
pub mod while_else;
pub use constant_folder::ConstantFolder;
pub use path_absoluter::NormalizePaths;
pub use squash_groups::SquashGroups;
pub use while_else::WhileElseDesugar;

View File

@@ -0,0 +1,90 @@
use crate::{
ast::{ExprKind as Ek, *},
ast_visitor::{Fold, fold::or_fold_expr_kind},
};
pub struct ConstantFolder;
macro bin_rule(
match ($kind: ident, $head: expr, $tail: expr) {
$(($op:ident, $impl:expr, $($ty:ident -> $rety:ident),*)),*$(,)?
}
) {
#[allow(clippy::all)]
match ($kind, $head, $tail) {
$($(( BinaryKind::$op,
Expr { kind: ExprKind::Literal(Literal::$ty(a)), .. },
Expr { kind: ExprKind::Literal(Literal::$ty(b)), .. },
) => {
ExprKind::Literal(Literal::$rety($impl(a, b)))
},)*)*
(kind, head, tail) => ExprKind::Binary(Binary {
kind,
parts: Box::new((head, tail)),
}),
}
}
macro un_rule(
match ($kind: ident, $tail: expr) {
$(($op:ident, $impl:expr, $($ty:ident),*)),*$(,)?
}
) {
match ($kind, $tail) {
$($((UnaryKind::$op, Expr { kind: ExprKind::Literal(Literal::$ty(v)), .. }) => {
ExprKind::Literal(Literal::$ty($impl(v)))
},)*)*
(kind, tail) => ExprKind::Unary(Unary { kind, tail: Box::new(tail) }),
}
}
impl Fold for ConstantFolder {
fn fold_expr_kind(&mut self, kind: Ek) -> Ek {
match kind {
Ek::Group(Group { expr }) => self.fold_expr_kind(expr.kind),
Ek::Binary(Binary { kind, parts }) => {
let (head, tail) = *parts;
bin_rule! (match (kind, self.fold_expr(head), self.fold_expr(tail)) {
(Lt, |a, b| a < b, Bool -> Bool, Int -> Bool),
(LtEq, |a, b| a <= b, Bool -> Bool, Int -> Bool),
(Equal, |a, b| a == b, Bool -> Bool, Int -> Bool),
(NotEq, |a, b| a != b, Bool -> Bool, Int -> Bool),
(GtEq, |a, b| a >= b, Bool -> Bool, Int -> Bool),
(Gt, |a, b| a > b, Bool -> Bool, Int -> Bool),
(BitAnd, |a, b| a & b, Bool -> Bool, Int -> Int),
(BitOr, |a, b| a | b, Bool -> Bool, Int -> Int),
(BitXor, |a, b| a ^ b, Bool -> Bool, Int -> Int),
(Shl, |a, b| a << b, Int -> Int),
(Shr, |a, b| a >> b, Int -> Int),
(Add, |a, b| a + b, Int -> Int),
(Sub, |a, b| a - b, Int -> Int),
(Mul, |a, b| a * b, Int -> Int),
(Div, |a, b| a / b, Int -> Int),
(Rem, |a, b| a % b, Int -> Int),
// Cursed bit-smuggled float shenanigans
(Lt, |a, b| f64::from_bits(a) < f64::from_bits(b), Float -> Bool),
(LtEq, |a, b| f64::from_bits(a) >= f64::from_bits(b), Float -> Bool),
(Equal, |a, b| f64::from_bits(a) == f64::from_bits(b), Float -> Bool),
(NotEq, |a, b| f64::from_bits(a) != f64::from_bits(b), Float -> Bool),
(GtEq, |a, b| f64::from_bits(a) <= f64::from_bits(b), Float -> Bool),
(Gt, |a, b| f64::from_bits(a) > f64::from_bits(b), Float -> Bool),
(Add, |a, b| (f64::from_bits(a) + f64::from_bits(b)).to_bits(), Float -> Float),
(Sub, |a, b| (f64::from_bits(a) - f64::from_bits(b)).to_bits(), Float -> Float),
(Mul, |a, b| (f64::from_bits(a) * f64::from_bits(b)).to_bits(), Float -> Float),
(Div, |a, b| (f64::from_bits(a) / f64::from_bits(b)).to_bits(), Float -> Float),
(Rem, |a, b| (f64::from_bits(a) % f64::from_bits(b)).to_bits(), Float -> Float),
})
}
Ek::Unary(Unary { kind, tail }) => {
un_rule! (match (kind, self.fold_expr(*tail)) {
(Not, std::ops::Not::not, Int, Bool),
(Neg, std::ops::Not::not, Bool),
(Neg, |i| -(i as i128) as u128, Int),
(Neg, |f| (-f64::from_bits(f)).to_bits(), Float),
(At, std::ops::Not::not, Float), /* Lmao */
})
}
_ => or_fold_expr_kind(self, kind),
}
}
}

View File

@@ -23,13 +23,14 @@ impl Default for NormalizePaths {
impl Fold for NormalizePaths {
fn fold_module(&mut self, m: Module) -> Module {
let Module { name, kind } = m;
let Module { name, file } = m;
self.path.push(PathPart::Ident(name));
let (name, kind) = (self.fold_sym(name), self.fold_module_kind(kind));
let name = self.fold_sym(name);
let file = file.map(|f| self.fold_file(f));
self.path.pop();
Module { name, kind }
Module { name, file }
}
fn fold_path(&mut self, p: Path) -> Path {
@@ -45,7 +46,7 @@ impl Fold for NormalizePaths {
if !absolute {
for segment in self.path.parts.iter().rev() {
tree = UseTree::Path(segment.clone(), Box::new(tree))
tree = UseTree::Path(*segment, Box::new(tree))
}
}

View File

@@ -7,7 +7,7 @@ pub struct SquashGroups;
impl Fold for SquashGroups {
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
match kind {
ExprKind::Group(Group { expr }) => self.fold_expr_kind(*expr),
ExprKind::Group(Group { expr }) => self.fold_expr(*expr).kind,
_ => or_fold_expr_kind(self, kind),
}
}

View File

@@ -10,24 +10,27 @@ pub struct WhileElseDesugar;
impl Fold for WhileElseDesugar {
fn fold_expr(&mut self, e: Expr) -> Expr {
let Expr { extents, kind } = e;
let kind = desugar_while(extents, kind);
Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) }
let Expr { span, kind } = e;
let kind = desugar_while(span, kind);
Expr { span: self.fold_span(span), kind: self.fold_expr_kind(kind) }
}
}
/// Desugars while(-else) expressions into loop-if-else-break expressions
fn desugar_while(extents: Span, kind: ExprKind) -> ExprKind {
fn desugar_while(span: Span, kind: ExprKind) -> ExprKind {
match kind {
// work backwards: fail -> break -> if -> loop
ExprKind::While(While { cond, pass, fail: Else { body } }) => {
// Preserve the else-expression's extents, if present, or use the parent's extents
let fail_span = body.as_ref().map(|body| body.extents).unwrap_or(extents);
let break_expr = Expr { extents: fail_span, kind: ExprKind::Break(Break { body }) };
// Preserve the else-expression's span, if present, or use the parent's span
let fail_span = body.as_ref().map(|body| body.span).unwrap_or(span);
let break_expr = Expr { span: fail_span, kind: ExprKind::Break(Break { body }) };
let loop_body = If { cond, pass, fail: Else { body: Some(Box::new(break_expr)) } };
let loop_body = Expr { extents, kind: ExprKind::If(loop_body) };
ExprKind::Loop(Loop { body: Box::new(loop_body) })
let loop_body = ExprKind::If(loop_body);
ExprKind::Unary(Unary {
kind: UnaryKind::Loop,
tail: Box::new(Expr { span, kind: loop_body }),
})
}
_ => kind,
}

View File

@@ -3,15 +3,15 @@ use std::fmt::Write;
impl<W: Write + ?Sized> FmtAdapter for W {}
pub trait FmtAdapter: Write {
fn indent(&mut self) -> Indent<Self> {
fn indent(&mut self) -> Indent<'_, Self> {
Indent { f: self }
}
fn delimit(&mut self, delim: Delimiters) -> Delimit<Self> {
fn delimit(&mut self, delim: Delimiters) -> Delimit<'_, Self> {
Delimit::new(self, delim)
}
fn delimit_with(&mut self, open: &'static str, close: &'static str) -> Delimit<Self> {
fn delimit_with(&mut self, open: &'static str, close: &'static str) -> Delimit<'_, Self> {
Delimit::new(self, Delimiters { open, close })
}
}
@@ -21,7 +21,7 @@ pub struct Indent<'f, F: Write + ?Sized> {
f: &'f mut F,
}
impl<'f, F: Write + ?Sized> Write for Indent<'f, F> {
impl<F: Write + ?Sized> Write for Indent<'_, F> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
for s in s.split_inclusive('\n') {
self.f.write_str(s)?;
@@ -45,14 +45,14 @@ impl<'f, F: Write + ?Sized> Delimit<'f, F> {
Self { f, delim }
}
}
impl<'f, F: Write + ?Sized> Drop for Delimit<'f, F> {
impl<F: Write + ?Sized> Drop for Delimit<'_, F> {
fn drop(&mut self) {
let Self { f: Indent { f, .. }, delim } = self;
let _ = f.write_str(delim.close);
}
}
impl<'f, F: Write + ?Sized> Write for Delimit<'f, F> {
impl<F: Write + ?Sized> Write for Delimit<'_, F> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.f.write_str(s)
}

View File

@@ -8,11 +8,13 @@
//! - [Assign], [Binary], and [Unary] expressions
//! - [ModifyKind], [BinaryKind], and [UnaryKind] operators
//! - [Ty] and [TyKind]: Type qualifiers
//! - [Pattern]: Pattern matching operators
//! - [Path]: Path expressions
#![warn(clippy::all)]
#![feature(decl_macro)]
pub use ast::*;
pub use ast_impl::weight_of::WeightOf;
pub mod ast;
pub mod ast_impl;

View File

@@ -0,0 +1,18 @@
[package]
name = "cl-embed"
version = "0.1.0"
repository.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
cl-interpret = { path = "../cl-interpret" }
cl-ast = { path = "../cl-ast" }
cl-structures = { path = "../cl-structures" }
cl-lexer = { path = "../cl-lexer" }
cl-parser = { path = "../cl-parser" }
[dev-dependencies]
repline = { version = "*", registry = "soft-fish" }

View File

@@ -0,0 +1,27 @@
//! Demonstrates the cl_embed library
use cl_embed::*;
use repline::{Response, prebaked};
fn main() -> Result<(), repline::Error> {
let mut env = Environment::new();
if let Err(e) = conlang_include!("calculator/expression.cl")(&mut env) {
panic!("{e}")
}
prebaked::read_and("", "calc >", " ? >", |line| {
env.bind("line", line);
let res = conlang! {
let (expr, rest) = parse(line.chars(), Power::None);
execute(expr)
}(&mut env)?;
println!("{res}");
Ok(Response::Accept)
})
}

View File

@@ -0,0 +1 @@
../../../../sample-code/calculator.cl

View File

@@ -0,0 +1,182 @@
//! Embed Conlang code into your Rust project!
//!
//! # This crate is experimental, and has no guarantees of stability.
#![feature(decl_macro)]
#![cfg_attr(test, feature(assert_matches))]
#![allow(unused_imports)]
pub use cl_interpret::{convalue::ConValue as Value, env::Environment};
use cl_ast::{Block, File, Module, ast_visitor::Fold};
use cl_interpret::{convalue::ConValue, interpret::Interpret};
use cl_lexer::Lexer;
use cl_parser::{Parser, error::Error as ParseError, inliner::ModuleInliner};
use std::{path::Path, sync::OnceLock};
/// Constructs a function which evaluates a Conlang Block
///
/// # Examples
///
/// Bind and use a variable
/// ```rust
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use cl_embed::{conlang, Environment, Value};
///
/// let mut env = Environment::new();
///
/// // Bind a variable named `message` to "Hello, world!"
/// env.bind("message", "Hello, World!");
///
/// let print_hello = conlang!{
/// println(message);
/// };
///
/// // Run the function
/// let ret = print_hello(&mut env)?;
///
/// // `println` returns Empty
/// assert!(matches!(ret, Value::Empty));
///
/// # Ok(())
/// # }
/// ```
pub macro conlang(
$($t:tt)*
) {{
// Parse once
static FN: OnceLock<Result<Block, ParseError>> = OnceLock::new();
|env: &mut Environment| -> Result<ConValue, EvalError> {
FN.get_or_init(|| {
// TODO: embed the full module tree at compile time
let path =
AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!()))
.with_extension("");
let mut mi = ModuleInliner::new(path);
let code = mi.fold_block(
Parser::new(
concat!(file!(), ":", line!(), ":", column!()),
Lexer::new(stringify!({ $($t)* })),
)
.parse::<Block>()?,
);
if let Some((ie, pe)) = mi.into_errs() {
for (file, err) in ie {
eprintln!("{}: {err}", file.display());
}
for (file, err) in pe {
eprintln!("{}: {err}", file.display());
}
}
Ok(code)
})
.as_ref()
.map_err(Clone::clone)?
.interpret(env)
.map_err(Into::into)
}
}}
pub macro conlang_include{
($path:literal, $name:ident) => {
|env: &mut Environment| -> Result<ConValue, EvalError> {
// TODO: embed the full module tree at compile time
let path = AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!()))
.with_file_name(concat!($path));
let mut mi = ModuleInliner::new(path);
let code = mi.fold_module(Module {
name: stringify!($name).into(),
file: Some(
Parser::new(
concat!(file!(), ":", line!(), ":", column!()),
Lexer::new(include_str!($path)),
)
.parse()?,
),
});
if let Some((ie, pe)) = mi.into_errs() {
for (file, err) in ie {
eprintln!("{}: {err}", file.display());
}
for (file, err) in pe {
eprintln!("{}: {err}", file.display());
}
}
code.interpret(env).map_err(Into::into)
}
},
($path:literal) => {
|env: &mut Environment| -> Result<ConValue, EvalError> {
// TODO: embed the full module tree at compile time
let path = AsRef::<Path>::as_ref(&concat!(env!("CARGO_MANIFEST_DIR"), "/../../", file!()))
.with_file_name(concat!($path));
let mut mi = ModuleInliner::new(path);
let code = mi.fold_file(
Parser::new(
concat!(file!(), ":", line!(), ":", column!()),
Lexer::new(include_str!($path)),
)
.parse()?,
);
if let Some((ie, pe)) = mi.into_errs() {
for (file, err) in ie {
eprintln!("{}: {err}", file.display());
}
for (file, err) in pe {
eprintln!("{}: {err}", file.display());
}
}
code.interpret(env).map_err(Into::into)
}
}
}
#[derive(Clone, Debug)]
pub enum EvalError {
Parse(cl_parser::error::Error),
Interpret(cl_interpret::error::Error),
}
impl From<cl_parser::error::Error> for EvalError {
fn from(value: cl_parser::error::Error) -> Self {
Self::Parse(value)
}
}
impl From<cl_interpret::error::Error> for EvalError {
fn from(value: cl_interpret::error::Error) -> Self {
Self::Interpret(value)
}
}
impl std::error::Error for EvalError {}
impl std::fmt::Display for EvalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EvalError::Parse(error) => error.fmt(f),
EvalError::Interpret(error) => error.fmt(f),
}
}
}
#[cfg(test)]
mod tests {
use std::assert_matches::assert_matches;
use super::*;
#[test]
fn it_works() -> Result<(), EvalError> {
let mut env = Environment::new();
let result = conlang! {
fn add(left, right) -> isize {
left + right
}
add(2, 2)
}(&mut env);
assert_matches!(result, Ok(Value::Int(4)));
Ok(())
}
}

View File

@@ -0,0 +1,59 @@
//! A bare-minimum harness to evaluate a Conlang program
use std::{error::Error, path::PathBuf};
use cl_ast::Expr;
use cl_interpret::{convalue::ConValue, env::Environment};
use cl_lexer::Lexer;
use cl_parser::{Parser, inliner::ModuleInliner};
fn main() -> Result<(), Box<dyn Error>> {
let mut args = std::env::args();
let prog = args.next().unwrap();
let Some(path) = args.next().map(PathBuf::from) else {
println!("Usage: {prog} `file.cl` [ args... ]");
return Ok(());
};
let parent = path.parent().unwrap_or("".as_ref());
let code = std::fs::read_to_string(&path)?;
let code = Parser::new(path.display().to_string(), Lexer::new(&code)).parse()?;
let code = match ModuleInliner::new(parent).inline(code) {
Ok(code) => code,
Err((code, ioerrs, perrs)) => {
for (p, err) in ioerrs {
eprintln!("{}:{err}", p.display());
}
for (p, err) in perrs {
eprintln!("{}:{err}", p.display());
}
code
}
};
let mut env = Environment::new();
env.eval(&code)?;
let main = "main".into();
if env.get(main).is_ok() {
let args = args
.flat_map(|arg| {
Parser::new(&arg, Lexer::new(&arg))
.parse::<Expr>()
.map(|arg| env.eval(&arg))
})
.collect::<Result<Vec<_>, _>>()?;
match env.call(main, &args) {
Ok(ConValue::Empty) => {}
Ok(retval) => println!("{retval}"),
Err(e) => {
panic!("{e}");
}
}
}
Ok(())
}

View File

@@ -8,9 +8,5 @@ fn main() {
/// Implements the classic recursive definition of fib()
fn fib(a: i64) -> i64 {
if a > 1 {
fib(a - 1) + fib(a - 2)
} else {
1
}
if a > 1 { fib(a - 1) + fib(a - 2) } else { a }
}

View File

@@ -1,284 +1,410 @@
//! Implementations of built-in functions
#![allow(non_upper_case_globals)]
use super::{
use crate::{
convalue::ConValue,
env::Environment,
error::{Error, IResult},
BuiltIn, Callable,
};
use cl_ast::Sym;
use std::{
io::{stdout, Write},
rc::Rc,
};
use std::io::{Write, stdout};
builtins! {
const MISC;
/// Unstable variadic format function
pub fn format<_, args> () -> IResult<ConValue> {
use std::fmt::Write;
let mut out = String::new();
for arg in args {
write!(out, "{arg}").ok();
}
Ok(ConValue::String(out.into()))
/// A function built into the interpreter.
#[derive(Clone, Copy)]
pub struct Builtin {
/// An identifier to be used during registration
pub name: &'static str,
/// The signature, displayed when the builtin is printed
pub desc: &'static str,
/// The function to be run when called
pub func: &'static dyn Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
}
impl Builtin {
/// Constructs a new Builtin
pub const fn new(
name: &'static str,
desc: &'static str,
func: &'static impl Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
) -> Builtin {
Builtin { name, desc, func }
}
/// Unstable variadic print function
pub fn print<_, args> () -> IResult<ConValue> {
let mut out = stdout().lock();
for arg in args {
write!(out, "{arg}").ok();
}
Ok(ConValue::Empty)
}
/// Unstable variadic println function
pub fn println<_, args> () -> IResult<ConValue> {
let mut out = stdout().lock();
for arg in args {
write!(out, "{arg}").ok();
}
writeln!(out).ok();
Ok(ConValue::Empty)
}
/// Prints the [Debug](std::fmt::Debug) version of the input values
pub fn dbg<_, args> () -> IResult<ConValue> {
let mut out = stdout().lock();
for arg in args {
writeln!(out, "{arg:?}").ok();
}
Ok(args.into())
}
/// Dumps info from the environment
pub fn dump<env, _>() -> IResult<ConValue> {
println!("{}", *env);
Ok(ConValue::Empty)
pub const fn description(&self) -> &'static str {
self.desc
}
}
builtins! {
const BINARY;
impl std::fmt::Debug for Builtin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Builtin")
.field("description", &self.desc)
.finish_non_exhaustive()
}
}
impl std::fmt::Display for Builtin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.desc)
}
}
impl super::Callable for Builtin {
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
(self.func)(interpreter, args)
}
fn name(&self) -> cl_ast::Sym {
self.name.into()
}
}
/// Turns a function definition into a [Builtin].
///
/// ```rust
/// # use cl_interpret::{builtin::builtin, convalue::ConValue};
/// let my_builtin = builtin! {
/// /// Use the `@env` suffix to bind the environment!
/// /// (needed for recursive calls)
/// fn my_builtin(ConValue::Bool(b), rest @ ..) @env {
/// // This is all Rust code!
/// eprintln!("my_builtin({b}, ..)");
/// match rest {
/// [] => Ok(ConValue::Empty),
/// _ => my_builtin(env, rest), // Can be called as a normal function!
/// }
/// }
/// };
/// ```
pub macro builtin(
$(#[$($meta:tt)*])*
fn $name:ident ($($arg:pat),*$(,)?) $(@$env:tt)? $body:block
) {{
$(#[$($meta)*])*
fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
// Set up the builtin! environment
$(#[allow(unused)]let $env = _env;)?
// Allow for single argument `fn foo(args @ ..)` pattern
#[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
let [$($arg),*] = _args else {
Err($crate::error::Error::TypeError())?
};
$body.map(Into::into)
}
Builtin {
name: stringify!($name),
desc: stringify![builtin fn $name($($arg),*)],
func: &$name,
}
}}
/// Constructs an array of [Builtin]s from pseudo-function definitions
pub macro builtins($(
$(#[$($meta:tt)*])*
fn $name:ident ($($args:tt)*) $(@$env:tt)? $body:block
)*) {
[$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
}
/// Creates an [Error::BuiltinError] using interpolation of runtime expressions.
/// See [std::format].
pub macro error_format ($($t:tt)*) {
$crate::error::Error::BuiltinError(format!($($t)*))
}
pub const Builtins: &[Builtin] = &builtins![
/// Unstable variadic format function
fn fmt(args @ ..) {
use std::fmt::Write;
let mut out = String::new();
if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
eprintln!("{e}");
}
Ok(out)
}
/// Prints the arguments in-order, with no separators
fn print(args @ ..) {
let mut out = stdout().lock();
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
Ok(())
}
/// Prints the arguments in-order, followed by a newline
fn println(args @ ..) {
let mut out = stdout().lock();
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
writeln!(out).ok();
Ok(())
}
/// Debug-prints the argument, returning a copy
fn dbg(arg) {
println!("{arg:?}");
Ok(arg.clone())
}
/// Debug-prints the argument
fn dbgp(args @ ..) {
let mut out = stdout().lock();
args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
Ok(())
}
fn panic(args @ ..) @env {
use std::fmt::Write;
let mut out = String::new();
if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
println!("{e}");
}
let mut stdout = stdout().lock();
write!(stdout, "Explicit panic: `").ok();
args.iter().try_for_each(|arg| write!(stdout, "{arg}") ).ok();
writeln!(stdout, "`").ok();
Err(Error::Panic(out))?;
Ok(())
}
/// Dumps the environment
fn dump() @env {
println!("{env}");
Ok(())
}
/// Gets all global variables in the environment
fn globals() @env {
let globals = env.globals();
Ok(ConValue::Slice(globals.base, globals.binds.len()))
}
fn builtins() @env {
let len = env.globals().binds.len();
for builtin in 0..len {
if let Some(value @ ConValue::Builtin(_)) = env.get_id(builtin) {
println!("{builtin}: {value}")
}
}
Ok(())
}
fn alloca(ConValue::Int(len)) @env {
Ok(env.alloca(ConValue::Empty, *len as usize))
}
/// Returns the length of the input list as a [ConValue::Int]
fn len(list) @env {
Ok(match list {
ConValue::Empty => 0,
ConValue::Str(s) => s.chars().count() as _,
ConValue::String(s) => s.chars().count() as _,
ConValue::Ref(r) => {
return len(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
}
ConValue::Slice(_, len) => *len as _,
ConValue::Array(arr) => arr.len() as _,
ConValue::Tuple(t) => t.len() as _,
_ => Err(Error::TypeError())?,
})
}
fn push(ConValue::Ref(index), item) @env{
let Some(ConValue::Array(v)) = env.get_id_mut(*index) else {
Err(Error::TypeError())?
};
let mut items = std::mem::take(v).into_vec();
items.push(item.clone());
*v = items.into_boxed_slice();
Ok(ConValue::Empty)
}
fn chars(string) @env {
Ok(match string {
ConValue::Str(s) => ConValue::Array(s.chars().map(Into::into).collect()),
ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
ConValue::Ref(r) => {
return chars(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
}
_ => Err(Error::TypeError())?,
})
}
fn dump_symbols() {
println!("{}", cl_structures::intern::string_interner::StringInterner::global());
Ok(ConValue::Empty)
}
fn slice_of(ConValue::Ref(arr), ConValue::Int(start)) {
Ok(ConValue::Slice(*arr, *start as usize))
}
/// Returns a shark
fn shark() {
Ok('\u{1f988}')
}
];
pub const Math: &[Builtin] = &builtins![
/// Multiplication `a * b`
pub fn mul(lhs, rhs) -> IResult<ConValue> {
fn mul(lhs, rhs) {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
_ => Err(Error::TypeError)?
_ => Err(Error::TypeError())?
})
}
/// Division `a / b`
pub fn div(lhs, rhs) -> IResult<ConValue> {
fn div(lhs, rhs) {
Ok(match (lhs, rhs){
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
_ => Err(Error::TypeError)?
_ => Err(Error::TypeError())?
})
}
/// Remainder `a % b`
pub fn rem(lhs, rhs) -> IResult<ConValue> {
fn rem(lhs, rhs) {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
_ => Err(Error::TypeError)?,
_ => Err(Error::TypeError())?,
})
}
/// Addition `a + b`
pub fn add(lhs, rhs) -> IResult<ConValue> {
fn add(lhs, rhs) {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
_ => Err(Error::TypeError)?
(ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + b).into(),
(ConValue::Str(a), ConValue::String(b)) => (a.to_string() + b).into(),
(ConValue::String(a), ConValue::Str(b)) => (a.to_string() + b).into(),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(),
(ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
(ConValue::Char(a), ConValue::Char(b)) => {
ConValue::String([a, b].into_iter().collect::<String>())
}
_ => Err(Error::TypeError())?
})
}
/// Subtraction `a - b`
pub fn sub(lhs, rhs) -> IResult<ConValue> {
fn sub(lhs, rhs) {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
_ => Err(Error::TypeError)?,
_ => Err(Error::TypeError())?,
})
}
/// Shift Left `a << b`
pub fn shl(lhs, rhs) -> IResult<ConValue> {
fn shl(lhs, rhs) {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
_ => Err(Error::TypeError)?,
_ => Err(Error::TypeError())?,
})
}
/// Shift Right `a >> b`
pub fn shr(lhs, rhs) -> IResult<ConValue> {
fn shr(lhs, rhs) {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
_ => Err(Error::TypeError)?,
_ => Err(Error::TypeError())?,
})
}
/// Bitwise And `a & b`
pub fn and(lhs, rhs) -> IResult<ConValue> {
fn and(lhs, rhs) {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError)?,
_ => Err(Error::TypeError())?,
})
}
/// Bitwise Or `a | b`
pub fn or(lhs, rhs) -> IResult<ConValue> {
fn or(lhs, rhs) {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError)?,
_ => Err(Error::TypeError())?,
})
}
/// Bitwise Exclusive Or `a ^ b`
pub fn xor(lhs, rhs) -> IResult<ConValue> {
fn xor(lhs, rhs) {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError)?,
_ => Err(Error::TypeError())?,
})
}
/// Tests whether `a < b`
pub fn lt(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, <)
#[allow(non_snake_case)]
fn RangeExc(start, end) @env {
Ok(ConValue::TupleStruct("RangeExc".into(), Box::new(Box::new([start.clone(), end.clone()]))))
}
/// Tests whether `a <= b`
pub fn lt_eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, <=)
#[allow(non_snake_case)]
fn RangeInc(start, end) @env {
Ok(ConValue::TupleStruct("RangeInc".into(), Box::new(Box::new([start.clone(), end.clone()]))))
}
/// Tests whether `a == b`
pub fn eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, ==)
#[allow(non_snake_case)]
fn RangeTo(end) @env {
Ok(ConValue::TupleStruct("RangeTo".into(), Box::new(Box::new([end.clone()]))))
}
/// Tests whether `a != b`
pub fn neq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, !=)
#[allow(non_snake_case)]
fn RangeToInc(end) @env {
Ok(ConValue::TupleStruct("RangeToInc".into(), Box::new(Box::new([end.clone()]))))
}
/// Tests whether `a <= b`
pub fn gt_eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, >=)
}
/// Tests whether `a < b`
pub fn gt(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, >)
}
}
builtins! {
const RANGE;
/// Exclusive Range `a..b`
pub fn range_exc(lhs, rhs) -> IResult<ConValue> {
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
Err(Error::TypeError)?
};
Ok(ConValue::RangeExc(lhs, rhs.saturating_sub(1)))
}
/// Inclusive Range `a..=b`
pub fn range_inc(lhs, rhs) -> IResult<ConValue> {
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
Err(Error::TypeError)?
};
Ok(ConValue::RangeInc(lhs, rhs))
}
}
builtins! {
const UNARY;
/// Negates the ConValue
pub fn neg(tail) -> IResult<ConValue> {
fn neg(tail) {
Ok(match tail {
ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
_ => Err(Error::TypeError)?,
ConValue::Float(v) => ConValue::Float(-v),
_ => Err(Error::TypeError())?,
})
}
/// Inverts the ConValue
pub fn not(tail) -> IResult<ConValue> {
fn not(tail) {
Ok(match tail {
ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(!v),
ConValue::Bool(v) => ConValue::Bool(!v),
_ => Err(Error::TypeError)?,
_ => Err(Error::TypeError())?,
})
}
pub fn deref(tail) -> IResult<ConValue> {
/// Compares two values
fn cmp(head, tail) {
Ok(ConValue::Int(match (head, tail) {
(ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
(ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
(ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
(ConValue::Str(a), ConValue::Str(b)) => a.cmp(b) as _,
(ConValue::Str(a), ConValue::String(b)) => a.to_ref().cmp(b.as_str()) as _,
(ConValue::String(a), ConValue::Str(b)) => a.as_str().cmp(b.to_ref()) as _,
(ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
_ => Err(error_format!("Incomparable values: {head}, {tail}"))?
}))
}
/// Does the opposite of `&`
fn deref(tail) @env {
Ok(match tail {
ConValue::Ref(v) => Rc::as_ref(v).clone(),
ConValue::Ref(v) => env.get_id(*v).cloned().ok_or(Error::StackOverflow(*v))?,
_ => tail.clone(),
})
}
}
/// Turns an argument slice into an array with the (inferred) correct number of elements
pub fn to_args<const N: usize>(args: &[ConValue]) -> IResult<&[ConValue; N]> {
args.try_into()
.map_err(|_| Error::ArgNumber { want: N, got: args.len() })
}
/// Turns function definitions into ZSTs which implement [Callable] and [BuiltIn]
macro builtins (
$(prefix = $prefix:literal)?
const $defaults:ident $( = [$($additional_builtins:expr),*$(,)?])?;
$(
$(#[$meta:meta])*$vis:vis fn $name:ident$(<$env:tt, $args:tt>)? ( $($($arg:tt),+$(,)?)? ) $(-> $rety:ty)?
$body:block
)*
) {
/// Builtins to load when a new interpreter is created
pub const $defaults: &[&dyn BuiltIn] = &[$(&$name,)* $($additional_builtins)*];
$(
$(#[$meta])* #[allow(non_camel_case_types)] #[derive(Clone, Debug)]
/// ```rust,ignore
#[doc = stringify!(builtin! fn $name($($($arg),*)?) $(-> $rety)? $body)]
/// ```
$vis struct $name;
impl BuiltIn for $name {
fn description(&self) -> &str { concat!("builtin ", stringify!($name), stringify!(($($($arg),*)?) )) }
}
impl Callable for $name {
#[allow(unused)]
fn call(&self, env: &mut Environment, args: &[ConValue]) $(-> $rety)? {
// println!("{}", stringify!($name), );
$(let $env = env;
let $args = args;)?
$(let [$($arg),*] = to_args(args)?;)?
$body
}
fn name(&self) -> Sym { stringify!($name).into() }
}
)*
}
/// Templates comparison functions for [ConValue]
macro cmp ($a:expr, $b:expr, $empty:literal, $op:tt) {
match ($a, $b) {
(ConValue::Empty, ConValue::Empty) => Ok(ConValue::Bool($empty)),
(ConValue::Int(a), ConValue::Int(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Bool(a), ConValue::Bool(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Char(a), ConValue::Char(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::String(a), ConValue::String(b)) => Ok(ConValue::Bool(&**a $op &**b)),
_ => Err(Error::TypeError)
}
}
];

View File

@@ -0,0 +1,68 @@
use crate::{
Callable,
convalue::ConValue,
env::Environment,
error::{Error, ErrorKind, IResult},
function::collect_upvars::CollectUpvars,
interpret::Interpret,
pattern,
};
use cl_ast::{Sym, ast_visitor::Visit};
use std::{collections::HashMap, fmt::Display};
/// Represents an ad-hoc anonymous function
/// which captures surrounding state by COPY
#[derive(Clone, Debug)]
pub struct Closure {
decl: cl_ast::Closure,
lift: HashMap<Sym, ConValue>,
}
impl Closure {
const NAME: &'static str = "{closure}";
}
impl Closure {
pub fn new(env: &mut Environment, decl: &cl_ast::Closure) -> Self {
let lift = CollectUpvars::new(env).visit(decl).finish_copied();
Self { decl: decl.clone(), lift }
}
}
impl Display for Closure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { decl, lift: _ } = self;
write!(f, "{decl}")
}
}
impl Callable for Closure {
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
let Self { decl, lift } = self;
let mut env = env.frame(Self::NAME);
// place lifts in scope
for (name, value) in lift.clone() {
env.insert(name, value);
}
let mut env = env.frame("args");
for (name, value) in pattern::substitution(&env, &decl.arg, ConValue::Tuple(args.into()))? {
env.insert(name, value);
}
let res = decl.body.interpret(&mut env);
drop(env);
match res {
Err(Error { kind: ErrorKind::Return(value), .. }) => Ok(value),
Err(Error { kind: ErrorKind::Break(value), .. }) => Err(Error::BadBreak(value)),
other => other,
}
}
fn name(&self) -> cl_ast::Sym {
Self::NAME.into()
}
}

View File

@@ -0,0 +1,449 @@
//! Values in the dynamically typed AST interpreter.
//!
//! The most permanent fix is a temporary one.
use cl_ast::{Expr, Sym, format::FmtAdapter};
use crate::{closure::Closure, constructor::Constructor};
use super::{
Callable, Environment,
builtin::Builtin,
error::{Error, IResult},
function::Function,
};
use std::{collections::HashMap, ops::*, rc::Rc};
/*
A Value can be:
- A Primitive (Empty, isize, etc.)
- A Record (Array, Tuple, Struct)
- A Variant (discriminant, Value) pair
array [
10, // 0
20, // 1
]
tuple (
10, // 0
20, // 1
)
struct {
x: 10, // x => 0
y: 20, // y => 1
}
*/
type Integer = isize;
/// A Conlang value stores data in the interpreter
#[derive(Clone, Debug, Default)]
pub enum ConValue {
/// The empty/unit `()` type
#[default]
Empty,
/// An integer
Int(Integer),
/// A floating point number
Float(f64),
/// A boolean
Bool(bool),
/// A unicode character
Char(char),
/// A string literal
Str(Sym),
/// A dynamic string
String(String),
/// A reference
Ref(usize),
/// A reference to an array
Slice(usize, usize),
/// An Array
Array(Box<[ConValue]>),
/// A tuple
Tuple(Box<[ConValue]>),
// TODO: Instead of storing the identifier, store the index of the struct module
/// A value of a product type
Struct(Sym, Box<HashMap<Sym, ConValue>>),
/// A value of a product type with anonymous members
TupleStruct(Sym, Box<Box<[ConValue]>>),
/// An entire namespace
Module(Box<HashMap<Sym, ConValue>>),
/// A quoted expression
Quote(Rc<Expr>),
/// A callable thing
Function(Rc<Function>),
/// A tuple constructor
TupleConstructor(Constructor),
/// A closure, capturing by reference
Closure(Rc<Closure>),
/// A built-in function
Builtin(&'static Builtin),
}
impl ConValue {
/// Gets whether the current value is true or false
pub fn truthy(&self) -> IResult<bool> {
match self {
ConValue::Bool(v) => Ok(*v),
_ => Err(Error::TypeError())?,
}
}
pub fn typename(&self) -> &'static str {
match self {
ConValue::Empty => "Empty",
ConValue::Int(_) => "i64",
ConValue::Float(_) => "f64",
ConValue::Bool(_) => "bool",
ConValue::Char(_) => "char",
ConValue::Str(_) => "str",
ConValue::String(_) => "String",
ConValue::Ref(_) => "Ref",
ConValue::Slice(_, _) => "Slice",
ConValue::Array(_) => "Array",
ConValue::Tuple(_) => "Tuple",
ConValue::Struct(_, _) => "Struct",
ConValue::TupleStruct(_, _) => "TupleStruct",
ConValue::Module(_) => "",
ConValue::Quote(_) => "Quote",
ConValue::Function(_) => "Fn",
ConValue::TupleConstructor(_) => "Fn",
ConValue::Closure(_) => "Fn",
ConValue::Builtin(_) => "Fn",
}
}
#[allow(non_snake_case)]
pub fn TupleStruct(id: Sym, values: Box<[ConValue]>) -> Self {
Self::TupleStruct(id, Box::new(values))
}
#[allow(non_snake_case)]
pub fn Struct(id: Sym, values: HashMap<Sym, ConValue>) -> Self {
Self::Struct(id, Box::new(values))
}
pub fn index(&self, index: &Self, _env: &Environment) -> IResult<ConValue> {
let &Self::Int(index) = index else {
Err(Error::TypeError())?
};
match self {
ConValue::Str(string) => string
.chars()
.nth(index as _)
.map(ConValue::Char)
.ok_or(Error::OobIndex(index as usize, string.chars().count())),
ConValue::String(string) => string
.chars()
.nth(index as _)
.map(ConValue::Char)
.ok_or(Error::OobIndex(index as usize, string.chars().count())),
ConValue::Array(arr) => arr
.get(index as usize)
.cloned()
.ok_or(Error::OobIndex(index as usize, arr.len())),
&ConValue::Slice(id, len) => {
let index = if index < 0 {
len.wrapping_add_signed(index)
} else {
index as usize
};
if index < len {
Ok(ConValue::Ref(id + index))
} else {
Err(Error::OobIndex(index, len))
}
}
_ => Err(Error::TypeError()),
}
}
cmp! {
lt: false, <;
lt_eq: true, <=;
eq: true, ==;
neq: false, !=;
gt_eq: true, >=;
gt: false, >;
}
assign! {
add_assign: +;
bitand_assign: &;
bitor_assign: |;
bitxor_assign: ^;
div_assign: /;
mul_assign: *;
rem_assign: %;
shl_assign: <<;
shr_assign: >>;
sub_assign: -;
}
}
impl Callable for ConValue {
fn name(&self) -> Sym {
match self {
ConValue::Function(func) => func.name(),
ConValue::Closure(func) => func.name(),
ConValue::Builtin(func) => func.name(),
_ => "".into(),
}
}
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
match self {
Self::Function(func) => func.call(env, args),
Self::TupleConstructor(func) => func.call(env, args),
Self::Closure(func) => func.call(env, args),
Self::Builtin(func) => func.call(env, args),
Self::Module(m) => {
if let Some(func) = m.get(&"call".into()) {
func.call(env, args)
} else {
Err(Error::NotCallable(self.clone()))
}
}
&Self::Ref(ptr) => {
// Move onto stack, and call
let func = env.get_id(ptr).ok_or(Error::StackOverflow(ptr))?.clone();
func.call(env, args)
}
_ => Err(Error::NotCallable(self.clone())),
}
}
}
/// Templates comparison functions for [ConValue]
macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
/// TODO: Remove when functions are implemented:
/// Desugar into function calls
pub fn $fn(&self, other: &Self) -> IResult<Self> {
match (self, other) {
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
(Self::Float(a), Self::Float(b)) => Ok(Self::Bool(a $op b)),
(Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
(Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
(Self::Str(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
(Self::Str(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
(Self::String(a), Self::Str(b)) => Ok(Self::Bool(&**a $op &**b)),
(Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
_ => Err(Error::TypeError())
}
}
)*}
macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
pub fn $fn(&mut self, other: Self) -> IResult<()> {
*self = (std::mem::take(self) $op other)?;
Ok(())
}
)*}
/// Implements [From] for an enum with 1-tuple variants
macro from ($($T:ty => $v:expr),*$(,)?) {
$(impl From<$T> for ConValue {
fn from(value: $T) -> Self { $v(value.into()) }
})*
}
impl From<&Sym> for ConValue {
fn from(value: &Sym) -> Self {
ConValue::Str(*value)
}
}
from! {
Integer => ConValue::Int,
f64 => ConValue::Float,
bool => ConValue::Bool,
char => ConValue::Char,
Sym => ConValue::Str,
&str => ConValue::Str,
Expr => ConValue::Quote,
String => ConValue::String,
Rc<str> => ConValue::Str,
Function => ConValue::Function,
Vec<ConValue> => ConValue::Tuple,
&'static Builtin => ConValue::Builtin,
}
impl From<()> for ConValue {
fn from(_: ()) -> Self {
Self::Empty
}
}
impl From<&[ConValue]> for ConValue {
fn from(value: &[ConValue]) -> Self {
match value {
[] => Self::Empty,
[value] => value.clone(),
_ => Self::Tuple(value.into()),
}
}
}
/// Implements binary [std::ops] traits for [ConValue]
///
/// TODO: Desugar operators into function calls
macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
$(impl $trait for ConValue {
type Output = IResult<Self>;
/// TODO: Desugar operators into function calls
fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
})*
}
ops! {
Add: add = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_add(b)),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a + b),
(ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
(ConValue::Str(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
(ConValue::String(a), ConValue::Str(b)) => (a.to_string() + &*b).into(),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &*b).into(),
(ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
(ConValue::Char(a), ConValue::Char(b)) => {
ConValue::String([a, b].into_iter().collect::<String>())
}
_ => Err(Error::TypeError())?
]
BitAnd: bitand = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError())?
]
BitOr: bitor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError())?
]
BitXor: bitxor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError())?
]
Div: div = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
eprintln!("Warning: Divide by zero in {a} / {b}"); a
})),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
_ => Err(Error::TypeError())?
]
Mul: mul = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b),
_ => Err(Error::TypeError())?
]
Rem: rem = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
println!("Warning: Divide by zero in {a} % {b}"); a
})),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
_ => Err(Error::TypeError())?
]
Shl: shl = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
_ => Err(Error::TypeError())?
]
Shr: shr = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
_ => Err(Error::TypeError())?
]
Sub: sub = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b),
_ => Err(Error::TypeError())?
]
}
impl std::fmt::Display for ConValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConValue::Empty => "Empty".fmt(f),
ConValue::Int(v) => v.fmt(f),
ConValue::Float(v) => v.fmt(f),
ConValue::Bool(v) => v.fmt(f),
ConValue::Char(v) => v.fmt(f),
ConValue::Str(v) => v.fmt(f),
ConValue::String(v) => v.fmt(f),
ConValue::Ref(v) => write!(f, "&<{}>", v),
ConValue::Slice(id, len) => write!(f, "&<{id}>[{len}..]"),
ConValue::Array(array) => {
'['.fmt(f)?;
for (idx, element) in array.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
']'.fmt(f)
}
ConValue::Tuple(tuple) => {
'('.fmt(f)?;
for (idx, element) in tuple.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
')'.fmt(f)
}
ConValue::TupleStruct(id, tuple) => {
write!(f, "{id}")?;
'('.fmt(f)?;
for (idx, element) in tuple.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
')'.fmt(f)
}
ConValue::Struct(id, map) => {
use std::fmt::Write;
write!(f, "{id} ")?;
let mut f = f.delimit_with("{", "\n}");
for (k, v) in map.iter() {
write!(f, "\n{k}: {v},")?;
}
Ok(())
}
ConValue::Module(module) => {
use std::fmt::Write;
let mut f = f.delimit_with("{", "\n}");
for (k, v) in module.iter() {
write!(f, "\n{k}: {v},")?;
}
Ok(())
}
ConValue::Quote(q) => {
write!(f, "`{q}`")
}
ConValue::Function(func) => {
write!(f, "{}", func.decl())
}
ConValue::TupleConstructor(Constructor { name: index, arity }) => {
write!(f, "{index}(..{arity})")
}
ConValue::Closure(func) => {
write!(f, "{}", func.as_ref())
}
ConValue::Builtin(func) => {
write!(f, "{}", func)
}
}
}
}
pub macro cvstruct (
$Name:ident {
$($member:ident : $expr:expr),*
}
) {{
let mut members = HashMap::new();
$(members.insert(stringify!($member).into(), ($expr).into());)*
ConValue::Struct(Box::new((stringify!($Name).into(), members)))
}}

View File

@@ -0,0 +1,310 @@
//! Lexical and non-lexical scoping for variables
use crate::{builtin::Builtin, constructor::Constructor, modules::ModuleTree};
use super::{
Callable, Interpret,
builtin::{Builtins, Math},
convalue::ConValue,
error::{Error, IResult},
function::Function,
};
use cl_ast::{Function as FnDecl, Sym};
use std::{
collections::HashMap,
fmt::Display,
ops::{Deref, DerefMut},
rc::Rc,
};
pub type StackFrame = HashMap<Sym, ConValue>;
pub type StackBinds = HashMap<Sym, usize>;
#[derive(Clone, Debug, Default)]
pub(crate) struct EnvFrame {
pub name: Option<&'static str>,
/// The length of the array when this stack frame was constructed
pub base: usize,
/// The bindings of name to stack position
pub binds: StackBinds,
}
/// Implements a nested lexical scope
#[derive(Clone, Debug)]
pub struct Environment {
values: Vec<ConValue>,
frames: Vec<EnvFrame>,
modules: ModuleTree,
}
impl Display for Environment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for EnvFrame { name, base: _, binds } in self.frames.iter().rev() {
writeln!(
f,
"--- {}[{}] ---",
if let Some(name) = name { name } else { "" },
binds.len(),
)?;
let mut binds: Vec<_> = binds.iter().collect();
binds.sort_by(|(_, a), (_, b)| a.cmp(b));
for (name, idx) in binds {
write!(f, "{idx:4} {name}: ")?;
match self.values.get(*idx) {
Some(value) => writeln!(f, "\t{value}"),
None => writeln!(f, "ERROR: {name}'s address blows the stack!"),
}?
}
}
Ok(())
}
}
impl Default for Environment {
fn default() -> Self {
let mut this = Self::no_builtins();
this.add_builtins(Builtins).add_builtins(Math);
this
}
}
impl Environment {
pub fn new() -> Self {
Self::default()
}
/// Creates an [Environment] with no [builtins](super::builtin)
pub fn no_builtins() -> Self {
Self {
values: Vec::new(),
frames: vec![EnvFrame::default()],
modules: ModuleTree::default(),
}
}
/// Reflexively evaluates a node
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
node.interpret(self)
}
/// Calls a function inside the Environment's scope,
/// and returns the result
pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
let function = self.get(name)?;
function.call(self, args)
}
pub fn modules_mut(&mut self) -> &mut ModuleTree {
&mut self.modules
}
pub fn modules(&self) -> &ModuleTree {
&self.modules
}
/// Binds a value to the given name in the current scope.
pub fn bind(&mut self, name: impl Into<Sym>, value: impl Into<ConValue>) {
self.insert(name.into(), value.into());
}
pub fn bind_raw(&mut self, name: Sym, id: usize) -> Option<()> {
let EnvFrame { name: _, base: _, binds } = self.frames.last_mut()?;
binds.insert(name, id);
Some(())
}
/// Gets all registered globals, bound or unbound.
pub(crate) fn globals(&self) -> &EnvFrame {
self.frames.first().unwrap()
}
/// Adds builtins
///
/// # Panics
///
/// Will panic if stack contains more than the globals frame!
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) -> &mut Self {
if self.frames.len() != 1 {
panic!("Cannot add builtins to full stack: {self}")
}
for builtin in builtins {
self.insert(builtin.name(), builtin.into());
}
self
}
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
self.frames.push(EnvFrame {
name: Some(name),
base: self.values.len(),
binds: HashMap::new(),
});
for (k, v) in frame {
self.insert(k, v);
}
}
pub fn pop_frame(&mut self) -> Option<(StackFrame, &'static str)> {
let mut out = HashMap::new();
let EnvFrame { name, base, binds } = self.frames.pop()?;
for (k, v) in binds {
out.insert(k, self.values.get_mut(v).map(std::mem::take)?);
}
self.values.truncate(base);
Some((out, name.unwrap_or("")))
}
/// Enters a nested scope, returning a [`Frame`] stack-guard.
///
/// [`Frame`] implements Deref/DerefMut for [`Environment`].
pub fn frame(&mut self, name: &'static str) -> Frame<'_> {
Frame::new(self, name)
}
/// Enters a nested scope, assigning the contents of `frame`,
/// and returning a [`Frame`] stack-guard.
///
/// [`Frame`] implements Deref/DerefMut for [`Environment`].
pub fn with_frame<'e>(&'e mut self, name: &'static str, frame: StackFrame) -> Frame<'e> {
let mut scope = self.frame(name);
for (k, v) in frame {
scope.insert(k, v);
}
scope
}
/// Resolves a variable mutably.
///
/// Returns a mutable reference to the variable's record, if it exists.
pub fn get_mut(&mut self, name: Sym) -> IResult<&mut ConValue> {
let at = self.id_of(name)?;
self.get_id_mut(at).ok_or(Error::NotDefined(name))
}
/// Resolves a variable immutably.
///
/// Returns a reference to the variable's contents, if it is defined and initialized.
pub fn get(&self, name: Sym) -> IResult<ConValue> {
let id = self.id_of(name)?;
let res = self.values.get(id);
Ok(res.ok_or(Error::NotDefined(name))?.clone())
}
/// Resolves the index associated with a [Sym]
pub fn id_of(&self, name: Sym) -> IResult<usize> {
for EnvFrame { binds, .. } in self.frames.iter().rev() {
if let Some(id) = binds.get(&name).copied() {
return Ok(id);
}
}
Err(Error::NotDefined(name))
}
pub fn get_id(&self, id: usize) -> Option<&ConValue> {
self.values.get(id)
}
pub fn get_id_mut(&mut self, id: usize) -> Option<&mut ConValue> {
self.values.get_mut(id)
}
pub fn get_slice(&self, start: usize, len: usize) -> Option<&[ConValue]> {
self.values.get(start..start + len)
}
pub fn get_slice_mut(&mut self, start: usize, len: usize) -> Option<&mut [ConValue]> {
self.values.get_mut(start..start + len)
}
/// Inserts a new [ConValue] into this [Environment]
pub fn insert(&mut self, k: Sym, v: ConValue) {
if self.bind_raw(k, self.values.len()).is_some() {
self.values.push(v);
}
}
/// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) {
let FnDecl { name, .. } = decl;
let (name, function) = (*name, Rc::new(Function::new(decl)));
self.insert(name, ConValue::Function(function.clone()));
// Tell the function to lift its upvars now, after it's been declared
function.lift_upvars(self);
}
pub fn insert_tup_constructor(&mut self, name: Sym, arity: usize) {
let cs = Constructor { arity: arity as _, name };
self.insert(name, ConValue::TupleConstructor(cs));
}
/// Gets the current stack top position
pub fn pos(&self) -> usize {
self.values.len()
}
/// Allocates a local variable
pub fn stack_alloc(&mut self, value: ConValue) -> IResult<usize> {
let adr = self.values.len();
self.values.push(value);
Ok(adr)
}
/// Allocates some space on the stack
pub fn alloca(&mut self, value: ConValue, len: usize) -> ConValue {
let idx = self.values.len();
self.values.extend(std::iter::repeat_n(value, len));
ConValue::Slice(idx, len)
}
}
/// Represents a stack frame
#[derive(Debug)]
pub struct Frame<'scope> {
scope: &'scope mut Environment,
}
impl<'scope> Frame<'scope> {
fn new(scope: &'scope mut Environment, name: &'static str) -> Self {
scope.frames.push(EnvFrame {
name: Some(name),
base: scope.values.len(),
binds: HashMap::new(),
});
Self { scope }
}
pub fn pop_values(mut self) -> Option<StackFrame> {
let mut out = HashMap::new();
let binds = std::mem::take(&mut self.frames.last_mut()?.binds);
for (k, v) in binds {
out.insert(k, self.values.get_mut(v).map(std::mem::take)?);
}
Some(out)
}
pub fn into_binds(mut self) -> Option<StackBinds> {
let EnvFrame { name: _, base: _, binds } = self.frames.pop()?;
std::mem::forget(self);
Some(binds)
}
}
impl Deref for Frame<'_> {
type Target = Environment;
fn deref(&self) -> &Self::Target {
self.scope
}
}
impl DerefMut for Frame<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.scope
}
}
impl Drop for Frame<'_> {
fn drop(&mut self) {
if let Some(frame) = self.frames.pop() {
self.values.truncate(frame.base);
}
}
}

View File

@@ -0,0 +1,218 @@
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
use cl_ast::{Pattern, Sym};
use cl_structures::span::Span;
use super::convalue::ConValue;
pub type IResult<T> = Result<T, Error>;
#[derive(Clone, Debug)]
pub struct Error {
pub kind: ErrorKind,
pub(super) span: Option<Span>,
}
impl Error {
#![allow(non_snake_case)]
/// Adds a [struct Span] to this [Error], if there isn't already a more specific one.
pub fn with_span(self, span: Span) -> Self {
Self { span: self.span.or(Some(span)), ..self }
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
/// Propagate a Return value
pub fn Return(value: ConValue) -> Self {
Self { kind: ErrorKind::Return(value), span: None }
}
/// Propagate a Break value
pub fn Break(value: ConValue) -> Self {
Self { kind: ErrorKind::Break(value), span: None }
}
/// Break propagated across function bounds
pub fn BadBreak(value: ConValue) -> Self {
Self { kind: ErrorKind::BadBreak(value), span: None }
}
/// Continue to the next iteration of a loop
pub fn Continue() -> Self {
Self { kind: ErrorKind::Continue, span: None }
}
/// Underflowed the stack
pub fn StackUnderflow() -> Self {
Self { kind: ErrorKind::StackUnderflow, span: None }
}
/// Overflowed the stack
pub fn StackOverflow(place: usize) -> Self {
Self { kind: ErrorKind::StackOverflow(place), span: None }
}
/// Exited the last scope
pub fn ScopeExit() -> Self {
Self { kind: ErrorKind::ScopeExit, span: None }
}
/// Type incompatibility
// TODO: store the type information in this error
pub fn TypeError() -> Self {
Self { kind: ErrorKind::TypeError, span: None }
}
/// In clause of For loop didn't yield a Range
pub fn NotIterable() -> Self {
Self { kind: ErrorKind::NotIterable, span: None }
}
/// A value could not be indexed
pub fn NotIndexable() -> Self {
Self { kind: ErrorKind::NotIndexable, span: None }
}
/// An array index went out of bounds
pub fn OobIndex(index: usize, length: usize) -> Self {
Self { kind: ErrorKind::OobIndex(index, length), span: None }
}
/// An expression is not assignable
pub fn NotAssignable() -> Self {
Self { kind: ErrorKind::NotAssignable, span: None }
}
/// A name was not defined in scope before being used
pub fn NotDefined(name: Sym) -> Self {
Self { kind: ErrorKind::NotDefined(name), span: None }
}
/// A name was defined but not initialized
pub fn NotInitialized(name: Sym) -> Self {
Self { kind: ErrorKind::NotInitialized(name), span: None }
}
/// A value was called, but is not callable
pub fn NotCallable(value: ConValue) -> Self {
Self { kind: ErrorKind::NotCallable(value), span: None }
}
/// A function was called with the wrong number of arguments
pub fn ArgNumber(want: usize, got: usize) -> Self {
Self { kind: ErrorKind::ArgNumber { want, got }, span: None }
}
/// A pattern failed to match
pub fn PatFailed(pat: Box<Pattern>) -> Self {
Self { kind: ErrorKind::PatFailed(pat), span: None }
}
/// Fell through a non-exhaustive match
pub fn MatchNonexhaustive() -> Self {
Self { kind: ErrorKind::MatchNonexhaustive, span: None }
}
/// Explicit panic
pub fn Panic(msg: String) -> Self {
Self { kind: ErrorKind::Panic(msg, 0), span: None }
}
/// Error produced by a Builtin
pub fn BuiltinError(msg: String) -> Self {
Self { kind: ErrorKind::BuiltinError(msg), span: None }
}
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { kind, span } = self;
if let Some(Span { head, tail }) = span {
write!(f, "{head}..{tail}: ")?;
}
write!(f, "{kind}")
}
}
/// Represents any error thrown by the [Environment](super::Environment)
#[derive(Clone, Debug)]
pub enum ErrorKind {
/// Propagate a Return value
Return(ConValue),
/// Propagate a Break value
Break(ConValue),
/// Break propagated across function bounds
BadBreak(ConValue),
/// Continue to the next iteration of a loop
Continue,
/// Underflowed the stack
StackUnderflow,
/// Overflowed the stack
StackOverflow(usize),
/// Exited the last scope
ScopeExit,
/// Type incompatibility
// TODO: store the type information in this error
TypeError,
/// In clause of For loop didn't yield a Range
NotIterable,
/// A value could not be indexed
NotIndexable,
/// An array index went out of bounds
OobIndex(usize, usize),
/// An expression is not assignable
NotAssignable,
/// A name was not defined in scope before being used
NotDefined(Sym),
/// A name was defined but not initialized
NotInitialized(Sym),
/// A value was called, but is not callable
NotCallable(ConValue),
/// A function was called with the wrong number of arguments
ArgNumber { want: usize, got: usize },
/// A pattern failed to match
PatFailed(Box<Pattern>),
/// Fell through a non-exhaustive match
MatchNonexhaustive,
/// Explicit panic
Panic(String, usize),
/// Error produced by a Builtin
BuiltinError(String),
}
impl std::error::Error for ErrorKind {}
impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::Return(value) => write!(f, "return {value}"),
ErrorKind::Break(value) => write!(f, "break {value}"),
ErrorKind::BadBreak(value) => write!(f, "rogue break: {value}"),
ErrorKind::Continue => "continue".fmt(f),
ErrorKind::StackUnderflow => "Stack underflow".fmt(f),
ErrorKind::StackOverflow(id) => {
write!(f, "Attempt to access <{id}> resulted in stack overflow.")
}
ErrorKind::ScopeExit => "Exited the last scope. This is a logic bug.".fmt(f),
ErrorKind::TypeError => "Incompatible types".fmt(f),
ErrorKind::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
ErrorKind::NotIndexable => {
write!(f, "expression cannot be indexed")
}
ErrorKind::OobIndex(idx, len) => {
write!(f, "Index out of bounds: index was {idx}. but len is {len}")
}
ErrorKind::NotAssignable => {
write!(f, "expression is not assignable")
}
ErrorKind::NotDefined(value) => {
write!(f, "{value} not bound. Did you mean `let {value};`?")
}
ErrorKind::NotInitialized(value) => {
write!(f, "{value} bound, but not initialized")
}
ErrorKind::NotCallable(value) => {
write!(f, "{value} is not callable.")
}
ErrorKind::ArgNumber { want, got } => {
write!(
f,
"Expected {want} argument{}, got {got}",
if *want == 1 { "" } else { "s" }
)
}
ErrorKind::PatFailed(pattern) => {
write!(f, "Failed to match pattern {pattern}")
}
ErrorKind::MatchNonexhaustive => {
write!(f, "Fell through a non-exhaustive match expression!")
}
ErrorKind::Panic(s, _depth) => write!(f, "Explicit panic: {s}"),
ErrorKind::BuiltinError(s) => write!(f, "{s}"),
}
}
}

View File

@@ -0,0 +1,83 @@
//! Represents a block of code which lives inside the Interpreter
use collect_upvars::collect_upvars;
use crate::error::ErrorKind;
use super::{Callable, ConValue, Environment, Error, IResult, Interpret, pattern};
use cl_ast::{Function as FnDecl, Sym};
use std::{
cell::{Ref, RefCell},
collections::HashMap,
rc::Rc,
};
pub mod collect_upvars;
type Upvars = HashMap<Sym, ConValue>;
/// Represents a block of code which persists inside the Interpreter
#[derive(Clone, Debug)]
pub struct Function {
/// Stores the contents of the function declaration
decl: Rc<FnDecl>,
/// Stores data from the enclosing scopes
upvars: RefCell<Upvars>,
}
impl Function {
pub fn new(decl: &FnDecl) -> Self {
// let upvars = collect_upvars(decl, env);
Self { decl: decl.clone().into(), upvars: Default::default() }
}
pub fn decl(&self) -> &FnDecl {
&self.decl
}
pub fn upvars(&self) -> Ref<'_, Upvars> {
self.upvars.borrow()
}
pub fn lift_upvars(&self, env: &Environment) {
let upvars = collect_upvars(&self.decl, env);
if let Ok(mut self_upvars) = self.upvars.try_borrow_mut() {
*self_upvars = upvars;
}
}
}
impl Callable for Function {
fn name(&self) -> Sym {
let FnDecl { name, .. } = *self.decl;
name
}
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
let FnDecl { name, gens: _, bind, body, sign: _ } = &*self.decl;
// Check arg mapping
let Some(body) = body else {
return Err(Error::NotDefined(*name));
};
let upvars = self.upvars.take();
let mut env = env.with_frame("upvars", upvars);
// TODO: completely refactor data storage
let mut frame = env.frame("fn args");
for (name, value) in pattern::substitution(&frame, bind, ConValue::Tuple(args.into()))? {
frame.insert(name, value);
}
let res = body.interpret(&mut frame);
drop(frame);
if let Some(upvars) = env.pop_values() {
self.upvars.replace(upvars);
}
match res {
Err(Error { kind: ErrorKind::Return(value), .. }) => Ok(value),
Err(Error { kind: ErrorKind::Break(value), .. }) => Err(Error::BadBreak(value)),
Err(Error { kind: ErrorKind::Panic(msg, depth), span: Some(span) }) => {
println!("{depth:>4}: {name}{bind} at {}", span.head);
Err(Error { kind: ErrorKind::Panic(msg, depth + 1), span: None })
}
other => other,
}
}
}

View File

@@ -0,0 +1,112 @@
//! Collects the "Upvars" of a function at the point of its creation, allowing variable capture
use crate::env::Environment;
use cl_ast::{
Function, Let, Path, PathPart, Pattern, Sym,
ast_visitor::{visit::*, walk::Walk},
};
use std::collections::{HashMap, HashSet};
pub fn collect_upvars(f: &Function, env: &Environment) -> super::Upvars {
CollectUpvars::new(env).visit(f).finish_copied()
}
#[derive(Clone, Debug)]
pub struct CollectUpvars<'env> {
env: &'env Environment,
upvars: HashMap<Sym, usize>,
blacklist: HashSet<Sym>,
}
impl<'env> CollectUpvars<'env> {
pub fn new(env: &'env Environment) -> Self {
Self { upvars: HashMap::new(), blacklist: HashSet::new(), env }
}
pub fn finish(&mut self) -> HashMap<Sym, usize> {
std::mem::take(&mut self.upvars)
}
pub fn finish_copied(&mut self) -> super::Upvars {
let Self { env, upvars, blacklist: _ } = self;
std::mem::take(upvars)
.into_iter()
.filter_map(|(k, v)| env.get_id(v).cloned().map(|v| (k, v)))
.collect()
}
pub fn add_upvar(&mut self, name: &Sym) {
let Self { env, upvars, blacklist } = self;
if blacklist.contains(name) || upvars.contains_key(name) {
return;
}
if let Ok(place) = env.id_of(*name) {
upvars.insert(*name, place);
}
}
pub fn bind_name(&mut self, name: &Sym) {
self.blacklist.insert(*name);
}
pub fn scope(&mut self, f: impl Fn(&mut CollectUpvars<'env>)) {
let blacklist = self.blacklist.clone();
// visit the scope
f(self);
// restore the blacklist
self.blacklist = blacklist;
}
}
impl<'a> Visit<'a> for CollectUpvars<'_> {
fn visit_block(&mut self, b: &'a cl_ast::Block) {
self.scope(|cu| b.children(cu));
}
fn visit_let(&mut self, l: &'a cl_ast::Let) {
let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable);
ty.visit_in(self);
// visit the initializer, which may use the bound name
init.visit_in(self);
// a bound name can never be an upvar
self.visit_pattern(name);
}
fn visit_path(&mut self, p: &'a cl_ast::Path) {
// TODO: path resolution in environments
let Path { absolute: false, parts } = p else {
return;
};
let [PathPart::Ident(name)] = parts.as_slice() else {
return;
};
self.add_upvar(name);
}
fn visit_fielder(&mut self, f: &'a cl_ast::Fielder) {
let cl_ast::Fielder { name, init } = f;
if let Some(init) = init {
self.visit_expr(init);
} else {
self.add_upvar(name); // fielder without init grabs from env
}
}
fn visit_pattern(&mut self, value: &'a cl_ast::Pattern) {
match value {
Pattern::Name(name) => {
self.bind_name(name);
}
Pattern::RangeExc(_, _) | Pattern::RangeInc(_, _) => {}
_ => value.children(self),
}
}
fn visit_match_arm(&mut self, value: &'a cl_ast::MatchArm) {
// MatchArms bind variables with a very small local scope
self.scope(|cu| value.children(cu));
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,347 @@
//! Unification algorithm for cl-ast [Pattern]s and [ConValue]s
//!
//! [`variables()`] returns a flat list of symbols that are bound by a given pattern
//! [`substitution()`] unifies a ConValue with a pattern, and produces a list of bound names
use crate::{
convalue::ConValue,
env::Environment,
error::{Error, IResult},
};
use cl_ast::{Literal, Pattern, Sym};
use std::collections::{HashMap, VecDeque};
/// Gets the path variables in the given Pattern
pub fn variables(pat: &Pattern) -> Vec<&Sym> {
fn patvars<'p>(set: &mut Vec<&'p Sym>, pat: &'p Pattern) {
match pat {
Pattern::Name(name) if name.to_ref() == "_" => {}
Pattern::Name(name) => set.push(name),
Pattern::Path(_) => {}
Pattern::Literal(_) => {}
Pattern::Rest(Some(pattern)) => patvars(set, pattern),
Pattern::Rest(None) => {}
Pattern::Ref(_, pattern) => patvars(set, pattern),
Pattern::RangeExc(_, _) => {}
Pattern::RangeInc(_, _) => {}
Pattern::Tuple(patterns) | Pattern::Array(patterns) => {
patterns.iter().for_each(|pat| patvars(set, pat))
}
Pattern::Struct(_path, items) => {
items.iter().for_each(|(name, pat)| match pat {
Some(pat) => patvars(set, pat),
None => set.push(name),
});
}
Pattern::TupleStruct(_path, items) => {
items.iter().for_each(|pat| patvars(set, pat));
}
}
}
let mut set = Vec::new();
patvars(&mut set, pat);
set
}
fn rest_binding<'pat>(
env: &Environment,
sub: &mut HashMap<Sym, ConValue>,
mut patterns: &'pat [Pattern],
mut values: VecDeque<ConValue>,
) -> IResult<Option<(&'pat Pattern, VecDeque<ConValue>)>> {
// Bind the head of the list
while let [pattern, tail @ ..] = patterns {
if matches!(pattern, Pattern::Rest(_)) {
break;
}
let value = values
.pop_front()
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
append_sub(env, sub, pattern, value)?;
patterns = tail;
}
// Bind the tail of the list
while let [head @ .., pattern] = patterns {
if matches!(pattern, Pattern::Rest(_)) {
break;
}
let value = values
.pop_back()
.ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?;
append_sub(env, sub, pattern, value)?;
patterns = head;
}
// Bind the ..rest of the list
match patterns {
[] | [Pattern::Rest(None)] => Ok(None),
[Pattern::Rest(Some(pattern))] => Ok(Some((pattern.as_ref(), values))),
_ => Err(Error::PatFailed(Box::new(Pattern::Array(patterns.into())))),
}
}
fn rest_binding_ref<'pat>(
env: &Environment,
sub: &mut HashMap<Sym, ConValue>,
mut patterns: &'pat [Pattern],
mut head: usize,
mut tail: usize,
) -> IResult<Option<(&'pat Pattern, usize, usize)>> {
// Bind the head of the list
while let [pattern, pat_tail @ ..] = patterns {
if matches!(pattern, Pattern::Rest(_)) {
break;
}
if head >= tail {
return Err(Error::PatFailed(Box::new(pattern.clone())));
}
append_sub(env, sub, pattern, ConValue::Ref(head))?;
head += 1;
patterns = pat_tail;
}
// Bind the tail of the list
while let [pat_head @ .., pattern] = patterns {
if matches!(pattern, Pattern::Rest(_)) {
break;
}
if head >= tail {
return Err(Error::PatFailed(Box::new(pattern.clone())));
};
append_sub(env, sub, pattern, ConValue::Ref(tail))?;
tail -= 1;
patterns = pat_head;
}
// Bind the ..rest of the list
match (patterns, tail - head) {
([], 0) | ([Pattern::Rest(None)], _) => Ok(None),
([Pattern::Rest(Some(pattern))], _) => Ok(Some((pattern.as_ref(), head, tail))),
_ => Err(Error::PatFailed(Box::new(Pattern::Array(patterns.into())))),
}
}
/// Appends a substitution to the provided table
pub fn append_sub(
env: &Environment,
sub: &mut HashMap<Sym, ConValue>,
pat: &Pattern,
value: ConValue,
) -> IResult<()> {
match (pat, value) {
(Pattern::Literal(Literal::Bool(a)), ConValue::Bool(b)) => {
(*a == b).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(Literal::Char(a)), ConValue::Char(b)) => {
(*a == b).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => (f64::from_bits(*a) == b)
.then_some(())
.ok_or(Error::NotAssignable()),
(Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => {
(b == *a as _).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(Literal::String(a)), ConValue::Str(b)) => {
(*a == *b).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(Literal::String(a)), ConValue::String(b)) => {
(*a == *b).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(_), _) => Err(Error::NotAssignable()),
(Pattern::Rest(Some(pat)), value) => match (pat.as_ref(), value) {
(Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => {
(b < *a as _).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(Literal::Char(a)), ConValue::Char(b)) => {
(b < *a as _).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(Literal::Bool(a)), ConValue::Bool(b)) => {
(!b & *a).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => {
(b < *a as _).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(Literal::String(a)), ConValue::Str(b)) => {
(&*b < a).then_some(()).ok_or(Error::NotAssignable())
}
(Pattern::Literal(Literal::String(a)), ConValue::String(b)) => {
(&*b < a).then_some(()).ok_or(Error::NotAssignable())
}
_ => Err(Error::NotAssignable()),
},
(Pattern::Name(name), _) if "_".eq(&**name) => Ok(()),
(Pattern::Name(name), value) => {
sub.insert(*name, value);
Ok(())
}
(Pattern::Ref(_, pat), ConValue::Ref(r)) => match env.get_id(r) {
Some(value) => append_sub(env, sub, pat, value.clone()),
None => Err(Error::PatFailed(pat.clone())),
},
(Pattern::Ref(_, pat), ConValue::Slice(head, len)) => {
let mut values = Vec::with_capacity(len);
for idx in head..(head + len) {
values.push(env.get_id(idx).cloned().ok_or(Error::StackOverflow(idx))?);
}
append_sub(env, sub, pat, ConValue::Array(values.into_boxed_slice()))
}
(Pattern::RangeExc(head, tail), value) => match (head.as_ref(), tail.as_ref(), value) {
(
Pattern::Literal(Literal::Int(a)),
Pattern::Literal(Literal::Int(c)),
ConValue::Int(b),
) => (*a as isize <= b as _ && b < *c as isize)
.then_some(())
.ok_or(Error::NotAssignable()),
(
Pattern::Literal(Literal::Char(a)),
Pattern::Literal(Literal::Char(c)),
ConValue::Char(b),
) => (*a <= b && b < *c)
.then_some(())
.ok_or(Error::NotAssignable()),
(
Pattern::Literal(Literal::Float(a)),
Pattern::Literal(Literal::Float(c)),
ConValue::Float(b),
) => (f64::from_bits(*a) <= b && b < f64::from_bits(*c))
.then_some(())
.ok_or(Error::NotAssignable()),
(
Pattern::Literal(Literal::String(a)),
Pattern::Literal(Literal::String(c)),
ConValue::Str(b),
) => (a.as_str() <= b.to_ref() && b.to_ref() < c.as_str())
.then_some(())
.ok_or(Error::NotAssignable()),
(
Pattern::Literal(Literal::String(a)),
Pattern::Literal(Literal::String(c)),
ConValue::String(b),
) => (a.as_str() <= b.as_str() && b.as_str() < c.as_str())
.then_some(())
.ok_or(Error::NotAssignable()),
_ => Err(Error::NotAssignable()),
},
(Pattern::RangeInc(head, tail), value) => match (head.as_ref(), tail.as_ref(), value) {
(
Pattern::Literal(Literal::Int(a)),
Pattern::Literal(Literal::Int(c)),
ConValue::Int(b),
) => (*a as isize <= b && b <= *c as isize)
.then_some(())
.ok_or(Error::NotAssignable()),
(
Pattern::Literal(Literal::Char(a)),
Pattern::Literal(Literal::Char(c)),
ConValue::Char(b),
) => (*a <= b && b <= *c)
.then_some(())
.ok_or(Error::NotAssignable()),
(
Pattern::Literal(Literal::Float(a)),
Pattern::Literal(Literal::Float(c)),
ConValue::Float(b),
) => (f64::from_bits(*a) <= b && b <= f64::from_bits(*c))
.then_some(())
.ok_or(Error::NotAssignable()),
(
Pattern::Literal(Literal::String(a)),
Pattern::Literal(Literal::String(c)),
ConValue::Str(b),
) => (a.as_str() <= b.to_ref() && b.to_ref() <= c.as_str())
.then_some(())
.ok_or(Error::NotAssignable()),
(
Pattern::Literal(Literal::String(a)),
Pattern::Literal(Literal::String(c)),
ConValue::String(b),
) => (a.as_str() <= b.as_str() && b.as_str() <= c.as_str())
.then_some(())
.ok_or(Error::NotAssignable()),
_ => Err(Error::NotAssignable()),
},
(Pattern::Array(patterns), ConValue::Array(values)) => {
match rest_binding(env, sub, patterns, values.into_vec().into())? {
Some((pattern, values)) => {
append_sub(env, sub, pattern, ConValue::Array(Vec::from(values).into()))
}
_ => Ok(()),
}
}
(Pattern::Array(patterns), ConValue::Slice(head, len)) => {
match rest_binding_ref(env, sub, patterns, head, head + len)? {
Some((pat, head, tail)) => {
append_sub(env, sub, pat, ConValue::Slice(head, tail - head))
}
None => Ok(()),
}
}
(Pattern::Tuple(patterns), ConValue::Empty) if patterns.is_empty() => Ok(()),
(Pattern::Tuple(patterns), ConValue::Tuple(values)) => {
match rest_binding(env, sub, patterns, values.into_vec().into())? {
Some((pattern, values)) => {
append_sub(env, sub, pattern, ConValue::Tuple(Vec::from(values).into()))
}
_ => Ok(()),
}
}
(Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(id, values)) => {
let tid = path
.as_sym()
.ok_or_else(|| Error::PatFailed(pat.clone().into()))?;
if id != tid {
return Err(Error::PatFailed(pat.clone().into()));
}
match rest_binding(env, sub, patterns, values.into_vec().into())? {
Some((pattern, values)) => {
append_sub(env, sub, pattern, ConValue::Tuple(Vec::from(values).into()))
}
_ => Ok(()),
}
}
(Pattern::Struct(path, patterns), ConValue::Struct(id, mut values)) => {
let tid = path
.as_sym()
.ok_or_else(|| Error::PatFailed(pat.clone().into()))?;
if id != tid {
return Err(Error::PatFailed(pat.clone().into()));
}
for (name, pat) in patterns {
let value = values.remove(name).ok_or(Error::TypeError())?;
match pat {
Some(pat) => append_sub(env, sub, pat, value)?,
None => {
sub.insert(*name, value);
}
}
}
Ok(())
}
_ => {
// eprintln!("Could not match pattern `{pat}` with value `{value}`!");
Err(Error::NotAssignable())
}
}
}
/// Constructs a substitution from a pattern and a value
pub fn substitution(
env: &Environment,
pat: &Pattern,
value: ConValue,
) -> IResult<HashMap<Sym, ConValue>> {
let mut sub = HashMap::new();
append_sub(env, &mut sub, pat, value)?;
Ok(sub)
}

View File

@@ -1,5 +1,5 @@
#![allow(unused_imports)]
use crate::{env::Environment, convalue::ConValue, Interpret};
use crate::{Interpret, convalue::ConValue, env::Environment};
use cl_ast::*;
use cl_lexer::Lexer;
use cl_parser::Parser;
@@ -48,6 +48,7 @@ mod macros {
//! ```
#![allow(unused_macros)]
use crate::IResult;
use cl_parser::parser::Parse;
use super::*;
@@ -63,14 +64,14 @@ mod macros {
///
/// Returns a `Result<`[`File`]`, ParseError>`
pub macro file($($t:tt)*) {
Parser::new(Lexer::new(stringify!( $($t)* ))).file()
File::parse(&mut Parser::new(Lexer::new(stringify!( $($t)* ))))
}
/// Stringifies, lexes, and parses everything you give to it
///
/// Returns a `Result<`[`Block`]`, ParseError>`
pub macro block($($t:tt)*) {
Parser::new(Lexer::new(stringify!({ $($t)* }))).block()
Block::parse(&mut Parser::new("test", Lexer::new(stringify!({ $($t)* }))))
}
/// Evaluates a block of code in the given environment
@@ -177,6 +178,45 @@ mod let_declarations {
env_eq!(env.x, 10);
env_eq!(env.y, 10);
}
#[test]
fn let_destructuring_tuple() {
let mut env = Environment::new();
assert_eval!(env,
let (x, y) = (10, 20);
);
env_eq!(env.x, 10);
env_eq!(env.y, 20);
}
#[test]
fn let_destructuring_array() {
let mut env = Environment::new();
assert_eval!(env,
let [x, y] = [10, 20];
);
env_eq!(env.x, 10);
env_eq!(env.y, 20);
}
#[test]
fn let_destructuring_nested() {
let mut env = Environment::new();
assert_eval!(env,
let (x, [one, two, three], (a, b, c))
= ('x', [1, 2, 3], ('a', 'b', 'c'));
);
env_eq!(env.x, 'x');
env_eq!(env.one, 1);
env_eq!(env.two, 2);
env_eq!(env.three, 3);
env_eq!(env.a, 'a');
env_eq!(env.b, 'b');
env_eq!(env.c, 'c');
}
}
mod fn_declarations {
@@ -187,7 +227,7 @@ mod fn_declarations {
assert_eval!(env, fn empty_fn() {});
// TODO: true equality for functions
assert_eq!(
"fn empty_fn () {\n \n}",
"fn empty_fn () {}",
format!(
"{}",
env.get("empty_fn".into())
@@ -436,16 +476,17 @@ mod operators {
env_eq!(env.y, 10);
env_eq!(env.z, 10);
}
#[test]
#[should_panic]
fn assignment_accounts_for_type() {
let mut env = Default::default();
assert_eval!(env,
let x = "a string";
let y = 0xdeadbeef;
y = x; // should crash: type error
);
}
// Test is disabled, since new assignment system intentionally does not care.
// #[test]
// #[should_panic]
// fn assignment_accounts_for_type() {
// let mut env = Default::default();
// assert_eval!(env,
// let x = "a string";
// let y = 0xdeadbeef;
// y = x; // should crash: type error
// );
// }
#[test]
fn precedence() {
let mut env = Default::default();
@@ -468,6 +509,75 @@ mod operators {
}
}
mod control_flow {
use super::*;
#[test]
fn if_evaluates_pass_block_on_true() {
let mut env = Default::default();
assert_eval!(env,
let evaluated = if true { "pass" } else { "fail" }
);
env_eq!(env.evaluated, "pass");
}
#[test]
fn if_evaluates_fail_block_on_false() {
let mut env = Default::default();
assert_eval!(env,
let evaluated = if false { "pass" } else { "fail" }
);
env_eq!(env.evaluated, "fail");
}
#[test]
fn while_evaluates_fail_block_on_false() {
let mut env = Default::default();
assert_eval!(env,
let cond = true;
let evaluated = while cond { cond = false } else { true }
);
env_eq!(env.evaluated, true);
}
#[test]
fn while_does_not_evaluate_fail_block_on_break() {
let mut env = Default::default();
assert_eval!(env,
let evaluated = while true { break true } else { false }
);
env_eq!(env.evaluated, true);
}
#[test]
fn match_evaluates_in_order() {
let mut env = Default::default();
assert_eval!(env,
let x = '\u{1f988}';
let passed = match x {
'\u{1f988}' => true,
_ => false,
};
);
env_eq!(env.passed, true);
}
#[test]
fn match_sinkoles_underscore_patterns() {
let mut env = Default::default();
assert_eval!(env,
let x = '\u{1f988}';
let passed = match x {
_ => true,
'\u{1f988}' => false,
};
);
env_eq!(env.passed, true);
}
//TODO: test other control flow constructs like loops, while-else, etc.
}
#[allow(dead_code)]
fn test_template() {
let mut env = Default::default();

View File

@@ -5,7 +5,7 @@ use cl_structures::span::Loc;
use cl_token::{TokenKind as Kind, *};
use std::{
iter::Peekable,
str::{Chars, FromStr},
str::{CharIndices, FromStr},
};
use unicode_ident::*;
@@ -15,15 +15,15 @@ mod tests;
pub mod lexer_iter {
//! Iterator over a [`Lexer`], returning [`LResult<Token>`]s
use super::{
error::{LResult, Reason},
Lexer, Token,
error::{LResult, Reason},
};
/// Iterator over a [`Lexer`], returning [`LResult<Token>`]s
pub struct LexerIter<'t> {
lexer: Lexer<'t>,
}
impl<'t> Iterator for LexerIter<'t> {
impl Iterator for LexerIter<'_> {
type Item = LResult<Token>;
fn next(&mut self) -> Option<Self::Item> {
match self.lexer.scan() {
@@ -76,383 +76,379 @@ pub mod lexer_iter {
/// ```
#[derive(Clone, Debug)]
pub struct Lexer<'t> {
iter: Peekable<Chars<'t>>,
start: usize,
start_loc: (u32, u32),
current: usize,
current_loc: (u32, u32),
/// The source text
text: &'t str,
/// A peekable iterator over the source text
iter: Peekable<CharIndices<'t>>,
/// The end of the current token
head: usize,
/// The (line, col) end of the current token
head_loc: (u32, u32),
/// The start of the current token
tail: usize,
/// The (line, col) start of the current token
tail_loc: (u32, u32),
}
impl<'t> Lexer<'t> {
/// Creates a new [Lexer] over a [str]
pub fn new(text: &'t str) -> Self {
Self {
iter: text.chars().peekable(),
start: 0,
start_loc: (1, 1),
current: 0,
current_loc: (1, 1),
}
}
/// Scans through the text, searching for the next [Token]
pub fn scan(&mut self) -> LResult<Token> {
match self.skip_whitespace().peek()? {
'{' => self.consume()?.produce_op(Punct::LCurly),
'}' => self.consume()?.produce_op(Punct::RCurly),
'[' => self.consume()?.produce_op(Punct::LBrack),
']' => self.consume()?.produce_op(Punct::RBrack),
'(' => self.consume()?.produce_op(Punct::LParen),
')' => self.consume()?.produce_op(Punct::RParen),
'&' => self.consume()?.amp(),
'@' => self.consume()?.produce_op(Punct::At),
'\\' => self.consume()?.produce_op(Punct::Backslash),
'!' => self.consume()?.bang(),
'|' => self.consume()?.bar(),
':' => self.consume()?.colon(),
',' => self.consume()?.produce_op(Punct::Comma),
'.' => self.consume()?.dot(),
'=' => self.consume()?.equal(),
'`' => self.consume()?.produce_op(Punct::Grave),
'>' => self.consume()?.greater(),
'#' => self.consume()?.hash(),
'<' => self.consume()?.less(),
'-' => self.consume()?.minus(),
'+' => self.consume()?.plus(),
'?' => self.consume()?.produce_op(Punct::Question),
'%' => self.consume()?.rem(),
';' => self.consume()?.produce_op(Punct::Semi),
'/' => self.consume()?.slash(),
'*' => self.consume()?.star(),
'~' => self.consume()?.produce_op(Punct::Tilde),
'^' => self.consume()?.xor(),
'0' => self.consume()?.int_with_base(),
'1'..='9' => self.digits::<10>(),
'"' => self.consume()?.string(),
'\'' => self.consume()?.character(),
'_' => self.identifier(),
i if is_xid_start(i) => self.identifier(),
e => {
let err = Err(Error::unexpected_char(e, self.line(), self.col()));
let _ = self.consume();
err
}
text,
iter: text.char_indices().peekable(),
head: 0,
head_loc: (1, 1),
tail: 0,
tail_loc: (1, 1),
}
}
/// Returns the current line
pub fn line(&self) -> u32 {
self.start_loc.0
self.tail_loc.0
}
/// Returns the current column
pub fn col(&self) -> u32 {
self.start_loc.1
self.tail_loc.1
}
fn next(&mut self) -> LResult<char> {
let out = self.peek();
self.consume()?;
out
/// Returns the current token's lexeme
fn lexeme(&mut self) -> &'t str {
&self.text[self.tail..self.head]
}
fn peek(&mut self) -> LResult<char> {
self.iter
.peek()
.copied()
.ok_or(Error::end_of_file(self.line(), self.col()))
/// Peeks the next character without advancing the lexer
fn peek(&mut self) -> Option<char> {
self.iter.peek().map(|(_, c)| *c)
}
fn produce(&mut self, kind: TokenKind, data: impl Into<TokenData>) -> LResult<Token> {
let loc = self.start_loc;
self.start_loc = self.current_loc;
self.start = self.current;
Ok(Token::new(kind, data, loc.0, loc.1))
}
fn produce_op(&mut self, kind: Punct) -> LResult<Token> {
self.produce(TokenKind::Punct(kind), ())
}
fn skip_whitespace(&mut self) -> &mut Self {
while let Ok(c) = self.peek() {
if !c.is_whitespace() {
break;
}
let _ = self.consume();
}
self.start = self.current;
self.start_loc = self.current_loc;
self
}
fn consume(&mut self) -> LResult<&mut Self> {
self.current += 1;
match self.iter.next() {
Some('\n') => {
let (line, col) = &mut self.current_loc;
/// Advances the 'tail' (current position)
fn advance_tail(&mut self) {
let (idx, c) = self.iter.peek().copied().unwrap_or((self.text.len(), '\0'));
let (line, col) = &mut self.head_loc;
let diff = idx - self.head;
self.head = idx;
match c {
'\n' => {
*line += 1;
*col = 1;
}
Some(_) => self.current_loc.1 += 1,
None => Err(Error::end_of_file(self.line(), self.col()))?,
}
Ok(self)
}
}
/// Digraphs and trigraphs
impl<'t> Lexer<'t> {
fn amp(&mut self) -> LResult<Token> {
match self.peek() {
Ok('&') => self.consume()?.produce_op(Punct::AmpAmp),
Ok('=') => self.consume()?.produce_op(Punct::AmpEq),
_ => self.produce_op(Punct::Amp),
}
}
fn bang(&mut self) -> LResult<Token> {
match self.peek() {
Ok('!') => self.consume()?.produce_op(Punct::BangBang),
Ok('=') => self.consume()?.produce_op(Punct::BangEq),
_ => self.produce_op(Punct::Bang),
}
}
fn bar(&mut self) -> LResult<Token> {
match self.peek() {
Ok('|') => self.consume()?.produce_op(Punct::BarBar),
Ok('=') => self.consume()?.produce_op(Punct::BarEq),
_ => self.produce_op(Punct::Bar),
}
}
fn colon(&mut self) -> LResult<Token> {
match self.peek() {
Ok(':') => self.consume()?.produce_op(Punct::ColonColon),
_ => self.produce_op(Punct::Colon),
}
}
fn dot(&mut self) -> LResult<Token> {
match self.peek() {
Ok('.') => {
if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::DotDotEq)
} else {
self.produce_op(Punct::DotDot)
}
}
_ => self.produce_op(Punct::Dot),
}
}
fn equal(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::EqEq),
Ok('>') => self.consume()?.produce_op(Punct::FatArrow),
_ => self.produce_op(Punct::Eq),
}
}
fn greater(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::GtEq),
Ok('>') => {
if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::GtGtEq)
} else {
self.produce_op(Punct::GtGt)
}
}
_ => self.produce_op(Punct::Gt),
}
}
fn hash(&mut self) -> LResult<Token> {
match self.peek() {
Ok('!') => self.consume()?.produce_op(Punct::HashBang),
_ => self.produce_op(Punct::Hash),
}
}
fn less(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::LtEq),
Ok('<') => {
if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::LtLtEq)
} else {
self.produce_op(Punct::LtLt)
}
}
_ => self.produce_op(Punct::Lt),
}
}
fn minus(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::MinusEq),
Ok('>') => self.consume()?.produce_op(Punct::Arrow),
_ => self.produce_op(Punct::Minus),
}
}
fn plus(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::PlusEq),
_ => self.produce_op(Punct::Plus),
}
}
fn rem(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::RemEq),
_ => self.produce_op(Punct::Rem),
}
}
fn slash(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::SlashEq),
Ok('/') => self.consume()?.line_comment(),
Ok('*') => self.consume()?.block_comment(),
_ => self.produce_op(Punct::Slash),
}
}
fn star(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::StarEq),
_ => self.produce_op(Punct::Star),
}
}
fn xor(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::XorEq),
Ok('^') => self.consume()?.produce_op(Punct::XorXor),
_ => self.produce_op(Punct::Xor),
_ => *col += diff as u32,
}
}
/// Takes the last-peeked character, or the next character if none peeked.
pub fn take(&mut self) -> Option<char> {
let (_, c) = self.iter.next()?;
self.advance_tail();
Some(c)
}
/// Takes the next char if it matches the `expected` char
pub fn next_if(&mut self, expected: char) -> Option<char> {
let (_, c) = self.iter.next_if(|&(_, c)| c == expected)?;
self.advance_tail();
Some(c)
}
/// Consumes the last-peeked character, advancing the tail
pub fn consume(&mut self) -> &mut Self {
self.iter.next();
self.advance_tail();
self
}
/// Produces an [Error] at the start of the current token
fn error(&self, reason: Reason) -> Error {
Error { reason, line: self.line(), col: self.col() }
}
/// Produces a token with the current [lexeme](Lexer::lexeme) as its data
fn produce(&mut self, kind: Kind) -> LResult<Token> {
let lexeme = self.lexeme().to_owned();
self.produce_with(kind, lexeme)
}
/// Produces a token with the provided `data`
fn produce_with(&mut self, kind: Kind, data: impl Into<TokenData>) -> LResult<Token> {
let loc = self.tail_loc;
self.tail_loc = self.head_loc;
self.tail = self.head;
Ok(Token::new(kind, data, loc.0, loc.1))
}
/// Produces a token with no `data`
fn produce_op(&mut self, kind: Kind) -> LResult<Token> {
self.produce_with(kind, ())
}
/// Consumes 0 or more whitespace
fn skip_whitespace(&mut self) -> &mut Self {
while self.peek().is_some_and(char::is_whitespace) {
let _ = self.consume();
}
self
}
/// Starts a new token
fn start_token(&mut self) -> &mut Self {
self.tail_loc = self.head_loc;
self.tail = self.head;
self
}
/// Scans through the text, searching for the next [Token]
pub fn scan(&mut self) -> LResult<Token> {
use TokenKind::*;
// !"#%&'()*+,-./:;<=>?@[\\]^`{|}~
let tok = match self
.skip_whitespace()
.start_token()
.peek()
.ok_or_else(|| self.error(Reason::EndOfFile))?
{
'!' => Bang,
'"' => return self.string(),
'#' => Hash,
'%' => Rem,
'&' => Amp,
'\'' => return self.character(),
'(' => LParen,
')' => RParen,
'*' => Star,
'+' => Plus,
',' => Comma,
'-' => Minus,
'.' => Dot,
'/' => Slash,
'0' => TokenKind::Literal,
'1'..='9' => return self.digits::<10>(),
':' => Colon,
';' => Semi,
'<' => Lt,
'=' => Eq,
'>' => Gt,
'?' => Question,
'@' => At,
'[' => LBrack,
'\\' => Backslash,
']' => RBrack,
'^' => Xor,
'`' => Grave,
'{' => LCurly,
'|' => Bar,
'}' => RCurly,
'~' => Tilde,
'_' => return self.identifier(),
c if is_xid_start(c) => return self.identifier(),
e => {
let err = Err(self.error(Reason::UnexpectedChar(e)));
let _ = self.consume();
err?
}
};
// Handle digraphs
let tok = match (tok, self.consume().peek()) {
(Literal, Some('b')) => return self.consume().digits::<2>(),
(Literal, Some('d')) => return self.consume().digits::<10>(),
(Literal, Some('o')) => return self.consume().digits::<8>(),
(Literal, Some('x')) => return self.consume().digits::<16>(),
(Literal, Some('~')) => return self.consume().digits::<36>(),
(Literal, _) => return self.digits::<10>(),
(Amp, Some('&')) => AmpAmp,
(Amp, Some('=')) => AmpEq,
(Bang, Some('!')) => BangBang,
(Bang, Some('=')) => BangEq,
(Bar, Some('|')) => BarBar,
(Bar, Some('=')) => BarEq,
(Colon, Some(':')) => ColonColon,
(Dot, Some('.')) => DotDot,
(Eq, Some('=')) => EqEq,
(Eq, Some('>')) => FatArrow,
(Gt, Some('=')) => GtEq,
(Gt, Some('>')) => GtGt,
(Hash, Some('!')) => HashBang,
(Lt, Some('=')) => LtEq,
(Lt, Some('<')) => LtLt,
(Minus, Some('=')) => MinusEq,
(Minus, Some('>')) => Arrow,
(Plus, Some('=')) => PlusEq,
(Rem, Some('=')) => RemEq,
(Slash, Some('*')) => return self.block_comment()?.produce(Kind::Comment),
(Slash, Some('/')) => return self.line_comment(),
(Slash, Some('=')) => SlashEq,
(Star, Some('=')) => StarEq,
(Xor, Some('=')) => XorEq,
(Xor, Some('^')) => XorXor,
_ => return self.produce_op(tok),
};
// Handle trigraphs
let tok = match (tok, self.consume().peek()) {
(HashBang, Some('/')) => return self.line_comment(),
(DotDot, Some('=')) => DotDotEq,
(GtGt, Some('=')) => GtGtEq,
(LtLt, Some('=')) => LtLtEq,
_ => return self.produce_op(tok),
};
self.consume().produce_op(tok)
}
}
/// Comments
impl<'t> Lexer<'t> {
impl Lexer<'_> {
/// Consumes until the next newline '\n', producing a [Comment](Kind::Comment)
fn line_comment(&mut self) -> LResult<Token> {
while Ok('\n') != self.peek() {
self.consume()?;
while self.consume().peek().is_some_and(|c| c != '\n') {}
self.produce(Kind::Comment)
}
self.produce(Kind::Comment, ())
/// Consumes nested block-comments. Does not produce by itself.
fn block_comment(&mut self) -> LResult<&mut Self> {
self.consume();
while let Some(c) = self.take() {
match (c, self.peek()) {
('/', Some('*')) => self.block_comment()?,
('*', Some('/')) => return Ok(self.consume()),
_ => continue,
};
}
fn block_comment(&mut self) -> LResult<Token> {
while let Ok(c) = self.next() {
if '*' == c && Ok('/') == self.next() {
break;
}
}
self.produce(Kind::Comment, ())
Err(self.error(Reason::UnmatchedDelimiters('/')))
}
}
/// Identifiers
impl<'t> Lexer<'t> {
impl Lexer<'_> {
/// Produces an [Identifier](Kind::Identifier) or keyword
fn identifier(&mut self) -> LResult<Token> {
let mut out = String::from(self.xid_start()?);
while let Ok(c) = self.xid_continue() {
out.push(c)
}
if let Ok(keyword) = Kind::from_str(&out) {
self.produce(keyword, ())
while self.consume().peek().is_some_and(is_xid_continue) {}
if let Ok(keyword) = Kind::from_str(self.lexeme()) {
self.produce_with(keyword, ())
} else {
self.produce(Kind::Identifier, TokenData::String(out))
}
}
fn xid_start(&mut self) -> LResult<char> {
match self.peek()? {
xid if xid == '_' || is_xid_start(xid) => {
self.consume()?;
Ok(xid)
}
bad => Err(Error::not_identifier(bad, self.line(), self.col())),
}
}
fn xid_continue(&mut self) -> LResult<char> {
match self.peek()? {
xid if is_xid_continue(xid) => {
self.consume()?;
Ok(xid)
}
bad => Err(Error::not_identifier(bad, self.line(), self.col())),
self.produce(Kind::Identifier)
}
}
}
/// Integers
impl<'t> Lexer<'t> {
fn int_with_base(&mut self) -> LResult<Token> {
match self.peek() {
Ok('x') => self.consume()?.digits::<16>(),
Ok('d') => self.consume()?.digits::<10>(),
Ok('o') => self.consume()?.digits::<8>(),
Ok('b') => self.consume()?.digits::<2>(),
Ok('0'..='9') => self.digits::<10>(),
_ => self.produce(Kind::Literal, 0),
}
}
impl Lexer<'_> {
/// Produces a [Literal](Kind::Literal) with an integer or float value.
fn digits<const B: u32>(&mut self) -> LResult<Token> {
let mut value = self.digit::<B>()? as u128;
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
let mut value = 0;
while let Some(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
value = value * B as u128 + self.digit::<B>()? as u128;
}
self.produce(Kind::Literal, value)
// TODO: find a better way to handle floats in the tokenizer
match self.peek() {
Some('.') => {
// FIXME: hack: 0.. is not [0.0, '.']
if let Some('.') = self.clone().consume().take() {
return self.produce_with(Kind::Literal, value);
}
let mut float = format!("{value}.");
self.consume();
while let Some(true) = self.peek().as_ref().map(char::is_ascii_digit) {
float.push(self.iter.next().map(|(_, c)| c).unwrap_or_default());
}
let float = f64::from_str(&float).expect("must be parsable as float");
self.produce_with(Kind::Literal, float)
}
_ => self.produce_with(Kind::Literal, value),
}
}
/// Consumes a single digit of base [B](Lexer::digit)
fn digit<const B: u32>(&mut self) -> LResult<u32> {
let digit = self.peek()?;
self.consume()?;
let digit = self.take().ok_or_else(|| self.error(Reason::EndOfFile))?;
digit
.to_digit(B)
.ok_or(Error::invalid_digit(digit, self.line(), self.col()))
.ok_or_else(|| self.error(Reason::InvalidDigit(digit)))
}
}
/// Strings and characters
impl<'t> Lexer<'t> {
fn string(&mut self) -> LResult<Token> {
let mut value = String::new();
while '"'
!= self
.peek()
.map_err(|e| e.mask_reason(Reason::UnmatchedDelimiters('"')))?
{
value.push(self.unescape()?)
impl Lexer<'_> {
/// Produces a [Literal](Kind::Literal) with a pre-escaped [String]
pub fn string(&mut self) -> Result<Token, Error> {
let mut lexeme = String::new();
let mut depth = 0;
self.consume();
loop {
lexeme.push(match self.take() {
None => Err(self.error(Reason::UnmatchedDelimiters('"')))?,
Some('\\') => self.unescape()?,
Some('"') if depth == 0 => break,
Some(c @ '{') => {
depth += 1;
c
}
self.consume()?.produce(Kind::Literal, value)
Some(c @ '}') => {
depth -= 1;
c
}
fn character(&mut self) -> LResult<Token> {
let out = self.unescape()?;
match self.peek()? {
'\'' => self.consume()?.produce(Kind::Literal, out),
_ => Err(Error::unmatched_delimiters('\'', self.line(), self.col())),
Some(c) => c,
})
}
lexeme.shrink_to_fit();
self.produce_with(Kind::Literal, lexeme)
}
/// Produces a [Literal](Kind::Literal) with a pre-escaped [char]
fn character(&mut self) -> Result<Token, Error> {
let c = match self.consume().take() {
Some('\\') => self.unescape()?,
Some(c) => c,
None => '\0',
};
if self.take().is_some_and(|c| c == '\'') {
self.produce_with(Kind::Literal, c)
} else {
Err(self.error(Reason::UnmatchedDelimiters('\'')))
}
}
/// Unescape a single character
/// Unescapes a single character
#[rustfmt::skip]
fn unescape(&mut self) -> LResult<char> {
match self.next() {
Ok('\\') => (),
other => return other,
}
Ok(match self.next()? {
Ok(match self.take().ok_or_else(|| self.error(Reason::EndOfFile))? {
' ' => '\u{a0}',
'0' => '\0',
'a' => '\x07',
'b' => '\x08',
'e' => '\x1b',
'f' => '\x0c',
'n' => '\n',
'r' => '\r',
't' => '\t',
'x' => self.hex_escape()?,
'u' => self.unicode_escape()?,
'0' => '\0',
'x' => self.hex_escape()?,
chr => chr,
})
}
/// unescape a single 2-digit hex escape
/// Unescapes a single 2-digit hex escape
fn hex_escape(&mut self) -> LResult<char> {
let out = (self.digit::<16>()? << 4) + self.digit::<16>()?;
char::from_u32(out).ok_or(Error::bad_unicode(out, self.line(), self.col()))
char::from_u32(out).ok_or_else(|| self.error(Reason::BadUnicode(out)))
}
/// unescape a single \u{} unicode escape
fn unicode_escape(&mut self) -> LResult<char> {
/// Unescapes a single \u{} unicode escape
pub fn unicode_escape(&mut self) -> Result<char, Error> {
self.next_if('{')
.ok_or_else(|| self.error(Reason::InvalidEscape('u')))?;
let mut out = 0;
let Ok('{') = self.peek() else {
return Err(Error::invalid_escape('u', self.line(), self.col()));
};
self.consume()?;
while let Ok(c) = self.peek() {
match c {
'}' => {
self.consume()?;
return char::from_u32(out).ok_or(Error::bad_unicode(
out,
self.line(),
self.col(),
));
while let Some(c) = self.take() {
if c == '}' {
return char::from_u32(out).ok_or_else(|| self.error(Reason::BadUnicode(out)));
}
_ => out = (out << 4) + self.digit::<16>()?,
out = out * 16
+ c.to_digit(16)
.ok_or_else(|| self.error(Reason::InvalidDigit(c)))?;
}
}
Err(Error::invalid_escape('u', self.line(), self.col()))
Err(self.error(Reason::UnmatchedDelimiters('}')))
}
}
@@ -482,8 +478,6 @@ pub mod error {
UnmatchedDelimiters(char),
/// Found a character that doesn't belong to any [TokenKind](cl_token::TokenKind)
UnexpectedChar(char),
/// Found a character that's not valid in identifiers while looking for an identifier
NotIdentifier(char),
/// Found a character that's not valid in an escape sequence while looking for an escape
/// sequence
UnknownEscape(char),
@@ -491,30 +485,12 @@ pub mod error {
InvalidEscape(char),
/// Character is not a valid digit in the requested base
InvalidDigit(char),
/// Base conversion requested, but the base character was not in the set of known
/// characters
UnknownBase(char),
/// Unicode escape does not map to a valid unicode code-point
BadUnicode(u32),
/// Reached end of input
EndOfFile,
}
error_impl! {
unmatched_delimiters(c: char) => Reason::UnmatchedDelimiters(c),
unexpected_char(c: char) => Reason::UnexpectedChar(c),
not_identifier(c: char) => Reason::NotIdentifier(c),
unknown_escape(e: char) => Reason::UnknownEscape(e),
invalid_escape(e: char) => Reason::InvalidEscape(e),
invalid_digit(digit: char) => Reason::InvalidDigit(digit),
unknown_base(base: char) => Reason::UnknownBase(base),
bad_unicode(value: u32) => Reason::BadUnicode(value),
end_of_file => Reason::EndOfFile,
}
impl Error {
/// Changes the [Reason] of this error
pub(super) fn mask_reason(self, reason: Reason) -> Self {
Self { reason, ..self }
}
/// Returns the [Reason] for this error
pub fn reason(&self) -> &Reason {
&self.reason
@@ -524,14 +500,6 @@ pub mod error {
(self.line, self.col)
}
}
macro error_impl ($($fn:ident$(( $($p:ident: $t:ty),* ))? => $reason:expr),*$(,)?) {
#[allow(dead_code)]
impl Error {
$(pub(super) fn $fn ($($($p: $t),*,)? line: u32, col: u32) -> Self {
Self { reason: $reason, line, col }
})*
}
}
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -541,14 +509,12 @@ pub mod error {
impl Display for Reason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Reason::UnmatchedDelimiters(c) => write! {f, "Unmatched `{c}` in input"},
Reason::UnexpectedChar(c) => write!(f, "Character `{c}` not expected"),
Reason::NotIdentifier(c) => write!(f, "Character `{c}` not valid in identifiers"),
Reason::UnmatchedDelimiters(c) => write! {f, "Unmatched `{c:?}` in input"},
Reason::UnexpectedChar(c) => write!(f, "Character `{c:?}` not expected"),
Reason::UnknownEscape(c) => write!(f, "`\\{c}` is not a known escape sequence"),
Reason::InvalidEscape(c) => write!(f, "Escape sequence `\\{c}`... is malformed"),
Reason::InvalidDigit(c) => write!(f, "`{c}` is not a valid digit"),
Reason::UnknownBase(c) => write!(f, "`0{c}`... is not a valid base"),
Reason::BadUnicode(c) => write!(f, "`{c}` is not a valid unicode code-point"),
Reason::InvalidDigit(c) => write!(f, "`{c:?}` is not a valid digit"),
Reason::BadUnicode(c) => write!(f, "`\\u{{{c:x}}}` is not valid unicode"),
Reason::EndOfFile => write!(f, "Reached end of input"),
}
}

View File

@@ -110,7 +110,7 @@ mod string {
}
mod punct {
macro op($op:ident) {
TokenKind::Punct(Punct::$op)
TokenKind::$op
}
use super::*;

View File

@@ -1,5 +1,6 @@
use super::*;
use cl_ast::{Expr, Sym};
use cl_lexer::error::{Error as LexError, Reason};
use std::fmt::Display;
pub type PResult<T> = Result<T, Error>;
@@ -7,6 +8,7 @@ pub type PResult<T> = Result<T, Error>;
/// Contains information about [Parser] errors
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error {
pub in_file: Sym,
pub reason: ErrorKind,
pub while_parsing: Parsing,
pub loc: Loc,
@@ -29,6 +31,7 @@ pub enum ErrorKind {
ExpectedParsing {
want: Parsing,
},
InvalidPattern(Box<Expr>),
/// Indicates unfinished code
Todo(&'static str),
}
@@ -57,6 +60,7 @@ pub enum Parsing {
Item,
ItemKind,
Generics,
Alias,
Const,
Static,
@@ -93,12 +97,14 @@ pub enum Parsing {
Expr,
ExprKind,
Closure,
Assign,
AssignKind,
Binary,
BinaryKind,
Unary,
UnaryKind,
Cast,
Index,
Structor,
Fielder,
@@ -118,17 +124,26 @@ pub enum Parsing {
Break,
Return,
Continue,
Pattern,
Match,
MatchArm,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { reason, while_parsing, loc } = self;
let Self { in_file, reason, while_parsing, loc } = self;
match reason {
// TODO entries are debug-printed
ErrorKind::Todo(_) => write!(f, "{loc} {reason} {while_parsing:?}"),
ErrorKind::Todo(_) => write!(f, "{in_file}:{loc} {reason} {while_parsing:?}"),
// lexical errors print their own higher-resolution loc info
ErrorKind::Lexical(e) => write!(f, "{e} (while parsing {while_parsing})"),
_ => write!(f, "{loc} {reason} while parsing {while_parsing}"),
_ => {
if !in_file.is_empty() {
write!(f, "{in_file}:")?
}
write!(f, "{loc}: {reason} while parsing {while_parsing}")
}
}
}
}
@@ -143,6 +158,7 @@ impl Display for ErrorKind {
ErrorKind::Unexpected(t) => write!(f, "Encountered unexpected token `{t}`"),
ErrorKind::ExpectedToken { want: e, got: g } => write!(f, "Expected `{e}`, got `{g}`"),
ErrorKind::ExpectedParsing { want } => write!(f, "Expected {want}"),
ErrorKind::InvalidPattern(got) => write!(f, "Got invalid `{got}`"),
ErrorKind::Todo(unfinished) => write!(f, "TODO: {unfinished}"),
}
}
@@ -162,6 +178,7 @@ impl Display for Parsing {
Parsing::MetaKind => "an attribute's arguments",
Parsing::Item => "an item",
Parsing::ItemKind => "an item",
Parsing::Generics => "a list of type arguments",
Parsing::Alias => "a type alias",
Parsing::Const => "a const item",
Parsing::Static => "a static variable",
@@ -198,12 +215,14 @@ impl Display for Parsing {
Parsing::Expr => "an expression",
Parsing::ExprKind => "an expression",
Parsing::Closure => "an anonymous function",
Parsing::Assign => "an assignment",
Parsing::AssignKind => "an assignment operator",
Parsing::Binary => "a binary expression",
Parsing::BinaryKind => "a binary operator",
Parsing::Unary => "a unary expression",
Parsing::UnaryKind => "a unary operator",
Parsing::Cast => "an `as`-casting expression",
Parsing::Index => "an indexing expression",
Parsing::Structor => "a struct constructor expression",
Parsing::Fielder => "a struct field expression",
@@ -223,6 +242,10 @@ impl Display for Parsing {
Parsing::Break => "a break expression",
Parsing::Return => "a return expression",
Parsing::Continue => "a continue expression",
Parsing::Pattern => "a pattern",
Parsing::Match => "a match expression",
Parsing::MatchArm => "a match arm",
}
.fmt(f)
}

View File

@@ -48,51 +48,71 @@ impl ModuleInliner {
}
/// Records an [I/O error](std::io::Error) for later
fn handle_io_error(&mut self, error: std::io::Error) -> ModuleKind {
fn handle_io_error(&mut self, error: std::io::Error) -> Option<File> {
self.io_errs.push((self.path.clone(), error));
ModuleKind::Outline
None
}
/// Records a [parse error](crate::error::Error) for later
fn handle_parse_error(&mut self, error: crate::error::Error) -> ModuleKind {
fn handle_parse_error(&mut self, error: crate::error::Error) -> Option<File> {
self.parse_errs.push((self.path.clone(), error));
ModuleKind::Outline
None
}
}
impl Fold for ModuleInliner {
/// Traverses down the module tree, entering ever nested directories
fn fold_module(&mut self, m: Module) -> Module {
let Module { name, kind } = m;
let Module { name, file } = m;
self.path.push(&*name); // cd ./name
let kind = self.fold_module_kind(kind);
let file = self.fold_module_kind(file);
self.path.pop(); // cd ..
Module { name, kind }
Module { name, file }
}
}
impl ModuleInliner {
/// Attempts to read and parse a file for every module in the tree
fn fold_module_kind(&mut self, m: Option<File>) -> Option<File> {
use std::borrow::Cow;
if let Some(f) = m {
return Some(self.fold_file(f));
}
/// Attempts to read and parse a file for every module in the tree
fn fold_module_kind(&mut self, m: ModuleKind) -> ModuleKind {
if let ModuleKind::Inline(f) = m {
return ModuleKind::Inline(self.fold_file(f));
}
// cd path/mod.cl
self.path.set_extension("cl");
let mut used_path: Cow<Path> = Cow::Borrowed(&self.path);
let file = match std::fs::read_to_string(&self.path) {
Err(error) => {
let Some(basename) = self.path.file_name() else {
return self.handle_io_error(error);
};
used_path = Cow::Owned(
self.path
.parent()
.and_then(Path::parent)
.map(|path| path.join(basename))
.unwrap_or_default(),
);
match std::fs::read_to_string(&used_path) {
Err(error) => return self.handle_io_error(error),
Ok(file) => file,
}
}
Ok(file) => file,
};
let kind = match Parser::new(Lexer::new(&file)).file() {
Err(e) => return self.handle_parse_error(e),
Ok(file) => ModuleKind::Inline(file),
};
// cd path/mod
match Parser::new(used_path.display().to_string(), Lexer::new(&file)).parse() {
Err(e) => self.handle_parse_error(e),
Ok(file) => {
self.path.set_extension("");
// The newly loaded module may need further inlining
self.fold_module_kind(kind)
Some(self.fold_file(file))
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,425 @@
//! Parses an [ExprKind] using a modified pratt parser
//!
//! See also: [Expr::parse], [ExprKind::parse]
//!
//! Implementer's note: [ExprKind::parse] is the public API for parsing [ExprKind]s.
//! Do not call it from within this function.
use super::{Parse, *};
/// Parses an [ExprKind]
pub fn expr(p: &mut Parser, power: u8) -> PResult<Expr> {
let parsing = Parsing::ExprKind;
let start = p.loc();
// Prefix expressions
let mut head = Expr {
kind: match p.peek_kind(Parsing::Unary)? {
literal_like!() => Literal::parse(p)?.into(),
path_like!() => exprkind_pathlike(p)?,
TokenKind::Amp | TokenKind::AmpAmp => AddrOf::parse(p)?.into(),
TokenKind::Bar | TokenKind::BarBar => Closure::parse(p)?.into(),
TokenKind::Grave => Quote::parse(p)?.into(),
TokenKind::LCurly => Block::parse(p)?.into(),
TokenKind::LBrack => exprkind_arraylike(p)?,
TokenKind::LParen => exprkind_tuplelike(p)?,
TokenKind::Let => Let::parse(p)?.into(),
TokenKind::Match => Match::parse(p)?.into(),
TokenKind::While => ExprKind::While(While::parse(p)?),
TokenKind::If => ExprKind::If(If::parse(p)?),
TokenKind::For => ExprKind::For(For::parse(p)?),
TokenKind::Break => ExprKind::Break(Break::parse(p)?),
TokenKind::Return => ExprKind::Return(Return::parse(p)?),
TokenKind::Continue => {
p.consume_peeked();
ExprKind::Continue
}
op => {
let (kind, prec) =
from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?;
let ((), after) = prec.prefix().expect("should have a precedence");
p.consume_peeked();
Unary { kind, tail: expr(p, after)?.into() }.into()
}
},
span: Span(start, p.loc()),
};
fn from_postfix(op: TokenKind) -> Option<Precedence> {
Some(match op {
TokenKind::LBrack => Precedence::Index,
TokenKind::LParen => Precedence::Call,
TokenKind::LCurly => Precedence::Structor,
TokenKind::Dot => Precedence::Member,
TokenKind::As => Precedence::Cast,
_ => None?,
})
}
while let Ok(op) = p.peek_kind(parsing) {
// Postfix expressions
if let Some((before, ())) = from_postfix(op).and_then(Precedence::postfix) {
if before < power {
break;
}
head = Expr {
kind: match op {
TokenKind::LBrack => {
p.consume_peeked();
let indices =
sep(Expr::parse, TokenKind::Comma, TokenKind::RBrack, parsing)(p)?;
p.match_type(TokenKind::RBrack, parsing)?;
ExprKind::Index(Index { head: head.into(), indices })
}
TokenKind::LParen => {
p.consume_peeked();
let exprs =
sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
p.match_type(TokenKind::RParen, parsing)?;
Binary {
kind: BinaryKind::Call,
parts: (
head,
Expr { kind: Tuple { exprs }.into(), span: Span(start, p.loc()) },
)
.into(),
}
.into()
}
TokenKind::LCurly => match head.kind {
ExprKind::Path(path) => ExprKind::Structor(structor_body(p, path)?),
_ => break,
},
TokenKind::Dot => {
p.consume_peeked();
let kind = MemberKind::parse(p)?;
Member { head: Box::new(head), kind }.into()
}
TokenKind::As => {
p.consume_peeked();
let ty = Ty::parse(p)?;
Cast { head: head.into(), ty }.into()
}
_ => Err(p.error(Unexpected(op), parsing))?,
},
span: Span(start, p.loc()),
};
continue;
}
// infix expressions
if let Some((kind, prec)) = from_infix(op) {
let (before, after) = prec.infix().expect("should have a precedence");
if before < power {
break;
}
p.consume_peeked();
let tail = expr(p, after)?;
head = Expr {
kind: Binary { kind, parts: (head, tail).into() }.into(),
span: Span(start, p.loc()),
};
continue;
}
if let Some((kind, prec)) = from_modify(op) {
let (before, after) = prec.infix().expect("should have a precedence");
if before < power {
break;
}
p.consume_peeked();
let tail = expr(p, after)?;
head = Expr {
kind: Modify { kind, parts: (head, tail).into() }.into(),
span: Span(start, p.loc()),
};
continue;
}
if let TokenKind::Eq = op {
let (before, after) = Precedence::Assign
.infix()
.expect("should have a precedence");
if before < power {
break;
}
p.consume_peeked();
let tail = expr(p, after)?;
head = Expr {
kind: Assign { parts: (head, tail).into() }.into(),
span: Span(start, p.loc()),
};
continue;
}
if let TokenKind::As = op {
let before = Precedence::Cast.level();
if before < power {
break;
}
p.consume_peeked();
let ty = Ty::parse(p)?;
head = Expr { kind: Cast { head: head.into(), ty }.into(), span: Span(start, p.loc()) };
continue;
}
break;
}
Ok(head)
}
/// [Array] = '[' ([Expr] ',')* [Expr]? ']'
///
/// Array and ArrayRef are ambiguous until the second token,
/// so they can't be independent subexpressions
fn exprkind_arraylike(p: &mut Parser) -> PResult<ExprKind> {
const P: Parsing = Parsing::Array;
const START: TokenKind = TokenKind::LBrack;
const END: TokenKind = TokenKind::RBrack;
p.match_type(START, P)?;
let out = match p.peek_kind(P)? {
END => Array { values: vec![] }.into(),
_ => exprkind_array_rep(p)?,
};
p.match_type(END, P)?;
Ok(out)
}
/// [ArrayRep] = `[` [Expr] `;` [Expr] `]`
fn exprkind_array_rep(p: &mut Parser) -> PResult<ExprKind> {
const P: Parsing = Parsing::Array;
const END: TokenKind = TokenKind::RBrack;
let first = Expr::parse(p)?;
Ok(match p.peek_kind(P)? {
TokenKind::Semi => ArrayRep {
value: first.into(),
repeat: {
p.consume_peeked();
p.parse()?
},
}
.into(),
TokenKind::RBrack => Array { values: vec![first] }.into(),
TokenKind::Comma => Array {
values: {
p.consume_peeked();
let mut out = vec![first];
out.extend(sep(Expr::parse, TokenKind::Comma, END, P)(p)?);
out
},
}
.into(),
ty => Err(p.error(Unexpected(ty), P))?,
})
}
/// [Group] = `(`([Empty](ExprKind::Empty)|[Expr]|[Tuple])`)`
///
/// [ExprKind::Empty] and [Group] are special cases of [Tuple]
fn exprkind_tuplelike(p: &mut Parser) -> PResult<ExprKind> {
p.match_type(TokenKind::LParen, Parsing::Group)?;
let out = match p.peek_kind(Parsing::Group)? {
TokenKind::RParen => Ok(ExprKind::Empty),
_ => exprkind_group(p),
};
p.match_type(TokenKind::RParen, Parsing::Group)?;
out
}
/// [Group] = `(`([Empty](ExprKind::Empty)|[Expr]|[Tuple])`)`
fn exprkind_group(p: &mut Parser) -> PResult<ExprKind> {
let first = Expr::parse(p)?;
match p.peek_kind(Parsing::Group)? {
TokenKind::Comma => {
let mut exprs = vec![first];
p.consume_peeked();
while TokenKind::RParen != p.peek_kind(Parsing::Tuple)? {
exprs.push(Expr::parse(p)?);
match p.peek_kind(Parsing::Tuple)? {
TokenKind::Comma => p.consume_peeked(),
_ => break,
};
}
Ok(Tuple { exprs }.into())
}
_ => Ok(Group { expr: first.into() }.into()),
}
}
/// Parses an expression beginning with a [Path] (i.e. [Path] or [Structor])
fn exprkind_pathlike(p: &mut Parser) -> PResult<ExprKind> {
Path::parse(p).map(Into::into)
}
/// [Structor]Body = `{` ([Fielder] `,`)* [Fielder]? `}`
fn structor_body(p: &mut Parser, to: Path) -> PResult<Structor> {
let init = delim(
sep(
Fielder::parse,
TokenKind::Comma,
CURLIES.1,
Parsing::Structor,
),
CURLIES,
Parsing::Structor,
)(p)?;
Ok(Structor { to, init })
}
/// Precedence provides a total ordering among operators
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Precedence {
Assign,
Structor, // A structor is never a valid conditional
Condition, // Anything that syntactically needs a block following it
Logic,
Compare,
Range,
Bitwise,
Shift,
Factor,
Term,
Unary,
Index,
Cast,
Member, // left-associative
Call,
Deref,
}
impl Precedence {
#[inline]
pub const fn level(self) -> u8 {
(self as u8) << 1
}
pub fn prefix(self) -> Option<((), u8)> {
match self {
Self::Assign => Some(((), self.level())),
Self::Unary => Some(((), self.level())),
Self::Deref => Some(((), self.level())),
_ => None,
}
}
pub fn infix(self) -> Option<(u8, u8)> {
let level = self.level();
match self {
Self::Unary => None,
Self::Assign => Some((level + 1, level)),
_ => Some((level, level + 1)),
}
}
pub fn postfix(self) -> Option<(u8, ())> {
match self {
Self::Structor | Self::Index | Self::Call | Self::Member | Self::Cast => {
Some((self.level(), ()))
}
_ => None,
}
}
}
impl From<ModifyKind> for Precedence {
fn from(_value: ModifyKind) -> Self {
Precedence::Assign
}
}
impl From<BinaryKind> for Precedence {
fn from(value: BinaryKind) -> Self {
use BinaryKind as Op;
match value {
Op::Call => Precedence::Call,
Op::Mul | Op::Div | Op::Rem => Precedence::Term,
Op::Add | Op::Sub => Precedence::Factor,
Op::Shl | Op::Shr => Precedence::Shift,
Op::BitAnd | Op::BitOr | Op::BitXor => Precedence::Bitwise,
Op::LogAnd | Op::LogOr | Op::LogXor => Precedence::Logic,
Op::RangeExc | Op::RangeInc => Precedence::Range,
Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => Precedence::Compare,
}
}
}
impl From<UnaryKind> for Precedence {
fn from(value: UnaryKind) -> Self {
use UnaryKind as Op;
match value {
Op::Loop => Precedence::Assign,
Op::Deref => Precedence::Deref,
_ => Precedence::Unary,
}
}
}
/// Creates helper functions for turning TokenKinds into AST operators
macro operator($($name:ident ($takes:ident => $returns:ident) {$($t:ident => $p:ident),*$(,)?};)*) {$(
pub fn $name (value: $takes) -> Option<($returns, Precedence)> {
match value {
$($takes::$t => Some(($returns::$p, Precedence::from($returns::$p))),)*
_ => None?,
}
})*
}
operator! {
from_prefix (TokenKind => UnaryKind) {
Loop => Loop,
Star => Deref,
Minus => Neg,
Bang => Not,
DotDot => RangeExc,
DotDotEq => RangeInc,
At => At,
Tilde => Tilde,
};
from_modify(TokenKind => ModifyKind) {
AmpEq => And,
BarEq => Or,
XorEq => Xor,
LtLtEq => Shl,
GtGtEq => Shr,
PlusEq => Add,
MinusEq => Sub,
StarEq => Mul,
SlashEq => Div,
RemEq => Rem,
};
from_infix (TokenKind => BinaryKind) {
Lt => Lt,
LtEq => LtEq,
EqEq => Equal,
BangEq => NotEq,
GtEq => GtEq,
Gt => Gt,
DotDot => RangeExc,
DotDotEq => RangeInc,
AmpAmp => LogAnd,
BarBar => LogOr,
XorXor => LogXor,
Amp => BitAnd,
Bar => BitOr,
Xor => BitXor,
LtLt => Shl,
GtGt => Shr,
Plus => Add,
Minus => Sub,
Star => Mul,
Slash => Div,
Rem => Rem,
};
}

View File

@@ -14,6 +14,9 @@ cl-ast = { path = "../cl-ast" }
cl-lexer = { path = "../cl-lexer" }
cl-token = { path = "../cl-token" }
cl-parser = { path = "../cl-parser" }
cl-typeck = { path = "../cl-typeck" }
cl-interpret = { path = "../cl-interpret" }
repline = { path = "../../repline" }
cl-structures = { path = "../cl-structures" }
cl-arena = { version = "0", registry = "soft-fish" }
repline = { version = "*", registry = "soft-fish" }
argwerk = "0.20.4"

View File

@@ -4,7 +4,7 @@ use cl_lexer::Lexer;
use cl_token::Token;
use std::{
error::Error,
io::{stdin, IsTerminal, Read},
io::{IsTerminal, Read, stdin},
path::{Path, PathBuf},
};

View File

@@ -0,0 +1,951 @@
//! Pretty prints a conlang AST in yaml
use cl_ast::{File, Stmt};
use cl_lexer::Lexer;
use cl_parser::Parser;
use repline::{Repline, error::Error as RlError};
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
if let Some(path) = std::env::args().nth(1) {
let f = std::fs::read_to_string(&path).expect("Path must be valid.");
let mut parser = Parser::new(path, Lexer::new(&f));
let code: File = match parser.parse() {
Ok(f) => f,
Err(e) => {
eprintln!("{e}");
return Ok(());
}
};
CLangifier::new().p(&code);
println!();
return Ok(());
}
let mut rl = Repline::new("\x1b[33m", "cl>", "? >");
loop {
let mut line = match rl.read() {
Err(RlError::CtrlC(_)) => break,
Err(RlError::CtrlD(line)) => {
rl.deny();
line
}
Ok(line) => line,
Err(e) => Err(e)?,
};
if !line.ends_with(';') {
line.push(';');
}
let mut parser = Parser::new("stdin", Lexer::new(&line));
let code = match parser.parse::<Stmt>() {
Ok(code) => {
rl.accept();
code
}
Err(e) => {
print!("\x1b[40G\x1bJ\x1b[91m{e}\x1b[0m");
continue;
}
};
print!("\x1b[G\x1b[J");
CLangifier::new().p(&code);
println!();
}
Ok(())
}
pub use clangifier::CLangifier;
pub mod clangifier {
use crate::clangify::CLangify;
use std::{
fmt::Display,
io::Write,
ops::{Add, Deref, DerefMut},
};
#[derive(Debug, Default)]
pub struct CLangifier {
depth: usize,
}
impl CLangifier {
pub fn new() -> Self {
Self::default()
}
pub fn indent(&mut self) -> Section<'_> {
Section::new(self)
}
/// Prints a [Yamlify] value
#[inline]
pub fn p<T: CLangify + ?Sized>(&mut self, yaml: &T) -> &mut Self {
yaml.print(self);
self
}
fn increase(&mut self) {
self.depth += 1;
}
fn decrease(&mut self) {
self.depth -= 1;
}
fn print_indentation(&self, writer: &mut impl Write) {
for _ in 0..self.depth {
let _ = write!(writer, " ");
}
}
pub fn endl(&mut self) -> &mut Self {
self.p("\n")
.print_indentation(&mut std::io::stdout().lock());
self
}
/// Prints a section header and increases indentation
pub fn nest(&mut self, name: impl Display) -> Section<'_> {
print!("{name}");
self.indent()
}
}
impl<C: CLangify + ?Sized> Add<&C> for &mut CLangifier {
type Output = Self;
fn add(self, rhs: &C) -> Self::Output {
self.p(rhs)
}
}
/// Tracks the start and end of an indented block (a "section")
pub struct Section<'y> {
yamler: &'y mut CLangifier,
}
impl<'y> Section<'y> {
pub fn new(yamler: &'y mut CLangifier) -> Self {
yamler.increase();
Self { yamler }
}
}
impl Deref for Section<'_> {
type Target = CLangifier;
fn deref(&self) -> &Self::Target {
self.yamler
}
}
impl DerefMut for Section<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.yamler
}
}
impl Drop for Section<'_> {
fn drop(&mut self) {
let Self { yamler } = self;
yamler.decrease();
}
}
}
pub mod clangify {
use core::panic;
use std::iter;
use super::clangifier::CLangifier;
use cl_ast::*;
pub trait CLangify {
fn print(&self, y: &mut CLangifier);
}
impl CLangify for File {
fn print(&self, mut y: &mut CLangifier) {
let File { name, items } = self;
// TODO: turn name into include guard
y = (y + "// Generated from " + name).endl();
for (idx, item) in items.iter().enumerate() {
if idx > 0 {
y.endl().endl();
}
y.p(item);
}
y.endl();
}
}
impl CLangify for Visibility {
fn print(&self, _y: &mut CLangifier) {}
}
impl CLangify for Mutability {
fn print(&self, y: &mut CLangifier) {
if let Mutability::Not = self {
y.p("const ");
}
}
}
impl CLangify for Attrs {
fn print(&self, y: &mut CLangifier) {
let Self { meta } = self;
y.nest("Attrs").p(meta);
todo!("Attributes");
}
}
impl CLangify for Meta {
fn print(&self, y: &mut CLangifier) {
let Self { name, kind } = self;
y.nest("Meta").p(name).p(kind);
todo!("Attributes");
}
}
impl CLangify for MetaKind {
fn print(&self, y: &mut CLangifier) {
match self {
MetaKind::Plain => y,
MetaKind::Equals(value) => y.p(value),
MetaKind::Func(args) => y.p(args),
};
todo!("Attributes");
}
}
impl CLangify for Item {
fn print(&self, y: &mut CLangifier) {
let Self { span: _, attrs: _, vis, kind } = self;
y.p(vis).p(kind);
}
}
impl CLangify for ItemKind {
fn print(&self, y: &mut CLangifier) {
match self {
ItemKind::Alias(f) => y.p(f),
ItemKind::Const(f) => y.p(f),
ItemKind::Static(f) => y.p(f),
ItemKind::Module(f) => y.p(f),
ItemKind::Function(f) => y.p(f),
ItemKind::Struct(f) => y.p(f),
ItemKind::Enum(f) => y.p(f),
ItemKind::Impl(f) => y.p(f),
ItemKind::Use(f) => y.p(f),
};
}
}
impl CLangify for Generics {
fn print(&self, _y: &mut CLangifier) {
let Self { vars } = self;
if !vars.is_empty() {
panic!("C doesn't have generics, dumbass.")
}
}
}
impl CLangify for Alias {
fn print(&self, y: &mut CLangifier) {
let Self { name, from } = self;
y.p("typedef ").p(from).p(" ");
y.p(name).p("; ");
}
}
impl CLangify for Const {
fn print(&self, y: &mut CLangifier) {
let Self { name, ty, init } = self;
y.p("const ").p(ty).p(" ");
y.p(name).p(" = ").p(init);
}
}
impl CLangify for Static {
fn print(&self, y: &mut CLangifier) {
let Self { mutable, name, ty, init } = self;
y.p(mutable).p(ty).p(" ");
y.p(name).p(" = ").p(init);
}
}
impl CLangify for Module {
fn print(&self, y: &mut CLangifier) {
let Self { name, file } = self;
y.nest("// mod ").p(name).p(" {").endl();
y.p(file);
y.endl().p("// } mod ").p(name);
}
}
impl CLangify for Function {
fn print(&self, y: &mut CLangifier) {
let Self { name, gens: _, sign, bind, body } = self;
let TyFn { args, rety } = sign;
let types = match &args.kind {
TyKind::Tuple(TyTuple { types }) => types.as_slice(),
_ => panic!("Unsupported function args: {args}"),
};
let bind = match bind {
Pattern::Tuple(tup) => tup.as_slice(),
_ => panic!("Unsupported function binders: {args}"),
};
y.p(rety).p(" ").p(name).p(" (");
for (idx, (bind, ty)) in bind.iter().zip(types).enumerate() {
if idx > 0 {
y.p(", ");
}
// y.print("/* TODO: desugar pat match args */");
y.p(ty).p(" ").p(bind);
}
y.p(") ").p(body);
}
}
impl CLangify for Struct {
fn print(&self, y: &mut CLangifier) {
let Self { name, gens: _, kind } = self;
y.p("struct ").p(name).nest(" {").p(kind);
y.endl().p("}");
}
}
impl CLangify for StructKind {
fn print(&self, y: &mut CLangifier) {
match self {
StructKind::Empty => y.endl().p("char _zero_sized_t;"),
StructKind::Tuple(k) => {
for (idx, ty) in k.iter().enumerate() {
y.endl().p(ty).p(" _").p(&idx).p(";");
}
y
}
StructKind::Struct(k) => y.p(k),
};
}
}
impl CLangify for StructMember {
fn print(&self, y: &mut CLangifier) {
let Self { vis, name, ty } = self;
y.p(vis).p(ty).p(" ").p(name).p(";");
}
}
impl CLangify for Enum {
fn print(&self, y: &mut CLangifier) {
let Self { name, gens: _, variants } = self;
y.nest("enum ").p(name).p(" {").endl();
for (idx, variant) in variants.iter().enumerate() {
if idx > 0 {
y.p(",").endl();
}
y.p(variant);
}
y.endl().p("\n}");
}
}
impl CLangify for Variant {
fn print(&self, y: &mut CLangifier) {
let Self { name, kind, body } = self;
y.p(name).p(kind).p(body);
}
}
impl CLangify for Impl {
fn print(&self, y: &mut CLangifier) {
let Self { gens, target, body } = self;
y.nest("/* TODO: impl ").p(gens).p(target).p(" { */ ");
y.p(body);
y.p("/* } // impl ").p(target).p(" */ ");
}
}
impl CLangify for ImplKind {
fn print(&self, y: &mut CLangifier) {
match self {
ImplKind::Type(t) => y.p(t),
ImplKind::Trait { impl_trait, for_type } => {
todo!("impl {impl_trait} for {for_type}")
}
};
}
}
impl CLangify for Use {
fn print(&self, y: &mut CLangifier) {
let Self { absolute: _, tree } = self;
y.p(tree);
}
}
impl CLangify for UseTree {
fn print(&self, y: &mut CLangifier) {
match self {
UseTree::Tree(trees) => y.p(trees),
UseTree::Path(path, tree) => y.p("/* ").p(path).p(" */").p(tree),
UseTree::Alias(from, to) => y.p("#import <").p(from).p(">.h// ").p(to).p(" "),
UseTree::Name(name) => y.p("#import <").p(name).p(".h> "),
UseTree::Glob => y.p("/* TODO: use globbing */"),
};
}
}
impl CLangify for Block {
fn print(&self, y: &mut CLangifier) {
let Self { stmts } = self;
{
let mut y = y.nest("{");
y.endl();
if let [
stmts @ ..,
Stmt { span: _, kind: StmtKind::Expr(expr), semi: Semi::Unterminated },
] = stmts.as_slice()
{
y.p(stmts).p("return ").p(expr).p(";");
} else {
y.p(stmts);
}
}
y.endl().p("}");
}
}
impl CLangify for Stmt {
fn print(&self, y: &mut CLangifier) {
let Self { span: _, kind, semi: _ } = self;
y.p(kind).p(";").endl();
}
}
impl CLangify for Semi {
fn print(&self, y: &mut CLangifier) {
y.p(";");
}
}
impl CLangify for StmtKind {
fn print(&self, y: &mut CLangifier) {
match self {
StmtKind::Empty => y,
StmtKind::Item(s) => y.p(s),
StmtKind::Expr(s) => y.p(s),
};
}
}
impl CLangify for Expr {
fn print(&self, y: &mut CLangifier) {
let Self { span: _, kind } = self;
y.p(kind);
}
}
impl CLangify for ExprKind {
fn print(&self, y: &mut CLangifier) {
match self {
ExprKind::Closure(k) => todo!("Downgrade {k}"),
ExprKind::Quote(k) => k.print(y),
ExprKind::Let(k) => k.print(y),
ExprKind::Match(k) => k.print(y),
ExprKind::Assign(k) => k.print(y),
ExprKind::Modify(k) => k.print(y),
ExprKind::Binary(k) => k.print(y),
ExprKind::Unary(k) => k.print(y),
ExprKind::Cast(k) => k.print(y),
ExprKind::Member(k) => k.print(y),
ExprKind::Index(k) => k.print(y),
ExprKind::Structor(k) => k.print(y),
ExprKind::Path(k) => k.print(y),
ExprKind::Literal(k) => k.print(y),
ExprKind::Array(k) => k.print(y),
ExprKind::ArrayRep(k) => k.print(y),
ExprKind::AddrOf(k) => k.print(y),
ExprKind::Block(k) => k.print(y),
ExprKind::Empty => {}
ExprKind::Group(k) => k.print(y),
ExprKind::Tuple(k) => k.print(y),
ExprKind::While(k) => k.print(y),
ExprKind::If(k) => k.print(y),
ExprKind::For(k) => k.print(y),
ExprKind::Break(k) => k.print(y),
ExprKind::Return(k) => k.print(y),
ExprKind::Continue => {
y.nest("continue");
}
}
}
}
impl CLangify for Quote {
fn print(&self, y: &mut CLangifier) {
y.nest("\"");
print!("{self}");
y.p("\"");
}
}
impl CLangify for Let {
fn print(&self, y: &mut CLangifier) {
let Self { mutable, name, ty, init } = self;
let ty = ty.as_deref().map(|ty| &ty.kind).unwrap_or(&TyKind::Infer);
match ty {
TyKind::Array(TyArray { ty, count }) => {
y.p(ty).p(" ").p(mutable).p(name).p("[").p(count).p("]");
}
TyKind::Fn(TyFn { args, rety }) => {
y.nest("(").p(rety).p(" *").p(mutable).p(name).p(")(");
match &args.kind {
TyKind::Tuple(TyTuple { types }) => {
for (idx, ty) in types.iter().enumerate() {
if idx > 0 {
y.p(", ");
}
y.p(ty);
}
}
_ => {
y.p(args);
}
}
y.p(")");
}
_ => {
y.indent().p(ty).p(" ").p(mutable).p(name);
}
}
if let Some(init) = init {
y.p(" = ").p(init);
}
}
}
impl CLangify for Pattern {
fn print(&self, y: &mut CLangifier) {
// TODO: Pattern match desugaring!!!
match self {
Pattern::Name(name) => y.p(name),
Pattern::Path(path) => y.p(path),
Pattern::Literal(literal) => y.p(literal),
Pattern::Rest(name) => y.p("..").p(name),
Pattern::Ref(mutability, pattern) => y.p("&").p(mutability).p(pattern),
Pattern::RangeExc(head, tail) => y.p("RangeExc").p(head).p(tail),
Pattern::RangeInc(head, tail) => y.p("RangeExc").p(head).p(tail),
Pattern::Tuple(patterns) => y.nest("Tuple").p(patterns),
Pattern::Array(patterns) => y.nest("Array").p(patterns),
Pattern::Struct(path, items) => {
{
let mut y = y.nest("Struct");
y.p(path);
for (name, item) in items {
y.p(name).p(item);
}
}
y
}
Pattern::TupleStruct(path, items) => {
{
let mut y = y.nest("TupleStruct");
y.p(path).p(items);
}
y
}
};
}
}
impl CLangify for Match {
fn print(&self, y: &mut CLangifier) {
let Self { scrutinee, arms } = self;
y.p("/* match ").p(scrutinee);
y.nest(" { ").p(arms);
y.p(" } */");
}
}
impl CLangify for MatchArm {
fn print(&self, y: &mut CLangifier) {
let Self(pat, expr) = self;
y.p(pat).p(" => ").p(expr).p(", ");
}
}
impl CLangify for Assign {
fn print(&self, y: &mut CLangifier) {
let Self { parts } = self;
y.p(&parts.0).p(" = ").p(&parts.1);
}
}
impl CLangify for Modify {
fn print(&self, y: &mut CLangifier) {
let Self { kind, parts } = self;
y.p(&parts.0).p(kind).p(&parts.1);
}
}
impl CLangify for ModifyKind {
fn print(&self, _y: &mut CLangifier) {
print!(" {self} ");
}
}
impl CLangify for Binary {
fn print(&self, y: &mut CLangifier) {
let Self { kind, parts } = self;
match kind {
BinaryKind::Call => y.p(&parts.0).p(&parts.1),
_ => y.p("(").p(&parts.0).p(kind).p(&parts.1).p(")"),
};
}
}
impl CLangify for BinaryKind {
fn print(&self, _y: &mut CLangifier) {
print!(" {self} ");
}
}
impl CLangify for Unary {
fn print(&self, y: &mut CLangifier) {
let Self { kind, tail } = self;
match kind {
UnaryKind::Deref => y.p("*").p(tail),
UnaryKind::Neg => y.p("-").p(tail),
UnaryKind::Not => y.p("!").p(tail),
UnaryKind::RangeInc => todo!("Unary RangeInc in C"),
UnaryKind::RangeExc => todo!("Unary RangeExc in C"),
UnaryKind::Loop => y.nest("while (1) { ").p(tail).p(" }"),
UnaryKind::At => todo!(),
UnaryKind::Tilde => todo!(),
};
}
}
impl CLangify for Cast {
fn print(&self, y: &mut CLangifier) {
let Self { head, ty } = self;
y.nest("(").p(ty).p(")");
y.p(head);
}
}
impl CLangify for Member {
fn print(&self, y: &mut CLangifier) {
let Self { head, kind } = self;
match kind {
MemberKind::Call(name, Tuple { exprs }) => {
y.p(name);
y.p("(");
for (idx, expr) in iter::once(head.as_ref()).chain(exprs).enumerate() {
if idx > 0 {
y.p(", ");
}
y.p(expr);
}
y.p(")")
}
MemberKind::Struct(name) => y.p(head).p(".").p(name),
MemberKind::Tuple(idx) => y.p(head).p("._").p(idx),
};
}
}
impl CLangify for Tuple {
fn print(&self, y: &mut CLangifier) {
let Self { exprs } = self;
let mut y = y.nest("( ");
for (idx, expr) in exprs.iter().enumerate() {
if idx > 0 {
y.p(", ");
}
y.p(expr);
}
y.p(" )");
}
}
impl CLangify for Index {
fn print(&self, y: &mut CLangifier) {
let Self { head, indices } = self;
y.p(head);
for index in indices {
y.p("[").p(index).p("]");
}
}
}
impl CLangify for Structor {
fn print(&self, y: &mut CLangifier) {
let Self { to, init } = self;
y.nest("(").p(to).p(")");
{
let mut y = y.nest("{ ");
for (idx, field) in init.iter().enumerate() {
if idx > 0 {
y.p(", ");
}
y.p(field);
}
y.p(init);
}
y.p("}");
}
}
impl CLangify for Fielder {
fn print(&self, y: &mut CLangifier) {
let Self { name, init } = self;
y.p(".").p(name).p(" = ").p(init);
}
}
impl CLangify for Array {
fn print(&self, y: &mut CLangifier) {
let Self { values } = self;
{
let mut y = y.nest("{");
y.endl();
for (idx, value) in values.iter().enumerate() {
if idx > 0 {
y.p(", ");
}
y.p(value);
}
}
y.endl().p("}");
}
}
impl CLangify for ArrayRep {
fn print(&self, y: &mut CLangifier) {
let Self { value, repeat } = self;
let ExprKind::Literal(Literal::Int(repeat)) = &repeat.kind else {
eprintln!("Constant needs folding: {repeat}");
return;
};
{
let mut y = y.nest("{");
for _ in 0..*repeat {
y.endl().p(value).p(",");
}
}
y.endl().p("}");
}
}
impl CLangify for AddrOf {
fn print(&self, y: &mut CLangifier) {
let Self { mutable: _, expr } = self;
y.p("&").p(expr);
}
}
impl CLangify for Group {
fn print(&self, y: &mut CLangifier) {
let Self { expr } = self;
y.p("(").p(expr).p(")");
}
}
impl CLangify for While {
fn print(&self, y: &mut CLangifier) {
// TODO: to properly propagate intermediate values, a new temp variable needs to be
// declared on every line lmao. This will require type info.
let Self { cond, pass, fail } = self;
let Else { body: fail } = fail;
y.nest("while(1) {")
.endl()
.p("if (")
.p(cond)
.p(") ")
.p(pass);
{
let mut y = y.nest(" else {");
y.endl();
if let Some(fail) = fail {
y.p(fail).p(";").endl();
}
y.p("break;");
}
y.endl().p("}");
}
}
impl CLangify for Else {
fn print(&self, y: &mut CLangifier) {
let Self { body } = self;
if let Some(body) = body {
y.p(" else ").p(body);
}
}
}
impl CLangify for If {
fn print(&self, y: &mut CLangifier) {
let Self { cond, pass, fail } = self;
y.p("if (").p(cond).p(")");
y.p(pass).p(fail);
}
}
impl CLangify for For {
#[rustfmt::skip]
fn print(&self, y: &mut CLangifier) {
let Self { bind, cond, pass, fail: _ } = self;
let (mode, (head, tail)) = match &cond.kind {
ExprKind::Binary(Binary { kind: BinaryKind::RangeExc, parts }) => (false, &**parts),
ExprKind::Binary(Binary { kind: BinaryKind::RangeInc, parts }) => (true, &**parts),
_ => todo!("Clangify for loops"),
};
// for (int bind = head; bind mode? < : <= tail; bind++);
y.p("for ( int ").p(bind).p(" = ").p(head).p("; ");
y.p(bind).p(if mode {"<="} else {"<"}).p(tail).p("; ");
y.p("++").p(bind).p(" ) ").p(pass);
}
}
impl CLangify for Break {
fn print(&self, y: &mut CLangifier) {
let Self { body } = self;
y.nest("break ").p(body);
}
}
impl CLangify for Return {
fn print(&self, y: &mut CLangifier) {
let Self { body } = self;
y.nest("return ").p(body);
}
}
impl CLangify for Literal {
fn print(&self, y: &mut CLangifier) {
match self {
Literal::Float(l) => y.p(l),
Literal::Bool(l) => y.p(l),
Literal::Int(l) => y.p(l),
Literal::Char(l) => y.p("'").p(l).p("'"),
Literal::String(l) => y.p(&'"').p(l).p(&'"'),
};
}
}
impl CLangify for Sym {
fn print(&self, y: &mut CLangifier) {
y.p(self.to_ref());
}
}
impl CLangify for Ty {
fn print(&self, y: &mut CLangifier) {
let Self { span: _, kind, gens: _ } = self;
y.p(kind);
}
}
impl CLangify for TyKind {
fn print(&self, y: &mut CLangifier) {
match self {
TyKind::Never => y.p("Never"),
TyKind::Infer => y.p("auto"),
TyKind::Path(t) => y.p(t),
TyKind::Tuple(t) => y.p(t),
TyKind::Ref(t) => y.p(t),
TyKind::Ptr(t) => y.p(t),
TyKind::Fn(t) => y.p(t),
TyKind::Slice(t) => y.p(t),
TyKind::Array(t) => y.p(t),
};
}
}
impl CLangify for Path {
fn print(&self, y: &mut CLangifier) {
let Self { absolute: _, parts } = self;
for (idx, part) in parts.iter().enumerate() {
if idx > 0 {
y.p("_");
}
y.p(part);
}
}
}
impl CLangify for PathPart {
fn print(&self, y: &mut CLangifier) {
match self {
PathPart::SuperKw => y.p("super"),
PathPart::SelfTy => y.p("Self"),
PathPart::Ident(i) => y.p(i),
};
}
}
impl CLangify for TyArray {
fn print(&self, y: &mut CLangifier) {
let Self { ty, count } = self;
y.p(ty).p("[").p(count).p("]");
}
}
impl CLangify for TySlice {
fn print(&self, y: &mut CLangifier) {
let Self { ty } = self;
y.p(ty).p("* ");
}
}
impl CLangify for TyTuple {
fn print(&self, y: &mut CLangifier) {
let Self { types } = self;
{
let mut y = y.nest("struct {");
y.endl();
for (idx, ty) in types.iter().enumerate() {
if idx > 0 {
y.p(",").endl();
}
y.p(ty);
}
}
y.endl().p("}");
}
}
impl CLangify for TyRef {
fn print(&self, y: &mut CLangifier) {
let Self { count, mutable, to } = self;
y.p(mutable).p(to);
for _ in 0..*count {
y.p("*");
}
}
}
impl CLangify for TyPtr {
fn print(&self, y: &mut CLangifier) {
let Self { to } = self;
y.p(to).p("*");
}
}
impl CLangify for TyFn {
fn print(&self, y: &mut CLangifier) {
let Self { args, rety } = self;
// TODO: function pointer syntax
y.nest("(").p(rety).p(" *)(");
match &args.kind {
TyKind::Tuple(TyTuple { types }) => {
for (idx, ty) in types.iter().enumerate() {
if idx > 0 {
y.p(", ");
}
y.p(ty);
}
y
}
_ => y.p(args),
}
.p(")");
}
}
impl<T: CLangify> CLangify for Option<T> {
fn print(&self, y: &mut CLangifier) {
if let Some(v) = self {
y.p(v);
}
}
}
impl<T: CLangify> CLangify for Box<T> {
fn print(&self, y: &mut CLangifier) {
y.p(&**self);
}
}
impl<T: CLangify> CLangify for Vec<T> {
fn print(&self, y: &mut CLangifier) {
for thing in self {
y.p(thing);
}
}
}
impl<T: CLangify> CLangify for [T] {
fn print(&self, y: &mut CLangifier) {
for thing in self {
y.p(thing);
}
}
}
impl CLangify for () {
fn print(&self, _y: &mut CLangifier) {
// TODO: C has no language support for zst
}
}
impl<T: CLangify> CLangify for &T {
fn print(&self, y: &mut CLangifier) {
(*self).print(y)
}
}
impl CLangify for std::fmt::Arguments<'_> {
fn print(&self, _y: &mut CLangifier) {
print!("{self}")
}
}
macro_rules! scalar {
($($t:ty),*$(,)?) => {
$(impl CLangify for $t {
fn print(&self, _y: &mut CLangifier) {
print!("{self}");
}
})*
};
}
scalar! {
bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, str, &str, String
}
}

View File

@@ -1,8 +1,9 @@
//! Pretty prints a conlang AST in yaml
use cl_ast::Stmt;
use cl_lexer::Lexer;
use cl_parser::Parser;
use repline::{error::Error as RlError, Repline};
use repline::{Repline, error::Error as RlError};
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
@@ -18,8 +19,8 @@ fn main() -> Result<(), Box<dyn Error>> {
Err(e) => Err(e)?,
};
let mut parser = Parser::new(Lexer::new(&line));
let code = match parser.stmt() {
let mut parser = Parser::new("", Lexer::new(&line));
let code = match parser.parse::<Stmt>() {
Ok(code) => {
rl.accept();
code
@@ -40,7 +41,6 @@ pub use yamler::Yamler;
pub mod yamler {
use crate::yamlify::Yamlify;
use std::{
fmt::Display,
io::Write,
ops::{Deref, DerefMut},
};
@@ -54,7 +54,7 @@ pub mod yamler {
Self::default()
}
pub fn indent(&mut self) -> Section {
pub fn indent(&mut self) -> Section<'_> {
Section::new(self)
}
@@ -80,28 +80,33 @@ pub mod yamler {
}
/// Prints a section header and increases indentation
pub fn key(&mut self, name: impl Display) -> Section {
pub fn key(&mut self, name: impl Yamlify) -> Section<'_> {
println!();
self.print_indentation(&mut std::io::stdout().lock());
print!("- {name}:");
print!(" ");
name.yaml(self);
print!(":");
self.indent()
}
/// Prints a yaml key value pair: `- name: "value"`
pub fn pair<D: Display, T: Yamlify>(&mut self, name: D, value: T) -> &mut Self {
self.key(name).yaml(&value);
pub fn pair<D: Yamlify, T: Yamlify>(&mut self, name: D, value: T) -> &mut Self {
self.key(name).value(value);
self
}
/// Prints a yaml scalar value: `"name"``
pub fn value<D: Display>(&mut self, value: D) -> &mut Self {
print!(" {value}");
pub fn value<D: Yamlify>(&mut self, value: D) -> &mut Self {
print!(" ");
value.yaml(self);
self
}
pub fn list<D: Yamlify>(&mut self, list: &[D]) -> &mut Self {
for (idx, value) in list.iter().enumerate() {
self.pair(idx, value);
for value in list {
println!();
self.print_indentation(&mut std::io::stdout().lock());
self.yaml(&"- ").yaml(value);
}
self
}
@@ -119,19 +124,19 @@ pub mod yamler {
}
}
impl<'y> Deref for Section<'y> {
impl Deref for Section<'_> {
type Target = Yamler;
fn deref(&self) -> &Self::Target {
self.yamler
}
}
impl<'y> DerefMut for Section<'y> {
impl DerefMut for Section<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.yamler
}
}
impl<'y> Drop for Section<'y> {
impl Drop for Section<'_> {
fn drop(&mut self) {
let Self { yamler } = self;
yamler.decrease();
@@ -149,8 +154,8 @@ pub mod yamlify {
impl Yamlify for File {
fn yaml(&self, y: &mut Yamler) {
let File { items } = self;
y.key("File").yaml(items);
let File { name, items } = self;
y.key("File").pair("name", name).yaml(items);
}
}
impl Yamlify for Visibility {
@@ -192,7 +197,7 @@ pub mod yamlify {
impl Yamlify for Item {
fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, attrs, vis, kind } = self;
let Self { span: _, attrs, vis, kind } = self;
y.key("Item").yaml(attrs).yaml(vis).yaml(kind);
}
}
@@ -211,10 +216,16 @@ pub mod yamlify {
};
}
}
impl Yamlify for Generics {
fn yaml(&self, y: &mut Yamler) {
let Self { vars } = self;
y.key("Generics").value(vars);
}
}
impl Yamlify for Alias {
fn yaml(&self, y: &mut Yamler) {
let Self { to, from } = self;
y.key("Alias").pair("to", to).pair("from", from);
let Self { name, from } = self;
y.key("Alias").pair("to", name).pair("from", from);
}
}
impl Yamlify for Const {
@@ -234,23 +245,16 @@ pub mod yamlify {
}
impl Yamlify for Module {
fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self;
y.key("Module").pair("name", name).yaml(kind);
}
}
impl Yamlify for ModuleKind {
fn yaml(&self, y: &mut Yamler) {
match self {
ModuleKind::Inline(f) => y.yaml(f),
ModuleKind::Outline => y,
};
let Self { name, file } = self;
y.key("Module").pair("name", name).yaml(file);
}
}
impl Yamlify for Function {
fn yaml(&self, y: &mut Yamler) {
let Self { name, sign, bind, body } = self;
let Self { name, gens, sign, bind, body } = self;
y.key("Function")
.pair("name", name)
.pair("gens", gens)
.pair("sign", sign)
.pair("bind", bind)
.pair("body", body);
@@ -258,8 +262,11 @@ pub mod yamlify {
}
impl Yamlify for Struct {
fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self;
y.key("Struct").pair("name", name).yaml(kind);
let Self { name, gens, kind } = self;
y.key("Struct")
.pair("gens", gens)
.pair("name", name)
.yaml(kind);
}
}
impl Yamlify for StructKind {
@@ -279,38 +286,29 @@ pub mod yamlify {
}
impl Yamlify for Enum {
fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self;
y.key("Enum").pair("name", name).yaml(kind);
}
}
impl Yamlify for EnumKind {
fn yaml(&self, y: &mut Yamler) {
match self {
EnumKind::NoVariants => y,
EnumKind::Variants(v) => y.yaml(v),
};
let Self { name, gens, variants: kind } = self;
y.key("Enum")
.pair("gens", gens)
.pair("name", name)
.yaml(kind);
}
}
impl Yamlify for Variant {
fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self;
y.key("Variant").pair("name", name).yaml(kind);
}
}
impl Yamlify for VariantKind {
fn yaml(&self, y: &mut Yamler) {
match self {
VariantKind::Plain => y,
VariantKind::CLike(v) => y.yaml(v),
VariantKind::Tuple(v) => y.yaml(v),
VariantKind::Struct(v) => y.yaml(v),
};
let Self { name, kind, body } = self;
y.key("Variant")
.pair("name", name)
.pair("kind", kind)
.pair("body", body);
}
}
impl Yamlify for Impl {
fn yaml(&self, y: &mut Yamler) {
let Self { target, body } = self;
y.key("Impl").pair("target", target).pair("body", body);
let Self { gens, target, body } = self;
y.key("Impl")
.pair("gens", gens)
.pair("target", target)
.pair("body", body);
}
}
impl Yamlify for ImplKind {
@@ -348,8 +346,8 @@ pub mod yamlify {
}
impl Yamlify for Stmt {
fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind, semi } = self;
y.key("Stmt").yaml(kind).yaml(semi);
let Self { span: _, kind, semi } = self;
y.key("Stmt").value(kind).yaml(semi);
}
}
impl Yamlify for Semi {
@@ -363,35 +361,29 @@ pub mod yamlify {
fn yaml(&self, y: &mut Yamler) {
match self {
StmtKind::Empty => y,
StmtKind::Local(s) => y.yaml(s),
StmtKind::Item(s) => y.yaml(s),
StmtKind::Expr(s) => y.yaml(s),
};
}
}
impl Yamlify for Let {
fn yaml(&self, y: &mut Yamler) {
let Self { mutable, name, ty, init } = self;
y.key("Let")
.pair("name", name)
.yaml(mutable)
.pair("ty", ty)
.pair("init", init);
}
}
impl Yamlify for Expr {
fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind } = self;
let Self { span: _, kind } = self;
y.yaml(kind);
}
}
impl Yamlify for ExprKind {
fn yaml(&self, y: &mut Yamler) {
match self {
ExprKind::Closure(k) => k.yaml(y),
ExprKind::Quote(k) => k.yaml(y),
ExprKind::Let(k) => k.yaml(y),
ExprKind::Match(k) => k.yaml(y),
ExprKind::Assign(k) => k.yaml(y),
ExprKind::Modify(k) => k.yaml(y),
ExprKind::Binary(k) => k.yaml(y),
ExprKind::Unary(k) => k.yaml(y),
ExprKind::Cast(k) => k.yaml(y),
ExprKind::Member(k) => k.yaml(y),
ExprKind::Index(k) => k.yaml(y),
ExprKind::Structor(k) => k.yaml(y),
@@ -404,16 +396,89 @@ pub mod yamlify {
ExprKind::Empty => {}
ExprKind::Group(k) => k.yaml(y),
ExprKind::Tuple(k) => k.yaml(y),
ExprKind::Loop(k) => k.yaml(y),
ExprKind::While(k) => k.yaml(y),
ExprKind::If(k) => k.yaml(y),
ExprKind::For(k) => k.yaml(y),
ExprKind::Break(k) => k.yaml(y),
ExprKind::Return(k) => k.yaml(y),
ExprKind::Continue(k) => k.yaml(y),
ExprKind::Continue => {
y.key("Continue");
}
}
}
}
impl Yamlify for Closure {
fn yaml(&self, y: &mut Yamler) {
let Self { arg, body } = self;
y.key("Closure").pair("arg", arg).pair("body", body);
}
}
impl Yamlify for Quote {
fn yaml(&self, y: &mut Yamler) {
y.key("Quote").value(self);
}
}
impl Yamlify for Let {
fn yaml(&self, y: &mut Yamler) {
let Self { mutable, name, ty, init } = self;
y.key("Let")
.pair("name", name)
.yaml(mutable)
.pair("ty", ty)
.pair("init", init);
}
}
impl Yamlify for Pattern {
fn yaml(&self, y: &mut Yamler) {
match self {
Pattern::Name(name) => y.value(name),
Pattern::Path(path) => y.value(path),
Pattern::Literal(literal) => y.value(literal),
Pattern::Rest(name) => y.pair("Rest", name),
Pattern::Ref(mutability, pattern) => y.yaml(mutability).pair("Pat", pattern),
Pattern::RangeInc(head, tail) => {
y.key("RangeInc").pair("head", head).pair("tail", tail);
y
}
Pattern::RangeExc(head, tail) => {
y.key("RangeExc").pair("head", head).pair("tail", tail);
y
}
Pattern::Tuple(patterns) => y.key("Tuple").list(patterns),
Pattern::Array(patterns) => y.key("Array").list(patterns),
Pattern::Struct(path, items) => {
{
let mut y = y.key("Struct");
y.yaml(path);
for (name, item) in items {
y.pair(name, item);
}
}
y
}
Pattern::TupleStruct(path, items) => {
y.key("TupleStruct").yaml(path).list(items);
y
}
};
}
}
impl Yamlify for Match {
fn yaml(&self, y: &mut Yamler) {
let Self { scrutinee, arms } = self;
y.key("Match")
.pair("scrutinee", scrutinee)
.pair("arms", arms);
}
}
impl Yamlify for MatchArm {
fn yaml(&self, y: &mut Yamler) {
let Self(pat, expr) = self;
y.pair("pat", pat).pair("expr", expr);
}
}
impl Yamlify for Assign {
fn yaml(&self, y: &mut Yamler) {
let Self { parts } = self;
@@ -431,11 +496,6 @@ pub mod yamlify {
.pair("tail", &parts.1);
}
}
impl Yamlify for ModifyKind {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
}
impl Yamlify for Binary {
fn yaml(&self, y: &mut Yamler) {
let Self { kind, parts } = self;
@@ -445,20 +505,16 @@ pub mod yamlify {
.pair("tail", &parts.1);
}
}
impl Yamlify for BinaryKind {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
}
impl Yamlify for Unary {
fn yaml(&self, y: &mut Yamler) {
let Self { kind, tail } = self;
y.key("Unary").pair("kind", kind).pair("tail", tail);
}
}
impl Yamlify for UnaryKind {
impl Yamlify for Cast {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
let Self { head, ty } = self;
y.key("Cast").pair("head", head).pair("ty", ty);
}
}
impl Yamlify for Member {
@@ -485,7 +541,10 @@ pub mod yamlify {
impl Yamlify for Index {
fn yaml(&self, y: &mut Yamler) {
let Self { head, indices } = self;
y.key("Index").pair("head", head).list(indices);
y.key("Index")
.pair("head", head)
.key("indices")
.list(indices);
}
}
impl Yamlify for Structor {
@@ -516,11 +575,8 @@ pub mod yamlify {
}
impl Yamlify for AddrOf {
fn yaml(&self, y: &mut Yamler) {
let Self { count, mutable, expr } = self;
y.key("AddrOf")
.pair("count", count)
.yaml(mutable)
.pair("expr", expr);
let Self { mutable, expr } = self;
y.key("AddrOf").yaml(mutable).pair("expr", expr);
}
}
impl Yamlify for Group {
@@ -529,12 +585,6 @@ pub mod yamlify {
y.key("Group").yaml(expr);
}
}
impl Yamlify for Loop {
fn yaml(&self, y: &mut Yamler) {
let Self { body } = self;
y.key("Loop").yaml(body);
}
}
impl Yamlify for While {
fn yaml(&self, y: &mut Yamler) {
let Self { cond, pass, fail } = self;
@@ -547,7 +597,7 @@ pub mod yamlify {
impl Yamlify for Else {
fn yaml(&self, y: &mut Yamler) {
let Self { body } = self;
y.key("Else").yaml(body);
y.key("fail").yaml(body);
}
}
impl Yamlify for If {
@@ -578,44 +628,35 @@ pub mod yamlify {
y.key("Return").yaml(body);
}
}
impl Yamlify for Continue {
fn yaml(&self, y: &mut Yamler) {
y.key("Continue");
}
}
impl Yamlify for Literal {
fn yaml(&self, y: &mut Yamler) {
y.value(format_args!("\"{self}\""));
fn yaml(&self, _y: &mut Yamler) {
match self {
Literal::Bool(v) => print!("{v}"),
Literal::Char(v) => print!("'{}'", v.escape_debug()),
Literal::Int(v) => print!("{v}"),
Literal::Float(v) => print!("{v}"),
Literal::String(v) => print!("{}", v.escape_debug()),
}
}
impl Yamlify for Sym {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
}
impl Yamlify for Param {
fn yaml(&self, y: &mut Yamler) {
let Self { mutability, name } = self;
y.key("Param").yaml(mutability).pair("name", name);
}
}
impl Yamlify for Ty {
fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind } = self;
y.key("Ty").yaml(kind);
let Self { span: _, kind, gens } = self;
y.key("Ty").yaml(kind).yaml(gens);
}
}
impl Yamlify for TyKind {
fn yaml(&self, y: &mut Yamler) {
match self {
TyKind::Never => y.value("Never"),
TyKind::Empty => y.value("Empty"),
TyKind::Infer => y.value("_"),
TyKind::Path(t) => y.yaml(t),
TyKind::Tuple(t) => y.yaml(t),
TyKind::Ref(t) => y.yaml(t),
TyKind::Ptr(t) => y.yaml(t),
TyKind::Fn(t) => y.yaml(t),
TyKind::Slice(_) => todo!(),
TyKind::Array(_) => todo!(),
TyKind::Slice(t) => y.yaml(t),
TyKind::Array(t) => y.yaml(t),
};
}
}
@@ -626,16 +667,13 @@ pub mod yamlify {
if *absolute {
y.pair("absolute", absolute);
}
for part in parts {
y.pair("part", part);
}
y.yaml(parts);
}
}
impl Yamlify for PathPart {
fn yaml(&self, y: &mut Yamler) {
match self {
PathPart::SuperKw => y.value("super"),
PathPart::SelfKw => y.value("self"),
PathPart::SelfTy => y.value("Self"),
PathPart::Ident(i) => y.yaml(i),
};
@@ -671,6 +709,12 @@ pub mod yamlify {
.pair("to", to);
}
}
impl Yamlify for TyPtr {
fn yaml(&self, y: &mut Yamler) {
let Self { to } = self;
y.key("TyPtr").pair("to", to);
}
}
impl Yamlify for TyFn {
fn yaml(&self, y: &mut Yamler) {
let Self { args, rety } = self;
@@ -694,9 +738,7 @@ pub mod yamlify {
}
impl<T: Yamlify> Yamlify for Vec<T> {
fn yaml(&self, y: &mut Yamler) {
for thing in self {
y.yaml(thing);
}
y.list(self);
}
}
impl Yamlify for () {
@@ -712,14 +754,15 @@ pub mod yamlify {
macro_rules! scalar {
($($t:ty),*$(,)?) => {
$(impl Yamlify for $t {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
fn yaml(&self, _y: &mut Yamler) {
print!("{self}");
}
})*
};
}
scalar! {
bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, &str, String
bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, &str, String,
BinaryKind, UnaryKind, ModifyKind, Sym,
}
}

View File

@@ -11,4 +11,4 @@ pub const RESET: &str = "\x1b[0m";
pub const OUTPUT: &str = "\x1b[38;5;117m";
pub const CLEAR_LINES: &str = "\x1b[G\x1b[J";
pub const CLEAR_ALL: &str = "\x1b[H\x1b[2J";
pub const CLEAR_ALL: &str = "\x1b[H\x1b[2J\x1b[3J";

View File

@@ -35,6 +35,10 @@ argwerk::define! {
[#[option] path] if file.is_none() => {
file = path.map(Into::into);
}
[path] if file.is_some() => {
include.push(path.into());
}
}
/// gets whether stdin AND stdout are a terminal, for pipelining
@@ -45,10 +49,9 @@ pub fn is_terminal() -> bool {
/// The CLI's operating mode
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum Mode {
#[default]
Menu,
Lex,
Fmt,
#[default]
Run,
}

View File

@@ -5,51 +5,112 @@ use crate::{
menu,
tools::print_token,
};
use cl_interpret::{convalue::ConValue, env::Environment, interpret::Interpret};
use cl_ast::File;
use cl_interpret::{builtin::builtins, convalue::ConValue, env::Environment, interpret::Interpret};
use cl_lexer::Lexer;
use cl_parser::Parser;
use std::{error::Error, path::Path};
use std::{borrow::Cow, error::Error, path::Path};
/// Run the command line interface
pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
let Args { file, include, mode, repl } = args;
let mut env = Environment::new();
env.add_builtins(&builtins! {
/// Lexes, parses, and evaluates an expression in the current env
fn eval(string) @env {
use cl_interpret::error::Error;
let string = match string {
ConValue::Str(string) => string.to_ref(),
ConValue::String(string) => string.as_str(),
ConValue::Ref(v) => {
let string = env.get_id(*v).cloned().unwrap_or_default();
return eval(env, &[string])
}
_ => Err(Error::TypeError())?
};
match Parser::new("eval", Lexer::new(string)).parse::<cl_ast::Stmt>() {
Err(e) => Ok(ConValue::Str(format!("{e}").into())),
Ok(v) => v.interpret(env),
}
}
/// Executes a file
fn import(path) @env {
use cl_interpret::error::Error;
match path {
ConValue::Str(path) => load_file(env, &**path).or(Ok(ConValue::Empty)),
ConValue::String(path) => load_file(env, &**path).or(Ok(ConValue::Empty)),
_ => Err(Error::TypeError())
}
}
fn putchar(ConValue::Char(c)) {
print!("{c}");
Ok(ConValue::Empty)
}
/// Gets a line of input from stdin
fn get_line(prompt) {
use cl_interpret::error::Error;
let prompt = match prompt {
ConValue::Str(prompt) => prompt.to_ref(),
ConValue::String(prompt) => prompt.as_str(),
_ => Err(Error::TypeError())?,
};
match repline::Repline::new("", prompt, "").read() {
Ok(line) => Ok(ConValue::String(line)),
Err(repline::Error::CtrlD(line)) => Ok(ConValue::String(line)),
Err(repline::Error::CtrlC(_)) => Err(cl_interpret::error::Error::Break(ConValue::Empty)),
Err(e) => Ok(ConValue::Str(e.to_string().into())),
}
}
});
for path in include {
load_file(&mut env, path)?;
}
if repl {
if let Some(file) = file {
load_file(&mut env, file)?;
if let Some(file) = file
&& let Err(e) = load_file(&mut env, file)
{
eprintln!("{e}")
}
let mut ctx = Context::with_env(env);
match mode {
Mode::Menu => menu::main_menu(&mut ctx)?,
Mode::Lex => menu::lex(&mut ctx)?,
Mode::Fmt => menu::fmt(&mut ctx)?,
Mode::Run => menu::run(&mut ctx)?,
}
menu::main_menu(mode, &mut ctx)?;
} else {
let path = format_path_for_display(file.as_deref());
let code = match &file {
Some(file) => std::fs::read_to_string(file)?,
None => std::io::read_to_string(std::io::stdin())?,
};
match mode {
Mode::Lex => lex_code(&code, file),
Mode::Fmt => fmt_code(&code),
Mode::Run | Mode::Menu => run_code(&code, &mut env),
Mode::Lex => lex_code(&path, &code),
Mode::Fmt => fmt_code(&path, &code),
Mode::Run => run_code(&path, &code, &mut env),
}?;
}
Ok(())
}
fn format_path_for_display(path: Option<&Path>) -> Cow<'_, str> {
match path {
Some(file) => file
.to_str()
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(file.display().to_string())),
None => Cow::Borrowed(""),
}
}
fn load_file(env: &mut Environment, path: impl AsRef<Path>) -> Result<ConValue, Box<dyn Error>> {
let inliner =
cl_parser::inliner::ModuleInliner::new(path.as_ref().parent().unwrap_or(Path::new("")));
let path = path.as_ref();
let inliner = cl_parser::inliner::ModuleInliner::new(path.with_extension(""));
let file = std::fs::read_to_string(path)?;
let code = Parser::new(Lexer::new(&file)).file()?;
let code = Parser::new(path.display().to_string(), Lexer::new(&file)).parse()?;
let code = match inliner.inline(code) {
Ok(a) => a,
Err((code, io_errs, parse_errs)) => {
@@ -62,13 +123,22 @@ fn load_file(env: &mut Environment, path: impl AsRef<Path>) -> Result<ConValue,
code
}
};
Ok(env.eval(&code)?)
use cl_ast::WeightOf;
eprintln!("File {} weighs {} units", code.name, code.weight_of());
match env.eval(&code) {
Ok(v) => Ok(v),
Err(e) => {
eprintln!("{e}");
Ok(ConValue::Empty)
}
}
}
fn lex_code(code: &str, path: Option<impl AsRef<Path>>) -> Result<(), Box<dyn Error>> {
fn lex_code(path: &str, code: &str) -> Result<(), Box<dyn Error>> {
for token in Lexer::new(code) {
if let Some(path) = &path {
print!("{}:", path.as_ref().display());
if !path.is_empty() {
print!("{}:", path);
}
match token {
Ok(token) => print_token(&token),
@@ -78,22 +148,23 @@ fn lex_code(code: &str, path: Option<impl AsRef<Path>>) -> Result<(), Box<dyn Er
Ok(())
}
fn fmt_code(code: &str) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).file()?;
fn fmt_code(path: &str, code: &str) -> Result<(), Box<dyn Error>> {
let code = Parser::new(path, Lexer::new(code)).parse::<File>()?;
println!("{code}");
Ok(())
}
fn run_code(code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).file()?;
fn run_code(path: &str, code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
let code = Parser::new(path, Lexer::new(code)).parse::<File>()?;
match code.interpret(env)? {
ConValue::Empty => {}
ret => println!("{ret}"),
}
if env.get("main".into()).is_ok() {
match env.call("main".into(), &[])? {
ConValue::Empty => {}
ret => println!("{ret}"),
match env.call("main".into(), &[]) {
Ok(ConValue::Empty) => {}
Ok(ret) => println!("{ret}"),
Err(e) => println!("Error: {e}"),
}
}
Ok(())

View File

@@ -1,6 +1,4 @@
use cl_interpret::{
env::Environment, error::IResult, interpret::Interpret, convalue::ConValue,
};
use cl_interpret::{convalue::ConValue, env::Environment, error::IResult, interpret::Interpret};
#[derive(Clone, Debug)]
pub struct Context {

View File

@@ -1,10 +1,14 @@
use crate::{ansi, ctx};
use std::error::Error;
use crate::{ansi, args::Mode, ctx};
use cl_ast::Stmt;
use cl_interpret::convalue::ConValue;
use cl_lexer::Lexer;
use cl_parser::Parser;
use repline::{error::ReplResult, prebaked::*};
use repline::{Error as RlError, error::ReplResult, prebaked::*};
fn clear() {
println!("{}", ansi::CLEAR_ALL);
pub fn clear() {
print!("{}", ansi::CLEAR_ALL);
banner()
}
@@ -12,50 +16,94 @@ pub fn banner() {
println!("--- conlang v{} 💪🦈 ---", env!("CARGO_PKG_VERSION"))
}
/// Presents a selection interface to the user
pub fn main_menu(ctx: &mut ctx::Context) -> ReplResult<()> {
banner();
run(ctx)?;
read_and(ansi::GREEN, "mu>", " ?>", |line| {
match line.trim() {
"clear" => clear(),
"l" | "lex" => lex(ctx)?,
"f" | "fmt" => fmt(ctx)?,
"r" | "run" => run(ctx)?,
"q" | "quit" => return Ok(Response::Break),
"h" | "help" => println!(
"Valid commands
lex (l): Spin up a lexer, and lex some lines
fmt (f): Format the input
run (r): Enter the REPL, and evaluate some statements
help (h): Print this list
quit (q): Exit the program"
),
_ => Err("Unknown command. Type \"help\" for help")?,
type ReplCallback = fn(&mut ctx::Context, &str) -> Result<Response, Box<dyn Error>>;
type ReplMode = (&'static str, &'static str, &'static str, ReplCallback);
#[rustfmt::skip]
const MODES: &[ReplMode] = &[
(ansi::CYAN, ".>", " >", mode_run),
(ansi::BRIGHT_BLUE, "l>", " >", mode_lex),
(ansi::BRIGHT_MAGENTA, "f>", " >", mode_fmt),
];
const fn get_mode(mode: Mode) -> ReplMode {
match mode {
Mode::Lex => MODES[1],
Mode::Fmt => MODES[2],
Mode::Run => MODES[0],
}
Ok(Response::Accept)
})
}
pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
/// Presents a selection interface to the user
pub fn main_menu(mode: Mode, ctx: &mut ctx::Context) -> ReplResult<()> {
let mut mode = get_mode(mode);
banner();
let mut rl = repline::Repline::new(mode.0, mode.1, mode.2);
loop {
rl.set_prompt(mode.0, mode.1, mode.2);
let line = match rl.read() {
Err(RlError::CtrlC(_)) => return Ok(()),
Err(RlError::CtrlD(line)) => {
rl.deny();
line
}
Ok(line) => line,
Err(e) => Err(e)?,
};
print!("\x1b[G\x1b[J");
match line.trim() {
"" => continue,
"clear" => clear(),
"mode run" => mode = get_mode(Mode::Run),
"mode lex" => mode = get_mode(Mode::Lex),
"mode fmt" => mode = get_mode(Mode::Fmt),
"quit" => return Ok(()),
"help" => println!(
"Valid commands
help : Print this list
clear : Clear the screen
quit : Exit the program
mode lex : Lex the input
mode fmt : Format the input
mode run : Evaluate some expressions"
),
_ => match mode.3(ctx, &line) {
Ok(Response::Accept) => {
rl.accept();
continue;
}
Ok(Response::Deny) => {}
Ok(Response::Break) => return Ok(()),
Ok(Response::Continue) => continue,
Err(e) => rl.print_inline(format_args!("\t\x1b[91m{e}\x1b[0m"))?,
},
}
rl.deny();
}
}
pub fn mode_run(ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
use cl_ast::ast_visitor::Fold;
use cl_parser::inliner::ModuleInliner;
read_and(ansi::CYAN, "cl>", " ?>", |line| {
let code = Parser::new(Lexer::new(line)).stmt()?;
if line.trim().is_empty() {
return Ok(Response::Deny);
}
let code = Parser::new("", Lexer::new(line)).parse::<Stmt>()?;
let code = ModuleInliner::new(".").fold_stmt(code);
print!("{}", ansi::OUTPUT);
match ctx.run(&code) {
Ok(ConValue::Empty) => print!("{}", ansi::RESET),
Ok(v) => println!("{}{v}", ansi::RESET),
Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET),
}
Ok(Response::Accept)
})
}
pub fn lex(_ctx: &mut ctx::Context) -> ReplResult<()> {
read_and(ansi::BRIGHT_BLUE, "lx>", " ?>", |line| {
pub fn mode_lex(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
for token in Lexer::new(line) {
match token {
Ok(token) => crate::tools::print_token(&token),
@@ -64,18 +112,15 @@ pub fn lex(_ctx: &mut ctx::Context) -> ReplResult<()> {
}
Ok(Response::Accept)
})
}
pub fn fmt(_ctx: &mut ctx::Context) -> ReplResult<()> {
read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| {
let mut p = Parser::new(Lexer::new(line));
pub fn mode_fmt(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
let mut p = Parser::new("", Lexer::new(line));
match p.stmt() {
match p.parse::<Stmt>() {
Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET),
Err(e) => Err(e)?,
}
Ok(Response::Accept)
})
}

View File

@@ -8,4 +8,4 @@ license.workspace = true
publish.workspace = true
[dependencies]
cl-arena = { path = "../cl-arena" }
cl-arena = { version = "0", registry = "soft-fish" }

View File

@@ -61,8 +61,10 @@ macro_rules! make_index {($($(#[$meta:meta])* $name:ident),*$(,)?) => {$(
)*}}
use self::iter::MapIndexIter;
use core::slice::GetManyMutError;
use std::ops::{Index, IndexMut};
use std::{
ops::{Index, IndexMut},
slice::GetDisjointMutError,
};
pub use make_index;
@@ -103,11 +105,11 @@ impl<V, K: MapIndex> IndexMap<K, V> {
/// Returns mutable references to many indices at once.
///
/// Returns an error if any index is out of bounds, or if the same index was passed twice.
pub fn get_many_mut<const N: usize>(
pub fn get_disjoint_mut<const N: usize>(
&mut self,
indices: [K; N],
) -> Result<[&mut V; N], GetManyMutError<N>> {
self.map.get_many_mut(indices.map(|id| id.get()))
) -> Result<[&mut V; N], GetDisjointMutError> {
self.map.get_disjoint_mut(indices.map(|id| id.get()))
}
/// Returns an iterator over the IndexMap.

View File

@@ -35,13 +35,17 @@ pub mod interned {
pub fn as_ptr(interned: &Self) -> *const T {
interned.value
}
/// Gets the internal value as a reference with the interner's lifetime
pub fn to_ref(&self) -> &'a T {
self.value
}
}
impl<'a, T: ?Sized + Debug> Debug for Interned<'a, T> {
impl<T: ?Sized + Debug> Debug for Interned<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Interned")
.field("value", &self.value)
.finish()
write!(f, "~")?;
self.value.fmt(f)
}
}
impl<'a, T: ?Sized> Interned<'a, T> {
@@ -49,14 +53,14 @@ pub mod interned {
Self { value }
}
}
impl<'a, T: ?Sized> Deref for Interned<'a, T> {
impl<T: ?Sized> Deref for Interned<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<'a, T: ?Sized> Copy for Interned<'a, T> {}
impl<'a, T: ?Sized> Clone for Interned<'a, T> {
impl<T: ?Sized> Copy for Interned<'_, T> {}
impl<T: ?Sized> Clone for Interned<'_, T> {
fn clone(&self) -> Self {
*self
}
@@ -79,13 +83,13 @@ pub mod interned {
// }
// }
impl<'a, T: ?Sized> Eq for Interned<'a, T> {}
impl<'a, T: ?Sized> PartialEq for Interned<'a, T> {
impl<T: ?Sized> Eq for Interned<'_, T> {}
impl<T: ?Sized> PartialEq for Interned<'_, T> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.value, other.value)
}
}
impl<'a, T: ?Sized> Hash for Interned<'a, T> {
impl<T: ?Sized> Hash for Interned<'_, T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
Self::as_ptr(self).hash(state)
}
@@ -119,6 +123,7 @@ pub mod string_interner {
};
/// A string interner hands out [Interned] copies of each unique string given to it.
#[derive(Default)]
pub struct StringInterner<'a> {
arena: DroplessArena<'a>,
keys: RwLock<HashSet<&'a str>>,
@@ -185,12 +190,30 @@ pub mod string_interner {
}
}
impl std::fmt::Display for StringInterner<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Ok(keys) = self.keys.read() else {
return write!(f, "Could not lock StringInterner key map.");
};
let mut keys: Vec<_> = keys.iter().collect();
keys.sort();
writeln!(f, "Keys:")?;
for (idx, key) in keys.iter().enumerate() {
writeln!(f, "{idx}:\t\"{key}\"")?
}
writeln!(f, "Count: {}", keys.len())?;
Ok(())
}
}
// # Safety:
// This is fine because StringInterner::get_or_insert(v) holds a RwLock
// for its entire duration, and doesn't touch the non-(Send+Sync) arena
// unless the lock is held by a write guard.
unsafe impl<'a> Send for StringInterner<'a> {}
unsafe impl<'a> Sync for StringInterner<'a> {}
unsafe impl Send for StringInterner<'_> {}
unsafe impl Sync for StringInterner<'_> {}
#[cfg(test)]
mod tests {
@@ -245,6 +268,12 @@ pub mod typed_interner {
keys: RwLock<HashSet<&'a T>>,
}
impl<'a, T: Eq + Hash> Default for TypedInterner<'a, T> {
fn default() -> Self {
Self { arena: Default::default(), keys: Default::default() }
}
}
impl<'a, T: Eq + Hash> TypedInterner<'a, T> {
/// Creates a new [TypedInterner] backed by the provided [TypedArena]
pub fn new(arena: TypedArena<'a, T>) -> Self {
@@ -286,5 +315,5 @@ pub mod typed_interner {
/// [get_or_insert](TypedInterner::get_or_insert) are unique, and the function uses
/// the [RwLock] around the [HashSet] to ensure mutual exclusion
unsafe impl<'a, T: Eq + Hash + Send> Send for TypedInterner<'a, T> where &'a T: Send {}
unsafe impl<'a, T: Eq + Hash + Send + Sync> Sync for TypedInterner<'a, T> {}
unsafe impl<T: Eq + Hash + Send + Sync> Sync for TypedInterner<'_, T> {}
}

View File

@@ -10,7 +10,7 @@
//! [im]: index_map::IndexMap
//! [mi]: index_map::MapIndex
#![warn(clippy::all)]
#![feature(dropck_eyepatch, decl_macro, get_many_mut)]
#![feature(dropck_eyepatch, decl_macro)]
#![deny(unsafe_op_in_unsafe_fn)]
pub mod intern;

View File

@@ -42,6 +42,6 @@ impl Loc {
impl std::fmt::Display for Loc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Loc { line, col } = self;
write!(f, "{line}:{col}:")
write!(f, "{line}:{col}")
}
}

View File

@@ -93,8 +93,8 @@ use std::{
/// assert_eq!(Some(&10), v.last());
/// ```
pub macro stack {
($count:literal) => {
Stack::<_, $count>::new()
($capacity:literal) => {
Stack::<_, $capacity>::new()
},
($value:expr ; $count:literal) => {{
let mut stack: Stack<_, $count> = Stack::new();
@@ -103,6 +103,13 @@ pub macro stack {
}
stack
}},
($value:expr ; $count:literal ; $capacity:literal) => {{
let mut stack: Stack<_, $capacity> = Stack::new();
for _ in 0..$count {
stack.push($value)
}
stack
}},
($($values:expr),* $(,)?) => {
Stack::from([$($values),*])
}
@@ -142,20 +149,14 @@ impl<T, const N: usize> Deref for Stack<T, N> {
#[inline]
fn deref(&self) -> &Self::Target {
// Safety:
// - We have ensured all elements from 0 to len have been initialized
// - self.elem[0] came from a reference, and so is aligned to T
// unsafe { &*(&self.buf[0..self.len] as *const [_] as *const [T]) }
unsafe { slice::from_raw_parts(self.buf.as_ptr().cast(), self.len) }
self.as_slice()
}
}
impl<T, const N: usize> DerefMut for Stack<T, N> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
// Safety:
// - See Deref
unsafe { slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast(), self.len) }
self.as_mut_slice()
}
}
@@ -163,7 +164,7 @@ impl<T, const N: usize> DerefMut for Stack<T, N> {
unsafe impl<#[may_dangle] T, const N: usize> Drop for Stack<T, N> {
#[inline]
fn drop(&mut self) {
// Safety: We have ensured that all elements in the list are
// Safety: Elements in [0..self.len] are initialized
if std::mem::needs_drop::<T>() {
unsafe { core::ptr::drop_in_place(self.as_mut_slice()) };
}
@@ -271,18 +272,24 @@ impl<T, const N: usize> Stack<T, N> {
}
/// Returns an unsafe mutable pointer to the stack's buffer
pub fn as_mut_ptr(&mut self) -> *mut T {
pub const fn as_mut_ptr(&mut self) -> *mut T {
self.buf.as_mut_ptr().cast()
}
/// Extracts a slice containing the entire vector
pub fn as_slice(&self) -> &[T] {
self
pub const fn as_slice(&self) -> &[T] {
// Safety:
// - We have ensured all elements from 0 to len have been initialized
// - self.elem[0] came from a reference, and so is aligned to T
// unsafe { &*(&self.buf[0..self.len] as *const [_] as *const [T]) }
unsafe { slice::from_raw_parts(self.buf.as_ptr().cast(), self.len) }
}
/// Extracts a mutable slice containing the entire vector
pub fn as_mut_slice(&mut self) -> &mut [T] {
self
pub const fn as_mut_slice(&mut self) -> &mut [T] {
// Safety:
// - See Stack::as_slice
unsafe { slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast(), self.len) }
}
/// Returns the total number of elements the stack can hold
@@ -355,7 +362,7 @@ impl<T, const N: usize> Stack<T, N> {
/// v.push(3);
/// assert_eq!(&[0, 1, 2, 3], v.as_slice());
/// ```
pub fn push(&mut self, value: T) {
pub const fn push(&mut self, value: T) {
if self.len >= N {
panic!("Attempted to push into full stack")
}
@@ -366,7 +373,7 @@ impl<T, const N: usize> Stack<T, N> {
/// Push a new element onto the end of the stack
///
/// Returns [`Err(value)`](Result::Err) if the new length would exceed capacity
pub fn try_push(&mut self, value: T) -> Result<(), T> {
pub const fn try_push(&mut self, value: T) -> Result<(), T> {
if self.len >= N {
return Err(value);
}
@@ -381,8 +388,11 @@ impl<T, const N: usize> Stack<T, N> {
///
/// len after push must not exceed capacity N
#[inline]
unsafe fn push_unchecked(&mut self, value: T) {
unsafe { ptr::write(self.as_mut_ptr().add(self.len), value) }
const unsafe fn push_unchecked(&mut self, value: T) {
unsafe {
// self.buf.get_unchecked_mut(self.len).write(value); // TODO: This is non-const
ptr::write(self.as_mut_ptr().add(self.len), value)
}
self.len += 1; // post inc
}
@@ -402,13 +412,14 @@ impl<T, const N: usize> Stack<T, N> {
/// assert_eq!(Some(0), v.pop());
/// assert_eq!(None, v.pop());
/// ```
pub fn pop(&mut self) -> Option<T> {
pub const fn pop(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
// Safety: MaybeUninit<T> implies ManuallyDrop<T>,
// therefore should not get dropped twice
// Some(unsafe { self.buf.get_unchecked_mut(self.len).assume_init_read() })
Some(unsafe { ptr::read(self.as_ptr().add(self.len).cast()) })
}
}
@@ -507,7 +518,7 @@ impl<T, const N: usize> Stack<T, N> {
///
/// assert_eq!(Ok(()), v.try_insert(0, 0));
/// ```
pub fn try_insert(&mut self, index: usize, data: T) -> Result<(), (T, InsertFailed<N>)> {
pub const fn try_insert(&mut self, index: usize, data: T) -> Result<(), (T, InsertFailed<N>)> {
if index > self.len {
return Err((data, InsertFailed::Bounds(index)));
}
@@ -523,7 +534,7 @@ impl<T, const N: usize> Stack<T, N> {
/// - index must be less than self.len
/// - length after insertion must be <= N
#[inline]
unsafe fn insert_unchecked(&mut self, index: usize, data: T) {
const unsafe fn insert_unchecked(&mut self, index: usize, data: T) {
let base = self.as_mut_ptr();
unsafe { ptr::copy(base.add(index), base.add(index + 1), self.len - index) }
@@ -547,7 +558,9 @@ impl<T, const N: usize> Stack<T, N> {
/// ```
pub fn clear(&mut self) {
// Hopefully copy elision takes care of this lmao
drop(std::mem::take(self))
while !self.is_empty() {
drop(self.pop());
}
}
/// Returns the number of elements in the stack
@@ -557,7 +570,7 @@ impl<T, const N: usize> Stack<T, N> {
///
/// assert_eq!(5, v.len());
/// ```
pub fn len(&self) -> usize {
pub const fn len(&self) -> usize {
self.len
}
@@ -572,7 +585,7 @@ impl<T, const N: usize> Stack<T, N> {
/// assert!(v.is_full());
/// ```
#[inline]
pub fn is_full(&self) -> bool {
pub const fn is_full(&self) -> bool {
self.len >= N
}
@@ -587,7 +600,7 @@ impl<T, const N: usize> Stack<T, N> {
/// assert!(v.is_empty());
/// ```
#[inline]
pub fn is_empty(&self) -> bool {
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
@@ -625,6 +638,7 @@ mod tests {
v.pop();
assert_eq!(v.len(), usize::MAX - 1);
}
#[test]
fn new() {
let v: Stack<(), 255> = Stack::new();
@@ -745,4 +759,19 @@ mod tests {
]);
std::mem::drop(std::hint::black_box(v));
}
#[test]
fn drop_zst() {
struct Droppable;
impl Drop for Droppable {
fn drop(&mut self) {
use std::sync::atomic::{AtomicU32, Ordering};
static V: AtomicU32 = AtomicU32::new(1);
eprintln!("{}", V.fetch_add(1, Ordering::Relaxed));
}
}
let v = Stack::from([const { Droppable }; 10]);
std::mem::drop(v);
}
}

View File

@@ -10,4 +10,4 @@ pub mod token_type;
pub use token::Token;
pub use token_data::TokenData;
pub use token_type::{Punct, TokenKind};
pub use token_type::TokenKind;

View File

@@ -13,41 +13,35 @@ pub enum TokenKind {
/// A non-keyword identifier
Identifier,
// A keyword
As,
Break,
Cl,
Const,
Continue,
Else,
Enum,
False,
For,
Fn,
If,
Impl,
In,
Let,
Loop,
Mod,
Mut,
Pub,
Return,
SelfKw,
SelfTy,
Static,
Struct,
Super,
True,
Type,
Use,
While,
/// Delimiter or punctuation
Punct(Punct),
}
/// An operator character (delimiter, punctuation)
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Punct {
As, // as
Break, // "break"
Cl, // "cl"
Const, // "const"
Continue, // "continue"
Else, // "else"
Enum, // "enum"
False, // "false"
Fn, // "fn"
For, // "for"
If, // "if"
Impl, // "impl"
In, // "in"
Let, // "let"
Loop, // "loop"
Match, // "match"
Mod, // "mod"
Mut, // "mut"
Pub, // "pub"
Return, // "return"
SelfTy, // "Self"
Static, // "static"
Struct, // "struct"
Super, // "super"
True, // "true"
Type, // "type"
Use, // "use"
While, // "while"
// Delimiter or punctuation
LCurly, // {
RCurly, // }
LBrack, // [
@@ -120,18 +114,18 @@ impl Display for TokenKind {
TokenKind::Else => "else".fmt(f),
TokenKind::Enum => "enum".fmt(f),
TokenKind::False => "false".fmt(f),
TokenKind::For => "for".fmt(f),
TokenKind::Fn => "fn".fmt(f),
TokenKind::For => "for".fmt(f),
TokenKind::If => "if".fmt(f),
TokenKind::Impl => "impl".fmt(f),
TokenKind::In => "in".fmt(f),
TokenKind::Let => "let".fmt(f),
TokenKind::Loop => "loop".fmt(f),
TokenKind::Match => "match".fmt(f),
TokenKind::Mod => "mod".fmt(f),
TokenKind::Mut => "mut".fmt(f),
TokenKind::Pub => "pub".fmt(f),
TokenKind::Return => "return".fmt(f),
TokenKind::SelfKw => "self".fmt(f),
TokenKind::SelfTy => "Self".fmt(f),
TokenKind::Static => "static".fmt(f),
TokenKind::Struct => "struct".fmt(f),
@@ -141,7 +135,60 @@ impl Display for TokenKind {
TokenKind::Use => "use".fmt(f),
TokenKind::While => "while".fmt(f),
TokenKind::Punct(op) => op.fmt(f),
TokenKind::LCurly => "{".fmt(f),
TokenKind::RCurly => "}".fmt(f),
TokenKind::LBrack => "[".fmt(f),
TokenKind::RBrack => "]".fmt(f),
TokenKind::LParen => "(".fmt(f),
TokenKind::RParen => ")".fmt(f),
TokenKind::Amp => "&".fmt(f),
TokenKind::AmpAmp => "&&".fmt(f),
TokenKind::AmpEq => "&=".fmt(f),
TokenKind::Arrow => "->".fmt(f),
TokenKind::At => "@".fmt(f),
TokenKind::Backslash => "\\".fmt(f),
TokenKind::Bang => "!".fmt(f),
TokenKind::BangBang => "!!".fmt(f),
TokenKind::BangEq => "!=".fmt(f),
TokenKind::Bar => "|".fmt(f),
TokenKind::BarBar => "||".fmt(f),
TokenKind::BarEq => "|=".fmt(f),
TokenKind::Colon => ":".fmt(f),
TokenKind::ColonColon => "::".fmt(f),
TokenKind::Comma => ",".fmt(f),
TokenKind::Dot => ".".fmt(f),
TokenKind::DotDot => "..".fmt(f),
TokenKind::DotDotEq => "..=".fmt(f),
TokenKind::Eq => "=".fmt(f),
TokenKind::EqEq => "==".fmt(f),
TokenKind::FatArrow => "=>".fmt(f),
TokenKind::Grave => "`".fmt(f),
TokenKind::Gt => ">".fmt(f),
TokenKind::GtEq => ">=".fmt(f),
TokenKind::GtGt => ">>".fmt(f),
TokenKind::GtGtEq => ">>=".fmt(f),
TokenKind::Hash => "#".fmt(f),
TokenKind::HashBang => "#!".fmt(f),
TokenKind::Lt => "<".fmt(f),
TokenKind::LtEq => "<=".fmt(f),
TokenKind::LtLt => "<<".fmt(f),
TokenKind::LtLtEq => "<<=".fmt(f),
TokenKind::Minus => "-".fmt(f),
TokenKind::MinusEq => "-=".fmt(f),
TokenKind::Plus => "+".fmt(f),
TokenKind::PlusEq => "+=".fmt(f),
TokenKind::Question => "?".fmt(f),
TokenKind::Rem => "%".fmt(f),
TokenKind::RemEq => "%=".fmt(f),
TokenKind::Semi => ";".fmt(f),
TokenKind::Slash => "/".fmt(f),
TokenKind::SlashEq => "/=".fmt(f),
TokenKind::Star => "*".fmt(f),
TokenKind::StarEq => "*=".fmt(f),
TokenKind::Tilde => "~".fmt(f),
TokenKind::Xor => "^".fmt(f),
TokenKind::XorEq => "^=".fmt(f),
TokenKind::XorXor => "^^".fmt(f),
}
}
}
@@ -159,18 +206,18 @@ impl FromStr for TokenKind {
"else" => Self::Else,
"enum" => Self::Enum,
"false" => Self::False,
"for" => Self::For,
"fn" => Self::Fn,
"for" => Self::For,
"if" => Self::If,
"impl" => Self::Impl,
"in" => Self::In,
"let" => Self::Let,
"loop" => Self::Loop,
"match" => Self::Match,
"mod" => Self::Mod,
"mut" => Self::Mut,
"pub" => Self::Pub,
"return" => Self::Return,
"self" => Self::SelfKw,
"Self" => Self::SelfTy,
"static" => Self::Static,
"struct" => Self::Struct,
@@ -183,64 +230,3 @@ impl FromStr for TokenKind {
})
}
}
impl Display for Punct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Punct::LCurly => "{".fmt(f),
Punct::RCurly => "}".fmt(f),
Punct::LBrack => "[".fmt(f),
Punct::RBrack => "]".fmt(f),
Punct::LParen => "(".fmt(f),
Punct::RParen => ")".fmt(f),
Punct::Amp => "&".fmt(f),
Punct::AmpAmp => "&&".fmt(f),
Punct::AmpEq => "&=".fmt(f),
Punct::Arrow => "->".fmt(f),
Punct::At => "@".fmt(f),
Punct::Backslash => "\\".fmt(f),
Punct::Bang => "!".fmt(f),
Punct::BangBang => "!!".fmt(f),
Punct::BangEq => "!=".fmt(f),
Punct::Bar => "|".fmt(f),
Punct::BarBar => "||".fmt(f),
Punct::BarEq => "|=".fmt(f),
Punct::Colon => ":".fmt(f),
Punct::ColonColon => "::".fmt(f),
Punct::Comma => ",".fmt(f),
Punct::Dot => ".".fmt(f),
Punct::DotDot => "..".fmt(f),
Punct::DotDotEq => "..=".fmt(f),
Punct::Eq => "=".fmt(f),
Punct::EqEq => "==".fmt(f),
Punct::FatArrow => "=>".fmt(f),
Punct::Grave => "`".fmt(f),
Punct::Gt => ">".fmt(f),
Punct::GtEq => ">=".fmt(f),
Punct::GtGt => ">>".fmt(f),
Punct::GtGtEq => ">>=".fmt(f),
Punct::Hash => "#".fmt(f),
Punct::HashBang => "#!".fmt(f),
Punct::Lt => "<".fmt(f),
Punct::LtEq => "<=".fmt(f),
Punct::LtLt => "<<".fmt(f),
Punct::LtLtEq => "<<=".fmt(f),
Punct::Minus => "-".fmt(f),
Punct::MinusEq => "-=".fmt(f),
Punct::Plus => "+".fmt(f),
Punct::PlusEq => "+=".fmt(f),
Punct::Question => "?".fmt(f),
Punct::Rem => "%".fmt(f),
Punct::RemEq => "%=".fmt(f),
Punct::Semi => ";".fmt(f),
Punct::Slash => "/".fmt(f),
Punct::SlashEq => "/=".fmt(f),
Punct::Star => "*".fmt(f),
Punct::StarEq => "*=".fmt(f),
Punct::Tilde => "~".fmt(f),
Punct::Xor => "^".fmt(f),
Punct::XorEq => "^=".fmt(f),
Punct::XorXor => "^^".fmt(f),
}
}
}

View File

@@ -12,6 +12,6 @@ cl-ast = { path = "../cl-ast" }
cl-structures = { path = "../cl-structures" }
[dev-dependencies]
repline = { path = "../../repline" }
repline = { version = "*", registry = "soft-fish" }
cl-lexer = { path = "../cl-lexer" }
cl-parser = { path = "../cl-parser" }

View File

@@ -1,76 +1,99 @@
use cl_typeck::{
entry::Entry,
stage::{
infer::{engine::InferenceEngine, error::InferenceError, inference::Inference},
*,
},
table::Table,
type_expression::TypeExpression,
};
use cl_ast::{
Expr, Path, Stmt, Ty,
ast_visitor::{Fold, Visit},
desugar::*,
};
use cl_lexer::Lexer;
use cl_parser::{inliner::ModuleInliner, Parser};
use cl_typeck::{
definition::Def,
handle::Handle,
name_collector::NameCollector,
node::{Node, NodeSource},
project::Project,
type_resolver::resolve,
};
use cl_parser::{Parser, inliner::ModuleInliner};
use cl_structures::intern::string_interner::StringInterner;
use repline::{error::Error as RlError, prebaked::*};
use std::{error::Error, path};
use std::{
error::Error,
path::{self, PathBuf},
sync::LazyLock,
};
// Path to display in standard library errors
const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl";
// Statically included standard library
const STDLIB: &str = include_str!("../../../stdlib/lib.cl");
const PREAMBLE: &str = r"
pub mod std;
pub use std::preamble::*;
";
// Colors
const C_MAIN: &str = "";
const C_MAIN: &str = C_LISTING;
const C_RESV: &str = "\x1b[35m";
const C_CODE: &str = "\x1b[36m";
const C_BYID: &str = "\x1b[95m";
const C_ERROR: &str = "\x1b[31m";
const C_LISTING: &str = "\x1b[38;5;117m";
/// A home for immutable intermediate ASTs
///
/// TODO: remove this.
static mut TREES: TreeManager = TreeManager::new();
fn main() -> Result<(), Box<dyn Error>> {
let mut prj = Project::default();
let mut prj = Table::default();
let mut parser = Parser::new(Lexer::new(STDLIB));
let code = match parser.file() {
let mut parser = Parser::new("PREAMBLE", Lexer::new(PREAMBLE));
let code = match parser.parse() {
Ok(code) => code,
Err(e) => {
eprintln!("{STDLIB_DISPLAY_PATH}:{e}");
Err(e)?
}
};
let code = inline_modules(code, concat!(env!("PWD"), "/stdlib"));
NameCollector::new(&mut prj).visit_file(unsafe { TREES.push(code) });
// This code is special - it gets loaded from a hard-coded project directory (for now)
let code = inline_modules(code, concat!(env!("CARGO_MANIFEST_DIR"), "/../../stdlib"));
let code = cl_ast::desugar::WhileElseDesugar.fold_file(code);
Populator::new(&mut prj).visit_file(interned(code));
for arg in std::env::args().skip(1) {
import_file(&mut prj, arg)?;
}
resolve_all(&mut prj)?;
main_menu(&mut prj)?;
Ok(())
}
fn main_menu(prj: &mut Project) -> Result<(), RlError> {
fn main_menu(prj: &mut Table) -> Result<(), RlError> {
banner();
read_and(C_MAIN, "mu>", "? >", |line| {
match line.trim() {
for line in line.trim().split_ascii_whitespace() {
match line {
"c" | "code" => enter_code(prj)?,
"clear" => clear()?,
"dump" => dump(prj)?,
"d" | "desugar" => live_desugar()?,
"e" | "exit" => return Ok(Response::Break),
"f" | "file" => import_files(prj)?,
"i" | "id" => get_by_id(prj)?,
"l" | "list" => list_types(prj),
"q" | "query" => query_type_expression(prj)?,
"i" | "id" => get_by_id(prj)?,
"r" | "resolve" => resolve_all(prj)?,
"d" | "desugar" => live_desugar()?,
"h" | "help" => {
"s" | "strings" => print_strings(),
"a" | "all" => infer_all(prj)?,
"t" | "test" => infer_expression(prj)?,
"h" | "help" | "" => {
println!(
"Valid commands are:
clear : Clear the screen
code (c): Enter code to type-check
desugar (d): WIP: Test the experimental desugaring passes
file (f): Load files from disk
id (i): Get a type by its type ID
list (l): List all known types
query (q): Query the type system
id (i): Get a type by its type ID
resolve (r): Perform type resolution
desugar (d): WIP: Test the experimental desugaring passes
help (h): Print this list
exit (e): Exit the program"
);
@@ -78,30 +101,33 @@ fn main_menu(prj: &mut Project) -> Result<(), RlError> {
}
_ => Err(r#"Invalid command. Type "help" to see the list of valid commands."#)?,
}
}
Ok(Response::Accept)
})
}
fn enter_code(prj: &mut Project) -> Result<(), RlError> {
fn enter_code(prj: &mut Table) -> Result<(), RlError> {
read_and(C_CODE, "cl>", "? >", |line| {
if line.trim().is_empty() {
return Ok(Response::Break);
}
let code = Parser::new(Lexer::new(line)).file()?;
let code = Parser::new("", Lexer::new(line)).parse()?;
let code = inline_modules(code, "");
let code = WhileElseDesugar.fold_file(code);
// Safety: this is totally unsafe
NameCollector::new(prj).visit_file(unsafe { TREES.push(code) });
Populator::new(prj).visit_file(interned(code));
Ok(Response::Accept)
})
}
fn live_desugar() -> Result<(), RlError> {
read_and(C_RESV, "se>", "? >", |line| {
let code = Parser::new(Lexer::new(line)).stmt()?;
let code = Parser::new("", Lexer::new(line)).parse::<Stmt>()?;
println!("Raw, as parsed:\n{C_LISTING}{code}\x1b[0m");
let code = ConstantFolder.fold_stmt(code);
println!("ConstantFolder\n{C_LISTING}{code}\x1b[0m");
let code = SquashGroups.fold_stmt(code);
println!("SquashGroups\n{C_LISTING}{code}\x1b[0m");
@@ -115,37 +141,74 @@ fn live_desugar() -> Result<(), RlError> {
})
}
fn query_type_expression(prj: &mut Project) -> Result<(), RlError> {
fn print_strings() {
println!("{}", StringInterner::global());
}
fn query_type_expression(prj: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "ty>", "? >", |line| {
if line.trim().is_empty() {
return Ok(Response::Break);
}
// parse it as a path, and convert the path into a borrowed path
let ty = Parser::new(Lexer::new(line)).ty()?.kind;
let id = prj.evaluate(&ty, prj.root)?.handle_unchecked(prj);
pretty_handle(id)?;
// A query is comprised of a Ty and a relative Path
let mut p = Parser::new("", Lexer::new(line));
let ty: Ty = p.parse()?;
let path: Path = p
.parse()
.map(|p| Path { absolute: false, ..p })
.unwrap_or_default();
let id = ty.evaluate(prj, prj.root())?;
let id = path.evaluate(prj, id)?;
pretty_handle(id.to_entry(prj))?;
Ok(Response::Accept)
})
}
fn get_by_id(prj: &mut Project) -> Result<(), RlError> {
#[allow(dead_code)]
fn infer_expression(prj: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "ex>", "!?>", |line| {
if line.trim().is_empty() {
return Ok(Response::Break);
}
let mut p = Parser::new("", Lexer::new(line));
let e: Expr = p.parse()?;
let mut inf = InferenceEngine::new(prj, prj.root());
let ty = match exp_terned(e).infer(&mut inf) {
Ok(ty) => ty,
Err(e) => match e {
InferenceError::Mismatch(a, b) => {
eprintln!("Mismatched types: {}, {}", prj.entry(a), prj.entry(b));
return Ok(Response::Deny);
}
InferenceError::Recursive(a, b) => {
eprintln!("Recursive types: {}, {}", prj.entry(a), prj.entry(b));
return Ok(Response::Deny);
}
e => Err(e)?,
},
};
eprintln!("--> {}", prj.entry(ty));
Ok(Response::Accept)
})
}
fn get_by_id(prj: &mut Table) -> Result<(), RlError> {
use cl_parser::parser::Parse;
use cl_structures::index_map::MapIndex;
use cl_typeck::handle::DefID;
use cl_typeck::handle::Handle;
read_and(C_BYID, "id>", "? >", |line| {
if line.trim().is_empty() {
return Ok(Response::Break);
}
let mut parser = Parser::new(Lexer::new(line));
let def_id = match parser.literal()? {
let mut parser = Parser::new("", Lexer::new(line));
let def_id = match Parse::parse(&mut parser)? {
cl_ast::Literal::Int(int) => int as _,
other => Err(format!("Expected integer, got {other}"))?,
};
let mut path = parser.path().unwrap_or_default();
let mut path = parser.parse::<cl_ast::Path>().unwrap_or_default();
path.absolute = false;
let Some(handle) = DefID::from_usize(def_id).handle(prj) else {
return Ok(Response::Deny);
};
let handle = Handle::from_usize(def_id).to_entry(prj);
print!(" > {{{C_LISTING}{handle}\x1b[0m}}");
if !path.parts.is_empty() {
@@ -153,71 +216,178 @@ fn get_by_id(prj: &mut Project) -> Result<(), RlError> {
}
println!();
let (ty, value) = handle.navigate((&path).into());
if let (None, None) = (ty, value) {
let Some(entry) = handle.nav(&path.parts) else {
Err("No results.")?
}
if let Some(t) = ty {
println!("Result in type namespace: {}", t.id());
pretty_handle(t)?;
}
if let Some(v) = value {
println!("Result in value namespace: {}", v.id());
pretty_handle(v)?;
}
};
pretty_handle(entry)?;
Ok(Response::Accept)
})
}
fn resolve_all(prj: &mut Project) -> Result<(), Box<dyn Error>> {
prj.resolve_imports()?;
for id in prj.pool.keys() {
resolve(prj, id)?;
fn resolve_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
for (id, error) in import(table) {
eprintln!("{error} in {} ({id})", id.to_entry(table))
}
println!("Types resolved successfully!");
for handle in table.handle_iter() {
if let Err(error) = handle.to_entry_mut(table).categorize() {
eprintln!("{error}");
}
}
for handle in implement(table) {
eprintln!("Unable to reparent {} ({handle})", handle.to_entry(table))
}
println!("...Resolved!");
Ok(())
}
fn list_types(prj: &mut Project) {
println!(" name\x1b[30G type");
for (idx, key) in prj.pool.keys().enumerate() {
let Def { node: Node { vis, kind: source, .. }, .. } = &prj[key];
let name = match source.as_ref().map(NodeSource::name) {
Some(Some(name)) => name,
_ => "".into(),
};
print!(
"{idx:3}: {vis}{name}\x1b[30G = {}",
key.handle_unchecked(prj)
);
println!();
fn infer_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
for (id, error) in InferenceEngine::new(table, table.root()).infer_all() {
match error {
InferenceError::Mismatch(a, b) => {
eprint!("Mismatched types: {}, {}", table.entry(a), table.entry(b));
}
InferenceError::Recursive(a, b) => {
eprint!("Recursive types: {}, {}", table.entry(a), table.entry(b));
}
e => eprint!("{e}"),
}
eprintln!(" in {id}\n({})\n", id.to_entry(table).source().unwrap())
}
println!("...Inferred!");
Ok(())
}
fn list_types(table: &mut Table) {
for handle in table.debug_entry_iter() {
let id = handle.id();
let kind = handle.kind().unwrap();
let name = handle.name().unwrap_or("".into());
println!("{id:3}: {name:16}| {kind}: {handle}");
}
}
fn pretty_handle(handle: Handle) -> Result<(), std::io::Error> {
use std::io::Write;
let mut stdout = std::io::stdout().lock();
let Some(Def { module, node: Node { vis, .. }, .. }) = handle.get() else {
return writeln!(stdout, "Invalid handle: {handle}");
fn import_file(table: &mut Table, path: impl AsRef<std::path::Path>) -> Result<(), Box<dyn Error>> {
let Ok(file) = std::fs::read_to_string(path.as_ref()) else {
for file in std::fs::read_dir(path)? {
println!("{}", file?.path().display())
}
return Ok(());
};
writeln!(stdout, "{C_LISTING}{vis}{handle}\x1b[0m: {}", handle.id())?;
if let Some(parent) = module.parent {
writeln!(stdout, "{C_LISTING}Parent\x1b[0m: {}", handle.with(parent))?;
let mut parser = Parser::new("", Lexer::new(&file));
let code = match parser.parse() {
Ok(code) => inline_modules(
code,
PathBuf::from(path.as_ref()).parent().unwrap_or("".as_ref()),
),
Err(e) => {
eprintln!("{C_ERROR}{}:{e}\x1b[0m", path.as_ref().display());
return Ok(());
}
if !module.types.is_empty() {
writeln!(stdout, "{C_LISTING}Types:\x1b[0m")?;
for (name, def) in &module.types {
writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))?
};
let code = cl_ast::desugar::WhileElseDesugar.fold_file(code);
Populator::new(table).visit_file(interned(code));
Ok(())
}
fn import_files(table: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "fi>", "? >", |line| {
let line = line.trim();
if line.is_empty() {
return Ok(Response::Break);
}
let Ok(file) = std::fs::read_to_string(line) else {
for file in std::fs::read_dir(line)? {
println!("{}", file?.path().display())
}
return Ok(Response::Accept);
};
let mut parser = Parser::new("", Lexer::new(&file));
let code = match parser.parse() {
Ok(code) => inline_modules(code, PathBuf::from(line).parent().unwrap_or("".as_ref())),
Err(e) => {
eprintln!("{C_ERROR}{line}:{e}\x1b[0m");
return Ok(Response::Deny);
}
};
Populator::new(table).visit_file(interned(code));
println!("...Imported!");
Ok(Response::Accept)
})
}
fn pretty_handle(entry: Entry) -> Result<(), std::io::Error> {
use std::io::Write;
let mut out = std::io::stdout().lock();
let Some(kind) = entry.kind() else {
return writeln!(out, "{entry}");
};
write!(out, "{C_LISTING}{kind}")?;
if let Some(name) = entry.name() {
write!(out, " {name}")?;
}
writeln!(out, "\x1b[0m ({}): {entry}", entry.id())?;
if let Some(parent) = entry.parent() {
writeln!(
out,
"- {C_LISTING}Parent\x1b[0m: {parent} ({})",
parent.id()
)?;
}
if let Some(span) = entry.span() {
writeln!(
out,
"- {C_LISTING}Span:\x1b[0m ({}, {})",
span.head, span.tail
)?;
}
match entry.meta() {
Some(meta) if !meta.is_empty() => {
writeln!(out, "- {C_LISTING}Meta:\x1b[0m")?;
for meta in meta {
writeln!(out, " - {meta}")?;
}
}
if !module.values.is_empty() {
writeln!(stdout, "{C_LISTING}Values:\x1b[0m")?;
for (name, def) in &module.values {
writeln!(stdout, "- {C_LISTING}{name}\x1b[0m: {}", handle.with(*def))?
_ => {}
}
if let Some(children) = entry.children() {
writeln!(out, "- {C_LISTING}Children:\x1b[0m")?;
for (name, child) in children {
writeln!(
out,
" - {C_LISTING}{name}\x1b[0m ({child}): {}",
entry.with_id(*child)
)?
}
}
write!(stdout, "\x1b[0m")
if let Some(imports) = entry.imports() {
writeln!(out, "- {C_LISTING}Imports:\x1b[0m")?;
for (name, child) in imports {
writeln!(
out,
" - {C_LISTING}{name}\x1b[0m ({child}): {}",
entry.with_id(*child)
)?
}
}
Ok(())
}
fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::File {
@@ -235,6 +405,30 @@ fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> cl_ast::F
}
}
fn dump(table: &Table) -> Result<(), Box<dyn Error>> {
fn dump_recursive(
name: cl_ast::Sym,
entry: Entry,
depth: usize,
to_file: &mut std::fs::File,
) -> std::io::Result<()> {
use std::io::Write;
write!(to_file, "{:w$}{name}: {entry}", "", w = depth)?;
if let Some(children) = entry.children() {
writeln!(to_file, " {{")?;
for (name, child) in children {
dump_recursive(*name, entry.with_id(*child), depth + 2, to_file)?;
}
write!(to_file, "{:w$}}}", "", w = depth)?;
}
writeln!(to_file)
}
let mut file = std::fs::File::create("typeck-table.ron")?;
dump_recursive("root".into(), table.root_entry(), 0, &mut file)?;
Ok(())
}
fn clear() -> Result<(), Box<dyn Error>> {
println!("\x1b[H\x1b[2J");
banner();
@@ -249,18 +443,20 @@ fn banner() {
);
}
/// Keeps leaked references to past ASTs, for posterity:tm:
struct TreeManager {
trees: Vec<&'static cl_ast::File>,
/// Interns a [File](cl_ast::File), returning a static reference to it.
fn interned(file: cl_ast::File) -> &'static cl_ast::File {
use cl_structures::intern::typed_interner::TypedInterner;
static INTERNER: LazyLock<TypedInterner<'static, cl_ast::File>> =
LazyLock::new(Default::default);
INTERNER.get_or_insert(file).to_ref()
}
impl TreeManager {
const fn new() -> Self {
Self { trees: vec![] }
}
fn push(&mut self, tree: cl_ast::File) -> &'static cl_ast::File {
let ptr = Box::leak(Box::new(tree));
self.trees.push(ptr);
ptr
}
/// Interns an [Expr](cl_ast::Expr), returning a static reference to it.
fn exp_terned(expr: cl_ast::Expr) -> &'static cl_ast::Expr {
use cl_structures::intern::typed_interner::TypedInterner;
static INTERNER: LazyLock<TypedInterner<'static, cl_ast::Expr>> =
LazyLock::new(Default::default);
INTERNER.get_or_insert(expr).to_ref()
}

View File

@@ -1,193 +0,0 @@
use crate::{
handle::DefID,
module::Module,
node::{Node, NodeSource},
};
use cl_ast::{Meta, Sym, Visibility};
use std::{fmt::Debug, str::FromStr};
mod display;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Def<'a> {
pub node: Node<'a>,
pub kind: DefKind,
pub module: Module,
}
impl<'a> Def<'a> {
pub fn with_node(node: Node<'a>) -> Self {
Self { node, kind: DefKind::Undecided, module: Default::default() }
}
}
impl Def<'_> {
pub fn name(&self) -> Option<Sym> {
match self.node.kind {
Some(source) => source.name(),
None => None,
}
}
pub fn is_transparent(&self) -> bool {
!matches!(self.kind, DefKind::Type(_))
}
}
mod builder_functions {
use super::*;
impl<'a> Def<'a> {
pub fn set_vis(&mut self, vis: Visibility) -> &mut Self {
self.node.vis = vis;
self
}
pub fn set_meta(&mut self, meta: &'a [Meta]) -> &mut Self {
self.node.meta = meta;
self
}
pub fn set_kind(&mut self, kind: DefKind) -> &mut Self {
self.kind = kind;
self
}
pub fn set_source(&mut self, source: NodeSource<'a>) -> &mut Self {
self.node.kind = Some(source);
self
}
pub fn set_module(&mut self, module: Module) -> &mut Self {
self.module = module;
self
}
}
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub enum DefKind {
/// An unevaluated definition
#[default]
Undecided,
/// An impl block
Impl(DefID),
/// A use tree, and its parent
Use(DefID),
/// A type, such as a `type`, `struct`, or `enum`
Type(TypeKind),
/// A value, such as a `const`, `static`, or `fn`
Value(ValueKind),
}
/// A [ValueKind] represents an item in the Value Namespace
/// (a component of a [Project](crate::project::Project)).
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ValueKind {
Const(DefID),
Static(DefID),
Local(DefID),
Fn(DefID),
}
/// A [TypeKind] represents an item in the Type Namespace
/// (a component of a [Project](crate::project::Project)).
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeKind {
/// An alias for an already-defined type
Alias(Option<DefID>),
/// A primitive type, built-in to the compiler
Intrinsic(Intrinsic),
/// A user-defined aromatic data type
Adt(Adt),
/// A reference to an already-defined type: &T
Ref(u16, DefID),
/// A contiguous view of dynamically sized memory
Slice(DefID),
/// A contiguous view of statically sized memory
Array(DefID, usize),
/// A tuple of existing types
Tuple(Vec<DefID>),
/// A function which accepts multiple inputs and produces an output
FnSig { args: DefID, rety: DefID },
/// The unit type
Empty,
/// The never type
Never,
/// An untyped module
Module,
}
/// A user-defined Aromatic Data Type
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adt {
/// A union-like enum type
Enum(Vec<(Sym, Option<DefID>)>),
/// A C-like enum
CLikeEnum(Vec<(Sym, u128)>),
/// An enum with no fields, which can never be constructed
FieldlessEnum,
/// A structural product type with named members
Struct(Vec<(Sym, Visibility, DefID)>),
/// A structural product type with unnamed members
TupleStruct(Vec<(Visibility, DefID)>),
/// A structural product type of neither named nor unnamed members
UnitStruct,
/// A choose your own undefined behavior type
/// TODO: should unions be a language feature?
Union(Vec<(Sym, DefID)>),
}
/// The set of compiler-intrinsic types.
/// These primitive types have native implementations of the basic operations.
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Intrinsic {
/// An 8-bit signed integer: `#[intrinsic = "i8"]`
I8,
/// A 16-bit signed integer: `#[intrinsic = "i16"]`
I16,
/// A 32-bit signed integer: `#[intrinsic = "i32"]`
I32,
/// A 64-bit signed integer: `#[intrinsic = "i32"]`
I64,
// /// A 128-bit signed integer: `#[intrinsic = "i32"]`
// I128,
/// A ptr-len signed integer: `#[intrinsic = "isize"]`
Isize,
/// An 8-bit unsigned integer: `#[intrinsic = "u8"]`
U8,
/// A 16-bit unsigned integer: `#[intrinsic = "u16"]`
U16,
/// A 32-bit unsigned integer: `#[intrinsic = "u32"]`
U32,
/// A 64-bit unsigned integer: `#[intrinsic = "u64"]`
U64,
// /// A 128-bit unsigned integer: `#[intrinsic = "u128"]`
// U128,
/// A ptr-len unsigned integer: `#[intrinsic = "isize"]`
Usize,
/// A boolean (`true` or `false`): `#[intrinsic = "bool"]`
Bool,
/// The unicode codepoint type: #[intrinsic = "char"]
Char,
}
impl FromStr for Intrinsic {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"i8" => Intrinsic::I8,
"i16" => Intrinsic::I16,
"i32" => Intrinsic::I32,
"i64" => Intrinsic::I64,
"isize" => Intrinsic::Isize,
"u8" => Intrinsic::U8,
"u16" => Intrinsic::U16,
"u32" => Intrinsic::U32,
"u64" => Intrinsic::U64,
"usize" => Intrinsic::Usize,
"bool" => Intrinsic::Bool,
"char" => Intrinsic::Char,
_ => Err(())?,
})
}
}

View File

@@ -1,147 +0,0 @@
//! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
use super::{Adt, Def, DefKind, Intrinsic, TypeKind, ValueKind};
use crate::{format_utils::*, node::Node};
use cl_ast::format::FmtAdapter;
use std::fmt::{self, Display, Write};
impl Display for Def<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { module, node: Node { in_path: _, span: _, meta, vis, kind: source }, kind } =
self;
if !meta.is_empty() {
writeln!(f, "#{meta:?}")?;
}
if let Some(source) = source {
if let Some(name) = source.name() {
writeln!(f, "{vis}{name}:")?;
}
writeln!(f.indent(), "source:\n{source}")?;
} else {
writeln!(f, "{vis}: ")?;
}
writeln!(f, "kind: {kind}")?;
write!(f, "module: {module}")
}
}
impl Display for DefKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DefKind::Undecided => write!(f, "undecided"),
DefKind::Impl(id) => write!(f, "impl {id}"),
DefKind::Use(id) => write!(f, "use (inside {id})"),
DefKind::Type(kind) => write!(f, "{kind}"),
DefKind::Value(kind) => write!(f, "{kind}"),
}
}
}
impl std::fmt::Display for ValueKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ValueKind::Const(id) => write!(f, "const ({id})"),
ValueKind::Static(id) => write!(f, "static ({id})"),
ValueKind::Local(id) => write!(f, "let ({id})"),
ValueKind::Fn(id) => write!(f, "fn def ({id})"),
}
}
}
impl Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeKind::Alias(def) => match def {
Some(def) => write!(f, "alias to #{def}"),
None => f.write_str("type"),
},
TypeKind::Intrinsic(i) => i.fmt(f),
TypeKind::Adt(a) => a.fmt(f),
TypeKind::Ref(cnt, def) => {
for _ in 0..*cnt {
f.write_str("&")?;
}
def.fmt(f)
}
TypeKind::Slice(def) => write!(f, "slice [#{def}]"),
TypeKind::Array(def, size) => write!(f, "array [#{def}; {size}]"),
TypeKind::Tuple(defs) => {
let mut defs = defs.iter();
separate(", ", || {
let def = defs.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "#{def}"))
})(f.delimit_with("tuple (", ")"))
}
TypeKind::FnSig { args, rety } => write!(f, "fn (#{args}) -> #{rety}"),
TypeKind::Empty => f.write_str("()"),
TypeKind::Never => f.write_str("!"),
TypeKind::Module => f.write_str("mod"),
}
}
}
impl Display for Adt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Adt::Enum(variants) => {
let mut variants = variants.iter();
separate(", ", || {
let (name, def) = variants.next()?;
Some(move |f: &mut Delimit<_>| match def {
Some(def) => write!(f, "{name}: #{def}"),
None => write!(f, "{name}"),
})
})(f.delimit_with("enum {", "}"))
}
Adt::CLikeEnum(variants) => {
let mut variants = variants.iter();
separate(", ", || {
let (name, descrim) = variants.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{name} = {descrim}"))
})(f.delimit_with("enum {", "}"))
}
Adt::FieldlessEnum => write!(f, "enum"),
Adt::Struct(members) => {
let mut members = members.iter();
separate(", ", || {
let (name, vis, def) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{vis}{name}: #{def}"))
})(f.delimit_with("struct {", "}"))
}
Adt::TupleStruct(members) => {
let mut members = members.iter();
separate(", ", || {
let (vis, def) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{vis}#{def}"))
})(f.delimit_with("struct (", ")"))
}
Adt::UnitStruct => write!(f, "struct"),
Adt::Union(variants) => {
let mut variants = variants.iter();
separate(", ", || {
let (name, def) = variants.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{name}: #{def}"))
})(f.delimit_with("union {", "}"))
}
}
}
}
impl Display for Intrinsic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Intrinsic::I8 => f.write_str("i8"),
Intrinsic::I16 => f.write_str("i16"),
Intrinsic::I32 => f.write_str("i32"),
Intrinsic::I64 => f.write_str("i64"),
Intrinsic::Isize => f.write_str("isize"),
Intrinsic::U8 => f.write_str("u8"),
Intrinsic::U16 => f.write_str("u16"),
Intrinsic::U32 => f.write_str("u32"),
Intrinsic::U64 => f.write_str("u64"),
Intrinsic::Usize => f.write_str("usize"),
Intrinsic::Bool => f.write_str("bool"),
Intrinsic::Char => f.write_str("char"),
}
}
}

View File

@@ -0,0 +1,212 @@
//! An [Entry] is an accessor for [nodes](Handle) in a [Table].
//!
//! There are two kinds of entry:
//! - [Entry]: Provides getters for an entry's fields, and an implementation of
//! [Display](std::fmt::Display)
//! - [EntryMut]: Provides setters for an entry's fields, and an [`as_ref`](EntryMut::as_ref) method
//! to demote to an [Entry].
use std::collections::HashMap;
use cl_ast::{Expr, Meta, PathPart, Sym};
use cl_structures::span::Span;
use crate::{
handle::Handle,
source::Source,
stage::categorize as cat,
table::{NodeKind, Table},
type_expression::{self as tex, TypeExpression},
type_kind::TypeKind,
};
mod debug;
mod display;
impl Handle {
pub const fn to_entry<'t, 'a>(self, table: &'t Table<'a>) -> Entry<'t, 'a> {
Entry { id: self, table }
}
pub fn to_entry_mut<'t, 'a>(self, table: &'t mut Table<'a>) -> EntryMut<'t, 'a> {
EntryMut { id: self, table }
}
}
pub struct Entry<'t, 'a> {
table: &'t Table<'a>,
id: Handle,
}
macro_rules! impl_entry_ {
() => {
pub const fn id(&self) -> Handle {
self.id
}
pub const fn inner(&'t self) -> &'t Table<'a> {
self.table
}
pub fn kind(&self) -> Option<&NodeKind> {
self.table.kind(self.id)
}
pub const fn root(&self) -> Handle {
self.table.root()
}
pub fn children(&self) -> Option<&HashMap<Sym, Handle>> {
self.table.children(self.id)
}
pub fn imports(&self) -> Option<&HashMap<Sym, Handle>> {
self.table.imports(self.id)
}
pub fn bodies(&self) -> Option<&'a Expr> {
self.table.body(self.id)
}
pub fn span(&self) -> Option<&Span> {
self.table.span(self.id)
}
pub fn meta(&self) -> Option<&[Meta]> {
self.table.meta(self.id)
}
pub fn source(&self) -> Option<&Source<'a>> {
self.table.source(self.id)
}
pub fn name(&self) -> Option<Sym> {
self.table.name(self.id)
}
};
}
impl<'t, 'a> Entry<'t, 'a> {
pub const fn new(table: &'t Table<'a>, id: Handle) -> Self {
Self { table, id }
}
impl_entry_!();
pub const fn with_id(&self, id: Handle) -> Entry<'t, 'a> {
Self { table: self.table, id }
}
pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'t, 'a>> {
Some(Entry { id: self.table.nav(self.id, path)?, table: self.table })
}
pub fn parent(&self) -> Option<Entry<'t, 'a>> {
Some(Entry { id: *self.table.parent(self.id)?, ..*self })
}
pub fn ty(&self) -> Option<&'t TypeKind> {
self.table.ty(self.id)
}
pub fn impl_target(&self) -> Option<Entry<'_, 'a>> {
Some(Entry { id: self.table.impl_target(self.id)?, ..*self })
}
pub fn selfty(&self) -> Option<Entry<'_, 'a>> {
Some(Entry { id: self.table.selfty(self.id)?, ..*self })
}
}
#[derive(Debug)]
pub struct EntryMut<'t, 'a> {
table: &'t mut Table<'a>,
id: Handle,
}
impl<'t, 'a> EntryMut<'t, 'a> {
pub fn new(table: &'t mut Table<'a>, id: Handle) -> Self {
Self { table, id }
}
impl_entry_!();
pub fn ty(&self) -> Option<&TypeKind> {
self.table.ty(self.id)
}
pub fn inner_mut(&mut self) -> &mut Table<'a> {
self.table
}
pub fn as_ref(&self) -> Entry<'_, 'a> {
Entry { table: self.table, id: self.id }
}
/// Evaluates a [TypeExpression] in this entry's context
pub fn evaluate<Out>(&mut self, ty: &impl TypeExpression<Out>) -> Result<Out, tex::Error> {
let Self { table, id } = self;
ty.evaluate(table, *id)
}
pub fn categorize(&mut self) -> Result<(), cat::Error> {
cat::categorize(self.table, self.id)
}
/// Constructs a new Handle with the provided parent [Handle]
pub fn with_id(&mut self, parent: Handle) -> EntryMut<'_, 'a> {
EntryMut { table: self.table, id: parent }
}
pub fn nav(&mut self, path: &[PathPart]) -> Option<EntryMut<'_, 'a>> {
Some(EntryMut { id: self.table.nav(self.id, path)?, table: self.table })
}
pub fn new_entry(&mut self, kind: NodeKind) -> EntryMut<'_, 'a> {
let id = self.table.new_entry(self.id, kind);
self.with_id(id)
}
pub fn add_child(&mut self, name: Sym, child: Handle) -> Option<Handle> {
self.table.add_child(self.id, name, child)
}
pub fn set_body(&mut self, body: &'a Expr) -> Option<&'a Expr> {
self.table.set_body(self.id, body)
}
pub fn set_ty(&mut self, kind: TypeKind) -> Option<TypeKind> {
self.table.set_ty(self.id, kind)
}
pub fn set_span(&mut self, span: Span) -> Option<Span> {
self.table.set_span(self.id, span)
}
pub fn set_meta(&mut self, meta: &'a [Meta]) -> Option<&'a [Meta]> {
self.table.set_meta(self.id, meta)
}
pub fn set_source(&mut self, source: Source<'a>) -> Option<Source<'a>> {
self.table.set_source(self.id, source)
}
pub fn set_impl_target(&mut self, target: Handle) -> Option<Handle> {
self.table.set_impl_target(self.id, target)
}
pub fn mark_unchecked(&mut self) {
self.table.mark_unchecked(self.id)
}
pub fn mark_use_item(&mut self) {
self.table.mark_use_item(self.id)
}
pub fn mark_impl_item(&mut self) {
self.table.mark_impl_item(self.id)
}
pub fn mark_lang_item(&mut self, lang_item: &'static str) {
self.table.mark_lang_item(lang_item, self.id)
}
}

View File

@@ -0,0 +1,33 @@
//! [std::fmt::Debug] implementation for [Entry]
use super::Entry;
impl std::fmt::Debug for Entry<'_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// virtual fields
let mut ds = f.debug_struct("Entry");
if let Some(name) = self.name() {
ds.field("name", &name.to_ref());
}
ds.field("kind", &self.kind());
if let Some(ty) = self.ty() {
ds.field("type", ty);
}
if let Some(meta) = self.meta() {
ds.field("meta", &meta);
}
if let Some(body) = self.bodies() {
ds.field("body", body);
}
if let Some(children) = self.children() {
ds.field("children", children);
}
if let Some(imports) = self.imports() {
ds.field("imports", imports);
}
// if let Some(source) = self.source() {
// ds.field("source", source);
// }
ds.field("implements", &self.impl_target()).finish()
}
}

View File

@@ -0,0 +1,100 @@
use super::*;
use crate::{format_utils::*, type_kind::Adt};
use std::fmt::{self, Write};
/// Printing the name of a named type stops infinite recursion
fn write_name_or(h: Entry, f: &mut impl Write) -> fmt::Result {
match h.name() {
Some(name) => write!(f, "{name}"),
None => write!(f, "{h}"),
}
}
impl fmt::Display for Entry<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Some(&kind) = self.kind() else {
return write!(f, "<invalid type: {}>", self.id);
};
if let Some(ty) = self.ty() {
match ty {
TypeKind::Inferred => write!(f, "<_{}>", self.id),
TypeKind::Variable => write!(f, "<?{}>", self.id),
TypeKind::Instance(id) => write!(f, "{}", self.with_id(*id)),
TypeKind::Primitive(kind) => write!(f, "{kind}"),
TypeKind::Adt(adt) => write_adt(adt, self, f),
&TypeKind::Ref(id) => {
f.write_str("&")?;
let h_id = self.with_id(id);
write_name_or(h_id, f)
}
&TypeKind::Ptr(id) => {
f.write_str("*")?;
let h_id = self.with_id(id);
write_name_or(h_id, f)
}
TypeKind::Slice(id) => {
write_name_or(self.with_id(*id), &mut f.delimit_with("[", "]"))
}
&TypeKind::Array(t, cnt) => {
let mut f = f.delimit_with("[", "]");
write_name_or(self.with_id(t), &mut f)?;
write!(f, "; {cnt}")
}
TypeKind::Tuple(ids) => {
let mut f = f.delimit_with("(", ")");
for (index, &id) in ids.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write_name_or(self.with_id(id), &mut f)?;
}
Ok(())
}
TypeKind::FnSig { args, rety } => {
write!(f, "fn {} -> ", self.with_id(*args))?;
write_name_or(self.with_id(*rety), f)
}
TypeKind::Module => write!(f, "module?"),
}
} else {
match kind {
NodeKind::Type
| NodeKind::Const
| NodeKind::Static
| NodeKind::Temporary
| NodeKind::Let => write!(f, "WARNING: NO TYPE ASSIGNED FOR {}", self.id),
_ => write!(f, "{kind}"),
}
}
}
}
fn write_adt(adt: &Adt, h: &Entry, f: &mut impl Write) -> fmt::Result {
match adt {
Adt::Enum(variants) => {
let mut variants = variants.iter();
separate(", ", || {
variants.next().map(|(name, def)| {
move |f: &mut Delimit<_>| write!(f, "{name}: {}", h.with_id(*def))
})
})(f.delimit_with("enum {", "}"))
}
Adt::Struct(members) => {
let mut members = members.iter();
separate(", ", || {
let (name, vis, id) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{vis}{name}: {}", h.with_id(*id)))
})(f.delimit_with("struct {", "}"))
}
Adt::TupleStruct(members) => {
let mut members = members.iter();
separate(", ", || {
let (vis, def) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{vis}{}", h.with_id(*def)))
})(f.delimit_with("struct (", ")"))
}
Adt::UnitStruct => write!(f, "struct"),
Adt::Union(_) => todo!("Display union types"),
}
}

View File

@@ -2,10 +2,7 @@ pub use cl_ast::format::*;
use std::{fmt, iter};
/// Separates the items yielded by iterating the provided function
pub const fn separate<'f, 's, Item, F, W>(
sep: &'s str,
t: F,
) -> impl FnOnce(W) -> fmt::Result + 's
pub const fn separate<'f, 's, Item, F, W>(sep: &'s str, t: F) -> impl FnOnce(W) -> fmt::Result + 's
where
Item: FnMut(&mut W) -> fmt::Result,
F: FnMut() -> Option<Item> + 's,

View File

@@ -1,187 +1,15 @@
use crate::{definition::Def, path::Path, project::Project};
//! A [Handle] uniquely represents an entry in the [Table](crate::table::Table)
use cl_structures::index_map::*;
// define the index types
make_index! {
/// Uniquely represents a [Def][1] in the [Def][1] [Pool]
///
/// [1]: crate::definition::Def
DefID,
/// Uniquely represents an entry in the [Table](crate::table::Table)
Handle,
}
/// A handle to a certain [Def] within a [Project]
#[derive(Clone, Copy, Debug)]
pub struct Handle<'prj, 'code> {
id: DefID,
prj: &'prj Project<'code>,
}
impl DefID {
/// Constructs a new [Handle] from this DefID and the provided [Project].
pub fn handle<'p, 'c>(self, prj: &'p Project<'c>) -> Option<Handle<'p, 'c>> {
Handle::new(self, prj)
}
/// Constructs a new [Handle] from this DefID and the provided [Project]
pub fn handle_unchecked<'p, 'c>(self, prj: &'p Project<'c>) -> Handle<'p, 'c> {
Handle::new_unchecked(self, prj)
}
}
impl<'p, 'c> Handle<'p, 'c> {
/// Constructs a new Handle from the provided [DefID] and [Project].
/// Returns [Some]\(Handle) if the [DefID] exists in the [Project].
pub fn new(id: DefID, prj: &'p Project<'c>) -> Option<Self> {
prj.pool.get(id).is_some().then_some(Self { id, prj })
}
/// Constructs a new Handle from the provided [DefID] and [Project] without checking membership.
/// Using the handle may cause panics or other unwanted (but defined) behavior.
pub fn new_unchecked(id: DefID, prj: &'p Project<'c>) -> Self {
Self { id, prj }
}
/// Gets the [Def] this handle points to.
pub fn get(self) -> Option<&'p Def<'c>> {
self.prj.pool.get(self.id)
}
pub fn navigate(self, path: Path) -> (Option<Self>, Option<Self>) {
match self.prj.get(path, self.id) {
Some((Some(ty), Some(vl), _)) => (Some(self.with(ty)), Some(self.with(vl))),
Some((_, Some(vl), _)) => (None, Some(self.with(vl))),
Some((Some(ty), _, _)) => (Some(self.with(ty)), None),
_ => (None, None),
}
}
/// Gets the [Project] this handle points to.
pub fn project(self) -> &'p Project<'c> {
self.prj
}
pub fn id(self) -> DefID {
self.id
}
// TODO: get parent, children, etc.
/// Gets a handle to the other ID within the same project
pub fn with(self, id: DefID) -> Self {
Self { id, ..self }
}
}
mod display {
use super::*;
use crate::{definition::*, format_utils::*};
use std::fmt::{self, Display, Write};
impl Display for DefID {
impl std::fmt::Display for Handle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl Display for Handle<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { id, prj } = *self;
let Some(def) = prj.pool.get(id) else {
return write!(f, "<invalid type: {id}>");
};
// Match the type
match &def.kind {
DefKind::Undecided => write!(f, "<undecided>"),
DefKind::Impl(id) => write!(f, "impl {}", self.with(*id)),
DefKind::Use(id) => write!(f, "use inside {}", self.with(*id)),
DefKind::Type(kind) => match kind {
TypeKind::Alias(None) => write!(f, "type"),
TypeKind::Alias(Some(id)) => write!(f, "{}", self.with(*id)),
TypeKind::Intrinsic(intrinsic) => write!(f, "{intrinsic}"),
TypeKind::Adt(adt) => display_adt(self, adt, f),
TypeKind::Ref(count, id) => {
for _ in 0..*count {
f.write_char('&')?;
}
self.with(*id).fmt(f)
}
TypeKind::Array(id, size) => {
write!(f.delimit_with("[", "]"), "{}; {size}", self.with(*id))
}
TypeKind::Slice(id) => write!(f.delimit_with("[", "]"), "{}", self.with(*id)),
TypeKind::Tuple(ids) => {
let mut ids = ids.iter();
separate(", ", || {
let id = ids.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{}", self.with(*id)))
})(f.delimit_with("(", ")"))
}
TypeKind::FnSig { args, rety } => {
write!(f, "fn {} -> {}", self.with(*args), self.with(*rety))
}
TypeKind::Empty => write!(f, "()"),
TypeKind::Never => write!(f, "!"),
TypeKind::Module => match def.name() {
Some(name) => write!(f, "mod {name}"),
None => write!(f, "mod"),
},
},
DefKind::Value(kind) => match kind {
ValueKind::Const(id) => write!(f, "const {}", self.with(*id)),
ValueKind::Static(id) => write!(f, "static {}", self.with(*id)),
ValueKind::Local(id) => write!(f, "local {}", self.with(*id)),
ValueKind::Fn(id) => write!(f, "{}", self.with(*id)),
},
}
}
}
/// Formats an ADT: a continuation of [Handle::fmt]
fn display_adt(handle: &Handle, adt: &Adt, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match adt {
Adt::Enum(variants) => {
let mut variants = variants.iter();
separate(",", || {
variants.next().map(|(name, def)| {
move |f: &mut Delimit<_>| match def {
Some(def) => write!(f, "\n{name}: {}", handle.with(*def)),
None => write!(f, "\n{name}"),
}
})
})(f.delimit_with("enum {", "\n}"))
}
Adt::CLikeEnum(variants) => {
let mut variants = variants.iter();
separate(",", || {
let (name, descrim) = variants.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "\n{name} = {descrim}"))
})(f.delimit_with("enum {", "\n}"))
}
Adt::FieldlessEnum => write!(f, "enum"),
Adt::Struct(members) => {
let mut members = members.iter();
separate(",", || {
let (name, vis, id) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "\n{vis}{name}: {}", handle.with(*id)))
})(f.delimit_with("struct {", "\n}"))
}
Adt::TupleStruct(members) => {
let mut members = members.iter();
separate(", ", || {
let (vis, def) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{vis}{}", handle.with(*def)))
})(f.delimit_with("struct (", ")"))
}
Adt::UnitStruct => write!(f, "struct;"),
Adt::Union(variants) => {
let mut variants = variants.iter();
separate(",", || {
let (name, def) = variants.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "\n{name}: {}", handle.with(*def)))
})(f.delimit_with("union {", "\n}"))
}
}
}
}

View File

@@ -1,248 +0,0 @@
//! Implements type unification, used by the Hindley-Milner type inference algorithm
//!
//! Inspired by [rust-hindley-milner][1] and [hindley-milner-python][2]
//!
//! [1]: https://github.com/tcr/rust-hindley-milner/
//! [2]: https://github.com/rob-smallshire/hindley-milner-python
use cl_ast::Sym;
use core::fmt;
use std::{cell::RefCell, rc::Rc};
/*
Types in Conlang:
- Never type: !
- type !
- for<A> ! -> A
- Primitive types: bool, i32, (), ...
- type bool; ...
- Reference types: &T, *T
- for<T> type ref<T>; for<T> type ptr<T>
- Slice type: [T]
- for<T> type slice<T>
- Array type: [T;usize]
- for<T> type array<T, instanceof<usize>>
- Tuple type: (T, ...Z)
- for<T, ..> type tuple<T, ..> // on a per-case basis!
- Funct type: fn Tuple -> R
- for<T, R> type T -> R // on a per-case basis!
*/
/// A refcounted [Type]
pub type RcType = Rc<Type>;
#[derive(Debug, PartialEq, Eq)]
pub struct Variable {
pub instance: RefCell<Option<RcType>>,
}
#[derive(Debug, PartialEq, Eq)]
pub struct Operator {
name: Sym,
types: RefCell<Vec<RcType>>,
}
/// A [Type::Variable] or [Type::Operator]:
/// - A [Type::Variable] can be either bound or unbound (instance: Some(_) | None)
/// - A [Type::Operator] has a name (used to identify the operator) and a list of types.
///
/// A type which contains unbound variables is considered "generic" (see
/// [`Type::is_generic()`]).
#[derive(Debug, PartialEq, Eq)]
pub enum Type {
Variable(Variable),
Operator(Operator),
}
impl Type {
/// Creates a new unbound [type variable](Type::Variable)
pub fn new_var() -> RcType {
Rc::new(Self::Variable(Variable { instance: RefCell::new(None) }))
}
/// Creates a variable that is a new instance of another [Type]
pub fn new_inst(of: &RcType) -> RcType {
Rc::new(Self::Variable(Variable {
instance: RefCell::new(Some(of.clone())),
}))
}
/// Creates a new [type operator](Type::Operator)
pub fn new_op(name: Sym, types: &[RcType]) -> RcType {
Rc::new(Self::Operator(Operator {
name,
types: RefCell::new(types.to_vec()),
}))
}
/// Creates a new [type operator](Type::Operator) representing a lambda
pub fn new_fn(takes: &RcType, returns: &RcType) -> RcType {
Self::new_op("fn".into(), &[takes.clone(), returns.clone()])
}
/// Creates a new [type operator](Type::Operator) representing a primitive type
pub fn new_prim(name: Sym) -> RcType {
Self::new_op(name, &[])
}
/// Creates a new [type operator](Type::Operator) representing a tuple
pub fn new_tuple(members: &[RcType]) -> RcType {
Self::new_op("tuple".into(), members)
}
/// Sets this type variable to be an instance `of` the other
/// # Panics
/// Panics if `self` is not a type variable
pub fn set_instance(self: &RcType, of: &RcType) {
match self.as_ref() {
Type::Operator(_) => unimplemented!("Cannot set instance of a type operator"),
Type::Variable(Variable { instance }) => *instance.borrow_mut() = Some(of.clone()),
}
}
/// Checks whether there are any unbound type variables in this type.
/// ```rust
/// # use cl_typeck::inference::*;
/// let bool = Type::new_op("bool".into(), &[]);
/// let true_v = Type::new_inst(&bool);
/// let unbound = Type::new_var();
/// let id_fun = Type::new_fn(&unbound, &unbound);
/// let truthy = Type::new_fn(&unbound, &bool);
/// assert!(!bool.is_generic()); // bool contains no unbound type variables
/// assert!(!true_v.is_generic()); // true_v is bound to `bool`
/// assert!(unbound.is_generic()); // unbound is an unbound type variable
/// assert!(id_fun.is_generic()); // id_fun is a function with unbound type variables
/// assert!(truthy.is_generic()); // truthy is a function with one unbound type variable
/// ```
pub fn is_generic(self: &RcType) -> bool {
match self.as_ref() {
Type::Variable(Variable { instance }) => match instance.borrow().as_ref() {
// base case: self is an unbound type variable (instance is none)
None => true,
// Variable is bound to a type which may be generic
Some(instance) => instance.is_generic(),
},
Type::Operator(Operator { types, .. }) => {
// Operator may have generic args
types.borrow().iter().any(Self::is_generic)
}
}
}
/// Makes a deep copy of a type expression.
///
/// Bound variables are shared, unbound variables are duplicated.
pub fn deep_clone(self: &RcType) -> RcType {
// If there aren't any unbound variables, it's fine to clone the entire expression
if !self.is_generic() {
return self.clone();
}
// There are unbound type variables, so we make a new one
match self.as_ref() {
Type::Variable { .. } => Self::new_var(),
Type::Operator(Operator { name, types }) => Self::new_op(
*name,
&types
.borrow()
.iter()
.map(Self::deep_clone)
.collect::<Vec<_>>(),
),
}
}
/// Returns the defining instance of `self`,
/// collapsing type instances along the way.
/// # May panic
/// Panics if this type variable's instance field is already borrowed.
/// # Examples
/// ```rust
/// # use cl_typeck::inference::*;
/// let t_bool = Type::new_op("bool".into(), &[]);
/// let t_nest = Type::new_inst(&Type::new_inst(&Type::new_inst(&t_bool)));
/// let pruned = t_nest.prune();
/// assert_eq!(pruned, t_bool);
/// assert_eq!(t_nest, Type::new_inst(&t_bool));
/// ```
pub fn prune(self: &RcType) -> RcType {
if let Type::Variable(Variable { instance }) = self.as_ref() {
if let Some(old_inst) = instance.borrow_mut().as_mut() {
let new_inst = old_inst.prune(); // get defining instance
*old_inst = new_inst.clone(); // collapse
return new_inst;
}
}
self.clone()
}
/// Checks whether a type expression occurs in another type expression
///
/// # Note:
/// - Since the test uses strict equality, `self` should be pruned prior to testing.
/// - The test is *not guaranteed to terminate* for recursive types.
pub fn occurs_in(self: &RcType, other: &RcType) -> bool {
if self == other {
return true;
}
match other.as_ref() {
Type::Variable(Variable { instance }) => match instance.borrow().as_ref() {
Some(t) => self.occurs_in(t),
None => false,
},
Type::Operator(Operator { types, .. }) => {
// Note: this might panic.
// Think about whether it panics for only recursive types?
types.borrow().iter().any(|other| self.occurs_in(other))
}
}
}
/// Unifies two type expressions, propagating changes via interior mutability
pub fn unify(self: &RcType, other: &RcType) -> Result<(), InferenceError> {
let (a, b) = (self.prune(), other.prune()); // trim the hedges
match (a.as_ref(), b.as_ref()) {
(Type::Variable { .. }, _) if !a.occurs_in(&b) => a.set_instance(&b),
(Type::Variable { .. }, _) => Err(InferenceError::Recursive(a, b))?,
(Type::Operator { .. }, Type::Variable { .. }) => b.unify(&a)?,
(
Type::Operator(Operator { name: a_name, types: a_types }),
Type::Operator(Operator { name: b_name, types: b_types }),
) => {
let (a_types, b_types) = (a_types.borrow(), b_types.borrow());
if a_name != b_name || a_types.len() != b_types.len() {
Err(InferenceError::Mismatch(a.clone(), b.clone()))?
}
for (a, b) in a_types.iter().zip(b_types.iter()) {
a.unify(b)?
}
}
}
Ok(())
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::Variable(Variable { instance }) => match instance.borrow().as_ref() {
Some(instance) => write!(f, "{instance}"),
None => write!(f, "_"),
},
Type::Operator(Operator { name, types }) => {
write!(f, "({name}")?;
for ty in types.borrow().iter() {
write!(f, " {ty}")?;
}
f.write_str(")")
}
}
}
}
/// An error produced during type inference
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InferenceError {
Mismatch(RcType, RcType),
Recursive(RcType, RcType),
}
impl fmt::Display for InferenceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InferenceError::Mismatch(a, b) => write!(f, "Type mismatch: {a:?} != {b:?}"),
InferenceError::Recursive(_, _) => write!(f, "Recursive type!"),
}
}
}

View File

@@ -4,154 +4,71 @@
//!
//! This crate is a major work-in-progress.
//!
//! # The [Project](project::Project)™
//! Contains [item definitions](definition) and type expression information.
//! # The [Table](table::Table)™
//! A directed graph of nodes and their dependencies.
//!
//! *Every* definition is itself a module, and can contain arbitrarily nested items
//! as part of the [Module](module::Module) tree.
//! Contains [item definitions](handle) and [type expression](type_expression) information.
//!
//! The Project keeps track of a *global intern pool* of definitions, which are
//! trivially comparable by [DefID](key::DefID). Note that, for item definitions,
//! identical types in different modules DO NOT COMPARE EQUAL under this constraint.
//! However, so-called "anonymous" types *do not* follow this rule, as their
//! definitions are constructed dynamically and ensured to be unique.
// Note: it's a class invariant that named types are not added
// to the anon-types list. Please keep it that way. ♥ Thanks!
//! *Every* item is itself a module, and can contain arbitrarily nested items
//! as part of the item graph
//!
//! The table, additionally, has some queues for use in external algorithms,
//! detailed in the [stage] module.
//!
//! # Namespaces
//! Within a Project, [definitions](definition::Def) are classified into two namespaces:
//!
//! ## Type Namespace:
//! - Modules
//! - Structs
//! - Enums
//! - Type aliases
//!
//! ## Value Namespace:
//! - Functions
//! - Constants
//! - Static variables
//!
//! There is a *key* distinction between the two namespaces when it comes to
//! [Path](path::Path) traversal: Only items in the Type Namespace will be considered
//! as an intermediate path target. This means items in the Value namespace are
//! entirely *opaque*, and form a one-way barrier. Items outside a Value can be
//! referred to within the Value, but items inside a Value *cannot* be referred to
//! outside the Value.
//! Each item in the graph is given its own namespace, which is further separated into
//! two distinct parts:
//! - Children of an item are direct descendents (i.e. their `parent` is a handle to the item)
//! - Imports of an item are indirect descendents created by `use` or `impl` directives. They are
//! shadowed by Children with the same name.
//!
//! # Order of operations:
//! Currently, the process of type resolution goes as follows:
//!
//! 1. Traverse the AST, [collecting all Items into item definitions](name_collector)
//! 2. Eagerly [resolve `use` imports](use_importer)
//! 3. Traverse all [definitions](definition::Def), and [resolve the types for every
//! item](type_resolver)
//! 4. TODO: Construct a typed AST for expressions, and type-check them
//! For order-of-operations information, see the [stage] module.
#![warn(clippy::all)]
/*
How do I flesh out modules in an incremental way?
1. Visit all *modules* and create nodes in a module tree for them
- This can be done by holding mutable references to submodules!
Module:
values: Map(name -> Value),
types: Map(name -> Type),
Value: Either
function: { signature: Type, body: FunctionBody }
static: { Mutability, ty: Type, init: ConstEvaluationResult }
const: { ty: Type, init: ConstEvaluationResult }
*/
pub mod handle;
pub mod node;
pub mod definition;
pub mod module;
pub mod path;
pub mod project;
pub mod name_collector;
pub mod use_importer;
pub mod type_resolver;
pub mod inference;
pub(crate) mod format_utils;
/*
pub mod table;
LET THERE BE NOTES:
pub mod handle;
/// What is an inference rule?
/// An inference rule is a specification with a set of predicates and a judgement
pub mod entry;
/// Let's give every type an ID
struct TypeID(usize);
pub mod source;
/// Let's give every type some data:
pub mod type_kind;
struct TypeDef<'def> {
name: String,
definition: &'def Item,
pub mod type_expression;
pub mod stage {
//! Type collection, evaluation, checking, and inference passes.
//!
//! # Order of operations
//! 1. [mod@populate]: Populate the graph with nodes for every named item.
//! 2. [mod@import]: Import the `use` nodes discovered in [Stage 1](populate).
//! 3. [mod@categorize]: Categorize the nodes according to textual type information.
//! - Creates anonymous types (`fn(T) -> U`, `&T`, `[T]`, etc.) as necessary to fill in the
//! type graph
//! - Creates a new struct type for every enum struct-variant.
//! 4. [mod@implement]: Import members of implementation modules into types.
pub use populate::Populator;
/// Stage 1: Populate the graph with nodes.
pub mod populate;
pub use import::import;
/// Stage 2: Import the `use` nodes discovered in Stage 1.
pub mod import;
pub use categorize::categorize;
/// Stage 3: Categorize the nodes according to textual type information.
pub mod categorize;
pub use implement::implement;
/// Stage 4: Import members of `impl` blocks into their corresponding types.
pub mod implement;
// TODO: Make type inference stage 5
// TODO: Use the type information stored in the [table]
pub mod infer;
}
and store them in a big vector of type descriptions:
struct TypeMap<'def> {
types: Vec<TypeDef<'def>>,
}
// todo: insertion of a type should yield a TypeID
// todo: impl index with TypeID
Let's store type information as either a concrete type or a generic type:
/// The Type struct represents all valid types, and can be trivially equality-compared
pub struct Type {
/// You can only have a pointer chain 65535 pointers long.
ref_depth: u16,
kind: TKind,
}
pub enum TKind {
Concrete(TypeID),
Generic(usize),
}
And assume I can specify a rule based on its inputs and outputs:
Rule {
operation: If,
/// The inputs field is populated by
inputs: [Concrete(BOOL), Generic(0), Generic(0)],
outputs: Generic(0),
/// This rule is compiler-intrinsic!
through: None,
}
Rule {
operation: Add,
inputs: [Concrete(I32), Concrete(I32)],
outputs: Concrete(I32),
/// This rule is not compiler-intrinsic (it is overloaded!)
through: Some(&ImplAddForI32::Add),
}
These rules can be stored in some kind of rule database:
let rules: Hashmap<Operation, Vec<Rule>> {
}
*/

View File

@@ -1,71 +0,0 @@
//! A [Module] is a node in the Module Tree (a component of a
//! [Project](crate::project::Project))
use cl_ast::Sym;
use cl_structures::index_map::MapIndex;
use crate::handle::DefID;
use std::collections::HashMap;
/// A [Module] is a node in the Module Tree (a component of a
/// [Project](crate::project::Project)).
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Module {
pub parent: Option<DefID>,
pub types: HashMap<Sym, DefID>,
pub values: HashMap<Sym, DefID>,
pub imports: Vec<DefID>,
}
impl Module {
pub fn new(parent: DefID) -> Self {
Self { parent: Some(parent), ..Default::default() }
}
pub fn with_optional_parent(parent: Option<DefID>) -> Self {
Self { parent, ..Default::default() }
}
pub fn get(&self, name: Sym) -> (Option<DefID>, Option<DefID>) {
(self.get_type(name), self.get_value(name))
}
pub fn get_type(&self, name: Sym) -> Option<DefID> {
self.types.get(&name).copied()
}
pub fn get_value(&self, name: Sym) -> Option<DefID> {
self.values.get(&name).copied()
}
/// Inserts a type with the provided [name](str) and [id](DefID)
pub fn insert_type(&mut self, name: Sym, id: DefID) -> Option<DefID> {
self.types.insert(name, id)
}
/// Inserts a value with the provided [name](str) and [id](DefID)
pub fn insert_value(&mut self, name: Sym, id: DefID) -> Option<DefID> {
self.values.insert(name, id)
}
}
impl std::fmt::Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let Self { parent, types, values, imports } = self;
if let Some(parent) = parent {
writeln!(f, "Parent: {}", parent.get())?;
}
for (name, table) in [("Types", types), ("Values", values)] {
if table.is_empty() {
continue;
}
writeln!(f, "{name}:")?;
for (name, id) in table.iter() {
writeln!(f, " {name} => {id}")?;
}
}
if !imports.is_empty() {
write!(f, "Imports:")?;
for id in imports {
write!(f, "{id},")?;
}
}
Ok(())
}
}

View File

@@ -1,218 +0,0 @@
//! Performs step 1 of type checking: Collecting all the names of things into [Module] units
use crate::{
definition::{Def, DefKind},
handle::DefID,
module::Module as Mod,
node::{Node, NodeSource},
project::Project as Prj,
};
use cl_ast::{ast_visitor::Visit, *};
use std::mem;
#[derive(Debug)]
pub struct NameCollector<'prj, 'a> {
path: cl_ast::Path,
prj: &'prj mut Prj<'a>,
parent: DefID,
retval: Option<DefID>,
}
impl<'prj, 'a> NameCollector<'prj, 'a> {
/// Constructs a new [NameCollector] out of a [Project](Prj)
pub fn new(prj: &'prj mut Prj<'a>) -> Self {
Self { parent: prj.root, prj, path: Default::default(), retval: None }
}
/// Constructs a new [NameCollector] out of a [Project](Prj) and a parent [DefID]
pub fn with_root(prj: &'prj mut Prj<'a>, parent: DefID) -> Self {
Self { prj, parent, path: Default::default(), retval: None }
}
/// Runs the provided function with the given parent
pub fn with_parent<F, N>(&mut self, parent: DefID, node: N, f: F)
where F: FnOnce(&mut Self, N) {
let parent = mem::replace(&mut self.parent, parent);
f(self, node);
self.parent = parent;
}
/// Extracts the return value from the provided function
pub fn returns<F, N>(&mut self, node: N, f: F) -> Option<DefID>
where F: FnOnce(&mut Self, N) {
let out = self.retval.take();
f(self, node);
mem::replace(&mut self.retval, out)
}
}
impl<'prj, 'a> Visit<'a> for NameCollector<'prj, 'a> {
fn visit_item(&mut self, i: &'a Item) {
let Item { extents: _, attrs, vis, kind } = i;
if let Some(def) = self.returns(kind, Self::visit_item_kind) {
self.prj[def].set_meta(&attrs.meta).set_vis(*vis);
}
}
fn visit_module(&mut self, m: &'a Module) {
let Self { prj, parent, path, retval: _ } = self;
let Module { name, kind } = m;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Module(m))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.path.push(PathPart::Ident(*name));
self.with_parent(id, kind, Self::visit_module_kind);
self.path.pop();
self.retval = Some(id);
}
fn visit_alias(&mut self, a: &'a Alias) {
let Self { prj, parent, path, retval: _ } = self;
let Alias { to: name, from: _ } = a;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Alias(a))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.retval = Some(id);
}
fn visit_enum(&mut self, e: &'a Enum) {
let Self { prj, parent, path, retval: _ } = self;
let Enum { name, kind } = e;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Enum(e))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.with_parent(id, kind, Self::visit_enum_kind);
self.retval = Some(id);
}
fn visit_variant(&mut self, v: &'a Variant) {
let Self { path, prj, parent, retval: _ } = self;
let Variant { name, kind } = v;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Variant(v))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.with_parent(id, kind, Self::visit_variant_kind);
self.retval = Some(id);
}
fn visit_struct(&mut self, s: &'a Struct) {
let Self { prj, parent, path, retval: _ } = self;
let Struct { name, kind } = s;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Struct(s))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_type(*name, id);
self.with_parent(id, kind, Self::visit_struct_kind);
self.retval = Some(id);
}
fn visit_const(&mut self, c: &'a Const) {
let Self { prj, parent, path, retval: _ } = self;
let Const { name, ty: _, init } = c;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Const(c))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_value(*name, id);
self.with_parent(id, &**init, Self::visit_expr);
self.retval = Some(id);
}
fn visit_static(&mut self, s: &'a Static) {
let Self { prj, parent, path, retval: _ } = self;
let Static { name, mutable: _, ty: _, init } = s;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Static(s))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_value(*name, id);
self.with_parent(id, &**init, Self::visit_expr);
self.retval = Some(id);
}
fn visit_function(&mut self, f: &'a Function) {
let Self { prj, parent, path, retval: _ } = self;
let Function { name, body, .. } = f;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Function(f))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_value(*name, id);
if let Some(body) = body {
self.with_parent(id, body, Self::visit_block);
}
self.retval = Some(id);
}
fn visit_impl(&mut self, i: &'a Impl) {
let Self { prj, parent, path, retval: _ } = self;
let Impl { target: _, body } = i;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Impl(i))),
};
let id = prj.pool.insert(def);
// items will get reparented after name collection, when target is available
self.with_parent(id, body, Self::visit_file);
self.retval = Some(id);
}
fn visit_use(&mut self, u: &'a Use) {
let Self { prj, parent, path, retval } = self;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Use(*parent),
node: Node::new(path.clone(), Some(NodeSource::Use(u))),
};
let id = prj.pool.insert(def);
prj[*parent].module.imports.push(id);
*retval = Some(id);
}
fn visit_let(&mut self, l: &'a Let) {
let Self { prj, parent, path, retval: _ } = self;
let Let { name, init, .. } = l;
let def = Def {
module: Mod::new(*parent),
kind: DefKind::Undecided,
node: Node::new(path.clone(), Some(NodeSource::Local(l))),
};
let id = prj.pool.insert(def);
prj[*parent].module.insert_value(*name, id);
if let Some(expr) = init {
self.visit_expr(expr)
}
self.retval = Some(id)
}
}

View File

@@ -1,214 +0,0 @@
//! A [Node] contains the [NodeSource] and [Item] metadata for any
//! [Def](crate::definition::Def), as well as the [Path] of the
//! containing [Module].
//!
//! [Node]s are collected by the [Node Sorcerer](sorcerer),
//! an AST visitor that pairs [NodeSource]s with their surrounding
//! context ([Path], [struct@Span], [Meta], [Visibility])
use cl_ast::ast::*;
use cl_structures::span::Span;
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Node<'a> {
pub in_path: Path,
pub span: &'a Span,
pub meta: &'a [Meta],
pub vis: Visibility,
pub kind: Option<NodeSource<'a>>,
}
impl<'a> Node<'a> {
pub fn new(path: Path, kind: Option<NodeSource<'a>>) -> Self {
const DUMMY_SPAN: Span = Span::dummy();
Self { in_path: path, span: &DUMMY_SPAN, meta: &[], vis: Visibility::Public, kind }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NodeSource<'a> {
Root,
Module(&'a Module),
Alias(&'a Alias),
Enum(&'a Enum),
Variant(&'a Variant),
Struct(&'a Struct),
Const(&'a Const),
Static(&'a Static),
Function(&'a Function),
Local(&'a Let),
Impl(&'a Impl),
Use(&'a Use),
Ty(&'a TyKind),
}
impl<'a> NodeSource<'a> {
pub fn name(&self) -> Option<Sym> {
match self {
NodeSource::Root => None,
NodeSource::Module(v) => Some(v.name),
NodeSource::Alias(v) => Some(v.to),
NodeSource::Enum(v) => Some(v.name),
NodeSource::Variant(v) => Some(v.name),
NodeSource::Struct(v) => Some(v.name),
NodeSource::Const(v) => Some(v.name),
NodeSource::Static(v) => Some(v.name),
NodeSource::Function(v) => Some(v.name),
NodeSource::Local(l) => Some(l.name),
NodeSource::Impl(_) | NodeSource::Use(_) | NodeSource::Ty(_) => None,
}
}
/// Returns `true` if this [NodeSource] defines a named value
pub fn is_named_value(&self) -> bool {
matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_))
}
/// Returns `true` if this [NodeSource] defines a named type
pub fn is_named_type(&self) -> bool {
matches!(
self,
Self::Module(_) | Self::Alias(_) | Self::Enum(_) | Self::Struct(_)
)
}
/// Returns `true` if this [NodeSource] refers to a [Ty] with no name
pub fn is_anon_type(&self) -> bool {
matches!(self, Self::Ty(_))
}
/// Returns `true` if this [NodeSource] refers to an [Impl] block
pub fn is_impl(&self) -> bool {
matches!(self, Self::Impl(_))
}
/// Returns `true` if this [NodeSource] refers to a [Use] import
pub fn is_use_import(&self) -> bool {
matches!(self, Self::Use(_))
}
}
impl fmt::Display for NodeSource<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Root => "🌳 root 🌳".fmt(f),
Self::Module(arg0) => arg0.fmt(f),
Self::Alias(arg0) => arg0.fmt(f),
Self::Enum(arg0) => arg0.fmt(f),
Self::Variant(arg0) => arg0.fmt(f),
Self::Struct(arg0) => arg0.fmt(f),
Self::Const(arg0) => arg0.fmt(f),
Self::Static(arg0) => arg0.fmt(f),
Self::Function(arg0) => arg0.fmt(f),
Self::Impl(arg0) => arg0.fmt(f),
Self::Use(arg0) => arg0.fmt(f),
Self::Ty(arg0) => arg0.fmt(f),
Self::Local(arg0) => arg0.fmt(f),
}
}
}
pub mod sorcerer {
//! An [AST](cl_ast) analysis pass that collects [Node] entries.
use super::{Node, NodeSource};
use cl_ast::{ast::*, ast_visitor::visit::*};
use cl_structures::span::Span;
use std::mem;
/// An AST analysis pass that collects [Node]s
#[derive(Clone, Debug)]
pub struct NodeSorcerer<'a> {
path: Path,
parts: Parts<'a>,
defs: Vec<Node<'a>>,
}
type Parts<'a> = (&'a Span, &'a [Meta], Visibility);
impl<'a> NodeSorcerer<'a> {
pub fn into_defs(self) -> Vec<Node<'a>> {
self.defs
}
fn with_parts<F>(&mut self, s: &'a Span, a: &'a [Meta], v: Visibility, f: F)
where F: FnOnce(&mut Self) {
let parts = mem::replace(&mut self.parts, (s, a, v));
f(self);
self.parts = parts;
}
fn with_only_span<F>(&mut self, span: &'a Span, f: F)
where F: FnOnce(&mut Self) {
self.with_parts(span, &[], Visibility::Public, f)
}
fn push(&mut self, kind: NodeSource<'a>) {
let Self { path, parts, defs } = self;
let (span, meta, vis) = *parts;
defs.push(Node { in_path: path.clone(), span, meta, vis, kind: Some(kind) })
}
}
impl Default for NodeSorcerer<'_> {
fn default() -> Self {
const DPARTS: Parts = (&Span::dummy(), &[], Visibility::Private);
Self {
path: Path { absolute: true, ..Default::default() },
parts: DPARTS,
defs: Default::default(),
}
}
}
impl<'a> Visit<'a> for NodeSorcerer<'a> {
fn visit_module(&mut self, m: &'a Module) {
let Module { name, kind } = m;
self.path.push(PathPart::Ident(*name));
self.visit_module_kind(kind);
self.path.pop();
}
fn visit_item(&mut self, i: &'a Item) {
let Item { extents, attrs, vis, kind } = i;
self.with_parts(extents, &attrs.meta, *vis, |v| {
v.visit_item_kind(kind);
});
}
fn visit_ty(&mut self, t: &'a Ty) {
let Ty { extents, kind } = t;
self.with_only_span(extents, |v| {
v.push(NodeSource::Ty(kind));
v.visit_ty_kind(kind);
});
}
fn visit_stmt(&mut self, s: &'a Stmt) {
let Stmt { extents, kind, semi } = s;
self.with_only_span(extents, |d| {
d.visit_stmt_kind(kind);
d.visit_semi(semi);
})
}
fn visit_item_kind(&mut self, kind: &'a ItemKind) {
match kind {
ItemKind::Module(i) => self.push(NodeSource::Module(i)),
ItemKind::Alias(i) => self.push(NodeSource::Alias(i)),
ItemKind::Enum(i) => self.push(NodeSource::Enum(i)),
ItemKind::Struct(i) => self.push(NodeSource::Struct(i)),
ItemKind::Const(i) => self.push(NodeSource::Const(i)),
ItemKind::Static(i) => self.push(NodeSource::Static(i)),
ItemKind::Function(i) => self.push(NodeSource::Function(i)),
ItemKind::Impl(i) => self.push(NodeSource::Impl(i)),
ItemKind::Use(i) => self.push(NodeSource::Use(i)),
}
or_visit_item_kind(self, kind);
}
fn visit_stmt_kind(&mut self, kind: &'a StmtKind) {
if let StmtKind::Local(l) = kind {
self.push(NodeSource::Local(l))
}
or_visit_stmt_kind(self, kind);
}
}
}

View File

@@ -1,60 +0,0 @@
//! A [Path] is a borrowed view of an [AST Path](AstPath)
use cl_ast::{Path as AstPath, PathPart};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Path<'p> {
pub absolute: bool,
pub parts: &'p [PathPart],
}
impl<'p> Path<'p> {
pub fn new(path: &'p AstPath) -> Self {
let AstPath { absolute, parts } = path;
Self { absolute: *absolute, parts }
}
pub fn relative(self) -> Self {
Self { absolute: false, ..self }
}
pub fn pop_front(self) -> Option<Self> {
let Self { absolute, parts } = self;
Some(Self { absolute, parts: parts.get(1..)? })
}
pub fn front(self) -> Option<Self> {
let Self { absolute, parts } = self;
Some(Self { absolute, parts: parts.get(..1)? })
}
pub fn is_empty(&self) -> bool {
self.parts.is_empty()
}
pub fn len(&self) -> usize {
self.parts.len()
}
pub fn first(&self) -> Option<&PathPart> {
self.parts.first()
}
}
impl<'p> From<&'p AstPath> for Path<'p> {
fn from(value: &'p AstPath) -> Self {
Self::new(value)
}
}
impl AsRef<[PathPart]> for Path<'_> {
fn as_ref(&self) -> &[PathPart] {
self.parts
}
}
impl std::fmt::Display for Path<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
const SEPARATOR: &str = "::";
let Self { absolute, parts } = self;
if *absolute {
write!(f, "{SEPARATOR}")?
}
for (idx, part) in parts.iter().enumerate() {
write!(f, "{}{part}", if idx > 0 { SEPARATOR } else { "" })?;
}
Ok(())
}
}

View File

@@ -1,376 +0,0 @@
//! A [Project] contains a tree of [Def]initions, referred to by their [Path]
use crate::{
definition::{Def, DefKind, TypeKind},
handle::DefID,
module,
node::{Node, NodeSource},
path::Path,
};
use cl_ast::PathPart;
use cl_structures::index_map::IndexMap;
use std::{
collections::HashMap,
ops::{Index, IndexMut},
};
use self::evaluate::EvaluableTypeExpression;
#[derive(Clone, Debug)]
pub struct Project<'a> {
pub pool: IndexMap<DefID, Def<'a>>,
/// Stores anonymous tuples, function pointer types, etc.
pub anon_types: HashMap<TypeKind, DefID>,
pub root: DefID,
}
impl Project<'_> {
pub fn new() -> Self {
Self::default()
}
}
impl Default for Project<'_> {
fn default() -> Self {
const ROOT_PATH: cl_ast::Path = cl_ast::Path { absolute: true, parts: Vec::new() };
let mut pool = IndexMap::default();
let root = pool.insert(Def {
module: Default::default(),
kind: DefKind::Type(TypeKind::Module),
node: Node::new(ROOT_PATH, Some(NodeSource::Root)),
});
let never = pool.insert(Def {
module: module::Module::new(root),
kind: DefKind::Type(TypeKind::Never),
node: Node::new(ROOT_PATH, None),
});
let empty = pool.insert(Def {
module: module::Module::new(root),
kind: DefKind::Type(TypeKind::Empty),
node: Node::new(ROOT_PATH, None),
});
let mut anon_types = HashMap::new();
anon_types.insert(TypeKind::Empty, empty);
anon_types.insert(TypeKind::Never, never);
Self { pool, root, anon_types }
}
}
impl<'a> Project<'a> {
pub fn parent_of(&self, module: DefID) -> Option<DefID> {
self[module].module.parent
}
pub fn root_of(&self, module: DefID) -> DefID {
match self.parent_of(module) {
Some(module) => self.root_of(module),
None => module,
}
}
/// Returns the DefID of the Self type within the given DefID's context
pub fn selfty_of(&self, node: DefID) -> Option<DefID> {
match self[node].kind {
DefKind::Impl(id) => Some(id),
DefKind::Type(_) => Some(node),
_ => self.selfty_of(self.parent_of(node)?),
}
}
pub fn get<'p>(
&self,
path: Path<'p>,
within: DefID,
) -> Option<(Option<DefID>, Option<DefID>, Path<'p>)> {
if path.absolute {
return self.get(path.relative(), self.root_of(within));
}
match path.as_ref() {
[PathPart::SuperKw, ..] => self.get(path.pop_front()?, self.parent_of(within)?),
[PathPart::SelfTy, ..] => self.get(path.pop_front()?, self.selfty_of(within)?),
[PathPart::SelfKw, ..] => self.get(path.pop_front()?, within),
[PathPart::Ident(name)] => {
let (ty, val) = self[within].module.get(*name);
// Transparent nodes can be looked through in reverse
if self[within].is_transparent() {
let lookback = self.parent_of(within).and_then(|p| self.get(path, p));
if let Some((subty, subval, path)) = lookback {
return Some((ty.or(subty), val.or(subval), path));
}
}
Some((ty, val, path.pop_front()?))
}
[PathPart::Ident(name), ..] => {
// TODO: This is currently too permissive, and treats undecided nodes as if they're
// always transparent, among other issues.
let (tysub, _, _) = match self[within].is_transparent() {
true => self.get(path.front()?, within)?,
false => (None, None, path),
};
let ty = self[within].module.get_type(*name).or(tysub)?;
self.get(path.pop_front()?, ty)
}
[] => Some((Some(within), None, path)),
}
}
/// Resolves a path within a module tree, finding the innermost module.
/// Returns the remaining path parts.
pub fn get_type<'p>(&self, path: Path<'p>, within: DefID) -> Option<(DefID, Path<'p>)> {
if path.absolute {
self.get_type(path.relative(), self.root_of(within))
} else if let Some(front) = path.first() {
let module = &self[within].module;
match front {
PathPart::SelfKw => self.get_type(path.pop_front()?, within),
PathPart::SuperKw => self.get_type(path.pop_front()?, module.parent?),
PathPart::SelfTy => self.get_type(path.pop_front()?, self.selfty_of(within)?),
PathPart::Ident(name) => match module.types.get(name) {
Some(&submodule) => self.get_type(path.pop_front()?, submodule),
None => Some((within, path)),
},
}
} else {
Some((within, path))
}
}
/// Inserts the type returned by the provided closure iff the TypeKind doesn't already exist
///
/// Assumes `kind` uniquely identifies the type!
pub fn insert_anonymous_type(
&mut self,
kind: TypeKind,
def: impl FnOnce() -> Def<'a>,
) -> DefID {
*(self
.anon_types
.entry(kind)
.or_insert_with(|| self.pool.insert(def())))
}
pub fn evaluate<T>(&mut self, expr: &T, parent: DefID) -> Result<T::Out, String>
where T: EvaluableTypeExpression {
expr.evaluate(self, parent)
}
}
impl<'a> Index<DefID> for Project<'a> {
type Output = Def<'a>;
fn index(&self, index: DefID) -> &Self::Output {
&self.pool[index]
}
}
impl IndexMut<DefID> for Project<'_> {
fn index_mut(&mut self, index: DefID) -> &mut Self::Output {
&mut self.pool[index]
}
}
pub mod evaluate {
//! An [EvaluableTypeExpression] is a component of a type expression tree
//! or an intermediate result of expression evaluation.
use super::*;
use crate::module;
use cl_ast::{Sym, Ty, TyArray, TyFn, TyKind, TyRef, TySlice, TyTuple};
/// Things that can be evaluated as a type expression
pub trait EvaluableTypeExpression {
/// The result of type expression evaluation
type Out;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String>;
}
impl EvaluableTypeExpression for Ty {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, id: DefID) -> Result<DefID, String> {
self.kind.evaluate(prj, id)
}
}
impl EvaluableTypeExpression for TyKind {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> {
let id = match self {
// TODO: reduce duplication here
TyKind::Never => prj.anon_types[&TypeKind::Never],
TyKind::Empty => prj.anon_types[&TypeKind::Empty],
// TyKind::Path must be looked up explicitly
TyKind::Path(path) => path.evaluate(prj, parent)?,
TyKind::Slice(slice) => slice.evaluate(prj, parent)?,
TyKind::Array(array) => array.evaluate(prj, parent)?,
TyKind::Tuple(tup) => tup.evaluate(prj, parent)?,
TyKind::Ref(tyref) => tyref.evaluate(prj, parent)?,
TyKind::Fn(tyfn) => tyfn.evaluate(prj, parent)?,
};
// println!("{self} => {id:?}");
Ok(id)
}
}
impl EvaluableTypeExpression for Sym {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
prj[parent]
.module
.types
.get(self)
.copied()
.ok_or_else(|| format!("{self} is not a member of {:?}", prj[parent].name()))
}
}
impl EvaluableTypeExpression for TySlice {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
let ty = self.ty.evaluate(prj, parent)?;
let root = prj.root;
let id = prj.insert_anonymous_type(TypeKind::Slice(ty), move || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(TypeKind::Slice(ty)),
});
Ok(id)
}
}
impl EvaluableTypeExpression for TyArray {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
let kind = TypeKind::Array(self.ty.evaluate(prj, parent)?, self.count);
let root = prj.root;
let id = prj.insert_anonymous_type(kind.clone(), move || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(kind),
});
Ok(id)
}
}
impl EvaluableTypeExpression for TyTuple {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> {
let types = self.types.evaluate(prj, parent)?;
let root = prj.root;
let id = prj.insert_anonymous_type(TypeKind::Tuple(types.clone()), move || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(TypeKind::Tuple(types)),
});
Ok(id)
}
}
impl EvaluableTypeExpression for TyRef {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> {
let TyRef { count, mutable: _, to } = self;
let to = to.evaluate(prj, parent)?;
let root = prj.root;
let id = prj.insert_anonymous_type(TypeKind::Ref(*count, to), move || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(TypeKind::Ref(*count, to)),
});
Ok(id)
}
}
impl EvaluableTypeExpression for TyFn {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<DefID, String> {
let TyFn { args, rety } = self;
let args = args.evaluate(prj, parent)?;
let rety = match rety {
Some(rety) => rety.evaluate(prj, parent)?,
_ => TyKind::Empty.evaluate(prj, parent)?,
};
let root = prj.root;
let id = prj.insert_anonymous_type(TypeKind::FnSig { args, rety }, || Def {
module: module::Module::new(root),
node: Node::new(Default::default(), None),
kind: DefKind::Type(TypeKind::FnSig { args, rety }),
});
Ok(id)
}
}
impl EvaluableTypeExpression for cl_ast::Path {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
Path::from(self).evaluate(prj, parent)
}
}
impl EvaluableTypeExpression for PathPart {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
match self {
PathPart::SuperKw => prj
.parent_of(parent)
.ok_or_else(|| "Attempt to get super of root".into()),
PathPart::SelfKw => Ok(parent),
PathPart::SelfTy => prj
.selfty_of(parent)
.ok_or_else(|| "Attempt to get Self outside a Self-able context".into()),
PathPart::Ident(name) => name.evaluate(prj, parent),
}
}
}
impl<'a> EvaluableTypeExpression for Path<'a> {
type Out = DefID;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
let (tid, vid, path) = prj.get(*self, parent).ok_or("Failed to traverse path")?;
if !path.is_empty() {
Err(format!("Could not traverse past boundary: {path}"))?;
}
match (tid, vid) {
(Some(ty), _) => Ok(ty),
(None, Some(val)) => Ok(val),
(None, None) => Err(format!("No type or value found at path {self}")),
}
}
}
impl<T: EvaluableTypeExpression> EvaluableTypeExpression for [T] {
type Out = Vec<T::Out>;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
let mut types = vec![];
for value in self {
types.push(value.evaluate(prj, parent)?)
}
Ok(types)
}
}
impl<T: EvaluableTypeExpression> EvaluableTypeExpression for Option<T> {
type Out = Option<T::Out>;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
Ok(match self {
Some(v) => Some(v.evaluate(prj, parent)?),
None => None,
})
}
}
impl<T: EvaluableTypeExpression> EvaluableTypeExpression for Box<T> {
type Out = T::Out;
fn evaluate(&self, prj: &mut Project, parent: DefID) -> Result<Self::Out, String> {
self.as_ref().evaluate(prj, parent)
}
}
}

View File

@@ -0,0 +1,87 @@
//! Holds the [Source] of a definition in the AST
use cl_ast::ast::*;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Source<'a> {
Root,
Module(&'a Module),
Alias(&'a Alias),
Enum(&'a Enum),
Variant(&'a Variant),
Struct(&'a Struct),
Const(&'a Const),
Static(&'a Static),
Function(&'a Function),
Local(&'a Let),
Impl(&'a Impl),
Use(&'a Use),
Ty(&'a TyKind),
}
impl Source<'_> {
pub fn name(&self) -> Option<Sym> {
match self {
Source::Root => None,
Source::Module(v) => Some(v.name),
Source::Alias(v) => Some(v.name),
Source::Enum(v) => Some(v.name),
Source::Variant(v) => Some(v.name),
Source::Struct(v) => Some(v.name),
Source::Const(v) => Some(v.name),
Source::Static(v) => Some(v.name),
Source::Function(v) => Some(v.name),
Source::Local(_) => None,
Source::Impl(_) | Source::Use(_) | Source::Ty(_) => None,
}
}
/// Returns `true` if this [Source] defines a named value
pub fn is_named_value(&self) -> bool {
matches!(self, Self::Const(_) | Self::Static(_) | Self::Function(_))
}
/// Returns `true` if this [Source] defines a named type
pub fn is_named_type(&self) -> bool {
matches!(
self,
Self::Module(_) | Self::Alias(_) | Self::Enum(_) | Self::Struct(_)
)
}
/// Returns `true` if this [Source] refers to a [Ty] with no name
pub fn is_anon_type(&self) -> bool {
matches!(self, Self::Ty(_))
}
/// Returns `true` if this [Source] refers to an [Impl] block
pub fn is_impl(&self) -> bool {
matches!(self, Self::Impl(_))
}
/// Returns `true` if this [Source] refers to a [Use] import
pub fn is_use_import(&self) -> bool {
matches!(self, Self::Use(_))
}
}
impl fmt::Display for Source<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Root => "🌳 root 🌳".fmt(f),
Self::Module(arg0) => arg0.fmt(f),
Self::Alias(arg0) => arg0.fmt(f),
Self::Enum(arg0) => arg0.fmt(f),
Self::Variant(arg0) => arg0.fmt(f),
Self::Struct(arg0) => arg0.fmt(f),
Self::Const(arg0) => arg0.fmt(f),
Self::Static(arg0) => arg0.fmt(f),
Self::Function(arg0) => arg0.fmt(f),
Self::Impl(arg0) => arg0.fmt(f),
Self::Use(arg0) => arg0.fmt(f),
Self::Ty(arg0) => arg0.fmt(f),
Self::Local(arg0) => arg0.fmt(f),
}
}
}

View File

@@ -0,0 +1,187 @@
//! Categorizes an entry in a table according to its embedded type information
#![allow(unused)]
use crate::{
entry::EntryMut,
handle::Handle,
source::Source,
table::{NodeKind, Table},
type_expression::{Error as TypeEval, TypeExpression},
type_kind::{Adt, TypeKind},
};
use cl_ast::*;
/// Ensures a type entry exists for the provided handle in the table
pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
let Some(source) = table.source(node) else {
return Ok(());
};
match source {
Source::Variant(v) => cat_variant(table, node, v)?,
Source::Struct(s) => cat_struct(table, node, s)?,
Source::Const(c) => cat_const(table, node, c)?,
Source::Static(s) => cat_static(table, node, s)?,
Source::Function(f) => cat_function(table, node, f)?,
Source::Local(l) => cat_local(table, node, l)?,
Source::Impl(i) => cat_impl(table, node, i)?,
// Source::Alias(_) => {table.mark_unchecked(node)},
_ => return Ok(()),
}
Ok(())
}
fn parent(table: &Table, node: Handle) -> Handle {
table.parent(node).copied().unwrap_or(node)
}
fn cat_struct(table: &mut Table, node: Handle, s: &Struct) -> CatResult<()> {
let Struct { name: _, gens: _, kind } = s;
// TODO: Generics
let kind = match kind {
StructKind::Empty => TypeKind::Adt(Adt::UnitStruct),
StructKind::Tuple(types) => {
let mut out = vec![];
for ty in types {
out.push((Visibility::Public, ty.evaluate(table, node)?))
}
TypeKind::Adt(Adt::TupleStruct(out))
}
StructKind::Struct(members) => {
let mut out = vec![];
for m in members {
out.push(cat_member(table, node, m)?)
}
TypeKind::Adt(Adt::Struct(out))
}
};
table.set_ty(node, kind);
Ok(())
}
fn cat_member(
table: &mut Table,
node: Handle,
m: &StructMember,
) -> CatResult<(Sym, Visibility, Handle)> {
let StructMember { vis, name, ty } = m;
Ok((*name, *vis, ty.evaluate(table, node)?))
}
fn cat_enum<'a>(_table: &mut Table<'a>, _node: Handle, e: &'a Enum) -> CatResult<()> {
let Enum { name: _, gens: _, variants: _ } = e;
// table.set_ty(node, kind);
Ok(())
}
fn cat_variant<'a>(table: &mut Table<'a>, node: Handle, v: &'a Variant) -> CatResult<()> {
let Variant { name, kind, body } = v;
let parent = table.parent(node).copied().unwrap_or(table.root());
match (kind) {
(StructKind::Empty) => Ok(()),
(StructKind::Empty) => Ok(()),
(StructKind::Tuple(ty)) => {
let ty = TypeKind::Adt(Adt::TupleStruct(
ty.iter()
.map(|ty| ty.evaluate(table, node).map(|ty| (Visibility::Public, ty)))
.collect::<Result<_, _>>()?,
));
table.set_ty(node, ty);
Ok(())
}
(StructKind::Struct(members)) => {
let mut out = vec![];
for StructMember { vis, name, ty } in members {
let ty = ty.evaluate(table, node)?;
out.push((*name, *vis, ty));
let mut this = node.to_entry_mut(table);
let mut child = this.new_entry(NodeKind::Type);
child.set_source(Source::Variant(v));
child.set_ty(TypeKind::Instance(ty));
let child = child.id();
this.add_child(*name, child);
}
table.set_ty(node, TypeKind::Adt(Adt::Struct(out)));
Ok(())
}
}
}
fn cat_const(table: &mut Table, node: Handle, c: &Const) -> CatResult<()> {
let parent = parent(table, node);
let kind = TypeKind::Instance(
c.ty.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing a const"))?,
);
table.set_ty(node, kind);
Ok(())
}
fn cat_static(table: &mut Table, node: Handle, s: &Static) -> CatResult<()> {
let parent = parent(table, node);
let kind = TypeKind::Instance(
s.ty.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing a static"))?,
);
table.set_ty(node, kind);
Ok(())
}
fn cat_function(table: &mut Table, node: Handle, f: &Function) -> CatResult<()> {
let kind = TypeKind::Instance(
f.sign
.evaluate(table, node)
.map_err(|e| Error::TypeEval(e, " while categorizing a function"))?,
);
table.set_ty(node, kind);
Ok(())
}
fn cat_local(table: &mut Table, node: Handle, l: &Let) -> CatResult<()> {
let parent = parent(table, node);
if let Some(ty) = &l.ty {
let kind = ty
.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing a let binding"))?;
table.set_ty(node, TypeKind::Instance(kind));
}
Ok(())
}
fn cat_impl(table: &mut Table, node: Handle, i: &Impl) -> CatResult<()> {
let parent = parent(table, node);
let Impl { gens, target, body: _ } = i;
let target = match target {
ImplKind::Type(t) => t.evaluate(table, parent),
ImplKind::Trait { impl_trait: _, for_type: t } => t.evaluate(table, parent),
}?;
table.set_impl_target(node, target);
Ok(())
}
type CatResult<T> = Result<T, Error>;
#[derive(Clone, Debug)]
pub enum Error {
BadMeta(Meta),
TypeEval(TypeEval, &'static str),
}
impl From<TypeEval> for Error {
fn from(value: TypeEval) -> Self {
Error::TypeEval(value, "")
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::BadMeta(meta) => write!(f, "Unknown attribute: #[{meta}]"),
Error::TypeEval(e, during) => write!(f, "{e}{during}"),
}
}
}

View File

@@ -0,0 +1,23 @@
use crate::{handle::Handle, table::Table};
pub fn implement(table: &mut Table) -> Vec<Handle> {
let pending = std::mem::take(&mut table.impls);
let mut errors = vec![];
for node in pending {
if let Err(e) = impl_one(table, node) {
errors.push(e);
}
}
errors
}
pub fn impl_one(table: &mut Table, node: Handle) -> Result<(), Handle> {
let Some(target) = table.impl_target(node) else {
Err(node)?
};
if let Some(children) = table.children.get_mut(&node) {
let children = children.clone();
table.children.entry(target).or_default().extend(children);
}
Ok(())
}

View File

@@ -0,0 +1,158 @@
//! An algorithm for importing external nodes
use crate::{
handle::Handle,
source::Source,
table::{NodeKind, Table},
};
use cl_ast::{PathPart, Sym, Use, UseTree};
use core::slice;
use std::{collections::HashSet, mem};
type Seen = HashSet<Handle>;
pub fn import<'a>(table: &mut Table<'a>) -> Vec<(Handle, Error<'a>)> {
let pending = mem::take(&mut table.uses);
let mut seen = Seen::new();
let mut failed = vec![];
for import in pending {
let Err(e) = import_one(table, import, &mut seen) else {
continue;
};
if let Error::NotFound(_, _) = e {
table.mark_use_item(import)
}
failed.push((import, e));
}
failed
}
fn import_one<'a>(table: &mut Table<'a>, item: Handle, seen: &mut Seen) -> UseResult<'a, ()> {
if !seen.insert(item) {
return Ok(());
}
let Some(NodeKind::Use) = table.kind(item) else {
Err(Error::ItsNoUse)?
};
let Some(&dst) = table.parent(item) else {
Err(Error::NoParents)?
};
let Some(code) = table.source(item) else {
Err(Error::NoSource)?
};
let &Source::Use(tree) = code else {
Err(Error::BadSource(*code))?
};
let Use { absolute, tree } = tree;
import_tree(
table,
if !absolute { dst } else { table.root() },
dst,
tree,
seen,
)
}
fn import_tree<'a>(
table: &mut Table<'a>,
src: Handle,
dst: Handle,
tree: &UseTree,
seen: &mut Seen,
) -> UseResult<'a, ()> {
match tree {
UseTree::Tree(trees) => trees
.iter()
.try_for_each(|tree| import_tree(table, src, dst, tree, seen)),
UseTree::Path(part, rest) => {
let source = table
.nav(src, slice::from_ref(part))
.ok_or(Error::NotFound(src, *part))?;
import_tree(table, source, dst, rest, seen)
}
UseTree::Alias(src_name, dst_name) => {
import_name(table, src, src_name, dst, dst_name, seen)
}
UseTree::Name(src_name) => import_name(table, src, src_name, dst, src_name, seen),
UseTree::Glob => import_glob(table, src, dst, seen),
}
}
fn import_glob<'a>(
table: &mut Table<'a>,
src: Handle,
dst: Handle,
seen: &mut Seen,
) -> UseResult<'a, ()> {
let Table { children, imports, .. } = table;
if let Some(c) = children.get(&src) {
imports.entry(dst).or_default().extend(c)
}
import_deps(table, src, seen)?;
let Table { imports, .. } = table;
// Importing imports requires some extra work, since we can't `get_many_mut`
if let Some(i) = imports.get(&src) {
let uses: Vec<_> = i.iter().map(|(&k, &v)| (k, v)).collect();
imports.entry(dst).or_default().extend(uses);
}
Ok(())
}
fn import_name<'a>(
table: &mut Table<'a>,
src: Handle,
src_name: &Sym,
dst: Handle,
dst_name: &Sym,
seen: &mut Seen,
) -> UseResult<'a, ()> {
import_deps(table, src, seen)?;
match table.get_by_sym(src, src_name) {
// TODO: check for new imports clobbering existing imports
Some(src_id) => table.add_import(dst, *dst_name, src_id),
None => Err(Error::NotFound(src, PathPart::Ident(*src_name)))?,
};
Ok(())
}
/// Imports the dependencies of this node
fn import_deps<'a>(table: &mut Table<'a>, node: Handle, seen: &mut Seen) -> UseResult<'a, ()> {
if let Some(items) = table.use_items.get(&node) {
let out = items.clone();
for item in out {
import_one(table, item, seen)?;
}
}
Ok(())
}
pub type UseResult<'a, T> = Result<T, Error<'a>>;
#[derive(Debug)]
pub enum Error<'a> {
ItsNoUse,
NoParents,
NoSource,
BadSource(Source<'a>),
NotFound(Handle, PathPart),
}
impl std::fmt::Display for Error<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::ItsNoUse => write!(f, "Entry is not use"),
Error::NoParents => write!(f, "Entry has no parents"),
Error::NoSource => write!(f, "Entry has no source"),
Error::BadSource(s) => write!(f, "Entry incorrectly marked as use item: {s}"),
Error::NotFound(id, part) => write!(f, "Could not traverse {id}::{part}"),
}
}
}

View File

@@ -0,0 +1,12 @@
//! Implements type unification, used by the Hindley-Milner type inference algorithm
//!
//! Inspired by [rust-hindley-milner][1] and [hindley-milner-python][2]
//!
//! [1]: https://github.com/tcr/rust-hindley-milner/
//! [2]: https://github.com/rob-smallshire/hindley-milner-python
pub mod engine;
pub mod inference;
pub mod error;

View File

@@ -0,0 +1,621 @@
use std::collections::HashSet;
use super::error::InferenceError;
use crate::{
entry::Entry,
handle::Handle,
source::Source,
stage::infer::inference::Inference,
table::{NodeKind, Table},
type_expression::TypeExpression,
type_kind::{Adt, Primitive, TypeKind},
};
use cl_ast::Sym;
/*
Types in Conlang:
- Never type: !
- type !
- for<A> ! -> A
- Primitive types: bool, i32, (), ...
- type bool; ...
- Reference types: &T, *T
- for<T> type ref<T>; for<T> type ptr<T>
- Slice type: [T]
- for<T> type slice<T>
- Array type: [T;usize]
- for<T> type array<T, instanceof<usize>>
- Tuple type: (T, ...Z)
- for<T, ..> type tuple<T, ..> // on a per-case basis!
- Funct type: fn Tuple -> R
- for<T, R> type T -> R // on a per-case basis!
*/
type HandleSet<'h> = Option<&'h mut Option<Handle>>;
pub struct InferenceEngine<'table, 'a, 'b, 'r> {
pub(super) table: &'table mut Table<'a>,
/// The current working node
pub(crate) at: Handle,
/// The current breakset
pub(crate) bset: HandleSet<'b>,
/// The current returnset
pub(crate) rset: HandleSet<'r>,
}
impl<'table, 'a, 'b, 'r> InferenceEngine<'table, 'a, 'b, 'r> {
/// Infers the type of an object by deferring to [`Inference::infer()`]
pub fn infer(&mut self, inferrable: &'a impl Inference<'a>) -> Result<Handle, InferenceError> {
inferrable.infer(self)
}
/// Constructs a new [`InferenceEngine`], scoped around a [`Handle`] in a [`Table`].
pub fn new(table: &'table mut Table<'a>, at: Handle) -> Self {
Self { at, table, bset: Default::default(), rset: Default::default() }
}
/// Constructs an [`InferenceEngine`] that borrows the same table as `self`,
/// but with a shortened lifetime.
pub fn scoped(&mut self) -> InferenceEngine<'_, 'a, '_, '_> {
InferenceEngine {
at: self.at,
table: self.table,
bset: self.bset.as_deref_mut(),
rset: self.rset.as_deref_mut(),
}
}
pub fn infer_all(&mut self) -> Vec<(Handle, InferenceError)> {
let queue = std::mem::take(&mut self.table.unchecked);
let mut res = Vec::new();
for handle in queue {
let mut eng = self.at(handle);
let Some(source) = eng.table.source(handle) else {
eprintln!("No source found for {handle}");
continue;
};
println!("Inferring {source}");
let ret = match source {
Source::Module(v) => v.infer(&mut eng),
Source::Alias(v) => v.infer(&mut eng),
Source::Enum(v) => v.infer(&mut eng),
// Source::Variant(v) => v.infer(&mut eng),
Source::Struct(v) => v.infer(&mut eng),
Source::Const(v) => v.infer(&mut eng),
Source::Static(v) => v.infer(&mut eng),
Source::Function(v) => v.infer(&mut eng),
Source::Local(v) => v.infer(&mut eng),
Source::Impl(v) => v.infer(&mut eng),
_ => Ok(eng.empty()),
};
match &ret {
Ok(handle) => println!("=> {}", eng.entry(*handle)),
Err(err @ InferenceError::AnnotationEval(_)) => eprintln!("=> ERROR: {err}"),
Err(InferenceError::FieldCount(h, want, got)) => {
eprintln!("=> ERROR: Field count {want} != {got} in {}", eng.entry(*h))
}
Err(err @ InferenceError::NotFound(_)) => eprintln!("=> ERROR: {err}"),
Err(InferenceError::Mismatch(h1, h2)) => eprintln!(
"=> ERROR: Type mismatch {} != {}",
eng.entry(*h1),
eng.entry(*h2),
),
Err(InferenceError::Recursive(h1, h2)) => eprintln!(
"=> ERROR: Cycle found in types {}, {}",
eng.entry(*h1),
eng.entry(*h2),
),
Err(InferenceError::NoBreak | InferenceError::NoReturn) => {}
}
println!();
if let Err(err) = ret {
res.push((handle, err));
eng.table.mark_unchecked(handle);
}
}
res
}
/// Constructs a new InferenceEngine with the
pub fn at(&mut self, at: Handle) -> InferenceEngine<'_, 'a, '_, '_> {
InferenceEngine { at, ..self.scoped() }
}
pub fn open_bset<'ob>(
&mut self,
bset: &'ob mut Option<Handle>,
) -> InferenceEngine<'_, 'a, 'ob, '_> {
InferenceEngine { bset: Some(bset), ..self.scoped() }
}
pub fn open_rset<'or>(
&mut self,
rset: &'or mut Option<Handle>,
) -> InferenceEngine<'_, 'a, '_, 'or> {
InferenceEngine { rset: Some(rset), ..self.scoped() }
}
pub fn bset(&mut self, ty: Handle) -> Result<(), InferenceError> {
match self.bset.as_mut() {
Some(&mut &mut Some(bset)) => self.unify(ty, bset),
Some(none) => {
let _ = none.insert(ty);
Ok(())
}
None => Err(InferenceError::NoBreak),
}
}
pub fn rset(&mut self, ty: Handle) -> Result<(), InferenceError> {
match self.rset.as_mut() {
Some(&mut &mut Some(rset)) => self.unify(ty, rset),
Some(none) => {
let _ = none.insert(ty);
Ok(())
}
None => Err(InferenceError::NoReturn),
}
}
/// Constructs an [Entry] out of a [Handle], for ease of use
pub fn entry(&self, of: Handle) -> Entry<'_, 'a> {
self.table.entry(of)
}
pub fn by_name<Out, N: TypeExpression<Out>>(
&mut self,
name: &N,
) -> Result<Out, crate::type_expression::Error> {
name.evaluate(self.table, self.at)
}
/// Creates a new unbound [type variable](Handle)
pub fn new_var(&mut self) -> Handle {
self.table.type_variable()
}
pub fn new_inferred(&mut self) -> Handle {
self.table.inferred_type()
}
/// Creates a variable that is a new instance of another [Type](Handle)
pub fn new_inst(&mut self, of: Handle) -> Handle {
self.table.anon_type(TypeKind::Instance(of))
}
/// Gets the defining usage of a type without collapsing intermediates
pub fn def_usage(&self, to: Handle) -> Handle {
match self.table.entry(to).ty() {
Some(TypeKind::Instance(id)) => self.def_usage(*id),
_ => to,
}
}
pub fn get_fn(&self, at: Handle, name: Sym) -> Option<(Handle, Handle)> {
use cl_ast::PathPart;
if let Some(&TypeKind::FnSig { args, rety }) = self
.entry(at)
.nav(&[PathPart::Ident(name)])
.as_ref()
.and_then(Entry::ty)
{
Some((args, rety))
} else {
None
}
}
/// Creates a new type variable representing a tuple
pub fn new_tuple(&mut self, tys: Vec<Handle>) -> Handle {
self.table.anon_type(TypeKind::Tuple(tys))
}
/// Creates a new type variable representing an array
pub fn new_array(&mut self, ty: Handle, size: usize) -> Handle {
self.table.anon_type(TypeKind::Array(ty, size))
}
/// Creates a new type variable representing a slice of contiguous memory
pub fn new_slice(&mut self, ty: Handle) -> Handle {
self.table.anon_type(TypeKind::Slice(ty))
}
/// Creates a new reference to a type
pub fn new_ref(&mut self, to: Handle) -> Handle {
self.table.anon_type(TypeKind::Ref(to))
}
/// All primitives must be predefined in the standard library.
pub fn primitive(&self, name: &'static str) -> Handle {
// TODO: keep a map of primitives in the table root
self.table.get_lang_item(name)
}
pub fn never(&mut self) -> Handle {
self.table.get_lang_item("never")
}
pub fn empty(&mut self) -> Handle {
self.table.anon_type(TypeKind::Tuple(vec![]))
}
pub fn bool(&self) -> Handle {
self.primitive("bool")
}
pub fn char(&self) -> Handle {
self.primitive("char")
}
pub fn str(&self) -> Handle {
self.primitive("str")
}
pub fn u32(&self) -> Handle {
self.primitive("u32")
}
pub fn usize(&self) -> Handle {
self.primitive("usize")
}
/// Creates a new inferred-integer literal
pub fn integer_literal(&mut self) -> Handle {
let h = self.table.new_entry(self.at, NodeKind::Temporary);
self.table
.set_ty(h, TypeKind::Primitive(Primitive::Integer));
h
}
/// Creates a new inferred-float literal
pub fn float_literal(&mut self) -> Handle {
let h = self.table.new_entry(self.at, NodeKind::Temporary);
self.table.set_ty(h, TypeKind::Primitive(Primitive::Float));
h
}
/// Enters a new scope
pub fn local_scope(&mut self, name: Sym) {
let scope = self.table.new_entry(self.at, NodeKind::Scope);
self.table.add_child(self.at, name, scope);
self.at = scope;
}
/// Creates a new locally-scoped InferenceEngine.
pub fn block_scope(&mut self) -> InferenceEngine<'_, 'a, '_, '_> {
let scope = self.table.new_entry(self.at, NodeKind::Scope);
self.table.add_child(self.at, "".into(), scope);
self.at(scope)
}
/// Sets this type variable `to` be an instance `of` the other
/// # Panics
/// Panics if `to` is not a type variable
pub fn set_instance(&mut self, to: Handle, of: Handle) {
let mut e = self.table.entry_mut(to);
match e.as_ref().ty() {
Some(TypeKind::Inferred) => {
if let Some(ty) = self.table.ty(of) {
self.table.set_ty(to, ty.clone());
}
None
}
Some(TypeKind::Variable)
| Some(TypeKind::Primitive(Primitive::Float | Primitive::Integer)) => {
e.set_ty(TypeKind::Instance(of))
}
other => todo!("Cannot set {} to instance of: {other:?}", e.as_ref()),
};
}
/// Checks whether there are any unbound type variables in this type
pub fn is_generic(&self, ty: Handle) -> bool {
fn is_generic_rec(this: &InferenceEngine, ty: Handle, seen: &mut HashSet<Handle>) -> bool {
if !seen.insert(ty) {
return false;
}
let entry = this.table.entry(ty);
let Some(ty) = entry.ty() else {
return false;
};
match ty {
TypeKind::Inferred => false,
TypeKind::Variable => true,
&TypeKind::Array(ty, _) => is_generic_rec(this, ty, seen),
&TypeKind::Instance(ty) => is_generic_rec(this, ty, seen),
TypeKind::Primitive(_) => false,
TypeKind::Adt(Adt::Enum(tys)) => {
tys.iter().any(|&(_, ty)| is_generic_rec(this, ty, seen))
}
TypeKind::Adt(Adt::Struct(tys)) => {
tys.iter().any(|&(_, _, ty)| is_generic_rec(this, ty, seen))
}
TypeKind::Adt(Adt::TupleStruct(tys)) => {
tys.iter().any(|&(_, ty)| is_generic_rec(this, ty, seen))
}
TypeKind::Adt(Adt::UnitStruct) => false,
TypeKind::Adt(Adt::Union(tys)) => {
tys.iter().any(|&(_, ty)| is_generic_rec(this, ty, seen))
}
&TypeKind::Ref(ty) => is_generic_rec(this, ty, seen),
&TypeKind::Ptr(ty) => is_generic_rec(this, ty, seen),
&TypeKind::Slice(ty) => is_generic_rec(this, ty, seen),
TypeKind::Tuple(tys) => tys.iter().any(|&ty| is_generic_rec(this, ty, seen)),
&TypeKind::FnSig { args, rety } => {
is_generic_rec(this, args, seen) || is_generic_rec(this, rety, seen)
}
TypeKind::Module => false,
}
}
is_generic_rec(self, ty, &mut HashSet::new())
}
/// Makes a deep copy of a type expression.
///
/// Bound variables are shared, unbound variables are duplicated.
pub fn deep_clone(&mut self, ty: Handle) -> Handle {
if !self.is_generic(ty) {
return ty;
};
let entry = self.table.entry(ty);
let Some(tykind) = entry.ty().cloned() else {
return ty;
};
// TODO: Parent the deep clone into a new "monomorphs" branch of tree
match tykind {
TypeKind::Variable => self.new_inferred(),
TypeKind::Array(h, s) => {
let ty = self.deep_clone(h);
self.table.anon_type(TypeKind::Array(ty, s))
}
TypeKind::Instance(h) => {
let ty = self.deep_clone(h);
self.table.anon_type(TypeKind::Instance(ty))
}
TypeKind::Adt(Adt::Enum(tys)) => {
let tys = tys
.into_iter()
.map(|(name, ty)| (name, self.deep_clone(ty)))
.collect();
self.table.anon_type(TypeKind::Adt(Adt::Enum(tys)))
}
TypeKind::Adt(Adt::Struct(tys)) => {
let tys = tys
.into_iter()
.map(|(n, v, ty)| (n, v, self.deep_clone(ty)))
.collect();
self.table.anon_type(TypeKind::Adt(Adt::Struct(tys)))
}
TypeKind::Adt(Adt::TupleStruct(tys)) => {
let tys = tys
.into_iter()
.map(|(v, ty)| (v, self.deep_clone(ty)))
.collect();
self.table.anon_type(TypeKind::Adt(Adt::TupleStruct(tys)))
}
TypeKind::Adt(Adt::Union(tys)) => {
let tys = tys
.into_iter()
.map(|(n, ty)| (n, self.deep_clone(ty)))
.collect();
self.table.anon_type(TypeKind::Adt(Adt::Union(tys)))
}
TypeKind::Ref(h) => {
let ty = self.deep_clone(h);
self.table.anon_type(TypeKind::Ref(ty))
}
TypeKind::Ptr(handle) => {
let ty = self.deep_clone(handle);
self.table.anon_type(TypeKind::Ptr(ty))
}
TypeKind::Slice(h) => {
let ty = self.deep_clone(h);
self.table.anon_type(TypeKind::Slice(ty))
}
TypeKind::Tuple(tys) => {
let tys = tys.into_iter().map(|ty| self.deep_clone(ty)).collect();
self.table.anon_type(TypeKind::Tuple(tys))
}
TypeKind::FnSig { args, rety } => {
let args = self.deep_clone(args);
let rety = self.deep_clone(rety);
self.table.anon_type(TypeKind::FnSig { args, rety })
}
_ => ty,
}
}
/// Returns the defining instance of `self`,
/// collapsing type instances along the way.
pub fn prune(&mut self, ty: Handle) -> Handle {
if let Some(TypeKind::Instance(new_ty)) = self.table.ty(ty) {
let new_ty = self.prune(*new_ty);
self.table.set_ty(ty, TypeKind::Instance(new_ty));
new_ty
} else {
ty
}
}
/// Checks whether a type occurs in another type
///
/// # Note:
/// - Since the test uses strict equality, `self` should be pruned prior to testing.
/// - The test is *not guaranteed to terminate* for recursive types.
pub fn occurs_in(&self, this: Handle, other: Handle) -> bool {
if this == other {
return true;
}
let Some(ty) = self.table.ty(other) else {
return false;
};
match ty {
TypeKind::Instance(other) => self.occurs_in(this, *other),
TypeKind::Adt(Adt::Enum(items)) => {
items.iter().any(|(_, other)| self.occurs_in(this, *other))
}
TypeKind::Adt(Adt::Struct(items)) => items
.iter()
.any(|(_, _, other)| self.occurs_in(this, *other)),
TypeKind::Adt(Adt::TupleStruct(items)) => {
items.iter().any(|(_, other)| self.occurs_in(this, *other))
}
TypeKind::Adt(Adt::Union(items)) => {
items.iter().any(|(_, other)| self.occurs_in(this, *other))
}
TypeKind::Ref(_) => false,
TypeKind::Ptr(_) => false,
TypeKind::Slice(other) => self.occurs_in(this, *other),
TypeKind::Array(other, _) => self.occurs_in(this, *other),
TypeKind::Tuple(handles) => handles.iter().any(|&other| self.occurs_in(this, other)),
TypeKind::FnSig { args, rety } => {
self.occurs_in(this, *args) || self.occurs_in(this, *rety)
}
TypeKind::Inferred
| TypeKind::Variable
| TypeKind::Adt(Adt::UnitStruct)
| TypeKind::Primitive(_)
| TypeKind::Module => false,
}
}
/// Unifies two types
pub fn unify(&mut self, this: Handle, other: Handle) -> Result<(), InferenceError> {
let (ah, bh) = (self.prune(this), self.prune(other));
if ah == bh {
return Ok(());
}
let (a, b) = (self.table.entry(ah), self.table.entry(bh));
let (Some(a), Some(b)) = (a.ty(), b.ty()) else {
return Err(InferenceError::Mismatch(ah, bh));
};
match (a, b) {
(TypeKind::Variable, TypeKind::Variable) => {
self.set_instance(ah, bh);
Ok(())
}
(TypeKind::Inferred, _) => {
self.set_instance(ah, bh);
Ok(())
}
(_, TypeKind::Inferred) => self.unify(bh, ah),
(TypeKind::Variable, _) => Err(InferenceError::Mismatch(ah, bh)),
(TypeKind::Instance(a), TypeKind::Instance(b)) if !self.occurs_in(*a, *b) => {
self.set_instance(*a, *b);
Ok(())
}
(TypeKind::Instance(_), _) => Err(InferenceError::Recursive(ah, bh)),
(TypeKind::Primitive(Primitive::Float), TypeKind::Primitive(Primitive::Integer))
| (TypeKind::Primitive(Primitive::Integer), TypeKind::Primitive(Primitive::Float)) => {
Err(InferenceError::Mismatch(ah, bh))
}
// Primitives have their own set of vars which only unify with primitives.
(TypeKind::Primitive(Primitive::Integer), TypeKind::Primitive(i)) if i.is_integer() => {
self.set_instance(ah, bh);
Ok(())
}
(TypeKind::Primitive(Primitive::Float), TypeKind::Primitive(f)) if f.is_float() => {
self.set_instance(ah, bh);
Ok(())
}
(_, TypeKind::Variable)
| (_, TypeKind::Instance(_))
| (TypeKind::Primitive(_), TypeKind::Primitive(Primitive::Integer))
| (TypeKind::Primitive(_), TypeKind::Primitive(Primitive::Float)) => self.unify(bh, ah),
(TypeKind::Adt(Adt::Enum(ia)), TypeKind::Adt(Adt::Enum(ib)))
if ia.len() == ib.len() =>
{
for ((na, a), (nb, b)) in ia.clone().into_iter().zip(ib.clone().into_iter()) {
if na != nb {
return Err(InferenceError::Mismatch(ah, bh));
}
self.unify(a, b)?;
}
Ok(())
}
(TypeKind::Adt(Adt::Enum(en)), TypeKind::Adt(_)) => {
#[allow(unused)]
let Some(other_parent) = self.table.parent(bh) else {
Err(InferenceError::Mismatch(ah, bh))?
};
if ah != *other_parent {
Err(InferenceError::Mismatch(ah, *other_parent))?
}
#[allow(unused)]
for (sym, handle) in en {
let handle = self.def_usage(*handle);
if handle == bh {
return Ok(());
}
}
Err(InferenceError::Mismatch(ah, bh))
}
(TypeKind::Adt(Adt::Struct(ia)), TypeKind::Adt(Adt::Struct(ib)))
if ia.len() == ib.len() =>
{
for ((na, va, a), (nb, vb, b)) in ia.clone().into_iter().zip(ib.clone().into_iter())
{
if na != nb || va != vb {
return Err(InferenceError::Mismatch(ah, bh));
}
self.unify(a, b)?;
}
Ok(())
}
(TypeKind::Adt(Adt::TupleStruct(ia)), TypeKind::Adt(Adt::TupleStruct(ib)))
if ia.len() == ib.len() =>
{
for ((va, a), (vb, b)) in ia.clone().into_iter().zip(ib.clone().into_iter()) {
if va != vb {
return Err(InferenceError::Mismatch(ah, bh));
}
self.unify(a, b)?;
}
Ok(())
}
(TypeKind::Adt(Adt::Union(ia)), TypeKind::Adt(Adt::Union(ib)))
if ia.len() == ib.len() =>
{
todo!()
}
(TypeKind::Ref(a), TypeKind::Ref(b)) => self.unify(*a, *b),
(TypeKind::Ptr(a), TypeKind::Ptr(b)) => self.unify(*a, *b),
(TypeKind::Slice(a), TypeKind::Slice(b)) => self.unify(*a, *b),
// Slice unifies with array
(TypeKind::Array(a, _), TypeKind::Slice(b)) => self.unify(*a, *b),
(TypeKind::Slice(_), TypeKind::Array(_, _)) => self.unify(bh, ah),
(TypeKind::Array(a, sa), TypeKind::Array(b, sb)) if sa == sb => self.unify(*a, *b),
(TypeKind::Tuple(a), TypeKind::Tuple(b)) => {
if a.len() != b.len() {
return Err(InferenceError::Mismatch(ah, bh));
}
let (a, b) = (a.clone(), b.clone());
for (a, b) in a.iter().zip(b.iter()) {
self.unify(*a, *b)?;
}
Ok(())
}
(&TypeKind::FnSig { args: a1, rety: r1 }, &TypeKind::FnSig { args: a2, rety: r2 }) => {
self.unify(a1, a2)?;
self.unify(r1, r2)
}
(TypeKind::Primitive(Primitive::Never), _)
| (_, TypeKind::Primitive(Primitive::Never)) => Ok(()),
(a, b) if a == b => Ok(()),
_ => Err(InferenceError::Mismatch(ah, bh)),
}
}
}

View File

@@ -0,0 +1,43 @@
use cl_ast::Path;
use crate::handle::Handle;
use core::fmt;
/// An error produced during type inference
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InferenceError {
AnnotationEval(crate::type_expression::Error),
FieldCount(Handle, usize, usize),
NotFound(Path),
Mismatch(Handle, Handle),
Recursive(Handle, Handle),
NoBreak,
NoReturn,
}
impl std::error::Error for InferenceError {}
#[rustfmt::skip]
impl fmt::Display for InferenceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InferenceError::AnnotationEval(error) => write!(f, "{error}"),
InferenceError::FieldCount(name, want, got) => {
write!(f,
"Struct {name} {} fields! Expected {want}, got {got}",
if want < got { "has too many" } else { "is missing" }
)
}
InferenceError::NotFound(p) => write!(f, "Path not visible in scope: {p}"),
InferenceError::Mismatch(a, b) => write!(f, "Type mismatch: {a:?} != {b:?}"),
InferenceError::Recursive(_, _) => write!(f, "Recursive type!"),
InferenceError::NoBreak => write!(f, "Encountered break outside loop!"),
InferenceError::NoReturn => write!(f, "Encountered return outside function!"),
}
}
}
impl From<crate::type_expression::Error> for InferenceError {
fn from(value: crate::type_expression::Error) -> Self {
Self::AnnotationEval(value)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,193 @@
//! The [Populator] populates entries in the sym table, including span info
use crate::{
entry::EntryMut,
handle::Handle,
source::Source,
table::{NodeKind, Table},
type_kind::TypeKind,
};
use cl_ast::{
ItemKind, Literal, Meta, MetaKind, Sym,
ast_visitor::{Visit, Walk},
};
#[derive(Debug)]
pub struct Populator<'t, 'a> {
inner: EntryMut<'t, 'a>,
name: Option<Sym>, // this is a hack to get around the Visitor interface
}
impl<'t, 'a> Populator<'t, 'a> {
pub fn new(table: &'t mut Table<'a>) -> Self {
Self { inner: table.root_entry_mut(), name: None }
}
/// Constructs a new Populator with the provided parent Handle
pub fn with_id(&mut self, parent: Handle) -> Populator<'_, 'a> {
Populator { inner: self.inner.with_id(parent), name: None }
}
pub fn new_entry(&mut self, kind: NodeKind) -> Populator<'_, 'a> {
Populator { inner: self.inner.new_entry(kind), name: None }
}
pub fn set_name(&mut self, name: Sym) {
self.name = Some(name);
}
}
impl<'a> Visit<'a> for Populator<'_, 'a> {
fn visit_item(&mut self, i: &'a cl_ast::Item) {
let cl_ast::Item { span, attrs, vis: _, kind } = i;
// TODO: this, better, better.
let entry_kind = match kind {
ItemKind::Alias(_) => NodeKind::Type,
ItemKind::Enum(_) => NodeKind::Type,
ItemKind::Struct(_) => NodeKind::Type,
ItemKind::Const(_) => NodeKind::Const,
ItemKind::Static(_) => NodeKind::Static,
ItemKind::Function(_) => NodeKind::Function,
ItemKind::Module(_) => NodeKind::Module,
ItemKind::Impl(_) => NodeKind::Impl,
ItemKind::Use(_) => NodeKind::Use,
};
let mut entry = self.new_entry(entry_kind);
entry.inner.set_span(*span);
entry.inner.set_meta(&attrs.meta);
for Meta { name, kind } in &attrs.meta {
if let ("lang", MetaKind::Equals(Literal::String(s))) = (name.to_ref(), kind) {
if let Ok(prim) = s.parse() {
entry.inner.set_ty(TypeKind::Primitive(prim));
}
entry.inner.mark_lang_item(Sym::from(s).to_ref());
}
}
entry.visit_children(i);
if let (Some(name), child) = (entry.name, entry.inner.id()) {
self.inner.add_child(name, child);
}
}
fn visit_generics(&mut self, value: &'a cl_ast::Generics) {
let cl_ast::Generics { vars } = value;
for var in vars {
let mut entry = self.inner.new_entry(NodeKind::Type);
entry.set_ty(TypeKind::Variable);
let id = entry.id();
self.inner.add_child(*var, id);
}
}
fn visit_alias(&mut self, a: &'a cl_ast::Alias) {
let cl_ast::Alias { name, from } = a;
self.inner.set_source(Source::Alias(a));
self.set_name(*name);
self.visit(from);
}
fn visit_const(&mut self, c: &'a cl_ast::Const) {
let cl_ast::Const { name, ty, init } = c;
self.inner.set_source(Source::Const(c));
self.inner.set_body(init);
self.set_name(*name);
self.visit(ty);
self.visit(init);
}
fn visit_static(&mut self, s: &'a cl_ast::Static) {
let cl_ast::Static { name, init, .. } = s;
self.inner.set_source(Source::Static(s));
self.inner.set_body(init);
self.set_name(*name);
s.children(self);
}
fn visit_function(&mut self, f: &'a cl_ast::Function) {
self.inner.set_source(Source::Function(f));
self.set_name(f.name);
if let Some(body) = &f.body {
self.inner.set_body(body);
}
f.children(self);
}
fn visit_module(&mut self, m: &'a cl_ast::Module) {
self.inner.set_source(Source::Module(m));
self.set_name(m.name);
m.children(self);
}
fn visit_struct(&mut self, s: &'a cl_ast::Struct) {
self.inner.set_source(Source::Struct(s));
self.set_name(s.name);
s.children(self);
}
fn visit_enum(&mut self, e: &'a cl_ast::Enum) {
let cl_ast::Enum { name, gens, variants } = e;
self.inner.set_source(Source::Enum(e));
self.set_name(*name);
self.visit(gens);
let mut children = Vec::new();
for variant in variants.iter() {
let mut entry = self.new_entry(NodeKind::Type);
variant.visit_in(&mut entry);
let child = entry.inner.id();
children.push((variant.name, child));
self.inner.add_child(variant.name, child);
}
self.inner
.set_ty(TypeKind::Adt(crate::type_kind::Adt::Enum(children)));
}
fn visit_variant(&mut self, value: &'a cl_ast::Variant) {
let cl_ast::Variant { name, kind, body } = value;
self.inner.set_source(Source::Variant(value));
self.set_name(*name);
self.visit(kind);
match (kind, body) {
(cl_ast::StructKind::Empty, None) => {
self.inner.set_ty(TypeKind::Inferred);
}
(cl_ast::StructKind::Empty, Some(body)) => {
self.inner.set_body(body);
}
(cl_ast::StructKind::Tuple(_items), None) => {}
(cl_ast::StructKind::Struct(_struct_members), None) => {}
(_, Some(body)) => panic!("Unexpected body {body} in enum variant `{value}`"),
}
}
fn visit_impl(&mut self, i: &'a cl_ast::Impl) {
let cl_ast::Impl { gens: _, target: _, body } = i;
self.inner.set_source(Source::Impl(i));
self.inner.mark_impl_item();
// We don't know if target is generic yet -- that's checked later.
self.visit(body);
}
fn visit_use(&mut self, u: &'a cl_ast::Use) {
let cl_ast::Use { absolute: _, tree } = u;
self.inner.set_source(Source::Use(u));
self.inner.mark_use_item();
self.visit(tree);
}
}

View File

@@ -0,0 +1,364 @@
//! The [Table] is a monolithic data structure representing everything the type checker
//! knows about a program.
//!
//! Individual nodes in the table can be queried using the [Entry] API ([Table::entry])
//! or modified using the [EntryMut] API ([Table::entry_mut]).
//!
//! # Contents of a "node"
//! Always present:
//! - [NodeKind]: Determines how this node will be treated during the [stages](crate::stage) of
//! compilation
//! - [Parent node](Handle): Arranges this node in the hierarchical graph structure
//!
//! Populated as needed:
//! - Children: An associative array of [names](Sym) to child nodes in the graph. Child nodes are
//! arranged in a *strict* tree structure, with no back edges
//! - Imports: An associative array of [names](Sym) to other nodes in the graph. Not all import
//! nodes are back edges, but all back edges *must be* import nodes.
//! - [Types](TypeKind): Contains type information populated through type checking and inference.
//! Nodes with unpopulated types may be considered type variables in the future.
//! - [Spans][span]: Positional information from the source text. See [cl_structures::span].
//! - [Metas](Meta): Metadata decorators. These may have an effect throughout the compiler.
//! - [Sources](Source): Pointers back into the AST, for future analysis.
//! - Impl Targets: Sparse mapping of `impl` nodes to their corresponding targets.
//! - etc.
//!
//! [span]: struct@Span
use crate::{
entry::{Entry, EntryMut},
handle::Handle,
source::Source,
type_kind::TypeKind,
};
use cl_ast::{Expr, Meta, PathPart, Sym};
use cl_structures::{index_map::IndexMap, span::Span};
use std::collections::HashMap;
// TODO: Cycle detection external to this module
/// The table is a monolithic data structure representing everything the type checker
/// knows about a program.
///
/// See [module documentation](self).
#[derive(Debug)]
pub struct Table<'a> {
root: Handle,
/// This is the source of truth for handles
kinds: IndexMap<Handle, NodeKind>,
parents: IndexMap<Handle, Handle>,
pub(crate) children: HashMap<Handle, HashMap<Sym, Handle>>,
pub(crate) imports: HashMap<Handle, HashMap<Sym, Handle>>,
pub(crate) use_items: HashMap<Handle, Vec<Handle>>,
bodies: HashMap<Handle, &'a Expr>,
types: HashMap<Handle, TypeKind>,
spans: HashMap<Handle, Span>,
metas: HashMap<Handle, &'a [Meta]>,
sources: HashMap<Handle, Source<'a>>,
impl_targets: HashMap<Handle, Handle>,
anon_types: HashMap<TypeKind, Handle>,
lang_items: HashMap<&'static str, Handle>,
// --- Queues for algorithms ---
pub(crate) unchecked: Vec<Handle>,
pub(crate) impls: Vec<Handle>,
pub(crate) uses: Vec<Handle>,
}
impl<'a> Table<'a> {
pub fn new() -> Self {
let mut kinds = IndexMap::new();
let mut parents = IndexMap::new();
let root = kinds.insert(NodeKind::Root);
assert_eq!(root, parents.insert(root));
Self {
root,
kinds,
parents,
children: HashMap::new(),
imports: HashMap::new(),
use_items: HashMap::new(),
bodies: HashMap::new(),
types: HashMap::new(),
spans: HashMap::new(),
metas: HashMap::new(),
sources: HashMap::new(),
impl_targets: HashMap::new(),
anon_types: HashMap::new(),
lang_items: HashMap::new(),
unchecked: Vec::new(),
impls: Vec::new(),
uses: Vec::new(),
}
}
pub fn entry(&self, handle: Handle) -> Entry<'_, 'a> {
handle.to_entry(self)
}
pub fn entry_mut(&mut self, handle: Handle) -> EntryMut<'_, 'a> {
handle.to_entry_mut(self)
}
pub fn new_entry(&mut self, parent: Handle, kind: NodeKind) -> Handle {
let entry = self.kinds.insert(kind);
assert_eq!(entry, self.parents.insert(parent));
entry
}
pub fn add_child(&mut self, parent: Handle, name: Sym, child: Handle) -> Option<Handle> {
self.children.entry(parent).or_default().insert(name, child)
}
pub fn add_import(&mut self, parent: Handle, name: Sym, import: Handle) -> Option<Handle> {
self.imports.entry(parent).or_default().insert(name, import)
}
pub fn mark_unchecked(&mut self, item: Handle) {
self.unchecked.push(item);
}
pub fn mark_use_item(&mut self, item: Handle) {
let parent = self.parents[item];
self.use_items.entry(parent).or_default().push(item);
self.uses.push(item);
}
pub fn mark_impl_item(&mut self, item: Handle) {
self.impls.push(item);
}
pub fn mark_lang_item(&mut self, name: &'static str, item: Handle) {
self.lang_items.insert(name, item);
}
pub fn get_lang_item(&self, name: &str) -> Handle {
match self.lang_items.get(name).copied() {
Some(handle) => handle,
None => todo!(),
}
}
pub fn handle_iter(&self) -> impl Iterator<Item = Handle> + use<> {
self.kinds.keys()
}
/// Returns handles to all nodes sequentially by [Entry]
pub fn debug_entry_iter(&self) -> impl Iterator<Item = Entry<'_, 'a>> {
self.kinds.keys().map(|key| key.to_entry(self))
}
/// Gets the [Handle] of an anonymous type with the provided [TypeKind].
/// If not already present, a new one is created.
pub(crate) fn anon_type(&mut self, kind: TypeKind) -> Handle {
if let Some(id) = self.anon_types.get(&kind) {
return *id;
}
let entry = self.new_entry(self.root, NodeKind::Type);
// Anonymous types require a bijective map (anon_types => Def => types)
self.types.insert(entry, kind.clone());
self.anon_types.insert(kind, entry);
entry
}
pub(crate) fn inferred_type(&mut self) -> Handle {
let handle = self.new_entry(self.root, NodeKind::Type);
self.types.insert(handle, TypeKind::Inferred);
handle
}
pub(crate) fn type_variable(&mut self) -> Handle {
let handle = self.new_entry(self.root, NodeKind::Type);
self.types.insert(handle, TypeKind::Variable);
handle
}
pub const fn root_entry(&self) -> Entry<'_, 'a> {
self.root.to_entry(self)
}
pub fn root_entry_mut(&mut self) -> crate::entry::EntryMut<'_, 'a> {
self.root.to_entry_mut(self)
}
// --- inherent properties ---
pub const fn root(&self) -> Handle {
self.root
}
pub fn kind(&self, node: Handle) -> Option<&NodeKind> {
self.kinds.get(node)
}
pub fn parent(&self, node: Handle) -> Option<&Handle> {
self.parents.get(node)
}
pub fn children(&self, node: Handle) -> Option<&HashMap<Sym, Handle>> {
self.children.get(&node)
}
pub fn imports(&self, node: Handle) -> Option<&HashMap<Sym, Handle>> {
self.imports.get(&node)
}
pub fn body(&self, node: Handle) -> Option<&'a Expr> {
self.bodies.get(&node).copied()
}
pub fn ty(&self, node: Handle) -> Option<&TypeKind> {
self.types.get(&node)
}
pub fn span(&self, node: Handle) -> Option<&Span> {
self.spans.get(&node)
}
pub fn meta(&self, node: Handle) -> Option<&'a [Meta]> {
self.metas.get(&node).copied()
}
pub fn source(&self, node: Handle) -> Option<&Source<'a>> {
self.sources.get(&node)
}
pub fn impl_target(&self, node: Handle) -> Option<Handle> {
self.impl_targets.get(&node).copied()
}
pub fn reparent(&mut self, node: Handle, parent: Handle) -> Handle {
self.parents.replace(node, parent)
}
pub fn set_body(&mut self, node: Handle, body: &'a Expr) -> Option<&'a Expr> {
self.mark_unchecked(node);
self.bodies.insert(node, body)
}
pub fn set_ty(&mut self, node: Handle, kind: TypeKind) -> Option<TypeKind> {
self.types.insert(node, kind)
}
pub fn set_span(&mut self, node: Handle, span: Span) -> Option<Span> {
self.spans.insert(node, span)
}
pub fn set_meta(&mut self, node: Handle, meta: &'a [Meta]) -> Option<&'a [Meta]> {
self.metas.insert(node, meta)
}
pub fn set_source(&mut self, node: Handle, source: Source<'a>) -> Option<Source<'a>> {
self.sources.insert(node, source)
}
pub fn set_impl_target(&mut self, node: Handle, target: Handle) -> Option<Handle> {
self.impl_targets.insert(node, target)
}
// --- derived properties ---
/// Gets a handle to the local `Self` type, if one exists
pub fn selfty(&self, node: Handle) -> Option<Handle> {
match self.kinds.get(node)? {
NodeKind::Root | NodeKind::Use => None,
NodeKind::Type => Some(node),
NodeKind::Impl => self.impl_target(node),
_ => self.selfty(*self.parent(node)?),
}
}
pub fn super_of(&self, node: Handle) -> Option<Handle> {
match self.kinds.get(node)? {
NodeKind::Root => None,
NodeKind::Module => self.parent(node).copied(),
_ => self.super_of(*self.parent(node)?),
}
}
pub fn name(&self, node: Handle) -> Option<Sym> {
self.source(node).and_then(|s| s.name())
}
pub fn is_transparent(&self, node: Handle) -> bool {
!matches!(
self.kind(node),
None | Some(NodeKind::Root | NodeKind::Module)
)
}
pub fn get_child(&self, node: Handle, name: &Sym) -> Option<Handle> {
self.children.get(&node).and_then(|c| c.get(name)).copied()
}
pub fn get_import(&self, node: Handle, name: &Sym) -> Option<Handle> {
self.imports.get(&node).and_then(|i| i.get(name)).copied()
}
pub fn get_by_sym(&self, node: Handle, name: &Sym) -> Option<Handle> {
self.get_child(node, name)
.or_else(|| self.get_import(node, name))
.or_else(|| {
self.is_transparent(node)
.then(|| {
self.parent(node)
.and_then(|node| self.get_by_sym(*node, name))
})
.flatten()
})
}
/// Does path traversal relative to the provided `node`.
pub fn nav(&self, node: Handle, path: &[PathPart]) -> Option<Handle> {
match path {
[PathPart::SuperKw, rest @ ..] => self.nav(self.super_of(node)?, rest),
[PathPart::SelfTy, rest @ ..] => self.nav(self.selfty(node)?, rest),
[PathPart::Ident(name), rest @ ..] => self.nav(self.get_by_sym(node, name)?, rest),
[] => Some(node),
}
}
}
impl Default for Table<'_> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug)]
pub enum NodeKind {
Root,
Module,
Type,
Const,
Static,
Function,
Temporary,
Let,
Scope,
Impl,
Use,
}
mod display {
use super::*;
use std::fmt;
impl fmt::Display for NodeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NodeKind::Root => write!(f, "root"),
NodeKind::Module => write!(f, "mod"),
NodeKind::Type => write!(f, "type"),
NodeKind::Const => write!(f, "const"),
NodeKind::Static => write!(f, "static"),
NodeKind::Function => write!(f, "fn"),
NodeKind::Temporary => write!(f, "temp"),
NodeKind::Let => write!(f, "let"),
NodeKind::Scope => write!(f, "scope"),
NodeKind::Use => write!(f, "use"),
NodeKind::Impl => write!(f, "impl"),
}
}
}
}

View File

@@ -0,0 +1,144 @@
//! A [TypeExpression] is a [syntactic](cl_ast) representation of a [TypeKind], and is used to
//! construct type bindings in a [Table]'s typing context.
use crate::{handle::Handle, table::Table, type_kind::TypeKind};
use cl_ast::{PathPart, Sym, Ty, TyArray, TyFn, TyKind, TyPtr, TyRef, TySlice, TyTuple};
#[derive(Clone, Debug, PartialEq, Eq)] // TODO: impl Display and Error
pub enum Error {
BadPath { parent: Handle, path: Vec<PathPart> },
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::BadPath { parent, path } => {
write!(f, "No item at path {parent}")?;
for part in path {
write!(f, "::{part}")?;
}
}
}
Ok(())
}
}
/// A [TypeExpression] is a syntactic representation of a [TypeKind], and is used to construct
/// type bindings in a [Table]'s typing context.
pub trait TypeExpression<Out = Handle> {
/// Evaluates a type expression, recursively creating intermediate bindings.
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Out, Error>;
}
impl TypeExpression for Ty {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
self.kind.evaluate(table, node)
}
}
impl TypeExpression for TyKind {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
match self {
TyKind::Never => Ok(table.get_lang_item("never")),
TyKind::Infer => Ok(table.inferred_type()),
TyKind::Path(p) => p.evaluate(table, node),
TyKind::Array(a) => a.evaluate(table, node),
TyKind::Slice(s) => s.evaluate(table, node),
TyKind::Tuple(t) => t.evaluate(table, node),
TyKind::Ref(r) => r.evaluate(table, node),
TyKind::Ptr(r) => r.evaluate(table, node),
TyKind::Fn(f) => f.evaluate(table, node),
}
}
}
impl TypeExpression for cl_ast::Path {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { absolute, parts } = self;
parts.evaluate(table, if *absolute { table.root() } else { node })
}
}
impl TypeExpression for [PathPart] {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
table
.nav(node, self)
.ok_or_else(|| Error::BadPath { parent: node, path: self.to_owned() })
}
}
impl TypeExpression for Sym {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let path = [PathPart::Ident(*self)];
table
.nav(node, &path)
.ok_or_else(|| Error::BadPath { parent: node, path: path.to_vec() })
}
}
impl TypeExpression for TyArray {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { ty, count } = self;
let kind = TypeKind::Array(ty.evaluate(table, node)?, *count);
Ok(table.anon_type(kind))
}
}
impl TypeExpression for TySlice {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { ty } = self;
let kind = TypeKind::Slice(ty.evaluate(table, node)?);
Ok(table.anon_type(kind))
}
}
impl TypeExpression for TyTuple {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { types } = self;
let kind = TypeKind::Tuple(types.evaluate(table, node)?);
Ok(table.anon_type(kind))
}
}
impl TypeExpression for TyRef {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { mutable: _, count, to } = self;
let mut t = to.evaluate(table, node)?;
for _ in 0..*count {
let kind = TypeKind::Ref(t);
t = table.anon_type(kind)
}
Ok(t)
}
}
impl TypeExpression for TyPtr {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { to } = self;
let mut t = to.evaluate(table, node)?;
t = table.anon_type(TypeKind::Ptr(t));
Ok(t)
}
}
impl TypeExpression for TyFn {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { args, rety } = self;
let kind = TypeKind::FnSig {
args: args.evaluate(table, node)?,
rety: rety.evaluate(table, node)?,
};
Ok(table.anon_type(kind))
}
}
impl<T: TypeExpression<U>, U> TypeExpression<Vec<U>> for [T] {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Vec<U>, Error> {
let mut out = Vec::with_capacity(self.len());
for te in self {
out.push(te.evaluate(table, node)?) // try_collect is unstable
}
Ok(out)
}
}

View File

@@ -0,0 +1,125 @@
//! A [TypeKind] is a node in the [Table](crate::table::Table)'s type graph
use crate::handle::Handle;
use cl_ast::{Sym, Visibility};
use std::{fmt::Debug, str::FromStr};
mod display;
/// A [TypeKind] represents an item
/// (a component of a [Table](crate::table::Table))
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeKind {
/// A type that is yet to be inferred!
Inferred,
/// A type variable, to be monomorphized
Variable,
/// An alias for an already-defined type
Instance(Handle),
/// A primitive type, built-in to the compiler
Primitive(Primitive),
/// A user-defined aromatic data type
Adt(Adt),
/// A reference to an already-defined type: &T
Ref(Handle),
/// A raw pointer to an already-defined type: &T
Ptr(Handle),
/// A contiguous view of dynamically sized memory
Slice(Handle),
/// A contiguous view of statically sized memory
Array(Handle, usize),
/// A tuple of existing types
Tuple(Vec<Handle>),
/// A function which accepts multiple inputs and produces an output
FnSig { args: Handle, rety: Handle },
/// An untyped module
Module,
}
/// A user-defined Aromatic Data Type
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adt {
/// A union-like enum type
Enum(Vec<(Sym, Handle)>),
/// A structural product type with named members
Struct(Vec<(Sym, Visibility, Handle)>),
/// A structural product type with unnamed members
TupleStruct(Vec<(Visibility, Handle)>),
/// A structural product type of neither named nor unnamed members
UnitStruct,
/// A choose your own undefined behavior type
/// TODO: should unions be a language feature?
Union(Vec<(Sym, Handle)>),
}
/// The set of compiler-intrinsic types.
/// These primitive types have native implementations of the basic operations.
#[rustfmt::skip]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Primitive {
I8, I16, I32, I64, I128, Isize, // Signed integers
U8, U16, U32, U64, U128, Usize, // Unsigned integers
F8, F16, F32, F64, F128, Fsize, // Floating point numbers
Integer, Float, // Inferred int and float
Bool, // boolean value
Char, // Unicode codepoint
Str, // UTF-8 string
Never, // The never type
}
#[rustfmt::skip]
impl Primitive {
/// Checks whether self is an integer
pub fn is_integer(self) -> bool {
matches!(
self,
| Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 | Self::Isize
| Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::U128 | Self::Usize
| Self::Integer
)
}
/// Checks whether self is a floating point number
pub fn is_float(self) -> bool {
matches!(
self,
| Self::F8 | Self::F16 | Self::F32 | Self::F64 | Self::F128 | Self::Fsize
| Self::Float
)
}
}
// Author's note: the fsize type is a meme
impl FromStr for Primitive {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"i8" => Primitive::I8,
"i16" => Primitive::I16,
"i32" => Primitive::I32,
"i64" => Primitive::I64,
"i128" => Primitive::I128,
"isize" => Primitive::Isize,
"u8" => Primitive::U8,
"u16" => Primitive::U16,
"u32" => Primitive::U32,
"u64" => Primitive::U64,
"u128" => Primitive::U128,
"usize" => Primitive::Usize,
"f8" => Primitive::F8,
"f16" => Primitive::F16,
"f32" => Primitive::F32,
"f64" => Primitive::F64,
"f128" => Primitive::F128,
"fsize" => Primitive::Fsize,
"bool" => Primitive::Bool,
"char" => Primitive::Char,
"str" => Primitive::Str,
"never" => Primitive::Never,
_ => Err(())?,
})
}
}

View File

@@ -0,0 +1,98 @@
//! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
use super::{Adt, Primitive, TypeKind};
use crate::format_utils::*;
use cl_ast::format::FmtAdapter;
use std::fmt::{self, Display, Write};
impl Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeKind::Inferred => write!(f, "_"),
TypeKind::Variable => write!(f, "?"),
TypeKind::Instance(def) => write!(f, "alias to #{def}"),
TypeKind::Primitive(i) => i.fmt(f),
TypeKind::Adt(a) => a.fmt(f),
TypeKind::Ref(def) => write!(f, "&{def}"),
TypeKind::Ptr(def) => write!(f, "*{def}"),
TypeKind::Slice(def) => write!(f, "slice [#{def}]"),
TypeKind::Array(def, size) => write!(f, "array [#{def}; {size}]"),
TypeKind::Tuple(defs) => {
let mut defs = defs.iter();
separate(", ", || {
let def = defs.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "#{def}"))
})(f.delimit_with("tuple (", ")"))
}
TypeKind::FnSig { args, rety } => write!(f, "fn (#{args}) -> #{rety}"),
TypeKind::Module => f.write_str("mod"),
}
}
}
impl Display for Adt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Adt::Enum(variants) => {
let mut variants = variants.iter();
separate(", ", || {
let (name, def) = variants.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{name}: #{def}"))
})(f.delimit_with("enum {", "}"))
}
Adt::Struct(members) => {
let mut members = members.iter();
separate(", ", || {
let (name, vis, def) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{vis}{name}: #{def}"))
})(f.delimit_with("struct {", "}"))
}
Adt::TupleStruct(members) => {
let mut members = members.iter();
separate(", ", || {
let (vis, def) = members.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{vis}#{def}"))
})(f.delimit_with("struct (", ")"))
}
Adt::UnitStruct => write!(f, "struct"),
Adt::Union(variants) => {
let mut variants = variants.iter();
separate(", ", || {
let (name, def) = variants.next()?;
Some(move |f: &mut Delimit<_>| write!(f, "{name}: #{def}"))
})(f.delimit_with("union {", "}"))
}
}
}
}
impl Display for Primitive {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Primitive::I8 => f.write_str("i8"),
Primitive::I16 => f.write_str("i16"),
Primitive::I32 => f.write_str("i32"),
Primitive::I64 => f.write_str("i64"),
Primitive::I128 => f.write_str("i128"),
Primitive::Isize => f.write_str("isize"),
Primitive::U8 => f.write_str("u8"),
Primitive::U16 => f.write_str("u16"),
Primitive::U32 => f.write_str("u32"),
Primitive::U64 => f.write_str("u64"),
Primitive::U128 => f.write_str("u128"),
Primitive::Usize => f.write_str("usize"),
Primitive::F8 => f.write_str("f8"),
Primitive::F16 => f.write_str("f16"),
Primitive::F32 => f.write_str("f32"),
Primitive::F64 => f.write_str("f64"),
Primitive::F128 => f.write_str("f128"),
Primitive::Fsize => f.write_str("fsize"),
Primitive::Integer => f.write_str("{integer}"),
Primitive::Float => f.write_str("{float}"),
Primitive::Bool => f.write_str("bool"),
Primitive::Char => f.write_str("char"),
Primitive::Str => f.write_str("str"),
Primitive::Never => f.write_str("!"),
}
}
}

View File

@@ -1,336 +0,0 @@
//! Performs step 2 of type checking: Evaluating type definitions
use crate::{
definition::{Adt, Def, DefKind, TypeKind, ValueKind},
handle::DefID,
node::{Node, NodeSource},
project::{evaluate::EvaluableTypeExpression, Project as Prj},
};
use cl_ast::*;
/// Evaluate a single ID
pub fn resolve(prj: &mut Prj, id: DefID) -> Result<(), &'static str> {
let Def { node: Node { kind: Some(source), meta, .. }, kind: DefKind::Undecided, .. } = prj[id]
else {
return Ok(());
};
let kind = match &source {
NodeSource::Root => "root",
NodeSource::Alias(_) => "type",
NodeSource::Module(_) => "mod",
NodeSource::Enum(_) => "enum",
NodeSource::Variant(_) => "variant",
NodeSource::Struct(_) => "struct",
NodeSource::Const(_) => "const",
NodeSource::Static(_) => "static",
NodeSource::Function(_) => "fn",
NodeSource::Impl(_) => "impl",
NodeSource::Use(_) => "use",
NodeSource::Local(_) => "let",
NodeSource::Ty(_) => "ty",
};
let name = prj[id].name().unwrap_or("".into());
eprintln!("Resolver: \x1b[32mEvaluating\x1b[0m \"\x1b[36m{kind} {name}\x1b[0m\" (`{id:?}`)");
for Meta { name, kind } in meta {
if let ("intrinsic", MetaKind::Equals(Literal::String(s))) = (&**name, kind) {
prj[id].kind = DefKind::Type(TypeKind::Intrinsic(
s.parse().map_err(|_| "Failed to parse intrinsic")?,
));
}
}
if DefKind::Undecided == prj[id].kind {
prj[id].kind = source.resolve_type(prj, id)?;
}
eprintln!("\x1b[33m=> {}\x1b[0m", prj[id].kind);
Ok(())
}
/// Resolves a given node
pub trait TypeResolvable<'a> {
/// The return type upon success
type Out;
/// Resolves type expressions within this node
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str>;
}
impl<'a> TypeResolvable<'a> for NodeSource<'a> {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
match self {
NodeSource::Root => Ok(DefKind::Type(TypeKind::Module)),
NodeSource::Module(v) => v.resolve_type(prj, id),
NodeSource::Alias(v) => v.resolve_type(prj, id),
NodeSource::Enum(v) => v.resolve_type(prj, id),
NodeSource::Variant(v) => v.resolve_type(prj, id),
NodeSource::Struct(v) => v.resolve_type(prj, id),
NodeSource::Const(v) => v.resolve_type(prj, id),
NodeSource::Static(v) => v.resolve_type(prj, id),
NodeSource::Function(v) => v.resolve_type(prj, id),
NodeSource::Local(v) => v.resolve_type(prj, id),
NodeSource::Impl(v) => v.resolve_type(prj, id),
NodeSource::Use(v) => v.resolve_type(prj, id),
NodeSource::Ty(v) => v.resolve_type(prj, id),
}
}
}
impl<'a> TypeResolvable<'a> for &'a Meta {
type Out = DefKind;
#[allow(unused_variables)]
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Meta { name, kind } = self;
match (name.as_ref(), kind) {
("intrinsic", MetaKind::Equals(Literal::String(intrinsic))) => Ok(DefKind::Type(
TypeKind::Intrinsic(intrinsic.parse().map_err(|_| "unknown intrinsic type")?),
)),
(_, MetaKind::Plain) => Ok(DefKind::Type(TypeKind::Intrinsic(
name.parse().map_err(|_| "Unknown intrinsic type")?,
))),
_ => Err("Unknown meta attribute"),
}
}
}
impl<'a> TypeResolvable<'a> for &'a Module {
type Out = DefKind;
#[allow(unused_variables)]
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
Ok(DefKind::Type(TypeKind::Module))
}
}
impl<'a> TypeResolvable<'a> for &'a Alias {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let alias = if let Some(ty) = &self.from {
Some(
ty.evaluate(prj, parent)
.or_else(|_| ty.evaluate(prj, id))
.map_err(|_| "Unresolved type in alias")?,
)
} else {
None
};
Ok(DefKind::Type(TypeKind::Alias(alias)))
}
}
impl<'a> TypeResolvable<'a> for &'a Enum {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Enum { name: _, kind } = self;
let EnumKind::Variants(v) = kind else {
return Ok(DefKind::Type(TypeKind::Adt(Adt::FieldlessEnum)));
};
let mut fields = vec![];
for Variant { name, kind: _ } in v {
let id = prj[id].module.get_type(*name);
fields.push((*name, id))
}
Ok(DefKind::Type(TypeKind::Adt(Adt::Enum(fields))))
}
}
impl<'a> TypeResolvable<'a> for &'a Variant {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
// Get the grandparent of this node, for name resolution
let parent = prj.parent_of(id).unwrap_or(id);
let grandparent = prj.parent_of(parent).unwrap_or(parent);
let Variant { name: _, kind } = self;
Ok(DefKind::Type(match kind {
VariantKind::Plain => return Ok(DefKind::Type(TypeKind::Empty)),
VariantKind::CLike(_) => return Ok(DefKind::Undecided),
VariantKind::Tuple(ty) => match &ty.kind {
TyKind::Empty => TypeKind::Tuple(vec![]),
TyKind::Tuple(TyTuple { types }) => {
TypeKind::Tuple(types.evaluate(prj, grandparent).map_err(|e| {
eprintln!("{e}");
""
})?)
}
_ => Err("Unexpected TyKind in tuple variant")?,
},
VariantKind::Struct(members) => {
TypeKind::Adt(Adt::Struct(members.resolve_type(prj, parent)?))
}
}))
}
}
impl<'a> TypeResolvable<'a> for &'a Struct {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Struct { name: _, kind } = self;
Ok(match kind {
StructKind::Empty => DefKind::Type(TypeKind::Empty),
StructKind::Tuple(types) => DefKind::Type(TypeKind::Adt(Adt::TupleStruct({
let mut out = vec![];
for ty in types {
out.push((
Visibility::Public,
ty.evaluate(prj, parent)
.map_err(|_| "Unresolved type in tuple-struct member")?,
));
}
out
}))),
StructKind::Struct(members) => {
DefKind::Type(TypeKind::Adt(Adt::Struct(members.resolve_type(prj, id)?)))
}
})
}
}
impl<'a> TypeResolvable<'a> for &'a StructMember {
type Out = (Sym, Visibility, DefID);
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let StructMember { name, vis, ty } = self;
let ty = ty
.evaluate(prj, parent)
.map_err(|_| "Invalid type while resolving StructMember")?;
Ok((*name, *vis, ty))
}
}
impl<'a> TypeResolvable<'a> for &'a Const {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Const { ty, .. } = self;
let ty = ty
.evaluate(prj, id)
.map_err(|_| "Invalid type while resolving const")?;
Ok(DefKind::Value(ValueKind::Const(ty)))
}
}
impl<'a> TypeResolvable<'a> for &'a Static {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Static { ty, .. } = self;
let ty = ty
.evaluate(prj, parent)
.map_err(|_| "Invalid type while resolving static")?;
Ok(DefKind::Value(ValueKind::Static(ty)))
}
}
impl<'a> TypeResolvable<'a> for &'a Function {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let Function { sign, .. } = self;
let sign = sign
.evaluate(prj, parent)
.map_err(|_| "Invalid type in function signature")?;
Ok(DefKind::Value(ValueKind::Fn(sign)))
}
}
impl<'a> TypeResolvable<'a> for &'a Let {
type Out = DefKind;
#[allow(unused)]
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let Let { mutable, name, ty, init } = self;
Ok(DefKind::Undecided)
}
}
impl<'a> TypeResolvable<'a> for &'a Impl {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let parent = prj.parent_of(id).unwrap_or(id);
let target = match &self.target {
ImplKind::Type(t) => t.evaluate(prj, parent),
ImplKind::Trait { for_type, .. } => for_type.evaluate(prj, parent),
}
.map_err(|_| "Unresolved type in impl target")?;
match prj.pool.get_many_mut([id, target]) {
// TODO: Better error handling
Err(_) => Err(concat!(
file!(),
line!(),
column!(),
"id and target are same"
))?,
Ok([id, target]) => {
for (name, def) in &id.module.types {
target.module.insert_type(*name, *def);
}
for (name, def) in &id.module.values {
target.module.insert_value(*name, *def);
}
}
}
Ok(DefKind::Impl(target))
}
}
impl<'a> TypeResolvable<'a> for &'a Use {
type Out = DefKind;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
todo!("Resolve types for {self} with ID {id} in {prj:?}")
}
}
impl<'a> TypeResolvable<'a> for &'a TyKind {
type Out = DefKind;
#[allow(unused)]
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
todo!()
}
}
impl<'a, T> TypeResolvable<'a> for &'a [T]
where &'a T: TypeResolvable<'a>
{
type Out = Vec<<&'a T as TypeResolvable<'a>>::Out>;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
let mut members = vec![];
for member in self {
members.push(member.resolve_type(prj, id)?);
}
Ok(members)
}
}
impl<'a, T> TypeResolvable<'a> for Option<&'a T>
where &'a T: TypeResolvable<'a>
{
type Out = Option<<&'a T as TypeResolvable<'a>>::Out>;
fn resolve_type(self, prj: &mut Prj<'a>, id: DefID) -> Result<Self::Out, &'static str> {
match self {
Some(t) => Some(t.resolve_type(prj, id)).transpose(),
None => Ok(None),
}
}
}

View File

@@ -1,125 +0,0 @@
//! WIP use-item importer. This performs eager import resolution on the AST
//!
//! # TODOs:
//! - [ ] Resolve imports using a graph traversal rather than linear iteration
//! - [ ] Separate imported items from natively declared items
//! - [ ] Separate the use-import pass from the project
//! - [ ] Report errors in a meaningful way
//! - [ ] Lazy import resolution using graph-edge traversal during name lookup?
//! - It doesn't seem to me like the imports in a given scope *can change*.
#![allow(unused)]
use std::fmt::format;
use cl_ast::*;
use crate::{
definition::{Def, DefKind},
handle::DefID,
node::NodeSource,
project::Project,
};
type UseResult = Result<(), String>;
impl<'a> Project<'a> {
pub fn resolve_imports(&mut self) -> UseResult {
for id in self.pool.keys() {
self.visit_def(id)?;
}
Ok(())
}
pub fn visit_def(&mut self, id: DefID) -> UseResult {
let Def { kind, node, module } = &self.pool[id];
if let (DefKind::Use(parent), Some(NodeSource::Use(u))) = (kind, node.kind) {
println!("Importing use item {u}");
self.visit_use(u, *parent);
}
Ok(())
}
pub fn visit_use(&mut self, u: &'a Use, parent: DefID) -> UseResult {
let Use { absolute, tree } = u;
self.visit_use_tree(tree, parent, if *absolute { self.root } else { parent })
}
pub fn visit_use_tree(&mut self, tree: &'a UseTree, parent: DefID, c: DefID) -> UseResult {
match tree {
UseTree::Tree(trees) => {
for tree in trees {
self.visit_use_tree(tree, parent, c)?;
}
}
UseTree::Path(part, rest) => {
let c = self.evaluate(part, c)?;
self.visit_use_tree(rest, parent, c)?;
}
UseTree::Name(name) => self.visit_use_leaf(name, parent, c)?,
UseTree::Alias(from, to) => self.visit_use_alias(from, to, parent, c)?,
UseTree::Glob => self.visit_use_glob(parent, c)?,
}
Ok(())
}
pub fn visit_use_path(&mut self) -> UseResult {
Ok(())
}
pub fn visit_use_leaf(&mut self, name: &'a Sym, parent: DefID, c: DefID) -> UseResult {
self.visit_use_alias(name, name, parent, c)
}
pub fn visit_use_alias(
&mut self,
from: &Sym,
name: &Sym,
parent: DefID,
c: DefID,
) -> UseResult {
let mut imported = false;
let c_mod = &self[c].module;
let (tid, vid) = (
c_mod.types.get(from).copied(),
c_mod.values.get(from).copied(),
);
let parent = &mut self[parent].module;
if let Some(tid) = tid {
parent.types.insert(*name, tid);
imported = true;
}
if let Some(vid) = vid {
parent.values.insert(*name, vid);
imported = true;
}
if imported {
Ok(())
} else {
Err(format!("Identifier {name} not found in module {c}"))
}
}
pub fn visit_use_glob(&mut self, parent: DefID, c: DefID) -> UseResult {
// Loop over all the items in c, and add them as items in the parent
if parent == c {
return Ok(());
}
let [parent, c] = self
.pool
.get_many_mut([parent, c])
.expect("parent and c are not the same");
for (k, v) in &c.module.types {
parent.module.types.entry(*k).or_insert(*v);
}
for (k, v) in &c.module.values {
parent.module.values.entry(*k).or_insert(*v);
}
Ok(())
}
}

View File

@@ -26,7 +26,7 @@ Static = "static" Mutability Identifier ':' Ty '=' Expr ';' ;
Module = "mod" Identifier ModuleKind ;
ModuleKind = '{' Item* '}' | ';' ;
Function = "fn" Identifier '(' (Param ',')* Param? ')' ('->' Ty)? Block? ;
Function = "fn" Identifier '(' (Param ',')* Param? ')' ('->' Ty)? (Expr | ';') ;
Param = Mutability Identifier ':' Ty ;
Struct = "struct" Identifier (StructTuple | StructBody)?;
@@ -96,7 +96,9 @@ Shift = Factor (ShiftOp Factor )* ;
Factor = Term (FactorOp Term )* ;
Term = Unary (TermOp Unary )* ;
Unary = (UnaryKind)* Member ;
Unary = (UnaryKind)* Cast ;
Cast = Member ("as" Ty)? ;
Member = Call (Access)* ;
Access = '.' (Identifier ('(' Tuple? ')')? | Literal) ;
@@ -125,6 +127,17 @@ Block = '{' Stmt* '}';
Group = Empty | '(' (Expr | Tuple) ')' ;
Tuple = (Expr ',')* Expr? ;
Match = "match" { (MatchArm ',')* MatchArm? } ;
MatchArm = Pattern '=>' Expr ;
Pattern = Path
| Literal
| '&' "mut"? Pattern
| '(' (Pattern ',')* (Pattern | '..' )? ')'
| '[' (Pattern ',')* (Pattern | '..' Identifier?)? ']'
| StructPattern
;
Loop = "loop" Block ;
While = "while" Expr Block Else ;
If = "if" Expr Block Else ;

View File

@@ -1,11 +0,0 @@
[package]
name = "repline"
repository.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
crossterm = { version = "0.27.0", default-features = false }

View File

@@ -1,324 +0,0 @@
//! The [Editor] is a multi-line buffer of [`char`]s which operates on an ANSI-compatible terminal.
use crossterm::{cursor::*, execute, queue, style::*, terminal::*};
use std::{collections::VecDeque, fmt::Display, io::Write};
use super::error::{Error, ReplResult};
fn is_newline(c: &char) -> bool {
*c == '\n'
}
fn write_chars<'a, W: Write>(
c: impl IntoIterator<Item = &'a char>,
w: &mut W,
) -> std::io::Result<()> {
for c in c {
write!(w, "{c}")?;
}
Ok(())
}
/// A multi-line editor which operates on an un-cleared ANSI terminal.
#[derive(Clone, Debug)]
pub struct Editor<'a> {
head: VecDeque<char>,
tail: VecDeque<char>,
pub color: &'a str,
begin: &'a str,
again: &'a str,
}
impl<'a> Editor<'a> {
/// Constructs a new Editor with the provided prompt color, begin prompt, and again prompt.
pub fn new(color: &'a str, begin: &'a str, again: &'a str) -> Self {
Self { head: Default::default(), tail: Default::default(), color, begin, again }
}
/// Returns an iterator over characters in the editor.
pub fn iter(&self) -> impl Iterator<Item = &char> {
let Self { head, tail, .. } = self;
head.iter().chain(tail.iter())
}
/// Moves up to the first line of the editor, and clears the screen.
///
/// This assumes the screen hasn't moved since the last draw.
pub fn undraw<W: Write>(&self, w: &mut W) -> ReplResult<()> {
let Self { head, .. } = self;
match head.iter().copied().filter(is_newline).count() {
0 => write!(w, "\x1b[0G"),
lines => write!(w, "\x1b[{}F", lines),
}?;
queue!(w, Clear(ClearType::FromCursorDown))?;
// write!(w, "\x1b[0J")?;
Ok(())
}
/// Redraws the entire editor
pub fn redraw<W: Write>(&self, w: &mut W) -> ReplResult<()> {
let Self { head, tail, color, begin, again } = self;
write!(w, "{color}{begin}\x1b[0m ")?;
// draw head
for c in head {
match c {
'\n' => write!(w, "\r\n{color}{again}\x1b[0m "),
_ => w.write_all({ *c as u32 }.to_le_bytes().as_slice()),
}?
}
// save cursor
execute!(w, SavePosition)?;
// draw tail
for c in tail {
match c {
'\n' => write!(w, "\r\n{color}{again}\x1b[0m "),
_ => write!(w, "{c}"),
}?
}
// restore cursor
execute!(w, RestorePosition)?;
Ok(())
}
/// Prints a context-sensitive prompt (either `begin` if this is the first line,
/// or `again` for subsequent lines)
pub fn prompt<W: Write>(&self, w: &mut W) -> ReplResult<()> {
let Self { head, color, begin, again, .. } = self;
queue!(
w,
MoveToColumn(0),
Print(color),
Print(if head.is_empty() { begin } else { again }),
ResetColor,
Print(' '),
)?;
Ok(())
}
/// Prints the characters before the cursor on the current line.
pub fn print_head<W: Write>(&self, w: &mut W) -> ReplResult<()> {
self.prompt(w)?;
write_chars(
self.head.iter().skip(
self.head
.iter()
.rposition(is_newline)
.unwrap_or(self.head.len())
+ 1,
),
w,
)?;
Ok(())
}
/// Prints the characters after the cursor on the current line.
pub fn print_tail<W: Write>(&self, w: &mut W) -> ReplResult<()> {
let Self { tail, .. } = self;
queue!(w, SavePosition, Clear(ClearType::UntilNewLine))?;
write_chars(tail.iter().take_while(|&c| !is_newline(c)), w)?;
queue!(w, RestorePosition)?;
Ok(())
}
/// Writes a character at the cursor, shifting the text around as necessary.
pub fn push<W: Write>(&mut self, c: char, w: &mut W) -> ReplResult<()> {
// Tail optimization: if the tail is empty,
//we don't have to undraw and redraw on newline
if self.tail.is_empty() {
self.head.push_back(c);
match c {
'\n' => {
write!(w, "\r\n")?;
self.print_head(w)?;
}
c => {
queue!(w, Print(c))?;
}
};
return Ok(());
}
if '\n' == c {
self.undraw(w)?;
}
self.head.push_back(c);
match c {
'\n' => self.redraw(w)?,
_ => {
write!(w, "{c}")?;
self.print_tail(w)?;
}
}
Ok(())
}
/// Erases a character at the cursor, shifting the text around as necessary.
pub fn pop<W: Write>(&mut self, w: &mut W) -> ReplResult<Option<char>> {
if let Some('\n') = self.head.back() {
self.undraw(w)?;
}
let c = self.head.pop_back();
// if the character was a newline, we need to go back a line
match c {
Some('\n') => self.redraw(w)?,
Some(_) => {
// go back a char
queue!(w, MoveLeft(1), Print(' '), MoveLeft(1))?;
self.print_tail(w)?;
}
None => {}
}
Ok(c)
}
/// Writes characters into the editor at the location of the cursor.
pub fn extend<T: IntoIterator<Item = char>, W: Write>(
&mut self,
iter: T,
w: &mut W,
) -> ReplResult<()> {
for c in iter {
self.push(c, w)?;
}
Ok(())
}
/// Sets the editor to the contents of a string, placing the cursor at the end.
pub fn restore(&mut self, s: &str) {
self.clear();
self.head.extend(s.chars())
}
/// Clears the editor, removing all characters.
pub fn clear(&mut self) {
self.head.clear();
self.tail.clear();
}
/// Pops the character after the cursor, redrawing if necessary
pub fn delete<W: Write>(&mut self, w: &mut W) -> ReplResult<char> {
match self.tail.front() {
Some('\n') => {
self.undraw(w)?;
let out = self.tail.pop_front();
self.redraw(w)?;
out
}
_ => {
let out = self.tail.pop_front();
self.print_tail(w)?;
out
}
}
.ok_or(Error::EndOfInput)
}
/// Erases a word from the buffer, where a word is any non-whitespace characters
/// preceded by a single whitespace character
pub fn erase_word<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
while self.pop(w)?.filter(|c| !c.is_whitespace()).is_some() {}
Ok(())
}
/// Returns the number of characters in the buffer
pub fn len(&self) -> usize {
self.head.len() + self.tail.len()
}
/// Returns true if the buffer is empty.
pub fn is_empty(&self) -> bool {
self.head.is_empty() && self.tail.is_empty()
}
/// Returns true if the buffer ends with a given pattern
pub fn ends_with(&self, iter: impl DoubleEndedIterator<Item = char>) -> bool {
let mut iter = iter.rev();
let mut head = self.head.iter().rev();
loop {
match (iter.next(), head.next()) {
(None, _) => break true,
(Some(_), None) => break false,
(Some(a), Some(b)) if a != *b => break false,
(Some(_), Some(_)) => continue,
}
}
}
/// Moves the cursor back `steps` steps
pub fn cursor_back<W: Write>(&mut self, steps: usize, w: &mut W) -> ReplResult<()> {
for _ in 0..steps {
if let Some('\n') = self.head.back() {
self.undraw(w)?;
}
let Some(c) = self.head.pop_back() else {
return Ok(());
};
self.tail.push_front(c);
match c {
'\n' => self.redraw(w)?,
_ => queue!(w, MoveLeft(1))?,
}
}
Ok(())
}
/// Moves the cursor forward `steps` steps
pub fn cursor_forward<W: Write>(&mut self, steps: usize, w: &mut W) -> ReplResult<()> {
for _ in 0..steps {
if let Some('\n') = self.tail.front() {
self.undraw(w)?
}
let Some(c) = self.tail.pop_front() else {
return Ok(());
};
self.head.push_back(c);
match c {
'\n' => self.redraw(w)?,
_ => queue!(w, MoveRight(1))?,
}
}
Ok(())
}
/// Moves the cursor to the beginning of the current line
pub fn home<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
loop {
match self.head.back() {
Some('\n') | None => break Ok(()),
Some(_) => self.cursor_back(1, w)?,
}
}
}
/// Moves the cursor to the end of the current line
pub fn end<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
loop {
match self.tail.front() {
Some('\n') | None => break Ok(()),
Some(_) => self.cursor_forward(1, w)?,
}
}
}
}
impl<'a, 'e> IntoIterator for &'e Editor<'a> {
type Item = &'e char;
type IntoIter = std::iter::Chain<
std::collections::vec_deque::Iter<'e, char>,
std::collections::vec_deque::Iter<'e, char>,
>;
fn into_iter(self) -> Self::IntoIter {
self.head.iter().chain(self.tail.iter())
}
}
impl<'a> Display for Editor<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
for c in self.iter() {
f.write_char(*c)?;
}
Ok(())
}
}

View File

@@ -1,42 +0,0 @@
use crate::iter::chars::BadUnicode;
/// Result type for Repline
pub type ReplResult<T> = std::result::Result<T, Error>;
/// Borrowed error (does not implement [Error](std::error::Error)!)
#[derive(Debug)]
pub enum Error {
/// User broke with Ctrl+C
CtrlC(String),
/// User broke with Ctrl+D
CtrlD(String),
/// Invalid unicode codepoint
BadUnicode(u32),
/// Error came from [std::io]
IoFailure(std::io::Error),
/// End of input
EndOfInput,
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::CtrlC(_) => write!(f, "Ctrl+C"),
Error::CtrlD(_) => write!(f, "Ctrl+D"),
Error::BadUnicode(u) => write!(f, "\\u{{{u:x}}} is not a valid unicode codepoint"),
Error::IoFailure(s) => write!(f, "{s}"),
Error::EndOfInput => write!(f, "End of input"),
}
}
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::IoFailure(value)
}
}
impl From<BadUnicode> for Error {
fn from(value: BadUnicode) -> Self {
let BadUnicode(code) = value;
Self::BadUnicode(code)
}
}

View File

@@ -1,68 +0,0 @@
//! Shmancy iterator adapters
pub use chars::Chars;
pub use flatten::Flatten;
pub mod chars {
//! Converts an <code>[Iterator]<Item = [u8]></code> into an
//! <code>[Iterator]<Item = [Result]<[char], [BadUnicode]>></code>
/// Invalid unicode codepoint found when iterating over [Chars]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BadUnicode(pub u32);
impl std::error::Error for BadUnicode {}
impl std::fmt::Display for BadUnicode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self(code) = self;
write!(f, "Bad unicode: {code}")
}
}
/// Converts an <code>[Iterator]<Item = [u8]></code> into an
/// <code>[Iterator]<Item = [char]></code>
#[derive(Clone, Debug)]
pub struct Chars<I: Iterator<Item = u8>>(pub I);
impl<I: Iterator<Item = u8>> Iterator for Chars<I> {
type Item = Result<char, BadUnicode>;
fn next(&mut self) -> Option<Self::Item> {
let Self(bytes) = self;
let start = bytes.next()? as u32;
let (mut out, count) = match start {
start if start & 0x80 == 0x00 => (start, 0), // ASCII valid range
start if start & 0xe0 == 0xc0 => (start & 0x1f, 1), // 1 continuation byte
start if start & 0xf0 == 0xe0 => (start & 0x0f, 2), // 2 continuation bytes
start if start & 0xf8 == 0xf0 => (start & 0x07, 3), // 3 continuation bytes
_ => return None,
};
for _ in 0..count {
let cont = bytes.next()? as u32;
if cont & 0xc0 != 0x80 {
return None;
}
out = out << 6 | (cont & 0x3f);
}
Some(char::from_u32(out).ok_or(BadUnicode(out)))
}
}
}
pub mod flatten {
//! Flattens an [Iterator] returning [`Result<T, E>`](Result) or [`Option<T>`](Option)
//! into a *non-[FusedIterator](std::iter::FusedIterator)* over `T`
/// Flattens an [Iterator] returning [`Result<T, E>`](Result) or [`Option<T>`](Option)
/// into a *non-[FusedIterator](std::iter::FusedIterator)* over `T`
#[derive(Clone, Debug)]
pub struct Flatten<T, I: Iterator<Item = T>>(pub I);
impl<T, E, I: Iterator<Item = Result<T, E>>> Iterator for Flatten<Result<T, E>, I> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()?.ok()
}
}
impl<T, I: Iterator<Item = Option<T>>> Iterator for Flatten<Option<T>, I> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()?
}
}
}

Some files were not shown because too many files have changed in this diff Show More