Skip to content
A wicked-powerful text macro language for building binary files.
Branch: master
Clone or download
Latest commit 60bce50 Feb 25, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.idea
example
example_old Start v2 examples Feb 25, 2019
include/t2b
src Init flex+bison Feb 25, 2019
.gitignore
CHANGELOG.md Create CHANGELOG Feb 25, 2019
CMakeLists.txt
LICENSE Create LICENSE Jun 12, 2018
README.md
_config.yml Set theme jekyll-theme-merlot Jun 12, 2018

README.md

t2b

A wicked-powerful text macro language for building binary files. Supports comments, looping, Unicode, variables, conditionals, macros and recursion.

TLDR; Check out example/.

Pre-built binaries are available for Win64 and MacOS64.

Usage

$ t2b <filename> here
$ t2b # Read directly from stdin

t2b always writes to stdout. To output to a file, simply use a greater-than sign (>).

Language

Newlines are solely for the sake of readability; all whitespace is the same.

# This is a comment!
#
# Comments must be on their own line.

# Emit a byte. In DECIMAL.
u8 10

# Emit a byte in hex.
u8 0xa

# Octal
u16 0o777

# And, of course, binary.
u8 0b00001110

# Spit out a signed integer.
i64 25677

# Print a string (no line break)
str hello

# Print with a line break.
strl hello

# Wrap in quotes to capture whitespace.
strl "hello world!"

# Escapes are supported.
str "hello, world!\n"

# Unicode?
str "\u{1234}"

# Print a newline.
endl

# Do something 5 times.
# Indentation is purely for readability.
times 5
    u8 23
    u32 24
    times 10
        # We can nest loops
        str "50 times!!!"
    endtimes
endtimes

# Capture the output of another command.
# Oh, and store it into a variable.
set foo (u8 33)

# Access its value.
set bar (get foo)

# Emit its value 3 times.
times 3 get foo endtimes

# Create a simple macro.
macro emit_twice x
begin
    times 2 (get x)
endmacro

# Call it!
emit_twice 24

Why?

The need for such a program arose when I was working on writing a simple VM. Manually hex-editing files for an ever changing bytecode spec is tedious, error-prone, and most of all - sucky.

Now there's a lightweight way to do just that.

Supported Commands

  • u8...u64 - Emit unsigned integer
  • i8...i64 - Emit signed integer
  • f - Emit float
  • d - Emit double
  • hex - Toggle hex mode on/off (defaults to OFF)
  • str <expr> - Write a string
  • strl <expr> - Write a string AND newline
  • endl - Write a newline
  • not <expr> Boolean NOT a char
  • if <cond> <pred> endif Execute <pred> if <cond> == 1
  • get <expr> - Fetch the global variable named expr
  • set <expr1> <expr2> - Assign the global variable named expr1 to expr2
  • = - Compare two values, return 0 or 1
  • times <count> <pred> endtimes - Execute <pred> <count> times. i is always set to the current iteration's index.
  • macro <name> <param-names...> begin <pred> endmacro - Declare a custom macro named <name>.
  • return - End termination of the current macro. Exits the script if not in a macro.
  • size - Return the size of an item
  • len - Equivalent to C strlen

What's next?

It's now feasible to write a machine code compiler in shell. Hooray. Not sure why you would ever do that to yourself, though.

You can’t perform that action at this time.