diff --git a/docs/docs/bindings/java/index.md b/docs/docs/bindings/java/index.md
index 37ab4162f..265291836 100644
--- a/docs/docs/bindings/java/index.md
+++ b/docs/docs/bindings/java/index.md
@@ -60,10 +60,7 @@ public class Example {
String source = "
<%= user.name %>
";
ParseResult result = Herb.parse(source);
-
- if (result.getValue() != null) {
- System.out.println(result.getValue().treeInspect());
- }
+ System.out.println(result.value.inspect());
}
}
```
@@ -85,7 +82,7 @@ public class LexExample {
LexResult result = Herb.lex(source);
- for (Token token : result.getTokens()) {
+ for (Token token : result.tokens) {
System.out.println(token.inspect());
}
}
diff --git a/docs/docs/bindings/java/reference.md b/docs/docs/bindings/java/reference.md
index 76006619c..f941822a2 100644
--- a/docs/docs/bindings/java/reference.md
+++ b/docs/docs/bindings/java/reference.md
@@ -34,7 +34,7 @@ import org.herb.Token;
String source = "Hello <%= user.name %>
";
LexResult result = Herb.lex(source);
-for (Token token : result.getTokens()) {
+for (Token token : result.tokens) {
System.out.println(token.inspect());
}
// Output:
@@ -51,8 +51,8 @@ The `LexResult` class provides access to the lexed tokens:
```java
public class LexResult {
- public List getTokens();
- public String getSource();
+ public List tokens;
+ public String source;
public int getTokenCount();
public boolean isEmpty();
}
@@ -73,9 +73,7 @@ String source = "Hello <%= user.name %>
";
ParseResult result = Herb.parse(source);
-if (result.getValue() != null) {
- System.out.println(result.getValue().treeInspect());
-}
+System.out.println(result.inspect();
// Output:
// @ DocumentNode (location: (1:0)-(1:29))
// └── children: (1 item)
@@ -133,12 +131,12 @@ The `ParseResult` class provides access to the parsed AST and any errors:
```java
public class ParseResult {
- public Node getValue();
- public List getErrors();
- public String getSource();
+ public Node value;
+ public List errors;
+ public String source;
public boolean hasErrors();
public int getErrorCount();
- public boolean isSuccess();
+ public boolean isSuccessful();
}
```
@@ -278,7 +276,7 @@ public interface Node {
String getNodeType();
Location getLocation();
List getErrors();
- String treeInspect();
+ String inspect();
T accept(Visitor visitor);
}
```
@@ -291,8 +289,8 @@ Parse errors are accessible through the `ParseResult`:
ParseResult result = Herb.parse(source);
if (result.hasErrors()) {
- for (Node error : result.getErrors()) {
- System.out.println(error.treeInspect());
+ for (Node error : result.recursiveErrors()) {
+ System.out.println(error.inspect());
}
}
```
diff --git a/java/org/herb/CLI.java b/java/org/herb/CLI.java
index a66cbe19f..7380b7481 100644
--- a/java/org/herb/CLI.java
+++ b/java/org/herb/CLI.java
@@ -41,33 +41,12 @@ public static void main(String[] args) {
case "lex":
LexResult lexResult = Herb.lex(source);
-
- if (lexResult.getTokens() != null) {
- for (Token token : lexResult.getTokens()) {
- System.out.println(token.inspect());
- }
- }
+ System.out.print(lexResult.inspect());
break;
case "parse":
ParseResult parseResult = Herb.parse(source);
-
- if (parseResult.getValue() != null) {
- System.out.print(parseResult.getValue().treeInspect());
- }
-
- if (parseResult.getErrors() != null && !parseResult.getErrors().isEmpty()) {
- System.out.println("Errors:");
-
- for (Object error : parseResult.getErrors()) {
- if (error instanceof org.herb.ast.Node) {
- System.out.println(((org.herb.ast.Node) error).treeInspect());
- } else {
- System.out.println(" " + error);
- }
- }
- }
-
+ System.out.print(parseResult.inspect());
break;
case "ruby":
diff --git a/java/org/herb/LexResult.java b/java/org/herb/LexResult.java
index a3621d7e9..4ac62a226 100644
--- a/java/org/herb/LexResult.java
+++ b/java/org/herb/LexResult.java
@@ -4,26 +4,14 @@
import java.util.List;
public class LexResult {
- private final List tokens;
- private final String source;
+ public final List tokens;
+ public final String source;
public LexResult(List tokens, String source) {
this.tokens = Collections.unmodifiableList(tokens);
this.source = source;
}
- public List getTokens() {
- return tokens;
- }
-
- public String getSource() {
- return source;
- }
-
- public int getTokenCount() {
- return tokens.size();
- }
-
public boolean isEmpty() {
return tokens.isEmpty();
}
@@ -32,4 +20,14 @@ public boolean isEmpty() {
public String toString() {
return String.format("LexResult{tokens=%d, source=%d chars}", tokens.size(), source.length());
}
+
+ public String inspect() {
+ StringBuilder builder = new StringBuilder();
+
+ for (Token token : tokens) {
+ builder.append(token.inspect()).append("\n");
+ }
+
+ return builder.toString();
+ }
}
diff --git a/java/org/herb/ParseResult.java b/java/org/herb/ParseResult.java
index 1fc56da02..9fa0acf40 100644
--- a/java/org/herb/ParseResult.java
+++ b/java/org/herb/ParseResult.java
@@ -1,13 +1,15 @@
package org.herb;
import org.herb.ast.Node;
+
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ParseResult {
- private final Node value;
+ public final Node value;
private final List errors;
- private final String source;
+ public final String source;
public ParseResult(Node value, List errors, String source) {
this.value = value;
@@ -15,27 +17,27 @@ public ParseResult(Node value, List errors, String source) {
this.source = source;
}
- public Node getValue() {
- return value;
- }
+ public List recursiveErrors() {
+ List result = new ArrayList<>();
- public List getErrors() {
- return errors;
- }
+ result.addAll(errors);
- public String getSource() {
- return source;
+ if (value != null) {
+ result.addAll(value.recursiveErrors());
+ }
+
+ return result;
}
public boolean hasErrors() {
- return !errors.isEmpty();
+ return !recursiveErrors().isEmpty();
}
public int getErrorCount() {
- return errors.size();
+ return recursiveErrors().size();
}
- public boolean isSuccess() {
+ public boolean isSuccessful() {
return errors.isEmpty();
}
@@ -43,4 +45,22 @@ public boolean isSuccess() {
public String toString() {
return String.format("ParseResult{errors=%d, source=%d chars}", errors.size(), source.length());
}
+
+ public String inspect() {
+ StringBuilder builder = new StringBuilder();
+
+ if (value != null) {
+ builder.append(value.inspect());
+ }
+
+ if (hasErrors()) {
+ builder.append("\n\nErrors:\n");
+
+ for (Node error : recursiveErrors()) {
+ builder.append(error.inspect()).append("\n");
+ }
+ }
+
+ return builder.toString();
+ }
}
diff --git a/java/org/herb/ast/BaseNode.java b/java/org/herb/ast/BaseNode.java
index 9e7bb5ecf..a142f16e3 100644
--- a/java/org/herb/ast/BaseNode.java
+++ b/java/org/herb/ast/BaseNode.java
@@ -59,7 +59,7 @@ protected String inspectErrors(String prefix) {
String nextPrefix = isLast ? " " : "│ ";
if (error != null) {
- String tree = error.treeInspect();
+ String tree = error.inspect();
if (tree.endsWith("\n")) {
tree = tree.substring(0, tree.length() - 1);
}
@@ -92,7 +92,7 @@ protected String inspectArray(java.util.List array, String prefix) {
String nextPrefix = isLast ? " " : "│ ";
if (item != null) {
- String tree = item.treeInspect();
+ String tree = item.inspect();
if (tree.endsWith("\n")) {
tree = tree.substring(0, tree.length() - 1);
@@ -120,7 +120,7 @@ protected String inspectArray(java.util.List array, String prefix) {
*/
protected String inspectNode(Node node, String prefix) {
if (node == null) return "∅\n";
- String tree = node.treeInspect();
+ String tree = node.inspect();
if (tree.endsWith("\n")) {
tree = tree.substring(0, tree.length() - 1);
diff --git a/java/org/herb/ast/ErrorNode.java b/java/org/herb/ast/ErrorNode.java
index 6ebf7be84..0c5d0724e 100644
--- a/java/org/herb/ast/ErrorNode.java
+++ b/java/org/herb/ast/ErrorNode.java
@@ -2,24 +2,24 @@
import org.herb.Location;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* Simple wrapper for parser errors returned in ParseResult.
* This is a lightweight representation that can be cast to specific error types if needed.
*/
public class ErrorNode extends BaseNode {
- private final String errorMessage;
+ public final String errorMessage;
public ErrorNode(String type, Location location, String errorMessage) {
super(type, location, null);
this.errorMessage = errorMessage;
}
- public String getErrorMessage() {
- return errorMessage;
- }
-
@Override
- public String treeInspect() {
+ public String inspect() {
StringBuilder output = new StringBuilder();
output.append("@ ").append(type).append(" ");
@@ -30,6 +30,15 @@ public String treeInspect() {
return output.toString();
}
+ @Override
+ public List recursiveErrors() {
+ if (errors != null && !errors.isEmpty()) {
+ return new ArrayList<>(errors);
+ }
+
+ return Collections.emptyList();
+ }
+
@Override
public String toString() {
return String.format("ErrorNode{type='%s', message='%s', location=%s}", type, errorMessage, location);
diff --git a/java/org/herb/ast/Node.java b/java/org/herb/ast/Node.java
index 4af7282fb..662fcd2ca 100644
--- a/java/org/herb/ast/Node.java
+++ b/java/org/herb/ast/Node.java
@@ -1,6 +1,7 @@
package org.herb.ast;
import org.herb.Location;
+import java.util.List;
/**
* Base interface for all AST nodes.
@@ -29,5 +30,10 @@ public interface Node {
/**
* Return a tree-like string representation of this node with all its fields.
*/
- String treeInspect();
+ String inspect();
+
+ /**
+ * Get all errors from this node and recursively from all child nodes.
+ */
+ List recursiveErrors();
}
diff --git a/templates/java/org/herb/ast/Errors.java.erb b/templates/java/org/herb/ast/Errors.java.erb
index 13c6bbd1b..9daa121fa 100644
--- a/templates/java/org/herb/ast/Errors.java.erb
+++ b/templates/java/org/herb/ast/Errors.java.erb
@@ -34,7 +34,7 @@ abstract class HerbError {
return String.format("HerbError{type='%s', message='%s', location=%s}", type, message, location);
}
- public abstract String treeInspect();
+ public abstract String inspect();
protected String indent(String str, int level) {
if (level == 0) return str;
@@ -90,7 +90,7 @@ class <%= error.name %> extends HerbError {
<%- end -%>
<%- end -%>
@Override
- public String treeInspect() {
+ public String inspect() {
StringBuilder output = new StringBuilder();
output.append("@ <%= error.name %> ");
@@ -109,7 +109,7 @@ class <%= error.name %> extends HerbError {
output.append("\n");
<%- when Herb::Template::TokenField -%>
output.append("<%= symbol %> <%= field.name %>: ");
- output.append(<%= field.name %> != null ? <%= field.name %>.treeInspect() : "∅");
+ output.append(<%= field.name %> != null ? <%= field.name %>.inspect() : "∅");
output.append("\n");
<%- end -%>
<%- end -%>
diff --git a/templates/java/org/herb/ast/Nodes.java.erb b/templates/java/org/herb/ast/Nodes.java.erb
index d0e91b51c..05f9db9ba 100644
--- a/templates/java/org/herb/ast/Nodes.java.erb
+++ b/templates/java/org/herb/ast/Nodes.java.erb
@@ -3,6 +3,7 @@ package org.herb.ast;
import org.herb.Location;
import org.herb.Token;
+import java.util.ArrayList;
import java.util.List;
<%- nodes.each do |node| -%>
@@ -136,7 +137,7 @@ class <%= node.name %> extends BaseNode {
}
@Override
- public String treeInspect() {
+ public String inspect() {
StringBuilder output = new StringBuilder();
output.append("@ <%= node.name %> ").append(location != null ? "(location: " + location.toString() + ")" : "no-location").append("\n");
@@ -177,6 +178,38 @@ class <%= node.name %> extends BaseNode {
return output.toString();
}
+ @Override
+ public List recursiveErrors() {
+ List result = new ArrayList<>();
+
+ if (errors != null) {
+ result.addAll(errors);
+ }
+
+ <%- if node.fields.any? -%>
+ <%- node.fields.each do |field| -%>
+ <%- if field.is_a?(Herb::Template::ArrayField) -%>
+
+ if (<%= field.name %> != null) {
+ for (Node child : <%= field.name %>) {
+ if (child != null) {
+ result.addAll(child.recursiveErrors());
+ }
+ }
+ }
+
+ <%- elsif field.is_a?(Herb::Template::NodeField) -%>
+ if (<%= field.name %> != null) {
+ result.addAll(<%= field.name %>.recursiveErrors());
+ }
+
+ <%- end -%>
+ <%- end -%>
+ <%- end -%>
+
+ return result;
+ }
+
@Override
public String toString() {
return "<%= node.name %> {}";