Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enh(gcode): rewrote language for moden gcode support #4040

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Expand Up @@ -40,6 +40,7 @@ Core Grammars:
- enh(css) add `justify-items` and `justify-self` attributes [Vasily Polovnyov][]
- enh(css) add `accent-color`, `appearance`, `color-scheme`, `rotate`, `scale` and `translate` attributes [Carl Räfting][]
- fix(fortran) fixes parsing of keywords delimited by dots [Julien Bloino][]
- enh(gcode) rewrote language for modern gcode support [Barthélémy Bonhomme][]

New Grammars:

Expand Down Expand Up @@ -92,6 +93,7 @@ Themes:
[Carl Räfting]: https://github.com/carlrafting
[BackupMiles]: https://github.com/BackupMiles
[Julien Bloino]: https://github.com/jbloino
[Barthélémy Bonhomme]: https://github.com/barthy-koeln



Expand Down
190 changes: 149 additions & 41 deletions src/languages/gcode.js
Expand Up @@ -7,58 +7,170 @@
*/

export default function(hljs) {
const GCODE_IDENT_RE = '[A-Z_][A-Z0-9_.]*';
const GCODE_CLOSE_RE = '%';
const regex = hljs.regex;
const GCODE_KEYWORDS = {
$pattern: GCODE_IDENT_RE,
keyword: 'IF DO WHILE ENDWHILE CALL ENDIF SUB ENDSUB GOTO REPEAT ENDREPEAT '
+ 'EQ LT GT NE GE LE OR XOR'
};
const GCODE_START = {
className: 'meta',
begin: '([O])([0-9]+)'
$pattern: /[A-Z]+|%/,
keyword: [
// conditions
'THEN',
'ELSE',
'ENDIF',
'IF',

// controls
'GOTO',
'DO',
'WHILE',
'WH',
'END',
'CALL',

// scoping
'SUB',
'ENDSUB',

// comparisons
'EQ',
'NE',
'LT',
'GT',
'LE',
'GE',
'AND',
'OR',
'XOR',

// start/end of program
'%'
],
built_in: [
'ATAN',
'ABS',
'ACOS',
'ASIN',
'COS',
'EXP',
'FIX',
'FUP',
'ROUND',
'LN',
'SIN',
'SQRT',
'TAN',
'EXISTS'
]
};
const NUMBER = hljs.inherit(hljs.C_NUMBER_MODE, { begin: '([-+]?((\\.\\d+)|(\\d+)(\\.\\d*)?))|' + hljs.C_NUMBER_RE });


// TODO: post v12 lets use look-behind, until then \b and a callback filter will be used
// const LETTER_BOUNDARY_RE = /(?<![A-Z])/;
const LETTER_BOUNDARY_RE = /\b/;

function LETTER_BOUNDARY_CALLBACK(matchdata, response) {
if (matchdata.index === 0) {
return;
}

const charBeforeMatch = matchdata.input[matchdata.index - 1];
if (charBeforeMatch >= '0' && charBeforeMatch <= '9') {
return;
}

if (charBeforeMatch === '_') {
return;
}

response.ignoreMatch();
}

const NUMBER_RE = /[+-]?((\.\d+)|(\d+)(\.\d*)?)/;

const GENERAL_MISC_FUNCTION_RE = /[GM]\s*\d+(\.\d+)?/;
const TOOLS_RE = /T\s*\d+/;
const SUBROUTINE_RE = /O\s*\d+/;
const SUBROUTINE_NAMED_RE = /O<.+>/;
const AXES_RE = /[ABCUVWXYZ]\s*/;
const PARAMETERS_RE = /[FHIJKPQRS]\s*/;

const GCODE_CODE = [
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
// comments
hljs.COMMENT(/\(/, /\)/),
NUMBER,
hljs.inherit(hljs.APOS_STRING_MODE, { illegal: null }),
hljs.inherit(hljs.QUOTE_STRING_MODE, { illegal: null }),
hljs.COMMENT(/;/, /$/),
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
joshgoebel marked this conversation as resolved.
Show resolved Hide resolved
hljs.C_NUMBER_MODE,

// gcodes
{
className: 'name',
begin: '([G])([0-9]+\\.?[0-9]?)'
scope: 'title.function',
variants: [
// G General functions: G0, G5.1, G5.2, …
// M Misc functions: M0, M55.6, M199, …
{ match: regex.concat(LETTER_BOUNDARY_RE, GENERAL_MISC_FUNCTION_RE) },
{
begin: GENERAL_MISC_FUNCTION_RE,
'on:begin': LETTER_BOUNDARY_CALLBACK
},
// T Tools
{ match: regex.concat(LETTER_BOUNDARY_RE, TOOLS_RE), },
{
begin: TOOLS_RE,
'on:begin': LETTER_BOUNDARY_CALLBACK
}
]
},

{
className: 'name',
begin: '([M])([0-9]+\\.?[0-9]?)'
scope: 'symbol',
variants: [
// O Subroutine ID: O100, O110, …
{ match: regex.concat(LETTER_BOUNDARY_RE, SUBROUTINE_RE) },
{
begin: SUBROUTINE_RE,
'on:begin': LETTER_BOUNDARY_CALLBACK
},
// O Subroutine name: O<some>, …
{ match: regex.concat(LETTER_BOUNDARY_RE, SUBROUTINE_NAMED_RE) },
{
begin: SUBROUTINE_NAMED_RE,
'on:begin': LETTER_BOUNDARY_CALLBACK
},
// Checksum at end of line: *71, *199, …
{ match: /\*\s*\d+\s*$/ }
]
},

{
className: 'attr',
begin: '(VC|VS|#)',
end: '(\\d+)'
scope: 'operator', // N Line number: N1, N2, N1020, …
match: /^N\s*\d+/
},

{
className: 'attr',
begin: '(VZOFX|VZOFY|VZOFZ)'
joshgoebel marked this conversation as resolved.
Show resolved Hide resolved
scope: 'variable',
match: /-?#\s*\d+/
},

{
className: 'built_in',
begin: '(ATAN|ABS|ACOS|ASIN|SIN|COS|EXP|FIX|FUP|ROUND|LN|TAN)(\\[)',
contains: [ NUMBER ],
end: '\\]'
scope: 'property', // Physical axes,
variants: [
{ match: regex.concat(LETTER_BOUNDARY_RE, AXES_RE, NUMBER_RE) },
{
begin: regex.concat(AXES_RE, NUMBER_RE),
'on:begin': LETTER_BOUNDARY_CALLBACK
},
]
},

{
className: 'symbol',
scope: 'params', // Different types of parameters
variants: [
{ match: regex.concat(LETTER_BOUNDARY_RE, PARAMETERS_RE, NUMBER_RE) },
{
begin: 'N',
end: '\\d+',
illegal: '\\W'
}
begin: regex.concat(PARAMETERS_RE, NUMBER_RE),
'on:begin': LETTER_BOUNDARY_CALLBACK
},
]
}
},
];

return {
Expand All @@ -67,13 +179,9 @@ export default function(hljs) {
// Some implementations (CNC controls) of G-code are interoperable with uppercase and lowercase letters seamlessly.
// However, most prefer all uppercase and uppercase is customary.
case_insensitive: true,
// TODO: post v12 with the use of look-behind this can be enabled
disableAutodetect: true,
keywords: GCODE_KEYWORDS,
contains: [
{
className: 'meta',
begin: GCODE_CLOSE_RE
},
GCODE_START
].concat(GCODE_CODE)
contains: GCODE_CODE
};
}
60 changes: 30 additions & 30 deletions test/markup/gcode/default.expect.txt
@@ -1,31 +1,31 @@
<span class="hljs-meta">O003</span> <span class="hljs-comment">(DIAMOND SQUARE)</span>
<span class="hljs-symbol">N2</span> <span class="hljs-name">G54</span> <span class="hljs-name">G90</span> <span class="hljs-name">G49</span> <span class="hljs-name">G80</span>
<span class="hljs-symbol">N3</span> <span class="hljs-name">M6</span> T<span class="hljs-number">1</span> <span class="hljs-comment">(1.ENDMILL)</span>
<span class="hljs-symbol">N4</span> <span class="hljs-name">M3</span> S<span class="hljs-number">1800</span>
<span class="hljs-symbol">N5</span> <span class="hljs-name">G0</span> X<span class="hljs-number">-.6</span> Y<span class="hljs-number">2.050</span>
<span class="hljs-symbol">N6</span> <span class="hljs-name">G43</span> H<span class="hljs-number">1</span> Z<span class="hljs-number">.1</span>
<span class="hljs-symbol">N7</span> <span class="hljs-name">G1</span> Z<span class="hljs-number">-.3</span> F<span class="hljs-number">50.</span>
<span class="hljs-symbol">N8</span> <span class="hljs-name">G41</span> D<span class="hljs-number">1</span> Y<span class="hljs-number">1.45</span>
<span class="hljs-symbol">N9</span> <span class="hljs-name">G1</span> X<span class="hljs-number">0</span> F<span class="hljs-number">20.</span>
<span class="hljs-symbol">N10</span> <span class="hljs-name">G2</span> J<span class="hljs-number">-1.45</span>
<span class="hljs-symbol">O003</span> <span class="hljs-comment">(DIAMOND SQUARE)</span>
<span class="hljs-operator">N2</span> <span class="hljs-title function_">G54</span> <span class="hljs-title function_">G90</span> <span class="hljs-title function_">G49</span> <span class="hljs-title function_">G80</span>
<span class="hljs-operator">N3</span> <span class="hljs-title function_">M6</span> <span class="hljs-title function_">T1</span> <span class="hljs-comment">(1.ENDMILL)</span>
<span class="hljs-operator">N4</span> <span class="hljs-title function_">M3</span> <span class="hljs-params">S1800</span>
<span class="hljs-operator">N5</span> <span class="hljs-title function_">G0</span> <span class="hljs-property">X-.6</span> <span class="hljs-property">Y2.050</span>
<span class="hljs-operator">N6</span> <span class="hljs-title function_">G43</span> <span class="hljs-params">H1</span> <span class="hljs-property">Z.1</span>
<span class="hljs-operator">N7</span> <span class="hljs-title function_">G1</span> <span class="hljs-property">Z-.3</span> <span class="hljs-params">F50.</span>
<span class="hljs-operator">N8</span> <span class="hljs-title function_">G41</span> D1 <span class="hljs-property">Y1.45</span>
<span class="hljs-operator">N9</span> <span class="hljs-title function_">G1</span> <span class="hljs-property">X0</span> <span class="hljs-params">F20.</span>
<span class="hljs-operator">N10</span> <span class="hljs-title function_">G2</span> <span class="hljs-params">J-1.45</span>
<span class="hljs-comment">(CUTTER COMP CANCEL)</span>
<span class="hljs-symbol">N11</span> <span class="hljs-name">G1</span> Z<span class="hljs-number">-.2</span> F<span class="hljs-number">50.</span>
<span class="hljs-symbol">N12</span> Y<span class="hljs-number">-.990</span>
<span class="hljs-symbol">N13</span> <span class="hljs-name">G40</span>
<span class="hljs-symbol">N14</span> <span class="hljs-name">G0</span> X<span class="hljs-number">-.6</span> Y<span class="hljs-number">1.590</span>
<span class="hljs-symbol">N15</span> <span class="hljs-name">G0</span> Z<span class="hljs-number">.1</span>
<span class="hljs-symbol">N16</span> <span class="hljs-name">M5</span> <span class="hljs-name">G49</span> <span class="hljs-name">G28</span> <span class="hljs-name">G91</span> Z<span class="hljs-number">0</span>
<span class="hljs-symbol">N17</span> <span class="hljs-keyword">CALL</span> <span class="hljs-meta">O9456</span>
<span class="hljs-symbol">N18</span> <span class="hljs-attr">#500</span>=<span class="hljs-number">0.004</span>
<span class="hljs-symbol">N19</span> <span class="hljs-attr">#503</span>=[<span class="hljs-attr">#500</span>+<span class="hljs-attr">#501</span>]
<span class="hljs-symbol">N20</span> <span class="hljs-attr">VC45</span>=<span class="hljs-number">0.0006</span>
<span class="hljs-attr">VS4</span>=<span class="hljs-number">0.0007</span>
<span class="hljs-symbol">N21</span> <span class="hljs-name">G90</span> <span class="hljs-name">G10</span> L<span class="hljs-number">20</span> P<span class="hljs-number">3</span> X<span class="hljs-number">5.</span>Y<span class="hljs-number">4.</span> Z<span class="hljs-number">6.567</span>
<span class="hljs-symbol">N22</span> <span class="hljs-name">G0</span> X<span class="hljs-number">5000</span>
<span class="hljs-symbol">N23</span> <span class="hljs-keyword">IF</span> [<span class="hljs-attr">#1</span> <span class="hljs-keyword">LT</span> <span class="hljs-number">0.370</span>] <span class="hljs-keyword">GOTO</span> <span class="hljs-number">49</span>
<span class="hljs-symbol">N24</span> X<span class="hljs-number">-0.678</span> Y<span class="hljs-number">+.990</span>
<span class="hljs-symbol">N25</span> <span class="hljs-name">G84.3</span> X<span class="hljs-number">-0.1</span>
<span class="hljs-symbol">N26</span> <span class="hljs-attr">#4</span>=<span class="hljs-attr">#5</span>*<span class="hljs-built_in">COS[<span class="hljs-number">45</span>]</span>
<span class="hljs-symbol">N27</span> <span class="hljs-attr">#4</span>=<span class="hljs-attr">#5</span>*<span class="hljs-built_in">SIN[<span class="hljs-number">45</span>]</span>
<span class="hljs-symbol">N28</span> <span class="hljs-attr">VZOFZ</span>=<span class="hljs-number">652.9658</span>
<span class="hljs-meta">%</span>
<span class="hljs-operator">N11</span> <span class="hljs-title function_">G1</span> <span class="hljs-property">Z-.2</span> <span class="hljs-params">F50.</span>
<span class="hljs-operator">N12</span> <span class="hljs-property">Y-.990</span>
<span class="hljs-operator">N13</span> <span class="hljs-title function_">G40</span>
<span class="hljs-operator">N14</span> <span class="hljs-title function_">G0</span> <span class="hljs-property">X-.6</span> <span class="hljs-property">Y1.590</span>
<span class="hljs-operator">N15</span> <span class="hljs-title function_">G0</span> <span class="hljs-property">Z.1</span>
<span class="hljs-operator">N16</span> <span class="hljs-title function_">M5</span> <span class="hljs-title function_">G49</span> <span class="hljs-title function_">G28</span> <span class="hljs-title function_">G91</span> <span class="hljs-property">Z0</span>
<span class="hljs-operator">N17</span> <span class="hljs-keyword">CALL</span> <span class="hljs-symbol">O9456</span>
<span class="hljs-operator">N18</span> <span class="hljs-variable">#500</span>=<span class="hljs-number">0.004</span>
<span class="hljs-operator">N19</span> <span class="hljs-variable">#503</span>=[<span class="hljs-variable">#500</span>+<span class="hljs-variable">#501</span>]
<span class="hljs-operator">N20</span> VC45=<span class="hljs-number">0.0006</span>
VS4=<span class="hljs-number">0.0007</span>
<span class="hljs-operator">N21</span> <span class="hljs-title function_">G90</span> <span class="hljs-title function_">G10</span> L20 <span class="hljs-params">P3</span> <span class="hljs-property">X5.</span><span class="hljs-property">Y4.</span> <span class="hljs-property">Z6.567</span>
<span class="hljs-operator">N22</span> <span class="hljs-title function_">G0</span> <span class="hljs-property">X5000</span>
<span class="hljs-operator">N23</span> <span class="hljs-keyword">IF</span> [<span class="hljs-variable">#1</span> <span class="hljs-keyword">LT</span> <span class="hljs-number">0.370</span>] <span class="hljs-keyword">GOTO</span> <span class="hljs-number">49</span>
<span class="hljs-operator">N24</span> <span class="hljs-property">X-0.678</span> <span class="hljs-property">Y+.990</span>
<span class="hljs-operator">N25</span> <span class="hljs-title function_">G84.3</span> <span class="hljs-property">X-0.1</span>
<span class="hljs-operator">N26</span> <span class="hljs-variable">#4</span>=<span class="hljs-variable">#5</span>*<span class="hljs-built_in">COS</span>[<span class="hljs-number">45</span>]
<span class="hljs-operator">N27</span> <span class="hljs-variable">#4</span>=<span class="hljs-variable">#5</span>*<span class="hljs-built_in">SIN</span>[<span class="hljs-number">45</span>]
<span class="hljs-operator">N28</span> VZOFZ=<span class="hljs-number">652.9658</span>
<span class="hljs-keyword">%</span>