DefLang

From Esolang
Jump to navigation Jump to search

DefLang is designed by PSTF.

It is derived from Brainfuck.

Specification

DefLang program consist a header and a script, where:

  • Header contains all the command you designed, and
  • Script is the main program.

Syntax

To define a command, you use this syntax: command_character@«argument_list» = implementation

The blue part will be implemented in future.

Command character can be ANY character that is not a built-in commands. This is the built-in command table:

Command Description
> Move the pointer to the right
< Move the pointer to the left
+ Increment the memory cell at the pointer
- Decrement the memory cell at the pointer
. Output the character signified by the cell at the pointer
, Input a character and store it in the cell at the pointer
[ Jump past the matching ] if the cell at the pointer is 0
] Jump back to the matching [ if the cell at the pointer is nonzero
{ Jump past the matching } anyway, ignore everything in it
} Does nothing
( Jump past the matching ) if the cell at the pointer is 0
) Does nothing
: Output the current cell data directly in decimal
; Input a decimal ending by a blank character and store it in the cell at the pointer
$ Does nothing, or as a command seperator
@ Switch from the tape to accumulator or back
* Multiply the current cell with accumulator
` Divide the current cell by accumulator, and is rounded toward 0
% Divide the current cell by accumulator, and results in remainder
/ Assign accumulator with current cell
\ Assign current cell with accumulator

Command expansion

The max expansion depth of command should stated in YOUR interpreter, and I recommend you to use a value between 1000 to 10000. If the current expansion depth is over the limit, then a Runtime Error is throwed out. This is to prevent from Infinity Recursion.

Execution

The infinite tape and the accumulator is first initialized to 0. Input is read from STDIN, and output is wrote to STDOUT.

Note

The current interpreter does not support instructions with arguments.

Interpreter in Python

from collections import defaultdict

def run_deflang(program, input_str=""):
    # Split into header and script
    header, _, script = program.partition("_________")
    
    # Parse header definitions
    defs = {}
    for line in header.splitlines():
        line = line.strip()
        if not line or '=' not in line:
            continue
        cmd, _, impl = line.partition('=')
        cmd = cmd.strip()
        impl = impl.strip()
        if len(cmd) == 1:
            defs[cmd] = impl
    
    # Expand script recursively
    expanded = expand_commands(script, defs)
    # Run as Brainfuck
    run_brainfuck(expanded, input_str)

MAX_DEPTH = 1000

def expand_commands(s, defs, depth=0):
    if depth > MAX_DEPTH:
        raise RuntimeError("Max recursion depth exceeded")
    res = []
    for c in s:
        if c in defs:
            res.append(expand_commands(defs[c], defs, depth + 1))
        else:
            res.append(c)
    return ''.join(res)

def run_brainfuck(code, input_str):
    tape = defaultdict(int)
    ptr = 0
    acc = 0
    pc = 0
    type = 0
    input_index = 0
    loop_map = build_loop_map(code)
    cond_map = build_cond_map(code)
    comment_map = build_comment_map(code)
    
    while pc < len(code):
        c = code[pc]
        if c == '>': ptr += 1
        elif c == '<': ptr -= 1
        elif c == '+':
            if type == 0:
                tape[ptr] = (tape[ptr] + 1)
            else: acc += 1
        elif c == '-':
            if type == 0:
                tape[ptr] = (tape[ptr] - 1)
            else: acc -= 1
        elif c == '@': type = (type + 1) % 2
        elif c == '.':
            print(chr(tape[ptr]), end='')
        elif c == ',':
            tape[ptr] = ord(input_str[input_index]) if input_index < len(input_str) else 0
            input_index += 1
        elif c == '[' and tape[ptr] == 0:
            pc = loop_map[pc]
        elif c == ']' and tape[ptr] != 0:
            pc = loop_map[pc]
        elif c == '(' and tape[ptr] == 0:
            pc = cond_map[pc]
        elif c == ')':
            pass
        elif c == '{':
            pc = comment_map[pc]
        elif c == '}':
            pass
        elif c == '$':
            pass
        elif c == '*':
            tape[ptr] = (tape[ptr] * acc)
        elif c == '`':
            tape[ptr] = (tape[ptr] // acc)
        elif c == '%':
            tape[ptr] = (tape[ptr] // acc)
        elif c == '\':
            tape[ptr] = acc
        elif c == '/':
            acc = tape[ptr]
        pc += 1

def build_loop_map(code):
    stack = []
    loop_map = {}
    
    for i, c in enumerate(code):
        if c == '[':
            stack.append(i)
        elif c == ']':
            start = stack.pop()
            loop_map[start] = i
            loop_map[i] = start
    if stack:
        raise SyntaxError("Unmatched brackets")
    return loop_map

def build_cond_map(code):
    stack2 = []
    cond_map = {}
    
    for i, c in enumerate(code):
        if c == '(':
            stack2.append(i)
        elif c == ')':
            start = stack2.pop()
            cond_map[start] = i
            cond_map[i] = start
    if stack2:
        raise SyntaxError("Unmatched brackets")
    return cond_map

def build_com_map(code):
    stack3 = []
    comment_map = {}
    
    for i, c in enumerate(code):
        if c == '{':
            stack3.append(i)
        elif c == '}':
            start = stack3.pop()
            comment_map[start] = i
            comment_map[i] = start
    if stack3:
        raise SyntaxError("Unmatched brackets")
    return comment_map

Example

Hello, World!

0 = [-]
A = ++++++++[>++++++++<-]>
~ = ++++[>++++++++<-]>
! = ~+
C = ~++++++++++++
a = ++++++++[>++++++++++++<-]>
_________
A++++++++.0<0a+++++.0<0a++++++++++++..+++.0<0C0<0~0<0
A+++++++++++++++++++++++.0<0a+++++++++++++++.+++.------.--------.0<0!.0<0

Categories and References