conlang: Add unconditional loop expression, for desugaring

This commit is contained in:
John 2024-04-17 00:29:09 -05:00
parent ec1a1255ad
commit 00d72b823a
8 changed files with 62 additions and 10 deletions

View File

@ -367,6 +367,7 @@ mod display {
ExprKind::Empty => "()".fmt(f),
ExprKind::Group(v) => v.fmt(f),
ExprKind::Tuple(v) => v.fmt(f),
ExprKind::Loop(v) => v.fmt(f),
ExprKind::While(v) => v.fmt(f),
ExprKind::If(v) => v.fmt(f),
ExprKind::For(v) => v.fmt(f),
@ -534,6 +535,12 @@ mod display {
write!(f, "({})", self.expr)
}
}
impl Display for Loop {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { body } = self;
write!(f, "loop {body}")
}
}
impl Display for While {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { cond, pass, fail } = self;

View File

@ -346,6 +346,8 @@ pub enum ExprKind {
Group(Group),
/// A [Tuple] expression: `(` [`Expr`] (`,` [`Expr`])+ `)`
Tuple(Tuple),
/// A [Loop] expression: `loop` [`Block`]
Loop(Loop),
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
While(While),
/// An [If] expression: `if` [`Expr`] [`Block`] [`Else`]?
@ -492,6 +494,12 @@ pub struct Tuple {
pub exprs: Vec<Expr>,
}
/// A [Loop] expression: `loop` [`Block`]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Loop {
pub body: Block,
}
/// A [While] expression: `while` [`Expr`] [`Block`] [`Else`]?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct While {

View File

@ -131,6 +131,7 @@ impl Interpret for ExprKind {
ExprKind::Empty => Ok(ConValue::Empty),
ExprKind::Group(v) => v.interpret(env),
ExprKind::Tuple(v) => v.interpret(env),
ExprKind::Loop(v) => v.interpret(env),
ExprKind::While(v) => v.interpret(env),
ExprKind::If(v) => v.interpret(env),
ExprKind::For(v) => v.interpret(env),
@ -365,17 +366,32 @@ impl Interpret for Tuple {
)?))
}
}
impl Interpret for While {
impl Interpret for Loop {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { cond, pass, fail } = self;
while cond.interpret(env)?.truthy()? {
match pass.interpret(env) {
let Self { body } = self;
loop {
match body.interpret(env) {
Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,
e => e?,
};
}
fail.interpret(env)
}
}
impl Interpret for While {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { cond, pass, fail } = self;
loop {
if cond.interpret(env)?.truthy()? {
match pass.interpret(env) {
Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,
e => e?,
};
} else {
break fail.interpret(env);
}
}
}
}
impl Interpret for If {
@ -392,23 +408,24 @@ impl Interpret for For {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { bind: Identifier(name), cond, pass, fail } = self;
// TODO: A better iterator model
let bounds = match cond.interpret(env)? {
let mut bounds = match cond.interpret(env)? {
ConValue::RangeExc(a, b) => a..=b,
ConValue::RangeInc(a, b) => a..=b,
_ => Err(Error::TypeError)?,
};
{
loop {
let mut env = env.frame("loop variable");
for loop_var in bounds {
if let Some(loop_var) = bounds.next() {
env.insert(name, Some(loop_var.into()));
match pass.interpret(&mut env) {
Err(Error::Break(value)) => return Ok(value),
Err(Error::Continue) => continue,
result => result?,
};
} else {
break fail.interpret(&mut env);
}
}
fail.interpret(env)
}
}
impl Interpret for Else {

View File

@ -103,6 +103,7 @@ pub enum Parsing {
Block,
Group,
Tuple,
Loop,
While,
If,
For,
@ -204,6 +205,7 @@ impl Display for Parsing {
Parsing::Block => "a block",
Parsing::Group => "a grouped expression",
Parsing::Tuple => "a tuple",
Parsing::Loop => "an unconditional loop expression",
Parsing::While => "a while expression",
Parsing::If => "an if expression",
Parsing::For => "a for expression",

View File

@ -809,6 +809,7 @@ impl<'t> Parser<'t> {
self.consume_peeked();
Unary { kind, tail: self.exprkind(after)?.into() }.into()
}
TokenKind::Loop => ExprKind::Loop(self.parse_loop()?),
TokenKind::While => ExprKind::While(self.parse_while()?),
TokenKind::If => ExprKind::If(self.parse_if()?),
TokenKind::For => ExprKind::For(self.parse_for()?),
@ -1010,6 +1011,12 @@ impl<'t> Parser<'t> {
}
/// ## Control flow subexpressions
impl<'t> Parser<'t> {
/// [Loop] = `loop` [Block]
pub fn parse_loop(&mut self) -> PResult<Loop> {
self.match_type(TokenKind::Loop, Parsing::Loop)?;
Ok(Loop { body: self.block()? })
}
/// [While] = `while` [Expr] [Block] [Else]?
pub fn parse_while(&mut self) -> PResult<While> {
self.match_type(TokenKind::While, Parsing::While)?;

View File

@ -383,6 +383,7 @@ pub mod yamlify {
ExprKind::Empty => {}
ExprKind::Group(k) => k.yaml(y),
ExprKind::Tuple(k) => k.yaml(y),
ExprKind::Loop(k) => k.yaml(y),
ExprKind::While(k) => k.yaml(y),
ExprKind::If(k) => k.yaml(y),
ExprKind::For(k) => k.yaml(y),
@ -472,6 +473,12 @@ pub mod yamlify {
y.key("Group").yaml(expr);
}
}
impl Yamlify for Loop {
fn yaml(&self, y: &mut Yamler) {
let Self { body } = self;
y.key("Loop").yaml(body);
}
}
impl Yamlify for While {
fn yaml(&self, y: &mut Yamler) {
let Self { cond, pass, fail } = self;

View File

@ -26,6 +26,7 @@ pub enum TokenKind {
Impl,
In,
Let,
Loop,
Mod,
Mut,
Pub,
@ -122,6 +123,7 @@ impl Display for TokenKind {
TokenKind::Impl => "impl".fmt(f),
TokenKind::In => "in".fmt(f),
TokenKind::Let => "let".fmt(f),
TokenKind::Loop => "loop".fmt(f),
TokenKind::Mod => "mod".fmt(f),
TokenKind::Mut => "mut".fmt(f),
TokenKind::Pub => "pub".fmt(f),
@ -158,6 +160,7 @@ impl FromStr for TokenKind {
"impl" => Self::Impl,
"in" => Self::In,
"let" => Self::Let,
"loop" => Self::Loop,
"mod" => Self::Mod,
"mut" => Self::Mut,
"pub" => Self::Pub,

View File

@ -99,7 +99,7 @@ Index = Primary ('[' Indices ']')* ;
Indices = (Expr ',')* Expr? ;
Primary = Literal | Path | Array | ArrayRep | AddrOf
| Block | Group
| Block | Group | Loop
| If | While | For | Break | Return | Continue;
Literal = STRING | CHARACTER | FLOAT | INTEGER | Bool ;
@ -114,6 +114,7 @@ Block = '{' Stmt* '}';
Group = Empty | '(' (Expr | Tuple) ')' ;
Tuple = (Expr ',')* Expr? ;
Loop = "loop" Block ;
While = "while" Expr Block Else ;
If = "if" Expr Block Else ;
For = "for" Identifier "in" Expr Block Else ;