alphalang 0.1.1

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.
@@ -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: []