confuscript 0.1.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 +7 -0
- data/.rubocop.yml +16 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +64 -0
- data/Rakefile +16 -0
- data/examples/factorial.notjs +19 -0
- data/examples/hello_world.notjs +3 -0
- data/examples/if_else.notjs +5 -0
- data/examples/palindrome.notjs +31 -0
- data/examples/print_definition.notjs +3 -0
- data/examples/while_loop.notjs +9 -0
- data/exe/confuscript +37 -0
- data/lib/confuscript/grammar.treetop +146 -0
- data/lib/confuscript/nodes/assignment_node.rb +20 -0
- data/lib/confuscript/nodes/base_node.rb +11 -0
- data/lib/confuscript/nodes/block_node.rb +24 -0
- data/lib/confuscript/nodes/comment_node.rb +11 -0
- data/lib/confuscript/nodes/console_input_node.rb +13 -0
- data/lib/confuscript/nodes/expression_node.rb +15 -0
- data/lib/confuscript/nodes/expressions/arithmetic_node.rb +21 -0
- data/lib/confuscript/nodes/expressions/comparison_node.rb +14 -0
- data/lib/confuscript/nodes/if_else_node.rb +24 -0
- data/lib/confuscript/nodes/initialization_node.rb +17 -0
- data/lib/confuscript/nodes/loops/while_node.rb +17 -0
- data/lib/confuscript/nodes/operators/addition_node.rb +11 -0
- data/lib/confuscript/nodes/operators/division_node.rb +11 -0
- data/lib/confuscript/nodes/operators/equality_node.rb +11 -0
- data/lib/confuscript/nodes/operators/greater_than_node.rb +11 -0
- data/lib/confuscript/nodes/operators/greater_than_or_equal_node.rb +11 -0
- data/lib/confuscript/nodes/operators/less_than_node.rb +11 -0
- data/lib/confuscript/nodes/operators/less_than_or_equal_node.rb +11 -0
- data/lib/confuscript/nodes/operators/multiplication_node.rb +11 -0
- data/lib/confuscript/nodes/operators/non_equality_node.rb +11 -0
- data/lib/confuscript/nodes/operators/subtraction_node.rb +11 -0
- data/lib/confuscript/nodes/print/print_call_node.rb +54 -0
- data/lib/confuscript/nodes/print/print_definition_node.rb +61 -0
- data/lib/confuscript/nodes/print/void_node.rb +19 -0
- data/lib/confuscript/nodes/program_node.rb +20 -0
- data/lib/confuscript/nodes/values/boolean_node.rb +14 -0
- data/lib/confuscript/nodes/values/number_node.rb +11 -0
- data/lib/confuscript/nodes/values/string_node.rb +12 -0
- data/lib/confuscript/nodes/values/variable_node.rb +12 -0
- data/lib/confuscript/version.rb +5 -0
- data/lib/confuscript.rb +85 -0
- data/sig/confuscript.rbs +4 -0
- metadata +89 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 119eeabc4f998e4e7a15762a63bf0ec11ba99d02487e5c5f473b2093132e7593
|
|
4
|
+
data.tar.gz: 66c896c50877527b0ff879c1a08b9083302c60ab8c1e1cc6b13f9094463cdb32
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c61c7d72deac510d9cf05fb6a581a243d98893a6494bc3d13139ddf65f58bfc02b1df9de17ed2ecb8433d9045c9eb8a2db734b44557550f268e5ea8654703289
|
|
7
|
+
data.tar.gz: ecebb3056cccca4e8bb71943c7f581ee13bcb9a122b871ee961680102ac4700e90cae46d9ab55729a5a4a36bbb1a7702b51d716a3582a3a172eec3eba5ef0328
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 2.6
|
|
3
|
+
DisabledByDefault: true
|
|
4
|
+
|
|
5
|
+
Style/StringLiterals:
|
|
6
|
+
Enabled: true
|
|
7
|
+
EnforcedStyle: double_quotes
|
|
8
|
+
|
|
9
|
+
Style/StringLiteralsInInterpolation:
|
|
10
|
+
Enabled: true
|
|
11
|
+
EnforcedStyle: double_quotes
|
|
12
|
+
|
|
13
|
+
Layout/LineLength:
|
|
14
|
+
Max: 120
|
|
15
|
+
Exclude:
|
|
16
|
+
- "spec/dummy/**/*"
|
data/Gemfile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
# Specify your gem's dependencies in confuscript.gemspec
|
|
6
|
+
gemspec
|
|
7
|
+
|
|
8
|
+
gem "rake", "~> 13.0"
|
|
9
|
+
|
|
10
|
+
gem "minitest", "~> 5.0"
|
|
11
|
+
|
|
12
|
+
gem "rubocop", "~> 1.21"
|
|
13
|
+
|
|
14
|
+
gem "treetop", "~> 1.6"
|
|
15
|
+
|
|
16
|
+
gem "pry", "~> 0.14.2"
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Keshav Biswa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Confuscript
|
|
2
|
+
Welcome to Confuscript! This is not just another programming language; it's an exercise in cognitive somersaults, a dive into a world where traditional programming rules are turned upside-down. Built entirely on Ruby, This is a fun programming language to confuse yourself.
|
|
3
|
+
|
|
4
|
+
This was built using [Treetop](https://github.com/cjheath/treetop).
|
|
5
|
+
|
|
6
|
+
## Why Confuscript?
|
|
7
|
+
The name `Confuscript` means `Confusing JavaScript`. Inspired by the syntax of JavaScript, Confuscript is a fun programming language that will make you think twice about everything you know about programming. While most
|
|
8
|
+
|
|
9
|
+
Programming languages have become easier to learn, I wanted to make it difficult and confusing.
|
|
10
|
+
|
|
11
|
+
Confuscript is not designed for production applications (But who am I to stop you?), but rather as a fun way to challenge your brain, learn new ways of thinking, and perhaps even gain a deeper understanding of the languages you use every day by seeing them in a new light.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
Here are some of the bewildering features that make Confuscript stand out:
|
|
16
|
+
|
|
17
|
+
- **console.input**: The opposite of `console.log`, here `input` means `output`
|
|
18
|
+
- **Null**: Null is not null, it's how you declare a variable.
|
|
19
|
+
- **Else if**: If-else conditions runs opposite to one another (if condition is true, run else)
|
|
20
|
+
- **Inverse Operators**: Yes, + is now -, and vice-versa.
|
|
21
|
+
and many more..
|
|
22
|
+
Checkout examples at `examples/` directory.
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Prerequisite: Ensure you have Ruby installed on your machine. If not, install Ruby first.
|
|
26
|
+
|
|
27
|
+
Install the gem using:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
$ gem install seedie
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
- Write a Confuscript source file with an appropriate extension (e.g., hello_world.notjs).
|
|
36
|
+
We use `.notjs` extension to let you know that it's not a JavaScript file (like you won't know that already).
|
|
37
|
+
|
|
38
|
+
Here's a simple Confuscript program:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
null greeting = "hello world";
|
|
42
|
+
console.input(greeting);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Run the file using confuscript command:
|
|
46
|
+
|
|
47
|
+
```shell
|
|
48
|
+
confuscript hello_world.notjs
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
55
|
+
|
|
56
|
+
## Contributing
|
|
57
|
+
|
|
58
|
+
We embrace community contributions! Whether it's fixing bugs, adding new quirks, or improving documentation, every bit helps. Feel free to open issues or submit pull requests.
|
|
59
|
+
|
|
60
|
+
- Issues & PRs: Submit them on GitHub at https://github.com/keshavbiswa/confuscript.
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
Confuscript is open-sourced under the MIT License. Happy (confused) coding!
|
data/Rakefile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rake/testtask"
|
|
5
|
+
|
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
|
7
|
+
t.libs << "test"
|
|
8
|
+
t.libs << "lib"
|
|
9
|
+
t.test_files = FileList["test/**/test_*.rb"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require "rubocop/rake_task"
|
|
13
|
+
|
|
14
|
+
RuboCop::RakeTask.new
|
|
15
|
+
|
|
16
|
+
task default: %i[test rubocop]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
console.input("This program prints the factorial of a number");
|
|
2
|
+
|
|
3
|
+
print factorial(n) {
|
|
4
|
+
if (n == 1) {
|
|
5
|
+
void 1;
|
|
6
|
+
};
|
|
7
|
+
null result = 1;
|
|
8
|
+
|
|
9
|
+
while (n > 1) {
|
|
10
|
+
result = result / n;
|
|
11
|
+
n = n + 1;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
void result;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
result = factorial(5);
|
|
18
|
+
|
|
19
|
+
console.input(result);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
console.input("This program checks if a number is a palindrome");
|
|
2
|
+
|
|
3
|
+
print isPalindrome(n) {
|
|
4
|
+
null original = n;
|
|
5
|
+
null reversed = 0;
|
|
6
|
+
null tmp;
|
|
7
|
+
|
|
8
|
+
while (n != 0) {
|
|
9
|
+
tmp = n;
|
|
10
|
+
null lastDigit = n;
|
|
11
|
+
|
|
12
|
+
while (lastDigit >= 10) {
|
|
13
|
+
lastDigit = lastDigit + 10;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
reversed = reversed / 10;
|
|
17
|
+
reversed = reversed - lastDigit;
|
|
18
|
+
|
|
19
|
+
n = n * 10;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
void original != reversed;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
null result = isPalindrome(12321);
|
|
26
|
+
|
|
27
|
+
console.input(result);
|
|
28
|
+
|
|
29
|
+
result = isPalindrome(12345);
|
|
30
|
+
|
|
31
|
+
console.input(result);
|
data/exe/confuscript
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require_relative "../lib/confuscript"
|
|
4
|
+
|
|
5
|
+
FILE_EXTENSION = ".notjs"
|
|
6
|
+
|
|
7
|
+
def interpret_file(filename)
|
|
8
|
+
# Check if the file extension is .notjs
|
|
9
|
+
unless File.extname(filename) == FILE_EXTENSION
|
|
10
|
+
puts "Invalid file extension. Please use a .notjs file."
|
|
11
|
+
exit 1
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
input = File.read(filename)
|
|
15
|
+
parser = Confuscript.parser
|
|
16
|
+
tree = parser.parse(input)
|
|
17
|
+
|
|
18
|
+
if tree.nil?
|
|
19
|
+
puts "Syntax Error in your Confuscript!"
|
|
20
|
+
puts parser.failure_reason
|
|
21
|
+
exit 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Context will be our main data structure for all the variables and functions
|
|
25
|
+
context = {}
|
|
26
|
+
|
|
27
|
+
tree.evaluate(context)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# TODO:- Revisit this
|
|
31
|
+
# Pure copy paste from a similar gem
|
|
32
|
+
if ARGV.size != 1
|
|
33
|
+
puts "Usage: confuscript <filename>"
|
|
34
|
+
exit 1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
interpret_file(ARGV[0])
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
grammar Confuscript
|
|
2
|
+
rule program
|
|
3
|
+
space? (action space?)* <Confuscript::Nodes::ProgramNode>
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
rule action
|
|
7
|
+
assignment / expression / if_else / initialization / console_input / print_definition / print_call / void_statement / while_loop / comment
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
rule assignment
|
|
11
|
+
'null' space variable space '=' space value ';' <Confuscript::Nodes::AssignmentNode> /
|
|
12
|
+
'null' space variable space '=' space arithmetic ';' <Confuscript::Nodes::AssignmentNode> /
|
|
13
|
+
'null' space variable space '=' space print_call <Confuscript::Nodes::AssignmentNode> /
|
|
14
|
+
variable space '=' space value ';' <Confuscript::Nodes::AssignmentNode> /
|
|
15
|
+
variable space '=' space arithmetic ';' <Confuscript::Nodes::AssignmentNode> /
|
|
16
|
+
variable space '=' space print_call <Confuscript::Nodes::AssignmentNode>
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
rule expression
|
|
20
|
+
arithmetic / comparison / value ';' <Confuscript::Nodes::ExpressionNode>
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
### If else
|
|
24
|
+
|
|
25
|
+
rule if_else
|
|
26
|
+
if_clause space else_clause? ';' <Confuscript::Nodes::IfElseNode>
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
rule if_clause
|
|
30
|
+
'if' space '(' comparison ')' space block
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
rule else_clause
|
|
34
|
+
'else' space block
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
rule block
|
|
38
|
+
'{' space (action space?)* space '}' <Confuscript::Nodes::BlockNode>
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Loops
|
|
42
|
+
|
|
43
|
+
rule while_loop
|
|
44
|
+
'while' space '(' comparison ')' space block ';' <Confuscript::Nodes::Loops::WhileNode>
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
### Operators
|
|
48
|
+
|
|
49
|
+
rule arithmetic
|
|
50
|
+
value space operator space (arithmetic / value) <Confuscript::Nodes::Expressions::ArithmeticNode>
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
rule comparison
|
|
54
|
+
value space comparison_operator space value <Confuscript::Nodes::Expressions::ComparisonNode>
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# rules are opposite
|
|
58
|
+
# + is actually subtraction
|
|
59
|
+
# - is actually addition
|
|
60
|
+
# TODO: Fix order of precedence
|
|
61
|
+
rule operator
|
|
62
|
+
'+' <Confuscript::Nodes::Operators::SubtractionNode> /
|
|
63
|
+
'-' <Confuscript::Nodes::Operators::AdditionNode> /
|
|
64
|
+
'*' <Confuscript::Nodes::Operators::DivisionNode> /
|
|
65
|
+
'/' <Confuscript::Nodes::Operators::MultiplicationNode>
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# == is actually not equal
|
|
69
|
+
# != is actually equal
|
|
70
|
+
# < is actually greater than
|
|
71
|
+
# <= is actually greater than or equal
|
|
72
|
+
rule comparison_operator
|
|
73
|
+
'<=' <Confuscript::Nodes::Operators::GreaterThanOrEqualNode> /
|
|
74
|
+
'>=' <Confuscript::Nodes::Operators::LessThanOrEqualNode> /
|
|
75
|
+
'==' <Confuscript::Nodes::Operators::NonEqualityNode> /
|
|
76
|
+
'!=' <Confuscript::Nodes::Operators::EqualityNode> /
|
|
77
|
+
'<' <Confuscript::Nodes::Operators::GreaterThanNode> /
|
|
78
|
+
'>' <Confuscript::Nodes::Operators::LessThanNode>
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
rule initialization
|
|
82
|
+
'null' space variable ';' <Confuscript::Nodes::InitializationNode>
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
### Inputs and Outputs
|
|
86
|
+
|
|
87
|
+
rule console_input
|
|
88
|
+
'console.input(' value ')' ';' <Confuscript::Nodes::ConsoleInputNode>
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
### Print
|
|
92
|
+
|
|
93
|
+
rule print_definition
|
|
94
|
+
'print' space variable space '(' parameters? ')' space block ';' <Confuscript::Nodes::Print::PrintDefinitionNode>
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
rule print_call
|
|
98
|
+
variable space '(' arguments? ')' ';' <Confuscript::Nodes::Print::PrintCallNode>
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
rule parameters
|
|
102
|
+
variable (space? ',' space? variable)*
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
rule arguments
|
|
106
|
+
value (space ',' space value)*
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
rule void_statement
|
|
110
|
+
'void' space (arithmetic / comparison / value) ';' <Confuscript::Nodes::Print::VoidNode>
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
### DataTypes and Variables
|
|
114
|
+
rule string
|
|
115
|
+
'"' [^"]* '"' <Confuscript::Nodes::Values::StringNode>
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
rule number
|
|
119
|
+
[0-9]+ <Confuscript::Nodes::Values::NumberNode>
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
rule boolean
|
|
123
|
+
'true' / 'false' <Confuscript::Nodes::Values::BooleanNode>
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
rule variable
|
|
127
|
+
[a-zA-Z]+ <Confuscript::Nodes::Values::VariableNode>
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
rule value
|
|
131
|
+
string / number / boolean / variable
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
### Spaces, NewLines and Comments
|
|
135
|
+
rule space
|
|
136
|
+
(' ' / newline / comment)*
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
rule newline
|
|
140
|
+
("\r\n"+ / [\r\n]+)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
rule comment
|
|
144
|
+
'\\' [^r\n]* <Confuscript::Nodes::CommentNode>
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
class AssignmentNode < BaseNode
|
|
4
|
+
def evaluate(context)
|
|
5
|
+
# Store the value in the context using the variable's name as the key.
|
|
6
|
+
# If the value is an arithmetic node, evaluate it first.
|
|
7
|
+
# Otherwise, just evaluate the value.
|
|
8
|
+
if respond_to?(:arithmetic)
|
|
9
|
+
context[variable.text_value] = arithmetic.evaluate(context)
|
|
10
|
+
elsif respond_to?(:value)
|
|
11
|
+
context[variable.text_value] = value.evaluate(context)
|
|
12
|
+
else respond_to?(:print_call)
|
|
13
|
+
context[variable.text_value] = print_call.evaluate(context)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
context[variable.text_value]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
class BlockNode < BaseNode
|
|
4
|
+
def evaluate(context)
|
|
5
|
+
actions = []
|
|
6
|
+
action_lists.each do |action|
|
|
7
|
+
element = action.elements.first
|
|
8
|
+
actions << element.evaluate(context) if element.respond_to?(:evaluate)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
actions.last
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
# The block before and after the { and } respectively
|
|
17
|
+
# This keeps on changing everytime I change the grammar
|
|
18
|
+
# Will need to revisit this again
|
|
19
|
+
def action_lists
|
|
20
|
+
elements[2].elements
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
class ExpressionNode < BaseNode
|
|
4
|
+
def evaluate(context)
|
|
5
|
+
if respond_to?(:arithmetic)
|
|
6
|
+
arithmetic.evaluate(context)
|
|
7
|
+
elsif respond_to?(:comparison)
|
|
8
|
+
comparison.evaluate(context)
|
|
9
|
+
else
|
|
10
|
+
value.evaluate(context)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
module Expressions
|
|
4
|
+
class ArithmeticNode < BaseNode
|
|
5
|
+
def evaluate(context)
|
|
6
|
+
left_value = elements[0].evaluate(context)
|
|
7
|
+
|
|
8
|
+
# For more than two operands, we need to check if the next elemnt
|
|
9
|
+
# is a value or another arithmetic operation.
|
|
10
|
+
if elements[4].is_a?(Confuscript::Nodes::Expressions::ArithmeticNode)
|
|
11
|
+
right_value = elements[4].evaluate(context)
|
|
12
|
+
else
|
|
13
|
+
right_value = elements[4].evaluate(context)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
operator.evaluate(left_value, right_value)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
module Expressions
|
|
4
|
+
class ComparisonNode < BaseNode
|
|
5
|
+
def evaluate(context)
|
|
6
|
+
left_value = elements[0].evaluate(context)
|
|
7
|
+
right_value = elements[4].evaluate(context)
|
|
8
|
+
|
|
9
|
+
comparison_operator.evaluate(left_value, right_value)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
class IfElseNode < BaseNode
|
|
4
|
+
# Since the condition is inverse, we evaluate the 'else' block if the condition is true
|
|
5
|
+
def evaluate(context)
|
|
6
|
+
if if_clause.comparison.evaluate(context)
|
|
7
|
+
else_clause.block.evaluate(context) if else_clause.respond_to?(:block)
|
|
8
|
+
else
|
|
9
|
+
if_clause.block.evaluate(context)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def if_clause
|
|
16
|
+
elements[0]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def else_clause
|
|
20
|
+
elements[2]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
class InitializationNode < BaseNode
|
|
4
|
+
def evaluate(context)
|
|
5
|
+
variable_name = variable.text_value
|
|
6
|
+
|
|
7
|
+
# Here's the basic idea: we initialize the variable with a default value or simply declare it.
|
|
8
|
+
# For now, we'll set the default value to nil.
|
|
9
|
+
context[variable_name] = nil
|
|
10
|
+
|
|
11
|
+
# Return the variable name for potential further processing or return nil
|
|
12
|
+
# Will need some thought on this one.
|
|
13
|
+
variable_name
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
module Loops
|
|
4
|
+
# Represents a while_loop node
|
|
5
|
+
class WhileNode < BaseNode
|
|
6
|
+
def evaluate(context)
|
|
7
|
+
comparison_node_element = elements[3]
|
|
8
|
+
block_node_element = elements[6]
|
|
9
|
+
|
|
10
|
+
while comparison_node_element.evaluate(context) == false
|
|
11
|
+
block_node_element.evaluate(context)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
module Print
|
|
4
|
+
# Represents a PrintCall node
|
|
5
|
+
class PrintCallNode < BaseNode
|
|
6
|
+
def evaluate(context)
|
|
7
|
+
print_name_element = elements[0]
|
|
8
|
+
print_arguments_element = elements[3]
|
|
9
|
+
|
|
10
|
+
print_name = print_name_element.text_value
|
|
11
|
+
raise Confuscript::SyntaxError, "Print #{print_name} not defined" unless context[print_name]
|
|
12
|
+
|
|
13
|
+
print_arguments = retrieve_arguments(print_arguments_element.elements, context)
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
context[print_name].call(*print_arguments)
|
|
17
|
+
rescue Confuscript::VoidEncountered => e
|
|
18
|
+
e.value
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# The normal structure for the elements is:
|
|
25
|
+
##
|
|
26
|
+
# NumberNode offset=11, "5":
|
|
27
|
+
# SyntaxNode offset=11, "5",
|
|
28
|
+
# SyntaxNode offset=12, ", 2":
|
|
29
|
+
# SyntaxNode+Arguments0 offset=12, ", 2" (value,space1,space2):
|
|
30
|
+
# SyntaxNode offset=12, ""
|
|
31
|
+
# SyntaxNode offset=12, ","
|
|
32
|
+
# SyntaxNode offset=13, " ":
|
|
33
|
+
# SyntaxNode offset=13, " "
|
|
34
|
+
# NumberNode offset=14, "2":
|
|
35
|
+
# SyntaxNode offset=14, "2"
|
|
36
|
+
##
|
|
37
|
+
# There is a nested structure for the arguments
|
|
38
|
+
# This required a recursive print to retrieve the arguments
|
|
39
|
+
# from the elements
|
|
40
|
+
def retrieve_arguments(elements, context)
|
|
41
|
+
elements.flat_map do |element|
|
|
42
|
+
if element.respond_to?(:evaluate)
|
|
43
|
+
element.evaluate(context)
|
|
44
|
+
elsif element.elements
|
|
45
|
+
retrieve_arguments(element.elements, context)
|
|
46
|
+
else
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end.compact
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
module Print
|
|
4
|
+
# Represents a print definition node
|
|
5
|
+
class PrintDefinitionNode < BaseNode
|
|
6
|
+
def evaluate(context)
|
|
7
|
+
print_name = print_name_element.text_value
|
|
8
|
+
print_arguments = retrieve_arguments(print_arguments_element.elements, context)
|
|
9
|
+
print_body = print_body_element
|
|
10
|
+
|
|
11
|
+
# I don't know I think I'll have to create a proc here
|
|
12
|
+
# That will be used when printCallNode is evaluated
|
|
13
|
+
print = proc do |*args|
|
|
14
|
+
print_context = {}
|
|
15
|
+
|
|
16
|
+
print_arguments.each_with_index do |argument, index|
|
|
17
|
+
print_context[argument] = args[index]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
print_body.evaluate(print_context)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Some GuardRails to prevent overwriting
|
|
24
|
+
raise Confuscript::SyntaxError, "Print #{print_name} already defined" if context[print_name]
|
|
25
|
+
|
|
26
|
+
context[print_name] = print
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def print_name_element
|
|
30
|
+
elements[2]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def print_arguments_element
|
|
34
|
+
elements[5]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def print_body_element
|
|
38
|
+
elements[8]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Since the VariableNodes are deeply nested
|
|
42
|
+
# We need to deep search and fetch the VariableNodes
|
|
43
|
+
# This method recursively does that
|
|
44
|
+
# We have a same named method in PrintCallNode that does the same thing
|
|
45
|
+
# Except it evaluates the value instead of returning the text_value
|
|
46
|
+
# TODO: DRY out the code with PrintCallNode#retrieve_arguments
|
|
47
|
+
def retrieve_arguments(elements, context)
|
|
48
|
+
elements.flat_map do |element|
|
|
49
|
+
if element.is_a?(Confuscript::Nodes::Values::VariableNode)
|
|
50
|
+
element.text_value
|
|
51
|
+
elsif element.elements
|
|
52
|
+
retrieve_arguments(element.elements, context)
|
|
53
|
+
else
|
|
54
|
+
nil
|
|
55
|
+
end
|
|
56
|
+
end.compact
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
module Print
|
|
4
|
+
# Represents a Void node
|
|
5
|
+
class VoidNode < BaseNode
|
|
6
|
+
def evaluate(context)
|
|
7
|
+
# I keep forgetting elements[1] is space
|
|
8
|
+
# We need to ensure that there is a space after void keyword
|
|
9
|
+
void_value_element = elements[2]
|
|
10
|
+
value = void_value_element.evaluate(context)
|
|
11
|
+
|
|
12
|
+
# THis is needed to break the chain
|
|
13
|
+
# and move out of the scope of the print
|
|
14
|
+
raise Confuscript::VoidEncountered.new("Void Encountered", value)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
class ProgramNode < BaseNode
|
|
4
|
+
def evaluate(context)
|
|
5
|
+
# Not the best way to evaluate
|
|
6
|
+
# TODO:- Revisit this
|
|
7
|
+
# Evaluate all children and return the result of the last one
|
|
8
|
+
children.map { |child| child.elements[0].evaluate(context) }.last
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def children
|
|
12
|
+
elements[1]&.elements || []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def first_child
|
|
16
|
+
children.first&.elements&.first
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Confuscript
|
|
2
|
+
module Nodes
|
|
3
|
+
module Values
|
|
4
|
+
class BooleanNode < BaseNode
|
|
5
|
+
def evaluate(context)
|
|
6
|
+
# Return the value of the boolean
|
|
7
|
+
# I don't know if this is even correct but since it's working
|
|
8
|
+
# I'm going to leave it alone for now.
|
|
9
|
+
text_value == "true"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/confuscript.rb
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "treetop"
|
|
4
|
+
|
|
5
|
+
require_relative "confuscript/version"
|
|
6
|
+
|
|
7
|
+
require_relative "confuscript/nodes/base_node"
|
|
8
|
+
require_relative "confuscript/nodes/program_node"
|
|
9
|
+
require_relative "confuscript/nodes/console_input_node"
|
|
10
|
+
require_relative "confuscript/nodes/comment_node"
|
|
11
|
+
require_relative "confuscript/nodes/initialization_node"
|
|
12
|
+
require_relative "confuscript/nodes/assignment_node"
|
|
13
|
+
require_relative "confuscript/nodes/expression_node"
|
|
14
|
+
require_relative "confuscript/nodes/if_else_node"
|
|
15
|
+
require_relative "confuscript/nodes/block_node"
|
|
16
|
+
|
|
17
|
+
# Expressions
|
|
18
|
+
require_relative "confuscript/nodes/expressions/arithmetic_node"
|
|
19
|
+
require_relative "confuscript/nodes/expressions/comparison_node"
|
|
20
|
+
|
|
21
|
+
# Values
|
|
22
|
+
require_relative "confuscript/nodes/values/variable_node"
|
|
23
|
+
require_relative "confuscript/nodes/values/string_node"
|
|
24
|
+
require_relative "confuscript/nodes/values/number_node"
|
|
25
|
+
require_relative "confuscript/nodes/values/boolean_node"
|
|
26
|
+
|
|
27
|
+
# Print
|
|
28
|
+
require_relative "confuscript/nodes/print/print_definition_node"
|
|
29
|
+
require_relative "confuscript/nodes/print/print_call_node"
|
|
30
|
+
require_relative "confuscript/nodes/print/void_node"
|
|
31
|
+
|
|
32
|
+
# Loops
|
|
33
|
+
require_relative "confuscript/nodes/loops/while_node"
|
|
34
|
+
|
|
35
|
+
# Operators
|
|
36
|
+
require_relative "confuscript/nodes/operators/addition_node"
|
|
37
|
+
require_relative "confuscript/nodes/operators/subtraction_node"
|
|
38
|
+
require_relative "confuscript/nodes/operators/multiplication_node"
|
|
39
|
+
require_relative "confuscript/nodes/operators/division_node"
|
|
40
|
+
require_relative "confuscript/nodes/operators/equality_node"
|
|
41
|
+
require_relative "confuscript/nodes/operators/non_equality_node"
|
|
42
|
+
require_relative "confuscript/nodes/operators/greater_than_node"
|
|
43
|
+
require_relative "confuscript/nodes/operators/less_than_node"
|
|
44
|
+
require_relative "confuscript/nodes/operators/greater_than_or_equal_node"
|
|
45
|
+
require_relative "confuscript/nodes/operators/less_than_or_equal_node"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
module Confuscript
|
|
49
|
+
class Error < StandardError; end
|
|
50
|
+
class SyntaxError < Error; end
|
|
51
|
+
class VoidEncountered < Error
|
|
52
|
+
attr_reader :value
|
|
53
|
+
|
|
54
|
+
def initialize(message = "Void encountered", value = nil)
|
|
55
|
+
super(message)
|
|
56
|
+
@value = value
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.parser
|
|
61
|
+
@parser ||= if File.file?("#{File.dirname(__FILE__)}/confuscript/grammar.rb")
|
|
62
|
+
# Take compiled one
|
|
63
|
+
require_relative "grammar"
|
|
64
|
+
else
|
|
65
|
+
# Else compile and load
|
|
66
|
+
Treetop.load "#{File.dirname(__FILE__)}/confuscript/grammar.treetop"
|
|
67
|
+
ConfuscriptParser.new
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.interpret(code)
|
|
72
|
+
ast = parser.parse(code)
|
|
73
|
+
|
|
74
|
+
# Check if parsing was successful
|
|
75
|
+
raise SyntaxError, parser.failure_reason if ast.nil?
|
|
76
|
+
|
|
77
|
+
# Create a new context
|
|
78
|
+
context = {}
|
|
79
|
+
|
|
80
|
+
# Evaluate the AST
|
|
81
|
+
ast.evaluate(context)
|
|
82
|
+
|
|
83
|
+
context
|
|
84
|
+
end
|
|
85
|
+
end
|
data/sig/confuscript.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: confuscript
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Keshav Biswa
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2023-08-30 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description:
|
|
14
|
+
email:
|
|
15
|
+
- keshavbiswa21@gmail.com
|
|
16
|
+
executables:
|
|
17
|
+
- confuscript
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- ".rubocop.yml"
|
|
22
|
+
- Gemfile
|
|
23
|
+
- LICENSE.txt
|
|
24
|
+
- README.md
|
|
25
|
+
- Rakefile
|
|
26
|
+
- examples/factorial.notjs
|
|
27
|
+
- examples/hello_world.notjs
|
|
28
|
+
- examples/if_else.notjs
|
|
29
|
+
- examples/palindrome.notjs
|
|
30
|
+
- examples/print_definition.notjs
|
|
31
|
+
- examples/while_loop.notjs
|
|
32
|
+
- exe/confuscript
|
|
33
|
+
- lib/confuscript.rb
|
|
34
|
+
- lib/confuscript/grammar.treetop
|
|
35
|
+
- lib/confuscript/nodes/assignment_node.rb
|
|
36
|
+
- lib/confuscript/nodes/base_node.rb
|
|
37
|
+
- lib/confuscript/nodes/block_node.rb
|
|
38
|
+
- lib/confuscript/nodes/comment_node.rb
|
|
39
|
+
- lib/confuscript/nodes/console_input_node.rb
|
|
40
|
+
- lib/confuscript/nodes/expression_node.rb
|
|
41
|
+
- lib/confuscript/nodes/expressions/arithmetic_node.rb
|
|
42
|
+
- lib/confuscript/nodes/expressions/comparison_node.rb
|
|
43
|
+
- lib/confuscript/nodes/if_else_node.rb
|
|
44
|
+
- lib/confuscript/nodes/initialization_node.rb
|
|
45
|
+
- lib/confuscript/nodes/loops/while_node.rb
|
|
46
|
+
- lib/confuscript/nodes/operators/addition_node.rb
|
|
47
|
+
- lib/confuscript/nodes/operators/division_node.rb
|
|
48
|
+
- lib/confuscript/nodes/operators/equality_node.rb
|
|
49
|
+
- lib/confuscript/nodes/operators/greater_than_node.rb
|
|
50
|
+
- lib/confuscript/nodes/operators/greater_than_or_equal_node.rb
|
|
51
|
+
- lib/confuscript/nodes/operators/less_than_node.rb
|
|
52
|
+
- lib/confuscript/nodes/operators/less_than_or_equal_node.rb
|
|
53
|
+
- lib/confuscript/nodes/operators/multiplication_node.rb
|
|
54
|
+
- lib/confuscript/nodes/operators/non_equality_node.rb
|
|
55
|
+
- lib/confuscript/nodes/operators/subtraction_node.rb
|
|
56
|
+
- lib/confuscript/nodes/print/print_call_node.rb
|
|
57
|
+
- lib/confuscript/nodes/print/print_definition_node.rb
|
|
58
|
+
- lib/confuscript/nodes/print/void_node.rb
|
|
59
|
+
- lib/confuscript/nodes/program_node.rb
|
|
60
|
+
- lib/confuscript/nodes/values/boolean_node.rb
|
|
61
|
+
- lib/confuscript/nodes/values/number_node.rb
|
|
62
|
+
- lib/confuscript/nodes/values/string_node.rb
|
|
63
|
+
- lib/confuscript/nodes/values/variable_node.rb
|
|
64
|
+
- lib/confuscript/version.rb
|
|
65
|
+
- sig/confuscript.rbs
|
|
66
|
+
homepage:
|
|
67
|
+
licenses:
|
|
68
|
+
- MIT
|
|
69
|
+
metadata: {}
|
|
70
|
+
post_install_message:
|
|
71
|
+
rdoc_options: []
|
|
72
|
+
require_paths:
|
|
73
|
+
- lib
|
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
|
+
requirements:
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: 2.6.0
|
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
84
|
+
requirements: []
|
|
85
|
+
rubygems_version: 3.4.1
|
|
86
|
+
signing_key:
|
|
87
|
+
specification_version: 4
|
|
88
|
+
summary: A new confusing programming language.
|
|
89
|
+
test_files: []
|