diff --git a/TODO b/TODO index 8fc6ae7c..93dbd7f7 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,5 @@ TODO: -* Need *way* better syntax errors. - * Is it possible to close blocks (functions, ifs, trys) without an explicit block delimiter or significant whitespace? diff --git a/examples/syntax_errors.cs b/examples/syntax_errors.cs index 5df5956c..aa72cd71 100644 --- a/examples/syntax_errors.cs +++ b/examples/syntax_errors.cs @@ -1,2 +1,20 @@ # Identifiers run together: -a b c \ No newline at end of file +# a b c + +# Trailing comma in array: +# array: [1, 2, 3, 4, 5,] + +# Unterminated object literal: +# obj: { one: 1, two: 2 + +# Numbers run together: +# 101 202 + +# Strings run together: +# str: "broken" "words" + +# Forgot to terminate a function: +# obj: { +# first: a => a[0]. +# last: a => a[a.length-1] +# } \ No newline at end of file diff --git a/lib/coffee-script.rb b/lib/coffee-script.rb index cfcf7de9..aad0fc07 100644 --- a/lib/coffee-script.rb +++ b/lib/coffee-script.rb @@ -1,9 +1,10 @@ $LOAD_PATH.unshift(File.dirname(__FILE__)) -require "coffee_script/value" -require "coffee_script/scope" require "coffee_script/lexer" require "coffee_script/parser" require "coffee_script/nodes" +require "coffee_script/value" +require "coffee_script/scope" +require "coffee_script/parse_error" # Namespace for all CoffeeScript internal classes. module CoffeeScript diff --git a/lib/coffee_script/command_line.rb b/lib/coffee_script/command_line.rb index 70e59004..66c02af7 100644 --- a/lib/coffee_script/command_line.rb +++ b/lib/coffee_script/command_line.rb @@ -31,7 +31,7 @@ Usage: def compile_javascript @sources.each do |source| next tokens(source) if @options[:tokens] - contents = CoffeeScript.compile(File.open(source)) + contents = compile(source) next puts(contents) if @options[:print] next lint(contents) if @options[:lint] File.open(path_for(source), 'w+') {|f| f.write(contents) } @@ -60,6 +60,15 @@ Usage: puts Lexer.new.tokenize(File.read(source)).inspect end + def compile(source) + begin + CoffeeScript.compile(File.open(source)) + rescue CoffeeScript::ParseError => e + STDERR.puts e.message(source) + exit(1) + end + end + # Write out JavaScript alongside CoffeeScript unless an output directory # is specified. def path_for(source) diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index 2fb3fe7f..cee409fb 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -294,13 +294,16 @@ rule end ---- inner - def parse(code, show_tokens=false) + def parse(code) # @yydebug = true @tokens = Lexer.new.tokenize(code) - puts @tokens.inspect if show_tokens do_parse end def next_token @tokens.shift + end + + def on_error(error_token_id, error_value, value_stack) + raise CoffeeScript::ParseError.new(token_to_str(error_token_id), error_value, value_stack) end \ No newline at end of file diff --git a/lib/coffee_script/parse_error.rb b/lib/coffee_script/parse_error.rb new file mode 100644 index 00000000..9f49f0bb --- /dev/null +++ b/lib/coffee_script/parse_error.rb @@ -0,0 +1,19 @@ +module CoffeeScript + + class ParseError < Racc::ParseError + + def initialize(token_id, value, stack) + @token_id, @value, @stack = token_id, value, stack + end + + def message(source_file=nil) + line = @value.respond_to?(:line) ? @value.line : "END" + line_part = source_file ? "#{source_file}:#{line}:" : "line #{line}:" + id_part = @token_id != @value.inspect ? ", unexpected #{@token_id.downcase}" : "" + "#{line_part} syntax error for '#{@value.to_s}'#{id_part}" + end + alias_method :inspect, :message + + end + +end \ No newline at end of file