conlang: Struct, tuple member accesses, member function call syntax

Currently uses UFCS in the interpreter. This may change after type checking?
This commit is contained in:
John 2024-05-19 13:55:28 -05:00
parent a033e9f33b
commit 8d8928b8a8
8 changed files with 140 additions and 8 deletions

View File

@ -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<ExprKind>,
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 {

View File

@ -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,

View File

@ -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<F: Fold + ?Sized>(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<F: Fold + ?Sized>(folder: &mut F, kind: ExprKind) -> Ex
ExprKind::Continue(c) => ExprKind::Continue(folder.fold_continue(c)),
}
}
pub fn or_fold_member_kind<F: Fold + ?Sized>(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)),
}
}

View File

@ -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),
}
}

View File

@ -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<ConValue> {
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<ConValue> {
let Self { head, indices } = self;

View File

@ -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<MemberKind> {
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<ExprKind> {
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<BinaryKind> 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,
};
}

View File

@ -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;

View File

@ -95,7 +95,8 @@ Term = Unary (TermOp Unary )* ;
Unary = (UnaryKind)* Member ;
Member = Call ('.' Call)* ;
Member = Call (Access)* ;
Access = '.' (Identifier ('(' Tuple? ')')? | Literal) ;
Call = Index ('(' Tuple? ')')* ;