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