Before:
```
$ cat tmp.coffee.md
test
a
$ ./bin/coffee tmp.coffee.md
ReferenceError: a is not defined
at Object.<anonymous> (/src/coffee-script/tmp.coffee.md:2:3)
...
```
Note how the line and column numbers (2 and 3, respectively) are not
correct.
After:
```
$ ./bin/coffee tmp.coffee.md
ReferenceError: a is not defined
at Object.<anonymous> (/home/lydell/forks/coffee-script/tmp.coffee.md:3:5)
...
```
Line 3, column 5 is the actual position of the `a` in tmp.coffee.md.
Supersedes and fixes#4204.
This pull request adds support for ES2015 modules, by recognizing `import` and `export` statements. The following syntaxes are supported, based on the MDN [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) and [export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) pages:
```js
import "module-name"
import defaultMember from "module-name"
import * as name from "module-name"
import { } from "module-name"
import { member } from "module-name"
import { member as alias } from "module-name"
import { member1, member2 as alias2, … } from "module-name"
import defaultMember, * as name from "module-name"
import defaultMember, { … } from "module-name"
export default expression
export class name
export { }
export { name }
export { name as exportedName }
export { name as default }
export { name1, name2 as exportedName2, name3 as default, … }
export * from "module-name"
export { … } from "module-name"
```
As a subsitute for ECMAScript’s `export var name = …` and `export function name {}`, CoffeeScript also supports:
```js
export name = …
```
CoffeeScript also supports optional commas within `{ … }`.
This PR converts the supported `import` and `export` statements into ES2015 `import` and `export` statements; it **does not resolve the modules**. So any CoffeeScript with `import` or `export` statements will be output as ES2015, and will need to be transpiled by another tool such as Babel before it can be used in a browser. We will need to add a warning to the documentation explaining this.
This should be fully backwards-compatible, as `import` and `export` were previously reserved keywords. No flags are used.
There are extensive tests included, though because no current JavaScript runtime supports `import` or `export`, the tests compare strings of what the compiled CoffeeScript output is against what the expected ES2015 should be. I also conducted two more elaborate tests:
* I forked the [ember-piqu](https://github.com/pauc/piqu-ember) project, which was an Ember CLI app that used ember-cli-coffeescript and [ember-cli-coffees6](https://github.com/alexspeller/ember-cli-coffees6) (which adds “support” for `import`/`export` by wrapping such statements in backticks before passing the result to the CoffeeScript compiler). I removed `ember-cli-coffees6` and replaced the CoffeeScript compiler used in the build chain with this code, and the app built without errors. [Demo here.](https://github.com/GeoffreyBooth/coffeescript-modules-test-piqu)
* I also forked the [CoffeeScript version of Meteor’s Todos example app](https://github.com/meteor/todos/tree/coffeescript), and replaced all of its `require` statements with the `import` and `export` statements from the original ES2015 version of the app on its `master` branch. I then updated the `coffeescript` Meteor package in the app to use this new code, and again the app builds without errors. [Demo here.](https://github.com/GeoffreyBooth/coffeescript-modules-test-meteor-todos)
The discussion history for this work started [here](https://github.com/jashkenas/coffeescript/pull/4160) and continued [here](https://github.com/GeoffreyBooth/coffeescript/pull/2). @lydell provided guidance, and @JimPanic and @rattrayalex contributed essential code.
In f609036bee, a line was changed from
`if length > 0 then (length - 1) else 0` to `Math.max 0, length - 1`. However,
in some cases, the `length` variable can be `undefined`. The previous code would
correctly compute `lastCharacter` as 0, but the new code would compute it as
`NaN`. This would cause trouble later on: the end location would just be the end
of the current chunk, which would be incorrect.
Here's a specific case where the parser was behaving incorrectly:
```
a {
b: ->
return c d,
if e
f
}
g
```
The OUTDENT tokens after the `f` had an undefined length, so the `NaN` made it
so the end location was at the end of the file. That meant that various nodes in
the AST, like the `return` node, would incorrectly have an end location at the
end of the file.
To fix, I just reverted the change to that particular line.
In for example `for` loops, a variable called `i` is generated (for the
loop index). If that name is unavailable, `j` is used instead, then `k`,
`l`, etc. all the way to `z`. Then, `aa`, `ab`, `ac` etc. are used.
This meant that, eventually, `do` would be used, but that's not a valid
variable name since `do` is a JavaScript keyword.
This logic was also inefficiently implemented. For example, going from
`aa` to `ab` or from `az` to `ba` required lots of loop iterations.
This commit changes the variable naming convention. Now, `i`, `j`, `k`,
etc. to `z` are used like before. Then comes `i1`, `j1`, `k1`, etc. Then
`i2`, `j2`, `k2` and so on. This is simpler, efficient and easier to
understand. `i1` is more obvious to be a loop index than `aa`.
Fixes#4267.
This should have been done in commit 841b3cd2, but I forgot to. Since
that commit, `SourceMap::generate` returns an object instead of
`JSON.stringify()` of that object, but the tests still compared strings.
Fixes#4269.
Note: `SourceMap::generate` is only used internally, so its change in
return type is not a breaking change. The "public API" is unchanged.
- Inline source maps are now shorter by not using pretty-printed JSON.
- `.register()`ed files are now given more information in their inline source
maps: The name and contents of the source file.
- Some code cleanup.
If you decode the inline source map generated (when using `.register()`) for a
file test.coffee with the contents `console.log "it works!"`, here is the
output:
Before:
{
"version": 3,
"file": "",
"sourceRoot": "",
"sources": [
""
],
"names": [],
"mappings": "AAAA;EAAA,OAAO,CAAC,GAAR,CAAY,eAAZ;AAAA"
}
After:
{"version":3,"file":"","sourceRoot":"","sources":["test.coffee"],"names":[],"mappings":"AAAA;EAAA,OAAO,CAAC,GAAR,CAAY,WAAZ;AAAA","sourcesContent":["console.log \"it works!\"\n"]}
Related: #4214.
- Split out a PROPERTY token from the IDENTIFIER token.
- Split out Property from the Identifier in the grammar.
- Split out PropertyLiteral from IdentifierLiteral.
- Show the same type of error message for compound assignment as for `=`
assignment when the LHS is invalid.
- Show the same type of error message when trying to assign to a CoffeeScript
keyword as when trying to assign to a JavaScript keyword.
- Now longer treat `&& =` as `&&=`. The same goes for `and=`, `||=` and `or=`.
- Unify the error message to: `<optional type> '<value>' can't be assigned`.
Previously, the parser created `Literal` nodes for many things. This resulted in
information loss. Instead of being able to check the node type, we had to use
regexes to tell the different types of `Literal`s apart. That was a bit like
parsing literals twice: Once in the lexer, and once (or more) in the compiler.
It also caused problems, such as `` `this` `` and `this` being indistinguishable
(fixes#2009).
Instead returning `new Literal` in the grammar, subtypes of it are now returned
instead, such as `NumberLiteral`, `StringLiteral` and `IdentifierLiteral`. `new
Literal` by itself is only used to represent code chunks that fit no category.
(While mentioning `NumberLiteral`, there's also `InfinityLiteral` now, which is
a subtype of `NumberLiteral`.)
`StringWithInterpolations` has been added as a subtype of `Parens`, and
`RegexWithInterpolations` as a subtype of `Call`. This makes it easier for other
programs to make use of CoffeeScript's "AST" (nodes). For example, it is now
possible to distinguish between `"a #{b} c"` and `"a " + b + " c"`. Fixes#4192.
`SuperCall` has been added as a subtype of `Call`.
Note, though, that some information is still lost, especially in the lexer. For
example, there is no way to distinguish a heredoc from a regular string, or a
heregex without interpolations from a regular regex. Binary and octal number
literals are indistinguishable from hexadecimal literals.
After the new subtypes were added, they were taken advantage of, removing most
regexes in nodes.coffee. `SIMPLENUM` (which matches non-hex integers) had to be
kept, though, because such numbers need special handling in JavaScript (for
example in `1..toString()`).
An especially nice hack to get rid of was using `new String()` for the token
value for reserved identifiers (to be able to set a property on them which could
survive through the parser). Now it's a good old regular string.
In range literals, slices, splices and for loop steps when number literals
are involved, CoffeeScript can do some optimizations, such as precomputing the
value of, say, `5 - 3` (outputting `2` instead of `5 - 3` literally). As a side
bonus, this now also works with hexadecimal number literals, such as `0x02`.
Finally, this also improves the output of `coffee --nodes`:
# Before:
$ bin/coffee -ne 'while true
"#{a}"
break'
Block
While
Value
Bool
Block
Value
Parens
Block
Op +
Value """"
Value
Parens
Block
Value "a" "break"
# After:
$ bin/coffee -ne 'while true
"#{a}"
break'
Block
While
Value BooleanLiteral: true
Block
Value
StringWithInterpolations
Block
Op +
Value StringLiteral: ""
Value
Parens
Block
Value IdentifierLiteral: a
StatementLiteral: break
Commit 347a6255 is a bit problematic:
- It doesn't include the built .js files.
- It breaks `CoffeeScript.register()`. This can be seen by running the tests;
four of them fails. The error is that `CoffeeScript.register()` calls
`CoffeeScript._compileFile()` with the `sourceMap` option enabled, which
returns an object while the code expected a string.
This commit fixes the broken `CoffeeScript.register()`, by setting the
`sourceMap` option to `false` (but still keeping the `inlineMap` option enabled,
which was the intention of commit 347a6255). It also commits the built .js
files. The tests now pass.
`({a = 1}) ->` and `({a: b}) ->` worked, but not the combination of the two:
`({a: b = 1}) ->`. That destrucuring worked for normal assignments, though:
`{a: b = 1} = c`. This commit fixes the param case.
This breaks compatibility with
->
yield for i in [1..3]
i * 2
and
->
yield
i * 2
yield's behaviour now mirrors that of return in that it can be used stand alone as well as with expressions. Thus, it currently also inherits the above limitations.
The example code in documentation/coffee/*.coffee accidentally had duplicate
compiled .js versions committed: Both as documentation/js/*.js and as
documentation/coffee/*.js.
The former is generated when running `cake doc:site`, while the latter has no
cake task to be generated. Removing the former and then re-compiling index.html
results in all the code examples being missing in the resulting HTML, while
removing the latter makes no difference.
In conclusion, documentation/coffee/*.js must be the unnecessary duplicates.
They are removed by this commit.
This was done by first checking all files in the repository root by hand, and
then by doing the following:
$ npm i get-stdin@4
$ cat clean.coffee
fs = require 'fs'
stdin = require 'get-stdin'
stdin (text) ->
for file in text.trim().split('\n')
contents = fs.readFileSync file
.toString().replace /\s*$/, '\n'
fs.writeFileSync file, contents
return
$ ls !(node_modules)/**/*.coffee | coffee clean.coffee
"Smaller screens" means screens smaller than 820px wide. That's the smallest
width that the current design looks good at.
I tried to not change the current design in any way and to make as few changes
as possible.
This is what happens on smaller screens:
- The navigation bar is no longer fixed to the viewport, but always at the top
of the page.
- The navigation bar is vertical rather than horizontal.
- CoffeeScript code is above the compiled JavaScript rather than having them
side by side.
This allows for a larger text size, which is more compfortable to read, and no
horizontal scrolling, which many find difficult to use.
This let's you do things like:
fullName = ({first = 'John', last = 'Doe'}) -> "#{first} #{last}"
Note: CoffeeScrits treats `undefined` and `null` the same, and that's true in
the case of destructuring defaults as well, as opposed to ES2015 which only uses
the default value if the target is `undefined`. A similar ES2015 difference
already exists for function parameter defaults. It is important for CoffeeScript
to be consistent with itself.
fullName2 = (first = 'John', last = 'Doe') -> "#{first} #{last}"
assert fullName('Bob', null) is fullName2(first: 'Bob', last: null)
Fixes#1558, #3288 and #4005.
Let me know if there's something I should be doing differently as this is my first contribution to coffeescript.
I fixed the reported issue where a generated variable could clash with a user-defined one in a try/catch block.
I added a test for a few scenarios with different variable names for a try/catch, to confirm the fix and avoid regressions.
Testing with `'[object Array]' is Object::toString.call element` allows arrays from another JS context to be properly handled. The specific use case here is to support jest, which sets up JS contexts using Node/io.js's "vm" module. This approach works in ES3 environments in contrast with ES5's `Array.isArray`.
If you passed an array of tokens (as opposed to a string of code) to
`CoffeeScript.nodes`, its attempts to prettify error messages would break. Now
it does not attempt to prettify error messages in that case anymore (because it
is not possible to prettify the errors without a string of code).
The repl was affected by the above bug.
Fixes#3887.
Instead of mapping all generated spaces and semicolons and newlines
to the source position (0,0), we avoid generating sourcemap information
for generated space-or-semicolon-only fragments.
(In addition to shortening sourcemaps, this fixes a correctness issue
where an empty fragment at the beginning of each line maps from (0,0),
but in a bare program, that position at the begining of the line
should map from the actual source line. When this conflict occurred,
(0,0) would win, resulting in an incorrect sourcemap, where each
top-level function call mapped to (0,0).)
The following is now allowed:
o =
a: 1
b: 2
"#{'c'}": 3
"#{'d'}": 4
e: 5
"#{'f'}": 6
g: 7
It compiles to:
o = (
obj = {
a: 1,
b: 2
},
obj["" + 'c'] = 3,
obj["" + 'd'] = 4,
obj.e = 5,
obj["" + 'f'] = 6,
obj.g = 7,
obj
);
- Closes#3039. Empty interpolations in object keys are now _supposed_ to be
allowed.
- Closes#1131. No need to improve error messages for attempted key
interpolation anymore.
- Implementing this required fixing the following bug: `("" + a): 1` used to
error out on the colon, saying "unexpected colon". But really, it is the
attempted object key that is unexpected. Now the error is on the opening
parenthesis instead.
- However, the above fix broke some error message tests for regexes. The easiest
way to fix this was to make a seemingly unrelated change: The error messages
for unexpected identifiers, numbers, strings and regexes now say for example
'unexpected string' instead of 'unexpected """some #{really long} string"""'.
In other words, the tag _name_ is used instead of the tag _value_.
This was way easier to implement, and is more helpful to the user. Using the
tag value is good for operators, reserved words and the like, but not for
tokens which can contain any text. For example, 'unexpected identifier' is
better than 'unexpected expected' (if a variable called 'expected' was used
erraneously).
- While writing tests for the above point I found a few minor bugs with string
locations which have been fixed.
- Invalid `\x` and `\u` escapes now throw errors.
- U+2028 and U+2029 (which JavaScript treats as newline characters) are now
escaped to `\u2028` and `\u2029`, respectively.
- Octal escapes are now forbidden not only in strings, but in regexes as well.
- `\0` escapes are now escaped if needed (so that they do not form an octal
literal by mistake). Note that `\01` is an octal escape in a regex, while `\1`
is a backreference. (Added a test for backreferences while at it.)
- Fixed a bug where newlines in strings weren't removed if preceded by an
escaped character.
The third element in a token should just be an object containing line
number and column info. This PR fixes the problem with one of the tokens
being set incorrectly.
As discussed in https://github.com/jashkenas/coffeescript/issues/3039#issuecomment-68916918.
This is the first step to implement dynamic object literal keys (see #3597).
This also fixes#1392.
In short, `super` is now allowed:
# in class definitions:
class A
instanceMethod: -> super
@staticMethod: -> super
@staticMethod2 = -> super
# in assignment where the next to last access is 'prototype':
A::m = -> super
A.prototype.m = -> super
a.b()[5]::m = -> super
A::[x()] = -> super
class B
@::m = -> super
The following two lines might seem equivalent:
for n in [1, 2, 3] by a then a = 4; n
for n in [1, 2, 3] by +a then a = 4; n
But they used not to be, because `+a` was cached into a `ref`, while the plain
`a` wasn’t. Now even simple identifiers are cached, making the two lines
equivalent as expected.
As evidenced in #3804, commit 8ab15d7 broke the CoffeeScript API. The REPL uses
those APIs, but wasn't updated in that commit. Still, that shouldn't have
_broken_ the REPL. The reason it broke is because the added _option_
'referencedVars' wasn't actually _optional;_ if it was omitted code that relies
on it being set broke. This commit defaults that option to an empty array, which
makes things behave exactly like before when the 'referencedVars' option is
omitted.
Supersedes #3805. Here is a comparison of master, #3805 and this commit:
# master
$ bin/coffee
coffee> 1 %% 2
TypeError: Array.prototype.indexOf called on null or undefined
# #3805
$ bin/coffee
coffee> 1 %% 2
1
coffee> (_results = null; i) for i in [1, 2, 3]
TypeError: Cannot call method 'push' of null
# this commit
$ bin/coffee
coffee> 1 %% 2
1
coffee> (_results = null; i) for i in [1, 2, 3]
[ 1, 2, 3 ]
Instead of compiling to `"" + + (+"-");`, `"#{+}-"'` now gives an appropriate
error message:
[stdin]:1:5: error: unexpected end of interpolation
"#{+}-"
^
This is done by _always_ (instead of just sometimes) wrapping the interpolations
in parentheses in the lexer. Unnecessary parentheses won't be output anyway.
I got tired of updating the tests in test/location.coffee (which I had enough of
in #3770), which relies on implementation details (the exact amount of tokens
generated for a given string of code) to do their testing, so I refactored them
to be less fragile.
Since zaach/jison commit 3548861b, `parser.lexer` is never modified anymore (a
copy of it is made, and that copy is modified instead). CoffeeScript itself
modifies `parser.lexer` and then accesses those modifications in the custom
`parser.yy.parseError` function, but that of course does not work anymore. This
commit puts the data that `parser.yy.parseError` needs directly on the `parser`
so that it is not lost.
Supersedes #3603. Fixes#3608 and zaach/jison#243.
Using the static property `Scope.root` for the top-level scope of a file is a
hack, which makes it impossible to have several independent `Scope` instances
at the same time (should we ever need that).
This commit makes every instance have a reference to its root instead.
Any variables generated by CoffeeScript are now made sure to be named to
something not present in the source code being compiled. This way you can no
longer interfere with them, either on purpose or by mistake. (#1500, #1574)
For example, `({a}, _arg) ->` now compiles correctly. (#1574)
As opposed to the somewhat complex implementations discussed in #1500, this
commit takes a very simple approach by saving all used variables names using a
single pass over the token stream. Any generated variables are then made sure
not to exist in that list.
`(@a) -> a` used to be equivalent to `(@a) -> @a`, but now throws a runtime
`ReferenceError` instead (unless `a` exists in an upper scope of course). (#3318)
`(@a) ->` used to compile to `(function(a) { this.a = a; })`. Now it compiles to
`(function(_at_a) { this.a = _at_a; })`. (But you cannot access `_at_a` either,
of course.)
Because of the above, `(@a, a) ->` is now valid; `@a` and `a` are not duplicate
parameters.
Duplicate this-parameters with a reserved word, such as `(@case, @case) ->`,
used to compile but now throws, just like regular duplicate parameters.
Allow the `by c` part in `for [a..b] by c then`.
Continue disallowing a `when d` part, since it makes no sense having a guard
that isn't given access to anything that changes on every iteration.
A regex may not follow a specific set of tokens. These were already known before
in the `NOT_REGEX` and `NOT_SPACED_REGEX` arrays. (However, I've refactored them
to be more correct and to add a few missing tokens). In all other cases (except
after a spaced callable) a slash is the start of a regex, and may now start with
a space or an equals sign. It’s really that simple!
A slash after a spaced callable is the only ambigous case. We cannot know if
that's division or function application with a regex as the argument. The
spacing determines which is which:
Space on both sides:
- `a / b/i` -> `a / b / i`
- `a /= b/i` -> `a /= b / i`
No spaces:
- `a/b/i` -> `a / b / i`
- `a/=b/i` -> `a /= b / i`
Space on the right side:
- `a/ b/i` -> `a / b / i`
- `a/= b/i` -> `a /= b / i`
Space on the left side:
- `a /b/i` -> `a(/b/i)`
- `a /=b/i` -> `a(/=b/i)`
The last case used to compile to `a /= b / i`, but that has been changed to be
consistent with the `/` operator. The last case really looks like a regex, so it
should be parsed as one.
Moreover, you may now also space the `/` and `/=` operators with other
whitespace characters than a space (such as tabs and non-breaking spaces) for
consistency.
Lastly, unclosed regexes are now reported as such, instead of generating some
other confusing error message.
It should perhaps also be noted that apart from escaping (such as `a /\ b/`) you
may now also use parentheses to disambiguate division and regex: `a (/ b/)`. See
https://github.com/jashkenas/coffeescript/issues/3182#issuecomment-26688427.
Before commit c056c93e `Op::isComplex()` used to return true always. As far as I
understand, that commit attempts to exclude code such as `+1` and `-2` from
being marked as complex (and thus getting cached into `_ref` variables
sometimes). CoffeeScript is supposed to generate readable output so that choice
is understandable. However, it also excludes code such as `+a` (by mistake I
believe), which can cause `a` to be coerced multiple times. This commit fixes
this by only excluding unary + and - ops followed by a number.
It is possible to match only valid JavaScript identifiers with a really long
regex (like coco and CoffeeScriptRedux does), but CoffeeScript uses a much
simpler one, which allows a bit too much.
Quoting jashkenas/coffeescript#1718 #issuecomment-2152464 @jashkenas:
> But it still seems very much across the "worth it" line. You'll get the
> SyntaxError as soon as it hits JS, and performance aside -- even the increase
> in filesize for our browser coffee-script.js lib seems too much, considering
> this is something no one ever does, apart from experimentation.
In short, CoffeeScript treats any non-ASCII character as part of an identifier.
However, unicode spaces should be excluded since having blank characters as part
of a _word_ is very confusing. This commit does so, while still keeping the
regex really simple.
Previously such errors pointed at the end of the input, which wasn't very
helpful. This is also consistent with unclosed strings, where the errors point
at the opening quote.
Note that this includes unclosed #{ (interpolations).
- Fix#3394: Unclosed single-quoted strings (both regular ones and heredocs)
used to pass through the lexer, causing a parsing error later, while
double-quoted strings caused an error already in the lexing phase. Now both
single and double-quoted unclosed strings error out in the lexer (which is the
more logical option) with consistent error messages. This also fixes the last
comment by @satyr in #3301.
- Similar to the above, unclosed heregexes also used to pass through the lexer
and not error until in the parsing phase, which resulted in confusing error
messages. This has been fixed, too.
- Fix#3348, by adding passing tests.
- Fix#3529: If a string starts with an interpolation, an empty string is no
longer emitted before the interpolation (unless it is needed to coerce the
interpolation into a string).
- Block comments cannot contain `*/`. Now the error message also shows exactly
where the offending `*/`. This improvement might seem unrelated, but I had to
touch that code anyway to refactor string and regex related code, and the
change was very trivial. Moreover, it's consistent with the next two points.
- Regexes cannot start with `*`. Now the error message also shows exactly where
the offending `*` is. (It might actually not be exatly at the start in
heregexes.) It is a very minor improvement, but it was trivial to add.
- Octal escapes in strings are forbidden in CoffeeScript (just like in
JavaScript strict mode). However, this used to be the case only for regular
strings. Now they are also forbidden in heredocs. Moreover, the errors now
point at the offending octal escape.
- Invalid regex flags are no longer allowed. This includes repeated modifiers
and unknown ones. Moreover, invalid modifiers do not stop a heregex from
being matched, which results in better error messages.
- Fix#3621: `///a#{1}///` compiles to `RegExp("a" + 1)`. So does
`RegExp("a#{1}")`. Still, those two code snippets used to generate different
tokens, which is a bit weird, but more importantly causes problems for
coffeelint (see clutchski/coffeelint#340). This required lots of tests in
test/location.coffee to be updated. Note that some updates to those tests are
unrelated to this point; some have been updated to be more consistent (I
discovered this because the refactored code happened to be seemingly more
correct).
- Regular regex literals used to erraneously allow newlines to be escaped,
causing invalid JavaScript output. This has been fixed.
- Heregexes may now be completely empty (`//////`), instead of erroring out with
a confusing message.
- Fix#2388: Heredocs and heregexes used to be lexed simply, which meant that
you couldn't nest a heredoc within a heredoc (double-quoted, that is) or a
heregex inside a heregex.
- Fix#2321: If you used division inside interpolation and then a slash later in
the string containing that interpolation, the division slash and the latter
slash was erraneously matched as a regex. This has been fixed.
- Indentation inside interpolations in heredocs no longer affect how much
indentation is removed from each line of the heredoc (which is more
intuitive).
- Whitespace is now correctly trimmed from the start and end of strings in a few
edge cases.
- Last but not least, the lexing of interpolated strings now seems to be more
efficient. For a regular double-quoted string, we used to use a custom
function to find the end of it (taking interpolations and interpolations
within interpolations etc. into account). Then we used to re-find the
interpolations and recursively lex their contents. In effect, the same string
was processed twice, or even more in the case of deeper nesting of
interpolations. Now the same string is processed just once.
- Code duplication between regular strings, heredocs, regular regexes and
heregexes has been reduced.
- The above two points should result in more easily read code, too.
Currently CoffeeScript reports a success even if it failed to write a
compiled file to disk. This behavior confuses automated tools such as
test runners which may return false positives if a test failed to
compile because of a file lock.
1. It just seems like a bad practice to encourage people to run npm with `sudo`
2. The doc wasn’t consistent with itself — down below in the full “Installation” section the same command did *not* include `sudo`
Node changed their repl so that it inherits from readline.Interface.
This means that `prompt` is now the rli function and not the original
prompt string. This may be a little hacky, but I figure it would give
someone a start if they want to do a better fix.
The commit that changed this in Node is joyent/node@3ae0b17c76
This bug was mentioned in Issue #3395.
This is done by adding a root level wrapper script for
lib/coffee-script/repl, similar to how the register script is wrapped.
This allows user programs to embed a CoffeeScript REPL without digging
into CoffeeScript's internals.
Per https://www.npmjs.org/doc/json.html#preferGlobal
> If your package is primarily a command-line application that should be installed globally, then set this value to true to provide a warning if it is installed locally.
>
> It doesn't actually prevent users from installing it locally, but it does help prevent some confusion if it doesn't work as expected.
- Simplify the command to install latest master.
- Promote using `--save` when installing locally, since it automatically
locks the minor version, protecting users from accidentally installing
breaking updates.
In V8, the `stack` property of errors contains a prelude and then the
stack trace. The contents of the prelude depends on whether the error
has a message or not.
If the error has _not_ got a message, the prelude contains the name of the
error and a newline.
If the error _has_ got a message, the prelude contains the name of the
error, a colon, a space, the message and a newline.
In other words, the prelude consists of `error.toString() + "\n"`
Before, coffee-script’s patched stack traces worked exactly like that,
except that it _always_ added a colon and a space after the name of the
error.
This fix is important because it allows for easy and consistent
consumption of the stack trace only:
`stack = error.stack[error.toString().length..]`
This was added in f4a7cca075 to fix#1038
for CoffeeScript 1.0.1. `.isUndefined` was removed in
caf3da2f66 but this code was never
updated. That actually caused the behavior of this code to change
(trailing `undefined` and `return undefined` statements no longer got
optimized away) when CoffeeScript 1.3.2 was released, but the new
behavior was deemed correct in
https://github.com/jashkenas/coffee-script/issues/1038#issuecomment-14427560.
This documents current behavior. When #1038 was fixed, we also optimized
away trailing "undefined" and "return undefined", but that is no longer
the case.
The commas separating these three keywords are not code themselves – not what the user would write – so they should be outside of the `<tt>` tags.
Edits the table in the section [Operators and Aliases](http://coffeescript.org/#operators).
* Move all source path filtering to `compilePath`
* Restrict modification of `sources` to
* `compilePath` for adding
* `removeSource` for removing
* Don't add unfiltered paths to `sources` (and remove them later on)
* Use absolute paths for source files and target paths
* Memorize and explicitly check for watched directories
* Make path comparison and construction more precise and clear
* Remove now unnecessary check for special relative paths
* Break up `Closure` and merge `Closure.wrap` into `Base.compileClosure`
* Construct class closure directly in `Class.compileNode`
* Reuse `isLiteralArguments` in `Range.compileArray`
* Move all helpers to bottom of file
* Add test for #3063
* Resolves#3059: Don't remove escaped whitespace.
* Fixes#2238: Prevent escaping slashes that are already escaped.
* Fix detection of end of heregex with escaped slashes.
Add a new cli option: -g --polling [SPAN]
If state polling mode is enabled, use it.
Else use the native api.
This is useful while watching remote directory.
Such as the `fs.watch` won't catch the SMB server's file change event.
Make the REPL *CLI* use the global context so as to (a) be consistent
with the `node` REPL CLI and, therefore, (b) make packages that modify
native prototypes (such as 'colors' and 'sugar') work as expected.
Note that, by contrast, programmatic use (`require 'repl'`) will
continue to default to a NON-global context - again, consistent with
node's behavior.
Make the REPL *CLI* use the global context so as to (a) be consistent
with the `node` REPL CLI and, therefore, (b) make packages that modify
native prototypes (such as 'colors' and 'sugar') work as expected.
Note that, by contrast, programmatic use (`require 'repl'`) will
continue to default to a NON-global context - again, consistent with
node's behavior.
Note that, at least for now, CoffeeScript's own REPL *CLI* still uses a
non-global context, rendering modules such as `color`, which attempt to
modify the prototypes of JavaScript primitives, ineffective. By
contrast, node's own CLI does use the global context.
(My apologies: In the previous commit I accidentally made `useGlobal:
yes` the default for _programmatic_ use also, but the intent was to
only do it for the stand-alone *CLI*.)
Make the REPL *CLI* use the global context so as to (a) be consistent
with the `node` REPL CLI and, therefore, (b) make packages that modify
native prototypes (such as 'colors' and 'sugar') work as expected.
Note that, by contrast, programmatic use (`require 'repl'`) will
continue to default to a NON-global context - again, consistent with
node's behavior.
This will make packages that modify prototypes - e.g. 'colors', 'sugar'
- work as expected.
To verify that the `node` REPL uses the global context, execute `global
=== module.exports.repl.context`.
Note: Tests pass, except `cluster.coffee`, which, however, failed even
before these modifications.
This solves two potential problems when it comes to forking:
1) Forking will now work correctly even when `coffee` is not installed
globally.
2) Forking when using a locally installed version of `coffee` will fork
using that version, and not fallback to a globally installed version.
Fixes#2957
This makes the "stack" property more useful when it's shown on other Node.js applications that compile CoffeeScript (e.g. testing libraries) and should fix#3023. A minimal example:
$ node -e 'require("coffee-script").compile("class class")'
/usr/lib/node_modules/coffee-script/lib/coffee-script/coffee-script.js:41
throw err;
^
[stdin]:1:7: error: unexpected CLASS
class class
^^^^^
Instead of throwing the syntax errors with their source file location and needing to then catch them and call a `prettyErrorMessage` function in order to get the formatted error message, now syntax errors know how to pretty-print themselves (their `toString` method gets overridden).
An intermediate `catch` & re-`throw` is needed at the level of `CoffeeScript.compile` and friends. But the benefit of this approach is that now libraries that use the `CoffeeScript` object directly don't need to bother catching the possible compilation errors and calling a special function in order to get the nice error messages; they can just print the error itself (or let it bubble up) and the error will know how to pretty-print itself.
* Supplement missing block before `ELSE` token only for multi-line `if`.
* Don't prematurely remove `TERMINATOR` before `ELSE` (so we can differentiate single- and multi-line forms).
* Cleanup: Remove `WHEN` from `EXPRESSION_CLOSE`. (Only `LEADING_WHEN` tokens are preceded by a `TERMINATOR`.)
* Cleanup: Remove really old, inapplicable text from comment.
In #3031, an extensions variable was introduced with file-level scope
that defined the filetypes that CoffeeScript can compile. However, the
Module::load patching calls findExtension() which uses a local variable
called "extensions", which was overriding the outer level one and
causing getSourceMap() to fail.
Example: if compilation invoked like this: 'coffee -o ../lib/ -cw ./', then
Source file: ./OutputFolder/file.coffee, compiled output: ./../lib/tputFolder/
The code only expected '.' to mark the local folder. However, './' is equally valid.
The history file was set to close on process exit, when it
should close on REPL exit. Listening to the process exit
event causes a warning when more than 10 CoffeeScript REPL
instances are opened in the same program, which happens in
the test.
* Expect a blank line as delimiter between text and code (#2821).
* Don't change indentation of code. It is not necessary and leads to
erroneous locationData. (#2835)
* Don't modify blank lines and reverse the change in the lexer.
* Don't ignore indentation with mixed whitespace.
remove unnecessary parens and else statement in repl
we do this by convention of the main coffee source
fix: exit should be process.exit
compiled and build full
Before:
coffee> foo = (bar, baz, bar, qux) ->
repl:1:7: error: multiple parameters named 'bar'
foo = (bar, baz, bar, qux) ->
^^^^^^^^^^^^^^^^^^^^^^^
Now:
coffee> foo = (bar, baz, bar, qux) ->
repl:1:18: error: multiple parameters named 'bar'
foo = (bar, baz, bar, qux) ->
^^^
Also works with destructuring parameters and what have you.
Nothing really fancy here; mostly preserves the old format. Maybe if we had a more full-fledged test editor we could show the errors in-line =D
Also, i couldn't get the `rake doc` task running properly, so i mostly test this editing the index.html directly (ups!).
These are the modifications I had to do in order to get source maps working
in 27.0.1425.2 (Official Build 185250) canary. I haven't tested other
browsers.
I first looked at the V3 spec and a few examples, and I saw that the
`source` key of the source map should be called `sources`.
After doing the `source` to `sources` change, the coffee source and for
some odd reason the javascript file would not show up in the browser
dev tools (it was being fetched but not evaluated).
To fix this, I had to add the coffee source to the `sources` list in the
source map file.
Move filename processing to a `parseFileName` function in
helpers.coffee.
Map `.coffee.md` as a Literate CoffeeScript extension.
Also, make .litcoffee and .coffee.md files executable without their file
extension - eg. `coffee test` would work for a file called
`test.litcoffee`.
Since the move to the nodeREPL package, input lines to be evaluated are
now wrapped in parentheses; that is:
'foo'
would become:
('foo'
)
The old way of detecting empty lines was to see if the input string was
either totally empty, or whitespace-only. The addition of these
parentheses breaks that.
In order to fix this, we simply tweak the regex a little to ignore these
added parentheses if they're present. As an added bonus, the regex
should match empty inputs even if they aren't.
This also makes the "empty command evaluates to undefined" test pass,
for the right reasons (i.e. not because of the broken error behavior
from before).
Compatibility is kept for path.exists. Versions of node that have
made the change will use fs.exists, while older versions will fall
back to path.exists. The same goes for path.existsSync.
The readline interface of node has changed in [aad12d0][] and because of
that the autocompletion and key movement didn't work anymore. This
commit fixes this by checking whether stdin is in raw mode (i.e. invoked
as a script) or not (as a repl).
[aad12d0]: https://github.com/joyent/node/commit/aad12d0
8bc6001d27 removed autocompletions of
non-enumerable own-properties in trying to add enumerable prototype
properties to the autocompletions. This commit adds them back and unions
them with the enumerable prototype properties.
case to show it's required.
What's going on: inside of Coffee-generated closures, calling `super()`
is implicitly making use of `this` (or explicitly doing so if you look
at the output code), so we have to pass `this` through closures as if
`@` is being accessed within the closure. So, just add one more
condition to the list in `Closure::literalThis`
Currently, the only mention of heregexes' support for interpolation is
in the change log. This feature is useful enough to warrant a mention in
the heregex section itself.
I also felt that the heregex section was a bit less clear than it could
be, so I slightly reworded it.
Make REPL continuation work better. Check for trailing "\" fails when
run function is called with buffer terminated by newline. Chomp'ing
buffer to remove newline fixes this issue.
The ultraviolet gem doesn't work with Ruby 1.9, instead
we need to use the updated 'spox' versions of ultraviolet
and plist:
spox-ultraviolet, spox-plist
This new rake task: install_coffeescript_syntax loads
either the original gems if running Ruby < 1.9 or the
'spox' versions if running on Ruby >= 1.9.
Load CoffeeScript.tmLanguage directly from the github repo
for the TextMate bundle.
Parse and write the yaml-format syntax file directly
into the correct location in the ultraviolet gem.
This is only slightly problematic for the most pathological of cases
where a prelude is followed by a set of statements, none of which
generate top-level variables or attempt to return. In these cases, the
non-prelude statements will be indented. See related discussion at
e4b3e838e2.
This pleasently surprised but also confused me when it worked. Now it's
documented. No example (yet), alas, but better than nothing.
Originally added in/around 07e66dd2.
This confused me every time I Ctrl+F'ed the home page for "index" and only got
this cryptic statement:
"Comprehensions replace (and compile into) for loops, with optional guard clauses
and the value of the current array index."
Now I can see how the index is used in the code.
While recursively traversing a source directory, if a directory was encountered containing either no .coffee files (ex. an .svn metadata directory) or where the last file processed in that directory was not a .coffee file, compileJoin() might never be called.
This issue was originally introduced by a (well-needed) optimization in commit dc272a680b.
In join mode, anytime the 'unprocessed' count is decremented, the remaining file count should be evaluated to determine if it is time to run compileJoin(). Previously, compileJoin() would only ever be called in one of the four possible terminating branches of this recursive asynchronous operation.
CoffeeScript.eval. Instead of writing about all the changes and why I
made those decisions, I'll just answer any questions in the commit
comments, so add a commit comment if you want to question anything.
Thanks to @TrevorBurnham and @satyr for their help/contributions. Also,
closes#1487. And still no REPL tests...
CoffeeScript.eval. Instead of writing about all the changes and why I
made those decisions, I'll just answer any questions in the commit
comments, so add a commit comment if you want to question anything.
Thanks to @TrevorBurnham and @satyr for their help/contributions. Also,
closes#1487. And still no REPL tests...
Here's how the algorithm in balancedString() was modified. When we
encounter a slash in an interpolation, we:
* try to find a heregex right after it; if found---skip it. Three
slashes always terminate a heregex, no matter if there is an open
"#{" before them or not, so we don't have to bother about
sub-interpolations inside the heregex.
* try to find a regex right after it; if found---skip it. Simple
regexen can't contain interpolations.
* otherwise, assume that the slash means division and carry on.
x = 10
([x]) -> # used to not declare var x
this is one fix, the other way to fix
it is to remove the entire if ... olen is 1 ....
block... not sure if that's a good idea or not.
*why* `require` and only `require` was affected. All other globals that
I tried were unaffected: `console`, `parseInt`, `process`, `global`,
`Array`, `Object`, `Buffer`, `setTimeout`, ...
"[v] = a ? b" must compile to
v = (typeof a != "undefined" && a !== null ? a : b)[0];
and not to:
v = typeof a != "undefined" && a !== null ? a : b[0];
Check the readline.createInterface for arity. If it is 3,
assume the newer interface requiring separate stdin and stdout.
Otherwise, use the older calling style.
This change allows files to be `--require`d before entering the REPL. It's also
an opimization, since files are `--require`d only once, rather than being
required again every time a file is compiled.
A secondary change is that `module.filename` is temporarily modified. This is
somewhat less aesthetically appealing than the old approach of using
fs.realpathSync, but it allows you to run `coffee -r ./foo` rather than having
to write `coffee -r ./foo.coffee`, since Node does not accept absolute paths
without a file extension.
See the tests added to test_comprehensions.coffee. Previously, after
`for i in [1..3]`, i was 4. Also, index variables were never set to
any value in comprehensions containing both a closure and a break or
return.
Allows `path.join` to do some processing on the base path
that was also happening on the full path.
Fixes: `coffee -o ./ ./`
Still broken: `coffee -o . .`
* Before you open a ticket or send a pull request, [search](https://github.com/jashkenas/coffeescript/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one.
* Before sending a pull request for a feature, be sure to have [tests](https://github.com/jashkenas/coffeescript/tree/master/test).
* Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/coffeescript/tree/master/src). If you're just getting started with CoffeeScript, there's a nice [style guide](https://github.com/polarmobile/coffeescript-style-guide).
* In your pull request, do not add documentation to `index.html` or re-build the minified `coffee-script.js` file. We'll do those things before cutting a new release.
<spanclass="nb">eval</span><spanclass="nx">CoffeeScript</span><spanclass="p">.</span><spanclass="nx">compile</span><spanclass="nx">code</span><spanclass="p">,</span><spanclass="nx">options</span></pre></div></td></tr><trid="section-3"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-3">#</a></div><p>Running code does not provide access to this scope.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">CoffeeScript.run = </span><spanclass="p">(</span><spanclass="nx">code</span><spanclass="p">,</span><spanclass="nx">options</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="p">(</span><spanclass="nb">Function</span><spanclass="nx">CoffeeScript</span><spanclass="p">.</span><spanclass="nx">compile</span><spanclass="nx">code</span><spanclass="p">,</span><spanclass="nx">options</span><spanclass="p">)()</span></pre></div></td></tr><trid="section-4"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-4">#</a></div><p>If we're not in a browser environment, we're finished with the public API.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="k">return</span><spanclass="nx">unless</span><spanclass="nb">window</span><spanclass="o">?</span></pre></div></td></tr><trid="section-5"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-5">#</a></div><p>Load a remote script from the current domain via XHR.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">CoffeeScript.load = </span><spanclass="p">(</span><spanclass="nx">url</span><spanclass="p">,</span><spanclass="nx">options</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">xhr</span><spanclass="p">.</span><spanclass="nx">send</span><spanclass="kc">null</span></pre></div></td></tr><trid="section-6"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-6">#</a></div><p>Activate CoffeeScript in the browser by having it compile and evaluate
all script tags with a content-type of <code>text/coffeescript</code>.
This happens on page load.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">processScripts = </span><spanclass="o">-></span>
<spanclass="nv">CoffeeScript = </span><spanclass="nx">require</span><spanclass="s1">'./coffee-script'</span></pre></div></td></tr><trid="section-3"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-3">#</a></div><p>Keep track of the list of defined tasks, the accepted options, and so on.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">tasks = </span><spanclass="p">{}</span>
<spanclass="nv">oparse = </span><spanclass="kc">null</span></pre></div></td></tr><trid="section-4"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-4">#</a></div><p>Mixin the top-level Cake functions for Cakefiles to use directly.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">helpers</span><spanclass="p">.</span><spanclass="nx">extend</span><spanclass="nx">global</span><spanclass="p">,</span></pre></div></td></tr><trid="section-5"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-5">#</a></div><p>Define a Cake task with a short name, an optional sentence description,
and the function to run as the action itself.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">task</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">name</span><spanclass="p">,</span><spanclass="nx">description</span><spanclass="p">,</span><spanclass="nx">action</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">tasks</span><spanclass="p">[</span><spanclass="nx">name</span><spanclass="p">]</span><spanclass="o">=</span><spanclass="p">{</span><spanclass="nx">name</span><spanclass="p">,</span><spanclass="nx">description</span><spanclass="p">,</span><spanclass="nx">action</span><spanclass="p">}</span></pre></div></td></tr><trid="section-6"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-6">#</a></div><p>Define an option that the Cakefile accepts. The parsed options hash,
containing all of the command-line options passed, will be made available
as the first argument to the action.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">option</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">letter</span><spanclass="p">,</span><spanclass="nx">flag</span><spanclass="p">,</span><spanclass="nx">description</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">switches</span><spanclass="p">.</span><spanclass="nx">push</span><spanclass="p">[</span><spanclass="nx">letter</span><spanclass="p">,</span><spanclass="nx">flag</span><spanclass="p">,</span><spanclass="nx">description</span><spanclass="p">]</span></pre></div></td></tr><trid="section-7"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-7">#</a></div><p>Invoke another task in the current Cakefile.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">invoke</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">name</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">tasks</span><spanclass="p">[</span><spanclass="nx">name</span><spanclass="p">].</span><spanclass="nx">action</span><spanclass="nx">options</span></pre></div></td></tr><trid="section-8"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-8">#</a></div><p>Run <code>cake</code>. Executes all of the tasks you pass, in order. Note that Node's
asynchrony may cause tasks to execute in a different order than you'd expect.
If no tasks are passed, print the help screen.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">exports.run = </span><spanclass="o">-></span>
<spanclass="k">throw</span><spanclass="k">new</span><spanclass="nb">Error</span><spanclass="p">(</span><spanclass="s2">"Cakefile not found in #{process.cwd()}"</span><spanclass="p">)</span><spanclass="nx">unless</span><spanclass="nx">exists</span>
<spanclass="nx">invoke</span><spanclass="nx">arg</span><spanclass="k">for</span><spanclass="nx">arg</span><spanclass="k">in</span><spanclass="nx">options</span><spanclass="p">.</span><spanclass="nx">arguments</span></pre></div></td></tr><trid="section-9"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-9">#</a></div><p>Display the list of Cake tasks in a format similar to <code>rake -T</code></p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">printTasks = </span><spanclass="o">-></span>
<spanclass="nx">puts</span><spanclass="nx">oparse</span><spanclass="p">.</span><spanclass="nx">help</span><spanclass="p">()</span><spanclass="k">if</span><spanclass="nx">switches</span><spanclass="p">.</span><spanclass="nx">length</span></pre></div></td></tr><trid="section-10"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-10">#</a></div><p>Print an error and exit when attempting to all an undefined task.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">missingTask = </span><spanclass="p">(</span><spanclass="nx">task</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">puts</span><spanclass="s2">"No such task: \"#{task}\""</span>
<spanclass="hljs-built_in">console</span>.log <spanclass="hljs-string">"<spanclass="hljs-subst">#{cakefilePath}</span> defines the following tasks:\n"</span>
<spanclass="hljs-keyword">return</span> cakefileDirectory parent <spanclass="hljs-keyword">unless</span> parent <spanclass="hljs-keyword">is</span> dir
<spanclass="hljs-keyword">throw</span><spanclass="hljs-keyword">new</span> Error <spanclass="hljs-string">"Cakefile not found in <spanclass="hljs-subst">#{process.cwd()}</span>"</span></pre></div></div>
<!DOCTYPE html><html><head><title>coffee-script.coffee</title><metahttp-equiv="content-type"content="text/html; charset=UTF-8"><linkrel="stylesheet"media="all"href="docco.css"/></head><body><divid="container"><divid="background"></div><divid="jump_to"> Jump To …<divid="jump_wrapper"><divid="jump_page"><aclass="source"href="browser.html"> browser.coffee </a><aclass="source"href="cake.html"> cake.coffee </a><aclass="source"href="coffee-script.html"> coffee-script.coffee </a><aclass="source"href="command.html"> command.coffee </a><aclass="source"href="grammar.html"> grammar.coffee </a><aclass="source"href="helpers.html"> helpers.coffee </a><aclass="source"href="index.html"> index.coffee </a><aclass="source"href="lexer.html"> lexer.coffee </a><aclass="source"href="nodes.html"> nodes.coffee </a><aclass="source"href="optparse.html"> optparse.coffee </a><aclass="source"href="repl.html"> repl.coffee </a><aclass="source"href="rewriter.html"> rewriter.coffee </a><aclass="source"href="scope.html"> scope.coffee </a></div></div></div><tablecellpadding="0"cellspacing="0"><thead><tr><thclass="docs"><h1> coffee-script.coffee </h1></th><thclass="code"></th></tr></thead><tbody><trid="section-1"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-1">#</a></div><p>CoffeeScript can be used both on the server, as a command-line compiler based
on Node.js/V8, or to run CoffeeScripts directly in the browser. This module
contains the main entry functions for tokenzing, parsing, and compiling source
CoffeeScript into JavaScript.</p>
<!DOCTYPE html>
<p>If included on a webpage, it will automatically sniff out, compile, and
execute all scripts present in <code>text/coffeescript</code> tags.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">path = </span><spanclass="nx">require</span><spanclass="s1">'path'</span>
<spanclass="nx">require</span><spanclass="p">.</span><spanclass="nx">registerExtension</span><spanclass="s1">'.coffee'</span><spanclass="p">,</span><spanclass="p">(</span><spanclass="nx">content</span><spanclass="p">)</span><spanclass="o">-></span><spanclass="nx">compile</span><spanclass="nx">content</span></pre></div></td></tr><trid="section-3"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-3">#</a></div><p>The current CoffeeScript version number.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">exports.VERSION = </span><spanclass="s1">'0.9.4'</span></pre></div></td></tr><trid="section-4"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-4">#</a></div><p>Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison
<spanclass="k">throw</span><spanclass="nx">err</span></pre></div></td></tr><trid="section-5"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-5">#</a></div><p>Tokenize a string of CoffeeScript code, and return the array of tokens.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">exports.tokens = </span><spanclass="p">(</span><spanclass="nx">code</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">lexer</span><spanclass="p">.</span><spanclass="nx">tokenize</span><spanclass="nx">code</span></pre></div></td></tr><trid="section-6"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-6">#</a></div><p>Tokenize and parse a string of CoffeeScript code, and return the AST. You can
then compile it by calling <code>.compile()</code> on the root, or traverse it by using
<code>.traverse()</code> with a callback.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">exports.nodes = </span><spanclass="p">(</span><spanclass="nx">code</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">parser</span><spanclass="p">.</span><spanclass="nx">parse</span><spanclass="nx">lexer</span><spanclass="p">.</span><spanclass="nx">tokenize</span><spanclass="nx">code</span></pre></div></td></tr><trid="section-7"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-7">#</a></div><p>Compile and execute a string of CoffeeScript (on the server), correctly
setting <code>__filename</code>, <code>__dirname</code>, and relative <code>require()</code>.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">exports.run = </span><spanclass="p">(</span><spanclass="nx">code</span><spanclass="p">,</span><spanclass="nx">options</span><spanclass="p">)</span><spanclass="o">-></span></pre></div></td></tr><trid="section-8"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-8">#</a></div><p>We want the root module.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">root = </span><spanclass="nx">module</span>
<spanclass="nv">root = </span><spanclass="nx">root</span><spanclass="p">.</span><spanclass="nx">parent</span></pre></div></td></tr><trid="section-9"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-9">#</a></div><p>Set the filename</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">root.filename = __filename = </span><spanclass="s2">"#{options.fileName} (compiled)"</span></pre></div></td></tr><trid="section-10"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-10">#</a></div><p>Clear the module cache</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">root.moduleCache = </span><spanclass="p">{}</span><spanclass="k">if</span><spanclass="nx">root</span><spanclass="p">.</span><spanclass="nx">moduleCache</span></pre></div></td></tr><trid="section-11"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-11">#</a></div><p>Compile</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">root</span><spanclass="p">.</span><spanclass="nx">_compile</span><spanclass="nx">exports</span><spanclass="p">.</span><spanclass="nx">compile</span><spanclass="p">(</span><spanclass="nx">code</span><spanclass="p">,</span><spanclass="nx">options</span><spanclass="p">),</span><spanclass="nx">root</span><spanclass="p">.</span><spanclass="nx">filename</span></pre></div></td></tr><trid="section-12"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-12">#</a></div><p>Compile and evaluate a string of CoffeeScript (in a Node.js-like environment).
The CoffeeScript REPL uses this to run the input.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">exports.eval = </span><spanclass="p">(</span><spanclass="nx">code</span><spanclass="p">,</span><spanclass="nx">options</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nb">eval</span><spanclass="nx">exports</span><spanclass="p">.</span><spanclass="nx">compile</span><spanclass="p">(</span><spanclass="nx">code</span><spanclass="p">,</span><spanclass="nx">options</span><spanclass="p">)</span></pre></div></td></tr><trid="section-13"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-13">#</a></div><p>Instantiate a Lexer for our use here.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">lexer = </span><spanclass="k">new</span><spanclass="nx">Lexer</span></pre></div></td></tr><trid="section-14"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-14">#</a></div><p>The real Lexer produces a generic stream of tokens. This object provides a
<spanclass="hljs-keyword">for</span> r <spanclass="hljs-keyword">in</span> Object.getOwnPropertyNames <spanclass="hljs-built_in">require</span><spanclass="hljs-keyword">when</span> r <spanclass="hljs-keyword">not</span><spanclass="hljs-keyword">in</span> [<spanclass="hljs-string">'paths'</span>, <spanclass="hljs-string">'arguments'</span>, <spanclass="hljs-string">'caller'</span>]
<p>Based on <ahref="http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js">http://v8.googlecode.com/svn/branches/bleeding_edge/src/messages.js</a>
<!DOCTYPE html><html><head><title>helpers.coffee</title><metahttp-equiv="content-type"content="text/html; charset=UTF-8"><linkrel="stylesheet"media="all"href="docco.css"/></head><body><divid="container"><divid="background"></div><divid="jump_to"> Jump To …<divid="jump_wrapper"><divid="jump_page"><aclass="source"href="browser.html"> browser.coffee </a><aclass="source"href="cake.html"> cake.coffee </a><aclass="source"href="coffee-script.html"> coffee-script.coffee </a><aclass="source"href="command.html"> command.coffee </a><aclass="source"href="grammar.html"> grammar.coffee </a><aclass="source"href="helpers.html"> helpers.coffee </a><aclass="source"href="index.html"> index.coffee </a><aclass="source"href="lexer.html"> lexer.coffee </a><aclass="source"href="nodes.html"> nodes.coffee </a><aclass="source"href="optparse.html"> optparse.coffee </a><aclass="source"href="repl.html"> repl.coffee </a><aclass="source"href="rewriter.html"> rewriter.coffee </a><aclass="source"href="scope.html"> scope.coffee </a></div></div></div><tablecellpadding="0"cellspacing="0"><thead><tr><thclass="docs"><h1> helpers.coffee </h1></th><thclass="code"></th></tr></thead><tbody><trid="section-1"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-1">#</a></div><p>This file contains the common helper functions that we'd like to share among
the <strong>Lexer</strong>, <strong>Rewriter</strong>, and the <strong>Nodes</strong>. Merge objects, flatten
arrays, count characters, that sort of thing.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers = exports.helpers = </span><spanclass="p">{}</span></pre></div></td></tr><trid="section-2"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-2">#</a></div><p>Cross-browser indexOf, so that IE can join the party.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.indexOf = indexOf = </span><spanclass="p">(</span><spanclass="nx">array</span><spanclass="p">,</span><spanclass="nx">item</span><spanclass="p">,</span><spanclass="nx">from</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="o">-</span><spanclass="mi">1</span></pre></div></td></tr><trid="section-3"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-3">#</a></div><p>Does a list include a value?</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.include = include = </span><spanclass="p">(</span><spanclass="nx">list</span><spanclass="p">,</span><spanclass="nx">value</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">indexOf</span><spanclass="p">(</span><spanclass="nx">list</span><spanclass="p">,</span><spanclass="nx">value</span><spanclass="p">)</span><spanclass="o">>=</span><spanclass="mi">0</span></pre></div></td></tr><trid="section-4"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-4">#</a></div><p>Peek at the beginning of a given string to see if it matches a sequence.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.starts = starts = </span><spanclass="p">(</span><spanclass="nx">string</span><spanclass="p">,</span><spanclass="nx">literal</span><spanclass="p">,</span><spanclass="nx">start</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">string</span><spanclass="p">.</span><spanclass="nx">substring</span><spanclass="p">(</span><spanclass="nx">start</span><spanclass="p">,</span><spanclass="p">(</span><spanclass="nx">start</span><spanclass="o">or</span><spanclass="mi">0</span><spanclass="p">)</span><spanclass="o">+</span><spanclass="nx">literal</span><spanclass="p">.</span><spanclass="nx">length</span><spanclass="p">)</span><spanclass="o">is</span><spanclass="nx">literal</span></pre></div></td></tr><trid="section-5"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-5">#</a></div><p>Peek at the end of a given string to see if it matches a sequence.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.ends = ends = </span><spanclass="p">(</span><spanclass="nx">string</span><spanclass="p">,</span><spanclass="nx">literal</span><spanclass="p">,</span><spanclass="nx">back</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">string</span><spanclass="p">.</span><spanclass="nx">substring</span><spanclass="p">(</span><spanclass="nx">start</span><spanclass="p">,</span><spanclass="nx">start</span><spanclass="o">+</span><spanclass="nx">literal</span><spanclass="p">.</span><spanclass="nx">length</span><spanclass="p">)</span><spanclass="o">is</span><spanclass="nx">literal</span></pre></div></td></tr><trid="section-6"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-6">#</a></div><p>Trim out all falsy values from an array.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.compact = compact = </span><spanclass="p">(</span><spanclass="nx">array</span><spanclass="p">)</span><spanclass="o">-></span><spanclass="nx">item</span><spanclass="k">for</span><spanclass="nx">item</span><spanclass="k">in</span><spanclass="nx">array</span><spanclass="k">when</span><spanclass="nx">item</span></pre></div></td></tr><trid="section-7"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-7">#</a></div><p>Count the number of occurences of a character in a string.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.count = count = </span><spanclass="p">(</span><spanclass="nx">string</span><spanclass="p">,</span><spanclass="nx">letter</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">num</span></pre></div></td></tr><trid="section-8"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-8">#</a></div><p>Merge objects, returning a fresh copy with attributes from both sides.
Used every time <code>BaseNode#compile</code> is called, to allow properties in the
options hash to propagate down the tree without polluting other branches.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.merge = merge = </span><spanclass="p">(</span><spanclass="nx">options</span><spanclass="p">,</span><spanclass="nx">overrides</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">fresh</span></pre></div></td></tr><trid="section-9"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-9">#</a></div><p>Extend a source object with the properties of another object (shallow copy).
We use this to simulate Node's deprecated <code>process.mixin</code></p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.extend = extend = </span><spanclass="p">(</span><spanclass="nx">object</span><spanclass="p">,</span><spanclass="nx">properties</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="p">(</span><spanclass="nx">object</span><spanclass="p">[</span><spanclass="nx">key</span><spanclass="p">]</span><spanclass="o">=</span><spanclass="nx">val</span><spanclass="p">)</span><spanclass="k">for</span><spanclass="nx">all</span><spanclass="nx">key</span><spanclass="p">,</span><spanclass="nx">val</span><spanclass="k">of</span><spanclass="nx">properties</span></pre></div></td></tr><trid="section-10"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-10">#</a></div><p>Return a completely flattened version of an array. Handy for getting a
list of <code>children</code> from the nodes.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.flatten = flatten = </span><spanclass="p">(</span><spanclass="nx">array</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">memo</span></pre></div></td></tr><trid="section-11"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-11">#</a></div><p>Delete a key from an object, returning the value. Useful when a node is
looking for a particular method in an options hash.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">helpers.del = del = </span><spanclass="p">(</span><spanclass="nx">obj</span><spanclass="p">,</span><spanclass="nx">key</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="hljs-keyword">return</span><spanclass="hljs-literal">true</span><spanclass="hljs-keyword">for</span> e <spanclass="hljs-keyword">in</span><spanclass="hljs-keyword">this</span><spanclass="hljs-keyword">when</span> fn e
<aclass="large"href="javascript:void(0);">Jump To …</a>
<aclass="small"href="javascript:void(0);">+</a>
<divid="jump_wrapper">
<divid="jump_page_wrapper">
<divid="jump_page">
<aclass="source"href="browser.html">
browser.coffee
</a>
<aclass="source"href="cake.html">
cake.coffee
</a>
<aclass="source"href="coffee-script.html">
coffee-script.coffee
</a>
<aclass="source"href="command.html">
command.coffee
</a>
<aclass="source"href="grammar.html">
grammar.coffee
</a>
<aclass="source"href="helpers.html">
helpers.coffee
</a>
<aclass="source"href="index.html">
index.coffee
</a>
<aclass="source"href="lexer.html">
lexer.coffee
</a>
<aclass="source"href="nodes.html">
nodes.coffee
</a>
<aclass="source"href="optparse.html">
optparse.coffee
</a>
<aclass="source"href="register.html">
register.coffee
</a>
<aclass="source"href="repl.html">
repl.coffee
</a>
<aclass="source"href="rewriter.html">
rewriter.coffee
</a>
<aclass="source"href="scope.html">
scope.litcoffee
</a>
<aclass="source"href="sourcemap.html">
sourcemap.litcoffee
</a>
</div>
</div>
</li>
</ul>
<ulclass="sections">
<liid="title">
<divclass="annotation">
<h1>index.coffee</h1>
</div>
</li>
<liid="section-1">
<divclass="annotation">
<divclass="pilwrap ">
<aclass="pilcrow"href="#section-1">¶</a>
</div>
<p>Loader for CoffeeScript as a Node.js library.</p>
</div>
<divclass="content"><divclass='highlight'><pre>exports[key] = val <spanclass="hljs-keyword">for</span> key, val <spanclass="hljs-keyword">of</span><spanclass="hljs-built_in">require</span><spanclass="hljs-string">'./coffee-script'</span></pre></div></div>
<p>The first non-option is considered to be the start of the file (and file
option) list, and all subsequent arguments are left unparsed.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">exports.OptionParser = </span><spanclass="nx">class</span><spanclass="nx">OptionParser</span></pre></div></td></tr><trid="section-2"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-2">#</a></div><p>Initialize with a list of valid options, in the form:</p>
</code></pre><p>The first non-option is considered to be the start of the file (and file
option) list, and all subsequent arguments are left unparsed.</p>
<p>Initialize with a list of valid options, in the form:</p>
<pre><code>[short-flag, long-flag, description]
</code></pre>
</code></pre><p>Along with an optional banner for the usage help.</p>
<p>Along with an an optional banner for the usage help.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">constructor</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">rules</span><spanclass="p">,</span><spanclass="nx">banner</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="vi">@rules=</span><spanclass="nx">buildRules</span><spanclass="nx">rules</span></pre></div></td></tr><trid="section-3"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-3">#</a></div><p>Parse the list of arguments, populating an <code>options</code> object with all of the
specified options, and returning it. <code>options.arguments</code> will be an array
containing the remaning non-option arguments. This is a simpler API than
many option parsers that allow you to attach callback actions for every
flag. Instead, you're responsible for interpreting the options object.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">parse</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">args</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">options</span></pre></div></td></tr><trid="section-4"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-4">#</a></div><p>Return the help text for this <strong>OptionParser</strong>, listing and describing all
of the valid options, for <code>--help</code> and such.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">help</span><spanclass="o">:</span><spanclass="o">-></span>
<spanclass="nv">OPTIONAL = </span><spanclass="sr">/\[(\w+(\*?))\]/</span></pre></div></td></tr><trid="section-7"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-7">#</a></div><p>Build and return the list of option rules. If the optional <em>short-flag</em> is
unspecified, leave it out by padding with <code>null</code>.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">buildRules = </span><spanclass="p">(</span><spanclass="nx">rules</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">buildRule</span><spanclass="nx">tuple</span><spanclass="p">...</span></pre></div></td></tr><trid="section-8"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-8">#</a></div><p>Build a rule from a <code>-o</code> short flag, a <code>--output [DIR]</code> long flag, and the
description of what the option does.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">buildRule = </span><spanclass="p">(</span><spanclass="nx">shortFlag</span><spanclass="p">,</span><spanclass="nx">longFlag</span><spanclass="p">,</span><spanclass="nx">description</span><spanclass="p">,</span><spanclass="nx">options</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="p">}</span></pre></div></td></tr><trid="section-9"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-9">#</a></div><p>Normalize arguments by expanding merged flags into multiple flags. This allows
you to have <code>-wl</code> be the same as <code>--watch --lint</code>.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">normalizeArguments = </span><spanclass="p">(</span><spanclass="nx">args</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="hljs-keyword">if</span> match = arg.match MULTI_FLAG
result.push <spanclass="hljs-string">'-'</span> + l <spanclass="hljs-keyword">for</span> l <spanclass="hljs-keyword">in</span> match[<spanclass="hljs-number">1</span>].split <spanclass="hljs-string">''</span>
<!DOCTYPE html><html><head><title>repl.coffee</title><metahttp-equiv="content-type"content="text/html; charset=UTF-8"><linkrel="stylesheet"media="all"href="docco.css"/></head><body><divid="container"><divid="background"></div><divid="jump_to"> Jump To …<divid="jump_wrapper"><divid="jump_page"><aclass="source"href="browser.html"> browser.coffee </a><aclass="source"href="cake.html"> cake.coffee </a><aclass="source"href="coffee-script.html"> coffee-script.coffee </a><aclass="source"href="command.html"> command.coffee </a><aclass="source"href="grammar.html"> grammar.coffee </a><aclass="source"href="helpers.html"> helpers.coffee </a><aclass="source"href="index.html"> index.coffee </a><aclass="source"href="lexer.html"> lexer.coffee </a><aclass="source"href="nodes.html"> nodes.coffee </a><aclass="source"href="optparse.html"> optparse.coffee </a><aclass="source"href="repl.html"> repl.coffee </a><aclass="source"href="rewriter.html"> rewriter.coffee </a><aclass="source"href="scope.html"> scope.coffee </a></div></div></div><tablecellpadding="0"cellspacing="0"><thead><tr><thclass="docs"><h1> repl.coffee </h1></th><thclass="code"></th></tr></thead><tbody><trid="section-1"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-1">#</a></div><p>A very simple Read-Eval-Print-Loop. Compiles one line at a time to JavaScript
and evaluates it. Good for simple tests, or poking around the <strong>Node.js</strong> API.
Using it looks like this:</p>
<!DOCTYPE html>
<pre><code>coffee> puts "#{num} bottles of beer" for num in [99..1]
</code></pre></td><tdclass="code"><divclass="highlight"><pre></pre></div></td></tr><trid="section-2"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-2">#</a></div><p>Require the <strong>coffee-script</strong> module to get access to the compiler.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">CoffeeScript = </span><spanclass="nx">require</span><spanclass="s1">'./coffee-script'</span>
<spanclass="nv">readline = </span><spanclass="nx">require</span><spanclass="s1">'readline'</span></pre></div></td></tr><trid="section-3"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-3">#</a></div><p>Start by opening up <strong>stdio</strong>.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">stdio = </span><spanclass="nx">process</span><spanclass="p">.</span><spanclass="nx">openStdin</span><spanclass="p">()</span></pre></div></td></tr><trid="section-4"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-4">#</a></div><p>Quick alias for quitting the REPL.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">helpers</span><spanclass="p">.</span><spanclass="nx">extend</span><spanclass="nx">global</span><spanclass="p">,</span><spanclass="nx">quit</span><spanclass="o">:</span><spanclass="o">-></span><spanclass="nx">process</span><spanclass="p">.</span><spanclass="nx">exit</span><spanclass="p">(</span><spanclass="mi">0</span><spanclass="p">)</span></pre></div></td></tr><trid="section-5"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-5">#</a></div><p>The main REPL function. <strong>run</strong> is called every time a line of code is entered.
Attempt to evaluate the command. If there's an exception, print it out instead
of exiting.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">run = </span><spanclass="p">(</span><spanclass="nx">buffer</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">repl</span><spanclass="p">.</span><spanclass="nx">prompt</span><spanclass="p">()</span></pre></div></td></tr><trid="section-6"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-6">#</a></div><p>Create the REPL by listening to <strong>stdin</strong>.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nv">repl = </span><spanclass="nx">readline</span><spanclass="p">.</span><spanclass="nx">createInterface</span><spanclass="nx">stdio</span>
<spanclass="hljs-keyword">if</span> major <spanclass="hljs-keyword">is</span><spanclass="hljs-number">0</span><spanclass="hljs-keyword">and</span> minor <<spanclass="hljs-number">8</span>
<spanclass="hljs-built_in">console</span>.warn <spanclass="hljs-string">"Node 0.8.0+ required for CoffeeScript REPL"</span>
<divclass="content"><divclass='highlight'><pre> repl.commands[getCommandId(repl, <spanclass="hljs-string">'load'</span>)].help = <spanclass="hljs-string">'Load code from a file into this REPL session'</span>
<aclass="large"href="javascript:void(0);">Jump To …</a>
<aclass="small"href="javascript:void(0);">+</a>
<divid="jump_wrapper">
<divid="jump_page_wrapper">
<divid="jump_page">
<aclass="source"href="browser.html">
browser.coffee
</a>
<aclass="source"href="cake.html">
cake.coffee
</a>
<aclass="source"href="coffee-script.html">
coffee-script.coffee
</a>
<aclass="source"href="command.html">
command.coffee
</a>
<aclass="source"href="grammar.html">
grammar.coffee
</a>
<aclass="source"href="helpers.html">
helpers.coffee
</a>
<aclass="source"href="index.html">
index.coffee
</a>
<aclass="source"href="lexer.html">
lexer.coffee
</a>
<aclass="source"href="nodes.html">
nodes.coffee
</a>
<aclass="source"href="optparse.html">
optparse.coffee
</a>
<aclass="source"href="register.html">
register.coffee
</a>
<aclass="source"href="repl.html">
repl.coffee
</a>
<aclass="source"href="rewriter.html">
rewriter.coffee
</a>
<aclass="source"href="scope.html">
scope.litcoffee
</a>
<aclass="source"href="sourcemap.html">
sourcemap.litcoffee
</a>
</div>
</div>
</li>
</ul>
<ulclass="sections">
<liid="title">
<divclass="annotation">
<h1>scope.litcoffee</h1>
</div>
</li>
<liid="section-1">
<divclass="annotation">
<divclass="pilwrap ">
<aclass="pilcrow"href="#section-1">¶</a>
</div>
<p>The <strong>Scope</strong> class regulates lexical scoping within CoffeeScript. As you
generate code, you create a tree of scopes in the same shape as the nested
function bodies. Each scope knows about the variables declared within it,
and has a reference to its parent enclosing scope. In this way, we know which
variables are new and need to be declared with <code>var</code>, and which are shared
with the outside.</p></td><tdclass="code"><divclass="highlight"><pre></pre></div></td></tr><trid="section-2"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-2">#</a></div><p>Import the helpers we plan to use.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="p">{</span><spanclass="nx">extend</span><spanclass="p">}</span><spanclass="o">=</span><spanclass="nx">require</span><spanclass="p">(</span><spanclass="s1">'./helpers'</span><spanclass="p">).</span><spanclass="nx">helpers</span>
with external scopes.</p>
<spanclass="nv">exports.Scope = </span><spanclass="nx">class</span><spanclass="nx">Scope</span></pre></div></td></tr><trid="section-3"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-3">#</a></div><p>The top-level <strong>Scope</strong> object.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">@root</span><spanclass="o">:</span><spanclass="kc">null</span></pre></div></td></tr><trid="section-4"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-4">#</a></div><p>Initialize a scope with its parent, for lookups up the chain,
as well as a reference to the <strong>Expressions</strong> node is belongs to, which is
where it should declare its variables, and a reference to the function that
it wraps.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">constructor</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">parent</span><spanclass="p">,</span><spanclass="nx">expressions</span><spanclass="p">,</span><spanclass="nx">method</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nv">Scope.root = </span><spanclass="k">this</span></pre></div></td></tr><trid="section-5"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-5">#</a></div><p>Create a new garbage level</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">startLevel</span><spanclass="o">:</span><spanclass="o">-></span>
<spanclass="nx">@garbage</span><spanclass="p">.</span><spanclass="nx">push</span><spanclass="p">[]</span></pre></div></td></tr><trid="section-6"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-6">#</a></div><p>Return to the previous garbage level and erase referenced temporary
variables in current level from scope.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">endLevel</span><spanclass="o">:</span><spanclass="o">-></span>
<spanclass="p">(</span><spanclass="nx">@variables</span><spanclass="p">[</span><spanclass="nx">name</span><spanclass="p">]</span><spanclass="o">=</span><spanclass="s1">'reuse'</span><spanclass="p">)</span><spanclass="k">for</span><spanclass="nx">name</span><spanclass="k">in</span><spanclass="nx">@garbage</span><spanclass="p">.</span><spanclass="nx">pop</span><spanclass="p">()</span><spanclass="k">when</span><spanclass="nx">@variables</span><spanclass="p">[</span><spanclass="nx">name</span><spanclass="p">]</span><spanclass="o">is</span><spanclass="s1">'var'</span></pre></div></td></tr><trid="section-7"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-7">#</a></div><p>Look up a variable name in lexical scope, and declare it if it does not
<spanclass="kc">false</span></pre></div></td></tr><trid="section-8"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-8">#</a></div><p>Test variables and return true the first time fn(v, k) returns true</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">any</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">fn</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="k">return</span><spanclass="kc">false</span></pre></div></td></tr><trid="section-9"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-9">#</a></div><p>Reserve a variable name as originating from a function parameter for this
scope. No <code>var</code> required for internal references.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">parameter</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">name</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">@variables</span><spanclass="p">[</span><spanclass="nx">name</span><spanclass="p">]</span><spanclass="o">=</span><spanclass="s1">'param'</span></pre></div></td></tr><trid="section-10"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-10">#</a></div><p>Just check to see if a variable has already been declared, without reserving,
walks up to the root scope.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">check</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">name</span><spanclass="p">,</span><spanclass="nx">options</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="o">!!</span><spanclass="p">(</span><spanclass="nx">@parent</span><spanclass="o">and</span><spanclass="nx">@parent</span><spanclass="p">.</span><spanclass="nx">check</span><spanclass="p">(</span><spanclass="nx">name</span><spanclass="p">))</span></pre></div></td></tr><trid="section-11"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-11">#</a></div><p>Generate a temporary variable name at the given index.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">temporary</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">type</span><spanclass="p">,</span><spanclass="nx">index</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="s1">'_'</span><spanclass="o">+</span><spanclass="p">(</span><spanclass="nx">index</span><spanclass="o">+</span><spanclass="nb">parseInt</span><spanclass="nx">type</span><spanclass="p">,</span><spanclass="mi">36</span><spanclass="p">).</span><spanclass="nx">toString</span><spanclass="p">(</span><spanclass="mi">36</span><spanclass="p">).</span><spanclass="nx">replace</span><spanclass="sr">/\d/g</span><spanclass="p">,</span><spanclass="s1">'a'</span></pre></div></td></tr><trid="section-12"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-12">#</a></div><p>If we need to store an intermediate result, find an available name for a
compiler-generated variable. <code>_var</code>, <code>_var2</code>, and so on...</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">freeVariable</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">type</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">temp</span></pre></div></td></tr><trid="section-13"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-13">#</a></div><p>Ensure that an assignment is made at the top of this scope
(or at the top-level scope, if requested).</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">assign</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">name</span><spanclass="p">,</span><spanclass="nx">value</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">@variables</span><spanclass="p">[</span><spanclass="nx">name</span><spanclass="p">]</span><spanclass="o">=</span><spanclass="nx">value</span><spanclass="o">:</span><spanclass="nx">value</span><spanclass="p">,</span><spanclass="nx">assigned</span><spanclass="o">:</span><spanclass="kc">true</span></pre></div></td></tr><trid="section-14"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-14">#</a></div><p>Does this scope reference any variables that need to be declared in the
given function body?</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">hasDeclarations</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">body</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">body</span><spanclass="o">is</span><spanclass="nx">@expressions</span><spanclass="o">and</span><spanclass="nx">@any</span><spanclass="p">(</span><spanclass="nx">k</span><spanclass="p">,</span><spanclass="nx">val</span><spanclass="p">)</span><spanclass="o">-></span><spanclass="nx">val</span><spanclass="o">is</span><spanclass="s1">'var'</span><spanclass="o">or</span><spanclass="nx">val</span><spanclass="o">is</span><spanclass="s1">'reuse'</span></pre></div></td></tr><trid="section-15"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-15">#</a></div><p>Does this scope reference any assignments that need to be declared at the
top of the given function body?</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">hasAssignments</span><spanclass="o">:</span><spanclass="p">(</span><spanclass="nx">body</span><spanclass="p">)</span><spanclass="o">-></span>
<spanclass="nx">body</span><spanclass="o">is</span><spanclass="nx">@expressions</span><spanclass="o">and</span><spanclass="nx">@any</span><spanclass="p">(</span><spanclass="nx">k</span><spanclass="p">,</span><spanclass="nx">val</span><spanclass="p">)</span><spanclass="o">-></span><spanclass="nx">val</span><spanclass="p">.</span><spanclass="nx">assigned</span></pre></div></td></tr><trid="section-16"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-16">#</a></div><p>Return the list of variables first declared in this scope.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">declaredVariables</span><spanclass="o">:</span><spanclass="o">-></span>
<spanclass="p">(</span><spanclass="nx">key</span><spanclass="k">for</span><spanclass="nx">key</span><spanclass="p">,</span><spanclass="nx">val</span><spanclass="k">of</span><spanclass="nx">@variables</span><spanclass="k">when</span><spanclass="nx">val</span><spanclass="o">is</span><spanclass="s1">'var'</span><spanclass="o">or</span><spanclass="nx">val</span><spanclass="o">is</span><spanclass="s1">'reuse'</span><spanclass="p">).</span><spanclass="nx">sort</span><spanclass="p">()</span></pre></div></td></tr><trid="section-17"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-17">#</a></div><p>Return the list of assignments that are supposed to be made at the top
of this scope.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">assignedVariables</span><spanclass="o">:</span><spanclass="o">-></span>
<spanclass="s2">"#{key} = #{val.value}"</span><spanclass="k">for</span><spanclass="nx">key</span><spanclass="p">,</span><spanclass="nx">val</span><spanclass="k">of</span><spanclass="nx">@variables</span><spanclass="k">when</span><spanclass="nx">val</span><spanclass="p">.</span><spanclass="nx">assigned</span></pre></div></td></tr><trid="section-18"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-18">#</a></div><p>Compile the JavaScript for all of the variable declarations in this scope.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">compiledDeclarations</span><spanclass="o">:</span><spanclass="o">-></span>
<spanclass="nx">@declaredVariables</span><spanclass="p">().</span><spanclass="nx">join</span><spanclass="s1">', '</span></pre></div></td></tr><trid="section-19"><tdclass="docs"><divclass="octowrap"><aclass="octothorpe"href="#section-19">#</a></div><p>Compile the JavaScript forall of the variable assignments in this scope.</p></td><tdclass="code"><divclass="highlight"><pre><spanclass="nx">compiledAssignments</span><spanclass="o">:</span><spanclass="o">-></span>
<spanclass="hljs-keyword">return</span> v.type <spanclass="hljs-keyword">for</span> v <spanclass="hljs-keyword">in</span> @variables <spanclass="hljs-keyword">when</span> v.name <spanclass="hljs-keyword">is</span> name
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.