Skip to content

Commit

Permalink
Block expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
Quaqqer committed Jan 5, 2024
1 parent 91ae8f7 commit 57522b2
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 14 deletions.
13 changes: 11 additions & 2 deletions crates/saft-ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ pub enum Statement {
Item(Item),
}

#[derive(Debug, Clone, PartialEq)]
pub struct Block {
pub stmts: Vec<Spanned<Statement>>,
pub tail: Option<Box<Spanned<Expr>>>,
}

#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Nil,
Expand All @@ -27,9 +33,11 @@ pub enum Expr {
String(String),

Var(Spanned<Ident>),
Grouping(Box<Spanned<Expr>>),
Vec(Vec<Spanned<Expr>>),

Grouping(Box<Spanned<Expr>>),
Block(Block),

Neg(Box<Spanned<Expr>>),
Not(Box<Spanned<Expr>>),
Assign(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
Expand Down Expand Up @@ -84,7 +92,8 @@ impl Expr {
Eq(..) => "equal",
Ne(..) => "not equal",
IDiv(..) => "integer division",
Not(_) => "not",
Not(..) => "not",
Block(..) => "block",
}
}
}
Expand Down
12 changes: 11 additions & 1 deletion crates/saft-eval/src/interpreter.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::natives::add_natives;
use crate::value::{Cast, Function, NativeFuncData, Num, SaftFunction, Value};
use codespan_reporting::diagnostic::{Diagnostic, Label};
use saft_ast::{Expr, Ident, Item, Module, Statement};
use saft_ast::{Block, Expr, Ident, Item, Module, Statement};
use saft_common::span::{Span, Spanned};
use std::borrow::Borrow;
use std::collections::HashMap;
Expand Down Expand Up @@ -586,6 +586,16 @@ impl Interpreter {
.ok_or::<Exception>(cast_error!(v, "numeric"))?),
))
}
Expr::Block(Block { stmts, tail }) => self.scoped(|interpreter| {
for stmt in stmts {
interpreter.exec_statement(stmt)?;
}

match tail {
Some(box expr) => Ok::<_, ControlFlow>(interpreter.eval_expr(expr)?.v),
None => Ok(Value::Nil),
}
})?,
}))
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/saft-eval/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ impl Num {
}
}

#[allow(clippy::should_implement_trait)]
pub fn eq(&self, rhs: impl Borrow<Num>) -> bool {
match bin_promote(self, rhs.borrow()) {
(Num::Int(a), Num::Int(b)) => a == b,
Expand Down Expand Up @@ -244,6 +245,7 @@ pub struct SaftFunction {
#[derive(Clone, Debug)]
pub struct NativeFuncData {
pub name: &'static str,
#[allow(clippy::type_complexity)]
pub f: fn(&mut Interpreter, &Span, Vec<Spanned<Value>>) -> Result<Value, ControlFlow>,
}

Expand Down
2 changes: 1 addition & 1 deletion crates/saft-lexer/src/lex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ mod test {
assert_eq!(Lexer::new(src).all_tokens(), spanned_tokens);
}

fn spanned<'a>(t: T, r: Range<usize>) -> Spanned<T> {
fn spanned(t: T, r: Range<usize>) -> Spanned<T> {
Spanned::new(t, span(r))
}

Expand Down
88 changes: 78 additions & 10 deletions crates/saft-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::{borrow::Borrow, collections::VecDeque};

use ast::{Expr, Item, Module, Statement};
use ast::{Block, Expr, Item, Module, Statement};
use codespan_reporting::diagnostic::{Diagnostic, Label};
use saft_ast as ast;
use saft_common::span::{Span, Spanned};
Expand All @@ -21,6 +21,16 @@ fn unexpected(got: impl Borrow<Spanned<Token>>, expected: impl Into<String>) ->
}
}

macro_rules! exotic {
($span:expr, $msg:expr) => {
return Err(exotic($span, $msg))
};
}

fn exotic(span: impl Borrow<Span>, expected: impl Into<String>) -> Error {
Error::Exotic(span.borrow().spanned(expected.into()))
}

mod prec {
pub const NONE: i32 = 0;
pub const ASSIGN: i32 = 1;
Expand All @@ -42,16 +52,20 @@ pub enum Error {
got: Spanned<Token>,
expected: String,
},
Exotic(Spanned<String>),
}

impl Error {
pub fn diagnostic<FileId>(&self, file_id: FileId) -> Diagnostic<FileId> {
pub fn diagnostic<FileId>(self, file_id: FileId) -> Diagnostic<FileId> {
match self {
Error::UnexpectedToken { got, expected } => Diagnostic::error()
.with_message("Got an unexpected token")
.with_labels(vec![Label::primary(file_id, got.s.r.clone()).with_message(
.with_labels(vec![Label::primary(file_id, got.s.r).with_message(
format!("Got {} but expected {}", got.v.describe(), expected),
)]),
Error::Exotic(sm) => Diagnostic::error()
.with_message(sm.v)
.with_labels(vec![Label::primary(file_id, sm.s.r)]),
}
}
}
Expand Down Expand Up @@ -93,6 +107,18 @@ impl<'a> Parser<'a> {
}
}

fn eat_msg(&mut self, t: Token, msg: impl Into<String>) -> Result<Span, Error> {
let st = self.peek();

match st.v {
v if v == t => {
self.advance();
Ok(st.s)
}
_ => exotic!(st.s, msg),
}
}

fn eat_ident(&mut self) -> Result<Spanned<String>, Error> {
let st = self.peek();

Expand All @@ -105,15 +131,15 @@ impl<'a> Parser<'a> {
}
}

fn try_eat(&mut self, t: Token) -> bool {
fn try_eat(&mut self, t: Token) -> Option<Span> {
let st = self.peek();

match st.v {
v if v == t => {
self.eat(t).expect("Was peeked, should not happen");
true
Some(st.s)
}
_ => false,
_ => None,
}
}

Expand Down Expand Up @@ -164,6 +190,44 @@ impl<'a> Parser<'a> {
Ok(stmts)
}

pub fn parse_block(&mut self, start: Option<Span>) -> Result<Spanned<Expr>, Error> {
let start = match start {
Some(s) => s,
None => self.eat(Token::LBrace)?,
};

let mut stmts = Vec::new();

while self.peek().v != Token::RBrace {
let stmt = self.parse_statement()?;

match &stmt.v {
Statement::Expr(e) => {
if let Some(end) = self.try_eat(Token::RBrace) {
let s = start.join(end);

return Ok(s.spanned(Expr::Block(Block {
stmts,
tail: Some(Box::new(e.clone())),
})));
}
}
Statement::Item(_) => {}
_ => {
self.eat_msg(Token::Semicolon, "Expected a ';' after a statement")?;
}
}

stmts.push(stmt);
}

let end = self.eat(Token::RBrace)?;

let s = start.join(end);

Ok(s.spanned(Expr::Block(Block { stmts, tail: None })))
}

pub fn parse_statement(&mut self) -> Result<Spanned<ast::Statement>, Error> {
let st = self.peek();

Expand Down Expand Up @@ -194,7 +258,8 @@ impl<'a> Parser<'a> {
| Token::LBracket
| Token::True
| Token::False
| Token::Bang => {
| Token::Bang
| Token::LBrace => {
let expr = self.parse_expr()?;
Ok(expr.s.clone().spanned(Statement::Expr(expr)))
}
Expand Down Expand Up @@ -250,14 +315,17 @@ impl<'a> Parser<'a> {

exprs.push(self.parse_expr()?);

if !self.try_eat(T::Comma) {
if self.try_eat(T::Comma).is_none() {
break;
}
}
let end = self.eat(T::RBracket)?;
let s = start.join(end);
Ok(s.spanned(Expr::Vec(exprs)))
}
T::LBrace => {
self.parse_block(Some(start))
}
T::True => Ok(st.s.spanned(Expr::Bool(true))),
T::False => Ok(st.s.spanned(Expr::Bool(false))),

Expand Down Expand Up @@ -335,7 +403,7 @@ impl<'a> Parser<'a> {
let arg = parser.parse_expr()?;
arguments.push(arg);

let ate_comma = parser.try_eat(Token::Comma);
let ate_comma = parser.try_eat(Token::Comma).is_some();
if !ate_comma {
break;
}
Expand Down Expand Up @@ -375,7 +443,7 @@ impl<'a> Parser<'a> {
let param = self.eat_ident()?;
params.push(param);

if !self.try_eat(Token::Comma) {
if self.try_eat(Token::Comma).is_none() {
break;
}
}
Expand Down

0 comments on commit 57522b2

Please sign in to comment.