From 789335394cb3633610004b45a25ed5d6c1f328c3 Mon Sep 17 00:00:00 2001 From: Barthy Date: Thu, 31 Oct 2024 22:53:43 +0100 Subject: [PATCH] enh(gcode): rewrote language for moden gcode support (#4040) --- CHANGES.md | 3 +- src/languages/gcode.js | 190 ++++++++++++++++++++------ test/markup/gcode/default.expect.txt | 60 ++++---- test/markup/gcode/extended.expect.txt | 86 ++++++++++++ test/markup/gcode/extended.txt | 86 ++++++++++++ 5 files changed, 353 insertions(+), 72 deletions(-) create mode 100644 test/markup/gcode/extended.expect.txt create mode 100644 test/markup/gcode/extended.txt diff --git a/CHANGES.md b/CHANGES.md index d424877729..0981e826dd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -28,6 +28,7 @@ Core Grammars: - fix(c) - Fixed hex numbers with decimals [Dxuian] - fix(typescript) - Fixedoptional property not highlighted correctly [Dxuian] - fix(ruby) - fix `|=` operator false positives (as block arguments) [Aboobacker MK] +- enh(gcode) rewrote language for modern gcode support [Barthélémy Bonhomme][] - fix(sql) - Fixed sql primary key and foreign key spacing issue [Dxuian] - fix(cpp) added flat_set and flat_map as a part of cpp 23 version [Lavan] - fix(yaml) - Fixed special chars in yaml [Dxuian] @@ -70,6 +71,7 @@ CONTRIBUTORS [Sainan]: https://github.com/Sainan [Osmocom]: https://github.com/osmocom [Álvaro Mondéjar]: https://github.com/mondeja +[Barthélémy Bonhomme]: https://github.com/barthy-koeln [Lavan]: https://github.com/jvlavan [Somya]: https://github.com/somya-05 [guuido]: https://github.com/guuido @@ -184,7 +186,6 @@ Themes: [Chiel van de Steeg]: https://github.com/cvdsteeg - ## Version 11.9.0 CAVEATS / POTENTIALLY BREAKING CHANGES diff --git a/src/languages/gcode.js b/src/languages/gcode.js index 15bf97af7c..405cf0fa9b 100644 --- a/src/languages/gcode.js +++ b/src/languages/gcode.js @@ -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 = /(?= '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, + 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, … + { 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)' + 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 { @@ -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 }; } diff --git a/test/markup/gcode/default.expect.txt b/test/markup/gcode/default.expect.txt index 500e8514c4..e0db3161f4 100644 --- a/test/markup/gcode/default.expect.txt +++ b/test/markup/gcode/default.expect.txt @@ -1,31 +1,31 @@ -O003 (DIAMOND SQUARE) -N2 G54 G90 G49 G80 -N3 M6 T1 (1.ENDMILL) -N4 M3 S1800 -N5 G0 X-.6 Y2.050 -N6 G43 H1 Z.1 -N7 G1 Z-.3 F50. -N8 G41 D1 Y1.45 -N9 G1 X0 F20. -N10 G2 J-1.45 +O003 (DIAMOND SQUARE) +N2 G54 G90 G49 G80 +N3 M6 T1 (1.ENDMILL) +N4 M3 S1800 +N5 G0 X-.6 Y2.050 +N6 G43 H1 Z.1 +N7 G1 Z-.3 F50. +N8 G41 D1 Y1.45 +N9 G1 X0 F20. +N10 G2 J-1.45 (CUTTER COMP CANCEL) -N11 G1 Z-.2 F50. -N12 Y-.990 -N13 G40 -N14 G0 X-.6 Y1.590 -N15 G0 Z.1 -N16 M5 G49 G28 G91 Z0 -N17 CALL O9456 -N18 #500=0.004 -N19 #503=[#500+#501] -N20 VC45=0.0006 -VS4=0.0007 -N21 G90 G10 L20 P3 X5.Y4. Z6.567 -N22 G0 X5000 -N23 IF [#1 LT 0.370] GOTO 49 -N24 X-0.678 Y+.990 -N25 G84.3 X-0.1 -N26 #4=#5*COS[45] -N27 #4=#5*SIN[45] -N28 VZOFZ=652.9658 -% +N11 G1 Z-.2 F50. +N12 Y-.990 +N13 G40 +N14 G0 X-.6 Y1.590 +N15 G0 Z.1 +N16 M5 G49 G28 G91 Z0 +N17 CALL O9456 +N18 #500=0.004 +N19 #503=[#500+#501] +N20 VC45=0.0006 +VS4=0.0007 +N21 G90 G10 L20 P3 X5.Y4. Z6.567 +N22 G0 X5000 +N23 IF [#1 LT 0.370] GOTO 49 +N24 X-0.678 Y+.990 +N25 G84.3 X-0.1 +N26 #4=#5*COS[45] +N27 #4=#5*SIN[45] +N28 VZOFZ=652.9658 +% diff --git a/test/markup/gcode/extended.expect.txt b/test/markup/gcode/extended.expect.txt new file mode 100644 index 0000000000..166c830f19 --- /dev/null +++ b/test/markup/gcode/extended.expect.txt @@ -0,0 +1,86 @@ +%; something important + +; another comment + +(yet another more different comment) +( + multi + line + madness +) + +M500; comment after code +M500 (comment after code) + +O<boring> SUB + +#20 = [#1 * SIN[#1]] +#21 = [-#2 * COS[#2]] +#22 = [#3 / COS[#3]] +#23 = [#4 + #4 - #4] +#24 = [[#5 - #5] * TAN[#5]] +#25=FIX[#101/2.75] +#26=ROUND[#104] +#27=ABS[#104] + +; WITH SANE SPACING + +N1 O107 IF [#10 GT 5] +N2 G0 A0 B0 C0 U0 V0 W0 X0 Y0 Z0 *71 +N3 G100 A100 B0 +N4 G5.2 A10.2 B0 +N5 G100 A.2 B0 +N6 G100 A-.2 B0 +N7 G100 A+.2 B0 +N8 T0 +N9 M100 F1 H2 I3 J4 K5 P6 Q7 R8 S9 +N10 M587 S"MYROUTER" P"ABCxyz;" "123" +N10 O107 ENDIF + + +; WITH EXCESSIVE SPACING + +O 108 IF [#10 GT 5] +N 102 G 0 A 0 B 0 C 0 U 0 V 0 W 0 X 0 Y 0 Z 0 *71 +N 103 G 100 A 100 B 0 +N 104 G 5.2 A 10.2 B 0 +N 105 G 100 A .2 B 0 +N 106 G 100 A -.2 B 0 +N 107 G 100 A +.2 B 0 +N 108 T 0 +N 109 M 100 F 1 H 2 I 3 J 4 K 5 P 6 Q 7 R 8 S 9 +N 110 M 587 S "MYROUTER" P "ABCxyz;" "123" +O 108 ENDIF + + +; WITHOUT SPACING + +O109IF[#10GT5] +N202G0A0B0C0U0V0W0X0Y0Z0*71 +N203G100A100B0 +N204G5.2A10.2B0 +N205G100A.2B0 +N206G100A-.2B0 +N207G100A+.2B0 +N208T0 +N209M100F1H2I3J4K5P6Q7R8S9 +N210M587S"MYROUTER"P"ABCxyz;""123" +O109ENDIF + +O<boring> ENDSUB + +O<boring> CALL [1] [2] [3] [4] [5] + +; Words as seen in Klipper Firmware GCode + +GET_RETRACTION +TURN_OFF_HEATERS +MANUAL_STEPPER STEPPER=config_name ENABLE=0 SET_POSITION=11.1 SPEED=22.2 ACCEL=33.3 MOVE=44.4 STOP_ON_ENDSTOP=-1 SYNC=0 + +SET_SKEW XY=100.0000,100.0000,70.7107 +SKEW_PROFILE SAVE="this name has spaces" +SAVE_CONFIG + +SKEW_PROFILE SAVE=some_ G123 +SKEW_PROFILE SAVE=some_G123 +% diff --git a/test/markup/gcode/extended.txt b/test/markup/gcode/extended.txt new file mode 100644 index 0000000000..b6b81a107d --- /dev/null +++ b/test/markup/gcode/extended.txt @@ -0,0 +1,86 @@ +%; something important + +; another comment + +(yet another more different comment) +( + multi + line + madness +) + +M500; comment after code +M500 (comment after code) + +O SUB + +#20 = [#1 * SIN[#1]] +#21 = [-#2 * COS[#2]] +#22 = [#3 / COS[#3]] +#23 = [#4 + #4 - #4] +#24 = [[#5 - #5] * TAN[#5]] +#25=FIX[#101/2.75] +#26=ROUND[#104] +#27=ABS[#104] + +; WITH SANE SPACING + +N1 O107 IF [#10 GT 5] +N2 G0 A0 B0 C0 U0 V0 W0 X0 Y0 Z0 *71 +N3 G100 A100 B0 +N4 G5.2 A10.2 B0 +N5 G100 A.2 B0 +N6 G100 A-.2 B0 +N7 G100 A+.2 B0 +N8 T0 +N9 M100 F1 H2 I3 J4 K5 P6 Q7 R8 S9 +N10 M587 S"MYROUTER" P"ABCxyz;" "123" +N10 O107 ENDIF + + +; WITH EXCESSIVE SPACING + +O 108 IF [#10 GT 5] +N 102 G 0 A 0 B 0 C 0 U 0 V 0 W 0 X 0 Y 0 Z 0 *71 +N 103 G 100 A 100 B 0 +N 104 G 5.2 A 10.2 B 0 +N 105 G 100 A .2 B 0 +N 106 G 100 A -.2 B 0 +N 107 G 100 A +.2 B 0 +N 108 T 0 +N 109 M 100 F 1 H 2 I 3 J 4 K 5 P 6 Q 7 R 8 S 9 +N 110 M 587 S "MYROUTER" P "ABCxyz;" "123" +O 108 ENDIF + + +; WITHOUT SPACING + +O109IF[#10GT5] +N202G0A0B0C0U0V0W0X0Y0Z0*71 +N203G100A100B0 +N204G5.2A10.2B0 +N205G100A.2B0 +N206G100A-.2B0 +N207G100A+.2B0 +N208T0 +N209M100F1H2I3J4K5P6Q7R8S9 +N210M587S"MYROUTER"P"ABCxyz;""123" +O109ENDIF + +O ENDSUB + +O CALL [1] [2] [3] [4] [5] + +; Words as seen in Klipper Firmware GCode + +GET_RETRACTION +TURN_OFF_HEATERS +MANUAL_STEPPER STEPPER=config_name ENABLE=0 SET_POSITION=11.1 SPEED=22.2 ACCEL=33.3 MOVE=44.4 STOP_ON_ENDSTOP=-1 SYNC=0 + +SET_SKEW XY=100.0000,100.0000,70.7107 +SKEW_PROFILE SAVE="this name has spaces" +SAVE_CONFIG + +SKEW_PROFILE SAVE=some_ G123 +SKEW_PROFILE SAVE=some_G123 +%