gcode.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. Language: G-code (ISO 6983)
  3. Contributors: Adam Joseph Cook <adam.joseph.cook@gmail.com>
  4. Description: G-code syntax highlighter for Fanuc and other common CNC machine tool controls.
  5. Website: https://www.sis.se/api/document/preview/911952/
  6. Category: hardware
  7. */
  8. function gcode(hljs) {
  9. const regex = hljs.regex;
  10. const GCODE_KEYWORDS = {
  11. $pattern: /[A-Z]+|%/,
  12. keyword: [
  13. // conditions
  14. 'THEN',
  15. 'ELSE',
  16. 'ENDIF',
  17. 'IF',
  18. // controls
  19. 'GOTO',
  20. 'DO',
  21. 'WHILE',
  22. 'WH',
  23. 'END',
  24. 'CALL',
  25. // scoping
  26. 'SUB',
  27. 'ENDSUB',
  28. // comparisons
  29. 'EQ',
  30. 'NE',
  31. 'LT',
  32. 'GT',
  33. 'LE',
  34. 'GE',
  35. 'AND',
  36. 'OR',
  37. 'XOR',
  38. // start/end of program
  39. '%'
  40. ],
  41. built_in: [
  42. 'ATAN',
  43. 'ABS',
  44. 'ACOS',
  45. 'ASIN',
  46. 'COS',
  47. 'EXP',
  48. 'FIX',
  49. 'FUP',
  50. 'ROUND',
  51. 'LN',
  52. 'SIN',
  53. 'SQRT',
  54. 'TAN',
  55. 'EXISTS'
  56. ]
  57. };
  58. // TODO: post v12 lets use look-behind, until then \b and a callback filter will be used
  59. // const LETTER_BOUNDARY_RE = /(?<![A-Z])/;
  60. const LETTER_BOUNDARY_RE = /\b/;
  61. function LETTER_BOUNDARY_CALLBACK(matchdata, response) {
  62. if (matchdata.index === 0) {
  63. return;
  64. }
  65. const charBeforeMatch = matchdata.input[matchdata.index - 1];
  66. if (charBeforeMatch >= '0' && charBeforeMatch <= '9') {
  67. return;
  68. }
  69. if (charBeforeMatch === '_') {
  70. return;
  71. }
  72. response.ignoreMatch();
  73. }
  74. const NUMBER_RE = /[+-]?((\.\d+)|(\d+)(\.\d*)?)/;
  75. const GENERAL_MISC_FUNCTION_RE = /[GM]\s*\d+(\.\d+)?/;
  76. const TOOLS_RE = /T\s*\d+/;
  77. const SUBROUTINE_RE = /O\s*\d+/;
  78. const SUBROUTINE_NAMED_RE = /O<.+>/;
  79. const AXES_RE = /[ABCUVWXYZ]\s*/;
  80. const PARAMETERS_RE = /[FHIJKPQRS]\s*/;
  81. const GCODE_CODE = [
  82. // comments
  83. hljs.COMMENT(/\(/, /\)/),
  84. hljs.COMMENT(/;/, /$/),
  85. hljs.APOS_STRING_MODE,
  86. hljs.QUOTE_STRING_MODE,
  87. hljs.C_NUMBER_MODE,
  88. // gcodes
  89. {
  90. scope: 'title.function',
  91. variants: [
  92. // G General functions: G0, G5.1, G5.2, …
  93. // M Misc functions: M0, M55.6, M199, …
  94. { match: regex.concat(LETTER_BOUNDARY_RE, GENERAL_MISC_FUNCTION_RE) },
  95. {
  96. begin: GENERAL_MISC_FUNCTION_RE,
  97. 'on:begin': LETTER_BOUNDARY_CALLBACK
  98. },
  99. // T Tools
  100. { match: regex.concat(LETTER_BOUNDARY_RE, TOOLS_RE), },
  101. {
  102. begin: TOOLS_RE,
  103. 'on:begin': LETTER_BOUNDARY_CALLBACK
  104. }
  105. ]
  106. },
  107. {
  108. scope: 'symbol',
  109. variants: [
  110. // O Subroutine ID: O100, O110, …
  111. { match: regex.concat(LETTER_BOUNDARY_RE, SUBROUTINE_RE) },
  112. {
  113. begin: SUBROUTINE_RE,
  114. 'on:begin': LETTER_BOUNDARY_CALLBACK
  115. },
  116. // O Subroutine name: O<some>, …
  117. { match: regex.concat(LETTER_BOUNDARY_RE, SUBROUTINE_NAMED_RE) },
  118. {
  119. begin: SUBROUTINE_NAMED_RE,
  120. 'on:begin': LETTER_BOUNDARY_CALLBACK
  121. },
  122. // Checksum at end of line: *71, *199, …
  123. { match: /\*\s*\d+\s*$/ }
  124. ]
  125. },
  126. {
  127. scope: 'operator', // N Line number: N1, N2, N1020, …
  128. match: /^N\s*\d+/
  129. },
  130. {
  131. scope: 'variable',
  132. match: /-?#\s*\d+/
  133. },
  134. {
  135. scope: 'property', // Physical axes,
  136. variants: [
  137. { match: regex.concat(LETTER_BOUNDARY_RE, AXES_RE, NUMBER_RE) },
  138. {
  139. begin: regex.concat(AXES_RE, NUMBER_RE),
  140. 'on:begin': LETTER_BOUNDARY_CALLBACK
  141. },
  142. ]
  143. },
  144. {
  145. scope: 'params', // Different types of parameters
  146. variants: [
  147. { match: regex.concat(LETTER_BOUNDARY_RE, PARAMETERS_RE, NUMBER_RE) },
  148. {
  149. begin: regex.concat(PARAMETERS_RE, NUMBER_RE),
  150. 'on:begin': LETTER_BOUNDARY_CALLBACK
  151. },
  152. ]
  153. },
  154. ];
  155. return {
  156. name: 'G-code (ISO 6983)',
  157. aliases: [ 'nc' ],
  158. // Some implementations (CNC controls) of G-code are interoperable with uppercase and lowercase letters seamlessly.
  159. // However, most prefer all uppercase and uppercase is customary.
  160. case_insensitive: true,
  161. // TODO: post v12 with the use of look-behind this can be enabled
  162. disableAutodetect: true,
  163. keywords: GCODE_KEYWORDS,
  164. contains: GCODE_CODE
  165. };
  166. }
  167. export { gcode as default };