newcommand.js 9.3 KB

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