pycplus 0.1.2 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5de94922fe2d48ec056ce74840f504963188d6cd6346d65e9c7396639087d8a0
4
- data.tar.gz: 4bb5265be59880acaf12d0adc1d38e563f2f9b737dd99f0881f985529b6d498d
3
+ metadata.gz: 2fde7222ccd4137c44659d5df46a12bc2cb57facbb373c129d54af19596aeb93
4
+ data.tar.gz: b86597ee3514507775c3a2be65e9fd8ed1f2778be48d276b45b99898e0ba8223
5
5
  SHA512:
6
- metadata.gz: eed4ab911e3f18860ec2c5975b7b731d3ee93974af681b8d1f2e5f81a10430ca6b75436700e4c542fea7e04c65764ad761c4032c8fe32493342356a73ac10971
7
- data.tar.gz: 459c3eff8977e4e485611db5a77f7d4528316cce6d37e82d1fdc07fd5be8bfdc4347d2354b403fdf966ece64bf7d8d2e5cb6f3683e17a152e16d1b3dc5690fb8
6
+ metadata.gz: 1437d02dc107e13a85893882db8d325e42d46e62783eeb27c7c58cbf4ce08bc844103e011084a36686ed465fac755bb3e45f96b274e9df4788637fd64ae33bfc
7
+ data.tar.gz: 03faa5bad0cd3ca3242d64e236995baa1d03337d68fe013d3152e6f2a82d74919c8311cd2d1c1361486a8ff55515134d7cd7c0e3be9931b9f72533160f6adb74
data/README.md CHANGED
@@ -1,93 +1,3 @@
1
1
  # TDP019
2
2
 
3
-
4
-
5
- ## Getting started
6
-
7
- To make it easy for you to get started with GitLab, here's a list of recommended next steps.
8
-
9
- Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
10
-
11
- ## Add your files
12
-
13
- - [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
14
- - [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
15
-
16
- ```
17
- cd existing_repo
18
- git remote add origin https://gitlab.liu.se/andjo858-ip/tdp019.git
19
- git branch -M main
20
- git push -uf origin main
21
- ```
22
-
23
- ## Integrate with your tools
24
-
25
- - [ ] [Set up project integrations](https://gitlab.liu.se/andjo858-ip/tdp019/-/settings/integrations)
26
-
27
- ## Collaborate with your team
28
-
29
- - [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
30
- - [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
31
- - [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
32
- - [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
33
- - [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
34
-
35
- ## Test and Deploy
36
-
37
- Use the built-in continuous integration in GitLab.
38
-
39
- - [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
40
- - [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
41
- - [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
42
- - [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
43
- - [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
44
-
45
- ***
46
-
47
- # Editing this README
48
-
49
- When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
50
-
51
- ## Suggestions for a good README
52
-
53
- Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
54
-
55
- ## Name
56
- Choose a self-explaining name for your project.
57
-
58
- ## Description
59
- Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
60
-
61
- ## Badges
62
- On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
63
-
64
- ## Visuals
65
- Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
66
-
67
- ## Installation
68
- Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
69
-
70
- ## Usage
71
- Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
72
-
73
- ## Support
74
- Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
75
-
76
- ## Roadmap
77
- If you have ideas for releases in the future, it is a good idea to list them in the README.
78
-
79
- ## Contributing
80
- State if you are open to contributions and what your requirements are for accepting them.
81
-
82
- For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
83
-
84
- You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
85
-
86
- ## Authors and acknowledgment
87
- Show your appreciation to those who have contributed to the project.
88
-
89
- ## License
90
- For open source projects, say how it is licensed.
91
-
92
- ## Project status
93
- If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
3
+ TODO
data/bin/pycplus CHANGED
@@ -1,26 +1,65 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative '../lib/lang.rb'
4
+ require 'optparse'
5
+ require_relative '../lib/pcpparse'
5
6
 
6
7
 
7
- if ARGV.empty?
8
- puts "No arguments provided."
9
- else
10
- arg = ARGV[0]
8
+ # Method to print gem version
9
+ def print_version
10
+ spec = Gem::Specification.load('../pycplus.gemspec')
11
+ puts "#{spec.name} version #{spec.version}"
12
+ end
13
+
14
+ def main
15
+ # Parse other command-line options using OptionParser
16
+ options = {}
17
+ OptionParser.new do |opts|
18
+
19
+ opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] filename"
20
+
21
+ # version is handled as a special case as it doesn't make sense to use it alongside the other options
22
+ opts.on("-v", "--version", "Display version information") do
23
+ print_version
24
+ exit
25
+ end
26
+
27
+ opts.on("-d", "--debug", "Enable debug mode") do
28
+ options[:debug] = true
29
+ print("dhsdhshdshds")
30
+ end
31
+ end.parse!
11
32
 
12
- @lang_parser = MyLang.new
33
+ # Check if a filename argument is provided
34
+ if ARGV.empty?
35
+ puts "Error: No filename or arguments provided."
36
+ exit 1
37
+ end
38
+
39
+ filename = ARGV.shift
40
+ @lang_parser = Pycplus.new
13
41
 
14
- if File.file?(arg)
15
- extension = File.extname(arg)
42
+ # Enable debug mode if specified
43
+ if options[:debug]
44
+ @lang_parser.log(true)
45
+ else
46
+ @lang_parser.log(false)
47
+ end
48
+
49
+ if File.file?(filename)
50
+ extension = File.extname(filename)
16
51
 
17
52
  if extension == ".pcp"
18
- @lang_parser.log(false)
19
- @lang_parser.parse_file(arg)
53
+ @lang_parser.parse_file(filename)
20
54
  else
21
- puts "File is not a .pcp file, make sure to use the correct extension."
55
+ puts "Error: File is not a .pcp file. Please provide a file with the correct extension."
22
56
  end
23
57
  else
24
- puts "Unable to find file #{arg}"
58
+ puts "Error: Unable to find file #{filename}"
25
59
  end
26
60
  end
61
+
62
+
63
+ if __FILE__ == $PROGRAM_NAME
64
+ main
65
+ end
data/lib/STL.rb CHANGED
@@ -1,12 +1,60 @@
1
- def print(x)
2
- pp x
3
- end
4
1
 
5
- module ArrayFunctions
6
- def self.pop()
7
- pop
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.push()
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
@@ -3,80 +3,56 @@
3
3
  require_relative 'STL.rb'
4
4
 
5
5
  class Scope
6
- attr_reader :variables, :functions
6
+ attr_reader :identifiers,:parent_scope
7
7
  def initialize(parent_scope = nil)
8
- @variables = {}
9
- @functions = {}
8
+ @identifiers = {}
10
9
  @parent_scope = parent_scope
11
10
  end
12
11
 
13
12
  def set_variable(name, op, expression)
14
- case op
15
- when '='
16
- @variables[name] = expression
17
- when '+='
18
- @variables[name] += expression
19
- when '-='
20
- @variables[name] -= expression
21
- end
22
-
23
- return unless @parent_scope
24
-
25
- variable_scope = @parent_scope.get_scope(name, :variables)
26
-
27
- if variable_scope
28
- variable_scope.set_variable(name, op, expression)
13
+ if op == '='
14
+ @identifiers[name] = expression
15
+ else
16
+ id_scope = get_scope(name)
17
+ @identifiers[name] = get_identifier(name) if id_scope
18
+ if op == '+='
19
+ @identifiers[name] = 0 if !id_scope
20
+ @identifiers[name] += expression
21
+ else
22
+ @identifiers[name] = 0 if !id_scope
23
+ @identifiers[name] -= expression
24
+ end
29
25
  end
30
26
  end
31
27
 
32
- def get_identifier(name, id_type)
33
- scope = get_scope(name, id_type)
28
+ def get_identifier(name)
29
+ scope = get_scope(name)
34
30
  if scope
35
- return scope.instance_variable_get("@#{id_type}")[name]
31
+ return scope.identifiers[name]
36
32
  else
37
- raise "[#{id_type}] #{name} not found."
33
+ raise NameError,"Identifier #{name} not defined."
38
34
  end
39
35
  end
40
36
 
41
- def get_scope(name, id_type)
42
- if instance_variable_get("@#{id_type}").has_key?(name)
37
+ def get_scope(name)
38
+ if @identifiers.has_key?(name)
43
39
  return self
44
40
  elsif @parent_scope
45
- return @parent_scope.get_scope(name, id_type)
41
+ return @parent_scope.get_scope(name)
46
42
  end
47
43
  end
48
44
 
45
+ def is_function?(name)
46
+ scope = get_scope(name)
47
+ id_value = scope.get_identifier(name) if scope
48
+ return id_value.is_a?(Hash)
49
+ end
50
+
49
51
  def set_function(name, block, parameters)
50
- @functions[name] = {:parameters => parameters, :block => block}
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
52
+ @identifiers[name] = {:parameters => parameters, :block => block}
57
53
  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
54
  end
78
55
 
79
-
80
56
  class BlockNode
81
57
  def initialize(statements = nil)
82
58
  @block = statements
@@ -84,10 +60,12 @@ class BlockNode
84
60
 
85
61
  def evaluate(scope)
86
62
  return unless @block
63
+ result = nil
87
64
  @block.each do |statement|
88
65
  result = statement.evaluate(scope)
89
66
  return result if result.is_a?(ReturnValue)
90
67
  end
68
+ return result
91
69
  end
92
70
  end
93
71
 
@@ -105,25 +83,27 @@ class FunctiondefNode
105
83
  end
106
84
 
107
85
  class FunctioncallNode
108
- def initialize(identifier, arguments = [])
86
+ include STL
87
+ def initialize(identifier, object = nil, arguments = [])
109
88
  @identifier = identifier.value
110
89
  @arguments = arguments
90
+ @object = object
111
91
  end
112
92
 
113
93
  def validate_arguments(parameters)
114
94
  if @arguments.size != parameters.size
115
- raise "Wrong number of arguments (given: #{@arguments.size} expected: #{parameters.size})."
95
+ raise ArgumentError, "Wrong number of arguments (given: #{@arguments.size} expected: #{parameters.size})."
116
96
  end
117
97
  end
118
98
 
119
- def evaluate_method(method, scope)
99
+ def evaluate_method(scope)
120
100
  args = @arguments.map {|arg| arg.evaluate(scope)}
121
- method.call(*args)
101
+ result = STL.send(@identifier, *args)
102
+ return result
122
103
  end
123
104
 
124
105
  def evaluate_block(parameters, block, scope)
125
106
  new_scope = Scope.new(scope)
126
-
127
107
  parameters.zip(@arguments).each do |parameter, argument|
128
108
  new_scope.set_variable(parameter, '=', argument.evaluate(scope))
129
109
  end
@@ -133,18 +113,25 @@ class FunctioncallNode
133
113
  end
134
114
 
135
115
  def evaluate(scope)
136
- function_scope = scope.get_scope(@identifier, :functions)
137
- if !function_scope
138
- if self.respond_to?(@identifier, true)
139
- method_object = method(@identifier)
140
- parameters = method_object.parameters.map {|type, name| name}.compact
116
+
117
+ id_scope = scope.get_scope(@identifier)
118
+ if id_scope && !id_scope.is_function?(@identifier)
119
+ raise TypeError, "Identifier #{@identifier} is not defined as a function."
120
+ end
121
+ if @object
122
+ @arguments.unshift(@object)
123
+ end
124
+ if !id_scope
125
+ if STL.respond_to?(@identifier)
126
+ method = STL.method(@identifier)
127
+ parameters = method.parameters
141
128
  validate_arguments(parameters)
142
- evaluate_method(method_object, scope)
129
+ evaluate_method(scope)
143
130
  else
144
- raise "Function #{@identifier} not found."
131
+ raise NameError, "Function #{@identifier} not defined."
145
132
  end
146
133
  else
147
- function = function_scope.get_identifier(@identifier, :functions)
134
+ function = id_scope.get_identifier(@identifier)
148
135
  parameters = function[:parameters]
149
136
  block = function[:block]
150
137
  validate_arguments(parameters)
@@ -154,19 +141,19 @@ class FunctioncallNode
154
141
  end
155
142
 
156
143
  class ProgramNode
144
+ attr_reader :statements
157
145
  def initialize(statements)
158
146
  @statements = statements
159
147
  end
160
148
 
161
149
  def evaluate
162
150
  global_scope = Scope.new
163
- # global_scope.initialize_STL
164
-
151
+ result = nil
165
152
  @statements.each do |statement|
166
153
  result = statement.evaluate(global_scope)
167
154
  return result.value if statement.is_a?(ReturnNode)
168
155
  end
169
- return
156
+ return result
170
157
  end
171
158
  end
172
159
 
@@ -187,7 +174,6 @@ class ReturnNode
187
174
  end
188
175
  end
189
176
 
190
-
191
177
  class BinaryexpressionNode
192
178
  def initialize(lhs, op, rhs)
193
179
  @lhs = lhs
@@ -196,7 +182,11 @@ class BinaryexpressionNode
196
182
  end
197
183
 
198
184
  def evaluate(scope)
199
- return @lhs.evaluate(scope).send(@op, @rhs.evaluate(scope))
185
+ rhs_value = @rhs.evaluate(scope)
186
+ if @op == :fdiv && rhs_value == 0
187
+ raise ZeroDivisionError,"Division by 0 not possible."
188
+ end
189
+ return @lhs.evaluate(scope).send(@op, rhs_value)
200
190
  end
201
191
  end
202
192
 
@@ -254,38 +244,21 @@ end
254
244
  class WhileNode < ControlflowNode
255
245
  def evaluate(scope)
256
246
  while @expression.evaluate(scope)
257
- result = @block.evaluate(scope)
247
+ @block.evaluate(scope)
258
248
  end
259
249
  end
260
250
  end
261
251
 
262
-
263
- # Unsure about implementation, will check with project assistant
264
252
  class ArrayNode
265
253
  def initialize(elements = [])
266
254
  @elements = elements
267
- @data_container = {}
268
- end
269
-
270
- def evaluate(scope)
271
- @elements.each_with_index do |element, index|
272
- @data_container[index] = element.evaluate(scope)
273
- end
274
- end
275
- end
276
-
277
-
278
- # Not implemented yet
279
- class StringNode
280
- def initialize(chars = [])
281
- @chars = chars
282
255
  end
283
256
 
284
257
  def evaluate(scope)
258
+ return @elements.map {|element| element.evaluate(scope)}
285
259
  end
286
260
  end
287
261
 
288
-
289
262
  class PrimitiveNode
290
263
  attr_reader :value
291
264
  def initialize(value)
@@ -299,7 +272,11 @@ end
299
272
 
300
273
  class IdentifierNode < PrimitiveNode
301
274
  def evaluate(scope)
302
- return scope.get_identifier(@value, :variables)
275
+ if scope.is_function?(@value)
276
+ 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
+ end
303
280
  end
304
281
  end
305
282
 
@@ -3,17 +3,20 @@
3
3
  require_relative 'rdparse.rb'
4
4
  require_relative 'nodes.rb'
5
5
 
6
- class MyLang
6
+ class Pycplus
7
7
 
8
8
  def initialize
9
9
 
10
- @lang_parser = Parser.new("lang parser") do
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/) {|m| m}
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(:return, ';')
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, :container) {|a, b, c| AssignmentNode.new(a,b,c)}
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, :function_call) {|a, b, c| AssignmentNode.new(a,b,c)}
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('def', :identifier, '(', :parameters , ')', :block) {|_, a, _, b, _, c| FunctiondefNode.new(a,c,b)}
67
- match('def', :identifier, '(', ')', :block) {|_, a, _, _, b| FunctiondefNode.new(a,b)}
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
- # match(:identifier, '.', :identifier,'(', :arguments, ')') {|a, _, b, _| FunctioncallNode.new(a,b)}
88
- # match(:identifier, '.', :identifier,'(',')')
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(:function_call)
104
+ match(:array)
99
105
  end
100
106
 
101
- rule :return do
102
- match('return', :logical_expr) {|_, a| ReturnNode.new(a)}
103
- match('return', :function_call) {|_, a| ReturnNode.new(a)}
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('while', '(', :logical_expr, ')', :block) {|_,_,a,_,b| WhileNode.new(a,b)}
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('if', '(', :logical_expr, ')', :block) {|_,_,a,_,b| IfNode.new(a,b)}
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('/') {|a| a}
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(:container)
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') {|_| BoolNode.new(true)}
229
- match('false') {|_| BoolNode.new(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(/_?[a-zA-Z*]\w*/) {|a| IdentifierNode.new(a.to_sym)}
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 = @lang_parser.parse(file_data)
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 = @lang_parser.parse(str)
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
- return output.evaluate
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
- @lang_parser.logger.level = Logger::DEBUG
256
+ @pycplus_parser.logger.level = Logger::DEBUG
261
257
  else
262
- @lang_parser.logger.level = Logger::WARN
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 &block # In practise, calls match 1..N times
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.1.2
4
+ version: 0.3.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-01 00:00:00.000000000 Z
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 domain-specific
35
- language (DSL) developed as part of a school project at LiU (Linköping University).
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 domain-specific language (DSL)
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