alphalang 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env ruby
2
+ ####################################################
3
+
4
+ $scopes = [{}]
5
+ $scope_lvl = 0
6
+ $test_nodes = false
7
+
8
+ ####################################################
9
+
10
+ class TrueClass
11
+ def evaluate
12
+ self
13
+ end
14
+ end
15
+
16
+ class FalseClass
17
+ def evaluate
18
+ self
19
+ end
20
+ end
21
+
22
+ class String
23
+ def name
24
+ self
25
+ end
26
+
27
+ def evaluate
28
+ self
29
+ end
30
+ end
31
+
32
+ ####################################################
33
+
34
+ class Node
35
+ attr_accessor :value
36
+
37
+ def initialize(value)
38
+ @value = value
39
+ end
40
+
41
+ def evaluate
42
+ @value.evaluate
43
+ end
44
+ end
45
+
46
+ class NumberNode < Node
47
+ def initialize(value)
48
+ super(value)
49
+ end
50
+
51
+ def evaluate
52
+ if @value.include?('.')
53
+ @value.to_f
54
+ else
55
+ @value.to_i
56
+ end
57
+ end
58
+ end
59
+
60
+ class BoolNode < Node
61
+ def initialize(value)
62
+ @value = false
63
+ @value = true if value == "true"
64
+ @value = false if value == "false"
65
+ end
66
+
67
+ def evaluate
68
+ @value
69
+ end
70
+ end
71
+
72
+ class AndNode < Node
73
+ def initialize(node1, node2)
74
+ @node1, @node2 = node1, node2
75
+ if @node2.class.method_defined? (:lhs)
76
+ if @node2.lhs == nil
77
+ @node2.lhs = @node1.lhs
78
+ end
79
+ end
80
+ end
81
+
82
+ def evaluate
83
+ @node1.evaluate && @node2.evaluate
84
+ end
85
+ end
86
+
87
+ class OrNode < Node
88
+ def initialize(node1, node2)
89
+ @node1, @node2 = node1, node2
90
+ if @node2.class.method_defined? (:lhs)
91
+ if @node2.lhs == nil
92
+ @node2.lhs = @node1.lhs
93
+ end
94
+ end
95
+ end
96
+
97
+ def evaluate
98
+ @node1.evaluate || @node2.evaluate
99
+ end
100
+ end
101
+
102
+ class NotNode < Node
103
+ def initialize(node)
104
+ @node = node
105
+ end
106
+
107
+ def evaluate
108
+ not @node.evaluate
109
+ end
110
+ end
111
+
112
+ ####################################################
113
+
114
+ class CompareNode < Node
115
+ attr_accessor :lhs, :op, :rhs
116
+
117
+ def initialize(node1, op, node2)
118
+ @lhs, @op, @rhs = node1, op, node2
119
+ end
120
+
121
+ def evaluate
122
+ @value = @lhs.evaluate.send(@op, @rhs.evaluate)
123
+ end
124
+ end
125
+
126
+ class BinaryOperationNode < Node
127
+ attr_accessor :lhs, :op, :rhs
128
+
129
+ def initialize(lhs, op, rhs)
130
+ super(op)
131
+ @lhs, @op, @rhs = lhs, op, rhs
132
+ end
133
+
134
+ def evaluate
135
+ if @op == '/'
136
+ @value = @lhs.evaluate.to_f.send(@op, @rhs.evaluate)
137
+ else
138
+ @value = @lhs.evaluate.send(@op, @rhs.evaluate)
139
+ end
140
+ end
141
+ end
142
+
143
+ ####################################################
144
+
145
+ class CompStmtNode < Node
146
+
147
+ def initialize(stmt_compstmt)
148
+ super
149
+ @comp_statements = stmt_compstmt
150
+ end
151
+
152
+ def evaluate
153
+ @comp_statements[0].evaluate
154
+ @comp_statements[1].evaluate # kanske bör loopas osv??? Root blir nu sista evalueringen.
155
+ end
156
+ end
157
+
158
+
159
+ ####################################################
160
+
161
+ class ArgListNode < Node
162
+ attr_accessor :lhs, :rhs
163
+
164
+ def initialize(lhs, rhs)
165
+ @lhs, @rhs = lhs, rhs
166
+
167
+ if @rhs == NilClass
168
+ @value = [@lhs]
169
+ else
170
+ result = [@lhs]
171
+ @rhs.each do |element|
172
+ result << element
173
+ end
174
+ @value = result
175
+ end
176
+ end
177
+
178
+ def [](index)
179
+ @value[index]
180
+ end
181
+
182
+ def []=(index, value)
183
+ @value[index] = value
184
+ end
185
+
186
+ def each
187
+ @value.each_with_index do |val, index|
188
+ yield val, index
189
+ end
190
+ end
191
+
192
+ def evaluate
193
+ @value
194
+ end
195
+ end
@@ -0,0 +1,200 @@
1
+ require_relative 'basenodes'
2
+
3
+ ####################################################
4
+ class VariableCallNode < Node
5
+ attr_accessor :name
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+
11
+ def lookup_var(name)
12
+ temp_scope_lvl = $scope_lvl
13
+ while temp_scope_lvl >= 0
14
+ if not $scopes[temp_scope_lvl].has_key?(name)
15
+ temp_scope_lvl -= 1
16
+ else
17
+ return $scopes[temp_scope_lvl][name]
18
+ end
19
+ end
20
+ end
21
+
22
+ def evaluate
23
+ return @value = lookup_var(@name)
24
+ end
25
+ end
26
+
27
+ class VariableDecNode < Node
28
+ attr_accessor :name
29
+
30
+ def initialize(name, value)
31
+ super(value)
32
+ @name = name
33
+ end
34
+
35
+ def evaluate
36
+ $scopes[$scope_lvl][@name.name] = @value.evaluate
37
+ self
38
+ # return nil
39
+ end
40
+ end
41
+
42
+ ####################################################
43
+
44
+ class FunctionDecNode < Node
45
+ def initialize(node, value)
46
+ super(value)
47
+ @name = node
48
+ @args = node.args
49
+ end
50
+
51
+ def evaluate
52
+ $scopes[0][@name.name] = [@value, @args]
53
+ return nil
54
+ end
55
+ end
56
+
57
+ class FuncCallNode < Node
58
+ attr_accessor :name, :args
59
+
60
+ def initialize(name, args)
61
+ @name = name
62
+ @args = args
63
+ end
64
+
65
+ def lookup_var(name)
66
+ return $scopes[0][name]
67
+ end
68
+
69
+ def evaluate
70
+ $scopes.push({})
71
+ func = lookup_var(@name)
72
+ function_body = func[0]
73
+ function_param = func[1]
74
+
75
+ return nil if func.is_a?(NilClass)
76
+
77
+ $scope_lvl += 1
78
+
79
+ if function_param.is_a?(ArgListNode)
80
+ function_param.each do |val, index|
81
+ function_param[index] = VariableDecNode.new(function_param[index].name, @args[index])
82
+ function_param[index].evaluate
83
+ end
84
+ end
85
+
86
+ func_result = function_body.evaluate
87
+ old_scope_lvl = $scope_lvl
88
+ # Om en assign är returnvärde så declareras variablen i globalt scope
89
+ if func_result.is_a?(VariableDecNode)
90
+ $scope_lvl = 0
91
+ func_result.evaluate
92
+ $scope_lvl = old_scope_lvl
93
+ end
94
+
95
+ $scope_lvl -= 1
96
+ $scopes.pop
97
+ return func_result
98
+ end
99
+ end
100
+
101
+ ####################################################
102
+
103
+ class IfNode < Node
104
+ attr_accessor :argument
105
+
106
+ def initialize(argument, node)
107
+ @argument, @node = argument, node
108
+ end
109
+
110
+ def evaluate
111
+ if @argument.evaluate
112
+ @value = @node.evaluate
113
+ else
114
+ @value = nil
115
+ end
116
+ @value
117
+ end
118
+ end
119
+
120
+ class ElseifNode < Node
121
+ attr_accessor :argument
122
+ def initialize(argument, node)
123
+ @argument, @node = argument, node
124
+ end
125
+ def evaluate
126
+ if @argument.evaluate
127
+ @value = @node.evaluate
128
+ end
129
+ end
130
+ end
131
+
132
+ class ElseNode < Node
133
+ attr_accessor :argument, :node
134
+ def initialize(node)
135
+ @node = node
136
+ @argument = true
137
+ end
138
+
139
+ def evaluate
140
+ @value = @node.evaluate
141
+ end
142
+ end
143
+
144
+ class IfCompStmtNode < Node
145
+ def initialize(*nodes)
146
+ @nodes = nodes.flatten
147
+ end
148
+
149
+ def evaluate
150
+ @nodes.each do |node|
151
+ if node.argument.evaluate
152
+ return node.evaluate
153
+ end
154
+ end
155
+ return nil
156
+ end
157
+ end
158
+
159
+ ####################################################
160
+
161
+ class WhileLoopNode < Node
162
+ attr_accessor :condition
163
+
164
+ def initialize(condition, statement)
165
+ @condition = condition
166
+ super(statement)
167
+ end
168
+
169
+ def evaluate
170
+ while @condition.evaluate
171
+ @value.evaluate
172
+ end
173
+ self.class
174
+ end
175
+ end
176
+
177
+ ####################################################
178
+
179
+ class PrintNode
180
+ attr_accessor :value
181
+
182
+ def initialize(value)
183
+ @value = value
184
+ end
185
+
186
+ def evaluate
187
+ puts @value.evaluate
188
+ self.class # detta kanske är trevligare än nil, åter igen den diskussionen.
189
+ end
190
+ end
191
+
192
+ class PauseNode < Node
193
+ def initialize(value)
194
+ super(value)
195
+ end
196
+
197
+ def evaluate
198
+ sleep @value.evaluate
199
+ end
200
+ end
data/lib/rdparse.rb ADDED
@@ -0,0 +1,238 @@
1
+ # This file is called rdparse.rb because it implements a Recursive
2
+ # Descent Parser. Read more about the theory on e.g.
3
+ # http://en.wikipedia.org/wiki/Recursive_descent_parser
4
+
5
+ # 2010-02-11 New version of this file for the 2010 instance of TDP007
6
+ # which handles false return values during parsing, and has an easy way
7
+ # of turning on and off debug messages.
8
+ # 2014-02-16 New version that handles { false } blocks and :empty tokens.
9
+
10
+ require 'logger'
11
+
12
+ class Rule
13
+
14
+ # A rule is created through the rule method of the Parser class, like this:
15
+ # rule :term do
16
+ # match(:term, '*', :dice) {|a, _, b| a * b }
17
+ # match(:term, '/', :dice) {|a, _, b| a / b }
18
+ # match(:dice)
19
+ # end
20
+
21
+ Match = Struct.new :pattern, :block
22
+
23
+ def initialize(name, parser)
24
+ @logger = parser.logger
25
+ # The name of the expressions this rule matches
26
+ @name = name
27
+ # We need the parser to recursively parse sub-expressions occurring
28
+ # within the pattern of the match objects associated with this rule
29
+ @parser = parser
30
+ @matches = []
31
+ # Left-recursive matches
32
+ @lrmatches = []
33
+ end
34
+
35
+ # Add a matching expression to this rule, as in this example:
36
+ # match(:term, '*', :dice) {|a, _, b| a * b }
37
+ # The arguments to 'match' describe the constituents of this expression.
38
+ def match(*pattern, &block)
39
+ match = Match.new(pattern, block)
40
+ # If the pattern is left-recursive, then add it to the left-recursive set
41
+ if pattern[0] == @name
42
+ pattern.shift
43
+ @lrmatches << match
44
+ else
45
+ @matches << match
46
+ end
47
+ end
48
+
49
+ def parse
50
+ # Try non-left-recursive matches first, to avoid infinite recursion
51
+ match_result = try_matches(@matches)
52
+ return nil if match_result.nil?
53
+ loop do
54
+ result = try_matches(@lrmatches, match_result)
55
+ return match_result if result.nil?
56
+ match_result = result
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ # Try out all matching patterns of this rule
63
+ def try_matches(matches, pre_result = nil)
64
+ match_result = nil
65
+ # Begin at the current position in the input string of the parser
66
+ start = @parser.pos
67
+ matches.each do |match|
68
+ # pre_result is a previously available result from evaluating expressions
69
+ result = pre_result.nil? ? [] : [pre_result]
70
+
71
+ # We iterate through the parts of the pattern, which may be e.g.
72
+ # [:expr,'*',:term]
73
+ match.pattern.each_with_index do |token,index|
74
+
75
+ # If this "token" is a compound term, add the result of
76
+ # parsing it to the "result" array
77
+ if @parser.rules[token]
78
+ result << @parser.rules[token].parse
79
+ if result.last.nil?
80
+ result = nil
81
+ break
82
+ end
83
+ @logger.debug("Matched '#{@name} = #{match.pattern[index..-1].inspect}'")
84
+ else
85
+ # Otherwise, we consume the token as part of applying this rule
86
+ nt = @parser.expect(token)
87
+ if nt
88
+ result << nt
89
+ if @lrmatches.include?(match.pattern) then
90
+ pattern = [@name]+match.pattern
91
+ else
92
+ pattern = match.pattern
93
+ end
94
+ @logger.debug("Matched token '#{nt}' as part of rule '#{@name} <= #{pattern.inspect}'")
95
+ else
96
+ result = nil
97
+ break
98
+ end
99
+ end # pattern.each
100
+ end # matches.each
101
+ if result
102
+ if match.block
103
+ match_result = match.block.call(*result)
104
+ else
105
+ match_result = result[0]
106
+ end
107
+ @logger.debug("'#{@parser.string[start..@parser.pos-1]}' matched '#{@name}' and generated '#{match_result.inspect}'") unless match_result.nil?
108
+ break
109
+ else
110
+ # If this rule did not match the current token list, move
111
+ # back to the scan position of the last match
112
+ @parser.pos = start
113
+ end
114
+ end
115
+
116
+ return match_result
117
+ end
118
+ end
119
+
120
+ class Parser
121
+
122
+ attr_accessor :pos
123
+ attr_reader :rules, :string, :logger
124
+
125
+ class ParseError < RuntimeError
126
+ end
127
+
128
+ def initialize(language_name, locale='en', &block)
129
+
130
+ locales_path = File.join(__dir__, "locales", locale)
131
+ token_pairs = File.readlines(locales_path)
132
+ @token_list = Hash.new
133
+ token_pairs.each do |pair|
134
+ default_value, locale_value = pair.split(' ')
135
+ @token_list[default_value] = locale_value
136
+ end
137
+
138
+ @logger = Logger.new(STDOUT)
139
+ @lex_tokens = []
140
+ @rules = {}
141
+ @start = nil
142
+ @language_name = language_name
143
+ instance_eval(&block)
144
+ end
145
+
146
+ # Tokenize the string into small pieces
147
+ def tokenize(string)
148
+ @tokens = []
149
+ @string = string.clone
150
+ until string.empty?
151
+ # Unless any of the valid tokens of our language are the prefix of
152
+ # 'string', we fail with an exception
153
+ raise ParseError, "unable to lex '#{string}" unless @lex_tokens.any? do |tok|
154
+ match = tok.pattern.match(string)
155
+ # The regular expression of a token has matched the beginning of 'string'
156
+ if match
157
+ @logger.debug("Token #{match[0]} consumed as #{match[0]}")
158
+ # Also, evaluate this expression by using the block
159
+ # associated with the token
160
+ @tokens << tok.block.call(match.to_s) if tok.block
161
+ # consume the match and proceed with the rest of the string
162
+ string = match.post_match
163
+ true
164
+ else
165
+ # this token pattern did not match, try the next
166
+ false
167
+ end # if
168
+ end # raise
169
+ end # until
170
+ end
171
+
172
+ def parse(string)
173
+ # First, split the string according to the "token" instructions given.
174
+ # Afterwards @tokens contains all tokens that are to be parsed.
175
+ tokenize(string)
176
+
177
+ # These variables are used to match if the total number of tokens
178
+ # are consumed by the parser
179
+ @pos = 0
180
+ @max_pos = 0
181
+ @expected = []
182
+ # Parse (and evaluate) the tokens received
183
+ result = @start.parse
184
+ # If there are unparsed extra tokens, signal error
185
+ if @pos != @tokens.size
186
+ raise ParseError, "Parse error. expected: '#{@expected.join(', ')}', found '#{@tokens[@max_pos]}'"
187
+ end
188
+ return result
189
+ end
190
+
191
+ def next_token
192
+ @pos += 1
193
+ return @tokens[@pos - 1]
194
+ end
195
+
196
+ # Return the next token in the queue
197
+ def expect(tok)
198
+ return tok if tok == :empty
199
+ t = next_token
200
+ if @pos - 1 > @max_pos
201
+ @max_pos = @pos - 1
202
+ @expected = []
203
+ end
204
+ return t if tok === t
205
+ @expected << tok if @max_pos == @pos - 1 && !@expected.include?(tok)
206
+ return nil
207
+ end
208
+
209
+ def to_s
210
+ "Parser for #{@language_name}"
211
+ end
212
+
213
+ private
214
+
215
+ LexToken = Struct.new(:pattern, :block)
216
+
217
+ def token(pattern, &block)
218
+ @lex_tokens << LexToken.new(Regexp.new('\\A' + @token_list[pattern.source]), block)
219
+ end
220
+
221
+ def start(name, &block)
222
+ rule(name, &block)
223
+ @start = @rules[name]
224
+ end
225
+
226
+ def rule(name,&block)
227
+ @current_rule = Rule.new(name, self)
228
+ @rules[name] = @current_rule
229
+ instance_eval &block # In practise, calls match 1..N times
230
+ @current_rule = nil
231
+ end
232
+
233
+ def match(*pattern, &block)
234
+ # Basically calls memberfunction "match(*pattern, &block)
235
+ @current_rule.send(:match, *pattern, &block)
236
+ end
237
+
238
+ end