ascesis 0.0.6

A language for analysis and synthesis of cause-effect synchronised interacting systems
Documentation
use lalrpop_util::ErrorRecovery;
use crate::{
    CesFile, CesFileBlock, CesName, ImmediateDef, CesImmediate, CesInstance,
    PropBlock, PropValue, CapacitiesBlock, UnboundedBlock, WeightsBlock,
    InhibitorsBlock, HoldersBlock, Rex, ThinArrowRule, FatArrowRule, Polynomial,
    Node, BinOp, Literal, Token, AscesisError, AscesisErrorKind,
};

grammar<'input, 'env>(errors: &'env mut Vec<ErrorRecovery<usize, Token<'input>, AscesisError>>);

// ces_file = { immediate_def | template_def | context_block } ;
pub CesFile: CesFile = <CesFileBlock*> => <>.into();

pub CesFileBlock: CesFileBlock = {
    <ImmediateDef> => <>.into(),
    <PropBlock> => <>.into(),
    <CapsBlock> => <>.into(),
    <UnboundedBlock> => <>.into(),
    <WeightsBlock> => <>.into(),
    <InhibitBlock> => <>.into(),
    <HoldBlock> => <>.into(),
    ! => {
        errors.push(<>);
        CesFileBlock::Bad(AscesisErrorKind::ParsingFailure.into())
    },
};

// Structure definition, immediate form

// immediate_def = immediate_sig "{" { rex } "}" ;
pub ImmediateDef: ImmediateDef = {
    <sig: ImmediateSig> "{" "}" =>
        ImmediateDef::new(sig, Rex::new()),
    <sig: ImmediateSig> "{" <rex: Rex> "}" =>
        ImmediateDef::new(sig, rex),
};

// immediate_sig = "ces" identifier ;
ImmediateSig: CesName =
    "ces" <Identifier> =>
        CesName::from(<>);

// Structure definition, template form

// FIXME
// template_def = template_sig "{" { rex } "}" ;
// template_sig = "ces" identifier "(" { template_args } ")" ;
// template_args = arg_decl { "," arg_decl } [ "," ] ;
// arg_decl = identifier ":" ( "Node" | "CES" | "Size" | "String" ) ;

// Structure instantiation

// ces_immediate = identifier "(" ")" ;
pub CesImmediate: CesImmediate =
    <name: Identifier> "(" ")" =>
        CesImmediate::new(CesName::from(name));

// ces_instance = identifier "!" "(" { instance_args } ")" ;
pub CesInstance: CesInstance = {
    <name: Identifier> "!" "(" ")" =>
        CesInstance::new(CesName::from(name)),
    <name: Identifier> "!" "(" <args: InstanceArgs> ")" =>
        CesInstance::new(CesName::from(name)).with_args(args),
};

// instance_args = arg_value { ","  arg_value } [ "," ] ;
InstanceArgs: Vec<String> = {
    <head: ArgValue> <tail: CommaThenArgValue*> ","? => {
        let mut tail = tail;
        let mut result = vec![head];
        result.append(&mut tail);
        result
    }
};

CommaThenArgValue: String = "," <ArgValue>;

// arg_value = identifier | size | string ;
ArgValue: String = <Identifier>;

// Context

// context_block = prop_block
//               | caps_block
//               | unbounded_block
//               | weights_block
//               | inhibit_block
//               | hold_block ;

// prop_selector = "vis" | "sat" ;
PropSelector: String = {
    "vis" => <>.into(),
    "sat" => <>.into(),
};

// prop_block = prop_selector "{" { prop_list } "}" ;
pub PropBlock: PropBlock = {
    <sel: PropSelector> "{" "}" =>
        PropBlock::new().with_selector(sel),
    <sel: PropSelector> "{" <blk: PropList> "}" =>
        blk.with_selector(sel),
};

// prop_list = prop_field { "," prop_field } [ "," ] ;
PropList: PropBlock =
    <head: PropField> <tail: CommaThenPropField*> ","? =>
        head.with_more(tail);

CommaThenPropField: PropBlock = "," <PropField>;

// prop_field = identifier ":" prop_value ;
PropField: PropBlock =
    <key: Identifier> ":" <value: PropValue> =>
        PropBlock::new().with_prop(key, value);

// prop_value_array = prop_value { "," prop_value } [ "," ] ;
PropValueArray: PropValue =
    <head: PropValue> <tail: CommaThenPropValue*> ","? =>
        head.into_array_with_more(tail);

CommaThenPropValue: PropValue = "," <PropValue>;

// prop_value = name
//            | size_list
//            | node_list
//            | "[" prop_value_array "]"
//            | "{" prop_list "}" ;
PropValue: PropValue = {
    <Name> =>? Ok(PropValue::new_name(<>)?),
    <SizeList> =>? Ok(PropValue::new_size_list(<>)?),
    <IdentifierList> =>? Ok(PropValue::new_node_list(<>)?),
    "[" <PropValueArray> "]" => <>.into(),
    "{" <PropList> "}" => <>.into(),
};

// caps_block = "caps" "{" { cap_list } "}" ;
pub CapsBlock: CapacitiesBlock = {
    "caps" "{" "}" => CapacitiesBlock::new(),
    "caps" "{" <head: CapField> <tail: CommaThenCapField*> ","? "}" =>
        head.with_more(tail),
};

// cap_list = cap_field { "," cap_field } [ "," ] ;
CommaThenCapField: CapacitiesBlock = "," <CapField>;

// cap_field = size node_list ;
CapField: CapacitiesBlock =
    <size: Size> <nodes: NodeList> =>
        CapacitiesBlock::new().with_nodes(size, nodes).unwrap();

// unbounded_block = "unbounded" "{" { node_list } "}" ;
pub UnboundedBlock: UnboundedBlock = {
    "unbounded" "{" "}" => UnboundedBlock::new(),
    "unbounded" "{" <nodes: NodeList> "}" =>
        UnboundedBlock::from_nodes(nodes).unwrap(),
};

// weights_block = "weights" "{" { weight_list } "}" ;
pub WeightsBlock: WeightsBlock = {
    "weights" "{" "}" => WeightsBlock::new(),
    "weights" "{" <head: WeightField> <tail: CommaThenWeightField*> ","? "}" =>
        head.with_more(tail),
};

// weight_list = weight_field { "," weight_field } [ "," ] ;
CommaThenWeightField: WeightsBlock = "," <WeightField>;

// weight_field = size node_list ( "->" | "<-" ) node_list ;
WeightField: WeightsBlock = {
    <size: Size> <pre_nodes: NodeList> "->" <post_set: NodeList> =>
        WeightsBlock::new_effects(size, pre_nodes, post_set).unwrap(),
    <size: Size> <post_nodes: NodeList> "<-" <pre_set: NodeList> =>
        WeightsBlock::new_causes(size, post_nodes, pre_set).unwrap(),
};

// inhibit_block = "inhibit" "{" { inhibit_list } "}" ;
pub InhibitBlock: InhibitorsBlock = {
    "inhibit" "{" "}" => InhibitorsBlock::new(),
    "inhibit" "{" <head: InhibitField> <tail: CommaThenInhibitField*> ","? "}" =>
        head.with_more(tail),
};

// inhibit_list = inhibit_field { "," inhibit_field } [ "," ] ;
CommaThenInhibitField: InhibitorsBlock = "," <InhibitField>;

// inhibit_field = node_list ( "->" | "<-" ) node_list ;
InhibitField: InhibitorsBlock = {
    <pre_nodes: NodeList> "->" <post_set: NodeList> =>
        InhibitorsBlock::new_effects(pre_nodes, post_set).unwrap(),
    <post_nodes: NodeList> "<-" <pre_set: NodeList> =>
        InhibitorsBlock::new_causes(post_nodes, pre_set).unwrap(),
};

// hold_block = "hold" "{" { hold_list } "}" ;
pub HoldBlock: HoldersBlock = {
    "hold" "{" "}" => HoldersBlock::new(),
    "hold" "{" <head: HoldField> <tail: CommaThenHoldField*> ","? "}" =>
        head.with_more(tail),
};

// hold_list = hold_field { "," hold_field } [ "," ] ;
CommaThenHoldField: HoldersBlock = "," <HoldField>;

// hold_field = node_list ( "->" | "<-" ) node_list ;
HoldField: HoldersBlock = {
    <pre_nodes: NodeList> "->" <post_set: NodeList> =>
        HoldersBlock::new_effects(pre_nodes, post_set).unwrap(),
    <post_nodes: NodeList> "<-" <pre_set: NodeList> =>
        HoldersBlock::new_causes(post_nodes, pre_set).unwrap(),
};

// Rule expression

// rex = thin_arrow_rule
//     | fat_arrow_rule
//     | rex_term { [ "+" ] rex_term } ;
pub Rex: Rex = {
    <ThinArrowRule> => <>.into(),
    <FatArrowRule> => <>.into(),
    <head: RexTerm> <tail: (<AddOp?> <RexTerm>)*> => head.with_more(tail),
};

// rex_term = ces_immediate | ces_instance | "{" rex "}" ;
RexTerm: Rex = {
    <CesImmediate> => <>.into(),
    <CesInstance> => <>.into(),
    "{" <Rex> "}",
};

// Arrow rules

// thin_arrow_rule = e_rule | c_rule | ec_rule | ce_rule | fw_rule | bw_rule ;
// e_rule = node_list "->" polynomial ;
// c_rule = node_list "<-" polynomial ;
// ec_rule = node_list "->" polynomial "<-" polynomial ;
// ce_rule = node_list "<-" polynomial "->" polynomial ;
// fw_rule = "+" plain_polynomial "->" node_list "->" polynomial ;
// bw_rule = "+" plain_polynomial "<-" node_list "<-" polynomial ;
pub ThinArrowRule: ThinArrowRule = {
    // effect-only rule
    <nodes: Polynomial> "->" <effect: Polynomial> => {
        ThinArrowRule::new().with_nodes(nodes).unwrap().with_effect(effect)
    },
    // cause-only rule
    <nodes: Polynomial> "<-" <cause: Polynomial> => {
        ThinArrowRule::new().with_nodes(nodes).unwrap().with_cause(cause)
    },
    // effect-then-cause rule
    <nodes: Polynomial> "->" <effect: Polynomial> "<-" <cause: Polynomial> => {
        ThinArrowRule::new().with_nodes(nodes).unwrap().with_cause(cause).with_effect(effect)
    },
    // cause-then-effect rule
    <nodes: Polynomial> "<-" <cause: Polynomial> "->" <effect: Polynomial> => {
        ThinArrowRule::new().with_nodes(nodes).unwrap().with_cause(cause).with_effect(effect)
    },
    // forward rule
    <cause: Polynomial> "->" <nodes: Polynomial> "->" <effect: Polynomial> => {
        ThinArrowRule::new().with_nodes(nodes).unwrap().with_cause(cause).with_effect(effect)
    },
    // backward rule
    <effect: Polynomial> "<-" <nodes: Polynomial> "<-" <cause: Polynomial> => {
        ThinArrowRule::new().with_nodes(nodes).unwrap().with_cause(cause).with_effect(effect)
    },
};

// fat_arrow_rule = polynomial ( "=>" | "<=" | "<=>" ) polynomial { ( "=>" | "<=" | "<=>" ) polynomial } ;
pub FatArrowRule: FatArrowRule =
    <head: Polynomial> <tail: (<FatOp> <Polynomial>)+> =>
        FatArrowRule::from_parts(head, tail);

// Polynomial

// polynomial = [ [ polynomial ] "+" ] poly_term { poly_term } ;
pub Polynomial: Polynomial = {
    AddOp? <head: PolyTerm> <tail: PolyTerm*> => head.with_product_multiplied(tail),
    <head: Polynomial> AddOp <tail: PolyTerm+> => head.with_product_added(tail),
};

// poly_term = identifier | "(" polynomial ")" ;
#[inline]
PolyTerm: Polynomial = {
    <Identifier> => Polynomial::from(Node::from(<>)),
    "(" <Polynomial> ")",
};

NodeList: Polynomial = <Polynomial>;

// Operators

AddOp: BinOp = "+" => BinOp::Add;
ThinOp: BinOp = { "->" => BinOp::ThinTx, "<-" => BinOp::ThinRx };
FatOp: BinOp = { "=>" => BinOp::FatTx, "<=" => BinOp::FatRx, "<=>" => BinOp::FatDx };

// Identifiers

IdentifierList: Vec<String> = <Identifier+>;

Identifier: String = r"[A-Za-z_][A-Za-z0-9_-]*" => <>.to_owned();

// Literals

SizeList: Vec<Literal> = <Size+>;

FiniteSize: Literal = r"[0-9]+" => Literal::from_digits(<>).unwrap();
Omega: Literal = r"[Ωω]" => Literal::omega();
Theta: Literal = r"[Θθ]" => Literal::theta();
Size: Literal = { FiniteSize, Omega };
Name: Literal = r#""[^"]*""# => Literal::from_quoted_str(<>).unwrap();

extern {
    type Location = usize;
    type Error = AscesisError;

    enum Token<'input> {
        r"///.*\n" => Token::DocComment(<&'input str>),
        r"[A-Za-z_][A-Za-z0-9_-]*" => Token::Identifier(<&'input str>),
        r"[0-9]+" => Token::LiteralFiniteSize(<&'input str>),
        r#""[^"]*""# => Token::LiteralName(<&'input str>),
        r"[Ωω]" => Token::Omega,
        r"[Θθ]" => Token::Theta,
        ";" => Token::Semicolon,
        "," => Token::Comma,
        ":" => Token::Colon,
        "{" => Token::OpenCurly,
        "}" => Token::CloseCurly,
        "(" => Token::OpenParen,
        ")" => Token::CloseParen,
        "[" => Token::OpenBracket,
        "]" => Token::CloseBracket,
        "+" => Token::Add,
        "->" => Token::ThinArrow,
        "<-" => Token::ThinBackArrow,
        "=>" => Token::FatArrow,
        "<=" => Token::FatBackArrow,
        "<=>" => Token::FatTwowayArrow,
        "!" => Token::Bang,
        "ces" => Token::Ces,
        "vis" => Token::Vis,
        "sat" => Token::Sat,
        "caps" => Token::Caps,
        "unbounded" => Token::Unbounded,
        "weights" => Token::Weights,
        "inhibit" => Token::Inhibit,
        "hold" => Token::Hold,
    }
}