Skip to content

Commit

Permalink
Hotifx: cleanup.
Browse files Browse the repository at this point in the history
Add LICENSE and README.
Bug and typo fixes. Remove visitor.
  • Loading branch information
karnkaul committed Oct 6, 2023
1 parent e34ee0d commit 300c112
Show file tree
Hide file tree
Showing 14 changed files with 98 additions and 41 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.24)

project(kalcy VERSION 0.1)
project(kalcy VERSION 0.2)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Karn Kaul and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
## Arithmetic expression evaluator

`kalcy` is a simple expression parsing library built using C++20. In addition to numeric literals and operators, it supports an environment with customizable functions and constants.

**Sample output**

```
./kalcy-quickstart
usage: ./kalcy-quickstart [-v] "<expression>"
./kalcy-quickstart 42
42
./kalcy-quickstart -v "1 + 2 * 3 ^ 2"
19
expression : 1 + 2 * 3 ^ 2
AST : (1 + (2 * (3 ^ 2)))
./kalcy-quickstart -v "-2.5 * sqrt(pi)"
-4.43113
expression : -2.5 * sqrt(pi)
AST : (-2.5 * sqrt(pi))
```

### Requirements

- CMake 3.24+
- C++20 compiler and standard library (+ runtime).

### CMake

Acquire the source and add it to your project's build tree via CMake `FetchContent` or `git clone` / `git submodules` + `add_subdirectory`.

Then link to `kalcy`:

```cmake
target_link_libraries(your-target PRIVATE kalcy::kalcy)
```

All required headers are included in:

```cpp
#include <kalcy/kalcy.hpp>
```

### Quickstart

Check out [examples/quickstart](examples/quickstart/quickstart.cpp).
4 changes: 2 additions & 2 deletions examples/quickstart/quickstart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace {
auto run(std::string_view const text, bool verbose) -> bool {
try {
// parse text into an expression
auto expr = kalcy::Parser{text}.parse();
auto expr = kalcy::Parser{}.parse(text);
assert(expr != nullptr);
// evaluate parsed expression
// a custom Env can be used and passed, if desired
Expand All @@ -33,7 +33,7 @@ auto main(int argc, char** argv) -> int {

bool verbose{};
// check if verbose
if (args.front() == std::string_view{"-v"}) {
if (!args.empty() && args.front() == std::string_view{"-v"}) {
verbose = true;
// advance args by 1
args = args.subspan(1);
Expand Down
2 changes: 1 addition & 1 deletion kalcy/include/kalcy/env.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Env {
/// \returns Return value of invoked function.
/// \param name Name of function.
/// \param args Arguments to function.
/// \throws UndefinedSymbol if name is not defined as a function.
/// \throws UndefinedSymbol, ArgsMismatch.
///
[[nodiscard]] auto invoke(Token name, std::span<double const> args) const noexcept(false) -> double;

Expand Down
4 changes: 2 additions & 2 deletions kalcy/include/kalcy/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct ArgsMismatch : Error {
explicit ArgsMismatch(Token token, std::size_t argument_count);
};

struct InvalidOperaor : Error {
explicit InvalidOperaor(Token token);
struct InvalidOperator : Error {
explicit InvalidOperator(Token token);
};
} // namespace kalcy
17 changes: 7 additions & 10 deletions kalcy/include/kalcy/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,18 @@ namespace kalcy {
class Parser {
public:
///
/// \brief Construct a parser for passed text.
/// \brief Parse tokens into an expression.
/// \param text text to parse.
/// \returns Parsed expression (or null if no tokens).
/// \throws ParseError.
///
/// Caller must ensure the source of text outlives all expressions parsed from it.
///
explicit Parser(std::string_view text);

///
/// \brief Parse tokens into an expression.
/// \returns Parsed expression (or null if no tokens).
/// \throws Error on parse error.
///
auto parse() noexcept(false) -> UExpr;
auto parse(std::string_view text) noexcept(false) -> UExpr;

private:
void reset(std::string_view text);

[[nodiscard]] auto expression() -> UExpr;
[[nodiscard]] auto sum() -> UExpr;
[[nodiscard]] auto product() -> UExpr;
Expand All @@ -37,7 +34,7 @@ class Parser {
auto consume(Token::Type type) noexcept(false) -> void;
void advance();

Scanner m_scanner;
Scanner m_scanner{};

Token m_previous{};
Token m_current{};
Expand Down
2 changes: 1 addition & 1 deletion kalcy/include/kalcy/scanner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Scanner {
///
/// Caller must ensure source of text outlives all tokens scanned from it.
///
explicit Scanner(std::string_view text) : m_text(text) {}
explicit Scanner(std::string_view text = {}) : m_text(text) {}

///
/// \brief Check if no more tokens remain.
Expand Down
14 changes: 0 additions & 14 deletions kalcy/include/kalcy/visitor.hpp

This file was deleted.

2 changes: 1 addition & 1 deletion kalcy/src/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ UndefinedSymbol::UndefinedSymbol(Token const token) : Error(token, std::format("
ArgsMismatch::ArgsMismatch(Token const token, std::size_t argument_count)
: Error(token, std::format("{} does not take {} argument(s)", token.lexeme, argument_count)), argument_count(argument_count) {}

InvalidOperaor::InvalidOperaor(Token const token) : Error(token, std::format("invalid operator: '{}'", token.lexeme)) {}
InvalidOperator::InvalidOperator(Token const token) : Error(token, std::format("invalid operator: '{}'", token.lexeme)) {}
} // namespace kalcy
5 changes: 2 additions & 3 deletions kalcy/src/eval.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include <kalcy/error.hpp>
#include <kalcy/eval.hpp>
#include <kalcy/visitor.hpp>
#include <cassert>
#include <cmath>
#include <format>
Expand All @@ -20,7 +19,7 @@ struct Evaluator {
}

auto operator()(expr::Unary const& unary) const -> double {
if (unary.op.type != Token::Type::eMinus) { throw InvalidOperaor{unary.op}; }
if (unary.op.type != Token::Type::eMinus) { throw InvalidOperator{unary.op}; }
return -Evaluator{env}.evaluate(*unary.right);
}

Expand All @@ -37,7 +36,7 @@ struct Evaluator {
default: break;
}

throw InvalidOperaor{binary.op};
throw InvalidOperator{binary.op};
}

auto operator()(expr::Call const& call) const -> double {
Expand Down
14 changes: 10 additions & 4 deletions kalcy/src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
#include <cassert>

namespace kalcy {
Parser::Parser(std::string_view text) : m_scanner(text), m_current(m_scanner.next()), m_next(m_scanner.next()) {}

auto Parser::parse() noexcept(false) -> UExpr {
auto Parser::parse(std::string_view text) noexcept(false) -> UExpr {
reset(text);
if (!m_current) { return {}; }
auto ret = expression();
if (m_current || m_next) {
Expand All @@ -16,14 +15,21 @@ auto Parser::parse() noexcept(false) -> UExpr {
return ret;
}

void Parser::reset(std::string_view const text) {
m_scanner = Scanner{text};
m_current = m_scanner.next();
m_next = m_scanner.next();
m_previous = {};
}

auto Parser::expression() -> UExpr { return sum(); }

auto Parser::sum() -> UExpr {
auto ret = product();

while (match(Token::Type::ePlus, Token::Type::eMinus)) {
auto const token = m_previous;
auto rhs = expression();
auto rhs = product();
ret = std::make_unique<Expr>(expr::Binary{.left = std::move(ret), .op = token, .right = std::move(rhs)});
}

Expand Down
2 changes: 1 addition & 1 deletion tests/tests/test_eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace {
using namespace kalcy;

auto make_expr(std::string_view text) -> UExpr { return Parser{text}.parse(); }
auto make_expr(std::string_view text) -> UExpr { return Parser{}.parse(text); }

ADD_TEST(Evaluate) {
auto const env = Env{};
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/test_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace {
using namespace kalcy;
auto parse(std::string_view const text) -> UExpr { return Parser{text}.parse(); }
auto parse(std::string_view const text) -> UExpr { return Parser{}.parse(text); }

ADD_TEST(ParseEmptyString) { EXPECT(parse("") == nullptr); }

Expand Down

0 comments on commit 300c112

Please sign in to comment.