mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
Conflicts: lib/coffee-script/lexer.js lib/coffee-script/parser.js lib/coffee-script/rewriter.js src/lexer.coffee src/rewriter.coffee
This commit is contained in:
commit
f375394381
42 changed files with 1011 additions and 508 deletions
2
Cakefile
2
Cakefile
|
@ -161,7 +161,7 @@ task 'bench', 'quick benchmark of compilation time', ->
|
|||
|
||||
# Run the CoffeeScript test suite.
|
||||
runTests = (CoffeeScript) ->
|
||||
require './lib/coffee-script/extensions'
|
||||
CoffeeScript.register()
|
||||
startTime = Date.now()
|
||||
currentFile = null
|
||||
passedTests = 0
|
||||
|
|
2
README
2
README
|
@ -46,5 +46,5 @@
|
|||
The source repository:
|
||||
git://github.com/jashkenas/coffee-script.git
|
||||
|
||||
All contributors are listed here:
|
||||
Top 100 contributors are listed here:
|
||||
http://github.com/jashkenas/coffee-script/contributors
|
||||
|
|
|
@ -1064,6 +1064,11 @@ Expressions
|
|||
is a new book from Packt Publishing that introduces CoffeeScript while
|
||||
walking through the process of building a demonstration web application.
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.manning.com/lee/">CoffeeScript in Action</a>
|
||||
is a new book from Manning Publications that covers CoffeeScript syntax, composition techniques
|
||||
and application development.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>
|
||||
|
|
|
@ -31,7 +31,7 @@ File.open = (path, mode, block) ->
|
|||
# Write.
|
||||
write = (location, data) ->
|
||||
path = new Pathname location
|
||||
throw new Error "Location does not exist" unless fs.existsSync(location)
|
||||
throw new Error "Location does not exist" unless fs.existsSync location
|
||||
|
||||
File.open path, 'w', (file) ->
|
||||
return false if Digest.MD5.hexdigest(file.read()) is data.hash()
|
||||
|
|
|
@ -13,7 +13,7 @@ run_loop = ->
|
|||
wait()
|
||||
|
||||
# Objects:
|
||||
dense_object_literal = {one: 1, two: 2, three: 3}
|
||||
dense_object_literal = one: 1, two: 2, three: 3
|
||||
|
||||
spaced_out_multiline_object =
|
||||
pi: 3.14159
|
||||
|
@ -56,7 +56,7 @@ race = ->
|
|||
run()
|
||||
walk()
|
||||
crawl()
|
||||
if tired then return sleep()
|
||||
return sleep() if tired
|
||||
race()
|
||||
|
||||
# Conditional assignment:
|
||||
|
@ -64,7 +64,7 @@ good or= evil
|
|||
wine and= cheese
|
||||
|
||||
# Nested property access and calls.
|
||||
((moon.turn(360))).shapes[3].move({x: 45, y: 30}).position['top'].offset('x')
|
||||
(moon.turn 360).shapes[3].move(x: 45, y: 30).position['top'].offset('x')
|
||||
|
||||
a = b = c = 5
|
||||
|
||||
|
@ -79,7 +79,7 @@ try
|
|||
dogs_and_cats_living_together()
|
||||
throw "up"
|
||||
catch error
|
||||
print(error)
|
||||
print error
|
||||
finally
|
||||
clean_up()
|
||||
|
||||
|
|
|
@ -22,4 +22,4 @@ binary_search = (items, value) ->
|
|||
console.log 2 is binary_search [10, 20, 30, 40, 50], 30
|
||||
console.log 4 is binary_search [-97, 35, 67, 88, 1200], 1200
|
||||
console.log 0 is binary_search [0, 45, 70], 0
|
||||
console.log(-1 is binary_search [0, 45, 70], 10)
|
||||
console.log -1 is binary_search [0, 45, 70], 10
|
|
@ -1,8 +1,8 @@
|
|||
# A bubble sort implementation, sorting the given array in-place.
|
||||
bubble_sort = (list) ->
|
||||
for i in [0...list.length]
|
||||
for j in [0...list.length - i]
|
||||
[list[j], list[j+1]] = [list[j+1], list[j]] if list[j] > list[j+1]
|
||||
for j in [0...list.length - i] when list[j] > list[j + 1]
|
||||
[list[j], list[j+1]] = [list[j + 1], list[j]]
|
||||
list
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# "Classic" linked list implementation that doesn't keep track of its size.
|
||||
class LinkedList
|
||||
|
||||
->
|
||||
this._head = null # Pointer to the first item in the list.
|
||||
constructor: ->
|
||||
@_head = null # Pointer to the first item in the list.
|
||||
|
||||
|
||||
# Appends some data to the end of the list. This method traverses the existing
|
||||
|
@ -12,10 +12,10 @@ class LinkedList
|
|||
# Create a new node object to wrap the data.
|
||||
node = data: data, next: null
|
||||
|
||||
current = this._head or= node
|
||||
current = @_head or= node
|
||||
|
||||
if this._head isnt node
|
||||
(current = current.next) while current.next
|
||||
if @_head isnt node
|
||||
current = current.next while current.next
|
||||
current.next = node
|
||||
|
||||
this
|
||||
|
@ -27,11 +27,11 @@ class LinkedList
|
|||
# Check for out-of-bounds values.
|
||||
return null if index < 0
|
||||
|
||||
current = this._head or null
|
||||
current = @_head or null
|
||||
i = -1
|
||||
|
||||
# Advance through the list.
|
||||
(current = current.next) while current and index > (i += 1)
|
||||
current = current.next while current and index > ++i
|
||||
|
||||
# Return null if we've reached the end.
|
||||
current and current.data
|
||||
|
@ -43,16 +43,16 @@ class LinkedList
|
|||
# Check for out-of-bounds values.
|
||||
return null if index < 0
|
||||
|
||||
current = this._head or null
|
||||
current = @_head or null
|
||||
i = -1
|
||||
|
||||
# Special case: removing the first item.
|
||||
if index is 0
|
||||
this._head = current.next
|
||||
@_head = current.next
|
||||
else
|
||||
|
||||
# Find the right location.
|
||||
([previous, current] = [current, current.next]) while index > (i += 1)
|
||||
[previous, current] = [current, current.next] while index > ++i
|
||||
|
||||
# Skip over the item to remove.
|
||||
previous.next = current.next
|
||||
|
@ -63,7 +63,7 @@ class LinkedList
|
|||
|
||||
# Calculate the number of items in the list.
|
||||
size: ->
|
||||
current = this._head
|
||||
current = @_head
|
||||
count = 0
|
||||
|
||||
while current
|
||||
|
@ -76,7 +76,7 @@ class LinkedList
|
|||
# Convert the list into an array.
|
||||
toArray: ->
|
||||
result = []
|
||||
current = this._head
|
||||
current = @_head
|
||||
|
||||
while current
|
||||
result.push current.data
|
||||
|
@ -86,7 +86,7 @@ class LinkedList
|
|||
|
||||
|
||||
# The string representation of the linked list.
|
||||
toString: -> this.toArray().toString()
|
||||
toString: -> @toArray().toString()
|
||||
|
||||
|
||||
# Tests.
|
||||
|
|
|
@ -7,13 +7,13 @@ is_valid_identifier = (identifier) ->
|
|||
sum = 0
|
||||
alt = false
|
||||
|
||||
for i in [identifier.length - 1..0] by -1
|
||||
for c in identifier by -1
|
||||
|
||||
# Get the next digit.
|
||||
num = parseInt identifier.charAt(i), 10
|
||||
num = parseInt c, 10
|
||||
|
||||
# If it's not a valid number, abort.
|
||||
return false if isNaN(num)
|
||||
return false if isNaN num
|
||||
|
||||
# If it's an alternate number...
|
||||
if alt
|
||||
|
|
|
@ -3,13 +3,12 @@ merge_sort = (list) ->
|
|||
|
||||
return list if list.length is 1
|
||||
|
||||
result = []
|
||||
pivot = Math.floor list.length / 2
|
||||
left = merge_sort list.slice 0, pivot
|
||||
right = merge_sort list.slice pivot
|
||||
|
||||
while left.length and right.length
|
||||
result.push(if left[0] < right[0] then left.shift() else right.shift())
|
||||
result = while left.length and right.length
|
||||
if left[0] < right[0] then left.shift() else right.shift()
|
||||
|
||||
result.concat(left).concat(right)
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
# Examples from the Poignant Guide.
|
||||
# These are examples of syntax differences between CoffeeScript and Ruby,
|
||||
# they won't run.
|
||||
|
||||
# ['toast', 'cheese', 'wine'].each { |food| print food.capitalize }
|
||||
|
||||
['toast', 'wine', 'cheese'].each (food) -> print food.capitalize()
|
||||
print food.capitalize() for food in ['toast', 'wine', 'cheese']
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -42,13 +45,10 @@ LotteryDraw =
|
|||
play: ->
|
||||
result = LotteryTicket.new_random()
|
||||
winners = {}
|
||||
this.tickets.each (buyer, ticket_list) ->
|
||||
ticket_list.each (ticket) ->
|
||||
score = ticket.score result
|
||||
return if score is 0
|
||||
winners[buyer] or= []
|
||||
winners[buyer].push [ticket, score]
|
||||
this.tickets = {}
|
||||
for buyer, ticketList of @tickets
|
||||
for ticket in ticketList when (score = ticket.score result) isnt 0
|
||||
(winners[buyer] or= []).push [ticket, score]
|
||||
@tickets = {}
|
||||
winners
|
||||
|
||||
|
||||
|
@ -64,7 +64,7 @@ LotteryDraw =
|
|||
|
||||
WishScanner =
|
||||
scan_for_a_wish: ->
|
||||
wish = this.read().detect (thought) -> thought.index('wish: ') is 0
|
||||
wish = @read().detect (thought) -> thought.indexOf('wish: ') is 0
|
||||
wish.replace 'wish: ', ''
|
||||
|
||||
|
||||
|
@ -109,28 +109,28 @@ Creature =
|
|||
|
||||
# This method applies a hit taken during a fight.
|
||||
hit: (damage) ->
|
||||
p_up = Math.rand this.charisma
|
||||
p_up = Math.rand @charisma
|
||||
if p_up % 9 is 7
|
||||
this.life += p_up / 4
|
||||
console.log "[" + this.name + " magick powers up " + p_up + "!]"
|
||||
this.life -= damage
|
||||
if this.life <= 0 then console.log "[" + this.name + " has died.]"
|
||||
@life += p_up / 4
|
||||
console.log "[#{@name} magick powers up #{p_up}!]"
|
||||
@life -= damage
|
||||
if @life <= 0 then console.log "[#{@name} has died.]"
|
||||
|
||||
# This method takes one turn in a fight.
|
||||
fight: (enemy, weapon) ->
|
||||
if this.life <= 0 then return console.log "[" + this.name + "is too dead to fight!]"
|
||||
return console.log "[#{@name} is too dead to fight!]" if @life <= 0
|
||||
|
||||
# Attack the opponent.
|
||||
your_hit = Math.rand this.strength + weapon
|
||||
console.log "[You hit with " + your_hit + "points of damage!]"
|
||||
your_hit = Math.rand @strength + weapon
|
||||
console.log "[You hit with #{your_hit}points of damage!]"
|
||||
enemy.hit your_hit
|
||||
|
||||
# Retaliation.
|
||||
console.log enemy
|
||||
if enemy.life > 0
|
||||
enemy_hit = Math.rand enemy.strength + enemy.weapon
|
||||
console.log "[Your enemy hit with " + enemy_hit + "points of damage!]"
|
||||
this.hit enemy_hit
|
||||
console.log "[Your enemy hit with #{enemy_hit}points of damage!]"
|
||||
@hit enemy_hit
|
||||
|
||||
|
||||
|
||||
|
@ -156,7 +156,7 @@ code_words.each (real, code) -> idea.replace(real, code)
|
|||
# Save the jibberish to a new file
|
||||
print "File encoded. Please enter a name for this idea: "
|
||||
idea_name = gets().strip()
|
||||
File.open "idea-" + idea_name + '.txt', 'w', (file) -> file.write idea
|
||||
File.open "idea-#{idea_name}.txt", 'w', (file) -> file.write idea
|
||||
|
||||
|
||||
|
||||
|
@ -174,8 +174,8 @@ File.open "idea-" + idea_name + '.txt', 'w', (file) -> file.write idea
|
|||
|
||||
wipe_mutterings_from = (sentence) ->
|
||||
throw new Error "cannot wipe mutterings" unless sentence.indexOf
|
||||
while sentence.indexOf('(') >= 0
|
||||
open = sentence.indexOf('(') - 1
|
||||
close = sentence.indexOf(')') + 1
|
||||
sentence = sentence.slice(0, open) + sentence.slice(close, sentence.length)
|
||||
while '(' in sentence
|
||||
open = sentence.indexOf('(')
|
||||
close = sentence.indexOf(')')
|
||||
sentence = "#{sentence[0...open]}#{sentence[close + 1..]}"
|
||||
sentence
|
||||
|
|
|
@ -45,7 +45,7 @@ foods[2]
|
|||
# (key, ' is a ', val) join print.
|
||||
|
||||
for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
|
||||
print key + ' is a ' + val
|
||||
print "#{key} is a #{val}"
|
||||
|
||||
|
||||
# Person = class: /name, /age, /sex.
|
||||
|
@ -54,7 +54,7 @@ for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
|
|||
|
||||
class Person
|
||||
print: ->
|
||||
print 'My name is ' + @name + '.'
|
||||
print "My name is #{@name}."
|
||||
|
||||
|
||||
# p = Person ()
|
||||
|
@ -74,7 +74,7 @@ class Policeman extends Person
|
|||
(@rank) ->
|
||||
|
||||
print: ->
|
||||
print 'My name is ' + @name + " and I'm a " + @rank + '.'
|
||||
print "My name is #{@name} and I'm a #{@rank}."
|
||||
|
||||
print new Policeman 'Constable'
|
||||
|
||||
|
@ -180,7 +180,7 @@ if 3.gender?
|
|||
# session = url query ? at ('session').
|
||||
|
||||
HomePage::get = (url) ->
|
||||
session = url.query.session if url.query?
|
||||
session = url.query?.session
|
||||
|
||||
|
||||
# BTree = class: /left, /right.
|
||||
|
|
|
@ -7,6 +7,6 @@ server = http.createServer (req, res) ->
|
|||
res.write 'Hello, World!'
|
||||
res.end()
|
||||
|
||||
server.listen 3000
|
||||
server.listen PORT = 3000
|
||||
|
||||
console.log "Server running at http://localhost:3000/"
|
||||
console.log "Server running at http://localhost:#{PORT}/"
|
||||
|
|
|
@ -182,6 +182,10 @@
|
|||
}
|
||||
};
|
||||
|
||||
exports.register = function() {
|
||||
return require('./register');
|
||||
};
|
||||
|
||||
exports._compileFile = function(filename, sourceMap) {
|
||||
var answer, err, raw, stripped;
|
||||
if (sourceMap == null) {
|
||||
|
@ -210,6 +214,7 @@
|
|||
token = this.tokens[this.pos++];
|
||||
if (token) {
|
||||
tag = token[0], this.yytext = token[1], this.yylloc = token[2];
|
||||
this.errorToken = token.origin || token;
|
||||
this.yylineno = this.yylloc.first_line;
|
||||
} else {
|
||||
tag = '';
|
||||
|
@ -228,10 +233,12 @@
|
|||
parser.yy = require('./nodes');
|
||||
|
||||
parser.yy.parseError = function(message, _arg) {
|
||||
var token;
|
||||
var errorLoc, errorText, errorToken, ignored, token, tokens, _ref;
|
||||
token = _arg.token;
|
||||
message = "unexpected " + (token === 1 ? 'end of input' : token);
|
||||
return helpers.throwSyntaxError(message, parser.lexer.yylloc);
|
||||
_ref = parser.lexer, errorToken = _ref.errorToken, tokens = _ref.tokens;
|
||||
ignored = errorToken[0], errorText = errorToken[1], errorLoc = errorToken[2];
|
||||
errorText = errorToken === tokens[tokens.length - 1] ? 'end of input' : helpers.nameWhitespaceCharacter(errorText);
|
||||
return helpers.throwSyntaxError("unexpected " + errorText, errorLoc);
|
||||
};
|
||||
|
||||
formatSourcePosition = function(frame, getSourceMapping) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, forkNode, fs, helpers, hidden, joinTimeout, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs, _ref,
|
||||
var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, findDirectoryIndex, forkNode, fs, helpers, hidden, joinTimeout, mkdirp, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, removeSourceDir, silentUnlink, sourceCode, sources, spawn, timeLog, usage, useWinPathSep, version, wait, watch, watchDir, watchedDirs, writeJs, _ref,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
fs = require('fs');
|
||||
|
@ -117,6 +117,10 @@
|
|||
notSources[source] = true;
|
||||
return;
|
||||
}
|
||||
if (opts.run) {
|
||||
compilePath(findDirectoryIndex(source), topLevel, base);
|
||||
return;
|
||||
}
|
||||
if (opts.watch) {
|
||||
watchDir(source, base);
|
||||
}
|
||||
|
@ -159,6 +163,27 @@
|
|||
}
|
||||
};
|
||||
|
||||
findDirectoryIndex = function(source) {
|
||||
var err, ext, index, _i, _len, _ref1;
|
||||
_ref1 = CoffeeScript.FILE_EXTENSIONS;
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
ext = _ref1[_i];
|
||||
index = path.join(source, "index" + ext);
|
||||
try {
|
||||
if ((fs.statSync(index)).isFile()) {
|
||||
return index;
|
||||
}
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.error("Missing index.coffee or index.litcoffee in " + source);
|
||||
return process.exit(1);
|
||||
};
|
||||
|
||||
compileScript = function(file, input, base) {
|
||||
var compiled, err, message, o, options, t, task;
|
||||
if (base == null) {
|
||||
|
@ -178,6 +203,7 @@
|
|||
} else if (o.nodes) {
|
||||
return printLine(CoffeeScript.nodes(t.input, t.options).toString().trim());
|
||||
} else if (o.run) {
|
||||
CoffeeScript.register();
|
||||
return CoffeeScript.run(t.input, t.options);
|
||||
} else if (o.join && t.file !== o.join) {
|
||||
if (helpers.isLiterate(file)) {
|
||||
|
@ -246,25 +272,24 @@
|
|||
};
|
||||
|
||||
watch = function(source, base) {
|
||||
var compile, compileTimeout, e, prevStats, rewatch, watchErr, watcher;
|
||||
var compile, compileTimeout, err, prevStats, rewatch, startWatcher, watchErr, watcher;
|
||||
watcher = null;
|
||||
prevStats = null;
|
||||
compileTimeout = null;
|
||||
watchErr = function(e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
if (sources.indexOf(source) === -1) {
|
||||
watchErr = function(err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
if (__indexOf.call(sources, source) < 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
rewatch();
|
||||
return compile();
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
removeSource(source, base, true);
|
||||
removeSource(source, base);
|
||||
return compileJoin();
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
compile = function() {
|
||||
clearTimeout(compileTimeout);
|
||||
|
@ -287,26 +312,39 @@
|
|||
});
|
||||
});
|
||||
};
|
||||
try {
|
||||
watcher = fs.watch(source, compile);
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
watchErr(e);
|
||||
startWatcher = function() {
|
||||
return watcher = fs.watch(source).on('change', compile).on('error', function(err) {
|
||||
if (err.code !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
return rewatch = function() {
|
||||
return removeSource(source, base);
|
||||
});
|
||||
};
|
||||
rewatch = function() {
|
||||
if (watcher != null) {
|
||||
watcher.close();
|
||||
}
|
||||
return watcher = fs.watch(source, compile);
|
||||
return startWatcher();
|
||||
};
|
||||
try {
|
||||
return startWatcher();
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return watchErr(err);
|
||||
}
|
||||
};
|
||||
|
||||
watchDir = function(source, base) {
|
||||
var e, readdirTimeout, watcher;
|
||||
var err, readdirTimeout, startWatcher, stopWatcher, watcher;
|
||||
watcher = null;
|
||||
readdirTimeout = null;
|
||||
try {
|
||||
watchedDirs[source] = true;
|
||||
return watcher = fs.watch(source, function() {
|
||||
startWatcher = function() {
|
||||
return watcher = fs.watch(source).on('error', function(err) {
|
||||
if (err.code !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
return stopWatcher();
|
||||
}).on('change', function() {
|
||||
clearTimeout(readdirTimeout);
|
||||
return readdirTimeout = wait(25, function() {
|
||||
var err, file, files, _i, _len, _results;
|
||||
|
@ -317,8 +355,7 @@
|
|||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
watcher.close();
|
||||
return removeSourceDir(source, base);
|
||||
return stopWatcher();
|
||||
}
|
||||
_results = [];
|
||||
for (_i = 0, _len = files.length; _i < _len; _i++) {
|
||||
|
@ -328,10 +365,18 @@
|
|||
return _results;
|
||||
});
|
||||
});
|
||||
};
|
||||
stopWatcher = function() {
|
||||
watcher.close();
|
||||
return removeSourceDir(source, base);
|
||||
};
|
||||
watchedDirs[source] = true;
|
||||
try {
|
||||
return startWatcher();
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
if (e.code !== 'ENOENT') {
|
||||
throw e;
|
||||
err = _error;
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -345,7 +390,7 @@
|
|||
if (!(source === path.dirname(file))) {
|
||||
continue;
|
||||
}
|
||||
removeSource(file, base, true);
|
||||
removeSource(file, base);
|
||||
sourcesChanged = true;
|
||||
}
|
||||
if (sourcesChanged) {
|
||||
|
@ -353,23 +398,28 @@
|
|||
}
|
||||
};
|
||||
|
||||
removeSource = function(source, base, removeJs) {
|
||||
var err, index, jsPath;
|
||||
removeSource = function(source, base) {
|
||||
var index;
|
||||
index = sources.indexOf(source);
|
||||
sources.splice(index, 1);
|
||||
sourceCode.splice(index, 1);
|
||||
if (removeJs && !opts.join) {
|
||||
jsPath = outputPath(source, base);
|
||||
if (!opts.join) {
|
||||
silentUnlink(outputPath(source, base));
|
||||
silentUnlink(outputPath(source, base, '.map'));
|
||||
return timeLog("removed " + source);
|
||||
}
|
||||
};
|
||||
|
||||
silentUnlink = function(path) {
|
||||
var err, _ref1;
|
||||
try {
|
||||
fs.unlinkSync(jsPath);
|
||||
return fs.unlinkSync(path);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (err.code !== 'ENOENT') {
|
||||
if ((_ref1 = err.code) !== 'ENOENT' && _ref1 !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return timeLog("removed " + source);
|
||||
}
|
||||
};
|
||||
|
||||
outputPath = function(source, base, extension) {
|
||||
|
|
|
@ -152,6 +152,8 @@
|
|||
return new Param($1, null, true);
|
||||
}), o('ParamVar = Expression', function() {
|
||||
return new Param($1, $3);
|
||||
}), o('...', function() {
|
||||
return new Expansion;
|
||||
})
|
||||
],
|
||||
ParamVar: [o('Identifier'), o('ThisProperty'), o('Array'), o('Object')],
|
||||
|
@ -331,7 +333,11 @@
|
|||
return $1.concat($4);
|
||||
})
|
||||
],
|
||||
Arg: [o('Expression'), o('Splat')],
|
||||
Arg: [
|
||||
o('Expression'), o('Splat'), o('...', function() {
|
||||
return new Expansion;
|
||||
})
|
||||
],
|
||||
SimpleArgs: [
|
||||
o('Expression'), o('SimpleArgs , Expression', function() {
|
||||
return [].concat($1, $3);
|
||||
|
@ -540,14 +546,16 @@
|
|||
Operation: [
|
||||
o('UNARY Expression', function() {
|
||||
return new Op($1, $2);
|
||||
}), o('UNARY_MATH Expression', function() {
|
||||
return new Op($1, $2);
|
||||
}), o('- Expression', (function() {
|
||||
return new Op('-', $2);
|
||||
}), {
|
||||
prec: 'UNARY'
|
||||
prec: 'UNARY_MATH'
|
||||
}), o('+ Expression', (function() {
|
||||
return new Op('+', $2);
|
||||
}), {
|
||||
prec: 'UNARY'
|
||||
prec: 'UNARY_MATH'
|
||||
}), o('-- SimpleAssignable', function() {
|
||||
return new Op('--', $2);
|
||||
}), o('++ SimpleAssignable', function() {
|
||||
|
@ -564,6 +572,8 @@
|
|||
return new Op('-', $1, $3);
|
||||
}), o('Expression MATH Expression', function() {
|
||||
return new Op($2, $1, $3);
|
||||
}), o('Expression ** Expression', function() {
|
||||
return new Op($2, $1, $3);
|
||||
}), o('Expression SHIFT Expression', function() {
|
||||
return new Op($2, $1, $3);
|
||||
}), o('Expression COMPARE Expression', function() {
|
||||
|
@ -588,7 +598,7 @@
|
|||
]
|
||||
};
|
||||
|
||||
operators = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF']];
|
||||
operators = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['right', '**'], ['right', 'UNARY_MATH'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['left', 'POST_IF']];
|
||||
|
||||
tokens = [];
|
||||
|
||||
|
|
|
@ -234,4 +234,19 @@
|
|||
return "" + filename + ":" + (first_line + 1) + ":" + (first_column + 1) + ": error: " + this.message + "\n" + codeLine + "\n" + marker;
|
||||
};
|
||||
|
||||
exports.nameWhitespaceCharacter = function(string) {
|
||||
switch (string) {
|
||||
case ' ':
|
||||
return 'space';
|
||||
case '\n':
|
||||
return 'newline';
|
||||
case '\r':
|
||||
return 'carriage return';
|
||||
case '\t':
|
||||
return 'tab';
|
||||
default:
|
||||
return string;
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, repeat, starts, throwSyntaxError, _ref, _ref1,
|
||||
var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, UNARY_MATH, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, repeat, starts, throwSyntaxError, _ref, _ref1,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
_ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES;
|
||||
|
@ -247,8 +247,7 @@
|
|||
if (this.chunk.charAt(0) !== '/') {
|
||||
return 0;
|
||||
}
|
||||
if (match = HEREGEX.exec(this.chunk)) {
|
||||
length = this.heregexToken(match);
|
||||
if (length = this.heregexToken()) {
|
||||
return length;
|
||||
}
|
||||
prev = last(this.tokens);
|
||||
|
@ -259,18 +258,21 @@
|
|||
return 0;
|
||||
}
|
||||
_ref3 = match, match = _ref3[0], regex = _ref3[1], flags = _ref3[2];
|
||||
if (regex === '//') {
|
||||
return 0;
|
||||
}
|
||||
if (regex.slice(0, 2) === '/*') {
|
||||
this.error('regular expressions cannot begin with `*`');
|
||||
}
|
||||
if (regex === '//') {
|
||||
regex = '/(?:)/';
|
||||
}
|
||||
this.token('REGEX', "" + regex + flags, 0, match.length);
|
||||
return match.length;
|
||||
};
|
||||
|
||||
Lexer.prototype.heregexToken = function(match) {
|
||||
var body, flags, flagsOffset, heregex, plusToken, prev, re, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
|
||||
Lexer.prototype.heregexToken = function() {
|
||||
var body, flags, flagsOffset, heregex, match, plusToken, prev, re, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
|
||||
if (!(match = HEREGEX.exec(this.chunk))) {
|
||||
return 0;
|
||||
}
|
||||
heregex = match[0], body = match[1], flags = match[2];
|
||||
if (0 > body.indexOf('#{')) {
|
||||
re = this.escapeLines(body.replace(HEREGEX_OMIT, '$1$2').replace(/\//g, '\\/'), true);
|
||||
|
@ -354,34 +356,39 @@
|
|||
this.indents.push(diff);
|
||||
this.ends.push('OUTDENT');
|
||||
this.outdebt = this.indebt = 0;
|
||||
this.indent = size;
|
||||
} else if (size < this.baseIndent) {
|
||||
this.error('missing indentation', indent.length);
|
||||
} else {
|
||||
this.indebt = 0;
|
||||
this.outdentToken(this.indent - size, noNewlines, indent.length);
|
||||
}
|
||||
this.indent = size;
|
||||
return indent.length;
|
||||
};
|
||||
|
||||
Lexer.prototype.outdentToken = function(moveOut, noNewlines, outdentLength) {
|
||||
var dent, len;
|
||||
var decreasedIndent, dent, lastIndent, _ref2;
|
||||
decreasedIndent = this.indent - moveOut;
|
||||
while (moveOut > 0) {
|
||||
len = this.indents.length - 1;
|
||||
if (this.indents[len] === void 0) {
|
||||
lastIndent = this.indents[this.indents.length - 1];
|
||||
if (!lastIndent) {
|
||||
moveOut = 0;
|
||||
} else if (this.indents[len] === this.outdebt) {
|
||||
} else if (lastIndent === this.outdebt) {
|
||||
moveOut -= this.outdebt;
|
||||
this.outdebt = 0;
|
||||
} else if (this.indents[len] < this.outdebt) {
|
||||
this.outdebt -= this.indents[len];
|
||||
moveOut -= this.indents[len];
|
||||
} else if (lastIndent < this.outdebt) {
|
||||
this.outdebt -= lastIndent;
|
||||
moveOut -= lastIndent;
|
||||
} else {
|
||||
dent = this.indents.pop() + this.outdebt;
|
||||
moveOut -= dent;
|
||||
if (outdentLength && (_ref2 = this.chunk[outdentLength], __indexOf.call(INDENTABLE_CLOSERS, _ref2) >= 0)) {
|
||||
decreasedIndent -= dent - moveOut;
|
||||
moveOut = dent;
|
||||
}
|
||||
this.outdebt = 0;
|
||||
this.pair('OUTDENT');
|
||||
this.token('OUTDENT', dent, 0, outdentLength);
|
||||
this.token('OUTDENT', moveOut, 0, outdentLength);
|
||||
moveOut -= dent;
|
||||
}
|
||||
}
|
||||
if (dent) {
|
||||
|
@ -393,6 +400,7 @@
|
|||
if (!(this.tag() === 'TERMINATOR' || noNewlines)) {
|
||||
this.token('TERMINATOR', '\n', outdentLength, 0);
|
||||
}
|
||||
this.indent = decreasedIndent;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -462,6 +470,8 @@
|
|||
tag = 'COMPOUND_ASSIGN';
|
||||
} else if (__indexOf.call(UNARY, value) >= 0) {
|
||||
tag = 'UNARY';
|
||||
} else if (__indexOf.call(UNARY_MATH, value) >= 0) {
|
||||
tag = 'UNARY_MATH';
|
||||
} else if (__indexOf.call(SHIFT, value) >= 0) {
|
||||
tag = 'SHIFT';
|
||||
} else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) {
|
||||
|
@ -591,14 +601,14 @@
|
|||
};
|
||||
|
||||
Lexer.prototype.interpolateString = function(str, options) {
|
||||
var column, expr, heredoc, i, inner, interpolated, len, letter, lexedLength, line, locationToken, nested, offsetInChunk, pi, plusToken, popped, regex, rparen, strOffset, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
|
||||
var column, errorToken, expr, heredoc, i, inner, interpolated, len, letter, lexedLength, line, locationToken, nested, offsetInChunk, pi, plusToken, popped, regex, rparen, strOffset, tag, token, tokens, value, _i, _len, _ref2, _ref3, _ref4;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
heredoc = options.heredoc, regex = options.regex, offsetInChunk = options.offsetInChunk, strOffset = options.strOffset, lexedLength = options.lexedLength;
|
||||
offsetInChunk = offsetInChunk || 0;
|
||||
strOffset = strOffset || 0;
|
||||
lexedLength = lexedLength || str.length;
|
||||
offsetInChunk || (offsetInChunk = 0);
|
||||
strOffset || (strOffset = 0);
|
||||
lexedLength || (lexedLength = str.length);
|
||||
tokens = [];
|
||||
pi = 0;
|
||||
i = -1;
|
||||
|
@ -613,6 +623,9 @@
|
|||
if (pi < i) {
|
||||
tokens.push(this.makeToken('NEOSTRING', str.slice(pi, i), strOffset + pi));
|
||||
}
|
||||
if (!errorToken) {
|
||||
errorToken = this.makeToken('', 'string interpolation', offsetInChunk + i + 1, 2);
|
||||
}
|
||||
inner = expr.slice(1, -1);
|
||||
if (inner.length) {
|
||||
_ref2 = this.getLineAndColumnFromChunk(strOffset + i + 1), line = _ref2[0], column = _ref2[1];
|
||||
|
@ -649,7 +662,7 @@
|
|||
tokens.unshift(this.makeToken('NEOSTRING', '', offsetInChunk));
|
||||
}
|
||||
if (interpolated = tokens.length > 1) {
|
||||
this.token('(', '(', offsetInChunk, 0);
|
||||
this.token('(', '(', offsetInChunk, 0, errorToken);
|
||||
}
|
||||
for (i = _i = 0, _len = tokens.length; _i < _len; i = ++_i) {
|
||||
token = tokens[i];
|
||||
|
@ -685,13 +698,12 @@
|
|||
};
|
||||
|
||||
Lexer.prototype.pair = function(tag) {
|
||||
var size, wanted;
|
||||
var wanted;
|
||||
if (tag !== (wanted = last(this.ends))) {
|
||||
if ('OUTDENT' !== wanted) {
|
||||
this.error("unmatched " + tag);
|
||||
}
|
||||
this.indent -= size = last(this.indents);
|
||||
this.outdentToken(size, true);
|
||||
this.outdentToken(last(this.indents), true);
|
||||
return this.pair(tag);
|
||||
}
|
||||
return this.ends.pop();
|
||||
|
@ -734,9 +746,12 @@
|
|||
return token;
|
||||
};
|
||||
|
||||
Lexer.prototype.token = function(tag, value, offsetInChunk, length) {
|
||||
Lexer.prototype.token = function(tag, value, offsetInChunk, length, origin) {
|
||||
var token;
|
||||
token = this.makeToken(tag, value, offsetInChunk, length);
|
||||
if (origin) {
|
||||
token.origin = origin;
|
||||
}
|
||||
this.tokens.push(token);
|
||||
return token;
|
||||
};
|
||||
|
@ -753,7 +768,7 @@
|
|||
|
||||
Lexer.prototype.unfinished = function() {
|
||||
var _ref2;
|
||||
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
|
||||
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === 'UNARY_MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === '**' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
|
||||
};
|
||||
|
||||
Lexer.prototype.removeNewlines = function(str) {
|
||||
|
@ -851,7 +866,7 @@
|
|||
|
||||
HEREDOC = /^("""|''')((?:\\[\s\S]|[^\\])*?)(?:\n[^\n\S]*)?\1/;
|
||||
|
||||
OPERATOR = /^(?:[-=]>\*?|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?(\.|::)|\.{2,3})/;
|
||||
OPERATOR = /^(?:[-=]>\*?|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>*\/%])\2=?|\?(\.|::)|\.{2,3})/;
|
||||
|
||||
WHITESPACE = /^[^\n\S]+/;
|
||||
|
||||
|
@ -881,9 +896,11 @@
|
|||
|
||||
TRAILING_SPACES = /\s+$/;
|
||||
|
||||
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
|
||||
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=', '**=', '//=', '%%='];
|
||||
|
||||
UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO', 'YIELD', 'YIELD*'];
|
||||
UNARY = ['NEW', 'TYPEOF', 'DELETE', 'DO', 'YIELD', 'YIELD*'];
|
||||
|
||||
UNARY_MATH = ['!', '~'];
|
||||
|
||||
LOGIC = ['&&', '||', '&', '|', '^'];
|
||||
|
||||
|
@ -891,7 +908,7 @@
|
|||
|
||||
COMPARE = ['==', '!=', '<', '>', '<=', '>='];
|
||||
|
||||
MATH = ['*', '/', '%'];
|
||||
MATH = ['*', '/', '%', '//', '%%'];
|
||||
|
||||
RELATION = ['IN', 'OF', 'INSTANCEOF'];
|
||||
|
||||
|
@ -907,4 +924,6 @@
|
|||
|
||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
|
||||
|
||||
INDENTABLE_CLOSERS = [')', '}', ']'];
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var Access, Arr, Assign, Base, Block, Call, Class, Code, CodeFragment, Comment, Existence, Extends, For, HEXNUM, IDENTIFIER, IDENTIFIER_STR, IS_REGEX, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isLiteralArguments, isLiteralThis, last, locationDataToString, merge, multident, parseNum, some, starts, throwSyntaxError, unfoldSoak, utility, _ref, _ref1,
|
||||
var Access, Arr, Assign, Base, Block, Call, Class, Code, CodeFragment, Comment, Existence, Expansion, Extends, For, HEXNUM, IDENTIFIER, IDENTIFIER_STR, IS_REGEX, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isLiteralArguments, isLiteralThis, last, locationDataToString, merge, multident, parseNum, some, starts, throwSyntaxError, unfoldSoak, utility, _ref, _ref1,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
|
@ -1591,7 +1591,7 @@
|
|||
};
|
||||
|
||||
Assign.prototype.compileNode = function(o) {
|
||||
var answer, compiledName, isValue, match, name, val, varBase, _ref2, _ref3, _ref4;
|
||||
var answer, compiledName, isValue, match, name, val, varBase, _ref2, _ref3, _ref4, _ref5;
|
||||
if (isValue = this.variable instanceof Value) {
|
||||
if (this.variable.isArray() || this.variable.isObject()) {
|
||||
return this.compilePatternMatch(o);
|
||||
|
@ -1602,6 +1602,9 @@
|
|||
if ((_ref2 = this.context) === '||=' || _ref2 === '&&=' || _ref2 === '?=') {
|
||||
return this.compileConditional(o);
|
||||
}
|
||||
if ((_ref3 = this.context) === '**=' || _ref3 === '//=' || _ref3 === '%%=') {
|
||||
return this.compileSpecialMath(o);
|
||||
}
|
||||
}
|
||||
compiledName = this.variable.compileToFragments(o, LEVEL_LIST);
|
||||
name = fragmentsToText(compiledName);
|
||||
|
@ -1622,7 +1625,7 @@
|
|||
if (match[2]) {
|
||||
this.value.klass = match[1];
|
||||
}
|
||||
this.value.name = (_ref3 = (_ref4 = match[3]) != null ? _ref4 : match[4]) != null ? _ref3 : match[5];
|
||||
this.value.name = (_ref4 = (_ref5 = match[3]) != null ? _ref5 : match[4]) != null ? _ref4 : match[5];
|
||||
}
|
||||
val = this.value.compileToFragments(o, LEVEL_LIST);
|
||||
if (this.context === 'object') {
|
||||
|
@ -1637,7 +1640,7 @@
|
|||
};
|
||||
|
||||
Assign.prototype.compilePatternMatch = function(o) {
|
||||
var acc, assigns, code, fragments, i, idx, isObject, ivar, name, obj, objects, olen, ref, rest, splat, top, val, value, vvar, vvarText, _i, _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
|
||||
var acc, assigns, code, expandedIdx, fragments, i, idx, isObject, ivar, name, obj, objects, olen, ref, rest, top, val, value, vvar, vvarText, _i, _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
|
||||
top = o.level === LEVEL_TOP;
|
||||
value = this.value;
|
||||
objects = this.variable.base.objects;
|
||||
|
@ -1669,7 +1672,7 @@
|
|||
vvar = value.compileToFragments(o, LEVEL_LIST);
|
||||
vvarText = fragmentsToText(vvar);
|
||||
assigns = [];
|
||||
splat = false;
|
||||
expandedIdx = false;
|
||||
if (!IDENTIFIER.test(vvarText) || this.variable.assigns(vvarText)) {
|
||||
assigns.push([this.makeCode("" + (ref = o.scope.freeVariable('ref')) + " = ")].concat(__slice.call(vvar)));
|
||||
vvar = [this.makeCode(ref)];
|
||||
|
@ -1689,7 +1692,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!splat && obj instanceof Splat) {
|
||||
if (!expandedIdx && obj instanceof Splat) {
|
||||
name = obj.name.unwrap().value;
|
||||
obj = obj.unwrap();
|
||||
val = "" + olen + " <= " + vvarText + ".length ? " + (utility('slice')) + ".call(" + vvarText + ", " + i;
|
||||
|
@ -1700,14 +1703,26 @@
|
|||
val += ") : []";
|
||||
}
|
||||
val = new Literal(val);
|
||||
splat = "" + ivar + "++";
|
||||
expandedIdx = "" + ivar + "++";
|
||||
} else if (!expandedIdx && obj instanceof Expansion) {
|
||||
if (rest = olen - i - 1) {
|
||||
if (rest === 1) {
|
||||
expandedIdx = "" + vvarText + ".length - 1";
|
||||
} else {
|
||||
ivar = o.scope.freeVariable('i');
|
||||
val = new Literal("" + ivar + " = " + vvarText + ".length - " + rest);
|
||||
expandedIdx = "" + ivar + "++";
|
||||
assigns.push(val.compileToFragments(o, LEVEL_LIST));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
name = obj.unwrap().value;
|
||||
if (obj instanceof Splat) {
|
||||
obj.error("multiple splats are disallowed in an assignment");
|
||||
if (obj instanceof Splat || obj instanceof Expansion) {
|
||||
obj.error("multiple splats/expansions are disallowed in an assignment");
|
||||
}
|
||||
if (typeof idx === 'number') {
|
||||
idx = new Literal(splat || idx);
|
||||
idx = new Literal(expandedIdx || idx);
|
||||
acc = false;
|
||||
} else {
|
||||
acc = isObject && IDENTIFIER.test(idx.unwrap().value || 0);
|
||||
|
@ -1754,6 +1769,12 @@
|
|||
}
|
||||
};
|
||||
|
||||
Assign.prototype.compileSpecialMath = function(o) {
|
||||
var left, right, _ref2;
|
||||
_ref2 = this.variable.cacheReference(o), left = _ref2[0], right = _ref2[1];
|
||||
return new Assign(left, new Op(this.context.slice(0, -1), right, this.value)).compileToFragments(o);
|
||||
};
|
||||
|
||||
Assign.prototype.compileSplice = function(o) {
|
||||
var answer, exclusive, from, fromDecl, fromRef, name, to, valDef, valRef, _ref2, _ref3, _ref4;
|
||||
_ref2 = this.variable.properties.pop().range, from = _ref2.from, to = _ref2.to, exclusive = _ref2.exclusive;
|
||||
|
@ -1835,17 +1856,22 @@
|
|||
_ref3 = this.params;
|
||||
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
|
||||
param = _ref3[_i];
|
||||
if (!(param instanceof Expansion)) {
|
||||
o.scope.parameter(param.asReference(o));
|
||||
}
|
||||
}
|
||||
_ref4 = this.params;
|
||||
for (_j = 0, _len1 = _ref4.length; _j < _len1; _j++) {
|
||||
param = _ref4[_j];
|
||||
if (!param.splat) {
|
||||
if (!(param.splat || param instanceof Expansion)) {
|
||||
continue;
|
||||
}
|
||||
_ref5 = this.params;
|
||||
for (_k = 0, _len2 = _ref5.length; _k < _len2; _k++) {
|
||||
p = _ref5[_k].name;
|
||||
if (!(!(param instanceof Expansion))) {
|
||||
continue;
|
||||
}
|
||||
if (p["this"]) {
|
||||
p = p.properties[0].name;
|
||||
}
|
||||
|
@ -2041,7 +2067,7 @@
|
|||
} else {
|
||||
iterator(obj.base.value, obj.base);
|
||||
}
|
||||
} else {
|
||||
} else if (!(obj instanceof Expansion)) {
|
||||
obj.error("illegal parameter " + (obj.compile()));
|
||||
}
|
||||
}
|
||||
|
@ -2121,6 +2147,29 @@
|
|||
|
||||
})(Base);
|
||||
|
||||
exports.Expansion = Expansion = (function(_super) {
|
||||
__extends(Expansion, _super);
|
||||
|
||||
function Expansion() {
|
||||
return Expansion.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
Expansion.prototype.isComplex = NO;
|
||||
|
||||
Expansion.prototype.compileNode = function(o) {
|
||||
return this.error('Expansion must be used inside a destructuring assignment or parameter list');
|
||||
};
|
||||
|
||||
Expansion.prototype.asReference = function(o) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Expansion.prototype.eachName = function(iterator) {};
|
||||
|
||||
return Expansion;
|
||||
|
||||
})(Base);
|
||||
|
||||
exports.While = While = (function(_super) {
|
||||
__extends(While, _super);
|
||||
|
||||
|
@ -2315,7 +2364,7 @@
|
|||
};
|
||||
|
||||
Op.prototype.compileNode = function(o) {
|
||||
var answer, isChain, _ref2, _ref3;
|
||||
var answer, isChain, lhs, rhs, _ref2, _ref3;
|
||||
isChain = this.isChainable() && this.first.isChainable();
|
||||
if (!isChain) {
|
||||
this.first.front = this.front;
|
||||
|
@ -2332,15 +2381,25 @@
|
|||
if (isChain) {
|
||||
return this.compileChain(o);
|
||||
}
|
||||
if (this.operator === '?') {
|
||||
switch (this.operator) {
|
||||
case '?':
|
||||
return this.compileExistence(o);
|
||||
}
|
||||
answer = [].concat(this.first.compileToFragments(o, LEVEL_OP), this.makeCode(' ' + this.operator + ' '), this.second.compileToFragments(o, LEVEL_OP));
|
||||
case '**':
|
||||
return this.compilePower(o);
|
||||
case '//':
|
||||
return this.compileFloorDivision(o);
|
||||
case '%%':
|
||||
return this.compileModulo(o);
|
||||
default:
|
||||
lhs = this.first.compileToFragments(o, LEVEL_OP);
|
||||
rhs = this.second.compileToFragments(o, LEVEL_OP);
|
||||
answer = [].concat(lhs, this.makeCode(" " + this.operator + " "), rhs);
|
||||
if (o.level <= LEVEL_OP) {
|
||||
return answer;
|
||||
} else {
|
||||
return this.wrapInBraces(answer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Op.prototype.compileChain = function(o) {
|
||||
|
@ -2391,6 +2450,25 @@
|
|||
return this.joinFragmentArrays(parts, '');
|
||||
};
|
||||
|
||||
Op.prototype.compilePower = function(o) {
|
||||
var pow;
|
||||
pow = new Value(new Literal('Math'), [new Access(new Literal('pow'))]);
|
||||
return new Call(pow, [this.first, this.second]).compileToFragments(o);
|
||||
};
|
||||
|
||||
Op.prototype.compileFloorDivision = function(o) {
|
||||
var div, floor;
|
||||
floor = new Value(new Literal('Math'), [new Access(new Literal('floor'))]);
|
||||
div = new Op('/', this.first, this.second);
|
||||
return new Call(floor, [div]).compileToFragments(o);
|
||||
};
|
||||
|
||||
Op.prototype.compileModulo = function(o) {
|
||||
var mod;
|
||||
mod = new Value(new Literal(utility('modulo')));
|
||||
return new Call(mod, [this.first, this.second]).compileToFragments(o);
|
||||
};
|
||||
|
||||
Op.prototype.toString = function(idt) {
|
||||
return Op.__super__.toString.call(this, idt, this.constructor.name + ' ' + this.operator);
|
||||
};
|
||||
|
@ -2413,7 +2491,7 @@
|
|||
|
||||
In.prototype.compileNode = function(o) {
|
||||
var hasSplat, obj, _i, _len, _ref2;
|
||||
if (this.array instanceof Value && this.array.isArray()) {
|
||||
if (this.array instanceof Value && this.array.isArray() && this.array.base.objects.length) {
|
||||
_ref2 = this.array.base.objects;
|
||||
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
|
||||
obj = _ref2[_i];
|
||||
|
@ -2432,9 +2510,6 @@
|
|||
|
||||
In.prototype.compileOrTest = function(o) {
|
||||
var cmp, cnj, i, item, ref, sub, tests, _i, _len, _ref2, _ref3, _ref4;
|
||||
if (this.array.base.objects.length === 0) {
|
||||
return [this.makeCode("" + (!!this.negated))];
|
||||
}
|
||||
_ref2 = this.object.cache(o, LEVEL_OP), sub = _ref2[0], ref = _ref2[1];
|
||||
_ref3 = this.negated ? [' !== ', ' && '] : [' === ', ' || '], cmp = _ref3[0], cnj = _ref3[1];
|
||||
tests = [];
|
||||
|
@ -2646,7 +2721,9 @@
|
|||
}
|
||||
source = this.range ? this.source.base : this.source;
|
||||
scope = o.scope;
|
||||
if (!this.pattern) {
|
||||
name = this.name && (this.name.compile(o, LEVEL_LIST));
|
||||
}
|
||||
index = this.index && (this.index.compile(o, LEVEL_LIST));
|
||||
if (name && !this.pattern) {
|
||||
scope.find(name);
|
||||
|
@ -3001,6 +3078,9 @@
|
|||
indexOf: function() {
|
||||
return "[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }";
|
||||
},
|
||||
modulo: function() {
|
||||
return "function(a, b) { return (a % b + +b) % b; }";
|
||||
},
|
||||
hasProp: function() {
|
||||
return '{}.hasOwnProperty';
|
||||
},
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -51,19 +51,14 @@
|
|||
fork = child_process.fork;
|
||||
binary = require.resolve('../../bin/coffee');
|
||||
child_process.fork = function(path, args, options) {
|
||||
var execPath;
|
||||
if (args == null) {
|
||||
args = [];
|
||||
}
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
execPath = helpers.isCoffee(path) ? binary : null;
|
||||
if (helpers.isCoffee(path)) {
|
||||
if (!Array.isArray(args)) {
|
||||
args = [];
|
||||
options = args || {};
|
||||
args = [];
|
||||
}
|
||||
args = [path].concat(args);
|
||||
path = binary;
|
||||
}
|
||||
options.execPath || (options.execPath = execPath);
|
||||
return fork(path, args, options);
|
||||
};
|
||||
}
|
|
@ -145,7 +145,7 @@
|
|||
console.warn("Node 0.8.0+ required for CoffeeScript REPL");
|
||||
process.exit(1);
|
||||
}
|
||||
require('./extensions');
|
||||
CoffeeScript.register();
|
||||
process.argv = ['coffee'].concat(process.argv.slice(2));
|
||||
opts = merge(replDefaults, opts);
|
||||
repl = nodeREPL.start(opts);
|
||||
|
|
|
@ -4,10 +4,13 @@
|
|||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
__slice = [].slice;
|
||||
|
||||
generate = function(tag, value) {
|
||||
generate = function(tag, value, origin) {
|
||||
var tok;
|
||||
tok = [tag, value];
|
||||
tok.generated = true;
|
||||
if (origin) {
|
||||
tok.origin = origin;
|
||||
}
|
||||
return tok;
|
||||
};
|
||||
|
||||
|
@ -195,12 +198,9 @@
|
|||
return i += 1;
|
||||
};
|
||||
endAllImplicitCalls = function() {
|
||||
var _results;
|
||||
_results = [];
|
||||
while (inImplicitCall()) {
|
||||
_results.push(endImplicitCall());
|
||||
endImplicitCall();
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
startImplicitObject = function(j, startsLine) {
|
||||
var idx;
|
||||
|
@ -215,7 +215,7 @@
|
|||
ours: true
|
||||
}
|
||||
]);
|
||||
tokens.splice(idx, 0, generate('{', generate(new String('{'))));
|
||||
tokens.splice(idx, 0, generate('{', generate(new String('{')), token));
|
||||
if (j == null) {
|
||||
return i += 1;
|
||||
}
|
||||
|
@ -223,7 +223,7 @@
|
|||
endImplicitObject = function(j) {
|
||||
j = j != null ? j : i;
|
||||
stack.pop();
|
||||
tokens.splice(j, 0, generate('}', '}'));
|
||||
tokens.splice(j, 0, generate('}', '}', token));
|
||||
return i += 1;
|
||||
};
|
||||
if (inImplicitCall() && (tag === 'IF' || tag === 'TRY' || tag === 'FINALLY' || tag === 'CATCH' || tag === 'CLASS' || tag === 'SWITCH')) {
|
||||
|
@ -293,16 +293,10 @@
|
|||
startImplicitObject(s, !!startsLine);
|
||||
return forward(2);
|
||||
}
|
||||
if (inImplicitCall() && __indexOf.call(CALL_CLOSERS, tag) >= 0) {
|
||||
if (prevTag === 'OUTDENT') {
|
||||
endImplicitCall();
|
||||
return forward(1);
|
||||
}
|
||||
if (prevToken.newLine) {
|
||||
if (inImplicitCall() && __indexOf.call(CALL_CLOSERS, tag) >= 0 && (prevTag === 'OUTDENT' || prevToken.newLine)) {
|
||||
endAllImplicitCalls();
|
||||
return forward(1);
|
||||
}
|
||||
}
|
||||
if (inImplicitObject() && __indexOf.call(LINEBREAKS, tag) >= 0) {
|
||||
stackTop()[2].sameLine = false;
|
||||
}
|
||||
|
@ -311,7 +305,7 @@
|
|||
_ref4 = stackTop(), stackTag = _ref4[0], stackIdx = _ref4[1], (_ref5 = _ref4[2], sameLine = _ref5.sameLine, startsLine = _ref5.startsLine);
|
||||
if (inImplicitCall() && prevTag !== ',') {
|
||||
endImplicitCall();
|
||||
} else if (inImplicitObject() && sameLine && !startsLine) {
|
||||
} else if (inImplicitObject() && sameLine && tag !== 'TERMINATOR' && prevTag !== ':') {
|
||||
endImplicitObject();
|
||||
} else if (inImplicitObject() && tag === 'TERMINATOR' && prevTag !== ',' && !(startsLine && this.looksObjectish(i + 1))) {
|
||||
endImplicitObject();
|
||||
|
@ -474,7 +468,7 @@
|
|||
|
||||
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS'];
|
||||
|
||||
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'SUPER', 'THROW', '@', '->', '=>', '->*', '=>*', '[', '(', '{', '--', '++'];
|
||||
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'];
|
||||
|
||||
IMPLICIT_UNSPACED_CALL = ['+', '-'];
|
||||
|
||||
|
|
1
register.js
Normal file
1
register.js
Normal file
|
@ -0,0 +1 @@
|
|||
require('./lib/coffee-script/register');
|
|
@ -156,6 +156,8 @@ exports.eval = (code, options = {}) ->
|
|||
else
|
||||
vm.runInContext js, sandbox
|
||||
|
||||
exports.register = -> require './register'
|
||||
|
||||
exports._compileFile = (filename, sourceMap = no) ->
|
||||
raw = fs.readFileSync filename, 'utf8'
|
||||
stripped = if raw.charCodeAt(0) is 0xFEFF then raw.substring 1 else raw
|
||||
|
@ -181,6 +183,7 @@ parser.lexer =
|
|||
token = @tokens[@pos++]
|
||||
if token
|
||||
[tag, @yytext, @yylloc] = token
|
||||
@errorToken = token.origin or token
|
||||
@yylineno = @yylloc.first_line
|
||||
else
|
||||
tag = ''
|
||||
|
@ -196,12 +199,21 @@ parser.yy = require './nodes'
|
|||
# Override Jison's default error handling function.
|
||||
parser.yy.parseError = (message, {token}) ->
|
||||
# Disregard Jison's message, it contains redundant line numer information.
|
||||
message = "unexpected #{if token is 1 then 'end of input' else token}"
|
||||
# Disregard the token, we take its value directly from the lexer in case
|
||||
# the error is caused by a generated token which might refer to its origin.
|
||||
{errorToken, tokens} = parser.lexer
|
||||
[ignored, errorText, errorLoc] = errorToken
|
||||
|
||||
errorText = if errorToken is tokens[tokens.length - 1]
|
||||
'end of input'
|
||||
else
|
||||
helpers.nameWhitespaceCharacter errorText
|
||||
|
||||
# The second argument has a `loc` property, which should have the location
|
||||
# data for this token. Unfortunately, Jison seems to send an outdated `loc`
|
||||
# (from the previous token), so we take the location information directly
|
||||
# from the lexer.
|
||||
helpers.throwSyntaxError message, parser.lexer.yylloc
|
||||
helpers.throwSyntaxError "unexpected #{errorText}", errorLoc
|
||||
|
||||
# Based on http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js
|
||||
# Modified to handle sourceMap
|
||||
|
|
|
@ -104,6 +104,9 @@ compilePath = (source, topLevel, base) ->
|
|||
if path.basename(source) is 'node_modules'
|
||||
notSources[source] = yes
|
||||
return
|
||||
if opts.run
|
||||
compilePath findDirectoryIndex(source), topLevel, base
|
||||
return
|
||||
watchDir source, base if opts.watch
|
||||
try
|
||||
files = fs.readdirSync source
|
||||
|
@ -124,6 +127,16 @@ compilePath = (source, topLevel, base) ->
|
|||
else
|
||||
notSources[source] = yes
|
||||
|
||||
findDirectoryIndex = (source) ->
|
||||
for ext in CoffeeScript.FILE_EXTENSIONS
|
||||
index = path.join source, "index#{ext}"
|
||||
try
|
||||
return index if (fs.statSync index).isFile()
|
||||
catch err
|
||||
throw err unless err.code is 'ENOENT'
|
||||
console.error "Missing index.coffee or index.litcoffee in #{source}"
|
||||
process.exit 1
|
||||
|
||||
# Compile a single source script, containing the given code, according to the
|
||||
# requested options. If evaluating the script directly sets `__filename`,
|
||||
# `__dirname` and `module.filename` to be correct relative to the script's path.
|
||||
|
@ -133,9 +146,13 @@ compileScript = (file, input, base = null) ->
|
|||
try
|
||||
t = task = {file, input, options}
|
||||
CoffeeScript.emit 'compile', task
|
||||
if o.tokens then printTokens CoffeeScript.tokens t.input, t.options
|
||||
else if o.nodes then printLine CoffeeScript.nodes(t.input, t.options).toString().trim()
|
||||
else if o.run then CoffeeScript.run t.input, t.options
|
||||
if o.tokens
|
||||
printTokens CoffeeScript.tokens t.input, t.options
|
||||
else if o.nodes
|
||||
printLine CoffeeScript.nodes(t.input, t.options).toString().trim()
|
||||
else if o.run
|
||||
CoffeeScript.register()
|
||||
CoffeeScript.run t.input, t.options
|
||||
else if o.join and t.file isnt o.join
|
||||
t.input = helpers.invertLiterate t.input if helpers.isLiterate file
|
||||
sourceCode[sources.indexOf(t.file)] = t.input
|
||||
|
@ -186,27 +203,27 @@ compileJoin = ->
|
|||
# time the file is updated. May be used in combination with other options,
|
||||
# such as `--print`.
|
||||
watch = (source, base) ->
|
||||
|
||||
watcher = null
|
||||
prevStats = null
|
||||
compileTimeout = null
|
||||
|
||||
watchErr = (e) ->
|
||||
if e.code is 'ENOENT'
|
||||
return if sources.indexOf(source) is -1
|
||||
watchErr = (err) ->
|
||||
throw err unless err.code is 'ENOENT'
|
||||
return unless source in sources
|
||||
try
|
||||
rewatch()
|
||||
compile()
|
||||
catch e
|
||||
removeSource source, base, yes
|
||||
catch
|
||||
removeSource source, base
|
||||
compileJoin()
|
||||
else throw e
|
||||
|
||||
compile = ->
|
||||
clearTimeout compileTimeout
|
||||
compileTimeout = wait 25, ->
|
||||
fs.stat source, (err, stats) ->
|
||||
return watchErr err if err
|
||||
return rewatch() if prevStats and stats.size is prevStats.size and
|
||||
return rewatch() if prevStats and
|
||||
stats.size is prevStats.size and
|
||||
stats.mtime.getTime() is prevStats.mtime.getTime()
|
||||
prevStats = stats
|
||||
fs.readFile source, (err, code) ->
|
||||
|
@ -214,57 +231,78 @@ watch = (source, base) ->
|
|||
compileScript(source, code.toString(), base)
|
||||
rewatch()
|
||||
|
||||
try
|
||||
watcher = fs.watch source, compile
|
||||
catch e
|
||||
watchErr e
|
||||
startWatcher = ->
|
||||
watcher = fs.watch source
|
||||
.on 'change', compile
|
||||
.on 'error', (err) ->
|
||||
throw err unless err.code is 'EPERM'
|
||||
removeSource source, base
|
||||
|
||||
rewatch = ->
|
||||
watcher?.close()
|
||||
watcher = fs.watch source, compile
|
||||
startWatcher()
|
||||
|
||||
try
|
||||
startWatcher()
|
||||
catch err
|
||||
watchErr err
|
||||
|
||||
# Watch a directory of files for new additions.
|
||||
watchDir = (source, base) ->
|
||||
watcher = null
|
||||
readdirTimeout = null
|
||||
try
|
||||
watchedDirs[source] = yes
|
||||
watcher = fs.watch source, ->
|
||||
|
||||
startWatcher = ->
|
||||
watcher = fs.watch source
|
||||
.on 'error', (err) ->
|
||||
throw err unless err.code is 'EPERM'
|
||||
stopWatcher()
|
||||
.on 'change', ->
|
||||
clearTimeout readdirTimeout
|
||||
readdirTimeout = wait 25, ->
|
||||
try
|
||||
files = fs.readdirSync source
|
||||
catch err
|
||||
throw err unless err.code is 'ENOENT'
|
||||
watcher.close()
|
||||
return removeSourceDir source, base
|
||||
return stopWatcher()
|
||||
for file in files
|
||||
compilePath (path.join source, file), no, base
|
||||
catch e
|
||||
throw e unless e.code is 'ENOENT'
|
||||
|
||||
stopWatcher = ->
|
||||
watcher.close()
|
||||
removeSourceDir source, base
|
||||
|
||||
watchedDirs[source] = yes
|
||||
try
|
||||
startWatcher()
|
||||
catch err
|
||||
throw err unless err.code is 'ENOENT'
|
||||
|
||||
removeSourceDir = (source, base) ->
|
||||
delete watchedDirs[source]
|
||||
sourcesChanged = no
|
||||
for file in sources when source is path.dirname file
|
||||
removeSource file, base, yes
|
||||
removeSource file, base
|
||||
sourcesChanged = yes
|
||||
compileJoin() if sourcesChanged
|
||||
|
||||
# Remove a file from our source list, and source code cache. Optionally remove
|
||||
# the compiled JS version as well.
|
||||
removeSource = (source, base, removeJs) ->
|
||||
removeSource = (source, base) ->
|
||||
index = sources.indexOf source
|
||||
sources.splice index, 1
|
||||
sourceCode.splice index, 1
|
||||
if removeJs and not opts.join
|
||||
jsPath = outputPath source, base
|
||||
try
|
||||
fs.unlinkSync jsPath
|
||||
catch err
|
||||
throw err unless err.code is 'ENOENT'
|
||||
unless opts.join
|
||||
silentUnlink outputPath source, base
|
||||
silentUnlink outputPath source, base, '.map'
|
||||
timeLog "removed #{source}"
|
||||
|
||||
silentUnlink = (path) ->
|
||||
try
|
||||
fs.unlinkSync path
|
||||
catch err
|
||||
throw err unless err.code in ['ENOENT', 'EPERM']
|
||||
|
||||
# Get the corresponding output JavaScript path for a source file.
|
||||
outputPath = (source, base, extension=".js") ->
|
||||
basename = helpers.baseFileName source, yes, useWinPathSep
|
||||
|
|
|
@ -221,6 +221,7 @@ grammar =
|
|||
o 'ParamVar', -> new Param $1
|
||||
o 'ParamVar ...', -> new Param $1, null, on
|
||||
o 'ParamVar = Expression', -> new Param $1, $3
|
||||
o '...', -> new Expansion
|
||||
]
|
||||
|
||||
# Function Parameters
|
||||
|
@ -382,6 +383,7 @@ grammar =
|
|||
Arg: [
|
||||
o 'Expression'
|
||||
o 'Splat'
|
||||
o '...', -> new Expansion
|
||||
]
|
||||
|
||||
# Just simple, comma-separated, required arguments (no fancy syntax). We need
|
||||
|
@ -535,8 +537,9 @@ grammar =
|
|||
# rules are necessary.
|
||||
Operation: [
|
||||
o 'UNARY Expression', -> new Op $1 , $2
|
||||
o '- Expression', (-> new Op '-', $2), prec: 'UNARY'
|
||||
o '+ Expression', (-> new Op '+', $2), prec: 'UNARY'
|
||||
o 'UNARY_MATH Expression', -> new Op $1 , $2
|
||||
o '- Expression', (-> new Op '-', $2), prec: 'UNARY_MATH'
|
||||
o '+ Expression', (-> new Op '+', $2), prec: 'UNARY_MATH'
|
||||
|
||||
o '-- SimpleAssignable', -> new Op '--', $2
|
||||
o '++ SimpleAssignable', -> new Op '++', $2
|
||||
|
@ -550,6 +553,7 @@ grammar =
|
|||
o 'Expression - Expression', -> new Op '-' , $1, $3
|
||||
|
||||
o 'Expression MATH Expression', -> new Op $2, $1, $3
|
||||
o 'Expression ** Expression', -> new Op $2, $1, $3
|
||||
o 'Expression SHIFT Expression', -> new Op $2, $1, $3
|
||||
o 'Expression COMPARE Expression', -> new Op $2, $1, $3
|
||||
o 'Expression LOGIC Expression', -> new Op $2, $1, $3
|
||||
|
@ -586,6 +590,8 @@ operators = [
|
|||
['nonassoc', '++', '--']
|
||||
['left', '?']
|
||||
['right', 'UNARY']
|
||||
['right', '**']
|
||||
['right', 'UNARY_MATH']
|
||||
['left', 'MATH']
|
||||
['left', '+', '-']
|
||||
['left', 'SHIFT']
|
||||
|
@ -596,7 +602,7 @@ operators = [
|
|||
['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
|
||||
['right', 'FORIN', 'FOROF', 'BY', 'WHEN']
|
||||
['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
|
||||
['right', 'POST_IF']
|
||||
['left', 'POST_IF']
|
||||
]
|
||||
|
||||
# Wrapping Up
|
||||
|
|
|
@ -188,3 +188,11 @@ syntaxErrorToString = ->
|
|||
#{codeLine}
|
||||
#{marker}
|
||||
"""
|
||||
|
||||
exports.nameWhitespaceCharacter = (string) ->
|
||||
switch string
|
||||
when ' ' then 'space'
|
||||
when '\n' then 'newline'
|
||||
when '\r' then 'carriage return'
|
||||
when '\t' then 'tab'
|
||||
else string
|
||||
|
|
|
@ -235,21 +235,21 @@ exports.Lexer = class Lexer
|
|||
# JavaScript and Ruby.
|
||||
regexToken: ->
|
||||
return 0 if @chunk.charAt(0) isnt '/'
|
||||
if match = HEREGEX.exec @chunk
|
||||
length = @heregexToken match
|
||||
return length
|
||||
return length if length = @heregexToken()
|
||||
|
||||
prev = last @tokens
|
||||
return 0 if prev and (prev[0] in (if prev.spaced then NOT_REGEX else NOT_SPACED_REGEX))
|
||||
return 0 unless match = REGEX.exec @chunk
|
||||
[match, regex, flags] = match
|
||||
# Avoid conflicts with floor division operator.
|
||||
return 0 if regex is '//'
|
||||
if regex[..1] is '/*' then @error 'regular expressions cannot begin with `*`'
|
||||
if regex is '//' then regex = '/(?:)/'
|
||||
@token 'REGEX', "#{regex}#{flags}", 0, match.length
|
||||
match.length
|
||||
|
||||
# Matches multiline extended regular expressions.
|
||||
heregexToken: (match) ->
|
||||
heregexToken: ->
|
||||
return 0 unless match = HEREGEX.exec @chunk
|
||||
[heregex, body, flags] = match
|
||||
if 0 > body.indexOf '#{'
|
||||
re = @escapeLines body.replace(HEREGEX_OMIT, '$1$2').replace(/\//g, '\\/'), yes
|
||||
|
@ -328,37 +328,43 @@ exports.Lexer = class Lexer
|
|||
@indents.push diff
|
||||
@ends.push 'OUTDENT'
|
||||
@outdebt = @indebt = 0
|
||||
@indent = size
|
||||
else if size < @baseIndent
|
||||
@error 'missing indentation', indent.length
|
||||
else
|
||||
@indebt = 0
|
||||
@outdentToken @indent - size, noNewlines, indent.length
|
||||
@indent = size
|
||||
indent.length
|
||||
|
||||
# Record an outdent token or multiple tokens, if we happen to be moving back
|
||||
# inwards past several recorded indents.
|
||||
# inwards past several recorded indents. Sets new @indent value.
|
||||
outdentToken: (moveOut, noNewlines, outdentLength) ->
|
||||
decreasedIndent = @indent - moveOut
|
||||
while moveOut > 0
|
||||
len = @indents.length - 1
|
||||
if @indents[len] is undefined
|
||||
lastIndent = @indents[@indents.length - 1]
|
||||
if not lastIndent
|
||||
moveOut = 0
|
||||
else if @indents[len] is @outdebt
|
||||
else if lastIndent is @outdebt
|
||||
moveOut -= @outdebt
|
||||
@outdebt = 0
|
||||
else if @indents[len] < @outdebt
|
||||
@outdebt -= @indents[len]
|
||||
moveOut -= @indents[len]
|
||||
else if lastIndent < @outdebt
|
||||
@outdebt -= lastIndent
|
||||
moveOut -= lastIndent
|
||||
else
|
||||
dent = @indents.pop() + @outdebt
|
||||
moveOut -= dent
|
||||
if outdentLength and @chunk[outdentLength] in INDENTABLE_CLOSERS
|
||||
decreasedIndent -= dent - moveOut
|
||||
moveOut = dent
|
||||
@outdebt = 0
|
||||
# pair might call outdentToken, so preserve decreasedIndent
|
||||
@pair 'OUTDENT'
|
||||
@token 'OUTDENT', dent, 0, outdentLength
|
||||
@token 'OUTDENT', moveOut, 0, outdentLength
|
||||
moveOut -= dent
|
||||
@outdebt -= moveOut if dent
|
||||
@tokens.pop() while @value() is ';'
|
||||
|
||||
@token 'TERMINATOR', '\n', outdentLength, 0 unless @tag() is 'TERMINATOR' or noNewlines
|
||||
@indent = decreasedIndent
|
||||
this
|
||||
|
||||
# Matches and consumes non-meaningful whitespace. Tag the previous token
|
||||
|
@ -409,6 +415,7 @@ exports.Lexer = class Lexer
|
|||
else if value in COMPARE then tag = 'COMPARE'
|
||||
else if value in COMPOUND_ASSIGN then tag = 'COMPOUND_ASSIGN'
|
||||
else if value in UNARY then tag = 'UNARY'
|
||||
else if value in UNARY_MATH then tag = 'UNARY_MATH'
|
||||
else if value in SHIFT then tag = 'SHIFT'
|
||||
else if value in LOGIC or value is '?' and prev?.spaced then tag = 'LOGIC'
|
||||
else if prev and not prev.spaced
|
||||
|
@ -521,9 +528,9 @@ exports.Lexer = class Lexer
|
|||
# current chunk.
|
||||
interpolateString: (str, options = {}) ->
|
||||
{heredoc, regex, offsetInChunk, strOffset, lexedLength} = options
|
||||
offsetInChunk = offsetInChunk || 0
|
||||
strOffset = strOffset || 0
|
||||
lexedLength = lexedLength || str.length
|
||||
offsetInChunk ||= 0
|
||||
strOffset ||= 0
|
||||
lexedLength ||= str.length
|
||||
|
||||
# Parse the string.
|
||||
tokens = []
|
||||
|
@ -538,6 +545,8 @@ exports.Lexer = class Lexer
|
|||
continue
|
||||
# NEOSTRING is a fake token. This will be converted to a string below.
|
||||
tokens.push @makeToken('NEOSTRING', str[pi...i], strOffset + pi) if pi < i
|
||||
unless errorToken
|
||||
errorToken = @makeToken '', 'string interpolation', offsetInChunk + i + 1, 2
|
||||
inner = expr[1...-1]
|
||||
if inner.length
|
||||
[line, column] = @getLineAndColumnFromChunk(strOffset + i + 1)
|
||||
|
@ -563,7 +572,9 @@ exports.Lexer = class Lexer
|
|||
# If the first token is not a string, add a fake empty string to the beginning.
|
||||
tokens.unshift @makeToken('NEOSTRING', '', offsetInChunk) unless tokens[0][0] is 'NEOSTRING'
|
||||
|
||||
@token '(', '(', offsetInChunk, 0 if interpolated = tokens.length > 1
|
||||
if interpolated = tokens.length > 1
|
||||
@token '(', '(', offsetInChunk, 0, errorToken
|
||||
|
||||
# Push all the tokens
|
||||
for token, i in tokens
|
||||
[tag, value] = token
|
||||
|
@ -603,8 +614,7 @@ exports.Lexer = class Lexer
|
|||
# el.click((event) ->
|
||||
# el.hide())
|
||||
#
|
||||
@indent -= size = last @indents
|
||||
@outdentToken size, true
|
||||
@outdentToken last(@indents), true
|
||||
return @pair tag
|
||||
@ends.pop()
|
||||
|
||||
|
@ -657,8 +667,9 @@ exports.Lexer = class Lexer
|
|||
# not specified, the length of `value` will be used.
|
||||
#
|
||||
# Returns the new token.
|
||||
token: (tag, value, offsetInChunk, length) ->
|
||||
token: (tag, value, offsetInChunk, length, origin) ->
|
||||
token = @makeToken tag, value, offsetInChunk, length
|
||||
token.origin = origin if origin
|
||||
@tokens.push token
|
||||
token
|
||||
|
||||
|
@ -673,8 +684,8 @@ exports.Lexer = class Lexer
|
|||
# Are we in the midst of an unfinished expression?
|
||||
unfinished: ->
|
||||
LINE_CONTINUER.test(@chunk) or
|
||||
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
|
||||
'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
|
||||
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-',
|
||||
'**', 'SHIFT', 'RELATION', 'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
|
||||
|
||||
# Remove newlines from beginning and (non escaped) from end of string literals.
|
||||
removeNewlines: (str) ->
|
||||
|
@ -778,7 +789,7 @@ OPERATOR = /// ^ (
|
|||
| [-+*/%<>&|^!?=]= # compound assign / compare
|
||||
| >>>=? # zero-fill right shift
|
||||
| ([-+:])\1 # doubles
|
||||
| ([&|<>])\2=? # logic / shift
|
||||
| ([&|<>*/%])\2=? # logic / shift / power / floor division / modulo
|
||||
| \?(\.|::) # soak access
|
||||
| \.{2,3} # range or splat
|
||||
) ///
|
||||
|
@ -831,11 +842,14 @@ TRAILING_SPACES = /\s+$/
|
|||
|
||||
# Compound assignment tokens.
|
||||
COMPOUND_ASSIGN = [
|
||||
'-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='
|
||||
'-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>='
|
||||
'&=', '^=', '|=', '**=', '//=', '%%='
|
||||
]
|
||||
|
||||
# Unary tokens.
|
||||
UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO', 'YIELD', 'YIELD*']
|
||||
UNARY = ['NEW', 'TYPEOF', 'DELETE', 'DO', 'YIELD', 'YIELD*']
|
||||
|
||||
UNARY_MATH = ['!', '~']
|
||||
|
||||
# Logical tokens.
|
||||
LOGIC = ['&&', '||', '&', '|', '^']
|
||||
|
@ -847,7 +861,7 @@ SHIFT = ['<<', '>>', '>>>']
|
|||
COMPARE = ['==', '!=', '<', '>', '<=', '>=']
|
||||
|
||||
# Mathematical tokens.
|
||||
MATH = ['*', '/', '%']
|
||||
MATH = ['*', '/', '%', '//', '%%']
|
||||
|
||||
# Relational tokens that are negatable with `not` prefix.
|
||||
RELATION = ['IN', 'OF', 'INSTANCEOF']
|
||||
|
@ -877,3 +891,6 @@ INDEXABLE = CALLABLE.concat 'NUMBER', 'BOOL', 'NULL', 'UNDEFINED'
|
|||
# occurs at the start of a line. We disambiguate these from trailing whens to
|
||||
# avoid an ambiguity in the grammar.
|
||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']
|
||||
|
||||
# Additional indent in front of these is ignored.
|
||||
INDENTABLE_CLOSERS = [')', '}', ']']
|
||||
|
|
124
src/nodes.coffee
124
src/nodes.coffee
|
@ -1156,6 +1156,7 @@ exports.Assign = class Assign extends Base
|
|||
return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
|
||||
return @compileSplice o if @variable.isSplice()
|
||||
return @compileConditional o if @context in ['||=', '&&=', '?=']
|
||||
return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
|
||||
compiledName = @variable.compileToFragments o, LEVEL_LIST
|
||||
name = fragmentsToText compiledName
|
||||
unless @context
|
||||
|
@ -1205,7 +1206,7 @@ exports.Assign = class Assign extends Base
|
|||
vvar = value.compileToFragments o, LEVEL_LIST
|
||||
vvarText = fragmentsToText vvar
|
||||
assigns = []
|
||||
splat = false
|
||||
expandedIdx = false
|
||||
# Make vvar into a simple variable if it isn't already.
|
||||
if not IDENTIFIER.test(vvarText) or @variable.assigns(vvarText)
|
||||
assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...]
|
||||
|
@ -1224,7 +1225,7 @@ exports.Assign = class Assign extends Base
|
|||
[obj, idx] = new Value(obj.unwrapAll()).cacheReference o
|
||||
else
|
||||
idx = if obj.this then obj.properties[0].name else obj
|
||||
if not splat and obj instanceof Splat
|
||||
if not expandedIdx and obj instanceof Splat
|
||||
name = obj.name.unwrap().value
|
||||
obj = obj.unwrap()
|
||||
val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice' }.call(#{vvarText}, #{i}"
|
||||
|
@ -1234,13 +1235,23 @@ exports.Assign = class Assign extends Base
|
|||
else
|
||||
val += ") : []"
|
||||
val = new Literal val
|
||||
splat = "#{ivar}++"
|
||||
expandedIdx = "#{ivar}++"
|
||||
else if not expandedIdx and obj instanceof Expansion
|
||||
if rest = olen - i - 1
|
||||
if rest is 1
|
||||
expandedIdx = "#{vvarText}.length - 1"
|
||||
else
|
||||
ivar = o.scope.freeVariable 'i'
|
||||
val = new Literal "#{ivar} = #{vvarText}.length - #{rest}"
|
||||
expandedIdx = "#{ivar}++"
|
||||
assigns.push val.compileToFragments o, LEVEL_LIST
|
||||
continue
|
||||
else
|
||||
name = obj.unwrap().value
|
||||
if obj instanceof Splat
|
||||
obj.error "multiple splats are disallowed in an assignment"
|
||||
if obj instanceof Splat or obj instanceof Expansion
|
||||
obj.error "multiple splats/expansions are disallowed in an assignment"
|
||||
if typeof idx is 'number'
|
||||
idx = new Literal splat or idx
|
||||
idx = new Literal expandedIdx or idx
|
||||
acc = no
|
||||
else
|
||||
acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
|
||||
|
@ -1268,6 +1279,12 @@ exports.Assign = class Assign extends Base
|
|||
fragments = new Op(@context[...-1], left, new Assign(right, @value, '=')).compileToFragments o
|
||||
if o.level <= LEVEL_LIST then fragments else @wrapInBraces fragments
|
||||
|
||||
# Convert special math assignment operators like `a **= b` to the equivalent
|
||||
# extended form `a = a ** b` and then compiles that.
|
||||
compileSpecialMath: (o) ->
|
||||
[left, right] = @variable.cacheReference o
|
||||
new Assign(left, new Op(@context[...-1], right, @value)).compileToFragments o
|
||||
|
||||
# Compile the assignment from an array splice literal, using JavaScript's
|
||||
# `Array#splice` method.
|
||||
compileSplice: (o) ->
|
||||
|
@ -1336,10 +1353,10 @@ exports.Code = class Code extends Base
|
|||
delete o.isExistentialEquals
|
||||
params = []
|
||||
exprs = []
|
||||
for param in @params
|
||||
for param in @params when param not instanceof Expansion
|
||||
o.scope.parameter param.asReference o
|
||||
for param in @params when param.splat
|
||||
for {name: p} in @params
|
||||
for param in @params when param.splat or param instanceof Expansion
|
||||
for {name: p} in @params when param not instanceof Expansion
|
||||
if p.this then p = p.properties[0].name
|
||||
if p.value then o.scope.add p.value, 'var', yes
|
||||
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
|
||||
|
@ -1454,7 +1471,7 @@ exports.Param = class Param extends Base
|
|||
atParam obj
|
||||
# * simple destructured parameters {foo}
|
||||
else iterator obj.base.value, obj.base
|
||||
else
|
||||
else if obj not instanceof Expansion
|
||||
obj.error "illegal parameter #{obj.compile()}"
|
||||
return
|
||||
|
||||
|
@ -1505,6 +1522,22 @@ exports.Splat = class Splat extends Base
|
|||
concatPart = list[index].joinFragmentArrays args, ', '
|
||||
[].concat list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, (last list).makeCode(")")
|
||||
|
||||
#### Expansion
|
||||
|
||||
# Used to skip values inside an array destructuring (pattern matching) or
|
||||
# parameter list.
|
||||
exports.Expansion = class Expansion extends Base
|
||||
|
||||
isComplex: NO
|
||||
|
||||
compileNode: (o) ->
|
||||
@error 'Expansion must be used inside a destructuring assignment or parameter list'
|
||||
|
||||
asReference: (o) ->
|
||||
this
|
||||
|
||||
eachName: (iterator) ->
|
||||
|
||||
#### While
|
||||
|
||||
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
||||
|
@ -1662,9 +1695,15 @@ exports.Op = class Op extends Base
|
|||
@error "cannot increment/decrement \"#{@first.unwrapAll().value}\""
|
||||
return @compileUnary o if @isUnary()
|
||||
return @compileChain o if isChain
|
||||
return @compileExistence o if @operator is '?'
|
||||
answer = [].concat @first.compileToFragments(o, LEVEL_OP), @makeCode(' ' + @operator + ' '),
|
||||
@second.compileToFragments(o, LEVEL_OP)
|
||||
switch @operator
|
||||
when '?' then @compileExistence o
|
||||
when '**' then @compilePower o
|
||||
when '//' then @compileFloorDivision o
|
||||
when '%%' then @compileModulo o
|
||||
else
|
||||
lhs = @first.compileToFragments o, LEVEL_OP
|
||||
rhs = @second.compileToFragments o, LEVEL_OP
|
||||
answer = [].concat lhs, @makeCode(" #{@operator} "), rhs
|
||||
if o.level <= LEVEL_OP then answer else @wrapInBraces answer
|
||||
|
||||
# Mimic Python's chained comparisons when multiple comparison operators are
|
||||
|
@ -1708,6 +1747,20 @@ exports.Op = class Op extends Base
|
|||
parts.reverse() if @flip
|
||||
@joinFragmentArrays parts, ''
|
||||
|
||||
compilePower: (o) ->
|
||||
# Make a Math.pow call
|
||||
pow = new Value new Literal('Math'), [new Access new Literal 'pow']
|
||||
new Call(pow, [@first, @second]).compileToFragments o
|
||||
|
||||
compileFloorDivision: (o) ->
|
||||
floor = new Value new Literal('Math'), [new Access new Literal 'floor']
|
||||
div = new Op '/', @first, @second
|
||||
new Call(floor, [div]).compileToFragments o
|
||||
|
||||
compileModulo: (o) ->
|
||||
mod = new Value new Literal utility 'modulo'
|
||||
new Call(mod, [@first, @second]).compileToFragments o
|
||||
|
||||
toString: (idt) ->
|
||||
super idt, @constructor.name + ' ' + @operator
|
||||
|
||||
|
@ -1720,7 +1773,7 @@ exports.In = class In extends Base
|
|||
invert: NEGATE
|
||||
|
||||
compileNode: (o) ->
|
||||
if @array instanceof Value and @array.isArray()
|
||||
if @array instanceof Value and @array.isArray() and @array.base.objects.length
|
||||
for obj in @array.base.objects when obj instanceof Splat
|
||||
hasSplat = yes
|
||||
break
|
||||
|
@ -1729,7 +1782,6 @@ exports.In = class In extends Base
|
|||
@compileLoopTest o
|
||||
|
||||
compileOrTest: (o) ->
|
||||
return [@makeCode("#{!!@negated}")] if @array.base.objects.length is 0
|
||||
[sub, ref] = @object.cache o, LEVEL_OP
|
||||
[cmp, cnj] = if @negated then [' !== ', ' && '] else [' === ', ' || ']
|
||||
tests = []
|
||||
|
@ -1890,7 +1942,7 @@ exports.For = class For extends While
|
|||
@returns = no if lastJumps and lastJumps instanceof Return
|
||||
source = if @range then @source.base else @source
|
||||
scope = o.scope
|
||||
name = @name and (@name.compile o, LEVEL_LIST)
|
||||
name = @name and (@name.compile o, LEVEL_LIST) if not @pattern
|
||||
index = @index and (@index.compile o, LEVEL_LIST)
|
||||
scope.find(name) if name and not @pattern
|
||||
scope.find(index) if index
|
||||
|
@ -2108,18 +2160,42 @@ UTILITIES =
|
|||
|
||||
# Correctly set up a prototype chain for inheritance, including a reference
|
||||
# to the superclass for `super()` calls, and copies of any static properties.
|
||||
extends: -> """
|
||||
function(child, parent) { for (var key in parent) { if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }
|
||||
"""
|
||||
extends: -> "
|
||||
function(child, parent) {
|
||||
for (var key in parent) {
|
||||
if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key];
|
||||
}
|
||||
function ctor() {
|
||||
this.constructor = child;
|
||||
}
|
||||
ctor.prototype = parent.prototype;
|
||||
child.prototype = new ctor();
|
||||
child.__super__ = parent.prototype;
|
||||
return child;
|
||||
}
|
||||
"
|
||||
|
||||
# Create a function bound to the current value of "this".
|
||||
bind: -> '''
|
||||
function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
|
||||
'''
|
||||
bind: -> '
|
||||
function(fn, me){
|
||||
return function(){
|
||||
return fn.apply(me, arguments);
|
||||
};
|
||||
}
|
||||
'
|
||||
|
||||
# Discover if an item is in an array.
|
||||
indexOf: -> """
|
||||
[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }
|
||||
indexOf: -> "
|
||||
[].indexOf || function(item) {
|
||||
for (var i = 0, l = this.length; i < l; i++) {
|
||||
if (i in this && this[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
"
|
||||
|
||||
modulo: -> """
|
||||
function(a, b) { return (a % b + +b) % b; }
|
||||
"""
|
||||
|
||||
# Shortcuts to speed up the lookup time for native functions.
|
||||
|
|
|
@ -42,10 +42,11 @@ if require.extensions
|
|||
if child_process
|
||||
{fork} = child_process
|
||||
binary = require.resolve '../../bin/coffee'
|
||||
child_process.fork = (path, args = [], options = {}) ->
|
||||
execPath = if helpers.isCoffee(path) then binary else null
|
||||
if not Array.isArray args
|
||||
args = []
|
||||
child_process.fork = (path, args, options) ->
|
||||
if helpers.isCoffee path
|
||||
unless Array.isArray args
|
||||
options = args or {}
|
||||
options.execPath or= execPath
|
||||
args = []
|
||||
args = [path].concat args
|
||||
path = binary
|
||||
fork path, args, options
|
|
@ -131,7 +131,7 @@ module.exports =
|
|||
console.warn "Node 0.8.0+ required for CoffeeScript REPL"
|
||||
process.exit 1
|
||||
|
||||
require './extensions'
|
||||
CoffeeScript.register()
|
||||
process.argv = ['coffee'].concat process.argv[2..]
|
||||
opts = merge replDefaults, opts
|
||||
repl = nodeREPL.start opts
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
# parentheses, and generally clean things up.
|
||||
|
||||
# Create a generated token: one that exists due to a use of implicit syntax.
|
||||
generate = (tag, value) ->
|
||||
generate = (tag, value, origin) ->
|
||||
tok = [tag, value]
|
||||
tok.generated = yes
|
||||
tok.origin = origin if origin
|
||||
tok
|
||||
|
||||
# The **Rewriter** class is used by the [Lexer](lexer.html), directly against
|
||||
|
@ -162,17 +163,18 @@ class exports.Rewriter
|
|||
endAllImplicitCalls = ->
|
||||
while inImplicitCall()
|
||||
endImplicitCall()
|
||||
return
|
||||
|
||||
startImplicitObject = (j, startsLine = yes) ->
|
||||
idx = j ? i
|
||||
stack.push ['{', idx, sameLine: yes, startsLine: startsLine, ours: yes]
|
||||
tokens.splice idx, 0, generate '{', generate(new String('{'))
|
||||
tokens.splice idx, 0, generate '{', generate(new String('{')), token
|
||||
i += 1 if not j?
|
||||
|
||||
endImplicitObject = (j) ->
|
||||
j = j ? i
|
||||
stack.pop()
|
||||
tokens.splice j, 0, generate '}', '}'
|
||||
tokens.splice j, 0, generate '}', '}', token
|
||||
i += 1
|
||||
|
||||
# Don't end an implicit call on next indent if any of these are in an argument
|
||||
|
@ -286,11 +288,8 @@ class exports.Rewriter
|
|||
# .g b
|
||||
# .h a
|
||||
#
|
||||
if inImplicitCall() and tag in CALL_CLOSERS
|
||||
if prevTag is 'OUTDENT'
|
||||
endImplicitCall()
|
||||
return forward(1)
|
||||
if prevToken.newLine
|
||||
if inImplicitCall() and tag in CALL_CLOSERS and
|
||||
(prevTag is 'OUTDENT' or prevToken.newLine)
|
||||
endAllImplicitCalls()
|
||||
return forward(1)
|
||||
|
||||
|
@ -304,7 +303,8 @@ class exports.Rewriter
|
|||
endImplicitCall()
|
||||
# Close implicit objects such as:
|
||||
# return a: 1, b: 2 unless true
|
||||
else if inImplicitObject() and sameLine and not startsLine
|
||||
else if inImplicitObject() and sameLine and
|
||||
tag isnt 'TERMINATOR' and prevTag isnt ':'
|
||||
endImplicitObject()
|
||||
# Close implicit objects when at end of line, line didn't end with a comma
|
||||
# and the implicit object didn't start the line or the next line doesn't look like
|
||||
|
@ -470,8 +470,8 @@ IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@
|
|||
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
|
||||
IMPLICIT_CALL = [
|
||||
'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS'
|
||||
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'SUPER'
|
||||
'THROW', '@', '->', '=>', '->*', '=>*', '[', '(', '{', '--', '++'
|
||||
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY',
|
||||
'UNARY_MATH', 'SUPER', 'THROW', '@', '->', '=>', '[', '(', '{', '--', '++'
|
||||
]
|
||||
|
||||
IMPLICIT_UNSPACED_CALL = ['+', '-']
|
||||
|
|
|
@ -268,6 +268,22 @@ test "#2055: destructuring assignment with `new`", ->
|
|||
{length} = new Array
|
||||
eq 0, length
|
||||
|
||||
test "#156: destructuring with expansion", ->
|
||||
array = [1..5]
|
||||
[first, ..., last] = array
|
||||
eq 1, first
|
||||
eq 5, last
|
||||
[..., lastButOne, last] = array
|
||||
eq 4, lastButOne
|
||||
eq 5, last
|
||||
[first, second, ..., last] = array
|
||||
eq 2, second
|
||||
[..., last] = 'strings as well -> x'
|
||||
eq 'x', last
|
||||
throws (-> CoffeeScript.compile "[1, ..., 3]"), null, "prohibit expansion outside of assignment"
|
||||
throws (-> CoffeeScript.compile "[..., a, b...] = c"), null, "prohibit expansion and a splat"
|
||||
throws (-> CoffeeScript.compile "[...] = c"), null, "prohibit lone expansion"
|
||||
|
||||
|
||||
# Existential Assignment
|
||||
|
||||
|
|
|
@ -537,7 +537,10 @@ test "#2525, #1187, #1208, #1758, looping over an array backwards", ->
|
|||
|
||||
arrayEq (index for i, index in list by ident(-1) * 2), [4, 2, 0]
|
||||
|
||||
test "splats in destructuring in comprehensions", ->
|
||||
list = [[0, 1, 2], [2, 3, 4], [4, 5, 6]]
|
||||
arrayEq (seq for [rep, seq...] in list), [[1, 2], [3, 4], [5, 6]]
|
||||
|
||||
|
||||
|
||||
|
||||
test "#156: expansion in destructuring in comprehensions", ->
|
||||
list = [[0, 1, 2], [2, 3, 4], [4, 5, 6]]
|
||||
arrayEq (last for [..., last] in list), [2, 4, 6]
|
||||
|
|
|
@ -198,6 +198,14 @@ test "#748: trailing reserved identifiers", ->
|
|||
nonce
|
||||
eq nonce, result
|
||||
|
||||
# Postfix
|
||||
|
||||
test "#3056: multiple postfix conditionals", ->
|
||||
temp = 'initial'
|
||||
temp = 'ignored' unless true if false
|
||||
eq temp, 'initial'
|
||||
|
||||
# Loops
|
||||
|
||||
test "basic `while` loops", ->
|
||||
|
||||
|
@ -296,6 +304,7 @@ test "break *not* at the top level", ->
|
|||
result
|
||||
eq 2, someFunc()
|
||||
|
||||
# Switch
|
||||
|
||||
test "basic `switch`", ->
|
||||
|
||||
|
@ -420,6 +429,7 @@ test "Issue #997. Switch doesn't fallthrough.", ->
|
|||
|
||||
eq val, 1
|
||||
|
||||
# Throw
|
||||
|
||||
test "Throw should be usable as an expression.", ->
|
||||
try
|
||||
|
|
|
@ -26,7 +26,7 @@ test "parser error formating", ->
|
|||
foo in bar or in baz
|
||||
''',
|
||||
'''
|
||||
[stdin]:1:15: error: unexpected RELATION
|
||||
[stdin]:1:15: error: unexpected in
|
||||
foo in bar or in baz
|
||||
^^
|
||||
'''
|
||||
|
@ -58,9 +58,44 @@ test "#2849: compilation error in a require()d file", ->
|
|||
require './test/syntax-error'
|
||||
''',
|
||||
"""
|
||||
#{path.join __dirname, 'syntax-error.coffee'}:1:15: error: unexpected RELATION
|
||||
#{path.join __dirname, 'syntax-error.coffee'}:1:15: error: unexpected in
|
||||
foo in bar or in baz
|
||||
^^
|
||||
"""
|
||||
finally
|
||||
fs.unlink 'test/syntax-error.coffee'
|
||||
|
||||
test "#1096: unexpected generated tokens", ->
|
||||
# Unexpected interpolation
|
||||
assertErrorFormat '{"#{key}": val}', '''
|
||||
[stdin]:1:3: error: unexpected string interpolation
|
||||
{"#{key}": val}
|
||||
^^
|
||||
'''
|
||||
# Implicit ends
|
||||
assertErrorFormat 'a:, b', '''
|
||||
[stdin]:1:3: error: unexpected ,
|
||||
a:, b
|
||||
^
|
||||
'''
|
||||
# Explicit ends
|
||||
assertErrorFormat '(a:)', '''
|
||||
[stdin]:1:4: error: unexpected )
|
||||
(a:)
|
||||
^
|
||||
'''
|
||||
# Unexpected end of file
|
||||
assertErrorFormat 'a:', '''
|
||||
[stdin]:1:3: error: unexpected end of input
|
||||
a:
|
||||
^
|
||||
'''
|
||||
# Unexpected implicit object
|
||||
assertErrorFormat '''
|
||||
for i in [1]:
|
||||
1
|
||||
''', '''
|
||||
[stdin]:1:13: error: unexpected :
|
||||
for i in [1]:
|
||||
^
|
||||
'''
|
||||
|
|
|
@ -98,6 +98,13 @@ test "#1495, method call chaining", ->
|
|||
).join ', '
|
||||
eq 'a, b, c', result
|
||||
|
||||
test "chaining after outdent", ->
|
||||
str = 'abc'
|
||||
zero = parseInt str.replace /\w/, (letter) ->
|
||||
0
|
||||
.toString()
|
||||
eq '0', zero
|
||||
|
||||
# Operators
|
||||
|
||||
test "newline suppression for operators", ->
|
||||
|
@ -192,7 +199,7 @@ test "#1299: Disallow token misnesting", ->
|
|||
|
||||
test "#2981: Enforce initial indentation", ->
|
||||
try
|
||||
CoffeeScript.compile ' a\nb'
|
||||
CoffeeScript.compile ' a\nb-'
|
||||
ok no
|
||||
catch e
|
||||
eq 'missing indentation', e.message
|
||||
|
@ -205,3 +212,16 @@ test "'single-line' expression containing multiple lines", ->
|
|||
then -b
|
||||
else null
|
||||
"""
|
||||
|
||||
test "#1275: allow indentation before closing brackets", ->
|
||||
array = [
|
||||
1
|
||||
2
|
||||
3
|
||||
]
|
||||
eq array, array
|
||||
do ->
|
||||
(
|
||||
a = 1
|
||||
)
|
||||
eq 1, a
|
||||
|
|
|
@ -178,6 +178,21 @@ test "default values with splatted arguments", ->
|
|||
eq 1, withSplats(1,1,1)
|
||||
eq 2, withSplats(1,1,1,1)
|
||||
|
||||
test "#156: parameter lists with expansion", ->
|
||||
expandArguments = (first, ..., lastButOne, last) ->
|
||||
eq 1, first
|
||||
eq 4, lastButOne
|
||||
last
|
||||
eq 5, expandArguments 1, 2, 3, 4, 5
|
||||
|
||||
throws (-> CoffeeScript.compile "(..., a, b...) ->"), null, "prohibit expansion and a splat"
|
||||
throws (-> CoffeeScript.compile "(...) ->"), null, "prohibit lone expansion"
|
||||
|
||||
test "#156: parameter lists with expansion in array destructuring", ->
|
||||
expandArray = (..., [..., last]) ->
|
||||
last
|
||||
eq 3, expandArray 1, 2, 3, [1, 2, 3]
|
||||
|
||||
test "default values with function calls", ->
|
||||
doesNotThrow -> CoffeeScript.compile "(x = f()) ->"
|
||||
|
||||
|
|
|
@ -387,6 +387,23 @@ test "#1871: Special case for IMPLICIT_END in the middle of an implicit object",
|
|||
|
||||
eq result.two.join(' '), '2 2 2'
|
||||
|
||||
test "#1871: implicit object closed by IMPLICIT_END in implicit returns", ->
|
||||
ob = do ->
|
||||
a: 1 if no
|
||||
eq ob, undefined
|
||||
|
||||
# instead these return an object
|
||||
func = ->
|
||||
key:
|
||||
i for i in [1, 2, 3]
|
||||
|
||||
eq func().key.join(' '), '1 2 3'
|
||||
|
||||
func = ->
|
||||
key: (i for i in [1, 2, 3])
|
||||
|
||||
eq func().key.join(' '), '1 2 3'
|
||||
|
||||
test "#1961, #1974, regression with compound assigning to an implicit object", ->
|
||||
|
||||
obj = null
|
||||
|
@ -410,3 +427,10 @@ test "#2207: Immediate implicit closes don't close implicit objects", ->
|
|||
key: for i in [1, 2, 3] then i
|
||||
|
||||
eq func().key.join(' '), '1 2 3'
|
||||
|
||||
test 'inline implicit object literals within multiline implicit object literals', ->
|
||||
x =
|
||||
a: aa: 0
|
||||
b: 0
|
||||
eq 0, x.b
|
||||
eq 0, x.a.aa
|
||||
|
|
|
@ -218,6 +218,10 @@ test "#1714: lexer bug with raw range `for` followed by `in`", ->
|
|||
test "#1099: statically determined `not in []` reporting incorrect result", ->
|
||||
ok 0 not in []
|
||||
|
||||
test "#1099: make sure expression tested gets evaluted when array is empty", ->
|
||||
a = 0
|
||||
(do -> a = 1) in []
|
||||
eq a, 1
|
||||
|
||||
# Chained Comparison
|
||||
|
||||
|
@ -296,3 +300,60 @@ test "#2567: Optimization of negated existential produces correct result", ->
|
|||
test "#2508: Existential access of the prototype", ->
|
||||
eq NonExistent?::nothing, undefined
|
||||
ok Object?::toString
|
||||
|
||||
test "power operator", ->
|
||||
eq 27, 3 ** 3
|
||||
|
||||
test "power operator has higher precedence than other maths operators", ->
|
||||
eq 55, 1 + 3 ** 3 * 2
|
||||
eq -4, -2 ** 2
|
||||
eq false, !2 ** 2
|
||||
eq 0, (!2) ** 2
|
||||
eq -2, ~1 ** 5
|
||||
|
||||
test "power operator is right associative", ->
|
||||
eq 2, 2 ** 1 ** 3
|
||||
|
||||
test "power operator compound assignment", ->
|
||||
a = 2
|
||||
a **= 3
|
||||
eq 8, a
|
||||
|
||||
test "floor division operator", ->
|
||||
eq 2, 7 // 3
|
||||
eq -3, -7 // 3
|
||||
eq NaN, 0 // 0
|
||||
|
||||
test "floor division operator compound assignment", ->
|
||||
a = 7
|
||||
a //= 2
|
||||
eq 3, a
|
||||
|
||||
test "modulo operator", ->
|
||||
check = (a, b, expected) ->
|
||||
eq expected, a %% b, "expected #{a} %%%% #{b} to be #{expected}"
|
||||
check 0, 1, 0
|
||||
check 0, -1, -0
|
||||
check 1, 0, NaN
|
||||
check 1, 2, 1
|
||||
check 1, -2, -1
|
||||
check 1, 3, 1
|
||||
check 2, 3, 2
|
||||
check 3, 3, 0
|
||||
check 4, 3, 1
|
||||
check -1, 3, 2
|
||||
check -2, 3, 1
|
||||
check -3, 3, 0
|
||||
check -4, 3, 2
|
||||
check 5.5, 2.5, 0.5
|
||||
check -5.5, 2.5, 2.0
|
||||
|
||||
test "modulo operator compound assignment", ->
|
||||
a = -2
|
||||
a %%= 5
|
||||
eq 3, a
|
||||
|
||||
test "modulo operator converts arguments to numbers", ->
|
||||
eq 1, 1 %% '42'
|
||||
eq 1, '1' %% 42
|
||||
eq 1, '1' %% '42'
|
||||
|
|
|
@ -55,25 +55,3 @@ test "an empty heregex will compile to an empty, non-capturing group", ->
|
|||
|
||||
test "#1724: regular expressions beginning with `*`", ->
|
||||
throws -> CoffeeScript.compile '/// * ///'
|
||||
|
||||
test "empty regular expressions with flags", ->
|
||||
fn = (x) -> x
|
||||
a = "" + //i
|
||||
fn ""
|
||||
eq '/(?:)/i', a
|
||||
|
||||
test "#3059: don't remove escaped whitespace", ->
|
||||
eq /// One\ cannot [\ ] escape \ \destiny. ///.source,
|
||||
/One cannot[ ]escape \destiny./.source
|
||||
|
||||
test "#2238: don't escape already escaped slashes", ->
|
||||
eq /// \\\/ \/ ///.source, /\\\/\//.source
|
||||
|
||||
test "escaped slashes don't close heregex", ->
|
||||
eq /// \/// ///.source, /\/\/\//.source
|
||||
eq /// \\\////.source, /\\\//.source
|
||||
|
||||
test "escaped linebreaks", ->
|
||||
eq /// \n\
|
||||
\
|
||||
///.source, /\n\n\n/.source
|
||||
|
|
Loading…
Reference in a new issue