2023-11-02 17:25:24 +01:00
|
|
|
#include <iostream>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <vector>
|
|
|
|
#include <sstream>
|
|
|
|
#include <iterator>
|
|
|
|
#include <fstream>
|
|
|
|
|
2024-03-20 09:18:56 +01:00
|
|
|
#include "src/adt.h"
|
2023-11-02 17:25:24 +01:00
|
|
|
#include "src/eval.h"
|
|
|
|
#include "src/macro.h" // for split static method
|
|
|
|
|
2024-03-12 15:34:14 +01:00
|
|
|
using namespace dc;
|
|
|
|
|
2023-11-02 17:25:24 +01:00
|
|
|
void helper() {
|
|
|
|
std::cout << "RPN desktop calculator with macro support. Usage: \n"
|
|
|
|
<< "-e, --expression <EXPRESSION> | Evaluate an expression\n"
|
|
|
|
<< "-f, --file <FILE> | Evaluate a file\n"
|
|
|
|
<< "-h, --help | Show this helper\n"
|
|
|
|
<< "-V, --version | Show version" << std::endl;
|
|
|
|
}
|
|
|
|
|
2024-03-14 10:24:25 +01:00
|
|
|
void version() {
|
|
|
|
std::cout << "dc (v" << DC_VER << ", " << DC_HASH << ", " << DC_BUILD ")" << std::endl;
|
2024-03-19 12:22:01 +01:00
|
|
|
std::cout << "Build date: " << DC_BUILD_DATE << std::endl;
|
2024-03-14 10:24:25 +01:00
|
|
|
std::cout << "Compile flags: " << DC_FLAGS << std::endl;
|
|
|
|
std::cout << "Copyright (c) 2024 Marco Cetica" << std::endl;
|
|
|
|
std::cout << "License GPLv3+: GNU GPL version 3 or later\n" << std::endl;
|
|
|
|
std::cout << "The original version of the dc command was written by Robert Morris\n"
|
|
|
|
<< "and Lorinda Cherry. This version of dc is developed by Marco Cetica.\n" << std::endl;
|
|
|
|
std::cout << "Project homepage: <https://git.marcocetica.com/marco/dc>.\n"
|
|
|
|
<< "Email bug reports to: <email@marcocetica.com>." << std::endl;
|
|
|
|
}
|
|
|
|
|
2023-11-02 17:25:24 +01:00
|
|
|
int main(int argc, char **argv) {
|
|
|
|
int opt;
|
|
|
|
const char *short_opts = "e:f:hV";
|
2024-03-07 16:16:17 +01:00
|
|
|
std::string cli_expression;
|
|
|
|
std::string file_name;
|
|
|
|
std::string stdin_expression;
|
2023-11-02 17:25:24 +01:00
|
|
|
bool execute_expression = false;
|
|
|
|
bool execute_file = false;
|
2024-03-12 15:34:14 +01:00
|
|
|
Stack<std::string> stack;
|
2023-11-02 17:25:24 +01:00
|
|
|
std::unordered_map<char, Register> regs;
|
2023-11-15 10:23:29 +01:00
|
|
|
Parameters parameters = {
|
|
|
|
.precision = 0,
|
|
|
|
.iradix = 10,
|
|
|
|
.oradix = radix_base::DEC
|
|
|
|
};
|
2023-11-02 17:25:24 +01:00
|
|
|
struct option long_opts[] = {
|
|
|
|
{"expression", required_argument, nullptr, 'e'},
|
|
|
|
{"file", required_argument, nullptr, 'f'},
|
|
|
|
{"help", no_argument, nullptr, 'h'},
|
|
|
|
{"version", no_argument, nullptr, 'V'},
|
|
|
|
{nullptr, 0, nullptr, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
while((opt = getopt_long(argc, argv, short_opts, long_opts, nullptr)) != -1) {
|
|
|
|
switch(opt) {
|
|
|
|
case 'e': {
|
|
|
|
cli_expression = std::string(optarg);
|
|
|
|
execute_expression = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'f': {
|
|
|
|
file_name = std::string(optarg);
|
|
|
|
execute_file = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'V': {
|
2024-03-14 10:24:25 +01:00
|
|
|
version();
|
2023-11-02 17:25:24 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: case 'h': case ':': case '?': helper(); return 0;;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Evaluate cli expression
|
|
|
|
if(execute_expression) {
|
|
|
|
// Split string expression into a vector
|
|
|
|
std::vector<std::string> tokens = Macro::split(cli_expression);
|
|
|
|
// Evaluate expression
|
2023-11-15 10:23:29 +01:00
|
|
|
Evaluate evaluator(tokens, regs, stack, parameters);
|
2023-11-02 17:25:24 +01:00
|
|
|
auto err = evaluator.eval();
|
|
|
|
// Handle errors
|
|
|
|
if(err != std::nullopt) {
|
|
|
|
std::cerr << err.value() << std::endl;
|
2023-11-03 10:48:30 +01:00
|
|
|
return 1;
|
2023-11-02 17:25:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
} else if(execute_file) {
|
|
|
|
// Open file from disk
|
|
|
|
std::fstream source_file(file_name, std::ios::in | std::ios::binary);
|
|
|
|
if(source_file.fail()) {
|
|
|
|
std::cerr << "Cannot open source file \"" << file_name << "\"." << std::endl;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read whole file into a buffer
|
|
|
|
std::stringstream buf;
|
|
|
|
buf << source_file.rdbuf();
|
|
|
|
|
|
|
|
// Execute file line by line
|
|
|
|
std::string line;
|
|
|
|
while(std::getline(buf, line, '\n')) {
|
|
|
|
// Ignore comments or empty lines
|
|
|
|
if(line.empty() || line.starts_with('#')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove inline comments
|
|
|
|
auto comment_pos = line.find('#');
|
|
|
|
std::vector<std::string> tokens;
|
|
|
|
|
|
|
|
if(comment_pos != std::string::npos) {
|
|
|
|
// Convert only the first part of the line
|
|
|
|
tokens = Macro::split(line.substr(0, comment_pos));
|
|
|
|
} else {
|
|
|
|
// Otherwise, convert the whole string
|
|
|
|
tokens = Macro::split(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Evaluate expression
|
2023-11-15 10:23:29 +01:00
|
|
|
Evaluate evaluator(tokens, regs, stack, parameters);
|
2023-11-02 17:25:24 +01:00
|
|
|
auto err = evaluator.eval();
|
|
|
|
// Handle errors
|
|
|
|
if(err != std::nullopt) {
|
|
|
|
std::cerr << err.value() << std::endl;
|
2024-03-19 12:22:01 +01:00
|
|
|
return 1;
|
2023-11-02 17:25:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Otherwise, evaluate from stdin
|
|
|
|
while(std::getline(std::cin, stdin_expression)) {
|
|
|
|
// Split string expression into a vector
|
|
|
|
std::vector<std::string> tokens = Macro::split(stdin_expression);
|
|
|
|
// Evaluate expression
|
2023-11-15 10:23:29 +01:00
|
|
|
Evaluate evaluator(tokens, regs, stack, parameters);
|
2023-11-02 17:25:24 +01:00
|
|
|
auto err = evaluator.eval();
|
|
|
|
// Handle errors
|
|
|
|
if(err != std::nullopt) {
|
|
|
|
std::cerr << err.value() << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|