Permalink
Showing with 2,339 additions and 0 deletions.
  1. +30 −0 SYNTAX
  2. +40 −0 code.jaa
  3. +76 −0 documents.jaa
  4. +206 −0 grammar.y
  5. +90 −0 lexer.rb
  6. +2 −0 lexer_test.rb
  7. +176 −0 nodes.rb
  8. +1,090 −0 parser.rb
  9. +19 −0 parser_test.rb
  10. +610 −0 underscore.jaa
@@ -0,0 +1,30 @@
+Every line is an expression. Multiple expressions on a single line can be
+separated by a ";" character.
+
+NUM: 1
+ 1.0
+
+STRING: "hello"
+ 'hello'
+
+OBJECT: {one : 1, two : 2}
+
+ARRAY: [1, 2, 3]
+
+CODE: a, b => a * b.
+
+IF: return x if x > 1
+
+ if (x > 1) return x
+
+ASSIGN: a : b
+
+LOGICAL: x && y
+ x and y
+ x || y
+ x or y
+
+
+
+
+
@@ -0,0 +1,40 @@
+# I'm a comment and I'm ok.
+# Return
+# Case
+
+square: x => x * x.
+
+sum: x, y => x + y.
+
+odd: x => x % 2 is 0.
+
+even: x => x % 2 aint 0.
+
+object_literal: {one: 1, two: 2, three: 3}
+
+multiline_object: {
+ pi: 3.14159
+ list: [1, 2, 3, 4]
+ three: 3
+ inner_obj: {
+ freedom: => _.freedom().
+ }
+}
+
+run_loop: =>
+ fire_events(e => e.stopPropagation().)
+ listen()
+ wait().
+
+if submarine.shields_up
+ full_speed_ahead()
+ weapons.fire_torpedos()
+else
+ run_away().
+
+eldest: if 25 > 21 then liz else marge.
+
+
+
+
+
@@ -0,0 +1,76 @@
+# Document Model
+dc.model.Document: dc.Model.extend({
+
+ constructor : attributes => this.base(attributes).
+
+ # For display, show either the highlighted search results, or the summary,
+ # if no highlights are available.
+ # The import process will take care of this in the future, but the inline
+ # version of the summary has all runs of whitespace squeezed out.
+ displaySummary : =>
+ text: this.get('highlight') or this.get('summary')
+ text ? text.replace(/\s+/g, ' ') else ''
+
+ # Return a list of the document's metadata. Think about caching this on the
+ # document by binding to Metadata, instead of on-the-fly.
+ metadata : =>
+ docId : this.id
+ _.select(Metadata.models(), (m =>
+ _.any(m.get('instances'), (i =>
+ i.document_id == docId
+ ))
+ ))
+
+ bookmark : pageNumber =>
+ bookmark : new dc.model.Bookmark({title : this.get('title'), page_number : pageNumber, document_id : this.id})
+ Bookmarks.create(bookmark)
+
+ # Inspect.
+ toString : =>
+ 'Document ' + this.id + ' "' + this.get('title') + '"'
+
+})
+
+# Document Set
+dc.model.DocumentSet : dc.model.RESTfulSet.extend({
+
+ resource : 'documents'
+
+ SELECTION_CHANGED : 'documents:selection_changed'
+
+ constructor : options =>
+ this.base(options)
+ _.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText')
+
+ selected : => _.select(this.models(), m => m.get('selected'))
+
+ selectedIds : => _.pluck(this.selected(), 'id')
+
+ countSelected : => this.selected().length
+
+ downloadSelectedViewers : =>
+ dc.app.download('/download/' + this.selectedIds().join('/') + '/document_viewer.zip');
+
+ downloadSelectedPDF : =>
+ return window.open(this.selected()[0].get('pdf_url')) if this.countSelected() <= 1
+ dc.app.download('/download/' + this.selectedIds().join('/') + '/document_pdfs.zip')
+
+ downloadSelectedFullText : =>
+ return window.open(this.selected()[0].get('full_text_url')) if this.countSelected() <= 1
+ dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip')
+
+ # We override "_onModelEvent" to fire selection changed events when documents
+ # change their selected state.
+ _onModelEvent : e, model =>
+ this.base(e, model)
+ fire : (e == dc.Model.CHANGED and model.hasChanged('selected'))
+ _.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this)) if fire
+ }
+
+})
+
+# The main set of Documents, used by the search tab.
+window.Documents : new dc.model.DocumentSet()
+
+# The set of documents that is used to look at a particular label.
+dc.app.LabeledDocuments : new dc.model.DocumentSet()
@@ -0,0 +1,206 @@
+class Parser
+
+# Declare tokens produced by the lexer
+token IF ELSE THEN
+token NEWLINE
+token NUMBER
+token STRING
+token TRUE FALSE NULL
+token IDENTIFIER PROPERTY_ACCESS
+token CODE
+
+prechigh
+ nonassoc UMINUS NOT '!'
+ left '*' '/' '%'
+ left '+' '-'
+ left '<=' '<' '>' '>='
+ right '==' '!=' IS AINT
+ left '&&' '||' AND OR
+ right '-=' '+=' '/=' '*='
+preclow
+
+rule
+ # All rules are declared in this format:
+ #
+ # RuleName:
+ # OtherRule TOKEN AnotherRule { code to run when this matches }
+ # | OtherRule { ... }
+ # ;
+ #
+ # In the code section (inside the {...} on the right):
+ # - Assign to "result" the value returned by the rule.
+ # - Use val[index of expression] to reference expressions on the left.
+
+
+ # All parsing will end in this rule, being the trunk of the AST.
+ Root:
+ /* nothing */ { result = Nodes.new([]) }
+ | Expressions { result = val[0] }
+ ;
+
+ # Any list of expressions or method body, seperated by line breaks.
+ Expressions:
+ Expression { result = Nodes.new(val) }
+ | Expressions Terminator Expression { result = val[0] << val[2] }
+ | Expressions Terminator { result = Nodes.new([val[0]]) }
+ | Terminator Expressions { result = Nodes.new([val[1]]) }
+ ;
+
+ # All types of expressions in our language
+ Expression:
+ Literal
+ | Variable
+ | Call
+ | Assign
+ | Object
+ | Code
+ | Operation
+ | Array
+ | If
+ ;
+
+ # All tokens that can terminate an expression
+ Terminator:
+ "\n"
+ | ";"
+ ;
+
+ # All hard-coded values
+ Literal:
+ NUMBER { result = LiteralNode.new(val[0]) }
+ | STRING { result = LiteralNode.new(val[0]) }
+ | TRUE { result = LiteralNode.new(true) }
+ | FALSE { result = LiteralNode.new(false) }
+ | NULL { result = LiteralNode.new(nil) }
+ ;
+
+ # Assign to a variable
+ Assign:
+ Variable ":" Expression { result = AssignNode.new(val[0], val[2]) }
+ ;
+
+ # Assignment within an object literal.
+ AssignObj:
+ IDENTIFIER ":" Expression { result = AssignNode.new(val[0], val[2], :object) }
+ ;
+
+ # Arithmetic and logical operators
+ # For Ruby's Operator precedence, see:
+ # https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
+ Operation:
+ '!' Expression { result = OpNode.new(val[0], val[1]) }
+ | '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
+ | NOT Expression { result = OpNode.new(val[0], val[1]) }
+
+
+ | Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '%' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+
+ | Expression '+' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '-' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+
+ | Expression '<=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '>=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+
+ | Expression '==' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '!=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression IS Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression AINT Expression { result = OpNode.new(val[1], val[0], val[2]) }
+
+ | Expression '&&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }
+
+ # Add ternary?
+
+ | Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ | Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
+ # Add ||= &&=
+ ;
+
+
+ # Method definition
+ Code:
+ "=>" Expressions "." { result = CodeNode.new([], val[1]) }
+ | ParamList
+ "=>" Expressions "." { result = CodeNode.new(val[0], val[2]) }
+ ;
+
+ ParamList:
+ /* nothing */ { result = [] }
+ | IDENTIFIER { result = val }
+ | ParamList "," IDENTIFIER { result = val[0] << val[2] }
+ ;
+
+ Variable:
+ IDENTIFIER { result = VariableNode.new(val) }
+ | Variable PROPERTY_ACCESS IDENTIFIER { result = val[0] << val[2] }
+ ;
+
+ Object:
+ "{" "}" { result = ObjectNode.new([]) }
+ | "{" AssignList "}" { result = ObjectNode.new(val[1]) }
+ | "{" Terminator AssignList
+ Terminator "}" { result = ObjectNode.new(val[2]) }
+ ;
+
+ AssignList:
+ /* nothing */ { result = []}
+ | AssignObj { result = val }
+ | AssignList "," AssignObj { result = val[0] << val[2] }
+ | AssignList Terminator AssignObj { result = val[0] << val[2] }
+ ;
+
+ # A method call.
+ Call:
+ Variable "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
+ ;
+
+ # An Array.
+ Array:
+ "[" ArgList "]" { result = ArrayNode.new(val[1]) }
+ ;
+
+ # A list of arguments to a method call.
+ ArgList:
+ /* nothing */ { result = [] }
+ | Expression { result = val }
+ | ArgList "," Expression { result = val[0] << val[2] }
+ ;
+
+ If:
+ IF Expression
+ THEN Expression "." { result = TernaryNode.new(val[1], val[3]) }
+ | IF Expression Terminator
+ Expressions "." { result = IfNode.new(val[1], val[3]) }
+ | IF Expression
+ THEN Expression
+ ELSE Expression "." { result = TernaryNode.new(val[1], val[3], val[5]) }
+ | IF Expression Terminator
+ Expressions Terminator
+ ELSE Expressions "." { result = IfNode.new(val[1], val[3], val[6]) }
+ ;
+
+end
+
+---- header
+ require "lexer"
+ require "nodes"
+
+---- inner
+ def parse(code, show_tokens=false)
+ # @yydebug = true
+ @tokens = Lexer.new.tokenize(code)
+ puts @tokens.inspect if show_tokens
+ do_parse
+ end
+
+ def next_token
+ @tokens.shift
+ end
Oops, something went wrong. Retry.

7 comments on commit 8e9d637

@amrnt
amrnt commented on 8e9d637 Jun 17, 2013

Oh my god! this is an epic commit!!
I know coffee was built initially on Ruby — but didn't know that the extension was .jaa! 😆

@jashkenas
Owner

I know, so embarrassing ;) Didn't have any sort of name for the language, and the first three letters that popped into my head were initials.

@vendethiel
Collaborator

I knew about the ruby part, the syntax part, but I certainly didn't know about that extension either !

@michaelficarra
Collaborator

I'm pretty glad we don't have an aint operator any longer.

@jiyinyiyong

Couldn't believe it was CoffeeScript:

sum: x, y => x + y.
@davidchambers

I'm pretty glad we don't have an aint operator any longer.

say it aint so
@amrnt
amrnt commented on 8e9d637 Jun 17, 2013

@jashkenas: here's a good guess for .jaa 😉

%w(awesome artistic javascript).collect{|s| s[0]}.join.reverse
Please sign in to comment.