alphalang 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,237 @@
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
+
13
+ class Rule
14
+
15
+ # A rule is created through the rule method of the Parser class, like this:
16
+ # rule :term do
17
+ # match(:term, '*', :dice) {|a, _, b| a * b }
18
+ # match(:term, '/', :dice) {|a, _, b| a / b }
19
+ # match(:dice)
20
+ # end
21
+
22
+ Match = Struct.new :pattern, :block
23
+
24
+ def initialize(name, parser)
25
+
26
+ # The name of the expressions this rule matches
27
+ @name = name
28
+ # We need the parser to recursively parse sub-expressions occurring
29
+ # within the pattern of the match objects associated with this rule
30
+ @parser = parser
31
+ @matches = []
32
+ # Left-recursive matches
33
+ @lrmatches = []
34
+ end
35
+
36
+ # Add a matching expression to this rule, as in this example:
37
+ # match(:term, '*', :dice) {|a, _, b| a * b }
38
+ # The arguments to 'match' describe the constituents of this expression.
39
+ def match(*pattern, &block)
40
+ match = Match.new(pattern, block)
41
+ # If the pattern is left-recursive, then add it to the left-recursive set
42
+ if pattern[0] == @name
43
+ pattern.shift
44
+ @lrmatches << match
45
+ else
46
+ @matches << match
47
+ end
48
+ end
49
+
50
+ def parse
51
+ # Try non-left-recursive matches first, to avoid infinite recursion
52
+ match_result = try_matches(@matches)
53
+ return nil if match_result.nil?
54
+ loop do
55
+ result = try_matches(@lrmatches, match_result)
56
+ return match_result if result.nil?
57
+ match_result = result
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ # Try out all matching patterns of this rule
64
+ def try_matches(matches, pre_result = nil)
65
+ match_result = nil
66
+ # Begin at the current position in the input string of the parser
67
+ start = @parser.pos
68
+ matches.each do |match|
69
+ # pre_result is a previously available result from evaluating expressions
70
+ result = pre_result.nil? ? [] : [pre_result]
71
+
72
+ # We iterate through the parts of the pattern, which may be e.g.
73
+ # [:expr,'*',:term]
74
+ match.pattern.each_with_index do |token,index|
75
+
76
+ # If this "token" is a compound term, add the result of
77
+ # parsing it to the "result" array
78
+ if @parser.rules[token]
79
+ result << @parser.rules[token].parse
80
+ if result.last.nil?
81
+ result = nil
82
+ break
83
+ end
84
+
85
+ else
86
+ # Otherwise, we consume the token as part of applying this rule
87
+ nt = @parser.expect(token)
88
+ if nt
89
+ result << nt
90
+ if @lrmatches.include?(match.pattern) then
91
+ pattern = [@name]+match.pattern
92
+ else
93
+ pattern = match.pattern
94
+ end
95
+
96
+ else
97
+ result = nil
98
+ break
99
+ end
100
+ end # pattern.each
101
+ end # matches.each
102
+ if result
103
+ if match.block
104
+ match_result = match.block.call(*result)
105
+ else
106
+ match_result = result[0]
107
+ end
108
+
109
+ break
110
+ else
111
+ # If this rule did not match the current token list, move
112
+ # back to the scan position of the last match
113
+ @parser.pos = start
114
+ end
115
+ end
116
+
117
+ return match_result
118
+ end
119
+ end
120
+
121
+ class Parser
122
+
123
+ attr_accessor :pos
124
+ attr_reader :rules, :string, :logger
125
+
126
+ class ParseError < RuntimeError
127
+ end
128
+
129
+ def initialize(language_name, locale='en', &block)
130
+
131
+ locales_path = File.join(__dir__, "locales", locale)
132
+ token_pairs = File.readlines(locales_path)
133
+ @token_list = Hash.new
134
+ token_pairs.each do |pair|
135
+ default_value, locale_value = pair.split(' ')
136
+ @token_list[default_value] = locale_value
137
+ end
138
+
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
+
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
+ end
@@ -0,0 +1,19 @@
1
+ a = 1
2
+ b = 1
3
+ print b
4
+ def fib(x)
5
+ f = 2
6
+ if f < x
7
+
8
+ print b
9
+ temp = a + b
10
+ a = b
11
+ b = temp
12
+ fib(x - 1)
13
+
14
+ else
15
+ b
16
+ end
17
+ end
18
+
19
+ fib(11)
@@ -0,0 +1,217 @@
1
+ require 'test/unit'
2
+
3
+ class TestArithmetic < Test::Unit::TestCase
4
+ def test_addition
5
+ assert_equal(3, LangParser.new.calc_test('1 + 2'))
6
+ assert_equal(3, LangParser.new.calc_test('(1 + 2)'))
7
+ assert_equal(3, LangParser.new.calc_test('((1 + 2))'))
8
+
9
+ assert_equal(6, LangParser.new.calc_test('1 + 2 + 3'))
10
+ assert_equal(6, LangParser.new.calc_test('(1 + 2) + 3'))
11
+ assert_equal(6, LangParser.new.calc_test('((1 + 2) + 3)'))
12
+ assert_equal(10, LangParser.new.calc_test('((1 + 2) + 3) + 4'))
13
+ assert_equal(10, LangParser.new.calc_test('4 + ((3) + (2 + 1))'))
14
+ end
15
+
16
+ def test_subtraction
17
+ assert_equal(1, LangParser.new.calc_test('2 - 1'))
18
+ assert_equal(-3, LangParser.new.calc_test('(-2) - 1'))
19
+ assert_equal(1, LangParser.new.calc_test('(2 - 1)'))
20
+ assert_equal(1, LangParser.new.calc_test('((2 - 1))'))
21
+
22
+ assert_equal(0, LangParser.new.calc_test('3 - 2 - 1'))
23
+ assert_equal(0, LangParser.new.calc_test('(3 - 2) - 1'))
24
+ assert_equal(0, LangParser.new.calc_test('((3 - 2)) - 1'))
25
+ assert_equal(2, LangParser.new.calc_test('(4 - (3 - 2)) - 1'))
26
+ assert_equal(2, LangParser.new.calc_test('(4 - ((3) - 2)) - 1'))
27
+ assert_equal(4, LangParser.new.calc_test('4 - (((3) - 2) - 1)'))
28
+ end
29
+
30
+ def test_multiply
31
+ assert_equal(2, LangParser.new.calc_test('2 * 1'))
32
+ assert_equal(2, LangParser.new.calc_test('1 * 2'))
33
+ assert_equal(2, LangParser.new.calc_test('1 * (2)'))
34
+ assert_equal(2, LangParser.new.calc_test('(1) * (2)'))
35
+
36
+ assert_equal(6, LangParser.new.calc_test('(1) * (2 * 3)'))
37
+ assert_equal(6, LangParser.new.calc_test('(1 * 2 * 3)'))
38
+ assert_equal(6, LangParser.new.calc_test('(1 * 2) * 3'))
39
+ assert_equal(18, LangParser.new.calc_test('(3) * 2 * (3)'))
40
+ assert_equal(-6, LangParser.new.calc_test('(3) * (0 - 2) * (1)'))
41
+ end
42
+
43
+ def test_division
44
+ assert_equal(2, LangParser.new.calc_test('2 / 1'))
45
+ assert_equal(1, LangParser.new.calc_test('2 / 2'))
46
+ assert_equal(0.5, LangParser.new.calc_test('1 / 2'))
47
+ assert_equal(-0.5, LangParser.new.calc_test('-1 / 2'))
48
+ assert_equal(-2.5, LangParser.new.calc_test('-5 / 2'))
49
+
50
+ assert_equal(0, LangParser.new.calc_test('(-1 + 1) / 2'))
51
+ assert_equal(1, LangParser.new.calc_test('(3 / (3 / 1)) / 1'))
52
+ assert_equal(1, LangParser.new.calc_test('(3 / (3 / 1)) / (1)'))
53
+ assert_equal(3, LangParser.new.calc_test('(3 / (3 / 1-1-1)) / (1)'))
54
+ end
55
+ end
56
+
57
+ class TestLogic < Test::Unit::TestCase
58
+ def test_logic
59
+ assert_equal(true, LangParser.new.calc_test('true'))
60
+ assert_equal(true, LangParser.new.calc_test('true and true'))
61
+ assert_equal(true, LangParser.new.calc_test('true or true'))
62
+ assert_equal(true, LangParser.new.calc_test('true or false'))
63
+ assert_equal(true, LangParser.new.calc_test(' not false'))
64
+
65
+ assert_equal(false, LangParser.new.calc_test('false'))
66
+ assert_equal(false, LangParser.new.calc_test('false and false'))
67
+ assert_equal(false, LangParser.new.calc_test('false and true'))
68
+ assert_equal(false, LangParser.new.calc_test('not true'))
69
+
70
+ assert_equal(true, LangParser.new.calc_test('(not false) and true'))
71
+ assert_equal(true, LangParser.new.calc_test('(not false) or false'))
72
+ assert_equal(true, LangParser.new.calc_test('true and true or false'))
73
+
74
+ end
75
+ end
76
+
77
+ class TestComparisons < Test::Unit::TestCase
78
+ def test_comparisons
79
+ assert_equal(true, LangParser.new.calc_test('1 < 2'))
80
+ assert_equal(false, LangParser.new.calc_test('1 > 2'))
81
+ assert_equal(true, LangParser.new.calc_test('1 < 2 and true'))
82
+ assert_equal(false, LangParser.new.calc_test('1 > 2 and true'))
83
+ assert_equal(false, LangParser.new.calc_test('1 > 2 + 3 and true'))
84
+ assert_equal(true, LangParser.new.calc_test('1+2 < 2+2 and true'))
85
+ assert_equal(true, LangParser.new.calc_test(' not 1 > 2 and true'))
86
+ assert_equal(true, LangParser.new.calc_test(' true and not 1 > 2'))
87
+ assert_equal(true, LangParser.new.calc_test(' not 1 > 2 and true'))
88
+
89
+ assert_equal(true, LangParser.new.calc_test(' not 1 > 2 and true or false'))
90
+ assert_equal(true, LangParser.new.calc_test(' not 1 > 2 and (true or false)'))
91
+ assert_equal(true, LangParser.new.calc_test(' not 1 > 2 and false or true and true'))
92
+
93
+ assert_equal(true, LangParser.new.calc_test(' 1 == 1'))
94
+ assert_equal(false, LangParser.new.calc_test(' 1 == 2'))
95
+ assert_equal(true, LangParser.new.calc_test(' not 1 == 2'))
96
+ assert_equal(false, LangParser.new.calc_test(' not 1 == 1'))
97
+
98
+ assert_equal(true, LangParser.new.calc_test(' 1 <= 1'))
99
+ assert_equal(false, LangParser.new.calc_test(' 3 <= 2'))
100
+ assert_equal(true, LangParser.new.calc_test(' 1 <= 2'))
101
+
102
+ assert_equal(true, LangParser.new.calc_test(' 1 >= 1'))
103
+ assert_equal(true, LangParser.new.calc_test(' 3 >= 2'))
104
+ assert_equal(false, LangParser.new.calc_test(' 1 >= 2'))
105
+ end
106
+ end
107
+
108
+ class TestIf < Test::Unit::TestCase
109
+ def test_if
110
+ assert_equal(2, LangParser.new.calc_test(' if true
111
+ 1+1
112
+ end'))
113
+
114
+ assert_equal(2, LangParser.new.calc_test(' if 3 == 3
115
+ 1+1
116
+ end'))
117
+
118
+ assert_equal(nil, LangParser.new.calc_test('if 3 == 2
119
+ 1+1
120
+ end'))
121
+ end
122
+ end
123
+ class TestVariables < Test::Unit::TestCase
124
+ def test_variables
125
+ assert_equal(1, LangParser.new.calc_test('x = 1
126
+ x'))
127
+
128
+ assert_equal(1, LangParser.new.calc_test('x = 1
129
+ x
130
+ x'))
131
+
132
+ assert_equal(1, LangParser.new.calc_test('x = 1
133
+ x
134
+ x + 1
135
+ x'))
136
+
137
+ assert_equal(3, LangParser.new.calc_test('x = 1
138
+ x + 2'))
139
+
140
+ assert_equal(2, LangParser.new.calc_test('x = 1
141
+ x = 2
142
+ x'))
143
+
144
+ assert_equal(5, LangParser.new.calc_test('x = 1 + 2
145
+ x + 2'))
146
+
147
+ assert_equal(5, LangParser.new.calc_test('x = 1 + 2
148
+ x = x + 2
149
+ x'))
150
+ end
151
+ end
152
+
153
+ class TestFunction < Test::Unit::TestCase
154
+ def test_function
155
+ assert_equal(nil, LangParser.new.calc_test(' def foo()
156
+ 5+5
157
+ end'))
158
+
159
+ assert_equal(10, LangParser.new.calc_test(' def foo()
160
+ 5+5
161
+ end
162
+ foo()'))
163
+
164
+ assert_equal(10, LangParser.new.calc_test(' def boo()
165
+ a = 5
166
+ a
167
+ end
168
+ b = 5 + boo()
169
+ b'))
170
+ end
171
+ end
172
+
173
+ class TestLoop < Test::Unit::TestCase
174
+ def test_while
175
+ assert_equal(WhileLoopNode, LangParser.new.calc_test('x = 5
176
+ while x < 10
177
+ x = x + 1
178
+ end'))
179
+
180
+ assert_equal(10, LangParser.new.calc_test('x = 5
181
+ while x < 10
182
+ x = x + 1
183
+ end
184
+ x'))
185
+
186
+ assert_equal(-10, LangParser.new.calc_test('x = 0
187
+ while x > -10
188
+ x = x - 1
189
+ end
190
+ x'))
191
+
192
+ # assert_equal(0, LangParser.new.calc_test('x = 10
193
+ # while -x < 0 # fundera på negering av variabler
194
+ # x = x + 1
195
+ # end
196
+ # x'))
197
+ end
198
+ end
199
+
200
+ class TesCompactIf < Test::Unit::TestCase
201
+ def test_CompactIf
202
+ assert_equal(4, LangParser.new.calc_test('x = 4
203
+ if 7 <= 10 and > 5
204
+ x
205
+ end'))
206
+
207
+ assert_equal(8, LangParser.new.calc_test('x = 8
208
+ if x < 10 and > 5
209
+ x
210
+ end'))
211
+
212
+ assert_equal(nil, LangParser.new.calc_test('x = 2
213
+ if x <= 10 and >= 5
214
+ x
215
+ end'))
216
+ end
217
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alphalang
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - mattias
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-05-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: optparse
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.3'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.3.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.3'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.3.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: logger
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.5'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.5.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.5'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.5.0
53
+ description: Abstract Syntax Tree building language with a recursive descent parser
54
+ email:
55
+ - mattiasreuterskiold@gmail.com
56
+ executables:
57
+ - alphalang
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - bin/alphalang
62
+ - lib/alpha.rb
63
+ - lib/lang_creator.rb
64
+ - lib/locales/de
65
+ - lib/locales/en
66
+ - lib/locales/locale_template
67
+ - lib/locales/sv
68
+ - lib/nodes/#stmtnodes.rb#
69
+ - lib/nodes/basenodes.rb
70
+ - lib/nodes/stmtnodes.rb
71
+ - lib/rdparse.rb
72
+ - lib/rdparse_quiet.rb
73
+ - lib/tester/fibonacci.alpha
74
+ - lib/tester/test_unit.rb
75
+ homepage: https://portfolio.reuterskiold.dev
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubygems_version: 3.5.4
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: alphalanguage
98
+ test_files: []