From 5341631781aa886987b57917c22dc1d965bf853f Mon Sep 17 00:00:00 2001 From: John Date: Sat, 20 Apr 2024 15:02:16 -0500 Subject: [PATCH] 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 --- compiler/cl-ast/src/ast.rs | 16 +++++++++++ compiler/cl-ast/src/ast_impl.rs | 20 ++++++++++++++ compiler/cl-ast/src/ast_visitor/fold.rs | 17 ++++++++++++ compiler/cl-ast/src/ast_visitor/visit.rs | 13 +++++++++ compiler/cl-interpret/src/interpret.rs | 8 +++++- compiler/cl-parser/src/error.rs | 4 +++ compiler/cl-parser/src/parser.rs | 34 +++++++++++++++++++++++- compiler/cl-repl/examples/yaml.rs | 13 +++++++++ grammar.ebnf | 9 ++++--- 9 files changed, 129 insertions(+), 5 deletions(-) diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index dd5d3f2..7fafffa 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -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, } +/// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}` +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Structor { + pub to: Path, + pub init: Vec, +} + +/// A [Struct field initializer] expression: [Identifier] (`=` [Expr])? +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Fielder { + pub name: Identifier, + pub init: Option>, +} + /// An [Array] literal: `[` [`Expr`] (`,` [`Expr`])\* `]` #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Array { diff --git a/compiler/cl-ast/src/ast_impl.rs b/compiler/cl-ast/src/ast_impl.rs index 740d350..8db3133 100644 --- a/compiler/cl-ast/src/ast_impl.rs +++ b/compiler/cl-ast/src/ast_impl.rs @@ -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)) diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index 36481d8..7ee0fec 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -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(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)), diff --git a/compiler/cl-ast/src/ast_visitor/visit.rs b/compiler/cl-ast/src/ast_visitor/visit.rs index dd7992c..fe17611 100644 --- a/compiler/cl-ast/src/ast_visitor/visit.rs +++ b/compiler/cl-ast/src/ast_visitor/visit.rs @@ -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), diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index 5e74275..0459fde 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -126,17 +126,18 @@ impl Interpret for Expr { impl Interpret for ExprKind { fn interpret(&self, env: &mut Environment) -> IResult { 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 { + todo!("struct construction in {env}") + } +} impl Interpret for Path { fn interpret(&self, env: &mut Environment) -> IResult { let Self { absolute: _, parts } = self; diff --git a/compiler/cl-parser/src/error.rs b/compiler/cl-parser/src/error.rs index c31c4ac..dc63c26 100644 --- a/compiler/cl-parser/src/error.rs +++ b/compiler/cl-parser/src/error.rs @@ -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", diff --git a/compiler/cl-parser/src/parser.rs b/compiler/cl-parser/src/parser.rs index cf39986..8b3082a 100644 --- a/compiler/cl-parser/src/parser.rs +++ b/compiler/cl-parser/src/parser.rs @@ -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 { + 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 { + 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 { + 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, diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index ddb34eb..503967d 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -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; diff --git a/grammar.ebnf b/grammar.ebnf index 85e7da1..ba02f91 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -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 ']' ;