From Esolang
Jump to navigation Jump to search

My favorite type of esolang is stack-based RPN languages. I also like esolangs that are not completely symmetrical (such as FALSE's peek without poke. I was introduced to the concept of an esolang after hearing about "dc.sed" and "unlambda.sed" which are interpreters for the Unix desk calculator and Unlambda, written in sed, while trying to figure out what kinds of things I can do with sed.

My LARSA interpreter is available here. I have also written an Underload interpreter in sed.

Non-Esolangs with unusual features

  • sed - Stream editor. All commands are one character. Only two variables: the pattern space and the hold space. All operations involve regexes and strings. No built-in support for any numeric operations. No numbers except for line numbers and repeat counts, which can only be used in conditions.
  • m4 - Macro processor. Operates on its own source code as its input. Can generate new functions by outputting define() statements.
  • dc - RPN calculator. Uses single character commands, except for some commands made up of a command+a register. Can use any character as a register name, even NUL, newline, EOF, and Ctrl-C. Every register is also a stack. There are no explicit stack operations because pushing and popping registers can do that. Similar to FALSE.
  • curses tiparm() function - Extended printf-like format that has a stack, immediate constants, and 26 local variables. Parameters can be read from the argument list and used in arithmetic, bitwise, and logical operations, and then formatted using one of the standard printf() string, character, or numeric formats. The specifiers pop their arguments from the stack, or if the stack is empty, read from the argument list. There's even an if-else based on percent specifiers.

Some ideas of mine

Three-way If for FALSE

Similar to how User:IanO's DUP uses [true][false]? which is much better for if-elses that push/pop more than one operand, I was inspired by Fortran's [Arithmetic IF] to create this type of operation. With this, there is no need for any comparison operators, just subtraction. You can use it to create a sign function, your own comparison operators, min/max, or anything else that uses a three-way comparison.

{<     tff} -[true][false]$?
{>=    ftt} -[true][false]\$?

{>     fft} -[true][false]$@?
{<=    ttf} -[true][false]\$@?

{==    ftf} -[true][false]$@\?
{!=    tft} -[true][false]\$@\?

Interesting things about FALSE

  • It's possible to compare variable pointers. For example, ac> is false, but ca> is true. Something like aa= can be used with ? to ensure that a variable passed by reference is/isn't allowed to be used.
  • Variables can be passed by reference, as xyzf;! and then the lambda f can dereference them itself. This lets functions write to different variables.
  • A function can make a "stack frame" like [z;1øz:y;3øy:x;5øx:...var;(2n+1)øvar:...ops...stores...x:y:z:%%%...drops]f: that saves the previous values of all used variables, even if they're undefined, replaces them with a number of stack arguments, and then restores them at the end. By replacing the stores with \var: or @var: and the drops with \% or @%, it can return 1 or 2 values, respectively. This allows functions to be more complicated, since they can work with up to 26 writable variables at once (if they don't need recursion or other functions), instead of just the top 3 stack items. They can still pick the values below the stack frame. This was inspired by dc's bc calculator compiler. Bc pushes the register stacks down with new values instead of pushing the previous values onto the main stack, but FALSE's variables aren't stacks, and dc doesn't have a pick operation (except for arrays), so both use the easiest way for their language.
  • The original implementation allowed variable pointers to be used with pointer arithmetic, such as "a 5+;" for a[5]. This would work with variables passed by reference to let the programmer choose different arrays to store data into.

dc/FALSE correspondences

For these comparisons, the dc representation will be on the left and the FALSE equivalent (or nearest equivalent) will be on the right. N/A means that there is no equivalent (without writing a complex routine by hand). Explanations are usually only given for items marked N/A for one of the languages.

dc			FALSE
123			123
_123			123_
123.456			N/A (FALSE is integer-only)

+			+
-			-
*			*
/			/
Sa0La-			_
%			$@$@/@*-
^			j;1°j:i;3°i:1[j;$1-j:][i;*]#\i:\j:\%\% (FALSE version is non-optimal)
v			N/A (compute square root)
X			N/A (pop number and push the number of digits after the decimal point)
Z			N/A (pop string or number and push its length)
N/A			& (bitwise and)
N/A			| (bitwise or)
N/A			~ (bitwise not)

d			$
s.			%
SaSbLaLb		\
SaSbScLbLaLc		@
dSaSa[Sbla1-dsa0!=c]dScxdscLas.[Lbla1-dsa0!=c]dScxLcs.Las.Lc			ø (dc version is non-optimal)
c			N/A (clear the stack)
z			N/A (push the stack depth)

Lambdas and flow control:
[...]			[...]
x			!
s.0!=.			?
q			N/A (quits the program)
Q			N/A (nQ breaks n levels of the call stack)
[s._1]s.-d0!=.		=
[ddSa*vLa/1+2/]s.-d0!=.	>
<var			>[;!]?
=var			=[;!]?
>var			\>[;!]?
!<var			>~[;!]?
!=var			=~[;!]?
!>var			\>~[;!]?

l			;
s			:
L			N/A (treat register as a stack and pop it)
S			N/A (treat register as a stack and push onto it)
;			+;
:			+:

String and I/O:
[...]P			"..."
P			.
p			$."\n"
N/A			^ (read a character)
?			N/A (read a line and execute it as code)
N/A			, (print a character; can be emulated in dc with an array mapping ASCII values to character strings)
N/A			ß (flush the buffer)

Base Conversion: (dc only)
i	(set the input base)
I	(push the current input base)
o	(set the output base)
O	(push the current output base)
k	(set the maximum number of decimal places)
K	(push the current maximum number of decimal places)

[...]s.			{...} (comment)
N/A			` (embed number as opcodes)
!			N/A (execute the rest of the line as a system/shell command)
f			N/A (print all values on the stack)
Y			D (print debugging information)