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,22 @@
1
+ require 'python/parser/combinator'
2
+
3
+ module Python
4
+ module Parser
5
+ class IdentifierParser < Combinator
6
+ parser :identifier do # -> String
7
+ token(xid_start >> proc{|head|
8
+ many(xid_continue) >> proc{|rests|
9
+ ret(head + rests.inject("", &:+))
10
+ }})
11
+ end
12
+
13
+ parser :xid_start do # -> Char (1-lenght String)
14
+ any_char("A".."Z") | any_char("a".."z") | char("_")
15
+ end
16
+
17
+ parser :xid_continue do # -> Char (1-lenght String)
18
+ xid_start | any_char("0".."9")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,52 @@
1
+ module Python
2
+ module Parser
3
+ class IndentConverter
4
+ IndentConversionError = Class.new(RuntimeError)
5
+
6
+ INDENT = "$I"
7
+ DEDENT = "$D"
8
+ NEWLINE = "\n"
9
+
10
+ def convert(str)
11
+ stack = [0]
12
+ converted_lines = []
13
+ lines = str.gsub("\r\n", "\n").gsub("\r", "\n").split("\n") + [""]
14
+ lines.each do |line|
15
+ ilevel = indent_level(line)
16
+ if ilevel > stack.last
17
+ stack << ilevel
18
+ converted_lines << convert_line(line, INDENT)
19
+ elsif ilevel < stack.last
20
+ dedent_livel = 0
21
+ while ilevel < stack.last
22
+ dedent_livel += 1
23
+ stack.pop
24
+ end
25
+ unless ilevel == stack.last
26
+ raise IndentConversionError.new
27
+ end
28
+ converted_lines << convert_line(line, DEDENT * dedent_livel)
29
+ else
30
+ converted_lines << convert_line(line, "")
31
+ end
32
+ end
33
+ converted_lines.join(NEWLINE) + NEWLINE
34
+ end
35
+
36
+ def indent_level(line)
37
+ line.chars.take_while{|c| c == "\s" || c == "\t"}.inject(0) do |acc, c|
38
+ case c
39
+ when "\s"
40
+ acc + 1
41
+ when "\t"
42
+ acc - (acc % 8) + 8
43
+ end
44
+ end
45
+ end
46
+
47
+ def convert_line(line, token)
48
+ token + line.chars.drop_while{|c| c == "\s" || c == "\t"}.inject("", &:+)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ require 'python/parser/combinator'
2
+
3
+ module Python
4
+ module Parser
5
+ class IntegerParser < Combinator
6
+ parser :integer do # -> Integer
7
+ token(decimalinteger)
8
+ end
9
+
10
+ parser :decimalinteger do # -> Integer
11
+ nonzero = nonzerodigit >> proc{|head|
12
+ many(digit) >> proc{|tail|
13
+ ret((head + tail.inject("", &:+)).to_i)
14
+ }}
15
+ zero = many1(char("0")) >> proc{|zs| ret(0)}
16
+ nonzero | zero
17
+ end
18
+
19
+ parser :nonzerodigit do # -> Char (1-length String)
20
+ any_char("1".."9")
21
+ end
22
+
23
+ parser :digit do # -> Char (1-length String)
24
+ any_char("0".."9")
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,86 @@
1
+ require 'python/parser/combinator'
2
+ require 'python/syntax'
3
+ require 'python/parser/identifier'
4
+
5
+ module Python
6
+ module Parser
7
+ class StatementParser < Combinator
8
+
9
+ NEWLINE = token_str("\n")
10
+ INDENT = token_str("$I")
11
+ DEDENT = token_str("$D")
12
+
13
+ parser :file_input do # -> StatementList
14
+ many(NEWLINE | statement) >> proc{|stmts|
15
+ ret(Syntax::StatementList.new(stmts.select{|stmt| stmt.is_a?(Syntax::Statement)}))
16
+ }
17
+ end
18
+
19
+ parser :suite do # -> StatementList
20
+ stmt_list - NEWLINE | NEWLINE + INDENT + many1(statement) - many(NEWLINE) - DEDENT >> proc{|stmts|
21
+ ret(Syntax::StatementList.new(stmts))
22
+ }
23
+ end
24
+
25
+ parser :statement do # -> Statement
26
+ compound_stmt | stmt_list - NEWLINE
27
+ end
28
+
29
+ parser :compound_stmt do # -> Statement
30
+ classdef | funcdef
31
+ end
32
+
33
+ parser :funcdef do # -> Statement
34
+ token_str("def") + IdentifierParser.identifier - token_str("(") >> proc{|funcname|
35
+ separator_allow_empty(IdentifierParser.identifier, ",") - token_str(")") - token_str(":") >> proc{|params|
36
+ suite >> proc{|stmt|
37
+ ret(Syntax::Def.new(funcname, stmt, params))
38
+ }}}
39
+ end
40
+
41
+ parser :classdef do # -> Statement
42
+ token_str("class") + IdentifierParser.identifier >> proc{|classname|
43
+ ((token_str("(") + separator(ExpressionParser.expression, ",") - token_str(")")) | ret([])) - token_str(":") >> proc{|base_exps|
44
+ suite >> proc{|stmt|
45
+ ret(Syntax::ClassDef.new(classname, stmt, base_exps))
46
+ }}}
47
+ end
48
+
49
+ parser :stmt_list do # -> StatementList
50
+ separator(simple_stmt, ";") - (token_str(";") | ret(nil)) >> proc{|stmts|
51
+ ret(Syntax::StatementList.new(stmts))
52
+ }
53
+ end
54
+
55
+ parser :simple_stmt do # -> Statement
56
+ assignment_stmt | return_stmt | expression_stmt
57
+ end
58
+
59
+ parser :assignment_stmt do # -> Statement
60
+ ((IdentifierParser.identifier - token_str("=")) | (ExpressionParser::primary - token_str("="))) >> proc{|target|
61
+ ExpressionParser.expression >> proc{|exp|
62
+ case target
63
+ when String
64
+ ret(Syntax::AssignIdentifier.new(target, exp))
65
+ when Syntax::AttrRef
66
+ ret(Syntax::AssignAttr.new(target.receiver, target.attrname, exp))
67
+ else
68
+ p target
69
+ failure
70
+ end
71
+ }}
72
+ end
73
+
74
+ parser :return_stmt do # -> Statement
75
+ token_str("return") + (ExpressionParser.expression | ret(nil)) >> proc{|exp|
76
+ ret(Syntax::Return.new(exp))
77
+ }
78
+ end
79
+
80
+ parser :expression_stmt do
81
+ ExpressionParser.expression
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,129 @@
1
+ module Python
2
+ class PyObject < Hash
3
+ def initialize(attr={})
4
+ self.merge!(attr)
5
+ end
6
+
7
+ def make_instance(entity=nil, &entity_proc)
8
+ PyObject.new(:class => self, :entity => entity || entity_proc)
9
+ end
10
+
11
+ def entity
12
+ self[:entity]
13
+ end
14
+
15
+ def call(*args)
16
+ if self[:entity] && self[:entity].is_a?(Proc)
17
+ self[:entity].call(*args)
18
+ else
19
+ call_special_method("__call__", *args)
20
+ end
21
+ end
22
+
23
+ def call_special_method(method_name, *args)
24
+ get_special_attr(method_name).call(*args)
25
+ end
26
+
27
+ def get_special_attr(name)
28
+ if !self[:class]
29
+ raise "failed to get special attr #{name} from #{self}: #{self} doesn't have class"
30
+ elsif cls = self[:class].base_traverse{|cls| cls[name] && cls[name].datadescriptor?}
31
+ cls[name].get_get_method.call(cls[name], self, self[:class])
32
+ elsif cls = self[:class].base_traverse{|cls| cls[name]}
33
+ if cls[name].descriptor?
34
+ cls[name].get_get_method.call(cls[name], self, self[:class])
35
+ else
36
+ cls[name]
37
+ end
38
+ else
39
+ raise "failed to get special attr #{name} from #{self}: #{name} is not found"
40
+ end
41
+ end
42
+
43
+ def has_special_attr?(name)
44
+ get_special_attr(name)
45
+ rescue
46
+ nil
47
+ end
48
+
49
+ def get_attr(name)
50
+ if !self[:class]
51
+ raise "failed to get attr #{name} from #{self}: #{self} doesn't have class"
52
+ elsif cls = self[:class].base_traverse{|cls| cls[name] && cls[name].datadescriptor?}
53
+ cls[name].get_get_method.call(cls[name], self, self[:class])
54
+ elsif owner = self.base_traverse{|owner| owner[name]}
55
+ owner[name]
56
+ elsif cls = self[:class].base_traverse{|cls| cls[name]}
57
+ if cls[name].descriptor?
58
+ cls[name].get_get_method.call(cls[name], self, self[:class])
59
+ else
60
+ cls[name]
61
+ end
62
+ else
63
+ raise "failed to get attr #{name} from #{self}: #{name} is not found"
64
+ end
65
+ end
66
+
67
+ def has_attr?(name)
68
+ get_attr(name)
69
+ rescue
70
+ nil
71
+ end
72
+
73
+ def set_attr(name, pyobj)
74
+ if !self[:class]
75
+ raise "failed to set attr #{name} on #{self}: #{self} doesn't have class"
76
+ elsif cls = self[:class].base_traverse{|cls| cls[name] && cls[name].datadescriptor?}
77
+ cls[name].get_set_method.call(cls[name], self, pyobj)
78
+ else
79
+ self[name] = pyobj
80
+ end
81
+ end
82
+
83
+ def get_get_method
84
+ cls = self[:class].base_traverse{|cls| cls["__get__"]}
85
+ cls["__get__"]
86
+ end
87
+
88
+ def descriptor?
89
+ self[:class] && self[:class].base_traverse{|cls| cls["__get__"]}
90
+ end
91
+
92
+ def get_set_method
93
+ cls = self[:class].base_traverse{|cls| cls["__set__"] || cls["__delete__"]}
94
+ unless cls["__set__"]
95
+ raise "cannot set data on datadescriptor '#{cls}'"
96
+ else
97
+ cls["__set__"]
98
+ end
99
+ end
100
+
101
+ def datadescriptor?
102
+ descriptor? && self[:class].base_traverse{|cls| cls["__set__"] || cls["__delete__"]}
103
+ end
104
+
105
+ def base_traverse(&proc)
106
+ queue = [self]
107
+ until queue.empty?
108
+ cls = queue.pop
109
+ if judge = proc.call(cls)
110
+ return cls
111
+ end
112
+ queue += (cls[:bases] || [])
113
+ end
114
+ return nil
115
+ end
116
+
117
+ def inspect
118
+ if self[:name]
119
+ self[:name]
120
+ elsif self[:entity]
121
+ self[:entity].to_s
122
+ elsif self[:class] && self[:class][:name]
123
+ "<instance of:#{self[:class][:name]}"
124
+ else
125
+ super
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,47 @@
1
+ require 'python/parser/statement'
2
+ require 'python/environment'
3
+
4
+ module Python
5
+ class REPL
6
+ ParsingError = Class.new(RuntimeError)
7
+
8
+ def initialize(output)
9
+ @output = output
10
+ @env = Environment.new
11
+ end
12
+
13
+ def start
14
+ prompt
15
+ end
16
+
17
+ def read_eval_print(code)
18
+ print(eval(read(code)))
19
+ end
20
+
21
+ def read(code)
22
+ parser = Parser::StatementParser.stmt_list
23
+ result = parser.parse(code)
24
+ if result.is_a?(Parser::Succeeded) && result.rest.chomp == ""
25
+ result.parsed
26
+ else
27
+ raise ParsingError.new
28
+ end
29
+ end
30
+
31
+ def eval(exp)
32
+ exp.eval(@env)
33
+ end
34
+
35
+ def print(obj)
36
+ if obj == nil
37
+ @output.print ""
38
+ else
39
+ @output.puts obj.inspect
40
+ end
41
+ end
42
+
43
+ def prompt
44
+ @output.print "python.rb> "
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,201 @@
1
+ require 'python/pyobject'
2
+ require 'python/builtins'
3
+
4
+ module Python
5
+ module Syntax
6
+ class Statement
7
+ def attrs
8
+ []
9
+ end
10
+
11
+ def eval_proc
12
+ proc{}
13
+ end
14
+
15
+ def initialize(*args)
16
+ min_argc = attrs.take_while{|attr| attr.is_a?(Symbol)}.count
17
+ max_argc = attrs.flatten.count
18
+ unless min_argc <= args.length && args.length <= max_argc
19
+ raise "Argument error: failed to make instance of #{self.class.name}." +
20
+ "expected: #{attrs}, actual: #{args}"
21
+ end
22
+ attrs.flatten.zip(args).each do |name, val|
23
+ instance_variable_set("@#{name}".to_sym, val)
24
+ end
25
+ end
26
+
27
+ def eval(env)
28
+ instance_exec(env, &eval_proc)
29
+ end
30
+
31
+ def ==(other)
32
+ self.class == other.class && attrs.flatten.all? do |vname|
33
+ ivname = "@#{vname}".to_sym
34
+ self.instance_variable_get(ivname) == other.instance_variable_get(ivname)
35
+ end
36
+ end
37
+ end
38
+ Expression = Class.new(Statement)
39
+
40
+ def self.stmt(*attrs, &eval)
41
+ define_element_type(Statement, *attrs, &eval)
42
+ end
43
+
44
+ def self.exp(*attrs, &eval)
45
+ define_element_type(Expression, *attrs, &eval)
46
+ end
47
+
48
+ def self.define_element_type(base, *attrs, &eval_proc)
49
+ cls = Class.new(base)
50
+ cls.send(:define_method, :attrs) { attrs }
51
+ cls.send(:define_method, :eval_proc) { eval_proc }
52
+ cls.send(:attr_reader, *attrs.flatten)
53
+ return cls
54
+ end
55
+
56
+ def self.draw_syntax_tree(val, depth=0)
57
+ case val
58
+ when Statement
59
+ puts (" " * depth) + "Node<#{val.class.name}>:"
60
+ val.attrs.flatten.each do |attrname|
61
+ puts (" " * (depth + 1)) + "#{attrname}:"
62
+ draw_syntax_tree(val.instance_variable_get("@#{attrname}".to_sym), depth + 2)
63
+ end
64
+ when Array
65
+ val.each_with_index do |v, i|
66
+ puts (" " * (depth + 1)) + "[#{i}]"
67
+ draw_syntax_tree(v, depth + 2)
68
+ end
69
+ else
70
+ puts (" " * (depth + 1)) + val.to_s
71
+ end
72
+ end
73
+
74
+ # Exceptions possibly occurring when evaluating
75
+ PyBoolizeError = Class.new(RuntimeError)
76
+ PyNameError = Class.new(RuntimeError)
77
+ PyCallError = Class.new(RuntimeError)
78
+
79
+ def self.pytrue?(object)
80
+ boolized = object.call_special_method("__bool__")
81
+ if boolized == Builtins::True
82
+ return true
83
+ elsif boolized == Builtins::False
84
+ return false
85
+ else
86
+ raise PyBoolizeError.new
87
+ end
88
+ end
89
+
90
+ #--------------------
91
+ # AST-elements of Statements
92
+ #--------------------
93
+
94
+ StatementList = stmt(:stmts) do |env|
95
+ @stmts.inject(nil) do |acc, stmt|
96
+ stmt.eval(env)
97
+ end
98
+ end
99
+
100
+ AssignIdentifier = stmt(:name, :exp) do |env|
101
+ env.set(@name, @exp.eval(env))
102
+ nil
103
+ end
104
+
105
+ AssignAttr = stmt(:receiver, :attrname, :exp) do |env|
106
+ @receiver.eval(env).set_attr(@attrname, @exp.eval(env))
107
+ nil
108
+ end
109
+
110
+ Def = stmt(:name, :stat, :fix_param_names, [:rest_param_name]) do |env|
111
+ entity = {:fix_param_names => @fix_param_names,
112
+ :rest_param_name => @rest_param_name,
113
+ :stat => @stat,
114
+ :env => env.getlink}
115
+ env.set(@name, Builtins::Func.make_instance(entity))
116
+ nil
117
+ end
118
+
119
+ ClassDef = stmt(:name, :stat, :base_exps) do |env|
120
+ bases = @base_exps.map{|e| e.eval(env)}
121
+ klassenv = ClassEnvironment.new(:parent => env)
122
+ stat.eval(klassenv)
123
+ klass = PyObject.new(klassenv.merge(:class => Builtins::Type, :bases => bases))
124
+ env.set(@name, klass)
125
+ nil
126
+ end
127
+
128
+ Return = stmt([:exp]) do |env|
129
+ res = if @exp then @exp.eval(env) else Builtins::None end
130
+ throw :return, res
131
+ end
132
+
133
+ #--------------------
134
+ # AST-elements of Expressions
135
+ #--------------------
136
+
137
+ Apply = exp(:callee_exp, :arg_exps) do |env|
138
+ @callee_exp.eval(env).call(*@arg_exps.map{|e| e.eval(env)})
139
+ end
140
+
141
+ AttrRef = exp(:receiver, :attrname) do |env|
142
+ @receiver.eval(env).get_attr(@attrname)
143
+ end
144
+
145
+ RefIdentifier = exp(:name) do |env|
146
+ if res = env.resolve(@name)
147
+ res
148
+ else
149
+ raise PyNameError.new("Unbound variable: #{@name}")
150
+ end
151
+ end
152
+
153
+ Conditional = exp(:cond_exp, :true_exp, :false_exp) do |env|
154
+ if Syntax.pytrue?(@cond_exp.eval(env))
155
+ @true_exp.eval(env)
156
+ else
157
+ @false_exp.eval(env)
158
+ end
159
+ end
160
+
161
+ UnaryOp = exp(:op_name, :exp) do |env|
162
+ @exp.eval(env).call_special_method(@op_name)
163
+ end
164
+
165
+ BinaryOp = exp(:op_name, :left_exp, :right_exp) do |env|
166
+ left = @left_exp.eval(env)
167
+ right = @right_exp.eval(env)
168
+ left.call_special_method(@op_name, right)
169
+ end
170
+
171
+ And = exp(:left_exp, :right_exp) do |env|
172
+ left = @left_exp.eval(env)
173
+ if !Syntax.pytrue?(left)
174
+ left
175
+ else
176
+ @right_exp.eval(env)
177
+ end
178
+ end
179
+
180
+ Or = exp(:left_exp, :right_exp) do |env|
181
+ left = @left_exp.eval(env)
182
+ if Syntax.pytrue?(left)
183
+ left
184
+ else
185
+ @right_exp.eval(env)
186
+ end
187
+ end
188
+
189
+ Not = exp(:cond_exp) do |env|
190
+ if Syntax.pytrue?(@cond_exp.eval(env))
191
+ Builtins::False
192
+ else
193
+ Builtins::True
194
+ end
195
+ end
196
+
197
+ LiteralObject = exp(:object) do |env|
198
+ @object
199
+ end
200
+ end
201
+ end