├── .gitignore ├── .npmignore ├── CNAME ├── CONTRIBUTING.md ├── Cakefile ├── LICENSE ├── README.md ├── bin ├── cake └── coffee ├── documentation ├── coffee │ ├── aliases.coffee │ ├── array_comprehensions.coffee │ ├── block_comment.coffee │ ├── cake_tasks.coffee │ ├── chaining.coffee │ ├── classes.coffee │ ├── comparisons.coffee │ ├── conditionals.coffee │ ├── constructor_destructuring.coffee │ ├── default_args.coffee │ ├── do.coffee │ ├── embedded.coffee │ ├── existence.coffee │ ├── expansion.coffee │ ├── expressions.coffee │ ├── expressions_assignment.coffee │ ├── expressions_comprehension.coffee │ ├── expressions_try.coffee │ ├── fat_arrow.coffee │ ├── functions.coffee │ ├── heredocs.coffee │ ├── heregexes.coffee │ ├── interpolation.coffee │ ├── multiple_return_values.coffee │ ├── object_comprehensions.coffee │ ├── object_extraction.coffee │ ├── objects_and_arrays.coffee │ ├── objects_reserved.coffee │ ├── overview.coffee │ ├── parallel_assignment.coffee │ ├── patterns_and_splats.coffee │ ├── prototypes.coffee │ ├── range_comprehensions.coffee │ ├── scope.coffee │ ├── slices.coffee │ ├── soaks.coffee │ ├── splats.coffee │ ├── splices.coffee │ ├── strings.coffee │ ├── switch.coffee │ ├── switch_with_no_expression.coffee │ ├── try.coffee │ └── while.coffee ├── css │ ├── docs.css │ └── tomorrow.css ├── docs │ ├── browser.html │ ├── cake.html │ ├── coffee-script.html │ ├── command.html │ ├── docco.css │ ├── grammar.html │ ├── helpers.html │ ├── index.html │ ├── lexer.html │ ├── nodes.html │ ├── optparse.html │ ├── public │ │ ├── fonts │ │ │ ├── aller-bold.eot │ │ │ ├── aller-bold.ttf │ │ │ ├── aller-bold.woff │ │ │ ├── aller-light.eot │ │ │ ├── aller-light.ttf │ │ │ ├── aller-light.woff │ │ │ ├── novecento-bold.eot │ │ │ ├── novecento-bold.ttf │ │ │ └── novecento-bold.woff │ │ └── stylesheets │ │ │ └── normalize.css │ ├── register.html │ ├── repl.html │ ├── rewriter.html │ ├── scope.html │ ├── sourcemap.html │ └── underscore.html ├── images │ ├── background.png │ ├── banding.png │ ├── button_bg.png │ ├── button_bg_dark.gif │ ├── button_bg_green.gif │ ├── favicon.ico │ ├── logo.png │ └── screenshadow.png ├── index.html.js ├── js │ ├── aliases.js │ ├── array_comprehensions.js │ ├── block_comment.js │ ├── cake_tasks.js │ ├── chaining.js │ ├── classes.js │ ├── comparisons.js │ ├── conditionals.js │ ├── constructor_destructuring.js │ ├── default_args.js │ ├── do.js │ ├── embedded.js │ ├── existence.js │ ├── expansion.js │ ├── expressions.js │ ├── expressions_assignment.js │ ├── expressions_comprehension.js │ ├── expressions_try.js │ ├── fat_arrow.js │ ├── functions.js │ ├── heredocs.js │ ├── heregexes.js │ ├── interpolation.js │ ├── multiple_return_values.js │ ├── object_comprehensions.js │ ├── object_extraction.js │ ├── objects_and_arrays.js │ ├── objects_reserved.js │ ├── overview.js │ ├── parallel_assignment.js │ ├── patterns_and_splats.js │ ├── prototypes.js │ ├── range_comprehensions.js │ ├── scope.js │ ├── slices.js │ ├── soaks.js │ ├── splats.js │ ├── splices.js │ ├── strings.js │ ├── switch.js │ ├── switch_with_no_expression.js │ ├── try.js │ └── while.js └── vendor │ └── jquery-1.6.4.js ├── examples ├── beautiful_code │ ├── binary_search.coffee │ ├── quicksort_runtime.coffee │ └── regular_expression_matcher.coffee ├── blocks.coffee ├── code.coffee ├── computer_science │ ├── README │ ├── binary_search.coffee │ ├── bubble_sort.coffee │ ├── linked_list.coffee │ ├── luhn_algorithm.coffee │ ├── merge_sort.coffee │ └── selection_sort.coffee ├── poignant.coffee ├── potion.coffee ├── underscore.coffee └── web_server.coffee ├── extras └── coffee-script.js ├── index.html ├── lib └── coffee-script │ ├── browser.js │ ├── cake.js │ ├── coffee-script.js │ ├── command.js │ ├── grammar.js │ ├── helpers.js │ ├── index.js │ ├── lexer.js │ ├── nodes.js │ ├── optparse.js │ ├── parser.js │ ├── register.js │ ├── repl.js │ ├── rewriter.js │ ├── scope.js │ └── sourcemap.js ├── package.json ├── register.js ├── src ├── browser.coffee ├── cake.coffee ├── coffee-script.coffee ├── command.coffee ├── grammar.coffee ├── helpers.coffee ├── index.coffee ├── lexer.coffee ├── nodes.coffee ├── optparse.coffee ├── register.coffee ├── repl.coffee ├── rewriter.coffee ├── scope.litcoffee └── sourcemap.litcoffee └── test ├── arrays.coffee ├── assignment.coffee ├── booleans.coffee ├── classes.coffee ├── cluster.coffee ├── comments.coffee ├── compilation.coffee ├── comprehensions.coffee ├── control_flow.coffee ├── error_messages.coffee ├── eval.coffee ├── exception_handling.coffee ├── formatting.coffee ├── function_invocation.coffee ├── functions.coffee ├── helpers.coffee ├── importing.coffee ├── importing ├── .coffee ├── .coffee.md ├── .import.coffee ├── .import.coffee.md ├── .import2 ├── import.coffee ├── import.coffee.md ├── import.extension.coffee ├── import.extension.coffee.md ├── import.extension.js ├── import.js ├── import.litcoffee ├── import.unknownextension ├── import2 └── index.coffee.md ├── interpolation.coffee ├── javascript_literals.coffee ├── literate.litcoffee ├── location.coffee ├── numbers.coffee ├── objects.coffee ├── operators.coffee ├── option_parser.coffee ├── ranges.coffee ├── regexps.coffee ├── repl.coffee ├── scope.coffee ├── slicing_and_splicing.coffee ├── soaks.coffee ├── sourcemap.coffee ├── strict.coffee ├── strings.coffee └── test.html /.gitignore: -------------------------------------------------------------------------------- 1 | raw 2 | presentation 3 | test.coffee 4 | test.litcoffee 5 | parser.output 6 | test/fixtures/underscore 7 | test/*.js 8 | examples/beautiful_code/parse.coffee 9 | *.gem 10 | /node_modules 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.coffee 2 | *.html 3 | .DS_Store 4 | .git* 5 | Cakefile 6 | documentation/ 7 | examples/ 8 | extras/coffee-script.js 9 | raw/ 10 | src/ 11 | test/ 12 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | coffee-script.org 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute to CoffeeScript 2 | 3 | * Before you open a ticket or send a pull request, [search](https://github.com/jashkenas/coffee-script/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one. 4 | 5 | * Before sending a pull request for a feature, be sure to have [tests](https://github.com/jashkenas/coffee-script/tree/master/test). 6 | 7 | * Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/coffee-script/tree/master/src). If you're just getting started with CoffeeScript, there's a nice [style guide](https://github.com/polarmobile/coffeescript-style-guide). 8 | 9 | * 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. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2014 Jeremy Ashkenas 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | CoffeeScript 中文翻译 3 | ------ 4 | 5 | 本站网址: http://coffee-script.org 6 | 7 | 翻译自 coffee-script 1.7.1 8 | 9 | ### 这个仓库不再维护! 10 | 11 | 我认为 ES6 将成为主流, CoffeeScript 技术不可能大幅更新. 12 | 因此以后以 ES6 为主, 不再跟进 CoffeeScript 的更新. 13 | 14 | ### 参与翻译 15 | 16 | 步骤: 17 | 18 | 1. Fork 本仓库, 或者 pull 最新的 `gh-pages` 分支 19 | 2. 运行 `cake doc:site` 生成和监视文件, 通过静态服务器查看 `index.html` 20 | 3. 本地翻译 `documents/` 目录里的内容 21 | 4. 检查无误后提交仓库, 发起 PR 22 | 23 | @jiyinyiyong @island205 会对代码进行合并. 24 | 25 | 管理员空闲时间较少, 目前项目翻译不多, 请求参与. 26 | -------------------------------------------------------------------------------- /bin/cake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); 6 | 7 | require(lib + '/coffee-script/cake').run(); 8 | -------------------------------------------------------------------------------- /bin/coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); 6 | 7 | require(lib + '/coffee-script/command').run(); 8 | -------------------------------------------------------------------------------- /documentation/coffee/aliases.coffee: -------------------------------------------------------------------------------- 1 | launch() if ignition is on 2 | 3 | volume = 10 if band isnt SpinalTap 4 | 5 | letTheWildRumpusBegin() unless answer is no 6 | 7 | if car.speed < limit then accelerate() 8 | 9 | winner = yes if pick in [47, 92, 13] 10 | 11 | print inspect "My name is #{@name}" 12 | -------------------------------------------------------------------------------- /documentation/coffee/array_comprehensions.coffee: -------------------------------------------------------------------------------- 1 | # 吃午饭. 2 | eat food for food in ['toast', 'cheese', 'wine'] 3 | 4 | # 精致的五道菜. 5 | courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'] 6 | menu i + 1, dish for dish, i in courses 7 | 8 | # 注重健康的一餐. 9 | foods = ['broccoli', 'spinach', 'chocolate'] 10 | eat food for food in foods when food isnt 'chocolate' 11 | -------------------------------------------------------------------------------- /documentation/coffee/block_comment.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | SkinnyMochaHalfCaffScript Compiler v1.0 3 | Released under the MIT License 4 | ### 5 | 6 | 7 | -------------------------------------------------------------------------------- /documentation/coffee/cake_tasks.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | 3 | option '-o', '--output [DIR]', 'directory for compiled code' 4 | 5 | task 'build:parser', 'rebuild the Jison parser', (options) -> 6 | require 'jison' 7 | code = require('./lib/grammar').parser.generate() 8 | dir = options.output or 'lib' 9 | fs.writeFile "#{dir}/parser.js", code -------------------------------------------------------------------------------- /documentation/coffee/chaining.coffee: -------------------------------------------------------------------------------- 1 | $ 'body' 2 | .click (e) -> 3 | $ '.box' 4 | .fadeIn 'fast' 5 | .addClass '.active' 6 | .css 'background', 'white' 7 | 8 | 9 | -------------------------------------------------------------------------------- /documentation/coffee/classes.coffee: -------------------------------------------------------------------------------- 1 | class Animal 2 | constructor: (@name) -> 3 | 4 | move: (meters) -> 5 | alert @name + " moved #{meters}m." 6 | 7 | class Snake extends Animal 8 | move: -> 9 | alert "Slithering..." 10 | super 5 11 | 12 | class Horse extends Animal 13 | move: -> 14 | alert "Galloping..." 15 | super 45 16 | 17 | sam = new Snake "Sammy the Python" 18 | tom = new Horse "Tommy the Palomino" 19 | 20 | sam.move() 21 | tom.move() 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /documentation/coffee/comparisons.coffee: -------------------------------------------------------------------------------- 1 | cholesterol = 127 2 | 3 | healthy = 200 > cholesterol > 60 4 | 5 | 6 | -------------------------------------------------------------------------------- /documentation/coffee/conditionals.coffee: -------------------------------------------------------------------------------- 1 | mood = greatlyImproved if singing 2 | 3 | if happy and knowsIt 4 | clapsHands() 5 | chaChaCha() 6 | else 7 | showIt() 8 | 9 | date = if friday then sue else jill 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /documentation/coffee/constructor_destructuring.coffee: -------------------------------------------------------------------------------- 1 | class Person 2 | constructor: (options) -> 3 | {@name, @age, @height} = options 4 | 5 | tim = new Person age: 4 6 | 7 | -------------------------------------------------------------------------------- /documentation/coffee/default_args.coffee: -------------------------------------------------------------------------------- 1 | fill = (container, liquid = "coffee") -> 2 | "Filling the #{container} with #{liquid}..." 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /documentation/coffee/do.coffee: -------------------------------------------------------------------------------- 1 | for filename in list 2 | do (filename) -> 3 | fs.readFile filename, (err, contents) -> 4 | compile filename, contents.toString() -------------------------------------------------------------------------------- /documentation/coffee/embedded.coffee: -------------------------------------------------------------------------------- 1 | hi = `function() { 2 | return [document.title, "Hello JavaScript"].join(": "); 3 | }` 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /documentation/coffee/existence.coffee: -------------------------------------------------------------------------------- 1 | solipsism = true if mind? and not world? 2 | 3 | speed = 0 4 | speed ?= 15 5 | 6 | footprints = yeti ? "bear" 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /documentation/coffee/expansion.coffee: -------------------------------------------------------------------------------- 1 | text = "Every literary critic believes he will 2 | outwit history and have the last word" 3 | 4 | [first, ..., last] = text.split " " 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /documentation/coffee/expressions.coffee: -------------------------------------------------------------------------------- 1 | grade = (student) -> 2 | if student.excellentWork 3 | "A+" 4 | else if student.okayStuff 5 | if student.triedHard then "B" else "B-" 6 | else 7 | "C" 8 | 9 | eldest = if 24 > 21 then "Liz" else "Ike" -------------------------------------------------------------------------------- /documentation/coffee/expressions_assignment.coffee: -------------------------------------------------------------------------------- 1 | six = (one = 1) + (two = 2) + (three = 3) 2 | 3 | 4 | -------------------------------------------------------------------------------- /documentation/coffee/expressions_comprehension.coffee: -------------------------------------------------------------------------------- 1 | # 前十个全局属性(变量). 2 | 3 | globals = (name for name of window)[0...10] -------------------------------------------------------------------------------- /documentation/coffee/expressions_try.coffee: -------------------------------------------------------------------------------- 1 | alert( 2 | try 3 | nonexistent / undefined 4 | catch error 5 | "And the error is ... #{error}" 6 | ) 7 | 8 | -------------------------------------------------------------------------------- /documentation/coffee/fat_arrow.coffee: -------------------------------------------------------------------------------- 1 | Account = (customer, cart) -> 2 | @customer = customer 3 | @cart = cart 4 | 5 | $('.shopping_cart').bind 'click', (event) => 6 | @customer.purchase @cart -------------------------------------------------------------------------------- /documentation/coffee/functions.coffee: -------------------------------------------------------------------------------- 1 | square = (x) -> x * x 2 | cube = (x) -> square(x) * x 3 | -------------------------------------------------------------------------------- /documentation/coffee/heredocs.coffee: -------------------------------------------------------------------------------- 1 | html = """ 2 | 3 | cup of coffeescript 4 | 5 | """ 6 | 7 | -------------------------------------------------------------------------------- /documentation/coffee/heregexes.coffee: -------------------------------------------------------------------------------- 1 | OPERATOR = /// ^ ( 2 | ?: [-=]> # 函数 3 | | [-+*/%<>&|^!?=]= # 复合赋值 / 比较 4 | | >>>=? # 补 0 右移 5 | | ([-+:])\1 # 双写 6 | | ([&|<>])\2=? # 逻辑 / 移位 7 | | \?\. # soak 访问 8 | | \.{2,3} # 范围或者 splat 9 | ) /// 10 | 11 | 12 | -------------------------------------------------------------------------------- /documentation/coffee/interpolation.coffee: -------------------------------------------------------------------------------- 1 | author = "Wittgenstein" 2 | quote = "A picture is a fact. -- #{ author }" 3 | 4 | sentence = "#{ 22 / 7 } is a decent approximation of π" 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /documentation/coffee/multiple_return_values.coffee: -------------------------------------------------------------------------------- 1 | weatherReport = (location) -> 2 | # 发起一个 Ajax 请求获取天气... 3 | [location, 72, "Mostly Sunny"] 4 | 5 | [city, temp, forecast] = weatherReport "Berkeley, CA" 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /documentation/coffee/object_comprehensions.coffee: -------------------------------------------------------------------------------- 1 | yearsOld = max: 10, ida: 9, tim: 11 2 | 3 | ages = for child, age of yearsOld 4 | "#{child} is #{age}" 5 | -------------------------------------------------------------------------------- /documentation/coffee/object_extraction.coffee: -------------------------------------------------------------------------------- 1 | futurists = 2 | sculptor: "Umberto Boccioni" 3 | painter: "Vladimir Burliuk" 4 | poet: 5 | name: "F.T. Marinetti" 6 | address: [ 7 | "Via Roma 42R" 8 | "Bellagio, Italy 22021" 9 | ] 10 | 11 | {poet: {name, address: [street, city]}} = futurists 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /documentation/coffee/objects_and_arrays.coffee: -------------------------------------------------------------------------------- 1 | song = ["do", "re", "mi", "fa", "so"] 2 | 3 | singers = {Jagger: "Rock", Elvis: "Roll"} 4 | 5 | bitlist = [ 6 | 1, 0, 1 7 | 0, 0, 1 8 | 1, 1, 0 9 | ] 10 | 11 | kids = 12 | brother: 13 | name: "Max" 14 | age: 11 15 | sister: 16 | name: "Ida" 17 | age: 9 18 | 19 | 20 | -------------------------------------------------------------------------------- /documentation/coffee/objects_reserved.coffee: -------------------------------------------------------------------------------- 1 | $('.account').attr class: 'active' 2 | 3 | log object.class 4 | 5 | 6 | -------------------------------------------------------------------------------- /documentation/coffee/overview.coffee: -------------------------------------------------------------------------------- 1 | # 赋值: 2 | number = 42 3 | opposite = true 4 | 5 | # 条件: 6 | number = -42 if opposite 7 | 8 | # 函数: 9 | square = (x) -> x * x 10 | 11 | # 数组: 12 | list = [1, 2, 3, 4, 5] 13 | 14 | # 对象: 15 | math = 16 | root: Math.sqrt 17 | square: square 18 | cube: (x) -> x * square x 19 | 20 | # Splats: 21 | race = (winner, runners...) -> 22 | print winner, runners 23 | 24 | # 存在性: 25 | alert "I knew it!" if elvis? 26 | 27 | # 数组 推导(comprehensions): 28 | cubes = (math.cube num for num in list) 29 | -------------------------------------------------------------------------------- /documentation/coffee/parallel_assignment.coffee: -------------------------------------------------------------------------------- 1 | theBait = 1000 2 | theSwitch = 0 3 | 4 | [theBait, theSwitch] = [theSwitch, theBait] 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /documentation/coffee/patterns_and_splats.coffee: -------------------------------------------------------------------------------- 1 | tag = "" 2 | 3 | [open, contents..., close] = tag.split("") 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /documentation/coffee/prototypes.coffee: -------------------------------------------------------------------------------- 1 | String::dasherize = -> 2 | this.replace /_/g, "-" 3 | 4 | -------------------------------------------------------------------------------- /documentation/coffee/range_comprehensions.coffee: -------------------------------------------------------------------------------- 1 | countdown = (num for num in [10..1]) 2 | 3 | -------------------------------------------------------------------------------- /documentation/coffee/scope.coffee: -------------------------------------------------------------------------------- 1 | outer = 1 2 | changeNumbers = -> 3 | inner = -1 4 | outer = 10 5 | inner = changeNumbers() -------------------------------------------------------------------------------- /documentation/coffee/slices.coffee: -------------------------------------------------------------------------------- 1 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] 2 | 3 | start = numbers[0..2] 4 | 5 | middle = numbers[3...-2] 6 | 7 | end = numbers[-2..] 8 | 9 | copy = numbers[..] 10 | -------------------------------------------------------------------------------- /documentation/coffee/soaks.coffee: -------------------------------------------------------------------------------- 1 | zip = lottery.drawWinner?().address?.zipcode 2 | -------------------------------------------------------------------------------- /documentation/coffee/splats.coffee: -------------------------------------------------------------------------------- 1 | gold = silver = rest = "unknown" 2 | 3 | awardMedals = (first, second, others...) -> 4 | gold = first 5 | silver = second 6 | rest = others 7 | 8 | contenders = [ 9 | "Michael Phelps" 10 | "Liu Xiang" 11 | "Yao Ming" 12 | "Allyson Felix" 13 | "Shawn Johnson" 14 | "Roman Sebrle" 15 | "Guo Jingjing" 16 | "Tyson Gay" 17 | "Asafa Powell" 18 | "Usain Bolt" 19 | ] 20 | 21 | awardMedals contenders... 22 | 23 | alert "Gold: " + gold 24 | alert "Silver: " + silver 25 | alert "The Field: " + rest 26 | 27 | 28 | -------------------------------------------------------------------------------- /documentation/coffee/splices.coffee: -------------------------------------------------------------------------------- 1 | numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 2 | 3 | numbers[3..6] = [-3, -4, -5, -6] 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /documentation/coffee/strings.coffee: -------------------------------------------------------------------------------- 1 | mobyDick = "Call me Ishmael. Some years ago -- 2 | never mind how long precisely -- having little 3 | or no money in my purse, and nothing particular 4 | to interest me on shore, I thought I would sail 5 | about a little and see the watery part of the 6 | world..." 7 | -------------------------------------------------------------------------------- /documentation/coffee/switch.coffee: -------------------------------------------------------------------------------- 1 | switch day 2 | when "Mon" then go work 3 | when "Tue" then go relax 4 | when "Thu" then go iceFishing 5 | when "Fri", "Sat" 6 | if day is bingoDay 7 | go bingo 8 | go dancing 9 | when "Sun" then go church 10 | else go work -------------------------------------------------------------------------------- /documentation/coffee/switch_with_no_expression.coffee: -------------------------------------------------------------------------------- 1 | score = 76 2 | grade = switch 3 | when score < 60 then 'F' 4 | when score < 70 then 'D' 5 | when score < 80 then 'C' 6 | when score < 90 then 'B' 7 | else 'A' 8 | # grade == 'C' 9 | -------------------------------------------------------------------------------- /documentation/coffee/try.coffee: -------------------------------------------------------------------------------- 1 | try 2 | allHellBreaksLoose() 3 | catsAndDogsLivingTogether() 4 | catch error 5 | print error 6 | finally 7 | cleanUp() 8 | 9 | -------------------------------------------------------------------------------- /documentation/coffee/while.coffee: -------------------------------------------------------------------------------- 1 | # 经济 101 2 | if this.studyingEconomics 3 | buy() while supply > demand 4 | sell() until supply > demand 5 | 6 | # 摇篮曲 7 | num = 6 8 | lyrics = while num -= 1 9 | "#{num} little monkeys, jumping on the bed. 10 | One fell out and bumped his head." 11 | -------------------------------------------------------------------------------- /documentation/css/tomorrow.css: -------------------------------------------------------------------------------- 1 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 2 | /* Original code:; http://softwaremaniacs.org/media/soft/highlight/styles/tomorrow.css */ 3 | /* But forked for CoffeeScript */ 4 | .tomorrow-comment, pre .comment, pre .title { 5 | color: #8e908c; 6 | } 7 | 8 | .tomorrow-red, pre .variable, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo { 9 | color: #c82829; 10 | } 11 | 12 | .tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .params, pre .constant { 13 | color: #000000; 14 | } 15 | 16 | .tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute { 17 | color: #eab700; 18 | } 19 | 20 | .tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata { 21 | color: #718c00; 22 | } 23 | 24 | .tomorrow-aqua, pre .css .hexcolor { 25 | color: #3e999f; 26 | } 27 | 28 | .tomorrow-blue, pre .function, pre .function .title, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title { 29 | color: #21439C; 30 | } 31 | 32 | .tomorrow-purple, pre .keyword, pre .reserved, pre .javascript .function { 33 | color: #FF5600; 34 | } 35 | 36 | pre .subst { 37 | color: #A535AE; 38 | } 39 | 40 | pre .literal { 41 | color: #A535AE; 42 | } 43 | 44 | pre .property { 45 | color: #A535AE; 46 | } 47 | 48 | pre .class .title { 49 | color: #21439C; 50 | } 51 | 52 | pre code { 53 | display: block; 54 | background: white; 55 | color: #000000; 56 | } 57 | 58 | pre .coffeescript .javascript, 59 | pre .javascript .xml, 60 | pre .tex .formula, 61 | pre .xml .javascript, 62 | pre .xml .vbscript, 63 | pre .xml .css, 64 | pre .xml .cdata { 65 | opacity: 0.5; 66 | } 67 | -------------------------------------------------------------------------------- /documentation/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | index.coffee 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Jump To … 17 | + 18 | 19 | 20 | 21 | 22 | 23 | browser.coffee 24 | 25 | 26 | 27 | 28 | cake.coffee 29 | 30 | 31 | 32 | 33 | coffee-script.coffee 34 | 35 | 36 | 37 | 38 | command.coffee 39 | 40 | 41 | 42 | 43 | grammar.coffee 44 | 45 | 46 | 47 | 48 | helpers.coffee 49 | 50 | 51 | 52 | 53 | index.coffee 54 | 55 | 56 | 57 | 58 | lexer.coffee 59 | 60 | 61 | 62 | 63 | nodes.coffee 64 | 65 | 66 | 67 | 68 | optparse.coffee 69 | 70 | 71 | 72 | 73 | register.coffee 74 | 75 | 76 | 77 | 78 | repl.coffee 79 | 80 | 81 | 82 | 83 | rewriter.coffee 84 | 85 | 86 | 87 | 88 | scope.litcoffee 89 | 90 | 91 | 92 | 93 | sourcemap.litcoffee 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | index.coffee 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | ¶ 115 | 116 | Loader for CoffeeScript as a Node.js library. 117 | 118 | 119 | 120 | exports[key] = val for key, val of require './coffee-script' 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /documentation/docs/public/fonts/aller-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/docs/public/fonts/aller-bold.eot -------------------------------------------------------------------------------- /documentation/docs/public/fonts/aller-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/docs/public/fonts/aller-bold.ttf -------------------------------------------------------------------------------- /documentation/docs/public/fonts/aller-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/docs/public/fonts/aller-bold.woff -------------------------------------------------------------------------------- /documentation/docs/public/fonts/aller-light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/docs/public/fonts/aller-light.eot -------------------------------------------------------------------------------- /documentation/docs/public/fonts/aller-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/docs/public/fonts/aller-light.ttf -------------------------------------------------------------------------------- /documentation/docs/public/fonts/aller-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/docs/public/fonts/aller-light.woff -------------------------------------------------------------------------------- /documentation/docs/public/fonts/novecento-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/docs/public/fonts/novecento-bold.eot -------------------------------------------------------------------------------- /documentation/docs/public/fonts/novecento-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/docs/public/fonts/novecento-bold.ttf -------------------------------------------------------------------------------- /documentation/docs/public/fonts/novecento-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/docs/public/fonts/novecento-bold.woff -------------------------------------------------------------------------------- /documentation/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/images/background.png -------------------------------------------------------------------------------- /documentation/images/banding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/images/banding.png -------------------------------------------------------------------------------- /documentation/images/button_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/images/button_bg.png -------------------------------------------------------------------------------- /documentation/images/button_bg_dark.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/images/button_bg_dark.gif -------------------------------------------------------------------------------- /documentation/images/button_bg_green.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/images/button_bg_green.gif -------------------------------------------------------------------------------- /documentation/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/images/favicon.ico -------------------------------------------------------------------------------- /documentation/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/images/logo.png -------------------------------------------------------------------------------- /documentation/images/screenshadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coffee-js/coffee-script/45cbd69d6c9d5602df8d5f7d4ea047d505ce3ad4/documentation/images/screenshadow.png -------------------------------------------------------------------------------- /documentation/js/aliases.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var volume, winner; 3 | 4 | if (ignition === true) { 5 | launch(); 6 | } 7 | 8 | if (band !== SpinalTap) { 9 | volume = 10; 10 | } 11 | 12 | if (answer !== false) { 13 | letTheWildRumpusBegin(); 14 | } 15 | 16 | if (car.speed < limit) { 17 | accelerate(); 18 | } 19 | 20 | if (pick === 47 || pick === 92 || pick === 13) { 21 | winner = true; 22 | } 23 | 24 | print(inspect("My name is " + this.name)); 25 | -------------------------------------------------------------------------------- /documentation/js/array_comprehensions.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var courses, dish, food, foods, i, _i, _j, _k, _len, _len1, _len2, _ref; 3 | 4 | _ref = ['toast', 'cheese', 'wine']; 5 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 6 | food = _ref[_i]; 7 | eat(food); 8 | } 9 | 10 | courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']; 11 | 12 | for (i = _j = 0, _len1 = courses.length; _j < _len1; i = ++_j) { 13 | dish = courses[i]; 14 | menu(i + 1, dish); 15 | } 16 | 17 | foods = ['broccoli', 'spinach', 'chocolate']; 18 | 19 | for (_k = 0, _len2 = foods.length; _k < _len2; _k++) { 20 | food = foods[_k]; 21 | if (food !== 'chocolate') { 22 | eat(food); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /documentation/js/block_comment.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | 3 | /* 4 | SkinnyMochaHalfCaffScript Compiler v1.0 5 | Released under the MIT License 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /documentation/js/cake_tasks.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var fs; 3 | 4 | fs = require('fs'); 5 | 6 | option('-o', '--output [DIR]', 'directory for compiled code'); 7 | 8 | task('build:parser', 'rebuild the Jison parser', function(options) { 9 | var code, dir; 10 | require('jison'); 11 | code = require('./lib/grammar').parser.generate(); 12 | dir = options.output || 'lib'; 13 | return fs.writeFile("" + dir + "/parser.js", code); 14 | }); 15 | -------------------------------------------------------------------------------- /documentation/js/chaining.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | $('body').click(function(e) { 3 | return $('.box').fadeIn('fast').addClass('.active'); 4 | }).css('background', 'white'); 5 | -------------------------------------------------------------------------------- /documentation/js/classes.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var Animal, Horse, Snake, sam, tom, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | Animal = (function() { 7 | function Animal(name) { 8 | this.name = name; 9 | } 10 | 11 | Animal.prototype.move = function(meters) { 12 | return alert(this.name + (" moved " + meters + "m.")); 13 | }; 14 | 15 | return Animal; 16 | 17 | })(); 18 | 19 | Snake = (function(_super) { 20 | __extends(Snake, _super); 21 | 22 | function Snake() { 23 | return Snake.__super__.constructor.apply(this, arguments); 24 | } 25 | 26 | Snake.prototype.move = function() { 27 | alert("Slithering..."); 28 | return Snake.__super__.move.call(this, 5); 29 | }; 30 | 31 | return Snake; 32 | 33 | })(Animal); 34 | 35 | Horse = (function(_super) { 36 | __extends(Horse, _super); 37 | 38 | function Horse() { 39 | return Horse.__super__.constructor.apply(this, arguments); 40 | } 41 | 42 | Horse.prototype.move = function() { 43 | alert("Galloping..."); 44 | return Horse.__super__.move.call(this, 45); 45 | }; 46 | 47 | return Horse; 48 | 49 | })(Animal); 50 | 51 | sam = new Snake("Sammy the Python"); 52 | 53 | tom = new Horse("Tommy the Palomino"); 54 | 55 | sam.move(); 56 | 57 | tom.move(); 58 | -------------------------------------------------------------------------------- /documentation/js/comparisons.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var cholesterol, healthy; 3 | 4 | cholesterol = 127; 5 | 6 | healthy = (200 > cholesterol && cholesterol > 60); 7 | -------------------------------------------------------------------------------- /documentation/js/conditionals.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var date, mood; 3 | 4 | if (singing) { 5 | mood = greatlyImproved; 6 | } 7 | 8 | if (happy && knowsIt) { 9 | clapsHands(); 10 | chaChaCha(); 11 | } else { 12 | showIt(); 13 | } 14 | 15 | date = friday ? sue : jill; 16 | -------------------------------------------------------------------------------- /documentation/js/constructor_destructuring.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var Person, tim; 3 | 4 | Person = (function() { 5 | function Person(options) { 6 | this.name = options.name, this.age = options.age, this.height = options.height; 7 | } 8 | 9 | return Person; 10 | 11 | })(); 12 | 13 | tim = new Person({ 14 | age: 4 15 | }); 16 | -------------------------------------------------------------------------------- /documentation/js/default_args.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var fill; 3 | 4 | fill = function(container, liquid) { 5 | if (liquid == null) { 6 | liquid = "coffee"; 7 | } 8 | return "Filling the " + container + " with " + liquid + "..."; 9 | }; 10 | -------------------------------------------------------------------------------- /documentation/js/do.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var filename, _fn, _i, _len; 3 | 4 | _fn = function(filename) { 5 | return fs.readFile(filename, function(err, contents) { 6 | return compile(filename, contents.toString()); 7 | }); 8 | }; 9 | for (_i = 0, _len = list.length; _i < _len; _i++) { 10 | filename = list[_i]; 11 | _fn(filename); 12 | } 13 | -------------------------------------------------------------------------------- /documentation/js/embedded.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var hi; 3 | 4 | hi = function() { 5 | return [document.title, "Hello JavaScript"].join(": "); 6 | }; 7 | -------------------------------------------------------------------------------- /documentation/js/existence.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var footprints, solipsism, speed; 3 | 4 | if ((typeof mind !== "undefined" && mind !== null) && (typeof world === "undefined" || world === null)) { 5 | solipsism = true; 6 | } 7 | 8 | speed = 0; 9 | 10 | if (speed == null) { 11 | speed = 15; 12 | } 13 | 14 | footprints = typeof yeti !== "undefined" && yeti !== null ? yeti : "bear"; 15 | -------------------------------------------------------------------------------- /documentation/js/expansion.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var first, last, text, _ref; 3 | 4 | text = "Every literary critic believes he will outwit history and have the last word"; 5 | 6 | _ref = text.split(" "), first = _ref[0], last = _ref[_ref.length - 1]; 7 | -------------------------------------------------------------------------------- /documentation/js/expressions.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var eldest, grade; 3 | 4 | grade = function(student) { 5 | if (student.excellentWork) { 6 | return "A+"; 7 | } else if (student.okayStuff) { 8 | if (student.triedHard) { 9 | return "B"; 10 | } else { 11 | return "B-"; 12 | } 13 | } else { 14 | return "C"; 15 | } 16 | }; 17 | 18 | eldest = 24 > 21 ? "Liz" : "Ike"; 19 | -------------------------------------------------------------------------------- /documentation/js/expressions_assignment.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var one, six, three, two; 3 | 4 | six = (one = 1) + (two = 2) + (three = 3); 5 | -------------------------------------------------------------------------------- /documentation/js/expressions_comprehension.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var globals, name; 3 | 4 | globals = ((function() { 5 | var _results; 6 | _results = []; 7 | for (name in window) { 8 | _results.push(name); 9 | } 10 | return _results; 11 | })()).slice(0, 10); 12 | -------------------------------------------------------------------------------- /documentation/js/expressions_try.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var error; 3 | 4 | alert((function() { 5 | try { 6 | return nonexistent / void 0; 7 | } catch (_error) { 8 | error = _error; 9 | return "And the error is ... " + error; 10 | } 11 | })()); 12 | -------------------------------------------------------------------------------- /documentation/js/fat_arrow.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var Account; 3 | 4 | Account = function(customer, cart) { 5 | this.customer = customer; 6 | this.cart = cart; 7 | return $('.shopping_cart').bind('click', (function(_this) { 8 | return function(event) { 9 | return _this.customer.purchase(_this.cart); 10 | }; 11 | })(this)); 12 | }; 13 | -------------------------------------------------------------------------------- /documentation/js/functions.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var cube, square; 3 | 4 | square = function(x) { 5 | return x * x; 6 | }; 7 | 8 | cube = function(x) { 9 | return square(x) * x; 10 | }; 11 | -------------------------------------------------------------------------------- /documentation/js/heredocs.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var html; 3 | 4 | html = "\n cup of coffeescript\n"; 5 | -------------------------------------------------------------------------------- /documentation/js/heregexes.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var OPERATOR; 3 | 4 | OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/; 5 | -------------------------------------------------------------------------------- /documentation/js/interpolation.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var author, quote, sentence; 3 | 4 | author = "Wittgenstein"; 5 | 6 | quote = "A picture is a fact. -- " + author; 7 | 8 | sentence = "" + (22 / 7) + " is a decent approximation of π"; 9 | -------------------------------------------------------------------------------- /documentation/js/multiple_return_values.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var city, forecast, temp, weatherReport, _ref; 3 | 4 | weatherReport = function(location) { 5 | return [location, 72, "Mostly Sunny"]; 6 | }; 7 | 8 | _ref = weatherReport("Berkeley, CA"), city = _ref[0], temp = _ref[1], forecast = _ref[2]; 9 | -------------------------------------------------------------------------------- /documentation/js/object_comprehensions.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var age, ages, child, yearsOld; 3 | 4 | yearsOld = { 5 | max: 10, 6 | ida: 9, 7 | tim: 11 8 | }; 9 | 10 | ages = (function() { 11 | var _results; 12 | _results = []; 13 | for (child in yearsOld) { 14 | age = yearsOld[child]; 15 | _results.push("" + child + " is " + age); 16 | } 17 | return _results; 18 | })(); 19 | -------------------------------------------------------------------------------- /documentation/js/object_extraction.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var city, futurists, name, street, _ref, _ref1; 3 | 4 | futurists = { 5 | sculptor: "Umberto Boccioni", 6 | painter: "Vladimir Burliuk", 7 | poet: { 8 | name: "F.T. Marinetti", 9 | address: ["Via Roma 42R", "Bellagio, Italy 22021"] 10 | } 11 | }; 12 | 13 | _ref = futurists.poet, name = _ref.name, (_ref1 = _ref.address, street = _ref1[0], city = _ref1[1]); 14 | -------------------------------------------------------------------------------- /documentation/js/objects_and_arrays.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var bitlist, kids, singers, song; 3 | 4 | song = ["do", "re", "mi", "fa", "so"]; 5 | 6 | singers = { 7 | Jagger: "Rock", 8 | Elvis: "Roll" 9 | }; 10 | 11 | bitlist = [1, 0, 1, 0, 0, 1, 1, 1, 0]; 12 | 13 | kids = { 14 | brother: { 15 | name: "Max", 16 | age: 11 17 | }, 18 | sister: { 19 | name: "Ida", 20 | age: 9 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /documentation/js/objects_reserved.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | $('.account').attr({ 3 | "class": 'active' 4 | }); 5 | 6 | log(object["class"]); 7 | -------------------------------------------------------------------------------- /documentation/js/overview.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var cubes, list, math, num, number, opposite, race, square, 3 | __slice = [].slice; 4 | 5 | number = 42; 6 | 7 | opposite = true; 8 | 9 | if (opposite) { 10 | number = -42; 11 | } 12 | 13 | square = function(x) { 14 | return x * x; 15 | }; 16 | 17 | list = [1, 2, 3, 4, 5]; 18 | 19 | math = { 20 | root: Math.sqrt, 21 | square: square, 22 | cube: function(x) { 23 | return x * square(x); 24 | } 25 | }; 26 | 27 | race = function() { 28 | var runners, winner; 29 | winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 30 | return print(winner, runners); 31 | }; 32 | 33 | if (typeof elvis !== "undefined" && elvis !== null) { 34 | alert("I knew it!"); 35 | } 36 | 37 | cubes = (function() { 38 | var _i, _len, _results; 39 | _results = []; 40 | for (_i = 0, _len = list.length; _i < _len; _i++) { 41 | num = list[_i]; 42 | _results.push(math.cube(num)); 43 | } 44 | return _results; 45 | })(); 46 | -------------------------------------------------------------------------------- /documentation/js/parallel_assignment.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var theBait, theSwitch, _ref; 3 | 4 | theBait = 1000; 5 | 6 | theSwitch = 0; 7 | 8 | _ref = [theSwitch, theBait], theBait = _ref[0], theSwitch = _ref[1]; 9 | -------------------------------------------------------------------------------- /documentation/js/patterns_and_splats.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var close, contents, open, tag, _i, _ref, 3 | __slice = [].slice; 4 | 5 | tag = ""; 6 | 7 | _ref = tag.split(""), open = _ref[0], contents = 3 <= _ref.length ? __slice.call(_ref, 1, _i = _ref.length - 1) : (_i = 1, []), close = _ref[_i++]; 8 | -------------------------------------------------------------------------------- /documentation/js/prototypes.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | String.prototype.dasherize = function() { 3 | return this.replace(/_/g, "-"); 4 | }; 5 | -------------------------------------------------------------------------------- /documentation/js/range_comprehensions.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var countdown, num; 3 | 4 | countdown = (function() { 5 | var _i, _results; 6 | _results = []; 7 | for (num = _i = 10; _i >= 1; num = --_i) { 8 | _results.push(num); 9 | } 10 | return _results; 11 | })(); 12 | -------------------------------------------------------------------------------- /documentation/js/scope.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var changeNumbers, inner, outer; 3 | 4 | outer = 1; 5 | 6 | changeNumbers = function() { 7 | var inner; 8 | inner = -1; 9 | return outer = 10; 10 | }; 11 | 12 | inner = changeNumbers(); 13 | -------------------------------------------------------------------------------- /documentation/js/slices.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var copy, end, middle, numbers, start; 3 | 4 | numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; 5 | 6 | start = numbers.slice(0, 3); 7 | 8 | middle = numbers.slice(3, -2); 9 | 10 | end = numbers.slice(-2); 11 | 12 | copy = numbers.slice(0); 13 | -------------------------------------------------------------------------------- /documentation/js/soaks.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var zip, _ref; 3 | 4 | zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0; 5 | -------------------------------------------------------------------------------- /documentation/js/splats.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var awardMedals, contenders, gold, rest, silver, 3 | __slice = [].slice; 4 | 5 | gold = silver = rest = "unknown"; 6 | 7 | awardMedals = function() { 8 | var first, others, second; 9 | first = arguments[0], second = arguments[1], others = 3 <= arguments.length ? __slice.call(arguments, 2) : []; 10 | gold = first; 11 | silver = second; 12 | return rest = others; 13 | }; 14 | 15 | contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"]; 16 | 17 | awardMedals.apply(null, contenders); 18 | 19 | alert("Gold: " + gold); 20 | 21 | alert("Silver: " + silver); 22 | 23 | alert("The Field: " + rest); 24 | -------------------------------------------------------------------------------- /documentation/js/splices.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var numbers, _ref; 3 | 4 | numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 5 | 6 | [].splice.apply(numbers, [3, 4].concat(_ref = [-3, -4, -5, -6])), _ref; 7 | -------------------------------------------------------------------------------- /documentation/js/strings.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var mobyDick; 3 | 4 | mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world..."; 5 | -------------------------------------------------------------------------------- /documentation/js/switch.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | switch (day) { 3 | case "Mon": 4 | go(work); 5 | break; 6 | case "Tue": 7 | go(relax); 8 | break; 9 | case "Thu": 10 | go(iceFishing); 11 | break; 12 | case "Fri": 13 | case "Sat": 14 | if (day === bingoDay) { 15 | go(bingo); 16 | go(dancing); 17 | } 18 | break; 19 | case "Sun": 20 | go(church); 21 | break; 22 | default: 23 | go(work); 24 | } 25 | -------------------------------------------------------------------------------- /documentation/js/switch_with_no_expression.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var grade, score; 3 | 4 | score = 76; 5 | 6 | grade = (function() { 7 | switch (false) { 8 | case !(score < 60): 9 | return 'F'; 10 | case !(score < 70): 11 | return 'D'; 12 | case !(score < 80): 13 | return 'C'; 14 | case !(score < 90): 15 | return 'B'; 16 | default: 17 | return 'A'; 18 | } 19 | })(); 20 | -------------------------------------------------------------------------------- /documentation/js/try.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var error; 3 | 4 | try { 5 | allHellBreaksLoose(); 6 | catsAndDogsLivingTogether(); 7 | } catch (_error) { 8 | error = _error; 9 | print(error); 10 | } finally { 11 | cleanUp(); 12 | } 13 | -------------------------------------------------------------------------------- /documentation/js/while.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | var lyrics, num; 3 | 4 | if (this.studyingEconomics) { 5 | while (supply > demand) { 6 | buy(); 7 | } 8 | while (!(supply > demand)) { 9 | sell(); 10 | } 11 | } 12 | 13 | num = 6; 14 | 15 | lyrics = (function() { 16 | var _results; 17 | _results = []; 18 | while (num -= 1) { 19 | _results.push("" + num + " little monkeys, jumping on the bed. One fell out and bumped his head."); 20 | } 21 | return _results; 22 | })(); 23 | -------------------------------------------------------------------------------- /examples/beautiful_code/binary_search.coffee: -------------------------------------------------------------------------------- 1 | # Beautiful Code, Chapter 6. 2 | # The implementation of binary search that is tested. 3 | 4 | # Return the index of an element in a sorted list. (or -1, if not present) 5 | index = (list, target) -> 6 | [low, high] = [0, list.length] 7 | while low < high 8 | mid = (low + high) >> 1 9 | val = list[mid] 10 | return mid if val is target 11 | if val < target then low = mid + 1 else high = mid 12 | return -1 13 | 14 | console.log 2 is index [10, 20, 30, 40, 50], 30 15 | console.log 4 is index [-97, 35, 67, 88, 1200], 1200 16 | console.log 0 is index [0, 45, 70], 0 -------------------------------------------------------------------------------- /examples/beautiful_code/quicksort_runtime.coffee: -------------------------------------------------------------------------------- 1 | # Beautiful Code, Chapter 3. 2 | # Produces the expected runtime of Quicksort, for every integer from 1 to N. 3 | 4 | runtime = (N) -> 5 | [sum, t] = [0, 0] 6 | for n in [1..N] 7 | sum += 2 * t 8 | t = n - 1 + sum / n 9 | t 10 | 11 | console.log runtime(3) is 2.6666666666666665 12 | console.log runtime(5) is 7.4 13 | console.log runtime(8) is 16.92142857142857 14 | -------------------------------------------------------------------------------- /examples/beautiful_code/regular_expression_matcher.coffee: -------------------------------------------------------------------------------- 1 | # Beautiful Code, Chapter 1. 2 | # Implements a regular expression matcher that supports character matches, 3 | # '.', '^', '$', and '*'. 4 | 5 | # Search for the regexp anywhere in the text. 6 | match = (regexp, text) -> 7 | return match_here(regexp.slice(1), text) if regexp[0] is '^' 8 | while text 9 | return true if match_here(regexp, text) 10 | text = text.slice(1) 11 | false 12 | 13 | # Search for the regexp at the beginning of the text. 14 | match_here = (regexp, text) -> 15 | [cur, next] = [regexp[0], regexp[1]] 16 | if regexp.length is 0 then return true 17 | if next is '*' then return match_star(cur, regexp.slice(2), text) 18 | if cur is '$' and not next then return text.length is 0 19 | if text and (cur is '.' or cur is text[0]) then return match_here(regexp.slice(1), text.slice(1)) 20 | false 21 | 22 | # Search for a kleene star match at the beginning of the text. 23 | match_star = (c, regexp, text) -> 24 | loop 25 | return true if match_here(regexp, text) 26 | return false unless text and (text[0] is c or c is '.') 27 | text = text.slice(1) 28 | 29 | console.log match("ex", "some text") 30 | console.log match("s..t", "spit") 31 | console.log match("^..t", "buttercup") 32 | console.log match("i..$", "cherries") 33 | console.log match("o*m", "vrooooommm!") 34 | console.log match("^hel*o$", "hellllllo") -------------------------------------------------------------------------------- /examples/blocks.coffee: -------------------------------------------------------------------------------- 1 | # After wycats' http://yehudakatz.com/2010/02/07/the-building-blocks-of-ruby/ 2 | 3 | # Sinatra. 4 | get '/hello', -> 5 | 'Hello World' 6 | 7 | 8 | # Append. 9 | append = (location, data) -> 10 | path = new Pathname location 11 | throw new Error "Location does not exist" unless fs.existsSync(location) 12 | 13 | File.open path, 'a', (file) -> 14 | file.console.log YAML.dump data 15 | 16 | data 17 | 18 | 19 | # Rubinius' File.open implementation. 20 | File.open = (path, mode, block) -> 21 | io = new File path, mode 22 | 23 | return io unless block 24 | 25 | try 26 | block io 27 | finally 28 | io.close() unless io.closed() 29 | 30 | 31 | # Write. 32 | write = (location, data) -> 33 | path = new Pathname location 34 | throw new Error "Location does not exist" unless fs.existsSync location 35 | 36 | File.open path, 'w', (file) -> 37 | return false if Digest.MD5.hexdigest(file.read()) is data.hash() 38 | file.console.log YAML.dump data 39 | true 40 | 41 | 42 | # Rails' respond_to. 43 | index = -> 44 | people = Person.find 'all' 45 | 46 | respond_to (format) -> 47 | format.html() 48 | format.xml -> render xml: people.xml() 49 | 50 | 51 | # Synchronization. 52 | synchronize = (block) -> 53 | lock() 54 | try block() finally unlock() 55 | -------------------------------------------------------------------------------- /examples/code.coffee: -------------------------------------------------------------------------------- 1 | # Functions: 2 | square = (x) -> x * x 3 | 4 | sum = (x, y) -> x + y 5 | 6 | odd = (x) -> x % 2 isnt 0 7 | 8 | even = (x) -> x % 2 is 0 9 | 10 | run_loop = -> 11 | fire_events((e) -> e.stopPropagation()) 12 | listen() 13 | wait() 14 | 15 | # Objects: 16 | dense_object_literal = one: 1, two: 2, three: 3 17 | 18 | spaced_out_multiline_object = 19 | pi: 3.14159 20 | list: [1, 2, 3, 4] 21 | regex: /match[ing](every|thing|\/)/gi 22 | three: new Idea 23 | 24 | inner_obj: 25 | freedom: -> _.freedom() 26 | 27 | # Arrays: 28 | stooges = [{moe: 45}, {curly: 43}, {larry: 46}] 29 | 30 | exponents = [((x) -> x), ((x) -> x * x), ((x) -> x * x * x)] 31 | 32 | empty = [] 33 | 34 | multiline = [ 35 | 'line one' 36 | 'line two' 37 | ] 38 | 39 | # Conditionals and ternaries. 40 | if submarine.shields_up 41 | full_speed_ahead() 42 | fire_torpedos() 43 | else if submarine.sinking 44 | abandon_ship() 45 | else 46 | run_away() 47 | 48 | eldest = if 25 > 21 then liz else marge 49 | 50 | decoration = medal_of_honor if war_hero 51 | 52 | go_to_sleep() unless coffee 53 | 54 | # Returning early: 55 | race = -> 56 | run() 57 | walk() 58 | crawl() 59 | return sleep() if tired 60 | race() 61 | 62 | # Conditional assignment: 63 | good or= evil 64 | wine and= cheese 65 | 66 | # Nested property access and calls. 67 | (moon.turn 360).shapes[3].move(x: 45, y: 30).position['top'].offset('x') 68 | 69 | a = b = c = 5 70 | 71 | # Embedded JavaScript. 72 | callback( 73 | `function(e) { e.stop(); }` 74 | ) 75 | 76 | # Try/Catch/Finally/Throw. 77 | try 78 | all_hell_breaks_loose() 79 | dogs_and_cats_living_together() 80 | throw "up" 81 | catch error 82 | print error 83 | finally 84 | clean_up() 85 | 86 | try all_hell_breaks_loose() catch error then print(error) finally clean_up() 87 | 88 | # While loops, break and continue. 89 | while demand > supply 90 | sell() 91 | restock() 92 | 93 | while supply > demand then buy() 94 | 95 | loop 96 | break if broken 97 | continue if continuing 98 | 99 | # Unary operators. 100 | !!true 101 | 102 | # Lexical scoping. 103 | v_1 = 5 104 | change_a_and_set_b = -> 105 | v_1 = 10 106 | v_2 = 15 107 | v_2 = 20 108 | 109 | # Array comprehensions. 110 | supper = food.capitalize() for food in ['toast', 'cheese', 'wine'] 111 | 112 | drink bottle for bottle, i in ['soda', 'wine', 'lemonade'] when even i 113 | 114 | # Switch statements ("else" serves as a default). 115 | activity = switch day 116 | when "Tuesday" then eat_breakfast() 117 | when "Sunday" then go_to_church() 118 | when "Saturday" then go_to_the_park() 119 | when "Wednesday" 120 | if day is bingo_day 121 | go_to_bingo() 122 | else 123 | eat_breakfast() 124 | go_to_work() 125 | eat_dinner() 126 | else go_to_work() 127 | 128 | # Semicolons can optionally be used instead of newlines. 129 | wednesday = -> eat_breakfast(); go_to_work(); eat_dinner() 130 | 131 | # Multiline strings with inner quotes. 132 | story = "Lorem ipsum dolor \"sit\" amet, consectetuer adipiscing elit, 133 | sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna 134 | aliquam erat volutpat. Ut wisi enim ad." 135 | 136 | # Inheritance and calling super. 137 | class Animal 138 | (@name) -> 139 | 140 | move: (meters) -> 141 | alert this.name + " moved " + meters + "m." 142 | 143 | class Snake extends Animal 144 | move: -> 145 | alert 'Slithering...' 146 | super 5 147 | 148 | class Horse extends Animal 149 | move: -> 150 | alert 'Galloping...' 151 | super 45 152 | 153 | sam = new Snake "Sammy the Snake" 154 | tom = new Horse "Tommy the Horse" 155 | 156 | sam.move() 157 | tom.move() 158 | 159 | # Numbers. 160 | a_googol = 1e100 161 | hex = 0xff0000 162 | negative = -1.0 163 | infinity = Infinity 164 | nan = NaN 165 | 166 | # Deleting. 167 | delete secret.identity -------------------------------------------------------------------------------- /examples/computer_science/README: -------------------------------------------------------------------------------- 1 | Ported from Nicholas Zakas' collection of computer science fundamentals, written 2 | in JavaScript. Originals available here: 3 | 4 | http://github.com/nzakas/computer-science-in-javascript 5 | -------------------------------------------------------------------------------- /examples/computer_science/binary_search.coffee: -------------------------------------------------------------------------------- 1 | # Uses a binary search algorithm to locate a value in the specified array. 2 | binary_search = (items, value) -> 3 | 4 | start = 0 5 | stop = items.length - 1 6 | pivot = Math.floor (start + stop) / 2 7 | 8 | while items[pivot] isnt value and start < stop 9 | 10 | # Adjust the search area. 11 | stop = pivot - 1 if value < items[pivot] 12 | start = pivot + 1 if value > items[pivot] 13 | 14 | # Recalculate the pivot. 15 | pivot = Math.floor (stop + start) / 2 16 | 17 | # Make sure we've found the correct value. 18 | if items[pivot] is value then pivot else -1 19 | 20 | 21 | # Test the function. 22 | console.log 2 is binary_search [10, 20, 30, 40, 50], 30 23 | console.log 4 is binary_search [-97, 35, 67, 88, 1200], 1200 24 | console.log 0 is binary_search [0, 45, 70], 0 25 | console.log -1 is binary_search [0, 45, 70], 10 -------------------------------------------------------------------------------- /examples/computer_science/bubble_sort.coffee: -------------------------------------------------------------------------------- 1 | # A bubble sort implementation, sorting the given array in-place. 2 | bubble_sort = (list) -> 3 | for i in [0...list.length] 4 | for j in [0...list.length - i] when list[j] > list[j + 1] 5 | [list[j], list[j+1]] = [list[j + 1], list[j]] 6 | list 7 | 8 | 9 | # Test the function. 10 | console.log bubble_sort([3, 2, 1]).join(' ') is '1 2 3' 11 | console.log bubble_sort([9, 2, 7, 0, 1]).join(' ') is '0 1 2 7 9' -------------------------------------------------------------------------------- /examples/computer_science/linked_list.coffee: -------------------------------------------------------------------------------- 1 | # "Classic" linked list implementation that doesn't keep track of its size. 2 | class LinkedList 3 | 4 | constructor: -> 5 | @_head = null # Pointer to the first item in the list. 6 | 7 | 8 | # Appends some data to the end of the list. This method traverses the existing 9 | # list and places the value at the end in a new node. 10 | add: (data) -> 11 | 12 | # Create a new node object to wrap the data. 13 | node = data: data, next: null 14 | 15 | current = @_head or= node 16 | 17 | if @_head isnt node 18 | current = current.next while current.next 19 | current.next = node 20 | 21 | this 22 | 23 | 24 | # Retrieves the data at the given position in the list. 25 | item: (index) -> 26 | 27 | # Check for out-of-bounds values. 28 | return null if index < 0 29 | 30 | current = @_head or null 31 | i = -1 32 | 33 | # Advance through the list. 34 | current = current.next while current and index > ++i 35 | 36 | # Return null if we've reached the end. 37 | current and current.data 38 | 39 | 40 | # Remove the item from the given location in the list. 41 | remove: (index) -> 42 | 43 | # Check for out-of-bounds values. 44 | return null if index < 0 45 | 46 | current = @_head or null 47 | i = -1 48 | 49 | # Special case: removing the first item. 50 | if index is 0 51 | @_head = current.next 52 | else 53 | 54 | # Find the right location. 55 | [previous, current] = [current, current.next] while index > ++i 56 | 57 | # Skip over the item to remove. 58 | previous.next = current.next 59 | 60 | # Return the value. 61 | current and current.data 62 | 63 | 64 | # Calculate the number of items in the list. 65 | size: -> 66 | current = @_head 67 | count = 0 68 | 69 | while current 70 | count += 1 71 | current = current.next 72 | 73 | count 74 | 75 | 76 | # Convert the list into an array. 77 | toArray: -> 78 | result = [] 79 | current = @_head 80 | 81 | while current 82 | result.push current.data 83 | current = current.next 84 | 85 | result 86 | 87 | 88 | # The string representation of the linked list. 89 | toString: -> @toArray().toString() 90 | 91 | 92 | # Tests. 93 | list = new LinkedList 94 | 95 | list.add("Hi") 96 | console.log list.size() is 1 97 | console.log list.item(0) is "Hi" 98 | console.log list.item(1) is null 99 | 100 | list = new LinkedList 101 | list.add("zero").add("one").add("two") 102 | console.log list.size() is 3 103 | console.log list.item(2) is "two" 104 | console.log list.remove(1) is "one" 105 | console.log list.item(0) is "zero" 106 | console.log list.item(1) is "two" 107 | console.log list.size() is 2 108 | console.log list.item(-10) is null 109 | -------------------------------------------------------------------------------- /examples/computer_science/luhn_algorithm.coffee: -------------------------------------------------------------------------------- 1 | # Use the Luhn algorithm to validate a numeric identifier, such as credit card 2 | # numbers, national insurance numbers, etc. 3 | # See: http://en.wikipedia.org/wiki/Luhn_algorithm 4 | 5 | is_valid_identifier = (identifier) -> 6 | 7 | sum = 0 8 | alt = false 9 | 10 | for c in identifier by -1 11 | 12 | # Get the next digit. 13 | num = parseInt c, 10 14 | 15 | # If it's not a valid number, abort. 16 | return false if isNaN num 17 | 18 | # If it's an alternate number... 19 | if alt 20 | num *= 2 21 | num = (num % 10) + 1 if num > 9 22 | 23 | # Flip the alternate bit. 24 | alt = !alt 25 | 26 | # Add to the rest of the sum. 27 | sum += num 28 | 29 | # Determine if it's valid. 30 | sum % 10 is 0 31 | 32 | 33 | # Tests. 34 | console.log is_valid_identifier("49927398716") is true 35 | console.log is_valid_identifier("4408041234567893") is true 36 | console.log is_valid_identifier("4408041234567890") is false 37 | -------------------------------------------------------------------------------- /examples/computer_science/merge_sort.coffee: -------------------------------------------------------------------------------- 1 | # Sorts an array in ascending natural order using merge sort. 2 | merge_sort = (list) -> 3 | 4 | return list if list.length is 1 5 | 6 | pivot = Math.floor list.length / 2 7 | left = merge_sort list.slice 0, pivot 8 | right = merge_sort list.slice pivot 9 | 10 | result = while left.length and right.length 11 | if left[0] < right[0] then left.shift() else right.shift() 12 | 13 | result.concat(left).concat(right) 14 | 15 | 16 | # Test the function. 17 | console.log merge_sort([3, 2, 1]).join(' ') is '1 2 3' 18 | console.log merge_sort([9, 2, 7, 0, 1]).join(' ') is '0 1 2 7 9' -------------------------------------------------------------------------------- /examples/computer_science/selection_sort.coffee: -------------------------------------------------------------------------------- 1 | # An in-place selection sort. 2 | selection_sort = (list) -> 3 | len = list.length 4 | 5 | # For each item in the list. 6 | for i in [0...len] 7 | 8 | # Set the minimum to this position. 9 | min = i 10 | 11 | # Check the rest of the array to see if anything is smaller. 12 | min = k for v, k in list[i+1...] when v < list[min] 13 | 14 | # Swap if a smaller item has been found. 15 | [list[i], list[min]] = [list[min], list[i]] if i isnt min 16 | 17 | # The list is now sorted. 18 | list 19 | 20 | 21 | # Test the function. 22 | console.log selection_sort([3, 2, 1]).join(' ') is '1 2 3' 23 | console.log selection_sort([9, 2, 7, 0, 1]).join(' ') is '0 1 2 7 9' -------------------------------------------------------------------------------- /examples/poignant.coffee: -------------------------------------------------------------------------------- 1 | # Examples from the Poignant Guide. 2 | # These are examples of syntax differences between CoffeeScript and Ruby, 3 | # they won't run. 4 | 5 | # ['toast', 'cheese', 'wine'].each { |food| print food.capitalize } 6 | 7 | print food.capitalize() for food in ['toast', 'wine', 'cheese'] 8 | 9 | 10 | 11 | 12 | # class LotteryTicket 13 | # def picks; @picks; end 14 | # def picks=(var); @picks = var; end 15 | # def purchased; @purchased; end 16 | # def purchased=(var); @purchased = var; end 17 | # end 18 | 19 | LotteryTicket = 20 | get_picks: -> @picks 21 | set_picks: (@picks) -> 22 | get_purchased: -> @purchase 23 | set_purchased: (@purchased) -> 24 | 25 | 26 | 27 | # class << LotteryDraw 28 | # def play 29 | # result = LotteryTicket.new_random 30 | # winners = {} 31 | # @@tickets.each do |buyer, ticket_list| 32 | # ticket_list.each do |ticket| 33 | # score = ticket.score( result ) 34 | # next if score.zero? 35 | # winners[buyer] ||= [] 36 | # winners[buyer] << [ ticket, score ] 37 | # end 38 | # end 39 | # @@tickets.clear 40 | # winners 41 | # end 42 | # end 43 | 44 | LotteryDraw = 45 | play: -> 46 | result = LotteryTicket.new_random() 47 | winners = {} 48 | for buyer, ticketList of @tickets 49 | for ticket in ticketList when (score = ticket.score result) isnt 0 50 | (winners[buyer] or= []).push [ticket, score] 51 | @tickets = {} 52 | winners 53 | 54 | 55 | 56 | # module WishScanner 57 | # def scan_for_a_wish 58 | # wish = self.read.detect do |thought| 59 | # thought.index( 'wish: ' ) == 0 60 | # end 61 | # wish.gsub( 'wish: ', '' ) 62 | # end 63 | # end 64 | 65 | WishScanner = 66 | scan_for_a_wish: -> 67 | wish = @read().detect (thought) -> thought.indexOf('wish: ') is 0 68 | wish.replace 'wish: ', '' 69 | 70 | 71 | 72 | # class Creature 73 | # 74 | # # This method applies a hit taken during a fight. 75 | # def hit( damage ) 76 | # p_up = rand( charisma ) 77 | # if p_up % 9 == 7 78 | # @life += p_up / 4 79 | # console.log "[#{ self.class } magick powers up #{ p_up }!]" 80 | # end 81 | # @life -= damage 82 | # console.log "[#{ self.class } has died.]" if @life <= 0 83 | # end 84 | # 85 | # # This method takes one turn in a fight. 86 | # def fight( enemy, weapon ) 87 | # if life <= 0 88 | # console.log "[#{ self.class } is too dead to fight!]" 89 | # return 90 | # end 91 | # 92 | # # Attack the opponent 93 | # your_hit = rand( strength + weapon ) 94 | # console.log "[You hit with #{ your_hit } points of damage!]" 95 | # enemy.hit( your_hit ) 96 | # 97 | # # Retaliation 98 | # p enemy 99 | # if enemy.life > 0 100 | # enemy_hit = rand( enemy.strength + enemy.weapon ) 101 | # console.log "[Your enemy hit with #{ enemy_hit } points of damage!]" 102 | # self.hit( enemy_hit ) 103 | # end 104 | # end 105 | # 106 | # end 107 | 108 | Creature = 109 | 110 | # This method applies a hit taken during a fight. 111 | hit: (damage) -> 112 | p_up = Math.rand @charisma 113 | if p_up % 9 is 7 114 | @life += p_up / 4 115 | console.log "[#{@name} magick powers up #{p_up}!]" 116 | @life -= damage 117 | if @life <= 0 then console.log "[#{@name} has died.]" 118 | 119 | # This method takes one turn in a fight. 120 | fight: (enemy, weapon) -> 121 | return console.log "[#{@name} is too dead to fight!]" if @life <= 0 122 | 123 | # Attack the opponent. 124 | your_hit = Math.rand @strength + weapon 125 | console.log "[You hit with #{your_hit}points of damage!]" 126 | enemy.hit your_hit 127 | 128 | # Retaliation. 129 | console.log enemy 130 | if enemy.life > 0 131 | enemy_hit = Math.rand enemy.strength + enemy.weapon 132 | console.log "[Your enemy hit with #{enemy_hit}points of damage!]" 133 | @hit enemy_hit 134 | 135 | 136 | 137 | # # Get evil idea and swap in code words 138 | # print "Enter your new idea: " 139 | # idea = gets 140 | # code_words.each do |real, code| 141 | # idea.gsub!( real, code ) 142 | # end 143 | # 144 | # # Save the jibberish to a new file 145 | # print "File encoded. Please enter a name for this idea: " 146 | # idea_name = gets.strip 147 | # File::open( "idea-" + idea_name + ".txt", "w" ) do |f| 148 | # f << idea 149 | # end 150 | 151 | # Get evil idea and swap in code words 152 | print "Enter your new idea: " 153 | idea = gets() 154 | code_words.each (real, code) -> idea.replace(real, code) 155 | 156 | # Save the jibberish to a new file 157 | print "File encoded. Please enter a name for this idea: " 158 | idea_name = gets().strip() 159 | File.open "idea-#{idea_name}.txt", 'w', (file) -> file.write idea 160 | 161 | 162 | 163 | # def wipe_mutterings_from( sentence ) 164 | # unless sentence.respond_to? :include? 165 | # raise ArgumentError, 166 | # "cannot wipe mutterings from a #{ sentence.class }" 167 | # end 168 | # while sentence.include? '(' 169 | # open = sentence.index( '(' ) 170 | # close = sentence.index( ')', open ) 171 | # sentence[open..close] = '' if close 172 | # end 173 | # end 174 | 175 | wipe_mutterings_from = (sentence) -> 176 | throw new Error "cannot wipe mutterings" unless sentence.indexOf 177 | while '(' in sentence 178 | open = sentence.indexOf('(') 179 | close = sentence.indexOf(')') 180 | sentence = "#{sentence[0...open]}#{sentence[close + 1..]}" 181 | sentence 182 | -------------------------------------------------------------------------------- /examples/potion.coffee: -------------------------------------------------------------------------------- 1 | # Examples from _why's Potion, the Readme and "Potion: A Short Pamphlet". 2 | 3 | # 5 times: "Odelay!" print. 4 | 5 | print "Odelay!" for i in [1..5] 6 | 7 | 8 | # add = (x, y): x + y. 9 | # add(2, 4) string print 10 | 11 | add = (x, y) -> x + y 12 | print add 2, 4 13 | 14 | 15 | # loop: 'quaff' print. 16 | 17 | loop print 'quaff' 18 | 19 | 20 | # ('cheese', 'bread', 'mayo') at (1) print 21 | 22 | print ['cheese', 'bread', 'mayo'][1] 23 | 24 | 25 | # (language='Potion', pointless=true) at (key='language') print 26 | 27 | print {language: 'Potion', pointless: true}['language'] 28 | 29 | 30 | # minus = (x, y): x - y. 31 | # minus (y=10, x=6) 32 | 33 | minus = (x, y) -> x - y 34 | minus 6, 10 35 | 36 | 37 | # foods = ('cheese', 'bread', 'mayo') 38 | # foods (2) 39 | 40 | foods = ['cheese', 'bread', 'mayo'] 41 | foods[2] 42 | 43 | 44 | # (dog='canine', cat='feline', fox='vulpine') each (key, val): 45 | # (key, ' is a ', val) join print. 46 | 47 | for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'} 48 | print "#{key} is a #{val}" 49 | 50 | 51 | # Person = class: /name, /age, /sex. 52 | # Person print = (): 53 | # ('My name is ', /name, '.') join print. 54 | 55 | class Person 56 | print: -> 57 | print "My name is #{@name}." 58 | 59 | 60 | # p = Person () 61 | # p /name string print 62 | 63 | p = new Person 64 | print p.name 65 | 66 | 67 | # Policeman = Person class (rank): /rank = rank. 68 | # Policeman print = (): 69 | # ('My name is ', /name, ' and I'm a ', /rank, '.') join print. 70 | # 71 | # Policeman ('Constable') print 72 | 73 | class Policeman extends Person 74 | (@rank) -> 75 | 76 | print: -> 77 | print "My name is #{@name} and I'm a #{@rank}." 78 | 79 | print new Policeman 'Constable' 80 | 81 | 82 | # app = [window (width=200, height=400) 83 | # [para 'Welcome.', button 'OK']] 84 | # app first name 85 | 86 | app = 87 | window: 88 | width: 200 89 | height: 200 90 | para: 'Welcome.' 91 | button: 'OK' 92 | 93 | app.window 94 | 95 | 96 | # x = 1 97 | # y = 2 98 | # 99 | # x = 1, y = 2 100 | 101 | x = 1 102 | y = 2 103 | 104 | x = 1; y = 2 105 | 106 | 107 | # table = (language='Potion' 108 | # pointless=true) 109 | 110 | table = 111 | language: 'Potion' 112 | pointless: yes 113 | 114 | 115 | # # this foul business... 116 | # String length = (): 10. 117 | 118 | # this foul business... 119 | String::length = -> 10 120 | 121 | 122 | # block = : 123 | # 'potion' print. 124 | 125 | block = -> 126 | print 'potion' 127 | 128 | 129 | # if (age > 100): 'ancient'. 130 | 131 | if age > 100 then 'ancient' 132 | 133 | 134 | # author = 135 | # if (title == 'Jonathan Strange & Mr. Norrell'): 136 | # 'Susanna Clarke'. 137 | # elsif (title == 'The Star Diaries'): 138 | # 'Stanislaw Lem'. 139 | # elsif (title == 'The Slynx'): 140 | # 'Tatyana Tolstaya'. 141 | # else: 142 | # '... probably Philip K. Dick'. 143 | 144 | switch author 145 | when 'Jonathan Strange & Mr. Norrell' 146 | 'Susanna Clarke' 147 | when 'The Star Diaries' 148 | 'Stanislaw Lem' 149 | when 'The Slynx' 150 | 'Tatyana Tolstaya' 151 | else 152 | '... probably Philip K. Dick' 153 | 154 | 155 | # count = 8 156 | # while (count > 0): 157 | # 'quaff' print 158 | # count--. 159 | 160 | count = 8 161 | while count > 0 162 | print 'quaff' 163 | count-- 164 | 165 | 166 | # 1 to 5 (a): 167 | # a string print. 168 | 169 | print a for a in [1..5] 170 | 171 | 172 | # if (3 ?gender): 173 | # "Huh? Numbers are sexed? That's amazing." print. 174 | 175 | if 3.gender? 176 | print "Huh? Numbers are sexed? That's amazing." 177 | 178 | 179 | # HomePage get = (url): 180 | # session = url query ? at ('session'). 181 | 182 | HomePage::get = (url) -> 183 | session = url.query?.session 184 | 185 | 186 | # BTree = class: /left, /right. 187 | # b = BTree () 188 | # b /left = BTree () 189 | # b /right = BTree () 190 | 191 | BTree = -> 192 | b = new BTree 193 | b.left = new BTree 194 | b.right = new BTree 195 | 196 | 197 | # BTree = class: /left, /right. 198 | # b = BTree () 199 | # 200 | # if (b ? /left): 201 | # 'left path found!' print. 202 | 203 | BTree = -> 204 | b = new BTree 205 | 206 | print 'left path found!' if b.left? 207 | -------------------------------------------------------------------------------- /examples/web_server.coffee: -------------------------------------------------------------------------------- 1 | # Contributed by Jason Huggins 2 | 3 | http = require 'http' 4 | 5 | server = http.createServer (req, res) -> 6 | res.writeHeader 200, 'Content-Type': 'text/plain' 7 | res.write 'Hello, World!' 8 | res.end() 9 | 10 | server.listen PORT = 3000 11 | 12 | console.log "Server running at http://localhost:#{PORT}/" 13 | -------------------------------------------------------------------------------- /lib/coffee-script/browser.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var CoffeeScript, compile, runScripts, 4 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 5 | 6 | CoffeeScript = require('./coffee-script'); 7 | 8 | CoffeeScript.require = require; 9 | 10 | compile = CoffeeScript.compile; 11 | 12 | CoffeeScript["eval"] = function(code, options) { 13 | if (options == null) { 14 | options = {}; 15 | } 16 | if (options.bare == null) { 17 | options.bare = true; 18 | } 19 | return eval(compile(code, options)); 20 | }; 21 | 22 | CoffeeScript.run = function(code, options) { 23 | if (options == null) { 24 | options = {}; 25 | } 26 | options.bare = true; 27 | options.shiftLine = true; 28 | return Function(compile(code, options))(); 29 | }; 30 | 31 | if (typeof window === "undefined" || window === null) { 32 | return; 33 | } 34 | 35 | if ((typeof btoa !== "undefined" && btoa !== null) && (typeof JSON !== "undefined" && JSON !== null) && (typeof unescape !== "undefined" && unescape !== null) && (typeof encodeURIComponent !== "undefined" && encodeURIComponent !== null)) { 36 | compile = function(code, options) { 37 | var js, v3SourceMap, _ref; 38 | if (options == null) { 39 | options = {}; 40 | } 41 | options.sourceMap = true; 42 | options.inline = true; 43 | _ref = CoffeeScript.compile(code, options), js = _ref.js, v3SourceMap = _ref.v3SourceMap; 44 | return "" + js + "\n//# sourceMappingURL=data:application/json;base64," + (btoa(unescape(encodeURIComponent(v3SourceMap)))) + "\n//# sourceURL=coffeescript"; 45 | }; 46 | } 47 | 48 | CoffeeScript.load = function(url, callback, options, hold) { 49 | var xhr; 50 | if (options == null) { 51 | options = {}; 52 | } 53 | if (hold == null) { 54 | hold = false; 55 | } 56 | options.sourceFiles = [url]; 57 | xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP') : new window.XMLHttpRequest(); 58 | xhr.open('GET', url, true); 59 | if ('overrideMimeType' in xhr) { 60 | xhr.overrideMimeType('text/plain'); 61 | } 62 | xhr.onreadystatechange = function() { 63 | var param, _ref; 64 | if (xhr.readyState === 4) { 65 | if ((_ref = xhr.status) === 0 || _ref === 200) { 66 | param = [xhr.responseText, options]; 67 | if (!hold) { 68 | CoffeeScript.run.apply(CoffeeScript, param); 69 | } 70 | } else { 71 | throw new Error("Could not load " + url); 72 | } 73 | if (callback) { 74 | return callback(param); 75 | } 76 | } 77 | }; 78 | return xhr.send(null); 79 | }; 80 | 81 | runScripts = function() { 82 | var coffees, coffeetypes, execute, i, index, s, script, scripts, _fn, _i, _len; 83 | scripts = window.document.getElementsByTagName('script'); 84 | coffeetypes = ['text/coffeescript', 'text/literate-coffeescript']; 85 | coffees = (function() { 86 | var _i, _len, _ref, _results; 87 | _results = []; 88 | for (_i = 0, _len = scripts.length; _i < _len; _i++) { 89 | s = scripts[_i]; 90 | if (_ref = s.type, __indexOf.call(coffeetypes, _ref) >= 0) { 91 | _results.push(s); 92 | } 93 | } 94 | return _results; 95 | })(); 96 | index = 0; 97 | execute = function() { 98 | var param; 99 | param = coffees[index]; 100 | if (param instanceof Array) { 101 | CoffeeScript.run.apply(CoffeeScript, param); 102 | index++; 103 | return execute(); 104 | } 105 | }; 106 | _fn = function(script, i) { 107 | var options; 108 | options = { 109 | literate: script.type === coffeetypes[1] 110 | }; 111 | if (script.src) { 112 | return CoffeeScript.load(script.src, function(param) { 113 | coffees[i] = param; 114 | return execute(); 115 | }, options, true); 116 | } else { 117 | options.sourceFiles = ['embedded']; 118 | return coffees[i] = [script.innerHTML, options]; 119 | } 120 | }; 121 | for (i = _i = 0, _len = coffees.length; _i < _len; i = ++_i) { 122 | script = coffees[i]; 123 | _fn(script, i); 124 | } 125 | return execute(); 126 | }; 127 | 128 | if (window.addEventListener) { 129 | window.addEventListener('DOMContentLoaded', runScripts, false); 130 | } else { 131 | window.attachEvent('onload', runScripts); 132 | } 133 | 134 | }).call(this); 135 | -------------------------------------------------------------------------------- /lib/coffee-script/cake.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var CoffeeScript, cakefileDirectory, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks; 4 | 5 | fs = require('fs'); 6 | 7 | path = require('path'); 8 | 9 | helpers = require('./helpers'); 10 | 11 | optparse = require('./optparse'); 12 | 13 | CoffeeScript = require('./coffee-script'); 14 | 15 | tasks = {}; 16 | 17 | options = {}; 18 | 19 | switches = []; 20 | 21 | oparse = null; 22 | 23 | helpers.extend(global, { 24 | task: function(name, description, action) { 25 | var _ref; 26 | if (!action) { 27 | _ref = [description, action], action = _ref[0], description = _ref[1]; 28 | } 29 | return tasks[name] = { 30 | name: name, 31 | description: description, 32 | action: action 33 | }; 34 | }, 35 | option: function(letter, flag, description) { 36 | return switches.push([letter, flag, description]); 37 | }, 38 | invoke: function(name) { 39 | if (!tasks[name]) { 40 | missingTask(name); 41 | } 42 | return tasks[name].action(options); 43 | } 44 | }); 45 | 46 | exports.run = function() { 47 | var arg, args, e, _i, _len, _ref, _results; 48 | global.__originalDirname = fs.realpathSync('.'); 49 | process.chdir(cakefileDirectory(__originalDirname)); 50 | args = process.argv.slice(2); 51 | CoffeeScript.run(fs.readFileSync('Cakefile').toString(), { 52 | filename: 'Cakefile' 53 | }); 54 | oparse = new optparse.OptionParser(switches); 55 | if (!args.length) { 56 | return printTasks(); 57 | } 58 | try { 59 | options = oparse.parse(args); 60 | } catch (_error) { 61 | e = _error; 62 | return fatalError("" + e); 63 | } 64 | _ref = options["arguments"]; 65 | _results = []; 66 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 67 | arg = _ref[_i]; 68 | _results.push(invoke(arg)); 69 | } 70 | return _results; 71 | }; 72 | 73 | printTasks = function() { 74 | var cakefilePath, desc, name, relative, spaces, task; 75 | relative = path.relative || path.resolve; 76 | cakefilePath = path.join(relative(__originalDirname, process.cwd()), 'Cakefile'); 77 | console.log("" + cakefilePath + " defines the following tasks:\n"); 78 | for (name in tasks) { 79 | task = tasks[name]; 80 | spaces = 20 - name.length; 81 | spaces = spaces > 0 ? Array(spaces + 1).join(' ') : ''; 82 | desc = task.description ? "# " + task.description : ''; 83 | console.log("cake " + name + spaces + " " + desc); 84 | } 85 | if (switches.length) { 86 | return console.log(oparse.help()); 87 | } 88 | }; 89 | 90 | fatalError = function(message) { 91 | console.error(message + '\n'); 92 | console.log('To see a list of all tasks/options, run "cake"'); 93 | return process.exit(1); 94 | }; 95 | 96 | missingTask = function(task) { 97 | return fatalError("No such task: " + task); 98 | }; 99 | 100 | cakefileDirectory = function(dir) { 101 | var parent; 102 | if (fs.existsSync(path.join(dir, 'Cakefile'))) { 103 | return dir; 104 | } 105 | parent = path.normalize(path.join(dir, '..')); 106 | if (parent !== dir) { 107 | return cakefileDirectory(parent); 108 | } 109 | throw new Error("Cakefile not found in " + (process.cwd())); 110 | }; 111 | 112 | }).call(this); 113 | -------------------------------------------------------------------------------- /lib/coffee-script/index.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var key, val, _ref; 4 | 5 | _ref = require('./coffee-script'); 6 | for (key in _ref) { 7 | val = _ref[key]; 8 | exports[key] = val; 9 | } 10 | 11 | }).call(this); 12 | -------------------------------------------------------------------------------- /lib/coffee-script/optparse.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat; 4 | 5 | repeat = require('./helpers').repeat; 6 | 7 | exports.OptionParser = OptionParser = (function() { 8 | function OptionParser(rules, banner) { 9 | this.banner = banner; 10 | this.rules = buildRules(rules); 11 | } 12 | 13 | OptionParser.prototype.parse = function(args) { 14 | var arg, i, isOption, matchedRule, options, originalArgs, pos, rule, seenNonOptionArg, skippingArgument, value, _i, _j, _len, _len1, _ref; 15 | options = { 16 | "arguments": [] 17 | }; 18 | skippingArgument = false; 19 | originalArgs = args; 20 | args = normalizeArguments(args); 21 | for (i = _i = 0, _len = args.length; _i < _len; i = ++_i) { 22 | arg = args[i]; 23 | if (skippingArgument) { 24 | skippingArgument = false; 25 | continue; 26 | } 27 | if (arg === '--') { 28 | pos = originalArgs.indexOf('--'); 29 | options["arguments"] = options["arguments"].concat(originalArgs.slice(pos + 1)); 30 | break; 31 | } 32 | isOption = !!(arg.match(LONG_FLAG) || arg.match(SHORT_FLAG)); 33 | seenNonOptionArg = options["arguments"].length > 0; 34 | if (!seenNonOptionArg) { 35 | matchedRule = false; 36 | _ref = this.rules; 37 | for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { 38 | rule = _ref[_j]; 39 | if (rule.shortFlag === arg || rule.longFlag === arg) { 40 | value = true; 41 | if (rule.hasArgument) { 42 | skippingArgument = true; 43 | value = args[i + 1]; 44 | } 45 | options[rule.name] = rule.isList ? (options[rule.name] || []).concat(value) : value; 46 | matchedRule = true; 47 | break; 48 | } 49 | } 50 | if (isOption && !matchedRule) { 51 | throw new Error("unrecognized option: " + arg); 52 | } 53 | } 54 | if (seenNonOptionArg || !isOption) { 55 | options["arguments"].push(arg); 56 | } 57 | } 58 | return options; 59 | }; 60 | 61 | OptionParser.prototype.help = function() { 62 | var letPart, lines, rule, spaces, _i, _len, _ref; 63 | lines = []; 64 | if (this.banner) { 65 | lines.unshift("" + this.banner + "\n"); 66 | } 67 | _ref = this.rules; 68 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 69 | rule = _ref[_i]; 70 | spaces = 15 - rule.longFlag.length; 71 | spaces = spaces > 0 ? repeat(' ', spaces) : ''; 72 | letPart = rule.shortFlag ? rule.shortFlag + ', ' : ' '; 73 | lines.push(' ' + letPart + rule.longFlag + spaces + rule.description); 74 | } 75 | return "\n" + (lines.join('\n')) + "\n"; 76 | }; 77 | 78 | return OptionParser; 79 | 80 | })(); 81 | 82 | LONG_FLAG = /^(--\w[\w\-]*)/; 83 | 84 | SHORT_FLAG = /^(-\w)$/; 85 | 86 | MULTI_FLAG = /^-(\w{2,})/; 87 | 88 | OPTIONAL = /\[(\w+(\*?))\]/; 89 | 90 | buildRules = function(rules) { 91 | var tuple, _i, _len, _results; 92 | _results = []; 93 | for (_i = 0, _len = rules.length; _i < _len; _i++) { 94 | tuple = rules[_i]; 95 | if (tuple.length < 3) { 96 | tuple.unshift(null); 97 | } 98 | _results.push(buildRule.apply(null, tuple)); 99 | } 100 | return _results; 101 | }; 102 | 103 | buildRule = function(shortFlag, longFlag, description, options) { 104 | var match; 105 | if (options == null) { 106 | options = {}; 107 | } 108 | match = longFlag.match(OPTIONAL); 109 | longFlag = longFlag.match(LONG_FLAG)[1]; 110 | return { 111 | name: longFlag.substr(2), 112 | shortFlag: shortFlag, 113 | longFlag: longFlag, 114 | description: description, 115 | hasArgument: !!(match && match[1]), 116 | isList: !!(match && match[2]) 117 | }; 118 | }; 119 | 120 | normalizeArguments = function(args) { 121 | var arg, l, match, result, _i, _j, _len, _len1, _ref; 122 | args = args.slice(0); 123 | result = []; 124 | for (_i = 0, _len = args.length; _i < _len; _i++) { 125 | arg = args[_i]; 126 | if (match = arg.match(MULTI_FLAG)) { 127 | _ref = match[1].split(''); 128 | for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { 129 | l = _ref[_j]; 130 | result.push('-' + l); 131 | } 132 | } else { 133 | result.push(arg); 134 | } 135 | } 136 | return result; 137 | }; 138 | 139 | }).call(this); 140 | -------------------------------------------------------------------------------- /lib/coffee-script/register.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var CoffeeScript, Module, binary, child_process, ext, findExtension, fork, helpers, loadFile, path, _i, _len, _ref; 4 | 5 | CoffeeScript = require('./coffee-script'); 6 | 7 | child_process = require('child_process'); 8 | 9 | helpers = require('./helpers'); 10 | 11 | path = require('path'); 12 | 13 | loadFile = function(module, filename) { 14 | var answer; 15 | answer = CoffeeScript._compileFile(filename, false); 16 | return module._compile(answer, filename); 17 | }; 18 | 19 | if (require.extensions) { 20 | _ref = CoffeeScript.FILE_EXTENSIONS; 21 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 22 | ext = _ref[_i]; 23 | require.extensions[ext] = loadFile; 24 | } 25 | Module = require('module'); 26 | findExtension = function(filename) { 27 | var curExtension, extensions; 28 | extensions = path.basename(filename).split('.'); 29 | if (extensions[0] === '') { 30 | extensions.shift(); 31 | } 32 | while (extensions.shift()) { 33 | curExtension = '.' + extensions.join('.'); 34 | if (Module._extensions[curExtension]) { 35 | return curExtension; 36 | } 37 | } 38 | return '.js'; 39 | }; 40 | Module.prototype.load = function(filename) { 41 | var extension; 42 | this.filename = filename; 43 | this.paths = Module._nodeModulePaths(path.dirname(filename)); 44 | extension = findExtension(filename); 45 | Module._extensions[extension](this, filename); 46 | return this.loaded = true; 47 | }; 48 | } 49 | 50 | if (child_process) { 51 | fork = child_process.fork; 52 | binary = require.resolve('../../bin/coffee'); 53 | child_process.fork = function(path, args, options) { 54 | if (helpers.isCoffee(path)) { 55 | if (!Array.isArray(args)) { 56 | options = args || {}; 57 | args = []; 58 | } 59 | args = [path].concat(args); 60 | path = binary; 61 | } 62 | return fork(path, args, options); 63 | }; 64 | } 65 | 66 | }).call(this); 67 | -------------------------------------------------------------------------------- /lib/coffee-script/repl.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var CoffeeScript, addHistory, addMultilineHandler, fs, merge, nodeREPL, path, replDefaults, updateSyntaxError, vm, _ref; 4 | 5 | fs = require('fs'); 6 | 7 | path = require('path'); 8 | 9 | vm = require('vm'); 10 | 11 | nodeREPL = require('repl'); 12 | 13 | CoffeeScript = require('./coffee-script'); 14 | 15 | _ref = require('./helpers'), merge = _ref.merge, updateSyntaxError = _ref.updateSyntaxError; 16 | 17 | replDefaults = { 18 | prompt: 'coffee> ', 19 | historyFile: process.env.HOME ? path.join(process.env.HOME, '.coffee_history') : void 0, 20 | historyMaxInputSize: 10240, 21 | "eval": function(input, context, filename, cb) { 22 | var Assign, Block, Literal, Value, ast, err, js, result, _ref1; 23 | input = input.replace(/\uFF00/g, '\n'); 24 | input = input.replace(/^\(([\s\S]*)\n\)$/m, '$1'); 25 | _ref1 = require('./nodes'), Block = _ref1.Block, Assign = _ref1.Assign, Value = _ref1.Value, Literal = _ref1.Literal; 26 | try { 27 | ast = CoffeeScript.nodes(input); 28 | ast = new Block([new Assign(new Value(new Literal('_')), ast, '=')]); 29 | js = ast.compile({ 30 | bare: true, 31 | locals: Object.keys(context) 32 | }); 33 | result = context === global ? vm.runInThisContext(js, filename) : vm.runInContext(js, context, filename); 34 | return cb(null, result); 35 | } catch (_error) { 36 | err = _error; 37 | updateSyntaxError(err, input); 38 | return cb(err); 39 | } 40 | } 41 | }; 42 | 43 | addMultilineHandler = function(repl) { 44 | var inputStream, multiline, nodeLineListener, outputStream, rli; 45 | rli = repl.rli, inputStream = repl.inputStream, outputStream = repl.outputStream; 46 | multiline = { 47 | enabled: false, 48 | initialPrompt: repl.prompt.replace(/^[^> ]*/, function(x) { 49 | return x.replace(/./g, '-'); 50 | }), 51 | prompt: repl.prompt.replace(/^[^> ]*>?/, function(x) { 52 | return x.replace(/./g, '.'); 53 | }), 54 | buffer: '' 55 | }; 56 | nodeLineListener = rli.listeners('line')[0]; 57 | rli.removeListener('line', nodeLineListener); 58 | rli.on('line', function(cmd) { 59 | if (multiline.enabled) { 60 | multiline.buffer += "" + cmd + "\n"; 61 | rli.setPrompt(multiline.prompt); 62 | rli.prompt(true); 63 | } else { 64 | nodeLineListener(cmd); 65 | } 66 | }); 67 | return inputStream.on('keypress', function(char, key) { 68 | if (!(key && key.ctrl && !key.meta && !key.shift && key.name === 'v')) { 69 | return; 70 | } 71 | if (multiline.enabled) { 72 | if (!multiline.buffer.match(/\n/)) { 73 | multiline.enabled = !multiline.enabled; 74 | rli.setPrompt(repl.prompt); 75 | rli.prompt(true); 76 | return; 77 | } 78 | if ((rli.line != null) && !rli.line.match(/^\s*$/)) { 79 | return; 80 | } 81 | multiline.enabled = !multiline.enabled; 82 | rli.line = ''; 83 | rli.cursor = 0; 84 | rli.output.cursorTo(0); 85 | rli.output.clearLine(1); 86 | multiline.buffer = multiline.buffer.replace(/\n/g, '\uFF00'); 87 | rli.emit('line', multiline.buffer); 88 | multiline.buffer = ''; 89 | } else { 90 | multiline.enabled = !multiline.enabled; 91 | rli.setPrompt(multiline.initialPrompt); 92 | rli.prompt(true); 93 | } 94 | }); 95 | }; 96 | 97 | addHistory = function(repl, filename, maxSize) { 98 | var buffer, fd, lastLine, readFd, size, stat; 99 | lastLine = null; 100 | try { 101 | stat = fs.statSync(filename); 102 | size = Math.min(maxSize, stat.size); 103 | readFd = fs.openSync(filename, 'r'); 104 | buffer = new Buffer(size); 105 | fs.readSync(readFd, buffer, 0, size, stat.size - size); 106 | repl.rli.history = buffer.toString().split('\n').reverse(); 107 | if (stat.size > maxSize) { 108 | repl.rli.history.pop(); 109 | } 110 | if (repl.rli.history[0] === '') { 111 | repl.rli.history.shift(); 112 | } 113 | repl.rli.historyIndex = -1; 114 | lastLine = repl.rli.history[0]; 115 | } catch (_error) {} 116 | fd = fs.openSync(filename, 'a'); 117 | repl.rli.addListener('line', function(code) { 118 | if (code && code.length && code !== '.history' && lastLine !== code) { 119 | fs.write(fd, "" + code + "\n"); 120 | return lastLine = code; 121 | } 122 | }); 123 | repl.rli.on('exit', function() { 124 | return fs.close(fd); 125 | }); 126 | return repl.commands['.history'] = { 127 | help: 'Show command history', 128 | action: function() { 129 | repl.outputStream.write("" + (repl.rli.history.slice(0).reverse().join('\n')) + "\n"); 130 | return repl.displayPrompt(); 131 | } 132 | }; 133 | }; 134 | 135 | module.exports = { 136 | start: function(opts) { 137 | var build, major, minor, repl, _ref1; 138 | if (opts == null) { 139 | opts = {}; 140 | } 141 | _ref1 = process.versions.node.split('.').map(function(n) { 142 | return parseInt(n); 143 | }), major = _ref1[0], minor = _ref1[1], build = _ref1[2]; 144 | if (major === 0 && minor < 8) { 145 | console.warn("Node 0.8.0+ required for CoffeeScript REPL"); 146 | process.exit(1); 147 | } 148 | CoffeeScript.register(); 149 | process.argv = ['coffee'].concat(process.argv.slice(2)); 150 | opts = merge(replDefaults, opts); 151 | repl = nodeREPL.start(opts); 152 | repl.on('exit', function() { 153 | return repl.outputStream.write('\n'); 154 | }); 155 | addMultilineHandler(repl); 156 | if (opts.historyFile) { 157 | addHistory(repl, opts.historyFile, opts.historyMaxInputSize); 158 | } 159 | return repl; 160 | } 161 | }; 162 | 163 | }).call(this); 164 | -------------------------------------------------------------------------------- /lib/coffee-script/scope.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var Scope, extend, last, _ref; 4 | 5 | _ref = require('./helpers'), extend = _ref.extend, last = _ref.last; 6 | 7 | exports.Scope = Scope = (function() { 8 | Scope.root = null; 9 | 10 | function Scope(parent, expressions, method) { 11 | this.parent = parent; 12 | this.expressions = expressions; 13 | this.method = method; 14 | this.variables = [ 15 | { 16 | name: 'arguments', 17 | type: 'arguments' 18 | } 19 | ]; 20 | this.positions = {}; 21 | if (!this.parent) { 22 | Scope.root = this; 23 | } 24 | } 25 | 26 | Scope.prototype.add = function(name, type, immediate) { 27 | if (this.shared && !immediate) { 28 | return this.parent.add(name, type, immediate); 29 | } 30 | if (Object.prototype.hasOwnProperty.call(this.positions, name)) { 31 | return this.variables[this.positions[name]].type = type; 32 | } else { 33 | return this.positions[name] = this.variables.push({ 34 | name: name, 35 | type: type 36 | }) - 1; 37 | } 38 | }; 39 | 40 | Scope.prototype.namedMethod = function() { 41 | var _ref1; 42 | if (((_ref1 = this.method) != null ? _ref1.name : void 0) || !this.parent) { 43 | return this.method; 44 | } 45 | return this.parent.namedMethod(); 46 | }; 47 | 48 | Scope.prototype.find = function(name) { 49 | if (this.check(name)) { 50 | return true; 51 | } 52 | this.add(name, 'var'); 53 | return false; 54 | }; 55 | 56 | Scope.prototype.parameter = function(name) { 57 | if (this.shared && this.parent.check(name, true)) { 58 | return; 59 | } 60 | return this.add(name, 'param'); 61 | }; 62 | 63 | Scope.prototype.check = function(name) { 64 | var _ref1; 65 | return !!(this.type(name) || ((_ref1 = this.parent) != null ? _ref1.check(name) : void 0)); 66 | }; 67 | 68 | Scope.prototype.temporary = function(name, index) { 69 | if (name.length > 1) { 70 | return '_' + name + (index > 1 ? index - 1 : ''); 71 | } else { 72 | return '_' + (index + parseInt(name, 36)).toString(36).replace(/\d/g, 'a'); 73 | } 74 | }; 75 | 76 | Scope.prototype.type = function(name) { 77 | var v, _i, _len, _ref1; 78 | _ref1 = this.variables; 79 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) { 80 | v = _ref1[_i]; 81 | if (v.name === name) { 82 | return v.type; 83 | } 84 | } 85 | return null; 86 | }; 87 | 88 | Scope.prototype.freeVariable = function(name, reserve) { 89 | var index, temp; 90 | if (reserve == null) { 91 | reserve = true; 92 | } 93 | index = 0; 94 | while (this.check((temp = this.temporary(name, index)))) { 95 | index++; 96 | } 97 | if (reserve) { 98 | this.add(temp, 'var', true); 99 | } 100 | return temp; 101 | }; 102 | 103 | Scope.prototype.assign = function(name, value) { 104 | this.add(name, { 105 | value: value, 106 | assigned: true 107 | }, true); 108 | return this.hasAssignments = true; 109 | }; 110 | 111 | Scope.prototype.hasDeclarations = function() { 112 | return !!this.declaredVariables().length; 113 | }; 114 | 115 | Scope.prototype.declaredVariables = function() { 116 | var realVars, tempVars, v, _i, _len, _ref1; 117 | realVars = []; 118 | tempVars = []; 119 | _ref1 = this.variables; 120 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) { 121 | v = _ref1[_i]; 122 | if (v.type === 'var') { 123 | (v.name.charAt(0) === '_' ? tempVars : realVars).push(v.name); 124 | } 125 | } 126 | return realVars.sort().concat(tempVars.sort()); 127 | }; 128 | 129 | Scope.prototype.assignedVariables = function() { 130 | var v, _i, _len, _ref1, _results; 131 | _ref1 = this.variables; 132 | _results = []; 133 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) { 134 | v = _ref1[_i]; 135 | if (v.type.assigned) { 136 | _results.push("" + v.name + " = " + v.type.value); 137 | } 138 | } 139 | return _results; 140 | }; 141 | 142 | return Scope; 143 | 144 | })(); 145 | 146 | }).call(this); 147 | -------------------------------------------------------------------------------- /lib/coffee-script/sourcemap.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var LineMap, SourceMap; 4 | 5 | LineMap = (function() { 6 | function LineMap(line) { 7 | this.line = line; 8 | this.columns = []; 9 | } 10 | 11 | LineMap.prototype.add = function(column, _arg, options) { 12 | var sourceColumn, sourceLine; 13 | sourceLine = _arg[0], sourceColumn = _arg[1]; 14 | if (options == null) { 15 | options = {}; 16 | } 17 | if (this.columns[column] && options.noReplace) { 18 | return; 19 | } 20 | return this.columns[column] = { 21 | line: this.line, 22 | column: column, 23 | sourceLine: sourceLine, 24 | sourceColumn: sourceColumn 25 | }; 26 | }; 27 | 28 | LineMap.prototype.sourceLocation = function(column) { 29 | var mapping; 30 | while (!((mapping = this.columns[column]) || (column <= 0))) { 31 | column--; 32 | } 33 | return mapping && [mapping.sourceLine, mapping.sourceColumn]; 34 | }; 35 | 36 | return LineMap; 37 | 38 | })(); 39 | 40 | SourceMap = (function() { 41 | var BASE64_CHARS, VLQ_CONTINUATION_BIT, VLQ_SHIFT, VLQ_VALUE_MASK; 42 | 43 | function SourceMap() { 44 | this.lines = []; 45 | } 46 | 47 | SourceMap.prototype.add = function(sourceLocation, generatedLocation, options) { 48 | var column, line, lineMap, _base; 49 | if (options == null) { 50 | options = {}; 51 | } 52 | line = generatedLocation[0], column = generatedLocation[1]; 53 | lineMap = ((_base = this.lines)[line] || (_base[line] = new LineMap(line))); 54 | return lineMap.add(column, sourceLocation, options); 55 | }; 56 | 57 | SourceMap.prototype.sourceLocation = function(_arg) { 58 | var column, line, lineMap; 59 | line = _arg[0], column = _arg[1]; 60 | while (!((lineMap = this.lines[line]) || (line <= 0))) { 61 | line--; 62 | } 63 | return lineMap && lineMap.sourceLocation(column); 64 | }; 65 | 66 | SourceMap.prototype.generate = function(options, code) { 67 | var buffer, lastColumn, lastSourceColumn, lastSourceLine, lineMap, lineNumber, mapping, needComma, v3, writingline, _i, _j, _len, _len1, _ref, _ref1; 68 | if (options == null) { 69 | options = {}; 70 | } 71 | if (code == null) { 72 | code = null; 73 | } 74 | writingline = 0; 75 | lastColumn = 0; 76 | lastSourceLine = 0; 77 | lastSourceColumn = 0; 78 | needComma = false; 79 | buffer = ""; 80 | _ref = this.lines; 81 | for (lineNumber = _i = 0, _len = _ref.length; _i < _len; lineNumber = ++_i) { 82 | lineMap = _ref[lineNumber]; 83 | if (lineMap) { 84 | _ref1 = lineMap.columns; 85 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 86 | mapping = _ref1[_j]; 87 | if (!(mapping)) { 88 | continue; 89 | } 90 | while (writingline < mapping.line) { 91 | lastColumn = 0; 92 | needComma = false; 93 | buffer += ";"; 94 | writingline++; 95 | } 96 | if (needComma) { 97 | buffer += ","; 98 | needComma = false; 99 | } 100 | buffer += this.encodeVlq(mapping.column - lastColumn); 101 | lastColumn = mapping.column; 102 | buffer += this.encodeVlq(0); 103 | buffer += this.encodeVlq(mapping.sourceLine - lastSourceLine); 104 | lastSourceLine = mapping.sourceLine; 105 | buffer += this.encodeVlq(mapping.sourceColumn - lastSourceColumn); 106 | lastSourceColumn = mapping.sourceColumn; 107 | needComma = true; 108 | } 109 | } 110 | } 111 | v3 = { 112 | version: 3, 113 | file: options.generatedFile || '', 114 | sourceRoot: options.sourceRoot || '', 115 | sources: options.sourceFiles || [''], 116 | names: [], 117 | mappings: buffer 118 | }; 119 | if (options.inline) { 120 | v3.sourcesContent = [code]; 121 | } 122 | return JSON.stringify(v3, null, 2); 123 | }; 124 | 125 | VLQ_SHIFT = 5; 126 | 127 | VLQ_CONTINUATION_BIT = 1 << VLQ_SHIFT; 128 | 129 | VLQ_VALUE_MASK = VLQ_CONTINUATION_BIT - 1; 130 | 131 | SourceMap.prototype.encodeVlq = function(value) { 132 | var answer, nextChunk, signBit, valueToEncode; 133 | answer = ''; 134 | signBit = value < 0 ? 1 : 0; 135 | valueToEncode = (Math.abs(value) << 1) + signBit; 136 | while (valueToEncode || !answer) { 137 | nextChunk = valueToEncode & VLQ_VALUE_MASK; 138 | valueToEncode = valueToEncode >> VLQ_SHIFT; 139 | if (valueToEncode) { 140 | nextChunk |= VLQ_CONTINUATION_BIT; 141 | } 142 | answer += this.encodeBase64(nextChunk); 143 | } 144 | return answer; 145 | }; 146 | 147 | BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 148 | 149 | SourceMap.prototype.encodeBase64 = function(value) { 150 | return BASE64_CHARS[value] || (function() { 151 | throw new Error("Cannot Base64 encode value: " + value); 152 | })(); 153 | }; 154 | 155 | return SourceMap; 156 | 157 | })(); 158 | 159 | module.exports = SourceMap; 160 | 161 | }).call(this); 162 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coffee-script", 3 | "description": "Unfancy JavaScript", 4 | "keywords": [ 5 | "javascript", 6 | "language", 7 | "coffeescript", 8 | "compiler" 9 | ], 10 | "author": "Jeremy Ashkenas", 11 | "version": "1.7.1", 12 | "license": "MIT", 13 | "engines": { 14 | "node": ">=0.8.0" 15 | }, 16 | "directories": { 17 | "lib": "./lib/coffee-script" 18 | }, 19 | "main": "./lib/coffee-script/coffee-script", 20 | "bin": { 21 | "coffee": "./bin/coffee", 22 | "cake": "./bin/cake" 23 | }, 24 | "scripts": { 25 | "test": "node ./bin/cake test" 26 | }, 27 | "homepage": "http://coffeescript.org", 28 | "bugs": "https://github.com/jashkenas/coffee-script/issues", 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/jashkenas/coffee-script.git" 32 | }, 33 | "devDependencies": { 34 | "uglify-js": "~2.2", 35 | "jison": ">=0.2.0", 36 | "highlight.js": "~8.0.0", 37 | "underscore": "~1.5.2" 38 | }, 39 | "dependencies": { 40 | "mkdirp": "~0.3.5", 41 | "docco": "~0.6.2" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /register.js: -------------------------------------------------------------------------------- 1 | require('./lib/coffee-script/register'); 2 | -------------------------------------------------------------------------------- /src/browser.coffee: -------------------------------------------------------------------------------- 1 | # This **Browser** compatibility layer extends core CoffeeScript functions 2 | # to make things work smoothly when compiling code directly in the browser. 3 | # We add support for loading remote Coffee scripts via **XHR**, and 4 | # `text/coffeescript` script tags, source maps via data-URLs, and so on. 5 | 6 | CoffeeScript = require './coffee-script' 7 | CoffeeScript.require = require 8 | compile = CoffeeScript.compile 9 | 10 | # Use standard JavaScript `eval` to eval code. 11 | CoffeeScript.eval = (code, options = {}) -> 12 | options.bare ?= on 13 | eval compile code, options 14 | 15 | # Running code does not provide access to this scope. 16 | CoffeeScript.run = (code, options = {}) -> 17 | options.bare = on 18 | options.shiftLine = on 19 | Function(compile code, options)() 20 | 21 | # If we're not in a browser environment, we're finished with the public API. 22 | return unless window? 23 | 24 | # Include source maps where possible. If we've got a base64 encoder, a 25 | # JSON serializer, and tools for escaping unicode characters, we're good to go. 26 | # Ported from https://developer.mozilla.org/en-US/docs/DOM/window.btoa 27 | if btoa? and JSON? and unescape? and encodeURIComponent? 28 | compile = (code, options = {}) -> 29 | options.sourceMap = true 30 | options.inline = true 31 | {js, v3SourceMap} = CoffeeScript.compile code, options 32 | "#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=coffeescript" 33 | 34 | # Load a remote script from the current domain via XHR. 35 | CoffeeScript.load = (url, callback, options = {}, hold = false) -> 36 | options.sourceFiles = [url] 37 | xhr = if window.ActiveXObject 38 | new window.ActiveXObject('Microsoft.XMLHTTP') 39 | else 40 | new window.XMLHttpRequest() 41 | xhr.open 'GET', url, true 42 | xhr.overrideMimeType 'text/plain' if 'overrideMimeType' of xhr 43 | xhr.onreadystatechange = -> 44 | if xhr.readyState is 4 45 | if xhr.status in [0, 200] 46 | param = [xhr.responseText, options] 47 | CoffeeScript.run param... unless hold 48 | else 49 | throw new Error "Could not load #{url}" 50 | callback param if callback 51 | xhr.send null 52 | 53 | # Activate CoffeeScript in the browser by having it compile and evaluate 54 | # all script tags with a content-type of `text/coffeescript`. 55 | # This happens on page load. 56 | runScripts = -> 57 | scripts = window.document.getElementsByTagName 'script' 58 | coffeetypes = ['text/coffeescript', 'text/literate-coffeescript'] 59 | coffees = (s for s in scripts when s.type in coffeetypes) 60 | index = 0 61 | 62 | execute = -> 63 | param = coffees[index] 64 | if param instanceof Array 65 | CoffeeScript.run param... 66 | index++ 67 | execute() 68 | 69 | for script, i in coffees 70 | do (script, i) -> 71 | options = literate: script.type is coffeetypes[1] 72 | if script.src 73 | CoffeeScript.load script.src, 74 | (param) -> 75 | coffees[i] = param 76 | execute() 77 | options 78 | true 79 | else 80 | options.sourceFiles = ['embedded'] 81 | coffees[i] = [script.innerHTML, options] 82 | 83 | execute() 84 | 85 | # Listen for window load, both in decent browsers and in IE. 86 | if window.addEventListener 87 | window.addEventListener 'DOMContentLoaded', runScripts, no 88 | else 89 | window.attachEvent 'onload', runScripts 90 | -------------------------------------------------------------------------------- /src/cake.coffee: -------------------------------------------------------------------------------- 1 | # `cake` is a simplified version of [Make](http://www.gnu.org/software/make/) 2 | # ([Rake](http://rake.rubyforge.org/), [Jake](http://github.com/280north/jake)) 3 | # for CoffeeScript. You define tasks with names and descriptions in a Cakefile, 4 | # and can call them from the command line, or invoke them from other tasks. 5 | # 6 | # Running `cake` with no arguments will print out a list of all the tasks in the 7 | # current directory's Cakefile. 8 | 9 | # External dependencies. 10 | fs = require 'fs' 11 | path = require 'path' 12 | helpers = require './helpers' 13 | optparse = require './optparse' 14 | CoffeeScript = require './coffee-script' 15 | 16 | # Keep track of the list of defined tasks, the accepted options, and so on. 17 | tasks = {} 18 | options = {} 19 | switches = [] 20 | oparse = null 21 | 22 | # Mixin the top-level Cake functions for Cakefiles to use directly. 23 | helpers.extend global, 24 | 25 | # Define a Cake task with a short name, an optional sentence description, 26 | # and the function to run as the action itself. 27 | task: (name, description, action) -> 28 | [action, description] = [description, action] unless action 29 | tasks[name] = {name, description, action} 30 | 31 | # Define an option that the Cakefile accepts. The parsed options hash, 32 | # containing all of the command-line options passed, will be made available 33 | # as the first argument to the action. 34 | option: (letter, flag, description) -> 35 | switches.push [letter, flag, description] 36 | 37 | # Invoke another task in the current Cakefile. 38 | invoke: (name) -> 39 | missingTask name unless tasks[name] 40 | tasks[name].action options 41 | 42 | # Run `cake`. Executes all of the tasks you pass, in order. Note that Node's 43 | # asynchrony may cause tasks to execute in a different order than you'd expect. 44 | # If no tasks are passed, print the help screen. Keep a reference to the 45 | # original directory name, when running Cake tasks from subdirectories. 46 | exports.run = -> 47 | global.__originalDirname = fs.realpathSync '.' 48 | process.chdir cakefileDirectory __originalDirname 49 | args = process.argv[2..] 50 | CoffeeScript.run fs.readFileSync('Cakefile').toString(), filename: 'Cakefile' 51 | oparse = new optparse.OptionParser switches 52 | return printTasks() unless args.length 53 | try 54 | options = oparse.parse(args) 55 | catch e 56 | return fatalError "#{e}" 57 | invoke arg for arg in options.arguments 58 | 59 | # Display the list of Cake tasks in a format similar to `rake -T` 60 | printTasks = -> 61 | relative = path.relative or path.resolve 62 | cakefilePath = path.join relative(__originalDirname, process.cwd()), 'Cakefile' 63 | console.log "#{cakefilePath} defines the following tasks:\n" 64 | for name, task of tasks 65 | spaces = 20 - name.length 66 | spaces = if spaces > 0 then Array(spaces + 1).join(' ') else '' 67 | desc = if task.description then "# #{task.description}" else '' 68 | console.log "cake #{name}#{spaces} #{desc}" 69 | console.log oparse.help() if switches.length 70 | 71 | # Print an error and exit when attempting to use an invalid task/option. 72 | fatalError = (message) -> 73 | console.error message + '\n' 74 | console.log 'To see a list of all tasks/options, run "cake"' 75 | process.exit 1 76 | 77 | missingTask = (task) -> fatalError "No such task: #{task}" 78 | 79 | # When `cake` is invoked, search in the current and all parent directories 80 | # to find the relevant Cakefile. 81 | cakefileDirectory = (dir) -> 82 | return dir if fs.existsSync path.join dir, 'Cakefile' 83 | parent = path.normalize path.join dir, '..' 84 | return cakefileDirectory parent unless parent is dir 85 | throw new Error "Cakefile not found in #{process.cwd()}" 86 | -------------------------------------------------------------------------------- /src/index.coffee: -------------------------------------------------------------------------------- 1 | # Loader for CoffeeScript as a Node.js library. 2 | exports[key] = val for key, val of require './coffee-script' -------------------------------------------------------------------------------- /src/optparse.coffee: -------------------------------------------------------------------------------- 1 | {repeat} = require './helpers' 2 | 3 | # A simple **OptionParser** class to parse option flags from the command-line. 4 | # Use it like so: 5 | # 6 | # parser = new OptionParser switches, helpBanner 7 | # options = parser.parse process.argv 8 | # 9 | # The first non-option is considered to be the start of the file (and file 10 | # option) list, and all subsequent arguments are left unparsed. 11 | exports.OptionParser = class OptionParser 12 | 13 | # Initialize with a list of valid options, in the form: 14 | # 15 | # [short-flag, long-flag, description] 16 | # 17 | # Along with an an optional banner for the usage help. 18 | constructor: (rules, @banner) -> 19 | @rules = buildRules rules 20 | 21 | # Parse the list of arguments, populating an `options` object with all of the 22 | # specified options, and return it. Options after the first non-option 23 | # argument are treated as arguments. `options.arguments` will be an array 24 | # containing the remaining arguments. This is a simpler API than many option 25 | # parsers that allow you to attach callback actions for every flag. Instead, 26 | # you're responsible for interpreting the options object. 27 | parse: (args) -> 28 | options = arguments: [] 29 | skippingArgument = no 30 | originalArgs = args 31 | args = normalizeArguments args 32 | for arg, i in args 33 | if skippingArgument 34 | skippingArgument = no 35 | continue 36 | if arg is '--' 37 | pos = originalArgs.indexOf '--' 38 | options.arguments = options.arguments.concat originalArgs[(pos + 1)..] 39 | break 40 | isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG)) 41 | # the CS option parser is a little odd; options after the first 42 | # non-option argument are treated as non-option arguments themselves 43 | seenNonOptionArg = options.arguments.length > 0 44 | unless seenNonOptionArg 45 | matchedRule = no 46 | for rule in @rules 47 | if rule.shortFlag is arg or rule.longFlag is arg 48 | value = true 49 | if rule.hasArgument 50 | skippingArgument = yes 51 | value = args[i + 1] 52 | options[rule.name] = if rule.isList then (options[rule.name] or []).concat value else value 53 | matchedRule = yes 54 | break 55 | throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule 56 | if seenNonOptionArg or not isOption 57 | options.arguments.push arg 58 | options 59 | 60 | # Return the help text for this **OptionParser**, listing and describing all 61 | # of the valid options, for `--help` and such. 62 | help: -> 63 | lines = [] 64 | lines.unshift "#{@banner}\n" if @banner 65 | for rule in @rules 66 | spaces = 15 - rule.longFlag.length 67 | spaces = if spaces > 0 then repeat ' ', spaces else '' 68 | letPart = if rule.shortFlag then rule.shortFlag + ', ' else ' ' 69 | lines.push ' ' + letPart + rule.longFlag + spaces + rule.description 70 | "\n#{ lines.join('\n') }\n" 71 | 72 | # Helpers 73 | # ------- 74 | 75 | # Regex matchers for option flags. 76 | LONG_FLAG = /^(--\w[\w\-]*)/ 77 | SHORT_FLAG = /^(-\w)$/ 78 | MULTI_FLAG = /^-(\w{2,})/ 79 | OPTIONAL = /\[(\w+(\*?))\]/ 80 | 81 | # Build and return the list of option rules. If the optional *short-flag* is 82 | # unspecified, leave it out by padding with `null`. 83 | buildRules = (rules) -> 84 | for tuple in rules 85 | tuple.unshift null if tuple.length < 3 86 | buildRule tuple... 87 | 88 | # Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the 89 | # description of what the option does. 90 | buildRule = (shortFlag, longFlag, description, options = {}) -> 91 | match = longFlag.match(OPTIONAL) 92 | longFlag = longFlag.match(LONG_FLAG)[1] 93 | { 94 | name: longFlag.substr 2 95 | shortFlag: shortFlag 96 | longFlag: longFlag 97 | description: description 98 | hasArgument: !!(match and match[1]) 99 | isList: !!(match and match[2]) 100 | } 101 | 102 | # Normalize arguments by expanding merged flags into multiple flags. This allows 103 | # you to have `-wl` be the same as `--watch --lint`. 104 | normalizeArguments = (args) -> 105 | args = args[..] 106 | result = [] 107 | for arg in args 108 | if match = arg.match MULTI_FLAG 109 | result.push '-' + l for l in match[1].split '' 110 | else 111 | result.push arg 112 | result 113 | -------------------------------------------------------------------------------- /src/register.coffee: -------------------------------------------------------------------------------- 1 | CoffeeScript = require './coffee-script' 2 | child_process = require 'child_process' 3 | helpers = require './helpers' 4 | path = require 'path' 5 | 6 | # Load and run a CoffeeScript file for Node, stripping any `BOM`s. 7 | loadFile = (module, filename) -> 8 | answer = CoffeeScript._compileFile filename, false 9 | module._compile answer, filename 10 | 11 | # If the installed version of Node supports `require.extensions`, register 12 | # CoffeeScript as an extension. 13 | if require.extensions 14 | for ext in CoffeeScript.FILE_EXTENSIONS 15 | require.extensions[ext] = loadFile 16 | 17 | # Patch Node's module loader to be able to handle mult-dot extensions. 18 | # This is a horrible thing that should not be required. Perhaps, one day, 19 | # when a truly benevolent dictator comes to rule over the Republik of Node, 20 | # it won't be. 21 | Module = require 'module' 22 | 23 | findExtension = (filename) -> 24 | extensions = path.basename(filename).split '.' 25 | # Remove the initial dot from dotfiles. 26 | extensions.shift() if extensions[0] is '' 27 | # Start with the longest possible extension and work our way shortwards. 28 | while extensions.shift() 29 | curExtension = '.' + extensions.join '.' 30 | return curExtension if Module._extensions[curExtension] 31 | '.js' 32 | 33 | Module::load = (filename) -> 34 | @filename = filename 35 | @paths = Module._nodeModulePaths path.dirname filename 36 | extension = findExtension filename 37 | Module._extensions[extension](this, filename) 38 | @loaded = true 39 | 40 | # If we're on Node, patch `child_process.fork` so that Coffee scripts are able 41 | # to fork both CoffeeScript files, and JavaScript files, directly. 42 | if child_process 43 | {fork} = child_process 44 | binary = require.resolve '../../bin/coffee' 45 | child_process.fork = (path, args, options) -> 46 | if helpers.isCoffee path 47 | unless Array.isArray args 48 | options = args or {} 49 | args = [] 50 | args = [path].concat args 51 | path = binary 52 | fork path, args, options -------------------------------------------------------------------------------- /src/repl.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | path = require 'path' 3 | vm = require 'vm' 4 | nodeREPL = require 'repl' 5 | CoffeeScript = require './coffee-script' 6 | {merge, updateSyntaxError} = require './helpers' 7 | 8 | replDefaults = 9 | prompt: 'coffee> ', 10 | historyFile: path.join process.env.HOME, '.coffee_history' if process.env.HOME 11 | historyMaxInputSize: 10240 12 | eval: (input, context, filename, cb) -> 13 | # XXX: multiline hack. 14 | input = input.replace /\uFF00/g, '\n' 15 | # Node's REPL sends the input ending with a newline and then wrapped in 16 | # parens. Unwrap all that. 17 | input = input.replace /^\(([\s\S]*)\n\)$/m, '$1' 18 | 19 | # Require AST nodes to do some AST manipulation. 20 | {Block, Assign, Value, Literal} = require './nodes' 21 | 22 | try 23 | # Generate the AST of the clean input. 24 | ast = CoffeeScript.nodes input 25 | # Add assignment to `_` variable to force the input to be an expression. 26 | ast = new Block [ 27 | new Assign (new Value new Literal '_'), ast, '=' 28 | ] 29 | js = ast.compile bare: yes, locals: Object.keys(context) 30 | result = if context is global 31 | vm.runInThisContext js, filename 32 | else 33 | vm.runInContext js, context, filename 34 | cb null, result 35 | catch err 36 | # AST's `compile` does not add source code information to syntax errors. 37 | updateSyntaxError err, input 38 | cb err 39 | 40 | addMultilineHandler = (repl) -> 41 | {rli, inputStream, outputStream} = repl 42 | 43 | multiline = 44 | enabled: off 45 | initialPrompt: repl.prompt.replace /^[^> ]*/, (x) -> x.replace /./g, '-' 46 | prompt: repl.prompt.replace /^[^> ]*>?/, (x) -> x.replace /./g, '.' 47 | buffer: '' 48 | 49 | # Proxy node's line listener 50 | nodeLineListener = rli.listeners('line')[0] 51 | rli.removeListener 'line', nodeLineListener 52 | rli.on 'line', (cmd) -> 53 | if multiline.enabled 54 | multiline.buffer += "#{cmd}\n" 55 | rli.setPrompt multiline.prompt 56 | rli.prompt true 57 | else 58 | nodeLineListener cmd 59 | return 60 | 61 | # Handle Ctrl-v 62 | inputStream.on 'keypress', (char, key) -> 63 | return unless key and key.ctrl and not key.meta and not key.shift and key.name is 'v' 64 | if multiline.enabled 65 | # allow arbitrarily switching between modes any time before multiple lines are entered 66 | unless multiline.buffer.match /\n/ 67 | multiline.enabled = not multiline.enabled 68 | rli.setPrompt repl.prompt 69 | rli.prompt true 70 | return 71 | # no-op unless the current line is empty 72 | return if rli.line? and not rli.line.match /^\s*$/ 73 | # eval, print, loop 74 | multiline.enabled = not multiline.enabled 75 | rli.line = '' 76 | rli.cursor = 0 77 | rli.output.cursorTo 0 78 | rli.output.clearLine 1 79 | # XXX: multiline hack 80 | multiline.buffer = multiline.buffer.replace /\n/g, '\uFF00' 81 | rli.emit 'line', multiline.buffer 82 | multiline.buffer = '' 83 | else 84 | multiline.enabled = not multiline.enabled 85 | rli.setPrompt multiline.initialPrompt 86 | rli.prompt true 87 | return 88 | 89 | # Store and load command history from a file 90 | addHistory = (repl, filename, maxSize) -> 91 | lastLine = null 92 | try 93 | # Get file info and at most maxSize of command history 94 | stat = fs.statSync filename 95 | size = Math.min maxSize, stat.size 96 | # Read last `size` bytes from the file 97 | readFd = fs.openSync filename, 'r' 98 | buffer = new Buffer(size) 99 | fs.readSync readFd, buffer, 0, size, stat.size - size 100 | # Set the history on the interpreter 101 | repl.rli.history = buffer.toString().split('\n').reverse() 102 | # If the history file was truncated we should pop off a potential partial line 103 | repl.rli.history.pop() if stat.size > maxSize 104 | # Shift off the final blank newline 105 | repl.rli.history.shift() if repl.rli.history[0] is '' 106 | repl.rli.historyIndex = -1 107 | lastLine = repl.rli.history[0] 108 | 109 | fd = fs.openSync filename, 'a' 110 | 111 | repl.rli.addListener 'line', (code) -> 112 | if code and code.length and code isnt '.history' and lastLine isnt code 113 | # Save the latest command in the file 114 | fs.write fd, "#{code}\n" 115 | lastLine = code 116 | 117 | repl.rli.on 'exit', -> fs.close fd 118 | 119 | # Add a command to show the history stack 120 | repl.commands['.history'] = 121 | help: 'Show command history' 122 | action: -> 123 | repl.outputStream.write "#{repl.rli.history[..].reverse().join '\n'}\n" 124 | repl.displayPrompt() 125 | 126 | module.exports = 127 | start: (opts = {}) -> 128 | [major, minor, build] = process.versions.node.split('.').map (n) -> parseInt(n) 129 | 130 | if major is 0 and minor < 8 131 | console.warn "Node 0.8.0+ required for CoffeeScript REPL" 132 | process.exit 1 133 | 134 | CoffeeScript.register() 135 | process.argv = ['coffee'].concat process.argv[2..] 136 | opts = merge replDefaults, opts 137 | repl = nodeREPL.start opts 138 | repl.on 'exit', -> repl.outputStream.write '\n' 139 | addMultilineHandler repl 140 | addHistory repl, opts.historyFile, opts.historyMaxInputSize if opts.historyFile 141 | repl 142 | -------------------------------------------------------------------------------- /src/scope.litcoffee: -------------------------------------------------------------------------------- 1 | The **Scope** class regulates lexical scoping within CoffeeScript. As you 2 | generate code, you create a tree of scopes in the same shape as the nested 3 | function bodies. Each scope knows about the variables declared within it, 4 | and has a reference to its parent enclosing scope. In this way, we know which 5 | variables are new and need to be declared with `var`, and which are shared 6 | with external scopes. 7 | 8 | Import the helpers we plan to use. 9 | 10 | {extend, last} = require './helpers' 11 | 12 | exports.Scope = class Scope 13 | 14 | The `root` is the top-level **Scope** object for a given file. 15 | 16 | @root: null 17 | 18 | Initialize a scope with its parent, for lookups up the chain, 19 | as well as a reference to the **Block** node it belongs to, which is 20 | where it should declare its variables, and a reference to the function that 21 | it belongs to. 22 | 23 | constructor: (@parent, @expressions, @method) -> 24 | @variables = [{name: 'arguments', type: 'arguments'}] 25 | @positions = {} 26 | Scope.root = this unless @parent 27 | 28 | Adds a new variable or overrides an existing one. 29 | 30 | add: (name, type, immediate) -> 31 | return @parent.add name, type, immediate if @shared and not immediate 32 | if Object::hasOwnProperty.call @positions, name 33 | @variables[@positions[name]].type = type 34 | else 35 | @positions[name] = @variables.push({name, type}) - 1 36 | 37 | When `super` is called, we need to find the name of the current method we're 38 | in, so that we know how to invoke the same method of the parent class. This 39 | can get complicated if super is being called from an inner function. 40 | `namedMethod` will walk up the scope tree until it either finds the first 41 | function object that has a name filled in, or bottoms out. 42 | 43 | namedMethod: -> 44 | return @method if @method?.name or !@parent 45 | @parent.namedMethod() 46 | 47 | Look up a variable name in lexical scope, and declare it if it does not 48 | already exist. 49 | 50 | find: (name) -> 51 | return yes if @check name 52 | @add name, 'var' 53 | no 54 | 55 | Reserve a variable name as originating from a function parameter for this 56 | scope. No `var` required for internal references. 57 | 58 | parameter: (name) -> 59 | return if @shared and @parent.check name, yes 60 | @add name, 'param' 61 | 62 | Just check to see if a variable has already been declared, without reserving, 63 | walks up to the root scope. 64 | 65 | check: (name) -> 66 | !!(@type(name) or @parent?.check(name)) 67 | 68 | Generate a temporary variable name at the given index. 69 | 70 | temporary: (name, index) -> 71 | if name.length > 1 72 | '_' + name + if index > 1 then index - 1 else '' 73 | else 74 | '_' + (index + parseInt name, 36).toString(36).replace /\d/g, 'a' 75 | 76 | Gets the type of a variable. 77 | 78 | type: (name) -> 79 | return v.type for v in @variables when v.name is name 80 | null 81 | 82 | If we need to store an intermediate result, find an available name for a 83 | compiler-generated variable. `_var`, `_var2`, and so on... 84 | 85 | freeVariable: (name, reserve=true) -> 86 | index = 0 87 | index++ while @check((temp = @temporary name, index)) 88 | @add temp, 'var', yes if reserve 89 | temp 90 | 91 | Ensure that an assignment is made at the top of this scope 92 | (or at the top-level scope, if requested). 93 | 94 | assign: (name, value) -> 95 | @add name, {value, assigned: yes}, yes 96 | @hasAssignments = yes 97 | 98 | Does this scope have any declared variables? 99 | 100 | hasDeclarations: -> 101 | !!@declaredVariables().length 102 | 103 | Return the list of variables first declared in this scope. 104 | 105 | declaredVariables: -> 106 | realVars = [] 107 | tempVars = [] 108 | for v in @variables when v.type is 'var' 109 | (if v.name.charAt(0) is '_' then tempVars else realVars).push v.name 110 | realVars.sort().concat tempVars.sort() 111 | 112 | Return the list of assignments that are supposed to be made at the top 113 | of this scope. 114 | 115 | assignedVariables: -> 116 | "#{v.name} = #{v.type.value}" for v in @variables when v.type.assigned 117 | 118 | -------------------------------------------------------------------------------- /src/sourcemap.litcoffee: -------------------------------------------------------------------------------- 1 | Source maps allow JavaScript runtimes to match running JavaScript back to 2 | the original source code that corresponds to it. This can be minified 3 | JavaScript, but in our case, we're concerned with mapping pretty-printed 4 | JavaScript back to CoffeeScript. 5 | 6 | In order to produce maps, we must keep track of positions (line number, column number) 7 | that originated every node in the syntax tree, and be able to generate a 8 | [map file](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit) 9 | — which is a compact, VLQ-encoded representation of the JSON serialization 10 | of this information — to write out alongside the generated JavaScript. 11 | 12 | 13 | LineMap 14 | ------- 15 | 16 | A **LineMap** object keeps track of information about original line and column 17 | positions for a single line of output JavaScript code. 18 | **SourceMaps** are implemented in terms of **LineMaps**. 19 | 20 | class LineMap 21 | constructor: (@line) -> 22 | @columns = [] 23 | 24 | add: (column, [sourceLine, sourceColumn], options={}) -> 25 | return if @columns[column] and options.noReplace 26 | @columns[column] = {line: @line, column, sourceLine, sourceColumn} 27 | 28 | sourceLocation: (column) -> 29 | column-- until (mapping = @columns[column]) or (column <= 0) 30 | mapping and [mapping.sourceLine, mapping.sourceColumn] 31 | 32 | 33 | SourceMap 34 | --------- 35 | 36 | Maps locations in a single generated JavaScript file back to locations in 37 | the original CoffeeScript source file. 38 | 39 | This is intentionally agnostic towards how a source map might be represented on 40 | disk. Once the compiler is ready to produce a "v3"-style source map, we can walk 41 | through the arrays of line and column buffer to produce it. 42 | 43 | class SourceMap 44 | constructor: -> 45 | @lines = [] 46 | 47 | Adds a mapping to this SourceMap. `sourceLocation` and `generatedLocation` 48 | are both `[line, column]` arrays. If `options.noReplace` is true, then if there 49 | is already a mapping for the specified `line` and `column`, this will have no 50 | effect. 51 | 52 | add: (sourceLocation, generatedLocation, options = {}) -> 53 | [line, column] = generatedLocation 54 | lineMap = (@lines[line] or= new LineMap(line)) 55 | lineMap.add column, sourceLocation, options 56 | 57 | Look up the original position of a given `line` and `column` in the generated 58 | code. 59 | 60 | sourceLocation: ([line, column]) -> 61 | line-- until (lineMap = @lines[line]) or (line <= 0) 62 | lineMap and lineMap.sourceLocation column 63 | 64 | 65 | V3 SourceMap Generation 66 | ----------------------- 67 | 68 | Builds up a V3 source map, returning the generated JSON as a string. 69 | `options.sourceRoot` may be used to specify the sourceRoot written to the source 70 | map. Also, `options.sourceFiles` and `options.generatedFile` may be passed to 71 | set "sources" and "file", respectively. 72 | 73 | generate: (options = {}, code = null) -> 74 | writingline = 0 75 | lastColumn = 0 76 | lastSourceLine = 0 77 | lastSourceColumn = 0 78 | needComma = no 79 | buffer = "" 80 | 81 | for lineMap, lineNumber in @lines when lineMap 82 | for mapping in lineMap.columns when mapping 83 | while writingline < mapping.line 84 | lastColumn = 0 85 | needComma = no 86 | buffer += ";" 87 | writingline++ 88 | 89 | Write a comma if we've already written a segment on this line. 90 | 91 | if needComma 92 | buffer += "," 93 | needComma = no 94 | 95 | Write the next segment. Segments can be 1, 4, or 5 values. If just one, then it 96 | is a generated column which doesn't match anything in the source code. 97 | 98 | The starting column in the generated source, relative to any previous recorded 99 | column for the current line: 100 | 101 | buffer += @encodeVlq mapping.column - lastColumn 102 | lastColumn = mapping.column 103 | 104 | The index into the list of sources: 105 | 106 | buffer += @encodeVlq 0 107 | 108 | The starting line in the original source, relative to the previous source line. 109 | 110 | buffer += @encodeVlq mapping.sourceLine - lastSourceLine 111 | lastSourceLine = mapping.sourceLine 112 | 113 | The starting column in the original source, relative to the previous column. 114 | 115 | buffer += @encodeVlq mapping.sourceColumn - lastSourceColumn 116 | lastSourceColumn = mapping.sourceColumn 117 | needComma = yes 118 | 119 | Produce the canonical JSON object format for a "v3" source map. 120 | 121 | v3 = 122 | version: 3 123 | file: options.generatedFile or '' 124 | sourceRoot: options.sourceRoot or '' 125 | sources: options.sourceFiles or [''] 126 | names: [] 127 | mappings: buffer 128 | 129 | v3.sourcesContent = [code] if options.inline 130 | 131 | JSON.stringify v3, null, 2 132 | 133 | 134 | Base64 VLQ Encoding 135 | ------------------- 136 | 137 | Note that SourceMap VLQ encoding is "backwards". MIDI-style VLQ encoding puts 138 | the most-significant-bit (MSB) from the original value into the MSB of the VLQ 139 | encoded value (see [Wikipedia](http://en.wikipedia.org/wiki/File:Uintvar_coding.svg)). 140 | SourceMap VLQ does things the other way around, with the least significat four 141 | bits of the original value encoded into the first byte of the VLQ encoded value. 142 | 143 | VLQ_SHIFT = 5 144 | VLQ_CONTINUATION_BIT = 1 << VLQ_SHIFT # 0010 0000 145 | VLQ_VALUE_MASK = VLQ_CONTINUATION_BIT - 1 # 0001 1111 146 | 147 | encodeVlq: (value) -> 148 | answer = '' 149 | 150 | # Least significant bit represents the sign. 151 | signBit = if value < 0 then 1 else 0 152 | 153 | # The next bits are the actual value. 154 | valueToEncode = (Math.abs(value) << 1) + signBit 155 | 156 | # Make sure we encode at least one character, even if valueToEncode is 0. 157 | while valueToEncode or not answer 158 | nextChunk = valueToEncode & VLQ_VALUE_MASK 159 | valueToEncode = valueToEncode >> VLQ_SHIFT 160 | nextChunk |= VLQ_CONTINUATION_BIT if valueToEncode 161 | answer += @encodeBase64 nextChunk 162 | 163 | answer 164 | 165 | 166 | Regular Base64 Encoding 167 | ----------------------- 168 | 169 | BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 170 | 171 | encodeBase64: (value) -> 172 | BASE64_CHARS[value] or throw new Error "Cannot Base64 encode value: #{value}" 173 | 174 | 175 | Our API for source maps is just the `SourceMap` class. 176 | 177 | module.exports = SourceMap 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /test/arrays.coffee: -------------------------------------------------------------------------------- 1 | # Array Literals 2 | # -------------- 3 | 4 | # * Array Literals 5 | # * Splats in Array Literals 6 | 7 | # TODO: add indexing and method invocation tests: [1][0] is 1, [].toString() 8 | 9 | test "trailing commas", -> 10 | trailingComma = [1, 2, 3,] 11 | ok (trailingComma[0] is 1) and (trailingComma[2] is 3) and (trailingComma.length is 3) 12 | 13 | trailingComma = [ 14 | 1, 2, 3, 15 | 4, 5, 6 16 | 7, 8, 9, 17 | ] 18 | (sum = (sum or 0) + n) for n in trailingComma 19 | 20 | a = [((x) -> x), ((x) -> x * x)] 21 | ok a.length is 2 22 | 23 | test "incorrect indentation without commas", -> 24 | result = [['a'] 25 | {b: 'c'}] 26 | ok result[0][0] is 'a' 27 | ok result[1]['b'] is 'c' 28 | 29 | 30 | # Splats in Array Literals 31 | 32 | test "array splat expansions with assignments", -> 33 | nums = [1, 2, 3] 34 | list = [a = 0, nums..., b = 4] 35 | eq 0, a 36 | eq 4, b 37 | arrayEq [0,1,2,3,4], list 38 | 39 | 40 | test "mixed shorthand objects in array lists", -> 41 | 42 | arr = [ 43 | a:1 44 | 'b' 45 | c:1 46 | ] 47 | ok arr.length is 3 48 | ok arr[2].c is 1 49 | 50 | arr = [b: 1, a: 2, 100] 51 | eq arr[1], 100 52 | 53 | arr = [a:0, b:1, (1 + 1)] 54 | eq arr[1], 2 55 | 56 | arr = [a:1, 'a', b:1, 'b'] 57 | eq arr.length, 4 58 | eq arr[2].b, 1 59 | eq arr[3], 'b' 60 | 61 | 62 | test "array splats with nested arrays", -> 63 | nonce = {} 64 | a = [nonce] 65 | list = [1, 2, a...] 66 | eq list[0], 1 67 | eq list[2], nonce 68 | 69 | a = [[nonce]] 70 | list = [1, 2, a...] 71 | arrayEq list, [1, 2, [nonce]] 72 | 73 | test "#1274: `[] = a()` compiles to `false` instead of `a()`", -> 74 | a = false 75 | fn = -> a = true 76 | [] = fn() 77 | ok a 78 | -------------------------------------------------------------------------------- /test/booleans.coffee: -------------------------------------------------------------------------------- 1 | # Boolean Literals 2 | # ---------------- 3 | 4 | # TODO: add method invocation tests: true.toString() is "true" 5 | 6 | test "#764 Booleans should be indexable", -> 7 | toString = Boolean::toString 8 | 9 | eq toString, true['toString'] 10 | eq toString, false['toString'] 11 | eq toString, yes['toString'] 12 | eq toString, no['toString'] 13 | eq toString, on['toString'] 14 | eq toString, off['toString'] 15 | 16 | eq toString, true.toString 17 | eq toString, false.toString 18 | eq toString, yes.toString 19 | eq toString, no.toString 20 | eq toString, on.toString 21 | eq toString, off.toString 22 | -------------------------------------------------------------------------------- /test/cluster.coffee: -------------------------------------------------------------------------------- 1 | # Cluster Module 2 | # --------- 3 | 4 | return if testingBrowser? 5 | 6 | cluster = require 'cluster' 7 | 8 | if cluster.isMaster 9 | test "#2737 - cluster module can spawn workers from a coffeescript process", -> 10 | cluster.once 'exit', (worker, code) -> 11 | eq code, 0 12 | 13 | cluster.fork() 14 | else 15 | process.exit 0 16 | -------------------------------------------------------------------------------- /test/compilation.coffee: -------------------------------------------------------------------------------- 1 | # Compilation 2 | # ----------- 3 | 4 | # helper to assert that a string should fail compilation 5 | cantCompile = (code) -> 6 | throws -> CoffeeScript.compile code 7 | 8 | 9 | test "ensure that carriage returns don't break compilation on Windows", -> 10 | doesNotThrow -> CoffeeScript.compile 'one\r\ntwo', bare: on 11 | 12 | test "#3089 - don't mutate passed in options to compile", -> 13 | opts = {} 14 | CoffeeScript.compile '1 + 1', opts 15 | ok !opts.scope 16 | 17 | test "--bare", -> 18 | eq -1, CoffeeScript.compile('x = y', bare: on).indexOf 'function' 19 | ok 'passed' is CoffeeScript.eval '"passed"', bare: on, filename: 'test' 20 | 21 | test "header (#1778)", -> 22 | header = "// Generated by CoffeeScript #{CoffeeScript.VERSION}\n" 23 | eq 0, CoffeeScript.compile('x = y', header: on).indexOf header 24 | 25 | test "header is disabled by default", -> 26 | header = "// Generated by CoffeeScript #{CoffeeScript.VERSION}\n" 27 | eq -1, CoffeeScript.compile('x = y').indexOf header 28 | 29 | test "multiple generated references", -> 30 | a = {b: []} 31 | a.b[true] = -> this == a.b 32 | c = 0 33 | d = [] 34 | ok a.b[0<++c<2] d... 35 | 36 | test "splat on a line by itself is invalid", -> 37 | cantCompile "x 'a'\n...\n" 38 | 39 | test "Issue 750", -> 40 | 41 | cantCompile 'f(->' 42 | 43 | cantCompile 'a = (break)' 44 | 45 | cantCompile 'a = (return 5 for item in list)' 46 | 47 | cantCompile 'a = (return 5 while condition)' 48 | 49 | cantCompile 'a = for x in y\n return 5' 50 | 51 | test "Issue #986: Unicode identifiers", -> 52 | λ = 5 53 | eq λ, 5 54 | 55 | test "don't accidentally stringify keywords", -> 56 | ok (-> this == 'this')() is false 57 | 58 | test "#1026", -> 59 | cantCompile ''' 60 | if a 61 | b 62 | else 63 | c 64 | else 65 | d 66 | ''' 67 | 68 | test "#1050", -> 69 | cantCompile "### */ ###" 70 | 71 | test "#1273: escaping quotes at the end of heredocs", -> 72 | cantCompile '"""\\"""' # """\""" 73 | cantCompile '"""\\\\\\"""' # """\\\""" 74 | 75 | test "#1106: __proto__ compilation", -> 76 | object = eq 77 | @["__proto__"] = true 78 | ok __proto__ 79 | 80 | test "reference named hasOwnProperty", -> 81 | CoffeeScript.compile 'hasOwnProperty = 0; a = 1' 82 | 83 | test "#1055: invalid keys in real (but not work-product) objects", -> 84 | cantCompile "@key: value" 85 | 86 | test "#1066: interpolated strings are not implicit functions", -> 87 | cantCompile '"int#{er}polated" arg' 88 | 89 | test "#2846: while with empty body", -> 90 | CoffeeScript.compile 'while 1 then', {sourceMap: true} 91 | 92 | test "#2944: implicit call with a regex argument", -> 93 | CoffeeScript.compile 'o[key] /regex/' 94 | 95 | test "#3001: `own` shouldn't be allowed in a `for`-`in` loop", -> 96 | cantCompile "a for own b in c" 97 | 98 | test "#2994: single-line `if` requires `then`", -> 99 | cantCompile "if b else x" 100 | -------------------------------------------------------------------------------- /test/error_messages.coffee: -------------------------------------------------------------------------------- 1 | # Error Formating 2 | # --------------- 3 | 4 | # Ensure that errors of different kinds (lexer, parser and compiler) are shown 5 | # in a consistent way. 6 | 7 | assertErrorFormat = (code, expectedErrorFormat) -> 8 | throws (-> CoffeeScript.run code), (err) -> 9 | err.colorful = no 10 | eq expectedErrorFormat, "#{err}" 11 | yes 12 | 13 | test "lexer errors formating", -> 14 | assertErrorFormat ''' 15 | normalObject = {} 16 | insideOutObject = }{ 17 | ''', 18 | ''' 19 | [stdin]:2:19: error: unmatched } 20 | insideOutObject = }{ 21 | ^ 22 | ''' 23 | 24 | test "parser error formating", -> 25 | assertErrorFormat ''' 26 | foo in bar or in baz 27 | ''', 28 | ''' 29 | [stdin]:1:15: error: unexpected in 30 | foo in bar or in baz 31 | ^^ 32 | ''' 33 | 34 | test "compiler error formatting", -> 35 | assertErrorFormat ''' 36 | evil = (foo, eval, bar) -> 37 | ''', 38 | ''' 39 | [stdin]:1:14: error: parameter name "eval" is not allowed 40 | evil = (foo, eval, bar) -> 41 | ^^^^ 42 | ''' 43 | 44 | 45 | if require? 46 | fs = require 'fs' 47 | path = require 'path' 48 | 49 | test "patchStackTrace line patching", -> 50 | err = new Error 'error' 51 | ok err.stack.match /test[\/\\]error_messages\.coffee:\d+:\d+\b/ 52 | 53 | test "#2849: compilation error in a require()d file", -> 54 | # Create a temporary file to require(). 55 | ok not fs.existsSync 'test/syntax-error.coffee' 56 | fs.writeFileSync 'test/syntax-error.coffee', 'foo in bar or in baz' 57 | 58 | try 59 | assertErrorFormat ''' 60 | require './test/syntax-error' 61 | ''', 62 | """ 63 | #{path.join __dirname, 'syntax-error.coffee'}:1:15: error: unexpected in 64 | foo in bar or in baz 65 | ^^ 66 | """ 67 | finally 68 | fs.unlink 'test/syntax-error.coffee' 69 | 70 | 71 | test "#1096: unexpected generated tokens", -> 72 | # Unexpected interpolation 73 | assertErrorFormat '{"#{key}": val}', ''' 74 | [stdin]:1:3: error: unexpected string interpolation 75 | {"#{key}": val} 76 | ^^ 77 | ''' 78 | # Implicit ends 79 | assertErrorFormat 'a:, b', ''' 80 | [stdin]:1:3: error: unexpected , 81 | a:, b 82 | ^ 83 | ''' 84 | # Explicit ends 85 | assertErrorFormat '(a:)', ''' 86 | [stdin]:1:4: error: unexpected ) 87 | (a:) 88 | ^ 89 | ''' 90 | # Unexpected end of file 91 | assertErrorFormat 'a:', ''' 92 | [stdin]:1:3: error: unexpected end of input 93 | a: 94 | ^ 95 | ''' 96 | # Unexpected implicit object 97 | assertErrorFormat ''' 98 | for i in [1]: 99 | 1 100 | ''', ''' 101 | [stdin]:1:13: error: unexpected : 102 | for i in [1]: 103 | ^ 104 | ''' 105 | 106 | test "#3325: implicit indentation errors", -> 107 | assertErrorFormat ''' 108 | i for i in a then i 109 | ''', ''' 110 | [stdin]:1:14: error: unexpected then 111 | i for i in a then i 112 | ^^^^ 113 | ''' 114 | 115 | test "explicit indentation errors", -> 116 | assertErrorFormat ''' 117 | a = b 118 | c 119 | ''', ''' 120 | [stdin]:2:1: error: unexpected indentation 121 | c 122 | ^^ 123 | ''' 124 | -------------------------------------------------------------------------------- /test/eval.coffee: -------------------------------------------------------------------------------- 1 | if vm = require? 'vm' 2 | 3 | test "CoffeeScript.eval runs in the global context by default", -> 4 | global.punctuation = '!' 5 | code = ''' 6 | global.fhqwhgads = "global superpower#{global.punctuation}" 7 | ''' 8 | result = CoffeeScript.eval code 9 | eq result, 'global superpower!' 10 | eq fhqwhgads, 'global superpower!' 11 | 12 | test "CoffeeScript.eval can run in, and modify, a Script context sandbox", -> 13 | sandbox = vm.Script.createContext() 14 | sandbox.foo = 'bar' 15 | code = ''' 16 | global.foo = 'not bar!' 17 | ''' 18 | result = CoffeeScript.eval code, {sandbox} 19 | eq result, 'not bar!' 20 | eq sandbox.foo, 'not bar!' 21 | 22 | test "CoffeeScript.eval can run in, but cannot modify, an ordinary object sandbox", -> 23 | sandbox = {foo: 'bar'} 24 | code = ''' 25 | global.foo = 'not bar!' 26 | ''' 27 | result = CoffeeScript.eval code, {sandbox} 28 | eq result, 'not bar!' 29 | eq sandbox.foo, 'bar' 30 | -------------------------------------------------------------------------------- /test/exception_handling.coffee: -------------------------------------------------------------------------------- 1 | # Exception Handling 2 | # ------------------ 3 | 4 | # shared nonce 5 | nonce = {} 6 | 7 | 8 | # Throw 9 | 10 | test "basic exception throwing", -> 11 | throws (-> throw 'error'), 'error' 12 | 13 | 14 | # Empty Try/Catch/Finally 15 | 16 | test "try can exist alone", -> 17 | try 18 | 19 | test "try/catch with empty try, empty catch", -> 20 | try 21 | # nothing 22 | catch err 23 | # nothing 24 | 25 | test "single-line try/catch with empty try, empty catch", -> 26 | try catch err 27 | 28 | test "try/finally with empty try, empty finally", -> 29 | try 30 | # nothing 31 | finally 32 | # nothing 33 | 34 | test "single-line try/finally with empty try, empty finally", -> 35 | try finally 36 | 37 | test "try/catch/finally with empty try, empty catch, empty finally", -> 38 | try 39 | catch err 40 | finally 41 | 42 | test "single-line try/catch/finally with empty try, empty catch, empty finally", -> 43 | try catch err then finally 44 | 45 | 46 | # Try/Catch/Finally as an Expression 47 | 48 | test "return the result of try when no exception is thrown", -> 49 | result = try 50 | nonce 51 | catch err 52 | undefined 53 | finally 54 | undefined 55 | eq nonce, result 56 | 57 | test "single-line result of try when no exception is thrown", -> 58 | result = try nonce catch err then undefined 59 | eq nonce, result 60 | 61 | test "return the result of catch when an exception is thrown", -> 62 | fn = -> 63 | try 64 | throw -> 65 | catch err 66 | nonce 67 | doesNotThrow fn 68 | eq nonce, fn() 69 | 70 | test "single-line result of catch when an exception is thrown", -> 71 | fn = -> 72 | try throw (->) catch err then nonce 73 | doesNotThrow fn 74 | eq nonce, fn() 75 | 76 | test "optional catch", -> 77 | fn = -> 78 | try throw -> 79 | nonce 80 | doesNotThrow fn 81 | eq nonce, fn() 82 | 83 | 84 | # Try/Catch/Finally Interaction With Other Constructs 85 | 86 | test "try/catch with empty catch as last statement in a function body", -> 87 | fn = -> 88 | try nonce 89 | catch err 90 | eq nonce, fn() 91 | 92 | 93 | # Catch leads to broken scoping: #1595 94 | 95 | test "try/catch with a reused variable name.", -> 96 | do -> 97 | try 98 | inner = 5 99 | catch inner 100 | # nothing 101 | eq typeof inner, 'undefined' 102 | 103 | 104 | # Allowed to destructure exceptions: #2580 105 | 106 | test "try/catch with destructuring the exception object", -> 107 | 108 | result = try 109 | missing.object 110 | catch {message} 111 | message 112 | 113 | eq message, 'missing is not defined' 114 | 115 | 116 | 117 | test "Try catch finally as implicit arguments", -> 118 | first = (x) -> x 119 | 120 | foo = no 121 | try 122 | first try iamwhoiam() finally foo = yes 123 | catch e 124 | eq foo, yes 125 | 126 | bar = no 127 | try 128 | first try iamwhoiam() catch e finally 129 | bar = yes 130 | catch e 131 | eq bar, yes 132 | 133 | # Catch Should Not Require Param: #2900 134 | test "parameter-less catch clause", -> 135 | try 136 | throw new Error 'failed' 137 | catch 138 | ok true 139 | 140 | try throw new Error 'failed' catch finally ok true 141 | 142 | ok try throw new Error 'failed' catch then true 143 | -------------------------------------------------------------------------------- /test/formatting.coffee: -------------------------------------------------------------------------------- 1 | # Formatting 2 | # ---------- 3 | 4 | # TODO: maybe this file should be split up into their respective sections: 5 | # operators -> operators 6 | # array literals -> array literals 7 | # string literals -> string literals 8 | # function invocations -> function invocations 9 | 10 | doesNotThrow -> CoffeeScript.compile "a = then b" 11 | 12 | test "multiple semicolon-separated statements in parentheticals", -> 13 | nonce = {} 14 | eq nonce, (1; 2; nonce) 15 | eq nonce, (-> return (1; 2; nonce))() 16 | 17 | # * Line Continuation 18 | # * Property Accesss 19 | # * Operators 20 | # * Array Literals 21 | # * Function Invocations 22 | # * String Literals 23 | 24 | # Property Access 25 | 26 | test "chained accesses split on period/newline, backwards and forwards", -> 27 | str = 'abc' 28 | result = str. 29 | split(''). 30 | reverse(). 31 | reverse(). 32 | reverse() 33 | arrayEq ['c','b','a'], result 34 | arrayEq ['c','b','a'], str. 35 | split(''). 36 | reverse(). 37 | reverse(). 38 | reverse() 39 | result = str 40 | .split('') 41 | .reverse() 42 | .reverse() 43 | .reverse() 44 | arrayEq ['c','b','a'], result 45 | arrayEq ['c','b','a'], 46 | str 47 | .split('') 48 | .reverse() 49 | .reverse() 50 | .reverse() 51 | arrayEq ['c','b','a'], 52 | str. 53 | split('') 54 | .reverse(). 55 | reverse() 56 | .reverse() 57 | 58 | # Operators 59 | 60 | test "newline suppression for operators", -> 61 | six = 62 | 1 + 63 | 2 + 64 | 3 65 | eq 6, six 66 | 67 | test "`?.` and `::` should continue lines", -> 68 | ok not ( 69 | Date 70 | :: 71 | ?.foo 72 | ) 73 | #eq Object::toString, Date?. 74 | #prototype 75 | #:: 76 | #?.foo 77 | 78 | doesNotThrow -> CoffeeScript.compile """ 79 | oh. yes 80 | oh?. true 81 | oh:: return 82 | """ 83 | 84 | doesNotThrow -> CoffeeScript.compile """ 85 | a?[b..] 86 | a?[...b] 87 | a?[b..c] 88 | """ 89 | 90 | # Array Literals 91 | 92 | test "indented array literals don't trigger whitespace rewriting", -> 93 | getArgs = -> arguments 94 | result = getArgs( 95 | [[[[[], 96 | []], 97 | [[]]]], 98 | []]) 99 | eq 1, result.length 100 | 101 | # Function Invocations 102 | 103 | doesNotThrow -> CoffeeScript.compile """ 104 | obj = then fn 1, 105 | 1: 1 106 | a: 107 | b: -> 108 | fn c, 109 | d: e 110 | f: 1 111 | """ 112 | 113 | # String Literals 114 | 115 | test "indented heredoc", -> 116 | result = ((_) -> _)( 117 | """ 118 | abc 119 | """) 120 | eq "abc", result 121 | 122 | # Chaining - all open calls are closed by property access starting a new line 123 | # * chaining after 124 | # * indented argument 125 | # * function block 126 | # * indented object 127 | # 128 | # * single line arguments 129 | # * inline function literal 130 | # * inline object literal 131 | 132 | test "chaining after outdent", -> 133 | id = (x) -> x 134 | 135 | # indented argument 136 | ff = id parseInt "ff", 137 | 16 138 | .toString() 139 | eq '255', ff 140 | 141 | # function block 142 | str = 'abc' 143 | zero = parseInt str.replace /\w/, (letter) -> 144 | 0 145 | .toString() 146 | eq '0', zero 147 | 148 | # indented object 149 | a = id id 150 | a: 1 151 | .a 152 | eq 1, a 153 | 154 | test "#1495, method call chaining", -> 155 | str = 'abc' 156 | 157 | result = str.split '' 158 | .join ', ' 159 | eq 'a, b, c', result 160 | 161 | result = str 162 | .split '' 163 | .join ', ' 164 | eq 'a, b, c', result 165 | 166 | eq 'a, b, c', (str 167 | .split '' 168 | .join ', ' 169 | ) 170 | 171 | eq 'abc', 172 | 'aaabbbccc'.replace /(\w)\1\1/g, '$1$1' 173 | .replace /([abc])\1/g, '$1' 174 | 175 | # Nested calls 176 | result = [1..3] 177 | .slice Math.max 0, 1 178 | .concat [3] 179 | arrayEq [2, 3, 3], result 180 | 181 | # Single line function arguments 182 | result = [1..6] 183 | .map (x) -> x * x 184 | .filter (x) -> x % 2 is 0 185 | .reverse() 186 | arrayEq [36, 16, 4], result 187 | 188 | # Single line implicit objects 189 | id = (x) -> x 190 | result = id a: 1 191 | .a 192 | eq 1, result 193 | 194 | # The parens are forced 195 | result = str.split(''. 196 | split '' 197 | .join '' 198 | ).join ', ' 199 | eq 'a, b, c', result 200 | 201 | # Nested blocks caused by paren unwrapping 202 | test "#1492: Nested blocks don't cause double semicolons", -> 203 | js = CoffeeScript.compile '(0;0)' 204 | eq -1, js.indexOf ';;' 205 | 206 | test "#1195 Ignore trailing semicolons (before newlines or as the last char in a program)", -> 207 | preNewline = (numSemicolons) -> 208 | """ 209 | nonce = {}; nonce2 = {} 210 | f = -> nonce#{Array(numSemicolons+1).join(';')} 211 | nonce2 212 | unless f() is nonce then throw new Error('; before linebreak should = newline') 213 | """ 214 | CoffeeScript.run(preNewline(n), bare: true) for n in [1,2,3] 215 | 216 | lastChar = '-> lastChar;' 217 | doesNotThrow -> CoffeeScript.compile lastChar, bare: true 218 | 219 | test "#1299: Disallow token misnesting", -> 220 | try 221 | CoffeeScript.compile ''' 222 | [{ 223 | ]} 224 | ''' 225 | ok no 226 | catch e 227 | eq 'unmatched ]', e.message 228 | 229 | test "#2981: Enforce initial indentation", -> 230 | try 231 | CoffeeScript.compile ' a\nb-' 232 | ok no 233 | catch e 234 | eq 'missing indentation', e.message 235 | 236 | test "'single-line' expression containing multiple lines", -> 237 | doesNotThrow -> CoffeeScript.compile """ 238 | (a, b) -> if a 239 | -a 240 | else if b 241 | then -b 242 | else null 243 | """ 244 | 245 | test "#1275: allow indentation before closing brackets", -> 246 | array = [ 247 | 1 248 | 2 249 | 3 250 | ] 251 | eq array, array 252 | do -> 253 | ( 254 | a = 1 255 | ) 256 | eq 1, a 257 | -------------------------------------------------------------------------------- /test/functions.coffee: -------------------------------------------------------------------------------- 1 | # Function Literals 2 | # ----------------- 3 | 4 | # TODO: add indexing and method invocation tests: (->)[0], (->).call() 5 | 6 | # * Function Definition 7 | # * Bound Function Definition 8 | # * Parameter List Features 9 | # * Splat Parameters 10 | # * Context (@) Parameters 11 | # * Parameter Destructuring 12 | # * Default Parameters 13 | 14 | # Function Definition 15 | 16 | x = 1 17 | y = {} 18 | y.x = -> 3 19 | ok x is 1 20 | ok typeof(y.x) is 'function' 21 | ok y.x instanceof Function 22 | ok y.x() is 3 23 | 24 | # The empty function should not cause a syntax error. 25 | -> 26 | () -> 27 | 28 | # Multiple nested function declarations mixed with implicit calls should not 29 | # cause a syntax error. 30 | (one) -> (two) -> three four, (five) -> six seven, eight, (nine) -> 31 | 32 | # with multiple single-line functions on the same line. 33 | func = (x) -> (x) -> (x) -> x 34 | ok func(1)(2)(3) is 3 35 | 36 | # Make incorrect indentation safe. 37 | func = -> 38 | obj = { 39 | key: 10 40 | } 41 | obj.key - 5 42 | eq func(), 5 43 | 44 | # Ensure that functions with the same name don't clash with helper functions. 45 | del = -> 5 46 | ok del() is 5 47 | 48 | 49 | # Bound Function Definition 50 | 51 | obj = 52 | bound: -> 53 | (=> this)() 54 | unbound: -> 55 | (-> this)() 56 | nested: -> 57 | (=> 58 | (=> 59 | (=> this)() 60 | )() 61 | )() 62 | eq obj, obj.bound() 63 | ok obj isnt obj.unbound() 64 | eq obj, obj.nested() 65 | 66 | 67 | test "even more fancy bound functions", -> 68 | obj = 69 | one: -> 70 | do => 71 | return this.two() 72 | two: -> 73 | do => 74 | do => 75 | do => 76 | return this.three 77 | three: 3 78 | 79 | eq obj.one(), 3 80 | 81 | 82 | test "self-referencing functions", -> 83 | changeMe = -> 84 | changeMe = 2 85 | 86 | changeMe() 87 | eq changeMe, 2 88 | 89 | 90 | # Parameter List Features 91 | 92 | test "splats", -> 93 | arrayEq [0, 1, 2], (((splat...) -> splat) 0, 1, 2) 94 | arrayEq [2, 3], (((_, _1, splat...) -> splat) 0, 1, 2, 3) 95 | arrayEq [0, 1], (((splat..., _, _1) -> splat) 0, 1, 2, 3) 96 | arrayEq [2], (((_, _1, splat..., _2) -> splat) 0, 1, 2, 3) 97 | 98 | test "destructured splatted parameters", -> 99 | arr = [0,1,2] 100 | splatArray = ([a...]) -> a 101 | splatArrayRest = ([a...],b...) -> arrayEq(a,b); b 102 | arrayEq splatArray(arr), arr 103 | arrayEq splatArrayRest(arr,0,1,2), arr 104 | 105 | test "@-parameters: automatically assign an argument's value to a property of the context", -> 106 | nonce = {} 107 | 108 | ((@prop) ->).call context = {}, nonce 109 | eq nonce, context.prop 110 | 111 | # allow splats along side the special argument 112 | ((splat..., @prop) ->).apply context = {}, [0, 0, nonce] 113 | eq nonce, context.prop 114 | 115 | # allow the argument itself to be a splat 116 | ((@prop...) ->).call context = {}, 0, nonce, 0 117 | eq nonce, context.prop[1] 118 | 119 | # the argument should still be able to be referenced normally 120 | eq nonce, (((@prop) -> prop).call {}, nonce) 121 | 122 | test "@-parameters and splats with constructors", -> 123 | a = {} 124 | b = {} 125 | class Klass 126 | constructor: (@first, splat..., @last) -> 127 | 128 | obj = new Klass a, 0, 0, b 129 | eq a, obj.first 130 | eq b, obj.last 131 | 132 | test "destructuring in function definition", -> 133 | (([{a: [b], c}]...) -> 134 | eq 1, b 135 | eq 2, c 136 | ) {a: [1], c: 2} 137 | 138 | test "default values", -> 139 | nonceA = {} 140 | nonceB = {} 141 | a = (_,_1,arg=nonceA) -> arg 142 | eq nonceA, a() 143 | eq nonceA, a(0) 144 | eq nonceB, a(0,0,nonceB) 145 | eq nonceA, a(0,0,undefined) 146 | eq nonceA, a(0,0,null) 147 | eq false , a(0,0,false) 148 | eq nonceB, a(undefined,undefined,nonceB,undefined) 149 | b = (_,arg=nonceA,_1,_2) -> arg 150 | eq nonceA, b() 151 | eq nonceA, b(0) 152 | eq nonceB, b(0,nonceB) 153 | eq nonceA, b(0,undefined) 154 | eq nonceA, b(0,null) 155 | eq false , b(0,false) 156 | eq nonceB, b(undefined,nonceB,undefined) 157 | c = (arg=nonceA,_,_1) -> arg 158 | eq nonceA, c() 159 | eq 0, c(0) 160 | eq nonceB, c(nonceB) 161 | eq nonceA, c(undefined) 162 | eq nonceA, c(null) 163 | eq false , c(false) 164 | eq nonceB, c(nonceB,undefined,undefined) 165 | 166 | test "default values with @-parameters", -> 167 | a = {} 168 | b = {} 169 | obj = f: (q = a, @p = b) -> q 170 | eq a, obj.f() 171 | eq b, obj.p 172 | 173 | test "default values with splatted arguments", -> 174 | withSplats = (a = 2, b..., c = 3, d = 5) -> a * (b.length + 1) * c * d 175 | eq 30, withSplats() 176 | eq 15, withSplats(1) 177 | eq 5, withSplats(1,1) 178 | eq 1, withSplats(1,1,1) 179 | eq 2, withSplats(1,1,1,1) 180 | 181 | test "#156: parameter lists with expansion", -> 182 | expandArguments = (first, ..., lastButOne, last) -> 183 | eq 1, first 184 | eq 4, lastButOne 185 | last 186 | eq 5, expandArguments 1, 2, 3, 4, 5 187 | 188 | throws (-> CoffeeScript.compile "(..., a, b...) ->"), null, "prohibit expansion and a splat" 189 | throws (-> CoffeeScript.compile "(...) ->"), null, "prohibit lone expansion" 190 | 191 | test "#156: parameter lists with expansion in array destructuring", -> 192 | expandArray = (..., [..., last]) -> 193 | last 194 | eq 3, expandArray 1, 2, 3, [1, 2, 3] 195 | 196 | test "default values with function calls", -> 197 | doesNotThrow -> CoffeeScript.compile "(x = f()) ->" 198 | 199 | test "arguments vs parameters", -> 200 | doesNotThrow -> CoffeeScript.compile "f(x) ->" 201 | f = (g) -> g() 202 | eq 5, f (x) -> 5 203 | 204 | test "#1844: bound functions in nested comprehensions causing empty var statements", -> 205 | a = ((=>) for a in [0] for b in [0]) 206 | eq 1, a.length 207 | 208 | test "#1859: inline function bodies shouldn't modify prior postfix ifs", -> 209 | list = [1, 2, 3] 210 | ok true if list.some (x) -> x is 2 211 | 212 | test "#2258: allow whitespace-style parameter lists in function definitions", -> 213 | func = ( 214 | a, b, c 215 | ) -> c 216 | eq func(1, 2, 3), 3 217 | 218 | func = ( 219 | a 220 | b 221 | c 222 | ) -> b 223 | eq func(1, 2, 3), 2 224 | 225 | test "#2621: fancy destructuring in parameter lists", -> 226 | func = ({ prop1: { key1 }, prop2: { key2, key3: [a, b, c] } }) -> 227 | eq(key2, 'key2') 228 | eq(a, 'a') 229 | 230 | func({prop1: {key1: 'key1'}, prop2: {key2: 'key2', key3: ['a', 'b', 'c']}}) 231 | 232 | test "#1435 Indented property access", -> 233 | rec = -> rec: rec 234 | 235 | eq 1, do -> 236 | rec() 237 | .rec -> 238 | rec() 239 | .rec -> 240 | rec.rec() 241 | .rec() 242 | 1 243 | -------------------------------------------------------------------------------- /test/helpers.coffee: -------------------------------------------------------------------------------- 1 | # Helpers 2 | # ------- 3 | 4 | # pull the helpers from `CoffeeScript.helpers` into local variables 5 | {starts, ends, repeat, compact, count, merge, extend, flatten, del, last, baseFileName} = CoffeeScript.helpers 6 | 7 | 8 | # `starts` 9 | 10 | test "the `starts` helper tests if a string starts with another string", -> 11 | ok starts('01234', '012') 12 | ok not starts('01234', '123') 13 | 14 | test "the `starts` helper can take an optional offset", -> 15 | ok starts('01234', '34', 3) 16 | ok not starts('01234', '01', 1) 17 | 18 | 19 | # `ends` 20 | 21 | test "the `ends` helper tests if a string ends with another string", -> 22 | ok ends('01234', '234') 23 | ok not ends('01234', '012') 24 | 25 | test "the `ends` helper can take an optional offset", -> 26 | ok ends('01234', '012', 2) 27 | ok not ends('01234', '234', 6) 28 | 29 | 30 | # `repeat` 31 | 32 | test "the `repeat` helper concatenates a given number of times", -> 33 | eq 'asdasdasd', repeat('asd', 3) 34 | 35 | test "`repeat`ing a string 0 times always returns the empty string", -> 36 | eq '', repeat('whatever', 0) 37 | 38 | 39 | # `compact` 40 | 41 | test "the `compact` helper removes falsey values from an array, preserves truthy ones", -> 42 | allValues = [1, 0, false, obj={}, [], '', ' ', -1, null, undefined, true] 43 | truthyValues = [1, obj, [], ' ', -1, true] 44 | arrayEq truthyValues, compact(allValues) 45 | 46 | 47 | # `count` 48 | 49 | test "the `count` helper counts the number of occurances of a string in another string", -> 50 | eq 1/0, count('abc', '') 51 | eq 0, count('abc', 'z') 52 | eq 1, count('abc', 'a') 53 | eq 1, count('abc', 'b') 54 | eq 2, count('abcdc', 'c') 55 | eq 2, count('abcdabcd','abc') 56 | 57 | 58 | # `merge` 59 | 60 | test "the `merge` helper makes a new object with all properties of the objects given as its arguments", -> 61 | ary = [0, 1, 2, 3, 4] 62 | obj = {} 63 | merged = merge obj, ary 64 | ok merged isnt obj 65 | ok merged isnt ary 66 | for own key, val of ary 67 | eq val, merged[key] 68 | 69 | 70 | # `extend` 71 | 72 | test "the `extend` helper performs a shallow copy", -> 73 | ary = [0, 1, 2, 3] 74 | obj = {} 75 | # should return the object being extended 76 | eq obj, extend(obj, ary) 77 | # should copy the other object's properties as well (obviously) 78 | eq 2, obj[2] 79 | 80 | 81 | # `flatten` 82 | 83 | test "the `flatten` helper flattens an array", -> 84 | success = yes 85 | (success and= typeof n is 'number') for n in flatten [0, [[[1]], 2], 3, [4]] 86 | ok success 87 | 88 | 89 | # `del` 90 | 91 | test "the `del` helper deletes a property from an object and returns the deleted value", -> 92 | obj = [0, 1, 2] 93 | eq 1, del(obj, 1) 94 | ok 1 not of obj 95 | 96 | 97 | # `last` 98 | 99 | test "the `last` helper returns the last item of an array-like object", -> 100 | ary = [0, 1, 2, 3, 4] 101 | eq 4, last(ary) 102 | 103 | test "the `last` helper allows one to specify an optional offset", -> 104 | ary = [0, 1, 2, 3, 4] 105 | eq 2, last(ary, 2) 106 | 107 | # `baseFileName` 108 | 109 | test "the `baseFileName` helper returns the file name to write to", -> 110 | ext = '.js' 111 | sourceToCompiled = 112 | '.coffee': ext 113 | 'a.coffee': 'a' + ext 114 | 'b.coffee': 'b' + ext 115 | 'coffee.coffee': 'coffee' + ext 116 | 117 | '.litcoffee': ext 118 | 'a.litcoffee': 'a' + ext 119 | 'b.litcoffee': 'b' + ext 120 | 'coffee.litcoffee': 'coffee' + ext 121 | 122 | '.lit': ext 123 | 'a.lit': 'a' + ext 124 | 'b.lit': 'b' + ext 125 | 'coffee.lit': 'coffee' + ext 126 | 127 | '.coffee.md': ext 128 | 'a.coffee.md': 'a' + ext 129 | 'b.coffee.md': 'b' + ext 130 | 'coffee.coffee.md': 'coffee' + ext 131 | 132 | for sourceFileName, expectedFileName of sourceToCompiled 133 | name = baseFileName sourceFileName, yes 134 | filename = name + ext 135 | eq filename, expectedFileName 136 | -------------------------------------------------------------------------------- /test/importing.coffee: -------------------------------------------------------------------------------- 1 | # Importing 2 | # --------- 3 | 4 | unless window? or testingBrowser? 5 | test "coffeescript modules can be imported and executed", -> 6 | 7 | magicKey = __filename 8 | magicValue = 0xFFFF 9 | 10 | if global[magicKey]? 11 | if exports? 12 | local = magicValue 13 | exports.method = -> local 14 | else 15 | global[magicKey] = {} 16 | if require?.extensions? 17 | ok require(__filename).method() is magicValue 18 | delete global[magicKey] 19 | 20 | test "javascript modules can be imported", -> 21 | magicVal = 1 22 | for module in 'import.js import2 .import2 import.extension.js import.unknownextension .coffee .coffee.md'.split ' ' 23 | ok require("./importing/#{module}").value?() is magicVal, module 24 | 25 | test "coffeescript modules can be imported", -> 26 | magicVal = 2 27 | for module in '.import.coffee import.coffee import.extension.coffee'.split ' ' 28 | ok require("./importing/#{module}").value?() is magicVal, module 29 | 30 | test "literate coffeescript modules can be imported", -> 31 | magicVal = 3 32 | # Leading space intentional to check for index.coffee.md 33 | for module in ' .import.coffee.md import.coffee.md import.litcoffee import.extension.coffee.md'.split ' ' 34 | ok require("./importing/#{module}").value?() is magicVal, module 35 | -------------------------------------------------------------------------------- /test/importing/.coffee: -------------------------------------------------------------------------------- 1 | // Required by ../importing.coffee 2 | module.exports = {value: function(){return 1;}}; 3 | -------------------------------------------------------------------------------- /test/importing/.coffee.md: -------------------------------------------------------------------------------- 1 | // Required by ../importing.coffee 2 | module.exports = {value: function(){return 1;}}; 3 | -------------------------------------------------------------------------------- /test/importing/.import.coffee: -------------------------------------------------------------------------------- 1 | # Required by ../importing.coffee 2 | module.exports = {value: -> 2} 3 | -------------------------------------------------------------------------------- /test/importing/.import.coffee.md: -------------------------------------------------------------------------------- 1 | Required by ../importing.coffee 2 | 3 | module.exports = {value: -> 3} 4 | -------------------------------------------------------------------------------- /test/importing/.import2: -------------------------------------------------------------------------------- 1 | // Required by ../importing.coffee 2 | module.exports = {value: function(){return 1;}}; 3 | -------------------------------------------------------------------------------- /test/importing/import.coffee: -------------------------------------------------------------------------------- 1 | # Required by ../importing.coffee 2 | module.exports = {value: -> 2} 3 | -------------------------------------------------------------------------------- /test/importing/import.coffee.md: -------------------------------------------------------------------------------- 1 | Required by ../importing.coffee 2 | 3 | module.exports = {value: -> 3} 4 | -------------------------------------------------------------------------------- /test/importing/import.extension.coffee: -------------------------------------------------------------------------------- 1 | # Required by ../importing.coffee 2 | module.exports = {value: -> 2} 3 | -------------------------------------------------------------------------------- /test/importing/import.extension.coffee.md: -------------------------------------------------------------------------------- 1 | Required by ../importing.coffee 2 | 3 | module.exports = {value: -> 3} 4 | -------------------------------------------------------------------------------- /test/importing/import.extension.js: -------------------------------------------------------------------------------- 1 | // Required by ../importing.coffee 2 | module.exports = {value: function(){return 1;}}; 3 | -------------------------------------------------------------------------------- /test/importing/import.js: -------------------------------------------------------------------------------- 1 | // Required by ../importing.coffee 2 | module.exports = {value: function(){return 1;}}; 3 | -------------------------------------------------------------------------------- /test/importing/import.litcoffee: -------------------------------------------------------------------------------- 1 | Required by ../importing.coffee 2 | 3 | module.exports = {value: -> 3} 4 | -------------------------------------------------------------------------------- /test/importing/import.unknownextension: -------------------------------------------------------------------------------- 1 | // Required by ../importing.coffee 2 | module.exports = {value: function(){return 1;}}; 3 | -------------------------------------------------------------------------------- /test/importing/import2: -------------------------------------------------------------------------------- 1 | // Required by ../importing.coffee 2 | module.exports = {value: function(){return 1;}}; 3 | -------------------------------------------------------------------------------- /test/importing/index.coffee.md: -------------------------------------------------------------------------------- 1 | Required by ../importing.coffee 2 | 3 | module.exports = {value: -> 3} 4 | -------------------------------------------------------------------------------- /test/interpolation.coffee: -------------------------------------------------------------------------------- 1 | # Interpolation 2 | # ------------- 3 | 4 | # * String Interpolation 5 | # * Regular Expression Interpolation 6 | 7 | # String Interpolation 8 | 9 | # TODO: refactor string interpolation tests 10 | 11 | eq 'multiline nested "interpolations" work', """multiline #{ 12 | "nested #{ 13 | ok true 14 | "\"interpolations\"" 15 | }" 16 | } work""" 17 | 18 | # Issue #923: Tricky interpolation. 19 | eq "#{ "{" }", "{" 20 | eq "#{ '#{}}' } }", '#{}} }' 21 | eq "#{"'#{ ({a: "b#{1}"}['a']) }'"}", "'b1'" 22 | 23 | # Issue #1150: String interpolation regression 24 | eq "#{'"/'}", '"/' 25 | eq "#{"/'"}", "/'" 26 | eq "#{/'"/}", '/\'"/' 27 | eq "#{"'/" + '/"' + /"'/}", '\'//"/"\'/' 28 | eq "#{"'/"}#{'/"'}#{/"'/}", '\'//"/"\'/' 29 | eq "#{6 / 2}", '3' 30 | eq "#{6 / 2}#{6 / 2}", '33' # parsed as division 31 | eq "#{6 + /2}#{6/ + 2}", '6/2}#{6/2' # parsed as a regex 32 | eq "#{6/2} 33 | #{6/2}", '3 3' # newline cannot be part of a regex, so it's division 34 | eq "#{/// "'/'"/" ///}", '/"\'\\/\'"\\/"/' # heregex, stuffed with spicy characters 35 | eq "#{/\\'/}", "/\\\\'/" 36 | 37 | hello = 'Hello' 38 | world = 'World' 39 | ok '#{hello} #{world}!' is '#{hello} #{world}!' 40 | ok "#{hello} #{world}!" is 'Hello World!' 41 | ok "[#{hello}#{world}]" is '[HelloWorld]' 42 | ok "#{hello}##{world}" is 'Hello#World' 43 | ok "Hello #{ 1 + 2 } World" is 'Hello 3 World' 44 | ok "#{hello} #{ 1 + 2 } #{world}" is "Hello 3 World" 45 | 46 | [s, t, r, i, n, g] = ['s', 't', 'r', 'i', 'n', 'g'] 47 | ok "#{s}#{t}#{r}#{i}#{n}#{g}" is 'string' 48 | ok "\#{s}\#{t}\#{r}\#{i}\#{n}\#{g}" is '#{s}#{t}#{r}#{i}#{n}#{g}' 49 | ok "\#{string}" is '#{string}' 50 | 51 | ok "\#{Escaping} first" is '#{Escaping} first' 52 | ok "Escaping \#{in} middle" is 'Escaping #{in} middle' 53 | ok "Escaping \#{last}" is 'Escaping #{last}' 54 | 55 | ok "##" is '##' 56 | ok "#{}" is '' 57 | ok "#{}A#{} #{} #{}B#{}" is 'A B' 58 | ok "\\\#{}" is '\\#{}' 59 | 60 | ok "I won ##{20} last night." is 'I won #20 last night.' 61 | ok "I won ##{'#20'} last night." is 'I won ##20 last night.' 62 | 63 | ok "#{hello + world}" is 'HelloWorld' 64 | ok "#{hello + ' ' + world + '!'}" is 'Hello World!' 65 | 66 | list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 67 | ok "values: #{list.join(', ')}, length: #{list.length}." is 'values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, length: 10.' 68 | ok "values: #{list.join ' '}" is 'values: 0 1 2 3 4 5 6 7 8 9' 69 | 70 | obj = { 71 | name: 'Joe' 72 | hi: -> "Hello #{@name}." 73 | cya: -> "Hello #{@name}.".replace('Hello','Goodbye') 74 | } 75 | ok obj.hi() is "Hello Joe." 76 | ok obj.cya() is "Goodbye Joe." 77 | 78 | ok "With #{"quotes"}" is 'With quotes' 79 | ok 'With #{"quotes"}' is 'With #{"quotes"}' 80 | 81 | ok "Where is #{obj["name"] + '?'}" is 'Where is Joe?' 82 | 83 | ok "Where is #{"the nested #{obj["name"]}"}?" is 'Where is the nested Joe?' 84 | ok "Hello #{world ? "#{hello}"}" is 'Hello World' 85 | 86 | ok "Hello #{"#{"#{obj["name"]}" + '!'}"}" is 'Hello Joe!' 87 | 88 | a = """ 89 | Hello #{ "Joe" } 90 | """ 91 | ok a is "Hello Joe" 92 | 93 | a = 1 94 | b = 2 95 | c = 3 96 | ok "#{a}#{b}#{c}" is '123' 97 | 98 | result = null 99 | stash = (str) -> result = str 100 | stash "a #{ ('aa').replace /a/g, 'b' } c" 101 | ok result is 'a bb c' 102 | 103 | foo = "hello" 104 | ok "#{foo.replace("\"", "")}" is 'hello' 105 | 106 | val = 10 107 | a = """ 108 | basic heredoc #{val} 109 | on two lines 110 | """ 111 | b = ''' 112 | basic heredoc #{val} 113 | on two lines 114 | ''' 115 | ok a is "basic heredoc 10\non two lines" 116 | ok b is "basic heredoc \#{val}\non two lines" 117 | 118 | eq 'multiline nested "interpolations" work', """multiline #{ 119 | "nested #{(-> 120 | ok yes 121 | "\"interpolations\"" 122 | )()}" 123 | } work""" 124 | 125 | 126 | # Regular Expression Interpolation 127 | 128 | # TODO: improve heregex interpolation tests 129 | 130 | test "heregex interpolation", -> 131 | eq /\\#{}\\\"/ + '', /// 132 | #{ 133 | "#{ '\\' }" # normal comment 134 | } 135 | # regex comment 136 | \#{} 137 | \\ \" 138 | /// + '' 139 | -------------------------------------------------------------------------------- /test/javascript_literals.coffee: -------------------------------------------------------------------------------- 1 | # Javascript Literals 2 | # ------------------- 3 | 4 | # TODO: refactor javascript literal tests 5 | # TODO: add indexing and method invocation tests: `[1]`[0] is 1, `function(){}`.call() 6 | 7 | eq '\\`', ` 8 | // Inline JS 9 | "\\\`" 10 | ` 11 | -------------------------------------------------------------------------------- /test/literate.litcoffee: -------------------------------------------------------------------------------- 1 | Literate CoffeeScript Test 2 | -------------------------- 3 | 4 | comment comment 5 | 6 | test "basic literate CoffeeScript parsing", -> 7 | ok yes 8 | 9 | now with a... 10 | 11 | test "broken up indentation", -> 12 | 13 | ... broken up ... 14 | 15 | do -> 16 | 17 | ... nested block. 18 | 19 | ok yes 20 | 21 | Code must be separated from text by a blank line. 22 | 23 | test "code blocks must be preceded by a blank line", -> 24 | 25 | The next line is part of the text and will not be executed. 26 | fail() 27 | 28 | ok yes 29 | 30 | Code in `backticks is not parsed` and... 31 | 32 | test "comments in indented blocks work", -> 33 | do -> 34 | do -> 35 | # Regular comment. 36 | 37 | ### 38 | Block comment. 39 | ### 40 | 41 | ok yes 42 | 43 | Regular [Markdown](http://example.com/markdown) features, like links 44 | and unordered lists, are fine: 45 | 46 | * I 47 | 48 | * Am 49 | 50 | * A 51 | 52 | * List 53 | 54 | Tabs work too: 55 | 56 | test "tabbed code", -> 57 | ok yes 58 | -------------------------------------------------------------------------------- /test/location.coffee: -------------------------------------------------------------------------------- 1 | testScript = ''' 2 | if true 3 | x = 6 4 | console.log "A console #{x + 7} log" 5 | 6 | foo = "bar" 7 | z = /// ^ (a#{foo}) /// 8 | 9 | x = () -> 10 | try 11 | console.log "foo" 12 | catch err 13 | # Rewriter will generate explicit indentation here. 14 | 15 | return null 16 | ''' 17 | 18 | test "Verify location of generated tokens", -> 19 | tokens = CoffeeScript.tokens "a = 79" 20 | 21 | eq tokens.length, 4 22 | 23 | aToken = tokens[0] 24 | eq aToken[2].first_line, 0 25 | eq aToken[2].first_column, 0 26 | eq aToken[2].last_line, 0 27 | eq aToken[2].last_column, 0 28 | 29 | equalsToken = tokens[1] 30 | eq equalsToken[2].first_line, 0 31 | eq equalsToken[2].first_column, 2 32 | eq equalsToken[2].last_line, 0 33 | eq equalsToken[2].last_column, 2 34 | 35 | numberToken = tokens[2] 36 | eq numberToken[2].first_line, 0 37 | eq numberToken[2].first_column, 4 38 | eq numberToken[2].last_line, 0 39 | eq numberToken[2].last_column, 5 40 | 41 | test "Verify location of generated tokens (with indented first line)", -> 42 | tokens = CoffeeScript.tokens " a = 83" 43 | 44 | eq tokens.length, 4 45 | [aToken, equalsToken, numberToken] = tokens 46 | 47 | eq aToken[2].first_line, 0 48 | eq aToken[2].first_column, 2 49 | eq aToken[2].last_line, 0 50 | eq aToken[2].last_column, 2 51 | 52 | eq equalsToken[2].first_line, 0 53 | eq equalsToken[2].first_column, 4 54 | eq equalsToken[2].last_line, 0 55 | eq equalsToken[2].last_column, 4 56 | 57 | eq numberToken[2].first_line, 0 58 | eq numberToken[2].first_column, 6 59 | eq numberToken[2].last_line, 0 60 | eq numberToken[2].last_column, 7 61 | 62 | test "Verify all tokens get a location", -> 63 | doesNotThrow -> 64 | tokens = CoffeeScript.tokens testScript 65 | for token in tokens 66 | ok !!token[2] 67 | -------------------------------------------------------------------------------- /test/numbers.coffee: -------------------------------------------------------------------------------- 1 | # Number Literals 2 | # --------------- 3 | 4 | # * Decimal Integer Literals 5 | # * Octal Integer Literals 6 | # * Hexadecimal Integer Literals 7 | # * Scientific Notation Integer Literals 8 | # * Scientific Notation Non-Integer Literals 9 | # * Non-Integer Literals 10 | # * Binary Integer Literals 11 | 12 | 13 | # Binary Integer Literals 14 | # Binary notation is understood as would be decimal notation. 15 | 16 | test "Parser recognises binary numbers", -> 17 | eq 4, 0b100 18 | 19 | # Decimal Integer Literals 20 | 21 | test "call methods directly on numbers", -> 22 | eq 4, 4.valueOf() 23 | eq '11', 4.toString 3 24 | 25 | eq -1, 3 -4 26 | 27 | #764: Numbers should be indexable 28 | eq Number::toString, 42['toString'] 29 | 30 | eq Number::toString, 42.toString 31 | 32 | 33 | # Non-Integer Literals 34 | 35 | # Decimal number literals. 36 | value = .25 + .75 37 | ok value is 1 38 | value = 0.0 + -.25 - -.75 + 0.0 39 | ok value is 0.5 40 | 41 | #764: Numbers should be indexable 42 | eq Number::toString, 4['toString'] 43 | eq Number::toString, 4.2['toString'] 44 | eq Number::toString, .42['toString'] 45 | eq Number::toString, (4)['toString'] 46 | 47 | eq Number::toString, 4.toString 48 | eq Number::toString, 4.2.toString 49 | eq Number::toString, .42.toString 50 | eq Number::toString, (4).toString 51 | 52 | test '#1168: leading floating point suppresses newline', -> 53 | eq 1, do -> 54 | 1 55 | .5 + 0.5 56 | 57 | test "Python-style octal literal notation '0o777'", -> 58 | eq 511, 0o777 59 | eq 1, 0o1 60 | eq 1, 0o00001 61 | eq parseInt('0777', 8), 0o777 62 | eq '777', 0o777.toString 8 63 | eq 4, 0o4.valueOf() 64 | eq Number::toString, 0o777['toString'] 65 | eq Number::toString, 0o777.toString 66 | 67 | test "#2060: Disallow uppercase radix prefixes and exponential notation", -> 68 | for char in ['b', 'o', 'x', 'e'] 69 | program = "0#{char}0" 70 | doesNotThrow -> CoffeeScript.compile program, bare: yes 71 | throws -> CoffeeScript.compile program.toUpperCase(), bare: yes 72 | 73 | test "#2224: hex literals with 0b or B or E", -> 74 | eq 176, 0x0b0 75 | eq 177, 0x0B1 76 | eq 225, 0xE1 77 | -------------------------------------------------------------------------------- /test/option_parser.coffee: -------------------------------------------------------------------------------- 1 | # Option Parser 2 | # ------------- 3 | 4 | # TODO: refactor option parser tests 5 | 6 | # Ensure that the OptionParser handles arguments correctly. 7 | return unless require? 8 | {OptionParser} = require './../lib/coffee-script/optparse' 9 | 10 | opt = new OptionParser [ 11 | ['-r', '--required [DIR]', 'desc required'] 12 | ['-o', '--optional', 'desc optional'] 13 | ['-l', '--list [FILES*]', 'desc list'] 14 | ] 15 | 16 | test "basic arguments", -> 17 | args = ['one', 'two', 'three', '-r', 'dir'] 18 | result = opt.parse args 19 | arrayEq args, result.arguments 20 | eq undefined, result.required 21 | 22 | test "boolean and parameterised options", -> 23 | result = opt.parse ['--optional', '-r', 'folder', 'one', 'two'] 24 | ok result.optional 25 | eq 'folder', result.required 26 | arrayEq ['one', 'two'], result.arguments 27 | 28 | test "list options", -> 29 | result = opt.parse ['-l', 'one.txt', '-l', 'two.txt', 'three'] 30 | arrayEq ['one.txt', 'two.txt'], result.list 31 | arrayEq ['three'], result.arguments 32 | 33 | test "-- and interesting combinations", -> 34 | result = opt.parse ['-o','-r','a','-r','b','-o','--','-a','b','--c','d'] 35 | arrayEq ['-a', 'b', '--c', 'd'], result.arguments 36 | ok result.optional 37 | eq 'b', result.required 38 | 39 | args = ['--','-o','a','-r','c','-o','--','-a','arg0','-b','arg1'] 40 | result = opt.parse args 41 | eq undefined, result.optional 42 | eq undefined, result.required 43 | arrayEq args[1..], result.arguments 44 | -------------------------------------------------------------------------------- /test/ranges.coffee: -------------------------------------------------------------------------------- 1 | # Range Literals 2 | # -------------- 3 | 4 | # TODO: add indexing and method invocation tests: [1..4][0] is 1, [0...3].toString() 5 | 6 | # shared array 7 | shared = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 8 | 9 | test "basic inclusive ranges", -> 10 | arrayEq [1, 2, 3] , [1..3] 11 | arrayEq [0, 1, 2] , [0..2] 12 | arrayEq [0, 1] , [0..1] 13 | arrayEq [0] , [0..0] 14 | arrayEq [-1] , [-1..-1] 15 | arrayEq [-1, 0] , [-1..0] 16 | arrayEq [-1, 0, 1], [-1..1] 17 | 18 | test "basic exclusive ranges", -> 19 | arrayEq [1, 2, 3] , [1...4] 20 | arrayEq [0, 1, 2] , [0...3] 21 | arrayEq [0, 1] , [0...2] 22 | arrayEq [0] , [0...1] 23 | arrayEq [-1] , [-1...0] 24 | arrayEq [-1, 0] , [-1...1] 25 | arrayEq [-1, 0, 1], [-1...2] 26 | 27 | arrayEq [], [1...1] 28 | arrayEq [], [0...0] 29 | arrayEq [], [-1...-1] 30 | 31 | test "downward ranges", -> 32 | arrayEq shared, [9..0].reverse() 33 | arrayEq [5, 4, 3, 2] , [5..2] 34 | arrayEq [2, 1, 0, -1], [2..-1] 35 | 36 | arrayEq [3, 2, 1] , [3..1] 37 | arrayEq [2, 1, 0] , [2..0] 38 | arrayEq [1, 0] , [1..0] 39 | arrayEq [0] , [0..0] 40 | arrayEq [-1] , [-1..-1] 41 | arrayEq [0, -1] , [0..-1] 42 | arrayEq [1, 0, -1] , [1..-1] 43 | arrayEq [0, -1, -2], [0..-2] 44 | 45 | arrayEq [4, 3, 2], [4...1] 46 | arrayEq [3, 2, 1], [3...0] 47 | arrayEq [2, 1] , [2...0] 48 | arrayEq [1] , [1...0] 49 | arrayEq [] , [0...0] 50 | arrayEq [] , [-1...-1] 51 | arrayEq [0] , [0...-1] 52 | arrayEq [0, -1] , [0...-2] 53 | arrayEq [1, 0] , [1...-1] 54 | arrayEq [2, 1, 0], [2...-1] 55 | 56 | test "ranges with variables as enpoints", -> 57 | [a, b] = [1, 3] 58 | arrayEq [1, 2, 3], [a..b] 59 | arrayEq [1, 2] , [a...b] 60 | b = -2 61 | arrayEq [1, 0, -1, -2], [a..b] 62 | arrayEq [1, 0, -1] , [a...b] 63 | 64 | test "ranges with expressions as endpoints", -> 65 | [a, b] = [1, 3] 66 | arrayEq [2, 3, 4, 5, 6], [(a+1)..2*b] 67 | arrayEq [2, 3, 4, 5] , [(a+1)...2*b] 68 | 69 | test "large ranges are generated with looping constructs", -> 70 | down = [99..0] 71 | eq 100, (len = down.length) 72 | eq 0, down[len - 1] 73 | 74 | up = [0...100] 75 | eq 100, (len = up.length) 76 | eq 99, up[len - 1] 77 | 78 | test "#1012 slices with arguments object", -> 79 | expected = [0..9] 80 | argsAtStart = (-> [arguments[0]..9]) 0 81 | arrayEq expected, argsAtStart 82 | argsAtEnd = (-> [0..arguments[0]]) 9 83 | arrayEq expected, argsAtEnd 84 | argsAtBoth = (-> [arguments[0]..arguments[1]]) 0, 9 85 | arrayEq expected, argsAtBoth 86 | 87 | test "#1409: creating large ranges outside of a function body", -> 88 | CoffeeScript.eval '[0..100]' 89 | -------------------------------------------------------------------------------- /test/regexps.coffee: -------------------------------------------------------------------------------- 1 | # Regular Expression Literals 2 | # --------------------------- 3 | 4 | # TODO: add method invocation tests: /regex/.toString() 5 | 6 | # * Regexen 7 | # * Heregexen 8 | 9 | test "basic regular expression literals", -> 10 | ok 'a'.match(/a/) 11 | ok 'a'.match /a/ 12 | ok 'a'.match(/a/g) 13 | ok 'a'.match /a/g 14 | 15 | test "division is not confused for a regular expression", -> 16 | eq 2, 4 / 2 / 1 17 | 18 | a = 4 19 | b = 2 20 | g = 1 21 | eq 2, a / b/g 22 | 23 | a = 10 24 | b = a /= 4 / 2 25 | eq a, 5 26 | 27 | obj = method: -> 2 28 | two = 2 29 | eq 2, (obj.method()/two + obj.method()/two) 30 | 31 | i = 1 32 | eq 2, (4)/2/i 33 | eq 1, i/i/i 34 | 35 | test "#764: regular expressions should be indexable", -> 36 | eq /0/['source'], ///#{0}///['source'] 37 | 38 | test "#584: slashes are allowed unescaped in character classes", -> 39 | ok /^a\/[/]b$/.test 'a//b' 40 | 41 | test "#1724: regular expressions beginning with `*`", -> 42 | throws -> CoffeeScript.compile '/*/' 43 | 44 | 45 | # Heregexe(n|s) 46 | 47 | test "a heregex will ignore whitespace and comments", -> 48 | eq /^I'm\x20+[a]\s+Heregex?\/\/\//gim + '', /// 49 | ^ I'm \x20+ [a] \s+ 50 | Heregex? / // # or not 51 | ///gim + '' 52 | 53 | test "an empty heregex will compile to an empty, non-capturing group", -> 54 | eq /(?:)/ + '', /// /// + '' 55 | 56 | test "#1724: regular expressions beginning with `*`", -> 57 | throws -> CoffeeScript.compile '/// * ///' 58 | -------------------------------------------------------------------------------- /test/repl.coffee: -------------------------------------------------------------------------------- 1 | return if global.testingBrowser 2 | 3 | fs = require 'fs' 4 | 5 | # REPL 6 | # ---- 7 | Stream = require 'stream' 8 | 9 | class MockInputStream extends Stream 10 | constructor: -> 11 | @readable = true 12 | 13 | resume: -> 14 | 15 | emitLine: (val) -> 16 | @emit 'data', new Buffer("#{val}\n") 17 | 18 | class MockOutputStream extends Stream 19 | constructor: -> 20 | @writable = true 21 | @written = [] 22 | 23 | write: (data) -> 24 | #console.log 'output write', arguments 25 | @written.push data 26 | 27 | lastWrite: (fromEnd = -1) -> 28 | @written[@written.length - 1 + fromEnd].replace /\n$/, '' 29 | 30 | # Create a dummy history file 31 | historyFile = '.coffee_history_test' 32 | fs.writeFileSync historyFile, '1 + 2\n' 33 | 34 | testRepl = (desc, fn) -> 35 | input = new MockInputStream 36 | output = new MockOutputStream 37 | repl = Repl.start {input, output, historyFile} 38 | test desc, -> fn input, output, repl 39 | 40 | ctrlV = { ctrl: true, name: 'v'} 41 | 42 | 43 | testRepl 'reads history file', (input, output, repl) -> 44 | input.emitLine repl.rli.history[0] 45 | eq '3', output.lastWrite() 46 | 47 | testRepl "starts with coffee prompt", (input, output) -> 48 | eq 'coffee> ', output.lastWrite(0) 49 | 50 | testRepl "writes eval to output", (input, output) -> 51 | input.emitLine '1+1' 52 | eq '2', output.lastWrite() 53 | 54 | testRepl "comments are ignored", (input, output) -> 55 | input.emitLine '1 + 1 #foo' 56 | eq '2', output.lastWrite() 57 | 58 | testRepl "output in inspect mode", (input, output) -> 59 | input.emitLine '"1 + 1\\n"' 60 | eq "'1 + 1\\n'", output.lastWrite() 61 | 62 | testRepl "variables are saved", (input, output) -> 63 | input.emitLine "foo = 'foo'" 64 | input.emitLine 'foobar = "#{foo}bar"' 65 | eq "'foobar'", output.lastWrite() 66 | 67 | testRepl "empty command evaluates to undefined", (input, output) -> 68 | input.emitLine '' 69 | eq 'undefined', output.lastWrite() 70 | 71 | testRepl "ctrl-v toggles multiline prompt", (input, output) -> 72 | input.emit 'keypress', null, ctrlV 73 | eq '------> ', output.lastWrite(0) 74 | input.emit 'keypress', null, ctrlV 75 | eq 'coffee> ', output.lastWrite(0) 76 | 77 | testRepl "multiline continuation changes prompt", (input, output) -> 78 | input.emit 'keypress', null, ctrlV 79 | input.emitLine '' 80 | eq '....... ', output.lastWrite(0) 81 | 82 | testRepl "evaluates multiline", (input, output) -> 83 | # Stubs. Could assert on their use. 84 | output.cursorTo = (pos) -> 85 | output.clearLine = -> 86 | 87 | input.emit 'keypress', null, ctrlV 88 | input.emitLine 'do ->' 89 | input.emitLine ' 1 + 1' 90 | input.emit 'keypress', null, ctrlV 91 | eq '2', output.lastWrite() 92 | 93 | testRepl "variables in scope are preserved", (input, output) -> 94 | input.emitLine 'a = 1' 95 | input.emitLine 'do -> a = 2' 96 | input.emitLine 'a' 97 | eq '2', output.lastWrite() 98 | 99 | testRepl "existential assignment of previously declared variable", (input, output) -> 100 | input.emitLine 'a = null' 101 | input.emitLine 'a ?= 42' 102 | eq '42', output.lastWrite() 103 | 104 | testRepl "keeps running after runtime error", (input, output) -> 105 | input.emitLine 'a = b' 106 | eq 0, output.lastWrite().indexOf 'ReferenceError: b is not defined' 107 | input.emitLine 'a' 108 | eq 'undefined', output.lastWrite() 109 | 110 | process.on 'exit', -> 111 | fs.unlinkSync historyFile 112 | -------------------------------------------------------------------------------- /test/scope.coffee: -------------------------------------------------------------------------------- 1 | # Scope 2 | # ----- 3 | 4 | # * Variable Safety 5 | # * Variable Shadowing 6 | # * Auto-closure (`do`) 7 | # * Global Scope Leaks 8 | 9 | test "reference `arguments` inside of functions", -> 10 | sumOfArgs = -> 11 | sum = (a,b) -> a + b 12 | sum = 0 13 | sum += num for num in arguments 14 | sum 15 | eq 10, sumOfArgs(0, 1, 2, 3, 4) 16 | 17 | test "assignment to an Object.prototype-named variable should not leak to outer scope", -> 18 | # FIXME: fails on IE 19 | (-> 20 | constructor = 'word' 21 | )() 22 | ok constructor isnt 'word' 23 | 24 | test "siblings of splat parameters shouldn't leak to surrounding scope", -> 25 | x = 10 26 | oops = (x, args...) -> 27 | oops(20, 1, 2, 3) 28 | eq x, 10 29 | 30 | test "catch statements should introduce their argument to scope", -> 31 | try throw '' 32 | catch e 33 | do -> e = 5 34 | eq 5, e 35 | 36 | class Array then slice: fail # needs to be global 37 | class Object then hasOwnProperty: fail 38 | test "#1973: redefining Array/Object constructors shouldn't confuse __X helpers", -> 39 | arr = [1..4] 40 | arrayEq [3, 4], arr[2..] 41 | obj = {arr} 42 | for own k of obj 43 | eq arr, obj[k] 44 | 45 | test "#2255: global leak with splatted @-params", -> 46 | ok not x? 47 | arrayEq [0], ((@x...) -> @x).call {}, 0 48 | ok not x? 49 | 50 | test "#1183: super + fat arrows", -> 51 | dolater = (cb) -> cb() 52 | 53 | class A 54 | constructor: -> 55 | @_i = 0 56 | foo : (cb) -> 57 | dolater => 58 | @_i += 1 59 | cb() 60 | 61 | class B extends A 62 | constructor : -> 63 | super 64 | foo : (cb) -> 65 | dolater => 66 | dolater => 67 | @_i += 2 68 | super cb 69 | 70 | b = new B 71 | b.foo => eq b._i, 3 72 | 73 | test "#1183: super + wrap", -> 74 | class A 75 | m : -> 10 76 | 77 | class B extends A 78 | constructor : -> super 79 | 80 | B::m = -> r = try super() 81 | 82 | eq (new B).m(), 10 83 | 84 | test "#1183: super + closures", -> 85 | class A 86 | constructor: -> 87 | @i = 10 88 | foo : -> @i 89 | 90 | class B extends A 91 | foo : -> 92 | ret = switch 1 93 | when 0 then 0 94 | when 1 then super() 95 | ret 96 | eq (new B).foo(), 10 97 | 98 | test "#2331: bound super regression", -> 99 | class A 100 | @value = 'A' 101 | method: -> @constructor.value 102 | 103 | class B extends A 104 | method: => super 105 | 106 | eq (new B).method(), 'A' 107 | 108 | test "#3259: leak with @-params within destructured parameters", -> 109 | fn = ({@foo}, [@bar], [{@baz}]) -> 110 | foo = bar = baz = false 111 | 112 | fn.call {}, {foo: 'foo'}, ['bar'], [{baz: 'baz'}] 113 | 114 | eq 'undefined', typeof foo 115 | eq 'undefined', typeof bar 116 | eq 'undefined', typeof baz -------------------------------------------------------------------------------- /test/slicing_and_splicing.coffee: -------------------------------------------------------------------------------- 1 | # Slicing and Splicing 2 | # -------------------- 3 | 4 | # * Slicing 5 | # * Splicing 6 | 7 | # shared array 8 | shared = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 9 | 10 | # Slicing 11 | 12 | test "basic slicing", -> 13 | arrayEq [7, 8, 9] , shared[7..9] 14 | arrayEq [2, 3] , shared[2...4] 15 | arrayEq [2, 3, 4, 5], shared[2...6] 16 | 17 | test "slicing with variables as endpoints", -> 18 | [a, b] = [1, 4] 19 | arrayEq [1, 2, 3, 4], shared[a..b] 20 | arrayEq [1, 2, 3] , shared[a...b] 21 | 22 | test "slicing with expressions as endpoints", -> 23 | [a, b] = [1, 3] 24 | arrayEq [2, 3, 4, 5, 6], shared[(a+1)..2*b] 25 | arrayEq [2, 3, 4, 5] , shared[a+1...(2*b)] 26 | 27 | test "unbounded slicing", -> 28 | arrayEq [7, 8, 9] , shared[7..] 29 | arrayEq [8, 9] , shared[-2..] 30 | arrayEq [9] , shared[-1...] 31 | arrayEq [0, 1, 2] , shared[...3] 32 | arrayEq [0, 1, 2, 3], shared[..-7] 33 | 34 | arrayEq shared , shared[..-1] 35 | arrayEq shared[0..8], shared[...-1] 36 | 37 | for a in [-shared.length..shared.length] 38 | arrayEq shared[a..] , shared[a...] 39 | for a in [-shared.length+1...shared.length] 40 | arrayEq shared[..a][...-1] , shared[...a] 41 | 42 | arrayEq [1, 2, 3], [1, 2, 3][..] 43 | 44 | test "#930, #835, #831, #746 #624: inclusive slices to -1 should slice to end", -> 45 | arrayEq shared, shared[0..-1] 46 | arrayEq shared, shared[..-1] 47 | arrayEq shared.slice(1,shared.length), shared[1..-1] 48 | 49 | test "string slicing", -> 50 | str = "abcdefghijklmnopqrstuvwxyz" 51 | ok str[1...1] is "" 52 | ok str[1..1] is "b" 53 | ok str[1...5] is "bcde" 54 | ok str[0..4] is "abcde" 55 | ok str[-5..] is "vwxyz" 56 | 57 | test "#1722: operator precedence in unbounded slice compilation", -> 58 | list = [0..9] 59 | n = 2 # some truthy number in `list` 60 | arrayEq [0..n], list[..n] 61 | arrayEq [0..n], list[..n or 0] 62 | arrayEq [0..n], list[..if n then n else 0] 63 | 64 | test "#2349: inclusive slicing to numeric strings", -> 65 | arrayEq [0, 1], [0..10][.."1"] 66 | 67 | 68 | # Splicing 69 | 70 | test "basic splicing", -> 71 | ary = [0..9] 72 | ary[5..9] = [0, 0, 0] 73 | arrayEq [0, 1, 2, 3, 4, 0, 0, 0], ary 74 | 75 | ary = [0..9] 76 | ary[2...8] = [] 77 | arrayEq [0, 1, 8, 9], ary 78 | 79 | test "unbounded splicing", -> 80 | ary = [0..9] 81 | ary[3..] = [9, 8, 7] 82 | arrayEq [0, 1, 2, 9, 8, 7]. ary 83 | 84 | ary[...3] = [7, 8, 9] 85 | arrayEq [7, 8, 9, 9, 8, 7], ary 86 | 87 | ary[..] = [1, 2, 3] 88 | arrayEq [1, 2, 3], ary 89 | 90 | test "splicing with variables as endpoints", -> 91 | [a, b] = [1, 8] 92 | 93 | ary = [0..9] 94 | ary[a..b] = [2, 3] 95 | arrayEq [0, 2, 3, 9], ary 96 | 97 | ary = [0..9] 98 | ary[a...b] = [5] 99 | arrayEq [0, 5, 8, 9], ary 100 | 101 | test "splicing with expressions as endpoints", -> 102 | [a, b] = [1, 3] 103 | 104 | ary = [0..9] 105 | ary[ a+1 .. 2*b+1 ] = [4] 106 | arrayEq [0, 1, 4, 8, 9], ary 107 | 108 | ary = [0..9] 109 | ary[a+1...2*b+1] = [4] 110 | arrayEq [0, 1, 4, 7, 8, 9], ary 111 | 112 | test "splicing to the end, against a one-time function", -> 113 | ary = null 114 | fn = -> 115 | if ary 116 | throw 'err' 117 | else 118 | ary = [1, 2, 3] 119 | 120 | fn()[0..] = 1 121 | 122 | arrayEq ary, [1] 123 | 124 | test "the return value of a splice literal should be the RHS", -> 125 | ary = [0, 0, 0] 126 | eq (ary[0..1] = 2), 2 127 | 128 | ary = [0, 0, 0] 129 | eq (ary[0..] = 3), 3 130 | 131 | arrayEq [ary[0..0] = 0], [0] 132 | 133 | test "#1723: operator precedence in unbounded splice compilation", -> 134 | n = 4 # some truthy number in `list` 135 | 136 | list = [0..9] 137 | list[..n] = n 138 | arrayEq [n..9], list 139 | 140 | list = [0..9] 141 | list[..n or 0] = n 142 | arrayEq [n..9], list 143 | 144 | list = [0..9] 145 | list[..if n then n else 0] = n 146 | arrayEq [n..9], list 147 | 148 | test "#2953: methods on endpoints in assignment from array splice literal", -> 149 | list = [0..9] 150 | 151 | Number.prototype.same = -> this 152 | list[1.same()...9.same()] = 5 153 | delete Number.prototype.same 154 | 155 | arrayEq [0, 5, 9], list 156 | -------------------------------------------------------------------------------- /test/soaks.coffee: -------------------------------------------------------------------------------- 1 | # Soaks 2 | # ----- 3 | 4 | # * Soaked Property Access 5 | # * Soaked Method Invocation 6 | # * Soaked Function Invocation 7 | 8 | 9 | # Soaked Property Access 10 | 11 | test "soaked property access", -> 12 | nonce = {} 13 | obj = a: b: nonce 14 | eq nonce , obj?.a.b 15 | eq nonce , obj?['a'].b 16 | eq nonce , obj.a?.b 17 | eq nonce , obj?.a?['b'] 18 | eq undefined, obj?.a?.non?.existent?.property 19 | 20 | test "soaked property access caches method calls", -> 21 | nonce ={} 22 | obj = fn: -> a: nonce 23 | eq nonce , obj.fn()?.a 24 | eq undefined, obj.fn()?.b 25 | 26 | test "soaked property access caching", -> 27 | nonce = {} 28 | counter = 0 29 | fn = -> 30 | counter++ 31 | 'self' 32 | obj = 33 | self: -> @ 34 | prop: nonce 35 | eq nonce, obj[fn()]()[fn()]()[fn()]()?.prop 36 | eq 3, counter 37 | 38 | test "method calls on soaked methods", -> 39 | nonce = {} 40 | obj = null 41 | eq undefined, obj?.a().b() 42 | obj = a: -> b: -> nonce 43 | eq nonce , obj?.a().b() 44 | 45 | test "postfix existential operator mixes well with soaked property accesses", -> 46 | eq false, nonexistent?.property? 47 | 48 | test "function invocation with soaked property access", -> 49 | id = (_) -> _ 50 | eq undefined, id nonexistent?.method() 51 | 52 | test "if-to-ternary should safely parenthesize soaked property accesses", -> 53 | ok (if nonexistent?.property then false else true) 54 | 55 | test "#726", -> 56 | # TODO: check this test, looks like it's not really testing anything 57 | eq undefined, nonexistent?[Date()] 58 | 59 | test "#756", -> 60 | # TODO: improve this test 61 | a = null 62 | ok isNaN a?.b.c + 1 63 | eq undefined, a?.b.c += 1 64 | eq undefined, ++a?.b.c 65 | eq undefined, delete a?.b.c 66 | 67 | test "operations on soaked properties", -> 68 | # TODO: improve this test 69 | a = b: {c: 0} 70 | eq 1, a?.b.c + 1 71 | eq 1, a?.b.c += 1 72 | eq 2, ++a?.b.c 73 | eq yes, delete a?.b.c 74 | 75 | 76 | # Soaked Method Invocation 77 | 78 | test "soaked method invocation", -> 79 | nonce = {} 80 | counter = 0 81 | obj = 82 | self: -> @ 83 | increment: -> counter++; @ 84 | eq obj , obj.self?() 85 | eq undefined, obj.method?() 86 | eq nonce , obj.self?().property = nonce 87 | eq undefined, obj.method?().property = nonce 88 | eq obj , obj.increment().increment().self?() 89 | eq 2 , counter 90 | 91 | test "#733", -> 92 | a = b: {c: null} 93 | eq a.b?.c?(), undefined 94 | a.b?.c or= (it) -> it 95 | eq a.b?.c?(1), 1 96 | eq a.b?.c?([2, 3]...), 2 97 | 98 | 99 | # Soaked Function Invocation 100 | 101 | test "soaked function invocation", -> 102 | nonce = {} 103 | id = (_) -> _ 104 | eq nonce , id?(nonce) 105 | eq nonce , (id? nonce) 106 | eq undefined, nonexistent?(nonce) 107 | eq undefined, (nonexistent? nonce) 108 | 109 | test "soaked function invocation with generated functions", -> 110 | nonce = {} 111 | id = (_) -> _ 112 | maybe = (fn, arg) -> if typeof fn is 'function' then () -> fn(arg) 113 | eq maybe(id, nonce)?(), nonce 114 | eq (maybe id, nonce)?(), nonce 115 | eq (maybe false, nonce)?(), undefined 116 | 117 | test "soaked constructor invocation", -> 118 | eq 42 , +new Number? 42 119 | eq undefined, new Other? 42 120 | 121 | test "soaked constructor invocations with caching and property access", -> 122 | semaphore = 0 123 | nonce = {} 124 | class C 125 | constructor: -> 126 | ok false if semaphore 127 | semaphore++ 128 | prop: nonce 129 | eq nonce, (new C())?.prop 130 | eq 1, semaphore 131 | 132 | test "soaked function invocation safe on non-functions", -> 133 | eq undefined, (0)?(1) 134 | eq undefined, (0)? 1, 2 135 | -------------------------------------------------------------------------------- /test/sourcemap.coffee: -------------------------------------------------------------------------------- 1 | return if global.testingBrowser 2 | 3 | SourceMap = require '../src/sourcemap' 4 | 5 | vlqEncodedValues = [ 6 | [1, "C"], 7 | [-1, "D"], 8 | [2, "E"], 9 | [-2, "F"], 10 | [0, "A"], 11 | [16, "gB"], 12 | [948, "o7B"] 13 | ] 14 | 15 | test "encodeVlq tests", -> 16 | for pair in vlqEncodedValues 17 | eq ((new SourceMap).encodeVlq pair[0]), pair[1] 18 | 19 | eqJson = (a, b) -> 20 | eq (JSON.stringify JSON.parse a), (JSON.stringify JSON.parse b) 21 | 22 | test "SourceMap tests", -> 23 | map = new SourceMap 24 | map.add [0, 0], [0, 0] 25 | map.add [1, 5], [2, 4] 26 | map.add [1, 6], [2, 7] 27 | map.add [1, 9], [2, 8] 28 | map.add [3, 0], [3, 4] 29 | 30 | testWithFilenames = map.generate { 31 | sourceRoot: "", 32 | sourceFiles: ["source.coffee"], 33 | generatedFile: "source.js"} 34 | eqJson testWithFilenames, '{"version":3,"file":"source.js","sourceRoot":"","sources":["source.coffee"],"names":[],"mappings":"AAAA;;IACK,GAAC,CAAG;IAET"}' 35 | eqJson map.generate(), '{"version":3,"file":"","sourceRoot":"","sources":[""],"names":[],"mappings":"AAAA;;IACK,GAAC,CAAG;IAET"}' 36 | 37 | # Look up a generated column - should get back the original source position. 38 | arrayEq map.sourceLocation([2,8]), [1,9] 39 | 40 | # Look up a point futher along on the same line - should get back the same source position. 41 | arrayEq map.sourceLocation([2,10]), [1,9] 42 | -------------------------------------------------------------------------------- /test/strict.coffee: -------------------------------------------------------------------------------- 1 | # Strict Early Errors 2 | # ------------------- 3 | 4 | # The following are prohibited under ES5's `strict` mode 5 | # * `Octal Integer Literals` 6 | # * `Octal Escape Sequences` 7 | # * duplicate property definitions in `Object Literal`s 8 | # * duplicate formal parameter 9 | # * `delete` operand is a variable 10 | # * `delete` operand is a parameter 11 | # * `delete` operand is `undefined` 12 | # * `Future Reserved Word`s as identifiers: implements, interface, let, package, private, protected, public, static, yield 13 | # * `eval` or `arguments` as `catch` identifier 14 | # * `eval` or `arguments` as formal parameter 15 | # * `eval` or `arguments` as function declaration identifier 16 | # * `eval` or `arguments` as LHS of assignment 17 | # * `eval` or `arguments` as the operand of a post/pre-fix inc/dec-rement expression 18 | 19 | # helper to assert that code complies with strict prohibitions 20 | strict = (code, msg) -> 21 | throws (-> CoffeeScript.compile code), null, msg ? code 22 | strictOk = (code, msg) -> 23 | doesNotThrow (-> CoffeeScript.compile code), msg ? code 24 | 25 | 26 | test "octal integer literals prohibited", -> 27 | strict '01' 28 | strict '07777' 29 | # decimals with a leading '0' are also prohibited 30 | strict '09' 31 | strict '079' 32 | strictOk '`01`' 33 | 34 | test "octal escape sequences prohibited", -> 35 | strict '"\\1"' 36 | strict '"\\7"' 37 | strict '"\\001"' 38 | strict '"\\777"' 39 | strict '"_\\1"' 40 | strict '"\\1_"' 41 | strict '"_\\1_"' 42 | strict '"\\\\\\1"' 43 | strictOk '"\\0"' 44 | eq "\x00", "\0" 45 | strictOk '"\\08"' 46 | eq "\x008", "\08" 47 | strictOk '"\\0\\8"' 48 | eq "\x008", "\0\8" 49 | strictOk '"\\8"' 50 | eq "8", "\8" 51 | strictOk '"\\\\1"' 52 | eq "\\" + "1", "\\1" 53 | strictOk '"\\\\\\\\1"' 54 | eq "\\\\" + "1", "\\\\1" 55 | strictOk "`'\\1'`" 56 | eq "\\" + "1", `"\\1"` 57 | 58 | test "duplicate formal parameters are prohibited", -> 59 | nonce = {} 60 | # a Param can be an Identifier, ThisProperty( @-param ), Array, or Object 61 | # a Param can also be a splat (...) or an assignment (param=value) 62 | # the following function expressions should throw errors 63 | strict '(_,_)->', 'param, param' 64 | strict '(_,@_)->', 'param, @param' 65 | strict '(_,_...)->', 'param, param...' 66 | strict '(@_,_...)->', '@param, param...' 67 | strict '(_,_ = true)->', 'param, param=' 68 | strict '(@_,@_)->', 'two @params' 69 | strict '(_,@_ = true)->', 'param, @param=' 70 | strict '(_,{_})->', 'param, {param}' 71 | strict '(@_,{_})->', '@param, {param}' 72 | strict '({_,_})->', '{param, param}' 73 | strict '({_,@_})->', '{param, @param}' 74 | strict '(_,[_])->', 'param, [param]' 75 | strict '([_,_])->', '[param, param]' 76 | strict '([_,@_])->', '[param, @param]' 77 | strict '(_,[_]=true)->', 'param, [param]=' 78 | strict '(_,[@_,{_}])->', 'param, [@param, {param}]' 79 | strict '(_,[_,{@_}])->', 'param, [param, {@param}]' 80 | strict '(_,[_,{_}])->', 'param, [param, {param}]' 81 | strict '(_,[_,{__}])->', 'param, [param, {param2}]' 82 | strict '(_,[__,{_}])->', 'param, [param2, {param}]' 83 | strict '(__,[_,{_}])->', 'param, [param2, {param2}]' 84 | strict '(0:a,1:a)->', '0:param,1:param' 85 | strict '({0:a,1:a})->', '{0:param,1:param}' 86 | # the following function expressions should **not** throw errors 87 | strictOk '({},_arg)->' 88 | strictOk '({},{})->' 89 | strictOk '([]...,_arg)->' 90 | strictOk '({}...,_arg)->' 91 | strictOk '({}...,[],_arg)->' 92 | strictOk '([]...,{},_arg)->' 93 | strictOk '(@case,_case)->' 94 | strictOk '(@case,_case...)->' 95 | strictOk '(@case...,_case)->' 96 | strictOk '(_case,@case)->' 97 | strictOk '(_case,@case...)->' 98 | strictOk '(a:a)->' 99 | strictOk '(a:a,a:b)->' 100 | 101 | test "`delete` operand restrictions", -> 102 | strict 'a = 1; delete a' 103 | strictOk 'delete a' #noop 104 | strict '(a) -> delete a' 105 | strict '(@a) -> delete a' 106 | strict '(a...) -> delete a' 107 | strict '(a = 1) -> delete a' 108 | strict '([a]) -> delete a' 109 | strict '({a}) -> delete a' 110 | 111 | test "`Future Reserved Word`s, `eval` and `arguments` restrictions", -> 112 | 113 | access = (keyword, check = strict) -> 114 | check "#{keyword}.a = 1" 115 | check "#{keyword}[0] = 1" 116 | assign = (keyword, check = strict) -> 117 | check "#{keyword} = 1" 118 | check "#{keyword} += 1" 119 | check "#{keyword} -= 1" 120 | check "#{keyword} *= 1" 121 | check "#{keyword} /= 1" 122 | check "#{keyword} ?= 1" 123 | check "{keyword}++" 124 | check "++{keyword}" 125 | check "{keyword}--" 126 | check "--{keyword}" 127 | destruct = (keyword, check = strict) -> 128 | check "{#{keyword}}" 129 | check "o = {#{keyword}}" 130 | invoke = (keyword, check = strict) -> 131 | check "#{keyword} yes" 132 | check "do #{keyword}" 133 | fnDecl = (keyword, check = strict) -> 134 | check "class #{keyword}" 135 | param = (keyword, check = strict) -> 136 | check "(#{keyword}) ->" 137 | check "({#{keyword}}) ->" 138 | prop = (keyword, check = strict) -> 139 | check "a.#{keyword} = 1" 140 | tryCatch = (keyword, check = strict) -> 141 | check "try new Error catch #{keyword}" 142 | 143 | future = 'implements interface let package private protected public static yield'.split ' ' 144 | for keyword in future 145 | access keyword 146 | assign keyword 147 | destruct keyword 148 | invoke keyword 149 | fnDecl keyword 150 | param keyword 151 | prop keyword, strictOk 152 | tryCatch keyword 153 | 154 | for keyword in ['eval', 'arguments'] 155 | access keyword, strictOk 156 | assign keyword 157 | destruct keyword, strictOk 158 | invoke keyword, strictOk 159 | fnDecl keyword 160 | param keyword 161 | prop keyword, strictOk 162 | tryCatch keyword 163 | -------------------------------------------------------------------------------- /test/strings.coffee: -------------------------------------------------------------------------------- 1 | # String Literals 2 | # --------------- 3 | 4 | # TODO: refactor string literal tests 5 | # TODO: add indexing and method invocation tests: "string"["toString"] is String::toString, "string".toString() is "string" 6 | 7 | # * Strings 8 | # * Heredocs 9 | 10 | test "backslash escapes", -> 11 | eq "\\/\\\\", /\/\\/.source 12 | 13 | eq '(((dollars)))', '\(\(\(dollars\)\)\)' 14 | eq 'one two three', "one 15 | two 16 | three" 17 | eq "four five", 'four 18 | 19 | five' 20 | 21 | test "#3229, multiline strings", -> 22 | # Separate lines by default by a single space in literal strings. 23 | eq 'one 24 | two', 'one two' 25 | eq "one 26 | two", 'one two' 27 | eq ' 28 | a 29 | b 30 | ', 'a b' 31 | eq " 32 | a 33 | b 34 | ", 'a b' 35 | eq 'one 36 | 37 | two', 'one two' 38 | eq "one 39 | 40 | two", 'one two' 41 | eq ' 42 | indentation 43 | doesn\'t 44 | matter', 'indentation doesn\'t matter' 45 | eq 'trailing ws 46 | doesn\'t matter', 'trailing ws doesn\'t matter' 47 | 48 | # Use backslashes at the end of a line to specify whitespace between lines. 49 | eq 'a \ 50 | b\ 51 | c \ 52 | d', 'a bc d' 53 | eq "a \ 54 | b\ 55 | c \ 56 | d", 'a bc d' 57 | eq 'ignore \ 58 | trailing whitespace', 'ignore trailing whitespace' 59 | 60 | # Backslash at the beginning of a literal string. 61 | eq '\ 62 | ok', 'ok' 63 | eq ' \ 64 | ok', ' ok' 65 | 66 | # #1273, empty strings. 67 | eq '\ 68 | ', '' 69 | eq ' 70 | ', '' 71 | eq ' 72 | ', '' 73 | eq ' ', ' ' 74 | 75 | # Same behavior in interpolated strings. 76 | eq "interpolation #{1} 77 | follows #{2} \ 78 | too #{3}\ 79 | !", 'interpolation 1 follows 2 too 3!' 80 | eq "a #{ 81 | 'string ' + "inside 82 | interpolation" 83 | }", "a string inside interpolation" 84 | eq " 85 | #{1} 86 | ", '1' 87 | 88 | # Handle escaped backslashes correctly. 89 | eq '\\', `'\\'` 90 | eq 'escaped backslash at EOL\\ 91 | next line', 'escaped backslash at EOL\\ next line' 92 | eq '\\ 93 | next line', '\\ next line' 94 | eq '\\ 95 | ', '\\' 96 | eq '\\\\\\ 97 | ', '\\\\\\' 98 | eq "#{1}\\ 99 | after interpolation", '1\\ after interpolation' 100 | eq 'escaped backslash before slash\\ \ 101 | next line', 'escaped backslash before slash\\ next line' 102 | eq 'triple backslash\\\ 103 | next line', 'triple backslash\\next line' 104 | eq 'several escaped backslashes\\\\\\ 105 | ok', 'several escaped backslashes\\\\\\ ok' 106 | eq 'several escaped backslashes slash\\\\\\\ 107 | ok', 'several escaped backslashes slash\\\\\\ok' 108 | eq 'several escaped backslashes with trailing ws \\\\\\ 109 | ok', 'several escaped backslashes with trailing ws \\\\\\ ok' 110 | 111 | # Backslashes at beginning of lines. 112 | eq 'first line 113 | \ backslash at BOL', 'first line \ backslash at BOL' 114 | eq 'first line\ 115 | \ backslash at BOL', 'first line\ backslash at BOL' 116 | 117 | # Edge case. 118 | eq 'lone 119 | 120 | \ 121 | 122 | backslash', 'lone backslash' 123 | 124 | test "#3249, escape newlines in heredocs with backslashes", -> 125 | # Ignore escaped newlines 126 | eq ''' 127 | Set whitespace \ 128 | <- this is ignored\ 129 | none 130 | normal indentation 131 | ''', 'Set whitespace <- this is ignorednone\n normal indentation' 132 | eq """ 133 | Set whitespace \ 134 | <- this is ignored\ 135 | none 136 | normal indentation 137 | """, 'Set whitespace <- this is ignorednone\n normal indentation' 138 | 139 | # Changed from #647, trailing backslash. 140 | eq ''' 141 | Hello, World\ 142 | 143 | ''', 'Hello, World' 144 | eq ''' 145 | \\ 146 | ''', '\\' 147 | 148 | # Backslash at the beginning of a literal string. 149 | eq '''\ 150 | ok''', 'ok' 151 | eq ''' \ 152 | ok''', ' ok' 153 | 154 | # Same behavior in interpolated strings. 155 | eq """ 156 | interpolation #{1} 157 | follows #{2} \ 158 | too #{3}\ 159 | ! 160 | """, 'interpolation 1\n follows 2 too 3!' 161 | eq """ 162 | 163 | #{1} #{2} 164 | 165 | """, '\n1 2\n' 166 | 167 | # TODO: uncomment when #2388 is fixed 168 | # eq """a heredoc #{ 169 | # "inside \ 170 | # interpolation" 171 | # }""", "a heredoc inside interpolation" 172 | 173 | # Handle escaped backslashes correctly. 174 | eq ''' 175 | escaped backslash at EOL\\ 176 | next line 177 | ''', 'escaped backslash at EOL\\\n next line' 178 | eq '''\\ 179 | 180 | ''', '\\\n' 181 | 182 | # Backslashes at beginning of lines. 183 | eq '''first line 184 | \ backslash at BOL''', 'first line\n\ backslash at BOL' 185 | eq """first line\ 186 | \ backslash at BOL""", 'first line\ backslash at BOL' 187 | 188 | # Edge cases. 189 | eq '''lone 190 | 191 | \ 192 | 193 | 194 | 195 | backslash''', 'lone\n\n backslash' 196 | eq '''\ 197 | ''', '' 198 | 199 | #647 200 | eq "''Hello, World\\''", ''' 201 | '\'Hello, World\\\'' 202 | ''' 203 | eq '""Hello, World\\""', """ 204 | "\"Hello, World\\\"" 205 | """ 206 | 207 | test "#1273, escaping quotes at the end of heredocs.", -> 208 | # """\""" no longer compiles 209 | eq """\\""", '\\' 210 | eq """\\\"""", '\\\"' 211 | 212 | a = """ 213 | basic heredoc 214 | on two lines 215 | """ 216 | ok a is "basic heredoc\non two lines" 217 | 218 | a = ''' 219 | a 220 | "b 221 | c 222 | ''' 223 | ok a is "a\n \"b\nc" 224 | 225 | a = """ 226 | a 227 | b 228 | c 229 | """ 230 | ok a is "a\n b\n c" 231 | 232 | a = '''one-liner''' 233 | ok a is 'one-liner' 234 | 235 | a = """ 236 | out 237 | here 238 | """ 239 | ok a is "out\nhere" 240 | 241 | a = ''' 242 | a 243 | b 244 | c 245 | ''' 246 | ok a is " a\n b\nc" 247 | 248 | a = ''' 249 | a 250 | 251 | 252 | b c 253 | ''' 254 | ok a is "a\n\n\nb c" 255 | 256 | a = '''more"than"one"quote''' 257 | ok a is 'more"than"one"quote' 258 | 259 | a = '''here's an apostrophe''' 260 | ok a is "here's an apostrophe" 261 | 262 | # The indentation detector ignores blank lines without trailing whitespace 263 | a = """ 264 | one 265 | two 266 | 267 | """ 268 | ok a is "one\ntwo\n" 269 | 270 | eq ''' line 0 271 | should not be relevant 272 | to the indent level 273 | ''', ' line 0\nshould not be relevant\n to the indent level' 274 | 275 | eq ''' '\\\' ''', " '\\' " 276 | eq """ "\\\" """, ' "\\" ' 277 | 278 | eq ''' <- keep these spaces -> ''', ' <- keep these spaces -> ' 279 | 280 | 281 | test "#1046, empty string interpolations", -> 282 | eq "#{ }", '' 283 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CoffeeScript Test Suite 6 | 7 | 18 | 19 | 20 | 21 | CoffeeScript Test Suite 22 | 23 | 24 | 118 | 119 | 120 | 121 | --------------------------------------------------------------------------------
Loader for CoffeeScript as a Node.js library.
exports[key] = val for key, val of require './coffee-script'