From dffb2ebf1f1e93799ec6cbf1d42155ace35e892c Mon Sep 17 00:00:00 2001
From: JC Chu <jcchu@acm.org>
Date: Wed, 28 Oct 2020 00:24:34 +0800
Subject: [PATCH] Add Modula-3 support

---
 src/m3/m3.contribution.ts  |  13 +++
 src/m3/m3.test.ts          | 119 +++++++++++++++++++
 src/m3/m3.ts               | 226 +++++++++++++++++++++++++++++++++++++
 src/monaco.contribution.ts |   1 +
 4 files changed, 359 insertions(+)
 create mode 100644 src/m3/m3.contribution.ts
 create mode 100644 src/m3/m3.test.ts
 create mode 100644 src/m3/m3.ts

diff --git a/src/m3/m3.contribution.ts b/src/m3/m3.contribution.ts
new file mode 100644
index 00000000..bbb84fb1
--- /dev/null
+++ b/src/m3/m3.contribution.ts
@@ -0,0 +1,13 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { registerLanguage } from '../_.contribution';
+
+registerLanguage({
+	id: 'm3',
+	extensions: ['.m3', '.i3', '.mg', '.ig'],
+	aliases: ['Modula-3', 'Modula3', 'modula3', 'm3'],
+	loader: () => import('./m3')
+});
diff --git a/src/m3/m3.test.ts b/src/m3/m3.test.ts
new file mode 100644
index 00000000..c089527e
--- /dev/null
+++ b/src/m3/m3.test.ts
@@ -0,0 +1,119 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { testTokenization } from '../test/testRunner';
+
+testTokenization('m3', [
+	[
+		{
+			line: '(**)',
+			tokens: [{ startIndex: 0, type: 'comment.m3' }]
+		}
+	],
+
+	[
+		{
+			line: '    (* a comment *)',
+			tokens: [
+				{ startIndex: 0, type: 'white.m3' },
+				{ startIndex: 4, type: 'comment.m3' }
+			]
+		}
+	],
+
+	[
+		{
+			line: '(* Lorem ipsum dolor sit amet, consectetur ',
+			tokens: [{ startIndex: 0, type: 'comment.m3' }]
+		},
+		{
+			line: '   adipiscing elit, sed do eiusmod tempor',
+			tokens: [{ startIndex: 0, type: 'comment.m3' }]
+		},
+		{
+			line: '  incididunt ut labore et dolore magna aliqua. *)',
+			tokens: [{ startIndex: 0, type: 'comment.m3' }]
+		}
+	],
+
+	[
+		{
+			line: '(* Lorem ipsum dolor sit amet (*, consectetur ',
+			tokens: [{ startIndex: 0, type: 'comment.m3' }]
+		},
+		{
+			line: '   adipiscing elit, sed do eiusmod tempor',
+			tokens: [{ startIndex: 0, type: 'comment.m3' }]
+		},
+		{
+			line: '  incididunt*) ut labore et dolore magna aliqua. *)',
+			tokens: [{ startIndex: 0, type: 'comment.m3' }]
+		}
+	],
+
+	[
+		{
+			line: 'MODULE Test EXPORTS Main; FROM IO IMPORT Put; BEGIN Put("test\\n") END Test.',
+			tokens: [
+				{ startIndex: 0, type: 'keyword.MODULE.m3' },
+				{ startIndex: 6, type: 'white.m3' },
+				{ startIndex: 7, type: 'identifier.m3' },
+				{ startIndex: 11, type: 'white.m3' },
+				{ startIndex: 12, type: 'keyword.EXPORTS.m3' },
+				{ startIndex: 19, type: 'white.m3' },
+				{ startIndex: 20, type: 'identifier.m3' },
+				{ startIndex: 24, type: 'delimiter.m3' },
+				{ startIndex: 25, type: 'white.m3' },
+
+				{ startIndex: 26, type: 'keyword.FROM.m3' },
+				{ startIndex: 30, type: 'white.m3' },
+				{ startIndex: 31, type: 'identifier.m3' },
+				{ startIndex: 33, type: 'white.m3' },
+				{ startIndex: 34, type: 'keyword.IMPORT.m3' },
+				{ startIndex: 40, type: 'white.m3' },
+				{ startIndex: 41, type: 'identifier.m3' },
+				{ startIndex: 44, type: 'delimiter.m3' },
+				{ startIndex: 45, type: 'white.m3' },
+
+				{ startIndex: 46, type: 'keyword.BEGIN.m3' },
+				{ startIndex: 51, type: 'white.m3' },
+				{ startIndex: 52, type: 'identifier.m3' },
+				{ startIndex: 55, type: 'delimiter.parenthesis.m3' },
+				{ startIndex: 56, type: 'string.text.m3' },
+				{ startIndex: 61, type: 'string.escape.m3' },
+				{ startIndex: 63, type: 'string.text.m3' },
+				{ startIndex: 64, type: 'delimiter.parenthesis.m3' },
+				{ startIndex: 65, type: 'white.m3' },
+
+				{ startIndex: 66, type: 'keyword.END.m3' },
+				{ startIndex: 69, type: 'white.m3' },
+				{ startIndex: 70, type: 'identifier.m3' },
+				{ startIndex: 74, type: 'operators.m3' }
+			]
+		}
+	],
+
+	[
+		{
+			line: '0',
+			tokens: [{ startIndex: 0, type: 'number.m3' }]
+		}
+	],
+	[
+		{
+			line: '-16_B33f',
+			tokens: [
+				{ startIndex: 0, type: 'operators.m3' },
+				{ startIndex: 1, type: 'number.m3' }
+			]
+		}
+	],
+	[
+		{
+			line: '2.0D-5',
+			tokens: [{ startIndex: 0, type: 'number.float.m3' }]
+		}
+	]
+]);
diff --git a/src/m3/m3.ts b/src/m3/m3.ts
new file mode 100644
index 00000000..243f36bd
--- /dev/null
+++ b/src/m3/m3.ts
@@ -0,0 +1,226 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import type { languages } from '../fillers/monaco-editor-core';
+
+export const conf: languages.LanguageConfiguration = {
+	comments: {
+		blockComment: ['(*', '*)']
+	},
+	brackets: [
+		['{', '}'],
+		['[', ']'],
+		['(', ')']
+	],
+	autoClosingPairs: [
+		{ open: '[', close: ']' },
+		{ open: '{', close: '}' },
+		{ open: '(', close: ')' },
+		{ open: '(*', close: '*)' },
+		{ open: '<*', close: '*>' },
+		{ open: "'", close: "'", notIn: ['string', 'comment'] },
+		{ open: '"', close: '"', notIn: ['string', 'comment'] }
+	]
+};
+
+export const language = <languages.IMonarchLanguage>{
+	defaultToken: '',
+	tokenPostfix: '.m3',
+
+	brackets: [
+		{ token: 'delimiter.curly', open: '{', close: '}' },
+		{ token: 'delimiter.parenthesis', open: '(', close: ')' },
+		{ token: 'delimiter.square', open: '[', close: ']' }
+	],
+	keywords: [
+		'AND',
+		'ANY',
+		'ARRAY',
+		'AS',
+		'BEGIN',
+		'BITS',
+		'BRANDED',
+		'BY',
+		'CASE',
+		'CONST',
+		'DIV',
+		'DO',
+		'ELSE',
+		'ELSIF',
+		'END',
+		'EVAL',
+		'EXCEPT',
+		'EXCEPTION',
+		'EXIT',
+		'EXPORTS',
+		'FINALLY',
+		'FOR',
+		'FROM',
+		'GENERIC',
+		'IF',
+		'IMPORT',
+		'IN',
+		'INTERFACE',
+		'LOCK',
+		'LOOP',
+		'METHODS',
+		'MOD',
+		'MODULE',
+		'NOT',
+		'OBJECT',
+		'OF',
+		'OR',
+		'OVERRIDES',
+		'PROCEDURE',
+		'RAISE',
+		'RAISES',
+		'READONLY',
+		'RECORD',
+		'REF',
+		'REPEAT',
+		'RETURN',
+		'REVEAL',
+		'SET',
+		'THEN',
+		'TO',
+		'TRY',
+		'TYPE',
+		'TYPECASE',
+		'UNSAFE',
+		'UNTIL',
+		'UNTRACED',
+		'VALUE',
+		'VAR',
+		'WHILE',
+		'WITH'
+	],
+	reservedConstNames: [
+		'ABS',
+		'ADR',
+		'ADRSIZE',
+		'BITSIZE',
+		'BYTESIZE',
+		'CEILING',
+		'DEC',
+		'DISPOSE',
+		'FALSE',
+		'FIRST',
+		'FLOAT',
+		'FLOOR',
+		'INC',
+		'ISTYPE',
+		'LAST',
+		'LOOPHOLE',
+		'MAX',
+		'MIN',
+		'NARROW',
+		'NEW',
+		'NIL',
+		'NUMBER',
+		'ORD',
+		'ROUND',
+		'SUBARRAY',
+		'TRUE',
+		'TRUNC',
+		'TYPECODE',
+		'VAL'
+	],
+	reservedTypeNames: [
+		'ADDRESS',
+		'ANY',
+		'BOOLEAN',
+		'CARDINAL',
+		'CHAR',
+		'EXTENDED',
+		'INTEGER',
+		'LONGCARD',
+		'LONGINT',
+		'LONGREAL',
+		'MUTEX',
+		'NULL',
+		'REAL',
+		'REFANY',
+		'ROOT',
+		'TEXT'
+	],
+	operators: ['+', '-', '*', '/', '&', '^', '.'],
+	relations: ['=', '#', '<', '<=', '>', '>=', '<:', ':'],
+	delimiters: ['|', '..', '=>', ',', ';', ':='],
+	symbols: /[>=<#.,:;+\-*/&^]+/,
+	escapes: /\\(?:[\\fnrt"']|[0-7]{3})/,
+
+	tokenizer: {
+		root: [
+			// Identifiers and keywords
+			[/_\w*/, 'invalid'],
+			[
+				/[a-zA-Z][a-zA-Z0-9_]*/,
+				{
+					cases: {
+						'@keywords': { token: 'keyword.$0' },
+						'@reservedConstNames': { token: 'constant.reserved.$0' },
+						'@reservedTypeNames': { token: 'type.reserved.$0' },
+						'@default': 'identifier'
+					}
+				}
+			],
+
+			// Whitespace
+			{ include: '@whitespace' },
+			[/[{}()\[\]]/, '@brackets'],
+
+			// Integer- and real literals
+			[/[0-9]+\.[0-9]+(?:[DdEeXx][\+\-]?[0-9]+)?/, 'number.float'],
+			[/[0-9]+(?:\_[0-9a-fA-F]+)?L?/, 'number'],
+
+			// Operators, relations, and delimiters
+			[
+				/@symbols/,
+				{
+					cases: {
+						'@operators': 'operators',
+						'@relations': 'operators',
+						'@delimiters': 'delimiter',
+						'@default': 'invalid'
+					}
+				}
+			],
+
+			// Character literals
+			[/'[^\\']'/, 'string.char'],
+			[/(')(@escapes)(')/, ['string.char', 'string.escape', 'string.char']],
+			[/'/, 'invalid'],
+
+			// Text literals
+			[/"([^"\\]|\\.)*$/, 'invalid'],
+			[/"/, 'string.text', '@text']
+		],
+
+		text: [
+			[/[^\\"]+/, 'string.text'],
+			[/@escapes/, 'string.escape'],
+			[/\\./, 'invalid'],
+			[/"/, 'string.text', '@pop']
+		],
+
+		comment: [
+			[/\(\*/, 'comment', '@push'],
+			[/\*\)/, 'comment', '@pop'],
+			[/./, 'comment']
+		],
+
+		pragma: [
+			[/<\*/, 'keyword.pragma', '@push'],
+			[/\*>/, 'keyword.pragma', '@pop'],
+			[/./, 'keyword.pragma']
+		],
+
+		whitespace: [
+			[/[ \t\r\n]+/, 'white'],
+			[/\(\*/, 'comment', '@comment'],
+			[/<\*/, 'keyword.pragma', '@pragma']
+		]
+	}
+};
diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts
index d67526d5..23151390 100644
--- a/src/monaco.contribution.ts
+++ b/src/monaco.contribution.ts
@@ -30,6 +30,7 @@ import './kotlin/kotlin.contribution';
 import './less/less.contribution';
 import './lexon/lexon.contribution';
 import './lua/lua.contribution';
+import './m3/m3.contribution';
 import './markdown/markdown.contribution';
 import './mips/mips.contribution';
 import './msdax/msdax.contribution';