scripts/CodeMirror/mode/oz/oz.js (view raw)
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: https://codemirror.net/LICENSE
3
4(function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11})(function(CodeMirror) {
12"use strict";
13
14CodeMirror.defineMode("oz", function (conf) {
15
16 function wordRegexp(words) {
17 return new RegExp("^((" + words.join(")|(") + "))\\b");
18 }
19
20 var singleOperators = /[\^@!\|<>#~\.\*\-\+\\/,=]/;
21 var doubleOperators = /(<-)|(:=)|(=<)|(>=)|(<=)|(<:)|(>:)|(=:)|(\\=)|(\\=:)|(!!)|(==)|(::)/;
22 var tripleOperators = /(:::)|(\.\.\.)|(=<:)|(>=:)/;
23
24 var middle = ["in", "then", "else", "of", "elseof", "elsecase", "elseif", "catch",
25 "finally", "with", "require", "prepare", "import", "export", "define", "do"];
26 var end = ["end"];
27
28 var atoms = wordRegexp(["true", "false", "nil", "unit"]);
29 var commonKeywords = wordRegexp(["andthen", "at", "attr", "declare", "feat", "from", "lex",
30 "mod", "div", "mode", "orelse", "parser", "prod", "prop", "scanner", "self", "syn", "token"]);
31 var openingKeywords = wordRegexp(["local", "proc", "fun", "case", "class", "if", "cond", "or", "dis",
32 "choice", "not", "thread", "try", "raise", "lock", "for", "suchthat", "meth", "functor"]);
33 var middleKeywords = wordRegexp(middle);
34 var endKeywords = wordRegexp(end);
35
36 // Tokenizers
37 function tokenBase(stream, state) {
38 if (stream.eatSpace()) {
39 return null;
40 }
41
42 // Brackets
43 if(stream.match(/[{}]/)) {
44 return "bracket";
45 }
46
47 // Special [] keyword
48 if (stream.match(/(\[])/)) {
49 return "keyword"
50 }
51
52 // Operators
53 if (stream.match(tripleOperators) || stream.match(doubleOperators)) {
54 return "operator";
55 }
56
57 // Atoms
58 if(stream.match(atoms)) {
59 return 'atom';
60 }
61
62 // Opening keywords
63 var matched = stream.match(openingKeywords);
64 if (matched) {
65 if (!state.doInCurrentLine)
66 state.currentIndent++;
67 else
68 state.doInCurrentLine = false;
69
70 // Special matching for signatures
71 if(matched[0] == "proc" || matched[0] == "fun")
72 state.tokenize = tokenFunProc;
73 else if(matched[0] == "class")
74 state.tokenize = tokenClass;
75 else if(matched[0] == "meth")
76 state.tokenize = tokenMeth;
77
78 return 'keyword';
79 }
80
81 // Middle and other keywords
82 if (stream.match(middleKeywords) || stream.match(commonKeywords)) {
83 return "keyword"
84 }
85
86 // End keywords
87 if (stream.match(endKeywords)) {
88 state.currentIndent--;
89 return 'keyword';
90 }
91
92 // Eat the next char for next comparisons
93 var ch = stream.next();
94
95 // Strings
96 if (ch == '"' || ch == "'") {
97 state.tokenize = tokenString(ch);
98 return state.tokenize(stream, state);
99 }
100
101 // Numbers
102 if (/[~\d]/.test(ch)) {
103 if (ch == "~") {
104 if(! /^[0-9]/.test(stream.peek()))
105 return null;
106 else if (( stream.next() == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) || stream.match(/^[0-9]*(\.[0-9]+)?([eE][~+]?[0-9]+)?/))
107 return "number";
108 }
109
110 if ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) || stream.match(/^[0-9]*(\.[0-9]+)?([eE][~+]?[0-9]+)?/))
111 return "number";
112
113 return null;
114 }
115
116 // Comments
117 if (ch == "%") {
118 stream.skipToEnd();
119 return 'comment';
120 }
121 else if (ch == "/") {
122 if (stream.eat("*")) {
123 state.tokenize = tokenComment;
124 return tokenComment(stream, state);
125 }
126 }
127
128 // Single operators
129 if(singleOperators.test(ch)) {
130 return "operator";
131 }
132
133 // If nothing match, we skip the entire alphanumerical block
134 stream.eatWhile(/\w/);
135
136 return "variable";
137 }
138
139 function tokenClass(stream, state) {
140 if (stream.eatSpace()) {
141 return null;
142 }
143 stream.match(/([A-Z][A-Za-z0-9_]*)|(`.+`)/);
144 state.tokenize = tokenBase;
145 return "variable-3"
146 }
147
148 function tokenMeth(stream, state) {
149 if (stream.eatSpace()) {
150 return null;
151 }
152 stream.match(/([a-zA-Z][A-Za-z0-9_]*)|(`.+`)/);
153 state.tokenize = tokenBase;
154 return "def"
155 }
156
157 function tokenFunProc(stream, state) {
158 if (stream.eatSpace()) {
159 return null;
160 }
161
162 if(!state.hasPassedFirstStage && stream.eat("{")) {
163 state.hasPassedFirstStage = true;
164 return "bracket";
165 }
166 else if(state.hasPassedFirstStage) {
167 stream.match(/([A-Z][A-Za-z0-9_]*)|(`.+`)|\$/);
168 state.hasPassedFirstStage = false;
169 state.tokenize = tokenBase;
170 return "def"
171 }
172 else {
173 state.tokenize = tokenBase;
174 return null;
175 }
176 }
177
178 function tokenComment(stream, state) {
179 var maybeEnd = false, ch;
180 while (ch = stream.next()) {
181 if (ch == "/" && maybeEnd) {
182 state.tokenize = tokenBase;
183 break;
184 }
185 maybeEnd = (ch == "*");
186 }
187 return "comment";
188 }
189
190 function tokenString(quote) {
191 return function (stream, state) {
192 var escaped = false, next, end = false;
193 while ((next = stream.next()) != null) {
194 if (next == quote && !escaped) {
195 end = true;
196 break;
197 }
198 escaped = !escaped && next == "\\";
199 }
200 if (end || !escaped)
201 state.tokenize = tokenBase;
202 return "string";
203 };
204 }
205
206 function buildElectricInputRegEx() {
207 // Reindentation should occur on [] or on a match of any of
208 // the block closing keywords, at the end of a line.
209 var allClosings = middle.concat(end);
210 return new RegExp("[\\[\\]]|(" + allClosings.join("|") + ")$");
211 }
212
213 return {
214
215 startState: function () {
216 return {
217 tokenize: tokenBase,
218 currentIndent: 0,
219 doInCurrentLine: false,
220 hasPassedFirstStage: false
221 };
222 },
223
224 token: function (stream, state) {
225 if (stream.sol())
226 state.doInCurrentLine = 0;
227
228 return state.tokenize(stream, state);
229 },
230
231 indent: function (state, textAfter) {
232 var trueText = textAfter.replace(/^\s+|\s+$/g, '');
233
234 if (trueText.match(endKeywords) || trueText.match(middleKeywords) || trueText.match(/(\[])/))
235 return conf.indentUnit * (state.currentIndent - 1);
236
237 if (state.currentIndent < 0)
238 return 0;
239
240 return state.currentIndent * conf.indentUnit;
241 },
242 fold: "indent",
243 electricInput: buildElectricInputRegEx(),
244 lineComment: "%",
245 blockCommentStart: "/*",
246 blockCommentEnd: "*/"
247 };
248});
249
250CodeMirror.defineMIME("text/x-oz", "oz");
251
252});