Allow user to specify AST node prefix or suffix

Add ast_prefix and ast_suffix grammar statements.
This commit is contained in:
josh committed 2024-05-04 21:49:13 -04:00
1 parent d0f542cbd7
commit 153f9d28f8
10 files changed
+272 -53

No files matched your search

+1 -1
View File
@@ -120,7 +120,7 @@ class Propane
end
determine_possibly_empty_rulesets!(rule_sets)
rule_sets.each do |name, rule_set|
rule_set.finalize
rule_set.finalize(@grammar)
end
# Generate the lexer.
@lexer = Lexer.new(@grammar)
+18
View File
@@ -6,6 +6,8 @@ class Propane
IDENTIFIER_REGEX = /(?:[a-zA-Z]|_[a-zA-Z0-9])[a-zA-Z_0-9]*/
attr_reader :ast
attr_reader :ast_prefix
attr_reader :ast_suffix
attr_reader :modulename
attr_reader :patterns
attr_reader :rules
@@ -26,6 +28,8 @@ class Propane
@ptypes = {"default" => "void *"}
@prefix = "p_"
@ast = false
@ast_prefix = ""
@ast_suffix = ""
parse_grammar!
end
@@ -54,6 +58,8 @@ class Propane
elsif parse_comment_line!
elsif @mode.nil? && parse_mode_label!
elsif parse_ast_statement!
elsif parse_ast_prefix_statement!
elsif parse_ast_suffix_statement!
elsif parse_module_statement!
elsif parse_ptype_statement!
elsif parse_pattern_statement!
@@ -91,6 +97,18 @@ class Propane
end
end
def parse_ast_prefix_statement!
if md = consume!(/ast_prefix\s+(\w+)\s*;/)
@ast_prefix = md[1]
end
end
def parse_ast_suffix_statement!
if md = consume!(/ast_suffix\s+(\w+)\s*;/)
@ast_suffix = md[1]
end
end
def parse_module_statement!
if consume!(/module\s+/)
md = consume!(/([\w.]+)\s*/, "expected module name")
+40 -37
View File
@@ -3,6 +3,10 @@ class Propane
# A RuleSet collects all grammar rules of the same name.
class RuleSet
# @return [Array<Hash>]
# AST fields.
attr_reader :ast_fields
# @return [Integer]
# ID of the RuleSet.
attr_reader :id
@@ -76,6 +80,13 @@ class Propane
@_start_token_set
end
# Finalize a RuleSet after adding all Rules to it.
def finalize(grammar)
build_ast_fields(grammar)
end
private
# Build the set of AST fields for this RuleSet.
#
# This is an Array of Hashes. Each entry in the Array corresponds to a
@@ -84,46 +95,38 @@ class Propane
# a key. It may also have the field name without the positional suffix if
# that field only exists in one position across all Rules in the RuleSet.
#
# @return [Array<Hash>]
# AST fields.
def ast_fields
@_ast_fields ||=
begin
field_ast_node_indexes = {}
field_indexes_across_all_rules = {}
ast_node_fields = []
@rules.each do |rule|
rule.components.each_with_index do |component, i|
if component.is_a?(Token)
node_name = "Token"
else
node_name = component.name
end
field_name = "p#{node_name}#{i + 1}"
unless field_ast_node_indexes[field_name]
field_ast_node_indexes[field_name] = ast_node_fields.size
ast_node_fields << {field_name => node_name}
end
field_indexes_across_all_rules[node_name] ||= Set.new
field_indexes_across_all_rules[node_name] << field_ast_node_indexes[field_name]
rule.rule_set_node_field_index_map[i] = field_ast_node_indexes[field_name]
end
# @return [void]
def build_ast_fields(grammar)
field_ast_node_indexes = {}
field_indexes_across_all_rules = {}
@ast_fields = []
@rules.each do |rule|
rule.components.each_with_index do |component, i|
if component.is_a?(Token)
node_name = "Token"
else
node_name = component.name
end
field_indexes_across_all_rules.each do |node_name, indexes_across_all_rules|
if indexes_across_all_rules.size == 1
# If this field was only seen in one position across all rules,
# then add an alias to the positional field name that does not
# include the position.
ast_node_fields[indexes_across_all_rules.first]["p#{node_name}"] = node_name
end
struct_name = "#{grammar.ast_prefix}#{node_name}#{grammar.ast_suffix}"
field_name = "p#{node_name}#{i + 1}"
unless field_ast_node_indexes[field_name]
field_ast_node_indexes[field_name] = @ast_fields.size
@ast_fields << {field_name => struct_name}
end
ast_node_fields
field_indexes_across_all_rules[node_name] ||= Set.new
field_indexes_across_all_rules[node_name] << field_ast_node_indexes[field_name]
rule.rule_set_node_field_index_map[i] = field_ast_node_indexes[field_name]
end
end
# Finalize a RuleSet after adding all Rules to it.
def finalize
ast_fields
end
field_indexes_across_all_rules.each do |node_name, indexes_across_all_rules|
if indexes_across_all_rules.size == 1
# If this field was only seen in one position across all rules,
# then add an alias to the positional field name that does not
# include the position.
@ast_fields[indexes_across_all_rules.first]["p#{node_name}"] =
"#{grammar.ast_prefix}#{node_name}#{grammar.ast_suffix}"
end
end
end
end