Added VSCode extension to format Knossos IR
Add comment Fix issues with comments and strings and added test Added launch.json and tasks.json added knossos_ir_formatter.ts fix lost indent Handle let and assert correctly Handle def args and pr correctly fix indent etc refactor into stack Handle whitespace at the end Fix install.ps1 and remove tsc Added .js files cleanup and comment
This commit is contained in:
Родитель
61888fd07d
Коммит
bda7e74f2a
|
@ -0,0 +1,22 @@
|
|||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/etc/ks-vscode/"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/etc/ks-vscode/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "npm-watch"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "npm-watch",
|
||||
"command": "npm run watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/etc/ks-vscode"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -21,6 +21,35 @@ This extension contributes the following settings:
|
|||
|
||||
--->
|
||||
|
||||
## How to build the formatter extension
|
||||
|
||||
1. Install npm from https://nodejs.org/en/download/
|
||||
2. In `./etc/ks-vscode` run
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
3. Then run
|
||||
|
||||
```
|
||||
npm run compile
|
||||
```
|
||||
|
||||
4. Copy the files in `./etc/ks-vscode/` to `C:\Users\<user>\.vscode\extensions\knossos-vscode-0.01`
|
||||
|
||||
```
|
||||
.
|
||||
├── package.json
|
||||
├── language-configuration.json
|
||||
├── out
|
||||
| ├── extension.js
|
||||
| └── knossos_ir_formatter.js
|
||||
└── syntaxes
|
||||
└── Knossos.tmLanguage
|
||||
```
|
||||
|
||||
5. Start a new instance of VS Code. Open a `.ks` file. Make sure that VS Code auto detects Knossos IR. Then try "Format Document" (`shift` + `alt` + `F`).
|
||||
## Known Issues
|
||||
|
||||
None yet.
|
||||
|
|
|
@ -10,7 +10,9 @@ $manifest = echo `
|
|||
language-configuration.json `
|
||||
package.json `
|
||||
README.md `
|
||||
syntaxes\Knossos.tmLanguage
|
||||
syntaxes\Knossos.tmLanguage `
|
||||
out\knossos_ir_formatter.js `
|
||||
out\extension.js
|
||||
|
||||
write-host "ks-vscode: Deleting $extensions_dst"
|
||||
Remove-Item -force -rec $extensions_dst
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
"use strict";
|
||||
/*
|
||||
Knossos IR formatter VSCode extension
|
||||
|
||||
Original extension vscode-lisp-formatter was developed by Jacob Clark and licensed under the MIT license
|
||||
https://github.com/imjacobclark/vscode-lisp-formatter
|
||||
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const vscode = require("vscode");
|
||||
const knossos_ir_formatter_1 = require("./knossos_ir_formatter");
|
||||
function getFullDocRange(document) {
|
||||
return document.validateRange(new vscode.Range(new vscode.Position(0, 0), new vscode.Position(Number.MAX_VALUE, Number.MAX_VALUE)));
|
||||
}
|
||||
function activate(context) {
|
||||
vscode.languages.registerDocumentFormattingEditProvider('ks-lisp', {
|
||||
provideDocumentFormattingEdits(document) {
|
||||
return [vscode.TextEdit.replace(getFullDocRange(document), knossos_ir_formatter_1.formatKnossosIR(document.getText()))];
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.activate = activate;
|
||||
//# sourceMappingURL=extension.js.map
|
|
@ -0,0 +1,221 @@
|
|||
"use strict";
|
||||
/*
|
||||
Knossos IR formatter VSCode extension
|
||||
|
||||
Original extension vscode-lisp-formatter was developed by Jacob Clark and licensed under the MIT license
|
||||
https://github.com/imjacobclark/vscode-lisp-formatter
|
||||
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
function insertNewline(state) {
|
||||
const indent = " ".repeat(2 * (getIndent(state) + 1));
|
||||
// don't insert an extra line break if it is already on a new line
|
||||
if (!state.newLine) {
|
||||
state.formattedDocument += "\n" + indent;
|
||||
return state;
|
||||
}
|
||||
// just add the indent
|
||||
state.formattedDocument += indent;
|
||||
state.newLine = false;
|
||||
return state;
|
||||
}
|
||||
function checkContext(state, contextOp, positionLimit) {
|
||||
if (state.stack.length == 0) {
|
||||
return false;
|
||||
}
|
||||
let index = -1;
|
||||
for (var i = state.stack.length - 1; i >= 0; i--) {
|
||||
if (state.stack[i].op == contextOp) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index >= 0 && state.stack[index].argIndex <= positionLimit;
|
||||
}
|
||||
function getIndent(state) {
|
||||
return (state.stack.length > 0) ? state.stack[state.stack.length - 1].indent : 0;
|
||||
}
|
||||
function needLineBreak(state) {
|
||||
let currentOp = "";
|
||||
let opIndex = -1;
|
||||
if (state.stack.length > 0) {
|
||||
currentOp = state.stack[state.stack.length - 1].op;
|
||||
opIndex = state.stack[state.stack.length - 1].argIndex;
|
||||
}
|
||||
const insideIfPred = checkContext(state, "if", 1);
|
||||
const insideAssertPred = checkContext(state, "assert", 1);
|
||||
const insideDeltaVecDim = checkContext(state, "deltaVec", 2);
|
||||
const insideIndexDim = checkContext(state, "index", 1);
|
||||
const insideLetBind = checkContext(state, "let", 1);
|
||||
const insideDefArgs = checkContext(state, "def", 3);
|
||||
return currentOp == "lam" && opIndex == 2
|
||||
|| !insideIfPred && !insideAssertPred && !insideDeltaVecDim && !insideIndexDim
|
||||
&& (currentOp == "add"
|
||||
|| currentOp == "sub"
|
||||
|| currentOp == "mul"
|
||||
|| currentOp == "div")
|
||||
|| currentOp == "if" && opIndex >= 2
|
||||
|| currentOp == "assert" && opIndex >= 2
|
||||
|| currentOp == "tuple"
|
||||
|| currentOp == "deltaVec" && opIndex == 3
|
||||
|| currentOp == "let"
|
||||
|| currentOp == "" && insideLetBind
|
||||
|| currentOp == "" && insideDefArgs
|
||||
|| currentOp == "def"
|
||||
|| currentOp == "pr";
|
||||
}
|
||||
function formatOpenList(state, token) {
|
||||
const charIsEscaped = state.escaped;
|
||||
if (charIsEscaped) {
|
||||
state.escaped = false;
|
||||
}
|
||||
if (!state.string && !state.comment) {
|
||||
// Increment the argdIndex if no whitespace before opening parenthesis
|
||||
if (state.stack.length > 0 && !state.whitespaceEmitted) {
|
||||
state.stack[state.stack.length - 1].argIndex++;
|
||||
}
|
||||
const isOnNewLine = needLineBreak(state);
|
||||
if (isOnNewLine) {
|
||||
insertNewline(state);
|
||||
}
|
||||
state.formattedDocument += token;
|
||||
state.openLists++;
|
||||
const currentIndent = getIndent(state);
|
||||
state.stack.push({
|
||||
op: "",
|
||||
argIndex: 0,
|
||||
indent: (isOnNewLine) ? currentIndent + 1 : currentIndent
|
||||
});
|
||||
state.whitespaceEmitted = false;
|
||||
}
|
||||
else {
|
||||
state.formattedDocument += token;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
function formatCloseList(state, token) {
|
||||
const charIsEscaped = state.escaped;
|
||||
if (charIsEscaped) {
|
||||
state.escaped = false;
|
||||
}
|
||||
if (!state.string && !state.comment) {
|
||||
state.formattedDocument += token;
|
||||
state.openLists--;
|
||||
state.stack.pop();
|
||||
state.whitespaceEmitted = false;
|
||||
}
|
||||
else {
|
||||
state.formattedDocument += token;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
function formatNewLine(state, token) {
|
||||
state.newLine = true;
|
||||
state.comment = false;
|
||||
if (state.stack.length > 0 && !state.whitespaceEmitted) {
|
||||
state.whitespaceEmitted = true;
|
||||
state.stack[state.stack.length - 1].argIndex++;
|
||||
}
|
||||
state.formattedDocument += token;
|
||||
return state;
|
||||
}
|
||||
function formatWhitespace(state, token) {
|
||||
const charIsInsideACommentOrString = state.comment || state.string;
|
||||
// ignore repeated whitespace characters
|
||||
if (charIsInsideACommentOrString || state.stack.length > 0 && !state.whitespaceEmitted) {
|
||||
state.formattedDocument += token;
|
||||
// increase the argIndex when inside an array
|
||||
if (!charIsInsideACommentOrString) {
|
||||
state.whitespaceEmitted = true;
|
||||
state.stack[state.stack.length - 1].argIndex++;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
function formatComment(state, token) {
|
||||
const charIsEscaped = state.escaped;
|
||||
if (charIsEscaped) {
|
||||
state.escaped = false;
|
||||
}
|
||||
else if (!state.string) {
|
||||
state.comment = true;
|
||||
}
|
||||
state.formattedDocument += token;
|
||||
return state;
|
||||
}
|
||||
function escapeFormatter(state, token) {
|
||||
state.escaped = !state.escaped;
|
||||
state.formattedDocument += token;
|
||||
return state;
|
||||
}
|
||||
function stringFormatter(state, token) {
|
||||
const charIsEscaped = state.escaped;
|
||||
if (charIsEscaped) {
|
||||
state.escaped = false;
|
||||
}
|
||||
else {
|
||||
state.string = !state.string;
|
||||
}
|
||||
// Reset whitespaceEmitted
|
||||
state.whitespaceEmitted = false;
|
||||
state.formattedDocument += token;
|
||||
return state;
|
||||
}
|
||||
function formatKnossosIR(document) {
|
||||
let state = {
|
||||
document: document,
|
||||
formattedDocument: "",
|
||||
openLists: 0,
|
||||
comment: false,
|
||||
escaped: false,
|
||||
string: false,
|
||||
newLine: false,
|
||||
array: false,
|
||||
whitespaceEmitted: false,
|
||||
stack: []
|
||||
};
|
||||
let formatters = {
|
||||
"(": formatOpenList,
|
||||
")": formatCloseList,
|
||||
"\r": formatNewLine,
|
||||
"\n": formatNewLine,
|
||||
" ": formatWhitespace,
|
||||
"\t": formatWhitespace,
|
||||
";": formatComment,
|
||||
"\\": escapeFormatter,
|
||||
"\"": stringFormatter
|
||||
};
|
||||
for (var i = 0; i < state.document.length; i++) {
|
||||
const cursor = state.document.charAt(i);
|
||||
const formatter = formatters[cursor];
|
||||
if (formatter) {
|
||||
state = formatter(state, cursor);
|
||||
}
|
||||
else {
|
||||
// Uncommenting this will insert line breaks even when the subexpression does not start from parenthesis
|
||||
// but I found it a bit too verbose. The current approach of reading one character at a time cannot look
|
||||
// ahead and a proper parsing followed by pretty printing will solve this issue.
|
||||
// if (state.whitespaceEmitted && !state.comment && !state.string && needLineBreak(state)) {
|
||||
// insertNewline(state);
|
||||
//}
|
||||
state.formattedDocument += cursor;
|
||||
if (state.stack.length > 0) {
|
||||
let currentOp = state.stack[state.stack.length - 1];
|
||||
if (currentOp.argIndex == 0) {
|
||||
currentOp.op += cursor;
|
||||
}
|
||||
}
|
||||
state.newLine = false;
|
||||
// reset the whitespceEmitted variable if not in string or comment
|
||||
if (!state.comment && !state.string) {
|
||||
state.whitespaceEmitted = false;
|
||||
}
|
||||
if (state.escaped) {
|
||||
state.escaped = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return state.formattedDocument;
|
||||
}
|
||||
exports.formatKnossosIR = formatKnossosIR;
|
||||
//# sourceMappingURL=knossos_ir_formatter.js.map
|
|
@ -9,17 +9,47 @@
|
|||
"categories": [
|
||||
"Programming Languages"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:ks-lisp"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"contributes": {
|
||||
"languages": [{
|
||||
"id": "ks-lisp",
|
||||
"aliases": ["Knossos IR", "ks-lisp"],
|
||||
"extensions": [".ks",".kso"],
|
||||
"configuration": "./language-configuration.json"
|
||||
}],
|
||||
"grammars": [{
|
||||
"language": "ks-lisp",
|
||||
"scopeName": "source.ks-lisp",
|
||||
"path": "./syntaxes/Knossos.tmLanguage"
|
||||
}]
|
||||
"languages": [
|
||||
{
|
||||
"id": "ks-lisp",
|
||||
"aliases": [
|
||||
"Knossos IR",
|
||||
"ks-lisp"
|
||||
],
|
||||
"extensions": [
|
||||
".ks",
|
||||
".kso"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "ks-lisp",
|
||||
"scopeName": "source.ks-lisp",
|
||||
"path": "./syntaxes/Knossos.tmLanguage"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "tsc -p ./",
|
||||
"postinstall": "node ./node_modules/vscode/bin/install",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"test": "mocha -u tdd out/test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/assert": "^1.4.3",
|
||||
"@types/mocha": "",
|
||||
"@types/node": "",
|
||||
"vscode": "^1.1.22"
|
||||
},
|
||||
"dependencies": {
|
||||
"mocha": "^6.2.1",
|
||||
"typescript": "^3.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Knossos IR formatter VSCode extension
|
||||
|
||||
Original extension vscode-lisp-formatter was developed by Jacob Clark and licensed under the MIT license
|
||||
https://github.com/imjacobclark/vscode-lisp-formatter
|
||||
|
||||
*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { formatKnossosIR } from './knossos_ir_formatter'
|
||||
|
||||
function getFullDocRange(document: vscode.TextDocument): vscode.Range {
|
||||
return document.validateRange(
|
||||
new vscode.Range(
|
||||
new vscode.Position(0, 0),
|
||||
new vscode.Position(Number.MAX_VALUE, Number.MAX_VALUE)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
vscode.languages.registerDocumentFormattingEditProvider('ks-lisp', {
|
||||
provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.TextEdit[] {
|
||||
|
||||
return [vscode.TextEdit.replace(
|
||||
getFullDocRange(document),
|
||||
formatKnossosIR(document.getText()))];
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
Knossos IR formatter VSCode extension
|
||||
|
||||
Original extension vscode-lisp-formatter was developed by Jacob Clark and licensed under the MIT license
|
||||
https://github.com/imjacobclark/vscode-lisp-formatter
|
||||
|
||||
*/
|
||||
|
||||
function insertNewline(state: any) {
|
||||
const indent = " ".repeat(2 * (getIndent(state) + 1));
|
||||
|
||||
// don't insert an extra line break if it is already on a new line
|
||||
if (!state.newLine) {
|
||||
state.formattedDocument += "\n" + indent;
|
||||
return state;
|
||||
}
|
||||
|
||||
// just add the indent
|
||||
state.formattedDocument += indent;
|
||||
state.newLine = false;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function checkContext(state: any, contextOp: string, positionLimit: number) {
|
||||
if (state.stack.length == 0) {
|
||||
return false;
|
||||
}
|
||||
let index = -1;
|
||||
for (var i = state.stack.length - 1; i >= 0; i--) {
|
||||
if (state.stack[i].op == contextOp) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index >= 0 && state.stack[index].argIndex <= positionLimit;
|
||||
}
|
||||
|
||||
function getIndent(state: any) {
|
||||
return (state.stack.length > 0) ? state.stack[state.stack.length-1].indent : 0;
|
||||
}
|
||||
|
||||
function needLineBreak(state: any) {
|
||||
let currentOp = "";
|
||||
let opIndex = -1;
|
||||
if (state.stack.length > 0) {
|
||||
currentOp = state.stack[state.stack.length-1].op;
|
||||
opIndex = state.stack[state.stack.length-1].argIndex;
|
||||
}
|
||||
|
||||
const insideIfPred = checkContext(state, "if", 1);
|
||||
const insideAssertPred = checkContext(state, "assert", 1);
|
||||
const insideDeltaVecDim = checkContext(state, "deltaVec", 2);
|
||||
const insideIndexDim = checkContext(state, "index", 1);
|
||||
const insideLetBind = checkContext(state, "let", 1);
|
||||
const insideDefArgs = checkContext(state, "def", 3);
|
||||
|
||||
return currentOp == "lam" && opIndex == 2
|
||||
|| !insideIfPred && !insideAssertPred && !insideDeltaVecDim && !insideIndexDim
|
||||
&& (currentOp == "add"
|
||||
|| currentOp == "sub"
|
||||
|| currentOp == "mul"
|
||||
|| currentOp == "div")
|
||||
|| currentOp == "if" && opIndex >= 2
|
||||
|| currentOp == "assert" && opIndex >= 2
|
||||
|| currentOp == "tuple"
|
||||
|| currentOp == "deltaVec" && opIndex == 3
|
||||
|| currentOp == "let"
|
||||
|| currentOp == "" && insideLetBind
|
||||
|| currentOp == "" && insideDefArgs
|
||||
|| currentOp == "def"
|
||||
|| currentOp == "pr";
|
||||
}
|
||||
|
||||
function formatOpenList(state: any, token: string) {
|
||||
const charIsEscaped = state.escaped;
|
||||
if (charIsEscaped) {
|
||||
state.escaped = false;
|
||||
}
|
||||
|
||||
if (!state.string && !state.comment) {
|
||||
// Increment the argdIndex if no whitespace before opening parenthesis
|
||||
if (state.stack.length > 0 && !state.whitespaceEmitted) {
|
||||
state.stack[state.stack.length-1].argIndex++;
|
||||
}
|
||||
const isOnNewLine = needLineBreak(state);
|
||||
|
||||
if (isOnNewLine) {
|
||||
insertNewline(state);
|
||||
}
|
||||
|
||||
state.formattedDocument += token;
|
||||
state.openLists++;
|
||||
|
||||
const currentIndent = getIndent(state);
|
||||
state.stack.push({
|
||||
op: "",
|
||||
argIndex: 0,
|
||||
indent: (isOnNewLine) ? currentIndent + 1 : currentIndent
|
||||
});
|
||||
state.whitespaceEmitted = false;
|
||||
} else {
|
||||
state.formattedDocument += token;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function formatCloseList(state: any, token: string) {
|
||||
const charIsEscaped = state.escaped;
|
||||
if (charIsEscaped) {
|
||||
state.escaped = false;
|
||||
}
|
||||
|
||||
if (!state.string && !state.comment) {
|
||||
state.formattedDocument += token;
|
||||
state.openLists--;
|
||||
|
||||
state.stack.pop();
|
||||
state.whitespaceEmitted = false;
|
||||
} else {
|
||||
state.formattedDocument += token;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function formatNewLine(state: any, token: string) {
|
||||
state.newLine = true;
|
||||
state.comment = false;
|
||||
if (state.stack.length > 0 && !state.whitespaceEmitted) {
|
||||
state.whitespaceEmitted = true;
|
||||
state.stack[state.stack.length-1].argIndex++;
|
||||
}
|
||||
state.formattedDocument += token;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function formatWhitespace(state: any, token: string) {
|
||||
const charIsInsideACommentOrString = state.comment || state.string;
|
||||
// ignore repeated whitespace characters
|
||||
if (charIsInsideACommentOrString || state.stack.length > 0 && !state.whitespaceEmitted) {
|
||||
state.formattedDocument += token;
|
||||
|
||||
// increase the argIndex when inside an array
|
||||
if (!charIsInsideACommentOrString) {
|
||||
state.whitespaceEmitted = true;
|
||||
state.stack[state.stack.length-1].argIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function formatComment(state: any, token: string) {
|
||||
const charIsEscaped = state.escaped;
|
||||
if (charIsEscaped) {
|
||||
state.escaped = false;
|
||||
} else if (!state.string) {
|
||||
state.comment = true;
|
||||
}
|
||||
|
||||
state.formattedDocument += token;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function escapeFormatter(state: any, token: string) {
|
||||
state.escaped = !state.escaped;
|
||||
state.formattedDocument += token;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function stringFormatter(state: any, token: string) {
|
||||
const charIsEscaped = state.escaped;
|
||||
if (charIsEscaped) {
|
||||
state.escaped = false;
|
||||
} else {
|
||||
state.string = !state.string;
|
||||
}
|
||||
|
||||
// Reset whitespaceEmitted
|
||||
state.whitespaceEmitted = false;
|
||||
|
||||
state.formattedDocument += token;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export function formatKnossosIR(document: string) {
|
||||
let state = {
|
||||
document: document,
|
||||
formattedDocument: "",
|
||||
openLists: 0,
|
||||
comment: false,
|
||||
escaped: false,
|
||||
string: false,
|
||||
newLine: false,
|
||||
array: false,
|
||||
whitespaceEmitted: false,
|
||||
stack: [] as {op: string, argIndex: number, indent: number}[]
|
||||
}
|
||||
|
||||
let formatters: any = {
|
||||
"(": formatOpenList,
|
||||
")": formatCloseList,
|
||||
"\r": formatNewLine,
|
||||
"\n": formatNewLine,
|
||||
" ": formatWhitespace,
|
||||
"\t": formatWhitespace,
|
||||
";": formatComment,
|
||||
"\\": escapeFormatter,
|
||||
"\"": stringFormatter
|
||||
}
|
||||
|
||||
for (var i = 0; i < state.document.length; i++) {
|
||||
const cursor = state.document.charAt(i)
|
||||
const formatter = formatters[cursor];
|
||||
|
||||
if (formatter) {
|
||||
state = formatter(state, cursor);
|
||||
} else {
|
||||
// Uncommenting this will insert line breaks even when the subexpression does not start from parenthesis
|
||||
// but I found it a bit too verbose. The current approach of reading one character at a time cannot look
|
||||
// ahead and a proper parsing followed by pretty printing will solve this issue.
|
||||
|
||||
// if (state.whitespaceEmitted && !state.comment && !state.string && needLineBreak(state)) {
|
||||
// insertNewline(state);
|
||||
//}
|
||||
state.formattedDocument += cursor;
|
||||
if (state.stack.length > 0) {
|
||||
let currentOp = state.stack[state.stack.length-1];
|
||||
if (currentOp.argIndex == 0) {
|
||||
currentOp.op += cursor;
|
||||
}
|
||||
}
|
||||
state.newLine = false;
|
||||
// reset the whitespceEmitted variable if not in string or comment
|
||||
if (!state.comment && !state.string) {
|
||||
state.whitespaceEmitted = false;
|
||||
}
|
||||
if (state.escaped) {
|
||||
state.escaped = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state.formattedDocument;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
import * as assert from 'assert'
|
||||
import { formatKnossosIR } from '../knossos_ir_formatter'
|
||||
|
||||
suite('Knossos IR Formatter Extension Tests', function() {
|
||||
// Defines a Mocha unit test
|
||||
test('Correctly handles comments and strings', function() {
|
||||
const formattedDocument = formatKnossosIR(`
|
||||
; comment
|
||||
(def "this is a test" (Vec m (Vec n Float)) ((m : Float) (n : Float)) ; comment with whitespace
|
||||
((build " " (lam (mi : Integer) ; (()) comment with parentheses
|
||||
(build " " (lam (ni : Integer) ")))"))))))`);
|
||||
const expectedFormattedDocument = `
|
||||
; comment
|
||||
(def "this is a test"
|
||||
(Vec m (Vec n Float))
|
||||
(
|
||||
(m : Float)
|
||||
(n : Float)) ; comment with whitespace
|
||||
((build " " (lam (mi : Integer) ; (()) comment with parentheses
|
||||
(build " " (lam (ni : Integer) ")))"))))))`;
|
||||
assert.equal(formattedDocument, expectedFormattedDocument);
|
||||
});
|
||||
|
||||
test('Do not lose indent after newline', function() {
|
||||
const formattedDocument = formatKnossosIR(`
|
||||
(build n (lam (i : Integer)
|
||||
(to_float i)))`);
|
||||
const expectedFormattedDocument = `
|
||||
(build n (lam (i : Integer)
|
||||
(to_float i)))`;
|
||||
assert.equal(formattedDocument, expectedFormattedDocument);
|
||||
});
|
||||
|
||||
test('Handle let correctly', function() {
|
||||
const formattedDocument = formatKnossosIR(`
|
||||
(let ((a 1.0) (b 2.0) (c (add a b))) (pr c))`);
|
||||
const expectedFormattedDocument = `
|
||||
(let
|
||||
(
|
||||
(a 1.0)
|
||||
(b 2.0)
|
||||
(c (add a b)))
|
||||
(pr c))`;
|
||||
assert.equal(formattedDocument, expectedFormattedDocument);
|
||||
});
|
||||
|
||||
test('Handle assert correctly', function() {
|
||||
const formattedDocument = formatKnossosIR(`
|
||||
(assert (gt (add (sub x 1.0) 1.0) 0.0) (div 1.0 x))`);
|
||||
const expectedFormattedDocument = `
|
||||
(assert (gt (add (sub x 1.0) 1.0) 0.0)
|
||||
(div 1.0 x))`;
|
||||
assert.equal(formattedDocument, expectedFormattedDocument);
|
||||
});
|
||||
|
||||
test('Handle pr correctly', function() {
|
||||
const formattedDocument = formatKnossosIR(`
|
||||
(pr (add x y) (sub x y))`);
|
||||
const expectedFormattedDocument = `
|
||||
(pr
|
||||
(add x y)
|
||||
(sub x y))`;
|
||||
assert.equal(formattedDocument, expectedFormattedDocument);
|
||||
});
|
||||
|
||||
test('Handle trailing whitespace', function() {
|
||||
const formattedDocument = formatKnossosIR(`
|
||||
(add x y) `);
|
||||
const expectedFormattedDocument = `
|
||||
(add x y)`
|
||||
assert.equal(formattedDocument, expectedFormattedDocument);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test",
|
||||
".vscode"
|
||||
]
|
||||
}
|
Загрузка…
Ссылка в новой задаче