21314ddf41b5f5b55d66d2d2d5bcd56f95ed16291cab136975ab94b7697875a2db6b9789e43a3121f5631dc736d2e2259c225b910ab4501d80342f614bc4ba 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. export function isFuzzyActionArr(what) {
  6. return (Array.isArray(what));
  7. }
  8. export function isFuzzyAction(what) {
  9. return !isFuzzyActionArr(what);
  10. }
  11. export function isString(what) {
  12. return (typeof what === 'string');
  13. }
  14. export function isIAction(what) {
  15. return !isString(what);
  16. }
  17. // Small helper functions
  18. /**
  19. * Is a string null, undefined, or empty?
  20. */
  21. export function empty(s) {
  22. return (s ? false : true);
  23. }
  24. /**
  25. * Puts a string to lower case if 'ignoreCase' is set.
  26. */
  27. export function fixCase(lexer, str) {
  28. return (lexer.ignoreCase && str ? str.toLowerCase() : str);
  29. }
  30. /**
  31. * Ensures there are no bad characters in a CSS token class.
  32. */
  33. export function sanitize(s) {
  34. return s.replace(/[&<>'"_]/g, '-'); // used on all output token CSS classes
  35. }
  36. // Logging
  37. /**
  38. * Logs a message.
  39. */
  40. export function log(lexer, msg) {
  41. console.log(`${lexer.languageId}: ${msg}`);
  42. }
  43. // Throwing errors
  44. export function createError(lexer, msg) {
  45. return new Error(`${lexer.languageId}: ${msg}`);
  46. }
  47. // Helper functions for rule finding and substitution
  48. /**
  49. * substituteMatches is used on lexer strings and can substitutes predefined patterns:
  50. * $$ => $
  51. * $# => id
  52. * $n => matched entry n
  53. * @attr => contents of lexer[attr]
  54. *
  55. * See documentation for more info
  56. */
  57. export function substituteMatches(lexer, str, id, matches, state) {
  58. const re = /\$((\$)|(#)|(\d\d?)|[sS](\d\d?)|@(\w+))/g;
  59. let stateMatches = null;
  60. return str.replace(re, function (full, sub, dollar, hash, n, s, attr, ofs, total) {
  61. if (!empty(dollar)) {
  62. return '$'; // $$
  63. }
  64. if (!empty(hash)) {
  65. return fixCase(lexer, id); // default $#
  66. }
  67. if (!empty(n) && n < matches.length) {
  68. return fixCase(lexer, matches[n]); // $n
  69. }
  70. if (!empty(attr) && lexer && typeof (lexer[attr]) === 'string') {
  71. return lexer[attr]; //@attribute
  72. }
  73. if (stateMatches === null) { // split state on demand
  74. stateMatches = state.split('.');
  75. stateMatches.unshift(state);
  76. }
  77. if (!empty(s) && s < stateMatches.length) {
  78. return fixCase(lexer, stateMatches[s]); //$Sn
  79. }
  80. return '';
  81. });
  82. }
  83. /**
  84. * Find the tokenizer rules for a specific state (i.e. next action)
  85. */
  86. export function findRules(lexer, inState) {
  87. let state = inState;
  88. while (state && state.length > 0) {
  89. const rules = lexer.tokenizer[state];
  90. if (rules) {
  91. return rules;
  92. }
  93. const idx = state.lastIndexOf('.');
  94. if (idx < 0) {
  95. state = null; // no further parent
  96. }
  97. else {
  98. state = state.substr(0, idx);
  99. }
  100. }
  101. return null;
  102. }
  103. /**
  104. * Is a certain state defined? In contrast to 'findRules' this works on a ILexerMin.
  105. * This is used during compilation where we may know the defined states
  106. * but not yet whether the corresponding rules are correct.
  107. */
  108. export function stateExists(lexer, inState) {
  109. let state = inState;
  110. while (state && state.length > 0) {
  111. const exist = lexer.stateNames[state];
  112. if (exist) {
  113. return true;
  114. }
  115. const idx = state.lastIndexOf('.');
  116. if (idx < 0) {
  117. state = null; // no further parent
  118. }
  119. else {
  120. state = state.substr(0, idx);
  121. }
  122. }
  123. return false;
  124. }