use crate::LRParseTable;
use crate::analysis::LookaheadDFA;
use crate::config::{CommonGeneratorConfig, ParserGeneratorConfig};
use crate::generators::parser_model::{
LookaheadAutomatonModel as LookaheadAutomatonIR, ProductionModel as ProductionIR,
build_lookahead_automata_model, build_production_model,
find_start_symbol_index as parser_model_find_start_symbol_index,
};
use crate::generators::parser_render_ir::{
build_csharp_lalr_parse_table_section_render_ir, build_csharp_llk_production_render_ir,
build_csharp_non_terminal_names_render_ir, build_lalr_production_render_ir,
build_non_terminal_metadata_ir, build_terminal_label_map,
};
use crate::generators::{GrammarConfig, NamingHelper};
use anyhow::Result;
use std::collections::BTreeMap;
use std::fmt::Write;
pub fn generate_parser_source<C: CommonGeneratorConfig + ParserGeneratorConfig>(
grammar_config: &GrammarConfig,
_lexer_source: &str, config: &C,
la_dfa: &BTreeMap<String, LookaheadDFA>,
_ast_type_has_lifetime: bool,
) -> Result<String> {
generate_parser_source_internal(grammar_config, config, la_dfa)
}
fn generate_parser_source_internal<C: CommonGeneratorConfig + ParserGeneratorConfig>(
grammar_config: &GrammarConfig,
config: &C,
la_dfa: &BTreeMap<String, LookaheadDFA>,
) -> Result<String> {
let mut source = String::new();
let non_terminal_metadata = build_non_terminal_metadata_ir(grammar_config);
let non_terminal_names = non_terminal_metadata.names;
let start_symbol_index =
parser_model_find_start_symbol_index(&non_terminal_names, grammar_config)?;
let non_terminal_names_render_ir =
build_csharp_non_terminal_names_render_ir(&non_terminal_names);
let parser_type_name = NamingHelper::to_upper_camel_case(config.user_type_name()) + "Parser";
writeln!(
source,
"// ---------------------------------------------------------"
)?;
writeln!(source, "// This file was generated by parol.")?;
writeln!(source, "// Do not edit this file manually.")?;
writeln!(source, "// Changes will be overwritten on the next build.")?;
writeln!(
source,
"// ---------------------------------------------------------"
)?;
writeln!(source)?;
writeln!(source, "using System;")?;
writeln!(source, "using System.Collections.Generic;")?;
writeln!(source, "using Parol.Runtime;")?;
writeln!(source, "using Parol.Runtime.Scanner;")?;
writeln!(source)?;
writeln!(source, "namespace {} {{", config.module_name())?;
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// Parser facade generated by parol for this grammar."
)?;
writeln!(
source,
" /// Provides parse tables and convenience entry points."
)?;
writeln!(source, " /// </summary>")?;
writeln!(source, " public class {} {{", parser_type_name)?;
let scanner_data =
crate::generators::cs_lexer_generator::generate_scanner_data(grammar_config, config)?;
writeln!(source, "{}", scanner_data)?;
writeln!(source)?;
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// Maximum lookahead k used by the generated grammar."
)?;
writeln!(source, " /// </summary>")?;
writeln!(
source,
" public const int MaxK = {};",
grammar_config.lookahead_size
)?;
writeln!(source)?;
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// Ordered non-terminal names used by the parser tables."
)?;
writeln!(source, " /// </summary>")?;
writeln!(
source,
" public static readonly string[] NonTerminalNames = ["
)?;
for row in &non_terminal_names_render_ir.rows {
writeln!(source, " {}", row)?;
}
writeln!(source, " ];")?;
writeln!(source)?;
let lookahead_automata_ir = build_lookahead_automata_model(la_dfa, &non_terminal_names);
generate_lookahead_automata(&mut source, &lookahead_automata_ir)?;
writeln!(source)?;
let production_ir = build_production_model(grammar_config, &non_terminal_names)?;
generate_productions(&mut source, grammar_config, &production_ir)?;
writeln!(source)?;
generate_parse_methods(&mut source, config, &parser_type_name, start_symbol_index)?;
writeln!(source, " }}")?;
writeln!(source, "}}")?;
Ok(source)
}
fn generate_lookahead_automata(
source: &mut String,
lookahead_automata_ir: &[LookaheadAutomatonIR],
) -> Result<()> {
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// Lookahead DFAs indexed by non-terminal index."
)?;
writeln!(source, " /// </summary>")?;
writeln!(
source,
" public static readonly LookaheadDfa[] LookaheadAutomata = ["
)?;
for automaton_ir in lookahead_automata_ir {
writeln!(
source,
" /* {} - \"{}\" */",
automaton_ir.non_terminal_index, automaton_ir.non_terminal_name
)?;
writeln!(source, " new(")?;
writeln!(source, " {},", automaton_ir.prod0)?;
writeln!(source, " [")?;
for transition in &automaton_ir.transitions {
writeln!(
source,
" new({}, {}, {}, {}),",
transition.from_state, transition.term, transition.to_state, transition.prod_num
)?;
}
writeln!(source, " ],")?;
writeln!(source, " {} // k", automaton_ir.k)?;
writeln!(source, " ),")?;
}
writeln!(source, " ];")?;
Ok(())
}
fn generate_productions(
source: &mut String,
_grammar_config: &GrammarConfig,
production_ir: &[ProductionIR],
) -> Result<()> {
let production_render_ir = build_csharp_llk_production_render_ir(production_ir);
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// Production table consumed by the LL(k) parser runtime."
)?;
writeln!(source, " /// </summary>")?;
writeln!(
source,
" public static readonly Production[] Productions = ["
)?;
for p in &production_render_ir {
writeln!(source, " // {} - {}", p.production_index, p.text)?;
writeln!(source, " new Production(")?;
writeln!(source, " {},", p.lhs_index)?;
writeln!(source, " [")?;
for symbol_source in &p.symbols {
writeln!(source, " {},", symbol_source)?;
}
writeln!(source, " ]")?;
writeln!(source, " ),")?;
}
writeln!(source, " ];")?;
Ok(())
}
fn generate_parse_methods(
source: &mut String,
config: &impl CommonGeneratorConfig,
_parser_type_name: &str,
start_symbol_index: usize,
) -> Result<()> {
let scanner_type_name = NamingHelper::to_upper_camel_case(config.user_type_name()) + "Scanner";
let actions_interface_name = format!(
"I{}Actions",
NamingHelper::to_upper_camel_case(config.user_type_name())
);
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// Parses input with strongly typed user actions."
)?;
writeln!(source, " /// </summary>")?;
writeln!(
source,
" /// <param name=\"input\">Input text to parse.</param>"
)?;
writeln!(
source,
" /// <param name=\"fileName\">Logical file name used for diagnostics.</param>"
)?;
writeln!(
source,
" /// <param name=\"userActions\">Typed semantic action receiver.</param>"
)?;
writeln!(
source,
" public static void Parse(string input, string fileName, {} userActions) {{",
actions_interface_name
)?;
writeln!(
source,
" ParseInternal(input, fileName, userActions);"
)?;
writeln!(source, " }}")?;
writeln!(source)?;
writeln!(
source,
" private static void ParseInternal(string input, string fileName, IUserActions userActions) {{"
)?;
writeln!(source, " var parser = new LLKParser(")?;
writeln!(source, " {},", start_symbol_index)?;
writeln!(source, " LookaheadAutomata,")?;
writeln!(source, " Productions,")?;
writeln!(
source,
" {}Data.TerminalNames,",
scanner_type_name
)?;
writeln!(source, " NonTerminalNames")?;
writeln!(source, " );")?;
writeln!(source)?;
writeln!(
source,
" var tokens = Scanner.Scan(input, fileName, {}Data.MatchFunction, {}Data.ScannerModes, {}Data.SkipTokensByScannerMode);",
scanner_type_name, scanner_type_name, scanner_type_name
)?;
writeln!(
source,
" parser.Parse(tokens, userActions, fileName);"
)?;
writeln!(source, " }}")?;
Ok(())
}
pub fn generate_lalr1_parser_source<C: CommonGeneratorConfig + ParserGeneratorConfig>(
grammar_config: &GrammarConfig,
_lexer_source: &str,
config: &C,
parse_table: &LRParseTable,
_ast_type_has_lifetime: bool,
) -> Result<String> {
generate_lalr1_parser_source_internal(grammar_config, config, parse_table)
}
fn generate_lalr1_parser_source_internal<C: CommonGeneratorConfig + ParserGeneratorConfig>(
grammar_config: &GrammarConfig,
config: &C,
parse_table: &LRParseTable,
) -> Result<String> {
let mut source = String::new();
let non_terminal_metadata = build_non_terminal_metadata_ir(grammar_config);
let non_terminal_names = non_terminal_metadata.names;
let start_symbol_index =
parser_model_find_start_symbol_index(&non_terminal_names, grammar_config)?;
let non_terminal_names_render_ir =
build_csharp_non_terminal_names_render_ir(&non_terminal_names);
let production_ir = build_production_model(grammar_config, &non_terminal_names)?;
let production_render_ir = build_lalr_production_render_ir(&production_ir);
let parser_type_name = NamingHelper::to_upper_camel_case(config.user_type_name()) + "Parser";
writeln!(
source,
"// ---------------------------------------------------------"
)?;
writeln!(source, "// This file was generated by parol.")?;
writeln!(source, "// Do not edit this file manually.")?;
writeln!(source, "// Changes will be overwritten on the next build.")?;
writeln!(
source,
"// ---------------------------------------------------------"
)?;
writeln!(source)?;
writeln!(source, "using System;")?;
writeln!(source, "using System.Collections.Generic;")?;
writeln!(source, "using Parol.Runtime;")?;
writeln!(source, "using Parol.Runtime.Scanner;")?;
writeln!(source)?;
writeln!(source, "namespace {} {{", config.module_name())?;
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// LALR(1) parser facade generated by parol for this grammar."
)?;
writeln!(
source,
" /// Provides parse table data and convenience entry points."
)?;
writeln!(source, " /// </summary>")?;
writeln!(source, " public class {} {{", parser_type_name)?;
let scanner_data =
crate::generators::cs_lexer_generator::generate_scanner_data(grammar_config, config)?;
writeln!(source, "{}", scanner_data)?;
writeln!(source)?;
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// Ordered non-terminal names used by parser diagnostics."
)?;
writeln!(source, " /// </summary>")?;
writeln!(
source,
" public static readonly string[] NonTerminalNames = ["
)?;
for row in &non_terminal_names_render_ir.rows {
writeln!(source, " {}", row)?;
}
writeln!(source, " ];")?;
writeln!(source)?;
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// LALR(1) production metadata consumed by the parser runtime."
)?;
writeln!(source, " /// </summary>")?;
writeln!(
source,
" public static readonly LRProduction[] Productions = ["
)?;
for p in &production_render_ir {
writeln!(source, " // {} - {}", p.production_index, p.text)?;
writeln!(source, " new LRProduction({}, [", p.lhs_index)?;
for is_semantic_child in &p.semantic_children {
writeln!(
source,
" {},",
if *is_semantic_child { "true" } else { "false" }
)?;
}
writeln!(source, " ]),")?;
}
writeln!(source, " ];")?;
writeln!(source)?;
emit_lalr_parse_table(
&mut source,
grammar_config,
parse_table,
&non_terminal_names,
)?;
writeln!(source)?;
emit_lalr_parse_methods(&mut source, config, &parser_type_name, start_symbol_index)?;
writeln!(source, " }}")?;
writeln!(source, "}}")?;
Ok(source)
}
fn emit_lalr_parse_table(
source: &mut String,
grammar_config: &GrammarConfig,
parse_table: &LRParseTable,
non_terminal_names: &[String],
) -> Result<()> {
let terminals = grammar_config
.cfg
.get_ordered_terminals()
.iter()
.map(|(t, _, l, _)| (*t, l.clone()))
.collect::<Vec<_>>();
let terminal_labels = build_terminal_label_map(&terminals);
let parse_table_render_ir = build_csharp_lalr_parse_table_section_render_ir(
parse_table,
&terminal_labels,
non_terminal_names,
);
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// Canonical LALR(1) parse table used by the parser runtime."
)?;
writeln!(source, " /// </summary>")?;
writeln!(
source,
" public static readonly LRParseTable ParseTable = new("
)?;
writeln!(source, " [")?;
for action_row in &parse_table_render_ir.action_rows {
writeln!(source, " {}", action_row)?;
}
writeln!(source, " ],")?;
writeln!(source, " [")?;
for state_row in &parse_table_render_ir.state_rows {
writeln!(source, "{}", state_row)?;
}
writeln!(source, " ]")?;
writeln!(source, " );")?;
Ok(())
}
fn emit_lalr_parse_methods(
source: &mut String,
config: &impl CommonGeneratorConfig,
_parser_type_name: &str,
start_symbol_index: usize,
) -> Result<()> {
let scanner_type_name = NamingHelper::to_upper_camel_case(config.user_type_name()) + "Scanner";
let actions_interface_name = format!(
"I{}Actions",
NamingHelper::to_upper_camel_case(config.user_type_name())
);
writeln!(source, " /// <summary>")?;
writeln!(
source,
" /// Parses input with strongly typed user actions."
)?;
writeln!(source, " /// </summary>")?;
writeln!(
source,
" /// <param name=\"input\">Input text to parse.</param>"
)?;
writeln!(
source,
" /// <param name=\"fileName\">Logical file name used for diagnostics.</param>"
)?;
writeln!(
source,
" /// <param name=\"userActions\">Typed semantic action receiver.</param>"
)?;
writeln!(
source,
" public static void Parse(string input, string fileName, {} userActions) {{",
actions_interface_name
)?;
writeln!(
source,
" ParseInternal(input, fileName, userActions);"
)?;
writeln!(source, " }}")?;
writeln!(source)?;
writeln!(
source,
" private static void ParseInternal(string input, string fileName, IUserActions userActions) {{"
)?;
writeln!(source, " var parser = new LRParser(")?;
writeln!(source, " {},", start_symbol_index)?;
writeln!(source, " ParseTable,")?;
writeln!(source, " Productions,")?;
writeln!(
source,
" {}Data.TerminalNames,",
scanner_type_name
)?;
writeln!(source, " NonTerminalNames")?;
writeln!(source, " );")?;
writeln!(source)?;
writeln!(
source,
" var tokens = Scanner.Scan(input, fileName, {}Data.MatchFunction, {}Data.ScannerModes, {}Data.SkipTokensByScannerMode);",
scanner_type_name, scanner_type_name, scanner_type_name
)?;
writeln!(
source,
" parser.Parse(tokens, userActions, fileName);"
)?;
writeln!(source, " }}")?;
Ok(())
}