Skip to content

implement bc #119

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 15 commits into from
May 31, 2024
297 changes: 297 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions calc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@ edition = "2021"
plib = { path = "../plib" }
gettext-rs.workspace = true
regex.workspace = true
clap.workspace = true
pest = "2.7.10"
pest_derive = "2.7.10"
lazy_static = "1.4.0"
bigdecimal = "0.4.3"
rustyline = "14.0.0"

[[bin]]
name = "expr"
path = "src/expr.rs"

[[bin]]
name = "bc"
path = "src/bc.rs"
118 changes: 118 additions & 0 deletions calc/src/bc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// Copyright (c) 2024 Hemi Labs, Inc.
//
// This file is part of the posixutils-rs project covered under
// the MIT License. For the full license text, please see the LICENSE
// file in the root directory of this project.
// SPDX-License-Identifier: MIT
//

use std::ffi::OsString;

use bc_util::{
interpreter::Interpreter,
parser::{is_incomplete, parse_program},
};
use clap::Parser;

use gettextrs::{bind_textdomain_codeset, textdomain};
use plib::PROJECT_NAME;
use rustyline::{error::ReadlineError, DefaultEditor, Result};

mod bc_util;

/// bc - arbitrary-precision arithmetic language
#[derive(Debug, Parser)]
#[command(author, version, about, long_about)]
struct Args {
#[arg(short = 'l')]
define_math_functions: bool,

files: Vec<OsString>,
}

fn exec_str(s: &str, interpreter: &mut Interpreter) -> bool {
match parse_program(s) {
Ok(program) => match interpreter.exec(program) {
Ok(output) => {
print!("{}", output.string);
if output.has_quit {
return true;
}
}
Err(e) => {
println!("runtime error: {}", e);
}
},
Err(e) => {
println!("{}", e);
}
}
false
}

fn main() -> Result<()> {
textdomain(PROJECT_NAME)?;
bind_textdomain_codeset(PROJECT_NAME, "UTF-8")?;

let args = Args::parse();
let mut interpreter = Interpreter::default();

if args.define_math_functions {
let lib = parse_program(include_str!("bc_util/math_functions.bc"))
.expect("error parsing standard math functions");
interpreter
.exec(lib)
.expect("error loading standard math functions");
}

for file in args.files {
match std::fs::read_to_string(&file) {
Ok(s) => {
if exec_str(&s, &mut interpreter) {
return Ok(());
}
}
Err(_) => {
eprintln!("Could not read file: {}", file.to_string_lossy());
return Ok(());
}
};
}

let mut repl = DefaultEditor::new()?;
let mut line_buffer = String::new();
loop {
let line = if line_buffer.is_empty() {
repl.readline(">> ")
} else {
repl.readline(".. ")
};
match line {
Ok(line) => {
line_buffer.push_str(&line);
line_buffer.push('\n');
if !is_incomplete(&line_buffer) {
if exec_str(&line_buffer, &mut interpreter) {
return Ok(());
}
line_buffer.clear();
}
repl.add_history_entry(line)?;
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
break;
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
break;
}
Err(e) => {
eprintln!("Error: {:?}", e);
break;
}
}
}
Ok(())
}
128 changes: 128 additions & 0 deletions calc/src/bc_util/grammar.pest
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//
// Copyright (c) 2024 Hemi Labs, Inc.
//
// This file is part of the posixutils-rs project covered under
// the MIT License. For the full license text, please see the LICENSE
// file in the root directory of this project.
// SPDX-License-Identifier: MIT
//

WHITESPACE = _{ " " | "\t" | "\\\n" }
COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") }

letter = { 'a'..'z' }
digit = _{ '0'..'9' | 'A' .. 'F' }
string = { "\"" ~ (!("\"") ~ ANY)* ~ "\"" }
integer = { ( digit | "\\\n" )+ }
number = @{
| (integer ~ "." ~ integer)
| ("." ~ integer)
| (integer ~ ".")
| integer
}
rel_op = { "==" | "<=" | ">=" | "!=" | "<" | ">" }
assign_op = _{ assign | add_assign | sub_assign | mul_assign | div_assign | mod_assign | pow_assign }
assign = { "=" }
add_assign = { "+=" }
sub_assign = { "-=" }
mul_assign = { "*=" }
div_assign = { "/=" }
mod_assign = { "%=" }
pow_assign = { "^=" }


neg = { "-" }

binary_op = _{
| add
| sub
| mul
| div
| modulus
| pow
}
add = { "+" }
sub = { "-" }
mul = { "*" }
div = { "/" }
modulus = { "%" }
pow = { "^" }

builtin_fn = { "length" | "sqrt" | "scale" }

program = { SOI ~ input_item* ~ EOI }
input_item = _{ (semicolon_list ~ "\n") | function }
semicolon_list = { (statement ~ (";" ~ statement)*)? ~ ";"? }
statement_list = { (";" | "\n")* ~ (statement ~ (";" | "\n")*)* }

statement = {
| break_stmt
| quit
| return_stmt
| if_stmt
| while_stmt
| for_stmt
| braced_statement_list
| string
| expression
}
break_stmt = { "break" }
quit = { "quit" }
return_stmt = { "return" ~ ( "(" ~ expression? ~ ")" )? }
for_stmt = { "for" ~ "(" ~ expression ~ ";" ~ condition ~ ";" ~ expression ~ ")" ~ statement }
if_stmt = { "if" ~ "(" ~ condition ~ ")" ~ statement }
while_stmt = { "while" ~ "(" ~ condition ~ ")" ~ statement }
braced_statement_list = { "{" ~ statement_list ~ "}" }
condition = { relational_expression | expression }
relational_expression = { expression ~ rel_op ~ expression }

function = { "define" ~ letter ~ "(" ~ parameter_list? ~ ")" ~ "{" ~ "\n" ~ auto_define_list? ~ statement_list ~ "}" }
parameter_list = { variable ~ ("," ~ variable)* }
variable = _{ array | variable_number }
variable_number = { letter }
array = { letter ~ "[" ~ "]" }

auto_define_list = { "auto" ~ define_list ~ ("\n" | ";") }
define_list = { variable ~ ("," ~ variable)* }
argument_list = { argument ~ ("," ~ argument)* }
argument = _{ array | expression }

expression = { primary ~ (binary_op ~ primary)* }
primary = {
number
| paren
| builtin_call
| fn_call
| prefix_increment
| prefix_decrement
| postfix_increment
| postfix_decrement
| negation
| register_assignment
| register
| assignment
| named_expression
}
paren = { "(" ~ expression ~ ")" }
builtin_call = { builtin_fn ~ "(" ~ expression ~ ")" }
fn_call = { letter ~ "(" ~ argument_list? ~ ")" }
prefix_increment = { "++" ~ named_expression }
prefix_decrement = { "--" ~ named_expression }
postfix_increment = { named_expression ~ "++" }
postfix_decrement = { named_expression ~ "--" }
negation = { "-" ~ primary }
assignment = { named_expression ~ assign_op ~ expression }
register_assignment = { register ~ assign_op ~ expression }
named_expression = {
| array_item
| variable_number
}
array_item = { letter ~ "[" ~ expression ~ "]" }
register = {
| scale
| ibase
| obase
}
scale = { "scale" }
ibase = { "ibase" }
obase = { "obase" }
Loading