Bidiroop

From Esolang
Jump to navigation Jump to search

Bidiroop is an esolang by User:BoundedBeans where objects and classes have a different sort of relationship than usual.

Fundamentals

There are four first-class types in Bidiroop, two specific and two general.

  1. Unbounded integers (positive or negative)
  2. Strings
  3. Classes (describes the characteristics of its instances, which are objects)
  4. Objects (describes the characteristics of classes via returning classes from their methods dynamically)

Classes have methods, which cannot be accessed without an object of the type. All methods return classes, but can take any arguments. Objects have their own instance values, which classes can base off of. This effectively, but not technically, means that objects describe classes. In fact, objects fields cannot even be accessed directly except in their own methods, making objects effectively useless other than to describe classes through their methods. However, to allow functions that don't return classes, functions can be declared in a separate way not pertaining to objects.

Classes may contain:

  • Inner classes (only accessible from the class's methods)
  • Functions (only accessible from the class's methods)
  • Fields (cannot be static but can have default values, only accessible from the class's methods)
  • Methods (accessible from anywhere)

Methods may contain:

  • Local classes
  • Functions
  • Local variables

All three of those are inaccessible outside the method. Functions may contain the same things as methods, but they may be outside of classes. Inside of classes, they are static. Fields and variables can contain any type, including classes. Any type may be a parameter to a function or method, and a function may return any type (methods can only return classes). If a method or function gets passed the wrong type as a parameter, it will throw an error. However, functions can be overloaded to have different parameter types. Methods and functions have access to direct members of any level that contains it, but not any indirect members. For example, say that there is a class named Foo, containing a method named Bar and an inner class named Baz. Baz contains method baz_event and an inner class named Baz_Inner. Bar can make objects of Baz (and also run their methods, since that qualifies as the object accessing it), but not Baz_Inner. Bar can also make objects of Foo. However, baz_event can make objects of Baz_Inner. All functions must have some return value, since they can only be run as part of an expression.

The program begins by executing the function named "main". This language should be garbage collected.

Syntax

This language uses indentation to store things, much like Python (the practical one, not the esolang). Unlike Python, the only valid indentation is two spaces, not more, not less.

Variable, field, class, method, and function names should contain only letters and underscores.

Statements

Import (filename)

This loads all functions and classes and their members into memory from the Bidiroop file.

If (integer)

Runs the indented code only if the integer is not zero.

If (string)

Runs the indented code only if the string is not empty.

Else

Runs the indented code if the previous condition was not met.

While (integer)

Repeats the indented code as long as the integer is not zero

While (string)

Repeats the indented code as long as the string is not empty.

Set (variable name), (expression)

Sets the variable to the expression. If the variable does not exist yet, create it. This can be put outside of functions to make global variables.

Delete (name)

Deletes the variable, function, or class.

Class (name)

Creates a class; the indented code should be made of Set commands (fields), method declarations, class declarations, and function declarations, comments, and nothing else.

Method (name)

Must be inside of a class. Creates a method, with the indented code being the content. After the name, there can optionally be a comma followed by a comma separated list of parameter declarations of the format (type) (name), where type can be "Integer", "String", "Class", "Any", or the name of a class to represent an object.

Function (name)

Creates a function, with the indented code being the content.. After the name, there can optionally be a comma followed by a comma separated list of parameter declarations of the format (type) (name), where type can be "Integer", "String", "Class", "Any", or the name of a class to represent an object.

Print (expression)

Prints a string or an integer.

InputInteger (name)

Inputs an integer into the named variable.

InputString (name)

Inputs a line of input into the named variable as a string.

Return (expression)

Returns a value from a method or function.

Comment (expression)

Ignored. Normally the expression should be a string literal, but it doesn't have to be.

Expressions

Expressions can be made of the following components:

Variable names: Can be any variable the code has access to, or current for the object calling the currently executing method or function.

Integer literals: same as in most other languages.

String literals: Text between double quotes. The following escape sequences are supported: \n for newline, \" for quote, \\ for backslash.

Objects: Goes something like this:

ClassName[my_var: 17 + 8, str: "Hello", x: -10349]. 

The variable names should be replaced with the actual parameter names specified in the class. They can be omitted to leave them at the default specified by the class.

Classes: Typed like this:

<C>("String"). 

The string should be a valid class definition (without the header) (the example is therefore not valid, just demonstrates syntax. It can however be an expression instead. This is pretty much the only way for a method to return a class, although the &* operator could be used to combine classes already specified somewhere. ). When creating a class this way, it is always treated as global. Classes that only exist in an expression and aren't assigned to anything will be deleted, unless they are the return value from a function or method, in which it will check if the function or method was called in an assignment statement, in which it will save the class in the variable.

Operators:

+ - addition
- - subtraction
* - multiplication
/ - division
% - modulo
^ - exponent
() - parenthesis for precedence, required for any more than one operator in an expression
+* - string concatenation (can accept numbers to convert them to strings)
^% - discards the second argument, converts the first to a string
&* - takes two classes, makes a new class with all methods, fields, inner classes, and functions from both. 
If there are duplicates, it discards the one from the second argument
/& - logical NAND
= - 1 if equal, zero if not (also works with strings)
> - 1 if the first is greater than the second, zero if not.
    (less than can be accomplished with (((a = b) /& (a = b)) /& ((a > b) /& (a > b))) /& (((a = b) /& (a = b)) /& ((a > b) /& (a > b)))   )
-/ - string cut off removing the position of the integer and afterwards
/- - string cut off removing everything before the position of the integer
-^ - ignores right, returns length of string

Method calls can be notated as <M>[(Object name)][(Method name)][(comma separated list of parameters)] Such as <M>[my_object][strip_arguments][" get rid of this whitespace", "hello", "hi! "] Function calls are the same, but they have no object name and the <M> is <F> instead.

Tricks

Data-containing objects

Objects can actually be used as containers of data as normal, but it's a bit weird. They will need getter and setter methods. Setter methods are simple, just set the value and return an empty class. Getters are weird, though. To make a getter you'll need to set a global variable to the value of an object field and return an empty class. Then other code can use the value of that global variable, which now contains the value of the object field. You can also use this as a general way to return things from methods that aren't classes.

First-class functions

Functions and methods are not first-class in Bidiroop, but by creating a class containing it from the text of the class, we can effectively emulate that.

Inheritance

Bidiroop does not have class inheritance, but it can be emulated with the &* operator, with the first class being the new content and the second class being the superclass. It will then evaluate to the class that is on the left, except that it extends the class on the right. Even normal overriding rules work if there are duplicate names!

Examples

Hello world

Function main
  Print "Hello, world!"

Calculator

Class OperatorConstruct
  Method a, op
    Return <C>(("Function apply, Integer a, Integer b\n  Return a " +* op) +* " b\nMethod m, Integer a, Integer b\n  <F>[apply][a, b]\n  return <C>(\"\")")
Function main
  InputInteger arga
  InputString operator
  InputInteger argb
  Set o, OperatorConstruct[]
  Set C, <M>[o][a][operator]
  Set oo, C[]
  Set mrun, <M>[oo][m][arga, argb]

Truth-machine

Function main
  InputInteger val
  If val
    Set TheClass, <C>("Method event\n  Set x, 1\n  While x\n    Print \"1\"\n  Return <C>(\"\")")
  Else
    Set TheClass, <C>("Method event\n  Print 0\n  Return <C>(\"\")")
  Set the_object, TheClass[]
  Set mrun, <M>[the_object][event][]

No object orientation:

Function main
  InputInteger val
  If val
    While val
      Print val
  Else
    Print 0

Deadfish interpreter

Set accumulator, 0
Class Deadfish
  Function i
    Set accumulator, accumulator + 1
    Return 0
  Function d
    Set accumulator, accumulator - 1
    Return 0
  Function s
    Set accumulator, accumulator ^ 2
    Return 0
  Function o
    Print accumulator
    Return 0
Function main
  InputString code
  Set counter, 0
  Set countermax, code -^ code
  Set total_class_str, "Method run\n"
  While countermax > counter
    Set piece, code -/ (counter + 1)
    Set piece, piece /- counter
    Set total_class_str, total_class_str +* ("  Set dummy, <F>[" +* (piece +* "][]\n"))
    Set counter, counter + 1
  Set total_class_str, total_class_str +* "Return <C>(\"\")"
  Set ConstructedClass, <C>(total_class_str) &* Deadfish
  Set the_object, ConstructedClass[]
  Set dummy, <M>[the_object][run][]

Brainfuck interpreter

This uses 3-cell unbounded brainfuck to prove Bidiroop Turing-complete, since Bidiroop does not have an array feature. Uses integer I/O rather than character. Code should be given as input, all on one line.

Set cella, 0
Set cellb, 0
Set cellc, 0
Set currentcell, 1
Class Brainfuck
  Function plus
    If currentcell = 1
      Set cella, cella + 1
    If currentcell = 2
      Set cellb, cellb + 1
    If currentcell = 3
      Set cellc, cellc + 1
    Return 0
  Function minus
    If currentcell = 1
      If currentcell > 0
        Set cella, cella - 1
    If currentcell = 2
      If currentcell > 0
        Set cellb, cellb - 1
    If currentcell = 3
      If currentcell > 0
        Set cellc, cellc - 1
    Return 0
  Function left
    If currentcell = 1
      Set currentcell, 3
    Else
      Set currentcell, currentcell - 1
    Return 0
  Function right
    If currentcell = 3
      Set currentcell, 1
    Else
      Set currentcell, currentcell + 1
    Return 0
  Function input
    If currentcell = 1
      InputInteger cella
    If currentcell = 2
      InputInteger cellb
    If currentcell = 3
      InputInteger cellc
    Return 0
  Function output
    If currentcell = 1
      Print cella
    If currentcell = 2
      Print cellb
    If currentcell = 3
      Print cellc
    Return 0
Function main
  InputString code
  Set counter, 0
  Set countermax, code -^ code
  Set total_class_str, "Method run\n"
  Set spacing, 1
  While countermax > counter
    Set piece, code -/ (counter + 1)
    Set piece, piece /- counter
    Set scounter, spacing
    While scounter
      Set total_class_str, total_class_str +* "  "
      Set scounter, scounter - 1
      If piece = ">"
        Set total_class_str, total_class_str +* "Set dummy, <F>[right][]\n"
      If piece = "<"
        Set total_class_str, total_class_str +* "Set dummy, <F>[left][]\n"
      If piece = "+"
        Set total_class_str, total_class_str +* "Set dummy, <F>[plus][]\n"
      If piece = "-"
        Set total_class_str, total_class_str +* "Set dummy, <F>[minus][]\n"
      If piece = "."
        Set total_class_str, total_class_str +* "Set dummy, <F>[output][]\n"
      If piece = ","
        Set total_class_str, total_class_str +* "Set dummy, <F>[input][]\n"
      If piece = "["
        Comment "A really long way of saying 'While ((currentcell = 1) and (cella > 0)) or (((currentcell = 2) and (cellb > 0)) or ((currentcell = 3) and (cellc > 0)))'"
        Set total_class_str, total_class_str +* "While (((currentcell = 1) /& (cella > 0)) /& ((currentcell = 1) /& (cella > 0)) /& ((currentcell = 1) /& (cella > 0)) /& ((currentcell = 1) /& (cella > 0))) /& (((((currentcell = 2) /& (cellb > 0)) /& ((currentcell = 2) /& (cellb > 0)) /& ((currentcell = 2) /& (cellb > 0)) /& ((currentcell = 2) /& (cellb > 0))) /& (((currentcell = 3) /& (cellc > 0)) /& ((currentcell = 3) /& (cellc > 0)) /& ((currentcell = 3) /& (cellc > 0)) /& ((currentcell = 3) /& (cellc > 0)))) /& ((((currentcell = 2) /& (cellb > 0)) /& ((currentcell = 2) /& (cellb > 0)) /& ((currentcell = 2) /& (cellb > 0)) /& ((currentcell = 2) /& (cellb > 0))) /& (((currentcell = 3) /& (cellc > 0)) /& ((currentcell = 3) /& (cellc > 0)) /& ((currentcell = 3) /& (cellc > 0)) /& ((currentcell = 3) /& (cellc > 0)))))\n"
        Set spacing, spacing + 1
      If piece = "]"
        Set spacing, spacing - 1
    Set counter, counter + 1
  Set total_class_str, total_class_str +* "  Return <C>(\"\")"
  Set ConstructedClass, <C>(total_class_str) &* Brainfuck
  Set the_object, ConstructedClass[]
  Set dummy, <M>[the_object][run][]

Self-interpreter

Give the program as input, with the final line containing @END

Function main
  Set code, ""
  Set line, ""
  While (line = "@END") /& (line = "@END")
    Set code, code +* ("  " +* (line +* "\n"))
    InputString line
  Set class_string, "Method __code__"
  Set class_string, class_string +* (code +* "  <F>[main][]\n  Return <C>(\"\")")
  Set ClassedCode, <C>(class_string)
  Set an_object = ClassedCode[]
  Set dummy, <M>[an_object][__code__][]

Data structures

Data structures are hard to make in Bidiroop, but not impossible. The main issue is that objects cannot make their fields available through returning. However, this is still possible by setting a global variable to the object field's value. We'll call this global variable globereturn. This way we can implement a getter. We can implement a setter much easier by passing a parameter, and having the method set the value. For both of these, the actual return value of the method will be an empty class.

Stack

The stack is the base for a lot of other data structures. Here is the stack class:

Class Stack
  Set value, 0
  Set link, 0
  Comment "Stores new stack element with the thing to push as the element and the link being the element you called the method on"
  Comment "You will have to get the value from globereturn"
  Method push, Any thing_to_push
    Set globereturn, Stack[value: thing_to_push, link: current]
    Return <C>("")
  Comment "Returns the linked element; also get the value from globereturn"
  Method pop
    Set globereturn, link
    Return <C>("")
  Method get_value
    Set globereturn, value
    Return <C>("")

Push and pop are the base operations needed. To pop and return the value, you'll need to use get_value, put the value in globereturn somewhere else, then pop. Using just get_value is equivalent to a peek. From these you can form duplicate, swap, roll, etc.

List with pointer

A list with a pointer can be accomplished with two stacks, and a delimiting value for each end. I used @FIRST and @LAST for the delimiting values, but you could use anything.

Class List
  Set stacka, Stack[value: "@FIRST", link: 0]
  Set stackb, Stack[value: "@LAST", link: 0]
  Set pointed, 0
  Comment "left, right, set_value, insert, and remove_pointed modify in place; there isn't any need to pick up the result from globereturn"
  Comment "for get_value and check_bounds you WILL need to get the result from globereturn"
  Method left
    Set dummy, <M>[stackb][push][pointed]
    Set stackb, globereturn
    Set dummy, <M>[stacka][get_value][]
    Set pointed, globereturn
    Set dummy, <M>[stacka][pop][]
    Set stacka, globereturn
    Return <C>("")
  Method right
    Set dummy, <M>[stacka][push][pointed]
    Set stacka, globereturn
    Set dummy, <M>[stackb][get_value][]
    Set pointed, globereturn
    Set dummy, <M>[stackb][pop][]
    Set stackb, globereturn
    Return <C>("")
  Method set_value, Any value_to_set
    Set pointed, value_to_set
    Return <C>("")
  Method get_value
    Set globereturn, pointed
    Return <C>("")
  Method insert, Any value_to_insert
    Set dummy, <M>[stacka][push][value_to_insert]
    Set stacka, globereturn
    Set dummy, <M>[current][left][]
    Return <C>("")
  Method remove_pointed
    Set dummy, <M>[stackb][get_value][]
    Set pointer, globereturn
    Set dummy, <M>[stackb][pop][]
    Set stackb, globereturn
    Return <C>("")
  Comment "Returns 1 if pointer on the border, 0 if not"
  Method check_bounds
    If pointed = "@FIRST"
      Set globereturn, 1
    Else
      If pointed = "@LAST"
        Set globereturn, 1
      Else
        Set globereturn, 0
    Return <C>("")

Now we have everything we need for a list with a pointer. This enables us to make a deque.

Deque

Class Deque
  Set mainlist = List[]
  Comment "remove_first and remove_last also retrieve the value and put it in globereturn. All methods modify in place."
  Method add_first, Any value_to_add
    While <M>[mainlist][check_bounds][] = 0
      Set dummy, <M>[mainlist][left][]
    Set dummy, <M>[mainlist][right][]
    Set dummy, <M>[mainlist][insert][value_to_add]
    Return <C>("")
  Method add_last, Any value_to_add
    While <M>[mainlist][check_bounds][] = 0
      Set dummy, <M>[mainlist][right][]
    Set dummy, <M>[mainlist][insert][value_to_add]
    Return <C>("")
  Method remove_first
    While <M>[mainlist][check_bounds][] = 0
      Set dummy, <M>[mainlist][left][]
    Set dummy, <M>[mainlist][right][]
    Set dummy, <M>[mainlist][get_value][]
    Set deque_local_value_holder, globereturn
    Set dummy, <M>[mainlist][remove_pointed][]
    Set globereturn, deque_local_value_holder
    Return <C>("")
  Method remove_last
    While <M>[mainlist][check_bounds][] = 0
      Set dummy, <M>[mainlist][right][]
    Set dummy, <M>[mainlist][left][]
    Set dummy, <M>[mainlist][get_value][]
    Set deque_local_value_holder, globereturn
    Set dummy, <M>[mainlist][remove_pointed][]
    Set globereturn, deque_local_value_holder
    Return <C>("")
    

Queue

A queue can be implemented simply by using only opposite sides of the deque. Funnily enough, you can actually make a stack from a deque too, making a totally pointless stack made out of two stacks.

Pair

Pair is separate from the rest of them, and is pretty easy.

Class Pair
  Set valuea, 0
  Set valueb, 0
  Method set_a, Any value_to_set
    Set valuea, value_to_set
    Return <C>("")
  Method set_b, Any value_to_set
    Set valueb, value_to_set
    Return <C>("")
  Method get_a
    Set globereturn, valuea
    Return <C>("")
  Method get_b
    Set globereturn, valueb
    Return <C>("")

Dictionary

This only works with strings, but if you added some sort of equals method to a class, you could make another class kind of like this one using different keys. It does not actually hash the values, merely checks every single one of them until it finds it. It uses a list of pairs to accomplish this.

Class Dictionary
  Set mainlist, List[]
  Method set, String k, Any v
    Set key_found, 0
    While <M>[mainlist][check_bounds][] = 0
      Set dummy, <M>[mainlist][get_value][]
      Set dictionary_local_value_holder, globereturn
      Set dummy, <M>[dictionary_local_value_holder][get_a]
      If globereturn = k
        Set dummy, <M>[mainlist][set_value][Pair[k, v]]
        Set key_found, 1
      Set dummy, <M>[mainlist][right][]
    If key_found = 0
      Set dummy, <M>[mainlist][insert][Pair[k, v]]
    Set dummy, <M>[mainlist][left][]
    While <M>[mainlist][check_bounds][] = 0
      Set dummy, <M>[mainlist][left][]
    Set dummy, <M>[mainlist][right][]
    Return <C>("")
  Comment "To check if a key exists, use the get method and check that the value is not '@KEY_NOT_FOUND'"
  Method get, String k
    Set key_found, 0
    While <M>[mainlist][check_bounds][] = 0  
      Set dummy, <M>[mainlist][get_value][]
      Set dictionary_local_value_holder, globereturn
      Set dummy, <M>[dictionary_local_value_holder][get_a]
      If globereturn = k
        Set dummy, <M>[mainlist][get_value][]
        Set key_found, 1
      Set dummy, <M>[mainlist][right][]
    If key_found = 0
      Set globereturn, "@KEY_NOT_FOUND"
    Set dummy, <M>[mainlist][left][]
    While <M>[mainlist][check_bounds][] = 0
      Set dummy, <M>[mainlist][left][]
    Set dummy, <M>[mainlist][right][]
    Return <C>("")
  Method remove, String k
    While <M>[mainlist][check_bounds][] = 0  
      Set dummy, <M>[mainlist][get_value][]
      Set dictionary_local_value_holder, globereturn
      Set dummy, <M>[dictionary_local_value_holder][get_a]
      If globereturn = k
        Set dummy, <M>[mainlist][remove_pointed][]
      Set dummy, <M>[mainlist][right][]
    Set dummy, <M>[mainlist][left][]
    While <M>[mainlist][check_bounds][] = 0
      Set dummy, <M>[mainlist][left][]
    Set dummy, <M>[mainlist][right][]
    Return <C>("")

Summary

We've basically constructed a stack through a singly-linked list concept. We construct a simple ordered pair as well. We can take two stacks to form a list with a pointer. Using that list, we can construct a few algorithms for the list to work like a deque. With a deque, we can have a queue. Also, a list of pairs, combined with certain algorithms, can make a dictionary from strings to other data types. It is also fully possible to combine these to nest them. We can use list objects as the element of another list to create a matrix. Or we can include a stack object as the values of a dictionary to construct a mapping from strings to stacks. Or nest dictionaries, or whatever else. Trees are really the only base data structure we haven't done, but a binary tree could be a ton of nested pairs, and something bigger could be nested dictionaries with the keys as child identifiers, paths encoded as a list of strings.