diff --git a/documentation/coffee/aliases.coffee b/documentation/coffee/aliases.coffee index 4c274ef9..9301fe81 100644 --- a/documentation/coffee/aliases.coffee +++ b/documentation/coffee/aliases.coffee @@ -8,4 +8,4 @@ if car.speed < limit then accelerate() winner = yes if pick in [47, 92, 13] -print "My name is " + @name +print inspect "My name is " + @name diff --git a/documentation/coffee/interpolation_expression.coffee b/documentation/coffee/interpolation_expression.coffee index 1e5e357f..1baa482a 100644 --- a/documentation/coffee/interpolation_expression.coffee +++ b/documentation/coffee/interpolation_expression.coffee @@ -1,6 +1,6 @@ sentence = "#{ 22 / 7 } is a decent approximation of π" sep = "[.\\/\\- ]" -dates = /\d+$sep\d+$sep\d+/g +dates = /\d+#sep\d+#sep\d+/g diff --git a/documentation/coffee/objects_and_arrays.coffee b/documentation/coffee/objects_and_arrays.coffee index 737bf5f8..d98be8b5 100644 --- a/documentation/coffee/objects_and_arrays.coffee +++ b/documentation/coffee/objects_and_arrays.coffee @@ -1,6 +1,6 @@ song = ["do", "re", "mi", "fa", "so"] -singers = {Jagger: 'Rock', Elvis: 'Roll'} +singers = {Jagger: "Rock", Elvis: "Roll"} matrix = [ 1, 0, 1 @@ -10,8 +10,8 @@ matrix = [ kids = brother: - name: 'Max' + name: "Max" age: 11 sister: - name: 'Ida' + name: "Ida" age: 9 \ No newline at end of file diff --git a/documentation/css/docs.css b/documentation/css/docs.css index dc90ed8c..f6969965 100644 --- a/documentation/css/docs.css +++ b/documentation/css/docs.css @@ -112,8 +112,9 @@ div.code { background: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#d0d0d0)); background: -moz-linear-gradient(top, #f5f5f5, #d0d0d0); filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr='#F5F5F5', EndColorStr='#D0D0D0'); + border: 1px solid #aaa; -webkit-border-radius: 20px; -moz-border-radius: 20px; border-radius: 20px; - -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-box-shadow: 0 0 7px #aaa; -moz-box-shadow: 0 0 7px #aaa; } #logo { display: block; @@ -161,16 +162,19 @@ div.code { background: -moz-linear-gradient(top, #f0f0f0, #c0c0c0); filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr='#F0F0F0', EndColorStr='#C0C0C0'); } + .navigation .button { + font-weight: bold; + } .navigation .contents { display: none; position: absolute; background: #fff; - top: 50px; left: 0; + top: 51px; left: 0; padding: 5px 0; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; -webkit-border-top-left-radius: 0; -moz-border-radius-topleft: 0; -webkit-border-top-right-radius: 0; -moz-border-radius-topright: 0; - -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-box-shadow: 0 5px 10px #999; -moz-box-shadow: 0 5px 10px #999; } .navigation.active .contents { display: block; diff --git a/documentation/docs/cake.html b/documentation/docs/cake.html index 6ae44bcd..1ed917b5 100644 --- a/documentation/docs/cake.html +++ b/documentation/docs/cake.html @@ -4,39 +4,39 @@ for CoffeeScript. You define tasks with names and descriptions in a Cakefile, and can call them from the command line, or invoke them from other tasks.
Running cake
with no arguments will print out a list of all the tasks in the
-current directory's Cakefile.
External dependencies.
fs = require 'fs'
-path = require 'path'
-helpers = require('./helpers').helpers
-optparse = require './optparse'
-CoffeeScript = require './coffee-script'
Keep track of the list of defined tasks, the accepted options, and so on.
tasks = {}
-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) ->
+current directory's Cakefile.
External dependencies.
fs = require 'fs'
+path = require 'path'
+helpers = require('./helpers').helpers
+optparse = require './optparse'
+CoffeeScript = require './coffee-script'
Keep track of the list of defined tasks, the accepted options, and so on.
tasks = {}
+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) ->
[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 cake
. 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.
exports.run = ->
+If no tasks are passed, print the help screen.
exports.run = ->
path.exists 'Cakefile', (exists) ->
throw new Error("Cakefile not found in #{process.cwd()}") unless exists
- args = process.argv[2...process.argv.length]
- CoffeeScript.run fs.readFileSync('Cakefile').toString(), fileName: 'Cakefile'
- oparse = new optparse.OptionParser switches
+ args = process.argv[2...process.argv.length]
+ CoffeeScript.run fs.readFileSync('Cakefile').toString(), fileName: 'Cakefile'
+ oparse = new optparse.OptionParser switches
return printTasks() unless args.length
- options = oparse.parse(args)
- invoke arg for arg in options.arguments
Display the list of Cake tasks in a format similar to rake -T
printTasks = ->
+ options = oparse.parse(args)
+ invoke arg for arg in options.arguments
Display the list of Cake tasks in a format similar to rake -T
printTasks = ->
puts ''
for all name, task of tasks
- spaces = 20 - name.length
- spaces = if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
- desc = if task.description then "# #task.description" else ''
+ spaces = 20 - name.length
+ spaces = if spaces > 0 then (' ' for i in [0..spaces]).join('') else ''
+ desc = if task.description then "# #task.description" else ''
puts "cake #name#spaces #desc"
- puts oparse.help() if switches.length
Print an error and exit when attempting to all an undefined task.
missingTask = (task) ->
+ puts oparse.help() if switches.length
Print an error and exit when attempting to all an undefined task.
missingTask = (task) ->
puts "No such task: \"#task\""
process.exit 1
diff --git a/documentation/docs/coffee-script.html b/documentation/docs/coffee-script.html
index 03a9738e..b3b932c5 100644
--- a/documentation/docs/coffee-script.html
+++ b/documentation/docs/coffee-script.html
@@ -5,50 +5,50 @@ CoffeeScript into JavaScript.
If included on a webpage, it will automatically sniff out, compile, and
execute all scripts present in text/coffeescript
tags.
Set up dependencies correctly for both the server and the browser.
if process?
- path = require 'path'
- Lexer = require('./lexer').Lexer
- parser = require('./parser').parser
- helpers = require('./helpers').helpers
+ path = require 'path'
+ Lexer = require('./lexer').Lexer
+ parser = require('./parser').parser
+ helpers = require('./helpers').helpers
helpers.extend global, require './nodes'
if require.registerExtension
require.registerExtension '.coffee', (content) -> compile content
else
- this.exports = this.CoffeeScript = {}
- Lexer = this.Lexer
- parser = this.parser
- helpers = this.helpers
The current CoffeeScript version number.
exports.VERSION = '0.9.0'
Instantiate a Lexer for our use here.
lexer = new Lexer
Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison -compiler.
exports.compile = compile = (code, options) ->
+ this.exports = this.CoffeeScript = {}
+ Lexer = this.Lexer
+ parser = this.parser
+ helpers = this.helpers
The current CoffeeScript version number.
exports.VERSION = '0.9.0'
Instantiate a Lexer for our use here.
lexer = new Lexer
Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison +compiler.
exports.compile = compile = (code, options) ->
options or= {}
try
(parser.parse lexer.tokenize code).compile 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) ->
+ 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) ->
lexer.tokenize code
Tokenize and parse a string of CoffeeScript code, and return the AST. You can
then compile it by calling .compile()
on the root, or traverse it by using
-.traverse()
with a callback.
exports.nodes = (code) ->
+.traverse()
with a callback.
exports.nodes = (code) ->
parser.parse lexer.tokenize code
Compile and execute a string of CoffeeScript (on the server), correctly
-setting __filename
, __dirname
, and relative require()
.
exports.run = ((code, options) ->
- module.filename = __filename = options.fileName
- __dirname = path.dirname __filename
+setting __filename
, __dirname
, and relative require()
.
exports.run = ((code, options) ->
+ module.filename = __filename = options.fileName
+ __dirname = path.dirname __filename
eval exports.compile code, options
)
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: ->
- token = @tokens[@pos] or [""]
+directly as a "Jison lexer".
parser.lexer =
+ lex: ->
+ token = @tokens[@pos] or [""]
@pos += 1
- this.yylineno = token[2]
- this.yytext = token[1]
+ this.yylineno = token[2]
+ this.yytext = token[1]
token[0]
- setInput: (tokens) ->
- @tokens = tokens
- @pos = 0
- upcomingInput: -> ""
Activate CoffeeScript in the browser by having it compile and evaluate + setInput: (tokens) -> + @tokens = tokens + @pos = 0 + upcomingInput: -> ""
Activate CoffeeScript in the browser by having it compile and evaluate
all script tags with a content-type of text/coffeescript
. This happens
on page load. Unfortunately, the text contents of remote scripts cannot be
accessed from the browser, so only inline script tags will work.
if document? and document.getElementsByTagName
- processScripts = ->
+ processScripts = ->
for tag in document.getElementsByTagName('script') when tag.type is 'text/coffeescript'
eval exports.compile tag.innerHTML
if window.addEventListener
diff --git a/documentation/docs/command.html b/documentation/docs/command.html
index 38792892..2765dfe9 100644
--- a/documentation/docs/command.html
+++ b/documentation/docs/command.html
@@ -2,16 +2,16 @@
into various forms: saved into .js
files or printed to stdout, piped to
JSLint or recompiled every time the source is
saved, printed as a token stream or as the syntax tree, or launch an
-interactive REPL.
External dependencies.
fs = require 'fs'
-path = require 'path'
-optparse = require './optparse'
-CoffeeScript = require './coffee-script'
-{spawn, exec} = require 'child_process'
The help banner that is printed when coffee
is called without arguments.
BANNER = '''
+interactive REPL.
External dependencies.
fs = require 'fs'
+path = require 'path'
+optparse = require './optparse'
+CoffeeScript = require './coffee-script'
+{spawn, exec} = require 'child_process'
The help banner that is printed when coffee
is called without arguments.
BANNER = '''
coffee compiles CoffeeScript source files into JavaScript.
Usage:
coffee path/to/script.coffee
- '''
The list of all the valid option flags that coffee
knows how to handle.
SWITCHES = [
+ '''
The list of all the valid option flags that coffee
knows how to handle.
SWITCHES = [
['-c', '--compile', 'compile to JavaScript and save as .js files']
['-i', '--interactive', 'run an interactive CoffeeScript REPL']
['-o', '--output [DIR]', 'set the directory for compiled JavaScript']
@@ -25,11 +25,11 @@ interactive REPL.
Top-level objects shared by all the functions.
options = {}
-sources = []
-optionParser = null
Run coffee
by parsing passed options and determining what action to take.
+]
Top-level objects shared by all the functions.
options = {}
+sources = []
+optionParser = null
Run coffee
by parsing passed options and determining what action to take.
Many flags cause us to divert before compiling anything. Flags passed after
---
will be passed verbatim to your script as arguments in process.argv
exports.run = ->
+--
will be passed verbatim to your script as arguments in process.argv
exports.run = ->
parseOptions()
return usage() if options.help
return version() if options.version
@@ -37,21 +37,21 @@ Many flags cause us to divert before compiling anything. Flags passed after
return compileStdio() if options.stdio
return compileScript 'console', sources[0] if options.eval
return require './repl' unless sources.length
- separator = sources.indexOf '--'
- flags = []
+ separator = sources.indexOf '--'
+ flags = []
if separator >= 0
- flags = sources[(separator + 1)...sources.length]
- sources = sources[0...separator]
+ flags = sources[(separator + 1)...sources.length]
+ sources = sources[0...separator]
if options.run
- flags = sources[1..sources.length].concat flags
- sources = [sources[0]]
- process.ARGV = process.argv = flags
+ flags = sources[1..sources.length].concat flags
+ sources = [sources[0]]
+ process.ARGV = process.argv = flags
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 = ->
+'.coffee' extension source files in it and all subdirectories.
compileScripts = ->
for source in sources
- base = source
- compile = (source, topLevel) ->
+ base = source
+ compile = (source, topLevel) ->
path.exists source, (exists) ->
throw new Error "File not found: #source" unless exists
fs.stat source, (err, stats) ->
@@ -64,68 +64,69 @@ compile them. If a directory is passed, recursively compile all
watch source, base if options.watch
compile source, true
Compile a single source script, containing the given code, according to the
requested options. If evaluating the script directly sets __filename
,
-__dirname
and module.filename
to be correct relative to the script's path.
compileScript = (source, code, base) ->
- o = options
- codeOpts = compileOptions source
+__dirname
and module.filename
to be correct relative to the script's path.
compileScript = (source, code, base) ->
+ o = options
+ codeOpts = compileOptions source
try
if o.tokens then printTokens CoffeeScript.tokens code
else if o.nodes then puts CoffeeScript.nodes(code).toString()
else if o.run then CoffeeScript.run code, codeOpts
else
- js = CoffeeScript.compile code, codeOpts
+ js = CoffeeScript.compile code, codeOpts
if o.print then print js
else if o.compile then writeJs source, js, base
else if o.lint then lint js
catch err
error(err.stack) and process.exit 1 unless o.watch
puts err.message
Attach the appropriate listeners to compile scripts incoming over stdin, -and write them back to stdout.
compileStdio = ->
- code = ''
- stdin = process.openStdin()
+and write them back to stdout.
compileStdio = ->
+ code = ''
+ stdin = process.openStdin()
stdin.on 'data', (buffer) ->
code += buffer.toString() if buffer
stdin.on 'end', ->
compileScript 'stdio', code
Watch a source CoffeeScript file using fs.watchFile
, recompiling it every
time the file is updated. May be used in combination with other options,
-such as --lint
or --print
.
watch = (source, base) ->
- fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) ->
+such as --lint
or --print
.
watch = (source, base) ->
+ fs.watchFile source, {persistent: true, interval: 500}, (curr, prev) ->
return if curr.mtime.getTime() is prev.mtime.getTime()
fs.readFile source, (err, code) -> compileScript(source, code.toString(), base)
Write out a JavaScript source file with the compiled code. By default, files
are written out in cwd
as .js
files with the same name, but the output
-directory can be customized with --output
.
writeJs = (source, js, base) ->
- filename = path.basename(source, path.extname(source)) + '.js'
- srcDir = path.dirname source
- baseDir = srcDir.substring base.length
- dir = if options.output then path.join options.output, baseDir else srcDir
- jsPath = path.join dir, filename
- compile = ->
+directory can be customized with --output
.
writeJs = (source, js, base) ->
+ filename = path.basename(source, path.extname(source)) + '.js'
+ srcDir = path.dirname source
+ baseDir = srcDir.substring base.length
+ dir = if options.output then path.join options.output, baseDir else srcDir
+ jsPath = path.join dir, filename
+ compile = ->
+ return unless js.length
fs.writeFile jsPath, js, (err) ->
puts "Compiled #source" if options.compile and options.watch
path.exists dir, (exists) ->
if exists then compile() else exec "mkdir -p #dir", compile
Pipe compiled JS through JSLint (requires a working jsl
command), printing
-any errors or warnings that arise.
lint = (js) ->
- printIt = (buffer) -> print buffer.toString()
- jsl = spawn 'jsl', ['-nologo', '-stdin']
+any errors or warnings that arise.
lint = (js) ->
+ printIt = (buffer) -> print buffer.toString()
+ jsl = spawn 'jsl', ['-nologo', '-stdin']
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) ->
- strings = for token in 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]"
puts strings.join(' ')
Use the OptionParser module to extract all options from
-process.argv
that are specified in SWITCHES
.
parseOptions = ->
- optionParser = new optparse.OptionParser SWITCHES, BANNER
- o = options = optionParser.parse(process.argv[2...process.argv.length])
+process.argv
that are specified in SWITCHES
.
parseOptions = ->
+ optionParser = new optparse.OptionParser SWITCHES, BANNER
+ o = options = optionParser.parse(process.argv[2...process.argv.length])
options.compile or= !!o.output
- options.run = not (o.compile or o.print or o.lint)
- options.print = !! (o.print or (o.eval or o.stdio and o.compile))
- sources = options.arguments
The compile-time options to pass to the CoffeeScript compiler.
compileOptions = (fileName) ->
- o = {fileName}
- o.noWrap = options['no-wrap']
- o
Print the --help
usage message and exit.
usage = ->
+ options.run = not (o.compile or o.print or o.lint)
+ options.print = !! (o.print or (o.eval or o.stdio and o.compile))
+ sources = options.arguments
The compile-time options to pass to the CoffeeScript compiler.
compileOptions = (fileName) ->
+ o = {fileName}
+ o.noWrap = options['no-wrap']
+ o
Print the --help
usage message and exit.
usage = ->
puts optionParser.help()
- process.exit 0
Print the --version
message and exit.
version = ->
+ process.exit 0
Print the --version
message and exit.
version = ->
puts "CoffeeScript version #CoffeeScript.VERSION"
process.exit 0
diff --git a/documentation/docs/grammar.html b/documentation/docs/grammar.html
index ec8143e4..d0283656 100644
--- a/documentation/docs/grammar.html
+++ b/documentation/docs/grammar.html
@@ -12,15 +12,15 @@ reduces into the
The only dependency is on the Jison.Parser.
Parser = require('jison').Parser
Since we're going to be wrapped in a function by Jison in any case, if our
+from our rules and saves it into lib/parser.js
.
The only dependency is on the Jison.Parser.
Parser = require('jison').Parser
Since we're going to be wrapped in a function by Jison in any case, if our action immediately returns a value, we can optimize by removing the function -wrapper and just returning the value directly.
unwrap = /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
Our handy DSL for Jison grammar generation, thanks to +wrapper and just returning the value directly.
unwrap = /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
Our handy DSL for Jison grammar generation, thanks to Tim Caswell. For every rule in the grammar, we pass the pattern-defining string, the action to run, and extra options, optionally. If no action is specified, we simply pass the value of the -previous nonterminal.
o = (patternString, action, options) ->
+previous nonterminal.
o = (patternString, action, options) ->
return [patternString, '$$ = $1;', options] unless action
- action = if match = (action + '').match(unwrap) then match[1] else "(#action())"
+ action = if match = (action + '').match(unwrap) then match[1] else "(#action())"
[patternString, "$$ = #action;", options]
In all of the rules that follow, you'll see the name of the nonterminal as the key to a list of alternative matches. With each match's action, the dollar-sign variables are provided by Jison as references to the value of @@ -31,20 +31,20 @@ 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: [
+Expression
.
grammar =
The Root is the top-level node in the syntax tree. Since we parse bottom-up, +all parsing must end here.
Root: [
o "", -> new Expressions
o "TERMINATOR", -> new Expressions
o "Body"
o "Block TERMINATOR"
- ]
Any list of statements and expressions, seperated by line breaks or semicolons.
Body: [
+ ]
Any list of statements and expressions, seperated by line breaks or semicolons.
Body: [
o "Line", -> Expressions.wrap [$1]
o "Body TERMINATOR Line", -> $1.push $3
o "Body TERMINATOR"
- ]
Expressions and statements, which make up a line in a body.
Line: [
+ ]
Expressions 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 "BREAK", -> new LiteralNode $1
@@ -52,7 +52,7 @@ all parsing must end here.
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. Expressions serve as the building blocks of many other rules, making -them somewhat circular.
Expression: [
+them somewhat circular.
Expression: [
o "Value"
o "Call"
o "Code"
@@ -70,18 +70,18 @@ them somewhat circular.
A 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 Body OUTDENT", -> $2
o "INDENT OUTDENT", -> new Expressions
o "TERMINATOR Comment", -> Expressions.wrap [$2]
- ]
A literal identifier, a variable name or property.
Identifier: [
+ ]
A literal identifier, a variable name or property.
Identifier: [
o "IDENTIFIER", -> new LiteralNode $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 LiteralNode $1
o "STRING", -> new LiteralNode $1
]
All of our immediate values. These can (in general), be passed straight -through and printed to JavaScript.
Literal: [
+through and printed to JavaScript.
Literal: [
o "AlphaNumeric"
o "JS", -> new LiteralNode $1
o "REGEX", -> new LiteralNode $1
@@ -91,11 +91,11 @@ through and printed to JavaScript.
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 AssignNode $1, $3
o "Assignable = INDENT Expression OUTDENT", -> new AssignNode $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 "Identifier", -> new ValueNode $1
o "AlphaNumeric"
o "Identifier : Expression", -> new AssignNode new ValueNode($1), $3, 'object'
@@ -103,48 +103,48 @@ the ordinary Assign is that these allow numbers and strings as
o "Identifier : INDENT Expression OUTDENT", -> new AssignNode new ValueNode($1), $4, 'object'
o "AlphaNumeric : INDENT Expression OUTDENT", -> new AssignNode new ValueNode($1), $4, 'object'
o "Comment"
- ]
A return statement from a function body.
Return: [
+ ]
A return statement from a function body.
Return: [
o "RETURN Expression", -> new ReturnNode $2
o "RETURN", -> new ReturnNode new ValueNode new LiteralNode 'null'
- ]
A block comment.
Comment: [
+ ]
A block comment.
Comment: [
o "HERECOMMENT", -> new CommentNode $1
- ]
Existence: [
+ ]
Existence: [
o "Expression ?", -> new ExistenceNode $1
]
The Code node is the function literal. It's defined by an indented block of Expressions preceded by a function arrow, with an optional parameter -list.
Code: [
+list.
Code: [
o "PARAM_START ParamList PARAM_END FuncGlyph Block", -> new CodeNode $2, $5, $4
o "FuncGlyph Block", -> new CodeNode [], $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 "PARAM", -> new LiteralNode $1
o "@ PARAM", -> new ParamNode $2, true
o "PARAM . . .", -> new ParamNode $1, false, true
o "@ PARAM . . .", -> new ParamNode $2, true, true
- ]
A splat that occurs outside of a parameter list.
Splat: [
+ ]
A splat that occurs outside of a parameter list.
Splat: [
o "Expression . . .", -> new SplatNode $1
- ]
Variables and properties that can be assigned to.
SimpleAssignable: [
+ ]
Variables and properties that can be assigned to.
SimpleAssignable: [
o "Identifier", -> new ValueNode $1
o "Value Accessor", -> $1.push $2
o "Invocation Accessor", -> new ValueNode $1, [$2]
o "ThisProperty"
- ]
Everything that can be assigned to.
Assignable: [
+ ]
Everything that can be assigned to.
Assignable: [
o "SimpleAssignable"
o "Array", -> new ValueNode $1
o "Object", -> new ValueNode $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 ValueNode $1
o "Parenthetical", -> new ValueNode $1
@@ -152,74 +152,74 @@ as functions, indexed into, named as a class, etc.
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 "PROPERTY_ACCESS Identifier", -> new AccessorNode $2
o "PROTOTYPE_ACCESS Identifier", -> new AccessorNode $2, 'prototype'
o "::", -> new AccessorNode(new LiteralNode('prototype'))
o "SOAK_ACCESS Identifier", -> new AccessorNode $2, 'soak'
o "Index"
o "Slice", -> new SliceNode $1
- ]
Indexing into an object or array using bracket notation.
Index: [
+ ]
Indexing into an object or array using bracket notation.
Index: [
o "INDEX_START Expression INDEX_END", -> new IndexNode $2
- o "INDEX_SOAK Index", -> $2.soakNode = yes; $2
- o "INDEX_PROTO Index", -> $2.proto = yes; $2
+ o "INDEX_SOAK Index", -> $2.soakNode = yes; $2
+ o "INDEX_PROTO Index", -> $2.proto = yes; $2
]
In CoffeeScript, an object literal is simply a list of assignments.
Object: [
o "{ AssignList OptComma }", -> new ObjectNode $2
]
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 SimpleAssignable", -> new ClassNode $2
o "CLASS SimpleAssignable EXTENDS Value", -> new ClassNode $2, $4
o "CLASS SimpleAssignable INDENT ClassBody OUTDENT", -> new ClassNode $2, null, $4
o "CLASS SimpleAssignable EXTENDS Value INDENT ClassBody OUTDENT", -> new ClassNode $2, $4, $6
o "CLASS INDENT ClassBody OUTDENT", -> new ClassNode '__temp__', null, $3
- ]
Assignments that can happen directly inside a class declaration.
ClassAssign: [
+ ]
Assignments that can happen directly inside a class declaration.
ClassAssign: [
o "AssignObj", -> $1
o "ThisProperty : Expression", -> new AssignNode new ValueNode($1), $3, 'this'
- ]
A list of assignments to a class.
ClassBody: [
+ ]
A list of assignments to a class.
ClassBody: [
o "", -> []
o "ClassAssign", -> [$1]
o "ClassBody TERMINATOR ClassAssign", -> $1.concat $3
o "{ ClassBody }", -> $2
]
The three flavors of function call: normal, object instantiation with new
,
-and calling super()
Call: [
+and calling super()
Call: [
o "Invocation"
o "Super"
o "NEW Invocation", -> $2.newInstance()
o "NEW Value", -> (new CallNode($2, [])).newInstance()
]
Extending an object by setting its prototype chain to reference a parent -object.
Extends: [
+object.
Extends: [
o "SimpleAssignable EXTENDS Value", -> new ExtendsNode $1, $3
- ]
Ordinary function invocation, or a chained series of calls.
Invocation: [
+ ]
Ordinary function invocation, or a chained series of calls.
Invocation: [
o "Value Arguments", -> new CallNode $1, $2
o "Invocation Arguments", -> new CallNode $1, $2
- ]
The list of arguments to a function call.
Arguments: [
+ ]
The list of arguments to a function call.
Arguments: [
o "CALL_START ArgList OptComma CALL_END", -> $2
- ]
Calling super.
Super: [
+ ]
Calling super.
Super: [
o "SUPER", -> new CallNode 'super', [new SplatNode(new LiteralNode('arguments'))]
o "SUPER Arguments", -> new CallNode 'super', $2
- ]
A reference to the this current object.
This: [
+ ]
A reference to the this current object.
This: [
o "THIS", -> new ValueNode new LiteralNode 'this'
o "@", -> new ValueNode new LiteralNode 'this'
- ]
A reference to a property on this.
ThisProperty: [
+ ]
A reference to a property on this.
ThisProperty: [
o "@ Identifier", -> new ValueNode new LiteralNode('this'), [new AccessorNode($2)]
- ]
The CoffeeScript range literal.
Range: [
+ ]
The CoffeeScript range literal.
Range: [
o "[ Expression . . Expression ]", -> new RangeNode $2, $5
o "[ Expression . . . Expression ]", -> new RangeNode $2, $6, true
- ]
The slice literal.
Slice: [
+ ]
The slice literal.
Slice: [
o "INDEX_START Expression . . Expression INDEX_END", -> new RangeNode $2, $5
o "INDEX_START Expression . . . Expression INDEX_END", -> new RangeNode $2, $6, true
]
The array literal.
Array: [
o "[ ArgList OptComma ]", -> new ArrayNode $2
]
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 "", -> []
o "Expression", -> [$1]
o "ArgList , Expression", -> $1.concat [$3]
@@ -227,112 +227,112 @@ as well as the contents of an array literal
o "ArgList OptComma INDENT ArgList OptComma OUTDENT", -> $1.concat $4
]
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", ->
if $1 instanceof Array then $1.concat([$3]) else [$1].concat([$3])
- ]
The variants of try/catch/finally exception handling blocks.
Try: [
+ ]
The variants of try/catch/finally exception handling blocks.
Try: [
o "TRY Block Catch", -> new TryNode $2, $3[0], $3[1]
o "TRY Block FINALLY Block", -> new TryNode $2, null, null, $4
o "TRY Block Catch FINALLY Block", -> new TryNode $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 ThrowNode $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 "( Line )", -> new ParentheticalNode $2
- ]
The condition portion of a while loop.
WhileSource: [
+ ]
The condition portion of a while loop.
WhileSource: [
o "WHILE Expression", -> new WhileNode $2
- o "WHILE Expression WHEN Expression", -> new WhileNode $2, guard: $4
- o "UNTIL Expression", -> new WhileNode $2, invert: true
- o "UNTIL Expression WHEN Expression", -> new WhileNode $2, invert: true, guard: $4
+ o "WHILE Expression WHEN Expression", -> new WhileNode $2, guard: $4
+ o "UNTIL Expression", -> new WhileNode $2, invert: true
+ o "UNTIL Expression WHEN Expression", -> new WhileNode $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 Expressions.wrap [$1]
o "Expression WhileSource", -> $2.addBody Expressions.wrap [$1]
o "Loop", -> $1
]
- Loop: [
+ Loop: [
o "LOOP Block", -> new WhileNode(new LiteralNode 'true').addBody $2
o "LOOP Expression", -> new WhileNode(new LiteralNode 'true').addBody Expressions.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 ForNode $1, $2, $2.vars[0], $2.vars[1]
o "Expression ForBody", -> new ForNode $1, $2, $2.vars[0], $2.vars[1]
o "ForBody Block", -> new ForNode $2, $1, $1.vars[0], $1.vars[1]
]
- ForBody: [
- o "FOR Range", -> source: new ValueNode($2), vars: []
- o "ForStart ForSource", -> $2.raw = $1.raw; $2.vars = $1; $2
+ ForBody: [
+ o "FOR Range", -> source: new ValueNode($2), vars: []
+ o "ForStart ForSource", -> $2.raw = $1.raw; $2.vars = $1; $2
]
- ForStart: [
+ ForStart: [
o "FOR ForVariables", -> $2
- o "FOR ALL ForVariables", -> $3.raw = true; $3
+ o "FOR ALL ForVariables", -> $3.raw = true; $3
]
An array of all accepted values for a variable inside the loop. This -enables support for pattern matching.
ForValue: [
+enables support for pattern matching.
ForValue: [
o "Identifier"
o "Array", -> new ValueNode $1
o "Object", -> new ValueNode $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 "IN Expression", -> source: $2
- o "OF Expression", -> source: $2, object: true
- o "IN Expression WHEN Expression", -> source: $2, guard: $4
- o "OF Expression WHEN Expression", -> source: $2, guard: $4, object: true
- o "IN Expression BY Expression", -> source: $2, step: $4
- o "IN Expression WHEN Expression BY Expression", -> source: $2, guard: $4, step: $6
- o "IN Expression BY Expression WHEN Expression", -> source: $2, step: $4, guard: $6
+in fixed-size increments.
ForSource: [
+ o "IN Expression", -> source: $2
+ o "OF Expression", -> source: $2, object: true
+ o "IN Expression WHEN Expression", -> source: $2, guard: $4
+ o "OF Expression WHEN Expression", -> source: $2, guard: $4, object: true
+ o "IN Expression BY Expression", -> source: $2, step: $4
+ o "IN Expression WHEN Expression BY Expression", -> source: $2, guard: $4, step: $6
+ o "IN Expression BY Expression WHEN Expression", -> source: $2, step: $4, guard: $6
]
The CoffeeScript switch/when/else block replaces the JavaScript -switch/case/default by compiling into an if-else chain.
Switch: [
+switch/case/default by compiling into an if-else chain.
Switch: [
o "SWITCH Expression INDENT Whens OUTDENT", -> $4.switchesOver $2
o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.switchesOver($2).addElse $6, true
o "SWITCH INDENT Whens OUTDENT", -> $3
o "SWITCH INDENT Whens ELSE Block OUTDENT", -> $3.addElse $5, true
]
The inner list of whens is left recursive. At code-generation time, the -IfNode will rewrite them into a proper chain.
Whens: [
+IfNode will rewrite them into a proper chain.
Whens: [
o "When"
o "Whens When", -> $1.addElse $2
- ]
An individual When clause, with action.
When: [
- o "LEADING_WHEN SimpleArgs Block", -> new IfNode $2, $3, statement: true
- o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode $2, $3, statement: true
+ ]
An individual When clause, with action.
When: [
+ o "LEADING_WHEN SimpleArgs Block", -> new IfNode $2, $3, statement: true
+ o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode $2, $3, statement: true
]
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: [
+ambiguity.
IfBlock: [
o "IF Expression Block", -> new IfNode $2, $3
- o "UNLESS Expression Block", -> new IfNode $2, $3, invert: true
+ o "UNLESS Expression Block", -> new IfNode $2, $3, invert: true
o "IfBlock ELSE IF Expression Block", -> $1.addElse (new IfNode($4, $5)).forceStatement()
o "IfBlock ELSE Block", -> $1.addElse $3
]
The full complement of if expressions, including postfix one-liner -if and unless.
If: [
+if and unless.
If: [
o "IfBlock"
- o "Statement IF Expression", -> new IfNode $3, Expressions.wrap([$1]), statement: true
- o "Expression IF Expression", -> new IfNode $3, Expressions.wrap([$1]), statement: true
- o "Statement UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), statement: true, invert: true
- o "Expression UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), statement: true, invert: true
+ o "Statement IF Expression", -> new IfNode $3, Expressions.wrap([$1]), statement: true
+ o "Expression IF Expression", -> new IfNode $3, Expressions.wrap([$1]), statement: true
+ o "Statement UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), statement: true, invert: true
+ o "Expression UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), statement: true, invert: 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 "! Expression", -> new OpNode '!', $2
o "!! Expression", -> new OpNode '!!', $2
- o("- Expression", (-> new OpNode('-', $2)), {prec: 'UMINUS'})
- o("+ Expression", (-> new OpNode('+', $2)), {prec: 'UPLUS'})
+ o("- Expression", (-> new OpNode('-', $2)), {prec: 'UMINUS'})
+ o("+ Expression", (-> new OpNode('+', $2)), {prec: 'UPLUS'})
o "~ Expression", -> new OpNode '~', $2
o "-- Expression", -> new OpNode '--', $2
o "++ Expression", -> new OpNode '++', $2
@@ -390,7 +390,7 @@ down. Following these rules is what makes 2 + 3 * 4
parse as:
And not:
(2 + 3) * 4
-
operators = [
+
operators = [
["left", '?']
["nonassoc", 'UMINUS', 'UPLUS', '!', '!!', '~', '++', '--']
["left", '*', '/', '%']
@@ -413,7 +413,7 @@ down. Following these rules is what makes 2 + 3 * 4
parse as:
]
Finally, now what we have our grammar and our operators, we can create our Jison.Parser. We do this by processing all of our rules, recording all terminals (every symbol which does not appear as the name of a rule above) -as "tokens".
tokens = []
+as "tokens".
tokens = []
for name, alternatives of grammar
grammar[name] = for alt in alternatives
for token in alt[0].split ' '
@@ -422,11 +422,11 @@ as "tokens".
Initialize the Parser with our list of terminal tokens, our grammar rules, and the name of the root. Reverse the operators because Jison orders precedence from low to high, and we have it high to low -(as in Yacc).
exports.parser = new Parser {
- tokens: tokens.join ' '
- bnf: grammar
- operators: operators.reverse()
- startSymbol: 'Root'
+(as in Yacc).
exports.parser = new Parser {
+ tokens: tokens.join ' '
+ bnf: grammar
+ operators: operators.reverse()
+ startSymbol: 'Root'
}