Skip to content

Commit 8e9d637

Browse files
committed
initial commit of the mystery language
0 parents  commit 8e9d637

10 files changed

+2339
-0
lines changed

SYNTAX

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Every line is an expression. Multiple expressions on a single line can be
2+
separated by a ";" character.
3+
4+
NUM: 1
5+
1.0
6+
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 > 1
17+
18+
if (x > 1) return x
19+
20+
ASSIGN: a : b
21+
22+
LOGICAL: x && y
23+
x and y
24+
x || y
25+
x or y
26+
27+
28+
29+
30+

code.jaa

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# I'm a comment and I'm ok.
2+
# Return
3+
# Case
4+
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.14159
17+
list: [1, 2, 3, 4]
18+
three: 3
19+
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_up
30+
full_speed_ahead()
31+
weapons.fire_torpedos()
32+
else
33+
run_away().
34+
35+
eldest: if 25 > 21 then liz else marge.
36+
37+
38+
39+
40+

documents.jaa

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Document Model
2+
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 inline
9+
# 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 the
15+
# document by binding to Metadata, instead of on-the-fly.
16+
metadata : =>
17+
docId : this.id
18+
_.select(Metadata.models(), (m =>
19+
_.any(m.get('instances'), (i =>
20+
i.document_id == docId
21+
))
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 Set
35+
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().length
50+
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() <= 1
56+
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() <= 1
60+
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip')
61+
62+
# We override "_onModelEvent" to fire selection changed events when documents
63+
# 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 fire
68+
}
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()

grammar.y

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
class Parser
2+
3+
# Declare tokens produced by the lexer
4+
token IF ELSE THEN
5+
token NEWLINE
6+
token NUMBER
7+
token STRING
8+
token TRUE FALSE NULL
9+
token IDENTIFIER PROPERTY_ACCESS
10+
token CODE
11+
12+
prechigh
13+
nonassoc UMINUS NOT '!'
14+
left '*' '/' '%'
15+
left '+' '-'
16+
left '<=' '<' '>' '>='
17+
right '==' '!=' IS AINT
18+
left '&&' '||' AND OR
19+
right '-=' '+=' '/=' '*='
20+
preclow
21+
22+
rule
23+
# 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 language
50+
Expression:
51+
Literal
52+
| Variable
53+
| Call
54+
| Assign
55+
| Object
56+
| Code
57+
| Operation
58+
| Array
59+
| If
60+
;
61+
62+
# All tokens that can terminate an expression
63+
Terminator:
64+
"\n"
65+
| ";"
66+
;
67+
68+
# All hard-coded values
69+
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 variable
78+
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 operators
88+
# For Ruby's Operator precedence, see:
89+
# https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
90+
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 definition
129+
Code:
130+
"=>" Expressions "." { result = CodeNode.new([], val[1]) }
131+
| ParamList
132+
"=>" 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 AssignList
150+
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 Expression
179+
THEN Expression "." { result = TernaryNode.new(val[1], val[3]) }
180+
| IF Expression Terminator
181+
Expressions "." { result = IfNode.new(val[1], val[3]) }
182+
| IF Expression
183+
THEN Expression
184+
ELSE Expression "." { result = TernaryNode.new(val[1], val[3], val[5]) }
185+
| IF Expression Terminator
186+
Expressions Terminator
187+
ELSE Expressions "." { result = IfNode.new(val[1], val[3], val[6]) }
188+
;
189+
190+
end
191+
192+
---- header
193+
require "lexer"
194+
require "nodes"
195+
196+
---- inner
197+
def parse(code, show_tokens=false)
198+
# @yydebug = true
199+
@tokens = Lexer.new.tokenize(code)
200+
puts @tokens.inspect if show_tokens
201+
do_parse
202+
end
203+
204+
def next_token
205+
@tokens.shift
206+
end

0 commit comments

Comments
 (0)

Follow Lee on X/Twitter - Father, Husband, Serial builder creating AI, crypto, games & web tools. We are friends :) AI Will Come To Life!

Check out: eBank.nz (Art Generator) | Netwrck.com (AI Tools) | Text-Generator.io (AI API) | BitBank.nz (Crypto AI) | ReadingTime (Kids Reading) | RewordGame | BigMultiplayerChess | WebFiddle | How.nz | Helix AI Assistant