Skip to content

Instantly share code, notes, and snippets.

@jashkenas
Created January 5, 2013 21:53
Show Gist options
  • Star 43 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save jashkenas/3fc3c1a8b1009c00d9df to your computer and use it in GitHub Desktop.
Save jashkenas/3fc3c1a8b1009c00d9df to your computer and use it in GitHub Desktop.

The Scope class regulates lexical scoping within CoffeeScript. As you generate code, you create a tree of scopes in the same shape as the nested function bodies. Each scope knows about the variables declared within it, and has a reference to its parent enclosing scope. In this way, we know which variables are new and need to be declared with var, and which are shared with external scopes.

Import the helpers we plan to use.

{extend, last} = require './helpers'

exports.Scope = class Scope

The root is the top-level Scope object for a given file.

  @root: null

Initialize a scope with its parent, for lookups up the chain, as well as a reference to the Block node it belongs to, which is where it should declare its variables, and a reference to the function that it belongs to.

  constructor: (@parent, @expressions, @method) ->
    @variables = [{name: 'arguments', type: 'arguments'}]
    @positions = {}
    Scope.root = this unless @parent

Adds a new variable or overrides an existing one.

  add: (name, type, immediate) ->
    return @parent.add name, type, immediate if @shared and not immediate
    if Object::hasOwnProperty.call @positions, name
      @variables[@positions[name]].type = type
    else
      @positions[name] = @variables.push({name, type}) - 1

When super is called, we need to find the name of the current method we're in, so that we know how to invoke the same method of the parent class. This can get complicated if super is being called from an inner function. namedMethod will walk up the scope tree until it either finds the first function object that has a name filled in, or bottoms out.

  namedMethod: ->
    return @method if @method.name or !@parent
    @parent.namedMethod()

Look up a variable name in lexical scope, and declare it if it does not already exist.

  find: (name) ->
    return yes if @check name
    @add name, 'var'
    no

Reserve a variable name as originating from a function parameter for this scope. No var required for internal references.

  parameter: (name) ->
    return if @shared and @parent.check name, yes
    @add name, 'param'

Just check to see if a variable has already been declared, without reserving, walks up to the root scope.

  check: (name) ->
    !!(@type(name) or @parent?.check(name))

Generate a temporary variable name at the given index.

  temporary: (name, index) ->
    if name.length > 1
      '_' + name + if index > 1 then index - 1 else ''
    else
      '_' + (index + parseInt name, 36).toString(36).replace /\d/g, 'a'

Gets the type of a variable.

  type: (name) ->
    return v.type for v in @variables when v.name is name
    null

If we need to store an intermediate result, find an available name for a compiler-generated variable. _var, _var2, and so on...

  freeVariable: (name, reserve=true) ->
    index = 0
    index++ while @check((temp = @temporary name, index))
    @add temp, 'var', yes if reserve
    temp

Ensure that an assignment is made at the top of this scope (or at the top-level scope, if requested).

  assign: (name, value) ->
    @add name, {value, assigned: yes}, yes
    @hasAssignments = yes

Does this scope have any declared variables?

  hasDeclarations: ->
    !!@declaredVariables().length

Return the list of variables first declared in this scope.

  declaredVariables: ->
    realVars = []
    tempVars = []
    for v in @variables when v.type is 'var'
      (if v.name.charAt(0) is '_' then tempVars else realVars).push v.name
    realVars.sort().concat tempVars.sort()

Return the list of assignments that are supposed to be made at the top of this scope.

  assignedVariables: ->
    "#{v.name} = #{v.type.value}" for v in @variables when v.type.assigned
@gbraad
Copy link

gbraad commented Feb 26, 2013

Screenshot of the output when using an editor with syntax-highlighting:
scope.litcoffee
source

@hickford
Copy link

@hoodie
Copy link

hoodie commented Feb 27, 2013

This is great, especially for libs or little helpers.
Now github only needs to treat it the way it treats .md files by default. But then, why does'n it do the same to any file with comments.
Neat idea!

@americos
Copy link

americos commented Mar 1, 2013

Cool, This looks pretty neat

@jamiter
Copy link

jamiter commented Mar 1, 2013

Like it!

Now github only needs to treat it the way it treats .md files by default.

Is there any way we could speed up this process?

@JogoShugh
Copy link

Very cool

@czarcrab
Copy link

czarcrab commented Mar 8, 2013

Absolutely love it

  • makes reviewing and writing code so painless
  • Sytax highlighting worked like a charm in sublime text
  • Got to remember the trick of adding a full stop at the end of comments, which is a really neat habit anyways to adopt, atleast till coffee-script connects to my temples and reads my mind

Now if I can just get docco to run on it.

czarcrab$ docco src/*.litcoffee
docco: skipped unknown type (src/checkers.litcoffee)

Which is a relatively easy fix using

".litcoffee" :
{"name" : "coffee-script", "symbol" : ""},

in languages.json, but this makes docco think that the file is all comments

@jashkenas
Copy link
Author

@czarcrab -- Docco supports it now (and is also now itself written in Literate CoffeeScript).

@aprice2704
Copy link

Really liking litcoffee so far!

OMG just tried docco. Fan-bloody-tastic. That is simply amazingly awesome. BY FAR the best looking code docs I have ever seen. Sooo cool, thanks Jeremy, you are a genius. Andy.

@paulvi
Copy link

paulvi commented Apr 29, 2013

@jamiter

Now github only needs to treat it the way it treats .md files by default.

Is there any way we could speed up this process?

Just use .coffee.md double extension, as mention on Docco page

@ALL
Nodeclipse (GitHub) is planning to add support for Coffee & LitCoffee

@madfriend
Copy link

@gbraad What's the color scheme you were using?

@tusharmath
Copy link

Literate Coffee script is just too good! But there were two problems that I faced -

  1. I am not able to add any non executable code to the markdown file. For example I tried to add some javascript code but failed as the interpreter got confused after seeing the backticks.
  2. Sometimes I don't want to show the coffee script code in the generated html even though I want it to be there in the file. Can that be done too? That would be a great feature!

@pateketrueke
Copy link

This is how it looks on SublimeText and TextMate (?):

Monokai dark-theme

Resources:

@merdekamentari
Copy link

Thank you for sharing, I would like to see your next amazing contents.

https://159.65.43.41/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment