summaryrefslogtreecommitdiff
path: root/src/main/java/bjc/rgens/parser/GenerationState.java
blob: 3a6f827b4aa232029a660fd2aef69f5fdca82667 (plain)
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
package bjc.rgens.parser;

import bjc.utils.esodata.MapSet;
import bjc.utils.ioutils.ReportWriter;

import java.io.IOException;
import java.io.StringWriter;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import static bjc.rgens.parser.RGrammarLogging.*;

/* 
 * The current state during generation.
 *
 */
public class GenerationState {
	/** The current string. */
	private ReportWriter contents;
	/** The RNG. */
	public Random rnd;

	/** The current grammar. */
	public RGrammar gram;
	/** The rules of the grammar. */
	private Map<String, Rule> rules;
	/** The rules imported from other grammars. */
	private Map<String, Rule> importRules;

	/** The current set of variables. */
	private MapSet<String, String> vars;
	private MapSet<String, Rule> rlVars;

	private static final Random BASE = new Random();

	/**
	 * Create a new generation state.
	 * 
	 * @param rw
	 *                The place to write the string.
	 *
	 * @param rand
	 *                The RNG to use.
	 *
	 * @param vs
	 *                The variables to use.
	 * @param rvs
	 *                The rule variables to use.
	 * @param gram
	 *                The current grammar.
	 */
	public GenerationState(ReportWriter rw, Random rand, Map<String, String> vs, Map<String, Rule> rvs,
			RGrammar gram) {
		vars = new MapSet<>();
		rlVars = new MapSet<>();

		contents = rw;
		rnd = rand;

		vars.setPutMap(gram.name, vs);
		rlVars.setPutMap(gram.name, rvs);

		this.gram = gram;

		this.rules = gram.getRules();
		this.importRules = gram.getImportRules();
	}

	public static GenerationState fromGrammar(RGrammar gram) {
		return fromGrammar(BASE, gram);
	}

	public static GenerationState fromGrammar(Random rand, RGrammar gram) {
		ReportWriter rw = new ReportWriter(new StringWriter());

		return new GenerationState(rw, rand, new HashMap<>(), new HashMap<>(), gram);
	}

	public void swapGrammar(RGrammar gram) {
		if (this.gram == gram) return;

		this.gram = gram;

		rules = gram.getRules();

		importRules = gram.getImportRules();

		vars.setCreateMap(gram.name);
		rlVars.setCreateMap(gram.name);
	}

	public GenerationState newBuf() {
		// @NOTE 9/5/18
		//
		// Not sure if this is the right thing to do or not.
		//
		// I suppose it'll only matter once we actually start using the
		// features of ReportWriter, instead of just using the basic
		// Writer functionality
		ReportWriter rw = contents.duplicate(new StringWriter());

		return new GenerationState(rw, rnd, vars, rlVars, gram);
	}

	/*
	 * @TODO 6/5/18 Ben Culkin :ImportRefactor
	 * 
	 * Change this so that imports in almost all cases have to specify where
	 * they are importing the rule from, so as to make it clear which rules
	 * are imported, and which aren't
	 */
	public Rule findRule(String ruleName, boolean allowImports) {
		if (rules.containsKey(ruleName)) {
			return rules.get(ruleName);
		}

		if (allowImports) return findImport(ruleName);

		return null;
	}

	public Rule findImport(String ruleName) {
		if (importRules.containsKey(ruleName)) {
			return importRules.get(ruleName);
		}

		return null;
	}

	public void defineVar(String name, String val) {
		if (vars.containsKey(name))
			warn("Shadowing variable %s with value %s (old value %s)", name, val, vars.get(name));
		else if (gram.autoVars.containsKey(name))
			warn("Shadowing autovariable %s with value %s (defn. %s)", name, val, gram.autoVars.get(name));

		vars.put(name, val);
	}

	public void defineRuleVar(String name, Rule rle) {
		if (rlVars.containsKey(name))
			warn("Shadowing rule variable %s with value %s (old value %s)", name, rlVars.get(name), rle);
		else if (gram.autoRlVars.containsKey(name))
			warn("Shadowing rule autovariable %s with value %s (defn. %s)", name, rle,
					gram.autoRlVars.get(name));

		rlVars.put(name, rle);
	}

	public String findVar(String name) {
		if (!vars.containsKey(name)) if (gram.autoVars.containsKey(name)) {
			gram.autoVars.get(name).generate(this);
		} else {
			throw new GrammarException(String.format("Variable %s not defined", name));
		}

		return vars.get(name);
	}

	public Rule findRuleVar(String name) {
		if (!rlVars.containsKey(name)) if (gram.autoRlVars.containsKey(name)) {
			gram.autoRlVars.get(name).generate(this);
		} else {
			throw new GrammarException(String.format("Rule variable %s not defined", name));
		}

		return rlVars.get(name);
	}

	public void appendContents(String strang) {
		try {
			contents.write(strang);
		} catch (IOException ioex) {
			throw new GrammarException("I/O Error", ioex);
		}
	}

	public void setContents(String strang) {
		// @NOTE 9/5/18
		//
		// This raises some interesting questions as to what the
		// appropriate behavior is.
		//
		// For now, I'm simply going to say to go with a StringWriter
		// and then write the contents to that, but I am not sure that
		// that is the right way to go about it.
		contents = contents.duplicate(new StringWriter());

		try {
			contents.write(strang);
		} catch (IOException ioex) {
			throw new GrammarException("I/O Error", ioex);
		}
	}

	public ReportWriter getWriter() {
		return contents;
	}

	public String getContents() {
		return contents.toString();
	}

	public void findReplaceContents(String find, String replace) {
		setContents(getContents().replaceAll(find, replace));
	}

	public void clearContents() {
		contents = contents.duplicate(new StringWriter());
	}
}