29 Commits

Author SHA1 Message Date
fc3cbbf450 Conlang v0.0.5: Pratternization
cl-token:
- Minimize data redundancy by consolidating TokenKind::Literal; TokenData::{String, Identifier}
- Rename Op to Punct

cl-ast:
- Remove ExprKind::{Member, Call} in favor of making them
'binary' operators
- Consolidate boxes (TODO: consolidate more boxes)
- Remove repetition vecs in favor of boxes (this may come with performance tradeoffs!)

cl-lexer:
- Reflect changes from cl-token

cl-interpret, cl-repl/src/examples:
- Reflect changes from cl-ast

cl-parser:
- Switch to Pratt parsing for expressions
  - TODO: Code cleanup
  - TODO: Use total ordering for Precedence instead of binding powers (that's what the binding powers are there for anyway)
- Switch functional parsers to take Punct instead of TokenKind
  - It's not like we need a `for`-separated list
- Remove `binary` macro. No longer needed with precedence climbing.
- Repurpose `operator` macro to produce both the operator and the respective Precedence
- Remove several of the smaller parser functions, since they've been consolidated into the larger `exprkind`
2024-04-13 03:33:26 -05:00
2c36ccc0cf cl-parser: Misc. cleanup 2024-04-13 03:02:54 -05:00
265db668ed cl-parser: Reword an error message 2024-04-13 02:57:29 -05:00
fa51f14db5 Remove collect-identifiers example 2024-04-13 02:54:30 -05:00
3b0190b389 cl-interpret: remove Loc from error type
This allows removal of intermediate expression metadata from the AST
2024-04-13 02:54:02 -05:00
21c9909f0c cl-token: make Token fields public
No sense in having them private, they're just plain old data.
2024-04-13 02:48:16 -05:00
290ede2fa3 cl-token: Break operators into their own separate enum, to make future pratt parsing easier 2024-04-12 16:20:24 -05:00
2091cce570 cl-token: Rename Type to TokenKind, Data to TokenData to match the project's general style 2024-04-12 14:36:26 -05:00
902494e95a cl-token: Merge token_type::Type and token_type::Keyword into a single enum 2024-04-12 14:25:49 -05:00
a213c7f70a grammar: Minor cleanup, fix compat with Grammatical parser 2024-04-11 19:59:00 -05:00
8dfddb739e cl-ast: Remove unused monovariant MemberKind enum 2024-04-06 01:03:01 -05:00
a31d285d99 grammar.ebnf: Clean up grammar
- TODO: Member access is totally broken lmao
2024-04-06 01:02:31 -05:00
a036ce260d cl-ast: Add doc comments for every node
This improves the rustdoc output somewhat
2024-04-03 13:19:57 -05:00
4a52d2bc6a conlang: Update type checker
- cl-typeck: Add modules, intrinsic types, unify definition ids
- cl-ast: make attribute lists `Default`
- cl-structures: Add functions to iterate through a pool
- cl-repl: Create an example REPL for the type checker
2024-04-01 05:14:06 -05:00
614d20ea2c cl-parser: parse enums + enum variants 2024-04-01 04:28:30 -05:00
7b40ddc845 cl-ast: destination side of type alias should be an identifier 2024-04-01 04:20:26 -05:00
bdf0bb68ca cl-ast: improve formatting of enums 2024-04-01 04:18:31 -05:00
8ee318f26b cl-ast: Move ExprKind::Assign outside the box, to be more consistent with other uses of Expr 2024-03-28 16:34:24 -05:00
ba148ef5de cl-typeck: Continue work on symbol namespaces
- Refactor for cl-structures intern pool
- Create a list of types the compiler is supposed to care about/have implementations for (Intrinsic/primitive types)
- Add modules and projects (the sym equivalent of ast::File)
- Flesh out value definitions
  - TODO: Create an IR for statements and expressions, and lower the AST into it
2024-03-27 01:54:19 -05:00
8cbe570811 cl-structures: Sketch out a type-safe generic interning pool 2024-03-27 01:26:27 -05:00
66c29d601c cl-structures: Tree cleanup 2024-03-27 01:25:19 -05:00
9f9a21b4c3 cl-repl: Add example that prints the AST in a more friendly way than Debug but in a more verbose way than Display 2024-03-27 01:24:28 -05:00
2cdf112aa6 cl-structures: add todo w/r/t tree traversal api 2024-03-15 05:53:26 -05:00
af35dd1bb3 cl-structures: add a sized, monotype stack 2024-03-15 05:11:47 -05:00
ecde44910f cl-structures: Add a simple potted tree structure, for future use in the module system 2024-03-15 05:10:34 -05:00
a74cd0b8ac cl-structures: break span into its own file. 2024-03-12 19:32:11 -05:00
2eade74d3a cl-repl: Terminal pipe support + fun stylistic fixups 2024-03-01 05:33:35 -06:00
9cae7e4eb8 cl-lexer: switch to unicode-ident crate, since a dependency of cl-repl depends on it. 2024-03-01 04:11:38 -06:00
a07312bf92 cl-lexer: fix link in doc comment 2024-03-01 03:17:43 -06:00
30 changed files with 3809 additions and 1677 deletions

View File

@@ -13,7 +13,7 @@ resolver = "2"
[workspace.package]
repository = "https://git.soft.fish/j/Conlang"
version = "0.0.4"
version = "0.0.5"
authors = ["John Breaux <j@soft.fish>"]
edition = "2021"
license = "MIT"

View File

@@ -5,7 +5,10 @@ mod display {
//! Implements [Display] for [AST](super::super) Types
use super::*;
pub use delimiters::*;
use std::fmt::{Display, Write};
use std::{
borrow::Borrow,
fmt::{Display, Write},
};
mod delimiters {
#![allow(dead_code)]
#[derive(Clone, Copy, Debug)]
@@ -13,6 +16,8 @@ mod display {
pub open: &'t str,
pub close: &'t str,
}
/// Delimits with braces decorated with spaces `" {n"`, ..., `"\n}"`
pub const SPACED_BRACES: Delimiters = Delimiters { open: " {\n", close: "\n}" };
/// Delimits with braces on separate lines `{\n`, ..., `\n}`
pub const BRACES: Delimiters = Delimiters { open: "{\n", close: "\n}" };
/// Delimits with parentheses on separate lines `{\n`, ..., `\n}`
@@ -190,9 +195,7 @@ mod display {
match self {
StructKind::Empty => ';'.fmt(f),
StructKind::Tuple(v) => delimit(separate(v, ", "), INLINE_PARENS)(f),
StructKind::Struct(v) => {
delimit(separate(v, ",\n"), Delimiters { open: " {\n", ..BRACES })(f)
}
StructKind::Struct(v) => delimit(separate(v, ",\n"), SPACED_BRACES)(f),
}
}
}
@@ -211,8 +214,8 @@ mod display {
impl Display for EnumKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EnumKind::NoVariants => todo!(),
EnumKind::Variants(v) => separate(v, ", ")(f),
EnumKind::NoVariants => ';'.fmt(f),
EnumKind::Variants(v) => delimit(separate(v, ",\n"), SPACED_BRACES)(f),
}
}
}
@@ -226,9 +229,9 @@ mod display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
VariantKind::Plain => Ok(()),
VariantKind::CLike(n) => n.fmt(f),
VariantKind::CLike(n) => write!(f, " = {n}"),
VariantKind::Tuple(v) => delimit(separate(v, ", "), INLINE_PARENS)(f),
VariantKind::Struct(v) => delimit(separate(v, ",\n"), BRACES)(f),
VariantKind::Struct(v) => delimit(separate(v, ", "), INLINE_BRACES)(f),
}
}
}
@@ -307,13 +310,16 @@ mod display {
impl Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.kind {
self.kind.fmt(f)
}
}
impl Display for ExprKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ExprKind::Assign(v) => v.fmt(f),
ExprKind::Binary(v) => v.fmt(f),
ExprKind::Unary(v) => v.fmt(f),
ExprKind::Index(v) => v.fmt(f),
ExprKind::Call(v) => v.fmt(f),
ExprKind::Member(v) => v.fmt(f),
ExprKind::Path(v) => v.fmt(f),
ExprKind::Literal(v) => v.fmt(f),
ExprKind::Array(v) => v.fmt(f),
@@ -334,8 +340,8 @@ mod display {
}
impl Display for Assign {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, op, tail } = self;
write!(f, "{head} {op} {tail}")
let Self { kind, parts } = self;
write!(f, "{} {kind} {}", parts.0, parts.1)
}
}
impl Display for AssignKind {
@@ -358,12 +364,13 @@ mod display {
}
impl Display for Binary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, tail } = self;
write!(f, "{head}")?;
for (kind, expr) in tail {
write!(f, " {kind} {expr}")?;
let Self { kind, parts } = self;
let (head, tail) = parts.borrow();
match kind {
BinaryKind::Dot => write!(f, "{head}{kind}{tail}"),
BinaryKind::Call => write!(f, "{head}{tail}"),
_ => write!(f, "{head} {kind} {tail}"),
}
Ok(())
}
}
impl Display for BinaryKind {
@@ -391,17 +398,15 @@ mod display {
BinaryKind::Div => "/",
BinaryKind::Rem => "%",
BinaryKind::Dot => ".",
BinaryKind::Call => "()",
}
.fmt(f)
}
}
impl Display for Unary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { ops: kinds, tail } = self;
for kind in kinds {
kind.fmt(f)?
}
tail.fmt(f)
let Self { kind, tail } = self;
write!(f, "{kind}{tail}")
}
}
impl Display for UnaryKind {
@@ -416,29 +421,11 @@ mod display {
.fmt(f)
}
}
impl Display for Call {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { callee, args } = self;
callee.fmt(f)?;
for args in args {
args.fmt(f)?;
}
Ok(())
}
}
impl Display for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
delimit(separate(&self.exprs, ", "), INLINE_PARENS)(f)
}
}
impl Display for Member {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head: parent, tail: children } = self;
write!(f, "{parent}.")?;
separate(children, ".")(f)?;
Ok(())
}
}
impl Display for Index {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, indices } = self;
@@ -449,11 +436,6 @@ mod display {
Ok(())
}
}
impl Display for Indices {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
delimit(separate(&self.exprs, ", "), INLINE_SQUARE)(f)
}
}
impl Display for Path {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { absolute, parts } = self;
@@ -563,7 +545,6 @@ mod display {
}
}
mod convert {
//! Converts between major enums and enum variants
use super::*;
@@ -625,8 +606,6 @@ mod convert {
Assign => ExprKind::Assign,
Binary => ExprKind::Binary,
Unary => ExprKind::Unary,
Call => ExprKind::Call,
Member => ExprKind::Member,
Index => ExprKind::Index,
Path => ExprKind::Path,
Literal => ExprKind::Literal,
@@ -647,18 +626,7 @@ mod convert {
bool => Literal::Bool,
char => Literal::Char,
u128 => Literal::Int,
&str => Literal::String,
}
}
impl From<Tuple> for Indices {
fn from(value: Tuple) -> Self {
Self { exprs: value.exprs }
}
}
impl From<Indices> for Tuple {
fn from(value: Indices) -> Self {
Self { exprs: value.exprs }
String => Literal::String,
}
}

View File

@@ -17,6 +17,7 @@ use cl_structures::span::*;
pub mod ast_impl;
pub mod format;
/// Whether a binding ([Static] or [Let]) or reference is mutable or not
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Mutability {
#[default]
@@ -24,6 +25,7 @@ pub enum Mutability {
Mut,
}
/// Whether an [Item] is visible outside of the current [Module]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Visibility {
#[default]
@@ -31,23 +33,26 @@ pub enum Visibility {
Public,
}
/// A list of [Item]s
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct File {
pub items: Vec<Item>,
}
// Metadata decorators
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Attrs {
pub meta: Vec<Meta>,
}
/// A metadata decorator
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Meta {
pub name: Identifier,
pub kind: MetaKind,
}
/// Information attached to [Meta]data
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MetaKind {
Plain,
@@ -56,7 +61,7 @@ pub enum MetaKind {
}
// Items
/// Stores an [ItemKind] and associated metadata
/// Anything that can appear at the top level of a [File]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Item {
pub extents: Span,
@@ -65,36 +70,37 @@ pub struct Item {
pub kind: ItemKind,
}
/// Stores a concrete Item
/// What kind of [Item] is this?
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ItemKind {
// TODO: Import declaration ("use") item
// TODO: Trait declaration ("trait") item?
/// A [module](Module)
Module(Module),
/// A [type alias](Alias)
Alias(Alias),
/// An [enumerated type](Enum), with a discriminant and optional data
Enum(Enum),
/// A [structure](Struct)
Struct(Struct),
/// A [constant](Const)
Const(Const),
/// A [static](Static) variable
Static(Static),
/// A [module](Module)
Module(Module),
/// A [function definition](Function)
Function(Function),
/// A [structure](Struct)
Struct(Struct),
/// An [enumerated type](Enum)
Enum(Enum),
/// An [implementation](Impl)
Impl(Impl),
}
/// An alias to another [Ty]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Alias {
pub to: Box<Ty>,
pub to: Identifier,
pub from: Option<Box<Ty>>,
}
/// Stores a `const` value
/// A compile-time constant
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Const {
pub name: Identifier,
@@ -102,7 +108,7 @@ pub struct Const {
pub init: Box<Expr>,
}
/// Stores a `static` variable
/// A `static` variable
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Static {
pub mutable: Mutability,
@@ -111,20 +117,21 @@ pub struct Static {
pub init: Box<Expr>,
}
/// Stores a collection of [Items](Item)
/// An ordered collection of [Items](Item)
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Module {
pub name: Identifier,
pub kind: ModuleKind,
}
/// The contents of a [Module], if they're in the same file
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ModuleKind {
Inline(File),
Outline,
}
/// Contains code, and the interface to that code
/// Code, and the interface to that code
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Function {
pub name: Identifier,
@@ -133,6 +140,7 @@ pub struct Function {
pub rety: Option<Box<Ty>>,
}
/// A single parameter for a [Function]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Param {
pub mutability: Mutability,
@@ -140,12 +148,14 @@ pub struct Param {
pub ty: Box<Ty>,
}
/// A user-defined product type
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Struct {
pub name: Identifier,
pub kind: StructKind,
}
/// Either a [Struct]'s [StructMember]s or tuple [Ty]pes, if present.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StructKind {
Empty,
@@ -153,6 +163,7 @@ pub enum StructKind {
Struct(Vec<StructMember>),
}
/// The [Visibility], [Identifier], and [Ty]pe of a single [Struct] member
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StructMember {
pub vis: Visibility,
@@ -160,12 +171,14 @@ pub struct StructMember {
pub ty: Ty,
}
/// A user-defined sum type
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Enum {
pub name: Identifier,
pub kind: EnumKind,
}
/// An [Enum]'s [Variant]s, if it has a variant block
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EnumKind {
/// Represents an enum with no variants
@@ -173,12 +186,14 @@ pub enum EnumKind {
Variants(Vec<Variant>),
}
/// A single [Enum] variant
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Variant {
pub name: Identifier,
pub kind: VariantKind,
}
/// Whether the [Variant] has a C-like constant value, a tuple, or [StructMember]s
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum VariantKind {
Plain,
@@ -187,6 +202,7 @@ pub enum VariantKind {
Struct(Vec<StructMember>),
}
/// Sub-[items](Item) (associated functions, etc.) for a [Ty]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Impl {
pub target: Ty,
@@ -200,13 +216,14 @@ pub enum ImplKind {
Trait { impl_trait: Path, for_type: Box<Ty> },
}
/// # Static Type Information
/// A type expression
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Ty {
pub extents: Span,
pub kind: TyKind,
}
/// Information about a [Ty]pe expression
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TyKind {
Never,
@@ -218,29 +235,34 @@ pub enum TyKind {
Fn(TyFn),
}
/// A tuple of [Ty]pes
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TyTuple {
pub types: Vec<Ty>,
}
/// A [Ty]pe-reference expression as (number of `&`, [Path])
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TyRef {
pub count: u16,
pub to: Path,
}
/// The args and return value for a function pointer [Ty]pe
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TyFn {
pub args: TyTuple,
pub rety: Option<Box<Ty>>,
}
// Path
/// A path to an [Item] in the [Module] tree
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Path {
pub absolute: bool,
pub parts: Vec<PathPart>,
}
/// A single component of a [Path]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PathPart {
SuperKw,
@@ -249,10 +271,11 @@ pub enum PathPart {
}
// TODO: Capture token?
/// A name
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Identifier(pub String);
/// Stores an abstract statement, and associated metadata
/// An abstract statement, and associated metadata
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Stmt {
pub extents: Span,
@@ -260,12 +283,14 @@ pub struct Stmt {
pub semi: Semi,
}
/// Whether or not a [Stmt] is followed by a semicolon
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Semi {
Terminated,
Unterminated,
}
/// Whether the [Stmt] is a [Let], [Item], or [Expr] statement
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StmtKind {
Empty,
@@ -274,6 +299,7 @@ pub enum StmtKind {
Expr(Box<Expr>),
}
/// A local variable declaration [Stmt]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Let {
pub mutable: Mutability,
@@ -282,25 +308,22 @@ pub struct Let {
pub init: Option<Box<Expr>>,
}
/// Stores an abstract expression, and associated metadata
/// An expression, the beating heart of the language
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Expr {
pub extents: Span,
pub kind: ExprKind,
}
/// Any of the different [Expr]essions
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExprKind {
/// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+
Assign(Box<Assign>),
Assign(Assign),
/// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+
Binary(Binary),
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
Unary(Unary),
/// A [Member] access expression: [`Expr`] (`.` [`Expr`])+
Member(Member),
/// A [Call] expression, with arguments: a(foo, bar)
Call(Call),
/// An Array [Index] expression: a[10, 20, 30]
Index(Index),
/// A [path expression](Path): `::`? [PathPart] (`::` [PathPart])*
@@ -336,11 +359,11 @@ pub enum ExprKind {
Continue(Continue),
}
/// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Assign {
pub head: Expr,
pub op: AssignKind,
pub tail: Box<Expr>,
pub kind: AssignKind,
pub parts: Box<(ExprKind, ExprKind)>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -359,12 +382,14 @@ pub enum AssignKind {
Rem,
}
/// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Binary {
pub head: Box<Expr>,
pub tail: Vec<(BinaryKind, Expr)>,
pub kind: BinaryKind,
pub parts: Box<(ExprKind, ExprKind)>,
}
/// A [Binary] operator
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BinaryKind {
Lt,
@@ -389,15 +414,18 @@ pub enum BinaryKind {
Div,
Rem,
Dot,
Call,
}
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Unary {
pub ops: Vec<UnaryKind>,
pub tail: Box<Expr>,
pub kind: UnaryKind,
pub tail: Box<ExprKind>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
/// A [Unary] operator
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UnaryKind {
Deref,
Neg,
@@ -407,36 +435,14 @@ pub enum UnaryKind {
/// Unused
Tilde,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Member {
pub head: Box<Expr>,
pub tail: Vec<Expr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MemberKind {
Dot,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Call {
pub callee: Box<Expr>,
pub args: Vec<Tuple>,
}
/// Index operator: Member (`[` Expr `]`)*
/// A repeated [Index] expression: a[10, 20, 30][40, 50, 60]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Index {
pub head: Box<Expr>,
pub indices: Vec<Indices>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Indices {
pub exprs: Vec<Expr>,
pub head: Box<ExprKind>,
pub indices: Vec<Expr>,
}
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Literal {
Bool(bool),
@@ -445,39 +451,47 @@ pub enum Literal {
String(String),
}
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Array {
pub values: Vec<Expr>,
}
/// An Array literal constructed with [repeat syntax](ArrayRep)
/// `[` [Expr] `;` [Literal] `]`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ArrayRep {
pub value: Box<Expr>,
pub repeat: Box<Expr>,
pub value: Box<ExprKind>,
pub repeat: Box<ExprKind>,
}
/// An address-of expression: `&` `mut`? [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AddrOf {
pub count: usize,
pub mutable: Mutability,
pub expr: Box<Expr>,
pub expr: Box<ExprKind>,
}
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Block {
pub stmts: Vec<Stmt>,
}
/// A [Grouping](Group) expression `(` [`Expr`] `)`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Group {
pub expr: Box<Expr>,
pub expr: Box<ExprKind>,
}
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Tuple {
pub exprs: Vec<Expr>,
}
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct While {
pub cond: Box<Expr>,
@@ -485,6 +499,7 @@ pub struct While {
pub fail: Else,
}
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct If {
pub cond: Box<Expr>,
@@ -492,6 +507,7 @@ pub struct If {
pub fail: Else,
}
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct For {
pub bind: Identifier, // TODO: Patterns?
@@ -500,20 +516,24 @@ pub struct For {
pub fail: Else,
}
/// The (optional) `else` clause of a [While], [If], or [For] expression
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Else {
pub body: Option<Box<Expr>>,
}
/// A [Break] expression: `break` [`Expr`]?
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Break {
pub body: Option<Box<Expr>>,
}
/// A [Return] expression `return` [`Expr`]?
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Return {
pub body: Option<Box<Expr>>,
}
/// A continue expression: `continue`
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Continue;

View File

@@ -5,6 +5,8 @@
//! meaningless to get a pointer to one, and would be undefined behavior to dereference a pointer to
//! one in any situation.
use std::borrow::Borrow;
use super::*;
use cl_ast::*;
/// A work-in-progress tree walk interpreter for Conlang
@@ -107,14 +109,18 @@ impl Interpret for Let {
}
}
impl Interpret for Expr {
#[inline]
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { extents: _, kind } = self;
match kind {
kind.interpret(env)
}
}
impl Interpret for ExprKind {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
match self {
ExprKind::Assign(v) => v.interpret(env),
ExprKind::Binary(v) => v.interpret(env),
ExprKind::Unary(v) => v.interpret(env),
ExprKind::Member(v) => v.interpret(env),
ExprKind::Call(v) => v.interpret(env),
ExprKind::Index(v) => v.interpret(env),
ExprKind::Path(v) => v.interpret(env),
ExprKind::Literal(v) => v.interpret(env),
@@ -136,24 +142,23 @@ impl Interpret for Expr {
}
impl Interpret for Assign {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Assign { head, op, tail } = self;
let Assign { kind: op, parts } = self;
let (head, tail) = parts.borrow();
// Resolve the head pattern
let head = match &head.kind {
let head = match &head {
ExprKind::Path(Path { parts, .. }) if parts.len() == 1 => {
match parts.last().expect("parts should not be empty") {
PathPart::SuperKw => Err(Error::NotAssignable(head.extents.head))?,
PathPart::SuperKw => Err(Error::NotAssignable)?,
PathPart::SelfKw => todo!("Assignment to `self`"),
PathPart::Ident(Identifier(s)) => s,
}
}
ExprKind::Member(_) => todo!("Member access assignment"),
ExprKind::Call(_) => todo!("Assignment to the result of a function call?"),
ExprKind::Index(_) => todo!("Assignment to an index operation"),
ExprKind::Path(_) => todo!("Path expression resolution (IMPORTANT)"),
ExprKind::Empty | ExprKind::Group(_) | ExprKind::Tuple(_) => {
todo!("Pattern Destructuring?")
}
_ => Err(Error::NotAssignable(head.extents.head))?,
_ => Err(Error::NotAssignable)?,
};
// Get the initializer and the tail
let init = tail.interpret(env)?;
@@ -193,33 +198,36 @@ impl Interpret for Assign {
}
impl Interpret for Binary {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Binary { head, tail } = self;
let mut head = head.interpret(env)?;
let Binary { kind, parts } = self;
let (head, tail) = parts.borrow();
let head = head.interpret(env)?;
// Short-circuiting ops
for (op, tail) in tail {
match op {
match kind {
BinaryKind::LogAnd => {
if head.truthy()? {
head = tail.interpret(env)?;
continue;
}
return Ok(head); // Short circuiting
return if head.truthy()? {
tail.interpret(env)
} else {
Ok(head)
}; // Short circuiting
}
BinaryKind::LogOr => {
if !head.truthy()? {
head = tail.interpret(env)?;
continue;
}
return Ok(head); // Short circuiting
return if !head.truthy()? {
tail.interpret(env)
} else {
Ok(head)
}; // Short circuiting
}
BinaryKind::LogXor => {
head = ConValue::Bool(head.truthy()? ^ tail.interpret(env)?.truthy()?);
continue;
return Ok(ConValue::Bool(
head.truthy()? ^ tail.interpret(env)?.truthy()?,
));
}
_ => {}
}
let tail = tail.interpret(env)?;
head = match op {
match kind {
BinaryKind::Mul => env.call("mul", &[head, tail]),
BinaryKind::Div => env.call("div", &[head, tail]),
BinaryKind::Rem => env.call("rem", &[head, tail]),
@@ -239,61 +247,39 @@ impl Interpret for Binary {
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),
}?;
}
Ok(head)
}
}
}
impl Interpret for Unary {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Unary { tail, ops } = self;
let mut operand = tail.interpret(env)?;
for op in ops.iter().rev() {
operand = match op {
UnaryKind::Deref => env.call("deref", &[operand])?,
UnaryKind::Neg => env.call("neg", &[operand])?,
UnaryKind::Not => env.call("not", &[operand])?,
let Unary { kind, tail } = self;
let operand = tail.interpret(env)?;
match kind {
UnaryKind::Deref => env.call("deref", &[operand]),
UnaryKind::Neg => env.call("neg", &[operand]),
UnaryKind::Not => env.call("not", &[operand]),
UnaryKind::At => {
println!("{operand}");
operand
}
UnaryKind::Tilde => unimplemented!("Tilde operator"),
};
}
Ok(operand)
}
UnaryKind::Tilde => unimplemented!("Tilde operator"),
}
impl Interpret for Member {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
todo!("Interpret member accesses in {env}")
}
}
impl Interpret for Call {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { callee, args } = self;
// evaluate the callee
let mut callee = callee.interpret(env)?;
for args in args {
let ConValue::Tuple(args) = args.interpret(env)? else {
Err(Error::TypeError)?
};
callee = callee.call(env, &args)?;
}
Ok(callee)
}
}
impl Interpret for Index {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { head, indices } = self;
let mut head = head.interpret(env)?;
for indices in indices {
let Indices { exprs } = indices;
for index in exprs {
for index in indices {
head = head.index(&index.interpret(env)?)?;
}
}
Ok(head)
}
}

View File

@@ -522,7 +522,6 @@ pub mod error {
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
use super::temp_type_impl::ConValue;
use cl_structures::span::Loc;
pub type IResult<T> = Result<T, Error>;
@@ -546,12 +545,12 @@ pub mod error {
TypeError,
/// In clause of For loop didn't yield a Range
NotIterable,
/// A value at this [location](struct@Loc) can't be indexed
NotIndexable(Loc),
/// A value could not be indexed
NotIndexable,
/// An array index went out of bounds
OobIndex(usize, usize),
/// An expression at this [location](struct@Loc)ation is not assignable
NotAssignable(Loc),
/// An expression is not assignable
NotAssignable,
/// A name was not defined in scope before being used
NotDefined(String),
/// A name was defined but not initialized
@@ -578,14 +577,14 @@ pub mod error {
Error::ScopeExit => "Exited the last scope. This is a logic bug.".fmt(f),
Error::TypeError => "Incompatible types".fmt(f),
Error::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
Error::NotIndexable(location) => {
write!(f, "{location} expression cannot be indexed")
Error::NotIndexable => {
write!(f, "expression cannot be indexed")
}
Error::OobIndex(idx, len) => {
write!(f, "Index out of bounds: index was {idx}. but len is {len}")
}
Error::NotAssignable(location) => {
write!(f, "{location} expression is not assignable")
Error::NotAssignable => {
write!(f, "expression is not assignable")
}
Error::NotDefined(value) => {
write!(f, "{value} not bound. Did you mean `let {value};`?")

View File

@@ -1,8 +1,8 @@
#![allow(unused_imports)]
use crate::{env::Environment, temp_type_impl::ConValue, Interpret};
use cl_ast::*;
use cl_parser::Parser;
use cl_lexer::Lexer;
use cl_parser::Parser;
pub use macros::*;
mod macros {
@@ -187,7 +187,7 @@ mod fn_declarations {
assert_eval!(env, fn empty_fn() {});
// TODO: true equality for functions
assert_eq!(
"fn empty_fn",
"fn empty_fn () {\n \n}",
format!(
"{}",
env.get("empty_fn")

View File

@@ -10,4 +10,4 @@ publish.workspace = true
[dependencies]
cl-token = { path = "../cl-token" }
cl-structures = { path = "../cl-structures" }
unicode-xid = "0.2.4"
unicode-ident = "1.0.12"

View File

@@ -2,12 +2,12 @@
#![warn(clippy::all)]
#![feature(decl_macro)]
use cl_structures::span::Loc;
use cl_token::*;
use cl_token::{TokenKind as Kind, *};
use std::{
iter::Peekable,
str::{Chars, FromStr},
};
use unicode_xid::UnicodeXID;
use unicode_ident::*;
#[cfg(test)]
mod tests;
@@ -51,7 +51,8 @@ pub mod lexer_iter {
///
/// # Examples
/// ```rust
/// # use conlang::lexer::Lexer;
/// # use cl_lexer::Lexer;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // Read in your code from somewhere
/// let some_code = "
/// fn main () {
@@ -61,16 +62,17 @@ pub mod lexer_iter {
/// // Create a lexer over your code
/// let mut lexer = Lexer::new(some_code);
/// // Scan for a single token
/// let first_token = lexer.scan().unwrap();
/// let first_token = lexer.scan()?;
/// println!("{first_token:?}");
/// // Loop over all the rest of the tokens
/// for token in lexer {
/// # let token: Result<_,()> = Ok(token.unwrap());
/// # let token: Result<_,()> = Ok(token?);
/// match token {
/// Ok(token) => println!("{token:?}"),
/// Err(e) => eprintln!("{e:?}"),
/// }
/// }
/// # Ok(()) }
/// ```
#[derive(Clone, Debug)]
pub struct Lexer<'t> {
@@ -95,40 +97,40 @@ impl<'t> Lexer<'t> {
/// Scans through the text, searching for the next [Token]
pub fn scan(&mut self) -> LResult<Token> {
match self.skip_whitespace().peek()? {
'{' => self.consume()?.produce(Type::LCurly, ()),
'}' => self.consume()?.produce(Type::RCurly, ()),
'[' => self.consume()?.produce(Type::LBrack, ()),
']' => self.consume()?.produce(Type::RBrack, ()),
'(' => self.consume()?.produce(Type::LParen, ()),
')' => self.consume()?.produce(Type::RParen, ()),
'{' => self.consume()?.produce_op(Punct::LCurly),
'}' => self.consume()?.produce_op(Punct::RCurly),
'[' => self.consume()?.produce_op(Punct::LBrack),
']' => self.consume()?.produce_op(Punct::RBrack),
'(' => self.consume()?.produce_op(Punct::LParen),
')' => self.consume()?.produce_op(Punct::RParen),
'&' => self.consume()?.amp(),
'@' => self.consume()?.produce(Type::At, ()),
'\\' => self.consume()?.produce(Type::Backslash, ()),
'@' => self.consume()?.produce_op(Punct::At),
'\\' => self.consume()?.produce_op(Punct::Backslash),
'!' => self.consume()?.bang(),
'|' => self.consume()?.bar(),
':' => self.consume()?.colon(),
',' => self.consume()?.produce(Type::Comma, ()),
',' => self.consume()?.produce_op(Punct::Comma),
'.' => self.consume()?.dot(),
'=' => self.consume()?.equal(),
'`' => self.consume()?.produce(Type::Grave, ()),
'`' => self.consume()?.produce_op(Punct::Grave),
'>' => self.consume()?.greater(),
'#' => self.consume()?.hash(),
'<' => self.consume()?.less(),
'-' => self.consume()?.minus(),
'+' => self.consume()?.plus(),
'?' => self.consume()?.produce(Type::Question, ()),
'?' => self.consume()?.produce_op(Punct::Question),
'%' => self.consume()?.rem(),
';' => self.consume()?.produce(Type::Semi, ()),
';' => self.consume()?.produce_op(Punct::Semi),
'/' => self.consume()?.slash(),
'*' => self.consume()?.star(),
'~' => self.consume()?.produce(Type::Tilde, ()),
'~' => self.consume()?.produce_op(Punct::Tilde),
'^' => self.consume()?.xor(),
'0' => self.consume()?.int_with_base(),
'1'..='9' => self.digits::<10>(),
'"' => self.consume()?.string(),
'\'' => self.consume()?.character(),
'_' => self.identifier(),
i if i.is_xid_start() => self.identifier(),
i if is_xid_start(i) => self.identifier(),
e => {
let err = Err(Error::unexpected_char(e, self.line(), self.col()));
let _ = self.consume();
@@ -155,11 +157,14 @@ impl<'t> Lexer<'t> {
.copied()
.ok_or(Error::end_of_file(self.line(), self.col()))
}
fn produce(&mut self, ty: Type, data: impl Into<Data>) -> LResult<Token> {
fn produce(&mut self, kind: TokenKind, data: impl Into<TokenData>) -> LResult<Token> {
let loc = self.start_loc;
self.start_loc = self.current_loc;
self.start = self.current;
Ok(Token::new(ty, data, loc.0, loc.1))
Ok(Token::new(kind, data, loc.0, loc.1))
}
fn produce_op(&mut self, kind: Punct) -> LResult<Token> {
self.produce(TokenKind::Punct(kind), ())
}
fn skip_whitespace(&mut self) -> &mut Self {
while let Ok(c) = self.peek() {
@@ -190,120 +195,120 @@ impl<'t> Lexer<'t> {
impl<'t> Lexer<'t> {
fn amp(&mut self) -> LResult<Token> {
match self.peek() {
Ok('&') => self.consume()?.produce(Type::AmpAmp, ()),
Ok('=') => self.consume()?.produce(Type::AmpEq, ()),
_ => self.produce(Type::Amp, ()),
Ok('&') => self.consume()?.produce_op(Punct::AmpAmp),
Ok('=') => self.consume()?.produce_op(Punct::AmpEq),
_ => self.produce_op(Punct::Amp),
}
}
fn bang(&mut self) -> LResult<Token> {
match self.peek() {
Ok('!') => self.consume()?.produce(Type::BangBang, ()),
Ok('=') => self.consume()?.produce(Type::BangEq, ()),
_ => self.produce(Type::Bang, ()),
Ok('!') => self.consume()?.produce_op(Punct::BangBang),
Ok('=') => self.consume()?.produce_op(Punct::BangEq),
_ => self.produce_op(Punct::Bang),
}
}
fn bar(&mut self) -> LResult<Token> {
match self.peek() {
Ok('|') => self.consume()?.produce(Type::BarBar, ()),
Ok('=') => self.consume()?.produce(Type::BarEq, ()),
_ => self.produce(Type::Bar, ()),
Ok('|') => self.consume()?.produce_op(Punct::BarBar),
Ok('=') => self.consume()?.produce_op(Punct::BarEq),
_ => self.produce_op(Punct::Bar),
}
}
fn colon(&mut self) -> LResult<Token> {
match self.peek() {
Ok(':') => self.consume()?.produce(Type::ColonColon, ()),
_ => self.produce(Type::Colon, ()),
Ok(':') => self.consume()?.produce_op(Punct::ColonColon),
_ => self.produce_op(Punct::Colon),
}
}
fn dot(&mut self) -> LResult<Token> {
match self.peek() {
Ok('.') => {
if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce(Type::DotDotEq, ())
self.consume()?.produce_op(Punct::DotDotEq)
} else {
self.produce(Type::DotDot, ())
self.produce_op(Punct::DotDot)
}
}
_ => self.produce(Type::Dot, ()),
_ => self.produce_op(Punct::Dot),
}
}
fn equal(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce(Type::EqEq, ()),
Ok('>') => self.consume()?.produce(Type::FatArrow, ()),
_ => self.produce(Type::Eq, ()),
Ok('=') => self.consume()?.produce_op(Punct::EqEq),
Ok('>') => self.consume()?.produce_op(Punct::FatArrow),
_ => self.produce_op(Punct::Eq),
}
}
fn greater(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce(Type::GtEq, ()),
Ok('=') => self.consume()?.produce_op(Punct::GtEq),
Ok('>') => {
if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce(Type::GtGtEq, ())
self.consume()?.produce_op(Punct::GtGtEq)
} else {
self.produce(Type::GtGt, ())
self.produce_op(Punct::GtGt)
}
}
_ => self.produce(Type::Gt, ()),
_ => self.produce_op(Punct::Gt),
}
}
fn hash(&mut self) -> LResult<Token> {
match self.peek() {
Ok('!') => self.consume()?.produce(Type::HashBang, ()),
_ => self.produce(Type::Hash, ()),
Ok('!') => self.consume()?.produce_op(Punct::HashBang),
_ => self.produce_op(Punct::Hash),
}
}
fn less(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce(Type::LtEq, ()),
Ok('=') => self.consume()?.produce_op(Punct::LtEq),
Ok('<') => {
if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce(Type::LtLtEq, ())
self.consume()?.produce_op(Punct::LtLtEq)
} else {
self.produce(Type::LtLt, ())
self.produce_op(Punct::LtLt)
}
}
_ => self.produce(Type::Lt, ()),
_ => self.produce_op(Punct::Lt),
}
}
fn minus(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce(Type::MinusEq, ()),
Ok('>') => self.consume()?.produce(Type::Arrow, ()),
_ => self.produce(Type::Minus, ()),
Ok('=') => self.consume()?.produce_op(Punct::MinusEq),
Ok('>') => self.consume()?.produce_op(Punct::Arrow),
_ => self.produce_op(Punct::Minus),
}
}
fn plus(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce(Type::PlusEq, ()),
_ => self.produce(Type::Plus, ()),
Ok('=') => self.consume()?.produce_op(Punct::PlusEq),
_ => self.produce_op(Punct::Plus),
}
}
fn rem(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce(Type::RemEq, ()),
_ => self.produce(Type::Rem, ()),
Ok('=') => self.consume()?.produce_op(Punct::RemEq),
_ => self.produce_op(Punct::Rem),
}
}
fn slash(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce(Type::SlashEq, ()),
Ok('=') => self.consume()?.produce_op(Punct::SlashEq),
Ok('/') => self.consume()?.line_comment(),
Ok('*') => self.consume()?.block_comment(),
_ => self.produce(Type::Slash, ()),
_ => self.produce_op(Punct::Slash),
}
}
fn star(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce(Type::StarEq, ()),
_ => self.produce(Type::Star, ()),
Ok('=') => self.consume()?.produce_op(Punct::StarEq),
_ => self.produce_op(Punct::Star),
}
}
fn xor(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce(Type::XorEq, ()),
Ok('^') => self.consume()?.produce(Type::XorXor, ()),
_ => self.produce(Type::Xor, ()),
Ok('=') => self.consume()?.produce_op(Punct::XorEq),
Ok('^') => self.consume()?.produce_op(Punct::XorXor),
_ => self.produce_op(Punct::Xor),
}
}
}
@@ -313,7 +318,7 @@ impl<'t> Lexer<'t> {
while Ok('\n') != self.peek() {
self.consume()?;
}
self.produce(Type::Comment, ())
self.produce(Kind::Comment, ())
}
fn block_comment(&mut self) -> LResult<Token> {
while let Ok(c) = self.next() {
@@ -321,7 +326,7 @@ impl<'t> Lexer<'t> {
break;
}
}
self.produce(Type::Comment, ())
self.produce(Kind::Comment, ())
}
}
/// Identifiers
@@ -331,15 +336,15 @@ impl<'t> Lexer<'t> {
while let Ok(c) = self.xid_continue() {
out.push(c)
}
if let Ok(keyword) = Keyword::from_str(&out) {
self.produce(Type::Keyword(keyword), ())
if let Ok(keyword) = Kind::from_str(&out) {
self.produce(keyword, ())
} else {
self.produce(Type::Identifier, Data::Identifier(out.into()))
self.produce(Kind::Identifier, TokenData::String(out))
}
}
fn xid_start(&mut self) -> LResult<char> {
match self.peek()? {
xid if xid == '_' || xid.is_xid_start() => {
xid if xid == '_' || is_xid_start(xid) => {
self.consume()?;
Ok(xid)
}
@@ -348,7 +353,7 @@ impl<'t> Lexer<'t> {
}
fn xid_continue(&mut self) -> LResult<char> {
match self.peek()? {
xid if xid.is_xid_continue() => {
xid if is_xid_continue(xid) => {
self.consume()?;
Ok(xid)
}
@@ -365,7 +370,7 @@ impl<'t> Lexer<'t> {
Ok('o') => self.consume()?.digits::<8>(),
Ok('b') => self.consume()?.digits::<2>(),
Ok('0'..='9') => self.digits::<10>(),
_ => self.produce(Type::Integer, 0),
_ => self.produce(Kind::Literal, 0),
}
}
fn digits<const B: u32>(&mut self) -> LResult<Token> {
@@ -373,7 +378,7 @@ impl<'t> Lexer<'t> {
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
value = value * B as u128 + self.digit::<B>()? as u128;
}
self.produce(Type::Integer, value)
self.produce(Kind::Literal, value)
}
fn digit<const B: u32>(&mut self) -> LResult<u32> {
let digit = self.peek()?;
@@ -394,12 +399,12 @@ impl<'t> Lexer<'t> {
{
value.push(self.unescape()?)
}
self.consume()?.produce(Type::String, value)
self.consume()?.produce(Kind::Literal, value)
}
fn character(&mut self) -> LResult<Token> {
let out = self.unescape()?;
match self.peek()? {
'\'' => self.consume()?.produce(Type::Character, out),
'\'' => self.consume()?.produce(Kind::Literal, out),
_ => Err(Error::unmatched_delimiters('\'', self.line(), self.col())),
}
}
@@ -475,7 +480,7 @@ pub mod error {
pub enum Reason {
/// Found an opening delimiter of type [char], but not the expected closing delimiter
UnmatchedDelimiters(char),
/// Found a character that doesn't belong to any [Type](crate::token::token_type::Type)
/// Found a character that doesn't belong to any [TokenKind](cl_token::TokenKind)
UnexpectedChar(char),
/// Found a character that's not valid in identifiers while looking for an identifier
NotIdentifier(char),

View File

@@ -35,7 +35,7 @@ macro td ($($id:expr),*) {
mod ident {
use super::*;
macro ident ($($id:literal),*) {
[$(Data::Identifier($id.into())),*]
[$(TokenData::String($id.into())),*]
}
test_lexer_data_type! {
underscore { "_ _" => ident!["_", "_"] }
@@ -47,7 +47,7 @@ mod ident {
mod keyword {
use super::*;
macro kw($($k:ident),*) {
[ $(Type::Keyword(Keyword::$k),)* ]
[ $(TokenKind::$k,)* ]
}
test_lexer_output_type! {
kw_break { "break break" => kw![Break, Break] }
@@ -109,59 +109,63 @@ mod string {
}
}
mod punct {
macro op($op:ident) {
TokenKind::Punct(Punct::$op)
}
use super::*;
test_lexer_output_type! {
l_curly { "{ {" => [ Type::LCurly, Type::LCurly ] }
r_curly { "} }" => [ Type::RCurly, Type::RCurly ] }
l_brack { "[ [" => [ Type::LBrack, Type::LBrack ] }
r_brack { "] ]" => [ Type::RBrack, Type::RBrack ] }
l_paren { "( (" => [ Type::LParen, Type::LParen ] }
r_paren { ") )" => [ Type::RParen, Type::RParen ] }
amp { "& &" => [ Type::Amp, Type::Amp ] }
amp_amp { "&& &&" => [ Type::AmpAmp, Type::AmpAmp ] }
amp_eq { "&= &=" => [ Type::AmpEq, Type::AmpEq ] }
arrow { "-> ->" => [ Type::Arrow, Type::Arrow] }
at { "@ @" => [ Type::At, Type::At] }
backslash { "\\ \\" => [ Type::Backslash, Type::Backslash] }
bang { "! !" => [ Type::Bang, Type::Bang] }
bangbang { "!! !!" => [ Type::BangBang, Type::BangBang] }
bangeq { "!= !=" => [ Type::BangEq, Type::BangEq] }
bar { "| |" => [ Type::Bar, Type::Bar] }
barbar { "|| ||" => [ Type::BarBar, Type::BarBar] }
bareq { "|= |=" => [ Type::BarEq, Type::BarEq] }
colon { ": :" => [ Type::Colon, Type::Colon] }
comma { ", ," => [ Type::Comma, Type::Comma] }
dot { ". ." => [ Type::Dot, Type::Dot] }
dotdot { ".. .." => [ Type::DotDot, Type::DotDot] }
dotdoteq { "..= ..=" => [ Type::DotDotEq, Type::DotDotEq] }
eq { "= =" => [ Type::Eq, Type::Eq] }
eqeq { "== ==" => [ Type::EqEq, Type::EqEq] }
fatarrow { "=> =>" => [ Type::FatArrow, Type::FatArrow] }
grave { "` `" => [ Type::Grave, Type::Grave] }
gt { "> >" => [ Type::Gt, Type::Gt] }
gteq { ">= >=" => [ Type::GtEq, Type::GtEq] }
gtgt { ">> >>" => [ Type::GtGt, Type::GtGt] }
gtgteq { ">>= >>=" => [ Type::GtGtEq, Type::GtGtEq] }
hash { "# #" => [ Type::Hash, Type::Hash] }
lt { "< <" => [ Type::Lt, Type::Lt] }
lteq { "<= <=" => [ Type::LtEq, Type::LtEq] }
ltlt { "<< <<" => [ Type::LtLt, Type::LtLt] }
ltlteq { "<<= <<=" => [ Type::LtLtEq, Type::LtLtEq] }
minus { "- -" => [ Type::Minus, Type::Minus] }
minuseq { "-= -=" => [ Type::MinusEq, Type::MinusEq] }
plus { "+ +" => [ Type::Plus, Type::Plus] }
pluseq { "+= +=" => [ Type::PlusEq, Type::PlusEq] }
question { "? ?" => [ Type::Question, Type::Question] }
rem { "% %" => [ Type::Rem, Type::Rem] }
remeq { "%= %=" => [ Type::RemEq, Type::RemEq] }
semi { "; ;" => [ Type::Semi, Type::Semi] }
slash { "/ /" => [ Type::Slash, Type::Slash] }
slasheq { "/= /=" => [ Type::SlashEq, Type::SlashEq] }
star { "* *" => [ Type::Star, Type::Star] }
stareq { "*= *=" => [ Type::StarEq, Type::StarEq] }
tilde { "~ ~" => [ Type::Tilde, Type::Tilde] }
xor { "^ ^" => [ Type::Xor, Type::Xor] }
xoreq { "^= ^=" => [ Type::XorEq, Type::XorEq] }
xorxor { "^^ ^^" => [ Type::XorXor, Type::XorXor] }
l_curly { "{ {" => [ op!(LCurly), op!(LCurly) ] }
r_curly { "} }" => [ op!(RCurly), op!(RCurly) ] }
l_brack { "[ [" => [ op!(LBrack), op!(LBrack) ] }
r_brack { "] ]" => [ op!(RBrack), op!(RBrack) ] }
l_paren { "( (" => [ op!(LParen), op!(LParen) ] }
r_paren { ") )" => [ op!(RParen), op!(RParen) ] }
amp { "& &" => [ op!(Amp), op!(Amp) ] }
amp_amp { "&& &&" => [ op!(AmpAmp), op!(AmpAmp) ] }
amp_eq { "&= &=" => [ op!(AmpEq), op!(AmpEq) ] }
arrow { "-> ->" => [ op!(Arrow), op!(Arrow)] }
at { "@ @" => [ op!(At), op!(At)] }
backslash { "\\ \\" => [ op!(Backslash), op!(Backslash)] }
bang { "! !" => [ op!(Bang), op!(Bang)] }
bangbang { "!! !!" => [ op!(BangBang), op!(BangBang)] }
bangeq { "!= !=" => [ op!(BangEq), op!(BangEq)] }
bar { "| |" => [ op!(Bar), op!(Bar)] }
barbar { "|| ||" => [ op!(BarBar), op!(BarBar)] }
bareq { "|= |=" => [ op!(BarEq), op!(BarEq)] }
colon { ": :" => [ op!(Colon), op!(Colon)] }
comma { ", ," => [ op!(Comma), op!(Comma)] }
dot { ". ." => [ op!(Dot), op!(Dot)] }
dotdot { ".. .." => [ op!(DotDot), op!(DotDot)] }
dotdoteq { "..= ..=" => [ op!(DotDotEq), op!(DotDotEq)] }
eq { "= =" => [ op!(Eq), op!(Eq)] }
eqeq { "== ==" => [ op!(EqEq), op!(EqEq)] }
fatarrow { "=> =>" => [ op!(FatArrow), op!(FatArrow)] }
grave { "` `" => [ op!(Grave), op!(Grave)] }
gt { "> >" => [ op!(Gt), op!(Gt)] }
gteq { ">= >=" => [ op!(GtEq), op!(GtEq)] }
gtgt { ">> >>" => [ op!(GtGt), op!(GtGt)] }
gtgteq { ">>= >>=" => [ op!(GtGtEq), op!(GtGtEq)] }
hash { "# #" => [ op!(Hash), op!(Hash)] }
lt { "< <" => [ op!(Lt), op!(Lt)] }
lteq { "<= <=" => [ op!(LtEq), op!(LtEq)] }
ltlt { "<< <<" => [ op!(LtLt), op!(LtLt)] }
ltlteq { "<<= <<=" => [ op!(LtLtEq), op!(LtLtEq)] }
minus { "- -" => [ op!(Minus), op!(Minus)] }
minuseq { "-= -=" => [ op!(MinusEq), op!(MinusEq)] }
plus { "+ +" => [ op!(Plus), op!(Plus)] }
pluseq { "+= +=" => [ op!(PlusEq), op!(PlusEq)] }
question { "? ?" => [ op!(Question), op!(Question)] }
rem { "% %" => [ op!(Rem), op!(Rem)] }
remeq { "%= %=" => [ op!(RemEq), op!(RemEq)] }
semi { "; ;" => [ op!(Semi), op!(Semi)] }
slash { "/ /" => [ op!(Slash), op!(Slash)] }
slasheq { "/= /=" => [ op!(SlashEq), op!(SlashEq)] }
star { "* *" => [ op!(Star), op!(Star)] }
stareq { "*= *=" => [ op!(StarEq), op!(StarEq)] }
tilde { "~ ~" => [ op!(Tilde), op!(Tilde)] }
xor { "^ ^" => [ op!(Xor), op!(Xor)] }
xoreq { "^= ^=" => [ op!(XorEq), op!(XorEq)] }
xorxor { "^^ ^^" => [ op!(XorXor), op!(XorXor)] }
}
}

View File

@@ -21,10 +21,10 @@ pub enum ErrorKind {
UnmatchedParentheses,
UnmatchedCurlyBraces,
UnmatchedSquareBrackets,
Unexpected(Type),
Unexpected(TokenKind),
Expected {
want: Type,
got: Type,
want: TokenKind,
got: TokenKind,
},
/// No rules matched
Nothing,
@@ -130,7 +130,7 @@ impl Display for ErrorKind {
ErrorKind::UnmatchedSquareBrackets => write!(f, "Unmatched square brackets"),
ErrorKind::Unexpected(t) => write!(f, "Encountered unexpected token `{t}`"),
ErrorKind::Expected { want: e, got: g } => {
write!(f, "Expected {e}, but got {g}")
write!(f, "Expected `{e}`, got `{g}`")
}
ErrorKind::Nothing => write!(f, "Nothing found"),
ErrorKind::Todo => write!(f, "TODO:"),

File diff suppressed because it is too large Load Diff

View File

@@ -20,3 +20,4 @@ argh = "0.1.12"
[dev-dependencies]
cl-structures = { path = "../cl-structures" }
cl-typeck = { path = "../cl-typeck" }

View File

@@ -1,525 +0,0 @@
//! Collects identifiers into a list
use cl_lexer::Lexer;
use cl_parser::Parser;
use cl_repl::repline::Repline;
use cl_structures::span::Loc;
use std::{
collections::HashMap,
error::Error,
fmt::Display,
ops::{Deref, DerefMut},
};
fn main() -> Result<(), Box<dyn Error>> {
let mut rl = Repline::new("\x1b[33m", "cl>", "? >");
while let Ok(line) = rl.read() {
let mut parser = Parser::new(Lexer::new(&line));
let code = match parser.stmt() {
Ok(code) => {
rl.accept();
code
}
Err(_) => {
continue;
}
};
let c = Collector::from(&code);
print!("\x1b[G\x1b[J{c}");
}
Ok(())
}
/// Ties the [Collector] location stack to the program stack
pub struct CollectorLoc<'loc, 'code> {
inner: &'loc mut Collector<'code>,
}
impl<'l, 'c> CollectorLoc<'l, 'c> {
pub fn new(c: &'l mut Collector<'c>, loc: Loc) -> Self {
c.location.push(loc);
Self { inner: c }
}
}
impl<'l, 'c> Deref for CollectorLoc<'l, 'c> {
type Target = Collector<'c>;
fn deref(&self) -> &Self::Target {
self.inner
}
}
impl<'l, 'c> DerefMut for CollectorLoc<'l, 'c> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner
}
}
impl<'l, 'c> Drop for CollectorLoc<'l, 'c> {
fn drop(&mut self) {
let Self { inner: c } = self;
c.location.pop();
}
}
#[derive(Clone, Debug, Default)]
pub struct Collector<'code> {
location: Vec<Loc>,
defs: HashMap<&'code str, Vec<Loc>>,
refs: HashMap<&'code str, Vec<Loc>>,
}
impl<'c> Collector<'c> {
pub fn new() -> Self {
Self::default()
}
pub fn location<'loc>(&'loc mut self, loc: Loc) -> CollectorLoc<'loc, 'c> {
CollectorLoc::new(self, loc)
}
pub fn collect(&mut self, collectible: &'c impl Collectible<'c>) -> &mut Self {
collectible.collect(self);
self
}
pub fn define(&mut self, name: &'c str) -> &mut Self {
// println!("Inserted definition of {name}");
let loc = self.location.last().copied().unwrap_or(Loc(0, 0));
self.defs
.entry(name)
.and_modify(|c| c.push(loc))
.or_insert_with(|| vec![loc]);
self
}
pub fn reference(&mut self, name: &'c str) -> &mut Self {
// println!("Inserted usage of {name}");
let loc = self.location.last().copied().unwrap_or(Loc(0, 0));
self.refs
.entry(name)
.and_modify(|c| c.push(loc))
.or_insert_with(|| vec![loc]);
self
}
}
impl<'c> Display for Collector<'c> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { location: _, defs, refs } = self;
writeln!(f, "Definitions:")?;
for (name, locs) in defs {
for loc in locs {
writeln!(f, "{loc} {name}")?;
}
}
writeln!(f, "Usages:")?;
for (name, locs) in refs {
for loc in locs {
writeln!(f, "{loc} {name}")?;
}
}
Ok(())
}
}
impl<'c, C: Collectible<'c>> From<&'c C> for Collector<'c> {
fn from(value: &'c C) -> Self {
let mut c = Self::new();
c.collect(value);
c
}
}
use collectible::Collectible;
pub mod collectible {
use super::Collector;
use cl_ast::*;
pub trait Collectible<'code> {
fn collect(&'code self, c: &mut Collector<'code>);
}
impl<'c> Collectible<'c> for File {
fn collect(&'c self, c: &mut Collector<'c>) {
let File { items } = self;
for item in items {
item.collect(c)
}
}
}
impl<'c> Collectible<'c> for Item {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { extents, attrs: _, vis: _, kind } = self;
kind.collect(&mut c.location(extents.head));
}
}
impl<'c> Collectible<'c> for ItemKind {
fn collect(&'c self, c: &mut Collector<'c>) {
match self {
ItemKind::Alias(f) => f.collect(c),
ItemKind::Const(f) => f.collect(c),
ItemKind::Static(f) => f.collect(c),
ItemKind::Module(f) => f.collect(c),
ItemKind::Function(f) => f.collect(c),
ItemKind::Struct(f) => f.collect(c),
ItemKind::Enum(f) => f.collect(c),
ItemKind::Impl(f) => f.collect(c),
}
}
}
impl<'c> Collectible<'c> for Alias {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { to, from } = self;
c.collect(to).collect(from);
}
}
impl<'c> Collectible<'c> for Const {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { name: Identifier(name), ty, init } = self;
c.define(name).collect(init).collect(ty);
}
}
impl<'c> Collectible<'c> for Static {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { mutable: _, name: Identifier(name), ty, init } = self;
c.define(name).collect(init).collect(ty);
}
}
impl<'c> Collectible<'c> for Module {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { name: Identifier(name), kind } = self;
c.define(name).collect(kind);
}
}
impl<'c> Collectible<'c> for ModuleKind {
fn collect(&'c self, c: &mut Collector<'c>) {
match self {
ModuleKind::Inline(f) => f.collect(c),
ModuleKind::Outline => {}
}
}
}
impl<'c> Collectible<'c> for Function {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { name: Identifier(name), args, body, rety } = self;
c.define(name).collect(args).collect(body).collect(rety);
}
}
impl<'c> Collectible<'c> for Struct {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { name: Identifier(name), kind } = self;
c.define(name).collect(kind);
}
}
impl<'c> Collectible<'c> for StructKind {
fn collect(&'c self, c: &mut Collector<'c>) {
match self {
StructKind::Empty => {}
StructKind::Tuple(k) => k.collect(c),
StructKind::Struct(k) => k.collect(c),
}
}
}
impl<'c> Collectible<'c> for StructMember {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { vis: _, name: Identifier(name), ty } = self;
c.define(name).collect(ty);
}
}
impl<'c> Collectible<'c> for Enum {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { name: Identifier(name), kind } = self;
c.define(name).collect(kind);
}
}
impl<'c> Collectible<'c> for EnumKind {
fn collect(&'c self, c: &mut Collector<'c>) {
match self {
EnumKind::NoVariants => {}
EnumKind::Variants(v) => v.collect(c),
}
}
}
impl<'c> Collectible<'c> for Variant {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { name: Identifier(name), kind } = self;
c.define(name).collect(kind);
}
}
impl<'c> Collectible<'c> for VariantKind {
fn collect(&'c self, c: &mut Collector<'c>) {
match self {
VariantKind::Plain => {}
VariantKind::CLike(_) => {}
VariantKind::Tuple(v) => v.collect(c),
VariantKind::Struct(v) => v.collect(c),
}
}
}
impl<'c> Collectible<'c> for Impl {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { target, body } = self;
c.collect(target).collect(body);
}
}
impl<'c> Collectible<'c> for Block {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { stmts } = self;
for stmt in stmts {
stmt.collect(c);
}
}
}
impl<'c> Collectible<'c> for Stmt {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { extents, kind, semi: _ } = self;
c.location(extents.head).collect(kind);
}
}
impl<'c> Collectible<'c> for StmtKind {
fn collect(&'c self, c: &mut Collector<'c>) {
match self {
StmtKind::Empty => {}
StmtKind::Local(s) => s.collect(c),
StmtKind::Item(s) => s.collect(c),
StmtKind::Expr(s) => s.collect(c),
}
}
}
impl<'c> Collectible<'c> for Let {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { mutable: _, name: Identifier(name), ty, init } = self;
c.collect(init).collect(ty).define(name);
}
}
impl<'c> Collectible<'c> for Expr {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { extents, kind } = self;
c.location(extents.head).collect(kind);
}
}
impl<'c> Collectible<'c> for ExprKind {
fn collect(&'c self, c: &mut Collector<'c>) {
match self {
ExprKind::Assign(k) => k.collect(c),
ExprKind::Binary(k) => k.collect(c),
ExprKind::Unary(k) => k.collect(c),
ExprKind::Member(k) => k.collect(c),
ExprKind::Call(k) => k.collect(c),
ExprKind::Index(k) => k.collect(c),
ExprKind::Path(k) => k.collect(c),
ExprKind::Literal(k) => k.collect(c),
ExprKind::Array(k) => k.collect(c),
ExprKind::ArrayRep(k) => k.collect(c),
ExprKind::AddrOf(k) => k.collect(c),
ExprKind::Block(k) => k.collect(c),
ExprKind::Empty => {}
ExprKind::Group(k) => k.collect(c),
ExprKind::Tuple(k) => k.collect(c),
ExprKind::While(k) => k.collect(c),
ExprKind::If(k) => k.collect(c),
ExprKind::For(k) => k.collect(c),
ExprKind::Break(k) => k.collect(c),
ExprKind::Return(k) => k.collect(c),
ExprKind::Continue(k) => k.collect(c),
}
}
}
impl<'c> Collectible<'c> for Assign {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { head, op: _, tail } = self;
c.collect(head).collect(tail);
}
}
impl<'c> Collectible<'c> for Binary {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { head, tail } = self;
c.collect(head);
for (_, tail) in tail {
c.collect(tail);
}
}
}
impl<'c> Collectible<'c> for Unary {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { ops: _, tail } = self;
c.collect(tail);
}
}
impl<'c> Collectible<'c> for Member {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { head, tail } = self;
c.collect(head).collect(tail);
}
}
impl<'c> Collectible<'c> for Call {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { callee, args } = self;
c.collect(callee).collect(args);
}
}
impl<'c> Collectible<'c> for Tuple {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { exprs } = self;
c.collect(exprs);
}
}
impl<'c> Collectible<'c> for Index {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { head, indices } = self;
c.collect(head).collect(indices);
}
}
impl<'c> Collectible<'c> for Indices {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { exprs } = self;
c.collect(exprs);
}
}
impl<'c> Collectible<'c> for Array {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { values } = self;
c.collect(values);
}
}
impl<'c> Collectible<'c> for ArrayRep {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { value, repeat } = self;
c.collect(value).collect(repeat);
}
}
impl<'c> Collectible<'c> for AddrOf {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { count: _, mutable: _, expr } = self;
c.collect(expr);
}
}
impl<'c> Collectible<'c> for Group {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { expr } = self;
c.collect(expr);
}
}
impl<'c> Collectible<'c> for While {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { cond, pass, fail } = self;
c.collect(cond).collect(pass).collect(fail);
}
}
impl<'c> Collectible<'c> for Else {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { body } = self;
c.collect(body);
}
}
impl<'c> Collectible<'c> for If {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { cond, pass, fail } = self;
c.collect(cond).collect(pass).collect(fail);
}
}
impl<'c> Collectible<'c> for For {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { bind: Identifier(name), cond, pass, fail } = self;
c.collect(cond).define(name).collect(pass).collect(fail);
}
}
impl<'c> Collectible<'c> for Break {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { body } = self;
c.collect(body);
}
}
impl<'c> Collectible<'c> for Return {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { body } = self;
c.collect(body);
}
}
impl<'c> Collectible<'c> for Continue {
fn collect(&'c self, _c: &mut Collector<'c>) {}
}
impl<'c> Collectible<'c> for Literal {
fn collect(&'c self, _c: &mut Collector<'c>) {}
}
impl<'c> Collectible<'c> for Identifier {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self(name) = self;
c.reference(name);
}
}
impl<'c> Collectible<'c> for Param {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { mutability: _, name, ty } = self;
c.collect(name).collect(ty);
}
}
impl<'c> Collectible<'c> for Ty {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { extents, kind } = self;
c.location(extents.head).collect(kind);
}
}
impl<'c> Collectible<'c> for TyKind {
fn collect(&'c self, c: &mut Collector<'c>) {
match self {
TyKind::Never => {}
TyKind::Empty => {}
TyKind::SelfTy => {}
TyKind::Path(t) => t.collect(c),
TyKind::Tuple(t) => t.collect(c),
TyKind::Ref(t) => t.collect(c),
TyKind::Fn(t) => t.collect(c),
}
}
}
impl<'c> Collectible<'c> for Path {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { absolute: _, parts } = self;
for part in parts {
c.collect(part);
}
}
}
impl<'c> Collectible<'c> for PathPart {
fn collect(&'c self, c: &mut Collector<'c>) {
match self {
PathPart::SuperKw => {}
PathPart::SelfKw => {}
PathPart::Ident(i) => i.collect(c),
}
}
}
impl<'c> Collectible<'c> for TyTuple {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { types } = self;
for ty in types {
c.collect(ty);
}
}
}
impl<'c> Collectible<'c> for TyRef {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { count: _, to } = self;
c.collect(to);
}
}
impl<'c> Collectible<'c> for TyFn {
fn collect(&'c self, c: &mut Collector<'c>) {
let Self { args, rety } = self;
c.collect(args).collect(rety);
}
}
impl<'c, C: Collectible<'c> + Sized> Collectible<'c> for Vec<C> {
fn collect(&'c self, c: &mut Collector<'c>) {
for i in self {
c.collect(i);
}
}
}
impl<'c, C: Collectible<'c> + Sized> Collectible<'c> for Option<C> {
fn collect(&'c self, c: &mut Collector<'c>) {
if let Some(i) = self {
c.collect(i);
}
}
}
impl<'c, C: Collectible<'c>> Collectible<'c> for Box<C> {
fn collect(&'c self, c: &mut Collector<'c>) {
c.collect(self.as_ref());
}
}
}

109
cl-repl/examples/typeck.rs Normal file
View File

@@ -0,0 +1,109 @@
use cl_lexer::Lexer;
use cl_parser::Parser;
use cl_repl::repline::{error::Error as RlError, Repline};
use cl_typeck::{name_collector::NameCollector, project::Project};
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let mut prj = Project::default();
let mut tcol = NameCollector::new(&mut prj);
println!(
"--- {} v{} 💪🦈 ---",
env!("CARGO_BIN_NAME"),
env!("CARGO_PKG_VERSION"),
);
read_and(
"\x1b[33m",
"cl>",
"? >",
|line| -> Result<_, Box<dyn Error>> {
if line.trim_start().is_empty() {
query(&tcol)?;
return Ok(Response::Deny);
}
let mut parser = Parser::new(Lexer::new(line));
let code = match parser.file() {
Ok(code) => code,
Err(e) => Err(e)?,
};
tcol.file(&code)?;
Ok(Response::Accept)
},
)
}
pub enum Response {
Accept,
Deny,
Break,
}
fn read_and(
color: &str,
begin: &str,
again: &str,
mut f: impl FnMut(&str) -> Result<Response, Box<dyn Error>>,
) -> Result<(), Box<dyn Error>> {
let mut rl = Repline::new(color, begin, again);
loop {
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,
Err(e) => print!("\x1b[40G\x1bJ\x1b[91m{e}\x1b[0m"),
}
}
Ok(())
}
fn query(prj: &Project) -> Result<(), Box<dyn Error>> {
use cl_typeck::{
definition::{Def, DefKind},
type_kind::TypeKind,
};
read_and("\x1b[35m", "qy>", "? >", |line| {
if line.trim_start().is_empty() {
return Ok(Response::Break);
}
match line {
"$all\n" => println!("{prj:#?}"),
_ => {
// parse it as a path, and convert the path into a borrowed path
let path = Parser::new(Lexer::new(line)).path()?;
let Some((type_id, path)) = prj.get_type((&path).into(), prj.module_root) else {
return Ok(Response::Deny);
};
let Def { name, vis, meta: _, kind, source: _, module } = &prj[type_id];
match (kind, prj.get_value(path, type_id)) {
(_, Some((val, path))) => {
println!("value {}; {path}\n{:#?}", usize::from(val), prj[val])
}
(DefKind::Type(TypeKind::Module), None) => println!(
"{vis}mod \"{name}\" (#{}); {path}\n{:#?}",
usize::from(type_id),
module
),
(_, None) => println!(
"type {name}(#{}); {path}\n{:#?}",
usize::from(type_id),
prj.pool[type_id]
),
};
}
}
Ok(Response::Accept)
})
}

639
cl-repl/examples/yaml.rs Normal file
View File

@@ -0,0 +1,639 @@
//! Pretty prints a conlang AST in yaml
use cl_lexer::Lexer;
use cl_parser::Parser;
use cl_repl::repline::{error::Error as RlError, Repline};
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let mut rl = Repline::new("\x1b[33m", "cl>", "? >");
loop {
let line = match rl.read() {
Err(RlError::CtrlC(_)) => break,
Err(RlError::CtrlD(line)) => {
rl.deny();
line
}
Ok(line) => line,
Err(e) => Err(e)?,
};
let mut parser = Parser::new(Lexer::new(&line));
let code = match parser.stmt() {
Ok(code) => {
rl.accept();
code
}
Err(e) => {
print!("\x1b[40G\x1bJ\x1b[91m{e}\x1b[0m");
continue;
}
};
print!("\x1b[G\x1b[J\x1b[A");
Yamler::new().yaml(&code);
println!();
}
Ok(())
}
pub use yamler::Yamler;
pub mod yamler {
use crate::yamlify::Yamlify;
use std::{
fmt::Display,
io::Write,
ops::{Deref, DerefMut},
};
#[derive(Debug, Default)]
pub struct Yamler {
depth: usize,
}
impl Yamler {
pub fn new() -> Self {
Self::default()
}
pub fn indent(&mut self) -> Section {
Section::new(self)
}
/// Prints a [Yamlify] value
#[inline]
pub fn yaml<T: Yamlify>(&mut self, yaml: &T) -> &mut Self {
yaml.yaml(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, " ");
}
}
/// Prints a section header and increases indentation
pub fn key(&mut self, name: impl Display) -> Section {
println!();
self.print_indentation(&mut std::io::stdout().lock());
print!("- {name}:");
self.indent()
}
/// Prints a yaml key value pair: `- name: "value"`
pub fn pair<D: Display, T: Yamlify>(&mut self, name: D, value: T) -> &mut Self {
self.key(name).yaml(&value);
self
}
/// Prints a yaml scalar value: `"name"``
pub fn value<D: Display>(&mut self, value: D) -> &mut Self {
print!(" {value}");
self
}
pub fn list<D: Yamlify>(&mut self, list: &[D]) -> &mut Self {
for (idx, value) in list.iter().enumerate() {
self.pair(idx, value);
}
self
}
}
/// Tracks the start and end of an indented block (a "section")
pub struct Section<'y> {
yamler: &'y mut Yamler,
}
impl<'y> Section<'y> {
pub fn new(yamler: &'y mut Yamler) -> Self {
yamler.increase();
Self { yamler }
}
}
impl<'y> Deref for Section<'y> {
type Target = Yamler;
fn deref(&self) -> &Self::Target {
self.yamler
}
}
impl<'y> DerefMut for Section<'y> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.yamler
}
}
impl<'y> Drop for Section<'y> {
fn drop(&mut self) {
let Self { yamler } = self;
yamler.decrease();
}
}
}
pub mod yamlify {
use super::yamler::Yamler;
use cl_ast::*;
pub trait Yamlify {
fn yaml(&self, y: &mut Yamler);
}
impl Yamlify for File {
fn yaml(&self, y: &mut Yamler) {
let File { items } = self;
y.key("File").yaml(items);
}
}
impl Yamlify for Visibility {
fn yaml(&self, y: &mut Yamler) {
if let Visibility::Public = self {
y.pair("vis", "pub");
}
}
}
impl Yamlify for Mutability {
fn yaml(&self, y: &mut Yamler) {
if let Mutability::Mut = self {
y.pair("mut", true);
}
}
}
impl Yamlify for Attrs {
fn yaml(&self, y: &mut Yamler) {
let Self { meta } = self;
y.key("Attrs").yaml(meta);
}
}
impl Yamlify for Meta {
fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self;
y.key("Meta").pair("name", name).yaml(kind);
}
}
impl Yamlify for MetaKind {
fn yaml(&self, y: &mut Yamler) {
match self {
MetaKind::Plain => y,
MetaKind::Equals(value) => y.pair("equals", value),
MetaKind::Func(args) => y.pair("args", args),
};
}
}
impl Yamlify for Item {
fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, attrs, vis, kind } = self;
y.key("Item").yaml(attrs).yaml(vis).yaml(kind);
}
}
impl Yamlify for ItemKind {
fn yaml(&self, y: &mut Yamler) {
match self {
ItemKind::Alias(f) => y.yaml(f),
ItemKind::Const(f) => y.yaml(f),
ItemKind::Static(f) => y.yaml(f),
ItemKind::Module(f) => y.yaml(f),
ItemKind::Function(f) => y.yaml(f),
ItemKind::Struct(f) => y.yaml(f),
ItemKind::Enum(f) => y.yaml(f),
ItemKind::Impl(f) => y.yaml(f),
};
}
}
impl Yamlify for Alias {
fn yaml(&self, y: &mut Yamler) {
let Self { to, from } = self;
y.key("Alias").pair("to", to).pair("from", from);
}
}
impl Yamlify for Const {
fn yaml(&self, y: &mut Yamler) {
let Self { name, ty, init } = self;
y.key("Const")
.pair("name", name)
.pair("ty", ty)
.pair("init", init);
}
}
impl Yamlify for Static {
fn yaml(&self, y: &mut Yamler) {
let Self { mutable, name, ty, init } = self;
y.key(name).yaml(mutable).pair("ty", ty).pair("init", init);
}
}
impl Yamlify for Module {
fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self;
y.key("Module").pair("name", name).yaml(kind);
}
}
impl Yamlify for ModuleKind {
fn yaml(&self, y: &mut Yamler) {
match self {
ModuleKind::Inline(f) => y.yaml(f),
ModuleKind::Outline => y,
};
}
}
impl Yamlify for Function {
fn yaml(&self, y: &mut Yamler) {
let Self { name, args, body, rety } = self;
y.key("Function")
.pair("name", name)
.pair("args", args)
.pair("body", body)
.pair("rety", rety);
}
}
impl Yamlify for Struct {
fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self;
y.key("Struct").pair("name", name).yaml(kind);
}
}
impl Yamlify for StructKind {
fn yaml(&self, y: &mut Yamler) {
match self {
StructKind::Empty => y,
StructKind::Tuple(k) => y.yaml(k),
StructKind::Struct(k) => y.yaml(k),
};
}
}
impl Yamlify for StructMember {
fn yaml(&self, y: &mut Yamler) {
let Self { vis, name, ty } = self;
y.key("StructMember").yaml(vis).pair("name", name).yaml(ty);
}
}
impl Yamlify for Enum {
fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self;
y.key("Enum").pair("name", name).yaml(kind);
}
}
impl Yamlify for EnumKind {
fn yaml(&self, y: &mut Yamler) {
match self {
EnumKind::NoVariants => y,
EnumKind::Variants(v) => y.yaml(v),
};
}
}
impl Yamlify for Variant {
fn yaml(&self, y: &mut Yamler) {
let Self { name, kind } = self;
y.key("Variant").pair("name", name).yaml(kind);
}
}
impl Yamlify for VariantKind {
fn yaml(&self, y: &mut Yamler) {
match self {
VariantKind::Plain => y,
VariantKind::CLike(v) => y.yaml(v),
VariantKind::Tuple(v) => y.yaml(v),
VariantKind::Struct(v) => y.yaml(v),
};
}
}
impl Yamlify for Impl {
fn yaml(&self, y: &mut Yamler) {
let Self { target, body } = self;
y.key("Impl").pair("target", target).pair("body", body);
}
}
impl Yamlify for Block {
fn yaml(&self, y: &mut Yamler) {
let Self { stmts } = self;
y.key("Block").yaml(stmts);
}
}
impl Yamlify for Stmt {
fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind, semi } = self;
y.key("Stmt").yaml(kind).yaml(semi);
}
}
impl Yamlify for Semi {
fn yaml(&self, y: &mut Yamler) {
if let Semi::Terminated = self {
y.pair("terminated", true);
}
}
}
impl Yamlify for StmtKind {
fn yaml(&self, y: &mut Yamler) {
match self {
StmtKind::Empty => y,
StmtKind::Local(s) => y.yaml(s),
StmtKind::Item(s) => y.yaml(s),
StmtKind::Expr(s) => y.yaml(s),
};
}
}
impl Yamlify for Let {
fn yaml(&self, y: &mut Yamler) {
let Self { mutable, name, ty, init } = self;
y.key("Let")
.pair("name", name)
.yaml(mutable)
.pair("ty", ty)
.pair("init", init);
}
}
impl Yamlify for Expr {
fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind } = self;
y.yaml(kind);
}
}
impl Yamlify for ExprKind {
fn yaml(&self, y: &mut Yamler) {
match self {
ExprKind::Assign(k) => k.yaml(y),
ExprKind::Binary(k) => k.yaml(y),
ExprKind::Unary(k) => k.yaml(y),
ExprKind::Index(k) => k.yaml(y),
ExprKind::Path(k) => k.yaml(y),
ExprKind::Literal(k) => k.yaml(y),
ExprKind::Array(k) => k.yaml(y),
ExprKind::ArrayRep(k) => k.yaml(y),
ExprKind::AddrOf(k) => k.yaml(y),
ExprKind::Block(k) => k.yaml(y),
ExprKind::Empty => {}
ExprKind::Group(k) => k.yaml(y),
ExprKind::Tuple(k) => k.yaml(y),
ExprKind::While(k) => k.yaml(y),
ExprKind::If(k) => k.yaml(y),
ExprKind::For(k) => k.yaml(y),
ExprKind::Break(k) => k.yaml(y),
ExprKind::Return(k) => k.yaml(y),
ExprKind::Continue(k) => k.yaml(y),
}
}
}
impl Yamlify for Assign {
fn yaml(&self, y: &mut Yamler) {
let Self { kind, parts } = self;
y.key("Assign")
.pair("kind", kind)
.pair("head", &parts.0)
.pair("tail", &parts.1);
}
}
impl Yamlify for AssignKind {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
}
impl Yamlify for Binary {
fn yaml(&self, y: &mut Yamler) {
let Self { kind, parts } = self;
y.key("Binary")
.pair("kind", kind)
.pair("head", &parts.0)
.pair("tail", &parts.1);
}
}
impl Yamlify for BinaryKind {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
}
impl Yamlify for Unary {
fn yaml(&self, y: &mut Yamler) {
let Self { kind, tail } = self;
y.key("Unary").pair("kind", kind).pair("tail", tail);
}
}
impl Yamlify for UnaryKind {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
}
impl Yamlify for Tuple {
fn yaml(&self, y: &mut Yamler) {
let Self { exprs } = self;
y.key("Tuple").list(exprs);
}
}
impl Yamlify for Index {
fn yaml(&self, y: &mut Yamler) {
let Self { head, indices } = self;
y.key("Index").pair("head", head).list(indices);
}
}
impl Yamlify for Array {
fn yaml(&self, y: &mut Yamler) {
let Self { values } = self;
y.key("Array").list(values);
}
}
impl Yamlify for ArrayRep {
fn yaml(&self, y: &mut Yamler) {
let Self { value, repeat } = self;
y.key("ArrayRep")
.pair("value", value)
.pair("repeat", repeat);
}
}
impl Yamlify for AddrOf {
fn yaml(&self, y: &mut Yamler) {
let Self { count, mutable, expr } = self;
y.key("AddrOf")
.yaml(mutable)
.pair("count", count)
.pair("expr", expr);
}
}
impl Yamlify for Group {
fn yaml(&self, y: &mut Yamler) {
let Self { expr } = self;
y.key("Group").yaml(expr);
}
}
impl Yamlify for While {
fn yaml(&self, y: &mut Yamler) {
let Self { cond, pass, fail } = self;
y.key("While")
.pair("cond", cond)
.pair("pass", pass)
.pair("fail", fail);
}
}
impl Yamlify for Else {
fn yaml(&self, y: &mut Yamler) {
let Self { body } = self;
y.key("Else").yaml(body);
}
}
impl Yamlify for If {
fn yaml(&self, y: &mut Yamler) {
let Self { cond, pass, fail } = self;
y.key("If").pair("cond", cond).pair("pass", pass).yaml(fail);
}
}
impl Yamlify for For {
fn yaml(&self, y: &mut Yamler) {
let Self { bind, cond, pass, fail } = self;
y.key("For")
.pair("bind", bind)
.pair("cond", cond)
.pair("pass", pass)
.yaml(fail);
}
}
impl Yamlify for Break {
fn yaml(&self, y: &mut Yamler) {
let Self { body } = self;
y.key("Break").yaml(body);
}
}
impl Yamlify for Return {
fn yaml(&self, y: &mut Yamler) {
let Self { body } = self;
y.key("Return").yaml(body);
}
}
impl Yamlify for Continue {
fn yaml(&self, y: &mut Yamler) {
y.key("Continue");
}
}
impl Yamlify for Literal {
fn yaml(&self, y: &mut Yamler) {
y.value(format_args!("\"{self}\""));
}
}
impl Yamlify for Identifier {
fn yaml(&self, y: &mut Yamler) {
let Self(name) = self;
y.value(name);
}
}
impl Yamlify for Param {
fn yaml(&self, y: &mut Yamler) {
let Self { mutability, name, ty } = self;
y.key("Param")
.yaml(mutability)
.pair("name", name)
.pair("ty", ty);
}
}
impl Yamlify for Ty {
fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind } = self;
y.key("Ty").yaml(kind);
}
}
impl Yamlify for TyKind {
fn yaml(&self, y: &mut Yamler) {
match self {
TyKind::Never => y.value("Never"),
TyKind::Empty => y.value("Empty"),
TyKind::SelfTy => y.value("Self"),
TyKind::Path(t) => y.yaml(t),
TyKind::Tuple(t) => y.yaml(t),
TyKind::Ref(t) => y.yaml(t),
TyKind::Fn(t) => y.yaml(t),
};
}
}
impl Yamlify for Path {
fn yaml(&self, y: &mut Yamler) {
let Self { absolute, parts } = self;
let mut y = y.key("Path");
if *absolute {
y.pair("absolute", absolute);
}
for part in parts {
y.pair("part", part);
}
}
}
impl Yamlify for PathPart {
fn yaml(&self, y: &mut Yamler) {
match self {
PathPart::SuperKw => y.value("super"),
PathPart::SelfKw => y.value("self"),
PathPart::Ident(i) => y.yaml(i),
};
}
}
impl Yamlify for TyTuple {
fn yaml(&self, y: &mut Yamler) {
let Self { types } = self;
let mut y = y.key("TyTuple");
for ty in types {
y.yaml(ty);
}
}
}
impl Yamlify for TyRef {
fn yaml(&self, y: &mut Yamler) {
let Self { count, to } = self;
y.key("TyRef").pair("count", count).pair("to", to);
}
}
impl Yamlify for TyFn {
fn yaml(&self, y: &mut Yamler) {
let Self { args, rety } = self;
y.key("TyFn").pair("args", args).pair("rety", rety);
}
}
impl<T: Yamlify> Yamlify for Option<T> {
fn yaml(&self, y: &mut Yamler) {
if let Some(v) = self {
y.yaml(v);
} else {
y.value("");
}
}
}
impl<T: Yamlify> Yamlify for Box<T> {
fn yaml(&self, y: &mut Yamler) {
y.yaml(&**self);
}
}
impl<T: Yamlify> Yamlify for Vec<T> {
fn yaml(&self, y: &mut Yamler) {
for thing in self {
y.yaml(thing);
}
}
}
impl Yamlify for () {
fn yaml(&self, _y: &mut Yamler) {}
}
impl<T: Yamlify> Yamlify for &T {
fn yaml(&self, y: &mut Yamler) {
(*self).yaml(y)
}
}
macro_rules! scalar {
($($t:ty),*$(,)?) => {
$(impl Yamlify for $t {
fn yaml(&self, y: &mut Yamler) {
y.value(self);
}
})*
};
}
scalar! {
bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, &str, String
}
}

View File

@@ -1,6 +1,13 @@
use cl_repl::cli::run;
use cl_repl::{cli::run, tools::is_terminal};
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
if is_terminal() {
println!(
"--- {} v{} 💪🦈 ---",
env!("CARGO_BIN_NAME"),
env!("CARGO_PKG_VERSION"),
);
}
run(argh::from_env())
}

View File

@@ -8,9 +8,9 @@
pub mod ansi {
// ANSI color escape sequences
pub const ANSI_RED: &str = "\x1b[31m";
// const ANSI_GREEN: &str = "\x1b[32m"; // the color of type checker mode
pub const ANSI_GREEN: &str = "\x1b[32m"; // the color of type checker mode
pub const ANSI_CYAN: &str = "\x1b[36m";
pub const ANSI_BRIGHT_GREEN: &str = "\x1b[92m";
// pub const ANSI_BRIGHT_GREEN: &str = "\x1b[92m";
pub const ANSI_BRIGHT_BLUE: &str = "\x1b[94m";
pub const ANSI_BRIGHT_MAGENTA: &str = "\x1b[95m";
// const ANSI_BRIGHT_CYAN: &str = "\x1b[96m";
@@ -21,6 +21,7 @@ pub mod ansi {
}
pub mod args {
use crate::tools::is_terminal;
use argh::FromArgs;
use std::{path::PathBuf, str::FromStr};
@@ -39,9 +40,9 @@ pub mod args {
#[argh(option, short = 'm', default = "Default::default()")]
pub mode: Mode,
/// whether to start the repl
#[argh(switch, short = 'r')]
pub no_repl: bool,
/// whether to start the repl (`true` or `false`)
#[argh(option, short = 'r', default = "is_terminal()")]
pub repl: bool,
}
/// The CLI's operating mode
@@ -187,14 +188,19 @@ pub mod cli {
/// Run the command line interface
pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
let Args { file, include, mode, no_repl } = args;
let Args { file, include, mode, repl } = args;
let mut env = Environment::new();
for path in include {
load_file(&mut env, path)?;
}
if no_repl {
if repl {
if let Some(file) = file {
load_file(&mut env, file)?;
}
Repl::with_env(mode, env).repl()
} else {
let code = match &file {
Some(file) => std::fs::read_to_string(file)?,
None => std::io::read_to_string(std::io::stdin())?,
@@ -206,11 +212,6 @@ pub mod cli {
Mode::Beautify => beautify(code),
Mode::Interpret => interpret(code, &mut env),
}?;
} else {
if let Some(file) = file {
load_file(&mut env, file)?;
}
Repl::with_env(mode, env).repl()
}
Ok(())
}
@@ -251,7 +252,10 @@ pub mod cli {
ret => println!("{ret}"),
}
if env.get("main").is_ok() {
println!("-> {}", env.call("main", &[])?);
match env.call("main", &[])? {
ConValue::Empty => {}
ret => println!("{ret}"),
}
}
Ok(())
}
@@ -304,8 +308,8 @@ pub mod repl {
println!("{ANSI_CLEAR_LINES}{ANSI_RED}{prompt} {err}{ANSI_RESET}")
}
pub fn prompt_succs(&self, value: &impl Display) {
let Self { prompt_succs: prompt, .. } = self;
println!("{ANSI_BRIGHT_GREEN}{prompt}{ANSI_RESET} {value}")
let Self { prompt_succs: _prompt, .. } = self;
println!("{ANSI_GREEN}{value}{ANSI_RESET}")
}
/// Resets the cursor to the start of the line, clears the terminal,
/// and sets the output color
@@ -327,6 +331,7 @@ pub mod repl {
/// Runs the main REPL loop
pub fn repl(&mut self) {
use crate::repline::{error::Error, Repline};
let mut rl = Repline::new(self.mode.ansi_color(), self.prompt_begin, self.prompt_again);
fn clear_line() {
print!("\x1b[G\x1b[J");
@@ -437,6 +442,8 @@ pub mod repl {
pub mod tools {
use cl_token::Token;
use std::io::IsTerminal;
/// Prints a token in the particular way cl-repl does
pub fn print_token(t: &Token) {
println!(
"{:02}:{:02}: {:#19}{}",
@@ -446,6 +453,10 @@ pub mod tools {
t.data(),
)
}
/// gets whether stdin AND stdout are a terminal, for pipelining
pub fn is_terminal() -> bool {
std::io::stdin().is_terminal() && std::io::stdout().is_terminal()
}
}
pub mod repline;

View File

@@ -0,0 +1,131 @@
//! Trivially-copyable, easily comparable typed indices, and a [Pool] to contain them
//!
//! # Examples
//!
//! ```rust
//! # use cl_structures::intern_pool::*;
//! // first, create a new InternKey type (this ensures type safety)
//! make_intern_key!{
//! NumbersKey
//! }
//!
//! // then, create a pool with that type
//! let mut numbers: Pool<i32, NumbersKey> = Pool::new();
//! let first = numbers.insert(1);
//! let second = numbers.insert(2);
//! let third = numbers.insert(3);
//!
//! // You can access elements immutably with `get`
//! assert_eq!(Some(&3), numbers.get(third));
//! assert_eq!(Some(&2), numbers.get(second));
//! // or by indexing
//! assert_eq!(1, numbers[first]);
//!
//! // Or mutably
//! *numbers.get_mut(first).unwrap() = 100000;
//!
//! assert_eq!(Some(&100000), numbers.get(first));
//! ```
/// Creates newtype indices over [`usize`] for use as [Pool] keys.
#[macro_export]
macro_rules! make_intern_key {($($(#[$meta:meta])* $name:ident),*$(,)?) => {$(
$(#[$meta])*
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name(usize);
impl $crate::intern_pool::InternKey for $name {
#[doc = concat!("Constructs a [`", stringify!($name), "`] from a [`usize`] without checking bounds.\n")]
/// # Safety
///
/// The provided value should be within the bounds of its associated container
unsafe fn from_raw_unchecked(value: usize) -> Self {
Self(value)
}
fn get(&self) -> usize {
self.0
}
}
impl From< $name > for usize {
fn from(value: $name) -> Self {
value.0
}
}
)*}}
use std::ops::{Index, IndexMut};
pub use make_intern_key;
/// An index into a [Pool]. For full type-safety,
/// there should be a unique [InternKey] for each [Pool]
pub trait InternKey: std::fmt::Debug {
/// Constructs an [`InternKey`] from a [`usize`] without checking bounds.
///
/// # Safety
///
/// The provided value should be within the bounds of its associated container.
// ID::from_raw_unchecked here isn't *actually* unsafe, since bounds should always be
// checked, however, the function has unverifiable preconditions.
unsafe fn from_raw_unchecked(value: usize) -> Self;
/// Gets the index of the [`InternKey`] by value
fn get(&self) -> usize;
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Pool<T, ID: InternKey> {
pool: Vec<T>,
id_type: std::marker::PhantomData<ID>,
}
impl<T, ID: InternKey> Pool<T, ID> {
pub fn new() -> Self {
Self::default()
}
pub fn get(&self, index: ID) -> Option<&T> {
self.pool.get(index.get())
}
pub fn get_mut(&mut self, index: ID) -> Option<&mut T> {
self.pool.get_mut(index.get())
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.pool.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.pool.iter_mut()
}
pub fn insert(&mut self, value: T) -> ID {
let id = self.pool.len();
self.pool.push(value);
// Safety: value was pushed to `self.pool[id]`
unsafe { ID::from_raw_unchecked(id) }
}
}
impl<T, ID: InternKey> Default for Pool<T, ID> {
fn default() -> Self {
Self { pool: vec![], id_type: std::marker::PhantomData }
}
}
impl<T, ID: InternKey> Index<ID> for Pool<T, ID> {
type Output = T;
fn index(&self, index: ID) -> &Self::Output {
match self.pool.get(index.get()) {
None => panic!("Index {:?} out of bounds in pool!", index),
Some(value) => value,
}
}
}
impl<T, ID: InternKey> IndexMut<ID> for Pool<T, ID> {
fn index_mut(&mut self, index: ID) -> &mut Self::Output {
match self.pool.get_mut(index.get()) {
None => panic!("Index {:?} out of bounds in pool!", index),
Some(value) => value,
}
}
}

View File

@@ -2,44 +2,13 @@
//! - [Span](struct@span::Span): Stores a start and end [Loc](struct@span::Loc)
//! - [Loc](struct@span::Loc): Stores the index in a stream
#![warn(clippy::all)]
#![feature(inline_const, dropck_eyepatch, decl_macro)]
#![deny(unsafe_op_in_unsafe_fn)]
pub mod span {
//! - [struct@Span]: Stores the start and end [struct@Loc] of a notable AST node
//! - [struct@Loc]: Stores the line/column of a notable AST node
#![allow(non_snake_case)]
pub mod span;
/// Stores the start and end [locations](struct@Loc) within the token stream
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Span {
pub head: Loc,
pub tail: Loc,
}
pub fn Span(head: Loc, tail: Loc) -> Span {
Span { head, tail }
}
pub mod tree;
/// Stores a read-only (line, column) location in a token stream
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Loc {
line: u32,
col: u32,
}
pub fn Loc(line: u32, col: u32) -> Loc {
Loc { line, col }
}
impl Loc {
pub fn line(self) -> u32 {
self.line
}
pub fn col(self) -> u32 {
self.col
}
}
pub mod stack;
impl std::fmt::Display for Loc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Loc { line, col } = self;
write!(f, "{line}:{col}:")
}
}
}
pub mod intern_pool;

38
cl-structures/src/span.rs Normal file
View File

@@ -0,0 +1,38 @@
//! - [struct@Span]: Stores the start and end [struct@Loc] of a notable AST node
//! - [struct@Loc]: Stores the line/column of a notable AST node
#![allow(non_snake_case)]
/// Stores the start and end [locations](struct@Loc) within the token stream
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Span {
pub head: Loc,
pub tail: Loc,
}
pub fn Span(head: Loc, tail: Loc) -> Span {
Span { head, tail }
}
/// Stores a read-only (line, column) location in a token stream
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Loc {
line: u32,
col: u32,
}
pub fn Loc(line: u32, col: u32) -> Loc {
Loc { line, col }
}
impl Loc {
pub fn line(self) -> u32 {
self.line
}
pub fn col(self) -> u32 {
self.col
}
}
impl std::fmt::Display for Loc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Loc { line, col } = self;
write!(f, "{line}:{col}:")
}
}

747
cl-structures/src/stack.rs Normal file
View File

@@ -0,0 +1,747 @@
//! A contiguous collection with constant capacity.
//!
//! Since the capacity of a [Stack] may be [*known at compile time*](Sized),
//! it may live on the call stack.
//!
//!
//! # Examples
//!
//! Unlike a [Vec], the [Stack] doesn't grow when it reaches capacity.
//! ```should_panic
//! # use cl_structures::stack::*;
//! let mut v = stack![1];
//! v.push("This should work");
//! v.push("This will panic!");
//! ```
//! To get around this limitation, the methods [try_push](Stack::try_push) and
//! [try_insert](Stack::try_insert) are provided:
//! ```
//! # use cl_structures::stack::*;
//! let mut v = stack![1];
//! v.push("This should work");
//! v.try_push("This should produce an err").unwrap_err();
//! ```
//!
//! As the name suggests, a [Stack] enforces a stack discipline:
//! ```
//! # use cl_structures::stack::*;
//! let mut v = stack![100];
//!
//! assert_eq!(100, v.capacity());
//! assert_eq!(0, v.len());
//!
//! // Elements are pushed one at a time onto the stack
//! v.push("foo");
//! v.push("bar");
//! assert_eq!(2, v.len());
//!
//! // The stack can be used anywhere a slice is expected
//! assert_eq!(Some(&"foo"), v.get(0));
//! assert_eq!(Some(&"bar"), v.last());
//!
//! // Elements are popped from the stack in reverse order
//! assert_eq!(Some("bar"), v.pop());
//! assert_eq!(Some("foo"), v.pop());
//! assert_eq!(None, v.pop());
//! ```
// yar har! here there be unsafe code! Tread carefully.
use core::slice;
use std::{
fmt::Debug,
marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut},
ptr,
};
/// Creates a [`stack`] containing the arguments
///
/// # Examples
///
/// Creates a *full* [`Stack`] containing a list of elements
/// ```
/// # use cl_structures::stack::stack;
/// let mut v = stack![1, 2, 3];
///
/// assert_eq!(Some(3), v.pop());
/// assert_eq!(Some(2), v.pop());
/// assert_eq!(Some(1), v.pop());
/// assert_eq!(None, v.pop());
/// ```
///
/// Creates a *full* [`Stack`] from a given element and size
/// ```
/// # use cl_structures::stack::stack;
/// let mut v = stack![1; 2];
///
/// assert_eq!(Some(1), v.pop());
/// assert_eq!(Some(1), v.pop());
/// assert_eq!(None, v.pop());
/// ```
///
/// Creates an *empty* [`Stack`] from a given size
/// ```
/// # use cl_structures::stack::{Stack, stack};
/// let mut v = stack![10];
///
/// assert_eq!(0, v.len());
/// assert_eq!(10, v.capacity());
///
/// v.push(10);
/// assert_eq!(Some(&10), v.last());
/// ```
pub macro stack {
($count:literal) => {
Stack::<_, $count>::new()
},
($value:expr ; $count:literal) => {{
let mut stack: Stack<_, $count> = Stack::new();
for _ in 0..$count {
stack.push($value)
}
stack
}},
($($values:expr),* $(,)?) => {
Stack::from([$($values),*])
}
}
/// A contiguous collection with constant capacity
pub struct Stack<T, const N: usize> {
_data: PhantomData<T>,
buf: [MaybeUninit<T>; N],
len: usize,
}
impl<T: Clone, const N: usize> Clone for Stack<T, N> {
fn clone(&self) -> Self {
let mut new = Self::new();
for value in self.iter() {
new.push(value.clone())
}
new
}
}
impl<T: Debug, const N: usize> Debug for Stack<T, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<T, const N: usize> Default for Stack<T, N> {
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> Deref for Stack<T, N> {
type Target = [T];
#[inline]
fn deref(&self) -> &Self::Target {
// Safety:
// - We have ensured all elements from 0 to len have been initialized
// - self.elem[0] came from a reference, and so is aligned to T
// unsafe { &*(&self.buf[0..self.len] as *const [_] as *const [T]) }
unsafe { slice::from_raw_parts(self.buf.as_ptr().cast(), self.len) }
}
}
impl<T, const N: usize> DerefMut for Stack<T, N> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
// Safety:
// - See Deref
unsafe { slice::from_raw_parts_mut(self.buf.as_mut_ptr().cast(), self.len) }
}
}
// requires dropck-eyepatch for elements with contravariant lifetimes
unsafe impl<#[may_dangle] T, const N: usize> Drop for Stack<T, N> {
#[inline]
fn drop(&mut self) {
// Safety: We have ensured that all elements in the list are
unsafe { core::ptr::drop_in_place(self.as_mut_slice()) };
}
}
impl<T, const N: usize> Extend<T> for Stack<T, N> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for value in iter {
self.push(value)
}
}
}
impl<T, const N: usize> From<[T; N]> for Stack<T, N> {
fn from(value: [T; N]) -> Self {
let value = ManuallyDrop::new(value);
if std::mem::size_of::<[T; N]>() == 0 {
// Safety: since [T; N] is zero-sized, and there are no other fields,
// it should be okay to interpret N as Self
unsafe { ptr::read(&N as *const _ as *const _) }
} else {
// Safety:
// - `value` is ManuallyDrop, so its destructor won't run
// - All elements are assumed to be initialized (so len is N)
Self {
buf: unsafe { ptr::read(&value as *const _ as *const _) },
len: N,
..Default::default()
}
}
}
}
impl<T, const N: usize> Stack<T, N> {
/// Constructs a new, empty [Stack]
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::Stack;
/// let mut v: Stack<_, 3> = Stack::new();
///
/// v.try_push(1).unwrap();
/// v.try_push(2).unwrap();
/// v.try_push(3).unwrap();
/// // Trying to push a 4th element will fail, and return the failed element
/// assert_eq!(4, v.try_push(4).unwrap_err());
///
/// assert_eq!(Some(3), v.pop());
/// ```
pub const fn new() -> Self {
Self { buf: [const { MaybeUninit::uninit() }; N], len: 0, _data: PhantomData }
}
/// Constructs a new [Stack] from an array of [`MaybeUninit<T>`] and an initialized length
///
/// # Safety
///
/// - Elements from `0..len` must be initialized
/// - len must not exceed the length of the array
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::Stack;
/// # use core::mem::MaybeUninit;
/// let mut v = unsafe { Stack::from_raw_parts([MaybeUninit::new(100)], 1) };
///
/// assert_eq!(1, v.len());
/// assert_eq!(1, v.capacity());
/// assert_eq!(Some(100), v.pop());
/// assert_eq!(None, v.pop());
/// ```
pub const unsafe fn from_raw_parts(buf: [MaybeUninit<T>; N], len: usize) -> Self {
Self { buf, len, _data: PhantomData }
}
/// Converts a [Stack] into an array of [`MaybeUninit<T>`] and the initialized length
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::Stack;
/// let mut v: Stack<_, 10> = Stack::new();
/// v.push(0);
/// v.push(1);
///
/// let (buf, len) = v.into_raw_parts();
///
/// assert_eq!(0, unsafe { buf[0].assume_init() });
/// assert_eq!(1, unsafe { buf[1].assume_init() });
/// assert_eq!(2, len);
/// ```
#[inline]
pub fn into_raw_parts(self) -> ([MaybeUninit<T>; N], usize) {
let this = ManuallyDrop::new(self);
// Safety: since
(unsafe { ptr::read(&this.buf) }, this.len)
}
/// Returns a raw pointer to the stack's buffer
pub const fn as_ptr(&self) -> *const T {
self.buf.as_ptr().cast()
}
/// Returns an unsafe mutable pointer to the stack's buffer
pub fn as_mut_ptr(&mut self) -> *mut T {
self.buf.as_mut_ptr().cast()
}
/// Extracts a slice containing the entire vector
pub fn as_slice(&self) -> &[T] {
self
}
/// Extracts a mutable slice containing the entire vector
pub fn as_mut_slice(&mut self) -> &mut [T] {
self
}
/// Returns the total number of elements the stack can hold
pub const fn capacity(&self) -> usize {
N
}
/// Moves an existing stack into an allocation of a (potentially) different size,
/// truncating if necessary.
///
/// This can be used to easily construct a half-empty stack
///
/// # Examples
///
/// You can grow a stack to fit more elements
/// ```
/// # use cl_structures::stack::Stack;
/// let v = Stack::from([0, 1, 2, 3, 4]);
/// assert_eq!(5, v.capacity());
///
/// let mut v = v.resize::<10>();
/// assert_eq!(10, v.capacity());
///
/// v.push(5);
/// ```
///
/// You can truncate a stack, dropping elements off the end
/// ```
/// # use cl_structures::stack::Stack;
/// let v = Stack::from([0, 1, 2, 3, 4, 5, 6, 7]);
/// assert_eq!(8, v.capacity());
///
/// let v = v.resize::<5>();
/// assert_eq!(5, v.capacity());
/// ```
pub fn resize<const M: usize>(mut self) -> Stack<T, M> {
// Drop elements until new length is reached
while self.len > M {
drop(self.pop());
}
let (old, len) = self.into_raw_parts();
let mut new: Stack<T, M> = Stack::new();
// Safety:
// - new and old are separate allocations
// - len <= M
unsafe {
ptr::copy_nonoverlapping(old.as_ptr(), new.buf.as_mut_ptr(), len);
}
new.len = len;
new
}
/// Push a new element onto the end of the stack
///
/// # May Panic
///
/// Panics if the new length exceeds capacity
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::Stack;
/// let mut v: Stack<_, 4> = Stack::new();
///
/// v.push(0);
/// v.push(1);
/// v.push(2);
/// v.push(3);
/// assert_eq!(&[0, 1, 2, 3], v.as_slice());
/// ```
pub fn push(&mut self, value: T) {
if self.len >= N {
panic!("Attempted to push into full stack")
}
// Safety: len is confirmed to be less than capacity
unsafe { self.push_unchecked(value) };
}
/// Push a new element onto the end of the stack
///
/// Returns [`Err(value)`](Result::Err) if the new length would exceed capacity
pub fn try_push(&mut self, value: T) -> Result<(), T> {
if self.len >= N {
return Err(value);
}
// Safety: len is confirmed to be less than capacity
unsafe { self.push_unchecked(value) };
Ok(())
}
/// Push a new element onto the end of the stack, without checking capacity
///
/// # Safety
///
/// len after push must not exceed capacity N
#[inline]
unsafe fn push_unchecked(&mut self, value: T) {
unsafe { ptr::write(self.as_mut_ptr().add(self.len), value) }
self.len += 1; // post inc
}
/// Pops the last element off the end of the stack, and returns it
///
/// Returns None if the stack is empty
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::Stack;
/// let mut v = Stack::from([0, 1, 2, 3]);
///
/// assert_eq!(Some(3), v.pop());
/// assert_eq!(Some(2), v.pop());
/// assert_eq!(Some(1), v.pop());
/// assert_eq!(Some(0), v.pop());
/// assert_eq!(None, v.pop());
/// ```
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
// Safety: MaybeUninit<T> implies ManuallyDrop<T>,
// therefore should not get dropped twice
Some(unsafe { ptr::read(self.as_ptr().add(self.len).cast()) })
}
}
/// Removes and returns the element at the given index,
/// shifting other elements toward the start
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::Stack;
/// let mut v = Stack::from([0, 1, 2, 3, 4]);
///
/// assert_eq!(2, v.remove(2));
/// assert_eq!(&[0, 1, 3, 4], v.as_slice());
/// ```
pub fn remove(&mut self, index: usize) -> T {
if index >= self.len {
panic!("Index {index} exceeded length {}", self.len)
}
let len = self.len - 1;
let base = self.as_mut_ptr();
let out = unsafe { ptr::read(base.add(index)) };
unsafe { ptr::copy(base.add(index + 1), base.add(index), len - index) };
self.len = len;
out
}
/// Removes and returns the element at the given index,
/// swapping it with the last element.
///
/// # May Panic
///
/// Panics if `index >= len`.
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::Stack;
/// let mut v = Stack::from([0, 1, 2, 3, 4]);
///
/// assert_eq!(2, v.swap_remove(2));
///
/// assert_eq!(&[0, 1, 4, 3], v.as_slice());
/// ```
pub fn swap_remove(&mut self, index: usize) -> T {
if index >= self.len {
panic!("Index {index} exceeds length {}", self.len);
}
let len = self.len - 1;
let ptr = self.as_mut_ptr();
let out = unsafe { ptr::read(ptr.add(index)) };
unsafe { ptr::copy(ptr.add(len), ptr.add(index), 1) };
self.len = len;
out
}
/// Inserts an element at position `index` in the stack,
/// shifting all elements after it to the right.
///
/// # May Panic
///
/// Panics if `index > len` or [`self.is_full()`](Stack::is_full)
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::Stack;
/// let mut v = Stack::from([0, 1, 2, 3, 4]).resize::<6>();
///
/// v.insert(3, 0xbeef);
/// assert_eq!(&[0, 1, 2, 0xbeef, 3, 4], v.as_slice());
/// ```
pub fn insert(&mut self, index: usize, data: T) {
if index > self.len {
panic!("Index {index} exceeded length {}", self.len)
}
if self.is_full() {
panic!("Attempted to insert into full stack")
}
unsafe { self.insert_unchecked(index, data) };
}
/// Attempts to insert an element at position `index` in the stack,
/// shifting all elements after it to the right.
///
/// If the stack is at capacity, returns the original element and an [InsertFailed] error.
///
/// # Examples
///
/// ```
/// use cl_structures::stack::Stack;
/// let mut v: Stack<_, 2> = Stack::new();
///
/// assert_eq!(Ok(()), v.try_insert(0, 0));
/// ```
pub fn try_insert(&mut self, index: usize, data: T) -> Result<(), (T, InsertFailed<N>)> {
if index > self.len {
return Err((data, InsertFailed::Bounds(index)));
}
if self.is_full() {
return Err((data, InsertFailed::Full));
}
// Safety: index < self.len && !self.is_full()
unsafe { self.insert_unchecked(index, data) };
Ok(())
}
/// # Safety:
/// - index must be less than self.len
/// - length after insertion must be <= N
#[inline]
unsafe fn insert_unchecked(&mut self, index: usize, data: T) {
let base = self.as_mut_ptr();
unsafe { ptr::copy(base.add(index), base.add(index + 1), self.len - index) }
self.len += 1;
self.buf[index] = MaybeUninit::new(data);
}
/// Clears the stack
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::Stack;
///
/// let mut v = Stack::from([0, 1, 2, 3, 4]);
/// assert_eq!(v.as_slice(), &[0, 1, 2, 3, 4]);
///
/// v.clear();
/// assert_eq!(v.as_slice(), &[]);
/// ```
pub fn clear(&mut self) {
// Hopefully copy elision takes care of this lmao
drop(std::mem::take(self))
}
/// Returns the number of elements in the stack
/// ```
/// # use cl_structures::stack::*;
/// let v = Stack::from([0, 1, 2, 3, 4]);
///
/// assert_eq!(5, v.len());
/// ```
pub fn len(&self) -> usize {
self.len
}
/// Returns true if the stack is at (or over) capacity
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::*;
/// let v = Stack::from([(); 10]);
///
/// assert!(v.is_full());
/// ```
#[inline]
pub fn is_full(&self) -> bool {
self.len >= N
}
/// Returns true if the stack contains no elements
///
/// # Examples
///
/// ```
/// # use cl_structures::stack::*;
/// let v: Stack<(), 10> = Stack::new();
///
/// assert!(v.is_empty());
/// ```
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum InsertFailed<const N: usize> {
Bounds(usize),
Full,
}
impl<const N: usize> std::error::Error for InsertFailed<N> {}
impl<const N: usize> std::fmt::Display for InsertFailed<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InsertFailed::Bounds(idx) => write!(f, "Index {idx} exceeded length {N}"),
InsertFailed::Full => {
write!(f, "Attempt to insert into full stack (length {N})")
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_sized() {
let v: Stack<(), { usize::MAX }> = Stack::new();
assert_eq!(usize::MAX, v.capacity());
assert_eq!(std::mem::size_of::<usize>(), std::mem::size_of_val(&v))
}
#[test]
#[cfg_attr(debug_assertions, ignore = "calls ().drop() usize::MAX times")]
fn from_usize_max_zst_array() {
let mut v = Stack::from([(); usize::MAX]);
assert_eq!(v.len(), usize::MAX);
v.pop();
assert_eq!(v.len(), usize::MAX - 1);
}
#[test]
fn new() {
let v: Stack<(), 255> = Stack::new();
assert_eq!(0, v.len());
assert_eq!(255, v.capacity());
}
#[test]
fn push() {
let mut v: Stack<_, 64> = Stack::new();
v.push(10);
}
#[test]
#[should_panic = "Attempted to push into full stack"]
fn push_overflow() {
let mut v = Stack::from([]);
v.push(10);
}
#[test]
fn pop() {
let mut v = Stack::from([1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(Some(9), v.pop());
assert_eq!(Some(8), v.pop());
assert_eq!(Some(7), v.pop());
assert_eq!(Some(6), v.pop());
assert_eq!(Some(5), v.pop());
assert_eq!(Some(4), v.pop());
assert_eq!(Some(3), v.pop());
assert_eq!(Some(2), v.pop());
assert_eq!(Some(1), v.pop());
assert_eq!(None, v.pop());
}
#[test]
fn resize_smaller() {
let v = Stack::from([1, 2, 3, 4, 5, 6, 7, 8, 9]);
let mut v = v.resize::<2>();
assert_eq!(2, v.capacity());
assert_eq!(Some(2), v.pop());
assert_eq!(Some(1), v.pop());
assert_eq!(None, v.pop());
}
#[test]
fn resize_bigger() {
let v = Stack::from([1, 2, 3, 4]);
let mut v: Stack<_, 10> = v.resize();
assert_eq!(Some(4), v.pop());
assert_eq!(Some(3), v.pop());
assert_eq!(Some(2), v.pop());
assert_eq!(Some(1), v.pop());
assert_eq!(None, v.pop());
}
#[test]
fn dangle() {
let mut v: Stack<_, 2> = Stack::new();
let a = 0;
let b = 1;
v.push(&a);
v.push(&b);
println!("{v:?}");
}
#[test]
fn remove() {
let mut v = Stack::from([0, 1, 2, 3, 4, 5]);
assert_eq!(3, v.remove(3));
assert_eq!(4, v.remove(3));
assert_eq!(5, v.remove(3));
assert_eq!(Some(2), v.pop());
assert_eq!(Some(1), v.pop());
assert_eq!(Some(0), v.pop());
assert_eq!(None, v.pop());
}
#[test]
fn swap_remove() {
let mut v = Stack::from([0, 1, 2, 3, 4, 5]);
assert_eq!(3, v.swap_remove(3));
assert_eq!(&[0, 1, 2, 5, 4], v.as_slice());
}
#[test]
fn swap_remove_last() {
let mut v = Stack::from([0, 1, 2, 3, 4, 5]);
assert_eq!(5, v.swap_remove(5));
assert_eq!(&[0, 1, 2, 3, 4], v.as_slice())
}
#[test]
fn insert() {
let mut v = Stack::from([0, 1, 2, 4, 5, 0x41414141]);
v.pop();
v.insert(3, 3);
assert_eq!(&[0, 1, 2, 3, 4, 5], v.as_slice())
}
#[test]
#[should_panic = "Attempted to insert into full stack"]
fn insert_overflow() {
let mut v = Stack::from([0]);
v.insert(0, 1);
}
#[test]
fn drop() {
let v = Stack::from([
Box::new(0),
Box::new(1),
Box::new(2),
Box::new(3),
Box::new(4),
]);
std::mem::drop(std::hint::black_box(v));
}
}

221
cl-structures/src/tree.rs Normal file
View File

@@ -0,0 +1,221 @@
//! An insert-only unordered tree, backed by a [Vec]
//!
//! # Examples
//! ```
//! use cl_structures::tree::{Tree, Node};
//! // A tree can be created
//! let mut tree = Tree::new();
//! // Provided with a root node
//! let root = tree.root("This is the root node").unwrap();
//!
//! // Nodes can be accessed by indexing
//! assert_eq!(*tree[root].as_ref(), "This is the root node");
//! // Nodes' data can be accessed directly by calling `get`/`get_mut`
//! assert_eq!(tree.get(root).unwrap(), &"This is the root node")
//! ```
// TODO: implement an Entry-style API for doing traversal algorithms
pub use self::tree_ref::Ref;
use std::ops::{Index, IndexMut};
pub mod tree_ref;
/// An insert-only unordered tree, backed by a [Vec]
#[derive(Debug)]
pub struct Tree<T> {
nodes: Vec<Node<T>>,
}
impl<T> Default for Tree<T> {
fn default() -> Self {
Self { nodes: Default::default() }
}
}
/// Getters
impl<T> Tree<T> {
pub fn get(&self, index: Ref<T>) -> Option<&T> {
self.get_node(index).map(|node| &node.value)
}
pub fn get_mut(&mut self, index: Ref<T>) -> Option<&mut T> {
self.get_node_mut(index).map(|node| &mut node.value)
}
pub fn get_node(&self, index: Ref<T>) -> Option<&Node<T>> {
self.nodes.get(usize::from(index))
}
pub fn get_node_mut(&mut self, index: Ref<T>) -> Option<&mut Node<T>> {
self.nodes.get_mut(usize::from(index))
}
}
/// Tree operations
impl<T> Tree<T> {
pub fn new() -> Self {
Self { nodes: Default::default() }
}
/// Creates a new root for the tree.
///
/// If the tree already has a root, the value will be returned.
pub fn root(&mut self, value: T) -> Result<Ref<T>, T> {
if self.is_empty() {
// Create an index for the new node
let node = Ref::new_unchecked(self.nodes.len());
// add child to tree
self.nodes.push(Node::from(value));
Ok(node)
} else {
Err(value)
}
}
pub fn get_root(&mut self) -> Option<Ref<T>> {
match self.nodes.is_empty() {
true => None,
false => Some(Ref::new_unchecked(0)),
}
}
/// Insert a value into the tree as a child of the parent node
///
/// # Panics
/// May panic if the node [Ref] is from a different tree
pub fn insert(&mut self, value: T, parent: Ref<T>) -> Ref<T> {
let child = Ref::new_unchecked(self.nodes.len());
// add child to tree before parent
self.nodes.push(Node::with_parent(value, parent));
// add child to parent
self[parent].children.push(child);
child
}
/// Gets the depth of a node
///
/// # Panics
/// May panic if the node [Ref] is from a different tree
pub fn depth(&self, node: Ref<T>) -> usize {
match self[node].parent {
Some(node) => self.depth(node) + 1,
None => 0,
}
}
/// Gets the number of branches in the tree
pub fn branches(&self) -> usize {
self.nodes.iter().fold(0, |edges, node| edges + node.len())
}
}
/// Standard data structure functions
impl<T> Tree<T> {
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
}
impl<T> Index<Ref<T>> for Tree<T> {
type Output = Node<T>;
fn index(&self, index: Ref<T>) -> &Self::Output {
self.get_node(index).expect("Ref should be inside Tree")
}
}
impl<T> IndexMut<Ref<T>> for Tree<T> {
fn index_mut(&mut self, index: Ref<T>) -> &mut Self::Output {
self.get_node_mut(index).expect("Ref should be inside Tree")
}
}
/// A node in a [Tree]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Node<T> {
value: T,
/// The parent
parent: Option<Ref<T>>,
/// The children
children: Vec<Ref<T>>,
}
impl<T> Node<T> {
pub const fn new(value: T) -> Self {
Self { value, parent: None, children: vec![] }
}
pub const fn with_parent(value: T, parent: Ref<T>) -> Self {
Self { value, parent: Some(parent), children: vec![] }
}
pub fn get(&self) -> &T {
self.as_ref()
}
pub fn get_mut(&mut self) -> &mut T {
self.as_mut()
}
pub fn swap(&mut self, value: T) -> T {
std::mem::replace(&mut self.value, value)
}
pub fn parent(&self) -> Option<Ref<T>> {
self.parent
}
pub fn children(&self) -> &[Ref<T>] {
&self.children
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
}
impl<T> AsRef<T> for Node<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl<T> AsMut<T> for Node<T> {
fn as_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T> From<T> for Node<T> {
#[inline]
fn from(value: T) -> Self {
Self::new(value)
}
}
#[cfg(test)]
mod test {
#[allow(unused)]
use super::*;
#[test]
fn add_children() {
let mut tree = Tree::new();
let root = tree.root(0).unwrap();
let one = tree.insert(1, root);
let two = tree.insert(2, root);
assert_eq!([one, two].as_slice(), tree[root].children());
}
#[test]
fn nest_children() {
let mut tree = Tree::new();
let root = tree.root(0).unwrap();
let one = tree.insert(1, root);
let two = tree.insert(2, one);
assert_eq!(&[one], tree[root].children());
assert_eq!(&[two], tree[one].children());
assert_eq!(tree[two].children(), &[]);
}
#[test]
fn compares_equal() {}
}

View File

@@ -0,0 +1,70 @@
//! An element in a [Tree](super::Tree)
///
/// Contains a niche, and as such, [`Option<TreeRef<T>>`] is free :D
use std::{marker::PhantomData, num::NonZeroUsize};
/// An element of in a [Tree](super::Tree).
//? The index of the node is stored as a [NonZeroUsize] for space savings
//? Making Refs T-specific helps the user keep track of which Refs belong to which trees.
//? This isn't bulletproof, of course, but it'll keep Ref<Foo> from being used on Tree<Bar>
pub struct Ref<T: ?Sized>(NonZeroUsize, PhantomData<T>);
impl<T: ?Sized> Ref<T> {
/// Constructs a new [Ref] with the given index
pub fn new_unchecked(index: usize) -> Self {
// Safety: index cannot be zero because we use saturating addition on unsigned type.
Self(
unsafe { NonZeroUsize::new_unchecked(index.saturating_add(1)) },
PhantomData,
)
}
}
impl<T: ?Sized> From<Ref<T>> for usize {
fn from(value: Ref<T>) -> Self {
usize::from(value.0) - 1
}
}
/* --- implementations of derivable traits, because we don't need bounds here --- */
impl<T: ?Sized> std::fmt::Debug for Ref<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("TreeRef").field(&self.0).finish()
}
}
impl<T: ?Sized> std::hash::Hash for Ref<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
self.1.hash(state);
}
}
impl<T: ?Sized> PartialEq for Ref<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0 && self.1 == other.1
}
}
impl<T: ?Sized> Eq for Ref<T> {}
impl<T: ?Sized> PartialOrd for Ref<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T: ?Sized> Ord for Ref<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<T: ?Sized> Clone for Ref<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for Ref<T> {}

View File

@@ -1,6 +1,6 @@
//! # Token
//!
//! Stores a component of a file as a [Type], some [Data], and a line and column number
//! Stores a component of a file as a [TokenKind], some [TokenData], and a line and column number
#![warn(clippy::all)]
#![feature(decl_macro)]
@@ -9,5 +9,5 @@ pub mod token_data;
pub mod token_type;
pub use token::Token;
pub use token_data::Data;
pub use token_type::{Keyword, Type};
pub use token_data::TokenData;
pub use token_type::{Punct, TokenKind};

View File

@@ -1,34 +1,34 @@
//! A [Token] contains a single unit of lexical information, and an optional bit of [Data]
use super::{Data, Type};
//! A [Token] contains a single unit of lexical information, and an optional bit of [TokenData]
use super::{TokenData, TokenKind};
/// Contains a single unit of lexical information,
/// and an optional bit of [Data]
/// and an optional bit of [TokenData]
#[derive(Clone, Debug, PartialEq)]
pub struct Token {
ty: Type,
data: Data,
line: u32,
col: u32,
pub ty: TokenKind,
pub data: TokenData,
pub line: u32,
pub col: u32,
}
impl Token {
/// Creates a new [Token] out of a [Type], [Data], line, and column.
pub fn new(ty: Type, data: impl Into<Data>, line: u32, col: u32) -> Self {
/// Creates a new [Token] out of a [TokenKind], [TokenData], line, and column.
pub fn new(ty: TokenKind, data: impl Into<TokenData>, line: u32, col: u32) -> Self {
Self { ty, data: data.into(), line, col }
}
/// Casts this token to a new [Type]
pub fn cast(self, ty: Type) -> Self {
/// Casts this token to a new [TokenKind]
pub fn cast(self, ty: TokenKind) -> Self {
Self { ty, ..self }
}
/// Returns the [Type] of this token
pub fn ty(&self) -> Type {
/// Returns the [TokenKind] of this token
pub fn ty(&self) -> TokenKind {
self.ty
}
/// Returns a reference to this token's [Data]
pub fn data(&self) -> &Data {
/// Returns a reference to this token's [TokenData]
pub fn data(&self) -> &TokenData {
&self.data
}
/// Converts this token into its inner [Data]
pub fn into_data(self) -> Data {
/// Converts this token into its inner [TokenData]
pub fn into_data(self) -> TokenData {
self.data
}
/// Returns the line where this token originated

View File

@@ -1,11 +1,9 @@
//! Additional data stored within a [Token](super::Token),
//! external to its [Type](super::token_type::Type)
//! external to its [TokenKind](super::token_type::TokenKind)
/// Additional data stored within a [Token](super::Token),
/// external to its [Type](super::token_type::Type)
/// external to its [TokenKind](super::token_type::TokenKind)
#[derive(Clone, Debug, PartialEq)]
pub enum Data {
/// [Token](super::Token) contains an [identifier](str)
Identifier(Box<str>),
pub enum TokenData {
/// [Token](super::Token) contains a [String]
String(String),
/// [Token](super::Token) contains a [character](char)
@@ -18,7 +16,6 @@ pub enum Data {
None,
}
from! {
value: &str => Self::Identifier(value.into()),
value: String => Self::String(value),
value: u128 => Self::Integer(value),
value: f64 => Self::Float(value),
@@ -27,19 +24,18 @@ from! {
}
/// Implements [From] for an enum
macro from($($value:ident: $src:ty => $dst:expr),*$(,)?) {
$(impl From<$src> for Data {
$(impl From<$src> for TokenData {
fn from($value: $src) -> Self { $dst }
})*
}
impl std::fmt::Display for Data {
impl std::fmt::Display for TokenData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Data::Identifier(v) => v.fmt(f),
Data::String(v) => write!(f, "\"{v}\""),
Data::Character(v) => write!(f, "'{v}'"),
Data::Integer(v) => v.fmt(f),
Data::Float(v) => v.fmt(f),
Data::None => "None".fmt(f),
TokenData::String(v) => write!(f, "\"{v}\""),
TokenData::Character(v) => write!(f, "'{v}'"),
TokenData::Integer(v) => v.fmt(f),
TokenData::Float(v) => v.fmt(f),
TokenData::None => "None".fmt(f),
}
}
}

View File

@@ -3,20 +3,48 @@ use std::{fmt::Display, str::FromStr};
/// Stores a [Token's](super::Token) lexical information
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Type {
// Invalid syntax
pub enum TokenKind {
/// Invalid sequence
Invalid,
// Any kind of comment
/// Any kind of comment
Comment,
// Any identifier
/// Any tokenizable literal (See [TokenData](super::TokenData))
Literal,
/// A non-keyword identifier
Identifier,
Keyword(Keyword),
// Literals
Integer,
Float,
String,
Character,
// Delimiters and punctuation
// A keyword
Break,
Cl,
Const,
Continue,
Else,
Enum,
False,
For,
Fn,
If,
Impl,
In,
Let,
Mod,
Mut,
Pub,
Return,
SelfKw,
SelfTy,
Static,
Struct,
Super,
True,
Type,
While,
/// Delimiter or punctuation
Punct(Punct),
}
/// An operator character (delimiter, punctuation)
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Punct {
LCurly, // {
RCurly, // }
LBrack, // [
@@ -73,139 +101,48 @@ pub enum Type {
XorXor, // ^^
}
/// Represents a reserved word.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Keyword {
Break,
Cl,
Const,
Continue,
Else,
Enum,
False,
For,
Fn,
If,
Impl,
In,
Let,
Mod,
Mut,
Pub,
Return,
SelfKw,
SelfTy,
Static,
Struct,
Super,
True,
Type,
While,
}
impl Display for Type {
impl Display for TokenKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Invalid => "invalid".fmt(f),
Type::Comment => "comment".fmt(f),
Type::Identifier => "identifier".fmt(f),
Type::Keyword(k) => k.fmt(f),
Type::Integer => "integer literal".fmt(f),
Type::Float => "float literal".fmt(f),
Type::String => "string literal".fmt(f),
Type::Character => "char literal".fmt(f),
Type::LCurly => "left curly".fmt(f),
Type::RCurly => "right curly".fmt(f),
Type::LBrack => "left brack".fmt(f),
Type::RBrack => "right brack".fmt(f),
Type::LParen => "left paren".fmt(f),
Type::RParen => "right paren".fmt(f),
Type::Amp => "and".fmt(f),
Type::AmpAmp => "and-and".fmt(f),
Type::AmpEq => "and-assign".fmt(f),
Type::Arrow => "arrow".fmt(f),
Type::At => "at".fmt(f),
Type::Backslash => "backslash".fmt(f),
Type::Bang => "bang".fmt(f),
Type::BangBang => "not-not".fmt(f),
Type::BangEq => "not equal to".fmt(f),
Type::Bar => "or".fmt(f),
Type::BarBar => "or-or".fmt(f),
Type::BarEq => "or-assign".fmt(f),
Type::Colon => "colon".fmt(f),
Type::ColonColon => "path separator".fmt(f),
Type::Comma => "comma".fmt(f),
Type::Dot => "dot".fmt(f),
Type::DotDot => "exclusive range".fmt(f),
Type::DotDotEq => "inclusive range".fmt(f),
Type::Eq => "assign".fmt(f),
Type::EqEq => "equal to".fmt(f),
Type::FatArrow => "fat arrow".fmt(f),
Type::Grave => "grave".fmt(f),
Type::Gt => "greater than".fmt(f),
Type::GtEq => "greater than or equal to".fmt(f),
Type::GtGt => "shift right".fmt(f),
Type::GtGtEq => "shift right-assign".fmt(f),
Type::Hash => "hash".fmt(f),
Type::HashBang => "shebang".fmt(f),
Type::Lt => "less than".fmt(f),
Type::LtEq => "less than or equal to".fmt(f),
Type::LtLt => "shift left".fmt(f),
Type::LtLtEq => "shift left-assign".fmt(f),
Type::Minus => "sub".fmt(f),
Type::MinusEq => "sub-assign".fmt(f),
Type::Plus => "add".fmt(f),
Type::PlusEq => "add-assign".fmt(f),
Type::Question => "huh?".fmt(f),
Type::Rem => "rem".fmt(f),
Type::RemEq => "rem-assign".fmt(f),
Type::Semi => "ignore".fmt(f),
Type::Slash => "div".fmt(f),
Type::SlashEq => "div-assign".fmt(f),
Type::Star => "star".fmt(f),
Type::StarEq => "star-assign".fmt(f),
Type::Tilde => "tilde".fmt(f),
Type::Xor => "xor".fmt(f),
Type::XorEq => "xor-assign".fmt(f),
Type::XorXor => "cat-ears".fmt(f),
}
}
}
TokenKind::Invalid => "invalid".fmt(f),
TokenKind::Comment => "comment".fmt(f),
TokenKind::Literal => "literal".fmt(f),
TokenKind::Identifier => "identifier".fmt(f),
impl Display for Keyword {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Break => "break".fmt(f),
Self::Cl => "cl".fmt(f),
Self::Const => "const".fmt(f),
Self::Continue => "continue".fmt(f),
Self::Else => "else".fmt(f),
Self::Enum => "enum".fmt(f),
Self::False => "false".fmt(f),
Self::For => "for".fmt(f),
Self::Fn => "fn".fmt(f),
Self::If => "if".fmt(f),
Self::Impl => "impl".fmt(f),
Self::In => "in".fmt(f),
Self::Let => "let".fmt(f),
Self::Mod => "mod".fmt(f),
Self::Mut => "mut".fmt(f),
Self::Pub => "pub".fmt(f),
Self::Return => "return".fmt(f),
Self::SelfKw => "self".fmt(f),
Self::SelfTy => "Self".fmt(f),
Self::Static => "static".fmt(f),
Self::Struct => "struct".fmt(f),
Self::Super => "super".fmt(f),
Self::True => "true".fmt(f),
Self::Type => "type".fmt(f),
Self::While => "while".fmt(f),
TokenKind::Break => "break".fmt(f),
TokenKind::Cl => "cl".fmt(f),
TokenKind::Const => "const".fmt(f),
TokenKind::Continue => "continue".fmt(f),
TokenKind::Else => "else".fmt(f),
TokenKind::Enum => "enum".fmt(f),
TokenKind::False => "false".fmt(f),
TokenKind::For => "for".fmt(f),
TokenKind::Fn => "fn".fmt(f),
TokenKind::If => "if".fmt(f),
TokenKind::Impl => "impl".fmt(f),
TokenKind::In => "in".fmt(f),
TokenKind::Let => "let".fmt(f),
TokenKind::Mod => "mod".fmt(f),
TokenKind::Mut => "mut".fmt(f),
TokenKind::Pub => "pub".fmt(f),
TokenKind::Return => "return".fmt(f),
TokenKind::SelfKw => "self".fmt(f),
TokenKind::SelfTy => "Self".fmt(f),
TokenKind::Static => "static".fmt(f),
TokenKind::Struct => "struct".fmt(f),
TokenKind::Super => "super".fmt(f),
TokenKind::True => "true".fmt(f),
TokenKind::Type => "type".fmt(f),
TokenKind::While => "while".fmt(f),
TokenKind::Punct(op) => op.fmt(f),
}
}
}
impl FromStr for Keyword {
impl FromStr for TokenKind {
/// [FromStr] can only fail when an identifier isn't a keyword
type Err = ();
/// Parses a string s to return a Keyword
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"break" => Self::Break,
@@ -237,3 +174,64 @@ impl FromStr for Keyword {
})
}
}
impl Display for Punct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Punct::LCurly => "{".fmt(f),
Punct::RCurly => "}".fmt(f),
Punct::LBrack => "[".fmt(f),
Punct::RBrack => "]".fmt(f),
Punct::LParen => "(".fmt(f),
Punct::RParen => ")".fmt(f),
Punct::Amp => "&".fmt(f),
Punct::AmpAmp => "&&".fmt(f),
Punct::AmpEq => "&=".fmt(f),
Punct::Arrow => "->".fmt(f),
Punct::At => "@".fmt(f),
Punct::Backslash => "\\".fmt(f),
Punct::Bang => "!".fmt(f),
Punct::BangBang => "!!".fmt(f),
Punct::BangEq => "!=".fmt(f),
Punct::Bar => "|".fmt(f),
Punct::BarBar => "||".fmt(f),
Punct::BarEq => "|=".fmt(f),
Punct::Colon => ":".fmt(f),
Punct::ColonColon => "::".fmt(f),
Punct::Comma => ",".fmt(f),
Punct::Dot => ".".fmt(f),
Punct::DotDot => "..".fmt(f),
Punct::DotDotEq => "..=".fmt(f),
Punct::Eq => "=".fmt(f),
Punct::EqEq => "==".fmt(f),
Punct::FatArrow => "=>".fmt(f),
Punct::Grave => "`".fmt(f),
Punct::Gt => ">".fmt(f),
Punct::GtEq => ">=".fmt(f),
Punct::GtGt => ">>".fmt(f),
Punct::GtGtEq => ">>=".fmt(f),
Punct::Hash => "#".fmt(f),
Punct::HashBang => "#!".fmt(f),
Punct::Lt => "<".fmt(f),
Punct::LtEq => "<=".fmt(f),
Punct::LtLt => "<<".fmt(f),
Punct::LtLtEq => "<<=".fmt(f),
Punct::Minus => "-".fmt(f),
Punct::MinusEq => "-=".fmt(f),
Punct::Plus => "+".fmt(f),
Punct::PlusEq => "+=".fmt(f),
Punct::Question => "?".fmt(f),
Punct::Rem => "%".fmt(f),
Punct::RemEq => "%=".fmt(f),
Punct::Semi => ";".fmt(f),
Punct::Slash => "/".fmt(f),
Punct::SlashEq => "/=".fmt(f),
Punct::Star => "*".fmt(f),
Punct::StarEq => "*=".fmt(f),
Punct::Tilde => "~".fmt(f),
Punct::Xor => "^".fmt(f),
Punct::XorEq => "^=".fmt(f),
Punct::XorXor => "^^".fmt(f),
}
}
}

View File

@@ -9,3 +9,4 @@ publish.workspace = true
[dependencies]
cl-ast = { path = "../cl-ast" }
cl-structures = { path = "../cl-structures" }

View File

@@ -1,142 +1,790 @@
//! # The Conlang Type Checker
//!
//! As a statically typed language, Conlang requires a robust type checker to enforce correctness.
#![feature(debug_closure_helpers)]
#![warn(clippy::all)]
#![allow(unused)]
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use cl_ast::*;
/*
pub mod intern {
//! Trivially-copyable, easily comparable typed indices for type system constructs
The type checker keeps track of a *global intern pool* for Types and Values
References to the intern pool are held by ID, and items cannot be freed from the pool EVER.
/// Creates newtype indices over [`usize`] for use elsewhere in the type checker
macro_rules! def_id {($($(#[$meta:meta])* $name:ident),*$(,)?) => {$(
$(#[$meta])*
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name(usize);
Items are inserted into their respective pools,
impl $name {
#[doc = concat!("Constructs a [`", stringify!($name), "`] from a [`usize`] without checking bounds.")]
/// # Safety
/// The provided value should be within the bounds of its associated container
pub unsafe fn from_raw_unchecked(value: usize) -> Self {
Self(value)
}
/// Gets the index of the type by-value
pub fn value(&self) -> usize {
self.0
}
}
impl From< $name > for usize {
fn from(value: $name) -> Self {
value.0
}
}
)*}}
*/
pub mod key {
use cl_structures::intern_pool::*;
// define the index types
def_id! {
/// Uniquely represents a Type
TypeID,
/// Uniquely represents a Value
ValueID,
make_intern_key! {
/// Uniquely represents a [Def][1] in the [Def][1] [Pool]
///
/// [1]: crate::definition::Def
DefID,
}
}
pub mod typedef {
//! Representations of type definitions
// use std::collections::HashMap;
pub mod definition {
use crate::{key::DefID, module::Module, type_kind::TypeKind, value_kind::ValueKind};
use cl_ast::{format::FmtPretty, Item, Meta, Visibility};
use std::fmt::Write;
use crate::intern::TypeID;
use cl_ast::{Item, Visibility};
#[derive(Clone, PartialEq, Eq)]
pub struct Def {
pub name: String,
pub vis: Visibility,
pub meta: Vec<Meta>,
pub kind: DefKind,
pub source: Option<Item>,
pub module: Module,
}
/// The definition of a type
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TypeDef {
impl Default for Def {
fn default() -> Self {
Self {
name: Default::default(),
vis: Default::default(),
meta: Default::default(),
kind: DefKind::Type(TypeKind::Module),
source: Default::default(),
module: Default::default(),
}
}
}
impl Def {
pub fn new_module(
name: String,
kind: Option<TypeKind>,
definition: Item,
vis: Visibility,
meta: Vec<Meta>,
parent: Option<DefID>,
) -> Self {
Self {
name,
vis,
meta,
kind: DefKind::Type(TypeKind::Module),
source: None,
module: Module { parent, ..Default::default() },
}
}
}
impl std::fmt::Debug for Def {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, vis, meta, kind, source, module } = self;
f.debug_struct("Def")
.field("name", &name)
.field("vis", &vis)
.field_with("meta", |f| write!(f, "{meta:?}"))
.field("kind", &kind)
.field_with("source", |f| match source {
Some(item) => write!(f.pretty(), "{{\n{item}\n}}"),
None => todo!(),
})
.field("module", &module)
.finish()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DefKind {
/// A type, such as a ``
Type(TypeKind),
/// A value, such as a `const`, `static`, or `fn`
Value(ValueKind),
}
impl DefKind {
pub fn is_type(&self) -> bool {
matches!(self, Self::Type(_))
}
pub fn ty(&self) -> Option<&TypeKind> {
match self {
DefKind::Type(t) => Some(t),
_ => None,
}
}
pub fn is_value(&self) -> bool {
matches!(self, Self::Value(_))
}
pub fn value(&self) -> Option<&ValueKind> {
match self {
DefKind::Value(v) => Some(v),
_ => None,
}
}
}
}
pub mod type_kind {
//! A [TypeKind] represents an item in the Type Namespace
//! (a component of a [Project](crate::project::Project)).
use cl_ast::Visibility;
use std::{fmt::Debug, str::FromStr};
use crate::key::DefID;
/// The kind of a type
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeKind {
/// A type which has not yet been resolved
Undecided,
/// An alias for an already-defined type
Alias(Option<DefID>),
/// A primitive type, built-in to the compiler
Intrinsic,
/// A user-defined structural product type
Struct(Vec<(String, Visibility, TypeID)>),
/// A user-defined union-like enum type
Enum(Vec<(String, TypeID)>),
/// A type alias
Alias(TypeID),
Intrinsic(Intrinsic),
/// A user-defined abstract data type
Adt(Adt),
/// A reference to an already-defined type: &T
Ref(DefID),
/// A contiguous view of dynamically sized memory
Slice(DefID),
/// A function pointer which accepts multiple inputs and produces an output
FnPtr { args: Vec<DefID>, rety: DefID },
/// The unit type
Empty,
/// The never type
Never,
/// The Self type
SelfTy,
// TODO: other types
/// An untyped module
Module,
}
/// A user-defined Abstract Data Type
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Adt {
/// A union-like enum type
Enum(Vec<(String, DefID)>),
CLikeEnum(Vec<(String, u128)>),
/// An enum with no fields, which can never be constructed
FieldlessEnum,
/// A structural product type with named members
Struct(Vec<(String, Visibility, DefID)>),
/// A structural product type with unnamed members
TupleStruct(Vec<(Visibility, DefID)>),
/// A structural product type of neither named nor unnamed members
UnitStruct,
/// A choose your own undefined behavior type
/// TODO: should unions be a language feature?
Union(Vec<(String, DefID)>),
}
/// The set of compiler-intrinsic types.
/// These primitive types have native implementations of the basic operations.
#[allow(non_camel_case_types)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Intrinsic {
/// An 8-bit signed integer: `#[intrinsic = "i8"]`
I8,
/// A 16-bit signed integer: `#[intrinsic = "i16"]`
I16,
/// A 32-bit signed integer: `#[intrinsic = "i32"]`
I32,
/// A 64-bit signed integer: `#[intrinsic = "i32"]`
I64,
// /// A 128-bit signed integer: `#[intrinsic = "i32"]`
// I128,
/// An 8-bit unsigned integer: `#[intrinsic = "u8"]`
U8,
/// A 16-bit unsigned integer: `#[intrinsic = "u16"]`
U16,
/// A 32-bit unsigned integer: `#[intrinsic = "u32"]`
U32,
/// A 64-bit unsigned integer: `#[intrinsic = "u64"]`
U64,
// /// A 128-bit unsigned integer: `#[intrinsic = "u128"]`
// U128,
/// A boolean (`true` or `false`): `#[intrinsic = "bool"]`
Bool,
}
impl FromStr for Intrinsic {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"i8" => Intrinsic::I8,
"i16" => Intrinsic::I16,
"i32" => Intrinsic::I32,
"i64" => Intrinsic::I64,
"u8" => Intrinsic::U8,
"u16" => Intrinsic::U16,
"u32" => Intrinsic::U32,
"u64" => Intrinsic::U64,
"bool" => Intrinsic::Bool,
_ => Err(())?,
})
}
}
pub mod valdef {
//! Representations of value definitions
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Float {
F32 = 0x20,
F64,
}
}
use crate::intern::{TypeID, ValueID};
pub mod value_kind {
//! A [ValueKind] represents an item in the Value Namespace
//! (a component of a [Project](crate::project::Project)).
use crate::typeref::TypeRef;
use cl_ast::Block;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ValueDef {
name: String,
kind: Option<ValueKind>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ValueKind {
Const(),
Static(),
Undecided,
Const(TypeRef),
Static(TypeRef),
Fn {
args: Vec<TypeID>,
rety: TypeID,
// TODO: Store the variable bindings here!
args: Vec<TypeRef>,
rety: TypeRef,
body: Block,
},
}
}
pub mod typeinfo {
//! Stores typeck-time type inference info
use crate::intern::TypeID;
/// The Type struct represents all valid types, and can be trivially equality-compared
pub struct Type {
/// You can only have a pointer chain 65535 pointers long.
ref_depth: u16,
/// Types can be [Generic](TKind::Generic) or [Concrete](TKind::Concrete)
kind: TKind,
}
/// Types can be [Generic](TKind::Generic) or [Concrete](TKind::Concrete)
pub enum TKind {
/// A Concrete type has an associated [TypeDef](super::typedef::TypeDef)
Concrete(TypeID),
/// A Generic type is a *locally unique* comparable value,
/// valid only until the end of its inference context
Generic(usize),
}
}
pub mod type_context {
//! A type context stores a map from names to TypeIDs
pub mod module {
//! A [Module] is a node in the Module Tree (a component of a
//! [Project](crate::project::Project))
use crate::key::DefID;
use std::collections::HashMap;
use crate::intern::TypeID;
/// A [Module] is a node in the Module Tree (a component of a
/// [Project](crate::project::Project)).
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Module {
pub parent: Option<DefID>,
pub types: HashMap<String, DefID>,
pub values: HashMap<String, DefID>,
}
pub struct TypeCtx {
parent: Option<Box<TypeCtx>>,
concrete: HashMap<String, TypeID>,
impl Module {
pub fn new(parent: DefID) -> Self {
Self { parent: Some(parent), ..Default::default() }
}
}
}
pub mod path {
use cl_ast::{Path as AstPath, PathPart};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Path<'p> {
pub absolute: bool,
pub parts: &'p [PathPart],
}
impl<'p> Path<'p> {
pub fn new(path: &'p AstPath) -> Self {
let AstPath { absolute, parts } = path;
Self { absolute: *absolute, parts }
}
pub fn relative(self) -> Self {
Self { absolute: false, ..self }
}
pub fn pop_front(self) -> Option<Self> {
let Self { absolute, parts } = self;
Some(Self { absolute, parts: parts.get(1..)? })
}
pub fn is_empty(&self) -> bool {
self.parts.is_empty()
}
pub fn len(&self) -> usize {
self.parts.len()
}
pub fn front(&self) -> Option<&PathPart> {
self.parts.first()
}
}
impl<'p> From<&'p AstPath> for Path<'p> {
fn from(value: &'p AstPath) -> Self {
Self::new(value)
}
}
impl std::fmt::Display for Path<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
const SEPARATOR: &str = "::";
let Self { absolute, parts } = self;
if *absolute {
write!(f, "{SEPARATOR}")?
}
for (idx, part) in parts.iter().enumerate() {
write!(f, "{}{part}", if idx > 0 { SEPARATOR } else { "" })?;
}
Ok(())
}
}
}
pub mod project {
use crate::{
definition::{Def, DefKind},
key::DefID,
path::Path,
type_kind::TypeKind,
};
use cl_ast::{Identifier, PathPart, Visibility};
use cl_structures::intern_pool::Pool;
use std::ops::{Index, IndexMut};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Project {
pub pool: Pool<Def, DefID>,
pub module_root: DefID,
}
impl Project {
pub fn new() -> Self {
Self::default()
}
}
impl Default for Project {
fn default() -> Self {
let mut pool = Pool::default();
let module_root = pool.insert(Def::default());
// Insert the Never(!) type
let never = pool.insert(Def {
name: String::from("!"),
vis: Visibility::Public,
kind: DefKind::Type(TypeKind::Never),
..Default::default()
});
pool[module_root]
.module
.types
.insert(String::from("!"), never);
Self { pool, module_root }
}
}
impl Project {
pub fn parent_of(&self, module: DefID) -> Option<DefID> {
self[module].module.parent
}
pub fn root_of(&self, module: DefID) -> DefID {
match self.parent_of(module) {
Some(module) => self.root_of(module),
None => module,
}
}
/// Resolves a path within a module tree, finding the innermost module.
/// Returns the remaining path parts.
pub fn get_type<'a>(&self, path: Path<'a>, within: DefID) -> Option<(DefID, Path<'a>)> {
// TODO: Cache module lookups
if path.absolute {
self.get_type(path.relative(), self.root_of(within))
} else if let Some(front) = path.front() {
let module = &self[within].module;
match front {
PathPart::SelfKw => self.get_type(path.pop_front()?, within),
PathPart::SuperKw => self.get_type(path.pop_front()?, module.parent?),
PathPart::Ident(Identifier(name)) => match module.types.get(name) {
Some(&submodule) => self.get_type(path.pop_front()?, submodule),
None => Some((within, path)),
},
}
} else {
Some((within, path))
}
}
pub fn get_value<'a>(&self, path: Path<'a>, within: DefID) -> Option<(DefID, Path<'a>)> {
match path.front()? {
PathPart::Ident(Identifier(name)) => Some((
self[within].module.values.get(name).copied()?,
path.pop_front()?,
)),
_ => None,
}
}
#[rustfmt::skip]
pub fn insert_type(&mut self, name: String, value: Def, parent: DefID) -> Option<DefID> {
let id = self.pool.insert(value);
self[parent].module.types.insert(name, id)
}
#[rustfmt::skip]
pub fn insert_value(&mut self, name: String, value: Def, parent: DefID) -> Option<DefID> {
let id = self.pool.insert(value);
self[parent].module.values.insert(name, id)
}
}
/// Implements [Index] and [IndexMut] for [Project]: `self.table[ID] -> Definition`
macro_rules! impl_index {
($(self.$table:ident[$idx:ty] -> $out:ty),*$(,)?) => {$(
impl Index<$idx> for Project {
type Output = $out;
fn index(&self, index: $idx) -> &Self::Output {
&self.$table[index]
}
}
impl IndexMut<$idx> for Project {
fn index_mut(&mut self, index: $idx) -> &mut Self::Output {
&mut self.$table[index]
}
}
)*};
}
impl_index! {
self.pool[DefID] -> Def,
// self.types[TypeID] -> TypeDef,
// self.values[ValueID] -> ValueDef,
}
}
pub mod name_collector {
//! Performs step 1 of type checking: Collecting all the names of things into [Module] units
use crate::{
definition::{Def, DefKind},
key,
project::Project,
type_kind::{Adt, TypeKind},
value_kind::ValueKind,
};
use cl_ast::*;
use std::ops::{Deref, DerefMut};
/// Collects types for future use
#[derive(Debug, PartialEq, Eq)]
pub struct NameCollector<'prj> {
/// A stack of the current modules
pub mod_stack: Vec<key::DefID>,
/// The [Project], the type checker and resolver's central data store
pub project: &'prj mut Project,
}
impl<'prj> NameCollector<'prj> {
pub fn new(project: &'prj mut Project) -> Self {
// create a root module
Self { mod_stack: vec![project.module_root], project }
}
/// Gets the currently traversed parent module
pub fn parent(&self) -> Option<key::DefID> {
self.mod_stack.last().copied()
}
}
impl Deref for NameCollector<'_> {
type Target = Project;
fn deref(&self) -> &Self::Target {
self.project
}
}
impl DerefMut for NameCollector<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.project
}
}
impl NameCollector<'_> {
pub fn file(&mut self, f: &File) -> Result<(), &'static str> {
let parent = self.parent().ok_or("No parent to add item to")?;
for item in &f.items {
let def = match &item.kind {
// modules
// types
ItemKind::Module(_) => {
self.module(item)?;
continue;
}
ItemKind::Enum(_) => Some(self.ty_enum(item)?),
ItemKind::Alias(_) => Some(self.ty_alias(item)?),
ItemKind::Struct(_) => Some(self.ty_struct(item)?),
// values processed by the value collector
ItemKind::Const(_) => Some(self.val_const(item)?),
ItemKind::Static(_) => Some(self.val_static(item)?),
ItemKind::Function(_) => Some(self.val_function(item)?),
ItemKind::Impl(_) => None,
};
let Some(def) = def else { continue };
match def.kind {
DefKind::Type(_) => {
if let Some(v) = self.insert_type(def.name.clone(), def, parent) {
panic!("Redefinition of type {} ({v:?})!", self[v].name)
}
}
DefKind::Value(_) => {
if let Some(v) = self.insert_value(def.name.clone(), def, parent) {
panic!("Redefinition of value {} ({v:?})!", self[v].name)
}
}
}
}
Ok(())
}
/// Collects a [Module]
pub fn module(&mut self, m: &Item) -> Result<(), &'static str> {
let Item { kind: ItemKind::Module(Module { name, kind }), vis, attrs, .. } = m else {
Err("module called on Item which was not an ItemKind::Module")?
};
let ModuleKind::Inline(kind) = kind else {
Err("Out-of-line modules not yet supported")?
};
let parent = self.parent().ok_or("No parent to add module to")?;
let module = self.pool.insert(Def::new_module(
name.0.clone(),
*vis,
attrs.meta.clone(),
Some(parent),
));
self[parent]
.module
.types
.insert(name.0.clone(), module)
.is_some()
.then(|| panic!("Error: redefinition of module {name}"));
self.mod_stack.push(module);
let out = self.file(kind);
self.mod_stack.pop();
out
}
}
/// Type collection
impl NameCollector<'_> {
/// Collects an [Item] of type [ItemKind::Enum]
pub fn ty_enum(&mut self, item: &Item) -> Result<Def, &'static str> {
let Item { kind: ItemKind::Enum(Enum { name, kind }), vis, attrs, .. } = item else {
Err("Enum called on item which was not ItemKind::Enum")?
};
let kind = match kind {
EnumKind::NoVariants => DefKind::Type(TypeKind::Adt(Adt::FieldlessEnum)),
EnumKind::Variants(_) => DefKind::Type(TypeKind::Undecided),
};
Ok(Def {
name: name.0.clone(),
vis: *vis,
meta: attrs.meta.clone(),
kind,
source: Some(item.clone()),
module: Default::default(),
})
}
/// Collects an [Item] of type [ItemKind::Alias]
pub fn ty_alias(&mut self, item: &Item) -> Result<Def, &'static str> {
let Item { kind: ItemKind::Alias(Alias { to: name, from }), vis, attrs, .. } = item
else {
Err("Alias called on Item which was not ItemKind::Alias")?
};
let mut kind = match from {
Some(_) => DefKind::Type(TypeKind::Undecided),
None => DefKind::Type(TypeKind::Alias(None)),
};
for meta in &attrs.meta {
let Meta { name: meta_name, kind: meta_kind } = meta;
match (meta_name.0.as_str(), meta_kind) {
("intrinsic", MetaKind::Equals(Literal::String(intrinsic))) => {
kind = DefKind::Type(TypeKind::Intrinsic(
intrinsic.parse().map_err(|_| "unknown intrinsic type")?,
));
}
("intrinsic", MetaKind::Plain) => {
kind = DefKind::Type(TypeKind::Intrinsic(
name.0.parse().map_err(|_| "Unknown intrinsic type")?,
))
}
_ => {}
}
}
Ok(Def {
name: name.0.clone(),
vis: *vis,
meta: attrs.meta.clone(),
kind,
source: Some(item.clone()),
module: Default::default(),
})
}
/// Collects an [Item] of type [ItemKind::Struct]
pub fn ty_struct(&mut self, item: &Item) -> Result<Def, &'static str> {
let Item { kind: ItemKind::Struct(Struct { name, kind }), vis, attrs, .. } = item
else {
Err("Struct called on item which was not ItemKind::Struct")?
};
let kind = match kind {
StructKind::Empty => DefKind::Type(TypeKind::Adt(Adt::UnitStruct)),
StructKind::Tuple(_) => DefKind::Type(TypeKind::Undecided),
StructKind::Struct(_) => DefKind::Type(TypeKind::Undecided),
};
Ok(Def {
name: name.0.clone(),
vis: *vis,
meta: attrs.meta.clone(),
kind,
source: Some(item.clone()),
module: Default::default(),
})
}
}
/// Value collection
impl NameCollector<'_> {
pub fn val_const(&mut self, item: &Item) -> Result<Def, &'static str> {
let Item { kind: ItemKind::Const(Const { name, .. }), vis, attrs, .. } = item else {
Err("Const called on Item which was not ItemKind::Const")?
};
Ok(Def {
name: name.0.clone(),
vis: *vis,
meta: attrs.meta.clone(),
kind: DefKind::Value(ValueKind::Undecided),
source: Some(item.clone()),
module: Default::default(),
})
}
pub fn val_static(&mut self, item: &Item) -> Result<Def, &'static str> {
let Item { kind: ItemKind::Static(Static { name, .. }), vis, attrs, .. } = item else {
Err("Static called on Item which was not ItemKind::Static")?
};
Ok(Def {
name: name.0.clone(),
vis: *vis,
meta: attrs.meta.clone(),
kind: DefKind::Type(TypeKind::Undecided),
source: Some(item.clone()),
module: Default::default(),
})
}
pub fn val_function(&mut self, item: &Item) -> Result<Def, &'static str> {
// TODO: treat function bodies like modules with internal items
let Item { kind: ItemKind::Function(Function { name, .. }), vis, attrs, .. } = item
else {
Err("val_function called on Item which was not ItemKind::Function")?
};
Ok(Def {
name: name.0.clone(),
vis: *vis,
meta: attrs.meta.clone(),
kind: DefKind::Value(ValueKind::Undecided),
source: Some(item.clone()),
module: Default::default(),
})
}
}
}
pub mod type_resolver {
//! Performs step 2 of type checking: Evaluating type definitions
#![allow(unused)]
use std::ops::{Deref, DerefMut};
use cl_ast::*;
use crate::{definition::Def, key::DefID, project::Project};
pub struct TypeResolver<'prj> {
pub project: &'prj mut Project,
}
impl Deref for TypeResolver<'_> {
type Target = Project;
fn deref(&self) -> &Self::Target {
self.project
}
}
impl DerefMut for TypeResolver<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.project
}
}
impl TypeResolver<'_> {
pub fn resolve(&mut self) -> Result<bool, &str> {
#![allow(unused)]
for typedef in self.pool.iter_mut().filter(|v| v.kind.is_type()) {
let Def { name, vis, meta: attr, kind, source: Some(ref definition), module: _ } =
typedef
else {
continue;
};
match &definition.kind {
ItemKind::Alias(Alias { to: _, from: Some(from) }) => match &from.kind {
TyKind::Never => todo!(),
TyKind::Empty => todo!(),
TyKind::SelfTy => todo!(),
TyKind::Path(_) => todo!(),
TyKind::Tuple(_) => todo!(),
TyKind::Ref(_) => todo!(),
TyKind::Fn(_) => todo!(),
},
ItemKind::Alias(_) => {}
ItemKind::Const(_) => todo!(),
ItemKind::Static(_) => todo!(),
ItemKind::Module(_) => todo!(),
ItemKind::Function(_) => {}
ItemKind::Struct(_) => {}
ItemKind::Enum(_) => {}
ItemKind::Impl(_) => {}
}
}
Ok(true)
}
pub fn get_type(&self, kind: &TyKind) -> Option<DefID> {
match kind {
TyKind::Never => todo!(),
TyKind::Empty => todo!(),
TyKind::SelfTy => todo!(),
TyKind::Path(_) => todo!(),
TyKind::Tuple(_) => todo!(),
TyKind::Ref(_) => todo!(),
TyKind::Fn(_) => todo!(),
}
None
}
}
}
pub mod typeref {
//! Stores type and reference info
use crate::key::DefID;
/// The Type struct represents all valid types, and can be trivially equality-compared
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TypeRef {
/// You can only have a pointer chain 65535 pointers long.
ref_depth: u16,
/// Types can be [Generic](RefKind::Generic) or [Concrete](RefKind::Concrete)
kind: RefKind,
}
/// Types can be [Generic](RefKind::Generic) or [Concrete](RefKind::Concrete)
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RefKind {
/// A Concrete type has an associated [Def](super::definition::Def)
Concrete(DefID),
/// A Generic type is a *locally unique* comparable value,
/// valid only until the end of its typing context.
/// This is usually the surrounding function.
Generic(usize),
}
}
@@ -204,7 +852,58 @@ let rules: Hashmap<Operation, Vec<Rule>> {
*/
/*
Potential solution:
Store reference to type field of each type expression in the AST
*/
pub mod rule {
use crate::{key::DefID, typeref::TypeRef};
pub struct Rule {
/// What is this Rule for?
pub operation: (),
/// What inputs does it take?
pub inputs: Vec<TypeRef>,
/// What output does it produce?
pub output: TypeRef,
/// Where did this rule come from?
pub through: Origin,
}
// TODO: Genericize
pub enum Operation {
Mul,
Div,
Rem,
Add,
Sub,
Deref,
Neg,
Not,
At,
Tilde,
Index,
If,
While,
For,
}
pub enum Origin {
/// This rule is built into the compiler
Intrinsic,
/// This rule is derived from an implementation on a type
Extrinsic(DefID),
}
}
pub mod typeck {
#![allow(unused)]
use cl_ast::*;
pub struct Context {
rules: (),
}
trait TypeCheck {}
}
//

View File

@@ -1,38 +1,38 @@
(* Conlang Expression Grammar *)
Start = File ;
Start = File EOI ;
Mutability = "mut"? ;
Visibility = "pub"? ;
File = Item* EOI ;
File = Item* ;
Attrs = ('#' '[' (Meta ',') Meta? ']')* ;
Attrs = ('#' '[' (Meta ',')* Meta? ']')* ;
Meta = Identifier ('=' Literal | '(' (Literal ',')* Literal? ')')? ;
Item = Attrs* Visibility ItemKind ;
Item = Attrs Visibility ItemKind ;
ItemKind = Const | Static | Module
| Function | Struct | Enum
| Alias | Impl ;
(* item *)
Const = "const" Identifier ':' Type = Expr ';' ;
Const = "const" Identifier ':' Ty '=' Expr ';' ;
Static = "static" Mutability Identifier ':' Type = Expr ';' ;
Static = "static" Mutability Identifier ':' Ty '=' Expr ';' ;
Module = "mod" Identifier ModuleKind ;
ModuleKind = '{' Item* '}' | ';' ;
Function = "fn" Identifier '(' (Param ',')* Param? ')' ('->' Type)? Block? ;
Param = Mutability Identifier ':' Type ;
Function = "fn" Identifier '(' (Param ',')* Param? ')' ('->' Ty)? Block? ;
Param = Mutability Identifier ':' Ty ;
Struct = "struct" Identifier (StructTuple | StructBody)?;
StructBody = '{' (StructMember ',')* StructMember? '}' ;
StructTuple = TyTuple ;
StructMember = Visibility Identifier ':' Type ;
StructMember = Visibility Identifier ':' Ty ;
Enum = "enum" Identifier '{' (Variant ',')* Variant? '}' ;
Variant = Identifier (VarStruct | VarTuple | VarCLike)? ;
@@ -40,7 +40,7 @@ VarStruct = '{' (StructMember ',')* StructMember? '}' ;
VarTuple = TyTuple ;
VarCLike = '=' INTEGER ;
Alias = "type" Ty ('=' Ty)? ';' ;
Alias = "type" Identifier ('=' Ty)? ';' ;
Impl = "impl" Path '{' Item* '}' ;
(* TODO: Impl Trait for Target*)
@@ -51,8 +51,9 @@ Ty = Never | Empty | Path | TyTuple | TyRef | TyFn ;
Never = '!' ;
Empty = '(' ')' ;
TyTuple = '(' (Ty ',')* Ty? ')' ;
TyRef = ('&' | '&&')* Path ;
TyFn = "fn" TyTuple (-> Ty)? ;
TyRef = Amps* Path ;
Amps = '&' | '&&' ;
TyFn = "fn" TyTuple ('->' Ty)? ;
(* path *)
@@ -74,24 +75,19 @@ Bool = "true" | "false" ;
(* expr *)
ExprKind = Assign | Compare | Range | Logic | Bitwise | Shift
| Factor | Term | Unary | Member | Call | Index
| Path | Literal | Array | ArrayRep | AddrOf
| Block | Group
| While | If | For | Break | Return | Continue ;
Expr = Assign ;
Assign = Path (AssignKind Assign ) | Compare ;
Binary = Compare | Range | Logic | Bitwise | Shift | Factor | Term ;
(* Binary = Compare | Range | Logic | Bitwise | Shift | Factor | Term ; *)
Compare = Range (CompareOp Range )* ;
Range = Logic (RangeOp Logic )* ;
Logic = Bitwise (LogicOp Bitwise)* ;
Bitwise = Shift (BitwiseOp Shift )* ;
Shift = Factor (ShiftOp Factor )* ;
Factor = Term (FactorOp Term )* ;
Term = Unary (FactorOp Unary )* ;
Term = Unary (TermOp Unary )* ;
Unary = (UnaryKind)* Member ;
@@ -111,13 +107,12 @@ Literal = STRING | CHARACTER | FLOAT | INTEGER | Bool ;
Array = '[' (Expr ',')* Expr? ']' ;
ArrayRep = '[' Expr ';' Expr ']' ;
AddrOf = ('&' | '&&')* Mutability? Expr ;
AddrOf = Amps Amps* Mutability Expr ;
Block = '{' Stmt* '}';
Group = '(' (Empty | Expr | Tuple) ')' ;
Group = Empty | '(' (Expr | Tuple) ')' ;
Tuple = (Expr ',')* Expr? ;
Empty = ;
While = "while" Expr Block Else ;
If = "if" Expr Block Else ;
@@ -127,11 +122,8 @@ Break = "break" Expr ;
Return = "return" Expr ;
Continue = "continue" ;
AssignKind = '=' | '+=' | '-=' | '*=' | '/=' |
'&=' | '|=' | '^=' |'<<=' |'>>=' ;
AssignKind = '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^=' |'<<=' |'>>=' ;
BinaryKind = CompareOp | RangeOp | LogicOp | BitwiseOp
| ShiftOp | TermOp | FactorOp ;
CompareOp = '<' | '<=' | '==' | '!=' | '>=' | '>' ;
RangeOp = '..' | '..=' ;
LogicOp = '&&' | '||' | '^^' ;