Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8675f91aca | |||
| de63a8c123 | |||
| 533436afc1 | |||
| 1eb0516baf | |||
| 97808fd855 | |||
| 388a69948e | |||
| 5e7ba6de24 | |||
| adb0fd229c | |||
| 0e545077c6 | |||
| b64cc232f9 | |||
| b0341f06fd | |||
| a3e383b53f | |||
| 1b217b2e75 | |||
| 5662bd8524 | |||
| 28f9048087 | |||
| b17164b68b | |||
| ecebefe218 | |||
| fc374e0108 | |||
| 4295982876 | |||
| 729155d3a4 | |||
| 8c0ae02a71 | |||
| 7f7836877e | |||
| b2733aa171 | |||
| a233bb18bc | |||
| e06a27a5b1 | |||
| 3f5c5480ae | |||
| 53cf71608a | |||
| 883c2677d9 | |||
| 7d98ef87d5 | |||
| a188c5b65e | |||
| 872818fe7c | |||
| 3aef055739 | |||
| 38a5d31b08 | |||
| e43847bbd4 | |||
| a8b8a91c79 | |||
| 695c812bf5 | |||
| 524c84be9e | |||
| 4096442f75 | |||
| 03a4e76292 | |||
| 46a1639990 | |||
| 5ea8039a8a | |||
| 479efbad73 | |||
| a462dd2be3 |
@@ -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"
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
56
compiler/cl-interpret/examples/conlang-run.rs
Normal file
56
compiler/cl-interpret/examples/conlang-run.rs
Normal 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(())
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
291
compiler/cl-interpret/src/convalue.rs
Normal file
291
compiler/cl-interpret/src/convalue.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
165
compiler/cl-interpret/src/env.rs
Normal file
165
compiler/cl-interpret/src/env.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
91
compiler/cl-interpret/src/error.rs
Normal file
91
compiler/cl-interpret/src/error.rs
Normal 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.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
compiler/cl-interpret/src/function.rs
Normal file
49
compiler/cl-interpret/src/function.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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::*;
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
385
compiler/cl-parser/src/parser/prec.rs
Normal file
385
compiler/cl-parser/src/parser/prec.rs
Normal 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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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}\""));
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}"),
|
||||||
|
|||||||
@@ -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)?,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(())?,
|
||||||
|
|||||||
@@ -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"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
48
sample-code/ascii.cl
Executable 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
7
sample-code/fib.cl
Normal file → Executable 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
5
sample-code/fizzbuzz.cl
Normal file → Executable 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
5
sample-code/hello.cl
Normal file → Executable 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!")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
37
sample-code/rand.cl
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -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
4
stdlib/std/str.cl
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
//! TODO: give conland a string type
|
||||||
|
use super::num::u8;
|
||||||
|
|
||||||
|
type str = [u8];
|
||||||
Reference in New Issue
Block a user