p-lang 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +38 -8
- data/Rakefile +0 -1
- data/VERSION.yml +3 -3
- data/bin/p-lang +36 -8
- data/lib/p-lang.rb +16 -26
- data/lib/parser/error.rb +32 -0
- data/lib/parser/lexer.rb +245 -0
- data/lib/parser/node.rb +23 -0
- data/lib/parser/syntax_analyser.rb +378 -0
- data/lib/parser/token.rb +19 -0
- data/lib/vm/core/pboolean.rb +27 -0
- data/lib/vm/core/pchar.rb +13 -0
- data/lib/vm/core/pdecimal.rb +130 -0
- data/lib/vm/core/pinteger.rb +125 -0
- data/lib/vm/core/pio.rb +16 -0
- data/lib/vm/core/plist.rb +33 -0
- data/lib/vm/core/pstring.rb +39 -0
- data/lib/vm/environment.rb +67 -42
- data/lib/vm/interpreter.rb +209 -0
- data/lib/vm/pfunctions.rb +46 -0
- data/lib/vm/plambda.rb +42 -0
- data/lib/vm/pobject.rb +44 -42
- data/test/test_lexer +34 -0
- data/test/test_lexer.rb +19 -0
- data/test/test_lexer_tokens +34 -0
- data/test/{test_parser_ok.txt → test_parser} +26 -27
- data/test/test_parser.rb +6 -13
- data/test/test_parser_build +73 -0
- data/test/test_vm +29 -0
- data/test/test_vm.rb +7 -10
- data/test/test_vm_result +29 -0
- metadata +31 -38
- data/bin/teste.p +0 -3
- data/lib/parser/ast.rb +0 -168
- data/lib/parser/nodes.rb +0 -212
- data/lib/parser/p-lang.treetop +0 -197
- data/lib/vm/perror.rb +0 -8
- data/lib/vm/proc.rb +0 -67
- data/lib/vm/std/pdecimal.rb +0 -42
- data/lib/vm/std/pinteger.rb +0 -42
- data/lib/vm/std/pio.rb +0 -17
- data/lib/vm/std/pstring.rb +0 -160
- data/lib/vm/vm.rb +0 -233
- data/test/test_parser_build.txt +0 -72
- data/test/test_vm_programs.txt +0 -64
- data/test/test_vm_results.txt +0 -64
@@ -0,0 +1,209 @@
|
|
1
|
+
module PLang
|
2
|
+
module VM
|
3
|
+
class Interpreter
|
4
|
+
include PFunctions
|
5
|
+
|
6
|
+
def initialize(ast)
|
7
|
+
@ast = ast
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute!
|
11
|
+
result = nil
|
12
|
+
env = load_basic_environment
|
13
|
+
@ast.each do |ast|
|
14
|
+
result = execute(ast, env)
|
15
|
+
end
|
16
|
+
result
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def load_basic_environment
|
22
|
+
@env = Environment.new
|
23
|
+
libraries = methods.grep /^add_to_interpreter/
|
24
|
+
libraries.each do |library|
|
25
|
+
send(library)
|
26
|
+
end
|
27
|
+
@env
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute(ast, env)
|
31
|
+
if ast.class == PLang::Parser::Node
|
32
|
+
case ast.type
|
33
|
+
when :integer, :decimal, :string, :char, :boolean
|
34
|
+
execute_literal(ast.type, ast.value, env)
|
35
|
+
when :object
|
36
|
+
execute_object(ast.id, ast.params, env)
|
37
|
+
when :list
|
38
|
+
execute_list(ast.elements, env)
|
39
|
+
when :if
|
40
|
+
execute_if(ast.condition, ast.true_expr, ast.false_expr, env)
|
41
|
+
when :lambda
|
42
|
+
execute_lambda(ast.params, ast.body, ast.where, ast.next_lambda, env)
|
43
|
+
when :call
|
44
|
+
execute_call(ast.lambda, ast.params, env)
|
45
|
+
when :object_message
|
46
|
+
execute_object_message(ast.object, ast.message, env)
|
47
|
+
when :let
|
48
|
+
execute_let(ast.lhs, ast.rhs, env)
|
49
|
+
when :id
|
50
|
+
execute_id(ast.value, env)
|
51
|
+
when :begin
|
52
|
+
execute_begin(ast.expressions, env)
|
53
|
+
when :add, :sub, :mul, :div, :equal, :diff, :major, :major_equal, :minor, :minor_equal
|
54
|
+
execute_binop(ast.type, ast.lhs, ast.rhs, env)
|
55
|
+
when :and, :or
|
56
|
+
execute_binop("_#{ast.type}".to_sym, ast.lhs, ast.rhs, env)
|
57
|
+
when :not
|
58
|
+
execute_unop("_#{ast.type}".to_sym, ast.lhs, env)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
ast
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def execute_if(cond, true_expr, false_expr, env)
|
66
|
+
cond = execute(cond, env)
|
67
|
+
if cond.id == :boolean
|
68
|
+
if cond.params[0] == :true
|
69
|
+
execute(true_expr, env)
|
70
|
+
else
|
71
|
+
execute(false_expr, env)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
raise "IfError"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def execute_binop(type, lhs, rhs, env)
|
79
|
+
execute_call(execute_object_message(lhs,PLang::Parser::Node.new(:id, {:value => type}), env), [rhs], env)
|
80
|
+
end
|
81
|
+
|
82
|
+
def execute_unop(type, lhs, env)
|
83
|
+
execute_call(execute_object_message(lhs,PLang::Parser::Node.new(:id, {:value => type}), env), [], env)
|
84
|
+
end
|
85
|
+
|
86
|
+
def execute_literal(type, value, env)
|
87
|
+
PObject.new(type, [value])
|
88
|
+
end
|
89
|
+
|
90
|
+
def execute_object(id, params, env)
|
91
|
+
object_params = []
|
92
|
+
params.each do |param|
|
93
|
+
object_params << execute(param, env)
|
94
|
+
end
|
95
|
+
case id.value
|
96
|
+
when :integer, :decimal, :string, :char, :boolean
|
97
|
+
if object_params.length == 1 and object_params[0].id == id.value
|
98
|
+
PObject.new(id.value, object_params[0].params)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
PObject.new(id.value, object_params)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def execute_lambda(params, body, where, next_lambda, env)
|
106
|
+
lambda = []
|
107
|
+
lambda << PLambda.new do |values|
|
108
|
+
new_env = Environment.new
|
109
|
+
new_env.parent = env
|
110
|
+
values.each_with_index do |value, i|
|
111
|
+
case params[i].type
|
112
|
+
when :id
|
113
|
+
unless params[i].value == :_
|
114
|
+
new_env.set_var(params[i].value, value)
|
115
|
+
end
|
116
|
+
when :object
|
117
|
+
new_env.set_object_var(params[i], value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
where.each do |w|
|
121
|
+
execute(w, new_env)
|
122
|
+
end
|
123
|
+
execute(body, new_env)
|
124
|
+
end
|
125
|
+
params.each do |param|
|
126
|
+
case param.type
|
127
|
+
when :id
|
128
|
+
lambda[0].form << nil
|
129
|
+
else
|
130
|
+
lambda[0].form << param
|
131
|
+
end
|
132
|
+
end
|
133
|
+
if next_lambda
|
134
|
+
lambda |= execute(next_lambda, env).params
|
135
|
+
end
|
136
|
+
PObject.new(:lambda, lambda)
|
137
|
+
end
|
138
|
+
|
139
|
+
def execute_call(lambda, params, env)
|
140
|
+
values = []
|
141
|
+
params.each do |param|
|
142
|
+
values << execute(param, env)
|
143
|
+
end
|
144
|
+
lambda = execute(lambda, env)
|
145
|
+
lambda.params.each do |lamb|
|
146
|
+
return lamb.call(values) if lamb.call?(values)
|
147
|
+
end
|
148
|
+
raise "CallError"
|
149
|
+
end
|
150
|
+
|
151
|
+
def execute_let(lhs, rhs, env)
|
152
|
+
case lhs.type
|
153
|
+
when :id
|
154
|
+
if lhs.value == :_
|
155
|
+
raise "WildCardError"
|
156
|
+
else
|
157
|
+
begin
|
158
|
+
env.set_var(lhs.value, execute(rhs, env))
|
159
|
+
rescue Exception => e
|
160
|
+
var = env.get_var(lhs.value)
|
161
|
+
if var.id == :lambda
|
162
|
+
env.add_lambda(lhs.value, execute(rhs, env))
|
163
|
+
else
|
164
|
+
raise e
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
when :object
|
169
|
+
env.set_object_var(lhs, execute(rhs, env))
|
170
|
+
when :object_message
|
171
|
+
params = [lhs.object, PLang::Parser::Node.new(:object, {:id => PLang::Parser::Node.new(:id, {:value => lhs.message.value}), :params => []})]
|
172
|
+
lamb = execute_lambda(params, rhs, [], nil, env)
|
173
|
+
begin
|
174
|
+
env.set_var(:get_object_message, lamb)
|
175
|
+
rescue
|
176
|
+
env.add_lambda(:get_object_message, lamb)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def execute_id(id, env)
|
182
|
+
env.get_var(id)
|
183
|
+
end
|
184
|
+
|
185
|
+
def execute_begin(expressions, env)
|
186
|
+
ret = nil
|
187
|
+
expressions.each do |expr|
|
188
|
+
ret = execute(expr, env)
|
189
|
+
end
|
190
|
+
ret
|
191
|
+
end
|
192
|
+
|
193
|
+
def execute_list(elements, env)
|
194
|
+
element = elements.delete_at(0)
|
195
|
+
if element
|
196
|
+
PObject.new(:list, [execute(element, env), execute_list(elements, env)])
|
197
|
+
else
|
198
|
+
PObject.new(:empty, [])
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def execute_object_message(object, message, env)
|
203
|
+
get_object_message = PLang::Parser::Node.new(:id, {:value => :get_object_message})
|
204
|
+
message = PLang::Parser::Node.new(:object, {:id => PLang::Parser::Node.new(:id, {:value => message.value}), :params => []})
|
205
|
+
execute_call(get_object_message, [object, message], env)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module PLang
|
2
|
+
module VM
|
3
|
+
module PFunctions
|
4
|
+
|
5
|
+
def plambda(*params)
|
6
|
+
lamb = PLambda.new do |values|
|
7
|
+
yield(values)
|
8
|
+
end
|
9
|
+
|
10
|
+
obj_params = []
|
11
|
+
params.each do |param|
|
12
|
+
param = object(param)
|
13
|
+
case param.type
|
14
|
+
when :id
|
15
|
+
lamb.form << nil
|
16
|
+
else
|
17
|
+
lamb.form << param
|
18
|
+
end
|
19
|
+
obj_params << param
|
20
|
+
end
|
21
|
+
|
22
|
+
PObject.new(:lambda, [lamb])
|
23
|
+
end
|
24
|
+
|
25
|
+
def object_message(object, message)
|
26
|
+
lamb = plambda(object, "{#{message.to_s}}") do |values|
|
27
|
+
yield(values[0], values[1])
|
28
|
+
end
|
29
|
+
begin
|
30
|
+
@env.set_var(:get_object_message, lamb)
|
31
|
+
rescue
|
32
|
+
@env.add_lambda(:get_object_message, lamb)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def var(id, value)
|
37
|
+
@env.set_var(id.to_sym, value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def object(expr)
|
41
|
+
PLang::Parser::SyntaxAnalyser.new(expr).parse[0]
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/vm/plambda.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module PLang
|
2
|
+
module VM
|
3
|
+
class PLambda < Proc
|
4
|
+
|
5
|
+
def call?(params)
|
6
|
+
_call?(params, @form||[])
|
7
|
+
end
|
8
|
+
|
9
|
+
def _call?(params, form)
|
10
|
+
if form.length == params.length
|
11
|
+
form.each_with_index do |f, i|
|
12
|
+
if f
|
13
|
+
case f.type
|
14
|
+
when :integer, :decimal, :char, :string
|
15
|
+
if f.type == params[i].id
|
16
|
+
unless f.value == params[i].params[0]
|
17
|
+
return false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
when :object
|
21
|
+
unless f.id.value == params[i].id
|
22
|
+
return false
|
23
|
+
else
|
24
|
+
unless _call?(params[i].params, f.params)
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
return true
|
32
|
+
else
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def form
|
38
|
+
@form ||= []
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/vm/pobject.rb
CHANGED
@@ -1,47 +1,49 @@
|
|
1
1
|
module PLang
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def ==(obj)
|
12
|
-
if @type == obj.type
|
13
|
-
@params.each_with_index do |param, i|
|
14
|
-
unless param == obj.params[i]
|
15
|
-
return false
|
16
|
-
end
|
17
|
-
end
|
18
|
-
return true
|
19
|
-
else
|
20
|
-
return false
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def form
|
25
|
-
case @type
|
26
|
-
when :integer, :decimal, :boolean, :char, :string
|
27
|
-
[:literal, @type, @params[0]]
|
28
|
-
else
|
29
|
-
[:object, @type, @params.collect(&:form)]
|
2
|
+
module VM
|
3
|
+
class PObject
|
4
|
+
attr_reader :id
|
5
|
+
attr_accessor :params
|
6
|
+
|
7
|
+
def initialize(id, params)
|
8
|
+
@id = id
|
9
|
+
@params = params
|
30
10
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
case @id
|
14
|
+
when :integer, :decimal, :char, :string, :boolean
|
15
|
+
return params[0]
|
16
|
+
when :empty
|
17
|
+
return "'()"
|
18
|
+
when :list
|
19
|
+
str = "'("
|
20
|
+
params = @params
|
21
|
+
ok = false
|
22
|
+
while params and params != []
|
23
|
+
str += "#{params[0].to_s}, "
|
24
|
+
params = params[1].params
|
25
|
+
ok = true
|
26
|
+
end
|
27
|
+
if ok
|
28
|
+
str[-2] = ')'
|
29
|
+
else
|
30
|
+
str += ')'
|
31
|
+
end
|
32
|
+
return str.strip
|
33
|
+
else
|
34
|
+
str = "{#{@id}"
|
35
|
+
if @params.length > 0
|
36
|
+
str += ": "
|
37
|
+
@params.each do |param|
|
38
|
+
str += "#{param.to_s}, "
|
39
|
+
end
|
40
|
+
str[-2] = "}"
|
41
|
+
else
|
42
|
+
str += "}"
|
43
|
+
end
|
44
|
+
return str.strip
|
45
|
+
end
|
43
46
|
end
|
44
47
|
end
|
45
|
-
|
46
48
|
end
|
47
|
-
end
|
49
|
+
end
|
data/test/test_lexer
ADDED
data/test/test_lexer.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestLexer < Test::Unit::TestCase
|
4
|
+
|
5
|
+
EXPRESSIONS = File.readlines(File.join(File.dirname(__FILE__), "test_lexer"))
|
6
|
+
TOKENS = File.readlines(File.join(File.dirname(__FILE__), "test_lexer_tokens"))
|
7
|
+
|
8
|
+
context "The Lexer" do
|
9
|
+
|
10
|
+
EXPRESSIONS.each_with_index do |expr, i|
|
11
|
+
lexer = PLang::Parser::Lexer.new(EXPRESSIONS[i])
|
12
|
+
should "tokenize the expression ##{i}" do
|
13
|
+
assert_equal lexer.next_token.type, eval(TOKENS[i])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|