Commit 8e9d637
committed
0 parents commit 8e9d637
File tree
10 files changed
+2339
-0
lines changed10 files changed
+2339-0lines changed+30Original file line number Diff line number Diff line change @@ -0,0 +1,30 @@1
+
Every line is an expression. Multiple expressions on a single line can be2
+
separated by a ";" character.3
+
4
+
NUM: 15
+
1.06
+
7
+
STRING: "hello"8
+
'hello'9
+
10
+
OBJECT: {one : 1, two : 2}11
+
12
+
ARRAY: [1, 2, 3]13
+
14
+
CODE: a, b => a * b.15
+
16
+
IF: return x if x > 117
+
18
+
if (x > 1) return x19
+
20
+
ASSIGN: a : b21
+
22
+
LOGICAL: x && y23
+
x and y24
+
x || y25
+
x or y26
+
27
+
28
+
29
+
30
+
+40Original file line number Diff line number Diff line change @@ -0,0 +1,40 @@1
+
# I'm a comment and I'm ok.2
+
# Return3
+
# Case4
+
5
+
square: x => x * x.6
+
7
+
sum: x, y => x + y.8
+
9
+
odd: x => x % 2 is 0.10
+
11
+
even: x => x % 2 aint 0.12
+
13
+
object_literal: {one: 1, two: 2, three: 3}14
+
15
+
multiline_object: {16
+
pi: 3.1415917
+
list: [1, 2, 3, 4]18
+
three: 319
+
inner_obj: {20
+
freedom: => _.freedom().21
+
}22
+
}23
+
24
+
run_loop: =>25
+
fire_events(e => e.stopPropagation().)26
+
listen()27
+
wait().28
+
29
+
if submarine.shields_up30
+
full_speed_ahead()31
+
weapons.fire_torpedos()32
+
else33
+
run_away().34
+
35
+
eldest: if 25 > 21 then liz else marge.36
+
37
+
38
+
39
+
40
+
+76Original file line number Diff line number Diff line change @@ -0,0 +1,76 @@1
+
# Document Model2
+
dc.model.Document: dc.Model.extend({3
+
4
+
constructor : attributes => this.base(attributes).5
+
6
+
# For display, show either the highlighted search results, or the summary,7
+
# if no highlights are available.8
+
# The import process will take care of this in the future, but the inline9
+
# version of the summary has all runs of whitespace squeezed out.10
+
displaySummary : =>11
+
text: this.get('highlight') or this.get('summary')12
+
text ? text.replace(/\s+/g, ' ') else ''13
+
14
+
# Return a list of the document's metadata. Think about caching this on the15
+
# document by binding to Metadata, instead of on-the-fly.16
+
metadata : =>17
+
docId : this.id18
+
_.select(Metadata.models(), (m =>19
+
_.any(m.get('instances'), (i =>20
+
i.document_id == docId21
+
))22
+
))23
+
24
+
bookmark : pageNumber =>25
+
bookmark : new dc.model.Bookmark({title : this.get('title'), page_number : pageNumber, document_id : this.id})26
+
Bookmarks.create(bookmark)27
+
28
+
# Inspect.29
+
toString : =>30
+
'Document ' + this.id + ' "' + this.get('title') + '"'31
+
32
+
})33
+
34
+
# Document Set35
+
dc.model.DocumentSet : dc.model.RESTfulSet.extend({36
+
37
+
resource : 'documents'38
+
39
+
SELECTION_CHANGED : 'documents:selection_changed'40
+
41
+
constructor : options =>42
+
this.base(options)43
+
_.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText')44
+
45
+
selected : => _.select(this.models(), m => m.get('selected'))46
+
47
+
selectedIds : => _.pluck(this.selected(), 'id')48
+
49
+
countSelected : => this.selected().length50
+
51
+
downloadSelectedViewers : =>52
+
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_viewer.zip');53
+
54
+
downloadSelectedPDF : =>55
+
return window.open(this.selected()[0].get('pdf_url')) if this.countSelected() <= 156
+
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_pdfs.zip')57
+
58
+
downloadSelectedFullText : =>59
+
return window.open(this.selected()[0].get('full_text_url')) if this.countSelected() <= 160
+
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip')61
+
62
+
# We override "_onModelEvent" to fire selection changed events when documents63
+
# change their selected state.64
+
_onModelEvent : e, model =>65
+
this.base(e, model)66
+
fire : (e == dc.Model.CHANGED and model.hasChanged('selected'))67
+
_.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this)) if fire68
+
}69
+
70
+
})71
+
72
+
# The main set of Documents, used by the search tab.73
+
window.Documents : new dc.model.DocumentSet()74
+
75
+
# The set of documents that is used to look at a particular label.76
+
dc.app.LabeledDocuments : new dc.model.DocumentSet()+206Original file line number Diff line number Diff line change @@ -0,0 +1,206 @@1
+
class Parser2
+
3
+
# Declare tokens produced by the lexer4
+
token IF ELSE THEN5
+
token NEWLINE6
+
token NUMBER7
+
token STRING8
+
token TRUE FALSE NULL9
+
token IDENTIFIER PROPERTY_ACCESS10
+
token CODE11
+
12
+
prechigh13
+
nonassoc UMINUS NOT '!'14
+
left '*' '/' '%'15
+
left '+' '-'16
+
left '<=' '<' '>' '>='17
+
right '==' '!=' IS AINT18
+
left '&&' '||' AND OR19
+
right '-=' '+=' '/=' '*='20
+
preclow21
+
22
+
rule23
+
# All rules are declared in this format:24
+
#25
+
# RuleName:26
+
# OtherRule TOKEN AnotherRule { code to run when this matches }27
+
# | OtherRule { ... }28
+
# ;29
+
#30
+
# In the code section (inside the {...} on the right):31
+
# - Assign to "result" the value returned by the rule.32
+
# - Use val[index of expression] to reference expressions on the left.33
+
34
+
35
+
# All parsing will end in this rule, being the trunk of the AST.36
+
Root:37
+
/* nothing */ { result = Nodes.new([]) }38
+
| Expressions { result = val[0] }39
+
;40
+
41
+
# Any list of expressions or method body, seperated by line breaks.42
+
Expressions:43
+
Expression { result = Nodes.new(val) }44
+
| Expressions Terminator Expression { result = val[0] << val[2] }45
+
| Expressions Terminator { result = Nodes.new([val[0]]) }46
+
| Terminator Expressions { result = Nodes.new([val[1]]) }47
+
;48
+
49
+
# All types of expressions in our language50
+
Expression:51
+
Literal52
+
| Variable53
+
| Call54
+
| Assign55
+
| Object56
+
| Code57
+
| Operation58
+
| Array59
+
| If60
+
;61
+
62
+
# All tokens that can terminate an expression63
+
Terminator:64
+
"\n"65
+
| ";"66
+
;67
+
68
+
# All hard-coded values69
+
Literal:70
+
NUMBER { result = LiteralNode.new(val[0]) }71
+
| STRING { result = LiteralNode.new(val[0]) }72
+
| TRUE { result = LiteralNode.new(true) }73
+
| FALSE { result = LiteralNode.new(false) }74
+
| NULL { result = LiteralNode.new(nil) }75
+
;76
+
77
+
# Assign to a variable78
+
Assign:79
+
Variable ":" Expression { result = AssignNode.new(val[0], val[2]) }80
+
;81
+
82
+
# Assignment within an object literal.83
+
AssignObj:84
+
IDENTIFIER ":" Expression { result = AssignNode.new(val[0], val[2], :object) }85
+
;86
+
87
+
# Arithmetic and logical operators88
+
# For Ruby's Operator precedence, see:89
+
# https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html90
+
Operation:91
+
'!' Expression { result = OpNode.new(val[0], val[1]) }92
+
| '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }93
+
| NOT Expression { result = OpNode.new(val[0], val[1]) }94
+
95
+
96
+
| Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }97
+
| Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }98
+
| Expression '%' Expression { result = OpNode.new(val[1], val[0], val[2]) }99
+
100
+
| Expression '+' Expression { result = OpNode.new(val[1], val[0], val[2]) }101
+
| Expression '-' Expression { result = OpNode.new(val[1], val[0], val[2]) }102
+
103
+
| Expression '<=' Expression { result = OpNode.new(val[1], val[0], val[2]) }104
+
| Expression '<' Expression { result = OpNode.new(val[1], val[0], val[2]) }105
+
| Expression '>' Expression { result = OpNode.new(val[1], val[0], val[2]) }106
+
| Expression '>=' Expression { result = OpNode.new(val[1], val[0], val[2]) }107
+
108
+
| Expression '==' Expression { result = OpNode.new(val[1], val[0], val[2]) }109
+
| Expression '!=' Expression { result = OpNode.new(val[1], val[0], val[2]) }110
+
| Expression IS Expression { result = OpNode.new(val[1], val[0], val[2]) }111
+
| Expression AINT Expression { result = OpNode.new(val[1], val[0], val[2]) }112
+
113
+
| Expression '&&' Expression { result = OpNode.new(val[1], val[0], val[2]) }114
+
| Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }115
+
| Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }116
+
| Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }117
+
118
+
# Add ternary?119
+
120
+
| Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }121
+
| Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }122
+
| Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }123
+
| Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }124
+
# Add ||= &&=125
+
;126
+
127
+
128
+
# Method definition129
+
Code:130
+
"=>" Expressions "." { result = CodeNode.new([], val[1]) }131
+
| ParamList132
+
"=>" Expressions "." { result = CodeNode.new(val[0], val[2]) }133
+
;134
+
135
+
ParamList:136
+
/* nothing */ { result = [] }137
+
| IDENTIFIER { result = val }138
+
| ParamList "," IDENTIFIER { result = val[0] << val[2] }139
+
;140
+
141
+
Variable:142
+
IDENTIFIER { result = VariableNode.new(val) }143
+
| Variable PROPERTY_ACCESS IDENTIFIER { result = val[0] << val[2] }144
+
;145
+
146
+
Object:147
+
"{" "}" { result = ObjectNode.new([]) }148
+
| "{" AssignList "}" { result = ObjectNode.new(val[1]) }149
+
| "{" Terminator AssignList150
+
Terminator "}" { result = ObjectNode.new(val[2]) }151
+
;152
+
153
+
AssignList:154
+
/* nothing */ { result = []}155
+
| AssignObj { result = val }156
+
| AssignList "," AssignObj { result = val[0] << val[2] }157
+
| AssignList Terminator AssignObj { result = val[0] << val[2] }158
+
;159
+
160
+
# A method call.161
+
Call:162
+
Variable "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }163
+
;164
+
165
+
# An Array.166
+
Array:167
+
"[" ArgList "]" { result = ArrayNode.new(val[1]) }168
+
;169
+
170
+
# A list of arguments to a method call.171
+
ArgList:172
+
/* nothing */ { result = [] }173
+
| Expression { result = val }174
+
| ArgList "," Expression { result = val[0] << val[2] }175
+
;176
+
177
+
If:178
+
IF Expression179
+
THEN Expression "." { result = TernaryNode.new(val[1], val[3]) }180
+
| IF Expression Terminator181
+
Expressions "." { result = IfNode.new(val[1], val[3]) }182
+
| IF Expression183
+
THEN Expression184
+
ELSE Expression "." { result = TernaryNode.new(val[1], val[3], val[5]) }185
+
| IF Expression Terminator186
+
Expressions Terminator187
+
ELSE Expressions "." { result = IfNode.new(val[1], val[3], val[6]) }188
+
;189
+
190
+
end191
+
192
+
---- header193
+
require "lexer"194
+
require "nodes"195
+
196
+
---- inner197
+
def parse(code, show_tokens=false)198
+
# @yydebug = true199
+
@tokens = Lexer.new.tokenize(code)200
+
puts @tokens.inspect if show_tokens201
+
do_parse202
+
end203
+
204
+
def next_token205
+
@tokens.shift206
+
end
0 commit comments