From 1de909aa4af5d027ad0da2598c78b0a6bdc8dd01 Mon Sep 17 00:00:00 2001 From: BlazingTwist Date: Sat, 2 Nov 2024 18:40:38 +0100 Subject: [PATCH 1/3] allow adding custom named instructions using java SPI --- build-scripts/release-zip-build.xml | 1 + libs/README.md | 41 +++ src/aya/AyaPrefs.java | 27 +- src/aya/StaticData.java | 155 ++++++--- src/aya/ext/color/ColorInstructionStore.java | 66 ++-- src/aya/ext/date/DateInstructionStore.java | 18 +- src/aya/ext/debug/DebugInstructionStore.java | 15 +- .../ext/dialog/DialogInstructionStore.java | 261 +++++++------- .../ext/fstream/FStreamInstructionStore.java | 55 +-- .../graphics/GraphicsInstructionStore.java | 82 ++--- src/aya/ext/image/ImageInstructionStore.java | 16 +- src/aya/ext/json/JSONInstructionStore.java | 17 +- .../ext/la/LinearAlgebraInstructionStore.java | 14 +- src/aya/ext/plot/PlotInstructionStore.java | 22 +- .../ext/socket/SocketInstructionStore.java | 232 ++++++------- src/aya/ext/sys/SystemInstructionStore.java | 322 +++++++++--------- .../ext/thread/ThreadInstructionStore.java | 229 +++++++------ .../named/NamedInstructionStore.java | 33 +- src/aya/instruction/named/NamedOperator.java | 4 + 19 files changed, 866 insertions(+), 744 deletions(-) create mode 100644 libs/README.md diff --git a/build-scripts/release-zip-build.xml b/build-scripts/release-zip-build.xml index d82ad302..c58bd504 100644 --- a/build-scripts/release-zip-build.xml +++ b/build-scripts/release-zip-build.xml @@ -32,6 +32,7 @@ + diff --git a/libs/README.md b/libs/README.md new file mode 100644 index 00000000..1e09ba4a --- /dev/null +++ b/libs/README.md @@ -0,0 +1,41 @@ +## Adding more Instructions + +You can add more Instructions by placing `.jar` files here. + +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 getNamedInstructions() { + return Arrays.asList( + new NamedOperator("example.instruction") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + blockEvaluator.push(List.fromString("hello, world")); + } + } + ); + } +} +``` + +Then you can register your implementation as a ServiceProvider like this: +``` +> cat META-INF/services/aya.instruction.named.NamedInstructionStore +my.instruction.MyInstructionStore +``` \ No newline at end of file diff --git a/src/aya/AyaPrefs.java b/src/aya/AyaPrefs.java index d6379776..89c796ee 100644 --- a/src/aya/AyaPrefs.java +++ b/src/aya/AyaPrefs.java @@ -22,25 +22,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; } diff --git a/src/aya/StaticData.java b/src/aya/StaticData.java index 8ed0e323..586c2343 100644 --- a/src/aya/StaticData.java +++ b/src/aya/StaticData.java @@ -1,7 +1,5 @@ package aya; -import java.util.ArrayList; - import aya.ext.color.ColorInstructionStore; import aya.ext.date.DateInstructionStore; import aya.ext.debug.DebugInstructionStore; @@ -26,6 +24,19 @@ import aya.parser.SpecialNumberParser; import aya.util.StringSearch; +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.ServiceLoader; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + public class StaticData { public static final boolean DEBUG = true; @@ -34,64 +45,59 @@ public class StaticData { public static final boolean PRINT_LARGE_ERRORS = true; public static final String QUIT = "\\Q"; - + public static final AyaStdIO IO = new AyaStdIO(System.out, System.err, System.in); - + // // 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 _namedInstructionStores; - - + private final Map _namedInstructions = new HashMap<>(); + + private StaticData() { _helpData = null; // initHelpData will create - _namedInstructionStores = new ArrayList(); } - + public static StaticData getInstance() { if (_instance == null) { _instance = new StaticData(); } return _instance; } - + public void init() { initHelpData(); initNamedInstructions(); } - - + + /////////////// // 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 searchList = new ArrayList(); @@ -103,12 +109,12 @@ private void initHelpData() { _helpData = new StringSearch(searchList); } } - + public StringSearch getHelpData() { initHelpData(); return _helpData; } - + public void addHelpText(String in) { getHelpData().addUnique(in); } @@ -116,7 +122,7 @@ public void addHelpText(String in) { public String[] getQuickSearchData() { return getHelpData().getAllItems(); } - + /* This function does nothing but force java to load * the operators and call the static blocks */ @@ -131,35 +137,72 @@ 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()); - - for (NamedInstructionStore x : _namedInstructionStores) { - x.initHelpData(this); + 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()); + + /* + Unfortunately, classpath wildcards are not supported in MANIFEST files. https://docs.oracle.com/javase/7/docs/technotes/tools/windows/classpath.html + So the user-libraries need to be loaded manually. + */ + try { + File libsDir = new File(AyaPrefs.getAyaRootDirectory(), "libs"); + final URL[] libUrls; + try (Stream libPaths = Files.list(libsDir.toPath())) { + libUrls = libPaths.map(path -> { + try { + return path.toUri().toURL(); + } catch (Exception e) { + return null; + } + }).filter(Objects::nonNull).toArray(URL[]::new); + } + + try (URLClassLoader libClassLoader = new URLClassLoader(libUrls)) { + StreamSupport.stream( + ServiceLoader.load(NamedInstructionStore.class, libClassLoader).spliterator(), + false + ).forEach(store -> { + IO.out().println("found store: " + store.getClass().getName()); + addNamedInstructionStore(store); + }); + } + } catch (Exception e) { + IO.err().println("Failed to load libraries due to exception: " + e.getMessage()); + e.printStackTrace(IO.err()); } - } - public NamedOperator getNamedInstruction(String name) { - for (NamedInstructionStore x : _namedInstructionStores) { - NamedOperator i = x.getInstruction(name); - if (i != null) { - return i; + + private 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); + } } diff --git a/src/aya/ext/color/ColorInstructionStore.java b/src/aya/ext/color/ColorInstructionStore.java index 34cd300f..aa03bd0d 100644 --- a/src/aya/ext/color/ColorInstructionStore.java +++ b/src/aya/ext/color/ColorInstructionStore.java @@ -1,50 +1,52 @@ package aya.ext.color; import java.awt.Color; +import java.util.Arrays; +import java.util.Collection; import aya.eval.BlockEvaluator; import aya.exceptions.runtime.ValueError; -import aya.instruction.named.NamedOperator; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; import aya.obj.dict.Dict; import aya.obj.list.List; import aya.obj.number.Num; import aya.obj.symbol.SymbolConstants; import aya.util.ColorFactory; -public class ColorInstructionStore extends NamedInstructionStore { - - @Override - protected void init() { +public class ColorInstructionStore implements NamedInstructionStore { - addInstruction(new NamedOperator("color.fromstr", "color::str: convert an html valid color to an rgba dict") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - String color_str = blockEvaluator.pop().str(); - try { - Color color = ColorFactory.web(color_str); - Dict d = new Dict(); - d.set(SymbolConstants.R, Num.fromInt(color.getRed())); - d.set(SymbolConstants.G, Num.fromInt(color.getGreen())); - d.set(SymbolConstants.B, Num.fromInt(color.getBlue())); - d.set(SymbolConstants.A, Num.fromInt(color.getAlpha())); - blockEvaluator.push(d); - } catch (IllegalArgumentException e) { - throw new ValueError(":{color.fromstr} Invalid color: '" + color_str + "'"); - } - } - }); + @Override + public Collection getNamedInstructions() { + return Arrays.asList( + new NamedOperator("color.fromstr", "color::str: convert an html valid color to an rgba dict") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + String color_str = blockEvaluator.pop().str(); + try { + Color color = ColorFactory.web(color_str); + Dict d = new Dict(); + d.set(SymbolConstants.R, Num.fromInt(color.getRed())); + d.set(SymbolConstants.G, Num.fromInt(color.getGreen())); + d.set(SymbolConstants.B, Num.fromInt(color.getBlue())); + d.set(SymbolConstants.A, Num.fromInt(color.getAlpha())); + blockEvaluator.push(d); + } catch (IllegalArgumentException e) { + throw new ValueError(":{color.fromstr} Invalid color: '" + color_str + "'"); + } + } + }, - addInstruction(new NamedOperator("color.name_list", "return a list of all named colors") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - List named_colors = new List(); - for (String s : ColorFactory.listNamedColors()) { - named_colors.mutAdd(List.fromString(s)); + new NamedOperator("color.name_list", "return a list of all named colors") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + List named_colors = new List(); + for (String s : ColorFactory.listNamedColors()) { + named_colors.mutAdd(List.fromString(s)); + } + blockEvaluator.push(named_colors); + } } - blockEvaluator.push(named_colors); - } - }); - + ); } } diff --git a/src/aya/ext/date/DateInstructionStore.java b/src/aya/ext/date/DateInstructionStore.java index fe8780e8..7888bad2 100644 --- a/src/aya/ext/date/DateInstructionStore.java +++ b/src/aya/ext/date/DateInstructionStore.java @@ -1,13 +1,19 @@ package aya.ext.date; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; + +import java.util.Arrays; +import java.util.Collection; + +public class DateInstructionStore implements NamedInstructionStore { -public class DateInstructionStore extends NamedInstructionStore { - @Override - protected void init() { - addInstruction(new ParseDateInstruction()); - addInstruction(new FormatDateInstruction()); - addInstruction(new DescribeDateInstruction()); + public Collection getNamedInstructions() { + return Arrays.asList( + new ParseDateInstruction(), + new FormatDateInstruction(), + new DescribeDateInstruction() + ); } } diff --git a/src/aya/ext/debug/DebugInstructionStore.java b/src/aya/ext/debug/DebugInstructionStore.java index 6e23c5ee..c69239e5 100644 --- a/src/aya/ext/debug/DebugInstructionStore.java +++ b/src/aya/ext/debug/DebugInstructionStore.java @@ -1,12 +1,17 @@ package aya.ext.debug; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; + +import java.util.Arrays; +import java.util.Collection; + +public class DebugInstructionStore implements NamedInstructionStore { -public class DebugInstructionStore extends NamedInstructionStore { - @Override - protected void init() { - // JSON - addInstruction(new PauseDebugInstruction()); + public Collection getNamedInstructions() { + return Arrays.asList( + new PauseDebugInstruction() + ); } } diff --git a/src/aya/ext/dialog/DialogInstructionStore.java b/src/aya/ext/dialog/DialogInstructionStore.java index d5da7b21..0ef19ef4 100644 --- a/src/aya/ext/dialog/DialogInstructionStore.java +++ b/src/aya/ext/dialog/DialogInstructionStore.java @@ -7,144 +7,149 @@ import aya.eval.BlockEvaluator; import aya.exceptions.runtime.TypeError; import aya.exceptions.runtime.ValueError; -import aya.instruction.named.NamedOperator; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; import aya.obj.Obj; import aya.obj.list.List; import aya.obj.number.Num; import aya.obj.symbol.Symbol; import aya.obj.symbol.SymbolConstants; -public class DialogInstructionStore extends NamedInstructionStore { - - @Override - protected void init() { - - addInstruction(new NamedOperator("dialog.getstr", "message::str: popup window with a a text input field") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj title = blockEvaluator.pop(); - blockEvaluator.push(List.fromString(QuickDialog.requestString(title.str()))); - } - }); - - addInstruction(new NamedOperator("dialog.getnum", "message::str: popup window with a number input field") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj title = blockEvaluator.pop(); - blockEvaluator.push(QuickDialog.numberInput(title.str())); - } - }); - - addInstruction(new NamedOperator("dialog.alert", "message::str title::str type::sym show an alert dialog") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj type_obj = blockEvaluator.pop(); - final Obj title_obj = blockEvaluator.pop(); - final Obj message_obj = blockEvaluator.pop(); - - if (!type_obj.isa(Obj.SYMBOL)) throw new TypeError(this, "SSJ", type_obj, title_obj, message_obj); - - int type = symToDialogType((Symbol)type_obj); - QuickDialog.alert(message_obj.str(), title_obj.str(), type); - } - }); - - addInstruction(new NamedOperator("dialog.confirm", "message::str options::list title::str type::sym show an alert dialog") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj type_obj = blockEvaluator.pop(); - final Obj title_obj = blockEvaluator.pop(); - final Obj options_obj = blockEvaluator.pop(); - final Obj message_obj = blockEvaluator.pop(); - - if (!(type_obj.isa(Obj.SYMBOL) && options_obj.isa(Obj.LIST))) throw new TypeError(this, "SSJ", type_obj, title_obj, message_obj); - - int type = symToDialogType((Symbol)type_obj); - List options = asList(options_obj); - - if (options.length() != 2) throw new ValueError(":{dialog.confirm} : Expected options list of length 2. Got " + options.repr()); - - boolean val = QuickDialog.confirm( - message_obj.str(), - options.getExact(0).str(), - options.getExact(1).str(), - title_obj.str(), - type); - - blockEvaluator.push(Num.fromBool(val)); - } - }); - - addInstruction(new NamedOperator("dialog.buttons", "message::str options::list title::str type::sym: show a dialog with several option buttons") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj type_obj = blockEvaluator.pop(); - final Obj title_obj = blockEvaluator.pop(); - final Obj options_obj = blockEvaluator.pop(); - final Obj message_obj = blockEvaluator.pop(); - - if (!(type_obj.isa(Obj.SYMBOL) && options_obj.isa(Obj.LIST))) throw new TypeError(this, "SSJ", type_obj, title_obj, message_obj); - - int type = symToDialogType((Symbol)type_obj); - - List options_list = asList(options_obj); - if (options_list.length() <= 0) throw new ValueError(":{dialog.buttons} : Expected non-empty options. Got " + options_list.repr()); - - String[] options = new String[options_list.length()]; - for (int i = 0; i < options_list.length(); i++) { - options[i] = options_list.getExact(i).str(); - } +import java.util.Arrays; +import java.util.Collection; - String selected = QuickDialog.selectOptionButtons( - message_obj.str(), - options, - title_obj.str(), - type); - - blockEvaluator.push(List.fromString(selected)); - } - }); - - addInstruction(new NamedOperator("dialog.dropdown", "message::str options::list title::str type::sym: show a dialog with several options as a dropdown") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj type_obj = blockEvaluator.pop(); - final Obj title_obj = blockEvaluator.pop(); - final Obj options_obj = blockEvaluator.pop(); - final Obj message_obj = blockEvaluator.pop(); - - if (!(type_obj.isa(Obj.SYMBOL) && options_obj.isa(Obj.LIST))) throw new TypeError(this, "SSJ", type_obj, title_obj, message_obj); - - int type = symToDialogType((Symbol)type_obj); - - List options_list = asList(options_obj); - if (options_list.length() <= 0) throw new ValueError(":{dialog.buttons} : Expected non-empty options. Got " + options_list.repr()); - - String[] options = new String[options_list.length()]; - for (int i = 0; i < options_list.length(); i++) { - options[i] = options_list.getExact(i).str(); - } +public class DialogInstructionStore implements NamedInstructionStore { - String selected = QuickDialog.selectOptionDropdown( - message_obj.str(), - options, - title_obj.str(), - type); - - blockEvaluator.push(List.fromString(selected)); - - } - }); - - addInstruction(new NamedOperator("dialog.choosefile") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - blockEvaluator.push(List.fromString(QuickDialog.chooseFile())); - } - }); + @Override + public Collection getNamedInstructions() { + return Arrays.asList( + + new NamedOperator("dialog.getstr", "message::str: popup window with a a text input field") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj title = blockEvaluator.pop(); + blockEvaluator.push(List.fromString(QuickDialog.requestString(title.str()))); + } + }, + + new NamedOperator("dialog.getnum", "message::str: popup window with a number input field") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj title = blockEvaluator.pop(); + blockEvaluator.push(QuickDialog.numberInput(title.str())); + } + }, + + new NamedOperator("dialog.alert", "message::str title::str type::sym show an alert dialog") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj type_obj = blockEvaluator.pop(); + final Obj title_obj = blockEvaluator.pop(); + final Obj message_obj = blockEvaluator.pop(); + + if (!type_obj.isa(Obj.SYMBOL)) throw new TypeError(this, "SSJ", type_obj, title_obj, message_obj); + + int type = symToDialogType((Symbol) type_obj); + QuickDialog.alert(message_obj.str(), title_obj.str(), type); + } + }, + + new NamedOperator("dialog.confirm", "message::str options::list title::str type::sym show an alert dialog") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj type_obj = blockEvaluator.pop(); + final Obj title_obj = blockEvaluator.pop(); + final Obj options_obj = blockEvaluator.pop(); + final Obj message_obj = blockEvaluator.pop(); + + if (!(type_obj.isa(Obj.SYMBOL) && options_obj.isa(Obj.LIST))) throw new TypeError(this, "SSJ", type_obj, title_obj, message_obj); + + int type = symToDialogType((Symbol) type_obj); + List options = asList(options_obj); + + if (options.length() != 2) throw new ValueError(":{dialog.confirm} : Expected options list of length 2. Got " + options.repr()); + + boolean val = QuickDialog.confirm( + message_obj.str(), + options.getExact(0).str(), + options.getExact(1).str(), + title_obj.str(), + type); + + blockEvaluator.push(Num.fromBool(val)); + } + }, + + new NamedOperator("dialog.buttons", "message::str options::list title::str type::sym: show a dialog with several option buttons") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj type_obj = blockEvaluator.pop(); + final Obj title_obj = blockEvaluator.pop(); + final Obj options_obj = blockEvaluator.pop(); + final Obj message_obj = blockEvaluator.pop(); + + if (!(type_obj.isa(Obj.SYMBOL) && options_obj.isa(Obj.LIST))) throw new TypeError(this, "SSJ", type_obj, title_obj, message_obj); + + int type = symToDialogType((Symbol) type_obj); + + List options_list = asList(options_obj); + if (options_list.length() <= 0) throw new ValueError(":{dialog.buttons} : Expected non-empty options. Got " + options_list.repr()); + + String[] options = new String[options_list.length()]; + for (int i = 0; i < options_list.length(); i++) { + options[i] = options_list.getExact(i).str(); + } + + String selected = QuickDialog.selectOptionButtons( + message_obj.str(), + options, + title_obj.str(), + type); + + blockEvaluator.push(List.fromString(selected)); + } + }, + + new NamedOperator("dialog.dropdown", "message::str options::list title::str type::sym: show a dialog with several options as a dropdown") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj type_obj = blockEvaluator.pop(); + final Obj title_obj = blockEvaluator.pop(); + final Obj options_obj = blockEvaluator.pop(); + final Obj message_obj = blockEvaluator.pop(); + + if (!(type_obj.isa(Obj.SYMBOL) && options_obj.isa(Obj.LIST))) throw new TypeError(this, "SSJ", type_obj, title_obj, message_obj); + + int type = symToDialogType((Symbol) type_obj); + + List options_list = asList(options_obj); + if (options_list.length() <= 0) throw new ValueError(":{dialog.buttons} : Expected non-empty options. Got " + options_list.repr()); + + String[] options = new String[options_list.length()]; + for (int i = 0; i < options_list.length(); i++) { + options[i] = options_list.getExact(i).str(); + } + + String selected = QuickDialog.selectOptionDropdown( + message_obj.str(), + options, + title_obj.str(), + type); + + blockEvaluator.push(List.fromString(selected)); + + } + }, + + new NamedOperator("dialog.choosefile") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + blockEvaluator.push(List.fromString(QuickDialog.chooseFile())); + } + } + ); } - + private int symToDialogType(Symbol sym) { final long id = sym.id(); if (id == SymbolConstants.PLAIN.id()) return JOptionPane.PLAIN_MESSAGE; diff --git a/src/aya/ext/fstream/FStreamInstructionStore.java b/src/aya/ext/fstream/FStreamInstructionStore.java index 3c3e9895..46d16980 100644 --- a/src/aya/ext/fstream/FStreamInstructionStore.java +++ b/src/aya/ext/fstream/FStreamInstructionStore.java @@ -1,6 +1,8 @@ package aya.ext.fstream; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import aya.eval.BlockEvaluator; import aya.exceptions.runtime.IOError; @@ -10,33 +12,36 @@ import aya.obj.list.numberlist.DoubleList; import aya.util.FileUtils; -public class FStreamInstructionStore extends NamedInstructionStore { - +public class FStreamInstructionStore implements NamedInstructionStore { + @Override - protected void init() { - // Legacy Stream Instructions - // TODO: Separate into individual instructions - addInstruction(new LegacyFStreamInstruction()); - - // - // Utility Instructions - // - - addInstruction(new NamedOperator("fileutils.readallbytes") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - String path = blockEvaluator.pop().str(); - try { - byte[] bytes = FileUtils.readAllBytes(FileUtils.resolveFile(path)); - double[] doubles = new double[bytes.length]; - for (int i = 0; i < bytes.length; i++) { - doubles[i] = bytes[i]; + public Collection getNamedInstructions() { + return Arrays.asList( + + // Legacy Stream Instructions + // TODO: Separate into individual instructions + new LegacyFStreamInstruction(), + + // + // Utility Instructions + // + + new NamedOperator("fileutils.readallbytes") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + String path = blockEvaluator.pop().str(); + try { + byte[] bytes = FileUtils.readAllBytes(FileUtils.resolveFile(path)); + double[] doubles = new double[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + doubles[i] = bytes[i]; + } + blockEvaluator.push(new List(new DoubleList(doubles))); + } catch (IOException e) { + throw new IOError("{fstream.readallbytes}", path, e); + } } - blockEvaluator.push(new List(new DoubleList(doubles))); - } catch (IOException e) { - throw new IOError("{fstream.readallbytes}", path, e); } - } - }); + ); } } diff --git a/src/aya/ext/graphics/GraphicsInstructionStore.java b/src/aya/ext/graphics/GraphicsInstructionStore.java index 5593b2e2..3b505319 100644 --- a/src/aya/ext/graphics/GraphicsInstructionStore.java +++ b/src/aya/ext/graphics/GraphicsInstructionStore.java @@ -37,48 +37,54 @@ import aya.ext.graphics.instruction.keyboard.PressedKeysInstruction; import aya.ext.graphics.instruction.keyboard.TypedCharsInstruction; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; -public class GraphicsInstructionStore extends NamedInstructionStore { +import java.util.Arrays; +import java.util.Collection; + +public class GraphicsInstructionStore implements NamedInstructionStore { public static final CanvasTable canvas_table = new CanvasTable(); @Override - protected void init() { - addInstruction(new ArcGraphicsInstruction(canvas_table)); - addInstruction(new ClearGraphicsInstruction(canvas_table)); - addInstruction(new ClearRectGraphicsInstruction(canvas_table)); - addInstruction(new ClickEventsInstruction(canvas_table)); - addInstruction(new CloseGraphicsInstruction(canvas_table)); - addInstruction(new CopyRectGraphicsInstruction(canvas_table)); - addInstruction(new EllipseGraphicsInstruction(canvas_table)); - addInstruction(new GetPixelsGraphicsInstruction(canvas_table)); - addInstruction(new IsOpenGraphicsInstruction(canvas_table)); - addInstruction(new LineGraphicsInstruction(canvas_table)); - addInstruction(new ListFontsGraphicsInstruction(canvas_table)); - addInstruction(new MoveEventsInstruction(canvas_table)); - addInstruction(new NewGraphicsInstruction(canvas_table)); - addInstruction(new OvalGraphicsInstruction(canvas_table)); - addInstruction(new PathGraphicsInstruction(canvas_table)); - addInstruction(new PressedKeysInstruction(canvas_table)); - addInstruction(new PressedButtonsInstruction(canvas_table)); - addInstruction(new RectGraphicsInstruction(canvas_table)); - addInstruction(new RotateGraphicsInstruction(canvas_table)); - addInstruction(new RoundRectGraphicsInstruction(canvas_table)); - addInstruction(new SaveGraphicsInstruction(canvas_table)); - addInstruction(new ScaleGraphicsInstruction(canvas_table)); - addInstruction(new SetAlphaGraphicsInstruction(canvas_table)); - addInstruction(new SetBGColorGraphicsInstruction(canvas_table)); - addInstruction(new SetColorGraphicsInstruction(canvas_table)); - addInstruction(new SetColorAlphaGraphicsInstruction(canvas_table)); - addInstruction(new SetFontGraphicsInstruction(canvas_table)); - addInstruction(new SetPaintGradGraphicsInstruction(canvas_table)); - addInstruction(new SetStrokeGraphicsInstruction(canvas_table)); - addInstruction(new SetStrokeWidthGraphicsInstruction(canvas_table)); - addInstruction(new ShearGraphicsInstruction(canvas_table)); - addInstruction(new ShowGraphicsInstruction(canvas_table)); - addInstruction(new TextGraphicsInstruction(canvas_table)); - addInstruction(new TranslateGraphicsInstruction(canvas_table)); - addInstruction(new TypedCharsInstruction(canvas_table)); - addInstruction(new ViewmatGraphicsInstruction(canvas_table)); + public Collection getNamedInstructions() { + return Arrays.asList( + new ArcGraphicsInstruction(canvas_table), + new ClearGraphicsInstruction(canvas_table), + new ClearRectGraphicsInstruction(canvas_table), + new ClickEventsInstruction(canvas_table), + new CloseGraphicsInstruction(canvas_table), + new CopyRectGraphicsInstruction(canvas_table), + new EllipseGraphicsInstruction(canvas_table), + new GetPixelsGraphicsInstruction(canvas_table), + new IsOpenGraphicsInstruction(canvas_table), + new LineGraphicsInstruction(canvas_table), + new ListFontsGraphicsInstruction(canvas_table), + new MoveEventsInstruction(canvas_table), + new NewGraphicsInstruction(canvas_table), + new OvalGraphicsInstruction(canvas_table), + new PathGraphicsInstruction(canvas_table), + new PressedKeysInstruction(canvas_table), + new PressedButtonsInstruction(canvas_table), + new RectGraphicsInstruction(canvas_table), + new RotateGraphicsInstruction(canvas_table), + new RoundRectGraphicsInstruction(canvas_table), + new SaveGraphicsInstruction(canvas_table), + new ScaleGraphicsInstruction(canvas_table), + new SetAlphaGraphicsInstruction(canvas_table), + new SetBGColorGraphicsInstruction(canvas_table), + new SetColorGraphicsInstruction(canvas_table), + new SetColorAlphaGraphicsInstruction(canvas_table), + new SetFontGraphicsInstruction(canvas_table), + new SetPaintGradGraphicsInstruction(canvas_table), + new SetStrokeGraphicsInstruction(canvas_table), + new SetStrokeWidthGraphicsInstruction(canvas_table), + new ShearGraphicsInstruction(canvas_table), + new ShowGraphicsInstruction(canvas_table), + new TextGraphicsInstruction(canvas_table), + new TranslateGraphicsInstruction(canvas_table), + new TypedCharsInstruction(canvas_table), + new ViewmatGraphicsInstruction(canvas_table) + ); } } diff --git a/src/aya/ext/image/ImageInstructionStore.java b/src/aya/ext/image/ImageInstructionStore.java index b9df3a4d..8b199254 100644 --- a/src/aya/ext/image/ImageInstructionStore.java +++ b/src/aya/ext/image/ImageInstructionStore.java @@ -1,12 +1,18 @@ package aya.ext.image; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; + +import java.util.Arrays; +import java.util.Collection; + +public class ImageInstructionStore implements NamedInstructionStore { -public class ImageInstructionStore extends NamedInstructionStore { - @Override - protected void init() { - addInstruction(new ReadImageInstruction()); - addInstruction(new WriteImageInstruction()); + public Collection getNamedInstructions() { + return Arrays.asList( + new ReadImageInstruction(), + new WriteImageInstruction() + ); } } diff --git a/src/aya/ext/json/JSONInstructionStore.java b/src/aya/ext/json/JSONInstructionStore.java index cefd4eef..266b983e 100644 --- a/src/aya/ext/json/JSONInstructionStore.java +++ b/src/aya/ext/json/JSONInstructionStore.java @@ -1,13 +1,18 @@ package aya.ext.json; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; + +import java.util.Arrays; +import java.util.Collection; + +public class JSONInstructionStore implements NamedInstructionStore { -public class JSONInstructionStore extends NamedInstructionStore { - @Override - protected void init() { - // JSON - addInstruction(new ToJSONInstruction()); - addInstruction(new LoadJSONInstruction()); + public Collection getNamedInstructions() { + return Arrays.asList( + new ToJSONInstruction(), + new LoadJSONInstruction() + ); } } diff --git a/src/aya/ext/la/LinearAlgebraInstructionStore.java b/src/aya/ext/la/LinearAlgebraInstructionStore.java index 30924f40..6d039679 100644 --- a/src/aya/ext/la/LinearAlgebraInstructionStore.java +++ b/src/aya/ext/la/LinearAlgebraInstructionStore.java @@ -1,11 +1,17 @@ package aya.ext.la; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; + +import java.util.Arrays; +import java.util.Collection; + +public class LinearAlgebraInstructionStore implements NamedInstructionStore { -public class LinearAlgebraInstructionStore extends NamedInstructionStore { - @Override - protected void init() { - addInstruction(new MatMulInstruction()); + public Collection getNamedInstructions() { + return Arrays.asList( + new MatMulInstruction() + ); } } diff --git a/src/aya/ext/plot/PlotInstructionStore.java b/src/aya/ext/plot/PlotInstructionStore.java index 3c4e02f9..8804324c 100644 --- a/src/aya/ext/plot/PlotInstructionStore.java +++ b/src/aya/ext/plot/PlotInstructionStore.java @@ -5,14 +5,20 @@ import aya.ext.plot.instruction.PieChartInstruction; import aya.ext.plot.instruction.PlotInstruction; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; -public class PlotInstructionStore extends NamedInstructionStore { +import java.util.Arrays; +import java.util.Collection; - @Override - protected void init() { - addInstruction(new PlotInstruction()); - addInstruction(new MultiPlotInstruction()); - addInstruction(new BoxPlotInstruction()); - addInstruction(new PieChartInstruction()); - } +public class PlotInstructionStore implements NamedInstructionStore { + + @Override + public Collection getNamedInstructions() { + return Arrays.asList( + new PlotInstruction(), + new MultiPlotInstruction(), + new BoxPlotInstruction(), + new PieChartInstruction() + ); + } } diff --git a/src/aya/ext/socket/SocketInstructionStore.java b/src/aya/ext/socket/SocketInstructionStore.java index 28012258..a309c7b0 100644 --- a/src/aya/ext/socket/SocketInstructionStore.java +++ b/src/aya/ext/socket/SocketInstructionStore.java @@ -1,138 +1,142 @@ package aya.ext.socket; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import aya.eval.BlockEvaluator; import aya.exceptions.runtime.IOError; import aya.exceptions.runtime.TypeError; -import aya.instruction.named.NamedOperator; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; import aya.obj.Obj; import aya.obj.list.List; import aya.obj.number.Num; import aya.util.Casting; -public class SocketInstructionStore extends NamedInstructionStore { - +public class SocketInstructionStore implements NamedInstructionStore { + @Override - protected void init() { + public Collection getNamedInstructions() { SocketManager socket_manager = new SocketManager(); - - addInstruction(new NamedOperator("socket.open_server", "ip::str port::int: Open a socket server") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj obj_port = blockEvaluator.pop(); - final Obj obj_ip = blockEvaluator.pop(); - if (obj_port.isa(Obj.NUM)) { - int port = Casting.asNumber(obj_port).toInt(); - String ip = obj_ip.str(); - int result = SocketManager.NULL_ID; - - try { - result = socket_manager.openServer(ip, port); - } catch (IOException e) { - result = SocketManager.NULL_ID; - throw new IOError(opName(), ip + ":" + port, e); + + return Arrays.asList( + new NamedOperator("socket.open_server", "ip::str port::int: Open a socket server") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj obj_port = blockEvaluator.pop(); + final Obj obj_ip = blockEvaluator.pop(); + if (obj_port.isa(Obj.NUM)) { + int port = Casting.asNumber(obj_port).toInt(); + String ip = obj_ip.str(); + int result = SocketManager.NULL_ID; + + try { + result = socket_manager.openServer(ip, port); + } catch (IOException e) { + result = SocketManager.NULL_ID; + throw new IOError(opName(), ip + ":" + port, e); + } + + blockEvaluator.push(Num.fromInt(result)); + } else { + throw new TypeError(this, "SN"); + } } - - blockEvaluator.push(Num.fromInt(result)); - } else { - throw new TypeError(this, "SN"); - } - } - }); - - - addInstruction(new NamedOperator("socket.accept", "server_id::int: Open a connection on the server") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - int id = getSingleIntArg(this, blockEvaluator); - int sock_id = socket_manager.accept(id); - blockEvaluator.push(Num.fromInt(sock_id)); - } - }); - - addInstruction(new NamedOperator("socket.open_client", "ip::str port::int: Open a socket client") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj obj_port = blockEvaluator.pop(); - final Obj obj_ip = blockEvaluator.pop(); - if (obj_port.isa(Obj.NUM)) { - int port = Casting.asNumber(obj_port).toInt(); - String ip = obj_ip.str(); - try { - int result = socket_manager.openClient(ip, port); - blockEvaluator.push(Num.fromInt(result)); - } catch (IOException e) { - throw new IOError(opName(), ip + ":" + port, e); + }, + + + new NamedOperator("socket.accept", "server_id::int: Open a connection on the server") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + int id = getSingleIntArg(this, blockEvaluator); + int sock_id = socket_manager.accept(id); + blockEvaluator.push(Num.fromInt(sock_id)); } - - } else { - throw new TypeError(this, "SN"); - } - } - }); - - - addInstruction(new NamedOperator("socket.send", "data::str id::int: Send data on a socket") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj obj_id = blockEvaluator.pop(); - final Obj obj_data = blockEvaluator.pop(); - if (obj_id.isa(Obj.NUM)) { - int id = Casting.asNumber(obj_id).toInt(); - AyaSocket sock = socket_manager.getSocket(id); - String data = obj_data.str(); - try { - sock.send(data); - } catch (IOException e) { - throw new IOError(opName(), sock.getAddr() + ":" + sock.getPort(), e); + }, + + new NamedOperator("socket.open_client", "ip::str port::int: Open a socket client") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj obj_port = blockEvaluator.pop(); + final Obj obj_ip = blockEvaluator.pop(); + if (obj_port.isa(Obj.NUM)) { + int port = Casting.asNumber(obj_port).toInt(); + String ip = obj_ip.str(); + try { + int result = socket_manager.openClient(ip, port); + blockEvaluator.push(Num.fromInt(result)); + } catch (IOException e) { + throw new IOError(opName(), ip + ":" + port, e); + } + + } else { + throw new TypeError(this, "SN"); + } + } + }, + + + new NamedOperator("socket.send", "data::str id::int: Send data on a socket") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj obj_id = blockEvaluator.pop(); + final Obj obj_data = blockEvaluator.pop(); + if (obj_id.isa(Obj.NUM)) { + int id = Casting.asNumber(obj_id).toInt(); + AyaSocket sock = socket_manager.getSocket(id); + String data = obj_data.str(); + try { + sock.send(data); + } catch (IOException e) { + throw new IOError(opName(), sock.getAddr() + ":" + sock.getPort(), e); + } + } else { + throw new TypeError(this, "SN"); + } + } + }, + + + new NamedOperator("socket.close", "id::num: Close a socket or server") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + int id = getSingleIntArg(this, blockEvaluator); + // May be either a socket or a server + socket_manager.closeSocket(id); + socket_manager.closeSocketServer(id); + } + }, + + + new NamedOperator("socket.recv", "id::num: Read from a socket") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + int id = getSingleIntArg(this, blockEvaluator); + AyaSocket sock = socket_manager.getSocket(id); + blockEvaluator.push(List.fromString(sock.recv())); + } + }, + + new NamedOperator("socket.get_addr", "id::num: Get the socket's connection addr") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + int id = getSingleIntArg(this, blockEvaluator); + blockEvaluator.push(List.fromString(socket_manager.getIP(id).toString())); + } + }, + + new NamedOperator("socket.get_port", "id::num: Get the socket's connection port") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + int id = getSingleIntArg(this, blockEvaluator); + blockEvaluator.push(Num.fromInt(socket_manager.getPort(id))); } - } else { - throw new TypeError(this, "SN"); } - } - }); - - - addInstruction(new NamedOperator("socket.close", "id::num: Close a socket or server") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - int id = getSingleIntArg(this, blockEvaluator); - // May be either a socket or a server - socket_manager.closeSocket(id); - socket_manager.closeSocketServer(id); - } - }); - - - addInstruction(new NamedOperator("socket.recv", "id::num: Read from a socket") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - int id = getSingleIntArg(this, blockEvaluator); - AyaSocket sock = socket_manager.getSocket(id); - blockEvaluator.push(List.fromString(sock.recv())); - } - }); - - addInstruction(new NamedOperator("socket.get_addr", "id::num: Get the socket's connection addr") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - int id = getSingleIntArg(this, blockEvaluator); - blockEvaluator.push(List.fromString(socket_manager.getIP(id).toString())); - } - }); - - addInstruction(new NamedOperator("socket.get_port", "id::num: Get the socket's connection port") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - int id = getSingleIntArg(this, blockEvaluator); - blockEvaluator.push(Num.fromInt(socket_manager.getPort(id))); - } - }); + ); } - + private static int getSingleIntArg(NamedOperator i, BlockEvaluator blockEvaluator) { final Obj obj_id = blockEvaluator.pop(); if (obj_id.isa(Obj.NUM)) { diff --git a/src/aya/ext/sys/SystemInstructionStore.java b/src/aya/ext/sys/SystemInstructionStore.java index 42fca420..3bc32f06 100644 --- a/src/aya/ext/sys/SystemInstructionStore.java +++ b/src/aya/ext/sys/SystemInstructionStore.java @@ -2,183 +2,187 @@ import java.io.File; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import aya.AyaPrefs; import aya.eval.BlockEvaluator; import aya.exceptions.runtime.ValueError; -import aya.instruction.named.NamedOperator; import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; import aya.obj.Obj; import aya.obj.list.List; import aya.obj.number.Num; import aya.util.FileUtils; -public class SystemInstructionStore extends NamedInstructionStore { - +public class SystemInstructionStore implements NamedInstructionStore { + @Override - protected void init() { - // Exec - addInstruction(new SysExecInstruction()); - - // Readdir - addInstruction(new NamedOperator("sys.readdir", "list files in working dir") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj arg = blockEvaluator.pop(); - - if (arg.isa(Obj.STR)) { - String fstr = arg.str(); - try { - ArrayList dirs = AyaPrefs.listFilesAndDirsForFolder(FileUtils.resolveFile(fstr)); - ArrayList obj_dirs = new ArrayList(dirs.size()); - for (String s : dirs) { - obj_dirs.add(List.fromString(s)); + public Collection getNamedInstructions() { + return Arrays.asList( + // Exec + new SysExecInstruction(), + + // Readdir + new NamedOperator("sys.readdir", "list files in working dir") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj arg = blockEvaluator.pop(); + + if (arg.isa(Obj.STR)) { + String fstr = arg.str(); + try { + ArrayList dirs = AyaPrefs.listFilesAndDirsForFolder(FileUtils.resolveFile(fstr)); + ArrayList obj_dirs = new ArrayList(dirs.size()); + for (String s : dirs) { + obj_dirs.add(List.fromString(s)); + } + blockEvaluator.push(new List(obj_dirs)); + } catch (NullPointerException e) { + throw new ValueError(":{sys.readdir} : arg is not a valid location. Received:\n'" + fstr + "'"); + } + } else { + throw new ValueError(":{sys.readdir} : arg must be a string. Received:\n" + arg.repr()); } - blockEvaluator.push(new List(obj_dirs)); - } catch (NullPointerException e) { - throw new ValueError(":{sys.readdir} : arg is not a valid location. Received:\n'" + fstr + "'"); } - } else { - throw new ValueError(":{sys.readdir} : arg must be a string. Received:\n" + arg.repr()); - } - } - }); - - // Get working dir - addInstruction(new NamedOperator("sys.wd", "get absolute path of working dir") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - blockEvaluator.push(List.fromString(AyaPrefs.getWorkingDir())); - } - }); - - // Get aya dir - addInstruction(new NamedOperator("sys.ad", "get absolute path of aya dir") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - blockEvaluator.push(List.fromString(AyaPrefs.getAyaDir())); - } - }); - - // Get aya dir - addInstruction(new NamedOperator("sys.set_ad", "set absolute path of aya dir") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - AyaPrefs.setAyaDir(blockEvaluator.pop().str()); - } - }); - - // Set working dir - addInstruction(new NamedOperator("sys.cd", "set the working dir (empy string resets to default)") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj arg = blockEvaluator.pop(); - - if (arg.isa(Obj.STR)) { - String dir = arg.str(); - if(dir.equals("")) { - AyaPrefs.resetWorkingDir(); - } else { - if (!AyaPrefs.setWorkingDir(arg.str())) { - throw new ValueError(":{sys.cd} : arg is not a valid path." - + " Did you include a '/' or '\' at the end? Received:\n" + arg.repr()); + }, + + // Get working dir + new NamedOperator("sys.wd", "get absolute path of working dir") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + blockEvaluator.push(List.fromString(AyaPrefs.getWorkingDir())); + } + }, + + // Get aya dir + new NamedOperator("sys.ad", "get absolute path of aya dir") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + blockEvaluator.push(List.fromString(AyaPrefs.getAyaDir())); + } + }, + + // Get aya dir + new NamedOperator("sys.set_ad", "set absolute path of aya dir") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + AyaPrefs.setAyaDir(blockEvaluator.pop().str()); + } + }, + + // Set working dir + new NamedOperator("sys.cd", "set the working dir (empy string resets to default)") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj arg = blockEvaluator.pop(); + + if (arg.isa(Obj.STR)) { + String dir = arg.str(); + if (dir.equals("")) { + AyaPrefs.resetWorkingDir(); + } else { + if (!AyaPrefs.setWorkingDir(arg.str())) { + throw new ValueError(":{sys.cd} : arg is not a valid path." + + " Did you include a '/' or '\' at the end? Received:\n" + arg.repr()); + } + } + } else { + throw new ValueError(":{sys.cd} : arg must be a string. Received:\n" + arg.repr()); } } - } else { - throw new ValueError(":{sys.cd} : arg must be a string. Received:\n" + arg.repr()); - } - } - }); - - // Make dir - addInstruction(new NamedOperator("sys.mkdir", "create a directory") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj arg = blockEvaluator.pop(); - - if(arg.isa(Obj.STR)) { - String fstr = arg.str(); - if(!AyaPrefs.mkDir(fstr)) { - throw new ValueError(":{sys.mkdir} : arg must be a valid name. Received:\n" + fstr); + }, + + // Make dir + new NamedOperator("sys.mkdir", "create a directory") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj arg = blockEvaluator.pop(); + + if (arg.isa(Obj.STR)) { + String fstr = arg.str(); + if (!AyaPrefs.mkDir(fstr)) { + throw new ValueError(":{sys.mkdir} : arg must be a valid name. Received:\n" + fstr); + } + } else { + throw new ValueError(":{sys.mkdir} : arg must be a string. Received:\n" + arg.repr()); + } } - } else { - throw new ValueError(":{sys.mkdir} : arg must be a string. Received:\n" + arg.repr()); - } - } - }); - - - // System.getProperty - addInstruction(new NamedOperator("sys.getprop", "call System.getProperty with the given key") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj arg = blockEvaluator.pop(); - String val = System.getProperty(arg.str()); - if (val == null) { - blockEvaluator.push(List.fromString("")); - } else { - blockEvaluator.push(List.fromString(val)); - } - } - }); - - // Delete file or directory - addInstruction(new NamedOperator("sys.rm", "remove a file or directory") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj arg = blockEvaluator.pop(); - - if(arg.isa(Obj.STR)) { - String arg_str = arg.str(); - if(arg_str.equals("")) { - throw new ValueError(":{sys.rm} : arg must be a valid name. Received:\n" + arg_str); + }, + + + // System.getProperty + new NamedOperator("sys.getprop", "call System.getProperty with the given key") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj arg = blockEvaluator.pop(); + String val = System.getProperty(arg.str()); + if (val == null) { + blockEvaluator.push(List.fromString("")); + } else { + blockEvaluator.push(List.fromString(val)); + } } - File delFile = FileUtils.resolveFile(arg.str()); - if(!AyaPrefs.deleteFile(delFile)) { - throw new ValueError(":{sys.rm} : arg must be a valid name. Received:\n" + delFile.getAbsolutePath()); + }, + + // Delete file or directory + new NamedOperator("sys.rm", "remove a file or directory") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj arg = blockEvaluator.pop(); + + if (arg.isa(Obj.STR)) { + String arg_str = arg.str(); + if (arg_str.equals("")) { + throw new ValueError(":{sys.rm} : arg must be a valid name. Received:\n" + arg_str); + } + File delFile = FileUtils.resolveFile(arg.str()); + if (!AyaPrefs.deleteFile(delFile)) { + throw new ValueError(":{sys.rm} : arg must be a valid name. Received:\n" + delFile.getAbsolutePath()); + } + } else { + throw new ValueError(":{sys.rm} : arg must be a string. Received:\n" + arg.repr()); + } + } + }, + + // Test if file exists + new NamedOperator("sys.file_exists", "test if the file exists") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj arg = blockEvaluator.pop(); + blockEvaluator.push(FileUtils.isFile(arg.str()) ? Num.ONE : Num.ZERO); + } + }, + + // Resolve home (replace ~/ with /path/to/home) + new NamedOperator("sys.resolvehome", "replace ~/.. with /path/to/home/..") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj arg = blockEvaluator.pop(); + blockEvaluator.push(List.fromString(FileUtils.resolveHome(arg.str()))); + } + }, + + // Change the prompt text + new NamedOperator("sys.alterprompt", "change the prompt text") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + final Obj arg = blockEvaluator.pop(); + AyaPrefs.setPrompt(arg.str()); + } + }, + + new NamedOperator("sys.args", "CLI args") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + List args = new List(); + for (String a : AyaPrefs.getArgs()) { + args.mutAdd(List.fromString(a)); + } + blockEvaluator.push(args); } - } else { - throw new ValueError(":{sys.rm} : arg must be a string. Received:\n" + arg.repr()); - } - } - }); - - // Test if file exists - addInstruction(new NamedOperator("sys.file_exists", "test if the file exists") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj arg = blockEvaluator.pop(); - blockEvaluator.push(FileUtils.isFile(arg.str()) ? Num.ONE : Num.ZERO); - } - }); - - // Resolve home (replace ~/ with /path/to/home) - addInstruction(new NamedOperator("sys.resolvehome", "replace ~/.. with /path/to/home/..") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj arg = blockEvaluator.pop(); - blockEvaluator.push(List.fromString(FileUtils.resolveHome(arg.str()))); - } - }); - - // Change the prompt text - addInstruction(new NamedOperator("sys.alterprompt", "change the prompt text") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - final Obj arg = blockEvaluator.pop(); - AyaPrefs.setPrompt(arg.str()); - } - }); - - addInstruction(new NamedOperator("sys.args", "CLI args") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - List args = new List(); - for (String a : AyaPrefs.getArgs()) { - args.mutAdd(List.fromString(a)); } - blockEvaluator.push(args); - } - }); + ); } } diff --git a/src/aya/ext/thread/ThreadInstructionStore.java b/src/aya/ext/thread/ThreadInstructionStore.java index 15490cb9..81a851a4 100644 --- a/src/aya/ext/thread/ThreadInstructionStore.java +++ b/src/aya/ext/thread/ThreadInstructionStore.java @@ -1,6 +1,8 @@ package aya.ext.thread; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import aya.AyaThread; @@ -21,17 +23,17 @@ import aya.util.Casting; import aya.util.Pair; -public class ThreadInstructionStore extends NamedInstructionStore { - +public class ThreadInstructionStore implements NamedInstructionStore { + HashMap _thread_table; volatile int _thread_counter; volatile int _request_id_counter; - + public ThreadInstructionStore() { _thread_table = new HashMap(); _thread_counter = 0; } - + private Pair newThread(ExecutionContext context) { AyaThread thread = AyaThread.spawnChildThread(context); _thread_counter++; @@ -39,7 +41,7 @@ private Pair newThread(ExecutionContext context) { _thread_table.put(pair.first(), pair.second()); return pair; } - + // Throws an exception if the thread does not exist or is not alive private AyaThread getThread(int thread_id) { AyaThread thread = _thread_table.get(thread_id); @@ -51,125 +53,126 @@ private AyaThread getThread(int thread_id) { return thread; } } - + private int newRequestId() { return _request_id_counter++; } - + @Override - protected void init() { - - addInstruction(new NamedOperator("thread.new", "create a thread") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - Pair thread = newThread(blockEvaluator.getContext()); - thread.second().start(); - blockEvaluator.push(Num.fromInt(thread.first())); - } - }); - - addInstruction(new NamedOperator("thread.add_task", "add a task") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - Obj thread_id_obj = blockEvaluator.pop(); - Obj task_obj = blockEvaluator.pop(); - if (thread_id_obj.isa(Obj.NUM) && task_obj.isa(Obj.BLOCK)) { - int thread_id = Casting.asNumber(thread_id_obj).toInt(); - AyaThread thread = getThread(thread_id); - thread.queueInput(new ExecutionRequest(newRequestId(), Casting.asStaticBlock(task_obj))); - } else { - throw new TypeError(this, "N", thread_id_obj); - } - } - }); - - addInstruction(new NamedOperator("thread.wait_for_result", "wait for a result") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - Obj thread_id_obj = blockEvaluator.pop(); - if (thread_id_obj.isa(Obj.NUM)) { - int thread_id = Casting.asNumber(thread_id_obj).toInt(); - AyaThread thread = getThread(thread_id); - try { - ExecutionResult res = thread.waitForResponse(); - List out = new List(ExecutionResultUtils.getDataOrThrowIfException(res)); - blockEvaluator.push(out); - } catch (InterruptedException e) { - throw new ThreadError(e); - } catch (ThreadError te) { - throw new ThreadError(thread_id, te.getMessage()); + public Collection getNamedInstructions() { + return Arrays.asList( + new NamedOperator("thread.new", "create a thread") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + Pair thread = newThread(blockEvaluator.getContext()); + thread.second().start(); + blockEvaluator.push(Num.fromInt(thread.first())); } - } else { - throw new TypeError(this, "N", thread_id_obj); - } - } - }); - - addInstruction(new NamedOperator("thread.has_unfinished_tasks", "check if the thread has any unfinished tasks") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - Obj thread_id_obj = blockEvaluator.pop(); - if (thread_id_obj.isa(Obj.NUM)) { - int thread_id = Casting.asNumber(thread_id_obj).toInt(); - AyaThread thread = getThread(thread_id); - blockEvaluator.push(Num.fromBool(thread.hasUnfinishedTasks())); - } else { - throw new TypeError(this, "N", thread_id_obj); - } - } - }); - - addInstruction(new NamedOperator("thread.runall", "Run all blocks in parallel") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - ExecutionContext context = blockEvaluator.getContext(); - - final Obj blocks_obj = blockEvaluator.pop(); - - if (blocks_obj.isa(Obj.LIST)) { - List blocks = Casting.asList(blocks_obj); - - ArrayList tasks = new ArrayList(); - for (int i = 0; i < blocks.length(); i++) { - if (blocks.getExact(i).isa(Obj.BLOCK)) { - tasks.add(Casting.asStaticBlock(blocks.getExact(i))); + }, + + new NamedOperator("thread.add_task", "add a task") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + Obj thread_id_obj = blockEvaluator.pop(); + Obj task_obj = blockEvaluator.pop(); + if (thread_id_obj.isa(Obj.NUM) && task_obj.isa(Obj.BLOCK)) { + int thread_id = Casting.asNumber(thread_id_obj).toInt(); + AyaThread thread = getThread(thread_id); + thread.queueInput(new ExecutionRequest(newRequestId(), Casting.asStaticBlock(task_obj))); } else { - throw new TypeError(this, "L", blocks_obj); + throw new TypeError(this, "N", thread_id_obj); } } - - ArrayList threads = new ArrayList(); - - // Spawn a thread for each task - for (int i = 0; i < tasks.size(); i++) { - AyaThread thread = AyaThread.spawnChildThread(context); - thread.queueInput(new ExecutionRequest(-1, tasks.get(i))); - threads.add(thread); + }, + + new NamedOperator("thread.wait_for_result", "wait for a result") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + Obj thread_id_obj = blockEvaluator.pop(); + if (thread_id_obj.isa(Obj.NUM)) { + int thread_id = Casting.asNumber(thread_id_obj).toInt(); + AyaThread thread = getThread(thread_id); + try { + ExecutionResult res = thread.waitForResponse(); + List out = new List(ExecutionResultUtils.getDataOrThrowIfException(res)); + blockEvaluator.push(out); + } catch (InterruptedException e) { + throw new ThreadError(e); + } catch (ThreadError te) { + throw new ThreadError(thread_id, te.getMessage()); + } + } else { + throw new TypeError(this, "N", thread_id_obj); + } } - - for (AyaThread t : threads) t.start(); - - List out = new List(); - - for (AyaThread thread : threads) { - ExecutionResult result; - try { - result = thread.waitForResponse(); - } catch (InterruptedException e) { - throw new ThreadError(e); + }, + + new NamedOperator("thread.has_unfinished_tasks", "check if the thread has any unfinished tasks") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + Obj thread_id_obj = blockEvaluator.pop(); + if (thread_id_obj.isa(Obj.NUM)) { + int thread_id = Casting.asNumber(thread_id_obj).toInt(); + AyaThread thread = getThread(thread_id); + blockEvaluator.push(Num.fromBool(thread.hasUnfinishedTasks())); + } else { + throw new TypeError(this, "N", thread_id_obj); + } + } + }, + + new NamedOperator("thread.runall", "Run all blocks in parallel") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + ExecutionContext context = blockEvaluator.getContext(); + + final Obj blocks_obj = blockEvaluator.pop(); + + if (blocks_obj.isa(Obj.LIST)) { + List blocks = Casting.asList(blocks_obj); + + ArrayList tasks = new ArrayList(); + for (int i = 0; i < blocks.length(); i++) { + if (blocks.getExact(i).isa(Obj.BLOCK)) { + tasks.add(Casting.asStaticBlock(blocks.getExact(i))); + } else { + throw new TypeError(this, "L", blocks_obj); + } + } + + ArrayList threads = new ArrayList(); + + // Spawn a thread for each task + for (int i = 0; i < tasks.size(); i++) { + AyaThread thread = AyaThread.spawnChildThread(context); + thread.queueInput(new ExecutionRequest(-1, tasks.get(i))); + threads.add(thread); + } + + for (AyaThread t : threads) t.start(); + + List out = new List(); + + for (AyaThread thread : threads) { + ExecutionResult result; + try { + result = thread.waitForResponse(); + } catch (InterruptedException e) { + throw new ThreadError(e); + } + + ArrayList data = ExecutionResultUtils.getDataOrThrowIfException(result); + out.mutAdd(new List(data)); + } + + blockEvaluator.push(out); + + } else { + throw new TypeError(this, "L", blocks_obj); } - - ArrayList data = ExecutionResultUtils.getDataOrThrowIfException(result); - out.mutAdd(new List(data)); } - - blockEvaluator.push(out); - - } else { - throw new TypeError(this, "L", blocks_obj); } - } - }); - + ); + } } diff --git a/src/aya/instruction/named/NamedInstructionStore.java b/src/aya/instruction/named/NamedInstructionStore.java index c28272c5..fafd9d3b 100644 --- a/src/aya/instruction/named/NamedInstructionStore.java +++ b/src/aya/instruction/named/NamedInstructionStore.java @@ -1,34 +1,7 @@ package aya.instruction.named; -import java.util.HashMap; +import java.util.Collection; -import aya.StaticData; - -public abstract class NamedInstructionStore { - - private HashMap _instructions; - - public NamedInstructionStore() { - _instructions = new HashMap(); - init(); - } - - protected void addInstruction(NamedOperator inst) { - _instructions.put(inst.getName(), inst); - } - - public NamedOperator getInstruction(String name) { - return _instructions.get(name); - } - - public void initHelpData(StaticData staticData) { - for (HashMap.Entry pair : _instructions.entrySet()) { - NamedOperator i = pair.getValue(); - String doc = i._doc; - if (doc == null || doc.equals("")) continue; - staticData.addHelpText(":{" + i.getName() + "}\n " + doc); - } - } - - protected abstract void init(); +public interface NamedInstructionStore { + Collection getNamedInstructions(); } diff --git a/src/aya/instruction/named/NamedOperator.java b/src/aya/instruction/named/NamedOperator.java index 51fd2323..06edb7eb 100644 --- a/src/aya/instruction/named/NamedOperator.java +++ b/src/aya/instruction/named/NamedOperator.java @@ -28,6 +28,10 @@ public String opName() { return ":{" + _name + "}"; } + public String getDoc() { + return _doc; + } + public ReprStream repr(ReprStream stream) { stream.print(opName()); return stream; From b8dcd18414b6108a1ab2306519c22219704e39bb Mon Sep 17 00:00:00 2001 From: npgit Date: Mon, 30 Dec 2024 13:11:07 -0500 Subject: [PATCH 2/3] Remove auto loading from libs folder and add :(library.load) for manual loading --- build-scripts/package-ayastdlib-js-build.xml | 1 - build-scripts/release-zip-build.xml | 1 - build-scripts/run-aya-tests-build.xml | 1 - libs/README.md => manual/libraries.md | 31 ++++----- src/aya/StaticData.java | 66 +++++++++---------- .../ext/library/LibraryInstructionStore.java | 35 ++++++++++ 6 files changed, 79 insertions(+), 56 deletions(-) rename libs/README.md => manual/libraries.md (53%) create mode 100644 src/aya/ext/library/LibraryInstructionStore.java diff --git a/build-scripts/package-ayastdlib-js-build.xml b/build-scripts/package-ayastdlib-js-build.xml index 5d3ddff3..97b1e155 100644 --- a/build-scripts/package-ayastdlib-js-build.xml +++ b/build-scripts/package-ayastdlib-js-build.xml @@ -29,7 +29,6 @@ - diff --git a/build-scripts/release-zip-build.xml b/build-scripts/release-zip-build.xml index c58bd504..d82ad302 100644 --- a/build-scripts/release-zip-build.xml +++ b/build-scripts/release-zip-build.xml @@ -32,7 +32,6 @@ - diff --git a/build-scripts/run-aya-tests-build.xml b/build-scripts/run-aya-tests-build.xml index 93bf0398..3846c53f 100644 --- a/build-scripts/run-aya-tests-build.xml +++ b/build-scripts/run-aya-tests-build.xml @@ -31,7 +31,6 @@ - diff --git a/libs/README.md b/manual/libraries.md similarity index 53% rename from libs/README.md rename to manual/libraries.md index 1e09ba4a..2638a6ff 100644 --- a/libs/README.md +++ b/manual/libraries.md @@ -1,6 +1,6 @@ ## Adding more Instructions -You can add more Instructions by placing `.jar` files here. +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: @@ -11,6 +11,7 @@ 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; @@ -20,22 +21,16 @@ import aya.instruction.named.NamedOperator; import aya.obj.list.List; public class MyInstructionStore implements NamedInstructionStore { - @Override - public Collection getNamedInstructions() { - return Arrays.asList( - new NamedOperator("example.instruction") { - @Override - public void execute(BlockEvaluator blockEvaluator) { - blockEvaluator.push(List.fromString("hello, world")); - } - } - ); - } + @Override + public Collection getNamedInstructions() { + return Arrays.asList( + new NamedOperator("example.instruction") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + blockEvaluator.push(List.fromString("hello, world")); + } + } + ); + } } ``` - -Then you can register your implementation as a ServiceProvider like this: -``` -> cat META-INF/services/aya.instruction.named.NamedInstructionStore -my.instruction.MyInstructionStore -``` \ No newline at end of file diff --git a/src/aya/StaticData.java b/src/aya/StaticData.java index 72124a4c..506a5c25 100644 --- a/src/aya/StaticData.java +++ b/src/aya/StaticData.java @@ -1,5 +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; @@ -9,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; @@ -28,19 +41,6 @@ import aya.parser.SpecialNumberParser; import aya.util.StringSearch; -import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.ServiceLoader; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - public class StaticData { public static final boolean DEBUG = true; @@ -157,37 +157,33 @@ private void initNamedInstructions() { addNamedInstructionStore(new ColorInstructionStore()); addNamedInstructionStore(new LinearAlgebraInstructionStore()); addNamedInstructionStore(new ThreadInstructionStore()); - - /* - Unfortunately, classpath wildcards are not supported in MANIFEST files. https://docs.oracle.com/javase/7/docs/technotes/tools/windows/classpath.html - So the user-libraries need to be loaded manually. - */ + addNamedInstructionStore(new LibraryInstructionStore()); + } + + public ArrayList loadLibrary(File path) { + ArrayList loaded = new ArrayList(); + try { - File libsDir = new File(AyaPrefs.getAyaRootDirectory(), "libs"); - final URL[] libUrls; - try (Stream libPaths = Files.list(libsDir.toPath())) { - libUrls = libPaths.map(path -> { - try { - return path.toUri().toURL(); - } catch (Exception e) { - return null; - } - }).filter(Objects::nonNull).toArray(URL[]::new); - } - - try (URLClassLoader libClassLoader = new URLClassLoader(libUrls)) { + 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()); + //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 (Exception e) { - IO.err().println("Failed to load libraries due to exception: " + e.getMessage()); - e.printStackTrace(IO.err()); + + } catch (MalformedURLException e) { + throw new IOError("library.load", path.getPath(), e); } + + return loaded; } public void addNamedInstructionStore(NamedInstructionStore store) { diff --git a/src/aya/ext/library/LibraryInstructionStore.java b/src/aya/ext/library/LibraryInstructionStore.java new file mode 100644 index 00000000..4eac165a --- /dev/null +++ b/src/aya/ext/library/LibraryInstructionStore.java @@ -0,0 +1,35 @@ +package aya.ext.library; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import aya.StaticData; +import aya.eval.BlockEvaluator; +import aya.instruction.named.NamedInstructionStore; +import aya.instruction.named.NamedOperator; +import aya.obj.list.List; +import aya.util.FileUtils; + +public class LibraryInstructionStore implements NamedInstructionStore { + + @Override + public Collection getNamedInstructions() { + return Arrays.asList( + new NamedOperator("library.load", "path::str: load an external library, return list of loaded operators") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + String lib_path = blockEvaluator.pop().str(); + ArrayList loaded = StaticData.getInstance().loadLibrary(FileUtils.resolveFile(lib_path)); + List loaded_names = new List(); + for (NamedInstructionStore lib : loaded) { + for (NamedOperator op : lib.getNamedInstructions()) { + loaded_names.mutAdd(List.fromString(op.opName())); + } + } + blockEvaluator.push(loaded_names); + } + } + ); + } +} From 187092e174d2f5fdaeb56db5c02f33f6874bdb9e Mon Sep 17 00:00:00 2001 From: npgit Date: Tue, 31 Dec 2024 17:44:18 -0500 Subject: [PATCH 3/3] Add ability to import jars using import statement (simple wrapper aroud library.load) --- base/importlib.aya | 13 ++++++++++++- src/web/WebAvailableNamedInstructionStore.java | 8 ++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/base/importlib.aya b/base/importlib.aya index c45e383b..9723ca0a 100644 --- a/base/importlib.aya +++ b/base/importlib.aya @@ -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 + + .# /base ] @@ -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 @@ -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 diff --git a/src/web/WebAvailableNamedInstructionStore.java b/src/web/WebAvailableNamedInstructionStore.java index ae79cabf..fc5f440a 100644 --- a/src/web/WebAvailableNamedInstructionStore.java +++ b/src/web/WebAvailableNamedInstructionStore.java @@ -43,6 +43,14 @@ public void execute(BlockEvaluator blockEvaluator) { // Unimplemented throw new UnimplementedError(); } + }, + + new NamedOperator("library.load", "load a jar file") { + @Override + public void execute(BlockEvaluator blockEvaluator) { + // Unimplemented + throw new UnimplementedError(); + } } ); }