newcommand.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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-2017 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.2"
  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[name]) {macro = ["csMathchar0mi",TEXDEF.mathchar0mi[name]]} else
  114. if (TEXDEF.mathchar0mo[name]) {macro = ["csMathchar0mo",TEXDEF.mathchar0mo[name]]} else
  115. if (TEXDEF.mathchar7[name]) {macro = ["csMathchar7",TEXDEF.mathchar7[name]]} else
  116. if (TEXDEF.delimiter["\\"+name] != null) {macro = ["csDelimiter",TEXDEF.delimiter["\\"+name]]} else
  117. return;
  118. }
  119. } else {macro = ["Macro",c]; this.i++}
  120. this.setDef(cs,macro);
  121. },
  122. /*
  123. * Routines to set the macro and environment definitions
  124. * (overridden by begingroup to make localized versions)
  125. */
  126. setDef: function (name,value) {value.isUser = true; TEXDEF.macros[name] = value},
  127. setEnv: function (name,value) {value.isUser = true; TEXDEF.environment[name] = value},
  128. /*
  129. * Get a CS name or give an error
  130. */
  131. GetCSname: function (cmd) {
  132. var c = this.GetNext();
  133. if (c !== "\\") {
  134. TEX.Error(["MissingCS",
  135. "%1 must be followed by a control sequence", cmd])
  136. }
  137. var cs = this.trimSpaces(this.GetArgument(cmd));
  138. return cs.substr(1);
  139. },
  140. /*
  141. * Get a \def parameter template
  142. */
  143. GetTemplate: function (cmd,cs) {
  144. var c, params = [], n = 0;
  145. c = this.GetNext(); var i = this.i;
  146. while (this.i < this.string.length) {
  147. c = this.GetNext();
  148. if (c === '#') {
  149. if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
  150. c = this.string.charAt(++this.i);
  151. if (!c.match(/^[1-9]$/)) {
  152. TEX.Error(["CantUseHash2",
  153. "Illegal use of # in template for %1",cs]);
  154. }
  155. if (parseInt(c) != ++n) {
  156. TEX.Error(["SequentialParam",
  157. "Parameters for %1 must be numbered sequentially",cs]);
  158. }
  159. i = this.i+1;
  160. } else if (c === '{') {
  161. if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
  162. if (params.length > 0) {return [n,params]} else {return n}
  163. }
  164. this.i++;
  165. }
  166. TEX.Error(["MissingReplacementString",
  167. "Missing replacement string for definition of %1",cmd]);
  168. },
  169. /*
  170. * Process a macro with a parameter template
  171. */
  172. MacroWithTemplate: function (name,text,n,params) {
  173. if (n) {
  174. var args = []; this.GetNext();
  175. if (params[0] && !this.MatchParam(params[0])) {
  176. TEX.Error(["MismatchUseDef",
  177. "Use of %1 doesn't match its definition",name]);
  178. }
  179. for (var i = 0; i < n; i++) {args.push(this.GetParameter(name,params[i+1]))}
  180. text = this.SubstituteArgs(args,text);
  181. }
  182. this.string = this.AddArgs(text,this.string.slice(this.i));
  183. this.i = 0;
  184. if (++this.macroCount > TEX.config.MAXMACROS) {
  185. TEX.Error(["MaxMacroSub1",
  186. "MathJax maximum macro substitution count exceeded; " +
  187. "is there a recursive macro call?"]);
  188. }
  189. },
  190. /*
  191. * Process a user-defined environment
  192. */
  193. BeginEnv: function (begin,bdef,edef,n,def) {
  194. if (n) {
  195. var args = [];
  196. if (def != null) {
  197. var optional = this.GetBrackets("\\begin{"+name+"}");
  198. args.push(optional == null ? def : optional);
  199. }
  200. for (var i = args.length; i < n; i++) {args.push(this.GetArgument("\\begin{"+name+"}"))}
  201. bdef = this.SubstituteArgs(args,bdef);
  202. edef = this.SubstituteArgs([],edef); // no args, but get errors for #n in edef
  203. }
  204. this.string = this.AddArgs(bdef,this.string.slice(this.i)); this.i = 0;
  205. return begin;
  206. },
  207. EndEnv: function (begin,bdef,edef,n) {
  208. var end = "\\end{\\end\\"+begin.name+"}"; // special version of \end for after edef
  209. this.string = this.AddArgs(edef,end+this.string.slice(this.i)); this.i = 0;
  210. return null;
  211. },
  212. /*
  213. * Find a single parameter delimited by a trailing template
  214. */
  215. GetParameter: function (name,param) {
  216. if (param == null) {return this.GetArgument(name)}
  217. var i = this.i, j = 0, hasBraces = 0;
  218. while (this.i < this.string.length) {
  219. var c = this.string.charAt(this.i);
  220. if (c === '{') {
  221. if (this.i === i) {hasBraces = 1}
  222. this.GetArgument(name); j = this.i - i;
  223. } else if (this.MatchParam(param)) {
  224. if (hasBraces) {i++; j -= 2}
  225. return this.string.substr(i,j);
  226. } else if (c === "\\") {
  227. this.i++; j++; hasBraces = 0;
  228. var match = this.string.substr(this.i).match(/[a-z]+|./i);
  229. if (match) {this.i += match[0].length; j = this.i - i}
  230. } else {
  231. this.i++; j++; hasBraces = 0;
  232. }
  233. }
  234. TEX.Error(["RunawayArgument","Runaway argument for %1?",name]);
  235. },
  236. /*
  237. * Check if a template is at the current location.
  238. * (The match must be exact, with no spacing differences. TeX is
  239. * a little more forgiving than this about spaces after macro names)
  240. */
  241. MatchParam: function (param) {
  242. if (this.string.substr(this.i,param.length) !== param) {return 0}
  243. if (param.match(/\\[a-z]+$/i) &&
  244. this.string.charAt(this.i+param.length).match(/[a-z]/i)) {return 0}
  245. this.i += param.length;
  246. return 1;
  247. }
  248. });
  249. TEX.Environment = function (name) {
  250. TEXDEF.environment[name] = ['BeginEnv',[null,'EndEnv']].concat([].slice.call(arguments,1));
  251. TEXDEF.environment[name].isUser = true;
  252. }
  253. MathJax.Hub.Startup.signal.Post("TeX newcommand Ready");
  254. });
  255. MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/newcommand.js");