From c5c11d4fc8a60a240b8f08e8cfffcf6919bda48b Mon Sep 17 00:00:00 2001 From: Marco Roth Date: Tue, 11 Nov 2025 21:05:30 +0100 Subject: [PATCH] Java: Clean up public API --- docs/docs/bindings/java/index.md | 7 +--- docs/docs/bindings/java/reference.md | 24 +++++------ java/org/herb/CLI.java | 25 +---------- java/org/herb/LexResult.java | 26 ++++++------ java/org/herb/ParseResult.java | 46 +++++++++++++++------ java/org/herb/ast/BaseNode.java | 6 +-- java/org/herb/ast/ErrorNode.java | 21 +++++++--- java/org/herb/ast/Node.java | 8 +++- templates/java/org/herb/ast/Errors.java.erb | 6 +-- templates/java/org/herb/ast/Nodes.java.erb | 35 +++++++++++++++- 10 files changed, 122 insertions(+), 82 deletions(-) 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 %> {}";