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 +4 -4
- data/README.md +1 -91
- data/bin/pycplus +51 -12
- data/lib/STL.rb +56 -8
- data/lib/nodes.rb +67 -90
- 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: 2fde7222ccd4137c44659d5df46a12bc2cb57facbb373c129d54af19596aeb93
|
4
|
+
data.tar.gz: b86597ee3514507775c3a2be65e9fd8ed1f2778be48d276b45b99898e0ba8223
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
4
|
+
require 'optparse'
|
5
|
+
require_relative '../lib/pcpparse'
|
5
6
|
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
15
|
-
|
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.
|
19
|
-
@lang_parser.parse_file(arg)
|
53
|
+
@lang_parser.parse_file(filename)
|
20
54
|
else
|
21
|
-
puts "File is not a .pcp file
|
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 #{
|
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
|
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
@@ -3,80 +3,56 @@
|
|
3
3
|
require_relative 'STL.rb'
|
4
4
|
|
5
5
|
class Scope
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :identifiers,:parent_scope
|
7
7
|
def initialize(parent_scope = nil)
|
8
|
-
@
|
9
|
-
@functions = {}
|
8
|
+
@identifiers = {}
|
10
9
|
@parent_scope = parent_scope
|
11
10
|
end
|
12
11
|
|
13
12
|
def set_variable(name, op, expression)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
@
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
33
|
-
scope = get_scope(name
|
28
|
+
def get_identifier(name)
|
29
|
+
scope = get_scope(name)
|
34
30
|
if scope
|
35
|
-
return scope.
|
31
|
+
return scope.identifiers[name]
|
36
32
|
else
|
37
|
-
raise "
|
33
|
+
raise NameError,"Identifier #{name} not defined."
|
38
34
|
end
|
39
35
|
end
|
40
36
|
|
41
|
-
def get_scope(name
|
42
|
-
if
|
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
|
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
|
-
@
|
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
|
-
|
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(
|
99
|
+
def evaluate_method(scope)
|
120
100
|
args = @arguments.map {|arg| arg.evaluate(scope)}
|
121
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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(
|
129
|
+
evaluate_method(scope)
|
143
130
|
else
|
144
|
-
raise "Function #{@identifier} not
|
131
|
+
raise NameError, "Function #{@identifier} not defined."
|
145
132
|
end
|
146
133
|
else
|
147
|
-
function =
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
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.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-
|
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
|