Module: Hecks::Projections::ParserTable

Extended by:
Hecks::Projector::Target
Defined in:
lib/hecks/projections/parser_table.rb

Overview

THE RUST PARSER'S KEYWORD TABLE, projected from the chapter's own Syntax aggregate — "the parser's grammar knowledge is DERIVED from hecks's self-description, not hand-typed a second time", which is the anti-drift idea the whole Rust-parser plan rests on.

A REGISTERED TARGET NOW, rather than a module living inside its own bin/ script. It was already a projection in everything but call shape; this only stops it being a fifth way of spelling one.

Constant Summary collapse

KEYWORD_FIELDS =
%i[word context body inner opens fills status was resolves_via disambiguator].freeze
ARGUMENT_FIELDS =
%i[keyword context at named kind required fills selects
pair_key_fills pair_value_fills pairs_shape status variadic minimum].freeze

Class Method Summary collapse

Methods included from Hecks::Projector::Target

projection_declares, projection_emits, projection_key, projection_requires, projects_as

Class Method Details

.argument_row(row) ⇒ Object



55
56
57
58
# File 'lib/hecks/projections/parser_table.rb', line 55

def argument_row(row)
  fields = ARGUMENT_FIELDS.map { |field| rust_string(row[field]) }
  "    ArgumentRow { #{ARGUMENT_FIELDS.zip(fields).map { |name, value| "#{name}: #{value}" }.join(', ')} },"
end

.call(bluebook:, options: {}) ⇒ Object



19
# File 'lib/hecks/projections/parser_table.rb', line 19

def call(bluebook:, options: {}) = render(bluebook)

.closed_set_members(bluebook, name) ⇒ Object



38
# File 'lib/hecks/projections/parser_table.rb', line 38

def closed_set_members(bluebook, name) = rows(bluebook, name).map { |row| row[:name] }

.const_str_array(name, values) ⇒ Object



60
61
62
63
# File 'lib/hecks/projections/parser_table.rb', line 60

def const_str_array(name, values)
  lines = values.map { |value| "    #{rust_string(value)}," }
  "pub static #{name}: &[&str] = &[\n#{lines.join("\n")}\n];\n"
end

.keyword_row(row) ⇒ Object



50
51
52
53
# File 'lib/hecks/projections/parser_table.rb', line 50

def keyword_row(row)
  fields = KEYWORD_FIELDS.map { |field| rust_string(row[field]) }
  "    KeywordRow { #{KEYWORD_FIELDS.zip(fields).map { |name, value| "#{name}: #{value}" }.join(', ')} },"
end

.render(bluebook) ⇒ Object

S14, ADR 0026 — Keyword/Argument are genuine entities of Syntax now, dispatched (not merely declared) so their own status really is a lifecycle. SyntaxBoot.call reads the STILL-STATIC seed rows (KeywordSeed/ArgumentSeed), dispatches each one through the real admission/lifecycle door, and hands back the exact same shape rows used to read straight off the closed set — symbol keys, string values, status included — so nothing else in this file needed to change.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/hecks/projections/parser_table.rb', line 73

def render(bluebook)
  table     = Hecks::Bluebook::MetaValidator::SyntaxBoot.call
  keywords  = table[:keywords]
  arguments = table[:arguments]

  <<~RUST
    // GENERATED by bin/project_parser_table from
    // aggregate-local syntax tables under lib/hecks/language/ — DO NOT EDIT BY HAND.
    //
    // Re-run `bin/project_parser_table` after any syntax-table change.
    // spec/parser_table_spec.rb regenerates this into memory and fails the
    // normal `bundle exec rspec` suite the moment this file drifts from what
    // the language currently declares — see that spec and
    // bin/project_parser_table's own header for why this file is never
    // hand-edited.

    #[derive(Debug, Clone, Copy)]
    pub struct KeywordRow {
        pub word: &'static str,
        pub context: &'static str,
        pub body: &'static str,
        pub inner: &'static str,
        pub opens: &'static str,
        pub fills: &'static str,
        pub status: &'static str,
        pub was: &'static str,
        /// Which shared Hecks::Bluebook::DSL::RuleReference
        /// primitive this word's own bare form resolves through,
        /// once it has one ("hash_chain" / "owner_keyed" /
        /// "sibling_scan") — "" for every word that only ever
        /// declares.
        pub resolves_via: &'static str,
        /// Which disambiguator keyword argument a bare reference
        /// may supply — "declared_by" today, "" otherwise.
        pub disambiguator: &'static str,
    }

    #[derive(Debug, Clone, Copy)]
    pub struct ArgumentRow {
        pub keyword: &'static str,
        pub context: &'static str,
        pub at: &'static str,
        pub named: &'static str,
        pub kind: &'static str,
        pub required: &'static str,
        pub fills: &'static str,
        pub selects: &'static str,
        pub pair_key_fills: &'static str,
        pub pair_value_fills: &'static str,
        pub pairs_shape: &'static str,
        pub status: &'static str,
        pub variadic: &'static str,
        pub minimum: &'static str,
    }

    impl KeywordRow {
        /// An absent status reads as admitted — the grown-column convention
        /// syntax_conformance_spec.rb's own `status_of`/`live?` already use.
        pub fn live(&self) -> bool {
            matches!(self.status, "" | "admitted" | "deprecated")
        }
    }

    impl ArgumentRow {
        pub fn live(&self) -> bool {
            matches!(self.status, "" | "admitted" | "deprecated")
        }
    }

    pub static KEYWORDS: &[KeywordRow] = &[
    #{keywords.map { |row| keyword_row(row) }.join("\n")}
    ];

    pub static ARGUMENTS: &[ArgumentRow] = &[
    #{arguments.map { |row| argument_row(row) }.join("\n")}
    ];

    #{const_str_array('CONTEXTS', closed_set_members(bluebook, 'Context'))}
    #{const_str_array('BODIES', closed_set_members(bluebook, 'Body'))}
    #{const_str_array('ARGUMENT_KINDS', closed_set_members(bluebook, 'ArgumentKind'))}
    #{const_str_array('PAIRS_SHAPES', closed_set_members(bluebook, 'PairsShape'))}
    #{const_str_array('STATUSES', closed_set_members(bluebook, 'Status'))}
  RUST
end

.rows(bluebook, name) ⇒ Object

Every cell as text — exactly spec/syntax_conformance_spec.rb's own rows helper, reused rather than re-derived: a member's fields decode back through typed literal decoding on the way out of reconstruction, and this reads it back as what was written.



33
34
35
36
# File 'lib/hecks/projections/parser_table.rb', line 33

def rows(bluebook, name)
  syntax(bluebook).value_objects.find { |vo| vo.hecks_name == name }
        .members.map { |row| row.to_h.transform_values(&:to_s) }
end

.rust_string(value) ⇒ Object

A Rust string literal for one field's value — every field here is plain ASCII (a word, a context name, a digit, "true"/"false"), so this only has to be safe against the two characters Rust string literals themselves reserve.



48
# File 'lib/hecks/projections/parser_table.rb', line 48

def rust_string(value) = "\"#{value.to_s.gsub('\\', '\\\\\\\\').gsub('"', '\\"')}\""

.syntax(bluebook) ⇒ Object

THE CHAPTER IS HANDED OVER, not reached for. This used to open the grammar registry itself, which meant the projection could only ever project one chapter — the language's own. Taking it as an argument is what the projector protocol asks for, and it costs nothing.



27
# File 'lib/hecks/projections/parser_table.rb', line 27

def syntax(bluebook) = bluebook.aggregate("Syntax")