Repository: alecthomas/chroma
Pull request #882 (2023-11-13 15:08:25)

Add Hare lexer
TODO:
- [x] Clean up
- [x] Add test(s)

alecthomas: LGTM!

Patch:
diff --git a/README.md b/README.md
index 25af3e0..6c347a0 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ translators for Pygments lexers and styles.
 |   E    | EBNF, Elixir, Elm, EmacsLisp, Erlang                                                                                                                                                                                                 |
 |   F    | Factor, Fennel, Fish, Forth, Fortran, FortranFixed, FSharp                                                                                                                                                                           |
 |   G    | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, Gherkin, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groff, Groovy                                                                                              |
-|   H    | Handlebars, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HolyC, HTML, HTTP, Hy                                                                                                                                                            |
+|   H    | Handlebars, Hare, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HolyC, HTML, HTTP, Hy                                                                                                                                                      |
 |   I    | Idris, Igor, INI, Io, ISCdhcpd                                                                                                                                                                                                       |
 |   J    | J, Java, JavaScript, JSON, Julia, Jungle                                                                                                                                                                                             |
 |   K    | Kotlin                                                                                                                                                                                                                               |
diff --git a/lexers/embedded/hare.xml b/lexers/embedded/hare.xml
new file mode 100644
index 0000000..ea63642
--- /dev/null
+++ b/lexers/embedded/hare.xml
@@ -0,0 +1,98 @@
+<lexer>
+  <config>
+    <name>Hare</name>
+    <alias>hare</alias>
+    <filename>*.ha</filename>
+    <mime_type>text/x-hare</mime_type>
+  </config>
+  <rules>
+    <state name="string">
+      <rule pattern="&quot;">
+        <token type="LiteralString"/>
+        <pop depth="1"/>
+      </rule>
+      <rule pattern="\\([\\0abfnrtv&quot;']|x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8})">
+        <token type="LiteralStringEscape"/>
+      </rule>
+      <rule pattern="[^\\&quot;\n]+">
+        <token type="LiteralString"/>
+      </rule>
+      <rule pattern="\\">
+        <token type="LiteralString"/>
+      </rule>
+    </state>
+    <state name="root">
+      <rule pattern="[\s\n]+">
+        <token type="TextWhitespace"/>
+      </rule>
+      <rule pattern="@[a-z]+">
+        <token type="NameDecorator"/>
+      </rule>
+      <rule pattern="//.*\n">
+        <token type="CommentSingle"/>
+      </rule>
+      <rule pattern="&quot;">
+        <token type="LiteralString"/>
+        <push state="string"/>
+      </rule>
+      <rule pattern="`[^`]*`">
+        <token type="LiteralString"/>
+      </rule>
+      <rule pattern="'(\\[\\0abfnrtv&quot;']||\\(x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8})|[^\\'])'">
+        <token type="LiteralStringChar"/>
+      </rule>
+      <rule pattern="(0|[1-9]\d*)\.\d+([eE][+-]?\d+)?(f32|f64)?">
+        <token type="LiteralNumberFloat"/>
+      </rule>
+      <rule pattern="(0|[1-9]\d*)([eE][+-]?\d+)?(f32|f64)">
+        <token type="LiteralNumberFloat"/>
+      </rule>
+      <rule pattern="0x[0-9a-fA-F]+\.[0-9a-fA-F]+([pP][+-]?\d+(f32|f64)?)?">
+        <token type="LiteralNumberFloat"/>
+      </rule>
+      <rule pattern="0x[0-9a-fA-F]+[pP][+-]?\d+(f32|f64)">
+        <token type="LiteralNumberFloat"/>
+      </rule>
+      <rule pattern="0x[0-9a-fA-F]+(z|[iu](8|16|32|64)?)?">
+        <token type="LiteralNumberHex"/>
+      </rule>
+      <rule pattern="0o[0-7]+(z|[iu](8|16|32|64)?)?">
+        <token type="LiteralNumberOct"/>
+      </rule>
+      <rule pattern="0b[01]+(z|[iu](8|16|32|64)?)?">
+        <token type="LiteralNumberBin"/>
+      </rule>
+      <rule pattern="(0|[1-9]\d*)([eE][+-]?\d+)?(z|[iu](8|16|32|64)?)?">
+        <token type="LiteralNumberInteger"/>
+      </rule>
+      <rule pattern="[~!%^&amp;*+=|?:&lt;&gt;/-]|[ai]s\b|\.\.\.">
+        <token type="Operator"/>
+      </rule>
+      <rule pattern="[()\[\],.{};]">
+        <token type="Punctuation"/>
+      </rule>
+      <rule pattern="use\b">
+        <token type="KeywordNamespace"/>
+      </rule>
+      <rule pattern="(_|align|break|const|continue|else|enum|export|for|if|return|static|struct|offset|union|fn|free|assert|abort|alloc|let|len|def|type|match|switch|case|append|delete|insert|defer|yield|vastart|vaarg|vaend)\b">
+        <token type="Keyword"/>
+      </rule>
+      <rule pattern="(size)([\s\n]*)(\()">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="TextWhitespace" />
+          <token type="Punctuation" />
+        </bygroups>
+      </rule>
+      <rule pattern="(str|size|rune|bool|int|uint|uintptr|u8|u16|u32|u64|i8|i16|i32|i64|f32|f64|null|void|nullable|valist|opaque|never)\b">
+        <token type="KeywordType"/>
+      </rule>
+      <rule pattern="(true|false)\b">
+        <token type="NameBuiltin"/>
+      </rule>
+      <rule pattern="[a-zA-Z_]\w*">
+        <token type="Name"/>
+      </rule>
+    </state>
+  </rules>
+</lexer>


Test patch:
diff --git a/lexers/testdata/hare.actual b/lexers/testdata/hare.actual
new file mode 100644
index 0000000..775d2f7
--- /dev/null
+++ b/lexers/testdata/hare.actual
@@ -0,0 +1,32 @@
+// test comment
+use bufio::*;
+use fmt, os::exec = exec;
+use time;
+
+type foo = struct {
+	bar: str,
+	baz: time::duration,
+};
+
+export type e = enum u8 {
+	a, b, c
+};
+
+const arr: [_]foo= [
+	foo { bar = "This is a \"string\"!", baz = 25 * time::MINUTE },
+	foo { bar = `This is also a
+string`, baz = 5 * time::SECOND},
+];
+
+let c = 'a'; // char
+let d = 0b1010u16;
+let e: size = 32z;
+let g = 0xffa31u32 + 0o3u32;
+
+export fn main() void = {
+	fmt::println("{}", size(int))!;
+
+	for (let i = 0z; i < 5; i += 1) {
+		fmt::println("{}", i);
+	};
+};
diff --git a/lexers/testdata/hare.expected b/lexers/testdata/hare.expected
new file mode 100644
index 0000000..9c56497
--- /dev/null
+++ b/lexers/testdata/hare.expected
@@ -0,0 +1,259 @@
+[
+  {"type":"CommentSingle","value":"// test comment\n"},
+  {"type":"KeywordNamespace","value":"use"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"bufio"},
+  {"type":"Operator","value":"::*"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"KeywordNamespace","value":"use"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"fmt"},
+  {"type":"Punctuation","value":","},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"os"},
+  {"type":"Operator","value":"::"},
+  {"type":"Name","value":"exec"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"exec"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"KeywordNamespace","value":"use"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"time"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"type"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"foo"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Keyword","value":"struct"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"Name","value":"bar"},
+  {"type":"Operator","value":":"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"KeywordType","value":"str"},
+  {"type":"Punctuation","value":","},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"Name","value":"baz"},
+  {"type":"Operator","value":":"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"time"},
+  {"type":"Operator","value":"::"},
+  {"type":"Name","value":"duration"},
+  {"type":"Punctuation","value":","},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Punctuation","value":"};"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"export"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Keyword","value":"type"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"e"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Keyword","value":"enum"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"KeywordType","value":"u8"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"Name","value":"a"},
+  {"type":"Punctuation","value":","},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"b"},
+  {"type":"Punctuation","value":","},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"c"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Punctuation","value":"};"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"const"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"arr"},
+  {"type":"Operator","value":":"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"["},
+  {"type":"Keyword","value":"_"},
+  {"type":"Punctuation","value":"]"},
+  {"type":"Name","value":"foo"},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"["},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"Name","value":"foo"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"bar"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralString","value":"\"This is a "},
+  {"type":"LiteralStringEscape","value":"\\\""},
+  {"type":"LiteralString","value":"string"},
+  {"type":"LiteralStringEscape","value":"\\\""},
+  {"type":"LiteralString","value":"!\""},
+  {"type":"Punctuation","value":","},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"baz"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralNumberInteger","value":"25"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"*"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"time"},
+  {"type":"Operator","value":"::"},
+  {"type":"Name","value":"MINUTE"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"},"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"Name","value":"foo"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"bar"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralString","value":"`This is also a\nstring`"},
+  {"type":"Punctuation","value":","},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"baz"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralNumberInteger","value":"5"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"*"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"time"},
+  {"type":"Operator","value":"::"},
+  {"type":"Name","value":"SECOND"},
+  {"type":"Punctuation","value":"},"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Punctuation","value":"];"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"let"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"c"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralStringChar","value":"'a'"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"CommentSingle","value":"// char\n"},
+  {"type":"Keyword","value":"let"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"d"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralNumberBin","value":"0b1010u16"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"let"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"e"},
+  {"type":"Operator","value":":"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"KeywordType","value":"size"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralNumberInteger","value":"32z"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"let"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"g"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralNumberHex","value":"0xffa31u32"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"+"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralNumberOct","value":"0o3u32"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"export"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Keyword","value":"fn"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"main"},
+  {"type":"Punctuation","value":"()"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"KeywordType","value":"void"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"Name","value":"fmt"},
+  {"type":"Operator","value":"::"},
+  {"type":"Name","value":"println"},
+  {"type":"Punctuation","value":"("},
+  {"type":"LiteralString","value":"\"{}\""},
+  {"type":"Punctuation","value":","},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Keyword","value":"size"},
+  {"type":"Punctuation","value":"("},
+  {"type":"KeywordType","value":"int"},
+  {"type":"Punctuation","value":"))"},
+  {"type":"Operator","value":"!"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n\n\t"},
+  {"type":"Keyword","value":"for"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"("},
+  {"type":"Keyword","value":"let"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"i"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralNumberInteger","value":"0z"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"i"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"\u003c"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralNumberInteger","value":"5"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"i"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"+="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"LiteralNumberInteger","value":"1"},
+  {"type":"Punctuation","value":")"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"TextWhitespace","value":"\n\t\t"},
+  {"type":"Name","value":"fmt"},
+  {"type":"Operator","value":"::"},
+  {"type":"Name","value":"println"},
+  {"type":"Punctuation","value":"("},
+  {"type":"LiteralString","value":"\"{}\""},
+  {"type":"Punctuation","value":","},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Name","value":"i"},
+  {"type":"Punctuation","value":");"},
+  {"type":"TextWhitespace","value":"\n\t"},
+  {"type":"Punctuation","value":"};"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Punctuation","value":"};"},
+  {"type":"TextWhitespace","value":"\n"}
+]