Dump wild_encounter data to C/JSON

This commit is contained in:
PikalaxALT
2019-10-31 20:05:10 -04:00
parent 079310c70b
commit 3f7c66703b
10 changed files with 12971 additions and 7286 deletions
+1
View File
@@ -42,6 +42,7 @@ sound/**/*.bin
sound/songs/midi/*.s sound/songs/midi/*.s
src/*.s src/*.s
src/data/items.h src/data/items.h
src/data/wild_encounters.h
tags tags
tools/agbcc tools/agbcc
tools/binutils tools/binutils
+1
View File
@@ -45,6 +45,7 @@ ELF = $(ROM:.gba=.elf)
MAP = $(ROM:.gba=.map) MAP = $(ROM:.gba=.map)
C_SUBDIR = src C_SUBDIR = src
DATA_C_SUBDIR = src/data
ASM_SUBDIR = asm ASM_SUBDIR = asm
DATA_ASM_SUBDIR = data DATA_ASM_SUBDIR = data
SONG_SUBDIR = sound/songs SONG_SUBDIR = sound/songs
-11
View File
@@ -6,17 +6,6 @@
.section .rodata .section .rodata
.align 2 .align 2
.include "data/wild_encounters.inc"
gUnknown_83CA71C:: @ 83CA71C
.byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27
.byte 2, 2, 2, 3, 3, 3, 7, 7, 7, 20, 20, 14
.byte 13, 13, 13, 13, 18, 18, 18, 18, 8, 8, 4, 4
.byte 15, 15, 11, 11, 9, 9, 17, 17, 17, 16, 16, 16
.byte 24, 24, 19, 19, 6, 6, 6, 5, 5, 5, 10, 10
.byte 21, 21, 21, 22, 22, 22, 23, 23, 12, 12, 1, 1
.byte 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26
.incbin "baserom.gba", 0x3CA770, 0xE80 .incbin "baserom.gba", 0x3CA770, 0xE80
gUnknown_83CB5F0:: @ 83CB5F0 gUnknown_83CB5F0:: @ 83CB5F0
File diff suppressed because it is too large Load Diff
+9 -3
View File
@@ -1,9 +1,15 @@
# JSON files are run through jsonproc, which is a tool that converts JSON data to an output file # JSON files are run through jsonproc, which is a tool that converts JSON data to an output file
# based on an Inja template. https://github.com/pantor/inja # based on an Inja template. https://github.com/pantor/inja
AUTO_GEN_TARGETS += src/data/items.h AUTO_GEN_TARGETS += $(DATA_C_SUBDIR)/items.h
src/data/items.h: src/data/items.json src/data/items.json.txt $(DATA_C_SUBDIR)/items.h: $(DATA_C_SUBDIR)/items.json $(DATA_C_SUBDIR)/items.json.txt
$(JSONPROC) $^ $@ $(JSONPROC) $^ $@
$(C_BUILDDIR)/item.o: c_dep += src/data/items.h $(C_BUILDDIR)/item.o: c_dep += $(DATA_C_SUBDIR)/items.h
AUTO_GEN_TARGETS += $(DATA_C_SUBDIR)/wild_encounters.h
$(DATA_C_SUBDIR)/wild_encounters.h: $(DATA_C_SUBDIR)/wild_encounters.json $(DATA_C_SUBDIR)/wild_encounters.json.txt
$(JSONPROC) $^ $@
$(C_BUILDDIR)/wild_encounter.o: c_dep += $(DATA_C_SUBDIR)/wild_encounters.h
File diff suppressed because it is too large Load Diff
+90
View File
@@ -0,0 +1,90 @@
{{ doNotModifyHeader }}
## for wild_encounter_group in wild_encounter_groups
{% if wild_encounter_group.for_maps %}
## for wild_encounter_field in wild_encounter_group.fields
{% if not existsIn(wild_encounter_field, "groups") %}
## for encounter_rate in wild_encounter_field.encounter_rates
{% if loop.index == 0 %}
#define ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_SLOT_{{ loop.index }} {{ encounter_rate }} {% else %}#define ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_SLOT_{{ loop.index }} ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_SLOT_{{ subtract(loop.index, 1) }} + {{ encounter_rate }}{% endif %} {{ setVarInt(wild_encounter_field.type, loop.index) }}
## endfor
#define ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_TOTAL (ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_SLOT_{{ getVar(wild_encounter_field.type) }})
{% else %}
## for field_subgroup_key, field_subgroup_subarray in wild_encounter_field.groups
## for field_subgroup_index in field_subgroup_subarray
{% if loop.index == 0 %}
#define ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_{{ upper(field_subgroup_key) }}_SLOT_{{ field_subgroup_index }} {{ at(wild_encounter_field.encounter_rates, field_subgroup_index) }} {% else %}#define ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_{{ upper(field_subgroup_key) }}_SLOT_{{ field_subgroup_index }} ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_{{ upper(field_subgroup_key) }}_SLOT_{{ getVar("previous_slot") }} + {{ at(wild_encounter_field.encounter_rates, field_subgroup_index) }}{% endif %}{{ setVarInt(concat(wild_encounter_field.type, field_subgroup_key), field_subgroup_index) }}{{ setVarInt("previous_slot", field_subgroup_index) }}
## endfor
#define ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_{{ upper(field_subgroup_key) }}_TOTAL (ENCOUNTER_CHANCE_{{ upper(wild_encounter_field.type) }}_{{ upper(field_subgroup_key) }}_SLOT_{{ getVar(concat(wild_encounter_field.type, field_subgroup_key)) }})
## endfor
{% endif %}
## endfor
{% endif %}
## for encounter in wild_encounter_group.encounters
{% if existsIn(encounter, "land_mons") %}
const struct WildPokemon {{ encounter.base_label }}_LandMons[] =
{
## for wild_mon in encounter.land_mons.mons
{ {{ wild_mon.min_level }}, {{ wild_mon.max_level }}, {{ wild_mon.species }} },
## endfor
};
const struct WildPokemonInfo {{ encounter.base_label }}_LandMonsInfo = { {{encounter.land_mons.encounter_rate}}, {{ encounter.base_label }}_LandMons };
{% endif %}
{% if existsIn(encounter, "water_mons") %}
const struct WildPokemon {{ encounter.base_label }}_WaterMons[] =
{
## for wild_mon in encounter.water_mons.mons
{ {{ wild_mon.min_level }}, {{ wild_mon.max_level }}, {{ wild_mon.species }} },
## endfor
};
const struct WildPokemonInfo {{ encounter.base_label }}_WaterMonsInfo = { {{encounter.water_mons.encounter_rate}}, {{ encounter.base_label }}_WaterMons };
{% endif %}
{% if existsIn(encounter, "rock_smash_mons") %}
const struct WildPokemon {{ encounter.base_label }}_RockSmashMons[] =
{
## for wild_mon in encounter.rock_smash_mons.mons
{ {{ wild_mon.min_level }}, {{ wild_mon.max_level }}, {{ wild_mon.species }} },
## endfor
};
const struct WildPokemonInfo {{ encounter.base_label }}_RockSmashMonsInfo = { {{encounter.rock_smash_mons.encounter_rate}}, {{ encounter.base_label }}_RockSmashMons };
{% endif %}
{% if existsIn(encounter, "fishing_mons") %}
const struct WildPokemon {{ encounter.base_label }}_FishingMons[] =
{
## for wild_mon in encounter.fishing_mons.mons
{ {{ wild_mon.min_level }}, {{ wild_mon.max_level }}, {{ wild_mon.species }} },
## endfor
};
const struct WildPokemonInfo {{ encounter.base_label }}_FishingMonsInfo = { {{encounter.fishing_mons.encounter_rate}}, {{ encounter.base_label }}_FishingMons };
{% endif %}
## endfor
const struct WildPokemonHeader {{ wild_encounter_group.label }}[] =
{
## for encounter in wild_encounter_group.encounters
{
.mapGroup = {% if wild_encounter_group.for_maps %}MAP_GROUP({{ removePrefix(encounter.map, "MAP_") }}){% else %}0{% endif %},
.mapNum = {% if wild_encounter_group.for_maps %}MAP_NUM({{ removePrefix(encounter.map, "MAP_") }}){% else %}{{ loop.index1 }}{% endif %},
.landMonsInfo = {% if existsIn(encounter, "land_mons") %}&{{ encounter.base_label }}_LandMonsInfo{% else %}NULL{% endif %},
.waterMonsInfo = {% if existsIn(encounter, "water_mons") %}&{{ encounter.base_label }}_WaterMonsInfo{% else %}NULL{% endif %},
.rockSmashMonsInfo = {% if existsIn(encounter, "rock_smash_mons") %}&{{ encounter.base_label }}_RockSmashMonsInfo{% else %}NULL{% endif %},
.fishingMonsInfo = {% if existsIn(encounter, "fishing_mons") %}&{{ encounter.base_label }}_FishingMonsInfo{% else %}NULL{% endif %},
},
## endfor
{
.mapGroup = MAP_GROUP(UNDEFINED),
.mapNum = MAP_NUM(UNDEFINED),
.landMonsInfo = NULL,
.waterMonsInfo = NULL,
.rockSmashMonsInfo = NULL,
.fishingMonsInfo = NULL,
},
};
## endfor
+20 -3
View File
@@ -31,8 +31,6 @@ struct WildEncounterData
static EWRAM_DATA struct WildEncounterData sWildEncounterData = {}; static EWRAM_DATA struct WildEncounterData sWildEncounterData = {};
static EWRAM_DATA bool8 sWildEncountersDisabled = FALSE; static EWRAM_DATA bool8 sWildEncountersDisabled = FALSE;
extern const u8 gUnknown_83CA71C[][12];
static bool8 UnlockedTanobyOrAreNotInTanoby(void); static bool8 UnlockedTanobyOrAreNotInTanoby(void);
static u32 GenerateUnownPersonalityByLetter(u8 letter); static u32 GenerateUnownPersonalityByLetter(u8 letter);
static bool8 IsWildLevelAllowedByRepel(u8 level); static bool8 IsWildLevelAllowedByRepel(u8 level);
@@ -43,6 +41,25 @@ static u8 IsLeadMonHoldingCleanseTag(void);
static u16 WildEncounterRandom(void); static u16 WildEncounterRandom(void);
static void AddToWildEncounterRateBuff(u8 encouterRate); static void AddToWildEncounterRateBuff(u8 encouterRate);
#include "data/wild_encounters.h"
static const u8 sUnownLetterSlots[][12] = {
// A A A A A A A A A A A ?
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27},
// C C C D D D H H H U U O
{ 2, 2, 2, 3, 3, 3, 7, 7, 7, 20, 20, 14},
// N N N N S S S S I I E E
{13, 13, 13, 13, 18, 18, 18, 18, 8, 8, 4, 4},
// P P L L J J R R R Q Q Q
{15, 15, 11, 11, 9, 9, 17, 17, 17, 16, 16, 16},
// Y Y T T G G G F F F K K
{24, 24, 19, 19, 6, 6, 6, 5, 5, 5, 10, 10},
// V V V W W W X X M M B B
{21, 21, 21, 22, 22, 22, 23, 23, 12, 12, 1, 1},
// Z Z Z Z Z Z Z Z Z Z Z !
{25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26},
};
void DisableWildEncounters(bool8 state) void DisableWildEncounters(bool8 state)
{ {
sWildEncountersDisabled = state; sWildEncountersDisabled = state;
@@ -209,7 +226,7 @@ static void GenerateWildMon(u16 species, u8 level, u8 slot)
else else
{ {
chamber = gSaveBlock1Ptr->location.mapNum - MAP_NUM(SEVEN_ISLAND_TANOBY_RUINS_MONEAN_CHAMBER); chamber = gSaveBlock1Ptr->location.mapNum - MAP_NUM(SEVEN_ISLAND_TANOBY_RUINS_MONEAN_CHAMBER);
personality = GenerateUnownPersonalityByLetter(gUnknown_83CA71C[chamber][slot]); personality = GenerateUnownPersonalityByLetter(sUnownLetterSlots[chamber][slot]);
CreateMon(&gEnemyParty[0], species, level, 32, TRUE, personality, FALSE, 0); CreateMon(&gEnemyParty[0], species, level, 32, TRUE, personality, FALSE, 0);
} }
} }
+204 -71
View File
@@ -517,7 +517,7 @@ public:
typedef const_pointer iterator; typedef const_pointer iterator;
typedef const_pointer const_iterator; typedef const_pointer const_iterator;
typedef std::reverse_iterator< const_iterator > reverse_iterator; typedef std::reverse_iterator< const_iterator > reverse_iterator;
typedef std::reverse_iterator< const_iterator > const_reverse_iterator; typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
typedef std::size_t size_type; typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type; typedef std::ptrdiff_t difference_type;
@@ -1411,6 +1411,9 @@ enum class ElementNotation {
Pointer Pointer
}; };
/*!
* \brief Class for lexer configuration.
*/
struct LexerConfig { struct LexerConfig {
std::string statement_open {"{%"}; std::string statement_open {"{%"};
std::string statement_close {"%}"}; std::string statement_close {"%}"};
@@ -1421,6 +1424,9 @@ struct LexerConfig {
std::string comment_close {"#}"}; std::string comment_close {"#}"};
std::string open_chars {"#{"}; std::string open_chars {"#{"};
bool trim_blocks {false};
bool lstrip_blocks {false};
void update_open_chars() { void update_open_chars() {
open_chars = ""; open_chars = "";
if (open_chars.find(line_statement[0]) == std::string::npos) { if (open_chars.find(line_statement[0]) == std::string::npos) {
@@ -1438,6 +1444,9 @@ struct LexerConfig {
} }
}; };
/*!
* \brief Class for parser configuration.
*/
struct ParserConfig { struct ParserConfig {
ElementNotation notation {ElementNotation::Dot}; ElementNotation notation {ElementNotation::Dot};
}; };
@@ -1450,10 +1459,13 @@ struct ParserConfig {
#ifndef PANTOR_INJA_FUNCTION_STORAGE_HPP #ifndef PANTOR_INJA_FUNCTION_STORAGE_HPP
#define PANTOR_INJA_FUNCTION_STORAGE_HPP #define PANTOR_INJA_FUNCTION_STORAGE_HPP
#include <vector>
// #include "bytecode.hpp" // #include "bytecode.hpp"
#ifndef PANTOR_INJA_BYTECODE_HPP #ifndef PANTOR_INJA_BYTECODE_HPP
#define PANTOR_INJA_BYTECODE_HPP #define PANTOR_INJA_BYTECODE_HPP
#include <string>
#include <utility> #include <utility>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@@ -1464,7 +1476,7 @@ struct ParserConfig {
namespace inja { namespace inja {
using namespace nlohmann; using json = nlohmann::json;
struct Bytecode { struct Bytecode {
@@ -1492,6 +1504,7 @@ struct Bytecode {
GreaterEqual, GreaterEqual,
Less, Less,
LessEqual, LessEqual,
At,
Different, Different,
DivisibleBy, DivisibleBy,
Even, Even,
@@ -1594,6 +1607,9 @@ using namespace nlohmann;
using Arguments = std::vector<const json*>; using Arguments = std::vector<const json*>;
using CallbackFunction = std::function<json(Arguments& args)>; using CallbackFunction = std::function<json(Arguments& args)>;
/*!
* \brief Class for builtin functions and user-defined callbacks.
*/
class FunctionStorage { class FunctionStorage {
public: public:
void add_builtin(nonstd::string_view name, unsigned int num_args, Bytecode::Op op) { void add_builtin(nonstd::string_view name, unsigned int num_args, Bytecode::Op op) {
@@ -1658,6 +1674,9 @@ class FunctionStorage {
#define PANTOR_INJA_PARSER_HPP #define PANTOR_INJA_PARSER_HPP
#include <limits> #include <limits>
#include <string>
#include <utility>
#include <vector>
// #include "bytecode.hpp" // #include "bytecode.hpp"
@@ -1678,12 +1697,17 @@ class FunctionStorage {
#ifndef PANTOR_INJA_TOKEN_HPP #ifndef PANTOR_INJA_TOKEN_HPP
#define PANTOR_INJA_TOKEN_HPP #define PANTOR_INJA_TOKEN_HPP
#include <string>
// #include "string_view.hpp" // #include "string_view.hpp"
namespace inja { namespace inja {
/*!
* \brief Helper-class for the inja Parser.
*/
struct Token { struct Token {
enum class Kind { enum class Kind {
Text, Text,
@@ -1737,13 +1761,17 @@ struct Token {
} }
#endif // PANTOR_INJA_TOKEN_HPP #endif // PANTOR_INJA_TOKEN_HPP
// #include "utils.hpp" // #include "utils.hpp"
#ifndef PANTOR_INJA_UTILS_HPP #ifndef PANTOR_INJA_UTILS_HPP
#define PANTOR_INJA_UTILS_HPP #define PANTOR_INJA_UTILS_HPP
#include <algorithm>
#include <fstream>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <utility>
// #include "string_view.hpp" // #include "string_view.hpp"
@@ -1755,11 +1783,22 @@ inline void inja_throw(const std::string& type, const std::string& message) {
throw std::runtime_error("[inja.exception." + type + "] " + message); throw std::runtime_error("[inja.exception." + type + "] " + message);
} }
inline std::ifstream open_file_or_throw(const std::string& path) {
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
file.open(path);
} catch(const std::ios_base::failure& e) {
inja_throw("file_error", "failed accessing file at '" + path + "'");
}
return file;
}
namespace string_view { namespace string_view {
inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) { inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
start = std::min(start, view.size()); start = std::min(start, view.size());
end = std::min(std::max(start, end), view.size()); end = std::min(std::max(start, end), view.size());
return view.substr(start, end - start); // StringRef(Data + Start, End - Start); return view.substr(start, end - start); // StringRef(Data + Start, End - Start);
} }
inline std::pair<nonstd::string_view, nonstd::string_view> split(nonstd::string_view view, char Separator) { inline std::pair<nonstd::string_view, nonstd::string_view> split(nonstd::string_view view, char Separator) {
@@ -1783,6 +1822,9 @@ namespace string_view {
namespace inja { namespace inja {
/*!
* \brief Class for lexing an inja Template.
*/
class Lexer { class Lexer {
enum class State { enum class State {
Text, Text,
@@ -1831,12 +1873,15 @@ class Lexer {
// try to match one of the opening sequences, and get the close // try to match one of the opening sequences, and get the close
nonstd::string_view open_str = m_in.substr(m_pos); nonstd::string_view open_str = m_in.substr(m_pos);
bool must_lstrip = false;
if (inja::string_view::starts_with(open_str, m_config.expression_open)) { if (inja::string_view::starts_with(open_str, m_config.expression_open)) {
m_state = State::ExpressionStart; m_state = State::ExpressionStart;
} else if (inja::string_view::starts_with(open_str, m_config.statement_open)) { } else if (inja::string_view::starts_with(open_str, m_config.statement_open)) {
m_state = State::StatementStart; m_state = State::StatementStart;
must_lstrip = m_config.lstrip_blocks;
} else if (inja::string_view::starts_with(open_str, m_config.comment_open)) { } else if (inja::string_view::starts_with(open_str, m_config.comment_open)) {
m_state = State::CommentStart; m_state = State::CommentStart;
must_lstrip = m_config.lstrip_blocks;
} else if ((m_pos == 0 || m_in[m_pos - 1] == '\n') && } else if ((m_pos == 0 || m_in[m_pos - 1] == '\n') &&
inja::string_view::starts_with(open_str, m_config.line_statement)) { inja::string_view::starts_with(open_str, m_config.line_statement)) {
m_state = State::LineStart; m_state = State::LineStart;
@@ -1844,8 +1889,13 @@ class Lexer {
m_pos += 1; // wasn't actually an opening sequence m_pos += 1; // wasn't actually an opening sequence
goto again; goto again;
} }
if (m_pos == m_tok_start) goto again; // don't generate empty token
return make_token(Token::Kind::Text); nonstd::string_view text = string_view::slice(m_in, m_tok_start, m_pos);
if (must_lstrip)
text = clear_final_line_if_whitespace(text);
if (text.empty()) goto again; // don't generate empty token
return Token(Token::Kind::Text, text);
} }
case State::ExpressionStart: { case State::ExpressionStart: {
m_state = State::ExpressionBody; m_state = State::ExpressionBody;
@@ -1872,7 +1922,7 @@ class Lexer {
case State::LineBody: case State::LineBody:
return scan_body("\n", Token::Kind::LineStatementClose); return scan_body("\n", Token::Kind::LineStatementClose);
case State::StatementBody: case State::StatementBody:
return scan_body(m_config.statement_close, Token::Kind::StatementClose); return scan_body(m_config.statement_close, Token::Kind::StatementClose, m_config.trim_blocks);
case State::CommentBody: { case State::CommentBody: {
// fast-scan to comment close // fast-scan to comment close
size_t end = m_in.substr(m_pos).find(m_config.comment_close); size_t end = m_in.substr(m_pos).find(m_config.comment_close);
@@ -1883,7 +1933,10 @@ class Lexer {
// return the entire comment in the close token // return the entire comment in the close token
m_state = State::Text; m_state = State::Text;
m_pos += end + m_config.comment_close.size(); m_pos += end + m_config.comment_close.size();
return make_token(Token::Kind::CommentClose); Token tok = make_token(Token::Kind::CommentClose);
if (m_config.trim_blocks)
skip_newline();
return tok;
} }
} }
} }
@@ -1891,7 +1944,7 @@ class Lexer {
const LexerConfig& get_config() const { return m_config; } const LexerConfig& get_config() const { return m_config; }
private: private:
Token scan_body(nonstd::string_view close, Token::Kind closeKind) { Token scan_body(nonstd::string_view close, Token::Kind closeKind, bool trim = false) {
again: again:
// skip whitespace (except for \n as it might be a close) // skip whitespace (except for \n as it might be a close)
if (m_tok_start >= m_in.size()) return make_token(Token::Kind::Eof); if (m_tok_start >= m_in.size()) return make_token(Token::Kind::Eof);
@@ -1905,7 +1958,10 @@ class Lexer {
if (inja::string_view::starts_with(m_in.substr(m_tok_start), close)) { if (inja::string_view::starts_with(m_in.substr(m_tok_start), close)) {
m_state = State::Text; m_state = State::Text;
m_pos = m_tok_start + close.size(); m_pos = m_tok_start + close.size();
return make_token(closeKind); Token tok = make_token(closeKind);
if (trim)
skip_newline();
return tok;
} }
// skip \n // skip \n
@@ -2026,6 +2082,34 @@ class Lexer {
Token make_token(Token::Kind kind) const { Token make_token(Token::Kind kind) const {
return Token(kind, string_view::slice(m_in, m_tok_start, m_pos)); return Token(kind, string_view::slice(m_in, m_tok_start, m_pos));
} }
void skip_newline() {
if (m_pos < m_in.size()) {
char ch = m_in[m_pos];
if (ch == '\n')
m_pos += 1;
else if (ch == '\r') {
m_pos += 1;
if (m_pos < m_in.size() && m_in[m_pos] == '\n')
m_pos += 1;
}
}
}
static nonstd::string_view clear_final_line_if_whitespace(nonstd::string_view text)
{
nonstd::string_view result = text;
while (!result.empty()) {
char ch = result.back();
if (ch == ' ' || ch == '\t')
result.remove_suffix(1);
else if (ch == '\n' || ch == '\r')
break;
else
return text;
}
return result;
}
}; };
} }
@@ -2036,6 +2120,7 @@ class Lexer {
#ifndef PANTOR_INJA_TEMPLATE_HPP #ifndef PANTOR_INJA_TEMPLATE_HPP
#define PANTOR_INJA_TEMPLATE_HPP #define PANTOR_INJA_TEMPLATE_HPP
#include <map>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -2045,6 +2130,9 @@ class Lexer {
namespace inja { namespace inja {
/*!
* \brief The main inja Template.
*/
struct Template { struct Template {
std::vector<Bytecode> bytecodes; std::vector<Bytecode> bytecodes;
std::string content; std::string content;
@@ -2054,7 +2142,7 @@ using TemplateStorage = std::map<std::string, Template>;
} }
#endif // PANTOR_INJA_TEMPLATE_HPP #endif // PANTOR_INJA_TEMPLATE_HPP
// #include "token.hpp" // #include "token.hpp"
@@ -2068,6 +2156,7 @@ namespace inja {
class ParserStatic { class ParserStatic {
ParserStatic() { ParserStatic() {
functions.add_builtin("at", 2, Bytecode::Op::At);
functions.add_builtin("default", 2, Bytecode::Op::Default); functions.add_builtin("default", 2, Bytecode::Op::Default);
functions.add_builtin("divisibleBy", 2, Bytecode::Op::DivisibleBy); functions.add_builtin("divisibleBy", 2, Bytecode::Op::DivisibleBy);
functions.add_builtin("even", 1, Bytecode::Op::Even); functions.add_builtin("even", 1, Bytecode::Op::Even);
@@ -2107,13 +2196,16 @@ class ParserStatic {
FunctionStorage functions; FunctionStorage functions;
}; };
/*!
* \brief Class for parsing an inja Template.
*/
class Parser { class Parser {
public: public:
explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& included_templates): m_config(parser_config), m_lexer(lexer_config), m_included_templates(included_templates), m_static(ParserStatic::get_instance()) { } explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& included_templates): m_config(parser_config), m_lexer(lexer_config), m_included_templates(included_templates), m_static(ParserStatic::get_instance()) { }
bool parse_expression(Template& tmpl) { bool parse_expression(Template& tmpl) {
if (!parse_expression_and(tmpl)) return false; if (!parse_expression_and(tmpl)) return false;
if (m_tok.kind != Token::Kind::Id || m_tok.text != "or") return true; if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast<decltype(m_tok.text)>("or")) return true;
get_next_token(); get_next_token();
if (!parse_expression_and(tmpl)) return false; if (!parse_expression_and(tmpl)) return false;
append_function(tmpl, Bytecode::Op::Or, 2); append_function(tmpl, Bytecode::Op::Or, 2);
@@ -2122,7 +2214,7 @@ class Parser {
bool parse_expression_and(Template& tmpl) { bool parse_expression_and(Template& tmpl) {
if (!parse_expression_not(tmpl)) return false; if (!parse_expression_not(tmpl)) return false;
if (m_tok.kind != Token::Kind::Id || m_tok.text != "and") return true; if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast<decltype(m_tok.text)>("and")) return true;
get_next_token(); get_next_token();
if (!parse_expression_not(tmpl)) return false; if (!parse_expression_not(tmpl)) return false;
append_function(tmpl, Bytecode::Op::And, 2); append_function(tmpl, Bytecode::Op::And, 2);
@@ -2130,7 +2222,7 @@ class Parser {
} }
bool parse_expression_not(Template& tmpl) { bool parse_expression_not(Template& tmpl) {
if (m_tok.kind == Token::Kind::Id && m_tok.text == "not") { if (m_tok.kind == Token::Kind::Id && m_tok.text == static_cast<decltype(m_tok.text)>("not")) {
get_next_token(); get_next_token();
if (!parse_expression_not(tmpl)) return false; if (!parse_expression_not(tmpl)) return false;
append_function(tmpl, Bytecode::Op::Not, 1); append_function(tmpl, Bytecode::Op::Not, 1);
@@ -2145,7 +2237,7 @@ class Parser {
Bytecode::Op op; Bytecode::Op op;
switch (m_tok.kind) { switch (m_tok.kind) {
case Token::Kind::Id: case Token::Kind::Id:
if (m_tok.text == "in") if (m_tok.text == static_cast<decltype(m_tok.text)>("in"))
op = Bytecode::Op::In; op = Bytecode::Op::In;
else else
return true; return true;
@@ -2233,7 +2325,9 @@ class Parser {
append_callback(tmpl, func_token.text, num_args); append_callback(tmpl, func_token.text, num_args);
return true; return true;
} }
} else if (m_tok.text == "true" || m_tok.text == "false" || m_tok.text == "null") { } else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") ||
m_tok.text == static_cast<decltype(m_tok.text)>("false") ||
m_tok.text == static_cast<decltype(m_tok.text)>("null")) {
// true, false, null are json literals // true, false, null are json literals
if (brace_level == 0 && bracket_level == 0) { if (brace_level == 0 && bracket_level == 0) {
json_first = m_tok.text; json_first = m_tok.text;
@@ -2312,7 +2406,7 @@ class Parser {
bool parse_statement(Template& tmpl, nonstd::string_view path) { bool parse_statement(Template& tmpl, nonstd::string_view path) {
if (m_tok.kind != Token::Kind::Id) return false; if (m_tok.kind != Token::Kind::Id) return false;
if (m_tok.text == "if") { if (m_tok.text == static_cast<decltype(m_tok.text)>("if")) {
get_next_token(); get_next_token();
// evaluate expression // evaluate expression
@@ -2323,7 +2417,7 @@ class Parser {
// conditional jump; destination will be filled in by else or endif // conditional jump; destination will be filled in by else or endif
tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump); tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump);
} else if (m_tok.text == "endif") { } else if (m_tok.text == static_cast<decltype(m_tok.text)>("endif")) {
if (m_if_stack.empty()) { if (m_if_stack.empty()) {
inja_throw("parser_error", "endif without matching if"); inja_throw("parser_error", "endif without matching if");
} }
@@ -2342,7 +2436,7 @@ class Parser {
// pop if stack // pop if stack
m_if_stack.pop_back(); m_if_stack.pop_back();
} else if (m_tok.text == "else") { } else if (m_tok.text == static_cast<decltype(m_tok.text)>("else")) {
if (m_if_stack.empty()) if (m_if_stack.empty())
inja_throw("parser_error", "else without matching if"); inja_throw("parser_error", "else without matching if");
auto& if_data = m_if_stack.back(); auto& if_data = m_if_stack.back();
@@ -2358,7 +2452,7 @@ class Parser {
if_data.prev_cond_jump = std::numeric_limits<unsigned int>::max(); if_data.prev_cond_jump = std::numeric_limits<unsigned int>::max();
// chained else if // chained else if
if (m_tok.kind == Token::Kind::Id && m_tok.text == "if") { if (m_tok.kind == Token::Kind::Id && m_tok.text == static_cast<decltype(m_tok.text)>("if")) {
get_next_token(); get_next_token();
// evaluate expression // evaluate expression
@@ -2370,7 +2464,7 @@ class Parser {
// conditional jump; destination will be filled in by else or endif // conditional jump; destination will be filled in by else or endif
tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump); tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump);
} }
} else if (m_tok.text == "for") { } else if (m_tok.text == static_cast<decltype(m_tok.text)>("for")) {
get_next_token(); get_next_token();
// options: for a in arr; for a, b in obj // options: for a in arr; for a, b in obj
@@ -2389,7 +2483,7 @@ class Parser {
get_next_token(); get_next_token();
} }
if (m_tok.kind != Token::Kind::Id || m_tok.text != "in") if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast<decltype(m_tok.text)>("in"))
inja_throw("parser_error", inja_throw("parser_error",
"expected 'in', got '" + m_tok.describe() + "'"); "expected 'in', got '" + m_tok.describe() + "'");
get_next_token(); get_next_token();
@@ -2403,7 +2497,7 @@ class Parser {
tmpl.bytecodes.back().value = key_token.text; tmpl.bytecodes.back().value = key_token.text;
} }
tmpl.bytecodes.back().str = static_cast<std::string>(value_token.text); tmpl.bytecodes.back().str = static_cast<std::string>(value_token.text);
} else if (m_tok.text == "endfor") { } else if (m_tok.text == static_cast<decltype(m_tok.text)>("endfor")) {
get_next_token(); get_next_token();
if (m_loop_stack.empty()) { if (m_loop_stack.empty()) {
inja_throw("parser_error", "endfor without matching for"); inja_throw("parser_error", "endfor without matching for");
@@ -2415,7 +2509,7 @@ class Parser {
tmpl.bytecodes.emplace_back(Bytecode::Op::EndLoop); tmpl.bytecodes.emplace_back(Bytecode::Op::EndLoop);
tmpl.bytecodes.back().args = m_loop_stack.back() + 1; // loop body tmpl.bytecodes.back().args = m_loop_stack.back() + 1; // loop body
m_loop_stack.pop_back(); m_loop_stack.pop_back();
} else if (m_tok.text == "include") { } else if (m_tok.text == static_cast<decltype(m_tok.text)>("include")) {
get_next_token(); get_next_token();
if (m_tok.kind != Token::Kind::String) { if (m_tok.kind != Token::Kind::String) {
@@ -2431,8 +2525,10 @@ class Parser {
} }
// sys::path::remove_dots(pathname, true, sys::path::Style::posix); // sys::path::remove_dots(pathname, true, sys::path::Style::posix);
Template include_template = parse_template(pathname); if (m_included_templates.find(pathname) == m_included_templates.end()) {
m_included_templates.emplace(pathname, include_template); Template include_template = parse_template(pathname);
m_included_templates.emplace(pathname, include_template);
}
// generate a reference bytecode // generate a reference bytecode
tmpl.bytecodes.emplace_back(Bytecode::Op::Include, json(pathname), Bytecode::Flag::ValueImmediate); tmpl.bytecodes.emplace_back(Bytecode::Op::Include, json(pathname), Bytecode::Flag::ValueImmediate);
@@ -2552,10 +2648,10 @@ class Parser {
} }
std::string load_file(nonstd::string_view filename) { std::string load_file(nonstd::string_view filename) {
std::ifstream file(static_cast<std::string>(filename)); std::ifstream file = open_file_or_throw(static_cast<std::string>(filename));
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return text; return text;
} }
private: private:
const ParserConfig& m_config; const ParserConfig& m_config;
@@ -2605,6 +2701,7 @@ class Parser {
#if __cplusplus < 201402L #if __cplusplus < 201402L
#include <cstddef> #include <cstddef>
#include <memory>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@@ -2655,6 +2752,9 @@ namespace stdinja = std;
#include <algorithm> #include <algorithm>
#include <numeric> #include <numeric>
#include <string>
#include <utility>
#include <vector>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@@ -2679,6 +2779,9 @@ inline nonstd::string_view convert_dot_to_json_pointer(nonstd::string_view dot,
return nonstd::string_view(out.data(), out.size()); return nonstd::string_view(out.data(), out.size());
} }
/*!
* \brief Class for rendering a Template with data.
*/
class Renderer { class Renderer {
std::vector<const json*>& get_args(const Bytecode& bc) { std::vector<const json*>& get_args(const Bytecode& bc) {
m_tmp_args.clear(); m_tmp_args.clear();
@@ -2765,7 +2868,7 @@ class Renderer {
LoopLevel& level = m_loop_stack.back(); LoopLevel& level = m_loop_stack.back();
if (level.loop_type == LoopLevel::Type::Array) { if (level.loop_type == LoopLevel::Type::Array) {
level.data[static_cast<std::string>(level.value_name)] = level.values.at(level.index); // *level.it; level.data[static_cast<std::string>(level.value_name)] = level.values.at(level.index); // *level.it;
auto& loopData = level.data["loop"]; auto& loopData = level.data["loop"];
loopData["index"] = level.index; loopData["index"] = level.index;
loopData["index1"] = level.index + 1; loopData["index1"] = level.index + 1;
@@ -2787,8 +2890,8 @@ class Renderer {
enum class Type { Map, Array }; enum class Type { Map, Array };
Type loop_type; Type loop_type;
nonstd::string_view key_name; // variable name for keys nonstd::string_view key_name; // variable name for keys
nonstd::string_view value_name; // variable name for values nonstd::string_view value_name; // variable name for values
json data; // data with loop info added json data; // data with loop info added
json values; // values to iterate over json values; // values to iterate over
@@ -2800,8 +2903,8 @@ class Renderer {
// loop over map // loop over map
using KeyValue = std::pair<nonstd::string_view, json*>; using KeyValue = std::pair<nonstd::string_view, json*>;
using MapValues = std::vector<KeyValue>; using MapValues = std::vector<KeyValue>;
MapValues map_values; // values to iterate over MapValues map_values; // values to iterate over
MapValues::iterator map_it; // iterator over values MapValues::iterator map_it; // iterator over values
}; };
@@ -2835,11 +2938,11 @@ class Renderer {
} }
case Bytecode::Op::PrintValue: { case Bytecode::Op::PrintValue: {
const json& val = *get_args(bc)[0]; const json& val = *get_args(bc)[0];
if (val.is_string()) if (val.is_string()) {
os << val.get_ref<const std::string&>(); os << val.get_ref<const std::string&>();
else } else {
os << val.dump(); os << val.dump();
// val.dump(os); }
pop_args(bc); pop_args(bc);
break; break;
} }
@@ -2870,7 +2973,15 @@ class Renderer {
break; break;
} }
case Bytecode::Op::Length: { case Bytecode::Op::Length: {
auto result = get_args(bc)[0]->size(); const json& val = *get_args(bc)[0];
int result;
if (val.is_string()) {
result = val.get_ref<const std::string&>().length();
} else {
result = val.size();
}
pop_args(bc); pop_args(bc);
m_stack.emplace_back(result); m_stack.emplace_back(result);
break; break;
@@ -2882,6 +2993,13 @@ class Renderer {
m_stack.emplace_back(std::move(result)); m_stack.emplace_back(std::move(result));
break; break;
} }
case Bytecode::Op::At: {
auto args = get_args(bc);
auto result = args[0]->at(args[1]->get<int>());
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::First: { case Bytecode::Op::First: {
auto result = get_args(bc)[0]->front(); auto result = get_args(bc)[0]->front();
pop_args(bc); pop_args(bc);
@@ -3091,7 +3209,7 @@ class Renderer {
break; break;
} }
case Bytecode::Op::Include: case Bytecode::Op::Include:
Renderer(m_included_templates, m_callbacks).render_to(os, m_included_templates.find(get_imm(bc)->get_ref<const std::string&>())->second, data); Renderer(m_included_templates, m_callbacks).render_to(os, m_included_templates.find(get_imm(bc)->get_ref<const std::string&>())->second, *m_data);
break; break;
case Bytecode::Op::Callback: { case Bytecode::Op::Callback: {
auto callback = m_callbacks.find_callback(bc.str, bc.args); auto callback = m_callbacks.find_callback(bc.str, bc.args);
@@ -3216,12 +3334,17 @@ class Renderer {
// #include "template.hpp" // #include "template.hpp"
// #include "utils.hpp"
namespace inja { namespace inja {
using namespace nlohmann; using namespace nlohmann;
/*!
* \brief Class for changing the configuration.
*/
class Environment { class Environment {
class Impl { class Impl {
public: public:
@@ -3238,7 +3361,7 @@ class Environment {
std::unique_ptr<Impl> m_impl; std::unique_ptr<Impl> m_impl;
public: public:
Environment(): Environment("./") { } Environment(): Environment("") { }
explicit Environment(const std::string& global_path): m_impl(stdinja::make_unique<Impl>()) { explicit Environment(const std::string& global_path): m_impl(stdinja::make_unique<Impl>()) {
m_impl->input_path = global_path; m_impl->input_path = global_path;
@@ -3277,6 +3400,16 @@ class Environment {
m_impl->lexer_config.update_open_chars(); m_impl->lexer_config.update_open_chars();
} }
/// Sets whether to remove the first newline after a block
void set_trim_blocks(bool trim_blocks) {
m_impl->lexer_config.trim_blocks = trim_blocks;
}
/// Sets whether to strip the spaces and tabs from the start of a line to a block
void set_lstrip_blocks(bool lstrip_blocks) {
m_impl->lexer_config.lstrip_blocks = lstrip_blocks;
}
/// Sets the element notation syntax /// Sets the element notation syntax
void set_element_notation(ElementNotation notation) { void set_element_notation(ElementNotation notation) {
m_impl->parser_config.notation = notation; m_impl->parser_config.notation = notation;
@@ -3290,8 +3423,8 @@ class Environment {
Template parse_template(const std::string& filename) { Template parse_template(const std::string& filename) {
Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates); Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
return parser.parse_template(m_impl->input_path + static_cast<std::string>(filename)); return parser.parse_template(m_impl->input_path + static_cast<std::string>(filename));
} }
std::string render(nonstd::string_view input, const json& data) { std::string render(nonstd::string_view input, const json& data) {
return render(parse(input), data); return render(parse(input), data);
@@ -3304,35 +3437,35 @@ class Environment {
} }
std::string render_file(const std::string& filename, const json& data) { std::string render_file(const std::string& filename, const json& data) {
return render(parse_template(filename), data); return render(parse_template(filename), data);
} }
std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) { std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) {
const json data = load_json(filename_data); const json data = load_json(filename_data);
return render_file(filename, data); return render_file(filename, data);
} }
void write(const std::string& filename, const json& data, const std::string& filename_out) { void write(const std::string& filename, const json& data, const std::string& filename_out) {
std::ofstream file(m_impl->output_path + filename_out); std::ofstream file(m_impl->output_path + filename_out);
file << render_file(filename, data); file << render_file(filename, data);
file.close(); file.close();
} }
void write(const Template& temp, const json& data, const std::string& filename_out) { void write(const Template& temp, const json& data, const std::string& filename_out) {
std::ofstream file(m_impl->output_path + filename_out); std::ofstream file(m_impl->output_path + filename_out);
file << render(temp, data); file << render(temp, data);
file.close(); file.close();
} }
void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) { void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
const json data = load_json(filename_data); const json data = load_json(filename_data);
write(filename, data, filename_out); write(filename, data, filename_out);
} }
void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) { void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
const json data = load_json(filename_data); const json data = load_json(filename_data);
write(temp, data, filename_out); write(temp, data, filename_out);
} }
std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) { std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
Renderer(m_impl->included_templates, m_impl->callbacks).render_to(os, tmpl, data); Renderer(m_impl->included_templates, m_impl->callbacks).render_to(os, tmpl, data);
@@ -3341,15 +3474,15 @@ class Environment {
std::string load_file(const std::string& filename) { std::string load_file(const std::string& filename) {
Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates); Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
return parser.load_file(m_impl->input_path + filename); return parser.load_file(m_impl->input_path + filename);
} }
json load_json(const std::string& filename) { json load_json(const std::string& filename) {
std::ifstream file(m_impl->input_path + filename); std::ifstream file = open_file_or_throw(m_impl->input_path + filename);
json j; json j;
file >> j; file >> j;
return j; return j;
} }
void add_callback(const std::string& name, unsigned int numArgs, const CallbackFunction& callback) { void add_callback(const std::string& name, unsigned int numArgs, const CallbackFunction& callback) {
m_impl->callbacks.add_callback(name, numArgs, callback); m_impl->callbacks.add_callback(name, numArgs, callback);
+21 -3
View File
@@ -5,7 +5,7 @@
#include <map> #include <map>
#include <string> #include <string>
using std::string; using std::string; using std::to_string;
#include <inja.hpp> #include <inja.hpp>
using namespace inja; using namespace inja;
@@ -36,7 +36,14 @@ int main(int argc, char *argv[])
// Add custom command callbacks. // Add custom command callbacks.
env.add_callback("doNotModifyHeader", 0, [jsonfilepath, templateFilepath](Arguments& args) { env.add_callback("doNotModifyHeader", 0, [jsonfilepath, templateFilepath](Arguments& args) {
return "//\n// DO NOT MODIFY THIS FILE! IT IS AUTO-GENERATED FROM " + jsonfilepath +" and Inja template " + templateFilepath + "\n//\n"; return "//\n// DO NOT MODIFY THIS FILE! It is auto-generated from " + jsonfilepath +" and Inja template " + templateFilepath + "\n//\n";
});
env.add_callback("subtract", 2, [](Arguments& args) {
int minuend = args.at(0)->get<int>();
int subtrahend = args.at(1)->get<int>();
return minuend - subtrahend;
}); });
env.add_callback("setVar", 2, [=](Arguments& args) { env.add_callback("setVar", 2, [=](Arguments& args) {
@@ -46,6 +53,13 @@ int main(int argc, char *argv[])
return ""; return "";
}); });
env.add_callback("setVarInt", 2, [=](Arguments& args) {
string key = args.at(0)->get<string>();
string value = to_string(args.at(1)->get<int>());
set_custom_var(key, value);
return "";
});
env.add_callback("getVar", 1, [=](Arguments& args) { env.add_callback("getVar", 1, [=](Arguments& args) {
string key = args.at(0)->get<string>(); string key = args.at(0)->get<string>();
return get_custom_var(key); return get_custom_var(key);
@@ -67,7 +81,6 @@ int main(int argc, char *argv[])
return rawValue.erase(0, prefix.length()); return rawValue.erase(0, prefix.length());
}); });
// Add custom command callbacks.
env.add_callback("removeSuffix", 2, [](Arguments& args) { env.add_callback("removeSuffix", 2, [](Arguments& args) {
string rawValue = args.at(0)->get<string>(); string rawValue = args.at(0)->get<string>();
string suffix = args.at(1)->get<string>(); string suffix = args.at(1)->get<string>();
@@ -78,6 +91,11 @@ int main(int argc, char *argv[])
return rawValue.substr(0, i); return rawValue.substr(0, i);
}); });
// single argument is a json object
env.add_callback("isEmpty", 1, [](Arguments& args) {
return args.at(0)->empty();
});
try try
{ {
env.write_with_json_file(templateFilepath, jsonfilepath, outputFilepath); env.write_with_json_file(templateFilepath, jsonfilepath, outputFilepath);