conlang: Add constructor expression for structs!

grammar:
- Add new rules `PathLike`, `Structor`, `Fielder`
- Replace Path with PathLike in Primary expressions

cl-ast:
- Add nodes for Structor and Fielder

cl-parser:
- Add branch to path-expression parsing
- Parse Structor bodies

interpret:
- Add TODO
This commit is contained in:
John 2024-04-20 15:02:16 -05:00
parent efd442bbfa
commit 5341631781
9 changed files with 129 additions and 5 deletions

View File

@ -350,6 +350,8 @@ pub enum ExprKind {
Unary(Unary),
/// An Array [Index] expression: a[10, 20, 30]
Index(Index),
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}`
Structor(Structor),
/// A [path expression](Path): `::`? [PathPart] (`::` [PathPart])*
Path(Path),
/// A [Literal]: 0x42, 1e123, 2.4, "Hello"
@ -466,6 +468,20 @@ pub struct Index {
pub indices: Vec<Expr>,
}
/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Structor {
pub to: Path,
pub init: Vec<Fielder>,
}
/// A [Struct field initializer] expression: [Identifier] (`=` [Expr])?
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Fielder {
pub name: Identifier,
pub init: Option<Box<Expr>>,
}
/// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]`
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Array {

View File

@ -424,6 +424,7 @@ mod display {
ExprKind::Binary(v) => v.fmt(f),
ExprKind::Unary(v) => v.fmt(f),
ExprKind::Index(v) => v.fmt(f),
ExprKind::Structor(v) => v.fmt(f),
ExprKind::Path(v) => v.fmt(f),
ExprKind::Literal(v) => v.fmt(f),
ExprKind::Array(v) => v.fmt(f),
@ -540,6 +541,25 @@ mod display {
}
}
impl Display for Structor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { to, init } = self;
write!(f, "{to}: ")?;
separate(init, ", ")(f.delimit(INLINE_BRACES))
}
}
impl Display for Fielder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { name, init } = self;
write!(f, "{name}")?;
if let Some(init) = init {
write!(f, ": {init}")?;
}
Ok(())
}
}
impl Display for Array {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
separate(&self.values, ", ")(f.delimit(INLINE_SQUARE))

View File

@ -269,6 +269,22 @@ pub trait Fold {
indices: indices.into_iter().map(|e| self.fold_expr(e)).collect(),
}
}
fn fold_structor(&mut self, s: Structor) -> Structor {
let Structor { to, init } = s;
Structor {
to: self.fold_path(to),
init: init.into_iter().map(|f| self.fold_fielder(f)).collect(),
}
}
fn fold_fielder(&mut self, f: Fielder) -> Fielder {
let Fielder { name, init } = f;
Fielder {
name: self.fold_identifier(name),
init: init.map(|e| Box::new(self.fold_expr(*e))),
}
}
fn fold_array(&mut self, a: Array) -> Array {
let Array { values } = a;
Array { values: values.into_iter().map(|e| self.fold_expr(e)).collect() }
@ -499,6 +515,7 @@ pub fn or_fold_expr_kind<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> Ex
ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)),
ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)),
ExprKind::Index(i) => ExprKind::Index(folder.fold_index(i)),
ExprKind::Structor(s) => ExprKind::Structor(folder.fold_structor(s)),
ExprKind::Path(p) => ExprKind::Path(folder.fold_path(p)),
ExprKind::Literal(l) => ExprKind::Literal(folder.fold_literal(l)),
ExprKind::Array(a) => ExprKind::Array(folder.fold_array(a)),

View File

@ -228,6 +228,18 @@ pub trait Visit<'a>: Sized {
self.visit_expr_kind(head);
indices.iter().for_each(|e| self.visit_expr(e));
}
fn visit_structor(&mut self, s: &'a Structor) {
let Structor { to, init } = s;
self.visit_path(to);
init.iter().for_each(|e| self.visit_fielder(e))
}
fn visit_fielder(&mut self, f: &'a Fielder) {
let Fielder { name, init } = f;
self.visit_identifier(name);
if let Some(init) = init {
self.visit_expr(init);
}
}
fn visit_array(&mut self, a: &'a Array) {
let Array { values } = a;
values.iter().for_each(|e| self.visit_expr(e))
@ -415,6 +427,7 @@ pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) {
ExprKind::Binary(b) => visitor.visit_binary(b),
ExprKind::Unary(u) => visitor.visit_unary(u),
ExprKind::Index(i) => visitor.visit_index(i),
ExprKind::Structor(s) => visitor.visit_structor(s),
ExprKind::Path(p) => visitor.visit_path(p),
ExprKind::Literal(l) => visitor.visit_literal(l),
ExprKind::Array(a) => visitor.visit_array(a),

View File

@ -126,17 +126,18 @@ impl Interpret for Expr {
impl Interpret for ExprKind {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
match self {
ExprKind::Empty => Ok(ConValue::Empty),
ExprKind::Assign(v) => v.interpret(env),
ExprKind::Binary(v) => v.interpret(env),
ExprKind::Unary(v) => v.interpret(env),
ExprKind::Index(v) => v.interpret(env),
ExprKind::Structor(v) => v.interpret(env),
ExprKind::Path(v) => v.interpret(env),
ExprKind::Literal(v) => v.interpret(env),
ExprKind::Array(v) => v.interpret(env),
ExprKind::ArrayRep(v) => v.interpret(env),
ExprKind::AddrOf(v) => v.interpret(env),
ExprKind::Block(v) => v.interpret(env),
ExprKind::Empty => Ok(ConValue::Empty),
ExprKind::Group(v) => v.interpret(env),
ExprKind::Tuple(v) => v.interpret(env),
ExprKind::Loop(v) => v.interpret(env),
@ -322,6 +323,11 @@ impl Interpret for Index {
Ok(head)
}
}
impl Interpret for Structor {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
todo!("struct construction in {env}")
}
}
impl Interpret for Path {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
let Self { absolute: _, parts } = self;

View File

@ -91,6 +91,8 @@ pub enum Parsing {
Unary,
UnaryKind,
Index,
Structor,
Fielder,
Call,
Member,
PathExpr,
@ -190,6 +192,8 @@ impl Display for Parsing {
Parsing::Unary => "a unary expression",
Parsing::UnaryKind => "a unary operator",
Parsing::Index => "an indexing expression",
Parsing::Structor => "a struct constructor expression",
Parsing::Fielder => "a struct field expression",
Parsing::Call => "a call expression",
Parsing::Member => "a member access expression",
Parsing::PathExpr => "a path",

View File

@ -820,7 +820,7 @@ impl<'t> Parser<'t> {
// Prefix expressions
let mut head = match self.peek_kind(Parsing::Unary)? {
literal_like!() => self.literal()?.into(),
path_like!() => self.path()?.into(),
path_like!() => self.exprkind_pathlike()?,
TokenKind::Punct(Punct::Amp | Punct::AmpAmp) => self.addrof()?.into(),
TokenKind::Punct(Punct::LCurly) => self.block()?.into(),
TokenKind::Punct(Punct::LBrack) => self.exprkind_arraylike()?,
@ -912,6 +912,38 @@ impl<'t> Parser<'t> {
Ok(head)
}
/// Parses an expression beginning with a [Path] (i.e. [Path] or [Structor])
pub fn exprkind_pathlike(&mut self) -> PResult<ExprKind> {
let head = self.path()?;
Ok(match self.match_op(Punct::Colon, Parsing::PathExpr) {
Ok(_) => ExprKind::Structor(self.structor_body(head)?),
Err(_) => ExprKind::Path(head),
})
}
/// [Structor]Body = `{` ([Fielder] `,`)* [Fielder]? `}`
pub fn structor_body(&mut self, to: Path) -> PResult<Structor> {
let init = delim(
sep(Self::fielder, Punct::Comma, CURLIES.1, Parsing::Structor),
CURLIES,
Parsing::Structor,
)(self)?;
Ok(Structor { to, init })
}
/// [Fielder] = [Identifier] (`:` [Expr])?
pub fn fielder(&mut self) -> PResult<Fielder> {
const PARSING: Parsing = Parsing::Fielder;
Ok(Fielder {
name: self.identifier()?,
init: match self.match_op(Punct::Colon, PARSING) {
Ok(_) => Some(Box::new(self.expr()?)),
Err(_) => None,
},
})
}
/// [Array] = '[' ([Expr] ',')* [Expr]? ']'
///
/// Array and ArrayRef are ambiguous until the second token,

View File

@ -391,6 +391,7 @@ pub mod yamlify {
ExprKind::Binary(k) => k.yaml(y),
ExprKind::Unary(k) => k.yaml(y),
ExprKind::Index(k) => k.yaml(y),
ExprKind::Structor(k) => k.yaml(y),
ExprKind::Path(k) => k.yaml(y),
ExprKind::Literal(k) => k.yaml(y),
ExprKind::Array(k) => k.yaml(y),
@ -461,6 +462,18 @@ pub mod yamlify {
y.key("Index").pair("head", head).list(indices);
}
}
impl Yamlify for Structor {
fn yaml(&self, y: &mut Yamler) {
let Self { to, init } = self;
y.key("Structor").pair("to", to).list(init);
}
}
impl Yamlify for Fielder {
fn yaml(&self, y: &mut Yamler) {
let Self { name: Identifier(name), init } = self;
y.key("Fielder").pair("name", name).pair("init", init);
}
}
impl Yamlify for Array {
fn yaml(&self, y: &mut Yamler) {
let Self { values } = self;

View File

@ -103,12 +103,15 @@ Call = Index ('(' Tuple? ')')* ;
Index = Primary ('[' Indices ']')* ;
Indices = (Expr ',')* Expr? ;
Primary = Literal | Path | Array | ArrayRep | AddrOf
| Block | Group | Loop
| If | While | For | Break | Return | Continue;
Primary = Literal | PathLike | Array | ArrayRep | AddrOf | Block | Group
| Loop | If | While | For | Break | Return | Continue;
Literal = STRING | CHARACTER | FLOAT | INTEGER | Bool ;
PathLike = Path | Structor ;
Structor = Path ':' '{' (Fielder ',')* Fielder? '}' ;
Fielder = Identifier ('=' Expr)? ;
Array = '[' (Expr ',')* Expr? ']' ;
ArrayRep = '[' Expr ';' Expr ']' ;