nodespeak 0.2.1

A JIT-ish compiler for number-crunching applications.
Documentation
use crate::high_level::problem::{CompileProblem, FilePosition};

use pest::error::ErrorVariant;
use pest::Parser;

#[derive(Parser)]
#[grammar = "ast/grammar.pest"]
struct NodespeakParser;

pub mod structure {
    pub use super::Rule;
    use pest::iterators::{Pair, Pairs};

    pub type Program<'a> = Pairs<'a, Rule>;
    pub type Node<'a> = Pair<'a, Rule>;

    pub fn rule_name(rule: &Rule) -> &'static str {
        match rule {
            Rule::WHITESPACE => "whitespace",
            Rule::COMMENT | Rule::block_comment | Rule::line_comment => "comment",
            Rule::EOI => "end of file",

            Rule::dec_int => "integer literal",
            Rule::hex_int => "hexadecimal literal",
            Rule::oct_int => "octal literal",
            Rule::legacy_oct_int => "c-style octal literal",
            Rule::bin_int => "binary literal",
            Rule::dec_digit => "digit",
            Rule::float => "float literal",
            Rule::int => "int literal",
            Rule::literal => "literal value",
            Rule::identifier => "identifier",

            Rule::vp_var => "variable",
            Rule::build_array => "array data",
            Rule::vpe_part_1 | Rule::vpe_part_2 | Rule::vpe_part_3 | Rule::vpe_part | Rule::vpe => {
                "value-producing expression"
            }
            Rule::build_array_type => "array type",
            Rule::optional_index_indicator => "?",
            Rule::vp_index => "index expression",
            Rule::get_property => "property access",
            Rule::negate => "negate",
            Rule::not => "not",
            Rule::operator => "binary operator",

            Rule::var_dec => "variable declaration",
            Rule::vc_identifier => "variable",
            Rule::vc_index => "index expression",
            Rule::vce => "value-consuming expression",

            Rule::macro_call_input_list => "input list for macro call",
            Rule::inline_output => "inline keyword",
            Rule::macro_call_output => "output for macro call",
            Rule::macro_call_output_list => "output list for macro call",
            Rule::macro_call => "single-output macro callession",

            Rule::macro_inputs => "input list for macro definition",
            Rule::macro_outputs => "output list for macro definition",
            Rule::single_macro_output => "single output for macro definition",
            Rule::macro_signature => "signature for macro definition",
            Rule::macro_definition => "macro definition",

            Rule::else_if_clause => "else if clause",
            Rule::else_clause => "else clause",
            Rule::if_statement => "if statement",
            Rule::no_unroll_keyword => "no_unroll (keyword)",
            Rule::for_loop_statement => "for loop",

            Rule::raw_string => "string segment",
            Rule::escape_sequence => "string escape sequence",
            Rule::string => "string literal",

            Rule::input_variable_statement => "input declaration",
            Rule::output_variable_statement => "output declaration",
            Rule::static_variable_statement => "static variable declaration",
            Rule::assign_statement => "assignment statement",
            Rule::macro_call_statement => "macro call as statement",
            Rule::var_dec_statement => "variable declaration as statement",
            Rule::return_statement => "return statement",
            Rule::assert_statement => "assert statement",
            Rule::include_statement => "include statement",
            Rule::statement => "statement",

            Rule::code_block => "code block",

            Rule::root => "program",
        }
    }
}

pub(self) mod problems {
    use crate::high_level::problem::{
        CompileProblem, FilePosition, ProblemDescriptor, ProblemType,
    };

    pub fn bad_syntax(pos: FilePosition, message: String) -> CompileProblem {
        CompileProblem::from_descriptors(vec![ProblemDescriptor::new(
            pos,
            ProblemType::Error,
            &format!("Bad Syntax\n{}", message),
        )])
    }
}

pub fn ingest(text: &str, file_id: usize) -> Result<structure::Program, CompileProblem> {
    NodespeakParser::parse(Rule::root, text).map_err(|parse_err| {
        problems::bad_syntax(
            FilePosition::from_input_location(parse_err.location, file_id),
            match parse_err.variant {
                ErrorVariant::ParsingError {
                    positives,
                    negatives,
                } => format!(
                    "Expected {}... but found {}.",
                    {
                        positives
                            .iter()
                            .map(|rule| structure::rule_name(rule))
                            .collect::<Vec<&str>>()
                            .join(", ")
                    },
                    {
                        if negatives.len() == 0 {
                            "unknown syntax".to_owned()
                        } else {
                            negatives
                                .iter()
                                .map(|rule| structure::rule_name(rule))
                                .collect::<Vec<&str>>()
                                .join(", ")
                        }
                    }
                ),
                ErrorVariant::CustomError { message: _message } => {
                    unreachable!("Only parsing errors are encountered in the parser.")
                }
            },
        )
    })
}