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
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
|