BigINTERCAL is an esoteric programming language that I made inspired by INTERCAL and bitch.
The biggest change from INTERCAL is that, whereas INTERCAL has onespot variables that are 16 bits and twospot variables that are 32 bits, BigINTERCAL has infinityspot variables that are an infinite number of bits (at least, until your computer runs out of memory). The downside is that once I made the first infinityspot variable, I couldn't fit in any other variables, not even the onespot or twospot variables from INTERCAL. Yeah, I could move things around, maybe move all the bits to just the evennumbered bits, but Hilbert's hotel tried that and they're never going to recover from the infinite number of onestar reviews (★
) they got, so I decided it just wasn't worth it. Since there's only one variable, it doesn't need a number, so naturally it's notated with two numbers: 1\1
.
There are also a number of other changes from INTERCAL, and none of the later extensions are implemented except for COME FROM
.
The following are all the valid expressions in BigINTERCAL. Anything else is not a valid expression. For instance, 🙄 is not a valid expression in BigINTERCAL.
Like in INTERCAL, there is no precedence; ambiguous expressions must be surrounded by sparks ('
… '
) or rabbit ears ("
… "
).
Constants are written in decimal and preceded by a mesh (#
). Unlike in INTERCAL, there is no restriction on the range numbers can have.
Please note that BigINTERCAL is not a confusing enough situation to confuse the mesh with the interleave operator.
Variable is written with an infinityspot, otherwise known as a spike (
), followed by a fraction. This fraction is written as two numbers separated by a slat (/
). The fraction must be in reduced form, and must have an odd denominator. Negative fractions can also be used; these are indicated by replacing the slat with a backslat (\
). The fraction is interpreted as a 2adic number, and the bits from the variable corresponding to the 1 bits in the fraction are selected. That is, something like 1\3
is equivalent to 1\1~#1\3
, or would be if fractions were allowed in select expressions, and selects every other bit (including the leastsignificant bit). (See the wisdom tooth for more information.)
Just like INTERCAL allows a spark and a spot to combine to form a wow (!
), BigINTERCAL allows a spark and a spike to combine to form a spoke (¦
).
BigINTERCAL has the same three unary operators as INTERCAL: and (&
), or (V
), and xor (∀
). Unary operators are placed directly after the mesh of a constant, the spike of a variable, or the opening spark or rabbit ears of a grouping expression. All of these operators perform the specified bitwise operator on consecutive bits of a number: the least significant bit of the result is the least significant bit of the input combined with the second least significant, the second least significant is the second least significant combined with the third least significant, and so on. The most significant bit of the result is the most significant bit of the input combined with the least significant bit of the input, but that bit is way off at infinity where the computer can't see it, so it doesn't matter.
The xor operator is normally represented by a bookworm (∀
), which should not be an issue because we have Unicode now and so there are never going to be any character encoding issues ever again. However, some people may find it inconvenient to type, so the bookworm can be approximated with the why money sign (¥
). Furthermore, the why money sign and the backslat (\
) are basically just two different ways of writing the same character, so BigINTERCAL treats them as equivalent. Note that this means that the xor of the variable can be written as \1\1
.
The interleave operator, represented by a change (¢
), rearranges the bits of its operands such that the least significant bit of the result in the least significant bit of the last operand, then the least significant bit of the other operands in reverse order, then the second least significant bit of each operand, and so on. In INTERCAL, interleave was a binary operator; however, BigINTERCAL can count much higher than two so it allows any number of operands. (It's still binary in the sense that it operates on bits, though, regardless of how many operands it has.)
If you don't like change, you can type big money ($
) instead, but since dollars and cents aren't the same thing, the next integer in the program (generally either a literal or the numerator of a fraction) must have a spot (.
) before its last two digits.
Select is a binary operator, represented by a sqiggle (~
). It selects all the bits in its first operand that correspond to one bits in its second operand, and then shifts them over so they're all together in the least significant part of the number. Aside from the fact that this works on bigints, it's exactly the same as in INTERCAL. How boring.
Do note that using a squiggle (ũ
) instead of a sqiggle (~
) is a syntax error.
A BigINTERCAL program is a sequence of statements. Each statement starts with a statement identifier and optionally some qualifiers, as follows:
(
… )
). Labels must be unique within a program. Labels in the range 10001999 should be avoided, because these are used by the system library.
DO
, PLEASE
, or PLEASE DO
. No whitespace is allowed within a statement identifier (other than between PLEASE
and DO
); because the exception proves the rule, whitespace is allowed absolutely anywhere else, including within a number or inside any other word. Because BigINTERCAL is doing a bigger job than INTERCAL, at least 1/3 of all statement identifiers must include PLEASE
(but no more than 2/3).
NOT
or N'T
, which means that the statement won't run unless it's REINSTATE
d.
%
). Because this is BigINTERCAL, big probabilities (or rather, precise probabilities) are allowed; instead of the number being out of 100, it's out of 10^{number of digits in the number}. If you want an actual perecentage that's less than ten, you can use the tripleohseven (‰
) instead (which is equivalent to a doubleohseven followed by an oh (0
)).
These must be in the order shown, except that NOT
and %
can be in either order if both are included.
The most basic statement is calculate, which is just the statement identifier and an expression. Because there's only one variable, the destination variable doesn't need to be (and can't be) specified.
STASH
and RETRIEVE
Like INTERCAL, BigINTERCAL lets you stash a variable (pushing it onto a stack) and retrieve it later. Since there's only one variable, the variable doesn't need to be (can't be) specified; just use DO STASH
or DO RETRIEVE
.
IGNORE
and REMEMBER
IGNORE
prevents some bits of the variable from changing. The word IGNORE
should be followed by an expression; bits in the variable corresponding to 1 bits in the expression are prevented from changing. (E.g., IGNORE #3
makes it so that the two least significant bits can't change.) If some bits are already being IGNORE
d, those bits stay ignored.
REMEMBER expression
causes the bits that are 1 in expression to no longer be ignored.
RETRIEVE
acts like an assignment with regards to ignored bits.
NEXT
, RESUME
, and FORGET
I put too big of an integer into FORGET
and it forgot the entire NEXT
stack, so these commands are not in the language. Besides, COME FROM
makes them pretty much obsolete, and NEXT
is basically the same as call
in Intel assembly language, so it's not that weird anymore.
COME FROM
COME FROM
has the form DO COME FROM (label)
. After the statement (label)
is executed (or skipped due to NOT
/ABSTAIN
/randomness), control will transfer to the COME FROM
statement. If (label)
doesn't exist, this is a noop (this is to make using the system library easier).
Since NEXT
doesn't exist in BigINTERCAL, a new form of COME FROM
was added to make subroutines easier: DO COME FROM (label) AFTER (label)
. All COME FROM
statements without an AFTER
start out active, and all COME FROM AFTER
statements start out inactive. If there's a statement DO COME FROM (x) AFTER (y)
, then after (y)
runs or is skipped, that COME FROM
will become active and any other COME FROM
statements with the same label will become inactive. This means that you can make a subroutine called in multiple places like this:
DO STUFF...
(1) DO SOMETHING
PLEASE COME FROM (3) AFTER (1)
DO MORE STUFF...
(2) DO SOMETHING ELSE
PLEASE COME FROM (3) AFTER (2)
DO MORE STUFF...
PLEASE COME FROM (1)
PLEASE COME FROM (2)
DO THE SUBROUTINE HERE...
(3) DO THE LAST LINE OF THE SUBROUTINE
If more than one active, nonabstained COME FROM
refers to the same line, it's a runtime error if that line ever gets executed. Do note that line labels must be unique, which means that subroutines used in multiple places have to have multiple COME FROM
s at the beginning.
ABSTAIN FROM
and REINSTATE
ABSTAIN FROM
causes some statements not to execute, same as if they had the NOT
qualifier. REINSTATE
undoes the effect of ABSTAIN FROM
or NOT
. Both of these can take a label or an intersection (+
)separated list of gerunds (e.g. CALCULATING
, STASHING
, etc.). This works the same as in INTERCAL, except that there's no restriction on abstaining from or reinstating GIVING UP
(or lines that give up), and you can abstain from or reinstate all syntax errors using the gerund SPLATTING
.
There is also a new form of abstaining: DO ABSTAIN FROM expression pluralgerund
. This evaluates the expression, and uses that to determine how many statements to skip; for instance, DO ABSTAIN FROM #3 CALCULATINGS
abstains from the next three calculate statements that would have been executed. Statements that would already have been skipped due to NOT
or another abstain don't count towards the number skipped (although statements skipped due to randomness do). If a statement type has already been numberabstained, numberabstaining it again adds to the number of times it'll be abstained from, e.g., DO ABSTAIN FROM #2 STASHINGS PLEASE ABSTAIN FROM #3 STASHINGS
is the same as DO ABSTAIN FROM #5 STASHINGS
.
DO REINSTATE expression pluralgerund
does the same thing in reverse, only affecting statements that are already abstained from. If a gerund is numberabstained and numberreinstated at the same time, the numberabstain only affects statements that aren't normalabstained and vice versa; they don't cancel each other out. There is no way to cancel a numbered abstain or reinstate.
For plural gerunds with multiple words, the plural goes on the first word, so COMINGS FROM
and GIVINGS UP
, not *COMING FROMS
or *GIVING UPS
.
For COME FROM
statements, coming from the specified line and encountering the statement normally (regardless of whether it's active) count towards numbered abstains and reinstates. Abstaining is separate from COME FROM
activeness; reinstating an inactive COME FROM
will not make it active.
READ OUT
DO READ OUT expression
outputs the value of expression. Multiple expressions can be given, separated by intersections (+
); in that case, the values are read out on the same line, separated by spaces.
In INTERCAL, numeric output used Roman numerals. However, Roman numerals don't extend well to arbitrary bigints, so BigINTERCAL upgrades to positional notation, but using the same alphabet (or rather, its modern version). Numbers are output in bijective base 26, where 1 is A
, 2 is B
, … 26 is Z
, 27 is AA
. This means that you can also output words; just take each letter in the word, convert it to a number, and add them all together, multiplying by 26 before each addition, so e.g. "BIG" would be (2 × 26 + 9) × 26 + 7 = #1593
, a fairly big number. ("SMALL" would be #8912032
, which is pretty small compared to infinity.)
Unlike INTERCAL, BigINTERCAL! lets "you" use punctuation‽ Before any expression, put WITH symbol name
, where symbol name is any symbol from the Tonsil in the INTERCAL reference manual, or any symbol name mentioned here, with any worms (
) in the name removed. (The word "worm" doesn't have to be removed, though.) The symbol name lasts until the end of the statement, so DO READ OUT WITH TAIL #1 + #2 + #3
reads out "A, B, C,"; you can override this by preceding an expression with WITH NOTHING
. You can't output multiple punctuation marks for the same number, but some precombined punctuation marks exist, like rabbit ears ("
) + spot (.
) = RABBIT
, or wow (!
) + what (?
) = HOW
.
You can also use this to overstrike each character output with a punctuation mark (but you can't have both an overstrike and a punctuation); just append an S
or ES
to the name of the punctuation mark. For instance, WITH FLATWORMS
will underline the word (WITH CRAWLING HORRORS
is also acceptable for CLCINTERCAL compatibility). WITH NOTHINGS
will put all digits of the number on top of each other, for reasons that should be obvious (but probably aren't actually obvious).
WRITE IN
PLEASE WRITE IN variable
inputs a value from the user. Multiple variables can't be given, separated by intersections.
Since there's only one regular variable, writing into it would clobber the program's entire state, so that's not allowed. Rather, the variable you write into has to be one of those variables starting with #
—you know, #1
, #2
, etc. That is, once you DO WRITE IN #3
, every #3
in the program from then on will refer to the input, rather than to the literal number 3. This includes the #3
in DO WRITE IN #3
, so if you want to input in a loop, you'll have to refer to the number indirectly, such as DO WRITE IN #1 ¢ #1
.
(Using number literals as variables has precedent, since I think some INTERCAL dialects allow literal overriding and I have my own older esolang based around it. This is okay, though; BigINTERCAL has a goal of avoiding precedents, which means that I can't have a goal of avoiding precedents since INTERCAL is a precedent for that, so I don't have a goal of avoiding precedents and therefore this precedent is okay.)
In INTERCAL, numeric input used spelledout digits—that is, a number was a sequence of words ZERO
(or OH
), ONE
, TWO
, THREE
, FOUR
, FIVE
, SIX
, SEVEN
, EIGHT
, and NINE
(or NINER
), separated by spaces. However, spelledout digits do extend well to arbitrary bigints, so BigINTERCAL uses them unchanged.
In addition to big integers, BigINTERCAL can also handle big characters, and by big characters I mean Unicode, and by Unicode I mean emoji. (I mean, I think there are maybe also some other characters in Unicode, but who cares about those?)
To use emoji, use the ℛℰ𝒜𝒟 𝒪𝒰𝒯
and 𝒲ℛℐ𝒯ℰ ℐ𝒩
statements. These have the same syntax as READ OUT
and WRITE IN
, respectively, but they interpret the number read out or written into the variable differently. In addition, ℛℰ𝒜𝒟 𝒪𝒰𝒯
does not automatically output any spaces or newlines (but it does still accept punctuation).
Both of these commands use the same format, which is pretty simple: the number n represents the emoji with code n + 65536. Most emoji have codes above 65536, so this should make things easier. For instance, 🙀 (U+1F640) is #63040
, 🙈 (U+1F648) is #63048
, and 😇 (U+1F607) is #62983
. Some emoji are made by combining other emoji with a zero width joiner emoji in between them; to use those, put WITH ZWIDGE
on the first emoji, and make sure to cancel it out with WITH NOTHING
on the second emoji; for instance, 👩💻 (woman technologist, 👩 + 💻) is WITH ZWIDGE #62569 + WITH NOTHING #62651
.
Occasionally, you might want to use emoji with emoji codes below 65536. To do that, you need to output two numbers, in reverse–UTF16 encoding. The first number should be in the range #55296
through #56319
(U+1D800 through U+1DBFF), and the second #56320
through #57343
(U+1DC00 through U+1DFFF). The bottom ten bits of each number is selected and then concatenated together, and that's used as an emoji code. If the emoji code is over 65536, or if there's any other encoding error, a sign (�) will be output instead.
(But what if you actually want to output emoji between U+1D800 and U+1DFFF? Well, the emoji codes U+D800 through U+DFFF aren't being used, so output them instead.)
ℛℰ𝒜𝒟 𝒪𝒰𝒯
and 𝒲ℛℐ𝒯ℰ ℐ𝒩
use the same gerunds as READ OUT
and WRITE IN
; abstaining by gerund abstains both versions of the statement.
GIVE UP
Exits the program. Running to the end of the program without giving up is an error.
The system library is a library that contains subroutines that may occasionally be useful. The system library contains COME FROM
statements from labels in the range (1000)
(1499)
; to use the system library, use one of these labels in your code, followed by a COME FROM
statement for the corresponding label in the (1500)
(1999)
range. (This should be a COME FROM AFTER
, since the system library uses its own subroutines internally.)
The system library will be auto–sufficientlyadvancedtechnologically included at the end of the program if it contains a COME FROM
statement with a label in the range (1500)
(1999)
, but no statements with labels in that range. Checking "Native syslib" will instead include a native library with the same interface (but only if the system library already would have been included).
Each subroutine takes one extra parameter, called the stash, which is passed through unchanged. This can be a useful place to put the rest of the program's state.
The system library routines are as follows:
Subroutine  Entry point  Exit point  Input  Output  Notes 

Add  (1000)  (1500)  stash ¢ x ¢ y  2\3 = stash, 1\3 = x + y  
Subtract  (1010)  (1510)  stash ¢ x ¢ y  4\7 = stash, 2\7 = (x − y) max 0, 1\7 = (y − x) max 0  Also useful for comparison 
Decrement  (1011)  (1511)  stash ¢ x  2\3 = stash, 1\3 = x − 1  Error if x is 0 
Multiply  (1020)  (1520)  stash ¢ x ¢ y  2\3 = stash, 1\3 = x × y  
Divide/modulo  (1030)  (1530)  stash ¢ x ¢ y  4\7 = stash, 2\7 = x ÷ y, 1\7 = x mod y  Error if y is 0 
Please do note that the system library assumes that none of its statements are abstained by the main program, that no bits have been ignored, and that the constants #0
, #1
, and #3
haven't been overwritten.
PLEASE
or PLEASE DO
as their statement identifier (must be at least 1/3 of all statements).
PLEASE
or PLEASE DO
as their statement identifier (must be at most 2/3 of all statements).
ABSTAIN FROM
or REINSTATE
doesn't refer to any existing statement.
(0)
as a label.

has an even denominator. This is not allowed.
RETRIEVE
was attempted when the stash stack was empty.
COME FROM
s refer to the same label.
GIVING UP
.

) symbol.
BigINTERCAL is not compatible with INTERCAL. If you find a nontrivial program that works in both INTERCAL and BigINTERCAL, please report it as a bug.
(The INTERCAL reference manual had a tonsil, so this needs yet another removable organ.)
2adic numbers are a type of number where, instead of digits continuing infinitely to the right of the decimal point, they continue infinitely to the left instead (also it has to be in base 2). This makes them useful for selecting an infinite number of bits from a number. In BigINTERCAL, only rational 2adic numbers can be used, which means that the digits of every number eventually form a repeating pattern. This section will focus on how to create a fraction with the bits you want; more information about padic numbers in general can be found elsewhere.
n/1
is the same as 1\1 ~ #n
.)
1\1
(−1/1) selects the entire variable.)
If the variable is…  Then… 

x ¢ y  x = 2\3 , y = 1\3

x ¢ y ¢ z  x = 4\7 , y = 2\7 , z = 1\7

x ¢ y ¢ z ¢ w  x = 8\15 , y = 4\15 , z = 2\15 , w = 1\15

a_{0} ¢ … ¢ a_{n−1}  a_{k} =  2^{n−1−k}\ (2^{n} − 1)

Showing that all of these are consistent with each other is left as an exercise to the reader. Due . Don't forget.
Keep in mind that bits that are selected will stay in the order they're already in, so for instance, if the variable is x ¢ y
, 7\15
(= −2/15 + −1/3) will select every other bit of x and every bit of y, they'll be arranged as …xyxxyxxyx, which is different than if you did 2\15 ¢ 1\3
.
(Oh, Grammar, what big teeth you have!)
Preprocessing: the following replacements are made before parsing:
Find  Replace 

a ⋮ z  A ⋮ Z

¥  \

¦  '

‰  %0

DO  DO (single token)

PLEASE  PLEASE (single token)

whitespace  ignored (except within DO or PLEASE )

In the following grammar, any word other than PLEASE
or DO
means a sequence of those characters, possibly with whitespace in between.
int  →  digit+ 
int$  →  digit* . digit digit

unary  →  &

  V
 
  ∀  \
 
primary  →  # unary? int

   unary? int (/  \ ) int
 
  ' unary? expr '
 
  " unary? expr "
 
primary$  →  # unary? int$

   unary? int$ (/  \ ) int
 
  ' unary? expr$ '
 
  " unary? expr$ "
 
expr  →  primary 
  primary (¢ primary  $ primary$)+
 
  primary ~ primary
 
expr$  →  primary$ 
  primary$ (¢ primary  $ primary$)+
 
  primary$ ~ primary
 
stmtstart  →  (( int ) )?

(PLEASE  DO  PLEASE DO )
 
((NOT  N'T ) (% digit+)?  % digit+ (NOT  N'T )?)?
 
stmtbody  →  expr 
  STASH  RETRIEVE
 
  (IGNORE  REMEMBER ) expr
 
  COMEFROM ( int ) (AFTER ( int ) )?
 
  (ABSTAINFROM  REINSTATE ) ( int )
 
  (ABSTAINFROM  REINSTATE ) gerund (+ gerund)*
 
  (ABSTAINFROM  REINSTATE ) expr pluralgerund
 
  (READOUT  ℛℰ𝒜𝒟𝒪𝒰𝒯 ) readclause (+ readclause)*
 
  (WRITEIN  𝒲ℛℐ𝒯ℰℐ𝒩 ) expr
 
  GIVEUP
 
  anycharacter* (until the next stmtstart or endoffile)  
gerund  →  CALCULATING

  STASHING
 
  RETRIEVING
 
  IGNORING
 
  REMEMBERING
 
  COMINGFROM
 
  ABSTAINING
 
  REINSTATING
 
  READINGOUT
 
  WRITINGIN
 
  GIVINGUP
 
  SPLATTING
 
pluralgerund  →  CALCULATINGS

  STASHINGS
 
  RETRIEVINGS
 
  IGNORINGS
 
  REMEMBERINGS
 
  COMINGSFROM
 
  ABSTAININGS
 
  REINSTATINGS
 
  READINGSOUT
 
  WRITINGSIN
 
  GIVINGSUP
 
  SPLATTINGS
 
readclause  →  (WITH charname (S  ES )?)? expr

stmt  →  stmtstart stmtbody 
program  →  stmt* endoffile 
Input: