python 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|