diff options
author | Matthias Schiffer <mschiffer@universe-factory.net> | 2024-04-28 23:50:46 +0200 |
---|---|---|
committer | Matthias Schiffer <mschiffer@universe-factory.net> | 2024-04-28 23:50:46 +0200 |
commit | 54030e686915a07452a3d4bb5a23616eec30f4fa (patch) | |
tree | b4bea5621abdaa64b06b39521c860bc96b1f2c05 | |
parent | f8c5dc596ca58bb02d60f35984a78e6f962820c9 (diff) | |
download | rebel-54030e686915a07452a3d4bb5a23616eec30f4fa.tar rebel-54030e686915a07452a3d4bb5a23616eec30f4fa.zip |
rebel-parse: split expr out of ast module
-rw-r--r-- | crates/rebel-lang/src/scope.rs | 6 | ||||
-rw-r--r-- | crates/rebel-lang/src/typing.rs | 58 | ||||
-rw-r--r-- | crates/rebel-lang/src/value.rs | 50 | ||||
-rw-r--r-- | crates/rebel-parse/src/ast/expr.rs (renamed from crates/rebel-parse/src/ast.rs) | 125 | ||||
-rw-r--r-- | crates/rebel-parse/src/ast/mod.rs | 125 | ||||
-rw-r--r-- | crates/rebel-parse/src/grammar/recipe.rs | 32 |
6 files changed, 206 insertions, 190 deletions
diff --git a/crates/rebel-lang/src/scope.rs b/crates/rebel-lang/src/scope.rs index 533af54..4df0909 100644 --- a/crates/rebel-lang/src/scope.rs +++ b/crates/rebel-lang/src/scope.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, ops::Deref}; -use rebel_parse::ast; +use rebel_parse::ast::{self, expr}; use crate::{ func::Func, @@ -35,7 +35,7 @@ impl Context { // TODO: Handle other assignable expressions let dest_path = match dest_expr { - ast::Expr::Path(path) => path, + expr::Expr::Path(path) => path, _ => return Err(TypeError), }; let [dest_ident] = dest_path.components[..] else { @@ -73,7 +73,7 @@ impl Context { // TODO: Handle other assignable expressions let dest_path = match dest_expr { - ast::Expr::Path(path) => path, + expr::Expr::Path(path) => path, _ => return Err(EvalError), }; let [dest_ident] = dest_path.components[..] else { diff --git a/crates/rebel-lang/src/typing.rs b/crates/rebel-lang/src/typing.rs index eaf30bc..7eaa5d8 100644 --- a/crates/rebel-lang/src/typing.rs +++ b/crates/rebel-lang/src/typing.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, fmt::Display}; use enum_kinds::EnumKind; -use rebel_parse::ast; +use rebel_parse::ast::{self, expr}; use crate::{func::FuncType, scope::Context}; @@ -92,8 +92,8 @@ impl Type { } } - pub fn ast_expr_type(ctx: &Context, expr: &ast::Expr<'_>) -> Result<Type> { - use ast::Expr::*; + pub fn ast_expr_type(ctx: &Context, expr: &expr::Expr<'_>) -> Result<Type> { + use expr::Expr::*; Ok(match expr { Binary { left, op, right } => Self::binary_op_expr_type(ctx, left, *op, right)?, @@ -114,11 +114,11 @@ impl Type { fn binary_op_expr_type( ctx: &Context, - left: &ast::Expr<'_>, - op: ast::OpBinary, - right: &ast::Expr<'_>, + left: &expr::Expr<'_>, + op: expr::OpBinary, + right: &expr::Expr<'_>, ) -> Result<Type> { - use ast::OpBinary::*; + use expr::OpBinary::*; use Type::*; let tl = Self::ast_expr_type(ctx, left)?; @@ -161,8 +161,8 @@ impl Type { }) } - fn unary_op_expr_type(ctx: &Context, op: ast::OpUnary, expr: &ast::Expr<'_>) -> Result<Type> { - use ast::OpUnary::*; + fn unary_op_expr_type(ctx: &Context, op: expr::OpUnary, expr: &expr::Expr<'_>) -> Result<Type> { + use expr::OpUnary::*; use Type::*; let typ = Self::ast_expr_type(ctx, expr)?; @@ -174,7 +174,11 @@ impl Type { }) } - fn index_expr_type(ctx: &Context, expr: &ast::Expr<'_>, index: &ast::Expr<'_>) -> Result<Type> { + fn index_expr_type( + ctx: &Context, + expr: &expr::Expr<'_>, + index: &expr::Expr<'_>, + ) -> Result<Type> { use Type::*; let expr_type = Self::ast_expr_type(ctx, expr)?; @@ -189,7 +193,11 @@ impl Type { Ok(*elem_type) } - fn apply_expr_type(ctx: &Context, expr: &ast::Expr<'_>, params: &[ast::Expr]) -> Result<Type> { + fn apply_expr_type( + ctx: &Context, + expr: &expr::Expr<'_>, + params: &[expr::Expr], + ) -> Result<Type> { use Type::*; let expr_type = Self::ast_expr_type(ctx, expr)?; @@ -214,9 +222,9 @@ impl Type { fn method_expr_type( ctx: &Context, - expr: &ast::Expr<'_>, + expr: &expr::Expr<'_>, method: &ast::Ident<'_>, - params: &[ast::Expr], + params: &[expr::Expr], ) -> Result<Type> { let expr_type = Self::ast_expr_type(ctx, expr)?; let type_family = TypeFamily::from(&expr_type); @@ -243,7 +251,7 @@ impl Type { fn field_expr_type( ctx: &Context, - expr: &ast::Expr<'_>, + expr: &expr::Expr<'_>, field: &ast::Ident<'_>, ) -> Result<Type> { use Type::*; @@ -274,24 +282,28 @@ impl Type { .ok_or(TypeError) } - fn check_string_piece(ctx: &Context, piece: &ast::StrPiece, kind: ast::StrKind) -> Result<()> { + fn check_string_piece( + ctx: &Context, + piece: &expr::StrPiece, + kind: expr::StrKind, + ) -> Result<()> { let typ = match piece { - ast::StrPiece::Chars(_) => return Ok(()), - ast::StrPiece::Escape(_) => return Ok(()), - ast::StrPiece::Interp(expr) => Self::ast_expr_type(ctx, expr)?, + expr::StrPiece::Chars(_) => return Ok(()), + expr::StrPiece::Escape(_) => return Ok(()), + expr::StrPiece::Interp(expr) => Self::ast_expr_type(ctx, expr)?, }; match (typ, kind) { (Type::Bool, _) => Ok(()), (Type::Int, _) => Ok(()), (Type::Str, _) => Ok(()), - (Type::Tuple(_), ast::StrKind::Script) => Ok(()), - (Type::Array(_, _), ast::StrKind::Script) => Ok(()), + (Type::Tuple(_), expr::StrKind::Script) => Ok(()), + (Type::Array(_, _), expr::StrKind::Script) => Ok(()), _ => Err(TypeError), } } - fn literal_expr_type(ctx: &Context, lit: &ast::Literal<'_>) -> Result<Type> { - use ast::Literal; + fn literal_expr_type(ctx: &Context, lit: &expr::Literal<'_>) -> Result<Type> { + use expr::Literal; use Type::*; Ok(match lit { @@ -319,7 +331,7 @@ impl Type { Literal::Struct(entries) => Struct( entries .iter() - .map(|ast::StructField { key, value }| { + .map(|expr::StructField { key, value }| { Ok(((*key).to_owned(), Self::ast_expr_type(ctx, value)?)) }) .collect::<Result<_>>()?, diff --git a/crates/rebel-lang/src/value.rs b/crates/rebel-lang/src/value.rs index 48d6272..616f393 100644 --- a/crates/rebel-lang/src/value.rs +++ b/crates/rebel-lang/src/value.rs @@ -4,7 +4,7 @@ use std::{ iter, }; -use rebel_parse::ast; +use rebel_parse::ast::{self, expr}; use crate::{ func::{Func, FuncDef}, @@ -62,8 +62,8 @@ impl Value { }) } - pub fn eval(ctx: &Context, expr: &ast::Expr<'_>) -> Result<Value> { - use ast::Expr::*; + pub fn eval(ctx: &Context, expr: &expr::Expr<'_>) -> Result<Value> { + use expr::Expr::*; Ok(match expr { Binary { left, op, right } => Self::eval_binary_op(ctx, left, *op, right)?, @@ -84,11 +84,11 @@ impl Value { fn eval_binary_op( ctx: &Context, - left: &ast::Expr<'_>, - op: ast::OpBinary, - right: &ast::Expr<'_>, + left: &expr::Expr<'_>, + op: expr::OpBinary, + right: &expr::Expr<'_>, ) -> Result<Value> { - use ast::OpBinary::*; + use expr::OpBinary::*; use Value::*; let tl = Self::eval(ctx, left)?; @@ -124,8 +124,8 @@ impl Value { }) } - fn eval_unary_op(ctx: &Context, op: ast::OpUnary, expr: &ast::Expr<'_>) -> Result<Value> { - use ast::OpUnary::*; + fn eval_unary_op(ctx: &Context, op: expr::OpUnary, expr: &expr::Expr<'_>) -> Result<Value> { + use expr::OpUnary::*; use Value::*; let typ = Self::eval(ctx, expr)?; @@ -137,7 +137,7 @@ impl Value { }) } - fn eval_index(ctx: &Context, expr: &ast::Expr<'_>, index: &ast::Expr<'_>) -> Result<Value> { + fn eval_index(ctx: &Context, expr: &expr::Expr<'_>, index: &expr::Expr<'_>) -> Result<Value> { use Value::*; let expr_value = Self::eval(ctx, expr)?; @@ -153,7 +153,7 @@ impl Value { elems.into_iter().nth(index).ok_or(EvalError) } - fn eval_apply(ctx: &Context, expr: &ast::Expr<'_>, params: &[ast::Expr]) -> Result<Value> { + fn eval_apply(ctx: &Context, expr: &expr::Expr<'_>, params: &[expr::Expr]) -> Result<Value> { use Value::*; let value = Self::eval(ctx, expr)?; @@ -189,9 +189,9 @@ impl Value { fn eval_method( ctx: &Context, - expr: &ast::Expr<'_>, + expr: &expr::Expr<'_>, method: &ast::Ident<'_>, - params: &[ast::Expr], + params: &[expr::Expr], ) -> Result<Value> { let expr_value = Self::eval(ctx, expr)?; let expr_type = expr_value.typ().or(Err(EvalError))?; @@ -231,7 +231,7 @@ impl Value { } } - fn eval_field(ctx: &Context, expr: &ast::Expr<'_>, field: &ast::Ident<'_>) -> Result<Value> { + fn eval_field(ctx: &Context, expr: &expr::Expr<'_>, field: &ast::Ident<'_>) -> Result<Value> { use Value::*; let expr_value = Self::eval(ctx, expr)?; @@ -258,8 +258,8 @@ impl Value { .ok_or(EvalError) } - fn eval_literal(ctx: &Context, lit: &ast::Literal<'_>) -> Result<Value> { - use ast::Literal; + fn eval_literal(ctx: &Context, lit: &expr::Literal<'_>) -> Result<Value> { + use expr::Literal; use Value::*; Ok(match lit { @@ -287,7 +287,7 @@ impl Value { Literal::Struct(entries) => Struct( entries .iter() - .map(|ast::StructField { key, value }| { + .map(|expr::StructField { key, value }| { Ok(((*key).to_owned(), Self::eval(ctx, value)?)) }) .collect::<Result<_>>()?, @@ -406,8 +406,8 @@ impl<'a> Display for ScriptStringify<'a> { #[derive(Debug)] struct StrDisplay<'a> { - pieces: &'a [ast::StrPiece<'a>], - kind: ast::StrKind, + pieces: &'a [expr::StrPiece<'a>], + kind: expr::StrKind, ctx: &'a Context, } @@ -415,14 +415,14 @@ impl<'a> Display for StrDisplay<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for piece in self.pieces { match piece { - ast::StrPiece::Chars(chars) => f.write_str(chars)?, - ast::StrPiece::Escape(c) => f.write_char(*c)?, - ast::StrPiece::Interp(expr) => { + expr::StrPiece::Chars(chars) => f.write_str(chars)?, + expr::StrPiece::Escape(c) => f.write_char(*c)?, + expr::StrPiece::Interp(expr) => { let val = Value::eval(self.ctx, expr).or(Err(std::fmt::Error))?; match self.kind { - ast::StrKind::Regular => Stringify(&val).fmt(f), - ast::StrKind::Raw => unreachable!(), - ast::StrKind::Script => ScriptStringify(&val).fmt(f), + expr::StrKind::Regular => Stringify(&val).fmt(f), + expr::StrKind::Raw => unreachable!(), + expr::StrKind::Script => ScriptStringify(&val).fmt(f), }?; } }; diff --git a/crates/rebel-parse/src/ast.rs b/crates/rebel-parse/src/ast/expr.rs index 8be015b..6899903 100644 --- a/crates/rebel-parse/src/ast.rs +++ b/crates/rebel-parse/src/ast/expr.rs @@ -1,100 +1,9 @@ use std::collections::HashSet; +use super::{Ident, Path, ValidationError}; use crate::token; -pub type Recipe<'a> = Vec<RecipeStmt<'a>>; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum RecipeStmt<'a> { - BodyStmt(BodyStmt<'a>), - Fetch { - name: Ident<'a>, - body: Body<'a>, - }, - Task { - name: Ident<'a>, - params: Vec<FuncParam<'a>>, - body: Body<'a>, - }, -} - -impl<'a> RecipeStmt<'a> { - pub fn validate(&self) -> Result<(), ValidationError> { - match self { - RecipeStmt::BodyStmt(stmt) => stmt.validate(), - RecipeStmt::Fetch { name: _, body } => body.validate(), - RecipeStmt::Task { - name: _, - params: _, - body, - } => { - // TODO: Validate params? - body.validate() - } - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Body<'a>(pub Vec<BodyStmt<'a>>); - -impl<'a> Body<'a> { - pub fn validate(&self) -> Result<(), ValidationError> { - for stmt in &self.0 { - stmt.validate()?; - } - Ok(()) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum BodyStmt<'a> { - Assign { - dest: Box<TypedExpr<'a>>, - expr: Box<Expr<'a>>, - }, - Expr { - expr: Box<Expr<'a>>, - }, - Empty, -} - -impl<'a> BodyStmt<'a> { - pub(crate) fn assign( - dest: TypedExpr<'a>, - op: Option<OpBinary>, - swapped: bool, - expr: Expr<'a>, - ) -> Self { - let expr = match op { - Some(op) => { - let dest_expr = dest.expr.clone(); - if swapped { - Expr::binary(expr, op, dest_expr) - } else { - Expr::binary(dest_expr, op, expr) - } - } - None => expr, - }; - BodyStmt::Assign { - dest: Box::new(dest), - expr: Box::new(expr), - } - } - - pub fn validate(&self) -> Result<(), ValidationError> { - match self { - BodyStmt::Assign { dest, expr } => { - // TODO: Extend destination validation - dest.expr.validate()?; - expr.validate() - } - BodyStmt::Expr { expr } => expr.validate(), - BodyStmt::Empty => Ok(()), - } - } -} +pub use token::StrKind; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Expr<'a> { @@ -234,20 +143,6 @@ impl<'a> Expr<'a> { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TypedExpr<'a> { - pub expr: Expr<'a>, - pub typ: Option<Expr<'a>>, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FuncParam<'a> { - pub name: Ident<'a>, - pub typ: Expr<'a>, -} - -pub use token::StrKind; - -#[derive(Debug, Clone, PartialEq, Eq)] pub enum Literal<'a> { Unit, Bool(bool), @@ -377,19 +272,3 @@ impl OpBinary { matches!(self, Eq | Lt | Le | Ne | Ge | Gt) } } - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Path<'a> { - pub components: Vec<Ident<'a>>, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Ident<'a> { - pub name: &'a str, -} - -#[derive(Debug, Clone, Copy)] -pub enum ValidationError { - DuplicateKey, - NeedsParens, -} diff --git a/crates/rebel-parse/src/ast/mod.rs b/crates/rebel-parse/src/ast/mod.rs new file mode 100644 index 0000000..9db7630 --- /dev/null +++ b/crates/rebel-parse/src/ast/mod.rs @@ -0,0 +1,125 @@ +pub mod expr; + +use expr::*; + +pub type Recipe<'a> = Vec<RecipeStmt<'a>>; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RecipeStmt<'a> { + BodyStmt(BodyStmt<'a>), + Fetch { + name: Ident<'a>, + body: Body<'a>, + }, + Task { + name: Ident<'a>, + params: Vec<FuncParam<'a>>, + body: Body<'a>, + }, +} + +impl<'a> RecipeStmt<'a> { + pub fn validate(&self) -> Result<(), ValidationError> { + match self { + RecipeStmt::BodyStmt(stmt) => stmt.validate(), + RecipeStmt::Fetch { name: _, body } => body.validate(), + RecipeStmt::Task { + name: _, + params: _, + body, + } => { + // TODO: Validate params? + body.validate() + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Body<'a>(pub Vec<BodyStmt<'a>>); + +impl<'a> Body<'a> { + pub fn validate(&self) -> Result<(), ValidationError> { + for stmt in &self.0 { + stmt.validate()?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BodyStmt<'a> { + Assign { + dest: Box<TypedExpr<'a>>, + expr: Box<Expr<'a>>, + }, + Expr { + expr: Box<Expr<'a>>, + }, + Empty, +} + +impl<'a> BodyStmt<'a> { + pub(crate) fn assign( + dest: TypedExpr<'a>, + op: Option<OpBinary>, + swapped: bool, + expr: Expr<'a>, + ) -> Self { + let expr = match op { + Some(op) => { + let dest_expr = dest.expr.clone(); + if swapped { + Expr::binary(expr, op, dest_expr) + } else { + Expr::binary(dest_expr, op, expr) + } + } + None => expr, + }; + BodyStmt::Assign { + dest: Box::new(dest), + expr: Box::new(expr), + } + } + + pub fn validate(&self) -> Result<(), ValidationError> { + match self { + BodyStmt::Assign { dest, expr } => { + // TODO: Extend destination validation + dest.expr.validate()?; + expr.validate() + } + BodyStmt::Expr { expr } => expr.validate(), + BodyStmt::Empty => Ok(()), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TypedExpr<'a> { + pub expr: Expr<'a>, + pub typ: Option<Expr<'a>>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FuncParam<'a> { + pub name: Ident<'a>, + pub typ: Expr<'a>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Path<'a> { + pub components: Vec<Ident<'a>>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Ident<'a> { + pub name: &'a str, +} + +#[derive(Debug, Clone, Copy)] +pub enum ValidationError { + DuplicateKey, + NeedsParens, +} diff --git a/crates/rebel-parse/src/grammar/recipe.rs b/crates/rebel-parse/src/grammar/recipe.rs index 61688f5..64c7acd 100644 --- a/crates/rebel-parse/src/grammar/recipe.rs +++ b/crates/rebel-parse/src/grammar/recipe.rs @@ -1,12 +1,12 @@ -use crate::ast::{self, Expr}; +use crate::ast::{self, expr, expr::Expr}; use crate::token::*; pub use rules::*; peg::parser! { pub grammar rules<'a>() for TokenStream<'a> { - use ast::OpBinary::*; - use ast::OpUnary::*; + use expr::OpBinary::*; + use expr::OpUnary::*; pub rule recipe() -> ast::Recipe<'a> = recipe:recipe_stmt()* { recipe } @@ -38,7 +38,7 @@ peg::parser! { } / { ast::BodyStmt::Empty } - rule assign_op() -> Option<ast::OpBinary> + rule assign_op() -> Option<expr::OpBinary> = p('=') { None } / p2('+', '=') { Some(Add) } / p2('-', '=') { Some(Sub) } @@ -89,7 +89,7 @@ peg::parser! { = lit:literal() { Expr::Literal(lit) } / path:path() { Expr::Path(path) } - rule call_params() -> Vec<ast::Expr<'a>> + rule call_params() -> Vec<expr::Expr<'a>> = args:delimited(<expr()>, <p(',')>) { args } rule func_params() -> Vec<ast::FuncParam<'a>> @@ -98,33 +98,33 @@ peg::parser! { rule func_param() -> ast::FuncParam<'a> = name:ident() p(':') typ:expr() { ast::FuncParam { name, typ } } - rule literal() -> ast::Literal<'a> - = [Token::Keyword(Keyword::True)] { ast::Literal::Bool(true) } - / [Token::Keyword(Keyword::False)] { ast::Literal::Bool(false) } + rule literal() -> expr::Literal<'a> + = [Token::Keyword(Keyword::True)] { expr::Literal::Bool(true) } + / [Token::Keyword(Keyword::False)] { expr::Literal::Bool(false) } / [Token::Number(content)] { ? - ast::Literal::number(content) + expr::Literal::number(content) } / [Token::Str(Str { pieces, kind })] { ? let pieces = pieces .iter() .map(|piece| piece.try_into()) .collect::<Result<_, _>>()?; - Ok(ast::Literal::Str{ pieces, kind: *kind }) + Ok(expr::Literal::Str{ pieces, kind: *kind }) } - / p('(') p(')') { ast::Literal::Unit } + / p('(') p(')') { expr::Literal::Unit } / p('(') elements:(expr() ** p(',')) p(',')? p(')') { - ast::Literal::Tuple(elements) + expr::Literal::Tuple(elements) } / p('[') elements:delimited(<expr()>, <p(',')>) p(']') { - ast::Literal::Array(elements) + expr::Literal::Array(elements) } / p('{') entries:delimited(<struct_field()>, <p(',')>) p('}') { - ast::Literal::Struct(entries) + expr::Literal::Struct(entries) } - rule struct_field() -> ast::StructField<'a> + rule struct_field() -> expr::StructField<'a> = key:field() p('=') value:expr() { - ast::StructField { key: key.name, value } + expr::StructField { key: key.name, value } } rule path() -> ast::Path<'a> |