// ASG IR metavariable parsing
//
// Copyright (C) 2014-2023 Ryan Specialty, LLC.
//
// This file is part of TAME.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//! AIR metavariable parser.
//!
//! See the [parent module](super) for more information.
use super::{
super::{AsgError, ObjectIndex},
ir::AirBindableMeta,
AirAggregate, AirAggregateCtx,
};
use crate::{asg::graph::object::Meta, diagnostic_todo, parse::prelude::*};
/// Metalinguistic variable (metavariable) parser.
#[derive(Debug, PartialEq)]
pub enum AirMetaAggregate {
/// Ready for the start of a metavariable.
Ready,
/// Defining a metavariable.
TplMeta(ObjectIndex),
}
impl Display for AirMetaAggregate {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use AirMetaAggregate::*;
match self {
Ready => write!(f, "ready for metavariable"),
TplMeta(_) => write!(f, "defining metavariable"),
}
}
}
impl ParseState for AirMetaAggregate {
type Token = AirBindableMeta;
type Object = ();
type Error = AsgError;
type Context = AirAggregateCtx;
type Super = AirAggregate;
fn parse_token(
self,
tok: Self::Token,
ctx: &mut Self::Context,
) -> TransitionResult {
use super::ir::{AirBind::*, AirDoc::*, AirMeta::*};
use AirBindableMeta::*;
use AirMetaAggregate::*;
match (self, tok) {
(Ready, AirMeta(MetaStart(span))) => {
let oi_meta = ctx.asg_mut().create(Meta::new_required(span));
Transition(TplMeta(oi_meta)).incomplete()
}
(TplMeta(oi_meta), AirMeta(MetaEnd(cspan))) => {
oi_meta.close(ctx.asg_mut(), cspan);
Transition(Ready).incomplete()
}
(TplMeta(oi_meta), AirMeta(MetaLexeme(lexeme))) => Transition(
TplMeta(oi_meta.append_lexeme(ctx.asg_mut(), lexeme)),
)
.incomplete(),
(TplMeta(oi_meta), AirBind(BindIdent(name))) => ctx
.defines(name)
.and_then(|oi_ident| {
oi_ident.bind_definition(ctx.asg_mut(), name, oi_meta)
})
.map(|_| ())
.transition(TplMeta(oi_meta)),
(TplMeta(oi_meta), AirBind(BindIdentAbstract(meta_name))) => {
diagnostic_todo!(
vec![
oi_meta.note("for this metavariable"),
meta_name.note(
"attempting to bind an abstract identifier with \
this metavariable"
),
],
"attempt to bind abstract identifier to metavariable",
)
}
(TplMeta(oi_meta), AirDoc(DocIndepClause(clause))) => {
oi_meta.desc_short(ctx.asg_mut(), clause);
Transition(TplMeta(oi_meta)).incomplete()
}
// TODO: The user _probably_ meant to use `` in XML NIR,
// so maybe we should have an error to that effect.
(TplMeta(..), tok @ AirDoc(DocText(..))) => {
diagnostic_todo!(
vec![tok.note("this token")],
"AirDoc in metavar context \
(is this something we want to support?)"
)
}
// Reference to another metavariable,
// e.g. using `` in XML NIR.
(TplMeta(oi_meta), AirBind(RefIdent(name))) => {
let oi_ref = ctx.lookup_lexical_or_missing(name);
Transition(TplMeta(oi_meta.concat_ref(ctx.asg_mut(), oi_ref)))
.incomplete()
}
(TplMeta(..), tok @ AirMeta(MetaStart(..))) => {
diagnostic_todo!(
vec![tok.note("this token")],
"AirMeta variant"
)
}
(Ready, tok @ AirMeta(MetaEnd(..))) => {
diagnostic_todo!(
vec![tok.note("this token")],
"unbalanced meta"
)
}
(Ready, tok @ AirMeta(MetaLexeme(..))) => {
diagnostic_todo!(
vec![tok.note("this token")],
"unexpected lexeme"
)
}
// Maybe the token can be handled by the parent frame.
(Ready, tok @ (AirBind(..) | AirDoc(..))) => {
Transition(Ready).dead(tok)
}
}
}
fn is_accepting(&self, _: &Self::Context) -> bool {
matches!(self, Self::Ready)
}
}
impl AirMetaAggregate {
pub(super) fn new() -> Self {
Self::Ready
}
}
#[cfg(test)]
mod test;