Permalink
Browse files

initial commit of the mystery language

  • Loading branch information...
jashkenas committed Dec 13, 2009
0 parents commit 8e9d637985d2dc9b44922076ad54ffef7fa8e9c2
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
30 SYNTAX
@@ -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()
206 grammar.y
@@ -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.

7 comments on commit 8e9d637

@amrnt

This comment has been minimized.

Show comment
Hide comment
@amrnt

amrnt 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! 😆

amrnt replied 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

This comment has been minimized.

Show comment
Hide comment
@jashkenas

jashkenas Jun 17, 2013

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.

Owner

jashkenas replied Jun 17, 2013

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

This comment has been minimized.

Show comment
Hide comment
@vendethiel

vendethiel Jun 17, 2013

Collaborator

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

Collaborator

vendethiel replied Jun 17, 2013

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

@michaelficarra

This comment has been minimized.

Show comment
Hide comment
@michaelficarra

michaelficarra Jun 17, 2013

Collaborator

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

Collaborator

michaelficarra replied Jun 17, 2013

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

@jiyinyiyong

This comment has been minimized.

Show comment
Hide comment
@jiyinyiyong

jiyinyiyong Jun 17, 2013

Contributor

Couldn't believe it was CoffeeScript:

sum: x, y => x + y.
Contributor

jiyinyiyong replied Jun 17, 2013

Couldn't believe it was CoffeeScript:

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

This comment has been minimized.

Show comment
Hide comment
@davidchambers

davidchambers Jun 17, 2013

Contributor

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

say it aint so
Contributor

davidchambers replied Jun 17, 2013

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

say it aint so
@amrnt

This comment has been minimized.

Show comment
Hide comment
@amrnt

amrnt Jun 17, 2013

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

%w(awesome artistic javascript).collect{|s| s[0]}.join.reverse

amrnt replied 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.