C ++ 11
Готово наконец. И это заняло у меня большую часть дня.
Прежде чем я перечислю код и пример вывода, несколько быстрых заметок:
Вещи, которые поддерживает эта программа
- Любое количество терминов, каждое с любым количеством входов
- Повторное использование входов
- Пропуск представления логического элемента И / ИЛИ при наличии только одного входа
- Используйте как кодовую страницу 437 символов рисования линий, так и 7-битные ASCII-дружественные символы (используйте -DCP437 или любой другой эквивалентный код вашего компилятора для поддержки кодовой страницы 437)
Вещи, которые не поддерживает эта программа
- Группировка с круглыми скобками (потому что, серьезно?)
- Несколько НОТ
- Любой пробел, который не является простым пространством ASCII
- Правильная игра в гольф (хотя я удалил все комментарии по духу)
- Вменяемые практики кодирования
- Зрительный контакт с кодом ввода
- Переносимые символы Unicode для рисования линий. Я не мог поддерживать Unicode, потому что логика рендеринга основана на возможности рисовать «вниз» двухмерный
char
буфер.
Код
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>
#include <stdexcept>
#include <set>
#include <map>
#include <valarray>
#include <cmath>
using namespace std;
vector<string> split(const string str, const string delim)
{
vector<string> v;
set<char> d;
for (char c : delim) d.insert(c);
string current {};
for (char c : str)
{
if (d.find(c) != d.end())
{
v.push_back(current);
current = "";
}
else
{
current += c;
}
}
v.push_back(current);
return v;
}
using Input = string;
struct TermInput
{
Input name;
int ypos;
bool neg;
};
using Term = vector<TermInput>;
using Expr = vector<Term>;
#ifdef CP437
constexpr char VERT = '\xB3';
constexpr char HORIZ = '\xC4';
constexpr char RBRANCH = '\xC3';
constexpr char LUP = '\xD9';
constexpr char LDOWN = '\xBF';
constexpr char RUP = '\xC0';
constexpr char RDOWN = '\xDA';
#else
constexpr char VERT = '|';
constexpr char HORIZ = '-';
constexpr char RBRANCH = '}';
constexpr char LUP = '\'';
constexpr char LDOWN = '.';
constexpr char RUP = '`';
constexpr char RDOWN = ',';
#endif
string repeat(string s, int n)
{
stringstream ss;
for (int i = 0; i < n; i++)
{
ss << s;
}
return ss.str();
}
string repeat(char c, int n)
{
stringstream ss;
for (int i = 0; i < n; i++)
{
ss << c;
}
return ss.str();
}
enum class Direction
{
RIGHT,
DOWN
};
void matrixPut(char *matrix, int h, int w, int x, int y, const string s, Direction dir)
{
if (x >= w || y >= h)
{
stringstream ss;
ss << "matrixPut: point (" << x << ", " << y << ") out of matrix bounds!";
throw new out_of_range(ss.str());
}
char *ins = matrix + (y * w) + x;
char *end = matrix + (h * w);
for (char c : s)
{
if (ins >= end) break;
*ins = c;
if (dir==Direction::RIGHT)
{
ins++;
}
else
{
ins += w;
}
}
}
void matrixPrint(char *matrix, int h, int w)
{
for (int i = 0; i < (h * w); i++)
{
cout << matrix[i];
if ((i+1)%w == 0) cout << endl;
}
}
int main (int argc, char *argv[])
{
string expression;
if (argc == 1)
{
cout << "Enter expression:" << endl;
getline(cin, expression);
}
else
{
expression = argv[1];
}
expression.erase(remove(expression.begin(), expression.end(), ' '), expression.end());
Expr expr {};
auto inputs = set<Input>();
auto termInputs = multimap<Input, TermInput>();
int inputYPos = 0;
auto e = split(expression, "+");
for (string term : e)
{
Term currTerm {};
auto t = split(term, "*");
for (string in : t)
{
bool neg = false;
if (in.back() == '\'')
{
in.pop_back();
neg = true;
}
Input currInput {in};
inputs.insert(currInput);
TermInput ti {currInput, inputYPos, neg};
currTerm.push_back(ti);
termInputs.insert(pair<Input, TermInput>{in, ti});
inputYPos++;
}
expr.push_back(currTerm);
inputYPos++;
}
int height = inputs.size() + termInputs.size() + expr.size() - 1;
int width = 0;
width += max_element(inputs.begin(), inputs.end(), [](Input a, Input b){ return a.length() < b.length(); })->length() + 2;
int inputWidth = width;
width += inputs.size()*2;
int notAndStart = width;
width += 7;
width += expr.size()+1;
int orStart = width;
width += 4;
width += 4;
width += expression.length();
char matrix[height * width];
for (int i = 0; i < height*width; i++) matrix[i] = ' ';
int baseY = inputs.size();
{
int x = width - 4 - expression.length();
int y = baseY + ((height-baseY) / 2);
matrixPut(matrix, height, width, x, y, repeat(HORIZ, 4) + expression, Direction::RIGHT);
}
{
int x = orStart;
int y = baseY + (height-baseY)/2 - expr.size()/2;
if (expr.size() == 1)
{
matrixPut(matrix, height, width, x, y, repeat(HORIZ, 4), Direction::RIGHT);
}
else {
matrixPut(matrix, height, width, x , y, repeat(HORIZ, expr.size()), Direction::DOWN);
matrixPut(matrix, height, width, x+1, y, repeat(VERT , expr.size()), Direction::DOWN);
matrixPut(matrix, height, width, x+2, y, repeat('O' , expr.size()), Direction::DOWN);
matrixPut(matrix, height, width, x+3, y, repeat('R' , expr.size()), Direction::DOWN);
}
}
{
int x = notAndStart;
int y = baseY;
for (Term t : expr)
{
string layer[7];
for (TermInput ti : t)
{
layer[0] += HORIZ;
layer[1] += (ti.neg ? '>' : HORIZ);
layer[2] += (ti.neg ? 'o' : HORIZ);
layer[3] += HORIZ;
layer[4] += (t.size() > 1 ? VERT : HORIZ);
layer[5] += (t.size() > 1 ? '&' : HORIZ);
layer[6] += (t.size() > 1 ? '&' : HORIZ);
}
for (int i = 0; i < 7; i++)
{
matrixPut(matrix, height, width, x+i, y, layer[i], Direction::DOWN);
}
y += t.size() + 1;
}
}
{
int x = 0;
int y = 0;
for (Input i : inputs)
{
string str = i + ": ";
str += repeat(HORIZ, inputWidth - str.length());
matrixPut(matrix, height, width, x, y, str, Direction::RIGHT);
y++;
}
}
{
int x = inputWidth;
int num = 0;
int offset = inputs.size() * 2 - 1;
for (Input in : inputs)
{
int y = 0;
int len = inputs.size() * 2 - 1;
auto it = inputs.find(in);
while (it != inputs.begin())
{
it--;
y++;
len -= 2;
}
matrixPut(matrix, height, width, x, y, repeat(HORIZ, len) + LDOWN, Direction::RIGHT);
}
for (Input in : inputs)
{
auto yBreakRange = termInputs.equal_range(in);
valarray<int> yBreaks(termInputs.count(in));
int idx = 0;
for (auto ti = yBreakRange.first; ti != yBreakRange.second; ti++)
{
yBreaks[idx++] = baseY + ti->second.ypos;
}
for (int y : yBreaks)
{
matrixPut(matrix, height, width, x+offset, y, repeat(HORIZ, inputs.size()*2 - offset), Direction::RIGHT);
}
int y = num + 1;
int maxBreak = yBreaks.max();
string branch = repeat(VERT, maxBreak - y + 1);
for (int i : yBreaks)
{
branch[i-y] = RBRANCH;
}
branch.back() = RUP;
matrixPut(matrix, height, width, x+offset, y, branch, Direction::DOWN);
offset -= 2;
num++;
}
}
{
int x = notAndStart + 7;
int outY = baseY + (height-baseY)/2 - expr.size()/2;
int breakx = expr.size()/2;
for (Term t : expr)
{
int inY = baseY + (t.front().ypos + t.back().ypos) / 2;
matrixPut(matrix, height, width, x, inY, repeat(HORIZ, abs(breakx)+1), Direction::RIGHT);
matrixPut(matrix, height, width, x+abs(breakx)+1, outY, repeat(HORIZ, expr.size()-abs(breakx)), Direction::RIGHT);
if (inY < outY)
{
string branch = LDOWN + repeat(VERT, outY-inY-1) + RUP;
matrixPut(matrix, height, width, x+abs(breakx)+1, inY, branch, Direction::DOWN);
}
else if (inY > outY)
{
string branch = RDOWN + repeat(VERT, inY-outY-1) + LUP;
matrixPut(matrix, height, width, x+abs(breakx)+1, outY, branch, Direction::DOWN);
}
outY++;
breakx--;
}
}
cout << endl;
matrixPrint(matrix, height, width);
cout << endl;
}
Образец вывода
$ ./gates "A'*B'+B*C"
A: -----.
B: ---. |
C: -. | |
| | `->o-|&&--.
| }--->o-|&& `-|OR
| | ,--|OR----A'*B'+B*C
| `------|&&-'
`--------|&&
$ ./gates "A*B'*C+B*D'+F*C'*D+G*E*A'+F"
A: -------------.
B: -----------. |
C: ---------. | |
D: -------. | | |
E: -----. | | | |
F: ---. | | | | |
G: -. | | | | | |
| | | | | | }----|&&
| | | | | }--->o-|&&---.
| | | | }--------|&& |
| | | | | | | |
| | | | | `------|&&--.|
| | | }------->o-|&& ||
| | | | | | |`---|OR
| }--------------|&& `----|OR
| | | | `----->o-|&&-------|OR----A*B'*C+B*D'+F*C'*D+G*E*A'+F
| | | `----------|&& ,----|OR
| | | | |,---|OR
`----------------|&& ||
| `------------|&&--'|
| `->o-|&& |
| |
`--------------------'
Пример вывода (с включенным CP437)
A: ─────────────┐
B: ───────────┐ │
C: ─────────┐ │ │
D: ───────┐ │ │ │
E: ─────┐ │ │ │ │
F: ───┐ │ │ │ │ │
G: ─┐ │ │ │ │ │ │
│ │ │ │ │ │ ├────│&&
│ │ │ │ │ ├───>o─│&&───┐
│ │ │ │ ├────────│&& │
│ │ │ │ │ │ │ │
│ │ │ │ │ └──────│&&──┐│
│ │ │ ├───────>o─│&& ││
│ │ │ │ │ │ │└───│OR
│ ├──────────────│&& └────│OR
│ │ │ │ └─────>o─│&&───────│OR────A*B'*C+B*D'+F*C'*D+G*E*A'+F
│ │ │ └──────────│&& ┌────│OR
│ │ │ │ │┌───│OR
└────────────────│&& ││
│ └────────────│&&──┘│
│ └─>o─│&& │
│ │
└────────────────────┘
─ │ ┌ ┐ └ ┘
. Я думаю, что вы имеете в виду набор символов MS-DOS . Кроме того, разве в вашем примере не должно быть одногоB
входного разбиения между двумя элементами И? И можем ли мы предположить, что не будет необходимости где-либо пересекать провода?