"use strict" ;
const matchValue = require ( "match-value" ) ;
const asExpression = require ( "as-expression" ) ;
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 ;
}
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" ,
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 ) ;
} ;