/Ohm

A stack-oriented golfing language inspired by 05AB1E and Jelly.

Primary LanguageRubyMIT LicenseMIT

Ohm Build Status

Ohm is a stack-oriented programming language inspired by 05AB1E and Jelly designed specifically for code golf.

Does tacit programming make no sense to you, but you really wish you could use Jelly's links? If so, Ohm is the language for you.

Interested in Ohm, but don't want to clone the repository? Thanks to Dennis, there is an online interpreter available on TryItOnline.

Programs and Syntax

Check the full list of components for more info!

The Stack

Ohm uses a stack memory model. Think of the stack as a big array that every component of an Ohm circuit interacts with. For example, when the instruction pointer hits a number, it will push that number to the stack and continue, while the + component pops two elements off the stack, adds them, and pushes their result. Thus, the following circuit will output 15.

13 2+

(The numbers have to be separated with a no-op (space, in this case) since Ohm continues parsing number literals until there are no more digits left.)

Blocks

Conditional statements and loops in Ohm are handled by a construct called a block. (If you're familiar with Ruby, it's pretty similar to Ruby's blocks.) They're essentially sections of code that are executed differently depending on what component they're attached to. Some components that use them are ? for if statements, : for for-each loops, for array select/filtering, and for array mapping.

If/Else

If you want to add an else clause to an if statement, just place the ¿ component before the end of the block and put the alternate code in between the two.

Example
0?"code for truthy condition"¿"code for falsy condition";

Auto-Pushing

Most array-looping components like , , and will automatically push the current value to the top of the stack, meaning that _ does not have to be explicitly called to get the value. There is one exception to this: the : (foreach) component. Because of the different use cases for :, the value is not automatically pushed and has to be explicitly pushed via _.

Example
5@€^*;  Creates a new array with every element multiplied by its index
5@:_,;  Prints each element in the array

Implicit Everything

Since Ohm is a golfing language, many things are done implicitly in order to save bytes for the golfer.

  • If there is nothing else in the circuit, concluding components like ; in conditional/loop blocks and " in string literals are inferred and do not have to be explicitly input.
    • One exception to this is that multi-line strings must be terminated. (i.e. "foo¶bar will not work, must use "foo¶bar")
  • If there are not enough items on the stack when a component tries to pop from it, Ohm will push user input to the stack until there are enough.
  • If a circuit has not printed anything once execution completes, the top element of the stack will automatically be printed.

Vectorization

Most components will automatically vectorize, meaning that if you pass in an array(s), it will automatically perform its function over those arguments. For example, passing [1, 2, 3] to the ² component will return [1, 4, 9]. This is more efficient than mapping or using the « component.

Wires

Wires are a way to splinter your code into different functions (similar to links in Jelly). New wires are placed on a separate line (or separated with a pilcrow ), and the top-most wire is always executed as the main wire.

The Ω component will execute the wire below the current one, whereas Θ will execute the one above it, and Φ will pop an element from the stack and execute the wire at that index.

Example

25@Ωτ@ΩΣ
:^²_-

As you can see, it saves bytes by only requiring the : block to be declared once.

Base 255

The B component can handle input bases up to 255. Because of how efficient base 255 is at storing numbers, Ohm comes with a built-in to convert base 255 back to base 10 for use in a circuit. Just surround the base 255 number with . (Here's a base 255 conversion script for your convenience.)

String Compression

Ohm uses a slightly-modified version of the Smaz compression library. Inside Ohm circuits, compressed string literals are delimited by characters. In order to generate a compressed string, use the Ohm::Smaz.compress method or the ·c component in a circuit.

What's New?

The release of Ohm v2 brought a few new changes, namely:

  • Instead of CP437, Ohm now uses its own custom code page.
  • true/false values have been replaced with 0 and 1.
  • Network access is now made possible with ·G, but safe mode will disable it.

Running

The Ohm interpreter is written in Ruby 2.x (tested on >= 2.2.10). The core interpreter relies on RSmaz for string compression and Ruby/GSL for polynomial solving, and the unit tests rely on RSpec, Timecop, and Rake.

NOTE: As Ruby 2.2.x is now out of security maintenance phase, the Ohm interpreter will soon stop supporting the 2.2.x branch and move to 2.3.x for the minimum supported version.

To install dependencies for normal use, run bundle install --without test. To install unit testing dependencies, run bundle install normally.

Tests

To run unit tests, run rake.

Interpreter Options

Flag Usage
-c, --code-page Reads the given file with the Ohm encoding (default UTF-8).
-d, --debug Activates debug mode, which prints the current command and stack at every iteration.
-e, --eval Evaluates the given circuit as Ohm code.
-h, --help Prints usage help.
-l, --length Prints the length of the program.
-s, --safe Activates safe mode, which disables components like ·G that may have unsafe side effects.
-t, --time Shows the time taken to execute (in seconds) after completion.

Troubleshooting

When using the -e flag for executing from the terminal, make sure your terminal is in UTF-8 mode. This can be achieved on Windows with the command chcp 65001. If the terminal is in the incorrect encoding, Ruby will raise an Encoding::CompatibilityError when trying to use non-ASCII characters (in my experience).

TODO

  • ¯\_(ツ)_/¯