Skip to content

Commit 1fcd9ad

Browse files
committed
rt: Implement a multi-threaded symbol table and adjust the native function so that they know their frame index
1 parent 0440272 commit 1fcd9ad

15 files changed

+277
-167
lines changed

src/main/java/org/piccode/rt/Context.java

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,60 @@
11
package org.piccode.rt;
22

3+
import java.nio.charset.StandardCharsets;
4+
import java.security.MessageDigest;
5+
import java.security.NoSuchAlgorithmException;
6+
import java.util.ArrayList;
37
import java.util.HashMap;
48
import java.util.List;
59
import java.util.Stack;
610
import java.util.UUID;
11+
import java.util.concurrent.ConcurrentHashMap;
12+
import java.util.concurrent.ConcurrentMap;
713
import java.util.concurrent.ExecutorService;
814
import java.util.concurrent.Executors;
915
import java.util.concurrent.Future;
1016
import org.piccode.ast.Ast;
17+
import org.piccode.backend.Compiler;
1118

1219
/**
1320
*
1421
* @author hexaredecimal
1522
*/
1623
public class Context {
24+
1725
private static HashMap<String, PiccodeValue> global_scope = new HashMap<>();
26+
private static MessageDigest hash = null;
27+
1828
private Stack<StackFrame> call_frames;
29+
public StackFrame bottom = null;
1930

2031
public static Context top = new Context();
21-
public static HashMap<String, PiccodeModule> modules = new HashMap<>();
22-
private HashMap<String, List<Ast>> import_cache = new HashMap<>();
23-
private ExecutorService threadPool;
24-
private HashMap<String, Future<PiccodeValue>> futureMap;
25-
32+
public static ConcurrentHashMap<String, PiccodeModule> modules = new ConcurrentHashMap<>();
33+
private static List<Context> threadContexts = new ArrayList<>();
34+
private ConcurrentHashMap<String, List<Ast>> import_cache = new ConcurrentHashMap<>();
35+
private static ExecutorService threadPool = Executors.newVirtualThreadPerTaskExecutor();
36+
private static ConcurrentMap<String, Future<PiccodeValue>> futureMap = new ConcurrentHashMap<>();
37+
2638
public Context() {
2739
call_frames = new Stack<>();
28-
import_cache = new HashMap<>();
29-
threadPool = Executors.newVirtualThreadPerTaskExecutor();
30-
futureMap = new HashMap<>();
40+
import_cache = new ConcurrentHashMap<>();
41+
}
42+
43+
public static int makeThreadContext(Context base) {
44+
int index = threadContexts.size();
45+
var context = new Context();
46+
context.call_frames.addAll(base.call_frames);
47+
context.futureMap.putAll(base.futureMap);
48+
threadContexts.add(context);
49+
return index;
50+
}
51+
52+
public static void dropThreadContext(int index) {
53+
threadContexts.remove(index);
54+
}
55+
56+
public static Context getContextAt(int index) {
57+
return threadContexts.get(index);
3158
}
3259

3360
public Stack<StackFrame> getCallStack() {
@@ -38,24 +65,35 @@ public int getFramesCount() {
3865
return call_frames.size();
3966
}
4067

41-
public String makeThread(PiccodeClosure node) {
42-
var future = threadPool.submit(() -> {
43-
var call = (PiccodeClosure) node.call(new PiccodeUnit());
44-
return call.evaluateIfReady();
45-
});
46-
var id = UUID.randomUUID().toString();
47-
futureMap.put(id, future);
48-
return id;
68+
public static String makeThread(PiccodeClosure node) {
69+
synchronized (Context.class) {
70+
var future = threadPool.submit(() -> {
71+
synchronized (node) {
72+
var ctx = node.frame == null ? top : threadContexts.get(node.frame);
73+
var frame = makeThreadContext(ctx);
74+
var call = (PiccodeClosure) node.call(new PiccodeUnit());
75+
call.frame = frame;
76+
return call.evaluateIfReady();
77+
}
78+
});
79+
var size = futureMap.size();
80+
var name = String.format("Thread@%d", size);
81+
var id = name + UUID.randomUUID().toString();
82+
futureMap.put(id, future);
83+
return id;
84+
}
4985
}
5086

51-
public Future<PiccodeValue> getFuture(String id) {
52-
return futureMap.get(id);
87+
public static Future<PiccodeValue> getFuture(String id) {
88+
synchronized (Context.class) {
89+
return futureMap.get(id);
90+
}
5391
}
5492

55-
public void removeFuture(String uuid) {
93+
public static void removeFuture(String uuid) {
5694
futureMap.remove(uuid);
5795
}
58-
96+
5997
public StackFrame getTopFrame() {
6098
return call_frames.peek();
6199
}
@@ -75,22 +113,23 @@ public void cacheImport(String path, List<Ast> nodes) {
75113
public List<Ast> getCached(String path) {
76114
return import_cache.get(path);
77115
}
78-
79-
116+
80117
public void pushScope() {
81118
call_frames.peek().addScope();
82119
}
83120

84121
public void dropScope() {
85122
call_frames.peek().dropScope();
86123
}
87-
124+
88125
public void pushStackFrame(Ast top) {
89126
if (call_frames.isEmpty()) {
90-
call_frames.push(new StackFrame(top));
127+
var sp = new StackFrame(top);
128+
bottom = sp;
129+
call_frames.push(sp);
91130
return;
92131
}
93-
132+
94133
var prev = call_frames.peek();
95134
call_frames.push(new StackFrame(top, prev));
96135
}
@@ -116,7 +155,6 @@ public PiccodeValue getValue(String name) {
116155
return value;
117156
}
118157

119-
120158
public String getSimilarName(String id) {
121159
HashMap<String, Integer> calculations = new HashMap<>();
122160
var top = call_frames.peek().toMap();

src/main/java/org/piccode/rt/NativeFunction.java

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,44 +12,58 @@
1212
public abstract class NativeFunction implements PiccodeValue {
1313

1414
private final String name;
15+
1516
private final List<String> params;
1617
private final Map<String, PiccodeValue> defaultArgs;
1718
private final Map<String, PiccodeValue> boundArgs;
19+
public Integer frame;
20+
public int line = 0;
21+
public int column = 0;
22+
public String file = null;
1823

19-
public NativeFunction(String name, List<String> params, Map<String, PiccodeValue> defaultArgs) {
24+
public NativeFunction(String name, List<String> params, Map<String, PiccodeValue> defaultArgs, Integer frame) {
2025
this.name = name;
2126
this.params = params;
2227
this.defaultArgs = defaultArgs != null ? defaultArgs : new HashMap<>();
2328
this.boundArgs = new HashMap<>();
29+
this.frame = frame;
2430
}
2531

26-
public NativeFunction(String name, List<String> params, Map<String, PiccodeValue> defaultArgs, Map<String, PiccodeValue> boundArgs) {
32+
public NativeFunction(String name, List<String> params, Map<String, PiccodeValue> defaultArgs, Map<String, PiccodeValue> boundArgs, Integer frame) {
2733
this.name = name;
2834
this.params = params;
2935
this.defaultArgs = defaultArgs != null ? defaultArgs : new HashMap<>();
3036
this.boundArgs = boundArgs != null ? new HashMap<>(boundArgs) : new HashMap<>();
37+
this.frame = frame;
3138
}
3239

33-
// Curry-style apply
3440
public PiccodeValue call(PiccodeValue value) {
3541
if (boundArgs.size() >= params.size()) {
36-
throw new PiccodeException("native", 0, 0, "Too many arguments applied");
42+
throw new PiccodeException(file, line, column, "Too many arguments applied");
3743
}
3844

3945
String nextParam = params.get(boundArgs.size());
4046
Map<String, PiccodeValue> newBound = new HashMap<>(boundArgs);
4147
newBound.put(nextParam, value);
42-
return new CurriedNativeFunction(name, params, defaultArgs, newBound, this);
48+
var cn = new CurriedNativeFunction(name, params, defaultArgs, newBound, this, frame);
49+
cn.file = file;
50+
cn.line = line;
51+
cn.column = column;
52+
return cn;
4353
}
4454

4555
public PiccodeValue callNamed(String name, PiccodeValue value) {
4656
if (!params.contains(name)) {
47-
throw new PiccodeException("native", 0, 0, "Unknown argument: " + name);
57+
throw new PiccodeException(file, line, column, "Unknown argument: " + name);
4858
}
4959

5060
Map<String, PiccodeValue> newBound = new HashMap<>(boundArgs);
5161
newBound.put(name, value);
52-
return new CurriedNativeFunction(name, params, defaultArgs, newBound, this);
62+
var cn = new CurriedNativeFunction(name, params, defaultArgs, newBound, this, frame);
63+
cn.file = file;
64+
cn.line = line;
65+
cn.column = column;
66+
return cn;
5367
}
5468

5569
public PiccodeValue evaluateIfReady() {
@@ -63,26 +77,29 @@ public PiccodeValue evaluateIfReady() {
6377
} else if (defaultArgs.containsKey(param)) {
6478
orderedArgs.add(defaultArgs.get(param));
6579
} else {
66-
throw new PiccodeException("native", 0, 0, "Missing argument: " + param);
80+
throw new PiccodeException(file, line, column, "Missing argument: " + param);
6781
}
6882
}
6983

70-
return invoke(orderedArgs, boundArgs);
84+
return invoke(orderedArgs, boundArgs, frame);
7185
}
7286

7387
public class CurriedNativeFunction extends NativeFunction {
7488

7589
private final NativeFunction target;
90+
public int line = 0;
91+
public int column = 0;
92+
public String file = null;
7693

7794
public CurriedNativeFunction(String name, List<String> params, Map<String, PiccodeValue> defaultArgs,
78-
Map<String, PiccodeValue> boundArgs, NativeFunction target) {
79-
super(name, params, defaultArgs, boundArgs);
95+
Map<String, PiccodeValue> boundArgs, NativeFunction target, Integer frame) {
96+
super(name, params, defaultArgs, boundArgs, frame);
8097
this.target = target;
8198
}
8299

83100
@Override
84-
public PiccodeValue invoke(List<PiccodeValue> args, Map<String, PiccodeValue> namedArgs) {
85-
return target.invoke(args, namedArgs);
101+
public PiccodeValue invoke(List<PiccodeValue> args, Map<String, PiccodeValue> namedArgs, Integer frame) {
102+
return target.invoke(args, namedArgs, frame);
86103
}
87104

88105
@Override
@@ -106,5 +123,5 @@ public String toString() {
106123
return "<native function " + name + ">";
107124
}
108125

109-
public abstract PiccodeValue invoke(List<PiccodeValue> args, Map<String, PiccodeValue> namedArgs);
126+
public abstract PiccodeValue invoke(List<PiccodeValue> args, Map<String, PiccodeValue> namedArgs, Integer frame);
110127
}

src/main/java/org/piccode/rt/PiccodeClosure.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class PiccodeClosure implements PiccodeValue {
2323

2424
public String file;
2525
public int line, column;
26+
public Integer frame = null;
2627
public PiccodeClosure(List<Arg> params, Map<String, PiccodeValue> appliedArgs, int positionalIndex, Ast body) {
2728
this.params = params == null ? List.of() : params;
2829
this.appliedArgs = appliedArgs;
@@ -34,6 +35,7 @@ public PiccodeClosure(List<Arg> params, Map<String, PiccodeValue> appliedArgs, i
3435
public PiccodeValue call(PiccodeValue arg) {
3536
if (positionalIndex >= params.size()) {
3637
var err = new PiccodeException(callSiteFile, callSite.line, callSite.col, "Too many arguments. Expected " + params.size() + " but got " + (positionalIndex + 1));
38+
err.frame = frame;
3739
var note = new PiccodeException(file, line, column, "The function you are trying to call is declared below");
3840
err.addNote(note);
3941
throw err;
@@ -45,6 +47,7 @@ public PiccodeValue call(PiccodeValue arg) {
4547

4648
var result = new PiccodeClosure(params, newArgs, positionalIndex + 1, body);
4749
result.creator = creator;
50+
result.frame = frame;
4851
result.callSite = callSite;
4952
result.callSiteFile = callSiteFile;
5053
result.file = file;
@@ -57,20 +60,23 @@ public PiccodeValue callNamed(String name, PiccodeValue arg) {
5760
boolean paramExists = params.stream().anyMatch(p -> p.name.equals(name));
5861
if (!paramExists) {
5962
var err = new PiccodeException(callSiteFile, callSite.line, callSite.col, "Function does not have a parameter named '" + name + "'");
63+
err.frame = frame;
6064
var note = new PiccodeException(file, line, column, "The function you are trying to call is declared below");
6165
err.addNote(note);
6266
throw err;
6367
}
6468

6569
if (positionalIndex >= params.size()) {
6670
var err = new PiccodeException(callSiteFile, callSite.line, callSite.col, "Too many arguments. Expected " + params.size() + " but got " + (positionalIndex + 1));
71+
err.frame = frame;
6772
var note = new PiccodeException(file, line, column, "The function you are trying to call is declared below");
6873
err.addNote(note);
6974
throw err;
7075
}
7176

7277
if (appliedArgs.containsKey(name)) {
7378
var err = new PiccodeException(callSiteFile, callSite.line, callSite.col, "Duplicate argument: " + name);
79+
err.frame = frame;
7480
var note = new PiccodeException(file, line, column, "The function you are trying to call is declared below");
7581
err.addNote(note);
7682
throw err;
@@ -81,6 +87,7 @@ public PiccodeValue callNamed(String name, PiccodeValue arg) {
8187

8288
var result = new PiccodeClosure(params, newArgs, positionalIndex + 1, body);
8389
result.creator = creator;
90+
result.frame = frame;
8491
result.callSite = callSite;
8592
result.callSiteFile = callSiteFile;
8693
result.file = file;
@@ -98,23 +105,29 @@ public PiccodeValue evaluateIfReady() {
98105
}
99106
}
100107

108+
var top = Context.top;
109+
if (frame != null) {
110+
top = Context.getContextAt(frame);
111+
}
101112
// All required args satisfied (either by user or by default), run
102-
Context.top.pushStackFrame(creator);
113+
top.pushStackFrame(creator);
103114
for (Arg param : params) {
104115
PiccodeValue val = appliedArgs.getOrDefault(
105116
param.name,
106-
param.def_val != null ? param.def_val.execute() : null
117+
param.def_val != null ? param.def_val.execute(frame) : null
107118
);
108-
Context.top.putLocal(param.name, val);
119+
top.putLocal(param.name, val);
109120
}
110121

111122
try {
112-
var result = body.execute();
113-
Context.top.dropStackFrame();
123+
var result = body.execute(frame);
124+
top.dropStackFrame();
125+
114126
return result;
115127
} catch (StackOverflowError se) {
116-
Context.top.dropStackFrame();
128+
top.dropStackFrame();
117129
var err = new PiccodeException(callSiteFile, callSite.line, callSite.col, "Stack overflow");
130+
err.frame = frame;
118131
var note = new PiccodeException(file, line, column, "Stack overflow error most likely occured when you called the function below. ");
119132
err.addNote(note);
120133
throw err;

src/main/java/org/piccode/rt/PiccodeException.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public class PiccodeException extends RuntimeException implements PiccodeInfo {
1717
public int line, col;
1818
public String message;
1919

20+
public Integer frame = null;
21+
2022
private List<PiccodeInfo> notes = new ArrayList<>();
2123

2224
public PiccodeException(String file, int line, int col, String message) {
@@ -92,15 +94,20 @@ public void reportError(boolean die, String kind) {
9294
}
9395
}
9496

95-
var stack = Context.top.getCallStack();
97+
var top = Context.top;
98+
if (frame != null) {
99+
top = Context.getContextAt(frame);
100+
}
101+
var stack = top.getCallStack();
96102

97103
var list = List.of(stack.toArray(StackFrame[]::new)).reversed();
98104

99105
if (!list.isEmpty()) {
100-
System.out.println("\n[STACK TRACE]");
106+
var thread = frame == null ? "" : String.format(".THREAD[%s]", frame);
107+
System.out.println("\n[STACK TRACE]" + thread);
101108
for (int i = 0; i < list.size(); i++) {
102-
var frame = list.get(i);
103-
var callSite = frame.caller;
109+
var _frame = list.get(i);
110+
var callSite = _frame.caller;
104111
var _str = String.format(
105112
"[%s:%d:%d]: %s", callSite.file,
106113
callSite.line, callSite.column + 1,

0 commit comments

Comments
 (0)