Compare commits

...

83 Commits

Author SHA1 Message Date
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
78 changed files with 8296 additions and 3327 deletions

View File

@ -2,6 +2,7 @@
members = [ members = [
"compiler/cl-repl", "compiler/cl-repl",
"compiler/cl-typeck", "compiler/cl-typeck",
"compiler/cl-embed",
"compiler/cl-interpret", "compiler/cl-interpret",
"compiler/cl-structures", "compiler/cl-structures",
"compiler/cl-token", "compiler/cl-token",
@ -16,7 +17,7 @@ resolver = "2"
repository = "https://git.soft.fish/j/Conlang" repository = "https://git.soft.fish/j/Conlang"
version = "0.0.9" version = "0.0.9"
authors = ["John Breaux <j@soft.fish>"] authors = ["John Breaux <j@soft.fish>"]
edition = "2021" edition = "2024"
license = "MIT" license = "MIT"
publish = ["soft-fish"] publish = ["soft-fish"]

View File

@ -31,19 +31,10 @@ pub enum Visibility {
Public, Public,
} }
/// 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 list of [Item]s /// A list of [Item]s
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct File { pub struct File {
pub name: &'static str,
pub items: Vec<Item>, pub items: Vec<Item>,
} }
@ -72,7 +63,7 @@ pub enum MetaKind {
/// Anything that can appear at the top level of a [File] /// Anything that can appear at the top level of a [File]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Item { pub struct Item {
pub extents: Span, pub span: Span,
pub attrs: Attrs, pub attrs: Attrs,
pub vis: Visibility, pub vis: Visibility,
pub kind: ItemKind, pub kind: ItemKind,
@ -102,10 +93,23 @@ pub enum ItemKind {
Use(Use), 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] /// An alias to another [Ty]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Alias { pub struct Alias {
pub to: Sym, pub name: Sym,
pub from: Option<Box<Ty>>, pub from: Option<Box<Ty>>,
} }
@ -126,40 +130,21 @@ pub struct Static {
pub init: Box<Expr>, 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 /// Code, and the interface to that code
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Function { pub struct Function {
pub name: Sym, pub name: Sym,
pub gens: Generics,
pub sign: TyFn, pub sign: TyFn,
pub bind: Vec<Param>, pub bind: Pattern,
pub body: Option<Block>, pub body: Option<Expr>,
}
/// A single parameter for a [Function]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Param {
pub mutability: Mutability,
pub name: Sym,
} }
/// A user-defined product type /// A user-defined product type
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Struct { pub struct Struct {
pub name: Sym, pub name: Sym,
pub gens: Generics,
pub kind: StructKind, pub kind: StructKind,
} }
@ -183,31 +168,16 @@ pub struct StructMember {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Enum { pub struct Enum {
pub name: Sym, pub name: Sym,
pub kind: EnumKind, pub gens: Generics,
} pub variants: Vec<Variant>,
/// 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>),
} }
/// A single [Enum] variant /// A single [Enum] variant
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Variant { pub struct Variant {
pub name: Sym, pub name: Sym,
pub kind: VariantKind, pub kind: StructKind,
} pub body: Option<Box<Expr>>,
/// 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>),
} }
/// Sub-[items](Item) (associated functions, etc.) for a [Ty] /// Sub-[items](Item) (associated functions, etc.) for a [Ty]
@ -244,7 +214,7 @@ pub enum UseTree {
/// A type expression /// A type expression
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Ty { pub struct Ty {
pub extents: Span, pub span: Span,
pub kind: TyKind, pub kind: TyKind,
} }
@ -253,6 +223,7 @@ pub struct Ty {
pub enum TyKind { pub enum TyKind {
Never, Never,
Empty, Empty,
Infer,
Path(Path), Path(Path),
Array(TyArray), Array(TyArray),
Slice(TySlice), Slice(TySlice),
@ -285,7 +256,7 @@ pub struct TyTuple {
pub struct TyRef { pub struct TyRef {
pub mutable: Mutability, pub mutable: Mutability,
pub count: u16, pub count: u16,
pub to: Path, pub to: Box<Ty>,
} }
/// The args and return value for a function pointer [Ty]pe /// The args and return value for a function pointer [Ty]pe
@ -306,7 +277,6 @@ pub struct Path {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PathPart { pub enum PathPart {
SuperKw, SuperKw,
SelfKw,
SelfTy, SelfTy,
Ident(Sym), Ident(Sym),
} }
@ -314,7 +284,7 @@ pub enum PathPart {
/// An abstract statement, and associated metadata /// An abstract statement, and associated metadata
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Stmt { pub struct Stmt {
pub extents: Span, pub span: Span,
pub kind: StmtKind, pub kind: StmtKind,
pub semi: Semi, pub semi: Semi,
} }
@ -337,7 +307,7 @@ pub enum Semi {
/// An expression, the beating heart of the language /// An expression, the beating heart of the language
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Expr { pub struct Expr {
pub extents: Span, pub span: Span,
pub kind: ExprKind, pub kind: ExprKind,
} }
@ -347,12 +317,28 @@ pub enum ExprKind {
/// An empty expression: `(` `)` /// An empty expression: `(` `)`
#[default] #[default]
Empty, 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 /// A backtick-quoted expression
Quote(Quote), Quote(Quote),
/// A local bind instruction, `let` [`Sym`] `=` [`Expr`] /// A [Literal]: 0x42, 1e123, 2.4, "Hello"
Let(Let), Literal(Literal),
/// A [Match] expression: `match` [Expr] `{` ([MatchArm] `,`)* [MatchArm]? `}` /// A [Grouping](Group) expression `(` [`Expr`] `)`
Match(Match), Group(Group),
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
Block(Block),
/// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+ /// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+
Assign(Assign), Assign(Assign),
/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+ /// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
@ -361,36 +347,23 @@ pub enum ExprKind {
Binary(Binary), Binary(Binary),
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`] /// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
Unary(Unary), Unary(Unary),
/// A [Cast] expression: [`Expr`] `as` [`Ty`]
Cast(Cast),
/// A [Member] access expression: [`Expr`] [`MemberKind`]\* /// A [Member] access expression: [`Expr`] [`MemberKind`]\*
Member(Member), Member(Member),
/// An Array [Index] expression: a[10, 20, 30] /// An Array [Index] expression: a[10, 20, 30]
Index(Index), Index(Index),
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}` /// A [Cast] expression: [`Expr`] `as` [`Ty`]
Structor(Structor), Cast(Cast),
/// A [path expression](Path): `::`? [PathPart] (`::` [PathPart])* /// A [path expression](Path): `::`? [PathPart] (`::` [PathPart])*
Path(Path), Path(Path),
/// A [Literal]: 0x42, 1e123, 2.4, "Hello" /// A local bind instruction, `let` [`Sym`] `=` [`Expr`]
Literal(Literal), Let(Let),
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]` /// A [Match] expression: `match` [Expr] `{` ([MatchArm] `,`)* [MatchArm]? `}`
Array(Array), Match(Match),
/// 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 [While] expression: `while` [`Expr`] [`Block`] [`Else`]? /// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
While(While), While(While),
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]? /// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
If(If), If(If),
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]? /// A [For] expression: `for` [`Pattern`] `in` [`Expr`] [`Block`] [`Else`]?
For(For), For(For),
/// A [Break] expression: `break` [`Expr`]? /// A [Break] expression: `break` [`Expr`]?
Break(Break), Break(Break),
@ -400,54 +373,100 @@ pub enum ExprKind {
Continue, Continue,
} }
/// A backtick-quoted subexpression-literal /// A Closure [expression](Expr): `|` [`Expr`] `|` ( -> [`Ty`])? [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Quote { pub struct Closure {
pub quote: Box<ExprKind>, pub arg: Box<Pattern>,
pub body: Box<Expr>,
} }
/// A local variable declaration [Stmt] /// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Let { pub struct Tuple {
pub mutable: Mutability, pub exprs: Vec<Expr>,
pub name: Pattern, }
pub ty: Option<Box<Ty>>,
/// 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>>, pub init: Option<Box<Expr>>,
} }
/// A [Pattern] meta-expression (any [`ExprKind`] that fits pattern rules) /// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Pattern { pub struct Array {
Path(Path), pub values: Vec<Expr>,
Literal(Literal),
Ref(Mutability, Box<Pattern>),
Tuple(Vec<Pattern>),
Array(Vec<Pattern>),
Struct(Path, Vec<(Path, Option<Pattern>)>),
} }
/// A `match` expression: `match` `{` ([MatchArm] `,`)* [MatchArm]? `}` /// An Array literal constructed with [repeat syntax](ArrayRep)
/// `[` [Expr] `;` [Literal] `]`
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Match { pub struct ArrayRep {
pub scrutinee: Box<Expr>, pub value: Box<Expr>,
pub arms: Vec<MatchArm>, pub repeat: usize,
} }
/// A single arm of a [Match] expression: [`Pattern`] `=>` [`Expr`] /// An address-of expression: `&` `mut`? [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct MatchArm(pub Pattern, pub Expr); 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`])\+ /// An [Assign]ment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Assign { pub struct Assign {
pub parts: Box<(ExprKind, ExprKind)>, pub parts: Box<(Expr, Expr)>,
} }
/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+ /// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Modify { pub struct Modify {
pub kind: ModifyKind, pub kind: ModifyKind,
pub parts: Box<(ExprKind, ExprKind)>, pub parts: Box<(Expr, Expr)>,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@ -468,7 +487,7 @@ pub enum ModifyKind {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Binary { pub struct Binary {
pub kind: BinaryKind, pub kind: BinaryKind,
pub parts: Box<(ExprKind, ExprKind)>, pub parts: Box<(Expr, Expr)>,
} }
/// A [Binary] operator /// A [Binary] operator
@ -502,7 +521,7 @@ pub enum BinaryKind {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Unary { pub struct Unary {
pub kind: UnaryKind, pub kind: UnaryKind,
pub tail: Box<ExprKind>, pub tail: Box<Expr>,
} }
/// A [Unary] operator /// A [Unary] operator
@ -511,6 +530,8 @@ pub enum UnaryKind {
Deref, Deref,
Neg, Neg,
Not, Not,
RangeInc,
RangeExc,
/// A Loop expression: `loop` [`Block`] /// A Loop expression: `loop` [`Block`]
Loop, Loop,
/// Unused /// Unused
@ -519,17 +540,10 @@ pub enum UnaryKind {
Tilde, Tilde,
} }
/// A cast expression: [`Expr`] `as` [`Ty`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Cast {
pub head: Box<ExprKind>,
pub ty: Ty,
}
/// A [Member] access expression: [`Expr`] [`MemberKind`]\* /// A [Member] access expression: [`Expr`] [`MemberKind`]\*
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Member { pub struct Member {
pub head: Box<ExprKind>, pub head: Box<Expr>,
pub kind: MemberKind, pub kind: MemberKind,
} }
@ -544,61 +558,44 @@ pub enum MemberKind {
/// A repeated [Index] expression: a[10, 20, 30][40, 50, 60] /// A repeated [Index] expression: a[10, 20, 30][40, 50, 60]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Index { pub struct Index {
pub head: Box<ExprKind>, pub head: Box<Expr>,
pub indices: Vec<Expr>, pub indices: Vec<Expr>,
} }
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}` /// A local variable declaration [Stmt]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Structor { pub struct Let {
pub to: Path, pub mutable: Mutability,
pub init: Vec<Fielder>, pub name: Pattern,
} pub ty: Option<Box<Ty>>,
/// A [Struct field initializer] expression: [Sym] (`=` [Expr])?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Fielder {
pub name: Sym,
pub init: Option<Box<Expr>>, pub init: Option<Box<Expr>>,
} }
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]` /// A `match` expression: `match` `{` ([MatchArm] `,`)* [MatchArm]? `}`
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Array { pub struct Match {
pub values: Vec<Expr>, pub scrutinee: Box<Expr>,
pub arms: Vec<MatchArm>,
} }
/// An Array literal constructed with [repeat syntax](ArrayRep) /// A single arm of a [Match] expression: [`Pattern`] `=>` [`Expr`]
/// `[` [Expr] `;` [Literal] `]`
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ArrayRep { pub struct MatchArm(pub Pattern, pub Expr);
pub value: Box<ExprKind>,
pub repeat: Box<ExprKind>,
}
/// An address-of expression: `&` `mut`? [`Expr`] /// A [Pattern] meta-expression (any [`ExprKind`] that fits pattern rules)
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AddrOf { pub enum Pattern {
pub mutable: Mutability, Name(Sym),
pub expr: Box<ExprKind>, Path(Path),
} Literal(Literal),
Rest(Option<Box<Pattern>>),
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}` Ref(Mutability, Box<Pattern>),
#[derive(Clone, Debug, PartialEq, Eq, Hash)] RangeExc(Box<Pattern>, Box<Pattern>),
pub struct Block { RangeInc(Box<Pattern>, Box<Pattern>),
pub stmts: Vec<Stmt>, Tuple(Vec<Pattern>),
} Array(Vec<Pattern>),
Struct(Path, Vec<(Sym, Option<Pattern>)>),
/// A [Grouping](Group) expression `(` [`Expr`] `)` TupleStruct(Path, Vec<Pattern>),
#[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 [While] expression: `while` [`Expr`] [`Block`] [`Else`]? /// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
@ -620,7 +617,7 @@ pub struct If {
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]? /// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct For { pub struct For {
pub bind: Sym, // TODO: Patterns? pub bind: Pattern,
pub cond: Box<Expr>, pub cond: Box<Expr>,
pub pass: Box<Block>, pub pass: Box<Block>,
pub fail: Else, pub fail: Else,

View File

@ -1,949 +1,8 @@
//! Implementations of AST nodes and traits //! Implementations of AST nodes and traits
use super::*; use super::*;
mod display { mod convert;
//! Implements [Display] for [AST](super::super) Types mod display;
mod path;
use super::*; pub(crate) mod weight_of;
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 { 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::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::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 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::Path(path) => path.fmt(f),
Pattern::Literal(literal) => literal.fmt(f),
Pattern::Ref(mutability, pattern) => write!(f, "&{mutability}{pattern}"),
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(BRACES);
for (idx, (name, item)) in items.iter().enumerate() {
if idx != 0 {
f.write_str(",\n")?;
}
write!(f, "{name}")?;
if let Some(pattern) = item {
write!(f, ": {pattern}")?
}
}
Ok(())
}
}
}
}
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::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(()),
}
}
}
}
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 {
Item => StmtKind::Item,
Expr => StmtKind::Expr,
}
impl From for ExprKind {
Let => ExprKind::Let,
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<ExprKind> for Pattern {
type Error = ExprKind;
/// Performs the conversion. On failure, returns the *first* non-pattern subexpression.
fn try_from(value: ExprKind) -> Result<Self, Self::Error> {
Ok(match value {
ExprKind::Literal(literal) => Pattern::Literal(literal),
ExprKind::Path(path) => Pattern::Path(path),
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(|e| Pattern::try_from(e.kind))
.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(|e| Pattern::try_from(e.kind))
.collect::<Result<_, _>>()?,
),
// ExprKind::Index(index) => todo!(),
// ExprKind::Member(member) => todo!(),
ExprKind::Structor(Structor { to, init }) => {
let fields = init
.into_iter()
.map(|Fielder { name, init }| {
Ok((
name.into(),
init.map(|i| Pattern::try_from(i.kind)).transpose()?,
))
})
.collect::<Result<_, Self::Error>>()?;
Pattern::Struct(to, fields)
}
err => Err(err)?,
})
}
}
}
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
}
}
/// Checks whether this path refers to the sinkhole identifier, `_`
pub fn is_sinkhole(&self) -> bool {
if let [PathPart::Ident(id)] = self.parts.as_slice() {
if let "_" = Sym::to_ref(id) {
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,161 @@
//! 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,
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,761 @@
//! 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 {
TyKind::Tuple(TyTuple { ref types }) => types.as_slice(),
TyKind::Empty => Default::default(),
_ => {
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 Some(rety) = rety {
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 { 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::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::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::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() {
if 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,609 @@
//! 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 { target, body } = self;
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 } = self;
span.weight_of() + kind.weight_of()
}
}
impl WeightOf for TyKind {
fn weight_of(&self) -> usize {
match self {
TyKind::Never | TyKind::Empty | 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::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 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 //! with default implementations across the entire AST
pub mod fold; pub mod fold;
pub mod visit; pub mod visit;
pub mod walk;
pub use fold::Fold; pub use fold::Fold;
pub use visit::Visit; 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*. /// For all other nodes, traversal is *explicit*.
pub trait Fold { pub trait Fold {
fn fold_span(&mut self, extents: Span) -> Span { fn fold_span(&mut self, span: Span) -> Span {
extents span
} }
fn fold_mutability(&mut self, mutability: Mutability) -> Mutability { fn fold_mutability(&mut self, mutability: Mutability) -> Mutability {
mutability mutability
@ -44,8 +44,8 @@ pub trait Fold {
s s
} }
fn fold_file(&mut self, f: File) -> File { fn fold_file(&mut self, f: File) -> File {
let File { items } = f; let File { name, items } = f;
File { items: items.into_iter().map(|i| self.fold_item(i)).collect() } File { name, items: items.into_iter().map(|i| self.fold_item(i)).collect() }
} }
fn fold_attrs(&mut self, a: Attrs) -> Attrs { fn fold_attrs(&mut self, a: Attrs) -> Attrs {
let Attrs { meta } = a; let Attrs { meta } = a;
@ -59,9 +59,9 @@ pub trait Fold {
or_fold_meta_kind(self, kind) or_fold_meta_kind(self, kind)
} }
fn fold_item(&mut self, i: Item) -> Item { fn fold_item(&mut self, i: Item) -> Item {
let Item { extents, attrs, vis, kind } = i; let Item { span, attrs, vis, kind } = i;
Item { Item {
extents: self.fold_span(extents), span: self.fold_span(span),
attrs: self.fold_attrs(attrs), attrs: self.fold_attrs(attrs),
vis: self.fold_visibility(vis), vis: self.fold_visibility(vis),
kind: self.fold_item_kind(kind), kind: self.fold_item_kind(kind),
@ -70,9 +70,13 @@ pub trait Fold {
fn fold_item_kind(&mut self, kind: ItemKind) -> ItemKind { fn fold_item_kind(&mut self, kind: ItemKind) -> ItemKind {
or_fold_item_kind(self, kind) 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 { fn fold_alias(&mut self, a: Alias) -> Alias {
let Alias { to, from } = a; let Alias { name, from } = a;
Alias { to: self.fold_sym(to), from: from.map(|from| Box::new(self.fold_ty(*from))) } Alias { name: self.fold_sym(name), from: from.map(|from| Box::new(self.fold_ty(*from))) }
} }
fn fold_const(&mut self, c: Const) -> Const { fn fold_const(&mut self, c: Const) -> Const {
let Const { name, ty, init } = c; let Const { name, ty, init } = c;
@ -92,31 +96,26 @@ pub trait Fold {
} }
} }
fn fold_module(&mut self, m: Module) -> Module { fn fold_module(&mut self, m: Module) -> Module {
let Module { name, kind } = m; let Module { name, file } = m;
Module { name: self.fold_sym(name), kind: self.fold_module_kind(kind) } Module { name: self.fold_sym(name), file: file.map(|v| self.fold_file(v)) }
}
fn fold_module_kind(&mut self, m: ModuleKind) -> ModuleKind {
match m {
ModuleKind::Inline(f) => ModuleKind::Inline(self.fold_file(f)),
ModuleKind::Outline => ModuleKind::Outline,
}
} }
fn fold_function(&mut self, f: Function) -> Function { fn fold_function(&mut self, f: Function) -> Function {
let Function { name, sign, bind, body } = f; let Function { name, gens, sign, bind, body } = f;
Function { Function {
name: self.fold_sym(name), name: self.fold_sym(name),
gens: self.fold_generics(gens),
sign: self.fold_ty_fn(sign), sign: self.fold_ty_fn(sign),
bind: bind.into_iter().map(|p| self.fold_param(p)).collect(), bind: self.fold_pattern(bind),
body: body.map(|b| self.fold_block(b)), 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 { fn fold_struct(&mut self, s: Struct) -> Struct {
let Struct { name, kind } = s; let Struct { name, gens, kind } = s;
Struct { name: self.fold_sym(name), kind: self.fold_struct_kind(kind) } 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 { fn fold_struct_kind(&mut self, kind: StructKind) -> StructKind {
match kind { match kind {
@ -140,19 +139,21 @@ pub trait Fold {
} }
} }
fn fold_enum(&mut self, e: Enum) -> Enum { fn fold_enum(&mut self, e: Enum) -> Enum {
let Enum { name, kind } = e; let Enum { name, gens, variants: kind } = e;
Enum { name: self.fold_sym(name), kind: self.fold_enum_kind(kind) } 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 { 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 { fn fold_impl(&mut self, i: Impl) -> Impl {
let Impl { target, body } = i; let Impl { target, body } = i;
@ -169,8 +170,8 @@ pub trait Fold {
or_fold_use_tree(self, tree) or_fold_use_tree(self, tree)
} }
fn fold_ty(&mut self, t: Ty) -> Ty { fn fold_ty(&mut self, t: Ty) -> Ty {
let Ty { extents, kind } = t; let Ty { span, kind } = t;
Ty { extents: self.fold_span(extents), kind: self.fold_ty_kind(kind) } Ty { span: self.fold_span(span), kind: self.fold_ty_kind(kind) }
} }
fn fold_ty_kind(&mut self, kind: TyKind) -> TyKind { fn fold_ty_kind(&mut self, kind: TyKind) -> TyKind {
or_fold_ty_kind(self, kind) or_fold_ty_kind(self, kind)
@ -194,7 +195,7 @@ pub trait Fold {
} }
fn fold_ty_ref(&mut self, t: TyRef) -> TyRef { fn fold_ty_ref(&mut self, t: TyRef) -> TyRef {
let TyRef { mutable, count, to } = t; 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_fn(&mut self, t: TyFn) -> TyFn { fn fold_ty_fn(&mut self, t: TyFn) -> TyFn {
let TyFn { args, rety } = t; let TyFn { args, rety } = t;
@ -210,15 +211,14 @@ pub trait Fold {
fn fold_path_part(&mut self, p: PathPart) -> PathPart { fn fold_path_part(&mut self, p: PathPart) -> PathPart {
match p { match p {
PathPart::SuperKw => PathPart::SuperKw, PathPart::SuperKw => PathPart::SuperKw,
PathPart::SelfKw => PathPart::SelfKw,
PathPart::SelfTy => PathPart::SelfTy, PathPart::SelfTy => PathPart::SelfTy,
PathPart::Ident(i) => PathPart::Ident(self.fold_sym(i)), PathPart::Ident(i) => PathPart::Ident(self.fold_sym(i)),
} }
} }
fn fold_stmt(&mut self, s: Stmt) -> Stmt { fn fold_stmt(&mut self, s: Stmt) -> Stmt {
let Stmt { extents, kind, semi } = s; let Stmt { span, kind, semi } = s;
Stmt { Stmt {
extents: self.fold_span(extents), span: self.fold_span(span),
kind: self.fold_stmt_kind(kind), kind: self.fold_stmt_kind(kind),
semi: self.fold_semi(semi), semi: self.fold_semi(semi),
} }
@ -230,12 +230,16 @@ pub trait Fold {
s s
} }
fn fold_expr(&mut self, e: Expr) -> Expr { fn fold_expr(&mut self, e: Expr) -> Expr {
let Expr { extents, kind } = e; let Expr { span, kind } = e;
Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) } Expr { span: self.fold_span(span), kind: self.fold_expr_kind(kind) }
} }
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind { fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
or_fold_expr_kind(self, kind) 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 { fn fold_let(&mut self, l: Let) -> Let {
let Let { mutable, name, ty, init } = l; let Let { mutable, name, ty, init } = l;
Let { Let {
@ -248,12 +252,23 @@ pub trait Fold {
fn fold_pattern(&mut self, p: Pattern) -> Pattern { fn fold_pattern(&mut self, p: Pattern) -> Pattern {
match p { match p {
Pattern::Name(sym) => Pattern::Name(self.fold_sym(sym)),
Pattern::Path(path) => Pattern::Path(self.fold_path(path)), Pattern::Path(path) => Pattern::Path(self.fold_path(path)),
Pattern::Literal(literal) => Pattern::Literal(self.fold_literal(literal)), 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( Pattern::Ref(mutability, pattern) => Pattern::Ref(
self.fold_mutability(mutability), self.fold_mutability(mutability),
Box::new(self.fold_pattern(*pattern)), 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) => {
Pattern::Tuple(patterns.into_iter().map(|p| self.fold_pattern(p)).collect()) Pattern::Tuple(patterns.into_iter().map(|p| self.fold_pattern(p)).collect())
} }
@ -267,6 +282,13 @@ pub trait Fold {
.map(|(name, bind)| (name, bind.map(|p| self.fold_pattern(p)))) .map(|(name, bind)| (name, bind.map(|p| self.fold_pattern(p))))
.collect(), .collect(),
), ),
Pattern::TupleStruct(path, items) => Pattern::TupleStruct(
self.fold_path(path),
items
.into_iter()
.map(|bind| self.fold_pattern(bind))
.collect(),
),
} }
} }
@ -289,14 +311,14 @@ pub trait Fold {
fn fold_assign(&mut self, a: Assign) -> Assign { fn fold_assign(&mut self, a: Assign) -> Assign {
let Assign { parts } = a; let Assign { parts } = a;
let (head, tail) = *parts; 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 { fn fold_modify(&mut self, m: Modify) -> Modify {
let Modify { kind, parts } = m; let Modify { kind, parts } = m;
let (head, tail) = *parts; let (head, tail) = *parts;
Modify { Modify {
kind: self.fold_modify_kind(kind), 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 { fn fold_modify_kind(&mut self, kind: ModifyKind) -> ModifyKind {
@ -307,7 +329,7 @@ pub trait Fold {
let (head, tail) = *parts; let (head, tail) = *parts;
Binary { Binary {
kind: self.fold_binary_kind(kind), 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 { fn fold_binary_kind(&mut self, kind: BinaryKind) -> BinaryKind {
@ -315,18 +337,18 @@ pub trait Fold {
} }
fn fold_unary(&mut self, u: Unary) -> Unary { fn fold_unary(&mut self, u: Unary) -> Unary {
let Unary { kind, tail } = u; 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 { fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind {
kind kind
} }
fn fold_cast(&mut self, cast: Cast) -> Cast { fn fold_cast(&mut self, cast: Cast) -> Cast {
let Cast { head, ty } = cast; let Cast { head, ty } = cast;
Cast { head: Box::new(self.fold_expr_kind(*head)), ty: self.fold_ty(ty) } Cast { head: Box::new(self.fold_expr(*head)), ty: self.fold_ty(ty) }
} }
fn fold_member(&mut self, m: Member) -> Member { fn fold_member(&mut self, m: Member) -> Member {
let Member { head, kind } = m; 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 { fn fold_member_kind(&mut self, kind: MemberKind) -> MemberKind {
or_fold_member_kind(self, kind) or_fold_member_kind(self, kind)
@ -334,7 +356,7 @@ pub trait Fold {
fn fold_index(&mut self, i: Index) -> Index { fn fold_index(&mut self, i: Index) -> Index {
let Index { head, indices } = i; let Index { head, indices } = i;
Index { 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(), indices: indices.into_iter().map(|e| self.fold_expr(e)).collect(),
} }
} }
@ -357,17 +379,11 @@ pub trait Fold {
} }
fn fold_array_rep(&mut self, a: ArrayRep) -> ArrayRep { fn fold_array_rep(&mut self, a: ArrayRep) -> ArrayRep {
let ArrayRep { value, repeat } = a; let ArrayRep { value, repeat } = a;
ArrayRep { ArrayRep { value: Box::new(self.fold_expr(*value)), repeat }
value: Box::new(self.fold_expr_kind(*value)),
repeat: Box::new(self.fold_expr_kind(*repeat)),
}
} }
fn fold_addrof(&mut self, a: AddrOf) -> AddrOf { fn fold_addrof(&mut self, a: AddrOf) -> AddrOf {
let AddrOf { mutable, expr } = a; let AddrOf { mutable, expr } = a;
AddrOf { AddrOf { mutable: self.fold_mutability(mutable), expr: Box::new(self.fold_expr(*expr)) }
mutable: self.fold_mutability(mutable),
expr: Box::new(self.fold_expr_kind(*expr)),
}
} }
fn fold_block(&mut self, b: Block) -> Block { fn fold_block(&mut self, b: Block) -> Block {
let Block { stmts } = b; let Block { stmts } = b;
@ -375,7 +391,7 @@ pub trait Fold {
} }
fn fold_group(&mut self, g: Group) -> Group { fn fold_group(&mut self, g: Group) -> Group {
let Group { expr } = g; 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 { fn fold_tuple(&mut self, t: Tuple) -> Tuple {
let Tuple { exprs } = t; let Tuple { exprs } = t;
@ -400,7 +416,7 @@ pub trait Fold {
fn fold_for(&mut self, f: For) -> For { fn fold_for(&mut self, f: For) -> For {
let For { bind, cond, pass, fail } = f; let For { bind, cond, pass, fail } = f;
For { For {
bind: self.fold_sym(bind), bind: self.fold_pattern(bind),
cond: Box::new(self.fold_expr(*cond)), cond: Box::new(self.fold_expr(*cond)),
pass: Box::new(self.fold_block(*pass)), pass: Box::new(self.fold_block(*pass)),
fail: self.fold_else(fail), fail: self.fold_else(fail),
@ -460,15 +476,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] #[inline]
/// Folds a [StructKind] in the default way /// Folds a [StructKind] in the default way
pub fn or_fold_struct_kind<F: Fold + ?Sized>(folder: &mut F, kind: StructKind) -> StructKind { pub fn or_fold_struct_kind<F: Fold + ?Sized>(folder: &mut F, kind: StructKind) -> StructKind {
@ -485,32 +492,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] #[inline]
/// Folds an [ImplKind] in the default way /// Folds an [ImplKind] in the default way
pub fn or_fold_impl_kind<F: Fold + ?Sized>(folder: &mut F, kind: ImplKind) -> ImplKind { pub fn or_fold_impl_kind<F: Fold + ?Sized>(folder: &mut F, kind: ImplKind) -> ImplKind {
@ -547,6 +528,7 @@ pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind
match kind { match kind {
TyKind::Never => TyKind::Never, TyKind::Never => TyKind::Never,
TyKind::Empty => TyKind::Empty, TyKind::Empty => TyKind::Empty,
TyKind::Infer => TyKind::Infer,
TyKind::Path(p) => TyKind::Path(folder.fold_path(p)), TyKind::Path(p) => TyKind::Path(folder.fold_path(p)),
TyKind::Array(a) => TyKind::Array(folder.fold_ty_array(a)), TyKind::Array(a) => TyKind::Array(folder.fold_ty_array(a)),
TyKind::Slice(s) => TyKind::Slice(folder.fold_ty_slice(s)), TyKind::Slice(s) => TyKind::Slice(folder.fold_ty_slice(s)),
@ -570,6 +552,7 @@ 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 { pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> ExprKind {
match kind { match kind {
ExprKind::Empty => ExprKind::Empty, 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::Quote(q) => ExprKind::Quote(q), // quoted expressions are left unmodified
ExprKind::Let(l) => ExprKind::Let(folder.fold_let(l)), ExprKind::Let(l) => ExprKind::Let(folder.fold_let(l)),
ExprKind::Match(m) => ExprKind::Match(folder.fold_match(m)), ExprKind::Match(m) => ExprKind::Match(folder.fold_match(m)),

View File

@ -3,527 +3,255 @@
use crate::ast::*; use crate::ast::*;
use cl_structures::span::Span; use cl_structures::span::Span;
use super::walk::Walk;
/// Immutably walks the entire AST /// Immutably walks the entire AST
/// ///
/// Each method acts as a customization point. /// 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 { pub trait Visit<'a>: Sized {
fn visit_span(&mut self, _extents: &'a Span) {} /// Visits a [Walker](Walk)
fn visit_mutability(&mut self, _mutable: &'a Mutability) {} #[inline]
fn visit_visibility(&mut self, _vis: &'a Visibility) {} fn visit<W: Walk>(&mut self, walker: &'a W) -> &mut Self {
fn visit_sym(&mut self, _name: &'a Sym) {} walker.visit_in(self);
fn visit_literal(&mut self, l: &'a Literal) { self
or_visit_literal(self, l)
}
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_smuggled_float(&mut self, _f: &'a u64) {}
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));
}
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_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_let(&mut self, l: &'a Let) {
let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable);
self.visit_pattern(name);
if let Some(ty) = ty {
self.visit_ty(ty);
}
if let Some(init) = init {
self.visit_expr(init)
} }
/// Visits the children of a [Walker](Walk)
fn visit_children<W: Walk>(&mut self, walker: &'a W) {
walker.children(self)
} }
fn visit_pattern(&mut self, p: &'a Pattern) { fn visit_span(&mut self, value: &'a Span) {
match p { value.children(self)
Pattern::Path(path) => self.visit_path(path),
Pattern::Literal(literal) => self.visit_literal(literal),
Pattern::Ref(mutability, pattern) => {
self.visit_mutability(mutability);
self.visit_pattern(pattern);
} }
Pattern::Tuple(patterns) => { fn visit_mutability(&mut self, value: &'a Mutability) {
patterns.iter().for_each(|p| self.visit_pattern(p)); value.children(self)
} }
Pattern::Array(patterns) => { fn visit_visibility(&mut self, value: &'a Visibility) {
patterns.iter().for_each(|p| self.visit_pattern(p)); value.children(self)
} }
Pattern::Struct(path, items) => { fn visit_sym(&mut self, value: &'a Sym) {
self.visit_path(path); value.children(self)
items.iter().for_each(|(_name, bind)| {
bind.as_ref().inspect(|bind| {
self.visit_pattern(bind);
});
});
} }
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_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)
} }
fn visit_match(&mut self, m: &'a Match) { fn visit_pattern(&mut self, value: &'a Pattern) {
let Match { scrutinee, arms } = m; value.children(self)
self.visit_expr(scrutinee);
arms.iter().for_each(|arm| self.visit_match_arm(arm));
} }
fn visit_match_arm(&mut self, a: &'a MatchArm) { fn visit_match(&mut self, value: &'a Match) {
let MatchArm(pat, expr) = a; value.children(self)
self.visit_pattern(pat);
self.visit_expr(expr);
} }
fn visit_assign(&mut self, a: &'a Assign) { fn visit_match_arm(&mut self, value: &'a MatchArm) {
let Assign { parts } = a; value.children(self)
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; fn visit_assign(&mut self, value: &'a Assign) {
let (head, tail) = parts.as_ref(); value.children(self)
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_modify(&mut self, value: &'a Modify) {
fn visit_binary(&mut self, b: &'a Binary) { value.children(self)
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_modify_kind(&mut self, value: &'a ModifyKind) {
fn visit_unary(&mut self, u: &'a Unary) { value.children(self)
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_binary(&mut self, value: &'a Binary) {
fn visit_cast(&mut self, cast: &'a Cast) { value.children(self)
let Cast { head, ty } = cast;
self.visit_expr_kind(head);
self.visit_ty(ty);
} }
fn visit_member(&mut self, m: &'a Member) { fn visit_binary_kind(&mut self, value: &'a BinaryKind) {
let Member { head, kind } = m; value.children(self)
self.visit_expr_kind(head);
self.visit_member_kind(kind);
} }
fn visit_member_kind(&mut self, kind: &'a MemberKind) { fn visit_unary(&mut self, value: &'a Unary) {
or_visit_member_kind(self, kind) value.children(self)
} }
fn visit_index(&mut self, i: &'a Index) { fn visit_unary_kind(&mut self, value: &'a UnaryKind) {
let Index { head, indices } = i; value.children(self)
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; fn visit_cast(&mut self, value: &'a Cast) {
self.visit_path(to); value.children(self)
init.iter().for_each(|e| self.visit_fielder(e))
} }
fn visit_fielder(&mut self, f: &'a Fielder) { fn visit_member(&mut self, value: &'a Member) {
let Fielder { name, init } = f; value.children(self)
self.visit_sym(name);
if let Some(init) = init {
self.visit_expr(init);
} }
fn visit_member_kind(&mut self, value: &'a MemberKind) {
value.children(self)
} }
fn visit_array(&mut self, a: &'a Array) { fn visit_index(&mut self, value: &'a Index) {
let Array { values } = a; value.children(self)
values.iter().for_each(|e| self.visit_expr(e))
} }
fn visit_array_rep(&mut self, a: &'a ArrayRep) { fn visit_structor(&mut self, value: &'a Structor) {
let ArrayRep { value, repeat } = a; value.children(self)
self.visit_expr_kind(value);
self.visit_expr_kind(repeat);
} }
fn visit_addrof(&mut self, a: &'a AddrOf) { fn visit_fielder(&mut self, value: &'a Fielder) {
let AddrOf { mutable, expr } = a; value.children(self)
self.visit_mutability(mutable);
self.visit_expr_kind(expr);
} }
fn visit_block(&mut self, b: &'a Block) { fn visit_array(&mut self, value: &'a Array) {
let Block { stmts } = b; value.children(self)
stmts.iter().for_each(|s| self.visit_stmt(s));
} }
fn visit_group(&mut self, g: &'a Group) { fn visit_array_rep(&mut self, value: &'a ArrayRep) {
let Group { expr } = g; value.children(self)
self.visit_expr_kind(expr)
} }
fn visit_tuple(&mut self, t: &'a Tuple) { fn visit_addrof(&mut self, value: &'a AddrOf) {
let Tuple { exprs } = t; value.children(self)
exprs.iter().for_each(|e| self.visit_expr(e))
} }
fn visit_while(&mut self, w: &'a While) { fn visit_block(&mut self, value: &'a Block) {
let While { cond, pass, fail } = w; value.children(self)
self.visit_expr(cond);
self.visit_block(pass);
self.visit_else(fail);
} }
fn visit_if(&mut self, i: &'a If) { fn visit_group(&mut self, value: &'a Group) {
let If { cond, pass, fail } = i; value.children(self)
self.visit_expr(cond);
self.visit_block(pass);
self.visit_else(fail);
} }
fn visit_for(&mut self, f: &'a For) { fn visit_tuple(&mut self, value: &'a Tuple) {
let For { bind, cond, pass, fail } = f; value.children(self)
self.visit_sym(bind);
self.visit_expr(cond);
self.visit_block(pass);
self.visit_else(fail);
} }
fn visit_else(&mut self, e: &'a Else) { fn visit_while(&mut self, value: &'a While) {
let Else { body } = e; value.children(self)
if let Some(body) = body {
self.visit_expr(body)
} }
fn visit_if(&mut self, value: &'a If) {
value.children(self)
} }
fn visit_break(&mut self, b: &'a Break) { fn visit_for(&mut self, value: &'a For) {
let Break { body } = b; value.children(self)
if let Some(body) = body {
self.visit_expr(body)
} }
fn visit_else(&mut self, value: &'a Else) {
value.children(self)
} }
fn visit_return(&mut self, r: &'a Return) { fn visit_break(&mut self, value: &'a Break) {
let Return { body } = r; value.children(self)
if let Some(body) = body {
self.visit_expr(body)
} }
fn visit_return(&mut self, value: &'a Return) {
value.children(self)
} }
fn visit_continue(&mut self) {} fn visit_continue(&mut self) {}
} }
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::Float(f) => visitor.visit_smuggled_float(f),
Literal::String(s) => visitor.visit_string(s),
}
}
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)),
}
}
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),
}
}
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 => {}
}
}
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)),
}
}
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::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::Quote(_q) => {} // Quoted expressions are left unvisited
ExprKind::Let(l) => visitor.visit_let(l),
ExprKind::Match(m) => visitor.visit_match(m),
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::Cast(c) => visitor.visit_cast(c),
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::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 => visitor.visit_continue(),
}
}
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),
}
}

View File

@ -0,0 +1,951 @@
//! 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 { target, body } = self;
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 } = self;
span.visit_in(v);
kind.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::Empty => {}
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::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 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;
bind.visit_in(v);
cond.visit_in(v);
pass.visit_in(v);
fail.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 //! Desugaring passes for Conlang
pub mod constant_folder;
pub mod path_absoluter; pub mod path_absoluter;
pub mod squash_groups; pub mod squash_groups;
pub mod while_else; pub mod while_else;
pub use constant_folder::ConstantFolder;
pub use path_absoluter::NormalizePaths; pub use path_absoluter::NormalizePaths;
pub use squash_groups::SquashGroups; pub use squash_groups::SquashGroups;
pub use while_else::WhileElseDesugar; pub use while_else::WhileElseDesugar;

View File

@ -0,0 +1,89 @@
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, Int, Bool),
(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 { impl Fold for NormalizePaths {
fn fold_module(&mut self, m: Module) -> Module { fn fold_module(&mut self, m: Module) -> Module {
let Module { name, kind } = m; let Module { name, file } = m;
self.path.push(PathPart::Ident(name)); 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(); self.path.pop();
Module { name, kind } Module { name, file }
} }
fn fold_path(&mut self, p: Path) -> Path { fn fold_path(&mut self, p: Path) -> Path {

View File

@ -7,7 +7,7 @@ pub struct SquashGroups;
impl Fold for SquashGroups { impl Fold for SquashGroups {
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind { fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
match kind { 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), _ => or_fold_expr_kind(self, kind),
} }
} }

View File

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

View File

@ -14,6 +14,7 @@
#![feature(decl_macro)] #![feature(decl_macro)]
pub use ast::*; pub use ast::*;
pub use ast_impl::weight_of::WeightOf;
pub mod ast; pub mod ast;
pub mod ast_impl; 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 = { path = "../../repline" }

View File

@ -0,0 +1,25 @@
use cl_embed::*;
use repline::{Response, prebaked};
fn main() -> Result<(), repline::Error> {
prebaked::read_and("", "calc >", " ? >", |line| {
calc(line).map_err(Into::into)
})
}
fn calc(line: &str) -> Result<Response, EvalError> {
let mut env = Environment::new();
env.bind("line", line);
let res = conlang!(
mod expression;
use expression::{eval, parse};
let (expr, rest) = parse(line.chars(), 0);
eval(expr)
)(&mut env)?;
println!("{res}");
Ok(Response::Accept)
}

View File

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

View File

@ -0,0 +1,126 @@
//! 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, 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!(), ":"),
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)
}
}}
#[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

@ -19,7 +19,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let parent = path.parent().unwrap_or("".as_ref()); let parent = path.parent().unwrap_or("".as_ref());
let code = std::fs::read_to_string(&path)?; let code = std::fs::read_to_string(&path)?;
let code = Parser::new(Lexer::new(&code)).parse()?; let code = Parser::new(path.display().to_string(), Lexer::new(&code)).parse()?;
let code = match ModuleInliner::new(parent).inline(code) { let code = match ModuleInliner::new(parent).inline(code) {
Ok(code) => code, Ok(code) => code,
Err((code, ioerrs, perrs)) => { Err((code, ioerrs, perrs)) => {
@ -40,7 +40,7 @@ fn main() -> Result<(), Box<dyn Error>> {
if env.get(main).is_ok() { if env.get(main).is_ok() {
let args = args let args = args
.flat_map(|arg| { .flat_map(|arg| {
Parser::new(Lexer::new(&arg)) Parser::new(&arg, Lexer::new(&arg))
.parse::<Expr>() .parse::<Expr>()
.map(|arg| env.eval(&arg)) .map(|arg| env.eval(&arg))
}) })

View File

@ -5,10 +5,7 @@ use crate::{
env::Environment, env::Environment,
error::{Error, IResult}, error::{Error, IResult},
}; };
use std::{ use std::io::{Write, stdout};
io::{stdout, Write},
slice,
};
/// A function built into the interpreter. /// A function built into the interpreter.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -57,7 +54,7 @@ impl super::Callable for Builtin {
/// Turns a function definition into a [Builtin]. /// Turns a function definition into a [Builtin].
/// ///
/// ```rust /// ```rust
/// # use cl_interpret::{builtin2::builtin, convalue::ConValue}; /// # use cl_interpret::{builtin::builtin, convalue::ConValue};
/// let my_builtin = builtin! { /// let my_builtin = builtin! {
/// /// Use the `@env` suffix to bind the environment! /// /// Use the `@env` suffix to bind the environment!
/// /// (needed for recursive calls) /// /// (needed for recursive calls)
@ -78,11 +75,11 @@ pub macro builtin(
$(#[$($meta)*])* $(#[$($meta)*])*
fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> { fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
// Set up the builtin! environment // Set up the builtin! environment
$(let $env = _env;)? $(#[allow(unused)]let $env = _env;)?
// Allow for single argument `fn foo(args @ ..)` pattern // Allow for single argument `fn foo(args @ ..)` pattern
#[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)] #[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
let [$($arg),*] = _args else { let [$($arg),*] = _args else {
Err($crate::error::Error::TypeError)? Err($crate::error::Error::TypeError())?
}; };
$body.map(Into::into) $body.map(Into::into)
} }
@ -101,10 +98,10 @@ pub macro builtins($(
[$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*] [$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
} }
/// Creates an [Error::BuiltinDebug] using interpolation of runtime expressions. /// Creates an [Error::BuiltinError] using interpolation of runtime expressions.
/// See [std::format]. /// See [std::format].
pub macro error_format ($($t:tt)*) { pub macro error_format ($($t:tt)*) {
$crate::error::Error::BuiltinDebug(format!($($t)*)) $crate::error::Error::BuiltinError(format!($($t)*))
} }
pub const Builtins: &[Builtin] = &builtins![ pub const Builtins: &[Builtin] = &builtins![
@ -146,6 +143,11 @@ pub const Builtins: &[Builtin] = &builtins![
Ok(()) Ok(())
} }
fn panic(message) {
Err(error_format!("Panic: {message}"))?;
Ok(())
}
/// Dumps the environment /// Dumps the environment
fn dump() @env { fn dump() @env {
println!("{env}"); println!("{env}");
@ -153,8 +155,8 @@ pub const Builtins: &[Builtin] = &builtins![
} }
fn builtins() @env { fn builtins() @env {
for builtin in env.builtins().values().flatten() { for builtin in env.globals().values().flatten().filter(|v| matches!(v, ConValue::Builtin(_))) {
println!("{builtin}"); println!("{builtin}")
} }
Ok(()) Ok(())
} }
@ -164,12 +166,22 @@ pub const Builtins: &[Builtin] = &builtins![
Ok(match list { Ok(match list {
ConValue::Empty => 0, ConValue::Empty => 0,
ConValue::String(s) => s.chars().count() as _, ConValue::String(s) => s.chars().count() as _,
ConValue::Ref(r) => return len(env, slice::from_ref(r.as_ref())), ConValue::Ref(r) => {
return len(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
}
ConValue::Array(t) => t.len() as _, ConValue::Array(t) => t.len() as _,
ConValue::Tuple(t) => t.len() as _, ConValue::Tuple(t) => t.len() as _,
ConValue::RangeExc(start, end) => (end - start) as _, _ => Err(Error::TypeError())?,
ConValue::RangeInc(start, end) => (end - start + 1) as _, })
_ => Err(Error::TypeError)?, }
fn chars(string) @env {
Ok(match string {
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())?,
}) })
} }
@ -178,6 +190,10 @@ pub const Builtins: &[Builtin] = &builtins![
Ok(ConValue::Empty) Ok(ConValue::Empty)
} }
fn slice_of(ConValue::Ref(arr), ConValue::Int(start)) {
Ok(ConValue::Slice(*arr, *start as usize))
}
/// Returns a shark /// Returns a shark
fn shark() { fn shark() {
Ok('\u{1f988}') Ok('\u{1f988}')
@ -190,7 +206,7 @@ pub const Math: &[Builtin] = &builtins![
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
}) })
} }
@ -199,7 +215,7 @@ pub const Math: &[Builtin] = &builtins![
Ok(match (lhs, rhs){ Ok(match (lhs, rhs){
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
}) })
} }
@ -208,7 +224,7 @@ pub const Math: &[Builtin] = &builtins![
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}) })
} }
@ -218,7 +234,7 @@ pub const Math: &[Builtin] = &builtins![
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(), (ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
}) })
} }
@ -227,7 +243,7 @@ pub const Math: &[Builtin] = &builtins![
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}) })
} }
@ -236,7 +252,7 @@ pub const Math: &[Builtin] = &builtins![
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}) })
} }
@ -245,7 +261,7 @@ pub const Math: &[Builtin] = &builtins![
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}) })
} }
@ -255,7 +271,7 @@ pub const Math: &[Builtin] = &builtins![
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b), (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}) })
} }
@ -265,7 +281,7 @@ pub const Math: &[Builtin] = &builtins![
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b), (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}) })
} }
@ -275,24 +291,29 @@ pub const Math: &[Builtin] = &builtins![
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b), (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}) })
} }
/// Exclusive Range `a..b` #[allow(non_snake_case)]
fn range_exc(from, to) { fn RangeExc(start, end) {
let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else { Ok(ConValue::TupleStruct(Box::new((
Err(Error::TypeError)? "RangeExc", Box::new([start.clone(), end.clone()])
}; ))))
Ok(ConValue::RangeExc(from, to))
} }
/// Inclusive Range `a..=b` #[allow(non_snake_case)]
fn range_inc(from, to) { fn RangeInc(start, end) {
let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else { Ok(ConValue::TupleStruct(Box::new((
Err(Error::TypeError)? "RangeInc", Box::new([start.clone(), end.clone()])
}; ))))
Ok(ConValue::RangeInc(from, to)) }
#[allow(non_snake_case)]
fn RangeTo(end) {
Ok(ConValue::TupleStruct(Box::new((
"RangeInc", Box::new([end.clone()])
))))
} }
/// Negates the ConValue /// Negates the ConValue
@ -301,7 +322,7 @@ pub const Math: &[Builtin] = &builtins![
ConValue::Empty => ConValue::Empty, ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(v.wrapping_neg()), ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
ConValue::Float(v) => ConValue::Float(-v), ConValue::Float(v) => ConValue::Float(-v),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}) })
} }
@ -311,7 +332,7 @@ pub const Math: &[Builtin] = &builtins![
ConValue::Empty => ConValue::Empty, ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(!v), ConValue::Int(v) => ConValue::Int(!v),
ConValue::Bool(v) => ConValue::Bool(!v), ConValue::Bool(v) => ConValue::Bool(!v),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}) })
} }
@ -327,10 +348,9 @@ pub const Math: &[Builtin] = &builtins![
} }
/// Does the opposite of `&` /// Does the opposite of `&`
fn deref(tail) { fn deref(tail) @env {
use std::rc::Rc;
Ok(match tail { 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(), _ => tail.clone(),
}) })
} }

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, Option<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(&decl.arg, ConValue::Tuple(args.into()))? {
env.insert(*name, Some(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 {
"{closure}".into()
}
}

View File

@ -1,15 +1,40 @@
//! Values in the dynamically typed AST interpreter. //! Values in the dynamically typed AST interpreter.
//! //!
//! The most permanent fix is a temporary one. //! The most permanent fix is a temporary one.
use cl_ast::{format::FmtAdapter, ExprKind, Sym}; use cl_ast::{Expr, Sym, format::FmtAdapter};
use crate::{closure::Closure, env::Place};
use super::{ use super::{
Callable, Environment,
builtin::Builtin, builtin::Builtin,
error::{Error, IResult}, error::{Error, IResult},
function::Function, Callable, Environment, function::Function,
}; };
use std::{collections::HashMap, ops::*, rc::Rc}; 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; type Integer = isize;
/// A Conlang value stores data in the interpreter /// A Conlang value stores data in the interpreter
@ -29,23 +54,25 @@ pub enum ConValue {
/// A string /// A string
String(Sym), String(Sym),
/// A reference /// A reference
Ref(Rc<ConValue>), Ref(Place),
/// A reference to an array
Slice(Place, usize),
/// An Array /// An Array
Array(Box<[ConValue]>), Array(Box<[ConValue]>),
/// A tuple /// A tuple
Tuple(Box<[ConValue]>), Tuple(Box<[ConValue]>),
/// An exclusive range
RangeExc(Integer, Integer),
/// An inclusive range
RangeInc(Integer, Integer),
/// A value of a product type /// A value of a product type
Struct(Box<(Sym, HashMap<Sym, ConValue>)>), Struct(Box<(Sym, HashMap<Sym, ConValue>)>),
/// A value of a product type with anonymous members
TupleStruct(Box<(&'static str, Box<[ConValue]>)>),
/// An entire namespace /// An entire namespace
Module(Box<HashMap<Sym, Option<ConValue>>>), Module(Box<HashMap<Sym, Option<ConValue>>>),
/// A quoted expression /// A quoted expression
Quote(Box<ExprKind>), Quote(Box<Expr>),
/// A callable thing /// A callable thing
Function(Rc<Function>), Function(Rc<Function>),
/// A closure, capturing by reference
Closure(Rc<Closure>),
/// A built-in function /// A built-in function
Builtin(&'static Builtin), Builtin(&'static Builtin),
} }
@ -55,24 +82,22 @@ impl ConValue {
pub fn truthy(&self) -> IResult<bool> { pub fn truthy(&self) -> IResult<bool> {
match self { match self {
ConValue::Bool(v) => Ok(*v), ConValue::Bool(v) => Ok(*v),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
} }
} }
pub fn range_exc(self, other: Self) -> IResult<Self> {
let (Self::Int(a), Self::Int(b)) = (self, other) else { #[allow(non_snake_case)]
Err(Error::TypeError)? pub fn TupleStruct(name: Sym, values: Box<[ConValue]>) -> Self {
}; Self::TupleStruct(Box::new((name.to_ref(), values)))
Ok(Self::RangeExc(a, b))
} }
pub fn range_inc(self, other: Self) -> IResult<Self> { #[allow(non_snake_case)]
let (Self::Int(a), Self::Int(b)) = (self, other) else { pub fn Struct(name: Sym, values: HashMap<Sym, ConValue>) -> Self {
Err(Error::TypeError)? Self::Struct(Box::new((name, values)))
};
Ok(Self::RangeInc(a, b))
} }
pub fn index(&self, index: &Self) -> IResult<ConValue> {
pub fn index(&self, index: &Self, env: &Environment) -> IResult<ConValue> {
let Self::Int(index) = index else { let Self::Int(index) = index else {
Err(Error::TypeError)? Err(Error::TypeError())?
}; };
match self { match self {
ConValue::String(string) => string ConValue::String(string) => string
@ -84,7 +109,11 @@ impl ConValue {
.get(*index as usize) .get(*index as usize)
.cloned() .cloned()
.ok_or(Error::OobIndex(*index as usize, arr.len())), .ok_or(Error::OobIndex(*index as usize, arr.len())),
_ => Err(Error::TypeError), ConValue::Slice(id, start) => env
.get_id(*id)
.ok_or(Error::StackOverflow(*id))?
.index(&ConValue::Int((*index as usize + start) as isize), env),
_ => Err(Error::TypeError()),
} }
} }
cmp! { cmp! {
@ -113,6 +142,7 @@ impl Callable for ConValue {
fn name(&self) -> Sym { fn name(&self) -> Sym {
match self { match self {
ConValue::Function(func) => func.name(), ConValue::Function(func) => func.name(),
ConValue::Closure(func) => func.name(),
ConValue::Builtin(func) => func.name(), ConValue::Builtin(func) => func.name(),
_ => "".into(), _ => "".into(),
} }
@ -120,6 +150,7 @@ impl Callable for ConValue {
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> { fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
match self { match self {
Self::Function(func) => func.call(interpreter, args), Self::Function(func) => func.call(interpreter, args),
Self::Closure(func) => func.call(interpreter, args),
Self::Builtin(func) => func.call(interpreter, args), Self::Builtin(func) => func.call(interpreter, args),
_ => Err(Error::NotCallable(self.clone())), _ => Err(Error::NotCallable(self.clone())),
} }
@ -137,7 +168,7 @@ macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
(Self::Bool(a), Self::Bool(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::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
(Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)), (Self::String(a), Self::String(b)) => Ok(Self::Bool(&**a $op &**b)),
_ => Err(Error::TypeError) _ => Err(Error::TypeError())
} }
} }
)*} )*}
@ -165,9 +196,9 @@ from! {
char => ConValue::Char, char => ConValue::Char,
Sym => ConValue::String, Sym => ConValue::String,
&str => ConValue::String, &str => ConValue::String,
Expr => ConValue::Quote,
String => ConValue::String, String => ConValue::String,
Rc<str> => ConValue::String, Rc<str> => ConValue::String,
ExprKind => ConValue::Quote,
Function => ConValue::Function, Function => ConValue::Function,
Vec<ConValue> => ConValue::Tuple, Vec<ConValue> => ConValue::Tuple,
&'static Builtin => ConValue::Builtin, &'static Builtin => ConValue::Builtin,
@ -179,9 +210,9 @@ impl From<()> for ConValue {
} }
impl From<&[ConValue]> for ConValue { impl From<&[ConValue]> for ConValue {
fn from(value: &[ConValue]) -> Self { fn from(value: &[ConValue]) -> Self {
match value.len() { match value {
0 => Self::Empty, [] => Self::Empty,
1 => value[0].clone(), [value] => value.clone(),
_ => Self::Tuple(value.into()), _ => Self::Tuple(value.into()),
} }
} }
@ -207,25 +238,25 @@ ops! {
(ConValue::Char(a), ConValue::Char(b)) => { (ConValue::Char(a), ConValue::Char(b)) => {
ConValue::String([a, b].into_iter().collect::<String>().into()) ConValue::String([a, b].into_iter().collect::<String>().into())
} }
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
BitAnd: bitand = [ BitAnd: bitand = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b), (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
BitOr: bitor = [ BitOr: bitor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b), (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
BitXor: bitxor = [ BitXor: bitxor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b), (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
Div: div = [ Div: div = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
@ -233,13 +264,13 @@ ops! {
eprintln!("Warning: Divide by zero in {a} / {b}"); a eprintln!("Warning: Divide by zero in {a} / {b}"); a
})), })),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b), (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a / b),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
Mul: mul = [ Mul: mul = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b), (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a * b),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
Rem: rem = [ Rem: rem = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
@ -247,23 +278,23 @@ ops! {
println!("Warning: Divide by zero in {a} % {b}"); a println!("Warning: Divide by zero in {a} % {b}"); a
})), })),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b), (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a % b),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
Shl: shl = [ Shl: shl = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
Shr: shr = [ Shr: shr = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
Sub: sub = [ Sub: sub = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)), (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
(ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b), (ConValue::Float(a), ConValue::Float(b)) => ConValue::Float(a - b),
_ => Err(Error::TypeError)? _ => Err(Error::TypeError())?
] ]
} }
impl std::fmt::Display for ConValue { impl std::fmt::Display for ConValue {
@ -275,7 +306,8 @@ impl std::fmt::Display for ConValue {
ConValue::Bool(v) => v.fmt(f), ConValue::Bool(v) => v.fmt(f),
ConValue::Char(v) => v.fmt(f), ConValue::Char(v) => v.fmt(f),
ConValue::String(v) => v.fmt(f), ConValue::String(v) => v.fmt(f),
ConValue::Ref(v) => write!(f, "&{v}"), ConValue::Ref(v) => write!(f, "&<{}>", v),
ConValue::Slice(v, len) => write!(f, "&<{v}>[{len}..]"),
ConValue::Array(array) => { ConValue::Array(array) => {
'['.fmt(f)?; '['.fmt(f)?;
for (idx, element) in array.iter().enumerate() { for (idx, element) in array.iter().enumerate() {
@ -286,8 +318,6 @@ impl std::fmt::Display for ConValue {
} }
']'.fmt(f) ']'.fmt(f)
} }
ConValue::RangeExc(a, b) => write!(f, "{a}..{}", b + 1),
ConValue::RangeInc(a, b) => write!(f, "{a}..={b}"),
ConValue::Tuple(tuple) => { ConValue::Tuple(tuple) => {
'('.fmt(f)?; '('.fmt(f)?;
for (idx, element) in tuple.iter().enumerate() { for (idx, element) in tuple.iter().enumerate() {
@ -298,11 +328,25 @@ impl std::fmt::Display for ConValue {
} }
')'.fmt(f) ')'.fmt(f)
} }
ConValue::TupleStruct(parts) => {
let (name, tuple) = parts.as_ref();
if !name.is_empty() {
write!(f, "{name}")?;
}
'('.fmt(f)?;
for (idx, element) in tuple.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
')'.fmt(f)
}
ConValue::Struct(parts) => { ConValue::Struct(parts) => {
let (name, map) = parts.as_ref(); let (name, map) = parts.as_ref();
use std::fmt::Write; use std::fmt::Write;
if !name.is_empty() { if !name.is_empty() {
write!(f, "{name}: ")?; write!(f, "{name} ")?;
} }
let mut f = f.delimit_with("{", "\n}"); let mut f = f.delimit_with("{", "\n}");
for (k, v) in map.iter() { for (k, v) in map.iter() {
@ -328,9 +372,22 @@ impl std::fmt::Display for ConValue {
ConValue::Function(func) => { ConValue::Function(func) => {
write!(f, "{}", func.decl()) write!(f, "{}", func.decl())
} }
ConValue::Closure(func) => {
write!(f, "{}", func.as_ref())
}
ConValue::Builtin(func) => { ConValue::Builtin(func) => {
write!(f, "{}", func.description()) write!(f, "{}", func.description())
} }
} }
} }
} }
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

@ -3,11 +3,11 @@
use crate::builtin::Builtin; use crate::builtin::Builtin;
use super::{ use super::{
Callable, Interpret,
builtin::{Builtins, Math}, builtin::{Builtins, Math},
convalue::ConValue, convalue::ConValue,
error::{Error, IResult}, error::{Error, IResult},
function::Function, function::Function,
Callable, Interpret,
}; };
use cl_ast::{Function as FnDecl, Sym}; use cl_ast::{Function as FnDecl, Sym};
use std::{ use std::{
@ -19,30 +19,51 @@ use std::{
type StackFrame = HashMap<Sym, Option<ConValue>>; type StackFrame = HashMap<Sym, Option<ConValue>>;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Place {
Global(Sym),
Local(usize),
}
impl Display for Place {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Place::Global(name) => name.fmt(f),
Place::Local(id) => id.fmt(f),
}
}
}
#[derive(Clone, Debug, Default)]
struct EnvFrame {
/// The length of the array when this stack frame was constructed
pub name: Option<&'static str>,
pub base: usize,
pub binds: HashMap<Sym, usize>,
}
/// Implements a nested lexical scope /// Implements a nested lexical scope
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Environment { pub struct Environment {
builtin: StackFrame, global: HashMap<Sym, Option<ConValue>>,
global: Vec<(StackFrame, &'static str)>, values: Vec<Option<ConValue>>,
frames: Vec<(StackFrame, &'static str)>, frames: Vec<EnvFrame>,
} }
impl Display for Environment { impl Display for Environment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (frame, name) in self for EnvFrame { name, base: _, binds } in self.frames.iter().rev() {
.global writeln!(
.iter() f,
.rev() "--- {} ---",
.take(2) if let Some(name) = name { name } else { "" }
.rev() )?;
.chain(self.frames.iter()) for (var, val) in binds {
{
writeln!(f, "--- {name} ---")?;
for (var, val) in frame {
write!(f, "{var}: ")?; write!(f, "{var}: ")?;
match val { match self.values.get(*val) {
Some(value) => writeln!(f, "\t{value}"), Some(Some(value)) => writeln!(f, "\t{value}"),
None => writeln!(f, "<undefined>"), Some(None) => writeln!(f, "<undefined>"),
None => writeln!(f, "ERROR: {var} address blows the stack!"),
}? }?
} }
} }
@ -51,19 +72,11 @@ impl Display for Environment {
} }
impl Default for Environment { impl Default for Environment {
fn default() -> Self { fn default() -> Self {
Self { let mut this = Self::no_builtins();
builtin: to_hashmap(Builtins.iter().chain(Math.iter())), this.add_builtins(Builtins).add_builtins(Math);
global: vec![(HashMap::new(), "globals")], this
frames: vec![],
} }
} }
}
fn to_hashmap(from: impl IntoIterator<Item = &'static Builtin>) -> HashMap<Sym, Option<ConValue>> {
from.into_iter()
.map(|v| (v.name(), Some(v.into())))
.collect()
}
impl Environment { impl Environment {
pub fn new() -> Self { pub fn new() -> Self {
@ -71,36 +84,10 @@ impl Environment {
} }
/// Creates an [Environment] with no [builtins](super::builtin) /// Creates an [Environment] with no [builtins](super::builtin)
pub fn no_builtins() -> Self { pub fn no_builtins() -> Self {
Self { Self { values: Vec::new(), global: HashMap::new(), frames: vec![] }
builtin: HashMap::new(),
global: vec![(Default::default(), "globals")],
frames: vec![],
}
}
pub fn builtins(&self) -> &StackFrame {
&self.builtin
}
pub fn add_builtin(&mut self, builtin: &'static Builtin) -> &mut Self {
self.builtin.insert(builtin.name(), Some(builtin.into()));
self
}
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) {
for builtin in builtins {
self.add_builtin(builtin);
}
}
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
self.frames.push((frame, name));
}
pub fn pop_frame(&mut self) -> Option<(StackFrame, &'static str)> {
self.frames.pop()
} }
/// Reflexively evaluates a node
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> { pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
node.interpret(self) node.interpret(self)
} }
@ -108,102 +95,153 @@ impl Environment {
/// Calls a function inside the interpreter's scope, /// Calls a function inside the interpreter's scope,
/// and returns the result /// and returns the result
pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> { pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
// FIXME: Clone to satisfy the borrow checker let function = self.get(name)?;
let function = self.get(name)?.clone();
function.call(self, args) function.call(self, args)
} }
/// Binds a value to the given name in the current scope.
pub fn bind(&mut self, name: &str, value: impl Into<ConValue>) {
self.insert(name.into(), Some(value.into()));
}
/// Gets all registered globals, bound or unbound.
pub fn globals(&self) -> &HashMap<Sym, Option<ConValue>> {
&self.global
}
/// Adds builtins
///
/// # Panics
///
/// Will panic if globals table is non-empty!
pub fn add_builtins(&mut self, builtins: &'static [Builtin]) -> &mut Self {
let Self { global, .. } = self;
for builtin in builtins {
global.insert(builtin.name(), Some(builtin.into()));
}
self
}
pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
self.enter(name);
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).and_then(std::mem::take));
}
self.values.truncate(base);
Some((out, name.unwrap_or("")))
}
/// Enters a nested scope, returning a [`Frame`] stack-guard. /// Enters a nested scope, returning a [`Frame`] stack-guard.
/// ///
/// [`Frame`] implements Deref/DerefMut for [`Environment`]. /// [`Frame`] implements Deref/DerefMut for [`Environment`].
pub fn frame(&mut self, name: &'static str) -> Frame { pub fn frame(&mut self, name: &'static str) -> Frame {
Frame::new(self, name) Frame::new(self, name)
} }
/// Resolves a variable mutably. /// Resolves a variable mutably.
/// ///
/// Returns a mutable reference to the variable's record, if it exists. /// Returns a mutable reference to the variable's record, if it exists.
pub fn get_mut(&mut self, id: Sym) -> IResult<&mut Option<ConValue>> { pub fn get_mut(&mut self, name: Sym) -> IResult<&mut Option<ConValue>> {
for (frame, _) in self.frames.iter_mut().rev() { let at = self.id_of(name)?;
if let Some(var) = frame.get_mut(&id) { self.get_id_mut(at).ok_or(Error::NotDefined(name))
return Ok(var);
}
}
for (frame, _) in self.global.iter_mut().rev() {
if let Some(var) = frame.get_mut(&id) {
return Ok(var);
}
}
self.builtin.get_mut(&id).ok_or(Error::NotDefined(id))
} }
/// Resolves a variable immutably. /// Resolves a variable immutably.
/// ///
/// Returns a reference to the variable's contents, if it is defined and initialized. /// Returns a reference to the variable's contents, if it is defined and initialized.
pub fn get(&self, id: Sym) -> IResult<ConValue> { pub fn get(&self, name: Sym) -> IResult<ConValue> {
for (frame, _) in self.frames.iter().rev() { let id = self.id_of(name)?;
match frame.get(&id) { let res = match id {
Some(Some(var)) => return Ok(var.clone()), Place::Global(name) => self.global.get(&name),
Some(None) => return Err(Error::NotInitialized(id)), Place::Local(id) => self.values.get(id),
_ => (), };
match res.ok_or(Error::NotDefined(name))? {
Some(value) => Ok(value.clone()),
None => Err(Error::NotInitialized(name)),
} }
} }
for (frame, _) in self.global.iter().rev() {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
self.builtin
.get(&id)
.cloned()
.flatten()
.ok_or(Error::NotDefined(id))
}
pub(crate) fn get_local(&self, id: Sym) -> IResult<ConValue> { /// Resolves the [Place] associated with a [Sym]
for (frame, _) in self.frames.iter().rev() { pub fn id_of(&self, name: Sym) -> IResult<Place> {
match frame.get(&id) { for EnvFrame { binds, .. } in self.frames.iter().rev() {
Some(Some(var)) => return Ok(var.clone()), if let Some(id) = binds.get(&name).copied() {
Some(None) => return Err(Error::NotInitialized(id)), return Ok(Place::Local(id));
_ => (),
} }
} }
Err(Error::NotInitialized(id)) Ok(Place::Global(name))
}
pub fn get_id(&self, at: Place) -> Option<&ConValue> {
let res = match at {
Place::Global(name) => self.global.get(&name),
Place::Local(id) => self.values.get(id),
}?;
res.as_ref()
}
pub fn get_id_mut(&mut self, at: Place) -> Option<&mut Option<ConValue>> {
match at {
Place::Global(name) => self.global.get_mut(&name),
Place::Local(id) => self.values.get_mut(id),
}
} }
/// Inserts a new [ConValue] into this [Environment] /// Inserts a new [ConValue] into this [Environment]
pub fn insert(&mut self, id: Sym, value: Option<ConValue>) { pub fn insert(&mut self, k: Sym, v: Option<ConValue>) {
if let Some((frame, _)) = self.frames.last_mut() { if self.bind_raw(k, self.values.len()).is_some() {
frame.insert(id, value); self.values.push(v);
} else if let Some((frame, _)) = self.global.last_mut() { } else {
frame.insert(id, value); self.global.insert(k, v);
} }
} }
/// A convenience function for registering a [FnDecl] as a [Function] /// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) { pub fn insert_fn(&mut self, decl: &FnDecl) {
let FnDecl { name, .. } = decl; let FnDecl { name, .. } = decl;
let (name, function) = (name, Rc::new(Function::new(decl))); let (name, function) = (*name, Rc::new(Function::new(decl)));
if let Some((frame, _)) = self.frames.last_mut() { self.insert(name, Some(ConValue::Function(function.clone())));
frame.insert(*name, Some(ConValue::Function(function.clone())));
} else if let Some((frame, _)) = self.global.last_mut() {
frame.insert(*name, Some(ConValue::Function(function.clone())));
}
// Tell the function to lift its upvars now, after it's been declared // Tell the function to lift its upvars now, after it's been declared
function.lift_upvars(self); function.lift_upvars(self);
} }
/// Allocates a local variable
pub fn stack_alloc(&mut self, value: ConValue) -> IResult<usize> {
let adr = self.values.len();
self.values.push(Some(value));
Ok(adr)
}
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(())
}
} }
/// Functions which aid in the implementation of [`Frame`] /// Functions which aid in the implementation of [`Frame`]
impl Environment { impl Environment {
/// Enters a scope, creating a new namespace for variables /// Enters a scope, creating a new namespace for variables
fn enter(&mut self, name: &'static str) -> &mut Self { fn enter(&mut self, name: &'static str) -> &mut Self {
self.frames.push((Default::default(), name)); let new_frame =
EnvFrame { name: Some(name), base: self.values.len(), binds: HashMap::new() };
self.frames.push(new_frame);
self self
} }
/// Exits the scope, destroying all local variables and /// Exits the scope, destroying all local variables and
/// returning the outer scope, if there is one /// returning the outer scope, if there is one
fn exit(&mut self) -> &mut Self { fn exit(&mut self) -> &mut Self {
self.frames.pop(); if let Some(frame) = self.frames.pop() {
self.values.truncate(frame.base);
}
self self
} }
} }

View File

@ -1,14 +1,123 @@
//! The [Error] type represents any error thrown by the [Environment](super::Environment) //! The [Error] type represents any error thrown by the [Environment](super::Environment)
use cl_ast::{Pattern, Sym}; use cl_ast::{Pattern, Sym};
use cl_structures::span::Span;
use super::convalue::ConValue; use super::{convalue::ConValue, env::Place};
pub type IResult<T> = Result<T, Error>; pub type IResult<T> = Result<T, Error>;
#[derive(Clone, Debug)]
pub struct Error {
pub kind: ErrorKind,
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: Place) -> 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 }
}
/// 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) /// Represents any error thrown by the [Environment](super::Environment)
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Error { pub enum ErrorKind {
/// Propagate a Return value /// Propagate a Return value
Return(ConValue), Return(ConValue),
/// Propagate a Break value /// Propagate a Break value
@ -19,6 +128,8 @@ pub enum Error {
Continue, Continue,
/// Underflowed the stack /// Underflowed the stack
StackUnderflow, StackUnderflow,
/// Overflowed the stack
StackOverflow(Place),
/// Exited the last scope /// Exited the last scope
ScopeExit, ScopeExit,
/// Type incompatibility /// Type incompatibility
@ -45,53 +156,56 @@ pub enum Error {
/// Fell through a non-exhaustive match /// Fell through a non-exhaustive match
MatchNonexhaustive, MatchNonexhaustive,
/// Error produced by a Builtin /// Error produced by a Builtin
BuiltinDebug(String), BuiltinError(String),
} }
impl std::error::Error for Error {} impl std::error::Error for ErrorKind {}
impl std::fmt::Display for Error { impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Error::Return(value) => write!(f, "return {value}"), ErrorKind::Return(value) => write!(f, "return {value}"),
Error::Break(value) => write!(f, "break {value}"), ErrorKind::Break(value) => write!(f, "break {value}"),
Error::BadBreak(value) => write!(f, "rogue break: {value}"), ErrorKind::BadBreak(value) => write!(f, "rogue break: {value}"),
Error::Continue => "continue".fmt(f), ErrorKind::Continue => "continue".fmt(f),
Error::StackUnderflow => "Stack underflow".fmt(f), ErrorKind::StackUnderflow => "Stack underflow".fmt(f),
Error::ScopeExit => "Exited the last scope. This is a logic bug.".fmt(f), ErrorKind::StackOverflow(id) => {
Error::TypeError => "Incompatible types".fmt(f), write!(f, "Attempt to access <{id}> resulted in stack overflow.")
Error::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f), }
Error::NotIndexable => { 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") write!(f, "expression cannot be indexed")
} }
Error::OobIndex(idx, len) => { ErrorKind::OobIndex(idx, len) => {
write!(f, "Index out of bounds: index was {idx}. but len is {len}") write!(f, "Index out of bounds: index was {idx}. but len is {len}")
} }
Error::NotAssignable => { ErrorKind::NotAssignable => {
write!(f, "expression is not assignable") write!(f, "expression is not assignable")
} }
Error::NotDefined(value) => { ErrorKind::NotDefined(value) => {
write!(f, "{value} not bound. Did you mean `let {value};`?") write!(f, "{value} not bound. Did you mean `let {value};`?")
} }
Error::NotInitialized(value) => { ErrorKind::NotInitialized(value) => {
write!(f, "{value} bound, but not initialized") write!(f, "{value} bound, but not initialized")
} }
Error::NotCallable(value) => { ErrorKind::NotCallable(value) => {
write!(f, "{value} is not callable.") write!(f, "{value} is not callable.")
} }
Error::ArgNumber { want, got } => { ErrorKind::ArgNumber { want, got } => {
write!( write!(
f, f,
"Expected {want} argument{}, got {got}", "Expected {want} argument{}, got {got}",
if *want == 1 { "" } else { "s" } if *want == 1 { "" } else { "s" }
) )
} }
Error::PatFailed(pattern) => { ErrorKind::PatFailed(pattern) => {
write!(f, "Failed to match pattern {pattern}") write!(f, "Failed to match pattern {pattern}")
} }
Error::MatchNonexhaustive => { ErrorKind::MatchNonexhaustive => {
write!(f, "Fell through a non-exhaustive match expression!") write!(f, "Fell through a non-exhaustive match expression!")
} }
Error::BuiltinDebug(s) => write!(f, "DEBUG: {s}"), ErrorKind::BuiltinError(s) => write!(f, "{s}"),
} }
} }
} }

View File

@ -2,8 +2,10 @@
use collect_upvars::collect_upvars; use collect_upvars::collect_upvars;
use super::{Callable, ConValue, Environment, Error, IResult, Interpret}; use crate::error::ErrorKind;
use cl_ast::{Function as FnDecl, Param, Sym};
use super::{Callable, ConValue, Environment, Error, IResult, Interpret, pattern};
use cl_ast::{Function as FnDecl, Sym};
use std::{ use std::{
cell::{Ref, RefCell}, cell::{Ref, RefCell},
collections::HashMap, collections::HashMap,
@ -21,12 +23,16 @@ pub struct Function {
decl: Rc<FnDecl>, decl: Rc<FnDecl>,
/// Stores data from the enclosing scopes /// Stores data from the enclosing scopes
upvars: RefCell<Upvars>, upvars: RefCell<Upvars>,
is_constructor: bool,
} }
impl Function { impl Function {
pub fn new(decl: &FnDecl) -> Self { pub fn new(decl: &FnDecl) -> Self {
// let upvars = collect_upvars(decl, env); // let upvars = collect_upvars(decl, env);
Self { decl: decl.clone().into(), upvars: Default::default() } Self { decl: decl.clone().into(), upvars: Default::default(), is_constructor: false }
}
pub fn new_constructor(decl: FnDecl) -> Self {
Self { decl: decl.into(), upvars: Default::default(), is_constructor: true }
} }
pub fn decl(&self) -> &FnDecl { pub fn decl(&self) -> &FnDecl {
&self.decl &self.decl
@ -48,11 +54,14 @@ impl Callable for Function {
name name
} }
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> { fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
let FnDecl { name, bind, body, sign: _ } = &*self.decl; let FnDecl { name, gens: _, bind, body, sign: _ } = &*self.decl;
// Check arg mapping // Check arg mapping
if args.len() != bind.len() { if self.is_constructor {
return Err(Error::ArgNumber { want: bind.len(), got: args.len() }); return Ok(ConValue::TupleStruct(Box::new((
name.to_ref(),
args.into(),
))));
} }
let Some(body) = body else { let Some(body) = body else {
return Err(Error::NotDefined(*name)); return Err(Error::NotDefined(*name));
@ -63,8 +72,8 @@ impl Callable for Function {
// TODO: completely refactor data storage // TODO: completely refactor data storage
let mut frame = env.frame("fn args"); let mut frame = env.frame("fn args");
for (Param { mutability: _, name }, value) in bind.iter().zip(args) { for (name, value) in pattern::substitution(bind, ConValue::Tuple(args.into()))? {
frame.insert(*name, Some(value.clone())); frame.insert(*name, Some(value));
} }
let res = body.interpret(&mut frame); let res = body.interpret(&mut frame);
drop(frame); drop(frame);
@ -72,9 +81,9 @@ impl Callable for Function {
self.upvars.replace(upvars); self.upvars.replace(upvars);
} }
match res { match res {
Err(Error::Return(value)) => Ok(value), Err(Error { kind: ErrorKind::Return(value), .. }) => Ok(value),
Err(Error::Break(value)) => Err(Error::BadBreak(value)), Err(Error { kind: ErrorKind::Break(value), .. }) => Err(Error::BadBreak(value)),
result => result, other => other,
} }
} }
} }

View File

@ -1,16 +1,19 @@
//! Collects the "Upvars" of a function at the point of its creation, allowing variable capture //! Collects the "Upvars" of a function at the point of its creation, allowing variable capture
use crate::{convalue::ConValue, env::Environment}; use crate::env::{Environment, Place};
use cl_ast::{ast_visitor::visit::*, Function, Let, Param, Path, PathPart, Pattern, Sym}; use cl_ast::{
Function, Let, Path, PathPart, Pattern, Sym,
ast_visitor::{visit::*, walk::Walk},
};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
pub fn collect_upvars(f: &Function, env: &Environment) -> super::Upvars { pub fn collect_upvars(f: &Function, env: &Environment) -> super::Upvars {
CollectUpvars::new(env).get_upvars(f) CollectUpvars::new(env).visit(f).finish_copied()
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CollectUpvars<'env> { pub struct CollectUpvars<'env> {
env: &'env Environment, env: &'env Environment,
upvars: HashMap<Sym, Option<ConValue>>, upvars: HashMap<Sym, Place>,
blacklist: HashSet<Sym>, blacklist: HashSet<Sym>,
} }
@ -18,9 +21,17 @@ impl<'env> CollectUpvars<'env> {
pub fn new(env: &'env Environment) -> Self { pub fn new(env: &'env Environment) -> Self {
Self { upvars: HashMap::new(), blacklist: HashSet::new(), env } Self { upvars: HashMap::new(), blacklist: HashSet::new(), env }
} }
pub fn get_upvars(mut self, f: &cl_ast::Function) -> HashMap<Sym, Option<ConValue>> {
self.visit_function(f); pub fn finish(&mut self) -> HashMap<Sym, Place> {
self.upvars 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()
.map(|(k, v)| (k, env.get_id(v).cloned()))
.collect()
} }
pub fn add_upvar(&mut self, name: &Sym) { pub fn add_upvar(&mut self, name: &Sym) {
@ -28,8 +39,8 @@ impl<'env> CollectUpvars<'env> {
if blacklist.contains(name) || upvars.contains_key(name) { if blacklist.contains(name) || upvars.contains_key(name) {
return; return;
} }
if let Ok(upvar) = env.get_local(*name) { if let Ok(place) = env.id_of(*name) {
upvars.insert(*name, Some(upvar)); upvars.insert(*name, place);
} }
} }
@ -43,8 +54,7 @@ impl<'a> Visit<'a> for CollectUpvars<'_> {
let blacklist = self.blacklist.clone(); let blacklist = self.blacklist.clone();
// visit the block // visit the block
let cl_ast::Block { stmts } = b; b.children(self);
stmts.iter().for_each(|s| self.visit_stmt(s));
// restore the blacklist // restore the blacklist
self.blacklist = blacklist; self.blacklist = blacklist;
@ -53,33 +63,26 @@ impl<'a> Visit<'a> for CollectUpvars<'_> {
fn visit_let(&mut self, l: &'a cl_ast::Let) { fn visit_let(&mut self, l: &'a cl_ast::Let) {
let Let { mutable, name, ty, init } = l; let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable); self.visit_mutability(mutable);
if let Some(ty) = ty {
self.visit_ty(ty); ty.visit_in(self);
}
// visit the initializer, which may use the bound name // visit the initializer, which may use the bound name
if let Some(init) = init { init.visit_in(self);
self.visit_expr(init)
}
// a bound name can never be an upvar // a bound name can never be an upvar
self.visit_pattern(name); self.visit_pattern(name);
} }
fn visit_function(&mut self, f: &'a cl_ast::Function) { fn visit_function(&mut self, f: &'a cl_ast::Function) {
let Function { name: _, sign: _, bind, body } = f; let Function { name: _, gens: _, sign: _, bind, body } = f;
// parameters can never be upvars // parameters can never be upvars
for Param { mutability: _, name } in bind { bind.visit_in(self);
self.bind_name(name); body.visit_in(self);
}
if let Some(body) = body {
self.visit_block(body);
}
} }
fn visit_for(&mut self, f: &'a cl_ast::For) { fn visit_for(&mut self, f: &'a cl_ast::For) {
let cl_ast::For { bind, cond, pass, fail } = f; let cl_ast::For { bind, cond, pass, fail } = f;
self.visit_expr(cond); self.visit_expr(cond);
self.visit_else(fail); self.visit_else(fail);
self.bind_name(bind); // TODO: is bind only bound in the pass block? self.visit_pattern(bind);
self.visit_block(pass); self.visit_block(pass);
} }
@ -105,30 +108,11 @@ impl<'a> Visit<'a> for CollectUpvars<'_> {
fn visit_pattern(&mut self, p: &'a cl_ast::Pattern) { fn visit_pattern(&mut self, p: &'a cl_ast::Pattern) {
match p { match p {
Pattern::Path(path) => { Pattern::Name(name) => {
if let [PathPart::Ident(name)] = path.parts.as_slice() { self.bind_name(name);
self.bind_name(name)
}
}
Pattern::Literal(literal) => self.visit_literal(literal),
Pattern::Ref(mutability, pattern) => {
self.visit_mutability(mutability);
self.visit_pattern(pattern);
}
Pattern::Tuple(patterns) => {
patterns.iter().for_each(|p| self.visit_pattern(p));
}
Pattern::Array(patterns) => {
patterns.iter().for_each(|p| self.visit_pattern(p));
}
Pattern::Struct(path, items) => {
self.visit_path(path);
items.iter().for_each(|(_name, bind)| {
bind.as_ref().inspect(|bind| {
self.visit_pattern(bind);
});
});
} }
Pattern::RangeExc(_, _) | Pattern::RangeInc(_, _) => {}
_ => p.children(self),
} }
} }
} }

View File

@ -5,11 +5,9 @@
//! meaningless to get a pointer to one, and would be undefined behavior to dereference a pointer to //! meaningless to get a pointer to one, and would be undefined behavior to dereference a pointer to
//! one in any situation. //! one in any situation.
use std::{borrow::Borrow, rc::Rc};
use super::*; use super::*;
use cl_ast::*; use cl_ast::{ast_visitor::Visit, *};
use cl_structures::intern::interned::Interned; use std::borrow::Borrow;
/// A work-in-progress tree walk interpreter for Conlang /// A work-in-progress tree walk interpreter for Conlang
pub trait Interpret { pub trait Interpret {
/// Interprets this thing in the given [`Environment`]. /// Interprets this thing in the given [`Environment`].
@ -20,9 +18,30 @@ pub trait Interpret {
impl Interpret for File { impl Interpret for File {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
for item in &self.items { /// Sorts items
#[derive(Debug, Default)]
struct ItemSorter<'ast>(pub [Vec<&'ast Item>; 8]);
impl<'ast> Visit<'ast> for ItemSorter<'ast> {
fn visit_item(&mut self, i: &'ast Item) {
for stage in match &i.kind {
ItemKind::Module(_) => [0].as_slice(),
ItemKind::Use(_) => &[1, 6],
ItemKind::Enum(_) | ItemKind::Struct(_) | ItemKind::Alias(_) => &[2],
ItemKind::Function(_) => &[3, 7],
ItemKind::Impl(_) => &[4],
ItemKind::Const(_) | ItemKind::Static(_) => &[5],
} {
self.0[*stage].push(i)
}
}
}
let mut items = ItemSorter::default();
items.visit_file(self);
for item in items.0.into_iter().flatten() {
item.interpret(env)?; item.interpret(env)?;
} }
Ok(ConValue::Empty) Ok(ConValue::Empty)
} }
} }
@ -66,12 +85,13 @@ impl Interpret for Static {
} }
} }
impl Interpret for Module { impl Interpret for Module {
// TODO: Keep modules around somehow, rather than putting them on the stack
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { name, kind } = self; let Self { name, file } = self;
env.push_frame(Interned::to_ref(name), Default::default()); env.push_frame(name.to_ref(), Default::default());
let out = match kind { let out = match file {
ModuleKind::Inline(file) => file.interpret(env), Some(file) => file.interpret(env),
ModuleKind::Outline => { None => {
eprintln!("Module {name} specified, but not imported."); eprintln!("Module {name} specified, but not imported.");
Ok(ConValue::Empty) Ok(ConValue::Empty)
} }
@ -93,22 +113,88 @@ impl Interpret for Function {
} }
} }
impl Interpret for Struct { impl Interpret for Struct {
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
println!("TODO: {self}"); let Self { name, gens: _, kind } = self;
match kind {
StructKind::Empty => {}
StructKind::Tuple(args) => {
// Constructs the AST from scratch. TODO: This, better.
let constructor = Function {
name: *name,
gens: Default::default(),
sign: TyFn {
args: TyKind::Tuple(TyTuple {
types: args.iter().map(|ty| ty.kind.clone()).collect(),
})
.into(),
rety: Some(
Ty {
span: cl_structures::span::Span::dummy(),
kind: TyKind::Path(Path::from(*name)),
}
.into(),
),
},
bind: Pattern::Tuple(
args.iter()
.enumerate()
.map(|(idx, _)| Pattern::Name(idx.to_string().into()))
.collect(),
),
body: None,
};
let constructor = crate::function::Function::new_constructor(constructor);
env.insert(*name, Some(constructor.into()));
}
StructKind::Struct(_) => eprintln!("TODO: {self}"),
}
Ok(ConValue::Empty) Ok(ConValue::Empty)
} }
} }
impl Interpret for Enum { impl Interpret for Enum {
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
println!("TODO: {self}"); let Self { name, gens: _, variants } = self;
env.push_frame(name.to_ref(), Default::default());
for (idx, Variant { name, kind, body }) in variants.iter().enumerate() {
match (kind, body) {
(StructKind::Empty, None) => env.insert(*name, Some(ConValue::Int(idx as _))),
(StructKind::Empty, Some(idx)) => {
let idx = idx.interpret(env)?;
env.insert(*name, Some(idx))
}
(StructKind::Tuple(_), None) => {}
(StructKind::Struct(_), None) => {}
_ => eprintln!("Well-formedness error in {self}"),
}
}
let (frame, _) = env
.pop_frame()
.expect("Frame stack should remain balanced.");
env.insert(*name, Some(ConValue::Module(Box::new(frame))));
Ok(ConValue::Empty) Ok(ConValue::Empty)
} }
} }
impl Interpret for Impl { impl Interpret for Impl {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
println!("TODO: {self}"); let Self { target: ImplKind::Type(Ty { span, kind: TyKind::Path(name) }), body } = self
let Self { target: _, body } = self; else {
body.interpret(env) eprintln!("TODO: impl X for Ty");
return Ok(ConValue::Empty);
};
env.push_frame("impl", Default::default());
body.interpret(env)?;
let (frame, _) = env
.pop_frame()
.expect("Environment frames must be balanced");
match assignment::addrof_path(env, name.parts.as_slice())
.map_err(|err| err.with_span(*span))?
{
Some(ConValue::Module(m)) => m.extend(frame),
Some(other) => eprintln!("TODO: impl for {other}"),
None => {}
}
Ok(ConValue::Empty)
} }
} }
@ -121,6 +207,7 @@ impl Interpret for Use {
impl Interpret for UseTree { impl Interpret for UseTree {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
// TODO: raw-bind use items
type Bindings = HashMap<Sym, ConValue>; type Bindings = HashMap<Sym, ConValue>;
use std::collections::HashMap; use std::collections::HashMap;
@ -137,9 +224,9 @@ impl Interpret for UseTree {
} }
UseTree::Path(PathPart::Ident(name), tree) => { UseTree::Path(PathPart::Ident(name), tree) => {
let Ok(ConValue::Module(m)) = env.get(*name) else { let Ok(ConValue::Module(m)) = env.get(*name) else {
Err(Error::TypeError)? Err(Error::TypeError())?
}; };
env.push_frame(Interned::to_ref(name), *m); env.push_frame(name.to_ref(), *m);
let out = get_bindings(tree, env, bindings); let out = get_bindings(tree, env, bindings);
env.pop_frame(); env.pop_frame();
return out; return out;
@ -180,12 +267,13 @@ impl Interpret for UseTree {
impl Interpret for Stmt { impl Interpret for Stmt {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { extents: _, kind, semi } = self; let Self { span, kind, semi } = self;
let out = match kind { let out = match kind {
StmtKind::Empty => ConValue::Empty, StmtKind::Empty => Ok(ConValue::Empty),
StmtKind::Item(stmt) => stmt.interpret(env)?, StmtKind::Item(stmt) => stmt.interpret(env),
StmtKind::Expr(stmt) => stmt.interpret(env)?, StmtKind::Expr(stmt) => stmt.interpret(env),
}; }
.map_err(|err| err.with_span(*span))?;
Ok(match semi { Ok(match semi {
Semi::Terminated => ConValue::Empty, Semi::Terminated => ConValue::Empty,
Semi::Unterminated => out, Semi::Unterminated => out,
@ -196,8 +284,8 @@ impl Interpret for Stmt {
impl Interpret for Expr { impl Interpret for Expr {
#[inline] #[inline]
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { extents: _, kind } = self; let Self { span, kind } = self;
kind.interpret(env) kind.interpret(env).map_err(|err| err.with_span(*span))
} }
} }
@ -205,6 +293,7 @@ impl Interpret for ExprKind {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
match self { match self {
ExprKind::Empty => Ok(ConValue::Empty), ExprKind::Empty => Ok(ConValue::Empty),
ExprKind::Closure(v) => v.interpret(env),
ExprKind::Quote(q) => q.interpret(env), ExprKind::Quote(q) => q.interpret(env),
ExprKind::Let(v) => v.interpret(env), ExprKind::Let(v) => v.interpret(env),
ExprKind::Match(v) => v.interpret(env), ExprKind::Match(v) => v.interpret(env),
@ -229,11 +318,19 @@ impl Interpret for ExprKind {
ExprKind::For(v) => v.interpret(env), ExprKind::For(v) => v.interpret(env),
ExprKind::Break(v) => v.interpret(env), ExprKind::Break(v) => v.interpret(env),
ExprKind::Return(v) => v.interpret(env), ExprKind::Return(v) => v.interpret(env),
ExprKind::Continue => Err(Error::Continue), ExprKind::Continue => Err(Error::Continue()),
} }
} }
} }
impl Interpret for Closure {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
Ok(ConValue::Closure(
crate::closure::Closure::new(env, self).into(),
))
}
}
impl Interpret for Quote { impl Interpret for Quote {
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
// TODO: squoosh down into a ConValue? // TODO: squoosh down into a ConValue?
@ -246,23 +343,20 @@ impl Interpret for Let {
let Let { mutable: _, name, ty: _, init } = self; let Let { mutable: _, name, ty: _, init } = self;
match init.as_ref().map(|i| i.interpret(env)).transpose()? { match init.as_ref().map(|i| i.interpret(env)).transpose()? {
Some(value) => { Some(value) => {
for (path, value) in assignment::pattern_substitution(name, value)? { if let Ok(sub) = pattern::substitution(name, value) {
match path.parts.as_slice() { for (name, value) in sub {
[PathPart::Ident(name)] => env.insert(*name, Some(value)), env.insert(*name, Some(value));
_ => eprintln!("Bad assignment: {path} = {value}"),
}
} }
return Ok(ConValue::Bool(true));
};
} }
None => { None => {
for path in assignment::pattern_variables(name) { for name in pattern::variables(name) {
match path.parts.as_slice() { env.insert(*name, None);
[PathPart::Ident(name)] => env.insert(*name, None),
_ => eprintln!("Bad assignment: {path}"),
} }
} }
} }
} Ok(ConValue::Bool(false))
Ok(ConValue::Empty)
} }
} }
@ -270,19 +364,16 @@ impl Interpret for Match {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { scrutinee, arms } = self; let Self { scrutinee, arms } = self;
let scrutinee = scrutinee.interpret(env)?; let scrutinee = scrutinee.interpret(env)?;
'arm: for MatchArm(pat, expr) in arms { for MatchArm(pat, expr) in arms {
if let Ok(substitution) = assignment::pattern_substitution(pat, scrutinee.clone()) { if let Ok(substitution) = pattern::substitution(pat, scrutinee.clone()) {
let mut env = env.frame("match"); let mut env = env.frame("match");
for (path, value) in substitution { for (name, value) in substitution {
let [PathPart::Ident(name)] = path.parts.as_slice() else {
continue 'arm;
};
env.insert(*name, Some(value)); env.insert(*name, Some(value));
} }
return expr.interpret(&mut env); return expr.interpret(&mut env);
} }
} }
Err(Error::MatchNonexhaustive) Err(Error::MatchNonexhaustive())
} }
} }
@ -292,165 +383,56 @@ mod assignment {
use std::collections::HashMap; use std::collections::HashMap;
type Namespace = HashMap<Sym, Option<ConValue>>; type Namespace = HashMap<Sym, Option<ConValue>>;
/// Gets the path variables in the given Pattern
pub fn pattern_variables(pat: &Pattern) -> Vec<&Path> {
fn patvars<'p>(set: &mut Vec<&'p Path>, pat: &'p Pattern) {
match pat {
Pattern::Path(path) if path.is_sinkhole() => {}
Pattern::Path(path) => set.push(path),
Pattern::Literal(_) => {}
Pattern::Ref(_, pattern) => patvars(set, pattern),
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),
});
}
}
}
let mut set = Vec::new();
patvars(&mut set, pat);
set
}
/// Appends a substitution to the provided table
pub fn append_sub<'pat>(
env: &mut HashMap<&'pat Path, ConValue>,
pat: &'pat Pattern,
value: ConValue,
) -> IResult<()> {
match pat {
Pattern::Path(path) if path.is_sinkhole() => Ok(()),
Pattern::Path(path) => {
env.insert(path, value);
Ok(())
}
Pattern::Literal(literal) => match (literal, value) {
(Literal::Bool(a), ConValue::Bool(b)) => *a == b,
(Literal::Char(a), ConValue::Char(b)) => *a == b,
(Literal::Int(a), ConValue::Int(b)) => *a as isize == b,
(Literal::Float(a), ConValue::Float(b)) => f64::from_bits(*a) == b,
(Literal::String(a), ConValue::String(b)) => *a == *b,
_ => false,
}
.then_some(())
.ok_or(Error::NotAssignable),
Pattern::Ref(_, pattern) => match value {
ConValue::Ref(value) => append_sub(env, pattern, Rc::unwrap_or_clone(value)),
_ => Err(Error::NotAssignable),
},
Pattern::Tuple(patterns) => match value {
ConValue::Tuple(values) => {
if patterns.len() != values.len() {
return Err(Error::OobIndex(patterns.len(), values.len()));
};
for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) {
append_sub(env, pat, value)?;
}
Ok(())
}
_ => Err(Error::NotAssignable),
},
Pattern::Array(patterns) => match value {
ConValue::Array(values) => {
if patterns.len() != values.len() {
return Err(Error::OobIndex(patterns.len(), values.len()));
};
for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) {
append_sub(env, pat, value)?;
}
Ok(())
}
_ => Err(Error::NotAssignable),
},
Pattern::Struct(_path, patterns) => {
let ConValue::Struct(parts) = value else {
return Err(Error::TypeError);
};
let (_, mut values) = *parts;
if values.len() != patterns.len() {
return Err(Error::TypeError);
}
for (name, pat) in patterns {
let [.., PathPart::Ident(index)] = name.parts.as_slice() else {
Err(Error::TypeError)?
};
let value = values.remove(index).ok_or(Error::TypeError)?;
match pat {
Some(pat) => append_sub(env, pat, value)?,
None => {
env.insert(name, value);
}
}
}
Ok(())
}
}
}
/// Constructs a substitution from a pattern and a value
pub fn pattern_substitution(
pat: &Pattern,
value: ConValue,
) -> IResult<HashMap<&Path, ConValue>> {
let mut substitution = HashMap::new();
append_sub(&mut substitution, pat, value)?;
Ok(substitution)
}
pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> { pub(super) fn pat_assign(env: &mut Environment, pat: &Pattern, value: ConValue) -> IResult<()> {
let mut substitution = HashMap::new(); for (name, value) in
append_sub(&mut substitution, pat, value) pattern::substitution(pat, value).map_err(|_| Error::PatFailed(pat.clone().into()))?
.map_err(|_| Error::PatFailed(pat.clone().into()))?; {
for (path, value) in substitution { match env.get_mut(*name)? {
assign_path(env, path, value)?; &mut Some(ConValue::Ref(id)) => {
*(env.get_id_mut(id).ok_or(Error::StackOverflow(id))?) = Some(value);
}
other => *other = Some(value),
}
} }
Ok(()) Ok(())
} }
pub(super) fn assign(env: &mut Environment, pat: &ExprKind, value: ConValue) -> IResult<()> { pub(super) fn assign(env: &mut Environment, pat: &Expr, value: ConValue) -> IResult<()> {
if let Ok(pat) = Pattern::try_from(pat.clone()) { if let Ok(pat) = Pattern::try_from(pat.clone()) {
return pat_assign(env, &pat, value); return pat_assign(env, &pat, value);
} }
match pat { match &pat.kind {
ExprKind::Member(member) => *addrof_member(env, member)? = value, ExprKind::Member(member) => *addrof_member(env, member)? = value,
ExprKind::Index(index) => *addrof_index(env, index)? = value, ExprKind::Index(index) => *addrof_index(env, index)? = value,
_ => Err(Error::NotAssignable)?, ExprKind::Path(path) => *addrof_path(env, &path.parts)? = Some(value),
ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match addrof(env, tail)? {
&mut ConValue::Ref(r) => {
*env.get_id_mut(r).ok_or(Error::StackOverflow(r))? = Some(value)
}
_ => Err(Error::NotAssignable())?,
},
_ => Err(Error::NotAssignable())?,
} }
Ok(()) Ok(())
} }
fn assign_path(env: &mut Environment, path: &Path, value: ConValue) -> IResult<()> { pub(super) fn addrof<'e>(env: &'e mut Environment, pat: &Expr) -> IResult<&'e mut ConValue> {
let Ok(addr) = addrof_path(env, &path.parts) else { match &pat.kind {
eprintln!("Cannot assign {value} to path {path}");
return Err(Error::NotAssignable);
};
*addr = Some(value);
Ok(())
}
pub(super) fn addrof<'e>(
env: &'e mut Environment,
pat: &ExprKind,
) -> IResult<&'e mut ConValue> {
match pat {
ExprKind::Path(path) => addrof_path(env, &path.parts)? ExprKind::Path(path) => addrof_path(env, &path.parts)?
.as_mut() .as_mut()
.ok_or(Error::NotInitialized("".into())), .ok_or(Error::NotInitialized("".into())),
ExprKind::Member(member) => addrof_member(env, member), ExprKind::Member(member) => addrof_member(env, member),
ExprKind::Index(index) => addrof_index(env, index), ExprKind::Index(index) => addrof_index(env, index),
ExprKind::Group(Group { expr }) => addrof(env, expr), ExprKind::Group(Group { expr }) => addrof(env, expr),
ExprKind::AddrOf(AddrOf { mutable: Mutability::Mut, expr }) => addrof(env, expr), ExprKind::Unary(Unary { kind: UnaryKind::Deref, tail }) => match *addrof(env, tail)? {
_ => Err(Error::TypeError), ConValue::Ref(place) => env
.get_id_mut(place)
.ok_or(Error::NotIndexable())?
.as_mut()
.ok_or(Error::NotAssignable()),
_ => Err(Error::TypeError()),
},
_ => Err(Error::TypeError()),
} }
} }
@ -461,30 +443,21 @@ mod assignment {
match path { match path {
[PathPart::Ident(name)] => env.get_mut(*name), [PathPart::Ident(name)] => env.get_mut(*name),
[PathPart::Ident(name), rest @ ..] => match env.get_mut(*name)? { [PathPart::Ident(name), rest @ ..] => match env.get_mut(*name)? {
Some(ConValue::Module(env)) => addrof_path_within_namespace(env, rest), Some(ConValue::Module(env)) => project_path_in_namespace(env, rest),
_ => Err(Error::NotIndexable), _ => Err(Error::NotIndexable()),
}, },
_ => Err(Error::NotAssignable), _ => Err(Error::NotAssignable()),
} }
} }
fn addrof_member<'e>(env: &'e mut Environment, member: &Member) -> IResult<&'e mut ConValue> { pub fn addrof_member<'e>(
env: &'e mut Environment,
member: &Member,
) -> IResult<&'e mut ConValue> {
let Member { head, kind } = member; let Member { head, kind } = member;
let ExprKind::Path(path) = head.as_ref() else {
return Err(Error::TypeError); let head = addrof(env, head)?;
}; project_memberkind(head, kind)
let slot = addrof_path(env, &path.parts)?
.as_mut()
.ok_or(Error::NotAssignable)?;
Ok(match (slot, kind) {
(ConValue::Struct(s), MemberKind::Struct(id)) => {
s.1.get_mut(id).ok_or(Error::NotDefined(*id))?
}
(ConValue::Tuple(t), MemberKind::Tuple(Literal::Int(id))) => t
.get_mut(*id as usize)
.ok_or_else(|| Error::NotDefined(id.to_string().into()))?,
_ => Err(Error::TypeError)?,
})
} }
fn addrof_index<'e>(env: &'e mut Environment, index: &Index) -> IResult<&'e mut ConValue> { fn addrof_index<'e>(env: &'e mut Environment, index: &Index) -> IResult<&'e mut ConValue> {
@ -493,34 +466,66 @@ mod assignment {
.iter() .iter()
.map(|index| index.interpret(env)) .map(|index| index.interpret(env))
.collect::<IResult<Vec<_>>>()?; .collect::<IResult<Vec<_>>>()?;
let mut head = addrof(env, head)?; let mut head = addrof(env, head)?;
for index in indices { for index in indices {
head = match (head, index) { head = project_index(head, &index)?;
(ConValue::Array(a), ConValue::Int(i)) => {
let a_len = a.len();
a.get_mut(i as usize)
.ok_or(Error::OobIndex(i as usize, a_len))?
}
_ => Err(Error::NotIndexable)?,
}
} }
Ok(head) Ok(head)
} }
pub fn addrof_path_within_namespace<'e>( /// Performs member-access "projection" from a ConValue to a particular element
pub fn project_memberkind<'v>(
value: &'v mut ConValue,
kind: &MemberKind,
) -> IResult<&'v mut ConValue> {
match (value, kind) {
(ConValue::Struct(s), MemberKind::Struct(id)) => {
s.1.get_mut(id).ok_or(Error::NotDefined(*id))
}
(ConValue::TupleStruct(s), MemberKind::Tuple(Literal::Int(id))) => {
let len = s.1.len();
s.1.get_mut(*id as usize)
.ok_or(Error::OobIndex(*id as _, len))
}
(ConValue::Tuple(t), MemberKind::Tuple(Literal::Int(id))) => {
let len = t.len();
t.get_mut(*id as usize)
.ok_or(Error::OobIndex(*id as _, len))
}
_ => Err(Error::TypeError()),
}
}
/// Performs index "projection" from a ConValue to a particular element
pub fn project_index<'v>(
value: &'v mut ConValue,
index: &ConValue,
) -> IResult<&'v mut ConValue> {
match (value, index) {
(ConValue::Array(a), ConValue::Int(i)) => {
let a_len = a.len();
a.get_mut(*i as usize)
.ok_or(Error::OobIndex(*i as usize, a_len))
}
(ConValue::Slice(_, _), _) => Err(Error::TypeError()),
_ => Err(Error::NotIndexable()),
}
}
pub fn project_path_in_namespace<'e>(
env: &'e mut Namespace, env: &'e mut Namespace,
path: &[PathPart], path: &[PathPart],
) -> IResult<&'e mut Option<ConValue>> { ) -> IResult<&'e mut Option<ConValue>> {
match path { match path {
[] => Err(Error::NotAssignable), [] => Err(Error::NotAssignable()),
[PathPart::Ident(name)] => env.get_mut(name).ok_or(Error::NotDefined(*name)), [PathPart::Ident(name)] => env.get_mut(name).ok_or(Error::NotDefined(*name)),
[PathPart::Ident(name), rest @ ..] => { [PathPart::Ident(name), rest @ ..] => {
match env.get_mut(name).ok_or(Error::NotDefined(*name))? { match env.get_mut(name).ok_or(Error::NotDefined(*name))? {
Some(ConValue::Module(env)) => addrof_path_within_namespace(env, rest), Some(ConValue::Module(env)) => project_path_in_namespace(env, rest),
_ => Err(Error::NotIndexable), _ => Err(Error::NotIndexable()),
} }
} }
[PathPart::SelfKw, rest @ ..] => addrof_path_within_namespace(env, rest),
[PathPart::SelfTy, ..] => todo!("calc_address for `Self`"), [PathPart::SelfTy, ..] => todo!("calc_address for `Self`"),
[PathPart::SuperKw, ..] => todo!("calc_address for `super`"), [PathPart::SuperKw, ..] => todo!("calc_address for `super`"),
} }
@ -566,8 +571,6 @@ impl Interpret for Binary {
let (head, tail) = parts.borrow(); let (head, tail) = parts.borrow();
let head = head.interpret(env)?; let head = head.interpret(env)?;
// Short-circuiting ops
match kind { match kind {
BinaryKind::LogAnd => { BinaryKind::LogAnd => {
return if head.truthy()? { return if head.truthy()? {
@ -590,6 +593,7 @@ impl Interpret for Binary {
} }
_ => {} _ => {}
} }
let tail = tail.interpret(env)?; let tail = tail.interpret(env)?;
match kind { match kind {
BinaryKind::Lt => head.lt(&tail), BinaryKind::Lt => head.lt(&tail),
@ -598,8 +602,8 @@ impl Interpret for Binary {
BinaryKind::NotEq => head.neq(&tail), BinaryKind::NotEq => head.neq(&tail),
BinaryKind::GtEq => head.gt_eq(&tail), BinaryKind::GtEq => head.gt_eq(&tail),
BinaryKind::Gt => head.gt(&tail), BinaryKind::Gt => head.gt(&tail),
BinaryKind::RangeExc => head.range_exc(tail), BinaryKind::RangeExc => env.call("RangeExc".into(), &[head, tail]),
BinaryKind::RangeInc => head.range_inc(tail), BinaryKind::RangeInc => env.call("RangeInc".into(), &[head, tail]),
BinaryKind::BitAnd => head & tail, BinaryKind::BitAnd => head & tail,
BinaryKind::BitOr => head | tail, BinaryKind::BitOr => head | tail,
BinaryKind::BitXor => head ^ tail, BinaryKind::BitXor => head ^ tail,
@ -613,40 +617,10 @@ impl Interpret for Binary {
BinaryKind::Call => match tail { BinaryKind::Call => match tail {
ConValue::Empty => head.call(env, &[]), ConValue::Empty => head.call(env, &[]),
ConValue::Tuple(args) => head.call(env, &args), ConValue::Tuple(args) => head.call(env, &args),
_ => Err(Error::TypeError), _ => Err(Error::TypeError()),
}, },
_ => Ok(head), _ => Ok(head),
} }
// // Temporarily disabled, to avoid function dispatch overhead while I screw around
// // Not like it helped much in the first place!
// match kind {
// BinaryKind::Mul => env.call("mul", &[head, tail]),
// BinaryKind::Div => env.call("div", &[head, tail]),
// BinaryKind::Rem => env.call("rem", &[head, tail]),
// BinaryKind::Add => env.call("add", &[head, tail]),
// BinaryKind::Sub => env.call("sub", &[head, tail]),
// BinaryKind::Shl => env.call("shl", &[head, tail]),
// BinaryKind::Shr => env.call("shr", &[head, tail]),
// BinaryKind::BitAnd => env.call("and", &[head, tail]),
// BinaryKind::BitOr => env.call("or", &[head, tail]),
// BinaryKind::BitXor => env.call("xor", &[head, tail]),
// BinaryKind::RangeExc => env.call("range_exc", &[head, tail]),
// BinaryKind::RangeInc => env.call("range_inc", &[head, tail]),
// BinaryKind::Lt => env.call("lt", &[head, tail]),
// BinaryKind::LtEq => env.call("lt_eq", &[head, tail]),
// BinaryKind::Equal => env.call("eq", &[head, tail]),
// BinaryKind::NotEq => env.call("neq", &[head, tail]),
// BinaryKind::GtEq => env.call("gt_eq", &[head, tail]),
// BinaryKind::Gt => env.call("gt", &[head, tail]),
// BinaryKind::Dot => todo!("search within a type's namespace!"),
// BinaryKind::Call => match tail {
// ConValue::Empty => head.call(env, &[]),
// ConValue::Tuple(args) => head.call(env, &args),
// _ => Err(Error::TypeError),
// },
// _ => Ok(head),
// }
} }
} }
@ -656,8 +630,8 @@ impl Interpret for Unary {
match kind { match kind {
UnaryKind::Loop => loop { UnaryKind::Loop => loop {
match tail.interpret(env) { match tail.interpret(env) {
Err(Error::Break(value)) => return Ok(value), Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value),
Err(Error::Continue) => continue, Err(Error { kind: ErrorKind::Continue, .. }) => continue,
e => e?, e => e?,
}; };
}, },
@ -673,6 +647,14 @@ impl Interpret for Unary {
let operand = tail.interpret(env)?; let operand = tail.interpret(env)?;
env.call("not".into(), &[operand]) env.call("not".into(), &[operand])
} }
UnaryKind::RangeExc => {
let operand = tail.interpret(env)?;
env.call("RangeTo".into(), &[operand])
}
UnaryKind::RangeInc => {
let operand = tail.interpret(env)?;
env.call("RangeToInc".into(), &[operand])
}
UnaryKind::At => { UnaryKind::At => {
let operand = tail.interpret(env)?; let operand = tail.interpret(env)?;
println!("{operand}"); println!("{operand}");
@ -683,17 +665,24 @@ impl Interpret for Unary {
} }
} }
fn cast(value: ConValue, ty: Sym) -> IResult<ConValue> { fn cast(env: &Environment, value: ConValue, ty: Sym) -> IResult<ConValue> {
let value = match value { let value = match value {
ConValue::Empty => 0, ConValue::Empty => 0,
ConValue::Int(i) => i as _, ConValue::Int(i) => i as _,
ConValue::Bool(b) => b as _, ConValue::Bool(b) => b as _,
ConValue::Char(c) => c as _, ConValue::Char(c) => c as _,
ConValue::Ref(v) => return cast((*v).clone(), ty), ConValue::Ref(v) => {
return cast(
env,
env.get_id(v).cloned().ok_or(Error::StackUnderflow())?,
ty,
);
}
// TODO: This, better // TODO: This, better
ConValue::Float(_) if ty.starts_with('f') => return Ok(value), ConValue::Float(_) if ty.starts_with('f') => return Ok(value),
ConValue::Float(f) => f as _, ConValue::Float(f) => f as _,
_ => Err(Error::TypeError)?, _ if (*ty).eq("str") => return Ok(ConValue::String(format!("{value}").into())),
_ => Err(Error::TypeError())?,
}; };
Ok(match &*ty { Ok(match &*ty {
"u8" => ConValue::Int(value as u8 as _), "u8" => ConValue::Int(value as u8 as _),
@ -720,11 +709,11 @@ impl Interpret for Cast {
return Ok(ConValue::Empty); return Ok(ConValue::Empty);
}; };
let TyKind::Path(Path { absolute: false, parts }) = &ty.kind else { let TyKind::Path(Path { absolute: false, parts }) = &ty.kind else {
Err(Error::TypeError)? Err(Error::TypeError())?
}; };
match parts.as_slice() { match parts.as_slice() {
[PathPart::Ident(ty)] => cast(value, *ty), [PathPart::Ident(ty)] => cast(env, value, *ty),
_ => Err(Error::TypeError), _ => Err(Error::TypeError()),
} }
} }
} }
@ -732,16 +721,22 @@ impl Interpret for Cast {
impl Interpret for Member { impl Interpret for Member {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Member { head, kind } = self; let Member { head, kind } = self;
let head = head.interpret(env)?; // Attempt member access projection (fast path)
match (head, kind) { if let Ok(member) = assignment::addrof_member(env, self) {
(ConValue::Tuple(v), MemberKind::Tuple(Literal::Int(id))) => v return Ok(member.clone());
.get(*id as usize)
.cloned()
.ok_or(Error::OobIndex(*id as usize, v.len())),
(ConValue::Struct(parts), MemberKind::Struct(name)) => {
parts.1.get(name).cloned().ok_or(Error::NotDefined(*name))
} }
(ConValue::Struct(parts), MemberKind::Call(name, args)) => { // Evaluate if this can be Self'd
let value = match (&head.kind, kind) {
(ExprKind::Path(p), MemberKind::Call(..)) => {
p.as_sym().map(|name| Ok(ConValue::Ref(env.id_of(name)?))) // "borrow" it
}
_ => None,
};
// Perform alternate member access
match (value.unwrap_or_else(|| head.interpret(env))?, kind) {
(ConValue::Struct(parts), MemberKind::Call(name, args))
if parts.1.contains_key(name) =>
{
let mut values = vec![]; let mut values = vec![];
for arg in &args.exprs { for arg in &args.exprs {
values.push(arg.interpret(env)?); values.push(arg.interpret(env)?);
@ -759,7 +754,7 @@ impl Interpret for Member {
} }
env.call(*name, &values) env.call(*name, &values)
} }
_ => Err(Error::TypeError)?, (mut head, kind) => assignment::project_memberkind(&mut head, kind).cloned(),
} }
} }
} }
@ -768,7 +763,7 @@ impl Interpret for Index {
let Self { head, indices } = self; let Self { head, indices } = self;
let mut head = head.interpret(env)?; let mut head = head.interpret(env)?;
for index in indices { for index in indices {
head = head.index(&index.interpret(env)?)?; head = head.index(&index.interpret(env)?, env)?;
} }
Ok(head) Ok(head)
} }
@ -778,9 +773,10 @@ impl Interpret for Structor {
let Self { to: Path { absolute: _, parts }, init } = self; let Self { to: Path { absolute: _, parts }, init } = self;
use std::collections::HashMap; use std::collections::HashMap;
// Look up struct/enum-struct definition
let name = match parts.last() { let name = match parts.last() {
Some(PathPart::Ident(name)) => *name, Some(PathPart::Ident(name)) => *name,
Some(PathPart::SelfKw) => "self".into(),
Some(PathPart::SelfTy) => "Self".into(), Some(PathPart::SelfTy) => "Self".into(),
Some(PathPart::SuperKw) => "super".into(), Some(PathPart::SuperKw) => "super".into(),
None => "".into(), None => "".into(),
@ -832,21 +828,24 @@ impl Interpret for Array {
impl Interpret for ArrayRep { impl Interpret for ArrayRep {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { value, repeat } = self; let Self { value, repeat } = self;
let repeat = match repeat.interpret(env)? {
ConValue::Int(v) => v,
_ => Err(Error::TypeError)?,
};
let value = value.interpret(env)?; let value = value.interpret(env)?;
Ok(ConValue::Array(vec![value; repeat as usize].into())) Ok(ConValue::Array(vec![value; *repeat].into()))
} }
} }
impl Interpret for AddrOf { impl Interpret for AddrOf {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { mutable: _, expr } = self; let Self { mutable: _, expr } = self;
match expr.as_ref() { match &expr.kind {
ExprKind::Index(_) => todo!("AddrOf array index"), ExprKind::Index(_) => todo!("AddrOf array index"),
ExprKind::Path(_) => todo!("Path traversal in addrof"), ExprKind::Path(Path { parts, .. }) => match parts.as_slice() {
_ => Ok(ConValue::Ref(Rc::new(expr.interpret(env)?))), [PathPart::Ident(name)] => Ok(ConValue::Ref(env.id_of(*name)?)),
_ => todo!("Path traversal in AddrOf(\"{self}\")"),
},
_ => {
let value = expr.interpret(env)?;
let temp = env.stack_alloc(value)?;
Ok(ConValue::Ref(env::Place::Local(temp)))
}
} }
} }
} }
@ -887,8 +886,8 @@ impl Interpret for While {
loop { loop {
if cond.interpret(env)?.truthy()? { if cond.interpret(env)?.truthy()? {
match pass.interpret(env) { match pass.interpret(env) {
Err(Error::Break(value)) => return Ok(value), Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value),
Err(Error::Continue) => continue, Err(Error { kind: ErrorKind::Continue, .. }) => continue,
e => e?, e => e?,
}; };
} else { } else {
@ -909,24 +908,39 @@ impl Interpret for If {
} }
impl Interpret for For { impl Interpret for For {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { bind: name, cond, pass, fail } = self; let Self { bind, cond, pass, fail } = self;
let cond = cond.interpret(env)?; let cond = cond.interpret(env)?;
// TODO: A better iterator model // TODO: A better iterator model
let mut bounds: Box<dyn Iterator<Item = ConValue>> = match &cond { let mut bounds: Box<dyn Iterator<Item = ConValue>> = match &cond {
&ConValue::RangeExc(a, b) => Box::new((a..b).map(ConValue::Int)), ConValue::TupleStruct(inner) => match &**inner {
&ConValue::RangeInc(a, b) => Box::new((a..=b).map(ConValue::Int)), ("RangeExc", values) => match **values {
[ConValue::Int(from), ConValue::Int(to)] => {
Box::new((from..to).map(ConValue::Int))
}
_ => Err(Error::NotIterable())?,
},
("RangeInc", values) => match **values {
[ConValue::Int(from), ConValue::Int(to)] => {
Box::new((from..=to).map(ConValue::Int))
}
_ => Err(Error::NotIterable())?,
},
_ => Err(Error::NotIterable())?,
},
ConValue::Array(a) => Box::new(a.iter().cloned()), ConValue::Array(a) => Box::new(a.iter().cloned()),
ConValue::String(s) => Box::new(s.chars().map(ConValue::Char)), ConValue::String(s) => Box::new(s.chars().map(ConValue::Char)),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError())?,
}; };
loop { loop {
let mut env = env.frame("loop variable"); let mut env = env.frame("loop variable");
if let Some(loop_var) = bounds.next() { if let Some(value) = bounds.next() {
env.insert(*name, Some(loop_var)); for (name, value) in pattern::substitution(bind, value)? {
env.insert(*name, Some(value));
}
match pass.interpret(&mut env) { match pass.interpret(&mut env) {
Err(Error::Break(value)) => return Ok(value), Err(Error { kind: ErrorKind::Break(value), .. }) => break Ok(value),
Err(Error::Continue) => continue, Err(Error { kind: ErrorKind::Continue, .. }) => continue,
result => result?, e => e?,
}; };
} else { } else {
break fail.interpret(&mut env); break fail.interpret(&mut env);

View File

@ -5,7 +5,7 @@
use cl_ast::Sym; use cl_ast::Sym;
use convalue::ConValue; use convalue::ConValue;
use env::Environment; use env::Environment;
use error::{Error, IResult}; use error::{Error, ErrorKind, IResult};
use interpret::Interpret; use interpret::Interpret;
/// Callable types can be called from within a Conlang program /// Callable types can be called from within a Conlang program
@ -23,8 +23,12 @@ pub mod interpret;
pub mod function; pub mod function;
pub mod closure;
pub mod builtin; pub mod builtin;
pub mod pattern;
pub mod env; pub mod env;
pub mod error; pub mod error;

View File

@ -0,0 +1,258 @@
//! 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,
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>(
sub: &mut HashMap<&'pat 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(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(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())))),
}
}
/// Appends a substitution to the provided table
pub fn append_sub<'pat>(
sub: &mut HashMap<&'pat Sym, ConValue>,
pat: &'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::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::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)) => {
todo!("Dereference <{r}> in pattern matching {pat}")
}
(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::String(b),
) => (a.as_str() <= b.to_ref() && b.to_ref() < 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::String(b),
) => (a.as_str() <= b.to_ref() && b.to_ref() <= c.as_str())
.then_some(())
.ok_or(Error::NotAssignable()),
_ => Err(Error::NotAssignable()),
},
(Pattern::Array(patterns), ConValue::Array(values)) => {
match rest_binding(sub, patterns, values.into_vec().into())? {
Some((pattern, values)) => {
append_sub(sub, pattern, ConValue::Array(Vec::from(values).into()))
}
_ => Ok(()),
}
}
(Pattern::Tuple(patterns), ConValue::Empty) if patterns.is_empty() => Ok(()),
(Pattern::Tuple(patterns), ConValue::Tuple(values)) => {
match rest_binding(sub, patterns, values.into_vec().into())? {
Some((pattern, values)) => {
append_sub(sub, pattern, ConValue::Tuple(Vec::from(values).into()))
}
_ => Ok(()),
}
}
(Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(parts)) => {
let (name, values) = *parts;
if !path.ends_with(name) {
Err(Error::TypeError())?
}
match rest_binding(sub, patterns, values.into_vec().into())? {
Some((pattern, values)) => {
append_sub(sub, pattern, ConValue::Tuple(Vec::from(values).into()))
}
_ => Ok(()),
}
}
(Pattern::Struct(path, patterns), ConValue::Struct(parts)) => {
let (name, mut values) = *parts;
if !path.ends_with(&name) {
Err(Error::TypeError())?
}
for (name, pat) in patterns {
let value = values.remove(name).ok_or(Error::TypeError())?;
match pat {
Some(pat) => append_sub(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(pat: &Pattern, value: ConValue) -> IResult<HashMap<&Sym, ConValue>> {
let mut sub = HashMap::new();
append_sub(&mut sub, pat, value)?;
Ok(sub)
}

View File

@ -1,5 +1,5 @@
#![allow(unused_imports)] #![allow(unused_imports)]
use crate::{convalue::ConValue, env::Environment, Interpret}; use crate::{Interpret, convalue::ConValue, env::Environment};
use cl_ast::*; use cl_ast::*;
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
@ -71,7 +71,7 @@ mod macros {
/// ///
/// Returns a `Result<`[`Block`]`, ParseError>` /// Returns a `Result<`[`Block`]`, ParseError>`
pub macro block($($t:tt)*) { pub macro block($($t:tt)*) {
Block::parse(&mut Parser::new(Lexer::new(stringify!({ $($t)* })))) Block::parse(&mut Parser::new("test", Lexer::new(stringify!({ $($t)* }))))
} }
/// Evaluates a block of code in the given environment /// Evaluates a block of code in the given environment
@ -530,6 +530,25 @@ mod control_flow {
env_eq!(env.evaluated, "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] #[test]
fn match_evaluates_in_order() { fn match_evaluates_in_order() {
let mut env = Default::default(); let mut env = Default::default();

View File

@ -1,5 +1,6 @@
use super::*; use super::*;
use cl_ast::{Expr, Sym};
use cl_lexer::error::{Error as LexError, Reason}; use cl_lexer::error::{Error as LexError, Reason};
use std::fmt::Display; use std::fmt::Display;
pub type PResult<T> = Result<T, Error>; pub type PResult<T> = Result<T, Error>;
@ -7,6 +8,7 @@ pub type PResult<T> = Result<T, Error>;
/// Contains information about [Parser] errors /// Contains information about [Parser] errors
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error { pub struct Error {
pub in_file: Sym,
pub reason: ErrorKind, pub reason: ErrorKind,
pub while_parsing: Parsing, pub while_parsing: Parsing,
pub loc: Loc, pub loc: Loc,
@ -29,6 +31,7 @@ pub enum ErrorKind {
ExpectedParsing { ExpectedParsing {
want: Parsing, want: Parsing,
}, },
InvalidPattern(Box<Expr>),
/// Indicates unfinished code /// Indicates unfinished code
Todo(&'static str), Todo(&'static str),
} }
@ -57,6 +60,7 @@ pub enum Parsing {
Item, Item,
ItemKind, ItemKind,
Generics,
Alias, Alias,
Const, Const,
Static, Static,
@ -93,6 +97,7 @@ pub enum Parsing {
Expr, Expr,
ExprKind, ExprKind,
Closure,
Assign, Assign,
AssignKind, AssignKind,
Binary, Binary,
@ -127,13 +132,18 @@ pub enum Parsing {
impl Display for Error { impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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 { match reason {
// TODO entries are debug-printed // 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 // lexical errors print their own higher-resolution loc info
ErrorKind::Lexical(e) => write!(f, "{e} (while parsing {while_parsing})"), 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}")
}
} }
} }
} }
@ -148,6 +158,7 @@ impl Display for ErrorKind {
ErrorKind::Unexpected(t) => write!(f, "Encountered unexpected token `{t}`"), ErrorKind::Unexpected(t) => write!(f, "Encountered unexpected token `{t}`"),
ErrorKind::ExpectedToken { want: e, got: g } => write!(f, "Expected `{e}`, got `{g}`"), ErrorKind::ExpectedToken { want: e, got: g } => write!(f, "Expected `{e}`, got `{g}`"),
ErrorKind::ExpectedParsing { want } => write!(f, "Expected {want}"), ErrorKind::ExpectedParsing { want } => write!(f, "Expected {want}"),
ErrorKind::InvalidPattern(got) => write!(f, "Got invalid `{got}`"),
ErrorKind::Todo(unfinished) => write!(f, "TODO: {unfinished}"), ErrorKind::Todo(unfinished) => write!(f, "TODO: {unfinished}"),
} }
} }
@ -167,6 +178,7 @@ impl Display for Parsing {
Parsing::MetaKind => "an attribute's arguments", Parsing::MetaKind => "an attribute's arguments",
Parsing::Item => "an item", Parsing::Item => "an item",
Parsing::ItemKind => "an item", Parsing::ItemKind => "an item",
Parsing::Generics => "a list of type arguments",
Parsing::Alias => "a type alias", Parsing::Alias => "a type alias",
Parsing::Const => "a const item", Parsing::Const => "a const item",
Parsing::Static => "a static variable", Parsing::Static => "a static variable",
@ -203,6 +215,7 @@ impl Display for Parsing {
Parsing::Expr => "an expression", Parsing::Expr => "an expression",
Parsing::ExprKind => "an expression", Parsing::ExprKind => "an expression",
Parsing::Closure => "an anonymous function",
Parsing::Assign => "an assignment", Parsing::Assign => "an assignment",
Parsing::AssignKind => "an assignment operator", Parsing::AssignKind => "an assignment operator",
Parsing::Binary => "a binary expression", Parsing::Binary => "a binary expression",

View File

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

View File

@ -13,6 +13,8 @@ mod prec;
/// Parses a sequence of [Tokens](Token) into an [AST](cl_ast) /// Parses a sequence of [Tokens](Token) into an [AST](cl_ast)
#[derive(Debug)] #[derive(Debug)]
pub struct Parser<'t> { pub struct Parser<'t> {
/// Name of the file being parsed
file: Sym,
/// Lazy tokenizer /// Lazy tokenizer
lexer: Lexer<'t>, lexer: Lexer<'t>,
/// Look-ahead buffer /// Look-ahead buffer
@ -23,8 +25,8 @@ pub struct Parser<'t> {
/// Basic parser functionality /// Basic parser functionality
impl<'t> Parser<'t> { impl<'t> Parser<'t> {
pub fn new(lexer: Lexer<'t>) -> Self { pub fn new(filename: impl AsRef<str>, lexer: Lexer<'t>) -> Self {
Self { loc: Loc::from(&lexer), lexer, next: None } Self { file: filename.as_ref().into(), loc: Loc::from(&lexer), lexer, next: None }
} }
/// Gets the location of the last consumed [Token] /// Gets the location of the last consumed [Token]
@ -40,7 +42,7 @@ impl<'t> Parser<'t> {
/// Constructs an [Error] /// Constructs an [Error]
pub fn error(&self, reason: ErrorKind, while_parsing: Parsing) -> Error { pub fn error(&self, reason: ErrorKind, while_parsing: Parsing) -> Error {
Error { reason, while_parsing, loc: self.loc } Error { in_file: self.file, reason, while_parsing, loc: self.loc }
} }
/// Internal impl of peek and consume /// Internal impl of peek and consume
@ -186,11 +188,7 @@ macro literal_like() {
/// Expands to a pattern which matches path-like [TokenKinds](TokenKind) /// Expands to a pattern which matches path-like [TokenKinds](TokenKind)
macro path_like() { macro path_like() {
TokenKind::Super TokenKind::Super | TokenKind::SelfTy | TokenKind::Identifier | TokenKind::ColonColon
| TokenKind::SelfKw
| TokenKind::SelfTy
| TokenKind::Identifier
| TokenKind::ColonColon
} }
pub trait Parse<'t>: Sized { pub trait Parse<'t>: Sized {
@ -260,9 +258,10 @@ impl Parse<'_> for File {
Ok(_) => true, Ok(_) => true,
Err(e) => Err(e)?, Err(e) => Err(e)?,
} { } {
items.push(Item::parse(p)?) items.push(Item::parse(p)?);
let _ = p.match_type(TokenKind::Semi, Parsing::File);
} }
Ok(File { items }) Ok(File { name: p.file.to_ref(), items })
} }
} }
@ -292,17 +291,13 @@ impl Parse<'_> for MetaKind {
/// Parses data associated with a [Meta] attribute /// Parses data associated with a [Meta] attribute
fn parse(p: &mut Parser) -> PResult<MetaKind> { fn parse(p: &mut Parser) -> PResult<MetaKind> {
const P: Parsing = Parsing::Meta; const P: Parsing = Parsing::Meta;
let lit_tuple = delim( let tuple = delim(sep(Parse::parse, TokenKind::Comma, PARENS.1, P), PARENS, P);
sep(Literal::parse, TokenKind::Comma, PARENS.1, P),
PARENS,
P,
);
Ok(match p.peek_kind(P) { Ok(match p.peek_kind(P) {
Ok(TokenKind::Eq) => { Ok(TokenKind::Eq) => {
p.consume_peeked(); p.consume_peeked();
MetaKind::Equals(Literal::parse(p)?) MetaKind::Equals(p.parse()?)
} }
Ok(TokenKind::LParen) => MetaKind::Func(lit_tuple(p)?), Ok(TokenKind::LParen) => MetaKind::Func(tuple(p)?),
_ => MetaKind::Plain, _ => MetaKind::Plain,
}) })
} }
@ -320,7 +315,7 @@ impl Parse<'_> for Item {
attrs: Attrs::parse(p)?, attrs: Attrs::parse(p)?,
vis: Visibility::parse(p)?, vis: Visibility::parse(p)?,
kind: ItemKind::parse(p)?, kind: ItemKind::parse(p)?,
extents: Span(start, p.loc()), span: Span(start, p.loc()),
}) })
} }
} }
@ -331,20 +326,35 @@ impl Parse<'_> for ItemKind {
/// See also: [Item::parse] /// See also: [Item::parse]
fn parse(p: &mut Parser) -> PResult<Self> { fn parse(p: &mut Parser) -> PResult<Self> {
Ok(match p.peek_kind(Parsing::Item)? { Ok(match p.peek_kind(Parsing::Item)? {
TokenKind::Type => Alias::parse(p)?.into(), TokenKind::Type => ItemKind::Alias(p.parse()?),
TokenKind::Const => Const::parse(p)?.into(), TokenKind::Const => ItemKind::Const(p.parse()?),
TokenKind::Static => Static::parse(p)?.into(), TokenKind::Static => ItemKind::Static(p.parse()?),
TokenKind::Mod => Module::parse(p)?.into(), TokenKind::Mod => ItemKind::Module(p.parse()?),
TokenKind::Fn => Function::parse(p)?.into(), TokenKind::Fn => ItemKind::Function(p.parse()?),
TokenKind::Struct => Struct::parse(p)?.into(), TokenKind::Struct => ItemKind::Struct(p.parse()?),
TokenKind::Enum => Enum::parse(p)?.into(), TokenKind::Enum => ItemKind::Enum(p.parse()?),
TokenKind::Impl => Impl::parse(p)?.into(), TokenKind::Impl => ItemKind::Impl(p.parse()?),
TokenKind::Use => Use::parse(p)?.into(), TokenKind::Use => ItemKind::Use(p.parse()?),
t => Err(p.error(Unexpected(t), Parsing::Item))?, t => Err(p.error(Unexpected(t), Parsing::Item))?,
}) })
} }
} }
impl Parse<'_> for Generics {
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
const P: Parsing = Parsing::Generics;
let vars = match p.peek_kind(P)? {
TokenKind::Lt => delim(
sep(Sym::parse, TokenKind::Comma, TokenKind::Gt, P),
(TokenKind::Lt, TokenKind::Gt),
P,
)(p)?,
_ => Vec::new(),
};
Ok(Generics { vars })
}
}
impl Parse<'_> for Alias { impl Parse<'_> for Alias {
/// Parses a [`type` alias](Alias) /// Parses a [`type` alias](Alias)
fn parse(p: &mut Parser<'_>) -> PResult<Self> { fn parse(p: &mut Parser<'_>) -> PResult<Self> {
@ -352,9 +362,9 @@ impl Parse<'_> for Alias {
p.consume_peeked(); p.consume_peeked();
let out = Ok(Alias { let out = Ok(Alias {
to: Sym::parse(p)?, name: Sym::parse(p)?,
from: if p.match_type(TokenKind::Eq, P).is_ok() { from: if p.match_type(TokenKind::Eq, P).is_ok() {
Some(Ty::parse(p)?.into()) Some(p.parse()?)
} else { } else {
None None
}, },
@ -378,7 +388,7 @@ impl Parse<'_> for Const {
}, },
init: { init: {
p.match_type(TokenKind::Eq, P)?; p.match_type(TokenKind::Eq, P)?;
Expr::parse(p)?.into() p.parse()?
}, },
}); });
p.match_type(TokenKind::Semi, P)?; p.match_type(TokenKind::Semi, P)?;
@ -397,11 +407,11 @@ impl Parse<'_> for Static {
name: Sym::parse(p)?, name: Sym::parse(p)?,
ty: { ty: {
p.match_type(TokenKind::Colon, P)?; p.match_type(TokenKind::Colon, P)?;
Ty::parse(p)?.into() p.parse()?
}, },
init: { init: {
p.match_type(TokenKind::Eq, P)?; p.match_type(TokenKind::Eq, P)?;
Expr::parse(p)?.into() p.parse()?
}, },
}); });
p.match_type(TokenKind::Semi, P)?; p.match_type(TokenKind::Semi, P)?;
@ -414,24 +424,22 @@ impl Parse<'_> for Module {
fn parse(p: &mut Parser<'_>) -> PResult<Self> { fn parse(p: &mut Parser<'_>) -> PResult<Self> {
p.consume_peeked(); p.consume_peeked();
Ok(Module { name: Sym::parse(p)?, kind: ModuleKind::parse(p)? }) Ok(Module {
} name: Sym::parse(p)?,
} file: {
impl Parse<'_> for ModuleKind {
/// Parses the item list associated with a [Module], if present
fn parse(p: &mut Parser) -> PResult<ModuleKind> {
const P: Parsing = Parsing::ModuleKind; const P: Parsing = Parsing::ModuleKind;
let inline = delim(Parse::parse, CURLIES, P); let inline = delim(Parse::parse, CURLIES, P);
match p.peek_kind(P)? { match p.peek_kind(P)? {
TokenKind::LCurly => Ok(ModuleKind::Inline(inline(p)?)), TokenKind::LCurly => Some(inline(p)?),
TokenKind::Semi => { TokenKind::Semi => {
p.consume_peeked(); p.consume_peeked();
Ok(ModuleKind::Outline) None
} }
got => Err(p.error(ExpectedToken { want: TokenKind::Semi, got }, P)), got => Err(p.error(ExpectedToken { want: TokenKind::Semi, got }, P))?,
} }
},
})
} }
} }
@ -442,6 +450,7 @@ impl Parse<'_> for Function {
p.consume_peeked(); p.consume_peeked();
let name = Sym::parse(p)?; let name = Sym::parse(p)?;
let gens = Generics::parse(p)?;
let (bind, types) = delim(FnSig::parse, PARENS, P)(p)?; let (bind, types) = delim(FnSig::parse, PARENS, P)(p)?;
let sign = TyFn { let sign = TyFn {
args: Box::new(match types.len() { args: Box::new(match types.len() {
@ -456,24 +465,24 @@ impl Parse<'_> for Function {
}; };
Ok(Function { Ok(Function {
name, name,
gens,
sign, sign,
bind, bind,
body: match p.peek_kind(P)? { body: match p.peek_kind(P)? {
TokenKind::LCurly => Some(Block::parse(p)?),
TokenKind::Semi => { TokenKind::Semi => {
p.consume_peeked(); p.consume_peeked();
None None
} }
t => Err(p.error(Unexpected(t), P))?, _ => Some(Expr::parse(p)?),
}, },
}) })
} }
} }
type FnSig = (Vec<Param>, Vec<TyKind>); type FnSig = (Pattern, Vec<TyKind>);
impl Parse<'_> for FnSig { impl Parse<'_> for FnSig {
/// Parses the [parameters](Param) associated with a Function /// Parses the parameter list of a Function
fn parse(p: &mut Parser) -> PResult<FnSig> { fn parse(p: &mut Parser) -> PResult<FnSig> {
const P: Parsing = Parsing::Function; const P: Parsing = Parsing::Function;
let (mut params, mut types) = (vec![], vec![]); let (mut params, mut types) = (vec![], vec![]);
@ -485,20 +494,21 @@ impl Parse<'_> for FnSig {
break; break;
} }
} }
Ok((params, types)) Ok((Pattern::Tuple(params), types))
} }
} }
type TypedParam = (Param, TyKind); type TypedParam = (Pattern, TyKind);
impl Parse<'_> for TypedParam { impl Parse<'_> for TypedParam {
/// Parses a single function [parameter](Param) /// Parses a single function parameter
fn parse(p: &mut Parser) -> PResult<(Param, TyKind)> { fn parse(p: &mut Parser) -> PResult<(Pattern, TyKind)> {
Ok(( Ok((
Param { mutability: Mutability::parse(p)?, name: Sym::parse(p)? }, Pattern::parse(p)?,
{ if p.match_type(TokenKind::Colon, Parsing::Param).is_ok() {
p.match_type(TokenKind::Colon, Parsing::Param)?;
TyKind::parse(p)? TyKind::parse(p)?
} else {
TyKind::Infer
}, },
)) ))
} }
@ -508,7 +518,7 @@ impl Parse<'_> for Struct {
/// Parses a [`struct` definition](Struct) /// Parses a [`struct` definition](Struct)
fn parse(p: &mut Parser) -> PResult<Struct> { fn parse(p: &mut Parser) -> PResult<Struct> {
p.match_type(TokenKind::Struct, Parsing::Struct)?; p.match_type(TokenKind::Struct, Parsing::Struct)?;
Ok(Struct { name: Sym::parse(p)?, kind: StructKind::parse(p)? }) Ok(Struct { name: Sym::parse(p)?, gens: Generics::parse(p)?, kind: StructKind::parse(p)? })
} }
} }
@ -516,22 +526,19 @@ impl Parse<'_> for StructKind {
/// Parses the various [kinds of Struct](StructKind) /// Parses the various [kinds of Struct](StructKind)
fn parse(p: &mut Parser<'_>) -> PResult<Self> { fn parse(p: &mut Parser<'_>) -> PResult<Self> {
const P: Parsing = Parsing::StructKind; const P: Parsing = Parsing::StructKind;
Ok(match p.peek_kind(P)? { Ok(match p.peek_kind(P) {
TokenKind::LParen => StructKind::Tuple(delim( Ok(TokenKind::LParen) => StructKind::Tuple(delim(
sep(Ty::parse, TokenKind::Comma, PARENS.1, P), sep(Ty::parse, TokenKind::Comma, PARENS.1, P),
PARENS, PARENS,
P, P,
)(p)?), )(p)?),
TokenKind::LCurly => StructKind::Struct(delim( Ok(TokenKind::LCurly) => StructKind::Struct(delim(
sep(StructMember::parse, TokenKind::Comma, CURLIES.1, P), sep(StructMember::parse, TokenKind::Comma, CURLIES.1, P),
CURLIES, CURLIES,
P, P,
)(p)?), )(p)?),
TokenKind::Semi => { Ok(_) | Err(Error { reason: ErrorKind::EndOfInput, .. }) => StructKind::Empty,
p.consume_peeked(); Err(e) => Err(e)?,
StructKind::Empty
}
got => Err(p.error(ExpectedToken { want: TokenKind::Semi, got }, P))?,
}) })
} }
} }
@ -555,26 +562,20 @@ impl Parse<'_> for Enum {
/// Parses an [`enum`](Enum) definition /// Parses an [`enum`](Enum) definition
fn parse(p: &mut Parser) -> PResult<Enum> { fn parse(p: &mut Parser) -> PResult<Enum> {
p.match_type(TokenKind::Enum, Parsing::Enum)?; p.match_type(TokenKind::Enum, Parsing::Enum)?;
Ok(Enum {
Ok(Enum { name: Sym::parse(p)?, kind: EnumKind::parse(p)? }) name: Sym::parse(p)?,
} gens: Generics::parse(p)?,
} variants: {
impl Parse<'_> for EnumKind {
/// Parses the various [kinds of Enum](EnumKind)
fn parse(p: &mut Parser<'_>) -> PResult<EnumKind> {
const P: Parsing = Parsing::EnumKind; const P: Parsing = Parsing::EnumKind;
Ok(match p.peek_kind(P)? { match p.peek_kind(P)? {
TokenKind::LCurly => EnumKind::Variants(delim( TokenKind::LCurly => delim(
sep(Variant::parse, TokenKind::Comma, TokenKind::RCurly, P), sep(Variant::parse, TokenKind::Comma, TokenKind::RCurly, P),
CURLIES, CURLIES,
P, P,
)(p)?), )(p)?,
TokenKind::Semi => {
p.consume_peeked();
EnumKind::NoVariants
}
t => Err(p.error(Unexpected(t), P))?, t => Err(p.error(Unexpected(t), P))?,
}
},
}) })
} }
} }
@ -582,39 +583,19 @@ impl Parse<'_> for EnumKind {
impl Parse<'_> for Variant { impl Parse<'_> for Variant {
/// Parses an [`enum`](Enum) [Variant] /// Parses an [`enum`](Enum) [Variant]
fn parse(p: &mut Parser) -> PResult<Variant> { fn parse(p: &mut Parser) -> PResult<Variant> {
Ok(Variant { name: Sym::parse(p)?, kind: VariantKind::parse(p)? }) let name = Sym::parse(p)?;
} let kind;
let body;
if p.match_type(TokenKind::Eq, Parsing::Variant).is_ok() {
kind = StructKind::Empty;
body = Some(Box::new(Expr::parse(p)?));
} else {
kind = StructKind::parse(p)?;
body = None;
} }
impl Parse<'_> for VariantKind { Ok(Variant { name, kind, body })
/// Parses the various [kinds of Enum Variant](VariantKind)
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
const P: Parsing = Parsing::VariantKind;
Ok(match p.peek_kind(P)? {
TokenKind::Eq => {
p.match_type(TokenKind::Eq, P)?;
let tok = p.match_type(TokenKind::Literal, P)?;
VariantKind::CLike(match tok.data() {
TokenData::Integer(i) => *i,
_ => panic!("Expected token data for {tok:?} while parsing {P}"),
})
}
TokenKind::LCurly => VariantKind::Struct(delim(
sep(StructMember::parse, TokenKind::Comma, TokenKind::RCurly, P),
CURLIES,
P,
)(p)?),
TokenKind::LParen => {
let tup = Ty::parse(p)?;
if !matches!(tup.kind, TyKind::Tuple(_) | TyKind::Empty) {
Err(p.error(ErrorKind::ExpectedParsing { want: Parsing::TyTuple }, P))?
}
VariantKind::Tuple(tup)
}
_ => VariantKind::Plain,
})
} }
} }
@ -639,9 +620,10 @@ impl Parse<'_> for ImplKind {
Ok(ImplKind::Trait { impl_trait, for_type: Ty::parse(p)?.into() }) Ok(ImplKind::Trait { impl_trait, for_type: Ty::parse(p)?.into() })
} else { } else {
Err(Error { Err(Error {
in_file: p.file,
reason: ExpectedParsing { want: Parsing::Path }, reason: ExpectedParsing { want: Parsing::Path },
while_parsing: P, while_parsing: P,
loc: target.extents.head, loc: target.span.head,
})? })?
} }
} }
@ -673,7 +655,7 @@ impl Parse<'_> for UseTree {
CURLIES, CURLIES,
P, P,
)(p)?), )(p)?),
TokenKind::SelfKw | TokenKind::Super | TokenKind::Identifier => { TokenKind::Super | TokenKind::Identifier => {
let name = PathPart::parse(p)?; let name = PathPart::parse(p)?;
if p.match_type(TokenKind::ColonColon, P).is_ok() { if p.match_type(TokenKind::ColonColon, P).is_ok() {
UseTree::Path(name, Box::new(UseTree::parse(p)?)) UseTree::Path(name, Box::new(UseTree::parse(p)?))
@ -701,7 +683,7 @@ impl Parse<'_> for Ty {
/// See also: [TyKind::parse] /// See also: [TyKind::parse]
fn parse(p: &mut Parser<'_>) -> PResult<Self> { fn parse(p: &mut Parser<'_>) -> PResult<Self> {
let start = p.loc(); let start = p.loc();
Ok(Ty { kind: TyKind::parse(p)?, extents: Span(start, p.loc()) }) Ok(Ty { kind: TyKind::parse(p)?, span: Span(start, p.loc()) })
} }
} }
@ -747,7 +729,14 @@ impl Parse<'_> for TyKind {
} }
} }
TokenKind::Fn => TyFn::parse(p)?.into(), TokenKind::Fn => TyFn::parse(p)?.into(),
path_like!() => Path::parse(p)?.into(), path_like!() => {
let path = Path::parse(p)?;
if path.is_sinkhole() {
TyKind::Infer
} else {
TyKind::Path(path)
}
}
t => Err(p.error(Unexpected(t), P))?, t => Err(p.error(Unexpected(t), P))?,
}; };
@ -778,7 +767,7 @@ impl Parse<'_> for TyRef {
} }
p.consume_peeked(); p.consume_peeked();
} }
Ok(TyRef { count, mutable: Mutability::parse(p)?, to: Path::parse(p)? }) Ok(TyRef { count, mutable: p.parse()?, to: p.parse()? })
} }
} }
@ -841,7 +830,6 @@ impl Parse<'_> for PathPart {
const P: Parsing = Parsing::PathPart; const P: Parsing = Parsing::PathPart;
let out = match p.peek_kind(P)? { let out = match p.peek_kind(P)? {
TokenKind::Super => PathPart::SuperKw, TokenKind::Super => PathPart::SuperKw,
TokenKind::SelfKw => PathPart::SelfKw,
TokenKind::SelfTy => PathPart::SelfTy, TokenKind::SelfTy => PathPart::SelfTy,
TokenKind::Identifier => PathPart::Ident(Sym::parse(p)?), TokenKind::Identifier => PathPart::Ident(Sym::parse(p)?),
t => return Err(p.error(Unexpected(t), P)), t => return Err(p.error(Unexpected(t), P)),
@ -866,7 +854,7 @@ impl Parse<'_> for Stmt {
Ok(_) => Semi::Terminated, Ok(_) => Semi::Terminated,
_ => Semi::Unterminated, _ => Semi::Unterminated,
}, },
extents: Span(start, p.loc()), span: Span(start, p.loc()),
}) })
} }
} }
@ -878,8 +866,8 @@ impl Parse<'_> for StmtKind {
fn parse(p: &mut Parser) -> PResult<StmtKind> { fn parse(p: &mut Parser) -> PResult<StmtKind> {
Ok(match p.peek_kind(Parsing::StmtKind)? { Ok(match p.peek_kind(Parsing::StmtKind)? {
TokenKind::Semi => StmtKind::Empty, TokenKind::Semi => StmtKind::Empty,
item_like!() => Item::parse(p)?.into(), item_like!() => StmtKind::Item(p.parse()?),
_ => Expr::parse(p)?.into(), _ => StmtKind::Expr(p.parse()?),
}) })
} }
} }
@ -888,26 +876,40 @@ impl Parse<'_> for StmtKind {
impl Parse<'_> for Expr { impl Parse<'_> for Expr {
/// Parses an [Expr] /// Parses an [Expr]
///
/// See also: [ExprKind::parse]
fn parse(p: &mut Parser) -> PResult<Expr> { fn parse(p: &mut Parser) -> PResult<Expr> {
let start = p.loc(); prec::expr(p, 0)
Ok(Expr { kind: ExprKind::parse(p)?, extents: Span(start, p.loc()) })
} }
} }
impl Parse<'_> for ExprKind { impl Parse<'_> for Closure {
/// Parses an [ExprKind] at the lowest precedence level fn parse(p: &mut Parser<'_>) -> PResult<Self> {
// Implementer's note: Do not call this from within [prec::exprkind] let args = sep(
fn parse(p: &mut Parser<'_>) -> PResult<ExprKind> { Pattern::parse,
prec::exprkind(p, 0) TokenKind::Comma,
TokenKind::Bar,
Parsing::Closure,
);
let arg = match p.peek_kind(Parsing::Closure)? {
TokenKind::BarBar => {
p.consume_peeked();
Box::new(Pattern::Tuple(vec![]))
}
_ => Box::new(delim(
|p| args(p).map(Pattern::Tuple),
(TokenKind::Bar, TokenKind::Bar),
Parsing::Closure,
)(p)?),
};
let body = p.parse()?;
Ok(Closure { arg, body })
} }
} }
impl Parse<'_> for Quote { impl Parse<'_> for Quote {
fn parse(p: &mut Parser<'_>) -> PResult<Self> { fn parse(p: &mut Parser<'_>) -> PResult<Self> {
let quote = delim( let quote = delim(
ExprKind::parse, Expr::parse,
(TokenKind::Grave, TokenKind::Grave), (TokenKind::Grave, TokenKind::Grave),
Parsing::ExprKind, Parsing::ExprKind,
)(p)? )(p)?
@ -920,15 +922,15 @@ impl Parse<'_> for Let {
fn parse(p: &mut Parser) -> PResult<Let> { fn parse(p: &mut Parser) -> PResult<Let> {
p.consume_peeked(); p.consume_peeked();
Ok(Let { Ok(Let {
mutable: Mutability::parse(p)?, mutable: p.parse()?,
name: Pattern::parse(p)?, name: p.parse()?,
ty: if p.match_type(TokenKind::Colon, Parsing::Let).is_ok() { ty: if p.match_type(TokenKind::Colon, Parsing::Let).is_ok() {
Some(Ty::parse(p)?.into()) Some(p.parse()?)
} else { } else {
None None
}, },
init: if p.match_type(TokenKind::Eq, Parsing::Let).is_ok() { init: if p.match_type(TokenKind::Eq, Parsing::Let).is_ok() {
Some(Expr::parse(p)?.into()) Some(condition(p)?.into())
} else { } else {
None None
}, },
@ -967,7 +969,7 @@ impl Parse<'_> for Fielder {
Ok(Fielder { Ok(Fielder {
name: Sym::parse(p)?, name: Sym::parse(p)?,
init: match p.match_type(TokenKind::Colon, P) { init: match p.match_type(TokenKind::Colon, P) {
Ok(_) => Some(Box::new(Expr::parse(p)?)), Ok(_) => Some(p.parse()?),
Err(_) => None, Err(_) => None,
}, },
}) })
@ -981,16 +983,20 @@ impl Parse<'_> for AddrOf {
match p.peek_kind(P)? { match p.peek_kind(P)? {
TokenKind::Amp => { TokenKind::Amp => {
p.consume_peeked(); p.consume_peeked();
Ok(AddrOf { mutable: Mutability::parse(p)?, expr: ExprKind::parse(p)?.into() }) Ok(AddrOf { mutable: p.parse()?, expr: p.parse()? })
} }
TokenKind::AmpAmp => { TokenKind::AmpAmp => {
let start = p.loc();
p.consume_peeked(); p.consume_peeked();
Ok(AddrOf { Ok(AddrOf {
mutable: Mutability::Not, mutable: Mutability::Not,
expr: ExprKind::AddrOf(AddrOf { expr: Expr {
kind: ExprKind::AddrOf(AddrOf {
mutable: Mutability::parse(p)?, mutable: Mutability::parse(p)?,
expr: ExprKind::parse(p)?.into(), expr: p.parse()?,
}) }),
span: Span(start, p.loc()),
}
.into(), .into(),
}) })
} }
@ -1007,13 +1013,18 @@ impl Parse<'_> for Block {
} }
} }
/// Conditions (which precede curly-braced blocks) get special treatment
fn condition(p: &mut Parser) -> PResult<Expr> {
prec::expr(p, prec::Precedence::Condition.level())
}
impl Parse<'_> for While { impl Parse<'_> for While {
/// [While] = `while` [Expr] [Block] [Else]? /// [While] = `while` [Expr] [Block] [Else]?
#[rustfmt::skip] #[rustfmt::skip]
fn parse(p: &mut Parser) -> PResult<While> { fn parse(p: &mut Parser) -> PResult<While> {
p.match_type(TokenKind::While, Parsing::While)?; p.match_type(TokenKind::While, Parsing::While)?;
Ok(While { Ok(While {
cond: Expr::parse(p)?.into(), cond: condition(p)?.into(),
pass: Block::parse(p)?.into(), pass: Block::parse(p)?.into(),
fail: Else::parse(p)? fail: Else::parse(p)?
}) })
@ -1026,7 +1037,7 @@ impl Parse<'_> for If {
fn parse(p: &mut Parser) -> PResult<If> { fn parse(p: &mut Parser) -> PResult<If> {
p.match_type(TokenKind::If, Parsing::If)?; p.match_type(TokenKind::If, Parsing::If)?;
Ok(If { Ok(If {
cond: Expr::parse(p)?.into(), cond: condition(p)?.into(),
pass: Block::parse(p)?.into(), pass: Block::parse(p)?.into(),
fail: Else::parse(p)?, fail: Else::parse(p)?,
}) })
@ -1034,16 +1045,13 @@ impl Parse<'_> for If {
} }
impl Parse<'_> for For { impl Parse<'_> for For {
/// [For]: `for` Pattern (TODO) `in` [Expr] [Block] [Else]? /// [For]: `for` [Pattern] `in` [Expr] [Block] [Else]?
#[rustfmt::skip] #[rustfmt::skip]
fn parse(p: &mut Parser) -> PResult<For> { fn parse(p: &mut Parser) -> PResult<For> {
p.match_type(TokenKind::For, Parsing::For)?;
let bind = Sym::parse(p)?;
p.match_type(TokenKind::In, Parsing::For)?;
Ok(For { Ok(For {
bind, bind: delim(Parse::parse, (TokenKind::For, TokenKind::In), Parsing::For)(p)?,
cond: Expr::parse(p)?.into(), cond: condition(p)?.into(),
pass: Block::parse(p)?.into(), pass: p.parse()?,
fail: Else::parse(p)?, fail: Else::parse(p)?,
}) })
} }
@ -1055,7 +1063,7 @@ impl Parse<'_> for Else {
match p.peek_kind(Parsing::Else) { match p.peek_kind(Parsing::Else) {
Ok(TokenKind::Else) => { Ok(TokenKind::Else) => {
p.consume_peeked(); p.consume_peeked();
Ok(Expr::parse(p)?.into()) Ok(Else { body: Some(p.parse()?) })
} }
Ok(_) | Err(Error { reason: EndOfInput, .. }) => Ok(None.into()), Ok(_) | Err(Error { reason: EndOfInput, .. }) => Ok(None.into()),
Err(e) => Err(e), Err(e) => Err(e),
@ -1079,11 +1087,106 @@ impl Parse<'_> for Return {
} }
} }
fn pathpattern(p: &mut Parser<'_>) -> PResult<Pattern> {
const P: Parsing = Parsing::Pattern;
let name = Path::parse(p)?;
let struct_members = |p: &mut Parser| {
let name = p.parse()?;
let pat = if p.match_type(TokenKind::Colon, P).is_ok() {
Some(p.parse()?)
} else {
None
};
Ok((name, pat))
};
Ok(match p.peek_kind(Parsing::Pattern)? {
TokenKind::LCurly => Pattern::Struct(
name,
delim(
sep(struct_members, TokenKind::Comma, TokenKind::RCurly, P),
CURLIES,
P,
)(p)?,
),
TokenKind::LParen => Pattern::TupleStruct(
name,
delim(
sep(Parse::parse, TokenKind::Comma, TokenKind::RParen, P),
PARENS,
P,
)(p)?,
),
_ => name
.as_sym()
.map(Pattern::Name)
.unwrap_or(Pattern::Path(name)),
})
}
impl Parse<'_> for Pattern { impl Parse<'_> for Pattern {
fn parse(p: &mut Parser<'_>) -> PResult<Self> { fn parse(p: &mut Parser<'_>) -> PResult<Self> {
let value = prec::exprkind(p, prec::Precedence::Highest.level())?; const P: Parsing = Parsing::Pattern;
Pattern::try_from(value) let head = match p.peek_kind(P)? {
.map_err(|_| p.error(ExpectedParsing { want: Parsing::Pattern }, Parsing::Pattern)) // Name, Path, Struct, TupleStruct
TokenKind::Identifier => pathpattern(p)?,
// Literal
TokenKind::Literal => Pattern::Literal(p.parse()?),
// Rest
TokenKind::DotDot => {
p.consume_peeked();
if matches!(
p.peek_kind(P),
Ok(TokenKind::Identifier | TokenKind::Literal)
) {
Pattern::Rest(Some(p.parse()?))
} else {
Pattern::Rest(None)
}
}
// Ref
TokenKind::Amp => {
p.consume_peeked();
Pattern::Ref(p.parse()?, p.parse()?)
}
// Ref(Ref)
TokenKind::AmpAmp => {
p.consume_peeked();
Pattern::Ref(
Mutability::Not,
Box::new(Pattern::Ref(p.parse()?, p.parse()?)),
)
}
// Tuple
TokenKind::LParen => Pattern::Tuple(delim(
sep(Parse::parse, TokenKind::Comma, TokenKind::RParen, P),
PARENS,
P,
)(p)?),
// Array
TokenKind::LBrack => Pattern::Array(delim(
sep(Parse::parse, TokenKind::Comma, TokenKind::RBrack, P),
BRACKETS,
P,
)(p)?),
_ => {
let bad_expr = p.parse()?;
Err(p.error(ErrorKind::InvalidPattern(bad_expr), P))?
}
};
match p.peek_kind(P) {
Ok(TokenKind::DotDot) => {
p.consume_peeked();
Ok(Pattern::RangeExc(head.into(), p.parse()?))
}
Ok(TokenKind::DotDotEq) => {
p.consume_peeked();
Ok(Pattern::RangeInc(head.into(), p.parse()?))
}
_ => Ok(head),
}
} }
} }
@ -1091,13 +1194,14 @@ impl Parse<'_> for Match {
/// [Match] = `match` [Expr] `{` [MatchArm],* `}` /// [Match] = `match` [Expr] `{` [MatchArm],* `}`
fn parse(p: &mut Parser<'_>) -> PResult<Self> { fn parse(p: &mut Parser<'_>) -> PResult<Self> {
p.match_type(TokenKind::Match, Parsing::Match)?; p.match_type(TokenKind::Match, Parsing::Match)?;
let scrutinee = Expr::parse(p)?.into(); Ok(Match {
let arms = delim( scrutinee: condition(p)?.into(),
arms: delim(
sep(MatchArm::parse, TokenKind::Comma, CURLIES.1, Parsing::Match), sep(MatchArm::parse, TokenKind::Comma, CURLIES.1, Parsing::Match),
CURLIES, CURLIES,
Parsing::Match, Parsing::Match,
)(p)?; )(p)?,
Ok(Match { scrutinee, arms }) })
} }
} }
@ -1115,6 +1219,12 @@ impl Parse<'_> for MatchArm {
fn ret_body(p: &mut Parser, while_parsing: Parsing) -> PResult<Option<Box<Expr>>> { fn ret_body(p: &mut Parser, while_parsing: Parsing) -> PResult<Option<Box<Expr>>> {
Ok(match p.peek_kind(while_parsing)? { Ok(match p.peek_kind(while_parsing)? {
TokenKind::Semi => None, TokenKind::Semi => None,
_ => Some(Expr::parse(p)?.into()), _ => Some(p.parse()?),
}) })
} }
impl<'t, P: Parse<'t>> Parse<'t> for Box<P> {
fn parse(p: &mut Parser<'t>) -> PResult<Self> {
p.parse().map(Box::new)
}
}

View File

@ -8,14 +8,16 @@
use super::{Parse, *}; use super::{Parse, *};
/// Parses an [ExprKind] /// Parses an [ExprKind]
pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> { pub fn expr(p: &mut Parser, power: u8) -> PResult<Expr> {
let parsing = Parsing::ExprKind; let parsing = Parsing::ExprKind;
let start = p.loc();
// Prefix expressions // Prefix expressions
let mut head = match p.peek_kind(Parsing::Unary)? { let mut head = Expr {
kind: match p.peek_kind(Parsing::Unary)? {
literal_like!() => Literal::parse(p)?.into(), literal_like!() => Literal::parse(p)?.into(),
path_like!() => exprkind_pathlike(p)?, path_like!() => exprkind_pathlike(p)?,
TokenKind::Amp | TokenKind::AmpAmp => AddrOf::parse(p)?.into(), TokenKind::Amp | TokenKind::AmpAmp => AddrOf::parse(p)?.into(),
TokenKind::Bar | TokenKind::BarBar => Closure::parse(p)?.into(),
TokenKind::Grave => Quote::parse(p)?.into(), TokenKind::Grave => Quote::parse(p)?.into(),
TokenKind::LCurly => Block::parse(p)?.into(), TokenKind::LCurly => Block::parse(p)?.into(),
TokenKind::LBrack => exprkind_arraylike(p)?, TokenKind::LBrack => exprkind_arraylike(p)?,
@ -33,18 +35,23 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
} }
op => { op => {
let (kind, prec) = from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?; let (kind, prec) =
from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?;
let ((), after) = prec.prefix().expect("should have a precedence"); let ((), after) = prec.prefix().expect("should have a precedence");
p.consume_peeked(); p.consume_peeked();
Unary { kind, tail: exprkind(p, after)?.into() }.into() Unary { kind, tail: expr(p, after)?.into() }.into()
} }
},
span: Span(start, p.loc()),
}; };
fn from_postfix(op: TokenKind) -> Option<Precedence> { fn from_postfix(op: TokenKind) -> Option<Precedence> {
Some(match op { Some(match op {
TokenKind::LBrack => Precedence::Index, TokenKind::LBrack => Precedence::Index,
TokenKind::LParen => Precedence::Call, TokenKind::LParen => Precedence::Call,
TokenKind::LCurly => Precedence::Structor,
TokenKind::Dot => Precedence::Member, TokenKind::Dot => Precedence::Member,
TokenKind::As => Precedence::Cast,
_ => None?, _ => None?,
}) })
} }
@ -55,26 +62,48 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
if before < power { if before < power {
break; break;
} }
p.consume_peeked();
head = match op { head = Expr {
kind: match op {
TokenKind::LBrack => { TokenKind::LBrack => {
p.consume_peeked();
let indices = let indices =
sep(Expr::parse, TokenKind::Comma, TokenKind::RBrack, parsing)(p)?; sep(Expr::parse, TokenKind::Comma, TokenKind::RBrack, parsing)(p)?;
p.match_type(TokenKind::RBrack, parsing)?; p.match_type(TokenKind::RBrack, parsing)?;
ExprKind::Index(Index { head: head.into(), indices }) ExprKind::Index(Index { head: head.into(), indices })
} }
TokenKind::LParen => { TokenKind::LParen => {
let exprs = sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?; p.consume_peeked();
let exprs =
sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
p.match_type(TokenKind::RParen, parsing)?; p.match_type(TokenKind::RParen, parsing)?;
Binary { kind: BinaryKind::Call, parts: (head, Tuple { exprs }.into()).into() } Binary {
kind: BinaryKind::Call,
parts: (
head,
Expr { kind: Tuple { exprs }.into(), span: Span(start, p.loc()) },
)
.into(),
}
.into() .into()
} }
TokenKind::LCurly => match head.kind {
ExprKind::Path(path) => ExprKind::Structor(structor_body(p, path)?),
_ => break,
},
TokenKind::Dot => { TokenKind::Dot => {
p.consume_peeked();
let kind = MemberKind::parse(p)?; let kind = MemberKind::parse(p)?;
Member { head: Box::new(head), kind }.into() 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))?, _ => Err(p.error(Unexpected(op), parsing))?,
},
span: Span(start, p.loc()),
}; };
continue; continue;
} }
@ -86,8 +115,11 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
} }
p.consume_peeked(); p.consume_peeked();
let tail = exprkind(p, after)?; let tail = expr(p, after)?;
head = Binary { kind, parts: (head, tail).into() }.into(); head = Expr {
kind: Binary { kind, parts: (head, tail).into() }.into(),
span: Span(start, p.loc()),
};
continue; continue;
} }
@ -98,8 +130,11 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
} }
p.consume_peeked(); p.consume_peeked();
let tail = exprkind(p, after)?; let tail = expr(p, after)?;
head = Modify { kind, parts: (head, tail).into() }.into(); head = Expr {
kind: Modify { kind, parts: (head, tail).into() }.into(),
span: Span(start, p.loc()),
};
continue; continue;
} }
@ -112,8 +147,12 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
} }
p.consume_peeked(); p.consume_peeked();
let tail = exprkind(p, after)?; let tail = expr(p, after)?;
head = Assign { parts: (head, tail).into() }.into(); head = Expr {
kind: Assign { parts: (head, tail).into() }.into(),
span: Span(start, p.loc()),
};
continue; continue;
} }
@ -125,7 +164,8 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
p.consume_peeked(); p.consume_peeked();
let ty = Ty::parse(p)?; let ty = Ty::parse(p)?;
head = Cast { head: head.into(), ty }.into(); head = Expr { kind: Cast { head: head.into(), ty }.into(), span: Span(start, p.loc()) };
continue; continue;
} }
@ -161,10 +201,16 @@ fn exprkind_array_rep(p: &mut Parser) -> PResult<ExprKind> {
let first = Expr::parse(p)?; let first = Expr::parse(p)?;
Ok(match p.peek_kind(P)? { Ok(match p.peek_kind(P)? {
TokenKind::Semi => ArrayRep { TokenKind::Semi => ArrayRep {
value: first.kind.into(), value: first.into(),
repeat: { repeat: {
p.consume_peeked(); p.consume_peeked();
Box::new(exprkind(p, 0)?) let value = p.match_type(TokenKind::Literal, Parsing::ArrayRep)?;
match value.data() {
TokenData::Integer(size) => *size as usize,
_ => {
Err(p.error(ErrorKind::Unexpected(TokenKind::Literal), Parsing::ArrayRep))?
}
}
}, },
} }
.into(), .into(),
@ -211,17 +257,13 @@ fn exprkind_group(p: &mut Parser) -> PResult<ExprKind> {
} }
Ok(Tuple { exprs }.into()) Ok(Tuple { exprs }.into())
} }
_ => Ok(Group { expr: first.kind.into() }.into()), _ => Ok(Group { expr: first.into() }.into()),
} }
} }
/// Parses an expression beginning with a [Path] (i.e. [Path] or [Structor]) /// Parses an expression beginning with a [Path] (i.e. [Path] or [Structor])
fn exprkind_pathlike(p: &mut Parser) -> PResult<ExprKind> { fn exprkind_pathlike(p: &mut Parser) -> PResult<ExprKind> {
let head = Path::parse(p)?; Path::parse(p).map(Into::into)
Ok(match p.match_type(TokenKind::Colon, Parsing::Path) {
Ok(_) => ExprKind::Structor(structor_body(p, head)?),
Err(_) => ExprKind::Path(head),
})
} }
/// [Structor]Body = `{` ([Fielder] `,`)* [Fielder]? `}` /// [Structor]Body = `{` ([Fielder] `,`)* [Fielder]? `}`
@ -244,6 +286,8 @@ fn structor_body(p: &mut Parser, to: Path) -> PResult<Structor> {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Precedence { pub enum Precedence {
Assign, Assign,
Structor, // A structor is never a valid conditional
Condition, // Anything that syntactically needs a block following it
Logic, Logic,
Compare, Compare,
Range, Range,
@ -256,7 +300,7 @@ pub enum Precedence {
Cast, Cast,
Member, // left-associative Member, // left-associative
Call, Call,
Highest, Deref,
} }
impl Precedence { impl Precedence {
@ -269,6 +313,7 @@ impl Precedence {
match self { match self {
Self::Assign => Some(((), self.level())), Self::Assign => Some(((), self.level())),
Self::Unary => Some(((), self.level())), Self::Unary => Some(((), self.level())),
Self::Deref => Some(((), self.level())),
_ => None, _ => None,
} }
} }
@ -284,7 +329,9 @@ impl Precedence {
pub fn postfix(self) -> Option<(u8, ())> { pub fn postfix(self) -> Option<(u8, ())> {
match self { match self {
Self::Index | Self::Call | Self::Member => Some((self.level(), ())), Self::Structor | Self::Index | Self::Call | Self::Member | Self::Cast => {
Some((self.level(), ()))
}
_ => None, _ => None,
} }
} }
@ -317,7 +364,8 @@ impl From<UnaryKind> for Precedence {
use UnaryKind as Op; use UnaryKind as Op;
match value { match value {
Op::Loop => Precedence::Assign, Op::Loop => Precedence::Assign,
Op::Deref | Op::Neg | Op::Not | Op::At | Op::Tilde => Precedence::Unary, Op::Deref => Precedence::Deref,
_ => Precedence::Unary,
} }
} }
} }
@ -338,6 +386,8 @@ operator! {
Star => Deref, Star => Deref,
Minus => Neg, Minus => Neg,
Bang => Not, Bang => Not,
DotDot => RangeExc,
DotDotEq => RangeInc,
At => At, At => At,
Tilde => Tilde, Tilde => Tilde,
}; };

View File

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

View File

@ -0,0 +1,954 @@
//! 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.as_ref() {
TyKind::Tuple(TyTuple { types }) => types.as_slice(),
TyKind::Empty => &[],
_ => panic!("Unsupported function args: {args}"),
};
let bind = match bind {
Pattern::Tuple(tup) => tup.as_slice(),
_ => panic!("Unsupported function binders: {args}"),
};
match rety {
Some(ty) => y.p(ty),
None => y.p("void"),
}
.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 { target, body } = self;
y.nest("/* TODO: impl ").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.as_ref() {
TyKind::Empty => {}
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 mut y = y.nest("{");
y.endl();
for idx in 0..*repeat {
if idx > 0 {
y.p(", ");
}
y.p(value);
}
}
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 } = self;
y.p(kind);
}
}
impl CLangify for TyKind {
fn print(&self, y: &mut CLangifier) {
match self {
TyKind::Never => y.p("Never"),
TyKind::Empty => y.p("Empty"),
TyKind::Infer => y.p("Any"),
TyKind::Path(t) => y.p(t),
TyKind::Tuple(t) => y.p(t),
TyKind::Ref(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 TyFn {
fn print(&self, y: &mut CLangifier) {
let Self { args, rety } = self;
// TODO: function pointer syntax
y.nest("(").p(rety).p(" *)(");
match args.as_ref() {
TyKind::Empty => y,
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

@ -3,7 +3,7 @@
use cl_ast::Stmt; use cl_ast::Stmt;
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
use repline::{error::Error as RlError, Repline}; use repline::{Repline, error::Error as RlError};
use std::error::Error; use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
@ -19,7 +19,7 @@ fn main() -> Result<(), Box<dyn Error>> {
Err(e) => Err(e)?, Err(e) => Err(e)?,
}; };
let mut parser = Parser::new(Lexer::new(&line)); let mut parser = Parser::new("", Lexer::new(&line));
let code = match parser.parse::<Stmt>() { let code = match parser.parse::<Stmt>() {
Ok(code) => { Ok(code) => {
rl.accept(); rl.accept();
@ -41,7 +41,6 @@ pub use yamler::Yamler;
pub mod yamler { pub mod yamler {
use crate::yamlify::Yamlify; use crate::yamlify::Yamlify;
use std::{ use std::{
fmt::Display,
io::Write, io::Write,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
@ -81,22 +80,25 @@ pub mod yamler {
} }
/// Prints a section header and increases indentation /// 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!(); println!();
self.print_indentation(&mut std::io::stdout().lock()); self.print_indentation(&mut std::io::stdout().lock());
print!("- {name}:"); print!("- ");
name.yaml(self);
print!(":");
self.indent() self.indent()
} }
/// Prints a yaml key value pair: `- name: "value"` /// Prints a yaml key value pair: `- name: "value"`
pub fn pair<D: Display, T: Yamlify>(&mut self, name: D, value: T) -> &mut Self { pub fn pair<D: Yamlify, T: Yamlify>(&mut self, name: D, value: T) -> &mut Self {
self.key(name).yaml(&value); self.key(name).value(value);
self self
} }
/// Prints a yaml scalar value: `"name"`` /// Prints a yaml scalar value: `"name"``
pub fn value<D: Display>(&mut self, value: D) -> &mut Self { pub fn value<D: Yamlify>(&mut self, value: D) -> &mut Self {
print!(" {value}"); print!(" ");
value.yaml(self);
self self
} }
@ -150,8 +152,8 @@ pub mod yamlify {
impl Yamlify for File { impl Yamlify for File {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let File { items } = self; let File { name, items } = self;
y.key("File").yaml(items); y.key("File").pair("name", name).yaml(items);
} }
} }
impl Yamlify for Visibility { impl Yamlify for Visibility {
@ -193,7 +195,7 @@ pub mod yamlify {
impl Yamlify for Item { impl Yamlify for Item {
fn yaml(&self, y: &mut Yamler) { 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); y.key("Item").yaml(attrs).yaml(vis).yaml(kind);
} }
} }
@ -212,10 +214,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 { impl Yamlify for Alias {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { to, from } = self; let Self { name, from } = self;
y.key("Alias").pair("to", to).pair("from", from); y.key("Alias").pair("to", name).pair("from", from);
} }
} }
impl Yamlify for Const { impl Yamlify for Const {
@ -235,23 +243,16 @@ pub mod yamlify {
} }
impl Yamlify for Module { impl Yamlify for Module {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self; let Self { name, file } = self;
y.key("Module").pair("name", name).yaml(kind); y.key("Module").pair("name", name).yaml(file);
}
}
impl Yamlify for ModuleKind {
fn yaml(&self, y: &mut Yamler) {
match self {
ModuleKind::Inline(f) => y.yaml(f),
ModuleKind::Outline => y,
};
} }
} }
impl Yamlify for Function { impl Yamlify for Function {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { name, sign, bind, body } = self; let Self { name, gens, sign, bind, body } = self;
y.key("Function") y.key("Function")
.pair("name", name) .pair("name", name)
.pair("gens", gens)
.pair("sign", sign) .pair("sign", sign)
.pair("bind", bind) .pair("bind", bind)
.pair("body", body); .pair("body", body);
@ -259,8 +260,11 @@ pub mod yamlify {
} }
impl Yamlify for Struct { impl Yamlify for Struct {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self; let Self { name, gens, kind } = self;
y.key("Struct").pair("name", name).yaml(kind); y.key("Struct")
.pair("gens", gens)
.pair("name", name)
.yaml(kind);
} }
} }
impl Yamlify for StructKind { impl Yamlify for StructKind {
@ -280,32 +284,20 @@ pub mod yamlify {
} }
impl Yamlify for Enum { impl Yamlify for Enum {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self; let Self { name, gens, variants: kind } = self;
y.key("Enum").pair("name", name).yaml(kind); y.key("Enum")
} .pair("gens", gens)
} .pair("name", name)
impl Yamlify for EnumKind { .yaml(kind);
fn yaml(&self, y: &mut Yamler) {
match self {
EnumKind::NoVariants => y,
EnumKind::Variants(v) => y.yaml(v),
};
} }
} }
impl Yamlify for Variant { impl Yamlify for Variant {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self; let Self { name, kind, body } = self;
y.key("Variant").pair("name", name).yaml(kind); y.key("Variant")
} .pair("name", name)
} .pair("kind", kind)
impl Yamlify for VariantKind { .pair("body", body);
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),
};
} }
} }
impl Yamlify for Impl { impl Yamlify for Impl {
@ -349,8 +341,8 @@ pub mod yamlify {
} }
impl Yamlify for Stmt { impl Yamlify for Stmt {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind, semi } = self; let Self { span: _, kind, semi } = self;
y.key("Stmt").yaml(kind).yaml(semi); y.key("Stmt").value(kind).yaml(semi);
} }
} }
impl Yamlify for Semi { impl Yamlify for Semi {
@ -371,13 +363,14 @@ pub mod yamlify {
} }
impl Yamlify for Expr { impl Yamlify for Expr {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind } = self; let Self { span: _, kind } = self;
y.yaml(kind); y.yaml(kind);
} }
} }
impl Yamlify for ExprKind { impl Yamlify for ExprKind {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
match self { match self {
ExprKind::Closure(k) => k.yaml(y),
ExprKind::Quote(k) => k.yaml(y), ExprKind::Quote(k) => k.yaml(y),
ExprKind::Let(k) => k.yaml(y), ExprKind::Let(k) => k.yaml(y),
ExprKind::Match(k) => k.yaml(y), ExprKind::Match(k) => k.yaml(y),
@ -409,6 +402,12 @@ pub mod yamlify {
} }
} }
} }
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 { impl Yamlify for Quote {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
y.key("Quote").value(self); y.key("Quote").value(self);
@ -428,23 +427,35 @@ pub mod yamlify {
impl Yamlify for Pattern { impl Yamlify for Pattern {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
match self { match self {
Pattern::Name(name) => y.value(name),
Pattern::Path(path) => y.value(path), Pattern::Path(path) => y.value(path),
Pattern::Literal(literal) => y.value(literal), Pattern::Literal(literal) => y.value(literal),
Pattern::Ref(mutability, pattern) => { Pattern::Rest(name) => y.pair("Rest", name),
y.pair("mutability", mutability).pair("subpattern", pattern) 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::Tuple(patterns) => y.key("Tuple").yaml(patterns), Pattern::RangeExc(head, tail) => {
Pattern::Array(patterns) => y.key("Array").yaml(patterns), 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) => { Pattern::Struct(path, items) => {
{ {
let mut y = y.key("Struct"); let mut y = y.key("Struct");
y.pair("name", path); y.yaml(path);
for (name, item) in items { for (name, item) in items {
y.pair(name, item); y.pair(name, item);
} }
} }
y y
} }
Pattern::TupleStruct(path, items) => {
y.key("TupleStruct").yaml(path).list(items);
y
}
}; };
} }
} }
@ -480,11 +491,6 @@ pub mod yamlify {
.pair("tail", &parts.1); .pair("tail", &parts.1);
} }
} }
impl Yamlify for ModifyKind {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
}
impl Yamlify for Binary { impl Yamlify for Binary {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { kind, parts } = self; let Self { kind, parts } = self;
@ -494,22 +500,12 @@ pub mod yamlify {
.pair("tail", &parts.1); .pair("tail", &parts.1);
} }
} }
impl Yamlify for BinaryKind {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
}
impl Yamlify for Unary { impl Yamlify for Unary {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { kind, tail } = self; let Self { kind, tail } = self;
y.key("Unary").pair("kind", kind).pair("tail", tail); y.key("Unary").pair("kind", kind).pair("tail", tail);
} }
} }
impl Yamlify for UnaryKind {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
}
impl Yamlify for Cast { impl Yamlify for Cast {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { head, ty } = self; let Self { head, ty } = self;
@ -625,24 +621,19 @@ pub mod yamlify {
} }
} }
impl Yamlify for Literal { impl Yamlify for Literal {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, _y: &mut Yamler) {
y.value(format_args!("\"{self}\"")); 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 { impl Yamlify for Ty {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind } = self; let Self { span: _, kind } = self;
y.key("Ty").yaml(kind); y.key("Ty").yaml(kind);
} }
} }
@ -651,12 +642,13 @@ pub mod yamlify {
match self { match self {
TyKind::Never => y.value("Never"), TyKind::Never => y.value("Never"),
TyKind::Empty => y.value("Empty"), TyKind::Empty => y.value("Empty"),
TyKind::Infer => y.value("_"),
TyKind::Path(t) => y.yaml(t), TyKind::Path(t) => y.yaml(t),
TyKind::Tuple(t) => y.yaml(t), TyKind::Tuple(t) => y.yaml(t),
TyKind::Ref(t) => y.yaml(t), TyKind::Ref(t) => y.yaml(t),
TyKind::Fn(t) => y.yaml(t), TyKind::Fn(t) => y.yaml(t),
TyKind::Slice(_) => todo!(), TyKind::Slice(t) => y.yaml(t),
TyKind::Array(_) => todo!(), TyKind::Array(t) => y.yaml(t),
}; };
} }
} }
@ -676,7 +668,6 @@ pub mod yamlify {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
match self { match self {
PathPart::SuperKw => y.value("super"), PathPart::SuperKw => y.value("super"),
PathPart::SelfKw => y.value("self"),
PathPart::SelfTy => y.value("Self"), PathPart::SelfTy => y.value("Self"),
PathPart::Ident(i) => y.yaml(i), PathPart::Ident(i) => y.yaml(i),
}; };
@ -753,14 +744,15 @@ pub mod yamlify {
macro_rules! scalar { macro_rules! scalar {
($($t:ty),*$(,)?) => { ($($t:ty),*$(,)?) => {
$(impl Yamlify for $t { $(impl Yamlify for $t {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, _y: &mut Yamler) {
y.value(self); print!("{self}");
} }
})* })*
}; };
} }
scalar! { 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

@ -9,7 +9,7 @@ use cl_ast::File;
use cl_interpret::{builtin::builtins, convalue::ConValue, env::Environment, interpret::Interpret}; use cl_interpret::{builtin::builtins, convalue::ConValue, env::Environment, interpret::Interpret};
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
use std::{error::Error, path::Path}; use std::{borrow::Cow, error::Error, path::Path};
/// Run the command line interface /// Run the command line interface
pub fn run(args: Args) -> Result<(), Box<dyn Error>> { pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
@ -36,6 +36,8 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
fn get_line() { fn get_line() {
match repline::Repline::new("", "", "").read() { match repline::Repline::new("", "", "").read() {
Ok(line) => Ok(ConValue::String(line.into())), Ok(line) => Ok(ConValue::String(line.into())),
Err(repline::Error::CtrlD(line)) => Ok(ConValue::String(line.into())),
Err(repline::Error::CtrlC(_)) => Err(cl_interpret::error::Error::Break(ConValue::Empty)),
Err(e) => Ok(ConValue::String(e.to_string().into())), Err(e) => Ok(ConValue::String(e.to_string().into())),
} }
} }
@ -47,7 +49,9 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
if repl { if repl {
if let Some(file) = file { if let Some(file) = file {
load_file(&mut env, file)?; if let Err(e) = load_file(&mut env, file) {
eprintln!("{e}")
}
} }
let mut ctx = Context::with_env(env); let mut ctx = Context::with_env(env);
match mode { match mode {
@ -57,25 +61,36 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
Mode::Run => menu::run(&mut ctx)?, Mode::Run => menu::run(&mut ctx)?,
} }
} else { } else {
let path = format_path_for_display(file.as_deref());
let code = match &file { let code = match &file {
Some(file) => std::fs::read_to_string(file)?, Some(file) => std::fs::read_to_string(file)?,
None => std::io::read_to_string(std::io::stdin())?, None => std::io::read_to_string(std::io::stdin())?,
}; };
match mode { match mode {
Mode::Lex => lex_code(&code, file), Mode::Lex => lex_code(&path, &code),
Mode::Fmt => fmt_code(&code), Mode::Fmt => fmt_code(&path, &code),
Mode::Run | Mode::Menu => run_code(&code, &mut env), Mode::Run | Mode::Menu => run_code(&path, &code, &mut env),
}?; }?;
} }
Ok(()) 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>> { fn load_file(env: &mut Environment, path: impl AsRef<Path>) -> Result<ConValue, Box<dyn Error>> {
let inliner = let path = path.as_ref();
cl_parser::inliner::ModuleInliner::new(path.as_ref().parent().unwrap_or(Path::new(""))); let inliner = cl_parser::inliner::ModuleInliner::new(path.with_extension(""));
let file = std::fs::read_to_string(path)?; let file = std::fs::read_to_string(path)?;
let code = Parser::new(Lexer::new(&file)).parse()?; let code = Parser::new(path.display().to_string(), Lexer::new(&file)).parse()?;
let code = match inliner.inline(code) { let code = match inliner.inline(code) {
Ok(a) => a, Ok(a) => a,
Err((code, io_errs, parse_errs)) => { Err((code, io_errs, parse_errs)) => {
@ -88,13 +103,22 @@ fn load_file(env: &mut Environment, path: impl AsRef<Path>) -> Result<ConValue,
code 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) { for token in Lexer::new(code) {
if let Some(path) = &path { if !path.is_empty() {
print!("{}:", path.as_ref().display()); print!("{}:", path);
} }
match token { match token {
Ok(token) => print_token(&token), Ok(token) => print_token(&token),
@ -104,14 +128,14 @@ fn lex_code(code: &str, path: Option<impl AsRef<Path>>) -> Result<(), Box<dyn Er
Ok(()) Ok(())
} }
fn fmt_code(code: &str) -> Result<(), Box<dyn Error>> { fn fmt_code(path: &str, code: &str) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).parse::<File>()?; let code = Parser::new(path, Lexer::new(code)).parse::<File>()?;
println!("{code}"); println!("{code}");
Ok(()) Ok(())
} }
fn run_code(code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> { fn run_code(path: &str, code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).parse::<File>()?; let code = Parser::new(path, Lexer::new(code)).parse::<File>()?;
match code.interpret(env)? { match code.interpret(env)? {
ConValue::Empty => {} ConValue::Empty => {}
ret => println!("{ret}"), ret => println!("{ret}"),

View File

@ -47,7 +47,7 @@ pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
if line.trim().is_empty() { if line.trim().is_empty() {
return Ok(Response::Deny); return Ok(Response::Deny);
} }
let code = Parser::new(Lexer::new(line)).parse::<Stmt>()?; let code = Parser::new("", Lexer::new(line)).parse::<Stmt>()?;
let code = ModuleInliner::new(".").fold_stmt(code); let code = ModuleInliner::new(".").fold_stmt(code);
print!("{}", ansi::OUTPUT); print!("{}", ansi::OUTPUT);
@ -75,7 +75,7 @@ pub fn lex(_ctx: &mut ctx::Context) -> ReplResult<()> {
pub fn fmt(_ctx: &mut ctx::Context) -> ReplResult<()> { pub fn fmt(_ctx: &mut ctx::Context) -> ReplResult<()> {
read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| { read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| {
let mut p = Parser::new(Lexer::new(line)); let mut p = Parser::new("", Lexer::new(line));
match p.parse::<Stmt>() { match p.parse::<Stmt>() {
Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET), Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET),

View File

@ -61,8 +61,10 @@ macro_rules! make_index {($($(#[$meta:meta])* $name:ident),*$(,)?) => {$(
)*}} )*}}
use self::iter::MapIndexIter; use self::iter::MapIndexIter;
use core::slice::GetManyMutError; use std::{
use std::ops::{Index, IndexMut}; ops::{Index, IndexMut},
slice::GetDisjointMutError,
};
pub use make_index; pub use make_index;
@ -103,11 +105,11 @@ impl<V, K: MapIndex> IndexMap<K, V> {
/// Returns mutable references to many indices at once. /// 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. /// 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, &mut self,
indices: [K; N], indices: [K; N],
) -> Result<[&mut V; N], GetManyMutError> { ) -> Result<[&mut V; N], GetDisjointMutError> {
self.map.get_many_mut(indices.map(|id| id.get())) self.map.get_disjoint_mut(indices.map(|id| id.get()))
} }
/// Returns an iterator over the IndexMap. /// Returns an iterator over the IndexMap.

View File

@ -37,8 +37,8 @@ pub mod interned {
} }
/// Gets the internal value as a reference with the interner's lifetime /// Gets the internal value as a reference with the interner's lifetime
pub fn to_ref(interned: &Self) -> &'a T { pub fn to_ref(&self) -> &'a T {
interned.value self.value
} }
} }
@ -264,12 +264,17 @@ pub mod typed_interner {
/// A [TypedInterner] hands out [Interned] references for arbitrary types. /// A [TypedInterner] hands out [Interned] references for arbitrary types.
/// ///
/// See the [module-level documentation](self) for more information. /// See the [module-level documentation](self) for more information.
#[derive(Default)]
pub struct TypedInterner<'a, T: Eq + Hash> { pub struct TypedInterner<'a, T: Eq + Hash> {
arena: TypedArena<'a, T>, arena: TypedArena<'a, T>,
keys: RwLock<HashSet<&'a T>>, 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> { impl<'a, T: Eq + Hash> TypedInterner<'a, T> {
/// Creates a new [TypedInterner] backed by the provided [TypedArena] /// Creates a new [TypedInterner] backed by the provided [TypedArena]
pub fn new(arena: TypedArena<'a, T>) -> Self { pub fn new(arena: TypedArena<'a, T>) -> Self {

View File

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

View File

@ -42,6 +42,6 @@ impl Loc {
impl std::fmt::Display for Loc { impl std::fmt::Display for Loc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Loc { line, col } = self; 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()); /// assert_eq!(Some(&10), v.last());
/// ``` /// ```
pub macro stack { pub macro stack {
($count:literal) => { ($capacity:literal) => {
Stack::<_, $count>::new() Stack::<_, $capacity>::new()
}, },
($value:expr ; $count:literal) => {{ ($value:expr ; $count:literal) => {{
let mut stack: Stack<_, $count> = Stack::new(); let mut stack: Stack<_, $count> = Stack::new();
@ -103,6 +103,13 @@ pub macro stack {
} }
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),* $(,)?) => { ($($values:expr),* $(,)?) => {
Stack::from([$($values),*]) Stack::from([$($values),*])
} }
@ -142,20 +149,14 @@ impl<T, const N: usize> Deref for Stack<T, N> {
#[inline] #[inline]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
// Safety: self.as_slice()
// - 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) }
} }
} }
impl<T, const N: usize> DerefMut for Stack<T, N> { impl<T, const N: usize> DerefMut for Stack<T, N> {
#[inline] #[inline]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
// Safety: self.as_mut_slice()
// - See Deref
unsafe { slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast(), self.len) }
} }
} }
@ -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> { unsafe impl<#[may_dangle] T, const N: usize> Drop for Stack<T, N> {
#[inline] #[inline]
fn drop(&mut self) { 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>() { if std::mem::needs_drop::<T>() {
unsafe { core::ptr::drop_in_place(self.as_mut_slice()) }; 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 /// 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() self.buf.as_mut_ptr().cast()
} }
/// Extracts a slice containing the entire vector /// Extracts a slice containing the entire vector
pub fn as_slice(&self) -> &[T] { pub const fn as_slice(&self) -> &[T] {
self // 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 /// Extracts a mutable slice containing the entire vector
pub fn as_mut_slice(&mut self) -> &mut [T] { pub const fn as_mut_slice(&mut self) -> &mut [T] {
self // 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 /// 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); /// v.push(3);
/// assert_eq!(&[0, 1, 2, 3], v.as_slice()); /// 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 { if self.len >= N {
panic!("Attempted to push into full stack") 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 /// Push a new element onto the end of the stack
/// ///
/// Returns [`Err(value)`](Result::Err) if the new length would exceed capacity /// 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 { if self.len >= N {
return Err(value); return Err(value);
} }
@ -381,8 +388,11 @@ impl<T, const N: usize> Stack<T, N> {
/// ///
/// len after push must not exceed capacity N /// len after push must not exceed capacity N
#[inline] #[inline]
unsafe fn push_unchecked(&mut self, value: T) { const unsafe fn push_unchecked(&mut self, value: T) {
unsafe { ptr::write(self.as_mut_ptr().add(self.len), value) } 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 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!(Some(0), v.pop());
/// assert_eq!(None, 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 { if self.len == 0 {
None None
} else { } else {
self.len -= 1; self.len -= 1;
// Safety: MaybeUninit<T> implies ManuallyDrop<T>, // Safety: MaybeUninit<T> implies ManuallyDrop<T>,
// therefore should not get dropped twice // 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()) }) 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)); /// 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 { if index > self.len {
return Err((data, InsertFailed::Bounds(index))); 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 /// - index must be less than self.len
/// - length after insertion must be <= N /// - length after insertion must be <= N
#[inline] #[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(); let base = self.as_mut_ptr();
unsafe { ptr::copy(base.add(index), base.add(index + 1), self.len - index) } 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) { pub fn clear(&mut self) {
// Hopefully copy elision takes care of this lmao // 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 /// 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()); /// assert_eq!(5, v.len());
/// ``` /// ```
pub fn len(&self) -> usize { pub const fn len(&self) -> usize {
self.len self.len
} }
@ -572,7 +585,7 @@ impl<T, const N: usize> Stack<T, N> {
/// assert!(v.is_full()); /// assert!(v.is_full());
/// ``` /// ```
#[inline] #[inline]
pub fn is_full(&self) -> bool { pub const fn is_full(&self) -> bool {
self.len >= N self.len >= N
} }
@ -587,7 +600,7 @@ impl<T, const N: usize> Stack<T, N> {
/// assert!(v.is_empty()); /// assert!(v.is_empty());
/// ``` /// ```
#[inline] #[inline]
pub fn is_empty(&self) -> bool { pub const fn is_empty(&self) -> bool {
self.len == 0 self.len == 0
} }
} }
@ -625,6 +638,7 @@ mod tests {
v.pop(); v.pop();
assert_eq!(v.len(), usize::MAX - 1); assert_eq!(v.len(), usize::MAX - 1);
} }
#[test] #[test]
fn new() { fn new() {
let v: Stack<(), 255> = Stack::new(); let v: Stack<(), 255> = Stack::new();
@ -745,4 +759,19 @@ mod tests {
]); ]);
std::mem::drop(std::hint::black_box(v)); 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

@ -33,7 +33,6 @@ pub enum TokenKind {
Mut, // "mut" Mut, // "mut"
Pub, // "pub" Pub, // "pub"
Return, // "return" Return, // "return"
SelfKw, // "self"
SelfTy, // "Self" SelfTy, // "Self"
Static, // "static" Static, // "static"
Struct, // "struct" Struct, // "struct"
@ -127,7 +126,6 @@ impl Display for TokenKind {
TokenKind::Mut => "mut".fmt(f), TokenKind::Mut => "mut".fmt(f),
TokenKind::Pub => "pub".fmt(f), TokenKind::Pub => "pub".fmt(f),
TokenKind::Return => "return".fmt(f), TokenKind::Return => "return".fmt(f),
TokenKind::SelfKw => "self".fmt(f),
TokenKind::SelfTy => "Self".fmt(f), TokenKind::SelfTy => "Self".fmt(f),
TokenKind::Static => "static".fmt(f), TokenKind::Static => "static".fmt(f),
TokenKind::Struct => "struct".fmt(f), TokenKind::Struct => "struct".fmt(f),
@ -220,7 +218,6 @@ impl FromStr for TokenKind {
"mut" => Self::Mut, "mut" => Self::Mut,
"pub" => Self::Pub, "pub" => Self::Pub,
"return" => Self::Return, "return" => Self::Return,
"self" => Self::SelfKw,
"Self" => Self::SelfTy, "Self" => Self::SelfTy,
"static" => Self::Static, "static" => Self::Static,
"struct" => Self::Struct, "struct" => Self::Struct,

View File

@ -1,12 +1,20 @@
use cl_typeck::{entry::Entry, stage::*, table::Table, type_expression::TypeExpression}; use cl_typeck::{
entry::Entry,
stage::{
infer::{engine::InferenceEngine, error::InferenceError, inference::Inference},
*,
},
table::Table,
type_expression::TypeExpression,
};
use cl_ast::{ use cl_ast::{
Expr, Path, Stmt, Ty,
ast_visitor::{Fold, Visit}, ast_visitor::{Fold, Visit},
desugar::*, desugar::*,
Stmt, Ty,
}; };
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::{inliner::ModuleInliner, Parser}; use cl_parser::{Parser, inliner::ModuleInliner};
use cl_structures::intern::string_interner::StringInterner; use cl_structures::intern::string_interner::StringInterner;
use repline::{error::Error as RlError, prebaked::*}; use repline::{error::Error as RlError, prebaked::*};
use std::{ use std::{
@ -34,7 +42,7 @@ const C_LISTING: &str = "\x1b[38;5;117m";
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let mut prj = Table::default(); let mut prj = Table::default();
let mut parser = Parser::new(Lexer::new(PREAMBLE)); let mut parser = Parser::new("PREAMBLE", Lexer::new(PREAMBLE));
let code = match parser.parse() { let code = match parser.parse() {
Ok(code) => code, Ok(code) => code,
Err(e) => { Err(e) => {
@ -44,8 +52,15 @@ fn main() -> Result<(), Box<dyn Error>> {
}; };
// This code is special - it gets loaded from a hard-coded project directory (for now) // 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 = 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)); 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)?; main_menu(&mut prj)?;
Ok(()) Ok(())
} }
@ -53,7 +68,8 @@ fn main() -> Result<(), Box<dyn Error>> {
fn main_menu(prj: &mut Table) -> Result<(), RlError> { fn main_menu(prj: &mut Table) -> Result<(), RlError> {
banner(); banner();
read_and(C_MAIN, "mu>", "? >", |line| { read_and(C_MAIN, "mu>", "? >", |line| {
match line.trim() { for line in line.trim().split_ascii_whitespace() {
match line {
"c" | "code" => enter_code(prj)?, "c" | "code" => enter_code(prj)?,
"clear" => clear()?, "clear" => clear()?,
"d" | "desugar" => live_desugar()?, "d" | "desugar" => live_desugar()?,
@ -64,6 +80,8 @@ fn main_menu(prj: &mut Table) -> Result<(), RlError> {
"q" | "query" => query_type_expression(prj)?, "q" | "query" => query_type_expression(prj)?,
"r" | "resolve" => resolve_all(prj)?, "r" | "resolve" => resolve_all(prj)?,
"s" | "strings" => print_strings(), "s" | "strings" => print_strings(),
"a" | "all" => infer_all(prj)?,
"t" | "test" => infer_expression(prj)?,
"h" | "help" | "" => { "h" | "help" | "" => {
println!( println!(
"Valid commands are: "Valid commands are:
@ -82,6 +100,7 @@ fn main_menu(prj: &mut Table) -> Result<(), RlError> {
} }
_ => Err(r#"Invalid command. Type "help" to see the list of valid commands."#)?, _ => Err(r#"Invalid command. Type "help" to see the list of valid commands."#)?,
} }
}
Ok(Response::Accept) Ok(Response::Accept)
}) })
} }
@ -91,7 +110,7 @@ fn enter_code(prj: &mut Table) -> Result<(), RlError> {
if line.trim().is_empty() { if line.trim().is_empty() {
return Ok(Response::Break); return Ok(Response::Break);
} }
let code = Parser::new(Lexer::new(line)).parse()?; let code = Parser::new("", Lexer::new(line)).parse()?;
let code = inline_modules(code, ""); let code = inline_modules(code, "");
let code = WhileElseDesugar.fold_file(code); let code = WhileElseDesugar.fold_file(code);
@ -102,9 +121,12 @@ fn enter_code(prj: &mut Table) -> Result<(), RlError> {
fn live_desugar() -> Result<(), RlError> { fn live_desugar() -> Result<(), RlError> {
read_and(C_RESV, "se>", "? >", |line| { read_and(C_RESV, "se>", "? >", |line| {
let code = Parser::new(Lexer::new(line)).parse::<Stmt>()?; let code = Parser::new("", Lexer::new(line)).parse::<Stmt>()?;
println!("Raw, as parsed:\n{C_LISTING}{code}\x1b[0m"); 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); let code = SquashGroups.fold_stmt(code);
println!("SquashGroups\n{C_LISTING}{code}\x1b[0m"); println!("SquashGroups\n{C_LISTING}{code}\x1b[0m");
@ -127,14 +149,48 @@ fn query_type_expression(prj: &mut Table) -> Result<(), RlError> {
if line.trim().is_empty() { if line.trim().is_empty() {
return Ok(Response::Break); return Ok(Response::Break);
} }
// parse it as a path, and convert the path into a borrowed path // A query is comprised of a Ty and a relative Path
let ty: Ty = Parser::new(Lexer::new(line)).parse()?; 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 = ty.evaluate(prj, prj.root())?;
let id = path.evaluate(prj, id)?;
pretty_handle(id.to_entry(prj))?; pretty_handle(id.to_entry(prj))?;
Ok(Response::Accept) Ok(Response::Accept)
}) })
} }
#[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> { fn get_by_id(prj: &mut Table) -> Result<(), RlError> {
use cl_parser::parser::Parse; use cl_parser::parser::Parse;
use cl_structures::index_map::MapIndex; use cl_structures::index_map::MapIndex;
@ -143,7 +199,7 @@ fn get_by_id(prj: &mut Table) -> Result<(), RlError> {
if line.trim().is_empty() { if line.trim().is_empty() {
return Ok(Response::Break); return Ok(Response::Break);
} }
let mut parser = Parser::new(Lexer::new(line)); let mut parser = Parser::new("", Lexer::new(line));
let def_id = match Parse::parse(&mut parser)? { let def_id = match Parse::parse(&mut parser)? {
cl_ast::Literal::Int(int) => int as _, cl_ast::Literal::Int(int) => int as _,
other => Err(format!("Expected integer, got {other}"))?, other => Err(format!("Expected integer, got {other}"))?,
@ -187,6 +243,24 @@ fn resolve_all(table: &mut Table) -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
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})", id.to_entry(table))
}
println!("...Inferred!");
Ok(())
}
fn list_types(table: &mut Table) { fn list_types(table: &mut Table) {
for handle in table.debug_entry_iter() { for handle in table.debug_entry_iter() {
let id = handle.id(); let id = handle.id();
@ -196,6 +270,32 @@ fn list_types(table: &mut Table) {
} }
} }
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(());
};
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(());
}
};
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> { fn import_files(table: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "fi>", "? >", |line| { read_and(C_RESV, "fi>", "? >", |line| {
let line = line.trim(); let line = line.trim();
@ -209,7 +309,7 @@ fn import_files(table: &mut Table) -> Result<(), RlError> {
return Ok(Response::Accept); return Ok(Response::Accept);
}; };
let mut parser = Parser::new(Lexer::new(&file)); let mut parser = Parser::new("", Lexer::new(&file));
let code = match parser.parse() { let code = match parser.parse() {
Ok(code) => inline_modules(code, PathBuf::from(line).parent().unwrap_or("".as_ref())), Ok(code) => inline_modules(code, PathBuf::from(line).parent().unwrap_or("".as_ref())),
Err(e) => { Err(e) => {
@ -320,9 +420,18 @@ fn banner() {
/// Interns a [File](cl_ast::File), returning a static reference to it. /// Interns a [File](cl_ast::File), returning a static reference to it.
fn interned(file: cl_ast::File) -> &'static cl_ast::File { fn interned(file: cl_ast::File) -> &'static cl_ast::File {
use cl_structures::intern::{interned::Interned, typed_interner::TypedInterner}; use cl_structures::intern::typed_interner::TypedInterner;
static INTERNER: LazyLock<TypedInterner<'static, cl_ast::File>> = static INTERNER: LazyLock<TypedInterner<'static, cl_ast::File>> =
LazyLock::new(Default::default); LazyLock::new(Default::default);
Interned::to_ref(&INTERNER.get_or_insert(file)) INTERNER.get_or_insert(file).to_ref()
}
/// 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

@ -8,7 +8,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use cl_ast::{Meta, PathPart, Sym}; use cl_ast::{Expr, Meta, PathPart, Sym};
use cl_structures::span::Span; use cl_structures::span::Span;
use crate::{ use crate::{
@ -46,15 +46,15 @@ impl<'t, 'a> Entry<'t, 'a> {
self.id self.id
} }
pub fn inner(&self) -> &Table<'a> { pub fn inner(&self) -> &'t Table<'a> {
self.table self.table
} }
pub const fn with_id(&self, id: Handle) -> Entry<'_, 'a> { pub const fn with_id(&self, id: Handle) -> Entry<'t, 'a> {
Self { table: self.table, id } Self { table: self.table, id }
} }
pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'_, 'a>> { pub fn nav(&self, path: &[PathPart]) -> Option<Entry<'t, 'a>> {
Some(Entry { id: self.table.nav(self.id, path)?, table: self.table }) Some(Entry { id: self.table.nav(self.id, path)?, table: self.table })
} }
@ -62,27 +62,31 @@ impl<'t, 'a> Entry<'t, 'a> {
self.table.root() self.table.root()
} }
pub fn kind(&self) -> Option<&NodeKind> { pub fn kind(&self) -> Option<&'t NodeKind> {
self.table.kind(self.id) self.table.kind(self.id)
} }
pub fn parent(&self) -> Option<Entry<'_, 'a>> { pub fn parent(&self) -> Option<Entry<'t, 'a>> {
Some(Entry { id: *self.table.parent(self.id)?, ..*self }) Some(Entry { id: *self.table.parent(self.id)?, ..*self })
} }
pub fn children(&self) -> Option<&HashMap<Sym, Handle>> { pub fn children(&self) -> Option<&'t HashMap<Sym, Handle>> {
self.table.children(self.id) self.table.children(self.id)
} }
pub fn imports(&self) -> Option<&HashMap<Sym, Handle>> { pub fn imports(&self) -> Option<&'t HashMap<Sym, Handle>> {
self.table.imports(self.id) self.table.imports(self.id)
} }
pub fn ty(&self) -> Option<&TypeKind> { pub fn bodies(&self) -> Option<&'a Expr> {
self.table.body(self.id)
}
pub fn ty(&self) -> Option<&'t TypeKind> {
self.table.ty(self.id) self.table.ty(self.id)
} }
pub fn span(&self) -> Option<&Span> { pub fn span(&self) -> Option<&'t Span> {
self.table.span(self.id) self.table.span(self.id)
} }
@ -90,7 +94,7 @@ impl<'t, 'a> Entry<'t, 'a> {
self.table.meta(self.id) self.table.meta(self.id)
} }
pub fn source(&self) -> Option<&Source<'a>> { pub fn source(&self) -> Option<&'t Source<'a>> {
self.table.source(self.id) self.table.source(self.id)
} }
@ -154,6 +158,10 @@ impl<'t, 'a> EntryMut<'t, 'a> {
self.table.add_child(self.id, name, child) 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> { pub fn set_ty(&mut self, kind: TypeKind) -> Option<TypeKind> {
self.table.set_ty(self.id, kind) self.table.set_ty(self.id, kind)
} }

View File

@ -18,8 +18,10 @@ impl fmt::Display for Entry<'_, '_> {
if let Some(ty) = self.ty() { if let Some(ty) = self.ty() {
match 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::Instance(id) => write!(f, "{}", self.with_id(*id)),
TypeKind::Intrinsic(kind) => write!(f, "{kind}"), TypeKind::Primitive(kind) => write!(f, "{kind}"),
TypeKind::Adt(adt) => write_adt(adt, self, f), TypeKind::Adt(adt) => write_adt(adt, self, f),
&TypeKind::Ref(id) => { &TypeKind::Ref(id) => {
f.write_str("&")?; f.write_str("&")?;
@ -64,13 +66,10 @@ fn write_adt(adt: &Adt, h: &Entry, f: &mut impl Write) -> fmt::Result {
let mut variants = variants.iter(); let mut variants = variants.iter();
separate(", ", || { separate(", ", || {
variants.next().map(|(name, def)| { variants.next().map(|(name, def)| {
move |f: &mut Delimit<_>| match def { move |f: &mut Delimit<_>| {
Some(def) => {
write!(f, "{name}: ")?; write!(f, "{name}: ")?;
write_name_or(h.with_id(*def), f) write_name_or(h.with_id(*def), f)
} }
None => write!(f, "{name}"),
}
}) })
})(f.delimit_with("enum {", "}")) })(f.delimit_with("enum {", "}"))
} }

View File

@ -25,7 +25,7 @@ impl Source<'_> {
match self { match self {
Source::Root => None, Source::Root => None,
Source::Module(v) => Some(v.name), Source::Module(v) => Some(v.name),
Source::Alias(v) => Some(v.to), Source::Alias(v) => Some(v.name),
Source::Enum(v) => Some(v.name), Source::Enum(v) => Some(v.name),
Source::Variant(v) => Some(v.name), Source::Variant(v) => Some(v.name),
Source::Struct(v) => Some(v.name), Source::Struct(v) => Some(v.name),

View File

@ -13,9 +13,9 @@ use cl_ast::*;
pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> { pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
if let Some(meta) = table.meta(node) { if let Some(meta) = table.meta(node) {
for meta @ Meta { name, kind } in meta { for meta @ Meta { name, kind } in meta {
if let ("intrinsic", MetaKind::Equals(Literal::String(s))) = (&**name, kind) { if let ("lang", MetaKind::Equals(Literal::String(s))) = (&**name, kind) {
let kind = let kind =
TypeKind::Intrinsic(s.parse().map_err(|_| Error::BadMeta(meta.clone()))?); TypeKind::Primitive(s.parse().map_err(|_| Error::BadMeta(meta.clone()))?);
table.set_ty(node, kind); table.set_ty(node, kind);
return Ok(()); return Ok(());
} }
@ -31,7 +31,7 @@ pub fn categorize(table: &mut Table, node: Handle) -> CatResult<()> {
Source::Module(_) => Ok(()), Source::Module(_) => Ok(()),
Source::Alias(a) => cat_alias(table, node, a), Source::Alias(a) => cat_alias(table, node, a),
Source::Enum(e) => cat_enum(table, node, e), Source::Enum(e) => cat_enum(table, node, e),
Source::Variant(_) => Ok(()), Source::Variant(v) => cat_variant(table, node, v),
Source::Struct(s) => cat_struct(table, node, s), Source::Struct(s) => cat_struct(table, node, s),
Source::Const(c) => cat_const(table, node, c), Source::Const(c) => cat_const(table, node, c),
Source::Static(s) => cat_static(table, node, s), Source::Static(s) => cat_static(table, node, s),
@ -65,14 +65,14 @@ fn cat_alias(table: &mut Table, node: Handle, a: &Alias) -> CatResult<()> {
} }
fn cat_struct(table: &mut Table, node: Handle, s: &Struct) -> CatResult<()> { fn cat_struct(table: &mut Table, node: Handle, s: &Struct) -> CatResult<()> {
let parent = parent(table, node); let Struct { name: _, gens: _, kind } = s;
let Struct { name: _, kind } = s; // TODO: Generics
let kind = match kind { let kind = match kind {
StructKind::Empty => TypeKind::Adt(Adt::UnitStruct), StructKind::Empty => TypeKind::Adt(Adt::UnitStruct),
StructKind::Tuple(types) => { StructKind::Tuple(types) => {
let mut out = vec![]; let mut out = vec![];
for ty in types { for ty in types {
out.push((Visibility::Public, ty.evaluate(table, parent)?)) out.push((Visibility::Public, ty.evaluate(table, node)?))
} }
TypeKind::Adt(Adt::TupleStruct(out)) TypeKind::Adt(Adt::TupleStruct(out))
} }
@ -98,51 +98,56 @@ fn cat_member(
Ok((*name, *vis, ty.evaluate(table, node)?)) Ok((*name, *vis, ty.evaluate(table, node)?))
} }
fn cat_enum<'a>(table: &mut Table<'a>, node: Handle, e: &'a Enum) -> CatResult<()> { fn cat_enum<'a>(_table: &mut Table<'a>, _node: Handle, e: &'a Enum) -> CatResult<()> {
let Enum { name: _, kind } = e; let Enum { name: _, gens: _, variants: _ } = e;
let kind = match kind {
EnumKind::NoVariants => TypeKind::Adt(Adt::Enum(vec![])),
EnumKind::Variants(variants) => {
let mut out_vars = vec![];
for v in variants {
out_vars.push(cat_variant(table, node, v)?)
}
TypeKind::Adt(Adt::Enum(out_vars))
}
};
table.set_ty(node, kind); // table.set_ty(node, kind);
Ok(()) Ok(())
} }
fn cat_variant<'a>( fn cat_variant<'a>(table: &mut Table<'a>, node: Handle, v: &'a Variant) -> CatResult<()> {
table: &mut Table<'a>, let Variant { name, kind, body } = v;
node: Handle, let parent = table.parent(node).copied().unwrap_or(table.root());
v: &'a Variant, table.add_child(parent, *name, node);
) -> CatResult<(Sym, Option<Handle>)> { match (kind, body) {
let parent = parent(table, node); (StructKind::Empty, None) => {
let Variant { name, kind } = v; table.set_ty(node, TypeKind::Adt(Adt::UnitStruct));
match kind { Ok(())
VariantKind::Plain => Ok((*name, None)),
VariantKind::CLike(c) => todo!("enum-variant constant {c}"),
VariantKind::Tuple(ty) => {
let ty = ty
.evaluate(table, parent)
.map_err(|e| Error::TypeEval(e, " while categorizing a variant"))?;
Ok((*name, Some(ty)))
} }
VariantKind::Struct(members) => { (StructKind::Empty, Some(c)) => {
table.set_body(node, c);
table.set_ty(node, TypeKind::Adt(Adt::UnitStruct));
Ok(())
}
(StructKind::Tuple(ty), None) => {
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), None) => {
let mut out = vec![]; let mut out = vec![];
for m in members { for StructMember { vis, name, ty } in members {
out.push(cat_member(table, node, m)?) let ty = ty.evaluate(table, node)?;
} out.push((*name, *vis, ty));
let kind = TypeKind::Adt(Adt::Struct(out));
let mut h = node.to_entry_mut(table); let mut this = node.to_entry_mut(table);
let mut variant = h.new_entry(NodeKind::Type); let mut child = this.new_entry(NodeKind::Type);
variant.set_source(Source::Variant(v)); child.set_source(Source::Variant(v));
variant.set_ty(kind); child.set_ty(TypeKind::Instance(ty));
Ok((*name, Some(variant.id())))
let child = child.id();
this.add_child(*name, child);
}
table.set_ty(node, TypeKind::Adt(Adt::Struct(out)));
Ok(())
}
(_, Some(body)) => {
panic!("Unexpected body `{body}` in enum variant `{v}`")
} }
} }
} }
@ -168,10 +173,9 @@ fn cat_static(table: &mut Table, node: Handle, s: &Static) -> CatResult<()> {
} }
fn cat_function(table: &mut Table, node: Handle, f: &Function) -> CatResult<()> { fn cat_function(table: &mut Table, node: Handle, f: &Function) -> CatResult<()> {
let parent = parent(table, node);
let kind = TypeKind::Instance( let kind = TypeKind::Instance(
f.sign f.sign
.evaluate(table, parent) .evaluate(table, node)
.map_err(|e| Error::TypeEval(e, " while categorizing a function"))?, .map_err(|e| Error::TypeEval(e, " while categorizing a function"))?,
); );
table.set_ty(node, kind); table.set_ty(node, kind);
@ -206,7 +210,6 @@ type CatResult<T> = Result<T, Error>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Error { pub enum Error {
BadMeta(Meta), BadMeta(Meta),
Recursive(Handle),
TypeEval(TypeEval, &'static str), TypeEval(TypeEval, &'static str),
} }
@ -219,10 +222,7 @@ impl From<TypeEval> for Error {
impl std::fmt::Display for Error { impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Error::BadMeta(meta) => write!(f, "Unknown meta attribute: #[{meta}]"), Error::BadMeta(meta) => write!(f, "Unknown attribute: #[{meta}]"),
Error::Recursive(id) => {
write!(f, "Encountered recursive type without indirection: {id}")
}
Error::TypeEval(e, during) => write!(f, "{e}{during}"), Error::TypeEval(e, during) => write!(f, "{e}{during}"),
} }
} }

View File

@ -5,244 +5,8 @@
//! [1]: https://github.com/tcr/rust-hindley-milner/ //! [1]: https://github.com/tcr/rust-hindley-milner/
//! [2]: https://github.com/rob-smallshire/hindley-milner-python //! [2]: https://github.com/rob-smallshire/hindley-milner-python
use cl_ast::Sym; pub mod engine;
use core::fmt;
use std::{cell::RefCell, rc::Rc};
/* pub mod inference;
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 mod error;
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::stage::infer::*;
/// 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::stage::infer::*;
/// 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

@ -0,0 +1,528 @@
use super::error::InferenceError;
use crate::{
entry::Entry,
handle::Handle,
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!
*/
pub struct InferenceEngine<'table, 'a> {
pub(super) table: &'table mut Table<'a>,
/// The current working node
pub(crate) at: Handle,
/// The current breakset
pub(crate) bset: Handle,
/// The current returnset
pub(crate) rset: Handle,
}
impl<'table, 'a> InferenceEngine<'table, 'a> {
/// 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 {
let never = table.anon_type(TypeKind::Never);
Self { at, table, bset: never, rset: never }
}
/// 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, rset: self.rset }
}
pub fn infer_all(&mut self) -> Vec<(Handle, InferenceError)> {
let iter = self.table.handle_iter();
let mut res = Vec::new();
for handle in iter {
let mut eng = self.at(handle);
// TODO: use sources instead of bodies, and infer the type globally
let Some(body) = eng.table.body(handle) else {
continue;
};
eprintln!("Evaluating body {body}");
match body.infer(&mut eng) {
Ok(ty) => println!("=> {}", eng.table.entry(ty)),
Err(e) => {
match &e {
&InferenceError::Mismatch(a, b) => {
eprintln!(
"=> Mismatched types: {}, {}",
eng.table.entry(a),
eng.table.entry(b)
);
}
&InferenceError::Recursive(a, b) => {
eprintln!(
"=> Recursive types: {}, {}",
eng.table.entry(a),
eng.table.entry(b)
);
}
e => eprintln!("=> {e}"),
}
res.push((handle, e))
}
}
}
res
}
/// Constructs a new InferenceEngine with the
pub fn at(&mut self, at: Handle) -> InferenceEngine<'_, 'a> {
InferenceEngine { at, ..self.scoped() }
}
pub fn open_bset(&mut self) -> InferenceEngine<'_, 'a> {
InferenceEngine { bset: self.new_var(), ..self.scoped() }
}
pub fn open_rset(&mut self) -> InferenceEngine<'_, 'a> {
InferenceEngine { rset: self.new_var(), ..self.scoped() }
}
/// Constructs an [Entry] out of a [Handle], for ease of use
pub fn entry(&self, of: Handle) -> Entry<'_, 'a> {
self.table.entry(of)
}
#[deprecated = "Use dedicated methods instead."]
pub fn from_type_kind(&mut self, kind: TypeKind) -> Handle {
// TODO: preserve type heirarchy (for, i.e., reference types)
self.table.anon_type(kind)
}
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()
}
/// 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: Sym) -> Option<Handle> {
// TODO: keep a map of primitives in the table root
self.table.get_by_sym(self.table.root(), &name)
}
pub fn never(&mut self) -> Handle {
self.table.anon_type(TypeKind::Never)
}
pub fn empty(&mut self) -> Handle {
self.table.anon_type(TypeKind::Empty)
}
pub fn bool(&self) -> Handle {
self.primitive("bool".into())
.expect("There should be a type named bool.")
}
pub fn char(&self) -> Handle {
self.primitive("char".into())
.expect("There should be a type named char.")
}
pub fn str(&self) -> Handle {
self.primitive("str".into())
.expect("There should be a type named str.")
}
pub fn u32(&self) -> Handle {
self.primitive("u32".into())
.expect("There should be a type named u32.")
}
pub fn usize(&self) -> Handle {
self.primitive("usize".into())
.expect("There should be a type named usize.")
}
/// Creates a new inferred-integer literal
pub fn integer_literal(&mut self) -> Handle {
let h = self.table.new_entry(self.at, NodeKind::Local);
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::Local);
self.table.set_ty(h, TypeKind::Primitive(Primitive::Float));
h
}
/// Enters a new scope
pub fn local_scope(&mut self) {
let scope = self.table.new_entry(self.at, NodeKind::Local);
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::Local);
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 {
let entry = self.table.entry(ty);
let Some(ty) = entry.ty() else {
return false;
};
match ty {
TypeKind::Inferred => false,
TypeKind::Variable => true,
&TypeKind::Array(h, _) => self.is_generic(h),
&TypeKind::Instance(h) => self.is_generic(h),
TypeKind::Primitive(_) => false,
TypeKind::Adt(Adt::Enum(tys)) => tys.iter().any(|(_, ty)| self.is_generic(*ty)),
TypeKind::Adt(Adt::Struct(tys)) => tys.iter().any(|&(_, _, ty)| self.is_generic(ty)),
TypeKind::Adt(Adt::TupleStruct(tys)) => tys.iter().any(|&(_, ty)| self.is_generic(ty)),
TypeKind::Adt(Adt::UnitStruct) => false,
TypeKind::Adt(Adt::Union(tys)) => tys.iter().any(|&(_, ty)| self.is_generic(ty)),
&TypeKind::Ref(h) => self.is_generic(h),
&TypeKind::Slice(h) => self.is_generic(h),
TypeKind::Tuple(handles) => handles.iter().any(|&ty| self.is_generic(ty)),
&TypeKind::FnSig { args, rety } => self.is_generic(args) || self.is_generic(rety),
TypeKind::Empty | TypeKind::Never | TypeKind::Module => false,
}
}
/// 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(ty) = entry.ty().cloned() else {
return ty;
};
match ty {
TypeKind::Variable => self.new_var(),
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::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 })
}
_ => self.table.anon_type(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(other) => self.occurs_in(this, *other),
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::Empty
| TypeKind::Never
| 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));
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::Inferred, _) => {
self.set_instance(ah, bh);
Ok(())
}
(_, TypeKind::Inferred) => self.unify(bh, ah),
(TypeKind::Variable, _) => {
self.set_instance(ah, bh);
Ok(())
}
(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::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::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::Empty, TypeKind::Tuple(t)) | (TypeKind::Tuple(t), TypeKind::Empty)
if t.is_empty() =>
{
Ok(())
}
(TypeKind::Never, _) | (_, TypeKind::Never) => Ok(()),
(a, b) if a == b => Ok(()),
_ => Err(InferenceError::Mismatch(ah, bh)),
}
}
}

View File

@ -0,0 +1,33 @@
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),
}
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!"),
}
}
}

View File

@ -0,0 +1,698 @@
//! The [Inference] trait is the heart of cl-typeck's type inference.
//!
//! Each syntax structure must describe how to unify its types.
use std::iter;
use super::{engine::InferenceEngine, error::InferenceError};
use crate::{
handle::Handle,
table::NodeKind,
type_expression::TypeExpression,
type_kind::{Adt, TypeKind},
};
use cl_ast::*;
// TODO: "Infer" the types of Items
type IfResult = Result<Handle, InferenceError>;
pub trait Inference<'a> {
/// Performs type inference
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult;
}
impl<'a> Inference<'a> for cl_ast::Expr {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
self.kind.infer(e)
}
}
impl<'a> Inference<'a> for cl_ast::ExprKind {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
match self {
ExprKind::Empty => Ok(e.empty()),
ExprKind::Closure(_) => todo!("Infer the type of a closure"),
ExprKind::Tuple(tuple) => tuple.infer(e),
ExprKind::Structor(structor) => structor.infer(e),
ExprKind::Array(array) => array.infer(e),
ExprKind::ArrayRep(array_rep) => array_rep.infer(e),
ExprKind::AddrOf(addr_of) => addr_of.infer(e),
ExprKind::Quote(quote) => quote.infer(e),
ExprKind::Literal(literal) => literal.infer(e),
ExprKind::Group(group) => group.infer(e),
ExprKind::Block(block) => block.infer(e),
ExprKind::Assign(assign) => assign.infer(e),
ExprKind::Modify(modify) => modify.infer(e),
ExprKind::Binary(binary) => binary.infer(e),
ExprKind::Unary(unary) => unary.infer(e),
ExprKind::Member(member) => member.infer(e),
ExprKind::Index(index) => index.infer(e),
ExprKind::Path(path) => path.infer(e),
ExprKind::Cast(cast) => cast.infer(e),
ExprKind::Let(l) => l.infer(e),
ExprKind::Match(m) => m.infer(e),
ExprKind::While(w) => w.infer(e),
ExprKind::If(i) => i.infer(e),
ExprKind::For(f) => f.infer(e),
ExprKind::Break(b) => b.infer(e),
ExprKind::Return(r) => r.infer(e),
ExprKind::Continue => Ok(e.never()),
}
}
}
impl<'a> Inference<'a> for Tuple {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Tuple { exprs } = self;
exprs
.iter()
// Infer each member
.map(|expr| expr.infer(e))
// Construct tuple
.collect::<Result<Vec<_>, InferenceError>>()
// Return tuple
.map(|tys| e.new_tuple(tys))
}
}
impl<'a> Inference<'a> for Structor {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Structor { to, init } = self;
// Evaluate the path in the current context
let to = to.infer(e)?;
match e.entry(to).ty() {
// Typecheck the fielders against the fields
Some(TypeKind::Adt(Adt::Struct(fields))) => {
if init.len() != fields.len() {
return Err(InferenceError::FieldCount(to, fields.len(), init.len()));
}
let fields = fields.clone(); // todo: fix this somehow.
let mut field_inits: std::collections::HashMap<_, _> = init
.iter()
.map(|Fielder { name, init }| (name, init))
.collect();
// Unify fields with fielders
for (name, _vis, ty) in fields {
match field_inits.remove(&name) {
Some(Some(field)) => {
let init_ty = field.infer(e)?;
e.unify(init_ty, ty)?;
}
Some(None) => {
// Get name in scope
let init_ty = e
.table
.get_by_sym(e.at, &name)
.ok_or_else(|| InferenceError::NotFound(Path::from(name)))?;
e.unify(init_ty, ty)?;
}
None => Err(InferenceError::NotFound(Path::from(name)))?,
}
}
Ok(to)
}
_ => Err(InferenceError::NotFound(self.to.clone())),
}
}
}
impl<'a> Inference<'a> for Array {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Array { values } = self;
let out = e.new_var();
for value in values {
let ty = value.infer(e)?;
e.unify(out, ty)?;
}
Ok(e.new_array(out, values.len()))
}
}
impl<'a> Inference<'a> for ArrayRep {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let ArrayRep { value, repeat } = self;
let ty = value.infer(e)?;
Ok(e.new_array(ty, *repeat))
}
}
impl<'a> Inference<'a> for AddrOf {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let AddrOf { mutable: _, expr } = self;
// TODO: mut ref
let ty = expr.infer(e)?;
Ok(e.new_ref(ty))
}
}
impl<'a> Inference<'a> for Quote {
fn infer(&'a self, _e: &mut InferenceEngine<'_, 'a>) -> IfResult {
todo!("Quote: {self}")
}
}
impl<'a> Inference<'a> for Literal {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let ty = match self {
Literal::Bool(_) => e.bool(),
Literal::Char(_) => e.char(),
Literal::Int(_) => e.integer_literal(),
Literal::Float(_) => e.float_literal(),
Literal::String(_) => {
let str_ty = e.str();
e.new_ref(str_ty)
}
};
Ok(e.new_inst(ty))
}
}
impl<'a> Inference<'a> for Group {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Group { expr } = self;
expr.infer(e)
}
}
impl<'a> Inference<'a> for Block {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Block { stmts } = self;
let mut e = e.block_scope();
let empty = e.empty();
if let [stmts @ .., ret] = stmts.as_slice() {
for stmt in stmts {
match (&stmt.kind, &stmt.semi) {
(StmtKind::Expr(expr), Semi::Terminated) => {
expr.infer(&mut e)?;
}
(StmtKind::Expr(expr), Semi::Unterminated) => {
let ty = expr.infer(&mut e)?;
e.unify(ty, empty)?;
}
_ => {}
}
}
match (&ret.kind, &ret.semi) {
(StmtKind::Expr(expr), Semi::Terminated) => {
expr.infer(&mut e)?;
}
(StmtKind::Expr(expr), Semi::Unterminated) => {
return expr.infer(&mut e);
}
_ => {}
}
}
Ok(empty)
}
}
impl<'a> Inference<'a> for Assign {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Assign { parts } = self;
let (head, tail) = parts.as_ref();
// Infer the tail expression
let tail = tail.infer(e)?;
// Infer the head expression
let head = head.infer(e)?;
// Unify head and tail
e.unify(head, tail)?;
// Return Empty
Ok(e.empty())
}
}
impl<'a> Inference<'a> for Modify {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Modify { kind: _, parts } = self;
let (head, tail) = parts.as_ref();
// Infer the tail expression
let tail = tail.infer(e)?;
// Infer the head expression
let head = head.infer(e)?;
// TODO: Search within the head type for `(op)_assign`
e.unify(head, tail)?;
// TODO: Typecheck `op_assign(&mut head, tail)`
Ok(e.empty())
}
}
impl<'a> Inference<'a> for Binary {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
use BinaryKind as Bk;
let Binary { kind, parts } = self;
let (head, tail) = parts.as_ref();
// Infer the tail expression
let tail = tail.infer(e)?;
// Infer the head expression
let head = head.infer(e)?;
let head = e.prune(head);
// TODO: Search within the head type for `(op)`
match kind {
BinaryKind::Call => match e.entry(head).ty() {
Some(TypeKind::Adt(Adt::TupleStruct(types))) => {
let Some(TypeKind::Tuple(values)) = e.entry(tail).ty() else {
Err(InferenceError::Mismatch(head, tail))?
};
if types.len() != values.len() {
Err(InferenceError::FieldCount(head, types.len(), values.len()))?
}
let pairs = types
.iter()
.zip(values.iter())
.map(|(&(_vis, ty), &value)| (ty, value))
.collect::<Vec<_>>();
for (ty, value) in pairs {
e.unify(ty, value)?;
}
Ok(head)
}
Some(&TypeKind::FnSig { args, rety }) => {
e.unify(tail, args)?;
Ok(rety)
}
_ => Err(InferenceError::Mismatch(head, tail))?,
},
Bk::Lt | Bk::LtEq | Bk::Equal | Bk::NotEq | Bk::GtEq | Bk::Gt => {
e.unify(head, tail)?;
Ok(e.bool())
}
Bk::LogAnd | Bk::LogOr | Bk::LogXor => {
let bool = e.bool();
e.unify(head, bool)?;
e.unify(tail, bool)?;
Ok(bool)
}
Bk::RangeExc => todo!("Ranges in the type checker"),
Bk::RangeInc => todo!("Ranges in the type checker"),
Bk::Shl | Bk::Shr => {
let shift_amount = e.u32();
e.unify(tail, shift_amount)?;
Ok(head)
}
Bk::BitAnd
| Bk::BitOr
| Bk::BitXor
| Bk::Add
| Bk::Sub
| Bk::Mul
| Bk::Div
| Bk::Rem => {
// Typecheck op(head, tail)
e.unify(head, tail)?;
Ok(head)
}
}
}
}
impl<'a> Inference<'a> for Unary {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Unary { kind, tail } = self;
match kind {
UnaryKind::Deref => {
let tail = tail.infer(e)?;
// TODO: get the base type
match e.entry(tail).ty() {
Some(&TypeKind::Ref(h)) => Ok(h),
other => todo!("Deref {other:?}"),
}
}
UnaryKind::Loop => {
let mut e = e.block_scope();
// Enter a new breakset
let mut e = e.open_bset();
// Infer the fail branch
let tail = tail.infer(&mut e)?;
// Unify the pass branch with Empty
let empt = e.empty();
e.unify(tail, empt)?;
// Return breakset
Ok(e.bset)
}
_op => {
// Infer the tail expression
let tail = tail.infer(e)?;
// TODO: Search within the tail type for `(op)`
Ok(tail)
}
}
}
}
impl<'a> Inference<'a> for Member {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Member { head, kind } = self;
// Infer the head expression
let head = head.infer(e)?;
// Get the type of head
let head = e.prune(head);
let ty = e.entry(head);
// Search within the head type for the memberkind
match kind {
MemberKind::Call(name, tuple) => {
if let Some((args, rety)) = e.get_fn(ty.id(), *name) {
let values = iter::once(Ok(e.new_ref(head)))
// infer for each member-
.chain(tuple.exprs.iter().map(|expr| expr.infer(e)))
// Construct tuple
.collect::<Result<Vec<_>, InferenceError>>()
// Return tuple
.map(|tys| e.new_tuple(tys))?;
e.unify(args, values)?;
Ok(rety)
} else {
Err(InferenceError::NotFound(Path::from(*name)))
}
}
MemberKind::Struct(name) => match ty.nav(&[PathPart::Ident(*name)]) {
Some(ty) => Ok(ty.id()),
None => Err(InferenceError::NotFound(Path::from(*name))),
},
MemberKind::Tuple(Literal::Int(idx)) => match ty.ty() {
Some(TypeKind::Tuple(tys)) => tys
.get(*idx as usize)
.copied()
.ok_or(InferenceError::FieldCount(head, tys.len(), *idx as usize)),
Some(TypeKind::Adt(Adt::TupleStruct(tys))) => tys
.get(*idx as usize)
.map(|(_vis, ty)| *ty)
.ok_or(InferenceError::FieldCount(head, tys.len(), *idx as usize)),
_ => Err(InferenceError::Mismatch(ty.id(), e.table.root())),
},
_ => Err(InferenceError::Mismatch(ty.id(), ty.root())),
}
// Type is required to be inferred at this point.
}
}
impl<'a> Inference<'a> for Index {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Index { head, indices } = self;
let usize = e.usize();
// Infer the head expression
let head = head.infer(e)?;
let mut head = e.prune(head);
// For each index expression:
for index in indices {
// Infer the index type
let index = index.infer(e)?;
if let Some((args, rety)) = e.get_fn(head, "index".into()) {
// Unify args and tuple (&head, index)
let selfty = e.new_ref(head);
let tupty = e.new_tuple(vec![selfty, index]);
e.unify(args, tupty)?;
head = e.prune(rety);
continue;
}
// Decide whether the head can be indexed by that type
// TODO: check for a `.index` method on the type
match e.entry(head).ty().unwrap() {
&TypeKind::Slice(handle) | &TypeKind::Array(handle, _) => {
e.unify(usize, index)?;
head = e.prune(handle);
}
other => todo!("Indexing on type {other}"),
}
// head = result of indexing head
}
Ok(head)
}
}
impl<'a> Inference<'a> for Cast {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Cast { head, ty } = self;
// Infer the head expression
let _head = head.infer(e)?;
// Evaluate the type
let ty = ty
.evaluate(e.table, e.at)
.map_err(InferenceError::AnnotationEval)?;
// Decide whether the type is castable
// TODO: not deciding is absolutely unsound!!!
// Return the type
Ok(ty)
}
}
impl<'a> Inference<'a> for Path {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
e.by_name(self)
.map_err(|_| InferenceError::NotFound(self.clone()))
}
}
impl<'a> Inference<'a> for Let {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Let { mutable: _, name, ty, init } = self;
let ty = match ty {
Some(ty) => ty
.evaluate(e.table, e.at)
.map_err(InferenceError::AnnotationEval)?,
None => e.new_var(),
};
// Infer the initializer
if let Some(init) = init {
// Unify the initializer and the ty
let initty = init.infer(e)?;
e.unify(ty, initty)?;
}
// Deep copy the ty, if it exists
let ty = e.deep_clone(ty);
// Enter a local scope (modifies the current scope)
e.local_scope();
// Infer the pattern
let patty = name.infer(e)?;
// Unify the pattern and the ty
e.unify(ty, patty)?;
// `if let` returns whether the pattern succeeded or not
Ok(e.bool())
}
}
impl<'a> Inference<'a> for Match {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Match { scrutinee, arms } = self;
// Infer the scrutinee
let scrutinee = scrutinee.infer(e)?;
let mut out = None;
// For each pattern:
for MatchArm(pat, expr) in arms {
let mut scope = e.block_scope();
// Infer the pattern
let pat = pat.infer(&mut scope)?;
// Unify it with the scrutinee
scope.unify(scrutinee, pat)?;
// Infer the Expr
let expr = expr.infer(&mut scope)?;
// Unify the expr with the out variable
match out {
Some(ty) => e.unify(ty, expr)?,
None => out = Some(expr),
}
}
// Return out. If there are no arms, assume Never.
match out {
Some(ty) => Ok(ty),
None => Ok(e.never()),
}
}
}
impl<'a> Inference<'a> for Pattern {
// TODO: This is the wrong way to typeck pattern matching.
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
match self {
Pattern::Name(name) => {
// Evaluating a pattern creates and enters a new scope.
// Surely this will cause zero problems.
let node = e.table.new_entry(e.at, NodeKind::Local);
e.table.set_ty(node, TypeKind::Variable);
e.table.add_child(e.at, *name, node);
e.at = node;
Ok(node)
}
Pattern::Path(path) => {
// Evaluating a path pattern puts type constraints on the scrutinee
path.evaluate(e.table, e.at)
.map_err(|_| InferenceError::NotFound(path.clone()))
}
Pattern::Literal(literal) => literal.infer(e),
Pattern::Rest(Some(pat)) => pat.infer(e), // <-- glaring soundness holes
Pattern::Rest(_) => todo!("Fix glaring soundness holes in pattern"),
Pattern::Ref(_, pattern) => {
let ty = pattern.infer(e)?;
Ok(e.new_ref(ty))
}
Pattern::RangeExc(pat1, pat2) => {
let ty1 = pat1.infer(e)?;
let ty2 = pat2.infer(e)?;
e.unify(ty1, ty2)?;
Ok(ty1)
}
Pattern::RangeInc(pat1, pat2) => {
let ty1 = pat1.infer(e)?;
let ty2 = pat2.infer(e)?;
e.unify(ty1, ty2)?;
Ok(ty1)
}
Pattern::Tuple(patterns) => {
let tys = patterns
.iter()
.map(|pat| pat.infer(e))
.collect::<Result<Vec<Handle>, InferenceError>>()?;
Ok(e.new_tuple(tys))
}
Pattern::Array(patterns) => match patterns.as_slice() {
// TODO: rest patterns here
[one, rest @ ..] => {
let ty = one.infer(e)?;
for rest in rest {
let ty2 = rest.infer(e)?;
e.unify(ty, ty2)?;
}
Ok(e.new_slice(ty))
}
[] => {
let ty = e.new_var();
Ok(e.new_slice(ty))
}
},
Pattern::Struct(_path, _items) => todo!("Struct patterns"),
Pattern::TupleStruct(_path, _patterns) => todo!("Tuple struct patterns"),
}
}
}
impl<'a> Inference<'a> for While {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let While { cond, pass, fail } = self;
// Infer the condition
let cond = cond.infer(e)?;
// Unify the condition with bool
let bool = e.bool();
e.unify(bool, cond)?;
// Infer the fail branch
let fail = fail.infer(e)?;
// Unify the fail branch with breakset
let mut e = InferenceEngine { bset: fail, ..e.scoped() };
// Infer the pass branch
let pass = pass.infer(&mut e)?;
// Unify the pass branch with Empty
let empt = e.empty();
e.unify(pass, empt)?;
// Return breakset
Ok(e.bset)
}
}
impl<'a> Inference<'a> for If {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let If { cond, pass, fail } = self;
// Do inference on the condition'
let cond = cond.infer(e)?;
// Unify the condition with bool
let bool = e.bool();
e.unify(bool, cond)?;
// Do inference on the pass branch
let pass = pass.infer(e)?;
// Do inference on the fail branch
let fail = fail.infer(e)?;
// Unify pass and fail
e.unify(pass, fail)?;
// Return the result
Ok(pass)
}
}
impl<'a> Inference<'a> for For {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let For { bind, cond, pass, fail } = self;
let mut e = e.block_scope();
let bind = bind.infer(&mut e)?;
// What does it mean to be iterable? Why, `next()`, of course!
let cond = cond.infer(&mut e)?;
let cond = e.prune(cond);
if let Some((args, rety)) = e.get_fn(cond, "next".into()) {
// Check that the args are correct
let params = vec![e.new_ref(cond)];
let params = e.new_tuple(params);
e.unify(args, params)?;
e.unify(rety, bind)?;
}
// Enter a new breakset
let mut e = e.open_bset();
// Infer the fail branch
let fail = fail.infer(&mut e)?;
// Unify the fail branch with breakset
let mut e = InferenceEngine { bset: fail, ..e.scoped() };
e.bset = fail;
// Infer the pass branch
let pass = pass.infer(&mut e)?;
// Unify the pass branch with Empty
let empt = e.empty();
e.unify(pass, empt)?;
// Return breakset
Ok(e.bset)
}
}
impl<'a> Inference<'a> for Else {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
self.body.infer(e)
}
}
impl<'a> Inference<'a> for Break {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Break { body } = self;
// Infer the body of the break
let ty = body.infer(e)?;
// Unify it with the breakset of the loop
e.unify(ty, e.bset)?;
// Return never
Ok(e.never())
}
}
impl<'a> Inference<'a> for Return {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
let Return { body } = self;
// Infer the body of the return
let ty = body.infer(e)?;
// Unify it with the return-set of the function
e.unify(ty, e.rset)?;
// Return never
Ok(e.never())
}
}
impl<'a, I: Inference<'a>> Inference<'a> for Option<I> {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
match self {
Some(expr) => expr.infer(e),
None => Ok(e.empty()),
}
}
}
impl<'a, I: Inference<'a>> Inference<'a> for Box<I> {
fn infer(&'a self, e: &mut InferenceEngine<'_, 'a>) -> IfResult {
self.as_ref().infer(e)
}
}

View File

@ -4,8 +4,12 @@ use crate::{
handle::Handle, handle::Handle,
source::Source, source::Source,
table::{NodeKind, Table}, table::{NodeKind, Table},
type_kind::TypeKind,
};
use cl_ast::{
ItemKind, Sym,
ast_visitor::{Visit, Walk},
}; };
use cl_ast::{ast_visitor::Visit, ItemKind, Sym};
#[derive(Debug)] #[derive(Debug)]
pub struct Populator<'t, 'a> { pub struct Populator<'t, 'a> {
@ -33,7 +37,7 @@ impl<'t, 'a> Populator<'t, 'a> {
impl<'a> Visit<'a> for Populator<'_, 'a> { impl<'a> Visit<'a> for Populator<'_, 'a> {
fn visit_item(&mut self, i: &'a cl_ast::Item) { fn visit_item(&mut self, i: &'a cl_ast::Item) {
let cl_ast::Item { extents, attrs, vis, kind } = i; let cl_ast::Item { span, attrs, vis: _, kind } = i;
// TODO: this, better, better. // TODO: this, better, better.
let entry_kind = match kind { let entry_kind = match kind {
ItemKind::Alias(_) => NodeKind::Type, ItemKind::Alias(_) => NodeKind::Type,
@ -50,82 +54,117 @@ impl<'a> Visit<'a> for Populator<'_, 'a> {
}; };
let mut entry = self.new_entry(entry_kind); let mut entry = self.new_entry(entry_kind);
entry.inner.set_span(*extents); entry.inner.set_span(*span);
entry.inner.set_meta(&attrs.meta); entry.inner.set_meta(&attrs.meta);
entry.visit_span(extents); entry.visit_children(i);
entry.visit_attrs(attrs);
entry.visit_visibility(vis);
entry.visit_item_kind(kind);
if let (Some(name), child) = (entry.name, entry.inner.id()) { if let (Some(name), child) = (entry.name, entry.inner.id()) {
self.inner.add_child(name, child); self.inner.add_child(name, child);
} }
} }
fn visit_alias(&mut self, a: &'a cl_ast::Alias) { fn visit_generics(&mut self, value: &'a cl_ast::Generics) {
let cl_ast::Alias { to, from } = a; let cl_ast::Generics { vars } = value;
self.inner.set_source(Source::Alias(a)); for var in vars {
self.set_name(*to); let mut entry = self.inner.new_entry(NodeKind::Type);
entry.set_ty(TypeKind::Variable);
if let Some(t) = from { let id = entry.id();
self.visit_ty(t) 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) { fn visit_const(&mut self, c: &'a cl_ast::Const) {
let cl_ast::Const { name, ty, init } = c; let cl_ast::Const { name, ty, init } = c;
self.inner.set_source(Source::Const(c)); self.inner.set_source(Source::Const(c));
self.inner.set_body(init);
self.set_name(*name); self.set_name(*name);
self.visit_ty(ty); self.visit(ty);
self.visit_expr(init); self.visit(init);
} }
fn visit_static(&mut self, s: &'a cl_ast::Static) { fn visit_static(&mut self, s: &'a cl_ast::Static) {
let cl_ast::Static { mutable, name, ty, init } = s; let cl_ast::Static { mutable, name, ty, init } = s;
self.inner.set_source(Source::Static(s)); self.inner.set_source(Source::Static(s));
self.inner.set_body(init);
self.set_name(*name); self.set_name(*name);
self.visit_mutability(mutable); self.visit(mutable);
self.visit_ty(ty); self.visit(ty);
self.visit_expr(init); self.visit(init);
} }
fn visit_module(&mut self, m: &'a cl_ast::Module) { fn visit_module(&mut self, m: &'a cl_ast::Module) {
let cl_ast::Module { name, kind } = m; let cl_ast::Module { name, file } = m;
self.inner.set_source(Source::Module(m)); self.inner.set_source(Source::Module(m));
self.set_name(*name); self.set_name(*name);
self.visit_module_kind(kind); self.visit(file);
} }
fn visit_function(&mut self, f: &'a cl_ast::Function) { fn visit_function(&mut self, f: &'a cl_ast::Function) {
let cl_ast::Function { name, sign, bind, body } = f; let cl_ast::Function { name, gens, sign, bind, body } = f;
// TODO: populate generics?
self.inner.set_source(Source::Function(f)); self.inner.set_source(Source::Function(f));
self.set_name(*name); self.set_name(*name);
self.visit_ty_fn(sign); self.visit(gens);
bind.iter().for_each(|p| self.visit_param(p)); self.visit(sign);
self.visit(bind);
if let Some(b) = body { if let Some(b) = body {
self.visit_block(b) self.inner.set_body(b);
self.visit(b);
} }
} }
fn visit_struct(&mut self, s: &'a cl_ast::Struct) { fn visit_struct(&mut self, s: &'a cl_ast::Struct) {
let cl_ast::Struct { name, kind } = s; let cl_ast::Struct { name, gens, kind } = s;
self.inner.set_source(Source::Struct(s)); self.inner.set_source(Source::Struct(s));
self.set_name(*name); self.set_name(*name);
self.visit_struct_kind(kind); self.visit(gens);
self.visit(kind);
} }
fn visit_enum(&mut self, e: &'a cl_ast::Enum) { fn visit_enum(&mut self, e: &'a cl_ast::Enum) {
let cl_ast::Enum { name, kind } = e; let cl_ast::Enum { name, gens, variants } = e;
self.inner.set_source(Source::Enum(e)); self.inner.set_source(Source::Enum(e));
self.set_name(*name); self.set_name(*name);
self.visit_enum_kind(kind); self.visit(gens);
self.visit(variants);
let mut children = Vec::new();
for variant in variants.iter() {
let mut entry = self.new_entry(NodeKind::Type);
variant.visit_in(&mut entry);
children.push((variant.name, entry.inner.id()));
}
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;
let mut entry = self.new_entry(NodeKind::Type);
entry.inner.set_source(Source::Variant(value));
entry.visit(kind);
if let Some(body) = body {
entry.inner.set_body(body);
}
let child = entry.inner.id();
self.inner.add_child(*name, child);
} }
fn visit_impl(&mut self, i: &'a cl_ast::Impl) { fn visit_impl(&mut self, i: &'a cl_ast::Impl) {
@ -133,8 +172,8 @@ impl<'a> Visit<'a> for Populator<'_, 'a> {
self.inner.set_source(Source::Impl(i)); self.inner.set_source(Source::Impl(i));
self.inner.mark_impl_item(); self.inner.mark_impl_item();
self.visit_impl_kind(target); self.visit(target);
self.visit_file(body); self.visit(body);
} }
fn visit_use(&mut self, u: &'a cl_ast::Use) { fn visit_use(&mut self, u: &'a cl_ast::Use) {
@ -142,26 +181,6 @@ impl<'a> Visit<'a> for Populator<'_, 'a> {
self.inner.set_source(Source::Use(u)); self.inner.set_source(Source::Use(u));
self.inner.mark_use_item(); self.inner.mark_use_item();
self.visit_use_tree(tree); self.visit(tree);
}
fn visit_let(&mut self, l: &'a cl_ast::Let) {
let cl_ast::Let { mutable, name: _, ty, init } = l;
let mut entry = self.new_entry(NodeKind::Local);
entry.inner.set_source(Source::Local(l));
// entry.set_name(*name);
entry.visit_mutability(mutable);
if let Some(ty) = ty {
entry.visit_ty(ty);
}
if let Some(init) = init {
entry.visit_expr(init)
}
// let child = entry.inner.id();
// self.inner.add_child(*name, child);
todo!("Pattern destructuring in cl-typeck")
} }
} }

View File

@ -31,7 +31,7 @@ use crate::{
source::Source, source::Source,
type_kind::TypeKind, type_kind::TypeKind,
}; };
use cl_ast::{Meta, PathPart, Sym}; use cl_ast::{Expr, Meta, PathPart, Sym};
use cl_structures::{index_map::IndexMap, span::Span}; use cl_structures::{index_map::IndexMap, span::Span};
use std::collections::HashMap; use std::collections::HashMap;
@ -50,11 +50,11 @@ pub struct Table<'a> {
pub(crate) children: HashMap<Handle, HashMap<Sym, Handle>>, pub(crate) children: HashMap<Handle, HashMap<Sym, Handle>>,
pub(crate) imports: HashMap<Handle, HashMap<Sym, Handle>>, pub(crate) imports: HashMap<Handle, HashMap<Sym, Handle>>,
pub(crate) use_items: HashMap<Handle, Vec<Handle>>, pub(crate) use_items: HashMap<Handle, Vec<Handle>>,
bodies: HashMap<Handle, &'a Expr>,
types: HashMap<Handle, TypeKind>, types: HashMap<Handle, TypeKind>,
spans: HashMap<Handle, Span>, spans: HashMap<Handle, Span>,
metas: HashMap<Handle, &'a [Meta]>, metas: HashMap<Handle, &'a [Meta]>,
sources: HashMap<Handle, Source<'a>>, sources: HashMap<Handle, Source<'a>>,
// code: HashMap<Handle, BasicBlock>, // TODO: lower sources
impl_targets: HashMap<Handle, Handle>, impl_targets: HashMap<Handle, Handle>,
anon_types: HashMap<TypeKind, Handle>, anon_types: HashMap<TypeKind, Handle>,
@ -77,6 +77,7 @@ impl<'a> Table<'a> {
children: HashMap::new(), children: HashMap::new(),
imports: HashMap::new(), imports: HashMap::new(),
use_items: HashMap::new(), use_items: HashMap::new(),
bodies: HashMap::new(),
types: HashMap::new(), types: HashMap::new(),
spans: HashMap::new(), spans: HashMap::new(),
metas: HashMap::new(), metas: HashMap::new(),
@ -120,7 +121,7 @@ impl<'a> Table<'a> {
self.impls.push(item); self.impls.push(item);
} }
pub fn handle_iter(&mut self) -> impl Iterator<Item = Handle> { pub fn handle_iter(&mut self) -> impl Iterator<Item = Handle> + use<> {
self.kinds.keys() self.kinds.keys()
} }
@ -142,6 +143,18 @@ impl<'a> Table<'a> {
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> { pub const fn root_entry(&self) -> Entry<'_, 'a> {
self.root.to_entry(self) self.root.to_entry(self)
} }
@ -172,6 +185,10 @@ impl<'a> Table<'a> {
self.imports.get(&node) 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> { pub fn ty(&self, node: Handle) -> Option<&TypeKind> {
self.types.get(&node) self.types.get(&node)
} }
@ -192,6 +209,10 @@ impl<'a> Table<'a> {
self.impl_targets.get(&node).copied() self.impl_targets.get(&node).copied()
} }
pub fn set_body(&mut self, node: Handle, body: &'a Expr) -> Option<&'a Expr> {
self.bodies.insert(node, body)
}
pub fn set_ty(&mut self, node: Handle, kind: TypeKind) -> Option<TypeKind> { pub fn set_ty(&mut self, node: Handle, kind: TypeKind) -> Option<TypeKind> {
self.types.insert(node, kind) self.types.insert(node, kind)
} }
@ -224,6 +245,14 @@ impl<'a> Table<'a> {
} }
} }
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> { pub fn name(&self, node: Handle) -> Option<Sym> {
self.source(node).and_then(|s| s.name()) self.source(node).and_then(|s| s.name())
} }
@ -259,8 +288,7 @@ impl<'a> Table<'a> {
/// Does path traversal relative to the provided `node`. /// Does path traversal relative to the provided `node`.
pub fn nav(&self, node: Handle, path: &[PathPart]) -> Option<Handle> { pub fn nav(&self, node: Handle, path: &[PathPart]) -> Option<Handle> {
match path { match path {
[PathPart::SuperKw, rest @ ..] => self.nav(*self.parent(node)?, rest), [PathPart::SuperKw, rest @ ..] => self.nav(self.super_of(node)?, rest),
[PathPart::SelfKw, rest @ ..] => self.nav(node, rest),
[PathPart::SelfTy, rest @ ..] => self.nav(self.selfty(node)?, rest), [PathPart::SelfTy, rest @ ..] => self.nav(self.selfty(node)?, rest),
[PathPart::Ident(name), rest @ ..] => self.nav(self.get_by_sym(node, name)?, rest), [PathPart::Ident(name), rest @ ..] => self.nav(self.get_by_sym(node, name)?, rest),
[] => Some(node), [] => Some(node),
@ -282,6 +310,7 @@ pub enum NodeKind {
Const, Const,
Static, Static,
Function, Function,
Temporary,
Local, Local,
Impl, Impl,
Use, Use,
@ -299,6 +328,7 @@ mod display {
NodeKind::Const => write!(f, "const"), NodeKind::Const => write!(f, "const"),
NodeKind::Static => write!(f, "static"), NodeKind::Static => write!(f, "static"),
NodeKind::Function => write!(f, "fn"), NodeKind::Function => write!(f, "fn"),
NodeKind::Temporary => write!(f, "temp"),
NodeKind::Local => write!(f, "local"), NodeKind::Local => write!(f, "local"),
NodeKind::Use => write!(f, "use"), NodeKind::Use => write!(f, "use"),
NodeKind::Impl => write!(f, "impl"), NodeKind::Impl => write!(f, "impl"),

View File

@ -42,6 +42,7 @@ impl TypeExpression for TyKind {
match self { match self {
TyKind::Never => Ok(table.anon_type(TypeKind::Never)), TyKind::Never => Ok(table.anon_type(TypeKind::Never)),
TyKind::Empty => Ok(table.anon_type(TypeKind::Empty)), TyKind::Empty => Ok(table.anon_type(TypeKind::Empty)),
TyKind::Infer => Ok(table.inferred_type()),
TyKind::Path(p) => p.evaluate(table, node), TyKind::Path(p) => p.evaluate(table, node),
TyKind::Array(a) => a.evaluate(table, node), TyKind::Array(a) => a.evaluate(table, node),
TyKind::Slice(s) => s.evaluate(table, node), TyKind::Slice(s) => s.evaluate(table, node),

View File

@ -10,10 +10,14 @@ mod display;
/// (a component of a [Table](crate::table::Table)) /// (a component of a [Table](crate::table::Table))
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeKind { 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 /// An alias for an already-defined type
Instance(Handle), Instance(Handle),
/// A primitive type, built-in to the compiler /// A primitive type, built-in to the compiler
Intrinsic(Intrinsic), Primitive(Primitive),
/// A user-defined aromatic data type /// A user-defined aromatic data type
Adt(Adt), Adt(Adt),
/// A reference to an already-defined type: &T /// A reference to an already-defined type: &T
@ -38,7 +42,7 @@ pub enum TypeKind {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adt { pub enum Adt {
/// A union-like enum type /// A union-like enum type
Enum(Vec<(Sym, Option<Handle>)>), Enum(Vec<(Sym, Handle)>),
/// A structural product type with named members /// A structural product type with named members
Struct(Vec<(Sym, Visibility, Handle)>), Struct(Vec<(Sym, Visibility, Handle)>),
@ -55,42 +59,64 @@ pub enum Adt {
/// The set of compiler-intrinsic types. /// The set of compiler-intrinsic types.
/// These primitive types have native implementations of the basic operations. /// These primitive types have native implementations of the basic operations.
#[rustfmt::skip] #[rustfmt::skip]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Intrinsic { pub enum Primitive {
I8, I16, I32, I64, I128, Isize, // Signed integers I8, I16, I32, I64, I128, Isize, // Signed integers
U8, U16, U32, U64, U128, Usize, // Unsigned integers U8, U16, U32, U64, U128, Usize, // Unsigned integers
F8, F16, F32, F64, F128, Fsize, // Floating point numbers F8, F16, F32, F64, F128, Fsize, // Floating point numbers
Integer, Float, // Inferred int and float
Bool, // boolean value Bool, // boolean value
Char, // Unicode codepoint Char, // Unicode codepoint
} }
#[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 // Author's note: the fsize type is a meme
impl FromStr for Intrinsic { impl FromStr for Primitive {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s { Ok(match s {
"i8" => Intrinsic::I8, "i8" => Primitive::I8,
"i16" => Intrinsic::I16, "i16" => Primitive::I16,
"i32" => Intrinsic::I32, "i32" => Primitive::I32,
"i64" => Intrinsic::I64, "i64" => Primitive::I64,
"i128" => Intrinsic::I128, "i128" => Primitive::I128,
"isize" => Intrinsic::Isize, "isize" => Primitive::Isize,
"u8" => Intrinsic::U8, "u8" => Primitive::U8,
"u16" => Intrinsic::U16, "u16" => Primitive::U16,
"u32" => Intrinsic::U32, "u32" => Primitive::U32,
"u64" => Intrinsic::U64, "u64" => Primitive::U64,
"u128" => Intrinsic::U128, "u128" => Primitive::U128,
"usize" => Intrinsic::Usize, "usize" => Primitive::Usize,
"f8" => Intrinsic::F8, "f8" => Primitive::F8,
"f16" => Intrinsic::F16, "f16" => Primitive::F16,
"f32" => Intrinsic::F32, "f32" => Primitive::F32,
"f64" => Intrinsic::F64, "f64" => Primitive::F64,
"f128" => Intrinsic::F128, "f128" => Primitive::F128,
"fsize" => Intrinsic::Fsize, "fsize" => Primitive::Fsize,
"bool" => Intrinsic::Bool, "bool" => Primitive::Bool,
"char" => Intrinsic::Char, "char" => Primitive::Char,
_ => Err(())?, _ => Err(())?,
}) })
} }

View File

@ -1,6 +1,6 @@
//! [Display] implementations for [TypeKind], [Adt], and [Intrinsic] //! [Display] implementations for [TypeKind], [Adt], and [Intrinsic]
use super::{Adt, Intrinsic, TypeKind}; use super::{Adt, Primitive, TypeKind};
use crate::format_utils::*; use crate::format_utils::*;
use cl_ast::format::FmtAdapter; use cl_ast::format::FmtAdapter;
use std::fmt::{self, Display, Write}; use std::fmt::{self, Display, Write};
@ -8,8 +8,10 @@ use std::fmt::{self, Display, Write};
impl Display for TypeKind { impl Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
TypeKind::Inferred => write!(f, "_"),
TypeKind::Variable => write!(f, "?"),
TypeKind::Instance(def) => write!(f, "alias to #{def}"), TypeKind::Instance(def) => write!(f, "alias to #{def}"),
TypeKind::Intrinsic(i) => i.fmt(f), TypeKind::Primitive(i) => i.fmt(f),
TypeKind::Adt(a) => a.fmt(f), TypeKind::Adt(a) => a.fmt(f),
TypeKind::Ref(def) => write!(f, "&{def}"), TypeKind::Ref(def) => write!(f, "&{def}"),
TypeKind::Slice(def) => write!(f, "slice [#{def}]"), TypeKind::Slice(def) => write!(f, "slice [#{def}]"),
@ -36,10 +38,7 @@ impl Display for Adt {
let mut variants = variants.iter(); let mut variants = variants.iter();
separate(", ", || { separate(", ", || {
let (name, def) = variants.next()?; let (name, def) = variants.next()?;
Some(move |f: &mut Delimit<_>| match def { Some(move |f: &mut Delimit<_>| write!(f, "{name}: #{def}"))
Some(def) => write!(f, "{name}: #{def}"),
None => write!(f, "{name}"),
})
})(f.delimit_with("enum {", "}")) })(f.delimit_with("enum {", "}"))
} }
Adt::Struct(members) => { Adt::Struct(members) => {
@ -68,29 +67,31 @@ impl Display for Adt {
} }
} }
impl Display for Intrinsic { impl Display for Primitive {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Intrinsic::I8 => f.write_str("i8"), Primitive::I8 => f.write_str("i8"),
Intrinsic::I16 => f.write_str("i16"), Primitive::I16 => f.write_str("i16"),
Intrinsic::I32 => f.write_str("i32"), Primitive::I32 => f.write_str("i32"),
Intrinsic::I64 => f.write_str("i64"), Primitive::I64 => f.write_str("i64"),
Intrinsic::I128 => f.write_str("i128"), Primitive::I128 => f.write_str("i128"),
Intrinsic::Isize => f.write_str("isize"), Primitive::Isize => f.write_str("isize"),
Intrinsic::U8 => f.write_str("u8"), Primitive::U8 => f.write_str("u8"),
Intrinsic::U16 => f.write_str("u16"), Primitive::U16 => f.write_str("u16"),
Intrinsic::U32 => f.write_str("u32"), Primitive::U32 => f.write_str("u32"),
Intrinsic::U64 => f.write_str("u64"), Primitive::U64 => f.write_str("u64"),
Intrinsic::U128 => f.write_str("u128"), Primitive::U128 => f.write_str("u128"),
Intrinsic::Usize => f.write_str("usize"), Primitive::Usize => f.write_str("usize"),
Intrinsic::F8 => f.write_str("f8"), Primitive::F8 => f.write_str("f8"),
Intrinsic::F16 => f.write_str("f16"), Primitive::F16 => f.write_str("f16"),
Intrinsic::F32 => f.write_str("f32"), Primitive::F32 => f.write_str("f32"),
Intrinsic::F64 => f.write_str("f64"), Primitive::F64 => f.write_str("f64"),
Intrinsic::F128 => f.write_str("f128"), Primitive::F128 => f.write_str("f128"),
Intrinsic::Fsize => f.write_str("fsize"), Primitive::Fsize => f.write_str("fsize"),
Intrinsic::Bool => f.write_str("bool"), Primitive::Integer => f.write_str("{integer}"),
Intrinsic::Char => f.write_str("char"), Primitive::Float => f.write_str("{float}"),
Primitive::Bool => f.write_str("bool"),
Primitive::Char => f.write_str("char"),
} }
} }
} }

View File

@ -26,7 +26,7 @@ Static = "static" Mutability Identifier ':' Ty '=' Expr ';' ;
Module = "mod" Identifier ModuleKind ; Module = "mod" Identifier ModuleKind ;
ModuleKind = '{' Item* '}' | ';' ; ModuleKind = '{' Item* '}' | ';' ;
Function = "fn" Identifier '(' (Param ',')* Param? ')' ('->' Ty)? Block? ; Function = "fn" Identifier '(' (Param ',')* Param? ')' ('->' Ty)? (Expr | ';') ;
Param = Mutability Identifier ':' Ty ; Param = Mutability Identifier ':' Ty ;
Struct = "struct" Identifier (StructTuple | StructBody)?; Struct = "struct" Identifier (StructTuple | StructBody)?;
@ -127,6 +127,17 @@ Block = '{' Stmt* '}';
Group = Empty | '(' (Expr | Tuple) ')' ; Group = Empty | '(' (Expr | Tuple) ')' ;
Tuple = (Expr ',')* Expr? ; Tuple = (Expr ',')* Expr? ;
Match = "match" { (MatchArm ',')* MatchArm? } ;
MatchArm = Pattern '=>' Expr ;
Pattern = Path
| Literal
| '&' "mut"? Pattern
| '(' (Pattern ',')* (Pattern | '..' )? ')'
| '[' (Pattern ',')* (Pattern | '..' Identifier?)? ']'
| StructPattern
;
Loop = "loop" Block ; Loop = "loop" Block ;
While = "while" Expr Block Else ; While = "while" Expr Block Else ;
If = "if" Expr Block Else ; If = "if" Expr Block Else ;

View File

@ -0,0 +1,42 @@
//! Demonstrates the use of [read_and()]:
//!
//! The provided closure:
//! 1. Takes a line of input (a [String])
//! 2. Performs some calculation (using [FromStr])
//! 3. Returns a [Result] containing a [Response] or an [Err]
use repline::{error::Error as RlError, Repline, Response};
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let mut rl = Repline::with_input(
"fn main {\r\n\tprintln(\"Foo!\")\r\n}\r\n".as_bytes(),
"\x1b[33m",
" >",
" ?>",
);
while rl.read().is_ok() {}
let mut rl = rl.swap_input(std::io::stdin());
loop {
let f = |_line| -> Result<_, RlError> { Ok(Response::Continue) };
let line = match rl.read() {
Err(RlError::CtrlC(_)) => break,
Err(RlError::CtrlD(line)) => {
rl.deny();
line
}
Ok(line) => line,
Err(e) => Err(e)?,
};
print!("\x1b[G\x1b[J");
match f(&line) {
Ok(Response::Accept) => rl.accept(),
Ok(Response::Deny) => rl.deny(),
Ok(Response::Break) => break,
Ok(Response::Continue) => continue,
Err(e) => print!("\x1b[40G\x1b[A\x1bJ\x1b[91m{e}\x1b[0m\x1b[B"),
}
}
Ok(())
}

View File

@ -1,9 +1,9 @@
//! The [Editor] is a multi-line buffer of [`char`]s which operates on an ANSI-compatible terminal. //! 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 crossterm::{cursor::*, queue, style::*, terminal::*};
use std::{collections::VecDeque, fmt::Display, io::Write}; use std::{collections::VecDeque, fmt::Display, io::Write};
use super::error::{Error, ReplResult}; use super::error::ReplResult;
fn is_newline(c: &char) -> bool { fn is_newline(c: &char) -> bool {
*c == '\n' *c == '\n'
@ -14,7 +14,7 @@ fn write_chars<'a, W: Write>(
w: &mut W, w: &mut W,
) -> std::io::Result<()> { ) -> std::io::Result<()> {
for c in c { for c in c {
write!(w, "{c}")?; queue!(w, Print(c))?;
} }
Ok(()) Ok(())
} }
@ -42,73 +42,63 @@ impl<'a> Editor<'a> {
head.iter().chain(tail.iter()) head.iter().chain(tail.iter())
} }
/// Moves up to the first line of the editor, and clears the screen. fn putchar<W: Write>(&self, c: char, w: &mut W) -> ReplResult<()> {
/// let Self { color, again, .. } = self;
/// 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 { match c {
'\n' => write!(w, "\r\n{color}{again}\x1b[0m "), '\n' => queue!(
_ => 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, w,
Print('\n'),
MoveToColumn(0), MoveToColumn(0),
Print(color), Print(color),
Print(if head.is_empty() { begin } else { again }), Print(again),
ResetColor, Print(ResetColor),
Print(' '), Print(' ')
)?; ),
c => queue!(w, Print(c)),
}?;
Ok(())
}
pub fn redraw_head<W: Write>(&self, w: &mut W) -> ReplResult<()> {
let Self { head, color, begin, .. } = self;
match head.iter().copied().filter(is_newline).count() {
0 => queue!(w, MoveToColumn(0)),
n => queue!(w, MoveUp(n as u16)),
}?;
queue!(w, Print(color), Print(begin), Print(ResetColor), Print(' '))?;
for c in head {
self.putchar(*c, w)?;
}
Ok(())
}
pub fn redraw_tail<W: Write>(&self, w: &mut W) -> ReplResult<()> {
let Self { tail, .. } = self;
queue!(w, SavePosition, Clear(ClearType::FromCursorDown))?;
for c in tail {
self.putchar(*c, w)?;
}
queue!(w, RestorePosition)?;
Ok(()) Ok(())
} }
/// Prints the characters before the cursor on the current line. /// Prints the characters before the cursor on the current line.
pub fn print_head<W: Write>(&self, w: &mut W) -> ReplResult<()> { pub fn print_head<W: Write>(&self, w: &mut W) -> ReplResult<()> {
self.prompt(w)?; let Self { head, color, begin, again, .. } = self;
write_chars( let nl = self.head.iter().rposition(is_newline).map(|n| n + 1);
self.head.iter().skip( let prompt = if nl.is_some() { again } else { begin };
self.head
.iter() queue!(
.rposition(is_newline)
.unwrap_or(self.head.len())
+ 1,
),
w, w,
MoveToColumn(0),
Print(color),
Print(prompt),
ResetColor,
Print(' '),
)?; )?;
write_chars(head.iter().skip(nl.unwrap_or(0)), w)?;
Ok(()) Ok(())
} }
@ -121,53 +111,53 @@ impl<'a> Editor<'a> {
Ok(()) Ok(())
} }
/// Writes a character at the cursor, shifting the text around as necessary. pub fn print_err<W: Write>(&self, err: impl Display, w: &mut W) -> ReplResult<()> {
pub fn push<W: Write>(&mut self, c: char, w: &mut W) -> ReplResult<()> { queue!(
// Tail optimization: if the tail is empty, w,
//we don't have to undraw and redraw on newline SavePosition,
if self.tail.is_empty() { Clear(ClearType::UntilNewLine),
self.head.push_back(c); Print(err),
match c { RestorePosition
'\n' => { )?;
write!(w, "\r\n")?; Ok(())
self.print_head(w)?;
}
c => {
queue!(w, Print(c))?;
}
};
return Ok(());
} }
if '\n' == c { /// Writes a character at the cursor, shifting the text around as necessary.
self.undraw(w)?; pub fn push<W: Write>(&mut self, c: char, w: &mut W) -> ReplResult<()> {
}
self.head.push_back(c); self.head.push_back(c);
self.putchar(c, w)?;
match c { match c {
'\n' => self.redraw(w)?, '\n' => self.redraw_tail(w),
_ => { _ => self.print_tail(w),
write!(w, "{c}")?;
self.print_tail(w)?;
} }
} }
Ok(())
}
/// Erases a character at the cursor, shifting the text around as necessary. /// 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>> { 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(); let c = self.head.pop_back();
// if the character was a newline, we need to go back a line
match c { match c {
Some('\n') => self.redraw(w)?, None => return Ok(None),
Some('\n') => {
queue!(w, MoveToPreviousLine(1))?;
self.print_head(w)?;
self.redraw_tail(w)?;
}
Some(_) => { Some(_) => {
// go back a char queue!(w, MoveLeft(1), Clear(ClearType::UntilNewLine))?;
queue!(w, MoveLeft(1), Print(' '), MoveLeft(1))?;
self.print_tail(w)?; self.print_tail(w)?;
} }
None => {} }
Ok(c)
}
/// Pops the character after the cursor, redrawing if necessary
pub fn delete<W: Write>(&mut self, w: &mut W) -> ReplResult<Option<char>> {
let c = self.tail.pop_front();
match c {
Some('\n') => self.redraw_tail(w)?,
_ => self.print_tail(w)?,
} }
Ok(c) Ok(c)
} }
@ -185,9 +175,14 @@ impl<'a> Editor<'a> {
} }
/// Sets the editor to the contents of a string, placing the cursor at the end. /// Sets the editor to the contents of a string, placing the cursor at the end.
pub fn restore(&mut self, s: &str) { pub fn restore<W: Write>(&mut self, s: &str, w: &mut W) -> ReplResult<()> {
match self.head.iter().copied().filter(is_newline).count() {
0 => queue!(w, MoveToColumn(0), Clear(ClearType::FromCursorDown))?,
n => queue!(w, MoveUp(n as u16), Clear(ClearType::FromCursorDown))?,
};
self.clear(); self.clear();
self.head.extend(s.chars()) self.print_head(w)?;
self.extend(s.chars(), w)
} }
/// Clears the editor, removing all characters. /// Clears the editor, removing all characters.
@ -196,24 +191,6 @@ impl<'a> Editor<'a> {
self.tail.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 /// Erases a word from the buffer, where a word is any non-whitespace characters
/// preceded by a single whitespace character /// preceded by a single whitespace character
pub fn erase_word<W: Write>(&mut self, w: &mut W) -> ReplResult<()> { pub fn erase_word<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
@ -226,9 +203,28 @@ impl<'a> Editor<'a> {
self.head.len() + self.tail.len() self.head.len() + self.tail.len()
} }
/// Returns true if the cursor is at the start of the buffer
pub fn at_start(&self) -> bool {
self.head.is_empty()
}
/// Returns true if the cursor is at the end of the buffer
pub fn at_end(&self) -> bool {
self.tail.is_empty()
}
/// Returns true if the cursor is at the start of a line
pub fn at_line_start(&self) -> bool {
matches!(self.head.back(), None | Some('\n'))
}
/// Returns true if the cursor is at the end of a line
pub fn at_line_end(&self) -> bool {
matches!(self.tail.front(), None | Some('\n'))
}
/// Returns true if the buffer is empty. /// Returns true if the buffer is empty.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.head.is_empty() && self.tail.is_empty() self.at_start() && self.at_end()
} }
/// Returns true if the buffer ends with a given pattern /// Returns true if the buffer ends with a given pattern
@ -246,59 +242,102 @@ impl<'a> Editor<'a> {
} }
/// Moves the cursor back `steps` steps /// Moves the cursor back `steps` steps
pub fn cursor_back<W: Write>(&mut self, steps: usize, w: &mut W) -> ReplResult<()> { pub fn cursor_back<W: Write>(&mut self, 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 { let Some(c) = self.head.pop_back() else {
return Ok(()); return Ok(());
}; };
self.tail.push_front(c); self.tail.push_front(c);
match c { match c {
'\n' => self.redraw(w)?, '\n' => {
_ => queue!(w, MoveLeft(1))?, queue!(w, MoveToPreviousLine(1))?;
self.print_head(w)
} }
_ => queue!(w, MoveLeft(1)).map_err(Into::into),
} }
Ok(())
} }
/// Moves the cursor forward `steps` steps /// Moves the cursor forward `steps` steps
pub fn cursor_forward<W: Write>(&mut self, steps: usize, w: &mut W) -> ReplResult<()> { pub fn cursor_forward<W: Write>(&mut self, 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 { let Some(c) = self.tail.pop_front() else {
return Ok(()); return Ok(());
}; };
self.head.push_back(c); self.head.push_back(c);
match c { match c {
'\n' => self.redraw(w)?, '\n' => {
_ => queue!(w, MoveRight(1))?, queue!(w, MoveToNextLine(1))?;
self.print_head(w)
}
_ => queue!(w, MoveRight(1)).map_err(Into::into),
} }
} }
/// Moves the cursor up to the previous line, attempting to preserve relative offset
pub fn cursor_up<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
// Calculates length of the current line
let mut len = self.head.len();
self.cursor_line_start(w)?;
len -= self.head.len();
if self.at_start() {
return Ok(());
}
self.cursor_back(w)?;
self.cursor_line_start(w)?;
while 0 < len && !self.at_line_end() {
self.cursor_forward(w)?;
len -= 1;
}
Ok(())
}
/// Moves the cursor down to the next line, attempting to preserve relative offset
pub fn cursor_down<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
let mut len = self.head.iter().rev().take_while(|&&c| c != '\n').count();
self.cursor_line_end(w)?;
self.cursor_forward(w)?;
while 0 < len && !self.at_line_end() {
self.cursor_forward(w)?;
len -= 1;
}
Ok(()) Ok(())
} }
/// Moves the cursor to the beginning of the current line /// Moves the cursor to the beginning of the current line
pub fn home<W: Write>(&mut self, w: &mut W) -> ReplResult<()> { pub fn cursor_line_start<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
loop { while !self.at_line_start() {
match self.head.back() { self.cursor_back(w)?
Some('\n') | None => break Ok(()),
Some(_) => self.cursor_back(1, w)?,
}
} }
Ok(())
} }
/// Moves the cursor to the end of the current line /// Moves the cursor to the end of the current line
pub fn end<W: Write>(&mut self, w: &mut W) -> ReplResult<()> { pub fn cursor_line_end<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
loop { while !self.at_line_end() {
match self.tail.front() { self.cursor_forward(w)?
Some('\n') | None => break Ok(()),
Some(_) => self.cursor_forward(1, w)?,
} }
Ok(())
} }
pub fn cursor_start<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
while !self.at_start() {
self.cursor_back(w)?
}
Ok(())
}
pub fn cursor_end<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
while !self.at_end() {
self.cursor_forward(w)?
}
Ok(())
} }
} }

View File

@ -49,7 +49,7 @@ where F: FnMut(&str) -> Result<Response, Box<dyn Error>> {
Ok(Response::Deny) => rl.deny(), Ok(Response::Deny) => rl.deny(),
Ok(Response::Break) => break, Ok(Response::Break) => break,
Ok(Response::Continue) => continue, Ok(Response::Continue) => continue,
Err(e) => print!("\x1b[40G\x1b[A\x1bJ\x1b[91m{e}\x1b[0m\x1b[B"), Err(e) => rl.print_inline(format_args!("\x1b[40G\x1b[91m{e}\x1b[0m"))?,
} }
} }
Ok(()) Ok(())

View File

@ -1,11 +1,12 @@
//! Prompts the user, reads the lines. Not much more to it than that. //! Prompts the user, reads the lines. Not much more to it than that.
//! //!
//! This module is in charge of parsing keyboard input and interpreting it for the line editor. //! This module is in charge of parsing keyboard input and interpreting it for the line editor.
#![allow(clippy::unbuffered_bytes)]
use crate::{editor::Editor, error::*, iter::*, raw::raw}; use crate::{editor::Editor, error::*, iter::*, raw::raw};
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
io::{stdout, Bytes, Read, Result, Write}, io::{Bytes, Read, Result, Write, stdout},
}; };
/// Prompts the user, reads the lines. Not much more to it than that. /// Prompts the user, reads the lines. Not much more to it than that.
@ -35,6 +36,14 @@ impl<'a, R: Read> Repline<'a, R> {
ed: Editor::new(color, begin, again), ed: Editor::new(color, begin, again),
} }
} }
pub fn swap_input<S: Read>(self, new_input: S) -> Repline<'a, S> {
Repline {
input: Chars(Flatten(new_input.bytes())),
history: self.history,
hindex: self.hindex,
ed: self.ed,
}
}
/// Set the terminal prompt color /// Set the terminal prompt color
pub fn set_color(&mut self, color: &'a str) { pub fn set_color(&mut self, color: &'a str) {
self.ed.color = color self.ed.color = color
@ -55,8 +64,7 @@ impl<'a, R: Read> Repline<'a, R> {
let mut stdout = stdout().lock(); let mut stdout = stdout().lock();
let stdout = &mut stdout; let stdout = &mut stdout;
let _make_raw = raw(); let _make_raw = raw();
// self.ed.begin_frame(stdout)?;
// self.ed.redraw_frame(stdout)?;
self.ed.print_head(stdout)?; self.ed.print_head(stdout)?;
loop { loop {
stdout.flush()?; stdout.flush()?;
@ -74,19 +82,19 @@ impl<'a, R: Read> Repline<'a, R> {
return Err(Error::CtrlD(self.ed.to_string())); return Err(Error::CtrlD(self.ed.to_string()));
} }
// Tab: extend line by 4 spaces // Tab: extend line by 4 spaces
'\t' => { '\t' => self.ed.extend(INDENT.chars(), stdout)?,
self.ed.extend(INDENT.chars(), stdout)?;
}
// ignore newlines, process line feeds. Not sure how cross-platform this is. // ignore newlines, process line feeds. Not sure how cross-platform this is.
'\n' => {} '\n' => {}
'\r' => { '\r' => {
if self.ed.at_end() {
self.ed.push('\n', stdout)?; self.ed.push('\n', stdout)?;
} else {
self.ed.cursor_line_end(stdout)?;
}
return Ok(self.ed.to_string()); return Ok(self.ed.to_string());
} }
// Ctrl+Backspace in my terminal // Ctrl+Backspace in my terminal
'\x17' => { '\x17' => self.ed.erase_word(stdout)?,
self.ed.erase_word(stdout)?;
}
// Escape sequence // Escape sequence
'\x1b' => self.escape(stdout)?, '\x1b' => self.escape(stdout)?,
// backspace // backspace
@ -111,6 +119,19 @@ impl<'a, R: Read> Repline<'a, R> {
} }
} }
} }
/// Prints a message without moving the cursor
pub fn print_inline(&mut self, value: impl std::fmt::Display) -> ReplResult<()> {
let mut stdout = stdout().lock();
self.print_err(&mut stdout, value)
}
/// Prints a message (ideally an error) without moving the cursor
fn print_err<W: Write>(&self, w: &mut W, value: impl std::fmt::Display) -> ReplResult<()> {
self.ed.print_err(value, w)
}
// Prints some debug info into the editor's buffer and the provided writer
pub fn put<D: std::fmt::Display, W: Write>(&mut self, disp: D, w: &mut W) -> ReplResult<()> {
self.ed.extend(format!("{disp}").chars(), w)
}
/// Handle ANSI Escape /// Handle ANSI Escape
fn escape<W: Write>(&mut self, w: &mut W) -> ReplResult<()> { fn escape<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
match self.input.next().ok_or(Error::EndOfInput)?? { match self.input.next().ok_or(Error::EndOfInput)?? {
@ -123,26 +144,39 @@ impl<'a, R: Read> Repline<'a, R> {
/// Handle ANSI Control Sequence Introducer /// Handle ANSI Control Sequence Introducer
fn csi<W: Write>(&mut self, w: &mut W) -> ReplResult<()> { fn csi<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
match self.input.next().ok_or(Error::EndOfInput)?? { match self.input.next().ok_or(Error::EndOfInput)?? {
'A' => { 'A' if self.ed.at_start() && self.hindex > 0 => {
self.hindex = self.hindex.saturating_sub(1); self.hindex -= 1;
self.restore_history(w)? self.restore_history(w)?;
self.ed.cursor_start(w)?;
} }
'B' => { 'A' => self.ed.cursor_up(w)?,
self.hindex = self.hindex.saturating_add(1).min(self.history.len()); 'B' if self.ed.at_end() && self.hindex < self.history.len() => {
self.restore_history(w)? self.restore_history(w)?;
self.hindex += 1;
} }
'C' => self.ed.cursor_forward(1, w)?, 'B' => self.ed.cursor_down(w)?,
'D' => self.ed.cursor_back(1, w)?, 'C' => self.ed.cursor_forward(w)?,
'H' => self.ed.home(w)?, 'D' => self.ed.cursor_back(w)?,
'F' => self.ed.end(w)?, 'H' => self.ed.cursor_line_start(w)?,
'F' => self.ed.cursor_line_end(w)?,
'3' => { '3' => {
if let '~' = self.input.next().ok_or(Error::EndOfInput)?? { if let '~' = self.input.next().ok_or(Error::EndOfInput)?? {
let _ = self.ed.delete(w); self.ed.delete(w)?;
}
}
'5' => {
if let '~' = self.input.next().ok_or(Error::EndOfInput)?? {
self.ed.cursor_start(w)?
}
}
'6' => {
if let '~' = self.input.next().ok_or(Error::EndOfInput)?? {
self.ed.cursor_end(w)?
} }
} }
other => { other => {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
self.ed.extend(other.escape_debug(), w)?; self.print_err(w, other.escape_debug())?;
} }
} }
} }
@ -151,11 +185,9 @@ impl<'a, R: Read> Repline<'a, R> {
/// Restores the currently selected history /// Restores the currently selected history
fn restore_history<W: Write>(&mut self, w: &mut W) -> ReplResult<()> { fn restore_history<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
let Self { history, hindex, ed, .. } = self; let Self { history, hindex, ed, .. } = self;
ed.undraw(w)?;
ed.clear();
ed.print_head(w)?;
if let Some(history) = history.get(*hindex) { if let Some(history) = history.get(*hindex) {
ed.extend(history.chars(), w)? ed.restore(history, w)?;
ed.print_err(format_args!(" History {hindex} restored!"), w)?;
} }
Ok(()) Ok(())
} }

95
sample-code/calculator.cl Normal file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env -S conlang-run
//! A simple five-function pn calculator
// TODO: enum constructors in the interpreter
struct Atom(f64);
struct Op(char, [Expr]);
enum Expr {
Atom(f64),
Op(char, [Expr]),
}
// Evaluates an expression
fn eval(expr: Expr) -> isize {
match expr {
Atom(value) => value,
Op('*', [lhs, rhs]) => eval(lhs) * eval(rhs),
Op('/', [lhs, rhs]) => eval(lhs) / eval(rhs),
Op('%', [lhs, rhs]) => eval(lhs) % eval(rhs),
Op('+', [lhs, rhs]) => eval(lhs) + eval(rhs),
Op('-', [lhs, rhs]) => eval(lhs) - eval(rhs),
Op('-', [lhs]) => - eval(lhs),
Op(other, ..rest) => {
panic("ERROR: Unknown operator: " + other)
},
other => {
println(other);
panic("ERROR: Unknown operation ^")
}
}
}
/// Parses expressions
fn parse(line: [char], power: i32) -> (Expr, [char]) {
fn map((expr, line): (Expr, [char]), f: fn(Expr) -> Expr) -> (Expr, [char]) {
(f(expr), line)
}
line = space(line);
let (lhs, line) = match line {
['0'..='9', ..] => number(line),
[op, ..rest] => {
parse(rest, pre_bp(op)).map(|lhs| Op(op, [lhs]))
},
_ => panic("Unexpected end of input"),
};
while let [op, ..rest] = space(line) {
let (before, after) = inf_bp(op);
if before < power {
break;
};
(lhs, line) = parse(rest, after).map(|rhs| Op(op, [lhs, rhs]));
};
(lhs, line)
}
fn number(line: [char]) -> (Expr, [char]) {
let value = 0.0;
while (let [first, ..rest] = line) && (let '0'..='9' = first) {
value = value * 10.0 + (first as f64 - '0' as f64);
line = rest;
};
(Atom(value), line)
}
fn space(line: [char]) -> [char] {
match line {
[' ', ..rest] => space(rest),
line => line
}
}
fn inf_bp(op: char) -> (i32, i32) {
(|x| (2 * x, 2 * x + 1))(
match op {
'*' => 2,
'/' => 2,
'%' => 2,
'+' => 1,
'-' => 1,
_ => -1,
})
}
fn pre_bp(op: char) -> i32 {
(|x| 2 * x + 1)(
match op {
'-' => 9,
_ => -1,
})
}

View File

@ -6,13 +6,13 @@ struct Student {
} }
fn Student(name: str, age: i32) -> Student { fn Student(name: str, age: i32) -> Student {
Student: { name, age } Student { name, age }
} }
fn match_test(student: Student) { fn match_test(student: Student) {
match student { match student {
Student: { name: "shark", age } => println("Found a shark of ", age, " year(s)"), Student { name: "shark", age } => println("Found a shark of ", age, " year(s)"),
Student: { name, age: 22 } => println("Found a 22-year-old named ", name), Student { name, age: 22 } => println("Found a 22-year-old named ", name),
Student: { name, age } => println("Found someone named ", name, " of ", age, " year(s)"), Student { name, age } => println("Found someone named ", name, " of ", age, " year(s)"),
} }
} }

View File

@ -0,0 +1,8 @@
//! Implements a Truth Machine
fn main (n)
match n {
1 => loop print(1),
n => println(n),
}

69
sample-code/unionfind.cl Normal file
View File

@ -0,0 +1,69 @@
//! Disjoint Set Forest implementation of the Union Find algorithm
// enum TreeNode {
// Empty,
// Filled { parent: usize, rank: usize }
// }
// TODO: Namespace based on type of first argument
fn makeset(f, x) {
match (*f)[x] {
() => (*f)[x] = Filled { parent: x, rank: 0 },
_ => {}
}
}
fn union(f, a, b) {
let (a, b) = (find(f, a), find(f, b));
if a == b { return; }
match ((*f)[a], (*f)[b]) {
(Filled {parent: _, rank: r_a}, Filled {parent: _, rank: r_b}) => {
if r_a < r_b {
union(f, b, a)
} else {
(*f)[b].parent = a;
(*f)[a].rank += 1;
}
}
}
}
fn find(f, x) {
match (*f)[x] {
Filled { parent, rank } => if parent == x {
x
} else {
let parent = find(f, parent);
(*f)[x].parent = parent;
parent
},
() => x,
}
}
fn show(f) {
for node in 0..(len((*f))) {
match (*f)[node] {
Filled { parent, rank } => println(node, ": { parent: ", parent, ", rank: ", rank, " }"),
_ => {}
}
}
}
fn test(f) {
"Union Find on Disjoint Set Forest".println()
for i in 0..10 { makeset(f, i) }
for i in 10..20 { makeset(f, i) }
show(f)
println()
for i in 1..10 { union(f, i*2-1, i*2) }
for i in 5..10 { union(f, i*2+1, i*2) }
show(f)
}
fn main() {
let f = [();20]
f.test()
}

View File

@ -1,12 +1,24 @@
//! # The Conlang Standard Library //! # The Conlang Standard Library
pub mod preamble { pub mod preamble {
pub use super::{num::*, str::str}; pub use super::{
num::*,
option::Option,
range::{RangeExc, RangeInc},
result::Result,
str::str,
};
} }
pub mod num; pub mod num;
pub mod str; pub mod str;
pub mod option;
pub mod result;
pub mod range;
#[cfg("test")] #[cfg("test")]
mod test; mod test;

View File

@ -1,51 +1,51 @@
//! The primitive numeric types //! The primitive numeric types
#[intrinsic = "bool"] #[lang = "bool"]
pub type bool; pub type bool;
#[intrinsic = "char"] #[lang = "char"]
pub type char; pub type char;
#[intrinsic = "i8"] #[lang = "i8"]
pub type i8; pub type i8;
#[intrinsic = "i16"] #[lang = "i16"]
pub type i16; pub type i16;
#[intrinsic = "i32"] #[lang = "i32"]
pub type i32; pub type i32;
#[intrinsic = "i64"] #[lang = "i64"]
pub type i64; pub type i64;
#[intrinsic = "i128"] #[lang = "i128"]
pub type i128; pub type i128;
#[intrinsic = "isize"] #[lang = "isize"]
pub type isize; pub type isize;
#[intrinsic = "u8"] #[lang = "u8"]
pub type u8; pub type u8;
#[intrinsic = "u16"] #[lang = "u16"]
pub type u16; pub type u16;
#[intrinsic = "u32"] #[lang = "u32"]
pub type u32; pub type u32;
#[intrinsic = "u64"] #[lang = "u64"]
pub type u64; pub type u64;
#[intrinsic = "u128"] #[lang = "u128"]
pub type u128; pub type u128;
#[intrinsic = "usize"] #[lang = "usize"]
pub type usize; pub type usize;
#[intrinsic = "f32"] #[lang = "f32"]
pub type f32; pub type f32;
#[intrinsic = "f64"] #[lang = "f64"]
pub type f64; pub type f64;
// Contains implementations for (TODO) overloaded operators on num types // Contains implementations for (TODO) overloaded operators on num types
@ -278,9 +278,9 @@ pub mod ops {
} }
impl usize { impl usize {
pub const MIN: Self = __march_ptr_width_unsigned_min(); pub const MIN: Self = (); // __march_ptr_width_unsigned_min(); // TODO: intrinsics
pub const MAX: Self = __march_ptr_width_unsigned_max(); pub const MAX: Self = (); // __march_ptr_width_unsigned_max(); // TODO: intrinsics
pub const BIT_WIDTH: u32 = __march_ptr_width_bits(); pub const BIT_WIDTH: u32 = (); // __march_ptr_width_bits(); // TODO: intrinsics
pub fn default() -> Self { pub fn default() -> Self {
0 0
} }
@ -512,9 +512,9 @@ pub mod ops {
} }
impl isize { impl isize {
pub const MIN: Self = __march_ptr_width_signed_min(); pub const MIN: Self = (); // __march_ptr_width_signed_min(); // TODO: intrinsics
pub const MAX: Self = __march_ptr_width_signed_max(); pub const MAX: Self = (); // __march_ptr_width_signed_max(); // TODO: intrinsics
pub const BIT_WIDTH: u32 = __march_ptr_width_bits(); pub const BIT_WIDTH: u32 = (); // __march_ptr_width_bits(); // TODO: intrinsics
pub fn default() -> Self { pub fn default() -> Self {
0 0
} }

29
stdlib/std/option.cl Normal file
View File

@ -0,0 +1,29 @@
//! The optional type, representing the presence or absence of a thing.
use super::preamble::*;
pub enum Option<T> {
Some(T),
None,
}
impl Option {
pub fn is_some(self: &Self) -> bool {
match self {
Some(_) => true,
None => false,
}
}
pub fn is_none(self: &Self) -> bool {
match self {
Some(_) => false,
None => true,
}
}
/// Maps from one option space to another
// pub fn map<U>(self: Self, f: fn(T) -> U) -> Option<U> {
// match self {
// Some(value) => Some(f(value)),
// None => None,
// }
// }
}

15
stdlib/std/range.cl Normal file
View File

@ -0,0 +1,15 @@
//! Iterable ranges
/// An Exclusive Range `a .. b` iterates from a to b, excluding b
// #[lang = "range_exc"]
pub struct RangeExc<T>(T, T)
/// An Inclusive Range `a ..= b` iterates from a to b, including b
// #[lang = "range_inc"]
pub struct RangeInc<T>(T, T)
impl RangeExc {
fn next<T>(this: &RangeExc) -> T {
(*this).0
}
}

36
stdlib/std/result.cl Normal file
View File

@ -0,0 +1,36 @@
//! The Result type, indicating a fallible operation.
use super::preamble::*;
pub enum Result<T, E> {
Ok(T),
Err(E),
}
impl Result {
pub fn is_ok(&self) -> bool {
match self {
Ok(_) => true,
Err(_) => false,
}
}
pub fn is_err(&self) -> bool {
match self {
Ok(_) => false,
Err(_) => true,
}
}
/// Maps the value inside the Result::Ok, leaving errors alone.
// pub fn map<U>(self, f: fn(T) -> U) -> Result<U, E> {
// match self {
// Ok(t) => Ok(f(t)),
// Err(e) => Err(e),
// }
// }
/// Maps the value inside the Result::Err, leaving values alone.
// pub fn map_err<F>(self, f: fn(E) -> F) -> Result<T, F> {
// match self {
// Ok(t) => Ok(t),
// Err(e) => Err(f(e)),
// }
// }
}

View File

@ -1,4 +1,5 @@
//! TODO: give conland a string type //! TODO: give conland a string type
use super::num::u8; use super::num::u8;
// #[lang = "str"]
type str = [u8]; type str = [u8];

View File

@ -4,14 +4,14 @@ use super::preamble::*;
struct UnitLike; struct UnitLike;
struct TupleLike(super::num::i32, super::self::test::char) struct TupleLike(super::num::i32, super::test::char)
struct StructLike { struct StructLike {
pub member1: UnitLike, pub member1: UnitLike,
member2: TupleLike, member2: TupleLike,
} }
enum NeverLike; // enum NeverLike;
enum EmptyLike { enum EmptyLike {
Empty, Empty,