Skip to content

Instantly share code, notes, and snippets.

@Cxarli
Created August 1, 2015 22:20
Show Gist options
  • Save Cxarli/5393ad09a5f9b99e374b to your computer and use it in GitHub Desktop.
Save Cxarli/5393ad09a5f9b99e374b to your computer and use it in GitHub Desktop.
CAPL.rb v1.1
# 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