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 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
@@ -1,5 +1,5 @@
1
1
  ---
2
- :patch: 1
3
- :build:
4
2
  :major: 0
5
- :minor: 1
3
+ :minor: 2
4
+ :patch: 0
5
+ :build:
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
- if ARGV[0]
6
- @parser = PLangParser.new
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
- if ast
11
- vm = PLang::VM.new(ast.build.collect(&:to_sexp))
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
- puts @parser.failure_reason
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 'rubygems'
4
- require 'treetop'
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
- Treetop.load File.join(ROOT_PATH, '/parser/p-lang')
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/perror')
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
- module PLang
23
- class VM
24
- def initialize_global_environment(env)
25
- PIO.def_pfunctions(env)
26
- PString.def_pfunctions(env)
27
- PInteger.def_pfunctions(env)
28
- PDecimal.def_pfunctions(env)
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')
@@ -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
@@ -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