This commit is contained in:
anon
2024-01-23 21:29:06 +01:00
commit e64a072634
13 changed files with 683 additions and 0 deletions

31
source/Automaton.pro Normal file
View File

@ -0,0 +1,31 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
automaton.cpp \
main.cpp \
automaton_gui.cpp
HEADERS += \
automaton.hpp \
automaton_gui.h \
util.h
FORMS += \
automaton_gui.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
DISTFILES += \
small.inc \
table.inc

78
source/automaton.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "automaton.hpp"
#include <stdlib.h>
#include <string.h>
using namespace std;
std::string to_string(const Rule &r) {
return std::string() + "Rule{ translation: " + r.transition + ", irrel: " + r.irrel + " }";
}
namespace automaton {
const char POP = '\01';
const char ACCEPT = '\02';
stack<char> symbol_stack;
const char * input_str;
size_t ip;
string irrelevant;
void (*display_callback)(decltype(input_str),
decltype(symbol_stack),
decltype(irrelevant) ir,
const Rule &r) = NULL;
map<char, map<char, Rule>> matrix;
}
bool automaton::run() {
while (true) {
// Preoperation calculations
char n = input_str[ip];
if (symbol_stack.empty()) {
return false;
}
char m = symbol_stack.top();
Rule r("");
try {
r = matrix.at(m)
.at(n);
} catch(...) {
return false;
}
// Render to display
if (display_callback) {
display_callback(input_str + ip, symbol_stack, irrelevant, r);
}
// Act on input
symbol_stack.pop();
irrelevant += r.irrel;
if (r.transition == string("") + ACCEPT) {
return true;
} if (r.transition == string("") + POP) {
if (not input_str[ip]) {
return false;
}
ip++;
} else {
for (auto it = r.transition.rbegin(); it != r.transition.rend(); it++) {
symbol_stack.push(*it);
}
}
}
}
void automaton::reset(){
free((char *)input_str);
input_str = NULL;
symbol_stack = stack<char>({'\0', 'E'});
ip = 0;
irrelevant = "";
matrix.clear();
}

75
source/automaton.hpp Normal file
View File

@ -0,0 +1,75 @@
#ifndef AUTOMATON_HPP
#define AUTOMATON_HPP
#include <string.h>
#include <string>
#include <map>
#include <stack>
namespace automaton {
extern const char POP;
extern const char ACCEPT;
}
class Rule {
public:
std::string transition;
std::string irrel = "";
Rule(const char c) {
this->transition = strndup(&c, 1);
}
Rule(const char * const s) {
if (not strcmp(s, "ACCEPT")) {
this->transition = automaton::ACCEPT;
this->irrel = "";
return;
} else if (not strcmp(s, "POP")) {
this->transition = automaton::POP;
this->irrel = "";
return;
}
const char * split = strchr(s, ',');
if (not split) {
this->transition = s;
return;
}
int i = split - s - 1;
i < 0 ? i = 0 : 0;
this->transition = std::string(s + 1, i);
this->irrel = std::string(split + 1);
this->irrel = this->irrel.substr(0, this->irrel.size()-1);
}
explicit operator bool() const {
return not
(transition == ""
&& irrel == "");
}
};
extern std::string to_string(const Rule &r);
namespace automaton {
extern std::stack<char> symbol_stack;
extern const char * input_str;
extern size_t ip;
extern std::string irrelevant;
extern void (*display_callback)(decltype(input_str),
decltype(symbol_stack),
decltype(irrelevant) ir,
const Rule &r);
extern std::map<char, std::map<char, Rule>> matrix;
extern bool run();
extern void reset();
}
#endif

181
source/automaton_gui.cpp Normal file
View File

@ -0,0 +1,181 @@
#include "automaton.hpp"
#include "automaton_gui.h"
#include "ui_automaton_gui.h"
#include "util.h"
AutomatonGUI::AutomatonGUI(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::AutomatonGUI)
{
ui->setupUi(this);
setCentralWidget(ui->horizontalLayoutWidget);
}
AutomatonGUI::~AutomatonGUI()
{
delete ui;
}
void AutomatonGUI::matrix_from_table() {
for (int i = 0; i < ui->InputTable->rowCount(); i++) {
char n = ui->InputTable->verticalHeaderItem(i)->text().toStdString()[0];
if (n == '#') { n = '\0'; }
automaton::matrix.insert({ n, std::map<char,Rule>()});
for (int h = 0; h < ui->InputTable->columnCount(); h++) {
char m = ui->InputTable->horizontalHeaderItem(h)->text().toStdString()[0];
if (m == '#') { m = '\0'; }
auto item = ui->InputTable->item(i, h);
std::string r = (item ? item->text().toStdString() : "");
automaton::matrix[n].insert({m, Rule(r.c_str())});
}
}
}
void AutomatonGUI::on_RunButton_clicked()
{
automaton::reset();
const auto &i = ui->StatusIndicator;
automaton::input_str = strdup(ui->InputLine->text().toStdString().c_str());
if (not *automaton::input_str) {
i->setText("No input");
i->setProperty("status", "negative");
i->style()->unpolish(i);
i->style()->polish(i);
i->update();
return;
}
matrix_from_table();
bool r = automaton::run();
const char * t;
const char * s;
if (r) {
t = "Accepted";
s = "positive";
} else {
t = "Denied";
s = "negative";
}
i->setProperty("status", s);
i->setText(t);
i->style()->unpolish(i);
i->style()->polish(i);
i->update();
}
void AutomatonGUI::on_InputLine_textChanged(const QString &arg1)
{
QString r;
for (const auto &c : arg1) {
if (ui->lineEdit_2->text().contains(c)) {
r.append(c);
}
}
ui->InputLine->setText(r);
}
void AutomatonGUI::on_lineEdit_2_textChanged(const QString &arg1)
{
std::string r = uniq(arg1.toStdString());
std::string extra = ui->ExtraStates->text().toStdString();
//ui->lineEdit_2->setText("");
ui->InputLine->setText("");
column_force(r);
row_force(uniq(r + extra));
}
void AutomatonGUI::on_ExtraStates_textChanged(const QString &arg1)
{
std::string r = arg1.toStdString();
std::string base = (arg1 + ui->lineEdit_2->text()).toStdString();
row_force(uniq(r + base));
}
void AutomatonGUI::column_force(const std::string &s) {
// Delete horizontal headers
{
std::stack<int> buf;
for (int i = 0; i < ui->InputTable->columnCount(); i++) {
if (s.find(ui->InputTable->horizontalHeaderItem(i)->text().toStdString()[0]) == std::string::npos) {
buf.push(i);
}
}
while (not buf.empty()) {
ui->InputTable->removeColumn(buf.top());
buf.pop();
}
}
// Append horizontal headers
{
int offset = ui->InputTable->columnCount();
int target_len = s.size();
ui->InputTable->setColumnCount(target_len);
for (int i = 0; i < target_len; i++) {
for (int h = 0; h < offset; h++) {
auto item = ui->InputTable->horizontalHeaderItem(h);
if (not item
|| item->text().toStdString()[0] == s[i]) {
goto long_continue;
}
}
ui->InputTable->setHorizontalHeaderItem(
offset,
new QTableWidgetItem(QString(s[i]))
);
long_continue:
;
}
}
}
void AutomatonGUI::row_force(const std::string &s) {
// Delete vertical headers
{
std::stack<int> buf;
for (int i = 0; i < ui->InputTable->rowCount(); i++) {
if (s.find(ui->InputTable->verticalHeaderItem(i)->text().toStdString()[0]) == std::string::npos) {
buf.push(i);
}
}
while (not buf.empty()) {
ui->InputTable->removeRow(buf.top());
buf.pop();
}
}
// Append vertical headers
{
int offset = ui->InputTable->rowCount();
int target_len = s.size();
ui->InputTable->setRowCount(target_len);
for (int i = 0; i < target_len; i++) {
for (int h = 0; h < offset; h++) {
auto item = ui->InputTable->verticalHeaderItem(h);
if (not item
|| item->text().toStdString()[0] == s[i]) {
goto long_continue;
}
}
ui->InputTable->setVerticalHeaderItem(
offset,
new QTableWidgetItem(QString(s[i]))
);
long_continue:
;
}
}
}
void AutomatonGUI::display_state(const std::string &s) {
ui->log->append(QString::fromStdString(s));
}

38
source/automaton_gui.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef AUTOMATONGUI_H
#define AUTOMATONGUI_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class AutomatonGUI; }
QT_END_NAMESPACE
class AutomatonGUI : public QMainWindow
{
Q_OBJECT
public:
AutomatonGUI(QWidget *parent = nullptr);
~AutomatonGUI();
void display_state(const std::string &s);
private slots:
void on_RunButton_clicked();
void on_lineEdit_2_textChanged(const QString &arg1);
void on_InputLine_textChanged(const QString &arg1);
void on_ExtraStates_textChanged(const QString &arg1);
void matrix_from_table();
private:
Ui::AutomatonGUI *ui;
void column_force(const std::string &s);
void row_force(const std::string &s);
};
#endif // AUTOMATONGUI_H

144
source/automaton_gui.ui Normal file
View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AutomatonGUI</class>
<widget class="QMainWindow" name="AutomatonGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>AutomatonGUI</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>711</width>
<height>481</height>
</rect>
</property>
<layout class="QHBoxLayout" name="topLevel">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLineEdit" name="InputLine">
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>Input</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEdit_2">
<property name="placeholderText">
<string>alphabet</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="ExtraStates">
<property name="statusTip">
<string/>
</property>
<property name="whatsThis">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>extra states</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="RunButton">
<property name="text">
<string>Run</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="StatusIndicator">
<property name="styleSheet">
<string notr="true">* {
background: yellow;
font-weight: bold;
}
*[status=negative] {
background: red;
}
*[status=positive] {
background: green;
}</string>
</property>
<property name="text">
<string>N/A</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="InputTable"/>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="log">
<property name="enabled">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background: white;
color: black;</string>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: &quot;\2610&quot;; }
li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>19</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

96
source/main.cpp Normal file
View File

@ -0,0 +1,96 @@
#include "automaton.hpp"
#include "automaton_gui.h"
#include <QApplication>
using namespace std;
using namespace automaton;
AutomatonGUI * g;
void print_state(decltype(input_str) is,
decltype(symbol_stack) ss,
decltype(irrelevant) ir,
const Rule &r) {
string ss_ = "";
while (not ss.empty()) {
if (ss.top() == '\0') {
ss_ += '#';
} else {
ss_ += ss.top();
}
ss.pop();
}
char * state;
char * transition;
#if 0
asprintf(&state, "\033[33;1m('%s', '%s', '%s')\033[0m\n",
is,
ss_.c_str(),
ir.c_str()
);
asprintf(&transition, "\033[32;1m[%c,%c]\033[0m -> \033[36;1m(%s,%s)\033[0m\n",
*input_str,
symbol_stack.top(),
r.transition.c_str(),
r.irrel.c_str()
);
#endif
asprintf(&state, "('%s', '%s', '%s')",
is,
ss_.c_str(),
ir.c_str()
);
asprintf(&transition, "[%c,%c] -> (%s,%s)",
*input_str,
symbol_stack.top() ? symbol_stack.top() : '#',
r.transition.c_str(),
r.irrel.c_str()
);
fputs(state, stdout);
fputs(transition, stdout);
g->display_state(state);
g->display_state(transition);
free(state);
free(transition);
for (auto it = ss_.rbegin(); it != ss_.rend(); it++) {
if (*it == '#') {
ss.push('\0');
} else {
ss.push(*it);
}
}
}
int main(int argc, char *argv[]) {
// load
reset();
#if 0
input_str = strdup("i+i*(i+i)");
matrix =
#include "table.inc"
;
#endif
#if 0
input_str = strdup("ii");
matrix =
#include "small.inc"
;
#endif
display_callback = print_state;
// IoC
QApplication a(argc, argv);
AutomatonGUI w;
g = &w;
w.show();
return a.exec();
}

5
source/small.inc Normal file
View File

@ -0,0 +1,5 @@
{// i #
{ 'E', { {'i', Rule("(iE,1)")}, {'\0', Rule("") }}},
{ 'i', { {'i', Rule(POP) }, {'\0', Rule("") }}},
{ '\0', { {'i', Rule("") }, {'\0', Rule(ACCEPT) }}},
};

13
source/table.inc Normal file
View File

@ -0,0 +1,13 @@
{// + * ( ) i #
{ 'E', { {'+', Rule("") }, {'*', Rule("") }, {'(', Rule("(TR,1)") }, {')', Rule("") }, {'i', Rule("(TR,1)")}, {'\0', Rule("") }, }},
{ 'R', { {'+', Rule("(+TR,2)")}, {'*', Rule("") }, {'(', Rule("") }, {')', Rule("(,3)")}, {'i', Rule("") }, {'\0', Rule("(,3)"), }, }},
{ 'T', { {'+', Rule("") }, {'*', Rule("") }, {'(', Rule("(FZ,4)") }, {')', Rule("") }, {'i', Rule("(FZ,4)")}, {'\0', Rule(""), }, }},
{ 'Z', { {'+', Rule("(,6)") }, {'*', Rule("(*FZ,5)")}, {'(', Rule("") }, {')', Rule("(,6)")}, {'i', Rule("") }, {'\0', Rule("(,6)"), }, }},
{ 'F', { {'+', Rule("") }, {'*', Rule("") }, {'(', Rule("((E),7)")}, {')', Rule("") }, {'i', Rule("(i,8)") }, {'\0', Rule(""), }, }},
{ '+', { {'+', Rule(POP) }, {'*', Rule("") }, {'(', Rule("") }, {')', Rule("") }, {'i', Rule("") }, {'\0', Rule(""), }, }},
{ '*', { {'+', Rule("") }, {'*', Rule(POP) }, {'(', Rule("") }, {')', Rule("") }, {'i', Rule("") }, {'\0', Rule(""), }, }},
{ '(', { {'+', Rule("") }, {'*', Rule("") }, {'(', Rule(POP) }, {')', Rule("") }, {'i', Rule("") }, {'\0', Rule(""), }, }},
{ ')', { {'+', Rule("") }, {'*', Rule("") }, {'(', Rule("") }, {')', Rule(POP) }, {'i', Rule("") }, {'\0', Rule(""), }, }},
{ 'i', { {'+', Rule("") }, {'*', Rule("") }, {'(', Rule("") }, {')', Rule("") }, {'i', Rule(POP) }, {'\0', Rule(""), }, }},
{ '\0', { {'+', Rule("") }, {'*', Rule("") }, {'(', Rule("") }, {')', Rule("") }, {'i', Rule("") }, {'\0', Rule(ACCEPT), }, }},
}

16
source/util.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef UTIL_H
#define UTIL_H
#include <string>
std::string uniq(const std::string &s) {
std::string r = "";
for(auto i : s) {
if (r.find(i) == std::string::npos) {
r += i;
}
}
return r;
}
#endif // UTIL_H