diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml
index 7e5c3afb..a5336f59 100644
--- a/.github/workflows/maven-publish.yml
+++ b/.github/workflows/maven-publish.yml
@@ -18,10 +18,10 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v3
- - name: Set up JDK 8
+ - name: Set up JDK 11
uses: actions/setup-java@v3
with:
- java-version: '8'
+ java-version: '11'
distribution: 'temurin'
cache: maven
- name: Run install phase
@@ -30,4 +30,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: aya.zip
- path: target/aya-*.zip
\ No newline at end of file
+ path: target/aya-*.zip
diff --git a/.github/workflows/maven-test.yml b/.github/workflows/maven-test.yml
index 563b5fc7..54973e4b 100644
--- a/.github/workflows/maven-test.yml
+++ b/.github/workflows/maven-test.yml
@@ -19,11 +19,11 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v3
- - name: Set up JDK 8
+ - name: Set up JDK 11
uses: actions/setup-java@v3
with:
- java-version: '8'
+ java-version: '11'
distribution: 'temurin'
cache: maven
- name: Run test phase
- run: mvn test --batch-mode
\ No newline at end of file
+ run: mvn test --batch-mode
diff --git a/build-scripts/package-ayastdlib-js-build.xml b/build-scripts/package-ayastdlib-js-build.xml
new file mode 100644
index 00000000..97b1e155
--- /dev/null
+++ b/build-scripts/package-ayastdlib-js-build.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/package_aya_libs.aya b/examples/package_aya_libs.aya
new file mode 100644
index 00000000..ebeb8767
--- /dev/null
+++ b/examples/package_aya_libs.aya
@@ -0,0 +1,29 @@
+import ::json
+import ::sys
+import ::io
+
+.# All files in base/ and std/
+[
+ "base" path! $ sys.readdir + ~
+ "std" path! $ sys.readdir + ~
+] :files;
+
+.# Convert to strings
+files #P :files;
+
+{,} :data;
+
+files :# {file,
+ file G data.:[file];
+};
+
+data json.dumps :json_data;
+
+"aya-stdlib.js" :outfile;
+
+"const AYA_STDLIB = $json_data;" outfile 0 .G
+
+"Created file $outfile" :P
+
+
+
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..937ba5b4
--- /dev/null
+++ b/index.html
@@ -0,0 +1,58 @@
+
+
+
+ TeaVM example
+
+
+
+
+
+
+
+
+
+ Press Shift+Enter to run
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 403e18df..aa98819a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,11 +9,13 @@
4.0.0
- 8
- 8
+ 11
+ 11
UTF-8
+
+
local-libs
@@ -23,7 +25,9 @@
src
+
+
org.apache.maven.plugins
maven-dependency-plugin
@@ -60,49 +64,14 @@
-
- release-zip
- verify
-
- run
-
-
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 3.5.0
-
-
- package
-
- shade
-
-
- false
-
-
- ui.AyaIDE
-
-
- false
-
-
-
-
+
ch.obermuhlner
big-math
@@ -128,6 +97,149 @@
json
20231013
+
+
+ org.teavm
+ teavm-jso
+ 0.10.2
+ true
+
-
\ No newline at end of file
+
+
+
+
+
+ desktop
+
+ true
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.5.0
+
+
+
+ package
+
+ shade
+
+
+ false
+
+
+ ui.AyaIDE
+
+
+ false
+
+
+
+
+ org.teavm:teavm-classlib
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ 3.1.0
+
+
+ release-zip
+ package
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ web
+
+
+
+
+
+ org.teavm
+ teavm-maven-plugin
+ 0.10.2
+
+
+
+ org.teavm
+ teavm-classlib
+ 0.10.2
+
+
+
+
+
+ compile
+
+ process-classes
+
+ web.AyaWeb
+ true
+ true
+ true
+ aya.js
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ 3.1.0
+
+
+ package-ayastdlib-js
+ package
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/aya/AyaPrefs.java b/src/aya/AyaPrefs.java
index d6379776..0f4d81d8 100644
--- a/src/aya/AyaPrefs.java
+++ b/src/aya/AyaPrefs.java
@@ -4,8 +4,10 @@
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
+import aya.exceptions.runtime.IOError;
import aya.obj.Obj;
import aya.obj.character.Char;
import aya.obj.list.Str;
@@ -100,15 +102,19 @@ public static boolean setAyaDir(String dir) {
}
}
- public static ArrayList listFilesAndDirsForFolder(final File folder) {
- File[] listOfFiles = folder.listFiles();
+ public static ArrayList listFilesAndDirsForFolder(final Path path) {
+ File[] listOfFiles = path.toFile().listFiles();
ArrayList fileList = new ArrayList();
- for (File file : listOfFiles) {
- if (file.isFile()) {
- fileList.add(file.getName());
- } else if (file.isDirectory()) {
- fileList.add(file.getName() + File.separator);
- }
+ if (listOfFiles == null) {
+ throw new IOError("AyaPrefs.listFilesAndDirsForFolder", path.toString(), "Unable to list files, path is invalid");
+ } else {
+ for (File file : listOfFiles) {
+ if (file.isFile()) {
+ fileList.add(file.getName());
+ } else if (file.isDirectory()) {
+ fileList.add(file.getName() + File.separator);
+ }
+ }
}
return fileList;
}
diff --git a/src/aya/AyaStdIO.java b/src/aya/AyaStdIO.java
index de650410..c6c52b7f 100644
--- a/src/aya/AyaStdIO.java
+++ b/src/aya/AyaStdIO.java
@@ -5,19 +5,19 @@
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
-import java.util.Scanner;
-public class AyaStdIO {
+import aya.io.stdin.InputWrapper;
+public class AyaStdIO {
private PrintStream _out;
private PrintStream _err;
private InputStream _in;
- private Scanner _scanner;
+ private InputWrapper _input_wrapper;
- public AyaStdIO(PrintStream out, PrintStream err, InputStream in) {
+ public AyaStdIO(PrintStream out, PrintStream err, InputStream in, InputWrapper iw) {
_out = out;
_err = err;
- setIn(in);
+ setIn(in, iw);
}
public PrintStream out() {
@@ -32,12 +32,8 @@ public InputStream in() {
return _in;
}
- public Scanner scanner() {
- return _scanner;
- }
-
public String nextLine() {
- return _scanner.nextLine();
+ return _input_wrapper.nextLine();
}
/** Return true if input is ready to be read */
@@ -71,9 +67,13 @@ public void setErr(OutputStream os) {
}
}
- public void setIn(InputStream is) {
+ public void setIn(InputStream is, InputWrapper input_wrapper) {
_in = is;
- _scanner = new Scanner(_in, "UTF-8");
+ _input_wrapper = input_wrapper;
+ }
+
+ public InputWrapper inputWrapper() {
+ return _input_wrapper;
}
diff --git a/src/aya/InteractiveAya.java b/src/aya/InteractiveAya.java
index 750abd18..9e4db1b4 100644
--- a/src/aya/InteractiveAya.java
+++ b/src/aya/InteractiveAya.java
@@ -8,6 +8,9 @@
import aya.eval.ExecutionContext;
import aya.exceptions.parser.ParserException;
+import aya.io.fs.FilesystemIO;
+import aya.io.http.HTTPDownloader;
+import aya.io.stdin.ScannerInputWrapper;
import aya.obj.Obj;
import aya.obj.block.StaticBlock;
import aya.parser.Parser;
@@ -165,7 +168,7 @@ public int loop() {
// Get Aya I/O
PrintStream out = _io().out();
PrintStream err = _io().err();
- Scanner scanner = _io().scanner();
+ Scanner scanner = ((ScannerInputWrapper)(_io().inputWrapper())).getScanner();
_aya.start();
@@ -285,8 +288,12 @@ public static InteractiveAya createInteractiveSession(String[] args) {
}
public static void main(String[] args) {
- InteractiveAya iaya = createInteractiveSession(args);
+ StaticData.IO = new AyaStdIO(System.out, System.err, System.in, new ScannerInputWrapper(System.in));
+ StaticData.HTTP_DOWNLOADER = new HTTPDownloader();
+ StaticData.FILESYSTEM = new FilesystemIO();
+ InteractiveAya iaya = createInteractiveSession(args);
+
// argument[0] is always the working directory, check for args 1+
if (args.length > 1) {
if (args[1].equals("-i")) {
diff --git a/src/aya/StandaloneAya.java b/src/aya/StandaloneAya.java
new file mode 100644
index 00000000..f2ed253d
--- /dev/null
+++ b/src/aya/StandaloneAya.java
@@ -0,0 +1,119 @@
+package aya;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+import aya.eval.BlockEvaluator;
+import aya.eval.ExecutionContext;
+import aya.exceptions.parser.ParserException;
+import aya.exceptions.runtime.AyaRuntimeException;
+import aya.exceptions.runtime.ValueError;
+import aya.io.fs.FilesystemIO;
+import aya.io.http.HTTPDownloader;
+import aya.io.stdin.ScannerInputWrapper;
+import aya.obj.Obj;
+import aya.obj.block.StaticBlock;
+import aya.parser.Parser;
+import aya.parser.SourceString;
+
+public class StandaloneAya {
+ /**
+ * A Bare-bones aya entry point that does not provide interactivity or preserve state
+ */
+
+ public static void main(String[] args) {
+ StaticData.IO = new AyaStdIO(System.out, System.err, System.in, new ScannerInputWrapper(System.in));
+ StaticData.HTTP_DOWNLOADER = new HTTPDownloader();
+ StaticData.FILESYSTEM = new FilesystemIO();
+
+ if (args.length == 1) {
+ runIsolated(args[0], StaticData.IO);
+ } else {
+ StaticData.IO.err().println("Please specify code to run as command line argument");
+ }
+ }
+
+ // An extremely basic linter that catches syntax errors only
+ // Currently the parser will stop after the first error, may need to update later
+ public static ArrayList lint(String input) {
+ ArrayList errors = new ArrayList();
+ try {
+ Parser.compile(new SourceString(input, ""));
+ } catch (ParserException err) {
+ errors.add(err);
+ }
+ return errors;
+ }
+
+ public static void runIsolated(String input, AyaStdIO io) {
+ ExecutionContext ctx = ExecutionContext.createIsolatedContext();
+ ctx.getVars().initGlobals();
+
+ // Create request
+ StaticBlock blk = Parser.compileSafeOrNull(new SourceString(input, ""), io);
+
+ if (blk != null) {
+ ExecutionRequest request = new ExecutionRequest(0, blk);
+
+ // Create evaluator and run
+ BlockEvaluator b = ctx.createEvaluator();
+ b.dump(request.getBlock());
+
+ ExecutionResult result = null;
+ try {
+ b.eval();
+ result = new ExecutionResultSuccess(request.id(), b.getStack());
+ } catch (AyaRuntimeException ex) {
+ result = new ExecutionResultException(request.id(), ex, ctx.getCallStack());
+ } catch (Exception e) {
+ PrintStream err = StaticData.IO.err();
+ err.println(DebugUtils.exToString(e));
+ try {
+ if (b.hasOutputState())
+ err.println("stack:\n\t" + b.getPrintOutputState());
+ if (b.getInstructions().size() > 0)
+ err.println("just before:\n\t" + b.getInstructions().toString());
+ if (!ctx.getCallStack().isEmpty())
+ err.print(ctx.getCallStack().toString());
+ } catch (Exception e2) {
+ err.println("An additional error was thrown when attempting to print the stack state:");
+ err.println(DebugUtils.exToString(e2));
+ err.println("This is likely caused by an error in an overloaded __str__ or __repr__ blockEvaluator.");
+ }
+ result = new ExecutionResultException(request.id(), new ValueError("TODO"), ctx.getCallStack());
+ } finally {
+ ctx.getVars().reset();
+ ctx.getCallStack().reset();
+ }
+
+ if (result != null) {
+ switch (result.getType()) {
+ case ExecutionResult.TYPE_SUCCESS:
+ {
+ ExecutionResultSuccess res = (ExecutionResultSuccess)result;
+ ArrayList data = res.getData();
+ if (data.size() > 0) {
+ for (int i = 0; i < data.size(); i++) {
+ io.out().print(data.get(i));
+ if (i < data.size() - 1) io.out().print(' ');
+ }
+ io.out().println();
+ }
+ }
+ break;
+ case ExecutionResult.TYPE_EXCEPTION:
+ {
+ ExecutionResultException res = (ExecutionResultException)result;
+ res.ex().print(io.err());
+ if (!res.callstack().equals("")) {
+ io.err().print(res.callstack());
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ }
+}
+
diff --git a/src/aya/StaticData.java b/src/aya/StaticData.java
index 8ed0e323..d5dfe207 100644
--- a/src/aya/StaticData.java
+++ b/src/aya/StaticData.java
@@ -23,6 +23,10 @@
import aya.instruction.op.OpDocReader;
import aya.instruction.op.Operator;
import aya.instruction.op.Ops;
+import aya.io.fs.AbstractFilesystemIO;
+import aya.io.fs.UnimplementedFilesystemIO;
+import aya.io.http.AbstractHTTPDownloader;
+import aya.io.http.UnimplementedHTTPDownloader;
import aya.parser.SpecialNumberParser;
import aya.util.StringSearch;
@@ -35,15 +39,16 @@ public class StaticData {
public static final String QUIT = "\\Q";
- public static final AyaStdIO IO = new AyaStdIO(System.out, System.err, System.in);
+ // Must me initialized in main
+ public static AyaStdIO IO = null;
+ public static AbstractHTTPDownloader HTTP_DOWNLOADER = new UnimplementedHTTPDownloader();
+ public static AbstractFilesystemIO FILESYSTEM = new UnimplementedFilesystemIO();
//
// All calls to modify this data will need to be thread safe
//
private static StaticData _instance;
-
-
//
// Data loaded in the parser
//
@@ -73,6 +78,11 @@ public void init() {
initNamedInstructions();
}
+ public void addNamedInstructionStore(NamedInstructionStore is) {
+ _namedInstructionStores.add(is);
+ is.initHelpData(this);
+ }
+
///////////////
// Help Data //
@@ -148,8 +158,9 @@ private void initNamedInstructions() {
for (NamedInstructionStore x : _namedInstructionStores) {
x.initHelpData(this);
}
-
}
+
+
public NamedOperator getNamedInstruction(String name) {
for (NamedInstructionStore x : _namedInstructionStores) {
NamedOperator i = x.getInstruction(name);
diff --git a/src/aya/eval/ExecutionContext.java b/src/aya/eval/ExecutionContext.java
index 6d2b70c4..2e5fd707 100644
--- a/src/aya/eval/ExecutionContext.java
+++ b/src/aya/eval/ExecutionContext.java
@@ -2,6 +2,7 @@
import aya.AyaStdIO;
import aya.CallStack;
+import aya.StaticData;
import aya.obj.dict.Dict;
import aya.variable.VariableData;
@@ -18,7 +19,8 @@ private ExecutionContext(AyaStdIO io) {
}
public static ExecutionContext createIsolatedContext() {
- final ExecutionContext at = new ExecutionContext(new AyaStdIO(System.out, System.err, System.in));
+ final ExecutionContext at = new ExecutionContext(StaticData.IO);
+
at.getVars().add(new Dict()); // Add empty globals
return at;
}
diff --git a/src/aya/ext/fstream/FStreamManager.java b/src/aya/ext/fstream/FStreamManager.java
index a54b516b..61290361 100644
--- a/src/aya/ext/fstream/FStreamManager.java
+++ b/src/aya/ext/fstream/FStreamManager.java
@@ -141,7 +141,23 @@ public static int read(int fileid) {
return -1;
}
}
-
+
+ public static String readAll(String path) throws IOException {
+ File file = new File(path);
+ String output = null;
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(file));
+ output = br.lines().collect(Collectors.joining("\n"));
+ br.close();
+ } catch (IOException e) {
+ br.close();
+ output = null;
+ }
+
+ return output;
+ }
+
public static String readAll(int fileid) {
BufferedReader f = _input_streams.get(fileid);
if (f == null) return null;
diff --git a/src/aya/ext/json/JSONUtils.java b/src/aya/ext/json/JSONUtils.java
index 0223f8ef..3bce1c54 100644
--- a/src/aya/ext/json/JSONUtils.java
+++ b/src/aya/ext/json/JSONUtils.java
@@ -161,7 +161,7 @@ private static Object toJSON(Obj o, JSONParams params, VisitedChecker vc) {
if (vc.hasVisited(e.second())) {
throw new ValueError("JSON: Circular reference detected when serializing json object. key: " + e.first());
} else {
- out.put(e.first().name(), toJSON(e.second(), params, vc));
+ out.put(e.first().unquotedName(), toJSON(e.second(), params, vc));
}
}
vc.pop(d);
diff --git a/src/aya/ext/sys/FileExistsSystemInstruction.java b/src/aya/ext/sys/FileExistsSystemInstruction.java
new file mode 100644
index 00000000..9ecbdf17
--- /dev/null
+++ b/src/aya/ext/sys/FileExistsSystemInstruction.java
@@ -0,0 +1,22 @@
+package aya.ext.sys;
+
+import aya.eval.BlockEvaluator;
+import aya.instruction.named.NamedOperator;
+import aya.obj.Obj;
+import aya.obj.number.Num;
+import aya.util.FileUtils;
+
+public class FileExistsSystemInstruction extends NamedOperator {
+
+ public FileExistsSystemInstruction() {
+ super("sys.file_exists");
+ _doc = "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);
+ }
+
+}
diff --git a/src/aya/ext/sys/SystemInstructionStore.java b/src/aya/ext/sys/SystemInstructionStore.java
index 42fca420..b0728283 100644
--- a/src/aya/ext/sys/SystemInstructionStore.java
+++ b/src/aya/ext/sys/SystemInstructionStore.java
@@ -1,16 +1,16 @@
package aya.ext.sys;
import java.io.File;
+import java.nio.file.Path;
import java.util.ArrayList;
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 {
@@ -28,16 +28,13 @@ public void execute(BlockEvaluator blockEvaluator) {
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 + "'");
+ Path path = FileUtils.resolvePath(fstr);
+ ArrayList dirs = AyaPrefs.listFilesAndDirsForFolder(path);
+ ArrayList obj_dirs = new ArrayList(dirs.size());
+ for (String s : dirs) {
+ obj_dirs.add(List.fromString(s));
}
+ blockEvaluator.push(new List(obj_dirs));
} else {
throw new ValueError(":{sys.readdir} : arg must be a string. Received:\n" + arg.repr());
}
@@ -143,14 +140,7 @@ public void execute(BlockEvaluator blockEvaluator) {
}
});
- // 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);
- }
- });
+ addInstruction(new FileExistsSystemInstruction());
// Resolve home (replace ~/ with /path/to/home)
addInstruction(new NamedOperator("sys.resolvehome", "replace ~/.. with /path/to/home/..") {
diff --git a/src/aya/instruction/op/DotOps.java b/src/aya/instruction/op/DotOps.java
index 1166e5b6..375b9396 100644
--- a/src/aya/instruction/op/DotOps.java
+++ b/src/aya/instruction/op/DotOps.java
@@ -19,7 +19,6 @@
import java.io.File;
import java.io.IOException;
-import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
@@ -40,7 +39,6 @@
import aya.exceptions.runtime.UnimplementedError;
import aya.exceptions.runtime.UserObjRuntimeException;
import aya.exceptions.runtime.ValueError;
-import aya.ext.dialog.QuickDialog;
import aya.instruction.DataInstruction;
import aya.instruction.ListBuilderInstruction;
import aya.obj.Obj;
@@ -132,7 +130,7 @@ public class DotOps {
/* 82 R */ new OP_Dot_R(),
/* 83 S */ new OP_Dot_S(),
/* 84 T */ new OP_Dot_T(),
- /* 85 U */ new OP_RequestString(),
+ /* 85 U */ null,
/* 86 V */ new OP_Dot_AppendBack(),
/* 87 W */ null,
/* 88 X */ null,
@@ -1020,7 +1018,7 @@ public void execute(BlockEvaluator blockEvaluator) {
if(option == 0) {
try {
- Files.write(file.toPath(), write.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+ StaticData.FILESYSTEM.write(file, write.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
}catch (IOException e) {
throw new IOError(".G", absFilePath, e);
} catch (InvalidPathException ipe) {
@@ -1030,7 +1028,7 @@ public void execute(BlockEvaluator blockEvaluator) {
else if (option == 1) {
try {
- Files.write(file.toPath(), write.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
+ StaticData.FILESYSTEM.write(file, write.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
}catch (IOException e) {
throw new IOError(".G", absFilePath, e);
} catch (InvalidPathException ipe) {
@@ -1378,20 +1376,6 @@ public void execute (BlockEvaluator blockEvaluator) {
}
}
-// U - 85
-class OP_RequestString extends Operator {
-
- public OP_RequestString() {
- init(".U");
- arg("S", "requests a string using a ui dialog, S is the prompt text");
- }
-
- @Override
- public void execute(BlockEvaluator blockEvaluator) {
- blockEvaluator.push(List.fromString(QuickDialog.requestString(blockEvaluator.pop().str())));
- }
-}
-
// V - 86
class OP_Dot_AppendBack extends Operator {
diff --git a/src/aya/instruction/op/Ops.java b/src/aya/instruction/op/Ops.java
index 902500ac..a5c78cbf 100644
--- a/src/aya/instruction/op/Ops.java
+++ b/src/aya/instruction/op/Ops.java
@@ -20,18 +20,17 @@
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
-import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map.Entry;
import java.util.Random;
-import java.util.Scanner;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import aya.eval.ExecutionContext;
+import aya.StaticData;
import aya.eval.BlockEvaluator;
+import aya.eval.ExecutionContext;
import aya.exceptions.parser.NotAnOperatorError;
import aya.exceptions.parser.ParserException;
import aya.exceptions.runtime.IOError;
@@ -933,22 +932,12 @@ public void execute (final BlockEvaluator blockEvaluator) {
String name = a.str();
if(Ops.PATTERN_URL.matcher(name).matches()) {
- Scanner scnr = null;
try {
- URL url = new URL(name);
- scnr = new Scanner(url.openStream());
- StringBuilder sb = new StringBuilder();
-
- while(scnr.hasNext()) {
- sb.append(scnr.nextLine()).append('\n');
- }
- blockEvaluator.push(List.fromString(sb.toString()));
+ String downloaded = StaticData.HTTP_DOWNLOADER.downloadFile(name);
+ blockEvaluator.push(List.fromString(downloaded));
}
catch(IOException ex) {
throw new IOError("G", name, ex);
- } finally {
- if(scnr != null)
- scnr.close();
}
} else {
File readFile = FileUtils.resolveFile(name);
diff --git a/src/aya/io/StringOut.java b/src/aya/io/StringOut.java
new file mode 100644
index 00000000..ad535e55
--- /dev/null
+++ b/src/aya/io/StringOut.java
@@ -0,0 +1,48 @@
+package aya.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+
+public class StringOut {
+
+ public String ENCODING = StandardCharsets.UTF_8.name();
+
+ private ByteArrayOutputStream _out;
+ private ByteArrayOutputStream _err;
+
+ public StringOut() {
+ _out = new ByteArrayOutputStream();
+ _err = new ByteArrayOutputStream();
+ }
+
+ public ByteArrayOutputStream getOutStream() {
+ return _out;
+ }
+
+ public ByteArrayOutputStream getErrStream() {
+ return _out;
+ }
+
+ public String flushOut() {
+ try {
+ String s = _out.toString(ENCODING);
+ _out.reset();
+ return s;
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ return e.getLocalizedMessage();
+ }
+ }
+
+ public String flushErr() {
+ try {
+ String s = _err.toString(ENCODING);
+ _err.reset();
+ return s;
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ return e.getLocalizedMessage();
+ }
+ }
+}
diff --git a/src/aya/io/fs/AbstractFilesystemIO.java b/src/aya/io/fs/AbstractFilesystemIO.java
new file mode 100644
index 00000000..bc4b12d2
--- /dev/null
+++ b/src/aya/io/fs/AbstractFilesystemIO.java
@@ -0,0 +1,14 @@
+package aya.io.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.StandardOpenOption;
+
+public abstract class AbstractFilesystemIO {
+ public abstract byte[] readAllBytes(File file) throws IOException;
+
+ public abstract void write(File file, byte[] bytes, StandardOpenOption create,
+ StandardOpenOption truncateExisting) throws IOException;
+
+ public abstract boolean isFile(File file);
+}
diff --git a/src/aya/io/fs/FilesystemIO.java b/src/aya/io/fs/FilesystemIO.java
new file mode 100644
index 00000000..bc59d3a5
--- /dev/null
+++ b/src/aya/io/fs/FilesystemIO.java
@@ -0,0 +1,25 @@
+package aya.io.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+
+public class FilesystemIO extends AbstractFilesystemIO {
+
+ @Override
+ public byte[] readAllBytes(File file) throws IOException {
+ return Files.readAllBytes(file.toPath());
+ }
+
+ @Override
+ public void write(File file, byte[] bytes, StandardOpenOption create, StandardOpenOption truncateExisting) throws IOException {
+ Files.write(file.toPath(), bytes, create, truncateExisting);
+ }
+
+ @Override
+ public boolean isFile(File file) {
+ return file.isFile();
+ }
+
+}
diff --git a/src/aya/io/fs/UnimplementedFilesystemIO.java b/src/aya/io/fs/UnimplementedFilesystemIO.java
new file mode 100644
index 00000000..966c5c28
--- /dev/null
+++ b/src/aya/io/fs/UnimplementedFilesystemIO.java
@@ -0,0 +1,27 @@
+package aya.io.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.StandardOpenOption;
+
+import aya.exceptions.runtime.IOError;
+
+public class UnimplementedFilesystemIO extends AbstractFilesystemIO {
+
+ @Override
+ public byte[] readAllBytes(File file) throws IOException {
+ throw new IOError("", "UnimplementedFilesystemReader.readAllBytes", "filesystem unavailable");
+ }
+
+ @Override
+ public void write(File file, byte[] bytes, StandardOpenOption create, StandardOpenOption truncateExisting)
+ throws IOException {
+ throw new IOError("", "UnimplementedFilesystemReader.write", "filesystem unavailable");
+ }
+
+ @Override
+ public boolean isFile(File file) {
+ throw new IOError("", "UnimplementedFilesystemReader.write", "filesystem unavailable");
+ }
+
+}
diff --git a/src/aya/io/http/AbstractHTTPDownloader.java b/src/aya/io/http/AbstractHTTPDownloader.java
new file mode 100644
index 00000000..697652ab
--- /dev/null
+++ b/src/aya/io/http/AbstractHTTPDownloader.java
@@ -0,0 +1,7 @@
+package aya.io.http;
+
+import java.io.IOException;
+
+public abstract class AbstractHTTPDownloader {
+ public abstract String downloadFile(String url) throws IOException;
+}
diff --git a/src/aya/io/http/HTTPDownloader.java b/src/aya/io/http/HTTPDownloader.java
new file mode 100644
index 00000000..5fe3230a
--- /dev/null
+++ b/src/aya/io/http/HTTPDownloader.java
@@ -0,0 +1,27 @@
+package aya.io.http;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Scanner;
+
+public class HTTPDownloader extends AbstractHTTPDownloader {
+
+ @Override
+ public String downloadFile(String url_str) throws IOException {
+ Scanner scanner = null;
+ try {
+ URL url = new URL(url_str);
+ scanner = new Scanner(url.openStream());
+ StringBuilder sb = new StringBuilder();
+ while(scanner.hasNext()) {
+ sb.append(scanner.nextLine()).append('\n');
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ if (scanner != null) scanner.close();
+ }
+ }
+
+}
diff --git a/src/aya/io/http/UnimplementedHTTPDownloader.java b/src/aya/io/http/UnimplementedHTTPDownloader.java
new file mode 100644
index 00000000..179c7973
--- /dev/null
+++ b/src/aya/io/http/UnimplementedHTTPDownloader.java
@@ -0,0 +1,14 @@
+package aya.io.http;
+
+import java.io.IOException;
+
+import aya.exceptions.runtime.IOError;
+
+public class UnimplementedHTTPDownloader extends AbstractHTTPDownloader {
+
+ @Override
+ public String downloadFile(String url) throws IOException {
+ throw new IOError("", "UnimplementedHTTPDownloader.downloadFile", "downloadFile unimplemented");
+ }
+
+}
diff --git a/src/aya/io/stdin/EmptyInputWrapper.java b/src/aya/io/stdin/EmptyInputWrapper.java
new file mode 100644
index 00000000..f33e70f4
--- /dev/null
+++ b/src/aya/io/stdin/EmptyInputWrapper.java
@@ -0,0 +1,12 @@
+package aya.io.stdin;
+
+import aya.exceptions.runtime.IOError;
+
+public class EmptyInputWrapper extends InputWrapper {
+
+ @Override
+ public String nextLine() {
+ throw new IOError("", "EmptyInputWrapper.nextLine()", "Next line called!");
+ }
+
+}
diff --git a/src/aya/io/stdin/InputWrapper.java b/src/aya/io/stdin/InputWrapper.java
new file mode 100644
index 00000000..24fdf352
--- /dev/null
+++ b/src/aya/io/stdin/InputWrapper.java
@@ -0,0 +1,5 @@
+package aya.io.stdin;
+
+public abstract class InputWrapper {
+ public abstract String nextLine();
+}
diff --git a/src/aya/io/stdin/ScannerInputWrapper.java b/src/aya/io/stdin/ScannerInputWrapper.java
new file mode 100644
index 00000000..3a707234
--- /dev/null
+++ b/src/aya/io/stdin/ScannerInputWrapper.java
@@ -0,0 +1,25 @@
+package aya.io.stdin;
+
+import java.io.InputStream;
+import java.util.Scanner;
+
+public class ScannerInputWrapper extends InputWrapper {
+ private Scanner _scanner;
+
+ public ScannerInputWrapper(Scanner scanner) {
+ _scanner = scanner;
+ }
+
+ public ScannerInputWrapper(InputStream in) {
+ _scanner = new Scanner(in, "UTF-8");
+ }
+
+ @Override
+ public String nextLine() {
+ return _scanner.nextLine();
+ }
+
+ public Scanner getScanner() {
+ return _scanner;
+ }
+}
diff --git a/src/aya/obj/symbol/Symbol.java b/src/aya/obj/symbol/Symbol.java
index 2f031cf4..8149867f 100644
--- a/src/aya/obj/symbol/Symbol.java
+++ b/src/aya/obj/symbol/Symbol.java
@@ -25,6 +25,10 @@ public String name() {
}
}
+ public String unquotedName() {
+ return SymbolTable.getName(this);
+ }
+
@Override
public int hashCode() {
return _id;
diff --git a/src/aya/util/FileUtils.java b/src/aya/util/FileUtils.java
index ee8255ad..222b9d53 100644
--- a/src/aya/util/FileUtils.java
+++ b/src/aya/util/FileUtils.java
@@ -3,18 +3,19 @@
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
+import java.nio.file.Path;
import aya.AyaPrefs;
+import aya.StaticData;
public class FileUtils {
public static String readAllText(File file) throws IOException {
- return new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); // in Java 11 you can also do Files.readString(Path)
+ return new String(readAllBytes(file), StandardCharsets.UTF_8); // in Java 11 you can also do Files.readString(Path)
}
-
- public static byte[] readAllBytes(File file) throws IOException {
- return Files.readAllBytes(file.toPath());
+
+ public static byte[] readAllBytes(File file) throws IOException {
+ return StaticData.FILESYSTEM.readAllBytes(file);
}
/**
@@ -25,9 +26,16 @@ public static File resolveFile(String pathName) {
File file = new File(pathName);
return file.isAbsolute() ? file : new File(AyaPrefs.getWorkingDir(), pathName);
}
+
+ /** See resolveFile(String) */
+ public static Path resolvePath(String pathName) {
+ Path path = Path.of(pathName);
+ return path.isAbsolute() ? path : Path.of(AyaPrefs.getWorkingDir(), pathName);
+
+ }
public static boolean isFile(String str) {
- return resolveFile(str).isFile();
+ return StaticData.FILESYSTEM.isFile(resolveFile(str));
}
public static String resolveHome(String path) {
diff --git a/src/ui/AyaIDE.java b/src/ui/AyaIDE.java
index 1aaccd58..dbb3163d 100644
--- a/src/ui/AyaIDE.java
+++ b/src/ui/AyaIDE.java
@@ -22,10 +22,14 @@
import javax.swing.JOptionPane;
import javax.swing.JPanel;
+import aya.AyaStdIO;
import aya.AyaThread;
import aya.ExecutionRequest;
import aya.InteractiveAya;
import aya.StaticData;
+import aya.io.fs.FilesystemIO;
+import aya.io.http.HTTPDownloader;
+import aya.io.stdin.ScannerInputWrapper;
import aya.obj.block.StaticBlock;
import aya.parser.Parser;
import aya.parser.SourceString;
@@ -347,6 +351,11 @@ public InputStream getInputStream() {
}
public static void main(String[] args) {
+ // Set to System.in/out/err. If using the GUI, these will be changed to IDE GUI later
+ StaticData.IO = new AyaStdIO(System.out, System.err, System.in, new ScannerInputWrapper(System.in));
+ StaticData.HTTP_DOWNLOADER = new HTTPDownloader();
+ StaticData.FILESYSTEM = new FilesystemIO();
+
InteractiveAya iaya = InteractiveAya.createInteractiveSession(args);
boolean readstdin = StaticData.IO.isInputAvaiable();
@@ -363,7 +372,7 @@ public static void main(String[] args) {
// Aya Prefs
StaticData.IO.setOut(ide.getOutputStream());
StaticData.IO.setErr(ide.getOutputStream());
- StaticData.IO.setIn(ide.getInputStream());
+ StaticData.IO.setIn(ide.getInputStream(), new ScannerInputWrapper(ide.getInputStream()));
// InteractiveAya Prefs
iaya.setPromptText(false);
@@ -374,8 +383,8 @@ public static void main(String[] args) {
}
- int resultCode = iaya.loop();
- System.exit( resultCode );
+ int resultCode = iaya.loop();
+ System.exit(resultCode);
}
}
diff --git a/src/web/AyaWeb.java b/src/web/AyaWeb.java
new file mode 100644
index 00000000..c16bb18d
--- /dev/null
+++ b/src/web/AyaWeb.java
@@ -0,0 +1,114 @@
+package web;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+import org.teavm.jso.JSBody;
+import org.teavm.jso.JSFunctor;
+import org.teavm.jso.JSObject;
+
+import aya.AyaStdIO;
+import aya.StandaloneAya;
+import aya.StaticData;
+import aya.exceptions.parser.ParserException;
+import aya.ext.color.ColorInstructionStore;
+import aya.ext.date.DateInstructionStore;
+import aya.ext.json.JSONInstructionStore;
+import aya.ext.la.LinearAlgebraInstructionStore;
+import aya.io.StringOut;
+import aya.io.stdin.EmptyInputWrapper;
+
+public class AyaWeb {
+
+ private static final StringOut output = new StringOut();
+
+ public static void main(String[] args) {
+
+ //
+ // Set up StaticData
+ //
+ StaticData sd = StaticData.getInstance();
+
+ StaticData.IO = new AyaStdIO(
+ new PrintStream(output.getOutStream()),
+ new PrintStream(output.getErrStream()),
+ null,
+ new EmptyInputWrapper());
+
+ WebFilesystemIO fs = new WebFilesystemIO();
+ StaticData.FILESYSTEM = fs;
+
+ //
+ // Named Instructions
+ // Web build only supports a limited set of named instructions
+ //
+ WebAvailableNamedInstructionStore wsi = new WebAvailableNamedInstructionStore();
+ sd.addNamedInstructionStore(wsi);
+ sd.addNamedInstructionStore(new JSONInstructionStore());
+ sd.addNamedInstructionStore(new DateInstructionStore());
+ sd.addNamedInstructionStore(new ColorInstructionStore());
+ sd.addNamedInstructionStore(new LinearAlgebraInstructionStore());
+
+ //
+ // Exported Functions Implementation
+ //
+ exportRunIsolated(s -> {
+ StandaloneAya.runIsolated(s, StaticData.IO);
+ return output.flushOut() + output.flushErr();
+ });
+
+ exportAddFile((path, content) -> ((WebFilesystemIO)(StaticData.FILESYSTEM)).addFile(path, content));
+
+ exportListFiles(() -> String.join(",", fs.listFiles()));
+
+ exportLint(source -> {
+ ArrayList errors = StandaloneAya.lint(source);
+ if (errors.size() > 0) {
+ // TODO: The compile function stops after the first error
+ // if we update the parser to catch multiple errors, we will need to update this
+ ParserException err = errors.get(0);
+ return err.getSource().getIndex() + ":" + err.getSimpleMessage();
+ } else {
+ return "";
+ }
+ });
+
+ }
+
+ //
+ // Exported Functions
+ //
+
+ @JSBody(params = "runIsolated", script = "main.runIsolated = runIsolated;")
+ private static native void exportRunIsolated(ExportFunctionRunIsolated fn);
+
+ @JSBody(params = "addFile", script = "main.addFile = addFile;")
+ private static native void exportAddFile(ExportFunctionAddFile fn);
+
+ @JSBody(params = "listFiles", script = "main.listFiles = listFiles;")
+ private static native void exportListFiles(ExportFunctionListFiles fn);
+
+ @JSBody(params = "lint", script = "main.lint = lint;")
+ private static native void exportLint(ExportFunctionLint fn);
+
+}
+
+@JSFunctor
+interface ExportFunctionRunIsolated extends JSObject {
+ String call(String s);
+}
+
+@JSFunctor
+interface ExportFunctionAddFile extends JSObject {
+ void call(String path, String content);
+}
+
+@JSFunctor
+interface ExportFunctionListFiles extends JSObject {
+ String call();
+}
+
+@JSFunctor
+interface ExportFunctionLint extends JSObject {
+ String call(String source);
+}
\ No newline at end of file
diff --git a/src/web/WebAvailableNamedInstructionStore.java b/src/web/WebAvailableNamedInstructionStore.java
new file mode 100644
index 00000000..c5a1eb77
--- /dev/null
+++ b/src/web/WebAvailableNamedInstructionStore.java
@@ -0,0 +1,47 @@
+package web;
+
+import aya.eval.BlockEvaluator;
+import aya.exceptions.runtime.UnimplementedError;
+import aya.ext.sys.FileExistsSystemInstruction;
+import aya.instruction.named.NamedInstructionStore;
+import aya.instruction.named.NamedOperator;
+import aya.obj.list.List;
+import aya.obj.list.Str;
+
+public class WebAvailableNamedInstructionStore extends NamedInstructionStore {
+ /**
+ * This class provides some overrides for aya instructions so they work in the web implementation
+ *
+ * Only a small subset of instructions are supported
+ */
+
+ @Override
+ protected void init() {
+
+ addInstruction(new FileExistsSystemInstruction());
+
+ addInstruction(new NamedOperator("sys.ad", "get absolute path of aya dir") {
+ @Override
+ public void execute(BlockEvaluator blockEvaluator) {
+ blockEvaluator.push(List.fromStr(Str.EMPTY));
+ }
+ });
+
+ addInstruction(new NamedOperator("sys.wd", "get absolute path of working dir") {
+ @Override
+ public void execute(BlockEvaluator blockEvaluator) {
+ blockEvaluator.push(List.fromStr(Str.EMPTY));
+ }
+ });
+
+ addInstruction(new NamedOperator("debug.pause", "pause execution and open a repl") {
+ @Override
+ public void execute(BlockEvaluator blockEvaluator) {
+ // Unimplemented
+ throw new UnimplementedError();
+ }
+ });
+
+ }
+
+}
diff --git a/src/web/WebFilesystemIO.java b/src/web/WebFilesystemIO.java
new file mode 100644
index 00000000..305b6514
--- /dev/null
+++ b/src/web/WebFilesystemIO.java
@@ -0,0 +1,83 @@
+package web;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import aya.io.fs.AbstractFilesystemIO;
+
+public class WebFilesystemIO extends AbstractFilesystemIO {
+ /**
+ * A very basic virtual filesystem implementation
+ * This is primarily used to load the standard library in the web implementation
+ *
+ * Very few features outside of loading the standard library are supported
+ */
+
+ private static class FileData {
+ public byte[] data;
+
+ public FileData(byte[] bytes) {
+ data = bytes;
+ }
+
+ public static FileData fromString(String s) {
+ return new FileData(s.getBytes(StandardCharsets.UTF_8));
+ }
+ }
+
+ private HashMap _files;
+
+ public WebFilesystemIO() {
+ _files = new HashMap();
+ }
+
+ public void addFile(String path, String content) {
+ _files.put(toPath(new File(path)), FileData.fromString(content));
+ }
+
+ private static String toPath(File f) {
+ String p = f.getAbsolutePath();
+ if (!p.startsWith("/")) {
+ p = "/" + p;
+ }
+ return p;
+ }
+
+ @Override
+ public byte[] readAllBytes(File file) throws IOException {
+ FileData data = _files.get(toPath(file));
+ if (data == null) {
+ throw new IOException();
+ } else {
+ return data.data;
+ }
+ }
+
+ @Override
+ public void write(File file, byte[] bytes, StandardOpenOption create, StandardOpenOption truncateExisting)
+ throws IOException {
+ // TODO: open option and truncate option
+ _files.put(toPath(file), new FileData(bytes));
+ }
+
+ public ArrayList listFiles() {
+ ArrayList out = new ArrayList();
+ for (String s : _files.keySet()) {
+ out.add(s);
+ }
+ return out;
+ }
+
+ @Override
+ public boolean isFile(File file) {
+ String path = toPath(file);
+ String[] sections = path.split("/");
+ String basename = sections[sections.length-1];
+ return basename.contains(".") && _files.get(path) != null;
+ }
+
+}