Skip to content

Commit 4fff99e

Browse files
authoredOct 16, 2023
feat: add nasal language (#5342)
* feat: add nasal language * fix: update nasal mode * fix: fix linter errors * feat: add nasal tokens
1 parent 25d86b6 commit 4fff99e

File tree

5 files changed

+479
-0
lines changed

5 files changed

+479
-0
lines changed
 

‎demo/kitchen-sink/docs/nasal.nas

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
var sayHello = func(names, favorite) {
2+
foreach (var name; names) {
3+
printf("Hello %s, %s is the best!", name, favorite);
4+
}
5+
}
6+
7+
sayHello(["World", "FlightGear"], "Nasal");

‎src/ext/modelist.js

+1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ var supportedModes = {
150150
MIXAL: ["mixal"],
151151
MUSHCode: ["mc|mush"],
152152
MySQL: ["mysql"],
153+
Nasal: ["nas"],
153154
Nginx: ["nginx|conf"],
154155
Nim: ["nim"],
155156
Nix: ["nix"],

‎src/mode/_test/tokens_nasal.json

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
[[
2+
"start",
3+
["storage.type.nasal","var"],
4+
["text"," sayHello "],
5+
["keyword.operator.nasal","="],
6+
["text"," func(names, favorite) {"]
7+
],[
8+
"start",
9+
["text"," "],
10+
["keyword.control.nasal","foreach"],
11+
["text"," ("],
12+
["storage.type.nasal","var"],
13+
["text"," name"],
14+
["punctuation.terminator.statement.nasal",";"],
15+
["text"," names) {"]
16+
],[
17+
"start",
18+
["text"," "],
19+
["variable.language.nasal","printf"],
20+
["text","("],
21+
["punctuation.definition.string.begin.nasal","\""],
22+
["string.quoted.double.nasal","Hello "],
23+
["constant.character.escape.nasal","%s"],
24+
["string.quoted.double.nasal",", "],
25+
["constant.character.escape.nasal","%s"],
26+
["string.quoted.double.nasal"," is the best!"],
27+
["punctuation.definition.string.end.nasal","\""],
28+
["text",", name, favorite)"],
29+
["punctuation.terminator.statement.nasal",";"]
30+
],[
31+
"start",
32+
["text"," }"]
33+
],[
34+
"start",
35+
["text","}"]
36+
],[
37+
"start"
38+
],[
39+
"start",
40+
["text","sayHello(["],
41+
["punctuation.definition.string.begin.nasal","\""],
42+
["string.quoted.double.nasal","World"],
43+
["punctuation.definition.string.end.nasal","\""],
44+
["text",", "],
45+
["punctuation.definition.string.begin.nasal","\""],
46+
["string.quoted.double.nasal","FlightGear"],
47+
["punctuation.definition.string.end.nasal","\""],
48+
["text","], "],
49+
["punctuation.definition.string.begin.nasal","\""],
50+
["string.quoted.double.nasal","Nasal"],
51+
["punctuation.definition.string.end.nasal","\""],
52+
["text",")"],
53+
["punctuation.terminator.statement.nasal",";"]
54+
],[
55+
"start"
56+
]]

‎src/mode/nasal.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/* ***** BEGIN LICENSE BLOCK *****
2+
* Distributed under the BSD license:
3+
*
4+
* Copyright (c) 2012, Ajax.org B.V.
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
* * Redistributions in binary form must reproduce the above copyright
12+
* notice, this list of conditions and the following disclaimer in the
13+
* documentation and/or other materials provided with the distribution.
14+
* * Neither the name of Ajax.org B.V. nor the
15+
* names of its contributors may be used to endorse or promote products
16+
* derived from this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
22+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
*
29+
* ***** END LICENSE BLOCK ***** */
30+
31+
/*
32+
THIS FILE WAS AUTOGENERATED BY mode.tmpl.js
33+
*/
34+
35+
"use strict";
36+
37+
var oop = require("../lib/oop");
38+
var TextMode = require("./text").Mode;
39+
var NasalHighlightRules = require("./nasal_highlight_rules").NasalHighlightRules;
40+
// TODO: pick appropriate fold mode
41+
var FoldMode = require("./folding/cstyle").FoldMode;
42+
43+
var Mode = function() {
44+
this.HighlightRules = NasalHighlightRules;
45+
this.foldingRules = new FoldMode();
46+
};
47+
oop.inherits(Mode, TextMode);
48+
49+
(function() {
50+
// this.lineCommentStart = ""//"";
51+
// this.blockComment = {start: ""/*"", end: ""*/""};
52+
// Extra logic goes here.
53+
this.$id = "ace/mode/nasal";
54+
}).call(Mode.prototype);
55+
56+
exports.Mode = Mode;

‎src/mode/nasal_highlight_rules.js

+359
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
/* ***** BEGIN LICENSE BLOCK *****
2+
* Distributed under the BSD license:
3+
*
4+
* Copyright (c) 2012, Ajax.org B.V.
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
* * Redistributions in binary form must reproduce the above copyright
12+
* notice, this list of conditions and the following disclaimer in the
13+
* documentation and/or other materials provided with the distribution.
14+
* * Neither the name of Ajax.org B.V. nor the
15+
* names of its contributors may be used to endorse or promote products
16+
* derived from this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
22+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
*
29+
* ***** END LICENSE BLOCK ***** */
30+
31+
/* This file was autogenerated from https://github.com/BobDotCom/Nasal.tmbundle/blob/95113f60db7cb7ac7b6c3d854683773879407a48/Syntaxes/Nasal.tmLanguage (uuid: ) */
32+
/****************************************************************************************
33+
* IT MIGHT NOT BE PERFECT ...But it's a good start from an existing *.tmlanguage file. *
34+
* fileTypes *
35+
****************************************************************************************/
36+
37+
"use strict";
38+
39+
var oop = require("../lib/oop");
40+
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
41+
42+
var NasalHighlightRules = function() {
43+
// regexp must not have capturing parentheses. Use (?:) instead.
44+
// regexps are ordered -> the first match is used
45+
46+
this.$rules = {
47+
start: [{
48+
token: "constant.other.allcaps.nasal",
49+
regex: /\b[[:upper:]_][[:upper:][:digit:]_]*\b(?![\.\(\'\"])/,
50+
comment: "Match identifiers in ALL_CAPS as constants, except when followed by `.`, `(`, `'`, or `\"`."
51+
}, {
52+
todo: {
53+
token: [
54+
"support.class.nasal",
55+
"meta.function.nasal",
56+
"entity.name.function.nasal",
57+
"meta.function.nasal",
58+
"keyword.operator.nasal",
59+
"meta.function.nasal",
60+
"storage.type.function.nasal",
61+
"meta.function.nasal",
62+
"punctuation.definition.parameters.begin.nasal"
63+
],
64+
regex: /([a-zA-Z_?.$][\w?.$]*)(\.)([a-zA-Z_?.$][\w?.$]*)(\s*)(=)(\s*)(func)(\s*)(\()/,
65+
push: [{
66+
token: "punctuation.definition.parameters.end.nasal",
67+
regex: /\)/,
68+
next: "pop"
69+
}, {
70+
include: "$self"
71+
}, {
72+
token: "variable.parameter.nasal",
73+
regex: /\w/
74+
}, {
75+
defaultToken: "meta.function.nasal"
76+
}]
77+
},
78+
comment: "match stuff like: Sound.play = func() { … }"
79+
}, {
80+
todo: {
81+
token: [
82+
"entity.name.function.nasal",
83+
"meta.function.nasal",
84+
"keyword.operator.nasal",
85+
"meta.function.nasal",
86+
"storage.type.function.nasal",
87+
"meta.function.nasal",
88+
"punctuation.definition.parameters.begin.nasal"
89+
],
90+
regex: /([a-zA-Z_?$][\w?$]*)(\s*)(=)(\s*)(func)(\s*)(\()/,
91+
push: [{
92+
token: "punctuation.definition.parameters.end.nasal",
93+
regex: /\)/,
94+
next: "pop"
95+
}, {
96+
include: "$self"
97+
}, {
98+
token: "variable.parameter.nasal",
99+
regex: /\w/
100+
}, {
101+
defaultToken: "meta.function.nasal"
102+
}]
103+
},
104+
comment: "match stuff like: play = func() { … }"
105+
}, {
106+
todo: {
107+
token: [
108+
"entity.name.function.nasal",
109+
"meta.function.nasal",
110+
"keyword.operator.nasal",
111+
"meta.function.nasal",
112+
"storage.type.function.nasal",
113+
"meta.function.nasal",
114+
"punctuation.definition.parameters.begin.nasal"
115+
],
116+
regex: /([a-zA-Z_?$][\w?$]*)(\s*)(=)(\s*\(\s*)(func)(\s*)(\()/,
117+
push: [{
118+
token: "punctuation.definition.parameters.end.nasal",
119+
regex: /\)/,
120+
next: "pop"
121+
}, {
122+
include: "$self"
123+
}, {
124+
token: "variable.parameter.nasal",
125+
regex: /\w/
126+
}, {
127+
defaultToken: "meta.function.nasal"
128+
}]
129+
},
130+
comment: "match stuff like: play = (func() { … }"
131+
}, {
132+
todo: {
133+
token: [
134+
"entity.name.function.nasal",
135+
"meta.function.hash.nasal",
136+
"storage.type.function.nasal",
137+
"meta.function.hash.nasal",
138+
"punctuation.definition.parameters.begin.nasal"
139+
],
140+
regex: /\b([a-zA-Z_?.$][\w?.$]*)(\s*:\s*\b)(func)(\s*)(\()/,
141+
push: [{
142+
token: "punctuation.definition.parameters.end.nasal",
143+
regex: /\)/,
144+
next: "pop"
145+
}, {
146+
include: "$self"
147+
}, {
148+
token: "variable.parameter.nasal",
149+
regex: /\w/
150+
}, {
151+
defaultToken: "meta.function.hash.nasal"
152+
}]
153+
},
154+
comment: "match stuff like: foobar: func() { … }"
155+
}, {
156+
todo: {
157+
token: [
158+
"storage.type.function.nasal",
159+
"meta.function.nasal",
160+
"punctuation.definition.parameters.begin.nasal"
161+
],
162+
regex: /\b(func)(\s*)(\()/,
163+
push: [{
164+
token: "punctuation.definition.parameters.end.nasal",
165+
regex: /\)/,
166+
next: "pop"
167+
}, {
168+
include: "$self"
169+
}, {
170+
token: "variable.parameter.nasal",
171+
regex: /\w/
172+
}, {
173+
defaultToken: "meta.function.nasal"
174+
}]
175+
},
176+
comment: "match stuff like: func() { … }"
177+
}, {
178+
token: [
179+
"keyword.operator.new.nasal",
180+
"meta.class.instance.constructor",
181+
"entity.name.type.instance.nasal"
182+
],
183+
regex: /(new)(\s+)(\w+(?:\.\w*)?)/
184+
}, {
185+
token: "keyword.control.nasal",
186+
regex: /\b(?:if|else|elsif|while|for|foreach|forindex)\b/
187+
}, {
188+
token: "keyword.control.nasal",
189+
regex: /\b(?:break(?:\s+[A-Z]{2,16})?(?=\s*(?:;|\}))|continue(?:\s+[A-Z]{2,16})?(?=\s*(?:;|\}))|[A-Z]{2,16}(?=\s*;(?:[^\)#;]*?;){0,2}[^\)#;]*?\)))\b/
190+
}, {
191+
token: "keyword.operator.nasal",
192+
regex: /!|\*|\-|\+|~|\/|==|=|!=|<=|>=|<|>|!|\?|\:|\*=|\/=|\+=|\-=|~=|\.\.\.|\b(?:and|or)\b/
193+
}, {
194+
token: "variable.language.nasal",
195+
regex: /\b(?:me|arg|parents|obj)\b/
196+
}, {
197+
token: "storage.type.nasal",
198+
regex: /\b(?:return|var)\b/
199+
}, {
200+
token: "constant.language.nil.nasal",
201+
regex: /\bnil\b/
202+
}, {
203+
token: "punctuation.definition.string.begin.nasal",
204+
regex: /'/,
205+
push: [{
206+
token: "punctuation.definition.string.end.nasal",
207+
regex: /'/,
208+
next: "pop"
209+
}, {
210+
token: "constant.character.escape.nasal",
211+
regex: /\\'/
212+
}, {
213+
defaultToken: "string.quoted.single.nasal"
214+
}],
215+
comment: "Single quoted strings"
216+
}, {
217+
token: "punctuation.definition.string.begin.nasal",
218+
regex: /"/,
219+
push: [{
220+
token: "punctuation.definition.string.end.nasal",
221+
regex: /"/,
222+
next: "pop"
223+
}, {
224+
token: "constant.character.escape.nasal",
225+
regex: /\\(?:x[\da-fA-F]{2}|[0-2][0-7]{,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|r|n|t|\\|")/
226+
}, {
227+
token: "constant.character.escape.nasal",
228+
regex: /%(?:%|(?:\d+\$)?[+-]?(?:[ 0]|'.{1})?-?\d*(?:\.\d+)?[bcdeEufFgGosxX])/
229+
}, {
230+
defaultToken: "string.quoted.double.nasal"
231+
}],
232+
comment: "Double quoted strings"
233+
}, {
234+
token: [
235+
"punctuation.definition.string.begin.nasal",
236+
"string.other",
237+
"punctuation.definition.string.end.nasal"
238+
],
239+
regex: /(`)(.)(`)/,
240+
comment: "Single-byte ASCII character constants"
241+
}, {
242+
token: [
243+
"punctuation.definition.comment.nasal",
244+
"comment.line.hash.nasal"
245+
],
246+
regex: /(#)(.*$)/,
247+
comment: "Comments"
248+
}, {
249+
token: "constant.numeric.nasal",
250+
regex: /(?:(?:\b[0-9]+)?\.)?\b[0-9]+(?:[eE][-+]?[0-9]+)?\b/,
251+
comment: "Integers, floats, and scientific format"
252+
}, {
253+
token: "constant.numeric.nasal",
254+
regex: /0[x|X][0-9a-fA-F]+/,
255+
comment: "Hex codes"
256+
}, {
257+
token: "punctuation.terminator.statement.nasal",
258+
regex: /\;/
259+
}, {
260+
token: [
261+
"punctuation.section.scope.begin.nasal",
262+
"punctuation.section.scope.end.nasal"
263+
],
264+
regex: /(\[)(\])/
265+
}, {
266+
todo: {
267+
token: "punctuation.section.scope.begin.nasal",
268+
regex: /\{/,
269+
push: [{
270+
token: "punctuation.section.scope.end.nasal",
271+
regex: /\}/,
272+
next: "pop"
273+
}, {
274+
include: "$self"
275+
}]
276+
}
277+
}, {
278+
todo: {
279+
token: "punctuation.section.scope.begin.nasal",
280+
regex: /\(/,
281+
push: [{
282+
token: "punctuation.section.scope.end.nasal",
283+
regex: /\)/,
284+
next: "pop"
285+
}, {
286+
include: "$self"
287+
}]
288+
}
289+
}, {
290+
token: "invalid.illegal",
291+
regex: /%|\$|@|&|\^|\||\\|`/,
292+
comment: "Illegal characters"
293+
}, {
294+
todo: {
295+
comment: "TODO: Symbols in hash keys"
296+
},
297+
comment: "TODO: Symbols in hash keys"
298+
}, {
299+
token: "variable.language.nasal",
300+
regex: /\b(?:append|bind|call|caller|chr|closure|cmp|compile|contains|delete|die|find|ghosttype|id|int|keys|left|num|pop|right|setsize|size|sort|split|sprintf|streq|substr|subvec|typeof|readline)\b/,
301+
comment: "Core functions"
302+
}, {
303+
token: "variable.language.nasal",
304+
regex: /\b(?:abort|abs|aircraftToCart|addcommand|airportinfo|airwaysRoute|assert|carttogeod|cmdarg|courseAndDistance|createDiscontinuity|createViaTo|createWP|createWPFrom|defined|directory|fgcommand|findAirportsByICAO|findAirportsWithinRange|findFixesByID|findNavaidByFrequency|findNavaidsByFrequency|findNavaidsByID|findNavaidsWithinRange|finddata|flightplan|geodinfo|geodtocart|get_cart_ground_intersection|getprop|greatCircleMove|interpolate|isa|logprint|magvar|maketimer|start|stop|restart|maketimestamp|md5|navinfo|parse_markdown|parsexml|print|printf|printlog|rand|registerFlightPlanDelegate|removecommand|removelistener|resolvepath|setlistener|_setlistener|setprop|srand|systime|thisfunc|tileIndex|tilePath|values)\b/,
305+
comment: "FG ext core functions"
306+
}, {
307+
token: "variable.language.nasal",
308+
regex: /\b(?:singleShot|isRunning|simulatedTime)\b/,
309+
comment: "FG ext core functions"
310+
}, {
311+
token: "constant.language.nasal",
312+
regex: /\b(?:D2R|FPS2KT|FT2M|GAL2L|IN2M|KG2LB|KT2FPS|KT2MPS|LG2GAL|LB2KG|M2FT|M2IN|M2NM|MPS2KT|NM2M|R2D)\b/,
313+
comment: "FG ext core constants"
314+
}, {
315+
token: "support.function.nasal",
316+
regex: /\b(?:addChild|addChildren|alias|clearValue|equals|getAliasTarget|getAttribute|getBoolValue|getChild|getChildren|getIndex|getName|getNode|getParent|getPath|getType|getValue|getValues|initNode|remove|removeAllChildren|removeChild|removeChildren|setAttribute|setBoolValue|setDoubleValue|setIntValue|setValue|setValues|unalias|compileCondition|condition|copy|dump|getNode|nodeList|runBinding|setAll|wrap|wrapNode)\b/,
317+
comment: "FG func props"
318+
}, {
319+
token: "support.class.nasal",
320+
regex: /\bNode\b/,
321+
comment: "FG node class"
322+
}, {
323+
token: "variable.language.nasal",
324+
regex: /\b(?:props|globals)\b/,
325+
comment: "FG func props variables"
326+
}, {
327+
todo: {
328+
token: [
329+
"support.function.nasal",
330+
"punctuation.definition.arguments.begin.nasal"
331+
],
332+
regex: /\b([a-zA-Z_?$][\w?$]*)(\()/,
333+
push: [{
334+
token: "punctuation.definition.arguments.end.nasal",
335+
regex: /\)/,
336+
next: "pop"
337+
}, {
338+
include: "$self"
339+
}, {
340+
defaultToken: "meta.function-call.nasal"
341+
}]
342+
},
343+
comment: "function call"
344+
}]
345+
};
346+
347+
this.normalizeRules();
348+
};
349+
350+
NasalHighlightRules.metaData = {
351+
fileTypes: ["nas"],
352+
name: "Nasal",
353+
scopeName: "source.nasal"
354+
};
355+
356+
357+
oop.inherits(NasalHighlightRules, TextHighlightRules);
358+
359+
exports.NasalHighlightRules = NasalHighlightRules;

0 commit comments

Comments
 (0)
Please sign in to comment.