diff --git a/AUTHORS b/AUTHORS index 5607d65668..503af632cc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -267,5 +267,6 @@ Other contributors, listed alphabetically, are: * Marc Auberer -- Spice lexer * Amr Hesham -- Carbon lexer * diskdance -- Wikitext lexer +* Nikolay Antipov -- OpenSCAD lexer Many thanks for all contributions! diff --git a/pygments/lexers/_mapping.py b/pygments/lexers/_mapping.py index 87d261c715..4703705ec4 100644 --- a/pygments/lexers/_mapping.py +++ b/pygments/lexers/_mapping.py @@ -353,6 +353,7 @@ 'OocLexer': ('pygments.lexers.ooc', 'Ooc', ('ooc',), ('*.ooc',), ('text/x-ooc',)), 'OpaLexer': ('pygments.lexers.ml', 'Opa', ('opa',), ('*.opa',), ('text/x-opa',)), 'OpenEdgeLexer': ('pygments.lexers.business', 'OpenEdge ABL', ('openedge', 'abl', 'progress'), ('*.p', '*.cls'), ('text/x-openedge', 'application/x-openedge')), + 'OpenScadLexer': ('pygments.lexers.openscad', 'OpenSCAD', ('openscad',), ('*.scad',), ('application/x-openscad',)), 'OutputLexer': ('pygments.lexers.special', 'Text output', ('output',), (), ()), 'PacmanConfLexer': ('pygments.lexers.configs', 'PacmanConf', ('pacmanconf',), ('pacman.conf',), ()), 'PanLexer': ('pygments.lexers.dsls', 'Pan', ('pan',), ('*.pan',), ()), diff --git a/pygments/lexers/openscad.py b/pygments/lexers/openscad.py new file mode 100644 index 0000000000..3d16977fb0 --- /dev/null +++ b/pygments/lexers/openscad.py @@ -0,0 +1,97 @@ +""" + pygments.lexers.openscad + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for the OpenSCAD languages. + + :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, bygroups, words, include +from pygments.token import Text, Comment, Punctuation, Operator, Keyword, Name, Number, Whitespace, Literal, String + +__all__ = ['OpenScadLexer'] + + +class OpenScadLexer(RegexLexer): + """For openSCAD code. + + .. versionadded:: 2.16.0 + """ + name = "OpenSCAD" + url = "https://openscad.org/" + aliases = ["openscad"] + filenames = ["*.scad"] + mimetypes = ["application/x-openscad"] + + tokens = { + "root": [ + (r"[^\S\n]+", Whitespace), + (r'//', Comment.Single, 'comment-single'), + (r'/\*', Comment.Multiline, 'comment-multi'), + (r"[{}\[\]\(\),;:]", Punctuation), + (r"[*!#%\-+=?/]", Operator), + (r"<=|<|==|!=|>=|>|&&|\|\|", Operator), + (r"\$(f[asn]|t|vp[rtd]|children)", Operator), + (r"(undef|PI)\b", Keyword.Constant), + ( + r"(use|include)((?:\s|\\\\s)+)", + bygroups(Keyword.Namespace, Text), + "includes", + ), + (r"(module)(\s*)([^\s\(]+)", + bygroups(Keyword.Namespace, Whitespace, Name.Namespace)), + (r"(function)(\s*)([^\s\(]+)", + bygroups(Keyword.Declaration, Whitespace, Name.Function)), + (words(("true", "false"), prefix=r"\b", suffix=r"\b"), Literal), + (words(( + "function", "module", "include", "use", "for", + "intersection_for", "if", "else", "return" + ), prefix=r"\b", suffix=r"\b"), Keyword + ), + (words(( + "circle", "square", "polygon", "text", "sphere", "cube", + "cylinder", "polyhedron", "translate", "rotate", "scale", + "resize", "mirror", "multmatrix", "color", "offset", "hull", + "minkowski", "union", "difference", "intersection", "abs", + "sign", "sin", "cos", "tan", "acos", "asin", "atan", "atan2", + "floor", "round", "ceil", "ln", "log", "pow", "sqrt", "exp", + "rands", "min", "max", "concat", "lookup", "str", "chr", + "search", "version", "version_num", "norm", "cross", + "parent_module", "echo", "import", "import_dxf", + "dxf_linear_extrude", "linear_extrude", "rotate_extrude", + "surface", "projection", "render", "dxf_cross", + "dxf_dim", "let", "assign", "len" + ), prefix=r"\b", suffix=r"\b"), + Name.Builtin + ), + (r"\bchildren\b", Name.Builtin.Pseudo), + (r'""".*?"""', String.Double), + (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double), + (r"-?\d+(\.\d+)?(e[+-]?\d+)?", Number), + (r"\w+", Name), + ], + "includes": [ + ( + r"(<)([^>]*)(>)", + bygroups(Punctuation, Comment.PreprocFile, Punctuation), + ), + ], + 'comment': [ + (r':param: [a-zA-Z_]\w*|:returns?:|(FIXME|MARK|TODO):', + Comment.Special) + ], + 'comment-single': [ + (r'\n', Text, '#pop'), + include('comment'), + (r'[^\n]+', Comment.Single) + ], + 'comment-multi': [ + include('comment'), + (r'[^*/]+', Comment.Multiline), + (r'/\*', Comment.Multiline, '#push'), + (r'\*/', Comment.Multiline, '#pop'), + (r'[*/]', Comment.Multiline) + ], + } diff --git a/tests/snippets/openscad/test_basic.txt b/tests/snippets/openscad/test_basic.txt new file mode 100644 index 0000000000..c1d2f3def3 --- /dev/null +++ b/tests/snippets/openscad/test_basic.txt @@ -0,0 +1,391 @@ +---input--- + +include + +/* + Multiline comment +*/ + +egg(1500); + +// Single line comment +// TODO: add a line + +module egg(length, start=-1000, end=-1000) { + rotate_extrude() + translate([0, -length/2, 0]) + rotate([0, 0, 90]) + polygon(egg_half_poly( + length, + start == -1000 ? 0 : start, + end == -1000 ? length : end + )); +} + +function egg_half_poly(length, start, end) = + concat( + [[start, 0]], + [for (x=[start : (release ? 0.5 : 1) : end]) [x, egg_eq(x - length/2, length)]], + [[end, 0]] + ); + +function egg_eq(x, length) = + length / 1.25 / 2 * sqrt( + (length * length - 4 * x * x) / + (length * length + 8 * egg_w * x + 4 * egg_w * egg_w) + ); + + +---tokens--- +'include' Keyword.Namespace +' ' Text +'<' Punctuation +'threads.scad' Comment.PreprocFile +'>' Punctuation +'\n' Text.Whitespace + +'\n' Text.Whitespace + +'/*' Comment.Multiline +'\n Multiline comment\n' Comment.Multiline + +'*/' Comment.Multiline +'\n' Text.Whitespace + +'\n' Text.Whitespace + +'egg' Name +'(' Punctuation +'1500' Literal.Number +')' Punctuation +';' Punctuation +'\n' Text.Whitespace + +'\n' Text.Whitespace + +'//' Comment.Single +' Single line comment' Comment.Single +'\n' Text + +'//' Comment.Single +' TODO: add a line' Comment.Single +'\n' Text + +'\n' Text.Whitespace + +'module' Keyword.Namespace +' ' Text.Whitespace +'egg' Name.Namespace +'(' Punctuation +'length' Name +',' Punctuation +' ' Text.Whitespace +'start' Name +'=' Operator +'-' Operator +'1000' Literal.Number +',' Punctuation +' ' Text.Whitespace +'end' Name +'=' Operator +'-' Operator +'1000' Literal.Number +')' Punctuation +' ' Text.Whitespace +'{' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'rotate_extrude' Name.Builtin +'(' Punctuation +')' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'translate' Name.Builtin +'(' Punctuation +'[' Punctuation +'0' Literal.Number +',' Punctuation +' ' Text.Whitespace +'-' Operator +'length' Name +'/' Operator +'2' Literal.Number +',' Punctuation +' ' Text.Whitespace +'0' Literal.Number +']' Punctuation +')' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'rotate' Name.Builtin +'(' Punctuation +'[' Punctuation +'0' Literal.Number +',' Punctuation +' ' Text.Whitespace +'0' Literal.Number +',' Punctuation +' ' Text.Whitespace +'90' Literal.Number +']' Punctuation +')' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'polygon' Name.Builtin +'(' Punctuation +'egg_half_poly' Name +'(' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'length' Name +',' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'start' Name +' ' Text.Whitespace +'=' Operator +'=' Operator +' ' Text.Whitespace +'-' Operator +'1000' Literal.Number +' ' Text.Whitespace +'?' Operator +' ' Text.Whitespace +'0' Literal.Number +' ' Text.Whitespace +':' Punctuation +' ' Text.Whitespace +'start' Name +',' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'end' Name +' ' Text.Whitespace +'=' Operator +'=' Operator +' ' Text.Whitespace +'-' Operator +'1000' Literal.Number +' ' Text.Whitespace +'?' Operator +' ' Text.Whitespace +'length' Name +' ' Text.Whitespace +':' Punctuation +' ' Text.Whitespace +'end' Name +'\n' Text.Whitespace + +' ' Text.Whitespace +')' Punctuation +')' Punctuation +';' Punctuation +'\n' Text.Whitespace + +'}' Punctuation +'\n' Text.Whitespace + +'\n' Text.Whitespace + +'function' Keyword.Declaration +' ' Text.Whitespace +'egg_half_poly' Name.Function +'(' Punctuation +'length' Name +',' Punctuation +' ' Text.Whitespace +'start' Name +',' Punctuation +' ' Text.Whitespace +'end' Name +')' Punctuation +' ' Text.Whitespace +'=' Operator +'\n' Text.Whitespace + +' ' Text.Whitespace +'concat' Name.Builtin +'(' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'[' Punctuation +'[' Punctuation +'start' Name +',' Punctuation +' ' Text.Whitespace +'0' Literal.Number +']' Punctuation +']' Punctuation +',' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'[' Punctuation +'for' Keyword +' ' Text.Whitespace +'(' Punctuation +'x' Name +'=' Operator +'[' Punctuation +'start' Name +' ' Text.Whitespace +':' Punctuation +' ' Text.Whitespace +'(' Punctuation +'release' Name +' ' Text.Whitespace +'?' Operator +' ' Text.Whitespace +'0.5' Literal.Number +' ' Text.Whitespace +':' Punctuation +' ' Text.Whitespace +'1' Literal.Number +')' Punctuation +' ' Text.Whitespace +':' Punctuation +' ' Text.Whitespace +'end' Name +']' Punctuation +')' Punctuation +' ' Text.Whitespace +'[' Punctuation +'x' Name +',' Punctuation +' ' Text.Whitespace +'egg_eq' Name +'(' Punctuation +'x' Name +' ' Text.Whitespace +'-' Operator +' ' Text.Whitespace +'length' Name +'/' Operator +'2' Literal.Number +',' Punctuation +' ' Text.Whitespace +'length' Name +')' Punctuation +']' Punctuation +']' Punctuation +',' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'[' Punctuation +'[' Punctuation +'end' Name +',' Punctuation +' ' Text.Whitespace +'0' Literal.Number +']' Punctuation +']' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +')' Punctuation +';' Punctuation +'\n' Text.Whitespace + +'\n' Text.Whitespace + +'function' Keyword.Declaration +' ' Text.Whitespace +'egg_eq' Name.Function +'(' Punctuation +'x' Name +',' Punctuation +' ' Text.Whitespace +'length' Name +')' Punctuation +' ' Text.Whitespace +'=' Operator +'\n' Text.Whitespace + +' ' Text.Whitespace +'length' Name +' ' Text.Whitespace +'/' Operator +' ' Text.Whitespace +'1.25' Literal.Number +' ' Text.Whitespace +'/' Operator +' ' Text.Whitespace +'2' Literal.Number +' ' Text.Whitespace +'*' Operator +' ' Text.Whitespace +'sqrt' Name.Builtin +'(' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +'(' Punctuation +'length' Name +' ' Text.Whitespace +'*' Operator +' ' Text.Whitespace +'length' Name +' ' Text.Whitespace +'-' Operator +' ' Text.Whitespace +'4' Literal.Number +' ' Text.Whitespace +'*' Operator +' ' Text.Whitespace +'x' Name +' ' Text.Whitespace +'*' Operator +' ' Text.Whitespace +'x' Name +')' Punctuation +' ' Text.Whitespace +'/' Operator +'\n' Text.Whitespace + +' ' Text.Whitespace +'(' Punctuation +'length' Name +' ' Text.Whitespace +'*' Operator +' ' Text.Whitespace +'length' Name +' ' Text.Whitespace +'+' Operator +' ' Text.Whitespace +'8' Literal.Number +' ' Text.Whitespace +'*' Operator +' ' Text.Whitespace +'egg_w' Name +' ' Text.Whitespace +'*' Operator +' ' Text.Whitespace +'x' Name +' ' Text.Whitespace +'+' Operator +' ' Text.Whitespace +'4' Literal.Number +' ' Text.Whitespace +'*' Operator +' ' Text.Whitespace +'egg_w' Name +' ' Text.Whitespace +'*' Operator +' ' Text.Whitespace +'egg_w' Name +')' Punctuation +'\n' Text.Whitespace + +' ' Text.Whitespace +')' Punctuation +';' Punctuation +'\n' Text.Whitespace