confuscript 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|