conlang: Move all cl-libs into the compiler directory

This commit is contained in:
2024-04-19 07:39:23 -05:00
parent 2a62a1c714
commit 90a3818ca0
52 changed files with 10 additions and 10 deletions

View File

@@ -0,0 +1,11 @@
[package]
name = "cl-ast"
repository.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
cl-structures = { path = "../cl-structures" }

543
compiler/cl-ast/src/ast.rs Normal file
View File

@@ -0,0 +1,543 @@
//! # The Abstract Syntax Tree
//! Contains definitions of Conlang AST Nodes.
//!
//! # Notable nodes
//! - [Item] and [ItemKind]: Top-level constructs
//! - [Stmt] and [StmtKind]: Statements
//! - [Expr] and [ExprKind]: Expressions
//! - [Assign], [Binary], and [Unary] expressions
//! - [AssignKind], [BinaryKind], and [UnaryKind] operators
//! - [Ty] and [TyKind]: Type qualifiers
//! - [Path]: Path expressions
use cl_structures::span::*;
/// Whether a binding ([Static] or [Let]) or reference is mutable or not
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Mutability {
#[default]
Not,
Mut,
}
/// Whether an [Item] is visible outside of the current [Module]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Visibility {
#[default]
Private,
Public,
}
// TODO: Capture token?
/// A name
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Identifier(pub String);
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Literal {
Bool(bool),
Char(char),
Int(u128),
String(String),
}
/// A list of [Item]s
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct File {
pub items: Vec<Item>,
}
// Metadata decorators
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Attrs {
pub meta: Vec<Meta>,
}
/// A metadata decorator
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Meta {
pub name: Identifier,
pub kind: MetaKind,
}
/// Information attached to [Meta]data
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum MetaKind {
Plain,
Equals(Literal),
Func(Vec<Literal>),
}
// Items
/// Anything that can appear at the top level of a [File]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Item {
pub extents: Span,
pub attrs: Attrs,
pub vis: Visibility,
pub kind: ItemKind,
}
/// What kind of [Item] is this?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ItemKind {
// TODO: Import declaration ("use") item
// TODO: Trait declaration ("trait") item?
/// A [module](Module)
Module(Module),
/// 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 [function definition](Function)
Function(Function),
/// An [implementation](Impl)
Impl(Impl),
}
/// An alias to another [Ty]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Alias {
pub to: Identifier,
pub from: Option<Box<Ty>>,
}
/// A compile-time constant
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Const {
pub name: Identifier,
pub ty: Box<Ty>,
pub init: Box<Expr>,
}
/// A `static` variable
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Static {
pub mutable: Mutability,
pub name: Identifier,
pub ty: Box<Ty>,
pub init: Box<Expr>,
}
/// An ordered collection of [Items](Item)
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
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, Hash)]
pub enum ModuleKind {
Inline(File),
Outline,
}
/// Code, and the interface to that code
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Function {
pub name: Identifier,
pub sign: TyFn,
pub bind: Vec<Param>,
pub body: Option<Block>,
}
/// A single parameter for a [Function]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Param {
pub mutability: Mutability,
pub name: Identifier,
}
/// A user-defined product type
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
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, Hash)]
pub enum StructKind {
Empty,
Tuple(Vec<Ty>),
Struct(Vec<StructMember>),
}
/// The [Visibility], [Identifier], and [Ty]pe of a single [Struct] member
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct StructMember {
pub vis: Visibility,
pub name: Identifier,
pub ty: Ty,
}
/// A user-defined sum type
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
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, Hash)]
pub enum EnumKind {
/// Represents an enum with no variants
NoVariants,
Variants(Vec<Variant>),
}
/// A single [Enum] variant
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
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, Hash)]
pub enum VariantKind {
Plain,
CLike(u128),
Tuple(Ty),
Struct(Vec<StructMember>),
}
/// Sub-[items](Item) (associated functions, etc.) for a [Ty]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Impl {
pub target: ImplKind,
pub body: File,
}
// TODO: `impl` Trait for <Target> { }
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ImplKind {
Type(Ty),
Trait { impl_trait: Path, for_type: Box<Ty> },
}
/// A type expression
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Ty {
pub extents: Span,
pub kind: TyKind,
}
/// Information about a [Ty]pe expression
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TyKind {
Never,
Empty,
SelfTy,
Path(Path),
Tuple(TyTuple),
Ref(TyRef),
Fn(TyFn),
// TODO: slice, array types
}
/// A tuple of [Ty]pes
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TyTuple {
pub types: Vec<TyKind>,
}
/// A [Ty]pe-reference expression as (number of `&`, [Path])
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TyRef {
pub mutable: Mutability,
pub count: u16,
pub to: Path,
}
/// The args and return value for a function pointer [Ty]pe
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TyFn {
pub args: Box<TyKind>,
pub rety: Option<Box<Ty>>,
}
/// A path to an [Item] in the [Module] tree
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Path {
pub absolute: bool,
pub parts: Vec<PathPart>,
}
/// A single component of a [Path]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PathPart {
SuperKw,
SelfKw,
Ident(Identifier),
}
/// An abstract statement, and associated metadata
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Stmt {
pub extents: Span,
pub kind: StmtKind,
pub semi: Semi,
}
/// Whether the [Stmt] is a [Let], [Item], or [Expr] statement
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum StmtKind {
Empty,
Local(Let),
Item(Box<Item>),
Expr(Box<Expr>),
}
/// Whether or not a [Stmt] is followed by a semicolon
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Semi {
Terminated,
Unterminated,
}
/// A local variable declaration [Stmt]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Let {
pub mutable: Mutability,
pub name: Identifier,
pub ty: Option<Box<Ty>>,
pub init: Option<Box<Expr>>,
}
/// An expression, the beating heart of the language
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Expr {
pub extents: Span,
pub kind: ExprKind,
}
/// Any of the different [Expr]essions
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
pub enum ExprKind {
/// An empty expression: `(` `)`
#[default]
Empty,
/// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+
Assign(Assign),
/// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+
Binary(Binary),
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
Unary(Unary),
/// An Array [Index] expression: a[10, 20, 30]
Index(Index),
/// A [path expression](Path): `::`? [PathPart] (`::` [PathPart])*
Path(Path),
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
Literal(Literal),
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
Array(Array),
/// An Array literal constructed with [repeat syntax](ArrayRep)
/// `[` [Expr] `;` [Literal] `]`
ArrayRep(ArrayRep),
/// An address-of expression: `&` `mut`? [`Expr`]
AddrOf(AddrOf),
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
Block(Block),
/// A [Grouping](Group) expression `(` [`Expr`] `)`
Group(Group),
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
Tuple(Tuple),
/// A [Loop] expression: `loop` [`Block`]
Loop(Loop),
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
While(While),
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
If(If),
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]?
For(For),
/// A [Break] expression: `break` [`Expr`]?
Break(Break),
/// A [Return] expression `return` [`Expr`]?
Return(Return),
/// A continue expression: `continue`
Continue(Continue),
}
/// An [Assign]ment expression: [`Expr`] ([`AssignKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Assign {
pub kind: AssignKind,
pub parts: Box<(ExprKind, ExprKind)>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AssignKind {
/// Standard Assignment with no read-back
Plain,
And,
Or,
Xor,
Shl,
Shr,
Add,
Sub,
Mul,
Div,
Rem,
}
/// A [Binary] expression: [`Expr`] ([`BinaryKind`] [`Expr`])\+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Binary {
pub kind: BinaryKind,
pub parts: Box<(ExprKind, ExprKind)>,
}
/// A [Binary] operator
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum BinaryKind {
Lt,
LtEq,
Equal,
NotEq,
GtEq,
Gt,
RangeExc,
RangeInc,
LogAnd,
LogOr,
LogXor,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
Add,
Sub,
Mul,
Div,
Rem,
Dot,
Call,
}
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Unary {
pub kind: UnaryKind,
pub tail: Box<ExprKind>,
}
/// A [Unary] operator
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum UnaryKind {
Deref,
Neg,
Not,
/// Unused
At,
/// Unused
Tilde,
}
/// A repeated [Index] expression: a[10, 20, 30][40, 50, 60]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Index {
pub head: Box<ExprKind>,
pub indices: Vec<Expr>,
}
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Array {
pub values: Vec<Expr>,
}
/// An Array literal constructed with [repeat syntax](ArrayRep)
/// `[` [Expr] `;` [Literal] `]`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ArrayRep {
pub value: Box<ExprKind>,
pub repeat: Box<ExprKind>,
}
/// An address-of expression: `&` `mut`? [`Expr`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AddrOf {
pub count: usize,
pub mutable: Mutability,
pub expr: Box<ExprKind>,
}
/// A [Block] expression: `{` [`Stmt`]\* [`Expr`]? `}`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Block {
pub stmts: Vec<Stmt>,
}
/// A [Grouping](Group) expression `(` [`Expr`] `)`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Group {
pub expr: Box<ExprKind>,
}
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Tuple {
pub exprs: Vec<Expr>,
}
/// A [Loop] expression: `loop` [`Block`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Loop {
pub body: Box<Expr>,
}
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct While {
pub cond: Box<Expr>,
pub pass: Box<Block>,
pub fail: Else,
}
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct If {
pub cond: Box<Expr>,
pub pass: Box<Block>,
pub fail: Else,
}
/// A [For] expression: `for` Pattern `in` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct For {
pub bind: Identifier, // TODO: Patterns?
pub cond: Box<Expr>,
pub pass: Box<Block>,
pub fail: Else,
}
/// The (optional) `else` clause of a [While], [If], or [For] expression
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Else {
pub body: Option<Box<Expr>>,
}
/// A [Break] expression: `break` [`Expr`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Break {
pub body: Option<Box<Expr>>,
}
/// A [Return] expression `return` [`Expr`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Return {
pub body: Option<Box<Expr>>,
}
/// A continue expression: `continue`
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Continue;

View File

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

View File

@@ -0,0 +1,8 @@
//! Contains an [immutable visitor](Visit) and an [owned folder](Fold) trait,
//! with default implementations across the entire AST
pub mod fold;
pub mod visit;
pub use fold::Fold;
pub use visit::Visit;

View File

@@ -0,0 +1,493 @@
//! A folder (implementer of the [Fold] trait) maps ASTs to ASTs
use crate::ast::*;
use cl_structures::span::Span;
/// Deconstructs the entire AST, and reconstructs it from scratch.
///
/// Each method acts as a customization point.
///
/// There are a set of default implementations for enums
/// under the name [`or_fold_`*](or_fold_expr_kind),
/// provided for ease of use.
///
/// For all other nodes, traversal is *explicit*.
pub trait Fold {
fn fold_span(&mut self, extents: Span) -> Span {
extents
}
fn fold_mutability(&mut self, mutability: Mutability) -> Mutability {
mutability
}
fn fold_visibility(&mut self, visibility: Visibility) -> Visibility {
visibility
}
fn fold_identifier(&mut self, ident: Identifier) -> Identifier {
ident
}
fn fold_literal(&mut self, lit: Literal) -> Literal {
or_fold_literal(self, lit)
}
fn fold_bool(&mut self, b: bool) -> bool {
b
}
fn fold_char(&mut self, c: char) -> char {
c
}
fn fold_int(&mut self, i: u128) -> u128 {
i
}
fn fold_string(&mut self, s: String) -> String {
s
}
fn fold_file(&mut self, f: File) -> File {
let File { items } = f;
File { items: items.into_iter().map(|i| self.fold_item(i)).collect() }
}
fn fold_attrs(&mut self, a: Attrs) -> Attrs {
let Attrs { meta } = a;
Attrs { meta: meta.into_iter().map(|m| self.fold_meta(m)).collect() }
}
fn fold_meta(&mut self, m: Meta) -> Meta {
let Meta { name, kind } = m;
Meta { name: self.fold_identifier(name), kind: self.fold_meta_kind(kind) }
}
fn fold_meta_kind(&mut self, kind: MetaKind) -> MetaKind {
or_fold_meta_kind(self, kind)
}
fn fold_item(&mut self, i: Item) -> Item {
let Item { extents, attrs, vis, kind } = i;
Item {
extents: self.fold_span(extents),
attrs: self.fold_attrs(attrs),
vis: self.fold_visibility(vis),
kind: self.fold_item_kind(kind),
}
}
fn fold_item_kind(&mut self, kind: ItemKind) -> ItemKind {
or_fold_item_kind(self, kind)
}
fn fold_alias(&mut self, a: Alias) -> Alias {
let Alias { to, from } = a;
Alias { to: self.fold_identifier(to), from: from.map(|from| Box::new(self.fold_ty(*from))) }
}
fn fold_const(&mut self, c: Const) -> Const {
let Const { name, ty, init } = c;
Const {
name: self.fold_identifier(name),
ty: Box::new(self.fold_ty(*ty)),
init: Box::new(self.fold_expr(*init)),
}
}
fn fold_static(&mut self, s: Static) -> Static {
let Static { mutable, name, ty, init } = s;
Static {
mutable: self.fold_mutability(mutable),
name: self.fold_identifier(name),
ty: Box::new(self.fold_ty(*ty)),
init: Box::new(self.fold_expr(*init)),
}
}
fn fold_module(&mut self, m: Module) -> Module {
let Module { name, kind } = m;
Module { name: self.fold_identifier(name), kind: self.fold_module_kind(kind) }
}
fn fold_module_kind(&mut self, m: ModuleKind) -> ModuleKind {
match m {
ModuleKind::Inline(f) => ModuleKind::Inline(self.fold_file(f)),
ModuleKind::Outline => ModuleKind::Outline,
}
}
fn fold_function(&mut self, f: Function) -> Function {
let Function { name, sign, bind, body } = f;
Function {
name: self.fold_identifier(name),
sign: self.fold_ty_fn(sign),
bind: bind.into_iter().map(|p| self.fold_param(p)).collect(),
body: body.map(|b| self.fold_block(b)),
}
}
fn fold_param(&mut self, p: Param) -> Param {
let Param { mutability, name } = p;
Param { mutability: self.fold_mutability(mutability), name: self.fold_identifier(name) }
}
fn fold_struct(&mut self, s: Struct) -> Struct {
let Struct { name, kind } = s;
Struct { name: self.fold_identifier(name), kind: self.fold_struct_kind(kind) }
}
fn fold_struct_kind(&mut self, kind: StructKind) -> StructKind {
match kind {
StructKind::Empty => StructKind::Empty,
StructKind::Tuple(tys) => {
StructKind::Tuple(tys.into_iter().map(|t| self.fold_ty(t)).collect())
}
StructKind::Struct(mem) => StructKind::Struct(
mem.into_iter()
.map(|m| self.fold_struct_member(m))
.collect(),
),
}
}
fn fold_struct_member(&mut self, m: StructMember) -> StructMember {
let StructMember { vis, name, ty } = m;
StructMember {
vis: self.fold_visibility(vis),
name: self.fold_identifier(name),
ty: self.fold_ty(ty),
}
}
fn fold_enum(&mut self, e: Enum) -> Enum {
let Enum { name, kind } = e;
Enum { name: self.fold_identifier(name), kind: self.fold_enum_kind(kind) }
}
fn fold_enum_kind(&mut self, kind: EnumKind) -> EnumKind {
or_fold_enum_kind(self, kind)
}
fn fold_variant(&mut self, v: Variant) -> Variant {
let Variant { name, kind } = v;
Variant { name: self.fold_identifier(name), kind: self.fold_variant_kind(kind) }
}
fn fold_variant_kind(&mut self, kind: VariantKind) -> VariantKind {
or_fold_variant_kind(self, kind)
}
fn fold_impl(&mut self, i: Impl) -> Impl {
let Impl { target, body } = i;
Impl { target: self.fold_impl_kind(target), body: self.fold_file(body) }
}
fn fold_impl_kind(&mut self, kind: ImplKind) -> ImplKind {
or_fold_impl_kind(self, kind)
}
fn fold_ty(&mut self, t: Ty) -> Ty {
let Ty { extents, kind } = t;
Ty { extents: self.fold_span(extents), kind: self.fold_ty_kind(kind) }
}
fn fold_ty_kind(&mut self, kind: TyKind) -> TyKind {
or_fold_ty_kind(self, kind)
}
fn fold_ty_tuple(&mut self, t: TyTuple) -> TyTuple {
let TyTuple { types } = t;
TyTuple {
types: types
.into_iter()
.map(|kind| self.fold_ty_kind(kind))
.collect(),
}
}
fn fold_ty_ref(&mut self, t: TyRef) -> TyRef {
let TyRef { mutable, count, to } = t;
TyRef { mutable: self.fold_mutability(mutable), count, to: self.fold_path(to) }
}
fn fold_ty_fn(&mut self, t: TyFn) -> TyFn {
let TyFn { args, rety } = t;
TyFn {
args: Box::new(self.fold_ty_kind(*args)),
rety: rety.map(|t| Box::new(self.fold_ty(*t))),
}
}
fn fold_path(&mut self, p: Path) -> Path {
let Path { absolute, parts } = p;
Path { absolute, parts: parts.into_iter().map(|p| self.fold_path_part(p)).collect() }
}
fn fold_path_part(&mut self, p: PathPart) -> PathPart {
match p {
PathPart::SuperKw => PathPart::SuperKw,
PathPart::SelfKw => PathPart::SelfKw,
PathPart::Ident(i) => PathPart::Ident(self.fold_identifier(i)),
}
}
fn fold_stmt(&mut self, s: Stmt) -> Stmt {
let Stmt { extents, kind, semi } = s;
Stmt {
extents: self.fold_span(extents),
kind: self.fold_stmt_kind(kind),
semi: self.fold_semi(semi),
}
}
fn fold_stmt_kind(&mut self, kind: StmtKind) -> StmtKind {
or_fold_stmt_kind(self, kind)
}
fn fold_semi(&mut self, s: Semi) -> Semi {
s
}
fn fold_let(&mut self, l: Let) -> Let {
let Let { mutable, name, ty, init } = l;
Let {
mutable: self.fold_mutability(mutable),
name: self.fold_identifier(name),
ty: ty.map(|t| Box::new(self.fold_ty(*t))),
init: init.map(|e| Box::new(self.fold_expr(*e))),
}
}
fn fold_expr(&mut self, e: Expr) -> Expr {
let Expr { extents, kind } = e;
Expr { extents: self.fold_span(extents), kind: self.fold_expr_kind(kind) }
}
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
or_fold_expr_kind(self, kind)
}
fn fold_assign(&mut self, a: Assign) -> Assign {
let Assign { kind, parts } = a;
let (head, tail) = *parts;
Assign {
kind: self.fold_assign_kind(kind),
parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))),
}
}
fn fold_assign_kind(&mut self, kind: AssignKind) -> AssignKind {
kind
}
fn fold_binary(&mut self, b: Binary) -> Binary {
let Binary { kind, parts } = b;
let (head, tail) = *parts;
Binary {
kind: self.fold_binary_kind(kind),
parts: Box::new((self.fold_expr_kind(head), self.fold_expr_kind(tail))),
}
}
fn fold_binary_kind(&mut self, kind: BinaryKind) -> BinaryKind {
kind
}
fn fold_unary(&mut self, u: Unary) -> Unary {
let Unary { kind, tail } = u;
Unary { kind: self.fold_unary_kind(kind), tail: Box::new(self.fold_expr_kind(*tail)) }
}
fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind {
kind
}
fn fold_index(&mut self, i: Index) -> Index {
let Index { head, indices } = i;
Index {
head: Box::new(self.fold_expr_kind(*head)),
indices: indices.into_iter().map(|e| self.fold_expr(e)).collect(),
}
}
fn fold_array(&mut self, a: Array) -> Array {
let Array { values } = a;
Array { values: values.into_iter().map(|e| self.fold_expr(e)).collect() }
}
fn fold_array_rep(&mut self, a: ArrayRep) -> ArrayRep {
let ArrayRep { value, repeat } = a;
ArrayRep {
value: Box::new(self.fold_expr_kind(*value)),
repeat: Box::new(self.fold_expr_kind(*repeat)),
}
}
fn fold_addrof(&mut self, a: AddrOf) -> AddrOf {
let AddrOf { count, mutable, expr } = a;
AddrOf {
count,
mutable: self.fold_mutability(mutable),
expr: Box::new(self.fold_expr_kind(*expr)),
}
}
fn fold_block(&mut self, b: Block) -> Block {
let Block { stmts } = b;
Block { stmts: stmts.into_iter().map(|s| self.fold_stmt(s)).collect() }
}
fn fold_group(&mut self, g: Group) -> Group {
let Group { expr } = g;
Group { expr: Box::new(self.fold_expr_kind(*expr)) }
}
fn fold_tuple(&mut self, t: Tuple) -> Tuple {
let Tuple { exprs } = t;
Tuple { exprs: exprs.into_iter().map(|e| self.fold_expr(e)).collect() }
}
fn fold_loop(&mut self, l: Loop) -> Loop {
let Loop { body } = l;
Loop { body: Box::new(self.fold_expr(*body)) }
}
fn fold_while(&mut self, w: While) -> While {
let While { cond, pass, fail } = w;
While {
cond: Box::new(self.fold_expr(*cond)),
pass: Box::new(self.fold_block(*pass)),
fail: self.fold_else(fail),
}
}
fn fold_if(&mut self, i: If) -> If {
let If { cond, pass, fail } = i;
If {
cond: Box::new(self.fold_expr(*cond)),
pass: Box::new(self.fold_block(*pass)),
fail: self.fold_else(fail),
}
}
fn fold_for(&mut self, f: For) -> For {
let For { bind, cond, pass, fail } = f;
For {
bind: self.fold_identifier(bind),
cond: Box::new(self.fold_expr(*cond)),
pass: Box::new(self.fold_block(*pass)),
fail: self.fold_else(fail),
}
}
fn fold_else(&mut self, e: Else) -> Else {
let Else { body } = e;
Else { body: body.map(|e| Box::new(self.fold_expr(*e))) }
}
fn fold_break(&mut self, b: Break) -> Break {
let Break { body } = b;
Break { body: body.map(|e| Box::new(self.fold_expr(*e))) }
}
fn fold_return(&mut self, r: Return) -> Return {
let Return { body } = r;
Return { body: body.map(|e| Box::new(self.fold_expr(*e))) }
}
fn fold_continue(&mut self, c: Continue) -> Continue {
let Continue = c;
Continue
}
}
#[inline]
/// Folds a [Literal] in the default way
pub fn or_fold_literal<F: Fold + ?Sized>(folder: &mut F, lit: Literal) -> Literal {
match lit {
Literal::Bool(b) => Literal::Bool(folder.fold_bool(b)),
Literal::Char(c) => Literal::Char(folder.fold_char(c)),
Literal::Int(i) => Literal::Int(folder.fold_int(i)),
Literal::String(s) => Literal::String(folder.fold_string(s)),
}
}
#[inline]
/// Folds a [MetaKind] in the default way
pub fn or_fold_meta_kind<F: Fold + ?Sized>(folder: &mut F, kind: MetaKind) -> MetaKind {
match kind {
MetaKind::Plain => MetaKind::Plain,
MetaKind::Equals(l) => MetaKind::Equals(folder.fold_literal(l)),
MetaKind::Func(lits) => {
MetaKind::Func(lits.into_iter().map(|l| folder.fold_literal(l)).collect())
}
}
}
#[inline]
/// Folds an [ItemKind] in the default way
pub fn or_fold_item_kind<F: Fold + ?Sized>(folder: &mut F, kind: ItemKind) -> ItemKind {
match kind {
ItemKind::Module(m) => ItemKind::Module(folder.fold_module(m)),
ItemKind::Alias(a) => ItemKind::Alias(folder.fold_alias(a)),
ItemKind::Enum(e) => ItemKind::Enum(folder.fold_enum(e)),
ItemKind::Struct(s) => ItemKind::Struct(folder.fold_struct(s)),
ItemKind::Const(c) => ItemKind::Const(folder.fold_const(c)),
ItemKind::Static(s) => ItemKind::Static(folder.fold_static(s)),
ItemKind::Function(f) => ItemKind::Function(folder.fold_function(f)),
ItemKind::Impl(i) => ItemKind::Impl(folder.fold_impl(i)),
}
}
#[inline]
/// Folds a [ModuleKind] in the default way
pub fn or_fold_module_kind<F: Fold + ?Sized>(folder: &mut F, kind: ModuleKind) -> ModuleKind {
match kind {
ModuleKind::Inline(f) => ModuleKind::Inline(folder.fold_file(f)),
ModuleKind::Outline => ModuleKind::Outline,
}
}
#[inline]
/// Folds a [StructKind] in the default way
pub fn or_fold_struct_kind<F: Fold + ?Sized>(folder: &mut F, kind: StructKind) -> StructKind {
match kind {
StructKind::Empty => StructKind::Empty,
StructKind::Tuple(tys) => {
StructKind::Tuple(tys.into_iter().map(|t| folder.fold_ty(t)).collect())
}
StructKind::Struct(mem) => StructKind::Struct(
mem.into_iter()
.map(|m| folder.fold_struct_member(m))
.collect(),
),
}
}
#[inline]
/// Folds an [EnumKind] in the default way
pub fn or_fold_enum_kind<F: Fold + ?Sized>(folder: &mut F, kind: EnumKind) -> EnumKind {
match kind {
EnumKind::NoVariants => EnumKind::NoVariants,
EnumKind::Variants(v) => {
EnumKind::Variants(v.into_iter().map(|v| folder.fold_variant(v)).collect())
}
}
}
#[inline]
/// Folds a [VariantKind] in the default way
pub fn or_fold_variant_kind<F: Fold + ?Sized>(folder: &mut F, kind: VariantKind) -> VariantKind {
match kind {
VariantKind::Plain => VariantKind::Plain,
VariantKind::CLike(n) => VariantKind::CLike(n),
VariantKind::Tuple(t) => VariantKind::Tuple(folder.fold_ty(t)),
VariantKind::Struct(mem) => VariantKind::Struct(
mem.into_iter()
.map(|m| folder.fold_struct_member(m))
.collect(),
),
}
}
#[inline]
/// Folds an [ImplKind] in the default way
pub fn or_fold_impl_kind<F: Fold + ?Sized>(folder: &mut F, kind: ImplKind) -> ImplKind {
match kind {
ImplKind::Type(t) => ImplKind::Type(folder.fold_ty(t)),
ImplKind::Trait { impl_trait, for_type } => ImplKind::Trait {
impl_trait: folder.fold_path(impl_trait),
for_type: Box::new(folder.fold_ty(*for_type)),
},
}
}
#[inline]
/// Folds a [TyKind] in the default way
pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind {
match kind {
TyKind::Never => TyKind::Never,
TyKind::Empty => TyKind::Empty,
TyKind::SelfTy => TyKind::SelfTy,
TyKind::Path(p) => TyKind::Path(folder.fold_path(p)),
TyKind::Tuple(t) => TyKind::Tuple(folder.fold_ty_tuple(t)),
TyKind::Ref(t) => TyKind::Ref(folder.fold_ty_ref(t)),
TyKind::Fn(t) => TyKind::Fn(folder.fold_ty_fn(t)),
}
}
#[inline]
/// Folds a [StmtKind] in the default way
pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> StmtKind {
match kind {
StmtKind::Empty => StmtKind::Empty,
StmtKind::Local(l) => StmtKind::Local(folder.fold_let(l)),
StmtKind::Item(i) => StmtKind::Item(Box::new(folder.fold_item(*i))),
StmtKind::Expr(e) => StmtKind::Expr(Box::new(folder.fold_expr(*e))),
}
}
#[inline]
/// Folds an [ExprKind] in the default way
pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> ExprKind {
match kind {
ExprKind::Empty => ExprKind::Empty,
ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)),
ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)),
ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)),
ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)),
ExprKind::Path(p) => ExprKind::Path(folder.fold_path(p)),
ExprKind::Literal(l) => ExprKind::Literal(folder.fold_literal(l)),
ExprKind::Array(a) => ExprKind::Array(folder.fold_array(a)),
ExprKind::ArrayRep(a) => ExprKind::ArrayRep(folder.fold_array_rep(a)),
ExprKind::AddrOf(a) => ExprKind::AddrOf(folder.fold_addrof(a)),
ExprKind::Block(b) => ExprKind::Block(folder.fold_block(b)),
ExprKind::Group(g) => ExprKind::Group(folder.fold_group(g)),
ExprKind::Tuple(t) => ExprKind::Tuple(folder.fold_tuple(t)),
ExprKind::Loop(l) => ExprKind::Loop(folder.fold_loop(l)),
ExprKind::While(w) => ExprKind::While(folder.fold_while(w)),
ExprKind::If(i) => ExprKind::If(folder.fold_if(i)),
ExprKind::For(f) => ExprKind::For(folder.fold_for(f)),
ExprKind::Break(b) => ExprKind::Break(folder.fold_break(b)),
ExprKind::Return(r) => ExprKind::Return(folder.fold_return(r)),
ExprKind::Continue(c) => ExprKind::Continue(folder.fold_continue(c)),
}
}

View File

@@ -0,0 +1,411 @@
//! A [visitor](Visit) (implementer of the [Visit] trait) walks the immutable AST, mutating itself.
use crate::ast::*;
use cl_structures::span::Span;
/// Immutably walks the entire AST
///
/// Each method acts as a customization point.
///
/// There are a set of default implementations for enums
/// under the name [`or_visit_`*](or_visit_expr_kind),
/// provided for ease of use.
///
/// For all other nodes, traversal is *explicit*.
pub trait Visit<'a>: Sized {
fn visit_span(&mut self, _extents: &'a Span) {}
fn visit_mutability(&mut self, _mutable: &'a Mutability) {}
fn visit_visibility(&mut self, _vis: &'a Visibility) {}
fn visit_identifier(&mut self, _name: &'a Identifier) {}
fn visit_literal(&mut self, l: &'a Literal) {
or_visit_literal(self, l)
}
fn visit_bool(&mut self, _b: &'a bool) {}
fn visit_char(&mut self, _c: &'a char) {}
fn visit_int(&mut self, _i: &'a u128) {}
fn visit_string(&mut self, _s: &'a str) {}
fn visit_file(&mut self, f: &'a File) {
let File { items } = f;
items.iter().for_each(|i| self.visit_item(i));
}
fn visit_attrs(&mut self, a: &'a Attrs) {
let Attrs { meta } = a;
meta.iter().for_each(|m| self.visit_meta(m));
}
fn visit_meta(&mut self, m: &'a Meta) {
let Meta { name, kind } = m;
self.visit_identifier(name);
self.visit_meta_kind(kind);
}
fn visit_meta_kind(&mut self, kind: &'a MetaKind) {
or_visit_meta_kind(self, kind)
}
fn visit_item(&mut self, i: &'a Item) {
let Item { extents, attrs, vis, kind } = i;
self.visit_span(extents);
self.visit_attrs(attrs);
self.visit_visibility(vis);
self.visit_item_kind(kind);
}
fn visit_item_kind(&mut self, kind: &'a ItemKind) {
or_visit_item_kind(self, kind)
}
fn visit_alias(&mut self, a: &'a Alias) {
let Alias { to, from } = a;
self.visit_identifier(to);
if let Some(t) = from {
self.visit_ty(t)
}
}
fn visit_const(&mut self, c: &'a Const) {
let Const { name, ty, init } = c;
self.visit_identifier(name);
self.visit_ty(ty);
self.visit_expr(init);
}
fn visit_static(&mut self, s: &'a Static) {
let Static { mutable, name, ty, init } = s;
self.visit_mutability(mutable);
self.visit_identifier(name);
self.visit_ty(ty);
self.visit_expr(init);
}
fn visit_module(&mut self, m: &'a Module) {
let Module { name, kind } = m;
self.visit_identifier(name);
self.visit_module_kind(kind);
}
fn visit_module_kind(&mut self, kind: &'a ModuleKind) {
or_visit_module_kind(self, kind)
}
fn visit_function(&mut self, f: &'a Function) {
let Function { name, sign, bind, body } = f;
self.visit_identifier(name);
self.visit_ty_fn(sign);
bind.iter().for_each(|p| self.visit_param(p));
if let Some(b) = body {
self.visit_block(b)
}
}
fn visit_param(&mut self, p: &'a Param) {
let Param { mutability, name } = p;
self.visit_mutability(mutability);
self.visit_identifier(name);
}
fn visit_struct(&mut self, s: &'a Struct) {
let Struct { name, kind } = s;
self.visit_identifier(name);
self.visit_struct_kind(kind);
}
fn visit_struct_kind(&mut self, kind: &'a StructKind) {
or_visit_struct_kind(self, kind)
}
fn visit_struct_member(&mut self, m: &'a StructMember) {
let StructMember { vis, name, ty } = m;
self.visit_visibility(vis);
self.visit_identifier(name);
self.visit_ty(ty);
}
fn visit_enum(&mut self, e: &'a Enum) {
let Enum { name, kind } = e;
self.visit_identifier(name);
self.visit_enum_kind(kind);
}
fn visit_enum_kind(&mut self, kind: &'a EnumKind) {
or_visit_enum_kind(self, kind)
}
fn visit_variant(&mut self, v: &'a Variant) {
let Variant { name, kind } = v;
self.visit_identifier(name);
self.visit_variant_kind(kind);
}
fn visit_variant_kind(&mut self, kind: &'a VariantKind) {
or_visit_variant_kind(self, kind)
}
fn visit_impl(&mut self, i: &'a Impl) {
let Impl { target, body } = i;
self.visit_impl_kind(target);
self.visit_file(body)
}
fn visit_impl_kind(&mut self, target: &'a ImplKind) {
or_visit_impl_kind(self, target)
}
fn visit_ty(&mut self, t: &'a Ty) {
let Ty { extents, kind } = t;
self.visit_span(extents);
self.visit_ty_kind(kind);
}
fn visit_ty_kind(&mut self, kind: &'a TyKind) {
or_visit_ty_kind(self, kind)
}
fn visit_ty_tuple(&mut self, t: &'a TyTuple) {
let TyTuple { types } = t;
types.iter().for_each(|kind| self.visit_ty_kind(kind))
}
fn visit_ty_ref(&mut self, t: &'a TyRef) {
let TyRef { mutable, count: _, to } = t;
self.visit_mutability(mutable);
self.visit_path(to);
}
fn visit_ty_fn(&mut self, t: &'a TyFn) {
let TyFn { args, rety } = t;
self.visit_ty_kind(args);
if let Some(rety) = rety {
self.visit_ty(rety);
}
}
fn visit_path(&mut self, p: &'a Path) {
let Path { absolute: _, parts } = p;
parts.iter().for_each(|p| self.visit_path_part(p))
}
fn visit_path_part(&mut self, p: &'a PathPart) {
match p {
PathPart::SuperKw => {}
PathPart::SelfKw => {}
PathPart::Ident(i) => self.visit_identifier(i),
}
}
fn visit_stmt(&mut self, s: &'a Stmt) {
let Stmt { extents, kind, semi } = s;
self.visit_span(extents);
self.visit_stmt_kind(kind);
self.visit_semi(semi);
}
fn visit_stmt_kind(&mut self, kind: &'a StmtKind) {
or_visit_stmt_kind(self, kind)
}
fn visit_semi(&mut self, _s: &'a Semi) {}
fn visit_let(&mut self, l: &'a Let) {
let Let { mutable, name, ty, init } = l;
self.visit_mutability(mutable);
self.visit_identifier(name);
if let Some(ty) = ty {
self.visit_ty(ty);
}
if let Some(init) = init {
self.visit_expr(init)
}
}
fn visit_expr(&mut self, e: &'a Expr) {
let Expr { extents, kind } = e;
self.visit_span(extents);
self.visit_expr_kind(kind)
}
fn visit_expr_kind(&mut self, e: &'a ExprKind) {
or_visit_expr_kind(self, e)
}
fn visit_assign(&mut self, a: &'a Assign) {
let Assign { kind, parts } = a;
let (head, tail) = parts.as_ref();
self.visit_assign_kind(kind);
self.visit_expr_kind(head);
self.visit_expr_kind(tail);
}
fn visit_assign_kind(&mut self, _kind: &'a AssignKind) {}
fn visit_binary(&mut self, b: &'a Binary) {
let Binary { kind, parts } = b;
let (head, tail) = parts.as_ref();
self.visit_binary_kind(kind);
self.visit_expr_kind(head);
self.visit_expr_kind(tail);
}
fn visit_binary_kind(&mut self, _kind: &'a BinaryKind) {}
fn visit_unary(&mut self, u: &'a Unary) {
let Unary { kind, tail } = u;
self.visit_unary_kind(kind);
self.visit_expr_kind(tail);
}
fn visit_unary_kind(&mut self, _kind: &'a UnaryKind) {}
fn visit_index(&mut self, i: &'a Index) {
let Index { head, indices } = i;
self.visit_expr_kind(head);
indices.iter().for_each(|e| self.visit_expr(e));
}
fn visit_array(&mut self, a: &'a Array) {
let Array { values } = a;
values.iter().for_each(|e| self.visit_expr(e))
}
fn visit_array_rep(&mut self, a: &'a ArrayRep) {
let ArrayRep { value, repeat } = a;
self.visit_expr_kind(value);
self.visit_expr_kind(repeat);
}
fn visit_addrof(&mut self, a: &'a AddrOf) {
let AddrOf { count: _, mutable, expr } = a;
self.visit_mutability(mutable);
self.visit_expr_kind(expr);
}
fn visit_block(&mut self, b: &'a Block) {
let Block { stmts } = b;
stmts.iter().for_each(|s| self.visit_stmt(s));
}
fn visit_group(&mut self, g: &'a Group) {
let Group { expr } = g;
self.visit_expr_kind(expr)
}
fn visit_tuple(&mut self, t: &'a Tuple) {
let Tuple { exprs } = t;
exprs.iter().for_each(|e| self.visit_expr(e))
}
fn visit_loop(&mut self, l: &'a Loop) {
let Loop { body } = l;
self.visit_expr(body)
}
fn visit_while(&mut self, w: &'a While) {
let While { cond, pass, fail } = w;
self.visit_expr(cond);
self.visit_block(pass);
self.visit_else(fail);
}
fn visit_if(&mut self, i: &'a If) {
let If { cond, pass, fail } = i;
self.visit_expr(cond);
self.visit_block(pass);
self.visit_else(fail);
}
fn visit_for(&mut self, f: &'a For) {
let For { bind, cond, pass, fail } = f;
self.visit_identifier(bind);
self.visit_expr(cond);
self.visit_block(pass);
self.visit_else(fail);
}
fn visit_else(&mut self, e: &'a Else) {
let Else { body } = e;
if let Some(body) = body {
self.visit_expr(body)
}
}
fn visit_break(&mut self, b: &'a Break) {
let Break { body } = b;
if let Some(body) = body {
self.visit_expr(body)
}
}
fn visit_return(&mut self, r: &'a Return) {
let Return { body } = r;
if let Some(body) = body {
self.visit_expr(body)
}
}
fn visit_continue(&mut self, c: &'a Continue) {
let Continue = c;
}
}
pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) {
match l {
Literal::Bool(b) => visitor.visit_bool(b),
Literal::Char(c) => visitor.visit_char(c),
Literal::Int(i) => visitor.visit_int(i),
Literal::String(s) => visitor.visit_string(s),
}
}
pub fn or_visit_meta_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a MetaKind) {
match kind {
MetaKind::Plain => {}
MetaKind::Equals(l) => visitor.visit_literal(l),
MetaKind::Func(lits) => lits.iter().for_each(|l| visitor.visit_literal(l)),
}
}
pub fn or_visit_item_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a ItemKind) {
match kind {
ItemKind::Module(m) => visitor.visit_module(m),
ItemKind::Alias(a) => visitor.visit_alias(a),
ItemKind::Enum(e) => visitor.visit_enum(e),
ItemKind::Struct(s) => visitor.visit_struct(s),
ItemKind::Const(c) => visitor.visit_const(c),
ItemKind::Static(s) => visitor.visit_static(s),
ItemKind::Function(f) => visitor.visit_function(f),
ItemKind::Impl(i) => visitor.visit_impl(i),
}
}
pub fn or_visit_module_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a ModuleKind) {
match kind {
ModuleKind::Inline(f) => visitor.visit_file(f),
ModuleKind::Outline => {}
}
}
pub fn or_visit_struct_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StructKind) {
match kind {
StructKind::Empty => {}
StructKind::Tuple(ty) => ty.iter().for_each(|t| visitor.visit_ty(t)),
StructKind::Struct(m) => m.iter().for_each(|m| visitor.visit_struct_member(m)),
}
}
pub fn or_visit_enum_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a EnumKind) {
match kind {
EnumKind::NoVariants => {}
EnumKind::Variants(variants) => variants.iter().for_each(|v| visitor.visit_variant(v)),
}
}
pub fn or_visit_variant_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a VariantKind) {
match kind {
VariantKind::Plain => {}
VariantKind::CLike(_) => {}
VariantKind::Tuple(t) => visitor.visit_ty(t),
VariantKind::Struct(m) => m.iter().for_each(|m| visitor.visit_struct_member(m)),
}
}
pub fn or_visit_impl_kind<'a, V: Visit<'a>>(visitor: &mut V, target: &'a ImplKind) {
match target {
ImplKind::Type(t) => visitor.visit_ty(t),
ImplKind::Trait { impl_trait, for_type } => {
visitor.visit_path(impl_trait);
visitor.visit_ty(for_type)
}
}
}
pub fn or_visit_ty_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a TyKind) {
match kind {
TyKind::Never => {}
TyKind::Empty => {}
TyKind::SelfTy => {}
TyKind::Path(p) => visitor.visit_path(p),
TyKind::Tuple(t) => visitor.visit_ty_tuple(t),
TyKind::Ref(t) => visitor.visit_ty_ref(t),
TyKind::Fn(t) => visitor.visit_ty_fn(t),
}
}
pub fn or_visit_stmt_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StmtKind) {
match kind {
StmtKind::Empty => {}
StmtKind::Local(l) => visitor.visit_let(l),
StmtKind::Item(i) => visitor.visit_item(i),
StmtKind::Expr(e) => visitor.visit_expr(e),
}
}
pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) {
match e {
ExprKind::Empty => {}
ExprKind::Assign(a) => visitor.visit_assign(a),
ExprKind::Binary(b) => visitor.visit_binary(b),
ExprKind::Unary(u) => visitor.visit_unary(u),
ExprKind::Index(i) => visitor.visit_index(i),
ExprKind::Path(p) => visitor.visit_path(p),
ExprKind::Literal(l) => visitor.visit_literal(l),
ExprKind::Array(a) => visitor.visit_array(a),
ExprKind::ArrayRep(a) => visitor.visit_array_rep(a),
ExprKind::AddrOf(a) => visitor.visit_addrof(a),
ExprKind::Block(b) => visitor.visit_block(b),
ExprKind::Group(g) => visitor.visit_group(g),
ExprKind::Tuple(t) => visitor.visit_tuple(t),
ExprKind::Loop(l) => visitor.visit_loop(l),
ExprKind::While(w) => visitor.visit_while(w),
ExprKind::If(i) => visitor.visit_if(i),
ExprKind::For(f) => visitor.visit_for(f),
ExprKind::Break(b) => visitor.visit_break(b),
ExprKind::Return(r) => visitor.visit_return(r),
ExprKind::Continue(c) => visitor.visit_continue(c),
}
}

View File

@@ -0,0 +1,5 @@
//! Desugaring passes for Conlang
pub mod while_else;
pub mod squash_groups;

View File

@@ -0,0 +1,14 @@
//! Squashes group expressions
use crate::{ast::*, ast_visitor::fold::*};
/// Squashes group expressions
pub struct SquashGroups;
impl Fold for SquashGroups {
fn fold_expr_kind(&mut self, kind: ExprKind) -> ExprKind {
match kind {
ExprKind::Group(Group { expr }) => self.fold_expr_kind(*expr),
_ => or_fold_expr_kind(self, kind),
}
}
}

View File

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

View File

@@ -0,0 +1,78 @@
use delimiters::Delimiters;
use std::fmt::Write;
impl<W: Write + ?Sized> FmtAdapter for W {}
pub trait FmtAdapter: Write {
fn indent(&mut self) -> Indent<Self> {
Indent { f: self }
}
fn delimit(&mut self, delim: Delimiters) -> Delimit<Self> {
Delimit::new(self, delim)
}
}
/// Pads text with leading indentation after every newline
pub struct Indent<'f, F: Write + ?Sized> {
f: &'f mut F,
}
impl<'f, F: Write + ?Sized> Write for Indent<'f, F> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
for s in s.split_inclusive('\n') {
self.f.write_str(s)?;
if s.ends_with('\n') {
self.f.write_str(" ")?;
}
}
Ok(())
}
}
/// Prints [Delimiters] around anything formatted with this. Implies [Indent]
pub struct Delimit<'f, F: Write + ?Sized> {
f: Indent<'f, F>,
delim: Delimiters,
}
impl<'f, F: Write + ?Sized> Delimit<'f, F> {
pub fn new(f: &'f mut F, delim: Delimiters) -> Self {
let mut f = f.indent();
let _ = f.write_str(delim.open);
Self { f, delim }
}
}
impl<'f, F: Write + ?Sized> Drop for Delimit<'f, F> {
fn drop(&mut self) {
let Self { f: Indent { f, .. }, delim } = self;
let _ = f.write_str(delim.close);
}
}
impl<'f, F: Write + ?Sized> Write for Delimit<'f, F> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.f.write_str(s)
}
}
pub mod delimiters {
#![allow(dead_code)]
#[derive(Clone, Copy, Debug)]
pub struct Delimiters {
pub open: &'static str,
pub close: &'static 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}`
pub const PARENS: Delimiters = Delimiters { open: "(\n", close: "\n)" };
/// Delimits with square brackets on separate lines `{\n`, ..., `\n}`
pub const SQUARE: Delimiters = Delimiters { open: "[\n", close: "\n]" };
/// Delimits with braces on the same line `{ `, ..., ` }`
pub const INLINE_BRACES: Delimiters = Delimiters { open: "{ ", close: " }" };
/// Delimits with parentheses on the same line `( `, ..., ` )`
pub const INLINE_PARENS: Delimiters = Delimiters { open: "(", close: ")" };
/// Delimits with square brackets on the same line `[ `, ..., ` ]`
pub const INLINE_SQUARE: Delimiters = Delimiters { open: "[", close: "]" };
}

View File

@@ -0,0 +1,21 @@
//! # The Abstract Syntax Tree
//! Contains definitions of Conlang AST Nodes.
//!
//! # Notable nodes
//! - [Item] and [ItemKind]: Top-level constructs
//! - [Stmt] and [StmtKind]: Statements
//! - [Expr] and [ExprKind]: Expressions
//! - [Assign], [Binary], and [Unary] expressions
//! - [AssignKind], [BinaryKind], and [UnaryKind] operators
//! - [Ty] and [TyKind]: Type qualifiers
//! - [Path]: Path expressions
#![warn(clippy::all)]
#![feature(decl_macro)]
pub use ast::*;
pub mod ast;
pub mod ast_impl;
pub mod ast_visitor;
pub mod desugar;
pub mod format;

View File

@@ -0,0 +1,17 @@
[package]
name = "cl-interpret"
repository.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
cl-ast = { path = "../cl-ast" }
cl-structures = { path = "../cl-structures" }
[dev-dependencies]
cl-lexer = { path = "../cl-lexer" }
cl-parser = { path = "../cl-parser" }

View File

@@ -0,0 +1,16 @@
// Calculate Fibonacci numbers
fn main() {
for num in 0..=30 {
println!("fib({num}) = {}", fib(num))
}
}
/// Implements the classic recursive definition of fib()
fn fib(a: i64) -> i64 {
if a > 1 {
fib(a - 1) + fib(a - 2)
} else {
1
}
}

View File

@@ -0,0 +1,239 @@
//! Implementations of built-in functions
use super::{
env::Environment,
error::{Error, IResult},
temp_type_impl::ConValue,
BuiltIn, Callable,
};
use std::io::{stdout, Write};
builtins! {
const MISC;
/// Unstable variadic print function
pub fn print<_, args> () -> IResult<ConValue> {
let mut out = stdout().lock();
for arg in args {
write!(out, "{arg}").ok();
}
writeln!(out).ok();
Ok(ConValue::Empty)
}
/// Prints the [Debug](std::fmt::Debug) version of the input values
pub fn dbg<_, args> () -> IResult<ConValue> {
let mut out = stdout().lock();
for arg in args {
writeln!(out, "{arg:?}").ok();
}
Ok(args.into())
}
/// Dumps info from the environment
pub fn dump<env, _>() -> IResult<ConValue> {
println!("{}", *env);
Ok(ConValue::Empty)
}
}
builtins! {
const BINARY;
/// Multiplication `a * b`
pub fn mul(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
_ => Err(Error::TypeError)?
})
}
/// Division `a / b`
pub fn div(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs){
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
_ => Err(Error::TypeError)?
})
}
/// Remainder `a % b`
pub fn rem(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
_ => Err(Error::TypeError)?,
})
}
/// Addition `a + b`
pub fn add(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
(ConValue::String(a), ConValue::String(b)) => ConValue::String(a.to_string() + b),
_ => Err(Error::TypeError)?
})
}
/// Subtraction `a - b`
pub fn sub(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
_ => Err(Error::TypeError)?,
})
}
/// Shift Left `a << b`
pub fn shl(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
_ => Err(Error::TypeError)?,
})
}
/// Shift Right `a >> b`
pub fn shr(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
_ => Err(Error::TypeError)?,
})
}
/// Bitwise And `a & b`
pub fn and(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError)?,
})
}
/// Bitwise Or `a | b`
pub fn or(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError)?,
})
}
/// Bitwise Exclusive Or `a ^ b`
pub fn xor(lhs, rhs) -> IResult<ConValue> {
Ok(match (lhs, rhs) {
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError)?,
})
}
/// Tests whether `a < b`
pub fn lt(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, <)
}
/// Tests whether `a <= b`
pub fn lt_eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, <=)
}
/// Tests whether `a == b`
pub fn eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, ==)
}
/// Tests whether `a != b`
pub fn neq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, !=)
}
/// Tests whether `a <= b`
pub fn gt_eq(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, true, >=)
}
/// Tests whether `a < b`
pub fn gt(lhs, rhs) -> IResult<ConValue> {
cmp!(lhs, rhs, false, >)
}
}
builtins! {
const RANGE;
/// Exclusive Range `a..b`
pub fn range_exc(lhs, rhs) -> IResult<ConValue> {
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
Err(Error::TypeError)?
};
Ok(ConValue::RangeExc(lhs, rhs.saturating_sub(1)))
}
/// Inclusive Range `a..=b`
pub fn range_inc(lhs, rhs) -> IResult<ConValue> {
let (&ConValue::Int(lhs), &ConValue::Int(rhs)) = (lhs, rhs) else {
Err(Error::TypeError)?
};
Ok(ConValue::RangeInc(lhs, rhs))
}
}
builtins! {
const UNARY;
/// Negates the ConValue
pub fn neg(tail) -> IResult<ConValue> {
Ok(match tail {
ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(-v),
_ => Err(Error::TypeError)?,
})
}
/// Inverts the ConValue
pub fn not(tail) -> IResult<ConValue> {
Ok(match tail {
ConValue::Empty => ConValue::Empty,
ConValue::Int(v) => ConValue::Int(!v),
ConValue::Bool(v) => ConValue::Bool(!v),
_ => Err(Error::TypeError)?,
})
}
}
/// Turns an argument slice into an array with the (inferred) correct number of elements
pub fn to_args<const N: usize>(args: &[ConValue]) -> IResult<&[ConValue; N]> {
args.try_into()
.map_err(|_| Error::ArgNumber { want: N, got: args.len() })
}
/// Turns function definitions into ZSTs which implement [Callable] and [BuiltIn]
macro builtins (
$(prefix = $prefix:literal)?
const $defaults:ident $( = [$($additional_builtins:expr),*$(,)?])?;
$(
$(#[$meta:meta])*$vis:vis fn $name:ident$(<$env:tt, $args:tt>)? ( $($($arg:tt),+$(,)?)? ) $(-> $rety:ty)?
$body:block
)*
) {
/// Builtins to load when a new interpreter is created
pub const $defaults: &[&dyn BuiltIn] = &[$(&$name,)* $($additional_builtins)*];
$(
$(#[$meta])* #[allow(non_camel_case_types)] #[derive(Clone, Debug)]
/// ```rust,ignore
#[doc = stringify!(builtin! fn $name($($($arg),*)?) $(-> $rety)? $body)]
/// ```
$vis struct $name;
impl BuiltIn for $name {
fn description(&self) -> &str { concat!("builtin ", stringify!($name), stringify!(($($($arg),*)?) )) }
}
impl Callable for $name {
#[allow(unused)]
fn call(&self, env: &mut Environment, args: &[ConValue]) $(-> $rety)? {
// println!("{}", stringify!($name), );
$(let $env = env;
let $args = args;)?
$(let [$($arg),*] = to_args(args)?;)?
$body
}
fn name(&self) -> &str { stringify!($name) }
}
)*
}
/// Templates comparison functions for [ConValue]
macro cmp ($a:expr, $b:expr, $empty:literal, $op:tt) {
match ($a, $b) {
(ConValue::Empty, ConValue::Empty) => Ok(ConValue::Bool($empty)),
(ConValue::Int(a), ConValue::Int(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Bool(a), ConValue::Bool(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::Char(a), ConValue::Char(b)) => Ok(ConValue::Bool(a $op b)),
(ConValue::String(a), ConValue::String(b)) => Ok(ConValue::Bool(a $op b)),
_ => Err(Error::TypeError)
}
}

View File

@@ -0,0 +1,464 @@
//! A work-in-progress tree walk interpreter for Conlang
//!
//! Currently, major parts of the interpreter are not yet implemented, and major parts will never be
//! implemented in its current form. Namely, since no [ConValue] has a stable location, it's
//! 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
pub trait Interpret {
/// Interprets this thing in the given [`Environment`].
///
/// Everything returns a value!™
fn interpret(&self, env: &mut Environment) -> IResult<ConValue>;
}
impl Interpret for File {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
for item in &self.items {
item.interpret(env)?;
}
Ok(ConValue::Empty)
}
}
impl Interpret for Item {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
match &self.kind {
ItemKind::Alias(item) => item.interpret(env),
ItemKind::Const(item) => item.interpret(env),
ItemKind::Static(item) => item.interpret(env),
ItemKind::Module(item) => item.interpret(env),
ItemKind::Function(item) => item.interpret(env),
ItemKind::Struct(item) => item.interpret(env),
ItemKind::Enum(item) => item.interpret(env),
ItemKind::Impl(item) => item.interpret(env),
}
}
}
impl Interpret for Alias {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
todo!("Interpret type alias in {env}")
}
}
impl Interpret for Const {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
todo!("interpret const in {env}")
}
}
impl Interpret for Static {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
todo!("interpret static in {env}")
}
}
impl Interpret for Module {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
// TODO: Enter this module's namespace
match &self.kind {
ModuleKind::Inline(file) => file.interpret(env),
ModuleKind::Outline => todo!("Load and parse external files"),
}
}
}
impl Interpret for Function {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
// register the function in the current environment
env.insert_fn(self);
Ok(ConValue::Empty)
}
}
impl Interpret for Struct {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
todo!("Interpret structs in {env}")
}
}
impl Interpret for Enum {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
todo!("Interpret enums in {env}")
}
}
impl Interpret for Impl {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
todo!("Enter a struct's namespace and insert function definitions into it in {env}");
}
}
impl Interpret for Stmt {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { extents: _, kind, semi } = self;
let out = match kind {
StmtKind::Empty => ConValue::Empty,
StmtKind::Local(stmt) => stmt.interpret(env)?,
StmtKind::Item(stmt) => stmt.interpret(env)?,
StmtKind::Expr(stmt) => stmt.interpret(env)?,
};
Ok(match semi {
Semi::Terminated => ConValue::Empty,
Semi::Unterminated => out,
})
}
}
impl Interpret for Let {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Let { mutable: _, name: Identifier(name), ty: _, init } = self;
let init = init.as_ref().map(|i| i.interpret(env)).transpose()?;
env.insert(name, init);
Ok(ConValue::Empty)
}
}
impl Interpret for Expr {
#[inline]
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { extents: _, kind } = self;
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::Index(v) => v.interpret(env),
ExprKind::Path(v) => v.interpret(env),
ExprKind::Literal(v) => v.interpret(env),
ExprKind::Array(v) => v.interpret(env),
ExprKind::ArrayRep(v) => v.interpret(env),
ExprKind::AddrOf(v) => v.interpret(env),
ExprKind::Block(v) => v.interpret(env),
ExprKind::Empty => Ok(ConValue::Empty),
ExprKind::Group(v) => v.interpret(env),
ExprKind::Tuple(v) => v.interpret(env),
ExprKind::Loop(v) => v.interpret(env),
ExprKind::While(v) => v.interpret(env),
ExprKind::If(v) => v.interpret(env),
ExprKind::For(v) => v.interpret(env),
ExprKind::Break(v) => v.interpret(env),
ExprKind::Return(v) => v.interpret(env),
ExprKind::Continue(v) => v.interpret(env),
}
}
}
impl Interpret for Assign {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Assign { kind: op, parts } = self;
let (head, tail) = parts.borrow();
// Resolve the head pattern
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)?,
PathPart::SelfKw => todo!("Assignment to `self`"),
PathPart::Ident(Identifier(s)) => s,
}
}
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)?,
};
// Get the initializer and the tail
let init = tail.interpret(env)?;
let target = env.get_mut(head)?;
if let AssignKind::Plain = op {
use std::mem::discriminant as variant;
// runtime typecheck
match target {
Some(value) if variant(value) == variant(&init) => {
*value = init;
}
value @ None => *value = Some(init),
_ => Err(Error::TypeError)?,
}
return Ok(ConValue::Empty);
}
let Some(target) = target else {
return Err(Error::NotInitialized(head.into()));
};
match op {
AssignKind::Add => target.add_assign(init)?,
AssignKind::Sub => target.sub_assign(init)?,
AssignKind::Mul => target.mul_assign(init)?,
AssignKind::Div => target.div_assign(init)?,
AssignKind::Rem => target.rem_assign(init)?,
AssignKind::And => target.bitand_assign(init)?,
AssignKind::Or => target.bitor_assign(init)?,
AssignKind::Xor => target.bitxor_assign(init)?,
AssignKind::Shl => target.shl_assign(init)?,
AssignKind::Shr => target.shr_assign(init)?,
_ => (),
}
Ok(ConValue::Empty)
}
}
impl Interpret for Binary {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Binary { kind, parts } = self;
let (head, tail) = parts.borrow();
let head = head.interpret(env)?;
// Short-circuiting ops
match kind {
BinaryKind::LogAnd => {
return if head.truthy()? {
tail.interpret(env)
} else {
Ok(head)
}; // Short circuiting
}
BinaryKind::LogOr => {
return if !head.truthy()? {
tail.interpret(env)
} else {
Ok(head)
}; // Short circuiting
}
BinaryKind::LogXor => {
return Ok(ConValue::Bool(
head.truthy()? ^ tail.interpret(env)?.truthy()?,
));
}
_ => {}
}
let tail = tail.interpret(env)?;
match kind {
BinaryKind::Mul => env.call("mul", &[head, tail]),
BinaryKind::Div => env.call("div", &[head, tail]),
BinaryKind::Rem => env.call("rem", &[head, tail]),
BinaryKind::Add => env.call("add", &[head, tail]),
BinaryKind::Sub => env.call("sub", &[head, tail]),
BinaryKind::Shl => env.call("shl", &[head, tail]),
BinaryKind::Shr => env.call("shr", &[head, tail]),
BinaryKind::BitAnd => env.call("and", &[head, tail]),
BinaryKind::BitOr => env.call("or", &[head, tail]),
BinaryKind::BitXor => env.call("xor", &[head, tail]),
BinaryKind::RangeExc => env.call("range_exc", &[head, tail]),
BinaryKind::RangeInc => env.call("range_inc", &[head, tail]),
BinaryKind::Lt => env.call("lt", &[head, tail]),
BinaryKind::LtEq => env.call("lt_eq", &[head, tail]),
BinaryKind::Equal => env.call("eq", &[head, tail]),
BinaryKind::NotEq => env.call("neq", &[head, tail]),
BinaryKind::GtEq => env.call("gt_eq", &[head, tail]),
BinaryKind::Gt => env.call("gt", &[head, tail]),
BinaryKind::Dot => todo!("search within a type's namespace!"),
BinaryKind::Call => match tail {
ConValue::Empty => head.call(env, &[]),
ConValue::Tuple(args) => head.call(env, &args),
_ => Err(Error::TypeError),
},
_ => Ok(head),
}
}
}
impl Interpret for Unary {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
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}");
Ok(operand)
}
UnaryKind::Tilde => unimplemented!("Tilde operator"),
}
}
}
impl Interpret for Index {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { head, indices } = self;
let mut head = head.interpret(env)?;
for index in indices {
head = head.index(&index.interpret(env)?)?;
}
Ok(head)
}
}
impl Interpret for Path {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { absolute: _, parts } = self;
if parts.len() == 1 {
match parts.last().expect("parts should not be empty") {
PathPart::SuperKw | PathPart::SelfKw => todo!("Path navigation"),
PathPart::Ident(Identifier(s)) => env.get(s).cloned(),
}
} else {
todo!("Path navigation!")
}
}
}
impl Interpret for Literal {
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
Ok(match self {
Literal::String(value) => ConValue::from(value.as_str()),
Literal::Char(value) => ConValue::Char(*value),
Literal::Bool(value) => ConValue::Bool(*value),
// Literal::Float(value) => todo!("Float values in interpreter: {value:?}"),
Literal::Int(value) => ConValue::Int(*value as _),
})
}
}
impl Interpret for Array {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { values } = self;
let mut out = vec![];
for expr in values {
out.push(expr.interpret(env)?)
}
Ok(ConValue::Array(out))
}
}
impl Interpret for ArrayRep {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { value, repeat } = self;
let repeat = match repeat.interpret(env)? {
ConValue::Int(v) => v,
_ => Err(Error::TypeError)?,
};
let value = value.interpret(env)?;
Ok(ConValue::Array(vec![value; repeat as usize]))
}
}
impl Interpret for AddrOf {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { count: _, mutable: _, expr } = self;
// this is stupid
todo!("Create reference\nfrom expr: {expr}\nin env:\n{env}\n")
}
}
impl Interpret for Block {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { stmts } = self;
let mut env = env.frame("block");
let mut out = ConValue::Empty;
for stmt in stmts {
out = stmt.interpret(&mut env)?;
}
Ok(out)
}
}
impl Interpret for Group {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { expr } = self;
expr.interpret(env)
}
}
impl Interpret for Tuple {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { exprs } = self;
Ok(ConValue::Tuple(exprs.iter().try_fold(
vec![],
|mut out, element| {
out.push(element.interpret(env)?);
Ok(out)
},
)?))
}
}
impl Interpret for Loop {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { body } = self;
loop {
match body.interpret(env) {
Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,
e => e?,
};
}
}
}
impl Interpret for While {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { cond, pass, fail } = self;
loop {
if cond.interpret(env)?.truthy()? {
match pass.interpret(env) {
Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,
e => e?,
};
} else {
break fail.interpret(env);
}
}
}
}
impl Interpret for If {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { cond, pass, fail } = self;
if cond.interpret(env)?.truthy()? {
pass.interpret(env)
} else {
fail.interpret(env)
}
}
}
impl Interpret for For {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { bind: Identifier(name), cond, pass, fail } = self;
// TODO: A better iterator model
let mut bounds = match cond.interpret(env)? {
ConValue::RangeExc(a, b) => a..=b,
ConValue::RangeInc(a, b) => a..=b,
_ => Err(Error::TypeError)?,
};
loop {
let mut env = env.frame("loop variable");
if let Some(loop_var) = bounds.next() {
env.insert(name, Some(loop_var.into()));
match pass.interpret(&mut env) {
Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,
result => result?,
};
} else {
break fail.interpret(&mut env);
}
}
}
}
impl Interpret for Else {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { body } = self;
match body {
Some(body) => body.interpret(env),
None => Ok(ConValue::Empty),
}
}
}
impl Interpret for Continue {
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
Err(Error::Continue)
}
}
impl Interpret for Return {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { body } = self;
Err(Error::Return(
body.as_ref()
.map(|body| body.interpret(env))
.unwrap_or(Ok(ConValue::Empty))?,
))
}
}
impl Interpret for Break {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { body } = self;
Err(Error::Break(
body.as_ref()
.map(|body| body.interpret(env))
.unwrap_or(Ok(ConValue::Empty))?,
))
}
}

View File

@@ -0,0 +1,612 @@
//! Walks a Conlang AST, interpreting it as a program.
#![warn(clippy::all)]
#![feature(decl_macro)]
use env::Environment;
use error::{Error, IResult};
use interpret::Interpret;
use temp_type_impl::ConValue;
/// Callable types can be called from within a Conlang program
pub trait Callable: std::fmt::Debug {
/// Calls this [Callable] in the provided [Environment], with [ConValue] args \
/// The Callable is responsible for checking the argument count and validating types
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue>;
/// Returns the common name of this identifier.
fn name(&self) -> &str;
}
/// [BuiltIn]s are [Callable]s with bespoke definitions
pub trait BuiltIn: std::fmt::Debug + Callable {
fn description(&self) -> &str;
}
pub mod temp_type_impl {
//! Temporary implementations of Conlang values
//!
//! The most permanent fix is a temporary one.
use super::{
error::{Error, IResult},
function::Function,
BuiltIn, Callable, Environment,
};
use std::ops::*;
type Integer = isize;
/// A Conlang value
///
/// This is a hack to work around the fact that Conlang doesn't
/// have a functioning type system yet :(
#[derive(Clone, Debug, Default)]
pub enum ConValue {
/// The empty/unit `()` type
#[default]
Empty,
/// An integer
Int(Integer),
/// A boolean
Bool(bool),
/// A unicode character
Char(char),
/// A string
String(String),
/// An Array
Array(Vec<ConValue>),
/// A tuple
Tuple(Vec<ConValue>),
/// An exclusive range
RangeExc(Integer, Integer),
/// An inclusive range
RangeInc(Integer, Integer),
/// A callable thing
Function(Function),
/// A built-in function
BuiltIn(&'static dyn BuiltIn),
}
impl ConValue {
/// Gets whether the current value is true or false
pub fn truthy(&self) -> IResult<bool> {
match self {
ConValue::Bool(v) => Ok(*v),
_ => Err(Error::TypeError)?,
}
}
pub fn range_exc(self, other: Self) -> IResult<Self> {
let (Self::Int(a), Self::Int(b)) = (self, other) else {
Err(Error::TypeError)?
};
Ok(Self::RangeExc(a, b.saturating_sub(1)))
}
pub fn range_inc(self, other: Self) -> IResult<Self> {
let (Self::Int(a), Self::Int(b)) = (self, other) else {
Err(Error::TypeError)?
};
Ok(Self::RangeInc(a, b))
}
pub fn index(&self, index: &Self) -> IResult<ConValue> {
let Self::Int(index) = index else {
Err(Error::TypeError)?
};
let Self::Array(arr) = self else {
Err(Error::TypeError)?
};
arr.get(*index as usize)
.cloned()
.ok_or(Error::OobIndex(*index as usize, arr.len()))
}
cmp! {
lt: false, <;
lt_eq: true, <=;
eq: true, ==;
neq: false, !=;
gt_eq: true, >=;
gt: false, >;
}
assign! {
add_assign: +;
bitand_assign: &;
bitor_assign: |;
bitxor_assign: ^;
div_assign: /;
mul_assign: *;
rem_assign: %;
shl_assign: <<;
shr_assign: >>;
sub_assign: -;
}
}
impl Callable for ConValue {
fn name(&self) -> &str {
match self {
ConValue::Function(func) => func.name(),
ConValue::BuiltIn(func) => func.name(),
_ => "",
}
}
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
match self {
Self::Function(func) => func.call(interpreter, args),
Self::BuiltIn(func) => func.call(interpreter, args),
_ => Err(Error::NotCallable(self.clone())),
}
}
}
/// Templates comparison functions for [ConValue]
macro cmp ($($fn:ident: $empty:literal, $op:tt);*$(;)?) {$(
/// TODO: Remove when functions are implemented:
/// Desugar into function calls
pub fn $fn(&self, other: &Self) -> IResult<Self> {
match (self, other) {
(Self::Empty, Self::Empty) => Ok(Self::Bool($empty)),
(Self::Int(a), Self::Int(b)) => Ok(Self::Bool(a $op b)),
(Self::Bool(a), Self::Bool(b)) => Ok(Self::Bool(a $op b)),
(Self::Char(a), Self::Char(b)) => Ok(Self::Bool(a $op b)),
(Self::String(a), Self::String(b)) => Ok(Self::Bool(a $op b)),
_ => Err(Error::TypeError)
}
}
)*}
macro assign($( $fn: ident: $op: tt );*$(;)?) {$(
pub fn $fn(&mut self, other: Self) -> IResult<()> {
*self = (std::mem::take(self) $op other)?;
Ok(())
}
)*}
/// Implements [From] for an enum with 1-tuple variants
macro from ($($T:ty => $v:expr),*$(,)?) {
$(impl From<$T> for ConValue {
fn from(value: $T) -> Self { $v(value.into()) }
})*
}
from! {
Integer => ConValue::Int,
bool => ConValue::Bool,
char => ConValue::Char,
&str => ConValue::String,
String => ConValue::String,
Function => ConValue::Function,
Vec<ConValue> => ConValue::Tuple,
&'static dyn BuiltIn => ConValue::BuiltIn,
}
impl From<()> for ConValue {
fn from(_: ()) -> Self {
Self::Empty
}
}
impl From<&[ConValue]> for ConValue {
fn from(value: &[ConValue]) -> Self {
match value.len() {
0 => Self::Empty,
1 => value[0].clone(),
_ => Self::Tuple(value.into()),
}
}
}
/// Implements binary [std::ops] traits for [ConValue]
///
/// TODO: Desugar operators into function calls
macro ops($($trait:ty: $fn:ident = [$($match:tt)*])*) {
$(impl $trait for ConValue {
type Output = IResult<Self>;
/// TODO: Desugar operators into function calls
fn $fn(self, rhs: Self) -> Self::Output {Ok(match (self, rhs) {$($match)*})}
})*
}
ops! {
Add: add = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
(ConValue::String(a), ConValue::String(b)) => ConValue::String(a + &b),
_ => Err(Error::TypeError)?
]
BitAnd: bitand = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError)?
]
BitOr: bitor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError)?
]
BitXor: bitxor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError)?
]
Div: div = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
_ => Err(Error::TypeError)?
]
Mul: mul = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
_ => Err(Error::TypeError)?
]
Rem: rem = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
_ => Err(Error::TypeError)?
]
Shl: shl = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
_ => Err(Error::TypeError)?
]
Shr: shr = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
_ => Err(Error::TypeError)?
]
Sub: sub = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
_ => Err(Error::TypeError)?
]
}
impl std::fmt::Display for ConValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConValue::Empty => "Empty".fmt(f),
ConValue::Int(v) => v.fmt(f),
ConValue::Bool(v) => v.fmt(f),
ConValue::Char(v) => v.fmt(f),
ConValue::String(v) => v.fmt(f),
ConValue::Array(array) => {
'['.fmt(f)?;
for (idx, element) in array.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
']'.fmt(f)
}
ConValue::RangeExc(a, b) => write!(f, "{a}..{}", b + 1),
ConValue::RangeInc(a, b) => write!(f, "{a}..={b}"),
ConValue::Tuple(tuple) => {
'('.fmt(f)?;
for (idx, element) in tuple.iter().enumerate() {
if idx > 0 {
", ".fmt(f)?
}
element.fmt(f)?
}
')'.fmt(f)
}
ConValue::Function(func) => {
write!(f, "{}", func.decl())
}
ConValue::BuiltIn(func) => {
write!(f, "{}", func.description())
}
}
}
}
}
pub mod interpret;
pub mod function {
//! Represents a block of code which lives inside the Interpreter
use super::{Callable, ConValue, Environment, Error, IResult, Interpret};
use cl_ast::{Function as FnDecl, Identifier, Param};
/// Represents a block of code which persists inside the Interpreter
#[derive(Clone, Debug)]
pub struct Function {
/// Stores the contents of the function declaration
decl: Box<FnDecl>,
// /// Stores the enclosing scope of the function
// env: Box<Environment>,
}
impl Function {
pub fn new(decl: &FnDecl) -> Self {
Self { decl: decl.clone().into() }
}
pub fn decl(&self) -> &FnDecl {
&self.decl
}
}
impl Callable for Function {
fn name(&self) -> &str {
let FnDecl { name: Identifier(ref name), .. } = *self.decl;
name
}
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
let FnDecl { name: Identifier(name), bind: declargs, body, sign: _ } = &*self.decl;
// Check arg mapping
if args.len() != declargs.len() {
return Err(Error::ArgNumber { want: declargs.len(), got: args.len() });
}
let Some(body) = body else {
return Err(Error::NotDefined(name.into()));
};
// TODO: completely refactor data storage
let mut frame = env.frame("fn args");
for (Param { mutability: _, name: Identifier(name) }, value) in
declargs.iter().zip(args)
{
frame.insert(name, Some(value.clone()));
}
match body.interpret(&mut frame) {
Err(Error::Return(value)) => Ok(value),
Err(Error::Break(value)) => Err(Error::BadBreak(value)),
result => result,
}
}
}
}
pub mod builtin;
pub mod env {
//! Lexical and non-lexical scoping for variables
use super::{
builtin::{BINARY, MISC, RANGE, UNARY},
error::{Error, IResult},
function::Function,
temp_type_impl::ConValue,
BuiltIn, Callable, Interpret,
};
use cl_ast::{Function as FnDecl, Identifier};
use std::{
collections::HashMap,
fmt::Display,
ops::{Deref, DerefMut},
};
/// Implements a nested lexical scope
#[derive(Clone, Debug)]
pub struct Environment {
frames: Vec<(HashMap<String, Option<ConValue>>, &'static str)>,
}
impl Display for Environment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (frame, name) in self.frames.iter().rev() {
writeln!(f, "--- {name} ---")?;
for (var, val) in frame {
write!(f, "{var}: ")?;
match val {
Some(value) => writeln!(f, "\t{value}"),
None => writeln!(f, "<undefined>"),
}?
}
}
Ok(())
}
}
impl Default for Environment {
fn default() -> Self {
Self {
frames: vec![
(to_hashmap(RANGE), "range ops"),
(to_hashmap(UNARY), "unary ops"),
(to_hashmap(BINARY), "binary ops"),
(to_hashmap(MISC), "builtins"),
(HashMap::new(), "globals"),
],
}
}
}
fn to_hashmap(from: &[&'static dyn BuiltIn]) -> HashMap<String, Option<ConValue>> {
from.iter()
.map(|&v| (v.name().into(), Some(v.into())))
.collect()
}
impl Environment {
pub fn new() -> Self {
Self::default()
}
/// Creates an [Environment] with no [builtins](super::builtin)
pub fn no_builtins(name: &'static str) -> Self {
Self { frames: vec![(Default::default(), name)] }
}
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
node.interpret(self)
}
/// Calls a function inside the interpreter's scope,
/// and returns the result
pub fn call(&mut self, name: &str, args: &[ConValue]) -> IResult<ConValue> {
// FIXME: Clone to satisfy the borrow checker
let function = self.get(name)?.clone();
function.call(self, args)
}
/// Enters a nested scope, returning a [`Frame`] stack-guard.
///
/// [`Frame`] implements Deref/DerefMut for [`Environment`].
pub fn frame(&mut self, name: &'static str) -> Frame {
Frame::new(self, name)
}
/// Resolves a variable mutably.
///
/// Returns a mutable reference to the variable's record, if it exists.
pub fn get_mut(&mut self, id: &str) -> IResult<&mut Option<ConValue>> {
for (frame, _) in self.frames.iter_mut().rev() {
if let Some(var) = frame.get_mut(id) {
return Ok(var);
}
}
Err(Error::NotDefined(id.into()))
}
/// Resolves a variable immutably.
///
/// Returns a reference to the variable's contents, if it is defined and initialized.
pub fn get(&self, id: &str) -> IResult<&ConValue> {
for (frame, _) in self.frames.iter().rev() {
match frame.get(id) {
Some(Some(var)) => return Ok(var),
Some(None) => return Err(Error::NotInitialized(id.into())),
_ => (),
}
}
Err(Error::NotDefined(id.into()))
}
/// Inserts a new [ConValue] into this [Environment]
pub fn insert(&mut self, id: &str, value: Option<ConValue>) {
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(id.into(), value);
}
}
/// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) {
let FnDecl { name: Identifier(name), .. } = decl;
let (name, function) = (name.clone(), Some(Function::new(decl).into()));
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(name, function);
}
}
}
/// Functions which aid in the implementation of [`Frame`]
impl Environment {
/// Enters a scope, creating a new namespace for variables
fn enter(&mut self, name: &'static str) -> &mut Self {
self.frames.push((Default::default(), name));
self
}
/// Exits the scope, destroying all local variables and
/// returning the outer scope, if there is one
fn exit(&mut self) -> &mut Self {
if self.frames.len() > 2 {
self.frames.pop();
}
self
}
}
/// Represents a stack frame
#[derive(Debug)]
pub struct Frame<'scope> {
scope: &'scope mut Environment,
}
impl<'scope> Frame<'scope> {
fn new(scope: &'scope mut Environment, name: &'static str) -> Self {
Self { scope: scope.enter(name) }
}
}
impl<'scope> Deref for Frame<'scope> {
type Target = Environment;
fn deref(&self) -> &Self::Target {
self.scope
}
}
impl<'scope> DerefMut for Frame<'scope> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.scope
}
}
impl<'scope> Drop for Frame<'scope> {
fn drop(&mut self) {
self.scope.exit();
}
}
}
pub mod error {
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
use super::temp_type_impl::ConValue;
pub type IResult<T> = Result<T, Error>;
/// Represents any error thrown by the [Environment](super::Environment)
#[derive(Clone, Debug)]
pub enum Error {
/// Propagate a Return value
Return(ConValue),
/// Propagate a Break value
Break(ConValue),
/// Break propagated across function bounds
BadBreak(ConValue),
/// Continue to the next iteration of a loop
Continue,
/// Underflowed the stack
StackUnderflow,
/// Exited the last scope
ScopeExit,
/// Type incompatibility
// TODO: store the type information in this error
TypeError,
/// In clause of For loop didn't yield a Range
NotIterable,
/// A value could not be indexed
NotIndexable,
/// An array index went out of bounds
OobIndex(usize, usize),
/// An expression is not assignable
NotAssignable,
/// A name was not defined in scope before being used
NotDefined(String),
/// A name was defined but not initialized
NotInitialized(String),
/// A value was called, but is not callable
NotCallable(ConValue),
/// A function was called with the wrong number of arguments
ArgNumber {
want: usize,
got: usize,
},
NullPointer,
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Return(value) => write!(f, "return {value}"),
Error::Break(value) => write!(f, "break {value}"),
Error::BadBreak(value) => write!(f, "rogue break: {value}"),
Error::Continue => "continue".fmt(f),
Error::StackUnderflow => "Stack underflow".fmt(f),
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 => {
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 => {
write!(f, "expression is not assignable")
}
Error::NotDefined(value) => {
write!(f, "{value} not bound. Did you mean `let {value};`?")
}
Error::NotInitialized(value) => {
write!(f, "{value} bound, but not initialized")
}
Error::NotCallable(value) => {
write!(f, "{value} is not callable.")
}
Error::ArgNumber { want, got } => {
write!(
f,
"Expected {want} argument{}, got {got}",
if *want == 1 { "" } else { "s" }
)
}
Error::NullPointer => {
write!(f, "Attempted to dereference a null pointer?")
}
}
}
}
}
#[cfg(test)]
mod tests;

View File

@@ -0,0 +1,476 @@
#![allow(unused_imports)]
use crate::{env::Environment, temp_type_impl::ConValue, Interpret};
use cl_ast::*;
use cl_lexer::Lexer;
use cl_parser::Parser;
pub use macros::*;
mod macros {
//! Useful macros for parsing Conlang
//!
//! # Parsing
//! Macro [`parse`] stringifies, lexes, and parses everything you give to it
//! ```rust
//! # use conlang::interpreter::tests::*;
//! parse!{
//! fn main () {
//! "Hello, world!"
//! }
//! }
//! ```
//!
//! # Evaluating
//! Macro [`eval`] parses code in the given [`Environment`].
//! - [`assert_eval`] does the above, but expects [`Ok`]
//! - [`assert_noeval`] does the above, but expects [`Err`]
//! ```rust
//! # use conlang::interpreter::tests::*;
//! let mut env = Default::default();
//! eval!{env,
//! let x = 2;
//! }.expect("variable binding should succeed.");
//! ```
//!
//! # Extracting Results
//! Macros [`env_eq`] and [`env_ne`] take an "Environment Member Expression" and a value which
//! implements [`Into<ConValue>`], and asserts that they are either equal or not equal,
//! respectively.
//!
//! Macro [`conv_cmp`] takes two things that can be converted to [`ConValue`], and calls the
//! provided comparison function on them, returning a [`bool`].
//! ```rust
//! # use conlang::interpreter::tests::*;
//! let mut env = Default::default();
//! assert_eval!{env,
//! let x = 10;
//! }
//! env_eq!(env.x, 10); // like assert_eq! for Environments
//! ```
#![allow(unused_macros)]
use crate::IResult;
use super::*;
pub fn test_inside_block(block: &Block, env: &mut Environment) -> IResult<()> {
let Block { stmts } = block;
for stmt in stmts {
stmt.interpret(env)?;
}
Ok(())
}
/// Stringifies, lexes, and parses everything you give to it
///
/// Returns a `Result<`[`File`]`, ParseError>`
pub macro file($($t:tt)*) {
Parser::new(Lexer::new(stringify!( $($t)* ))).file()
}
/// Stringifies, lexes, and parses everything you give to it
///
/// Returns a `Result<`[`Block`]`, ParseError>`
pub macro block($($t:tt)*) {
Parser::new(Lexer::new(stringify!({ $($t)* }))).block()
}
/// Evaluates a block of code in the given environment
///
/// ```rust,ignore
/// eval!(env,
/// // Conlang code goes here
/// fn main () {
/// "Hello, world!"
/// }
/// )
/// ```
pub macro eval($env: path, $($t:tt)*) {{
test_inside_block(&block!($($t)*)
.expect("code passed to eval! should parse correctly"),
&mut $env)
}}
/// Evaluates a block of code in the given environment, expecting the interpreter to succeed
///
/// ```rust,ignore
/// assert_eval!(env,
/// // Conlang code goes here
/// fn main () {
/// "Hello, world!"
/// }
/// )
/// ```
pub macro assert_eval($($t:tt)*) {
eval!($($t)*)
.expect(stringify!($($t)* should execute correctly))
}
/// Evaluates a block of code in the given environment, expecting the interpreter to fail
///
/// ```rust,ignore
/// assert_noeval!(env,
/// // Conlang code goes here
/// fn main () {
/// 1 == "Hello world!" // type incompatibility
/// }
/// )
/// ```
pub macro assert_noeval($($t:tt)*) {
eval!($($t)*)
.expect_err(stringify!($($t)* should not execute correctly))
}
pub macro conv_cmp($func: ident, $a: expr, $b: expr) {
$a.$func(&($b).into())
.expect(stringify!($a should be comparable to $b))
.truthy()
.expect(stringify!(result of comparison should be ConValue::Bool))
}
pub macro env_ne($env:ident.$var:ident, $expr:expr) {{
let evaluated = $env.get(stringify!($var))
.expect(stringify!($var should be defined and initialized));
if !conv_cmp!(neq, evaluated, $expr) {
panic!("assertion {} ({evaluated}) != {} failed.", stringify!($var), stringify!($expr))
}
}}
pub macro env_eq($env:ident.$var:ident, $expr:expr) {{
let evaluated = $env.get(stringify!($var))
.expect(stringify!($var should be defined and initialized));
if !conv_cmp!(eq, evaluated, $expr) {
panic!("assertion {} ({evaluated}) == {} failed.", stringify!($var), stringify!($expr))
}
}}
pub macro tuple($($expr:expr),*) {
ConValue::from(vec![$(ConValue::from($expr)),*])
}
}
mod let_declarations {
use super::*;
#[test]
fn let_binding_uninit() {
let mut env = Environment::new();
assert_eval!(
env,
let x;
);
}
#[test]
fn let_binding_with_init() {
let mut env = Environment::new();
assert_eval!( env,
let x = 10;
);
env_eq!(env.x, 10)
}
#[test]
fn let_binding_from_another_variable() {
let mut env = Environment::new();
assert_eval!(env,
let x = 10;
let y = x;
);
env_eq!(env.x, 10);
env_eq!(env.y, 10);
}
}
mod fn_declarations {
use super::*;
#[test]
fn empty_fn() {
let mut env = Environment::new();
assert_eval!(env, fn empty_fn() {});
// TODO: true equality for functions
assert_eq!(
"fn empty_fn () {\n \n}",
format!(
"{}",
env.get("empty_fn")
.expect(stringify!(empty_fn should be defined and initialized))
)
)
}
#[test]
fn identity_fn() {
let mut env = Environment::new();
assert_eval!(
env,
fn identity(input: i32) -> i32 {
input
}
let output = identity(12);
);
env_eq!(env.output, 12);
}
}
mod operators {
use cl_ast::Tuple;
use super::*;
#[test]
fn unary() {
let mut env = Default::default();
assert_eval!(env,
let neg_one = -1;
let pos_one = -neg_one;
let not_true = !true;
let not_false = !false;
);
env_eq!(env.neg_one, -1);
env_eq!(env.pos_one, 1);
env_eq!(env.not_true, false);
env_eq!(env.not_false, true);
}
#[test]
fn mul_div_rem() {
let mut env = Default::default();
assert_eval!(env,
let one = 129 % 32;
let twelve = 144 / 12;
let one_forty_four = 12 * 12;
);
env_eq!(env.one, 1);
env_eq!(env.twelve, 12);
env_eq!(env.one_forty_four, 144);
}
#[test]
fn add_sub() {
let mut env = Default::default();
assert_eval!(env,
let is_42 = 17 + 25;
let also_42 = 165 - 123;
);
env_eq!(env.is_42, 42);
env_eq!(env.also_42, 42);
}
#[test]
fn shift() {
let mut env = Default::default();
assert_eval!(env,
let eight = 1<<3;
let one = 8>>3;
);
env_eq!(env.eight, 8);
env_eq!(env.one, 1);
}
#[test]
fn bitwise() {
let mut env = Default::default();
assert_eval!(env,
let and_b1010 = 0b1111 & 0b1010;
let or__b1111 = 0b1111 | 0b1010;
let xor_b0101 = 0b1111 ^ 0b1010;
);
env_eq!(env.and_b1010, 0b1010);
env_eq!(env.or__b1111, 0b1111);
env_eq!(env.xor_b0101, 0b0101);
}
#[test]
fn logical() {
let mut env = Default::default();
assert_eval!(env,
let t_and_t = true && true;
let t_and_f = true && false;
let f_and_t = false && true;
let f_and_f = false && false;
let t_or_t = true || true;
let t_or_f = true || false;
let f_or_t = false || true;
let f_or_f = false || false;
let t_xor_t = true ^^ true;
let t_xor_f = true ^^ false;
let f_xor_t = false ^^ true;
let f_xor_f = false ^^ false;
);
env_eq!(env.t_and_t, true);
env_eq!(env.t_and_f, false);
env_eq!(env.f_and_t, false);
env_eq!(env.f_and_f, false);
env_eq!(env.t_or_t, true);
env_eq!(env.t_or_f, true);
env_eq!(env.f_or_t, true);
env_eq!(env.f_or_f, false);
env_eq!(env.t_xor_t, false);
env_eq!(env.t_xor_f, true);
env_eq!(env.f_xor_t, true);
env_eq!(env.f_xor_f, false);
}
#[test]
fn logical_short_circuits() {
let mut env = Default::default();
assert_eval!(env,
let mut and_short_circuits = true;
false && { and_short_circuits = false; false };
let mut or_short_circuits = true;
true || { or_short_circuits = false; false };
);
env_eq!(env.and_short_circuits, true);
env_eq!(env.or_short_circuits, true);
}
#[test]
fn range() {
let mut env = Default::default();
assert_eval!(env,
let inclusive = 0..=10;
let exclusive = 0..10;
);
// TODO: extract the ranges and actually check them
}
#[test]
fn compare() {
let mut env = Default::default();
assert_eval!(env,
// Less than
let is_10_lt_20 = 10 < 20;
let is_10_le_20 = 10 <= 20;
let is_10_eq_20 = 10 == 20;
let is_10_ne_20 = 10 != 20;
let is_10_ge_20 = 10 >= 20;
let is_10_gt_20 = 10 > 20;
// Equal to
let is_10_lt_10 = 10 < 10;
let is_10_le_10 = 10 <= 10;
let is_10_eq_10 = 10 == 10;
let is_10_ne_10 = 10 != 10;
let is_10_ge_10 = 10 >= 10;
let is_10_gt_10 = 10 > 10;
// Greater than
let is_20_lt_10 = 20 < 10;
let is_20_le_10 = 20 <= 10;
let is_20_eq_10 = 20 == 10;
let is_20_ne_10 = 20 != 10;
let is_20_ge_10 = 20 >= 10;
let is_20_gt_10 = 20 > 10;
dump();
);
// Less than
env_eq!(env.is_10_lt_20, true); // <
env_eq!(env.is_10_le_20, true); // <=
env_eq!(env.is_10_eq_20, false); // ==
env_eq!(env.is_10_ne_20, true); // !=
env_eq!(env.is_10_ge_20, false); // >=
env_eq!(env.is_10_gt_20, false); // >
// Equal to
env_eq!(env.is_10_lt_10, false);
env_eq!(env.is_10_le_10, true);
env_eq!(env.is_10_eq_10, true);
env_eq!(env.is_10_ne_10, false);
env_eq!(env.is_10_ge_10, true);
env_eq!(env.is_10_gt_10, false);
// Greater than
env_eq!(env.is_20_lt_10, false);
env_eq!(env.is_20_le_10, false);
env_eq!(env.is_20_eq_10, false);
env_eq!(env.is_20_ne_10, true);
env_eq!(env.is_20_ge_10, true);
env_eq!(env.is_20_gt_10, true);
}
#[test]
fn assign() {
let mut env = Default::default();
assert_eval!(env,
let base = 10;
let mut assign = base;
let mut add = base;
let mut sub = base;
let mut mul = base;
let mut div = base;
let mut rem = base;
let mut and = base;
let mut or_ = base;
let mut xor = base;
let mut shl = base;
let mut shr = base;
let modifier = 3;
assign = modifier;
add += modifier;
sub -= modifier;
mul *= modifier;
div /= modifier;
rem %= modifier;
and &= modifier;
or_ |= modifier;
xor ^= modifier;
shl <<= modifier;
shr >>= modifier;
);
let (base, modifier) = (10, 3);
env_eq!(env.assign, modifier);
env_eq!(env.add, base + modifier);
env_eq!(env.sub, base - modifier);
env_eq!(env.mul, base * modifier);
env_eq!(env.div, base / modifier);
env_eq!(env.rem, base % modifier);
env_eq!(env.and, base & modifier);
env_eq!(env.or_, base | modifier);
env_eq!(env.xor, base ^ modifier);
env_eq!(env.shl, base << modifier);
env_eq!(env.shr, base >> modifier);
}
#[test]
fn assignment_is_left_assoc_and_returns_empty() {
let mut env = Default::default();
assert_eval!(env,
let x; // uninitialized (no type)
let y = 0xdeadbeef;
let z = 10;
x = y = z;
);
env_eq!(env.x, ());
env_eq!(env.y, 10);
env_eq!(env.z, 10);
}
#[test]
#[should_panic]
fn assignment_accounts_for_type() {
let mut env = Default::default();
assert_eval!(env,
let x = "a string";
let y = 0xdeadbeef;
y = x; // should crash: type error
);
}
#[test]
fn precedence() {
let mut env = Default::default();
assert_eval!(env,
// mul/div/rem > add/sub
let a = 2 * 3 + 4 * 5 / 6; // = 9
// add/sub > shift
let b = 1 << 3 + 1 << 2; // 1 << 6 = 64
// shift > bitwise
let c = 4 | 4 << 4; // 4 | 64 = 68
// all together now!
let d = 1 << 2 + 3 * 4; // 2 << 14 = 16384
let e = 4 * 3 + 2 << 1; // 14 << 1 = 28
);
env_eq!(env.a, 9);
env_eq!(env.b, 64);
env_eq!(env.c, 68);
env_eq!(env.d, 16384);
env_eq!(env.e, 28);
}
}
#[allow(dead_code)]
fn test_template() {
let mut env = Default::default();
assert_eval!(env,);
//env_eq!(, );
}

View File

@@ -0,0 +1,13 @@
[package]
name = "cl-lexer"
repository.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
cl-token = { path = "../cl-token" }
cl-structures = { path = "../cl-structures" }
unicode-ident = "1.0.12"

View File

@@ -0,0 +1,556 @@
//! Converts a text file into tokens
#![warn(clippy::all)]
#![feature(decl_macro)]
use cl_structures::span::Loc;
use cl_token::{TokenKind as Kind, *};
use std::{
iter::Peekable,
str::{Chars, FromStr},
};
use unicode_ident::*;
#[cfg(test)]
mod tests;
pub mod lexer_iter {
//! Iterator over a [`Lexer`], returning [`LResult<Token>`]s
use super::{
error::{LResult, Reason},
Lexer, Token,
};
/// Iterator over a [`Lexer`], returning [`LResult<Token>`]s
pub struct LexerIter<'t> {
lexer: Lexer<'t>,
}
impl<'t> Iterator for LexerIter<'t> {
type Item = LResult<Token>;
fn next(&mut self) -> Option<Self::Item> {
match self.lexer.scan() {
Ok(v) => Some(Ok(v)),
Err(e) => {
if e.reason == Reason::EndOfFile {
None
} else {
Some(Err(e))
}
}
}
}
}
impl<'t> IntoIterator for Lexer<'t> {
type Item = LResult<Token>;
type IntoIter = LexerIter<'t>;
fn into_iter(self) -> Self::IntoIter {
LexerIter { lexer: self }
}
}
}
/// The Lexer iterates over the characters in a body of text, searching for [Tokens](Token).
///
/// # Examples
/// ```rust
/// # use cl_lexer::Lexer;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // Read in your code from somewhere
/// let some_code = "
/// fn main () {
/// // TODO: code goes here!
/// }
/// ";
/// // Create a lexer over your code
/// let mut lexer = Lexer::new(some_code);
/// // Scan for a single token
/// 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?);
/// match token {
/// Ok(token) => println!("{token:?}"),
/// Err(e) => eprintln!("{e:?}"),
/// }
/// }
/// # Ok(()) }
/// ```
#[derive(Clone, Debug)]
pub struct Lexer<'t> {
iter: Peekable<Chars<'t>>,
start: usize,
start_loc: (u32, u32),
current: usize,
current_loc: (u32, u32),
}
impl<'t> Lexer<'t> {
/// Creates a new [Lexer] over a [str]
pub fn new(text: &'t str) -> Self {
Self {
iter: text.chars().peekable(),
start: 0,
start_loc: (1, 1),
current: 0,
current_loc: (1, 1),
}
}
/// Scans through the text, searching for the next [Token]
pub fn scan(&mut self) -> LResult<Token> {
match self.skip_whitespace().peek()? {
'{' => self.consume()?.produce_op(Punct::LCurly),
'}' => self.consume()?.produce_op(Punct::RCurly),
'[' => self.consume()?.produce_op(Punct::LBrack),
']' => self.consume()?.produce_op(Punct::RBrack),
'(' => self.consume()?.produce_op(Punct::LParen),
')' => self.consume()?.produce_op(Punct::RParen),
'&' => self.consume()?.amp(),
'@' => self.consume()?.produce_op(Punct::At),
'\\' => self.consume()?.produce_op(Punct::Backslash),
'!' => self.consume()?.bang(),
'|' => self.consume()?.bar(),
':' => self.consume()?.colon(),
',' => self.consume()?.produce_op(Punct::Comma),
'.' => self.consume()?.dot(),
'=' => self.consume()?.equal(),
'`' => self.consume()?.produce_op(Punct::Grave),
'>' => self.consume()?.greater(),
'#' => self.consume()?.hash(),
'<' => self.consume()?.less(),
'-' => self.consume()?.minus(),
'+' => self.consume()?.plus(),
'?' => self.consume()?.produce_op(Punct::Question),
'%' => self.consume()?.rem(),
';' => self.consume()?.produce_op(Punct::Semi),
'/' => self.consume()?.slash(),
'*' => self.consume()?.star(),
'~' => self.consume()?.produce_op(Punct::Tilde),
'^' => self.consume()?.xor(),
'0' => self.consume()?.int_with_base(),
'1'..='9' => self.digits::<10>(),
'"' => self.consume()?.string(),
'\'' => self.consume()?.character(),
'_' => self.identifier(),
i if is_xid_start(i) => self.identifier(),
e => {
let err = Err(Error::unexpected_char(e, self.line(), self.col()));
let _ = self.consume();
err
}
}
}
/// Returns the current line
pub fn line(&self) -> u32 {
self.start_loc.0
}
/// Returns the current column
pub fn col(&self) -> u32 {
self.start_loc.1
}
fn next(&mut self) -> LResult<char> {
let out = self.peek();
self.consume()?;
out
}
fn peek(&mut self) -> LResult<char> {
self.iter
.peek()
.copied()
.ok_or(Error::end_of_file(self.line(), self.col()))
}
fn produce(&mut self, kind: TokenKind, data: impl Into<TokenData>) -> LResult<Token> {
let loc = self.start_loc;
self.start_loc = self.current_loc;
self.start = self.current;
Ok(Token::new(kind, data, loc.0, loc.1))
}
fn produce_op(&mut self, kind: Punct) -> LResult<Token> {
self.produce(TokenKind::Punct(kind), ())
}
fn skip_whitespace(&mut self) -> &mut Self {
while let Ok(c) = self.peek() {
if !c.is_whitespace() {
break;
}
let _ = self.consume();
}
self.start = self.current;
self.start_loc = self.current_loc;
self
}
fn consume(&mut self) -> LResult<&mut Self> {
self.current += 1;
match self.iter.next() {
Some('\n') => {
let (line, col) = &mut self.current_loc;
*line += 1;
*col = 1;
}
Some(_) => self.current_loc.1 += 1,
None => Err(Error::end_of_file(self.line(), self.col()))?,
}
Ok(self)
}
}
/// Digraphs and trigraphs
impl<'t> Lexer<'t> {
fn amp(&mut self) -> LResult<Token> {
match self.peek() {
Ok('&') => self.consume()?.produce_op(Punct::AmpAmp),
Ok('=') => self.consume()?.produce_op(Punct::AmpEq),
_ => self.produce_op(Punct::Amp),
}
}
fn bang(&mut self) -> LResult<Token> {
match self.peek() {
Ok('!') => self.consume()?.produce_op(Punct::BangBang),
Ok('=') => self.consume()?.produce_op(Punct::BangEq),
_ => self.produce_op(Punct::Bang),
}
}
fn bar(&mut self) -> LResult<Token> {
match self.peek() {
Ok('|') => self.consume()?.produce_op(Punct::BarBar),
Ok('=') => self.consume()?.produce_op(Punct::BarEq),
_ => self.produce_op(Punct::Bar),
}
}
fn colon(&mut self) -> LResult<Token> {
match self.peek() {
Ok(':') => self.consume()?.produce_op(Punct::ColonColon),
_ => self.produce_op(Punct::Colon),
}
}
fn dot(&mut self) -> LResult<Token> {
match self.peek() {
Ok('.') => {
if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::DotDotEq)
} else {
self.produce_op(Punct::DotDot)
}
}
_ => self.produce_op(Punct::Dot),
}
}
fn equal(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::EqEq),
Ok('>') => self.consume()?.produce_op(Punct::FatArrow),
_ => self.produce_op(Punct::Eq),
}
}
fn greater(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::GtEq),
Ok('>') => {
if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::GtGtEq)
} else {
self.produce_op(Punct::GtGt)
}
}
_ => self.produce_op(Punct::Gt),
}
}
fn hash(&mut self) -> LResult<Token> {
match self.peek() {
Ok('!') => self.consume()?.produce_op(Punct::HashBang),
_ => self.produce_op(Punct::Hash),
}
}
fn less(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::LtEq),
Ok('<') => {
if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::LtLtEq)
} else {
self.produce_op(Punct::LtLt)
}
}
_ => self.produce_op(Punct::Lt),
}
}
fn minus(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::MinusEq),
Ok('>') => self.consume()?.produce_op(Punct::Arrow),
_ => self.produce_op(Punct::Minus),
}
}
fn plus(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::PlusEq),
_ => self.produce_op(Punct::Plus),
}
}
fn rem(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::RemEq),
_ => self.produce_op(Punct::Rem),
}
}
fn slash(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::SlashEq),
Ok('/') => self.consume()?.line_comment(),
Ok('*') => self.consume()?.block_comment(),
_ => self.produce_op(Punct::Slash),
}
}
fn star(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::StarEq),
_ => self.produce_op(Punct::Star),
}
}
fn xor(&mut self) -> LResult<Token> {
match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::XorEq),
Ok('^') => self.consume()?.produce_op(Punct::XorXor),
_ => self.produce_op(Punct::Xor),
}
}
}
/// Comments
impl<'t> Lexer<'t> {
fn line_comment(&mut self) -> LResult<Token> {
while Ok('\n') != self.peek() {
self.consume()?;
}
self.produce(Kind::Comment, ())
}
fn block_comment(&mut self) -> LResult<Token> {
while let Ok(c) = self.next() {
if '*' == c && Ok('/') == self.next() {
break;
}
}
self.produce(Kind::Comment, ())
}
}
/// Identifiers
impl<'t> Lexer<'t> {
fn identifier(&mut self) -> LResult<Token> {
let mut out = String::from(self.xid_start()?);
while let Ok(c) = self.xid_continue() {
out.push(c)
}
if let Ok(keyword) = Kind::from_str(&out) {
self.produce(keyword, ())
} else {
self.produce(Kind::Identifier, TokenData::String(out))
}
}
fn xid_start(&mut self) -> LResult<char> {
match self.peek()? {
xid if xid == '_' || is_xid_start(xid) => {
self.consume()?;
Ok(xid)
}
bad => Err(Error::not_identifier(bad, self.line(), self.col())),
}
}
fn xid_continue(&mut self) -> LResult<char> {
match self.peek()? {
xid if is_xid_continue(xid) => {
self.consume()?;
Ok(xid)
}
bad => Err(Error::not_identifier(bad, self.line(), self.col())),
}
}
}
/// Integers
impl<'t> Lexer<'t> {
fn int_with_base(&mut self) -> LResult<Token> {
match self.peek() {
Ok('x') => self.consume()?.digits::<16>(),
Ok('d') => self.consume()?.digits::<10>(),
Ok('o') => self.consume()?.digits::<8>(),
Ok('b') => self.consume()?.digits::<2>(),
Ok('0'..='9') => self.digits::<10>(),
_ => self.produce(Kind::Literal, 0),
}
}
fn digits<const B: u32>(&mut self) -> LResult<Token> {
let mut value = self.digit::<B>()? as u128;
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
value = value * B as u128 + self.digit::<B>()? as u128;
}
self.produce(Kind::Literal, value)
}
fn digit<const B: u32>(&mut self) -> LResult<u32> {
let digit = self.peek()?;
self.consume()?;
digit
.to_digit(B)
.ok_or(Error::invalid_digit(digit, self.line(), self.col()))
}
}
/// Strings and characters
impl<'t> Lexer<'t> {
fn string(&mut self) -> LResult<Token> {
let mut value = String::new();
while '"'
!= self
.peek()
.map_err(|e| e.mask_reason(Reason::UnmatchedDelimiters('"')))?
{
value.push(self.unescape()?)
}
self.consume()?.produce(Kind::Literal, value)
}
fn character(&mut self) -> LResult<Token> {
let out = self.unescape()?;
match self.peek()? {
'\'' => self.consume()?.produce(Kind::Literal, out),
_ => Err(Error::unmatched_delimiters('\'', self.line(), self.col())),
}
}
/// Unescape a single character
fn unescape(&mut self) -> LResult<char> {
match self.next() {
Ok('\\') => (),
other => return other,
}
Ok(match self.next()? {
'a' => '\x07',
'b' => '\x08',
'f' => '\x0c',
'n' => '\n',
'r' => '\r',
't' => '\t',
'x' => self.hex_escape()?,
'u' => self.unicode_escape()?,
'0' => '\0',
chr => chr,
})
}
/// unescape a single 2-digit hex escape
fn hex_escape(&mut self) -> LResult<char> {
let out = (self.digit::<16>()? << 4) + self.digit::<16>()?;
char::from_u32(out).ok_or(Error::bad_unicode(out, self.line(), self.col()))
}
/// unescape a single \u{} unicode escape
fn unicode_escape(&mut self) -> LResult<char> {
let mut out = 0;
let Ok('{') = self.peek() else {
return Err(Error::invalid_escape('u', self.line(), self.col()));
};
self.consume()?;
while let Ok(c) = self.peek() {
match c {
'}' => {
self.consume()?;
return char::from_u32(out).ok_or(Error::bad_unicode(
out,
self.line(),
self.col(),
));
}
_ => out = (out << 4) + self.digit::<16>()?,
}
}
Err(Error::invalid_escape('u', self.line(), self.col()))
}
}
impl<'t> From<&Lexer<'t>> for Loc {
fn from(value: &Lexer<'t>) -> Self {
Loc(value.line(), value.col())
}
}
use error::{Error, LResult, Reason};
pub mod error {
//! [Error] type for the [Lexer](super::Lexer)
use std::fmt::Display;
/// Result type with [Err] = [Error]
pub type LResult<T> = Result<T, Error>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error {
pub reason: Reason,
pub line: u32,
pub col: u32,
}
/// The reason for the [Error]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
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 [TokenKind](cl_token::TokenKind)
UnexpectedChar(char),
/// Found a character that's not valid in identifiers while looking for an identifier
NotIdentifier(char),
/// Found a character that's not valid in an escape sequence while looking for an escape
/// sequence
UnknownEscape(char),
/// Escape sequence contains invalid hexadecimal digit or unmatched braces
InvalidEscape(char),
/// Character is not a valid digit in the requested base
InvalidDigit(char),
/// Base conversion requested, but the base character was not in the set of known
/// characters
UnknownBase(char),
/// Unicode escape does not map to a valid unicode code-point
BadUnicode(u32),
/// Reached end of input
EndOfFile,
}
error_impl! {
unmatched_delimiters(c: char) => Reason::UnmatchedDelimiters(c),
unexpected_char(c: char) => Reason::UnexpectedChar(c),
not_identifier(c: char) => Reason::NotIdentifier(c),
unknown_escape(e: char) => Reason::UnknownEscape(e),
invalid_escape(e: char) => Reason::InvalidEscape(e),
invalid_digit(digit: char) => Reason::InvalidDigit(digit),
unknown_base(base: char) => Reason::UnknownBase(base),
bad_unicode(value: u32) => Reason::BadUnicode(value),
end_of_file => Reason::EndOfFile,
}
impl Error {
/// Changes the [Reason] of this error
pub(super) fn mask_reason(self, reason: Reason) -> Self {
Self { reason, ..self }
}
/// Returns the [Reason] for this error
pub fn reason(&self) -> &Reason {
&self.reason
}
/// Returns the (line, col) where the error happened
pub fn location(&self) -> (u32, u32) {
(self.line, self.col)
}
}
macro error_impl ($($fn:ident$(( $($p:ident: $t:ty),* ))? => $reason:expr),*$(,)?) {
#[allow(dead_code)]
impl Error {
$(pub(super) fn $fn ($($($p: $t),*,)? line: u32, col: u32) -> Self {
Self { reason: $reason, line, col }
})*
}
}
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}: {}", self.line, self.col, self.reason)
}
}
impl Display for Reason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Reason::UnmatchedDelimiters(c) => write! {f, "Unmatched `{c}` in input"},
Reason::UnexpectedChar(c) => write!(f, "Character `{c}` not expected"),
Reason::NotIdentifier(c) => write!(f, "Character `{c}` not valid in identifiers"),
Reason::UnknownEscape(c) => write!(f, "`\\{c}` is not a known escape sequence"),
Reason::InvalidEscape(c) => write!(f, "Escape sequence `\\{c}`... is malformed"),
Reason::InvalidDigit(c) => write!(f, "`{c}` is not a valid digit"),
Reason::UnknownBase(c) => write!(f, "`0{c}`... is not a valid base"),
Reason::BadUnicode(c) => write!(f, "`{c}` is not a valid unicode code-point"),
Reason::EndOfFile => write!(f, "Reached end of input"),
}
}
}
}

View File

@@ -0,0 +1,171 @@
use crate::Lexer;
use cl_token::*;
macro test_lexer_output_type ($($f:ident {$($test:expr => $expect:expr),*$(,)?})*) {$(
#[test]
fn $f() {$(
assert_eq!(
Lexer::new($test)
.into_iter()
.map(|t| t.unwrap().ty())
.collect::<Vec<_>>(),
dbg!($expect)
);
)*}
)*}
macro test_lexer_data_type ($($f:ident {$($test:expr => $expect:expr),*$(,)?})*) {$(
#[test]
fn $f() {$(
assert_eq!(
Lexer::new($test)
.into_iter()
.map(|t| t.unwrap().into_data())
.collect::<Vec<_>>(),
dbg!($expect)
);
)*}
)*}
/// Convert an `[ expr, ... ]` into a `[ *, ... ]`
macro td ($($id:expr),*) {
[$($id.into()),*]
}
mod ident {
use super::*;
macro ident ($($id:literal),*) {
[$(TokenData::String($id.into())),*]
}
test_lexer_data_type! {
underscore { "_ _" => ident!["_", "_"] }
unicode { "_ε ε_" => ident!["", "ε_"] }
many_underscore { "____________________________________" =>
ident!["____________________________________"] }
}
}
mod keyword {
use super::*;
macro kw($($k:ident),*) {
[ $(TokenKind::$k,)* ]
}
test_lexer_output_type! {
kw_break { "break break" => kw![Break, Break] }
kw_continue { "continue continue" => kw![Continue, Continue] }
kw_else { "else else" => kw![Else, Else] }
kw_false { "false false" => kw![False, False] }
kw_for { "for for" => kw![For, For] }
kw_fn { "fn fn" => kw![Fn, Fn] }
kw_if { "if if" => kw![If, If] }
kw_in { "in in" => kw![In, In] }
kw_let { "let let" => kw![Let, Let] }
kw_return { "return return" => kw![Return, Return] }
kw_true { "true true" => kw![True, True] }
kw_while { "while while" => kw![While, While] }
keywords { "break continue else false for fn if in let return true while" =>
kw![Break, Continue, Else, False, For, Fn, If, In, Let, Return, True, While] }
}
}
mod integer {
use super::*;
test_lexer_data_type! {
hex {
"0x0 0x1 0x15 0x2100 0x8000" =>
td![0x0, 0x1, 0x15, 0x2100, 0x8000]
}
dec {
"0d0 0d1 0d21 0d8448 0d32768" =>
td![0, 0x1, 0x15, 0x2100, 0x8000]
}
oct {
"0o0 0o1 0o25 0o20400 0o100000" =>
td![0x0, 0x1, 0x15, 0x2100, 0x8000]
}
bin {
"0b0 0b1 0b10101 0b10000100000000 0b1000000000000000" =>
td![0x0, 0x1, 0x15, 0x2100, 0x8000]
}
baseless {
"0 1 21 8448 32768" =>
td![0x0, 0x1, 0x15, 0x2100, 0x8000]
}
}
}
mod string {
use super::*;
test_lexer_data_type! {
empty_string {
"\"\"" =>
td![String::from("")]
}
unicode_string {
"\"I 💙 🦈!\"" =>
td![String::from("I 💙 🦈!")]
}
escape_string {
" \"This is a shark: \\u{1f988}\" " =>
td![String::from("This is a shark: 🦈")]
}
}
}
mod punct {
macro op($op:ident) {
TokenKind::Punct(Punct::$op)
}
use super::*;
test_lexer_output_type! {
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

@@ -0,0 +1,14 @@
[package]
name = "cl-parser"
repository.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
cl-ast = { path = "../cl-ast" }
cl-lexer = { path = "../cl-lexer" }
cl-token = { path = "../cl-token" }
cl-structures = { path = "../cl-structures" }

View File

@@ -0,0 +1,212 @@
use super::*;
use cl_lexer::error::{Error as LexError, Reason};
use std::fmt::Display;
pub type PResult<T> = Result<T, Error>;
/// Contains information about [Parser] errors
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error {
pub reason: ErrorKind,
pub while_parsing: Parsing,
pub loc: Loc,
}
impl std::error::Error for Error {}
/// Represents the reason for parse failure
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ErrorKind {
Lexical(LexError),
EndOfInput,
UnmatchedParentheses,
UnmatchedCurlyBraces,
UnmatchedSquareBrackets,
Unexpected(TokenKind),
ExpectedToken {
want: TokenKind,
got: TokenKind,
},
ExpectedParsing {
want: Parsing,
},
/// Indicates unfinished code
Todo,
}
impl From<LexError> for ErrorKind {
fn from(value: LexError) -> Self {
match value.reason() {
Reason::EndOfFile => Self::EndOfInput,
_ => Self::Lexical(value),
}
}
}
/// Compactly represents the stage of parsing an [Error] originated in
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Parsing {
File,
Attrs,
Meta,
Item,
Visibility,
Mutability,
ItemKind,
Alias,
Const,
Static,
Module,
ModuleKind,
Function,
Param,
Struct,
StructKind,
StructMember,
Enum,
EnumKind,
Variant,
VariantKind,
Impl,
ImplKind,
Ty,
TyKind,
TyTuple,
TyRef,
TyFn,
Stmt,
StmtKind,
Let,
Expr,
ExprKind,
Assign,
AssignKind,
Binary,
BinaryKind,
Unary,
UnaryKind,
Index,
Call,
Member,
PathExpr,
PathPart,
Identifier,
Literal,
Array,
ArrayRep,
AddrOf,
Block,
Group,
Tuple,
Loop,
While,
If,
For,
Else,
Break,
Return,
Continue,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { reason, while_parsing, loc } = self;
match reason {
// TODO entries are debug-printed
ErrorKind::Todo => write!(f, "{loc} {reason} {while_parsing:?}"),
// lexical errors print their own higher-resolution loc info
ErrorKind::Lexical(e) => write!(f, "{e} (while parsing {while_parsing})"),
_ => write!(f, "{loc} {reason} while parsing {while_parsing}"),
}
}
}
impl Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::Lexical(e) => e.fmt(f),
ErrorKind::EndOfInput => write!(f, "End of input"),
ErrorKind::UnmatchedParentheses => write!(f, "Unmatched parentheses"),
ErrorKind::UnmatchedCurlyBraces => write!(f, "Unmatched curly braces"),
ErrorKind::UnmatchedSquareBrackets => write!(f, "Unmatched square brackets"),
ErrorKind::Unexpected(t) => write!(f, "Encountered unexpected token `{t}`"),
ErrorKind::ExpectedToken { want: e, got: g } => write!(f, "Expected `{e}`, got `{g}`"),
ErrorKind::ExpectedParsing { want } => write!(f, "Expected {want}"),
ErrorKind::Todo => write!(f, "TODO:"),
}
}
}
impl Display for Parsing {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Parsing::File => "a file",
Parsing::Attrs => "an attribute-set",
Parsing::Meta => "an attribute",
Parsing::Item => "an item",
Parsing::Visibility => "a visibility qualifier",
Parsing::Mutability => "a mutability qualifier",
Parsing::ItemKind => "an item",
Parsing::Alias => "a type alias",
Parsing::Const => "a const item",
Parsing::Static => "a static variable",
Parsing::Module => "a module",
Parsing::ModuleKind => "a module",
Parsing::Function => "a function",
Parsing::Param => "a function parameter",
Parsing::Struct => "a struct",
Parsing::StructKind => "a struct",
Parsing::StructMember => "a struct member",
Parsing::Enum => "an enum",
Parsing::EnumKind => "an enum",
Parsing::Variant => "an enum variant",
Parsing::VariantKind => "an enum variant",
Parsing::Impl => "an impl block",
Parsing::ImplKind => "the target of an impl block",
Parsing::Ty => "a type",
Parsing::TyKind => "a type",
Parsing::TyTuple => "a tuple of types",
Parsing::TyRef => "a reference type",
Parsing::TyFn => "a function pointer type",
Parsing::Stmt => "a statement",
Parsing::StmtKind => "a statement",
Parsing::Let => "a local variable declaration",
Parsing::Expr => "an expression",
Parsing::ExprKind => "an expression",
Parsing::Assign => "an assignment",
Parsing::AssignKind => "an assignment operator",
Parsing::Binary => "a binary expression",
Parsing::BinaryKind => "a binary operator",
Parsing::Unary => "a unary expression",
Parsing::UnaryKind => "a unary operator",
Parsing::Index => "an indexing expression",
Parsing::Call => "a call expression",
Parsing::Member => "a member access expression",
Parsing::PathExpr => "a path",
Parsing::PathPart => "a path component",
Parsing::Identifier => "an identifier",
Parsing::Literal => "a literal",
Parsing::Array => "an array",
Parsing::ArrayRep => "an array of form [k;N]",
Parsing::AddrOf => "a borrow op",
Parsing::Block => "a block",
Parsing::Group => "a grouped expression",
Parsing::Tuple => "a tuple",
Parsing::Loop => "an unconditional loop expression",
Parsing::While => "a while expression",
Parsing::If => "an if expression",
Parsing::For => "a for expression",
Parsing::Else => "an else block",
Parsing::Break => "a break expression",
Parsing::Return => "a return expression",
Parsing::Continue => "a continue expression",
}
.fmt(f)
}
}

View File

@@ -0,0 +1,16 @@
//! Parses [tokens](cl_token::token) into an [AST](cl_ast)
//!
//! For the full grammar, see [grammar.ebnf][1]
//!
//! [1]: https://git.soft.fish/j/Conlang/src/branch/main/grammar.ebnf
#![warn(clippy::all)]
#![feature(decl_macro)]
pub use parser::Parser;
use cl_structures::span::*;
use cl_token::*;
pub mod error;
pub mod parser;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
[package]
name = "cl-repl"
repository.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cl-ast = { path = "../cl-ast" }
cl-lexer = { path = "../cl-lexer" }
cl-token = { path = "../cl-token" }
cl-parser = { path = "../cl-parser" }
cl-interpret = { path = "../cl-interpret" }
repline = { path = "../../repline" }
argh = "0.1.12"
[dev-dependencies]
cl-typeck = { path = "../cl-typeck" }

View File

@@ -0,0 +1,69 @@
//! This example grabs input from stdin, lexes it, and prints which lexer rules matched
#![allow(unused_imports)]
use cl_lexer::Lexer;
use cl_token::Token;
use std::{
error::Error,
io::{stdin, IsTerminal, Read},
path::{Path, PathBuf},
};
fn main() -> Result<(), Box<dyn Error>> {
let conf = Config::new();
if conf.paths.is_empty() {
take_stdin()?;
} else {
for path in conf.paths.iter().map(PathBuf::as_path) {
lex_tokens(&std::fs::read_to_string(path)?, Some(path))?;
}
}
Ok(())
}
struct Config {
paths: Vec<PathBuf>,
}
impl Config {
fn new() -> Self {
Config { paths: std::env::args().skip(1).map(PathBuf::from).collect() }
}
}
fn take_stdin() -> Result<(), Box<dyn Error>> {
if stdin().is_terminal() {
for line in stdin().lines() {
lex_tokens(&line?, None)?
}
} else {
lex_tokens(&std::io::read_to_string(stdin())?, None)?
}
Ok(())
}
fn lex_tokens(file: &str, path: Option<&Path>) -> Result<(), Box<dyn Error>> {
for token in Lexer::new(file) {
let token = match token {
Ok(t) => t,
Err(e) => {
println!("{e:?}");
continue;
}
};
if let Some(path) = path {
print!("{path:?}:")
}
print_token(token);
}
Ok(())
}
fn print_token(t: Token) {
println!(
"{:02}:{:02}: {:#19}{}",
t.line(),
t.col(),
t.ty(),
t.data(),
)
}

View File

@@ -0,0 +1,179 @@
use cl_ast::{
ast_visitor::fold::Fold,
desugar::{squash_groups::SquashGroups, while_else::WhileElseDesugar},
};
use cl_lexer::Lexer;
use cl_parser::Parser;
use cl_typeck::{
definition::Def, name_collector::NameCollectable, project::Project, type_resolver::resolve,
};
use repline::{error::Error as RlError, prebaked::*};
use std::error::Error;
// Path to display in standard library errors
const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl";
// Statically included standard library
const STDLIB: &str = include_str!("../../../stdlib/lib.cl");
// Colors
const C_MAIN: &str = "";
const C_RESV: &str = "\x1b[35m";
const C_CODE: &str = "\x1b[36m";
const C_LISTING: &str = "\x1b[38;5;117m";
/// A home for immutable intermediate ASTs
///
/// TODO: remove this.
static mut TREES: TreeManager = TreeManager::new();
fn main() -> Result<(), Box<dyn Error>> {
let mut prj = Project::default();
let mut parser = Parser::new(Lexer::new(STDLIB));
let code = match parser.file() {
Ok(code) => code,
Err(e) => {
eprintln!("{STDLIB_DISPLAY_PATH}:{e}");
Err(e)?
}
};
unsafe { TREES.push(code) }.collect_in_root(&mut prj)?;
main_menu(&mut prj)?;
Ok(())
}
fn main_menu(prj: &mut Project) -> Result<(), RlError> {
banner();
read_and(C_MAIN, "mu>", "? >", |line| {
match line.trim() {
"c" | "code" => enter_code(prj)?,
"clear" => clear()?,
"e" | "exit" => return Ok(Response::Break),
"l" | "list" => list_types(prj),
"q" | "query" => query_type_expression(prj)?,
"r" | "resolve" => resolve_all(prj)?,
"d" | "desugar" => live_desugar()?,
"h" | "help" => {
println!(
"Valid commands are:
code (c): Enter code to type-check
list (l): List all known types
query (q): Query the type system
resolve (r): Perform type resolution
desugar (d): WIP: Test the experimental desugaring passes
help (h): Print this list
exit (e): Exit the program"
);
return Ok(Response::Deny);
}
_ => Err(r#"Invalid command. Type "help" to see the list of valid commands."#)?,
}
Ok(Response::Accept)
})
}
fn enter_code(prj: &mut Project) -> Result<(), RlError> {
read_and(C_CODE, "cl>", "? >", |line| {
if line.trim().is_empty() {
return Ok(Response::Break);
}
let code = Parser::new(Lexer::new(line)).file()?;
let code = WhileElseDesugar.fold_file(code);
// Safety: this is totally unsafe
unsafe { TREES.push(code) }.collect_in_root(prj)?;
Ok(Response::Accept)
})
}
fn live_desugar() -> Result<(), RlError> {
read_and(C_RESV, "se>", "? >", |line| {
let code = Parser::new(Lexer::new(line)).stmt()?;
println!("Raw, as parsed:\n{C_LISTING}{code}\x1b[0m");
let code = SquashGroups.fold_stmt(code);
println!("SquashGroups\n{C_LISTING}{code}\x1b[0m");
let code = WhileElseDesugar.fold_stmt(code);
println!("WhileElseDesugar\n{C_LISTING}{code}\x1b[0m");
Ok(Response::Accept)
})
}
fn query_type_expression(prj: &mut Project) -> Result<(), RlError> {
read_and(C_RESV, "ty>", "? >", |line| {
if line.trim().is_empty() {
return Ok(Response::Break);
}
// parse it as a path, and convert the path into a borrowed path
let ty = Parser::new(Lexer::new(line)).ty()?.kind;
let id = prj.evaluate(&ty, prj.root)?;
pretty_def(&prj[id], id);
Ok(Response::Accept)
})
}
fn resolve_all(prj: &mut Project) -> Result<(), Box<dyn Error>> {
for id in prj.pool.key_iter() {
resolve(prj, id)?;
}
println!("Types resolved successfully!");
Ok(())
}
fn list_types(prj: &mut Project) {
println!(" name\x1b[30G type");
for (idx, Def { name, vis, kind, .. }) in prj.pool.iter().enumerate() {
print!("{idx:3}: {vis}");
if name.is_empty() {
print!("\x1b[30m_\x1b[0m")
}
println!("{name}\x1b[30G| {kind}");
}
}
fn pretty_def(def: &Def, id: impl Into<usize>) {
let id = id.into();
let Def { vis, name, kind, module, meta, source } = def;
for meta in *meta {
println!("#[{meta}]")
}
println!("{vis}{name} [id: {id}] = {kind}");
if let Some(source) = source {
println!("Source:\n{C_LISTING}{source}\x1b[0m");
}
println!("\x1b[90m{module}\x1b[0m");
}
fn clear() -> Result<(), Box<dyn Error>> {
println!("\x1b[H\x1b[2J");
banner();
Ok(())
}
fn banner() {
println!(
"--- {} v{} 💪🦈 ---",
env!("CARGO_BIN_NAME"),
env!("CARGO_PKG_VERSION"),
);
}
/// Keeps leaked references to past ASTs, for posterity:tm:
struct TreeManager {
trees: Vec<&'static cl_ast::File>,
}
impl TreeManager {
const fn new() -> Self {
Self { trees: vec![] }
}
fn push(&mut self, tree: cl_ast::File) -> &'static cl_ast::File {
let ptr = Box::leak(Box::new(tree));
self.trees.push(ptr);
ptr
}
}

View File

@@ -0,0 +1,656 @@
//! Pretty prints a conlang AST in yaml
use cl_lexer::Lexer;
use cl_parser::Parser;
use 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, sign, bind, body } = self;
y.key("Function")
.pair("name", name)
.pair("sign", sign)
.pair("bind", bind)
.pair("body", body);
}
}
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 ImplKind {
fn yaml(&self, y: &mut Yamler) {
match self {
ImplKind::Type(t) => y.value(t),
ImplKind::Trait { impl_trait, for_type } => {
y.pair("trait", impl_trait).pair("for_type", for_type)
}
};
}
}
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::Loop(k) => k.yaml(y),
ExprKind::While(k) => k.yaml(y),
ExprKind::If(k) => k.yaml(y),
ExprKind::For(k) => k.yaml(y),
ExprKind::Break(k) => k.yaml(y),
ExprKind::Return(k) => k.yaml(y),
ExprKind::Continue(k) => k.yaml(y),
}
}
}
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")
.pair("count", count)
.yaml(mutable)
.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 Loop {
fn yaml(&self, y: &mut Yamler) {
let Self { body } = self;
y.key("Loop").yaml(body);
}
}
impl Yamlify for While {
fn yaml(&self, y: &mut Yamler) {
let Self { cond, pass, fail } = self;
y.key("While")
.pair("cond", cond)
.pair("pass", pass)
.yaml(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 } = self;
y.key("Param").yaml(mutability).pair("name", name);
}
}
impl Yamlify for Ty {
fn yaml(&self, y: &mut Yamler) {
let Self { extents: _, kind } = self;
y.key("Ty").yaml(kind);
}
}
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, mutable, to } = self;
y.key("TyRef")
.pair("count", count)
.yaml(mutable)
.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

@@ -0,0 +1,14 @@
//! ANSI escape sequences
pub const RED: &str = "\x1b[31m";
pub const GREEN: &str = "\x1b[32m"; // the color of type checker mode
pub const CYAN: &str = "\x1b[36m";
pub const BRIGHT_GREEN: &str = "\x1b[92m";
pub const BRIGHT_BLUE: &str = "\x1b[94m";
pub const BRIGHT_MAGENTA: &str = "\x1b[95m";
pub const BRIGHT_CYAN: &str = "\x1b[96m";
pub const RESET: &str = "\x1b[0m";
pub const OUTPUT: &str = "\x1b[38;5;117m";
pub const CLEAR_LINES: &str = "\x1b[G\x1b[J";
pub const CLEAR_ALL: &str = "\x1b[H\x1b[2J";

View File

@@ -0,0 +1,51 @@
//! Handles argument parsing (currently using the [argh] crate)
use argh::FromArgs;
use std::{io::IsTerminal, path::PathBuf, str::FromStr};
/// The Conlang prototype debug interface
#[derive(Clone, Debug, FromArgs, PartialEq, Eq, PartialOrd, Ord)]
pub struct Args {
/// the main source file
#[argh(positional)]
pub file: Option<PathBuf>,
/// files to include
#[argh(option, short = 'I')]
pub include: Vec<PathBuf>,
/// the CLI operating mode (`f`mt | `l`ex | `r`un)
#[argh(option, short = 'm', default = "Default::default()")]
pub mode: Mode,
/// whether to start the repl (`true` or `false`)
#[argh(option, short = 'r', default = "is_terminal()")]
pub repl: bool,
}
/// 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()
}
/// The CLI's operating mode
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum Mode {
#[default]
Menu,
Lex,
Fmt,
Run,
}
impl FromStr for Mode {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, &'static str> {
Ok(match s {
"f" | "fmt" | "p" | "pretty" => Mode::Fmt,
"l" | "lex" | "tokenize" | "token" => Mode::Lex,
"r" | "run" => Mode::Run,
_ => Err("Recognized modes are: 'r' \"run\", 'f' \"fmt\", 'l' \"lex\"")?,
})
}
}

View File

@@ -0,0 +1,5 @@
use cl_repl::cli::run;
fn main() -> Result<(), Box<dyn std::error::Error>> {
run(argh::from_env())
}

View File

@@ -0,0 +1,89 @@
//! Implement's the command line interface
use crate::{
args::{Args, Mode},
ctx::Context,
menu,
tools::print_token,
};
use cl_interpret::{env::Environment, interpret::Interpret, temp_type_impl::ConValue};
use cl_lexer::Lexer;
use cl_parser::Parser;
use std::{error::Error, path::Path};
/// Run the command line interface
pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
let Args { file, include, mode, repl } = args;
let mut env = Environment::new();
for path in include {
load_file(&mut env, path)?;
}
if repl {
if let Some(file) = file {
load_file(&mut env, file)?;
}
let mut ctx = Context::with_env(env);
match mode {
Mode::Menu => menu::main_menu(&mut ctx)?,
Mode::Lex => menu::lex(&mut ctx)?,
Mode::Fmt => menu::fmt(&mut ctx)?,
Mode::Run => menu::run(&mut ctx)?,
}
} else {
let code = match &file {
Some(file) => std::fs::read_to_string(file)?,
None => std::io::read_to_string(std::io::stdin())?,
};
match mode {
Mode::Lex => lex_code(&code, file),
Mode::Fmt => fmt_code(&code),
Mode::Run | Mode::Menu => run_code(&code, &mut env),
}?;
}
Ok(())
}
fn load_file(
env: &mut Environment,
path: impl AsRef<Path>,
) -> Result<ConValue, Box<dyn Error>> {
let file = std::fs::read_to_string(path)?;
let code = Parser::new(Lexer::new(&file)).file()?;
Ok(env.eval(&code)?)
}
fn lex_code(code: &str, path: Option<impl AsRef<Path>>) -> Result<(), Box<dyn Error>> {
for token in Lexer::new(code) {
if let Some(path) = &path {
print!("{}:", path.as_ref().display());
}
match token {
Ok(token) => print_token(&token),
Err(e) => println!("{e}"),
}
}
Ok(())
}
fn fmt_code(code: &str) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).file()?;
println!("{code}");
Ok(())
}
fn run_code(code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).file()?;
match code.interpret(env)? {
ConValue::Empty => {}
ret => println!("{ret}"),
}
if env.get("main").is_ok() {
match env.call("main", &[])? {
ConValue::Empty => {}
ret => println!("{ret}"),
}
}
Ok(())
}

View File

@@ -0,0 +1,26 @@
use cl_interpret::{
env::Environment, error::IResult, interpret::Interpret, temp_type_impl::ConValue,
};
#[derive(Clone, Debug)]
pub struct Context {
pub env: Environment,
}
impl Context {
pub fn new() -> Self {
Self { env: Environment::new() }
}
pub fn with_env(env: Environment) -> Self {
Self { env }
}
pub fn run(&mut self, code: &impl Interpret) -> IResult<ConValue> {
code.interpret(&mut self.env)
}
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}

View File

@@ -0,0 +1,11 @@
//! The Conlang REPL, based on [repline]
//!
//! Uses [argh] for argument parsing.
#![warn(clippy::all)]
pub mod ansi;
pub mod args;
pub mod cli;
pub mod ctx;
pub mod menu;
pub mod tools;

View File

@@ -0,0 +1,76 @@
use crate::{ansi, ctx};
use cl_lexer::Lexer;
use cl_parser::Parser;
use repline::{error::ReplResult, prebaked::*};
fn clear() {
println!("{}", ansi::CLEAR_ALL);
banner()
}
pub fn banner() {
println!("--- conlang v{} 💪🦈 ---\n", env!("CARGO_PKG_VERSION"))
}
/// Presents a selection interface to the user
pub fn main_menu(ctx: &mut ctx::Context) -> ReplResult<()> {
banner();
read_and(ansi::GREEN, "mu>", " ?>", |line| {
match line.trim() {
"clear" => clear(),
"l" | "lex" => lex(ctx)?,
"f" | "fmt" => fmt(ctx)?,
"r" | "run" => run(ctx)?,
"q" | "quit" => return Ok(Response::Break),
"h" | "help" => println!(
"Valid commands
lex (l): Spin up a lexer, and lex some lines
fmt (f): Format the input
run (r): Enter the REPL, and evaluate some statements
help (h): Print this list
quit (q): Exit the program"
),
_ => Err("Unknown command. Type \"help\" for help")?,
}
Ok(Response::Accept)
})
}
pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
read_and(ansi::CYAN, "cl>", " ?>", |line| {
let code = Parser::new(Lexer::new(line)).stmt()?;
print!("{}", ansi::OUTPUT);
match ctx.run(&code) {
Ok(v) => println!("{}{v}", ansi::RESET),
Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET),
}
Ok(Response::Accept)
})
}
pub fn lex(_ctx: &mut ctx::Context) -> ReplResult<()> {
read_and(ansi::BRIGHT_BLUE, "lx>", " ?>", |line| {
for token in Lexer::new(line) {
match token {
Ok(token) => crate::tools::print_token(&token),
Err(e) => eprintln!("! > {}{e}{}", ansi::RED, ansi::RESET),
}
}
Ok(Response::Accept)
})
}
pub fn fmt(_ctx: &mut ctx::Context) -> ReplResult<()> {
read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| {
let mut p = Parser::new(Lexer::new(line));
match p.stmt() {
Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET),
Err(e) => Err(e)?,
}
Ok(Response::Accept)
})
}

View File

@@ -0,0 +1,11 @@
use cl_token::Token;
/// Prints a token in the particular way [cl-repl](crate) does
pub fn print_token(t: &Token) {
println!(
"{:02}:{:02}: {:#19}{}",
t.line(),
t.col(),
t.ty(),
t.data(),
)
}

View File

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

View File

@@ -0,0 +1,191 @@
//! 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;
use self::iter::InternKeyIter;
/// 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 key_iter(&self) -> iter::InternKeyIter<ID> {
// Safety: Pool currently has pool.len() entries, and data cannot be removed
unsafe { InternKeyIter::new(0..self.pool.len()) }
}
/// Constructs an [ID](InternKey) from a [usize], if it's within bounds
#[doc(hidden)]
pub fn try_key_from(&self, value: usize) -> Option<ID> {
(value < self.pool.len()).then(|| unsafe { ID::from_raw_unchecked(value) })
}
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,
}
}
}
mod iter {
use std::{marker::PhantomData, ops::Range};
use super::InternKey;
/// Iterates over the keys of a [Pool](super::Pool) independently of the pool itself.
///
/// This is guaranteed to never overrun the length of the pool,
/// but is *NOT* guaranteed to iterate over all elements of the pool
/// if the pool is extended during iteration.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct InternKeyIter<ID: InternKey> {
range: Range<usize>,
_id: PhantomData<ID>,
}
impl<ID: InternKey> InternKeyIter<ID> {
/// Creates a new [InternKeyIter] producing the given [InternKey]
///
/// # Safety:
/// - Range must not exceed bounds of the associated [Pool](super::Pool)
/// - Items must not be removed from the pool
/// - Items must be contiguous within the pool
pub(super) unsafe fn new(range: Range<usize>) -> Self {
Self { range, _id: Default::default() }
}
}
impl<ID: InternKey> Iterator for InternKeyIter<ID> {
type Item = ID;
fn next(&mut self) -> Option<Self::Item> {
// Safety: InternKeyIter can only be created by InternKeyIter::new()
Some(unsafe { ID::from_raw_unchecked(self.range.next()?) })
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
}
impl<ID: InternKey> DoubleEndedIterator for InternKeyIter<ID> {
fn next_back(&mut self) -> Option<Self::Item> {
// Safety: see above
Some(unsafe { ID::from_raw_unchecked(self.range.next_back()?) })
}
}
impl<ID: InternKey> ExactSizeIterator for InternKeyIter<ID> {}
}

View File

@@ -0,0 +1,14 @@
//! # Universally useful structures
//! - [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;
pub mod tree;
pub mod stack;
pub mod intern_pool;

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}:")
}
}

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));
}
}

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

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

View File

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

View File

@@ -0,0 +1,42 @@
//! 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 [TokenData]
#[derive(Clone, Debug, PartialEq)]
pub struct Token {
pub ty: TokenKind,
pub data: TokenData,
pub line: u32,
pub col: u32,
}
impl Token {
/// 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 [TokenKind]
pub fn cast(self, ty: TokenKind) -> Self {
Self { ty, ..self }
}
/// Returns the [TokenKind] of this token
pub fn ty(&self) -> TokenKind {
self.ty
}
/// Returns a reference to this token's [TokenData]
pub fn data(&self) -> &TokenData {
&self.data
}
/// Converts this token into its inner [TokenData]
pub fn into_data(self) -> TokenData {
self.data
}
/// Returns the line where this token originated
pub fn line(&self) -> u32 {
self.line
}
/// Returns the column where this token originated
pub fn col(&self) -> u32 {
self.col
}
}

View File

@@ -0,0 +1,41 @@
//! Additional data stored within a [Token](super::Token),
//! external to its [TokenKind](super::token_type::TokenKind)
/// Additional data stored within a [Token](super::Token),
/// external to its [TokenKind](super::token_type::TokenKind)
#[derive(Clone, Debug, PartialEq)]
pub enum TokenData {
/// [Token](super::Token) contains a [String]
String(String),
/// [Token](super::Token) contains a [character](char)
Character(char),
/// [Token](super::Token) contains an [integer](u128)
Integer(u128),
/// [Token](super::Token) contains a [float](f64)
Float(f64),
/// [Token](super::Token) contains no additional data
None,
}
from! {
value: String => Self::String(value),
value: u128 => Self::Integer(value),
value: f64 => Self::Float(value),
value: char => Self::Character(value),
_v: () => Self::None,
}
/// Implements [From] for an enum
macro from($($value:ident: $src:ty => $dst:expr),*$(,)?) {
$(impl From<$src> for TokenData {
fn from($value: $src) -> Self { $dst }
})*
}
impl std::fmt::Display for TokenData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
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

@@ -0,0 +1,240 @@
//! Stores a [Token's](super::Token) lexical information
use std::{fmt::Display, str::FromStr};
/// Stores a [Token's](super::Token) lexical information
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TokenKind {
/// Invalid sequence
Invalid,
/// Any kind of comment
Comment,
/// Any tokenizable literal (See [TokenData](super::TokenData))
Literal,
/// A non-keyword identifier
Identifier,
// A keyword
Break,
Cl,
Const,
Continue,
Else,
Enum,
False,
For,
Fn,
If,
Impl,
In,
Let,
Loop,
Mod,
Mut,
Pub,
Return,
SelfKw,
SelfTy,
Static,
Struct,
Super,
True,
Type,
While,
/// Delimiter or punctuation
Punct(Punct),
}
/// An operator character (delimiter, punctuation)
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Punct {
LCurly, // {
RCurly, // }
LBrack, // [
RBrack, // ]
LParen, // (
RParen, // )
Amp, // &
AmpAmp, // &&
AmpEq, // &=
Arrow, // ->
At, // @
Backslash, // \
Bang, // !
BangBang, // !!
BangEq, // !=
Bar, // |
BarBar, // ||
BarEq, // |=
Colon, // :
ColonColon, // ::
Comma, // ,
Dot, // .
DotDot, // ..
DotDotEq, // ..=
Eq, // =
EqEq, // ==
FatArrow, // =>
Grave, // `
Gt, // >
GtEq, // >=
GtGt, // >>
GtGtEq, // >>=
Hash, // #
HashBang, // #!
Lt, // <
LtEq, // <=
LtLt, // <<
LtLtEq, // <<=
Minus, // -
MinusEq, // -=
Plus, // +
PlusEq, // +=
Question, // ?
Rem, // %
RemEq, // %=
Semi, // ;
Slash, // /
SlashEq, // /=
Star, // *
StarEq, // *=
Tilde, // ~
Xor, // ^
XorEq, // ^=
XorXor, // ^^
}
impl Display for TokenKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TokenKind::Invalid => "invalid".fmt(f),
TokenKind::Comment => "comment".fmt(f),
TokenKind::Literal => "literal".fmt(f),
TokenKind::Identifier => "identifier".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::Loop => "loop".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 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,
"cl" => Self::Cl,
"const" => Self::Const,
"continue" => Self::Continue,
"else" => Self::Else,
"enum" => Self::Enum,
"false" => Self::False,
"for" => Self::For,
"fn" => Self::Fn,
"if" => Self::If,
"impl" => Self::Impl,
"in" => Self::In,
"let" => Self::Let,
"loop" => Self::Loop,
"mod" => Self::Mod,
"mut" => Self::Mut,
"pub" => Self::Pub,
"return" => Self::Return,
"self" => Self::SelfKw,
"Self" => Self::SelfTy,
"static" => Self::Static,
"struct" => Self::Struct,
"super" => Self::Super,
"true" => Self::True,
"type" => Self::Type,
"while" => Self::While,
_ => Err(())?,
})
}
}
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

@@ -0,0 +1,12 @@
[package]
name = "cl-typeck"
repository.workspace = true
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
[dependencies]
cl-ast = { path = "../cl-ast" }
cl-structures = { path = "../cl-structures" }

View File

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

File diff suppressed because it is too large Load Diff