Skip to content
This repository has been archived by the owner on Feb 26, 2021. It is now read-only.

Optimize SExpression Parsing #36

Merged
merged 2 commits into from
Sep 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 46 additions & 31 deletions lib/parser/agda.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

114 changes: 0 additions & 114 deletions lib/parser/stream/agda-response.js

This file was deleted.

58 changes: 0 additions & 58 deletions lib/parser/stream/s-expression.js

This file was deleted.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"dependencies": {
"bluebird": "^3.4.1",
"classnames": "^2.2.5",
"lisp-to-array": "^0.2",
"lodash": "^4.13.1",
"parsimmon": "^0.9.0",
"react": "^15.3.1",
Expand Down
76 changes: 45 additions & 31 deletions src/parser/agda.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as _ from "lodash";
import { Agda } from "../types";
var lispToArray = require("lisp-to-array");

function parseAgdaResponse(raw: string): Agda.Response {

Expand Down Expand Up @@ -145,55 +144,70 @@ function parseInfoActionType(s: String): string {
////////////////////////////////////////////////////////////////////////////////
// Parsing S-Expressions
////////////////////////////////////////////////////////////////////////////////
function parse_sexp(string: string): any {
var sexp = [[]];
var word = '';
var in_str = false;

function pushLastWord(word) {
var n = parseInt(word);
if (isNaN(n)) {
pushInLast(word);
} else {
pushInLast(n);
}
}

function pushInLast(elem) {
sexp[sexp.length - 1].push(elem);
}

for (var i = 0; i < string.length; i++) {
var char = string[i];
if (char == '\'' && !in_str) {
} else if (char == '(' && !in_str) {
sexp.push([]);
} else if (char == ')' && !in_str) {
if (word != '') {
pushLastWord(word);
word = '';
}
pushInLast(sexp.pop());
} else if (char == ' ' && !in_str) {
if (word != '') {
pushLastWord(word);
word = '';
}
} else if (char == '\"') {
in_str = !in_str;
} else {
word += char;
}
}
return sexp[0];
}

function parseSExpression(s: string): any {
return postprocess(lispToArray(preprocess(s)));
return parse_sexp(preprocess(s))[0];
}

function preprocess(chunk: string): string {
// polyfill String::startsWith
if (chunk.substr(0, 6) === "((last") {
// drop wierd prefix like ((last . 1))
let index = chunk.indexOf("(agda");
let length = chunk.length
let length = chunk.length;
chunk = chunk.substring(index, length - 1);
}
if (chunk.substr(0, 13) === "cannot read: ") {
// handles Agda parse error
chunk = chunk.substring(12);
chunk = `(agda2-parse-error${chunk})`;
}
// make it friendly to 'lisp-to-array' package
chunk = chunk.replace(/'\(/g, '(__number__ ');
chunk = chunk.replace(/\("/g, '(__string__ "');
chunk = chunk.replace(/\(\)/g, '(__nil__)');

return chunk;
}

// recursive cosmetic surgery
function postprocess(node: string | string[]): any {
if (node instanceof Array) {
switch (node[0]) {
case "`": // ["`", "some string"] => "some string"
return postprocess(node[1]);
case "__number__": // ["__number__", 1, 2, 3] => [1, 2, 3]
case "__string__": // ["__string__", 1, 2, 3] => [1, 2, 3]
case "__nil__": // ["__nil__"] => []
node.shift();
return postprocess(node);
default: // keep traversing
return node.map(function(x) { return postprocess(x); });
}
} else {
if (typeof node === "string") {
// some ()s in strings were replaced with (__nil__) when preprocessing
return node.replace("(__nil__)", "()");
} else {
return node;
}
}
}

export {
parseAgdaResponse
}