The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
context_free_grammar_generator.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 by Ján Dugáček
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Algorithm to generate names using a context-free grammar, which allows more control
18  * than the usual Markov chain generator
19  */
20 
22 
23 #include "log.hpp"
24 #include "random_new.hpp"
26 
27 #include <algorithm>
28 
30 {
31 }
32 
34  initialized_(false)
35 {
36  const char* reading = source.c_str();
37  nonterminal* current = nullptr;
38  std::vector<std::string>* filled = nullptr;
40 
41  while (*reading != 0) {
42  if (*reading == '=') {
43  // Leading and trailing whitespace is not significant, but internal whitespace is
44  current = &nonterminals_[utils::strip(buf)];
45  current->possibilities_.push_back(std::vector<std::string>());
46  filled = &current->possibilities_.back();
47  buf.clear();
48  } else if (*reading == '\n') {
49  // All whitespace is significant after the =
50  if (filled) filled->push_back(buf);
51  filled = nullptr;
52  current = nullptr;
53  buf.clear();
54  } else if (*reading == '|') {
55  if (!filled || !current) {
56  lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced | symbol";
57  return;
58  }
59  filled->push_back(buf);
60  current->possibilities_.push_back(std::vector<std::string>());
61  filled = &current->possibilities_.back();
62  buf.clear();
63  } else if (*reading == '\\' && reading[1] == 'n') {
64  reading++;
65  buf.push_back('\n');
66  } else if (*reading == '\\' && reading[1] == 't') {
67  reading++;
68  buf.push_back('\t');
69  } else {
70  if (*reading == '{') {
71  if (!filled) {
72  lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced { symbol";
73  return;
74  }
75  filled->push_back(buf);
76  buf.clear();
77  }
78  else if (*reading == '}') {
79  if (!filled) {
80  lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced } symbol";
81  return;
82  }
83  filled->push_back('{' + utils::strip(buf));
84  buf.clear();
85  } else buf.push_back(*reading);
86  }
87  reading++;
88  }
89  if (filled) filled->push_back(buf);
90 
91  initialized_ = true;
92 }
93 
95  initialized_(false)
96 {
97  for(auto rule : source) {
98  std::string key = rule.first; // Need to do this because utils::strip is mutating
99  key = utils::strip(key);
100  for(std::string str : rule.second) {
101  nonterminals_[key].possibilities_.emplace_back();
102  std::vector<std::string>* filled = &nonterminals_[key].possibilities_.back();
104  // A little code duplication here...
105  for(char c : str) {
106  if (c == '{') {
107  if (!filled) {
108  lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced { symbol";
109  return;
110  }
111  filled->push_back(buf);
112  buf.clear();
113  }
114  else if (c == '}') {
115  if (!filled) {
116  lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced } symbol";
117  return;
118  }
119  filled->push_back('{' + utils::strip(buf));
120  buf.clear();
121  } else buf.push_back(c);
122  }
123  if(!buf.empty()) {
124  filled->push_back(buf);
125  }
126  }
127  }
128  initialized_ = true;
129 }
130 
133  std::map<std::string, nonterminal>::const_iterator found = nonterminals_.find(name);
134  if (found == nonterminals_.end()) {
135  lg::wml_error() << "[context_free_grammar_generator] Warning: needed nonterminal " << name << " not defined";
136  return "!" + name;
137  }
138  const context_free_grammar_generator::nonterminal& got = found->second;
139  unsigned int picked = seed[seed_pos++] % got.possibilities_.size();
140  if (seed_pos >= seed_size) seed_pos = 0;
141  if (picked == got.last_) {
142  picked = seed[seed_pos++] % got.possibilities_.size();
143  if (seed_pos >= seed_size) seed_pos = 0;
144  }
145  got.last_ = picked;
146  const std::vector<std::string>& used = got.possibilities_[picked];
147  for (unsigned int i = 0; i < used.size(); i++) {
148  if (used[i][0] == '{') result += print_nonterminal(used[i].substr(1), seed, seed_pos);
149  else result += used[i];
150  }
151  return result;
152 }
153 
155  uint32_t seed[seed_size];
156  for (unsigned short int i = 0; i < seed_size; i++) {
158  }
159  return print_nonterminal("main", seed, 0);
160 }
bool initialized_
Definition: font.cpp:609
const GLfloat * c
Definition: glew.h:12741
rng * generator
This generator is automatically synced during synced context.
Definition: random_new.cpp:52
boost::uint32_t uint32_t
Definition: xbrz.hpp:45
std::string & strip(std::string &str)
Remove whitespace from the front and back of the string 'str'.
GLuint64EXT * result
Definition: glew.h:10727
std::vector< std::vector< std::string > > possibilities_
uint32_t next_random()
Provides the next random draw.
Definition: random_new.cpp:76
GLenum GLuint GLsizei const char * buf
Definition: glew.h:2498
std::string generate() const override
Generates a possible word in the grammar set before.
context_free_grammar_generator(const std::string &source)
Initialisation.
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:262
size_t i
Definition: function.cpp:1057
std::string print_nonterminal(const std::string &name, uint32_t *seed, short int seed_pos) const
GLuint const GLchar * name
Definition: glew.h:1782
std::map< std::string, nonterminal > nonterminals_
Standard logging facilities (interface).
GLsizei const GLcharARB ** string
Definition: glew.h:4503
GLsizei GLsizei GLchar * source
Definition: glew.h:1800
static const short unsigned int seed_size