Compare commits

...
Sign in to create a new pull request.

5 commits
main ... antlr

Author SHA1 Message Date
eelke
698ccab6ab wip 2022-04-07 19:35:29 +02:00
eelke
0da32b916c Lexer improvements:
- Convert unquoted idents to lowercase.
- Recognize quoted idents.
- Allow all unicode whitespace characters
- Added UnexpectedSymbol token for unexpected input (otherwise it is just ignored)
- Handle mixed case keywords in the lexer file instead of filtering the stream
2022-04-03 20:09:58 +02:00
eelke
81f27a6a18 check there are no unexpected parser errors in tests 2022-04-03 12:50:08 +02:00
eelke
a0ba9b894f Improve construction of out Parser class. 2022-04-03 12:30:23 +02:00
eelke
fbbe832a05 Start of new ANTLR4 based parser.
Very simple tests pass.
2022-04-03 12:27:35 +02:00
47 changed files with 1123 additions and 8 deletions

2
.gitignore vendored
View file

@ -11,3 +11,5 @@ srcdoc/
pglabAll.pro.user.4.8-pre1 pglabAll.pro.user.4.8-pre1
*.user *.user
/pglabAll.pro.user* /pglabAll.pro.user*
.antlr/
**/.generated/*

View file

@ -5,8 +5,6 @@ error( "Use local.pri.sample to create your own local.pri" )
LIBS += -lUser32 -lws2_32 -llibpq LIBS += -lUser32 -lws2_32 -llibpq
CONFIG += c++17 CONFIG += c++17
QMAKE_CXXFLAGS += /std:c++17
# The following define makes your compiler emit warnings if you use # The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings # any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the # depend on your compiler). Please consult the documentation of the

View file

@ -19,7 +19,9 @@ enum class BasicTokenType {
Comma, Comma,
Cast, Cast,
WhiteSpace, WhiteSpace,
NewLine NewLine,
LastLexerToken
}; };
enum class LexerState { enum class LexerState {

View file

@ -26,7 +26,24 @@ Keyword isKeyword(const QString &symbol)
return Keyword::NotAKeyword; return Keyword::NotAKeyword;
} }
/*
Put tokens on a stack
Every time something is put on the stack see if it matches a rule
The stack needs to contain both tokens from the lexical analyzer as tokens for reductions done by the parser.
Matching rules, as we need to match against the top of the stack we should match the rules end to start.
Meaning if we have on the stack A B C then we need to consider rules ending with a C
*/
class StackItem {
public:
int Token;
};
SqlParser::SqlParser(SqlLexer &lexer) SqlParser::SqlParser(SqlLexer &lexer)
: lexer(lexer) : lexer(lexer)

View file

@ -36,6 +36,7 @@ class ConnectionConfig;
class ConnectionNode { class ConnectionNode {
public: public:
virtual ~ConnectionNode() = default; virtual ~ConnectionNode() = default;
}; };
class ConnectionGroup: public ConnectionNode { class ConnectionGroup: public ConnectionNode {
@ -115,7 +116,10 @@ public:
bool dirty() const; bool dirty() const;
void clean(); void clean();
bool operator==(QUuid id) const { return m_uuid == id; } bool operator==(const ConnectionConfig &rhs) const
{
return m_uuid == rhs.m_uuid;
}
QString makeLongDescription() const; QString makeLongDescription() const;
QByteArray encodedPassword() const; QByteArray encodedPassword() const;

77
pglablib/PgsqlLexer.g4 Normal file
View file

@ -0,0 +1,77 @@
lexer grammar PgsqlLexer;
@lexer::preinclude {
#include <QString>
}
SemiColon: ';';
Comma: ',';
Dot: '.';
OpenParen: '(';
CloseParen: ')';
fragment A : [aA];
fragment B : [bB];
fragment C : [cC];
fragment D : [dD];
fragment E : [eE];
fragment F : [fF];
fragment G : [gG];
fragment H : [hH];
fragment I : [iI];
fragment J : [jJ];
fragment K : [kK];
fragment L : [lL];
fragment M : [mM];
fragment N : [nN];
fragment O : [oO];
fragment P : [pP];
fragment Q : [qQ];
fragment R : [rR];
fragment S : [sS];
fragment T : [tT];
fragment U : [uU];
fragment V : [vV];
fragment W : [wW];
fragment X : [xX];
fragment Y : [yY];
fragment Z : [zZ];
As: A S;
By: B Y;
Cross: C R O S S;
From: F R O M;
Full: F U L L;
Group: G R O U P;
Having: H A V I N G;
Inner: I N N E R;
Join: J O I N;
Left : L E F T;
Natural : N A T U R A L;
On : O N;
Order : O R D E R;
Outer : O U T E R;
Right : R I G H T;
Select : S E L E C T;
Using : U S I N G;
Where : W H E R E;
Ident: [\p{Alpha}]~[\p{White_Space}]*
{
setText(QString::fromStdString(getText()).toLower().toStdString());
}
| '"' ~["]+ '"'
{
{
std::string s = getText();
s = s.substr(1, s.length() - 2);
setText(s);
}
};
IntegerLiteral: [1-9][0-9]*;
StringLiteral: '\'' ('\'\'' | ~['])+ '\'' { setText(getText().substr(1, getText().length()-2)); };
Whitespace: [\p{White_Space}] -> skip ; // skip spaces, tabs, newlines
UnexpectedSymbol: .;

119
pglablib/PgsqlParser.g4 Normal file
View file

@ -0,0 +1,119 @@
// Define a grammar called postgresql
parser grammar PgsqlParser;
options {
tokenVocab = PgsqlLexer;
}
@parser::preinclude {
#include "sqlast/sqlast.h"
}
@parser::includes {
}
@parser::members {
}
main returns [std::unique_ptr<sqlast::StatementList> program]
: statement_list { $program = std::move($statement_list.result); }
;
statement_list returns [std::unique_ptr<sqlast::StatementList> result]
: { $result = std::make_unique<sqlast::StatementList>(); }
(statement SemiColon { $result->Add(std::move($statement.result)); } | empty_statement)*
(statement SemiColon? { $result->Add(std::move($statement.result)); } | empty_statement )
;
statement returns [std::unique_ptr<sqlast::Statement> result]
: select_stmt { $result = std::move($select_stmt.result); }
;
empty_statement
: SemiColon
;
select_stmt returns [std::unique_ptr<sqlast::SelectStatement> result]
: Select select_list
{
$result = std::make_unique<sqlast::SelectStatement>();
$result->SetSelectList(std::move($select_list.result));
}
(From from_item (Comma from_item)* )?
(Where condition)?
(Group By group_by)?
(Order By order_by)?
(Having having)?
;
from_item
: Ident Dot Ident from_alias?
| Ident from_alias?
| OpenParen select_stmt CloseParen from_alias
| from_item (Left|Right|Full) Outer? Join from_item (join_on_condition|join_using_condition)
| from_item Natural (Left|Right|Full) Outer? Join from_item
;
join_on_condition
: On
;
join_using_condition
: Using OpenParen ident_list CloseParen (As join_using_alias)
;
join_using_alias
:
;
ident_list
: Ident (Comma Ident)*
;
from_alias
: As? Ident (OpenParen Ident (Comma Ident)* CloseParen)?
;
condition
: expr
;
group_by
:
;
order_by
:
;
having
:
;
select_list returns [std::unique_ptr<sqlast::SelectList> result]
: select_item
{
$result = std::make_unique<sqlast::SelectList>();
$result->Add(std::move($select_item.result));
}
(Comma select_item)* { $result->Add(std::move($select_item.result)); }
|
;
select_item returns [std::unique_ptr<sqlast::SelectItem> result]
: expr { $result = std::make_unique<sqlast::SelectItem>(std::move($expr.result)); }
(As? Ident { $result->SetAlias($Ident.text); })?
;
expr returns [std::unique_ptr<sqlast::Expression> result]
: expr Dot Ident
| Ident
| value { $result = std::move($value.result); }
;
value returns [std::unique_ptr<sqlast::Literal> result]
: IntegerLiteral
| StringLiteral { $result = std::make_unique<sqlast::StringLiteral>($StringLiteral.text); }
;

1
pglablib/build-cpp.bat Normal file
View file

@ -0,0 +1 @@
java -Xmx500M -cp "c:\prog\antlr\antlr-4.9.2-complete.jar;%CLASSPATH%" org.antlr.v4.Tool -Dlanguage=Cpp PgsqlLexer.g4 PgsqlParser.g4 -o sqlparser/.generated -no-listener %2 %3 %4 %5 %6 %7

View file

@ -4,11 +4,11 @@
# #
#------------------------------------------------- #-------------------------------------------------
QT += widgets core QT += widgets core concurrent
TARGET = pglablib TARGET = pglablib
TEMPLATE = lib TEMPLATE = lib
CONFIG += staticlib CONFIG += staticlib no_keywords
! include( ../common.pri ) { ! include( ../common.pri ) {
error( "Couldn't find the common.pri file!" ) error( "Couldn't find the common.pri file!" )
@ -45,6 +45,25 @@ SOURCES += \
catalog/PgConstraintContainer.cpp \ catalog/PgConstraintContainer.cpp \
ParamListJson.cpp \ ParamListJson.cpp \
ParamListModel.cpp \ ParamListModel.cpp \
sqlast/BuildStandardItemTreeModelNodeVisitor.cpp \
sqlast/ColumnDefinition.cpp \
sqlast/CreateTable.cpp \
sqlast/Expression.cpp \
sqlast/Literal.cpp \
sqlast/Node.cpp \
sqlast/NodeVisitor.cpp \
sqlast/SelectItem.cpp \
sqlast/SelectList.cpp \
sqlast/SelectStatement.cpp \
sqlast/Statement.cpp \
sqlast/StatementList.cpp \
sqlast/StringLiteral.cpp \
sqlast/TypeSpecification.cpp \
sqlast/Visitor.cpp \
sqlparser/.generated/PgsqlLexer.cpp \
sqlparser/.generated/PgsqlParser.cpp \
sqlparser/ErrorListener.cpp \
sqlparser/Parser.cpp \
util.cpp \ util.cpp \
SqlFormattingUtils.cpp \ SqlFormattingUtils.cpp \
catalog/PgKeywordList.cpp \ catalog/PgKeywordList.cpp \
@ -113,6 +132,26 @@ HEADERS += \
catalog/PgConstraintContainer.h \ catalog/PgConstraintContainer.h \
ParamListJson.h \ ParamListJson.h \
ParamListModel.h \ ParamListModel.h \
sqlast/BuildStandardItemTreeModelNodeVisitor.h \
sqlast/ColumnDefinition.h \
sqlast/CreateTable.h \
sqlast/Expression.h \
sqlast/Literal.h \
sqlast/Node.h \
sqlast/NodeVisitor.h \
sqlast/SelectItem.h \
sqlast/SelectList.h \
sqlast/SelectStatement.h \
sqlast/Statement.h \
sqlast/StatementList.h \
sqlast/StringLiteral.h \
sqlast/TypeSpecification.h \
sqlast/Visitor.h \
sqlast/sqlast.h \
sqlparser/.generated/PgsqlLexer.h \
sqlparser/.generated/PgsqlParser.h \
sqlparser/ErrorListener.h \
sqlparser/Parser.h \
util.h \ util.h \
SqlFormattingUtils.h \ SqlFormattingUtils.h \
catalog/PgCatalogTypes.h \ catalog/PgCatalogTypes.h \
@ -170,6 +209,10 @@ else:unix:!macx: LIBS += -L$$OUT_PWD/../core/ -lcore
INCLUDEPATH += $$PWD/../core INCLUDEPATH += $$PWD/../core
DEPENDPATH += $$PWD/../core DEPENDPATH += $$PWD/../core
INCLUDEPATH += C:\Prog\include\antlr
win32:CONFIG(debug, debug|release): LIBS += -lantlr4-runtimed
else:win32:CONFIG(release, debug|release): LIBS += -lantlr4-runtime
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/libcore.a win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/libcore.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/libcore.a else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/libcore.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/core.lib else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/core.lib

View file

@ -0,0 +1,69 @@
#include "BuildStandardItemTreeModelNodeVisitor.h"
using namespace sqlast;
namespace {
template <class T>
class AutoRevert {
public:
AutoRevert(T& var, const T newValue)
: variable(var)
, previousValue(var)
{
variable = std::move(newValue);
}
AutoRevert(T& var, const T&& newValue)
: variable(var)
, previousValue(var)
{
variable = std::move(newValue);
}
~AutoRevert()
{
variable = std::move(previousValue);
}
AutoRevert(const AutoRevert&) = delete;
AutoRevert operator=(const AutoRevert&) = delete;
private:
T& variable;
T previousValue;
};
template<class T>
AutoRevert<T> MakeAutoRevert(T& var, const T newValue)
{
return AutoRevert<T>(var, newValue);
}
}
BuildStandardItemTreeModelNodeVisitor::BuildStandardItemTreeModelNodeVisitor()
: model(std::make_unique<QStandardItemModel>())
, currentParent(model->invisibleRootItem())
{
}
void BuildStandardItemTreeModelNodeVisitor::Visit(SelectItem &selectItem)
{
}
void BuildStandardItemTreeModelNodeVisitor::Visit(SelectList &selectList)
{
}
void BuildStandardItemTreeModelNodeVisitor::Visit(SelectStatement &selectStatement)
{
auto item = new QStandardItem("SELECT");
currentParent->appendRow(item);
auto guard = MakeAutoRevert(currentParent, item);
}
void sqlast::BuildStandardItemTreeModelNodeVisitor::Visit(StatementList &statementList)
{
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "NodeVisitor.h"
#include <QStandardItemModel>
#include <memory>
namespace sqlast {
class BuildStandardItemTreeModelNodeVisitor : public NodeVisitor
{
public:
BuildStandardItemTreeModelNodeVisitor();
// NodeVisitor interface
public:
virtual void Visit(SelectItem &selectItem) override;
virtual void Visit(SelectList &selectList) override;
virtual void Visit(SelectStatement &selectStatement) override;
virtual void Visit(StatementList &statementList) override;
virtual void Visit(StringLiteral &stringLiteral) override;
private:
std::unique_ptr<QStandardItemModel> model;
QStandardItem *currentParent;
};
}

View file

@ -0,0 +1,9 @@
#include "ColumnDefinition.h"
#include "TypeSpecification.h"
using namespace sqlast;
ColumnDefinition::ColumnDefinition()
{
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "Node.h"
#include <QString>
#include <memory>
namespace sqlast {
class TypeSpecification;
/// Defines the details of a table column
///
/// Constraints are not included here, as we handle constraints can apply to multiple columns
/// and we want to put them all in one place. The UI and SQL generator is allowed to display
/// column specific constraints with the column they belong to.
class ColumnDefinition : public Node
{
public:
ColumnDefinition();
private:
QString name;
std::unique_ptr<TypeSpecification> typeName;
bool notNull = true;
};
}

View file

@ -0,0 +1,8 @@
#include "CreateTable.h"
using namespace sqlast;
CreateTable::CreateTable()
{
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "Statement.h"
#include <memory>
namespace sqlast {
class ColumnDefinition;
class TableConstraint;
class CreateTable: public Statement
{
public:
CreateTable();
private:
};
}

View file

@ -0,0 +1,8 @@
#include "Expression.h"
using namespace sqlast;
Expression::Expression()
{
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "Node.h"
namespace sqlast {
class Expression: public Node
{
public:
Expression();
};
}

View file

@ -0,0 +1,8 @@
#include "Literal.h"
using namespace sqlast;
Literal::Literal()
{
}

13
pglablib/sqlast/Literal.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include "Expression.h"
namespace sqlast {
class Literal: public Expression
{
public:
Literal();
};
}

12
pglablib/sqlast/Node.cpp Normal file
View file

@ -0,0 +1,12 @@
#include "Node.h"
using namespace sqlast;
Node::Node()
{
}
QString Node::ToString() const
{
return QString::fromUtf8(typeid(*this).name());
}

37
pglablib/sqlast/Node.h Normal file
View file

@ -0,0 +1,37 @@
#pragma once
#include <QString>
#include <stdexcept>
#include <typeinfo>
namespace sqlast {
class NodeVisitor;
class Node {
public:
Node();
virtual ~Node() = default;
virtual void Accept(NodeVisitor &visitor) = 0;
/// Every derived class that has child nodes should override these
/// to facilitate
// virtual int ChildCount() const { return 0; }
// virtual const Node* GetChild(int index) const { throw std::out_of_range("GetChild"); }
virtual QString ToString() const;
};
}
/*
- Node
- INSERT
- UPDATE
- DELETE
- SELECT
- WITH
- CTE
*/

View file

@ -0,0 +1,8 @@
#include "NodeVisitor.h"
#include "sqlast/SelectList.h"
void sqlast::NodeVisitor::VisitSelectListItems(SelectList &selectList)
{
for (int idx = 0; idx < selectList.Count(); ++idx)
Visit(selectList.Get(idx));
}

View file

@ -0,0 +1,27 @@
#pragma once
namespace sqlast {
class SelectItem;
class SelectList;
class SelectStatement;
class StatementList;
class StringLiteral;
class NodeVisitor
{
public:
virtual ~NodeVisitor() = default;
virtual void Visit(SelectItem &selectItem) = 0;
virtual void Visit(SelectList &selectList) = 0;
virtual void Visit(SelectStatement &selectStatement) = 0;
virtual void Visit(StatementList &statementList) = 0;
virtual void Visit(StringLiteral &stringLiteral) = 0;
protected:
void VisitSelectListItems(SelectList &selectList);
};
}

View file

@ -0,0 +1,19 @@
#include "SelectItem.h"
#include "Expression.h"
#include "NodeVisitor.h"
using namespace sqlast;
SelectItem::SelectItem(std::unique_ptr<sqlast::Expression> expr)
: expression(std::move(expr))
{}
void SelectItem::SetAlias(const std::string &alias)
{
this->alias = alias;
}
void SelectItem::Accept(NodeVisitor &visitor)
{
visitor.Visit(*this);
}

View file

@ -0,0 +1,27 @@
#pragma once
#include "Node.h"
#include <memory>
#include <string>
namespace sqlast {
class Expression;
class SelectItem : public Node
{
public:
explicit SelectItem(std::unique_ptr<sqlast::Expression> expr);
Expression& GetExpression() { return *expression; }
void SetAlias(const std::string &alias);
std::string GetAlias() const { return alias; }
void Accept(NodeVisitor &visitor) override;
private:
std::unique_ptr<Expression> expression;
std::string alias;
};
}

View file

@ -0,0 +1,29 @@
#include "SelectList.h"
#include "SelectItem.h"
#include "sqlast/NodeVisitor.h"
using namespace sqlast;
SelectList::SelectList()
{
}
void SelectList::Add(std::unique_ptr<SelectItem> select_item)
{
selectItems.push_back(std::move(select_item));
}
int SelectList::Count() const
{
return static_cast<int>(selectItems.size());
}
SelectItem &SelectList::Get(int index)
{
return *selectItems.at(index);
}
void SelectList::Accept(NodeVisitor &visitor)
{
visitor.Visit(*this);
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "Node.h"
#include <memory>
#include <vector>
namespace sqlast {
class SelectItem;
class SelectList : public Node
{
public:
SelectList();
void Add(std::unique_ptr<SelectItem> select_item);
int Count() const;
SelectItem& Get(int index);
void Accept(NodeVisitor &visitor) override;
private:
using List = std::vector<std::unique_ptr<SelectItem>>;
List selectItems;
};
}

View file

@ -0,0 +1,25 @@
#include "SelectStatement.h"
#include "SelectList.h"
#include "NodeVisitor.h"
using namespace sqlast;
SelectStatement::SelectStatement()
{
}
SelectList* SelectStatement::GetSelectList()
{
return selectList.get();
}
void SelectStatement::SetSelectList(std::unique_ptr<SelectList> value)
{
selectList = std::move(value);
}
void SelectStatement::Accept(NodeVisitor &visitor)
{
visitor.Visit(*this);
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "Statement.h"
#include <memory>
namespace sqlast {
class SelectList;
class SelectStatement: public Statement
{
public:
SelectStatement();
SelectList* GetSelectList();
void SetSelectList(std::unique_ptr<SelectList> value);
void Accept(NodeVisitor &visitor) override;
private:
std::unique_ptr<SelectList> selectList;
};
}

View file

@ -0,0 +1,10 @@
#include "Statement.h"
using namespace sqlast;
Statement::Statement()
{
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "Node.h"
namespace sqlast {
class Statement: public Node
{
public:
Statement();
};
}

View file

@ -0,0 +1,29 @@
#include "StatementList.h"
#include "Statement.h"
#include "sqlast/NodeVisitor.h"
using namespace sqlast;
StatementList::StatementList()
{}
void StatementList::Add(std::unique_ptr<Statement> &&statement)
{
statements.push_back(std::move(statement));
}
Statement &StatementList::Get(int index)
{
return *statements[index];
}
int StatementList::Count() const
{
return static_cast<int>(statements.size());
}
void StatementList::Accept(NodeVisitor &visitor)
{
visitor.Visit(*this);
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "Node.h"
#include <memory>
#include <vector>
namespace sqlast {
class Statement;
class StatementList: public Node
{
public:
StatementList();
void Add(std::unique_ptr<Statement> &&statement);
Statement &Get(int index);
int Count() const;
void Accept(NodeVisitor &visitor) override;
private:
using Statements = std::vector<std::unique_ptr<Statement>>;
Statements statements;
};
}

View file

@ -0,0 +1,13 @@
#include "StringLiteral.h"
#include "NodeVisitor.h"
using namespace sqlast;
StringLiteral::StringLiteral(const std::string s)
: value(QString::fromStdString(s))
{}
void StringLiteral::Accept(NodeVisitor &visitor)
{
visitor.Visit(*this);
}

View file

@ -0,0 +1,20 @@
#pragma once
#include "Literal.h"
#include <QString>
#include <string>
namespace sqlast {
class StringLiteral : public Literal
{
public:
explicit StringLiteral(const std::string s);
QString GetValue() const { return value; }
void Accept(NodeVisitor &visitor) override;
private:
QString value;
};
}

View file

@ -0,0 +1,8 @@
#include "TypeSpecification.h"
using namespace sqlast;
TypeSpecification::TypeSpecification()
{
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "Node.h"
#include <QString>
namespace sqlast {
/// These object define not only the base type, but also
/// parameters used with the type
/// Think the precision of numeric, max length of char, array
class TypeSpecification : public Node
{
public:
TypeSpecification();
private:
/// We do not use the PgType from the catalog here as the type used might be defined
/// inside the script and not present yet in the catalog.
QString baseType;
// is_array
};
}

View file

@ -0,0 +1,8 @@
#include "Visitor.h"
using namespace sqlast;
Visitor::Visitor()
{
}

13
pglablib/sqlast/Visitor.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
namespace sqlast {
class Visitor
{
public:
Visitor();
};
}

12
pglablib/sqlast/sqlast.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include "Node.h"
#include "SelectStatement.h"
#include "SelectItem.h"
#include "SelectList.h"
#include "Statement.h"
#include "StatementList.h"
#include "StringLiteral.h"
#undef emit

View file

@ -0,0 +1,22 @@
#include "ErrorListener.h"
void ErrorListener::syntaxError(antlr4::Recognizer *recognizer, antlr4::Token *offendingSymbol, size_t line, size_t charPositionInLine, const std::string &msg, std::exception_ptr e)
{
++errors;
}
void ErrorListener::reportAmbiguity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact, const antlrcpp::BitSet &ambigAlts, antlr4::atn::ATNConfigSet *configs)
{
++errors;
}
void ErrorListener::reportAttemptingFullContext(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, const antlrcpp::BitSet &conflictingAlts, antlr4::atn::ATNConfigSet *configs)
{
++errors;
}
void ErrorListener::reportContextSensitivity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, size_t prediction, antlr4::atn::ATNConfigSet *configs)
{
++errors;
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "antlr4-runtime.h"
class ErrorListener : public antlr4::ANTLRErrorListener
{
// ANTLRErrorListener interface
public:
virtual void syntaxError(antlr4::Recognizer *recognizer, antlr4::Token *offendingSymbol, size_t line, size_t charPositionInLine, const std::string &msg, std::exception_ptr e) override;
virtual void reportAmbiguity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact, const antlrcpp::BitSet &ambigAlts, antlr4::atn::ATNConfigSet *configs) override;
virtual void reportAttemptingFullContext(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, const antlrcpp::BitSet &conflictingAlts, antlr4::atn::ATNConfigSet *configs) override;
virtual void reportContextSensitivity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, size_t prediction, antlr4::atn::ATNConfigSet *configs) override;
public:
int errorCount() const
{
return errors;
}
private:
int errors = 0;
};

View file

@ -0,0 +1,24 @@
#include "Parser.h"
#include "antlr4-runtime.h"
Parser::Parser(const std::string &input_string)
: Parser(std::make_unique<antlr4::ANTLRInputStream>(input_string))
{}
Parser::Parser(std::unique_ptr<antlr4::CharStream> stream)
: InputStream(std::move(stream))
, Lexer(InputStream.get())
, TokenStream(&Lexer)
, AParser(&TokenStream)
{
AParser.removeErrorListeners();
AParser.addErrorListener(&Errors);
}
std::unique_ptr<sqlast::StatementList> Parser::Parse()
{
auto context = AParser.main();
return std::move(context->program);
}

View file

@ -0,0 +1,26 @@
#pragma once
#include ".generated/PgsqlLexer.h"
#include ".generated/PgsqlParser.h"
#include "ErrorListener.h"
class Parser
{
public:
Parser(const std::string &input_string);
Parser(std::unique_ptr<antlr4::CharStream> stream);
std::unique_ptr<sqlast::StatementList> Parse();
int errorCount() const
{
return Errors.errorCount();
}
private:
std::unique_ptr<antlr4::CharStream> InputStream;
PgsqlLexer Lexer;
antlr4::CommonTokenStream TokenStream;
PgsqlParser AParser;
ErrorListener Errors;
};

View file

@ -17,7 +17,7 @@ QString msfloatToHumanReadableString(float ms)
if (ms < 1.0f) { if (ms < 1.0f) {
val = ms * 1000.f; val = ms * 1000.f;
//result = QString::asprintf("%0.3f", ms * 1000.0f); //result = QString::asprintf("%0.3f", ms * 1000.0f);
unit = u8"μs"; unit = QString::fromUtf8("μs");
} }
else if (ms >= 1000.0) { else if (ms >= 1000.0) {
val = ms / 1000.0f; val = ms / 1000.0f;

View file

@ -6,7 +6,7 @@ include(gtest_dependency.pri)
TEMPLATE = app TEMPLATE = app
CONFIG += console CONFIG += console
CONFIG -= app_bundle CONFIG -= app_bundle
CONFIG += thread CONFIG += thread no_keywords
CONFIG += qt CONFIG += qt
QT += core widgets QT += core widgets
@ -20,6 +20,7 @@ SOURCES += main.cpp \
tst_escapeConnectionStringValue.cpp \ tst_escapeConnectionStringValue.cpp \
tst_expected.cpp \ tst_expected.cpp \
tst_SqlLexer.cpp \ tst_SqlLexer.cpp \
tst_newParser.cpp \
tst_scopeguard.cpp \ tst_scopeguard.cpp \
tst_CsvWriter.cpp \ tst_CsvWriter.cpp \
tst_PasswordManager.cpp \ tst_PasswordManager.cpp \
@ -39,6 +40,10 @@ DEPENDPATH += $$PWD/../../core
win32:CONFIG(debug, debug|release): LIBS += -lbotand win32:CONFIG(debug, debug|release): LIBS += -lbotand
else:win32:CONFIG(release, debug|release): LIBS += -lbotan else:win32:CONFIG(release, debug|release): LIBS += -lbotan
INCLUDEPATH += C:\Prog\include\antlr
win32:CONFIG(debug, debug|release): LIBS += -lantlr4-runtimed
else:win32:CONFIG(release, debug|release): LIBS += -lantlr4-runtime
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/release/libcore.a win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/release/libcore.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/debug/libcore.a else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/debug/libcore.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/release/core.lib else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/release/core.lib

View file

@ -0,0 +1,135 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "PrintTo_Qt.h"
#include "sqlparser/Parser.h"
using namespace testing;
using namespace sqlast;
TEST(NewSqlLexer, Select)
{
std::string source = "SELECT";
antlr4::ANTLRInputStream input(source);
PgsqlLexer lexer(&input);
auto token = lexer.nextToken();
ASSERT_EQ(PgsqlLexer::Select, token->getType());
}
TEST(NewSqlLexer, Ident)
{
std::string source = "Abc";
antlr4::ANTLRInputStream input(source);
PgsqlLexer lexer(&input);
auto token = lexer.nextToken();
ASSERT_EQ(PgsqlLexer::Ident, token->getType());
ASSERT_EQ("abc", token->getText());
}
TEST(NewSqlLexer, QuotedIdent)
{
std::string source = "\"Abc\"";
antlr4::ANTLRInputStream input(source);
PgsqlLexer lexer(&input);
auto token = lexer.nextToken();
ASSERT_EQ(PgsqlLexer::Ident, token->getType());
ASSERT_EQ("Abc", token->getText());
}
TEST(NewSqlLexer, AcceptNewLineInQuotedIdent)
{
std::string source = "\"Ab\nc\"";
antlr4::ANTLRInputStream input(source);
PgsqlLexer lexer(&input);
auto token = lexer.nextToken();
ASSERT_EQ(PgsqlLexer::Ident, token->getType());
ASSERT_EQ("Ab\nc", token->getText());
}
TEST(NewSqlParser, statementList)
{
std::string input_string = "SEleCT 1; Select 2;";
Parser parser(input_string);
std::unique_ptr<sqlast::StatementList> program = parser.Parse();
ASSERT_TRUE(program != nullptr);
ASSERT_EQ(2, program->Count());
ASSERT_EQ(0, parser.errorCount());
}
TEST(NewSqlParser, missingSemi)
{
std::string input_string = "1";
Parser parser(input_string);
std::unique_ptr<sqlast::StatementList> program = parser.Parse();
ASSERT_EQ(1, parser.errorCount());
}
TEST(NewSqlParser, selectList)
{
std::string input_string = "SEleCT 1, 'Tekst'";
Parser parser(input_string);
std::unique_ptr<sqlast::StatementList> program = parser.Parse();
ASSERT_TRUE(program != nullptr);
ASSERT_EQ(1, program->Count());
ASSERT_EQ(0, parser.errorCount());
SelectStatement &s = dynamic_cast<SelectStatement&>(program->Get(0));
SelectList* sl = s.GetSelectList();
ASSERT_TRUE(sl != nullptr);
ASSERT_EQ(2, sl->Count());
SelectItem& si = sl->Get(1);
StringLiteral& string_literal = dynamic_cast<StringLiteral&>(si.GetExpression());
ASSERT_EQ("Tekst", string_literal.GetValue());
}
TEST(NewSqlParser, selectAliasWithoutAs)
{
std::string input_string = "SELECT 1 a";
Parser parser(input_string);
std::unique_ptr<sqlast::StatementList> program = parser.Parse();
ASSERT_TRUE(program != nullptr);
ASSERT_EQ(1, program->Count());
ASSERT_EQ(0, parser.errorCount());
SelectStatement &s = dynamic_cast<SelectStatement&>(program->Get(0));
SelectList* sl = s.GetSelectList();
SelectItem& si = sl->Get(0);
ASSERT_EQ("a", si.GetAlias());
}
TEST(NewSqlParser, selectAliasWithAs)
{
std::string input_string = "SELECT 1 AS b";
Parser parser(input_string);
std::unique_ptr<sqlast::StatementList> program = parser.Parse();
ASSERT_TRUE(program != nullptr);
ASSERT_EQ(1, program->Count());
ASSERT_EQ(0, parser.errorCount());
SelectStatement &s = dynamic_cast<SelectStatement&>(program->Get(0));
SelectList* sl = s.GetSelectList();
SelectItem& si = sl->Get(0);
ASSERT_EQ("b", si.GetAlias());
}
TEST(NewSqlParser, selectFrom)
{
std::string input_string = "SELECT 1 FROM a";
Parser parser(input_string);
std::unique_ptr<sqlast::StatementList> program = parser.Parse();
ASSERT_TRUE(program != nullptr);
ASSERT_EQ(1, program->Count());
ASSERT_EQ(0, parser.errorCount());
}