Playing with a comparison of python vs. c++ for something simple. stats.py reads lines on stdin and outputs some json, and goc.cpp reads stdin and outputs csv. I may just go with csv for the project but I'll see what json is like in C++ too.

master
Zed A. Shaw 7 months ago
parent 6a777e4c2d
commit 567ffec4ac
  1. 6
      PPP3/Makefile
  2. 1
      PPP3/README.md
  3. 238
      PPP3/ex07.cpp
  4. 8
      PPP3/ex08.cpp
  5. 31
      PPP3/goc.cpp
  6. 9
      PPP3/meson.build
  7. 21
      PPP3/stats.py

@ -0,0 +1,6 @@
all:
meson compile -C .
test:
@echo "../ex08.cpp:7:10: error: use of undeclared identifier \'oops\'" | ../builddir/goc

@ -38,3 +38,4 @@ This is dumb as hell but it's working for now. If you have a fancy way to tell V
5. **Disorganized presentation of the core concepts.** The book is all over the place. First you cover objects, types, and values, but this doesn't cover objects it covers base types like `int`, then it goes into computation but nothing about classes and struct, then he gets into error reporting with a reporting method that doesn't work without the `PPP.h` file you can't use, but then gets into writing a calculator that uses `class` but no explanation of that until Chapters 7 and 8 which finally explains all the things you needed to work on the calculator. It's understandable to not cover things until you can explain them later, since programming is so complicated you do have to gloss over concepts until later. But, Chapters 7 and 8 should have probably replaced the majority of the content in chapters 1-4 before he gets into making code with the concepts.
That's my critique for far. Basically, the book is your classic code book where the author has absolutely no idea who he's writing the book for, and just assumes that everyone who reads it has his brain and his computer. He most likely has a whole directory full of the code in the book but you and I will never see it. It's also _definitely_, _DEFINITELY_ not a beginner book. I couldn't imagine someone who's never written code trying to setup MinGW with Meson on a Windows machine or even getting Visual C++ to work, let alone figure out his mishmash of `PPP_*.h` files.
6. **Constantly using things that don't exist.** The book is riddled with things that are claimed to be supported or standard, but that isn't available in many compilers. A great example is `std::format`, which would be great since I planned on having people use the excellent `fmt` library, but none of the compilers I have had this. He does this all the time where he just assumes something exists but it doesn't, and what's even more irritating about this is it's not hard at all to test. There's only like 4 compilers to verify. A single test suite could do it, but nope, I'm forced to research it myself.

@ -0,0 +1,238 @@
/*
* Same code as ex06.cpp but I use auto everywhere I could to
* see how it works.
*/
#include <iostream>
#include <queue>
using namespace std;
class Error {
public:
string message;
Error(string m) : message{m} {};
};
enum class TokenKind {
SEMI_COLON,
QUIT,
LPAREN,
RPAREN,
PLUS,
MINUS,
MULT,
DIV,
NUMBER,
NAME,
EQ,
ERROR
};
class Token {
public:
TokenKind kind = TokenKind::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:
auto 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{TokenKind::SEMI_COLON};
case 'q': return Token{TokenKind::QUIT};
case '(': return Token{TokenKind::LPAREN};
case ')': return Token{TokenKind::RPAREN};
case '+': return Token{TokenKind::PLUS};
case '-': return Token{TokenKind::MINUS};
case '*': return Token{TokenKind::MULT};
case '/': return Token{TokenKind::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);
auto val = 0.0;
cin >> val;
return Token{TokenKind::NUMBER, val};
}
case '=':
return Token{TokenKind::EQ};
default:
if(isalpha(ch)) {
cin.putback(ch);
string s;
cin >> s;
return Token{TokenKind::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) {
auto lval = stream.get();
auto op = stream.get();
auto rval = stream.get();
double result = 0;
// parens start a sub-expression
if(rval.kind == TokenKind::LPAREN) {
rval = expression(stream);
// eat the RPAREN
auto rparen = stream.get();
if(rparen.kind != TokenKind::RPAREN) {
throw Error{"expected RPAREN"};
}
} else if(rval.kind == TokenKind::NAME) {
auto var_val = get_value(rval.name);
rval = Token{TokenKind::NUMBER, var_val};
}
switch(op.kind) {
case TokenKind::PLUS:
result = lval.value + rval.value;
break;
case TokenKind::MINUS:
result = lval.value - rval.value;
break;
case TokenKind::DIV:
result = lval.value / rval.value;
break;
case TokenKind::MULT:
result = lval.value * rval.value;
default:
throw Error{"invalid syntax in expresion"};
}
return Token{TokenKind::NUMBER, result};
}
Token parse(TokenStream &stream) {
auto tok = stream.get();
switch(tok.kind) {
case TokenKind::NUMBER:
stream.putback(tok);
return expression(stream);
case TokenKind::NAME: {
auto eq = stream.get();
if(eq.kind == TokenKind::EQ) {
auto val = stream.get();
if(val.kind != TokenKind::NUMBER) throw Error{"invalid value in variable assign"};
set_value(tok.name, val.value);
return val;
} else {
auto var_val = get_value(tok.name);
stream.putback(Token{TokenKind::NUMBER, var_val});
stream.putback(eq); // push the next token back on
return expression(stream);
}
}
case TokenKind::LPAREN: {
auto val = expression(stream);
tok = stream.get();
if(tok.kind != TokenKind::RPAREN) {
throw Error{"missing rparen"};
}
return val;
}
case TokenKind::QUIT:
return tok;
default:
cout << "Got token " << ":" << tok.value << " expected NUMBER or LPAREN\n";
throw Error{"invalid syntax in parse"};
}
return Token{TokenKind::ERROR};
}
int main() {
TokenStream stream;
while(cin) {
try {
auto val = parse(stream);
if(val.kind == TokenKind::ERROR) {
cout << "Parse returned an error token.\n";
} else if(val.kind == TokenKind::QUIT) {
cout << "Goodbye!\n";
return 0;
} else {
cout << "=" << val.value << "\n";
}
} catch (Error &err) {
cout << "ERROR " << err.message << "\n";
}
}
return 0;
}

@ -0,0 +1,8 @@
#include <iostream>
using namespace std;
int main()
{
return 0;
}

@ -0,0 +1,31 @@
#include <iostream>
#include <fmt/core.h>
#include <regex>
#include <string>
#include <iterator>
using namespace std;
using namespace fmt;
int main()
{
regex err_re("(.*?):([0-9]+):([0-9]+):\\s*(.*?):\\s*(.*)");
string line;
smatch err_match;
while(getline(cin, line)) {
print("{}\n", line);
if(regex_match(line, err_match, err_re)) {
string file_name = err_match[1].str();
string line = err_match[2].str();
string col = err_match[3].str();
string type = err_match[4].str();
string message = err_match[5].str();
print("{},{},{},{},{}\n",
file_name, line, col, type, message);
}
}
return 0;
}

@ -1,9 +1,18 @@
project('lcppthw', 'cpp',
default_options: ['cpp_std=c++20'])
fmt_dep = dependency('fmt')
executable('goc', 'goc.cpp',
win_subsystem: 'windows',
cpp_args: '-DFMT_HEADER_ONLY',
dependencies: fmt_dep)
executable('ex01', 'ex01.cpp')
executable('ex02', 'ex02.cpp')
executable('ex03', 'ex03.cpp')
executable('ex04', 'ex04.cpp')
executable('ex05', 'ex05.cpp')
executable('ex06', 'ex06.cpp')
executable('ex07', 'ex07.cpp')
executable('ex08', 'ex08.cpp')

@ -0,0 +1,21 @@
import sys
import re
import json
from datetime import datetime
err_re = re.compile("(?P<file>.*?):(?P<line>[0-9]+):(?P<col>[0-9]+): (?P<type>.*?): (?P<message>.*)\n")
stats = [];
for line in sys.stdin:
found = err_re.fullmatch(line)
print(line, end="")
if found:
stats.append(found.groupdict())
print("FOUND", found.groupdict())
with open("stats.json", "a+") as out:
out.write(json.dumps({"date": datetime.now().isoformat(),
"messages": stats}));
out.write("\n")
Loading…
Cancel
Save