--- /dev/null
+#include <limits>
+#include <cmath> // abs
+#include <memory>
+
+#include "catch.hpp"
+
+TEST_CASE("operand real")
+{
+ Operand* pi{ new Real{3.14} };
+
+ CHECK( pi->evaluate() == 3.14 );
+ CHECK( pi->postfix() == "3.140" );
+}
+
+#if 0 // Flytta ned denna rad för att aktivera nästa TEST_CASE
+
+TEST_CASE("addition")
+{
+ Node* pi = new Real{3.14};
+ Node* e = new Real{2.72};
+ Node* pluslr = new Addition{pi, e};
+ Node* plusrl = new Addition{e, pi};
+
+ CHECK( pluslr->evaluate() == 5.86 );
+ CHECK( plusrl->evaluate() == 5.86 );
+ CHECK( pluslr->postfix() == "3.140 2.720 +" );
+ CHECK( plusrl->postfix() == "2.720 3.140 +" );
+}
+
+// If catch report 0.42 != 0.42 you are likely to have a small
+// rounding error in some invisible decimal place. In such case you
+// can compare if the difference is small enough to consider two
+// doubles equal anyway.
+bool compare_equal(double a, double b)
+{
+ return std::abs(a - b) <= std::numeric_limits<double>::epsilon();
+}
+
+TEST_CASE("test case ignoring rounding errors")
+{
+ Node* a = new Real{0.01};
+ Node* b = new Real{0.09};
+ Node* plus = new Addition{a, b};
+
+ CHECK( abs(-0.1) == 0.1 ); // make sure we use correct "abs"
+ // CHECK( plus->evaluate() == 0.1 ); // Rounding error!!
+ CHECK( compare_equal(plus->evaluate(), 0.1) );
+}
+
+
+TEST_CASE("operand integer")
+{
+ Operand* i = new Integer{7};
+
+ CHECK( i->evaluate() == 7 );
+ CHECK( i->postfix() == "7" );
+}
+
+TEST_CASE("conversion to string")
+{
+ Node* a = new Addition{ new Subtraction{ new Real{1.0},
+ new Integer{1} },
+ new Multiplication{ new Integer{2},
+ new Integer{5} }
+ };
+ SECTION("prefix")
+ {
+ CHECK( a->prefix() == "+ - 1.000 1 * 2 5" );
+ }
+ SECTION("infix")
+ {
+ CHECK( a->infix() == "( ( 1.000 - 1 ) + ( 2 * 5 ) )" );
+ }
+ SECTION("postfix")
+ {
+ CHECK( a->postfix() == "1.000 1 - 2 5 * +" );
+ }
+}
+
+#endif
7. Behöver inte veta någonting mer än att noden har en get_value funktion.
+# Uppgift 2
+
+Diagram finns. https://app.smartdraw.com/editor.aspx?credID=-54147836&depoId=50100329&flags=128#depoId=50103329&credID=-54147836, forssennils2@gmail.com
+fråga om resultatnod /lövnoder/lövklasser. Behövs alla 9?
+
+
+
class Equation {
string result_var
expression (operator eller operand nod)
operand-nod värde;
}
-sin(4)
\ No newline at end of file
+sin(4)
--- /dev/null
+#include "postfix.h"
+#include "token.h"
+
+#include <string>
+#include <vector>
+#include <stack>
+#include <map>
+#include <istream>
+
+#include <sstream>
+#include <algorithm>
+#include <iterator>
+
+// PUBLIC
+
+Postfix::Postfix(std::string const& infix_string) : expr{}
+{
+ std::istringstream is{infix_string};
+ expr = make_postfix(is);
+}
+
+std::string Postfix::to_string() const
+{
+ std::ostringstream os;
+ std::copy( std::begin(expr), std::end(expr),
+ std::ostream_iterator<std::string>{os, " "});
+ return os.str();
+}
+
+Postfix::operator std::string() const
+{
+ return to_string();
+}
+
+bool Postfix::operator==(std::string const& rhs) const
+{
+ return to_string() == rhs;
+}
+
+
+// PRIVATE
+
+// right associative operators have input priority > stack priority
+// left associative operators have input priority < stack priority
+const Postfix::priority_table Postfix::operator_table {
+ // {symbol, input prio, stack prio}
+ {"^", {8, 7}},
+ {"*", {5, 6}},
+ {"/", {5, 6}},
+ {"%", {5, 6}},
+ {"+", {3, 4}},
+ {"-", {3, 4}},
+ {"=", {2, 1}}
+};
+
+bool Postfix::is_operator(const std::string& token)
+{
+ // C++20: return operator_table.contains( token );
+ return ( operator_table.count( token ) > 0 );
+}
+
+Postfix::expression Postfix::make_postfix(std::istream& is, bool match_parenthesis) const
+{
+ using namespace std::literals; // for string literals ""s
+
+ op_stack operator_stack;
+ expression postfix;
+ Token token;
+ int unused_operands{0};
+
+ while (is >> token && token != ")"s )
+ {
+ if ( is_operator(token) )
+ {
+ while ( ! operator_stack.empty() &&
+ operator_table.at(token).input <=
+ operator_table.at(operator_stack.top()).stack )
+ {
+ postfix.push_back( operator_stack.top() );
+ unused_operands -= 1; // uses 2 but creates 1
+ operator_stack.pop();
+ }
+ operator_stack.push(token);
+ }
+ else if (token == "("s)
+ {
+ expression parentesis{ make_postfix(is, true) };
+ std::copy( std::begin(parentesis), std::end(parentesis), std::back_inserter(postfix) );
+ unused_operands += 1; // entire postix is 1 operand
+ }
+ else
+ {
+ postfix.push_back( token );
+ unused_operands += 1;
+ }
+ }
+
+ if ( postfix.empty() && match_parenthesis && token == ")"s )
+ {
+ throw Infix_Error{"Empty parenthesis"};
+ }
+
+ if ( match_parenthesis && token != ")"s )
+ {
+ throw Infix_Error{"Missing ending parenthesis"};
+ }
+
+ if ( ! match_parenthesis && token == ")"s )
+ {
+ throw Infix_Error{"Missing starting parenthesis"};
+ }
+
+ while ( ! operator_stack.empty() )
+ {
+ postfix.push_back( operator_stack.top() );
+ unused_operands -= 1; // uses 2 but creates 1
+ operator_stack.pop();
+ }
+
+ if ( unused_operands != 1 ) // the last one is the answer
+ {
+ if ( unused_operands > 1 )
+ throw Infix_Error{"Missing operator"};
+ else
+ throw Infix_Error{"Missing operand"};
+ }
+
+ return postfix;
+}
--- /dev/null
+#define CATCH_CONFIG_RUNNER
+#include "catch.hpp"
+
+#include <iostream>
+#include <sstream>
+
+#include "postfix.h"
+
+using namespace std;
+
+TEST_CASE("infix to postfix conversion")
+{
+ CHECK( Postfix{"1"} == "1 ");
+
+ CHECK( Postfix{"1 + 2"} == "1 2 + ");
+
+ CHECK( Postfix{"1 + 2 + 3"} == "1 2 + 3 + ");
+ CHECK( Postfix{"1 + 2 * 3"} == "1 2 3 * + ");
+ CHECK( Postfix{"1 * 2 + 3"} == "1 2 * 3 + ");
+ CHECK( Postfix{"1 ^ 2 ^ 3"} == "1 2 3 ^ ^ ");
+ CHECK( Postfix{"1 - 2 - 3"} == "1 2 - 3 - ");
+ CHECK( Postfix{"1 - 2 + 3"} == "1 2 - 3 + ");
+
+ CHECK( Postfix{"1 / 2 - 3 * 4"} == "1 2 / 3 4 * - ");
+
+ CHECK( Postfix{"( ( 0 ) )"} == "0 ");
+
+ CHECK( Postfix{"( 1 ^ 2 ) ^ 3"} == "1 2 ^ 3 ^ ");
+ CHECK( Postfix{"1 ^ ( 2 ^ 3 )"} == "1 2 3 ^ ^ ");
+ CHECK( Postfix{"( 1 ^ ( 2 ^ 3 ) )"} == "1 2 3 ^ ^ ");
+ CHECK( Postfix{"( ( 1 ^ ( 2 ) ^ 3 ) )"} == "1 2 3 ^ ^ ");
+
+ CHECK( Postfix{"1 + 2 * 3 - 4 * 5 + 6 / 7 - 8"} == "1 2 3 * + 4 5 * - 6 7 / + 8 - ");
+ CHECK( Postfix{"( 1 + 2 ) * 3 - ( 4 * 5 + 6 ) / 7 - 8"} == "1 2 + 3 * 4 5 * 6 + 7 / - 8 - ");
+
+// CHECK( Postfix{"(1+2)*3-(4*5+6)/7-8"} == "1 2 + 3 * 4 5 * 6 + 7 / - 8 - ");
+}
+
+TEST_CASE("infix to postfix conversion problems")
+{
+ // Missing operands (empty infix)
+ CHECK_THROWS( Postfix("") );
+
+ // Missing operands (empty paranthesis)
+ CHECK_THROWS( Postfix("( )") );
+
+ // Missing operands
+ CHECK_THROWS( Postfix("1 +") );
+ CHECK_THROWS( Postfix("+ 2") );
+
+ // Missing operator
+ CHECK_THROWS( Postfix("1 1 - 1") );
+
+ // Missing parenthesis
+ CHECK_THROWS( Postfix("( ) + 2") );
+ CHECK_THROWS( Postfix("( + 2") );
+ CHECK_THROWS( Postfix("1 + 2 )") );
+}
+
+int main()
+{
+ Catch::Session session;
+
+ session.run();
+
+ return 0;
+}
--- /dev/null
+#ifndef POSTFIX_H
+#define POSTFIX_H
+
+#include <string>
+#include <vector>
+#include <stack>
+#include <map>
+#include <istream>
+#include <stdexcept>
+
+class Infix_Error : public std::logic_error
+{
+ using std::logic_error::logic_error;
+};
+
+// Creates a Postfix from a given infix string. All operands and
+// operators are required to be separated by at least one space.
+class Postfix
+{
+public:
+ Postfix(std::string const& infix_string);
+
+ std::string to_string() const;
+ operator std::string() const;
+ bool operator==(std::string const& rhs) const;
+
+private:
+ using op_stack = std::stack<std::string>;
+ using expression = std::vector<std::string>;
+
+ expression expr;
+
+ struct priority
+ {
+ int input;
+ int stack;
+ };
+
+ using priority_table = std::map<std::string, Postfix::priority>;
+
+ static const priority_table operator_table;
+
+ static bool is_operator(const std::string& token);
+
+ expression make_postfix(std::istream& is, bool match_parenthesis = false) const;
+};
+
+#endif
--- /dev/null
+#include <iostream>
+#include <iomanip>
+#include <string>
+#include <set>
+#include <algorithm>
+#include <cctype>
+
+#include "token.h"
+
+using namespace std;
+
+// Public
+
+set<string> const Token::default_operators{
+ "+", "-", "*", "/", "%", "^", "=", "(", ")", "?", "£", "$"
+ };
+
+string const Token::default_separators{" \t\n\r"};
+
+Token::Token(set<string> const& ops, string const& sep)
+ : operators{ops}, separators{sep}, token{}
+{
+}
+
+istream& operator>>(istream& iss, Token& t)
+{
+ return t.next(iss);
+}
+
+ostream& operator<<(ostream& oss, Token const& t)
+{
+ return oss << t.token;
+}
+
+bool Token::is_operator() const
+{
+ return operators.count( token ) == 1;
+}
+
+bool Token::is_integer() const
+{
+ return all_of(token.begin(), token.end(), ::isdigit);
+}
+
+bool Token::is_decimal() const
+{
+ bool valid_chars = all_of(token.begin(), token.end(), [](char c)->bool
+ {
+ return c == '.' || isdigit(c);
+ });
+ bool one_dot = count(token.begin(), token.end(), '.') == 1;
+ return valid_chars && one_dot;
+}
+
+bool Token::is_identifier() const
+{
+ return all_of(token.begin(), token.end(), [](char c)->bool
+ {
+ return c == '_' || isalnum(c);
+ });
+}
+
+// Private
+
+bool Token::is_separator(int c) const
+{
+ return ( separators.find(c) != string::npos );
+}
+
+bool Token::is_delimeter(int c) const
+{
+ if ( c == -1 ) // Traits::eof()
+ return true;
+
+ if ( is_separator( c ) )
+ return true;
+
+ auto b{ operators.begin() };
+ auto e{ operators.end() };
+
+ for ( ; b != e; ++b )
+ {
+ if ( b->at(0) == c )
+ return true;
+ }
+ return false;
+}
+
+bool Token::is_candidate() const
+{
+ auto b{ operators.begin() };
+ auto e{ operators.end() };
+
+ for ( ; b != e; ++b )
+ {
+ if ( b->find( token ) == 0 && *b != token )
+ return true;
+ }
+ return false;
+}
+
+void Token::append(int c)
+{
+ token.push_back( static_cast<char>(c) );
+}
+
+void Token::ignore_separators(istream& iss)
+{
+ auto c{ iss.peek() };
+
+ while ( c != -1 ) // Traits::eof()
+ {
+ if ( ! is_separator( c ) )
+ return;
+
+ iss.get();
+ c = iss.peek();
+ }
+}
+
+istream& Token::next(istream& iss)
+{
+ token.clear();
+
+ ignore_separators( iss );
+
+ auto c{ iss.peek() };
+
+ bool prev_is_op{false};
+
+ while ( c != -1 ) // Traits::eof()
+ {
+ append( c );
+
+ bool is_op{ is_operator() };
+ bool is_can{ is_candidate() };
+ bool is_del{ is_delimeter(c) };
+
+ if ( is_op && ! is_can )
+ {
+ iss.get();
+ return iss;
+ }
+
+ if ( prev_is_op && ! is_op && ! is_can )
+ {
+ token.pop_back();
+ return iss;
+ }
+
+ if ( is_del && ! is_can )
+ {
+ token.pop_back();
+ return iss;
+ }
+
+ prev_is_op = is_op;
+ iss.get();
+ c = iss.peek();
+ }
+
+ return iss;
+}
--- /dev/null
+#ifndef TOKEN_H
+#define TOKEN_H
+
+#include <iostream>
+#include <string>
+#include <set>
+
+// A class to read strings from an istream just like std::string,
+// but tokens are separated not only by space, but also by operators.
+//
+// You should be able to just change from "std::string" to "Token" in
+// your program unless you do something special with your strings.
+//
+// Tokens can be classified as integers, deciamls, identifiers or operators.
+//
+class Token
+{
+public:
+
+ // interoperability with standard library (STL)
+ using token_t = std::string;
+
+ using value_type = token_t::value_type;
+ using size_type = token_t::size_type;
+ using pointer = token_t::pointer;
+ using reference = token_t::reference;
+ using iterator = token_t::const_iterator;
+
+ iterator begin() const { return token.begin(); }
+ iterator end() const { return token.end(); }
+ size_type size() const { return token.size(); }
+ size_type length() const { return token.length(); }
+ value_type at(int i) const { return token.at(i); }
+
+ // static constants for default values
+ static std::set<std::string> const default_operators;
+ static std::string const default_separators;
+
+ // public members
+ Token(std::set<std::string> const& operators = default_operators,
+ std::string const& separators = default_separators);
+
+ friend std::istream& operator>>(std::istream& iss, Token& t);
+
+ friend std::ostream& operator<<(std::ostream& oss, Token const& t);
+
+ operator std::string() const { return token; }
+
+ bool operator==(std::string const& rhs) { return token == rhs; }
+ bool operator!=(std::string const& rhs) { return token != rhs; }
+
+ bool is_operator() const;
+ bool is_integer() const;
+ bool is_decimal() const;
+ bool is_identifier() const;
+
+private:
+
+ // help functions
+ bool is_delimeter(int c) const;
+ bool is_separator(int c) const;
+
+ bool is_candidate() const;
+
+ void append(int c);
+ void ignore_separators(std::istream& iss);
+
+ std::istream& next(std::istream& iss);
+
+ // data members
+ std::set<std::string> const& operators;
+ std::string const& separators;
+ std::string token;
+
+ friend class Token_Private_Tester;
+};
+
+#endif
--- /dev/null
+#define CATCH_CONFIG_RUNNER
+#include "catch.hpp"
+
+#include <iostream>
+#include <sstream>
+
+#include "token.h"
+
+using namespace std;
+
+const std::set<std::string> my_operators{
+ "+", "+-+", "-", "*", "/", "%", "^", "=", "==", "(", ")", "?", "£", "$", "####"
+ };
+
+class Token_Private_Tester
+{
+public:
+ Token_Private_Tester(Token& t) : t{t} {}
+
+ bool is_delimeter(int c) const { return t.is_delimeter(c); }
+ bool is_separator(int c) const { return t.is_separator(c); }
+
+ bool is_candidate() const { return t.is_candidate(); }
+
+ bool check_candidate(string const& s) { t.token = s; return t.is_candidate(); }
+
+ Token& set(string const& s) { t.token = s; return t; }
+
+private:
+ Token& t;
+};
+
+TEST_CASE("help functions")
+{
+ Token tok{my_operators};
+ Token_Private_Tester t{tok};
+
+ CHECK( t.is_delimeter('+') );
+ CHECK( t.is_delimeter('#') );
+ CHECK( t.is_delimeter(' ') );
+ CHECK_FALSE( t.is_delimeter('.') );
+
+ CHECK( t.is_separator(' ') );
+ CHECK_FALSE( t.is_separator('0') );
+
+ CHECK( t.check_candidate("+") );
+ CHECK( t.check_candidate("+-") );
+ CHECK_FALSE( t.check_candidate("+-+") );
+ CHECK_FALSE( t.check_candidate("+-++") );
+
+}
+
+TEST_CASE("classifier functions")
+{
+ Token tok{my_operators};
+ Token_Private_Tester t{tok};
+
+ CHECK( t.set("+").is_operator() );
+ CHECK_FALSE( t.set("+-").is_operator() );
+ CHECK( t.set("+-+").is_operator() );
+ CHECK_FALSE( t.set("+-++").is_operator() );
+
+ CHECK( t.set("123").is_integer() );
+ CHECK( t.set("3.14").is_decimal() );
+ CHECK( t.set("x_34").is_identifier() );
+
+ CHECK_FALSE( t.set("123a").is_integer() );
+ CHECK_FALSE( t.set("3.14.").is_decimal() );
+ CHECK_FALSE( t.set("=x_34").is_identifier() );
+}
+
+TEST_CASE("normal input output")
+{
+ Token t{my_operators};
+ istringstream iss{"1+2*34-sfd"};
+ ostringstream oss{};
+
+ while ( iss >> t )
+ {
+ oss << t << " ";
+ }
+ CHECK( oss.str() == "1 + 2 * 34 - sfd ");
+}
+
+TEST_CASE("normal input output, default operators")
+{
+ Token t;
+ istringstream iss{"1+2*34-sfd"};
+ ostringstream oss{};
+
+ while ( iss >> t )
+ {
+ oss << t << " ";
+ }
+ CHECK( oss.str() == "1 + 2 * 34 - sfd ");
+}
+
+TEST_CASE("multichar operator input output")
+{
+ Token t{my_operators};
+ istringstream iss{"1+-++2===as#####fgh"};
+ ostringstream oss{};
+
+ while ( iss >> t )
+ {
+ oss << t << " ";
+ }
+ CHECK( oss.str() == "1 +-+ + 2 == = as #### #fgh ");
+}
+
+int main(int argc, char* argv[] __attribute__((unused)))
+{
+ Catch::Session session;
+
+ session.run();
+
+
+ Token t{my_operators};
+ string s;
+
+ if ( argc <= 1 )
+ return 0;
+
+ cout << "Manual test mode, press Ctrl-D to exit. "
+ << "Please enter lines to split: " << endl;
+ while ( getline(cin, s) )
+ {
+ istringstream iss{s};
+
+ while ( iss >> t )
+ {
+ if ( t.is_operator() )
+ cout << "op: '" << t << "', ";
+ else if ( t.is_integer() )
+ cout << "int: '" << t << "', ";
+ else if ( t.is_decimal() )
+ cout << "double: '" << t << "', ";
+ else if ( t.is_identifier() )
+ cout << "var: '" << t << "', ";
+ else
+ cout << "token: '" << t << "', ";
+ }
+ cout << endl;
+ }
+ return 0;
+}
--- /dev/null
+#include <iostream>
+
+using namespace std;
+
+int main()
+{
+ string line;
+ while ( getline(cin, line) )
+ {
+ Expression e{line};
+ cout << e.to_string() << endl
+ << e.evaluate() << endl;
+ }
+ return 0;
+}