Skip to content

Commit

Permalink
Merge pull request #125 from aya-lang/named-instruction-spi-v0.6
Browse files Browse the repository at this point in the history
Load new operators at runtime
  • Loading branch information
nick-paul authored Dec 31, 2024
2 parents 99875fc + 187092e commit 4a02a79
Show file tree
Hide file tree
Showing 22 changed files with 921 additions and 754 deletions.
13 changes: 12 additions & 1 deletion base/importlib.aya
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ def importlib::aya_dir :(sys.ad)
.# Dictionary of files which have been imported
def importlib::imported :{}

def importlib::loaded_jars []

def importlib::path [
importlib.aya_dir "std" :9s + + .# <aya>/base
]
Expand Down Expand Up @@ -164,7 +166,9 @@ def importlib::import {__name : importlib^,
__name importlib.from_path
} (__name :T ::str =) {
{ .# Determine load command based on string type
(__name ".aya" H) {
(__name ".jar" H) {
__name importlib.load_library
} (__name ".aya" H) {
__name importlib.from_file
} (__name.[0] '/ =) {
__name importlib.load_file
Expand All @@ -181,6 +185,13 @@ def importlib::import {__name : importlib^,
}


def importlib::load_library {jar_file : importlib^,
importlib.loaded_jars jar_file H ! {
jar_file :(library.load) ;
jar_file importlib.loaded_jars .B ;
} ?
}


def importlib::is_exportall {
{ .# try
Expand Down
36 changes: 36 additions & 0 deletions manual/libraries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## Adding more Instructions

You can add more Instructions by loading a `.jar` file with `:(library.load)`

A jar can provide one or more `NamedInstructionStore` implementations.
This is done by adding the fully qualified class name(s) to this file:
`META-INF/services/aya.instruction.named.NamedInstructionStore`

For more information, you should look up 'Java SPI' (Service Provider Interface).

### Example

This adds an instruction `:{example.instruction}` which pushes the String `hello, world` onto the stack.

```java
package my.instruction;

import aya.eval.BlockEvaluator;
import aya.instruction.named.NamedInstructionStore;
import aya.instruction.named.NamedOperator;
import aya.obj.list.List;

public class MyInstructionStore implements NamedInstructionStore {
@Override
public Collection<NamedOperator> getNamedInstructions() {
return Arrays.asList(
new NamedOperator("example.instruction") {
@Override
public void execute(BlockEvaluator blockEvaluator) {
blockEvaluator.push(List.fromString("hello, world"));
}
}
);
}
}
```
27 changes: 12 additions & 15 deletions src/aya/AyaPrefs.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,22 @@ public class AyaPrefs {
+ "https://github.com/nick-paul/aya-lang/issues with the stacktrace below.\n"
+ "=== [ Stacktrace ] ===";

public static void initDefaultWorkingDir() {
public static File getAyaRootDirectory() {
try {
workingDir = AyaThread.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
// if(workingDir.length() > 0) {
// workingDir = workingDir.substring(1, workingDir.length()); //Remove the leading '/'
// }
if(workingDir.contains(".jar")) {
int ix = workingDir.lastIndexOf('/');
workingDir = workingDir.substring(0, ix+1);
File codeSource = new File(AyaThread.class.getProtectionDomain().getCodeSource().getLocation().toURI());
if(codeSource.isFile()) {
codeSource = codeSource.getParentFile();
}

workingDir = (new File(workingDir).getCanonicalPath()).toString() + File.separator;
return codeSource;
} catch (URISyntaxException e) {
workingDir = "";
StaticData.IO.printDebug("Cannot locate working dir");
} catch (IOException e) {
workingDir = "";
StaticData.IO.printDebug("Cannot locate working dir");
StaticData.IO.printDebug("Cannot locate aya dir: " + e.getMessage());
return null;
}
}

public static void initDefaultWorkingDir() {
File rootDir = getAyaRootDirectory();
workingDir = rootDir == null ? "" : (rootDir.getAbsolutePath() + File.separator);
defaultWorkingDir = workingDir;
}

Expand Down
145 changes: 90 additions & 55 deletions src/aya/StaticData.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
package aya;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.stream.StreamSupport;

import aya.exceptions.runtime.IOError;
import aya.ext.color.ColorInstructionStore;
import aya.ext.date.DateInstructionStore;
import aya.ext.debug.DebugInstructionStore;
Expand All @@ -11,6 +21,7 @@
import aya.ext.image.ImageInstructionStore;
import aya.ext.json.JSONInstructionStore;
import aya.ext.la.LinearAlgebraInstructionStore;
import aya.ext.library.LibraryInstructionStore;
import aya.ext.plot.PlotInstructionStore;
import aya.ext.socket.SocketInstructionStore;
import aya.ext.sys.SystemInstructionStore;
Expand Down Expand Up @@ -48,60 +59,52 @@ public class StaticData {
// All calls to modify this data will need to be thread safe
//
private static StaticData _instance;



//
// Data loaded in the parser
//
private StringSearch _helpData;


//
// Data Loaded on Start-up
//
private ArrayList<NamedInstructionStore> _namedInstructionStores;
private final Map<String, NamedOperator> _namedInstructions = new HashMap<>();


private StaticData() {
_helpData = null; // initHelpData will create
_namedInstructionStores = new ArrayList<NamedInstructionStore>();
}

public static StaticData getInstance() {
if (_instance == null) {
_instance = new StaticData();
}
return _instance;
}

public void init() {
initHelpData();
initNamedInstructions();
}

public void addNamedInstructionStore(NamedInstructionStore is) {
_namedInstructionStores.add(is);
is.initHelpData(this);
}




///////////////
// Help Data //
///////////////

private void initHelpData() {
if(_helpData == null) {
if (_helpData == null) {

//Make sure all classes are loaded
try
{
loadOps(Ops.OPS);
loadOps(Ops.EXTRA_OPS);
loadOps(MiscOps.MATH_OPS);
loadOps(ColonOps.COLON_OPS);
loadOps(DotOps.DOT_OPS);
}
catch(Exception e)
{
try {
loadOps(Ops.OPS);
loadOps(Ops.EXTRA_OPS);
loadOps(MiscOps.MATH_OPS);
loadOps(ColonOps.COLON_OPS);
loadOps(DotOps.DOT_OPS);
} catch (Exception e) {
e.printStackTrace();
}
ArrayList<String> searchList = new ArrayList<String>();
Expand All @@ -113,20 +116,20 @@ private void initHelpData() {
_helpData = new StringSearch(searchList);
}
}

public StringSearch getHelpData() {
initHelpData();
return _helpData;
}

public void addHelpText(String in) {
getHelpData().addUnique(in);
}

public String[] getQuickSearchData() {
return getHelpData().getAllItems();
}

/* This function does nothing but force java to load
* the operators and call the static blocks
*/
Expand All @@ -141,36 +144,68 @@ private void loadOps(Operator[] ops) {
////////////////////////

private void initNamedInstructions() {
_namedInstructionStores.add(new DebugInstructionStore());
_namedInstructionStores.add(new JSONInstructionStore());
_namedInstructionStores.add(new ImageInstructionStore());
_namedInstructionStores.add(new GraphicsInstructionStore());
_namedInstructionStores.add(new FStreamInstructionStore());
_namedInstructionStores.add(new SystemInstructionStore());
_namedInstructionStores.add(new DialogInstructionStore());
_namedInstructionStores.add(new PlotInstructionStore());
_namedInstructionStores.add(new DateInstructionStore());
_namedInstructionStores.add(new SocketInstructionStore());
_namedInstructionStores.add(new ColorInstructionStore());
_namedInstructionStores.add(new LinearAlgebraInstructionStore());
_namedInstructionStores.add(new ThreadInstructionStore());
addNamedInstructionStore(new DebugInstructionStore());
addNamedInstructionStore(new JSONInstructionStore());
addNamedInstructionStore(new ImageInstructionStore());
addNamedInstructionStore(new GraphicsInstructionStore());
addNamedInstructionStore(new FStreamInstructionStore());
addNamedInstructionStore(new SystemInstructionStore());
addNamedInstructionStore(new DialogInstructionStore());
addNamedInstructionStore(new PlotInstructionStore());
addNamedInstructionStore(new DateInstructionStore());
addNamedInstructionStore(new SocketInstructionStore());
addNamedInstructionStore(new ColorInstructionStore());
addNamedInstructionStore(new LinearAlgebraInstructionStore());
addNamedInstructionStore(new ThreadInstructionStore());
addNamedInstructionStore(new LibraryInstructionStore());
}

public ArrayList<NamedInstructionStore> loadLibrary(File path) {
ArrayList<NamedInstructionStore> loaded = new ArrayList<NamedInstructionStore>();

for (NamedInstructionStore x : _namedInstructionStores) {
x.initHelpData(this);
try {
URL[] urls = {path.toURI().toURL()};

try (URLClassLoader libClassLoader = new URLClassLoader(urls)) {
StreamSupport.stream(
ServiceLoader.load(NamedInstructionStore.class, libClassLoader).spliterator(),
false
).forEach(store -> {
//IO.out().println("found store: " + store.getClass().getName());
addNamedInstructionStore(store);
loaded.add(store);
});
} catch (IOException e) {
throw new IOError("library.load", path.getPath(), e);
}

} catch (MalformedURLException e) {
throw new IOError("library.load", path.getPath(), e);
}

return loaded;
}


public NamedOperator getNamedInstruction(String name) {
for (NamedInstructionStore x : _namedInstructionStores) {
NamedOperator i = x.getInstruction(name);
if (i != null) {
return i;

public void addNamedInstructionStore(NamedInstructionStore store) {
for (NamedOperator instruction : store.getNamedInstructions()) {
String iName = instruction.getName();
NamedOperator previous = _namedInstructions.put(iName, instruction);
if (previous != null) {
IO.err().println("NamedInstruction '" + iName + "' has multiple implementations:\n"
+ " " + previous.getClass().getName() + "\n"
+ " " + instruction.getClass().getName()
);
}

String doc = instruction.getDoc();
if (doc != null && !doc.isEmpty()) {
addHelpText(instruction.opName() + "\n " + doc);
}
}
return null;
}


public NamedOperator getNamedInstruction(String name) {
return _namedInstructions.get(name);
}

}
Loading

0 comments on commit 4a02a79

Please sign in to comment.