UNPKG

ti-el

Version:
216 lines (186 loc) 5.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.tldoc = void 0; var _tl = require("./tl"); const findCommentOnLine = line => { const commentRegexp = /^[ \t]*\/\/(.*)/; const [, comment] = line.match(commentRegexp) || []; return comment; }; const findComments = str => str.split(/\r?\n/).map(findCommentOnLine).map((e, i) => e ? { line: i + 1, string: e.trim() } : null).filter(Boolean); const findDocComments = str => findComments(str).filter(({ string }) => ['@', '-'].includes(string[0])).map(obj => ({ ...obj, string: obj.string.replace(/^-/, '') })); const last = arr => arr[arr.length - 1]; const push = (el, arr) => { arr.push(el); return arr; }; const updateLast = (el, arr) => { arr[arr.length - 1] = el; return arr; }; const groupDocComments = comments => comments.reduce((acc, el, i) => { if (i === 0) return push(el, acc); const { line, string } = el; const { line: prevLine, string: prevString } = last(acc); return prevLine === line - 1 ? updateLast({ line, string: prevString + ' ' + string }, acc) : push(el, acc); }, []); // mutates regexp const regAll = (reg, str) => { const output = []; let result; while (result = reg.exec(str)) output.push(result); return output; }; const parseDocComment = string => { const regexp = /@(.+?) ([^@]+)/g; return new Map(regAll(regexp, string).map(result => { const [, key = "", value = ""] = result; return [key, value.trim()]; })); }; // --- const removeHash = str => str.replace(/#.*/, ''); const combinatorsFromAST = ast => { const constrDecls = ast.constructors.declarations; const funcDecls = ast.functions.declarations; const constCombs = constrDecls.filter(e => e.type === 'CombinatorDeclaration' // Also remove vector constructors && e.id.name !== 'vector'); const funcCombs = funcDecls.filter(e => e.type === 'CombinatorDeclaration'); const combinators = constCombs.map(c => normalizeCombinator('constructor', c)).concat(funcCombs.map(c => normalizeCombinator('function', c))); return combinators; }; const normalizeExpression = expr => { if (typeof expr === 'number') throw new Error('Invalid expression'); if (expr.type === 'ETypeIdentifier') return { type: expr.id.name, vector: 0 }; if (expr.type === 'EExpression') { if (expr.subexpressions.length !== 2) throw new Error('Unsupported expression length'); const typeConstr = expr.subexpressions[0]; if (typeConstr.type === 'ETypeIdentifier' && typeConstr.id.name === 'vector') { const subterm = expr.subexpressions[1]; const nSubterm = normalizeExpression(subterm); return { ...nSubterm, vector: nSubterm.vector + 1 }; } throw new Error('Unsupported type constructor'); } throw new Error('Unsupported expression'); }; const normalizeArgument = arg => { const { id } = arg; if (!id) throw new Error('No arg.id'); const { name } = id; const type = arg.argType.expression; return { name, ...normalizeExpression(type) }; }; const normalizeCombinator = (kind, comb) => { const line = comb.start.line; const name = removeHash(comb.id.name); if (!comb.args) throw new Error('No combinator.args'); const args = comb.args.map(normalizeArgument); const result = comb.resultType.id.name; return { line, kind, name, args, result }; }; // --- const tldoc = input => { const comments = groupDocComments(findDocComments(input)); const docMaps = comments.map(({ line, string }) => ({ line, map: parseDocComment(string) })); const baseClasses = docMaps.map(e => e.map).filter(m => !!m.get('class')).map(m => { const className = m.get('class'); const description = m.get('description'); if (!description) throw new Error('No BaseClass description'); if (!className) throw new Error('No className'); return { name: className, description }; }); const ast = (0, _tl.parse)(input); const combinators = combinatorsFromAST(ast); const classes = docMaps.filter(({ map }) => !map.get('class')).map(({ line, map }) => { const combinator = combinators.find(c => c.line === line + 1); if (!combinator) throw new Error(`Combinator on line ${line + 1} not found`); const { name, kind, result } = combinator; let description; const parameters = []; for (const [key, value] of map.entries()) { if (key === 'description') { description = value; continue; } const name = key.replace(/^param_/, ''); const combArg = combinator.args.find(arg => arg.name === name); if (!combArg) throw new Error(`Argument ${name} not found on line ${line + 1}`); const { type, vector } = combArg; parameters.push({ name, type, vector, description: value }); } if (!description) throw new Error(`No description of ${name}`); return { line, name, kind, description, parameters, result }; }); return { baseClasses, classes }; }; exports.tldoc = tldoc;