Created
August 1, 2015 22:20
-
-
Save Cxarli/5393ad09a5f9b99e374b to your computer and use it in GitHub Desktop.
CAPL.rb v1.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Custom class for errorcodes | |
class ERR | |
def self.ERR; -1 end | |
def self.OK; 0 end | |
def self.EXIT; 1 end | |
def self.UNKNOWN; 2 end | |
def self.EMPTY_STACK; 3 end | |
def self.get x | |
case x | |
when self.ERR; 'ERR' | |
when self.OK; 'OK' | |
when self.EXIT; 'EXIT' | |
when self.UNKNOWN; 'UNKNOWN' | |
when self.EMPTY_STACK; 'EMPTY_STACK' | |
end | |
end | |
end | |
# Custom class for stack | |
class Stack | |
# The array used | |
$mystack=[] | |
# --- Main functions --- | |
# Create stack from existing array, or create empty array | |
def initialize arr=nil | |
$mystack = arr || [] | |
end | |
# Push a new value on the stack | |
def push x | |
log "PUSH #{x}" | |
$mystack.push x | |
end | |
# Pop the top value from the stack | |
def pop | |
log "POP #{$mystack[-1]}" | |
if $mystack.length == 0 | |
puts "COULD NOT POP: EMPTY STACK" | |
return [ERR.EMPTY_STACK, "COULD NOT POP"] | |
end | |
$mystack.pop | |
end | |
# --- Custom functions --- | |
# Print the stack | |
def print | |
$mystack.each.with_index do |item, inx| | |
puts "\t[#{inx}] #{item} '#{item.chr}'" | |
end | |
end | |
# Get the length from the stack | |
def length | |
$mystack.length | |
end | |
# Reverse the array `amount` times | |
def reverse amount | |
# Temporary array | |
temp=[] | |
# Pop item from current array, and push it to the temp array | |
amount.times do | |
temp.push pop | |
end | |
# Get first item from temp array, and push it to the current stack | |
temp.each do |x| | |
push x | |
end | |
# Free temp array | |
temp=nil | |
# No need to return anything | |
end | |
# Get the current working array | |
def get | |
$mystack | |
end | |
# Compare to other Stack's | |
def == obj | |
$mystack == obj.get | |
end | |
# See above | |
def eql? obj | |
self == obj | |
end | |
# Create the hash by using a "magic" value, and the hash of the array | |
def hash | |
"CAPL Stack".hash ^ $mystack.hash | |
end | |
end | |
# --- All global variables --- | |
# The content of the file | |
$content=[] | |
# The current stack | |
$stack=Stack.new | |
# The location of the pointer on the grid from $content | |
$loc = { | |
x: 0, | |
y: 0 | |
} | |
# The custom functions | |
$functions = { | |
} | |
# The functions used by the system itself | |
$protectedfunctions=( ' [] 1234567890abcdef " ,.P ~: +-*/% >=E # < vV^ ?! xX L {} ` ; ' .split '' ).uniq | |
# Function detection | |
$isfunction=false | |
$function="" | |
# Number detection | |
$isnumber=false | |
$number=0 | |
# String detection | |
$isstring=false | |
$string="" | |
# Skip the next character | |
$skip=false | |
# Debugging mode | |
$debug=false | |
# Colours :3 | |
$colours = { | |
red: '\\\e[1;31m', | |
none: '\\\e[0m' | |
} | |
$incolour=false | |
# Output a message when it's given and when debug is on, | |
# return the debug value if no message is given | |
def log message=nil | |
# Return debug value if no message is given | |
return $debug if message==nil | |
if $incolour | |
# Colours, run like: | |
# ruby CAPL.rb > >(while read line; do echo -e $line; done) | |
puts $colours[:red]+"\\\\t#{message}"+$colours[:none] if $debug | |
else | |
puts "\t#{message}" if $debug | |
end | |
end | |
# Load a custom framework | |
def load type | |
begin | |
# FileIO; custom library to read and write files | |
if type=="FileIO" | |
# Load the file | |
require '~/Ruby Frameworks/FileIO.rb' | |
# Check if it works | |
log FileIO.check | |
else | |
throw "Type unknown" | |
end | |
# When we failed to load framework | |
rescue Exception => e | |
puts "Failed to load #{type}!" | |
raise e | |
end | |
end | |
# Get the function from a given character | |
def getfunc char | |
log "Getting function '#{char}'" | |
$functions[char] | |
end | |
# Create new function | |
def setfunc char, func | |
log "Adding function '#{char}'" | |
if getfunc char | |
if $protectedfunctions.include? char | |
puts "Overwriting system function '#{char}' !!" | |
else | |
log "Overwriting function '#{char}'." | |
end | |
end | |
$functions[char]=func | |
end | |
# Execute a function | |
def execfunc char | |
# Get function | |
func=getfunc char | |
log "Function '#{char}': #{func}" | |
# Backup and reset location | |
backup=$loc | |
$loc = { | |
x: 0, | |
y: 0 | |
} | |
# Create new grid | |
grid=explode func.split "\n" | |
# Parse function | |
while $loc[:y] < grid.length && grid[$loc[:y]][$loc[:x]] != ';' | |
parse grid[$loc[:y]][$loc[:x]] | |
$loc[:x]+=1 | |
end | |
# Re-set location from backup | |
$loc=backup | |
end | |
# Load external file | |
def extload file | |
log "Loading file '#{file}'..." | |
# Saving global values | |
backup = [ | |
$content, | |
$stack.get, | |
$loc, | |
$functions, | |
$debug | |
] | |
# Resetting global values | |
$stack=Stack.new | |
$loc = { | |
x: 0, | |
y: 0 | |
} | |
$functions = { | |
} | |
$debug=false | |
# Unsafe ^^' | |
# Luckily I have backups. Heh.. heh.... *help* | |
main [file] | |
# Get the new stack and functions from the new file | |
newstack=$stack.get | |
newfunctions=$functions | |
# Resetting global values | |
$content, starr, $loc, $functions, $debug = backup | |
$stack=Stack.new starr | |
# Push the items from the new stack onto the old stack | |
newstack.each do |x| | |
$stack.push x | |
end | |
# Add the new functions to the current functionlist | |
newfunctions.each do |char, func| | |
setfunc char, func | |
end | |
log "Loading #{file} done!" | |
end | |
# Parse the current character | |
def parse char | |
# If the character is a nullpointer (end of the line), continue to next line | |
if char==nil | |
log "NIL DOWN" | |
$loc[:x]=-1 | |
$loc[:y]+=1 | |
char="\n" | |
end | |
# If we expect a function, and the character isn't '}' | |
if $isfunction && char != '}' | |
# Add the character to the current function | |
$function+=char | |
# Return OK value | |
return [ERR.OK] | |
end | |
# If we expect a number, and the character isn't ']' | |
if $isnumber && char != ']' | |
# Check if it really is a number | |
if char=~/^[0-9]+$/ | |
# Add the new number to the global number | |
$number*=10 | |
# MY way of converting a single-digit number | |
$number+= char.ord - '0'.ord | |
# Return OK value | |
return [ERR.OK] | |
# If it isn't a number | |
else | |
puts "'#{$char}' is not a number, but is between square brackets [] !" | |
end | |
end | |
# If we expect a string, and the character isn't '"' | |
if $isstring && char != '"' | |
# Add the character to the current string | |
$string+=char | |
# Return OK value | |
return [ERR.OK] | |
end | |
if char==0 | |
# Check for custom functions | |
elsif getfunc char | |
execfunc char | |
# We can safely ignore whitespace | |
elsif char==' ' || char=="\n" | |
# : WHY ?! | |
# Charlie: Because you're not important for this project | |
# : ... | |
# Charlie: I'm sorry, space | |
# : I'll get you back | |
# Charlie: I'll make a project full of spaces sometime | |
# : <3 | |
# Charlie: <3 you too, space | |
# | |
#: And what about me? | |
# Charlie: And I'll add you too to that project, newline c: | |
# | |
#: Thanks :) | |
# Charlie: For you: | |
whitespace = "My best friends!" | |
# | |
#: <3 | |
# : <3 | |
# Skip the following character | |
elsif $skip | |
log "SKIP" | |
$skip=false | |
# char: DON'T IGNORE ME YOU STUPID PROGRAMMER | |
# Charlie: ... | |
# char: I HEAR YOU SKIPPING ME | |
# Charlie: ... | |
# char: Are you kidding... | |
# Charlie: ... | |
# char: Okay, nevermind, this is worse than being skipped | |
# Charlie: Okay, I'll give you some use | |
char=nil | |
# char: YOU JUST DELETED ME | |
# Charlie: Hmn-mn | |
# char: ... | |
# Charlie: Now you're skipping me | |
# char: ... | |
# Charlie: *sigh* I'll return that you are annoying | |
return [ERR.OK, "CHAR IS ANNOYING :'("] | |
# Multi-digit number opening | |
elsif char=='[' | |
$isnumber=true | |
# Multi-digit number closing | |
elsif char==']' | |
$isnumber=false | |
$stack.push $number | |
$number=0 | |
# Check for single hexadecimal number | |
elsif char=~/[0-9a-f]/ | |
x=char.to_i 16 | |
$stack.push x | |
# String | |
elsif char=='"' | |
# If we don't expect a string ... | |
if ! $isstring | |
# ... we do now | |
$isstring=true | |
# ... but if we do ... | |
else | |
# ... we don't now | |
$isstring=false | |
# Add the global string to the stack | |
$string.split('').each do |c| | |
$stack.push c.ord | |
end | |
# Reset global string | |
$string="" | |
end | |
# Output | |
# Output as number | |
elsif char==',' | |
log "NUMBER OUT" | |
print $stack.pop | |
puts if log | |
# Output as letter | |
elsif char=='.' | |
log "CHAR OUT" | |
print $stack.pop.chr | |
puts if log | |
# Output the stack | |
elsif char=='P' | |
log "PRINT" | |
$stack.print | |
# Manipulation | |
# Reverse `x` items | |
elsif char=='~' | |
log "REVERSE #{x=$stack.pop}" | |
$stack.reverse x | |
# Copy top value | |
elsif char==':' | |
log "COPY" | |
x=$stack.pop | |
$stack.push x | |
$stack.push x | |
# Add, subtract, multiplicate, devide and modulus | |
elsif char=~/[\+\-\*\/\%]/ | |
log "EXPR #{char}" | |
y=$stack.pop | |
x=$stack.pop | |
# Eval is safe here | |
z=eval("#{x}#{char}#{y}") | |
$stack.push z | |
# Comparison | |
# Do 2~> if you want < (less than) | |
elsif char=='>' | |
log "GT" | |
x=$stack.pop | |
y=$stack.pop | |
z=x>y ? 1:0 | |
$stack.push z | |
# Simple equal to | |
elsif char=='=' | |
log "SIMPLEQ" | |
x=$stack.pop | |
y=$stack.pop | |
z=x==y ? 1:0 | |
$stack.push z | |
# Extended equal to | |
elsif char=='E' | |
log "EXTEQ" | |
# Amount of items to compare | |
x=$stack.pop | |
item0=[] | |
item1=[] | |
z=1 | |
x.times do |i| | |
item0[i]=$stack.pop | |
end | |
x.times do |i| | |
if item0[i] != $stack.pop | |
z=0 | |
end | |
end | |
$stack.push z | |
# Get length | |
elsif char=='#' | |
log "LENGTH" | |
x=$stack.length | |
$stack.push x | |
# Go back to the start of the line | |
elsif char=='<' | |
log "HOME" | |
$loc[:x]=-1 | |
# Go `x` lines down | |
elsif char=='v' | |
log "DOWN" | |
x=$stack.pop | |
$loc[:x]=-1 | |
$loc[:y]+=x | |
# Go 1 line down | |
elsif char=='V' | |
log "DOWN" | |
$loc[:x]=-1 | |
$loc[:y]+=1 | |
# Go `x` lines up | |
elsif char=='^' | |
log "UP" | |
x=$stack.pop | |
$loc[:x]=-1 | |
$loc[:y]-=x | |
# Contitional. Go 1 to the right if `x` is not 0, or 2 to the right if it is | |
elsif char=='?' | |
log "COND" | |
x=$stack.pop | |
if x==0 | |
$skip=true | |
end | |
# Skip the following character | |
elsif char=='!' | |
log "SKIP" | |
$skip=true | |
# Delete top values | |
elsif char=='x' | |
log "DELETE" | |
# Amount of items to delete | |
x=$stack.pop | |
x.times do |i| | |
$stack.pop | |
end | |
# Delete top value | |
elsif char=='X' | |
$stack.pop | |
# Load external file | |
elsif char=='L' | |
log "LOAD" | |
x=$stack.pop | |
name="" | |
# Collect name | |
x.times do |i| | |
name+=$stack.pop.chr | |
end | |
log "Filename: '#{name}'" | |
extload name | |
# Create funcion | |
elsif char=='{' | |
$isfunction=true | |
elsif char=='}' | |
$isfunction=false | |
char=$stack.pop.chr | |
setfunc char, $function | |
$function="" | |
# Debugging mode | |
elsif char=='`' | |
log "DEBUG " + ( $debug ? "OFF" : "ON" ) | |
$debug = !$debug | |
# Exit file | |
elsif char==';' | |
return [ERR.EXIT] | |
# If we have NO idea what we have to do, just throw an error | |
else | |
return [ERR.UNKNOWN, "[#{char.ord}] '#{char}'"] | |
end | |
return [ERR.OK] | |
end | |
# Explode an array | |
def explode pre | |
# New array | |
post=[] | |
# Split each string from `pre` into its characters | |
pre.each.with_index do |item, inx| | |
post[inx] = item.split '' | |
end | |
# New array | |
post | |
end | |
# Get the character at the current position | |
def get | |
line = $content [ $loc[:y] ] | |
# End of file | |
if line==nil | |
# Send exit | |
log "UNSAFE EXIT" | |
return ';' | |
end | |
char = line [ $loc[:x] ] | |
return char | |
end | |
def main args | |
# No arguments enables debug mode | |
if args.count == 0 | |
$debug=true | |
log "Debug enabled!" | |
end | |
setfunc 'H', | |
' | |
"Hello World!" #~ | |
#?!V.< | |
a. | |
' | |
# Setting filename | |
filename=args[0] || "debug.cpl" | |
log "Filename: #{filename}" | |
# Loading FileIO to load files | |
load "FileIO" | |
# Loading FileIO for current file | |
fio=FileIO.new "#{filename}" | |
# Convert 2D array to 3D array | |
# ['abc', 'def'] => [['a','b','c'], ['d','e','f']] | |
$content=explode fio.getContent | |
# Logging content if debug mode enabled | |
if log | |
puts "Content:" | |
$content.each do |line| | |
# Two tabs | |
print "\t\t" | |
# Outputting each line | |
line.each do |char| | |
print char | |
end | |
# Newline | |
puts | |
end | |
# Newline | |
puts | |
end | |
# Start parsing everything | |
x=[ERR.OK] | |
# While we don't have to exit | |
while x[0] != ERR.EXIT | |
# Log the character | |
chr=get | |
log "'#{chr}'" | |
# Default errorcode | |
x=ERR.ERR | |
begin | |
# Parsing current character | |
x=parse chr | |
# When error happens | |
rescue Exception => e | |
# Print location and character | |
puts "(#{$loc[:x]},#{$loc[:y]}) '#{get}'" | |
# Print default stacktrace | |
raise e | |
end | |
# If it didn't run correctly | |
if x[0] != ERR.OK && x[0] != ERR.EXIT | |
# Output location, character, errorcode and errormessage | |
puts "(#{$loc[:x]},#{$loc[:y]}) '#{get}' #{ERR.get x[0]}: #{x[1]}" | |
end | |
# Next character | |
$loc[:x]+=1 | |
end | |
end | |
# Start with arguments | |
main ARGV |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment