mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
Improved lexer error messages
This commit is contained in:
@@ -5,15 +5,16 @@
|
||||
# interactive REPL.
|
||||
|
||||
# External dependencies.
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
helpers = require './helpers'
|
||||
optparse = require './optparse'
|
||||
CoffeeScript = require './coffee-script'
|
||||
{spawn, exec} = require 'child_process'
|
||||
{EventEmitter} = require 'events'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
helpers = require './helpers'
|
||||
optparse = require './optparse'
|
||||
CoffeeScript = require './coffee-script'
|
||||
{CompilerError} = require './error'
|
||||
{spawn, exec} = require 'child_process'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
exists = fs.exists or path.exists
|
||||
exists = fs.exists or path.exists
|
||||
|
||||
# Allow CoffeeScript to emit Node.js events.
|
||||
helpers.extend CoffeeScript, new EventEmitter
|
||||
@@ -138,9 +139,17 @@ compileScript = (file, input, base) ->
|
||||
catch err
|
||||
CoffeeScript.emit 'failure', err, task
|
||||
return if CoffeeScript.listeners('failure').length
|
||||
return printLine err.message + '\x07' if o.watch
|
||||
printWarn err instanceof Error and err.stack or "ERROR: #{err}"
|
||||
process.exit 1
|
||||
|
||||
message = if err instanceof CompilerError
|
||||
err.prettyMessage file or '[stdin]', input
|
||||
else
|
||||
err.stack or "ERROR: #{err}"
|
||||
|
||||
if o.watch
|
||||
printLine message + '\x07' if o.watch
|
||||
else
|
||||
printWarn message
|
||||
process.exit 1
|
||||
|
||||
# Attach the appropriate listeners to compile scripts incoming over **stdin**,
|
||||
# and write them back to **stdout**.
|
||||
|
||||
27
src/error.coffee
Normal file
27
src/error.coffee
Normal file
@@ -0,0 +1,27 @@
|
||||
{repeat} = require './helpers'
|
||||
|
||||
# A common error class used throughout the compiler to indicate compilation
|
||||
# errors at a given location in the source code.
|
||||
exports.CompilerError = class CompilerError extends Error
|
||||
name: 'CompilerError'
|
||||
|
||||
constructor: (@message, @startLine, @startColumn,
|
||||
@endLine = @startLine, @endColumn = @startColumn) ->
|
||||
# Add a stack trace in V8.
|
||||
Error.captureStackTrace? @, CompilerError
|
||||
|
||||
# Creates a nice error message like, following the "standard" format
|
||||
# <filename>:<line>:<col>: <message> plus the line with the error and a marker
|
||||
# showing where the error is.
|
||||
# TODO: tests
|
||||
prettyMessage: (fileName, code) ->
|
||||
message = "#{fileName}:#{@startLine}:#{@startColumn}: #{@message}"
|
||||
if @startLine is @endLine
|
||||
errorLine = code.split('\n')[@startLine - 1]
|
||||
errorLength = @endColumn - @startColumn + 1
|
||||
marker = (repeat ' ', @startColumn - 1) + (repeat '^', errorLength)
|
||||
message += "\n#{errorLine}\n#{marker}"
|
||||
else
|
||||
# TODO: How do we show multi-line errors?
|
||||
undefined
|
||||
message
|
||||
@@ -11,6 +11,10 @@ exports.ends = (string, literal, back) ->
|
||||
len = literal.length
|
||||
literal is string.substr string.length - len - (back or 0), len
|
||||
|
||||
# Repeat a string `n` times.
|
||||
exports.repeat = (string, n) ->
|
||||
(Array n + 1).join string
|
||||
|
||||
# Trim out all falsy values from an array.
|
||||
exports.compact = (array) ->
|
||||
item for item in array when item
|
||||
@@ -71,8 +75,9 @@ buildLocationData = (first, last) ->
|
||||
last_line: last.last_line
|
||||
last_column: last.last_column
|
||||
|
||||
# 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.
|
||||
# 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.addLocationDataFn = (first, last) ->
|
||||
(obj) ->
|
||||
if ((typeof obj) is 'object') and (!!obj['updateLocationDataIfMissing'])
|
||||
@@ -91,5 +96,3 @@ exports.locationDataToString = (obj) ->
|
||||
"#{locationData.last_line + 1}:#{locationData.last_column + 1}"
|
||||
else
|
||||
"No location data"
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
# Import the helpers we need.
|
||||
{count, starts, compact, last, locationDataToString} = require './helpers'
|
||||
{CompilerError} = require './error'
|
||||
|
||||
# The Lexer Class
|
||||
# ---------------
|
||||
@@ -41,7 +42,7 @@ exports.Lexer = class Lexer
|
||||
@outdebt = 0 # The under-outdentation at the current level.
|
||||
@indents = [] # The stack of all current indentation levels.
|
||||
@ends = [] # The stack for pairing up tokens.
|
||||
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, line]`.
|
||||
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, location data]`.
|
||||
|
||||
@chunkLine =
|
||||
opts.line or 0 # The start line for the current @chunk.
|
||||
@@ -693,11 +694,11 @@ exports.Lexer = class Lexer
|
||||
body = body.replace /// #{quote} ///g, '\\$&'
|
||||
quote + @escapeLines(body, heredoc) + quote
|
||||
|
||||
# Throws a syntax error on the current `@line`.
|
||||
# Throws a compiler error on the current position.
|
||||
error: (message) ->
|
||||
# TODO: Are there some cases we could improve the error line number by
|
||||
# passing the offset in the chunk where the error happened?
|
||||
throw SyntaxError "#{message} on line #{ @chunkLine + 1 }"
|
||||
throw new CompilerError message, @chunkLine + 1, @chunkColumn + 1
|
||||
|
||||
# Constants
|
||||
# ---------
|
||||
|
||||
Reference in New Issue
Block a user