turmali 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 06bcafea0f78c8e5a95c998bae921490a31b233a
4
+ data.tar.gz: 8c0618191772b4335fe85a17a5a99bdcf72e46a2
5
+ SHA512:
6
+ metadata.gz: ca731d0ed9efb286a5ee7c602951878831af0db2d54887a451be0b97a5ce3781f2a616bbe7f6638ff129baf426ce511809caafc8a04d628f4e4c552ec1b9dbfb
7
+ data.tar.gz: 697603935ef127cfe705943b6e9c83f742a078212453ee132c3bb4e3d273d12a9767946748d85937ce4c6eb37d5ffd300d124c067951a075102f25091a51f3ee
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.15.3
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at eiffelqiu@qq.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in turmali.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Eiffel Qiu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Turmali
2
+
3
+ Turmali is a website building language.
4
+
5
+ ```
6
+ class Turmali:
7
+ def name:
8
+ "I'm Turmali"
9
+ def turmali:
10
+ 100
11
+
12
+
13
+ tml = Turmali.new
14
+ print(tml.name)
15
+ print(tml.turmali)
16
+ ```
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'turmali'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install turmali
33
+
34
+ ## Usage
35
+
36
+ ```The Turmali language!
37
+
38
+ usage:
39
+ ./tml example.tml # to eval a file
40
+ ./tml # to start the REPL
41
+
42
+ on Windows run with: ruby -I. tml [options]
43
+ ```
44
+
45
+ ## Development
46
+
47
+ After checking out the repo, run `rake spec` to run the tests. You can also run `bin/tml` for an interactive prompt that will allow you to experiment.
48
+
49
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
50
+
51
+ ## Contributing
52
+
53
+ Bug reports and pull requests are welcome on GitHub at https://github.com/eiffelqiu/turmali. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
54
+
55
+ ## License
56
+
57
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
58
+
59
+ ## Code of Conduct
60
+
61
+ Everyone interacting in the Turmali project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/eiffelqiu/turmali/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/tml ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # The Turmali language!
3
+ #
4
+ # usage:
5
+ # ./tml example.tml # to eval a file
6
+ # ./tml # to start the REPL
7
+ #
8
+ # on Windows run with: ruby -I. tml [options]
9
+
10
+ require "bundler/setup"
11
+ require "turmali"
12
+
13
+ require "readline"
14
+
15
+ interpreter = Interpreter.new
16
+
17
+ # If a file is given we eval it.
18
+ if file = ARGV.first
19
+ interpreter.eval File.read(file)
20
+
21
+ # Start the REPL, read-eval-print-loop, or interactive interpreter
22
+ else
23
+ puts "Turmali REPL, CTRL+C to quit"
24
+ loop do
25
+ line = Readline::readline(">> ") # 1. Read
26
+ Readline::HISTORY.push(line)
27
+ value = interpreter.eval(line) # 2. Eval
28
+ puts "=> #{value.ruby_value.inspect}" # 3. Print
29
+ end # 4. Loop
30
+
31
+ end
data/lib/.DS_Store ADDED
Binary file
data/lib/turmali.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "turmali/version"
2
+ require "turmali/interpreter"
Binary file
@@ -0,0 +1,228 @@
1
+ class Parser
2
+
3
+ # We need to tell the parser what tokens to expect. So each type of token produced
4
+ # by our lexer needs to be declared here.
5
+ token IF
6
+ token DEF
7
+ token CLASS
8
+ token NEWLINE
9
+ token NUMBER
10
+ token STRING
11
+ token TRUE FALSE NIL
12
+ token IDENTIFIER
13
+ token CONSTANT
14
+ token INDENT DEDENT
15
+
16
+ # Here is the Operator Precedence Table. As presented before, it tells the parser in
17
+ # which order to parse expressions containing operators.
18
+ # This table is based on the [C and C++ Operator Precedence Table](http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence).
19
+ prechigh
20
+ left '.'
21
+ right '!'
22
+ left '*' '/'
23
+ left '+' '-'
24
+ left '>' '>=' '<' '<='
25
+ left '==' '!='
26
+ left '&&'
27
+ left '||'
28
+ right '='
29
+ left ','
30
+ preclow
31
+
32
+ # In the following `rule` section, we define the parsing rules.
33
+ # All rules are declared using the following format:
34
+ #
35
+ # RuleName:
36
+ # OtherRule TOKEN AnotherRule { result = Node.new }
37
+ # | OtherRule { ... }
38
+ # ;
39
+ #
40
+ # In the action section (inside the `{...}` on the right), you can do the following:
41
+ #
42
+ # * Assign to `result` the value returned by the rule, usually a node for the AST.
43
+ # * Use `val[index of expression]` to get the `result` of a matched
44
+ # expressions on the left.
45
+ rule
46
+ # First, parsers are dumb, we need to explicitly tell it how to handle empty
47
+ # programs. This is what the first rule does. Note that everything between `/* ... */` is
48
+ # a comment.
49
+ Program:
50
+ /* nothing */ { result = Nodes.new([]) }
51
+ | Expressions { result = val[0] }
52
+ ;
53
+
54
+ # Next, we define what a list of expressions is. Simply put, it's series of expressions separated by a
55
+ # terminator (a new line or `;` as defined later). But once again, we need to explicitly
56
+ # define how to handle trailing and orphans line breaks (the last two lines).
57
+ #
58
+ # One very powerful trick we'll use to define variable rules like this one
59
+ # (rules which can match any number of tokens) is *left-recursion*. Which means we reference
60
+ # the rule itself, directly or indirectly, on the left side **only**. This is true for the current
61
+ # type of parser we're using (LR). For other types of parsers like ANTLR (LL), it's the opposite,
62
+ # you can only use right-recursion.
63
+ #
64
+ # As you'll see bellow, the `Expressions` rule references `Expressions` itself.
65
+ # In other words, a list of expressions can be another list of expressions followed by
66
+ # another expression.
67
+ Expressions:
68
+ Expression { result = Nodes.new(val) }
69
+ | Expressions Terminator Expression { result = val[0] << val[2] }
70
+ | Expressions Terminator { result = val[0] }
71
+ | Terminator { result = Nodes.new([]) }
72
+ ;
73
+
74
+ # Every type of expression supported by our language is defined here.
75
+ Expression:
76
+ Literal
77
+ | Call
78
+ | Operator
79
+ | GetConstant
80
+ | SetConstant
81
+ | GetLocal
82
+ | SetLocal
83
+ | Def
84
+ | Class
85
+ | If
86
+ | '(' Expression ')' { result = val[1] }
87
+ ;
88
+
89
+ # Notice how we implement support for parentheses using the previous rule.
90
+ # `'(' Expression ')'` will force the parsing of `Expression` in its
91
+ # entirety first. Parentheses will then be discarded leaving only the fully parsed expression.
92
+ #
93
+ # Terminators are tokens that can terminate an expression.
94
+ # When using tokens to define rules, we simply reference them by their type which we defined in
95
+ # the lexer.
96
+ Terminator:
97
+ NEWLINE
98
+ | ";"
99
+ ;
100
+
101
+ # Literals are the hard-coded values inside the program. If you want to add support
102
+ # for other literal types, such as arrays or hashes, this it where you'd do it.
103
+ Literal:
104
+ NUMBER { result = NumberNode.new(val[0]) }
105
+ | STRING { result = StringNode.new(val[0]) }
106
+ | TRUE { result = TrueNode.new }
107
+ | FALSE { result = FalseNode.new }
108
+ | NIL { result = NilNode.new }
109
+ ;
110
+
111
+ # Method calls can take three forms:
112
+ #
113
+ # * Without a receiver (`self` is assumed): `method(arguments)`.
114
+ # * With a receiver: `receiver.method(arguments)`.
115
+ # * And a hint of syntactic sugar so that we can drop
116
+ # the `()` if no arguments are given: `receiver.method`.
117
+ #
118
+ # Each one of those is handled by the following rule.
119
+ Call:
120
+ IDENTIFIER Arguments { result = CallNode.new(nil, val[0], val[1]) }
121
+ | Expression "." IDENTIFIER
122
+ Arguments { result = CallNode.new(val[0], val[2], val[3]) }
123
+ | Expression "." IDENTIFIER { result = CallNode.new(val[0], val[2], []) }
124
+ ;
125
+
126
+ Arguments:
127
+ "(" ")" { result = [] }
128
+ | "(" ArgList ")" { result = val[1] }
129
+ ;
130
+
131
+ ArgList:
132
+ Expression { result = val }
133
+ | ArgList "," Expression { result = val[0] << val[2] }
134
+ ;
135
+
136
+
137
+ # In our language, like in Ruby, operators are converted to method calls.
138
+ # So `1 + 2` will be converted to `1.+(2)`.
139
+ # `1` is the receiver of the `+` method call, passing `2`
140
+ # as an argument.
141
+ # Operators need to be defined individually for the Operator Precedence Table to take
142
+ # action.
143
+ Operator:
144
+ Expression '||' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
145
+ | Expression '&&' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
146
+ | Expression '==' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
147
+ | Expression '!=' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
148
+ | Expression '>' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
149
+ | Expression '>=' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
150
+ | Expression '<' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
151
+ | Expression '<=' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
152
+ | Expression '+' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
153
+ | Expression '-' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
154
+ | Expression '*' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
155
+ | Expression '/' Expression { result = CallNode.new(val[0], val[1], [val[2]]) }
156
+ ;
157
+
158
+ # Then we have rules for getting and setting values of constants and local variables.
159
+ GetConstant:
160
+ CONSTANT { result = GetConstantNode.new(val[0]) }
161
+ ;
162
+
163
+ SetConstant:
164
+ CONSTANT "=" Expression { result = SetConstantNode.new(val[0], val[2]) }
165
+ ;
166
+
167
+ GetLocal:
168
+ IDENTIFIER { result = GetLocalNode.new(val[0]) }
169
+ ;
170
+
171
+ SetLocal:
172
+ IDENTIFIER "=" Expression { result = SetLocalNode.new(val[0], val[2]) }
173
+ ;
174
+
175
+ # Our language uses indentation to separate blocks of code. But the lexer took care of all
176
+ # that complexity for us and wrapped all blocks in `INDENT ... DEDENT`. A block
177
+ # is simply an increment in indentation followed by some code and closing with an equivalent
178
+ # decrement in indentation.
179
+ #
180
+ # If you'd like to use curly brackets or `end` to delimit blocks instead, you'd
181
+ # simply need to modify this one rule.
182
+ # You'll also need to remove the indentation logic from the lexer.
183
+ Block:
184
+ INDENT Expressions DEDENT { result = val[1] }
185
+ ;
186
+
187
+ # The `def` keyword is used for defining methods. Once again, we're introducing
188
+ # a bit of syntactic sugar here to allow skipping the parentheses when there are no parameters.
189
+ Def:
190
+ DEF IDENTIFIER Block { result = DefNode.new(val[1], [], val[2]) }
191
+ | DEF IDENTIFIER
192
+ "(" ParamList ")" Block { result = DefNode.new(val[1], val[3], val[5]) }
193
+ ;
194
+
195
+ ParamList:
196
+ /* nothing */ { result = [] }
197
+ | IDENTIFIER { result = val }
198
+ | ParamList "," IDENTIFIER { result = val[0] << val[2] }
199
+ ;
200
+
201
+ # Class definition is similar to method definition.
202
+ # Class names are also constants because they start with a capital letter.
203
+ Class:
204
+ CLASS CONSTANT Block { result = ClassNode.new(val[1], val[2]) }
205
+ ;
206
+
207
+ # Finally, `if` is similar to `class` but receives a *condition*.
208
+ If:
209
+ IF Expression Block { result = IfNode.new(val[1], val[2]) }
210
+ ;
211
+ end
212
+
213
+ # The final code at the bottom of this Racc file will be put as-is in the generated `Parser` class.
214
+ # You can put some code at the top (`header`) and some inside the class (`inner`).
215
+ ---- header
216
+ require "lexer"
217
+ require "nodes"
218
+
219
+ ---- inner
220
+ def parse(code, show_tokens=false)
221
+ @tokens = Lexer.new.tokenize(code) # Tokenize the code using our lexer
222
+ puts @tokens.inspect if show_tokens
223
+ do_parse # Kickoff the parsing process
224
+ end
225
+
226
+ def next_token
227
+ @tokens.shift
228
+ end