From dc1565f4d0fb9d100272ba2b9e9ed39100095153 Mon Sep 17 00:00:00 2001 From: jakergrossman Date: Thu, 4 Nov 2021 12:52:18 -0500 Subject: [PATCH] Update parser Implied nodes caused ambiguity when querying for context. Now every line is considered, even blank lines, to make the process much easier. --- package.json | 6 +++++ src/commands.ts | 59 +++++++++++++++++++++++++++++++++++++++++++++ src/pylex/index.ts | 2 +- src/pylex/lexer.ts | 11 +++++---- src/pylex/node.ts | 2 ++ src/pylex/parser.ts | 54 +++++++++++++++++++++++++---------------- src/pylex/token.ts | 1 + 7 files changed, 108 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index e6c5da7..323ec5a 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,13 @@ { "command": "mind-reader.selectTheme", "title": "Select Theme" + }, + + { + "command": "mind-reader.runLineContext", + "title": "Run Line Context" } + ], "keybindings": [ { diff --git a/src/commands.ts b/src/commands.ts index 537df19..b587629 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import * as pl from './pylex'; /** * @type {Object} Command // Command to register with the VS Code Extension API @@ -48,6 +49,11 @@ const commands: Command[] = [ name: 'mind-reader.resetEditorScale', callback: resetEditorScale, }, + + { + name: 'mind-reader.runLineContext', + callback: runLineContext, + }, ]; // COMMAND CALLBACK IMPLEMENTATIONS @@ -76,4 +82,57 @@ function resetEditorScale(): void { vscode.commands.executeCommand('workbench.action.zoomReset'); } +function runLineContext(): void { + let editor = vscode.window.activeTextEditor; + if (editor) { + // current text and line number + let editorText = editor.document.getText(); + let line = editor.selection.active.line; + + // get tab info settings + let size = parseInt(editor.options.tabSize as string); + let hard = !editor.options.insertSpaces; + + // initialize parser + let parser = new pl.Parser(editorText, {size, hard}); + parser.parse(); + + let context = parser.context(line); + + // build text + let contentString = createContextString(context, line); + vscode.window.showInformationMessage(contentString); + } else { + vscode.window.showErrorMessage('No document currently active'); + } +} + +function createContextString(context: pl.LexNode[], line: number): string { + if (context.length < 1) { + throw new Error('Cannot create context string for empty context'); + } + + let contextString = 'Line ' + (line+1); // 1 based + if (context[0].token && context[0].token.attr) { + contextString += ': ' + context[0].token.type.toString() + ' ' + context[0].token.attr.toString(); + } + for (let i = 1; i < context.length; i++) { + let node = context[i]; + if (node.label === 'root') { + // root + contextString += ' in the Document Root'; + continue; + } + + if (node.token!.type != pl.PylexSymbol.EMPTY && + node.token!.type != pl.PylexSymbol.INDENT) { + contextString += ' inside ' + node.token!.type.toString(); + if (node.token!.attr) { + contextString += ' ' + node.token!.attr.toString(); + } + } + } + return contextString; +} + export default commands; diff --git a/src/pylex/index.ts b/src/pylex/index.ts index 720a23b..66a31ed 100644 --- a/src/pylex/index.ts +++ b/src/pylex/index.ts @@ -4,4 +4,4 @@ export {default as LineToken} from './token'; export {default as Lexer} from './lexer'; export {default as LexNode} from './node'; export {TabInfo as TabInfo} from './token'; - +export {Symbol as PylexSymbol} from './token'; diff --git a/src/pylex/lexer.ts b/src/pylex/lexer.ts index 02f831c..1f7c336 100644 --- a/src/pylex/lexer.ts +++ b/src/pylex/lexer.ts @@ -159,14 +159,15 @@ export default class Lexer { } // No rules matched - // Skip this line if it is whitespace, comment, or empty + // TODO: move to rules if (/^\s*(#.*)?$/.test(line)) { - this.pos++; - continue; + // "empty" line + token = new LineToken(Symbol.EMPTY, this.pos, 999999); + } else { + // This is an INDENT token + token = new LineToken(Symbol.INDENT, this.pos, indent); } - // This is an INDENT token - token = new LineToken(Symbol.INDENT, this.pos, indent); this._currToken = token; this.pos++; return this.currToken(); diff --git a/src/pylex/node.ts b/src/pylex/node.ts index a51010f..a3e00e5 100644 --- a/src/pylex/node.ts +++ b/src/pylex/node.ts @@ -2,6 +2,8 @@ import * as vscode from 'vscode'; import LineToken from './token'; +/* TODO: make accessing children and parent less tedious */ +/* TODO: 'root.children()![i])' */ /** * A node in a Parse tree. */ diff --git a/src/pylex/parser.ts b/src/pylex/parser.ts index 6d3c6d3..277451a 100644 --- a/src/pylex/parser.ts +++ b/src/pylex/parser.ts @@ -3,6 +3,7 @@ import * as vscode from 'vscode'; import { EOFTOKEN, Symbol, TabInfo } from './token'; import Lexer from './lexer'; import LexNode from './node'; +/* TODO: update design doc */ /** * A parse tree generator @@ -71,8 +72,20 @@ export default class Parser { // go up 1 level of recursion at a time to unravel properly this.currIndent--; return children; - } else if (this.lexer.currToken().type === Symbol.INDENT) { + } + + if (this.lexer.currToken().type === Symbol.INDENT || + this.lexer.currToken().type === Symbol.EMPTY) { + const label = this.lexer.currToken().type; // regular code, advance and stay in same block + children.push(new LexNode( + label, + vscode.TreeItemCollapsibleState.None, + this.lexer.currToken(), + null, + parent) + ); + this.lexer.next(); continue; } else { @@ -105,29 +118,28 @@ export default class Parser { * @param `lineNumber` The line number to query context for. * @return An array of LexNodes for the root path containing `lineNumber` */ - context(lineNumber: number): LexNode[] { - if (!this.root.children()) { - return []; + context(lineNumber: number, root?: LexNode): LexNode[] { + if (!root) { + root = this.root } - // Returns the LexNode that is the parent - // of the queried line number - let find = (root: LexNode): LexNode | undefined => { - let prevChild: LexNode; - for (var child of root.children()!) { - if (lineNumber < child.token!.linenr) { - if (prevChild!.children()) { - return find(prevChild!); - } else { - return prevChild!; - } - } else { - prevChild = child; + // is this the target? + if (root.token && root.token!.linenr === lineNumber) { + // match + return root.rootPath(); + } + + if (root.children()) { + // recursive call + for (let i = 0; i < root.children()!.length; i++) { + let ctx = this.context(lineNumber, root.children()![i]); + if (ctx.length > 0) { + // a rootpath was returned + return ctx; } } - }; - - let target = find(this.root); - return target!.rootPath(); + } + // no matches + return []; } } diff --git a/src/pylex/token.ts b/src/pylex/token.ts index a726fb3..b6a54e0 100644 --- a/src/pylex/token.ts +++ b/src/pylex/token.ts @@ -17,6 +17,7 @@ export enum Symbol { FINALLY = "finally", WITH = "with", INDENT = "INDENT", // Indent token, default if not EOF, only contains indent information + EMPTY = "EMPTY", // empty line, used only to associate with the previous line EOF = "EOF" }