From 79b5433bb4fc2a9e158d96fa87d50fab9d3befb4 Mon Sep 17 00:00:00 2001 From: Matt Masson Date: Tue, 10 Jul 2018 13:00:51 -0400 Subject: [PATCH] Initial commit of Power Query / M Language --- src/powerquery/powerquery.contribution.ts | 17 ++++ src/powerquery/powerquery.test.ts | 18 ++++ src/powerquery/powerquery.ts | 116 ++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 src/powerquery/powerquery.contribution.ts create mode 100644 src/powerquery/powerquery.test.ts create mode 100644 src/powerquery/powerquery.ts diff --git a/src/powerquery/powerquery.contribution.ts b/src/powerquery/powerquery.contribution.ts new file mode 100644 index 00000000..7ffdd692 --- /dev/null +++ b/src/powerquery/powerquery.contribution.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { registerLanguage } from '../_.contribution'; + +// Allow for running under nodejs/requirejs in tests +const _monaco: typeof monaco = (typeof monaco === 'undefined' ? (self).monaco : monaco); + +registerLanguage({ + id: 'powerquery', + extensions: ['.pq', '.pqm'], + aliases: ['PQ', 'M', 'Power Query', 'Power Query M'], + loader: () => _monaco.Promise.wrap(import('./powerquery')) +}); diff --git a/src/powerquery/powerquery.test.ts b/src/powerquery/powerquery.test.ts new file mode 100644 index 00000000..eac0c4f5 --- /dev/null +++ b/src/powerquery/powerquery.test.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { testTokenization } from '../test/testRunner'; + +testTokenization('powerquery', [ + // Comments + [{ + line: '// a comment', + tokens: [ + { startIndex: 0, type: 'comment.pq' } + ] + }], +]); diff --git a/src/powerquery/powerquery.ts b/src/powerquery/powerquery.ts new file mode 100644 index 00000000..2fb62b8f --- /dev/null +++ b/src/powerquery/powerquery.ts @@ -0,0 +1,116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import IRichLanguageConfiguration = monaco.languages.LanguageConfiguration; +import ILanguage = monaco.languages.IMonarchLanguage; + +export const conf: IRichLanguageConfiguration = { + comments: { + lineComment: '//', + blockComment: ['/*', '*/'], + }, + brackets: [['[', ']'], ['(', ')'], ['{', '}']], + autoClosingPairs: [ + { open: '"', close: '"', notIn: ['string', 'comment'] }, // quoted identifier? + { open: '[', close: ']', notIn: ['string', 'comment'] }, + { open: '(', close: ')', notIn: ['string', 'comment'] }, + { open: '{', close: '}', notIn: ['string', 'comment'] }, + ] +}; + +export const language = { + defaultToken: '', + tokenPostfix: '.pq', + ignoreCase: false, + + brackets: [ + { open: '[', close: ']', token: 'delimiter.square' }, + { open: '{', close: '}', token: 'delimiter.brackets' }, + { open: '(', close: ')', token: 'delimiter.parenthesis' } + ], + + keywords: [ + "and", "as", "each", "else", + "error", "false", "if", "in", + "is", "let", "meta", "not", + "otherwise", "or", "section", + "shared", "then", "true", + "try", "type", "#binary", + "#date", "#datetime", + "#datetimezone", "#duration", + "#infinity", "#nan", "#sections", + "#shared", "#table", "#time" + ], + + typeKeywords: [ + "null", + "logical", + "number", + "time", + "date", + "datetime", + "datetimezone", + "duration", + "text", + "binary", + "list", + "record", + "table", + "function" + ], + + wordDefinition: /([a-zA-Z_\.][a-zA-Z\._0-9]*)|([0-9][_\.a-zA-Z0-9]*[_\.a-zA-Z])/, + + tokenizer: { + root: [ + { include: "@comments" }, + + [/\d+(\.\d+)?/, "number"], + [/(([a-zA-Z_\.][a-zA-Z\._0-9]*)|([0-9][_\.a-zA-Z0-9]*[_\.a-zA-Z]))|(#["]([ \[\]_\.a-zA-Z0-9]+)["])/, + { + cases: { + "@keywords": "keyword", + "@default": "identifier" + } + }], + { include: "@strings" }, + [/[{}()\[\]]/, "@brackets"], + // Removed forward slash for now to allow comments + [/[,;=+<>\-*&@?]|([<>]=)|(<>)|([\.\.][\.]?)|(=>)/, "punctuator"], + + ], + comments: [ + ["\\/\\*", "comment", "@comment"], + ["\\/\\/+.*", "comment"] + ], + comment: [ + ["\\*\\/", "comment", "@pop"], + [".", "comment"] + ], + // Recognize strings, including those broken across lines with \ (but not without) + strings: [ + [/"$/, "string.escape", "@root"], + [/"/, "string.escape", "@stringBody"], + [/"$/, "string.escape", "@root"], + [/"/, "string.escape", "@dblStringBody"] + ], + stringBody: [ + [/\\./, "string"], + [/"/, "string.escape", "@root"], + [/.(?=.*")/, "string"], + [/.*\\$/, "string"], + [/.*$/, "string", "@root"] + ], + dblStringBody: [ + [/\\./, "string"], + [/"/, "string.escape", "@root"], + [/.(?=.*")/, "string"], + [/.*\\$/, "string"], + [/.*$/, "string", "@root"] + ] + } +};