Skip to content

Commit

Permalink
Add grammar for StringTemplate files
Browse files Browse the repository at this point in the history
  • Loading branch information
Alhadis committed Mar 7, 2021
1 parent 954c6e5 commit f41e1a4
Show file tree
Hide file tree
Showing 5 changed files with 405 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Supported formats
* [x] [Sieve](http://sieve.info/)
* [x] [SSH configs](https://www.ssh.com/ssh/config/)
* [ ] [STEP-Files](https://en.wikipedia.org/wiki/ISO_10303-21)
* [x] [StringTemplate](https://www.stringtemplate.org/)
* [ ] [`sudoers`](https://linux.die.net/man/5/sudoers)
* [x] [SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Element)
* [x] [Tags files](https://en.wikipedia.org/wiki/Ctags#Tags_file_formats)
Expand Down
306 changes: 306 additions & 0 deletions grammars/string-template.cson
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
# Source: https://github.com/antlr/stringtemplate4/blob/a914b54/doc/cheatsheet.md
name: "StringTemplate"
scopeName: "source.string-template"
fileTypes: ["st"]
patterns: [{
# Skip empty lines
begin: "\\A(?=\\s*$)"
end: "(?=\\S)"
},{
# HTML: Use $…$ delimiters
name: "meta.document.dollar-delimiters.string-template"
begin: """(?xi)(?=
<(abbr|acronym|address|applet|area|article|aside|audio|a|basefont|base
| bdi|bdo|bgsound|big|blink|blockquote|body|br|button|b|canvas|caption
| center|cite|code|colgroup|col|content|datalist|data|dd|del|details|dfn
| dialog|dir|div|dl|dt|embed|em|fieldset|figcaption|figure|font|footer
| form|frameset|frame|h1|h2|h3|h4|h5|h6|header|head|hgroup|hr|html|iframe
| image|img|input|ins|isindex|i|kbd|keygen|label|legend|link|listing|li
| main|map|mark|marquee|math|menuitem|menu|meta|meter|multicol|nav|nextid
| nobr|noembed|noframes|noscript|object|ol|optgroup|option|output|param
| picture|plaintext|portal|pre|progress|p|q|rb|rp|rtc|rt|ruby|samp|script
| section|select|shadow|slot|small|source|spacer|span|strike|strong|style
| sub|summary|sup|svg|s|table|tbody|td|template|textarea|tfoot|thead|th
| time|title|track|tr|tt|ul|u|var|video|wbr|xmp
) (?=\\\\s|/?>)[^>]*>
| <!DOCTYPE
| <!--.*?-->
| &(?:amp|lt|gt|quot|nbsp|\\\#(?:[xX][\\dA-Fa-f]+|\\d+));
| \\$! .+? !\\$
)"""
end: "(?=A)B"
patterns: [include: "#main$"]
},{
# Not HTML: Any other use of <…>
name: "meta.document.angle-delimiters.string-template"
begin: "(?<!\\\\)(?=<[\\a-zA-Z].*?>|<!.*?!>)"
end: "(?=A)B"
patterns: [include: "#main"]
}]
injections:
"L:source.string-template meta.document.dollar-delimiters.string-template - comment":
patterns: [
{match: "<|>"}
{include: "#main$"}
]

repository:
# Angle brackets: <…>
main:
patterns: [
{include: "#escape"}
{include: "#comment"}
{include: "#conditional"}
{include: "#attribute"}
]

# Dollarydoos: $…$
main$:
patterns: [
{include: "#escape$"}
{include: "#comment$"}
{include: "#conditional$"}
{include: "#attribute$"}
]

# <attribute>, $attribute$
attribute:
name: "meta.embedded.section.string-template"
begin: "<"
end: ">"
beginCaptures: 0: name: "keyword.operator.section.begin.string-template"
endCaptures: 0: name: "keyword.operator.section.end.string-template"
patterns: [include: "#expression"]
attribute$:
name: "meta.embedded.section.string-template"
begin: "\\$"
end: "([^$]*+)(\\$)"
beginCaptures:
0: name: "keyword.operator.section.begin.string-template"
endCaptures:
1: patterns: [include: "#expression"]
2: name: "keyword.operator.section.end.string-template"
patterns: [include: "#expression"]


# Identifiers I don't have a better scope for
bareword:
name: "variable.identifier.string-template"
match: "[-\\w.]+"


# <! Comment !>
comment:
name: "comment.block.string-template"
begin: "<!"
end: "!>"
beginCaptures: 0: name: "punctuation.definition.comment.begin.string-template"
endCaptures: 0: name: "punctuation.definition.comment.end.string-template"
comment$:
name: "comment.block.string-template"
begin: "\\$!"
end: "!\\$"
beginCaptures: 0: name: "punctuation.definition.comment.begin.string-template"
endCaptures: 0: name: "punctuation.definition.comment.end.string-template"


# <if(cond)> … <elseif(cond)> … <else> … <endif>
conditional:
patterns: [{
name: "meta.embedded.conditional.$2.string-template"
begin: "(<)(if|elseif|else|endif)(\\()"
end: "(\\))(>)"
beginCaptures:
1: name: "keyword.operator.section.begin.string-template"
2: name: "keyword.control.flow.$2.string-template"
3: name: "punctuation.section.conditional.begin.string-template"
endCaptures:
1: name: "punctuation.section.conditional.end.string-template"
2: name: "keyword.operator.section.end.string-template"
contentName: "meta.condition.string-template"
patterns: [include: "#condition"]
}]
conditional$:
patterns: [{
name: "meta.embedded.conditional.$2.string-template"
begin: "(\\$)(if|elseif|else|endif)(\\()"
end: "(\\))(\\$)"
beginCaptures:
1: name: "keyword.operator.section.begin.string-template"
2: name: "keyword.control.flow.$2.string-template"
3: name: "punctuation.section.conditional.begin.string-template"
endCaptures:
1: name: "punctuation.section.conditional.end.string-template"
2: name: "keyword.operator.section.end.string-template"
contentName: "meta.condition.string-template"
patterns: [include: "#condition"]
}]

condition:
patterns: [{
name: "keyword.operator.logical.string-template"
match: "\\|\\||&&|!"
},{
name: "variable.identifier.string-template"
match: "\\G([-\\w.]+)"
},
{include: "#expression"}
{include: "#bareword"}]


# \< \> \$
escape:
name: "constant.character.escape.delimiter.string-template"
match: "\\\\[<>]"
escape$:
name: "constant.character.escape.delimiter.string-template"
match: "\\\\\\$"


# Most useful language constructs
expression:
patterns: [
{match: "\\b(true|false)\\b", name: "constant.language.boolean.$1.string-template"}
{match: "(?:\\G|^)i0?\\b", name: "constant.language.iteration-index.string-template"}
{match: "\\\\[ ntr]", name: "constant.character.whitespace.string-template"}
{match: "\\\\u[0-9A-Fa-f]{4}", name: "constant.character.escape.codepoint.hex.string-template"}
{match: "\\\\{2}", name: "constant.character.escape.line-continuation.string-template"}
{include: "etc#strDouble"}
{
name: "meta.function-call.indirect.string-template"
begin: "(?:\\G|^|:)(?=\\()"
end: "(?<=\\))"
beginCaptures:
0: name: "keyword.operator.separator.string-template"
patterns: [include: "#expressionGroup"]
}
{
name: "meta.argument-list.string-template"
begin: "(?<=\\))(?=\\()"
end: "(?<=\\))(?!\\G)"
patterns: [include: "#expressionGroup"]
}
{
# <[foo, bar]>
name: "meta.list.array.string-template"
begin: "(?:\\G|^)\\["
end: "\\]"
beginCaptures: 0: name: "punctuation.section.array.begin.string-template"
endCaptures: 0: name: "punctuation.section.array.end.string-template"
patterns: [
{include: "#bareword"}
{include: "#expression"}
]
},{
include: "#subtemplate"
},{
# <attr; key=value>
begin: "(?:\\G|^)[-\\w.]+(?=\\s*;\\s*\\w[-\\w.]*=)"
end: "(?=[^\\s;])"
beginCaptures:
0: name: "entity.name.tag.attribute.string-template"
patterns: [{
match: "(;)\\s*([-\\w.]+)(=)([-\\w.]+)"
captures:
1: patterns: [include: "etc#semi"]
3: patterns: [include: "etc#eql"]
2: name: "entity.other.attribute-name.string-template"
4: name: "constant.other.attribute-value.string-template"
}]
},{
# <template(argument-list)>
name: "meta.function-call.string-template"
begin: "(?:\\G|^|(?<!\\))(:))([-\\w.]+)(?=\\()"
end: "(?<=\\))"
beginCaptures:
1: name: "keyword.operator.separator.string-template"
2: patterns: [include: "#functionName"]
patterns: [include: "#expressionGroup"]
},{
# <attr.(expr)>
begin: "(?:\\G|^)[-\\w.]+(?=(\\.\\())"
end: "(?<=\\))"
beginCaptures:
0: name: "entity.name.tag.attribute.string-template"
patterns: [{
name: "meta.subscript.property.indirect-lookup.string-template"
begin: "\\G\\.(?=\\()"
end: "(?!\\G)"
beginCaptures:
0: patterns: [include: "etc#dot"]
patterns: [include: "#expressionGroup"]
}]
},{
# <attr1,attr2,t()>
begin: "(?:\\G|^)([-\\w.]+)(?=\\,)|(?<=\\))(?=\\s*,)"
end: "(?=[^-\\w.\\s,])"
beginCaptures:
1: name: "entity.name.tag.attribute.string-template"
patterns: [{
# ,template()
begin: "\\s*(,)\\s*([-\\w.]+)(?=\\()"
end: "(?<=\\))"
beginCaptures:
1: patterns: [include: "etc#comma"]
2: patterns: [include: "#functionName"]
patterns: [include: "#expressionGroup"]
},{
# ,attr1
match: "\\s*(,)\\s*([-\\w.]+)"
captures:
1: patterns: [include: "etc#comma"]
2: name: "entity.name.tag.attribute.string-template"
}, include: "#expression"]
},{
# <attr>, <attr.prop>
match: "(?:\\G|^)([-\\w.]+)((\\.)([-\\w.]+))?"
captures:
1: name: "entity.name.tag.attribute.string-template"
2: name: "meta.subscript.property.direct-lookup.string-template"
3: patterns: [include: "etc#dot"]
4: name: "variable.member.property.string-template"
5: name: "meta.subscript.property.indirect-lookup.string-template"
},
{include: "etc#kolon"}
{include: "etc#comma"}
{include: "etc#eql"}
{include: "etc#semi"}
]


# Parenthesised expression
expressionGroup:
name: "meta.expression.string-template"
begin: "\\("
end: "\\)"
beginCaptures: 0: name: "punctuation.definition.expression.begin.string-template"
endCaptures: 0: name: "punctuation.definition.expression.end.string-template"
patterns: [
{include: "#bareword"}
{include: "#expression"}
]


# Name of an executed function
functionName:
patterns: [{
name: "support.function.$1.string-template"
match: "(?:\\G|^)(first|last|length|rest|reverse|strip|strlen|trim|trunc)$"
},{
name: "entity.name.function.string-template"
match: "(?:\\G|^)(.+)"
}]


# {x | anonymous-template}
subtemplate:
name: "meta.subtemplate.string-template"
begin: "{"
end: "}"
beginCaptures: 0: name: "punctuation.section.subtemplate.begin.string-template"
endCaptures: 0: name: "punctuation.section.subtemplate.end.string-template"
patterns: [
{match: "\\|", name: "keyword.operator.separator.string-template"}
{include: "#main"}
{include: "#bareword"}
]
44 changes: 44 additions & 0 deletions samples/template.default.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
< div> <\n>
<! $multi-valued-attribute; separator=expr$ !>

<[foo, bar]>
<template(argument-list)>

<(expr)(argument-list)>
<attribute:template(argument-list)>

<attribute:(expr)(argument-list)>
<[]>
<attribute:t1(argument-list): ... :tN(argument-list)>
<attribute:{x | anonymous-template}>
<a1,a2,...,aN:{argument-list | anonymous-template}>
<attribute:t1(), t2(),...,tN()>
\< or \>
<\ >, <\n>, <\t>, <\r>
<\uFEFF>
<\\>
<! comment !>
<attr.(expr)>

<names,phones:{ n,p | <n>: <p>}>
<["a","b"]:{v | <v>=<i>;}>


<if(boolexpr1)> subtemplate
<elseif(A || boolexpr2)> subtemplate2
...
<elseif(boolexprN)> subtemplateN
<else> defaultsubtemplate
<endif>

<method(name="f", body={x=1;}, cleanup={printf("leaving <name>");})>

<first(attr)> The first or only element of attr. You can combine operations to say things like first(rest(names)) to get second element.
<length(attr)> Return the length of a mult-valued attribute or 1 if it is single attribute. If attribute is null return 0. Strings are not special; i.e., length("foo") is 1 meaning "1 attribute". Nulls are counted in lists so a list of 300 nulls is length 300. If you don't want to count nulls, use length(strip(list)).
<strlen(attr)> Return the length of a string attribute; runtime error if not string.
<last(attr)> The last or only element of attr.
<rest(attr)> All but the first element of attr. Returns nothing if is single valued.
<reverse(attr)> Return a list with the same elements as v but in reverse order. null values are NOT stripped out. Use reverse(strip(v)) to do that.
<trunc(attr)> Returns all elements but last element.
<strip(attr)> Return a new list w/o null values.
<trim(attr)> Trim whitespace from back/front of a string; runtime error if not string.
Loading

0 comments on commit f41e1a4

Please sign in to comment.