Skip to content

Commit

Permalink
Add Gcode abstract syntax tree (AST) representations
Browse files Browse the repository at this point in the history
New classes are added to define the structure of Gcode commands, comments and related entities in an AST. This includes common structure like comments, commands and optional values plus specific types for the G0 and G1 instructions. The definitions allow parsing Gcode to construct an intermediate representation that can be further processed or translated. In addition to the AST implementation, the main application (translator_main) is updated to include a preliminary use of the parser, demonstrating its functionality. The commit also includes updates to CMakeLists.txt and conanfile.py to bring in necessary dependencies.

Contribute to CURA-10561
  • Loading branch information
jellespijker committed Oct 15, 2023
1 parent 8632ed7 commit 4c50256
Show file tree
Hide file tree
Showing 13 changed files with 262 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ message(STATUS "Configuring Dulcificum version: ${DULCIFICUM_VERSION}")
find_package(nlohmann_json REQUIRED)
find_package(spdlog REQUIRED)
find_package(range-v3 REQUIRED)
find_package(Boost REQUIRED)

# --- Setup the shared C++ mgjtp library ---
set(DULCIFICUM_SRC
Expand All @@ -35,6 +36,7 @@ target_link_libraries(dulcificum
PUBLIC
nlohmann_json::nlohmann_json
PRIVATE
boost::boost
range-v3::range-v3
spdlog::spdlog)

Expand Down
15 changes: 13 additions & 2 deletions apps/translator_main.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
#include "cmdline.h"

#include <fmt/ranges.h>
#include <spdlog/spdlog.h>

#include <docopt/docopt.h>
#include <dulcificum/gcode/ast/ast.h>
#include <dulcificum/utils/io.h>
#include <iostream>
#include <map>


int main(int argc, const char** argv)
{
constexpr bool show_help = true;
const std::map<std::string, docopt::value> args
= docopt::docopt(fmt::format(apps::cmdline::USAGE, apps::cmdline::NAME), { argv + 1, argv + argc }, show_help, apps::cmdline::VERSION_ID);

if (args.contains("--quiet"))
if (args.at("--quiet").asBool())
{
spdlog::set_level(spdlog::level::err);
}
else if (args.contains("--verbose"))
else if (args.at("--verbose").asBool())
{
spdlog::set_level(spdlog::level::debug);
}
Expand All @@ -24,4 +29,10 @@ int main(int argc, const char** argv)
spdlog::set_level(spdlog::level::info);
}
spdlog::info("Tasting the menu");

auto input{ dulcificum::utils::readFile(args.at("INPUT").asString()).value() };
std::vector<dulcificum::gcode::ast::command_t> parsedCommand;
bool r = boost::spirit::x3::phrase_parse(input.begin(), input.end(), *dulcificum::gcode::ast::all_rule, boost::spirit::x3::space, parsedCommand);

return 0;
}
3 changes: 3 additions & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def config_options(self):
del self.options.fPIC

def configure(self):
self.options["boost"].header_only = True
if self.options.shared:
self.options.rm_safe("fPIC")

Expand All @@ -81,6 +82,8 @@ def requirements(self):
self.requires("nlohmann_json/3.11.2", transitive_headers = True)
self.requires("range-v3/0.12.0")
self.requires("spdlog/1.10.0")
self.requires("boost/1.82.0")
self.requires("zlib/1.2.13")
if self.options.with_apps:
self.requires("docopt.cpp/0.6.3")
if self.options.with_python_bindings:
Expand Down
12 changes: 12 additions & 0 deletions include/dulcificum/gcode/ast/ast.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_AST_AST_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_AST_AST_H

#include "dulcificum/gcode/ast/words/words.h"

namespace dulcificum::gcode::ast
{
auto const all_rule = boost::spirit::x3::rule<struct class_all, command_t>{} = g0_rule | g1_rule; // NOLINT

}

#endif // DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_AST_AST_H
28 changes: 28 additions & 0 deletions include/dulcificum/gcode/ast/rules.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_AST_RULES_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_AST_RULES_H

#include <boost/spirit/home/x3.hpp>

namespace dulcificum::gcode::ast
{

// TODO: Figure out if the I should maybe use this pattern
//struct rules {
// const boost::spirit::x3::rule<struct x, double> x_rule;
//
// rules() : x_rule{ "x" }
// {
// x_rule = 'X' >> boost::spirit::x3::double_;
// }
//};
// NOLINTBEGIN
auto const x_rule = boost::spirit::x3::rule<struct x, double>{ "x" } = 'X' >> boost::spirit::x3::double_;
auto const y_rule = boost::spirit::x3::rule<struct y, double>{ "y" } = 'Y' >> boost::spirit::x3::double_;
auto const z_rule = boost::spirit::x3::rule<struct z, double>{ "z" } = 'Z' >> boost::spirit::x3::double_;
auto const e_rule = boost::spirit::x3::rule<struct e, double>{ "e" } = 'E' >> boost::spirit::x3::double_;
auto const f_rule = boost::spirit::x3::rule<struct f, double>{ "f" } = 'F' >> boost::spirit::x3::double_;
// NOLINTEND

} // namespace dulcificum::gcode::ast

#endif // DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_AST_RULES_H
39 changes: 39 additions & 0 deletions include/dulcificum/gcode/ast/words/G0.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_G0_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_G0_H
#include "dulcificum/gcode/ast/rules.h"
#include "dulcificum/gcode/ast/words/command.h"
#include "dulcificum/gcode/ast/words/optional_values.h"
namespace dulcificum::gcode::ast
{
struct G0 : public Command<"G0">
{
std::optional<double> X{}; ///< X coordinate
std::optional<double> Y{}; ///< Y coordinate
std::optional<double> Z{}; ///< Z coordinate
std::optional<double> F{}; ///< Feedrate
};


// Construct the parser rules

details::OptionalValues G0_VALUES;

auto G0_ASSIGN_OPTIONAL_VALUES = [](auto& ctx)
{
_val(ctx).X = G0_VALUES.val_map["x"];
_val(ctx).Y = G0_VALUES.val_map["y"];
_val(ctx).Z = G0_VALUES.val_map["z"];
_val(ctx).F = G0_VALUES.val_map["f"];
};

// clang-format off
auto const g0_rule = boost::spirit::x3::rule<struct g0, G0>{ "g0" } = G0::word.data()
>> *(x_rule[G0_VALUES.assign("x", G0_VALUES.val_map)]
| y_rule[G0_VALUES.assign("y", G0_VALUES.val_map)]
| z_rule[G0_VALUES.assign("z", G0_VALUES.val_map)]
| f_rule[G0_VALUES.assign("f", G0_VALUES.val_map)])
>> boost::spirit::x3::eps[G0_ASSIGN_OPTIONAL_VALUES];
// clang-format on

} // namespace dulcificum::gcode::ast
#endif // DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_G0_H
44 changes: 44 additions & 0 deletions include/dulcificum/gcode/ast/words/G1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_G1_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_G1_H

#include "dulcificum/gcode/ast/rules.h"
#include "dulcificum/gcode/ast/words/command.h"

namespace dulcificum::gcode::ast
{
struct G1 : public Command<"G1">
{
std::optional<double> X{}; ///< X coordinate
std::optional<double> Y{}; ///< Y coordinate
std::optional<double> Z{}; ///< Z coordinate
std::optional<double> E{}; ///< E position
std::optional<double> F{}; ///< Feedrate
};


// Construct the parser rules

details::OptionalValues G1_VALUES;

auto G1_ASSIGN_OPTIONAL_VALUES = [](auto& ctx)
{
_val(ctx).X = G0_VALUES.val_map["x"];
_val(ctx).Y = G0_VALUES.val_map["y"];
_val(ctx).Z = G0_VALUES.val_map["z"];
_val(ctx).E = G0_VALUES.val_map["e"];
_val(ctx).F = G0_VALUES.val_map["f"];
};

// clang-format off
auto const g1_rule = boost::spirit::x3::rule<struct g1, G1>{ "g1" } = G1::word.data()
>> *(x_rule[G1_VALUES.assign("x", G1_VALUES.val_map)]
| y_rule[G1_VALUES.assign("y", G1_VALUES.val_map)]
| z_rule[G1_VALUES.assign("z", G1_VALUES.val_map)]
| e_rule[G1_VALUES.assign("e", G1_VALUES.val_map)]
| f_rule[G1_VALUES.assign("f", G1_VALUES.val_map)])
>> boost::spirit::x3::eps[G1_ASSIGN_OPTIONAL_VALUES];
// clang-format on

} // namespace dulcificum::gcode::ast

#endif // DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_G1_H
21 changes: 21 additions & 0 deletions include/dulcificum/gcode/ast/words/command.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_COMMAND_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_COMMAND_H

#include "dulcificum/gcode/ast/words/comment.h"
#include "dulcificum/gcode/ast/words/entry.h"
#include "dulcificum/utils/char_range_literal.h"

#include <optional>

namespace dulcificum::gcode::ast
{
template<utils::CharRangeLiteral Word>
struct Command : public Entry
{
static constexpr std::string_view word{ Word.value }; ///< The specific word
std::optional<Comment> comment; ///< Optional comment related to the command
};

} // namespace dulcificum::gcode::details

#endif // DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_COMMAND_H
15 changes: 15 additions & 0 deletions include/dulcificum/gcode/ast/words/comment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_COMMENT_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_COMMENT_H

#include "dulcificum/gcode/ast/words/entry.h"

namespace dulcificum::gcode::ast
{
struct Comment : public Entry
{
static constexpr std::string_view word{ ";" };
std::string msg; ///< Message contained in the comment
};
} // namespace dulcificum::gcode::details

#endif // DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_COMMENT_H
17 changes: 17 additions & 0 deletions include/dulcificum/gcode/ast/words/entry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_ENTRY_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_ENTRY_H

#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>

namespace dulcificum::gcode::ast
{
struct Entry : public boost::spirit::x3::position_tagged
{
std::size_t index{ 0 }; ///< Represents the index in the source
std::string raw_value; ///< Raw value as represented in the source
};

} // namespace dulcificum::gcode::details


#endif // DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_ENTRY_H
29 changes: 29 additions & 0 deletions include/dulcificum/gcode/ast/words/optional_values.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_AST_WORDS_OPTIONAL_VALUES_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_AST_WORDS_OPTIONAL_VALUES_H

#include <map>
#include <string>

namespace dulcificum::gcode::ast::details
{

struct OptionalValues
{
std::map<std::string, double> val_map;
struct AssignCtxValToVar
{
template<typename T>
auto operator()(const std::string& key, T& map) const
{
return [&, key](auto& ctx)
{
map[key] = _attr(ctx);
};
}
} assign;
};

} // namespace dulcificum::gcode::ast::details


#endif // DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_AST_WORDS_OPTIONAL_VALUES_H
15 changes: 15 additions & 0 deletions include/dulcificum/gcode/ast/words/words.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_WORDS_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_WORDS_H

#include "dulcificum/gcode/ast/words/G0.h"
#include "dulcificum/gcode/ast/words/G1.h"

#include <variant>

namespace dulcificum::gcode::ast
{
using command_t = std::variant<G0, G1>;
}


#endif // DULCIFICUM_INCLUDE_DULCIFICUM_GCODE_WORDS_WORDS_H
24 changes: 24 additions & 0 deletions include/dulcificum/utils/char_range_literal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef DULCIFICUM_INCLUDE_DULCIFICUM_UTILS_CHAR_RANGE_LITERAL_H
#define DULCIFICUM_INCLUDE_DULCIFICUM_UTILS_CHAR_RANGE_LITERAL_H

#include <algorithm>
#include <cstddef>

namespace dulcificum::utils
{

// NOLINTBEGIN
template<std::size_t N>
struct CharRangeLiteral
{
constexpr CharRangeLiteral(const char (&str)[N]) noexcept
{
std::copy_n(str, N, value);
}
char value[N]; ///< The character array holding the literal
};
// NOLINTEND

} // namespace dulcificum::utils

#endif // DULCIFICUM_INCLUDE_DULCIFICUM_UTILS_CHAR_RANGE_LITERAL_H

0 comments on commit 4c50256

Please sign in to comment.