Commit 4943abf4980f9d20cf7aa3e0ccfa3be071b4ae72

TSUYUSATO Kitsune 2015-09-26T23:45:28

Add Crystal language definition

diff --git a/components.js b/components.js
index c7c0cae..87cb5cf 100644
--- a/components.js
+++ b/components.js
@@ -130,6 +130,11 @@ var components = {
 			"require": "javascript",
 			"owner": "R-osey"
 		},
+		"crystal": {
+			"title": "Crystal",
+			"require": "ruby",
+			"owner": "MakeNowJust"
+		},
 		"css-extras": {
 			"title": "CSS Extras",
 			"require": "css",
diff --git a/components/prism-crystal.js b/components/prism-crystal.js
new file mode 100644
index 0000000..cb22482
--- /dev/null
+++ b/components/prism-crystal.js
@@ -0,0 +1,54 @@
+(function(Prism) {
+	Prism.languages.crystal = Prism.languages.extend('ruby', {
+		number: /\b(?:0b[01_]*[01]|0o[0-7_]*[0-7]|0x[0-9a-fA-F_]*[0-9a-fA-F]|(?:[0-9][0-9_]*[0-9]|[0-9])(?:\.[0-9_]*[0-9])?(?:[eE][+-]?[0-9_]*[0-9])?)(?:_[uif](?:8|16|32|64|))?\b/,
+	});
+
+	Prism.languages.insertBefore('crystal', 'operator', {
+		keyword: [
+			/\b(?:abstract|alias|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|ifdef|include|instance_sizeof|lib|macro|module|next|of|out|pointerof|private|protected|rescue|return|require|self|sizeof|struct|super|then|type|typeof|undef|union|unless|until|when|while|with|yield|__DIR__|__FILE__|__LINE__)\b/,
+			{
+				pattern: /(\.\s*)(is_a|responds_to)\?/,
+				lookbehind: true
+			}
+		]
+	});
+
+	var rest = Prism.util.clone(Prism.languages.crystal);
+
+	Prism.languages.insertBefore('crystal', 'string', {
+		attribute: {
+			pattern: /@\[.+?\]/,
+			alias: 'attr-name',
+			inside: {
+				delimiter: {
+					pattern: /^@\[|\]$/,
+					alias: 'tag'
+				},
+				rest: rest
+			},
+		},
+		expansion: [
+			{
+				pattern: /\{\{.+?\}\}/,
+				inside: {
+					delimiter: {
+						pattern: /^\{\{|\}\}$/,
+						alias: 'tag'
+					},
+					rest: rest
+				}
+			},
+			{
+				pattern: /\{%.+?%\}/,
+				inside: {
+					delimiter: {
+						pattern: /^\{%|%\}$/,
+						alias: 'tag'
+					},
+					rest: rest
+				}
+			}
+		]
+	});
+
+}(Prism));
diff --git a/components/prism-crystal.min.js b/components/prism-crystal.min.js
new file mode 100644
index 0000000..a5a2f36
--- /dev/null
+++ b/components/prism-crystal.min.js
@@ -0,0 +1 @@
+!function(e){e.languages.crystal=e.languages.extend("ruby",{number:/\b(?:0b[01_]*[01]|0o[0-7_]*[0-7]|0x[0-9a-fA-F_]*[0-9a-fA-F]|(?:[0-9][0-9_]*[0-9]|[0-9])(?:\.[0-9_]*[0-9])?(?:[eE][+-]?[0-9_]*[0-9])?)(?:_[uif](?:8|16|32|64|))?\b/}),e.languages.insertBefore("crystal","operator",{keyword:[/\b(?:abstract|alias|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|ifdef|include|instance_sizeof|lib|macro|module|next|of|out|pointerof|private|protected|rescue|return|require|self|sizeof|struct|super|then|type|typeof|undef|union|unless|until|when|while|with|yield|__DIR__|__FILE__|__LINE__)\b/,{pattern:/(\.\s*)(is_a|responds_to)\?/,lookbehind:!0}]});var t=e.util.clone(e.languages.crystal);e.languages.insertBefore("crystal","string",{attribute:{pattern:/@\[.+?\]/,alias:"attr-name",inside:{delimiter:{pattern:/^@\[|\]$/,alias:"tag"},rest:t}},expansion:[{pattern:/\{\{.+?\}\}/,inside:{delimiter:{pattern:/^\{\{|\}\}$/,alias:"tag"},rest:t}},{pattern:/\{%.+?%\}/,inside:{delimiter:{pattern:/^\{%|%\}$/,alias:"tag"},rest:t}}]})}(Prism);
\ No newline at end of file
diff --git a/tests/languages/crystal/attribute_feature.test b/tests/languages/crystal/attribute_feature.test
new file mode 100644
index 0000000..02d07af
--- /dev/null
+++ b/tests/languages/crystal/attribute_feature.test
@@ -0,0 +1,21 @@
+@[AlwaysInline]
+@[CallConvention("X86_StdCall")]
+
+----------------------------------------------------
+
+[
+	["attribute", [
+		["delimiter", "@["],
+		["constant", "AlwaysInline"],
+		["delimiter", "]"]
+	]],
+	["attribute", [
+		["delimiter", "@["],
+		["function", "CallConvention"], ["punctuation", "("], ["string", [ "\"X86_StdCall\"" ]], ["punctuation", ")"],
+		["delimiter", "]"]
+	]]
+]
+
+----------------------------------------------------
+
+Checks for attributes.
diff --git a/tests/languages/crystal/expansion_feature.test b/tests/languages/crystal/expansion_feature.test
new file mode 100644
index 0000000..b40bd1f
--- /dev/null
+++ b/tests/languages/crystal/expansion_feature.test
@@ -0,0 +1,37 @@
+{{ 1_u32 }}
+{% 2_u32 %}
+{{ { 3_u32 } }}
+{% % 4_u32 % %}
+
+----------------------------------------------------
+
+[
+	["expansion", [
+		["delimiter", "{{"],
+		["number", "1_u32"],
+		["delimiter", "}}"]
+	]],
+	["expansion", [
+		["delimiter", "{%"],
+		["number", "2_u32"],
+		["delimiter", "%}"]
+	]],
+	["expansion", [
+		["delimiter", "{{"],
+		["punctuation", "{"],
+		["number", "3_u32"],
+		["punctuation", "}"],
+		["delimiter", "}}"]
+	]],
+	["expansion", [
+		["delimiter", "{%"],
+		["operator", "%"],
+		["number", "4_u32"],
+		["operator", "%"],
+		["delimiter", "%}"]
+	]]
+]
+
+----------------------------------------------------
+
+Checks for macro expansions.
diff --git a/tests/languages/crystal/keyword_feature.test b/tests/languages/crystal/keyword_feature.test
new file mode 100644
index 0000000..2d58f50
--- /dev/null
+++ b/tests/languages/crystal/keyword_feature.test
@@ -0,0 +1,33 @@
+abstract alias as asm begin break case
+class;
+def do else elsif
+end ensure enum extend for fun
+if ifdef include instance_sizeof
+.is_a?
+lib macro module next of out pointerof
+private protected rescue
+.responds_to?
+return require self sizeof struct super
+then type typeof undef union unless until when
+while with yield __DIR__ __FILE__ __LINE__
+
+----------------------------------------------------
+
+[
+	["keyword", "abstract"], ["keyword", "alias"], ["keyword", "as"], ["keyword", "asm"], ["keyword", "begin"], ["keyword", "break"], ["keyword", "case"],
+	["keyword", "class"], ["punctuation", ";"],
+	["keyword", "def"], ["keyword", "do"], ["keyword", "else"], ["keyword", "elsif"],
+	["keyword", "end"], ["keyword", "ensure"], ["keyword", "enum"], ["keyword", "extend"], ["keyword", "for"], ["keyword", "fun"],
+	["keyword", "if"], ["keyword", "ifdef"], ["keyword", "include"], ["keyword", "instance_sizeof"],
+	["punctuation", "."], ["keyword", "is_a?"],
+	["keyword", "lib"], ["keyword", "macro"], ["keyword", "module"], ["keyword", "next"], ["keyword", "of"], ["keyword", "out"], ["keyword", "pointerof"],
+	["keyword", "private"], ["keyword", "protected"], ["keyword", "rescue"],
+	["punctuation", "."], ["keyword", "responds_to?"],
+	["keyword", "return"], ["keyword", "require"], ["keyword", "self"], ["keyword", "sizeof"], ["keyword", "struct"], ["keyword", "super"],
+	["keyword", "then"], ["keyword", "type"], ["keyword", "typeof"], ["keyword", "undef"], ["keyword", "union"], ["keyword", "unless"], ["keyword", "until"], ["keyword", "when"],
+	["keyword", "while"], ["keyword", "with"], ["keyword", "yield"], ["keyword", "__DIR__"], ["keyword", "__FILE__"], ["keyword", "__LINE__"]
+]
+
+----------------------------------------------------
+
+Checks for all keywords.
diff --git a/tests/languages/crystal/number_feature.test b/tests/languages/crystal/number_feature.test
new file mode 100644
index 0000000..cefdc5e
--- /dev/null
+++ b/tests/languages/crystal/number_feature.test
@@ -0,0 +1,23 @@
+1
+1_1
+1_1_1
+0b10_01
+0o1_2_3
+0x123456789abcdef
+012_345.678_9e+10_f64
+
+----------------------------------------------------
+
+[
+	["number", "1"],
+	["number", "1_1"],
+	["number", "1_1_1"],
+	["number", "0b10_01"],
+	["number", "0o1_2_3"],
+	["number", "0x123456789abcdef"],
+	["number", "012_345.678_9e+10_f64"]
+]
+
+----------------------------------------------------
+
+Checks for number literals.