summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgarak <garakmon@gmail.com>2019-09-30 19:18:06 -0400
committerhuderlem <huderlem@gmail.com>2019-10-16 20:09:49 -0500
commitfc2926aa469de176403d0ccaba3ad503c65ff98e (patch)
treefdd891289107eaeef5a03af1ae18ac8db831ba24
parent943005316b673252329aae214280790b15caa910 (diff)
add fishing rod groups to encounter json
-rw-r--r--include/constants/pokemon.h144
-rw-r--r--include/pokemon.h140
-rw-r--r--src/data/wild_encounters.json16
-rw-r--r--src/data/wild_encounters.json.txt14
-rw-r--r--src/wild_encounter.c21
-rwxr-xr-xtools/jsonproc/inja.hpp275
-rwxr-xr-xtools/jsonproc/jsonproc.cpp20
7 files changed, 390 insertions, 240 deletions
diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h
new file mode 100644
index 000000000..8e7c01eb5
--- /dev/null
+++ b/include/constants/pokemon.h
@@ -0,0 +1,144 @@
+#ifndef GUARD_CONSTANTS_POKEMON_H
+#define GUARD_CONSTANTS_POKEMON_H
+
+#define MON_DATA_PERSONALITY 0
+#define MON_DATA_OT_ID 1
+#define MON_DATA_NICKNAME 2
+#define MON_DATA_LANGUAGE 3
+#define MON_DATA_SANITY_BIT1 4
+#define MON_DATA_SANITY_BIT2 5
+#define MON_DATA_SANITY_BIT3 6
+#define MON_DATA_OT_NAME 7
+#define MON_DATA_MARKINGS 8
+#define MON_DATA_CHECKSUM 9
+#define MON_DATA_10 10
+#define MON_DATA_SPECIES 11
+#define MON_DATA_HELD_ITEM 12
+#define MON_DATA_MOVE1 13
+#define MON_DATA_MOVE2 14
+#define MON_DATA_MOVE3 15
+#define MON_DATA_MOVE4 16
+#define MON_DATA_PP1 17
+#define MON_DATA_PP2 18
+#define MON_DATA_PP3 19
+#define MON_DATA_PP4 20
+#define MON_DATA_PP_BONUSES 21
+#define MON_DATA_COOL 22
+#define MON_DATA_BEAUTY 23
+#define MON_DATA_CUTE 24
+#define MON_DATA_EXP 25
+#define MON_DATA_HP_EV 26
+#define MON_DATA_ATK_EV 27
+#define MON_DATA_DEF_EV 28
+#define MON_DATA_SPEED_EV 29
+#define MON_DATA_SPATK_EV 30
+#define MON_DATA_SPDEF_EV 31
+#define MON_DATA_FRIENDSHIP 32
+#define MON_DATA_SMART 33
+#define MON_DATA_POKERUS 34
+#define MON_DATA_MET_LOCATION 35
+#define MON_DATA_MET_LEVEL 36
+#define MON_DATA_MET_GAME 37
+#define MON_DATA_POKEBALL 38
+#define MON_DATA_HP_IV 39
+#define MON_DATA_ATK_IV 40
+#define MON_DATA_DEF_IV 41
+#define MON_DATA_SPEED_IV 42
+#define MON_DATA_SPATK_IV 43
+#define MON_DATA_SPDEF_IV 44
+#define MON_DATA_IS_EGG 45
+#define MON_DATA_ALT_ABILITY 46
+#define MON_DATA_TOUGH 47
+#define MON_DATA_SHEEN 48
+#define MON_DATA_OT_GENDER 49
+#define MON_DATA_COOL_RIBBON 50
+#define MON_DATA_BEAUTY_RIBBON 51
+#define MON_DATA_CUTE_RIBBON 52
+#define MON_DATA_SMART_RIBBON 53
+#define MON_DATA_TOUGH_RIBBON 54
+#define MON_DATA_STATUS 55
+#define MON_DATA_LEVEL 56
+#define MON_DATA_HP 57
+#define MON_DATA_MAX_HP 58
+#define MON_DATA_ATK 59
+#define MON_DATA_DEF 60
+#define MON_DATA_SPEED 61
+#define MON_DATA_SPATK 62
+#define MON_DATA_SPDEF 63
+#define MON_DATA_MAIL 64
+#define MON_DATA_SPECIES2 65
+#define MON_DATA_IVS 66
+#define MON_DATA_CHAMPION_RIBBON 67
+#define MON_DATA_WINNING_RIBBON 68
+#define MON_DATA_VICTORY_RIBBON 69
+#define MON_DATA_ARTIST_RIBBON 70
+#define MON_DATA_EFFORT_RIBBON 71
+#define MON_DATA_GIFT_RIBBON_1 72
+#define MON_DATA_GIFT_RIBBON_2 73
+#define MON_DATA_GIFT_RIBBON_3 74
+#define MON_DATA_GIFT_RIBBON_4 75
+#define MON_DATA_GIFT_RIBBON_5 76
+#define MON_DATA_GIFT_RIBBON_6 77
+#define MON_DATA_GIFT_RIBBON_7 78
+#define MON_DATA_FATEFUL_ENCOUNTER 79
+#define MON_DATA_KNOWN_MOVES 80
+#define MON_DATA_RIBBON_COUNT 81
+#define MON_DATA_RIBBONS 82
+#define MON_DATA_83 83
+#define MON_DATA_ATK2 84
+#define MON_DATA_DEF2 85
+#define MON_DATA_SPEED2 86
+#define MON_DATA_SPATK2 87
+#define MON_DATA_SPDEF2 88
+
+#define MIN_LEVEL 1
+#define MAX_LEVEL 100
+
+#define MON_MALE 0x00
+#define MON_FEMALE 0xFE
+#define MON_GENDERLESS 0xFF
+
+#define TYPE_NORMAL 0x00
+#define TYPE_FIGHTING 0x01
+#define TYPE_FLYING 0x02
+#define TYPE_POISON 0x03
+#define TYPE_GROUND 0x04
+#define TYPE_ROCK 0x05
+#define TYPE_BUG 0x06
+#define TYPE_GHOST 0x07
+#define TYPE_STEEL 0x08
+#define TYPE_MYSTERY 0x09
+#define TYPE_FIRE 0x0a
+#define TYPE_WATER 0x0b
+#define TYPE_GRASS 0x0c
+#define TYPE_ELECTRIC 0x0d
+#define TYPE_PSYCHIC 0x0e
+#define TYPE_ICE 0x0f
+#define TYPE_DRAGON 0x10
+#define TYPE_DARK 0x11
+#define NUMBER_OF_MON_TYPES 0x12
+
+#define FRIENDSHIP_EVENT_GROW_LEVEL 0x0
+#define FRIENDSHIP_EVENT_VITAMIN 0x1 // unused
+#define FRIENDSHIP_EVENT_BATTLE_ITEM 0x2 // unused
+#define FRIENDSHIP_EVENT_LEAGUE_BATTLE 0x3
+#define FRIENDSHIP_EVENT_LEARN_TMHM 0x4
+#define FRIENDSHIP_EVENT_WALKING 0x5
+#define FRIENDSHIP_EVENT_FAINT_SMALL 0x6
+#define FRIENDSHIP_EVENT_FAINT_OUTSIDE_BATTLE 0x7
+#define FRIENDSHIP_EVENT_FAINT_LARGE 0x8
+
+#define STATUS_PRIMARY_NONE 0x0
+#define STATUS_PRIMARY_POISON 0x1
+#define STATUS_PRIMARY_PARALYSIS 0x2
+#define STATUS_PRIMARY_SLEEP 0x3
+#define STATUS_PRIMARY_FREEZE 0x4
+#define STATUS_PRIMARY_BURN 0x5
+#define STATUS_PRIMARY_POKERUS 0x6
+#define STATUS_PRIMARY_FAINTED 0x7
+
+#define MAX_TOTAL_EVS 510
+#define NUM_STATS 6
+#define UNOWN_FORM_COUNT 28
+
+#endif // GUARD_CONSTANTS_POKEMON_H
diff --git a/include/pokemon.h b/include/pokemon.h
index 67816b8f0..df2d42522 100644
--- a/include/pokemon.h
+++ b/include/pokemon.h
@@ -1,147 +1,9 @@
#ifndef GUARD_POKEMON_H
#define GUARD_POKEMON_H
+#include "constants/pokemon.h"
#include "sprite.h"
-#define MON_DATA_PERSONALITY 0
-#define MON_DATA_OT_ID 1
-#define MON_DATA_NICKNAME 2
-#define MON_DATA_LANGUAGE 3
-#define MON_DATA_SANITY_BIT1 4
-#define MON_DATA_SANITY_BIT2 5
-#define MON_DATA_SANITY_BIT3 6
-#define MON_DATA_OT_NAME 7
-#define MON_DATA_MARKINGS 8
-#define MON_DATA_CHECKSUM 9
-#define MON_DATA_10 10
-#define MON_DATA_SPECIES 11
-#define MON_DATA_HELD_ITEM 12
-#define MON_DATA_MOVE1 13
-#define MON_DATA_MOVE2 14
-#define MON_DATA_MOVE3 15
-#define MON_DATA_MOVE4 16
-#define MON_DATA_PP1 17
-#define MON_DATA_PP2 18
-#define MON_DATA_PP3 19
-#define MON_DATA_PP4 20
-#define MON_DATA_PP_BONUSES 21
-#define MON_DATA_COOL 22
-#define MON_DATA_BEAUTY 23
-#define MON_DATA_CUTE 24
-#define MON_DATA_EXP 25
-#define MON_DATA_HP_EV 26
-#define MON_DATA_ATK_EV 27
-#define MON_DATA_DEF_EV 28
-#define MON_DATA_SPEED_EV 29
-#define MON_DATA_SPATK_EV 30
-#define MON_DATA_SPDEF_EV 31
-#define MON_DATA_FRIENDSHIP 32
-#define MON_DATA_SMART 33
-#define MON_DATA_POKERUS 34
-#define MON_DATA_MET_LOCATION 35
-#define MON_DATA_MET_LEVEL 36
-#define MON_DATA_MET_GAME 37
-#define MON_DATA_POKEBALL 38
-#define MON_DATA_HP_IV 39
-#define MON_DATA_ATK_IV 40
-#define MON_DATA_DEF_IV 41
-#define MON_DATA_SPEED_IV 42
-#define MON_DATA_SPATK_IV 43
-#define MON_DATA_SPDEF_IV 44
-#define MON_DATA_IS_EGG 45
-#define MON_DATA_ALT_ABILITY 46
-#define MON_DATA_TOUGH 47
-#define MON_DATA_SHEEN 48
-#define MON_DATA_OT_GENDER 49
-#define MON_DATA_COOL_RIBBON 50
-#define MON_DATA_BEAUTY_RIBBON 51
-#define MON_DATA_CUTE_RIBBON 52
-#define MON_DATA_SMART_RIBBON 53
-#define MON_DATA_TOUGH_RIBBON 54
-#define MON_DATA_STATUS 55
-#define MON_DATA_LEVEL 56
-#define MON_DATA_HP 57
-#define MON_DATA_MAX_HP 58
-#define MON_DATA_ATK 59
-#define MON_DATA_DEF 60
-#define MON_DATA_SPEED 61
-#define MON_DATA_SPATK 62
-#define MON_DATA_SPDEF 63
-#define MON_DATA_MAIL 64
-#define MON_DATA_SPECIES2 65
-#define MON_DATA_IVS 66
-#define MON_DATA_CHAMPION_RIBBON 67
-#define MON_DATA_WINNING_RIBBON 68
-#define MON_DATA_VICTORY_RIBBON 69
-#define MON_DATA_ARTIST_RIBBON 70
-#define MON_DATA_EFFORT_RIBBON 71
-#define MON_DATA_GIFT_RIBBON_1 72
-#define MON_DATA_GIFT_RIBBON_2 73
-#define MON_DATA_GIFT_RIBBON_3 74
-#define MON_DATA_GIFT_RIBBON_4 75
-#define MON_DATA_GIFT_RIBBON_5 76
-#define MON_DATA_GIFT_RIBBON_6 77
-#define MON_DATA_GIFT_RIBBON_7 78
-#define MON_DATA_FATEFUL_ENCOUNTER 79
-#define MON_DATA_KNOWN_MOVES 80
-#define MON_DATA_RIBBON_COUNT 81
-#define MON_DATA_RIBBONS 82
-#define MON_DATA_83 83
-#define MON_DATA_ATK2 84
-#define MON_DATA_DEF2 85
-#define MON_DATA_SPEED2 86
-#define MON_DATA_SPATK2 87
-#define MON_DATA_SPDEF2 88
-
-#define MAX_LEVEL 100
-
-#define MON_MALE 0x00
-#define MON_FEMALE 0xFE
-#define MON_GENDERLESS 0xFF
-
-#define TYPE_NORMAL 0x00
-#define TYPE_FIGHTING 0x01
-#define TYPE_FLYING 0x02
-#define TYPE_POISON 0x03
-#define TYPE_GROUND 0x04
-#define TYPE_ROCK 0x05
-#define TYPE_BUG 0x06
-#define TYPE_GHOST 0x07
-#define TYPE_STEEL 0x08
-#define TYPE_MYSTERY 0x09
-#define TYPE_FIRE 0x0a
-#define TYPE_WATER 0x0b
-#define TYPE_GRASS 0x0c
-#define TYPE_ELECTRIC 0x0d
-#define TYPE_PSYCHIC 0x0e
-#define TYPE_ICE 0x0f
-#define TYPE_DRAGON 0x10
-#define TYPE_DARK 0x11
-#define NUMBER_OF_MON_TYPES 0x12
-
-#define FRIENDSHIP_EVENT_GROW_LEVEL 0x0
-#define FRIENDSHIP_EVENT_VITAMIN 0x1 // unused
-#define FRIENDSHIP_EVENT_BATTLE_ITEM 0x2 // unused
-#define FRIENDSHIP_EVENT_LEAGUE_BATTLE 0x3
-#define FRIENDSHIP_EVENT_LEARN_TMHM 0x4
-#define FRIENDSHIP_EVENT_WALKING 0x5
-#define FRIENDSHIP_EVENT_FAINT_SMALL 0x6
-#define FRIENDSHIP_EVENT_FAINT_OUTSIDE_BATTLE 0x7
-#define FRIENDSHIP_EVENT_FAINT_LARGE 0x8
-
-#define STATUS_PRIMARY_NONE 0x0
-#define STATUS_PRIMARY_POISON 0x1
-#define STATUS_PRIMARY_PARALYSIS 0x2
-#define STATUS_PRIMARY_SLEEP 0x3
-#define STATUS_PRIMARY_FREEZE 0x4
-#define STATUS_PRIMARY_BURN 0x5
-#define STATUS_PRIMARY_POKERUS 0x6
-#define STATUS_PRIMARY_FAINTED 0x7
-
-#define MAX_TOTAL_EVS 510
-#define NUM_STATS 6
-#define UNOWN_FORM_COUNT 28
-
enum {
EGG_GROUP_NONE,
EGG_GROUP_MONSTER,
diff --git a/src/data/wild_encounters.json b/src/data/wild_encounters.json
index 9adbc28de..37cffbca9 100644
--- a/src/data/wild_encounters.json
+++ b/src/data/wild_encounters.json
@@ -8,25 +8,33 @@
"type": "land_mons",
"encounter_rates": [
20, 20, 10, 10, 10, 10, 5, 5, 4, 4, 1, 1
- ]
+ ],
+ "groups": {}
},
{
"type": "water_mons",
"encounter_rates": [
60, 30, 5, 4, 1
- ]
+ ],
+ "groups": {}
},
{
"type": "rock_smash_mons",
"encounter_rates": [
60, 30, 5, 4, 1
- ]
+ ],
+ "groups": {}
},
{
"type": "fishing_mons",
"encounter_rates": [
70, 30, 60, 20, 20, 40, 40, 15, 4, 1
- ]
+ ],
+ "groups": {
+ "old_rod": [0, 1],
+ "good_rod": [2, 3, 4],
+ "super_rod": [5, 6, 7, 8, 9]
+ }
}
],
"encounters": [
diff --git a/src/data/wild_encounters.json.txt b/src/data/wild_encounters.json.txt
index de8396dd6..5e0286b61 100644
--- a/src/data/wild_encounters.json.txt
+++ b/src/data/wild_encounters.json.txt
@@ -3,14 +3,26 @@
## for wild_encounter_group in wild_encounter_groups
{% if wild_encounter_group.for_maps %}
## for wild_encounter_field in wild_encounter_group.fields
+{% if isEmpty(wild_encounter_field.groups) %}
## for encounter_rate in wild_encounter_field.encounter_rates
-{% if trackVar(encounter_rate, 100) %}
+{% 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 contains(encounter.base_label, "Sapphire") %}#ifdef SAPPHIRE
{% else if contains(encounter.base_label, "Ruby") %}#ifdef RUBY{% endif %}
diff --git a/src/wild_encounter.c b/src/wild_encounter.c
index 0680570fc..47eb81541 100644
--- a/src/wild_encounter.c
+++ b/src/wild_encounter.c
@@ -203,34 +203,35 @@ enum
static u8 ChooseWildMonIndex_Fishing(u8 rod)
{
u8 wildMonIndex = 0;
- u8 rand = Random() % 100;
+ u8 rand = Random() % max(max(ENCOUNTER_CHANCE_FISHING_MONS_OLD_ROD_TOTAL, ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_TOTAL),
+ ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_TOTAL);;
switch (rod)
{
case OLD_ROD:
- if (rand < ENCOUNTER_CHANCE_FISHING_MONS_SLOT_0)
+ if (rand < ENCOUNTER_CHANCE_FISHING_MONS_OLD_ROD_SLOT_0)
wildMonIndex = 0;
else
wildMonIndex = 1;
break;
case GOOD_ROD:
- if (rand < ENCOUNTER_CHANCE_FISHING_MONS_SLOT_2)
+ if (rand < ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_2)
wildMonIndex = 2;
- if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SLOT_2 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SLOT_3)
+ if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_2 && rand < ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_3)
wildMonIndex = 3;
- if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SLOT_3 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SLOT_4)
+ if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_3 && rand < ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_4)
wildMonIndex = 4;
break;
case SUPER_ROD:
- if (rand < ENCOUNTER_CHANCE_FISHING_MONS_SLOT_5)
+ if (rand < ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_5)
wildMonIndex = 5;
- if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SLOT_5 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SLOT_6)
+ if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_5 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_6)
wildMonIndex = 6;
- if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SLOT_6 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SLOT_7)
+ if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_6 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_7)
wildMonIndex = 7;
- if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SLOT_7 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SLOT_8)
+ if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_7 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_8)
wildMonIndex = 8;
- if (rand == ENCOUNTER_CHANCE_FISHING_MONS_SLOT_8)
+ if (rand == ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_8)
wildMonIndex = 9;
break;
}
diff --git a/tools/jsonproc/inja.hpp b/tools/jsonproc/inja.hpp
index 3b7263546..d5bf5bcba 100755
--- a/tools/jsonproc/inja.hpp
+++ b/tools/jsonproc/inja.hpp
@@ -517,7 +517,7 @@ public:
typedef const_pointer iterator;
typedef const_pointer const_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::ptrdiff_t difference_type;
@@ -1411,6 +1411,9 @@ enum class ElementNotation {
Pointer
};
+/*!
+ * \brief Class for lexer configuration.
+ */
struct LexerConfig {
std::string statement_open {"{%"};
std::string statement_close {"%}"};
@@ -1421,6 +1424,9 @@ struct LexerConfig {
std::string comment_close {"#}"};
std::string open_chars {"#{"};
+ bool trim_blocks {false};
+ bool lstrip_blocks {false};
+
void update_open_chars() {
open_chars = "";
if (open_chars.find(line_statement[0]) == std::string::npos) {
@@ -1438,6 +1444,9 @@ struct LexerConfig {
}
};
+/*!
+ * \brief Class for parser configuration.
+ */
struct ParserConfig {
ElementNotation notation {ElementNotation::Dot};
};
@@ -1450,10 +1459,13 @@ struct ParserConfig {
#ifndef PANTOR_INJA_FUNCTION_STORAGE_HPP
#define PANTOR_INJA_FUNCTION_STORAGE_HPP
+#include <vector>
+
// #include "bytecode.hpp"
#ifndef PANTOR_INJA_BYTECODE_HPP
#define PANTOR_INJA_BYTECODE_HPP
+#include <string>
#include <utility>
#include <nlohmann/json.hpp>
@@ -1464,7 +1476,7 @@ struct ParserConfig {
namespace inja {
-using namespace nlohmann;
+using json = nlohmann::json;
struct Bytecode {
@@ -1492,6 +1504,7 @@ struct Bytecode {
GreaterEqual,
Less,
LessEqual,
+ At,
Different,
DivisibleBy,
Even,
@@ -1594,6 +1607,9 @@ using namespace nlohmann;
using Arguments = std::vector<const json*>;
using CallbackFunction = std::function<json(Arguments& args)>;
+/*!
+ * \brief Class for builtin functions and user-defined callbacks.
+ */
class FunctionStorage {
public:
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
#include <limits>
+#include <string>
+#include <utility>
+#include <vector>
// #include "bytecode.hpp"
@@ -1678,12 +1697,17 @@ class FunctionStorage {
#ifndef PANTOR_INJA_TOKEN_HPP
#define PANTOR_INJA_TOKEN_HPP
+#include <string>
+
// #include "string_view.hpp"
namespace inja {
+/*!
+ * \brief Helper-class for the inja Parser.
+ */
struct Token {
enum class Kind {
Text,
@@ -1737,13 +1761,17 @@ struct Token {
}
-#endif // PANTOR_INJA_TOKEN_HPP
+#endif // PANTOR_INJA_TOKEN_HPP
// #include "utils.hpp"
#ifndef PANTOR_INJA_UTILS_HPP
#define PANTOR_INJA_UTILS_HPP
+#include <algorithm>
+#include <fstream>
#include <stdexcept>
+#include <string>
+#include <utility>
// #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);
}
+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 {
inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
start = std::min(start, 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) {
@@ -1783,6 +1822,9 @@ namespace string_view {
namespace inja {
+/*!
+ * \brief Class for lexing an inja Template.
+ */
class Lexer {
enum class State {
Text,
@@ -1831,12 +1873,15 @@ class Lexer {
// try to match one of the opening sequences, and get the close
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)) {
m_state = State::ExpressionStart;
} else if (inja::string_view::starts_with(open_str, m_config.statement_open)) {
m_state = State::StatementStart;
+ must_lstrip = m_config.lstrip_blocks;
} else if (inja::string_view::starts_with(open_str, m_config.comment_open)) {
m_state = State::CommentStart;
+ must_lstrip = m_config.lstrip_blocks;
} else if ((m_pos == 0 || m_in[m_pos - 1] == '\n') &&
inja::string_view::starts_with(open_str, m_config.line_statement)) {
m_state = State::LineStart;
@@ -1844,8 +1889,13 @@ class Lexer {
m_pos += 1; // wasn't actually an opening sequence
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: {
m_state = State::ExpressionBody;
@@ -1872,7 +1922,7 @@ class Lexer {
case State::LineBody:
return scan_body("\n", Token::Kind::LineStatementClose);
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: {
// fast-scan to 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
m_state = State::Text;
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; }
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:
// skip whitespace (except for \n as it might be a close)
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)) {
m_state = State::Text;
m_pos = m_tok_start + close.size();
- return make_token(closeKind);
+ Token tok = make_token(closeKind);
+ if (trim)
+ skip_newline();
+ return tok;
}
// skip \n
@@ -2026,6 +2082,34 @@ class Lexer {
Token make_token(Token::Kind kind) const {
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
#define PANTOR_INJA_TEMPLATE_HPP
+#include <map>
#include <string>
#include <vector>
@@ -2045,6 +2130,9 @@ class Lexer {
namespace inja {
+/*!
+ * \brief The main inja Template.
+ */
struct Template {
std::vector<Bytecode> bytecodes;
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"
@@ -2068,6 +2156,7 @@ namespace inja {
class ParserStatic {
ParserStatic() {
+ functions.add_builtin("at", 2, Bytecode::Op::At);
functions.add_builtin("default", 2, Bytecode::Op::Default);
functions.add_builtin("divisibleBy", 2, Bytecode::Op::DivisibleBy);
functions.add_builtin("even", 1, Bytecode::Op::Even);
@@ -2107,13 +2196,16 @@ class ParserStatic {
FunctionStorage functions;
};
+/*!
+ * \brief Class for parsing an inja Template.
+ */
class Parser {
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()) { }
bool parse_expression(Template& tmpl) {
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();
if (!parse_expression_and(tmpl)) return false;
append_function(tmpl, Bytecode::Op::Or, 2);
@@ -2122,7 +2214,7 @@ class Parser {
bool parse_expression_and(Template& tmpl) {
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();
if (!parse_expression_not(tmpl)) return false;
append_function(tmpl, Bytecode::Op::And, 2);
@@ -2130,7 +2222,7 @@ class Parser {
}
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();
if (!parse_expression_not(tmpl)) return false;
append_function(tmpl, Bytecode::Op::Not, 1);
@@ -2145,7 +2237,7 @@ class Parser {
Bytecode::Op op;
switch (m_tok.kind) {
case Token::Kind::Id:
- if (m_tok.text == "in")
+ if (m_tok.text == static_cast<decltype(m_tok.text)>("in"))
op = Bytecode::Op::In;
else
return true;
@@ -2233,7 +2325,9 @@ class Parser {
append_callback(tmpl, func_token.text, num_args);
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
if (brace_level == 0 && bracket_level == 0) {
json_first = m_tok.text;
@@ -2312,7 +2406,7 @@ class Parser {
bool parse_statement(Template& tmpl, nonstd::string_view path) {
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();
// evaluate expression
@@ -2323,7 +2417,7 @@ class Parser {
// conditional jump; destination will be filled in by else or endif
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()) {
inja_throw("parser_error", "endif without matching if");
}
@@ -2342,7 +2436,7 @@ class Parser {
// pop if stack
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())
inja_throw("parser_error", "else without matching if");
auto& if_data = m_if_stack.back();
@@ -2358,7 +2452,7 @@ class Parser {
if_data.prev_cond_jump = std::numeric_limits<unsigned int>::max();
// 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();
// evaluate expression
@@ -2370,7 +2464,7 @@ class Parser {
// conditional jump; destination will be filled in by else or endif
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();
// options: for a in arr; for a, b in obj
@@ -2389,7 +2483,7 @@ class Parser {
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",
"expected 'in', got '" + m_tok.describe() + "'");
get_next_token();
@@ -2403,7 +2497,7 @@ class Parser {
tmpl.bytecodes.back().value = key_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();
if (m_loop_stack.empty()) {
inja_throw("parser_error", "endfor without matching for");
@@ -2415,7 +2509,7 @@ class Parser {
tmpl.bytecodes.emplace_back(Bytecode::Op::EndLoop);
tmpl.bytecodes.back().args = m_loop_stack.back() + 1; // loop body
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();
if (m_tok.kind != Token::Kind::String) {
@@ -2431,8 +2525,10 @@ class Parser {
}
// sys::path::remove_dots(pathname, true, sys::path::Style::posix);
- Template include_template = parse_template(pathname);
- m_included_templates.emplace(pathname, include_template);
+ if (m_included_templates.find(pathname) == m_included_templates.end()) {
+ Template include_template = parse_template(pathname);
+ m_included_templates.emplace(pathname, include_template);
+ }
// generate a reference bytecode
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::ifstream file(static_cast<std::string>(filename));
- std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
- return text;
- }
+ std::ifstream file = open_file_or_throw(static_cast<std::string>(filename));
+ std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+ return text;
+ }
private:
const ParserConfig& m_config;
@@ -2605,6 +2701,7 @@ class Parser {
#if __cplusplus < 201402L
#include <cstddef>
+#include <memory>
#include <type_traits>
#include <utility>
@@ -2655,6 +2752,9 @@ namespace stdinja = std;
#include <algorithm>
#include <numeric>
+#include <string>
+#include <utility>
+#include <vector>
#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());
}
+/*!
+ * \brief Class for rendering a Template with data.
+ */
class Renderer {
std::vector<const json*>& get_args(const Bytecode& bc) {
m_tmp_args.clear();
@@ -2765,7 +2868,7 @@ class Renderer {
LoopLevel& level = m_loop_stack.back();
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"];
loopData["index"] = level.index;
loopData["index1"] = level.index + 1;
@@ -2787,8 +2890,8 @@ class Renderer {
enum class Type { Map, Array };
Type loop_type;
- nonstd::string_view key_name; // variable name for keys
- nonstd::string_view value_name; // variable name for values
+ nonstd::string_view key_name; // variable name for keys
+ nonstd::string_view value_name; // variable name for values
json data; // data with loop info added
json values; // values to iterate over
@@ -2800,8 +2903,8 @@ class Renderer {
// loop over map
using KeyValue = std::pair<nonstd::string_view, json*>;
using MapValues = std::vector<KeyValue>;
- MapValues map_values; // values to iterate over
- MapValues::iterator map_it; // iterator over values
+ MapValues map_values; // values to iterate over
+ MapValues::iterator map_it; // iterator over values
};
@@ -2835,11 +2938,11 @@ class Renderer {
}
case Bytecode::Op::PrintValue: {
const json& val = *get_args(bc)[0];
- if (val.is_string())
+ if (val.is_string()) {
os << val.get_ref<const std::string&>();
- else
+ } else {
os << val.dump();
- // val.dump(os);
+ }
pop_args(bc);
break;
}
@@ -2870,7 +2973,15 @@ class Renderer {
break;
}
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);
m_stack.emplace_back(result);
break;
@@ -2882,6 +2993,13 @@ class Renderer {
m_stack.emplace_back(std::move(result));
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: {
auto result = get_args(bc)[0]->front();
pop_args(bc);
@@ -3091,7 +3209,7 @@ class Renderer {
break;
}
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;
case Bytecode::Op::Callback: {
auto callback = m_callbacks.find_callback(bc.str, bc.args);
@@ -3216,12 +3334,17 @@ class Renderer {
// #include "template.hpp"
+// #include "utils.hpp"
+
namespace inja {
using namespace nlohmann;
+/*!
+ * \brief Class for changing the configuration.
+ */
class Environment {
class Impl {
public:
@@ -3238,7 +3361,7 @@ class Environment {
std::unique_ptr<Impl> m_impl;
public:
- Environment(): Environment("./") { }
+ Environment(): Environment("") { }
explicit Environment(const std::string& global_path): m_impl(stdinja::make_unique<Impl>()) {
m_impl->input_path = global_path;
@@ -3277,6 +3400,16 @@ class Environment {
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
void set_element_notation(ElementNotation notation) {
m_impl->parser_config.notation = notation;
@@ -3290,8 +3423,8 @@ class Environment {
Template parse_template(const std::string& filename) {
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) {
return render(parse(input), data);
@@ -3304,35 +3437,35 @@ class Environment {
}
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) {
- const json data = load_json(filename_data);
- return render_file(filename, data);
- }
+ const json data = load_json(filename_data);
+ return render_file(filename, data);
+ }
void write(const std::string& filename, const json& data, const std::string& filename_out) {
- std::ofstream file(m_impl->output_path + filename_out);
- file << render_file(filename, data);
- file.close();
- }
+ std::ofstream file(m_impl->output_path + filename_out);
+ file << render_file(filename, data);
+ file.close();
+ }
void write(const Template& temp, const json& data, const std::string& filename_out) {
- std::ofstream file(m_impl->output_path + filename_out);
- file << render(temp, data);
- file.close();
- }
+ std::ofstream file(m_impl->output_path + filename_out);
+ file << render(temp, data);
+ file.close();
+ }
- 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);
- write(filename, data, 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);
+ write(filename, data, 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);
- write(temp, data, 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);
+ write(temp, data, filename_out);
+ }
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);
@@ -3341,15 +3474,15 @@ class Environment {
std::string load_file(const std::string& filename) {
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) {
- std::ifstream file(m_impl->input_path + filename);
- json j;
- file >> j;
- return j;
- }
+ std::ifstream file = open_file_or_throw(m_impl->input_path + filename);
+ json j;
+ file >> j;
+ return j;
+ }
void add_callback(const std::string& name, unsigned int numArgs, const CallbackFunction& callback) {
m_impl->callbacks.add_callback(name, numArgs, callback);
diff --git a/tools/jsonproc/jsonproc.cpp b/tools/jsonproc/jsonproc.cpp
index 428d8e6e9..f70c9131d 100755
--- a/tools/jsonproc/jsonproc.cpp
+++ b/tools/jsonproc/jsonproc.cpp
@@ -72,21 +72,6 @@ int main(int argc, char *argv[])
return get_custom_var(key);
});
- env.add_callback("trackVar", 2, [](Arguments& args) {
- static int counter = 0;
-
- int addValue = args.at(0)->get<int>();
- int checkValue = args.at(1)->get<int>();
-
- bool over = false;
-
- counter = (counter + addValue) % (checkValue + 1);
-
- if (counter <= addValue) over = true;
-
- return over;
- });
-
env.add_callback("concat", 2, [](Arguments& args) {
string first = args.at(0)->get<string>();
string second = args.at(1)->get<string>();
@@ -113,6 +98,11 @@ int main(int argc, char *argv[])
return rawValue.substr(0, i);
});
+ // single argument is a json object
+ env.add_callback("isEmpty", 1, [](Arguments& args) {
+ return args.at(0)->empty();
+ });
+
try
{
env.write_with_json_file(templateFilepath, jsonfilepath, outputFilepath);