p-lang 0.1.1 → 0.2.0
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.
- 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
|