Skip to content

Instantly share code, notes, and snippets.

@Raincode
Last active June 5, 2017 21:55
Show Gist options
  • Save Raincode/e5115669bb647ceba690252ed39445f8 to your computer and use it in GitHub Desktop.
Save Raincode/e5115669bb647ceba690252ed39445f8 to your computer and use it in GitHub Desktop.
Desk Calculator in C++
#include "Calculator.hpp"
#include <fstream>
#include <iostream>
#include <sstream>
#include "mps/str_util.hpp"
#include "mps/clipboard.hpp"
#include "mps/console_util.hpp"
#include "math_util.hpp"
#include "types.hpp"
#include "Warning.hpp"
using namespace std;
Calculator::Calculator()
: parser{ symbolTable }
{
register_commands();
ifstream ifs{ "prompt.txt" }; // ommitted error handling, since there is a default prompt in place
getline(ifs, prompt);
if (ifstream ifs{ "intro.txt" }) {
std::ostringstream s;
s << ifs.rdbuf();
intro = s.str();
}
parser.on_result([this](auto&& n) {
parser.symbol_table().set_var("_", n);
parser.symbol_table().set_var("ans", n);
print_complex(cout, n);
cout << '\n';
});
}
void Calculator::run(int argc, char* argv[])
{
switch (argc) {
case 1:
run_cli();
break;
case 2:
if (string(argv[1]) == "-")
run_cli();
else if (!run_file(argv[1]))
parser.parse(argv[1]);
break;
default:
throw runtime_error{ "Invalid number of arguments" };
}
}
bool Calculator::run_file(const string& path)
{
ifstream ifs{ path };
if (!ifs)
return false;
parser.parse(ifs);
return true;
}
void Calculator::run_cli()
{
show_intro();
cout << prompt;
for (string s; getline(cin, s); ) {
s = mps::str::trim(s);
try {
if (s.size() && !handle_cmd(s))
parser.parse(s);
}
catch (runtime_error& e) {
cerr << e.what() << '\n';
}
cout << prompt;
}
}
void Calculator::show_intro() const
{
cout << intro;
static Warning w{ "Copyright (C) 2017 Matthias Stauber\n"
"This program comes with ABSOLUTELY NO WARRANTY\n" };
}
bool Calculator::handle_cmd(const std::string& cmd)
{
auto found = commands.find(mps::str::tolower(cmd));
if (found != end(commands)) {
found->second();
return true;
}
return false;
}
void Calculator::register_commands()
{
static const string helpText{
"For a list of operators, commands and functions please view the readme file\n"
};
commands["help"] = [] { std::cout << helpText; };
commands["clear"] = commands["cls"] = [this] {
mps::cls();
show_intro();
};
commands["clear all"] = [this] {
parser.symbol_table().clear();
mps::cls();
show_intro();
};
commands["clear vars"] = [this] { parser.symbol_table().clear_vars(); };
commands["clear funcs"] = [this] { parser.symbol_table().clear_funcs(); };
commands["clear lists"] = [this] { parser.symbol_table().clear_lists(); };
commands["hide vars"] = [this] { parser.set_vardef_is_res(false); };
commands["show vars"] = [this] { parser.set_vardef_is_res(true); };
commands["ls"] = [this] {
auto&& vars = parser.symbol_table().vars();
if (vars.size())
cout << "Variables:\n~~~~~~~~~~\n";
for (const auto& v : vars) {
cout << " " << v.first << " = ";
print_complex(cout, v.second);
cout << '\n';
}
auto&& funcs = parser.symbol_table().funcs();
if (funcs.size())
cout << "\nFunctions:\n~~~~~~~~~~\n";
for (const auto& f : funcs)
cout << " " << f.second << '\n';
auto&& lists = parser.symbol_table().lists();
if (lists.size())
cout << "\nLists:\n~~~~~~\n";
for (const auto& l : lists) {
cout << " " << l.first << " = ";
print_list(cout, l.second);
cout << '\n';
}
};
commands["run"] = [this] {
string fname;
if (cout << "file: " && getline(cin, fname))
run_file(fname);
};
commands["copy"] = [this] {
auto str = mps::str::to_string(parser.symbol_table().value_of("ans"));
mps::set_clipboard_text(str);
};
commands["copy,"] = [this] {
auto str = mps::str::to_string(parser.symbol_table().value_of("ans"));
mps::set_clipboard_text(mps::str::format_number_EU(str));
};
commands["table"] = [this] {
string func = mps::get_str("Function: ");
double low = mps::get_num<double>("Low: ");
double high = mps::get_num<double>("High: ");
cout << "Sorry, table feature not implemented yet.\n";
};
commands["dec"] = [] {
cout << "hex/bin (W/ leading 0): ";
int val{};
if (mps::read_hex(cin, val))
cin.ignore();
else if (!mps::read_bin(cin, val)) {
mps::recover_line(cin);
return;
}
std::cout << val << '\n';
};
commands["bin"] = [] {
cout << "dec/hex: ";
int val{};
if (!mps::read_hex(cin, val) && !(cin >> val)) {
mps::recover_line(cin);
return;
}
cin.ignore();
mps::print_binary(cout, val);
cout << '\n';
};
commands["hex"] = [] {
cout << "dec/bin (w/ leading 0): ";
int val{};
if (!mps::read_bin(cin, val)) {
if (!(cin >> val)) {
mps::recover_line(cin);
return;
}
cin.ignore();
}
cout << "0x" << hex << val << dec << '\n';
};
commands["exp"] = [this] {
if (!parser.has_result())
return;
auto && n = parser.result();
cout << abs(n) << "*e^(" << deg(arg(n)) << "deg)i\n";
};
}
#pragma once
#include <functional>
#include <map>
#include <string>
#include "Parser.hpp"
#include "SymbolTable.hpp"
class Calculator {
public:
Calculator();
void run(int argc, char* argv[]);
private:
void show_intro() const;
bool handle_cmd(const std::string& cmd);
void register_commands();
bool run_file(const std::string& path);
void run_cli();
SymbolTable symbolTable;
Parser parser;
std::string prompt{ "> " };
std::string intro;
std::map<std::string, std::function<void()>> commands;
};
#include "Function.hpp"
#include <ostream>
#include "mps/str_util.hpp"
#include "Parser.hpp"
#include "SymbolGuard.hpp"
#include "SymbolTable.hpp"
#include "types.hpp"
Function::Function(const std::string& name, SymbolTable& table)
: funcName{ name }, table{ table }
{
}
Complex Function::operator()(const List& args) const
{
if (args.size() != vars.size())
throw std::runtime_error{ funcName + " expects " + std::to_string(vars.size()) +
" arguments (received " + std::to_string(args.size()) + ")" };
Parser parser{ table };
SymbolGuard guard{ table };
for (std::size_t i = 0; i < vars.size(); ++i)
guard.shadow_var(vars[i], args[i]);
parser.parse(term);
return parser.result();
}
std::ostream& operator<<(std::ostream& os, const Function& func)
{
os << func.funcName << '(';
std::string sep;
for (const auto& v : func.vars) {
os << sep << v;
sep = ",";
}
return os << ") = " << func.term;
}
#pragma once
#include <string>
#include <vector>
#include "types.hpp"
class SymbolTable;
class Function {
public:
Function(const std::string& name, SymbolTable& table);
Complex operator()(const List& args) const;
void set_term(const std::string& t) { term = t; }
void add_var(const std::string& v) { vars.push_back(v); }
std::size_t numArgs() const { return vars.size(); }
const std::string& name() const { return funcName; }
friend std::ostream& operator<<(std::ostream& os, const Function& func);
private:
SymbolTable& table;
std::string funcName;
std::string term;
std::vector<std::string> vars;
};
Start = {Statement} End
Statement = [Expression | FunctionDefinition | ListDefinition | Deletion] (';' | '\n')
FunctionDefinition = 'fn' Identifier ParamList '=' Expression
Deletion = 'del' Identifier {Identifier}
Expression = Term { ('+' | '-') Term }
Term = Sign { ('*' | '/' | '%' | 'mod' | 'div' | '||') Sign }
Sign = ['+' | '-'] Postfix
Postfix = Primary ( ('^' | '**') Sign | {'!'} | Variable | FunctionCall)
Primary = Number | Variable | VariableDefinition | FunctionCall | '(' Expression ')'
ListDefinition = Identifier '=' BracketList
FunctionCall = Identifier ArgList
BracketList = '[' ListElements ']'
ArgList = '(' (ListElements | List) ')'
ListElements = [Expression] {',' Expression}
ParamList = '(' Identifier {',' Identifier} ')'
VariableDefinition = Identifier '=' Expression
Variable = ? Identifier stored in symbol table as Number value ?
List = ? Identifier stored in symbol table as List of Numbers ?
Identifier = Letter {'_' | Letter | Digit}
Number = ? C++ double ?
Letter = a..z | A..Z
Digit = 0..9
#include "Parser.hpp"
#include <cmath>
#include <iostream>
#include "mps/stream_util.hpp"
#include "math_util.hpp"
#include "SymbolTable.hpp"
Parser::Parser(SymbolTable& table)
: table{ table } { }
void Parser::set_symbol_table(SymbolTable& t)
{
table = t;
}
void Parser::parse(std::istream& is)
{
ts.set_input(is);
parse();
}
void Parser::parse(const std::string& str)
{
ts.set_input(str);
parse();
}
void Parser::parse()
{
ts.get();
while (!consume(Kind::End))
stmt();
}
void Parser::stmt()
{
hasResult = true;
if (peek(Kind::FuncDef))
func_def();
else if (peek(Kind::Delete))
deletion();
else if (!peek(Kind::Print))
res = expr();
if (hasResult && onRes)
onRes(res);
if (!peek(Kind::End))
expect(Kind::Print);
}
void Parser::func_def()
{
expect(Kind::FuncDef);
Function func{ ident(), table };
parse_param_list(func);
expect(Kind::Assign);
parse_func_term(func);
List test_args(func.numArgs(), 1);
func(test_args);
table.set_func(func.name(), func);
hasResult = false;
}
void Parser::parse_param_list(Function& func)
{
expect(Kind::LParen);
do {
func.add_var(ident());
} while (consume(Kind::Comma));
expect(Kind::RParen);
}
void Parser::parse_func_term(Function& func)
{
std::ostringstream term;
while (!peek(Kind::Print) && !peek(Kind::End) && !peek(Kind::RBracket)) {
term << ts.current();
ts.get();
}
func.set_term(term.str());
}
void Parser::deletion()
{
expect(Kind::Delete);
if (!peek(Kind::String))
error("No arguments provided to delete");
do {
del_symbol();
} while (peek(Kind::String));
hasResult = false;
}
static bool is_const(const std::string& var)
{
return var == "pi" || var == "e" || var == "i" || var == "deg";
}
void Parser::del_symbol()
{
auto& name = ident();
if (is_const(name))
error(name, " is a constant");
if (table.is_reserved_func(name))
error(name, " is a built-in function");
table.remove_symbol(name);
}
void Parser::list_def(const std::string& name)
{
table.set_list(name, list());
}
Complex Parser::expr()
{
auto left = term();
for (;;) {
if (consume(Kind::Plus))
left += term();
else if (consume(Kind::Minus))
left -= term();
else
return left;
}
}
Complex Parser::term()
{
auto left = sign();
for (;;) {
if (consume(Kind::Mul))
left *= sign();
else if (consume(Kind::Div))
left = safe_div(left, sign());
else if (consume(Kind::FloorDiv))
left = safe_floordiv(left, sign());
else if (consume(Kind::Mod))
left = safe_mod(left, sign());
else if (consume(Kind::Parallel))
left = impedance_parallel(left, sign());
else
return left;
}
}
Complex Parser::sign()
{
if (consume(Kind::Minus))
return -postfix();
consume(Kind::Plus);
return postfix();
}
Complex Parser::postfix()
{
auto left = prim();
for (;;) {
if (consume(Kind::Pow))
return pretty_pow(left, sign());
else if (peek(Kind::String))
return left * postfix();
else if (peek(Kind::LParen))
return left * prim();
else if (consume(Kind::Fac))
left = factorial(left);
else
return left;
}
}
Complex Parser::prim()
{
if (consume(Kind::Number))
return prevTok.num;
if (peek(Kind::String))
return resolve_str_tok();
if (consume(Kind::LParen)) {
auto val = expr();
expect(Kind::RParen);
return val;
}
error("Unexpected Token ", ts.current());
throw std::logic_error{ "Fall through prim()" }; // silence C4715
}
Complex Parser::resolve_str_tok()
{
auto name = ident();
if (peek(Kind::LParen))
return table.call_func(name, arg_list());
else if (consume(Kind::Assign)) {
if (peek(Kind::LBracket)) {
list_def(name);
return no_result();
}
return var_def(name);
}
else if (table.has_list(name)) {
if (!peek(Kind::Print) && !peek(Kind::End))
error("Unexpected Token ", ts.current());
print_list(std::cout, table.list(name));
std::cout << '\n';
return no_result();
}
return table.value_of(name);
}
Complex Parser::var_def(const std::string& name)
{
if (is_const(name))
error("Cannot override constant ", name);
if (peek(Kind::LBracket)) {
table.set_list(name, list());
return no_result();
}
if (table.isset(name) && !table.has_var(name))
error(name, " is already defined");
auto val = expr();
table.set_var(name, val);
return varDefIsRes ? val : no_result();
}
Complex Parser::no_result()
{
hasResult = false;
return 0; // dummy value
}
List Parser::list()
{
expect(Kind::LBracket);
List l;
if (consume(Kind::For)) { // [for var=start, end:step loopExpression]
auto var = ident();
expect(Kind::Assign);
auto start = prim().real();
expect(Kind::Comma);
auto end = prim().real();
double step{ 1 };
if (consume(Kind::Colon))
step = prim().real();
Function f{ "__internal__", table };
f.add_var(var);
parse_func_term(f);
if (start < end && step <= 0 || start > end && step >= 0)
error("Infinite loop");
for (auto i = start; i <= end; i += step)
l.emplace_back(f({ Complex(i) }));
}
else
l = list_elem();
expect(Kind::RBracket);
return l;
}
List Parser::arg_list()
{
expect(Kind::LParen);
List args;
if (peek(Kind::String) && table.has_list(ts.current().str))
args = table.list(ident());
else if (peek(Kind::LBracket))
args = list();
else
args = list_elem();
expect(Kind::RParen);
if (args.empty())
error("Invalid empty argument list");
return args;
}
List Parser::list_elem()
{
List list;
if (!peek(Kind::RParen) && !peek(Kind::RBracket)) {
do {
list.emplace_back(expr());
} while (consume(Kind::Comma));
}
return list;
}
const std::string& Parser::ident()
{
expect(Kind::String);
return prevTok.str;
}
bool Parser::consume(Kind kind)
{
if (ts.current().kind == kind) {
prevTok = ts.current();
ts.get();
return true;
}
return false;
}
bool Parser::peek(Kind kind) const
{
return ts.current().kind == kind;
}
void Parser::expect(Kind kind)
{
if (!consume(kind))
error("Expected Token ", kind);
}
#pragma once
#include <istream>
#include <map>
#include <string>
#include "ErrorReporter.hpp"
#include "Function.hpp"
#include "TokenStream.hpp"
#include "types.hpp"
class SymbolTable;
class Parser {
public:
Parser(SymbolTable& table);
void parse(std::istream& is);
void parse(const std::string& input);
Complex result() const { return res; }
bool has_result() const { return hasResult; }
SymbolTable& symbol_table() { return table; }
void set_symbol_table(SymbolTable& t);
void set_vardef_is_res(bool isRes) { varDefIsRes = isRes; }
void on_result(std::function<void(Complex)> handler) { onRes = std::move(handler); }
private:
void parse();
void stmt();
void func_def();
void parse_param_list(Function& func);
void parse_func_term(Function& func);
void deletion();
void del_symbol();
void list_def(const std::string& name);
Complex expr();
Complex term();
Complex sign();
Complex postfix();
Complex prim();
Complex resolve_str_tok();
Complex var_def(const std::string& name);
Complex no_result();
List list();
List arg_list();
List list_elem();
const std::string& ident();
bool peek(Kind kind) const;
bool consume(Kind kind);
void expect(Kind kind);
Token prevTok;
SymbolTable& table;
TokenStream ts;
ErrorReporter error;
Complex res;
bool hasResult{};
bool varDefIsRes{ true };
std::function<void(Complex)> onRes;
};
#include "SymbolGuard.hpp"
SymbolGuard::SymbolGuard(SymbolTable& table)
: table{ table }
{
}
SymbolGuard::~SymbolGuard()
{
for (const auto& var : shadowedVars)
table.remove_var(var);
for (const auto& pair : varCache)
table.set_var(pair.first, pair.second);
}
void SymbolGuard::shadow_var(const std::string& name, Complex tempVal)
{
if (table.has_var(name)) {
auto val = table.value_of(name);
varCache[name] = val;
table.remove_var(name);
}
table.set_var(name, tempVal);
shadowedVars.push_back(name);
}
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include "SymbolTable.hpp"
#include "types.hpp"
class SymbolGuard {
public:
SymbolGuard(SymbolTable& table);
~SymbolGuard();
void shadow_var(const std::string& name, Complex value);
private:
SymbolTable& table;
std::unordered_map<std::string, Complex> varCache;
std::vector<std::string> shadowedVars;
};
#include "SymbolTable.hpp"
#include <cassert>
#include <iostream>
#include <numeric>
#include <stdexcept>
#include <mps/stl_util.hpp>
#include "math_util.hpp"
using namespace std;
using namespace temp;
using namespace mps::stl;
#define CHECK_SINGLE_ARG(f) \
if (list.size() != 1) throw std::runtime_error{ #f " expects exactly 1 argument" };
#define MAKE_COMPLEX_FUNC(f) [] (const List& list) { \
CHECK_SINGLE_ARG(f) \
return (f)(list.front()); }
#define MAKE_PROXY_FUNC(f) [] (const Complex& c) { \
if (c.imag()) \
throw std::runtime_error{ #f " not defined for complex numbers" }; \
return Complex{ (f)(c.real()) }; }
#define MAKE_REAL_FUNC(f) [] (const List& list) { \
CHECK_SINGLE_ARG(f) \
if (list.front().imag()) \
throw std::runtime_error{ #f " not defined for complex numbers" }; \
return Complex{ (f)(list.front().real()) }; }
SymbolTable::SymbolTable()
{
add_constants();
}
bool SymbolTable::is_reserved_func(const std::string& name) const
{
return contains(defaultFuncTable, name);
}
void SymbolTable::set_var(ConstStrRef name, Complex val)
{
varTable[name] = val;
}
void SymbolTable::set_list(ConstStrRef name, List&& list)
{
listTable[name] = list;
}
void SymbolTable::set_func(ConstStrRef name, Function func)
{
funcTable.erase(name);
funcTable.emplace(name, std::move(func));
}
Complex SymbolTable::value_of(ConstStrRef var) const
{
return find_or_throw(varTable, var, "Variable " + var + " is undefined")->second;
}
const List& SymbolTable::list(ConstStrRef name) const
{
return find_or_throw(listTable, name, "List " + name + " is undefined")->second;
}
Complex SymbolTable::call_func(ConstStrRef func, const List& arg) const
{
auto f = funcTable.find(func);
if (f != end(funcTable))
return f->second(arg);
return find_or_throw(defaultFuncTable, func, "Function " + func + " is undefined")->second(arg);
}
bool SymbolTable::has_var(ConstStrRef name) const
{
return contains(varTable, name);
}
bool SymbolTable::has_list(ConstStrRef name) const
{
return contains(listTable, name);
}
bool SymbolTable::has_func(ConstStrRef name) const
{
return contains(funcTable, name);
}
bool SymbolTable::isset(ConstStrRef name) const
{
return has_var(name) || has_list(name) || has_func(name);
}
void SymbolTable::remove_var(ConstStrRef name)
{
varTable.erase(name);
}
void SymbolTable::remove_list(ConstStrRef name)
{
listTable.erase(name);
}
void SymbolTable::remove_func(ConstStrRef name)
{
funcTable.erase(name);
}
void SymbolTable::remove_symbol(ConstStrRef name)
{
if (!isset(name))
throw runtime_error{ name + " is undefined" };
if (has_func(name))
remove_func(name);
if (has_list(name))
remove_list(name);
if (has_var(name))
remove_var(name);
}
void SymbolTable::clear()
{
clear_vars();
clear_funcs();
clear_lists();
}
void SymbolTable::clear_vars()
{
varTable.clear();
add_constants();
}
void SymbolTable::clear_funcs()
{
funcTable.clear();
}
void SymbolTable::clear_lists()
{
listTable.clear();
}
void SymbolTable::add_constants()
{
varTable["i"] = { 0, 1 };
varTable["pi"] = pi;
varTable["e"] = 2.7182818284590452354;
varTable["deg"] = pi / 180;
}
const SymbolTable::FuncMap SymbolTable::defaultFuncTable{
{ "sin", MAKE_COMPLEX_FUNC(sin) },
{ "cos", MAKE_COMPLEX_FUNC(cos) },
{ "tan", MAKE_COMPLEX_FUNC(tan) },
{ "asin", MAKE_COMPLEX_FUNC(asin) },
{ "acos", MAKE_COMPLEX_FUNC(acos) },
{ "atan", MAKE_COMPLEX_FUNC(atan) },
{ "sinh", MAKE_COMPLEX_FUNC(sinh) },
{ "cosh", MAKE_COMPLEX_FUNC(cosh) },
{ "tanh", MAKE_COMPLEX_FUNC(tanh) },
{ "asinh", MAKE_COMPLEX_FUNC(asinh) },
{ "acosh", MAKE_COMPLEX_FUNC(acosh) },
{ "atanh", MAKE_COMPLEX_FUNC(atanh) },
{ "deg", MAKE_REAL_FUNC(deg) },
{ "rad", MAKE_REAL_FUNC(rad) },
{ "CtoK", MAKE_REAL_FUNC(CtoK) },
{ "KtoC", MAKE_REAL_FUNC(KtoC) },
{ "FtoC", MAKE_REAL_FUNC(FtoC) },
{ "CtoF", MAKE_REAL_FUNC(CtoF) },
{ "FtoK", MAKE_REAL_FUNC(FtoK) },
{ "KtoF", MAKE_REAL_FUNC(KtoF) },
{ "abs", MAKE_COMPLEX_FUNC(abs) },
{ "norm", MAKE_COMPLEX_FUNC(norm) },
{ "arg", MAKE_COMPLEX_FUNC(arg) },
{ "exp", MAKE_COMPLEX_FUNC(exp) },
{ "sqr", MAKE_COMPLEX_FUNC(sqr) },
{ "sqrt", MAKE_COMPLEX_FUNC(sqrt) },
{ "ln", MAKE_COMPLEX_FUNC(log) },
{ "log", MAKE_COMPLEX_FUNC(log10) },
{ "Re", MAKE_COMPLEX_FUNC(real) },
{ "Im", MAKE_COMPLEX_FUNC(imag) },
{ "floor", MAKE_REAL_FUNC(floor) },
{ "ceil", MAKE_REAL_FUNC(ceil) },
{ "round", MAKE_REAL_FUNC(round) },
{ "trunc", MAKE_REAL_FUNC(trunc) },
{ "cbrt", MAKE_REAL_FUNC(cbrt) },
{ "sum", sum },
{ "sum2", sqr_sum },
{ "avg", avg },
{ "len", len },
{ "sx", standard_deviation },
{ "ux", standard_uncertainty }
};
#pragma once
#include <map>
#include <string>
#include "mps/stl_util.hpp"
#include "Function.hpp"
#include "types.hpp"
class SymbolTable {
public:
using ConstStrRef = const std::string&;
using FuncMap = std::map<std::string, Func>;
SymbolTable();
bool is_reserved_func(const std::string& name) const;
void set_var(ConstStrRef name, Complex value);
void set_list(ConstStrRef name, List&& list);
void set_func(ConstStrRef name, Function func);
Complex value_of(ConstStrRef var) const;
const List& list(ConstStrRef var) const;
Complex call_func(ConstStrRef func, const List& args) const;
bool has_var(ConstStrRef name) const;
bool has_list(ConstStrRef name) const;
bool has_func(ConstStrRef name) const;
bool isset(ConstStrRef name) const;
void remove_var(ConstStrRef name);
void remove_list(ConstStrRef name);
void remove_func(ConstStrRef name);
void remove_symbol(ConstStrRef name);
void clear();
void clear_vars();
void clear_funcs();
void clear_lists();
const std::map<std::string, Complex>& vars() const { return varTable; }
const std::map<std::string, List>& lists() const { return listTable; }
const std::map<std::string, Function>& funcs() const { return funcTable; }
private:
void add_constants();
static const FuncMap defaultFuncTable;
std::map<std::string, Complex> varTable;
std::map<std::string, List> listTable;
std::map<std::string, Function> funcTable;
};
#pragma once
#include <string>
enum class Kind : char {
End,
Invalid,
Number,
String,
Parallel,
Print,
FloorDiv,
Delete,
FuncDef,
For,
Plus = '+',
Minus = '-',
Mul = '*',
Div = '/',
Mod = '%',
Pow = '^',
Fac = '!',
Assign = '=',
Comma = ',',
Colon = ':',
LParen = '(',
RParen = ')',
LBrace = '{',
RBrace = '}',
LBracket = '[',
RBracket = ']',
};
struct Token {
Kind kind{};
std::string str;
double num{};
};
inline std::ostream& operator<<(std::ostream& os, Kind kind)
{
switch (kind) {
case Kind::End: return os << "END";
case Kind::Invalid: return os << "INVALID";
case Kind::Number: return os << "NUMBER";
case Kind::String: return os << "STRING";
case Kind::Parallel: return os << "PARALLEL";
case Kind::Print: return os << "PRINT";
case Kind::FloorDiv: return os << "div";
default: return os << static_cast<char>(kind);
}
}
inline std::ostream& operator<<(std::ostream& os, const Token& token)
{
switch (token.kind) {
case Kind::Number: return os << token.num;
case Kind::String: return os << token.str;
default: return os << token.kind;
}
}
#include "TokenStream.hpp"
#include <cassert>
#include <cctype>
#include <iostream>
#include <map>
#include <sstream>
TokenStream::TokenStream(std::istream& is)
: input{ &is } { }
TokenStream::TokenStream(std::istream* is)
: input{ is }, ownsInput{ true } { }
TokenStream::TokenStream(const std::string& str)
: input{ new std::istringstream{str} }, ownsInput{ true } { }
void TokenStream::set_input(std::istream& is)
{
cleanup();
input = &is;
}
void TokenStream::set_input(std::istream* is)
{
cleanup();
input = is;
ownsInput = true;
}
void TokenStream::set_input(const std::string& str)
{
set_input(new std::istringstream{ str });
}
static constexpr unsigned char uchar(char ch)
{ // for isalpha etc. which expect ch >= -1 and ch <= 255
return static_cast<unsigned char>(ch);
}
Token TokenStream::get()
{
char ch;
do {
if (!input || !input->get(ch))
return ct = { Kind::End };
} while (std::isspace(uchar(ch)) && ch != '\n');
switch (ch) {
case ';':
case '\n':
return ct = { Kind::Print };
case '+':
case '-':
case '%':
case '!':
case '^':
case ',':
case '=':
case ':':
case '(': case ')':
case '[': case ']':
case '{': case '}':
return ct = { static_cast<Kind>(ch) };
case '*':
return ct = parse_double_op('*', Kind::Pow, Kind::Mul);
case '/':
return ct = parse_double_op('/', Kind::FloorDiv, Kind::Div);
case '|':
return ct = parse_double_op('|', Kind::Parallel, Kind::Invalid);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '.':
input->unget();
*input >> ct.num;
ct.kind = Kind::Number;
return ct;
default:
return ct = parse_identifier(ch);
}
}
Token TokenStream::parse_double_op(char next, Kind onMatch, Kind onFailure)
{
char c;
if (input->get(c) && c == next)
return { onMatch };
else {
input->unget();
if (onFailure != Kind::Invalid)
return { onFailure };
error("Expected Token: ", next);
}
return { Kind::Invalid };
}
Token TokenStream::parse_identifier(char ch)
{
if (std::isalpha(uchar(ch)) || ch == '_') {
std::string s{ ch };
while (input->get(ch) && (std::isalnum(uchar(ch)) || ch == '_'))
s += ch;
input->unget();
return identifier_to_token(s);
}
error("Bad Token ", ch);
return { Kind::Invalid };
}
static const std::map<std::string, Kind> strTokens{
{ "div", Kind::FloorDiv },
{ "mod", Kind::Mod },
{ "del", Kind::Delete },
{ "fn", Kind::FuncDef },
{ "for", Kind::For }
};
Token TokenStream::identifier_to_token(const std::string& str) const
{
auto found = strTokens.find(str);
if (found != cend(strTokens))
return { found->second };
return { Kind::String, str };
}
void TokenStream::cleanup()
{
if (ownsInput) {
delete input;
ownsInput = false;
}
}
#pragma once
#include <istream>
#include "ErrorReporter.hpp"
#include "Token.hpp"
class TokenStream {
public:
TokenStream() = default;
explicit TokenStream(std::istream& input);
explicit TokenStream(std::istream* input);
explicit TokenStream(const std::string& input);
TokenStream(const TokenStream&) = delete;
TokenStream& operator=(const TokenStream&) = delete;
void set_input(std::istream& input);
void set_input(std::istream* input);
void set_input(const std::string& input);
Token get();
const Token& current() const { return ct; }
private:
Token parse_identifier(char firstChar);
Token parse_double_op(char expected, Kind onSuccess, Kind onFailure);
Token identifier_to_token(const std::string& str) const;
void cleanup();
Token ct{ Kind::End };
std::istream* input{};
bool ownsInput{};
ErrorReporter error;
};
#pragma once
#include <complex>
#include <functional>
#include <iostream>
#include <vector>
using Complex = std::complex<double>;
using List = std::vector<Complex>;
using Func = std::function<Complex(const List&)>;
template<class T>
void print_complex(std::ostream& os, const std::complex<T>& n)
{
using std::cout;
if (n.imag()) {
if (n.real()) {
cout << n.real();
if (n.imag() > 0)
cout << '+';
}
if (std::abs(n.imag()) != 1)
cout << n.imag();
cout << (n.imag() == -1 ? "-" : "") << 'i';
}
else
cout << n.real();
}
template<class T>
void print_list(std::ostream& os, const std::vector<T>& v)
{
std::cout << '[';
std::string sep;
for (const auto& item : v) {
std::cout << sep;
print_complex(std::cout, item);
sep = ", ";
}
std::cout << ']';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment