summaryrefslogtreecommitdiffstats
path: root/src/Lang/C
diff options
context:
space:
mode:
authorRowan Goemans <RB.Goemans@student.han.nl>2017-12-31 04:07:55 +0100
committerChristoph Schlosser <christophschlosser@users.noreply.github.com>2018-02-20 22:02:22 +0100
commit31f6517aa5d8d3ccdd0b39880337c000943d08c1 (patch)
tree2c3c5240470a592f4c0bd134d779f4f3c25d6b2b /src/Lang/C
parent10aac7196a3a701860525d0f466896efe182d39a (diff)
downloaddoxdocgen-31f6517aa5d8d3ccdd0b39880337c000943d08c1.tar.gz
-- Completed unit tests for operators. Conversion operators aren't working yet.
-- Renamed all C things to Cpp since that is really what they are. -- Made empty files for the remaining unit tests.
Diffstat (limited to 'src/Lang/C')
-rw-r--r--src/Lang/C/CArgument.ts6
-rw-r--r--src/Lang/C/CDocGen.ts178
-rw-r--r--src/Lang/C/CParseTree.ts121
-rw-r--r--src/Lang/C/CParser.ts608
-rw-r--r--src/Lang/C/CToken.ts26
5 files changed, 0 insertions, 939 deletions
diff --git a/src/Lang/C/CArgument.ts b/src/Lang/C/CArgument.ts
deleted file mode 100644
index 46f180c..0000000
--- a/src/Lang/C/CArgument.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { CParseTree } from "./CParseTree";
-
-export class CArgument {
- public name: string = null;
- public type: CParseTree = new CParseTree();
-}
diff --git a/src/Lang/C/CDocGen.ts b/src/Lang/C/CDocGen.ts
deleted file mode 100644
index 5488020..0000000
--- a/src/Lang/C/CDocGen.ts
+++ /dev/null
@@ -1,178 +0,0 @@
-import { Position, Range, Selection, TextEditor, TextLine, WorkspaceEdit } from "vscode";
-import { IDocGen } from "../../Common/IDocGen";
-import { Config } from "../../Config";
-import { CArgument } from "./CArgument";
-import { CParseTree } from "./CParseTree";
-import { CToken, CTokenType } from "./CToken";
-
-export default class CDocGen implements IDocGen {
- protected activeEditor: TextEditor;
-
- protected readonly cfg: Config;
-
- protected func: CArgument;
- protected templateParams: string[];
- protected params: CArgument[];
-
- /**
- * @param {TextEditor} actEdit Active editor window
- * @param {Position} cursorPosition Where the cursor of the user currently is
- * @param {string[]} templateParams The template parameters of the declaration.
- * @param {CArgument} func The type and name of the function to generate doxygen.
- * Doesn't contain anything if it is not a function.
- * @param {CArgument[]} params The parameters of the function. Doesn't contain anything if it is not a function.
- */
- public constructor(
- actEdit: TextEditor,
- cursorPosition: Position,
- cfg: Config,
- templateParams: string[],
- func: CArgument,
- params: CArgument[],
- ) {
- this.activeEditor = actEdit;
- this.cfg = cfg;
- this.func = func;
- this.templateParams = templateParams;
- this.params = params;
- }
-
- /**
- * @inheritdoc
- */
- public GenerateDoc(rangeToReplace: Range) {
- const comment: string = this.generateComment();
-
- this.activeEditor.edit((editBuilder) => {
- editBuilder.replace(rangeToReplace, comment); // Insert the comment
- });
-
- // Set cursor to first DoxyGen command.
- this.moveCursurToFirstDoxyCommand(comment, rangeToReplace.start.line, rangeToReplace.start.character);
- }
-
- /***************************************************************************
- Implementation
- ***************************************************************************/
- protected getIndentation(): string {
- const line: TextLine = this.activeEditor.document.lineAt(this.activeEditor.selection.start.line);
- return line.text.slice(0, line.firstNonWhitespaceCharacterIndex - 1);
- }
-
- protected getTemplatedString(replace: string, template: string, param: string): string {
- return template.replace(replace, param);
- }
-
- protected generateBrief(lines: string[]) {
- lines.push(this.cfg.commentPrefix + this.cfg.briefTemplate);
- }
-
- protected generateFromTemplate(lines: string[], replace: string, template: string, templateWith: string[]) {
- let line: string = "";
-
- templateWith.forEach((element: string) => {
- // Ignore null values
- if (element !== null) {
- line = this.cfg.commentPrefix;
- line += this.getTemplatedString(replace, template, element);
- lines.push(line);
- }
- });
- }
-
- protected generateReturnParams(): string[] {
- const params: string[] = [];
-
- // Check if return type is a pointer
- const ptrReturnIndex = this.func.type.nodes
- .findIndex((n) => n instanceof CToken && n.type === CTokenType.Pointer);
-
- // Special case for void functions.
- const voidReturnIndex = this.func.type.nodes
- .findIndex((n) => n instanceof CToken && n.type === CTokenType.Symbol && n.value === "void");
-
- // Special case for bool return type.
- const boolReturnIndex: number = this.func.type.nodes
- .findIndex((n) => n instanceof CToken && n.type === CTokenType.Symbol && n.value === "bool");
-
- if (boolReturnIndex !== -1 && this.cfg.boolReturnsTrueFalse === true) {
- params.push("true");
- params.push("false");
- } else if (voidReturnIndex !== -1 && ptrReturnIndex !== -1) {
- params.push(this.cfg.includeTypeAtReturn === true ? this.func.type.Yield() : "");
- } else if (voidReturnIndex === -1 && this.func.type.nodes.length > 0) {
- params.push(this.cfg.includeTypeAtReturn === true ? this.func.type.Yield() : "");
- }
-
- return params;
- }
-
- protected generateComment(): string {
- const lines: string[] = [];
-
- if (this.cfg.firstLine.trim().length !== 0) {
- lines.push(this.cfg.firstLine);
- }
-
- if (this.cfg.briefTemplate.trim().length !== 0) {
- this.generateBrief(lines);
- if (this.cfg.newLineAfterBrief === true) {
- lines.push(this.cfg.commentPrefix);
- }
- }
-
- if (this.cfg.tparamTemplate.trim().length !== 0 && this.templateParams.length > 0) {
- this.generateFromTemplate(
- lines,
- this.cfg.paramTemplateReplace,
- this.cfg.tparamTemplate,
- this.templateParams,
- );
- if (this.cfg.newLineAfterTParams === true) {
- lines.push(this.cfg.commentPrefix);
- }
- }
-
- if (this.cfg.paramTemplate.trim().length !== 0 && this.params.length > 0) {
- const paramNames: string[] = this.params.map((p) => p.name);
- this.generateFromTemplate(lines, this.cfg.paramTemplateReplace, this.cfg.paramTemplate, paramNames);
- if (this.cfg.newLineAfterParams === true) {
- lines.push(this.cfg.commentPrefix);
- }
- }
-
- if (this.cfg.returnTemplate.trim().length !== 0 && this.func.type !== null) {
- const returnParams = this.generateReturnParams();
- this.generateFromTemplate(lines, this.cfg.typeTemplateReplace, this.cfg.returnTemplate, returnParams);
- }
-
- if (this.cfg.lastLine.trim().length !== 0) {
- lines.push(this.cfg.lastLine);
- }
-
- const comment: string = lines.join("\n" + this.getIndentation());
- return comment;
- }
-
- protected moveCursurToFirstDoxyCommand(comment: string, baseLine: number, baseCharacter) {
- // Find first offset of a new line in the comment. Since that's when the line where the first param starts.
- let line: number = baseLine;
- let character: number = comment.indexOf("\n");
-
- // If a first line is included find the 2nd line with a newline.
- if (this.cfg.firstLine.trim().length !== 0) {
- line++;
- const oldCharacter: number = character;
- character = comment.indexOf("\n", oldCharacter + 1) - oldCharacter;
- }
-
- // If newline is not found means no first param was found so Set to base line before the newline.
- if (character < 0) {
- line = baseLine;
- character = baseCharacter;
- }
-
- const moveTo: Position = new Position(line, character);
- this.activeEditor.selection = new Selection(moveTo, moveTo);
- }
-}
diff --git a/src/Lang/C/CParseTree.ts b/src/Lang/C/CParseTree.ts
deleted file mode 100644
index f3ec870..0000000
--- a/src/Lang/C/CParseTree.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import { CToken, CTokenType } from "./CToken";
-
-export class CParseTree {
-
- /**
- * Create a tree from CTokens. This consumes the CTokens.
- * @param CTokens The CTokens to create a tree for.
- * @param inNested If currently allready nesting.
- */
- public static CreateTree(CTokens: CToken[], inNested: boolean = false): CParseTree {
- const tree: CParseTree = new CParseTree();
-
- while (CTokens.length > 0) {
- const token: CToken = CTokens.shift();
- switch (token.type) {
- case CTokenType.OpenParenthesis:
- tree.nodes.push(this.CreateTree(CTokens, true));
- break;
- case CTokenType.CloseParenthesis:
- if (inNested === false) {
- throw new Error("Unmatched closing parenthesis.");
- }
- return tree;
- default:
- tree.nodes.push(token);
- break;
- }
- }
-
- if (inNested === true) {
- throw new Error("No match found for an opening parenthesis.");
- }
-
- return tree;
- }
-
- public nodes: Array<CToken | CParseTree> = [];
-
- /**
- * Compact empty branches. Example ((foo))(((bar))) will become (foo)(bar)
- * @param tree The CParseTree to compact. Defaults to the current tree.
- */
- public Compact(tree: CParseTree = this): CParseTree {
- const newTree: CParseTree = new CParseTree();
- newTree.nodes = tree.nodes.map((n) => n);
- const isNotCompact = (n) => n instanceof CParseTree && n.nodes.length === 1 && n.nodes[0] instanceof CParseTree;
-
- // Compact current level of nodes to the maximum amount.
- while (newTree.nodes.some((n) => isNotCompact(n))) {
- newTree.nodes = newTree.nodes
- .map((n) => n instanceof CParseTree && isNotCompact(n) ? n.nodes[0] : n);
- }
-
- // Compact all nested CParseTrees.
- newTree.nodes = newTree.nodes
- .map((n) => n instanceof CParseTree ? this.Compact(n) : n);
-
- return newTree;
- }
-
- /**
- * Copy CParseTree.
- * @param tree The CParseTree to compact. Defaults to the current tree.
- */
- public Copy(tree: CParseTree = this): CParseTree {
- const newTree: CParseTree = new CParseTree();
- newTree.nodes = tree.nodes
- .map((n) => n instanceof CToken ? n : this.Copy(n));
- return newTree;
- }
-
- /**
- * Create string from the CParseTree which is a representation of the original code.
- * @param tree The CParseTree to compact. Defaults to the current tree.
- */
- public Yield(tree: CParseTree = this): string {
- let code: string = "";
-
- for (const node of tree.nodes) {
- if (node instanceof CParseTree) {
- code += "(" + this.Yield(node) + ")";
- continue;
- }
-
- switch (node.type) {
- case CTokenType.Symbol:
- code += code === "" ? node.value : " " + node.value;
- break;
- case CTokenType.Pointer:
- code += node.value;
- break;
- case CTokenType.Reference:
- code += node.value;
- break;
- case CTokenType.ArraySubscript:
- code += node.value;
- break;
- case CTokenType.CurlyBlock:
- code += node.value;
- break;
- case CTokenType.Assignment:
- code += " " + node.value;
- break;
- case CTokenType.Comma:
- code += node.value;
- break;
- case CTokenType.Arrow:
- code += " " + node.value;
- break;
- case CTokenType.Ellipsis:
- code += node.value;
- break;
- case CTokenType.Attribute:
- code += code === "" ? node.value : " " + node.value;
- break;
- }
- }
-
- return code;
- }
-}
diff --git a/src/Lang/C/CParser.ts b/src/Lang/C/CParser.ts
deleted file mode 100644
index 859dc6b..0000000
--- a/src/Lang/C/CParser.ts
+++ /dev/null
@@ -1,608 +0,0 @@
-import { Position, TextDocumentContentChangeEvent, TextEditor, TextLine, workspace } from "vscode";
-import ICodeParser from "../../Common/ICodeParser";
-import { IDocGen } from "../../Common/IDocGen";
-import { Config } from "../../Config";
-import { CArgument } from "./CArgument";
-import CDocGen from "./CDocGen";
-import { CParseTree } from "./CParseTree";
-import { CToken, CTokenType } from "./CToken";
-
-/**
- *
- * Parses C code for methods and signatures
- *
- * @export
- * @class CParser
- * @implements {ICodeParser}
- */
-export default class CParser implements ICodeParser {
- protected activeEditor: TextEditor;
- protected activeSelection: Position;
- protected readonly cfg: Config;
-
- private typeKeywords: string[];
- private stripKeywords: string[];
- private keywords: string[];
- private attributes: string[];
- private lexerVocabulary;
-
- constructor(cfg: Config) {
- this.cfg = cfg;
-
- this.typeKeywords = [
- "constexpr",
- "const",
- "struct",
- ];
-
- this.stripKeywords = [
- "final",
- "static",
- "inline",
- "friend",
- "virtual",
- "extern",
- "explicit",
- "class",
- "override",
- "typename",
- ];
-
- this.attributes = [
- "noexcept",
- "throw",
- "alignas",
- ];
-
- // Non type keywords will be stripped from the final return type.
- this.keywords = this.typeKeywords.concat(this.stripKeywords);
-
- this.lexerVocabulary = {
- ArraySubscript: (x: string): string => (x.match("^\\[[^\\[]*?\\]") || [])[0],
- Arrow: (x: string): string => (x.match("^->") || [])[0],
- Assignment: (x: string): string => (x.match("^=") || [])[0],
- Attribute: (x: string): string => {
- const attribute: string = (x.match("^\\[\\[[^\\[]*?\\]\\]") || [])[0];
- if (attribute !== undefined) {
- return attribute;
- }
-
- const foundIndex: number = this.attributes
- .findIndex((n: string) => x.startsWith(n) === true);
-
- if (foundIndex === -1) {
- return undefined;
- }
-
- if (x.slice(this.attributes[foundIndex].length).trim().startsWith("(") === false) {
- return x.slice(0, this.attributes[foundIndex].length);
- }
-
- const startEndOffset: number[] = this.GetSubExprStartEnd(x, 0, "(", ")");
- return startEndOffset[1] === 0 ? undefined : x.slice(0, startEndOffset[1]);
- },
- CloseParenthesis: (x: string): string => (x.match("^\\)") || [])[0],
- Comma: (x: string): string => (x.match("^,") || [])[0],
- CommentBlock: (x: string): string => {
- if (x.startsWith("/*") === false) {
- return undefined;
- }
-
- let closeOffset: number = x.indexOf("*/");
- closeOffset = closeOffset === -1 ? x.length : closeOffset + 2;
- return x.slice(0, closeOffset);
- },
- CommentLine: (x: string): string => {
- if (x.startsWith("//") === false) {
- return undefined;
- }
-
- let closeOffset: number = x.indexOf("\n");
- closeOffset = closeOffset === -1 ? x.length : closeOffset + 1;
- return x.slice(0, closeOffset);
- },
- CurlyBlock: (x: string): string => {
- if (x.startsWith("{") === false) {
- return undefined;
- }
- const startEndOffset: number[] = this.GetSubExprStartEnd(x, 0, "{", "}");
- return startEndOffset[1] === 0 ? undefined : x.slice(0, startEndOffset[1]);
- },
- Ellipsis: (x: string): string => (x.match("^\\.\\.\\.") || [])[0],
- OpenParenthesis: (x: string): string => (x.match("^\\(") || [])[0],
- Pointer: (x: string): string => (x.match("^\\*") || [])[0],
- Reference: (x: string): string => (x.match("^&") || [])[0],
- Symbol: (x: string): string => {
- // Handle access specifiers since they aren't really symbols.
- if (x.startsWith("public:") || x.startsWith("protected:") || x.startsWith("private:")) {
- return undefined;
- }
-
- // Handle specifiers
- const specifierFound: number = this.attributes
- .findIndex((n: string) => x.startsWith(n) === true);
-
- if (specifierFound !== -1) {
- return undefined;
- }
-
- // Handle decltype special cases.
- if (x.startsWith("decltype") === true) {
- const startEndOffset: number[] = this.GetSubExprStartEnd(x, 0, "(", ")");
- return startEndOffset[1] === 0 ? undefined : x.slice(0, startEndOffset[1]);
- }
-
- // Special case group up the fundamental types with the modifiers.
- // tslint:disable-next-line:max-line-length
- let reMatch: string = (x.match("^(unsigned|signed|short|long|int|char|double)(\\s+(unsigned|signed|short|long|int|char|double))+") || [])[0];
- if (reMatch !== undefined) {
- return reMatch.trim();
- }
-
- // Regex to handle a part of all symbols and includes all symbol special cases.
- // This is run in a loop because template parts of a symbol can't be parsed using regex.
- // tslint:disable-next-line:max-line-length
- const symbolRegex: string = "^([a-z|A-Z|:|_|~|\\d]*operator\\s*(\"\"_[a-z|A-Z]+|>>=|<<=|->\\*|\\+=|-=|\\*=|\\/=|%=|\\^=|&=|\\|=|<<|>>|==|!=|<=|->|>=|&&|\\|\\||\\+\\+|--|\\+|-|\\*|\\/|%|\\^|&|\||~|!|=|<|>|,|\\[\\s*\\]|\\(\\s*\\)|(new|delete)\\s*(\\[\\s*\\]){0,1}){0,1}|[a-z|A-Z|:|_|~|\\d]+)";
-
- reMatch = (x.match(symbolRegex) || [])[0];
- if (reMatch === undefined) {
- return undefined;
- }
-
- let symbol: string = reMatch;
- while (true) {
- if (x.slice(symbol.length).trim().startsWith("<") === true) {
- const offsets: number[] = this.GetSubExprStartEnd(x, symbol.length, "<", ">");
- if (offsets[1] === 0) {
- return undefined;
- }
- symbol = x.slice(0, offsets[1]);
- }
-
- reMatch = (x.slice(symbol.length).match(symbolRegex) || [])[0];
- if (reMatch === undefined) {
- break;
- }
-
- symbol += reMatch;
- }
-
- return symbol.replace(/\s+$/, "");
- },
- };
- }
-
- /**
- * @inheritdoc
- */
- public Parse(activeEdit: TextEditor): IDocGen {
- this.activeEditor = activeEdit;
- this.activeSelection = this.activeEditor.selection.active;
-
- let line: string = "";
- try {
- line = this.getLogicalLine();
- } catch (err) {
- // console.dir(err);
- }
-
- // template parsing is simpler by using heuristics rather then CTokenizing first.
- const templateArgs: string[] = [];
- while (line.startsWith("template")) {
- const template: string = this.GetTemplate(line);
-
- templateArgs.push.apply(templateArgs, this.GetArgsFromTemplate(template));
-
- line = line.slice(template.length, line.length + 1).trim();
- }
-
- let args: [CArgument, CArgument[]] = [new CArgument(), []];
- try {
- args = this.GetReturnAndArgs(line);
- } catch (err) {
- // console.dir(err);
- }
-
- return new CDocGen(
- this.activeEditor,
- this.activeSelection,
- this.cfg,
- templateArgs,
- args[0],
- args[1],
- );
- }
-
- /***************************************************************************
- Implementation
- ***************************************************************************/
- private getLogicalLine(): string {
- let logicalLine: string = "";
-
- let nextLine: Position = new Position(this.activeSelection.line + 1, this.activeSelection.character);
-
- let nextLineTxt: string = this.activeEditor.document.lineAt(nextLine.line).text.trim();
-
- // VSCode may enter a * on itself, we don"t want that in our method
- if (nextLineTxt === "*") {
- nextLineTxt = "";
- }
-
- let currentNest: number = 0;
- logicalLine = nextLineTxt;
-
- // Get method end line
- let linesToGet: number = 20;
- while (linesToGet-- > 0) { // Check for end of expression.
- nextLine = new Position(nextLine.line + 1, nextLine.character);
- nextLineTxt = this.activeEditor.document.lineAt(nextLine.line).text.trim();
-
- // Check if method has finished if curly brace is opened while
- // nesting is occuring.
- for (let i: number = 0; i < nextLineTxt.length; i++) {
- if (nextLineTxt[i] === "(") {
- currentNest++;
- } else if (nextLineTxt[i] === ")") {
- currentNest--;
- } else if (nextLineTxt[i] === "{" && currentNest === 0) {
- logicalLine += "\n" + nextLineTxt.slice(0, i);
- return logicalLine.replace(/^\s+|\s+$/g, "");
- } else if ((nextLineTxt[i] === ";"
- || (nextLineTxt[i] === ":" && nextLineTxt[i - 1] !== ":" && nextLineTxt[i + 1] !== ":"))
- && currentNest === 0) {
-
- logicalLine += "\n" + nextLineTxt.slice(0, i);
- return logicalLine.replace(/^\s+|\s+$/g, "");
- }
- }
-
- logicalLine += "\n" + nextLineTxt;
- }
-
- throw new Error("More then 20 lines were gotten from editor and no end of expression was found.");
- }
-
- private Tokenize(expression: string): CToken[] {
- const CTokens: CToken[] = [];
- expression = expression.replace(/^\s+|\s+$/g, "");
-
- while (expression.length !== 0) {
- const matches: CToken[] = Object.keys(this.lexerVocabulary)
- .map((k): CToken => new CToken(CTokenType[k], this.lexerVocabulary[k](expression)))
- .filter((t) => t.value !== undefined);
-
- if (matches.length === 0) {
- throw new Error("Next CToken couldn\'t be determined: " + expression);
- } else if (matches.length > 1) {
- throw new Error("Multiple matches for next CToken: " + expression);
- }
-
- CTokens.push(matches[0]);
- expression = expression.slice(matches[0].value.length, expression.length).replace(/^\s+|\s+$/g, "");
- }
-
- return CTokens;
- }
-
- private GetReturnAndArgs(line: string): [CArgument, CArgument[]] {
- // CTokenize rest of expression and remove comment CTokens;
- const CTokens: CToken[] = this.Tokenize(line)
- .filter((t) => t.type !== CTokenType.CommentBlock)
- .filter((t) => t.type !== CTokenType.CommentLine);
-
- // Create hierarchical tree based on the parenthesis.
- const tree: CParseTree = CParseTree.CreateTree(CTokens).Compact();
-
- // return argument.
- const func = this.GetArgument(tree);
- // check if it is a constructor or descructor since these have no name..
- // and reverse the assignment of type and name.
- if (func.name === null) {
- if (func.type.nodes.length !== 1) {
- throw new Error("Too many symbols found for constructor/descructor.");
- } else if (func.type.nodes[0] instanceof CParseTree) {
- throw new Error("One node found with just a CParseTree. Malformed input.");
- }
-
- func.name = (func.type.nodes[0] as CToken).value;
- func.type.nodes = [];
- }
-
- // Get arguments list as a CParseTree and create arguments from them.
- const params = this.GetArgumentList(tree)
- .map((a) => this.GetArgument(a));
-
- return [func, params];
- }
-
- private RemoveUnusedTokens(tree: CParseTree): CParseTree {
- tree = tree.Copy();
-
- // First slice of everything after assignment since that will not be used.
- const assignmentIndex = tree.nodes.findIndex((n) => n instanceof CToken && n.type === CTokenType.Assignment);
- if (assignmentIndex !== -1) {
- tree.nodes = tree.nodes.slice(0, assignmentIndex);
- }
-
- // Specifiers aren't needed so remove them.
- tree.nodes = tree.nodes
- .filter((n) => n instanceof CParseTree || (n instanceof CToken && n.type !== CTokenType.Attribute));
-
- return tree;
- }
-
- private GetArgumentList(tree: CParseTree): CParseTree[] {
- const args: CParseTree[] = [];
-
- tree = this.RemoveUnusedTokens(tree);
-
- let cursor: CParseTree = tree;
- while (this.IsFuncPtr(cursor.nodes) === true) {
- cursor = cursor.nodes.find((n) => n instanceof CParseTree) as CParseTree;
- }
-
- const argTree: CParseTree = cursor.nodes.find((n) => n instanceof CParseTree) as CParseTree;
- if (argTree === undefined) {
- throw new Error("Function arguments not found.");
- }
-
- // Split the argument tree on commas
- let arg: CParseTree = new CParseTree();
- for (const node of argTree.nodes) {
- if (node instanceof CToken && node.type === CTokenType.Comma) {
- args.push(arg);
- arg = new CParseTree();
- } else {
- arg.nodes.push(node);
- }
- }
-
- if (arg.nodes.length > 0) {
- args.push(arg);
- }
-
- return args;
- }
-
- private IsFuncPtr(nodes: Array<CToken | CParseTree>) {
- return nodes.filter((n) => n instanceof CParseTree).length === 2;
- }
-
- private StripNonTypeNodes(tree: CParseTree) {
- tree.nodes = tree.nodes
- // All strippable keywords.
- .filter((n) => {
- return !(n instanceof CToken
- && n.type === CTokenType.Symbol
- && this.stripKeywords.find((k) => k === n.value) !== undefined);
- });
- }
-
- private GetArgumentFromTrailingReturn(tree: CParseTree, startTrailingReturn: number): CArgument {
- const argument: CArgument = new CArgument();
-
- // Find index of auto prior to the first CParseTree.
- // If auto is not found something is going wrong since trailing return
- // requires auto.
- let autoIndex: number = -1;
- for (let i: number = 0; i < tree.nodes.length; i++) {
- const node = tree.nodes[i];
- if (node instanceof CParseTree) {
- break;
- }
- if (node.type === CTokenType.Symbol && node.value === "auto") {
- autoIndex = i;
- break;
- }
- }
-
- if (autoIndex === -1) {
- throw new Error("Function declaration has trailing return but type is not auto.");
- }
-
- // Get symbol between auto and CParseTree which is the argument name. It also may not be a keyword.
- for (let i: number = autoIndex + 1; i < tree.nodes.length; i++) {
- const node = tree.nodes[i];
- if (node instanceof CParseTree) {
- break;
- }
- if (node.type === CTokenType.Symbol && this.keywords.find((k) => k === node.value) === undefined) {
- argument.name = node.value;
- break;
- }
- }
-
- argument.type.nodes = tree.nodes.slice(startTrailingReturn + 1, tree.nodes.length);
- this.StripNonTypeNodes(argument.type);
-
- return argument;
- }
-
- private GetArgumentFromFuncPtr(tree: CParseTree): CArgument {
- const argument: CArgument = new CArgument();
-
- argument.type = tree;
-
- let cursor: CParseTree = tree;
-
- while (this.IsFuncPtr(cursor.nodes) === true) {
- cursor = cursor.nodes.find((n) => n instanceof CParseTree) as CParseTree;
- }
-
- // Remove CParseTree. This can be if it is a function declaration.
- const argumentsIndex = cursor.nodes.findIndex((n) => n instanceof CParseTree);
- if (argumentsIndex !== -1) {
- cursor.nodes.splice(argumentsIndex, 1);
- }
-
- // Find first symbol that is the argument name.
- // Remove it from the tree and set the name to the argument name
- for (let i: number = 0; i < cursor.nodes.length; i++) {
- const node = cursor.nodes[i];
- if (node instanceof CParseTree) {
- continue;
- }
-
- if (node.type === CTokenType.Symbol && this.keywords.find((k) => k === node.value) === undefined) {
- argument.name = node.value;
- cursor.nodes.splice(i, 1);
- }
- }
-
- this.StripNonTypeNodes(argument.type);
- return argument;
- }
-
- private GetDefaultArgument(tree: CParseTree): CArgument {
- const argument: CArgument = new CArgument();
-
- for (const node of tree.nodes) {
- if (node instanceof CParseTree) {
- break;
- }
- const symbolCount = argument.type.nodes
- .filter((n) => n instanceof CToken)
- .map((n) => n as CToken)
- .filter((n) => n.type === CTokenType.Symbol)
- .filter((n) => this.keywords.find((k) => k === n.value) === undefined)
- .length;
-
- if (node.type === CTokenType.Symbol
- && this.keywords.find((k) => k === node.value) === undefined
- ) {
- if (symbolCount === 1 && argument.name === null) {
- argument.name = node.value;
- continue;
- } else if (symbolCount > 1) {
- throw new Error("Too many non keyword symbols.");
- }
- }
-
- argument.type.nodes.push(node);
- }
-
- this.StripNonTypeNodes(argument.type);
- return argument;
- }
-
- private GetArgument(tree: CParseTree): CArgument {
- // Copy tree structure leave original untouched.
- const copy = this.RemoveUnusedTokens(tree);
-
- // Special case with only ellipsis. C style variadic arguments
- if (copy.nodes.length === 1) {
- const node = copy.nodes[0];
- if (node instanceof CToken && node.type === CTokenType.Ellipsis) {
- const argument: CArgument = new CArgument();
- argument.name = node.value;
- return argument;
- }
- }
-
- // Check if it is has a trailing return.
- const startTrailingReturn: number = copy.nodes
- .findIndex((t) => t instanceof CToken ? t.type === CTokenType.Arrow : false);
-
- // Special case trailing return.
- if (startTrailingReturn !== -1) {
- return this.GetArgumentFromTrailingReturn(copy, startTrailingReturn);
- }
-
- // Handle function pointers
- if (this.IsFuncPtr(copy.nodes) === true) {
- return this.GetArgumentFromFuncPtr(copy);
- }
-
- return this.GetDefaultArgument(copy);
- }
-
- private GetSubExprStartEnd(expression: string, startSearch: number, openExpr: string, closeExpr: string): number[] {
- let openExprOffset: number = -1;
- let nestedCount: number = 0;
- for (let i: number = startSearch; i < expression.length; i++) {
- if (expression[i] === openExpr && openExprOffset === -1) {
- openExprOffset = i;
- }
-
- if (expression[i] === openExpr) {
- nestedCount++;
- } else if (expression[i] === closeExpr && nestedCount > 0) {
- nestedCount--;
- }
-
- if (expression[i] === closeExpr && nestedCount === 0 && openExprOffset !== -1) {
- return [openExprOffset, i + 1];
- }
- }
-
- return [0, 0];
- }
-
- private GetTemplate(expression: string): string {
- if (expression.startsWith("template") === false) {
- return "";
- }
-
- let startTemplateOffset: number = -1;
- for (let i: number = "template".length; i < expression.length; i++) {
- if (expression[i] === "<") {
- startTemplateOffset = i;
- break;
- } else if (expression[i] !== " ") {
- return "";
- }
- }
-
- if (startTemplateOffset === -1) {
- return "";
- }
-
- const [start, end] = this.GetSubExprStartEnd(expression, startTemplateOffset, "<", ">");
- return expression.slice(0, end);
- }
-
- private GetArgsFromTemplate(template: string): string[] {
- const args: string[] = [];
- if (template === "") {
- return args;
- }
-
- // Remove <> and add a comma to the end to remove edge case.
- template = template.slice(template.indexOf("<") + 1, template.lastIndexOf(">")).replace(/^\s+|\s+$/g, "") + ",";
-
- const nestedCounts: { [key: string]: number; } = {
- "(": 0,
- "<": 0,
- "{": 0,
- };
-
- let lastSeparator: number = 0;
- for (let i: number = 0; i < template.length; i++) {
- const notInSubExpr: boolean = nestedCounts["<"] === 0
- && nestedCounts["("] === 0
- && nestedCounts["{"] === 0;
-
- if (notInSubExpr === true && template[i] === ",") {
- args.push(template.slice(lastSeparator + 1, i).replace(/^\s+|\s+$/g, ""));
- } else if (notInSubExpr === true && (template[i] === " " || template[i] === ".")) {
- lastSeparator = i;
- }
-
- if (template[i] === "(") {
- nestedCounts["("]++;
- } else if (template[i] === ")" && nestedCounts["("] > 0) {
- nestedCounts["("]--;
- } else if (template[i] === "<") {