nil 1.0.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.
- data/.gitignore +22 -0
- data/.rvmrc +1 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +96 -0
- data/Rakefile +13 -0
- data/lib/nil.rb +8 -0
- data/lib/nil/interpreter.rb +63 -0
- data/lib/nil/macro.rb +68 -0
- data/lib/nil/version.rb +3 -0
- data/nil.gemspec +17 -0
- data/test/another.nil +1 -0
- data/test/nil_test.rb +103 -0
- data/test/test.nil +1 -0
- metadata +62 -0
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
.*.sw[a-z]
|
19
|
+
*.un~
|
20
|
+
Session.vim
|
21
|
+
|
22
|
+
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm gemset use nil
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Wuffers Lightwolf
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# nil
|
2
|
+
|
3
|
+
nil is a brain**** language macro language and interpreter. ## Installation
|
4
|
+
|
5
|
+
## Macro language
|
6
|
+
|
7
|
+
nil supports a few macros that get expanded to vanilla brain**** code.
|
8
|
+
|
9
|
+
### Numbered commands
|
10
|
+
|
11
|
+
The commands `>`, `<`, `+`, and `-` can be given a number to repeat that
|
12
|
+
command. For example:
|
13
|
+
|
14
|
+
`+5` will be expanded into `++++++`
|
15
|
+
|
16
|
+
### Macros
|
17
|
+
|
18
|
+
Macros can be defined and referenced. However, since they are only expanded,
|
19
|
+
they cannot recurse.
|
20
|
+
|
21
|
+
Macros are defined in the form of `:sub_name{code}` and are called in the form
|
22
|
+
of `sub_name:`
|
23
|
+
|
24
|
+
Example:
|
25
|
+
|
26
|
+
:add_two{++}add_two:
|
27
|
+
|
28
|
+
will be expanded to:
|
29
|
+
|
30
|
+
++
|
31
|
+
|
32
|
+
### Including other files
|
33
|
+
|
34
|
+
Other files can be included in the current one. Files are included in the format
|
35
|
+
of: `.include(file.nil)`
|
36
|
+
|
37
|
+
### Comments
|
38
|
+
|
39
|
+
Unlike regular brain**** where everything is treated as a comment, only lines
|
40
|
+
looking like such are comments:
|
41
|
+
|
42
|
+
;this is a comment;
|
43
|
+
|
44
|
+
Comments *must* include both semicolons.
|
45
|
+
|
46
|
+
## Interpreter
|
47
|
+
|
48
|
+
nil is also a brain**** interpreter. It does the interpreting in its own special
|
49
|
+
way, though:
|
50
|
+
|
51
|
+
1. It uses the MacroCompiler to compile the code
|
52
|
+
2. It translates the brain**** to Ruby code
|
53
|
+
3. It then runs the Ruby code it generated.
|
54
|
+
|
55
|
+
The interpreter tries to be smart about the way it translates the code, for
|
56
|
+
instance:
|
57
|
+
|
58
|
+
`+++++` will be translated to:
|
59
|
+
|
60
|
+
sp += 5
|
61
|
+
|
62
|
+
instead of
|
63
|
+
|
64
|
+
sp += 1
|
65
|
+
sp += 1
|
66
|
+
sp += 1
|
67
|
+
sp += 1
|
68
|
+
sp += 1
|
69
|
+
|
70
|
+
## Installing
|
71
|
+
|
72
|
+
Add this line to your application's Gemfile:
|
73
|
+
|
74
|
+
gem 'nil'
|
75
|
+
|
76
|
+
And then execute:
|
77
|
+
|
78
|
+
$ bundle
|
79
|
+
|
80
|
+
Or install it yourself as:
|
81
|
+
|
82
|
+
$ gem install nil
|
83
|
+
|
84
|
+
## Usage
|
85
|
+
|
86
|
+
```
|
87
|
+
$ nil file.nil
|
88
|
+
```
|
89
|
+
|
90
|
+
## Contributing
|
91
|
+
|
92
|
+
1. Fork it
|
93
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
94
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
95
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
96
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/nil.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Nil
|
2
|
+
class Interpreter
|
3
|
+
attr_reader :compiled_ruby
|
4
|
+
|
5
|
+
def run
|
6
|
+
eval(@compiled_ruby)
|
7
|
+
end
|
8
|
+
|
9
|
+
def handle_non_multi_command(char)
|
10
|
+
case char
|
11
|
+
when '['
|
12
|
+
"while stack[sp] != 0\n"
|
13
|
+
when ']'
|
14
|
+
"end\n"
|
15
|
+
when '.'
|
16
|
+
"print stack[sp]\n"
|
17
|
+
when ','
|
18
|
+
"stack[sp] = gets[0]\n"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def handle_multi_command(command)
|
23
|
+
count = command.length
|
24
|
+
case command[0]
|
25
|
+
when '+'
|
26
|
+
"stack[sp] += #{count}\n"
|
27
|
+
when '-'
|
28
|
+
"stack[sp] -= #{count}\n"
|
29
|
+
when '>'
|
30
|
+
"sp += #{count}\n"
|
31
|
+
when '<'
|
32
|
+
"sp -= #{count}\n"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def compile(code)
|
37
|
+
@compiled_ruby = <<RB
|
38
|
+
sp = 0
|
39
|
+
stack = [0] * 10_000
|
40
|
+
RB
|
41
|
+
|
42
|
+
code.each_char do |c|
|
43
|
+
if !COMMANDS.include? c
|
44
|
+
raise SyntaxError, "Invalid characters in code."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
while code != ""
|
49
|
+
multi_command = code.match(/([><\-\+])(\1*)/)
|
50
|
+
|
51
|
+
if multi_command == nil
|
52
|
+
ruby = handle_non_multi_command(code[0])
|
53
|
+
@compiled_ruby << ruby
|
54
|
+
code[0] = ""
|
55
|
+
else
|
56
|
+
@compiled_ruby << handle_multi_command(multi_command.to_s)
|
57
|
+
code.sub!(multi_command.to_s, "")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
@compiled_ruby
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/nil/macro.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module Nil
|
2
|
+
class MacroParser
|
3
|
+
attr_accessor :original_code, :compiled_code
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
# This method intentionally left blank.
|
7
|
+
end
|
8
|
+
|
9
|
+
def compile_subroutines(code)
|
10
|
+
# TODO: Throw an error when trying to define a subroutine that calls
|
11
|
+
# itself.
|
12
|
+
subroutines = {}
|
13
|
+
sub_def_regex = Regexp.new ':(?<sub-name>[a-zA-Z0-9_]+)\{(?<sub-code>[><\-\+.,A-Za-z0-9:]+)\}'
|
14
|
+
sub_call_regex = Regexp.new '([a-zA-Z0-9_]+):'
|
15
|
+
code.scan(sub_def_regex).map do |sub|
|
16
|
+
subroutines[sub[0]] = sub[1]
|
17
|
+
end
|
18
|
+
|
19
|
+
subroutines.each do |s, c|
|
20
|
+
if c =~ sub_call_regex
|
21
|
+
raise SyntaxError, "Error: Recursive subroutine."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
code.gsub!(sub_call_regex) do |m|
|
26
|
+
subroutines[$1]
|
27
|
+
end
|
28
|
+
|
29
|
+
code.gsub(sub_def_regex, '')
|
30
|
+
end
|
31
|
+
|
32
|
+
def expand_included_files(code)
|
33
|
+
c = code
|
34
|
+
include_regex = Regexp.new '\.include\(([^\)]+)\)'
|
35
|
+
included_code = ""
|
36
|
+
files = c.scan(include_regex).flatten
|
37
|
+
files.each do |f|
|
38
|
+
included_code << File.open(f, 'r').read.strip
|
39
|
+
end
|
40
|
+
c.gsub!(include_regex, '')
|
41
|
+
c = included_code + c
|
42
|
+
c
|
43
|
+
end
|
44
|
+
|
45
|
+
def strip_comments(code)
|
46
|
+
code.gsub(/;(.+);/, '')
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse(code)
|
50
|
+
@original_code = code
|
51
|
+
|
52
|
+
compiled_code = @original_code
|
53
|
+
|
54
|
+
compiled_code.gsub!(/\s/, '')
|
55
|
+
|
56
|
+
compiled_code = expand_included_files(compiled_code)
|
57
|
+
|
58
|
+
compiled_code = strip_comments(compiled_code)
|
59
|
+
|
60
|
+
compiled_code.gsub!(/([\+-<>])(\d+)/) do |m| # Parse the numerical commands.
|
61
|
+
$1 * $2.to_i
|
62
|
+
end
|
63
|
+
|
64
|
+
compiled_code = compile_subroutines(compiled_code)
|
65
|
+
compiled_code
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/nil/version.rb
ADDED
data/nil.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/nil/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Wuffers Lightwolf"]
|
6
|
+
gem.email = ["wuffers.lightwolf@me.com"]
|
7
|
+
gem.description = %q{A brain**** macro language and interpreter}
|
8
|
+
gem.summary = %q{A macro language/interpreter for brain****}
|
9
|
+
gem.homepage = "http://github.com/w-x-l/nil"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "nil"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Nil::VERSION
|
17
|
+
end
|
data/test/another.nil
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
>>
|
data/test/nil_test.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'nil'
|
2
|
+
require 'riot'
|
3
|
+
|
4
|
+
Riot.pretty_dots
|
5
|
+
|
6
|
+
BOILERPLATE = <<RB
|
7
|
+
sp = 0
|
8
|
+
stack = Array.new 10_000
|
9
|
+
RB
|
10
|
+
|
11
|
+
context "Interpreter" do
|
12
|
+
setup { Nil::Interpreter.new }
|
13
|
+
asserts("invalid characters in code cause failure") { topic.compile "a++" }.raises SyntaxError
|
14
|
+
asserts("creates boilerplate code") { topic.compiled_ruby == BOILERPLATE }
|
15
|
+
asserts("handles .") { topic.compile('.') == (BOILERPLATE + "print stack[sp]\n") }
|
16
|
+
asserts("handles ,") { topic.compile(',') == (BOILERPLATE + "stack[sp] = gets[0]\n") }
|
17
|
+
asserts("handles >") { topic.compile('>') == (BOILERPLATE + "sp += 1\n") }
|
18
|
+
asserts("handles <") { topic.compile('<') == (BOILERPLATE + "sp -= 1\n") }
|
19
|
+
asserts("handles +") { topic.compile('+') == (BOILERPLATE + "stack[sp] += 1\n") }
|
20
|
+
asserts("handles -") { topic.compile('-') == (BOILERPLATE + "stack[sp] -= 1\n") }
|
21
|
+
asserts("handles [") { topic.compile('[') == (BOILERPLATE + "while stack[sp] != 0\n" ) }
|
22
|
+
asserts("handles ]") { topic.compile(']') == (BOILERPLATE + "end\n" ) }
|
23
|
+
asserts("handles multiple operators smartly") do
|
24
|
+
code = topic.compile "++++---->>>><<<<"
|
25
|
+
code == BOILERPLATE + <<CODE
|
26
|
+
stack[sp] += 4
|
27
|
+
stack[sp] -= 4
|
28
|
+
sp += 4
|
29
|
+
sp -= 4
|
30
|
+
CODE
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "MacroParser" do
|
35
|
+
setup { Nil::MacroParser.new }
|
36
|
+
|
37
|
+
context "Basics" do
|
38
|
+
asserts("that the original code equals code passed to parser") do
|
39
|
+
code = "++><>--" # Just some random stuff
|
40
|
+
topic.parse code
|
41
|
+
compiled_original_code = topic.original_code
|
42
|
+
code == compiled_original_code
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "Numbered commands" do
|
47
|
+
asserts("that numbered commands are expanded") do
|
48
|
+
code = "+5-5>5<5"
|
49
|
+
compiled_code = topic.parse code
|
50
|
+
compiled_code == "+++++----->>>>><<<<<"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "Subroutines" do
|
55
|
+
asserts("that subroutines are expanded") do
|
56
|
+
code = ":test{++--><}++test:"
|
57
|
+
compiled_code = topic.parse code
|
58
|
+
compiled_code == "++++--><"
|
59
|
+
end
|
60
|
+
|
61
|
+
asserts("that subroutines contaning numbered commands are expanded") do
|
62
|
+
code = ":test{+5}test:"
|
63
|
+
compiled_code = topic.parse code
|
64
|
+
compiled_code == "+++++"
|
65
|
+
end
|
66
|
+
|
67
|
+
asserts("that recursive subroutines throw exceptions") { topic.parse ":sub{sub:}sub:" }.raises(SyntaxError)
|
68
|
+
end
|
69
|
+
|
70
|
+
context "Whitespace & Comments" do
|
71
|
+
asserts("that whitespace is stripped") do
|
72
|
+
code = <<CODE
|
73
|
+
++ --++
|
74
|
+
:add{
|
75
|
+
++
|
76
|
+
}
|
77
|
+
add:
|
78
|
+
CODE
|
79
|
+
compiled_code = topic.parse code
|
80
|
+
compiled_code == "++--++++"
|
81
|
+
end
|
82
|
+
|
83
|
+
asserts("that comments are stripped") do
|
84
|
+
code = ";this is a comment; ++--"
|
85
|
+
compiled_code = topic.parse code
|
86
|
+
compiled_code == "++--"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "File includes" do
|
91
|
+
asserts("that other files are included") do
|
92
|
+
code = ".include(test/test.nil)--"
|
93
|
+
compiled_code = topic.parse code
|
94
|
+
compiled_code == "++--"
|
95
|
+
end
|
96
|
+
|
97
|
+
asserts("that multiple files are included correctly") do
|
98
|
+
code = ".include(test/test.nil).include(test/another.nil)--"
|
99
|
+
compiled_code = topic.parse code
|
100
|
+
compiled_code == "++>>--"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/test/test.nil
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
++
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nil
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Wuffers Lightwolf
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-29 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A brain**** macro language and interpreter
|
15
|
+
email:
|
16
|
+
- wuffers.lightwolf@me.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- .rvmrc
|
23
|
+
- Gemfile
|
24
|
+
- LICENSE
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- lib/nil.rb
|
28
|
+
- lib/nil/interpreter.rb
|
29
|
+
- lib/nil/macro.rb
|
30
|
+
- lib/nil/version.rb
|
31
|
+
- nil.gemspec
|
32
|
+
- test/another.nil
|
33
|
+
- test/nil_test.rb
|
34
|
+
- test/test.nil
|
35
|
+
homepage: http://github.com/w-x-l/nil
|
36
|
+
licenses: []
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.8.23
|
56
|
+
signing_key:
|
57
|
+
specification_version: 3
|
58
|
+
summary: A macro language/interpreter for brain****
|
59
|
+
test_files:
|
60
|
+
- test/another.nil
|
61
|
+
- test/nil_test.rb
|
62
|
+
- test/test.nil
|