Skip to content

Commit ad97c9f

Browse files
committed
parser: ti manual: deal with sub-alternate syntax lines
1 parent 517d5f1 commit ad97c9f

File tree

4 files changed

+234
-133
lines changed

4 files changed

+234
-133
lines changed

TI-84_Plus_CE_catalog-tokens.json

+50
Original file line numberDiff line numberDiff line change
@@ -4777,6 +4777,28 @@
47774777
"2:Input"
47784778
]
47794779
},
4780+
{
4781+
"syntax": "Input [\"text\",variable]",
4782+
"arguments": [
4783+
[
4784+
"text",
4785+
"string",
4786+
true
4787+
],
4788+
[
4789+
"variable",
4790+
"",
4791+
true
4792+
]
4793+
],
4794+
"description": "Prompts for value to store to `variable`.",
4795+
"inEditorOnly": true,
4796+
"location": [
4797+
"【prgm】",
4798+
"I/O",
4799+
"2:Input"
4800+
]
4801+
},
47804802
{
47814803
"syntax": "Input [Strn,variable]",
47824804
"arguments": [
@@ -4976,6 +4998,34 @@
49764998
"DISTR",
49774999
"3:invNorm("
49785000
]
5001+
},
5002+
{
5003+
"syntax": "tail [catalog]: LEFT, CENTER, RIGHT",
5004+
"arguments": [
5005+
[
5006+
"tail catalog: LEFT",
5007+
"",
5008+
false
5009+
],
5010+
[
5011+
"CENTER",
5012+
"",
5013+
false
5014+
],
5015+
[
5016+
"RIGHT",
5017+
"",
5018+
false
5019+
]
5020+
],
5021+
"description": "Computes the inverse cumulative normal distribution function for a given area under the normal distribution curve specified by μ and s.. The optional argument tail can be LEFT (-∞,-a), CENTER [-a,a] or RIGHT (a, ∞) for Real a.\nThe tokens LEFT, CENTER and RIGHT can be found in [catalog].",
5022+
"inEditorOnly": false,
5023+
"location": [
5024+
"【2nd】",
5025+
"[distr]",
5026+
"DISTR",
5027+
"3:invNorm("
5028+
]
49795029
}
49805030
],
49815031
"localizations": {

parser.js

+135-133
Original file line numberDiff line numberDiff line change
@@ -345,162 +345,164 @@ for(let i = 0; i < 26; i++)
345345
description = descRaw.map(el => el.textContent.trim()).join("\n").replace('``', '');
346346
}
347347

348-
const syntaxLine = token.querySelector('tbody p.SyntaxLine');
349-
350-
let wholeSyntaxLine = (syntaxLine?.textContent ?? '').replace(',[,','[,').replace(/ +/g,' ').replace(/ +/g,' ').trim();
351-
if (comment) {
352-
if (/^\(.*\)$/.test(comment)) {
353-
wholeSyntaxLine = wholeSyntaxLine.replace(new RegExp(escapeRegExp(comment) + '$'), ''); // remove potential comment at the end
354-
comment = comment.substring(1, comment.length-1);
355-
} else if (wholeSyntaxLine.endsWith(comment)) {
356-
comment = undefined;
348+
const syntaxLines = token.querySelectorAll('tbody p.SyntaxLine');
349+
for (const syntaxLine of syntaxLines)
350+
{
351+
let wholeSyntaxLine = (syntaxLine?.textContent ?? '').replace(',[,','[,').replace(/ +/g,' ').replace(/ +/g,' ').trim();
352+
if (comment) {
353+
if (/^\(.*\)$/.test(comment)) {
354+
wholeSyntaxLine = wholeSyntaxLine.replace(new RegExp(escapeRegExp(comment) + '$'), ''); // remove potential comment at the end
355+
comment = comment.substring(1, comment.length-1);
356+
} else if (wholeSyntaxLine.endsWith(comment)) {
357+
comment = undefined;
358+
}
357359
}
358-
}
359360

360-
let optionalSinceIdx = (wholeSyntaxLine.startsWith(`${name}[`) || wholeSyntaxLine.startsWith(`${name.trim()}[`)) ? 0 : null;
361-
let _argIdx = 0;
362-
let args = [...syntaxLine.childNodes]
363-
.filter(el => el.nodeType !== 8 /*COMMENT_NODE*/ && el.textContent !== '→' && el.textContent.trim().length &&
364-
(!el.classList?.contains('Function') || el.textContent === ',' || el.getAttribute("style")?.includes('font-weight: normal')))
365-
.filter((el, idx) => {
366-
const str = el.textContent.trim();
367-
if (str === '►') { return false; }
368-
const strOrFirstPart = str.split(' ')[0];
369-
return (idx > 0) || (strOrFirstPart !== name.trim() && (strOrFirstPart + '(') !== name.trim());
370-
})
371-
.map((el,idx,arr) => {
372-
const argStr = el.textContent.trim().replace(',[,','[,');
373-
if (optionalSinceIdx === null && /^\[,?$/.test(argStr)) {
374-
optionalSinceIdx = _argIdx
375-
if (optionalSinceIdx === 0 && name.endsWith('(')) {
376-
optionalSinceIdx = 1;
361+
let optionalSinceIdx = (wholeSyntaxLine.startsWith(`${name}[`) || wholeSyntaxLine.startsWith(`${name.trim()}[`)) ? 0 : null;
362+
let _argIdx = 0;
363+
let args = [...syntaxLine.childNodes]
364+
.filter(el => el.nodeType !== 8 /*COMMENT_NODE*/ && el.textContent !== '→' && el.textContent.trim().length &&
365+
(!el.classList?.contains('Function') || el.textContent === ',' || el.getAttribute("style")?.includes('font-weight: normal')))
366+
.filter((el, idx) => {
367+
const str = el.textContent.trim();
368+
if (str === '►') { return false; }
369+
const strOrFirstPart = str.split(' ')[0];
370+
return (idx > 0) || (strOrFirstPart !== name.trim() && (strOrFirstPart + '(') !== name.trim());
371+
})
372+
.map((el,idx,arr) => {
373+
const argStr = el.textContent.trim().replace(',[,','[,');
374+
if (optionalSinceIdx === null && /^\[,?$/.test(argStr)) {
375+
optionalSinceIdx = _argIdx
376+
if (optionalSinceIdx === 0 && name.endsWith('(')) {
377+
optionalSinceIdx = 1;
378+
}
379+
// console.log(` optional args start now => ${optionalSinceIdx} ; ${wholeSyntaxLine}`)
377380
}
378-
// console.log(` optional args start now => ${optionalSinceIdx} ; ${wholeSyntaxLine}`)
379-
}
380-
if (el.textContent === ',' || el.classList?.contains('Variable')) {
381-
_argIdx += 1 + (argStr.replace(/(^,|,$)/g, '').match(/,/g) || []).length
381+
if (el.textContent === ',' || el.classList?.contains('Variable')) {
382+
_argIdx += 1 + (argStr.replace(/(^,|,$)/g, '').match(/,/g) || []).length
383+
}
384+
return argStr;
385+
})
386+
.filter(el => el && el.textContent !== ',')
387+
.map((el) => [ el.replace(/(^,*|[\[\]\(\)]|,*$)/g, ''), '', false ])
388+
.map((el,idx,arr) => [ el[0], '', optionalSinceIdx !== null && idx >= optionalSinceIdx ])
389+
.filter(n => n && n[0].length && !/^[\[\]\(\)]$/.test(n[0]));
390+
391+
{
392+
let newArgs = [];
393+
for (const [idx, [argName, argType, isOptional]] of Object.entries(args)) {
394+
if (argName.includes(',')) {
395+
// console.warn(` arg ${idx} wasn't split correctly: "${argName}". args was: ${JSON.stringify(args)}`);
396+
for (const [idx2, arg] of Object.entries(argName.split(','))) {
397+
newArgs.push([arg.replace(/^\(/, '').trim(), argType, isOptional])
382398
}
383-
return argStr;
384-
})
385-
.filter(el => el && el.textContent !== ',')
386-
.map((el) => [ el.replace(/(^,*|[\[\]\(\)]|,*$)/g, ''), '', false ])
387-
.map((el,idx,arr) => [ el[0], '', optionalSinceIdx !== null && idx >= optionalSinceIdx ])
388-
.filter(n => n && n[0].length && !/^[\[\]\(\)]$/.test(n[0]));
389-
390-
{
391-
let newArgs = [];
392-
for (const [idx, [argName, argType, isOptional]] of Object.entries(args)) {
393-
if (argName.includes(',')) {
394-
// console.warn(` arg ${idx} wasn't split correctly: "${argName}". args was: ${JSON.stringify(args)}`);
395-
for (const [idx2, arg] of Object.entries(argName.split(','))) {
396-
newArgs.push([arg.replace(/^\(/, '').trim(), argType, isOptional])
399+
} else {
400+
newArgs.push([argName, argType, isOptional]);
397401
}
398-
} else {
399-
newArgs.push([argName, argType, isOptional]);
400402
}
403+
args = newArgs;
401404
}
402-
args = newArgs;
403-
}
404405

405-
// args misc. cleanup and type determination...
406-
args = args.filter(el => el && el[0].length);
407-
for (const [idx, [argName]] of Object.entries(args)) {
408-
if (argName === "Plot#type") {
409-
wholeSyntaxLine = wholeSyntaxLine.replace(new RegExp('^' + escapeRegExp(name)), '').trim();
410-
args[idx] = [ 'type', `${name} token`, false ];
411-
}
412-
if (/^listname\d/i.test(argName)) {
413-
args[idx][1] = 'listName'
414-
} else if (argName.includes('list')) {
415-
args[idx][1] = 'list'
416-
} else if (argName.includes('matrix')) {
417-
args[idx][1] = 'matrix'
418-
} else if (argName.endsWith('string') || argName.startsWith('text')) {
419-
args[idx][1] = 'string'
420-
} else if (argName === 'color#') {
421-
args[idx][1] = 'colorNum'
422-
} else if (/^(length|rows|columns|linestyle#)$/.test(argName)) {
423-
args[idx][1] = 'integer'
424-
} else if (argName === 'expression') {
425-
args[idx][1] = 'expression'
426-
} else if (argName === 'complex value') {
427-
args[idx][1] = 'complex'
428-
}
406+
// args misc. cleanup and type determination...
407+
args = args.filter(el => el && el[0].length);
408+
for (const [idx, [argName]] of Object.entries(args)) {
409+
if (argName === "Plot#type") {
410+
wholeSyntaxLine = wholeSyntaxLine.replace(new RegExp('^' + escapeRegExp(name)), '').trim();
411+
args[idx] = [ 'type', `${name} token`, false ];
412+
}
413+
if (/^listname\d/i.test(argName)) {
414+
args[idx][1] = 'listName'
415+
} else if (argName.includes('list')) {
416+
args[idx][1] = 'list'
417+
} else if (argName.includes('matrix')) {
418+
args[idx][1] = 'matrix'
419+
} else if (argName.endsWith('string') || argName.startsWith('text')) {
420+
args[idx][1] = 'string'
421+
} else if (argName === 'color#') {
422+
args[idx][1] = 'colorNum'
423+
} else if (/^(length|rows|columns|linestyle#)$/.test(argName)) {
424+
args[idx][1] = 'integer'
425+
} else if (argName === 'expression') {
426+
args[idx][1] = 'expression'
427+
} else if (argName === 'complex value') {
428+
args[idx][1] = 'complex'
429+
}
429430

430-
// some overrides...
431-
if (description.includes('`valueA` and `valueB`, which can be real numbers or lists') // gcd/lcm, expressions work fine
432-
|| description.includes('`valueA` and `valueB` can be real numbers, expressions, or lists')) {
433-
args[idx][1] = 'real|expression|real[]'
434-
} else if (description.includes('of a real number, expression, or list')) {
435-
args[idx][1] = 'real|expression|real[]'
436-
} else if (description.includes('of a real number, expression, list, or matrix')) {
437-
args[idx][1] = 'real|expression|real[]|matrix'
438-
} else if (description.includes('of a complex number or list')) {
439-
args[idx][1] = 'complex|complex[]'
431+
// some overrides...
432+
if (description.includes('`valueA` and `valueB`, which can be real numbers or lists') // gcd/lcm, expressions work fine
433+
|| description.includes('`valueA` and `valueB` can be real numbers, expressions, or lists')) {
434+
args[idx][1] = 'real|expression|real[]'
435+
} else if (description.includes('of a real number, expression, or list')) {
436+
args[idx][1] = 'real|expression|real[]'
437+
} else if (description.includes('of a real number, expression, list, or matrix')) {
438+
args[idx][1] = 'real|expression|real[]|matrix'
439+
} else if (description.includes('of a complex number or list')) {
440+
args[idx][1] = 'complex|complex[]'
441+
}
442+
//console.log(idx, argName, arguments[idx][1])
440443
}
441-
//console.log(idx, argName, arguments[idx][1])
442-
}
443444

444-
// remove last arg if it's just a comment that got there somehow (not an actual arg)
445-
if (comment && args.length && new RegExp('^\\(?' + comment + '$').test(args[args.length-1][0])) {
446-
args.pop();
447-
}
445+
// remove last arg if it's just a comment that got there somehow (not an actual arg)
446+
if (comment && args.length && new RegExp('^\\(?' + comment + '$').test(args[args.length-1][0])) {
447+
args.pop();
448+
}
448449

449-
const rawLocation = token.querySelector('tbody p.MenuName > span')?.parentElement;
450-
const location = [ ...(rawLocation?.children ?? []) ]
451-
.map(el => el.textContent.trim())
452-
.filter(el => el)
453-
.map(el => menuReplacements[el] ?? el);
454-
if (location.length >= 2) {
455-
if (name.includes('►')) {
456-
const [ part1, part2 ] = name.split('►');
457-
if (location[location.length-1] === part2 && (new RegExp('^([A-Z\d]:)?' + escapeRegExp(part1) + '$').test(location[location.length-2]))) {
458-
location[location.length-2] += '►' + location.pop();
450+
const rawLocation = token.querySelector('tbody p.MenuName > span')?.parentElement;
451+
const location = [ ...(rawLocation?.children ?? []) ]
452+
.map(el => el.textContent.trim())
453+
.filter(el => el)
454+
.map(el => menuReplacements[el] ?? el);
455+
if (location.length >= 2) {
456+
if (name.includes('►')) {
457+
const [ part1, part2 ] = name.split('►');
458+
if (location[location.length-1] === part2 && (new RegExp('^([A-Z\d]:)?' + escapeRegExp(part1) + '$').test(location[location.length-2]))) {
459+
location[location.length-2] += '►' + location.pop();
460+
}
461+
}
462+
if (/^[A-Z\d]:$/.test(location[location.length-2])) {
463+
location[location.length-2] += location.pop();
459464
}
460465
}
461-
if (/^[A-Z\d]:$/.test(location[location.length-2])) {
462-
location[location.length-2] += location.pop();
463-
}
464-
}
465466

466-
let specificName = undefined;
467-
if (!bytes) {
468-
bytes = {
469-
'If Then End': name2bytes['If '],
470-
'If Then Else End': name2bytes['If '],
471-
}[name];
472-
specificName = name;
467+
let specificName = undefined;
473468
if (!bytes) {
474-
throw `bytes not defined for token name: [${name}]`;
469+
bytes = {
470+
'If Then End': name2bytes['If '],
471+
'If Then Else End': name2bytes['If '],
472+
}[name];
473+
specificName = name;
474+
if (!bytes) {
475+
throw `bytes not defined for token name: [${name}]`;
476+
}
475477
}
476-
}
477478

478-
if (tkXML[bytes]) {
479-
const lastTkData = tkXML[bytes][tkXML[bytes].length - 1];
480-
if (lastTkData.enAccessible && tokenEntry.name !== lastTkData.enAccessible) {
481-
tokenEntry.accessibleName = lastTkData.enAccessible;
479+
if (tkXML[bytes]) {
480+
const lastTkData = tkXML[bytes][tkXML[bytes].length - 1];
481+
if (lastTkData.enAccessible && tokenEntry.name !== lastTkData.enAccessible) {
482+
tokenEntry.accessibleName = lastTkData.enAccessible;
483+
}
484+
if (lastTkData.enVariants) {
485+
tokenEntry.nameVariants = lastTkData.enVariants;
486+
}
487+
mergeSinceUntilFromTkXML(tokenEntry, tkXML[bytes], name);
482488
}
483-
if (lastTkData.enVariants) {
484-
tokenEntry.nameVariants = lastTkData.enVariants;
489+
490+
if (comment && comment.startsWith('Alias')) {
491+
tokenEntry.isAlias = true;
485492
}
486-
mergeSinceUntilFromTkXML(tokenEntry, tkXML[bytes], name);
487-
}
488493

489-
if (comment && comment.startsWith('Alias')) {
490-
tokenEntry.isAlias = true;
494+
(json[bytes] ??= tokenEntry).syntaxes.push({
495+
specificName: specificName,
496+
syntax: wholeSyntaxLine,
497+
comment: comment,
498+
arguments: args,
499+
description: description,
500+
inEditorOnly: (token.querySelector('tbody p.MenuName')?.textContent ?? '').includes('†'),
501+
location: location.length && location[0].length ? location : [`[${name.replace(/\($/,'')}]`],
502+
specialCategory: specialCategory.length ? specialCategory : undefined,
503+
});
491504
}
492505

493-
(json[bytes] ??= tokenEntry).syntaxes.push({
494-
specificName: specificName,
495-
syntax: wholeSyntaxLine,
496-
comment: comment,
497-
arguments: args,
498-
description: description,
499-
inEditorOnly: (token.querySelector('tbody p.MenuName')?.textContent ?? '').includes('†'),
500-
location: location.length && location[0].length ? location : [`[${name.replace(/\($/,'')}]`],
501-
specialCategory: specialCategory.length ? specialCategory : undefined,
502-
});
503-
504506
if (manualOverrides[bytes]) {
505507
for (const [ what, override ] of Object.entries(manualOverrides[bytes])) {
506508
if (typeof(json[bytes][what]) === 'undefined') {

tokens/0xBB11.md

+26
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,32 @@ The tokens LEFT, CENTER and RIGHT can be found in [catalog].
3434
<tt><kbd><b>2nd</b></kbd></tt>, <kbd>distr</kbd>, `DISTR`, `3:invNorm(`
3535
<hr>
3636

37+
## Overview
38+
Computes the inverse cumulative normal distribution function for a given area under the normal distribution curve specified by μ and s.. The optional argument tail can be LEFT (-∞,-a), CENTER [-a,a] or RIGHT (a, ∞) for Real a.
39+
The tokens LEFT, CENTER and RIGHT can be found in [catalog].
40+
41+
42+
<b>Availability</b>: Token available everywhere.
43+
44+
## Syntax
45+
`tail [catalog]: LEFT, CENTER, RIGHT`
46+
47+
## Arguments
48+
<table>
49+
<tr><th>Name</th><th>Type</th><th>Optional</th></tr>
50+
51+
<tr><td><b>tail catalog: LEFT</b></td><td></td><td></td></tr>
52+
53+
<tr><td><b>CENTER</b></td><td></td><td></td></tr>
54+
55+
<tr><td><b>RIGHT</b></td><td></td><td></td></tr>
56+
57+
</table>
58+
59+
## Location
60+
<tt><kbd><b>2nd</b></kbd></tt>, <kbd>distr</kbd>, `DISTR`, `3:invNorm(`
61+
<hr>
62+
3763
## Description
3864

3965
<tt>invNorm(</tt> is the inverse of the cumulative normal distribution function: given a probability, it will give you a z-score with that tail probability. The probability argument of <tt>invNorm(</tt> is between 0 and 1; 0 will give <tt>-1<span style="font-size:75%;">E</span>99</tt> instead of negative infinity, and 1 will give <tt>1<span style="font-size:75%;">E</span>99</tt> instead of positive infinity

0 commit comments

Comments
 (0)