python 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +60 -0
- data/Rakefile +2 -0
- data/bin/python.rb +16 -0
- data/examples/demo.py +10 -0
- data/examples/fibonacci.py +5 -0
- data/examples/twoclass.py +9 -0
- data/features/programmer_caclulate_numerical_expression.feature +50 -0
- data/features/programmer_execute_from_source_file.feature +10 -0
- data/features/programmer_starts_repl_console.feature +10 -0
- data/features/programmer_use_advanced_calculator.feature +35 -0
- data/features/programmer_use_class.feature +42 -0
- data/features/programmer_use_closure.feature +66 -0
- data/features/programmer_use_variables.feature +12 -0
- data/features/step_definitions/calculate_numerical_steps.rb +67 -0
- data/features/support/env.rb +2 -0
- data/lib/python.rb +10 -0
- data/lib/python/builtins.rb +72 -0
- data/lib/python/environment.rb +42 -0
- data/lib/python/file_interpreter.rb +29 -0
- data/lib/python/parser/combinator.rb +206 -0
- data/lib/python/parser/expression.rb +125 -0
- data/lib/python/parser/identifier.rb +22 -0
- data/lib/python/parser/indent_converter.rb +52 -0
- data/lib/python/parser/integer.rb +28 -0
- data/lib/python/parser/statement.rb +86 -0
- data/lib/python/pyobject.rb +129 -0
- data/lib/python/repl.rb +47 -0
- data/lib/python/syntax.rb +201 -0
- data/lib/python/version.rb +3 -0
- data/python.gemspec +24 -0
- data/spec/python/parser/expression_spec.rb +28 -0
- data/spec/python/parser/indent_converter_spec.rb +36 -0
- data/spec/python/pyobject_spec.rb +20 -0
- data/spec/python/repl_spec.rb +14 -0
- data/spec/spec_helper.rb +1 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c712a1eeb1a31ce723c1fa4c0f6f499a3b04de9f
|
4
|
+
data.tar.gz: 0e3a1c2d0123e206072ee79a27d28fbe87d687bb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 75ab974d6430f574011536b451895263d6c62b82adf89eb73832b2393880aa9e550e8b3642e64d160e0787b4fffe230fec67db348d2cb0978c6194cb00e15d7e
|
7
|
+
data.tar.gz: 77723e0b9e1e3f01d22032442e0881bc3659d4955706b401a53962e839f9201ceddcfdb93508a35c60355f21013767bcb19c9cdca0395ffa85e0a37248c12540
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 TODO: Write your name
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Python
|
2
|
+
|
3
|
+
Python implemented in pure Ruby without any libraries.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'python'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install python
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Executable script "python.rb" will be installed in your bin path.
|
24
|
+
You can execute python program by two way.
|
25
|
+
One way is through command-line interpreter (REPL), and another is from source-file.
|
26
|
+
|
27
|
+
### Command-line interpreter (REPL)
|
28
|
+
|
29
|
+
$ python.rb
|
30
|
+
python.rb> 1 + 2
|
31
|
+
3
|
32
|
+
python.rb>
|
33
|
+
|
34
|
+
### Executing program from source file
|
35
|
+
|
36
|
+
```python
|
37
|
+
# demo.py
|
38
|
+
class A:
|
39
|
+
def __init__(self, x):
|
40
|
+
self.x = x
|
41
|
+
|
42
|
+
def add(self, d):
|
43
|
+
self.x = self.x + d
|
44
|
+
return self
|
45
|
+
|
46
|
+
a = A(1).add(2).add(3)
|
47
|
+
print(a.x)
|
48
|
+
```
|
49
|
+
|
50
|
+
$ python.rb demo.py
|
51
|
+
6
|
52
|
+
$
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
1. Fork it ( https://github.com/sawaken/python.rb/fork )
|
57
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
58
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
59
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
60
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/python.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'python'
|
4
|
+
|
5
|
+
if ARGV[0]
|
6
|
+
File.open(ARGV[0], "r") do |file|
|
7
|
+
Python::FileInterpreter.new(file.read).execute
|
8
|
+
end
|
9
|
+
else
|
10
|
+
repl = Python::REPL.new(STDOUT)
|
11
|
+
repl.start
|
12
|
+
while input = STDIN.gets
|
13
|
+
repl.read_eval_print(input)
|
14
|
+
repl.prompt
|
15
|
+
end
|
16
|
+
end
|
data/examples/demo.py
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
Feature: programmer calculate numerical expression
|
2
|
+
|
3
|
+
The programmer calculates numerical expressions through REPL console.
|
4
|
+
REPL console print caluculated number in one line.
|
5
|
+
|
6
|
+
Scenario Outline: calculate numerical expression
|
7
|
+
Given I started repl but didn't input anything
|
8
|
+
When I input "<expression>"
|
9
|
+
Then the output expect be "<evaluated>"
|
10
|
+
|
11
|
+
Scenarios: without operator
|
12
|
+
| expression | evaluated |
|
13
|
+
| 1 | 1 |
|
14
|
+
| 0 | 0 |
|
15
|
+
| -1 | -1 |
|
16
|
+
| +1 | 1 |
|
17
|
+
|
18
|
+
Scenarios: single operator
|
19
|
+
| expression | evaluated |
|
20
|
+
| 1 + 2 | 3 |
|
21
|
+
| 1 - 2 | -1 |
|
22
|
+
| 1 * 2 | 2 |
|
23
|
+
| 1 // 2 | 0 |
|
24
|
+
|
25
|
+
Scenarios: without space between tokens
|
26
|
+
| expression | evaluated |
|
27
|
+
| 1+2 | 3 |
|
28
|
+
| 100+100 | 200 |
|
29
|
+
|
30
|
+
Scenarios: double operator
|
31
|
+
| expression | evaluated |
|
32
|
+
| 1 + 2 + 3 | 6 |
|
33
|
+
| 1 - 2 - 3 | -4 |
|
34
|
+
| 1 * 2 * 3 | 6 |
|
35
|
+
| 9 // 3 // 2 | 1 |
|
36
|
+
|
37
|
+
Scenarios: priority
|
38
|
+
| expression | evaluated |
|
39
|
+
| 1 + 2 * 3 | 7 |
|
40
|
+
| 1 - 2 * 3 | -5 |
|
41
|
+
| 1 + 6 // 3 | 3 |
|
42
|
+
| 1 - 6 // 3 | -1 |
|
43
|
+
|
44
|
+
Scenarios: parentheses
|
45
|
+
| expression | evaluated |
|
46
|
+
| (1) | 1 |
|
47
|
+
| (-1) | -1 |
|
48
|
+
| (1 + 2) * (3 + 4) | 21 |
|
49
|
+
| (1 - (2 - 3)) | 2 |
|
50
|
+
| ((1 + 2) * 3) | 9 |
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: programmer execute python program in source file
|
2
|
+
|
3
|
+
Scenario: execute script file
|
4
|
+
Given in silence, I am on shell
|
5
|
+
When I start interpreter with argument "number_of_the_beast.py":
|
6
|
+
"""
|
7
|
+
six = 6
|
8
|
+
print(six * 100 + six * 10 + six)
|
9
|
+
"""
|
10
|
+
Then the output expect be "666"
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: programmer starts REPL console
|
2
|
+
|
3
|
+
As a programmer
|
4
|
+
I want to start REPL console
|
5
|
+
So that I can input and run python code
|
6
|
+
|
7
|
+
Scenario: start REPL
|
8
|
+
Given I am not yet inputting anything
|
9
|
+
When I start a new REPL console
|
10
|
+
Then I expect see "python.rb> "
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Feature: programmer use advanced calculator
|
2
|
+
|
3
|
+
The programmer calculates numerical expressions with conditions through REPL console.
|
4
|
+
REPL console print caluculated number in one line.
|
5
|
+
|
6
|
+
Scenario Outline: calculate numerical expression with conditions
|
7
|
+
Given I started repl but didn't input anything
|
8
|
+
When I input "<statement>"
|
9
|
+
Then the output expect be "<evaluated>"
|
10
|
+
|
11
|
+
Scenarios: with conditional operator
|
12
|
+
| statement | evaluated |
|
13
|
+
| 1 < 2 | True |
|
14
|
+
| 1 < 2 < 3 | True |
|
15
|
+
| 2 < 1 | False |
|
16
|
+
| 1 == 1 | True |
|
17
|
+
| 1 == 2 | False |
|
18
|
+
| 2 > 1 | True |
|
19
|
+
|
20
|
+
Scenarios: with not, and, or
|
21
|
+
| statement | evaluated |
|
22
|
+
| not 1 | False |
|
23
|
+
| not 0 | True |
|
24
|
+
| not True | False |
|
25
|
+
| not False | True |
|
26
|
+
| 1 and 2 | 2 |
|
27
|
+
| 1 and 0 | 0 |
|
28
|
+
| 1 or 0 | 1 |
|
29
|
+
| 0 or 1 | 1 |
|
30
|
+
| 1 or 2 | 1 |
|
31
|
+
|
32
|
+
Scenarios: with conditional expression
|
33
|
+
| statement | evaluated |
|
34
|
+
| 1 + 1 if 1 == 2 else 2 + 2 | 4 |
|
35
|
+
| 1 + 1 if 1 == 1 else 2 + 2 | 2 |
|
@@ -0,0 +1,42 @@
|
|
1
|
+
Feature: programmer use class
|
2
|
+
|
3
|
+
Scenario: define simple class and make instance
|
4
|
+
Given in silence, I am on shell
|
5
|
+
When I start interpreter with argument "defineclass.py":
|
6
|
+
"""
|
7
|
+
class A:
|
8
|
+
def mymethod(self, x):
|
9
|
+
return x
|
10
|
+
|
11
|
+
a = A()
|
12
|
+
print(a.mymethod(2))
|
13
|
+
"""
|
14
|
+
Then the output expect be "2"
|
15
|
+
|
16
|
+
Scenario: define class having static variable and use it
|
17
|
+
Given in silence, I am on shell
|
18
|
+
When I start interpreter with argument "staticclassvariable.py":
|
19
|
+
"""
|
20
|
+
class B:
|
21
|
+
staticvar = 123
|
22
|
+
|
23
|
+
b = B()
|
24
|
+
print(b.staticvar)
|
25
|
+
"""
|
26
|
+
Then the output expect be "123"
|
27
|
+
|
28
|
+
Scenario: define two class
|
29
|
+
Given in silence, I am on shell
|
30
|
+
When I start interpreter with argument "class2.py":
|
31
|
+
"""
|
32
|
+
class Y:
|
33
|
+
def __init__(self, val):
|
34
|
+
self.val = val
|
35
|
+
|
36
|
+
class X:
|
37
|
+
def __init__(self, val):
|
38
|
+
self.y = Y(val)
|
39
|
+
|
40
|
+
print(X(1).y.val)
|
41
|
+
"""
|
42
|
+
Then the output expect be "1"
|
@@ -0,0 +1,66 @@
|
|
1
|
+
Feature: programmer use closure
|
2
|
+
|
3
|
+
Scenario: calc fibonacci-number by using closure in script file
|
4
|
+
Given in silence, I am on shell
|
5
|
+
When I start interpreter with argument "fibonacci.py":
|
6
|
+
"""
|
7
|
+
def fib(n):
|
8
|
+
cond = n == 0 or n == 1
|
9
|
+
return n if cond else fib(n - 1) + fib(n - 2)
|
10
|
+
|
11
|
+
print(fib(10))
|
12
|
+
"""
|
13
|
+
Then the output expect be "55"
|
14
|
+
|
15
|
+
Scenario: play with a high-order function (a function returns a function) in script file
|
16
|
+
Given in silence, I am on shell
|
17
|
+
When I start interpreter with argument "highorder1.py":
|
18
|
+
"""
|
19
|
+
def curried_add(a):
|
20
|
+
def add(b):
|
21
|
+
return a + b
|
22
|
+
return add
|
23
|
+
|
24
|
+
print(curried_add(1)(2))
|
25
|
+
"""
|
26
|
+
Then the output expect be "3"
|
27
|
+
|
28
|
+
Scenario: play with a high-order function (a function takes a function) in script file
|
29
|
+
Given in silence, I am on shell
|
30
|
+
When I start interpreter with argument "highorder2.py":
|
31
|
+
"""
|
32
|
+
def twice(f, arg):
|
33
|
+
return f(f(arg))
|
34
|
+
|
35
|
+
def double(a):
|
36
|
+
return a * 2
|
37
|
+
|
38
|
+
print(twice(double, 5))
|
39
|
+
"""
|
40
|
+
Then the output expect be "20"
|
41
|
+
|
42
|
+
Scenario: argument-less function
|
43
|
+
Given in silence, I am on shell
|
44
|
+
When I start interpreter with argument "noargs.py":
|
45
|
+
"""
|
46
|
+
def hello():
|
47
|
+
print(12)
|
48
|
+
|
49
|
+
hello()
|
50
|
+
"""
|
51
|
+
Then the output expect be "12"
|
52
|
+
|
53
|
+
Scenario: double DEDENT
|
54
|
+
Given in silence, I am on shell
|
55
|
+
When I start interpreter with argument "doublededent.py":
|
56
|
+
"""
|
57
|
+
def foo():
|
58
|
+
def bar():
|
59
|
+
return 1
|
60
|
+
def baz():
|
61
|
+
return 2
|
62
|
+
|
63
|
+
a = foo()
|
64
|
+
print(0)
|
65
|
+
"""
|
66
|
+
Then the output expect be "0"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature: programmer use variables on REPL console
|
2
|
+
|
3
|
+
Scenario: use variables
|
4
|
+
Given I started repl but didn't input anything
|
5
|
+
When I input "x = 1"
|
6
|
+
Then the output expect be ""
|
7
|
+
When I input "x"
|
8
|
+
Then the output expect be "1"
|
9
|
+
When I input "x = x + 1"
|
10
|
+
Then the output expect be ""
|
11
|
+
When I input "x"
|
12
|
+
Then the output expect be "2"
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Out
|
2
|
+
def messages
|
3
|
+
@messages ||= []
|
4
|
+
end
|
5
|
+
|
6
|
+
def write(messge)
|
7
|
+
print(message)
|
8
|
+
end
|
9
|
+
|
10
|
+
def puts(message)
|
11
|
+
print(message + "\n")
|
12
|
+
end
|
13
|
+
|
14
|
+
def print(message)
|
15
|
+
messages << message
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Feature: programmer starts REPL console
|
20
|
+
#-----
|
21
|
+
|
22
|
+
Given(/^I am not yet inputting anything$/) do
|
23
|
+
@out = Out.new
|
24
|
+
@repl = Python::REPL.new(@out)
|
25
|
+
end
|
26
|
+
|
27
|
+
When(/^I start a new REPL console$/) do
|
28
|
+
@repl.start
|
29
|
+
end
|
30
|
+
|
31
|
+
Then(/^I expect see "([^"]*)"$/) do |message|
|
32
|
+
expect(@out.messages).to eq([message])
|
33
|
+
end
|
34
|
+
|
35
|
+
# Feature: programmer calculate numerical expression
|
36
|
+
#-----
|
37
|
+
|
38
|
+
Given(/^I started repl but didn't input anything$/) do
|
39
|
+
@out = Out.new
|
40
|
+
@repl = Python::REPL.new(@out)
|
41
|
+
@repl.start
|
42
|
+
end
|
43
|
+
|
44
|
+
When(/^I input "([^"]*)"$/) do |input|
|
45
|
+
@repl.read_eval_print(input)
|
46
|
+
end
|
47
|
+
|
48
|
+
Then(/^the output expect be "([^"]*)"$/) do |expected|
|
49
|
+
if expected != ""
|
50
|
+
expect(@out.messages.last).to eq(expected + "\n")
|
51
|
+
else
|
52
|
+
expect(@out.messages.last).to eq("")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Feature: programmer execute python program in source file
|
57
|
+
#-----
|
58
|
+
|
59
|
+
Given(/^in silence, I am on shell$/) do
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
When(/^I start interpreter with argument "([^"]*)":$/) do |filename, code|
|
64
|
+
@out = Out.new
|
65
|
+
printfunc = Python::Builtins::Func.make_instance{|obj| @out.puts(obj.inspect)}
|
66
|
+
Python::FileInterpreter.new(code, "print" => printfunc).execute
|
67
|
+
end
|