slang_solidity 1.3.5

A modular set of compiler APIs empowering the next generation of Solidity code analysis and developer tooling. Written in Rust and distributed in multiple languages.
Documentation
{%- set target = model.ir_languages["ir2_flat_contracts"].target -%}
{%- set custom_types = ["Identifier", "YulIdentifier"] -%}

#[allow(clippy::wildcard_imports)]
use super::nodes::*;
use std::rc::Rc;
use crate::cst::TerminalNode;

use super::{Identifier, YulIdentifier};

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 %}
    {%- if collection.item_type.is_terminal and custom_types is not containing(collection.item_type.name) %}
      fn enter_{{ parent_type | snake_case }}(&mut self, _items: &[Rc<TerminalNode>]) -> bool { true }
      fn leave_{{ parent_type | snake_case }}(&mut self, _items: &[Rc<TerminalNode>]) {}
    {% else %}
      fn enter_{{ parent_type | snake_case }}(&mut self, _items: &{{ parent_type }}) -> bool { true }
      fn leave_{{ parent_type | snake_case }}(&mut self, _items: &{{ parent_type }}) {}
    {% endif -%}
  {% endfor -%}

  // Terminal types that are visited
  fn visit_identifier(&mut self, _node: &Identifier) {}
  fn visit_yul_identifier(&mut self, _node: &YulIdentifier) {}
}

pub fn accept_identifier(node: &Identifier, visitor: &mut impl Visitor) {
  visitor.visit_identifier(node);
}

pub fn accept_yul_identifier(node: &YulIdentifier, visitor: &mut impl Visitor) {
  visitor.visit_yul_identifier(node);
}

//
// 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 field.is_optional -%}
        {%- if custom_types is containing(field.type.name) or not field.type.is_terminal -%}
          if let Some(ref {{ field.label | snake_case }}) = node.{{ field.label | snake_case }}() {
            accept_{{ field.type.name | snake_case }}({{ field.label | snake_case }}, visitor);
          }
        {%- endif -%}
      {%- else -%}
        {%- if custom_types is containing(field.type.name) or not field.type.is_terminal -%}
          accept_{{ field.type.name | snake_case }}(&node.{{ field.label | snake_case }}(), 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) and (non_unique_terminals | length == 0) %}
    pub fn accept_{{ parent_type | snake_case }}(_node: &{{ parent_type }}, _visitor: &mut impl Visitor) {}
  {% else %}
    #[allow(clippy::too_many_lines)]
    pub fn accept_{{ parent_type | snake_case }}(node: &{{ parent_type }}, visitor: &mut impl Visitor) {
      if !visitor.enter_{{ parent_type | snake_case }}(node) {
        return;
      }
      #[allow(clippy::single_match)]
      #[allow(clippy::match_wildcard_for_single_variants)]
      match node {
        {% for nonterminal in nonterminals -%}
          {{ parent_type }}::{{ nonterminal.name }}(variant) => {
            accept_{{ nonterminal.name | snake_case }}(variant, visitor);
          }
        {%- endfor %}
        {% for terminal in non_unique_terminals -%}
          {% if custom_types is containing(terminal.name) %}
            {{ parent_type }}::{{ terminal.name }}(variant) => {
              accept_{{ terminal.name | snake_case }}(variant, visitor);
            }
          {% else %}
            {{ parent_type }}::{{ terminal.name }}(_) => {},
          {% endif %}
        {%- endfor %}
        {%- if unique_terminals | length > 0 %}
          _ => {}
        {% endif -%}
      }
      visitor.leave_{{ parent_type | snake_case }}(node);
    }
  {% endif %}
{% endfor %}

//
// Repeated & Separated
//

{% for parent_type, collection in target.collections -%}
  {%- if custom_types is containing(collection.item_type.name) or
         not collection.item_type.is_terminal %}
    {% set actual_type = parent_type %}
    {% set visit_items = True %}
  {% else %}
    {% set actual_type = "[Rc<TerminalNode>]" %}
  {%- endif %}

  pub fn accept_{{ parent_type | snake_case }}(items: &{{ actual_type }}, visitor: &mut impl Visitor) {
    if !visitor.enter_{{ parent_type | snake_case }}(items) {
      return;
    }
    {%- if visit_items %}
      for item in items.iter() {
        accept_{{ collection.item_type.name | snake_case }}(&item, visitor);
      }
    {% endif -%}
    visitor.leave_{{ parent_type | snake_case }}(items);
  }
{% endfor %}