Commit 08054b153b9d7bb1ac109c6bad56ea2e95ea6e38

Golmote 2015-06-30T21:29:30

Add support for elixir

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
diff --git a/components.js b/components.js
index 919b3c1..71400f0 100644
--- a/components.js
+++ b/components.js
@@ -122,6 +122,10 @@ var components = {
 			"title": "Eiffel",
 			"owner": "Conaclos"
 		},
+		"elixir": {
+			"title": "elixir",
+			"owner": "Golmote"
+		},
 		"erlang": {
 			"title": "Erlang",
 			"owner": "Golmote"
diff --git a/components/prism-elixir.js b/components/prism-elixir.js
new file mode 100644
index 0000000..a8c52b8
--- /dev/null
+++ b/components/prism-elixir.js
@@ -0,0 +1,82 @@
+Prism.languages.elixir = {
+	'heredoc': {
+		pattern: /(?:~[csw])?("""|''')[\s\S]*?\1/i,
+		alias: 'string'
+	},
+	// Negative look-ahead is needed for string interpolation
+	'comment': /#(?!\{).*/,
+	// ~r"""foo""", ~r'''foo''', ~r/foo/, ~r|foo|, ~r"foo", ~r'foo', ~r(foo), ~r[foo], ~r{foo}, ~r<foo>
+	'regex': /~[rR](?:("""|'''|[\/|"'])(?:\\.|(?!\1)[^\\])+\1|\((?:\\\)|[^)])+\)|\[(?:\\\]|[^\]])+\]|\{(?:\\\}|[^}])+\}|<(?:\\>|[^>])+>)[uismxfr]*/,
+	'string': [
+		{
+			// ~s"""foo""", ~s'''foo''', ~s/foo/, ~s|foo|, ~s"foo", ~s'foo', ~s(foo), ~s[foo], ~s{foo}, ~s<foo>
+			pattern: /~[cCsSwW](?:([\/|"'])(?:\\.|(?!\1)[^\\])+\1|\((?:\\\)|[^)])+\)|\[(?:\\\]|[^\]])+\]|\{(?:\\\}|[^}])+\}|<(?:\\>|[^>])+>)[csa]?/,
+			inside: {
+				// See interpolation below
+			}
+		},
+		{
+			// Multi-line strings are allowed
+			pattern: /("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/,
+			inside: {
+				// See interpolation below
+			}
+		}
+	],
+	'atom': {
+		// Look-behind prevents bad highlighting of the :: operator
+		pattern: /(^|[^:]):\w+/,
+		lookbehind: true,
+		alias: 'symbol'
+	},
+	// Look-ahead prevents bad highlighting of the :: operator
+	'attr-name': /\w+:(?!:)/,
+	'capture': {
+		// Look-behind prevents bad highlighting of the && operator
+		pattern: /(^|[^&])&(?:[^&\s\d()][^()]*|(?=\())/,
+		lookbehind: true,
+		alias: 'function'
+	},
+	'argument': {
+		// Look-behind prevents bad highlighting of the && operator
+		pattern: /(^|[^&])&\d+/,
+		lookbehind: true,
+		alias: 'variable'
+	},
+	'attribute': {
+		pattern: /@[\S]+/,
+		alias: 'variable'
+	},
+	'number': /\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,
+	'keyword': /\b(?:after|alias|and|case|catch|cond|def|defcallback|defexception|defimpl|defmodule|defp|defprotocol|defstruct|do|else|end|fn|for|if|import|not|or|require|rescue|try|unless|use|when)\b/,
+	'boolean': /\b(?:true|false|nil)\b/,
+	'operator': [
+		/\bin\b|\|>|\\\\|::|\.\.\.?|\+\+|--|&&|\|\||<>|[!=]==?|[<>]=|->|=>|<-|=~|\B!|[+\-*\/=|^&]/,
+		{
+			// We don't want to match <<
+			pattern: /([^<])<(?!<)/,
+			lookbehind: true
+		},
+		{
+			// We don't want to match >>
+			pattern: /([^>])>(?!>)/,
+			lookbehind: true
+		}
+	],
+	'punctuation': /<<|>>|[.,%\[\]{}()]/
+};
+
+Prism.languages.elixir.string.forEach(function(o) {
+	o.inside = {
+		'interpolation': {
+			pattern: /#\{[^}]+\}/,
+			inside: {
+				'delimiter': {
+					pattern: /^#\{|\}$/,
+					alias: 'punctuation'
+				},
+				rest: Prism.util.clone(Prism.languages.elixir)
+			}
+		}
+	};
+});
\ No newline at end of file
diff --git a/components/prism-elixir.min.js b/components/prism-elixir.min.js
new file mode 100644
index 0000000..d412bab
--- /dev/null
+++ b/components/prism-elixir.min.js
@@ -0,0 +1 @@
+Prism.languages.elixir={heredoc:{pattern:/(?:~[csw])?("""|''')[\s\S]*?\1/i,alias:"string"},comment:/#(?!\{).*/,regex:/~[rR](?:("""|'''|[\/|"'])(?:\\.|(?!\1)[^\\])+\1|\((?:\\\)|[^)])+\)|\[(?:\\\]|[^\]])+\]|\{(?:\\\}|[^}])+\}|<(?:\\>|[^>])+>)[uismxfr]*/,string:[{pattern:/~[cCsSwW](?:([\/|"'])(?:\\.|(?!\1)[^\\])+\1|\((?:\\\)|[^)])+\)|\[(?:\\\]|[^\]])+\]|\{(?:\\\}|[^}])+\}|<(?:\\>|[^>])+>)[csa]?/,inside:{}},{pattern:/("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/,inside:{}}],atom:{pattern:/(^|[^:]):\w+/,lookbehind:!0,alias:"symbol"},"attr-name":/\w+:(?!:)/,capture:{pattern:/(^|[^&])&(?:[^&\s\d()][^()]*|(?=\())/,lookbehind:!0,alias:"function"},argument:{pattern:/(^|[^&])&\d+/,lookbehind:!0,alias:"variable"},attribute:{pattern:/@[\S]+/,alias:"variable"},number:/\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,keyword:/\b(?:after|alias|and|case|catch|cond|def|defcallback|defexception|defimpl|defmodule|defp|defprotocol|defstruct|do|else|end|fn|for|if|import|not|or|require|rescue|try|unless|use|when)\b/,"boolean":/\b(?:true|false|nil)\b/,operator:[/\bin\b|\|>|\\\\|::|\.\.\.?|\+\+|--|&&|\|\||<>|[!=]==?|[<>]=|->|=>|<-|=~|\B!|[+\-*\/=|^&]/,{pattern:/([^<])<(?!<)/,lookbehind:!0},{pattern:/([^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,%\[\]{}()]/},Prism.languages.elixir.string.forEach(function(e){e.inside={interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:Prism.util.clone(Prism.languages.elixir)}}}});
\ No newline at end of file
diff --git a/examples/prism-elixir.html b/examples/prism-elixir.html
new file mode 100644
index 0000000..09b3631
--- /dev/null
+++ b/examples/prism-elixir.html
@@ -0,0 +1,474 @@
+<h1>elixir</h1>
+<p>To use this language, use the class "language-elixir".</p>
+
+<h2>Comments</h2>
+<pre><code># This is a comment</code></pre>
+
+<h2>Atoms</h2>
+<pre><code>:foo
+:bar</code></pre>
+
+<h2>Numbers</h2>
+<pre><code>42
+0b1010
+0o777
+0x1F
+3.14159
+5.2e10
+100_000</code></pre>
+
+<h2>Strings and heredoc</h2>
+<pre><code>'A string with \'quotes\'!'
+"A string with \"quotes\"!"
+"Multi-line
+strings are supported"
+""" "Heredoc" strings are
+also supported. # This is not a comment
+"""</code></pre>
+
+<h2>Sigils</h2>
+<pre><code>~s"""This is a sigil
+using heredoc delimiters"""
+~r/a [reg]exp/
+~r(another|regexp)
+~w[some words]s
+~c&lt;a char list></code></pre>
+
+<h2>Interpolation</h2>
+<pre><code>"This is an #{:atom}"
+~s/#{40+2} is the answer/</code></pre>
+
+<h2>Function capturing</h2>
+<pre><code>fun = &Math.zero?/1
+(&is_function/1).(fun)
+fun = &(&1 + 1)
+fun.(1)
+fun = &List.flatten(&1, &2)
+fun.([1, [[2], 3]], [4, 5])</code></pre>
+
+<h2>Module attributes</h2>
+<pre><code>defmodule MyServer do
+  @vsn 2
+end
+
+defmodule Math do
+  @moduledoc """
+  Provides math-related functions.
+
+  ## Examples
+
+      iex> Math.sum(1, 2)
+      3
+
+  """
+
+  @doc """
+  Calculates the sum of two numbers.
+  """
+  def sum(a, b), do: a + b
+end</code></pre>
+
+<h2>Full example</h2>
+<pre><code># Example from http://learnxinyminutes.com/docs/elixir/
+
+# Single line comments start with a number symbol.
+
+# There's no multi-line comment,
+# but you can stack multiple comments.
+
+# To use the elixir shell use the `iex` command.
+# Compile your modules with the `elixirc` command.
+
+# Both should be in your path if you installed elixir correctly.
+
+## ---------------------------
+## -- Basic types
+## ---------------------------
+
+# There are numbers
+3    # integer
+0x1F # integer
+3.0  # float
+
+# Atoms, that are literals, a constant with name. They start with `:`.
+:hello # atom
+
+# Tuples that are stored contiguously in memory.
+{1,2,3} # tuple
+
+# We can access a tuple element with the `elem` function:
+elem({1, 2, 3}, 0) #=> 1
+
+# Lists that are implemented as linked lists.
+[1,2,3] # list
+
+# We can access the head and tail of a list as follows:
+[head | tail] = [1,2,3]
+head #=> 1
+tail #=> [2,3]
+
+# In elixir, just like in Erlang, the `=` denotes pattern matching and
+# not an assignment.
+#
+# This means that the left-hand side (pattern) is matched against a
+# right-hand side.
+#
+# This is how the above example of accessing the head and tail of a list works.
+
+# A pattern match will error when the sides don't match, in this example
+# the tuples have different sizes.
+# {a, b, c} = {1, 2} #=> ** (MatchError) no match of right hand side value: {1,2}
+
+# There are also binaries
+&lt;&lt;1,2,3>> # binary
+
+# Strings and char lists
+"hello" # string
+'hello' # char list
+
+# Multi-line strings
+"""
+I'm a multi-line
+string.
+"""
+#=> "I'm a multi-line\nstring.\n"
+
+# Strings are all encoded in UTF-8:
+"héllò" #=> "héllò"
+
+# Strings are really just binaries, and char lists are just lists.
+&lt;&lt;?a, ?b, ?c>> #=> "abc"
+[?a, ?b, ?c]   #=> 'abc'
+
+# `?a` in elixir returns the ASCII integer for the letter `a`
+?a #=> 97
+
+# To concatenate lists use `++`, for binaries use `&lt;>`
+[1,2,3] ++ [4,5]     #=> [1,2,3,4,5]
+'hello ' ++ 'world'  #=> 'hello world'
+
+&lt;&lt;1,2,3>> &lt;> &lt;&lt;4,5>> #=> &lt;&lt;1,2,3,4,5>>
+"hello " &lt;> "world"  #=> "hello world"
+
+# Ranges are represented as `start..end` (both inclusive)
+1..10 #=> 1..10
+lower..upper = 1..10 # Can use pattern matching on ranges as well
+[lower, upper] #=> [1, 10]
+
+## ---------------------------
+## -- Operators
+## ---------------------------
+
+# Some math
+1 + 1  #=> 2
+10 - 5 #=> 5
+5 * 2  #=> 10
+10 / 2 #=> 5.0
+
+# In elixir the operator `/` always returns a float.
+
+# To do integer division use `div`
+div(10, 2) #=> 5
+
+# To get the division remainder use `rem`
+rem(10, 3) #=> 1
+
+# There are also boolean operators: `or`, `and` and `not`.
+# These operators expect a boolean as their first argument.
+true and true #=> true
+false or true #=> true
+# 1 and true    #=> ** (ArgumentError) argument error
+
+# Elixir also provides `||`, `&&` and `!` which accept arguments of any type.
+# All values except `false` and `nil` will evaluate to true.
+1 || true  #=> 1
+false && 1 #=> false
+nil && 20  #=> nil
+!true #=> false
+
+# For comparisons we have: `==`, `!=`, `===`, `!==`, `&lt;=`, `>=`, `&lt;` and `>`
+1 == 1 #=> true
+1 != 1 #=> false
+1 &lt; 2  #=> true
+
+# `===` and `!==` are more strict when comparing integers and floats:
+1 == 1.0  #=> true
+1 === 1.0 #=> false
+
+# We can also compare two different data types:
+1 &lt; :hello #=> true
+
+# The overall sorting order is defined below:
+# number &lt; atom &lt; reference &lt; functions &lt; port &lt; pid &lt; tuple &lt; list &lt; bit string
+
+# To quote Joe Armstrong on this: "The actual order is not important,
+# but that a total ordering is well defined is important."
+
+## ---------------------------
+## -- Control Flow
+## ---------------------------
+
+# `if` expression
+if false do
+  "This will never be seen"
+else
+  "This will"
+end
+
+# There's also `unless`
+unless true do
+  "This will never be seen"
+else
+  "This will"
+end
+
+# Remember pattern matching? Many control-flow structures in elixir rely on it.
+
+# `case` allows us to compare a value against many patterns:
+case {:one, :two} do
+  {:four, :five} ->
+    "This won't match"
+  {:one, x} ->
+    "This will match and bind `x` to `:two`"
+  _ ->
+    "This will match any value"
+end
+
+# It's common to bind the value to `_` if we don't need it.
+# For example, if only the head of a list matters to us:
+[head | _] = [1,2,3]
+head #=> 1
+
+# For better readability we can do the following:
+[head | _tail] = [:a, :b, :c]
+head #=> :a
+
+# `cond` lets us check for many conditions at the same time.
+# Use `cond` instead of nesting many `if` expressions.
+cond do
+  1 + 1 == 3 ->
+    "I will never be seen"
+  2 * 5 == 12 ->
+    "Me neither"
+  1 + 2 == 3 ->
+    "But I will"
+end
+
+# It is common to set the last condition equal to `true`, which will always match.
+cond do
+  1 + 1 == 3 ->
+    "I will never be seen"
+  2 * 5 == 12 ->
+    "Me neither"
+  true ->
+    "But I will (this is essentially an else)"
+end
+
+# `try/catch` is used to catch values that are thrown, it also supports an
+# `after` clause that is invoked whether or not a value is caught.
+try do
+  throw(:hello)
+catch
+  message -> "Got #{message}."
+after
+  IO.puts("I'm the after clause.")
+end
+#=> I'm the after clause
+# "Got :hello"
+
+## ---------------------------
+## -- Modules and Functions
+## ---------------------------
+
+# Anonymous functions (notice the dot)
+square = fn(x) -> x * x end
+square.(5) #=> 25
+
+# They also accept many clauses and guards.
+# Guards let you fine tune pattern matching,
+# they are indicated by the `when` keyword:
+f = fn
+  x, y when x > 0 -> x + y
+  x, y -> x * y
+end
+
+f.(1, 3)  #=> 4
+f.(-1, 3) #=> -3
+
+# Elixir also provides many built-in functions.
+# These are available in the current scope.
+is_number(10)    #=> true
+is_list("hello") #=> false
+elem({1,2,3}, 0) #=> 1
+
+# You can group several functions into a module. Inside a module use `def`
+# to define your functions.
+defmodule Math do
+  def sum(a, b) do
+    a + b
+  end
+
+  def square(x) do
+    x * x
+  end
+end
+
+Math.sum(1, 2)  #=> 3
+Math.square(3) #=> 9
+
+# To compile our simple Math module save it as `math.ex` and use `elixirc`
+# in your terminal: elixirc math.ex
+
+# Inside a module we can define functions with `def` and private functions with `defp`.
+# A function defined with `def` is available to be invoked from other modules,
+# a private function can only be invoked locally.
+defmodule PrivateMath do
+  def sum(a, b) do
+    do_sum(a, b)
+  end
+
+  defp do_sum(a, b) do
+    a + b
+  end
+end
+
+PrivateMath.sum(1, 2)    #=> 3
+# PrivateMath.do_sum(1, 2) #=> ** (UndefinedFunctionError)
+
+# Function declarations also support guards and multiple clauses:
+defmodule Geometry do
+  def area({:rectangle, w, h}) do
+    w * h
+  end
+
+  def area({:circle, r}) when is_number(r) do
+    3.14 * r * r
+  end
+end
+
+Geometry.area({:rectangle, 2, 3}) #=> 6
+Geometry.area({:circle, 3})       #=> 28.25999999999999801048
+# Geometry.area({:circle, "not_a_number"})
+#=> ** (FunctionClauseError) no function clause matching in Geometry.area/1
+
+# Due to immutability, recursion is a big part of elixir
+defmodule Recursion do
+  def sum_list([head | tail], acc) do
+    sum_list(tail, acc + head)
+  end
+
+  def sum_list([], acc) do
+    acc
+  end
+end
+
+Recursion.sum_list([1,2,3], 0) #=> 6
+
+# Elixir modules support attributes, there are built-in attributes and you
+# may also add custom ones.
+defmodule MyMod do
+  @moduledoc """
+  This is a built-in attribute on a example module.
+  """
+
+  @my_data 100 # This is a custom attribute.
+  IO.inspect(@my_data) #=> 100
+end
+
+## ---------------------------
+## -- Structs and Exceptions
+## ---------------------------
+
+# Structs are extensions on top of maps that bring default values,
+# compile-time guarantees and polymorphism into Elixir.
+defmodule Person do
+  defstruct name: nil, age: 0, height: 0
+end
+
+joe_info = %Person{ name: "Joe", age: 30, height: 180 }
+#=> %Person{age: 30, height: 180, name: "Joe"}
+
+# Access the value of name
+joe_info.name #=> "Joe"
+
+# Update the value of age
+older_joe_info = %{ joe_info | age: 31 }
+#=> %Person{age: 31, height: 180, name: "Joe"}
+
+# The `try` block with the `rescue` keyword is used to handle exceptions
+try do
+  raise "some error"
+rescue
+  RuntimeError -> "rescued a runtime error"
+  _error -> "this will rescue any error"
+end
+
+# All exceptions have a message
+try do
+  raise "some error"
+rescue
+  x in [RuntimeError] ->
+    x.message
+end
+
+## ---------------------------
+## -- Concurrency
+## ---------------------------
+
+# Elixir relies on the actor model for concurrency. All we need to write
+# concurrent programs in elixir are three primitives: spawning processes,
+# sending messages and receiving messages.
+
+# To start a new process we use the `spawn` function, which takes a function
+# as argument.
+f = fn -> 2 * 2 end #=> #Function&lt;erl_eval.20.80484245>
+spawn(f) #=> #PID&lt;0.40.0>
+
+# `spawn` returns a pid (process identifier), you can use this pid to send
+# messages to the process. To do message passing we use the `send` operator.
+# For all of this to be useful we need to be able to receive messages. This is
+# achieved with the `receive` mechanism:
+defmodule Geometry do
+  def area_loop do
+    receive do
+      {:rectangle, w, h} ->
+        IO.puts("Area = #{w * h}")
+        area_loop()
+      {:circle, r} ->
+        IO.puts("Area = #{3.14 * r * r}")
+        area_loop()
+    end
+  end
+end
+
+# Compile the module and create a process that evaluates `area_loop` in the shell
+pid = spawn(fn -> Geometry.area_loop() end) #=> #PID&lt;0.40.0>
+
+# Send a message to `pid` that will match a pattern in the receive statement
+send pid, {:rectangle, 2, 3}
+#=> Area = 6
+#   {:rectangle,2,3}
+
+send pid, {:circle, 2}
+#=> Area = 12.56000000000000049738
+#   {:circle,2}
+
+# The shell is also a process, you can use `self` to get the current pid
+self() #=> #PID&lt;0.27.0></code></pre>
+
+<h2>Known failures</h2>
+<p>There are certain edge cases where Prism will fail.
+	There are always such cases in every regex-based syntax highlighter.
+	However, Prism dares to be open and honest about them.
+	If a failure is listed here, it doesn’t mean it will never be fixed. This is more of a “known bugs” list, just with a certain type of bug.
+</p>
+
+<h3>Comments starting with an opening curly brace</h3>
+<pre><code>#{ This is not highlighted properly.</code></pre>
+
+<h3>Comment-like substrings</h3>
+<pre><code>"foo # var"</code></pre>
+
+<h3>String interpolation in single-quoted strings</h3>
+<pre><code>'#{:atom} &lt;- this should not be highligted'</code></pre>
+