python 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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