From 345fa67059d6a32d0ec2059a7cc1b344f88b6aa7 Mon Sep 17 00:00:00 2001 From: Scott McMaster Date: Wed, 17 May 2017 14:59:05 -0700 Subject: [PATCH] MSDAX support --- gulpfile.js | 1 + src/monaco.contribution.ts | 6 + src/msdax.ts | 175 ++++++++++++++++++ test/all.js | 1 + test/msdax.test.ts | 370 +++++++++++++++++++++++++++++++++++++ 5 files changed, 553 insertions(+) create mode 100644 src/msdax.ts create mode 100644 test/msdax.test.ts diff --git a/gulpfile.js b/gulpfile.js index 1a1af34c..65cf6344 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -61,6 +61,7 @@ gulp.task('release', ['clean-release','compile'], function() { bundleOne('src/less'), bundleOne('src/lua'), bundleOne('src/markdown'), + bundleOne('src/msdax'), bundleOne('src/objective-c'), bundleOne('src/php'), bundleOne('src/powershell'), diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index 318a204f..866fa233 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -147,6 +147,12 @@ registerLanguage({ aliases: ['Markdown', 'markdown'], module: './markdown' }); +registerLanguage({ + id: 'msdax', + extensions: ['.dax', '.msdax'], + aliases: ['DAX', 'MSDAX'], + module: './msdax' +}); registerLanguage({ id: 'objective-c', extensions: [ '.m' ], diff --git a/src/msdax.ts b/src/msdax.ts new file mode 100644 index 00000000..d48463df --- /dev/null +++ b/src/msdax.ts @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- + * 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 var conf:IRichLanguageConfiguration = { + comments: { + lineComment: '//', + blockComment: ['/*', '*/'], + }, + brackets: [['[',']'],['(',')'],['{','}']], + autoClosingPairs: [ + { open: '"', close: '"', notIn: ['string', 'comment'] }, + { open: '\'', close: '\'', notIn: ['string', 'comment'] }, + { open: '[', close: ']', notIn: ['string', 'comment'] }, + { open: '(', close: ')', notIn: ['string', 'comment'] }, + { open: '{', close: '}', notIn: ['string', 'comment'] }, + ] +}; + +export var language = { + defaultToken: '', + tokenPostfix: '.msdax', + ignoreCase: true, + + brackets: [ + { open: '[', close: ']', token: 'delimiter.square' }, + { open: '{', close: '}', token: 'delimiter.brackets' }, + { open: '(', close: ')', token: 'delimiter.parenthesis' } + ], + + keywords: [ + // Query keywords + 'VAR', + 'RETURN', + 'NOT', + 'EVALUATE', + 'DATATABLE', + 'ORDER', + 'BY', + 'START', + 'AT', + 'DEFINE', + 'MEASURE', + 'ASC', + 'DESC', + 'IN', + // Datatable types + 'BOOLEAN', + 'DOUBLE', + 'INTEGER', + 'DATETIME', + 'CURRENCY', + 'STRING' + ], + functions: [ + // Relational + 'CLOSINGBALANCEMONTH', 'CLOSINGBALANCEQUARTER', 'CLOSINGBALANCEYEAR', 'DATEADD', 'DATESBETWEEN', + 'DATESINPERIOD', 'DATESMTD', 'DATESQTD', 'DATESYTD', 'ENDOFMONTH', + 'ENDOFQUARTER', 'ENDOFYEAR', 'FIRSTDATE', 'FIRSTNONBLANK', 'LASTDATE', + 'LASTNONBLANK', 'NEXTDAY', 'NEXTMONTH', 'NEXTQUARTER', 'NEXTYEAR', + 'OPENINGBALANCEMONTH', 'OPENINGBALANCEQUARTER', 'OPENINGBALANCEYEAR', 'PARALLELPERIOD', 'PREVIOUSDAY', + 'PREVIOUSMONTH', 'PREVIOUSQUARTER', 'PREVIOUSYEAR', 'SAMEPERIODLASTYEAR', 'STARTOFMONTH', + 'STARTOFQUARTER', 'STARTOFYEAR', 'TOTALMTD', 'TOTALQTD', 'TOTALYTD', + 'ADDCOLUMNS', 'ADDMISSINGITEMS', 'ALL', 'ALLEXCEPT', 'ALLNOBLANKROW', + 'ALLSELECTED', 'CALCULATE', 'CALCULATETABLE', 'CALENDAR', 'CALENDARAUTO', + 'CROSSFILTER', 'CROSSJOIN', 'CURRENTGROUP', 'DATATABLE', 'DETAILROWS', + 'DISTINCT', 'EARLIER', 'EARLIEST', 'EXCEPT', 'FILTER', + 'FILTERS', 'GENERATE', 'GENERATEALL', 'GROUPBY', 'IGNORE', + 'INTERSECT', 'ISONORAFTER', 'KEEPFILTERS', 'LOOKUPVALUE', 'NATURALINNERJOIN', + 'NATURALLEFTOUTERJOIN', 'RELATED', 'RELATEDTABLE', 'ROLLUP', 'ROLLUPADDISSUBTOTAL', + 'ROLLUPGROUP', 'ROLLUPISSUBTOTAL', 'ROW', 'SAMPLE', 'SELECTCOLUMNS', + 'SUBSTITUTEWITHINDEX', 'SUMMARIZE', 'SUMMARIZECOLUMNS', 'TOPN', 'TREATAS', + 'UNION', 'USERELATIONSHIP', 'VALUES', 'SUM', 'SUMX', + 'PATH', 'PATHCONTAINS', 'PATHITEM', 'PATHITEMREVERSE', 'PATHLENGTH', + 'AVERAGE', 'AVERAGEA', 'AVERAGEX', 'COUNT', 'COUNTA', + 'COUNTAX', 'COUNTBLANK', 'COUNTROWS', 'COUNTX', 'DISTINCTCOUNT', + 'DIVIDE', 'GEOMEAN', 'GEOMEANX', 'MAX', 'MAXA', + 'MAXX', 'MEDIAN', 'MEDIANX', 'MIN', 'MINA', + 'MINX', 'PERCENTILE.EXC', 'PERCENTILE.INC', 'PERCENTILEX.EXC', 'PERCENTILEX.INC', + 'PRODUCT', 'PRODUCTX', 'RANK.EQ', 'RANKX', 'STDEV.P', + 'STDEV.S', 'STDEVX.P', 'STDEVX.S', 'VAR.P', 'VAR.S', + 'VARX.P', 'VARX.S', 'XIRR', 'XNPV', + // Scalar + 'DATE', 'DATEDIFF', 'DATEVALUE', 'DAY', 'EDATE', + 'EOMONTH', 'HOUR', 'MINUTE', 'MONTH', 'NOW', + 'SECOND', 'TIME', 'TIMEVALUE', 'TODAY', 'WEEKDAY', + 'WEEKNUM', 'YEAR', 'YEARFRAC', 'CONTAINS', 'CONTAINSROW', + 'CUSTOMDATA', 'ERROR', 'HASONEFILTER', 'HASONEVALUE', 'ISBLANK', + 'ISCROSSFILTERED', 'ISEMPTY', 'ISERROR', 'ISEVEN', 'ISFILTERED', + 'ISLOGICAL', 'ISNONTEXT', 'ISNUMBER', 'ISODD', 'ISSUBTOTAL', + 'ISTEXT', 'USERNAME', 'USERPRINCIPALNAME', 'AND', 'FALSE', + 'IF', 'IFERROR', 'NOT', 'OR', 'SWITCH', + 'TRUE', 'ABS', 'ACOS', 'ACOSH', 'ACOT', + 'ACOTH', 'ASIN', 'ASINH', 'ATAN', 'ATANH', + 'BETA.DIST', 'BETA.INV', 'CEILING', 'CHISQ.DIST', 'CHISQ.DIST.RT', + 'CHISQ.INV', 'CHISQ.INV.RT', 'COMBIN', 'COMBINA', 'CONFIDENCE.NORM', + 'CONFIDENCE.T', 'COS', 'COSH', 'COT', 'COTH', + 'CURRENCY', 'DEGREES', 'EVEN', 'EXP', 'EXPON.DIST', + 'FACT', 'FLOOR', 'GCD', 'INT', 'ISO.CEILING', + 'LCM', 'LN', 'LOG', 'LOG10', 'MOD', + 'MROUND', 'ODD', 'PERMUT', 'PI', 'POISSON.DIST', + 'POWER', 'QUOTIENT', 'RADIANS', 'RAND', 'RANDBETWEEN', + 'ROUND', 'ROUNDDOWN', 'ROUNDUP', 'SIGN', 'SIN', + 'SINH', 'SQRT', 'SQRTPI', 'TAN', 'TANH', + 'TRUNC', 'BLANK', 'CONCATENATE', 'CONCATENATEX', 'EXACT', + 'FIND', 'FIXED', 'FORMAT', 'LEFT', 'LEN', + 'LOWER', 'MID', 'REPLACE', 'REPT', 'RIGHT', + 'SEARCH', 'SUBSTITUTE', 'TRIM', 'UNICHAR', 'UNICODE', + 'UPPER', 'VALUE' + ], + tokenizer: { + root: [ + { include: '@comments' }, + { include: '@whitespace' }, + { include: '@numbers' }, + { include: '@strings' }, + { include: '@complexIdentifiers' }, + [/[;,.]/, 'delimiter'], + [/[({})]/, '@brackets'], + [/[a-z_][a-zA-Z0-9_]*/, + { cases: { '@keywords': 'keyword' + , '@functions': 'keyword' + , '@default': 'identifier' } + } +], + [/[<>=!%&+\-*/|~^]/, 'operator'], + ], + whitespace: [ + [/\s+/, 'white'] + ], + comments: [ + [/\/\/+.*/, 'comment'], + [/\/\*/, { token: 'comment.quote', next: '@comment' }] + ], + comment: [ + [/[^*/]+/, 'comment'], + [/\*\//, { token: 'comment.quote', next: '@pop' }], + [/./, 'comment'] + ], + numbers: [ + [/0[xX][0-9a-fA-F]*/, 'number'], + [/[$][+-]*\d*(\.\d*)?/, 'number'], + [/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/, 'number'] + ], + strings: [ + [/N"/, { token: 'string', next: '@string' }], + [/"/, { token: 'string', next: '@string' }] + ], + string: [ + [/[^"]+/, 'string'], + [/""/, 'string'], + [/"/, { token: 'string', next: '@pop' }] + ], + complexIdentifiers: [ + [/\[/, { token: 'identifier.quote', next: '@bracketedIdentifier' }], + [/'/, { token: 'identifier.quote', next: '@quotedIdentifier' }] + ], + bracketedIdentifier: [ + [/[^\]]+/, 'identifier'], + [/]]/, 'identifier'], + [/]/, { token: 'identifier.quote', next: '@pop' }] + ], + quotedIdentifier: [ + [/[^']+/, 'identifier'], + [/''/, 'identifier'], + [/'/, { token: 'identifier.quote', next: '@pop' }] + ] + } +}; diff --git a/test/all.js b/test/all.js index 43de0c9e..78d1eb54 100644 --- a/test/all.js +++ b/test/all.js @@ -39,6 +39,7 @@ requirejs([ 'out/test/less.test', 'out/test/lua.test', 'out/test/markdown.test', + 'out/test/msdax.test', 'out/test/objective-c.test', 'out/test/php.test', 'out/test/postiats.test', diff --git a/test/msdax.test.ts b/test/msdax.test.ts new file mode 100644 index 00000000..6cc8878d --- /dev/null +++ b/test/msdax.test.ts @@ -0,0 +1,370 @@ +/*--------------------------------------------------------------------------------------------- + * 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 './testRunner'; + +testTokenization('msdax', [ + // Comments + [{ + line: '// a comment', + tokens: [ + { startIndex: 0, type: 'comment.msdax' } + ]}], + + [{ + line: '-almost a comment', + tokens: [ + { startIndex: 0, type: 'operator.msdax' }, + { startIndex: 1, type: 'identifier.msdax' }, + { startIndex: 7, type: 'white.msdax' }, + { startIndex: 8, type: 'identifier.msdax' }, + { startIndex: 9, type: 'white.msdax' }, + { startIndex: 10, type: 'identifier.msdax' } + ]}], + + [{ + line: '/* a full line comment */', + tokens: [ + { startIndex: 0, type: 'comment.quote.msdax' }, + { startIndex: 2, type: 'comment.msdax' }, + { startIndex: 23, type: 'comment.quote.msdax' } + ]}], + + [{ + line: '/* /// *** /// */', + tokens: [ + { startIndex: 0, type: 'comment.quote.msdax' }, + { startIndex: 2, type: 'comment.msdax' }, + { startIndex: 15, type: 'comment.quote.msdax' } + ]}], + + [{ + line: 'define measure x = /* a simple comment */ 1;', + tokens: [ + { startIndex: 0, type: 'keyword.msdax' }, + { startIndex: 6, type: 'white.msdax' }, + { startIndex: 7, type: 'keyword.msdax' }, + { startIndex: 14, type: 'white.msdax' }, + { startIndex: 15, type: 'identifier.msdax' }, + { startIndex: 16, type: 'white.msdax' }, + { startIndex: 17, type: 'operator.msdax' }, + { startIndex: 18, type: 'white.msdax' }, + { startIndex: 19, type: 'comment.quote.msdax' }, + { startIndex: 21, type: 'comment.msdax' }, + { startIndex: 39, type: 'comment.quote.msdax' }, + { startIndex: 41, type: 'white.msdax' }, + { startIndex: 42, type: 'number.msdax' }, + { startIndex: 43, type: 'delimiter.msdax' } + ]}], + + // Numbers + [{ + line: '123', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '-123', + tokens: [ + { startIndex: 0, type: 'operator.msdax' }, + { startIndex: 1, type: 'number.msdax' } + ]}], + + [{ + line: '0xaBc123', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '0XaBc123', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '0x', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '0x0', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '0xAB_CD', + tokens: [ + { startIndex: 0, type: 'number.msdax' }, + { startIndex: 4, type: 'identifier.msdax' } + ]}], + + [{ + line: '.', + tokens: [ + { startIndex: 0, type: 'delimiter.msdax' } + ]}], + + [{ + line: '123', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '123.5678', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '0.99', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '.99', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '99.', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '0.', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '.0', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '1E-2', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '1E+2', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '1E2', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '0.1E2', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '1.E2', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + [{ + line: '.1E2', + tokens: [ + { startIndex: 0, type: 'number.msdax' } + ]}], + + // Identifiers + [{ + line: '_abc01', + tokens: [ + { startIndex: 0, type: 'identifier.msdax' } + ]}], + + [{ + line: 'abc01', + tokens: [ + { startIndex: 0, type: 'identifier.msdax' } + ]}], + + [{ + line: 'evaluate filter', + tokens: [ + { startIndex: 0, type: 'keyword.msdax' }, + { startIndex: 8, type: 'white.msdax' }, + { startIndex: 9, type: 'keyword.msdax' } + ]}], + + [{ + line: '[abc[[ 321 ]] xyz]', + tokens: [ + { startIndex: 0, type: 'identifier.quote.msdax' }, + { startIndex: 1, type: 'identifier.msdax' }, + { startIndex: 17, type: 'identifier.quote.msdax' } + ]}], + + [{ + line: '[abc', + tokens: [ + { startIndex: 0, type: 'identifier.quote.msdax' }, + { startIndex: 1, type: 'identifier.msdax' } + ]}], + + [{ + line: 'define measure \'abc\'[def]', + tokens: [ + { startIndex: 0, type: 'keyword.msdax' }, + { startIndex: 6, type: 'white.msdax' }, + { startIndex: 7, type: 'keyword.msdax' }, + { startIndex: 14, type: 'white.msdax' }, + { startIndex: 15, type: 'identifier.quote.msdax' }, + { startIndex: 16, type: 'identifier.msdax' }, + { startIndex: 19, type: 'identifier.quote.msdax' }, + { startIndex: 21, type: 'identifier.msdax' } + { startIndex: 24, type: 'identifier.quote.msdax' } + ]}], + + [{ + line: 'int', + tokens: [ + { startIndex: 0, type: 'keyword.msdax' } + ]}], + + [{ + line: '[int]', + tokens: [ + { startIndex: 0, type: 'identifier.quote.msdax' }, + { startIndex: 1, type: 'identifier.msdax' }, + { startIndex: 4, type: 'identifier.quote.msdax' } + ]}], + + // Strings + [{ + line: '"abc"" 321 "" xyz"', + tokens: [ + { startIndex: 0, type: 'string.msdax' } + ]}], + + [{ + line: 'define var x=\"a string\"', + tokens: [ + { startIndex: 0, type: 'keyword.msdax' }, + { startIndex: 6, type: 'white.msdax' }, + { startIndex: 7, type: 'keyword.msdax' }, + { startIndex: 10, type: 'white.msdax' }, + { startIndex: 11, type: 'identifier.msdax' }, + { startIndex: 12, type: 'operator.msdax' }, + { startIndex: 13, type: 'string.msdax' } + ]}], + + [{ + line: '"a "" string with quotes"', + tokens: [ + { startIndex: 0, type: 'string.msdax' }, + ]}], + + [{ + line: '"a // string with comment"', + tokens: [ + { startIndex: 0, type: 'string.msdax' }, + ]}], + + [{ + line: 'N"a unicode string"', + tokens: [ + { startIndex: 0, type: 'string.msdax' }, + ]}], + + [{ + line: '"a endless string', + tokens: [ + { startIndex: 0, type: 'string.msdax' }, + ]}], + + // Operators + [{ + line: 'define var x=1+3', + tokens: [ + { startIndex: 0, type: 'keyword.msdax' }, + { startIndex: 6, type: 'white.msdax' }, + { startIndex: 7, type: 'keyword.msdax' }, + { startIndex: 10, type: 'white.msdax' }, + { startIndex: 11, type: 'identifier.msdax' }, + { startIndex: 12, type: 'operator.msdax' }, + { startIndex: 13, type: 'number.msdax' }, + { startIndex: 14, type: 'operator.msdax' }, + { startIndex: 15, type: 'number.msdax' } + ]}], + + [{ + line: 'define var x=1^+abc', + tokens: [ + { startIndex: 0, type: 'keyword.msdax' }, + { startIndex: 6, type: 'white.msdax' }, + { startIndex: 7, type: 'keyword.msdax' }, + { startIndex: 10, type: 'white.msdax' }, + { startIndex: 11, type: 'identifier.msdax' }, + { startIndex: 12, type: 'operator.msdax' }, + { startIndex: 13, type: 'number.msdax' }, + { startIndex: 14, type: 'operator.msdax' }, + { startIndex: 16, type: 'identifier.msdax' } + ]}], + + // Realistic queries and expressions + [{ + line: 'EVALUATE \'Products\' ORDER BY [Product Id] DESC', + tokens: [ + { startIndex: 0, type: 'keyword.msdax' }, + { startIndex: 8, type: 'white.msdax' }, + { startIndex: 9, type: 'identifier.quote.msdax' }, + { startIndex: 10, type: 'identifier.msdax' }, + { startIndex: 18, type: 'identifier.quote.msdax' }, + { startIndex: 19, type: 'white.msdax' }, + { startIndex: 20, type: 'keyword.msdax' }, + { startIndex: 25, type: 'white.msdax' }, + { startIndex: 26, type: 'keyword.msdax' }, + { startIndex: 28, type: 'white.msdax' }, + { startIndex: 29, type: 'identifier.quote.msdax' }, + { startIndex: 30, type: 'identifier.msdax' }, + { startIndex: 40, type: 'identifier.quote.msdax' }, + { startIndex: 41, type: 'white.msdax' }, + { startIndex: 42, type: 'keyword.msdax' } + ]}], + + [{ + line: 'DATATABLE("Price", STRING, {{"Low"},{"Medium"}})', + tokens: [ + { startIndex: 0, type: 'keyword.msdax' }, + { startIndex: 9, type: 'delimiter.parenthesis.msdax' }, + { startIndex: 10, type: 'string.msdax' }, + { startIndex: 17, type: 'delimiter.msdax' }, + { startIndex: 18, type: 'white.msdax' }, + { startIndex: 19, type: 'keyword.msdax' }, + { startIndex: 25, type: 'delimiter.msdax' }, + { startIndex: 26, type: 'white.msdax' }, + { startIndex: 27, type: 'delimiter.brackets.msdax' }, + { startIndex: 29, type: 'string.msdax' }, + { startIndex: 34, type: 'delimiter.brackets.msdax' }, + { startIndex: 35, type: 'delimiter.msdax' }, + { startIndex: 36, type: 'delimiter.brackets.msdax' }, + { startIndex: 37, type: 'string.msdax' }, + { startIndex: 45, type: 'delimiter.brackets.msdax' }, + { startIndex: 47, type: 'delimiter.parenthesis.msdax' } + ]}] +]);