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:
parent
a033e9f33b
commit
8d8928b8a8
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -95,7 +95,8 @@ Term = Unary (TermOp Unary )* ;
|
||||
|
||||
Unary = (UnaryKind)* Member ;
|
||||
|
||||
Member = Call ('.' Call)* ;
|
||||
Member = Call (Access)* ;
|
||||
Access = '.' (Identifier ('(' Tuple? ')')? | Literal) ;
|
||||
|
||||
Call = Index ('(' Tuple? ')')* ;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user