mod lexer;
mod built_info {
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
use std::io::{stdin, Read};
use anyhow::{bail, Result};
use clap::Arg;
use is_terminal::IsTerminal;
use lexer::*;
use rustyline::{error::ReadlineError, DefaultEditor};
fn main() -> anyhow::Result<()> {
let matches = clap::Command::new(built_info::PKG_NAME)
.about(built_info::PKG_DESCRIPTION)
.author(built_info::PKG_AUTHORS)
.version(built_info::PKG_VERSION)
.help_expected(true)
.next_line_help(false)
.max_term_width(72)
.help_template(
r#"
{before-help}{name} {version}
{about}
Copyright (c) 2023, 2024 {author}
SPDX-License-Identifier: GPL-3.0-or-later
{usage-heading} {usage}
{all-args}{after-help}
"#,
)
.after_help(format!(
"\
Hey you, out there beyond the wall,
Breaking bottles in the hall,
Can you help me?
Send bug reports to {}
Attaching poems encourages consideration tremendously.
License: {}
Homepage: {}
Repository: {}
",
built_info::PKG_AUTHORS,
built_info::PKG_LICENSE,
built_info::PKG_HOMEPAGE,
built_info::PKG_REPOSITORY,
))
.arg(
Arg::new("command")
.short('c')
.help("Commands are read from string")
.num_args(1),
)
.arg(
Arg::new("login")
.short('l')
.help("Ignored (not implemented yet)")
.num_args(0),
)
.get_matches();
if let Some(command) = matches.try_get_one::<String>("command").expect("command") {
eval(command)?;
return Ok(());
}
if !stdin().is_terminal() {
let mut buffer = String::new();
stdin().read_to_string(&mut buffer)?;
eval(&buffer)?;
return Ok(());
}
let mut rl = match DefaultEditor::new() {
Ok(editor) => editor,
Err(error) => {
bail!("! {error:?}");
}
};
let mut prompt = "; ";
loop {
let mut command = String::new();
loop {
let readline = rl.readline(prompt);
match readline {
Ok(line) => {
if line.ends_with('\\') {
command.push_str(&line[..line.len() - 1]);
command.push(' '); prompt = ""; continue;
} else {
command.push_str(&line);
prompt = "; "; break;
}
}
Err(ReadlineError::Eof) | Err(ReadlineError::Interrupted) => {
return Ok(());
}
Err(error) => return Err(error.into()),
}
}
if !command.trim().is_empty() {
println!("< {command}");
if let Err(error) = rl.add_history_entry(command.as_str()) {
eprintln!("? {error:?}");
}
if let Err(error) = eval(&command) {
eprintln!("? {error:?}");
}
}
}
}
fn eval(input: &str) -> Result<()> {
match tokenize(&input) {
Ok(token) => {
println!("> {token:?}");
Ok(())
}
Err(error) => {
println!("! {error:?}");
Err(error)
}
}
}