From 8d8928b8a8d159aaadd44c79762d3f8934fc889d Mon Sep 17 00:00:00 2001 From: John Date: Sun, 19 May 2024 13:55:28 -0500 Subject: [PATCH] conlang: Struct, tuple member accesses, member function call syntax Currently uses UFCS in the interpreter. This may change after type checking? --- compiler/cl-ast/src/ast.rs | 19 ++++++++++++++- compiler/cl-ast/src/ast_impl.rs | 21 ++++++++++++++-- compiler/cl-ast/src/ast_visitor/fold.rs | 17 +++++++++++++ compiler/cl-ast/src/ast_visitor/visit.rs | 19 +++++++++++++++ compiler/cl-interpret/src/interpret.rs | 22 ++++++++++++++++- compiler/cl-parser/src/parser.rs | 31 +++++++++++++++++++++--- compiler/cl-repl/examples/yaml.rs | 16 ++++++++++++ grammar.ebnf | 3 ++- 8 files changed, 140 insertions(+), 8 deletions(-) diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index d2ee65f..3239501 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -352,6 +352,8 @@ pub enum ExprKind { Binary(Binary), /// A [Unary] expression: [`UnaryKind`]\* [`Expr`] Unary(Unary), + /// A [Member] access expression: [`Expr`] [`MemberKind`]\* + Member(Member), /// An Array [Index] expression: a[10, 20, 30] Index(Index), /// A [Struct creation](Structor) expression: [Path] `{` ([Fielder] `,`)* [Fielder]? `}` @@ -443,7 +445,6 @@ pub enum BinaryKind { Mul, Div, Rem, - Dot, Call, } @@ -465,6 +466,22 @@ pub enum UnaryKind { /// Unused Tilde, } + +/// A [Member] access expression: [`Expr`] [`MemberKind`]\* +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Member { + pub head: Box, + pub kind: MemberKind, +} + +/// The kind of [Member] access +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum MemberKind { + Call(Identifier, Tuple), + Struct(Identifier), + Tuple(Literal), +} + /// A repeated [Index] expression: a[10, 20, 30][40, 50, 60] #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Index { diff --git a/compiler/cl-ast/src/ast_impl.rs b/compiler/cl-ast/src/ast_impl.rs index c442bb5..67cdb1a 100644 --- a/compiler/cl-ast/src/ast_impl.rs +++ b/compiler/cl-ast/src/ast_impl.rs @@ -422,6 +422,7 @@ mod display { ExprKind::Assign(v) => v.fmt(f), ExprKind::Binary(v) => v.fmt(f), ExprKind::Unary(v) => v.fmt(f), + ExprKind::Member(v) => v.fmt(f), ExprKind::Index(v) => v.fmt(f), ExprKind::Structor(v) => v.fmt(f), ExprKind::Path(v) => v.fmt(f), @@ -474,7 +475,6 @@ mod display { let Self { kind, parts } = self; let (head, tail) = parts.borrow(); match kind { - BinaryKind::Dot => write!(f, "{head}{kind}{tail}"), BinaryKind::Call => write!(f, "{head}{tail}"), _ => write!(f, "{head} {kind} {tail}"), } @@ -505,7 +505,6 @@ mod display { BinaryKind::Mul => "*", BinaryKind::Div => "/", BinaryKind::Rem => "%", - BinaryKind::Dot => ".", BinaryKind::Call => "()", } .fmt(f) @@ -532,6 +531,23 @@ mod display { } } + impl Display for Member { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { head, kind } = self; + write!(f, "{head}.{kind}") + } + } + + impl Display for MemberKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MemberKind::Call(name, args) => write!(f, "{name}{args}"), + MemberKind::Struct(name) => write!(f, "{name}"), + MemberKind::Tuple(name) => write!(f, "{name}"), + } + } + } + impl Display for Index { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { head, indices } = self; @@ -735,6 +751,7 @@ mod convert { Assign => ExprKind::Assign, Binary => ExprKind::Binary, Unary => ExprKind::Unary, + Member => ExprKind::Member, Index => ExprKind::Index, Path => ExprKind::Path, Literal => ExprKind::Literal, diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index 7bc1dea..a2f9926 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -262,6 +262,13 @@ pub trait Fold { fn fold_unary_kind(&mut self, kind: UnaryKind) -> UnaryKind { kind } + fn fold_member(&mut self, m: Member) -> Member { + let Member { head, kind } = m; + Member { head: Box::new(self.fold_expr_kind(*head)), kind: self.fold_member_kind(kind) } + } + fn fold_member_kind(&mut self, kind: MemberKind) -> MemberKind { + or_fold_member_kind(self, kind) + } fn fold_index(&mut self, i: Index) -> Index { let Index { head, indices } = i; Index { @@ -517,6 +524,7 @@ pub fn or_fold_expr_kind(folder: &mut F, kind: ExprKind) -> Ex ExprKind::Assign(a) => ExprKind::Assign(folder.fold_assign(a)), ExprKind::Binary(b) => ExprKind::Binary(folder.fold_binary(b)), ExprKind::Unary(u) => ExprKind::Unary(folder.fold_unary(u)), + ExprKind::Member(m) => ExprKind::Member(folder.fold_member(m)), 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)), @@ -536,3 +544,12 @@ pub fn or_fold_expr_kind(folder: &mut F, kind: ExprKind) -> Ex ExprKind::Continue(c) => ExprKind::Continue(folder.fold_continue(c)), } } +pub fn or_fold_member_kind(folder: &mut F, kind: MemberKind) -> MemberKind { + match kind { + MemberKind::Call(name, args) => { + MemberKind::Call(folder.fold_identifier(name), folder.fold_tuple(args)) + } + MemberKind::Struct(name) => MemberKind::Struct(folder.fold_identifier(name)), + MemberKind::Tuple(name) => MemberKind::Tuple(folder.fold_literal(name)), + } +} diff --git a/compiler/cl-ast/src/ast_visitor/visit.rs b/compiler/cl-ast/src/ast_visitor/visit.rs index e18efa9..dbf8000 100644 --- a/compiler/cl-ast/src/ast_visitor/visit.rs +++ b/compiler/cl-ast/src/ast_visitor/visit.rs @@ -223,6 +223,14 @@ pub trait Visit<'a>: Sized { self.visit_expr_kind(tail); } fn visit_unary_kind(&mut self, _kind: &'a UnaryKind) {} + fn visit_member(&mut self, m: &'a Member) { + let Member { head, kind } = m; + self.visit_expr_kind(head); + self.visit_member_kind(kind); + } + fn visit_member_kind(&mut self, kind: &'a MemberKind) { + or_visit_member_kind(self, kind) + } fn visit_index(&mut self, i: &'a Index) { let Index { head, indices } = i; self.visit_expr_kind(head); @@ -431,6 +439,7 @@ pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) { ExprKind::Assign(a) => visitor.visit_assign(a), ExprKind::Binary(b) => visitor.visit_binary(b), ExprKind::Unary(u) => visitor.visit_unary(u), + ExprKind::Member(m) => visitor.visit_member(m), ExprKind::Index(i) => visitor.visit_index(i), ExprKind::Structor(s) => visitor.visit_structor(s), ExprKind::Path(p) => visitor.visit_path(p), @@ -450,3 +459,13 @@ pub fn or_visit_expr_kind<'a, V: Visit<'a>>(visitor: &mut V, e: &'a ExprKind) { ExprKind::Continue(c) => visitor.visit_continue(c), } } +pub fn or_visit_member_kind<'a, V: Visit<'a>>(visitor: &mut V, kind: &'a MemberKind) { + match kind { + MemberKind::Call(field, args) => { + visitor.visit_identifier(field); + visitor.visit_tuple(args); + } + MemberKind::Struct(field) => visitor.visit_identifier(field), + MemberKind::Tuple(field) => visitor.visit_literal(field), + } +} diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index c98a190..e7945a5 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -130,6 +130,7 @@ impl Interpret for ExprKind { ExprKind::Assign(v) => v.interpret(env), ExprKind::Binary(v) => v.interpret(env), ExprKind::Unary(v) => v.interpret(env), + ExprKind::Member(v) => v.interpret(env), ExprKind::Index(v) => v.interpret(env), ExprKind::Structor(v) => v.interpret(env), ExprKind::Path(v) => v.interpret(env), @@ -256,7 +257,6 @@ impl Interpret for Binary { BinaryKind::Mul => head * tail, BinaryKind::Div => head / tail, BinaryKind::Rem => head % tail, - BinaryKind::Dot => todo!("search within a type's namespace!"), BinaryKind::Call => match tail { ConValue::Empty => head.call(env, &[]), ConValue::Tuple(args) => head.call(env, &args), @@ -313,6 +313,26 @@ impl Interpret for Unary { } } } +impl Interpret for Member { + fn interpret(&self, env: &mut Environment) -> IResult { + let Member { head, kind } = self; + let head = head.interpret(env)?; + match (head, kind) { + (ConValue::Tuple(v), MemberKind::Tuple(Literal::Int(id))) => v + .get(*id as usize) + .cloned() + .ok_or(Error::OobIndex(*id as usize, v.len())), + (head, MemberKind::Call(name, args)) => { + let mut values = vec![head]; + for arg in &args.exprs { + values.push(arg.interpret(env)?); + } + env.call(name.0, &values) + } + _ => Err(Error::TypeError)?, + } + } +} impl Interpret for Index { fn interpret(&self, env: &mut Environment) -> IResult { let Self { head, indices } = self; diff --git a/compiler/cl-parser/src/parser.rs b/compiler/cl-parser/src/parser.rs index 4facf95..66b9408 100644 --- a/compiler/cl-parser/src/parser.rs +++ b/compiler/cl-parser/src/parser.rs @@ -862,6 +862,7 @@ impl<'t> Parser<'t> { Some(match op { Punct::LBrack => Precedence::Index, Punct::LParen => Precedence::Call, + Punct::Dot => Precedence::Member, _ => None?, }) } @@ -889,6 +890,10 @@ impl<'t> Parser<'t> { } .into() } + Punct::Dot => { + let kind = self.access()?; + Member { head: Box::new(head), kind }.into() + } _ => Err(self.error(Unexpected(TokenKind::Punct(op)), parsing))?, }; continue; @@ -922,6 +927,28 @@ impl<'t> Parser<'t> { Ok(head) } + pub fn access(&mut self) -> PResult { + const PARSING: Parsing = Parsing::Member; + const DEL: (Punct, Punct) = PARENS; // delimiter + match self.peek_kind(PARSING)? { + TokenKind::Identifier => { + let name = self.identifier()?; + if self.match_op(DEL.0, PARSING).is_err() { + Ok(MemberKind::Struct(name)) + } else { + let exprs = sep(Self::expr, Punct::Comma, DEL.1, PARSING)(self)?; + self.match_op(DEL.1, PARSING)?; // should succeed + Ok(MemberKind::Call(name, Tuple { exprs })) + } + } + TokenKind::Literal => { + let name = self.literal()?; // TODO: Maybe restrict this to just + Ok(MemberKind::Tuple(name)) + } + t => Err(self.error(Unexpected(t), PARSING)), + } + } + /// Parses an expression beginning with a [Path] (i.e. [Path] or [Structor]) pub fn exprkind_pathlike(&mut self) -> PResult { let head = self.path()?; @@ -1174,7 +1201,7 @@ impl Precedence { } pub fn postfix(self) -> Option<(u8, ())> { match self { - Self::Index | Self::Call => Some((self.level(), ())), + Self::Index | Self::Call | Self::Member => Some((self.level(), ())), _ => None, } } @@ -1190,7 +1217,6 @@ impl From for Precedence { use BinaryKind as Op; match value { Op::Call => Precedence::Call, - Op::Dot => Precedence::Member, Op::Mul | Op::Div | Op::Rem => Precedence::Term, Op::Add | Op::Sub => Precedence::Factor, Op::Shl | Op::Shr => Precedence::Shift, @@ -1264,6 +1290,5 @@ operator! { Star => Mul, Slash => Div, Rem => Rem, - Dot => Dot, }; } diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index fabdd4c..2a45b43 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -391,6 +391,7 @@ pub mod yamlify { ExprKind::Assign(k) => k.yaml(y), ExprKind::Binary(k) => k.yaml(y), ExprKind::Unary(k) => k.yaml(y), + ExprKind::Member(k) => k.yaml(y), ExprKind::Index(k) => k.yaml(y), ExprKind::Structor(k) => k.yaml(y), ExprKind::Path(k) => k.yaml(y), @@ -451,6 +452,21 @@ pub mod yamlify { y.value(self); } } + impl Yamlify for Member { + fn yaml(&self, y: &mut Yamler) { + let Self { head, kind } = self; + y.key("Member").pair("head", head).pair("kind", kind); + } + } + impl Yamlify for MemberKind { + fn yaml(&self, y: &mut Yamler) { + match self { + MemberKind::Call(id, args) => y.pair("id", id).pair("args", args), + MemberKind::Struct(id) => y.pair("id", id), + MemberKind::Tuple(id) => y.pair("id", id), + }; + } + } impl Yamlify for Tuple { fn yaml(&self, y: &mut Yamler) { let Self { exprs } = self; diff --git a/grammar.ebnf b/grammar.ebnf index 235ade0..e9a2887 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -95,7 +95,8 @@ Term = Unary (TermOp Unary )* ; Unary = (UnaryKind)* Member ; -Member = Call ('.' Call)* ; +Member = Call (Access)* ; +Access = '.' (Identifier ('(' Tuple? ')')? | Literal) ; Call = Index ('(' Tuple? ')')* ;