mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
Merge pull request #15 from GeoffreyBooth/value-ast-revisions-helpers
AST helpers revisions
This commit is contained in:
6
Cakefile
6
Cakefile
@@ -279,10 +279,7 @@ buildDocTests = (watch = no) ->
|
||||
outputFolder = "docs/v#{majorVersion}"
|
||||
|
||||
# Included in test.html
|
||||
readHelpersFile = (basename) ->
|
||||
fs.readFileSync("test/support/#{basename}.coffee", 'utf-8').replace /exports\./g, '@'
|
||||
testHelpers = readHelpersFile('helpers') + '\n'
|
||||
testHelpers += readHelpersFile('abstract_syntax_tree_helpers')
|
||||
testHelpers = fs.readFileSync('test/support/helpers.coffee', 'utf-8').replace /exports\./g, '@'
|
||||
|
||||
# Helpers
|
||||
testsInScriptBlocks = ->
|
||||
@@ -421,7 +418,6 @@ runTests = (CoffeeScript) ->
|
||||
onFail description, fn, err
|
||||
|
||||
helpers.extend global, require './test/support/helpers'
|
||||
helpers.extend global, require './test/support/abstract_syntax_tree_helpers'
|
||||
|
||||
# When all the tests have run, collect and print errors.
|
||||
# If a stacktrace is available, output the compiled function source.
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
// is added to the first parameter passed in, and the parameter is returned.
|
||||
// If the parameter is not a node, it will just be passed through unaffected.
|
||||
getAddDataToNodeFunctionString = function(first, last) {
|
||||
return `yy.addDataToNode(yy, {first: @${first}, ${last ? `last: @${last}, ` : ''}})`;
|
||||
return `yy.addDataToNode(yy, @${first}${last ? `, @${last}` : ''})`;
|
||||
};
|
||||
action = action.replace(/LOC\(([0-9]*)\)/g, getAddDataToNodeFunctionString('$1'));
|
||||
action = action.replace(/LOC\(([0-9]*),\s*([0-9]*)\)/g, getAddDataToNodeFunctionString('$1', '$2'));
|
||||
|
||||
@@ -199,14 +199,12 @@
|
||||
// This returns a function which takes an object as a parameter, and if that
|
||||
// object is an AST node, updates that object's locationData.
|
||||
// The object is returned either way.
|
||||
exports.addDataToNode = function(parserState, {first, last, forceUpdateLocation = true}) {
|
||||
exports.addDataToNode = function(parserState, first, last, forceUpdateLocation = true) {
|
||||
return function(obj) {
|
||||
var objHash, ref1;
|
||||
// Add location data.
|
||||
if (((obj != null ? obj.updateLocationDataIfMissing : void 0) != null) && (first != null)) {
|
||||
obj.updateLocationDataIfMissing(buildLocationData(first, last), {
|
||||
force: forceUpdateLocation
|
||||
});
|
||||
obj.updateLocationDataIfMissing(buildLocationData(first, last), forceUpdateLocation);
|
||||
}
|
||||
// Add comments, building the dictionary of token data if it hasn’t been
|
||||
// built yet.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// nodes are created as the result of actions in the [grammar](grammar.html),
|
||||
// but some are created by other nodes as a method of code generation. To convert
|
||||
// the syntax tree into a string of JavaScript code, call `compile()` on the root.
|
||||
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXTag, Call, Class, Code, CodeFragment, ComputedPropertyName, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncGlyph, HEREGEX_OMIT, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, Interpolation, JS_FORBIDDEN, LEADING_BLANK_LINE, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, SIMPLE_STRING_OMIT, STRING_OMIT, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TRAILING_BLANK_LINE, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, hasLineComments, indentInitial, isFunction, isLiteralArguments, isLiteralThis, isNumber, isPlainObject, isUnassignable, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
|
||||
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXTag, Call, Class, Code, CodeFragment, ComputedPropertyName, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncGlyph, HEREGEX_OMIT, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, Interpolation, JS_FORBIDDEN, LEADING_BLANK_LINE, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, SIMPLE_STRING_OMIT, STRING_OMIT, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TRAILING_BLANK_LINE, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isFunction, isLiteralArguments, isLiteralThis, isNumber, isPlainObject, isUnassignable, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
|
||||
indexOf = [].indexOf,
|
||||
splice = [].splice,
|
||||
slice1 = [].slice;
|
||||
@@ -491,7 +491,7 @@
|
||||
|
||||
// For this node and all descendents, set the location data to `locationData`
|
||||
// if the location data is not already set.
|
||||
updateLocationDataIfMissing(locationData, {force} = {}) {
|
||||
updateLocationDataIfMissing(locationData, force) {
|
||||
if (force) {
|
||||
this.forceUpdateLocation = true;
|
||||
}
|
||||
@@ -1746,17 +1746,18 @@
|
||||
ref1 = this.properties;
|
||||
for (propIndex = j = 0, len1 = ref1.length; j < len1; propIndex = ++j) {
|
||||
prop = ref1[propIndex];
|
||||
ret = mergeAstLocationData(Object.assign({
|
||||
ret = {
|
||||
type: 'MemberExpression',
|
||||
object: ret,
|
||||
property: prop.ast(),
|
||||
computed: prop instanceof Index || !(((ref2 = prop.name) != null ? ref2.unwrap() : void 0) instanceof PropertyName),
|
||||
optional: !!prop.soak,
|
||||
shorthand: !!prop.shorthand
|
||||
}, prop.astLocationData()), ret);
|
||||
if (propIndex === 0 && this.base instanceof Parens && (this.base.locationData != null)) {
|
||||
mergeAstLocationData(ret, this.base.astLocationData());
|
||||
}
|
||||
};
|
||||
// When the `Value` has properties, the location data of a `MemberExpression` AST node
|
||||
// corresponding to a given property should span the location of the `Value`'s `base`
|
||||
// (including parens if present) through the property's location
|
||||
Object.assign(ret, mergeAstLocationData(this.base.astLocationData(), prop.astLocationData()));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -6298,36 +6299,42 @@
|
||||
return `${options.delimiter}${body}${options.delimiter}`;
|
||||
};
|
||||
|
||||
// Extends the location data of an AST node to include the location data from
|
||||
// another AST node.
|
||||
mergeAstLocationData = function(intoNode, fromNode) {
|
||||
var fromRange, intoRange;
|
||||
({
|
||||
range: intoRange
|
||||
} = intoNode);
|
||||
({
|
||||
range: fromRange
|
||||
} = fromNode);
|
||||
if (!(intoRange && fromRange)) {
|
||||
return intoNode;
|
||||
// Take two AST nodes, or two AST nodes’ location data objects, and return a new
|
||||
// location data object that encompasses the location data of both nodes. So the
|
||||
// new `start` value will be the earlier of the two nodes’ `start` values, the
|
||||
// new `end` value will be the later of the two nodes’ `end` values, etc.
|
||||
lesser = function(a, b) {
|
||||
if (a < b) {
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
if (fromRange[0] < intoRange[0]) {
|
||||
intoNode.range = intoRange = [fromRange[0], intoRange[1]];
|
||||
intoNode.start = fromNode.start;
|
||||
intoNode.loc = {
|
||||
start: fromNode.loc.start,
|
||||
end: intoNode.loc.end
|
||||
};
|
||||
};
|
||||
|
||||
greater = function(a, b) {
|
||||
if (a > b) {
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
if (fromRange[1] > intoRange[1]) {
|
||||
intoNode.range = [intoRange[0], fromRange[1]];
|
||||
intoNode.end = fromNode.end;
|
||||
intoNode.loc = {
|
||||
start: intoNode.loc.start,
|
||||
end: fromNode.loc.end
|
||||
};
|
||||
}
|
||||
return intoNode;
|
||||
};
|
||||
|
||||
mergeAstLocationData = function(nodeA, nodeB) {
|
||||
return {
|
||||
loc: {
|
||||
start: {
|
||||
line: lesser(nodeA.loc.start.line, nodeB.loc.start.line),
|
||||
column: lesser(nodeA.loc.start.column, nodeB.loc.start.column)
|
||||
},
|
||||
end: {
|
||||
line: greater(nodeA.loc.end.line, nodeB.loc.end.line),
|
||||
column: greater(nodeA.loc.end.column, nodeB.loc.end.column)
|
||||
}
|
||||
},
|
||||
range: [lesser(nodeA.range[0], nodeB.range[0]), greater(nodeA.range[1], nodeB.range[1])],
|
||||
start: lesser(nodeA.start, nodeB.start),
|
||||
end: greater(nodeA.end, nodeB.end)
|
||||
};
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -45,7 +45,7 @@ o = (patternString, action, options) ->
|
||||
# is added to the first parameter passed in, and the parameter is returned.
|
||||
# If the parameter is not a node, it will just be passed through unaffected.
|
||||
getAddDataToNodeFunctionString = (first, last) ->
|
||||
"yy.addDataToNode(yy, {first: @#{first}, #{if last then "last: @#{last}, " else ''}})"
|
||||
"yy.addDataToNode(yy, @#{first}#{if last then ", @#{last}" else ''})"
|
||||
|
||||
action = action.replace /LOC\(([0-9]*)\)/g, getAddDataToNodeFunctionString('$1')
|
||||
action = action.replace /LOC\(([0-9]*),\s*([0-9]*)\)/g, getAddDataToNodeFunctionString('$1', '$2')
|
||||
|
||||
@@ -136,11 +136,11 @@ buildTokenDataDictionary = (parserState) ->
|
||||
# This returns a function which takes an object as a parameter, and if that
|
||||
# object is an AST node, updates that object's locationData.
|
||||
# The object is returned either way.
|
||||
exports.addDataToNode = (parserState, {first, last, forceUpdateLocation = yes}) ->
|
||||
exports.addDataToNode = (parserState, first, last, forceUpdateLocation = yes) ->
|
||||
(obj) ->
|
||||
# Add location data.
|
||||
if obj?.updateLocationDataIfMissing? and first?
|
||||
obj.updateLocationDataIfMissing buildLocationData(first, last), force: forceUpdateLocation
|
||||
obj.updateLocationDataIfMissing buildLocationData(first, last), forceUpdateLocation
|
||||
|
||||
# Add comments, building the dictionary of token data if it hasn’t been
|
||||
# built yet.
|
||||
|
||||
@@ -294,17 +294,17 @@ exports.Base = class Base
|
||||
return
|
||||
loc:
|
||||
start:
|
||||
line: first_line + 1
|
||||
line: first_line + 1
|
||||
column: first_column
|
||||
end:
|
||||
line: last_line + 1
|
||||
line: last_line + 1
|
||||
column: last_column + 1
|
||||
range: [
|
||||
range[0]
|
||||
range[1]
|
||||
]
|
||||
start: range[0]
|
||||
end: range[1]
|
||||
end: range[1]
|
||||
|
||||
# Passes each child to a function, breaking when the function returns `false`.
|
||||
eachChild: (func) ->
|
||||
@@ -398,7 +398,7 @@ exports.Base = class Base
|
||||
|
||||
# For this node and all descendents, set the location data to `locationData`
|
||||
# if the location data is not already set.
|
||||
updateLocationDataIfMissing: (locationData, {force} = {}) ->
|
||||
updateLocationDataIfMissing: (locationData, force) ->
|
||||
@forceUpdateLocation = yes if force
|
||||
return this if @locationData and not @forceUpdateLocation
|
||||
delete @forceUpdateLocation
|
||||
@@ -1154,19 +1154,16 @@ exports.Value = class Value extends Base
|
||||
ret = @base.ast()
|
||||
for prop, propIndex in @properties
|
||||
ret =
|
||||
mergeAstLocationData(
|
||||
Object.assign {
|
||||
type: 'MemberExpression'
|
||||
object: ret
|
||||
property: prop.ast()
|
||||
computed: prop instanceof Index or prop.name?.unwrap() not instanceof PropertyName
|
||||
optional: !!prop.soak
|
||||
shorthand: !!prop.shorthand
|
||||
}, prop.astLocationData()
|
||||
ret
|
||||
)
|
||||
if propIndex is 0 and @base instanceof Parens and @base.locationData?
|
||||
mergeAstLocationData ret, @base.astLocationData()
|
||||
type: 'MemberExpression'
|
||||
object: ret
|
||||
property: prop.ast()
|
||||
computed: prop instanceof Index or prop.name?.unwrap() not instanceof PropertyName
|
||||
optional: !!prop.soak
|
||||
shorthand: !!prop.shorthand
|
||||
# When the `Value` has properties, the location data of a `MemberExpression` AST node
|
||||
# corresponding to a given property should span the location of the `Value`'s `base`
|
||||
# (including parens if present) through the property's location
|
||||
Object.assign ret, mergeAstLocationData(@base.astLocationData(), prop.astLocationData())
|
||||
ret
|
||||
|
||||
checkNewTarget: (o) ->
|
||||
@@ -4166,28 +4163,24 @@ makeDelimitedLiteral = (body, options = {}) ->
|
||||
when other then (if options.double then "\\#{other}" else other)
|
||||
"#{options.delimiter}#{body}#{options.delimiter}"
|
||||
|
||||
# Extends the location data of an AST node to include the location data from
|
||||
# another AST node.
|
||||
mergeAstLocationData = (intoNode, fromNode) ->
|
||||
{range: intoRange} = intoNode
|
||||
{range: fromRange} = fromNode
|
||||
return intoNode unless intoRange and fromRange
|
||||
if fromRange[0] < intoRange[0]
|
||||
intoNode.range = intoRange = [
|
||||
fromRange[0]
|
||||
intoRange[1]
|
||||
# Take two AST nodes, or two AST nodes’ location data objects, and return a new
|
||||
# location data object that encompasses the location data of both nodes. So the
|
||||
# new `start` value will be the earlier of the two nodes’ `start` values, the
|
||||
# new `end` value will be the later of the two nodes’ `end` values, etc.
|
||||
lesser = (a, b) -> if a < b then a else b
|
||||
greater = (a, b) -> if a > b then a else b
|
||||
mergeAstLocationData = (nodeA, nodeB) ->
|
||||
return
|
||||
loc:
|
||||
start:
|
||||
line: lesser nodeA.loc.start.line, nodeB.loc.start.line
|
||||
column: lesser nodeA.loc.start.column, nodeB.loc.start.column
|
||||
end:
|
||||
line: greater nodeA.loc.end.line, nodeB.loc.end.line
|
||||
column: greater nodeA.loc.end.column, nodeB.loc.end.column
|
||||
range: [
|
||||
lesser nodeA.range[0], nodeB.range[0]
|
||||
greater nodeA.range[1], nodeB.range[1]
|
||||
]
|
||||
intoNode.start = fromNode.start
|
||||
intoNode.loc =
|
||||
start: fromNode.loc.start
|
||||
end: intoNode.loc.end
|
||||
if fromRange[1] > intoRange[1]
|
||||
intoNode.range = [
|
||||
intoRange[0]
|
||||
fromRange[1]
|
||||
]
|
||||
intoNode.end = fromNode.end
|
||||
intoNode.loc =
|
||||
start: intoNode.loc.start
|
||||
end: fromNode.loc.end
|
||||
intoNode
|
||||
start: lesser nodeA.start, nodeB.start
|
||||
end: greater nodeA.end, nodeB.end
|
||||
|
||||
@@ -4,37 +4,41 @@
|
||||
# Recursively compare all values of enumerable properties of `expected` with
|
||||
# those of `actual`. Use `looseArray` helper function to skip array length
|
||||
# comparison.
|
||||
deepStrictEqualExpectedProperties = (actual, expected) ->
|
||||
white = (text, values...) -> (text[i] + "#{reset}#{value}#{red}" for value, i in values).join('') + text[i]
|
||||
deepStrictIncludeExpectedProperties = (actual, expected) ->
|
||||
eq actual.length, expected.length if expected instanceof Array and not expected.loose
|
||||
for key, val of expected
|
||||
if 'object' is typeof val
|
||||
fail white"Property #{key} expected, but was missing" unless actual[key]
|
||||
deepStrictEqualExpectedProperties actual[key], val
|
||||
fail "Property #{reset}#{key}#{red} expected, but was missing" unless actual[key]
|
||||
deepStrictIncludeExpectedProperties actual[key], val
|
||||
else
|
||||
eq actual[key], val, white"Property #{key}: expected #{actual[key]} to equal #{val}"
|
||||
eq actual[key], val, """
|
||||
Property #{reset}#{key}#{red}: expected #{reset}#{actual[key]}#{red} to equal #{reset}#{val}#{red}
|
||||
Expected AST output to include:
|
||||
#{reset}#{inspect expected}#{red}
|
||||
but instead it was:
|
||||
#{reset}#{inspect actual}#{red}
|
||||
"""
|
||||
actual
|
||||
|
||||
testExpression = (code, expected) ->
|
||||
ast = getExpressionAst code
|
||||
if expected?
|
||||
deepStrictEqualExpectedProperties ast, expected
|
||||
else
|
||||
# Convenience for creating new tests; call `testExpression` with no second
|
||||
# parameter to see what the current AST generation is for your input code.
|
||||
console.log require('util').inspect ast,
|
||||
depth: 10
|
||||
colors: yes
|
||||
|
||||
# Flag array for loose comparision. See reference to `.loose` in
|
||||
# `deepStrictEqualExpectedProperties` above.
|
||||
# Flag array for loose comparison. See reference to `.loose` in
|
||||
# `deepStrictIncludeExpectedProperties` above.
|
||||
looseArray = (arr) ->
|
||||
Object.defineProperty arr, 'loose',
|
||||
value: yes
|
||||
enumerable: no
|
||||
arr
|
||||
|
||||
test 'Confirm functionality of `deepStrictEqualExpectedProperties`', ->
|
||||
testExpression = (code, expected) ->
|
||||
ast = getAstExpression code
|
||||
if expected?
|
||||
deepStrictIncludeExpectedProperties ast, expected
|
||||
else
|
||||
# Convenience for creating new tests; call `testExpression` with no second
|
||||
# parameter to see what the current AST generation is for your input code.
|
||||
console.log inspect ast
|
||||
|
||||
|
||||
test 'Confirm functionality of `deepStrictIncludeExpectedProperties`', ->
|
||||
actual =
|
||||
name: 'Name'
|
||||
a:
|
||||
@@ -43,7 +47,7 @@ test 'Confirm functionality of `deepStrictEqualExpectedProperties`', ->
|
||||
x: [1, 2, 3]
|
||||
|
||||
check = (message, test, expected) ->
|
||||
test (-> deepStrictEqualExpectedProperties actual, expected), message
|
||||
test (-> deepStrictIncludeExpectedProperties actual, expected), message
|
||||
|
||||
check 'Expected property does not match', throws,
|
||||
name: '"Name"'
|
||||
@@ -88,7 +92,7 @@ test 'Confirm functionality of `deepStrictEqualExpectedProperties`', ->
|
||||
# properties are as expected.
|
||||
|
||||
test "AST as expected for Block node", ->
|
||||
deepStrictEqualExpectedProperties CoffeeScript.compile('return', ast: yes),
|
||||
deepStrictIncludeExpectedProperties CoffeeScript.compile('return', ast: yes),
|
||||
type: 'Block'
|
||||
expressions: [
|
||||
type: 'Return'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# ---------------------------------
|
||||
|
||||
testAstLocationData = (code, expected) ->
|
||||
testAstNodeLocationData getExpressionAst(code), expected
|
||||
testAstNodeLocationData getAstExpression(code), expected
|
||||
|
||||
testAstNodeLocationData = (node, expected, path = '') ->
|
||||
extendPath = (additionalPath) ->
|
||||
@@ -16,10 +16,9 @@ testAstNodeLocationData = (node, expected, path = '') ->
|
||||
for expectedItem, index in expectedChild
|
||||
testAstNodeLocationData node[key][index], expectedItem, extendPath "#{key}[#{index}]"
|
||||
else if typeof expectedChild is 'object'
|
||||
testAstNodeLocationData node[key], expectedChild, extendPath key
|
||||
testAstNodeLocationData node[key], expectedChild, extendPath(key)
|
||||
|
||||
testSingleNodeLocationData = (node, expected, path) ->
|
||||
pathStr = if path then " at '#{path}'" else ''
|
||||
testSingleNodeLocationData = (node, expected, path = '') ->
|
||||
# Even though it’s not part of the location data, check the type to ensure
|
||||
# that we’re testing the node we think we are.
|
||||
if expected.type?
|
||||
@@ -27,19 +26,20 @@ testSingleNodeLocationData = (node, expected, path) ->
|
||||
"Expected AST node type #{reset}#{node.type}#{red} to equal #{reset}#{expected.type}#{red}"
|
||||
|
||||
eq node.start, expected.start, \
|
||||
"Expected location start #{reset}#{node.start}#{red} to equal #{reset}#{expected.start}#{red}#{pathStr}"
|
||||
"Expected #{path}.start: #{reset}#{node.start}#{red} to equal #{reset}#{expected.start}#{red}"
|
||||
eq node.end, expected.end, \
|
||||
"Expected location end #{reset}#{node.end}#{red} to equal #{reset}#{expected.end}#{red}#{pathStr}"
|
||||
"Expected #{path}.end: #{reset}#{node.end}#{red} to equal #{reset}#{expected.end}#{red}"
|
||||
arrayEq node.range, expected.range, \
|
||||
"Expected location range #{reset}#{JSON.stringify node.range}#{red} to equal #{reset}#{JSON.stringify expected.range}#{red}#{pathStr}"
|
||||
"Expected #{path}.range: #{reset}#{JSON.stringify node.range}#{red} to equal #{reset}#{JSON.stringify expected.range}#{red}"
|
||||
eq node.loc.start.line, expected.loc.start.line, \
|
||||
"Expected location start line #{reset}#{node.loc.start.line}#{red} to equal #{reset}#{expected.loc.start.line}#{red}#{pathStr}"
|
||||
"Expected #{path}.loc.start.line: #{reset}#{node.loc.start.line}#{red} to equal #{reset}#{expected.loc.start.line}#{red}"
|
||||
eq node.loc.start.column, expected.loc.start.column, \
|
||||
"Expected location start column #{reset}#{node.loc.start.column}#{red} to equal #{reset}#{expected.loc.start.column}#{red}#{pathStr}"
|
||||
"Expected #{path}.loc.start.column: #{reset}#{node.loc.start.column}#{red} to equal #{reset}#{expected.loc.start.column}#{red}"
|
||||
eq node.loc.end.line, expected.loc.end.line, \
|
||||
"Expected location end line #{reset}#{node.loc.end.line}#{red} to equal #{reset}#{expected.loc.end.line}#{red}#{pathStr}"
|
||||
"Expected #{path}.loc.end.line: #{reset}#{node.loc.end.line}#{red} to equal #{reset}#{expected.loc.end.line}#{red}"
|
||||
eq node.loc.end.column, expected.loc.end.column, \
|
||||
"Expected location end column #{reset}#{node.loc.end.column}#{red} to equal #{reset}#{expected.loc.end.column}#{red}#{pathStr}"
|
||||
"Expected #{path}.loc.end.column: #{reset}#{node.loc.end.column}#{red} to equal #{reset}#{expected.loc.end.column}#{red}"
|
||||
|
||||
|
||||
test "AST location data as expected for NumberLiteral node", ->
|
||||
testAstLocationData '42',
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# Helpers to get AST nodes for a string of code. The root node is always a
|
||||
# `Block` node, so for brevity in the tests return its children from
|
||||
# `expressions`.
|
||||
getAstExpressions = (code) ->
|
||||
ast = CoffeeScript.compile code, ast: yes
|
||||
ast.expressions
|
||||
|
||||
exports.getExpressionAst = (code) -> getAstExpressions(code)[0]
|
||||
@@ -38,3 +38,21 @@ exports.eqJS = (input, expectedOutput, msg) ->
|
||||
ok egal(expectedOutput, actualOutput), msg or diffOutput expectedOutput, actualOutput
|
||||
|
||||
exports.isWindows = -> process.platform is 'win32'
|
||||
|
||||
exports.inspect = (obj) ->
|
||||
if global.testingBrowser
|
||||
JSON.stringify obj, null, 2
|
||||
else
|
||||
require('util').inspect obj,
|
||||
depth: 10
|
||||
colors: if process.env.NODE_DISABLE_COLORS then no else yes
|
||||
|
||||
# Helpers to get AST nodes for a string of code. The root node is always a
|
||||
# `Block` node, so for brevity in the tests return its children from
|
||||
# `expressions`.
|
||||
exports.getAstExpressions = (code) ->
|
||||
ast = CoffeeScript.compile code, ast: yes
|
||||
ast.expressions
|
||||
|
||||
# Many tests want just the root node.
|
||||
exports.getAstExpression = (code) -> getAstExpressions(code)[0]
|
||||
|
||||
Reference in New Issue
Block a user