Skip to content

Commit 4a02a79

Browse files
authored
Merge pull request #125 from aya-lang/named-instruction-spi-v0.6
Load new operators at runtime
2 parents 99875fc + 187092e commit 4a02a79

22 files changed

+921
-754
lines changed

base/importlib.aya

+12-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ def importlib::aya_dir :(sys.ad)
1111
.# Dictionary of files which have been imported
1212
def importlib::imported :{}
1313

14+
def importlib::loaded_jars []
15+
1416
def importlib::path [
1517
importlib.aya_dir "std" :9s + + .# <aya>/base
1618
]
@@ -164,7 +166,9 @@ def importlib::import {__name : importlib^,
164166
__name importlib.from_path
165167
} (__name :T ::str =) {
166168
{ .# Determine load command based on string type
167-
(__name ".aya" H) {
169+
(__name ".jar" H) {
170+
__name importlib.load_library
171+
} (__name ".aya" H) {
168172
__name importlib.from_file
169173
} (__name.[0] '/ =) {
170174
__name importlib.load_file
@@ -181,6 +185,13 @@ def importlib::import {__name : importlib^,
181185
}
182186

183187

188+
def importlib::load_library {jar_file : importlib^,
189+
importlib.loaded_jars jar_file H ! {
190+
jar_file :(library.load) ;
191+
jar_file importlib.loaded_jars .B ;
192+
} ?
193+
}
194+
184195

185196
def importlib::is_exportall {
186197
{ .# try

manual/libraries.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
## Adding more Instructions
2+
3+
You can add more Instructions by loading a `.jar` file with `:(library.load)`
4+
5+
A jar can provide one or more `NamedInstructionStore` implementations.
6+
This is done by adding the fully qualified class name(s) to this file:
7+
`META-INF/services/aya.instruction.named.NamedInstructionStore`
8+
9+
For more information, you should look up 'Java SPI' (Service Provider Interface).
10+
11+
### Example
12+
13+
This adds an instruction `:{example.instruction}` which pushes the String `hello, world` onto the stack.
14+
15+
```java
16+
package my.instruction;
17+
18+
import aya.eval.BlockEvaluator;
19+
import aya.instruction.named.NamedInstructionStore;
20+
import aya.instruction.named.NamedOperator;
21+
import aya.obj.list.List;
22+
23+
public class MyInstructionStore implements NamedInstructionStore {
24+
@Override
25+
public Collection<NamedOperator> getNamedInstructions() {
26+
return Arrays.asList(
27+
new NamedOperator("example.instruction") {
28+
@Override
29+
public void execute(BlockEvaluator blockEvaluator) {
30+
blockEvaluator.push(List.fromString("hello, world"));
31+
}
32+
}
33+
);
34+
}
35+
}
36+
```

src/aya/AyaPrefs.java

+12-15
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,22 @@ public class AyaPrefs {
2424
+ "https://github.com/nick-paul/aya-lang/issues with the stacktrace below.\n"
2525
+ "=== [ Stacktrace ] ===";
2626

27-
public static void initDefaultWorkingDir() {
27+
public static File getAyaRootDirectory() {
2828
try {
29-
workingDir = AyaThread.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
30-
// if(workingDir.length() > 0) {
31-
// workingDir = workingDir.substring(1, workingDir.length()); //Remove the leading '/'
32-
// }
33-
if(workingDir.contains(".jar")) {
34-
int ix = workingDir.lastIndexOf('/');
35-
workingDir = workingDir.substring(0, ix+1);
29+
File codeSource = new File(AyaThread.class.getProtectionDomain().getCodeSource().getLocation().toURI());
30+
if(codeSource.isFile()) {
31+
codeSource = codeSource.getParentFile();
3632
}
37-
38-
workingDir = (new File(workingDir).getCanonicalPath()).toString() + File.separator;
33+
return codeSource;
3934
} catch (URISyntaxException e) {
40-
workingDir = "";
41-
StaticData.IO.printDebug("Cannot locate working dir");
42-
} catch (IOException e) {
43-
workingDir = "";
44-
StaticData.IO.printDebug("Cannot locate working dir");
35+
StaticData.IO.printDebug("Cannot locate aya dir: " + e.getMessage());
36+
return null;
4537
}
38+
}
39+
40+
public static void initDefaultWorkingDir() {
41+
File rootDir = getAyaRootDirectory();
42+
workingDir = rootDir == null ? "" : (rootDir.getAbsolutePath() + File.separator);
4643
defaultWorkingDir = workingDir;
4744
}
4845

src/aya/StaticData.java

+90-55
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
package aya;
22

3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.net.MalformedURLException;
6+
import java.net.URL;
7+
import java.net.URLClassLoader;
38
import java.util.ArrayList;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
import java.util.ServiceLoader;
12+
import java.util.stream.StreamSupport;
413

14+
import aya.exceptions.runtime.IOError;
515
import aya.ext.color.ColorInstructionStore;
616
import aya.ext.date.DateInstructionStore;
717
import aya.ext.debug.DebugInstructionStore;
@@ -11,6 +21,7 @@
1121
import aya.ext.image.ImageInstructionStore;
1222
import aya.ext.json.JSONInstructionStore;
1323
import aya.ext.la.LinearAlgebraInstructionStore;
24+
import aya.ext.library.LibraryInstructionStore;
1425
import aya.ext.plot.PlotInstructionStore;
1526
import aya.ext.socket.SocketInstructionStore;
1627
import aya.ext.sys.SystemInstructionStore;
@@ -48,60 +59,52 @@ public class StaticData {
4859
// All calls to modify this data will need to be thread safe
4960
//
5061
private static StaticData _instance;
51-
62+
63+
5264
//
5365
// Data loaded in the parser
5466
//
5567
private StringSearch _helpData;
5668

57-
69+
5870
//
5971
// Data Loaded on Start-up
6072
//
61-
private ArrayList<NamedInstructionStore> _namedInstructionStores;
62-
63-
73+
private final Map<String, NamedOperator> _namedInstructions = new HashMap<>();
74+
75+
6476
private StaticData() {
6577
_helpData = null; // initHelpData will create
66-
_namedInstructionStores = new ArrayList<NamedInstructionStore>();
6778
}
68-
79+
6980
public static StaticData getInstance() {
7081
if (_instance == null) {
7182
_instance = new StaticData();
7283
}
7384
return _instance;
7485
}
75-
86+
7687
public void init() {
7788
initHelpData();
7889
initNamedInstructions();
7990
}
80-
81-
public void addNamedInstructionStore(NamedInstructionStore is) {
82-
_namedInstructionStores.add(is);
83-
is.initHelpData(this);
84-
}
85-
86-
91+
92+
8793
///////////////
8894
// Help Data //
8995
///////////////
90-
96+
9197
private void initHelpData() {
92-
if(_helpData == null) {
93-
98+
if (_helpData == null) {
99+
94100
//Make sure all classes are loaded
95-
try
96-
{
97-
loadOps(Ops.OPS);
98-
loadOps(Ops.EXTRA_OPS);
99-
loadOps(MiscOps.MATH_OPS);
100-
loadOps(ColonOps.COLON_OPS);
101-
loadOps(DotOps.DOT_OPS);
102-
}
103-
catch(Exception e)
104-
{
101+
try {
102+
loadOps(Ops.OPS);
103+
loadOps(Ops.EXTRA_OPS);
104+
loadOps(MiscOps.MATH_OPS);
105+
loadOps(ColonOps.COLON_OPS);
106+
loadOps(DotOps.DOT_OPS);
107+
} catch (Exception e) {
105108
e.printStackTrace();
106109
}
107110
ArrayList<String> searchList = new ArrayList<String>();
@@ -113,20 +116,20 @@ private void initHelpData() {
113116
_helpData = new StringSearch(searchList);
114117
}
115118
}
116-
119+
117120
public StringSearch getHelpData() {
118121
initHelpData();
119122
return _helpData;
120123
}
121-
124+
122125
public void addHelpText(String in) {
123126
getHelpData().addUnique(in);
124127
}
125128

126129
public String[] getQuickSearchData() {
127130
return getHelpData().getAllItems();
128131
}
129-
132+
130133
/* This function does nothing but force java to load
131134
* the operators and call the static blocks
132135
*/
@@ -141,36 +144,68 @@ private void loadOps(Operator[] ops) {
141144
////////////////////////
142145

143146
private void initNamedInstructions() {
144-
_namedInstructionStores.add(new DebugInstructionStore());
145-
_namedInstructionStores.add(new JSONInstructionStore());
146-
_namedInstructionStores.add(new ImageInstructionStore());
147-
_namedInstructionStores.add(new GraphicsInstructionStore());
148-
_namedInstructionStores.add(new FStreamInstructionStore());
149-
_namedInstructionStores.add(new SystemInstructionStore());
150-
_namedInstructionStores.add(new DialogInstructionStore());
151-
_namedInstructionStores.add(new PlotInstructionStore());
152-
_namedInstructionStores.add(new DateInstructionStore());
153-
_namedInstructionStores.add(new SocketInstructionStore());
154-
_namedInstructionStores.add(new ColorInstructionStore());
155-
_namedInstructionStores.add(new LinearAlgebraInstructionStore());
156-
_namedInstructionStores.add(new ThreadInstructionStore());
147+
addNamedInstructionStore(new DebugInstructionStore());
148+
addNamedInstructionStore(new JSONInstructionStore());
149+
addNamedInstructionStore(new ImageInstructionStore());
150+
addNamedInstructionStore(new GraphicsInstructionStore());
151+
addNamedInstructionStore(new FStreamInstructionStore());
152+
addNamedInstructionStore(new SystemInstructionStore());
153+
addNamedInstructionStore(new DialogInstructionStore());
154+
addNamedInstructionStore(new PlotInstructionStore());
155+
addNamedInstructionStore(new DateInstructionStore());
156+
addNamedInstructionStore(new SocketInstructionStore());
157+
addNamedInstructionStore(new ColorInstructionStore());
158+
addNamedInstructionStore(new LinearAlgebraInstructionStore());
159+
addNamedInstructionStore(new ThreadInstructionStore());
160+
addNamedInstructionStore(new LibraryInstructionStore());
161+
}
162+
163+
public ArrayList<NamedInstructionStore> loadLibrary(File path) {
164+
ArrayList<NamedInstructionStore> loaded = new ArrayList<NamedInstructionStore>();
157165

158-
for (NamedInstructionStore x : _namedInstructionStores) {
159-
x.initHelpData(this);
166+
try {
167+
URL[] urls = {path.toURI().toURL()};
168+
169+
try (URLClassLoader libClassLoader = new URLClassLoader(urls)) {
170+
StreamSupport.stream(
171+
ServiceLoader.load(NamedInstructionStore.class, libClassLoader).spliterator(),
172+
false
173+
).forEach(store -> {
174+
//IO.out().println("found store: " + store.getClass().getName());
175+
addNamedInstructionStore(store);
176+
loaded.add(store);
177+
});
178+
} catch (IOException e) {
179+
throw new IOError("library.load", path.getPath(), e);
180+
}
181+
182+
} catch (MalformedURLException e) {
183+
throw new IOError("library.load", path.getPath(), e);
160184
}
185+
186+
return loaded;
161187
}
162-
163-
164-
public NamedOperator getNamedInstruction(String name) {
165-
for (NamedInstructionStore x : _namedInstructionStores) {
166-
NamedOperator i = x.getInstruction(name);
167-
if (i != null) {
168-
return i;
188+
189+
public void addNamedInstructionStore(NamedInstructionStore store) {
190+
for (NamedOperator instruction : store.getNamedInstructions()) {
191+
String iName = instruction.getName();
192+
NamedOperator previous = _namedInstructions.put(iName, instruction);
193+
if (previous != null) {
194+
IO.err().println("NamedInstruction '" + iName + "' has multiple implementations:\n"
195+
+ " " + previous.getClass().getName() + "\n"
196+
+ " " + instruction.getClass().getName()
197+
);
198+
}
199+
200+
String doc = instruction.getDoc();
201+
if (doc != null && !doc.isEmpty()) {
202+
addHelpText(instruction.opName() + "\n " + doc);
169203
}
170204
}
171-
return null;
172205
}
173206

174-
207+
public NamedOperator getNamedInstruction(String name) {
208+
return _namedInstructions.get(name);
209+
}
175210

176211
}

0 commit comments

Comments
 (0)