Pointing

From Esolang
Jump to navigation Jump to search
Pointing
Paradigm(s) functional,imperative
Designed by User:Calculus is fun
Appeared in 2025
Computational class Turing-Complete
Major implementations JavaScript
Influenced by C++

Pointing is a silly language made by Calculus is fun (talk), The basic idea came to me when learning C++ in 2023, what if pointers where the main show?

Memory

The memory of Pointing is an array of arbitrarily sized signed integers, the memory is divide as such:

[<variables pointers>, ROZ, <empty × ∞>]

The read only zero (ROZ) is indexed as 0, and indices increase to the right. the memory is "sparse", and can have missing entries. When writing code, the ROZ behaves like the null pointer of other languages, since it can not have any data stored there, while the ROZ is equivalent to 0, It is to clarify a ROZ pointer check from a normal zero check

Instructions

Operators & variables

Notation Arity Name Description
foo 0 Identifier An identifier for a variable pointer
@foo 0 Identifier location The memory location of foo
$ 1 Read Read memory location, use preceding subexpression as address.
Note that $ @foo is equivalent to foo
+ 2 Addition Adds the 2 preceding subexpressions.
- 2 Subtraction Subtracts the second subexpression from the first subexpression.
_ 1 Negate Negates the preceding subexpression.
Note that _foo is equivalent to - 0 foo
* 2 Multiplication Multiplies the 2 preceding subexpressions.
/ 2 Division Divides the first subexpression by the second, and gets the integer part of the quotient.
Note that unlike MoreMathRPN, * a b is not equivalent to / a / 1 b, in fact the latter will give a division by zero error
% 2 Modulus Divides the first subexpression by the second, and gets the remainder.
¬ 1 Boolean not Boolean nots the preceding subexpression. †
2 Boolean and Boolean ands the 2 preceding subexpressions,
if the first subexpression is falsey†, then the second subexpression is skipped.
2 Boolean or Boolean ors the 2 preceding subexpressions,
if the first subexpression is truthy†, then the second subexpression is skipped.
2 Boolean xor Boolean xors the 2 preceding subexpressions.
~ 1 Bitwise not Bitwise nots the preceding subexpression. ⸸
& 2 Bitwise and Bitwise ands the 2 preceding subexpressions,
if the first subexpression is 0, the second subexpression is skipped. ⸸
| 2 Bitwise or Bitwise ors the 2 preceding subexpressions,
if the first subexpression is -1, the second subexpression is skipped. ⸸
^ 2 Bitwise xor Bitwise xors the 2 preceding subexpressions. ⸸
== 2 Equality Outputs true if the 2 preceding subexpressions evaluate to the same thing, otherwise outputs false
< 2 Less than Outputs true if the first subexpression is smaller than the second, otherwise outputs false
> 2 Greater than Outputs true if the first subexpression is bigger than the second, otherwise outputs false
<= 2 Less than or equal to Outputs false if the first subexpression is bigger than the second, otherwise outputs true
>= 2 Greater than or equal to Outputs false if the first subexpression is smaller than the second, otherwise outputs true
? 3 Ternary condition If the first expression is truthy† evaluates the second and skips the third, otherwise skips the second and evaluates the third.
† Boolean values are nominally -1 and 0 for true and false (taken from FALSE). but for courtesy, any non-zero integer is coerced to true, and 0 and <empty> are coerced to false.
⸸ <empty> is coerced to 0 in bitwise operations

Expressions

Expressions are of the following form:

<expression> := <number> | <global constant> | ["@"] <identifier> | <unary expression> | <binary expression> | <ternary expression> | <function call>
<number> := ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9")+
<global constant> := "true" | "false" | "empty" | "ROZ"
<unary expression> := ("$" | "_" | "¬" | "~")<expression>
<binary expression> := ("+" | "-" | "*" | "/" | "%" | "==" | "<" | ">" | "<=" | ">= | "∧" | "∨" | "⊻" | "&" | "|" | "^")<expression><expression> 
<ternary expression> := "?"<expression><expression><expression>
<function call> := <function name> "(" <expression>* ")"
If 2 conflicting parts are back to back, separated them with a comma or space.

Please notice that expressions are written in Polish notation, this is to decrease the work the compiler/interpreter has to do.

Here's some examples

+*5,3,2 [Infix is 5 * 3 + 2]
+1*5,3 [Same as above]
*+5,3,2 [Infix is (5 + 3) * 2]
$+@p1 [gets variable initialized before p]

Here's some invalid examples

@1 [invalid because 1 isn't an identifier, it's a number, so it doesn't have a unique location]
?==$p5+$x1 [? is missing third argument] 
+5,6,2 [2 is hanging, so it becomes a new expression]

Statements

Expressions are good and all, but most of them don't affect anything. that's where statements come in:

<statement> := <assignment> | <identifierCreation> | <if> | <while> | <other statements>
<block> := "{" <statement>+ "}"
<identifierCreation> := "@" <identifier> "=" <expression>
<assignment> := <expression> "=" <expression>
<if> := "if (" <expression> ")" <block>[<else>]
<else> := "else" (<if> | <block>)
<while> := "while(" <expression> ")" <block>
<other statements> = (<expression> | "break"‡ | "continue"‡ | "return " (<expression> | ";")⁕)
‡ only valid in a while's block
⁕ only valid in a function

Here's some examples: This sets $x to 5 if $x was 3

if (==$x3) {
  x=5
}

This is equivalent to @p = allocate(1)

@p = 1
while (¬ == $p empty) {
  @p = + p 1
}
p = 0

Predefined functions

These functions are always available, here's a table.

Function Description
allocate(size) Finds a contiguous empty patch of the given size, fills the patch with 0s, and returns an orphaned pointer pointing at the first slot, if given 0 or a negative, returns a pointer pointing to the ROZ.
Unless you've set a countably infinite number of address to non-empty values, this command will not fail.
free(pointer, size) sets size number of slots on and after the pointer to empty then deletes pointer, if size is 0 or negative, it only deletes the pointer.
fread(pointer, size, reads) like free, but returns reads*$pointer before freeing the pointer, a common value for reads is 2
inputInt(pointer) pulls an integer from stdin, if a valid number is found, pointer = <input>;, otherwise does the assignment pointer = empty
inputStr(pointer) pulls a string from stdin stopping at and including the newline character, and preforms the following @pointer = allocate(len(<input>)) before assigning the memory values their corresponding character
outputInt(value) sends the characters representing <value> when written in decimal to stdout.
outputChar(value) send the character corresponding to value to a buffer.

Custom functions

Functions are labeled blocks of code, allowing for more sophisticated structures.

<function> := "function " <identifier> "(" <parameter identifiers>* ")" <block>

A function always creates new local variables corresponding to it's arguments, all variables created in a function have free(@var, 0) called at the function's termination

here's a simple example of a function that returns a pointer

function nextAddress(addr) {
  @addr = + addr 1
  return addr [@addr will be deleted, but addr will still exist]
}

here's how you return $val

function doubleXor(i) {
  @result = allocate(1)
  result = ^$i*2$i
  free(@i, 1)
  return fread(@result, 1, 2) [use fread to return $result instead of result]
}

Examples

Assignments

Pointing's assignment isn't like other languages, instead of an identifier getting a value, it's always a memory location being written to, it's a bit tricky to get used to, but it's very powerful!

@p = allocate(3) [creates p]
p = + $p 1 [increments $p]
@p = + p 1 [moves p to next cell]
p = 5 [assigns $p to 5]
+ p 1 = 9 [assigns the cell after $p]

using outputInt() is a great way to learn the differences

@acc=allocate(1)
acc=3
outputInt(@acc) [-1 (it is the first variable)]
outputChar(44) [,]
outputInt(acc) [1 (where allocate found enough empty space)]
outputChar(44) [,]
outputInt($acc) [3 (from line 2)]

Pointer manipulation

You can use variables as meta-variables by assigning another variable's location

@i = allocate(1) [creates a pointer to a 0]
@p = allocate(0) [create a pointer to ROZ]
i = 5 [sets value at pointer to 5]
p = 1 [silently does nothing]
@p = @i [p is pointing at i]
outputInt($$p) [5 (see line 3)]

Hello world

outputChar(72) [H] 
outputChar(101) [e]
outputChar(108) [l]
outputChar(108) [l]
outputChar(111) [o]
outputChar(44) [,]
outputChar(32) [ ]
outputChar(119) [w]
outputChar(111) [o]
outputChar(114) [r]
outputChar(108) [l]
outputChar(100) [d]
outputChar(33) [!]

Cat

@s=allocate(0)
while (true) {
  inputStr(@s)
  [if the string immediately ends]
  if (== $s 10) {
    free(s 1)
    break
  }
  [for each character, output it until you hit a line break]
  while (¬ == $s 10) {
    outputChar($s)
    s = empty
    @s = + s 1
  }
  [output newline]
  outputChar(10)
  [clean up newline]
  free(s 1)
}

Short circuiting

function order(char, val) {
  outputChar($char)
  free(@char, 1)
  return fread(@val, 1, 2)
}

@a = allocate(3)

[These are all the same expression logically, but the arrangement is different]
[The arrangement changes the output because of short circuit evaluation]
a = ∨ order(65, _1) ∧ order(66, 0) order(67, _1)
outputChar(10)
+ a 1 = ∨ ∧ order(66, 0) order(67, _1) order(65, _1)
outputChar(10)
+ a 2 = ∨ ∧ order(67, _1) order(66, 0) order(65, _1)

Linked list

function length(listStart) {
  [$$listStart could contain metadata about the list]
  @len=allocate(1)
  while (¬ == $ + $listStart 1 ROZ) {
    len = + $len 1
    listStart = $ + $listStart 1
  }
  free(@listStart 1)
  return fread(@len, 1, 2)
}

[The comma and space could just be a comma or a space,]
[but Pointing doesn't distinguish commas and spaces]
function pointerToIndex(listStart, index) {
  index = + $index 1
  while (∧ > $ index 0 ¬ == $ + $listStart 1 ROZ) {
    index = - $index 1
    listStart = $ + $listStart 1
  }
  if (== $ index 0) {
    free(@index, 1)
    return listStart
  }
  free(@index, 1)
  free(@listStart, 1)
  return ROZ
}

function insertedElement(listStart, index) {
  @prev=pointerToIndex($listStart, -$index1)
  free(@listStart 1)
  free(@index 1)
  @insert = allocate(2)
  + insert 1 = $ + $prev 1
  + $prev 1 = insert
  free(@prev 1)
  return insert
}

[use G affix so Pointing makes local variables in a function instead of overwriting global ones]
@listG = allocate(2)
[setup 1-element list]
+ listG 1 = allocate(2)
$ + listG 1 = 72
[create a new element in index 1 of the list]
@secondG = insertedElement(listG, 1)
[we now have a list of 2 elements, and we could repeat this indefinitely]
secondG = 3
outputInt($secondG)

brainfuck interpreter

If you have any doubts about turing completeness, here's the proof

function pointerToIndex(listStart, index) {
  index = + $index 1
  while (∧ > $ index 0 ¬ == $ + $listStart 1 ROZ) {
    index = - $index 1
    listStart = $ + $listStart 1
  }
  if (== $ index 0) {
    free(@index, 1)
    return listStart
  }
  free(@index, 1)
  free(@listStart, 1)
  return ROZ
}

function insertedElement(listStart, index) {
  @prev=pointerToIndex($listStart, -$index1)
  free(@listStart, 1)
  free(@index, 1)
  @insert = allocate(2)
  + insert 1 = $ + $prev 1
  + $prev 1 = insert
  free(@prev 1)
  return insert
}

@error = allocate(1)
@pc = allocate(1)
@program = allocate(0)
@len = allocate(1)
len = 1
inputStr(@program)
@pointer = allocate(1)
@memPointer = allocate(0)
@memory = allocate(2)
[setup 1-element list]
+ memory 1 = allocate(2)
@memPointer = $ + memory 1
while (¬ == $ + program $pc 10) {
  if (== $ + program $pc 43) {
    [+]
    memPointer = & + $memPointer 1 255
  } elseif (== $ + program $pc 44) {
    [,]
    @input = allocate(0)
    inputStr(@input)
    memPointer = $input
    while (¬ == $input 10) {
      input = empty
      @input = + input 1
    }
    free(@input 1)
  } elseif (== $ + program $pc 45) {
    [-]
    memPointer = & + $memPointer 255 255
  } elseif (== $ + program $pc 46) {
    [.]
    outputChar($memPointer)
  } elseif (== $ + program $pc 60) {
    [<]
    if (== $pointer 0) {
       @memPointer = insertedElement(memory, 0)
       len = + $len 1
    } else {
       pointer = - $pointer 1
       @memPointer = fread(pointerToIndex(memory, $pointer), 0, 1)
    }
  } elseif (== $ + program $pc 62) {
    [>]
    pointer = + $pointer 1
    if (== $pointer $len) {
       @memPointer = insertedElement(memory, $len)
       len = + $len 1
    } else {
       @memPointer = $ + memPointer 1
    }
  } elseif (== $ + program $pc 91) {
    [open bracket]
    if (== $memPointer 0) {
      @depth = allocate(1)
      depth = 1
      while (∧ > $depth 0 ¬ == $ + program $pc 10) {
        pc = + $pc 1
        if (== $ + program $pc 91) {
          depth = + $depth 1
        } elseif (== $ + program $pc 93) {
          depth = - $depth 1
        }
      }
      free(@depth 1)
    }
  } elseif (== $ + program $pc 93) {
    [close bracket]
    if (¬ == $memPointer 0) {
      @depth = allocate(1)
      depth = 1
      while (∧ > $depth 0 >= $pc 0) {
        pc = - $pc 1
        if (== $ + program $pc 91) {
          depth = - $depth 1
        } elseif (== $ + program $pc 93) {
          depth = + $depth 1
        }
      }
      free(@depth, 1)
    }
  }
  pc = + $pc 1
}

Simple RPN calculator

a stack based calculator with the following commands:

  • 0-9 multiple TOS by 10 and add the corresponding digit
  • ; pushes 0
  • +-*/ does the binary operation you would expect
function push(listStart, element) {
  @next = allocate(2)
  next = $element
  element = empty
  + next 1 = $ + $listStart 1
  + $listStart 1 = next
  listStart = empty
}

function pop(listStart) {
   @first = allocate(1)
   first = $ + $listStart 1
   + $listStart 1 = $ + $first 1
   listStart = empty
   return fread(first, 2, 2)
}

@s = 0
inputStr(@s)
@list = allocate(2)
push(list, 0)
while (¬ == $s 10) {
  if (== $s 42) {
     push(list, * pop(list) pop(list))
  } elseif (== $s 43) {
     push(list, + pop(list) pop(list))
  } elseif (== $s 45) {
     @temp = allocate(1)
     temp = pop(list)
     push(list, - pop(list) $temp)
     free(@temp, 1)
  } elseif (== $s 47) {
     @temp = allocate(1)
     temp = pop(list)
     push(list, / pop(list) $temp)
     free(@temp, 1)
  } elseif (∧ <= 48 $s <= $s 57) {
     push(list, + * 10 pop(list) - $s 48)
  } elseif (== $s 59) {
     push(list, 0)
  }
  s = empty
  @s = + s 1
}
free(@s, 1)
outputInt(pop(list))

Quine

The quine is very long, check it out.

Implementations

JS interpreter by Calculus is fun (talk)