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