Incremental parsing.

This commit is contained in:
chriseth 2022-06-27 18:04:39 +02:00
parent 7abe2750c6
commit eafe0e0621

View File

@ -35,12 +35,12 @@ using namespace solidity::smtutil;
struct SMTLib2Expression struct SMTLib2Expression
{ {
variant<string_view, vector<SMTLib2Expression>> data; variant<string, vector<SMTLib2Expression>> data;
string toString() const string toString() const
{ {
return std::visit(GenericVisitor{ return std::visit(GenericVisitor{
[](string_view const& _sv) { return string{_sv}; }, [](string const& _sv) { return string{_sv}; },
[](vector<SMTLib2Expression> const& _subExpr) { [](vector<SMTLib2Expression> const& _subExpr) {
vector<string> formatted; vector<string> formatted;
for (auto const& item: _subExpr) for (auto const& item: _subExpr)
@ -54,7 +54,10 @@ struct SMTLib2Expression
class SMTLib2Parser class SMTLib2Parser
{ {
public: public:
SMTLib2Parser(string_view const& _data): m_data(_data) {} SMTLib2Parser(istream& _input):
m_input(_input),
m_token(static_cast<char>(m_input.get()))
{}
SMTLib2Expression parseExpression() SMTLib2Expression parseExpression()
{ {
@ -62,44 +65,52 @@ public:
if (token() == '(') if (token() == '(')
{ {
advance(); advance();
skipWhitespace();
vector<SMTLib2Expression> subExpressions; vector<SMTLib2Expression> subExpressions;
while (token() != 0 && token() != ')') while (token() != 0 && token() != ')')
{ {
subExpressions.emplace_back(parseExpression()); subExpressions.emplace_back(parseExpression());
skipWhitespace(); skipWhitespace();
} }
if (token() == ')') solAssert(token() == ')');
advance(); // simulate whitespace because we do not want to read the next token
// since it might block.
m_token = ' ';
return {move(subExpressions)}; return {move(subExpressions)};
} }
else else
return {parseToken()}; return {parseToken()};
} }
string_view remainingInput() const bool isEOF()
{ {
return m_data.substr(m_pos); skipWhitespace();
return m_input.eof();
} }
private: private:
string_view parseToken() string parseToken()
{ {
string result;
skipWhitespace(); skipWhitespace();
size_t start = m_pos;
bool isPipe = token() == '|'; bool isPipe = token() == '|';
while (m_pos < m_data.size()) if (isPipe)
advance();
while (token() != 0)
{ {
char c = token(); char c = token();
if (isPipe && (m_pos > start && c == '|')) if (isPipe && c == '|')
{ {
advance(); advance();
break; break;
} }
else if (!isPipe && (langutil::isWhiteSpace(c) || c == '(' || c == ')')) else if (!isPipe && (langutil::isWhiteSpace(c) || c == '(' || c == ')'))
break; break;
result.push_back(c);
advance(); advance();
} }
return m_data.substr(start, m_pos - start); return result;
} }
void skipWhitespace() void skipWhitespace()
@ -108,30 +119,37 @@ private:
advance(); advance();
} }
char token() char token() const
{ {
return m_pos < m_data.size() ? m_data[m_pos] : 0; return m_token;
} }
void advance() { m_pos++;}
size_t m_pos = 0; void advance()
string_view const m_data; {
m_token = static_cast<char>(m_input.get());
if (token() == ';')
while (token() != '\n' && token() != 0)
m_token = static_cast<char>(m_input.get());
}
istream& m_input;
char m_token = 0;
}; };
namespace namespace
{ {
string_view command(SMTLib2Expression const& _expr) string const& command(SMTLib2Expression const& _expr)
{ {
vector<SMTLib2Expression> const& items = get<vector<SMTLib2Expression>>(_expr.data); vector<SMTLib2Expression> const& items = get<vector<SMTLib2Expression>>(_expr.data);
solAssert(!items.empty()); solAssert(!items.empty());
solAssert(holds_alternative<string_view>(items.front().data)); solAssert(holds_alternative<string>(items.front().data));
return get<string_view>(items.front().data); return get<string>(items.front().data);
} }
namespace namespace
{ {
bool isNumber(string_view const& _expr) bool isNumber(string const& _expr)
{ {
for (char c: _expr) for (char c: _expr)
if (!isDigit(c) && c != '.') if (!isDigit(c) && c != '.')
@ -143,18 +161,18 @@ bool isNumber(string_view const& _expr)
smtutil::Expression toSMTUtilExpression(SMTLib2Expression const& _expr, map<string, SortPointer>& _variableSorts) smtutil::Expression toSMTUtilExpression(SMTLib2Expression const& _expr, map<string, SortPointer>& _variableSorts)
{ {
return std::visit(GenericVisitor{ return std::visit(GenericVisitor{
[&](string_view const& _atom) { [&](string const& _atom) {
if (_atom == "true" || _atom == "false") if (_atom == "true" || _atom == "false")
return Expression(_atom == "true"); return Expression(_atom == "true");
else if (isNumber(_atom)) else if (isNumber(_atom))
return Expression(string(_atom), {}, SortProvider::realSort); return Expression(_atom, {}, SortProvider::realSort);
else else
return Expression(string(_atom), {}, _variableSorts.at(string(_atom))); return Expression(_atom, {}, _variableSorts.at(_atom));
}, },
[&](vector<SMTLib2Expression> const& _subExpr) { [&](vector<SMTLib2Expression> const& _subExpr) {
SortPointer sort; SortPointer sort;
vector<smtutil::Expression> arguments; vector<smtutil::Expression> arguments;
string_view op = get<string_view>(_subExpr.front().data); string const& op = get<string>(_subExpr.front().data);
if (op == "let") if (op == "let")
{ {
map<string, SortPointer> subSorts; map<string, SortPointer> subSorts;
@ -165,13 +183,13 @@ smtutil::Expression toSMTUtilExpression(SMTLib2Expression const& _expr, map<stri
{ {
auto const& bindingElements = get<vector<SMTLib2Expression>>(binding.data); auto const& bindingElements = get<vector<SMTLib2Expression>>(binding.data);
solAssert(bindingElements.size() == 2); solAssert(bindingElements.size() == 2);
string_view varName = get<string_view>(bindingElements.at(0).data); string const& varName = get<string>(bindingElements.at(0).data);
Expression replacement = toSMTUtilExpression(bindingElements.at(1), _variableSorts); Expression replacement = toSMTUtilExpression(bindingElements.at(1), _variableSorts);
#ifdef DEBUG #ifdef DEBUG
cerr << "Binding " << varName << " to " << replacement.toString() << endl; cerr << "Binding " << varName << " to " << replacement.toString() << endl;
#endif #endif
subSorts[string(varName)] = replacement.sort; subSorts[varName] = replacement.sort;
arguments.emplace_back(Expression(string(varName), {move(replacement)}, replacement.sort)); arguments.emplace_back(Expression(varName, {move(replacement)}, replacement.sort));
} }
for (auto&& [name, value]: subSorts) for (auto&& [name, value]: subSorts)
_variableSorts[name] = value; _variableSorts[name] = value;
@ -190,70 +208,37 @@ smtutil::Expression toSMTUtilExpression(SMTLib2Expression const& _expr, map<stri
SortProvider::boolSort : SortProvider::boolSort :
arguments.back().sort; arguments.back().sort;
} }
return Expression(string(op), move(arguments), move(sort)); return Expression(op, move(arguments), move(sort));
} }
}, _expr.data); }, _expr.data);
} }
string removeComments(string _input) class SolSMT
{ {
string result; public:
auto it = _input.begin(); SolSMT(istream& _input): m_input(_input) {}
auto end = _input.end(); void run();
while (it != end) private:
{
if (*it == ';')
{
while (it != end && *it != '\n')
++it;
if (it != end)
++it;
}
else
{
result.push_back(*it);
it++;
}
} istream& m_input;
return result; bool m_doExit = false;
} bool m_printSuccess = false;
map<string, SortPointer> m_variableSorts;
BooleanLPSolver m_solver;
};
} void SolSMT::run()
int main(int argc, char** argv)
{ {
if (argc != 2) SMTLib2Parser parser(m_input);
{
cout << "Usage: solsmt <smtlib2 file>" << endl;
return -1;
}
string input = removeComments(readFileAsString(argv[1])); while (!m_doExit && !parser.isEOF())
string_view inputToParse = input;
bool printSuccess = false;
// bool produceModels = false;
map<string, SortPointer> variableSorts;
BooleanLPSolver solver;
bool doExit = false;
while (!doExit)
{ {
while (!inputToParse.empty() && isWhiteSpace(inputToParse.front()))
inputToParse = inputToParse.substr(1);
if (inputToParse.empty())
break;
//cout << line << endl;
SMTLib2Parser parser(inputToParse);
SMTLib2Expression expr = parser.parseExpression(); SMTLib2Expression expr = parser.parseExpression();
auto newInputToParse = parser.remainingInput();
#ifdef DEBUG #ifdef DEBUG
cerr << "got : " << string(inputToParse.begin(), newInputToParse.begin()) << endl;
cerr << " -> " << expr.toString() << endl; cerr << " -> " << expr.toString() << endl;
#endif #endif
inputToParse = move(newInputToParse);
vector<SMTLib2Expression> const& items = get<vector<SMTLib2Expression>>(expr.data); vector<SMTLib2Expression> const& items = get<vector<SMTLib2Expression>>(expr.data);
string_view cmd = command(expr); string const& cmd = command(expr);
if (cmd == "set-info") if (cmd == "set-info")
{ {
// ignore // ignore
@ -261,23 +246,23 @@ int main(int argc, char** argv)
else if (cmd == "set-option") else if (cmd == "set-option")
{ {
solAssert(items.size() >= 2); solAssert(items.size() >= 2);
string_view option = get<string_view>(items[1].data); string const& option = get<string>(items[1].data);
if (option == ":print-success") if (option == ":print-success")
printSuccess = (get<string_view>(items[2].data) == "true"); m_printSuccess = (get<string>(items[2].data) == "true");
// else if (option == ":produce-models") // else if (option == ":produce-models")
// produceModels = (get<string_view>(items[2].data) == "true"); // produceModels = (get<string>(items[2].data) == "true");
// ignore the rest // ignore the rest
} }
else if (cmd == "declare-fun") else if (cmd == "declare-fun")
{ {
solAssert(items.size() == 4); solAssert(items.size() == 4);
string variableName = string{get<string_view>(items[1].data)}; string variableName = string{get<string>(items[1].data)};
solAssert(get<vector<SMTLib2Expression>>(items[2].data).empty()); solAssert(get<vector<SMTLib2Expression>>(items[2].data).empty());
string_view type = get<string_view>(items[3].data); string const& type = get<string>(items[3].data);
solAssert(type == "Real" || type == "Bool"); solAssert(type == "Real" || type == "Bool");
SortPointer sort = type == "Real" ? SortProvider::realSort : SortProvider::boolSort; SortPointer sort = type == "Real" ? SortProvider::realSort : SortProvider::boolSort;
variableSorts[variableName] = sort; m_variableSorts[variableName] = sort;
solver.declareVariable(variableName, move(sort)); m_solver.declareVariable(variableName, move(sort));
} }
else if (cmd == "define-fun") else if (cmd == "define-fun")
{ {
@ -286,19 +271,19 @@ int main(int argc, char** argv)
else if (cmd == "assert") else if (cmd == "assert")
{ {
solAssert(items.size() == 2); solAssert(items.size() == 2);
solver.addAssertion(toSMTUtilExpression(items[1], variableSorts)); m_solver.addAssertion(toSMTUtilExpression(items[1], m_variableSorts));
} }
else if (cmd == "push") else if (cmd == "push")
{ {
// TODO what is the meaning of the numeric argument? // TODO what is the meaning of the numeric argument?
solAssert(items.size() == 2); solAssert(items.size() == 2);
solver.push(); m_solver.push();
} }
else if (cmd == "pop") else if (cmd == "pop")
{ {
// TODO what is the meaning of the numeric argument? // TODO what is the meaning of the numeric argument?
solAssert(items.size() == 2); solAssert(items.size() == 2);
solver.pop(); m_solver.pop();
} }
else if (cmd == "set-logic") else if (cmd == "set-logic")
{ {
@ -306,7 +291,7 @@ int main(int argc, char** argv)
} }
else if (cmd == "check-sat") else if (cmd == "check-sat")
{ {
auto&& [result, model] = solver.check({}); auto&& [result, model] = m_solver.check({});
if (result == CheckResult::SATISFIABLE) if (result == CheckResult::SATISFIABLE)
cout << "sat" << endl; cout << "sat" << endl;
else if (result == CheckResult::UNSATISFIABLE) else if (result == CheckResult::UNSATISFIABLE)
@ -317,12 +302,28 @@ int main(int argc, char** argv)
continue; continue;
} }
else if (cmd == "exit") else if (cmd == "exit")
doExit = true; m_doExit = true;
else else
solAssert(false, "Unknown instruction: " + string(cmd)); solAssert(false, "Unknown instruction: " + string(cmd));
if (printSuccess) if (m_printSuccess)
cout << "success" << endl; cout << "success" << endl;
} }
}
}
int main(int argc, char** argv)
{
if (argc != 2 && argc != 1)
{
cout << "Usage: solsmt [<smtlib2 file>]" << endl;
return -1;
}
optional<ifstream> input;
if (argc == 2)
input = ifstream(argv[1]);
SolSMT{argc == 1 ? cin : *input}.run();
return 0; return 0;
} }