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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +60 -0
  6. data/Rakefile +2 -0
  7. data/bin/python.rb +16 -0
  8. data/examples/demo.py +10 -0
  9. data/examples/fibonacci.py +5 -0
  10. data/examples/twoclass.py +9 -0
  11. data/features/programmer_caclulate_numerical_expression.feature +50 -0
  12. data/features/programmer_execute_from_source_file.feature +10 -0
  13. data/features/programmer_starts_repl_console.feature +10 -0
  14. data/features/programmer_use_advanced_calculator.feature +35 -0
  15. data/features/programmer_use_class.feature +42 -0
  16. data/features/programmer_use_closure.feature +66 -0
  17. data/features/programmer_use_variables.feature +12 -0
  18. data/features/step_definitions/calculate_numerical_steps.rb +67 -0
  19. data/features/support/env.rb +2 -0
  20. data/lib/python.rb +10 -0
  21. data/lib/python/builtins.rb +72 -0
  22. data/lib/python/environment.rb +42 -0
  23. data/lib/python/file_interpreter.rb +29 -0
  24. data/lib/python/parser/combinator.rb +206 -0
  25. data/lib/python/parser/expression.rb +125 -0
  26. data/lib/python/parser/identifier.rb +22 -0
  27. data/lib/python/parser/indent_converter.rb +52 -0
  28. data/lib/python/parser/integer.rb +28 -0
  29. data/lib/python/parser/statement.rb +86 -0
  30. data/lib/python/pyobject.rb +129 -0
  31. data/lib/python/repl.rb +47 -0
  32. data/lib/python/syntax.rb +201 -0
  33. data/lib/python/version.rb +3 -0
  34. data/python.gemspec +24 -0
  35. data/spec/python/parser/expression_spec.rb +28 -0
  36. data/spec/python/parser/indent_converter_spec.rb +36 -0
  37. data/spec/python/pyobject_spec.rb +20 -0
  38. data/spec/python/repl_spec.rb +14 -0
  39. data/spec/spec_helper.rb +1 -0
  40. metadata +125 -0
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
+ require 'python'
@@ -0,0 +1,10 @@
1
+ require "python/version"
2
+ require "python/repl"
3
+ require "python/parser/expression"
4
+ require "python/syntax"
5
+ require "python/builtins"
6
+ require "python/file_interpreter"
7
+
8
+ module Python
9
+ # Your code goes here...
10
+ end
@@ -0,0 +1,72 @@
1
+ require 'python/pyobject'
2
+
3
+ module Python
4
+ module Builtins
5
+ # Primitive objects
6
+ Func = PyObject.new(:name => "Func")
7
+ Int = PyObject.new(:name => "Int")
8
+ Bool = PyObject.new(:name => "Bool", :bases => [Int])
9
+
10
+ None = PyObject.new(:name => "None")
11
+ True = PyObject.new(:class => Bool, :name => "True", :entity => 1)
12
+ False = PyObject.new(:class => Bool, :name => "False", :entity => 0)
13
+
14
+ Type = PyObject.new(:name => "Type")
15
+
16
+
17
+ # Primitiv functions
18
+ AddTwoInt = Func.make_instance{|a, b| Int.make_instance(a.entity + b.entity)}
19
+ SubTwoInt = Func.make_instance{|a, b| Int.make_instance(a.entity - b.entity)}
20
+ MulTwoInt = Func.make_instance{|a, b| Int.make_instance(a.entity * b.entity)}
21
+ FloordivTwoInt = Func.make_instance{|a, b| Int.make_instance(a.entity / b.entity)}
22
+ PosInt = Func.make_instance{|n| Int.make_instance(+ n.entity)}
23
+ NegInt = Func.make_instance{|n| Int.make_instance(- n.entity)}
24
+ EQInt = Func.make_instance{|a, b| a.entity == b.entity ? True : False}
25
+ LTInt = Func.make_instance{|a, b| a.entity < b.entity ? True : False}
26
+ GTInt = Func.make_instance{|a, b| a.entity > b.entity ? True : False}
27
+ IntToBool = Func.make_instance{|n| n.entity == 0 ? False : True}
28
+
29
+ Print = Func.make_instance{|o| puts(o.inspect)}
30
+ ClosureCall = Func.make_instance do |f, *args|
31
+ if f.entity[:rest_param_name]
32
+ unless f.entity[:fix_param_names].length <= args.length
33
+ raise Syntax::PyCallError.new
34
+ end
35
+ else
36
+ unless f.entity[:fix_param_names].length == args.length
37
+ raise Syntax::PyCallError.new
38
+ end
39
+ end
40
+ bind = f.entity[:fix_param_names].zip(args).to_h
41
+ env = Environment.new(bind.merge(:parent => f.entity[:env]))
42
+ catch(:return) { f.entity[:stat].eval(env) } || None
43
+ end
44
+
45
+ MakeInstance = Func.make_instance do |cls, *args|
46
+ if class_having_new = cls.base_traverse{|cls| cls["__new__"]}
47
+ madeobj = class_having_new["__new__"].call(*args)
48
+ else
49
+ madeobj = PyObject.new(:class => cls)
50
+ end
51
+ if madeobj[:class] == cls && madeobj.has_special_attr?("__init__")
52
+ madeobj.call_special_method("__init__", *args)
53
+ end
54
+ madeobj
55
+ end
56
+
57
+ # Method binds of primitive functions
58
+ Func["__get__"] = Func.make_instance{|_self, obj, objtype| Func.make_instance{|*args| _self.call(obj, *args)}}
59
+ Func["__call__"] = Func.make_instance{|_self, *args| ClosureCall.call(_self, *args)}
60
+ Int["__add__"] = Func.make_instance{|_self, other| AddTwoInt.call(_self, other)}
61
+ Int["__sub__"] = Func.make_instance{|_self, other| SubTwoInt.call(_self, other)}
62
+ Int["__mul__"] = Func.make_instance{|_self, other| MulTwoInt.call(_self, other)}
63
+ Int["__floordiv__"] = Func.make_instance{|_self, other| FloordivTwoInt.call(_self, other)}
64
+ Int["__pos__"] = Func.make_instance{|_self| PosInt.call(_self)}
65
+ Int["__neg__"] = Func.make_instance{|_self| NegInt.call(_self)}
66
+ Int["__bool__"] = Func.make_instance{|_self| IntToBool.call(_self)}
67
+ Int["__eq__"] = Func.make_instance{|_self, other| EQInt.call(_self, other)}
68
+ Int["__lt__"] = Func.make_instance{|_self, other| LTInt.call(_self, other)}
69
+ Int["__gt__"] = Func.make_instance{|_self, other| GTInt.call(_self, other)}
70
+ Type["__call__"] = Func.make_instance{|_self, *args| MakeInstance.call(_self, *args)}
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ require 'python/builtins'
2
+
3
+ module Python
4
+ class Environment < Hash
5
+ def initialize(initial_attr={})
6
+ self.merge!(initial_attr)
7
+ end
8
+
9
+ def resolve(name)
10
+ if self[name]
11
+ self[name]
12
+ elsif self[:parent]
13
+ self[:parent].resolve(name)
14
+ else
15
+ case name
16
+ when "True"
17
+ Builtins::True
18
+ when "False"
19
+ Builtins::False
20
+ when "None"
21
+ Builtins::None
22
+ when "print"
23
+ Builtins::Print
24
+ end
25
+ end
26
+ end
27
+
28
+ def set(name, pyobj)
29
+ self[name] = pyobj
30
+ end
31
+
32
+ def getlink
33
+ self
34
+ end
35
+ end
36
+
37
+ class ClassEnvironment < Environment
38
+ def getlink
39
+ self[:parent].getlink
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,29 @@
1
+ require 'python/parser/statement'
2
+ require 'python/parser/indent_converter'
3
+ require 'python/environment'
4
+
5
+ module Python
6
+ class FileInterpreter
7
+ ParsingError = Class.new(RuntimeError)
8
+
9
+ def initialize(code, bind={})
10
+ @code = code
11
+ @bind = bind
12
+ end
13
+
14
+ def parse
15
+ parser = Parser::StatementParser.file_input
16
+ result = parser.parse(Parser::IndentConverter.new.convert(@code))
17
+
18
+ if result.is_a?(Parser::Succeeded) && result.rest == ""
19
+ result.parsed
20
+ else
21
+ raise ParsingError.new
22
+ end
23
+ end
24
+
25
+ def execute
26
+ parse().eval(Environment.new(@bind))
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,206 @@
1
+ module Python
2
+ module Parser
3
+ Result = Class.new
4
+ Failed = Class.new(Result)
5
+ Succeeded = Class.new(Result)
6
+
7
+ class Succeeded
8
+ attr_reader :parsed, :rest
9
+ def initialize(parsed, rest)
10
+ @parsed, @rest = parsed, rest
11
+ end
12
+ end
13
+
14
+ class Combinator
15
+ ParserDefinitionError = Class.new(RuntimeError)
16
+
17
+ attr_accessor :f
18
+ def initialize(&proc)
19
+ @f = proc
20
+ end
21
+
22
+ def parse(inp)
23
+ @f.call(inp)
24
+ end
25
+
26
+ def >>(proc)
27
+ self.class.so(self, &proc)
28
+ end
29
+
30
+ def |(other)
31
+ self.class.either(self, other)
32
+ end
33
+
34
+ def +(other)
35
+ self.class.discardl(self, other)
36
+ end
37
+
38
+ def -(other)
39
+ self.class.discardr(self, other)
40
+ end
41
+
42
+ def self.ret(something)
43
+ new{|inp| Succeeded.new(something, inp)}
44
+ end
45
+
46
+ def self.failure
47
+ new{|inp| Failed.new}
48
+ end
49
+
50
+ def self.item
51
+ new{|inp| inp.size == 0 ? Failed.new : Succeeded.new(inp[0], inp[1, inp.size - 1])}
52
+ end
53
+
54
+ def self.so(parser, &proc)
55
+ new{|inp|
56
+ case result = parser.parse(inp)
57
+ when Failed
58
+ result
59
+ when Succeeded
60
+ proc.call(result.parsed).parse(result.rest)
61
+ else
62
+ raise "error."
63
+ end
64
+ }
65
+ end
66
+
67
+ def self.either(parser1, parser2)
68
+ new{|inp|
69
+ case result = parser1.parse(inp)
70
+ when Failed
71
+ parser2.parse(inp)
72
+ when Succeeded
73
+ result
74
+ else
75
+ raise "error."
76
+ end
77
+ }
78
+ end
79
+
80
+ def self.discardl(parser1, parser2)
81
+ parser1 >> proc{parser2}
82
+ end
83
+
84
+ def self.discardr(parser1, parser2)
85
+ parser1 >> proc{|x|
86
+ parser2 >> proc{
87
+ ret(x)
88
+ }}
89
+ end
90
+
91
+ def self.separator(element_parser, separating_token_str)
92
+ element_parser >> proc{|x|
93
+ many(token_str(separating_token_str) + element_parser) >> proc{|xs|
94
+ ret([x] + xs)
95
+ }}
96
+ end
97
+
98
+ def self.separator_allow_empty(element_parser, separating_token_str)
99
+ separator(element_parser, separating_token_str) | ret([])
100
+ end
101
+
102
+ def self.optional(parser)
103
+ (parser >> proc{|x| ret([x])}) | ret([])
104
+ end
105
+
106
+ def self.many(parser)
107
+ many1(parser) | ret([])
108
+ end
109
+
110
+ def self.many1(parser)
111
+ parser >> proc{|x|
112
+ many(parser) >> proc{|xs|
113
+ ret([x] + xs)
114
+ }}
115
+ end
116
+
117
+ def self.sat(&proc)
118
+ item >> proc{|c|
119
+ proc.call(c) ? ret(c) : failure
120
+ }
121
+ end
122
+
123
+ def self.char(char)
124
+ sat{|c| c == char}
125
+ end
126
+
127
+ def self.any_char(x)
128
+ case x
129
+ when String
130
+ chars = x.cahrs.map{|c| char(c)}
131
+ if chars.length < 0
132
+ raise ParserDefinitionError.new
133
+ else
134
+ chars.inject(&:|)
135
+ end
136
+ when Range
137
+ chars = x.map{|c| char(c)}
138
+ if chars.length < 0
139
+ raise ParserDefinitionError.new
140
+ else
141
+ chars.inject(&:|)
142
+ end
143
+ else
144
+ raise ParserDefinitionError.new
145
+ end
146
+ end
147
+
148
+ def self.string(str)
149
+ if str.size == 0
150
+ ret(str)
151
+ else
152
+ char(str[0]) >> proc{|c|
153
+ string(str[1, str.size - 1]) >> proc{
154
+ ret(str)
155
+ }
156
+ }
157
+ end
158
+ end
159
+
160
+ def self.whitespace
161
+ many(char("\s") | char("\t")) >> proc{|ws|
162
+ ret(:whitespace)
163
+ }
164
+ end
165
+
166
+ def self.token(parser)
167
+ whitespace >> proc{
168
+ parser >> proc{|x|
169
+ whitespace >> proc{
170
+ ret(x)
171
+ }}}
172
+ end
173
+
174
+ def self.token_str(str)
175
+ token(string(str))
176
+ end
177
+
178
+ def self.binopl(parser, op_proc_parser)
179
+ rest = proc{|a|
180
+ op_proc_parser >> proc{|f|
181
+ parser >> proc{|b|
182
+ rest.call(f.call(a, b))
183
+ }} | ret(a)
184
+ }
185
+ parser >> proc{|a|
186
+ rest.call(a)
187
+ }
188
+ end
189
+
190
+ def self.parser(name, &proc)
191
+ @cache ||= {}
192
+ spcls = class << self; self end
193
+ spcls.send(:define_method, name) do |*args|
194
+ key = [name, args]
195
+ if @cache[key]
196
+ return @cache[key]
197
+ else
198
+ @cache[key] = self.new{}
199
+ @cache[key].f = proc.call(*args).f
200
+ return @cache[key]
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,125 @@
1
+ require 'python/parser/combinator'
2
+ require 'python/parser/integer'
3
+ require 'python/parser/identifier'
4
+ require 'python/syntax'
5
+ require 'python/builtins'
6
+
7
+ module Python
8
+ module Parser
9
+ class ExpressionParser < Combinator
10
+
11
+ parser :expression do
12
+ conditional_expression
13
+ end
14
+
15
+ parser :conditional_expression do
16
+ or_test >> proc{|true_exp|
17
+ token_str("if") + or_test - token_str("else") >> proc{|cond|
18
+ expression >> proc{|false_exp|
19
+ ret(Syntax::Conditional.new(cond, true_exp, false_exp))
20
+ }} | ret(true_exp)
21
+ }
22
+ end
23
+
24
+ parser :or_test do # -> Expression
25
+ orop = token_str("or") + ret(proc{|l, r| Syntax::Or.new(l, r)})
26
+ binopl(and_test, orop)
27
+ end
28
+
29
+ parser :and_test do # -> Expression
30
+ andop = token_str("and") + ret(proc{|l, r| Syntax::And.new(l, r)})
31
+ binopl(not_test, andop)
32
+ end
33
+
34
+ parser :not_test do # -> Expression
35
+ token_str("not") + not_test >> proc{|exp|
36
+ ret(Syntax::Not.new(exp))
37
+ } | comparison
38
+ end
39
+
40
+ parser :comparison do # -> Expression
41
+ a_expr >> proc{|head|
42
+ many(comp_operator >> proc{|op| a_expr >> proc{|exp| ret([op, exp])}}) >> proc{|tail|
43
+ if tail.length == 0
44
+ ret(head)
45
+ else
46
+ exps = [head] + tail.map{|op, exp| exp}
47
+ ops = tail.map{|op, exp| op}
48
+ comps = exps.each_cons(2).zip(ops).map{|p, op| Syntax::BinaryOp.new(op, p[0], p[1])}
49
+ ret(comps.inject{|l, r| Syntax::And.new(l, r)})
50
+ end
51
+ }
52
+ }
53
+ end
54
+
55
+ parser :comp_operator do # -> Symbol
56
+ token_str("==") + ret("__eq__") |
57
+ token_str(">=") + ret("__ge__") |
58
+ token_str("<=") + ret("__le__") |
59
+ token_str("<") + ret("__lt__") |
60
+ token_str(">") + ret("__gt__") |
61
+ token_str("!=") + ret("__ne__")
62
+ end
63
+
64
+ parser :a_expr do # -> Expression
65
+ addop = token_str("+") + ret(lambda{|l, r| Syntax::BinaryOp.new("__add__", l, r)})
66
+ subop = token_str("-") + ret(lambda{|l, r| Syntax::BinaryOp.new("__sub__", l, r)})
67
+ binopl(m_expr, addop | subop)
68
+ end
69
+
70
+ parser :m_expr do # -> Expression
71
+ mulop = token_str("*") + ret(proc{|l, r| Syntax::BinaryOp.new("__mul__", l, r)})
72
+ fdivop = token_str("//") + ret(proc{|l, r| Syntax::BinaryOp.new("__floordiv__", l, r)})
73
+ binopl(u_expr, mulop | fdivop)
74
+ end
75
+
76
+ parser :u_expr do # -> Expression
77
+ posop = token_str("+") + ret("__pos__")
78
+ negop = token_str("-") + ret("__neg__")
79
+ (posop | negop) >> proc{|op_name|
80
+ u_expr >> proc{|exp|
81
+ ret(Syntax::UnaryOp.new(op_name, exp))
82
+ }} | primary
83
+ end
84
+
85
+ parser :primary do
86
+ atom >> proc{|at|
87
+ many(attributeref | call) >> proc{|ps|
88
+ ret(ps.inject(at){|a, p| p.call(a)})
89
+ }}
90
+ end
91
+
92
+ parser :attributeref do
93
+ token_str(".") + IdentifierParser.identifier >> proc{|ident|
94
+ ret(proc{|receiver| Syntax::AttrRef.new(receiver, ident)})
95
+ }
96
+ end
97
+
98
+ parser :call do # -> Apply
99
+ token_str("(") + positional_arguments - token_str(")") >> proc{|args|
100
+ ret(proc{|callee| Syntax::Apply.new(callee, args)})
101
+ }
102
+ end
103
+
104
+ parser :positional_arguments do # -> [Expression]
105
+ separator_allow_empty(expression, ",")
106
+ end
107
+
108
+ parser :atom do
109
+ identifier | integerliteral | parenth_form
110
+ end
111
+
112
+ parser :identifier do
113
+ IdentifierParser.identifier >> proc{|ident| ret(Syntax::RefIdentifier.new(ident))}
114
+ end
115
+
116
+ parser :integerliteral do
117
+ IntegerParser.integer >> proc{|n| ret(Syntax::LiteralObject.new(Builtins::Int.make_instance(n)))}
118
+ end
119
+
120
+ parser :parenth_form do
121
+ token_str("(") + expression - token_str(")")
122
+ end
123
+ end
124
+ end
125
+ end