mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
Support import.meta and import.meta.* (#5319)
Co-authored-by: Geoffrey Booth <webmaster@geoffreybooth.com>
This commit is contained in:
parent
389ce89555
commit
2fd9ee403c
12 changed files with 358 additions and 168 deletions
3
.github/workflows/continuous-integration.yml
vendored
3
.github/workflows/continuous-integration.yml
vendored
|
@ -2,8 +2,7 @@
|
|||
|
||||
name: Build and Test
|
||||
|
||||
on:
|
||||
push
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
patternString = patternString.replace(/\s{2,}/g, ' ');
|
||||
patternCount = patternString.split(' ').length;
|
||||
if (action) {
|
||||
// This code block does string replacements in the generated `parser.js`
|
||||
// file, replacing the calls to the `LOC` function and other strings as
|
||||
// listed below.
|
||||
action = (match = unwrap.exec(action)) ? match[1] : `(${action}())`;
|
||||
// All runtime functions we need are defined on `yy`
|
||||
action = action.replace(/\bnew /g, '$&yy.');
|
||||
|
@ -50,8 +53,33 @@
|
|||
getAddDataToNodeFunctionString = function(first, last, forceUpdateLocation = true) {
|
||||
return `yy.addDataToNode(yy, @${first}, ${first[0] === '$' ? '$$' : '$'}${first}, ${last ? `@${last}, ${last[0] === '$' ? '$$' : '$'}${last}` : 'null, null'}, ${forceUpdateLocation ? 'true' : 'false'})`;
|
||||
};
|
||||
// This code replaces the calls to `LOC` with the `yy.addDataToNode` string
|
||||
// defined above. The `LOC` function, when used below in the grammar rules,
|
||||
// is used to make sure that newly created node class objects get correct
|
||||
// location data assigned to them. By default, the grammar will assign the
|
||||
// location data spanned by *all* of the tokens on the left (e.g. a string
|
||||
// such as `'Body TERMINATOR Line'`) to the “top-level” node returned by
|
||||
// the grammar rule (the function on the right). But for “inner” node class
|
||||
// objects created by grammar rules, they won’t get correct location data
|
||||
// assigned to them without adding `LOC`.
|
||||
|
||||
// For example, consider the grammar rule `'NEW_TARGET . Property'`, which
|
||||
// is handled by a function that returns
|
||||
// `new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3)`.
|
||||
// The `1` in `LOC(1)` refers to the first token (`NEW_TARGET`) and the `3`
|
||||
// in `LOC(3)` refers to the third token (`Property`). In order for the
|
||||
// `new IdentifierLiteral` to get assigned the location data corresponding
|
||||
// to `new` in the source code, we use
|
||||
// `LOC(1)(new IdentifierLiteral ...)` to mean “assign the location data of
|
||||
// the *first* token of this grammar rule (`NEW_TARGET`) to this
|
||||
// `new IdentifierLiteral`”. The `LOC(3)` means “assign the location data of
|
||||
// the *third* token of this grammar rule (`Property`) to this
|
||||
// `new Access`”.
|
||||
returnsLoc = /^LOC/.test(action);
|
||||
action = action.replace(/LOC\(([0-9]*)\)/g, getAddDataToNodeFunctionString('$1'));
|
||||
// A call to `LOC` with two arguments, e.g. `LOC(2,4)`, sets the location
|
||||
// data for the generated node on both of the referenced tokens (the second
|
||||
// and fourth in this example).
|
||||
action = action.replace(/LOC\(([0-9]*),\s*([0-9]*)\)/g, getAddDataToNodeFunctionString('$1', '$2'));
|
||||
performActionFunctionString = `$$ = ${getAddDataToNodeFunctionString(1, patternCount, !returnsLoc)}(${action});`;
|
||||
} else {
|
||||
|
@ -700,6 +728,11 @@
|
|||
function() {
|
||||
return new MetaProperty(LOC(1)(new IdentifierLiteral($1)),
|
||||
LOC(3)(new Access($3)));
|
||||
}),
|
||||
o('IMPORT_META . Property',
|
||||
function() {
|
||||
return new MetaProperty(LOC(1)(new IdentifierLiteral($1)),
|
||||
LOC(3)(new Access($3)));
|
||||
})
|
||||
],
|
||||
// The general group of accessors into an object, by property, by prototype
|
||||
|
|
|
@ -226,6 +226,9 @@
|
|||
this.error(`'${prev[1]}' cannot be used as a keyword, or as a function call without parentheses`, prev[2]);
|
||||
} else if (prev[0] === '.' && this.tokens.length > 1 && (prevprev = this.tokens[this.tokens.length - 2])[0] === 'UNARY' && prevprev[1] === 'new') {
|
||||
prevprev[0] = 'NEW_TARGET';
|
||||
} else if (prev[0] === '.' && this.tokens.length > 1 && (prevprev = this.tokens[this.tokens.length - 2])[0] === 'IMPORT' && prevprev[1] === 'import') {
|
||||
this.seenImport = false;
|
||||
prevprev[0] = 'IMPORT_META';
|
||||
} else if (this.tokens.length > 2) {
|
||||
prevprev = this.tokens[this.tokens.length - 2];
|
||||
if (((ref11 = prev[0]) === '@' || ref11 === 'THIS') && prevprev && prevprev.spaced && /^[gs]et$/.test(prevprev[1]) && ((ref12 = this.tokens[this.tokens.length - 3][0]) !== '.' && ref12 !== '?.' && ref12 !== '@')) {
|
||||
|
|
|
@ -2325,6 +2325,10 @@
|
|||
} else {
|
||||
return this.error("the only valid meta property for new is new.target");
|
||||
}
|
||||
} else if (this.meta.value === 'import') {
|
||||
if (!(this.property instanceof Access && this.property.name.value === 'meta')) {
|
||||
return this.error("the only valid meta property for import is import.meta");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -34,6 +34,9 @@ o = (patternString, action, options) ->
|
|||
patternString = patternString.replace /\s{2,}/g, ' '
|
||||
patternCount = patternString.split(' ').length
|
||||
if action
|
||||
# This code block does string replacements in the generated `parser.js`
|
||||
# file, replacing the calls to the `LOC` function and other strings as
|
||||
# listed below.
|
||||
action = if match = unwrap.exec action then match[1] else "(#{action}())"
|
||||
|
||||
# All runtime functions we need are defined on `yy`
|
||||
|
@ -47,8 +50,33 @@ o = (patternString, action, options) ->
|
|||
getAddDataToNodeFunctionString = (first, last, forceUpdateLocation = yes) ->
|
||||
"yy.addDataToNode(yy, @#{first}, #{if first[0] is '$' then '$$' else '$'}#{first}, #{if last then "@#{last}, #{if last[0] is '$' then '$$' else '$'}#{last}" else 'null, null'}, #{if forceUpdateLocation then 'true' else 'false'})"
|
||||
|
||||
# This code replaces the calls to `LOC` with the `yy.addDataToNode` string
|
||||
# defined above. The `LOC` function, when used below in the grammar rules,
|
||||
# is used to make sure that newly created node class objects get correct
|
||||
# location data assigned to them. By default, the grammar will assign the
|
||||
# location data spanned by *all* of the tokens on the left (e.g. a string
|
||||
# such as `'Body TERMINATOR Line'`) to the “top-level” node returned by
|
||||
# the grammar rule (the function on the right). But for “inner” node class
|
||||
# objects created by grammar rules, they won’t get correct location data
|
||||
# assigned to them without adding `LOC`.
|
||||
|
||||
# For example, consider the grammar rule `'NEW_TARGET . Property'`, which
|
||||
# is handled by a function that returns
|
||||
# `new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3)`.
|
||||
# The `1` in `LOC(1)` refers to the first token (`NEW_TARGET`) and the `3`
|
||||
# in `LOC(3)` refers to the third token (`Property`). In order for the
|
||||
# `new IdentifierLiteral` to get assigned the location data corresponding
|
||||
# to `new` in the source code, we use
|
||||
# `LOC(1)(new IdentifierLiteral ...)` to mean “assign the location data of
|
||||
# the *first* token of this grammar rule (`NEW_TARGET`) to this
|
||||
# `new IdentifierLiteral`”. The `LOC(3)` means “assign the location data of
|
||||
# the *third* token of this grammar rule (`Property`) to this
|
||||
# `new Access`”.
|
||||
returnsLoc = /^LOC/.test action
|
||||
action = action.replace /LOC\(([0-9]*)\)/g, getAddDataToNodeFunctionString('$1')
|
||||
# A call to `LOC` with two arguments, e.g. `LOC(2,4)`, sets the location
|
||||
# data for the generated node on both of the referenced tokens (the second
|
||||
# and fourth in this example).
|
||||
action = action.replace /LOC\(([0-9]*),\s*([0-9]*)\)/g, getAddDataToNodeFunctionString('$1', '$2')
|
||||
performActionFunctionString = "$$ = #{getAddDataToNodeFunctionString(1, patternCount, not returnsLoc)}(#{action});"
|
||||
else
|
||||
|
@ -396,6 +424,7 @@ grammar =
|
|||
# A "meta-property" access e.g. `new.target`
|
||||
MetaProperty: [
|
||||
o 'NEW_TARGET . Property', -> new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3)
|
||||
o 'IMPORT_META . Property', -> new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3)
|
||||
]
|
||||
|
||||
# The general group of accessors into an object, by property, by prototype
|
||||
|
|
|
@ -214,6 +214,9 @@ exports.Lexer = class Lexer
|
|||
without parentheses", prev[2]
|
||||
else if prev[0] is '.' and @tokens.length > 1 and (prevprev = @tokens[@tokens.length - 2])[0] is 'UNARY' and prevprev[1] is 'new'
|
||||
prevprev[0] = 'NEW_TARGET'
|
||||
else if prev[0] is '.' and @tokens.length > 1 and (prevprev = @tokens[@tokens.length - 2])[0] is 'IMPORT' and prevprev[1] is 'import'
|
||||
@seenImport = no
|
||||
prevprev[0] = 'IMPORT_META'
|
||||
else if @tokens.length > 2
|
||||
prevprev = @tokens[@tokens.length - 2]
|
||||
if prev[0] in ['@', 'THIS'] and prevprev and prevprev.spaced and
|
||||
|
|
|
@ -1576,6 +1576,9 @@ exports.MetaProperty = class MetaProperty extends Base
|
|||
@error "new.target can only occur inside functions"
|
||||
else
|
||||
@error "the only valid meta property for new is new.target"
|
||||
else if @meta.value is 'import'
|
||||
unless @property instanceof Access and @property.name.value is 'meta'
|
||||
@error "the only valid meta property for import is import.meta"
|
||||
|
||||
compileNode: (o) ->
|
||||
@checkValid o
|
||||
|
|
|
@ -4041,7 +4041,7 @@ test "AST as expected for If node", ->
|
|||
inverted: no
|
||||
]
|
||||
|
||||
test "AST as expected for MetaProperty node", ->
|
||||
test "AST as expected for `new.target` MetaProperty node", ->
|
||||
testExpression '''
|
||||
-> new.target
|
||||
''',
|
||||
|
@ -4074,6 +4074,25 @@ test "AST as expected for MetaProperty node", ->
|
|||
computed: no
|
||||
]
|
||||
|
||||
test "AST as expected for `import.meta` MetaProperty node", ->
|
||||
testExpression '''
|
||||
import.meta
|
||||
''',
|
||||
type: 'MetaProperty'
|
||||
meta: ID 'import'
|
||||
property: ID 'meta'
|
||||
|
||||
testExpression '''
|
||||
import.meta.name
|
||||
''',
|
||||
type: 'MemberExpression'
|
||||
object:
|
||||
type: 'MetaProperty'
|
||||
meta: ID 'import'
|
||||
property: ID 'meta'
|
||||
property: ID 'name'
|
||||
computed: no
|
||||
|
||||
test "AST as expected for dynamic import", ->
|
||||
testExpression '''
|
||||
import('a')
|
||||
|
|
|
@ -6006,7 +6006,7 @@ test "AST as expected for While node", ->
|
|||
line: 3
|
||||
column: 5
|
||||
|
||||
test "AST location data as expected for MetaProperty node", ->
|
||||
test "AST location data as expected for `new.target` MetaProperty node", ->
|
||||
testAstLocationData '''
|
||||
-> new.target
|
||||
''',
|
||||
|
@ -6058,6 +6058,44 @@ test "AST location data as expected for MetaProperty node", ->
|
|||
line: 1
|
||||
column: 13
|
||||
|
||||
test "AST location data as expected for `import.meta` MetaProperty node", ->
|
||||
testAstLocationData '''
|
||||
import.meta
|
||||
''',
|
||||
type: 'MetaProperty'
|
||||
meta:
|
||||
start: 0
|
||||
end: 6
|
||||
range: [0, 6]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 1
|
||||
column: 6
|
||||
property:
|
||||
start: 7
|
||||
end: 11
|
||||
range: [7, 11]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 7
|
||||
end:
|
||||
line: 1
|
||||
column: 11
|
||||
start: 0
|
||||
end: 11
|
||||
range: [0, 11]
|
||||
loc:
|
||||
start:
|
||||
line: 1
|
||||
column: 0
|
||||
end:
|
||||
line: 1
|
||||
column: 11
|
||||
|
||||
test "AST location data as expected for For node", ->
|
||||
testAstLocationData 'for x, i in arr when x? then return',
|
||||
type: 'For'
|
||||
|
|
|
@ -1967,6 +1967,15 @@ test "`new.target` is only allowed meta property", ->
|
|||
^^^^^^^^^^^^^
|
||||
'''
|
||||
|
||||
test "`import.meta` is only allowed meta property", ->
|
||||
assertErrorFormat '''
|
||||
foo = import.something
|
||||
''', '''
|
||||
[stdin]:1:7: error: the only valid meta property for import is import.meta
|
||||
foo = import.something
|
||||
^^^^^^^^^^^^^^^^
|
||||
'''
|
||||
|
||||
test "`new.target` cannot be assigned", ->
|
||||
assertErrorFormat '''
|
||||
->
|
||||
|
|
|
@ -941,3 +941,53 @@ test "#4834: dynamic import", ->
|
|||
return bar = (await import('bar'));
|
||||
};
|
||||
"""
|
||||
|
||||
test "#5317: Support import.meta", ->
|
||||
eqJS """
|
||||
foo = import.meta
|
||||
""",
|
||||
"""
|
||||
var foo;
|
||||
|
||||
foo = import.meta;
|
||||
"""
|
||||
|
||||
eqJS """
|
||||
foo = import
|
||||
.meta
|
||||
""",
|
||||
"""
|
||||
var foo;
|
||||
|
||||
foo = import.meta;
|
||||
"""
|
||||
|
||||
eqJS """
|
||||
foo = import.
|
||||
meta
|
||||
""",
|
||||
"""
|
||||
var foo;
|
||||
|
||||
foo = import.meta;
|
||||
"""
|
||||
|
||||
eqJS """
|
||||
foo = import.meta.bar
|
||||
""",
|
||||
"""
|
||||
var foo;
|
||||
|
||||
foo = import.meta.bar;
|
||||
"""
|
||||
|
||||
eqJS """
|
||||
foo = import
|
||||
.meta
|
||||
.bar
|
||||
""",
|
||||
"""
|
||||
var foo;
|
||||
|
||||
foo = import.meta.bar;
|
||||
"""
|
||||
|
|
Loading…
Add table
Reference in a new issue