pycplus 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/bin/pycplus +45 -42
- data/lib/nodes.rb +33 -13
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f3bc5bfe8923bf561c4a2b3167a862151f1487d62b84bb1587f9f07b0433c63
|
4
|
+
data.tar.gz: 4872ec376c3501b869ed773b0a535b894abe35d9af05af81784cf081a34696dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84dd3244332bfe07a86e8448276f0bdb7ebd5318696ba882ab707fbb8e82aaeba63e40c6cc531a166012461df549a47bede2e9a07bab81cf58255f1d6faeb784
|
7
|
+
data.tar.gz: 3002a3bc7ce8f595fd1f84371796cd4ad0ce9de8ad4f0d08c5dbe7278685c9278f8be3fbc7f7d8618b923ef03af483c0e0f4d65cda9b934918f3bc3ff0c918b6
|
data/bin/pycplus
CHANGED
@@ -7,59 +7,62 @@ require_relative '../lib/pcpparse'
|
|
7
7
|
|
8
8
|
# Method to print gem version
|
9
9
|
def print_version
|
10
|
-
|
11
|
-
|
10
|
+
begin
|
11
|
+
spec = Gem.loaded_specs['pycplus']
|
12
|
+
puts "#{spec.name} version #{spec.version}"
|
13
|
+
rescue
|
14
|
+
begin
|
15
|
+
spec = Gem::Specification.load('../pycplus.gemspec')
|
16
|
+
puts "#{spec.name} version #{spec.version}"
|
17
|
+
rescue
|
18
|
+
puts "Error: Unable to find version for pycplus gem"
|
19
|
+
end
|
20
|
+
end
|
12
21
|
end
|
13
22
|
|
14
|
-
def main
|
15
|
-
# Parse other command-line options using OptionParser
|
16
|
-
options = {}
|
17
|
-
OptionParser.new do |opts|
|
18
23
|
|
19
|
-
|
24
|
+
# Parse other command-line options using OptionParser
|
25
|
+
options = {}
|
26
|
+
OptionParser.new do |opts|
|
20
27
|
|
21
|
-
|
22
|
-
opts.on("-v", "--version", "Display version information") do
|
23
|
-
print_version
|
24
|
-
exit
|
25
|
-
end
|
28
|
+
opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] filename"
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
30
|
+
# version is handled as a special case as it doesn't make sense to use it alongside the other options
|
31
|
+
opts.on("-v", "--version", "Display version information") do
|
32
|
+
print_version
|
33
|
+
exit
|
34
|
+
end
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
exit 1
|
36
|
+
opts.on("-d", "--debug", "Enable debug mode") do
|
37
|
+
options[:debug] = true
|
38
|
+
print("dhsdhshdshds")
|
37
39
|
end
|
40
|
+
end.parse!
|
38
41
|
|
39
|
-
|
40
|
-
|
42
|
+
# Check if a filename argument is provided
|
43
|
+
if ARGV.empty?
|
44
|
+
puts "Error: No filename or arguments provided."
|
45
|
+
exit 1
|
46
|
+
end
|
41
47
|
|
42
|
-
|
43
|
-
|
44
|
-
@lang_parser.log(true)
|
45
|
-
else
|
46
|
-
@lang_parser.log(false)
|
47
|
-
end
|
48
|
+
filename = ARGV.shift
|
49
|
+
@lang_parser = Pycplus.new
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
+
# Enable debug mode if specified
|
52
|
+
if options[:debug]
|
53
|
+
@lang_parser.log(true)
|
54
|
+
else
|
55
|
+
@lang_parser.log(false)
|
56
|
+
end
|
51
57
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
if File.file?(filename)
|
59
|
+
extension = File.extname(filename)
|
60
|
+
|
61
|
+
if extension == ".pcp"
|
62
|
+
@lang_parser.parse_file(filename)
|
57
63
|
else
|
58
|
-
puts "Error:
|
64
|
+
puts "Error: File is not a .pcp file. Please provide a file with the correct extension."
|
59
65
|
end
|
66
|
+
else
|
67
|
+
puts "Error: Unable to find file #{filename}"
|
60
68
|
end
|
61
|
-
|
62
|
-
|
63
|
-
if __FILE__ == $PROGRAM_NAME
|
64
|
-
main
|
65
|
-
end
|
data/lib/nodes.rb
CHANGED
@@ -2,38 +2,51 @@
|
|
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 :identifiers,:parent_scope
|
7
|
+
attr_reader :identifiers,:parent_scope, :non_local_variables
|
7
8
|
def initialize(parent_scope = nil)
|
8
9
|
@identifiers = {}
|
10
|
+
@non_local_variables = []
|
9
11
|
@parent_scope = parent_scope
|
10
12
|
end
|
11
13
|
|
14
|
+
# Set variable in current scope
|
12
15
|
def set_variable(name, op, expression)
|
16
|
+
if @non_local_variables.include?(name)
|
17
|
+
raise NameError, "local identifier #{name} referenced before assignment"
|
18
|
+
end
|
13
19
|
if op == '='
|
14
20
|
@identifiers[name] = expression
|
15
21
|
else
|
16
|
-
|
17
|
-
|
22
|
+
unless @identifiers.has_key?(name)
|
23
|
+
raise NameError, "local identifier #{name} referenced before assignment"
|
24
|
+
end
|
18
25
|
if op == '+='
|
19
|
-
@identifiers[name] = 0 if !id_scope
|
20
26
|
@identifiers[name] += expression
|
21
27
|
else
|
22
|
-
@identifiers[name] = 0 if !id_scope
|
23
28
|
@identifiers[name] -= expression
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
27
32
|
|
33
|
+
def set_non_local_variable(name)
|
34
|
+
unless @non_local_variables.include?(name) && is_function(name)
|
35
|
+
@non_local_variables.append(name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get value of identifier in current or parent scope(s) if defined.
|
28
40
|
def get_identifier(name)
|
29
41
|
scope = get_scope(name)
|
30
42
|
if scope
|
31
43
|
return scope.identifiers[name]
|
32
44
|
else
|
33
|
-
raise NameError,"Identifier #{name} not defined."
|
45
|
+
raise NameError,"Identifier #{name} not defined in scope."
|
34
46
|
end
|
35
47
|
end
|
36
48
|
|
49
|
+
# Get first scope where identifier is defined.
|
37
50
|
def get_scope(name)
|
38
51
|
if @identifiers.has_key?(name)
|
39
52
|
return self
|
@@ -42,17 +55,20 @@ class Scope
|
|
42
55
|
end
|
43
56
|
end
|
44
57
|
|
58
|
+
# Checks if a identifier is a function.
|
45
59
|
def is_function?(name)
|
46
60
|
scope = get_scope(name)
|
47
61
|
id_value = scope.get_identifier(name) if scope
|
48
62
|
return id_value.is_a?(Hash)
|
49
63
|
end
|
50
64
|
|
65
|
+
# Set a function in current scope.
|
51
66
|
def set_function(name, block, parameters)
|
52
67
|
@identifiers[name] = {:parameters => parameters, :block => block}
|
53
68
|
end
|
54
69
|
end
|
55
70
|
|
71
|
+
|
56
72
|
class BlockNode
|
57
73
|
def initialize(statements = nil)
|
58
74
|
@block = statements
|
@@ -102,10 +118,10 @@ class FunctioncallNode
|
|
102
118
|
return result
|
103
119
|
end
|
104
120
|
|
105
|
-
def evaluate_block(parameters, block,
|
106
|
-
new_scope = Scope.new(
|
121
|
+
def evaluate_block(parameters, block, current_scope, id_scope)
|
122
|
+
new_scope = Scope.new(id_scope)
|
107
123
|
parameters.zip(@arguments).each do |parameter, argument|
|
108
|
-
new_scope.set_variable(parameter, '=', argument.evaluate(
|
124
|
+
new_scope.set_variable(parameter, '=', argument.evaluate(current_scope))
|
109
125
|
end
|
110
126
|
|
111
127
|
result = block.evaluate(new_scope)
|
@@ -135,7 +151,7 @@ class FunctioncallNode
|
|
135
151
|
parameters = function[:parameters]
|
136
152
|
block = function[:block]
|
137
153
|
validate_arguments(parameters)
|
138
|
-
evaluate_block(parameters, block,
|
154
|
+
evaluate_block(parameters, block,scope,id_scope)
|
139
155
|
end
|
140
156
|
end
|
141
157
|
end
|
@@ -274,9 +290,12 @@ class IdentifierNode < PrimitiveNode
|
|
274
290
|
def evaluate(scope)
|
275
291
|
if scope.is_function?(@value)
|
276
292
|
raise SyntaxError, "Identifier #{@value} is assigned to a function. Please use correct syntax for function call."
|
277
|
-
else
|
278
|
-
return scope.get_identifier(@value)
|
279
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
|
280
299
|
end
|
281
300
|
end
|
282
301
|
|
@@ -287,4 +306,5 @@ class DigitNode < PrimitiveNode
|
|
287
306
|
end
|
288
307
|
|
289
308
|
class BoolNode < PrimitiveNode
|
290
|
-
end
|
309
|
+
end
|
310
|
+
|