Experiment: DNA: makesdna parser #118532
|
@ -1122,7 +1122,7 @@ typedef enum eDirEntry_SelectFlag {
|
|||
ENUM_OPERATORS(eDirEntry_SelectFlag, FILE_SEL_EDITING);
|
||||
|
||||
/* ***** Related to file browser, but never saved in DNA, only here to help with RNA. ***** */
|
||||
|
||||
#define DATETIME_STR_SIZE 16 + 8
|
||||
#
|
||||
#
|
||||
typedef struct FileDirEntry {
|
||||
|
@ -1139,7 +1139,7 @@ typedef struct FileDirEntry {
|
|||
struct {
|
||||
/* Temp caching of UI-generated strings. */
|
||||
char size_str[16];
|
||||
char datetime_str[16 + 8];
|
||||
char datetime_str[DATETIME_STR_SIZE];
|
||||
|
||||
} draw_data;
|
||||
|
||||
/** #eFileSel_File_Types. */
|
||||
|
|
|
@ -18,6 +18,7 @@ set(INC_SYS
|
|||
set(LIB
|
||||
PRIVATE bf::intern::atomic
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::extern::fmtlib
|
||||
)
|
||||
|
||||
add_definitions(-DWITH_DNA_GHASH)
|
||||
|
@ -67,6 +68,8 @@ set(SRC_BLENLIB
|
|||
|
||||
set(SRC
|
||||
dna_utils.cc
|
||||
dna_lexer.cc
|
||||
dna_parser.cc
|
||||
makesdna.cc
|
||||
${SRC_BLENLIB}
|
||||
../../../../intern/guardedalloc/intern/leak_detector.cc
|
||||
|
@ -123,6 +126,8 @@ add_custom_command(
|
|||
set(SRC
|
||||
dna_defaults.c
|
||||
dna_genfile.cc
|
||||
dna_lexer.cc
|
||||
dna_parser.cc
|
||||
dna_utils.cc
|
||||
${CMAKE_CURRENT_BINARY_DIR}/dna.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/dna_verify.c
|
||||
|
@ -130,6 +135,8 @@ set(SRC
|
|||
|
||||
${CMAKE_CURRENT_BINARY_DIR}/dna_type_offsets.h
|
||||
dna_rename_defs.h
|
||||
dna_lexer.hh
|
||||
dna_parser.hh
|
||||
dna_utils.h
|
||||
)
|
||||
|
||||
|
@ -164,6 +171,16 @@ set(SRC
|
|||
set(LIB
|
||||
PRIVATE bf::intern::atomic
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::extern::fmtlib
|
||||
)
|
||||
|
||||
blender_add_lib(bf_dna_blenlib "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
if(WITH_GTESTS)
|
||||
set(TEST_INC
|
||||
)
|
||||
set(TEST_SRC
|
||||
dna_parser_test.cc
|
||||
)
|
||||
blender_add_test_suite_lib(dna "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB}")
|
||||
endif()
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
#include "dna_lexer.hh"
|
||||
#include <cctype>
|
||||
#include <charconv>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace blender::dna::lex {
|
||||
|
||||
static std::string_view string_view_from_range(const std::string_view::iterator first,
|
||||
const std::string_view::iterator last)
|
||||
{
|
||||
return std::string_view{&*first, size_t(last - first)};
|
||||
}
|
||||
|
||||
/* Match any white space except break lines. */
|
||||
static void eval_space(std::string_view::iterator &itr,
|
||||
std::string_view::iterator last,
|
||||
TokenIterator & /*cont*/)
|
||||
{
|
||||
while (itr < last && itr[0] != '\n' && std::isspace(itr[0])) {
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Match break lines, added as tokens as they are needed for `#define` blocks. */
|
||||
static void eval_break_line(std::string_view::iterator &itr,
|
||||
std::string_view::iterator /*last*/,
|
||||
TokenIterator &cont)
|
||||
{
|
||||
if (itr[0] != '\n') {
|
||||
return;
|
||||
}
|
||||
cont.append(BreakLineToken{string_view_from_range(itr, itr + 1)});
|
||||
itr++;
|
||||
}
|
||||
|
||||
/* Match any identifier substring, also matches C++ keywords. */
|
||||
static void eval_identifier(std::string_view::iterator &itr,
|
||||
std::string_view::iterator last,
|
||||
TokenIterator &cont)
|
||||
{
|
||||
if (!(std::isalpha(itr[0]) || itr[0] == '_')) {
|
||||
return;
|
||||
}
|
||||
std::string_view::iterator start{itr++};
|
||||
while (itr < last && (std::isalnum(itr[0]) || itr[0] == '_')) {
|
||||
itr++;
|
||||
}
|
||||
struct KeywordItem {
|
||||
std::string_view word;
|
||||
KeywordType type;
|
||||
};
|
||||
using namespace std::string_view_literals;
|
||||
static constexpr KeywordItem keywords[]{
|
||||
{"BLI_STATIC_ASSERT_ALIGN"sv, KeywordType::BLI_STATIC_ASSERT_ALIGN},
|
||||
{"DNA_DEFINE_CXX_METHODS"sv, KeywordType::DNA_DEFINE_CXX_METHODS},
|
||||
{"DNA_DEPRECATED"sv, KeywordType::DNA_DEPRECATED},
|
||||
{"DNA_DEPRECATED_ALLOW"sv, KeywordType::DNA_DEPRECATED_ALLOW},
|
||||
{"ENUM_OPERATORS"sv, KeywordType::ENUM_OPERATORS},
|
||||
{"extern"sv, KeywordType::EXTERN},
|
||||
{"char"sv, KeywordType::CHAR},
|
||||
{"char16_t"sv, KeywordType::CHAR16_T},
|
||||
{"char32_t"sv, KeywordType::CHAR32_T},
|
||||
{"class"sv, KeywordType::CLASS},
|
||||
{"const"sv, KeywordType::CONST},
|
||||
{"define"sv, KeywordType::DEFINE},
|
||||
{"double"sv, KeywordType::DOUBLE},
|
||||
{"endif"sv, KeywordType::ENDIF},
|
||||
{"enum"sv, KeywordType::ENUM},
|
||||
{"float"sv, KeywordType::FLOAT},
|
||||
{"if"sv, KeywordType::IF},
|
||||
{"ifdef"sv, KeywordType::IFDEF},
|
||||
{"ifndef"sv, KeywordType::IFNDEF},
|
||||
{"include"sv, KeywordType::INCLUDE},
|
||||
{"int"sv, KeywordType::INT},
|
||||
{"int16_t"sv, KeywordType::INT16_T},
|
||||
{"int32_t"sv, KeywordType::INT32_T},
|
||||
{"int64_t"sv, KeywordType::INT64_T},
|
||||
{"int8_t"sv, KeywordType::INT8_T},
|
||||
{"long"sv, KeywordType::LONG},
|
||||
{"ulong"sv, KeywordType::ULONG},
|
||||
{"once"sv, KeywordType::ONCE},
|
||||
{"pragma"sv, KeywordType::PRAGMA},
|
||||
{"private"sv, KeywordType::PRIVATE},
|
||||
{"public"sv, KeywordType::PUBLIC},
|
||||
{"short"sv, KeywordType::SHORT},
|
||||
{"signed"sv, KeywordType::SIGNED},
|
||||
{"struct"sv, KeywordType::STRUCT},
|
||||
{"typedef"sv, KeywordType::TYPEDEF},
|
||||
{"uint16_t"sv, KeywordType::UINT16_T},
|
||||
{"uint32_t"sv, KeywordType::UINT32_T},
|
||||
{"uint64_t"sv, KeywordType::UINT64_T},
|
||||
{"uint8_t"sv, KeywordType::UINT8_T},
|
||||
{"unsigned"sv, KeywordType::UNSIGNED},
|
||||
{"void"sv, KeywordType::VOID},
|
||||
};
|
||||
|
||||
std::string_view str = string_view_from_range(start, itr);
|
||||
auto test_keyword_fn = [str](const KeywordItem &val) -> bool { return val.word == str; };
|
||||
const KeywordItem *keyword_itr = std::find_if(
|
||||
std::begin(keywords), std::end(keywords), test_keyword_fn);
|
||||
if (keyword_itr != std::end(keywords)) {
|
||||
cont.append(KeywordToken{str, keyword_itr->type});
|
||||
return;
|
||||
}
|
||||
cont.append(IdentifierToken{str});
|
||||
}
|
||||
|
||||
/* Match a line comment until break line. */
|
||||
static void eval_line_comment(std::string_view::iterator &itr,
|
||||
std::string_view::iterator last,
|
||||
TokenIterator & /*cont*/)
|
||||
{
|
||||
if (last - itr < 2) {
|
||||
return;
|
||||
}
|
||||
if (!(itr[0] == '/' && itr[1] == '/')) {
|
||||
return;
|
||||
}
|
||||
while (itr != last && itr[0] != '\n') {
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Match a int literal. */
|
||||
static void eval_int_literal(std::string_view::iterator &itr,
|
||||
std::string_view::iterator last,
|
||||
TokenIterator &cont)
|
||||
{
|
||||
const std::string_view::iterator start{itr};
|
||||
while (itr < last && std::isdigit(itr[0])) {
|
||||
itr++;
|
||||
}
|
||||
if (itr == start) {
|
||||
return;
|
||||
}
|
||||
int val{};
|
||||
std::from_chars(&*start, &*itr, val);
|
||||
cont.append(IntLiteralToken{string_view_from_range(start, itr), val});
|
||||
}
|
||||
|
||||
/* Match a c-style comment. */
|
||||
static void eval_multiline_comment(std::string_view::iterator &itr,
|
||||
std::string_view::iterator last,
|
||||
TokenIterator & /*cont*/)
|
||||
{
|
||||
if (last - itr < +2) {
|
||||
return;
|
||||
}
|
||||
if (!(itr[0] == '/' && itr[1] == '*')) {
|
||||
return;
|
||||
}
|
||||
char carry = itr[0];
|
||||
itr += 2;
|
||||
while (itr < last && !(carry == '*' && itr[0] == '/')) {
|
||||
carry = itr[0];
|
||||
itr++;
|
||||
}
|
||||
if (itr < last) {
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Match a symbol. */
|
||||
static void eval_symbol(std::string_view::iterator &itr,
|
||||
std::string_view::iterator /*last*/,
|
||||
TokenIterator &cont)
|
||||
{
|
||||
struct SymbolItem {
|
||||
char value;
|
||||
SymbolType type;
|
||||
};
|
||||
static constexpr SymbolItem symbols[]{
|
||||
{'!', SymbolType::EXCLAMATION}, {'#', SymbolType::HASH}, {'%', SymbolType::PERCENT},
|
||||
{'&', SymbolType::BIT_AND}, {'(', SymbolType::LPAREN}, {')', SymbolType::RPAREN},
|
||||
{'*', SymbolType::STAR}, {'+', SymbolType::PLUS}, {',', SymbolType::COMMA},
|
||||
{'-', SymbolType::MINUS}, {'.', SymbolType::DOT}, {'/', SymbolType::SLASH},
|
||||
{':', SymbolType::COLON}, {';', SymbolType::SEMICOLON}, {'<', SymbolType::LESS},
|
||||
{'=', SymbolType::ASSIGN}, {'>', SymbolType::GREATER}, {'?', SymbolType::QUESTION},
|
||||
{'[', SymbolType::LBRACKET}, {'\\', SymbolType::BACKSLASH}, {']', SymbolType::RBRACKET},
|
||||
{'^', SymbolType::CARET}, {'{', SymbolType::LBRACE}, {'|', SymbolType::BIT_OR},
|
||||
{'}', SymbolType::RBRACE}, {'~', SymbolType::TILDE},
|
||||
};
|
||||
const char value = itr[0];
|
||||
auto test_symbol = [value](const SymbolItem &item) -> bool { return item.value == value; };
|
||||
const SymbolItem *symbol_itr = std::find_if(std::begin(symbols), std::end(symbols), test_symbol);
|
||||
if (symbol_itr != std::end(symbols)) {
|
||||
cont.append(SymbolToken{string_view_from_range(itr, itr + 1), symbol_itr->type});
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Match a string or char literal. */
|
||||
static void eval_string_literal(std::string_view::iterator &itr,
|
||||
std::string_view::iterator last,
|
||||
TokenIterator &cont)
|
||||
{
|
||||
const char opening = itr[0];
|
||||
if (!(opening == '"' || opening == '\'')) {
|
||||
return;
|
||||
}
|
||||
const std::string_view::iterator start{itr++};
|
||||
bool scape{false};
|
||||
while (itr < last && !(!scape && itr[0] == opening)) {
|
||||
scape = itr[0] == '\\' && !scape;
|
||||
itr++;
|
||||
}
|
||||
if (!(itr < last)) {
|
||||
itr = start;
|
||||
return;
|
||||
}
|
||||
itr++;
|
||||
cont.append(StringLiteralToken{string_view_from_range(start, itr)});
|
||||
}
|
||||
|
||||
void TokenIterator::print_unkown_token(std::string_view filepath,
|
||||
std::string_view::iterator start,
|
||||
std::string_view::iterator where)
|
||||
{
|
||||
size_t line = 1;
|
||||
while (start < where) {
|
||||
if (start[0] == '\n') {
|
||||
line++;
|
||||
}
|
||||
start++;
|
||||
}
|
||||
printf("%s\n", fmt::format("{}({}) Unknown token: ({})", filepath, line, where[0]).c_str());
|
||||
}
|
||||
|
||||
void TokenIterator::skip_break_lines()
|
||||
{
|
||||
while (next_ < token_stream_.end() && std::holds_alternative<BreakLineToken>(*next_)) {
|
||||
next_++;
|
||||
}
|
||||
}
|
||||
|
||||
void TokenIterator::process_text(std::string_view filepath, std::string_view text)
|
||||
{
|
||||
std::string_view::iterator itr = text.begin();
|
||||
const std::string_view::iterator end = text.end();
|
||||
|
||||
auto eval_token = [this](std::string_view::iterator &itr,
|
||||
std::string_view::iterator end,
|
||||
auto &&eval_fn) -> bool {
|
||||
std::string_view::iterator current = itr;
|
||||
eval_fn(itr, end, *this);
|
||||
return current != itr;
|
||||
};
|
||||
|
||||
while (itr != text.end()) {
|
||||
const std::string_view::iterator current = itr;
|
||||
|
||||
if (eval_token(itr, end, eval_space) || eval_token(itr, end, eval_line_comment) ||
|
||||
eval_token(itr, end, eval_multiline_comment) || eval_token(itr, end, eval_identifier) ||
|
||||
eval_token(itr, end, eval_int_literal) || eval_token(itr, end, eval_string_literal) ||
|
||||
eval_token(itr, end, eval_symbol) || eval_token(itr, end, eval_break_line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
/* Unkown token found. */
|
||||
if (current == itr) {
|
||||
print_unkown_token(filepath, text.begin(), itr);
|
||||
token_stream_.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
next_ = token_stream_.begin();
|
||||
};
|
||||
|
||||
TokenVariant *TokenIterator::next_variant()
|
||||
{
|
||||
if (next_ < token_stream_.end()) {
|
||||
return next_++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TokenIterator::has_finish()
|
||||
{
|
||||
return !(next_ < token_stream_.end());
|
||||
}
|
||||
|
||||
void TokenIterator::push_waypoint()
|
||||
{
|
||||
waypoints_.append(next_);
|
||||
}
|
||||
|
||||
void TokenIterator::end_waypoint(bool success)
|
||||
{
|
||||
if (!success) {
|
||||
if (last_unmatched < next_) {
|
||||
last_unmatched = next_;
|
||||
}
|
||||
next_ = waypoints_.last();
|
||||
}
|
||||
waypoints_.remove_last();
|
||||
}
|
||||
|
||||
KeywordToken *TokenIterator::next_keyword(KeywordType type)
|
||||
{
|
||||
TokenVariant *tmp = next_;
|
||||
if (KeywordToken *keyword = next<KeywordToken>(); keyword && keyword->type == type) {
|
||||
return keyword;
|
||||
}
|
||||
next_ = tmp;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SymbolToken *TokenIterator::next_symbol(SymbolType type)
|
||||
{
|
||||
TokenVariant *tmp = next_;
|
||||
if (SymbolToken *symbol = next<SymbolToken>(); symbol && symbol->type == type) {
|
||||
return symbol;
|
||||
}
|
||||
next_ = tmp;
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace blender::dna::lex
|
|
@ -0,0 +1,181 @@
|
|||
#pragma once
|
||||
#include "BLI_vector.hh"
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
namespace blender::dna::lex {
|
||||
|
||||
enum class SymbolType : int8_t {
|
||||
COLON = 0,
|
||||
SEMICOLON,
|
||||
LPAREN,
|
||||
RPAREN,
|
||||
LBRACKET,
|
||||
RBRACKET,
|
||||
LBRACE,
|
||||
RBRACE,
|
||||
ASSIGN,
|
||||
HASH,
|
||||
DOT,
|
||||
COMMA,
|
||||
STAR,
|
||||
LESS,
|
||||
GREATER,
|
||||
BIT_OR,
|
||||
BIT_AND,
|
||||
PLUS,
|
||||
MINUS,
|
||||
EXCLAMATION,
|
||||
PERCENT,
|
||||
CARET,
|
||||
QUESTION,
|
||||
TILDE,
|
||||
BACKSLASH,
|
||||
SLASH,
|
||||
};
|
||||
|
||||
enum class KeywordType : int8_t {
|
||||
INCLUDE = 0,
|
||||
STRUCT,
|
||||
TYPEDEF,
|
||||
CLASS,
|
||||
ENUM,
|
||||
DEFINE,
|
||||
PUBLIC,
|
||||
PRIVATE,
|
||||
CONST,
|
||||
VOID,
|
||||
CHAR,
|
||||
CHAR16_T,
|
||||
CHAR32_T,
|
||||
UNSIGNED,
|
||||
SIGNED,
|
||||
SHORT,
|
||||
LONG,
|
||||
ULONG,
|
||||
INT,
|
||||
INT8_T,
|
||||
INT16_T,
|
||||
INT32_T,
|
||||
INT64_T,
|
||||
UINT8_T,
|
||||
UINT16_T,
|
||||
UINT32_T,
|
||||
UINT64_T,
|
||||
FLOAT,
|
||||
DOUBLE,
|
||||
IF,
|
||||
IFDEF,
|
||||
IFNDEF,
|
||||
ENDIF,
|
||||
EXTERN,
|
||||
PRAGMA,
|
||||
ONCE,
|
||||
DNA_DEFINE_CXX_METHODS,
|
||||
DNA_DEPRECATED,
|
||||
ENUM_OPERATORS,
|
||||
BLI_STATIC_ASSERT_ALIGN,
|
||||
DNA_DEPRECATED_ALLOW
|
||||
};
|
||||
|
||||
struct Token {
|
||||
std::string_view where;
|
||||
};
|
||||
|
||||
struct BreakLineToken : public Token {};
|
||||
|
||||
struct IdentifierToken : public Token {};
|
||||
|
||||
struct StringLiteralToken : public Token {};
|
||||
|
||||
struct IntLiteralToken : public Token {
|
||||
int32_t val{0};
|
||||
};
|
||||
|
||||
struct SymbolToken : public Token {
|
||||
SymbolType type;
|
||||
};
|
||||
|
||||
struct KeywordToken : public Token {
|
||||
KeywordType type;
|
||||
};
|
||||
|
||||
using TokenVariant = std::variant<BreakLineToken,
|
||||
IdentifierToken,
|
||||
IntLiteralToken,
|
||||
SymbolToken,
|
||||
KeywordToken,
|
||||
StringLiteralToken>;
|
||||
|
||||
struct TokenIterator {
|
||||
/** Last token that fails to match a token request. */
|
||||
TokenVariant *last_unmatched{nullptr};
|
||||
|
||||
private:
|
||||
/* Token stream. */
|
||||
Vector<TokenVariant> token_stream_;
|
||||
/* Return points to use if the parser fails when parsing tokens. */
|
||||
Vector<TokenVariant *> waypoints_;
|
||||
/* Pointer to next token to use. */
|
||||
TokenVariant *next_{nullptr};
|
||||
|
||||
/* Print line where a unkown token was found. */
|
||||
void print_unkown_token(std::string_view filepath,
|
||||
std::string_view::iterator start,
|
||||
std::string_view::iterator where);
|
||||
|
||||
void skip_break_lines();
|
||||
|
||||
public:
|
||||
/* Iterates over the input text looking for tokens. */
|
||||
void process_text(std::string_view filepath, std::string_view text);
|
||||
|
||||
/* Add a return point in case the token parser fails create an item. */
|
||||
void push_waypoint();
|
||||
|
||||
/**
|
||||
* Removes the last return point, if `success==false` the iterator steps back to the return
|
||||
* point.
|
||||
*/
|
||||
void end_waypoint(bool success);
|
||||
|
||||
/* Return the pointer to the next token, and advances the iterator. */
|
||||
TokenVariant *next_variant();
|
||||
|
||||
/* Checks if the token iterator has reach the last token. */
|
||||
bool has_finish();
|
||||
|
||||
/* Appends a token. */
|
||||
template<class TokenType> void append(TokenType &&token)
|
||||
{
|
||||
token_stream_.append(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next token if it type matches to `Type`.
|
||||
* Break lines are skipped for not break lines requested tokens.
|
||||
*/
|
||||
template<class Type> Type *next()
|
||||
{
|
||||
TokenVariant *current_next = next_;
|
||||
if constexpr (!std::is_same_v<Type, BreakLineToken>) {
|
||||
skip_break_lines();
|
||||
}
|
||||
if (next_ < token_stream_.end() && std::holds_alternative<Type>(*next_)) {
|
||||
return &std::get<Type>(*next_++);
|
||||
}
|
||||
if (last_unmatched < next_) {
|
||||
last_unmatched = next_;
|
||||
}
|
||||
next_ = current_next;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Return the next token if it matches to the requested keyword type. */
|
||||
KeywordToken *next_keyword(KeywordType type);
|
||||
|
||||
/* Return the next token if it matches to the requested symbol type. */
|
||||
SymbolToken *next_symbol(SymbolType type);
|
||||
};
|
||||
|
||||
} // namespace blender::dna::lex
|
|
@ -0,0 +1,858 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "dna_parser.hh"
|
||||
#include <fmt/format.h>
|
||||
|
||||
#ifdef DEBUG_PRINT_DNA_PARSER
|
||||
namespace blender::dna::parser {
|
||||
|
||||
void printf_struct(ast::Struct &val, size_t padding);
|
||||
|
||||
struct StructMemberPrinter {
|
||||
size_t padding;
|
||||
void operator()(ast::Variable &var) const
|
||||
{
|
||||
if (var.const_tag) {
|
||||
printf("const ");
|
||||
}
|
||||
printf("%s", fmt::format("{} ", var.type).c_str());
|
||||
|
||||
bool first = true;
|
||||
for (auto &variable_item : var.items) {
|
||||
if (!first) {
|
||||
printf(",");
|
||||
}
|
||||
first = false;
|
||||
printf("%s",
|
||||
fmt::format("{}{}", variable_item.ptr.value_or(""), variable_item.name).c_str());
|
||||
for (auto &size : variable_item.size) {
|
||||
if (std::holds_alternative<std::string_view>(size)) {
|
||||
printf("%s", fmt::format("[{}]", std::get<std::string_view>(size)).c_str());
|
||||
}
|
||||
else {
|
||||
printf("%s", fmt::format("[{}]", std::get<int32_t>(size)).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void operator()(ast::FunctionPtr &fn) const
|
||||
{
|
||||
if (fn.const_tag) {
|
||||
printf("%s", "const ");
|
||||
}
|
||||
printf("%s", fmt::format("{} (*{})(...)", fn.type, fn.name).c_str());
|
||||
}
|
||||
void operator()(ast::PointerToArray &ptr) const
|
||||
{
|
||||
printf("%s", fmt::format("{} (*{})[{}]", ptr.type, ptr.name, ptr.size).c_str());
|
||||
}
|
||||
void operator()(ast::Struct &val) const
|
||||
{
|
||||
printf_struct(val, padding);
|
||||
}
|
||||
};
|
||||
|
||||
struct ParserDebugPrinter {
|
||||
size_t padding;
|
||||
void operator()(ast::DefineInt &val) const
|
||||
{
|
||||
printf("%s\n", fmt::format("#define {} {}", val.name, val.value).c_str());
|
||||
}
|
||||
void operator()(ast::Enum &val) const
|
||||
{
|
||||
printf("%s", fmt::format("enum {}", val.name.value_or("unnamed")).c_str());
|
||||
if (val.type) {
|
||||
printf("%s", fmt::format(": {}", val.type.value()).c_str());
|
||||
}
|
||||
printf(" {...};\n");
|
||||
}
|
||||
void operator()(ast::Struct &val) const
|
||||
{
|
||||
printf_struct(val, padding);
|
||||
printf(";\n");
|
||||
}
|
||||
void operator()(ast::FunctionPtr &fn) const
|
||||
{
|
||||
StructMemberPrinter{padding + 1}.operator()(fn);
|
||||
printf("\n");
|
||||
}
|
||||
void operator()(ast::Variable &var) const
|
||||
{
|
||||
StructMemberPrinter{padding + 1}.operator()(var);
|
||||
printf("\n");
|
||||
}
|
||||
};
|
||||
|
||||
void printf_struct(ast::Struct &val, size_t padding)
|
||||
{
|
||||
printf("%s\n", fmt::format("struct {} {{", val.name).c_str());
|
||||
for (auto &item : val.items) {
|
||||
for (size_t x = 0; x < padding + 1; x++) {
|
||||
printf(" ");
|
||||
}
|
||||
std::visit(StructMemberPrinter{padding + 1}, item);
|
||||
printf(";\n");
|
||||
}
|
||||
for (size_t x = 0; x < padding; x++) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("}");
|
||||
};
|
||||
|
||||
} // namespace blender::dna::parser
|
||||
#endif
|
||||
|
||||
namespace blender::dna::parser::ast {
|
||||
|
||||
/**
|
||||
* Parser that matches a sequence of elements to parse, fails if any `Args` in `Args...` fails to
|
||||
* parse.
|
||||
* Given the following example:`Sequence<HashSymbol,PragmaKeyword,OnceKeyword>` parses when the
|
||||
* text contains `#pragma once`.
|
||||
*/
|
||||
template<class... Args> struct Sequence : public std::tuple<Args...> {
|
||||
|
||||
private:
|
||||
template<std::size_t I, typename Type>
|
||||
static inline bool parse_type(TokenIterator &token_iterator, Sequence &sequence)
|
||||
{
|
||||
std::optional<Type> val = Type::parse(token_iterator);
|
||||
if (val.has_value()) {
|
||||
std::get<I>(sequence) = std::move(val.value());
|
||||
}
|
||||
return val.has_value();
|
||||
};
|
||||
|
||||
template<std::size_t... I>
|
||||
static inline bool parse_impl(std::index_sequence<I...> /*indices*/,
|
||||
TokenIterator &token_iterator,
|
||||
Sequence &sequence)
|
||||
{
|
||||
return (parse_type<I, Args>(token_iterator, sequence) && ...);
|
||||
};
|
||||
|
||||
public:
|
||||
static std::optional<Sequence> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
token_iterator.push_waypoint();
|
||||
Sequence sequence;
|
||||
const bool success = parse_impl(
|
||||
std::make_index_sequence<sizeof...(Args)>{}, token_iterator, sequence);
|
||||
token_iterator.end_waypoint(success);
|
||||
if (success) {
|
||||
return sequence;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parser that don't fails if `Type` can't be parsed.
|
||||
* Parsing the sequence `Sequence<Optional<ConstKeyword>,IntKeyword, Identifier,SemicolonSymbol>`
|
||||
* success either if text is `const int num;` or `int num;`
|
||||
*/
|
||||
template<typename Type> struct Optional : public std::optional<Type> {
|
||||
static std::optional<Optional> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
token_iterator.push_waypoint();
|
||||
std::optional<Type> result = Type::parse(token_iterator);
|
||||
token_iterator.end_waypoint(result.has_value());
|
||||
return Optional{std::move(result)};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parser that tries to match any `Arg` in `Args...`
|
||||
* Parsing the sequence `Sequence<Variant<IntKeyword,FloatKeyword>,Identifier,SemicolonSymbol>`
|
||||
* success either if text is `int num;` or `float num;`
|
||||
*/
|
||||
template<class... Args> struct Variant : public std::variant<Args...> {
|
||||
private:
|
||||
template<typename Type>
|
||||
static inline bool parse_type(TokenIterator &token_iterator, Variant &variant)
|
||||
{
|
||||
token_iterator.push_waypoint();
|
||||
std::optional<Type> val = Type::parse(token_iterator);
|
||||
if (val.has_value()) {
|
||||
variant.template emplace<Type>(std::move(val.value()));
|
||||
}
|
||||
token_iterator.end_waypoint(val.has_value());
|
||||
return val.has_value();
|
||||
};
|
||||
|
||||
public:
|
||||
static std::optional<Variant> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
Variant tmp;
|
||||
if ((parse_type<Args>(token_iterator, tmp) || ...)) {
|
||||
return tmp;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/** Keyword parser. */
|
||||
template<KeywordType Type> struct Keyword {
|
||||
static std::optional<Keyword> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
if (token_iterator.next_keyword(Type)) {
|
||||
return Keyword{};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
using ConstKeyword = Keyword<KeywordType::CONST>;
|
||||
using IncludeKeyword = Keyword<KeywordType::INCLUDE>;
|
||||
using StructKeyword = Keyword<KeywordType::STRUCT>;
|
||||
using DefineKeyword = Keyword<KeywordType::DEFINE>;
|
||||
using UnsignedKeyword = Keyword<KeywordType::UNSIGNED>;
|
||||
using IntKeyword = Keyword<KeywordType::INT>;
|
||||
using Int8Keyword = Keyword<KeywordType::INT8_T>;
|
||||
using Int16Keyword = Keyword<KeywordType::INT16_T>;
|
||||
using Int32Keyword = Keyword<KeywordType::INT32_T>;
|
||||
using Int64Keyword = Keyword<KeywordType::INT64_T>;
|
||||
using UInt8Keyword = Keyword<KeywordType::UINT8_T>;
|
||||
using UInt16Keyword = Keyword<KeywordType::UINT16_T>;
|
||||
using UInt32Keyword = Keyword<KeywordType::UINT32_T>;
|
||||
using UInt64Keyword = Keyword<KeywordType::UINT64_T>;
|
||||
using FloatKeyword = Keyword<KeywordType::FLOAT>;
|
||||
using DoubleKeyword = Keyword<KeywordType::DOUBLE>;
|
||||
using ShortKeyword = Keyword<KeywordType::SHORT>;
|
||||
using CharKeyword = Keyword<KeywordType::CHAR>;
|
||||
using VoidKeyword = Keyword<KeywordType::VOID>;
|
||||
using IfKeyword = Keyword<KeywordType::IF>;
|
||||
using IfDefKeyword = Keyword<KeywordType::IFDEF>;
|
||||
using IfnDefKeyword = Keyword<KeywordType::IFNDEF>;
|
||||
using EndIfKeyword = Keyword<KeywordType::ENDIF>;
|
||||
using ExternKeyword = Keyword<KeywordType::EXTERN>;
|
||||
using TypedefKeyword = Keyword<KeywordType::TYPEDEF>;
|
||||
using PragmaKeyword = Keyword<KeywordType::PRAGMA>;
|
||||
using OnceKeyword = Keyword<KeywordType::ONCE>;
|
||||
using EnumKeyword = Keyword<KeywordType::ENUM>;
|
||||
using ClassKeyword = Keyword<KeywordType::CLASS>;
|
||||
using DNADeprecatedKeyword = Keyword<KeywordType::DNA_DEPRECATED>;
|
||||
using DNADeprecatedAllowKeyword = Keyword<KeywordType::DNA_DEPRECATED_ALLOW>;
|
||||
|
||||
/** Symbol parser. */
|
||||
template<SymbolType type> struct Symbol {
|
||||
static std::optional<Symbol> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
if (token_iterator.next_symbol(type)) {
|
||||
return Symbol{};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
using LBracketSymbol = Symbol<SymbolType::LBRACKET>;
|
||||
using RBracketSymbol = Symbol<SymbolType::RBRACKET>;
|
||||
using LBraceSymbol = Symbol<SymbolType::LBRACE>;
|
||||
using RBraceSymbol = Symbol<SymbolType::RBRACE>;
|
||||
using LParenSymbol = Symbol<SymbolType::LPAREN>;
|
||||
using RParenSymbol = Symbol<SymbolType::RPAREN>;
|
||||
using StarSymbol = Symbol<SymbolType::STAR>;
|
||||
using SemicolonSymbol = Symbol<SymbolType::SEMICOLON>;
|
||||
using ColonSymbol = Symbol<SymbolType::COLON>;
|
||||
using CommaSymbol = Symbol<SymbolType::COMMA>;
|
||||
using HashSymbol = Symbol<SymbolType::HASH>;
|
||||
using LessSymbol = Symbol<SymbolType::LESS>;
|
||||
using GreaterSymbol = Symbol<SymbolType::GREATER>;
|
||||
using AssignSymbol = Symbol<SymbolType::ASSIGN>;
|
||||
using MinusSymbol = Symbol<SymbolType::MINUS>;
|
||||
|
||||
static void skip_until_match_paired_symbols(SymbolType left,
|
||||
SymbolType right,
|
||||
TokenIterator &token_iterator);
|
||||
|
||||
/**
|
||||
* Parses a macro call, `MacroCall<KeywordType::DNA_DEFINE_CXX_METHODS>` parses
|
||||
* `DNA_DEFINE_CXX_METHODS(...)`.
|
||||
*/
|
||||
template<lex::KeywordType Type> struct MacroCall {
|
||||
static std::optional<MacroCall> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
if (Sequence<Keyword<Type>, LParenSymbol>::parse(token_iterator).has_value()) {
|
||||
skip_until_match_paired_symbols(SymbolType::LPAREN, SymbolType::RPAREN, token_iterator);
|
||||
SemicolonSymbol::parse(token_iterator);
|
||||
return MacroCall{};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses a string literal. */
|
||||
struct StringLiteral {
|
||||
std::string_view value;
|
||||
static std::optional<StringLiteral> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
if (StringLiteralToken *literal = token_iterator.next<StringLiteralToken>(); literal) {
|
||||
return StringLiteral{literal->where};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses a int literal. */
|
||||
struct IntLiteral {
|
||||
int value;
|
||||
static std::optional<IntLiteral> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
if (IntLiteralToken *value = token_iterator.next<IntLiteralToken>(); value) {
|
||||
return IntLiteral{value->val};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses a identifier. */
|
||||
struct Identifier {
|
||||
std::string_view str;
|
||||
static std::optional<Identifier> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
if (IdentifierToken *identifier = token_iterator.next<IdentifierToken>(); identifier) {
|
||||
return Identifier{identifier->where};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses a include, either `#include "include_name.hh"` or `#include <path/to/include.hh>`. */
|
||||
struct Include {
|
||||
static std::optional<Include> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
if (Sequence<HashSymbol, IncludeKeyword>::parse(token_iterator).has_value()) {
|
||||
TokenVariant *token = token_iterator.next_variant();
|
||||
while (token && !std::holds_alternative<BreakLineToken>(*token)) {
|
||||
token = token_iterator.next_variant();
|
||||
}
|
||||
return Include{};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/** Check if a token is a symbol and has a type. */
|
||||
static bool inline is_symbol_type(const TokenVariant &token, const SymbolType type)
|
||||
{
|
||||
return std::holds_alternative<SymbolToken>(token) && std::get<SymbolToken>(token).type == type;
|
||||
}
|
||||
|
||||
/** Parses `#define` directives except to const int defines. */
|
||||
struct Define {
|
||||
static std::optional<Define> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
if (!Sequence<HashSymbol, DefineKeyword>::parse(token_iterator)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
bool scape_bl = false;
|
||||
for (TokenVariant *token = token_iterator.next_variant(); token;
|
||||
token = token_iterator.next_variant())
|
||||
{
|
||||
if (std::holds_alternative<BreakLineToken>(*token) && !scape_bl) {
|
||||
break;
|
||||
}
|
||||
scape_bl = is_symbol_type(*token, SymbolType::BACKSLASH);
|
||||
}
|
||||
return Define{};
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses const int defines, like `#define FILE_MAX 1024`. */
|
||||
std::optional<DefineInt> DefineInt::parse(TokenIterator &token_iterator)
|
||||
{
|
||||
using DefineConstIntSeq = Sequence<HashSymbol, DefineKeyword, Identifier, IntLiteral>;
|
||||
std::optional<DefineConstIntSeq> val = DefineConstIntSeq::parse(token_iterator);
|
||||
if (!val.has_value() || !token_iterator.next<BreakLineToken>()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return DefineInt{std::get<2>(val.value()).str, std::get<3>(val.value()).value};
|
||||
}
|
||||
|
||||
bool DefineInt::operator==(const DefineInt &other) const
|
||||
{
|
||||
return name == other.name && value == other.value;
|
||||
}
|
||||
|
||||
/** Parses most c++ primitive types. */
|
||||
struct PrimitiveType {
|
||||
std::string_view str;
|
||||
static std::optional<PrimitiveType> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
/* TODO: Add all primitive types. */
|
||||
using PrimitiveTypeVariants = Variant<IntKeyword,
|
||||
CharKeyword,
|
||||
ShortKeyword,
|
||||
FloatKeyword,
|
||||
DoubleKeyword,
|
||||
VoidKeyword,
|
||||
Sequence<UnsignedKeyword, IntKeyword>,
|
||||
Sequence<UnsignedKeyword, ShortKeyword>,
|
||||
Sequence<UnsignedKeyword, CharKeyword>,
|
||||
Int8Keyword,
|
||||
Int16Keyword,
|
||||
Int32Keyword,
|
||||
Int64Keyword,
|
||||
UInt8Keyword,
|
||||
UInt16Keyword,
|
||||
UInt32Keyword,
|
||||
UInt64Keyword,
|
||||
Keyword<KeywordType::LONG>,
|
||||
Keyword<KeywordType::ULONG>>;
|
||||
std::optional<PrimitiveTypeVariants> type = PrimitiveTypeVariants::parse(token_iterator);
|
||||
if (!type.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/* Use `unsigned int` as uint32?.... */
|
||||
using namespace std::string_view_literals;
|
||||
/* Note: makesdna ignores `unsigned` keyword. */
|
||||
static constexpr std::string_view primitive_types[]{
|
||||
"int"sv, "char"sv, "short"sv, "float"sv, "double"sv,
|
||||
"void"sv, "int"sv, "short"sv, "char"sv, "int8_t"sv,
|
||||
"int16_t"sv, "int32_t"sv, "int64_t"sv, "uint8_t"sv, "uint16_t"sv,
|
||||
"uint32_t"sv, "uint64_t"sv, "long"sv, "ulong"sv,
|
||||
};
|
||||
return PrimitiveType{primitive_types[type.value().index()]};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the type in variable declarations or function return value, either a primitive type or
|
||||
* custom type.
|
||||
*/
|
||||
struct Type {
|
||||
bool const_tag{false};
|
||||
std::string_view str;
|
||||
static std::optional<Type> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
using TypeVariant = Variant<PrimitiveType, Sequence<Optional<StructKeyword>, Identifier>>;
|
||||
using TypeSequence = Sequence<Optional<ConstKeyword>, TypeVariant>;
|
||||
|
||||
const std::optional<TypeSequence> type_seq = TypeSequence::parse(token_iterator);
|
||||
if (!type_seq) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const bool const_tag = std::get<0>(type_seq.value()).has_value();
|
||||
const TypeVariant &type_variant = std::get<1>(type_seq.value());
|
||||
if (std::holds_alternative<PrimitiveType>(type_variant)) {
|
||||
return Type{const_tag, std::get<0>(type_variant).str};
|
||||
}
|
||||
return Type{const_tag, std::get<1>(std::get<1>(type_variant)).str};
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses the array part of variable declarations: with `int num[3][4];` parses `[3][4]`. */
|
||||
static Vector<std::variant<std::string_view, int32_t>> variable_size_array_part(
|
||||
TokenIterator &token_iterator)
|
||||
{
|
||||
Vector<std::variant<std::string_view, int32_t>> result;
|
||||
/* Dynamic array. */
|
||||
if (Sequence<LBracketSymbol, RBracketSymbol>::parse(token_iterator).has_value()) {
|
||||
result.append(std::string_view{""});
|
||||
}
|
||||
while (true) {
|
||||
using ArraySize = Sequence<LBracketSymbol, Variant<IntLiteral, Identifier>, RBracketSymbol>;
|
||||
const std::optional<ArraySize> size_seq = ArraySize::parse(token_iterator);
|
||||
if (!size_seq.has_value()) {
|
||||
break;
|
||||
}
|
||||
const auto &item_size = std::get<1>(size_seq.value());
|
||||
if (std::holds_alternative<IntLiteral>(item_size)) {
|
||||
result.append(std::get<IntLiteral>(item_size).value);
|
||||
}
|
||||
else {
|
||||
result.append(std::get<Identifier>(item_size).str);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable parser, parses multiple inline declarations, like:
|
||||
* `int value;`
|
||||
* `const int value[256][DEFINE_VALUE];`
|
||||
* `float *value1,value2[256][256];`
|
||||
*/
|
||||
std::optional<Variable> Variable::parse(TokenIterator &token_iterator)
|
||||
{
|
||||
const std::optional<Type> type{Type::parse(token_iterator)};
|
||||
if (!type) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Variable variable;
|
||||
variable.const_tag = type.value().const_tag;
|
||||
variable.type = type.value().str;
|
||||
|
||||
while (true) {
|
||||
std::string start;
|
||||
for (; StarSymbol::parse(token_iterator);) {
|
||||
start += '*';
|
||||
}
|
||||
std::optional<Identifier> name{Identifier::parse(token_iterator)};
|
||||
if (!name.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Variable::Item item{};
|
||||
item.ptr = !start.empty() ? std::optional{start} : std::nullopt;
|
||||
item.name = name.value().str;
|
||||
item.size = variable_size_array_part(token_iterator);
|
||||
variable.items.append(std::move(item));
|
||||
DNADeprecatedKeyword::parse(token_iterator);
|
||||
if (SemicolonSymbol::parse(token_iterator).has_value()) {
|
||||
break;
|
||||
}
|
||||
if (!CommaSymbol::parse(token_iterator).has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return variable;
|
||||
}
|
||||
|
||||
bool Variable::Item::operator==(const Variable::Item &other) const
|
||||
{
|
||||
return ptr == other.ptr && name == other.name && size == other.size;
|
||||
}
|
||||
|
||||
bool Variable::operator==(const Variable &other) const
|
||||
{
|
||||
return type == other.type && items == other.items;
|
||||
}
|
||||
|
||||
/* Skips tokens until match the closing right symbol, like function body braces `{...}`. */
|
||||
static void skip_until_match_paired_symbols(SymbolType left,
|
||||
SymbolType right,
|
||||
TokenIterator &token_iterator)
|
||||
{
|
||||
int left_count = 1;
|
||||
for (TokenVariant *token = token_iterator.next_variant(); token;
|
||||
token = token_iterator.next_variant())
|
||||
{
|
||||
if (is_symbol_type(*token, right)) {
|
||||
left_count--;
|
||||
if (left_count == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (is_symbol_type(*token, left)) {
|
||||
left_count++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses function pointer variables, like `bool (*poll)(struct bContext *);`
|
||||
*/
|
||||
std::optional<FunctionPtr> FunctionPtr::parse(TokenIterator &token_iterator)
|
||||
{
|
||||
using FunctionPtrBegin = Sequence<Type,
|
||||
Optional<StarSymbol>,
|
||||
LParenSymbol,
|
||||
StarSymbol,
|
||||
Identifier,
|
||||
RParenSymbol,
|
||||
LParenSymbol>;
|
||||
const std::optional<FunctionPtrBegin> fn = FunctionPtrBegin::parse(token_iterator);
|
||||
if (!fn.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
FunctionPtr fn_ptr{};
|
||||
fn_ptr.const_tag = std::get<0>(fn.value()).const_tag;
|
||||
fn_ptr.type = std::get<0>(fn.value()).str;
|
||||
fn_ptr.name = std::get<4>(fn.value()).str;
|
||||
/* Skip Function params. */
|
||||
skip_until_match_paired_symbols(SymbolType::LPAREN, SymbolType::RPAREN, token_iterator);
|
||||
|
||||
/* Closing sequence. */
|
||||
if (!SemicolonSymbol::parse(token_iterator).has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return fn_ptr;
|
||||
}
|
||||
|
||||
bool FunctionPtr::operator==(const FunctionPtr &other) const
|
||||
{
|
||||
return type == other.type && name == other.name;
|
||||
}
|
||||
|
||||
bool PointerToArray::operator==(const PointerToArray &other) const
|
||||
{
|
||||
return type == other.type && name == other.name && size == other.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses array pointer variables, like `float (*vert_coords_prev)[3];`
|
||||
*/
|
||||
std::optional<PointerToArray> PointerToArray::parse(TokenIterator &token_iterator)
|
||||
{
|
||||
using PointerToArraySequence = Sequence<Type,
|
||||
LParenSymbol,
|
||||
StarSymbol,
|
||||
Identifier,
|
||||
RParenSymbol,
|
||||
LBracketSymbol,
|
||||
IntLiteral,
|
||||
RBracketSymbol,
|
||||
SemicolonSymbol>;
|
||||
std::optional<PointerToArraySequence> val = PointerToArraySequence::parse(token_iterator);
|
||||
if (!val.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
PointerToArray ptr{};
|
||||
ptr.type = std::get<0>(val.value()).str;
|
||||
ptr.name = std::get<3>(val.value()).str;
|
||||
ptr.size = std::get<6>(val.value()).value;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template<KeywordType... type> static bool is_keyword_type(TokenVariant token)
|
||||
{
|
||||
return std::holds_alternative<KeywordToken>(token) &&
|
||||
((std::get<KeywordToken>(token).type == type) || ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses `#if....#endif` code blocks.
|
||||
*/
|
||||
struct IfDef {
|
||||
static std::optional<IfDef> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
using IfDefBeginSequence =
|
||||
Sequence<HashSymbol, Variant<IfDefKeyword, IfKeyword, IfnDefKeyword>>;
|
||||
const std::optional<IfDefBeginSequence> val = IfDefBeginSequence::parse(token_iterator);
|
||||
if (!val.has_value()) {
|
||||
return std::nullopt;
|
||||
};
|
||||
int ifdef_deep = 1;
|
||||
bool hash_carried = false;
|
||||
for (TokenVariant *token = token_iterator.next_variant(); token;
|
||||
token = token_iterator.next_variant())
|
||||
{
|
||||
if (hash_carried &&
|
||||
is_keyword_type<KeywordType::IF, KeywordType::IFDEF, KeywordType::IFNDEF>(*token))
|
||||
{
|
||||
ifdef_deep++;
|
||||
}
|
||||
if (hash_carried && is_keyword_type<KeywordType::ENDIF>(*token)) {
|
||||
ifdef_deep--;
|
||||
}
|
||||
if (ifdef_deep == 0) {
|
||||
break;
|
||||
}
|
||||
hash_carried = is_symbol_type(*token, SymbolType::HASH);
|
||||
}
|
||||
/* Not matching #endif. */
|
||||
if (ifdef_deep != 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return IfDef{};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses struct declarations.
|
||||
*/
|
||||
std::optional<Struct> Struct::parse(TokenIterator &token_iterator)
|
||||
{
|
||||
using StructBeginSequence =
|
||||
Sequence<Optional<TypedefKeyword>, StructKeyword, Optional<Identifier>, LBraceSymbol>;
|
||||
std::optional<StructBeginSequence> struct_seq = StructBeginSequence::parse(token_iterator);
|
||||
if (!struct_seq.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Struct result{};
|
||||
if (std::get<2>(struct_seq.value()).has_value()) {
|
||||
result.name = std::get<2>(struct_seq.value()).value().str;
|
||||
}
|
||||
while (true) {
|
||||
using DNA_DEF_CCX_Macro = MacroCall<lex::KeywordType::DNA_DEFINE_CXX_METHODS>;
|
||||
|
||||
if (auto member = Variant<Variable, FunctionPtr, PointerToArray, Struct>::parse(
|
||||
token_iterator);
|
||||
member.has_value())
|
||||
{
|
||||
result.items.append(std::move(member.value()));
|
||||
}
|
||||
else if (DNA_DEF_CCX_Macro::parse(token_iterator).has_value() ||
|
||||
IfDef::parse(token_iterator).has_value())
|
||||
{
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
using StructEndSequence = Sequence<RBraceSymbol, Optional<Identifier>, SemicolonSymbol>;
|
||||
std::optional<StructEndSequence> struct_end = StructEndSequence ::parse(token_iterator);
|
||||
if (!struct_end.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (std::get<1>(struct_end.value()).has_value()) {
|
||||
result.member_name = std::get<1>(struct_end.value()).value().str;
|
||||
}
|
||||
if (result.member_name == result.name && result.name.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Struct::operator==(const Struct &other) const
|
||||
{
|
||||
return name == other.name && items == other.items;
|
||||
}
|
||||
|
||||
/** Parses non used definitions that DNA. */
|
||||
struct Skip {
|
||||
static std::optional<Skip> parse(TokenIterator &token_iterator)
|
||||
{
|
||||
using UnusedDeclarations =
|
||||
Variant<Define,
|
||||
Include,
|
||||
IfDef,
|
||||
Sequence<HashSymbol, PragmaKeyword, OnceKeyword>,
|
||||
Sequence<HashSymbol, HashSymbol, Struct>,
|
||||
Sequence<ExternKeyword, Variable>,
|
||||
MacroCall<lex::KeywordType::BLI_STATIC_ASSERT_ALIGN>,
|
||||
MacroCall<lex::KeywordType::ENUM_OPERATORS>,
|
||||
Sequence<TypedefKeyword, StructKeyword, Identifier, Identifier, SemicolonSymbol>>;
|
||||
if (UnusedDeclarations::parse(token_iterator).has_value()) {
|
||||
return Skip{};
|
||||
}
|
||||
/* Forward declare. */
|
||||
if (Sequence<StructKeyword, Identifier>::parse(token_iterator).has_value()) {
|
||||
for (; Sequence<CommaSymbol, Identifier>::parse(token_iterator).has_value();) {
|
||||
}
|
||||
if (SemicolonSymbol::parse(token_iterator).has_value()) {
|
||||
return Skip{};
|
||||
}
|
||||
}
|
||||
else if (token_iterator.next<BreakLineToken>()) {
|
||||
return Skip{};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/** Parse enums, with a name or not and with a fixed type or not. */
|
||||
std::optional<Enum> ast::Enum::parse(TokenIterator &token_iterator)
|
||||
{
|
||||
using EnumBeginSequence = Sequence<Optional<TypedefKeyword>,
|
||||
EnumKeyword,
|
||||
Optional<ClassKeyword>,
|
||||
Optional<Identifier>,
|
||||
Optional<Sequence<ColonSymbol, PrimitiveType>>,
|
||||
LBraceSymbol>;
|
||||
std::optional<EnumBeginSequence> enum_begin = EnumBeginSequence::parse(token_iterator);
|
||||
if (!enum_begin.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Enum enum_def;
|
||||
if (std::get<3>(enum_begin.value()).has_value()) {
|
||||
enum_def.name = std::get<3>(enum_begin.value()).value().str;
|
||||
}
|
||||
if (std::get<4>(enum_begin.value()).has_value()) {
|
||||
enum_def.type = std::get<1>(std::get<4>(enum_begin.value()).value()).str;
|
||||
}
|
||||
/* Skip enum body. */
|
||||
skip_until_match_paired_symbols(SymbolType::LBRACE, SymbolType::RBRACE, token_iterator);
|
||||
|
||||
/* Enum end sequence. */
|
||||
if (!Sequence<Optional<Identifier>, Optional<DNADeprecatedKeyword>, SemicolonSymbol>::parse(
|
||||
token_iterator)
|
||||
.has_value())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
return enum_def;
|
||||
}
|
||||
bool Enum::operator==(const Enum &other) const
|
||||
{
|
||||
return name == other.name && type == other.type;
|
||||
}
|
||||
|
||||
} // namespace blender::dna::parser::ast
|
||||
|
||||
namespace blender::dna::parser {
|
||||
static void print_unhandled_token_error(std::string_view filepath,
|
||||
std::string_view text,
|
||||
lex::TokenVariant *what)
|
||||
{
|
||||
auto visit_fn = [text, filepath](auto &&token) {
|
||||
std::string_view::iterator itr = text.begin();
|
||||
size_t line = 1;
|
||||
while (itr < token.where.begin()) {
|
||||
if (itr[0] == '\n') {
|
||||
line++;
|
||||
}
|
||||
itr++;
|
||||
}
|
||||
printf("%s\n",
|
||||
fmt::format("{}{} Unhandled token: \"{}\"", filepath, line, token.where).c_str());
|
||||
};
|
||||
std::visit(visit_fn, *what);
|
||||
}
|
||||
bool parse_include(std::string_view filepath,
|
||||
std::string_view text,
|
||||
lex::TokenIterator &token_iterator,
|
||||
Vector<ast::CppType> &dest)
|
||||
{
|
||||
using namespace ast;
|
||||
int dna_deprecated_allow_count = 0;
|
||||
using DNADeprecatedAllowSeq = Sequence<HashSymbol, IfDefKeyword, DNADeprecatedAllowKeyword>;
|
||||
using EndIfSeq = Sequence<HashSymbol, EndIfKeyword>;
|
||||
|
||||
while (!token_iterator.has_finish()) {
|
||||
using CPPTypeVariant = Variant<Struct,
|
||||
Enum,
|
||||
Sequence<Optional<TypedefKeyword>, FunctionPtr>,
|
||||
Variable,
|
||||
DefineInt,
|
||||
DNADeprecatedAllowSeq,
|
||||
EndIfSeq,
|
||||
Skip>;
|
||||
std::optional<CPPTypeVariant> val = CPPTypeVariant::parse(token_iterator);
|
||||
if (!val.has_value()) {
|
||||
print_unhandled_token_error(filepath, text, token_iterator.last_unmatched);
|
||||
return false;
|
||||
}
|
||||
if (std::holds_alternative<Struct>(val.value())) {
|
||||
dest.append(std::move(std::get<Struct>(val.value())));
|
||||
}
|
||||
else if (std::holds_alternative<DefineInt>(val.value())) {
|
||||
dest.append(std::move(std::get<DefineInt>(val.value())));
|
||||
}
|
||||
else if (std::holds_alternative<Variable>(val.value())) {
|
||||
continue;
|
||||
dest.append(std::move(std::get<Variable>(val.value())));
|
||||
}
|
||||
else if (std::holds_alternative<Enum>(val.value())) {
|
||||
Enum &enum_def = std::get<Enum>(val.value());
|
||||
/** Keep only named enums with fixed type. */
|
||||
if (!enum_def.name.has_value() || !enum_def.type.has_value()) {
|
||||
continue;
|
||||
}
|
||||
dest.append(enum_def);
|
||||
}
|
||||
else if (std::holds_alternative<DNADeprecatedAllowSeq>(val.value())) {
|
||||
dna_deprecated_allow_count++;
|
||||
}
|
||||
else if (std::holds_alternative<EndIfSeq>(val.value())) {
|
||||
dna_deprecated_allow_count++;
|
||||
if (dna_deprecated_allow_count < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_PRINT_DNA_PARSER
|
||||
static constexpr std::string_view debug_file{""};
|
||||
if (!debug_file.empty() && filepath.find(debug_file) != filepath.npos) {
|
||||
for (auto &val : dest) {
|
||||
std::visit(ParserDebugPrinter{}, val);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender::dna::parser
|
|
@ -0,0 +1,101 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
#include "dna_lexer.hh"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
namespace blender::dna::parser {
|
||||
|
||||
namespace ast {
|
||||
using namespace lex;
|
||||
|
||||
/* Constant int defined value. */
|
||||
struct DefineInt {
|
||||
std::string_view name;
|
||||
int32_t value{0};
|
||||
|
||||
static std::optional<DefineInt> parse(TokenIterator &token_iterator);
|
||||
bool operator==(const DefineInt &other) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Variable declaration, can hold multiple inline declarations, like:
|
||||
* `float *value1,value2[256][256];`
|
||||
*/
|
||||
struct Variable {
|
||||
struct Item {
|
||||
std::optional<std::string> ptr;
|
||||
std::string_view name;
|
||||
/** Item array size definition, empty for not arrays items. */
|
||||
Vector<std::variant<std::string_view, int32_t>> size;
|
||||
|
||||
bool operator==(const Item &other) const;
|
||||
};
|
||||
bool const_tag{false};
|
||||
std::string_view type;
|
||||
Vector<Item> items;
|
||||
|
||||
bool operator==(const Variable &other) const;
|
||||
static std::optional<Variable> parse(TokenIterator &token_iterator);
|
||||
};
|
||||
|
||||
/* Function pointer declaration. */
|
||||
struct FunctionPtr {
|
||||
bool const_tag{false};
|
||||
std::string_view type;
|
||||
std::string_view name;
|
||||
|
||||
bool operator==(const FunctionPtr &other) const;
|
||||
static std::optional<FunctionPtr> parse(TokenIterator &token_iterator);
|
||||
};
|
||||
|
||||
/* Pointer to array declaration. */
|
||||
struct PointerToArray {
|
||||
std::string_view type;
|
||||
std::string_view name;
|
||||
int32_t size;
|
||||
|
||||
bool operator==(const PointerToArray &other) const;
|
||||
static std::optional<PointerToArray> parse(TokenIterator &token_iterator);
|
||||
};
|
||||
|
||||
/* Struct declaration.*/
|
||||
struct Struct {
|
||||
std::string_view name;
|
||||
/* Recursive struct keep inline buffer capacity to 0. */
|
||||
Vector<std::variant<Variable, FunctionPtr, PointerToArray, Struct>, 0> items;
|
||||
/* Name set if struct is declared as member variable. */
|
||||
std::string_view member_name;
|
||||
|
||||
static std::optional<Struct> parse(TokenIterator &token_iterator);
|
||||
bool operator==(const Struct &other) const;
|
||||
};
|
||||
|
||||
/* Enum declaration. */
|
||||
struct Enum {
|
||||
/* Enum name, unset for unnamed enums. */
|
||||
std::optional<std::string_view> name;
|
||||
/** Fixed type specification. */
|
||||
std::optional<std::string_view> type;
|
||||
|
||||
bool operator==(const Enum &other) const;
|
||||
static std::optional<Enum> parse(TokenIterator &token_iterator);
|
||||
};
|
||||
|
||||
using CppType = std::variant<DefineInt, Enum, Struct, FunctionPtr, Variable>;
|
||||
|
||||
} // namespace ast
|
||||
|
||||
bool parse_include(std::string_view filepath,
|
||||
std::string_view text,
|
||||
lex::TokenIterator &token_iterator,
|
||||
Vector<ast::CppType> &c);
|
||||
} // namespace blender::dna::parser
|
|
@ -0,0 +1,103 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "dna_parser.hh"
|
||||
|
||||
namespace blender::dna::parser::tests {
|
||||
|
||||
TEST(parser, parse_file)
|
||||
{
|
||||
std::string_view text = R"x(
|
||||
/*----------------------------------------------------------------*/
|
||||
/** #pragma once and includes */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "...."
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_session_uid_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
// Forward declarations
|
||||
struct Collection;
|
||||
struct GHash;
|
||||
struct Object, SpaceLink;
|
||||
|
||||
#if 0
|
||||
typedef enum class UnusedEnum: uint8_t {
|
||||
/* vert is selected */
|
||||
UNUSED_1 = (1 + 0*FILE_MAX),
|
||||
UNUSED_2 = (1 << 1),
|
||||
} UnusedEnum;
|
||||
ENUM_OPERATORS(UnusedEnum, UNUSED_2);
|
||||
#endif
|
||||
|
||||
|
||||
/* Const int define */
|
||||
#define FILE_MAX 1024
|
||||
|
||||
/* define non const int*/
|
||||
#define DNA_DEFINE_CXX_METHODS() ....
|
||||
|
||||
/** bMotionPathVert, taken from DNA_action_Types.h
|
||||
* modified with a child struct. */
|
||||
typedef struct bMotionPathVert {
|
||||
DNA_DEFINE_CXX_METHODS(bMotionPathVert);
|
||||
/* Child struct */
|
||||
struct bMotionPathVertItem {
|
||||
DNA_DEFINE_CXX_METHODS(bMotionPathVert);
|
||||
float co[3], pre[235];
|
||||
struct Link *next, *pre;
|
||||
char path[FILE_MAX];
|
||||
bool (*poll)(bContext *, ARegion *);
|
||||
float (*data_src)[256];
|
||||
};
|
||||
/** Coordinates of point in 3D-space. */
|
||||
float co[3];
|
||||
/** Quick settings. */
|
||||
int flag;
|
||||
} bMotionPathVert;
|
||||
|
||||
/** eMotionPathVert_Flag, taken from DNA_action_Types.h
|
||||
* modified with a fixed size. */
|
||||
typedef enum class eMotionPathVert_Flag : uint8_t {
|
||||
/* vert is selected */
|
||||
VERT_SEL = (1 << 0),
|
||||
VERT_KEY = (1 << 1),
|
||||
} eMotionPathVert_Flag;
|
||||
ENUM_OPERATORS(eMotionPathVert_Flag, VERT_KEY);
|
||||
)x";
|
||||
|
||||
using namespace ast;
|
||||
lex::TokenIterator iterator;
|
||||
iterator.process_text("", text);
|
||||
|
||||
blender::Vector<CppType> cpp_defines;
|
||||
const bool parse_result = parse_include("", text, iterator, cpp_defines);
|
||||
Vector<CppType> expected{
|
||||
{DefineInt{"FILE_MAX", 1024}},
|
||||
{
|
||||
Struct{"bMotionPathVert",
|
||||
{Struct{"bMotionPathVertItem",
|
||||
{
|
||||
{Variable{false, "float", {{{}, "co", {{3}}}, {{}, "pre", {{235}}}}}},
|
||||
{Variable{false, "Link", {{"*", "next", {}}, {"*", "pre", {}}}}},
|
||||
{Variable{false, "char", {{{}, "path", {{"FILE_MAX"}}}}}},
|
||||
{FunctionPtr{false, "bool", "poll"}},
|
||||
{PointerToArray{"float", "data_src", 256}},
|
||||
}},
|
||||
{Variable{false, "float", {{{}, "co", {{3}}}}}},
|
||||
{Variable{false, "int", {{{}, "flag", {}}}}}}},
|
||||
},
|
||||
Enum{"eMotionPathVert_Flag", "uint8_t"}};
|
||||
ASSERT_TRUE(parse_result);
|
||||
ASSERT_EQ(expected, cpp_defines);
|
||||
}
|
||||
|
||||
} // namespace blender::dna::parser::tests
|
|
@ -31,6 +31,11 @@
|
|||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
|
@ -42,6 +47,7 @@
|
|||
#include "BLI_system.h" /* for 'BLI_system_backtrace' stub. */
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "dna_parser.hh"
|
||||
#include "dna_utils.h"
|
||||
|
||||
#define SDNA_MAX_FILENAME_LENGTH 255
|
||||
|
@ -148,12 +154,6 @@ static int add_name(const char *str);
|
|||
*/
|
||||
static short *add_struct(int namecode);
|
||||
|
||||
/**
|
||||
* Remove comments from this buffer. Assumes that the buffer refers to
|
||||
* ascii-code text.
|
||||
*/
|
||||
static int preprocess_include(char *maindata, const int maindata_len);
|
||||
|
||||
/**
|
||||
* Scan this file for serializable types.
|
||||
*/
|
||||
|
@ -560,105 +560,6 @@ static char *match_preproc_strstr(char *__restrict str, const char *__restrict s
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static int preprocess_include(char *maindata, const int maindata_len)
|
||||
{
|
||||
/* NOTE: len + 1, last character is a dummy to prevent
|
||||
* comparisons using uninitialized memory */
|
||||
char *temp = static_cast<char *>(MEM_mallocN(maindata_len + 1, "preprocess_include"));
|
||||
temp[maindata_len] = ' ';
|
||||
|
||||
memcpy(temp, maindata, maindata_len);
|
||||
|
||||
/* remove all c++ comments */
|
||||
/* replace all enters/tabs/etc with spaces */
|
||||
char *cp = temp;
|
||||
int a = maindata_len;
|
||||
int comment = 0;
|
||||
while (a--) {
|
||||
if (cp[0] == '/' && cp[1] == '/') {
|
||||
comment = 1;
|
||||
}
|
||||
else if (*cp == '\n') {
|
||||
comment = 0;
|
||||
}
|
||||
if (comment || *cp < 32 || *cp > 128) {
|
||||
*cp = 32;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
|
||||
/* No need for leading '#' character. */
|
||||
const char *cpp_block_start = "ifdef __cplusplus";
|
||||
const char *cpp_block_end = "endif";
|
||||
|
||||
/* data from temp copy to maindata, remove comments and double spaces */
|
||||
cp = temp;
|
||||
char *md = maindata;
|
||||
int newlen = 0;
|
||||
comment = 0;
|
||||
a = maindata_len;
|
||||
bool skip_until_closing_brace = false;
|
||||
while (a--) {
|
||||
|
||||
if (cp[0] == '/' && cp[1] == '*') {
|
||||
comment = 1;
|
||||
cp[0] = cp[1] = 32;
|
||||
}
|
||||
if (cp[0] == '*' && cp[1] == '/') {
|
||||
comment = 0;
|
||||
cp[0] = cp[1] = 32;
|
||||
}
|
||||
|
||||
/* do not copy when: */
|
||||
if (comment) {
|
||||
/* pass */
|
||||
}
|
||||
else if (cp[0] == ' ' && cp[1] == ' ') {
|
||||
/* pass */
|
||||
}
|
||||
else if (cp[-1] == '*' && cp[0] == ' ') {
|
||||
/* pointers with a space */
|
||||
} /* skip special keywords */
|
||||
else if (match_identifier(cp, "DNA_DEPRECATED")) {
|
||||
/* single values are skipped already, so decrement 1 less */
|
||||
a -= 13;
|
||||
cp += 13;
|
||||
}
|
||||
else if (match_identifier(cp, "DNA_DEFINE_CXX_METHODS")) {
|
||||
/* single values are skipped already, so decrement 1 less */
|
||||
a -= 21;
|
||||
cp += 21;
|
||||
skip_until_closing_brace = true;
|
||||
}
|
||||
else if (skip_until_closing_brace) {
|
||||
if (cp[0] == ')') {
|
||||
skip_until_closing_brace = false;
|
||||
}
|
||||
}
|
||||
else if (match_preproc_prefix(cp, cpp_block_start)) {
|
||||
char *end_ptr = match_preproc_strstr(cp, cpp_block_end);
|
||||
|
||||
if (end_ptr == nullptr) {
|
||||
fprintf(stderr, "Error: '%s' block must end with '%s'\n", cpp_block_start, cpp_block_end);
|
||||
}
|
||||
else {
|
||||
const int skip_offset = end_ptr - cp + strlen(cpp_block_end);
|
||||
a -= skip_offset;
|
||||
cp += skip_offset;
|
||||
}
|
||||
}
|
||||
else {
|
||||
md[0] = cp[0];
|
||||
md++;
|
||||
newlen++;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
|
||||
MEM_freeN(temp);
|
||||
return newlen;
|
||||
}
|
||||
|
||||
static void *read_file_data(const char *filepath, int *r_len)
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
@ -700,198 +601,148 @@ static void *read_file_data(const char *filepath, int *r_len)
|
|||
return data;
|
||||
}
|
||||
|
||||
using namespace blender::dna::parser::ast;
|
||||
|
||||
struct StrucMemberRegister {
|
||||
|
||||
int strct{0};
|
||||
short *structpoin{nullptr};
|
||||
short *sp{nullptr};
|
||||
const char *filepath{nullptr};
|
||||
|
||||
std::optional<int> _add_type(std::string_view type)
|
||||
{
|
||||
/** TODO: if type is enum, replace by it fixed size. */
|
||||
const std::string type_str = fmt::format("{}", type);
|
||||
if (ELEM(type, "long", "ulong")) {
|
||||
fprintf(stderr,
|
||||
"File '%s' contains use of \"%s\" in DNA struct which is not allowed\n",
|
||||
filepath,
|
||||
type_str.c_str());
|
||||
return -1;
|
||||
}
|
||||
const int type_result = add_type(type_str.c_str(), 0);
|
||||
if (type_result == -1) {
|
||||
fprintf(
|
||||
stderr, "File '%s' contains struct we can't parse \"%s\"\n", filepath, type_str.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
return type_result;
|
||||
}
|
||||
|
||||
bool _add_name(int type, const std::string &name)
|
||||
{
|
||||
const int name_result = add_name(version_elem_static_from_alias(strct, name.c_str()));
|
||||
if (name_result == -1) {
|
||||
fprintf(stderr,
|
||||
"File '%s' contains struct with name that can't be added \"%s\"\n",
|
||||
filepath,
|
||||
name.c_str());
|
||||
return false;
|
||||
}
|
||||
sp[0] = type;
|
||||
sp[1] = name_result;
|
||||
structpoin[1]++;
|
||||
sp += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(FunctionPtr &fn_ptr)
|
||||
{
|
||||
std::optional<int> type = _add_type(fn_ptr.type);
|
||||
if (!type.has_value()) {
|
||||
return false;
|
||||
}
|
||||
const std::string name = fmt::format("(*{})()", fn_ptr.name);
|
||||
return _add_name(type.value(), name);
|
||||
}
|
||||
|
||||
bool operator()(PointerToArray &array_ptr)
|
||||
{
|
||||
std::optional<int> type = _add_type(array_ptr.type);
|
||||
if (!type.has_value()) {
|
||||
return false;
|
||||
}
|
||||
const std::string name = fmt::format("(*{})[{}]", array_ptr.name, array_ptr.size);
|
||||
return _add_name(type.value(), name);
|
||||
}
|
||||
|
||||
bool operator()(Struct & /*struct*/)
|
||||
{
|
||||
printf("Unexpedted child struct declaration.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(Variable &var)
|
||||
{
|
||||
std::optional<int> type = _add_type(var.type);
|
||||
if (!type.has_value()) {
|
||||
return false;
|
||||
}
|
||||
for (auto &var_item : var.items) {
|
||||
std::string name_str = fmt::format("{}{}", var_item.ptr.value_or(""), var_item.name);
|
||||
for (auto &size : var_item.size) {
|
||||
if (std::holds_alternative<std::string_view>(size)) {
|
||||
/** TODO: Look to #defines to find name. */
|
||||
// name_str += fmt::format("[{}]", std::get<std::string_view>(size));
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
name_str += fmt::format("[{}]", std::get<int32_t>(size));
|
||||
}
|
||||
}
|
||||
if (!_add_name(type.value(), name_str)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static int convert_include(const char *filepath)
|
||||
{
|
||||
/* read include file, skip structs with a '#' before it.
|
||||
* store all data in temporal arrays.
|
||||
*/
|
||||
|
||||
int maindata_len;
|
||||
char *maindata = static_cast<char *>(read_file_data(filepath, &maindata_len));
|
||||
char *md = maindata;
|
||||
if (maindata_len == -1) {
|
||||
std::ifstream file(std::string{filepath});
|
||||
if (!file.is_open()) {
|
||||
fprintf(stderr, "Can't read file %s\n", filepath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
maindata_len = preprocess_include(maindata, maindata_len);
|
||||
char *mainend = maindata + maindata_len - 1;
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
std::string text = buffer.str();
|
||||
|
||||
/* we look for '{' and then back to 'struct' */
|
||||
int count = 0;
|
||||
bool skip_struct = false;
|
||||
while (count < maindata_len) {
|
||||
/* Generate tokens. */
|
||||
blender::dna::lex::TokenIterator iterator;
|
||||
iterator.process_text(filepath, text);
|
||||
/* Parse tokens. */
|
||||
blender::Vector<blender::dna::parser::ast::CppType> cpp_defines;
|
||||
blender::dna::parser::parse_include(filepath, text, iterator, cpp_defines);
|
||||
|
||||
/* code for skipping a struct: two hashes on 2 lines. (preprocess added a space) */
|
||||
if (md[0] == '#' && md[1] == ' ' && md[2] == '#') {
|
||||
skip_struct = true;
|
||||
/* Generate DNA. */
|
||||
for (auto &cpp_type : cpp_defines) {
|
||||
if (!std::holds_alternative<Struct>(cpp_type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (md[0] == '{') {
|
||||
md[0] = 0;
|
||||
if (skip_struct) {
|
||||
skip_struct = false;
|
||||
}
|
||||
else {
|
||||
if (md[-1] == ' ') {
|
||||
md[-1] = 0;
|
||||
}
|
||||
char *md1 = md - 2;
|
||||
while (*md1 != 32) {
|
||||
/* to beginning of word */
|
||||
md1--;
|
||||
}
|
||||
md1++;
|
||||
|
||||
/* we've got a struct name when... */
|
||||
if (match_identifier(md1 - 7, "struct")) {
|
||||
|
||||
const int strct = add_type(md1, 0);
|
||||
if (strct == -1) {
|
||||
fprintf(stderr, "File '%s' contains struct we can't parse \"%s\"\n", filepath, md1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
short *structpoin = add_struct(strct);
|
||||
short *sp = structpoin + 2;
|
||||
|
||||
DEBUG_PRINTF(1, "\t|\t|-- detected struct %s\n", types[strct]);
|
||||
|
||||
/* first lets make it all nice strings */
|
||||
md1 = md + 1;
|
||||
while (*md1 != '}') {
|
||||
if (md1 > mainend) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ELEM(*md1, ',', ' ')) {
|
||||
*md1 = 0;
|
||||
}
|
||||
md1++;
|
||||
}
|
||||
|
||||
/* read types and names until first character that is not '}' */
|
||||
md1 = md + 1;
|
||||
while (*md1 != '}') {
|
||||
if (md1 > mainend) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip when it says 'struct' or 'unsigned' or 'const' */
|
||||
if (*md1) {
|
||||
const char *md1_prev = md1;
|
||||
while (match_identifier_and_advance(&md1, "struct") ||
|
||||
match_identifier_and_advance(&md1, "unsigned") ||
|
||||
match_identifier_and_advance(&md1, "const"))
|
||||
{
|
||||
if (UNLIKELY(!ELEM(*md1, '\0', ' '))) {
|
||||
/* This will happen with: `unsigned(*value)[3]` which isn't supported. */
|
||||
fprintf(stderr,
|
||||
"File '%s' contains non white space character "
|
||||
"\"%c\" after identifier \"%s\"\n",
|
||||
filepath,
|
||||
*md1,
|
||||
md1_prev);
|
||||
return 1;
|
||||
}
|
||||
/* Skip ' ' or '\0'. */
|
||||
md1++;
|
||||
}
|
||||
|
||||
/* we've got a type! */
|
||||
if (STR_ELEM(md1, "long", "ulong")) {
|
||||
/* Forbid using long/ulong because those can be either 32 or 64 bit. */
|
||||
fprintf(stderr,
|
||||
"File '%s' contains use of \"%s\" in DNA struct which is not allowed\n",
|
||||
filepath,
|
||||
md1);
|
||||
return -1;
|
||||
}
|
||||
const int type = add_type(md1, 0);
|
||||
if (type == -1) {
|
||||
fprintf(
|
||||
stderr, "File '%s' contains struct we can't parse \"%s\"\n", filepath, md1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF(1, "\t|\t|\tfound type %s (", md1);
|
||||
|
||||
md1 += strlen(md1);
|
||||
|
||||
/* read until ';' */
|
||||
while (*md1 != ';') {
|
||||
if (md1 > mainend) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*md1) {
|
||||
/* We've got a name. slen needs
|
||||
* correction for function
|
||||
* pointers! */
|
||||
int slen = int(strlen(md1));
|
||||
if (md1[slen - 1] == ';') {
|
||||
md1[slen - 1] = 0;
|
||||
|
||||
const int name = add_name(version_elem_static_from_alias(strct, md1));
|
||||
if (name == -1) {
|
||||
fprintf(stderr,
|
||||
"File '%s' contains struct with name that can't be added \"%s\"\n",
|
||||
filepath,
|
||||
md1);
|
||||
return 1;
|
||||
}
|
||||
slen += additional_slen_offset;
|
||||
sp[0] = type;
|
||||
sp[1] = name;
|
||||
|
||||
if (names[name] != nullptr) {
|
||||
DEBUG_PRINTF(1, "%s |", names[name]);
|
||||
}
|
||||
|
||||
structpoin[1]++;
|
||||
sp += 2;
|
||||
|
||||
md1 += slen;
|
||||
break;
|
||||
}
|
||||
|
||||
const int name = add_name(version_elem_static_from_alias(strct, md1));
|
||||
if (name == -1) {
|
||||
fprintf(stderr,
|
||||
"File '%s' contains struct with name that can't be added \"%s\"\n",
|
||||
filepath,
|
||||
md1);
|
||||
return 1;
|
||||
}
|
||||
slen += additional_slen_offset;
|
||||
|
||||
sp[0] = type;
|
||||
sp[1] = name;
|
||||
if (names[name] != nullptr) {
|
||||
DEBUG_PRINTF(1, "%s ||", names[name]);
|
||||
}
|
||||
|
||||
structpoin[1]++;
|
||||
sp += 2;
|
||||
|
||||
md1 += slen;
|
||||
}
|
||||
md1++;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF(1, ")\n");
|
||||
}
|
||||
md1++;
|
||||
}
|
||||
}
|
||||
Struct &struct_def = std::get<Struct>(cpp_type);
|
||||
const std::string struct_name = fmt::format("{}", struct_def.name);
|
||||
const int strct = add_type(struct_name.c_str(), 0);
|
||||
if (strct == -1) {
|
||||
fprintf(stderr,
|
||||
"File '%s' contains struct we can't parse \"%s\"\n",
|
||||
filepath,
|
||||
struct_name.c_str());
|
||||
return 1;
|
||||
}
|
||||
short *structpoin = add_struct(strct);
|
||||
StrucMemberRegister member_register{strct, structpoin, structpoin + 2, filepath};
|
||||
/** Register struct members. */
|
||||
for (auto &item : struct_def.items) {
|
||||
if (!std::visit(member_register, item)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
md++;
|
||||
}
|
||||
|
||||
MEM_freeN(maindata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1328,6 +1179,10 @@ static int make_structDNA(const char *base_directory,
|
|||
char str[SDNA_MAX_FILENAME_LENGTH];
|
||||
SNPRINTF(str, "%s%s", base_directory, includefiles[i]);
|
||||
DEBUG_PRINTF(0, "\t|-- Converting %s\n", str);
|
||||
/* `DNA_genfile.h` only contains functions declarations that can't be parsed at the moment. */
|
||||
if (ELEM(includefiles[i], std::string_view{"DNA_defs.h"}, std::string_view{"DNA_genfile.h"})) {
|
||||
continue;
|
||||
}
|
||||
if (convert_include(str)) {
|
||||
return 1;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Although it is ignored by the parser, even the ignored structures are parsed then discarded, expressions like sums would not be supported to parse