You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
4.6 KiB
JavaScript

"use strict";
const matchValue = require("match-value");
const asExpression = require("as-expression");
// TODO: Refactor this
function convertStringNodeParts(node) {
let fullText = node.text;
let currentIndex = node.startIndex;
let parts = [];
function relative(index) {
return index - node.startIndex;
}
for (let child of node.children) {
if (child.type === "interpolation") {
if (child.startIndex > currentIndex) {
// We skipped some literal string
let value = fullText.slice(relative(currentIndex), relative(child.startIndex));
parts.push({ type: "NixInterpolationLiteral", value: value });
}
parts.push(convertNode(child));
currentIndex = child.endIndex;
}
}
if (currentIndex < node.endIndex) {
// Last bit of string literal at the end
let value = fullText.slice(relative(currentIndex), relative(node.endIndex));
parts.push({ type: "NixInterpolationLiteral", value: value });
}
return parts.map((part, i) => {
// FIXME: Verify that this logic is correct, and also holds up for actual interpolated strings
let isLiteralString = (part.type === "NixInterpolationLiteral");
if (isLiteralString) {
let strippedString = part.value;
if (i === 0 && isLiteralString) {
// Strip prefix quote
strippedString = strippedString.slice(1);
}
if (i === parts.length - 1 && isLiteralString) {
// Strip suffix quote
strippedString = strippedString.slice(0, -1);
}
return { ... part, value: strippedString };
} else {
return part;
}
});
}
function convertNode(node) {
let { type } = node;
let result = (node.isNamed)
? { type: type }
: { type: "token", text: node.text }
if (node.fields != null) {
for (let field of node.fields) {
let children = node[field];
let fieldName = field.replace(/Nodes?$/, "");
result[fieldName] = asExpression(() => {
if (children == null) {
return null;
} else if (Array.isArray(children)) {
return children.map(convertNode);
} else {
return convertNode(children);
}
});
}
}
// Special case: if we don't provide a Babel-compatible top-level Program entry, traversal will fail
if (type === "source_expression") {
return {
type: "Program",
sourceType: "script",
body: [ result.expression ]
};
}
// The below section is based on `alias` expressions throughout the grammar, and the rules in https://github.com/cstrahan/tree-sitter-nix/blob/83ee5993560bf15854c69b77d92e34456f8fb655/grammar.js#L53-L59
if (type === "identifier" || type === "attr_identifier") {
result.name = node.text;
} else if (type === "integer" || type === "float") {
result.value = node.text;
} else if (type === "string") {
result.parts = convertStringNodeParts(node);
} else if (type === "path" || type === "hpath") {
result.path = node.text;
} else if (type === "spath") {
// Strip the < and >
result.path = node.text.slice(1, -1);
} else if (type === "uri") {
result.uri = node.text;
}
if (type === "binary") {
// Unpack the anonymous token
result.operator = result.operator.text;
}
if (type === "rec_attrset") {
result.recursive = true;
} else if (type === "attrset") {
result.recursive = false;
}
if (type === "inherit") {
// We're inheriting from scope here, so the source expression is explicitly not set
result.expression = null;
} else if (type === "inherit_from") {
// Already set
}
// console.log(result);
result.type = matchValue(result.type, {
source_expression: "NixProgram",
token: "NixAnonymousToken",
path: "NixPathLiteral",
hpath: "NixHomePathLiteral",
spath: "NixEnvironmentPathLiteral",
uri: "NixURILiteral",
integer: "NixIntegerLiteral",
float: "NixFloatLiteral",
string: "NixStringLiteral",
parenthesized: "NixParenthesizedExpression",
attrset: "NixAttributeSet",
rec_attrset: "NixAttributeSet",
let: "NixLetIn",
let_attrset: "NixLetAttributeSet",
identifier: "NixIdentifier",
attr_identifier: "NixAttributeIdentifier",
attrpath: "NixAttributePath",
inherit: "NixInherit",
inherit_from: "NixInherit",
attrs_inherited: "NixInheritAttributes",
attrs_inherited_from: "NixInheritAttributes",
bind: "NixBinding",
binary: "NixBinaryOperation",
app: "NixFunctionCall",
select: "NixAttributeSelection",
interpolation: "NixInterpolationExpression",
// Function definitions
function: "NixFunctionDefinition",
formals: "NixUnpackedAttributes",
formal: "NixUnpackedAttribute",
});
return result;
}
/* Produces an AST that's roughly shape-compatible with ESTree so that Babel can deal with it */
module.exports = function prepareAST(tree) {
return convertNode(tree.rootNode);
};