fruitloop 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ == FRUITLOOP
2
+
3
+ Copyright (c) 2012-2012 Thomas Hoefer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ FRUITLOOP
2
+ =========
3
+ Implementation of the LOOP programming language which compiles to JavaScript. See: http://de.wikipedia.org/wiki/LOOP-Programm.
4
+
5
+
6
+ DEPENDENCIES
7
+ ============
8
+ - Ruby >= 1.9.2p290, not tested with other versions
9
+ - Node.js >= 0.5.6; the binary is expected to be located at /usr/local/bin/node
10
+
11
+
12
+ HOWTO
13
+ =====
14
+ - Run "ruby fruitloop.rb add.loop" to compile and execute "add.loop". The same goes for the other examples.
15
+ - The result of a computation will always be located in variable "xa".
16
+
17
+
18
+ GRAMMAR
19
+ =======
20
+ - S ::= P$
21
+ - P ::= id A X | loop id do P end X
22
+ - A ::= : B
23
+ - B ::= = C
24
+ - C ::= id D | number
25
+ - D ::= + number | - number
26
+ - X ::= ; P X | Epsilon
27
+
28
+
29
+ License
30
+ =======
31
+ FRUITLOOP is released under the MIT license: [www.opensource.org/licenses/MIT](www.opensource.org/licenses/MIT)
32
+
33
+
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+
6
+ require 'rubygems'
7
+ require 'rake'
8
+ require 'rake/clean'
9
+ require 'rake/gempackagetask'
10
+ require 'rake/rdoctask'
11
+ require 'rake/testtask'
12
+
13
+ Rake::GemPackageTask.new(spec) do |p|
14
+ p.gem_spec = spec
15
+ p.need_tar = true
16
+ p.need_zip = true
17
+ end
18
+
19
+ Rake::RDocTask.new do |rdoc|
20
+ files =['README', 'LICENSE', 'lib/**/*.rb']
21
+ rdoc.rdoc_files.add(files)
22
+ rdoc.main = "README" # page to start on
23
+ rdoc.title = "LoopLang Docs"
24
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
25
+ rdoc.options << '--line-numbers'
26
+ end
27
+
28
+ Rake::TestTask.new do |t|
29
+ t.test_files = FileList['test/**/*.rb']
30
+ end
data/lib/add.loop ADDED
@@ -0,0 +1,6 @@
1
+ xa := 7;
2
+ xb := 3;
3
+
4
+ loop xb do
5
+ xa := xa + 1
6
+ end
data/lib/fruitloop.rb ADDED
@@ -0,0 +1,8 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__)))
2
+
3
+ require "lexer"
4
+ require "parser"
5
+
6
+ lexer = Lexer.new
7
+ parser = Parser.new(lexer)
8
+ parser.parse_start
@@ -0,0 +1,53 @@
1
+ require 'gen/symboltable'
2
+
3
+ class JSVisitor
4
+
5
+ def initialize
6
+ @target = ""
7
+ @loop_counter = 0
8
+ end
9
+
10
+ def visit_start(node)
11
+ compile "sys = require('sys');"
12
+ compile "xa = 0;"
13
+ compile "/* adding null initializers */"
14
+ SymbolTable.null_initializers.each do |token|
15
+ compile "#{token.name} = 0;"
16
+ end
17
+ end
18
+
19
+ def visit_assignment(node)
20
+ if node.rvalue
21
+ compile "#{node.lvalue.name} = #{node.rvalue.number};"
22
+ else
23
+ compile "#{node.lvalue.name} = #{node.op1.name} #{node.op.op} #{node.op2.number}; "
24
+ end
25
+ end
26
+
27
+ def visit_loop_start(node)
28
+ compile "_loopTerminate#{@loop_counter} = #{node.to.name};"
29
+ compile "for(var _loopVar#{@loop_counter} = 0; _loopVar#{@loop_counter} < _loopTerminate#{@loop_counter}; _loopVar#{@loop_counter}++) {"
30
+ @loop_counter += 1
31
+ end
32
+
33
+ def visit_loop_end(node)
34
+ compile "}"
35
+ end
36
+
37
+ def run
38
+ compile "sys.puts(\"xa: \" + xa);"
39
+ nodejs = IO.popen("/usr/local/bin/node", "r+")
40
+ nodejs.puts @target
41
+ nodejs.close_write
42
+ puts nodejs.gets
43
+ end
44
+
45
+
46
+ private
47
+
48
+ def compile(source)
49
+ #p source
50
+ @target += "#{source}"
51
+ end
52
+
53
+ end
@@ -0,0 +1,20 @@
1
+ class SymbolTable
2
+
3
+ @@symbols = {}
4
+ @@identifiers = []
5
+
6
+ def self.get_id(sym_or_tk)
7
+ @@symbols[sym_or_tk.is_a?(Token) ? sym_or_tk.name : sym_or_tk]
8
+ end
9
+
10
+ def self.add_id token
11
+ @@symbols[token.name] = token
12
+ end
13
+
14
+ def self.add_null_initializer token
15
+ @@identifiers << token
16
+ end
17
+
18
+ def self.null_initializers; @@identifiers; end;
19
+
20
+ end
data/lib/lexer.rb ADDED
@@ -0,0 +1,105 @@
1
+ require "token/all"
2
+
3
+
4
+ # Scanner-Komponente
5
+ # Liefert bei einem Aufruf von "input_token" immer das nächste Token im Quellprogramm.
6
+ class Lexer
7
+
8
+ attr_accessor :lexem, :char, :state
9
+
10
+ def initialize
11
+ @source = ""
12
+ read_input(ARGV.first)
13
+ @pos = -1
14
+ end
15
+
16
+
17
+ # Gibt das nächste Token der Eingabe zurück
18
+ def input_token
19
+
20
+ self.state = :start
21
+ next_char
22
+
23
+ while true
24
+
25
+ # puts "state: #{state}, char: #{char}, lexem: #{lexem}"
26
+
27
+ case state
28
+ when :start then
29
+ case char
30
+ when ":" then self.state = :colon
31
+ when "=" then self.state = :eq
32
+ when /[+|-]/ then self.state = :binop
33
+ when ";" then self.state = :semicolon
34
+ when "@" then self.state = :terminate
35
+ when /[a-z]/ then
36
+ self.lexem = char
37
+ self.state = :identifier_start
38
+ when /[0-9]/ then
39
+ self.lexem = char
40
+ self.state = :number_start
41
+ else next_char
42
+ end
43
+ when :colon then return TColon.new
44
+ when :eq then return TEq.new
45
+ when :binop then return TBinOp.new(char)
46
+ when :semicolon then return TSemicolon.new
47
+ when :identifier_start then
48
+ if next_char =~ /[a-z]/
49
+ self.lexem += char
50
+ else
51
+ self.state = :identifier
52
+ @pos -= 1
53
+ end
54
+ when :identifier then
55
+ l = get_and_clear_lexem
56
+ return case l
57
+ when "loop" then TLoop.new
58
+ when "do" then TDo.new
59
+ when "end" then TEnd.new
60
+ else TIdentifier.new(l)
61
+ end
62
+ when :number_start then
63
+ if next_char =~ /[0-9]/ then
64
+ self.lexem += char
65
+ else
66
+ @pos -= 1
67
+ self.state = :number
68
+ end
69
+ when :number then
70
+ l = get_and_clear_lexem
71
+ return TNumber.new(l)
72
+ when :terminate then return TTerminate.new
73
+ end
74
+ end
75
+ end
76
+
77
+
78
+ private
79
+
80
+ # Gibt das aktuelle Lexem zurück. Löscht zusätzlich die Instanzvariable.
81
+ def get_and_clear_lexem
82
+ lex = self.lexem
83
+ self.lexem = ""
84
+ lex
85
+ end
86
+
87
+ # Reader-Methode die das aktuelle Zeichen liefert
88
+ def char; @c; end;
89
+
90
+ # Liest den Quellcode
91
+ def read_input(file); @source = open(file).read; end;
92
+
93
+ # Liefert das nächste Zeichen der Eingabe
94
+ def next_char
95
+ if @pos <= @source.length-1
96
+ @pos += 1
97
+ @c = @source.slice(@pos, 1)
98
+ else
99
+ @c = "@"
100
+ end
101
+ @c
102
+ end
103
+
104
+ end
105
+
data/lib/mult.loop ADDED
@@ -0,0 +1,9 @@
1
+ a := a + 5;
2
+ b := b + 8;
3
+
4
+ loop a do
5
+ loop b do
6
+ xa := xa + 1
7
+ end
8
+ end
9
+
@@ -0,0 +1,11 @@
1
+
2
+ class NodeAssignment
3
+
4
+ attr_accessor :lvalue # will always be present with either...
5
+ attr_accessor :op1, :op, :op2 # ... formal correct version or...
6
+ attr_accessor :rvalue # ... the normal version :)
7
+
8
+ def accept(visitor)
9
+ visitor.visit_assignment self
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ class NodeLoop < ParentNode
2
+ attr_accessor :to
3
+
4
+ def accept(visitor)
5
+ visitor.visit_loop_start self
6
+ stmts.each do |stmt|
7
+ stmt.accept visitor
8
+ end
9
+ visitor.visit_loop_end self
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ class ParentNode
2
+
3
+ attr_accessor :stmts
4
+
5
+ def initialize
6
+ @stmts = []
7
+ end
8
+
9
+ def <<(it)
10
+ @stmts << it
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ class NodeStart < ParentNode
2
+ def accept(visitor)
3
+ visitor.visit_start self
4
+ stmts.each do |stmt|
5
+ stmt.accept visitor
6
+ end
7
+ end
8
+ end
data/lib/parser.rb ADDED
@@ -0,0 +1,176 @@
1
+ require "nodes/node_parent"
2
+ require "nodes/node_assignment"
3
+ require "nodes/node_loop"
4
+ require "nodes/node_start"
5
+ require "gen/jsvisitor"
6
+
7
+
8
+
9
+ class Parser
10
+
11
+ attr_reader :lookahead
12
+
13
+ def initialize(lexer)
14
+ @lexer = lexer
15
+ end
16
+
17
+ def test_tokens
18
+ while @token = @lexer.input_token
19
+ p @token
20
+ break if @token.is_a?TTerminate
21
+ end
22
+ end
23
+
24
+ # Parsing method for the start production
25
+ def parse_start
26
+ consume_token
27
+ case lookahead
28
+ when TIdentifier, TLoop then
29
+ @node_start = NodeStart.new # The AST´s root node
30
+ parse_p @node_start
31
+ match TTerminate
32
+ else
33
+ parse_error
34
+ end
35
+
36
+ # Evaluate AST after the syntax is good
37
+ traverse_ast
38
+ end
39
+
40
+
41
+ def parse_p parent_node
42
+ case lookahead
43
+ when TIdentifier then
44
+ @node_assignment = NodeAssignment.new
45
+ @node_assignment.lvalue = lookahead
46
+ SymbolTable.add_id lookahead
47
+ parent_node << @node_assignment # Assignment node
48
+ match TIdentifier
49
+ parse_a
50
+ parse_x parent_node
51
+ when TLoop then
52
+ @node_loop = NodeLoop.new
53
+ parent_node << @node_loop # Loop node
54
+ match TLoop
55
+ to = lookahead
56
+ semantic_error "loop variable must be defined" unless SymbolTable.get_id(to)
57
+ @node_loop.to = to
58
+ match TIdentifier
59
+ match TDo
60
+ parse_p @node_loop
61
+ match TEnd
62
+ parse_x parent_node
63
+ else
64
+ parse_error
65
+ end
66
+ end
67
+
68
+
69
+ # As x can evaluate to Epsilon...
70
+ def parse_x parent_node
71
+ case lookahead
72
+ when TSemicolon then # ... besides the case that it is not Epsilon ...
73
+ match TSemicolon
74
+ parse_p parent_node
75
+ parse_x parent_node
76
+ when TTerminate, TEnd, TSemicolon then # ... predict for the follow set.
77
+ # Epsilon production
78
+ else
79
+ # Error - the current lookahead is neither in..
80
+ # 1. FIRST(x)
81
+ # 2. FOLLOW(x) if Epsilon in FIRST(x)
82
+ parse_error
83
+ end
84
+ end
85
+
86
+
87
+ def parse_a
88
+ case lookahead
89
+ when TColon then
90
+ match TColon
91
+ parse_b
92
+ else
93
+ parse_error
94
+ end
95
+ end
96
+
97
+
98
+ def parse_b
99
+ case lookahead
100
+ when TEq then
101
+ match TEq
102
+ parse_c
103
+ else
104
+ parse_error
105
+ end
106
+ end
107
+
108
+
109
+ def parse_c
110
+ case lookahead
111
+ when TIdentifier
112
+ SymbolTable.add_null_initializer lookahead
113
+ @node_assignment.op1 = lookahead # An assignment´s first operand
114
+ match TIdentifier
115
+ parse_d
116
+ when TNumber
117
+ @node_assignment.rvalue = lookahead # Short assignment
118
+ match TNumber
119
+ else
120
+ parse_error
121
+ end
122
+ end
123
+
124
+
125
+ def parse_d
126
+ case lookahead
127
+ when TBinOp then
128
+ @node_assignment.op = lookahead # Operand
129
+ match TBinOp
130
+ @node_assignment.op2 = lookahead # An assignment´s second operand
131
+ match TNumber
132
+ else
133
+ parse_error
134
+ end
135
+ end
136
+
137
+
138
+ private
139
+
140
+ # Checks if the current token is an instance of <which_class>. If this is not the case
141
+ # the programm terminates as an syntacial error occured. Reads the next token after the
142
+ # check was successful.
143
+ def match(which_class)
144
+ if lookahead.is_a?(which_class)
145
+ #p "Is: #{lookahead.class}, Expected: #{which_class}"
146
+ else
147
+ p "Syntaxfehler! Is: #{lookahead.class} Expected: #{which_class}"
148
+ exit
149
+ end
150
+ consume_token
151
+ end
152
+
153
+ # Returns the next token from the scanner
154
+ def consume_token
155
+ @lookahead = @lexer.input_token
156
+ @lookahead
157
+ end
158
+
159
+ def parse_error
160
+ p "Syntax error: #{lookahead.class}"
161
+ exit
162
+ end
163
+
164
+ def semantic_error(message)
165
+ p "Semantic error: #{message}"
166
+ exit
167
+ end
168
+
169
+ def traverse_ast
170
+ visitor = JSVisitor.new
171
+ @node_start.accept visitor
172
+ visitor.run
173
+ end
174
+
175
+
176
+ end
data/lib/sub.loop ADDED
@@ -0,0 +1,6 @@
1
+ xa := 7;
2
+ xb := 1;
3
+
4
+ loop xb do
5
+ xa := xa - 1
6
+ end
data/lib/token/all.rb ADDED
@@ -0,0 +1,32 @@
1
+ class Token
2
+ attr_accessor :line, :row
3
+ end
4
+
5
+ class TIdentifier < Token
6
+ attr_accessor :name
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+ end
11
+
12
+ class TNumber < Token
13
+ attr_accessor :number
14
+ def initialize(number)
15
+ @number = number
16
+ end
17
+ end
18
+
19
+ class TBinOp < Token
20
+ attr_accessor :op
21
+ def initialize(op)
22
+ @op = op
23
+ end
24
+ end
25
+
26
+ class TColon < Token; end;
27
+ class TEq < Token; end;
28
+ class TTerminate < Token; end;
29
+ class TEnd < Token; end;
30
+ class TLoop < Token; end;
31
+ class TDo < Token; end;
32
+ class TSemicolon < Token; end;
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fruitloop
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.3'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Thomas Hoefer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-12 00:00:00.000000000 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+ description: FRUITLOOP is an implementation of the LOOP programming language.
16
+ email: mail@tomhoefer.de
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files:
20
+ - README.md
21
+ - MIT-LICENSE
22
+ files:
23
+ - MIT-LICENSE
24
+ - README.md
25
+ - Rakefile
26
+ - lib/add.loop
27
+ - lib/fruitloop.rb
28
+ - lib/gen/jsvisitor.rb
29
+ - lib/gen/symboltable.rb
30
+ - lib/lexer.rb
31
+ - lib/mult.loop
32
+ - lib/nodes/node_assignment.rb
33
+ - lib/nodes/node_loop.rb
34
+ - lib/nodes/node_parent.rb
35
+ - lib/nodes/node_start.rb
36
+ - lib/parser.rb
37
+ - lib/sub.loop
38
+ - lib/token/all.rb
39
+ has_rdoc: true
40
+ homepage: http://www.github.com/thoefer/fruitloop
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.6.2
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: FRUITLOOP is an implementation of the LOOP programming language.
64
+ test_files: []