-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial commit of the mystery language
- Loading branch information
0 parents
commit 8e9d637
Showing
10 changed files
with
2,339 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.
8e9d637
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
! 😆8e9d637
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
8e9d637
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I knew about the ruby part, the syntax part, but I certainly didn't know about that extension either !
8e9d637
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty glad we don't have an
aint
operator any longer.8e9d637
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't believe it was CoffeeScript:
8e9d637
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
8e9d637
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jashkenas: here's a good guess for
.jaa
😉