diff --git a/grammar.js b/grammar.js index 7395f02..7283a5e 100644 --- a/grammar.js +++ b/grammar.js @@ -32,8 +32,16 @@ const attrAlias = mkAlias(($) => $.attribute); // mkAttrCont :: ($ -> Rule) -> ($ -> Rule) -> $ -> Rule const mkAttrCont = (onValue) => (onKey) => ($) => seq(onKey($), $._colon, onValue($)); -const mkBaseAttr = (onKey) => - mkAttrCont(($) => $.attr_value)(attrKeyAlias(onKey)); +const mkAttr = (onKey) => mkAttrCont(($) => $.attr_value)(attrKeyAlias(onKey)); +const mkListAttr = (onKey) => + mkAttrCont( + either( + ($) => $.attr_value_list, + ($) => $.attr_value + ) + )(attrKeyAlias(onKey)); + +const either = (onLeft, onRight) => ($) => choice(onLeft($), onRight($)); module.exports = grammar({ name: "d2", @@ -59,6 +67,7 @@ module.exports = grammar({ [$._classes_block], [$._classes_item_block], [$.class_list], + [$.attr_value_list], [$._text_block_attrs], ], @@ -216,7 +225,7 @@ module.exports = grammar({ // attributes - _root_attribute: mkBaseAttr(($) => $._root_attr_key), + _root_attribute: mkAttr(($) => $._root_attr_key), _root_attr_key: ($) => choice( @@ -249,7 +258,10 @@ module.exports = grammar({ _class_name: ($) => alias($.shape_key, $.class_name), - _base_shape_attribute: mkBaseAttr(($) => $._shape_attr_key), + _base_shape_attribute: either( + mkListAttr(($) => $._shape_list_attr_key), + mkAttr(($) => $._shape_attr_key) + ), _shape_attr_key: ($) => prec( @@ -260,9 +272,6 @@ module.exports = grammar({ "label", "link", "tooltip", - // sql - "constraint", - // image "icon", "width", "height", @@ -271,6 +280,8 @@ module.exports = grammar({ ) ), + _shape_list_attr_key: ($) => prec(PREC.ATTRIBUTE_KEY, "constraint"), + _style_attribute: ($) => prec( PREC.ATTRIBUTE, @@ -285,7 +296,7 @@ module.exports = grammar({ _style_attribute_block: mkBlock(attrAlias(($) => $._inner_style_attribute)), - _inner_style_attribute: mkBaseAttr(($) => $._style_attr_key), + _inner_style_attribute: mkAttr(($) => $._style_attr_key), _grid_attr_key: ($) => choice( @@ -321,7 +332,7 @@ module.exports = grammar({ "text-transform" ), - _text_shape_attribute: mkBaseAttr(($) => $._text_attr_key), + _text_shape_attribute: mkAttr(($) => $._text_attr_key), _text_attr_key: ($) => "near", @@ -355,6 +366,8 @@ module.exports = grammar({ label: ($) => choice($.string, $._unquoted_string), + attr_value_list: mkList(($) => $.attr_value), + attr_value: ($) => seq(choice($.boolean, $.integer, $.float, $.string, $._unquoted_string)), @@ -375,7 +388,7 @@ module.exports = grammar({ token( prec( PREC.UNQUOTED_STRING, - /[^'"`\\|\n\s;{}]([^\\\n;{}]*[^\\\n\s;{}])?/ + /[^'"`\\|\n\s;{}\[\]]([^\\\n;{}\[\]]*[^\\\n\s;{}\[\]])?/ ) ) ) diff --git a/src/grammar.json b/src/grammar.json index 3766456..e5db853 100644 --- a/src/grammar.json +++ b/src/grammar.json @@ -1251,24 +1251,60 @@ "value": "class_name" }, "_base_shape_attribute": { - "type": "SEQ", + "type": "CHOICE", "members": [ { - "type": "ALIAS", - "content": { - "type": "SYMBOL", - "name": "_shape_attr_key" - }, - "named": true, - "value": "attr_key" + "type": "SEQ", + "members": [ + { + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_shape_list_attr_key" + }, + "named": true, + "value": "attr_key" + }, + { + "type": "SYMBOL", + "name": "_colon" + }, + { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "attr_value_list" + }, + { + "type": "SYMBOL", + "name": "attr_value" + } + ] + } + ] }, { - "type": "SYMBOL", - "name": "_colon" - }, - { - "type": "SYMBOL", - "name": "attr_value" + "type": "SEQ", + "members": [ + { + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_shape_attr_key" + }, + "named": true, + "value": "attr_key" + }, + { + "type": "SYMBOL", + "name": "_colon" + }, + { + "type": "SYMBOL", + "name": "attr_value" + } + ] } ] }, @@ -1298,10 +1334,6 @@ "type": "STRING", "value": "tooltip" }, - { - "type": "STRING", - "value": "constraint" - }, { "type": "STRING", "value": "icon" @@ -1325,6 +1357,14 @@ ] } }, + "_shape_list_attr_key": { + "type": "PREC", + "value": 0, + "content": { + "type": "STRING", + "value": "constraint" + } + }, "_style_attribute": { "type": "PREC", "value": 0, @@ -1843,6 +1883,73 @@ } ] }, + "attr_value_list": { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "[" + }, + { + "type": "REPEAT", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "_eol" + }, + { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "attr_value" + }, + { + "type": "SYMBOL", + "name": "_end" + } + ] + } + ] + } + }, + { + "type": "CHOICE", + "members": [ + { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "attr_value" + }, + { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "_end" + }, + { + "type": "BLANK" + } + ] + } + ] + }, + { + "type": "BLANK" + } + ] + }, + { + "type": "STRING", + "value": "]" + } + ] + }, "attr_value": { "type": "SEQ", "members": [ @@ -1940,7 +2047,7 @@ "value": -1, "content": { "type": "PATTERN", - "value": "[^'\"`\\\\|\\n\\s;{}]([^\\\\\\n;{}]*[^\\\\\\n\\s;{}])?" + "value": "[^'\"`\\\\|\\n\\s;{}\\[\\]]([^\\\\\\n;{}\\[\\]]*[^\\\\\\n\\s;{}\\[\\]])?" } } } @@ -2167,6 +2274,9 @@ [ "class_list" ], + [ + "attr_value_list" + ], [ "_text_block_attrs" ] diff --git a/src/node-types.json b/src/node-types.json index 3974383..212b2f4 100644 --- a/src/node-types.json +++ b/src/node-types.json @@ -45,6 +45,21 @@ ] } }, + { + "type": "attr_value_list", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "attr_value", + "named": true + } + ] + } + }, { "type": "attribute", "named": true, @@ -61,6 +76,10 @@ "type": "attr_value", "named": true }, + { + "type": "attr_value_list", + "named": true + }, { "type": "attribute", "named": true diff --git a/src/parser.c b/src/parser.c index 2a47b89..561b158 100644 Binary files a/src/parser.c and b/src/parser.c differ diff --git a/test/corpus/attributes.txt b/test/corpus/attributes.txt index 542efca..0915414 100644 --- a/test/corpus/attributes.txt +++ b/test/corpus/attributes.txt @@ -488,4 +488,30 @@ footer (shape (shape_key)) ) +================================================================================ +Declare a attribute with many values +================================================================================ +block: { + constraint: primary_key + constraint: [primary_key] + constraint: [primary_key; unique] + constraint: [ + primary_key + unique + ] +} + +-------------------------------------------------------------------------------- + +(source_file + (container + (container_key) + (block + (attribute (attr_key) (attr_value)) + (attribute (attr_key) (attr_value_list (attr_value))) + (attribute (attr_key) (attr_value_list (attr_value) (attr_value))) + (attribute (attr_key) (attr_value_list (attr_value) (attr_value))) + ) + ) +) diff --git a/test/corpus/classes.txt b/test/corpus/classes.txt index a840f96..c29122c 100644 --- a/test/corpus/classes.txt +++ b/test/corpus/classes.txt @@ -158,11 +158,22 @@ foo.class: biz ================================================================================ Declare shape with many classes ================================================================================ +fee.class: [biz] foo.class: [biz; baz] -------------------------------------------------------------------------------- (source_file + (shape + (shape_key) + (dot) + (attribute + (keyword_class) + (class_list + (class_name) + ) + ) + ) (shape (shape_key) (dot) @@ -181,6 +192,12 @@ Declare a class in the container ================================================================================ foo: { class: biz + class: [biz] + class: [biz; baz] + class: [ + biz + baz + ] } -------------------------------------------------------------------------------- @@ -193,6 +210,26 @@ foo: { (keyword_class) (class_name) ) + (attribute + (keyword_class) + (class_list + (class_name) + ) + ) + (attribute + (keyword_class) + (class_list + (class_name) + (class_name) + ) + ) + (attribute + (keyword_class) + (class_list + (class_name) + (class_name) + ) + ) ) ) ) diff --git a/tree-sitter-d2.wasm b/tree-sitter-d2.wasm index 06e0feb..edec16b 100755 Binary files a/tree-sitter-d2.wasm and b/tree-sitter-d2.wasm differ