mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-02-18 19:34:27 -05:00
merging in master
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<key>name</key>
|
||||
<string>comments</string>
|
||||
<key>scope</key>
|
||||
<string>source.cs</string>
|
||||
<string>source.coffee</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>shellVariables</key>
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
<string>CoffeeScript Syntax: version 1</string>
|
||||
<key>fileTypes</key>
|
||||
<array>
|
||||
<string>cs</string>
|
||||
<string>coffeescript</string>
|
||||
<string>coffee</string>
|
||||
</array>
|
||||
<key>name</key>
|
||||
<string>CoffeeScript</string>
|
||||
@@ -19,22 +18,22 @@
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.name.function.cs</string>
|
||||
<string>entity.name.function.coffee</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.cs</string>
|
||||
<string>keyword.operator.coffee</string>
|
||||
</dict>
|
||||
<key>3</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.parameter.function.cs</string>
|
||||
<string>variable.parameter.function.coffee</string>
|
||||
</dict>
|
||||
<key>4</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>storage.type.function.cs</string>
|
||||
<string>storage.type.function.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>comment</key>
|
||||
@@ -42,7 +41,7 @@
|
||||
<key>match</key>
|
||||
<string>([a-zA-Z_?.$]*)\s*(=|:)\s*([\w,\s]*?)\s*(=>)</string>
|
||||
<key>name</key>
|
||||
<string>meta.function.cs</string>
|
||||
<string>meta.function.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
@@ -50,12 +49,12 @@
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>variable.parameter.function.cs</string>
|
||||
<string>variable.parameter.function.coffee</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>storage.type.function.cs</string>
|
||||
<string>storage.type.function.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>comment</key>
|
||||
@@ -63,7 +62,7 @@
|
||||
<key>match</key>
|
||||
<string>([a-zA-Z_?., $]*)\s*(=>)</string>
|
||||
<key>name</key>
|
||||
<string>meta.inline.function.cs</string>
|
||||
<string>meta.inline.function.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>captures</key>
|
||||
@@ -71,12 +70,12 @@
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.new.cs</string>
|
||||
<string>keyword.operator.new.coffee</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>entity.name.type.instance.cs</string>
|
||||
<string>entity.name.type.instance.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
@@ -88,7 +87,7 @@
|
||||
<key>match</key>
|
||||
<string>\b((0(x|X)[0-9a-fA-F]+)|([0-9]+(\.[0-9]+)?(e[+\-]?[0-9]+)?))\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.numeric.cs</string>
|
||||
<string>constant.numeric.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
@@ -98,7 +97,7 @@
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.cs</string>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
@@ -108,18 +107,18 @@
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.cs</string>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>string.quoted.single.cs</string>
|
||||
<string>string.quoted.single.coffee</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escape.cs</string>
|
||||
<string>constant.character.escape.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
@@ -131,7 +130,7 @@
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.cs</string>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
@@ -141,18 +140,18 @@
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.cs</string>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>string.quoted.double.cs</string>
|
||||
<string>string.quoted.double.coffee</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]|37[0-7]?|[4-7][0-7]?|.)</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escape.cs</string>
|
||||
<string>constant.character.escape.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
@@ -164,7 +163,7 @@
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.cs</string>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
@@ -174,18 +173,18 @@
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.cs</string>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>string.quoted.script.cs</string>
|
||||
<string>string.quoted.script.coffee</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\(x\h{2}|[0-2][0-7]{,2}|3[0-6][0-7]|37[0-7]?|[4-7][0-7]?|.)</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escape.cs</string>
|
||||
<string>constant.character.escape.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
@@ -195,61 +194,61 @@
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.comment.cs</string>
|
||||
<string>punctuation.definition.comment.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
<string>(#).*$\n?</string>
|
||||
<key>name</key>
|
||||
<string>comment.line.cs</string>
|
||||
<string>comment.line.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(break|when|catch|continue|else|finally|for|if|return|switch|then|throw|try|unless|while)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.control.cs</string>
|
||||
<string>keyword.control.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(true|on|yes)\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.language.boolean.true.cs</string>
|
||||
<string>constant.language.boolean.true.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(false|off|no)\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.language.boolean.false.cs</string>
|
||||
<string>constant.language.boolean.false.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\bnull\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.language.null.cs</string>
|
||||
<string>constant.language.null.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(super|this)\b</string>
|
||||
<string>\b(super|this|extends)\b</string>
|
||||
<key>name</key>
|
||||
<string>variable.language.cs</string>
|
||||
<string>variable.language.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(debugger)\b</string>
|
||||
<string>\b(debugger|\\)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.other.cs</string>
|
||||
<string>keyword.other.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>!|\$|%|&|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|aint|not)\b</string>
|
||||
<string>!|\$|%|&|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\?|\|\||\:|\*=|(?<!\()/=|%=|\+=|\-=|&=|\^=|\b(in|instanceof|new|delete|typeof|and|or|is|isnt|not)\b</string>
|
||||
<key>name</key>
|
||||
<string>keyword.operator.cs</string>
|
||||
<string>keyword.operator.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\b(Infinity|NaN|undefined)\b</string>
|
||||
<key>name</key>
|
||||
<string>constant.language.cs</string>
|
||||
<string>constant.language.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>begin</key>
|
||||
@@ -259,7 +258,7 @@
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.begin.cs</string>
|
||||
<string>punctuation.definition.string.begin.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
@@ -269,18 +268,18 @@
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>punctuation.definition.string.end.cs</string>
|
||||
<string>punctuation.definition.string.end.coffee</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>name</key>
|
||||
<string>string.regexp.cs</string>
|
||||
<string>string.regexp.coffee</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\\.</string>
|
||||
<key>name</key>
|
||||
<string>constant.character.escape.cs</string>
|
||||
<string>constant.character.escape.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
@@ -288,41 +287,41 @@
|
||||
<key>match</key>
|
||||
<string>\;</string>
|
||||
<key>name</key>
|
||||
<string>punctuation.terminator.statement.cs</string>
|
||||
<string>punctuation.terminator.statement.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>,[ |\t]*</string>
|
||||
<key>name</key>
|
||||
<string>meta.delimiter.object.comma.cs</string>
|
||||
<string>meta.delimiter.object.comma.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\.</string>
|
||||
<key>name</key>
|
||||
<string>meta.delimiter.method.period.cs</string>
|
||||
<string>meta.delimiter.method.period.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\{|\}</string>
|
||||
<key>name</key>
|
||||
<string>meta.brace.curly.cs</string>
|
||||
<string>meta.brace.curly.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\(|\)</string>
|
||||
<key>name</key>
|
||||
<string>meta.brace.round.cs</string>
|
||||
<string>meta.brace.round.coffee</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>match</key>
|
||||
<string>\[|\]</string>
|
||||
<key>name</key>
|
||||
<string>meta.brace.square.cs</string>
|
||||
<string>meta.brace.square.coffee</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>scopeName</key>
|
||||
<string>source.cs</string>
|
||||
<string>source.coffee</string>
|
||||
<key>uuid</key>
|
||||
<string>5B520980-A7D5-4E10-8582-1A4C889A8DE5</string>
|
||||
</dict>
|
||||
|
||||
@@ -5,15 +5,15 @@ require File.expand_path(File.dirname(__FILE__) + '/../coffee-script')
|
||||
|
||||
module CoffeeScript
|
||||
|
||||
# The CommandLine handles all of the functionality of the `coffee-script`
|
||||
# The CommandLine handles all of the functionality of the `coffee`
|
||||
# utility.
|
||||
class CommandLine
|
||||
|
||||
BANNER = <<-EOS
|
||||
coffee-script compiles CoffeeScript source files into JavaScript.
|
||||
coffee compiles CoffeeScript source files into JavaScript.
|
||||
|
||||
Usage:
|
||||
coffee-script path/to/script.cs
|
||||
coffee path/to/script.coffee
|
||||
EOS
|
||||
|
||||
# Seconds to pause between checks for changed source files.
|
||||
@@ -23,8 +23,10 @@ Usage:
|
||||
def initialize
|
||||
@mtimes = {}
|
||||
parse_options
|
||||
return launch_repl if @options[:interactive]
|
||||
return eval_scriptlet if @options[:eval]
|
||||
check_sources
|
||||
return run_scripts if @options[:run]
|
||||
@sources.each {|source| compile_javascript(source) }
|
||||
watch_coffee_scripts if @options[:watch]
|
||||
end
|
||||
@@ -100,17 +102,36 @@ Usage:
|
||||
puts js
|
||||
end
|
||||
|
||||
# Use Narwhal to run an interactive CoffeeScript session.
|
||||
def launch_repl
|
||||
exec "narwhal lib/coffee_script/narwhal/js/launcher.js"
|
||||
rescue Errno::ENOENT
|
||||
puts "Error: Narwhal must be installed to use the interactive REPL."
|
||||
exit(1)
|
||||
end
|
||||
|
||||
# Use Narwhal to compile and execute CoffeeScripts.
|
||||
def run_scripts
|
||||
sources = @sources.join(' ')
|
||||
exec "narwhal lib/coffee_script/narwhal/js/launcher.js #{sources}"
|
||||
rescue Errno::ENOENT
|
||||
puts "Error: Narwhal must be installed in order to execute CoffeeScripts."
|
||||
exit(1)
|
||||
end
|
||||
|
||||
# Print the tokens that the lexer generates from a source script.
|
||||
def tokens(script)
|
||||
puts Lexer.new.tokenize(script).inspect
|
||||
end
|
||||
|
||||
# Compile a single source file to JavaScript.
|
||||
def compile(script, source='')
|
||||
def compile(script, source='error')
|
||||
begin
|
||||
CoffeeScript.compile(script)
|
||||
rescue CoffeeScript::ParseError => e
|
||||
STDERR.puts e.message(source)
|
||||
options = {}
|
||||
options[:no_wrap] = true if @options[:no_wrap]
|
||||
CoffeeScript.compile(script, options)
|
||||
rescue CoffeeScript::ParseError, SyntaxError => e
|
||||
STDERR.puts "#{source}: #{e.message}"
|
||||
exit(1) unless @options[:watch]
|
||||
nil
|
||||
end
|
||||
@@ -134,6 +155,12 @@ Usage:
|
||||
def parse_options
|
||||
@options = {}
|
||||
@option_parser = OptionParser.new do |opts|
|
||||
opts.on('-i', '--interactive', 'run a CoffeeScript REPL (requires Narwhal)') do |i|
|
||||
@options[:interactive] = true
|
||||
end
|
||||
opts.on('-r', '--run', 'compile and run a script (requires Narwhal)') do |r|
|
||||
@options[:run] = true
|
||||
end
|
||||
opts.on('-o', '--output [DIR]', 'set the directory for compiled JavaScript') do |d|
|
||||
@options[:output] = d
|
||||
FileUtils.mkdir_p(d) unless File.exists?(d)
|
||||
@@ -147,7 +174,7 @@ Usage:
|
||||
opts.on('-l', '--lint', 'pipe the compiled JavaScript through JSLint') do |l|
|
||||
@options[:lint] = true
|
||||
end
|
||||
opts.on('-e', '--eval', 'eval a little scriptlet directly from the cli') do |e|
|
||||
opts.on('-e', '--eval', 'compile a cli scriptlet or read from stdin') do |e|
|
||||
@options[:eval] = true
|
||||
end
|
||||
opts.on('-t', '--tokens', 'print the tokens that the lexer produces') do |t|
|
||||
@@ -156,12 +183,15 @@ Usage:
|
||||
opts.on('-v', '--verbose', 'print at every step of code generation') do |v|
|
||||
ENV['VERBOSE'] = 'true'
|
||||
end
|
||||
opts.on('-n', '--no-wrap', 'raw output, no safety wrapper or vars') do |n|
|
||||
@options[:no_wrap] = true
|
||||
end
|
||||
opts.on_tail('--install-bundle', 'install the CoffeeScript TextMate bundle') do |i|
|
||||
install_bundle
|
||||
exit
|
||||
end
|
||||
opts.on_tail('--version', 'display coffee-script version') do
|
||||
puts "coffee-script version #{CoffeeScript::VERSION}"
|
||||
opts.on_tail('--version', 'display CoffeeScript version') do
|
||||
puts "CoffeeScript version #{CoffeeScript::VERSION}"
|
||||
exit
|
||||
end
|
||||
opts.on_tail('-h', '--help', 'display this help message') do
|
||||
|
||||
@@ -11,7 +11,7 @@ token BREAK CONTINUE
|
||||
token FOR IN WHILE
|
||||
token SWITCH WHEN
|
||||
token DELETE INSTANCEOF TYPEOF
|
||||
token SUPER
|
||||
token SUPER EXTENDS
|
||||
token NEWLINE
|
||||
token COMMENT
|
||||
token JS
|
||||
@@ -25,21 +25,21 @@ prechigh
|
||||
left '<<' '>>' '>>>'
|
||||
left '&' '|' '^'
|
||||
left '<=' '<' '>' '>='
|
||||
right '==' '!=' IS AINT
|
||||
right '==' '!=' IS ISNT
|
||||
left '&&' '||' AND OR
|
||||
right '-=' '+=' '/=' '*='
|
||||
right '-=' '+=' '/=' '*=' '%='
|
||||
right DELETE INSTANCEOF TYPEOF
|
||||
left "."
|
||||
right THROW FOR IN WHILE NEW
|
||||
left UNLESS IF ELSE
|
||||
left ":" '||:' '&&:'
|
||||
right RETURN INDENT
|
||||
left '.'
|
||||
right THROW FOR IN WHILE NEW SUPER
|
||||
left UNLESS IF ELSE EXTENDS
|
||||
left ASSIGN '||=' '&&='
|
||||
right RETURN
|
||||
left OUTDENT
|
||||
preclow
|
||||
|
||||
# We expect 4 shift/reduce errors for optional syntax.
|
||||
# We expect 3 shift/reduce errors for optional syntax.
|
||||
# There used to be 252 -- greatly improved.
|
||||
expect 4
|
||||
expect 3
|
||||
|
||||
rule
|
||||
|
||||
@@ -66,11 +66,11 @@ rule
|
||||
|
||||
# The parts that are natural JavaScript expressions.
|
||||
PureExpression:
|
||||
Literal
|
||||
| Value
|
||||
Value
|
||||
| Call
|
||||
| Code
|
||||
| Operation
|
||||
| Range
|
||||
;
|
||||
|
||||
# We have to take extra care to convert these statements into expressions.
|
||||
@@ -83,6 +83,7 @@ rule
|
||||
| While
|
||||
| For
|
||||
| Switch
|
||||
| Extends
|
||||
| Comment
|
||||
;
|
||||
|
||||
@@ -126,12 +127,13 @@ rule
|
||||
|
||||
# Assignment to a variable.
|
||||
Assign:
|
||||
Value ":" Expression { result = AssignNode.new(val[0], val[2]) }
|
||||
Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) }
|
||||
;
|
||||
|
||||
# Assignment within an object literal.
|
||||
AssignObj:
|
||||
IDENTIFIER ":" Expression { result = AssignNode.new(val[0], val[2], :object) }
|
||||
IDENTIFIER ASSIGN Expression { result = AssignNode.new(ValueNode.new(val[0]), val[2], :object) }
|
||||
| STRING ASSIGN Expression { result = AssignNode.new(ValueNode.new(LiteralNode.new(val[0])), val[2], :object) }
|
||||
| Comment { result = val[0] }
|
||||
;
|
||||
|
||||
@@ -154,10 +156,10 @@ rule
|
||||
| '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
|
||||
| NOT Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '~' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '--' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '++' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
|
||||
| Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
|
||||
| '--' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| '++' Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
|
||||
| Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
|
||||
|
||||
| Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
@@ -182,7 +184,7 @@ rule
|
||||
| Expression '==' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '!=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression IS Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression AINT Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression ISNT Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| Expression '&&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
@@ -193,8 +195,9 @@ rule
|
||||
| Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '||:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '&&:' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '%=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
| Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
|
||||
|
||||
| DELETE Expression { result = OpNode.new(val[0], val[1]) }
|
||||
| TYPEOF Expression { result = OpNode.new(val[0], val[1]) }
|
||||
@@ -222,6 +225,7 @@ rule
|
||||
# Expressions that can be treated as values.
|
||||
Value:
|
||||
IDENTIFIER { result = ValueNode.new(val[0]) }
|
||||
| Literal { result = ValueNode.new(val[0]) }
|
||||
| Array { result = ValueNode.new(val[0]) }
|
||||
| Object { result = ValueNode.new(val[0]) }
|
||||
| Parenthetical { result = ValueNode.new(val[0]) }
|
||||
@@ -233,7 +237,7 @@ rule
|
||||
Accessor:
|
||||
PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
|
||||
| Index { result = val[0] }
|
||||
| Slice { result = val[0] }
|
||||
| Range { result = SliceNode.new(val[0]) }
|
||||
;
|
||||
|
||||
# Indexing into an object or array.
|
||||
@@ -241,11 +245,6 @@ rule
|
||||
"[" Expression "]" { result = IndexNode.new(val[1]) }
|
||||
;
|
||||
|
||||
# Array slice literal.
|
||||
Slice:
|
||||
"[" Expression "," Expression "]" { result = SliceNode.new(val[1], val[3]) }
|
||||
;
|
||||
|
||||
# An object literal.
|
||||
Object:
|
||||
"{" AssignList "}" { result = ObjectNode.new(val[1]) }
|
||||
@@ -266,9 +265,15 @@ rule
|
||||
| Super { result = val[0] }
|
||||
;
|
||||
|
||||
# Extending an object's prototype.
|
||||
Extends:
|
||||
Value EXTENDS Value { result = ExtendsNode.new(val[0], val[2]) }
|
||||
;
|
||||
|
||||
# A generic function invocation.
|
||||
Invocation:
|
||||
Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
|
||||
| Invocation "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
|
||||
;
|
||||
|
||||
# Calling super.
|
||||
@@ -276,6 +281,12 @@ rule
|
||||
SUPER "(" ArgList ")" { result = CallNode.new(:super, val[2]) }
|
||||
;
|
||||
|
||||
# The range literal.
|
||||
Range:
|
||||
"[" Value "." "." Value "]" { result = RangeNode.new(val[1], val[4]) }
|
||||
| "[" Value "." "." "." Value "]" { result = RangeNode.new(val[1], val[5], true) }
|
||||
;
|
||||
|
||||
# The array literal.
|
||||
Array:
|
||||
"[" ArgList "]" { result = ArrayNode.new(val[1]) }
|
||||
@@ -318,19 +329,23 @@ rule
|
||||
;
|
||||
|
||||
# Array comprehensions, including guard and current index.
|
||||
# Looks a little confusing, check nodes.rb for the arguments to ForNode.
|
||||
For:
|
||||
Expression FOR IDENTIFIER
|
||||
IN PureExpression { result = ForNode.new(val[0], val[4], val[2], nil) }
|
||||
| Expression FOR
|
||||
IDENTIFIER "," IDENTIFIER
|
||||
IN PureExpression { result = ForNode.new(val[0], val[6], val[2], nil, val[4]) }
|
||||
| Expression FOR IDENTIFIER
|
||||
IN PureExpression
|
||||
IF Expression { result = ForNode.new(val[0], val[4], val[2], val[6]) }
|
||||
| Expression FOR
|
||||
IDENTIFIER "," IDENTIFIER
|
||||
IN PureExpression
|
||||
IF Expression { result = ForNode.new(val[0], val[6], val[2], val[8], val[4]) }
|
||||
Expression FOR
|
||||
ForVariables ForSource { result = ForNode.new(val[0], val[3][0], val[2][0], val[3][1], val[2][1]) }
|
||||
;
|
||||
|
||||
# An array comprehension has variables for the current element and index.
|
||||
ForVariables:
|
||||
IDENTIFIER { result = val }
|
||||
| IDENTIFIER "," IDENTIFIER { result = [val[0], val[2]] }
|
||||
;
|
||||
|
||||
# The source of the array comprehension can optionally be filtered.
|
||||
ForSource:
|
||||
IN PureExpression "." { result = [val[1]] }
|
||||
| IN PureExpression
|
||||
IF Expression "." { result = [val[1], val[3]] }
|
||||
;
|
||||
|
||||
# Switch/When blocks.
|
||||
|
||||
@@ -8,13 +8,13 @@ module CoffeeScript
|
||||
# The list of keywords passed verbatim to the parser.
|
||||
KEYWORDS = ["if", "else", "then", "unless",
|
||||
"true", "false", "yes", "no", "on", "off",
|
||||
"and", "or", "is", "aint", "not",
|
||||
"and", "or", "is", "isnt", "not",
|
||||
"new", "return",
|
||||
"try", "catch", "finally", "throw",
|
||||
"break", "continue",
|
||||
"for", "in", "while",
|
||||
"switch", "when",
|
||||
"super",
|
||||
"super", "extends",
|
||||
"delete", "instanceof", "typeof"]
|
||||
|
||||
# Token matching regexes.
|
||||
@@ -41,6 +41,9 @@ module CoffeeScript
|
||||
# Tokens that always constitute the end of an expression.
|
||||
EXP_END = ['}', ')', ']']
|
||||
|
||||
# Assignment tokens.
|
||||
ASSIGN = [':', '=']
|
||||
|
||||
# Scan by attempting to match tokens one character at a time. Slow and steady.
|
||||
def tokenize(code)
|
||||
@code = code.chomp # Cleanup code by remove extra line breaks
|
||||
@@ -76,7 +79,7 @@ module CoffeeScript
|
||||
# Keywords are special identifiers tagged with their own name, 'if' will result
|
||||
# in an [:IF, "if"] token
|
||||
tag = KEYWORDS.include?(identifier) ? identifier.upcase.to_sym : :IDENTIFIER
|
||||
@tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.'
|
||||
@tokens[-1][0] = :PROPERTY_ACCESS if tag == :IDENTIFIER && last_value == '.' && !(@tokens[-2][1] == '.')
|
||||
token(tag, identifier)
|
||||
@i += identifier.length
|
||||
end
|
||||
@@ -150,11 +153,13 @@ module CoffeeScript
|
||||
# We treat all other single characters as a token. Eg.: ( ) , . !
|
||||
# Multi-character operators are also literal tokens, so that Racc can assign
|
||||
# the proper order of operations. Multiple newlines get merged together.
|
||||
# Use a trailing \ to escape newlines.
|
||||
def literal_token
|
||||
value = @chunk[NEWLINE, 1]
|
||||
if value
|
||||
@line += value.length
|
||||
token("\n", "\n") unless last_value == "\n"
|
||||
token("\n", "\n") unless ["\n", "\\"].include?(last_value)
|
||||
@tokens.pop if last_value == "\\"
|
||||
return @i += value.length
|
||||
end
|
||||
value = @chunk[OPERATOR, 1]
|
||||
@@ -162,7 +167,8 @@ module CoffeeScript
|
||||
value ||= @chunk[0,1]
|
||||
skip_following_newlines if EXP_START.include?(value)
|
||||
remove_leading_newlines if EXP_END.include?(value)
|
||||
token(value, value)
|
||||
tag = ASSIGN.include?(value) ? :ASSIGN : value
|
||||
token(tag, value)
|
||||
@i += value.length
|
||||
end
|
||||
|
||||
|
||||
59
lib/coffee_script/narwhal/coffee-script.coffee
Normal file
59
lib/coffee_script/narwhal/coffee-script.coffee
Normal file
@@ -0,0 +1,59 @@
|
||||
# This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee
|
||||
|
||||
# Executes the `coffee` Ruby program to convert from CoffeeScript
|
||||
# to Javascript. Eventually this will hopefully happen entirely within JS.
|
||||
|
||||
# Require external dependencies.
|
||||
OS: require('os')
|
||||
File: require('file')
|
||||
Readline: require('readline')
|
||||
|
||||
# The path to the CoffeeScript Compiler.
|
||||
coffeePath: File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee')
|
||||
|
||||
# Our general-purpose error handler.
|
||||
checkForErrors: coffeeProcess =>
|
||||
return true if coffeeProcess.wait() is 0
|
||||
system.stderr.print(coffeeProcess.stderr.read())
|
||||
throw new Error("CoffeeScript compile error").
|
||||
|
||||
# Run a simple REPL, round-tripping to the CoffeeScript compiler for every
|
||||
# command.
|
||||
exports.run: args =>
|
||||
args.shift()
|
||||
return require(File.absolute(args[0])) if args.length
|
||||
|
||||
while true
|
||||
try
|
||||
system.stdout.write('coffee> ').flush()
|
||||
result: exports.evalCS(Readline.readline())
|
||||
print(result) if result isnt undefined
|
||||
catch e
|
||||
print(e)...
|
||||
|
||||
# Compile a given CoffeeScript file into JavaScript.
|
||||
exports.compileFile: path =>
|
||||
coffee: OS.popen([coffeePath, "--print", "--no-wrap", path])
|
||||
checkForErrors(coffee)
|
||||
coffee.stdout.read().
|
||||
|
||||
# Compile a string of CoffeeScript into JavaScript.
|
||||
exports.compile: source =>
|
||||
coffee: OS.popen([coffeePath, "--eval", "--no-wrap"])
|
||||
coffee.stdin.write(source).flush().close()
|
||||
checkForErrors(coffee)
|
||||
coffee.stdout.read().
|
||||
|
||||
# Evaluating a string of CoffeeScript first compiles it externally.
|
||||
exports.evalCS: source =>
|
||||
eval(exports.compile(source)).
|
||||
|
||||
# Make a factory for the CoffeeScript environment.
|
||||
exports.makeNarwhalFactory: path =>
|
||||
code: exports.compileFile(path)
|
||||
factoryText: "function(require,exports,module,system,print){" + code + "/**/\n}"
|
||||
if system.engine is "rhino"
|
||||
Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null)
|
||||
else
|
||||
# eval requires parenthesis, but parenthesis break compileFunction.
|
||||
eval("(" + factoryText + ")")..
|
||||
69
lib/coffee_script/narwhal/js/coffee-script.js
Normal file
69
lib/coffee_script/narwhal/js/coffee-script.js
Normal file
@@ -0,0 +1,69 @@
|
||||
(function(){
|
||||
var File, OS, Readline, checkForErrors, coffeePath;
|
||||
// This (javascript) file is generated from lib/coffee_script/narwhal/coffee-script.coffee Executes the `coffee` Ruby program to convert from CoffeeScript
|
||||
// to Javascript. Eventually this will hopefully happen entirely within JS. Require external dependencies.
|
||||
OS = require('os');
|
||||
File = require('file');
|
||||
Readline = require('readline');
|
||||
// The path to the CoffeeScript Compiler.
|
||||
coffeePath = File.path(module.path).dirname().dirname().dirname().dirname().dirname().join('bin', 'coffee');
|
||||
// Our general-purpose error handler.
|
||||
checkForErrors = function(coffeeProcess) {
|
||||
if (coffeeProcess.wait() === 0) {
|
||||
return true;
|
||||
}
|
||||
system.stderr.print(coffeeProcess.stderr.read());
|
||||
throw new Error("CoffeeScript compile error");
|
||||
};
|
||||
// Run a simple REPL, round-tripping to the CoffeeScript compiler for every
|
||||
// command.
|
||||
exports.run = function(args) {
|
||||
var result;
|
||||
args.shift();
|
||||
if (args.length) {
|
||||
return require(File.absolute(args[0]));
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
system.stdout.write('coffee> ').flush();
|
||||
result = exports.evalCS(Readline.readline());
|
||||
if (result !== undefined) {
|
||||
print(result);
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Compile a given CoffeeScript file into JavaScript.
|
||||
exports.compileFile = function(path) {
|
||||
var coffee;
|
||||
coffee = OS.popen([coffeePath, "--print", "--no-wrap", path]);
|
||||
checkForErrors(coffee);
|
||||
return coffee.stdout.read();
|
||||
};
|
||||
// Compile a string of CoffeeScript into JavaScript.
|
||||
exports.compile = function(source) {
|
||||
var coffee;
|
||||
coffee = OS.popen([coffeePath, "--eval", "--no-wrap"]);
|
||||
coffee.stdin.write(source).flush().close();
|
||||
checkForErrors(coffee);
|
||||
return coffee.stdout.read();
|
||||
};
|
||||
// Evaluating a string of CoffeeScript first compiles it externally.
|
||||
exports.evalCS = function(source) {
|
||||
return eval(exports.compile(source));
|
||||
};
|
||||
// Make a factory for the CoffeeScript environment.
|
||||
exports.makeNarwhalFactory = function(path) {
|
||||
var code, factoryText;
|
||||
code = exports.compileFile(path);
|
||||
factoryText = "function(require,exports,module,system,print){" + code + "/**/\n}";
|
||||
if (system.engine === "rhino") {
|
||||
return Packages.org.mozilla.javascript.Context.getCurrentContext().compileFunction(global, factoryText, path, 0, null);
|
||||
} else {
|
||||
// eval requires parenthesis, but parenthesis break compileFunction.
|
||||
return eval("(" + factoryText + ")");
|
||||
}
|
||||
};
|
||||
})();
|
||||
3
lib/coffee_script/narwhal/js/launcher.js
Normal file
3
lib/coffee_script/narwhal/js/launcher.js
Normal file
@@ -0,0 +1,3 @@
|
||||
(function(){
|
||||
require("coffee-script").run(system.args);
|
||||
})();
|
||||
20
lib/coffee_script/narwhal/js/loader.js
Normal file
20
lib/coffee_script/narwhal/js/loader.js
Normal file
@@ -0,0 +1,20 @@
|
||||
(function(){
|
||||
var coffeescript, factories, loader;
|
||||
// This (javascript) file is generated from lib/coffee_script/narwhal/loader.coffee
|
||||
coffeescript = null;
|
||||
factories = {
|
||||
};
|
||||
loader = {
|
||||
// Reload the coffee-script environment from source.
|
||||
reload: function(topId, path) {
|
||||
coffeescript = coffeescript || require('coffee-script');
|
||||
factories[topId] = coffeescript.makeNarwhalFactory(path);
|
||||
return factories[topId];
|
||||
},
|
||||
// Ensure that the coffee-script environment is loaded.
|
||||
load: function(topId, path) {
|
||||
return factories[topId] = factories[topId] || this.reload(topId, path);
|
||||
}
|
||||
};
|
||||
require.loader.loaders.unshift([".coffee", loader]);
|
||||
})();
|
||||
1
lib/coffee_script/narwhal/launcher.coffee
Normal file
1
lib/coffee_script/narwhal/launcher.coffee
Normal file
@@ -0,0 +1 @@
|
||||
require("coffee-script").run(system.args)
|
||||
19
lib/coffee_script/narwhal/loader.coffee
Normal file
19
lib/coffee_script/narwhal/loader.coffee
Normal file
@@ -0,0 +1,19 @@
|
||||
# This (javascript) file is generated from lib/coffee_script/narwhal/loader.coffee
|
||||
|
||||
coffeescript: null
|
||||
factories: {}
|
||||
|
||||
loader: {
|
||||
|
||||
# Reload the coffee-script environment from source.
|
||||
reload: topId, path =>
|
||||
coffeescript ||= require('coffee-script')
|
||||
factories[topId]: coffeescript.makeNarwhalFactory(path).
|
||||
|
||||
# Ensure that the coffee-script environment is loaded.
|
||||
load: topId, path =>
|
||||
factories[topId] ||= this.reload(topId, path).
|
||||
|
||||
}
|
||||
|
||||
require.loader.loaders.unshift([".coffee", loader])
|
||||
@@ -75,17 +75,20 @@ module CoffeeScript
|
||||
end
|
||||
|
||||
# If this is the top-level Expressions, wrap everything in a safety closure.
|
||||
def root_compile
|
||||
code = compile(:indent => TAB, :scope => Scope.new)
|
||||
def root_compile(o={})
|
||||
indent = o[:no_wrap] ? '' : TAB
|
||||
code = compile(o.merge(:indent => indent, :scope => Scope.new), o[:no_wrap] ? nil : :code)
|
||||
code.gsub!(STRIP_TRAILING_WHITESPACE, '')
|
||||
"(function(){\n#{code}\n})();"
|
||||
o[:no_wrap] ? code : "(function(){\n#{code}\n})();"
|
||||
end
|
||||
|
||||
# The extra fancy is to handle pushing down returns and assignments
|
||||
# recursively to the final lines of inner statements.
|
||||
def compile(options={})
|
||||
return root_compile unless options[:scope]
|
||||
code = @expressions.map { |node|
|
||||
# Variables first defined within the Expressions body have their
|
||||
# declarations pushed up to the top scope.
|
||||
def compile(options={}, parent=nil)
|
||||
return root_compile(options) unless options[:scope]
|
||||
compiled = @expressions.map do |node|
|
||||
o = super(options)
|
||||
if last?(node) && (o[:return] || o[:assign])
|
||||
if o[:return]
|
||||
@@ -98,14 +101,17 @@ module CoffeeScript
|
||||
if node.statement? || node.custom_assign?
|
||||
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
|
||||
else
|
||||
"#{o[:indent]}#{AssignNode.new(ValueNode.new(LiteralNode.new(o[:assign])), node).compile(o)};"
|
||||
"#{o[:indent]}#{AssignNode.new(o[:assign], node).compile(o)};"
|
||||
end
|
||||
end
|
||||
else
|
||||
o.delete(:return) and o.delete(:assign)
|
||||
"#{o[:indent]}#{node.compile(o)}#{node.line_ending}"
|
||||
end
|
||||
}.join("\n")
|
||||
end
|
||||
scope = options[:scope]
|
||||
declarations = scope.any_declared? && parent == :code ? "#{options[:indent]}var #{scope.declared_variables.join(', ')};\n" : ''
|
||||
code = declarations + compiled.join("\n")
|
||||
write(code)
|
||||
end
|
||||
end
|
||||
@@ -209,10 +215,29 @@ module CoffeeScript
|
||||
|
||||
def compile_super(args, o)
|
||||
methname = o[:last_assign].sub(LEADING_DOT, '')
|
||||
"this.constructor.prototype.#{methname}.call(this, #{args})"
|
||||
arg_part = args.empty? ? '' : ", #{args}"
|
||||
"#{o[:proto_assign]}.__superClass__.#{methname}.call(this#{arg_part})"
|
||||
end
|
||||
end
|
||||
|
||||
# Node to extend an object's prototype with an ancestor object.
|
||||
# After goog.inherits from the Closure Library.
|
||||
class ExtendsNode < Node
|
||||
attr_reader :sub_object, :super_object
|
||||
|
||||
def initialize(sub_object, super_object)
|
||||
@sub_object, @super_object = sub_object, super_object
|
||||
end
|
||||
|
||||
def compile(o={})
|
||||
sub, sup = @sub_object.compile(o), @super_object.compile(o)
|
||||
"#{sub}.__superClass__ = #{sup}.prototype;\n#{o[:indent]}" +
|
||||
"#{sub}.prototype = new #{sup}();\n#{o[:indent]}" +
|
||||
"#{sub}.prototype.constructor = #{sub}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A value, indexed or dotted into, or vanilla.
|
||||
class ValueNode < Node
|
||||
attr_reader :literal, :properties, :last
|
||||
@@ -280,27 +305,57 @@ module CoffeeScript
|
||||
end
|
||||
end
|
||||
|
||||
# A range literal. Ranges can be used to extract portions (slices) of arrays,
|
||||
# or to specify a range for array comprehensions.
|
||||
# RangeNodes get expanded into the equivalent array, if not used to index
|
||||
# a slice or define an array comprehension.
|
||||
class RangeNode
|
||||
attr_reader :from, :to
|
||||
|
||||
def initialize(from, to, exclusive=false)
|
||||
@from, @to, @exclusive = from, to, exclusive
|
||||
end
|
||||
|
||||
def exclusive?
|
||||
@exclusive
|
||||
end
|
||||
|
||||
# TODO -- figure out if we can detect if a range runs negative.
|
||||
def downward?
|
||||
|
||||
end
|
||||
|
||||
# TODO -- figure out if we can expand ranges into the corresponding array,
|
||||
# as an expression, reliably.
|
||||
def compile(o={})
|
||||
write()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# An array slice literal. Unlike JavaScript's Array#slice, the second parameter
|
||||
# specifies the index of the end of the slice (just like the first parameter)
|
||||
# is the index of the beginning.
|
||||
class SliceNode < Node
|
||||
attr_reader :from, :to
|
||||
attr_reader :range
|
||||
|
||||
def initialize(from, to)
|
||||
@from, @to = from, to
|
||||
def initialize(range)
|
||||
@range = range
|
||||
end
|
||||
|
||||
def compile(o={})
|
||||
o = super(o)
|
||||
write(".slice(#{@from.compile(o)}, #{@to.compile(o)} + 1)")
|
||||
o = super(o)
|
||||
from = @range.from.compile(o)
|
||||
to = @range.to.compile(o)
|
||||
plus_part = @range.exclusive? ? '' : ' + 1'
|
||||
write(".slice(#{from}, #{to}#{plus_part})")
|
||||
end
|
||||
end
|
||||
|
||||
# Setting the value of a local variable, or the value of an object property.
|
||||
class AssignNode < Node
|
||||
LEADING_VAR = /\Avar\s+/
|
||||
PROTO_ASSIGN = /\A(\S+)\.prototype/
|
||||
|
||||
statement
|
||||
custom_return
|
||||
|
||||
attr_reader :variable, :value, :context
|
||||
@@ -315,18 +370,16 @@ module CoffeeScript
|
||||
|
||||
def compile(o={})
|
||||
o = super(o)
|
||||
name = @variable.respond_to?(:compile) ? @variable.compile(o) : @variable
|
||||
last = @variable.respond_to?(:last) ? @variable.last.to_s : name.to_s
|
||||
o = o.merge(:assign => name, :last_assign => last)
|
||||
name = @variable.compile(o)
|
||||
last = @variable.last.to_s
|
||||
proto = name[PROTO_ASSIGN, 1]
|
||||
o = o.merge(:assign => @variable, :last_assign => last, :proto_assign => proto)
|
||||
postfix = o[:return] ? ";\n#{o[:indent]}return #{name}" : ''
|
||||
return write("#{@variable}: #{@value.compile(o)}") if @context == :object
|
||||
return write("#{name}: #{@value.compile(o)}") if @context == :object
|
||||
return write("#{name} = #{@value.compile(o)}#{postfix}") if @variable.properties? && !@value.custom_assign?
|
||||
defined = o[:scope].find(name)
|
||||
def_part = defined || @variable.properties? ? "" : "var #{name};\n#{o[:indent]}"
|
||||
return write(def_part + @value.compile(o)) if @value.custom_assign?
|
||||
def_part = defined ? name : "var #{name}"
|
||||
val_part = @value.compile(o).sub(LEADING_VAR, '')
|
||||
write("#{def_part} = #{val_part}#{postfix}")
|
||||
o[:scope].find(name) unless @variable.properties?
|
||||
return write(@value.compile(o)) if @value.custom_assign?
|
||||
write("#{name} = #{@value.compile(o)}#{postfix}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -339,10 +392,10 @@ module CoffeeScript
|
||||
'and' => '&&',
|
||||
'or' => '||',
|
||||
'is' => '===',
|
||||
"aint" => "!==",
|
||||
'not' => '!',
|
||||
"isnt" => "!==",
|
||||
'not' => '!'
|
||||
}
|
||||
CONDITIONALS = ['||:', '&&:']
|
||||
CONDITIONALS = ['||=', '&&=']
|
||||
PREFIX_OPERATORS = ['typeof', 'delete']
|
||||
|
||||
attr_reader :operator, :first, :second
|
||||
@@ -393,8 +446,9 @@ module CoffeeScript
|
||||
indent = o[:indent]
|
||||
o[:indent] += TAB
|
||||
o.delete(:assign)
|
||||
@params.each {|id| o[:scope].find(id.to_s) }
|
||||
code = @body.compile(o)
|
||||
o.delete(:no_wrap)
|
||||
@params.each {|id| o[:scope].parameter(id.to_s) }
|
||||
code = @body.compile(o, :code)
|
||||
write("function(#{@params.join(', ')}) {\n#{code}\n#{indent}}")
|
||||
end
|
||||
end
|
||||
@@ -455,6 +509,7 @@ module CoffeeScript
|
||||
|
||||
def compile(o={})
|
||||
o = super(o)
|
||||
o.delete(:return)
|
||||
indent = o[:indent] + TAB
|
||||
cond = @condition.compile(o.merge(:no_paren => true))
|
||||
write("while (#{cond}) {\n#{@body.compile(o.merge(:indent => indent))}\n#{o[:indent]}}")
|
||||
@@ -482,32 +537,38 @@ module CoffeeScript
|
||||
|
||||
def compile(o={})
|
||||
o = super(o)
|
||||
scope = o[:scope]
|
||||
name_found = scope.find(@name)
|
||||
index_found = @index && scope.find(@index)
|
||||
svar = scope.free_variable
|
||||
ivar = scope.free_variable
|
||||
lvar = scope.free_variable
|
||||
name_part = name_found ? @name : "var #{@name}"
|
||||
index_name = @index ? (index_found ? @index : "var #{@index}") : nil
|
||||
source_part = "var #{svar} = #{@source.compile(o)};"
|
||||
for_part = "var #{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
|
||||
var_part = "\n#{o[:indent] + TAB}#{name_part} = #{svar}[#{ivar}];\n"
|
||||
index_part = @index ? "#{o[:indent] + TAB}#{index_name} = #{ivar};\n" : ''
|
||||
range = @source.is_a?(RangeNode)
|
||||
scope = o[:scope]
|
||||
name_found = scope.find(@name)
|
||||
index_found = @index && scope.find(@index)
|
||||
svar = scope.free_variable
|
||||
ivar = range ? name : scope.free_variable
|
||||
lvar = scope.free_variable
|
||||
rvar = scope.free_variable
|
||||
index_name = @index ? @index : nil
|
||||
if range
|
||||
source_part = ''
|
||||
operator = @source.exclusive? ? '<' : '<='
|
||||
index_var = scope.free_variable
|
||||
for_part = "#{index_var}=0, #{ivar}=#{@source.from.compile(o)}, #{lvar}=#{@source.to.compile(o)}; #{ivar}#{operator}#{lvar}; #{ivar}++, #{index_var}++"
|
||||
var_part = ''
|
||||
index_part = ''
|
||||
else
|
||||
index_var = nil
|
||||
source_part = "#{svar} = #{@source.compile(o)};\n#{o[:indent]}"
|
||||
for_part = "#{ivar}=0, #{lvar}=#{svar}.length; #{ivar}<#{lvar}; #{ivar}++"
|
||||
var_part = "\n#{o[:indent] + TAB}#{@name} = #{svar}[#{ivar}];"
|
||||
index_part = @index ? "\n#{o[:indent] + TAB}#{index_name} = #{ivar};" : ''
|
||||
end
|
||||
body = @body
|
||||
suffix = ';'
|
||||
set_result = "#{rvar} = [];\n#{o[:indent]}"
|
||||
save_result = "#{rvar}[#{index_var || ivar}] = "
|
||||
return_result = rvar
|
||||
|
||||
set_result = ''
|
||||
save_result = ''
|
||||
return_result = ''
|
||||
body = @body
|
||||
suffix = ';'
|
||||
if o[:return] || o[:assign]
|
||||
rvar = scope.free_variable
|
||||
set_result = "var #{rvar} = [];\n#{o[:indent]}"
|
||||
save_result += "#{rvar}[#{ivar}] = "
|
||||
return_result = rvar
|
||||
return_result = "#{o[:assign]} = #{return_result};" if o[:assign]
|
||||
return_result = "return #{return_result};" if o[:return]
|
||||
return_result = "\n#{o[:indent]}#{return_result}"
|
||||
return_result = "#{o[:assign].compile(o)} = #{return_result}" if o[:assign]
|
||||
return_result = "return #{return_result}" if o[:return]
|
||||
if @filter
|
||||
body = CallNode.new(ValueNode.new(LiteralNode.new(rvar), [AccessorNode.new('push')]), [@body])
|
||||
body = IfNode.new(@filter, body, nil, :statement => true)
|
||||
@@ -518,9 +579,10 @@ module CoffeeScript
|
||||
body = IfNode.new(@filter, @body)
|
||||
end
|
||||
|
||||
return_result = "\n#{o[:indent]}#{return_result};"
|
||||
indent = o[:indent] + TAB
|
||||
body = body.compile(o.merge(:indent => indent))
|
||||
write("#{source_part}\n#{o[:indent]}#{set_result}for (#{for_part}) {#{var_part}#{index_part}#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}")
|
||||
write("#{source_part}#{set_result}for (#{for_part}) {#{var_part}#{index_part}\n#{indent}#{save_result}#{body}#{suffix}\n#{o[:indent]}}#{return_result}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -567,7 +629,9 @@ module CoffeeScript
|
||||
end
|
||||
end
|
||||
|
||||
# An extra set of parenthesis, supplied by the script source.
|
||||
# An extra set of parentheses, supplied by the script source.
|
||||
# You can't wrap parentheses around bits that get compiled into JS statements,
|
||||
# unfortunately.
|
||||
class ParentheticalNode < Node
|
||||
attr_reader :expressions
|
||||
|
||||
@@ -576,7 +640,7 @@ module CoffeeScript
|
||||
end
|
||||
|
||||
def statement?
|
||||
@expressions.statement?
|
||||
@expressions.unwrap.statement?
|
||||
end
|
||||
|
||||
def custom_assign?
|
||||
@@ -588,10 +652,11 @@ module CoffeeScript
|
||||
end
|
||||
|
||||
def compile(o={})
|
||||
raise SyntaxError, "parentheses can't be wrapped around a statement" if statement?
|
||||
o = super(o)
|
||||
compiled = @expressions.compile(o)
|
||||
compiled = compiled[0...-1] if compiled[-1..-1] == ';'
|
||||
write(o[:no_paren] || statement? ? compiled : "(#{compiled})")
|
||||
write(o[:no_paren] ? compiled : "(#{compiled})")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ module CoffeeScript
|
||||
@token_id, @value, @stack = token_id, value, stack
|
||||
end
|
||||
|
||||
def message(source_file=nil)
|
||||
def message
|
||||
line = @value.respond_to?(:line) ? @value.line : "END"
|
||||
line_part = source_file ? "#{source_file}:#{line}:" : "line #{line}:"
|
||||
line_part = "line #{line}:"
|
||||
id_part = @token_id != @value.inspect ? ", unexpected #{@token_id.downcase}" : ""
|
||||
"#{line_part} syntax error for '#{@value.to_s}'#{id_part}"
|
||||
end
|
||||
|
||||
@@ -5,23 +5,29 @@ module CoffeeScript
|
||||
# whether a variable has been seen before or if it needs to be declared.
|
||||
class Scope
|
||||
|
||||
attr_reader :parent, :temp_variable
|
||||
attr_reader :parent, :variables, :temp_variable
|
||||
|
||||
# Initialize a scope with its parent, for lookups up the chain.
|
||||
def initialize(parent=nil)
|
||||
@parent = parent
|
||||
@variables = {}
|
||||
@temp_variable = @parent ? @parent.temp_variable : 'a'
|
||||
@temp_variable = @parent ? @parent.temp_variable : '__a'
|
||||
end
|
||||
|
||||
# Look up a variable in lexical scope, or declare it if not found.
|
||||
def find(name, remote=false)
|
||||
found = check(name, remote)
|
||||
return found if found || remote
|
||||
@variables[name.to_sym] = true
|
||||
@variables[name.to_sym] = :var
|
||||
found
|
||||
end
|
||||
|
||||
# Define a local variable as originating from a parameter in current scope
|
||||
# -- no var required.
|
||||
def parameter(name)
|
||||
@variables[name.to_sym] = :param
|
||||
end
|
||||
|
||||
# Just check to see if a variable has already been declared.
|
||||
def check(name, remote=false)
|
||||
return true if @variables[name.to_sym]
|
||||
@@ -36,10 +42,19 @@ module CoffeeScript
|
||||
# Find an available, short, name for a compiler-generated variable.
|
||||
def free_variable
|
||||
@temp_variable.succ! while check(@temp_variable)
|
||||
@variables[@temp_variable.to_sym] = true
|
||||
@variables[@temp_variable.to_sym] = :var
|
||||
@temp_variable.dup
|
||||
end
|
||||
|
||||
def any_declared?
|
||||
!declared_variables.empty?
|
||||
end
|
||||
|
||||
# Return the list of variables first declared in current scope.
|
||||
def declared_variables
|
||||
@variables.select {|k, v| v == :var }.map {|pair| pair[0].to_s }.sort
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user