pycplus 0.1.2 → 0.2.0
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.
- checksums.yaml +4 -4
- data/bin/pycplus +0 -0
- data/lib/STL.rb +56 -8
- data/lib/nodes.rb +89 -92
- data/lib/{lang.rb → pcpparse.rb} +59 -60
- data/lib/rdparse.rb +1 -1
- metadata +6 -7
- data/lib/test.rb +0 -161
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd13b4d71513629e82bfdc275b30886005d1f0c7958c96562d4f9f8b0703ccc2
|
4
|
+
data.tar.gz: a925d460e45d955bef30bc3fb466b766f8e2aa26867763e70e82b613e8310dcf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5927e705387375c7c05b90029993c51639c0bd523f117a435a7a531a4f9918d54e0feb7c622d049a29b8be2d8f0c239497c7ab9fb1d138214b600dfadf4a958f
|
7
|
+
data.tar.gz: 4deab1e187a588b7e2c19a6449252503c93be7057d785d19ed267956b587be60713e374347fe291b5f9cc34a43930590119244cc54798fb76152296543e153e1
|
data/bin/pycplus
CHANGED
File without changes
|
data/lib/STL.rb
CHANGED
@@ -1,12 +1,60 @@
|
|
1
|
-
def print(x)
|
2
|
-
pp x
|
3
|
-
end
|
4
1
|
|
5
|
-
module
|
6
|
-
def self.
|
7
|
-
|
2
|
+
module STL
|
3
|
+
def self.print(x)
|
4
|
+
pp x
|
5
|
+
return nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.size(array)
|
9
|
+
unless array.is_a?(Array)
|
10
|
+
raise TypeError, "Function #{__method__} must be used on an array."
|
11
|
+
end
|
12
|
+
|
13
|
+
return array.count
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.pop(array)
|
17
|
+
unless array.is_a?(Array)
|
18
|
+
raise TypeError, "Function #{__method__} must be used on an array."
|
19
|
+
end
|
20
|
+
if array.empty?
|
21
|
+
raise IndexError, "Can not use pop on an empty array."
|
22
|
+
end
|
23
|
+
pop_element = array.last
|
24
|
+
array.delete_at(-1)
|
25
|
+
return pop_element
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.push(array, element)
|
29
|
+
unless array.is_a?(Array)
|
30
|
+
raise TypeError, "Function #{__method__} must be used on an array."
|
31
|
+
end
|
32
|
+
array << element
|
33
|
+
return array
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.get_element(array, index)
|
37
|
+
unless array.is_a?(Array)
|
38
|
+
raise TypeError, "Function #{__method__} must be used on an array."
|
39
|
+
end
|
40
|
+
unless index.between?(0, array.size-1)
|
41
|
+
raise IndexError, "Index out of bounds. Max index: #{array.size-1}."
|
42
|
+
end
|
43
|
+
|
44
|
+
element = array[index]
|
45
|
+
return element
|
8
46
|
end
|
9
47
|
|
10
|
-
def self.
|
48
|
+
def self.delete_element(array, index)
|
49
|
+
unless array.is_a?(Array)
|
50
|
+
raise TypeError, "Function #{__method__} must be used on an array."
|
51
|
+
end
|
52
|
+
unless index.between?(0, array.size-1)
|
53
|
+
raise IndexError, "Index out of bounds. Max index: #{array.size-1}."
|
54
|
+
end
|
55
|
+
|
56
|
+
element = array[index]
|
57
|
+
array.delete_at(index)
|
58
|
+
return element
|
11
59
|
end
|
12
|
-
end
|
60
|
+
end
|
data/lib/nodes.rb
CHANGED
@@ -2,78 +2,70 @@
|
|
2
2
|
|
3
3
|
require_relative 'STL.rb'
|
4
4
|
|
5
|
+
# Class to handle scopes when evaluating AST.
|
5
6
|
class Scope
|
6
|
-
attr_reader :
|
7
|
+
attr_reader :identifiers,:parent_scope, :non_local_variables
|
7
8
|
def initialize(parent_scope = nil)
|
8
|
-
@
|
9
|
-
@
|
9
|
+
@identifiers = {}
|
10
|
+
@non_local_variables = []
|
10
11
|
@parent_scope = parent_scope
|
11
12
|
end
|
12
13
|
|
14
|
+
# Set variable in current scope
|
13
15
|
def set_variable(name, op, expression)
|
14
|
-
|
15
|
-
|
16
|
-
@variables[name] = expression
|
17
|
-
when '+='
|
18
|
-
@variables[name] += expression
|
19
|
-
when '-='
|
20
|
-
@variables[name] -= expression
|
16
|
+
if @non_local_variables.include?(name)
|
17
|
+
raise NameError, "local identifier #{name} referenced before assignment"
|
21
18
|
end
|
19
|
+
if op == '='
|
20
|
+
@identifiers[name] = expression
|
21
|
+
else
|
22
|
+
unless @identifiers.has_key?(name)
|
23
|
+
raise NameError, "local identifier #{name} referenced before assignment"
|
24
|
+
end
|
25
|
+
if op == '+='
|
26
|
+
@identifiers[name] += expression
|
27
|
+
else
|
28
|
+
@identifiers[name] -= expression
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
22
32
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
if variable_scope
|
28
|
-
variable_scope.set_variable(name, op, expression)
|
33
|
+
def set_non_local_variable(name)
|
34
|
+
unless @non_local_variables.include?(name) && is_function(name)
|
35
|
+
@non_local_variables.append(name)
|
29
36
|
end
|
30
37
|
end
|
31
38
|
|
32
|
-
|
33
|
-
|
39
|
+
# Get value of identifier in current or parent scope(s) if defined.
|
40
|
+
def get_identifier(name)
|
41
|
+
scope = get_scope(name)
|
34
42
|
if scope
|
35
|
-
return scope.
|
43
|
+
return scope.identifiers[name]
|
36
44
|
else
|
37
|
-
raise "
|
45
|
+
raise NameError,"Identifier #{name} not defined in scope."
|
38
46
|
end
|
39
47
|
end
|
40
48
|
|
41
|
-
|
42
|
-
|
49
|
+
# Get first scope where identifier is defined.
|
50
|
+
def get_scope(name)
|
51
|
+
if @identifiers.has_key?(name)
|
43
52
|
return self
|
44
53
|
elsif @parent_scope
|
45
|
-
return @parent_scope.get_scope(name
|
54
|
+
return @parent_scope.get_scope(name)
|
46
55
|
end
|
47
56
|
end
|
48
57
|
|
58
|
+
# Checks if a identifier is a function.
|
59
|
+
def is_function?(name)
|
60
|
+
scope = get_scope(name)
|
61
|
+
id_value = scope.get_identifier(name) if scope
|
62
|
+
return id_value.is_a?(Hash)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set a function in current scope.
|
49
66
|
def set_function(name, block, parameters)
|
50
|
-
@
|
51
|
-
if @parent_scope
|
52
|
-
function_scope = @parent_scope.get_scope(name, :functions)
|
53
|
-
end
|
54
|
-
if function_scope
|
55
|
-
function_scope.set_function(name, block, parameters)
|
56
|
-
end
|
67
|
+
@identifiers[name] = {:parameters => parameters, :block => block}
|
57
68
|
end
|
58
|
-
|
59
|
-
# def initialize_STL
|
60
|
-
# return unless defined?(STL)
|
61
|
-
# # STL.methods(false).each do |name|
|
62
|
-
# # method_object = STL.method(name)
|
63
|
-
# # @functions[name] = {:parameters => method_object.parameters.flatten[1..-1], :block => method_object}
|
64
|
-
# # end
|
65
|
-
# STL.singleton_methods(false).each do |name|
|
66
|
-
# method_object = STL.method(name)
|
67
|
-
# parameters = method_object.parameters.map { |type, name| name }.compact
|
68
|
-
# @functions[name] = { parameters: parameters, block: method_object }
|
69
|
-
# end
|
70
|
-
|
71
|
-
# STL.instance_methods(false).each do |name|
|
72
|
-
# method_object = STL.instance_method(name)
|
73
|
-
# parameters = method_object.parameters.map { |type, name| name }.compact
|
74
|
-
# @functions[name] = { parameters: parameters, block: method_object }
|
75
|
-
# end
|
76
|
-
# end
|
77
69
|
end
|
78
70
|
|
79
71
|
|
@@ -84,10 +76,12 @@ class BlockNode
|
|
84
76
|
|
85
77
|
def evaluate(scope)
|
86
78
|
return unless @block
|
79
|
+
result = nil
|
87
80
|
@block.each do |statement|
|
88
81
|
result = statement.evaluate(scope)
|
89
82
|
return result if result.is_a?(ReturnValue)
|
90
83
|
end
|
84
|
+
return result
|
91
85
|
end
|
92
86
|
end
|
93
87
|
|
@@ -105,27 +99,29 @@ class FunctiondefNode
|
|
105
99
|
end
|
106
100
|
|
107
101
|
class FunctioncallNode
|
108
|
-
|
102
|
+
include STL
|
103
|
+
def initialize(identifier, object = nil, arguments = [])
|
109
104
|
@identifier = identifier.value
|
110
105
|
@arguments = arguments
|
106
|
+
@object = object
|
111
107
|
end
|
112
108
|
|
113
109
|
def validate_arguments(parameters)
|
114
110
|
if @arguments.size != parameters.size
|
115
|
-
raise "Wrong number of arguments (given: #{@arguments.size} expected: #{parameters.size})."
|
111
|
+
raise ArgumentError, "Wrong number of arguments (given: #{@arguments.size} expected: #{parameters.size})."
|
116
112
|
end
|
117
113
|
end
|
118
114
|
|
119
|
-
def evaluate_method(
|
115
|
+
def evaluate_method(scope)
|
120
116
|
args = @arguments.map {|arg| arg.evaluate(scope)}
|
121
|
-
|
117
|
+
result = STL.send(@identifier, *args)
|
118
|
+
return result
|
122
119
|
end
|
123
120
|
|
124
|
-
def evaluate_block(parameters, block,
|
125
|
-
new_scope = Scope.new(
|
126
|
-
|
121
|
+
def evaluate_block(parameters, block, current_scope, id_scope)
|
122
|
+
new_scope = Scope.new(id_scope)
|
127
123
|
parameters.zip(@arguments).each do |parameter, argument|
|
128
|
-
new_scope.set_variable(parameter, '=', argument.evaluate(
|
124
|
+
new_scope.set_variable(parameter, '=', argument.evaluate(current_scope))
|
129
125
|
end
|
130
126
|
|
131
127
|
result = block.evaluate(new_scope)
|
@@ -133,40 +129,47 @@ class FunctioncallNode
|
|
133
129
|
end
|
134
130
|
|
135
131
|
def evaluate(scope)
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
132
|
+
|
133
|
+
id_scope = scope.get_scope(@identifier)
|
134
|
+
if id_scope && !id_scope.is_function?(@identifier)
|
135
|
+
raise TypeError, "Identifier #{@identifier} is not defined as a function."
|
136
|
+
end
|
137
|
+
if @object
|
138
|
+
@arguments.unshift(@object)
|
139
|
+
end
|
140
|
+
if !id_scope
|
141
|
+
if STL.respond_to?(@identifier)
|
142
|
+
method = STL.method(@identifier)
|
143
|
+
parameters = method.parameters
|
141
144
|
validate_arguments(parameters)
|
142
|
-
evaluate_method(
|
145
|
+
evaluate_method(scope)
|
143
146
|
else
|
144
|
-
raise "Function #{@identifier} not
|
147
|
+
raise NameError, "Function #{@identifier} not defined."
|
145
148
|
end
|
146
149
|
else
|
147
|
-
function =
|
150
|
+
function = id_scope.get_identifier(@identifier)
|
148
151
|
parameters = function[:parameters]
|
149
152
|
block = function[:block]
|
150
153
|
validate_arguments(parameters)
|
151
|
-
evaluate_block(parameters, block,
|
154
|
+
evaluate_block(parameters, block,scope,id_scope)
|
152
155
|
end
|
153
156
|
end
|
154
157
|
end
|
155
158
|
|
156
159
|
class ProgramNode
|
160
|
+
attr_reader :statements
|
157
161
|
def initialize(statements)
|
158
162
|
@statements = statements
|
159
163
|
end
|
160
164
|
|
161
165
|
def evaluate
|
162
166
|
global_scope = Scope.new
|
163
|
-
|
164
|
-
|
167
|
+
result = nil
|
165
168
|
@statements.each do |statement|
|
166
169
|
result = statement.evaluate(global_scope)
|
167
170
|
return result.value if statement.is_a?(ReturnNode)
|
168
171
|
end
|
169
|
-
return
|
172
|
+
return result
|
170
173
|
end
|
171
174
|
end
|
172
175
|
|
@@ -187,7 +190,6 @@ class ReturnNode
|
|
187
190
|
end
|
188
191
|
end
|
189
192
|
|
190
|
-
|
191
193
|
class BinaryexpressionNode
|
192
194
|
def initialize(lhs, op, rhs)
|
193
195
|
@lhs = lhs
|
@@ -196,7 +198,11 @@ class BinaryexpressionNode
|
|
196
198
|
end
|
197
199
|
|
198
200
|
def evaluate(scope)
|
199
|
-
|
201
|
+
rhs_value = @rhs.evaluate(scope)
|
202
|
+
if @op == :fdiv && rhs_value == 0
|
203
|
+
raise ZeroDivisionError,"Division by 0 not possible."
|
204
|
+
end
|
205
|
+
return @lhs.evaluate(scope).send(@op, rhs_value)
|
200
206
|
end
|
201
207
|
end
|
202
208
|
|
@@ -254,38 +260,21 @@ end
|
|
254
260
|
class WhileNode < ControlflowNode
|
255
261
|
def evaluate(scope)
|
256
262
|
while @expression.evaluate(scope)
|
257
|
-
|
263
|
+
@block.evaluate(scope)
|
258
264
|
end
|
259
265
|
end
|
260
266
|
end
|
261
267
|
|
262
|
-
|
263
|
-
# Unsure about implementation, will check with project assistant
|
264
268
|
class ArrayNode
|
265
269
|
def initialize(elements = [])
|
266
270
|
@elements = elements
|
267
|
-
@data_container = {}
|
268
271
|
end
|
269
272
|
|
270
273
|
def evaluate(scope)
|
271
|
-
@elements.
|
272
|
-
@data_container[index] = element.evaluate(scope)
|
273
|
-
end
|
274
|
+
return @elements.map {|element| element.evaluate(scope)}
|
274
275
|
end
|
275
276
|
end
|
276
277
|
|
277
|
-
|
278
|
-
# Not implemented yet
|
279
|
-
class StringNode
|
280
|
-
def initialize(chars = [])
|
281
|
-
@chars = chars
|
282
|
-
end
|
283
|
-
|
284
|
-
def evaluate(scope)
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
|
289
278
|
class PrimitiveNode
|
290
279
|
attr_reader :value
|
291
280
|
def initialize(value)
|
@@ -299,7 +288,14 @@ end
|
|
299
288
|
|
300
289
|
class IdentifierNode < PrimitiveNode
|
301
290
|
def evaluate(scope)
|
302
|
-
|
291
|
+
if scope.is_function?(@value)
|
292
|
+
raise SyntaxError, "Identifier #{@value} is assigned to a function. Please use correct syntax for function call."
|
293
|
+
end
|
294
|
+
id_value = scope.get_identifier(@value)
|
295
|
+
unless scope.identifiers.has_key?(@value)
|
296
|
+
scope.set_non_local_variable(@value)
|
297
|
+
end
|
298
|
+
return id_value
|
303
299
|
end
|
304
300
|
end
|
305
301
|
|
@@ -310,4 +306,5 @@ class DigitNode < PrimitiveNode
|
|
310
306
|
end
|
311
307
|
|
312
308
|
class BoolNode < PrimitiveNode
|
313
|
-
end
|
309
|
+
end
|
310
|
+
|
data/lib/{lang.rb → pcpparse.rb}
RENAMED
@@ -3,17 +3,20 @@
|
|
3
3
|
require_relative 'rdparse.rb'
|
4
4
|
require_relative 'nodes.rb'
|
5
5
|
|
6
|
-
class
|
6
|
+
class Pycplus
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
|
10
|
-
@
|
10
|
+
@pycplus_parser = Parser.new("Pycplus parser") do
|
11
|
+
token(/#[^#]*#/)
|
11
12
|
token(/\n/)
|
12
|
-
# token(/\\/)
|
13
|
-
# token(/"/) {|m| m}
|
14
|
-
token(/"[^"]"/) {|m| m.chars}
|
15
13
|
token(/\s+/)
|
16
|
-
token(/if/) {
|
14
|
+
token(/if/) {:if}
|
15
|
+
token(/while/) {:while}
|
16
|
+
token(/def/) {:def}
|
17
|
+
token(/return/) {:return}
|
18
|
+
token(/false/) {|m| m}
|
19
|
+
token(/true/) {|m| m}
|
17
20
|
token(/>=/) {|m| m}
|
18
21
|
token(/<=/) {|m| m}
|
19
22
|
token(/==/) {|m| m}
|
@@ -22,11 +25,6 @@ class MyLang
|
|
22
25
|
token(/\-=/) {|m| m}
|
23
26
|
token(/&&/) {|m| m}
|
24
27
|
token(/\|\|/) {|m| m}
|
25
|
-
token(/true/) {|m| m}
|
26
|
-
token(/false/) {|m| m}
|
27
|
-
token(/return/) {|m| m}
|
28
|
-
token(/def/) {|m| m}
|
29
|
-
token(/while/) {|m| m}
|
30
28
|
token(/\d+\.\d+/) {|m| m.to_f}
|
31
29
|
token(/\d+/) {|m| m.to_i}
|
32
30
|
token(/\w+/) {|m| m}
|
@@ -43,17 +41,18 @@ class MyLang
|
|
43
41
|
|
44
42
|
rule :statement do
|
45
43
|
match(:assignment, ';')
|
46
|
-
match(:function_def)
|
47
44
|
match(:function_call, ';')
|
48
|
-
match(:
|
45
|
+
match(:function_def)
|
46
|
+
match(:return_statement, ';')
|
49
47
|
match(:if_statement)
|
50
48
|
match(:while_statement)
|
49
|
+
match(:expression_statement)
|
51
50
|
end
|
52
51
|
|
53
52
|
rule :assignment do
|
54
|
-
match(:identifier, :assignment_OP, :
|
53
|
+
# match(:identifier, :assignment_OP, :function_call) {|a, b, c| AssignmentNode.new(a,b,c)}
|
55
54
|
match(:identifier, :assignment_OP, :logical_expr) {|a, b, c| AssignmentNode.new(a,b,c)}
|
56
|
-
match(:identifier, :assignment_OP, :
|
55
|
+
match(:identifier, :assignment_OP, :array) {|a, b, c| AssignmentNode.new(a,b,c)}
|
57
56
|
end
|
58
57
|
|
59
58
|
rule :assignment_OP do
|
@@ -63,8 +62,8 @@ class MyLang
|
|
63
62
|
end
|
64
63
|
|
65
64
|
rule :function_def do
|
66
|
-
match(
|
67
|
-
match(
|
65
|
+
match(:def, :identifier, '(', :parameters , ')', :block) {|_, a, _, b, _, c| FunctiondefNode.new(a,c,b)}
|
66
|
+
match(:def, :identifier, '(', ')', :block) {|_, a, _, _, b| FunctiondefNode.new(a,b)}
|
68
67
|
end
|
69
68
|
|
70
69
|
rule :parameters do
|
@@ -82,10 +81,17 @@ class MyLang
|
|
82
81
|
end
|
83
82
|
|
84
83
|
rule :function_call do
|
85
|
-
match(:identifier,'(', :arguments, ')') {|a, _, b, _| FunctioncallNode.new(a,b)}
|
84
|
+
match(:identifier,'(', :arguments, ')') {|a, _, b, _| FunctioncallNode.new(a,nil,b)}
|
86
85
|
match(:identifier,'(', ')') {|a, _, _| FunctioncallNode.new(a)}
|
87
|
-
|
88
|
-
|
86
|
+
match(:function_call,'.',:identifier ,'(',')') {|a, _, b, _, _| FunctioncallNode.new(b, a)}
|
87
|
+
match(:function_call,'.',:identifier ,'(',:arguments,')') {|a, _, b, _, c, _| FunctioncallNode.new(b,a,c)}
|
88
|
+
match(:object,'.',:identifier ,'(',')') {|a, _, b, _, _| FunctioncallNode.new(b, a)}
|
89
|
+
match(:object,'.',:identifier ,'(',:arguments,')') {|a, _, b, _, c, _| FunctioncallNode.new(b,a,c)}
|
90
|
+
end
|
91
|
+
|
92
|
+
rule :object do
|
93
|
+
match(:array)
|
94
|
+
match(:atom)
|
89
95
|
end
|
90
96
|
|
91
97
|
rule :arguments do
|
@@ -95,20 +101,25 @@ class MyLang
|
|
95
101
|
|
96
102
|
rule :argument do
|
97
103
|
match(:logical_expr)
|
98
|
-
match(:
|
104
|
+
match(:array)
|
99
105
|
end
|
100
106
|
|
101
|
-
rule :
|
102
|
-
match(
|
103
|
-
match(
|
107
|
+
rule :return_statement do
|
108
|
+
match(:return, :logical_expr) {|_, a| ReturnNode.new(a)}
|
109
|
+
match(:return, :array) {|_, a| ReturnNode.new(a)}
|
110
|
+
# match(:return, :function_call) {|_, a| ReturnNode.new(a)}
|
104
111
|
end
|
105
112
|
|
106
113
|
rule :while_statement do
|
107
|
-
match(
|
114
|
+
match(:while, '(', :logical_expr, ')', :block) {|_,_,a,_,b| WhileNode.new(a,b)}
|
108
115
|
end
|
109
116
|
|
110
117
|
rule :if_statement do
|
111
|
-
match(
|
118
|
+
match(:if, '(', :logical_expr, ')', :block) {|_,_,a,_,b| IfNode.new(a,b)}
|
119
|
+
end
|
120
|
+
|
121
|
+
rule :expression_statement do
|
122
|
+
match(:logical_expr, ';')
|
112
123
|
end
|
113
124
|
|
114
125
|
rule :logical_expr do
|
@@ -153,7 +164,7 @@ class MyLang
|
|
153
164
|
rule :multiplication_OP do
|
154
165
|
match('%') {|a| a}
|
155
166
|
match('*') {|a| a}
|
156
|
-
match('/') {|
|
167
|
+
match('/') {|_| :fdiv}
|
157
168
|
end
|
158
169
|
|
159
170
|
rule :power_expr do
|
@@ -173,21 +184,17 @@ class MyLang
|
|
173
184
|
rule :unary_OP do
|
174
185
|
match('!') {|a| a}
|
175
186
|
match('-') {|a| a}
|
187
|
+
match('+') {|a| a}
|
176
188
|
end
|
177
189
|
|
178
190
|
rule :binary_operand do
|
191
|
+
match(:function_call)
|
179
192
|
match(:bool)
|
180
193
|
match(:digit)
|
181
|
-
match(:function_call)
|
182
194
|
match(:identifier)
|
183
195
|
match('(', :logical_expr, ')') {|_, a, _| a}
|
184
196
|
end
|
185
197
|
|
186
|
-
rule :container do
|
187
|
-
match(:string)
|
188
|
-
match(:array)
|
189
|
-
end
|
190
|
-
|
191
198
|
rule :array do
|
192
199
|
match('[', :elements , ']') {|_, a, _| ArrayNode.new(a)}
|
193
200
|
match('[', ']') {|_,_| ArrayNode.new()}
|
@@ -200,33 +207,18 @@ class MyLang
|
|
200
207
|
|
201
208
|
rule :element do
|
202
209
|
match(:logical_expr)
|
203
|
-
match(:
|
204
|
-
end
|
205
|
-
|
206
|
-
rule :string do
|
207
|
-
match('"', :chars, '"') {|_, a, _| StringNode.new(a)}
|
208
|
-
match('"', '"') {|_, _| StringNode.new()}
|
209
|
-
end
|
210
|
-
|
211
|
-
rule :chars do
|
212
|
-
match(:chars, :char) {|a , b| a << b}
|
213
|
-
match(:char) {|a| [a]}
|
210
|
+
match(:array)
|
214
211
|
end
|
215
212
|
|
216
213
|
rule :atom do
|
217
214
|
match(:bool)
|
218
215
|
match(:digit)
|
219
|
-
match(:char)
|
220
216
|
match(:identifier)
|
221
217
|
end
|
222
218
|
|
223
|
-
rule :char do
|
224
|
-
match(/./) {|a| CharNode.new(a)}
|
225
|
-
end
|
226
|
-
|
227
219
|
rule :bool do
|
228
|
-
match('true') {|
|
229
|
-
match('false') {|
|
220
|
+
match('true') {|a| BoolNode.new(true)}
|
221
|
+
match('false') {|a| BoolNode.new(false)}
|
230
222
|
end
|
231
223
|
|
232
224
|
rule :digit do
|
@@ -235,7 +227,7 @@ class MyLang
|
|
235
227
|
end
|
236
228
|
|
237
229
|
rule :identifier do
|
238
|
-
match(/
|
230
|
+
match(/[_a-zA-Z]+\w*/) {|a| IdentifierNode.new(a.to_sym)}
|
239
231
|
end
|
240
232
|
end
|
241
233
|
end
|
@@ -243,23 +235,30 @@ class MyLang
|
|
243
235
|
def parse_file(filename)
|
244
236
|
file = File.open(filename)
|
245
237
|
file_data = file.read
|
246
|
-
result = @
|
247
|
-
return result.evaluate
|
238
|
+
result = @pycplus_parser.parse(file_data)
|
239
|
+
return result.evaluate if result
|
248
240
|
end
|
249
241
|
|
250
|
-
def parse_string(str, display_output = false)
|
251
|
-
output = @
|
242
|
+
def parse_string(str, return_tree = false, display_output = false)
|
243
|
+
output = @pycplus_parser.parse(str)
|
252
244
|
if display_output
|
253
245
|
puts "=> #{output}"
|
254
246
|
end
|
255
|
-
|
247
|
+
if return_tree
|
248
|
+
return output
|
249
|
+
else
|
250
|
+
return output.evaluate if output
|
251
|
+
end
|
256
252
|
end
|
257
253
|
|
258
254
|
def log(state = true)
|
259
255
|
if state
|
260
|
-
@
|
256
|
+
@pycplus_parser.logger.level = Logger::DEBUG
|
261
257
|
else
|
262
|
-
@
|
258
|
+
@pycplus_parser.logger.level = Logger::WARN
|
263
259
|
end
|
264
260
|
end
|
265
|
-
end
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
# _?[a-zA-Z*]\w*
|
data/lib/rdparse.rb
CHANGED
@@ -219,7 +219,7 @@ class Parser
|
|
219
219
|
def rule(name,&block)
|
220
220
|
@current_rule = Rule.new(name, self)
|
221
221
|
@rules[name] = @current_rule
|
222
|
-
instance_eval
|
222
|
+
instance_eval(&block) # In practise, calls match 1..N times
|
223
223
|
@current_rule = nil
|
224
224
|
end
|
225
225
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pycplus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Johannes
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-05-
|
12
|
+
date: 2024-05-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: test-unit
|
@@ -31,8 +31,8 @@ dependencies:
|
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 3.6.0
|
34
|
-
description: This Ruby Gem provides a framework for creating and utilizing a
|
35
|
-
language
|
34
|
+
description: This Ruby Gem provides a framework for creating and utilizing a programming
|
35
|
+
language developed as part of a school project at LiU (Linköping University).
|
36
36
|
email: test@test.com
|
37
37
|
executables:
|
38
38
|
- pycplus
|
@@ -42,10 +42,9 @@ files:
|
|
42
42
|
- README.md
|
43
43
|
- bin/pycplus
|
44
44
|
- lib/STL.rb
|
45
|
-
- lib/lang.rb
|
46
45
|
- lib/nodes.rb
|
46
|
+
- lib/pcpparse.rb
|
47
47
|
- lib/rdparse.rb
|
48
|
-
- lib/test.rb
|
49
48
|
homepage: https://rubygems.org/gems/pycplus
|
50
49
|
licenses: []
|
51
50
|
metadata: {}
|
@@ -67,5 +66,5 @@ requirements: []
|
|
67
66
|
rubygems_version: 3.3.5
|
68
67
|
signing_key:
|
69
68
|
specification_version: 4
|
70
|
-
summary: A Ruby Gem for creating and using a
|
69
|
+
summary: A Ruby Gem for creating and using a programming language.
|
71
70
|
test_files: []
|
data/lib/test.rb
DELETED
@@ -1,161 +0,0 @@
|
|
1
|
-
require_relative '../lib/lang.rb'
|
2
|
-
require 'test/unit'
|
3
|
-
|
4
|
-
class TestFaculty < Test::Unit::TestCase
|
5
|
-
def setup
|
6
|
-
@lang_parser = MyLang.new
|
7
|
-
@lang_parser.log(false)
|
8
|
-
end
|
9
|
-
|
10
|
-
# # Test for simple addition
|
11
|
-
# def test_addition_expr
|
12
|
-
# assert_equal(1, @lang_parser.parse_string("return 0+1;"))
|
13
|
-
# assert_equal(0, @lang_parser.parse_string("return -1+1;"))
|
14
|
-
# assert_equal(-1, @lang_parser.parse_string("return -2+1;"))
|
15
|
-
# end
|
16
|
-
|
17
|
-
# # Test for simple subtraction
|
18
|
-
# def test_subtraction_expr
|
19
|
-
# assert_equal(1, @lang_parser.parse_string("return 2-1;"))
|
20
|
-
# assert_equal(0, @lang_parser.parse_string("return 1-1;"))
|
21
|
-
# assert_equal(-1, @lang_parser.parse_string("return 1-2;"))
|
22
|
-
# assert_equal(2, @lang_parser.parse_string("return 1--1;"))
|
23
|
-
# end
|
24
|
-
|
25
|
-
# # Test for simple multiplication
|
26
|
-
# def test_multiplication_expr
|
27
|
-
# assert_equal(1, @lang_parser.parse_string("return 1*1;"))
|
28
|
-
# assert_equal(0, @lang_parser.parse_string("return 0*1;"))
|
29
|
-
# assert_equal(-1, @lang_parser.parse_string("return -1*1;"))
|
30
|
-
# end
|
31
|
-
|
32
|
-
# # Test for simple division
|
33
|
-
# def test_division_expr
|
34
|
-
# assert_equal(1, @lang_parser.parse_string("return 1/1;"))
|
35
|
-
# assert_equal(0, @lang_parser.parse_string("return 0/1;"))
|
36
|
-
# assert_equal(-1, @lang_parser.parse_string("return -1/1;"))
|
37
|
-
# end
|
38
|
-
|
39
|
-
# # Test for simple modulo
|
40
|
-
# def test_modulo_expr
|
41
|
-
# assert_equal(0, @lang_parser.parse_string("return 9%3;"))
|
42
|
-
# assert_equal(1, @lang_parser.parse_string("return 5%2;"))
|
43
|
-
# end
|
44
|
-
|
45
|
-
# # Test for parentheses priority
|
46
|
-
# def test_parentheses_priority
|
47
|
-
# assert_equal(3, @lang_parser.parse_string("return (3-2)*3;"))
|
48
|
-
# assert_equal(7, @lang_parser.parse_string("return (3+4)*(6-2)/(2+2);"))
|
49
|
-
# assert_equal(1, @lang_parser.parse_string("return (5-3)/(1*2);"))
|
50
|
-
# end
|
51
|
-
|
52
|
-
# # test for power expressions
|
53
|
-
# def test_power_expr
|
54
|
-
# assert_equal(4, @lang_parser.parse_string("return 2^2;"))
|
55
|
-
# assert_equal(8, @lang_parser.parse_string("return 2^2*2;"))
|
56
|
-
# assert_equal(256, @lang_parser.parse_string("return 2^2^3;"))
|
57
|
-
# end
|
58
|
-
|
59
|
-
# # Test for variable names
|
60
|
-
# def test_variable_names
|
61
|
-
# assert_equal(1, @lang_parser.parse_string("a = 1; return a;"))
|
62
|
-
# assert_equal(1, @lang_parser.parse_string("_a = 1; return _a;"))
|
63
|
-
# assert_equal(1, @lang_parser.parse_string("A131 = 1; return A131;"))
|
64
|
-
|
65
|
-
# # Forbidden variable names throws parsing error
|
66
|
-
# assert_raises Parser::ParseError do
|
67
|
-
# @lang_parser.parse_string("1a = 1;")
|
68
|
-
# end
|
69
|
-
# assert_raises Parser::ParseError do
|
70
|
-
# @lang_parser.parse_string("_1 = 1;")
|
71
|
-
# end
|
72
|
-
# assert_raises Parser::ParseError do
|
73
|
-
# @lang_parser.parse_string("1 = 1;")
|
74
|
-
# end
|
75
|
-
# assert_raises Parser::ParseError do
|
76
|
-
# @lang_parser.parse_string("1 = 1;")
|
77
|
-
# end
|
78
|
-
# assert_raises Parser::ParseError do
|
79
|
-
# @lang_parser.parse_string("_a? = 1;")
|
80
|
-
# end
|
81
|
-
# end
|
82
|
-
|
83
|
-
# # Test for comparsion expressions
|
84
|
-
# def test_comparison_expr
|
85
|
-
# assert_equal(true, @lang_parser.parse_string("return 1 < 2;"))
|
86
|
-
# assert_equal(false, @lang_parser.parse_string("return 1 > 2;"))
|
87
|
-
|
88
|
-
# assert_equal(false, @lang_parser.parse_string("return 1 >= 2;"))
|
89
|
-
# assert_equal(true, @lang_parser.parse_string("return 2 >= 2;"))
|
90
|
-
# assert_equal(true, @lang_parser.parse_string("return 1 <= 2;"))
|
91
|
-
# assert_equal(false, @lang_parser.parse_string("return 3 <= 2;"))
|
92
|
-
|
93
|
-
# assert_equal(false, @lang_parser.parse_string("return 1 == 2;"))
|
94
|
-
# assert_equal(true, @lang_parser.parse_string("return 2 == 2;"))
|
95
|
-
# assert_equal(true, @lang_parser.parse_string("return false == false;"))
|
96
|
-
# assert_equal(true, @lang_parser.parse_string("a=2; return a == 2;"))
|
97
|
-
# assert_equal(false, @lang_parser.parse_string("a=2; return a == 3;"))
|
98
|
-
|
99
|
-
# assert_equal(true, @lang_parser.parse_string("return 1 != 2;"))
|
100
|
-
# assert_equal(false, @lang_parser.parse_string("return 2 != 2;"))
|
101
|
-
# end
|
102
|
-
|
103
|
-
# # Test for bool assignment
|
104
|
-
# def test_assignmen_bool
|
105
|
-
# assert_equal(false, @lang_parser.parse_string("a = false; return a;"))
|
106
|
-
# assert_equal(true, @lang_parser.parse_string("a = true; return a;"))
|
107
|
-
# end
|
108
|
-
|
109
|
-
# # Test for logical expressions
|
110
|
-
# def test_logical_expr
|
111
|
-
# assert_equal(false, @lang_parser.parse_string("a = true && false; return a;"))
|
112
|
-
# assert_equal(true, @lang_parser.parse_string("a = false || true; return a;"))
|
113
|
-
# assert_equal(true, @lang_parser.parse_string("a = true && false || true; return a;"))
|
114
|
-
# end
|
115
|
-
|
116
|
-
# # Test for if statements
|
117
|
-
# def test_if_statement
|
118
|
-
# assert_equal(11, @lang_parser.parse_string("a=1; if(10>1){b=10; a=a+b;} return a;"))
|
119
|
-
# assert_equal(1, @lang_parser.parse_string("a=1; if(a<1){b=10; a=a+b;} return a;"))
|
120
|
-
|
121
|
-
# # Raises error when variable has not been defined in if-statement
|
122
|
-
# assert_raises RuntimeError do
|
123
|
-
# @lang_parser.parse_string("if(10<1){a=33;} return a;")
|
124
|
-
# end
|
125
|
-
# end
|
126
|
-
|
127
|
-
# # Test for define function and call
|
128
|
-
# def test_function_def_and_call
|
129
|
-
# assert_equal(4, @lang_parser.parse_string("def test(z,x){a = z+x; return a+1;} b=test(1,2); return b;"))
|
130
|
-
# assert_equal(11, @lang_parser.parse_string("def test(){a = 10; return a+1;} return test();"))
|
131
|
-
# assert_equal(true, @lang_parser.parse_string("def test(x){return x && true;} return test(true);"))
|
132
|
-
# assert_equal(100, @lang_parser.parse_string("a=1; b=99; def test(x){c=x+b; return c;} return test(a);"))
|
133
|
-
# assert_equal(100, @lang_parser.parse_string("a=1; b=99; def test(x){a=x+b;} test(a); return a;"))
|
134
|
-
|
135
|
-
# # Raises error when functioncall with wrong number of arguments
|
136
|
-
# assert_raises RuntimeError do
|
137
|
-
# @lang_parser.parse_string("def test(){a = 10; return a+1;} return test(1);")
|
138
|
-
# end
|
139
|
-
|
140
|
-
# # Raises error when functioncall with wrong name
|
141
|
-
# assert_raises RuntimeError do
|
142
|
-
# @lang_parser.parse_string("def test(){a = 10; return a+1;} a(1);")
|
143
|
-
# end
|
144
|
-
# end
|
145
|
-
|
146
|
-
# # Test for while-statement
|
147
|
-
# def test_while_statement
|
148
|
-
# assert_equal(10, @lang_parser.parse_string("a=1; while(a<10){a=a+1;} return a;"))
|
149
|
-
# assert_equal(18, @lang_parser.parse_string("a=1; while(a<10){b=a*2; a=a+1;} return b;"))
|
150
|
-
|
151
|
-
# assert_raises RuntimeError do
|
152
|
-
# assert_equal(10, @lang_parser.parse_string("a=1; while(a>10){b=a+1;} return b;"))
|
153
|
-
# end
|
154
|
-
# end
|
155
|
-
|
156
|
-
# Test for recursion
|
157
|
-
def test_recursion
|
158
|
-
# assert_equal(720, @lang_parser.parse_string("def factorial(n){if(n==0 || n==1){return 1;} return n*factorial(n-1);} print(factorial(6);)"))
|
159
|
-
assert_equal(720, @lang_parser.parse_string("x=1; def hahatest(){ print(x); return x;} x=99; def tt(){x=22;} print(hahatest()); tt(); return x;"))
|
160
|
-
end
|
161
|
-
end
|