Tagged template literal AST (#5185)
* tagged template literal ast * add comment
This commit is contained in:
parent
7466c81414
commit
730a4bcdad
|
@ -1353,6 +1353,30 @@
|
|||
return unquoted;
|
||||
}
|
||||
|
||||
// `StringLiteral`s can represent either entire literal strings
|
||||
// or pieces of text inside of e.g. an interpolated string.
|
||||
// When parsed as the former but needing to be treated as the latter
|
||||
// (e.g. the string part of a tagged template literal), this will return
|
||||
// a copy of the `StringLiteral` with the quotes trimmed from its location
|
||||
// data (like it would have if parsed as part of an interpolated string).
|
||||
withoutQuotesInLocationData() {
|
||||
var copy, endsWithNewline, locationData;
|
||||
endsWithNewline = this.originalValue.slice(-1) === '\n';
|
||||
locationData = Object.assign({}, this.locationData);
|
||||
locationData.first_column += this.quote.length;
|
||||
if (endsWithNewline) {
|
||||
locationData.last_line -= 1;
|
||||
locationData.last_column = locationData.last_line === locationData.first_line ? locationData.first_column + this.originalValue.length - '\n'.length : this.originalValue.slice(0, -1).length - '\n'.length - this.originalValue.slice(0, -1).lastIndexOf('\n');
|
||||
} else {
|
||||
locationData.last_column -= this.quote.length;
|
||||
}
|
||||
locationData.last_column_exclusive -= this.quote.length;
|
||||
locationData.range = [locationData.range[0] + this.quote.length, locationData.range[1] - this.quote.length];
|
||||
copy = new StringLiteral(this.originalValue, {quote: this.quote, initialChunk: this.initialChunk, finalChunk: this.finalChunk, indent: this.indent, double: this.double, heregex: this.heregex});
|
||||
copy.locationData = locationData;
|
||||
return copy;
|
||||
}
|
||||
|
||||
astProperties() {
|
||||
return {
|
||||
value: this.originalValue,
|
||||
|
@ -2831,7 +2855,7 @@
|
|||
exports.TaggedTemplateCall = TaggedTemplateCall = class TaggedTemplateCall extends Call {
|
||||
constructor(variable, arg, soak) {
|
||||
if (arg instanceof StringLiteral) {
|
||||
arg = new StringWithInterpolations(Block.wrap([new Value(arg)]));
|
||||
arg = StringWithInterpolations.fromStringLiteral(arg);
|
||||
}
|
||||
super(variable, [arg], soak);
|
||||
}
|
||||
|
@ -2840,6 +2864,17 @@
|
|||
return this.variable.compileToFragments(o, LEVEL_ACCESS).concat(this.args[0].compileToFragments(o, LEVEL_LIST));
|
||||
}
|
||||
|
||||
astType() {
|
||||
return 'TaggedTemplateExpression';
|
||||
}
|
||||
|
||||
astProperties(o) {
|
||||
return {
|
||||
tag: this.variable.ast(o, LEVEL_ACCESS),
|
||||
quasi: this.args[0].ast(o, LEVEL_LIST)
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//### Extends
|
||||
|
@ -6699,6 +6734,15 @@
|
|||
this.startQuote = startQuote;
|
||||
}
|
||||
|
||||
static fromStringLiteral(stringLiteral) {
|
||||
var updatedString, updatedStringValue;
|
||||
updatedString = stringLiteral.withoutQuotesInLocationData();
|
||||
updatedStringValue = new Value(updatedString).withLocationDataFrom(updatedString);
|
||||
return new StringWithInterpolations(Block.wrap([updatedStringValue]), {
|
||||
quote: stringLiteral.quote
|
||||
}).withLocationDataFrom(stringLiteral);
|
||||
}
|
||||
|
||||
// `unwrap` returns `this` to stop ancestor nodes reaching in to grab @body,
|
||||
// and using @body.compileNode. `StringWithInterpolations.compileNode` is
|
||||
// _the_ custom logic to output interpolated strings as code.
|
||||
|
|
|
@ -949,6 +949,34 @@ exports.StringLiteral = class StringLiteral extends Literal
|
|||
unquoted = unquoted.replace /\\n/g, '\n' if csx
|
||||
unquoted
|
||||
|
||||
# `StringLiteral`s can represent either entire literal strings
|
||||
# or pieces of text inside of e.g. an interpolated string.
|
||||
# When parsed as the former but needing to be treated as the latter
|
||||
# (e.g. the string part of a tagged template literal), this will return
|
||||
# a copy of the `StringLiteral` with the quotes trimmed from its location
|
||||
# data (like it would have if parsed as part of an interpolated string).
|
||||
withoutQuotesInLocationData: ->
|
||||
endsWithNewline = @originalValue[-1..] is '\n'
|
||||
locationData = Object.assign {}, @locationData
|
||||
locationData.first_column += @quote.length
|
||||
if endsWithNewline
|
||||
locationData.last_line -= 1
|
||||
locationData.last_column =
|
||||
if locationData.last_line is locationData.first_line
|
||||
locationData.first_column + @originalValue.length - '\n'.length
|
||||
else
|
||||
@originalValue[...-1].length - '\n'.length - @originalValue[...-1].lastIndexOf('\n')
|
||||
else
|
||||
locationData.last_column -= @quote.length
|
||||
locationData.last_column_exclusive -= @quote.length
|
||||
locationData.range = [
|
||||
locationData.range[0] + @quote.length
|
||||
locationData.range[1] - @quote.length
|
||||
]
|
||||
copy = new StringLiteral @originalValue, {@quote, @initialChunk, @finalChunk, @indent, @double, @heregex}
|
||||
copy.locationData = locationData
|
||||
copy
|
||||
|
||||
astProperties: ->
|
||||
return
|
||||
value: @originalValue
|
||||
|
@ -1906,12 +1934,19 @@ exports.RegexWithInterpolations = class RegexWithInterpolations extends Base
|
|||
|
||||
exports.TaggedTemplateCall = class TaggedTemplateCall extends Call
|
||||
constructor: (variable, arg, soak) ->
|
||||
arg = new StringWithInterpolations Block.wrap([ new Value arg ]) if arg instanceof StringLiteral
|
||||
arg = StringWithInterpolations.fromStringLiteral arg if arg instanceof StringLiteral
|
||||
super variable, [ arg ], soak
|
||||
|
||||
compileNode: (o) ->
|
||||
@variable.compileToFragments(o, LEVEL_ACCESS).concat @args[0].compileToFragments(o, LEVEL_LIST)
|
||||
|
||||
astType: -> 'TaggedTemplateExpression'
|
||||
|
||||
astProperties: (o) ->
|
||||
return
|
||||
tag: @variable.ast o, LEVEL_ACCESS
|
||||
quasi: @args[0].ast o, LEVEL_LIST
|
||||
|
||||
#### Extends
|
||||
|
||||
# Node to extend an object's prototype with an ancestor object.
|
||||
|
@ -4469,6 +4504,12 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
|
|||
constructor: (@body, {@quote, @startQuote} = {}) ->
|
||||
super()
|
||||
|
||||
@fromStringLiteral: (stringLiteral) ->
|
||||
updatedString = stringLiteral.withoutQuotesInLocationData()
|
||||
updatedStringValue = new Value(updatedString).withLocationDataFrom updatedString
|
||||
new StringWithInterpolations Block.wrap([updatedStringValue]), quote: stringLiteral.quote
|
||||
.withLocationDataFrom stringLiteral
|
||||
|
||||
children: ['body']
|
||||
|
||||
# `unwrap` returns `this` to stop ancestor nodes reaching in to grab @body,
|
||||
|
|
|
@ -670,12 +670,80 @@ test "AST as expected for RegexWithInterpolations node", ->
|
|||
quote: '///'
|
||||
flags: 'ig'
|
||||
|
||||
# test "AST as expected for TaggedTemplateCall node", ->
|
||||
# testExpression 'func"tagged"',
|
||||
# type: 'TaggedTemplateCall'
|
||||
# args: [
|
||||
# type: 'StringWithInterpolations'
|
||||
# ]
|
||||
test "AST as expected for TaggedTemplateCall node", ->
|
||||
testExpression 'func"tagged"',
|
||||
type: 'TaggedTemplateExpression'
|
||||
tag: ID 'func'
|
||||
quasi:
|
||||
type: 'TemplateLiteral'
|
||||
expressions: []
|
||||
quasis: [
|
||||
type: 'TemplateElement'
|
||||
value:
|
||||
raw: 'tagged'
|
||||
tail: yes
|
||||
]
|
||||
|
||||
testExpression 'a"b#{c}"',
|
||||
type: 'TaggedTemplateExpression'
|
||||
tag: ID 'a'
|
||||
quasi:
|
||||
type: 'TemplateLiteral'
|
||||
expressions: [
|
||||
ID 'c'
|
||||
]
|
||||
quasis: [
|
||||
type: 'TemplateElement'
|
||||
value:
|
||||
raw: 'b'
|
||||
tail: no
|
||||
,
|
||||
type: 'TemplateElement'
|
||||
value:
|
||||
raw: ''
|
||||
tail: yes
|
||||
]
|
||||
|
||||
testExpression '''
|
||||
a"""
|
||||
b#{c}
|
||||
"""
|
||||
''',
|
||||
type: 'TaggedTemplateExpression'
|
||||
tag: ID 'a'
|
||||
quasi:
|
||||
type: 'TemplateLiteral'
|
||||
expressions: [
|
||||
ID 'c'
|
||||
]
|
||||
quasis: [
|
||||
type: 'TemplateElement'
|
||||
value:
|
||||
raw: '\n b'
|
||||
tail: no
|
||||
,
|
||||
type: 'TemplateElement'
|
||||
value:
|
||||
raw: '\n'
|
||||
tail: yes
|
||||
]
|
||||
|
||||
testExpression """
|
||||
a'''
|
||||
b
|
||||
'''
|
||||
""",
|
||||
type: 'TaggedTemplateExpression'
|
||||
tag: ID 'a'
|
||||
quasi:
|
||||
type: 'TemplateLiteral'
|
||||
expressions: []
|
||||
quasis: [
|
||||
type: 'TemplateElement'
|
||||
value:
|
||||
raw: '\n b\n'
|
||||
tail: yes
|
||||
]
|
||||
|
||||
# test "AST as expected for Extends node", ->
|
||||
# testExpression 'class child extends parent',
|
||||
|
|
|
@ -5872,3 +5872,246 @@ test "AST location data as expected for RegexLiteral node", ->
|
|||
end:
|
||||
line: 5
|
||||
column: 3
|
||||
|
||||
test "AST as expected for TaggedTemplateCall node", ->
|
||||
testAstLocationData 'func"tagged"',
|
||||
type: 'TaggedTemplateExpression'
|
||||
tag:
|
||||
start: 0
|
||||
end: 4
|
||||
range: [0, 4]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 1
|
||||
column: 4
|
||||
quasi:
|
||||
quasis: [
|
||||
start: 5
|
||||
end: 11
|
||||
range: [5, 11]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 5
|
||||
end:
|
||||
line: 1
|
||||
column: 11
|
||||
]
|
||||
start: 4
|
||||
end: 12
|
||||
range: [4, 12]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 4
|
||||
end:
|
||||
line: 1
|
||||
column: 12
|
||||
start: 0
|
||||
end: 12
|
||||
range: [0, 12]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 1
|
||||
column: 12
|
||||
|
||||
testAstLocationData 'a"b#{c}"',
|
||||
type: 'TaggedTemplateExpression'
|
||||
tag:
|
||||
start: 0
|
||||
end: 1
|
||||
range: [0, 1]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 1
|
||||
column: 1
|
||||
quasi:
|
||||
expressions: [
|
||||
start: 5
|
||||
end: 6
|
||||
range: [5, 6]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 5
|
||||
end:
|
||||
line: 1
|
||||
column: 6
|
||||
]
|
||||
quasis: [
|
||||
start: 2
|
||||
end: 3
|
||||
range: [2, 3]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 2
|
||||
end:
|
||||
line: 1
|
||||
column: 3
|
||||
,
|
||||
start: 7
|
||||
end: 7
|
||||
range: [7, 7]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 7
|
||||
end:
|
||||
line: 1
|
||||
column: 7
|
||||
]
|
||||
start: 1
|
||||
end: 8
|
||||
range: [1, 8]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 1
|
||||
end:
|
||||
line: 1
|
||||
column: 8
|
||||
start: 0
|
||||
end: 8
|
||||
range: [0, 8]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 1
|
||||
column: 8
|
||||
|
||||
testAstLocationData '''
|
||||
a"""
|
||||
b#{c}
|
||||
"""
|
||||
''',
|
||||
type: 'TaggedTemplateExpression'
|
||||
tag:
|
||||
start: 0
|
||||
end: 1
|
||||
range: [0, 1]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 1
|
||||
column: 1
|
||||
quasi:
|
||||
expressions: [
|
||||
start: 10
|
||||
end: 11
|
||||
range: [10, 11]
|
||||
loc:
|
||||
start:
|
||||
line: 2
|
||||
column: 5
|
||||
end:
|
||||
line: 2
|
||||
column: 6
|
||||
]
|
||||
quasis: [
|
||||
start: 4
|
||||
end: 8
|
||||
range: [4, 8]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 4
|
||||
end:
|
||||
line: 2
|
||||
column: 3
|
||||
,
|
||||
start: 12
|
||||
end: 13
|
||||
range: [12, 13]
|
||||
loc:
|
||||
start:
|
||||
line: 2
|
||||
column: 7
|
||||
end:
|
||||
line: 3
|
||||
column: 0
|
||||
]
|
||||
start: 1
|
||||
end: 16
|
||||
range: [1, 16]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 1
|
||||
end:
|
||||
line: 3
|
||||
column: 3
|
||||
start: 0
|
||||
end: 16
|
||||
range: [0, 16]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 3
|
||||
column: 3
|
||||
|
||||
testAstLocationData """
|
||||
a'''
|
||||
b
|
||||
'''
|
||||
""",
|
||||
type: 'TaggedTemplateExpression'
|
||||
tag:
|
||||
start: 0
|
||||
end: 1
|
||||
range: [0, 1]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 1
|
||||
column: 1
|
||||
quasi:
|
||||
quasis: [
|
||||
start: 4
|
||||
end: 9
|
||||
range: [4, 9]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 4
|
||||
end:
|
||||
line: 3
|
||||
column: 0
|
||||
]
|
||||
start: 1
|
||||
end: 12
|
||||
range: [1, 12]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 1
|
||||
end:
|
||||
line: 3
|
||||
column: 3
|
||||
start: 0
|
||||
end: 12
|
||||
range: [0, 12]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 3
|
||||
column: 3
|
||||
|
|
|
@ -651,3 +651,72 @@ test 'Values with properties end up with a location that includes the properties
|
|||
eq complexIndex.locationData.first_column, 0
|
||||
eq complexIndex.locationData.last_line, 3
|
||||
eq complexIndex.locationData.last_column, 9
|
||||
|
||||
test 'StringWithInterpolations::fromStringLiteral() assigns correct location to tagged template literal', ->
|
||||
checkLocationData = (source, {stringWithInterpolationsLocationData, stringLocationData}) ->
|
||||
block = CoffeeScript.nodes source
|
||||
taggedTemplateLiteral = block.expressions[0].unwrap()
|
||||
{args: [stringWithInterpolations]} = taggedTemplateLiteral
|
||||
{body} = stringWithInterpolations
|
||||
{expressions: [stringValue]} = body
|
||||
string = stringValue.unwrap()
|
||||
|
||||
for field in ['first_line', 'first_column', 'last_line', 'last_column', 'last_line_exclusive', 'last_column_exclusive']
|
||||
eq stringWithInterpolations.locationData[field], stringWithInterpolationsLocationData[field]
|
||||
eq stringValue.locationData[field], stringLocationData[field]
|
||||
eq string.locationData[field], stringLocationData[field]
|
||||
|
||||
checkLocationData 'a"b"',
|
||||
stringWithInterpolationsLocationData:
|
||||
first_line: 0
|
||||
first_column: 1
|
||||
last_line: 0
|
||||
last_column: 3
|
||||
last_line_exclusive: 0
|
||||
last_column_exclusive: 4
|
||||
stringLocationData:
|
||||
first_line: 0
|
||||
first_column: 2
|
||||
last_line: 0
|
||||
last_column: 2
|
||||
last_line_exclusive: 0
|
||||
last_column_exclusive: 3
|
||||
|
||||
checkLocationData '''
|
||||
a"""
|
||||
b
|
||||
"""
|
||||
''',
|
||||
stringWithInterpolationsLocationData:
|
||||
first_line: 0
|
||||
first_column: 1
|
||||
last_line: 2
|
||||
last_column: 2
|
||||
last_line_exclusive: 2
|
||||
last_column_exclusive: 3
|
||||
stringLocationData:
|
||||
first_line: 0
|
||||
first_column: 4
|
||||
last_line: 1
|
||||
last_column: 3
|
||||
last_line_exclusive: 2
|
||||
last_column_exclusive: 0
|
||||
|
||||
checkLocationData '''
|
||||
a"""b
|
||||
"""
|
||||
''',
|
||||
stringWithInterpolationsLocationData:
|
||||
first_line: 0
|
||||
first_column: 1
|
||||
last_line: 1
|
||||
last_column: 2
|
||||
last_line_exclusive: 1
|
||||
last_column_exclusive: 3
|
||||
stringLocationData:
|
||||
first_line: 0
|
||||
first_column: 4
|
||||
last_line: 0
|
||||
last_column: 5
|
||||
last_line_exclusive: 1
|
||||
last_column_exclusive: 0
|
||||
|
|
Loading…
Reference in New Issue