newcommand.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
  2. /* vim: set ts=2 et sw=2 tw=80: */
  3. /*************************************************************
  4. *
  5. * MathJax/extensions/TeX/newcommand.js
  6. *
  7. * Implements the \newcommand, \newenvironment and \def
  8. * macros, and is loaded automatically when needed.
  9. *
  10. * ---------------------------------------------------------------------
  11. *
  12. * Copyright (c) 2009-2018 The MathJax Consortium
  13. *
  14. * Licensed under the Apache License, Version 2.0 (the "License");
  15. * you may not use this file except in compliance with the License.
  16. * You may obtain a copy of the License at
  17. *
  18. * http://www.apache.org/licenses/LICENSE-2.0
  19. *
  20. * Unless required by applicable law or agreed to in writing, software
  21. * distributed under the License is distributed on an "AS IS" BASIS,
  22. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  23. * See the License for the specific language governing permissions and
  24. * limitations under the License.
  25. */
  26. MathJax.Extension["TeX/newcommand"] = {
  27. version: "2.7.5"
  28. };
  29. MathJax.Hub.Register.StartupHook("TeX Jax Ready",function () {
  30. var TEX = MathJax.InputJax.TeX;
  31. var TEXDEF = TEX.Definitions;
  32. TEXDEF.Add({
  33. macros: {
  34. newcommand: 'NewCommand',
  35. renewcommand: 'NewCommand',
  36. newenvironment: 'NewEnvironment',
  37. renewenvironment: 'NewEnvironment',
  38. def: 'MacroDef',
  39. 'let': 'Let'
  40. }
  41. },null,true);
  42. TEX.Parse.Augment({
  43. /*
  44. * Implement \newcommand{\name}[n][default]{...}
  45. */
  46. NewCommand: function (name) {
  47. var cs = this.trimSpaces(this.GetArgument(name)),
  48. n = this.GetBrackets(name),
  49. opt = this.GetBrackets(name),
  50. def = this.GetArgument(name);
  51. if (cs.charAt(0) === "\\") {cs = cs.substr(1)}
  52. if (!cs.match(/^(.|[a-z]+)$/i)) {
  53. TEX.Error(["IllegalControlSequenceName",
  54. "Illegal control sequence name for %1",name]);
  55. }
  56. if (n) {
  57. n = this.trimSpaces(n);
  58. if (!n.match(/^[0-9]+$/)) {
  59. TEX.Error(["IllegalParamNumber",
  60. "Illegal number of parameters specified in %1",name]);
  61. }
  62. }
  63. this.setDef(cs,['Macro',def,n,opt]);
  64. },
  65. /*
  66. * Implement \newenvironment{name}[n][default]{begincmd}{endcmd}
  67. */
  68. NewEnvironment: function (name) {
  69. var env = this.trimSpaces(this.GetArgument(name)),
  70. n = this.GetBrackets(name),
  71. opt = this.GetBrackets(name),
  72. bdef = this.GetArgument(name),
  73. edef = this.GetArgument(name);
  74. if (n) {
  75. n = this.trimSpaces(n);
  76. if (!n.match(/^[0-9]+$/)) {
  77. TEX.Error(["IllegalParamNumber",
  78. "Illegal number of parameters specified in %1",name]);
  79. }
  80. }
  81. this.setEnv(env,['BeginEnv',[null,'EndEnv'],bdef,edef,n,opt]);
  82. },
  83. /*
  84. * Implement \def command
  85. */
  86. MacroDef: function (name) {
  87. var cs = this.GetCSname(name),
  88. params = this.GetTemplate(name,"\\"+cs),
  89. def = this.GetArgument(name);
  90. if (!(params instanceof Array)) {this.setDef(cs,['Macro',def,params])}
  91. else {this.setDef(cs,['MacroWithTemplate',def].concat(params))}
  92. },
  93. /*
  94. * Implements the \let command
  95. */
  96. Let: function (name) {
  97. var cs = this.GetCSname(name), macro;
  98. var c = this.GetNext(); if (c === "=") {this.i++; c = this.GetNext()}
  99. //
  100. // All \let commands create entries in the macros array, but we
  101. // have to look in the various mathchar and delimiter arrays if
  102. // the source isn't a macro already, and attach the data to a
  103. // macro with the proper routine to process it.
  104. //
  105. // A command of the form \let\cs=char produces a macro equivalent
  106. // to \def\cs{char}, which is as close as MathJax can get for this.
  107. // So \let\bgroup={ is possible, but doesn't work as it does in TeX.
  108. //
  109. if (c === "\\") {
  110. name = this.GetCSname(name);
  111. macro = this.csFindMacro(name);
  112. if (!macro) {
  113. if (TEXDEF.mathchar0mi.hasOwnProperty(name)) {macro = ["csMathchar0mi",TEXDEF.mathchar0mi[name]]} else
  114. if (TEXDEF.mathchar0mo.hasOwnProperty(name)) {macro = ["csMathchar0mo",TEXDEF.mathchar0mo[name]]} else
  115. if (TEXDEF.mathchar7.hasOwnProperty(name)) {macro = ["csMathchar7",TEXDEF.mathchar7[name]]} else
  116. if (TEXDEF.delimiter.hasOwnProperty("\\"+name)) {macro = ["csDelimiter",TEXDEF.delimiter["\\"+name]]} else
  117. return;
  118. }
  119. } else {macro = ["Macro",c]; this.i++}
  120. this.setDef(cs,macro);
  121. },
  122. /*
  123. * Get a CS name or give an error
  124. */
  125. GetCSname: function (cmd) {
  126. var c = this.GetNext();
  127. if (c !== "\\") {
  128. TEX.Error(["MissingCS",
  129. "%1 must be followed by a control sequence", cmd])
  130. }
  131. var cs = this.trimSpaces(this.GetArgument(cmd));
  132. return cs.substr(1);
  133. },
  134. /*
  135. * Get a \def parameter template
  136. */
  137. GetTemplate: function (cmd,cs) {
  138. var c, params = [], n = 0;
  139. c = this.GetNext(); var i = this.i;
  140. while (this.i < this.string.length) {
  141. c = this.GetNext();
  142. if (c === '#') {
  143. if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
  144. c = this.string.charAt(++this.i);
  145. if (!c.match(/^[1-9]$/)) {
  146. TEX.Error(["CantUseHash2",
  147. "Illegal use of # in template for %1",cs]);
  148. }
  149. if (parseInt(c) != ++n) {
  150. TEX.Error(["SequentialParam",
  151. "Parameters for %1 must be numbered sequentially",cs]);
  152. }
  153. i = this.i+1;
  154. } else if (c === '{') {
  155. if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
  156. if (params.length > 0) {return [n,params]} else {return n}
  157. }
  158. this.i++;
  159. }
  160. TEX.Error(["MissingReplacementString",
  161. "Missing replacement string for definition of %1",cmd]);
  162. },
  163. /*
  164. * Process a macro with a parameter template
  165. */
  166. MacroWithTemplate: function (name,text,n,params) {
  167. if (n) {
  168. var args = []; this.GetNext();
  169. if (params[0] && !this.MatchParam(params[0])) {
  170. TEX.Error(["MismatchUseDef",
  171. "Use of %1 doesn't match its definition",name]);
  172. }
  173. for (var i = 0; i < n; i++) {args.push(this.GetParameter(name,params[i+1]))}
  174. text = this.SubstituteArgs(args,text);
  175. }
  176. this.string = this.AddArgs(text,this.string.slice(this.i));
  177. this.i = 0;
  178. if (++this.macroCount > TEX.config.MAXMACROS) {
  179. TEX.Error(["MaxMacroSub1",
  180. "MathJax maximum macro substitution count exceeded; " +
  181. "is there a recursive macro call?"]);
  182. }
  183. },
  184. /*
  185. * Process a user-defined environment
  186. */
  187. BeginEnv: function (begin,bdef,edef,n,def) {
  188. if (n) {
  189. var args = [];
  190. if (def != null) {
  191. var optional = this.GetBrackets("\\begin{"+name+"}");
  192. args.push(optional == null ? def : optional);
  193. }
  194. for (var i = args.length; i < n; i++) {args.push(this.GetArgument("\\begin{"+name+"}"))}
  195. bdef = this.SubstituteArgs(args,bdef);
  196. edef = this.SubstituteArgs([],edef); // no args, but get errors for #n in edef
  197. }
  198. this.string = this.AddArgs(bdef,this.string.slice(this.i)); this.i = 0;
  199. return begin;
  200. },
  201. EndEnv: function (begin,bdef,edef,n) {
  202. var end = "\\end{\\end\\"+begin.name+"}"; // special version of \end for after edef
  203. this.string = this.AddArgs(edef,end+this.string.slice(this.i)); this.i = 0;
  204. return null;
  205. },
  206. /*
  207. * Find a single parameter delimited by a trailing template
  208. */
  209. GetParameter: function (name,param) {
  210. if (param == null) {return this.GetArgument(name)}
  211. var i = this.i, j = 0, hasBraces = 0;
  212. while (this.i < this.string.length) {
  213. var c = this.string.charAt(this.i);
  214. if (c === '{') {
  215. if (this.i === i) {hasBraces = 1}
  216. this.GetArgument(name); j = this.i - i;
  217. } else if (this.MatchParam(param)) {
  218. if (hasBraces) {i++; j -= 2}
  219. return this.string.substr(i,j);
  220. } else if (c === "\\") {
  221. this.i++; j++; hasBraces = 0;
  222. var match = this.string.substr(this.i).match(/[a-z]+|./i);
  223. if (match) {this.i += match[0].length; j = this.i - i}
  224. } else {
  225. this.i++; j++; hasBraces = 0;
  226. }
  227. }
  228. TEX.Error(["RunawayArgument","Runaway argument for %1?",name]);
  229. },
  230. /*
  231. * Check if a template is at the current location.
  232. * (The match must be exact, with no spacing differences. TeX is
  233. * a little more forgiving than this about spaces after macro names)
  234. */
  235. MatchParam: function (param) {
  236. if (this.string.substr(this.i,param.length) !== param) {return 0}
  237. if (param.match(/\\[a-z]+$/i) &&
  238. this.string.charAt(this.i+param.length).match(/[a-z]/i)) {return 0}
  239. this.i += param.length;
  240. return 1;
  241. }
  242. });
  243. TEX.Environment = function (name) {
  244. TEXDEF.environment[name] = ['BeginEnv',[null,'EndEnv']].concat([].slice.call(arguments,1));
  245. TEXDEF.environment[name].isUser = true;
  246. }
  247. MathJax.Hub.Startup.signal.Post("TeX newcommand Ready");
  248. });
  249. MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/newcommand.js");