Support loading files in config dir

This commit is contained in:
PaddiM8 2022-01-01 02:35:33 +01:00
parent 474b4d9016
commit dbf3866137
4 changed files with 57 additions and 9 deletions

View File

@ -9,10 +9,10 @@ use std::io::Read;
fn main() {
let args: Vec<String> = env::args().collect();
let app = App::new("kalk")
let app = App::new("kalker")
.author(env!("CARGO_PKG_AUTHORS"))
.version(env!("CARGO_PKG_VERSION"))
.usage("kalk [options] [input]")
.usage("kalker [options] [input]")
.action(default_action)
.flag(
Flag::new("input-file", FlagType::String)
@ -55,6 +55,10 @@ fn default_action(context: &Context) {
.int_flag("precision")
.unwrap_or(output::DEFAULT_PRECISION as isize) as u32;
if let Some(input_file_path) = get_input_file_by_name("default") {
load_input_file(&input_file_path, precision, &mut parser_context);
}
if let Ok(input_file_path) = context.string_flag("input-file") {
load_input_file(&input_file_path, precision, &mut parser_context);
}
@ -68,7 +72,20 @@ fn default_action(context: &Context) {
}
}
fn load_input_file(file_name: &str, precision: u32, parser_context: &mut parser::Context) {
pub(crate) fn get_input_file_by_name(name: &str) -> Option<String> {
let mut path = dirs::config_dir()?;
path.push("kalker");
path.push(name);
path.set_extension("kalker");
if path.exists() {
Some(path.to_str()?.to_string())
} else {
None
}
}
pub fn load_input_file(file_name: &str, precision: u32, parser_context: &mut parser::Context) {
let mut file_content = String::new();
File::open(&file_name)
.expect("Couldn't find file.")
@ -77,7 +94,9 @@ fn load_input_file(file_name: &str, precision: u32, parser_context: &mut parser:
// Parse the input file content, resulting in the symbol table being filled out.
// Output is not needed here.
parser::eval(parser_context, &file_content, precision).expect("Failed to parse input file.");
if let Err(error) = parser::eval(parser_context, &file_content, precision) {
eprintln!("{}", error.to_string());
}
}
fn get_env_angle_unit() -> String {

View File

@ -74,6 +74,16 @@ pub fn start(mut parser: &mut parser::Context, precision: u32) {
}
fn eval_repl(parser: &mut parser::Context, input: &str, precision: u32) {
if input.starts_with("load ") {
let file_name = &input[5..];
if let Some(file_path) = crate::get_input_file_by_name(file_name) {
crate::load_input_file(&file_path, precision, parser);
} else {
println!("Unable to find '{}'", file_name);
}
return;
}
match input {
"" => eprint!(""),
"clear" => print!("\x1B[2J"),
@ -96,7 +106,7 @@ impl Highlighter for LineHighlighter {
let reg = Regex::new(
r"(?x)
(?P<op>([+\-/*%^!×÷]|if|otherwise)) |
(?P<op>([+\-/*%^!×÷]|if|otherwise|load|exit|clear|help)) |
(?P<radix>0[box][a-zA-Z0-9]+) |
(?P<identifier>[^!-@\s_|^\[\]\{\}¹²³]+(_\d+)?)",
)

View File

@ -42,6 +42,7 @@ pub enum TokenKind {
ClosedBrace,
Comma,
Semicolon,
Newline,
EOF,
}
@ -97,7 +98,7 @@ impl<'a> Lexer<'a> {
return eof;
};
while c == ' ' || c == '\t' || c == '\r' || c == '\n' {
while c == ' ' || c == '\t' || c == '\r' {
if let None = self.advance() {
return eof;
}
@ -141,6 +142,7 @@ impl<'a> Lexer<'a> {
'<' => build(TokenKind::LessThan, "", span),
',' => build(TokenKind::Comma, "", span),
';' => build(TokenKind::Semicolon, "", span),
'\n' => build(TokenKind::Newline, "", span),
'%' => build(TokenKind::Percent, "", span),
'\'' => build(TokenKind::Tick, "", span),
'≠' => build(TokenKind::NotEquals, "", span),
@ -350,7 +352,7 @@ fn is_valid_identifier(c: Option<&char>) -> bool {
match c {
'+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|'
| '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | '{' | '}' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<'
| '>' | '≠' | '≥' | '≤' | '×' | '÷' => false,
| '>' | '≠' | '≥' | '≤' | '×' | '÷' | '\n' => false,
_ => !c.is_digit(10) || is_superscript(c) || is_subscript(c),
}
} else {

View File

@ -205,6 +205,8 @@ pub fn parse(context: &mut Context, input: &str) -> Result<Vec<Stmt>, CalcError>
if match_token(context, TokenKind::Semicolon) {
advance(context);
}
skip_newlines(context);
}
Ok(statements)
@ -283,6 +285,7 @@ fn parse_identifier_stmt(context: &mut Context) -> Result<Stmt, CalcError> {
fn parse_piecewise(context: &mut Context) -> Result<Expr, CalcError> {
advance(context);
skip_newlines(context);
let mut pieces = Vec::new();
let mut reached_otherwise = false;
@ -314,10 +317,18 @@ fn parse_piecewise(context: &mut Context) -> Result<Expr, CalcError> {
return Err(CalcError::ExpectedIf);
}
advance(context);
previous(context).kind == TokenKind::Semicolon && !reached_otherwise
if match_token(context, TokenKind::Semicolon) {
advance(context);
}
skip_newlines(context);
(previous(context).kind == TokenKind::Semicolon
|| previous(context).kind == TokenKind::Newline)
&& !reached_otherwise
} {}
advance(context);
Ok(Expr::Piecewise(pieces))
}
@ -864,6 +875,12 @@ fn is_at_end(context: &Context) -> bool {
context.pos >= context.tokens.len() || peek(context).kind == TokenKind::EOF
}
fn skip_newlines(context: &mut Context) {
while match_token(context, TokenKind::Newline) {
advance(context);
}
}
fn string_to_num(value: &str) -> Result<f64, CalcError> {
let base = get_base(value)?;
if let Some(result) = crate::radix::parse_float_radix(&value.replace(" ", ""), base) {