From dfbba980abc322bf65a7b97f1a7bd508d4afd5d5 Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Tue, 5 Apr 2016 16:48:21 -0400 Subject: [PATCH 01/14] Added a NodeJS program to find occurences of math mode. --- texvcjs_readinfile.js | 151 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100755 texvcjs_readinfile.js diff --git a/texvcjs_readinfile.js b/texvcjs_readinfile.js new file mode 100755 index 0000000..b2ecea9 --- /dev/null +++ b/texvcjs_readinfile.js @@ -0,0 +1,151 @@ +#!/usr/bin/env node +var mathStart = {"\\[":"\\]","\\(":"\\)","$":"$","$$":"$$","\\begin{equation}":"\\end{equation}","\\begin{equation*}":"\\end{equation*}","\\begin{align}":"\\end{align}","\\begin{align*}":"\\end{align*}","\\begin{multline}":"\\end{multline}","\\begin{multline*}":"\\end{multline*}"}; +var mathEnd = ["\\hbox{", "\\mbox{", "\\text{"]; + +function escapeRegExp(str) { + return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); + +} + +function getLine(res, char) { + return (res.substring(0, char).match(/\n/g)||[]).length + 1; + +} + +function findFirst(res, delim, start) { + i = res.indexOf(delim, start); + if (delim == "$" || delim == "\\[" || delim == "\\(") { + if (i == -1) { + return -1; + } + else if (i != 0 && res.charAt(i - 1) == "\\") { + return findFirst(res, delim, i + delim.length); + } + } + return i; +} + +function firstDelim(res, enter) { + if (enter) { + var min = "\\["; + var list = mathStart; + } + else { + var min = "\\hbox{"; + var list = mathEnd; + } + for (var key in list) { + var i = findFirst(res, key, 0); + if (i != -1 && (i <= findFirst(res, min, 0) || res.indexOf(min) == -1)) { + min = key; + } + } + return min; +} + +function startsWith(string, substring) { + return string.lastIndexOf(substring, 0) === 0; + +} + +function doesExit(string) { + for (var i = 0; i < mathEnd.length; i++) { + if (startsWith(string, mathEnd[i])) { + return true; + } + } + return false; +} + +function doesEnter(string) { + for (var key in mathStart) { + if (startsWith(string, key)) { + return key; + } + } + return ""; +} + +function parseMath(res, start, ranges) { + var delim = firstDelim(res, true); + var i = delim.length; + var begin = start + i; + while (i < res.length) { + if (startsWith(res.substring(i), "\\$")) { + i++; + } + else if (startsWith(res.substring(i), "\\\\]")) { + i += 2; + } + else if (startsWith(res.substring(i), "\\\\)")) { + i += 2; + } + else { + if (doesExit(res.substring(i))) { + if (begin != start + i) { + ranges.push([begin, start + i]); + } + i += parseNonMath(res.substring(i), start + i, ranges); + begin = start + i; + } + if (startsWith(res.substring(i), mathStart[delim])) { + if (begin != start + i) { + ranges.push([begin, start + i]); + } + return i + mathStart[delim].length - 1; + } + } + i++; + } + return i; +} + +function parseNonMath(res, start, ranges) { + var delim = firstDelim(res, false); + if (!startsWith(res, delim)) { + delim = ""; + } + var level = 0; + var i = delim.length; + while (i < res.length) { + if (startsWith(res.substring(i), "\\$")) { + i++; + } + else if (startsWith(res.substring(i), "\\\\[")) { + i += 2; + } + else if (startsWith(res.substring(i), "\\\\(")) { + i += 2; + } + else if (doesEnter(res.substring(i))) { + i += parseMath(res.substring(i), start + i, ranges); + } + else if (res.charAt(i) == "{") { + level++; + } + else if (res.charAt(i) == "}") { + if (level == 0 && delim != "") { + i++; + return i; + } + else { + level--; + } + } + i++; + } + return i; +} + +function find_ranges(res) { + var ranges = []; + parseNonMath(res, 0, ranges); + var mathModes = []; + for (var index = 0; index < ranges.length; index++) { + mathModes.push(res.substring(ranges[index][0], ranges[index][1])); + } + return mathModes; +} + +//var res = "This is a test $a$ and $b$"; +//console.log(find_ranges(res)); \ No newline at end of file From 75c52be4b02ac02af84fb84154690667c40a9e4c Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Tue, 5 Apr 2016 17:02:38 -0400 Subject: [PATCH 02/14] Added a NodeJS program to find occurrences of math mode. --- texvcjs_readinfile.js | 2 +- texvcjs_readinfile.js~ | 0 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 texvcjs_readinfile.js create mode 100644 texvcjs_readinfile.js~ diff --git a/texvcjs_readinfile.js b/texvcjs_readinfile.js old mode 100755 new mode 100644 index b2ecea9..c898bda --- a/texvcjs_readinfile.js +++ b/texvcjs_readinfile.js @@ -148,4 +148,4 @@ function find_ranges(res) { } //var res = "This is a test $a$ and $b$"; -//console.log(find_ranges(res)); \ No newline at end of file +//console.log(find_ranges(res)); diff --git a/texvcjs_readinfile.js~ b/texvcjs_readinfile.js~ new file mode 100644 index 0000000..e69de29 From 5b652240584fc4502b793215ae33a6566d70036b Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Tue, 12 Apr 2016 17:48:52 -0400 Subject: [PATCH 03/14] Corrected the majority of the lint errors in mathmode.js (originally texvcjs_readinfile.js). Also added a test case for mathmode.js under test/mathmodetest.js. --- .idea/.name | 1 + .idea/compiler.xml | 22 ++++ .idea/copyright/profiles_settings.xml | 3 + .idea/encodings.xml | 6 + .idea/misc.xml | 36 ++++++ .idea/modules.xml | 8 ++ mathmode.js | 149 +++++++++++++++++++++++++ test/index.js | 0 test/mathmodetest.js | 27 +++++ texvcjs_readinfile.js | 151 -------------------------- texvcjs_readinfile.js~ | 0 11 files changed, 252 insertions(+), 151 deletions(-) create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100755 mathmode.js mode change 100644 => 100755 test/index.js create mode 100644 test/mathmodetest.js delete mode 100644 texvcjs_readinfile.js delete mode 100644 texvcjs_readinfile.js~ diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..2239e9e --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +texer \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f14b1b5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..31e7272 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/mathmode.js b/mathmode.js new file mode 100755 index 0000000..492ed62 --- /dev/null +++ b/mathmode.js @@ -0,0 +1,149 @@ +#!/usr/bin/env node +"use strict"; + +var mathMode = {'\\[' : '\\]', + '\\(' : '\\)', + '$' : '$', + '$$' : '$$', + '\\begin{equation}' : '\\end{equation}', + '\\begin{equation*}' : '\\end{equation*}', + '\\begin{align}' : '\\end{align}', + '\\begin{align*}' : '\\end{align*}', + '\\begin{multline}' : '\\end{multline}', + '\\begin{multline*}' : '\\end{multline*}'}; + +var textMode = ['\\hbox{', '\\mbox{', '\\text{']; + +function findFirst(res, delim, start) { + var i = res.indexOf(delim, start); + if (delim === '$' || delim === '\\[' || delim === '\\(') { + if (i === -1) { + return -1; + } + else if (i !== 0 && res.charAt(i - 1) === '\\') { + return findFirst(res, delim, i + delim.length); + } + } + return i; +} + +function firstDelim(res, enter) { + var min, list; + if (enter) { + min = '\\['; + list = mathMode; + } + else { + min = '\\hbox{'; + list = textMode; + } + for (var key in list) { + var i = findFirst(res, key, 0); + if (i !== -1 && (i <= findFirst(res, min, 0) || res.indexOf(min) === -1)) { + min = key; + } + } + return min; +} + +function startsWith(string, substring) { + return string.lastIndexOf(substring, 0) === 0; + +} + +function doesExit(string) { + for (var i = 0; i < textMode.length; i++) { + if (startsWith(string, textMode[i])) { + return true; + } + } + return false; +} + +function doesEnter(string) { + for (var key in mathMode) { + if (startsWith(string, key)) { + return key; + } + } + return ''; +} + +function skip(res) { + if (startsWith(res, '\\$')) { + return 1; + } + else if (startsWith(res, '\\\\[')) { + return 2; + } + else if (startsWith(res, '\\\\(')) { + return 2; + } + return 0; +} + +function parseMath(res, start, ranges) { + var delim = firstDelim(res, true); + var i = delim.length; + var begin = start + i; + while (i < res.length) { + i += skip(res.substring(i)); + if (doesExit(res.substring(i))) { + if (begin !== start + i) { + ranges.push([begin, start + i]); + } + i += parseNonMath(res.substring(i), start + i, ranges); + begin = start + i; + } + if (startsWith(res.substring(i), mathMode[delim])) { + if (begin !== start + i) { + ranges.push([begin, start + i]); + } + return i + mathMode[delim].length - 1; + } + i++; + } + return i; +} + +function parseNonMath(res, start, ranges) { + var delim = firstDelim(res, false); + if (!startsWith(res, delim)) { + delim = ''; + } + var level = 0; + var i = delim.length; + while (i < res.length) { + i += skip(res.substring(i)); + if (doesEnter(res.substring(i))) { + i += parseMath(res.substring(i), start + i, ranges); + } + else if (res.charAt(i) === '{') { + level++; + } + else if (res.charAt(i) === '}') { + if (level === 0 && delim !== '') { + i++; + return i; + } + else { + level--; + } + } + i++; + } + return i; +} + +function findRanges(res) { + var ranges = []; + parseNonMath(res, 0, ranges); + var mathSections = []; + for (var index = 0; index < ranges.length; index++) { + mathSections.push(res.substring(ranges[index][0], ranges[index][1])); + } + return mathSections; +} + +//var texer = require('./'); +console.log(findRanges('$a$ and $b$')); diff --git a/test/index.js b/test/index.js old mode 100644 new mode 100755 diff --git a/test/mathmodetest.js b/test/mathmodetest.js new file mode 100644 index 0000000..2793d4a --- /dev/null +++ b/test/mathmodetest.js @@ -0,0 +1,27 @@ +"use strict"; +var assert = require('assert'); +var mathFinder = require('../mathmode.js'); +var testcases = [ + {input: '', options: '', out: ''}, + { + input: 'This is a test', + options: {}, + out: 'This is a test' + } + { + input : '$a$ and $b$'; + options: {}; + out: ['a', 'b'] + } +]; + +describe('Index', function () { + testcases.forEach(function (tc) { + var input = tc.input; + var options = tc.options; + var output = tc.out; + it('should correctly replace ' + JSON.stringify(input), function () { + assert.deepEqual(mathmode.findRanges(input), output); + }); + }); +}); \ No newline at end of file diff --git a/texvcjs_readinfile.js b/texvcjs_readinfile.js deleted file mode 100644 index c898bda..0000000 --- a/texvcjs_readinfile.js +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env node -var mathStart = {"\\[":"\\]","\\(":"\\)","$":"$","$$":"$$","\\begin{equation}":"\\end{equation}","\\begin{equation*}":"\\end{equation*}","\\begin{align}":"\\end{align}","\\begin{align*}":"\\end{align*}","\\begin{multline}":"\\end{multline}","\\begin{multline*}":"\\end{multline*}"}; -var mathEnd = ["\\hbox{", "\\mbox{", "\\text{"]; - -function escapeRegExp(str) { - return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); - -} - -function getLine(res, char) { - return (res.substring(0, char).match(/\n/g)||[]).length + 1; - -} - -function findFirst(res, delim, start) { - i = res.indexOf(delim, start); - if (delim == "$" || delim == "\\[" || delim == "\\(") { - if (i == -1) { - return -1; - } - else if (i != 0 && res.charAt(i - 1) == "\\") { - return findFirst(res, delim, i + delim.length); - } - } - return i; -} - -function firstDelim(res, enter) { - if (enter) { - var min = "\\["; - var list = mathStart; - } - else { - var min = "\\hbox{"; - var list = mathEnd; - } - for (var key in list) { - var i = findFirst(res, key, 0); - if (i != -1 && (i <= findFirst(res, min, 0) || res.indexOf(min) == -1)) { - min = key; - } - } - return min; -} - -function startsWith(string, substring) { - return string.lastIndexOf(substring, 0) === 0; - -} - -function doesExit(string) { - for (var i = 0; i < mathEnd.length; i++) { - if (startsWith(string, mathEnd[i])) { - return true; - } - } - return false; -} - -function doesEnter(string) { - for (var key in mathStart) { - if (startsWith(string, key)) { - return key; - } - } - return ""; -} - -function parseMath(res, start, ranges) { - var delim = firstDelim(res, true); - var i = delim.length; - var begin = start + i; - while (i < res.length) { - if (startsWith(res.substring(i), "\\$")) { - i++; - } - else if (startsWith(res.substring(i), "\\\\]")) { - i += 2; - } - else if (startsWith(res.substring(i), "\\\\)")) { - i += 2; - } - else { - if (doesExit(res.substring(i))) { - if (begin != start + i) { - ranges.push([begin, start + i]); - } - i += parseNonMath(res.substring(i), start + i, ranges); - begin = start + i; - } - if (startsWith(res.substring(i), mathStart[delim])) { - if (begin != start + i) { - ranges.push([begin, start + i]); - } - return i + mathStart[delim].length - 1; - } - } - i++; - } - return i; -} - -function parseNonMath(res, start, ranges) { - var delim = firstDelim(res, false); - if (!startsWith(res, delim)) { - delim = ""; - } - var level = 0; - var i = delim.length; - while (i < res.length) { - if (startsWith(res.substring(i), "\\$")) { - i++; - } - else if (startsWith(res.substring(i), "\\\\[")) { - i += 2; - } - else if (startsWith(res.substring(i), "\\\\(")) { - i += 2; - } - else if (doesEnter(res.substring(i))) { - i += parseMath(res.substring(i), start + i, ranges); - } - else if (res.charAt(i) == "{") { - level++; - } - else if (res.charAt(i) == "}") { - if (level == 0 && delim != "") { - i++; - return i; - } - else { - level--; - } - } - i++; - } - return i; -} - -function find_ranges(res) { - var ranges = []; - parseNonMath(res, 0, ranges); - var mathModes = []; - for (var index = 0; index < ranges.length; index++) { - mathModes.push(res.substring(ranges[index][0], ranges[index][1])); - } - return mathModes; -} - -//var res = "This is a test $a$ and $b$"; -//console.log(find_ranges(res)); diff --git a/texvcjs_readinfile.js~ b/texvcjs_readinfile.js~ deleted file mode 100644 index e69de29..0000000 From 6bec8f6376935a794559097a3d8af954e1ebd78e Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Tue, 19 Apr 2016 16:28:09 -0400 Subject: [PATCH 04/14] Fixed the test cases for mathmode.js. --- .jshintrc | 2 +- mathmode.js => lib/mathmode.js | 7 ++----- test/mathmodetest.js | 14 +++++++------- 3 files changed, 10 insertions(+), 13 deletions(-) rename mathmode.js => lib/mathmode.js (97%) mode change 100644 => 100755 test/mathmodetest.js diff --git a/.jshintrc b/.jshintrc index 3823eeb..f2dce6a 100644 --- a/.jshintrc +++ b/.jshintrc @@ -14,7 +14,7 @@ "curly": true, "eqeqeq": true, "immed": true, - "latedef": true, + "latedef": false, "newcap": true, "noarg": true, "noempty": true, diff --git a/mathmode.js b/lib/mathmode.js similarity index 97% rename from mathmode.js rename to lib/mathmode.js index 492ed62..0a3b510 100755 --- a/mathmode.js +++ b/lib/mathmode.js @@ -135,7 +135,7 @@ function parseNonMath(res, start, ranges) { return i; } -function findRanges(res) { +module.exports.findRanges = function (res) { var ranges = []; parseNonMath(res, 0, ranges); var mathSections = []; @@ -143,7 +143,4 @@ function findRanges(res) { mathSections.push(res.substring(ranges[index][0], ranges[index][1])); } return mathSections; -} - -//var texer = require('./'); -console.log(findRanges('$a$ and $b$')); +}; \ No newline at end of file diff --git a/test/mathmodetest.js b/test/mathmodetest.js old mode 100644 new mode 100755 index 2793d4a..d236559 --- a/test/mathmodetest.js +++ b/test/mathmodetest.js @@ -1,16 +1,16 @@ "use strict"; var assert = require('assert'); -var mathFinder = require('../mathmode.js'); +var mathFinder = require('../lib/mathmode.js'); var testcases = [ - {input: '', options: '', out: ''}, + {input: '', options: '', out: []}, { input: 'This is a test', options: {}, - out: 'This is a test' - } + out: [] + }, { - input : '$a$ and $b$'; - options: {}; + input : '$a$ and $b$', + options: {}, out: ['a', 'b'] } ]; @@ -21,7 +21,7 @@ describe('Index', function () { var options = tc.options; var output = tc.out; it('should correctly replace ' + JSON.stringify(input), function () { - assert.deepEqual(mathmode.findRanges(input), output); + assert.deepEqual(mathFinder.findRanges(input), output); }); }); }); \ No newline at end of file From 12556b297513b49b496768537d734a6eab53540f Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Tue, 19 Apr 2016 16:38:53 -0400 Subject: [PATCH 05/14] Added more test cases to mathmodetest.js. --- lib/mathmode.js | 6 +++--- test/mathmodetest.js | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/mathmode.js b/lib/mathmode.js index 0a3b510..de8890f 100755 --- a/lib/mathmode.js +++ b/lib/mathmode.js @@ -71,13 +71,13 @@ function doesEnter(string) { function skip(res) { if (startsWith(res, '\\$')) { - return 1; + return 2; } else if (startsWith(res, '\\\\[')) { - return 2; + return 3; } else if (startsWith(res, '\\\\(')) { - return 2; + return 3; } return 0; } diff --git a/test/mathmodetest.js b/test/mathmodetest.js index d236559..d0a3eb7 100755 --- a/test/mathmodetest.js +++ b/test/mathmodetest.js @@ -9,9 +9,19 @@ var testcases = [ out: [] }, { - input : '$a$ and $b$', + input: '$a$ and $b$', options: {}, out: ['a', 'b'] + }, + { + input: '$c\\hbox{and $d$}$', + options: {}, + out: ['c', 'd'] + }, + { + input: '\\$Not this\\$ $This$', + options: {}, + out: ['This'] } ]; From 75b25b9272d481aee78e77074814695f945f237c Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Tue, 19 Apr 2016 18:25:01 -0400 Subject: [PATCH 06/14] Removed unnecessary code from mathmode.js and added more coverage to tests. --- lib/mathmode.js | 2 -- test/mathmodetest.js | 7 ++++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/mathmode.js b/lib/mathmode.js index de8890f..12250b6 100755 --- a/lib/mathmode.js +++ b/lib/mathmode.js @@ -1,4 +1,3 @@ -#!/usr/bin/env node "use strict"; var mathMode = {'\\[' : '\\]', @@ -103,7 +102,6 @@ function parseMath(res, start, ranges) { } i++; } - return i; } function parseNonMath(res, start, ranges) { diff --git a/test/mathmodetest.js b/test/mathmodetest.js index d0a3eb7..22d0512 100755 --- a/test/mathmodetest.js +++ b/test/mathmodetest.js @@ -19,9 +19,14 @@ var testcases = [ out: ['c', 'd'] }, { - input: '\\$Not this\\$ $This$', + input: '\\$Not this\\$ \\\\[or this\\\\] \\\\(or even this\\\\) $This$', options: {}, out: ['This'] + }, + { + input: '$math mode\\hbox{not math {still not math}}$', + options: {}, + out: ['math mode'] } ]; From e1ac2dec62cbb3bc386eda8904b3144d088441d4 Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Tue, 19 Apr 2016 18:42:53 -0400 Subject: [PATCH 07/14] Cleaned up some indentation inconsistencies, and removed more redundant code. --- lib/mathmode.js | 47 +++++++++++++++++--------------------------- test/mathmodetest.js | 4 ++-- 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/lib/mathmode.js b/lib/mathmode.js index 12250b6..c8c5215 100755 --- a/lib/mathmode.js +++ b/lib/mathmode.js @@ -1,30 +1,19 @@ "use strict"; var mathMode = {'\\[' : '\\]', - '\\(' : '\\)', - '$' : '$', - '$$' : '$$', - '\\begin{equation}' : '\\end{equation}', - '\\begin{equation*}' : '\\end{equation*}', - '\\begin{align}' : '\\end{align}', - '\\begin{align*}' : '\\end{align*}', - '\\begin{multline}' : '\\end{multline}', - '\\begin{multline*}' : '\\end{multline*}'}; + '\\(' : '\\)', + '$' : '$', + '$$' : '$$', + '\\begin{equation}' : '\\end{equation}', + '\\begin{equation*}' : '\\end{equation*}', + '\\begin{align}' : '\\end{align}', + '\\begin{align*}' : '\\end{align*}', + '\\begin{multline}' : '\\end{multline}', + '\\begin{multline*}' : '\\end{multline*}'}; -var textMode = ['\\hbox{', '\\mbox{', '\\text{']; - -function findFirst(res, delim, start) { - var i = res.indexOf(delim, start); - if (delim === '$' || delim === '\\[' || delim === '\\(') { - if (i === -1) { - return -1; - } - else if (i !== 0 && res.charAt(i - 1) === '\\') { - return findFirst(res, delim, i + delim.length); - } - } - return i; -} +var textMode = ['\\hbox{', + '\\mbox{', + '\\text{']; function firstDelim(res, enter) { var min, list; @@ -37,8 +26,8 @@ function firstDelim(res, enter) { list = textMode; } for (var key in list) { - var i = findFirst(res, key, 0); - if (i !== -1 && (i <= findFirst(res, min, 0) || res.indexOf(min) === -1)) { + var i = res.indexOf(key); + if (i !== -1 && (i <= res.indexOf(min) || res.indexOf(min) === -1)) { min = key; } } @@ -68,7 +57,7 @@ function doesEnter(string) { return ''; } -function skip(res) { +function skipEscaped(res) { if (startsWith(res, '\\$')) { return 2; } @@ -86,7 +75,7 @@ function parseMath(res, start, ranges) { var i = delim.length; var begin = start + i; while (i < res.length) { - i += skip(res.substring(i)); + i += skipEscaped(res.substring(i)); if (doesExit(res.substring(i))) { if (begin !== start + i) { ranges.push([begin, start + i]); @@ -112,7 +101,7 @@ function parseNonMath(res, start, ranges) { var level = 0; var i = delim.length; while (i < res.length) { - i += skip(res.substring(i)); + i += skipEscaped(res.substring(i)); if (doesEnter(res.substring(i))) { i += parseMath(res.substring(i), start + i, ranges); } @@ -133,7 +122,7 @@ function parseNonMath(res, start, ranges) { return i; } -module.exports.findRanges = function (res) { +module.exports.findMathSections = function (res) { var ranges = []; parseNonMath(res, 0, ranges); var mathSections = []; diff --git a/test/mathmodetest.js b/test/mathmodetest.js index 22d0512..d6f65cc 100755 --- a/test/mathmodetest.js +++ b/test/mathmodetest.js @@ -19,7 +19,7 @@ var testcases = [ out: ['c', 'd'] }, { - input: '\\$Not this\\$ \\\\[or this\\\\] \\\\(or even this\\\\) $This$', + input: '\\$Not this\\$ \\\\[or this\\\\] \\\\(or even this\\\\) $This$', options: {}, out: ['This'] }, @@ -36,7 +36,7 @@ describe('Index', function () { var options = tc.options; var output = tc.out; it('should correctly replace ' + JSON.stringify(input), function () { - assert.deepEqual(mathFinder.findRanges(input), output); + assert.deepEqual(mathFinder.findMathSections(input), output); }); }); }); \ No newline at end of file From a7173dc97da4c0a3199e211e20ea6a82d68dd996 Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Fri, 22 Apr 2016 16:37:31 -0400 Subject: [PATCH 08/14] Removed .idea files (I think) and added to .gitignore. --- .gitignore | 6 +++++ .idea/.name | 1 - .idea/compiler.xml | 22 ---------------- .idea/copyright/profiles_settings.xml | 3 --- .idea/encodings.xml | 6 ----- .idea/misc.xml | 36 --------------------------- .idea/modules.xml | 8 ------ 7 files changed, 6 insertions(+), 76 deletions(-) delete mode 100644 .idea/.name delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/copyright/profiles_settings.xml delete mode 100644 .idea/encodings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml diff --git a/.gitignore b/.gitignore index 84e7b0a..f06f304 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,12 @@ .idea/dictionaries .idea/vcs.xml .idea/jsLibraryMappings.xml +.idea/.name +.idea/compiler.xml +.idea/copyright/profiles_settings.xml +.idea/encodings.xml +.idea/misc.xml +.idea/modules.xml # Sensitive or high-churn files: .idea/dataSources.ids diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 2239e9e..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -texer \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 96cc43e..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf3..0000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 97626ba..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index f14b1b5..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 31e7272..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file From 5105d7ec4b676abb7082ef305f9262ca4c108576 Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Mon, 18 Jul 2016 09:03:30 -0400 Subject: [PATCH 09/14] Create code to dynamically implement macro replacements from .sty file. --- bin/texer | 59 ++ lib/build-parser.js | 32 + lib/index.js | 67 +- lib/templates/ast.js | 194 +++++ lib/templates/astutil.js | 217 ++++++ lib/templates/parser.pegjs | 688 ++++++++++++++++++ lib/templates/render.js | 131 ++++ lib/texutil.js | 1359 ++++++++++++++++++++++++++++++++++++ 8 files changed, 2743 insertions(+), 4 deletions(-) create mode 100755 bin/texer create mode 100644 lib/build-parser.js create mode 100644 lib/templates/ast.js create mode 100644 lib/templates/astutil.js create mode 100644 lib/templates/parser.pegjs create mode 100644 lib/templates/render.js create mode 100644 lib/texutil.js diff --git a/bin/texer b/bin/texer new file mode 100755 index 0000000..3199d36 --- /dev/null +++ b/bin/texer @@ -0,0 +1,59 @@ +#!/usr/bin/env node + +var PACKAGES = ['ams', 'cancel', 'color', 'euro', 'teubner']; + +var program = require('commander'); +var util = require('util'); + +var json = require('../package.json'); + +program + .version(json.version) + .usage('[options] ') + .option('--rebuild', 'Rebuild PEGjs grammar before parsing') + .option('-v, --verbose', 'Show verbose error information') + .option('-D, --debug', 'Show stack trace on failure') + .option('--usemathrm', 'Use \\mathrm instead of \\mbox to escape some text literals') + .option('--usemhchem', 'Allow commands from the mhchem package') + .option('--semanticlatex', 'Include semantic LaTeX') + .parse(process.argv); + +PACKAGES.forEach(function(pkg) { + var msg = 'Fail validation if input requires the '; + if (pkg === 'ams') { + msg += 'ams* packages'; + } else { + msg += pkg + ' package'; + } + program.option('--no-'+pkg, msg); +}); + +program.parse(process.argv); + +var input = program.args.join(' '); + +if (program.rebuild) { + require('../lib/build-parser'); +} + +var texer = require('../'); +var result = texer.check(input, { debug: program.debug, usemathrm:program.usemathrm, usemhchem: program.usemhchem, semanticlatex:program.semanticlatex }); + +// check required packages +PACKAGES.forEach(function(pkg) { + if (result[pkg+'_required'] && !program[pkg]) { + result.status = 'F'; + result.details = result[pkg+'_required']; + }; +}); + +// output result +if (result.status === '+') { + util.print(result.status + (result.output || '')); +} else if (result.status === 'F' || program.verbose) { + util.print(result.status + (result.details || '')); +} else { + util.print(result.status); +} + +process.exit(result.status === '+' ? 0 : 1); diff --git a/lib/build-parser.js b/lib/build-parser.js new file mode 100644 index 0000000..2640392 --- /dev/null +++ b/lib/build-parser.js @@ -0,0 +1,32 @@ +// Helper module to re/build the PEGJS parser. +"use strict"; + +var INPUT_FILE = __dirname + '/parser.pegjs'; +var OUTPUT_FILE = __dirname + '/parser.js'; + +var buildParser = module.exports = function(inFile, outFile) { + var PEG = require('pegjs'); + var fs = require('fs'); + var tu = require('./texutil'); + tu.createMacroCode(); + + var parserSource = PEG.buildParser(fs.readFileSync(inFile, 'utf8'), { + /* PEGJS options */ + output: "source", + cache: true,// makes repeated calls to generic_func production efficient + allowedStartTules: [ "start" ] + }); + // hack up the source to make it pass jshint + parserSource = parserSource + .replace(/(peg\$subclass\(child, parent\)|peg\$SyntaxError\(message, expected, found, location\)|peg\$parse\(input\)) {/g, + function (m) { + return m + "\n /*jshint validthis:true, newcap:false*/ "; + }).replace(/\n(\s+)([?:+]) (expectedDescs|" or "|peg)/g, ' $2\n$1$3'); + parserSource = + '/* jshint latedef: false */\n' + + 'module.exports = ' + parserSource + ';'; + + fs.writeFileSync(outFile, parserSource, 'utf8'); +}; + +buildParser(INPUT_FILE, OUTPUT_FILE); diff --git a/lib/index.js b/lib/index.js index 09f7472..01074fe 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,10 +7,69 @@ module.exports = { version: json.version // version # for this package }; -module.exports.texer = function (input, options) { +var tu = require('./texutil'); +var Parser = require('./parser'); +var render = module.exports.render = require('./render'); + +module.exports.ast = require('./ast'); +module.exports.parse = Parser.parse.bind(Parser); +module.exports.SyntaxError = Parser.SyntaxError; + +var astutil = require('./astutil'); +var contains_func = module.exports.contains_func = astutil.contains_func; + +var check = module.exports.check = function(input, options) { + /* status is one character: + * + : success! result is in 'output' + * E : Lexer exception raised + * F : TeX function not recognized + * S : Parsing error + * - : Generic/Default failure code. Might be an invalid argument, + * output file already exist, a problem with an external + * command ... + */ try { - return input; + if( typeof options === "undefined" ){ + options = {}; + options.usemathrm = false; + options.usmhchem = false; + options.semanticlatex = false; + } + // allow user to pass a parsed AST as input, as well as a string + if (typeof(input)==='string') { + input = Parser.parse(input, {usemathrm:options.usemathrm, semanticlatex:options.semanticlatex}); + } + var output = render(input); + var result = { status: '+', output: output }; + ['ams', 'cancel', 'color', 'euro', 'teubner', 'mhchem'].forEach(function(pkg) { + pkg = pkg + '_required'; + result[pkg] = astutil.contains_func(input, tu[pkg]); + }); + if (!options.usemhchem){ + if (result.mhchem_required){ + return { + status: 'C', details: "mhchem package required." + }; + } + } + return result; } catch (e) { - return 'error something went wrong'; + if (options && options.debug) { + throw e; + } + if (e instanceof Parser.SyntaxError) { + if (e.message === 'Illegal TeX function') { + return { + status: 'F', details: e.found, + offset: e.offset, line: e.line, column: e.column + }; + } + return { + status: 'S', details: e.toString(), + offset: e.offset, line: e.line, column: e.column + }; + } + console.log(e.toString()); + return { status: '-', details: e.toString() }; } -}; +}; \ No newline at end of file diff --git a/lib/templates/ast.js b/lib/templates/ast.js new file mode 100644 index 0000000..c3bc36a --- /dev/null +++ b/lib/templates/ast.js @@ -0,0 +1,194 @@ +// AST type declarations +"use strict"; + +var typecheck = module.exports.typecheck = function(val, type, self) { + switch (type) { + 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 typecheck(elem, type[0], self); + }); + } + return type.contains(val); +}; +var type2str = function(type) { + if (typeof(type) === 'string') { + return type; + } + if (Array.isArray(type)) { + return '[' + type2str(type[0]) + ']'; + } + return type.name; +}; + +// "Enum" helper +// vaguely based on: +// https://github.com/rauschma/enums/blob/master/enums.js +var 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; + this[fname] = function EnumField() { + if (!(this instanceof EnumField)) { + var 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" + / "\\:" + / "\\enspace" + / "\\quad" + / "\\qquad" + / "\\thinspace" + / "\\thinmuskip" + / "\\medmuskip" + / "\\thickmuskip" + / "\\kern" _ num _ + / "\\hskip" _ num _ + / "\\hspace" _ "{" _ num _ "}" _ + / "~" + / "\\ " + / "\\hfill" + / "\\hfil" + / "\\hphantom" _ "{" _ lit _ "}" _ + / [ \t\n\r] + +///////////////////////////////////////////////////////////// +// PARSER +//---------------------------------------------------------- + +tex_expr + = e:expr EOF + { return e; } + / e1:ne_expr name:FUN_INFIX e2:ne_expr EOF + { return ast.LList(ast.Tex.INFIX(name, e1.toArray(), e2.toArray())); } + / e1:ne_expr f:FUN_INFIXh e2:ne_expr EOF + { return ast.LList(ast.Tex.INFIXh(f[0], f[1], e1.toArray(), e2.toArray()));} + +expr + = ne_expr + / "" + { return ast.LList.EMPTY; } + +ne_expr + = h:lit_aq t:expr + { return ast.LList(h, t); } + / h:litsq_aq t:expr + { return ast.LList(h, t); } + / d:DECLh e:expr + { return ast.LList(ast.Tex.DECLh(d[0], d[1], e.toArray())); } +litsq_aq + = litsq_fq + / litsq_dq + / litsq_uq + / litsq_zq +litsq_fq + = l1:litsq_dq SUP l2:lit + { return ast.Tex.FQ(l1[0], l1[1], l2); } + / l1:litsq_uq SUB l2:lit + { return ast.Tex.FQ(l1[0], l2, l1[1]); } +litsq_uq + = base:litsq_zq SUP upi:lit + { return ast.Tex.UQ(base, upi); } +litsq_dq + = base:litsq_zq SUB downi:lit + { return ast.Tex.DQ(base, downi); } +litsq_zq + = SQ_CLOSE + { return ast.Tex.LITERAL(sq_close_ri); } +expr_nosqc + = l:lit_aq e:expr_nosqc + { return ast.LList(l, e); } + / "" /* */ + { return ast.LList.EMPTY; } +lit_aq + = lit_fq + / lit_dq + / lit_uq + / lit_dqn + / lit_uqn + / lit + +lit_fq + = l1:lit_dq SUP l2:lit + { return ast.Tex.FQ(l1[0], l1[1], l2); } + / l1:lit_uq SUB l2:lit + { return ast.Tex.FQ(l1[0], l2, l1[1]); } + / l1:lit_dqn SUP l2:lit + { return ast.Tex.FQN(l1[0], l2); } + +lit_uq + = base:lit SUP upi:lit + { return ast.Tex.UQ(base, upi); } +lit_dq + = base:lit SUB downi:lit + { return ast.Tex.DQ(base, downi); } +lit_uqn + = SUP l:lit + { return ast.Tex.UQN(l); } +lit_dqn + = SUB l:lit + { return ast.Tex.DQN(l); } + + +left + = LEFT d:DELIMITER + { return d; } + / LEFT SQ_CLOSE + { return sq_close_ri; } +right + = RIGHT d:DELIMITER + { return d; } + / RIGHT SQ_CLOSE + { return sq_close_ri; } +lit + = + f:PAREN atsymbol atsymbol l:lit &{ return options.semanticlatex; } + { return ast.Tex.PAREN2(f, l); } + / f:PAREN atsymbol l:lit &{ return options.semanticlatex; } + { return ast.Tex.PAREN1(f, l); } + / "P_" l:lit "^" "{"* _ PAREN_OPEN _ l2:lit _ "," _ l3:lit _ PAREN_CLOSE _ "}"* l4:litparen1 &{ return options.semanticlatex; } + { return ast.Tex.JACOBI(l, l2, l3, l4); } + / "L_" l:lit "^" l2:litparen1 l3:litparen1 &{ return options.semanticlatex; } + { return ast.Tex.LAGUERRE2(l, l2, l3); } + / "L_" l:lit l2:litparen1 &{ return options.semanticlatex; } + { return ast.Tex.LAGUERRE1(l, l2); } + / PAREN_OPEN l:lit ";" l2:lit PAREN_CLOSE "_" l3:lit &{ return options.semanticlatex; } + { return ast.Tex.QPOCH(l, l2, l3); } + / l1:litparen1 "_" l2:lit &{ return options.semanticlatex; } + { return ast.Tex.POCH(l1, l2); } + / f:PAREN l:litparen1 &{ return options.semanticlatex; } + { return ast.Tex.PAREN1(f, l); } //must be before LITERAL!! + / name:PAREN l:lit &{ return options.semanticlatex; } + { return ast.Tex.PAREN2(name, l); } + / "{" _ "\\rm" _ e:EJACOBI _ "}" _ PAREN_OPEN _ l1:lit _ "," _ l2:lit _ PAREN_CLOSE &{ return options.semanticlatex; } + { return ast.Tex.EJACOBI(e, l1, l2); } + / r:LITERAL { return ast.Tex.LITERAL(r); } + // quasi-literal; this is from Texutil.find(...) but the result is not + // guaranteed to be Tex.LITERAL(RenderT...) + / f:generic_func &{ return tu.other_literals3[f]; } _ // from Texutil.find(...) + { + var ast = peg$parse(tu.other_literals3[f]); + console.assert(Array.isArray(ast) && ast.length === 1); + return ast[0]; + } + / r:DELIMITER { return ast.Tex.LITERAL(r); } + / b:BIG r:DELIMITER { return ast.Tex.BIG(b, r); } + / b:BIG SQ_CLOSE { return ast.Tex.BIG(b, sq_close_ri); } + / l:left e:expr r:right { return ast.Tex.LR(l, r, e.toArray()); } + / name:FUN_AR1opt e:expr_nosqc SQ_CLOSE l:lit /* must be before FUN_AR1 */ + { return ast.Tex.FUN2sq(name, ast.Tex.CURLY(e.toArray()), l); } + / name:FUN_AR1 l:lit { return ast.Tex.FUN1(name, l); } + / name:FUN_AR1nb l:lit { return ast.Tex.FUN1nb(name, l); } + / name:FUN_AR2 l1:lit l2:lit { return ast.Tex.FUN2(name, l1, l2); } + / name:FUN_AR2nb l1:lit l2:lit { return ast.Tex.FUN2nb(name, l1, l2); } + / name:FUN_AR3 l1:lit l2:lit l3:lit { return ast.Tex.FUN3(name, l1, l2, l3); } + / f:JACOBI l1:lit l2:lit l3:lit atsymbol l4:lit &{ return options.semanticlatex; } { return ast.Tex.JACOBI(l1, l2, l3, l4); } + / f:LAGUERRE l1:lit atsymbol l2:lit &{ return options.semanticlatex; } { return ast.Tex.LAGUERRE1(l1 ,l2); } + / f:LAGUERRE l1:lit "[" l2:lit "]" atsymbol l3:lit &{ return options.semanticlatex; } { return ast.Tex.LAGUERRE2(l1, l2, l3); } + / "\\Jacobi" e:EJACOBI atsymbol l1:lit l2:lit &{ return options.semanticlatex; } { return ast.Tex.EJACOBI(e, l1, l2); } + / BOX + / CURLY_OPEN e:expr CURLY_CLOSE + { return ast.Tex.CURLY(e.toArray()); } + / CURLY_OPEN e1:ne_expr name:FUN_INFIX e2:ne_expr CURLY_CLOSE + { return ast.Tex.INFIX(name, e1.toArray(), e2.toArray()); } + / CURLY_OPEN e1:ne_expr f:FUN_INFIXh e2:ne_expr CURLY_CLOSE + { return ast.Tex.INFIXh(f[0], f[1], e1.toArray(), e2.toArray()); } + / BEGIN_MATRIX m:(array/matrix) END_MATRIX + { return ast.Tex.MATRIX("matrix", lst2arr(m)); } + / BEGIN_PMATRIX m:(array/matrix) END_PMATRIX + { return ast.Tex.MATRIX("pmatrix", lst2arr(m)); } + / BEGIN_BMATRIX m:(array/matrix) END_BMATRIX + { return ast.Tex.MATRIX("bmatrix", lst2arr(m)); } + / BEGIN_BBMATRIX m:(array/matrix) END_BBMATRIX + { return ast.Tex.MATRIX("Bmatrix", lst2arr(m)); } + / BEGIN_VMATRIX m:(array/matrix) END_VMATRIX + { return ast.Tex.MATRIX("vmatrix", lst2arr(m)); } + / BEGIN_VVMATRIX m:(array/matrix) END_VVMATRIX + { return ast.Tex.MATRIX("Vmatrix", lst2arr(m)); } + / BEGIN_ARRAY opt_pos m:array END_ARRAY + { return ast.Tex.MATRIX("array", lst2arr(m)); } + / BEGIN_ALIGN opt_pos m:matrix END_ALIGN + { return ast.Tex.MATRIX("aligned", lst2arr(m)); } + / BEGIN_ALIGNED opt_pos m:matrix END_ALIGNED // parse what we emit + { return ast.Tex.MATRIX("aligned", lst2arr(m)); } + / BEGIN_ALIGNAT m:alignat END_ALIGNAT + { return ast.Tex.MATRIX("alignedat", lst2arr(m)); } + / BEGIN_ALIGNEDAT m:alignat END_ALIGNEDAT // parse what we emit + { return ast.Tex.MATRIX("alignedat", lst2arr(m)); } + / BEGIN_SMALLMATRIX m:(array/matrix) END_SMALLMATRIX + { return ast.Tex.MATRIX("smallmatrix", lst2arr(m)); } + / BEGIN_CASES m:matrix END_CASES + { return ast.Tex.MATRIX("cases", lst2arr(m)); } + / "\\begin{" alpha+ "}" /* better error messages for unknown environments */ + { throw new peg$SyntaxError("Illegal TeX function", [], text(), location()); } + / f:generic_func &{ return !tu.all_functions[f]; } + { throw new peg$SyntaxError("Illegal TeX function", [], f, location()); } + +litstuff = //basically lit without DELIMITER or LITERAL as an option (replaced by LITERALPART at the end), needed because they interrupt the parsing of commands in PAREN. + f:PAREN atsymbol atsymbol l:lit &{ return options.semanticlatex; } + { return ast.Tex.PAREN2(f, l); } + / f:PAREN atsymbol l:lit &{ return options.semanticlatex; } + { return ast.Tex.PAREN1(f, l); } + / "P_" l:lit "^" "{"* _ PAREN_OPEN _ l2:lit _ "," _ l3:lit _ PAREN_CLOSE _ "}"* l4:litparen1 &{ return options.semanticlatex; } + { return ast.Tex.JACOBI(l, l2, l3, l4); } + / "L_" l:lit "^" l2:litparen1 l3:litparen1 &{ return options.semanticlatex; } + { return ast.Tex.LAGUERRE2(l, l2, l3); } + / "L_" l:lit l2:litparen1 &{ return options.semanticlatex; } + { return ast.Tex.LAGUERRE1(l, l2); } + / PAREN_OPEN l:lit ";" l2:lit PAREN_CLOSE "_" l3:lit &{ return options.semanticlatex; } + { return ast.Tex.QPOCH(l, l2, l3); } + / l1:litparen1 "_" l2:lit &{ return options.semanticlatex; } + { return ast.Tex.POCH(l1, l2); } + / f:PAREN l:litparen1 &{ return options.semanticlatex; } + { return ast.Tex.PAREN1(f, l); } //must be before LITERAL!! + / name:PAREN l:lit &{ return options.semanticlatex; } + { return ast.Tex.PAREN2(name, l); } + / "{" _ "\\rm" _ e:EJACOBI _ "}" _ PAREN_OPEN _ l1:lit _ "," _ l2:lit _ PAREN_CLOSE &{ return options.semanticlatex; } + { return ast.Tex.EJACOBI(e, l1, l2); } + / b:BIG r:DELIMITER { return ast.Tex.BIG(b, r); } + / b:BIG SQ_CLOSE { return ast.Tex.BIG(b, sq_close_ri); } + / l:left e:expr r:right { return ast.Tex.LR(l, r, e.toArray()); } + / name:FUN_AR1opt e:expr_nosqc SQ_CLOSE l:lit /* must be before FUN_AR1 */ + { return ast.Tex.FUN2sq(name, ast.Tex.CURLY(e.toArray()), l); } + / name:FUN_AR1 l:lit { return ast.Tex.FUN1(name, l); } + / name:FUN_AR1nb l:lit { return ast.Tex.FUN1nb(name, l); } + / f:FUN_AR1hl l:lit { return ast.Tex.FUN1hl(f[0], f[1], l); } + / f:FUN_AR1hf l:lit { return ast.Tex.FUN1hf(f[0], f[1], l); } + / name:FUN_AR2 l1:lit l2:lit { return ast.Tex.FUN2(name, l1, l2); } + / name:FUN_AR2nb l1:lit l2:lit { return ast.Tex.FUN2nb(name, l1, l2); } + / f:FUN_AR2h l1:lit l2:lit { return ast.Tex.FUN2h(f[0], f[1], l1, l2); } + / name:FUN_AR3 l1:lit l2:lit l3:lit &{ return options.semanticlatex; } { return ast.Tex.FUN3(name, l1, l2, l3); } + / f:JACOBI l1:lit l2:lit l3:lit atsymbol l4:lit &{ return options.semanticlatex; } { return ast.Tex.JACOBI(l1, l2, l3, l4); } + / f:LAGUERRE l1:lit atsymbol l2:lit &{ return options.semanticlatex; } { return ast.Tex.LAGUERRE1(l1 ,l2); } + / f:LAGUERRE l1:lit "[" l2:lit "]" atsymbol l3:lit &{ return options.semanticlatex; } { return ast.Tex.LAGUERRE2(l1, l2, l3); } + / "\\Jacobi" e:EJACOBI atsymbol l1:lit l2:lit &{ return options.semanticlatex; } { return ast.Tex.EJACOBI(e, l1, l2); } + / BOX + / CURLY_OPEN e:expr CURLY_CLOSE + { return ast.Tex.CURLY(e.toArray()); } + / CURLY_OPEN e1:ne_expr name:FUN_INFIX e2:ne_expr CURLY_CLOSE + { return ast.Tex.INFIX(name, e1.toArray(), e2.toArray()); } + / CURLY_OPEN e1:ne_expr f:FUN_INFIXh e2:ne_expr CURLY_CLOSE + { return ast.Tex.INFIXh(f[0], f[1], e1.toArray(), e2.toArray()); } + / BEGIN_MATRIX m:(array/matrix) END_MATRIX + { return ast.Tex.MATRIX("matrix", lst2arr(m)); } + / BEGIN_PMATRIX m:(array/matrix) END_PMATRIX + { return ast.Tex.MATRIX("pmatrix", lst2arr(m)); } + / BEGIN_BMATRIX m:(array/matrix) END_BMATRIX + { return ast.Tex.MATRIX("bmatrix", lst2arr(m)); } + / BEGIN_BBMATRIX m:(array/matrix) END_BBMATRIX + { return ast.Tex.MATRIX("Bmatrix", lst2arr(m)); } + / BEGIN_VMATRIX m:(array/matrix) END_VMATRIX + { return ast.Tex.MATRIX("vmatrix", lst2arr(m)); } + / BEGIN_VVMATRIX m:(array/matrix) END_VVMATRIX + { return ast.Tex.MATRIX("Vmatrix", lst2arr(m)); } + / BEGIN_ARRAY opt_pos m:array END_ARRAY + { return ast.Tex.MATRIX("array", lst2arr(m)); } + / BEGIN_ALIGN opt_pos m:matrix END_ALIGN + { return ast.Tex.MATRIX("aligned", lst2arr(m)); } + / BEGIN_ALIGNED opt_pos m:matrix END_ALIGNED // parse what we emit + { return ast.Tex.MATRIX("aligned", lst2arr(m)); } + / BEGIN_ALIGNAT m:alignat END_ALIGNAT + { return ast.Tex.MATRIX("alignedat", lst2arr(m)); } + / BEGIN_ALIGNEDAT m:alignat END_ALIGNEDAT // parse what we emit + { return ast.Tex.MATRIX("alignedat", lst2arr(m)); } + / BEGIN_SMALLMATRIX m:(array/matrix) END_SMALLMATRIX + { return ast.Tex.MATRIX("smallmatrix", lst2arr(m)); } + / BEGIN_CASES m:matrix END_CASES + { return ast.Tex.MATRIX("cases", lst2arr(m)); } + / "\\begin{" alpha+ "}" /* better error messages for unknown environments */ + { throw new peg$SyntaxError("Illegal TeX function", [], text(), location()); } + / f:generic_func &{ return !tu.all_functions[f]; } + { throw new peg$SyntaxError("Illegal TeX function", [], text(), location()); } + / l:LITERALPART { return ast.Tex.LITERAL(l); } + +litparen1 + = + "{" _ l:litparen1 _ "}" { return l; } + / l:litparen { return l; } +litparen + = + PAREN_OPEN _ l:litplus _ PAREN_CLOSE { return l;} +litplus + = + l:litstuff { return l; } + / r:LITERAL+ { + var c = 0; + var str = ""; + str = r.join(""); + while (c < r.join("").length) { //this code removes all the unneeded text and combines it together into one string. + if (str.indexOf("TEX_ONLY(\"") >= 0) { + str = str.replace("TEX_ONLY(\"", ""); + } + if (str.indexOf("\")") >= 0) { + str = str.replace("\")", ""); + } + c = c + 1; + } + return ast.Tex.LITERAL(ast.RenderT.TEX_ONLY(str)); } + / "" { return ast.Tex.LITERAL(ast.RenderT.TEX_ONLY("")); } + +// "array" requires mandatory column specification +array + = cs:column_spec m:matrix + { m.head[0].unshift(cs); return m; } + +// "alignat" requires mandatory # of columns +alignat + = as:alignat_spec m:matrix + { m.head[0].unshift(as); return m; } + +// "matrix" does not require column specification +matrix + = l:line_start tail:( NEXT_ROW m:matrix { return m; } )? + { return { head: lst2arr(l), tail: tail }; } +line_start + = f:HLINE l:line_start + { l.head.unshift(ast.Tex.LITERAL(ast.RenderT.TEX_ONLY(f + " "))); return l;} + / line +line + = e:expr tail:( NEXT_CELL l:line { return l; } )? + { return { head: e.toArray(), tail: tail }; } + +column_spec + = CURLY_OPEN cs:(one_col+ { return text(); }) CURLY_CLOSE + { return ast.Tex.CURLY([ast.Tex.LITERAL(ast.RenderT.TEX_ONLY(cs))]); } + +one_col + = [lrc] _ + / "p" CURLY_OPEN boxchars+ CURLY_CLOSE + / "*" CURLY_OPEN [0-9]+ _ CURLY_CLOSE + ( one_col + / CURLY_OPEN one_col+ CURLY_CLOSE + ) + / "||" _ + / "|" _ + / "@" _ CURLY_OPEN boxchars+ CURLY_CLOSE + +alignat_spec + = CURLY_OPEN num:([0-9]+ { return text(); }) _ CURLY_CLOSE + { return ast.Tex.CURLY([ast.Tex.LITERAL(ast.RenderT.TEX_ONLY(num))]); } + +opt_pos + = "[" _ [tcb] _ "]" _ + / "" /* empty */ + +///////////////////////////////////////////////////////////// +// LEXER +//---------------------------------------------------------- +//space = [ \t\n\r] +alpha = [a-zA-Z] +literal_id = [a-zA-Z] +literal_mn = [0-9] +literal_uf_lt = [,:;?!\'] +delimiter_uf_lt = [().] +literal_uf_op = [-+*=] +delimiter_uf_op = [\/|] +num = + "-"? [0-9]* "." [0-9]+ _ spce? + / "-"? [0-9]+ "."? _ spce? +spce = "pt" / "pc" / "in" / "cm" / "bp" / "mm" / "dd" / "cc" / "sp" / "ex" / "em" / "mu" / "px" +atsymbol = [@] +boxchars // match only valid UTF-16 sequences + = [-0-9a-zA-Z+*,=():\/;?.!\'` \x80-\ud7ff\ue000-\uffff] + / l:[\ud800-\udbff] h:[\udc00-\udfff] { return text(); } +//aboxchars = [-0-9a-zA-Z+*,=():\/;?.!\'` ] + +BOX + = b:generic_func &{ return tu.box_functions[b]; } _ "{" cs:boxchars+ "}" _ + { return ast.Tex.BOX(b, cs.join('')); } + +// returns a RenderT +LITERAL + = c:( literal_id / literal_mn / literal_uf_lt / "-" / literal_uf_op ) _ + { return ast.RenderT.TEX_ONLY(c); } + / f:generic_func &{ return tu.latex_function_names[f]; } _ + c:( "(" / "[" / "\\{" / "" { return " ";}) _ + { return ast.RenderT.TEX_ONLY(f + c); } + / f:generic_func &{ return tu.mediawiki_function_names[f]; } _ + c:( "(" / "[" / "\\{" / "" { return "";}) _ + { return ast.RenderT.TEX_ONLY("\\operatorname{" + f.slice(1) + "}" + c); } + / f:generic_func &{ return tu.other_literals1[f]; } _ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY(f + " "); } + / f:generic_func &{ return options.usemathrm && tu.other_literals2[f]; } _ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY("\\mathrm{" + f + "} "); } + / mathrm:generic_func &{ return options.usemathrm && mathrm === "\\mathrm"; } _ + "{" f:generic_func &{ return options.usemathrm && tu.other_literals2[f]; } _ "}" _ + /* make sure we can parse what we emit */ + { return options.usemathrm && ast.RenderT.TEX_ONLY("\\mathrm{" + f + "} "); } + / f:generic_func &{ return tu.other_literals2[f]; } _ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY("\\mbox{" + f + "} "); } + / mbox:generic_func &{ return mbox === "\\mbox"; } _ + "{" f:generic_func &{ return tu.other_literals2[f]; } _ "}" _ + /* make sure we can parse what we emit */ + { return ast.RenderT.TEX_ONLY("\\mbox{" + f + "} "); } + / f:(COLOR / DEFINECOLOR) + { return ast.RenderT.TEX_ONLY(f); } + / "\\" c:[, ;!_#%$&] _ + { return ast.RenderT.TEX_ONLY("\\" + c); } + / c:[><~] _ + { return ast.RenderT.TEX_ONLY(c); } + / c:[%$] _ + { return ast.RenderT.TEX_ONLY("\\" + c); /* escape dangerous chars */} + +LITERALPART //LITERAL without the first option to prevent interruption of parsing + = + f:generic_func &{ return tu.latex_function_names[f]; } _ + c:( "(" / "[" / "\\{" / "" { return " ";}) _ + { return ast.RenderT.TEX_ONLY(f + c); } + / f:generic_func &{ return tu.mediawiki_function_names[f]; } _ + c:( "(" / "[" / "\\{" / ""{ return "";}) _ + { return ast.RenderT.TEX_ONLY("\\operatorname{" + f.slice(1) + "}" + c); } + / f:generic_func &{ return tu.other_literals1[f]; } _ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY(f + " "); } + / f:generic_func &{ return tu.other_literals2[f]; } _ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY("\\mbox{" + f + "} "); } + / mbox:generic_func &{ return mbox === "\\mbox"; } _ + "{" f:generic_func &{ return tu.other_literals2[f]; } _ "}" _ + /* make sure we can parse what we emit */ + { return ast.RenderT.TEX_ONLY("\\mbox{" + f + "} "); } + / f:generic_func &{ return tu.other_literals3[f]; } _ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY(tu.other_literals3[f] + " "); } + / f:(COLOR / DEFINECOLOR) + { return ast.RenderT.TEX_ONLY(f); } + / "\\" c:[, ;!_#%$&] _ + { return ast.RenderT.TEX_ONLY("\\" + c); } + / c:[><~] _ + { return ast.RenderT.TEX_ONLY(c); } + / c:[%$] _ + { return ast.RenderT.TEX_ONLY("\\" + c); /* escape dangerous chars */} + +// returns a RenderT +DELIMITER + = c:( delimiter_uf_lt / delimiter_uf_op / "[" ) _ + { return ast.RenderT.TEX_ONLY(c); } + / "\\" c:[{}|] _ + { return ast.RenderT.TEX_ONLY("\\" + c); } + / f:generic_func &{ return tu.other_delimiters1[f]; } _ // from Texutil.find() + { return ast.RenderT.TEX_ONLY(f + " "); } + / f:generic_func &{ return tu.other_delimiters2[f]; } _ // from Texutil.find() + { var p = peg$parse(tu.other_delimiters2[f]); + console.assert(Array.isArray(p) && p.length === 1); + console.assert(p[0].constructor === ast.Tex.LITERAL); + console.assert(p[0][0].constructor === ast.RenderT.TEX_ONLY); + return p[0][0]; + } + / c:atsymbol _ //temporary workaround until @ symbol is properly implemented (bypasses syntax error in test cases) + { return ast.RenderT.TEX_ONLY(""); } + +FUN_AR1nb + = f:generic_func &{ return tu.fun_ar1nb[f]; } space* { return f; } + +FUN_AR1opt + = f:generic_func &{ return tu.fun_ar1opt[f]; } space* "[" space* { return f; } + +NEXT_CELL + = "&" _ + +NEXT_ROW + = "\\\\" _ + +BEGIN + = "\\begin" _ +END + = "\\end" _ + +BEGIN_MATRIX + = BEGIN "{matrix}" _ +END_MATRIX + = END "{matrix}" _ +BEGIN_PMATRIX + = BEGIN "{pmatrix}" _ +END_PMATRIX + = END "{pmatrix}" _ +BEGIN_BMATRIX + = BEGIN "{bmatrix}" _ +END_BMATRIX + = END "{bmatrix}" _ +BEGIN_BBMATRIX + = BEGIN "{Bmatrix}" _ +END_BBMATRIX + = END "{Bmatrix}" _ +BEGIN_VMATRIX + = BEGIN "{vmatrix}" _ +END_VMATRIX + = END "{vmatrix}" _ +BEGIN_VVMATRIX + = BEGIN "{Vmatrix}" _ +END_VVMATRIX + = END "{Vmatrix}" _ +BEGIN_ARRAY + = BEGIN "{array}" _ +END_ARRAY + = END "{array}" _ +BEGIN_ALIGN + = BEGIN "{align}" _ +END_ALIGN + = END "{align}" _ +BEGIN_ALIGNED + = BEGIN "{aligned}" _ +END_ALIGNED + = END "{aligned}" _ +BEGIN_ALIGNAT + = BEGIN "{alignat}" _ +END_ALIGNAT + = END "{alignat}" _ +BEGIN_ALIGNEDAT + = BEGIN "{alignedat}" _ +END_ALIGNEDAT + = END "{alignedat}" _ +BEGIN_SMALLMATRIX + = BEGIN "{smallmatrix}" _ +END_SMALLMATRIX + = END "{smallmatrix}" _ +BEGIN_CASES + = BEGIN "{cases}" _ +END_CASES + = END "{cases}" _ + +SQ_CLOSE + = "]" _ +CURLY_OPEN + = "{" _ +CURLY_CLOSE + = "}" _ +PAREN_OPEN + = "(" _ / "\\big(" _ / "\\Big(" _ / "\\bigg(" _ / "\\Bigg(" _ + / "\\bigl(" _ / "\\Bigl(" _ / "\\bigg1(" _ / "\\Biggl(" _ / "\\left(" _ +PAREN_CLOSE + = ")" _ / "\\big)" _ / "\\Big)" _ / "\\bigg)" _ / "\\Bigg)" _ + / "\\bigr)" _ / "\\Bigr)" _ / "\\biggr)" _ / "\\Biggr)" _ / "\\right)" +SUP + = "^" _ +SUB + = "_" _ + +// This is from Texutil.find in texvc +generic_func + = "\\" alpha+ { return text(); } + +BIG + = f:generic_func &{ return tu.big_literals[f]; } _ + { return f; } + +PAREN + = f:generic_func &{ return tu.paren[f]; } space* { return f; } + +FUN_AR1 + = f:generic_func &{ return tu.fun_ar1[f]; } space* + { return f; } + / f:generic_func &{ return tu.other_fun_ar1[f]; } space* + { return tu.other_fun_ar1[f]; } + +FUN_AR2 + = f:generic_func &{ return tu.fun_ar2[f]; } space* + { return f; } + +FUN_AR3 + = f:generic_func &{ return tu.fun_ar3[f]; } space* + { return f; } + +FUN_INFIX + = f:generic_func &{ return tu.fun_infix[f]; } _ + { return f; } + +JACOBI + = f:generic_func &{ return tu.jacobi[f]; } space* { return f; } + +LAGUERRE + = f:generic_func &{ return tu.laguerre[f]; } space* { return f; } + +EJACOBI + = "sn" + / "cn" + / "dn" + +DECLh + = f:generic_func &{ return tu.declh_function[f]; } _ + { return ast.Tex.DECLh(f, ast.FontForce.RM(), []); /*see bug 54818*/ } + +FUN_AR2nb + = f:generic_func &{ return tu.fun_ar2nb[f]; } space* + { return f; } + +LEFT + = f:generic_func &{ return tu.left_function[f]; } _ + +RIGHT + = f:generic_func &{ return tu.right_function[f]; } _ + +HLINE + = f:generic_func &{ return tu.hline_function[f]; } _ + { return f; } + +COLOR + = f:generic_func &{ return tu.color_function[f]; } _ cs:COLOR_SPEC + { return f + cs; } + +DEFINECOLOR + = f:generic_func &{ return tu.definecolor_function[f]; } _ + "{" _ name:alpha+ _ "}" _ "{" _ + a:( "named"i _ "}" _ cs:COLOR_SPEC_NAMED { return "{named}" + cs; } + / "gray"i _ "}" _ cs:COLOR_SPEC_GRAY { return "{gray}" + cs; } + / "rgb" _ "}" _ cs:COLOR_SPEC_rgb { return "{rgb}" + cs; } + // Note that we actually convert RGB format to rgb format here. + / "RGB" _ "}" _ cs:COLOR_SPEC_RGB { return "{rgb}" + cs; } + / "cmyk"i _ "}" _ cs:COLOR_SPEC_CMYK { return "{cmyk}" + cs; } ) + { return f + "{" + name.join('') + "}" + a; } + +COLOR_SPEC + = COLOR_SPEC_NAMED + / "[" _ "named"i _ "]" _ cs:COLOR_SPEC_NAMED + { return "[named]" + cs; } + / "[" _ "gray"i _ "]" _ cs:COLOR_SPEC_GRAY + { return "[gray]" + cs; } + / "[" _ "rgb" _ "]" _ cs:COLOR_SPEC_rgb + { return "[rgb]" + cs; } + / "[" _ "RGB" _ "]" _ cs:COLOR_SPEC_RGB + // Note that we actually convert RGB format to rgb format here. + { return "[rgb]" + cs; } + / "[" _ "cmyk"i _ "]" _ cs:COLOR_SPEC_CMYK + { return "[cmyk]" + cs; } + +COLOR_SPEC_NAMED + = "{" _ name:alpha+ _ "}" _ + { return "{" + name.join('') + "}"; } +COLOR_SPEC_GRAY + = "{" _ k:CNUM + "}" + { return "{"+k+"}"; } +COLOR_SPEC_rgb + = "{" _ r:CNUM "," _ g:CNUM "," _ b:CNUM "}" _ + { return "{"+r+","+g+","+b+"}"; } +COLOR_SPEC_RGB + = "{" _ r:CNUM255 "," _ g:CNUM255 "," _ b:CNUM255 "}" _ + // Note that we normalize the values to [0,1] here. + { return "{"+r+","+g+","+b+"}"; } +COLOR_SPEC_CMYK + = "{" _ c:CNUM "," _ m:CNUM "," _ y:CNUM "," _ k:CNUM "}" _ + { return "{"+c+","+m+","+y+","+k+"}"; } + +// An integer in [0, 255] => normalize it to [0,1] +CNUM255 + = n:$( "0" / ([1-9] ([0-9] [0-9]?)? ) ) &{ return parseInt(n, 10) <= 255; } _ + { return n / 255; } + +// A floating-point number in [0, 1] +CNUM + = n:$( "0"? "." [0-9]+ ) _ + { return n; } + / n:$( [01] "."? ) _ + { return n; } + +// Missing lexer tokens! +FUN_INFIXh = impossible +FUN_AR1hl = impossible +FUN_AR1hf = impossible +FUN_AR2h = impossible +impossible = & { return false; } + +// End of file +EOF = & { return peg$currPos === input.length; } diff --git a/lib/templates/render.js b/lib/templates/render.js new file mode 100644 index 0000000..34d7b7d --- /dev/null +++ b/lib/templates/render.js @@ -0,0 +1,131 @@ +// Render an AST. +"use strict"; + +var ast = require('./ast'); + +ast.RenderT.defineVisitor("tex_part", { + HTMLABLE: function(_,t,_2) { return t; }, + HTMLABLEM: function(_,t,_2) { return t; }, + HTMLABLEC: function(_,t,_2) { return t; }, + MHTMLABLEC: function(_,t,_2,_3,_4) { return t; }, + HTMLABLE_BIG: function(t,_) { return t; }, + TEX_ONLY: function(t) { return t; } +}); + + +var render = module.exports = function render(e) { + if (Array.isArray(e)) { + return e.map(render).join(''); + } + return e.render_tex(); +}; + +var curlies = function(t) { + switch (t.constructor) { + // constructs which are surrounded by curlies + case ast.Tex.CURLY: + case ast.Tex.MATRIX: + return t.render_tex(); + case String: + break; + default: + t = t.render_tex(); + } + return "{" + t + "}"; +}; + +var render_curlies = function(a) { + if (a.length === 1) { + return curlies(a[0]); + } + return curlies(render(a)); +}; + +ast.Tex.defineVisitor("render_tex", { + FQ: function(base, down, up) { + return base.render_tex() + "_" + curlies(down) + "^" + curlies(up); + }, + DQ: function(base, down) { + return base.render_tex() + "_" + curlies(down); + }, + UQ: function(base, up) { + return base.render_tex() + "^" + curlies(up); + }, + FQN: function(down, up) { + return "_" + curlies(down) + "^" + curlies(up); + }, + DQN: function(down) { + return "_" + curlies(down); + }, + UQN: function(up) { + return "^" + curlies(up); + }, + JACOBI: function(a, b, c, d) { + return "\\Jacobi" + curlies(a) + curlies(b) + curlies(c) + "@" + curlies(d); + }, + LAGUERRE1: function(a, b) { + return "\\Laguerre" + curlies(a) + "@" + curlies(b); + }, + LAGUERRE2: function(a, b, c) { + return "\\Laguerre" + curlies(a) + "[" + b.render_tex() + "]" + "@" + curlies(c); + }, + EJACOBI: function(a, b, c) { + return "\\Jacobi" + a + "@" + curlies(b) + curlies(c); + }, + LITERAL: function(r) { + return r.tex_part(); + }, + PAREN1: function(f, a) { + return f + "@" + curlies(a); + }, + PAREN2: function(f, a) { + return f + "@@" + curlies(a); + }, + QPOCH: function(a, b, c) { + return "\\qPochhammer" + curlies(a) + curlies(b) + curlies(c); + }, + POCH: function(a, b) { + return "\\pochhammer" + curlies(a) + curlies(b); + }, + FUN1: function(f, a) { + return f + curlies(a); + }, + FUN1nb: function(f, a) { + return f + curlies(a); + }, + DECLh: function(f, _, a) { + return f + render_curlies(a); + }, + FUN2: function(f, a, b) { + return f + curlies(a) + curlies(b); + }, + FUN2nb: function(f, a, b) { + return f + curlies(a) + curlies(b); + }, + FUN2sq: function(f, a, b) { + return f + "[" + a.render_tex() + "]" + curlies(b); + }, + FUN3: function(f, a, b, c) { + return f + curlies(a) + curlies(b) + curlies(c); + }, + CURLY: function(tl) { + return render_curlies(tl); + }, + INFIX: function(s, ll, rl) { + return curlies(render(ll) + " " + s + " " + render(rl)); + }, + BOX: function(bt, s) { + return bt + curlies(s); + }, + BIG: function(bt, d) { + return bt + " " + d.tex_part(); + }, + MATRIX: function(t, m) { + var render_line = function(l) { return l.map(render).join('&'); }; + var render_matrix = function(m) { return m.map(render_line).join('\\\\'); }; + return curlies("\\begin{"+t+"}" + render_matrix(m) + "\\end{"+t+"}"); + }, + LR: function(l, r, tl) { + return "\\left" + l.tex_part() + render(tl) + "\\right" + r.tex_part(); + } +}); diff --git a/lib/texutil.js b/lib/texutil.js new file mode 100644 index 0000000..e2ccba1 --- /dev/null +++ b/lib/texutil.js @@ -0,0 +1,1359 @@ +// Information about TeX functions. +// In its own module so that the sets aren't recreated from scratch +// every time that parse() is called. +"use strict"; + +// track all known function names, so we can give good errors for unknown +// functions. +var all_functions = module.exports.all_functions = Object.create(null); +all_functions['\\begin'] = all_functions['\\end'] = true; + +var arr2set = function(a) { + // note that the fact that all keys in the set are prefixed with '\\' + // helps avoid accidental name conflicts. But use Object.create(null) + // to be extra safe. + var set = Object.create(null); + a.forEach(function(v) { + console.assert(!set[v], v); + set[v] = all_functions[v] = true; + }); + return set; +}; +var obj2map = function(o) { + // this just recreates the argument, but with `null` as prototype. + var map = Object.create(null); + Object.keys(o).forEach(function(f) { + console.assert(!map[f]); + map[f] = o[f]; all_functions[f] = true; + }); + return map; +}; + +// Sets of function names +module.exports.box_functions = arr2set([ + "\\text", "\\mbox", "\\hbox", "\\vbox" +]); + +module.exports.latex_function_names = arr2set([ + "\\arccos", "\\arcsin", "\\arctan", "\\arg", "\\cosh", "\\cos", + "\\cot", "\\coth", "\\csc", "\\deg", "\\det", "\\dim", "\\exp", + "\\gcd", "\\hom", "\\inf", "\\ker", "\\lg", "\\lim", "\\liminf", + "\\limsup", "\\ln", "\\log", "\\max", "\\min", "\\Pr", "\\sec", + "\\sin", "\\sinh", "\\sup", "\\tan", "\\tanh" +]); + +function findFirst(string, subs, start) { + var min = -1; + var index; + var first = subs[0]; + for (var i = 0; i < subs.length; i++) { + index = string.indexOf(subs[i], start); + if ((index !== -1) && (index < min || min === -1)) { + min = index; + first = subs[i]; + } + } + return [min, first]; +} + +function findBracketSections(string) { + var sections = [[""]]; + var char; + var level = 0; + string = string.substring(findFirst(string, ["[", "{"])[0]); + for (var i = 0; i < string.length; i++) { + char = string.charAt(i); + if (char == "}" || char == "]") { + level--; + if (level == 0) { + sections.push([""]); + } + else { + sections[sections.length - 1][0] += char; + } + } + else if (char == "{" || char == "[") { + if (level != 0) { + sections[sections.length - 1][0] += char; + } + else { + sections[sections.length - 1].push(char); + } + level++; + } + else { + sections[sections.length - 1][0] += char; + } + } + sections = sections.slice(0, sections.length - 1); + return sections; +} + +String.prototype.replaceAll = function(str1, str2, ignore) { + return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2); +} + +function generateCode(cmd, parameterCount, replacement, argumentCount, renderOption, optionNum, code, extra) { + if (argumentCount != 0 && renderOption == null) { + return; + } + for (var i = 0; i < argumentCount; i++) { + if (renderOption.indexOf("#" + (i + 1)) == -1) { + return; + } + } + var command = cmd + (optionNum + 1); + if (extra != null) { + command += extra; + } + var pegjs = code[0]; + var ast = code[1]; + var astutil = code[2]; + var render = code[3]; + var pegjscode = "\"" + replacement; + var astcode = command + ": { args: [ 'string', "; + var astutilcode1 = command + ": function(target, "; + var astutilcode2 = "\treturn "; + var rendercode1 = command + ": function("; + var rendercode2 = "\treturn "; + var args = []; + var temp; + for (var par = 0; par < parameterCount; par++) { + if (pegjscode.indexOf("#" + (par + 1)) != -1) { + temp = "l" + (par + 1); + args.push(temp); + pegjscode = pegjscode.replaceAll("#" + (par + 1), "\" _ " + temp + ":lit _ \""); + } + } + var index = args.length; + if (renderOption != null) { + pegjscode += renderOption; + for (var arg = 0; arg < argumentCount; arg++) { + temp = "k" + (arg + 1); + args.push(temp); + pegjscode = pegjscode.replaceAll("#" + (arg + 1), "\" _ " + temp + ":lit _ \""); + } + } + pegjscode = (pegjscode + "\" ").replaceAll("_ \" ", "_ \""); + pegjscode = [pegjscode.replaceAll(" \" _", "\" _")].concat("{ return ast.Tex." + command + ["(\"\\\\" + cmd + "\""].concat(args).join(", ") + "); }"); + astcode += Array.apply(null, Array(args.length)).map(function() {return "'self'"}).join(", ") + " ] },"; + astutilcode1 += ["f"].concat(args).join(", ") + ") {\n\t"; + astutilcode2 += ["match(target, f)"].concat(args.map(function(a) {return a + ".contains_func(target)"})).join(" || "); + var astutilcode = astutilcode1 + astutilcode2 + ";\n\t},"; + rendercode1 += ["f"].concat(args).join(", ") + ") {\n\t"; + temp = ["\"\\\\" + cmd + "\""].concat(args.map(function(a) {return "curlies(" + a + ")"})) + if (renderOption != null) { + temp.splice(index + 1, 0, "\"" + Array(optionNum + 2).join("@") + "\""); + } + rendercode2 += temp.join(" + "); + var rendercode = rendercode1 + rendercode2 + ";\n\t},"; + pegjs.push(pegjscode); + ast.push(astcode); + astutil.push(astutilcode); + render.push(rendercode); +} + +function parseSpecFun(text, code, extra) { + var sections = findBracketSections(text); + var command = sections[0][0]; + var offset = 0; + var parameterCount = 0; + if (sections[1][1] == "[") { + parameterCount = parseInt(sections[1][0]); + if (sections[2][1] == "[") { + offset = 1; + } + } + else { + offset = -1; + } + var replacement = sections[2 + offset][0].replaceAll("\\", "\\\\").replaceAll("\\\\!", "\\!"); + var meaning = sections[3 + offset][0]; + var argumentCount = parseInt(sections[4 + offset][0]); + if (sections.length == 5 + offset) { + generateCode(command, parameterCount, replacement, argumentCount, null, 0, code, extra); + } + else { + for (var i = 0; i < sections.length - 5 - offset; i++) { + var renderOption = sections[i + 5 + offset][0].replaceAll("\\", "\\\\").replaceAll("\\\\!", "\\!"); + generateCode(command, parameterCount, replacement, argumentCount, renderOption, i, code, extra); + } + } +} + +function parseIfx(string) { + var start = string.indexOf("\\ifx"); + if (start == -1) { + return [string]; + } + else { + var end = string.indexOf("\\fi") + 3; + var ifx = string.substring(start, end); + var false_text = ifx.substring(ifx.indexOf("\\else") + 5, ifx.length - 3); + return [string.substring(0, start) + string.substring(end), string.substring(0, start) + false_text + string.substring(end)]; + } +} + +function parseSty(sty, code) { + var starts = ["\\newcommand", "\\renewcommand", "\\DeclareRobustCommand", "\\defSpecFun"]; + var fs = require('fs'); + var path = require('path'); + var filePathRead = path.join(__dirname, sty); + var inp = fs.readFileSync(filePathRead, 'utf8'); + var cmds = {}; + var firstStart = findFirst(inp, starts, 0); + var first = firstStart[0]; + var next; + var nextStart; + var text; + var sections; + var prev = firstStart[1]; + while (first !== -1) { + firstStart = findFirst(inp, starts, first + prev.length); + next = firstStart[0]; + if (next === -1) { + text = inp.substring(first + prev.length, inp.indexOf("\n", first + prev.length)); + } + else { + text = inp.substring(first + prev.length, next); + } + if (text.indexOf("\n") !== -1) { + text = text.substring(0, text.indexOf("\n")); + } + if (prev === "\\defSpecFun") { + var ifx = parseIfx(text); + for (var condition in ifx) { + parseSpecFun(ifx[condition], code, String.fromCharCode(65 + parseInt(condition))); + } + } + else { + sections = findBracketSections(text.replace("\n", "")); + if (sections[0][0] != undefined && sections[1][0] != undefined) { + cmds[sections[0][0]] = sections[1][0]; + } + } + first = next; + prev = firstStart[1]; + } + return cmds; +} + +function createFromTemplate(template, func) { + var filePathRead = path.join(__dirname, "templates/" + template); + var inp = fs.readFileSync(filePathRead, 'utf8'); + var result = func(inp); + var filePathWrite = path.join(__dirname, template); + return fs.writeFileSync(filePathWrite, result, 'utf8'); +} + +module.exports.createMacroCode = function() { + try { + var code = [[], [], [], []]; + module.exports.other_literals3 = parseSty("DRMFfcns.sty", code); + module.exports.other_required = module.exports.other_literals3; + var pegjs = code[0]; + var ast = code[1]; + var astutil = code[2]; + var render = code[3]; + var csv = require('csv-parse/lib/sync'); + var fs = require('fs'); + var path = require('path'); + createFromTemplate("parser.pegjs", function(data) { + for (var i in [0, 1]) { + var start = ["lit\n =", "litstuff ="][i]; + var index = data.indexOf(start) + start.length; + pegjs.sort(function(a, b) {return b[0].length - a[0].length}); + pegjs = pegjs.map(function(a) {return a.join("")}).join("\n / "); + var result = data.substring(0, index) + "\n " + pegjs + data.substring(index).replace(" ", "\n / "); + data = result; + } + return data; + }); + createFromTemplate("ast.js", function(data) { + var start = "new Enum( 'Tex', {\n"; + var index = data.indexOf(start) + start.length; + var result = data.substring(0, index) + " " + ast.join("\n ") + "\n" + data.substring(index); + return result; + }); + createFromTemplate("astutil.js", function(data) { + var start = "ast.Tex.defineVisitor(\"contains_func\", {\n"; + var index = data.indexOf(start) + start.length; + var result = data.substring(0, index) + " " + astutil.join("\n ") + "\n" + data.substring(index); + return result; + }); + createFromTemplate("render.js", function(data) { + var start = "ast.Tex.defineVisitor(\"render_tex\", {\n"; + var index = data.indexOf(start) + start.length; + var result = data.substring(0, index) + " " + render.join("\n ") + "\n" + data.substring(index); + return result; + }); + } catch (e) {} +} + +module.exports.paren = arr2set(["\\sinh"]); +module.exports.other_literals3 = obj2map({}); +try{ //try catch to make sure program still runs without csv file + var csv = require('csv-parse/lib/sync'); + var fs = require('fs'); + var path = require('path'); + var filePathRead = path.join(__dirname, 'optionalFunctions.csv'); + var paren = []; + var inp = fs.readFileSync(filePathRead, 'utf8'); + var cmds = csv(inp, {columns: true}); + var c = 0; + while (c < cmds.length) { + if (cmds[c].type === 'paren') { + paren.push(cmds[c].command); + } + c++; + } + module.exports.paren = arr2set(paren); +} catch (e) {} + +module.exports.mediawiki_function_names = arr2set([ + "\\arccot", "\\arcsec", "\\arccsc", "\\sgn", "\\sen" +]); + +module.exports.other_literals1 = arr2set([ + "\\aleph", + "\\alpha", + "\\amalg", + "\\And", + "\\angle", + "\\approx", + "\\approxeq", + "\\ast", + "\\asymp", + "\\backepsilon", + "\\backprime", + "\\backsim", + "\\backsimeq", + "\\barwedge", + "\\Bbbk", + "\\because", + "\\beta", + "\\beth", + "\\between", + "\\bigcap", + "\\bigcirc", + "\\bigcup", + "\\bigodot", + "\\bigoplus", + "\\bigotimes", + "\\bigsqcup", + "\\bigstar", + "\\bigtriangledown", + "\\bigtriangleup", + "\\biguplus", + "\\bigvee", + "\\bigwedge", + "\\blacklozenge", + "\\blacksquare", + "\\blacktriangle", + "\\blacktriangledown", + "\\blacktriangleleft", + "\\blacktriangleright", + "\\bot", + "\\bowtie", + "\\Box", + "\\boxdot", + "\\boxminus", + "\\boxplus", + "\\boxtimes", + "\\bullet", + "\\bumpeq", + "\\Bumpeq", + "\\cap", + "\\Cap", + "\\CatalansConstant", + "\\cdot", + "\\cdots", + "\\centerdot", + "\\checkmark", + "\\chi", + "\\circ", + "\\circeq", + "\\circlearrowleft", + "\\circlearrowright", + "\\circledast", + "\\circledcirc", + "\\circleddash", + "\\circledS", + "\\clubsuit", + "\\colon", + "\\complement", + "\\cong", + "\\coprod", + "\\cup", + "\\Cup", + "\\curlyeqprec", + "\\curlyeqsucc", + "\\curlyvee", + "\\curlywedge", + "\\curvearrowleft", + "\\curvearrowright", + "\\dagger", + "\\daleth", + "\\dashv", + "\\ddagger", + "\\ddots", + "\\delta", + "\\Delta", + "\\diagdown", + "\\diagup", + "\\diamond", + "\\Diamond", + "\\diamondsuit", + "\\digamma", + "\\displaystyle", + "\\div", + "\\divideontimes", + "\\doteq", + "\\doteqdot", + "\\dotplus", + "\\dots", + "\\dotsb", + "\\dotsc", + "\\dotsi", + "\\dotsm", + "\\dotso", + "\\doublebarwedge", + "\\downdownarrows", + "\\downharpoonleft", + "\\downharpoonright", + "\\ell", + "\\emptyset", + "\\epsilon", + "\\eqcirc", + "\\eqsim", + "\\eqslantgtr", + "\\eqslantless", + "\\equiv", + "\\eta", + "\\eth", + "\\exists", + "\\fallingdotseq", + "\\Finv", + "\\flat", + "\\forall", + "\\frown", + "\\Game", + "\\gamma", + "\\Gamma", + "\\geq", + "\\geqq", + "\\geqslant", + "\\gets", + "\\gg", + "\\ggg", + "\\gimel", + "\\gnapprox", + "\\gneq", + "\\gneqq", + "\\gnsim", + "\\gtrapprox", + "\\gtrdot", + "\\gtreqless", + "\\gtreqqless", + "\\gtrless", + "\\gtrsim", + "\\gvertneqq", + "\\hbar", + "\\heartsuit", + //"\\hline", // moved to hline_function + "\\hookleftarrow", + "\\hookrightarrow", + "\\hslash", + "\\iff", + "\\iiiint", + "\\iiint", + "\\iint", + "\\Im", + "\\imath", + "\\implies", + "\\in", + "\\infty", + "\\injlim", + "\\int", + "\\intercal", + "\\iota", + "\\jmath", + "\\kappa", + "\\lambda", + "\\Lambda", + "\\land", + "\\ldots", + "\\leftarrow", + "\\Leftarrow", + "\\leftarrowtail", + "\\leftharpoondown", + "\\leftharpoonup", + "\\leftleftarrows", + "\\leftrightarrow", + "\\Leftrightarrow", + "\\leftrightarrows", + "\\leftrightharpoons", + "\\leftrightsquigarrow", + "\\leftthreetimes", + "\\leq", + "\\leqq", + "\\leqslant", + "\\lessapprox", + "\\lessdot", + "\\lesseqgtr", + "\\lesseqqgtr", + "\\lessgtr", + "\\lesssim", + "\\limits", // XXX only valid in certain contexts + "\\ll", + "\\Lleftarrow", + "\\lll", + "\\lnapprox", + "\\lneq", + "\\lneqq", + "\\lnot", + "\\lnsim", + "\\longleftarrow", + "\\Longleftarrow", + "\\longleftrightarrow", + "\\Longleftrightarrow", + "\\longmapsto", + "\\longrightarrow", + "\\Longrightarrow", + "\\looparrowleft", + "\\looparrowright", + "\\lor", + "\\lozenge", + "\\Lsh", + "\\ltimes", + "\\lVert", + "\\lvertneqq", + "\\mapsto", + "\\measuredangle", + "\\mho", + "\\mid", + "\\mod", + "\\models", + "\\mp", + "\\mu", + "\\multimap", + "\\nabla", + "\\natural", + "\\ncong", + "\\nearrow", + "\\neg", + "\\neq", + "\\nexists", + "\\ngeq", + "\\ngeqq", + "\\ngeqslant", + "\\ngtr", + "\\ni", + "\\nleftarrow", + "\\nLeftarrow", + "\\nleftrightarrow", + "\\nLeftrightarrow", + "\\nleq", + "\\nleqq", + "\\nleqslant", + "\\nless", + "\\nmid", + "\\nolimits", // XXX see \limits + "\\not", + "\\notin", + "\\nparallel", + "\\nprec", + "\\npreceq", + "\\nrightarrow", + "\\nRightarrow", + "\\nshortmid", + "\\nshortparallel", + "\\nsim", + "\\nsubseteq", + "\\nsubseteqq", + "\\nsucc", + "\\nsucceq", + "\\nsupseteq", + "\\nsupseteqq", + "\\ntriangleleft", + "\\ntrianglelefteq", + "\\ntriangleright", + "\\ntrianglerighteq", + "\\nu", + "\\nvdash", + "\\nVdash", + "\\nvDash", + "\\nVDash", + "\\nwarrow", + "\\odot", + "\\oint", + "\\omega", + "\\Omega", + "\\ominus", + "\\oplus", + "\\oslash", + "\\otimes", + //"\\overbrace", // moved to ar1nb (grabs trailing sub/superscript) + "\\P", + "\\parallel", + "\\partial", + "\\perp", + "\\phi", + "\\Phi", + "\\pi", + "\\Pi", + "\\pitchfork", + "\\pm", + "\\prec", + "\\precapprox", + "\\preccurlyeq", + "\\preceq", + "\\precnapprox", + "\\precneqq", + "\\precnsim", + "\\precsim", + "\\prime", + "\\prod", + "\\projlim", + "\\propto", + "\\psi", + "\\Psi", + "\\qquad", + "\\quad", + "\\Re", + "\\rho", + "\\rightarrow", + "\\Rightarrow", + "\\rightarrowtail", + "\\rightharpoondown", + "\\rightharpoonup", + "\\rightleftarrows", + "\\rightrightarrows", + "\\rightsquigarrow", + "\\rightthreetimes", + "\\risingdotseq", + "\\Rrightarrow", + "\\Rsh", + "\\rtimes", + "\\rVert", + "\\S", + "\\scriptscriptstyle", + "\\scriptstyle", + "\\searrow", + "\\setminus", + "\\sharp", + "\\shortmid", + "\\shortparallel", + "\\sigma", + "\\Sigma", + "\\sim", + "\\simeq", + "\\smallfrown", + "\\smallsetminus", + "\\smallsmile", + "\\smile", + "\\spadesuit", + "\\sphericalangle", + "\\sqcap", + "\\sqcup", + "\\sqsubset", + "\\sqsubseteq", + "\\sqsupset", + "\\sqsupseteq", + "\\square", + "\\star", + "\\subset", + "\\Subset", + "\\subseteq", + "\\subseteqq", + "\\subsetneq", + "\\subsetneqq", + "\\succ", + "\\succapprox", + "\\succcurlyeq", + "\\succeq", + "\\succnapprox", + "\\succneqq", + "\\succnsim", + "\\succsim", + "\\sum", + "\\supset", + "\\Supset", + "\\supseteq", + "\\supseteqq", + "\\supsetneq", + "\\supsetneqq", + "\\surd", + "\\swarrow", + "\\tau", + "\\textstyle", + "\\therefore", + "\\theta", + "\\Theta", + "\\thickapprox", + "\\thicksim", + "\\times", + "\\to", + "\\top", + "\\triangle", + "\\triangledown", + "\\triangleleft", + "\\trianglelefteq", + "\\triangleq", + "\\triangleright", + "\\trianglerighteq", + //"\\underbrace", // moved to ar1nb (grabs trailing sub/superscript) + "\\upharpoonleft", + "\\upharpoonright", + "\\uplus", + "\\upsilon", + "\\Upsilon", + "\\upuparrows", + "\\varepsilon", + "\\varinjlim", + "\\varkappa", + "\\varliminf", + "\\varlimsup", + "\\varnothing", + "\\varphi", + "\\varpi", + "\\varprojlim", + "\\varpropto", + "\\varrho", + "\\varsigma", + "\\varsubsetneq", + "\\varsubsetneqq", + "\\varsupsetneq", + "\\varsupsetneqq", + "\\vartheta", + "\\vartriangle", + "\\vartriangleleft", + "\\vartriangleright", + "\\vdash", + "\\Vdash", + "\\vDash", + "\\vdots", + "\\vee", + "\\veebar", + "\\vline", + "\\Vvdash", + "\\wedge", + "\\wp", + "\\wr", + "\\xi", + "\\Xi", + "\\zeta" +]); + +// text-mode literals; enclose in \mbox +module.exports.other_literals2 = arr2set([ + "\\AA", + "\\Coppa", + "\\coppa", + "\\Digamma", + "\\euro", + "\\geneuro", + "\\geneuronarrow", + "\\geneurowide", + "\\Koppa", + "\\koppa", + "\\officialeuro", + "\\Sampi", + "\\sampi", + "\\Stigma", + "\\stigma", + "\\textvisiblespace", + "\\varstigma" +]); + +module.exports.other_literals3 = obj2map({ + "\\C": "\\mathbb{C}", + "\\H": "\\mathbb{H}", + "\\N": "\\mathbb{N}", + "\\Q": "\\mathbb{Q}", + "\\R": "\\mathbb{R}", + "\\Z": "\\mathbb{Z}", + "\\alef": "\\aleph", + "\\alefsym": "\\aleph", + "\\Alpha": "\\mathrm{A}", + "\\and": "\\land", + "\\ang": "\\angle", + "\\Beta": "\\mathrm{B}", + "\\bull": "\\bullet", + "\\Chi": "\\mathrm{X}", + "\\clubs": "\\clubsuit", + "\\cnums": "\\mathbb{C}", + "\\Complex": "\\mathbb{C}", + "\\Dagger": "\\ddagger", + "\\diamonds": "\\diamondsuit", + "\\Doteq": "\\doteqdot", + "\\doublecap": "\\Cap", + "\\doublecup": "\\Cup", + "\\empty": "\\emptyset", + "\\Epsilon": "\\mathrm{E}", + "\\Eta": "\\mathrm{H}", + "\\exist": "\\exists", + "\\ge": "\\geq", + "\\gggtr": "\\ggg", + "\\hAar": "\\Leftrightarrow", + "\\harr": "\\leftrightarrow", + "\\Harr": "\\Leftrightarrow", + "\\hearts": "\\heartsuit", + "\\image": "\\Im", + "\\infin": "\\infty", + "\\Iota": "\\mathrm{I}", + "\\isin": "\\in", + "\\Kappa": "\\mathrm{K}", + "\\larr": "\\leftarrow", + "\\Larr": "\\Leftarrow", + "\\lArr": "\\Leftarrow", + "\\le": "\\leq", + "\\lrarr": "\\leftrightarrow", + "\\Lrarr": "\\Leftrightarrow", + "\\lrArr": "\\Leftrightarrow", + "\\Mu": "\\mathrm{M}", + "\\natnums": "\\mathbb{N}", + "\\ne": "\\neq", + "\\Nu": "\\mathrm{N}", + "\\O": "\\emptyset", + "\\omicron": "\\mathrm{o}", + "\\Omicron": "\\mathrm{O}", + "\\or": "\\lor", + "\\part": "\\partial", + "\\plusmn": "\\pm", + "\\rarr": "\\rightarrow", + "\\Rarr": "\\Rightarrow", + "\\rArr": "\\Rightarrow", + "\\real": "\\Re", + "\\reals": "\\mathbb{R}", + "\\Reals": "\\mathbb{R}", + "\\restriction": "\\upharpoonright", + "\\Rho": "\\mathrm{P}", + "\\sdot": "\\cdot", + "\\sect": "\\S", + "\\spades": "\\spadesuit", + "\\sub": "\\subset", + "\\sube": "\\subseteq", + "\\supe": "\\supseteq", + "\\Tau": "\\mathrm{T}", + "\\thetasym": "\\vartheta", + "\\varcoppa": "\\mbox{\\coppa}", + "\\weierp": "\\wp", + "\\Zeta": "\\mathrm{Z}" +}); + +module.exports.big_literals = arr2set([ + "\\big", + "\\Big", + "\\bigg", + "\\Bigg", + "\\biggl", + "\\Biggl", + "\\biggr", + "\\Biggr", + "\\bigl", + "\\Bigl", + "\\bigr", + "\\Bigr" +]); + +module.exports.other_delimiters1 = arr2set([ + "\\backslash", + "\\downarrow", + "\\Downarrow", + "\\langle", + "\\lbrace", + "\\lceil", + "\\lfloor", + "\\llcorner", + "\\lrcorner", + "\\rangle", + "\\rbrace", + "\\rceil", + "\\rfloor", + "\\rightleftharpoons", + "\\twoheadleftarrow", + "\\twoheadrightarrow", + "\\ulcorner", + "\\uparrow", + "\\Uparrow", + "\\updownarrow", + "\\Updownarrow", + "\\urcorner", + "\\Vert", + "\\vert", + "\\lbrack", + "\\rbrack" +]); + +module.exports.other_delimiters2 = obj2map({ + "\\darr": "\\downarrow", + "\\dArr": "\\Downarrow", + "\\Darr": "\\Downarrow", + "\\lang": "\\langle", + "\\rang": "\\rangle", + "\\uarr": "\\uparrow", + "\\uArr": "\\Uparrow", + "\\Uarr": "\\Uparrow" +}); + +module.exports.fun_ar1 = arr2set([ + "\\acute", + "\\bar", + "\\bcancel", + "\\bmod", + "\\boldsymbol", + "\\breve", + "\\cancel", + "\\ce", + "\\check", + "\\ddot", + "\\dot", + "\\emph", + "\\grave", + "\\hat", + //"\\mathbb", // moved to fun_ar1nb + //"\\mathbf", // moved to fun_ar1nb + "\\mathbin", + "\\mathcal", + "\\mathclose", + "\\mathfrak", + "\\mathit", + "\\mathop", + "\\mathopen", + "\\mathord", + "\\mathpunct", + "\\mathrel", + //"\\mathrm", // moved to fun_ar1nb + "\\mathsf", + "\\mathtt", + //"\\operatorname", // already exists in fun_ar1nb + "\\overleftarrow", + "\\overleftrightarrow", + "\\overline", + "\\overrightarrow", + "\\pmod", + "\\sqrt", + "\\textbf", + "\\textit", + "\\textrm", + "\\textsf", + "\\texttt", + "\\tilde", + "\\underline", + "\\vec", + "\\widehat", + "\\widetilde", + "\\xcancel", + "\\xleftarrow", + "\\xrightarrow" +]); + +module.exports.other_fun_ar1 = obj2map({ + "\\Bbb": "\\mathbb", + "\\bold": "\\mathbf" +}); + +module.exports.fun_ar1nb = arr2set([ + "\\operatorname", + "\\overbrace", + "\\mathbb", + "\\mathbf", + "\\mathrm", + "\\underbrace" +]); + +module.exports.fun_ar1opt = arr2set([ + "\\sqrt", "\\xleftarrow", "\\xrightarrow" +]); + +module.exports.fun_ar2 = arr2set([ + "\\binom", + "\\cancelto", + "\\cfrac", + "\\dbinom", + "\\dfrac", + "\\frac", + "\\overset", + "\\pochhammer", + "\\stackrel", + "\\tbinom", + "\\tfrac", + "\\underset" +]); + +module.exports.fun_ar3 = arr2set([ + "\\qPochhammer" +]); + +module.exports.jacobi = arr2set([ + "\\Jacobi" +]); + +module.exports.laguerre = arr2set([ + "\\Laguerre" +]); + +module.exports.elliptical_jacobi = arr2set([ + "\\Jacobi" +]); + +module.exports.fun_ar2nb = arr2set([ + "\\sideset" +]); + +module.exports.fun_infix = arr2set([ + "\\atop", + "\\choose", + "\\over" +]); + +module.exports.declh_function = arr2set([ + "\\rm", + "\\it", + "\\cal", + "\\bf" +]); + +module.exports.left_function = arr2set([ "\\left" ]); +module.exports.right_function = arr2set([ "\\right" ]); +module.exports.hline_function = arr2set([ "\\hline" ]); +module.exports.definecolor_function = arr2set([ "\\definecolor" ]); +module.exports.color_function = arr2set([ "\\color", "\\pagecolor" ]); + +// ------------------------------------------------------ +// Package dependencies for various allowed commands. + +module.exports.ams_required = arr2set([ + "\\text", + "\\begin{matrix}", + "\\begin{pmatrix}", + "\\begin{bmatrix}", + "\\begin{Bmatrix}", + "\\begin{vmatrix}", + "\\begin{Vmatrix}", + "\\begin{aligned}", + "\\begin{alignedat}", + "\\begin{smallmatrix}", + "\\begin{cases}", + + "\\ulcorner", + "\\urcorner", + "\\llcorner", + "\\lrcorner", + "\\twoheadleftarrow", + "\\twoheadrightarrow", + "\\xleftarrow", + "\\xrightarrow", + //"\\angle", // in texvc, but ams not actually required + "\\sqsupset", + "\\sqsubset", + //"\\sqsupseteq", // in texvc, but ams not actually required + //"\\sqsubseteq", // in texvc, but ams not actually required + "\\smallsetminus", + "\\And", + //"\\sqcap", // in texvc, but ams not actually required + //"\\sqcup", // in texvc, but ams not actually required + "\\implies", + "\\mod", + "\\Diamond", + "\\dotsb", + "\\dotsc", + "\\dotsi", + "\\dotsm", + "\\dotso", + "\\lVert", + "\\rVert", + "\\nmid", + "\\lesssim", + "\\ngeq", + "\\smallsmile", + "\\smallfrown", + "\\nleftarrow", + "\\nrightarrow", + "\\trianglelefteq", + "\\trianglerighteq", + "\\square", + "\\checkmark", + "\\supsetneq", + "\\subsetneq", + "\\Box", + "\\nleq", + "\\upharpoonright", + "\\upharpoonleft", + "\\downharpoonright", + "\\downharpoonleft", + //"\\rightharpoonup", // in texvc, but ams not actually required + //"\\rightharpoondown", // in texvc, but ams not actually required + //"\\leftharpoonup", // in texvc, but ams not actually required + //"\\leftharpoondown", // in texvc, but ams not actually required + "\\nless", + "\\Vdash", + "\\vDash", + "\\varkappa", + "\\digamma", + "\\beth", + "\\daleth", + "\\gimel", + "\\complement", + "\\eth", + "\\hslash", + "\\mho", + "\\Finv", + "\\Game", + "\\varlimsup", + "\\varliminf", + "\\varinjlim", + "\\varprojlim", + "\\injlim", + "\\projlim", + "\\iint", + "\\iiint", + "\\iiiint", + "\\varnothing", + "\\overleftrightarrow", + "\\binom", + "\\dbinom", + "\\tbinom", + "\\sideset", + "\\underset", + "\\overset", + "\\dfrac", + "\\tfrac", + "\\cfrac", + //"\\bigl", // in texvc, but ams not actually required + //"\\bigr", // in texvc, but ams not actually required + //"\\Bigl", // in texvc, but ams not actually required + //"\\Bigr", // in texvc, but ams not actually required + //"\\biggl", // in texvc, but ams not actually required + //"\\biggr", // in texvc, but ams not actually required + //"\\Biggl", // in texvc, but ams not actually required + //"\\Biggr", // in texvc, but ams not actually required + "\\vartriangle", + "\\triangledown", + "\\lozenge", + "\\circledS", + "\\measuredangle", + "\\nexists", + "\\Bbbk", + "\\backprime", + "\\blacktriangle", + "\\blacktriangledown", + "\\blacksquare", + "\\blacklozenge", + "\\bigstar", + "\\sphericalangle", + "\\diagup", + "\\diagdown", + "\\dotplus", + "\\Cap", + "\\Cup", + "\\barwedge", + "\\veebar", + "\\doublebarwedge", + "\\boxminus", + "\\boxtimes", + "\\boxdot", + "\\boxplus", + "\\divideontimes", + "\\ltimes", + "\\rtimes", + "\\leftthreetimes", + "\\rightthreetimes", + "\\curlywedge", + "\\curlyvee", + "\\circleddash", + "\\circledast", + "\\circledcirc", + "\\centerdot", + "\\intercal", + "\\leqq", + "\\leqslant", + "\\eqslantless", + "\\lessapprox", + "\\approxeq", + "\\lessdot", + "\\lll", + "\\lessgtr", + "\\lesseqgtr", + "\\lesseqqgtr", + "\\doteqdot", + "\\risingdotseq", + "\\fallingdotseq", + "\\backsim", + "\\backsimeq", + "\\subseteqq", + "\\Subset", + "\\preccurlyeq", + "\\curlyeqprec", + "\\precsim", + "\\precapprox", + "\\vartriangleleft", + "\\Vvdash", + "\\bumpeq", + "\\Bumpeq", + "\\geqq", + "\\geqslant", + "\\eqslantgtr", + "\\gtrsim", + "\\gtrapprox", + "\\eqsim", + "\\gtrdot", + "\\ggg", + "\\gtrless", + "\\gtreqless", + "\\gtreqqless", + "\\eqcirc", + "\\circeq", + "\\triangleq", + "\\thicksim", + "\\thickapprox", + "\\supseteqq", + "\\Supset", + "\\succcurlyeq", + "\\curlyeqsucc", + "\\succsim", + "\\succapprox", + "\\vartriangleright", + "\\shortmid", + "\\shortparallel", + "\\between", + "\\pitchfork", + "\\varpropto", + "\\blacktriangleleft", + "\\therefore", + "\\backepsilon", + "\\blacktriangleright", + "\\because", + "\\nleqslant", + "\\nleqq", + "\\lneq", + "\\lneqq", + "\\lvertneqq", + "\\lnsim", + "\\lnapprox", + "\\nprec", + "\\npreceq", + "\\precneqq", + "\\precnsim", + "\\precnapprox", + "\\nsim", + "\\nshortmid", + "\\nvdash", + "\\nVdash", + "\\ntriangleleft", + "\\ntrianglelefteq", + "\\nsubseteq", + "\\nsubseteqq", + "\\varsubsetneq", + "\\subsetneqq", + "\\varsubsetneqq", + "\\ngtr", + "\\ngeqslant", + "\\ngeqq", + "\\gneq", + "\\gneqq", + "\\gvertneqq", + "\\gnsim", + "\\gnapprox", + "\\nsucc", + "\\nsucceq", + "\\succneqq", + "\\succnsim", + "\\succnapprox", + "\\ncong", + "\\nshortparallel", + "\\nparallel", + "\\nvDash", + "\\nVDash", + "\\ntriangleright", + "\\ntrianglerighteq", + "\\nsupseteq", + "\\nsupseteqq", + "\\varsupsetneq", + "\\supsetneqq", + "\\varsupsetneqq", + "\\leftleftarrows", + "\\leftrightarrows", + "\\Lleftarrow", + "\\leftarrowtail", + "\\looparrowleft", + "\\leftrightharpoons", + "\\curvearrowleft", + "\\circlearrowleft", + "\\Lsh", + "\\upuparrows", + "\\rightrightarrows", + "\\rightleftarrows", + "\\Rrightarrow", + "\\rightarrowtail", + "\\looparrowright", + "\\curvearrowright", + "\\circlearrowright", + "\\Rsh", + "\\downdownarrows", + "\\multimap", + "\\leftrightsquigarrow", + "\\rightsquigarrow", + "\\nLeftarrow", + "\\nleftrightarrow", + "\\nRightarrow", + "\\nLeftrightarrow", + + //"\\mathit", // in texvc, but ams not actually required + //"\\mathrm", // in texvc, but ams not actually required + //"\\mathord", // in texvc, but ams not actually required + //"\\mathop", // in texvc, but ams not actually required + //"\\mathbin", // in texvc, but ams not actually required + //"\\mathrel", // in texvc, but ams not actually required + //"\\mathopen", // in texvc, but ams not actually required + //"\\mathclose", // in texvc, but ams not actually required + //"\\mathpunct", // in texvc, but ams not actually required + "\\boldsymbol", + "\\mathbb", + //"\\mathbf", // in texvc, but ams not actually required + //"\\mathsf", // in texvc, but ams not actually required + //"\\mathcal", // in texvc, but ams not actually required + //"\\mathtt", // in texvc, but ams not actually required + "\\mathfrak", + "\\operatorname", + "\\mathbb{R}" +]); + +module.exports.cancel_required = arr2set([ + "\\bcancel", + "\\cancel", + "\\xcancel", + "\\cancelto" +]); + +module.exports.color_required = arr2set([ + "\\color", + "\\pagecolor", + "\\definecolor" +]); + +module.exports.euro_required = arr2set([ + "\\euro", + "\\geneuro", + "\\geneuronarrow", + "\\geneurowide", + "\\officialeuro" +]); + +module.exports.teubner_required = arr2set([ + "\\Coppa", + "\\coppa", + "\\Digamma", + "\\Koppa", + "\\koppa", + "\\Sampi", + "\\sampi", + "\\Stigma", + "\\stigma", + "\\varstigma" +]); + +module.exports.mhchem_required = arr2set([ + "\\ce" +]); From 63183d53862d3f0d97ae0ca9ca52fde23aee5bbb Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Mon, 18 Jul 2016 11:04:56 -0400 Subject: [PATCH 10/14] Change index.js to more closely match previous format. --- bin/texer | 59 --------------------------------------- lib/index.js | 76 ++++++++++++++++++++++++++------------------------ lib/texutil.js | 4 +-- 3 files changed, 41 insertions(+), 98 deletions(-) delete mode 100755 bin/texer diff --git a/bin/texer b/bin/texer deleted file mode 100755 index 3199d36..0000000 --- a/bin/texer +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env node - -var PACKAGES = ['ams', 'cancel', 'color', 'euro', 'teubner']; - -var program = require('commander'); -var util = require('util'); - -var json = require('../package.json'); - -program - .version(json.version) - .usage('[options] ') - .option('--rebuild', 'Rebuild PEGjs grammar before parsing') - .option('-v, --verbose', 'Show verbose error information') - .option('-D, --debug', 'Show stack trace on failure') - .option('--usemathrm', 'Use \\mathrm instead of \\mbox to escape some text literals') - .option('--usemhchem', 'Allow commands from the mhchem package') - .option('--semanticlatex', 'Include semantic LaTeX') - .parse(process.argv); - -PACKAGES.forEach(function(pkg) { - var msg = 'Fail validation if input requires the '; - if (pkg === 'ams') { - msg += 'ams* packages'; - } else { - msg += pkg + ' package'; - } - program.option('--no-'+pkg, msg); -}); - -program.parse(process.argv); - -var input = program.args.join(' '); - -if (program.rebuild) { - require('../lib/build-parser'); -} - -var texer = require('../'); -var result = texer.check(input, { debug: program.debug, usemathrm:program.usemathrm, usemhchem: program.usemhchem, semanticlatex:program.semanticlatex }); - -// check required packages -PACKAGES.forEach(function(pkg) { - if (result[pkg+'_required'] && !program[pkg]) { - result.status = 'F'; - result.details = result[pkg+'_required']; - }; -}); - -// output result -if (result.status === '+') { - util.print(result.status + (result.output || '')); -} else if (result.status === 'F' || program.verbose) { - util.print(result.status + (result.details || '')); -} else { - util.print(result.status); -} - -process.exit(result.status === '+' ? 0 : 1); diff --git a/lib/index.js b/lib/index.js index 01074fe..d25e2b4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,57 +7,60 @@ module.exports = { version: json.version // version # for this package }; -var tu = require('./texutil'); -var Parser = require('./parser'); -var render = module.exports.render = require('./render'); - -module.exports.ast = require('./ast'); -module.exports.parse = Parser.parse.bind(Parser); -module.exports.SyntaxError = Parser.SyntaxError; +module.exports.initialize = function(rebuild) { + if (rebuild) { + require("./build-parser"); + } + module.exports.tu = require("./texutil"); + var Parser = module.exports.Parser = require('./parser'); + module.exports.render = require('./render'); -var astutil = require('./astutil'); -var contains_func = module.exports.contains_func = astutil.contains_func; + module.exports.ast = require('./ast'); + module.exports.astutil = require('./astutil'); + module.exports.parse = Parser.parse.bind(Parser); + module.exports.SyntaxError = Parser.SyntaxError; +}; -var check = module.exports.check = function(input, options) { - /* status is one character: - * + : success! result is in 'output' - * E : Lexer exception raised - * F : TeX function not recognized - * S : Parsing error - * - : Generic/Default failure code. Might be an invalid argument, - * output file already exist, a problem with an external - * command ... - */ - try { - if( typeof options === "undefined" ){ +// for only math mode input +module.exports.makeReplacements = function(input, options) { + if (typeof options === "undefined") { options = {}; options.usemathrm = false; - options.usmhchem = false; + options.usemhchem = false; options.semanticlatex = false; } - // allow user to pass a parsed AST as input, as well as a string - if (typeof(input)==='string') { - input = Parser.parse(input, {usemathrm:options.usemathrm, semanticlatex:options.semanticlatex}); - } - var output = render(input); - var result = { status: '+', output: output }; - ['ams', 'cancel', 'color', 'euro', 'teubner', 'mhchem'].forEach(function(pkg) { - pkg = pkg + '_required'; - result[pkg] = astutil.contains_func(input, tu[pkg]); - }); + // allow user to pass a parsed AST as input, as well as a string + if (typeof(input) === 'string') { + input = module.exports.parse(input, {usemathrm:options.usemathrm, semanticlatex:options.semanticlatex}); + } + var result = module.exports.render(input); + ['ams', 'cancel', 'color', 'euro', 'teubner', 'mhchem'].forEach(function(pkg) { + pkg = pkg + '_required'; + result[pkg] = module.exports.astutil.contains_func(input, module.exports.tu[pkg]); + }); + return result; +}; + +// will be for all latex; separated from makeReplacements for testing purposes +module.exports.texer = function(input, options) { + try { + var result = { + status: "+", output: module.exports.makeReplacements(input, options) + }; if (!options.usemhchem){ if (result.mhchem_required){ return { - status: 'C', details: "mhchem package required." + status: 'C', details: "mhchem package is required" }; } } return result; - } catch (e) { + } + catch (e) { if (options && options.debug) { throw e; } - if (e instanceof Parser.SyntaxError) { + if (e instanceof module.exports.SyntaxError) { if (e.message === 'Illegal TeX function') { return { status: 'F', details: e.found, @@ -69,7 +72,6 @@ var check = module.exports.check = function(input, options) { offset: e.offset, line: e.line, column: e.column }; } - console.log(e.toString()); return { status: '-', details: e.toString() }; } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/lib/texutil.js b/lib/texutil.js index e2ccba1..18554e7 100644 --- a/lib/texutil.js +++ b/lib/texutil.js @@ -259,11 +259,11 @@ module.exports.createMacroCode = function() { var fs = require('fs'); var path = require('path'); createFromTemplate("parser.pegjs", function(data) { + pegjs.sort(function(a, b) {return b[0].length - a[0].length}); + pegjs = pegjs.map(function(a) {return a.join("")}).join("\n / "); for (var i in [0, 1]) { var start = ["lit\n =", "litstuff ="][i]; var index = data.indexOf(start) + start.length; - pegjs.sort(function(a, b) {return b[0].length - a[0].length}); - pegjs = pegjs.map(function(a) {return a.join("")}).join("\n / "); var result = data.substring(0, index) + "\n " + pegjs + data.substring(index).replace(" ", "\n / "); data = result; } From 130fb028d2c7b6e11c61033643bb96f354f2473c Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Tue, 9 Aug 2016 15:16:32 -0400 Subject: [PATCH 11/14] Add support for text mode and revamp .sty parsing (unfinished, only for tracking progress) --- .gitignore | 7 + lib/KLSadd.tex | 2637 ++++++++++++++++++++++++++++++++++++ lib/build-parser.js | 16 +- lib/index.js | 34 +- lib/mathmode.js | 2 +- lib/templates/parser.pegjs | 3 +- lib/textmode.js | 238 ++++ lib/texutil.js | 217 ++- test/mathmode.js | 42 + 9 files changed, 3106 insertions(+), 90 deletions(-) create mode 100644 lib/KLSadd.tex create mode 100644 lib/textmode.js create mode 100755 test/mathmode.js diff --git a/.gitignore b/.gitignore index f06f304..097950e 100644 --- a/.gitignore +++ b/.gitignore @@ -85,4 +85,11 @@ jspm_packages # Optional REPL history .node_repl_history +# Generated code +lib/ast.js +lib/astutil.js +lib/parser.pegjs +lib/parser.js +lib/render.js + texer.iml \ No newline at end of file diff --git a/lib/KLSadd.tex b/lib/KLSadd.tex new file mode 100644 index 0000000..f9f3e63 --- /dev/null +++ b/lib/KLSadd.tex @@ -0,0 +1,2637 @@ +%until 185 +\documentclass[twoside,11pt]{article} +\usepackage[pdftex]{hyperref} +\hypersetup{ + colorlinks = true, + allcolors = {red}, +} +\usepackage{color} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{xparse} +\usepackage{cite} +\renewcommand\citeform[1]{K#1} +\setlength{\textwidth}{16cm} +\setlength{\textheight}{21cm} +\setlength{\topmargin}{0cm} +\setlength{\oddsidemargin}{0.2mm} +\setlength{\evensidemargin}{0.2mm} + +\newcommand\sa{\smallskipamount} +\newcommand\sLP{\\[\sa]} +\newcommand\sPP{\\[\sa]\indent} +\newcommand\ba{\bigskipamount} +\newcommand\bLP{\\[\ba]} +\newcommand\CC{\mathbb{C}} +\newcommand\RR{\mathbb{R}} +\newcommand\ZZ{\mathbb{Z}} +\newcommand\al\alpha +\newcommand\be\beta +\newcommand\ga\gamma +\newcommand\de\delta +\newcommand\tha\theta +\newcommand\la\lambda +\newcommand\om\omega +\newcommand\Ga{\Gamma} +\newcommand\half{\frac12} +\newcommand\thalf{\tfrac12} +\newcommand\iy\infty +\newcommand\wt{\widetilde} +\newcommand\const{{\rm const.}\,} +\newcommand\Zpos{\ZZ_{>0}} +\newcommand\Znonneg{\ZZ_{\ge0}} +\newcommand{\hyp}[5]{\,\mbox{}_{#1}F_{#2}\!\left( + \genfrac{}{}{0pt}{}{#3}{#4};#5\right)} +\newcommand{\qhyp}[5]{\,\mbox{}_{#1}\phi_{#2}\!\left( + \genfrac{}{}{0pt}{}{#3}{#4};#5\right)} +\newcommand\LHS{left-hand side} +\newcommand\RHS{right-hand side} +\renewcommand\Re{{\rm Re}\,} +\renewcommand\Im{{\rm Im}\,} + +\NewDocumentCommand\mycite{m g}{% + \IfNoValueTF{#2} + {[\hyperlink{#1}{#1}]} + {[\hyperlink{#1}{#1}, #2]}% +} +\newcommand\mybibitem[1]{\bibitem[#1]{#1}\hypertarget{#1}{}} + +%\NewDocumentCommand{\myciteKLS}{m g}{% +% \IfNoValueTF{#2} +% {\hyperlink{KLS#1}{[#1]}} +% {\hyperlink{KLS#1}{[#1, #2]}}% +%} +\NewDocumentCommand{\myciteKLS}{m g}{% + \IfNoValueTF{#2} + {[\hyperlink{KLS#1}{#1}]} + {[\hyperlink{KLS#1}{#1}, #2]}% +} +\newcommand\mybibitemKLS[1]{\bibitem[#1]{#1}\hypertarget{KLS#1}{}} + +\begin{document} + +\title{Additions to the formula lists in +``Hypergeometric orthogonal polynomials and their $q$-analogues'' +by Koekoek, Lesky and Swarttouw} +\author{Tom H. Koornwinder} +\date{June 19, 2015} +\maketitle +\begin{abstract} +This report gives a rather arbitrary choice of formulas for +($q$-)hypergeometric orthogonal polynomials which the author missed +while consulting Chapters 9 and 14 in the book +``Hypergeometric orthogonal polynomials and their $q$-analogues'' +by Koekoek, Lesky and Swarttouw. The systematics of these chapters will be followed +here, in particular for the numbering of subsections and of references. +\end{abstract} +% +\subsection*{Introduction} +\label{sec_intro} +This report contains some formulas about ($q$-)hypergeometric +orthogonal polynomials which I missed but wanted to use +while consulting Chapters 9 and 14 in the book \mycite{KLS}: +\sLP +R. Koekoek, P.~A. Lesky and R.~F. Swarttouw, +{\em Hypergeometric orthogonal polynomials and their $q$-analogues}, +Springer-Verlag, 2010. +\sLP +These chapters form together the (slightly extended) successor of the report +\sLP +R.~Koekoek and R.~F. Swarttouw, +{\em The Askey-scheme of hypergeometric orthogonal +polynomials and its $q$-analogue}, +Report 98-17, Faculty of Technical Mathematics and Informatics, +Delft University of Technology, 1998; +\url{http://aw.twi.tudelft.nl/~koekoek/askey/}. +\sPP +Certainly these chapters give complete lists of formulas of special type, for instance +orthogonality relations and three-term recurrence relations. But outside these narrow +categories there are many other +formulas for ($q$-)orthogonal polynomials which one wants to have available. +Often one can find the desired formula in one of the +\hyperref[sec_ref1]{standard references} listed at the end of this report. +Sometimes it is only available in a journal or a less common monograph. +Just for my own comfort, I have brought together some of these formulas. +This will possibly also be helpful for some other users. + +Usually, any type of formula I give for a special class of polynomials, will suggest +a similar formula for many other classes, but I have not aimed at completeness +by filling in a formula of such type at all places. The resulting choice of formulas is +rather arbitrary, just depending on the formulas which I happened to need or which raised my interest. +For each formula I give a suitable reference or I sketch a +proof. +It is my intention to gradually extend this collection of formulas. +% +\subsection*{Conventions} +\label{sec_conv} +The (x.y) and (x.y.z) type subsection numbers, the +(x.y.z) type formula numbers, and the [x] type citation numbers +refer to \mycite{KLS}. +The (x) type formula numbers refer to this manuscript and the [Kx] type citation numbers refer to citations which are not in \mycite{KLS}. +Some standard references like \mycite{DLMF} +are given by special acronyms. + +$N$ is always a positive integer. Always assume $n$ to be a nonnegative +integer or, if $N$ is present, to be in $\{0,1,\ldots,N\}$. +Throughout assume $00$ and $0\le\frac{4xy}{(1-x-y)^2}<1$. +% +\paragraph{$q$-Hypergeometric series of base $q^{-1}$} +By \mycite{GR}{Exercise 1.4(i)}: +\begin{equation} +\qhyp rs{a_1,\ldots,a_r}{b_1,\ldots b_s}{q^{-1},z} +=\qhyp{s+1}s{a_1^{-1},\ldots a_r^{-1},0,\ldots,0} +{b_1^{-1},\ldots,b_s^{-1}}{q,\frac{qa_1\ldots a_rz}{b_1\ldots b_s}} +\label{154} +\end{equation} +for $r\le s+1$, $a_1,\ldots,a_r,b_1,\ldots,b_s\ne0$. +In the non-terminating case, for $00$ and $(c,d)=(\overline a,\overline b)$ or $(\overline b,\overline a)$.\\ +Thus, under these assumptions, the continuous Hahn polynomial +$p_n(x;a,b,c,d)$ +is symmetric in $a,b$ and in $c,d$. +This follows from the orthogonality relation (9.4.2) +together with the value of its coefficient of $x^n$ given in (9.4.4b).\\ +As a consequence, it is sufficient to give generating function (9.4.11). Then the generating +function (9.4.12) will follow by symmetry in the parameters. +% +\paragraph{Uniqueness of orthogonality measure} +The coefficient of $p_{n-1}(x)$ in (9.4.4) behaves as $O(n^2)$ as $n\to\iy$. +Hence \eqref{93} holds, by which the orthogonality measure is unique. +% +\paragraph{Special cases} +In the following special case there is a reduction to +Meixner-Pollaczek: +\begin{equation} +p_n(x;a,a+\thalf,a,a+\thalf)= +\frac{(2a)_n (2a+\thalf)_n}{(4a)_n}\,P_n^{(2a)}(2x;\thalf\pi). +\end{equation} +See \myciteKLS{342}{(2.6)} (note that in \myciteKLS{342}{(2.3)} the +Meixner-Pollaczek polynonmials are defined different from (9.7.1), +without a constant factor in front). + +For $0\al>-1,\quad m,n<-\thalf(\al+\be+1),\\ +&\frac{h_n}{h_0}= +\frac{n+\al+\be+1}{2n+\al+\be+1}\, +\frac{(\al+1)_n(\be+1)_n}{(\al+\be+2)_n\,n!}\,,\quad +h_0=\frac{2^{\al+\be+1}\Ga(\al+1)\Ga(-\al-\be-1)}{\Ga(-\be)}\,. +\end{split} +\label{122} +\end{equation} + +% +\paragraph{Symmetry} +\begin{equation} +P_n^{(\al,\be)}(-x)=(-1)^n\,P_n^{(\be,\al)}(x). +\label{48} +\end{equation} +Use (9.8.2) and (9.8.5b) or see \mycite{DLMF}{Table 18.6.1}. +% +\paragraph{Special values} +\begin{equation} +P_n^{(\al,\be)}(1)=\frac{(\al+1)_n}{n!}\,,\quad +P_n^{(\al,\be)}(-1)=\frac{(-1)^n(\be+1)_n}{n!}\,,\quad +\frac{P_n^{(\al,\be)}(-1)}{P_n^{(\al,\be)}(1)}=\frac{(-1)^n(\be+1)_n}{(\al+1)_n}\,. +\label{50} +\end{equation} +Use (9.8.1) and \eqref{48} or see \mycite{DLMF}{Table 18.6.1}. +% +\paragraph{Generating functions} +Formula (9.8.15) was first obtained by Brafman \myciteKLS{109}. +% +\paragraph{Bilateral generating functions} +For $0\le r<1$ and $x,y\in[-1,1]$ we have in terms of $F_4$ (see~\eqref{62}): +\begin{align} +&\sum_{n=0}^\iy\frac{(\al+\be+1)_n\,n!}{(\al+1)_n(\be+1)_n}\,r^n\, +P_n^{(\al,\be)}(x)\,P_n^{(\al,\be)}(y) +=\frac1{(1+r)^{\al+\be+1}} +\nonumber\\ +&\qquad\quad\times F_4\Big(\thalf(\al+\be+1),\thalf(\al+\be+2);\al+1,\be+1; +\frac{r(1-x)(1-y)}{(1+r)^2},\frac{r(1+x)(1+y)}{(1+r)^2}\Big), +\label{58}\sLP +&\sum_{n=0}^\iy\frac{2n+\al+\be+1}{n+\al+\be+1} +\frac{(\al+\be+2)_n\,n!}{(\al+1)_n(\be+1)_n}\,r^n\, +P_n^{(\al,\be)}(x)\,P_n^{(\al,\be)}(y) +=\frac{1-r}{(1+r)^{\al+\be+2}}\nonumber\\ +&\qquad\quad\times F_4\Big(\thalf(\al+\be+2),\thalf(\al+\be+3);\al+1,\be+1; +\frac{r(1-x)(1-y)}{(1+r)^2},\frac{r(1+x)(1+y)}{(1+r)^2}\Big). +\label{59} +\end{align} +Formulas \eqref{58} and \eqref{59} were first +given by Bailey \myciteKLS{91}{(2.1), (2.3)}. +See Stanton \myciteKLS{485} for a shorter proof. +(However, in the second line of +\myciteKLS{485}{(1)} $z$ and $Z$ should be interchanged.)$\;$ +As observed in Bailey \myciteKLS{91}{p.10}, \eqref{59} follows +from \eqref{58} +by applying the operator $r^{-\half(\al+\be-1)}\,\frac d{dr}\circ r^{\half(\al+\be+1)}$ +to both sides of \eqref{58}. +In view of \eqref{60}, formula \eqref{59} is the Poisson kernel for Jacobi +polynomials. The \RHS\ of \eqref{59} makes clear that this kernel is positive. +See also the discussion in Askey \myciteKLS{46}{following (2.32)}. +% +\paragraph{Quadratic transformations} +\begin{align} +\frac{C_{2n}^{(\al+\half)}(x)}{C_{2n}^{(\al+\half)}(1)} +=\frac{P_{2n}^{(\al,\al)}(x)}{P_{2n}^{(\al,\al)}(1)} +&=\frac{P_n^{(\al,-\half)}(2x^2-1)}{P_n^{(\al,-\half)}(1)}\,, +\label{51}\\ +\frac{C_{2n+1}^{(\al+\half)}(x)}{C_{2n+1}^{(\al+\half)}(1)} +=\frac{P_{2n+1}^{(\al,\al)}(x)}{P_{2n+1}^{(\al,\al)}(1)} +&=\frac{x\,P_n^{(\al,\half)}(2x^2-1)}{P_n^{(\al,\half)}(1)}\,. +\label{52} +\end{align} +See p.221, Remarks, last two formulas together with \eqref{50} and \eqref{49}. +Or see \mycite{DLMF}{(18.7.13), (18.7.14)}. +% +\paragraph{Differentiation formulas} +Each differentiation formula is given in two equivalent forms. +\begin{equation} +\begin{split} +\frac d{dx}\left((1-x)^\al P_n^{(\al,\be)}(x)\right)&= +-(n+\al)\,(1-x)^{\al-1} P_n^{(\al-1,\be+1)}(x),\\ +\left((1-x)\frac d{dx}-\al\right)P_n^{(\al,\be)}(x)&= +-(n+\al)\,P_n^{(\al-1,\be+1)}(x). +\end{split} +\label{68} +\end{equation} +% +\begin{equation} +\begin{split} +\frac d{dx}\left((1+x)^\be P_n^{(\al,\be)}(x)\right)&= +(n+\be)\,(1+x)^{\be-1} P_n^{(\al+1,\be-1)}(x),\\ +\left((1+x)\frac d{dx}+\be\right)P_n^{(\al,\be)}(x)&= +(n+\be)\,P_n^{(\al+1,\be-1)}(x). +\end{split} +\label{69} +\end{equation} +Formulas \eqref{68} and \eqref{69} follow from +\mycite{DLMF}{(15.5.4), (15.5.6)} +together with (9.8.1). They also follow from each other by \eqref{48}. +% +\paragraph{Generalized Gegenbauer polynomials} +These are defined by +\begin{equation} +S_{2m}^{(\al,\be)}(x):=\const P_m^{(\al,\be)}(2x^2-1),\qquad +S_{2m+1}^{(\al,\be)}(x):=\const x\,P_m^{(\al,\be+1)}(2x^2-1) +\label{70} +\end{equation} +in the notation of \myciteKLS{146}{p.156} +(see also \cite{K27}), while \cite[Section 1.5.2]{K26} +has $C_n^{(\la,\mu)}(x)=\const\allowbreak\times S_n^{(\la-\half,\mu-\half)}(x)$. +For $\al,\be>-1$ we have the orthogonality relation +\begin{equation} +\int_{-1}^1 S_m^{(\al,\be)}(x)\,S_n^{(\al,\be)}(x)\,|x|^{2\be+1}(1-x^2)^\al\,dx +=0\qquad(m\ne n). +\label{71} +\end{equation} +For $\be=\al-1$ generalized Gegenbauer polynomials are limit cases of +continuous $q$-ultraspherical polynomials, see \eqref{176}. + +If we define the {\em Dunkl operator} $T_\mu$ by +\begin{equation} +(T_\mu f)(x):=f'(x)+\mu\,\frac{f(x)-f(-x)}x +\label{72} +\end{equation} +and if we choose the constants in \eqref{70} as +\begin{equation} +S_{2m}^{(\al,\be)}(x)=\frac{(\al+\be+1)_m}{(\be+1)_m}\, P_m^{(\al,\be)}(2x^2-1),\quad +S_{2m+1}^{(\al,\be)}(x)=\frac{(\al+\be+1)_{m+1}}{(\be+1)_{m+1}}\, +x\,P_m^{(\al,\be+1)}(2x^2-1) +\label{73} +\end{equation} +then (see \cite[(1.6)]{K5}) +\begin{equation} +T_{\be+\half}S_n^{(\al,\be)}=2(\al+\be+1)\,S_{n-1}^{(\al+1,\be)}. +\label{74} +\end{equation} +Formula \eqref{74} with \eqref{73} substituted gives rise to two +differentiation formulas involving Jacobi polynomials which are equivalent to +(9.8.7) and \eqref{69}. + +Composition of \eqref{74} with itself gives +\[ +T_{\be+\half}^2S_n^{(\al,\be)}=4(\al+\be+1)(\al+\be+2)\,S_{n-2}^{(\al+2,\be)}, +\] +which is equivalent to the composition of (9.8.7) and \eqref{69}: +\begin{equation} +\left(\frac{d^2}{dx^2}+\frac{2\be+1}x\,\frac d{dx}\right)P_n^{(\al,\be)}(2x^2-1) +=4(n+\al+\be+1)(n+\be)\,P_{n-1}^{(\al+2,\be)}(2x^2-1). +\label{75} +\end{equation} +Formula \eqref{75} was also given in \myciteKLS{322}{(2.4)}. +% +\subsection*{9.8.1 Gegenbauer / Ultraspherical} +\label{sec9.8.1} +% +\paragraph{Notation} +Here the Gegenbauer polynomial is denoted by $C_n^\la$ instead of $C_n^{(\la)}$. +% +\paragraph{Orthogonality relation} +Write the \RHS\ of (9.8.20) as $h_n\,\de_{m,n}$. Then +\begin{equation} +\frac{h_n}{h_0}= +\frac\la{\la+n}\,\frac{(2\la)_n}{n!}\,,\quad +h_0=\frac{\pi^\half\,\Ga(\la+\thalf)}{\Ga(\la+1)},\quad +\frac{h_n}{h_0\,(C_n^\la(1))^2}= +\frac\la{\la+n}\,\frac{n!}{(2\la)_n}\,. +\label{61} +\end{equation} +% +\paragraph{Hypergeometric representation} +Beside (9.8.19) we have also +\begin{equation} +C_n^\lambda(x)=\sum_{\ell=0}^{\lfloor n/2\rfloor}\frac{(-1)^{\ell}(\lambda)_{n-\ell}} +{\ell!\;(n-2\ell)!}\,(2x)^{n-2\ell} +=(2x)^{n}\,\frac{(\lambda)_{n}}{n!}\, +\hyp21{-\thalf n,-\thalf n+\thalf}{1-\la-n}{\frac1{x^2}}. +\label{57} +\end{equation} +See \mycite{DLMF}{(18.5.10)}. +% +\paragraph{Special value} +\begin{equation} +C_n^{\la}(1)=\frac{(2\la)_n}{n!}\,. +\label{49} +\end{equation} +Use (9.8.19) or see \mycite{DLMF}{Table 18.6.1}. +% +\paragraph{Expression in terms of Jacobi} +% +\begin{equation} +\frac{C_n^\la(x)}{C_n^\la(1)}= +\frac{P_n^{(\la-\half,\la-\half)}(x)}{P_n^{(\la-\half,\la-\half)}(1)}\,,\qquad +C_n^\la(x)=\frac{(2\la)_n}{(\la+\thalf)_n}\,P_n^{(\la-\half,\la-\half)}(x). +\label{65} +\end{equation} +% +\paragraph{Re: (9.8.21)} +By iteration of recurrence relation (9.8.21): +\begin{multline} +x^2 C_n^\la(x)= +\frac{(n+1)(n+2)}{4(n+\la)(n+\la+1)}\,C_{n+2}^\la(x)+ +\frac{n^2+2n\la+\la-1}{2(n+\la-1)(n+\la+1)}\,C_n^\la(x)\\ ++\frac{(n+2\la-1)(n+2\la-2)}{4(n+\la)(n+\la-1)}\,C_{n-2}^\la(x). +\label{6} +\end{multline} +% +\paragraph{Bilateral generating functions} +\begin{multline} +\sum_{n=0}^\iy\frac{n!}{(2\la)_n}\,r^n\,C_n^\la(x)\,C_n^\la(y) +=\frac1{(1-2rxy+r^2)^\la}\,\hyp21{\thalf\la,\thalf(\la+1)}{\la+\thalf} +{\frac{4r^2(1-x^2)(1-y^2)}{(1-2rxy+r^2)^2}}\\ +(r\in(-1,1),\;x,y\in[-1,1]). +\label{66} +\end{multline} +For the proof put $\be:=\al$ in \eqref{58}, then use \eqref{63} and \eqref{65}. +The Poisson kernel for Gegenbauer polynomials can be derived in a similar way +from \eqref{59}, or alternatively by applying the operator +$r^{-\la+1}\frac d{dr}\circ r^\la$ to both sides of \eqref{66}: +\begin{multline} +\sum_{n=0}^\iy\frac{\la+n}\la\,\frac{n!}{(2\la)_n}\,r^n\,C_n^\la(x)\,C_n^\la(y) +=\frac{1-r^2}{(1-2rxy+r^2)^{\la+1}}\\ +\times\hyp21{\thalf(\la+1),\thalf(\la+2)}{\la+\thalf} +{\frac{4r^2(1-x^2)(1-y^2)}{(1-2rxy+r^2)^2}}\qquad +(r\in(-1,1),\;x,y\in[-1,1]). +\label{67} +\end{multline} +Formula \eqref{67} was obtained by Gasper \& Rahman \myciteKLS{234}{(4.4)} +as a limit case of their formula for the Poisson kernel for continuous +$q$-ultraspherical polynomials. +% +\paragraph{Trigonometric expansions} +By \mycite{DLMF}{(18.5.11), (15.8.1)}: +\begin{align} +C_n^{\la}(\cos\tha) +&=\sum_{k=0}^n\frac{(\la)_k(\la)_{n-k}}{k!\,(n-k)!}\,e^{i(n-2k)\tha} +=e^{in\tha}\frac{(\la)_n}{n!}\, +\hyp21{-n,\la}{1-\la-n}{e^{-2i\tha}}\label{103}\\ +&=\frac{(\la)_n}{2^\la n!}\, +e^{-\half i\la\pi}e^{i(n+\la)\tha}\,(\sin\tha)^{-\la}\, +\hyp21{\la,1-\la}{1-\la-n}{\frac{i e^{-i\tha}}{2\sin\tha}}\label{104}\\ +&=\frac{(\la)_n}{n!}\,\sum_{k=0}^\iy\frac{(\la)_k(1-\la)_k}{(1-\la-n)_k k!}\, +\frac{\cos((n-k+\la)\tha+\thalf(k-\la)\pi)}{(2\sin\tha)^{k+\la}}\,.\label{105} +\end{align} +In \eqref{104} and \eqref{105} we require that +$\tfrac16\pi<\tha<\tfrac56\pi$. Then the convergence is absolute for $\la>\thalf$ +and conditional for $0<\la\le\thalf$. + +By \mycite{DLMF}{(14.13.1), (14.3.21), (15.8.1)]}: +\begin{align} +C_n^\la(\cos\tha)&=\frac{2\Ga(\la+\thalf)}{\pi^\half\Ga(\la+1)}\, +\frac{(2\la)_n}{(\la+1)_n}\,(\sin\tha)^{1-2\la}\, +\sum_{k=0}^\iy\frac{(1-\la)_k(n+1)_k}{(n+\la+1)_k k!}\, +\sin\big((2k+n+1)\tha\big) +\label{7}\\ +&=\frac{2\Ga(\la+\thalf)}{\pi^\half\Ga(\la+1)}\, +\frac{(2\la)_n}{(\la+1)_n}\,(\sin\tha)^{1-2\la}\, +\Im\!\!\left(e^{i(n+1)\tha}\,\hyp21{1-\la,n+1}{n+\la+1}{e^{2i\tha}}\right)\nonumber\\ +&=\frac{2^\la\Ga(\la+\thalf)}{\pi^\half\Ga(\la+1)}\, +\frac{(2\la)_n}{(\la+1)_n}\,(\sin\tha)^{-\la}\, +\Re\!\!\left(e^{-\thalf i\la\pi}e^{i(n+\la)\tha}\, +\hyp21{\la,1-\la}{1+\la+n}{\frac{e^{i\tha}}{2i\sin\tha}}\right)\nonumber\\ +&=\frac{2^{2\la}\Ga(\la+\thalf)}{\pi^\half\Ga(\la+1)}\,\frac{(2\la)_n}{(\la+1)_n}\, +\sum_{k=0}^\iy\frac{(\la)_k(1-\la)_k}{(1+\la+n)_k k!}\, +\frac{\cos((n+k+\la)\tha-\thalf(k+\la)\pi)}{(2\sin\tha)^{k+\la}}\,. +\label{106} +\end{align} +We require that $0<\tha<\pi$ in \eqref{7} and $\tfrac16\pi<\tha<\tfrac56\pi$ in +\eqref{106} The convergence is absolute for $\la>\thalf$ and conditional for +$0<\la\le\thalf$. +For $\la\in\Zpos$ the above series terminate after the term with +$k=\la-1$. +Formulas \eqref{7} and \eqref{106} are also given in +\mycite{Sz}{(4.9.22), (4.9.25)}. +% +\paragraph{Fourier transform} +\begin{equation} +\frac{\Ga(\la+1)}{\Ga(\la+\thalf)\,\Ga(\thalf)}\, +\int_{-1}^1 \frac{C_n^\la(y)}{C_n^\la(1)}\,(1-y^2)^{\la-\half}\, +e^{ixy}\,dy +=i^n\,2^\la\,\Ga(\la+1)\,x^{-\la}\,J_{\la+n}(x). +\label{8} +\end{equation} +See \mycite{DLMF}{(18.17.17) and (18.17.18)}. +% +\paragraph{Laplace transforms} +\begin{equation} +\frac2{n!\,\Ga(\la)}\, +\int_0^\iy H_n(tx)\,t^{n+2\la-1}\,e^{-t^2}\,dt=C_n^\la(x). +\label{56} +\end{equation} +See Nielsen \cite[p.48, (4) with p.47, (1) and p.28, (10)]{K4} (1918) +or Feldheim \cite[(28)]{K3} (1942). +\begin{equation} +\frac2{\Ga(\la+\thalf)}\,\int_0^1 \frac{C_n^\la(t)}{C_n^\la(1)}\, +(1-t^2)^{\la-\half}\,t^{-1}\,(x/t)^{n+2\la+1}\,e^{-x^2/t^2}\,dt +=2^{-n}\,H_n(x)\,e^{-x^2}\quad(\la>-\thalf). +\label{46} +\end{equation} +Use Askey \& Fitch \cite[(3.29)]{K2} for $\al=\pm\thalf$ together with +\eqref{48}, \eqref{51}, \eqref{52}, \eqref{54} and \eqref{55}. +\paragraph{Addition formula} (see \mycite{AAR}{(9.8.5$'$)]}) +\begin{multline} +R_n^{(\al,\al)}\big(xy+(1-x^2)^\half(1-y^2)^\half t\big) +=\sum_{k=0}^n \frac{(-1)^k(-n)_k\,(n+2\al+1)_k}{2^{2k}((\al+1)_k)^2}\\ +\times(1-x^2)^{k/2} R_{n-k}^{(\al+k,\al+k)}(x)\,(1-y^2)^{k/2} R_{n-k}^{(\al+k,\al+k)}(y)\, +\om_k^{(\al-\half,\al-\half)}\,R_k^{(\al-\half,\al-\half)}(t), +\label{108} +\end{multline} +where +\[ +R_n^{(\al,\be)}(x):=P_n^{(\al,\be)}(x)/P_n^{(\al,\be)}(1),\quad +\om_n^{(\al,\be)}:=\frac{\int_{-1}^1 (1-x)^\al(1+x)^\be\,dx} +{\int_{-1}^1 (R_n^{(\al,\be)}(x))^2\,(1-x)^\al(1+x)^\be\,dx}\,. +\] +% +\subsection*{9.8.2 Chebyshev} +\label{sec9.8.2} +In addition to the Chebyshev polynomials $T_n$ of the first kind (9.8.35) +and $U_n$ of the second kind (9.8.36), +\begin{align} +T_n(x)&:=\frac{P_n^{(-\half,-\half)}(x)}{P_n^{(-\half,-\half)}(1)} +=\cos(n\tha),\quad x=\cos\tha,\\ +U_n(x)&:=(n+1)\,\frac{P_n^{(\half,\half)}(x)}{P_n^{(\half,\half)}(1)} +=\frac{\sin((n+1)\tha)}{\sin\tha}\,,\quad x=\cos\tha, +\end{align} +we have Chebyshev polynomials $V_n$ {\em of the third kind} +and $W_n$ {\em of the fourth kind}, +\begin{align} +V_n(x)&:=\frac{P_n^{(-\half,\half)}(x)}{P_n^{(-\half,\half)}(1)} +=\frac{\cos((n+\thalf)\tha)}{\cos(\thalf\tha)}\,,\quad x=\cos\tha,\\ +W_n(x)&:=(2n+1)\,\frac{P_n^{(\half,-\half)}(x)}{P_n^{(\half,-\half)}(1)} +=\frac{\sin((n+\thalf)\tha)}{\sin(\thalf\tha)}\,,\quad x=\cos\tha, +\end{align} +see \cite[Section 1.2.3]{K20}. Then there is the symmetry +\begin{equation} +V_n(-x)=(-1)^n W_n(x). +\label{140} +\end{equation} + +The names of Chebyshev polynomials of the third and fourth kind +and the notation $V_n(x)$ are due to Gautschi \cite{K21}. +The notation $W_n(x)$ was first used by Mason \cite{K22}. +Names and notations for Chebyshev polynomials of the third and fourth +kind are interchanged in \mycite{AAR}{Remark 2.5.3} and +\mycite{DLMF}{Table 18.3.1}. +% +\subsection*{9.9 Pseudo Jacobi (or Routh-Romanovski)} +\label{sec9.9} +In this section in \mycite{KLS} the pseudo Jacobi polynomial $P_n(x;\nu,N)$ in (9.9.1) +is considered +for $N\in\ZZ_{\ge0}$ and $n=0,1,\ldots,n$. However, we can more generally take +$-\thalf\be>0\;\;{\rm or}\;\;\al<\be<0). +\] +Then $P_n$ can be expressed as a Meixner polynomial: +\[ +P_n(x)=(-k_2(\al\be)^{-1})_n\,\be^n\, +M_n\left(-\,\frac{x+k_2\al^{-1}}{\al-\be},-k_2(\al\be)^{-1},\be\al^{-1}\right). +\] + +In 1938 Gottlieb \cite[\S2]{K1} introduces polynomials $l_n$ ``of Laguerre type'' +which turn out to be special Meixner polynomials: +$l_n(x)=e^{-n\la} M_n(x;1,e^{-\la})$. +% +\paragraph{Uniqueness of orthogonality measure} +The coefficient of $p_{n-1}(x)$ in (9.10.4) behaves as $O(n^2)$ as $n\to\iy$. +Hence \eqref{93} holds, by which the orthogonality measure is unique. +% +\subsection*{9.11 Krawtchouk} +\label{sec9.11} +% +\paragraph{Special values} +By (9.11.1) and the binomial formula: +\begin{equation} +K_n(0;p,N)=1,\qquad +K_n(N;p,N)=(1-p^{-1})^n. +\label{9} +\end{equation} +The self-duality (p.240, Remarks, first formula) +\begin{equation} +K_n(x;p,N)=K_x(n;p,N)\qquad (n,x\in \{0,1,\ldots,N\}) +\label{147} +\end{equation} +combined with \eqref{9} yields: +\begin{equation} +K_N(x;p,N)=(1-p^{-1})^x\qquad(x\in\{0,1,\ldots,N\}). +\label{148} +\end{equation} +% +\paragraph{Symmetry} +By the orthogonality relation (9.11.2): +\begin{equation} +\frac{K_n(N-x;p,N)}{K_n(N;p,N)}=K_n(x;1-p,N). +\label{10} +\end{equation} +By \eqref{10} and \eqref{147} we have also +\begin{equation} +\frac{K_{N-n}(x;p,N)}{K_N(x;p,N)}=K_n(x;1-p,N) +\qquad(n,x\in\{0,1,\ldots,N\}), +\label{149} +\end{equation} +and, by \eqref{149}, \eqref{10} and \eqref{9}, +\begin{equation} +K_{N-n}(N-x;p,N)=\left(\frac p{p-1}\right)^{n+x-N}K_n(x;p,N) +\qquad(n,x\in\{0,1,\ldots,N\}). +\label{150} +\end{equation} +A particular case of \eqref{10} is: +\begin{equation} +K_n(N-x;\thalf,N)=(-1)^n K_n(x;\thalf,N). +\label{11} +\end{equation} +Hence +\begin{equation} +K_{2m+1}(N;\thalf,2N)=0. +\label{12} +\end{equation} +From (9.11.11): +\begin{equation} +K_{2m}(N;\thalf,2N)=\frac{(\thalf)_m}{(-N+\thalf)_m}\,. +\label{13} +\end{equation} +% +\paragraph{Quadratic transformations} +\begin{align} +K_{2m}(x+N;\thalf,2N)&=\frac{(\thalf)_m}{(-N+\thalf)_m}\, +R_m(x^2;-\thalf,-\thalf,N), +\label{31}\\ +K_{2m+1}(x+N;\thalf,2N)&=-\,\frac{(\tfrac32)_m}{N\,(-N+\thalf)_m}\, +x\,R_m(x^2-1;\thalf,\thalf,N-1), +\label{33}\\ +K_{2m}(x+N+1;\thalf,2N+1)&=\frac{(\tfrac12)_m}{(-N-\thalf)_m}\, +R_m(x(x+1);-\thalf,\thalf,N), +\label{32}\\ +K_{2m+1}(x+N+1;\thalf,2N+1)&=\frac{(\tfrac32)_m}{(-N-\thalf)_{m+1}}\, +(x+\thalf)\,R_m(x(x+1);\thalf,-\thalf,N), +\label{34} +\end{align} +where $R_m$ is a dual Hahn polynomial (9.6.1). For the proofs use +(9.6.2), (9.11.2), (9.6.4) and (9.11.4). +% +\paragraph{Generating functions} +\begin{multline} +\sum_{x=0}^N\binom Nx K_m(x;p,N)K_n(x;q,N)z^x\\ +=\left(\frac{p-z+pz}p\right)^m +\left(\frac{q-z+qz}q\right)^n +(1+z)^{N-m-n} +K_m\left(n;-\,\frac{(p-z+pz)(q-z+qz)}z,N\right). +\label{107} +\end{multline} +This follows immediately from Rosengren \cite[(3.5)]{K8}, which goes back +to Meixner \cite{K9}. +% +\subsection*{9.12 Laguerre} +\label{sec9.12} +\paragraph{Notation} +Here the Laguerre polynomial is denoted by $L_n^\al$ instead of +$L_n^{(\al)}$. +% +\paragraph{Hypergeometric representation} +\begin{align} +L_n^\al(x)&= +\frac{(\al+1)_n}{n!}\,\hyp11{-n}{\al+1}x +\label{182}\\ +&=\frac{(-x)^n}{n!} \hyp20{-n,-n-\al}-{-\,\frac1x} +\label{183}\\ +&=\frac{(-x)^n}{n!}\,C_n(n+\al;x), +\label{184} +\end{align} +where $C_n$ in \eqref{184} is a +\hyperref[sec9.14]{Charlier polynomial}. +Formula \eqref{182} is (9.12.1). Then \eqref{183} follows by reversal +of summation. Finally \eqref{184} follows by \eqref{183} and \eqref{179}. +It is also the remark on top of p.244 in \mycite{KLS}, and it is essentially +\myciteKLS{416}{(2.7.10)}. +% +\paragraph{Uniqueness of orthogonality measure} +The coefficient of $p_{n-1}(x)$ in (9.12.4) behaves as $O(n^2)$ as $n\to\iy$. +Hence \eqref{93} holds, by which the orthogonality measure is unique. +% +\paragraph{Special value} +\begin{equation} +L_n^{\al}(0)=\frac{(\al+1)_n}{n!}\,. +\label{53} +\end{equation} +Use (9.12.1) or see \mycite{DLMF}{18.6.1)}. +% +\paragraph{Quadratic transformations} +\begin{align} +H_{2n}(x)&=(-1)^n\,2^{2n}\,n!\,L_n^{-1/2}(x^2), +\label{54}\\ +H_{2n+1}(x)&=(-1)^n\,2^{2n+1}\,n!\,x\,L_n^{1/2}(x^2). +\label{55} +\end{align} +See p.244, Remarks, last two formulas. +Or see \mycite{DLMF}{(18.7.19), (18.7.20)}. +% +\paragraph{Fourier transform} +\begin{equation} +\frac1{\Ga(\al+1)}\,\int_0^\iy \frac{L_n^\al(y)}{L_n^\al(0)}\, +e^{-y}\,y^\al\,e^{ixy}\,dy= +i^n\,\frac{y^n}{(iy+1)^{n+\al+1}}\,, +\label{14} +\end{equation} +see \mycite{DLMF}{(18.17.34)}. +% +\paragraph{Differentiation formulas} +Each differentiation formula is given in two equivalent forms. +\begin{equation} +\frac d{dx}\left(x^\al L_n^\al(x)\right)= +(n+\al)\,x^{\al-1} L_n^{\al-1}(x),\qquad +\left(x\frac d{dx}+\al\right)L_n^\al(x)= +(n+\al)\,L_n^{\al-1}(x). +\label{76} +\end{equation} +% +\begin{equation} +\frac d{dx}\left(e^{-x} L_n^\al(x)\right)= +-e^{-x} L_n^{\al+1}(x),\qquad +\left(\frac d{dx}-1\right)L_n^\al(x)= +-L_n^{\al+1}(x). +\label{77} +\end{equation} +% +Formulas \eqref{76} and \eqref{77} follow from +\mycite{DLMF}{(13.3.18), (13.3.20)} +together with (9.12.1). +% +\paragraph{Generalized Hermite polynomials} +See \myciteKLS{146}{p.156}, \cite[Section 1.5.1]{K26}. +These are defined by +\begin{equation} +H_{2m}^\mu(x):=\const L_m^{\mu-\half}(x^2),\qquad +H_{2m+1}^\mu(x):=\const x\,L_m^{\mu+\half}(x^2). +\label{78} +\end{equation} +Then for $\mu>-\thalf$ we have orthogonality relation +\begin{equation} +\int_{-\iy}^{\iy} H_m^\mu(x)\,H_n^\mu(x)\,|x|^{2\mu}e^{-x^2}\,dx +=0\qquad(m\ne n). +\label{79} +\end{equation} +Let the Dunkl operator $T_\mu$ be defined by \eqref{72}. +If we choose the constants in \eqref{78} as +\begin{equation} +H_{2m}^\mu(x)=\frac{(-1)^m(2m)!}{(\mu+\thalf)_m}\,L_m^{\mu-\half}(x^2),\qquad +H_{2m+1}^\mu(x)=\frac{(-1)^m(2m+1)!}{(\mu+\thalf)_{m+1}}\, + x\,L_m^{\mu+\half}(x^2) + \label{80} +\end{equation} +then (see \cite[(1.6)]{K5}) +\begin{equation} +T_\mu H_n^\mu=2n\,H_{n-1}^\mu. +\label{81} +\end{equation} +Formula \eqref{81} with \eqref{80} substituted gives rise to two +differentiation formulas involving Laguerre polynomials which are equivalent to +(9.12.6) and \eqref{76}. + +Composition of \eqref{81} with itself gives +\[ +T_\mu^2 H_n^\mu=4n(n-1)\,H_{n-2}^\mu, +\] +which is equivalent to the composition of (9.12.6) and \eqref{76}: +\begin{equation} +\left(\frac{d^2}{dx^2}+\frac{2\al+1}x\,\frac d{dx}\right)L_n^\al(x^2) +=-4(n+\al)\,L_{n-1}^\al(x^2). +\label{82} +\end{equation} +% +\subsection*{9.14 Charlier} +\label{sec9.14} +% +\paragraph{Hypergeometric representation} +\begin{align} +C_n(x;a)&=\hyp20{-n,-x}-{-\,\frac1a} +\label{179}\\ +&=\frac{(-x)_n}{a^n} \hyp11{-n}{x-n+1}a +\label{180}\\ +&=\frac{n!}{(-a)^n}\,L_n^{x-n}(a), +\label{181} +\end{align} +where $L_n^\al(x)$ is a +\hyperref[sec9.12]{Laguerre polynomial}. +Formula \eqref{179} is (9.14.1). Then \eqref{180} follows by reversal +of the summation. Finally \eqref{181} follows by \eqref{180} and +(9.12.1). It is also the Remark on p.249 of \mycite{KLS}, and it +was earlier given in \myciteKLS{416}{(2.7.10)}. +% +\paragraph{Uniqueness of orthogonality measure} +The coefficient of $p_{n-1}(x)$ in (9.14.4) behaves as $O(n)$ as $n\to\iy$. +Hence \eqref{93} holds, by which the orthogonality measure is unique. +% +\subsection*{9.15 Hermite} +\label{sec9.15} +% +\paragraph{Uniqueness of orthogonality measure} +The coefficient of $p_{n-1}(x)$ in (9.15.4) behaves as $O(n)$ as $n\to\iy$. +Hence \eqref{93} holds, by which the orthogonality measure is unique. +% +\paragraph{Fourier transforms} +\begin{equation} +\frac1{\sqrt{2\pi}}\,\int_{-\iy}^\iy H_n(y)\,e^{-\half y^2}\,e^{ixy}\,dy= +i^n\,H_n(x)\,e^{-\half x^2}, +\label{15} +\end{equation} +see \mycite{AAR}{(6.1.15) and Exercise 6.11}. +\begin{equation} +\frac1{\sqrt\pi}\,\int_{-\iy}^\iy H_n(y)\,e^{-y^2}\,e^{ixy}\,dy= +i^n\,x^n\,e^{-\frac14 x^2}, +\label{16} +\end{equation} +see \mycite{DLMF}{(18.17.35)}. +\begin{equation} +\frac{i^n}{2\sqrt\pi}\,\int_{-\iy}^\iy y^n\,e^{-\frac14 y^2}\,e^{-ixy}\,dy= +H_n(x)\,e^{-x^2}, +\label{17} +\end{equation} +see \mycite{AAR}{(6.1.4)}. +% +\subsection*{14.1 Askey-Wilson} +\label{sec14.1} +% +\paragraph{Symmetry} +The Askey-Wilson polynomials $p_n(x;a,b,c,d\,|\,q)$ are \,|\,symmetric +in $a,b,c,d$. +\sLP +This follows from the orthogonality relation (14.1.2) +together with the value of its coefficient of $x^n$ given in (14.1.5b). +Alternatively, combine (14.1.1) with \mycite{GR}{(III.15)}.\\ +As a consequence, it is sufficient to give generating function (14.1.13). Then the generating +functions (14.1.14), (14.1.15) will follow by symmetry in the parameters. +% +\paragraph{Basic hypergeometric representation} +In addition to (14.1.1) we have (in notation \eqref{111}): +\begin{multline} +p_n(\cos\tha;a,b,c,d\,|\, q) +=\frac{(ae^{-i\tha},be^{-i\tha},ce^{-i\tha},de^{-i\tha};q)_n} +{(e^{-2i\tha};q)_n}\,e^{in\tha}\\ +\times {}_8W_7\big(q^{-n}e^{2i\tha};ae^{i\tha},be^{i\tha}, +ce^{i\tha},de^{i\tha},q^{-n};q,q^{2-n}/(abcd)\big). +\label{113} +\end{multline} +This follows from (14.1.1) by combining (III.15) and (III.19) in +\mycite{GR}. +It is also given in \myciteKLS{513}{(4.2)}, but be aware for some slight errors. +The symmetry in $a,b,c,d$ is evident from \eqref{113}. +% +\paragraph{Special value} +\begin{equation} +p_n\big(\thalf(a+a^{-1});a,b,c,d\,|\, q\big)=a^{-n}\,(ab,ac,ad;q)_n\,, +\label{40} +\end{equation} +and similarly for arguments $\thalf(b+b^{-1})$, $\thalf(c+c^{-1})$ and +$\thalf(d+d^{-1})$ by symmetry of $p_n$ in $a,b,c,d$. +% +\paragraph{Trivial symmetry} +\begin{equation} +p_n(-x;a,b,c,d\,|\, q)=(-1)^n p_n(x;-a,-b,-c,-d\,|\, q). +\label{41} +\end{equation} +Both \eqref{40} and \eqref{41} are obtained from (14.1.1). +% +\paragraph{Re: (14.1.5)} +Let +\begin{equation} +p_n(x):=\frac{p_n(x;a,b,c,d\,|\, q)}{2^n(abcdq^{n-1};q)_n}=x^n+\wt k_n x^{n-1} ++\cdots\;. +\label{18} +\end{equation} +Then +\begin{equation} +\wt k_n=-\frac{(1-q^n)(a+b+c+d-(abc+abd+acd+bcd)q^{n-1})} +{2(1-q)(1-abcdq^{2n-2})}\,. +\label{19} +\end{equation} +This follows because $\tilde k_n-\tilde k_{n+1}$ equals the coefficient +$\thalf\bigl(a+a^{-1}-(A_n+C_n)\bigr)$ of $p_n(x)$ in (14.1.5). +% +\paragraph{Generating functions} +Rahman \myciteKLS{449}{(4.1), (4.9)} gives: +\begin{align} +&\sum_{n=0}^\iy \frac{(abcdq^{-1};q)_n a^n}{(ab,ac,ad,q;q)_n}\,t^n\, +p_n(\cos\tha;a,b,c,d\,|\,q) +\nonumber\\ +&=\frac{(abcdtq^{-1};q)_\iy}{(t;q)_\iy}\, +\qhyp65{(abcdq^{-1})^\half,-(abcdq^{-1})^\half,(abcd)^\half, +-(abcd)^\half,a e^{i\tha},a e^{-i\tha}} +{ab,ac,ad,abcdtq^{-1},qt^{-1}}{q,q} +\nonumber\\ +&+\frac{(abcdq^{-1},abt,act,adt,ae^{i\tha},ae^{-i\tha};q)_\iy} +{(ab,ac,ad,t^{-1},ate^{i\tha},ate^{-i\tha};q)_\iy} +\nonumber\\ +&\times\qhyp65{t(abcdq^{-1})^\half,-t(abcdq^{-1})^\half,t(abcd)^\half, +-t(abcd)^\half,at e^{i\tha},at e^{-i\tha}} +{abt,act,adt,abcdt^2q^{-1},qt}{q,q}\quad(|t|<1). +\label{185} +\end{align} +In the limit \eqref{109} the first term on the \RHS\ of \eqref{185} +tends to the \LHS\ of (9.1.15), while the second term tends formally +to 0. The special case $ad=bc$ of \eqref{185} was earlier given in +\myciteKLS{236}{(4.1), (4.6)}. +% +\paragraph{Limit relations}\quad\sLP +{\bf Askey-Wilson $\longrightarrow$ Wilson}\\ +Instead of (14.1.21) we can keep a polynomial of degree $n$ while the limit is approached: +\begin{equation} +\lim_{q\to1}\frac{p_n(1-\thalf x(1-q)^2;q^a,q^b,q^c,q^d\,|\, q)}{(1-q)^{3n}} +=W_n(x;a,b,c,d). +\label{109} +\end{equation} +For the proof first derive the corresponding limit for the monic polynomials by comparing +(14.1.5) with (9.4.4). +\bLP +{\bf Askey-Wilson $\longrightarrow$ Continuous Hahn}\\ +Instead of (14.4.15) we can keep a polynomial of degree $n$ while the limit is approached: +\begin{multline} +\lim_{q\uparrow1} +\frac{p_n\big(\cos\phi-x(1-q)\sin\phi;q^a e^{i\phi},q^b e^{i\phi},q^{\overline a} e^{-i\phi}, +q^{\overline b} e^{-i\phi}\,|\, q\big)} +{(1-q)^{2n}}\\ +=(-2\sin\phi)^n\,n!\,p_n(x;a,b,\overline a,\overline b)\qquad +(0<\phi<\pi). +\label{177} +\end{multline} +Here the \RHS\ has a continuous Hahn polynomial (9.4.1). +For the proof first derive the corresponding limit for the monic polynomials by comparing +(14.1.5) with (9.1.5). +In fact, define the monic polynomial +\[ +\wt p_n(x):= +\frac{p_n\big(\cos\phi-x(1-q)\sin\phi;q^a e^{i\phi},q^b e^{i\phi},q^{\overline a} e^{-i\phi}, +q^{\overline b} e^{-i\phi}\,|\, q\big)} +{(-2(1-q)\sin\phi)^n\,(abcdq^{n-1};q)_n}\,. +\] +Then it follows from (14.1.5) that +\begin{equation*} +x\,\wt p_n(x)=\wt p_{n+1}(x) ++\frac{(1-q^a)e^{i\phi}+(1-q^{-a})e^{-i\phi}+\wt A_n+\wt C_n}{2(1-q)\sin\phi}\,\wt p_n(x)\\ ++\frac{\wt A_{n-1} \wt C_n}{(1-q)^2 \sin^2\phi}\,\wt p_{n-1}(x), +\end{equation*} +where $\wt A_n$ and $\wt C_n$ are as given after (14.1.3) with $a,b,c,d$ replaced by +$q^a e^{i\phi},q^b e^{i\phi},q^{\overline a} e^{-i\phi},q^{\overline b} e^{-i\phi}$. +Then the recurrence equation for $\wt p_n(x)$ tends for $q\uparrow 1$ to +the recurrence equation (9.4.4) with $c=\overline a$, $d=\overline b$. +\bLP +{\bf Askey-Wilson $\longrightarrow$ Meixner-Pollaczek}\\ +Instead of (14.9.15) we can keep a polynomial of degree $n$ while the limit is approached: +\begin{equation} +\lim_{q\uparrow1} +\frac{p_n\big(\cos\phi-x(1-q)\sin\phi; +q^\la e^{i\phi},0,q^\la e^{-i\phi},0\,|\, q\big)}{(1-q)^n} +=n!\,P_n^{(\la)}(x;\pi-\phi)\quad +(0<\phi<\pi). +\label{178} +\end{equation} +Here the \RHS\ has a Meixner-Pollaczek polynomial (9.7.1). +For the proof first derive the corresponding limit for the monic polynomials by comparing +(14.1.5) with (9.7.4). +In fact, define the monic polynomial +\[ +\wt p_n(x):= +\frac{p_n\big(\cos\phi-x(1-q)\sin\phi; +q^\la e^{i\phi},0,q^\la e^{-i\phi},0\,|\, q\big)}{(-2(1-q)\sin\phi)^n}\,. +\] +Then it follows from (14.1.5) that +\begin{equation*} +x\,\wt p_n(x)=\wt p_{n+1}(x) ++\frac{(1-q^\la)e^{i\phi}+(1-q^{-\la})e^{-i\phi}+\wt A_n+\wt C_n} +{2(1-q)\sin\phi}\,\wt p_n(x)\\ ++\frac{\wt A_{n-1} \wt C_n}{(1-q)^2 \sin^2\phi}\,\wt p_{n-1}(x), +\end{equation*} +where $\wt A_n$ and $\wt C_n$ are as given after (14.1.3) with $a,b,c,d$ replaced by +$q^\la e^{i\phi},0,q^\la e^{-i\phi},0$. +Then the recurrence equation for $\wt p_n(x)$ tends for $q\uparrow 1$ to +the recurrence equation (9.7.4). +% +\paragraph{References} +See also Koornwinder \cite{K7}. +% +\subsection*{14.2 $q$-Racah} +\label{sec14.2} +\paragraph{Symmetry} +\begin{equation} +R_n(x;\al,\be,q^{-N-1},\de\,|\, q) +=\frac{(\be q,\al\de^{-1}q;q)_n}{(\al q,\be\de q;q)_n}\,\de^n\, +R_n(\de^{-1}x;\be,\al,q^{-N-1},\de^{-1}\,|\, q). +\label{84} +\end{equation} +This follows from (14.2.1) combined with \mycite{GR}{(III.15)}. +\sLP +In particular, +\begin{equation} +R_n(x;\al,\be,q^{-N-1},-1\,|\, q) +=\frac{(\be q,-\al q;q)_n}{(\al q,-\be q;q)_n}\,(-1)^n\, +R_n(-x;\be,\al,q^{-N-1},-1\,|\, q), +\label{85} +\end{equation} +and +\begin{equation} +R_n(x;\al,\al,q^{-N-1},-1\,|\, q) +=(-1)^n\,R_n(-x;\al,\al,q^{-N-1},-1\,|\, q), +\label{86} +\end{equation} + +\paragraph{Trivial symmetry} +Clearly from (14.2.1): +\begin{equation} +R_n(x;\al,\be,\ga,\de\,|\, q)=R_n(x;\be\de,\al\de^{-1},\ga,\de\,|\, q) +=R_n(x;\ga,\al\be\ga^{-1},\al,\ga\de\al^{-1}\,|\, q). +\label{83} +\end{equation} +For $\al=q^{-N-1}$ this shows that the three cases +$\al q=q^{-N}$ or $\be\de q=q^{-N}$ or $\ga q=q^{-N}$ of (14.2.1) +are not essentially different. +% +\paragraph{Duality} +It follows from (14.2.1) that +\begin{equation} +R_n(q^{-y}+\ga\de q^{y+1};q^{-N-1},\be,\ga,\de\,|\, q) +=R_y(q^{-n}+\be q^{n-N};\ga,\de,q^{-N-1},\be\,|\, q)\quad +(n,y=0,1,\ldots,N). +\end{equation} +% +\subsection*{14.3 Continuous dual $q$-Hahn} +\label{sec14.3} +The continuous dual $q$-Hahn polynomials are the special case $d=0$ of the +Askey-Wilson polynomials: +\[ +p_n(x;a,b,c\,|\, q):=p_n(x;a,b,c,0\,|\, q). +\] +Hence all formulas in \S14.3 are specializations for $d=0$ of formulas in \S14.1. +% +\subsection*{14.4 Continuous $q$-Hahn} +\label{sec14.4} +The continuous $q$-Hahn polynomials are the special case +of Askey-Wilson polynomials with parameters +$a e^{i\phi},b e^{i\phi},a e^{-i\phi},b e^{-i\phi}$: +\[ +p_n(x;a,b,\phi\,|\, q):= +p_n(x;a e^{i\phi},b e^{i\phi},a e^{-i\phi},b e^{-i\phi}\,|\, q). +\] +In \myciteKLS{72}{(4.29)} and \mycite{GR}{(7.5.43)} +(who write $p_n(x;a,b\,|\,q)$, $x=\cos(\tha+\phi)$) +and in \mycite{KLS}{\S14.4} (who writes $p_n(x;a,b,c,d;q)$, +$x=\cos(\tha+\phi)$) +the parameter +dependence on $\phi$ is incorrectly omitted. + +Since all formulas in \S14.4 are specializations of formulas in \S14.1, +there is no real need to give these specializations explicitly. +In particular, the limit (14.4.15) is in fact a limit from Askey-Wilson to +continuous $q$-Hahn. See also \eqref{177}. +% +\subsection*{14.5 Big $q$-Jacobi} +\label{sec14.5} +% +\paragraph{Different notation} +See p.442, Remarks: +\begin{equation} +P_n(x;a,b,c,d;q):=P_n(qac^{-1}x;a,b,-ac^{-1}d;q) +=\qhyp32{q^{-n},q^{n+1}ab,qac^{-1}x}{qa,-qac^{-1}d}{q,q}. +\label{123} +\end{equation} +Furthermore, +\begin{equation} +P_n(x;a,b,c,d;q)=P_n(\la x;a,b,\la c,\la d;q), +\label{141} +\end{equation} +\begin{equation} +P_n(x;a,b,c;q)=P_n(-q^{-1}c^{-1}x;a,b,-ac^{-1},1;q) +\label{142} +\end{equation} +% +\paragraph{Orthogonality relation} +(equivalent to (14.5.2), see also \cite[(2.42), (2.41), (2.36), (2.35)]{K17}). +Let $c,d>0$ and either $a\in (-c/(qd),1/q)$, $b\in(-d/(cq),1/q)$ or +$a/c=-\overline b/d\notin\RR$. Then +\begin{equation} +\int_{-d}^c P_m(x;a,b,c,d;q) P_n(x;a,b,c,d;q)\, +\frac{(qx/c,-qx/d;q)_\iy}{(qax/c,-qbx/d;q)_\iy}\,d_qx=h_n\,\de_{m,n}\,, +\label{124} +\end{equation} +where +\begin{equation} +\frac{h_n}{h_0}=q^{\half n(n-1)}\left(\frac{q^2a^2d}c\right)^n\, +\frac{1-qab}{1-q^{2n+1}ab}\, +\frac{(q,qb,-qbc/d;q)_n}{(qa,qab,-qad/c;q)_n} +\label{125} +\end{equation} +and +\begin{equation} +h_0=(1-q)c\,\frac{(q,-d/c,-qc/d,q^2ab;q)_\iy} +{(qa,qb,-qbc/d,-qad/c;q)_\iy}\,. +\label{126} +\end{equation} +% +\paragraph{Other hypergeometric representation and asymptotics} +\begin{align} +&P_n(x;a,b,c,d;q) +=\frac{(-qbd^{-1}x;q)_n}{(-q^{-n}a^{-1}cd^{-1};q)_n}\, +\qhyp32{q^{-n},q^{-n}b^{-1},cx^{-1}}{qa,-q^{-n}b^{-1}dx^{-1}}{q,q} +\label{138}\\ +&\qquad=(qac^{-1}x)^n\,\frac{(qb,cx^{-1};q)_n}{(qa,-qac^{-1}d;q)_n}\, +\qhyp32{q^{-n},q^{-n}a^{-1},-qbd^{-1}x}{qb,q^{1-n}c^{-1}x} +{q,-q^{n+1}ac^{-1}d} +\label{132}\\ +&\qquad=(qac^{-1}x)^n\,\frac{(qb,q;q)_n}{(-qac^{-1}d;q)_n}\, +\sum_{k=0}^n\frac{(cx^{-1};q)_{n-k}}{(q,qa;q)_{n-k}}\, +\frac{(-qbd^{-1}x;q)_k}{(qb,q;q)_k}\,(-1)^k q^{\half k(k-1)}(-dx^{-1})^k. +\label{133} +\end{align} +Formula \eqref{138} follows from \eqref{123} by +\mycite{GR}{(III.11)} and next \eqref{132} follows by series inversion +\mycite{GR}{Exercise 1.4(ii)}. +Formulas \eqref{138} and \eqref{133} are also given in +\mycite{Ism}{(18.4.28), (18.4.29)}. +It follows from \eqref{132} or \eqref{133} that +(see \myciteKLS{298}{(1.17)} or \mycite{Ism}{(18.4.31)}) +\begin{equation} +\lim_{n\to\iy}(qac^{-1}x)^{-n} P_n(x;a,b,c,d;q) +=\frac{(cx^{-1},-dx^{-1};q)_\iy}{(-qac^{-1}d,qa;q)_\iy}\,, +\label{134} +\end{equation} +uniformly for $x$ in compact subsets of $\CC\backslash\{0\}$. +(Exclusion of the spectral points $x=cq^m,dq^m$ ($m=0,1,2,\ldots$), +as was done in \myciteKLS{298} and \mycite{Ism}, is not necessary. However, +while \eqref{134} yields 0 at these points, a more refined asymptotics +at these points is given in \myciteKLS{298} and \mycite{Ism}.)$\;$ +For the proof of \eqref{134} use that +\begin{equation} +\lim_{n\to\iy}(qac^{-1}x)^{-n} P_n(x;a,b,c,d;q) +=\frac{(qb,cx^{-1};q)_n}{(qa,-qac^{-1}d;q)_n}\, +\qhyp11{-qbd^{-1}x}{qb}{q,-dx^{-1}}, +\label{135} +\end{equation} +which can be evaluated by \mycite{GR}{(II.5)}. +Formula \eqref{135} follows formally from \eqref{132}, and it follows rigorously, by +dominated convergence, from \eqref{133}. +% +\paragraph{Symmetry} +(see \cite[\S2.5]{K17}). +\begin{equation} +\frac{P_n(-x;a,b,c,d;q)}{P_n(-d/(qb);a,b,c,d;q)} +=P_n(x;b,a,d,c;q). +\end{equation} +% +\paragraph{Special values} +\begin{align} +P_n(c/(qa);a,b,c,d;q)&=1,\\ +P_n(-d/(qb);a,b,c,d;q)&=\left(-\,\frac{ad}{bc}\right)^n\, +\frac{(qb,-qbc/d;q)_n}{(qa,-qad/c;q)_n}\,,\\ +P_n(c;a,b,c,d;q)&= +q^{\half n(n+1)}\left(\frac{ad}c\right)^n +\frac{(-qbc/d;q)_n}{(-qad/c;q)_n}\,,\\ +P_n(-d;a,b,c,d;q)&=q^{\half n(n+1)} (-a)^n\,\frac{(qb;q)_n}{(qa;q)_n}\,. +\end{align} +% +\paragraph{Quadratic transformations} +(see \cite[(2.48), (2.49)]{K17} and \eqref{128}).\\ +These express big $q$-Jacobi polynomials $P_m(x;a,a,1,1;q)$ in terms of little +$q$-Jacobi polynomials (see \S14.12). +\begin{align} +P_{2n}(x;a,a,1,1;q)&=\frac{p_n(x^2;q^{-1},a^2;q^2)}{p_n((qa)^{-2};q^{-1},a^2;q^2)}\,, +\label{130}\\ +P_{2n+1}(x;a,a,1,1;q)&=\frac{qax\,p_n(x^2;q,a^2;q^2)}{p_n((qa)^{-2};q,a^2;q^2)}\,. +\label{131} +\end{align} +Hence, by (14.12.1), \mycite{GR}{Exercise 1.4(ii)} and \eqref{128}, +\begin{align} +P_n(x;a,a,1,1;q)&=\frac{(qa^2;q^2)_n}{(qa^2;q)_n}\,(qax)^n\, +\qhyp21{q^{-n},q^{-n+1}}{q^{-2n+1}a^{-2}}{q^2,(ax)^{-2}} +\label{136}\\ +&=\frac{(q;q)_n}{(qa^2;q)_n}\,(qa)^n\, +\sum_{k=0}^{[\half n]}(-1)^k q^{k(k-1)} +\frac{(qa^2;q^2)_{n-k}}{(q^2;q^2)_k\,(q;q)_{n-2k}}\,x^{n-2k}. +\label{137} +\end{align} +% +\paragraph{$q$-Chebyshev polynomials} +In \eqref{123}, with $c=d=1$, the cases $a=b=q^{-\half}$ and $a=b=q^\half$ can be considered +as $q$-analogues of the Chebyshev polynomials of the first and second kind, respectively +(\S9.8.2) because of the limit (14.5.17). The quadratic relations \eqref{130}, \eqref{131} +can also be specialized to these cases. The definition of the $q$-Chebyshev polynomials +may vary by normalization and by dilation of argument. They were considered in +\cite{K18}. +By \myciteKLS{24}{p.279} and \eqref{130}, \eqref{131}, the {\em Al-Salam-Ismail polynomials} +$U_n(x;a,b)$ ($q$-dependence suppressed) in the case $a=q$ can be expressed as +$q$-Chebyshev polynomials of the second kind: +\begin{equation*} +U_n(x,q,b)=(q^{-3} b)^{\half n}\,\frac{1-q^{n+1}}{1-q}\, +P_n(b^{-\half}x;q^\half,q^\half,1,1;q). +\end{equation*} +Similarly, by \cite[(5.4), (5.1), (5.3)]{K19} and \eqref{130}, \eqref{131}, Cigler's $q$-Chebyshev +polynomials $T_n(x,s,q)$ and $U_n(x,s,q)$ +can be expressed in terms of the $q$-Chebyshev cases of \eqref{123}: +\begin{align*} +T_n(x,s,q)&=(-s)^{\half n}\,P_n((-qs)^{-\half} x;q^{-\half},q^{-\half},1,1;q),\\ +U_n(x,s,q)&=(-q^{-2}s)^{\half n}\,\frac{1-q^{n+1}}{1-q}\, +P_n((-qs)^{-\half} x;q^{\half},q^{\half},1,1;q). +\end{align*} +% +\paragraph{Limit to Discrete $q$-Hermite I} +\begin{equation} +\lim_{a\to0} a^{-n}\,P_n(x;a,a,1,1;q)=q^n\,h_n(x;q). +\label{139} +\end{equation} +Here $h_n(x;q)$ is given by (14.28.1). +For the proof of \eqref{139} use \eqref{138}. +% +\paragraph{Pseudo big $q$-Jacobi polynomials} +Let $a,b,c,d\in\CC$, $z_+>0$, $z_-<0$ such that +$\tfrac{(ax,bx;q)_\iy}{(cx,dx;q)_\iy}>0$ for $x\in z_- q^\ZZ\cup z_+ q^\ZZ$. +Then $(ab)/(qcd)>0$. Assume that $(ab)/(qcd)<1$. +Let $N$ be the largest nonnegative integer such that $q^{2N}>(ab)/(qcd)$. +Then +\begin{multline} +\int_{z_- q^\ZZ\cup z_+ q^\ZZ}P_m(cx;c/b,d/a,c/a;q)\,P_n(cx;c/b,d/a,c/a;q)\, +\frac{(ax,bx;q)_\iy}{(cx,dx;q)_\iy}\,d_qx=h_n\de_{m,n}\\ +(m,n=0,1,\ldots,N), +\label{114} +\end{multline} +where +\begin{equation} +\frac{h_n}{h_0}=(-1)^n\left(\frac{c^2}{ab}\right)^n q^{\half n(n-1)} q^{2n}\, +\frac{(q,qd/a,qd/b;q)_n}{(qcd/(ab),qc/a,qc/b;q)_n}\, +\frac{1-qcd/(ab)}{1-q^{2n+1}cd/(ab)} +\label{115} +\end{equation} +and +\begin{equation} +h_0=\int_{z_- q^\ZZ\cup z_+ q^\ZZ}\frac{(ax,bx;q)_\iy}{(cx,dx;q)_\iy}\,d_qx +=(1-q)z_+\, +\frac{(q,a/c,a/d,b/c,b/d;q)_\iy}{(ab/(qcd);q)_\iy}\, +\frac{\tha(z_-/z_+,cdz_-z_+;q)}{\tha(cz_-,dz_-,cz_+,dz_+;q)}\,. +\label{116} +\end{equation} +See Groenevelt \& Koelink \cite[Prop.~2.2]{K14}. +Formula \eqref{116} was first given by Slater \cite[(5)]{K15} as an evaluation +of a sum of two ${}_2\psi_2$ series. +The same formula is given in Slater \myciteKLS{471}{(7.2.6)} and in +\mycite{GR}{Exercise 5.10}, but in both cases with the same slight error, +see \cite[2nd paragraph after Lemma 2.1]{K14} for correction. +The theta function is given by \eqref{117}. +Note that +\begin{equation} +P_n(cx;c/b,d/a,c/a;q)=P_n(-q^{-1}ax;c/b,d/a,-a/b,1;q). +\label{145} +\end{equation} + +In \cite{K29} the weights of the pseudo big $q$-Jacobi polynomials +occur in certain measures on the space of $N$-point configurations +on the so-called extended Gelfand-Tsetlin graph. +% +\subsubsection*{Limit relations} +\paragraph{Pseudo big $q$-Jacobi $\longrightarrow$ Discrete Hermite II} +\begin{equation} +\lim_{a\to\iy}i^n q^{\half n(n-1)} P_n(q^{-1}a^{-1}ix;a,a,1,1;q)= +\wt h_n(x;q). +\label{144} +\end{equation} +For the proof use \eqref{137} and \eqref{143}. +Note that $P_n(q^{-1}a^{-1}ix;a,a,1,1;q)$ is obtained from the +\RHS\ of \eqref{144} by replacing $a,b,c,d$ by $-ia^{-1},ia^{-1},i,-i$. +% +\paragraph{Pseudo big $q$-Jacobi $\longrightarrow$ Pseudo Jacobi} +\begin{equation} +\lim_{q\uparrow1}P_n(iq^{\half(-N-1+i\nu)}x;-q^{-N-1},-q^{-N-1},q^{-N+i\nu-1};q) +=\frac{P_n(x;\nu,N)}{P_n(-i;\nu,N)}\,. +\label{118} +\end{equation} +Here the big $q$-Jacobi polynomial on the \LHS\ equals +$P_n(cx;c/b,d/a,c/a;q)$ with\\ +$a=iq^{\half(N+1-i\nu)}$, $b=-iq^{\half(N+1+i\nu)}$, +$c=iq^{\half(-N-1+i\nu)}$, $d=-iq^{\half(-N-1-i\nu)}$. +% +\subsection*{14.7 Dual $q$-Hahn} +\label{sec14.7} +\paragraph{Orthogonality relation} +More generally we have (14.7.2) with positive weights in any of the following +cases: +(i) $0<\ga q<1$, $0<\de q<1$;\quad +(ii) $0<\ga q<1$, $\de<0$;\quad +(iii) $\ga<0$, $\de>q^{-N}$;\quad +(iv) $\ga>q^{-N}$, $\de>q^{-N}$;\quad +(v) $01,\;qb1$ +if $p>q^{-1}$. +% +\paragraph{History} +The origin of the name of the quantum $q$-Krawtchouk polynomials +is by their interpretation +as matrix elements of irreducible corepresentations of (the quantized +function algebra of) the quantum group $SU_q(2)$ considered +with respect to its quantum subgroup $U(1)$. The orthogonality +relation and dual orthogonality relation of these polynomials +are an expression of the unitarity of these corepresentations. +See for instance \myciteKLS{343}{Section 6}. +% +\subsection*{14.16 Affine $q$-Krawtchouk} +\label{sec14.16} +% +\paragraph{$q$-Hypergeometric representation} +For $n=0,1,\ldots,N$ +(see (14.16.1)): +\begin{align} +K_n^{\rm Aff}(y;p,N;q) +&=\frac1{(p^{-1}q^{-1};q^{-1})_n}\,\qhyp21{q^{-n},q^{-N}y^{-1}}{q^{-N}}{q,p^{-1}y} +\label{156}\\ +&=\qhyp32{q^{-n},y,0}{q^{-N},pq}{q,q}. +\label{157} +\end{align} +% +\paragraph{Self-duality} +By \eqref{157}: +\begin{equation} +K_n^{\rm Aff}(q^{-x};p,N;q)=K_x^{\rm Aff}(q^{-n};p,N;q)\qquad +(n,x\in\{0,1,\ldots,N\}). +\label{168} +\end{equation} +% +\paragraph{Special values} +By \eqref{156} and \mycite{GR}{(II.4)}: +\begin{equation} +K_n^{\rm Aff}(1;p,N;q)=1,\qquad +K_n^{\rm Aff}(q^{-N};p,N;q)=\frac1{((pq)^{-1};q^{-1})_n}\,. +\label{165} +\end{equation} +By \eqref{165} and \eqref{168} we have also +\begin{equation} +K_N^{\rm Aff}(q^{-x};p,N;q)=\frac1{((pq)^{-1};q^{-1})_x}\,. +\label{170} +\end{equation} +% +\paragraph{Limit for $q\to1$ to Krawtchouk} (see (14.16.14) and Section \hyperref[sec9.11]{9.11}): +\begin{align} +\lim_{q\to1} K_n^{\rm Aff}(1+(1-q)x;p,N;q)&=K_n(x;1-p,N), +\label{162}\\ +\lim_{q\to1} K_n^{\rm Aff}(q^{-x};p,N;q)&=K_n(x;1-p,N). +\label{166} +\end{align} +% +\paragraph{A relation between quantum and affine $q$-Krawtchouk}\quad\\ +By \eqref{152}, \eqref{156}, \eqref{165} and \eqref{168} +we have for $x\in\{0,1,\ldots,N\}$: +\begin{align} +K_{N-n}^{\rm qtm}(q^{-x};p^{-1}q^{-N-1},N;q) +&=\frac{K_x^{\rm Aff}(q^{-n};p,N;q)}{K_x^{\rm Aff}(q^{-N};p,N;q)} +\label{171}\\ +&=\frac{K_n^{\rm Aff}(q^{-x};p,N;q)}{K_N^{\rm Aff}(q^{-x};p,N;q)}\,. +\label{172} +\end{align} +Formula \eqref{171} is given in \cite[formula after (12)]{K24} +and \cite[(59)]{K25}. +In view of \eqref{164} and \eqref{166} +formula \eqref{172} has \eqref{149} as a limit case for +$q\to 1$. +% +\paragraph{Affine $q^{-1}$-Krawtchouk} +By \eqref{156}, \eqref{165}, +\eqref{154} and \eqref{152} (see also p.505, first formula): +\begin{align} +\frac{K_n^{\rm Aff}(y;p,N;q^{-1})}{K_n^{\rm Aff}(q^N;p,N;q^{-1})} +&=\qhyp21{q^{-n},q^{-N}y}{q^{-N}}{q,p^{-1}q^{n+1}} +\label{158}\\ +&=K_n^{\rm qtm}(q^{-N}y;p^{-1},N;q). +\label{159} +\end{align} +Formula \eqref{159} is equivalent to \eqref{160}. +Just as for \eqref{160}, it tends after suitable substitutions to +\eqref{10} as $q\to1$. + +The orthogonality relation (14.16.2) holds with positive weights for $q>1$ +if $0-1) +\label{119} +\end{equation} +with +\begin{equation} +\frac{h_n}{h_0}=\frac{(q^{\al+1};q)_n}{(q;q)_n q^n},\qquad +h_0=-\,\frac{(q^{-\al};q)_\iy}{(q;q)_\iy}\,\frac\pi{\sin(\pi\al)}\,. +\label{120} +\end{equation} +The expression for $h_0$ (which is Askey's $q$-gamma evaluation +\cite[(4.2)]{K16}) +should be interpreted by continuity in $\al$ for +$\al\in\Znonneg$. +Explicitly we can write +\begin{equation} +h_n=q^{-\half\al(\al+1)}\,(q;q)_\al\,\log(q^{-1})\qquad(\al\in\Znonneg). +\label{121} +\end{equation} +% +\subsubsection*{Expansion of $x^n$} +\begin{equation} +x^n=q^{-\half n(n+2\al+1)}\,(q^{\al+1};q)_n\, +\sum_{k=0}^n\frac{(q^{-n};q)_k}{(q^{\al+1};q)_k}\,q^k\,L_k^\al(x;q). +\label{37} +\end{equation} +This follows from \eqref{36} by the equality given in the Remark at the end +of \S14.20. Alternatively, it can be derived in the same way as \eqref{36} +from the generating function (14.21.14). +% +\subsubsection*{Quadratic transformations} +$q$-Laguerre polynomials $L_n^\al(x;q)$ with $\al=\pm\half$ are +related to discrete $q$-Hermite II polynomials $\wt h_n(x;q)$: +\begin{align} +L_n^{-1/2}(x^2;q^2)&= +\frac{(-1)^n q^{2n^2-n}}{(q^2;q^2)_n}\,\wt h_{2n}(x;q), +\label{38}\\ +xL_n^{1/2}(x^2;q^2)&= +\frac{(-1)^n q^{2n^2+n}}{(q^2;q^2)_n}\,\wt h_{2n+1}(x;q). +\label{39} +\end{align} +These follows from \eqref{28} and \eqref{29}, respectively, by applying +the equalities given in the Remarks at the end of \S14.20 and \S14.28. +% +\subsection*{14.27 Stieltjes-Wigert} +\label{sec14.27} +% +\subsubsection*{An alternative weight function} +The formula on top of p.547 should be corrected as +\begin{equation} +w(x)=\frac\ga{\sqrt\pi}\,x^{-\half}\exp(-\ga^2\ln^2 x),\quad x>0,\quad +{\rm with}\quad\ga^2=-\,\frac1{2\ln q}\,. +\label{94} +\end{equation} +For $w$ the weight function given in \mycite{Sz}{\S2.7} the \RHS\ of \eqref{94} +equals $\const w(q^{-\half}x)$. See also +\mycite{DLMF}{\S18.27(vi)}. +% +\subsection*{14.28 Discrete $q$-Hermite I} +\label{sec14.28} +% +\paragraph{History} +Discrete $q$ Hermite I polynomials (not yet with this name) first occurred in +Hahn \myciteKLS{261}, see there p.29, case V and the $q$-weight $\pi(x)$ given by +the second expression on line 4 of p.30. However note that on the line on p.29 dealing with +case V, one should read $k^2=q^{-n}$ instead of $k^2=-q^n$. Then, with the indicated +substitutions, \myciteKLS{261}{(4.11), (4.12)} yield constant multiples of +$h_{2n}(q^{-1}x;q)$ and $h_{2n+1}(q^{-1}x;q)$, respectively, + due to the quadratic transformations \eqref{28}, \eqref{29} together with (4.20.1). +% +\subsection*{14.29 Discrete $q$-Hermite II} +\label{sec14.29} +% +\paragraph{Basic hypergeometric representation}(see (14.29.1)) +\begin{equation} +\wt h_n(x;q)=x^n\,\qhyp21{q^{-n},q^{-n+1}}0{q^2,-q^2 x^{-2}}. +\label{143} +\end{equation} +% +\renewcommand{\refname}{Standard references} +\begin{thebibliography}{999999} +\label{sec_ref1} +% +\mybibitem{AAR} +G. E. Andrews, R. Askey and R. Roy, +{\em Special functions}, +Cambridge University Press, 1999. +% +\mybibitem{DLMF} +{\em NIST Handbook of Mathematical Functions}, +Cambridge University Press, 2010;\\ +{\em DLMF, Digital Library of Mathematical Functions}, +\url{http://dlmf.nist.gov}. +% +\mybibitem{GR} +G.~Gasper and M.~Rahman, +{\em Basic hypergeometric series}, 2nd edn., +Cambridge University Press, 2004. +% +\mybibitem{HTF1} +A. Erd\'elyi, +{\em Higher transcendental functions, Vol. 1}, +McGraw-Hill, 1953. +% +\mybibitem{HTF2} +A. Erd\'elyi, +{\em Higher transcendental functions, Vol. 2}, +McGraw-Hill, 1953. +% +\mybibitem{Ism} +M. E. H. Ismail, +{\em Classical and quantum orthogonal polynomials in one variable}, +Cambridge University Press, 2005; reprinted and corrected, 2009. +% +\mybibitem{KLS} +R. Koekoek, P.~A. Lesky and R.~F. Swarttouw, +{\em Hypergeometric orthogonal polynomials and their $q$-analogues}, +Springer-Verlag, 2010. +% +\mybibitem{Sz} +G. Szeg{\H{o}}, +{\em Orthogonal polynomials}, +Colloquium Publications 23, +American Mathematical Society, Fourth Edition, 1975. +% +\end{thebibliography} +% +\renewcommand{\refname}{References from Koekoek, Lesky \& Swarttouw} +\begin{thebibliography}{999} +\label{sec_ref2} +% +\mybibitemKLS{24} +W. A. Al-Salam and M. E. H. Ismail, +{\em Orthogonal polynomials associated with the Rogers-Ramanujan continued fraction}, +Pacific J. Math. 104 (1983), 269--283. +% +\mybibitemKLS{46} +R. Askey, +{\em Orthogonal polynomials and special functions}, +CBMS Regional Conference Series, Vol.~21, SIAM, 1975. +% +\mybibitemKLS{51} + R. Askey, +{\em Beta integrals and the associated orthogonal polynomials}, +in: {\em Number theory, Madras 1987}, +Lecture Notes in Mathematics 1395, Springer-Verlag, 1989, pp. 84--121. +% +\mybibitemKLS{63} +R. Askey and M. E. H. Ismail, +{\em A generalization of ultraspherical polynomials}, +in: {\em Studies in Pure Mathematics}, +Birkh\"auser, 1983, pp. 55--78. +% +\mybibitemKLS{64} +R. Askey and M. E. H. Ismail, +{\em Recurrence relations, continued fractions, and orthogonal polynomials}, +Mem. Amer. Math. Soc. 49 (1984), no. 300. +% +\mybibitemKLS{72} +R. Askey and J. A. Wilson, +{\em Some basic hypergeometric orthogonal polynomials that generalize Jacobi polynomials}, +Mem. Amer. Math. Soc. 54 (1985), no. 319. +% +\mybibitemKLS{79} +N. M. Atakishiyev and A. U. Klimyk, +{\em On $q$-orthogonal polynomials, dual to little and big +$q$-Jacobi polynomials}, +J. Math. Anal. Appl. 294 (2004), 246--257. +% +\mybibitemKLS{91} +W. N. Bailey, +{\em The generating function of Jacobi polynomials}, +J. London Math. Soc. 13 (1938), 8--12. +% +\mybibitemKLS{109} +F. Brafman, +{\em Generating functions of Jacobi and related polynomials}, +Proc. Amer. Math. Soc. 2 (1951), 942--949. +% +\mybibitemKLS{146} +T. S. Chihara, +{\em An introduction to orthogonal polynomials}, Gordon and Breach, 1978; +reprinted Dover Publications, 2011. +% +\mybibitemKLS{161} +Ph. Delsarte, {\em Association schemes and $t$-designs in regular +semilattices}, J. Combin. Theory Ser. A 20 (1976), 230--243. +% +\mybibitemKLS{186} +C. F. Dunkl, +{\em An addition theorem for some $q$-Hahn polynomials}, +Monatsh. Math. 85 (1977), 5--37. +% +\mybibitemKLS{234} +G. Gasper and M. Rahman, +{\em Positivity of the Poisson kernel for the continuous +$q$-ultraspherical polynomials}, +SIAM J. Math. Anal. 14 (1983), 409--420. +% +\mybibitem{236} + G. Gasper and M. Rahman, +{\em Positivity of the Poisson kernel for the continuous $q$-Jacobi +polynomials and some quadratic transformation formulas for basic +hypergeometric series}, +SIAM J. Math. Anal. 17 (1986), 970--999. +% +\mybibitemKLS{261} +W. Hahn, +{\em \"Uber Orthogonalpolynome, die $q$-Differenzengleichungen gen\"ugen}, +Math. Nachr. 2 (1949), 4--34. +% +\mybibitemKLS{281} +M. E. H. Ismail, J. Letessier, G. Valent and J. Wimp, +{\em Two families of associated Wilson polynomials}, +Canad. J. Math. 42 (1990), 659--695. +% +\mybibitemKLS{298} +M. E. H. Ismail and J. A. Wilson, +{\em Asymptotic and generating relations for the $q$-Jacobi and +${}_4 \phi_3$ polynomials}, +J. Approx. Theory 36 (1982), 43--54. +% +\mybibitemKLS{322} +T. Koornwinder, +{\em Jacobi polynomials III. Analytic proof of the addition formula}, +SIAM J. Math. Anal. 6 (1975) 533--543. +% +\mybibitemKLS{342} +T. H. Koornwinder, +{\em Meixner-Pollaczek polynomials and the Heisenberg algebra}, +J. Math. Phys. 30 (1989), 767--769. +% +\mybibitemKLS{343} +T. H. Koornwinder, +{\em Representations of the twisted $SU(2)$ quantum group and some +$q$-hypergeometric orthogonal polynomials}, Indag. Math. 51 (1989), 97--117. +% +\mybibitemKLS{351} +T. H. Koornwinder, +{\em The structure relation for Askey-Wilson polynomials}, +J.~Comput.\ Appl.\ Math.\ 207 (2007), 214--226; {\tt arXiv:math/0601303v3}. +% +\mybibitemKLS{382} +P. A. Lesky, +{\em Endliche und unendliche Systeme von kontinuierlichen klassischen Orthogonalpolynomen}, +Z. Angew. Math. Mech. 76 (1996), 181--184. +% +\mybibitemKLS{384} + P. A. Lesky, +{\em Einordnung der Polynome von Romanovski-Bessel in das Askey-Tableau}, +Z.~Angew. Math. Mech. 78 (1998), 646--648. +% +\mybibitemKLS{406} +J. Meixner, +{\em Orthogonale Polynomsysteme mit einer besonderen Gestalt der erzeugenden Funktion}, +J. London Math. Soc. 9 (1934), 6--13. +% +\mybibitemKLS{416} +A. F. Nikiforov, S. K. Suslov and V. B. Uvarov, +{\em Classical orthogonal polynomials of a discrete variable}, +Springer-Verlag, 1991. +% +\mybibitemKLS{449} +M. Rahman, +{\em Some generating functions for the associated Askey-Wilson polynomials}, +J.~Comput. Appl. Math. 68 (1996), 287--296. +% +\mybibitemKLS{463} +V. Romanovski, +{\em Sur quelques classes nouvelles de polyn\^omes orthogonaux}, +C. R. Acad. Sci. Paris 188 (1929), 1023--1025. +% +\mybibitemKLS{471} +L. J. Slater, +{\em Generalized hypergeometric functions}, Cambridge University Press, 1966. +% +\mybibitemKLS{485} +D. Stanton, +{\em A short proof of a generating function for Jacobi polynomials}, +Proc. Amer. Math. Soc. 80 (1980), 398--400. +% +\mybibitemKLS{488} +D. Stanton, +{\em Orthogonal polynomials and Chevalley groups}, +in: {\em Special functions: group theoretical aspects and applications}, +Reidel, 1984, pp. 87-128. +% +\mybibitemKLS{513} +J. A. Wilson, +{\em Asymptotics for the ${}_4F_3$ polynomials}, +J. Approx. Theory 66 (1991), 58--71. +% +\end{thebibliography} +% +\makeatletter +\renewcommand\@biblabel[1]{[K#1]} +\makeatother +% +\renewcommand{\refname}{Other references} +\begin{thebibliography}{999} +\label{sec_ref3} +% +% +\bibitem{K16} +R. Askey, +{\em Ramanujan's extensions of the gamma and beta functions}, +Amer. Math. Monthly 87 (1980), 346--359. +% +\bibitem{K2} +R. Askey and J. Fitch, +{\em Integral representations for Jacobi polynomials and some applications}, +J. Math. Anal. Appl. 26 (1969), 411--437. +% +\bibitem{K24} +M. N. Atakishiyev and V. A. Groza, +{\em The quantum algebra $U_q(su_2)$ and $q$-Krawtchouk families of +polynomials}, +J. Phys. A 37 (2004), 2625--2635. +% +\bibitem{K18} +M. Atakishiyeva and N. Atakishiyev, +{\em On discrete $q$-extensions of Chebyshev polynomials}, +Commun. Math. Anal. 14 (2013), 1--12. +% +\bibitem{K27} +S. Belmehdi, +{\em Generalized Gegenbauer orthogonal polynomials}, +J. Comput. Appl. Math. 133 (2001), 195--205. +% +\bibitem{K5} +Y. Ben Cheikh and M. Gaied, +{\em Characterization of the Dunkl-classical symmetric orthogonal polynomials}, +Appl. Math. Comput. 187 (2007), 105--114. +% +\bibitem{K19} +J. Cigler, +{\em A simple approach to $q$-Chebyshev polynomials}, +{\tt arXiv:1201.4703v2 [math.CO]}, 2012. +% +\bibitem{K23} +Ph. Delsarte, +{\em Properties and applications of the recurrence +$F(i+1,k+1,n+1)=q^{k+1}F(i,k+1,n)-q^{k}F(i,k,n)$}, +SIAM J. Appl. Math. 31 (1976), 262--270. +% +\bibitem{K26} +C. F. Dunkl and Y. Xu, +{\em Orthogonal polynomials of several variables}, +Cambridge University Press, 2014, second ed. +% +\bibitem{K3} +E. Feldheim, +{\em Relations entre les polynomes de Jacobi, Laguerre et Hermite}, +Acta Math. 75 (1942), 117--138. +% +\bibitem{K21} +W. Gautschi, +{\em On mean convergence of extended Lagrange interpolation}, +J. Comput. Appl. Math. 43 (1992), 19--35. +% +\bibitem{K25} +V. X. Genest, S. Post, L. Vinet, G.-F. Yu and A. Zhedanov, +{\em $q$-Rotations and Krawtchouk polynomials}, +arXiv:1408.5292v2 [math-ph], 2014. +% +\bibitem{K28} +V. X. Genest, L. Vinet and A. Zhedanov, +{\em A "continuous" limit of the Complementary Bannai-Ito polynomials: +Chihara polynomials}, +SIGMA 10 (2014), 038, 18 pp.; arXiv:1309.7235v3 [math.CA]. +% +\bibitem{K29} +V. Gorin and G. Olshanski, +{\em A quantization of the harmonic analysis on the infinite-dimensional +unitary group}, +\href{http://arxiv.org/abs/1504.06832}{arXiv:1504.06832}v1 [math.RT], 2015. +% +\bibitem{K1} +M. J. Gottlieb, +{\em Concerning some polynomials orthogonal on a finite or enumerable set of points}, +Amer. J. Math. 60 (1938), 453--458. +% +\bibitem{K14} +W. Groenevelt and E. Koelink, +{\em The indeterminate moment problem for the $q$-Meixner polynomials}, +J. Approx. Theory 163 (2011), 836--863. +% +\bibitem{K11} +K. Jordaan and F. To\'okos, +{\em Orthogonality and asymptotics of Pseudo-Jacobi polynomials for + non-classical parameters}, +J. Approx. Theory 178 (2014), 1--12. +% +\bibitem{K7} +T. H. Koornwinder, {\em Askey-Wilson polynomial}, Scholarpedia 7 (2012), no.~7, +7761;\\ + \url{http://www.scholarpedia.org/article/Askey-Wilson_polynomial}. +% +\bibitem{K17} +T. H. Koornwinder, +{$q$-Special functions, a tutorial}, +{\tt arXiv:math/9403216v2 [math.CA]}, 2013. + % + \bibitem{K10} + N. N. Leonenko and N. \v{S}uvak, +{\em Statistical inference for student diffusion process}, +Stoch. Anal. Appl. 28 (2010), 972--1002. +% +\bibitem{K22} +J. C. Mason, +{\em Chebyshev polynomials of the second, third and fourth kinds in +approximation, indefinite integration, and integral transforms}, +J. Comput. Appl. Math. 49 (1993), 169--178. +% +\bibitem{K20} +J. C. Mason and D. Handscomb, +{\em Chebyshev polynomials}, +Chapman \& Hall / CRC, 2002. +% +\bibitem{K9} +J. Meixner, +{\em Umformung gewisser Reihen, deren Glieder Produkte hypergeometrischer +Funktionen sind}, +Deutsche Math. 6 (1942), 341--349. +% +\bibitem{K4} +N. Nielsen, +{\em Recherches sur les polyn\^omes d' Hermite}, +Kgl. Danske Vidensk. Selsk. Math.-Fys. Medd. I.6, K\o benhavn, 1918. +% +\bibitem{K12} +J. Peetre, +{\em Correspondence principle for the quantized annulus, Romanovski polynomials, +and Morse potential}, +J. Funct. Anal. 117 (1993), 377--400. +% +\bibitem{K8} +H. Rosengren, +{\em Multivariable orthogonal polynomials and coupling coefficients for +discrete series representations}, +SIAM J. Math. Anal. 30 (1999), 233--272. +% +\bibitem{K13} +E. J. Routh, +{\em On some properties of certain solutions of a differential equation of the second order}, +Proc. London Math. Soc. 16 (1885), 245--261. +% +\bibitem{K6} +J. A. Shohat and J. D. Tamarkin, +{\em The problem of moments}, +American Mathematical Society, 1943. +% +\bibitem{K15} +L. J. Slater, +{\em General transformations of bilateral series}, +Quart. J. Math., Oxford Ser. (2) 3 (1952), 73--80. +% +%until K29 +\end{thebibliography} +\quad\\ +\begin{footnotesize} +\begin{quote} +{T. H. Koornwinder, Korteweg-de Vries Institute, University of Amsterdam,\\ +P.O.\ Box 94248, 1090 GE Amsterdam, The Netherlands; + +\vspace{\smallskipamount} +email: }{\tt T.H.Koornwinder@uva.nl} +\end{quote} +\end{footnotesize} +\end{document} \ No newline at end of file diff --git a/lib/build-parser.js b/lib/build-parser.js index 2640392..e32dcb1 100644 --- a/lib/build-parser.js +++ b/lib/build-parser.js @@ -4,17 +4,22 @@ var INPUT_FILE = __dirname + '/parser.pegjs'; var OUTPUT_FILE = __dirname + '/parser.js'; -var buildParser = module.exports = function(inFile, outFile) { +var buildParser = module.exports = function(commandDefinitions, inFile, outFile) { + if (typeof inFile == "undefined") { + inFile = INPUT_FILE; + } + if (typeof outFile == "undefined") { + outFile = OUTPUT_FILE; + } var PEG = require('pegjs'); var fs = require('fs'); var tu = require('./texutil'); - tu.createMacroCode(); - + tu.createMacroCode(commandDefinitions); var parserSource = PEG.buildParser(fs.readFileSync(inFile, 'utf8'), { /* PEGJS options */ output: "source", cache: true,// makes repeated calls to generic_func production efficient - allowedStartTules: [ "start" ] + allowedStartRules: [ "start" ] }); // hack up the source to make it pass jshint parserSource = parserSource @@ -25,8 +30,5 @@ var buildParser = module.exports = function(inFile, outFile) { parserSource = '/* jshint latedef: false */\n' + 'module.exports = ' + parserSource + ';'; - fs.writeFileSync(outFile, parserSource, 'utf8'); }; - -buildParser(INPUT_FILE, OUTPUT_FILE); diff --git a/lib/index.js b/lib/index.js index d25e2b4..07564ba 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,16 +1,14 @@ "use strict"; var json = require('../package.json'); +var fs = require("fs"); module.exports = { name: json.name, // package name version: json.version // version # for this package }; -module.exports.initialize = function(rebuild) { - if (rebuild) { - require("./build-parser"); - } +module.exports.initialize = function() { module.exports.tu = require("./texutil"); var Parser = module.exports.Parser = require('./parser'); module.exports.render = require('./render'); @@ -21,14 +19,23 @@ module.exports.initialize = function(rebuild) { module.exports.SyntaxError = Parser.SyntaxError; }; +module.exports.rebuild = function(commandDefinitions) { + require("./build-parser")(commandDefinitions); +} + +module.exports.reset = function() { + ["ast.js", "astutil.js", "parser.js", "parser.pegjs", "render.js"].forEach( + function(file) { + try { + fs.unlinkSync("./" + file); + } + catch (e) {} + } + ) +}; + // for only math mode input module.exports.makeReplacements = function(input, options) { - if (typeof options === "undefined") { - options = {}; - options.usemathrm = false; - options.usemhchem = false; - options.semanticlatex = false; - } // allow user to pass a parsed AST as input, as well as a string if (typeof(input) === 'string') { input = module.exports.parse(input, {usemathrm:options.usemathrm, semanticlatex:options.semanticlatex}); @@ -44,6 +51,12 @@ module.exports.makeReplacements = function(input, options) { // will be for all latex; separated from makeReplacements for testing purposes module.exports.texer = function(input, options) { try { + if (typeof options === "undefined") { + options = {}; + options.usemathrm = false; + options.usemhchem = false; + options.semanticlatex = false; + } var result = { status: "+", output: module.exports.makeReplacements(input, options) }; @@ -57,6 +70,7 @@ module.exports.texer = function(input, options) { return result; } catch (e) { +// console.log(e); if (options && options.debug) { throw e; } diff --git a/lib/mathmode.js b/lib/mathmode.js index c8c5215..94511d5 100755 --- a/lib/mathmode.js +++ b/lib/mathmode.js @@ -122,7 +122,7 @@ function parseNonMath(res, start, ranges) { return i; } -module.exports.findMathSections = function (res) { +module.exports.findMathSections = function(res) { var ranges = []; parseNonMath(res, 0, ranges); var mathSections = []; diff --git a/lib/templates/parser.pegjs b/lib/templates/parser.pegjs index 9ff9884..edeed68 100644 --- a/lib/templates/parser.pegjs +++ b/lib/templates/parser.pegjs @@ -223,7 +223,8 @@ lit / f:generic_func &{ return !tu.all_functions[f]; } { throw new peg$SyntaxError("Illegal TeX function", [], f, location()); } -litstuff = //basically lit without DELIMITER or LITERAL as an option (replaced by LITERALPART at the end), needed because they interrupt the parsing of commands in PAREN. +litstuff = + //basically lit without DELIMITER or LITERAL as an option (replaced by LITERALPART at the end), needed because they interrupt the parsing of commands in PAREN. f:PAREN atsymbol atsymbol l:lit &{ return options.semanticlatex; } { return ast.Tex.PAREN2(f, l); } / f:PAREN atsymbol l:lit &{ return options.semanticlatex; } diff --git a/lib/textmode.js b/lib/textmode.js new file mode 100644 index 0000000..2b591e6 --- /dev/null +++ b/lib/textmode.js @@ -0,0 +1,238 @@ +"use strict"; + +var texer = module.exports.texer = require("./"); +texer.reset(); + +var rebuilt = false; + +var mathStart = {'\\[' : '\\]', + '\\(' : '\\)', + '$' : '$', + '$$' : '$$', + '\\begin{equation}' : '\\end{equation}', + '\\begin{equation*}' : '\\end{equation*}', + '\\begin{align}' : '\\end{align}', + '\\begin{align*}' : '\\end{align*}', + '\\begin{multline}' : '\\end{multline}', + '\\begin{multline*}' : '\\end{multline*}'}; + +var textStart = ['\\hbox{', + '\\mbox{', + '\\text{', + '\\label{']; + +var escapes = ['\\$', + '\\\\[', + '\\\\]', + '\\\\(', + '\\\\)', + '\\%']; + +var insertion = "{--insertion--}"; + +function makeInsertions(string, sections, replacements) { + var offset = 0; + for (var index in sections) { + var section = sections[index]; + var start = section.startIndex + offset, end = section.endIndex + offset + 1; + replacements.push(section.makeReplacements(string.substring(start, end))); + string = string.substring(0, start) + insertion + string.substring(end); + offset += insertion.length - end + start; + } + return string; +} + +function TextMode(startIndex) { + this.startIndex = startIndex; + this.mathSections = []; +} + +TextMode.prototype.addMathSection = function(mathSection) { + this.mathSections.push(mathSection); + +} + +TextMode.prototype.makeReplacements = function(string) { + var replacements = []; + string = makeInsertions(string, this.mathSections, replacements); + for (var index in replacements) { + var replacement = replacements[index]; + string = string.replace(insertion, replacement.replace(/\$/g, '$$$')); + } + return string; +} + +function MathMode(startIndex) { + this.startIndex = startIndex; + this.textSections = []; +} + +MathMode.prototype.addTextSection = function(textSection) { + this.textSections.push(textSection); + +} + +MathMode.prototype.makeReplacements = function(string) { + var replacements = []; + string = makeInsertions(string, this.textSections, replacements); + var delimStart = this.delim, delimEnd = mathStart[this.delim]; + string = string.substring(delimStart.length, string.length - delimEnd.length); + var output = texer.texer(string).output; + string = delimStart + output + delimEnd; + for (var index in replacements) { + var replacement = replacements[index]; + string = string.replace(insertion, replacement.replace(/\$/g, '$$$')); + } + return string; +} + +function firstDelim(res, enter) { + var min, list; + if (enter) { + min = '\\['; + list = Object.keys(mathStart); + } + else { + min = '\\hbox{'; + list = textStart; + } + for (var key in list) { + var i = res.indexOf(list[key]); + if (i !== -1 && (i <= res.indexOf(min) || res.indexOf(min) === -1)) { + min = list[key]; + } + } + return min; +} + +function startsWith(string, substring) { + return string.lastIndexOf(substring, 0) === 0; + +} + +function doesExit(string) { + for (var i in textStart) { + if (startsWith(string, textStart[i])) { + return true; + } + } + return false; +} + +function doesEnter(string) { + for (var key in mathStart) { + if (startsWith(string, key)) { + return key; + } + } + return false; +} + +function skipEscaped(res) { + for (var index in escapes) { + var escape = escapes[index]; + if (startsWith(res, escape)) { + return escape.length; + } + } + return 0; +} + +function parseMath(res, startIndex) { + var mathSection = new MathMode(startIndex); + mathSection.delim = firstDelim(res, true); + var i = mathSection.delim.length; + var end = mathStart[mathSection.delim]; + while (i < res.length) { + i += skipEscaped(res.substring(i)); + var sub = res.substring(i); + if (sub.charAt(0) == "%") { + var textSection = parseText(sub.substring(0, sub.indexOf("\n")), i); + mathSection.addTextSection(textSection); + i += textSection.endIndex - textSection.startIndex; + } + if (doesExit(sub)) { + var textSection = parseText(sub, i); + mathSection.addTextSection(textSection); + i += textSection.endIndex - textSection.startIndex; + } + if (startsWith(sub, end)) { + mathSection.endIndex = mathSection.startIndex + i + end.length - 1; + return mathSection; + } + i++; + } +} + +function parseText(res, startIndex) { + if (startIndex == undefined) { + startIndex = 0; + } + var textSection = new TextMode(startIndex); + var delim = firstDelim(res, false); + if (!startsWith(res, delim)) { + delim = ''; + } + var level = 0; + var i = delim.length; + var commented = false; + while (i < res.length) { + if (!commented) { + i += skipEscaped(res.substring(i)); + if (i >= res.length) { + break; + } + var sub = res.substring(i); + if (sub.charAt(0) == "%") { + commented = true; + i++; + continue; + } + if (doesEnter(sub)) { + var mathSection = parseMath(sub, i); + textSection.addMathSection(mathSection); + i += mathSection.endIndex - mathSection.startIndex; + } + else if (sub.charAt(0) === '{') { + level++; + } + else if (sub.charAt(0) === '}') { + if (level === 0 && delim !== '') { + textSection.endIndex = startIndex + i; + return textSection; + } + level--; + } + } + else if (res.charAt(i) == "\n") { + commented = false; + } + i++; + } + textSection.endIndex = startIndex + i - 1; + return textSection; +} + +module.exports.replaceText = function(res, preserveBuild) { + if (!rebuilt && !preserveBuild) { + rebuilt = true; + texer.rebuild(); + texer.initialize(); + } + return parseText(res).makeReplacements(res); +} + +module.exports.replaceFile = function(inFile, outFile) { + var path = require('path'); + var fs = require('fs'); + inFile = path.join(__dirname, inFile); + outFile = path.join(__dirname, outFile); + var res = fs.readFileSync(inFile, 'utf8'); + var lines = res.split("\n").map(function(line) {return line.trim();}); + var sty = lines.slice(0, lines.indexOf("\\begin{document}")).join("\n"); + texer.rebuild(sty); + texer.initialize(); + rebuilt = true; + fs.writeFileSync(outFile, module.exports.replaceText(res), 'utf8'); + rebuilt = false; +} \ No newline at end of file diff --git a/lib/texutil.js b/lib/texutil.js index 18554e7..27cc02d 100644 --- a/lib/texutil.js +++ b/lib/texutil.js @@ -46,7 +46,7 @@ function findFirst(string, subs, start) { var min = -1; var index; var first = subs[0]; - for (var i = 0; i < subs.length; i++) { + for (var i in subs) { index = string.indexOf(subs[i], start); if ((index !== -1) && (index < min || min === -1)) { min = index; @@ -93,7 +93,7 @@ String.prototype.replaceAll = function(str1, str2, ignore) { return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2); } -function generateCode(cmd, parameterCount, replacement, argumentCount, renderOption, optionNum, code, extra) { +function generateSpecCode(cmd, parameterCount, replacement, argumentCount, renderOption, optionNum, code, extra) { if (argumentCount != 0 && renderOption == null) { return; } @@ -122,7 +122,7 @@ function generateCode(cmd, parameterCount, replacement, argumentCount, renderOpt if (pegjscode.indexOf("#" + (par + 1)) != -1) { temp = "l" + (par + 1); args.push(temp); - pegjscode = pegjscode.replaceAll("#" + (par + 1), "\" _ " + temp + ":lit _ \""); + pegjscode = pegjscode.replaceAll("{#" + (par + 1) + "}", "\" _ " + temp + ":lit _ \"").replaceAll("#" + (par + 1), "\" _ " + temp + ":lit _ \""); } } var index = args.length; @@ -131,17 +131,17 @@ function generateCode(cmd, parameterCount, replacement, argumentCount, renderOpt for (var arg = 0; arg < argumentCount; arg++) { temp = "k" + (arg + 1); args.push(temp); - pegjscode = pegjscode.replaceAll("#" + (arg + 1), "\" _ " + temp + ":lit _ \""); + pegjscode = pegjscode.replaceAll("{#" + (arg + 1) + "}", "\" _ " + temp + ":lit _ \"").replaceAll("#" + (arg + 1), "\" _ " + temp + ":lit _ \""); } } - pegjscode = (pegjscode + "\" ").replaceAll("_ \" ", "_ \""); - pegjscode = [pegjscode.replaceAll(" \" _", "\" _")].concat("{ return ast.Tex." + command + ["(\"\\\\" + cmd + "\""].concat(args).join(", ") + "); }"); + pegjscode = (pegjscode + "\" _ ").replaceAll("_ \" ", "_ \"").replaceAll(" \" _", "\" _").replaceAll(" _ \"\" _ ", " "); + pegjscode = [pegjscode].concat("{ return ast.Tex." + command + ["(\"\\\\" + cmd + "\""].concat(args).join(", ") + "); }"); astcode += Array.apply(null, Array(args.length)).map(function() {return "'self'"}).join(", ") + " ] },"; astutilcode1 += ["f"].concat(args).join(", ") + ") {\n\t"; astutilcode2 += ["match(target, f)"].concat(args.map(function(a) {return a + ".contains_func(target)"})).join(" || "); var astutilcode = astutilcode1 + astutilcode2 + ";\n\t},"; rendercode1 += ["f"].concat(args).join(", ") + ") {\n\t"; - temp = ["\"\\\\" + cmd + "\""].concat(args.map(function(a) {return "curlies(" + a + ")"})) + temp = ["\"\\\\" + cmd + "\""].concat(args.map(function(a) {return "curlies(" + a + ")"})); if (renderOption != null) { temp.splice(index + 1, 0, "\"" + Array(optionNum + 2).join("@") + "\""); } @@ -153,6 +153,32 @@ function generateCode(cmd, parameterCount, replacement, argumentCount, renderOpt render.push(rendercode); } +function generateGeneralCode(cmd, argumentCount, replacement, code) { + var command = cmd.substring(1); + replacement = replacement.replaceAll("\\", "\\\\"); + var pegjs = code[0]; + var ast = code[1]; + var astutil = code[2]; + var render = code[3]; + var args = Array.apply(null, Array(argumentCount)).map(function(a, i) {return "l" + (i + 1)}); + var pegjscode = "\"\\" + cmd + "\"" + args.map(function(arg) {return " _ " + arg + ":lit"}).join("") +" "; + pegjscode = [pegjscode].concat("{ return ast.Tex." + command + ["(\"\\" + cmd + "\""].concat(args).join(", ") + "); }"); + var astcode = command + ": { args: [ 'string', "; + astcode += Array.apply(null, Array(argumentCount)).map(function() {return "'self'"}).join(", ") + " ] },"; + var astutilcode = command + ": function(target, " + ["f"].concat(args).join(", ") + ") {\n\t\treturn "; + astutilcode += ["match(target, f)"].concat(args.map(function(a) {return a + ".contains_func(target)"})).join(" || ") + ";\n\t},"; + var rendercode = command + ": function(" + ["f"].concat(args).join(", ") + ") {\n\t\treturn \"" + replacement + "\";\n\t},"; +// rendercode += ["\"\\" + cmd + "\""].concat(args.map(function(a) {return "curlies(" + a + ")"})).join(" + ") + ";\n\t},"; + for (var i = 0; i < argumentCount; i++) { + rendercode = rendercode.replaceAll("{#" + (i + 1) + "}", "\" + curlies(l" + (i + 1) + ") + \"").replaceAll(" + \"\"", ""); + rendercode = rendercode.replaceAll("#" + (i + 1), "\" + curlies(l" + (i + 1) + ") + \"").replaceAll(" + \"\"", ""); + } + pegjs.push(pegjscode); + ast.push(astcode); + astutil.push(astutilcode); + render.push(rendercode); +} + function parseSpecFun(text, code, extra) { var sections = findBracketSections(text); var command = sections[0][0]; @@ -171,16 +197,47 @@ function parseSpecFun(text, code, extra) { var meaning = sections[3 + offset][0]; var argumentCount = parseInt(sections[4 + offset][0]); if (sections.length == 5 + offset) { - generateCode(command, parameterCount, replacement, argumentCount, null, 0, code, extra); + generateSpecCode(command, parameterCount, replacement, argumentCount, null, 0, code, extra); } else { for (var i = 0; i < sections.length - 5 - offset; i++) { var renderOption = sections[i + 5 + offset][0].replaceAll("\\", "\\\\").replaceAll("\\\\!", "\\!"); - generateCode(command, parameterCount, replacement, argumentCount, renderOption, i, code, extra); + generateSpecCode(command, parameterCount, replacement, argumentCount, renderOption, i, code, extra); + } + } +} + +function parseNewCommand(definition, code, command, cmds) { + definition = definition.substring(command.length); + var sections = findBracketSections(definition); + if (sections.length == 0) { + var commands = definition.match(/\\[a-zA-Z]+/g); + cmds[commands[0]] = commands[1]; + } + else if (sections.length == 1) { + var command = definition.replaceAll(sections[0][0], "").match(/\\[a-zA-Z]+/)[0]; + if (definition.indexOf(command) == 0) { + cmds[command] = sections[0][0]; + } + else { + cmds[sections[0][0]] = command; + } + } + else if (sections.length == 2) { + if (sections[0][1] == "[") { + var command = definition.match(/\\[a-zA-Z]+/)[0]; + generateGeneralCode(command, parseInt(sections[0][0]), sections[1][0], code); + } + else { + cmds[sections[0][0]] = sections[1][0]; } } + else if (sections.length == 3) { + generateGeneralCode(sections[0][0], parseInt(sections[1][0]), sections[2][0], code); + } } + function parseIfx(string) { var start = string.indexOf("\\ifx"); if (start == -1) { @@ -194,46 +251,55 @@ function parseIfx(string) { } } -function parseSty(sty, code) { +function bracketBalance(string) { + var balance = 0; + var open = ["(", "[", "{"]; + for (var index in open) { + balance += string.split(open[index]).length - 1; + } + var close = [")", "]", "}"]; + for (var index in close) { + balance -= string.split(close[index]).length - 1; + } + return balance; +} + +function parseSty(inp, code, cmds) { var starts = ["\\newcommand", "\\renewcommand", "\\DeclareRobustCommand", "\\defSpecFun"]; - var fs = require('fs'); - var path = require('path'); - var filePathRead = path.join(__dirname, sty); - var inp = fs.readFileSync(filePathRead, 'utf8'); - var cmds = {}; - var firstStart = findFirst(inp, starts, 0); - var first = firstStart[0]; - var next; - var nextStart; - var text; - var sections; - var prev = firstStart[1]; - while (first !== -1) { - firstStart = findFirst(inp, starts, first + prev.length); - next = firstStart[0]; - if (next === -1) { - text = inp.substring(first + prev.length, inp.indexOf("\n", first + prev.length)); - } - else { - text = inp.substring(first + prev.length, next); - } - if (text.indexOf("\n") !== -1) { - text = text.substring(0, text.indexOf("\n")); + inp = inp.split("\n").map(function(line) {return line.match(/([^\\%]|\\.|^)+/g)[0]}).join("\n"); + inp = inp.replace(/\n+/g, "\n").replace(/^\n|\n^/g, ""); + var lines = inp.split("\n"); + var lineno = 0; + while (lineno < lines.length) { + var line = lines[lineno]; + var command = findFirst(line, starts, 0); + var balance = bracketBalance(line); + while (balance != 0 || (lineno + 1 < lines.length && lines[lineno + 1].charAt(0) != "\\")) { + lineno++; + balance += bracketBalance(lines[lineno]); + line += lines[lineno]; } - if (prev === "\\defSpecFun") { - var ifx = parseIfx(text); - for (var condition in ifx) { - parseSpecFun(ifx[condition], code, String.fromCharCode(65 + parseInt(condition))); + line = line.replace(/\s/g, ""); + if (command[0] != -1) { + //console.log(command); + command = command[1]; + if (command == "\\defSpecFun") { + var ifx = parseIfx(line); + for (var condition in ifx) { + parseSpecFun(ifx[condition], code, String.fromCharCode(65 + parseInt(condition))); + } } - } - else { - sections = findBracketSections(text.replace("\n", "")); - if (sections[0][0] != undefined && sections[1][0] != undefined) { - cmds[sections[0][0]] = sections[1][0]; + else if (command == "\\newcommand" | command == "\\renewcommand") { + parseNewCommand(line, code, command, cmds); + } + else if (command == "\\DeclareRobustCommand") { + var sections = findBracketSections(line); + if (sections[0][0] != undefined && sections[1][0] != undefined) { + cmds[sections[0][0]] = sections[1][0]; + } } } - first = next; - prev = firstStart[1]; + lineno++; } return cmds; } @@ -246,18 +312,35 @@ function createFromTemplate(template, func) { return fs.writeFileSync(filePathWrite, result, 'utf8'); } -module.exports.createMacroCode = function() { +function genericCreationFunc(start, array) { + return function(data) { + var index = data.indexOf(start) + start.length; + var result = data.substring(0, index) + " " + array.join("\n ") + "\n" + data.substring(index); + return result; + } +} + +module.exports.createMacroCode = function(commandDefinitions) { try { var code = [[], [], [], []]; - module.exports.other_literals3 = parseSty("DRMFfcns.sty", code); - module.exports.other_required = module.exports.other_literals3; - var pegjs = code[0]; - var ast = code[1]; - var astutil = code[2]; - var render = code[3]; - var csv = require('csv-parse/lib/sync'); var fs = require('fs'); var path = require('path'); + var sty = path.join(__dirname, "DRMFfcns.sty"); + var inp = fs.readFileSync(sty, 'utf8'); + module.exports.other_literals3 = {}; + parseSty(inp, code, module.exports.other_literals3); + var pegjs = []; + var ast = []; + var astutil = []; + var render = []; + if (typeof commandDefinitions != "undefined") { + parseSty(commandDefinitions, code, module.exports.other_literals3); + } + module.exports.other_required = module.exports.other_literals3; + pegjs = pegjs.concat(code[0]); + ast = ast.concat(code[1]); + astutil = astutil.concat(code[2]); + render = render.concat(code[3]); createFromTemplate("parser.pegjs", function(data) { pegjs.sort(function(a, b) {return b[0].length - a[0].length}); pegjs = pegjs.map(function(a) {return a.join("")}).join("\n / "); @@ -269,25 +352,10 @@ module.exports.createMacroCode = function() { } return data; }); - createFromTemplate("ast.js", function(data) { - var start = "new Enum( 'Tex', {\n"; - var index = data.indexOf(start) + start.length; - var result = data.substring(0, index) + " " + ast.join("\n ") + "\n" + data.substring(index); - return result; - }); - createFromTemplate("astutil.js", function(data) { - var start = "ast.Tex.defineVisitor(\"contains_func\", {\n"; - var index = data.indexOf(start) + start.length; - var result = data.substring(0, index) + " " + astutil.join("\n ") + "\n" + data.substring(index); - return result; - }); - createFromTemplate("render.js", function(data) { - var start = "ast.Tex.defineVisitor(\"render_tex\", {\n"; - var index = data.indexOf(start) + start.length; - var result = data.substring(0, index) + " " + render.join("\n ") + "\n" + data.substring(index); - return result; - }); - } catch (e) {} + createFromTemplate("ast.js", genericCreationFunc("new Enum( 'Tex', {\n", ast)); + createFromTemplate("astutil.js", genericCreationFunc("ast.Tex.defineVisitor(\"contains_func\", {\n", astutil)); + createFromTemplate("render.js", genericCreationFunc("ast.Tex.defineVisitor(\"render_tex\", {\n", render)); + } catch (e) {console.log(e)} } module.exports.paren = arr2set(["\\sinh"]); @@ -440,6 +508,7 @@ module.exports.other_literals1 = arr2set([ "\\Game", "\\gamma", "\\Gamma", + "\\ge", "\\geq", "\\geqq", "\\geqslant", @@ -448,6 +517,7 @@ module.exports.other_literals1 = arr2set([ "\\ggg", "\\gimel", "\\gnapprox", + "\\gne", "\\gneq", "\\gneqq", "\\gnsim", @@ -495,6 +565,7 @@ module.exports.other_literals1 = arr2set([ "\\leftrightharpoons", "\\leftrightsquigarrow", "\\leftthreetimes", + "\\le", "\\leq", "\\leqq", "\\leqslant", @@ -509,6 +580,7 @@ module.exports.other_literals1 = arr2set([ "\\Lleftarrow", "\\lll", "\\lnapprox", + "\\lne", "\\lneq", "\\lneqq", "\\lnot", @@ -542,8 +614,10 @@ module.exports.other_literals1 = arr2set([ "\\ncong", "\\nearrow", "\\neg", + "\\ne", "\\neq", "\\nexists", + "\\nge", "\\ngeq", "\\ngeqq", "\\ngeqslant", @@ -553,6 +627,7 @@ module.exports.other_literals1 = arr2set([ "\\nLeftarrow", "\\nleftrightarrow", "\\nLeftrightarrow", + "\\nle", "\\nleq", "\\nleqq", "\\nleqslant", diff --git a/test/mathmode.js b/test/mathmode.js new file mode 100755 index 0000000..b3d97b7 --- /dev/null +++ b/test/mathmode.js @@ -0,0 +1,42 @@ +"use strict"; +var assert = require('assert'); +var mathFinder = require('../lib/mathmode.js'); +var testCases = [ + {input: '', options: {}, out: []}, + { + input: 'This is a test', + options: {}, + out: [] + }, + { + input: '$a$ and $b$', + options: {}, + out: ['a', 'b'] + }, + { + input: '$c\\hbox{and $d$}$', + options: {}, + out: ['c', 'd'] + }, + { + input: '\\$Not this\\$ \\\\[or this\\\\] \\\\(or even this\\\\) $This$', + options: {}, + out: ['This'] + }, + { + input: '$math mode\\hbox{not math {still not math}}$', + options: {}, + out: ['math mode'] + } +]; + +describe('Index', function () { + testCases.forEach(function (tc) { + var input = tc.input; + var options = tc.options; + var output = tc.out; + it('should correctly replace ' + JSON.stringify(input), function () { + assert.deepEqual(mathFinder.findMathSections(input), output); + }); + }); +}); \ No newline at end of file From 76aed6c344a95dacdde8e4553819fe95e135fd1d Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Fri, 19 Aug 2016 11:13:22 -0400 Subject: [PATCH 12/14] Increase support for DRMFfcns.sty and add support for DLMFfcns.sty. --- lib/build-parser.js | 5 +- lib/index.js | 7 +- lib/mathmode.js | 133 ------------------------------------- lib/templates/ast.js | 1 + lib/templates/astutil.js | 11 +++ lib/templates/parser.pegjs | 57 +++++++++++----- lib/templates/render.js | 56 +++++++++++----- lib/textmode.js | 113 +++++++++++++++++++++++-------- lib/texutil.js | 104 ++++++++++++++++++----------- 9 files changed, 248 insertions(+), 239 deletions(-) delete mode 100755 lib/mathmode.js diff --git a/lib/build-parser.js b/lib/build-parser.js index e32dcb1..2252ff0 100644 --- a/lib/build-parser.js +++ b/lib/build-parser.js @@ -4,7 +4,7 @@ var INPUT_FILE = __dirname + '/parser.pegjs'; var OUTPUT_FILE = __dirname + '/parser.js'; -var buildParser = module.exports = function(commandDefinitions, inFile, outFile) { +var buildParser = module.exports = function(styFile, commandDefinitions, inFile, outFile) { if (typeof inFile == "undefined") { inFile = INPUT_FILE; } @@ -14,7 +14,8 @@ var buildParser = module.exports = function(commandDefinitions, inFile, outFile) var PEG = require('pegjs'); var fs = require('fs'); var tu = require('./texutil'); - tu.createMacroCode(commandDefinitions); + tu.createMacroCode(styFile, commandDefinitions); + var parserSource = PEG.buildParser(fs.readFileSync(inFile, 'utf8'), { /* PEGJS options */ output: "source", diff --git a/lib/index.js b/lib/index.js index 07564ba..b2ba5a7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -19,8 +19,8 @@ module.exports.initialize = function() { module.exports.SyntaxError = Parser.SyntaxError; }; -module.exports.rebuild = function(commandDefinitions) { - require("./build-parser")(commandDefinitions); +module.exports.rebuild = function(styFile, commandDefinitions) { + require("./build-parser")(styFile, commandDefinitions); } module.exports.reset = function() { @@ -28,10 +28,11 @@ module.exports.reset = function() { function(file) { try { fs.unlinkSync("./" + file); + delete require.cache[require.resolve("./" + file.substring(0, file.indexOf(".js")))]; } catch (e) {} } - ) + ); }; // for only math mode input diff --git a/lib/mathmode.js b/lib/mathmode.js deleted file mode 100755 index 94511d5..0000000 --- a/lib/mathmode.js +++ /dev/null @@ -1,133 +0,0 @@ -"use strict"; - -var mathMode = {'\\[' : '\\]', - '\\(' : '\\)', - '$' : '$', - '$$' : '$$', - '\\begin{equation}' : '\\end{equation}', - '\\begin{equation*}' : '\\end{equation*}', - '\\begin{align}' : '\\end{align}', - '\\begin{align*}' : '\\end{align*}', - '\\begin{multline}' : '\\end{multline}', - '\\begin{multline*}' : '\\end{multline*}'}; - -var textMode = ['\\hbox{', - '\\mbox{', - '\\text{']; - -function firstDelim(res, enter) { - var min, list; - if (enter) { - min = '\\['; - list = mathMode; - } - else { - min = '\\hbox{'; - list = textMode; - } - for (var key in list) { - var i = res.indexOf(key); - if (i !== -1 && (i <= res.indexOf(min) || res.indexOf(min) === -1)) { - min = key; - } - } - return min; -} - -function startsWith(string, substring) { - return string.lastIndexOf(substring, 0) === 0; - -} - -function doesExit(string) { - for (var i = 0; i < textMode.length; i++) { - if (startsWith(string, textMode[i])) { - return true; - } - } - return false; -} - -function doesEnter(string) { - for (var key in mathMode) { - if (startsWith(string, key)) { - return key; - } - } - return ''; -} - -function skipEscaped(res) { - if (startsWith(res, '\\$')) { - return 2; - } - else if (startsWith(res, '\\\\[')) { - return 3; - } - else if (startsWith(res, '\\\\(')) { - return 3; - } - return 0; -} - -function parseMath(res, start, ranges) { - var delim = firstDelim(res, true); - var i = delim.length; - var begin = start + i; - while (i < res.length) { - i += skipEscaped(res.substring(i)); - if (doesExit(res.substring(i))) { - if (begin !== start + i) { - ranges.push([begin, start + i]); - } - i += parseNonMath(res.substring(i), start + i, ranges); - begin = start + i; - } - if (startsWith(res.substring(i), mathMode[delim])) { - if (begin !== start + i) { - ranges.push([begin, start + i]); - } - return i + mathMode[delim].length - 1; - } - i++; - } -} - -function parseNonMath(res, start, ranges) { - var delim = firstDelim(res, false); - if (!startsWith(res, delim)) { - delim = ''; - } - var level = 0; - var i = delim.length; - while (i < res.length) { - i += skipEscaped(res.substring(i)); - if (doesEnter(res.substring(i))) { - i += parseMath(res.substring(i), start + i, ranges); - } - else if (res.charAt(i) === '{') { - level++; - } - else if (res.charAt(i) === '}') { - if (level === 0 && delim !== '') { - i++; - return i; - } - else { - level--; - } - } - i++; - } - return i; -} - -module.exports.findMathSections = function(res) { - var ranges = []; - parseNonMath(res, 0, ranges); - var mathSections = []; - for (var index = 0; index < ranges.length; index++) { - mathSections.push(res.substring(ranges[index][0], ranges[index][1])); - } - return mathSections; -}; \ No newline at end of file diff --git a/lib/templates/ast.js b/lib/templates/ast.js index c3bc36a..9085fdc 100644 --- a/lib/templates/ast.js +++ b/lib/templates/ast.js @@ -163,6 +163,7 @@ var Tex = module.exports.Tex = new Enum( 'Tex', { FUN2sq: { args: [ 'string', 'self', 'self' ] }, FUN3: { args: [ 'string', 'self', 'self', 'self'] }, MATRIX: { args: [ 'string', [ [ [ 'self' ] ] ] ] }, + MULTLINE: { args: [ 'string', [ [ [ 'self' ] ] ] ] }, DECLh: { args: [ 'string', FontForce, [ 'self' ] ] }, JACOBI: { args: [ 'self', 'self', 'self', 'self']}, LAGUERRE1: { args:['self', 'self']}, diff --git a/lib/templates/astutil.js b/lib/templates/astutil.js index 1fddce3..07e74b1 100644 --- a/lib/templates/astutil.js +++ b/lib/templates/astutil.js @@ -196,6 +196,17 @@ ast.Tex.defineVisitor("contains_func", { match(target, '\\end{'+t+'}') || matrix_has(m); }, + MULTLINE: function(target, t, m) { + // \begin{env} .. \\ .. \\ .. \\ .. \end{env} + // t is the environment name. + // m is a one-dimensional array. + var expr_has = function(e) { return arr_contains_func(e, target); }; + var line_has = function(l) { return some(l, expr_has); }; + var lines_have = function(m) { return some(m, line_has); }; + return match(target, '\\begin{'+t+'}') || + match(target, '\\end{'+t+'}') || + lines_have(m); + }, LR: function(target, l, r, tl) { // \left\l tl1 tl2 tl3 ... \right\r (a balanced pair of delimiters) return match(target, '\\left') || match(target, '\\right') || diff --git a/lib/templates/parser.pegjs b/lib/templates/parser.pegjs index edeed68..ca8588c 100644 --- a/lib/templates/parser.pegjs +++ b/lib/templates/parser.pegjs @@ -164,9 +164,9 @@ lit / r:LITERAL { return ast.Tex.LITERAL(r); } // quasi-literal; this is from Texutil.find(...) but the result is not // guaranteed to be Tex.LITERAL(RenderT...) - / f:generic_func &{ return tu.other_literals3[f]; } _ // from Texutil.find(...) + / f:generic_func &{ return tu.other_literals3[f]; } s:_ // from Texutil.find(...) { - var ast = peg$parse(tu.other_literals3[f]); + var ast = peg$parse(tu.other_literals3[f] + s); console.assert(Array.isArray(ast) && ast.length === 1); return ast[0]; } @@ -218,6 +218,10 @@ lit { return ast.Tex.MATRIX("smallmatrix", lst2arr(m)); } / BEGIN_CASES m:matrix END_CASES { return ast.Tex.MATRIX("cases", lst2arr(m)); } + / BEGIN_MULTLINE m:multline END_MULTLINE + { return ast.Tex.MULTLINE("multline", lst2arr(m)); } + / BEGIN_MULTLINE_STAR m:multline END_MULTLINE_STAR + { return ast.Tex.MULTLINE("multline*", lst2arr(m)); } / "\\begin{" alpha+ "}" /* better error messages for unknown environments */ { throw new peg$SyntaxError("Illegal TeX function", [], text(), location()); } / f:generic_func &{ return !tu.all_functions[f]; } @@ -295,11 +299,15 @@ litstuff = { return ast.Tex.MATRIX("smallmatrix", lst2arr(m)); } / BEGIN_CASES m:matrix END_CASES { return ast.Tex.MATRIX("cases", lst2arr(m)); } + / BEGIN_MULTLINE m:multline END_MULTLINE + { return ast.Tex.MULTLINE("multline", lst2arr(m)); } + / BEGIN_MULTLINE_STAR m:multline END_MULTLINE_STAR + { return ast.Tex.MULTLINE("multline*", lst2arr(m)); } / "\\begin{" alpha+ "}" /* better error messages for unknown environments */ { throw new peg$SyntaxError("Illegal TeX function", [], text(), location()); } + / l:LITERALPART { return ast.Tex.LITERAL(l); } / f:generic_func &{ return !tu.all_functions[f]; } { throw new peg$SyntaxError("Illegal TeX function", [], text(), location()); } - / l:LITERALPART { return ast.Tex.LITERAL(l); } litparen1 = @@ -349,6 +357,15 @@ line = e:expr tail:( NEXT_CELL l:line { return l; } )? { return { head: e.toArray(), tail: tail }; } +multline + = l:mline_start tail:( NEXT_ROW m:multline { return m; } )? + { return { head: lst2arr(l), tail: tail }; } +mline_start + = f:HLINE m:mline_start + { m.head.unshift(ast.Tex.LITERAL(ast.RenderT.TEX_ONLY(f + " "))); return m; } + / e:expr + { return { head: e.toArray(), tail: null }; } + column_spec = CURLY_OPEN cs:(one_col+ { return text(); }) CURLY_CLOSE { return ast.Tex.CURLY([ast.Tex.LITERAL(ast.RenderT.TEX_ONLY(cs))]); } @@ -407,20 +424,20 @@ LITERAL / f:generic_func &{ return tu.mediawiki_function_names[f]; } _ c:( "(" / "[" / "\\{" / "" { return "";}) _ { return ast.RenderT.TEX_ONLY("\\operatorname{" + f.slice(1) + "}" + c); } - / f:generic_func &{ return tu.other_literals1[f]; } _ // from Texutil.find(...) - { return ast.RenderT.TEX_ONLY(f + " "); } - / f:generic_func &{ return options.usemathrm && tu.other_literals2[f]; } _ // from Texutil.find(...) - { return ast.RenderT.TEX_ONLY("\\mathrm{" + f + "} "); } + / f:generic_func &{ return tu.other_literals1[f]; } s:_ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY(f + s); } + / f:generic_func &{ return options.usemathrm && tu.other_literals2[f]; } s:_ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY("\\mathrm{" + f + "}" + s); } / mathrm:generic_func &{ return options.usemathrm && mathrm === "\\mathrm"; } _ - "{" f:generic_func &{ return options.usemathrm && tu.other_literals2[f]; } _ "}" _ + "{" f:generic_func &{ return options.usemathrm && tu.other_literals2[f]; } _ "}" s:_ /* make sure we can parse what we emit */ - { return options.usemathrm && ast.RenderT.TEX_ONLY("\\mathrm{" + f + "} "); } - / f:generic_func &{ return tu.other_literals2[f]; } _ // from Texutil.find(...) - { return ast.RenderT.TEX_ONLY("\\mbox{" + f + "} "); } + { return options.usemathrm && ast.RenderT.TEX_ONLY("\\mathrm{" + f + "}" + s); } + / f:generic_func &{ return tu.other_literals2[f]; } s:_ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY("\\mbox{" + f + "}" + s); } / mbox:generic_func &{ return mbox === "\\mbox"; } _ - "{" f:generic_func &{ return tu.other_literals2[f]; } _ "}" _ + "{" f:generic_func &{ return tu.other_literals2[f]; } _ "}" s:_ /* make sure we can parse what we emit */ - { return ast.RenderT.TEX_ONLY("\\mbox{" + f + "} "); } + { return ast.RenderT.TEX_ONLY("\\mbox{" + f + "}" + s); } / f:(COLOR / DEFINECOLOR) { return ast.RenderT.TEX_ONLY(f); } / "\\" c:[, ;!_#%$&] _ @@ -446,8 +463,8 @@ LITERALPART //LITERAL without the first option to prevent interruption of parsin "{" f:generic_func &{ return tu.other_literals2[f]; } _ "}" _ /* make sure we can parse what we emit */ { return ast.RenderT.TEX_ONLY("\\mbox{" + f + "} "); } - / f:generic_func &{ return tu.other_literals3[f]; } _ // from Texutil.find(...) - { return ast.RenderT.TEX_ONLY(tu.other_literals3[f] + " "); } + / f:generic_func &{ return tu.other_literals3[f]; } s:_ // from Texutil.find(...) + { return ast.RenderT.TEX_ONLY(tu.other_literals3[f] + s); } / f:(COLOR / DEFINECOLOR) { return ast.RenderT.TEX_ONLY(f); } / "\\" c:[, ;!_#%$&] _ @@ -473,7 +490,7 @@ DELIMITER return p[0][0]; } / c:atsymbol _ //temporary workaround until @ symbol is properly implemented (bypasses syntax error in test cases) - { return ast.RenderT.TEX_ONLY(""); } + { return ast.RenderT.TEX_ONLY("@"); } FUN_AR1nb = f:generic_func &{ return tu.fun_ar1nb[f]; } space* { return f; } @@ -544,6 +561,14 @@ BEGIN_CASES = BEGIN "{cases}" _ END_CASES = END "{cases}" _ +BEGIN_MULTLINE + = BEGIN "{multline}" _ +END_MULTLINE + = END "{multline}" _ +BEGIN_MULTLINE_STAR + = BEGIN "{multline*}" _ +END_MULTLINE_STAR + = END "{multline*}" _ SQ_CLOSE = "]" _ diff --git a/lib/templates/render.js b/lib/templates/render.js index 34d7b7d..72566f0 100644 --- a/lib/templates/render.js +++ b/lib/templates/render.js @@ -20,10 +20,21 @@ var render = module.exports = function render(e) { return e.render_tex(); }; -var curlies = function(t) { +var curlies = function(t, optional, alpha) { + if (optional == null) { + optional = true; + } + if (alpha == null) { + alpha = true; + } switch (t.constructor) { // constructs which are surrounded by curlies case ast.Tex.CURLY: + t = t.render_tex(); + if (t.charAt(0) !== "{" || t.charAt(t.length - 1) !== "}") { + t = "{" + t + "}"; + } + return t; case ast.Tex.MATRIX: return t.render_tex(); case String: @@ -31,46 +42,51 @@ var curlies = function(t) { default: t = t.render_tex(); } - return "{" + t + "}"; + if (!optional || (alpha && t.match(/^[a-zA-Z]/))) { + return "{" + t + "}"; + } + else { + return t; + } }; -var render_curlies = function(a) { +var render_curlies = function(a, optional, alpha) { if (a.length === 1) { - return curlies(a[0]); + return curlies(a[0], optional, alpha); } - return curlies(render(a)); + return curlies(render(a), optional, alpha); }; ast.Tex.defineVisitor("render_tex", { FQ: function(base, down, up) { - return base.render_tex() + "_" + curlies(down) + "^" + curlies(up); + return base.render_tex() + "_" + curlies(down, true, false) + "^" + curlies(up, true, false); }, DQ: function(base, down) { - return base.render_tex() + "_" + curlies(down); + return base.render_tex() + "_" + curlies(down, true, false); }, UQ: function(base, up) { - return base.render_tex() + "^" + curlies(up); + return base.render_tex() + "^" + curlies(up, true, false); }, FQN: function(down, up) { - return "_" + curlies(down) + "^" + curlies(up); + return "_" + curlies(down, true, false) + "^" + curlies(up, true, false); }, DQN: function(down) { - return "_" + curlies(down); + return "_" + curlies(down, true, false); }, UQN: function(up) { - return "^" + curlies(up); + return "^" + curlies(up, true, false); }, JACOBI: function(a, b, c, d) { - return "\\Jacobi" + curlies(a) + curlies(b) + curlies(c) + "@" + curlies(d); + return "\\Jacobi" + curlies(a, false) + curlies(b, false) + curlies(c, false) + "@" + curlies(d, false); }, LAGUERRE1: function(a, b) { - return "\\Laguerre" + curlies(a) + "@" + curlies(b); + return "\\Laguerre" + curlies(a, false) + "@" + curlies(b, false); }, LAGUERRE2: function(a, b, c) { - return "\\Laguerre" + curlies(a) + "[" + b.render_tex() + "]" + "@" + curlies(c); + return "\\Laguerre" + curlies(a, false) + "[" + b.render_tex() + "]" + "@" + curlies(c, false); }, EJACOBI: function(a, b, c) { - return "\\Jacobi" + a + "@" + curlies(b) + curlies(c); + return "\\Jacobi" + a + "@" + curlies(b, false) + curlies(c, false); }, LITERAL: function(r) { return r.tex_part(); @@ -82,10 +98,10 @@ ast.Tex.defineVisitor("render_tex", { return f + "@@" + curlies(a); }, QPOCH: function(a, b, c) { - return "\\qPochhammer" + curlies(a) + curlies(b) + curlies(c); + return "\\qPochhammer" + curlies(a, false) + curlies(b, false) + curlies(c, false); }, POCH: function(a, b) { - return "\\pochhammer" + curlies(a) + curlies(b); + return "\\pochhammer" + curlies(a, false) + curlies(b, false); }, FUN1: function(f, a) { return f + curlies(a); @@ -109,7 +125,7 @@ ast.Tex.defineVisitor("render_tex", { return f + curlies(a) + curlies(b) + curlies(c); }, CURLY: function(tl) { - return render_curlies(tl); + return render_curlies(tl, false); }, INFIX: function(s, ll, rl) { return curlies(render(ll) + " " + s + " " + render(rl)); @@ -125,6 +141,10 @@ ast.Tex.defineVisitor("render_tex", { var render_matrix = function(m) { return m.map(render_line).join('\\\\'); }; return curlies("\\begin{"+t+"}" + render_matrix(m) + "\\end{"+t+"}"); }, + MULTLINE: function(t, m) { + var render_lines = function(l) { return l.map(render).join('\\\\'); }; + return "\\begin{"+t+"}" + render_lines(m) + "\\end{"+t+"}"; + }, LR: function(l, r, tl) { return "\\left" + l.tex_part() + render(tl) + "\\right" + r.tex_part(); } diff --git a/lib/textmode.js b/lib/textmode.js index 2b591e6..fff3a5b 100644 --- a/lib/textmode.js +++ b/lib/textmode.js @@ -2,6 +2,8 @@ var texer = module.exports.texer = require("./"); texer.reset(); +var path = require('path'); +var fs = require('fs'); var rebuilt = false; @@ -29,21 +31,23 @@ var escapes = ['\\$', '\\%']; var insertion = "{--insertion--}"; +var newline = "{--newline--}" -function makeInsertions(string, sections, replacements) { +function makeInsertions(string, sections, replacements, undef) { var offset = 0; for (var index in sections) { var section = sections[index]; var start = section.startIndex + offset, end = section.endIndex + offset + 1; - replacements.push(section.makeReplacements(string.substring(start, end))); + replacements.push(section.makeReplacements(string.substring(start, end), undef)); string = string.substring(0, start) + insertion + string.substring(end); offset += insertion.length - end + start; } return string; } -function TextMode(startIndex) { +function TextMode(startIndex, lineNumber) { this.startIndex = startIndex; + this.lineNumber = lineNumber; this.mathSections = []; } @@ -52,9 +56,9 @@ TextMode.prototype.addMathSection = function(mathSection) { } -TextMode.prototype.makeReplacements = function(string) { +TextMode.prototype.makeReplacements = function(string, undef) { var replacements = []; - string = makeInsertions(string, this.mathSections, replacements); + string = makeInsertions(string, this.mathSections, replacements, undef); for (var index in replacements) { var replacement = replacements[index]; string = string.replace(insertion, replacement.replace(/\$/g, '$$$')); @@ -62,8 +66,9 @@ TextMode.prototype.makeReplacements = function(string) { return string; } -function MathMode(startIndex) { +function MathMode(startIndex, lineNumber) { this.startIndex = startIndex; + this.lineNumber = lineNumber; this.textSections = []; } @@ -72,13 +77,30 @@ MathMode.prototype.addTextSection = function(textSection) { } -MathMode.prototype.makeReplacements = function(string) { +MathMode.prototype.makeReplacements = function(string, undef) { var replacements = []; - string = makeInsertions(string, this.textSections, replacements); - var delimStart = this.delim, delimEnd = mathStart[this.delim]; - string = string.substring(delimStart.length, string.length - delimEnd.length); - var output = texer.texer(string).output; - string = delimStart + output + delimEnd; + var original = string; + string = makeInsertions(string, this.textSections, replacements, undef); + var delimStart = this.delim; + var delimEnd = mathStart[delimStart]; + if (["\\begin{multline}", "\\begin{multline*}"].indexOf(delimStart) === -1) { + string = string.substring(delimStart.length, string.length - delimEnd.length); + var output = texer.texer(string).output + ""; + if (output === "undefined") { + undef.push("[" + this.lineNumber + "]\n" + original); + string = original; + } + else { + string = delimStart + output + delimEnd; + } + } + else { + var string = texer.texer(string).output + ""; + if (string === "undefined") { + undef.push("[" + this.lineNumber + "]\n" + original); + string = original; + } + } for (var index in replacements) { var replacement = replacements[index]; string = string.replace(insertion, replacement.replace(/\$/g, '$$$')); @@ -138,37 +160,45 @@ function skipEscaped(res) { return 0; } -function parseMath(res, startIndex) { - var mathSection = new MathMode(startIndex); +function parseMath(res, startIndex, lineNumber) { + var mathSection = new MathMode(startIndex, lineNumber); mathSection.delim = firstDelim(res, true); var i = mathSection.delim.length; var end = mathStart[mathSection.delim]; while (i < res.length) { i += skipEscaped(res.substring(i)); var sub = res.substring(i); - if (sub.charAt(0) == "%") { - var textSection = parseText(sub.substring(0, sub.indexOf("\n")), i); + if (sub.charAt(0) === "%") { + var textSection = parseText(sub.substring(0, sub.indexOf(newline)), i, lineNumber); mathSection.addTextSection(textSection); i += textSection.endIndex - textSection.startIndex; + lineNumber = textSection.endLine; } if (doesExit(sub)) { - var textSection = parseText(sub, i); + var textSection = parseText(sub, i, lineNumber); mathSection.addTextSection(textSection); i += textSection.endIndex - textSection.startIndex; + lineNumber = textSection.endLine; } if (startsWith(sub, end)) { mathSection.endIndex = mathSection.startIndex + i + end.length - 1; + mathSection.endLine = lineNumber; return mathSection; } + if (startsWith(sub, newline)) { + i += newline.length - 1; + lineNumber += 1; + } i++; } } -function parseText(res, startIndex) { +function parseText(res, startIndex, lineNumber) { if (startIndex == undefined) { startIndex = 0; + lineNumber = 1; } - var textSection = new TextMode(startIndex); + var textSection = new TextMode(startIndex, lineNumber); var delim = firstDelim(res, false); if (!startsWith(res, delim)) { delim = ''; @@ -189,9 +219,10 @@ function parseText(res, startIndex) { continue; } if (doesEnter(sub)) { - var mathSection = parseMath(sub, i); + var mathSection = parseMath(sub, i, lineNumber); textSection.addMathSection(mathSection); i += mathSection.endIndex - mathSection.startIndex; + lineNumber = mathSection.endLine; } else if (sub.charAt(0) === '{') { level++; @@ -199,40 +230,64 @@ function parseText(res, startIndex) { else if (sub.charAt(0) === '}') { if (level === 0 && delim !== '') { textSection.endIndex = startIndex + i; + textSection.endLine = lineNumber; return textSection; } level--; } } - else if (res.charAt(i) == "\n") { + if (startsWith(res.substring(i), newline)) { commented = false; + i += newline.length - 1; + lineNumber += 1; } i++; } textSection.endIndex = startIndex + i - 1; + textSection.endLine = lineNumber; return textSection; } -module.exports.replaceText = function(res, preserveBuild) { +module.exports.replaceText = function(res, preserveBuild, logFile, undef) { if (!rebuilt && !preserveBuild) { rebuilt = true; - texer.rebuild(); + texer.reset(); + texer.rebuild("DRMFfcns.sty"); texer.initialize(); } - return parseText(res).makeReplacements(res); + if (!undef) { + undef = []; + } + res = res.replace(/\r?\n/g, newline); + var result = parseText(res).makeReplacements(res, undef).replace(new RegExp(newline, "g"), "\n"); + if (logFile) { + fs.writeFileSync(path.join(__dirname, logFile), undef.join("\n\n").replace(new RegExp(newline, "g"), "\n")); + } + return result; } -module.exports.replaceFile = function(inFile, outFile) { - var path = require('path'); - var fs = require('fs'); +var replaceFile = module.exports.replaceFile = function(inFile, outFile, styFile, logFile, undef) { + if (!logFile) { + logFile = inFile + ".log"; + } + if (!undef) { + undef = []; + } + if (!styFile) { + replaceFile(inFile, outFile, "DRMFfcns.sty", logFile, undef); + replaceFile(outFile, outFile, "DLMFfcns.sty", logFile, undef); +// replaceFile(outFile, outFile, "DLMFmath.sty", logFile); + return; + } inFile = path.join(__dirname, inFile); outFile = path.join(__dirname, outFile); var res = fs.readFileSync(inFile, 'utf8'); var lines = res.split("\n").map(function(line) {return line.trim();}); var sty = lines.slice(0, lines.indexOf("\\begin{document}")).join("\n"); - texer.rebuild(sty); + texer.reset(); + texer.rebuild(styFile, sty); texer.initialize(); rebuilt = true; - fs.writeFileSync(outFile, module.exports.replaceText(res), 'utf8'); + fs.writeFileSync(outFile, module.exports.replaceText(res, true, logFile, undef), 'utf8'); rebuilt = false; } \ No newline at end of file diff --git a/lib/texutil.js b/lib/texutil.js index 27cc02d..68ded17 100644 --- a/lib/texutil.js +++ b/lib/texutil.js @@ -42,6 +42,9 @@ module.exports.latex_function_names = arr2set([ "\\sin", "\\sinh", "\\sup", "\\tan", "\\tanh" ]); +var space = " space* " +var type = ":lit" + function findFirst(string, subs, start) { var min = -1; var index; @@ -122,7 +125,7 @@ function generateSpecCode(cmd, parameterCount, replacement, argumentCount, rende if (pegjscode.indexOf("#" + (par + 1)) != -1) { temp = "l" + (par + 1); args.push(temp); - pegjscode = pegjscode.replaceAll("{#" + (par + 1) + "}", "\" _ " + temp + ":lit _ \"").replaceAll("#" + (par + 1), "\" _ " + temp + ":lit _ \""); + pegjscode = pegjscode.replaceAll("{#" + (par + 1) + "}", "\"" + space + temp + type + space + "\"").replaceAll("#" + (par + 1), "\"" + space + temp + type + space + "\""); } } var index = args.length; @@ -131,17 +134,18 @@ function generateSpecCode(cmd, parameterCount, replacement, argumentCount, rende for (var arg = 0; arg < argumentCount; arg++) { temp = "k" + (arg + 1); args.push(temp); - pegjscode = pegjscode.replaceAll("{#" + (arg + 1) + "}", "\" _ " + temp + ":lit _ \"").replaceAll("#" + (arg + 1), "\" _ " + temp + ":lit _ \""); + pegjscode = pegjscode.replaceAll("{#" + (arg + 1) + "}", "\"" + space + temp + type + space + "\"").replaceAll("#" + (arg + 1), "\"" + space + temp + type + space + "\""); } } - pegjscode = (pegjscode + "\" _ ").replaceAll("_ \" ", "_ \"").replaceAll(" \" _", "\" _").replaceAll(" _ \"\" _ ", " "); + pegjscode = (pegjscode + "\"" + space).replaceAll(space + "\" ", space + "\"").replaceAll(" \"" + space, "\"" + space).replaceAll(space + "\"\"" + space, " "); + pegjscode = pegjscode.replaceAll("\\\\left(", "\" PAREN_OPEN \"").replaceAll("\\\\right)", "\" PAREN_CLOSE \"").replaceAll(" \"\" ", " "); pegjscode = [pegjscode].concat("{ return ast.Tex." + command + ["(\"\\\\" + cmd + "\""].concat(args).join(", ") + "); }"); astcode += Array.apply(null, Array(args.length)).map(function() {return "'self'"}).join(", ") + " ] },"; astutilcode1 += ["f"].concat(args).join(", ") + ") {\n\t"; astutilcode2 += ["match(target, f)"].concat(args.map(function(a) {return a + ".contains_func(target)"})).join(" || "); var astutilcode = astutilcode1 + astutilcode2 + ";\n\t},"; rendercode1 += ["f"].concat(args).join(", ") + ") {\n\t"; - temp = ["\"\\\\" + cmd + "\""].concat(args.map(function(a) {return "curlies(" + a + ")"})); + temp = ["\"\\\\" + cmd + "\""].concat(args.map(function(a) {return "curlies(" + a + ", false)"})); if (renderOption != null) { temp.splice(index + 1, 0, "\"" + Array(optionNum + 2).join("@") + "\""); } @@ -161,17 +165,17 @@ function generateGeneralCode(cmd, argumentCount, replacement, code) { var astutil = code[2]; var render = code[3]; var args = Array.apply(null, Array(argumentCount)).map(function(a, i) {return "l" + (i + 1)}); - var pegjscode = "\"\\" + cmd + "\"" + args.map(function(arg) {return " _ " + arg + ":lit"}).join("") +" "; + var pegjscode = "\"\\" + cmd + "\"" + args.map(function(arg) {return space + arg + type}).join("") +" "; + pegjscode = pegjscode.replaceAll("\\\\left(", "\" PAREN_OPEN \"").replaceAll("\\\\right)", "\" PAREN_CLOSE \"").replaceAll(" \"\" ", " "); pegjscode = [pegjscode].concat("{ return ast.Tex." + command + ["(\"\\" + cmd + "\""].concat(args).join(", ") + "); }"); var astcode = command + ": { args: [ 'string', "; astcode += Array.apply(null, Array(argumentCount)).map(function() {return "'self'"}).join(", ") + " ] },"; var astutilcode = command + ": function(target, " + ["f"].concat(args).join(", ") + ") {\n\t\treturn "; astutilcode += ["match(target, f)"].concat(args.map(function(a) {return a + ".contains_func(target)"})).join(" || ") + ";\n\t},"; var rendercode = command + ": function(" + ["f"].concat(args).join(", ") + ") {\n\t\treturn \"" + replacement + "\";\n\t},"; -// rendercode += ["\"\\" + cmd + "\""].concat(args.map(function(a) {return "curlies(" + a + ")"})).join(" + ") + ";\n\t},"; for (var i = 0; i < argumentCount; i++) { - rendercode = rendercode.replaceAll("{#" + (i + 1) + "}", "\" + curlies(l" + (i + 1) + ") + \"").replaceAll(" + \"\"", ""); - rendercode = rendercode.replaceAll("#" + (i + 1), "\" + curlies(l" + (i + 1) + ") + \"").replaceAll(" + \"\"", ""); + rendercode = rendercode.replaceAll("{#" + (i + 1) + "}", "\" + curlies(l" + (i + 1) + ", false) + \"").replaceAll(" + \"\"", ""); + rendercode = rendercode.replaceAll("#" + (i + 1), "\" + curlies(l" + (i + 1) + ", false) + \"").replaceAll(" + \"\"", ""); } pegjs.push(pegjscode); ast.push(astcode); @@ -180,8 +184,27 @@ function generateGeneralCode(cmd, argumentCount, replacement, code) { } function parseSpecFun(text, code, extra) { + var space_delims = [ + "\\,", + "\\;", + "\\ ", + "\\!", + "\\>", + "\\:", + "\\enspace", + "\\quad", + "\\qquad", + "\\thinspace", + "\\thinmuskip", + "\\medmuskip", + "\\thickmuskip" + ] + for (var i in space_delims) { + text = text.replace(space_delims[i], ""); + } var sections = findBracketSections(text); var command = sections[0][0]; + module.exports.other_literals1["\\" + command] = all_functions["\\" + command] = true; var offset = 0; var parameterCount = 0; if (sections[1][1] == "[") { @@ -193,7 +216,7 @@ function parseSpecFun(text, code, extra) { else { offset = -1; } - var replacement = sections[2 + offset][0].replaceAll("\\", "\\\\").replaceAll("\\\\!", "\\!"); + var replacement = sections[2 + offset][0].replaceAll("\\", "\\\\"); var meaning = sections[3 + offset][0]; var argumentCount = parseInt(sections[4 + offset][0]); if (sections.length == 5 + offset) { @@ -201,14 +224,17 @@ function parseSpecFun(text, code, extra) { } else { for (var i = 0; i < sections.length - 5 - offset; i++) { - var renderOption = sections[i + 5 + offset][0].replaceAll("\\", "\\\\").replaceAll("\\\\!", "\\!"); + var renderOption = sections[i + 5 + offset][0].replaceAll("\\", "\\\\"); generateSpecCode(command, parameterCount, replacement, argumentCount, renderOption, i, code, extra); } } } function parseNewCommand(definition, code, command, cmds) { - definition = definition.substring(command.length); +// if (command == "\\DeclareRobustCommand") { +// console.log(definition); +// } + definition = definition.substring(command.length).replaceAll("\\,", ""); var sections = findBracketSections(definition); if (sections.length == 0) { var commands = definition.match(/\\[a-zA-Z]+/g); @@ -235,6 +261,11 @@ function parseNewCommand(definition, code, command, cmds) { else if (sections.length == 3) { generateGeneralCode(sections[0][0], parseInt(sections[1][0]), sections[2][0], code); } + else if (sections.length == 4) { + if (sections[2][0] == "" && sections[2][1] == "[") { + generateGeneralCode(sections[0][0], parseInt(sections[1][0]), sections[3][0], code); + } + } } @@ -274,14 +305,15 @@ function parseSty(inp, code, cmds) { var line = lines[lineno]; var command = findFirst(line, starts, 0); var balance = bracketBalance(line); - while (balance != 0 || (lineno + 1 < lines.length && lines[lineno + 1].charAt(0) != "\\")) { - lineno++; - balance += bracketBalance(lines[lineno]); - line += lines[lineno]; - } - line = line.replace(/\s/g, ""); if (command[0] != -1) { - //console.log(command); + while (lineno + 1 < lines.length && (balance != 0 || lines[lineno + 1].charAt(0).match(/\s/))) { + lineno++; + balance += bracketBalance(lines[lineno]); + line += lines[lineno]; + } + line = line.replace(/\t|\r?\n|\s{2,}/g, ""); +// console.log(line); +// console.log(command); command = command[1]; if (command == "\\defSpecFun") { var ifx = parseIfx(line); @@ -289,15 +321,9 @@ function parseSty(inp, code, cmds) { parseSpecFun(ifx[condition], code, String.fromCharCode(65 + parseInt(condition))); } } - else if (command == "\\newcommand" | command == "\\renewcommand") { + else if (["\\newcommand", "\\renewcommand", "\\DeclareRobustCommand"].indexOf(command) !== -1) { parseNewCommand(line, code, command, cmds); } - else if (command == "\\DeclareRobustCommand") { - var sections = findBracketSections(line); - if (sections[0][0] != undefined && sections[1][0] != undefined) { - cmds[sections[0][0]] = sections[1][0]; - } - } } lineno++; } @@ -320,27 +346,24 @@ function genericCreationFunc(start, array) { } } -module.exports.createMacroCode = function(commandDefinitions) { - try { +module.exports.createMacroCode = function(styFile, commandDefinitions) { +// try { var code = [[], [], [], []]; var fs = require('fs'); var path = require('path'); - var sty = path.join(__dirname, "DRMFfcns.sty"); + var sty = path.join(__dirname, styFile); var inp = fs.readFileSync(sty, 'utf8'); module.exports.other_literals3 = {}; parseSty(inp, code, module.exports.other_literals3); - var pegjs = []; - var ast = []; - var astutil = []; - var render = []; if (typeof commandDefinitions != "undefined") { - parseSty(commandDefinitions, code, module.exports.other_literals3); + module.exports.other_literals3 = parseSty(commandDefinitions, code, module.exports.other_literals3); } module.exports.other_required = module.exports.other_literals3; - pegjs = pegjs.concat(code[0]); - ast = ast.concat(code[1]); - astutil = astutil.concat(code[2]); - render = render.concat(code[3]); +// console.log(module.exports.other_literals3); + var pegjs = code[0]; + var ast = code[1]; + var astutil = code[2]; + var render = code[3]; createFromTemplate("parser.pegjs", function(data) { pegjs.sort(function(a, b) {return b[0].length - a[0].length}); pegjs = pegjs.map(function(a) {return a.join("")}).join("\n / "); @@ -355,7 +378,7 @@ module.exports.createMacroCode = function(commandDefinitions) { createFromTemplate("ast.js", genericCreationFunc("new Enum( 'Tex', {\n", ast)); createFromTemplate("astutil.js", genericCreationFunc("ast.Tex.defineVisitor(\"contains_func\", {\n", astutil)); createFromTemplate("render.js", genericCreationFunc("ast.Tex.defineVisitor(\"render_tex\", {\n", render)); - } catch (e) {console.log(e)} +// } catch (e) {console.log(e)} } module.exports.paren = arr2set(["\\sinh"]); @@ -408,6 +431,7 @@ module.exports.other_literals1 = arr2set([ "\\bigodot", "\\bigoplus", "\\bigotimes", + "\\bigskipamount", "\\bigsqcup", "\\bigstar", "\\bigtriangledown", @@ -509,6 +533,7 @@ module.exports.other_literals1 = arr2set([ "\\gamma", "\\Gamma", "\\ge", + "\\genfrac", "\\geq", "\\geqq", "\\geqslant", @@ -725,6 +750,7 @@ module.exports.other_literals1 = arr2set([ "\\simeq", "\\smallfrown", "\\smallsetminus", + "\\smallskipamount", "\\smallsmile", "\\smile", "\\spadesuit", @@ -1111,6 +1137,8 @@ module.exports.ams_required = arr2set([ "\\begin{alignedat}", "\\begin{smallmatrix}", "\\begin{cases}", + "\\begin{multline}", + "\\begin{multline*}", "\\ulcorner", "\\urcorner", From 367fbbf85df1483a45aac97e5853803007d32d7f Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Fri, 14 Oct 2016 16:34:06 -0400 Subject: [PATCH 13/14] Add functionality for more delimiters and logging --- .gitignore | 2 ++ lib/index.js | 2 +- lib/templates/parser.pegjs | 28 +++++++++++++++++++----- lib/textmode.js | 45 ++++++++++++++++++++++++++++++++++---- lib/texutil.js | 23 +++++++++++++++---- package.json | 3 ++- 6 files changed, 87 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 097950e..c8ddb99 100644 --- a/.gitignore +++ b/.gitignore @@ -91,5 +91,7 @@ lib/astutil.js lib/parser.pegjs lib/parser.js lib/render.js +lib/out.tex +lib/copy.tex texer.iml \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index b2ba5a7..e70b3fa 100644 --- a/lib/index.js +++ b/lib/index.js @@ -71,7 +71,7 @@ module.exports.texer = function(input, options) { return result; } catch (e) { -// console.log(e); + console.log(e); if (options && options.debug) { throw e; } diff --git a/lib/templates/parser.pegjs b/lib/templates/parser.pegjs index ca8588c..cd50ca0 100644 --- a/lib/templates/parser.pegjs +++ b/lib/templates/parser.pegjs @@ -128,7 +128,6 @@ lit_dqn = SUB l:lit { return ast.Tex.DQN(l); } - left = LEFT d:DELIMITER { return d; } @@ -168,6 +167,7 @@ lit { var ast = peg$parse(tu.other_literals3[f] + s); console.assert(Array.isArray(ast) && ast.length === 1); + console.log(ast[0]); return ast[0]; } / r:DELIMITER { return ast.Tex.LITERAL(r); } @@ -207,20 +207,24 @@ lit / BEGIN_ARRAY opt_pos m:array END_ARRAY { return ast.Tex.MATRIX("array", lst2arr(m)); } / BEGIN_ALIGN opt_pos m:matrix END_ALIGN - { return ast.Tex.MATRIX("aligned", lst2arr(m)); } + { return ast.Tex.MATRIX("align", lst2arr(m)); } + / BEGIN_ALIGN_STAR opt_pos m:matrix END_ALIGN_STAR + { return ast.Tex.MATRIX("align*", lst2arr(m)); } / BEGIN_ALIGNED opt_pos m:matrix END_ALIGNED // parse what we emit { return ast.Tex.MATRIX("aligned", lst2arr(m)); } / BEGIN_ALIGNAT m:alignat END_ALIGNAT - { return ast.Tex.MATRIX("alignedat", lst2arr(m)); } + { return ast.Tex.MATRIX("alignat", lst2arr(m)); } / BEGIN_ALIGNEDAT m:alignat END_ALIGNEDAT // parse what we emit { return ast.Tex.MATRIX("alignedat", lst2arr(m)); } / BEGIN_SMALLMATRIX m:(array/matrix) END_SMALLMATRIX { return ast.Tex.MATRIX("smallmatrix", lst2arr(m)); } / BEGIN_CASES m:matrix END_CASES { return ast.Tex.MATRIX("cases", lst2arr(m)); } + / BEGIN_SPLIT m:matrix END_SPLIT + { return ast.Tex.MATRIX("split", lst2arr(m)); } / BEGIN_MULTLINE m:multline END_MULTLINE { return ast.Tex.MULTLINE("multline", lst2arr(m)); } - / BEGIN_MULTLINE_STAR m:multline END_MULTLINE_STAR + / BEGIN_MULTLINE_STAR m:multline END_MULTLINE_STAR { return ast.Tex.MULTLINE("multline*", lst2arr(m)); } / "\\begin{" alpha+ "}" /* better error messages for unknown environments */ { throw new peg$SyntaxError("Illegal TeX function", [], text(), location()); } @@ -288,17 +292,21 @@ litstuff = / BEGIN_ARRAY opt_pos m:array END_ARRAY { return ast.Tex.MATRIX("array", lst2arr(m)); } / BEGIN_ALIGN opt_pos m:matrix END_ALIGN - { return ast.Tex.MATRIX("aligned", lst2arr(m)); } + { return ast.Tex.MATRIX("align", lst2arr(m)); } + / BEGIN_ALIGN_STAR opt_pos m:matrix END_ALIGN_STAR + { return ast.Tex.MATRIX("align*", lst2arr(m)); } / BEGIN_ALIGNED opt_pos m:matrix END_ALIGNED // parse what we emit { return ast.Tex.MATRIX("aligned", lst2arr(m)); } / BEGIN_ALIGNAT m:alignat END_ALIGNAT - { return ast.Tex.MATRIX("alignedat", lst2arr(m)); } + { return ast.Tex.MATRIX("alignat", lst2arr(m)); } / BEGIN_ALIGNEDAT m:alignat END_ALIGNEDAT // parse what we emit { return ast.Tex.MATRIX("alignedat", lst2arr(m)); } / BEGIN_SMALLMATRIX m:(array/matrix) END_SMALLMATRIX { return ast.Tex.MATRIX("smallmatrix", lst2arr(m)); } / BEGIN_CASES m:matrix END_CASES { return ast.Tex.MATRIX("cases", lst2arr(m)); } + / BEGIN_SPLIT m:matrix END_SPLIT + { return ast.Tex.MATRIX("split", lst2arr(m)); } / BEGIN_MULTLINE m:multline END_MULTLINE { return ast.Tex.MULTLINE("multline", lst2arr(m)); } / BEGIN_MULTLINE_STAR m:multline END_MULTLINE_STAR @@ -541,6 +549,10 @@ BEGIN_ALIGN = BEGIN "{align}" _ END_ALIGN = END "{align}" _ +BEGIN_ALIGN_STAR + = BEGIN "{align*}" _ +END_ALIGN_STAR + = END "{align*}" _ BEGIN_ALIGNED = BEGIN "{aligned}" _ END_ALIGNED @@ -561,6 +573,10 @@ BEGIN_CASES = BEGIN "{cases}" _ END_CASES = END "{cases}" _ +BEGIN_SPLIT + = BEGIN "{split}" _ +END_SPLIT + = END "{split}" _ BEGIN_MULTLINE = BEGIN "{multline}" _ END_MULTLINE diff --git a/lib/textmode.js b/lib/textmode.js index fff3a5b..b5db2af 100644 --- a/lib/textmode.js +++ b/lib/textmode.js @@ -6,6 +6,8 @@ var path = require('path'); var fs = require('fs'); var rebuilt = false; +var total; +var replaced; var mathStart = {'\\[' : '\\]', '\\(' : '\\)', @@ -78,16 +80,31 @@ MathMode.prototype.addTextSection = function(textSection) { } MathMode.prototype.makeReplacements = function(string, undef) { + total += 1; var replacements = []; var original = string; string = makeInsertions(string, this.textSections, replacements, undef); var delimStart = this.delim; var delimEnd = mathStart[delimStart]; - if (["\\begin{multline}", "\\begin{multline*}"].indexOf(delimStart) === -1) { + var manualReplacements = {"\\wt": "\\widetilde", + "\\sLP": "\\\\[\\sa]"}; + for (var key in manualReplacements) { + if (string.indexOf(key) !== -1) { + var num = string.split(key).length - 1; + string = string.replace(new RegExp(key.replace(/\\/g, "\\\\"), "g"), manualReplacements[key]); + undef.push([this.lineNumber, num + " occurrence(s) of " + key + " replaced.", null]); + } + } + if (["\\begin{multline}", "\\begin{multline*}", "\\begin{align}", "\\begin{align*}"].indexOf(delimStart) === -1) { string = string.substring(delimStart.length, string.length - delimEnd.length); + if (string.indexOf("\\\\") !== -1) { + var num = string.split("\\\\").length - 1; + string = string.replace(/\\\\/g, ""); + undef.push([this.lineNumber, num + " occurrence(s) of \\\\ replaced.", null]); + } var output = texer.texer(string).output + ""; if (output === "undefined") { - undef.push("[" + this.lineNumber + "]\n" + original); + undef.push([this.lineNumber, original]); string = original; } else { @@ -97,7 +114,7 @@ MathMode.prototype.makeReplacements = function(string, undef) { else { var string = texer.texer(string).output + ""; if (string === "undefined") { - undef.push("[" + this.lineNumber + "]\n" + original); + undef.push([this.lineNumber, original]); string = original; } } @@ -258,10 +275,30 @@ module.exports.replaceText = function(res, preserveBuild, logFile, undef) { if (!undef) { undef = []; } + total = 0; + replaced res = res.replace(/\r?\n/g, newline); var result = parseText(res).makeReplacements(res, undef).replace(new RegExp(newline, "g"), "\n"); if (logFile) { - fs.writeFileSync(path.join(__dirname, logFile), undef.join("\n\n").replace(new RegExp(newline, "g"), "\n")); + var lines = []; + undef = undef.filter(function(l) { + if (lines.length == 3 && lines.indexOf(l[0] + l[1]) === -1) { + lines.push(l[0] + l[1]); + return true; + } + if (lines.indexOf(l[0]) === -1) { + lines.push(l[0]); + return true; + } + return false; + }).sort(function (a, b) { return a[0] - b[0]; }); + var num = undef.filter(function(a) { return a.length == 2; }).length; + undef = undef.map(function(l) { return "[[[" + l[0] + "]]]\n" + l[1]; }); + var percent = Math.round((num / total) * 10000) / 100; + var log = total + " occurrences of math mode\n" + num + " encountered an error (" + percent + "%)\n"; + log += (total - num) + " parsed without error " + "(" + (100 - percent) + "%)\n\n"; + log += undef.join("\n\n").replace(new RegExp(newline, "g"), "\n"); + fs.writeFileSync(path.join(__dirname, logFile), log); } return result; } diff --git a/lib/texutil.js b/lib/texutil.js index 68ded17..876288d 100644 --- a/lib/texutil.js +++ b/lib/texutil.js @@ -113,6 +113,10 @@ function generateSpecCode(cmd, parameterCount, replacement, argumentCount, rende var ast = code[1]; var astutil = code[2]; var render = code[3]; + var sec = findBracketSections(replacement); + if (sec.length == 1 && sec[0][1] == "{" && sec[0][0].length == replacement.length - 2) { + replacement = sec[0][0]; + } var pegjscode = "\"" + replacement; var astcode = command + ": { args: [ 'string', "; var astutilcode1 = command + ": function(target, "; @@ -125,7 +129,9 @@ function generateSpecCode(cmd, parameterCount, replacement, argumentCount, rende if (pegjscode.indexOf("#" + (par + 1)) != -1) { temp = "l" + (par + 1); args.push(temp); - pegjscode = pegjscode.replaceAll("{#" + (par + 1) + "}", "\"" + space + temp + type + space + "\"").replaceAll("#" + (par + 1), "\"" + space + temp + type + space + "\""); +// pegjscode = pegjscode.replaceAll("(#" + (arg + 1) + ")", "#" + (i + 1)); + pegjscode = pegjscode.replaceAll("{#" + (par + 1) + "}", "#" + (par + 1)); + pegjscode = pegjscode.replaceAll("#" + (par + 1), "\"" + space + temp + type + space + "\""); } } var index = args.length; @@ -134,9 +140,12 @@ function generateSpecCode(cmd, parameterCount, replacement, argumentCount, rende for (var arg = 0; arg < argumentCount; arg++) { temp = "k" + (arg + 1); args.push(temp); - pegjscode = pegjscode.replaceAll("{#" + (arg + 1) + "}", "\"" + space + temp + type + space + "\"").replaceAll("#" + (arg + 1), "\"" + space + temp + type + space + "\""); +// pegjscode = pegjscode.replaceAll("(#" + (arg + 1) + ")", "#" + (i + 1)); + pegjscode = pegjscode.replaceAll("{#" + (arg + 1) + "}", "#" + (arg + 1)); + pegjscode = pegjscode.replaceAll("#" + (arg + 1), "\"" + space + temp + type + space + "\""); } } + pegjscode = pegjscode.replace(/\{(.)\}/g, "$1"); pegjscode = (pegjscode + "\"" + space).replaceAll(space + "\" ", space + "\"").replaceAll(" \"" + space, "\"" + space).replaceAll(space + "\"\"" + space, " "); pegjscode = pegjscode.replaceAll("\\\\left(", "\" PAREN_OPEN \"").replaceAll("\\\\right)", "\" PAREN_CLOSE \"").replaceAll(" \"\" ", " "); pegjscode = [pegjscode].concat("{ return ast.Tex." + command + ["(\"\\\\" + cmd + "\""].concat(args).join(", ") + "); }"); @@ -174,7 +183,8 @@ function generateGeneralCode(cmd, argumentCount, replacement, code) { astutilcode += ["match(target, f)"].concat(args.map(function(a) {return a + ".contains_func(target)"})).join(" || ") + ";\n\t},"; var rendercode = command + ": function(" + ["f"].concat(args).join(", ") + ") {\n\t\treturn \"" + replacement + "\";\n\t},"; for (var i = 0; i < argumentCount; i++) { - rendercode = rendercode.replaceAll("{#" + (i + 1) + "}", "\" + curlies(l" + (i + 1) + ", false) + \"").replaceAll(" + \"\"", ""); +// rendercode = rendercode.replaceAll("(#" + (i + 1) + ")", "#" + (i + 1)); + rendercode = rendercode.replaceAll("{#" + (i + 1) + "}", "#" + (i + 1)); rendercode = rendercode.replaceAll("#" + (i + 1), "\" + curlies(l" + (i + 1) + ", false) + \"").replaceAll(" + \"\"", ""); } pegjs.push(pegjscode); @@ -407,6 +417,7 @@ module.exports.mediawiki_function_names = arr2set([ module.exports.other_literals1 = arr2set([ "\\aleph", + "\\allowbreak", "\\alpha", "\\amalg", "\\And", @@ -658,7 +669,8 @@ module.exports.other_literals1 = arr2set([ "\\nleqslant", "\\nless", "\\nmid", - "\\nolimits", // XXX see \limits + "\\nolimits", // XXX see \limits, + "\\nonumber", "\\not", "\\notin", "\\nparallel", @@ -1133,10 +1145,13 @@ module.exports.ams_required = arr2set([ "\\begin{Bmatrix}", "\\begin{vmatrix}", "\\begin{Vmatrix}", + "\\begin{align}", + "\\begin{align*}", "\\begin{aligned}", "\\begin{alignedat}", "\\begin{smallmatrix}", "\\begin{cases}", + "\\begin{split}", "\\begin{multline}", "\\begin{multline*}", diff --git a/package.json b/package.json index 43ffaef..43e7af3 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "istanbul": "^0.4.2", "jshint": "^2.9.1", "mocha": "^2.4.5", - "mocha-lcov-reporter": "^1.2.0" + "mocha-lcov-reporter": "^1.2.0", + "pegjs": "~0.9.0" }, "dependencies": { "commander": "^2.9.0" From 1824d241f522105045338e92137dc4b48b4aafe9 Mon Sep 17 00:00:00 2001 From: Jagan Prem Date: Fri, 14 Oct 2016 16:43:36 -0400 Subject: [PATCH 14/14] fix gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index c8ddb99..39c0df1 100644 --- a/.gitignore +++ b/.gitignore @@ -94,4 +94,8 @@ lib/render.js lib/out.tex lib/copy.tex +#Macro definitions +lib/DRMFfcns.sty +lib/DLMFfcns.sty + texer.iml \ No newline at end of file