Skip to content

Instantly share code, notes, and snippets.

@lyxal
Last active September 27, 2019 02:34
Show Gist options
  • Save lyxal/a95b781c09d066db4ec5062bb3afc89c to your computer and use it in GitHub Desktop.
Save lyxal/a95b781c09d066db4ec5062bb3afc89c to your computer and use it in GitHub Desktop.
Keg2py

Keg to Python Reference

It's official. Keg is now a transpiled language, as interpreting it was just too hard. Here is a guide of how everything now works

graph TB
First[Preprocessor] --> Second
Second[Uncompressor] --> Third
Third[Parser] --> Fourth
Fourth[Translator] --> Fifth
Fifth[Execution] --> Output
Loading

Command Glossary

Alphanumeric characters

Characters in the range of a-z and A-Z will be pushed as so:

letter()

As such, the program ABC would be transpiled as:

A(); B(); C()

And Hello World would become:

H(); e(); l(); l(); o(); space(); W(); o(); r(); l(); d()

This allows for letters to be redefined as macros if that makes sense. (More on that later)

Numbers in the range of 0-9 would follow as such:

0: zero()
1: one()
2: two()
3: three()
4: four()
5: five()
6: six()
7: seven()
8: eight()
9: nine()

Thenceforth, 89 would become:

eight(); nine()

Mathematical operators

Quite simple really:

+: add(stack.pop(), stack.pop())
-: minus(stack.pop(), stack.pop())
*: times(stack.pop(), stack.pop())
/: divide(stack.pop(), stack.pop())
%: modulo(stack.pop(), stack.pop())

Conditional Operators

I'mma start using a new way of writing these. All functions assume the parameters stack.pop(), stack.pop()

=: eq()
≠: nq()
>: gt()
<: lt()
≥: ge()
≤: le()
≬: g0()

Built-in FNS

These functions don't assume any parameters

!: length()
$: swap()
^: reverse()
:: duplicate()
": r_shift()
': l_shift()
,: nice()
.: raw()
?: _input()
~: random()
_: stack.pop()

If Statements

Unlike all prior sections, this section shan't be so brief. Why? Because the humble [...|...] isn't just a function you see.

The general form will become:

if bool(stack.pop):
	...
else:
	...

But what if there is only one section? (i.e. [...])

if bool(stack.pop()):
	...

But what if there is an empty ifTrue section but a filled ifFalse section? (i.e. [|...])

if bool(stack.pop()):
	pass
else:
	...

Although maybe this is more appropriate:

if not bool(stack.pop()):
	...

I mean, really, the only person bothering with this is those making transpilers.

For Loops

These are a bit harder, as, well, Keg loops are a little different. Given the normal counted loop (no vars, integer as condition):

for _ in _loop_eval(stack.pop())
	...

It is important to note that _loop_eval() is defined as such:

def _loop_eval(expr):
	if type(expr) in [int, chr]:
		return range(expr)
	else:
		return expr

But what if there are three parts? (i.e. (count|var|code))

for var in _loop_eval(count):
	code

But what if there isn't a loop condition?

length()
for _ in _loop_eval(stack.pop()):
	...

While loops

While loops are pretty similar to for loops, but easier in a way:

while bool(stack.pop()):
	...

But what if it is a post-test loop? (i.e. {...|P|...})

condition = True
while condition:
	...
	condition = bool(stack.pop())

Functions

The function @name n|...ƒ turns into:

def name(stack):
	temp = Stack()
	for _ in range(n): temp.push(stack.pop())
	...
	for item in temp:
		stack.push(item)

The function @name *|...ƒ gets turned into:

def name(stack):
	...

The function @name _|...ƒ gets turned into:

def name(stack):
	field = stack.pop()
	if type(field) in [int, float]:
		n = int(field)
	elif type(field) is str and len(field) == 1:
		n = _chr(field)
	else:
		n = len(field)
	
	temp = Stack()
	for _ in range(n):
		temp.push(stack.pop())
	
	...
	for item in temp:
		stack.push(item)

Escaping Characters

Escaping characters is an interesting problem:

stack.push("character")

or perhaps

escape(CHARACTER)

Comments

Comments aren't included in the transpiled program

The Register

Hmm. The register. Toggled with &, it will be turned into:

register()

The Reg Extension

Iota

iota(stack)

Sine

sine(stack)

Decrement

decrement(stack)

Nice Input

stack.push(_eval(_input()))

Apply to All

preprocesses as usual

Exclusive Range

Pop a, b and c and generate a range from a to b - 1 and return if c is in that range.

def excl_range(stack):
	query = stack.pop()
	values = [stack.pop(), stack.pop()]
	start, stop = sorted(values)
	range_object = range(start, stop)
	if query in range_object:
		stack.push(1)
	else:
		stack.push(0)

Inclusive Range

Pop a, b and c and generate a range from a to b and return if c is in that range.

def incl_range(stack):
	query = stack.pop()
	values = [stack.pop(), stack.pop()]
	start, stop = sorted(values)
	range_object = range(start, stop + 1)
	if query in range_object:
		stack.push(1)
	else:
		stack.push(0)

Generate Range

Pop a and b and generate a smart, inclusive range:

def smart_range(stack):
	values = [stack.pop(), stack.pop()]
	start, stop = sorted(values)
	range_object = range(start, stop + 1)
	for item in range_object:
		stack.push(item)

Item Split

This is an interesting one, as it requires type checking: nonetheless, here is what it probably transpile to:

def item_split(stack):
	item = stack.pop()
	_type = type(item)
	if _type is int:
		for number in str(item):
			stack.push(int(item))
	elif _type is str and len(item) == 1:
		for number in str(_ord(item)):
			stack.push(int(item))
	else:
		for value in item:
			stack.push(value)

Factorial

def factorial(stack):
	number = stack.pop()
	import math
	try:
		result = math.factorial(number)
	except Exception as e:
		result = "" #A whole lot of who knows what?
	stack.push(result)

Empty stack

def empty(stack):
	stack.clear()
	#Yes, it really is that simple.

Print All

def print_all(stack):
	for item in stack:
		print(item)

Keg+ Extension

Now, for the big guns: it's Keg+ time! This stuff isn't exactly defined yet, so this is new even for me. This is coming out soon, but I figured I might as well incorporate it into here right now.

So I guess each section will have the transpiled code and then a little description of what in the actual fr*ck everything does.

Push'n'Print

Includes characters in the range of ȦƁƇƉƐƑƓǶȊȷǨȽƜƝǪǷɊƦȘȚȔƲɅƛƳƵ

Pushes the specified letter to the stack and immediately prints it -- really helpful for those pesky restricted source challenges!

print(character)

Yes. It really is that easy.

Integer Scanning

The keyword is the new integer scanning operator, meaning that it will read through the code until a non-integer/decimal character is found. This is more a pre-processing command, but will be transpiled as so:

def integer(stack, number):
	stack.push(number)

Type Conversions

The commands ℤℝ⅍℠ (int, float, list and str respectively) all transpile as such:

def convert(stack, _type):
	item = stack.pop()
	try:
		item = _type(item)
	except:
		pass
	stack.push(item)

Case Conversion

The commands ⟰⟱⟷ represent uppercase, lowercase and switch case respectively:

def case_switch(stack, how):
	string = stack.pop()
	if type(string) is not string:
		stack.push(string)
		return
	if how == "upper":
		stack.push(string.upper())
	elif how == "lower":
		stack.push(string.lower())
	else:
		stack.push(string.swapcase())

Square Operator

² is probably found in most golfing languages, and it will square the top of the stack.

def square(stack):
	item = stack.pop()
	stack.push(times(item, item))

Take input as String

will take input as a string, ready to be casted as anything it needs to be casted as:

def string_input(stack):
	stack.push(input())

All(stack)

returns 1 if all items on the big stack are "true"

def all_true(stack):
	if all(stack):
		stack.push(1)
	else:
		stack.push(0)

All equal

pushes 1 if everything is the same (no popping)

def all_equal(stack):
	equal = 1
	last = None
	for item in stack:
		if last is None:
			last = item
			continue
		else:
			if item != last:
				equal = 0
				break
	stack.push(equal)

Summate stack

does this

def summate(stack):
	for item in stack:
		stack.push(add(stack.pop(), stack.pop()))

Written with StackEdit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment