From 7f51d8c4bc8e4f9482da5ce44b86c1f67628b53e Mon Sep 17 00:00:00 2001 From: kolen Date: Sat, 17 Feb 2024 02:08:57 +0300 Subject: [PATCH] Add support for splat attributes --- grammar.js | 28 ++- src/grammar.json | 422 ++++++++++++++++++++++--------------- src/node-types.json | 24 ++- test/corpus/attributes.txt | 30 +++ 4 files changed, 325 insertions(+), 179 deletions(-) diff --git a/grammar.js b/grammar.js index 747c955..4bef2c1 100644 --- a/grammar.js +++ b/grammar.js @@ -2,13 +2,16 @@ // Also: https://rdoc.info/gems/slim/frames const make_attr_delimited = (token_suffix) => { - return ($) => seq( - field('name', $.attr_name), - field('assignment', choice($.attr_assignment, $.attr_assignment_noescape)), - field('value', choice( - alias($._attr_value_quoted, $.attr_value), - alias($[`_attr_value_ruby_${token_suffix}`], $.attr_value) - )) + return ($) => choice( + seq('*', alias($[`_attr_value_ruby_${token_suffix}`], $.attr_splat)), + seq( + field('name', $.attr_name), + field('assignment', choice($.attr_assignment, $.attr_assignment_noescape)), + field('value', choice( + alias($._attr_value_quoted, $.attr_value), + alias($[`_attr_value_ruby_${token_suffix}`], $.attr_value) + )) + ) ) } @@ -143,10 +146,13 @@ module.exports = grammar({ _attr_delimited_s: make_attr_delimited('s'), _attr_delimited_b: make_attr_delimited('b'), - attr: $ => seq( - field('name', $.attr_name), - field('assignment', choice($.attr_assignment, $.attr_assignment_noescape)), - field('value', $.attr_value), + attr: $ => choice( + seq('*', alias($._attr_value_ruby, $.attr_splat)), + seq( + field('name', $.attr_name), + field('assignment', choice($.attr_assignment, $.attr_assignment_noescape)), + field('value', $.attr_value), + ), ), attr_name: $ => $._attr_name, _attr_name: $ => /[a-zA-Z0-9_-]+/, // TODO: very wrong diff --git a/src/grammar.json b/src/grammar.json index 3674fba..7f1a1ed 100644 --- a/src/grammar.json +++ b/src/grammar.json @@ -594,211 +594,303 @@ ] }, "_attr_delimited_p": { - "type": "SEQ", + "type": "CHOICE", "members": [ { - "type": "FIELD", - "name": "name", - "content": { - "type": "SYMBOL", - "name": "attr_name" - } - }, - { - "type": "FIELD", - "name": "assignment", - "content": { - "type": "CHOICE", - "members": [ - { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "*" + }, + { + "type": "ALIAS", + "content": { "type": "SYMBOL", - "name": "attr_assignment" + "name": "_attr_value_ruby_p" }, - { - "type": "SYMBOL", - "name": "attr_assignment_noescape" - } - ] - } + "named": true, + "value": "attr_splat" + } + ] }, { - "type": "FIELD", - "name": "value", - "content": { - "type": "CHOICE", - "members": [ - { - "type": "ALIAS", - "content": { - "type": "SYMBOL", - "name": "_attr_value_quoted" - }, - "named": true, - "value": "attr_value" - }, - { - "type": "ALIAS", - "content": { - "type": "SYMBOL", - "name": "_attr_value_ruby_p" - }, - "named": true, - "value": "attr_value" + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "name", + "content": { + "type": "SYMBOL", + "name": "attr_name" } - ] - } + }, + { + "type": "FIELD", + "name": "assignment", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "attr_assignment" + }, + { + "type": "SYMBOL", + "name": "attr_assignment_noescape" + } + ] + } + }, + { + "type": "FIELD", + "name": "value", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_attr_value_quoted" + }, + "named": true, + "value": "attr_value" + }, + { + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_attr_value_ruby_p" + }, + "named": true, + "value": "attr_value" + } + ] + } + } + ] } ] }, "_attr_delimited_s": { - "type": "SEQ", + "type": "CHOICE", "members": [ { - "type": "FIELD", - "name": "name", - "content": { - "type": "SYMBOL", - "name": "attr_name" - } - }, - { - "type": "FIELD", - "name": "assignment", - "content": { - "type": "CHOICE", - "members": [ - { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "*" + }, + { + "type": "ALIAS", + "content": { "type": "SYMBOL", - "name": "attr_assignment" + "name": "_attr_value_ruby_s" }, - { - "type": "SYMBOL", - "name": "attr_assignment_noescape" - } - ] - } + "named": true, + "value": "attr_splat" + } + ] }, { - "type": "FIELD", - "name": "value", - "content": { - "type": "CHOICE", - "members": [ - { - "type": "ALIAS", - "content": { - "type": "SYMBOL", - "name": "_attr_value_quoted" - }, - "named": true, - "value": "attr_value" - }, - { - "type": "ALIAS", - "content": { - "type": "SYMBOL", - "name": "_attr_value_ruby_s" - }, - "named": true, - "value": "attr_value" + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "name", + "content": { + "type": "SYMBOL", + "name": "attr_name" } - ] - } + }, + { + "type": "FIELD", + "name": "assignment", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "attr_assignment" + }, + { + "type": "SYMBOL", + "name": "attr_assignment_noescape" + } + ] + } + }, + { + "type": "FIELD", + "name": "value", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_attr_value_quoted" + }, + "named": true, + "value": "attr_value" + }, + { + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_attr_value_ruby_s" + }, + "named": true, + "value": "attr_value" + } + ] + } + } + ] } ] }, "_attr_delimited_b": { - "type": "SEQ", + "type": "CHOICE", "members": [ { - "type": "FIELD", - "name": "name", - "content": { - "type": "SYMBOL", - "name": "attr_name" - } - }, - { - "type": "FIELD", - "name": "assignment", - "content": { - "type": "CHOICE", - "members": [ - { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "*" + }, + { + "type": "ALIAS", + "content": { "type": "SYMBOL", - "name": "attr_assignment" + "name": "_attr_value_ruby_b" }, - { - "type": "SYMBOL", - "name": "attr_assignment_noescape" - } - ] - } + "named": true, + "value": "attr_splat" + } + ] }, { - "type": "FIELD", - "name": "value", - "content": { - "type": "CHOICE", - "members": [ - { - "type": "ALIAS", - "content": { - "type": "SYMBOL", - "name": "_attr_value_quoted" - }, - "named": true, - "value": "attr_value" - }, - { - "type": "ALIAS", - "content": { - "type": "SYMBOL", - "name": "_attr_value_ruby_b" - }, - "named": true, - "value": "attr_value" + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "name", + "content": { + "type": "SYMBOL", + "name": "attr_name" } - ] - } + }, + { + "type": "FIELD", + "name": "assignment", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "attr_assignment" + }, + { + "type": "SYMBOL", + "name": "attr_assignment_noescape" + } + ] + } + }, + { + "type": "FIELD", + "name": "value", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_attr_value_quoted" + }, + "named": true, + "value": "attr_value" + }, + { + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_attr_value_ruby_b" + }, + "named": true, + "value": "attr_value" + } + ] + } + } + ] } ] }, "attr": { - "type": "SEQ", + "type": "CHOICE", "members": [ { - "type": "FIELD", - "name": "name", - "content": { - "type": "SYMBOL", - "name": "attr_name" - } + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "*" + }, + { + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_attr_value_ruby" + }, + "named": true, + "value": "attr_splat" + } + ] }, { - "type": "FIELD", - "name": "assignment", - "content": { - "type": "CHOICE", - "members": [ - { + "type": "SEQ", + "members": [ + { + "type": "FIELD", + "name": "name", + "content": { "type": "SYMBOL", - "name": "attr_assignment" - }, - { + "name": "attr_name" + } + }, + { + "type": "FIELD", + "name": "assignment", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "attr_assignment" + }, + { + "type": "SYMBOL", + "name": "attr_assignment_noescape" + } + ] + } + }, + { + "type": "FIELD", + "name": "value", + "content": { "type": "SYMBOL", - "name": "attr_assignment_noescape" + "name": "attr_value" } - ] - } - }, - { - "type": "FIELD", - "name": "value", - "content": { - "type": "SYMBOL", - "name": "attr_value" - } + } + ] } ] }, diff --git a/src/node-types.json b/src/node-types.json index f326fdf..9ded81d 100644 --- a/src/node-types.json +++ b/src/node-types.json @@ -5,7 +5,7 @@ "fields": { "assignment": { "multiple": false, - "required": true, + "required": false, "types": [ { "type": "attr_assignment", @@ -19,7 +19,7 @@ }, "name": { "multiple": false, - "required": true, + "required": false, "types": [ { "type": "attr_name", @@ -29,7 +29,7 @@ }, "value": { "multiple": false, - "required": true, + "required": false, "types": [ { "type": "attr_value", @@ -37,6 +37,16 @@ } ] } + }, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "attr_splat", + "named": true + } + ] } }, { @@ -544,6 +554,10 @@ "type": ")", "named": false }, + { + "type": "*", + "named": false + }, { "type": "-", "named": false @@ -584,6 +598,10 @@ "type": "attr_shortcut_custom", "named": true }, + { + "type": "attr_splat", + "named": true + }, { "type": "basic", "named": false diff --git a/test/corpus/attributes.txt b/test/corpus/attributes.txt index 8083896..5427da3 100644 --- a/test/corpus/attributes.txt +++ b/test/corpus/attributes.txt @@ -139,3 +139,33 @@ a[ (attr_name) (attr_assignment) (attr_value))))) + +================================ +Splat attributes - non-delimited +================================ + +a *attrs + +----- + +(source_file + (element + (tag_name) + (attrs + (attr + (attr_splat))))) + +============================ +Splat attributes - delimited +============================ + +a[*attrs] + +----- + +(source_file + (element + (tag_name) + (attrs + (attr + (attr_splat)))))