Version 0.0.1
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
.zig-cache
|
||||
zig-out
|
||||
@@ -0,0 +1,84 @@
|
||||
# inferius
|
||||
Quod est superius est sicut quod inferius, et quod inferius est sicut quod est superius.
|
||||
|
||||
## Usage
|
||||
|
||||
```inferius -m <Size of scratch buffer, default to 30,000 bytes> <filename.inferius> "Arguments"```
|
||||
|
||||
## Additional Usage Notes
|
||||
|
||||
You may pass null for the stdin and stdout of inferius.inferiusInterpretor, disabling stdin as a NOOP and outputting results to an output buffer. Using the stack functions inferius.inferiusInterpretor.push() and inferius.inferiusInterpretor.pop(), you can pass data for execution of inferius instructions, and return the results using either the stack or inferius.inferiusInterpretor.output.items output buffer.
|
||||
|
||||
## Instructions
|
||||
|
||||
Unless it is a valid instruction, it treats it as a NOOP. It has complete compatability with BF otherwise.
|
||||
|
||||
### The Usual BF Instructions
|
||||
|
||||
|Instruction|Description|
|
||||
|-|-|
|
||||
|>|Increase Scratch Memory Pointer by 1 (Wraps back to zero)|
|
||||
|<|Decrease Scratch Memory Pointer by 1 (Wraps back to end of scratch buffer)|
|
||||
|+|Increase Byte pointed to by Scratch Memory Pointer by 1 (Wraps back to zero)|
|
||||
|-|Increase Byte pointed to by Scratch Memory Pointer by 1 (Wraps back to 0xFF)|
|
||||
|.|Print char to stdout pointed to by Scratch Memory Pointer|
|
||||
|,|Read char from stdin and save to Scratch Memory pointed to by Scratch Memory Pointer|
|
||||
|[|If Scratch location ponted to by Scratch Memory Pointer is 0, skips to matching ']', else executes until ']'|
|
||||
|]|If Scratch location pointed to by Scratch Memory Pointer is not 0, jumps back to matching '[', else keeps executing|
|
||||
|
||||
### Instruction Extensions
|
||||
|
||||
|Instruction|Description|
|
||||
|-|-|
|
||||
|%|Swap Byte of the Scratch location pointed to by Scratch Memory Pointer with Byte Value of the SWP register|
|
||||
|{|Binary Shift Left Byte of Scratch location pointed to by Scratch Memory Pointer|
|
||||
|}|Binary Shift Right Byte of Scratch location pointed to by Scratch Memory Pointer|
|
||||
|~|Invert Bits of Scratch location pointed to by Scratch Memory Pointer|
|
||||
|^|XOR Byte of the Scratch location pointed to by Scratch Memory Pointer with the Byte Value stored in the SWP register|
|
||||
|&|AND Byte of the Scratch location pointed to by Scratch Memory Pointer with the Byte Value stored in the SWP register|
|
||||
|\||OR Byte of the Scratch location pointed to by Scratch Memory Pointer with the Byte Value stored in the SWP register|
|
||||
|?|Print Debuging Information|
|
||||
|*|Save Scratch Memory Pointer to SAV register|
|
||||
|0|Restore Scratch Memory Pointer from SAV register (Initial State is zero)|
|
||||
|
||||
### Stack Instruction Extensions
|
||||
|
||||
|Instruction|Description|
|
||||
|-|-|
|
||||
|#|Toggle PUSH/POP Operation destination to SWP register or Scratch location pointed to by Scratch Memory Pointer (Defaults to Scratch location pointed to by Scratch Memory Pointer)|
|
||||
|@|Toggle Stack Operation between FIFO and a FILO (Defaults to FIFO)|
|
||||
|:|PUSH Byte Value pointed to by Scratch Memory Pointer onto the Stack|
|
||||
|;|POP Byte Value from the Stack and Set Byte of location pointed to by Scratch Memory Pointer|
|
||||
|a|POP Two Byte Values from the Stack, Add them together (Wrapping Overflow Values), and PUSH result back onto the Stack|
|
||||
|s|POP Two Byte Values from the Stack, Subtract them (Wrapping Overflow Values), and PUSH result back onto the Stack|
|
||||
|m|POP Two Byte Values from the Stack, Multiply them together (Wrapping Overflow Values), and PUSH result back onto the Stack|
|
||||
|/|POP Two Byte Values from the Stack, Divide them, and PUSH result back onto the Stack|
|
||||
|c|POP Two Byte Values from the Stack, perform Modulus Division on them, and PUSH result back onto the Stack|
|
||||
|L|POP Byte Value from the Stack, perform a Binary Shift Left of it's value, and PUSH result back onto the Stack|
|
||||
|R|POP Byte Value from the Stack, perform a Binary Shift Right of it's value, and PUSH result back onto the Stack|
|
||||
|O|POP Two Byte Values from the Stack, OR the Byte Values together, and PUSH result back onto the Stack|
|
||||
|A|POP Two Byte Values from the Stack, AND the Byte Values together, and PUSH result back onto the Stack|
|
||||
|X|POP Two Byte Values from the Stack, XOR the Byte Values together, and PUSH result back onto the Stack|
|
||||
|I|POP Byte from the Stack, Invert Bits, and PUSH result back onto the Stack|
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2025 William Welna (wwelna@occultusterra.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,57 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const mod = b.addModule("inferius", .{
|
||||
.root_source_file = b.path("src/inferius.zig"),
|
||||
.target = target,
|
||||
});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "inferius",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "inferius", .module = mod },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const clap = b.dependency("clap", .{});
|
||||
exe.root_module.addImport("clap", clap.module("clap"));
|
||||
exe.linkLibC();
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const mod_tests = b.addTest(.{
|
||||
.root_module = mod,
|
||||
});
|
||||
|
||||
// A run step that will run the test executable.
|
||||
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||
|
||||
const exe_tests = b.addTest(.{
|
||||
.root_module = exe.root_module,
|
||||
});
|
||||
|
||||
const run_exe_tests = b.addRunArtifact(exe_tests);
|
||||
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&run_mod_tests.step);
|
||||
test_step.dependOn(&run_exe_tests.step);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
.{
|
||||
.name = .inferius,
|
||||
.version = "0.0.1",
|
||||
.fingerprint = 0xe30472b6da223d0e,
|
||||
.minimum_zig_version = "0.15.2",
|
||||
.dependencies = .{
|
||||
.clap = .{
|
||||
.url = "https://github.com/Hejsil/zig-clap/archive/refs/tags/0.11.0.tar.gz",
|
||||
.hash = "clap-0.11.0-oBajB-HnAQDPCKYzwF7rO3qDFwRcD39Q0DALlTSz5H7e",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (C) 2025 William Welna (wwelna@occultusterra.com)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const inferiusInterpretor = struct {
|
||||
const Self = @This();
|
||||
pub const exeParam = struct {
|
||||
program_pos:usize = 0,
|
||||
loop:bool = false,
|
||||
loop_start:usize = 0,
|
||||
};
|
||||
pub const Fail = error {
|
||||
Odd_Brackets,
|
||||
Odd_Brackets_Overflow,
|
||||
};
|
||||
mem:[]u8,
|
||||
swp:u8,
|
||||
pos_sav:usize,
|
||||
size:usize,
|
||||
pos:usize,
|
||||
toggle_swp:bool,
|
||||
toggle_stack:bool,
|
||||
program:[]const u8,
|
||||
output:std.ArrayList(u8),
|
||||
stack:std.ArrayList(u8),
|
||||
allocator:std.mem.Allocator,
|
||||
stdout:?*std.io.Writer,
|
||||
stdin:?*std.io.Reader,
|
||||
|
||||
pub fn init(allocator:std.mem.Allocator, size:usize, program:[]const u8, stdin:?*std.io.Reader, stdout:?*std.io.Writer) !inferiusInterpretor {
|
||||
const mem = try allocator.alloc(u8, size);
|
||||
@memset(mem, 0);
|
||||
return .{
|
||||
.program = program,
|
||||
.mem = mem,
|
||||
.swp = 0,
|
||||
.pos_sav = 0,
|
||||
.allocator = allocator,
|
||||
.pos = 0,
|
||||
.toggle_swp = false,
|
||||
.toggle_stack = false,
|
||||
.size = size,
|
||||
.output = .empty,
|
||||
.stack = .empty,
|
||||
.stdin = stdin,
|
||||
.stdout = stdout,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn push(self:*Self, c:u8) !void {
|
||||
try self.stack.append(self.allocator, c);
|
||||
}
|
||||
|
||||
pub fn pop(self:*Self) ?u8 {
|
||||
if(!self.toggle_stack) if(self.stack.items.len > 0) return self.stack.orderedRemove(0) else return null else return self.stack.pop();
|
||||
}
|
||||
|
||||
pub fn execute(self:*Self, param:exeParam) !usize {
|
||||
var program_pos = param.program_pos;
|
||||
var temp:u8 = undefined;
|
||||
while(program_pos < self.program.len) {
|
||||
switch(self.program[program_pos]) {
|
||||
'>' => {
|
||||
if(self.pos < self.size-1) self.pos += 1 else self.pos %= self.size-1;
|
||||
program_pos += 1;
|
||||
},
|
||||
'<' => {
|
||||
if(self.pos == 0) self.pos = self.size-1 else self.pos -= 1;
|
||||
program_pos += 1;
|
||||
},
|
||||
'+' => {self.mem[self.pos] +%= 1; program_pos += 1;},
|
||||
'-' => {self.mem[self.pos] -%= 1; program_pos += 1;},
|
||||
'.' => {
|
||||
if(self.stdout) |o| { // use output buffer if stdout not set
|
||||
try o.print("{c}", .{self.mem[self.pos]}); try o.flush();
|
||||
} else { try self.output.append(self.allocator, self.mem[self.pos]); }
|
||||
program_pos += 1;
|
||||
},
|
||||
',' => {
|
||||
if(self.stdin) |i| { // ignore if stdin not set
|
||||
self.mem[self.pos] = try i.takeByte();
|
||||
} else self.mem[self.pos] = 0;
|
||||
program_pos += 1;
|
||||
},
|
||||
// Extended Operations { left bitshift } right bitshift % swap ~ bit invert ^ xor | or & and
|
||||
'{' => {self.mem[self.pos] <<= 1; program_pos += 1;},
|
||||
'}' => {self.mem[self.pos] >>= 1; program_pos += 1;},
|
||||
'%' => {temp = self.mem[self.pos]; self.mem[self.pos] = self.swp; self.swp = temp; program_pos += 1;},
|
||||
'~' => {self.mem[self.pos] = ~self.mem[self.pos]; program_pos += 1;},
|
||||
'^' => {self.mem[self.pos] ^= self.swp; program_pos += 1;},
|
||||
'|' => {self.mem[self.pos] |= self.swp; program_pos += 1;},
|
||||
'&' => {self.mem[self.pos] &= self.swp; program_pos += 1;},
|
||||
'?' => { // Perfectly readable
|
||||
std.debug.print("<{}/{}=>[{x}]->{}:{}/{}:{}>", .{self.pos, self.pos_sav, self.mem[self.pos], self.swp, self.toggle_stack, self.toggle_swp, self.stack.items.len});
|
||||
program_pos += 1;
|
||||
},
|
||||
'*' => {self.pos_sav = self.pos; program_pos += 1;},
|
||||
'0' => {self.pos = self.pos_sav; program_pos += 1;},
|
||||
// Stack Operations : push ; pop # toggle between swp and pointer, a add s sub d div m mul c mod , L leftshift R rightshift O or X xor A and I invert
|
||||
':' => {if(!self.toggle_swp) { try self.push(self.mem[self.pos]);} else { try self.push(self.swp);} program_pos += 1;},
|
||||
';' => {temp = self.pop() orelse 0; if(!self.toggle_swp) self.mem[self.pos] = temp else self.swp = temp; program_pos += 1;},
|
||||
'#' => {self.toggle_swp = ~self.toggle_swp; program_pos += 1;},
|
||||
'@' => {self.toggle_stack = ~self.toggle_stack; program_pos += 1;},
|
||||
'a' => {temp = self.pop() orelse 0; temp +%= self.pop() orelse 0; try self.push(temp); program_pos += 1;},
|
||||
's' => {temp = self.pop() orelse 0; temp -%= self.pop() orelse 0; try self.push(temp); program_pos += 1;},
|
||||
'm' => {temp = self.pop() orelse 0; temp *%= self.pop() orelse 0; try self.push(temp); program_pos += 1;},
|
||||
'd' => {temp = self.pop() orelse 0; temp /= self.pop() orelse 0; try self.push(temp); program_pos += 1;},
|
||||
'c' => {temp = self.pop() orelse 0; temp %= self.pop() orelse 0; try self.push(temp); program_pos += 1;},
|
||||
'L' => {temp = self.pop() orelse 0; temp <<= 1; try self.push(temp); program_pos += 1;},
|
||||
'R' => {temp = self.pop() orelse 0; temp >>= 1; try self.push(temp); program_pos += 1;},
|
||||
'O' => {temp = self.pop() orelse 0; temp |= self.pop() orelse 0; try self.push(temp); program_pos += 1;},
|
||||
'X' => {temp = self.pop() orelse 0; temp ^= self.pop() orelse 0; try self.push(temp); program_pos += 1;},
|
||||
'A' => {temp = self.pop() orelse 0; temp &= self.pop() orelse 0; try self.push(temp); program_pos += 1;},
|
||||
'I' => {temp = self.pop() orelse 0; try self.push(~temp); program_pos += 1;},
|
||||
// Loops
|
||||
'[' => {
|
||||
if(self.mem[self.pos] == 0) {
|
||||
var nested:usize = 1;
|
||||
program_pos += 1;
|
||||
while(nested > 0 and program_pos < self.program.len) : (program_pos += 1) {
|
||||
if(self.program[program_pos] == ']') nested -= 1 else if (self.program[program_pos] == '[') nested += 1;
|
||||
}
|
||||
if(nested != 0) return Fail.Odd_Brackets_Overflow;
|
||||
} else {
|
||||
program_pos += 1;
|
||||
program_pos = try self.execute(.{.program_pos = program_pos, .loop = true, .loop_start = program_pos});
|
||||
}
|
||||
},
|
||||
']' => {
|
||||
if(param.loop == true) {
|
||||
if(self.mem[self.pos] == 0) {
|
||||
return program_pos + 1;
|
||||
} else program_pos = param.loop_start;
|
||||
} else { return Fail.Odd_Brackets; }
|
||||
},
|
||||
else => {program_pos += 1;},
|
||||
}
|
||||
}
|
||||
return self.program.len-1;
|
||||
}
|
||||
|
||||
pub fn deinit(self:*Self) void {
|
||||
self.output.deinit(self.allocator);
|
||||
self.stack.deinit(self.allocator);
|
||||
self.allocator.free(self.mem);
|
||||
}
|
||||
};
|
||||
|
||||
pub const inferiusCompiler = struct { }; // WIP
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (C) 2025 William Welna (wwelna@occultusterra.com)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
const VERSION_STRING = "0.0.1";
|
||||
|
||||
const std = @import("std");
|
||||
const clap = @import("clap");
|
||||
const inferius = @import("inferius");
|
||||
|
||||
pub fn halp(mauh:*std.io.Writer) !void {
|
||||
try mauh.print("Quod est superius est sicut quod inferius, et quod inferius est sicut quod est superius.\n", .{});
|
||||
try mauh.flush();
|
||||
}
|
||||
|
||||
pub fn version(mauh:*std.io.Writer) !void {
|
||||
try mauh.print("inferius {s}\n", .{VERSION_STRING});
|
||||
try mauh.flush();
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var stdout_buffer: [128]u8 = undefined;
|
||||
var stdin_buffer: [128]u8 = undefined;
|
||||
|
||||
var stdout_writer_wrapper = std.fs.File.stdout().writer(&stdout_buffer);
|
||||
const stdout: *std.io.Writer = &stdout_writer_wrapper.interface;
|
||||
|
||||
var stdin_reader_wrapper = std.fs.File.stdin().reader(&stdin_buffer);
|
||||
const stdin: *std.io.Reader = &stdin_reader_wrapper.interface;
|
||||
|
||||
var memory:usize = 30000;
|
||||
|
||||
const params = comptime clap.parseParamsComptime(
|
||||
\\-h, --help Display this help and exit.
|
||||
\\-v, --version Display Version
|
||||
\\-m, --memory <usize> Size of scratch buffer, default to 30,000 bytes
|
||||
\\<str>...
|
||||
\\
|
||||
);
|
||||
|
||||
var diag = clap.Diagnostic{};
|
||||
var res = clap.parse(clap.Help, ¶ms, clap.parsers.default, .{
|
||||
.diagnostic = &diag,
|
||||
.allocator = allocator,
|
||||
}) catch |err| {
|
||||
try diag.reportToFile(.stderr(), err);
|
||||
return;
|
||||
};
|
||||
defer res.deinit();
|
||||
|
||||
if(res.args.version != 0) {try version(stdout); return;}
|
||||
if(res.args.help != 0) {try halp(stdout); return;}
|
||||
if(res.args.memory) |m| memory = m;
|
||||
if(res.positionals[0].len > 0 and res.positionals[0].len < 3) {
|
||||
const d = std.fs.cwd().readFileAlloc(allocator, res.positionals[0][0], 1024^2) catch |err| {
|
||||
std.debug.print("Can't open '{s}': {s}\n", .{res.positionals[0][0], @errorName(err)});
|
||||
return;
|
||||
};
|
||||
defer allocator.free(d);
|
||||
var vm = try inferius.inferiusInterpretor.init(allocator, memory, d, stdin, stdout);
|
||||
if(res.positionals[0].len == 2) {
|
||||
for(res.positionals[0][1]) |c| {
|
||||
try vm.push(c);
|
||||
}
|
||||
}
|
||||
_ = try vm.execute(.{});
|
||||
defer vm.deinit();
|
||||
try stdout.flush();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
;[
|
||||
.>;
|
||||
]
|
||||
@@ -0,0 +1,12 @@
|
||||
~}}++
|
||||
+++++++.
|
||||
---.
|
||||
+++++++..
|
||||
+++.
|
||||
>~}}}+.
|
||||
<++++++++.
|
||||
--------.
|
||||
+++.
|
||||
------.
|
||||
--------.
|
||||
}}}++.
|
||||
@@ -0,0 +1,13 @@
|
||||
~}}++
|
||||
+++++++:
|
||||
---:
|
||||
+++++++::
|
||||
+++::@#;#@
|
||||
>~}}}+:
|
||||
<++++++++:
|
||||
%:
|
||||
+++:
|
||||
------:
|
||||
--------:
|
||||
}}}++:
|
||||
;[.>;]
|
||||
Reference in New Issue
Block a user