43 Commits

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

Currently implemented as jankily as possible in the interpreter, but it works alright™️
2024-07-26 05:26:08 -05:00
a8b8a91c79 sample-code: print->println to match interpreter behavior 2024-07-26 05:13:52 -05:00
695c812bf5 cl-repl: increase jank: first positional arg is main file, remainder are imports 2024-07-26 05:13:06 -05:00
524c84be9e cl_typeck: Add new primitive types (including joking-point numbers) 2024-07-26 03:24:34 -05:00
4096442f75 cl-typeck: Turn ref into a linked list.
This should be fine, since the only thing you can do with a ref is dereference it.
2024-07-26 02:14:41 -05:00
03a4e76292 cl-typeck: rustfmt implement.rs 2024-07-26 00:15:00 -05:00
46a1639990 sample-code: Have fun with random number generators 2024-07-25 23:59:41 -05:00
5ea8039a8a typeck.rs: Update for new stdlib layout; don't hardcode the root stdlib module. 2024-07-25 07:09:12 -05:00
479efbad73 typeck.rs: Add file-loading mode 2024-07-25 07:08:07 -05:00
a462dd2be3 stdlib: Use Conlang module layout 2024-07-25 07:05:57 -05:00
48 changed files with 2610 additions and 2124 deletions

View File

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

View File

@@ -258,7 +258,6 @@ pub enum TyKind {
Tuple(TyTuple), Tuple(TyTuple),
Ref(TyRef), Ref(TyRef),
Fn(TyFn), Fn(TyFn),
// TODO: slice, array types
} }
/// An array of [`T`](Ty) /// An array of [`T`](Ty)
@@ -323,7 +322,6 @@ pub struct Stmt {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum StmtKind { pub enum StmtKind {
Empty, Empty,
Local(Let),
Item(Box<Item>), Item(Box<Item>),
Expr(Box<Expr>), Expr(Box<Expr>),
} }
@@ -335,15 +333,6 @@ pub enum Semi {
Unterminated, Unterminated,
} }
/// A local variable declaration [Stmt]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Let {
pub mutable: Mutability,
pub name: Sym,
pub ty: Option<Box<Ty>>,
pub init: Option<Box<Expr>>,
}
/// An expression, the beating heart of the language /// An expression, the beating heart of the language
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Expr { pub struct Expr {
@@ -357,6 +346,8 @@ pub enum ExprKind {
/// An empty expression: `(` `)` /// An empty expression: `(` `)`
#[default] #[default]
Empty, Empty,
/// A local bind instruction, `let` [`Sym`] `=` [`Expr`]
Let(Let),
/// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+ /// An [Assign]ment expression: [`Expr`] (`=` [`Expr`])\+
Assign(Assign), Assign(Assign),
/// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+ /// A [Modify]-assignment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
@@ -365,6 +356,8 @@ pub enum ExprKind {
Binary(Binary), Binary(Binary),
/// A [Unary] expression: [`UnaryKind`]\* [`Expr`] /// A [Unary] expression: [`UnaryKind`]\* [`Expr`]
Unary(Unary), Unary(Unary),
/// A [Cast] expression: [`Expr`] `as` [`Ty`]
Cast(Cast),
/// A [Member] access expression: [`Expr`] [`MemberKind`]\* /// A [Member] access expression: [`Expr`] [`MemberKind`]\*
Member(Member), Member(Member),
/// An Array [Index] expression: a[10, 20, 30] /// An Array [Index] expression: a[10, 20, 30]
@@ -388,8 +381,6 @@ pub enum ExprKind {
Group(Group), Group(Group),
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)` /// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
Tuple(Tuple), Tuple(Tuple),
/// A [Loop] expression: `loop` [`Block`]
Loop(Loop),
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]? /// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
While(While), While(While),
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]? /// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
@@ -401,7 +392,16 @@ pub enum ExprKind {
/// A [Return] expression `return` [`Expr`]? /// A [Return] expression `return` [`Expr`]?
Return(Return), Return(Return),
/// A continue expression: `continue` /// A continue expression: `continue`
Continue(Continue), Continue,
}
/// A local variable declaration [Stmt]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Let {
pub mutable: Mutability,
pub name: Sym,
pub ty: Option<Box<Ty>>,
pub init: Option<Box<Expr>>,
} }
/// An [Assign]ment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+ /// An [Assign]ment expression: [`Expr`] ([`ModifyKind`] [`Expr`])\+
@@ -478,12 +478,20 @@ pub enum UnaryKind {
Deref, Deref,
Neg, Neg,
Not, Not,
/// A Loop expression: `loop` [`Block`]
Loop,
/// Unused /// Unused
At, At,
/// Unused /// Unused
Tilde, Tilde,
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Cast {
pub head: Box<ExprKind>,
pub ty: Ty,
}
/// A [Member] access expression: [`Expr`] [`MemberKind`]\* /// A [Member] access expression: [`Expr`] [`MemberKind`]\*
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Member { pub struct Member {
@@ -560,12 +568,6 @@ pub struct Tuple {
pub exprs: Vec<Expr>, 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`]? /// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct While { pub struct While {
@@ -608,7 +610,3 @@ pub struct Break {
pub struct Return { pub struct Return {
pub body: Option<Box<Expr>>, pub body: Option<Box<Expr>>,
} }
/// A continue expression: `continue`
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Continue;

View File

@@ -48,9 +48,9 @@ mod display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Literal::Bool(v) => v.fmt(f), Literal::Bool(v) => v.fmt(f),
Literal::Char(v) => write!(f, "'{v}'"), Literal::Char(v) => write!(f, "'{}'", v.escape_debug()),
Literal::Int(v) => v.fmt(f), Literal::Int(v) => v.fmt(f),
Literal::String(v) => write!(f, "\"{v}\""), Literal::String(v) => write!(f, "\"{}\"", v.escape_debug()),
} }
} }
} }
@@ -389,7 +389,6 @@ mod display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
StmtKind::Empty => Ok(()), StmtKind::Empty => Ok(()),
StmtKind::Local(v) => v.fmt(f),
StmtKind::Item(v) => v.fmt(f), StmtKind::Item(v) => v.fmt(f),
StmtKind::Expr(v) => v.fmt(f), StmtKind::Expr(v) => v.fmt(f),
} }
@@ -405,20 +404,6 @@ mod display {
} }
} }
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 { impl Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f) self.kind.fmt(f)
@@ -429,10 +414,12 @@ mod display {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
ExprKind::Empty => "()".fmt(f), ExprKind::Empty => "()".fmt(f),
ExprKind::Let(v) => v.fmt(f),
ExprKind::Assign(v) => v.fmt(f), ExprKind::Assign(v) => v.fmt(f),
ExprKind::Modify(v) => v.fmt(f), ExprKind::Modify(v) => v.fmt(f),
ExprKind::Binary(v) => v.fmt(f), ExprKind::Binary(v) => v.fmt(f),
ExprKind::Unary(v) => v.fmt(f), ExprKind::Unary(v) => v.fmt(f),
ExprKind::Cast(v) => v.fmt(f),
ExprKind::Member(v) => v.fmt(f), ExprKind::Member(v) => v.fmt(f),
ExprKind::Index(v) => v.fmt(f), ExprKind::Index(v) => v.fmt(f),
ExprKind::Structor(v) => v.fmt(f), ExprKind::Structor(v) => v.fmt(f),
@@ -444,17 +431,30 @@ mod display {
ExprKind::Block(v) => v.fmt(f), ExprKind::Block(v) => v.fmt(f),
ExprKind::Group(v) => v.fmt(f), ExprKind::Group(v) => v.fmt(f),
ExprKind::Tuple(v) => v.fmt(f), ExprKind::Tuple(v) => v.fmt(f),
ExprKind::Loop(v) => v.fmt(f),
ExprKind::While(v) => v.fmt(f), ExprKind::While(v) => v.fmt(f),
ExprKind::If(v) => v.fmt(f), ExprKind::If(v) => v.fmt(f),
ExprKind::For(v) => v.fmt(f), ExprKind::For(v) => v.fmt(f),
ExprKind::Break(v) => v.fmt(f), ExprKind::Break(v) => v.fmt(f),
ExprKind::Return(v) => v.fmt(f), ExprKind::Return(v) => v.fmt(f),
ExprKind::Continue(_) => "continue".fmt(f), ExprKind::Continue => "continue".fmt(f),
} }
} }
} }
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 Assign { impl Display for Assign {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { parts } = self; let Self { parts } = self;
@@ -538,6 +538,7 @@ mod display {
impl Display for UnaryKind { impl Display for UnaryKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
UnaryKind::Loop => "loop ",
UnaryKind::Deref => "*", UnaryKind::Deref => "*",
UnaryKind::Neg => "-", UnaryKind::Neg => "-",
UnaryKind::Not => "!", UnaryKind::Not => "!",
@@ -548,6 +549,13 @@ mod display {
} }
} }
impl Display for Cast {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, ty } = self;
write!(f, "{head} as {ty}")
}
}
impl Display for Member { impl Display for Member {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { head, kind } = self; let Self { head, kind } = self;
@@ -617,7 +625,12 @@ mod display {
impl Display for Block { impl Display for Block {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.stmts, "\n")(f.delimit(BRACES)) let Self { stmts } = self;
match stmts.as_slice() {
[] => "{}".fmt(f),
stmts => separate(stmts, "\n")(f.delimit(BRACES)),
}
} }
} }
@@ -629,14 +642,13 @@ mod display {
impl Display for Tuple { impl Display for Tuple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.exprs, ", ")(f.delimit(INLINE_PARENS)) let Self { exprs } = self;
}
}
impl Display for Loop { match exprs.as_slice() {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { [] => write!(f, "()"),
let Self { body } = self; [expr] => write!(f, "({expr},)"),
write!(f, "loop {body}") exprs => separate(exprs, ", ")(f.delimit(INLINE_PARENS)),
}
} }
} }
@@ -689,12 +701,6 @@ mod display {
} }
} }
} }
impl Display for Continue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
"continue".fmt(f)
}
}
} }
mod convert { mod convert {
@@ -755,15 +761,16 @@ mod convert {
TyFn => TyKind::Fn, TyFn => TyKind::Fn,
} }
impl From for StmtKind { impl From for StmtKind {
Let => StmtKind::Local,
Item => StmtKind::Item, Item => StmtKind::Item,
Expr => StmtKind::Expr, Expr => StmtKind::Expr,
} }
impl From for ExprKind { impl From for ExprKind {
Let => ExprKind::Let,
Assign => ExprKind::Assign, Assign => ExprKind::Assign,
Modify => ExprKind::Modify, Modify => ExprKind::Modify,
Binary => ExprKind::Binary, Binary => ExprKind::Binary,
Unary => ExprKind::Unary, Unary => ExprKind::Unary,
Cast => ExprKind::Cast,
Member => ExprKind::Member, Member => ExprKind::Member,
Index => ExprKind::Index, Index => ExprKind::Index,
Path => ExprKind::Path, Path => ExprKind::Path,
@@ -774,13 +781,11 @@ mod convert {
Block => ExprKind::Block, Block => ExprKind::Block,
Group => ExprKind::Group, Group => ExprKind::Group,
Tuple => ExprKind::Tuple, Tuple => ExprKind::Tuple,
Loop => ExprKind::Loop,
While => ExprKind::While, While => ExprKind::While,
If => ExprKind::If, If => ExprKind::If,
For => ExprKind::For, For => ExprKind::For,
Break => ExprKind::Break, Break => ExprKind::Break,
Return => ExprKind::Return, Return => ExprKind::Return,
Continue => ExprKind::Continue,
} }
impl From for Literal { impl From for Literal {
bool => Literal::Bool, bool => Literal::Bool,

View File

@@ -276,6 +276,10 @@ pub trait Fold {
fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind { fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind {
kind kind
} }
fn fold_cast(&mut self, cast: Cast) -> Cast {
let Cast { head, ty } = cast;
Cast { head: Box::new(self.fold_expr_kind(*head)), ty: self.fold_ty(ty) }
}
fn fold_member(&mut self, m: Member) -> Member { fn fold_member(&mut self, m: Member) -> Member {
let Member { head, kind } = m; let Member { head, kind } = m;
Member { head: Box::new(self.fold_expr_kind(*head)), kind: self.fold_member_kind(kind) } Member { head: Box::new(self.fold_expr_kind(*head)), kind: self.fold_member_kind(kind) }
@@ -334,10 +338,6 @@ pub trait Fold {
let Tuple { exprs } = t; let Tuple { exprs } = t;
Tuple { exprs: exprs.into_iter().map(|e| self.fold_expr(e)).collect() } 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 { fn fold_while(&mut self, w: While) -> While {
let While { cond, pass, fail } = w; let While { cond, pass, fail } = w;
While { While {
@@ -375,10 +375,6 @@ pub trait Fold {
let Return { body } = r; let Return { body } = r;
Return { body: body.map(|e| Box::new(self.fold_expr(*e))) } Return { body: body.map(|e| Box::new(self.fold_expr(*e))) }
} }
fn fold_continue(&mut self, c: Continue) -> Continue {
let Continue = c;
Continue
}
} }
#[inline] #[inline]
@@ -521,7 +517,6 @@ pub fn or_fold_ty_kind<F: Fold + ?Sized>(folder: &mut F, kind: TyKind) -> TyKind
pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> StmtKind { pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> StmtKind {
match kind { match kind {
StmtKind::Empty => StmtKind::Empty, 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::Item(i) => StmtKind::Item(Box::new(folder.fold_item(*i))),
StmtKind::Expr(e) => StmtKind::Expr(Box::new(folder.fold_expr(*e))), StmtKind::Expr(e) => StmtKind::Expr(Box::new(folder.fold_expr(*e))),
} }
@@ -531,10 +526,12 @@ pub fn or_fold_stmt_kind<F: Fold + ?Sized>(folder: &mut F, kind: StmtKind) -> St
pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> ExprKind { pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> ExprKind {
match kind { match kind {
ExprKind::Empty => ExprKind::Empty, ExprKind::Empty => ExprKind::Empty,
ExprKind::Let(l) => ExprKind::Let(folder.fold_let(l)),
ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)), ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)),
ExprKind::Modify(m) => ExprKind::Modify(folder.fold_modify(m)), ExprKind::Modify(m) => ExprKind::Modify(folder.fold_modify(m)),
ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)), ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)),
ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)), ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)),
ExprKind::Cast(c) => ExprKind::Cast(folder.fold_cast(c)),
ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)), ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)),
ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)), ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)),
ExprKind::Structor(s) => ExprKind::Structor(folder.fold_structor(s)), ExprKind::Structor(s) => ExprKind::Structor(folder.fold_structor(s)),
@@ -546,13 +543,12 @@ pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> Ex
ExprKind::Block(b) => ExprKind::Block(folder.fold_block(b)), ExprKind::Block(b) => ExprKind::Block(folder.fold_block(b)),
ExprKind::Group(g) => ExprKind::Group(folder.fold_group(g)), ExprKind::Group(g) => ExprKind::Group(folder.fold_group(g)),
ExprKind::Tuple(t) => ExprKind::Tuple(folder.fold_tuple(t)), 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::While(w) => ExprKind::While(folder.fold_while(w)),
ExprKind::If(i) => ExprKind::If(folder.fold_if(i)), ExprKind::If(i) => ExprKind::If(folder.fold_if(i)),
ExprKind::For(f) => ExprKind::For(folder.fold_for(f)), ExprKind::For(f) => ExprKind::For(folder.fold_for(f)),
ExprKind::Break(b) => ExprKind::Break(folder.fold_break(b)), ExprKind::Break(b) => ExprKind::Break(folder.fold_break(b)),
ExprKind::Return(r) => ExprKind::Return(folder.fold_return(r)), ExprKind::Return(r) => ExprKind::Return(folder.fold_return(r)),
ExprKind::Continue(c) => ExprKind::Continue(folder.fold_continue(c)), ExprKind::Continue => ExprKind::Continue,
} }
} }
pub fn or_fold_member_kind<F: Fold + ?Sized>(folder: &mut F, kind: MemberKind) -> MemberKind { pub fn or_fold_member_kind<F: Fold + ?Sized>(folder: &mut F, kind: MemberKind) -> MemberKind {

View File

@@ -238,6 +238,11 @@ pub trait Visit<'a>: Sized {
self.visit_expr_kind(tail); self.visit_expr_kind(tail);
} }
fn visit_unary_kind(&mut self, _kind: &'a UnaryKind) {} fn visit_unary_kind(&mut self, _kind: &'a UnaryKind) {}
fn visit_cast(&mut self, cast: &'a Cast) {
let Cast { head, ty } = cast;
self.visit_expr_kind(head);
self.visit_ty(ty);
}
fn visit_member(&mut self, m: &'a Member) { fn visit_member(&mut self, m: &'a Member) {
let Member { head, kind } = m; let Member { head, kind } = m;
self.visit_expr_kind(head); self.visit_expr_kind(head);
@@ -289,10 +294,6 @@ pub trait Visit<'a>: Sized {
let Tuple { exprs } = t; let Tuple { exprs } = t;
exprs.iter().for_each(|e| self.visit_expr(e)) 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) { fn visit_while(&mut self, w: &'a While) {
let While { cond, pass, fail } = w; let While { cond, pass, fail } = w;
self.visit_expr(cond); self.visit_expr(cond);
@@ -330,9 +331,7 @@ pub trait Visit<'a>: Sized {
self.visit_expr(body) self.visit_expr(body)
} }
} }
fn visit_continue(&mut self, c: &'a Continue) { fn visit_continue(&mut self) {}
let Continue = c;
}
} }
pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) { pub fn or_visit_literal<'a, V: Visit<'a>>(visitor: &mut V, l: &'a Literal) {
@@ -443,7 +442,6 @@ pub fn or_visit_ty_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a TyKind) {
pub fn or_visit_stmt_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StmtKind) { pub fn or_visit_stmt_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StmtKind) {
match kind { match kind {
StmtKind::Empty => {} StmtKind::Empty => {}
StmtKind::Local(l) => visitor.visit_let(l),
StmtKind::Item(i) => visitor.visit_item(i), StmtKind::Item(i) => visitor.visit_item(i),
StmtKind::Expr(e) => visitor.visit_expr(e), StmtKind::Expr(e) => visitor.visit_expr(e),
} }
@@ -452,10 +450,12 @@ pub fn or_visit_stmt_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a StmtKind)
pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) { pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) {
match e { match e {
ExprKind::Empty => {} ExprKind::Empty => {}
ExprKind::Let(l) => visitor.visit_let(l),
ExprKind::Assign(a) => visitor.visit_assign(a), ExprKind::Assign(a) => visitor.visit_assign(a),
ExprKind::Modify(m) => visitor.visit_modify(m), ExprKind::Modify(m) => visitor.visit_modify(m),
ExprKind::Binary(b) => visitor.visit_binary(b), ExprKind::Binary(b) => visitor.visit_binary(b),
ExprKind::Unary(u) => visitor.visit_unary(u), ExprKind::Unary(u) => visitor.visit_unary(u),
ExprKind::Cast(c) => visitor.visit_cast(c),
ExprKind::Member(m) => visitor.visit_member(m), ExprKind::Member(m) => visitor.visit_member(m),
ExprKind::Index(i) => visitor.visit_index(i), ExprKind::Index(i) => visitor.visit_index(i),
ExprKind::Structor(s) => visitor.visit_structor(s), ExprKind::Structor(s) => visitor.visit_structor(s),
@@ -467,13 +467,12 @@ pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) {
ExprKind::Block(b) => visitor.visit_block(b), ExprKind::Block(b) => visitor.visit_block(b),
ExprKind::Group(g) => visitor.visit_group(g), ExprKind::Group(g) => visitor.visit_group(g),
ExprKind::Tuple(t) => visitor.visit_tuple(t), ExprKind::Tuple(t) => visitor.visit_tuple(t),
ExprKind::Loop(l) => visitor.visit_loop(l),
ExprKind::While(w) => visitor.visit_while(w), ExprKind::While(w) => visitor.visit_while(w),
ExprKind::If(i) => visitor.visit_if(i), ExprKind::If(i) => visitor.visit_if(i),
ExprKind::For(f) => visitor.visit_for(f), ExprKind::For(f) => visitor.visit_for(f),
ExprKind::Break(b) => visitor.visit_break(b), ExprKind::Break(b) => visitor.visit_break(b),
ExprKind::Return(r) => visitor.visit_return(r), ExprKind::Return(r) => visitor.visit_return(r),
ExprKind::Continue(c) => visitor.visit_continue(c), ExprKind::Continue => visitor.visit_continue(),
} }
} }
pub fn or_visit_member_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a MemberKind) { pub fn or_visit_member_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a MemberKind) {

View File

@@ -26,8 +26,8 @@ fn desugar_while(extents: Span, kind: ExprKind) -> ExprKind {
let break_expr = Expr { extents: fail_span, kind: ExprKind::Break(Break { body }) }; 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 = If { cond, pass, fail: Else { body: Some(Box::new(break_expr)) } };
let loop_body = Expr { extents, kind: ExprKind::If(loop_body) }; let loop_body = ExprKind::If(loop_body);
ExprKind::Loop(Loop { body: Box::new(loop_body) }) ExprKind::Unary(Unary { kind: UnaryKind::Loop, tail: Box::new(loop_body) })
} }
_ => kind, _ => kind,
} }

View File

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

View File

@@ -10,6 +10,7 @@ use cl_ast::Sym;
use std::{ use std::{
io::{stdout, Write}, io::{stdout, Write},
rc::Rc, rc::Rc,
slice,
}; };
builtins! { builtins! {
@@ -57,6 +58,19 @@ builtins! {
println!("{}", *env); println!("{}", *env);
Ok(ConValue::Empty) Ok(ConValue::Empty)
} }
pub fn len<env, _>(list) -> IResult<ConValue> {
Ok(ConValue::Int(match list {
ConValue::Empty => 0,
ConValue::String(s) => s.chars().count() as _,
ConValue::Ref(r) => return len.call(env, slice::from_ref(r.as_ref())),
ConValue::Array(t) => t.len() as _,
ConValue::Tuple(t) => t.len() as _,
ConValue::RangeExc(start, end) => (end - start) as _,
ConValue::RangeInc(start, end) => (end - start + 1) as _,
_ => Err(Error::TypeError)?,
}))
}
} }
builtins! { builtins! {
const BINARY; const BINARY;

View File

@@ -0,0 +1,291 @@
//! Values in the dynamically typed AST interpreter.
//!
//! The most permanent fix is a temporary one.
use cl_ast::Sym;
use super::{
error::{Error, IResult},
function::Function,
BuiltIn, Callable, Environment,
};
use std::{ops::*, rc::Rc};
type Integer = isize;
/// A Conlang value stores data in the interpreter
#[derive(Clone, Debug, Default)]
pub enum ConValue {
/// The empty/unit `()` type
#[default]
Empty,
/// An integer
Int(Integer),
/// A boolean
Bool(bool),
/// A unicode character
Char(char),
/// A string
String(Sym),
/// A reference
Ref(Rc<ConValue>),
/// An Array
Array(Rc<[ConValue]>),
/// A tuple
Tuple(Rc<[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)?
};
match self {
ConValue::String(string) => string
.chars()
.nth(*index as _)
.map(ConValue::Char)
.ok_or(Error::OobIndex(*index as usize, string.chars().count())),
ConValue::Array(arr) => arr
.get(*index as usize)
.cloned()
.ok_or(Error::OobIndex(*index as usize, arr.len())),
_ => Err(Error::TypeError),
}
}
cmp! {
lt: false, <;
lt_eq: true, <=;
eq: true, ==;
neq: false, !=;
gt_eq: true, >=;
gt: false, >;
}
assign! {
add_assign: +;
bitand_assign: &;
bitor_assign: |;
bitxor_assign: ^;
div_assign: /;
mul_assign: *;
rem_assign: %;
shl_assign: <<;
shr_assign: >>;
sub_assign: -;
}
}
impl Callable for ConValue {
fn name(&self) -> Sym {
match self {
ConValue::Function(func) => func.name(),
ConValue::BuiltIn(func) => func.name(),
_ => "".into(),
}
}
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()) }
})*
}
impl From<&Sym> for ConValue {
fn from(value: &Sym) -> Self {
ConValue::String(*value)
}
}
from! {
Integer => ConValue::Int,
bool => ConValue::Bool,
char => ConValue::Char,
Sym => ConValue::String,
&str => ConValue::String,
String => ConValue::String,
Rc<str> => 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.wrapping_add(b)),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
(ConValue::Char(a), ConValue::Char(b)) => {
ConValue::String([a, b].into_iter().collect::<String>().into())
}
_ => Err(Error::TypeError)?
]
BitAnd: bitand = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError)?
]
BitOr: bitor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError)?
]
BitXor: bitxor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError)?
]
Div: div = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
eprintln!("Warning: Divide by zero in {a} / {b}"); a
})),
_ => Err(Error::TypeError)?
]
Mul: mul = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
_ => Err(Error::TypeError)?
]
Rem: rem = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
eprintln!("Warning: Divide by zero in {a} % {b}"); a
})),
_ => Err(Error::TypeError)?
]
Shl: shl = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
_ => Err(Error::TypeError)?
]
Shr: shr = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
_ => Err(Error::TypeError)?
]
Sub: sub = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
_ => 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::Ref(v) => write!(f, "&{v}"),
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())
}
}
}
}

View File

@@ -0,0 +1,165 @@
//! Lexical and non-lexical scoping for variables
use super::{
builtin::{BINARY, MISC, RANGE, UNARY},
convalue::ConValue,
error::{Error, IResult},
function::Function,
BuiltIn, Callable, Interpret,
};
use cl_ast::{Function as FnDecl, Sym};
use std::{
collections::HashMap,
fmt::Display,
ops::{Deref, DerefMut},
};
type StackFrame = HashMap<Sym, Option<ConValue>>;
/// Implements a nested lexical scope
#[derive(Clone, Debug)]
pub struct Environment {
frames: Vec<(StackFrame, &'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<Sym, Option<ConValue>> {
from.iter().map(|&v| (v.name(), 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: Sym, 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: Sym) -> 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))
}
/// Resolves a variable immutably.
///
/// Returns a reference to the variable's contents, if it is defined and initialized.
pub fn get(&self, id: Sym) -> IResult<ConValue> {
for (frame, _) in self.frames.iter().rev() {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
Err(Error::NotDefined(id))
}
/// Inserts a new [ConValue] into this [Environment]
pub fn insert(&mut self, id: Sym, value: Option<ConValue>) {
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(id, value);
}
}
/// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) {
let FnDecl { name, .. } = decl;
let (name, function) = (name, 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();
}
}

View File

@@ -0,0 +1,91 @@
//! The [Error] type represents any error thrown by the [Environment](super::Environment)
use cl_ast::Sym;
use super::convalue::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(Sym),
/// A name was defined but not initialized
NotInitialized(Sym),
/// A value was called, but is not callable
NotCallable(ConValue),
/// A function was called with the wrong number of arguments
ArgNumber {
want: usize,
got: usize,
},
Outlined(Sym),
}
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::Outlined(name) => {
write!(f, "Module {name} specified, but not imported.")
}
}
}
}

View File

@@ -0,0 +1,49 @@
//! Represents a block of code which lives inside the Interpreter
use super::{Callable, ConValue, Environment, Error, IResult, Interpret};
use cl_ast::{Function as FnDecl, Param, Sym};
use std::rc::Rc;
/// Represents a block of code which persists inside the Interpreter
#[derive(Clone, Debug)]
pub struct Function {
/// Stores the contents of the function declaration
decl: Rc<FnDecl>,
// /// Stores 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) -> Sym {
let FnDecl { name, .. } = *self.decl;
name
}
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
let FnDecl { name, bind, body, sign: _ } = &*self.decl;
// Check arg mapping
if args.len() != bind.len() {
return Err(Error::ArgNumber { want: bind.len(), got: args.len() });
}
let Some(body) = body else {
return Err(Error::NotDefined(*name));
};
// TODO: completely refactor data storage
let mut frame = env.frame("fn args");
for (Param { mutability: _, name }, value) in bind.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,
}
}
}

View File

@@ -105,7 +105,6 @@ impl Interpret for Stmt {
let Self { extents: _, kind, semi } = self; let Self { extents: _, kind, semi } = self;
let out = match kind { let out = match kind {
StmtKind::Empty => ConValue::Empty, StmtKind::Empty => ConValue::Empty,
StmtKind::Local(stmt) => stmt.interpret(env)?,
StmtKind::Item(stmt) => stmt.interpret(env)?, StmtKind::Item(stmt) => stmt.interpret(env)?,
StmtKind::Expr(stmt) => stmt.interpret(env)?, StmtKind::Expr(stmt) => stmt.interpret(env)?,
}; };
@@ -134,10 +133,12 @@ impl Interpret for ExprKind {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
match self { match self {
ExprKind::Empty => Ok(ConValue::Empty), ExprKind::Empty => Ok(ConValue::Empty),
ExprKind::Let(v) => v.interpret(env),
ExprKind::Assign(v) => v.interpret(env), ExprKind::Assign(v) => v.interpret(env),
ExprKind::Modify(v) => v.interpret(env), ExprKind::Modify(v) => v.interpret(env),
ExprKind::Binary(v) => v.interpret(env), ExprKind::Binary(v) => v.interpret(env),
ExprKind::Unary(v) => v.interpret(env), ExprKind::Unary(v) => v.interpret(env),
ExprKind::Cast(v) => v.interpret(env),
ExprKind::Member(v) => v.interpret(env), ExprKind::Member(v) => v.interpret(env),
ExprKind::Index(v) => v.interpret(env), ExprKind::Index(v) => v.interpret(env),
ExprKind::Structor(v) => v.interpret(env), ExprKind::Structor(v) => v.interpret(env),
@@ -149,13 +150,12 @@ impl Interpret for ExprKind {
ExprKind::Block(v) => v.interpret(env), ExprKind::Block(v) => v.interpret(env),
ExprKind::Group(v) => v.interpret(env), ExprKind::Group(v) => v.interpret(env),
ExprKind::Tuple(v) => v.interpret(env), ExprKind::Tuple(v) => v.interpret(env),
ExprKind::Loop(v) => v.interpret(env),
ExprKind::While(v) => v.interpret(env), ExprKind::While(v) => v.interpret(env),
ExprKind::If(v) => v.interpret(env), ExprKind::If(v) => v.interpret(env),
ExprKind::For(v) => v.interpret(env), ExprKind::For(v) => v.interpret(env),
ExprKind::Break(v) => v.interpret(env), ExprKind::Break(v) => v.interpret(env),
ExprKind::Return(v) => v.interpret(env), ExprKind::Return(v) => v.interpret(env),
ExprKind::Continue(v) => v.interpret(env), ExprKind::Continue => Err(Error::Continue),
} }
} }
} }
@@ -321,12 +321,28 @@ impl Interpret for Binary {
impl Interpret for Unary { impl Interpret for Unary {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Unary { kind, tail } = self; let Unary { kind, tail } = self;
let operand = tail.interpret(env)?;
match kind { match kind {
UnaryKind::Deref => env.call("deref".into(), &[operand]), UnaryKind::Loop => loop {
UnaryKind::Neg => env.call("neg".into(), &[operand]), match tail.interpret(env) {
UnaryKind::Not => env.call("not".into(), &[operand]), Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,
e => e?,
};
},
UnaryKind::Deref => {
let operand = tail.interpret(env)?;
env.call("deref".into(), &[operand])
}
UnaryKind::Neg => {
let operand = tail.interpret(env)?;
env.call("neg".into(), &[operand])
}
UnaryKind::Not => {
let operand = tail.interpret(env)?;
env.call("not".into(), &[operand])
}
UnaryKind::At => { UnaryKind::At => {
let operand = tail.interpret(env)?;
println!("{operand}"); println!("{operand}");
Ok(operand) Ok(operand)
} }
@@ -334,6 +350,48 @@ impl Interpret for Unary {
} }
} }
} }
fn cast(value: ConValue, ty: Sym) -> IResult<ConValue> {
let value = match value {
ConValue::Empty => 0,
ConValue::Int(i) => i as _,
ConValue::Bool(b) => b as _,
ConValue::Char(c) => c as _,
ConValue::Ref(v) => return cast((*v).clone(), ty),
_ => Err(Error::TypeError)?,
};
Ok(match &*ty {
"u8" => ConValue::Int(value as u8 as _),
"i8" => ConValue::Int(value as i8 as _),
"u16" => ConValue::Int(value as u16 as _),
"i16" => ConValue::Int(value as i16 as _),
"u32" => ConValue::Int(value as u32 as _),
"i32" => ConValue::Int(value as i32 as _),
"u64" => ConValue::Int(value),
"i64" => ConValue::Int(value),
"char" => ConValue::Char(char::from_u32(value as _).unwrap_or('\u{fffd}')),
"bool" => ConValue::Bool(value < 0),
_ => Err(Error::NotDefined(ty))?,
})
}
impl Interpret for Cast {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Cast { head, ty } = self;
let value = head.interpret(env)?;
if TyKind::Empty == ty.kind {
return Ok(ConValue::Empty);
};
let TyKind::Path(Path { absolute: false, parts }) = &ty.kind else {
Err(Error::TypeError)?
};
match parts.as_slice() {
[PathPart::Ident(ty)] => cast(value, *ty),
_ => Err(Error::TypeError),
}
}
}
impl Interpret for Member { impl Interpret for Member {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Member { head, kind } = self; let Member { head, kind } = self;
@@ -461,18 +519,6 @@ impl Interpret for Tuple {
)) ))
} }
} }
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 { impl Interpret for While {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { cond, pass, fail } = self; let Self { cond, pass, fail } = self;
@@ -532,11 +578,6 @@ impl Interpret for Else {
} }
} }
} }
impl Interpret for Continue {
fn interpret(&self, _env: &mut Environment) -> IResult<ConValue> {
Err(Error::Continue)
}
}
impl Interpret for Return { impl Interpret for Return {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { body } = self; let Self { body } = self;

View File

@@ -22,611 +22,17 @@ pub trait BuiltIn: std::fmt::Debug + Callable {
fn description(&self) -> &str; fn description(&self) -> &str;
} }
pub mod convalue { pub mod convalue;
//! Values in the dynamically typed AST interpreter.
//!
//! The most permanent fix is a temporary one.
use cl_ast::Sym;
use super::{
error::{Error, IResult},
function::Function,
BuiltIn, Callable, Environment,
};
use std::{ops::*, rc::Rc};
type Integer = isize;
/// A Conlang value stores data in the interpreter
#[derive(Clone, Debug, Default)]
pub enum ConValue {
/// The empty/unit `()` type
#[default]
Empty,
/// An integer
Int(Integer),
/// A boolean
Bool(bool),
/// A unicode character
Char(char),
/// A string
String(Sym),
/// A reference
Ref(Rc<ConValue>),
/// An Array
Array(Rc<[ConValue]>),
/// A tuple
Tuple(Rc<[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) -> Sym {
match self {
ConValue::Function(func) => func.name(),
ConValue::BuiltIn(func) => func.name(),
_ => "".into(),
}
}
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()) }
})*
}
impl From<&Sym> for ConValue {
fn from(value: &Sym) -> Self {
ConValue::String(*value)
}
}
from! {
Integer => ConValue::Int,
bool => ConValue::Bool,
char => ConValue::Char,
Sym => ConValue::String,
&str => ConValue::String,
String => ConValue::String,
Rc<str> => 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.wrapping_add(b)),
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
(ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(c); s.into() }
(ConValue::Char(a), ConValue::Char(b)) => {
ConValue::String([a, b].into_iter().collect::<String>().into())
}
_ => Err(Error::TypeError)?
]
BitAnd: bitand = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
_ => Err(Error::TypeError)?
]
BitOr: bitor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
_ => Err(Error::TypeError)?
]
BitXor: bitxor = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
_ => Err(Error::TypeError)?
]
Div: div = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_div(b).unwrap_or_else(|| {
eprintln!("Warning: Divide by zero in {a} / {b}"); a
})),
_ => Err(Error::TypeError)?
]
Mul: mul = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_mul(b)),
_ => Err(Error::TypeError)?
]
Rem: rem = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.checked_rem(b).unwrap_or_else(|| {
eprintln!("Warning: Divide by zero in {a} % {b}"); a
})),
_ => Err(Error::TypeError)?
]
Shl: shl = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shl(b as _)),
_ => Err(Error::TypeError)?
]
Shr: shr = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_shr(b as _)),
_ => Err(Error::TypeError)?
]
Sub: sub = [
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a.wrapping_sub(b)),
_ => 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::Ref(v) => write!(f, "&{v}"),
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 interpret;
pub mod function { 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, Param, Sym};
use std::rc::Rc;
/// Represents a block of code which persists inside the Interpreter
#[derive(Clone, Debug)]
pub struct Function {
/// Stores the contents of the function declaration
decl: Rc<FnDecl>,
// /// Stores 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) -> Sym {
let FnDecl { name, .. } = *self.decl;
name
}
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
let FnDecl { name, bind, body, sign: _ } = &*self.decl;
// Check arg mapping
if args.len() != bind.len() {
return Err(Error::ArgNumber { want: bind.len(), got: args.len() });
}
let Some(body) = body else {
return Err(Error::NotDefined(*name));
};
// TODO: completely refactor data storage
let mut frame = env.frame("fn args");
for (Param { mutability: _, name }, value) in bind.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 builtin;
pub mod env { pub mod env;
//! Lexical and non-lexical scoping for variables
use super::{
builtin::{BINARY, MISC, RANGE, UNARY},
convalue::ConValue,
error::{Error, IResult},
function::Function,
BuiltIn, Callable, Interpret,
};
use cl_ast::{Function as FnDecl, Sym};
use std::{
collections::HashMap,
fmt::Display,
ops::{Deref, DerefMut},
};
type StackFrame = HashMap<Sym, Option<ConValue>>; pub mod error;
/// Implements a nested lexical scope
#[derive(Clone, Debug)]
pub struct Environment {
frames: Vec<(StackFrame, &'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<Sym, Option<ConValue>> {
from.iter().map(|&v| (v.name(), 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: Sym, 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: Sym) -> 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))
}
/// Resolves a variable immutably.
///
/// Returns a reference to the variable's contents, if it is defined and initialized.
pub fn get(&self, id: Sym) -> IResult<ConValue> {
for (frame, _) in self.frames.iter().rev() {
match frame.get(&id) {
Some(Some(var)) => return Ok(var.clone()),
Some(None) => return Err(Error::NotInitialized(id)),
_ => (),
}
}
Err(Error::NotDefined(id))
}
/// Inserts a new [ConValue] into this [Environment]
pub fn insert(&mut self, id: Sym, value: Option<ConValue>) {
if let Some((frame, _)) = self.frames.last_mut() {
frame.insert(id, value);
}
}
/// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) {
let FnDecl { name, .. } = decl;
let (name, function) = (name, 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 cl_ast::Sym;
use super::convalue::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(Sym),
/// A name was defined but not initialized
NotInitialized(Sym),
/// A value was called, but is not callable
NotCallable(ConValue),
/// A function was called with the wrong number of arguments
ArgNumber {
want: usize,
got: usize,
},
Outlined(Sym),
}
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::Outlined(name) => {
write!(f, "Module {name} specified, but not imported.")
}
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View File

@@ -48,6 +48,7 @@ mod macros {
//! ``` //! ```
#![allow(unused_macros)] #![allow(unused_macros)]
use crate::IResult; use crate::IResult;
use cl_parser::parser::Parse;
use super::*; use super::*;
@@ -63,14 +64,14 @@ mod macros {
/// ///
/// Returns a `Result<`[`File`]`, ParseError>` /// Returns a `Result<`[`File`]`, ParseError>`
pub macro file($($t:tt)*) { pub macro file($($t:tt)*) {
Parser::new(Lexer::new(stringify!( $($t)* ))).file() File::parse(&mut Parser::new(Lexer::new(stringify!( $($t)* ))))
} }
/// Stringifies, lexes, and parses everything you give to it /// Stringifies, lexes, and parses everything you give to it
/// ///
/// Returns a `Result<`[`Block`]`, ParseError>` /// Returns a `Result<`[`Block`]`, ParseError>`
pub macro block($($t:tt)*) { pub macro block($($t:tt)*) {
Parser::new(Lexer::new(stringify!({ $($t)* }))).block() Block::parse(&mut Parser::new(Lexer::new(stringify!({ $($t)* }))))
} }
/// Evaluates a block of code in the given environment /// Evaluates a block of code in the given environment

View File

@@ -97,33 +97,33 @@ impl<'t> Lexer<'t> {
/// Scans through the text, searching for the next [Token] /// Scans through the text, searching for the next [Token]
pub fn scan(&mut self) -> LResult<Token> { pub fn scan(&mut self) -> LResult<Token> {
match self.skip_whitespace().peek()? { match self.skip_whitespace().peek()? {
'{' => self.consume()?.produce_op(Punct::LCurly), '{' => self.consume()?.produce_op(Kind::LCurly),
'}' => self.consume()?.produce_op(Punct::RCurly), '}' => self.consume()?.produce_op(Kind::RCurly),
'[' => self.consume()?.produce_op(Punct::LBrack), '[' => self.consume()?.produce_op(Kind::LBrack),
']' => self.consume()?.produce_op(Punct::RBrack), ']' => self.consume()?.produce_op(Kind::RBrack),
'(' => self.consume()?.produce_op(Punct::LParen), '(' => self.consume()?.produce_op(Kind::LParen),
')' => self.consume()?.produce_op(Punct::RParen), ')' => self.consume()?.produce_op(Kind::RParen),
'&' => self.consume()?.amp(), '&' => self.consume()?.amp(),
'@' => self.consume()?.produce_op(Punct::At), '@' => self.consume()?.produce_op(Kind::At),
'\\' => self.consume()?.produce_op(Punct::Backslash), '\\' => self.consume()?.produce_op(Kind::Backslash),
'!' => self.consume()?.bang(), '!' => self.consume()?.bang(),
'|' => self.consume()?.bar(), '|' => self.consume()?.bar(),
':' => self.consume()?.colon(), ':' => self.consume()?.colon(),
',' => self.consume()?.produce_op(Punct::Comma), ',' => self.consume()?.produce_op(Kind::Comma),
'.' => self.consume()?.dot(), '.' => self.consume()?.dot(),
'=' => self.consume()?.equal(), '=' => self.consume()?.equal(),
'`' => self.consume()?.produce_op(Punct::Grave), '`' => self.consume()?.produce_op(Kind::Grave),
'>' => self.consume()?.greater(), '>' => self.consume()?.greater(),
'#' => self.consume()?.hash(), '#' => self.consume()?.hash(),
'<' => self.consume()?.less(), '<' => self.consume()?.less(),
'-' => self.consume()?.minus(), '-' => self.consume()?.minus(),
'+' => self.consume()?.plus(), '+' => self.consume()?.plus(),
'?' => self.consume()?.produce_op(Punct::Question), '?' => self.consume()?.produce_op(Kind::Question),
'%' => self.consume()?.rem(), '%' => self.consume()?.rem(),
';' => self.consume()?.produce_op(Punct::Semi), ';' => self.consume()?.produce_op(Kind::Semi),
'/' => self.consume()?.slash(), '/' => self.consume()?.slash(),
'*' => self.consume()?.star(), '*' => self.consume()?.star(),
'~' => self.consume()?.produce_op(Punct::Tilde), '~' => self.consume()?.produce_op(Kind::Tilde),
'^' => self.consume()?.xor(), '^' => self.consume()?.xor(),
'0' => self.consume()?.int_with_base(), '0' => self.consume()?.int_with_base(),
'1'..='9' => self.digits::<10>(), '1'..='9' => self.digits::<10>(),
@@ -157,14 +157,14 @@ impl<'t> Lexer<'t> {
.copied() .copied()
.ok_or(Error::end_of_file(self.line(), self.col())) .ok_or(Error::end_of_file(self.line(), self.col()))
} }
fn produce(&mut self, kind: TokenKind, data: impl Into<TokenData>) -> LResult<Token> { fn produce(&mut self, kind: Kind, data: impl Into<TokenData>) -> LResult<Token> {
let loc = self.start_loc; let loc = self.start_loc;
self.start_loc = self.current_loc; self.start_loc = self.current_loc;
self.start = self.current; self.start = self.current;
Ok(Token::new(kind, data, loc.0, loc.1)) Ok(Token::new(kind, data, loc.0, loc.1))
} }
fn produce_op(&mut self, kind: Punct) -> LResult<Token> { fn produce_op(&mut self, kind: Kind) -> LResult<Token> {
self.produce(TokenKind::Punct(kind), ()) self.produce(kind, ())
} }
fn skip_whitespace(&mut self) -> &mut Self { fn skip_whitespace(&mut self) -> &mut Self {
while let Ok(c) = self.peek() { while let Ok(c) = self.peek() {
@@ -195,138 +195,147 @@ impl<'t> Lexer<'t> {
impl<'t> Lexer<'t> { impl<'t> Lexer<'t> {
fn amp(&mut self) -> LResult<Token> { fn amp(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('&') => self.consume()?.produce_op(Punct::AmpAmp), Ok('&') => self.consume()?.produce_op(Kind::AmpAmp),
Ok('=') => self.consume()?.produce_op(Punct::AmpEq), Ok('=') => self.consume()?.produce_op(Kind::AmpEq),
_ => self.produce_op(Punct::Amp), _ => self.produce_op(Kind::Amp),
} }
} }
fn bang(&mut self) -> LResult<Token> { fn bang(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('!') => self.consume()?.produce_op(Punct::BangBang), Ok('!') => self.consume()?.produce_op(Kind::BangBang),
Ok('=') => self.consume()?.produce_op(Punct::BangEq), Ok('=') => self.consume()?.produce_op(Kind::BangEq),
_ => self.produce_op(Punct::Bang), _ => self.produce_op(Kind::Bang),
} }
} }
fn bar(&mut self) -> LResult<Token> { fn bar(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('|') => self.consume()?.produce_op(Punct::BarBar), Ok('|') => self.consume()?.produce_op(Kind::BarBar),
Ok('=') => self.consume()?.produce_op(Punct::BarEq), Ok('=') => self.consume()?.produce_op(Kind::BarEq),
_ => self.produce_op(Punct::Bar), _ => self.produce_op(Kind::Bar),
} }
} }
fn colon(&mut self) -> LResult<Token> { fn colon(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok(':') => self.consume()?.produce_op(Punct::ColonColon), Ok(':') => self.consume()?.produce_op(Kind::ColonColon),
_ => self.produce_op(Punct::Colon), _ => self.produce_op(Kind::Colon),
} }
} }
fn dot(&mut self) -> LResult<Token> { fn dot(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('.') => { Ok('.') => {
if let Ok('=') = self.consume()?.peek() { if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::DotDotEq) self.consume()?.produce_op(Kind::DotDotEq)
} else { } else {
self.produce_op(Punct::DotDot) self.produce_op(Kind::DotDot)
} }
} }
_ => self.produce_op(Punct::Dot), _ => self.produce_op(Kind::Dot),
} }
} }
fn equal(&mut self) -> LResult<Token> { fn equal(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::EqEq), Ok('=') => self.consume()?.produce_op(Kind::EqEq),
Ok('>') => self.consume()?.produce_op(Punct::FatArrow), Ok('>') => self.consume()?.produce_op(Kind::FatArrow),
_ => self.produce_op(Punct::Eq), _ => self.produce_op(Kind::Eq),
} }
} }
fn greater(&mut self) -> LResult<Token> { fn greater(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::GtEq), Ok('=') => self.consume()?.produce_op(Kind::GtEq),
Ok('>') => { Ok('>') => {
if let Ok('=') = self.consume()?.peek() { if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::GtGtEq) self.consume()?.produce_op(Kind::GtGtEq)
} else { } else {
self.produce_op(Punct::GtGt) self.produce_op(Kind::GtGt)
} }
} }
_ => self.produce_op(Punct::Gt), _ => self.produce_op(Kind::Gt),
} }
} }
fn hash(&mut self) -> LResult<Token> { fn hash(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('!') => self.consume()?.produce_op(Punct::HashBang), Ok('!') => self.consume()?.hashbang(),
_ => self.produce_op(Punct::Hash), _ => self.produce_op(Kind::Hash),
}
}
fn hashbang(&mut self) -> LResult<Token> {
match self.peek() {
Ok('/' | '\'') => self.line_comment(),
_ => self.produce_op(Kind::HashBang),
} }
} }
fn less(&mut self) -> LResult<Token> { fn less(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::LtEq), Ok('=') => self.consume()?.produce_op(Kind::LtEq),
Ok('<') => { Ok('<') => {
if let Ok('=') = self.consume()?.peek() { if let Ok('=') = self.consume()?.peek() {
self.consume()?.produce_op(Punct::LtLtEq) self.consume()?.produce_op(Kind::LtLtEq)
} else { } else {
self.produce_op(Punct::LtLt) self.produce_op(Kind::LtLt)
} }
} }
_ => self.produce_op(Punct::Lt), _ => self.produce_op(Kind::Lt),
} }
} }
fn minus(&mut self) -> LResult<Token> { fn minus(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::MinusEq), Ok('=') => self.consume()?.produce_op(Kind::MinusEq),
Ok('>') => self.consume()?.produce_op(Punct::Arrow), Ok('>') => self.consume()?.produce_op(Kind::Arrow),
_ => self.produce_op(Punct::Minus), _ => self.produce_op(Kind::Minus),
} }
} }
fn plus(&mut self) -> LResult<Token> { fn plus(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::PlusEq), Ok('=') => self.consume()?.produce_op(Kind::PlusEq),
_ => self.produce_op(Punct::Plus), _ => self.produce_op(Kind::Plus),
} }
} }
fn rem(&mut self) -> LResult<Token> { fn rem(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::RemEq), Ok('=') => self.consume()?.produce_op(Kind::RemEq),
_ => self.produce_op(Punct::Rem), _ => self.produce_op(Kind::Rem),
} }
} }
fn slash(&mut self) -> LResult<Token> { fn slash(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::SlashEq), Ok('=') => self.consume()?.produce_op(Kind::SlashEq),
Ok('/') => self.consume()?.line_comment(), Ok('/') => self.consume()?.line_comment(),
Ok('*') => self.consume()?.block_comment(), Ok('*') => self.consume()?.block_comment(),
_ => self.produce_op(Punct::Slash), _ => self.produce_op(Kind::Slash),
} }
} }
fn star(&mut self) -> LResult<Token> { fn star(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::StarEq), Ok('=') => self.consume()?.produce_op(Kind::StarEq),
_ => self.produce_op(Punct::Star), _ => self.produce_op(Kind::Star),
} }
} }
fn xor(&mut self) -> LResult<Token> { fn xor(&mut self) -> LResult<Token> {
match self.peek() { match self.peek() {
Ok('=') => self.consume()?.produce_op(Punct::XorEq), Ok('=') => self.consume()?.produce_op(Kind::XorEq),
Ok('^') => self.consume()?.produce_op(Punct::XorXor), Ok('^') => self.consume()?.produce_op(Kind::XorXor),
_ => self.produce_op(Punct::Xor), _ => self.produce_op(Kind::Xor),
} }
} }
} }
/// Comments /// Comments
impl<'t> Lexer<'t> { impl<'t> Lexer<'t> {
fn line_comment(&mut self) -> LResult<Token> { fn line_comment(&mut self) -> LResult<Token> {
let mut comment = String::new();
while Ok('\n') != self.peek() { while Ok('\n') != self.peek() {
self.consume()?; comment.push(self.next()?);
} }
self.produce(Kind::Comment, ()) self.produce(Kind::Comment, comment)
} }
fn block_comment(&mut self) -> LResult<Token> { fn block_comment(&mut self) -> LResult<Token> {
let mut comment = String::new();
while let Ok(c) = self.next() { while let Ok(c) = self.next() {
if '*' == c && Ok('/') == self.next() { if '*' == c && Ok('/') == self.peek() {
break; break;
} }
comment.push(c);
} }
self.produce(Kind::Comment, ()) self.consume()?.produce(Kind::Comment, comment)
} }
} }
/// Identifiers /// Identifiers

View File

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

View File

@@ -99,6 +99,7 @@ pub enum Parsing {
BinaryKind, BinaryKind,
Unary, Unary,
UnaryKind, UnaryKind,
Cast,
Index, Index,
Structor, Structor,
Fielder, Fielder,
@@ -204,6 +205,7 @@ impl Display for Parsing {
Parsing::BinaryKind => "a binary operator", Parsing::BinaryKind => "a binary operator",
Parsing::Unary => "a unary expression", Parsing::Unary => "a unary expression",
Parsing::UnaryKind => "a unary operator", Parsing::UnaryKind => "a unary operator",
Parsing::Cast => "an `as`-casting expression",
Parsing::Index => "an indexing expression", Parsing::Index => "an indexing expression",
Parsing::Structor => "a struct constructor expression", Parsing::Structor => "a struct constructor expression",
Parsing::Fielder => "a struct field expression", Parsing::Fielder => "a struct field expression",

View File

@@ -85,7 +85,7 @@ impl Fold for ModuleInliner {
Ok(file) => file, Ok(file) => file,
}; };
let kind = match Parser::new(Lexer::new(&file)).file() { let kind = match Parser::new(Lexer::new(&file)).parse() {
Err(e) => return self.handle_parse_error(e), Err(e) => return self.handle_parse_error(e),
Ok(file) => ModuleKind::Inline(file), Ok(file) => ModuleKind::Inline(file),
}; };

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,5 +1,6 @@
//! Pretty prints a conlang AST in yaml //! Pretty prints a conlang AST in yaml
use cl_ast::Stmt;
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
use repline::{error::Error as RlError, Repline}; use repline::{error::Error as RlError, Repline};
@@ -19,7 +20,7 @@ fn main() -> Result<(), Box<dyn Error>> {
}; };
let mut parser = Parser::new(Lexer::new(&line)); let mut parser = Parser::new(Lexer::new(&line));
let code = match parser.stmt() { let code = match parser.parse::<Stmt>() {
Ok(code) => { Ok(code) => {
rl.accept(); rl.accept();
code code
@@ -363,7 +364,6 @@ pub mod yamlify {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
match self { match self {
StmtKind::Empty => y, StmtKind::Empty => y,
StmtKind::Local(s) => y.yaml(s),
StmtKind::Item(s) => y.yaml(s), StmtKind::Item(s) => y.yaml(s),
StmtKind::Expr(s) => y.yaml(s), StmtKind::Expr(s) => y.yaml(s),
}; };
@@ -388,10 +388,12 @@ pub mod yamlify {
impl Yamlify for ExprKind { impl Yamlify for ExprKind {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
match self { match self {
ExprKind::Let(k) => k.yaml(y),
ExprKind::Assign(k) => k.yaml(y), ExprKind::Assign(k) => k.yaml(y),
ExprKind::Modify(k) => k.yaml(y), ExprKind::Modify(k) => k.yaml(y),
ExprKind::Binary(k) => k.yaml(y), ExprKind::Binary(k) => k.yaml(y),
ExprKind::Unary(k) => k.yaml(y), ExprKind::Unary(k) => k.yaml(y),
ExprKind::Cast(k) => k.yaml(y),
ExprKind::Member(k) => k.yaml(y), ExprKind::Member(k) => k.yaml(y),
ExprKind::Index(k) => k.yaml(y), ExprKind::Index(k) => k.yaml(y),
ExprKind::Structor(k) => k.yaml(y), ExprKind::Structor(k) => k.yaml(y),
@@ -404,13 +406,14 @@ pub mod yamlify {
ExprKind::Empty => {} ExprKind::Empty => {}
ExprKind::Group(k) => k.yaml(y), ExprKind::Group(k) => k.yaml(y),
ExprKind::Tuple(k) => k.yaml(y), ExprKind::Tuple(k) => k.yaml(y),
ExprKind::Loop(k) => k.yaml(y),
ExprKind::While(k) => k.yaml(y), ExprKind::While(k) => k.yaml(y),
ExprKind::If(k) => k.yaml(y), ExprKind::If(k) => k.yaml(y),
ExprKind::For(k) => k.yaml(y), ExprKind::For(k) => k.yaml(y),
ExprKind::Break(k) => k.yaml(y), ExprKind::Break(k) => k.yaml(y),
ExprKind::Return(k) => k.yaml(y), ExprKind::Return(k) => k.yaml(y),
ExprKind::Continue(k) => k.yaml(y), ExprKind::Continue => {
y.key("Continue");
}
} }
} }
} }
@@ -461,6 +464,12 @@ pub mod yamlify {
y.value(self); y.value(self);
} }
} }
impl Yamlify for Cast {
fn yaml(&self, y: &mut Yamler) {
let Self { head, ty } = self;
y.key("Cast").pair("head", head).pair("ty", ty);
}
}
impl Yamlify for Member { impl Yamlify for Member {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { head, kind } = self; let Self { head, kind } = self;
@@ -529,12 +538,6 @@ pub mod yamlify {
y.key("Group").yaml(expr); 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 { impl Yamlify for While {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
let Self { cond, pass, fail } = self; let Self { cond, pass, fail } = self;
@@ -578,11 +581,6 @@ pub mod yamlify {
y.key("Return").yaml(body); y.key("Return").yaml(body);
} }
} }
impl Yamlify for Continue {
fn yaml(&self, y: &mut Yamler) {
y.key("Continue");
}
}
impl Yamlify for Literal { impl Yamlify for Literal {
fn yaml(&self, y: &mut Yamler) { fn yaml(&self, y: &mut Yamler) {
y.value(format_args!("\"{self}\"")); y.value(format_args!("\"{self}\""));

View File

@@ -35,6 +35,10 @@ argwerk::define! {
[#[option] path] if file.is_none() => { [#[option] path] if file.is_none() => {
file = path.map(Into::into); file = path.map(Into::into);
} }
[path] if file.is_some() => {
include.push(path.into());
}
} }
/// gets whether stdin AND stdout are a terminal, for pipelining /// gets whether stdin AND stdout are a terminal, for pipelining

View File

@@ -5,6 +5,7 @@ use crate::{
menu, menu,
tools::print_token, tools::print_token,
}; };
use cl_ast::File;
use cl_interpret::{convalue::ConValue, env::Environment, interpret::Interpret}; use cl_interpret::{convalue::ConValue, env::Environment, interpret::Interpret};
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
@@ -49,7 +50,7 @@ fn load_file(env: &mut Environment, path: impl AsRef<Path>) -> Result<ConValue,
let inliner = let inliner =
cl_parser::inliner::ModuleInliner::new(path.as_ref().parent().unwrap_or(Path::new(""))); cl_parser::inliner::ModuleInliner::new(path.as_ref().parent().unwrap_or(Path::new("")));
let file = std::fs::read_to_string(path)?; let file = std::fs::read_to_string(path)?;
let code = Parser::new(Lexer::new(&file)).file()?; let code = Parser::new(Lexer::new(&file)).parse()?;
let code = match inliner.inline(code) { let code = match inliner.inline(code) {
Ok(a) => a, Ok(a) => a,
Err((code, io_errs, parse_errs)) => { Err((code, io_errs, parse_errs)) => {
@@ -79,13 +80,13 @@ fn lex_code(code: &str, path: Option<impl AsRef<Path>>) -> Result<(), Box<dyn Er
} }
fn fmt_code(code: &str) -> Result<(), Box<dyn Error>> { fn fmt_code(code: &str) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).file()?; let code = Parser::new(Lexer::new(code)).parse::<File>()?;
println!("{code}"); println!("{code}");
Ok(()) Ok(())
} }
fn run_code(code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> { fn run_code(code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
let code = Parser::new(Lexer::new(code)).file()?; let code = Parser::new(Lexer::new(code)).parse::<File>()?;
match code.interpret(env)? { match code.interpret(env)? {
ConValue::Empty => {} ConValue::Empty => {}
ret => println!("{ret}"), ret => println!("{ret}"),

View File

@@ -1,4 +1,5 @@
use crate::{ansi, ctx}; use crate::{ansi, ctx};
use cl_ast::Stmt;
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::Parser; use cl_parser::Parser;
use repline::{error::ReplResult, prebaked::*}; use repline::{error::ReplResult, prebaked::*};
@@ -42,7 +43,7 @@ pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
use cl_parser::inliner::ModuleInliner; use cl_parser::inliner::ModuleInliner;
read_and(ansi::CYAN, "cl>", " ?>", |line| { read_and(ansi::CYAN, "cl>", " ?>", |line| {
let code = Parser::new(Lexer::new(line)).stmt()?; let code = Parser::new(Lexer::new(line)).parse::<Stmt>()?;
let code = ModuleInliner::new(".").fold_stmt(code); let code = ModuleInliner::new(".").fold_stmt(code);
print!("{}", ansi::OUTPUT); print!("{}", ansi::OUTPUT);
@@ -71,7 +72,7 @@ pub fn fmt(_ctx: &mut ctx::Context) -> ReplResult<()> {
read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| { read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| {
let mut p = Parser::new(Lexer::new(line)); let mut p = Parser::new(Lexer::new(line));
match p.stmt() { match p.parse::<Stmt>() {
Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET), Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET),
Err(e) => Err(e)?, Err(e) => Err(e)?,
} }

View File

@@ -185,6 +185,24 @@ pub mod string_interner {
} }
} }
impl std::fmt::Display for StringInterner<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Ok(keys) = self.keys.read() else {
return write!(f, "Could not lock StringInterner key map.");
};
let mut keys: Vec<_> = keys.iter().collect();
keys.sort();
writeln!(f, "Keys:")?;
for (idx, key) in keys.iter().enumerate() {
writeln!(f, "{idx}:\t\"{key}\"")?
}
writeln!(f, "Count: {}", keys.len())?;
Ok(())
}
}
// # Safety: // # Safety:
// This is fine because StringInterner::get_or_insert(v) holds a RwLock // This is fine because StringInterner::get_or_insert(v) holds a RwLock
// for its entire duration, and doesn't touch the non-(Send+Sync) arena // for its entire duration, and doesn't touch the non-(Send+Sync) arena

View File

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

View File

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

View File

@@ -1,24 +1,33 @@
use cl_structures::intern::string_interner::StringInterner;
use cl_typeck::{entry::Entry, stage::*, table::Table, type_expression::TypeExpression}; use cl_typeck::{entry::Entry, stage::*, table::Table, type_expression::TypeExpression};
use cl_ast::{ use cl_ast::{
ast_visitor::{Fold, Visit}, ast_visitor::{Fold, Visit},
desugar::*, desugar::*,
Stmt, Ty,
}; };
use cl_lexer::Lexer; use cl_lexer::Lexer;
use cl_parser::{inliner::ModuleInliner, Parser}; use cl_parser::{inliner::ModuleInliner, Parser};
use repline::{error::Error as RlError, prebaked::*}; use repline::{error::Error as RlError, prebaked::*};
use std::{error::Error, path}; use std::{
error::Error,
path::{self, PathBuf},
};
// Path to display in standard library errors // Path to display in standard library errors
const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl"; const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl";
// Statically included standard library // Statically included standard library
const STDLIB: &str = include_str!("../../../stdlib/lib.cl"); const PREAMBLE: &str = r"
pub mod std;
pub use std::preamble::*;
";
// Colors // Colors
const C_MAIN: &str = C_LISTING; const C_MAIN: &str = C_LISTING;
const C_RESV: &str = "\x1b[35m"; const C_RESV: &str = "\x1b[35m";
const C_CODE: &str = "\x1b[36m"; const C_CODE: &str = "\x1b[36m";
const C_BYID: &str = "\x1b[95m"; const C_BYID: &str = "\x1b[95m";
const C_ERROR: &str = "\x1b[31m";
const C_LISTING: &str = "\x1b[38;5;117m"; const C_LISTING: &str = "\x1b[38;5;117m";
/// A home for immutable intermediate ASTs /// A home for immutable intermediate ASTs
@@ -29,17 +38,17 @@ static mut TREES: TreeManager = TreeManager::new();
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let mut prj = Table::default(); let mut prj = Table::default();
let mut parser = Parser::new(Lexer::new(STDLIB)); let mut parser = Parser::new(Lexer::new(PREAMBLE));
let code = match parser.file() { let code = match parser.parse() {
Ok(code) => code, Ok(code) => code,
Err(e) => { Err(e) => {
eprintln!("{STDLIB_DISPLAY_PATH}:{e}"); eprintln!("{STDLIB_DISPLAY_PATH}:{e}");
Err(e)? Err(e)?
} }
}; };
let code = inline_modules(code, concat!(env!("PWD"), "/stdlib")); // This code is special - it gets loaded from a hard-coded project directory (for now)
let code = inline_modules(code, concat!(env!("CARGO_MANIFEST_DIR"), "/../../stdlib"));
Populator::new(&mut prj).visit_file(unsafe { TREES.push(code) }); Populator::new(&mut prj).visit_file(unsafe { TREES.push(code) });
// NameCollector::new(&mut prj).visit_file(unsafe { TREES.push(code) });
main_menu(&mut prj)?; main_menu(&mut prj)?;
Ok(()) Ok(())
@@ -51,21 +60,25 @@ fn main_menu(prj: &mut Table) -> Result<(), RlError> {
match line.trim() { match line.trim() {
"c" | "code" => enter_code(prj)?, "c" | "code" => enter_code(prj)?,
"clear" => clear()?, "clear" => clear()?,
"d" | "desugar" => live_desugar()?,
"e" | "exit" => return Ok(Response::Break), "e" | "exit" => return Ok(Response::Break),
"f" | "file" => import_files(prj)?,
"i" | "id" => get_by_id(prj)?,
"l" | "list" => list_types(prj), "l" | "list" => list_types(prj),
"q" | "query" => query_type_expression(prj)?, "q" | "query" => query_type_expression(prj)?,
"i" | "id" => get_by_id(prj)?,
"r" | "resolve" => resolve_all(prj)?, "r" | "resolve" => resolve_all(prj)?,
"d" | "desugar" => live_desugar()?, "s" | "strings" => print_strings(),
"h" | "help" => { "h" | "help" | "" => {
println!( println!(
"Valid commands are: "Valid commands are:
clear : Clear the screen
code (c): Enter code to type-check code (c): Enter code to type-check
desugar (d): WIP: Test the experimental desugaring passes
file (f): Load files from disk
id (i): Get a type by its type ID
list (l): List all known types list (l): List all known types
query (q): Query the type system query (q): Query the type system
id (i): Get a type by its type ID
resolve (r): Perform type resolution resolve (r): Perform type resolution
desugar (d): WIP: Test the experimental desugaring passes
help (h): Print this list help (h): Print this list
exit (e): Exit the program" exit (e): Exit the program"
); );
@@ -82,7 +95,7 @@ fn enter_code(prj: &mut Table) -> Result<(), RlError> {
if line.trim().is_empty() { if line.trim().is_empty() {
return Ok(Response::Break); return Ok(Response::Break);
} }
let code = Parser::new(Lexer::new(line)).file()?; let code = Parser::new(Lexer::new(line)).parse()?;
let code = inline_modules(code, ""); let code = inline_modules(code, "");
let code = WhileElseDesugar.fold_file(code); let code = WhileElseDesugar.fold_file(code);
// Safety: this is totally unsafe // Safety: this is totally unsafe
@@ -93,7 +106,7 @@ fn enter_code(prj: &mut Table) -> Result<(), RlError> {
fn live_desugar() -> Result<(), RlError> { fn live_desugar() -> Result<(), RlError> {
read_and(C_RESV, "se>", "? >", |line| { read_and(C_RESV, "se>", "? >", |line| {
let code = Parser::new(Lexer::new(line)).stmt()?; let code = Parser::new(Lexer::new(line)).parse::<Stmt>()?;
println!("Raw, as parsed:\n{C_LISTING}{code}\x1b[0m"); println!("Raw, as parsed:\n{C_LISTING}{code}\x1b[0m");
let code = SquashGroups.fold_stmt(code); let code = SquashGroups.fold_stmt(code);
@@ -109,13 +122,17 @@ fn live_desugar() -> Result<(), RlError> {
}) })
} }
fn print_strings() {
println!("{}", StringInterner::global());
}
fn query_type_expression(prj: &mut Table) -> Result<(), RlError> { fn query_type_expression(prj: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "ty>", "? >", |line| { read_and(C_RESV, "ty>", "? >", |line| {
if line.trim().is_empty() { if line.trim().is_empty() {
return Ok(Response::Break); return Ok(Response::Break);
} }
// parse it as a path, and convert the path into a borrowed path // parse it as a path, and convert the path into a borrowed path
let ty = Parser::new(Lexer::new(line)).ty()?.kind; let ty: Ty = Parser::new(Lexer::new(line)).parse()?;
let id = ty.evaluate(prj, prj.root())?; let id = ty.evaluate(prj, prj.root())?;
pretty_handle(id.to_entry(prj))?; pretty_handle(id.to_entry(prj))?;
Ok(Response::Accept) Ok(Response::Accept)
@@ -123,6 +140,7 @@ fn query_type_expression(prj: &mut Table) -> Result<(), RlError> {
} }
fn get_by_id(prj: &mut Table) -> Result<(), RlError> { fn get_by_id(prj: &mut Table) -> Result<(), RlError> {
use cl_parser::parser::Parse;
use cl_structures::index_map::MapIndex; use cl_structures::index_map::MapIndex;
use cl_typeck::handle::Handle; use cl_typeck::handle::Handle;
read_and(C_BYID, "id>", "? >", |line| { read_and(C_BYID, "id>", "? >", |line| {
@@ -130,11 +148,11 @@ fn get_by_id(prj: &mut Table) -> Result<(), RlError> {
return Ok(Response::Break); return Ok(Response::Break);
} }
let mut parser = Parser::new(Lexer::new(line)); let mut parser = Parser::new(Lexer::new(line));
let def_id = match parser.literal()? { let def_id = match Parse::parse(&mut parser)? {
cl_ast::Literal::Int(int) => int as _, cl_ast::Literal::Int(int) => int as _,
other => Err(format!("Expected integer, got {other}"))?, other => Err(format!("Expected integer, got {other}"))?,
}; };
let mut path = parser.path().unwrap_or_default(); let mut path = parser.parse::<cl_ast::Path>().unwrap_or_default();
path.absolute = false; path.absolute = false;
let handle = Handle::from_usize(def_id).to_entry(prj); let handle = Handle::from_usize(def_id).to_entry(prj);
@@ -182,6 +200,35 @@ fn list_types(table: &mut Table) {
} }
} }
fn import_files(table: &mut Table) -> Result<(), RlError> {
read_and(C_RESV, "fi>", "? >", |line| {
let line = line.trim();
if line.is_empty() {
return Ok(Response::Break);
}
let Ok(file) = std::fs::read_to_string(line) else {
for file in std::fs::read_dir(line)? {
println!("{}", file?.path().display())
}
return Ok(Response::Accept);
};
let mut parser = Parser::new(Lexer::new(&file));
let code = match parser.parse() {
Ok(code) => inline_modules(code, PathBuf::from(line).parent().unwrap_or("".as_ref())),
Err(e) => {
eprintln!("{C_ERROR}{line}:{e}\x1b[0m");
return Ok(Response::Deny);
}
};
Populator::new(table).visit_file(unsafe { TREES.push(code) });
println!("...Imported!");
Ok(Response::Accept)
})
}
fn pretty_handle(entry: Entry) -> Result<(), std::io::Error> { fn pretty_handle(entry: Entry) -> Result<(), std::io::Error> {
use std::io::Write; use std::io::Write;
let mut out = std::io::stdout().lock(); let mut out = std::io::stdout().lock();

View File

@@ -21,10 +21,8 @@ impl fmt::Display for Entry<'_, '_> {
TypeKind::Instance(id) => write!(f, "{}", self.with_id(*id)), TypeKind::Instance(id) => write!(f, "{}", self.with_id(*id)),
TypeKind::Intrinsic(kind) => write!(f, "{kind}"), TypeKind::Intrinsic(kind) => write!(f, "{kind}"),
TypeKind::Adt(adt) => write_adt(adt, self, f), TypeKind::Adt(adt) => write_adt(adt, self, f),
&TypeKind::Ref(cnt, id) => { &TypeKind::Ref(id) => {
for _ in 0..cnt { f.write_str("&")?;
f.write_str("&")?;
}
let h_id = self.with_id(id); let h_id = self.with_id(id);
write_name_or(h_id, f) write_name_or(h_id, f)
} }

View File

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

View File

@@ -96,7 +96,7 @@ impl Type {
} }
/// Checks whether there are any unbound type variables in this type. /// Checks whether there are any unbound type variables in this type.
/// ```rust /// ```rust
/// # use cl_typeck::inference::*; /// # use cl_typeck::stage::infer::*;
/// let bool = Type::new_op("bool".into(), &[]); /// let bool = Type::new_op("bool".into(), &[]);
/// let true_v = Type::new_inst(&bool); /// let true_v = Type::new_inst(&bool);
/// let unbound = Type::new_var(); /// let unbound = Type::new_var();
@@ -149,7 +149,7 @@ impl Type {
/// Panics if this type variable's instance field is already borrowed. /// Panics if this type variable's instance field is already borrowed.
/// # Examples /// # Examples
/// ```rust /// ```rust
/// # use cl_typeck::inference::*; /// # use cl_typeck::stage::infer::*;
/// let t_bool = Type::new_op("bool".into(), &[]); /// let t_bool = Type::new_op("bool".into(), &[]);
/// let t_nest = Type::new_inst(&Type::new_inst(&Type::new_inst(&t_bool))); /// let t_nest = Type::new_inst(&Type::new_inst(&Type::new_inst(&t_bool)));
/// let pruned = t_nest.prune(); /// let pruned = t_nest.prune();

View File

@@ -145,35 +145,22 @@ impl<'a> Visit<'a> for Populator<'_, 'a> {
self.visit_use_tree(tree); self.visit_use_tree(tree);
} }
fn visit_stmt(&mut self, s: &'a cl_ast::Stmt) {
let cl_ast::Stmt { extents, kind, semi } = s;
let cl_ast::StmtKind::Local(local) = kind else {
self.visit_span(extents);
self.visit_stmt_kind(kind);
self.visit_semi(semi);
return;
};
let mut entry = self.new_entry(NodeKind::Local);
entry.inner.set_span(*extents);
entry.visit_let(local);
if let (Some(name), child) = (entry.name, entry.inner.id()) {
self.inner.add_child(name, child);
}
}
fn visit_let(&mut self, l: &'a cl_ast::Let) { fn visit_let(&mut self, l: &'a cl_ast::Let) {
let cl_ast::Let { mutable, name, ty, init } = l; let cl_ast::Let { mutable, name, ty, init } = l;
self.inner.set_source(Source::Local(l)); let mut entry = self.new_entry(NodeKind::Local);
self.set_name(*name);
self.visit_mutability(mutable); entry.inner.set_source(Source::Local(l));
entry.set_name(*name);
entry.visit_mutability(mutable);
if let Some(ty) = ty { if let Some(ty) = ty {
self.visit_ty(ty); entry.visit_ty(ty);
} }
if let Some(init) = init { if let Some(init) = init {
self.visit_expr(init) entry.visit_expr(init)
} }
let child = entry.inner.id();
self.inner.add_child(*name, child);
} }
} }

View File

@@ -97,8 +97,12 @@ impl TypeExpression for TyTuple {
impl TypeExpression for TyRef { impl TypeExpression for TyRef {
fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> { fn evaluate(&self, table: &mut Table, node: Handle) -> Result<Handle, Error> {
let Self { mutable: _, count, to } = self; let Self { mutable: _, count, to } = self;
let kind = TypeKind::Ref(*count, to.evaluate(table, node)?); let mut t = to.evaluate(table, node)?;
Ok(table.anon_type(kind)) for _ in 0..*count {
let kind = TypeKind::Ref(t);
t = table.anon_type(kind)
}
Ok(t)
} }
} }

View File

@@ -17,7 +17,7 @@ pub enum TypeKind {
/// A user-defined aromatic data type /// A user-defined aromatic data type
Adt(Adt), Adt(Adt),
/// A reference to an already-defined type: &T /// A reference to an already-defined type: &T
Ref(u16, Handle), Ref(Handle),
/// A contiguous view of dynamically sized memory /// A contiguous view of dynamically sized memory
Slice(Handle), Slice(Handle),
/// A contiguous view of statically sized memory /// A contiguous view of statically sized memory
@@ -54,39 +54,18 @@ pub enum Adt {
/// The set of compiler-intrinsic types. /// The set of compiler-intrinsic types.
/// These primitive types have native implementations of the basic operations. /// These primitive types have native implementations of the basic operations.
#[allow(non_camel_case_types)] #[rustfmt::skip]
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Intrinsic { pub enum Intrinsic {
/// An 8-bit signed integer: `#[intrinsic = "i8"]` I8, I16, I32, I64, I128, Isize, // Signed integers
I8, U8, U16, U32, U64, U128, Usize, // Unsigned integers
/// A 16-bit signed integer: `#[intrinsic = "i16"]` F8, F16, F32, F64, F128, Fsize, // Floating point numbers
I16, Bool, // boolean value
/// A 32-bit signed integer: `#[intrinsic = "i32"]` Char, // Unicode codepoint
I32,
/// A 64-bit signed integer: `#[intrinsic = "i32"]`
I64,
// /// A 128-bit signed integer: `#[intrinsic = "i32"]`
// I128,
/// A ptr-len signed integer: `#[intrinsic = "isize"]`
Isize,
/// An 8-bit unsigned integer: `#[intrinsic = "u8"]`
U8,
/// A 16-bit unsigned integer: `#[intrinsic = "u16"]`
U16,
/// A 32-bit unsigned integer: `#[intrinsic = "u32"]`
U32,
/// A 64-bit unsigned integer: `#[intrinsic = "u64"]`
U64,
// /// A 128-bit unsigned integer: `#[intrinsic = "u128"]`
// U128,
/// A ptr-len unsigned integer: `#[intrinsic = "isize"]`
Usize,
/// A boolean (`true` or `false`): `#[intrinsic = "bool"]`
Bool,
/// The unicode codepoint type: #[intrinsic = "char"]
Char,
} }
// Author's note: the fsize type is a meme
impl FromStr for Intrinsic { impl FromStr for Intrinsic {
type Err = (); type Err = ();
@@ -96,12 +75,20 @@ impl FromStr for Intrinsic {
"i16" => Intrinsic::I16, "i16" => Intrinsic::I16,
"i32" => Intrinsic::I32, "i32" => Intrinsic::I32,
"i64" => Intrinsic::I64, "i64" => Intrinsic::I64,
"i128" => Intrinsic::I128,
"isize" => Intrinsic::Isize, "isize" => Intrinsic::Isize,
"u8" => Intrinsic::U8, "u8" => Intrinsic::U8,
"u16" => Intrinsic::U16, "u16" => Intrinsic::U16,
"u32" => Intrinsic::U32, "u32" => Intrinsic::U32,
"u64" => Intrinsic::U64, "u64" => Intrinsic::U64,
"u128" => Intrinsic::U128,
"usize" => Intrinsic::Usize, "usize" => Intrinsic::Usize,
"f8" => Intrinsic::F8,
"f16" => Intrinsic::F16,
"f32" => Intrinsic::F32,
"f64" => Intrinsic::F64,
"f128" => Intrinsic::F128,
"fsize" => Intrinsic::Fsize,
"bool" => Intrinsic::Bool, "bool" => Intrinsic::Bool,
"char" => Intrinsic::Char, "char" => Intrinsic::Char,
_ => Err(())?, _ => Err(())?,

View File

@@ -11,12 +11,7 @@ impl Display for TypeKind {
TypeKind::Instance(def) => write!(f, "alias to #{def}"), TypeKind::Instance(def) => write!(f, "alias to #{def}"),
TypeKind::Intrinsic(i) => i.fmt(f), TypeKind::Intrinsic(i) => i.fmt(f),
TypeKind::Adt(a) => a.fmt(f), TypeKind::Adt(a) => a.fmt(f),
TypeKind::Ref(cnt, def) => { TypeKind::Ref(def) => write!(f, "&{def}"),
for _ in 0..*cnt {
f.write_str("&")?;
}
def.fmt(f)
}
TypeKind::Slice(def) => write!(f, "slice [#{def}]"), TypeKind::Slice(def) => write!(f, "slice [#{def}]"),
TypeKind::Array(def, size) => write!(f, "array [#{def}; {size}]"), TypeKind::Array(def, size) => write!(f, "array [#{def}; {size}]"),
TypeKind::Tuple(defs) => { TypeKind::Tuple(defs) => {
@@ -80,12 +75,20 @@ impl Display for Intrinsic {
Intrinsic::I16 => f.write_str("i16"), Intrinsic::I16 => f.write_str("i16"),
Intrinsic::I32 => f.write_str("i32"), Intrinsic::I32 => f.write_str("i32"),
Intrinsic::I64 => f.write_str("i64"), Intrinsic::I64 => f.write_str("i64"),
Intrinsic::I128 => f.write_str("i128"),
Intrinsic::Isize => f.write_str("isize"), Intrinsic::Isize => f.write_str("isize"),
Intrinsic::U8 => f.write_str("u8"), Intrinsic::U8 => f.write_str("u8"),
Intrinsic::U16 => f.write_str("u16"), Intrinsic::U16 => f.write_str("u16"),
Intrinsic::U32 => f.write_str("u32"), Intrinsic::U32 => f.write_str("u32"),
Intrinsic::U64 => f.write_str("u64"), Intrinsic::U64 => f.write_str("u64"),
Intrinsic::U128 => f.write_str("u128"),
Intrinsic::Usize => f.write_str("usize"), Intrinsic::Usize => f.write_str("usize"),
Intrinsic::F8 => f.write_str("f8"),
Intrinsic::F16 => f.write_str("f16"),
Intrinsic::F32 => f.write_str("f32"),
Intrinsic::F64 => f.write_str("f64"),
Intrinsic::F128 => f.write_str("f128"),
Intrinsic::Fsize => f.write_str("fsize"),
Intrinsic::Bool => f.write_str("bool"), Intrinsic::Bool => f.write_str("bool"),
Intrinsic::Char => f.write_str("char"), Intrinsic::Char => f.write_str("char"),
} }

View File

@@ -96,7 +96,9 @@ Shift = Factor (ShiftOp Factor )* ;
Factor = Term (FactorOp Term )* ; Factor = Term (FactorOp Term )* ;
Term = Unary (TermOp Unary )* ; Term = Unary (TermOp Unary )* ;
Unary = (UnaryKind)* Member ; Unary = (UnaryKind)* Cast ;
Cast = Member ("as" Ty)? ;
Member = Call (Access)* ; Member = Call (Access)* ;
Access = '.' (Identifier ('(' Tuple? ')')? | Literal) ; Access = '.' (Identifier ('(' Tuple? ')')? | Literal) ;

48
sample-code/ascii.cl Executable file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env -S conlang -r false
//! Prints out the characters in the ASCII printable range
//! and the Latin-1 supplement in the format of a hex-dump
fn main () {
ascii()
}
fn n_digit(n: u32) -> char {
(if n > 9 {
('a' as u32) + n - 10
} else {
('0' as u32) + n
}) as char
}
fn in_range(num: u32, start: u32, end: u32) -> bool {
(start <= num) && (num <= end )
}
fn ascii() {
for row in 0..16 {
for col in 0..16 {
if col == 8 {
print(' ')
}
print(n_digit(row), n_digit(col), ' ')
}
print(" │")
for col in 0..16 {
print(ascii_picture(row << 4 | col))
}
println("│")
}
}
// Start of the C0 control pictures region
const CO_CONTROL_PICTURES: u32 = '\u{2400}' as u32;
fn ascii_picture(c: u32) -> char {
if c < ' ' as u32 { // C0
(CO_CONTROL_PICTURES + c) as char
} else if c == 127 { // C0:DEL
'' // SYMBOL_FOR_DELETE
} else if c.in_range(0x7f, 0xa0) { // C1
' '
} else c as char
}

7
sample-code/fib.cl Normal file → Executable file
View File

@@ -1,11 +1,12 @@
#!/usr/bin/env -S conlang -r false
// Calculate Fibonacci numbers // Calculate Fibonacci numbers
fn main() { fn main() {
for num in 0..=30 { for num in 0..=30 {
print("fib(", num, ") = ", fib_iterative(num)) println("fib(", num, ") = ", fibit(num))
} }
for num in 0..=30 { for num in 0..=30 {
print("fib(", num, ") = ", fib(num)) println("fib(", num, ") = ", fib(num))
} }
} }
@@ -17,7 +18,7 @@ fn fib(a: i64) -> i64 {
} }
/// The classic iterative algorithm for fib() /// The classic iterative algorithm for fib()
fn fib_iterative(n: i64) -> i64 { fn fibit(n: i64) -> i64 {
let mut a = 0; let mut a = 0;
let mut b = 1; let mut b = 1;
let mut c = 1; let mut c = 1;

5
sample-code/fizzbuzz.cl Normal file → Executable file
View File

@@ -1,13 +1,14 @@
#!/usr/bin/env -S conlang -r false
// FizzBuzz, using the unstable variadic-`print` builtin // FizzBuzz, using the unstable variadic-`print` builtin
fn main() { fn main() {
fizzbuzz(10, 20) fizzbuzz(0, 30)
} }
// Outputs FizzBuzz for numbers between `start` and `end`, inclusive // Outputs FizzBuzz for numbers between `start` and `end`, inclusive
fn fizzbuzz(start: i128, end: i128) { fn fizzbuzz(start: i128, end: i128) {
for x in start..=end { for x in start..=end {
print(if x % 15 == 0 { println(if x % 15 == 0 {
"FizzBuzz" "FizzBuzz"
} else if 0 == x % 3 { } else if 0 == x % 3 {
"Fizz" "Fizz"

5
sample-code/hello.cl Normal file → Executable file
View File

@@ -1,3 +1,6 @@
#!/usr/bin/env -S conlang -r false
//! Prints "Hello, world!"
fn main() { fn main() {
print("Hello, world!") println("Hello, world!")
} }

View File

@@ -1,23 +1,39 @@
//! Formats numbers in hexadecimal, octal, or binary //! Formats numbers in hexadecimal, octal, or binary
mod math; mod math;
// TODO: casting and/or conversion fn as_digit(n: u32) -> char {
const HEX_LUT: Array = [ (if n > 9 {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', n - 10 + ('a' as u32)
]; } else {
n + ('0' as u32)
}) as char
}
pub fn radix(n: i64, radix: i64) {
fn r_str_radix(n: i64, radix: i64) {
if n != 0 {
r_str_radix(n / radix, radix) + as_digit(n % radix)
} else ""
}
if n == 0 {
"0"
} else if n < 0 {
// TODO: breaks at i64::MIN
"-" + r_str_radix(-n, radix)
} else r_str_radix(n, radix)
}
pub fn hex(n: u64) { pub fn hex(n: u64) {
let out = "0x"; let out = "0x";
for xd in min(count_leading_zeroes(n) / 4, 15)..16 { for xd in min(count_leading_zeroes(n) / 4, 15)..16 {
out += HEX_LUT[(n >> (15 - xd) * 4) & 0xf] out += as_digit((n >> (15 - xd) * 4) & 0xf)
} }
out out
} }
pub fn oct(n: u64) { pub fn oct(n: u64) {
let out = "0o"; let out = "0o";
for xd in min((count_leading_zeroes(n) + 2) / 3, 21)..22 { for xd in min((count_leading_zeroes(n) + 2) / 3, 21)..22 {
out += HEX_LUT[(n >> max(63 - (3 * xd), 0)) & 7] out += as_digit((n >> max(63 - (3 * xd), 0)) & 7)
} }
out out
} }
@@ -25,7 +41,7 @@ pub fn oct(n: u64) {
pub fn bin(n: u64) { pub fn bin(n: u64) {
let out = "0b"; let out = "0b";
for xd in min(count_leading_zeroes(n), 63)..64 { for xd in min(count_leading_zeroes(n), 63)..64 {
out += HEX_LUT[(n >> 63 - xd) & 1] out += as_digit((n >> 63 - xd) & 1)
} }
out out
} }

View File

@@ -1,9 +1,15 @@
//! Useful math functions //! Useful math functions
// FIXME:
// These two functions shouldn't actually be polymorphic, but
// the AST interpreter doesn't know about type annotations
// or operator overloading.
#[generic("T")]
pub fn max(a: T, b: T) -> T { pub fn max(a: T, b: T) -> T {
(if a < b { b } else { a }) (if a < b { b } else { a })
} }
#[generic("T")]
pub fn min(a: T, b: T) -> T { pub fn min(a: T, b: T) -> T {
(if a > b { b } else { a }) (if a > b { b } else { a })
} }

37
sample-code/rand.cl Normal file
View File

@@ -0,0 +1,37 @@
//! Pseudo-random number generation using a LFSR algorithm
static state: u64 = 0xdeadbeefdeadbeef;
pub fn seed(seed: u64) {
state = seed;
}
pub fn lfsr_next() {
state ^= state >> 7;
state ^= state << 9;
state ^= state >> 13;
}
/// Returns a pseudorandom byte
pub fn rand() -> u8 {
for _ in 0..8 {
lfsr_next()
}
state & 0xff
}
// Prints a maze out of diagonal box drawing characters, ['', '']
fn mazel(width: u64, height: u64) {
let walls = ['\u{2571}', '\u{2572}'];
rand_rect(width, height, walls)
}
// Prints a rectangle with the provided walls
fn rand_rect(width: u64, height: u64, walls: [char; 2]) {
for _ in 0..height {
for _ in 0..width {
print(walls[rand() % 2])
}
println()
}
}

View File

@@ -1,10 +1,12 @@
//! # The Conlang Standard Library //! # The Conlang Standard Library
pub mod preamble { pub mod preamble {
pub use super::num::*; pub use super::{num::*, str::str};
} }
pub mod num; pub mod num;
pub mod str;
#[cfg("test")] #[cfg("test")]
mod test; mod test;

View File

@@ -18,6 +18,9 @@ pub type i32;
#[intrinsic = "i64"] #[intrinsic = "i64"]
pub type i64; pub type i64;
#[intrinsic = "i128"]
pub type i128;
#[intrinsic = "isize"] #[intrinsic = "isize"]
pub type isize; pub type isize;
@@ -33,9 +36,18 @@ pub type u32;
#[intrinsic = "u64"] #[intrinsic = "u64"]
pub type u64; pub type u64;
#[intrinsic = "u128"]
pub type u128;
#[intrinsic = "usize"] #[intrinsic = "usize"]
pub type usize; pub type usize;
#[intrinsic = "f32"]
pub type f32;
#[intrinsic = "f64"]
pub type f64;
// Contains implementations for (TODO) overloaded operators on num types // Contains implementations for (TODO) overloaded operators on num types
pub mod ops { pub mod ops {
use super::*; use super::*;
@@ -226,6 +238,45 @@ pub mod ops {
} }
} }
impl u128 {
pub const MIN: Self = 0;
pub const MAX: Self = !0;
pub const BIT_WIDTH: u32 = 8;
pub fn default() -> Self {
0
}
pub fn mul(a: Self, b: Self) -> Self {
a * b
}
pub fn div(a: Self, b: Self) -> Self {
a / b
}
pub fn rem(a: Self, b: Self) -> Self {
a % b
}
pub fn add(a: Self, b: Self) -> Self {
a + b
}
pub fn sub(a: Self, b: Self) -> Self {
a - b
}
pub fn shl(a: Self, b: u32) -> Self {
a << b
}
pub fn shr(a: Self, b: u32) -> Self {
a >> b
}
pub fn and(a: Self, b: Self) -> Self {
a & b
}
pub fn or(a: Self, b: Self) -> Self {
a | b
}
pub fn xor(a: Self, b: Self) -> Self {
a ^ b
}
}
impl usize { impl usize {
pub const MIN: Self = __march_ptr_width_unsigned_min(); pub const MIN: Self = __march_ptr_width_unsigned_min();
pub const MAX: Self = __march_ptr_width_unsigned_max(); pub const MAX: Self = __march_ptr_width_unsigned_max();
@@ -421,6 +472,45 @@ pub mod ops {
} }
} }
impl i128 {
pub const MIN: Self = !(1 << 128);
pub const MAX: Self = 1 << 128;
pub const BIT_WIDTH: u32 = 8;
pub fn default() -> Self {
0
}
pub fn mul(a: Self, b: Self) -> Self {
a * b
}
pub fn div(a: Self, b: Self) -> Self {
a / b
}
pub fn rem(a: Self, b: Self) -> Self {
a % b
}
pub fn add(a: Self, b: Self) -> Self {
a + b
}
pub fn sub(a: Self, b: Self) -> Self {
a - b
}
pub fn shl(a: Self, b: u32) -> Self {
a << b
}
pub fn shr(a: Self, b: u32) -> Self {
a >> b
}
pub fn and(a: Self, b: Self) -> Self {
a & b
}
pub fn or(a: Self, b: Self) -> Self {
a | b
}
pub fn xor(a: Self, b: Self) -> Self {
a ^ b
}
}
impl isize { impl isize {
pub const MIN: Self = __march_ptr_width_signed_min(); pub const MIN: Self = __march_ptr_width_signed_min();
pub const MAX: Self = __march_ptr_width_signed_max(); pub const MAX: Self = __march_ptr_width_signed_max();

4
stdlib/std/str.cl Normal file
View File

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