diff --git a/components.js b/components.js index 01cba3a46e..774bf2a88b 100644 --- a/components.js +++ b/components.js @@ -266,6 +266,10 @@ var components = { "title": "NASM", "owner": "rbmj" }, + "nim": { + "title": "Nim", + "owner": "Golmote" + }, "nsis": { "title": "NSIS", "owner": "idleberg" diff --git a/components/prism-nim.js b/components/prism-nim.js new file mode 100644 index 0000000000..8592bc3a4b --- /dev/null +++ b/components/prism-nim.js @@ -0,0 +1,30 @@ +Prism.languages.nim = { + 'comment': /#.*/, + // Double-quoted strings can be prefixed by an identifier (Generalized raw string literals) + // Character literals are handled specifically to prevent issues with numeric type suffixes + 'string': /(?:(?:\b(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+)?(?:"""[\s\S]*?"""(?!")|"(?:\\[\s\S]|""|[^"\\])*")|'(?:\\(?:\d+|x[\da-fA-F]{2}|.)|[^'])')/, + // The negative look ahead prevents wrong highlighting of the .. operator + 'number': /\b(?:0[xXoObB][\da-fA-F_]+|\d[\d_]*(?:(?!\.\.)\.[\d_]*)?(?:[eE][+-]?\d[\d_]*)?)(?:'?[iuf]\d*)?/, + 'keyword': /\b(?:addr|as|asm|atomic|bind|block|break|case|cast|concept|const|continue|converter|defer|discard|distinct|do|elif|else|end|enum|except|export|finally|for|from|func|generic|if|import|include|interface|iterator|let|macro|method|mixin|nil|object|out|proc|ptr|raise|ref|return|static|template|try|tuple|type|using|var|when|while|with|without|yield)\b/, + 'function': { + pattern: /(?:(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+|`[^`\r\n]+`)\*?(?:\[[^\]]+\])?(?=\s*\()/, + inside: { + 'operator': /\*$/ + } + }, + // We don't want to highlight operators inside backticks + 'ignore': { + pattern: /`[^`\r\n]+`/, + inside: { + 'punctuation': /`/ + } + }, + 'operator': { + // Look behind and look ahead prevent wrong highlighting of punctuations [. .] {. .} (. .) + // but allow the slice operator .. to take precedence over them + // One can define his own operators in Nim so all combination of operators might be an operator. + pattern: /(^|[({\[](?=\.\.)|(?![({\[]\.).)(?:(?:[=+\-*\/<>@$~&%|!?^:\\]|\.\.|\.(?![)}\]]))+|\b(?:and|div|of|or|in|is|isnot|mod|not|notin|shl|shr|xor)\b)/m, + lookbehind: true + }, + 'punctuation': /[({\[]\.|\.[)}\]]|[`(){}\[\],:]/ +}; \ No newline at end of file diff --git a/components/prism-nim.min.js b/components/prism-nim.min.js new file mode 100644 index 0000000000..e45372ed29 --- /dev/null +++ b/components/prism-nim.min.js @@ -0,0 +1 @@ +Prism.languages.nim={comment:/#.*/,string:/(?:(?:\b(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+)?(?:"""[\s\S]*?"""(?!")|"(?:\\[\s\S]|""|[^"\\])*")|'(?:\\(?:\d+|x[\da-fA-F]{2}|.)|[^'])')/,number:/\b(?:0[xXoObB][\da-fA-F_]+|\d[\d_]*(?:(?!\.\.)\.[\d_]*)?(?:[eE][+-]?\d[\d_]*)?)(?:'?[iuf]\d*)?/,keyword:/\b(?:addr|as|asm|atomic|bind|block|break|case|cast|concept|const|continue|converter|defer|discard|distinct|do|elif|else|end|enum|except|export|finally|for|from|func|generic|if|import|include|interface|iterator|let|macro|method|mixin|nil|object|out|proc|ptr|raise|ref|return|static|template|try|tuple|type|using|var|when|while|with|without|yield)\b/,"function":{pattern:/(?:(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+|`[^`\r\n]+`)\*?(?:\[[^\]]+\])?(?=\s*\()/,inside:{operator:/\*$/}},ignore:{pattern:/`[^`\r\n]+`/,inside:{punctuation:/`/}},operator:{pattern:/(^|[({\[](?=\.\.)|(?![({\[]\.).)(?:(?:[=+\-*\/<>@$~&%|!?^:\\]|\.\.|\.(?![)}\]]))+|\b(?:and|div|of|or|in|is|isnot|mod|not|notin|shl|shr|xor)\b)/m,lookbehind:!0},punctuation:/[({\[]\.|\.[)}\]]|[`(){}\[\],:]/}; \ No newline at end of file diff --git a/examples/prism-nim.html b/examples/prism-nim.html new file mode 100644 index 0000000000..5575374f03 --- /dev/null +++ b/examples/prism-nim.html @@ -0,0 +1,235 @@ +
To use this language, use the class "language-nim".
+ +# This is a comment
+
+"This is a string."
+"This is a string with \"quotes\" in it."
+"""This is
+a "multi-line"
+string."""
+""""A long string within quotes.""""
+R"This is a raw string."
+r"Some ""quotes"" inside a raw string."
+r"""Raw strings
+can also be multi-line."""
+foo"This is a generalized raw string literal."
+bar"""This is also
+a generalized raw string literal."""
+
+'a'
+'\''
+'\t'
+'\15'
+'\xFC'
+
+42
+0xaf
+0xf_2_c
+0o07
+0b1111_0000
+0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64
+9_000'u
+32.
+32.1f32
+32.e-5
+32.2e+2
+2'i16
+2i16
+0xfe'f32
+
+# Example from http://nim-by-example.github.io/oop_macro/
+import macros
+
+macro class*(head: expr, body: stmt): stmt {.immediate.} =
+ # The macro is immediate so that it doesn't
+ # resolve identifiers passed to it
+
+ var typeName, baseName: NimNode
+
+ if head.kind == nnkIdent:
+ # `head` is expression `typeName`
+ # echo head.treeRepr
+ # --------------------
+ # Ident !"Animal"
+ typeName = head
+
+ elif head.kind == nnkInfix and $head[0] == "of":
+ # `head` is expression `typeName of baseClass`
+ # echo head.treeRepr
+ # --------------------
+ # Infix
+ # Ident !"of"
+ # Ident !"Animal"
+ # Ident !"RootObj"
+ typeName = head[1]
+ baseName = head[2]
+
+ else:
+ quit "Invalid node: " & head.lispRepr
+
+ # echo treeRepr(body)
+ # --------------------
+ # StmtList
+ # VarSection
+ # IdentDefs
+ # Ident !"name"
+ # Ident !"string"
+ # Empty
+ # IdentDefs
+ # Ident !"age"
+ # Ident !"int"
+ # Empty
+ # MethodDef
+ # Ident !"vocalize"
+ # Empty
+ # Empty
+ # FormalParams
+ # Ident !"string"
+ # Empty
+ # Empty
+ # StmtList
+ # StrLit ...
+ # MethodDef
+ # Ident !"age_human_yrs"
+ # Empty
+ # Empty
+ # FormalParams
+ # Ident !"int"
+ # Empty
+ # Empty
+ # StmtList
+ # DotExpr
+ # Ident !"this"
+ # Ident !"age"
+
+ # create a new stmtList for the result
+ result = newStmtList()
+
+ # var declarations will be turned into object fields
+ var recList = newNimNode(nnkRecList)
+
+ # Iterate over the statements, adding `this: T`
+ # to the parameters of functions
+ for node in body.children:
+ case node.kind:
+
+ of nnkMethodDef, nnkProcDef:
+ # inject `this: T` into the arguments
+ let p = copyNimTree(node.params)
+ p.insert(1, newIdentDefs(ident"this", typeName))
+ node.params = p
+ result.add(node)
+
+ of nnkVarSection:
+ # variables get turned into fields of the type.
+ for n in node.children:
+ recList.add(n)
+
+ else:
+ result.add(node)
+
+ # The following prints out the AST structure:
+ #
+ # import macros
+ # dumptree:
+ # type X = ref object of Y
+ # z: int
+ # --------------------
+ # TypeSection
+ # TypeDef
+ # Ident !"X"
+ # Empty
+ # RefTy
+ # ObjectTy
+ # Empty
+ # OfInherit
+ # Ident !"Y"
+ # RecList
+ # IdentDefs
+ # Ident !"z"
+ # Ident !"int"
+ # Empty
+
+ result.insert(0,
+ if baseName == nil:
+ quote do:
+ type `typeName` = ref object of RootObj
+ else:
+ quote do:
+ type `typeName` = ref object of `baseName`
+ )
+ # Inspect the tree structure:
+ #
+ # echo result.treeRepr
+ # --------------------
+ # StmtList
+ # StmtList
+ # TypeSection
+ # TypeDef
+ # Ident !"Animal"
+ # Empty
+ # RefTy
+ # ObjectTy
+ # Empty
+ # OfInherit
+ # Ident !"RootObj"
+ # Empty <= We want to replace this
+ # MethodDef
+ # ...
+
+ result[0][0][0][2][0][2] = recList
+
+ # Lets inspect the human-readable version of the output
+ # echo repr(result)
+ # Output:
+ # type
+ # Animal = ref object of RootObj
+ # name: string
+ # age: int
+ #
+ # method vocalize(this: Animal): string =
+ # "..."
+ #
+ # method age_human_yrs(this: Animal): int =
+ # this.age
+
+# ---
+
+class Animal of RootObj:
+ var name: string
+ var age: int
+ method vocalize: string = "..."
+ method age_human_yrs: int = this.age # `this` is injected
+
+class Dog of Animal:
+ method vocalize: string = "woof"
+ method age_human_yrs: int = this.age * 7
+
+class Cat of Animal:
+ method vocalize: string = "meow"
+
+# ---
+
+var animals: seq[Animal] = @[]
+animals.add(Dog(name: "Sparky", age: 10))
+animals.add(Cat(name: "Mitten", age: 10))
+
+for a in animals:
+ echo a.vocalize()
+ echo a.age_human_yrs()
+
+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. +
+ +"This # is a broken string"
\ No newline at end of file
diff --git a/tests/languages/nim/comment_feature.test b/tests/languages/nim/comment_feature.test
new file mode 100644
index 0000000000..09493ab581
--- /dev/null
+++ b/tests/languages/nim/comment_feature.test
@@ -0,0 +1,13 @@
+#
+# Foobar
+
+----------------------------------------------------
+
+[
+ ["comment", "#"],
+ ["comment", "# Foobar"]
+]
+
+----------------------------------------------------
+
+Checks for comments.
\ No newline at end of file
diff --git a/tests/languages/nim/function_feature.test b/tests/languages/nim/function_feature.test
new file mode 100644
index 0000000000..899ad50f30
--- /dev/null
+++ b/tests/languages/nim/function_feature.test
@@ -0,0 +1,17 @@
+fo\x9ao(
+class*(
+takeV[T](
+`$`(
+
+----------------------------------------------------
+
+[
+ ["function", ["fo\\x9ao"]], ["punctuation", "("],
+ ["function", ["class", ["operator", "*"]]], ["punctuation", "("],
+ ["function", ["takeV[T]"]], ["punctuation", "("],
+ ["function", ["`$`"]], ["punctuation", "("]
+]
+
+----------------------------------------------------
+
+Checks for functions.
\ No newline at end of file
diff --git a/tests/languages/nim/keyword_feature.test b/tests/languages/nim/keyword_feature.test
new file mode 100644
index 0000000000..02d92d6779
--- /dev/null
+++ b/tests/languages/nim/keyword_feature.test
@@ -0,0 +1,123 @@
+addr
+as
+asm
+atomic
+bind
+block
+break
+case
+cast
+concept
+const
+continue
+converter
+defer
+discard
+distinct
+do
+elif
+else
+end
+enum
+except
+export
+finally
+for
+from
+func
+generic
+if
+import
+include
+interface
+iterator
+let
+macro
+method
+mixin
+nil
+object
+out
+proc
+ptr
+raise
+ref
+return
+static
+template
+try
+tuple
+type
+using
+var
+when
+while
+with
+without
+yield
+
+----------------------------------------------------
+
+[
+ ["keyword", "addr"],
+ ["keyword", "as"],
+ ["keyword", "asm"],
+ ["keyword", "atomic"],
+ ["keyword", "bind"],
+ ["keyword", "block"],
+ ["keyword", "break"],
+ ["keyword", "case"],
+ ["keyword", "cast"],
+ ["keyword", "concept"],
+ ["keyword", "const"],
+ ["keyword", "continue"],
+ ["keyword", "converter"],
+ ["keyword", "defer"],
+ ["keyword", "discard"],
+ ["keyword", "distinct"],
+ ["keyword", "do"],
+ ["keyword", "elif"],
+ ["keyword", "else"],
+ ["keyword", "end"],
+ ["keyword", "enum"],
+ ["keyword", "except"],
+ ["keyword", "export"],
+ ["keyword", "finally"],
+ ["keyword", "for"],
+ ["keyword", "from"],
+ ["keyword", "func"],
+ ["keyword", "generic"],
+ ["keyword", "if"],
+ ["keyword", "import"],
+ ["keyword", "include"],
+ ["keyword", "interface"],
+ ["keyword", "iterator"],
+ ["keyword", "let"],
+ ["keyword", "macro"],
+ ["keyword", "method"],
+ ["keyword", "mixin"],
+ ["keyword", "nil"],
+ ["keyword", "object"],
+ ["keyword", "out"],
+ ["keyword", "proc"],
+ ["keyword", "ptr"],
+ ["keyword", "raise"],
+ ["keyword", "ref"],
+ ["keyword", "return"],
+ ["keyword", "static"],
+ ["keyword", "template"],
+ ["keyword", "try"],
+ ["keyword", "tuple"],
+ ["keyword", "type"],
+ ["keyword", "using"],
+ ["keyword", "var"],
+ ["keyword", "when"],
+ ["keyword", "while"],
+ ["keyword", "with"],
+ ["keyword", "without"],
+ ["keyword", "yield"]
+]
+
+----------------------------------------------------
+
+Checks for keywords.
\ No newline at end of file
diff --git a/tests/languages/nim/number_feature.test b/tests/languages/nim/number_feature.test
new file mode 100644
index 0000000000..0587edb094
--- /dev/null
+++ b/tests/languages/nim/number_feature.test
@@ -0,0 +1,31 @@
+0xBad_Face
+0o754_173
+0B1111_0000
+42_000
+3.14_15_9
+3E7
+0.5e-84_741
+9.8e+54
+9000'u
+2'i16
+2i16
+0xfe'f32
+
+----------------------------------------------------
+
+[
+ ["number", "0xBad_Face"],
+ ["number", "0o754_173"],
+ ["number", "0B1111_0000"],
+ ["number", "42_000"],
+ ["number", "3.14_15_9"],
+ ["number", "3E7"],
+ ["number", "0.5e-84_741"],
+ ["number", "9.8e+54"],
+ ["number", "9000'u"],
+ ["number", "2'i16"],
+ ["number", "2i16"],
+ ["number", "0xfe'f32"]
+]
+
+----------------------------------------------------
diff --git a/tests/languages/nim/operator_feature.test b/tests/languages/nim/operator_feature.test
new file mode 100644
index 0000000000..9981f4a402
--- /dev/null
+++ b/tests/languages/nim/operator_feature.test
@@ -0,0 +1,39 @@
+= + -
+* /
+< >
+@ $ ~
+& % |
+! ? ^
+: \
+.. .
+
++*<>
+
+and div of or
+in is isnot mod
+not notin
+shl shr xor
+
+----------------------------------------------------
+
+[
+ ["operator", "="], ["operator", "+"], ["operator", "-"],
+ ["operator", "*"], ["operator", "/"],
+ ["operator", "<"], ["operator", ">"],
+ ["operator", "@"], ["operator", "$"], ["operator", "~"],
+ ["operator", "&"], ["operator", "%"], ["operator", "|"],
+ ["operator", "!"], ["operator", "?"], ["operator", "^"],
+ ["operator", ":"], ["operator", "\\"],
+ ["operator", ".."], ["operator", "."],
+
+ ["operator", "+*<>"],
+
+ ["operator", "and"], ["operator", "div"], ["operator", "of"], ["operator", "or"],
+ ["operator", "in"], ["operator", "is"], ["operator", "isnot"], ["operator", "mod"],
+ ["operator", "not"], ["operator", "notin"],
+ ["operator", "shl"], ["operator", "shr"], ["operator", "xor"]
+]
+
+----------------------------------------------------
+
+Checks for operators.
\ No newline at end of file
diff --git a/tests/languages/nim/string_feature.test b/tests/languages/nim/string_feature.test
new file mode 100644
index 0000000000..cd41819d1d
--- /dev/null
+++ b/tests/languages/nim/string_feature.test
@@ -0,0 +1,38 @@
+""
+"Fo\"obar"
+
+""""""
+"""Fo"o
+bar"""
+
+R"Raw ""string"
+
+r"Raw
+""string"
+
+fo\x8Fo"Foobar"
+
+bar"""Foo
+bar"""
+
+'\''
+'\xFC'
+
+----------------------------------------------------
+
+[
+ ["string", "\"\""],
+ ["string", "\"Fo\\\"obar\""],
+ ["string", "\"\"\"\"\"\""],
+ ["string", "\"\"\"Fo\"o\r\nbar\"\"\""],
+ ["string", "R\"Raw \"\"string\""],
+ ["string", "r\"Raw\r\n\"\"string\""],
+ ["string", "fo\\x8Fo\"Foobar\""],
+ ["string", "bar\"\"\"Foo\r\nbar\"\"\""],
+ ["string", "'\\''"],
+ ["string", "'\\xFC'"]
+]
+
+----------------------------------------------------
+
+Checks for strings and character literals.
\ No newline at end of file