use crate::ast_impl::*;
use crate::error_impl::ParseError;
use crate::lexer::Lexer;
use crate::parser_impl::Parser;
use super::functions::*;
#[allow(dead_code)]
#[derive(Default, Debug, Clone)]
#[allow(missing_docs)]
pub struct CommandTally {
pub evals: u64,
pub type_queries: u64,
pub checks: u64,
pub loads: u64,
#[allow(missing_docs)]
pub quits: u64,
pub helps: u64,
pub other: u64,
}
impl CommandTally {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn record(&mut self, cmd: &ReplCommand) {
match cmd {
ReplCommand::Eval(_) => self.evals += 1,
ReplCommand::Type(_) => self.type_queries += 1,
ReplCommand::Check(_) => self.checks += 1,
ReplCommand::Load(_) => self.loads += 1,
ReplCommand::Quit => self.quits += 1,
ReplCommand::Help => self.helps += 1,
_ => self.other += 1,
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn total(&self) -> u64 {
self.evals
+ self.type_queries
+ self.checks
+ self.loads
+ self.quits
+ self.helps
+ self.other
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct ReplHistoryEntry {
pub input: String,
pub parse_ok: bool,
pub seq: usize,
}
impl ReplHistoryEntry {
#[allow(dead_code)]
pub fn new(seq: usize, input: &str, parse_ok: bool) -> Self {
ReplHistoryEntry {
input: input.to_string(),
parse_ok,
seq,
}
}
}
#[derive(Clone, Debug, Default)]
#[allow(missing_docs)]
pub struct InputSplitter {
pub(super) buffer: Vec<String>,
pub(super) depth: i32,
}
impl InputSplitter {
#[allow(missing_docs)]
pub fn new() -> Self {
Self::default()
}
#[allow(missing_docs)]
pub fn push(&mut self, line: &str) {
self.buffer.push(line.to_string());
for ch in line.chars() {
match ch {
'(' | '[' | '{' => self.depth += 1,
')' | ']' | '}' => self.depth -= 1,
_ => {}
}
}
}
#[allow(missing_docs)]
pub fn is_complete(&self) -> bool {
self.depth <= 0 && !self.buffer.is_empty()
}
#[allow(missing_docs)]
pub fn flush(&mut self) -> String {
let result = self.buffer.join("\n");
self.buffer.clear();
self.depth = 0;
result
}
#[allow(missing_docs)]
pub fn line_count(&self) -> usize {
self.buffer.len()
}
#[allow(missing_docs)]
pub fn depth(&self) -> i32 {
self.depth
}
#[allow(missing_docs)]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
}
#[derive(Clone, Debug, Default)]
#[allow(missing_docs)]
pub struct OptionStore {
values: std::collections::HashMap<String, String>,
}
impl OptionStore {
#[allow(missing_docs)]
pub fn new() -> Self {
Self::default()
}
#[allow(missing_docs)]
pub fn set(&mut self, name: impl Into<String>, value: impl Into<String>) {
self.values.insert(name.into(), value.into());
}
#[allow(missing_docs)]
pub fn get(&self, name: &str) -> Option<&str> {
self.values.get(name).map(|s| s.as_str())
}
#[allow(missing_docs)]
pub fn has(&self, name: &str) -> bool {
self.values.contains_key(name)
}
#[allow(missing_docs)]
pub fn remove(&mut self, name: &str) -> bool {
self.values.remove(name).is_some()
}
#[allow(missing_docs)]
pub fn len(&self) -> usize {
self.values.len()
}
#[allow(missing_docs)]
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
#[allow(missing_docs)]
pub fn get_bool(&self, name: &str) -> bool {
self.get(name)
.map(|v| matches!(v, "true" | "1" | "yes" | "on"))
.unwrap_or(false)
}
#[allow(missing_docs)]
pub fn get_u64(&self, name: &str, default: u64) -> u64 {
self.get(name)
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
#[allow(missing_docs)]
pub fn keys(&self) -> Vec<&str> {
self.values.keys().map(|s| s.as_str()).collect()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
#[allow(missing_docs)]
pub struct CommandAlias {
pub from: String,
pub to: String,
}
impl CommandAlias {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new(from: &str, to: &str) -> Self {
Self {
from: from.to_string(),
to: to.to_string(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum ReplMode {
Normal,
Tactic,
Search,
}
impl ReplMode {
#[allow(missing_docs)]
pub fn name(&self) -> &'static str {
match self {
ReplMode::Normal => "normal",
ReplMode::Tactic => "tactic",
ReplMode::Search => "search",
}
}
#[allow(missing_docs)]
pub fn from_name(s: &str) -> Option<Self> {
match s {
"normal" => Some(ReplMode::Normal),
"tactic" => Some(ReplMode::Tactic),
"search" => Some(ReplMode::Search),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[allow(clippy::large_enum_variant)]
#[allow(missing_docs)]
pub enum ReplCommand {
Eval(Located<SurfaceExpr>),
Type(Located<SurfaceExpr>),
Check(Located<Decl>),
Load(String),
ShowEnv,
Clear,
Help,
Quit,
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct NoopListener;
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct FilterPipeline {
filters: Vec<Box<dyn InputFilter>>,
}
impl FilterPipeline {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new() -> Self {
Self {
filters: Vec::new(),
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn add<F: InputFilter + 'static>(&mut self, f: F) {
self.filters.push(Box::new(f));
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn apply(&self, input: &str) -> String {
let mut result = input.to_string();
for f in &self.filters {
result = f.filter(&result);
}
result
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn len(&self) -> usize {
self.filters.len()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn is_empty(&self) -> bool {
self.filters.is_empty()
}
}
#[derive(Debug, Clone, Default)]
#[allow(missing_docs)]
pub struct ReplStats {
pub commands_run: u64,
pub successes: u64,
#[allow(missing_docs)]
pub errors: u64,
pub chars_typed: u64,
}
impl ReplStats {
#[allow(missing_docs)]
pub fn new() -> Self {
Self::default()
}
#[allow(missing_docs)]
pub fn record_success(&mut self) {
self.commands_run += 1;
self.successes += 1;
}
#[allow(missing_docs)]
pub fn record_error(&mut self) {
self.commands_run += 1;
self.errors += 1;
}
#[allow(missing_docs)]
pub fn record_chars(&mut self, n: u64) {
self.chars_typed += n;
}
#[allow(missing_docs)]
pub fn success_rate(&self) -> f64 {
if self.commands_run == 0 {
1.0
} else {
self.successes as f64 / self.commands_run as f64
}
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ReplFormatter {
use_color: bool,
max_width: usize,
}
impl ReplFormatter {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new(use_color: bool, max_width: usize) -> Self {
Self {
use_color,
max_width,
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn success(&self, msg: &str) -> String {
if self.use_color {
format!("\x1b[1;32m✓\x1b[0m {}", msg)
} else {
format!("OK: {}", msg)
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn error(&self, msg: &str) -> String {
if self.use_color {
format!("\x1b[1;31m✗\x1b[0m {}", msg)
} else {
format!("Error: {}", msg)
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn info(&self, msg: &str) -> String {
if self.use_color {
format!("\x1b[1;34mℹ\x1b[0m {}", msg)
} else {
format!("Info: {}", msg)
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn truncate(&self, s: &str) -> String {
if s.len() <= self.max_width {
s.to_string()
} else {
format!("{}..", &s[..self.max_width.saturating_sub(2)])
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn list(&self, items: &[String]) -> String {
items
.iter()
.enumerate()
.map(|(i, s)| format!(" {}: {}", i + 1, self.truncate(s)))
.collect::<Vec<_>>()
.join("\n")
}
}
#[allow(missing_docs)]
pub struct ReplParser {
input: String,
}
impl ReplParser {
#[allow(missing_docs)]
pub fn new(input: String) -> Self {
Self { input }
}
#[allow(missing_docs)]
pub fn parse(&self) -> Result<ReplCommand, ParseError> {
let trimmed = self.input.trim();
if let Some(cmd) = trimmed.strip_prefix(':') {
return self.parse_meta_command(cmd);
}
self.parse_input()
}
fn parse_meta_command(&self, cmd: &str) -> Result<ReplCommand, ParseError> {
let parts: Vec<&str> = cmd.split_whitespace().collect();
if parts.is_empty() {
return Err(ParseError::unexpected(
vec!["command".to_string()],
crate::tokens::TokenKind::Eof,
crate::span_util::dummy_span(),
));
}
match parts[0] {
"quit" | "q" | "exit" => Ok(ReplCommand::Quit),
"help" | "h" | "?" => Ok(ReplCommand::Help),
"env" | "show" => Ok(ReplCommand::ShowEnv),
"clear" | "reset" => Ok(ReplCommand::Clear),
"type" | "t" => {
if parts.len() < 2 {
return Err(ParseError::unexpected(
vec!["expression".to_string()],
crate::tokens::TokenKind::Eof,
crate::span_util::dummy_span(),
));
}
let expr_str = parts[1..].join(" ");
let expr = self.parse_expr_from_str(&expr_str)?;
Ok(ReplCommand::Type(expr))
}
"load" | "l" => {
if parts.len() < 2 {
return Err(ParseError::unexpected(
vec!["filename".to_string()],
crate::tokens::TokenKind::Eof,
crate::span_util::dummy_span(),
));
}
Ok(ReplCommand::Load(parts[1].to_string()))
}
"check" | "c" => {
if parts.len() < 2 {
return Err(ParseError::unexpected(
vec!["declaration".to_string()],
crate::tokens::TokenKind::Eof,
crate::span_util::dummy_span(),
));
}
let decl_str = parts[1..].join(" ");
let decl = self.parse_decl_from_str(&decl_str)?;
Ok(ReplCommand::Check(decl))
}
_ => Err(ParseError::unexpected(
vec!["known command".to_string()],
crate::tokens::TokenKind::Eof,
crate::span_util::dummy_span(),
)),
}
}
fn parse_input(&self) -> Result<ReplCommand, ParseError> {
if let Ok(decl) = self.parse_decl_from_str(&self.input) {
return Ok(ReplCommand::Check(decl));
}
let expr = self.parse_expr_from_str(&self.input)?;
Ok(ReplCommand::Eval(expr))
}
fn parse_expr_from_str(&self, s: &str) -> Result<Located<SurfaceExpr>, ParseError> {
let mut lexer = Lexer::new(s);
let tokens = lexer.tokenize();
let mut parser = Parser::new(tokens);
parser.parse_expr()
}
fn parse_decl_from_str(&self, s: &str) -> Result<Located<Decl>, ParseError> {
let mut lexer = Lexer::new(s);
let tokens = lexer.tokenize();
let mut parser = Parser::new(tokens);
parser.parse_decl()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ReplHistory {
pub entries: Vec<ReplHistoryEntry>,
}
impl ReplHistory {
#[allow(dead_code)]
pub fn new() -> Self {
ReplHistory {
entries: Vec::new(),
}
}
#[allow(dead_code)]
pub fn push(&mut self, input: &str, parse_ok: bool) {
let seq = self.entries.len();
self.entries
.push(ReplHistoryEntry::new(seq, input, parse_ok));
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.entries.len()
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[derive(Debug, Clone, Default)]
#[allow(missing_docs)]
pub struct CommandHistory {
entries: Vec<String>,
max_entries: usize,
position: usize,
}
impl CommandHistory {
#[allow(missing_docs)]
pub fn new(max_entries: usize) -> Self {
Self {
entries: Vec::new(),
max_entries,
position: 0,
}
}
#[allow(missing_docs)]
pub fn push(&mut self, entry: String) {
if entry.trim().is_empty() {
return;
}
if self.entries.last().map(|s| s.as_str()) == Some(&entry) {
return;
}
if self.entries.len() >= self.max_entries {
self.entries.remove(0);
}
self.entries.push(entry);
self.position = self.entries.len();
}
#[allow(missing_docs)]
pub fn prev(&mut self) -> Option<&str> {
if self.entries.is_empty() || self.position == 0 {
return None;
}
self.position -= 1;
self.entries.get(self.position).map(|s| s.as_str())
}
#[allow(clippy::should_implement_trait)]
#[allow(missing_docs)]
pub fn next(&mut self) -> Option<&str> {
if self.position >= self.entries.len() {
return None;
}
self.position += 1;
self.entries.get(self.position).map(|s| s.as_str())
}
#[allow(missing_docs)]
pub fn entries(&self) -> &[String] {
&self.entries
}
#[allow(missing_docs)]
pub fn clear(&mut self) {
self.entries.clear();
self.position = 0;
}
#[allow(missing_docs)]
pub fn len(&self) -> usize {
self.entries.len()
}
#[allow(missing_docs)]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[allow(missing_docs)]
pub fn search(&self, query: &str) -> Vec<&str> {
self.entries
.iter()
.filter(|e| e.contains(query))
.map(|s| s.as_str())
.collect()
}
}
#[derive(Debug, Clone, PartialEq)]
#[allow(clippy::large_enum_variant)]
#[allow(missing_docs)]
pub enum ExtendedReplCommand {
SetOption(String, String),
GetOption(String),
History,
Undo,
Reduce(Located<SurfaceExpr>),
Stats,
Search(String),
Print(String),
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum CompletionKind {
Command,
Keyword,
Identifier,
Tactic,
}
#[derive(Debug, Clone)]
#[allow(missing_docs)]
pub struct ReplOptions {
pub show_timing: bool,
pub print_types: bool,
#[allow(missing_docs)]
pub max_history: usize,
pub color: bool,
pub verbose: bool,
}
impl ReplOptions {
#[allow(missing_docs)]
pub fn new() -> Self {
Self::default()
}
#[allow(missing_docs)]
pub fn set(&mut self, name: &str, value: &str) -> Result<(), String> {
match name {
"timing" => {
self.show_timing = parse_bool(value)?;
Ok(())
}
"types" => {
self.print_types = parse_bool(value)?;
Ok(())
}
"color" => {
self.color = parse_bool(value)?;
Ok(())
}
"verbose" => {
self.verbose = parse_bool(value)?;
Ok(())
}
"history" => {
self.max_history = value
.parse::<usize>()
.map_err(|_| format!("Expected number, got '{}'", value))?;
Ok(())
}
_ => Err(format!("Unknown option '{}'", name)),
}
}
#[allow(missing_docs)]
pub fn get(&self, name: &str) -> Option<String> {
match name {
"timing" => Some(self.show_timing.to_string()),
"types" => Some(self.print_types.to_string()),
"color" => Some(self.color.to_string()),
"verbose" => Some(self.verbose.to_string()),
"history" => Some(self.max_history.to_string()),
_ => None,
}
}
#[allow(missing_docs)]
pub fn known_options() -> &'static [&'static str] {
&["timing", "types", "color", "verbose", "history"]
}
}
#[derive(Debug, Clone, Default)]
#[allow(missing_docs)]
pub struct MultilineState {
pub(super) lines: Vec<String>,
pub(super) depth: i32,
}
impl MultilineState {
#[allow(missing_docs)]
pub fn new() -> Self {
Self::default()
}
#[allow(missing_docs)]
pub fn push_line(&mut self, line: &str) {
for ch in line.chars() {
match ch {
'(' | '[' | '{' => self.depth += 1,
')' | ']' | '}' => self.depth -= 1,
_ => {}
}
}
self.lines.push(line.to_string());
}
#[allow(missing_docs)]
pub fn is_complete(&self) -> bool {
self.depth <= 0 && !self.lines.is_empty()
}
#[allow(missing_docs)]
pub fn get_input(&self) -> String {
self.lines.join("\n")
}
#[allow(missing_docs)]
pub fn reset(&mut self) {
self.lines.clear();
self.depth = 0;
}
#[allow(missing_docs)]
pub fn depth(&self) -> i32 {
self.depth
}
#[allow(missing_docs)]
pub fn line_count(&self) -> usize {
self.lines.len()
}
}
#[derive(Debug)]
#[allow(missing_docs)]
pub struct ReplSession {
pub history: CommandHistory,
pub options: ReplOptions,
#[allow(missing_docs)]
pub stats: ReplStats,
_phantom: std::marker::PhantomData<()>,
}
impl ReplSession {
#[allow(missing_docs)]
pub fn new() -> Self {
let options = ReplOptions::default();
let max_history = options.max_history;
Self {
history: CommandHistory::new(max_history),
options,
stats: ReplStats::new(),
_phantom: std::marker::PhantomData,
}
}
#[allow(missing_docs)]
pub fn process(&mut self, input: &str) -> Result<ReplCommand, String> {
let input = input.trim().to_string();
if input.is_empty() {
return Err("Empty input".to_string());
}
self.history.push(input.clone());
self.stats.record_chars(input.len() as u64);
let parser = ReplParser::new(input);
parser.parse().map_err(|e| e.to_string())
}
#[allow(missing_docs)]
pub fn help(&self) -> &'static str {
help_text()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub struct CompletionItem {
pub text: String,
pub description: String,
pub kind: CompletionKind,
}
impl CompletionItem {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new(text: &str, description: &str, kind: CompletionKind) -> Self {
Self {
text: text.to_string(),
description: description.to_string(),
kind,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub enum ReplOption {
Timing,
ShowElaborated,
Verbose,
PrettyPrint,
MaxLines,
}
impl ReplOption {
#[allow(missing_docs)]
pub fn from_name(s: &str) -> Option<Self> {
match s {
"timing" => Some(ReplOption::Timing),
"show_elaborated" | "showElaborated" => Some(ReplOption::ShowElaborated),
"verbose" => Some(ReplOption::Verbose),
"pretty_print" | "prettyPrint" => Some(ReplOption::PrettyPrint),
"max_lines" | "maxLines" => Some(ReplOption::MaxLines),
_ => None,
}
}
#[allow(missing_docs)]
pub fn name(&self) -> &'static str {
match self {
ReplOption::Timing => "timing",
ReplOption::ShowElaborated => "show_elaborated",
ReplOption::Verbose => "verbose",
ReplOption::PrettyPrint => "pretty_print",
ReplOption::MaxLines => "max_lines",
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
#[allow(missing_docs)]
pub struct ReplParserConfig {
pub allow_multiline: bool,
pub normalize_whitespace: bool,
#[allow(missing_docs)]
pub strip_comments: bool,
}
impl ReplParserConfig {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn with_multiline(mut self) -> Self {
self.allow_multiline = true;
self
}
}
#[allow(dead_code)]
#[derive(Default, Debug)]
#[allow(missing_docs)]
pub struct EventLog {
pub(super) events: Vec<ReplEvent>,
}
impl EventLog {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn events(&self) -> &[ReplEvent] {
&self.events
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn clear(&mut self) {
self.events.clear();
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn len(&self) -> usize {
self.events.len()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct LowercaseCommandFilter;
#[allow(dead_code)]
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReplInputKind {
Term,
Definition,
Tactic,
Command,
Incomplete,
}
#[derive(Debug, Clone, PartialEq)]
#[allow(missing_docs)]
pub enum ReplEvent {
Parsed(String),
Error(String),
Reset,
OptionChanged(String, String),
Exit,
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ReplCompleter {
items: Vec<CompletionItem>,
}
impl ReplCompleter {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new() -> Self {
let mut items = Vec::new();
for cmd in [
":quit", ":help", ":env", ":clear", ":type", ":load", ":check", ":set", ":history",
] {
items.push(CompletionItem::new(
cmd,
"REPL command",
CompletionKind::Command,
));
}
for kw in [
"theorem",
"def",
"axiom",
"inductive",
"fun",
"let",
"match",
"forall",
] {
items.push(CompletionItem::new(kw, "keyword", CompletionKind::Keyword));
}
Self { items }
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn complete(&self, prefix: &str) -> Vec<&CompletionItem> {
self.items
.iter()
.filter(|item| item.text.starts_with(prefix))
.collect()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn add(&mut self, item: CompletionItem) {
self.items.push(item);
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn len(&self) -> usize {
self.items.len()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
}
#[allow(dead_code)]
#[derive(Default, Debug)]
#[allow(missing_docs)]
pub struct AliasRegistry {
aliases: Vec<CommandAlias>,
}
impl AliasRegistry {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn register(&mut self, from: &str, to: &str) {
self.aliases.push(CommandAlias::new(from, to));
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn expand<'a>(&'a self, input: &'a str) -> &'a str {
for alias in &self.aliases {
if input.trim_start() == alias.from {
return &alias.to;
}
}
input
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn len(&self) -> usize {
self.aliases.len()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn is_empty(&self) -> bool {
self.aliases.is_empty()
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn names(&self) -> Vec<&str> {
self.aliases.iter().map(|a| a.from.as_str()).collect()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct StripSemicolonFilter;
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ConfigurableReplParser {
config: ReplParserConfig,
}
impl ConfigurableReplParser {
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn new(config: ReplParserConfig) -> Self {
Self { config }
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn preprocess(&self, input: &str) -> String {
let mut s = input.to_string();
if self.config.normalize_whitespace {
s = s.split_whitespace().collect::<Vec<_>>().join(" ");
}
if self.config.strip_comments {
s = s
.lines()
.map(|line| {
if let Some(idx) = line.find("--") {
&line[..idx]
} else {
line
}
})
.collect::<Vec<_>>()
.join("\n");
}
s
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub fn parse(&self, input: &str) -> Result<ReplCommand, String> {
let processed = self.preprocess(input);
let p = ReplParser::new(processed);
p.parse().map_err(|e| e.to_string())
}
}