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
data/README.rdoc
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
P is a small ('pequena' in portuguese) functional programming language.
|
4
4
|
|
5
|
-
fib = [1| 1]
|
6
|
-
[2| 1]
|
5
|
+
fib = [1| 1];
|
6
|
+
[2| 1];
|
7
7
|
[n| fib(n-1) + fib(n-2)]
|
8
8
|
|
9
9
|
fib(10)
|
@@ -12,11 +12,37 @@ P is a small ('pequena' in portuguese) functional programming language.
|
|
12
12
|
|
13
13
|
Functions:
|
14
14
|
|
15
|
-
fib = [1| 1]
|
16
|
-
[2| 1]
|
15
|
+
fib = [1| 1];
|
16
|
+
[2| 1];
|
17
17
|
[n| fib(n-1) + fib(n-2)]
|
18
18
|
|
19
|
-
fib(10)
|
19
|
+
fib(10) # => 55
|
20
|
+
|
21
|
+
# or
|
22
|
+
|
23
|
+
fib = [1|1]
|
24
|
+
fib = [2|1]
|
25
|
+
fib = [n|fib(n-1)+fib(n-2)]
|
26
|
+
|
27
|
+
fib(10) # => 55
|
28
|
+
|
29
|
+
And more functions:
|
30
|
+
|
31
|
+
f = [x| x*pi] : (pi = 3.14)
|
32
|
+
|
33
|
+
f(1) # => 3.14
|
34
|
+
|
35
|
+
Wildcards
|
36
|
+
|
37
|
+
f = [1, _, x| x*2]
|
38
|
+
|
39
|
+
f(1,'aaaaa', 10) # => 20
|
40
|
+
|
41
|
+
Lists:
|
42
|
+
|
43
|
+
x = '(1,2,3,4)
|
44
|
+
x->head() # => 1
|
45
|
+
x->tail() # => '(2,3,4)
|
20
46
|
|
21
47
|
Objects:
|
22
48
|
|
@@ -34,10 +60,14 @@ Messages:
|
|
34
60
|
|
35
61
|
3->plus2() # => 5
|
36
62
|
|
63
|
+
IO:
|
64
|
+
|
65
|
+
x = read() # <= 123
|
66
|
+
print(x) # => 123
|
67
|
+
|
37
68
|
== Requirements
|
38
69
|
|
39
|
-
* Ruby
|
40
|
-
* Treetop
|
70
|
+
* Ruby 1.9
|
41
71
|
|
42
72
|
== Install
|
43
73
|
|
@@ -45,7 +75,7 @@ Messages:
|
|
45
75
|
|
46
76
|
== Running
|
47
77
|
|
48
|
-
$ p-lang your-program.p
|
78
|
+
$ p-lang -i your-program.p
|
49
79
|
|
50
80
|
== Note on Patches/Pull Requests
|
51
81
|
|
data/Rakefile
CHANGED
@@ -10,7 +10,6 @@ begin
|
|
10
10
|
gem.email = "igorbonadio@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/igorbonadio/p-lang"
|
12
12
|
gem.authors = ["Igor Bonadio"]
|
13
|
-
gem.add_dependency "treetop", ">= 0"
|
14
13
|
gem.add_development_dependency "shoulda", ">= 0"
|
15
14
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
15
|
end
|
data/VERSION.yml
CHANGED
data/bin/p-lang
CHANGED
@@ -1,17 +1,45 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require 'optparse'
|
4
|
+
|
3
5
|
require File.join(File.expand_path(File.dirname(__FILE__)), '/../lib/p-lang')
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
ast = @parser.parse(File.readlines(ARGV[0]).join(""))
|
7
|
+
options = OptionParser.new do|opts|
|
8
|
+
opts.banner = "Usage: p-lang [option]"
|
9
|
+
opts.version = "0.2.0"
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
opts.on("-e", "-e 'COMMAND'", "interp one line of script.") do |command|
|
12
|
+
sa = PLang::Parser::SyntaxAnalyser.new(command)
|
13
|
+
vm = PLang::VM::Interpreter.new(sa.parse)
|
13
14
|
vm.execute!
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on("-i", "-i FILE", "interp a script file.") do |file|
|
18
|
+
begin
|
19
|
+
program = File.readlines(file).join("")
|
20
|
+
rescue
|
21
|
+
puts "No such file (LoadError)"
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
begin
|
25
|
+
sa = PLang::Parser::SyntaxAnalyser.new program
|
26
|
+
vm = PLang::VM::Interpreter.new(sa.parse)
|
27
|
+
vm.execute!
|
28
|
+
rescue Exception => e
|
29
|
+
puts "#{file}:#{e}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
argv = ARGV
|
35
|
+
begin
|
36
|
+
if ARGV.length > 0
|
37
|
+
if options.parse!(ARGV).length > 0
|
38
|
+
options.parse!(["-h"])
|
39
|
+
end
|
14
40
|
else
|
15
|
-
|
41
|
+
options.parse!(["-h"])
|
16
42
|
end
|
43
|
+
rescue
|
44
|
+
puts "p-lang: unrecognized option"
|
17
45
|
end
|
data/lib/p-lang.rb
CHANGED
@@ -1,31 +1,21 @@
|
|
1
1
|
ROOT_PATH = File.expand_path(File.dirname(__FILE__))
|
2
2
|
|
3
|
-
require '
|
4
|
-
require '
|
3
|
+
require File.join(ROOT_PATH, '/parser/lexer')
|
4
|
+
require File.join(ROOT_PATH, '/parser/error')
|
5
|
+
require File.join(ROOT_PATH, '/parser/token')
|
6
|
+
require File.join(ROOT_PATH, '/parser/syntax_analyser')
|
7
|
+
require File.join(ROOT_PATH, '/parser/node')
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
require File.join(ROOT_PATH, '/parser/nodes')
|
9
|
-
require File.join(ROOT_PATH, '/parser/ast')
|
10
|
-
|
11
|
-
require File.join(ROOT_PATH, '/vm/environment')
|
12
|
-
require File.join(ROOT_PATH, '/vm/vm')
|
13
|
-
require File.join(ROOT_PATH, '/vm/proc')
|
9
|
+
require File.join(ROOT_PATH, '/vm/pfunctions')
|
10
|
+
require File.join(ROOT_PATH, '/vm/interpreter')
|
14
11
|
require File.join(ROOT_PATH, '/vm/pobject')
|
15
|
-
require File.join(ROOT_PATH, '/vm/
|
16
|
-
|
17
|
-
require File.join(ROOT_PATH, '/vm/std/pio')
|
18
|
-
require File.join(ROOT_PATH, '/vm/std/pstring')
|
19
|
-
require File.join(ROOT_PATH, '/vm/std/pinteger')
|
20
|
-
require File.join(ROOT_PATH, '/vm/std/pdecimal')
|
12
|
+
require File.join(ROOT_PATH, '/vm/environment')
|
13
|
+
require File.join(ROOT_PATH, '/vm/plambda')
|
21
14
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
15
|
+
require File.join(ROOT_PATH, '/vm/core/pinteger')
|
16
|
+
require File.join(ROOT_PATH, '/vm/core/pboolean')
|
17
|
+
require File.join(ROOT_PATH, '/vm/core/pdecimal')
|
18
|
+
require File.join(ROOT_PATH, '/vm/core/pstring')
|
19
|
+
require File.join(ROOT_PATH, '/vm/core/pchar')
|
20
|
+
require File.join(ROOT_PATH, '/vm/core/plist')
|
21
|
+
require File.join(ROOT_PATH, '/vm/core/pio')
|
data/lib/parser/error.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module PLang
|
2
|
+
module Parser
|
3
|
+
class Error
|
4
|
+
def Error.syntax_error(line, src, i, msg)
|
5
|
+
error = "#{line}:sintax error: #{msg}\n#{Error.show_error(src, i)}"
|
6
|
+
raise error
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def Error.show_error(src, i)
|
12
|
+
_src = src[0,i]
|
13
|
+
src[i,src.length].each_byte do |c|
|
14
|
+
break if c.chr == "\n"
|
15
|
+
_src += c.chr
|
16
|
+
end
|
17
|
+
pos = _src.length - i
|
18
|
+
__src = ""
|
19
|
+
_src.reverse.each_byte do |c|
|
20
|
+
break if c.chr == "\n"
|
21
|
+
__src += c.chr
|
22
|
+
end
|
23
|
+
__src = __src.reverse
|
24
|
+
__src += "\n"
|
25
|
+
(__src.length - pos -1).times do
|
26
|
+
__src += " "
|
27
|
+
end
|
28
|
+
__src += "^"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/parser/lexer.rb
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
module PLang
|
2
|
+
module Parser
|
3
|
+
class Lexer
|
4
|
+
def initialize(src)
|
5
|
+
@line = 1
|
6
|
+
@src = src+"\n"
|
7
|
+
process()
|
8
|
+
end
|
9
|
+
|
10
|
+
def next_token
|
11
|
+
@fib.resume
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def process()
|
17
|
+
@i = 0
|
18
|
+
@fib = Fiber.new do
|
19
|
+
while @i < @src.length
|
20
|
+
case @src[@i]
|
21
|
+
when 'a'..'z', 'A'..'Z', '_'
|
22
|
+
@i = identifier
|
23
|
+
when '0'..'9'
|
24
|
+
@i = number
|
25
|
+
when "'"
|
26
|
+
@i = char
|
27
|
+
when '"'
|
28
|
+
@i = string
|
29
|
+
when '['
|
30
|
+
send_token(:lsquare, '[')
|
31
|
+
@i += 1
|
32
|
+
when ']'
|
33
|
+
send_token(:rsquare, ']')
|
34
|
+
@i += 1
|
35
|
+
when '('
|
36
|
+
send_token(:lround, '(')
|
37
|
+
@i += 1
|
38
|
+
when ')'
|
39
|
+
send_token(:rround, ')')
|
40
|
+
@i += 1
|
41
|
+
when '{'
|
42
|
+
send_token(:lcurly, '{')
|
43
|
+
@i += 1
|
44
|
+
when '}'
|
45
|
+
send_token(:rcurly, '}')
|
46
|
+
@i += 1
|
47
|
+
when ','
|
48
|
+
send_token(:comma, ',')
|
49
|
+
@i += 1
|
50
|
+
when ';'
|
51
|
+
send_token(:semicolon, ';')
|
52
|
+
@i += 1
|
53
|
+
when '|'
|
54
|
+
send_token(:pipe, '|')
|
55
|
+
@i += 1
|
56
|
+
when ':'
|
57
|
+
send_token(:colon, ':')
|
58
|
+
@i += 1
|
59
|
+
when '-'
|
60
|
+
@i = sub
|
61
|
+
when '+'
|
62
|
+
send_token(:add, '+')
|
63
|
+
@i += 1
|
64
|
+
when '*'
|
65
|
+
send_token(:mul, '*')
|
66
|
+
@i += 1
|
67
|
+
when '/'
|
68
|
+
send_token(:div, '/')
|
69
|
+
@i += 1
|
70
|
+
when '%'
|
71
|
+
send_token(:mod, '%')
|
72
|
+
@i += 1
|
73
|
+
when '='
|
74
|
+
@i = equal
|
75
|
+
when '!'
|
76
|
+
@i = diff
|
77
|
+
when '>'
|
78
|
+
@i = major
|
79
|
+
when '<'
|
80
|
+
@i = minor
|
81
|
+
when '#'
|
82
|
+
@i = comment
|
83
|
+
when "\t", "\s"
|
84
|
+
@i += 1
|
85
|
+
when "\n"
|
86
|
+
send_token(:break, "\\n")
|
87
|
+
@line += 1
|
88
|
+
@i += 1
|
89
|
+
when "\r"
|
90
|
+
send_token(:break, "\\r")
|
91
|
+
@line += 1
|
92
|
+
@i += 1
|
93
|
+
else
|
94
|
+
Error.syntax_error(@line, @src, @i, "unknown token '#{@src[@i]}'")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def send_token(type, value)
|
101
|
+
token = Token.new(type)
|
102
|
+
token.value = value
|
103
|
+
token.line = @line
|
104
|
+
token.i = @i
|
105
|
+
token.src = @src
|
106
|
+
Fiber.yield token
|
107
|
+
end
|
108
|
+
|
109
|
+
def identifier
|
110
|
+
token = ""
|
111
|
+
while ('a'..'z') === @src[@i] or ('A'..'Z') === @src or ('0'..'9') === @src[@i] or @src[@i] == '_'
|
112
|
+
token += @src[@i]
|
113
|
+
@i += 1
|
114
|
+
end
|
115
|
+
token = token.to_sym
|
116
|
+
case token
|
117
|
+
when :and, :or, :not, :true, :false, :nil, :begin, :if
|
118
|
+
send_token(token, token)
|
119
|
+
else
|
120
|
+
send_token(:id, token)
|
121
|
+
end
|
122
|
+
@i
|
123
|
+
end
|
124
|
+
|
125
|
+
def number
|
126
|
+
token = ""
|
127
|
+
type = :integer
|
128
|
+
while ('0'..'9') === @src[@i]
|
129
|
+
token += @src[@i]
|
130
|
+
@i += 1
|
131
|
+
end
|
132
|
+
if @src[@i] == '.'
|
133
|
+
token += "."
|
134
|
+
type = :decimal
|
135
|
+
@i+=1
|
136
|
+
ok = false
|
137
|
+
while ('0'..'9') === @src[@i]
|
138
|
+
token += @src[@i]
|
139
|
+
@i += 1
|
140
|
+
ok = true
|
141
|
+
end
|
142
|
+
if not ok
|
143
|
+
Error.syntax_error(@line, @src, @i, "invalid decimal number")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
if type == :decimal
|
147
|
+
send_token(:decimal, token.to_f)
|
148
|
+
else
|
149
|
+
send_token(:integer, token.to_i)
|
150
|
+
end
|
151
|
+
@i
|
152
|
+
end
|
153
|
+
|
154
|
+
def char
|
155
|
+
unless @src[@i+2] == "'"
|
156
|
+
if @src[@i+1] == "("
|
157
|
+
send_token(:list, :list)
|
158
|
+
@i+1
|
159
|
+
else
|
160
|
+
Error.syntax_error(@line, @src, @i, "unclosed character literal")
|
161
|
+
end
|
162
|
+
else
|
163
|
+
send_token(:char, @src[@i+1])
|
164
|
+
@i+3
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def string
|
169
|
+
token = ""
|
170
|
+
@i += 1
|
171
|
+
while @src[@i] and @src[@i] != '"'
|
172
|
+
token += @src[@i]
|
173
|
+
@i += 1
|
174
|
+
end
|
175
|
+
if @src[@i]
|
176
|
+
send_token(:string, token)
|
177
|
+
else
|
178
|
+
Error.syntax_error(@line, @src, @i - token.length - 1, "unclused string literal")
|
179
|
+
end
|
180
|
+
@i+1
|
181
|
+
end
|
182
|
+
|
183
|
+
def sub
|
184
|
+
if @src[@i+1] == '>'
|
185
|
+
send_token(:arrow, '->')
|
186
|
+
@i += 2
|
187
|
+
else
|
188
|
+
send_token(:sub, '-')
|
189
|
+
@i += 1
|
190
|
+
end
|
191
|
+
@i
|
192
|
+
end
|
193
|
+
|
194
|
+
def equal
|
195
|
+
if @src[@i+1] == '='
|
196
|
+
send_token(:equal, '==')
|
197
|
+
@i += 2
|
198
|
+
else
|
199
|
+
send_token(:let, '=')
|
200
|
+
@i += 1
|
201
|
+
end
|
202
|
+
@i
|
203
|
+
end
|
204
|
+
|
205
|
+
def diff
|
206
|
+
if @src[@i+1] == '='
|
207
|
+
send_token(:diff, '!=')
|
208
|
+
@i += 2
|
209
|
+
else
|
210
|
+
Error.syntax_error(@line, @src, @i, "unexpected '!#{@src[@i+1]}', expecting '!='")
|
211
|
+
end
|
212
|
+
@i
|
213
|
+
end
|
214
|
+
|
215
|
+
def major
|
216
|
+
if @src[@i+1] == '='
|
217
|
+
send_token(:major_equal, '>=')
|
218
|
+
@i += 2
|
219
|
+
else
|
220
|
+
send_token(:major, '>')
|
221
|
+
@i += 1
|
222
|
+
end
|
223
|
+
@i
|
224
|
+
end
|
225
|
+
|
226
|
+
def minor
|
227
|
+
if @src[@i+1] == '='
|
228
|
+
send_token(:minor_equal, '<=')
|
229
|
+
@i += 2
|
230
|
+
else
|
231
|
+
send_token(:minor, '<')
|
232
|
+
@i += 1
|
233
|
+
end
|
234
|
+
@i
|
235
|
+
end
|
236
|
+
|
237
|
+
def comment
|
238
|
+
while @src[@i] != "\n" and @src[@i] != "\r"
|
239
|
+
@i += 1
|
240
|
+
end
|
241
|
+
@i
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|