{% macro render_visitor(language) -%}
{%- set target = model.ir_languages[language].target -%}
use std::rc::Rc;
use crate::cst::TerminalNode;
#[allow(clippy::wildcard_imports)]
use super::nodes::*;
pub trait Visitor {
{%- for parent_type, sequence in target.sequences %}
fn enter_{{ parent_type | snake_case }}(&mut self, _node: &{{ parent_type }}) -> bool { true }
fn leave_{{ parent_type | snake_case }}(&mut self, _node: &{{ parent_type }}) {}
{% endfor -%}
{%- for parent_type, choice in target.choices %}
fn enter_{{ parent_type | snake_case }}(&mut self, _node: &{{ parent_type }}) -> bool { true }
fn leave_{{ parent_type | snake_case }}(&mut self, _node: &{{ parent_type }}) {}
{% endfor -%}
{%- for parent_type, collection in target.collections %}
fn enter_{{ parent_type | snake_case }}(&mut self, _items: &{{ parent_type }}) -> bool { true }
fn leave_{{ parent_type | snake_case }}(&mut self, _items: &{{ parent_type }}) {}
{% endfor -%}
}
//
// Sequences:
//
{% for parent_type, sequence in target.sequences %}
pub fn accept_{{ parent_type | snake_case }}(node: &{{ parent_type }}, visitor: &mut impl Visitor) {
if !visitor.enter_{{ parent_type | snake_case }}(node) {
return;
}
{% for field in sequence.fields -%}
{%- if not field.type.is_terminal -%}
{%- if field.is_optional -%}
if let Some(ref {{ field.label | snake_case }}) = node.{{ field.label | snake_case }} {
accept_{{ field.type.name | snake_case }}({{ field.label }}, visitor);
}
{% else -%}
accept_{{ field.type.name | snake_case }}(&node.{{ field.label }}, visitor);
{%- endif -%}
{%- endif -%}
{%- endfor -%}
visitor.leave_{{ parent_type | snake_case }}(node);
}
{% endfor %}
//
// Choices:
//
{% for parent_type, choice in target.choices %}
{%- set nonterminals = choice.variants | filter(attribute="kind", value="Nonterminal") -%}
{%- set unique_terminals = choice.variants | filter(attribute="kind", value="UniqueTerminal") -%}
{%- set non_unique_terminals = choice.variants | filter(attribute="kind", value="Terminal") -%}
{% if nonterminals | length == 0 %}
pub fn accept_{{ parent_type | snake_case }}(_node: &{{ parent_type }}, _visitor: &mut impl Visitor) {}
{% else %}
pub fn accept_{{ parent_type | snake_case }}(node: &{{ parent_type }}, visitor: &mut impl Visitor) {
if !visitor.enter_{{ parent_type | snake_case }}(node) {
return;
}
match node {
{% for nonterminal in nonterminals -%}
{{ parent_type }}::{{ nonterminal.name }}(ref {{ nonterminal.name | snake_case }}) => {
accept_{{ nonterminal.name | snake_case }}({{ nonterminal.name | snake_case }}, visitor);
}
{%- endfor %}
{%- if non_unique_terminals | length > 0 %}
{%- for terminal in non_unique_terminals -%}
{%- if not loop.first -%} | {%- endif -%}
{{ parent_type }}::{{ terminal.name }}(_)
{%- endfor -%}
=> {}
{% endif -%}
{%- if unique_terminals | length > 0 %}
{%- for terminal in unique_terminals -%}
{%- if not loop.first -%} | {%- endif -%}
{{ parent_type }}::{{ terminal.name }}
{%- endfor -%}
=> {}
{% endif -%}
}
visitor.leave_{{ parent_type | snake_case }}(node);
}
{% endif %}
{% endfor %}
//
// Repeated & Separated
//
{% for parent_type, collection in target.collections -%}
{%- if collection.item_type.is_terminal %}
#[inline]
fn accept_{{ parent_type | snake_case }}(items: &Vec<Rc<TerminalNode>>, visitor: &mut impl Visitor) {
if visitor.enter_{{ parent_type | snake_case }}(items) {
visitor.leave_{{ parent_type | snake_case }}(items);
}
}
{% else %}
#[inline]
fn accept_{{ parent_type | snake_case }}(items: &Vec<{{ collection.item_type.name }}>, visitor: &mut impl Visitor) {
if !visitor.enter_{{ parent_type | snake_case }}(items) {
return;
}
for item in items {
accept_{{ collection.item_type.name | snake_case }}(item, visitor);
}
visitor.leave_{{ parent_type | snake_case }}(items);
}
{% endif -%}
{% endfor %}
{% endmacro render_visitor %}