diff --git a/documentation/coffee/block_comment.coffee b/documentation/coffee/block_comment.coffee index 47d50f3c..31ca0bd5 100644 --- a/documentation/coffee/block_comment.coffee +++ b/documentation/coffee/block_comment.coffee @@ -1,5 +1,5 @@ ### -CoffeeScript Compiler v1.1.3 +CoffeeScript Compiler v1.2.0 Released under the MIT License ### diff --git a/documentation/docs/browser.html b/documentation/docs/browser.html index cf0d6cae..5111aa95 100644 --- a/documentation/docs/browser.html +++ b/documentation/docs/browser.html @@ -1,8 +1,9 @@
browser.coffee | |||
---|---|---|---|
Override exported methods for non-Node.js engines. | CoffeeScript = require './coffee-script'
-CoffeeScript.require = require | ||
Use standard JavaScript | CoffeeScript.eval = (code, options) ->
- eval CoffeeScript.compile code, options | ||
Running code does not provide access to this scope. | CoffeeScript.run = (code, options = {}) ->
+CoffeeScript.require = require | ||
Use standard JavaScript | global ?= this
+CoffeeScript.eval = (code, options) ->
+ global.eval CoffeeScript.compile code, options | ||
Running code does not provide access to this scope. | CoffeeScript.run = (code, options = {}) ->
options.bare = on
- Function(CoffeeScript.compile code, options)() | ||
If we're not in a browser environment, we're finished with the public API. | return unless window? | ||
Load a remote script from the current domain via XHR. | CoffeeScript.load = (url, callback) ->
+ Function(CoffeeScript.compile code, options)() | ||
If we're not in a browser environment, we're finished with the public API. | return unless window? | ||
Load a remote script from the current domain via XHR. | CoffeeScript.load = (url, callback) ->
xhr = new (window.ActiveXObject or XMLHttpRequest)('Microsoft.XMLHTTP')
xhr.open 'GET', url, true
xhr.overrideMimeType 'text/plain' if 'overrideMimeType' of xhr
diff --git a/documentation/docs/cake.html b/documentation/docs/cake.html
index 9490119f..69049f64 100644
--- a/documentation/docs/cake.html
+++ b/documentation/docs/cake.html
@@ -12,35 +12,42 @@ current directory's Cakefile. | options = {} switches = [] oparse = null | |
Mixin the top-level Cake functions for Cakefiles to use directly. | helpers.extend global, | ||
Define a Cake task with a short name, an optional sentence description, -and the function to run as the action itself. | task: (name, description, action) ->
+and the function to run as the action itself. | task: (name, description, action) ->
[action, description] = [description, action] unless action
tasks[name] = {name, description, action} | |
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. | option: (letter, flag, description) ->
- switches.push [letter, flag, description] | ||
Invoke another task in the current Cakefile. | invoke: (name) ->
+as the first argument to the action. | option: (letter, flag, description) ->
+ switches.push [letter, flag, description] | |
Invoke another task in the current Cakefile. | invoke: (name) ->
missingTask name unless tasks[name]
tasks[name].action options | ||
Run | exports.run = ->
+If no tasks are passed, print the help screen. Keep a reference to the
+original directory name, when running Cake tasks from subdirectories. | exports.run = ->
global.__originalDirname = fs.realpathSync '.'
process.chdir cakefileDirectory __originalDirname
args = process.argv.slice 2
- CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile'
+ CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile'
oparse = new optparse.OptionParser switches
return printTasks() unless args.length
- options = oparse.parse(args)
+ try
+ options = oparse.parse(args)
+ catch e
+ return fatalError "#{e}"
invoke arg for arg in options.arguments | |
Display the list of Cake tasks in a format similar to | printTasks = ->
- console.log ''
+ cakefilePath = path.join path.relative(__originalDirname, process.cwd()), 'Cakefile'
+ console.log "#{cakefilePath} defines the following tasks:\n"
for name, task of tasks
spaces = 20 - name.length
spaces = if spaces > 0 then Array(spaces + 1).join(' ') else ''
desc = if task.description then "# #{task.description}" else ''
console.log "cake #{name}#{spaces} #{desc}"
- console.log oparse.help() if switches.length | ||
Print an error and exit when attempting to call an undefined task. | missingTask = (task) ->
- console.log "No such task: \"#{task}\""
- process.exit 1 | ||
When | cakefileDirectory = (dir) ->
+ console.log oparse.help() if switches.length | ||
Print an error and exit when attempting to use an invalid task/option. | fatalError = (message) ->
+ console.error message + '\n'
+ console.log 'To see a list of all tasks/options, run "cake"'
+ process.exit 1
+
+missingTask = (task) -> fatalError "No such task: #{task}" | ||
When | cakefileDirectory = (dir) ->
return dir if path.existsSync path.join dir, 'Cakefile'
parent = path.normalize path.join dir, '..'
return cakefileDirectory parent unless parent is dir
diff --git a/documentation/docs/coffee-script.html b/documentation/docs/coffee-script.html
index 343be7e8..bdf5cb31 100644
--- a/documentation/docs/coffee-script.html
+++ b/documentation/docs/coffee-script.html
@@ -9,31 +9,32 @@ execute all scripts present in | ||
TODO: Remove registerExtension when fully deprecated. | if require.extensions
- require.extensions['.coffee'] = (module, filename) ->
+ require.extensions['.coffee'] = (module, filename) ->
content = compile fs.readFileSync(filename, 'utf8'), {filename}
module._compile content, filename
else if require.registerExtension
- require.registerExtension '.coffee', (content) -> compile content | ||
The current CoffeeScript version number. | exports.VERSION = '1.1.3' | ||
Words that cannot be used as identifiers in CoffeeScript code | exports.RESERVED = RESERVED | ||
Expose helpers for testing. | exports.helpers = require './helpers' | ||
Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison -compiler. | exports.compile = compile = (code, options = {}) ->
+ require.registerExtension '.coffee', (content) -> compile content | ||
The current CoffeeScript version number. | exports.VERSION = '1.2.0' | ||
Words that cannot be used as identifiers in CoffeeScript code | exports.RESERVED = RESERVED | ||
Expose helpers for testing. | exports.helpers = require './helpers' | ||
Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison +compiler. | exports.compile = compile = (code, options = {}) ->
+ {merge} = exports.helpers
try
- (parser.parse lexer.tokenize code).compile options
+ (parser.parse lexer.tokenize code).compile merge {}, options
catch err
err.message = "In #{options.filename}, #{err.message}" if options.filename
- throw err | ||
Tokenize a string of CoffeeScript code, and return the array of tokens. | exports.tokens = (code, options) ->
+ throw err | ||
Tokenize a string of CoffeeScript code, and return the array of tokens. | exports.tokens = (code, options) ->
lexer.tokenize code, options | ||
Parse a string of CoffeeScript code or an array of lexed tokens, and
return the AST. You can then compile it by calling | exports.nodes = (source, options) ->
+or traverse it by using | exports.nodes = (source, options) ->
if typeof source is 'string'
parser.parse lexer.tokenize source, options
else
parser.parse source | |
Compile and execute a string of CoffeeScript (on the server), correctly
-setting | exports.run = (code, options) ->
+setting | exports.run = (code, options) ->
mainModule = require.main | |
Set the filename. | mainModule.filename = process.argv[1] =
if options.filename then fs.realpathSync(options.filename) else '.' | ||
Clear the module cache. | mainModule.moduleCache and= {} | ||
Assign paths for node_modules loading | mainModule.paths = require('module')._nodeModulePaths path.dirname options.filename | ||
Compile. | if path.extname(mainModule.filename) isnt '.coffee' or require.extensions
mainModule._compile compile(code, options), mainModule.filename
else
mainModule._compile code, mainModule.filename | ||
Compile and evaluate a string of CoffeeScript (in a Node.js-like environment). -The CoffeeScript REPL uses this to run the input. | exports.eval = (code, options = {}) ->
+The CoffeeScript REPL uses this to run the input. | exports.eval = (code, options = {}) ->
return unless code = code.trim()
Script = vm.Script
if Script
@@ -50,10 +51,10 @@ The CoffeeScript REPL uses this to run the input. |
sandbox.__dirname = path.dirname sandbox.__filename |
define module/require only if they chose not to specify their own | unless sandbox isnt global or sandbox.module or sandbox.require
Module = require 'module'
sandbox.module = _module = new Module(options.modulename || 'eval')
- sandbox.require = _require = (path) -> Module._load path, _module, true
+ sandbox.require = _require = (path) -> Module._load path, _module, true
_module.filename = sandbox.__filename
_require[r] = require[r] for r in Object.getOwnPropertyNames require when r isnt 'paths' | ||
use the same hack node currently uses for their own REPL | _require.paths = _module.paths = Module._nodeModulePaths process.cwd()
- _require.resolve = (request) -> Module._resolveFilename request, _module
+ _require.resolve = (request) -> Module._resolveFilename request, _module
o = {}
o[k] = v for own k, v of options
o.bare = on # ensure return value
@@ -64,12 +65,12 @@ The CoffeeScript REPL uses this to run the input. |
vm.runInContext js, sandbox ||
Instantiate a Lexer for our use here. | lexer = new Lexer | ||
The real Lexer produces a generic stream of tokens. This object provides a thin wrapper around it, compatible with the Jison API. We can then pass it directly as a "Jison lexer". | parser.lexer =
- lex: ->
+ lex: ->
[tag, @yytext, @yylineno] = @tokens[@pos++] or ['']
tag
- setInput: (@tokens) ->
+ setInput: (@tokens) ->
@pos = 0
- upcomingInput: ->
+ upcomingInput: ->
""
parser.yy = require './nodes'
diff --git a/documentation/docs/command.html b/documentation/docs/command.html
index 480e051d..b830c63e 100644
--- a/documentation/docs/command.html
+++ b/documentation/docs/command.html
@@ -10,8 +10,8 @@ interactive REPL. | {spawn, exec} = require 'child_process' {EventEmitter} = require 'events' | |
Allow CoffeeScript to emit Node.js events. | helpers.extend CoffeeScript, new EventEmitter
-printLine = (line) -> process.stdout.write line + '\n'
-printWarn = (line) -> process.stderr.write line + '\n' | ||
The help banner that is printed when | BANNER = '''
+printLine = (line) -> process.stdout.write line + '\n'
+printWarn = (line) -> process.stderr.write line + '\n' | ||
The help banner that is printed when | BANNER = '''
Usage: coffee [options] path/to/script.coffee
If called without options, `coffee` will run your script.
@@ -34,7 +34,9 @@ interactive REPL. | ['-w', '--watch', 'watch scripts for changes and rerun commands'] ] | |
Top-level objects shared by all the functions. | opts = {}
sources = []
-contents = []
+sourceCode = []
+notSources = {}
+watchers = {}
optionParser = null | ||
Run | exports.run = ->
@@ -44,6 +46,8 @@ Many flags cause us to divert before compiling anything. Flags passed after
return version() if opts.version
loadRequires() if opts.require
return require './repl' if opts.interactive
+ if opts.watch and !fs.watch
+ printWarn "The --watch feature depends on Node v0.6.0+. You are running #{process.version}."
return compileStdio() if opts.stdio
return compileScript null, sources[0] if opts.eval
return require './repl' unless sources.length
@@ -52,54 +56,40 @@ Many flags cause us to divert before compiling anything. Flags passed after
process.argv = process.argv.slice(0, 2).concat opts.literals
process.argv[0] = 'coffee'
process.execPath = require.main.filename
- compileScripts() | ||
Asynchronously read in each CoffeeScript in a list of source files and -compile them. If a directory is passed, recursively compile all -'.coffee' extension source files in it and all subdirectories. | compileScripts = ->
- unprocessed = []
- remaining_files = ->
- total = 0
- total += x for x in unprocessed
- total
- trackUnprocessedFiles = (sourceIndex, fileCount) ->
- unprocessed[sourceIndex] ?= 0
- unprocessed[sourceIndex] += fileCount
- trackCompleteFiles = (sourceIndex, fileCount) ->
- unprocessed[sourceIndex] -= fileCount
- if opts.join
- if helpers.compact(contents).length > 0 and remaining_files() == 0
- compileJoin()
for source in sources
- trackUnprocessedFiles sources.indexOf(source), 1
- for source in sources
- base = path.join(source)
- compile = (source, sourceIndex, topLevel) ->
- path.exists source, (exists) ->
- if topLevel and not exists and source[-7..] isnt '.coffee'
- return compile "#{source}.coffee", sourceIndex, topLevel
- throw new Error "File not found: #{source}" if topLevel and not exists
- fs.stat source, (err, stats) ->
+ compilePath source, yes, path.normalize source
+ | ||
Compile a path, which could be a script or a directory. If a directory +is passed, recursively compile all '.coffee' extension source files in it +and all subdirectories. | compilePath = (source, topLevel, base) ->
+ path.exists source, (exists) ->
+ if topLevel and not exists and source[-7..] isnt '.coffee'
+ source = sources[sources.indexOf(source)] = "#{source}.coffee"
+ return compilePath source, topLevel, base
+ if topLevel and not exists
+ console.error "File not found: #{source}"
+ process.exit 1
+ fs.stat source, (err, stats) ->
+ throw err if err
+ if stats.isDirectory()
+ watchDir source, base if opts.watch
+ fs.readdir source, (err, files) ->
throw err if err
- if stats.isDirectory()
- fs.readdir source, (err, files) ->
- throw err if err
- trackUnprocessedFiles sourceIndex, files.length
- for file in files
- compile path.join(source, file), sourceIndex
- trackCompleteFiles sourceIndex, 1
- else if topLevel or path.extname(source) is '.coffee'
- fs.readFile source, (err, code) ->
- throw err if err
- if opts.join
- contents[sourceIndex] = helpers.compact([contents[sourceIndex], code.toString()]).join('\n')
- else
- compileScript(source, code.toString(), base)
- trackCompleteFiles sourceIndex, 1
- watch source, base if opts.watch and not opts.join
- else
- trackCompleteFiles sourceIndex, 1
- compile source, sources.indexOf(source), true | ||
Compile a single source script, containing the given code, according to the + files = files.map (file) -> path.join source, file + index = sources.indexOf source + sources[index..index] = files + sourceCode[index..index] = files.map -> null + compilePath file, no, base for file in files + else if topLevel or path.extname(source) is '.coffee' + watch source, base if opts.watch + fs.readFile source, (err, code) -> + throw err if err + compileScript(source, code.toString(), base) + else + notSources[source] = yes + removeSource source, base + | |||
Compile a single source script, containing the given code, according to the
requested options. If evaluating the script directly sets | compileScript = (file, input, base) ->
+ | compileScript = (file, input, base) ->
o = opts
options = compileOptions file
try
@@ -108,6 +98,9 @@ requested options. If evaluating the script directly sets | compileStdio = ->
code = ''
stdin = process.openStdin()
- stdin.on 'data', (buffer) ->
+ stdin.on 'data', (buffer) ->
code += buffer.toString() if buffer
stdin.on 'end', ->
- compileScript null, code |
After all of the source files are done being read, concatenate and compile + compileScript null, code | |||
If all of the source files are done being read, concatenate and compile them together. | compileJoin = ->
- code = contents.join '\n'
- compileScript opts.join, code, opts.join | ||
Load files that are to-be-required before compilation occurs. | loadRequires = ->
+ return unless opts.join
+ unless sourceCode.some((code) -> code is null)
+ compileScript opts.join, sourceCode.join('\n'), opts.join | ||
Load files that are to-be-required before compilation occurs. | loadRequires = ->
realFilename = module.filename
module.filename = '.'
require req for req in opts.require
module.filename = realFilename | ||
Watch a source CoffeeScript file using | watch = (source, base) ->
- fs.stat source, (err, prevStats)->
- throw err if err
- fs.watch source, (event) ->
- if event is 'change'
- fs.stat source, (err, stats) ->
+such as | watch = (source, base) ->
+
+ prevStats = null
+
+ compile = ->
+ fs.stat source, (err, stats) ->
+ throw err if err
+ return if prevStats and (stats.size is prevStats.size and
+ stats.mtime.getTime() is prevStats.mtime.getTime())
+ prevStats = stats
+ fs.readFile source, (err, code) ->
+ throw err if err
+ compileScript(source, code.toString(), base)
+
+ watcher = fs.watch source, callback = (event) ->
+ if event is 'change'
+ compile()
+ else if event is 'rename'
+ watcher.close()
+ setTimeout ->
+ path.exists source, (exists) ->
+ if exists
+ compile()
+ watcher = fs.watch source, callback
+ else
+ removeSource source, base, yes
+ compileJoin()
+ , 250
+ | |
Watch a directory of files for new additions. | watchDir = (source, base) ->
+ watcher = fs.watch source, ->
+ path.exists source, (exists) ->
+ if exists
+ fs.readdir source, (err, files) ->
throw err if err
- return if stats.size is prevStats.size and
- stats.mtime.getTime() is prevStats.mtime.getTime()
- prevStats = stats
- fs.readFile source, (err, code) ->
- throw err if err
- compileScript(source, code.toString(), base) | ||
Write out a JavaScript source file with the compiled code. By default, files
-are written out in | writeJs = (source, js, base) ->
+ files = files.map (file) -> path.join source, file
+ for file in files when not notSources[file]
+ continue if sources.some (s) -> s.indexOf(file) >= 0
+ sources.push file
+ sourceCode.push null
+ compilePath file, no, base
+ else
+ watcher.close()
+ toRemove = (file for file in sources when file.indexOf(source) >= 0)
+ removeSource file, base, yes for file in toRemove
+ compileJoin()
+ | ||
Remove a file from our source list, and source code cache. Optionally remove +the compiled JS version as well. | removeSource = (source, base, removeJs) ->
+ index = sources.indexOf source
+ sources.splice index, 1
+ sourceCode.splice index, 1
+ if removeJs and not opts.join
+ jsPath = outputPath source, base
+ path.exists jsPath, (exists) ->
+ if exists
+ fs.unlink jsPath, (err) ->
+ throw err if err
+ timeLog "removed #{source}"
+ | ||
Get the corresponding output JavaScript path for a source file. | outputPath = (source, base) ->
filename = path.basename(source, path.extname(source)) + '.js'
srcDir = path.dirname source
baseDir = if base is '.' then srcDir else srcDir.substring base.length
dir = if opts.output then path.join opts.output, baseDir else srcDir
- jsPath = path.join dir, filename
- compile = ->
+ path.join dir, filename | ||
Write out a JavaScript source file with the compiled code. By default, files
+are written out in | writeJs = (source, js, base) ->
+ jsPath = outputPath source, base
+ jsDir = path.dirname jsPath
+ compile = ->
js = ' ' if js.length <= 0
- fs.writeFile jsPath, js, (err) ->
+ fs.writeFile jsPath, js, (err) ->
if err
printLine err.message
else if opts.compile and opts.watch
- console.log "#{(new Date).toLocaleTimeString()} - compiled #{source}"
- path.exists dir, (exists) ->
- if exists then compile() else exec "mkdir -p #{dir}", compile | ||
Pipe compiled JS through JSLint (requires a working | lint = (file, js) ->
- printIt = (buffer) -> printLine file + ':\t' + buffer.toString().trim()
+ timeLog "compiled #{source}"
+ path.exists jsDir, (exists) ->
+ if exists then compile() else exec "mkdir -p #{jsDir}", compile
+ | ||
When watching scripts, it's useful to log changes with the timestamp. | timeLog = (message) ->
+ console.log "#{(new Date).toLocaleTimeString()} - #{message}" | ||
Pipe compiled JS through JSLint (requires a working | lint = (file, js) ->
+ printIt = (buffer) -> printLine file + ':\t' + buffer.toString().trim()
conf = __dirname + '/../../extras/jsl.conf'
jsl = spawn 'jsl', ['-nologo', '-stdin', '-conf', conf]
jsl.stdout.on 'data', printIt
jsl.stderr.on 'data', printIt
jsl.stdin.write js
- jsl.stdin.end() | ||
Pretty-print a stream of tokens. | printTokens = (tokens) ->
+ jsl.stdin.end() | ||
Pretty-print a stream of tokens. | printTokens = (tokens) ->
strings = for token in tokens
[tag, value] = [token[0], token[1].toString().replace(/\n/, '\\n')]
"[#{tag} #{value}]"
- printLine strings.join(' ') | ||
Use the OptionParser module to extract all options from + printLine strings.join(' ') | |||
Use the OptionParser module to extract all options from
| parseOptions = ->
optionParser = new optparse.OptionParser SWITCHES, BANNER
o = opts = optionParser.parse process.argv.slice 2
o.compile or= !!o.output
o.run = not (o.compile or o.print or o.lint)
o.print = !! (o.print or (o.eval or o.stdio and o.compile))
- sources = o.arguments | ||
The compile-time options to pass to the CoffeeScript compiler. | compileOptions = (filename) -> {filename, bare: opts.bare} | ||
Start up a new Node.js instance with the arguments in | |||
The compile-time options to pass to the CoffeeScript compiler. | compileOptions = (filename) -> {filename, bare: opts.bare} | ||
Start up a new Node.js instance with the arguments in | forkNode = ->
nodeArgs = opts.nodejs.split /\s+/
args = process.argv[1..]
args.splice args.indexOf('--nodejs'), 2
spawn process.execPath, nodeArgs.concat(args),
- cwd: process.cwd()
- env: process.env
- customFds: [0, 1, 2] | ||
Print the | |||
Print the | usage = ->
- printLine (new optparse.OptionParser SWITCHES, BANNER).help() | ||
Print the | version = ->
+ printLine (new optparse.OptionParser SWITCHES, BANNER).help() | ||
Print the | version = ->
printLine "CoffeeScript version #{CoffeeScript.VERSION}"
|
o = (patternString, action, options) ->
+previous nonterminal.
o = (patternString, action, options) ->
patternString = patternString.replace /\s{2,}/g, ' '
return [patternString, '$$ = $1;', options] unless action
action = if match = unwrap.exec action then match[1] else "(#{action}())"
@@ -35,26 +35,25 @@ their numeric position, so in this rule:
$1
would be the value of the first Expression
, $2
would be the token
for the UNLESS
terminal, and $3
would be the value of the second
Expression
.
grammar =
The Root is the top-level node in the syntax tree. Since we parse bottom-up, -all parsing must end here.
Root: [
+all parsing must end here.
Root: [
o '', -> new Block
o 'Body'
o 'Block TERMINATOR'
- ]
Any list of statements and expressions, separated by line breaks or semicolons.
Body: [
+ ]
Any list of statements and expressions, separated by line breaks or semicolons.
Body: [
o 'Line', -> Block.wrap [$1]
o 'Body TERMINATOR Line', -> $1.push $3
o 'Body TERMINATOR'
- ]
Block and statements, which make up a line in a body.
Line: [
+ ]
Block and statements, which make up a line in a body.
Line: [
o 'Expression'
o 'Statement'
- ]
Pure statements which cannot be expressions.
Statement: [
+ ]
Pure statements which cannot be expressions.
Statement: [
o 'Return'
- o 'Throw'
o 'Comment'
o 'STATEMENT', -> new Literal $1
]
All the different types of expressions in our language. The basic unit of CoffeeScript is the Expression -- everything that can be an expression is one. Blocks serve as the building blocks of many other rules, making -them somewhat circular.
Expression: [
+them somewhat circular.
Expression: [
o 'Value'
o 'Invocation'
o 'Code'
@@ -66,31 +65,34 @@ them somewhat circular.
An indented block of expressions. Note that the Rewriter will convert some postfix forms into blocks for us, by adjusting the -token stream.
Block: [
+token stream.
Block: [
o 'INDENT OUTDENT', -> new Block
o 'INDENT Body OUTDENT', -> $2
- ]
A literal identifier, a variable name or property.
Identifier: [
+ ]
A literal identifier, a variable name or property.
Identifier: [
o 'IDENTIFIER', -> new Literal $1
]
Alphanumerics are separated from the other Literal matchers because -they can also serve as keys in object literals.
AlphaNumeric: [
+they can also serve as keys in object literals.
AlphaNumeric: [
o 'NUMBER', -> new Literal $1
o 'STRING', -> new Literal $1
]
All of our immediate values. Generally these can be passed straight -through and printed to JavaScript.
Literal: [
+through and printed to JavaScript.
Literal: [
o 'AlphaNumeric'
o 'JS', -> new Literal $1
o 'REGEX', -> new Literal $1
+ o 'DEBUGGER', -> new Literal $1
o 'BOOL', ->
val = new Literal $1
val.isUndefined = yes if $1 is 'undefined'
val
- ]
Assignment of a variable, property, or index to a value.
Assign: [
+ ]
Assignment of a variable, property, or index to a value.
Assign: [
o 'Assignable = Expression', -> new Assign $1, $3
+ o 'Assignable = TERMINATOR Expression', -> new Assign $1, $4
o 'Assignable = INDENT Expression OUTDENT', -> new Assign $1, $4
]
Assignment when it happens within an object literal. The difference from -the ordinary Assign is that these allow numbers and strings as keys.
AssignObj: [
+the ordinary Assign is that these allow numbers and strings as keys.
AssignObj: [
o 'ObjAssignable', -> new Value $1
o 'ObjAssignable : Expression', -> new Assign new Value($1), $3, 'object'
o 'ObjAssignable :
@@ -98,85 +100,85 @@ the ordinary Assign is that these allow numbers and strings as
o 'Comment'
]
- ObjAssignable: [
+ ObjAssignable: [
o 'Identifier'
o 'AlphaNumeric'
o 'ThisProperty'
- ]
A return statement from a function body.
Return: [
+ ]
A return statement from a function body.
Return: [
o 'RETURN Expression', -> new Return $2
o 'RETURN', -> new Return
- ]
A block comment.
Comment: [
+ ]
A block comment.
Comment: [
o 'HERECOMMENT', -> new Comment $1
]
The Code node is the function literal. It's defined by an indented block of Block preceded by a function arrow, with an optional parameter -list.
Code: [
+list.
Code: [
o 'PARAM_START ParamList PARAM_END FuncGlyph Block', -> new Code $2, $5, $4
o 'FuncGlyph Block', -> new Code [], $2, $1
]
CoffeeScript has two different symbols for functions. ->
is for ordinary
-functions, and =>
is for functions bound to the current value of this.
FuncGlyph: [
+functions, and =>
is for functions bound to the current value of this.
FuncGlyph: [
o '->', -> 'func'
o '=>', -> 'boundfunc'
- ]
An optional, trailing comma.
OptComma: [
+ ]
An optional, trailing comma.
OptComma: [
o ''
o ','
- ]
The list of parameters that a function accepts can be of any length.
ParamList: [
+ ]
The list of parameters that a function accepts can be of any length.
ParamList: [
o '', -> []
o 'Param', -> [$1]
o 'ParamList , Param', -> $1.concat $3
]
A single parameter in a function definition can be ordinary, or a splat -that hoovers up the remaining arguments.
Param: [
+that hoovers up the remaining arguments.
Param: [
o 'ParamVar', -> new Param $1
o 'ParamVar ...', -> new Param $1, null, on
o 'ParamVar = Expression', -> new Param $1, $3
- ]
Function Parameters
ParamVar: [
+ ]
Function Parameters
ParamVar: [
o 'Identifier'
o 'ThisProperty'
o 'Array'
o 'Object'
- ]
A splat that occurs outside of a parameter list.
Splat: [
+ ]
A splat that occurs outside of a parameter list.
Splat: [
o 'Expression ...', -> new Splat $1
- ]
Variables and properties that can be assigned to.
SimpleAssignable: [
+ ]
Variables and properties that can be assigned to.
SimpleAssignable: [
o 'Identifier', -> new Value $1
o 'Value Accessor', -> $1.add $2
- o 'Invocation Accessor', -> new Value $1, [$2]
+ o 'Invocation Accessor', -> new Value $1, [].concat $2
o 'ThisProperty'
- ]
Everything that can be assigned to.
Assignable: [
+ ]
Everything that can be assigned to.
Assignable: [
o 'SimpleAssignable'
o 'Array', -> new Value $1
o 'Object', -> new Value $1
]
The types of things that can be treated as values -- assigned to, invoked -as functions, indexed into, named as a class, etc.
Value: [
+as functions, indexed into, named as a class, etc.
Value: [
o 'Assignable'
o 'Literal', -> new Value $1
o 'Parenthetical', -> new Value $1
o 'Range', -> new Value $1
o 'This'
]
The general group of accessors into an object, by property, by prototype -or by array index or slice.
Accessor: [
+or by array index or slice.
Accessor: [
o '. Identifier', -> new Access $2
o '?. Identifier', -> new Access $2, 'soak'
o ':: Identifier', -> [(new Access new Literal 'prototype'), new Access $2]
o '::', -> new Access new Literal 'prototype'
o 'Index'
- ]
Indexing into an object or array using bracket notation.
Index: [
+ ]
Indexing into an object or array using bracket notation.
Index: [
o 'INDEX_START IndexValue INDEX_END', -> $2
- o 'INDEX_SOAK Index', -> extend $2, soak : yes
+ o 'INDEX_SOAK Index', -> extend $2, soak : yes
]
- IndexValue: [
+ IndexValue: [
o 'Expression', -> new Index $1
o 'Slice', -> new Slice $1
]
In CoffeeScript, an object literal is simply a list of assignments.
Object: [
o '{ AssignList OptComma }', -> new Obj $2, $1.generated
]
Assignment of properties within an object literal can be separated by -comma, as in JavaScript, or simply by newline.
AssignList: [
+comma, as in JavaScript, or simply by newline.
AssignList: [
o '', -> []
o 'AssignObj', -> [$1]
o 'AssignList , AssignObj', -> $1.concat $3
o 'AssignList OptComma TERMINATOR AssignObj', -> $1.concat $4
o 'AssignList OptComma INDENT AssignList OptComma OUTDENT', -> $1.concat $4
]
Class definitions have optional bodies of prototype property assignments, -and optional references to the superclass.
Class: [
+and optional references to the superclass.
Class: [
o 'CLASS', -> new Class
o 'CLASS Block', -> new Class null, null, $2
o 'CLASS EXTENDS Expression', -> new Class null, $3
@@ -185,152 +187,152 @@ and optional references to the superclass.
Ordinary function invocation, or a chained series of calls.
Invocation: [
+ ]
Ordinary function invocation, or a chained series of calls.
Invocation: [
o 'Value OptFuncExist Arguments', -> new Call $1, $3, $2
o 'Invocation OptFuncExist Arguments', -> new Call $1, $3, $2
o 'SUPER', -> new Call 'super', [new Splat new Literal 'arguments']
o 'SUPER Arguments', -> new Call 'super', $2
- ]
An optional existence check on a function.
OptFuncExist: [
+ ]
An optional existence check on a function.
OptFuncExist: [
o '', -> no
o 'FUNC_EXIST', -> yes
- ]
The list of arguments to a function call.
Arguments: [
+ ]
The list of arguments to a function call.
Arguments: [
o 'CALL_START CALL_END', -> []
o 'CALL_START ArgList OptComma CALL_END', -> $2
- ]
A reference to the this current object.
This: [
+ ]
A reference to the this current object.
This: [
o 'THIS', -> new Value new Literal 'this'
o '@', -> new Value new Literal 'this'
- ]
A reference to a property on this.
ThisProperty: [
+ ]
A reference to a property on this.
ThisProperty: [
o '@ Identifier', -> new Value new Literal('this'), [new Access($2)], 'this'
]
The array literal.
Array: [
o '[ ]', -> new Arr []
o '[ ArgList OptComma ]', -> new Arr $2
- ]
Inclusive and exclusive range dots.
RangeDots: [
+ ]
Inclusive and exclusive range dots.
RangeDots: [
o '..', -> 'inclusive'
o '...', -> 'exclusive'
- ]
The CoffeeScript range literal.
Range: [
+ ]
The CoffeeScript range literal.
Range: [
o '[ Expression RangeDots Expression ]', -> new Range $2, $4, $3
- ]
Array slice literals.
Slice: [
+ ]
Array slice literals.
Slice: [
o 'Expression RangeDots Expression', -> new Range $1, $3, $2
o 'Expression RangeDots', -> new Range $1, null, $2
o 'RangeDots Expression', -> new Range null, $2, $1
]
The ArgList is both the list of objects passed into a function call, as well as the contents of an array literal -(i.e. comma-separated expressions). Newlines work as well.
ArgList: [
+(i.e. comma-separated expressions). Newlines work as well.
ArgList: [
o 'Arg', -> [$1]
o 'ArgList , Arg', -> $1.concat $3
o 'ArgList OptComma TERMINATOR Arg', -> $1.concat $4
o 'INDENT ArgList OptComma OUTDENT', -> $2
o 'ArgList OptComma INDENT ArgList OptComma OUTDENT', -> $1.concat $4
- ]
Valid arguments are Blocks or Splats.
Arg: [
+ ]
Valid arguments are Blocks or Splats.
Arg: [
o 'Expression'
o 'Splat'
]
Just simple, comma-separated, required arguments (no fancy syntax). We need this to be separate from the ArgList for use in Switch blocks, where -having the newlines wouldn't make sense.
SimpleArgs: [
+having the newlines wouldn't make sense.
SimpleArgs: [
o 'Expression'
o 'SimpleArgs , Expression', -> [].concat $1, $3
- ]
The variants of try/catch/finally exception handling blocks.
Try: [
+ ]
The variants of try/catch/finally exception handling blocks.
Try: [
o 'TRY Block', -> new Try $2
o 'TRY Block Catch', -> new Try $2, $3[0], $3[1]
o 'TRY Block FINALLY Block', -> new Try $2, null, null, $4
o 'TRY Block Catch FINALLY Block', -> new Try $2, $3[0], $3[1], $5
- ]
A catch clause names its error and runs a block of code.
Catch: [
+ ]
A catch clause names its error and runs a block of code.
Catch: [
o 'CATCH Identifier Block', -> [$2, $3]
- ]
Throw an exception object.
Throw: [
+ ]
Throw an exception object.
Throw: [
o 'THROW Expression', -> new Throw $2
]
Parenthetical expressions. Note that the Parenthetical is a Value, not an Expression, so if you need to use an expression in a place where only values are accepted, wrapping it in parentheses will always do -the trick.
Parenthetical: [
+the trick.
Parenthetical: [
o '( Body )', -> new Parens $2
o '( INDENT Body OUTDENT )', -> new Parens $3
- ]
The condition portion of a while loop.
WhileSource: [
+ ]
The condition portion of a while loop.
WhileSource: [
o 'WHILE Expression', -> new While $2
- o 'WHILE Expression WHEN Expression', -> new While $2, guard: $4
- o 'UNTIL Expression', -> new While $2, invert: true
- o 'UNTIL Expression WHEN Expression', -> new While $2, invert: true, guard: $4
+ o 'WHILE Expression WHEN Expression', -> new While $2, guard: $4
+ o 'UNTIL Expression', -> new While $2, invert: true
+ o 'UNTIL Expression WHEN Expression', -> new While $2, invert: true, guard: $4
]
The while loop can either be normal, with a block of expressions to execute, -or postfix, with a single expression. There is no do..while.
While: [
+or postfix, with a single expression. There is no do..while.
While: [
o 'WhileSource Block', -> $1.addBody $2
o 'Statement WhileSource', -> $2.addBody Block.wrap [$1]
o 'Expression WhileSource', -> $2.addBody Block.wrap [$1]
o 'Loop', -> $1
]
- Loop: [
+ Loop: [
o 'LOOP Block', -> new While(new Literal 'true').addBody $2
o 'LOOP Expression', -> new While(new Literal 'true').addBody Block.wrap [$2]
]
Array, object, and range comprehensions, at the most generic level. Comprehensions can either be normal, with a block of expressions to execute, -or postfix, with a single expression.
For: [
+or postfix, with a single expression.
For: [
o 'Statement ForBody', -> new For $1, $2
o 'Expression ForBody', -> new For $1, $2
o 'ForBody Block', -> new For $2, $1
]
- ForBody: [
- o 'FOR Range', -> source: new Value($2)
+ ForBody: [
+ o 'FOR Range', -> source: new Value($2)
o 'ForStart ForSource', -> $2.own = $1.own; $2.name = $1[0]; $2.index = $1[1]; $2
]
- ForStart: [
+ ForStart: [
o 'FOR ForVariables', -> $2
o 'FOR OWN ForVariables', -> $3.own = yes; $3
]
An array of all accepted values for a variable inside the loop. -This enables support for pattern matching.
ForValue: [
+This enables support for pattern matching.
ForValue: [
o 'Identifier'
o 'Array', -> new Value $1
o 'Object', -> new Value $1
]
An array or range comprehension has variables for the current element and (optional) reference to the current index. Or, key, value, in the case -of object comprehensions.
ForVariables: [
+of object comprehensions.
ForVariables: [
o 'ForValue', -> [$1]
o 'ForValue , ForValue', -> [$1, $3]
]
The source of a comprehension is an array or object with an optional guard clause. If it's an array comprehension, you can also choose to step through -in fixed-size increments.
ForSource: [
- o 'FORIN Expression', -> source: $2
- o 'FOROF Expression', -> source: $2, object: yes
- o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
- o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
- o 'FORIN Expression BY Expression', -> source: $2, step: $4
- o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
- o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
+in fixed-size increments.
ForSource: [
+ o 'FORIN Expression', -> source: $2
+ o 'FOROF Expression', -> source: $2, object: yes
+ o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
+ o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
+ o 'FORIN Expression BY Expression', -> source: $2, step: $4
+ o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
+ o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
]
- Switch: [
+ Switch: [
o 'SWITCH Expression INDENT Whens OUTDENT', -> new Switch $2, $4
o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, $6
o 'SWITCH INDENT Whens OUTDENT', -> new Switch null, $3
o 'SWITCH INDENT Whens ELSE Block OUTDENT', -> new Switch null, $3, $5
]
- Whens: [
+ Whens: [
o 'When'
o 'Whens When', -> $1.concat $2
- ]
An individual When clause, with action.
When: [
+ ]
An individual When clause, with action.
When: [
o 'LEADING_WHEN SimpleArgs Block', -> [[$2, $3]]
o 'LEADING_WHEN SimpleArgs Block TERMINATOR', -> [[$2, $3]]
]
The most basic form of if is a condition and an action. The following if-related rules are broken up along these lines in order to avoid -ambiguity.
IfBlock: [
- o 'IF Expression Block', -> new If $2, $3, type: $1
- o 'IfBlock ELSE IF Expression Block', -> $1.addElse new If $4, $5, type: $3
+ambiguity.
IfBlock: [
+ o 'IF Expression Block', -> new If $2, $3, type: $1
+ o 'IfBlock ELSE IF Expression Block', -> $1.addElse new If $4, $5, type: $3
]
The full complement of if expressions, including postfix one-liner -if and unless.
If: [
+if and unless.
If: [
o 'IfBlock'
o 'IfBlock ELSE Block', -> $1.addElse $3
- o 'Statement POST_IF Expression', -> new If $3, Block.wrap([$1]), type: $2, statement: true
- o 'Expression POST_IF Expression', -> new If $3, Block.wrap([$1]), type: $2, statement: true
+ o 'Statement POST_IF Expression', -> new If $3, Block.wrap([$1]), type: $2, statement: true
+ o 'Expression POST_IF Expression', -> new If $3, Block.wrap([$1]), type: $2, statement: true
]
Arithmetic and logical operators, working on one or more operands. Here they are grouped by order of precedence. The actual precedence rules are defined at the bottom of the page. It would be shorter if we could combine most of these rules into a single generic Operand OpSymbol Operand -type rule, but in order to make the precedence binding possible, separate -rules are necessary.
Operation: [
+rules are necessary.
Operation: [
o 'UNARY Expression', -> new Op $1 , $2
- o '- Expression', (-> new Op '-', $2), prec: 'UNARY'
- o '+ Expression', (-> new Op '+', $2), prec: 'UNARY'
+ o '- Expression', (-> new Op '-', $2), prec: 'UNARY'
+ o '+ Expression', (-> new Op '+', $2), prec: 'UNARY'
o '-- SimpleAssignable', -> new Op '--', $2
o '++ SimpleAssignable', -> new Op '++', $2
@@ -394,9 +396,9 @@ as "tokens".
exports.parser = new Parser
- tokens : tokens.join ' '
- bnf : grammar
- operators : operators.reverse()
- startSymbol : 'Root'
+ tokens : tokens.join ' '
+ bnf : grammar
+ operators : operators.reverse()
+ startSymbol : 'Root'