Fueue, 423 bytes
Fueue is a queue-based esolang in which the running program is the queue.
)$$4255%%1(~):[)$$24%%0:<[~:)~)]~[$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]](H-):~:[)[):~[)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/]+:5):]~:](106328966328112328136317639696111819119696281563139628116326221310190661962811611211962861109696289611619628116111612896281115421063633063961111116163963011632811111819159628151213262722151522061361613096119619190661966311961128966130281807072220060611612811961019070723232022060611
Try it online!
How it works
This explanation may or may not have got way out of hand. On the other hand I don't know how to explain it much shorter in a way I hope people can follow.
Fueue cheat sheet
See esolang wiki article for details, including the few features not used in this program.
The initial program is the initial state of the queue, which can contain the following elements:
- Integer literals (only non-negative in the source, but negative ones can be calculated), executing them prints a character.
- Square-bracket delimited nested blocks, inert (preserved intact unless some function acts upon them).
- Functions, their arguments are the elements following them immediately in the queue:
+*/-%
: integer arithmetic (-
is unary, %
logical negation). Inert if not given number arguments.
()<
: put element in brackets, remove brackets from block, add final element to block. The latter two are inert unless followed by a block.
~:
: swap, duplicate.
$
: copy (takes number + element). Inert before non-number.
H
: halt program.
Note that while []
nest, ()
don't - the latter are simply separate functions.
Execution trace syntax
Whitespace is optional in Fueue, except between numerals. In the following execution traces it will be used to suggest program structure, in particular:
- When a function executes, it and its arguments will be set off from the surrounding elements with spaces. If some of the arguments are complicated, there may be a space between them as well.
- Many execution traces are divided into a "delay blob" on the left, separated from a part to the right that does the substantial data manipulation. See next section.
Curly brackets {}
(not used in Fueue) are used in the traces to represent the integer result of mathematical expressions. This includes negative numbers, as Fueue has only non-negative literals – -
is the negation function.
Various metavariable names and ...
are used to denote values and abbreviations.
Delaying tactics
Intuitively, execution cycles around the queue, partially modifying what it passes through. The results of a function cannot be acted on again until the next cycle. Different parts of the program effectively evolve in parallel as long as they don't interact.
As a result, a lot of the code is devoted to synchronization, in particular to delaying execution of parts of the program until the right time. There are a lot of options for golfing this, which tends to turn those parts into unreadable blobs that can only be understood by tracing their execution cycle by cycle.
These tactics won't always be individually mentioned in the below:
)[A]
delays A
for a cycle. (Probably the easiest and most readable method.)
~ef
swaps the elements e
and f
which also delays their execution. (Probably the least readable, but often shortest for minor delays.)
$1e
delays a single element e
.
-
and %
are useful for delaying numbers (the latter for 0
and 1
.)
- When delaying several equal elements in a row,
:
or $
can be used to create them from a single one.
(n
wraps n
in brackets, which may later be removed at convenience. This is particularly vital for numeric calculations, since numbers are too unstable to even be copied without first putting them in a block.
Overall structure
The rest of the explanation is divided into seven parts, each for a section of the running program. The larger cycles after which most of them repeat themselves will be called "iterations" to distinguish them from the "cycles" of single passes through the entire queue.
Here is how the initial program is divided between them:
A: )$$4255%%1(~
B: ):[)$$24%%0:<[~:)~)]~[$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]]
C:
D: (H-
E:
F:
G: ):~:[)[):~[)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/]+:5):]~:](106328966328112328136317639696111819119696281563139628116326221310190661962811611211962861109696289611619628116111612896281115421063633063961111116163963011632811111819159628151213262722151522061361613096119619190661966311961128966130281807072220060611612811961019070723232022060611
The big numeral at the end of the program encodes the rest in reverse, two digits per character, with 30 subtracted from each ASCII value (so e.g. 10
encodes a (
.)
On a higher level you can think of the data in this program (starting with the bignum) as flowing from right to left, but control flowing from left to right. However, at a lower level Fueue muddles the distinction between code and data all the time.
- Section G decodes the bignum into ASCII digits (e.g. digit
0
as the integer 48
), splitting off the least significant digits first. It produces one digit every 15 cycles.
- Section F contains the produced digit ASCII values (each inside a block) until section E can consume them.
- Section E handles the produced digits two at a time, pairing them up into blocks of the form
[x[y]]
, also printing the encoded character of each pair.
- Section D consists of a deeply nested block gradually constructed from the
[x[y]]
blocks in such a way that once it contains all digits, it can be run to print all of them, then halt the entire program.
- Section C handles the construction of section D, and also recreates section E.
- Section B recreates section C as well as itself every 30 cycles.
- Section A counts down cycles until the last iteration of the other sections. Then it aborts section B and runs section D.
Section A
Section A handles scheduling the end of the program.
It takes 4258 cycles to reduce to a single swap function ~
, which then makes an adjustment to section B that stops its main loop and starts running section D instead.
)$ $4255% %1 (~
)$%%%...%% %0 [~]
)$%%%...% %1 [~]
⋮
)$ %0 [~]
) $1[~]
)[~]
~
- A
$
function creates 4255 copies of the following %
while the (
wraps the ~
in brackets.
- Each cycle the last
%
is used up to toggle the following number between 0
and 1
.
- When all
%
s are used up, the $1
creates 1 copy of the [~]
(effectively a NOP), and on the next cycle the )
removes the brackets.
Section B
Section B handles regenerating itself as well as a new iteration of section C every 30 cycles.
) : [)$$24%%0:<[~:)~)]~[$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]]
) [)$$24%%0:<[~:)~)]~[$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]] [BkB]
)$ $24% %0 :< [~:)~)] ~ [$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<] [BkB]
)$ %...%%% %1 < < [~:)~)] [BkB] [$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]
)$ %...%% %0 < [~:)~)[BkB]] [$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]
)$ %...% %1 [~:)~)[BkB][$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]]
⋮
) $1 [~:)~)[BkB][$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]]
) [~:)~)[BkB][$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]] (1)
~:) ~)[BkB] [$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]
) : [BkB] ) [$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<] (2)
) [BkB] [BkB] $11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<
- A
:
duplicates the big block following (one copy abbreviated as [BkB]
), then )
removes the brackets from the first copy.
$$24%%0
sets up a countdown similar to the one in section A.
- While this counts down,
:<
turns into <<
, and a ~
swaps two of the blocks, placing the code for a new section C last.
- The two
<
functions pack the two final blocks into the first one - this is redundant in normal iterations, but will allow the ~
from section A to do its job at the end.
- (1) When the countdown is finished, the
)
removes the outer brackets. Next ~:)
turns into ):
and ~)
swaps a )
to the beginning of the section C code.
- (2) Section B is now back at its initial cycle, while a
)
is just about to remove the brackets to start running a new iteration of section C.
In the final iteration, the ~
from section A appears at point (1) above:
~ ) [~:)~)[BkB][$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]] (1)
[~:)~)[BkB][$11~)~<[[+$4--498+*-:~-10)):])<~][)))~]<]] )
The ~
swaps the )
across the block and into section C, preventing section B from being run again.
Section C
Section C handles merging new digit character pairs into section D's block, and also creating new iterations of section E.
The below shows a typical iteration with x
and y
representing the digits' ASCII codes. In the very first iteration, the incoming "D" and "E" elements are the initial [H]
and -
instead, as no previous section E has run to produce any digit character pairs.
C D E
$11~ ) ~<[[+$4--498+*-:~-10)):])<~] [)))~] < [)))~[...]] [x[y]]
~~~ ~~~ ~~~ ~~) [[+$4--498+*-:~-10)):])<~] < [)))~] [)))~[...][x[y]]]
~~~ ~~~ ) ~ [[+$4--498+*-:~-10)):])<~] [)))~[)))~[...][x[y]]]]
~~~ ~ ) [)))~[....]] [[+$4--498+*-:~-10)):])<~]
~~[)))~[....]] )[[+$4--498+*-:~-10)):])<~]
[)))~[....]] ~[+$4--498+*-:~-10)):])<~
- This uses a different method of synchronization which I discovered for this answer. When you have several swap functions
~
in a row, the row will shrink to approximately 2/3 each cycle (because one ~
swaps two following), but occasionally with a remainder of ~
s that wreaks havoc on carefully manipulates what follows.
$11~
produces such a row. The next ~
swaps a <
across the following block. Another <
at the end appends a new digit pair block (digits x and y as ASCII codes) into the section D block.
- Next cycle, the
~
row has a ~~
remainder, which swaps a ~
over the following )
. The other <
appends section D to a [)))~]
block.
- Next the swapped
~
itself swaps the following block with new section E code across the section D block. Then a new leftover ~
swaps a )
across, and finally the last ~~
in the ~
row swap one of them across to section E just as the )
has removed its brackets.
In the final iteration, section A's ~
has swapped a )
across section B and into section C. However, section C is so short-lived that it already has disappeared, and the )
ends up at the beginning of section D.
Section D
Section D handles printing the final big numeral and halting the program. During most of the program run, it is an inert block that sections B–G cooperate on building.
(H -
[H]-
⋮
[)))~[H-]] After one iteration of section C
⋮
[)))~[)))~[H-][49[49]]]] Second iteration, after E has also run
⋮
) [)))~[...]] [49[48]] Final printing starts as ) is swapped in
))) ~[...][49[48]]
)) )[49[48]] [...]
)) 49 [48][...] Print first 1
) )[48] [...]
) 48 [...] Print 0
)[...] Recurse to inner block
...
⋮
)[H-] Innermost block reached
H - Program halts
- In the first cycle of the program, a
(
wraps the halting function H
in brackets. A -
follows, it will be used as a dummy element for the first iteration instead of a digit pair.
- The first real digit pair incorporated is
[49[49]]
, corresponding to the final 11
in the numeral.
- The very last digit pair
[49[48]]
(corresponding to the 10
at the beginning of the numeral) is not actually incorporated into the block, but this makes no difference as )[A[B]]
and )[A][B]
are equivalent, both turning into A[B]
.
After the final iteration, the )
swapped rightwards from section B arrives and the section D block is deblocked. The )))~
at the beginning of each sub-block makes sure that all parts are executed in the right order. Finally the innermost block contains an H
halting the program.
Section E
Section E handles combining pairs of ASCII digits produced by section G, and both prints the corresponding encoded character and sends a block with the combined pair leftwards to sections C and D.
Again the below shows a typical iteration with x
and y
representing the digits' ASCII codes.
E F
~ [+$4--498+*-:~-10)):] ) < ~ [y] [x]
) [+$4--498+*-:~-10)):] < [x] [y]
+ $4- - 498 +*- :~ -10 ) ) : [x[y]]
+--- -{-498} +*- ~~{-10} ) ) [x[y]] [x[y]]
+-- - 498 +* -{-10} ~ ) x [y] [x[y]]
+- -{-498} + * 10 x )[y] [x[y]]
+ - 498 + {10*x} y [x[y]]
+ {-498} {10*x+y} [x[y]]
{10*x+y-498} [x[y]]
[x[y]]
- The incoming digit blocks are swapped, then the y block is appended to the x block, and the whole pair block is copied. One copy will be left until the end for sections C and D.
- The other copy is deblocked again, then a sequence of arithmetic functions are applied to calculate
10*x+y-498
, the ASCII value of the encoded character. 498 = 10*48+48-30
, the 48
s undo the ASCII encoding of x
and y
while the 30
shifts the encoding from 00–99
to 30–129
, which includes all printable ASCII.
- The resulting number is then left to execute, which prints its character.
Section F
Section F consists of inert blocks containing ASCII codes of digits. For most of the program run there will be at most two here, since section E consumes them at the same speed that G produces them with. However, in the final printing phase some redundant 0
digits will collect here.
[y] [x] ...
Section G
Section G handles splitting up the big number at the end of the program, least significant digits first, and sending blocks with their ASCII codes leftward to the other sections.
As it has no halting check, it will actually continue producing 0
digits when the number has whittled down to 0, until section D halts the entire program with the H
function.
[BkG]
abbreviates a copy of the big starting code block, which is used for self-replication to start new iterations.
Initialization in the first cycles:
) :~ : [)[):~[)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/]+:5):]~:] ( 106328966328112328136317639696111819119696281563139628116326221310190661962811611211962861109696289611619628116111612896281115421063633063961111116163963011632811111819159628151213262722151522061361613096119619190661966311961128966130281807072220060611612811961019070723232022060611
) ~ ~ [)[):~[)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/]+:5):]~:] [BkG] [10...11]
) [)[):~[)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/]+:5):]~:] ~ [BkG] [10...11]
) [):~[)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/]+:5):] ~ : [10...11] [BkG]
Typical iteration, N
denotes the number to split:
) [):~[)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/]+:5):] ~ : [N] [BkG]
) :~ [)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/]+ :5 ) : [N] : [BkG]
) ~ ~ [)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/] +5 5 ) [N] [N] [BkG] [BkG]
) [)~:~~([:~)*[):~[$1(+48]):~+]-:~~)10)~~]/] ~ 10 N [N] [BkG] [BkG]
) ~:~ ~ ( [:~)*[):~[$1(+48]):~+]-:~~)10)~~] / N 10 [N] [BkG] [BkG]
) ~ : [:~)*[):~[$1(+48]):~+]-:~~)10)~~] ( {N/10} [N] [BkG] [BkG]
) [:~)*[):~[$1(+48]):~+]-:~~)10)~~] : [{N/10}] [N] [BkG] [BkG]
:~ )*[):~[$1(+48]):~+]- :~ ~)10 ) ~ ~ [{N/10}] [{N/10}] [N] [BkG] [BkG]
~~) *[):~[$1(+48]):~+]- ~~10 ) ) [{N/10}] ~ [{N/10}] [N] [BkG] [BkG]
) ~ * [):~[$1(+48]):~+] -10 ~ ) {N/10} [N] [{N/10}] [BkG] [BkG]
) [):~[$1(+48]):~+] * {-10} {N/10} ) [N] [{N/10}] [BkG] [BkG]
) :~ [$1(+48]) :~ + {-10*(N/10)} N [{N/10}] [BkG] [BkG]
) ~ ~ [$1(+48] ) ~ ~ {N%10} [{N/10}] [BkG] [BkG]
) [$1(+48] ~ ) {N%10} ~ [{N/10}] [BkG] [BkG]
$1( + 48 {N%10} ) [BkG] [{N/10}] [BkG]
( {48+N%10} BkG [{N/10}] [BkG] New iteration starts
[{48+N%10}] ....
- The delay blob here is particularly hairy. However, the only new delaying trick is to use
+:5
instead of --10
to delay a 10
two cycles. Alas only one of the 10
s in the program was helped by this.
- The
[N]
and [BkG]
blocks are duplicated, then one copy of N
is divided by 10
.
[{N/10}]
is duplicated, then more arithmetic functions are used to calculate the ASCII code of the last digit of N
as 48+((-10)*(N/10)+N)
. The block with this ASCII code is left for section F.
- The other copy of
[{N/10}]
gets swapped between the [BkG]
blocks to set up the start of a new iteration.
Bonus quine (540 bytes)
)$$3371%%1[~!~~!)!]):[)$$20%%0[):]~)~~[)$$12%%0[<$$7%~~0):~[+----48+*-~~10))]<]<~!:~)~~[40~[:~))~:~[)~(~~/[+--48):]~10]+30])):]]][)[H]](11(06(06(21(21(25(19(07(07(19(61(96(03(96(96(03(11(03(63(11(28(61(11(06(06(20(18(07(07(18(61(11(28(63(96(11(96(96(61(11(06(06(19(20(07(07(18(61(30(06(06(25(07(96(96(18(11(28(96(61(13(15(15(15(15(22(26(13(12(15(96(96(19(18(11(11(63(30(63(30(96(03(28(96(11(96(96(61(22(18(96(61(28(96(11(11(96(28(96(61(11(96(10(96(96(17(61(13(15(15(22(26(11(28(63(96(19(18(63(13(21(18(63(11(11(28(63(63(63(61(11(61(42(63(63
Try it online!
Since I wasn't sure which method would be shortest, I first tried encoding characters as two-digit numbers separated by (
s. The core code is a bit shorter, but the 50% larger data representation makes up for it. Not as golfed as the other one, as I stopped when I realized it wouldn't beat it. It has one advantage: It doesn't require an implementation with bignum support.
Its overall structure is somewhat similar to the main one. Section G is missing since the data representation fills in section F directly. However, section E must do a similar divmod calculation to reconstruct the digits of the two-digit numbers.