Skip to content

Commit b0358e8

Browse files
authored
Add lexer for microcad (#1171)
µcad is a domain specific language for CAD and is hosted on https://codeberg.org/microcad/microcad/ , which uses forgejo, which uses chroma. We want to have syntax highlighting support for µcad on Codeberg, hence the PR.
1 parent 0fe6941 commit b0358e8

3 files changed

Lines changed: 276 additions & 0 deletions

File tree

lexers/embedded/microcad.xml

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<lexer>
2+
<config>
3+
<name>microcad</name>
4+
<alias>µcad</alias>
5+
<filename>*.µcad</filename>
6+
<filename>*.ucad</filename>
7+
<filename>*.mcad</filename>
8+
<mime_type>text/microcad</mime_type>
9+
<ensure_nl>true</ensure_nl>
10+
</config>
11+
12+
<rules>
13+
<!-- Root state -->
14+
<state name="root">
15+
<rule pattern="\n">
16+
<token type="TextWhitespace"/>
17+
</rule>
18+
<rule pattern="\s+">
19+
<token type="TextWhitespace"/>
20+
</rule>
21+
22+
<!-- COMMENTS -->
23+
<rule pattern="///.*">
24+
<token type="LiteralStringDoc"/>
25+
</rule>
26+
27+
<rule pattern="//.*">
28+
<token type="CommentSingle"/>
29+
</rule>
30+
31+
<rule pattern="/\*.*">
32+
<token type="CommentMultiline"/>
33+
<push state="comment"/>
34+
</rule>
35+
36+
<!-- KEYWORDS -->
37+
<rule pattern="\b(pub|sketch|part|op|mod|use|fn|const|prop|init|return|if|else|mat|__builtin|or|and|not|as)\b">
38+
<token type="Keyword"/>
39+
</rule>
40+
41+
<!-- TYPES -->
42+
<rule pattern="\b(Integer|Scalar|String|Color|Length|Area|Volume|Angle|Weight|Density|Bool|Matrix[0-9])\b">
43+
<token type="KeywordType"/>
44+
</rule>
45+
46+
<!-- FUNCTION NAMES -->
47+
<rule pattern="\b([a-z_][a-zA-Z0-9_]*)\s*(?=\()">
48+
<token type="NameFunction"/>
49+
</rule>
50+
51+
<!-- OPERATORS -->
52+
<rule pattern="[+\-*/%&amp;|&lt;&gt;^!@=]">
53+
<token type="Operator"/>
54+
</rule>
55+
56+
<!-- NUMBERS + UNITS -->
57+
<rule pattern="\b(([0-9]+(\.[0-9]+)?)(%|m²|cm²|mm²|µm²|in²|ft²|yd²|m³|cm³|mm³|µm³|in³|ft³|yd³|ml|cl|l|µl|cm|mm|m|µm|in|ft|yd|deg|°|grad|turn|rad|g|kg|lb|oz)?)|true|false\b">
58+
<token type="LiteralNumber"/>
59+
</rule>
60+
61+
<!-- NAMESPACES -->
62+
<rule pattern="([a-z_][a-z0-9_]*)(::)">
63+
<bygroups>
64+
<token type="NameNamespace"/>
65+
<token type="Operator"/>
66+
</bygroups>
67+
</rule>
68+
69+
<!-- CONSTANTS (ALL CAPS) -->
70+
<rule pattern="\b([A-Z_][A-Z0-9_]*)\b">
71+
<token type="NameConstant"/>
72+
</rule>
73+
74+
<!-- CUSTOM TYPES (Capitalized identifiers) -->
75+
<rule pattern="\b([A-Z_][a-zA-Z0-9_]*)\b">
76+
<token type="NameClass"/>
77+
</rule>
78+
79+
<!-- VARIABLES -->
80+
<rule pattern="\b([a-z_][a-zA-Z0-9_]*)\b">
81+
<token type="Name"/>
82+
</rule>
83+
84+
<!-- STRINGS -->
85+
<rule pattern="&quot;[^&quot;]*&quot;">
86+
<token type="LiteralString"/>
87+
</rule>
88+
89+
<!-- PAREN GROUP -->
90+
<rule pattern="[{}()\[\],.;:]">
91+
<token type="Punctuation"/>
92+
</rule>
93+
94+
95+
<!-- BRACE GROUP -->
96+
<rule pattern="\{">
97+
<token type="Punctuation"/>
98+
<push state="brace"/>
99+
</rule>
100+
101+
<rule pattern="[\}\)]">
102+
<token type="Punctuation"/>
103+
</rule>
104+
105+
</state>
106+
107+
<!-- Parenthesized expression -->
108+
<state name="paren">
109+
<rule pattern="\)">
110+
<token type="Punctuation"/>
111+
<pop/>
112+
</rule>
113+
<rule>
114+
<include state="root"/>
115+
</rule>
116+
</state>
117+
118+
<!-- Braced expression -->
119+
<state name="brace">
120+
<rule pattern="\}">
121+
<token type="Punctuation"/>
122+
<pop/>
123+
</rule>
124+
<rule>
125+
<include state="root"/>
126+
</rule>
127+
</state>
128+
129+
<state name="comment">
130+
<!-- End of comment -->
131+
<rule pattern="\*/">
132+
<token type="CommentMultiline"/>
133+
<pop/>
134+
</rule>
135+
</state>
136+
137+
138+
</rules>
139+
</lexer>

lexers/testdata/microcad.actual

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright © 2025 The µcad authors <info@ucad.xyz>
2+
// SPDX-License-Identifier: AGPL-3.0-or-later
3+
4+
use std::math::*;
5+
use std::ops::*;
6+
use std::geo3d::*;
7+
8+
/// A CSG cube.
9+
part CsgCube(size: Length) {
10+
s = size / sqrt(2.1); /* Inline comment */
11+
body = Sphere(r = s) & Cube(size);
12+
holes = Cylinder(h = size, d = s).orient([X,Y,Z]);
13+
body - holes;
14+
}
15+
16+
CsgCube(50mm);

lexers/testdata/microcad.expected

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
[
2+
{"type":"CommentSingle","value":"// Copyright © 2025 The µcad authors \u003cinfo@ucad.xyz\u003e"},
3+
{"type":"TextWhitespace","value":"\n"},
4+
{"type":"CommentSingle","value":"// SPDX-License-Identifier: AGPL-3.0-or-later"},
5+
{"type":"TextWhitespace","value":"\n\n"},
6+
{"type":"Keyword","value":"use"},
7+
{"type":"TextWhitespace","value":" "},
8+
{"type":"NameNamespace","value":"std"},
9+
{"type":"Operator","value":"::"},
10+
{"type":"NameNamespace","value":"math"},
11+
{"type":"Operator","value":"::*"},
12+
{"type":"Punctuation","value":";"},
13+
{"type":"TextWhitespace","value":"\n"},
14+
{"type":"Keyword","value":"use"},
15+
{"type":"TextWhitespace","value":" "},
16+
{"type":"NameNamespace","value":"std"},
17+
{"type":"Operator","value":"::"},
18+
{"type":"NameNamespace","value":"ops"},
19+
{"type":"Operator","value":"::*"},
20+
{"type":"Punctuation","value":";"},
21+
{"type":"TextWhitespace","value":"\n"},
22+
{"type":"Keyword","value":"use"},
23+
{"type":"TextWhitespace","value":" "},
24+
{"type":"NameNamespace","value":"std"},
25+
{"type":"Operator","value":"::"},
26+
{"type":"NameNamespace","value":"geo3d"},
27+
{"type":"Operator","value":"::*"},
28+
{"type":"Punctuation","value":";"},
29+
{"type":"TextWhitespace","value":"\n\n"},
30+
{"type":"LiteralStringDoc","value":"/// A CSG cube."},
31+
{"type":"TextWhitespace","value":"\n"},
32+
{"type":"Keyword","value":"part"},
33+
{"type":"TextWhitespace","value":" "},
34+
{"type":"NameClass","value":"CsgCube"},
35+
{"type":"Punctuation","value":"("},
36+
{"type":"Name","value":"size"},
37+
{"type":"Punctuation","value":":"},
38+
{"type":"TextWhitespace","value":" "},
39+
{"type":"KeywordType","value":"Length"},
40+
{"type":"Punctuation","value":")"},
41+
{"type":"TextWhitespace","value":" "},
42+
{"type":"Punctuation","value":"{"},
43+
{"type":"TextWhitespace","value":"\n "},
44+
{"type":"Name","value":"s"},
45+
{"type":"TextWhitespace","value":" "},
46+
{"type":"Operator","value":"="},
47+
{"type":"TextWhitespace","value":" "},
48+
{"type":"Name","value":"size"},
49+
{"type":"TextWhitespace","value":" "},
50+
{"type":"Operator","value":"/"},
51+
{"type":"TextWhitespace","value":" "},
52+
{"type":"NameFunction","value":"sqrt"},
53+
{"type":"Punctuation","value":"("},
54+
{"type":"LiteralNumber","value":"2.1"},
55+
{"type":"Punctuation","value":");"},
56+
{"type":"TextWhitespace","value":" "},
57+
{"type":"CommentMultiline","value":"/* Inline comment */"},
58+
{"type":"TextWhitespace","value":"\n "},
59+
{"type":"Name","value":"body"},
60+
{"type":"TextWhitespace","value":" "},
61+
{"type":"Operator","value":"="},
62+
{"type":"TextWhitespace","value":" "},
63+
{"type":"NameClass","value":"Sphere"},
64+
{"type":"Punctuation","value":"("},
65+
{"type":"Name","value":"r"},
66+
{"type":"TextWhitespace","value":" "},
67+
{"type":"Operator","value":"="},
68+
{"type":"TextWhitespace","value":" "},
69+
{"type":"Name","value":"s"},
70+
{"type":"Punctuation","value":")"},
71+
{"type":"TextWhitespace","value":" "},
72+
{"type":"Operator","value":"\u0026"},
73+
{"type":"TextWhitespace","value":" "},
74+
{"type":"NameClass","value":"Cube"},
75+
{"type":"Punctuation","value":"("},
76+
{"type":"Name","value":"size"},
77+
{"type":"Punctuation","value":");"},
78+
{"type":"TextWhitespace","value":"\n "},
79+
{"type":"Name","value":"holes"},
80+
{"type":"TextWhitespace","value":" "},
81+
{"type":"Operator","value":"="},
82+
{"type":"TextWhitespace","value":" "},
83+
{"type":"NameClass","value":"Cylinder"},
84+
{"type":"Punctuation","value":"("},
85+
{"type":"Name","value":"h"},
86+
{"type":"TextWhitespace","value":" "},
87+
{"type":"Operator","value":"="},
88+
{"type":"TextWhitespace","value":" "},
89+
{"type":"Name","value":"size"},
90+
{"type":"Punctuation","value":","},
91+
{"type":"TextWhitespace","value":" "},
92+
{"type":"Name","value":"d"},
93+
{"type":"TextWhitespace","value":" "},
94+
{"type":"Operator","value":"="},
95+
{"type":"TextWhitespace","value":" "},
96+
{"type":"Name","value":"s"},
97+
{"type":"Punctuation","value":")."},
98+
{"type":"NameFunction","value":"orient"},
99+
{"type":"Punctuation","value":"(["},
100+
{"type":"NameConstant","value":"X"},
101+
{"type":"Punctuation","value":","},
102+
{"type":"NameConstant","value":"Y"},
103+
{"type":"Punctuation","value":","},
104+
{"type":"NameConstant","value":"Z"},
105+
{"type":"Punctuation","value":"]);"},
106+
{"type":"TextWhitespace","value":"\n "},
107+
{"type":"Name","value":"body"},
108+
{"type":"TextWhitespace","value":" "},
109+
{"type":"Operator","value":"-"},
110+
{"type":"TextWhitespace","value":" "},
111+
{"type":"Name","value":"holes"},
112+
{"type":"Punctuation","value":";"},
113+
{"type":"TextWhitespace","value":"\n"},
114+
{"type":"Punctuation","value":"}"},
115+
{"type":"TextWhitespace","value":"\n\n"},
116+
{"type":"NameClass","value":"CsgCube"},
117+
{"type":"Punctuation","value":"("},
118+
{"type":"LiteralNumber","value":"50mm"},
119+
{"type":"Punctuation","value":");"},
120+
{"type":"TextWhitespace","value":"\n"}
121+
]

0 commit comments

Comments
 (0)