Hello libcode-query

This commit is contained in:
G.H.O.S.T 2024-12-24 22:00:48 +01:00
commit 6bab8338f8
Signed by: G.H.O.S.T
GPG Key ID: 3BD93EABD1407B82
58 changed files with 2193 additions and 0 deletions

17
.editorconfig Normal file
View File

@ -0,0 +1,17 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
indent_size = 4
max_line_length = off
trim_trailing_whitespace = false
[*.yaml]
indent_size = 2

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto

View File

@ -0,0 +1,24 @@
name: on-push
on: [push]
jobs:
build-and-test:
runs-on: linux
container: code.helloryan.se/infra/buildenv/cxx-amd64-fedora-40:latest
volumes:
- /build
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: Authenticate
run: |
git config unset http.https://code.helloryan.se/.extraheader
echo "${{ secrets.NETRC }}" >> ~/.netrc
- name: Initialize
run: |
bpkg create -d /build cc config.cc.coptions="-Wall -Werror"
bdep init -A /build
- name: Build
run: b
- name: Test
run: b test

31
.gitignore vendored Normal file
View File

@ -0,0 +1,31 @@
.bdep/
# Local default options files.
#
.build2/local/
# Compiler/linker output.
#
*.d
*.t
*.i
*.i.*
*.ii
*.ii.*
*.o
*.obj
*.gcm
*.pcm
*.ifc
*.so
*.dylib
*.dll
*.a
*.lib
*.exp
*.pdb
*.ilk
*.exe
*.exe.dlls/
*.exe.manifest
*.pc

31
LICENSE Normal file
View File

@ -0,0 +1,31 @@
Copyright © 2024 Ryan. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must
display the following acknowledgement:
This product includes software developed by Ryan, http://helloryan.se/.
4. Neither the name(s) of the copyright holder(s) nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

21
README.md Normal file
View File

@ -0,0 +1,21 @@
# libcode-query
![Build status](https://code.helloryan.se/code/libcode-query/actions/workflows/on-push.yaml/badge.svg)
## Requirements
None, other than a modern C++-compiler.
## Building
See the wiki, https://code.helloryan.se/code/wiki/wiki/Build-Instructions, for
build instructions.
## Contact
Please report bugs and issues by sending an e-mail to: ryan@helloryan.se.
## Contributing
Please send an e-mail to ryan@helloryan.se to request an account and
write-access to the libcode-query repository.

4
build/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

7
build/bootstrap.build Normal file
View File

@ -0,0 +1,7 @@
project = libcode-query
using version
using config
using test
using install
using dist

6
build/export.build Normal file
View File

@ -0,0 +1,6 @@
$out_root/
{
include code/query/
}
export $out_root/code/query/$import.target

16
build/root.build Normal file
View File

@ -0,0 +1,16 @@
# Uncomment to suppress warnings coming from external libraries.
#
#cxx.internal.scope = current
cxx.std = latest
using cxx
hxx{*}: extension = hxx
ixx{*}: extension = ixx
txx{*}: extension = txx
cxx{*}: extension = cxx
# The test target for cross-testing (running tests under Wine, etc).
#
test.target = $cxx.target

5
buildfile Normal file
View File

@ -0,0 +1,5 @@
./: {code/ tests/} doc{README.md} legal{LICENSE} manifest
# Don't install tests.
#
tests/: install = false

9
code/query/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# Generated version header.
#
version.hxx
# Unit test executables and Testscript output directories
# (can be symlinks).
#
*.test
test-*.test

31
code/query/boolean.cxx Normal file
View File

@ -0,0 +1,31 @@
#include <code/query/boolean.hxx>
namespace code::query
{
boolean_t::
boolean_t(source_location_t origin, bool value)
: origin_{move(origin)}, value_{value}
{}
source_location_t const&
boolean_t::
origin() const
{
return origin_;
}
bool
boolean_t::
value() const
{
return value_;
}
string
to_string(boolean_t const& boolean)
{
return boolean.value() ? "true" : "false";
}
} // namespace code::query

32
code/query/boolean.hxx Normal file
View File

@ -0,0 +1,32 @@
#ifndef code__query__boolean_hxx_
#define code__query__boolean_hxx_
#include <code/query/source-location.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class boolean_t
{
public:
boolean_t(source_location_t, bool);
source_location_t const&
origin() const;
bool
value() const;
private:
source_location_t origin_;
bool value_;
};
string
to_string(boolean_t const&);
} // namespace code::query
#endif

62
code/query/buildfile Normal file
View File

@ -0,0 +1,62 @@
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
./: lib{code-query}: libul{code-query}
libul{code-query}: {hxx ixx txx cxx}{** -**.test... -version} \
{hxx }{ version}
libul{code-query}: $impl_libs $intf_libs
# Unit tests.
#
exe{*.test}:
{
test = true
install = false
}
for t: cxx{**.test...}
{
d = $directory($t)
n = $name($t)...
./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n}
$d/exe{$n}: libul{code-query}: bin.whole = false
}
hxx{version}: in{version} $src_root/manifest
{
dist = true
clean = ($src_root != $out_root)
}
# Build options.
#
cxx.poptions =+ "-I$out_root" "-I$src_root"
# Export options.
#
lib{code-query}:
{
cxx.export.poptions = "-I$out_root" "-I$src_root"
cxx.export.libs = $intf_libs
}
# For pre-releases use the complete version to make sure they cannot
# be used in place of another pre-release or the final version. See
# the version module for details on the version.* variable values.
#
if $version.pre_release
lib{code-query}: bin.lib.version = "-$version.project_id"
else
lib{code-query}: bin.lib.version = "-$version.major.$version.minor"
# Install into the code/query/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/code/query/
install.subdirs = true
}

25
code/query/concepts.hxx Normal file
View File

@ -0,0 +1,25 @@
#ifndef code__query__concepts_hxx_
#define code__query__concepts_hxx_
#include <code/query/source-location.hxx>
#include <code/query/types.hxx>
#include <concepts>
namespace code::query
{
class expression_t;
template<typename E>
concept Expression = requires(E const& e)
{
{ e.origin() } -> std::convertible_to<source_location_t>;
{ to_string(e) } -> std::convertible_to<string>;
};
} // namespace code::query
#endif

46
code/query/error.cxx Normal file
View File

@ -0,0 +1,46 @@
#include <code/query/error.hxx>
namespace code::query
{
warning_t::
warning_t(source_location_t origin, string description)
: origin_{move(origin)},
description_{move(description)}
{}
source_location_t const&
warning_t::
origin() const
{
return origin_;
}
string const&
warning_t::
description() const
{
return description_;
}
error_t::
error_t(source_location_t origin, string description)
: origin_{move(origin)},
description_{move(description)}
{}
source_location_t const&
error_t::
origin() const
{
return origin_;
}
string const&
error_t::
description() const
{
return description_;
}
} // namespace code::query

46
code/query/error.hxx Normal file
View File

@ -0,0 +1,46 @@
#ifndef code__query__error_hxx_
#define code__query__error_hxx_
#include <code/query/source-location.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class warning_t
{
public:
warning_t(source_location_t, string);
source_location_t const&
origin() const;
string const&
description() const;
private:
source_location_t origin_;
string description_;
};
class error_t
{
public:
error_t(source_location_t source, string);
source_location_t const&
origin() const;
string const&
description() const;
private:
source_location_t origin_;
string description_;
};
} // namespace code::query
#endif

25
code/query/expression.cxx Normal file
View File

@ -0,0 +1,25 @@
#include <code/query/expression.hxx>
namespace code::query
{
source_location_t const&
expression_t::
origin() const
{
return container_->origin_();
}
void
accept(expression_t const& e, visitor_t& v)
{
e.container_->accept_(v);
}
string
to_string(expression_t const& e)
{
return e.container_->to_string_();
}
} // namespace code::query

95
code/query/expression.hxx Normal file
View File

@ -0,0 +1,95 @@
#ifndef code__query__expression_hxx_
#define code__query__expression_hxx_
#include <code/query/concepts.hxx>
#include <code/query/error.hxx>
#include <code/query/source-location.hxx>
#include <code/query/types.hxx>
#include <code/query/visitor.hxx>
namespace code::query
{
class expression_t
{
public:
template<Expression E>
expression_t(E e)
: container_{make_shared<container_t<E>>(move(e))}
{}
source_location_t const&
origin() const;
friend
void
accept(expression_t const& e, visitor_t& v);
friend
string
to_string(expression_t const& e);
private:
struct abstract_t
{
virtual
~abstract_t() noexcept = default;
virtual
source_location_t const&
origin_() const = 0;
virtual
void
accept_(visitor_t& v) const = 0;
virtual
string
to_string_() const = 0;
};
template<Expression E>
struct container_t
: abstract_t
{
explicit
container_t(E e)
: e{move(e)}
{}
~container_t() noexcept override
{}
source_location_t const&
origin_() const override
{
return e.origin();
}
void
accept_(visitor_t& v) const override
{
if (auto c = dynamic_cast<basic_visitor_t<E>*>(&v); c) {
c->visit(e);
return;
}
v.visit_default();
}
string
to_string_() const override
{
return to_string(e);
}
E e;
};
shared_ptr<abstract_t const> container_;
};
} // namespace code::query
#endif

View File

@ -0,0 +1,124 @@
#include <code/query/lexical-analyzer.hxx>
#include <sstream>
namespace code::query
{
lexical_analyzer_t::
~lexical_analyzer_t() noexcept = default;
token_t
lexical_analyzer_t::
peek()
{
if (!current_)
current_ = extract();
return *current_;
}
void
lexical_analyzer_t::
consume()
{
current_ = extract();
}
lexical_analyzer_t::
lexical_analyzer_t() = default;
token_t
lexical_analyzer_t::
extract()
{
char_type c = peek_char();
while (is_whitespace(c)) {
consume_char();
c = peek_char();
}
if (c == 0)
return token_t{token_type_t::end};
if (c == ':') {
consume_char();
return token_t{token_type_t::colon};
}
if (c == '(') {
consume_char();
return token_t{token_type_t::open_parens};
}
if (c == ')') {
consume_char();
return token_t{token_type_t::close_parens};
}
if (c == '"') {
consume_char();
c = peek_char();
std::stringstream str;
while (c != 0 && c != '"') {
str << (char)c; // FIXME: UTF-8 encode.
consume_char();
c = peek_char();
}
consume_char();
return token_t{token_type_t::quoted_term, move(str.str())};
}
std::stringstream str;
while (c != 0 && !is_whitespace(c) && c != '(' && c != ')' && c != ':') {
consume_char();
str << (char)c; // FIXME: UFT-8 encode.
c = peek_char();
}
auto term = str.str();
if (term == "AND")
return token_t{token_type_t::logical_and};
if (term == "OR")
return token_t{token_type_t::logical_or};
if (term == "NOT")
return token_t{token_type_t::logical_not};
return token_t{token_type_t::simple_term, move(term)};
}
lexical_analyzer_t::char_type
lexical_analyzer_t::
peek_char()
{
if (!next_char_)
next_char_ = extract_char();
return *next_char_;
}
void
lexical_analyzer_t::
consume_char()
{
next_char_ = extract_char();
}
bool
lexical_analyzer_t::
is_whitespace(char_type c)
{
return c == ' ' || c == '\t' || c == '\v';
}
} // namespace code::query

View File

@ -0,0 +1,94 @@
#ifndef code__query__lexical_analyzer_hxx_
#define code__query__lexical_analyzer_hxx_
#include <code/query/token.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class lexical_analyzer_t
{
public:
using char_type = uint32_t;
virtual
~lexical_analyzer_t() noexcept;
token_t
peek();
void
consume();
protected:
lexical_analyzer_t();
lexical_analyzer_t(lexical_analyzer_t const&) = delete;
lexical_analyzer_t(lexical_analyzer_t&&) = delete;
token_t
extract();
char_type
peek_char();
void
consume_char();
virtual
char_type
extract_char() = 0;
lexical_analyzer_t& operator=(lexical_analyzer_t const&) = delete;
lexical_analyzer_t& operator=(lexical_analyzer_t&&) = delete;
static
bool
is_whitespace(char_type);
private:
optional<char_type> next_char_;
optional<token_t> current_;
};
template<typename Iterator, typename EndIterator = Iterator>
class basic_lexical_analyzer_t
: public lexical_analyzer_t
{
public:
using iterator = Iterator;
using end_iterator = EndIterator;
basic_lexical_analyzer_t(iterator, end_iterator);
basic_lexical_analyzer_t(basic_lexical_analyzer_t const&);
basic_lexical_analyzer_t(basic_lexical_analyzer_t&&);
~basic_lexical_analyzer_t() noexcept override;
basic_lexical_analyzer_t&
operator=(basic_lexical_analyzer_t const&) = delete;
basic_lexical_analyzer_t&
operator=(basic_lexical_analyzer_t&&) = delete;
protected:
char_type
extract_char() override;
private:
iterator current_;
end_iterator end_;
};
using string_lexical_analyzer_t = basic_lexical_analyzer_t<string::const_iterator>;
} // namespace code::query
#include <code/query/lexical-analyzer.txx>
#endif

View File

@ -0,0 +1,40 @@
namespace code::query
{
template<typename I, typename E>
basic_lexical_analyzer_t<I, E>::
basic_lexical_analyzer_t(iterator begin, end_iterator end)
: current_{move(begin)},
end_{move(end)}
{}
template<typename I, typename E>
basic_lexical_analyzer_t<I, E>::
basic_lexical_analyzer_t(basic_lexical_analyzer_t const& other)
: current_{other.current_},
end_{other.end_}
{}
template<typename I, typename E>
basic_lexical_analyzer_t<I, E>::
basic_lexical_analyzer_t(basic_lexical_analyzer_t&& other)
: current_{move(other.current_)},
end_{move(other.end_)}
{}
template<typename I, typename E>
basic_lexical_analyzer_t<I, E>::
~basic_lexical_analyzer_t() noexcept = default;
template<typename I, typename E>
basic_lexical_analyzer_t<I, E>::char_type
basic_lexical_analyzer_t<I, E>::
extract_char()
{
if (current_ == end_)
return 0;
return *current_++;
}
} // namespace code::query

View File

@ -0,0 +1,56 @@
#include <code/query/logical-and.hxx>
#include <sstream>
namespace code::query
{
logical_and_t::
logical_and_t(expression_t left, expression_t right)
: left_{move(left)},
right_{move(right)}
{}
logical_and_t::
logical_and_t(source_location_t origin,
expression_t left,
expression_t right)
: origin_{move(origin)},
left_{move(left)},
right_{move(right)}
{}
source_location_t const&
logical_and_t::
origin() const
{
return origin_;
}
expression_t const&
logical_and_t::
left() const
{
return left_;
}
expression_t const&
logical_and_t::
right() const
{
return right_;
}
string
to_string(logical_and_t const& e)
{
std::stringstream str;
str << to_string(e.left());
str << " AND ";
str << to_string(e.right());
return str.str();
}
} // namespace code::query

View File

@ -0,0 +1,39 @@
#ifndef code__query__logical_and_hxx_
#define code__query__logical_and_hxx_
#include <code/query/expression.hxx>
#include <code/query/source-location.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class logical_and_t
{
public:
logical_and_t(expression_t, expression_t);
logical_and_t(source_location_t, expression_t, expression_t);
source_location_t const&
origin() const;
expression_t const&
left() const;
expression_t const&
right() const;
private:
source_location_t origin_;
expression_t left_;
expression_t right_;
};
string
to_string(logical_and_t const);
} // namespace code::query
#endif

View File

@ -0,0 +1,43 @@
#include <code/query/logical-not.hxx>
#include <sstream>
namespace code::query
{
logical_not_t::
logical_not_t(expression_t right)
: right_{move(right)}
{}
logical_not_t::
logical_not_t(source_location_t origin, expression_t right)
: origin_{move(origin)},
right_{move(right)}
{}
source_location_t const&
logical_not_t::
origin() const
{
return origin_;
}
expression_t const&
logical_not_t::
right() const
{
return right_;
}
string
to_string(logical_not_t const& e)
{
std::stringstream str;
str << "NOT " << to_string(e.right());
return str.str();
}
} // namespace code::query

View File

@ -0,0 +1,34 @@
#ifndef code__query__logical_not_t_hxx_
#define code__query__logical_not_t_hxx_
#include <code/query/expression.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class logical_not_t
{
public:
explicit
logical_not_t(expression_t);
logical_not_t(source_location_t, expression_t);
source_location_t const&
origin() const;
expression_t const&
right() const;
private:
source_location_t origin_;
expression_t right_;
};
string
to_string(logical_not_t const& e);
} // namespace code::query
#endif

56
code/query/logical-or.cxx Normal file
View File

@ -0,0 +1,56 @@
#include <code/query/logical-or.hxx>
#include <sstream>
namespace code::query
{
logical_or_t::
logical_or_t(expression_t left, expression_t right)
: left_{move(left)},
right_{move(right)}
{}
logical_or_t::
logical_or_t(source_location_t origin,
expression_t left,
expression_t right)
: origin_{move(origin)},
left_{move(left)},
right_{move(right)}
{}
source_location_t const&
logical_or_t::
origin() const
{
return origin_;
}
expression_t const&
logical_or_t::
left() const
{
return left_;
}
expression_t const&
logical_or_t::
right() const
{
return right_;
}
string
to_string(logical_or_t const& e)
{
std::stringstream str;
str << to_string(e.left());
str << " or ";
str << to_string(e.right());
return str.str();
}
} // namespace code::query

39
code/query/logical-or.hxx Normal file
View File

@ -0,0 +1,39 @@
#ifndef code__query__logical_or_hxx_
#define code__query__logical_or_hxx_
#include <code/query/types.hxx>
#include <code/query/expression.hxx>
#include <code/query/source-location.hxx>
namespace code::query
{
class logical_or_t
{
public:
logical_or_t(expression_t, expression_t);
logical_or_t(source_location_t, expression_t, expression_t);
source_location_t const&
origin() const;
expression_t const&
left() const;
expression_t const&
right() const;
private:
source_location_t origin_;
expression_t left_;
expression_t right_;
};
string
to_string(logical_or_t const);
} // namespace code::query
#endif

97
code/query/match.cxx Normal file
View File

@ -0,0 +1,97 @@
#include <code/query/match.hxx>
#include <code/query/boolean.hxx>
#include <code/query/logical-and.hxx>
#include <code/query/logical-not.hxx>
#include <code/query/logical-or.hxx>
#include <code/query/parenthesized.hxx>
#include <code/query/term.hxx>
namespace code::query
{
bool
match(query_t const& q, predicate_t const& p)
{
class matcher_t
: public visitor_t,
public basic_visitor_t<term_t>,
public basic_visitor_t<boolean_t>,
public basic_visitor_t<tag_t>,
public basic_visitor_t<logical_and_t>,
public basic_visitor_t<logical_not_t>,
public basic_visitor_t<logical_or_t>,
public basic_visitor_t<parenthesized_t>
{
public:
static
bool
match(expression_t const& e, predicate_t const& p)
{
matcher_t m{p};
accept(e, m);
return m.result;
}
private:
explicit
matcher_t(predicate_t const& p)
: predicate_{p}
{}
void
visit_default() override
{
result = false;
}
void
visit(term_t const& e) override
{
result = predicate_(e);
}
void
visit(tag_t const& e) override
{
result = predicate_(e);
}
void
visit(boolean_t const& e) override
{
result = e.value();
}
void
visit(logical_and_t const& e) override
{
result = match(e.left(), predicate_) && match(e.right(), predicate_);
}
void
visit(logical_or_t const& e) override
{
result = match(e.left(), predicate_) || match(e.right(), predicate_);
}
void
visit(logical_not_t const& e) override
{
result = !match(e.right(), predicate_);
}
void
visit(parenthesized_t const& e) override
{
result = match(e.expr(), predicate_);
}
predicate_t const& predicate_;
bool result{};
};
return matcher_t::match(q.expr(), p);
}
} // namespace code::query

22
code/query/match.hxx Normal file
View File

@ -0,0 +1,22 @@
#ifndef code__query__match_hxx_
#define code__query__match_hxx_
#include <code/query/types.hxx>
#include <code/query/query.hxx>
#include <code/query/tag.hxx>
#include <code/query/term.hxx>
namespace code::query
{
using predicate_t = function<
bool(variant<term_t, tag_t> const&)
>;
bool
match(query_t const& q, predicate_t const& p);
} // namespace code::query
#endif

View File

@ -0,0 +1,43 @@
#include <code/query/parenthesized.hxx>
#include <sstream>
namespace code::query
{
parenthesized_t::
parenthesized_t(expression_t expr)
: expr_{move(expr)}
{}
parenthesized_t::
parenthesized_t(source_location_t origin, expression_t expr)
: origin_{move(origin)},
expr_{move(expr)}
{}
source_location_t const&
parenthesized_t::
origin() const
{
return origin_;
}
expression_t const&
parenthesized_t::
expr() const
{
return expr_;
}
string
to_string(parenthesized_t const& e)
{
std::stringstream str;
str << '(' << to_string(e.expr()) << ')';
return str.str();
}
} // namespace code::query

View File

@ -0,0 +1,35 @@
#ifndef code__query__parenthesized_hxx_
#define code__query__parenthesized_hxx_
#include <code/query/expression.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class parenthesized_t
{
public:
explicit
parenthesized_t(expression_t);
parenthesized_t(source_location_t, expression_t);
source_location_t const&
origin() const;
expression_t const&
expr() const;
private:
source_location_t origin_;
expression_t expr_;
};
string
to_string(parenthesized_t const& e);
} // namespace code::query
#endif

View File

@ -0,0 +1,38 @@
#include <code/query/parse-context.hxx>
namespace code::query
{
parse_context_t::
parse_context_t()
{}
vector<warning_t> const&
parse_context_t::
warnings() const
{
return warnings_;
}
vector<error_t> const&
parse_context_t::
errors() const
{
return errors_;
}
void
parse_context_t::
report_warning(warning_t w)
{
warnings_.emplace_back(move(w));
}
void
parse_context_t::
report_error(error_t e)
{
errors_.emplace_back(move(e));
}
} // namespace code::query

View File

@ -0,0 +1,36 @@
#ifndef code__query__parse_context_hxx_
#define code__query__parse_context_hxx_
#include <code/query/error.hxx>
#include <code/query/source-location.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class parse_context_t
{
public:
parse_context_t();
vector<warning_t> const&
warnings() const;
vector<error_t> const&
errors() const;
void
report_warning(warning_t);
void
report_error(error_t);
private:
vector<warning_t> warnings_;
vector<error_t> errors_;
};
} // namespace code::query
#endif

17
code/query/parse.cxx Normal file
View File

@ -0,0 +1,17 @@
#include <code/query/parse.hxx>
#include <code/query/lexical-analyzer.hxx>
#include <code/query/syntactical-analyzer.hxx>
namespace code::query
{
optional<query_t>
try_parse(string const& q, parse_context_t& context)
{
string_lexical_analyzer_t lexer{q.begin(), q.end()};
return syntactical_analyzer_t{lexer, context}.try_parse();
}
} // namespace code::query

16
code/query/parse.hxx Normal file
View File

@ -0,0 +1,16 @@
#ifndef code__query__parse_hxx_
#define code__query__parse_hxx_
#include <code/query/parse-context.hxx>
#include <code/query/query.hxx>
#include <code/query/types.hxx>
namespace code::query
{
optional<query_t>
try_parse(string const&, parse_context_t&);
} // namespace code::query
#endif

48
code/query/query.cxx Normal file
View File

@ -0,0 +1,48 @@
#include <code/query/query.hxx>
namespace code::query
{
query_t::
query_t(expression_t e,
vector<warning_t> warnings,
vector<error_t> errors)
: expr_{move(e)},
warnings_{move(warnings)},
errors_{move(errors)}
{}
expression_t const&
query_t::
expr() const
{
return expr_;
}
vector<warning_t> const&
query_t::
warnings() const
{
return warnings_;
}
vector<error_t> const&
query_t::
errors() const
{
return errors_;
}
void
accept(query_t const& q, visitor_t& v)
{
accept(q.expr(), v);
}
string
to_string(query_t const& q)
{
return to_string(q.expr());
}
} // namespace code::query

43
code/query/query.hxx Normal file
View File

@ -0,0 +1,43 @@
#ifndef code__query__query_hxx_
#define code__query__query_hxx_
#include <code/query/error.hxx>
#include <code/query/expression.hxx>
#include <code/query/types.hxx>
#include <code/query/visitor.hxx>
namespace code::query
{
class query_t
{
public:
query_t(expression_t e,
vector<warning_t> warnings,
vector<error_t> errors);
expression_t const&
expr() const;
vector<warning_t> const&
warnings() const;
vector<error_t> const&
errors() const;
private:
expression_t expr_;
vector<warning_t> warnings_;
vector<error_t> errors_;
};
void
accept(query_t const& q, visitor_t& v);
string
to_string(query_t const& q);
} // namespace code::query
#endif

View File

@ -0,0 +1,19 @@
#ifndef code__query__source_location_hxx_
#define code__query__source_location_hxx_
#include <code/query/types.hxx>
namespace code::query
{
struct source_location_t
{
string name;
uint32_t row{};
uint32_t column{};
};
} // namespace code::query
#endif

View File

@ -0,0 +1,245 @@
#include <code/query/boolean.hxx>
#include <code/query/logical-and.hxx>
#include <code/query/logical-not.hxx>
#include <code/query/logical-or.hxx>
#include <code/query/parenthesized.hxx>
#include <code/query/syntactical-analyzer.hxx>
#include <code/query/tag.hxx>
#include <code/query/term.hxx>
namespace code::query
{
syntactical_analyzer_t::
syntactical_analyzer_t(lexical_analyzer_t& lexer,
parse_context_t& context)
: lexer_{lexer},
context_{context}
{}
lexical_analyzer_t&
syntactical_analyzer_t::
lexer()
{
return lexer_;
}
parse_context_t&
syntactical_analyzer_t::
context()
{
return context_;
}
optional<query_t>
syntactical_analyzer_t::
try_parse()
{
auto expr = try_parse_expression();
if (expr) {
auto last = lexer().peek();
if (last.type() != token_type_t::end) {
context().report_warning({{}, "trailing token at end of query"});
}
return query_t{
*move(expr),
context().warnings(),
context().errors()
};
}
return nullopt;
}
optional<expression_t>
syntactical_analyzer_t::
try_parse_primary_expression()
{
optional<expression_t> expr;
for (;;) {
auto t = lexer().peek();
if (t.type() == token_type_t::logical_not) {
lexer().consume();
auto rhs = try_parse_primary_expression();
if (!rhs) {
context().report_warning({{}, "expected expression after NOT"});
break;
}
if (expr) {
expr = logical_and_t{
source_location_t{}, *expr, logical_not_t{{}, *rhs}
};
}
else {
expr = logical_not_t{{}, *rhs};
}
}
else if (t.type() == token_type_t::simple_term) {
lexer().consume();
// If the next token is a ':' then this is a tag.
//
if (lexer().peek().type() == token_type_t::colon) {
lexer().consume();
auto identifier = *t.value();
// Next expect a term or quoted term.
//
t = lexer().peek();
if (t.type() != token_type_t::simple_term &&
t.type() != token_type_t::quoted_term) {
context().report_warning({{}, "expected simple-term or quoted-term after tag" });
break;
}
lexer().consume();
// Construct the term
//
term_t term{
{},
t.type() == token_type_t::simple_term ? term_t::simple : term_t::quoted,
*t.value()
};
if (expr) {
expr = logical_and_t{
{}, *expr, tag_t{{}, move(identifier), move(term)}
};
}
else {
expr = tag_t{{}, move(identifier), move(term)};
}
}
else if (expr) {
expr = logical_and_t{
{}, *expr, term_t{{}, term_t::simple, *t.value()}
};
}
else {
expr = term_t{{}, term_t::simple, *t.value()};
}
}
else if (t.type() == token_type_t::quoted_term) {
if (expr) {
expr = logical_and_t{
{}, *expr, term_t{{}, term_t::quoted, *t.value()}
};
}
else {
expr = term_t{{}, term_t::quoted, *t.value()};
}
lexer().consume();
}
else if (t.type() == token_type_t::open_parens) {
lexer().consume();
t = lexer().peek();
if (t.type() == token_type_t::close_parens) {
lexer().consume();
continue;
}
auto next_expr = try_parse_expression();
if (!next_expr) {
context().report_warning({{}, "expected expression inside parenthesis"});
break;
}
next_expr = parenthesized_t{{}, *next_expr};
if (expr) {
expr = logical_and_t{{}, *expr, *next_expr};
}
else {
expr = next_expr;
}
t = lexer().peek();
if (t.type() != token_type_t::close_parens) {
context().report_warning({{}, "expected end parenthesis"});
break;
}
lexer().consume();
}
else {
break;
}
}
return expr;
}
optional<expression_t>
syntactical_analyzer_t::
try_parse_logical_and()
{
auto lhs = try_parse_primary_expression();
if (!lhs)
return nullopt;
while (lexer().peek().type() == token_type_t::logical_and) {
lexer().consume();
auto rhs = try_parse_primary_expression();
if (!rhs) {
context().report_warning({{}, "expected expression after AND"});
return lhs;
}
lhs = logical_and_t{{}, *lhs, *rhs};
}
return lhs;
}
optional<expression_t>
syntactical_analyzer_t::
try_parse_logical_or()
{
auto lhs = try_parse_logical_and();
if (!lhs)
return nullopt;
while (lexer().peek().type() == token_type_t::logical_or) {
lexer().consume();
auto rhs = try_parse_logical_and();
if (!rhs)
return nullopt;
lhs = logical_or_t{{}, *lhs, *rhs};
}
return lhs;
}
optional<expression_t>
syntactical_analyzer_t::
try_parse_expression()
{
return try_parse_logical_or();
}
} // namespace code::query

View File

@ -0,0 +1,48 @@
#ifndef code__query__syntactical_analyzer_hxx_
#define code__query__syntactical_analyzer_hxx_
#include <code/query/expression.hxx>
#include <code/query/lexical-analyzer.hxx>
#include <code/query/parse-context.hxx>
#include <code/query/query.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class syntactical_analyzer_t
{
public:
syntactical_analyzer_t(lexical_analyzer_t&, parse_context_t&);
lexical_analyzer_t&
lexer();
parse_context_t&
context();
optional<query_t>
try_parse();
private:
optional<expression_t>
try_parse_primary_expression();
optional<expression_t>
try_parse_logical_and();
optional<expression_t>
try_parse_logical_or();
optional<expression_t>
try_parse_expression();
private:
lexical_analyzer_t& lexer_;
parse_context_t& context_;
};
} // namespace code::query
#endif

48
code/query/tag.cxx Normal file
View File

@ -0,0 +1,48 @@
#include <code/query/tag.hxx>
namespace code::query
{
tag_t::
tag_t(string identifier, term_t value)
: identifier_{move(identifier)},
value_{move(value)}
{}
tag_t::
tag_t(source_location_t origin,
string identifier,
term_t value)
: origin_{move(origin)},
identifier_{move(identifier)},
value_{move(value)}
{}
source_location_t const&
tag_t::
origin() const
{
return origin_;
}
string const&
tag_t::
identifier() const
{
return identifier_;
}
term_t const&
tag_t::
value() const
{
return value_;
}
string
to_string(tag_t const& e)
{
return e.identifier() + ":" + to_string(e.value());
}
} // namespace code::query

40
code/query/tag.hxx Normal file
View File

@ -0,0 +1,40 @@
#ifndef code__query__tag_hxx_
#define code__query__tag_hxx_
#include <code/query/expression.hxx>
#include <code/query/source-location.hxx>
#include <code/query/term.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class tag_t
{
public:
tag_t(string, term_t);
tag_t(source_location_t, string, term_t);
source_location_t const&
origin() const;
string const&
identifier() const;
term_t const&
value() const;
private:
source_location_t origin_;
string identifier_;
term_t value_;
};
string
to_string(tag_t const& e);
} // namespace imperium::contract::query
#endif

53
code/query/term.cxx Normal file
View File

@ -0,0 +1,53 @@
#include <code/query/term.hxx>
namespace code::query
{
term_t::
term_t(term_type_t type, string value)
: type_{type},
value_{move(value)}
{}
term_t::
term_t(source_location_t origin,
term_type_t type,
string value)
: origin_{move(origin)},
type_{type},
value_{move(value)}
{}
source_location_t const&
term_t::
origin() const
{
return origin_;
}
term_t::term_type_t
term_t::
type() const
{
return type_;
}
string const&
term_t::
value() const
{
return value_;
}
string
to_string(term_t const& term)
{
if (term.type() == term_t::simple)
return term.value();
// fixme: escape term.value()?
//
return "\"" + term.value() + "\"";
}
} // namespace code::query

45
code/query/term.hxx Normal file
View File

@ -0,0 +1,45 @@
#ifndef code__query__term_hxx_
#define code__query__term_hxx_
#include <code/query/expression.hxx>
#include <code/query/source-location.hxx>
#include <code/query/types.hxx>
namespace code::query
{
class term_t
{
public:
enum term_type_t
{
simple,
quoted
};
term_t(term_type_t, string);
term_t(source_location_t, term_type_t, string);
source_location_t const&
origin() const;
term_type_t
type() const;
string const&
value() const;
private:
source_location_t origin_;
term_type_t type_;
string value_;
};
string
to_string(term_t const&);
} // namespace code::query
#endif

26
code/query/token.cxx Normal file
View File

@ -0,0 +1,26 @@
#include <code/query/token.hxx>
namespace code::query
{
token_t::
token_t(token_type_t type, optional<string> value)
: type_{type},
value_{move(value)}
{}
token_type_t
token_t::
type() const
{
return type_;
}
optional<string> const&
token_t::
value() const
{
return value_;
}
} // namespace code::query

47
code/query/token.hxx Normal file
View File

@ -0,0 +1,47 @@
#ifndef code__query__token_hxx_
#define code__query__token_hxx_
#include <code/query/types.hxx>
namespace code::query
{
enum class token_type_t
{
end,
simple_term,
quoted_term,
colon,
logical_and,
logical_not,
logical_or,
open_parens,
close_parens
};
class token_t
{
public:
explicit
token_t(token_type_t, optional<string> = nullopt);
token_type_t
type() const;
optional<string> const&
value() const;
private:
token_type_t type_;
optional<string> value_;
};
} // namespace code::query
#endif

45
code/query/types.hxx Normal file
View File

@ -0,0 +1,45 @@
#ifndef code__query__types_hxx_
#define code__query__types_hxx_
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <variant>
#include <vector>
namespace code::query
{
using std::forward;
using std::move;
using std::int8_t;
using std::int16_t;
using std::int32_t;
using std::int64_t;
using std::uint8_t;
using std::uint16_t;
using std::uint32_t;
using std::uint64_t;
using std::make_shared;
using std::shared_ptr;
using std::weak_ptr;
using std::optional;
using std::nullopt;
using std::vector;
using std::variant;
using std::string;
using strings = vector<string>;
using std::function;
} // namespace code::query
#endif

37
code/query/version.hxx.in Normal file
View File

@ -0,0 +1,37 @@
#ifndef code__query__version_hxx_
#define code__query__version_hxx_
// The numeric version format is AAAAABBBBBCCCCCDDDE where:
//
// AAAAA - major version number
// BBBBB - minor version number
// CCCCC - bugfix version number
// DDD - code / beta (DDD + 500) version number
// E - final (0) / snapshot (1)
//
// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
//
// Version AAAAABBBBBCCCCCDDDE
//
// 0.1.0 0000000001000000000
// 0.1.2 0000000001000020000
// 1.2.3 0000100002000030000
// 2.2.0-a.1 0000200001999990010
// 3.0.0-b.2 0000299999999995020
// 2.2.0-a.1.z 0000200001999990011
//
#define LIBCODE_QUERY_VERSION $libcode_query.version.project_number$ULL
#define LIBCODE_QUERY_VERSION_STR "$libcode_query.version.project$"
#define LIBCODE_QUERY_VERSION_ID "$libcode_query.version.project_id$"
#define LIBCODE_QUERY_VERSION_FULL "$libcode_query.version$"
#define LIBCODE_QUERY_VERSION_MAJOR $libcode_query.version.major$
#define LIBCODE_QUERY_VERSION_MINOR $libcode_query.version.minor$
#define LIBCODE_QUERY_VERSION_PATCH $libcode_query.version.patch$
#define LIBCODE_QUERY_PRE_RELEASE $libcode_query.version.pre_release$
#define LIBCODE_QUERY_SNAPSHOT_SN $libcode_query.version.snapshot_sn$ULL
#define LIBCODE_QUERY_SNAPSHOT_ID "$libcode_query.version.snapshot_id$"
#endif

39
code/query/visitor.hxx Normal file
View File

@ -0,0 +1,39 @@
#ifndef code__query__visitor_hxx_
#define code__query__visitor_hxx_
namespace code::query
{
class visitor_t
{
public:
virtual
~visitor_t() noexcept = default;
virtual
void
visit_default() = 0;
protected:
visitor_t() = default;
};
template<typename V>
class basic_visitor_t
{
public:
virtual
void
visit(V const&) = 0;
protected:
basic_visitor_t() = default;
~basic_visitor_t() noexcept = default;
};
} // namespace code::query
#endif

11
manifest Normal file
View File

@ -0,0 +1,11 @@
: 1
name: libcode-query
version: 0.1.0-a.0.z
language: c++
summary: libcode-query C++ library
license: BSD-4-Clause
description-file: README.md
url: https://helloryan.se/code/
email: ryan@helloryan.se
depends: * build2 >= 0.17.0
depends: * bpkg >= 0.17.0

2
repositories.manifest Normal file
View File

@ -0,0 +1,2 @@
: 1
summary: libcode-query project repository

8
tests/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Test executables.
#
driver
# Testscript output directories (can be symlinks).
#
test
test-*

4
tests/build/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

View File

@ -0,0 +1,5 @@
project = # Unnamed tests subproject.
using config
using test
using dist

16
tests/build/root.build Normal file
View File

@ -0,0 +1,16 @@
cxx.std = latest
using cxx
hxx{*}: extension = hxx
ixx{*}: extension = ixx
txx{*}: extension = txx
cxx{*}: extension = cxx
# Every exe{} in this subproject is by default a test.
#
exe{*}: test = true
# The test target for cross-testing (running tests under Wine, etc).
#
test.target = $cxx.target

1
tests/buildfile Normal file
View File

@ -0,0 +1 @@
./: {*/ -build/}