Skip to content

Commit

Permalink
Merge branch 'master' into T246011
Browse files Browse the repository at this point in the history
  • Loading branch information
physikerwelt committed Mar 24, 2020
2 parents 9349dd9 + dbf6911 commit 421f7dd
Show file tree
Hide file tree
Showing 19 changed files with 1,645 additions and 1,652 deletions.
2 changes: 1 addition & 1 deletion .jshintignore → .eslintignore
@@ -1,2 +1,2 @@
node_modules
lib/parser.js
test
30 changes: 30 additions & 0 deletions .eslintrc.yml
@@ -0,0 +1,30 @@
extends:
- 'wikimedia/server'

rules:
array-bracket-spacing:
- off
camelcase:
- error
- properties: never
computed-property-spacing:
- error
- never
indent:
- error
- 4
- SwitchCase: 1
MemberExpression: 'off'
no-multi-spaces:
- off
# defined in eslint-config-wikimedia to avoid breaking IE. Not needed here
no-restricted-syntax:
- off
no-underscore-dangle:
- off
no-unused-vars:
- error
- args: none
space-in-parens:
- error
- never
36 changes: 0 additions & 36 deletions .jshintrc

This file was deleted.

171 changes: 74 additions & 97 deletions lib/ast.js
@@ -1,22 +1,22 @@
// AST type declarations
"use strict";
'use strict';

var typecheck = module.exports.typecheck = function(val, type, self) {
const typecheck = module.exports.typecheck = function (val, type, self) {
switch (type) {
case 'string':
return typeof(val) === type;
case 'self':
return self && self.contains(val);
case 'string':
return typeof (val) === type;
case 'self':
return self && self.contains(val);
}
if (Array.isArray(type)) {
return Array.isArray(val) && val.every(function(elem) {
return Array.isArray(val) && val.every(function (elem) {
return typecheck(elem, type[0], self);
});
}
return type.contains(val);
};
var type2str = function(type) {
if (typeof(type) === 'string') {
const type2str = function (type) {
if (typeof (type) === 'string') {
return type;
}
if (Array.isArray(type)) {
Expand All @@ -28,65 +28,68 @@ var type2str = function(type) {
// "Enum" helper
// vaguely based on:
// https://github.com/rauschma/enums/blob/master/enums.js
var Enum = function(name, fields, proto) {
const Enum = function (name, fields, proto) {
proto = proto || {};
// Non-enumerable properties 'name' and 'prototype'
Object.defineProperty(this, 'name', { value: name });
Object.defineProperty(this, 'prototype', { value: proto });
Object.keys(fields).forEach(function(fname) {
var args = fields[fname].args || [];
var self = this;
Object.keys(fields).forEach(function (fname) {
const args = fields[fname].args || [];
const self = this;
this[fname] = function EnumField() {
if (!(this instanceof EnumField)) {
var o = Object.create(EnumField.prototype);
const o = Object.create(EnumField.prototype);
o.constructor = EnumField;
EnumField.apply(o, arguments);
return o;
}
this.name = fname;
console.assert(arguments.length === args.length,
"Wrong # of args for " + name + "." + fname);
for (var i=0; i<args.length; i++) {
'Wrong # of args for ' + name + '.' + fname);
for (let i = 0; i < args.length; i++) {
console.assert(typecheck(arguments[i], args[i], self),
"Argument " + i + " of " + name + "." + fname +
" should be " + type2str(args[i]));
'Argument ' + i + ' of ' + name + '.' + fname +
' should be ' + type2str(args[i]));
this[i] = arguments[i];
}
this.length = args.length;
};
this[fname].prototype = Object.create(proto);
this[fname].prototype.toString = function() {
var stringify = function(type, val) {
if (type==='string') {
this[fname].prototype.toString = function () {
const stringify = function (type, val) {
if (type === 'string') {
return JSON.stringify(val);
} else if (Array.isArray(type)) {
return '[' + val.map(function(v) {
return '[' + val.map(function (v) {
return stringify(type[0], v);
}).join(',') + ']';
}
return val.toString();
};
return fname + '(' + args.map(function(type, i) {
return fname + '(' + args.map(function (type, i) {
return stringify(type, this[i]);
}.bind(this)).join(',') + ')';
};
}.bind(this));
};
Enum.prototype.contains = function(sym) {
Enum.prototype.contains = function (sym) {
// eslint-disable-next-line no-prototype-builtins
return sym.name && this.hasOwnProperty(sym.name) &&
sym instanceof this[sym.name];
};
Enum.prototype.defineVisitor = function(visitorName, o, numArgs) {
var self = this;
Enum.prototype.defineVisitor = function (visitorName, o, numArgs) {
const self = this;
numArgs = numArgs || 0;
console.assert(Object.keys(o).length === Object.keys(self).length,
"Missing cases in " + self.name + ". Expected:\n" +
Object.keys(self).sort() + " but got: \n" +
'Missing cases in ' + self.name + '. Expected:\n' +
Object.keys(self).sort() + ' but got: \n' +
Object.keys(o).sort());
Object.keys(o).forEach(function(fname) {
self[fname].prototype[visitorName] = function() {
var args = [];
for (var i=0; i<numArgs; i++) { args.push(arguments[i]); }
Object.keys(o).forEach(function (fname) {
self[fname].prototype[visitorName] = function () {
const args = [];
for (let i = 0; i < numArgs; i++) {
args.push(arguments[i]);
}
args.push.apply(args, this);
return o[fname].apply(this, args);
};
Expand All @@ -95,89 +98,63 @@ Enum.prototype.defineVisitor = function(visitorName, o, numArgs) {

// Actual AST starts here.

var FontForce = module.exports.FontForce = new Enum( 'FontForce', {
const FontForce = module.exports.FontForce = new Enum('FontForce', {
IT: {},
RM: {}
});

var FontClass = module.exports.FontClass = new Enum( 'FontClass', {
IT: {}, /* IT default, may be forced to be RM */
RM: {}, /* RM default, may be forced to be IT */
UF: {}, /* not affected by IT/RM setting */
RTI: {}, /* RM - any, IT - not available in HTML */
UFH: {} /* in TeX UF, in HTML RM */
const RenderT = module.exports.RenderT = new Enum('RenderT', {
TEX_ONLY: { args: ['string'] }
});

var MathClass = module.exports.MathClass = new Enum( 'MathClass', {
MN: {},
MI: {},
MO: {}
});

var RenderT = module.exports.RenderT = new Enum( 'RenderT', {
TEX_ONLY: { args: ['string'] }
}/*, {
// demonstration of doing dispatch on the enumerated type with a
// switch statement. it turns out to be more self-documenting if we
// use Enum.defineVisitor() for this instead (see render.js)
tex_part: function() {
switch(this.constructor) {
case RenderT.HTMLABLE:
case RenderT.HTMLABLEM:
case RenderT.HTMLABLEC:
case RenderT.MHTMLABLEC:
return this[1];
case RenderT.HTMLABLE_BIG:
case RenderT.TEX_ONLY:
return this[0];
}
}
}*/);

var Tex = module.exports.Tex = new Enum( 'Tex', {
LITERAL: { args: [ RenderT ] }, // contents
CURLY: { args: [ ['self'] ] }, // expr
DOLLAR: { args: [ ['self'] ] }, // expr
FQ: { args: [ 'self', 'self', 'self' ] }, // base, down, up
DQ: { args: [ 'self', 'self' ] }, // base, down
UQ: { args: [ 'self', 'self' ] }, // base, up
FQN: { args: [ 'self', 'self' ] }, // down, up (no base)
DQN: { args: [ 'self' ] }, // down (no base)
UQN: { args: [ 'self' ] }, // up (no base)
LR: { args: [ RenderT, RenderT, [ 'self' ] ] }, // left, right, expr
BOX: { args: [ 'string', 'string' ] }, // name, contents
BIG: { args: [ 'string', RenderT ] }, // name, contents
FUN1: { args: [ 'string', 'self' ] }, // name, expr
MHCHEM: { args: [ 'string', 'self' ] }, // name, expr
CHEM_WORD:{ args: [ 'self', 'self' ] }, // name, expr
CHEM_FUN2u:{ args: [ 'string', 'self', 'self' ] }, // name, expr
FUN1nb: { args: [ 'string', 'self' ] }, // name, expr
FUN2: { args: [ 'string', 'self', 'self' ] },
FUN2nb: { args: [ 'string', 'self', 'self' ] },
INFIX: { args: [ 'string', ['self'], ['self'] ] },
FUN2sq: { args: [ 'string', 'self', 'self' ] },
MATRIX: { args: [ 'string', [ [ [ 'self' ] ] ] ] },
DECLh: { args: [ 'string', FontForce, [ 'self' ] ] }
const Tex = module.exports.Tex = new Enum('Tex', {
LITERAL: { args: [RenderT] }, // contents
CURLY: { args: [['self']] }, // expr
DOLLAR: { args: [['self']] }, // expr
FQ: { args: ['self', 'self', 'self'] }, // base, down, up
DQ: { args: ['self', 'self'] }, // base, down
UQ: { args: ['self', 'self'] }, // base, up
FQN: { args: ['self', 'self'] }, // down, up (no base)
DQN: { args: ['self'] }, // down (no base)
UQN: { args: ['self'] }, // up (no base)
LR: { args: [RenderT, RenderT, ['self']] }, // left, right, expr
BOX: { args: ['string', 'string'] }, // name, contents
BIG: { args: ['string', RenderT] }, // name, contents
FUN1: { args: ['string', 'self'] }, // name, expr
MHCHEM: { args: ['string', 'self'] }, // name, expr
CHEM_WORD: { args: ['self', 'self'] }, // name, expr
CHEM_FUN2u: { args: ['string', 'self', 'self'] }, // name, expr
FUN1nb: { args: ['string', 'self'] }, // name, expr
FUN2: { args: ['string', 'self', 'self'] },
FUN2nb: { args: ['string', 'self', 'self'] },
INFIX: { args: ['string', ['self'], ['self']] },
FUN2sq: { args: ['string', 'self', 'self'] },
MATRIX: { args: ['string', [[['self']]]] },
DECLh: { args: ['string', FontForce, ['self']] }
});

// Linked List of Tex, useful for efficient "append to head" operations.
var nil = {};
var LList = module.exports.LList = function LList(head, tail) {
const nil = {};
const LList = module.exports.LList = function LList(head, tail) {
if (!(this instanceof LList)) {
return new LList(head, tail);
}
if (head === nil && tail === nil) { return; /* empty list singleton */ }
if (!tail) { tail = LList.EMPTY; }
if (head === nil && tail === nil) {
return; /* empty list singleton */
}
if (!tail) {
tail = LList.EMPTY;
}
console.assert(typecheck(head, Tex));
console.assert(tail instanceof LList);
this.head = head;
this.tail = tail;
};
LList.EMPTY = new LList(nil, nil);
LList.prototype.toArray = function() {
console.assert(this instanceof LList, "not a LList");
var arr = [];
for (var l = this ; l !== LList.EMPTY; l = l.tail) {
LList.prototype.toArray = function () {
console.assert(this instanceof LList, 'not a LList');
const arr = [];
for (let l = this; l !== LList.EMPTY; l = l.tail) {
arr.push(l.head);
}
return arr;
Expand Down

0 comments on commit 421f7dd

Please sign in to comment.