diff --git a/README.md b/README.md index cb0604a..df4f98a 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,10 @@ git clone git@github.com:ssm-lang/vscode-sslang.git $HOME/.vscode/extensions/vsc ## Extension Settings -No extension settings. \ No newline at end of file +No extension settings. + +## Contributing + +VS Code can only load json grammars, but yaml is easier to work with (more concise; has regular expressions, comments and multi-line strings). Therefore, we generate `syntaxes/sslang.tmLanguage.json` from `syntaxes/sslang.tmLanguage.yaml`. + +The generation of the json grammar can be done by calling `./build-grammar.sh`. \ No newline at end of file diff --git a/build-grammar.sh b/build-grammar.sh new file mode 100755 index 0000000..1815de1 --- /dev/null +++ b/build-grammar.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +if ! command -v npx &> /dev/null; then + echo "'npm' is required to run this script." + exit +fi + +npx js-yaml syntaxes/sslang.tmLanguage.yaml > syntaxes/sslang.tmLanguage.json \ No newline at end of file diff --git a/language-configuration.json b/language-configuration.json index 8f162a0..07cc94f 100644 --- a/language-configuration.json +++ b/language-configuration.json @@ -3,28 +3,70 @@ // symbol used for single line comment. Remove this entry if your language does not support line comments "lineComment": "//", // symbols used for start and end a block comment. Remove this entry if your language does not support block comments - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, // symbols used as brackets "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], // symbols that are auto closed when typing "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ] ], // symbols that can be used to surround a selection "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ] ] } \ No newline at end of file diff --git a/package.json b/package.json index cf371e9..2c5a422 100644 --- a/package.json +++ b/package.json @@ -10,16 +10,29 @@ "Programming Languages" ], "contributes": { - "languages": [{ - "id": "sslang", - "aliases": ["sslang", "sslang"], - "extensions": [".ssl"], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "sslang", - "scopeName": "source.sslang", - "path": "./syntaxes/sslang.tmLanguage.json" - }] + "languages": [ + { + "id": "sslang", + "aliases": [ + "sslang", + "sslang" + ], + "extensions": [ + ".ssl" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "sslang", + "scopeName": "source.sslang", + "path": "./syntaxes/sslang.tmLanguage.json", + "embeddedLanguages": { + "meta.embedded.block.c": "c", + "meta.embedded.inline.c": "c" + } + } + ] } -} +} \ No newline at end of file diff --git a/syntaxes/backup-sslang.tmLanguage.json b/syntaxes/backup-sslang.tmLanguage.json new file mode 100644 index 0000000..cbeef9b --- /dev/null +++ b/syntaxes/backup-sslang.tmLanguage.json @@ -0,0 +1,247 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "sslang", + "patterns": [ + { + "include": "#c-block" + }, + { + "include": "#c-inline" + }, + { + "include": "#type-declaration" + }, + { + "include": "#top-level-value" + }, + { + "include": "#type-annotation" + }, + { + "include": "#keywords" + }, + { + "include": "#strings" + }, + { + "include": "#chars" + }, + { + "include": "#comments" + }, + { + "include": "#operators" + }, + { + "include": "#unit" + }, + { + "include": "#constructor" + }, + { + "include": "#value" + }, + { + "comment": "Floats are always decimal", + "match": "\\b([0-9]+\\.[0-9]+([eE][+-]?[0-9]+)?|[0-9]+[eE][+-]?[0-9]+)\\b", + "name": "constant.numeric.float.elm" + }, + { + "match": "\\b([0-9]+)\\b", + "name": "constant.numeric.elm" + } + ], + "repository": { + "keywords": { + "patterns": [ + { + "name": "keyword.control.sslang", + "match": "\\b(if|then|else|while|do|par|loop|let|match|after|wait|fun)\\b" + }, + { + "name": "keyword.other.sslang", + "match": "\\b(type|extern)\\b" + } + ] + }, + "strings": { + "name": "string.quoted.double.sslang", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.sslang", + "match": "\\\\." + } + ] + }, + "chars": { + "name": "string.quoted.single.sslang", + "begin": "'", + "end": "'", + "patterns": [ + { + "name": "constant.character.escape.sslang", + "match": "\\\\." + } + ] + }, + "comments": { + "patterns": [ + { + "name": "comment.line.double-slash.sslang", + "begin": "//", + "end": "$" + }, + { + "name": "comment.block.sslang", + "begin": "/\\*", + "end": "\\*/" + } + ] + }, + "value": { + "match": "\\b[a-z][a-zA-Z0-9_]*\\b", + "name": "meta.value.sslang" + }, + "operators": { + "match": "(=|;|<-|&|@|!|==|!=|<=|>=|<|>|\\+|-|\\*|/|%|\\^)", + "name": "keyword.operator.sslang" + }, + "constructor": { + "match": "\\b[A-Z][a-zA-Z0-9_]*\\b", + "name": "constant.constructor.sslang" + }, + "unit": { + "match": "\\(\\)", + "name": "constant.unit.sslang" + }, + "c-block": { + "name": "string.other.sslang", + "begin": "\\$\\$\\$", + "end": "\\$\\$\\$" + }, + "c-inline": { + "name": "string.other.sslang", + "begin": "\\$\\$", + "end": "\\$\\$" + }, + "type-declaration": { + "begin": "^(type\\s+)([A-Z][a-zA-Z0-9_']*)\\s+", + "beginCaptures": { + "1": { + "name": "keyword.type.sslang" + }, + "2": { + "name": "storage.type.sslang" + } + }, + "end": "^(?=\\S)", + "name": "meta.type.declaration.sslang", + "patterns": [ + { + "name": "meta.type.field.sslang", + "match": "^\\s+([A-Z][a-zA-Z0-9_]*)\\b", + "captures": { + "1": { + "name": "constant.constructor.sslang" + } + } + }, + { + "match": "\\s+", + "name": "punctuation.spaces.sslang" + }, + { + "match": "\\-\\>", + "name": "keyword.operator.arrow.sslang" + }, + { + "match": "\\b[a-z][a-zA-Z0-9_]*\\b", + "name": "variable.type.sslang" + }, + { + "match": "\\b[A-Z][a-zA-Z0-9_]*\\b", + "name": "storage.type.sslang" + }, + { + "include": "#comments" + } + ] + }, + "type-annotation": { + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "keyword.operator.annotation.sslang" + } + }, + "end": "[\\)\n]", + "name": "meta.type.annotation.sslang", + "patterns": [ + { + "match": "\\-\\>", + "name": "keyword.operator.arrow.sslang" + }, + { + "match": "\\b[a-z][a-zA-Z0-9_]*\\b", + "name": "variable.type.sslang" + }, + { + "match": "\\b[A-Z][a-zA-Z0-9_]*\\b", + "name": "storage.type.sslang" + }, + { + "match": "&", + "name": "keyword.operator.reference.sslang" + }, + { + "match": "\\s+", + "name": "punctuation.spaces.sslang" + }, + { + "include": "#comments" + } + ] + }, + "top-level-value": { + "begin": "^([a-z][a-zA-Z0-9_]*)\\b", + "beginCaptures": { + "1": { + "name": "entity.name.function.top_level.sslang" + } + }, + "end": "(=)", + "endCaptures": { + "1": { + "name": "keyword.operator.define.sslang" + } + }, + "name": "meta.function.top_level.sslang", + "patterns": [ + { + "include": "#type-annotation" + }, + { + "include": "#value" + }, + { + "match": "\\-\\>", + "name": "keyword.operator.arrow.sslang" + }, + { + "match": "\\b[A-Z][a-zA-Z0-9_]*\\b", + "name": "storage.type.sslang" + }, + { + "match": "\\s+", + "name": "punctuation.spaces.sslang" + }, + { + "include": "#comments" + } + ] + } + }, + "scopeName": "source.sslang" +} \ No newline at end of file diff --git a/syntaxes/sslang.tmLanguage.json b/syntaxes/sslang.tmLanguage.json index 4fac337..a198044 100644 --- a/syntaxes/sslang.tmLanguage.json +++ b/syntaxes/sslang.tmLanguage.json @@ -1,247 +1,192 @@ { - "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", - "name": "sslang", - "patterns": [ - { - "include": "#c-block" - }, - { - "include": "#c-inline" - }, - { - "include": "#type-declaration" - }, - { - "include": "#top-level-value" - }, - { - "include": "#type-annotation" - }, - { - "include": "#keywords" - }, - { - "include": "#strings" - }, - { - "include": "#chars" - }, - { - "include": "#comments" - }, - { - "include": "#infix_op" - }, - { - "include": "#unit" - }, - { - "include": "#constructor" - }, - { - "include": "#value" - }, - { - "comment": "Floats are always decimal", - "match": "\\b([0-9]+\\.[0-9]+([eE][+-]?[0-9]+)?|[0-9]+[eE][+-]?[0-9]+)\\b", - "name": "constant.numeric.float.elm" - }, - { - "match": "\\b([0-9]+)\\b", - "name": "constant.numeric.elm" - } - ], - "repository": { - "keywords": { - "patterns": [ - { - "name": "keyword.control.sslang", - "match": "\\b(if|then|else|while|do|par|loop|let|match|after|wait|fun)\\b" - }, - { - "name": "keyword.other.sslang", - "match": "\\b(type|extern)\\b" - } - ] - }, - "strings": { - "name": "string.quoted.double.sslang", - "begin": "\"", - "end": "\"", - "patterns": [ - { - "name": "constant.character.escape.sslang", - "match": "\\\\." - } - ] - }, - "chars": { - "name": "string.quoted.single.sslang", - "begin": "'", - "end": "'", - "patterns": [ - { - "name": "constant.character.escape.sslang", - "match": "\\\\." - } - ] - }, - "comments": { - "patterns": [ - { - "name": "comment.line.double-slash.sslang", - "begin": "//", - "end": "$" - }, - { - "name": "comment.block.sslang", - "begin": "/\\*", - "end": "\\*/" - } - ] - }, - "value": { - "match": "\\b[a-z][a-zA-Z0-9_]*\\b", - "name": "meta.value.sslang" - }, - "infix_op": { - "match": "(=|<-|==|!=|<=|>=|<|>|\\+|-|\\*|/|%|\\^)", - "name": "keyword.operator.sslang" - }, - "constructor": { - "match": "\\b[A-Z][a-zA-Z0-9_]*\\b", - "name": "constant.constructor.sslang" - }, - "unit": { - "match": "\\(\\)", - "name": "constant.unit.sslang" - }, - "c-block": { - "name": "string.other.sslang", - "begin": "\\$\\$\\$", - "end": "\\$\\$\\$" - }, - "c-inline": { - "name": "string.other.sslang", - "begin": "\\$\\$", - "end": "\\$\\$" - }, - "type-declaration": { - "begin": "^(type\\s+)([A-Z][a-zA-Z0-9_']*)\\s+", - "beginCaptures": { - "1": { - "name": "keyword.type.sslang" - }, - "2": { - "name": "storage.type.sslang" - } - }, - "end": "^(?=\\S)", - "name": "meta.type.declaration.sslang", - "patterns": [ - { - "name": "meta.type.field.sslang", - "match": "^\\s+([A-Z][a-zA-Z0-9_]*)\\b", - "captures": { - "1": { - "name": "constant.constructor.sslang" - } - } - }, - { - "match": "\\s+", - "name": "punctuation.spaces.sslang" - }, - { - "match": "\\-\\>", - "name": "keyword.operator.arrow.sslang" - }, - { - "match": "\\b[a-z][a-zA-Z0-9_]*\\b", - "name": "variable.type.sslang" - }, - { - "match": "\\b[A-Z][a-zA-Z0-9_]*\\b", - "name": "storage.type.sslang" - }, - { - "include": "#comments" - } - ] - }, - "type-annotation": { - "begin": "(:)", - "beginCaptures": { - "1": { - "name": "keyword.operator.annotation.sslang" - } - }, - "end": "[\\)\n]", - "name": "meta.type.annotation.sslang", - "patterns": [ - { - "match": "\\-\\>", - "name": "keyword.operator.arrow.sslang" - }, - { - "match": "\\b[a-z][a-zA-Z0-9_]*\\b", - "name": "variable.type.sslang" - }, - { - "match": "\\b[A-Z][a-zA-Z0-9_]*\\b", - "name": "storage.type.sslang" - }, - { - "match": "&", - "name": "keyword.operator.reference.sslang" - }, - { - "match": "\\s+", - "name": "punctuation.spaces.sslang" - }, - { - "include": "#comments" - } - ] - }, - "top-level-value": { - "begin": "^([a-z][a-zA-Z0-9_]*)\\b", - "beginCaptures": { - "1": { - "name": "entity.name.function.top_level.sslang" - } - }, - "end": "(=)", - "endCaptures": { - "1": { - "name": "keyword.operator.define.sslang" - } - }, - "name": "meta.function.top_level.sslang", - "patterns": [ - { - "include": "#type-annotation" - }, - { - "include": "#value" - }, - { - "match": "\\-\\>", - "name": "keyword.operator.arrow.sslang" - }, - { - "match": "\\b[A-Z][a-zA-Z0-9_]*\\b", - "name": "storage.type.sslang" - }, - { - "match": "\\s+", - "name": "punctuation.spaces.sslang" - }, - { - "include": "#comments" - } - ] - } - }, - "scopeName": "source.sslang" -} \ No newline at end of file + "fileTypes": "sslang", + "name": "sslang", + "scopeName": "source.sslang", + "patterns": [ + { + "include": "#top-type" + }, + { + "include": "#top-extern" + }, + { + "include": "#top-c-block" + }, + { + "include": "#top-value" + }, + { + "include": "#comment" + }, + { + "include": "#string" + } + ], + "repository": { + "top-type": { + "begin": "^(type)\\s+", + "end": "^(?=\\S)", + "beginCaptures": { + "1": { + "name": "keyword.type.sslang" + } + }, + "name": "meta.function.type-definition.sslang", + "patterns": [ + { + "match": "^\\s+([A-Z][a-zA-Z0-9_']*)\\b", + "captures": { + "1": { + "name": "constant.data-constructor.sslang" + } + } + }, + { + "include": "#arrow" + }, + { + "include": "#type-variable" + }, + { + "include": "#type-constructor" + }, + { + "include": "#comments" + } + ] + }, + "top-extern": { + "begin": "^(extern)\\s+([a-z_][a-zA-Z0-9_']*)\\s*(\\:)", + "end": "^(?=\\S)", + "beginCaptures": { + "1": { + "name": "keyword.extern.sslang" + }, + "2": { + "name": "entity.name.function.sslang" + }, + "3": { + "name": "keyword.other.sslang" + } + }, + "name": "meta.function.extern-declaration.sslang", + "patterns": [ + { + "include": "#type" + } + ] + }, + "top-c-block": { + "begin": "^(\\$\\$\\$)", + "end": "^(\\$\\$\\$)", + "beginCaptures": { + "1": { + "name": "keyword.c-block.begin.sslang" + } + }, + "endCaptures": { + "1": { + "name": "keyword.c-block.end.sslang" + } + }, + "contentName": "meta.embedded.block.c", + "patterns": [ + { + "include": "source.c" + } + ] + }, + "top-value": { + "begin": "^([a-z_][a-zA-Z0-9_']*)\\s*(\\=)\\s*", + "end": "^(?=\\S)", + "beginCaptures": { + "1": { + "name": "entity.name.function" + }, + "2": { + "name": "keyword.operator.assignment.sslang" + } + }, + "name": "meta.function.sslang" + }, + "comment": { + "patterns": [ + { + "name": "comment.line.double-slash.sslang", + "begin": "//", + "end": "$" + }, + { + "name": "comment.block.sslang", + "begin": "/\\*", + "end": "\\*/" + } + ] + }, + "type-constructor": { + "match": "\\b[A-Z][a-zA-Z0-9_']*\\b", + "name": "storage.type.sslang" + }, + "data-constructor": { + "match": "\\b[A-Z][a-zA-Z0-9_']*\\b", + "name": "constant.data-constructor.sslang" + }, + "type-variable": { + "match": "\\b[a-z_][a-zA-Z0-9_']*\\b", + "name": "variable.type.sslang" + }, + "arrow": { + "match": "\\-\\>", + "name": "keyword.operator.arrow.sslang" + }, + "string": { + "begin": "\"", + "end": "\"", + "name": "string.quoted.double.sslang", + "patterns": [ + { + "name": "constant.character.escape.sslang", + "match": "\\\\." + } + ] + }, + "type": { + "patterns": [ + { + "include": "#arrow" + }, + { + "include": "#type-constructor" + }, + { + "include": "#type-variable" + }, + { + "include": "#paren-type" + }, + { + "include": "#comment" + } + ] + }, + "paren-type": { + "begin": "\\(", + "end": "\\)", + "beginCaptures": { + "0": { + "name": "punctuation.paren.open" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.paren.open" + } + }, + "patterns": [ + { + "include": "#type" + } + ] + } + } +} diff --git a/syntaxes/sslang.tmLanguage.yaml b/syntaxes/sslang.tmLanguage.yaml new file mode 100644 index 0000000..828f4c4 --- /dev/null +++ b/syntaxes/sslang.tmLanguage.yaml @@ -0,0 +1,129 @@ +fileTypes: sslang +name: sslang +scopeName: source.sslang +patterns: + - include: '#top-type' + - include: '#top-extern' + - include: '#top-c-block' + - include: '#top-value' + - include: '#comment' + - include: '#string' +repository: + # top-level type definition + top-type: + begin: ^(type)\s+ # leading 'type' + end: ^(?=\S) # non-indented line + beginCaptures: + '1': + name: keyword.type.sslang + name: meta.function.type-definition.sslang + patterns: + # data constructors (first uppercase identifier) + - match: ^\s+([A-Z][a-zA-Z0-9_']*)\b + captures: + '1': + name: constant.data-constructor.sslang + # arrows + - include: '#arrow' + # type variables + - include: '#type-variable' + # types (non-first uppercase identifiers) + - include: '#type-constructor' + - include: '#comments' + # top-level extern declaration + top-extern: + begin: ^(extern)\s+([a-z_][a-zA-Z0-9_']*)\s*(\:) + end: ^(?=\S) # non-indented line + beginCaptures: + '1': + name: keyword.extern.sslang + '2': + name: entity.name.function.sslang + '3': + name: keyword.other.sslang + name: meta.function.extern-declaration.sslang + patterns: + - include: '#type' + # arrows + # - include: '#arrow' + # type variables + # - include: '#type-variable' + # types (non-first uppercase identifiers) + # - include: '#type-constructor' + # - include: '#comments' + # top-level c block + top-c-block: + begin: ^(\$\$\$) + end: ^(\$\$\$) + beginCaptures: + '1': + name: keyword.c-block.begin.sslang + endCaptures: + '1': + name: keyword.c-block.end.sslang + contentName: meta.embedded.block.c + patterns: + - include: source.c + # top-level value definition + top-value: + begin: ^([a-z_][a-zA-Z0-9_']*)\s*(\=)\s* + end: ^(?=\S) + beginCaptures: + '1': + name: entity.name.function + '2': + name: keyword.operator.assignment.sslang + name: meta.function.sslang + # comments + comment: + patterns: + - name: comment.line.double-slash.sslang + begin: // + end: $ + - name: comment.block.sslang + begin: /\* + end: \*/ + # type constructors + type-constructor: + match: \b[A-Z][a-zA-Z0-9_']*\b + name: storage.type.sslang + # data constructors + data-constructor: + match: \b[A-Z][a-zA-Z0-9_']*\b + name: constant.data-constructor.sslang + # type variables + type-variable: + match: \b[a-z_][a-zA-Z0-9_']*\b + name: variable.type.sslang + # arrow type + arrow: + match: \-\> + name: keyword.operator.arrow.sslang + # string literals + string: + begin: '"' + end: '"' + name: string.quoted.double.sslang + patterns: + - name: constant.character.escape.sslang + match: \\. + # type + type: + patterns: + - include: '#arrow' + - include: '#type-constructor' + - include: '#type-variable' + - include: '#paren-type' + - include: '#comment' + # paren-type + paren-type: + begin: \( + end: \) + beginCaptures: + '0': + name: punctuation.paren.open + endCaptures: + '0': + name: punctuation.paren.open + patterns: + - include: '#type' \ No newline at end of file