summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2024-04-28 23:50:46 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2024-04-28 23:50:46 +0200
commit54030e686915a07452a3d4bb5a23616eec30f4fa (patch)
treeb4bea5621abdaa64b06b39521c860bc96b1f2c05
parentf8c5dc596ca58bb02d60f35984a78e6f962820c9 (diff)
downloadrebel-54030e686915a07452a3d4bb5a23616eec30f4fa.tar
rebel-54030e686915a07452a3d4bb5a23616eec30f4fa.zip
rebel-parse: split expr out of ast module
-rw-r--r--crates/rebel-lang/src/scope.rs6
-rw-r--r--crates/rebel-lang/src/typing.rs58
-rw-r--r--crates/rebel-lang/src/value.rs50
-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.rs125
-rw-r--r--crates/rebel-parse/src/grammar/recipe.rs32
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>