Skip to content

Commit 08a55ce

Browse files
committed
Parser: Expose Prism Nodes in Herb Syntax Tree
1 parent 2ec7dc8 commit 08a55ce

File tree

182 files changed

+6936
-120
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

182 files changed

+6936
-120
lines changed

.rubocop.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ Metrics/CyclomaticComplexity:
5858
- lib/herb/ast/nodes.rb
5959
- lib/herb/cli.rb
6060
- lib/herb/engine.rb
61+
- lib/herb/prism_inspect.rb
6162
- lib/herb/engine/**/*.rb
63+
- templates/template.rb
6264

6365
Metrics/MethodLength:
6466
Max: 20
@@ -68,6 +70,7 @@ Metrics/MethodLength:
6870
- lib/herb/project.rb
6971
- lib/herb/engine.rb
7072
- lib/herb/engine/**/*.rb
73+
- lib/herb/prism_inspect.rb
7174
- templates/template.rb
7275
- test/fork_helper.rb
7376
- test/snapshot_utils.rb
@@ -83,6 +86,7 @@ Metrics/AbcSize:
8386
- lib/herb/engine.rb
8487
- lib/herb/engine/**/*.rb
8588
- lib/herb/token.rb
89+
- lib/herb/prism_inspect.rb
8690
- templates/template.rb
8791
- test/fork_helper.rb
8892
- test/snapshot_utils.rb
@@ -98,6 +102,7 @@ Metrics/ClassLength:
98102
- lib/herb/engine/**/*.rb
99103
- lib/herb/project.rb
100104
- lib/herb/visitor.rb
105+
- lib/herb/ast/nodes.rb
101106
- test/**/*_test.rb
102107

103108
Metrics/ModuleLength:
@@ -121,14 +126,17 @@ Metrics/ParameterLists:
121126
Exclude:
122127
- lib/herb/ast/node.rb
123128
- lib/herb/ast/nodes.rb
124-
- lib/herb/errors.rb
125129
- lib/herb/engine/validators/security_validator.rb
130+
- lib/herb/errors.rb
131+
- lib/herb/parser_options.rb
132+
- lib/herb/prism_inspect.rb
126133

127134
Metrics/PerceivedComplexity:
128135
Exclude:
129136
- lib/herb/ast/nodes.rb
130137
- lib/herb/cli.rb
131138
- lib/herb/project.rb
139+
- lib/herb/prism_inspect.rb
132140
- lib/herb/engine.rb
133141
- lib/herb/engine/**/*.rb
134142
- templates/template.rb

config.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ nodes:
369369
type: array
370370
kind: Node
371371
372+
- name: prism_context
373+
type: prism_context
374+
375+
- name: prism_node
376+
type: prism_node
377+
372378
- name: LiteralNode
373379
fields:
374380
- name: content
@@ -643,6 +649,9 @@ nodes:
643649
- name: valid
644650
type: boolean
645651
652+
- name: prism_node
653+
type: prism_node
654+
646655
- name: ERBEndNode
647656
fields:
648657
- name: tag_opening
@@ -686,6 +695,9 @@ nodes:
686695
# - name: predicate
687696
# type: prism_node
688697
698+
- name: prism_node
699+
type: prism_node
700+
689701
- name: statements
690702
type: array
691703
kind: Node
@@ -717,6 +729,9 @@ nodes:
717729
# - name: opener
718730
# type: prism_node
719731
732+
- name: prism_node
733+
type: prism_node
734+
720735
- name: body
721736
type: array
722737
kind: Node
@@ -765,6 +780,9 @@ nodes:
765780
# - name: predicate
766781
# type: prism_node
767782
783+
- name: prism_node
784+
type: prism_node
785+
768786
- name: conditions
769787
type: array
770788
kind: ERBWhenNode
@@ -795,6 +813,9 @@ nodes:
795813
# - name: predicate
796814
# type: prism_node
797815
816+
- name: prism_node
817+
type: prism_node
818+
798819
- name: conditions
799820
type: array
800821
kind: ERBInNode
@@ -821,6 +842,9 @@ nodes:
821842
# - name: predicate
822843
# type: prism_node
823844
845+
- name: prism_node
846+
type: prism_node
847+
824848
- name: statements
825849
type: array
826850
kind: Node
@@ -843,6 +867,9 @@ nodes:
843867
# - name: predicate
844868
# type: prism_node
845869
870+
- name: prism_node
871+
type: prism_node
872+
846873
- name: statements
847874
type: array
848875
kind: Node
@@ -868,6 +895,9 @@ nodes:
868895
# - name: collection
869896
# type: prism_node
870897
898+
- name: prism_node
899+
type: prism_node
900+
871901
- name: statements
872902
type: array
873903
kind: Node
@@ -928,6 +958,9 @@ nodes:
928958
- name: tag_closing
929959
type: token
930960
961+
- name: prism_node
962+
type: prism_node
963+
931964
- name: statements
932965
type: array
933966
kind: Node
@@ -965,6 +998,9 @@ nodes:
965998
# - name: predicate
966999
# type: prism_node
9671000
1001+
- name: prism_node
1002+
type: prism_node
1003+
9681004
- name: statements
9691005
type: array
9701006
kind: Node

ext/herb/extconf.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
$CFLAGS << " -DPRISM_EXCLUDE_PRETTYPRINT"
2828
$CFLAGS << " -DPRISM_EXCLUDE_JSON"
2929
$CFLAGS << " -DPRISM_EXCLUDE_PACK"
30-
$CFLAGS << " -DPRISM_EXCLUDE_SERIALIZATION"
3130

3231
herb_src_files = Dir.glob("#{$srcdir}/../../src/**/*.c").map { |file| file.delete_prefix("../../../../ext/herb/") }.sort
3332

ext/herb/extension.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,18 @@ static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) {
136136
}
137137
if (!NIL_P(action_view_helpers) && RTEST(action_view_helpers)) { parser_options.action_view_helpers = true; }
138138

139+
VALUE prism_nodes = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_nodes"));
140+
if (NIL_P(prism_nodes)) { prism_nodes = rb_hash_lookup(options, ID2SYM(rb_intern("prism_nodes"))); }
141+
if (!NIL_P(prism_nodes) && RTEST(prism_nodes)) { parser_options.prism_nodes = true; }
142+
143+
VALUE prism_nodes_deep = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_nodes_deep"));
144+
if (NIL_P(prism_nodes_deep)) { prism_nodes_deep = rb_hash_lookup(options, ID2SYM(rb_intern("prism_nodes_deep"))); }
145+
if (!NIL_P(prism_nodes_deep) && RTEST(prism_nodes_deep)) { parser_options.prism_nodes_deep = true; }
146+
147+
VALUE prism_program = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_program"));
148+
if (NIL_P(prism_program)) { prism_program = rb_hash_lookup(options, ID2SYM(rb_intern("prism_program"))); }
149+
if (!NIL_P(prism_program) && RTEST(prism_program)) { parser_options.prism_program = true; }
150+
139151
VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats"));
140152
if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); }
141153
if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; }

java/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ PRISM_BUILD = $(PRISM_PATH)/build
3030

3131
JAVAC = $(JAVA_HOME)/bin/javac
3232
JAVA_CMD = $(JAVA_HOME)/bin/java
33-
CFLAGS = -std=c99 -Wall -Wextra -fPIC -O2 -DHERB_EXCLUDE_PRETTYPRINT -DPRISM_EXCLUDE_PRETTYPRINT -DPRISM_EXCLUDE_JSON -DPRISM_EXCLUDE_PACK -DPRISM_EXCLUDE_SERIALIZATION
33+
CFLAGS = -std=c99 -Wall -Wextra -fPIC -O2 -DHERB_EXCLUDE_PRETTYPRINT -DPRISM_EXCLUDE_PRETTYPRINT -DPRISM_EXCLUDE_JSON -DPRISM_EXCLUDE_PACK
3434
INCLUDES = -I. -I$(SRC_DIR)/include -I$(PRISM_INCLUDE) $(JNI_INCLUDES)
3535
LDFLAGS = -shared
3636
LIBS = $(PRISM_BUILD)/libprism.a

java/herb_jni.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,30 @@ Java_org_herb_Herb_parse(JNIEnv* env, jclass clazz, jstring source, jobject opti
6868
jboolean actionViewHelpers = (*env)->CallBooleanMethod(env, options, getActionViewHelpers);
6969
parser_options.action_view_helpers = (actionViewHelpers == JNI_TRUE);
7070
}
71+
72+
jmethodID getPrismNodes =
73+
(*env)->GetMethodID(env, optionsClass, "isPrismNodes", "()Z");
74+
75+
if (getPrismNodes != NULL) {
76+
jboolean prismNodes = (*env)->CallBooleanMethod(env, options, getPrismNodes);
77+
parser_options.prism_nodes = (prismNodes == JNI_TRUE);
78+
}
79+
80+
jmethodID getPrismNodesDeep =
81+
(*env)->GetMethodID(env, optionsClass, "isPrismNodesDeep", "()Z");
82+
83+
if (getPrismNodesDeep != NULL) {
84+
jboolean prismNodesDeep = (*env)->CallBooleanMethod(env, options, getPrismNodesDeep);
85+
parser_options.prism_nodes_deep = (prismNodesDeep == JNI_TRUE);
86+
}
87+
88+
jmethodID getPrismProgram =
89+
(*env)->GetMethodID(env, optionsClass, "isPrismProgram", "()Z");
90+
91+
if (getPrismProgram != NULL) {
92+
jboolean prismProgram = (*env)->CallBooleanMethod(env, options, getPrismProgram);
93+
parser_options.prism_program = (prismProgram == JNI_TRUE);
94+
}
7195
}
7296

7397
hb_allocator_T allocator;

java/org/herb/HerbTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,55 @@ void testParserOptionsTrackWhitespace() {
102102
assertFalse(inspectWithout.contains("WhitespaceNode"));
103103
}
104104

105+
@Test
106+
void testParserOptionsPrismNodes() {
107+
String source = "<%= user.name %>";
108+
109+
ParseResult withPrism = Herb.parse(source, ParserOptions.create().prismNodes(true));
110+
String inspected = withPrism.inspect();
111+
112+
assertTrue(inspected.contains("prism_node_serialized:"));
113+
assertTrue(inspected.contains("CallNode"));
114+
}
115+
116+
@Test
117+
void testParserOptionsPrismNodesString() {
118+
String source = "<%= \"String\" %>";
119+
120+
ParseResult withPrism = Herb.parse(source, ParserOptions.create().prismNodes(true));
121+
String inspected = withPrism.inspect();
122+
123+
assertTrue(inspected.contains("prism_node_serialized:"));
124+
assertTrue(inspected.contains("StringNode"));
125+
assertTrue(inspected.contains("String"));
126+
}
127+
128+
@Test
129+
void testParserOptionsPrismProgram() {
130+
String source = "<%= \"hello\" %>";
131+
132+
ParseResult withPrism = Herb.parse(source, ParserOptions.create().prismProgram(true));
133+
String inspected = withPrism.inspect();
134+
135+
assertTrue(inspected.contains("prism_node_serialized:"));
136+
assertTrue(inspected.contains("ProgramNode"));
137+
}
138+
139+
@Test
140+
void testParserOptionsPrismNodesDeep() {
141+
String source = "<% if true %><%= \"yes\" %><% end %>";
142+
143+
ParseResult withDeep = Herb.parse(source, ParserOptions.create().prismNodes(true).prismNodesDeep(true));
144+
ParseResult withoutDeep = Herb.parse(source, ParserOptions.create().prismNodes(true).prismNodesDeep(false));
145+
146+
String inspectDeep = withDeep.inspect();
147+
String inspectShallow = withoutDeep.inspect();
148+
149+
assertTrue(inspectDeep.contains("prism_node_serialized:"));
150+
assertTrue(inspectShallow.contains("prism_node_serialized:"));
151+
assertTrue(inspectDeep.contains("IfNode"));
152+
}
153+
105154
@Test
106155
void testParserOptionsAnalyze() {
107156
String source = "<% if true %><div></div><% end %>";

java/org/herb/ParseResult.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public String inspect() {
5050
StringBuilder builder = new StringBuilder();
5151

5252
if (value != null) {
53-
builder.append(value.inspect());
53+
builder.append(value.inspect(source));
5454
}
5555

5656
if (hasErrors()) {

java/org/herb/ParserOptions.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ public class ParserOptions {
55
private boolean analyze = true;
66
private boolean strict = true;
77
private boolean actionViewHelpers = false;
8+
private boolean prismNodes = false;
9+
private boolean prismNodesDeep = false;
10+
private boolean prismProgram = false;
811

912
public ParserOptions() {}
1013

@@ -44,6 +47,33 @@ public boolean isActionViewHelpers() {
4447
return actionViewHelpers;
4548
}
4649

50+
public ParserOptions prismNodes(boolean value) {
51+
this.prismNodes = value;
52+
return this;
53+
}
54+
55+
public boolean isPrismNodes() {
56+
return prismNodes;
57+
}
58+
59+
public ParserOptions prismNodesDeep(boolean value) {
60+
this.prismNodesDeep = value;
61+
return this;
62+
}
63+
64+
public boolean isPrismNodesDeep() {
65+
return prismNodesDeep;
66+
}
67+
68+
public ParserOptions prismProgram(boolean value) {
69+
this.prismProgram = value;
70+
return this;
71+
}
72+
73+
public boolean isPrismProgram() {
74+
return prismProgram;
75+
}
76+
4777
public static ParserOptions create() {
4878
return new ParserOptions();
4979
}

java/org/herb/ast/BaseNode.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public java.util.List<Node> getErrors() {
3030
return errors;
3131
}
3232

33+
@Override
34+
public String inspect(String source) {
35+
return inspect();
36+
}
37+
3338
@Override
3439
public <R, C> R accept(NodeVisitor<R, C> visitor, C context) {
3540
return visitor.visitNode(this, context);
@@ -79,6 +84,13 @@ protected String inspectErrors(String prefix) {
7984
* Helper to format an array of nodes for tree inspection.
8085
*/
8186
protected String inspectArray(java.util.List<Node> array, String prefix) {
87+
return inspectArray(array, prefix, null);
88+
}
89+
90+
/**
91+
* Helper to format an array of nodes for tree inspection with source for Prism nodes.
92+
*/
93+
protected String inspectArray(java.util.List<Node> array, String prefix, String source) {
8294
if (array == null) return "∅\n";
8395
if (array.isEmpty()) return "[]\n";
8496

@@ -92,7 +104,7 @@ protected String inspectArray(java.util.List<Node> array, String prefix) {
92104
String nextPrefix = isLast ? " " : "│ ";
93105

94106
if (item != null) {
95-
String tree = item.inspect();
107+
String tree = source != null ? item.inspect(source) : item.inspect();
96108

97109
if (tree.endsWith("\n")) {
98110
tree = tree.substring(0, tree.length() - 1);
@@ -119,8 +131,15 @@ protected String inspectArray(java.util.List<Node> array, String prefix) {
119131
* Helper to format a single node for tree inspection.
120132
*/
121133
protected String inspectNode(Node node, String prefix) {
134+
return inspectNode(node, prefix, null);
135+
}
136+
137+
/**
138+
* Helper to format a single node for tree inspection with source for Prism nodes.
139+
*/
140+
protected String inspectNode(Node node, String prefix, String source) {
122141
if (node == null) return "∅\n";
123-
String tree = node.inspect();
142+
String tree = source != null ? node.inspect(source) : node.inspect();
124143

125144
if (tree.endsWith("\n")) {
126145
tree = tree.substring(0, tree.length() - 1);

0 commit comments

Comments
 (0)