Skip to content

Commit

Permalink
Updates to path builtins to work on web
Browse files Browse the repository at this point in the history
  • Loading branch information
nick-paul committed Jan 12, 2025
1 parent 80a1e4b commit f539db8
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 12 deletions.
13 changes: 1 addition & 12 deletions base/importlib.aya
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,7 @@ Assume "sample.aya" has variables "foo", "bar", and "_baz"

.# Join paths a and b in a portable way
def importlib::_join_path {a::str b::str,
.# If b has a leading separator, refuse to join
b.[0]P :9s = {"Can't join, Path b has leading separator (is it an absolute path?): $b".D} ?

.# If a ends with a separator, don't add another
a.[-1]P :9s =! {
a :9s +
} {
a
} .?

.# Join with b
b +
a b :(sys.joinpath)
}

"a" "b" importlib._join_path ["a" :9s "b"] W :!
Expand Down
130 changes: 130 additions & 0 deletions src/web/StringPath.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package web;

import aya.exceptions.runtime.ValueError;

public class StringPath {
private final String path;

// Constructor
public StringPath(String path) {
if (path == null) {
throw new ValueError("Path cannot be null.");
}
this.path = normalizePath(path);
}

// Check if the path is absolute
public boolean isAbsolute() {
return path.startsWith("/");
}

// Get the parent path
public StringPath getParent() {
if (path.equals("/") || path.isEmpty()) {
return null; // Root or empty paths have no parent
}

int lastSlashIndex = path.lastIndexOf('/');
if (lastSlashIndex <= 0) {
return new StringPath("/"); // Parent of a top-level path is root
}

return new StringPath(path.substring(0, lastSlashIndex));
}

// Join the current path with another path
public StringPath join(StringPath other) {
if (other == null) {
throw new ValueError("Other path cannot be null.");
}

String otherPath = other.path;

// If the other path is absolute, return it as the result
if (other.isAbsolute()) {
return new StringPath(otherPath);
}

// Ensure no duplicate slashes
String basePath = this.path.endsWith("/") ? this.path.substring(0, this.path.length() - 1) : this.path;
String relativePath = otherPath.startsWith("/") ? otherPath.substring(1) : otherPath;

return new StringPath(basePath + "/" + relativePath);
}

public StringPath normalize() {
return new StringPath(normalizePath(this.path));
}

// Normalize a path (simplifies it by handling "..", ".", etc.)
private static String normalizePath(String path) {
String[] parts = path.split("/");
StringBuilder normalized = new StringBuilder();
int skip = 0;

for (int i = parts.length - 1; i >= 0; i--) {
String part = parts[i];

if (part.isEmpty() || part.equals(".")) {
continue;
}

if (part.equals("..")) {
skip++;
} else {
if (skip > 0) {
skip--;
} else {
normalized.insert(0, "/" + part);
}
}
}

String out = normalized.toString();

// Don't convert to absolute path
if (out.length() > 0) {
if (path.charAt(0) != '/' && out.charAt(0) == '/') {
out = out.substring(1);
}
}

return out;
}

public String getName() {
String str_path = normalize().toString();

// Find the last separator index
int lastSeparatorIndex = str_path.lastIndexOf('/');

// Return the substring after the last separator, or the whole path if no separator is found
return lastSeparatorIndex >= 0 ? path.substring(lastSeparatorIndex + 1) : path;
}

// Get the string representation of the path
@Override
public String toString() {
return path;
}

// Main method to test the class
public static void main(String[] args) {
StringPath path1 = new StringPath("/home/user/docs");
StringPath path2 = new StringPath("projects/file.txt");

System.out.println("Path 1: " + path1); // Output: /home/user/docs
System.out.println("Path 1 is absolute: " + path1.isAbsolute()); // Output: true
System.out.println("Path 1 parent: " + path1.getParent()); // Output: /home/user

System.out.println("Path 2: " + path2); // Output: projects/file.txt
System.out.println("Path 2 is absolute: " + path2.isAbsolute()); // Output: false
System.out.println("Path 2 parent: " + path2.getParent()); // Output: projects

StringPath joinedPath = path1.join(path2);
System.out.println("Joined Path: " + joinedPath); // Output: /home/user/docs/projects/file.txt

StringPath absolutePath = new StringPath("/etc/config");
System.out.println("Joined with absolute path: " + path1.join(absolutePath)); // Output: /etc/config
}
}
52 changes: 52 additions & 0 deletions src/web/WebAvailableNamedInstructionStore.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package web;

import java.io.File;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;

import aya.AyaPrefs;
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;
import aya.util.FileUtils;

public class WebAvailableNamedInstructionStore implements NamedInstructionStore {
/**
Expand All @@ -36,6 +40,54 @@ public void execute(BlockEvaluator blockEvaluator) {
blockEvaluator.push(List.fromStr(Str.EMPTY));
}
},

// Absolute Path
new NamedOperator("sys.abspath", "convert path string to absolute path. Normalize the path if '.' or '..' specifiers exist") {
@Override
public void execute(BlockEvaluator blockEvaluator) {
String path_str = blockEvaluator.pop().str();
StringPath path = new StringPath(path_str);

if (!path.isAbsolute()) {
path = new StringPath(AyaPrefs.getWorkingDir()).join(path);
}

path = path.normalize();

blockEvaluator.push(List.fromString(path.toString()));
}
},

// Join Path
new NamedOperator("sys.joinpath", "a::str b::str join two paths") {
@Override
public void execute(BlockEvaluator blockEvaluator) {
final String b = blockEvaluator.pop().str();
final String a = blockEvaluator.pop().str();

final StringPath path = new StringPath(a);

blockEvaluator.push(List.fromString(path.join(new StringPath(b)).toString()));
}
},

// Get parent name from path
new NamedOperator("sys.parent", "get the parent dir from a path") {
@Override
public void execute(BlockEvaluator blockEvaluator) {
final String path = blockEvaluator.pop().str();
blockEvaluator.push(List.fromString(new StringPath(path).getParent().toString()));
}
},

// Get file name from path
new NamedOperator("sys.get_filename", "get the filename from a path") {
@Override
public void execute(BlockEvaluator blockEvaluator) {
final String arg = blockEvaluator.pop().str();
blockEvaluator.push(List.fromString(new StringPath(arg).getName()));
}
},

new NamedOperator("debug.pause", "pause execution and open a repl") {
@Override
Expand Down
61 changes: 61 additions & 0 deletions src/web/WebFilesystemIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,66 @@ public boolean isFile(File file) {
String basename = sections[sections.length-1];
return basename.contains(".") && _files.get(path) != null;
}

// Implementation of normalize using only string functions
public static String normalizePath(String path) {
if (path == null || path.isEmpty()) {
return "";
}

// Split the path by "/"
String[] parts = path.split("/");
StringBuilder normalized = new StringBuilder();
int skip = 0;

// Traverse the path components in reverse
for (int i = parts.length - 1; i >= 0; i--) {
String part = parts[i];

// Ignore empty parts and current directory "."
if (part.isEmpty() || part.equals(".")) {
continue;
}

// Handle parent directory ".."
if (part.equals("..")) {
skip++;
} else {
if (skip > 0) {
skip--; // Skip this directory as it's canceled out by a ".."
} else {
normalized.insert(0, part).insert(0, "/");
}
}
}

// Handle root cases
String result = normalized.length() > 0 ? normalized.toString() : "/";
return result;
}


public static String joinPaths(String basePath, String relativePath) {
if (basePath == null || basePath.isEmpty()) {
return relativePath == null ? "" : relativePath;
}
if (relativePath == null || relativePath.isEmpty()) {
return basePath;
}

// Ensure basePath does not end with a trailing slash
if (basePath.endsWith("/")) {
basePath = basePath.substring(0, basePath.length() - 1);
}

// Ensure relativePath does not start with a leading slash
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}

// Combine the paths with a single slash in between
return basePath + "/" + relativePath;
}


}

0 comments on commit f539db8

Please sign in to comment.