Reprog

From Esolang
Jump to navigation Jump to search
Reprog
Paradigm(s) imperative
Designed by User:Orisphera
Appeared in 2024
Dimensions one-dimensional
Computational class Turing complete
Reference implementation Unimplemented
File extension(s) {{{files}}}

Reprog is an esoteric programming language. It was originally intended as a language where you can use different representations of numbers.

Specification

In Reprog, there are the following data types:

  • New in 2.1: base aka al2;
  • Non-nullable number aka al1;
  • Non-nullable number reference aka al1 reference;
  • Nullable number aka al0;
  • Nullable number reference aka al0 reference;
  • Very nullable number aka al-1;
  • New in 1.2: very nullable number reference aka al-1 reference;
  • Infinite ONS digit sequence;
  • Infinite ONS digit sequence reference;
  • Infinite PNS digit sequence;
  • Infinite PNS digit sequence reference;
  • Finite ONS digit sequence;
  • Finite PNS digit sequence;
  • Boolean;
  • Assignment.

In 1.1 and 1.2: Non-nullable numbers are natural numbers. They can't be 0. Nullable numbers are natural numbers and 0. Very nullable numbers are nullable numbers and another value.

In 2.1: aln means a number that's at least n. Nan and -1 are the same.

Infinite digit sequences have a finite number of digits that aren't the smallest possible value. They are stored as finite, but unbounded. Adding the smallest possible value as the highest digit doesn't change infinite sequences, but changes finite sequences. References can be used to modify the value. (New in 1.2: Writes to al-1 references have no effect if the referenced value, new or old, is NaN. However, new returns the value attempted to write there. New in 2.1: Some al0 references may keep it 0 if it's 0 and change to a different number if it's not 0 but 0 is written. Same applies.) You can use a reference as the value it's pointing to. Digit sequence types are not one type each, but an infinite class of types. They differ by the base. Assignment is also a class of types. New in 1.2: Patterns are infinite sets of infinite digit sequences. Only a finite (but unlimited) start can be used to determine if it's there. You can use finite digit sequences as patterns; they check that it's the lowest some digits. You can use operations that operate on booleans on patterns; in this case, both operands should be patterns.

A variable name must start with a letter. If it's uppercase, the variable is a al1 reference. If it's lowercase, it's a al1 reference. Different variable names can't point to the same or otherwise entangled value.

Numbers entered directly are al1 if they start with 0 and al1 otherwise. You can use them as constants.

Operations can be unary or binary. Unary operations are represented as [operand].[operation name]. Binary operations are represented by [operand 0].[operation name]([operand 1]).

If a binary operation operand 0 has the same type as the return value, there's a version you can get by prepending it with =. It has the reference version as the operand 0 type, the original operand 1 type as the operand 1 type, and the corresponding assignment type as the return type. Evaluating it has a side-effect of setting operand 0 to the result.

New in 1.2: If an operation has al1 as argument 0 and return and contains no =, adding * before it gives an operation with (overloaded) same type infinite digit sequence as argument 0 and return. Each ith digit of return is f(i)th digit of argument where f is the original operation. It's reference if argument 0 is reference, but using that for non-injections is likely to lead to undefined behavior.

New in 1.2: You can construct an operation by putting several operations in parentheses separated by periods. You can specify arguments 1 for binary ones by putting it in parentheses after the name. If you don't, it'll use the argument 1 of the constructed operation. (Lambda expressions were originally planned, but are now delayed until 2.2 or later.)


PNS is the numeral system we usually use. ONS is the numeral system like this: 3873 means 3rd thousand, 8th hundred, 7th ten, 3th one, i.e., 2763. Unless stated otherwise, the same numeral system is used as operands and return. OPNS is like ONS, but with PNS digits, i.e., ranging from 1 to the base. Numbers in the program are in PNS even if they're non-nullible.

The operations are as follows (with some overloads split):

  • mov: binary, any operand types with the operand 1 type as the return type. Returns operand 1;
  • min: binary, same type of numbers as operands and the return type. Returns the smaller one. New in 1.2: It can be al-1;
  • max: binary, same type of numbers as operands and the return type. Returns the larger one. New in 1.2: It can be al-1;
  • sum: binary, aln as operand 0, alm as operand 1, aln+m as return. Returns the sum. New in 1.2: Operand 0 can be al-1. New in 2.1: Changed the types and united;
  • dif: binary, any numbers as operands, al1 as the return type. Returns the absolute value of the difference. New in 2.1: Can be any types;
  • def: binary, aln as operand 0, aln+1 as operand 1 and return. Returns operand 0 if possible, operand 1 otherwise;
  • fed: unary, aln as operand, aln-1 as return. Returns the value as a more nullable type;
  • ons: binary, al1 as operand 0, base as operand 1. Infinite ONS digit sequence as return, reference if operand 0 is reference. Returns the ONS representation. New in 1.2: You can use sum of two al1s as operand 1 instead;
  • ons: unary, boolean as operand, finite binary ONS digit sequence as return. Returns one digit, 1 for true and vice versa.
  • pns: binary, al1 as operand 0, base as operand 1. Infinite PNS digit sequence as return, reference if operand 0 is reference. Returns the PNS representation. New in 1.2: You can use sum of two al1s as operand 1 instead;
  • pns: unary, boolean as operand, finite binary PNS digit sequence as return. Returns one digit, 1 for true and vice versa.
  • ops: unary, al1 as operand 0, al1 constant as operand 1. Finite ONS digit sequence as return, reference if operand 0 is reference. Returns the OPNS representation (empty if it's 0);
  • num: unary, infinite digit sequence as operand 0. Number, al1 if it's PNS and al1 if it's ONS, reference if it's reference, as return. Returns the number represented by it;
  • car: binary, infinite digit sequence as operand 0, al1 as operand 1, finite digit sequence as return. Returns lowest (operand 1) digits;
  • cdr: binary, infinite digit sequence as operand 0, al1 as operand 1. Infinite digit sequence as return, reference if operand 0 is reference. Returns the rest (equivalent to *sum);
  • con: binary, digit sequence or pattern as operand 0, finite digit sequence as operand 1, digit sequence like operand 0 as return. Concatenates (analogous to CONS). New in 1.2: it doesn't have to be infinite;
  • fst: binary, digit sequence as operand 0, finite digit sequence as operand 1, al-1 as return. Returns index of lowest digit of lowest occurrence of operand 1 in operand 0. Only in 1.2: Return is reference if operand 0 is reference. New in 1.2: Operand 1 is pattern instead;
  • nol: binary, digit sequence as operand 0, any number as operand 1, al-1 as return. Returns the number of times the last digit of operand 1 is in the end of operand 0. Reference if operand 0 is reference;
  • nla: unary, digit sequence as operand 0, al0 as return. Returns the nol that's not 0, except 0 if it's -1. Reference if operand 0 is reference;
  • pri: binary, al1 as operand 0, prime number constant as operand 1. An al0 as return, reference if operand 0 is reference. Returns the power in which the prime number is in the factorisation of operand 0. New in 1.2: You don't need the parentheses. Deprecated in 1.2; use ons(p).fst(p.ons(p).car(01).not) instead. Removed in 2.1; use ons(p).nol(p) instead;
  • old: unary, assignment as operand. Extracts the old value;
  • new: unary, assignment as operand. Extracts the new value. New in 2.1: reference;
  • rhs: unary, assignment as operand. Extracts the operand 1;
  • eq0: unary, al1 as operand, boolean as return. Returns true if it's 0;
  • ne0: unary, al1 as operand, boolean as return. Returns true if it isn't 0;
  • not: unary, boolean as operand and return. Negates;
  • and: binary, boolean as operands and return;
  • nan: binary, boolean as operands and return. NAND;
  • orr: binary, boolean as operands and return. OR;
  • nor: binary, boolean as operands and return;
  • xor: binary, boolean as operands and return;
  • xan: binary, boolean as operands and return. XNOR;

The code consists of expressions and special operators ? and !. The special operators consist of the corresponding character followed by a name. Each name can only appear once as !. It must if it appears as ?. The latter can only be after a boolean expression. It jumps to the ! if it's true.

Races

If the same value is accessed and modified and/or modified several times in the same instruction, the order is undefined. However, the result is as if each access was atomic and the operations that don't assign were lazy. (Some optimisations may make that actually not true, but they shouldn't change the result.)

Examples

A simple program:

I.=min(2)
O.=mov(I)

This sets I to 2 if it's less than 2 and sets O to the new I. You can also do this with one line.

A more complicated program in the original version:

O.=mov(I)
! l
I.pri(3).=sum(I.pri(2).=mov(0).old)
I.ons(2).=cdr(I.ons(2).fst(2.ons(2).car(01)).def(0))
O.=min(I).old.dif(I).ne0
? l

This is intended to be an over-engineered version of the same. However, it may have a different behavior.

A similar program in 1.2:

s.=mov(0)
s.pns(2).*(pns(2).cons(0.pns(2).car(0))).=mov(I.fed.pns)
! outer
I.ons(3).fst(3.ons(3).car(01).not).=sum(I.ons(2).fst(2.ons(2).car(01).not).=mov(0).old.def(0))
I.ons(2).=cdr(I.ons(2).fst(2.ons(2).car(01)).def(0))
! inner
s.pns(2).*(pns(2).cons(0.pns(2).car(0))).num.dif(I.fed).eq0
? end
s.pns(2).*(pns(2).cons(0.pns(2).car(0))).num.def(I).min(I).dif(I).eq0
? stash
t.=mov(s)
s.pns(2).*(pns(2).cons(0.pns(2).car(01))).=mov(t.pns(2))
0.eq0
? inner
! stash
t.=mov(s)
s.pns(2).*(pns(2).cons(0.pns(2).car(01))).=mov(t.pns(2))
s.pns(2).*(pns(2).cons(0.pns(2).car(0))).=mov(I.fed.pns(2))
0.eq0
? outer
! end
s.=mov(0)
t.=mov(0)

This is intended to be an over-engineered version of

I.=min(2)
s.=mov(0)
t.=mov(0)

However, just like above, it may have a different behavior. However, Orisphera considers it less likely to loop forever for some inputs.

A similar program in 2.1:

s.=mov(0)
s.pns(2.def(2)).*(pns(2.def(2)).cons(0.pns(2.def(2)).car(0))).=mov(I.fed.pns)
! outer
I.ons(3.def(3)).fsn(3).=sum(I.ons(2.def(2)).fsn(2).=mov(0).old.def(0))
I.ons(2.def(2)).=cdr(I.ons(2.def(2)).fsn(2).def(0))
! inner
s.pns(2.def(2)).*(pns(2.def(2)).cons(0.pns(2.def(2)).car(0))).num.dif(I.fed).eq0
? end
s.pns(2.def(2)).*(pns(2.def(2)).cons(0.pns(2.def(2)).car(0))).num.def(I).min(I).dif(I).eq0
? stash
t.=mov(s)
s.pns(2.def(2)).*(pns(2.def(2)).cons(0.pns(2.def(2)).car(01))).=mov(t.pns(2.def(2)))
0.eq0
? inner
! stash
t.=mov(s)
s.pns(2.def(2)).*(pns(2.def(2)).cons(0.pns(2.def(2)).car(01))).=mov(t.pns(2.def(2)))
s.pns(2.def(2)).*(pns(2.def(2)).cons(0.pns(2.def(2)).car(0))).=mov(I.fed.pns(2.def(2)))
0.eq0
? outer
! end
s.=mov(0)
t.=mov(0)

This is also intended to be an over-engineered version of

I.=min(2)
s.=mov(0)
t.=mov(0)

However, just like above, it may have a different behavior.