aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-dataloader/v2script/EventGrammar.hpp
blob: 20168f8dcfbd91317cc7469cb3c6da832a1b020e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#pragma once

#include <cctype>
#include <cstdlib>

#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp>

#include <lexy/callback.hpp>
#include <lexy/dsl.hpp>
#include <lexy/grammar.hpp>

#include "openvic-dataloader/NodeLocation.hpp"

#include "SimpleGrammar.hpp"
#include "detail/InternalConcepts.hpp"
#include "detail/dsl.hpp"
#include "v2script/AiBehaviorGrammar.hpp"
#include "v2script/EffectGrammar.hpp"
#include "v2script/ModifierGrammar.hpp"

// Event Grammar Definitions //
namespace ovdl::v2script::grammar {
   static constexpr auto event_symbols = lexy::symbol_table<bool> //
                                   .map<LEXY_SYMBOL("country_event")>(false)
                                   .map<LEXY_SYMBOL("province_event")>(true);

   struct EventMtthStatement {
      struct MonthValue {
         static constexpr auto rule = lexy::dsl::p<Identifier<StringEscapeOption>>;
         static constexpr auto value = dsl::callback<ast::IdentifierValue*>(
            [](detail::IsParseState auto& state, ast::IdentifierValue* value) {
               bool is_number = true;
               for (auto* current = value->value().c_str(); *current; current++) {
                  is_number = is_number && std::isdigit(*current);
                  if (!is_number) break;
               }
               if (!is_number) {
                  state.logger().warning("month is not an integer") //
                     .primary(state.ast().location_of(value), "here")
                     .finish();
               }
               return value;
            });
      };

      using months = keyword_rule<"months", lexy::dsl::p<MonthValue>>;

      static constexpr auto rule = dsl::curly_bracketed(lexy::dsl::p<months> | lexy::dsl::p<ModifierStatement>);

      static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue, true>;
   };

   static constexpr auto str_or_id = lexy::dsl::p<SimpleGrammar<StringEscapeOption>::ValueExpression>;

   struct EventOptionList {
      using name = fkeyword_rule<"name", str_or_id>;
      using ai_chance = fkeyword_rule<"ai_chance", lexy::dsl::p<AiBehaviorBlock>>;

      static constexpr auto rule = [] {
         using helper = dsl::rule_helper<name, ai_chance>;

         return dsl::curly_bracketed(helper::flags + lexy::dsl::list(helper::p | lexy::dsl::p<EffectStatement>));
      }();

      static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue, true>;
   };

   struct EventStatement {
      using id = fkeyword_rule<"id", str_or_id>;
      using title = fkeyword_rule<"title", str_or_id>;
      using desc = fkeyword_rule<"desc", str_or_id>;
      using picture = fkeyword_rule<"picture", str_or_id>;
      using is_triggered_only = fkeyword_rule<"is_triggered_only", str_or_id>;
      using fire_only_once = fkeyword_rule<"fire_only_once", str_or_id>;
      using immediate = fkeyword_rule<"immediate", lexy::dsl::p<EffectBlock>>;
      using mean_time_to_happen = fkeyword_rule<"mean_time_to_happen", lexy::dsl::p<EventMtthStatement>>;
      using trigger = keyword_rule<"trigger", lexy::dsl::p<TriggerBlock>>;
      using option = keyword_rule<"option", lexy::dsl::p<EventOptionList>>;

      struct EventList {
         static constexpr auto rule = [] {
            using helper = dsl::rule_helper<id, title, desc, picture, is_triggered_only, fire_only_once, immediate, mean_time_to_happen>;

            return helper::flags +
                  dsl::curly_bracketed.opt_list(
                     helper::p | lexy::dsl::p<trigger> | lexy::dsl::p<option> |
                     lexy::dsl::p<SAssignStatement<StringEscapeOption>>);
         }();

         static constexpr auto value = lexy::as_list<ast::AssignStatementList> >> construct_list<ast::ListValue, true>;
      };

      static constexpr auto rule = dsl::p<Identifier<StringEscapeOption>> >> lexy::dsl::equal_sign >> lexy::dsl::p<EventList>;

      static constexpr auto value =
         dsl::callback<ast::EventStatement*>(
            [](detail::IsParseState auto& state, NodeLocation loc, ast::IdentifierValue* name, ast::ListValue* list) {
               auto country_decl = state.ast().intern("country_event");
               auto province_decl = state.ast().intern("province_event");

               if (name->value() != country_decl || name->value() != province_decl) {
                  state.logger().warning("event declarator \"{}\" is not {} or {}", name->value().c_str(), country_decl.c_str(), province_decl.c_str()) //
                     .primary(loc, "here")
                     .finish();
               }

               return state.ast().template create<ast::EventStatement>(loc, name->value() == province_decl, list);
            });
   };

   struct EventFile {
      // Allow arbitrary spaces between individual tokens.
      static constexpr auto whitespace = whitespace_specifier | comment_specifier;

      static constexpr auto rule = lexy::dsl::position + lexy::dsl::terminator(lexy::dsl::eof).list(lexy::dsl::p<EventStatement> | lexy::dsl::p<SAssignStatement<StringEscapeOption>>);

      static constexpr auto value = lexy::as_list<ast::StatementList> >> construct<ast::FileTree>;
   };
}