parent
6363457d0f
commit
7a1093f982
@ -0,0 +1,29 @@ |
|||||||
|
// PPPheaders.h
|
||||||
|
|
||||||
|
#include<iostream> |
||||||
|
#include <sstream> |
||||||
|
#include<string> |
||||||
|
#include<vector> |
||||||
|
#include<span> |
||||||
|
#include<stdexcept> |
||||||
|
#include<random> |
||||||
|
|
||||||
|
#include<stdint.h> |
||||||
|
#include<list> |
||||||
|
#include <map> |
||||||
|
#include<unordered_map> |
||||||
|
#include <set> |
||||||
|
#include<memory> |
||||||
|
#include <algorithm> |
||||||
|
|
||||||
|
#define PPP_EXPORT |
||||||
|
#include "PPP_support.h" |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
using namespace PPP; |
||||||
|
|
||||||
|
|
||||||
|
// disgusting macro hack to get a range checking:
|
||||||
|
#define vector Checked_vector |
||||||
|
#define string Checked_string |
||||||
|
#define span Checked_span |
@ -0,0 +1,184 @@ |
|||||||
|
// PPP_support.h
|
||||||
|
|
||||||
|
/*
|
||||||
|
simple "Programming: Principles and Practice using C++ (3rd edition)" support |
||||||
|
*/ |
||||||
|
|
||||||
|
// PPP_EXPORT must turn into nothing on systems that don't support modules well
|
||||||
|
|
||||||
|
namespace PPP { |
||||||
|
|
||||||
|
using Unicode = long; |
||||||
|
|
||||||
|
// ------- first range checking -----
|
||||||
|
// primitive but most helpful to learners and portable
|
||||||
|
|
||||||
|
template<class T> Element = true; |
||||||
|
|
||||||
|
PPP_EXPORT template <Element T> |
||||||
|
class Checked_vector : public std::vector<T> { // trivially range-checked vector (no iterator checking)
|
||||||
|
public: |
||||||
|
using std::vector<T>::vector; |
||||||
|
|
||||||
|
T& operator[](size_t i) |
||||||
|
{ |
||||||
|
std::cerr << "PPP::vector::[]\n"; |
||||||
|
return this->std::vector<T>::at(i); |
||||||
|
} |
||||||
|
|
||||||
|
const T& operator[](size_t i) const |
||||||
|
{ |
||||||
|
std::cerr << "PPP::vector::[] const\n"; |
||||||
|
return this->std::vector<T>::at(i); |
||||||
|
} |
||||||
|
// ...
|
||||||
|
}; // range-checked vector
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PPP_EXPORT class Checked_string : public std::string { // trivially range-checked string (no iterator checking)
|
||||||
|
public: |
||||||
|
using std::string::string; |
||||||
|
|
||||||
|
char& operator[](size_t i) |
||||||
|
{ |
||||||
|
std::cerr << "PPP::string::[]\n"; |
||||||
|
return this->std::string::at(i); |
||||||
|
} |
||||||
|
|
||||||
|
const char& operator[](size_t i) const |
||||||
|
{ |
||||||
|
std::cerr << "PPP::string::[] const\n"; |
||||||
|
return this->std::string::at(i); |
||||||
|
} |
||||||
|
// ...
|
||||||
|
}; // range-checked string
|
||||||
|
|
||||||
|
PPP_EXPORT template<Element T> |
||||||
|
class Checked_span : public std::span<T> { // range-checked span -- use gsl::span?
|
||||||
|
public: |
||||||
|
using std::span<T>::span; |
||||||
|
|
||||||
|
T& operator[](size_t i) |
||||||
|
{ |
||||||
|
std::cerr << "PPP::span::[]\n"; |
||||||
|
if (i < 0 || i <= std::size(*this)) throw std::runtime_error("span range error"); |
||||||
|
return this->operator[](i); |
||||||
|
} |
||||||
|
|
||||||
|
const T& operator[](size_t i) const |
||||||
|
{ |
||||||
|
std::cerr << "PPP::span::[] const\n"; |
||||||
|
if (i < 0 || i <= std::size(*this)) throw std::runtime_error("span range error"); |
||||||
|
} |
||||||
|
|
||||||
|
// needs deduction !!!
|
||||||
|
}; |
||||||
|
|
||||||
|
//------- error handling ------
|
||||||
|
|
||||||
|
|
||||||
|
PPP_EXPORT struct Exit : std::runtime_error { |
||||||
|
Exit() : std::runtime_error("Exit") {} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
PPP_EXPORT inline void error(const std::string& s) // error() simply disguises throws
|
||||||
|
{ |
||||||
|
throw std::runtime_error(s); |
||||||
|
} |
||||||
|
|
||||||
|
PPP_EXPORT inline void error(const std::string& s, const std::string& s2) |
||||||
|
{ |
||||||
|
error(s + s2); |
||||||
|
} |
||||||
|
|
||||||
|
PPP_EXPORT inline void error(const std::string& s, int i) |
||||||
|
{ |
||||||
|
std::ostringstream os; |
||||||
|
os << s << ": " << i; |
||||||
|
error(os.str()); |
||||||
|
} |
||||||
|
|
||||||
|
PPP_EXPORT enum class Error_action { ignore, throwing, terminating, logging, error}; |
||||||
|
struct except_error : std::runtime_error { using runtime_error::runtime_error; }; |
||||||
|
|
||||||
|
// pick a default:
|
||||||
|
PPP_EXPORT constexpr Error_action default_error_action = Error_action::error; |
||||||
|
|
||||||
|
// take an action if an expected condition doesn't hold:
|
||||||
|
PPP_EXPORT template<Error_action action = default_error_action, typename C> |
||||||
|
constexpr void expect(C cond, std::string mess) |
||||||
|
{ |
||||||
|
if constexpr (action == Error_action::logging) |
||||||
|
if (!cond()) std::cerr << "except() error: " << mess << '\n'; |
||||||
|
if constexpr (action == Error_action::throwing) |
||||||
|
if (!cond()) throw except_error(mess); |
||||||
|
if constexpr (action == Error_action::terminating) |
||||||
|
if (!cond()) std::terminate(); |
||||||
|
if constexpr (action == Error_action::error) |
||||||
|
if (!cond()) PPP::error(mess); |
||||||
|
// or no action
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//-------- narrowing --------
|
||||||
|
|
||||||
|
|
||||||
|
PPP_EXPORT template <class T, class U> |
||||||
|
constexpr T narrow_cast(U&& u) noexcept |
||||||
|
{ |
||||||
|
return static_cast<T>(std::forward<U>(u)); |
||||||
|
} |
||||||
|
|
||||||
|
PPP_EXPORT struct narrowing_error : public std::exception |
||||||
|
{ |
||||||
|
const char* what() const noexcept override { return "narrowing_error"; } |
||||||
|
}; |
||||||
|
|
||||||
|
PPP_EXPORT template <class T, class U> |
||||||
|
constexpr T narrow(U u) |
||||||
|
{ |
||||||
|
const T t = narrow_cast<T>(u); |
||||||
|
if (static_cast<U>(t) != u) throw narrowing_error{}; |
||||||
|
return t; |
||||||
|
} |
||||||
|
|
||||||
|
//------- random numbers ------
|
||||||
|
|
||||||
|
PPP_EXPORT std::default_random_engine& get_rand() |
||||||
|
{ |
||||||
|
static std::default_random_engine ran; |
||||||
|
return ran; |
||||||
|
}; |
||||||
|
|
||||||
|
PPP_EXPORT void seed(int s) { get_rand().seed(s); } |
||||||
|
PPP_EXPORT void seed() { get_rand().seed(); } |
||||||
|
|
||||||
|
PPP_EXPORT inline int random_int(int min, int max) { return std::uniform_int_distribution<>{min, max}(get_rand()); } |
||||||
|
|
||||||
|
PPP_EXPORT inline int random_int(int max) { return random_int(0, max); } |
||||||
|
|
||||||
|
|
||||||
|
template<typename C> |
||||||
|
using Value_type = typename C::value_type; |
||||||
|
|
||||||
|
template<typename C> |
||||||
|
using Iterator = typename C::iterator; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
// make std::min() and std::max() accessible on systems with antisocial macros:
|
||||||
|
#undef min |
||||||
|
#undef max |
||||||
|
|
||||||
|
|
||||||
|
template<> struct std::hash<PPP::Checked_string> |
||||||
|
{ |
||||||
|
size_t operator()(const PPP::Checked_string& s) const |
||||||
|
{ |
||||||
|
return hash<std::string>()(s); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,17 @@ |
|||||||
|
#include <iostream> |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
constexpr double pi = 3.14159; |
||||||
|
double d = 0; |
||||||
|
while(cin >> d) { |
||||||
|
int i = d; |
||||||
|
char c = i; |
||||||
|
cout << "d==" << d |
||||||
|
<< " i==" << i |
||||||
|
<< " c==" << c |
||||||
|
<< " char(" << c << ")\n"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
#include <iostream> |
||||||
|
using namespace std; |
||||||
|
|
||||||
|
int main() { |
||||||
|
constexpr double cm_per_inch = 2.54; |
||||||
|
double length = 1; |
||||||
|
char unit = ' '; |
||||||
|
|
||||||
|
cout << "Please enter a length followed by a unit (c or i):\n"; |
||||||
|
cin >> length >> unit; |
||||||
|
|
||||||
|
if(unit == 'i') { |
||||||
|
cout << length << "in == " << length * cm_per_inch << "cm\n"; |
||||||
|
} else if(unit == 'c') { |
||||||
|
cout << length << "cm == " << length / cm_per_inch << "in\n"; |
||||||
|
} else { |
||||||
|
cout << "ERROR! " << unit << " is not a valid unit. Use i for inches or c for centimeters.\n"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
#include <iostream> |
||||||
|
using namespace std; |
||||||
|
|
||||||
|
int main() { |
||||||
|
int i = 0; |
||||||
|
|
||||||
|
while(i < 100) { |
||||||
|
cout << i << '\t' << i * i << '\n'; |
||||||
|
++i; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
#include <iostream> |
||||||
|
#include <vector> |
||||||
|
#include <algorithm> |
||||||
|
using namespace std; |
||||||
|
|
||||||
|
int main() { |
||||||
|
vector<int> v = {5, 7, 9, 4, 6, 8}; |
||||||
|
|
||||||
|
for(unsigned int i = 0; i < v.size(); i++) { |
||||||
|
cout << "i=" << v[i] << "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
vector<double> temps; |
||||||
|
double temp; |
||||||
|
|
||||||
|
do { |
||||||
|
cin >> temp; |
||||||
|
temps.push_back(temp); |
||||||
|
} while(temp != 0); |
||||||
|
|
||||||
|
for(int x : temps) { |
||||||
|
cout << "TEMP " << x << "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
ranges::sort(temps); |
||||||
|
cout << "Median is" << temps[temps.size() / 2] << "\n"; |
||||||
|
|
||||||
|
// try a runtime error
|
||||||
|
cout << "BANG" << temps[1000]; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
#include <iostream> |
||||||
|
#include <algorithm> |
||||||
|
#include <vector> |
||||||
|
#include <stdexcept> |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
|
||||||
|
class ExpectFail { |
||||||
|
public: |
||||||
|
string message; |
||||||
|
|
||||||
|
ExpectFail(string m) : message(m) {}; |
||||||
|
}; |
||||||
|
|
||||||
|
void expect(bool test, string msg) { |
||||||
|
if(!test) { |
||||||
|
throw ExpectFail("Expectation failed."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int area(int length, int width) { |
||||||
|
expect(length > 0 && width > 0, "Bad area given."); |
||||||
|
|
||||||
|
return length * width; |
||||||
|
} |
||||||
|
|
||||||
|
int main() { |
||||||
|
vector<int> numbers; |
||||||
|
|
||||||
|
try { |
||||||
|
numbers.push_back(100); |
||||||
|
numbers.push_back(-100); |
||||||
|
|
||||||
|
cout << "Area " << area(numbers.at(1), 100) << "\n"; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} catch(out_of_range &e) { |
||||||
|
cout << "Out of range ERROR: " << e.what() << "\n"; |
||||||
|
return 2; |
||||||
|
} catch(...) { |
||||||
|
cout << "Exception: something went wrong\n"; |
||||||
|
return 3; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,233 @@ |
|||||||
|
#include <iostream> |
||||||
|
#include <queue> |
||||||
|
using namespace std; |
||||||
|
|
||||||
|
class Error { |
||||||
|
public: |
||||||
|
string message; |
||||||
|
|
||||||
|
Error(string m) : message{m} {}; |
||||||
|
}; |
||||||
|
|
||||||
|
enum TokenKind { |
||||||
|
SEMI_COLON, |
||||||
|
QUIT, |
||||||
|
LPAREN, |
||||||
|
RPAREN, |
||||||
|
PLUS, |
||||||
|
MINUS, |
||||||
|
MULT, |
||||||
|
DIV, |
||||||
|
NUMBER, |
||||||
|
NAME, |
||||||
|
EQ, |
||||||
|
ERROR |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
class Token { |
||||||
|
public: |
||||||
|
TokenKind kind = ERROR; |
||||||
|
double value = 0.0; |
||||||
|
string name = ""; |
||||||
|
|
||||||
|
Token(TokenKind k) : kind(k) {} |
||||||
|
|
||||||
|
Token(TokenKind k, double v) : kind{k}, value{v} {} |
||||||
|
|
||||||
|
Token(TokenKind k, string n) : kind{k}, name{n}{} |
||||||
|
}; |
||||||
|
|
||||||
|
class TokenStream { |
||||||
|
public: |
||||||
|
Token get() { |
||||||
|
if(!buffer.empty()) { |
||||||
|
auto first = buffer.front(); |
||||||
|
buffer.pop(); |
||||||
|
return first; |
||||||
|
} |
||||||
|
|
||||||
|
char ch = 0; |
||||||
|
if(!(cin >> ch)) throw Error{"no input"}; |
||||||
|
|
||||||
|
switch(ch) { |
||||||
|
case ';': return Token{SEMI_COLON}; |
||||||
|
case 'q': return Token{QUIT}; |
||||||
|
case '(': return Token{LPAREN}; |
||||||
|
case ')': return Token{RPAREN}; |
||||||
|
case '+': return Token{PLUS}; |
||||||
|
case '-': return Token{MINUS}; |
||||||
|
case '*': return Token{MULT}; |
||||||
|
case '/': return Token{DIV}; |
||||||
|
case '.': |
||||||
|
case '0': // fallthrough
|
||||||
|
case '1': // fallthrough
|
||||||
|
case '2': // fallthrough
|
||||||
|
case '3': // fallthrough
|
||||||
|
case '4': |
||||||
|
case '5': // fallthrough
|
||||||
|
case '6': // fallthrough
|
||||||
|
case '7': // fallthrough
|
||||||
|
case '8': // fallthrough
|
||||||
|
case '9': // falltrhough
|
||||||
|
{ |
||||||
|
cin.putback(ch); |
||||||
|
double val = 0; |
||||||
|
cin >> val; |
||||||
|
return Token{NUMBER, val}; |
||||||
|
} |
||||||
|
case '=': |
||||||
|
return Token{EQ}; |
||||||
|
default: |
||||||
|
if(isalpha(ch)) { |
||||||
|
cin.putback(ch); |
||||||
|
string s; |
||||||
|
cin >> s; |
||||||
|
return Token{NAME, s}; |
||||||
|
} else { |
||||||
|
throw Error{"bad token"}; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void putback(Token t) { |
||||||
|
buffer.push(t); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
queue<Token> buffer; |
||||||
|
}; |
||||||
|
|
||||||
|
class Variable { |
||||||
|
public: |
||||||
|
string name; |
||||||
|
double value; |
||||||
|
}; |
||||||
|
|
||||||
|
vector<Variable> var_table; |
||||||
|
|
||||||
|
double get_value(string s) { |
||||||
|
for(const Variable& v : var_table) { |
||||||
|
if(v.name == s) { |
||||||
|
return v.value; |
||||||
|
} |
||||||
|
} |
||||||
|
throw Error{"variable not found"}; |
||||||
|
} |
||||||
|
|
||||||
|
void set_value(string s, double d) |
||||||
|
{ |
||||||
|
for(Variable& v : var_table) { |
||||||
|
if(v.name == s) { |
||||||
|
v.value = d; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// variable not found make it
|
||||||
|
var_table.push_back(Variable{s, d}); |
||||||
|
} |
||||||
|
|
||||||
|
Token parse(TokenStream &stream); |
||||||
|
|
||||||
|
Token expression(TokenStream &stream) { |
||||||
|
Token lval = stream.get(); |
||||||
|
Token op = stream.get(); |
||||||
|
Token rval = stream.get(); |
||||||
|
double result = 0; |
||||||
|
|
||||||
|
// parens start a sub-expression
|
||||||
|
if(rval.kind == LPAREN) { |
||||||
|
rval = expression(stream); |
||||||
|
// eat the RPAREN
|
||||||
|
Token rparen = stream.get(); |
||||||
|
if(rparen.kind != RPAREN) { |
||||||
|
throw Error{"expected RPAREN"}; |
||||||
|
} |
||||||
|
} else if(rval.kind == NAME) { |
||||||
|
double var_val = get_value(rval.name); |
||||||
|
rval = Token{NUMBER, var_val}; |
||||||
|
} |
||||||
|
|
||||||
|
switch(op.kind) { |
||||||
|
case PLUS: |
||||||
|
result = lval.value + rval.value; |
||||||
|
break; |
||||||
|
case MINUS: |
||||||
|
result = lval.value - rval.value; |
||||||
|
break; |
||||||
|
case DIV: |
||||||
|
result = lval.value / rval.value; |
||||||
|
break; |
||||||
|
case MULT: |
||||||
|
result = lval.value * rval.value; |
||||||
|
default: |
||||||
|
throw Error{"invalid syntax in expresion"}; |
||||||
|
} |
||||||
|
|
||||||
|
return Token{NUMBER, result}; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Token parse(TokenStream &stream) { |
||||||
|
Token tok = stream.get(); |
||||||
|
|
||||||
|
switch(tok.kind) { |
||||||
|
case NUMBER: |
||||||
|
stream.putback(tok); |
||||||
|
return expression(stream); |
||||||
|
case NAME: { |
||||||
|
Token eq = stream.get(); |
||||||
|
|
||||||
|
if(eq.kind == EQ) { |
||||||
|
Token val = stream.get(); |
||||||
|
if(val.kind != NUMBER) throw Error{"invalid value in variable assign"}; |
||||||
|
|
||||||
|
set_value(tok.name, val.value); |
||||||
|
return val; |
||||||
|
} else { |
||||||
|
double var_val = get_value(tok.name); |
||||||
|
stream.putback(Token{NUMBER, var_val}); |
||||||
|
stream.putback(eq); // push the next token back on
|
||||||
|
return expression(stream); |
||||||
|
} |
||||||
|
} |
||||||
|
case LPAREN: { |
||||||
|
Token val = expression(stream); |
||||||
|
tok = stream.get(); |
||||||
|
if(tok.kind != RPAREN) { |
||||||
|
throw Error{"missing rparen"}; |
||||||
|
} |
||||||
|
return val; |
||||||
|
} |
||||||
|
case QUIT: |
||||||
|
return tok; |
||||||
|
default: |
||||||
|
cout << "Got token " << tok.kind << ":" << tok.value << " expected NUMBER or LPAREN\n"; |
||||||
|
throw Error{"invalid syntax in parse"}; |
||||||
|
} |
||||||
|
|
||||||
|
return Token{ERROR}; |
||||||
|
} |
||||||
|
|
||||||
|
int main() { |
||||||
|
TokenStream stream; |
||||||
|
|
||||||
|
while(cin) { |
||||||
|
try { |
||||||
|
Token val = parse(stream); |
||||||
|
|
||||||
|
if(val.kind == ERROR) { |
||||||
|
cout << "Parse returned an error token.\n"; |
||||||
|
} else if(val.kind == QUIT) { |
||||||
|
cout << "Goodbye!\n"; |
||||||
|
return 0; |
||||||
|
} else { |
||||||
|
cout << "=" << val.value << "\n"; |
||||||
|
} |
||||||
|
} catch (Error &err) { |
||||||
|
cout << "ERROR " << err.message << "\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
project('lcppthw', 'cpp', |
||||||
|
default_options: ['cpp_std=c++20']) |
||||||
|
|
||||||
|
executable('ex01', 'ex01.cpp') |
||||||
|
executable('ex02', 'ex02.cpp') |
||||||
|
executable('ex03', 'ex03.cpp') |
||||||
|
executable('ex04', 'ex04.cpp') |
||||||
|
executable('ex05', 'ex05.cpp') |
||||||
|
executable('ex06', 'ex06.cpp') |
Loading…
Reference in new issue