newcommand.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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-2013 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.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]{...}
  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. bdef = this.GetArgument(name),
  72. edef = this.GetArgument(name);
  73. if (n) {
  74. n = this.trimSpaces(n);
  75. if (!n.match(/^[0-9]+$/)) {
  76. TEX.Error(["IllegalParamNumber",
  77. "Illegal number of parameters specified in %1",name]);
  78. }
  79. }
  80. this.setEnv(env,['BeginEnv','EndEnv',bdef,edef,n]);
  81. },
  82. /*
  83. * Implement \def command
  84. */
  85. MacroDef: function (name) {
  86. var cs = this.GetCSname(name),
  87. params = this.GetTemplate(name,"\\"+cs),
  88. def = this.GetArgument(name);
  89. if (!(params instanceof Array)) {this.setDef(cs,['Macro',def,params])}
  90. else {this.setDef(cs,['MacroWithTemplate',def].concat(params))}
  91. },
  92. /*
  93. * Implements the \let command
  94. */
  95. Let: function (name) {
  96. var cs = this.GetCSname(name), macro;
  97. var c = this.GetNext(); if (c === "=") {this.i++; c = this.GetNext()}
  98. //
  99. // All \let commands create entries in the macros array, but we
  100. // have to look in the various mathchar and delimiter arrays if
  101. // the source isn't a macro already, and attach the data to a
  102. // macro with the proper routine to process it.
  103. //
  104. // A command of the form \let\cs=char produces a macro equivalent
  105. // to \def\cs{char}, which is as close as MathJax can get for this.
  106. // So \let\bgroup={ is possible, but doesn't work as it does in TeX.
  107. //
  108. if (c === "\\") {
  109. name = this.GetCSname(name);
  110. macro = this.csFindMacro(name);
  111. if (!macro) {
  112. if (TEXDEF.mathchar0mi[name]) {macro = ["csMathchar0mi",TEXDEF.mathchar0mi[name]]} else
  113. if (TEXDEF.mathchar0mo[name]) {macro = ["csMathchar0mo",TEXDEF.mathchar0mo[name]]} else
  114. if (TEXDEF.mathchar7[name]) {macro = ["csMathchar7",TEXDEF.mathchar7[name]]} else
  115. if (TEXDEF.delimiter["\\"+name] != null) {macro = ["csDelimiter",TEXDEF.delimiter["\\"+name]]}
  116. }
  117. } else {macro = ["Macro",c]; this.i++}
  118. this.setDef(cs,macro);
  119. },
  120. /*
  121. * Routines to set the macro and environment definitions
  122. * (overridden by begingroup to make localized versions)
  123. */
  124. setDef: function (name,value) {value.isUser = true; TEXDEF.macros[name] = value},
  125. setEnv: function (name,value) {value.isUser = true; TEXDEF.environment[name] = value},
  126. /*
  127. * Get a CS name or give an error
  128. */
  129. GetCSname: function (cmd) {
  130. var c = this.GetNext();
  131. if (c !== "\\") {
  132. TEX.Error(["DoubleBackSlash",
  133. "\\ must be followed by a control sequence"])
  134. }
  135. var cs = this.trimSpaces(this.GetArgument(cmd));
  136. return cs.substr(1);
  137. },
  138. /*
  139. * Get a \def parameter template
  140. */
  141. GetTemplate: function (cmd,cs) {
  142. var c, params = [], n = 0;
  143. c = this.GetNext(); var i = this.i;
  144. while (this.i < this.string.length) {
  145. c = this.GetNext();
  146. if (c === '#') {
  147. if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
  148. c = this.string.charAt(++this.i);
  149. if (!c.match(/^[1-9]$/)) {
  150. TEX.Error(["CantUseHash2",
  151. "Illegal use of # in template for %1",cs]);
  152. }
  153. if (parseInt(c) != ++n) {
  154. TEX.Error(["SequentialParam",
  155. "Parameters for %1 must be numbered sequentially",cs]);
  156. }
  157. i = this.i+1;
  158. } else if (c === '{') {
  159. if (i !== this.i) {params[n] = this.string.substr(i,this.i-i)}
  160. if (params.length > 0) {return [n,params]} else {return n}
  161. }
  162. this.i++;
  163. }
  164. TEX.Error(["MissingReplacementString",
  165. "Missing replacement string for definition of %1",cmd]);
  166. },
  167. /*
  168. * Process a macro with a parameter template
  169. */
  170. MacroWithTemplate: function (name,text,n,params) {
  171. if (n) {
  172. var args = []; this.GetNext();
  173. if (params[0] && !this.MatchParam(params[0])) {
  174. TEX.Error(["MismatchUseDef",
  175. "Use of %1 doesn't match its definition",name]);
  176. }
  177. for (var i = 0; i < n; i++) {args.push(this.GetParameter(name,params[i+1]))}
  178. text = this.SubstituteArgs(args,text);
  179. }
  180. this.string = this.AddArgs(text,this.string.slice(this.i));
  181. this.i = 0;
  182. if (++this.macroCount > TEX.config.MAXMACROS) {
  183. TEX.Error(["MaxMacroSub1",
  184. "MathJax maximum macro substitution count exceeded; " +
  185. "is there a recursive macro call?"]);
  186. }
  187. },
  188. /*
  189. * Process a user-defined environment
  190. */
  191. BeginEnv: function (begin,bdef,edef,n) {
  192. if (n) {
  193. var args = [];
  194. for (var i = 0; i < n; i++) {args.push(this.GetArgument("\\begin{"+name+"}"))}
  195. bdef = this.SubstituteArgs(args,bdef);
  196. edef = this.SubstituteArgs(args,edef);
  197. }
  198. begin.edef = edef;
  199. this.string = this.AddArgs(bdef,this.string.slice(this.i)); this.i = 0;
  200. return begin;
  201. },
  202. EndEnv: function (begin,row) {
  203. this.string = this.AddArgs(begin.edef,this.string.slice(this.i)); this.i = 0
  204. return row;
  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. if (this.string.charAt(this.i) === '{') {
  214. if (this.i === i) {hasBraces = 1}
  215. this.GetArgument(name); j = this.i - i;
  216. } else if (this.MatchParam(param)) {
  217. if (hasBraces) {i++; j -= 2}
  218. return this.string.substr(i,j);
  219. } else {
  220. this.i++; j++; hasBraces = 0;
  221. }
  222. }
  223. TEX.Error(["RunawayArgument","Runaway argument for %1?",name]);
  224. },
  225. /*
  226. * Check if a template is at the current location.
  227. * (The match must be exact, with no spacing differences. TeX is
  228. * a little more forgiving than this about spaces after macro names)
  229. */
  230. MatchParam: function (param) {
  231. if (this.string.substr(this.i,param.length) !== param) {return 0}
  232. this.i += param.length;
  233. return 1;
  234. }
  235. });
  236. TEX.Environment = function (name) {
  237. TEXDEF.environment[name] = ['BeginEnv','EndEnv'].concat([].slice.call(arguments,1));
  238. TEXDEF.environment[name].isUser = true;
  239. }
  240. MathJax.Hub.Startup.signal.Post("TeX newcommand Ready");
  241. });
  242. MathJax.Ajax.loadComplete("[MathJax]/extensions/TeX/newcommand.js");