on master help sign in
Add CoffeeScript syntax test file
commitc9627040ccc74919af59a7602decf7797ef01085 authorMohamed Abdelnour <mohamed.k.abdelnour@gmail.com> dateSat 29 May 2021, 02:21 (5 years ago) parentb1f6943
3 files changed+808
tests/syntax-tests/highlighted/CoffeeScript/coffeescript.coffee+391 tests/syntax-tests/source/CoffeeScript/LICENSE.md+26 tests/syntax-tests/source/CoffeeScript/coffeescript.coffee+391
tests/syntax-tests/highlighted/CoffeeScript/coffeescript.coffeenew file+391
@@ -0,0 +1,391 @@
1+# CoffeeScript can be used both on the server, as a command-line compiler based
2+# on Node.js/V8, or to run CoffeeScript directly in the browser. This module
3+# contains the main entry functions for tokenizing, parsing, and compiling
4+# source CoffeeScript into JavaScript.
5+
6+{Lexer} = require './lexer'
7+{parser} = require './parser'
8+helpers = require './helpers'
9+SourceMap = require './sourcemap'
10+# Require `package.json`, which is two levels above this file, as this file is
11+# evaluated from `lib/coffeescript`.
12+packageJson = require '../../package.json'
13+
14+# The current CoffeeScript version number.
15+exports.VERSION = packageJson.version
16+
17+exports.FILE_EXTENSIONS = FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md']
18+
19+# Expose helpers for testing.
20+exports.helpers = helpers
21+
22+# Function that allows for btoa in both nodejs and the browser.
23+base64encode = (src) -> switch
24+ when typeof Buffer is 'function'
25+ Buffer.from(src).toString('base64')
26+ when typeof btoa is 'function'
27+ # The contents of a `<script>` block are encoded via UTF-16, so if any extended
28+ # characters are used in the block, btoa will fail as it maxes out at UTF-8.
29+ # See https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
30+ # for the gory details, and for the solution implemented here.
31+ btoa encodeURIComponent(src).replace /%([0-9A-F]{2})/g, (match, p1) ->
32+ String.fromCharCode '0x' + p1
33+ else
34+ throw new Error('Unable to base64 encode inline sourcemap.')
35+
36+# Function wrapper to add source file information to SyntaxErrors thrown by the
37+# lexer/parser/compiler.
38+withPrettyErrors = (fn) ->
39+ (code, options = {}) ->
40+ try
41+ fn.call @, code, options
42+ catch err
43+ throw err if typeof code isnt 'string' # Support `CoffeeScript.nodes(tokens)`.
44+ throw helpers.updateSyntaxError err, code, options.filename
45+
46+# For each compiled file, save its source in memory in case we need to
47+# recompile it later. We might need to recompile if the first compilation
48+# didn’t create a source map (faster) but something went wrong and we need
49+# a stack trace. Assuming that most of the time, code isn’t throwing
50+# exceptions, it’s probably more efficient to compile twice only when we
51+# need a stack trace, rather than always generating a source map even when
52+# it’s not likely to be used. Save in form of `filename`: [`(source)`]
53+sources = {}
54+# Also save source maps if generated, in form of `(source)`: [`(source map)`].
55+sourceMaps = {}
56+
57+# This is exported to enable an external module to implement caching of
58+# compilation results. When the compiled js source is loaded from cache, the
59+# original coffee code should be added with this method in order to enable the
60+# Error.prepareStackTrace below to correctly adjust the stack trace for the
61+# corresponding file (the source map will be generated on demand).
62+exports.registerCompiled = registerCompiled = (filename, source, sourcemap) ->
63+
64+ sources[filename] ?= []
65+ sources[filename].push source
66+
67+ if sourcemap?
68+ sourceMaps[filename] ?= []
69+ sourceMaps[filename].push sourcemap
70+
71+# Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
72+#
73+# If `options.sourceMap` is specified, then `options.filename` must also be
74+# specified. All options that can be passed to `SourceMap#generate` may also
75+# be passed here.
76+#
77+# This returns a javascript string, unless `options.sourceMap` is passed,
78+# in which case this returns a `{js, v3SourceMap, sourceMap}`
79+# object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for
80+# doing programmatic lookups.
81+exports.compile = compile = withPrettyErrors (code, options = {}) ->
82+ # Clone `options`, to avoid mutating the `options` object passed in.
83+ options = Object.assign {}, options
84+ # Always generate a source map if no filename is passed in, since without a
85+ # a filename we have no way to retrieve this source later in the event that
86+ # we need to recompile it to get a source map for `prepareStackTrace`.
87+ generateSourceMap = options.sourceMap or options.inlineMap or not options.filename?
88+ filename = options.filename or '<anonymous>'
89+
90+ checkShebangLine filename, code
91+
92+ map = new SourceMap if generateSourceMap
93+
94+ tokens = lexer.tokenize code, options
95+
96+ # Pass a list of referenced variables, so that generated variables won’t get
97+ # the same name.
98+ options.referencedVars = (
99+ token[1] for token in tokens when token[0] is 'IDENTIFIER'
100+ )
101+
102+ # Check for import or export; if found, force bare mode.
103+ unless options.bare? and options.bare is yes
104+ for token in tokens
105+ if token[0] in ['IMPORT', 'EXPORT']
106+ options.bare = yes
107+ break
108+
109+ nodes = parser.parse tokens
110+ # If all that was requested was a POJO representation of the nodes, e.g.
111+ # the abstract syntax tree (AST), we can stop now and just return that
112+ # (after fixing the location data for the root/`File`»`Program` node,
113+ # which might’ve gotten misaligned from the original source due to the
114+ # `clean` function in the lexer).
115+ if options.ast
116+ nodes.allCommentTokens = helpers.extractAllCommentTokens tokens
117+ sourceCodeNumberOfLines = (code.match(/\r?\n/g) or '').length + 1
118+ sourceCodeLastLine = /.*$/.exec(code)[0] # `.*` matches all but line break characters.
119+ ast = nodes.ast options
120+ range = [0, code.length]
121+ ast.start = ast.program.start = range[0]
122+ ast.end = ast.program.end = range[1]
123+ ast.range = ast.program.range = range
124+ ast.loc.start = ast.program.loc.start = {line: 1, column: 0}
125+ ast.loc.end.line = ast.program.loc.end.line = sourceCodeNumberOfLines
126+ ast.loc.end.column = ast.program.loc.end.column = sourceCodeLastLine.length
127+ ast.tokens = tokens
128+ return ast
129+
130+ fragments = nodes.compileToFragments options
131+
132+ currentLine = 0
133+ currentLine += 1 if options.header
134+ currentLine += 1 if options.shiftLine
135+ currentColumn = 0
136+ js = ""
137+ for fragment in fragments
138+ # Update the sourcemap with data from each fragment.
139+ if generateSourceMap
140+ # Do not include empty, whitespace, or semicolon-only fragments.
141+ if fragment.locationData and not /^[;\s]*$/.test fragment.code
142+ map.add(
143+ [fragment.locationData.first_line, fragment.locationData.first_column]
144+ [currentLine, currentColumn]
145+ {noReplace: true})
146+ newLines = helpers.count fragment.code, "\n"
147+ currentLine += newLines
148+ if newLines
149+ currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1)
150+ else
151+ currentColumn += fragment.code.length
152+
153+ # Copy the code from each fragment into the final JavaScript.
154+ js += fragment.code
155+
156+ if options.header
157+ header = "Generated by CoffeeScript #{@VERSION}"
158+ js = "// #{header}\n#{js}"
159+
160+ if generateSourceMap
161+ v3SourceMap = map.generate options, code
162+
163+ if options.transpile
164+ if typeof options.transpile isnt 'object'
165+ # This only happens if run via the Node API and `transpile` is set to
166+ # something other than an object.
167+ throw new Error 'The transpile option must be given an object with options to pass to Babel'
168+
169+ # Get the reference to Babel that we have been passed if this compiler
170+ # is run via the CLI or Node API.
171+ transpiler = options.transpile.transpile
172+ delete options.transpile.transpile
173+
174+ transpilerOptions = Object.assign {}, options.transpile
175+
176+ # See https://github.com/babel/babel/issues/827#issuecomment-77573107:
177+ # Babel can take a v3 source map object as input in `inputSourceMap`
178+ # and it will return an *updated* v3 source map object in its output.
179+ if v3SourceMap and not transpilerOptions.inputSourceMap?
180+ transpilerOptions.inputSourceMap = v3SourceMap
181+ transpilerOutput = transpiler js, transpilerOptions
182+ js = transpilerOutput.code
183+ if v3SourceMap and transpilerOutput.map
184+ v3SourceMap = transpilerOutput.map
185+
186+ if options.inlineMap
187+ encoded = base64encode JSON.stringify v3SourceMap
188+ sourceMapDataURI = "//# sourceMappingURL=data:application/json;base64,#{encoded}"
189+ sourceURL = "//# sourceURL=#{options.filename ? 'coffeescript'}"
190+ js = "#{js}\n#{sourceMapDataURI}\n#{sourceURL}"
191+
192+ registerCompiled filename, code, map
193+
194+ if options.sourceMap
195+ {
196+ js
197+ sourceMap: map
198+ v3SourceMap: JSON.stringify v3SourceMap, null, 2
199+ }
200+ else
201+ js
202+
203+# Tokenize a string of CoffeeScript code, and return the array of tokens.
204+exports.tokens = withPrettyErrors (code, options) ->
205+ lexer.tokenize code, options
206+
207+# Parse a string of CoffeeScript code or an array of lexed tokens, and
208+# return the AST. You can then compile it by calling `.compile()` on the root,
209+# or traverse it by using `.traverseChildren()` with a callback.
210+exports.nodes = withPrettyErrors (source, options) ->
211+ source = lexer.tokenize source, options if typeof source is 'string'
212+ parser.parse source
213+
214+# This file used to export these methods; leave stubs that throw warnings
215+# instead. These methods have been moved into `index.coffee` to provide
216+# separate entrypoints for Node and non-Node environments, so that static
217+# analysis tools don’t choke on Node packages when compiling for a non-Node
218+# environment.
219+exports.run = exports.eval = exports.register = ->
220+ throw new Error 'require index.coffee, not this file'
221+
222+# Instantiate a Lexer for our use here.
223+lexer = new Lexer
224+
225+# The real Lexer produces a generic stream of tokens. This object provides a
226+# thin wrapper around it, compatible with the Jison API. We can then pass it
227+# directly as a “Jison lexer.”
228+parser.lexer =
229+ yylloc:
230+ range: []
231+ options:
232+ ranges: yes
233+ lex: ->
234+ token = parser.tokens[@pos++]
235+ if token
236+ [tag, @yytext, @yylloc] = token
237+ parser.errorToken = token.origin or token
238+ @yylineno = @yylloc.first_line
239+ else
240+ tag = ''
241+ tag
242+ setInput: (tokens) ->
243+ parser.tokens = tokens
244+ @pos = 0
245+ upcomingInput: -> ''
246+
247+# Make all the AST nodes visible to the parser.
248+parser.yy = require './nodes'
249+
250+# Override Jison's default error handling function.
251+parser.yy.parseError = (message, {token}) ->
252+ # Disregard Jison's message, it contains redundant line number information.
253+ # Disregard the token, we take its value directly from the lexer in case
254+ # the error is caused by a generated token which might refer to its origin.
255+ {errorToken, tokens} = parser
256+ [errorTag, errorText, errorLoc] = errorToken
257+
258+ errorText = switch
259+ when errorToken is tokens[tokens.length - 1]
260+ 'end of input'
261+ when errorTag in ['INDENT', 'OUTDENT']
262+ 'indentation'
263+ when errorTag in ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START']
264+ errorTag.replace(/_START$/, '').toLowerCase()
265+ else
266+ helpers.nameWhitespaceCharacter errorText
267+
268+ # The second argument has a `loc` property, which should have the location
269+ # data for this token. Unfortunately, Jison seems to send an outdated `loc`
270+ # (from the previous token), so we take the location information directly
271+ # from the lexer.
272+ helpers.throwSyntaxError "unexpected #{errorText}", errorLoc
273+
274+# Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js
275+# Modified to handle sourceMap
276+formatSourcePosition = (frame, getSourceMapping) ->
277+ filename = undefined
278+ fileLocation = ''
279+
280+ if frame.isNative()
281+ fileLocation = "native"
282+ else
283+ if frame.isEval()
284+ filename = frame.getScriptNameOrSourceURL()
285+ fileLocation = "#{frame.getEvalOrigin()}, " unless filename
286+ else
287+ filename = frame.getFileName()
288+
289+ filename or= "<anonymous>"
290+
291+ line = frame.getLineNumber()
292+ column = frame.getColumnNumber()
293+
294+ # Check for a sourceMap position
295+ source = getSourceMapping filename, line, column
296+ fileLocation =
297+ if source
298+ "#{filename}:#{source[0]}:#{source[1]}"
299+ else
300+ "#{filename}:#{line}:#{column}"
301+
302+ functionName = frame.getFunctionName()
303+ isConstructor = frame.isConstructor()
304+ isMethodCall = not (frame.isToplevel() or isConstructor)
305+
306+ if isMethodCall
307+ methodName = frame.getMethodName()
308+ typeName = frame.getTypeName()
309+
310+ if functionName
311+ tp = as = ''
312+ if typeName and functionName.indexOf typeName
313+ tp = "#{typeName}."
314+ if methodName and functionName.indexOf(".#{methodName}") isnt functionName.length - methodName.length - 1
315+ as = " [as #{methodName}]"
316+
317+ "#{tp}#{functionName}#{as} (#{fileLocation})"
318+ else
319+ "#{typeName}.#{methodName or '<anonymous>'} (#{fileLocation})"
320+ else if isConstructor
321+ "new #{functionName or '<anonymous>'} (#{fileLocation})"
322+ else if functionName
323+ "#{functionName} (#{fileLocation})"
324+ else
325+ fileLocation
326+
327+getSourceMap = (filename, line, column) ->
328+ # Skip files that we didn’t compile, like Node system files that appear in
329+ # the stack trace, as they never have source maps.
330+ return null unless filename is '<anonymous>' or filename.slice(filename.lastIndexOf('.')) in FILE_EXTENSIONS
331+
332+ if filename isnt '<anonymous>' and sourceMaps[filename]?
333+ return sourceMaps[filename][sourceMaps[filename].length - 1]
334+ # CoffeeScript compiled in a browser or via `CoffeeScript.compile` or `.run`
335+ # may get compiled with `options.filename` that’s missing, which becomes
336+ # `<anonymous>`; but the runtime might request the stack trace with the
337+ # filename of the script file. See if we have a source map cached under
338+ # `<anonymous>` that matches the error.
339+ else if sourceMaps['<anonymous>']?
340+ # Work backwards from the most recent anonymous source maps, until we find
341+ # one that works. This isn’t foolproof; there is a chance that multiple
342+ # source maps will have line/column pairs that match. But we have no other
343+ # way to match them. `frame.getFunction().toString()` doesn’t always work,
344+ # and it’s not foolproof either.
345+ for map in sourceMaps['<anonymous>'] by -1
346+ sourceLocation = map.sourceLocation [line - 1, column - 1]
347+ return map if sourceLocation?[0]? and sourceLocation[1]?
348+
349+ # If all else fails, recompile this source to get a source map. We need the
350+ # previous section (for `<anonymous>`) despite this option, because after it
351+ # gets compiled we will still need to look it up from
352+ # `sourceMaps['<anonymous>']` in order to find and return it. That’s why we
353+ # start searching from the end in the previous block, because most of the
354+ # time the source map we want is the last one.
355+ if sources[filename]?
356+ answer = compile sources[filename][sources[filename].length - 1],
357+ filename: filename
358+ sourceMap: yes
359+ literate: helpers.isLiterate filename
360+ answer.sourceMap
361+ else
362+ null
363+
364+# Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p)
365+# NodeJS / V8 have no support for transforming positions in stack traces using
366+# sourceMap, so we must monkey-patch Error to display CoffeeScript source
367+# positions.
368+Error.prepareStackTrace = (err, stack) ->
369+ getSourceMapping = (filename, line, column) ->
370+ sourceMap = getSourceMap filename, line, column
371+ answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap?
372+ if answer? then [answer[0] + 1, answer[1] + 1] else null
373+
374+ frames = for frame in stack
375+ break if frame.getFunction() is exports.run
376+ " at #{formatSourcePosition frame, getSourceMapping}"
377+
378+ "#{err.toString()}\n#{frames.join '\n'}\n"
379+
380+checkShebangLine = (file, input) ->
381+ firstLine = input.split(/$/m)[0]
382+ rest = firstLine?.match(/^#!\s*([^\s]+\s*)(.*)/)
383+ args = rest?[2]?.split(/\s/).filter (s) -> s isnt ''
384+ if args?.length > 1
385+ console.error '''
386+ The script to be run begins with a shebang line with more than one
387+ argument. This script will fail on platforms such as Linux which only
388+ allow a single argument.
389+ '''
390+ console.error "The shebang line was: '#{firstLine}' in file '#{file}'"
391+ console.error "The arguments were: #{JSON.stringify args}"
tests/syntax-tests/source/CoffeeScript/LICENSE.mdnew file+26
@@ -0,0 +1,26 @@
1+The `coffeescript.coffee` file has been added from https://github.com/jashkenas/coffeescript under the following license:
2+
3+```text
4+Copyright (c) 2009-2018 Jeremy Ashkenas
5+
6+Permission is hereby granted, free of charge, to any person
7+obtaining a copy of this software and associated documentation
8+files (the "Software"), to deal in the Software without
9+restriction, including without limitation the rights to use,
10+copy, modify, merge, publish, distribute, sublicense, and/or sell
11+copies of the Software, and to permit persons to whom the
12+Software is furnished to do so, subject to the following
13+conditions:
14+
15+The above copyright notice and this permission notice shall be
16+included in all copies or substantial portions of the Software.
17+
18+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25+OTHER DEALINGS IN THE SOFTWARE.
26+```
tests/syntax-tests/source/CoffeeScript/coffeescript.coffeenew file+391
@@ -0,0 +1,391 @@
1+# CoffeeScript can be used both on the server, as a command-line compiler based
2+# on Node.js/V8, or to run CoffeeScript directly in the browser. This module
3+# contains the main entry functions for tokenizing, parsing, and compiling
4+# source CoffeeScript into JavaScript.
5+
6+{Lexer} = require './lexer'
7+{parser} = require './parser'
8+helpers = require './helpers'
9+SourceMap = require './sourcemap'
10+# Require `package.json`, which is two levels above this file, as this file is
11+# evaluated from `lib/coffeescript`.
12+packageJson = require '../../package.json'
13+
14+# The current CoffeeScript version number.
15+exports.VERSION = packageJson.version
16+
17+exports.FILE_EXTENSIONS = FILE_EXTENSIONS = ['.coffee', '.litcoffee', '.coffee.md']
18+
19+# Expose helpers for testing.
20+exports.helpers = helpers
21+
22+# Function that allows for btoa in both nodejs and the browser.
23+base64encode = (src) -> switch
24+ when typeof Buffer is 'function'
25+ Buffer.from(src).toString('base64')
26+ when typeof btoa is 'function'
27+ # The contents of a `<script>` block are encoded via UTF-16, so if any extended
28+ # characters are used in the block, btoa will fail as it maxes out at UTF-8.
29+ # See https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
30+ # for the gory details, and for the solution implemented here.
31+ btoa encodeURIComponent(src).replace /%([0-9A-F]{2})/g, (match, p1) ->
32+ String.fromCharCode '0x' + p1
33+ else
34+ throw new Error('Unable to base64 encode inline sourcemap.')
35+
36+# Function wrapper to add source file information to SyntaxErrors thrown by the
37+# lexer/parser/compiler.
38+withPrettyErrors = (fn) ->
39+ (code, options = {}) ->
40+ try
41+ fn.call @, code, options
42+ catch err
43+ throw err if typeof code isnt 'string' # Support `CoffeeScript.nodes(tokens)`.
44+ throw helpers.updateSyntaxError err, code, options.filename
45+
46+# For each compiled file, save its source in memory in case we need to
47+# recompile it later. We might need to recompile if the first compilation
48+# didn’t create a source map (faster) but something went wrong and we need
49+# a stack trace. Assuming that most of the time, code isn’t throwing
50+# exceptions, it’s probably more efficient to compile twice only when we
51+# need a stack trace, rather than always generating a source map even when
52+# it’s not likely to be used. Save in form of `filename`: [`(source)`]
53+sources = {}
54+# Also save source maps if generated, in form of `(source)`: [`(source map)`].
55+sourceMaps = {}
56+
57+# This is exported to enable an external module to implement caching of
58+# compilation results. When the compiled js source is loaded from cache, the
59+# original coffee code should be added with this method in order to enable the
60+# Error.prepareStackTrace below to correctly adjust the stack trace for the
61+# corresponding file (the source map will be generated on demand).
62+exports.registerCompiled = registerCompiled = (filename, source, sourcemap) ->
63+
64+ sources[filename] ?= []
65+ sources[filename].push source
66+
67+ if sourcemap?
68+ sourceMaps[filename] ?= []
69+ sourceMaps[filename].push sourcemap
70+
71+# Compile CoffeeScript code to JavaScript, using the Coffee/Jison compiler.
72+#
73+# If `options.sourceMap` is specified, then `options.filename` must also be
74+# specified. All options that can be passed to `SourceMap#generate` may also
75+# be passed here.
76+#
77+# This returns a javascript string, unless `options.sourceMap` is passed,
78+# in which case this returns a `{js, v3SourceMap, sourceMap}`
79+# object, where sourceMap is a sourcemap.coffee#SourceMap object, handy for
80+# doing programmatic lookups.
81+exports.compile = compile = withPrettyErrors (code, options = {}) ->
82+ # Clone `options`, to avoid mutating the `options` object passed in.
83+ options = Object.assign {}, options
84+ # Always generate a source map if no filename is passed in, since without a
85+ # a filename we have no way to retrieve this source later in the event that
86+ # we need to recompile it to get a source map for `prepareStackTrace`.
87+ generateSourceMap = options.sourceMap or options.inlineMap or not options.filename?
88+ filename = options.filename or '<anonymous>'
89+
90+ checkShebangLine filename, code
91+
92+ map = new SourceMap if generateSourceMap
93+
94+ tokens = lexer.tokenize code, options
95+
96+ # Pass a list of referenced variables, so that generated variables won’t get
97+ # the same name.
98+ options.referencedVars = (
99+ token[1] for token in tokens when token[0] is 'IDENTIFIER'
100+ )
101+
102+ # Check for import or export; if found, force bare mode.
103+ unless options.bare? and options.bare is yes
104+ for token in tokens
105+ if token[0] in ['IMPORT', 'EXPORT']
106+ options.bare = yes
107+ break
108+
109+ nodes = parser.parse tokens
110+ # If all that was requested was a POJO representation of the nodes, e.g.
111+ # the abstract syntax tree (AST), we can stop now and just return that
112+ # (after fixing the location data for the root/`File`»`Program` node,
113+ # which might’ve gotten misaligned from the original source due to the
114+ # `clean` function in the lexer).
115+ if options.ast
116+ nodes.allCommentTokens = helpers.extractAllCommentTokens tokens
117+ sourceCodeNumberOfLines = (code.match(/\r?\n/g) or '').length + 1
118+ sourceCodeLastLine = /.*$/.exec(code)[0] # `.*` matches all but line break characters.
119+ ast = nodes.ast options
120+ range = [0, code.length]
121+ ast.start = ast.program.start = range[0]
122+ ast.end = ast.program.end = range[1]
123+ ast.range = ast.program.range = range
124+ ast.loc.start = ast.program.loc.start = {line: 1, column: 0}
125+ ast.loc.end.line = ast.program.loc.end.line = sourceCodeNumberOfLines
126+ ast.loc.end.column = ast.program.loc.end.column = sourceCodeLastLine.length
127+ ast.tokens = tokens
128+ return ast
129+
130+ fragments = nodes.compileToFragments options
131+
132+ currentLine = 0
133+ currentLine += 1 if options.header
134+ currentLine += 1 if options.shiftLine
135+ currentColumn = 0
136+ js = ""
137+ for fragment in fragments
138+ # Update the sourcemap with data from each fragment.
139+ if generateSourceMap
140+ # Do not include empty, whitespace, or semicolon-only fragments.
141+ if fragment.locationData and not /^[;\s]*$/.test fragment.code
142+ map.add(
143+ [fragment.locationData.first_line, fragment.locationData.first_column]
144+ [currentLine, currentColumn]
145+ {noReplace: true})
146+ newLines = helpers.count fragment.code, "\n"
147+ currentLine += newLines
148+ if newLines
149+ currentColumn = fragment.code.length - (fragment.code.lastIndexOf("\n") + 1)
150+ else
151+ currentColumn += fragment.code.length
152+
153+ # Copy the code from each fragment into the final JavaScript.
154+ js += fragment.code
155+
156+ if options.header
157+ header = "Generated by CoffeeScript #{@VERSION}"
158+ js = "// #{header}\n#{js}"
159+
160+ if generateSourceMap
161+ v3SourceMap = map.generate options, code
162+
163+ if options.transpile
164+ if typeof options.transpile isnt 'object'
165+ # This only happens if run via the Node API and `transpile` is set to
166+ # something other than an object.
167+ throw new Error 'The transpile option must be given an object with options to pass to Babel'
168+
169+ # Get the reference to Babel that we have been passed if this compiler
170+ # is run via the CLI or Node API.
171+ transpiler = options.transpile.transpile
172+ delete options.transpile.transpile
173+
174+ transpilerOptions = Object.assign {}, options.transpile
175+
176+ # See https://github.com/babel/babel/issues/827#issuecomment-77573107:
177+ # Babel can take a v3 source map object as input in `inputSourceMap`
178+ # and it will return an *updated* v3 source map object in its output.
179+ if v3SourceMap and not transpilerOptions.inputSourceMap?
180+ transpilerOptions.inputSourceMap = v3SourceMap
181+ transpilerOutput = transpiler js, transpilerOptions
182+ js = transpilerOutput.code
183+ if v3SourceMap and transpilerOutput.map
184+ v3SourceMap = transpilerOutput.map
185+
186+ if options.inlineMap
187+ encoded = base64encode JSON.stringify v3SourceMap
188+ sourceMapDataURI = "//# sourceMappingURL=data:application/json;base64,#{encoded}"
189+ sourceURL = "//# sourceURL=#{options.filename ? 'coffeescript'}"
190+ js = "#{js}\n#{sourceMapDataURI}\n#{sourceURL}"
191+
192+ registerCompiled filename, code, map
193+
194+ if options.sourceMap
195+ {
196+ js
197+ sourceMap: map
198+ v3SourceMap: JSON.stringify v3SourceMap, null, 2
199+ }
200+ else
201+ js
202+
203+# Tokenize a string of CoffeeScript code, and return the array of tokens.
204+exports.tokens = withPrettyErrors (code, options) ->
205+ lexer.tokenize code, options
206+
207+# Parse a string of CoffeeScript code or an array of lexed tokens, and
208+# return the AST. You can then compile it by calling `.compile()` on the root,
209+# or traverse it by using `.traverseChildren()` with a callback.
210+exports.nodes = withPrettyErrors (source, options) ->
211+ source = lexer.tokenize source, options if typeof source is 'string'
212+ parser.parse source
213+
214+# This file used to export these methods; leave stubs that throw warnings
215+# instead. These methods have been moved into `index.coffee` to provide
216+# separate entrypoints for Node and non-Node environments, so that static
217+# analysis tools don’t choke on Node packages when compiling for a non-Node
218+# environment.
219+exports.run = exports.eval = exports.register = ->
220+ throw new Error 'require index.coffee, not this file'
221+
222+# Instantiate a Lexer for our use here.
223+lexer = new Lexer
224+
225+# The real Lexer produces a generic stream of tokens. This object provides a
226+# thin wrapper around it, compatible with the Jison API. We can then pass it
227+# directly as a “Jison lexer.”
228+parser.lexer =
229+ yylloc:
230+ range: []
231+ options:
232+ ranges: yes
233+ lex: ->
234+ token = parser.tokens[@pos++]
235+ if token
236+ [tag, @yytext, @yylloc] = token
237+ parser.errorToken = token.origin or token
238+ @yylineno = @yylloc.first_line
239+ else
240+ tag = ''
241+ tag
242+ setInput: (tokens) ->
243+ parser.tokens = tokens
244+ @pos = 0
245+ upcomingInput: -> ''
246+
247+# Make all the AST nodes visible to the parser.
248+parser.yy = require './nodes'
249+
250+# Override Jison's default error handling function.
251+parser.yy.parseError = (message, {token}) ->
252+ # Disregard Jison's message, it contains redundant line number information.
253+ # Disregard the token, we take its value directly from the lexer in case
254+ # the error is caused by a generated token which might refer to its origin.
255+ {errorToken, tokens} = parser
256+ [errorTag, errorText, errorLoc] = errorToken
257+
258+ errorText = switch
259+ when errorToken is tokens[tokens.length - 1]
260+ 'end of input'
261+ when errorTag in ['INDENT', 'OUTDENT']
262+ 'indentation'
263+ when errorTag in ['IDENTIFIER', 'NUMBER', 'INFINITY', 'STRING', 'STRING_START', 'REGEX', 'REGEX_START']
264+ errorTag.replace(/_START$/, '').toLowerCase()
265+ else
266+ helpers.nameWhitespaceCharacter errorText
267+
268+ # The second argument has a `loc` property, which should have the location
269+ # data for this token. Unfortunately, Jison seems to send an outdated `loc`
270+ # (from the previous token), so we take the location information directly
271+ # from the lexer.
272+ helpers.throwSyntaxError "unexpected #{errorText}", errorLoc
273+
274+# Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js
275+# Modified to handle sourceMap
276+formatSourcePosition = (frame, getSourceMapping) ->
277+ filename = undefined
278+ fileLocation = ''
279+
280+ if frame.isNative()
281+ fileLocation = "native"
282+ else
283+ if frame.isEval()
284+ filename = frame.getScriptNameOrSourceURL()
285+ fileLocation = "#{frame.getEvalOrigin()}, " unless filename
286+ else
287+ filename = frame.getFileName()
288+
289+ filename or= "<anonymous>"
290+
291+ line = frame.getLineNumber()
292+ column = frame.getColumnNumber()
293+
294+ # Check for a sourceMap position
295+ source = getSourceMapping filename, line, column
296+ fileLocation =
297+ if source
298+ "#{filename}:#{source[0]}:#{source[1]}"
299+ else
300+ "#{filename}:#{line}:#{column}"
301+
302+ functionName = frame.getFunctionName()
303+ isConstructor = frame.isConstructor()
304+ isMethodCall = not (frame.isToplevel() or isConstructor)
305+
306+ if isMethodCall
307+ methodName = frame.getMethodName()
308+ typeName = frame.getTypeName()
309+
310+ if functionName
311+ tp = as = ''
312+ if typeName and functionName.indexOf typeName
313+ tp = "#{typeName}."
314+ if methodName and functionName.indexOf(".#{methodName}") isnt functionName.length - methodName.length - 1
315+ as = " [as #{methodName}]"
316+
317+ "#{tp}#{functionName}#{as} (#{fileLocation})"
318+ else
319+ "#{typeName}.#{methodName or '<anonymous>'} (#{fileLocation})"
320+ else if isConstructor
321+ "new #{functionName or '<anonymous>'} (#{fileLocation})"
322+ else if functionName
323+ "#{functionName} (#{fileLocation})"
324+ else
325+ fileLocation
326+
327+getSourceMap = (filename, line, column) ->
328+ # Skip files that we didn’t compile, like Node system files that appear in
329+ # the stack trace, as they never have source maps.
330+ return null unless filename is '<anonymous>' or filename.slice(filename.lastIndexOf('.')) in FILE_EXTENSIONS
331+
332+ if filename isnt '<anonymous>' and sourceMaps[filename]?
333+ return sourceMaps[filename][sourceMaps[filename].length - 1]
334+ # CoffeeScript compiled in a browser or via `CoffeeScript.compile` or `.run`
335+ # may get compiled with `options.filename` that’s missing, which becomes
336+ # `<anonymous>`; but the runtime might request the stack trace with the
337+ # filename of the script file. See if we have a source map cached under
338+ # `<anonymous>` that matches the error.
339+ else if sourceMaps['<anonymous>']?
340+ # Work backwards from the most recent anonymous source maps, until we find
341+ # one that works. This isn’t foolproof; there is a chance that multiple
342+ # source maps will have line/column pairs that match. But we have no other
343+ # way to match them. `frame.getFunction().toString()` doesn’t always work,
344+ # and it’s not foolproof either.
345+ for map in sourceMaps['<anonymous>'] by -1
346+ sourceLocation = map.sourceLocation [line - 1, column - 1]
347+ return map if sourceLocation?[0]? and sourceLocation[1]?
348+
349+ # If all else fails, recompile this source to get a source map. We need the
350+ # previous section (for `<anonymous>`) despite this option, because after it
351+ # gets compiled we will still need to look it up from
352+ # `sourceMaps['<anonymous>']` in order to find and return it. That’s why we
353+ # start searching from the end in the previous block, because most of the
354+ # time the source map we want is the last one.
355+ if sources[filename]?
356+ answer = compile sources[filename][sources[filename].length - 1],
357+ filename: filename
358+ sourceMap: yes
359+ literate: helpers.isLiterate filename
360+ answer.sourceMap
361+ else
362+ null
363+
364+# Based on [michaelficarra/CoffeeScriptRedux](http://goo.gl/ZTx1p)
365+# NodeJS / V8 have no support for transforming positions in stack traces using
366+# sourceMap, so we must monkey-patch Error to display CoffeeScript source
367+# positions.
368+Error.prepareStackTrace = (err, stack) ->
369+ getSourceMapping = (filename, line, column) ->
370+ sourceMap = getSourceMap filename, line, column
371+ answer = sourceMap.sourceLocation [line - 1, column - 1] if sourceMap?
372+ if answer? then [answer[0] + 1, answer[1] + 1] else null
373+
374+ frames = for frame in stack
375+ break if frame.getFunction() is exports.run
376+ " at #{formatSourcePosition frame, getSourceMapping}"
377+
378+ "#{err.toString()}\n#{frames.join '\n'}\n"
379+
380+checkShebangLine = (file, input) ->
381+ firstLine = input.split(/$/m)[0]
382+ rest = firstLine?.match(/^#!\s*([^\s]+\s*)(.*)/)
383+ args = rest?[2]?.split(/\s/).filter (s) -> s isnt ''
384+ if args?.length > 1
385+ console.error '''
386+ The script to be run begins with a shebang line with more than one
387+ argument. This script will fail on platforms such as Linux which only
388+ allow a single argument.
389+ '''
390+ console.error "The shebang line was: '#{firstLine}' in file '#{file}'"
391+ console.error "The arguments were: #{JSON.stringify args}"