Skip to content

Commit bf9db2c

Browse files
committed
Add BareInterpreter
1 parent 039b46e commit bf9db2c

File tree

13 files changed

+1068
-2
lines changed

13 files changed

+1068
-2
lines changed

src/main/java/com/forthix/forthic/errors/ForthicError.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/**
44
* Base exception class for all Forthic interpreter errors.
55
*/
6-
public class ForthicError extends Exception {
6+
public class ForthicError extends RuntimeException {
77
private final String forthic;
88
private final String note;
99
private final CodeLocation location;
Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
package com.forthix.forthic.interpreter;
2+
3+
import com.forthix.forthic.errors.*;
4+
import com.forthix.forthic.module.*;
5+
import com.forthix.forthic.tokenizer.*;
6+
import java.util.*;
7+
8+
/**
9+
* BareInterpreter - Core Forthic execution engine
10+
*/
11+
public class BareInterpreter {
12+
protected ForthicStack stack;
13+
protected ForthicModule appModule;
14+
protected List<ForthicModule> moduleStack;
15+
protected Map<String, ForthicModule> registeredModules;
16+
protected List<Tokenizer> tokenizerStack;
17+
protected Token previousToken;
18+
protected boolean isCompiling;
19+
protected boolean isMemoDefinition;
20+
protected DefinitionWord curDefinition;
21+
protected CodeLocation stringLocation;
22+
protected List<LiteralHandler> literalHandlers;
23+
24+
public BareInterpreter(List<ForthicModule> modules) {
25+
this.stack = new ForthicStack();
26+
this.tokenizerStack = new ArrayList<>();
27+
this.appModule = new ForthicModule("");
28+
this.appModule.setInterp(this);
29+
this.moduleStack = new ArrayList<>();
30+
this.moduleStack.add(appModule);
31+
this.registeredModules = new HashMap<>();
32+
this.isCompiling = false;
33+
this.isMemoDefinition = false;
34+
this.curDefinition = null;
35+
this.stringLocation = null;
36+
this.previousToken = null;
37+
this.literalHandlers = new ArrayList<>();
38+
registerStandardLiterals();
39+
importModules(modules);
40+
}
41+
42+
public BareInterpreter() {
43+
this(new ArrayList<>());
44+
}
45+
46+
public ForthicModule getAppModule() {
47+
return appModule;
48+
}
49+
50+
public ForthicStack getStack() {
51+
return stack;
52+
}
53+
54+
public String getTopInputString() {
55+
if (tokenizerStack.isEmpty()) return "";
56+
return tokenizerStack.get(0).getInputString();
57+
}
58+
59+
public Tokenizer getTokenizer() {
60+
return tokenizerStack.get(tokenizerStack.size() - 1);
61+
}
62+
63+
public CodeLocation getStringLocation() {
64+
return stringLocation;
65+
}
66+
67+
public void reset() {
68+
stack = new ForthicStack();
69+
appModule.getVariables().clear();
70+
moduleStack.clear();
71+
moduleStack.add(appModule);
72+
isCompiling = false;
73+
isMemoDefinition = false;
74+
curDefinition = null;
75+
stringLocation = null;
76+
}
77+
78+
public boolean run(String string, CodeLocation referenceLocation) throws Exception {
79+
tokenizerStack.add(new Tokenizer(string, referenceLocation));
80+
runWithTokenizer(tokenizerStack.get(tokenizerStack.size() - 1));
81+
tokenizerStack.remove(tokenizerStack.size() - 1);
82+
return true;
83+
}
84+
85+
public boolean run(String string) throws Exception {
86+
return run(string, null);
87+
}
88+
89+
public ForthicModule curModule() {
90+
return moduleStack.get(moduleStack.size() - 1);
91+
}
92+
93+
public void moduleStackPush(ForthicModule module) {
94+
moduleStack.add(module);
95+
}
96+
97+
public void moduleStackPop() {
98+
if (moduleStack.size() > 1) {
99+
moduleStack.remove(moduleStack.size() - 1);
100+
}
101+
}
102+
103+
public void registerModule(ForthicModule module) {
104+
registeredModules.put(module.getName(), module);
105+
}
106+
107+
public ForthicModule findModule(String name) {
108+
ForthicModule result = registeredModules.get(name);
109+
if (result == null) {
110+
throw new UnknownModuleError(getTopInputString(), name, stringLocation);
111+
}
112+
return result;
113+
}
114+
115+
public void importModules(List<ForthicModule> modules) {
116+
for (ForthicModule module : modules) {
117+
appModule.importModule("", module, this);
118+
}
119+
}
120+
121+
public void stackPush(Object val) {
122+
stack.push(val);
123+
}
124+
125+
public Object stackPop() {
126+
if (stack.length() == 0) {
127+
throw new StackUnderflowError(getTopInputString(), getTokenizer().getTokenLocation());
128+
}
129+
Object result = stack.pop();
130+
if (result instanceof PositionedString) {
131+
result = ((PositionedString) result).valueOf();
132+
}
133+
return result;
134+
}
135+
136+
protected boolean runWithTokenizer(Tokenizer tokenizer) throws Exception {
137+
Token token;
138+
do {
139+
token = tokenizer.nextToken();
140+
handleToken(token);
141+
if (token.getType() == TokenType.EOS) break;
142+
previousToken = token;
143+
} while (true);
144+
return true;
145+
}
146+
147+
protected void handleToken(Token token) throws Exception {
148+
switch (token.getType()) {
149+
case STRING: handleStringToken(token); break;
150+
case COMMENT: break;
151+
case START_ARRAY: handleStartArrayToken(token); break;
152+
case END_ARRAY: handleEndArrayToken(token); break;
153+
case START_MODULE: handleStartModuleToken(token); break;
154+
case END_MODULE: handleEndModuleToken(token); break;
155+
case START_DEF: handleStartDefinitionToken(token); break;
156+
case END_DEF: handleEndDefinitionToken(token); break;
157+
case START_MEMO: handleStartMemoToken(token); break;
158+
case WORD: handleWordToken(token); break;
159+
case DOT_SYMBOL: handleDotSymbolToken(token); break;
160+
case EOS:
161+
if (isCompiling) {
162+
CodeLocation location = previousToken != null ? previousToken.getLocation() : token.getLocation();
163+
throw new MissingSemicolonError(getTopInputString(), location);
164+
}
165+
break;
166+
default: throw new UnknownTokenError(getTopInputString(), token.getString(), token.getLocation());
167+
}
168+
}
169+
170+
protected void handleStringToken(Token token) {
171+
PositionedString posString = new PositionedString(token.getString(), token.getLocation());
172+
if (isCompiling) {
173+
PushValueWord word = new PushValueWord(token.getString(), posString);
174+
word.setLocation(token.getLocation());
175+
curDefinition.addWord(word);
176+
} else {
177+
stackPush(posString);
178+
}
179+
}
180+
181+
protected void handleStartArrayToken(Token token) {
182+
if (isCompiling) {
183+
PushValueWord word = new PushValueWord("[", token);
184+
word.setLocation(token.getLocation());
185+
curDefinition.addWord(word);
186+
} else {
187+
stackPush(token);
188+
}
189+
}
190+
191+
protected void handleEndArrayToken(Token token) throws Exception {
192+
EndArrayWord word = new EndArrayWord();
193+
word.setLocation(token.getLocation());
194+
if (isCompiling) {
195+
curDefinition.addWord(word);
196+
} else {
197+
word.execute(this);
198+
}
199+
}
200+
201+
protected void handleStartModuleToken(Token token) throws Exception {
202+
StartModuleWord word = new StartModuleWord(token.getString());
203+
word.setLocation(token.getLocation());
204+
if (isCompiling) {
205+
curDefinition.addWord(word);
206+
} else {
207+
word.execute(this);
208+
}
209+
}
210+
211+
protected void handleEndModuleToken(Token token) throws Exception {
212+
EndModuleWord word = new EndModuleWord();
213+
word.setLocation(token.getLocation());
214+
if (isCompiling) {
215+
curDefinition.addWord(word);
216+
} else {
217+
word.execute(this);
218+
}
219+
}
220+
221+
protected void handleStartDefinitionToken(Token token) {
222+
if (isCompiling) {
223+
throw new MissingSemicolonError(getTopInputString(), token.getLocation());
224+
}
225+
isCompiling = true;
226+
isMemoDefinition = false;
227+
curDefinition = new DefinitionWord(token.getString());
228+
curDefinition.setLocation(token.getLocation());
229+
}
230+
231+
protected void handleEndDefinitionToken(Token token) {
232+
if (!isCompiling) {
233+
throw new ExtraSemicolonError(getTopInputString(), token.getLocation());
234+
}
235+
isCompiling = false;
236+
if (isMemoDefinition) {
237+
curModule().addMemoWords(curDefinition);
238+
} else {
239+
curModule().addWord(curDefinition);
240+
}
241+
curDefinition = null;
242+
}
243+
244+
protected void handleStartMemoToken(Token token) {
245+
if (isCompiling) {
246+
throw new MissingSemicolonError(getTopInputString(), token.getLocation());
247+
}
248+
isCompiling = true;
249+
isMemoDefinition = true;
250+
curDefinition = new DefinitionWord(token.getString());
251+
curDefinition.setLocation(token.getLocation());
252+
}
253+
254+
protected void handleWordToken(Token token) throws Exception {
255+
Word word = findWord(token.getString());
256+
if (word == null) {
257+
throw new UnknownWordError(getTopInputString(), token.getString(), token.getLocation());
258+
}
259+
word.setLocation(token.getLocation());
260+
if (isCompiling) {
261+
curDefinition.addWord(word);
262+
} else {
263+
word.execute(this);
264+
}
265+
}
266+
267+
protected void handleDotSymbolToken(Token token) {
268+
if (isCompiling) {
269+
PushValueWord word = new PushValueWord(token.getString(), token.getString());
270+
word.setLocation(token.getLocation());
271+
curDefinition.addWord(word);
272+
} else {
273+
stackPush(token.getString());
274+
}
275+
}
276+
277+
protected void registerStandardLiterals() {
278+
// Order matters: more specific handlers first
279+
literalHandlers.add(Literals::toBool); // TRUE, FALSE
280+
literalHandlers.add(Literals::toFloat); // 3.14
281+
literalHandlers.add(Literals::toInt); // 42
282+
}
283+
284+
protected Word findLiteralWord(String name) {
285+
for (LiteralHandler handler : literalHandlers) {
286+
Object value = handler.handle(name);
287+
if (value != null) {
288+
return new PushValueWord(name, value);
289+
}
290+
}
291+
return null;
292+
}
293+
294+
protected Word findWord(String name) {
295+
// 1. Check module stack (dictionary words + variables)
296+
Word result = curModule().findWord(name);
297+
if (result == null) {
298+
for (ForthicModule module : registeredModules.values()) {
299+
result = module.findWord(name);
300+
if (result != null) break;
301+
}
302+
}
303+
304+
// 2. Check literal handlers as fallback
305+
if (result == null) {
306+
result = findLiteralWord(name);
307+
}
308+
309+
return result;
310+
}
311+
312+
protected static class StartModuleWord extends Word {
313+
public StartModuleWord(String moduleName) {
314+
super(moduleName);
315+
}
316+
317+
@Override
318+
public void execute(BareInterpreter interp) {
319+
if (name.isEmpty()) {
320+
interp.moduleStackPush(interp.getAppModule());
321+
return;
322+
}
323+
ForthicModule module = interp.curModule().findModule(name);
324+
if (module == null) {
325+
module = new ForthicModule(name);
326+
interp.curModule().registerModule(module.getName(), module.getName(), module);
327+
if (interp.curModule().getName().isEmpty()) {
328+
interp.registerModule(module);
329+
}
330+
}
331+
interp.moduleStackPush(module);
332+
}
333+
}
334+
335+
protected static class EndModuleWord extends Word {
336+
public EndModuleWord() {
337+
super("}");
338+
}
339+
340+
@Override
341+
public void execute(BareInterpreter interp) {
342+
interp.moduleStackPop();
343+
}
344+
}
345+
346+
protected static class EndArrayWord extends Word {
347+
public EndArrayWord() {
348+
super("]");
349+
}
350+
351+
@Override
352+
public void execute(BareInterpreter interp) {
353+
List<Object> items = new ArrayList<>();
354+
Object item = interp.stackPop();
355+
while (true) {
356+
if (item instanceof Token && ((Token) item).getType() == TokenType.START_ARRAY) {
357+
break;
358+
}
359+
items.add(item);
360+
item = interp.stackPop();
361+
}
362+
Collections.reverse(items);
363+
interp.stackPush(items);
364+
}
365+
}
366+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.forthix.forthic.interpreter;
2+
3+
/**
4+
* Functional interface for literal handlers.
5+
* Takes a string and returns a parsed value, or null if it can't parse it.
6+
*/
7+
@FunctionalInterface
8+
public interface LiteralHandler {
9+
Object handle(String str);
10+
}

0 commit comments

Comments
 (0)