Transpile Brainstract to Brainfuck

3

1

Introduction

Brainstract, a newly created dialect of Brainfuck adds a bit more elements to the syntax. In addition to the normal Brainfuck syntax, there are macros. To define a macro:

{macro_name ++++}

Then use it:

{macro_name} Adds 4 to the current cell

Challenge

Your challenge is to take a valid Brainstract program as input (command-line-arguments, function arguments, STDIN or file), and output a valid Brainfuck program that is equivalent to the Brainstract program (non-brainfuck characters are allowed in output). You may assume all input will be valid Brainstract.

Brainstract Spec

Whitespace counts as ANY whitespace (tabs, spaces, etc.)

A macro is defined by an opening brace character ({) followed by a macro name which must be made up of any characters except whitespace, brainfuck, and Brainstract characters (anything except whitespace and {}[]<>+-.,)

Then, it is followed by any amount of whitespace and a macro definition, made up of macro calls and non-brace characters and finally the macro is closed off with a closing brace (})

A macro call is of the form {macro_name} where macro name is the same as above.


Examples

Format: Input -> Output

{cat ,.} {cat}{cat}{cat}            -> ,.,.,.
{add [>+<-]} ++++>+<{add}           -> ++++>+<[>+<-]
{recursive ,.{recursive}}           -> (undefined behavior)
++++ {decrement -} ++++ {decrement} -> ++++++++-
{a >>>---<<<} {b {a}{a}} {b}        -> >>>---<<<>>>---<<<

Standard Loopholes apply, and shortest code wins


FireCubez

Posted 2018-09-19T13:36:02.473

Reputation: 857

Could you perhaps add some test cases? Example Brainstract programs and their expected Brainfuck program outputs? – Kevin Cruijssen – 2018-09-19T13:42:47.253

@KevinCruijssen Will do – FireCubez – 2018-09-19T13:44:56.343

Could you also link to a Brainstract documentation? – Jonathan Frech – 2018-09-19T14:03:59.610

@JonathanFrech Brainstract is actually just a language I made for the purpose of this challenge – FireCubez – 2018-09-19T14:06:10.297

1@FireCubez Under those circumstances, I would suggest mentioning that the language is newly created and adding a more sophisticated language definition -- what counts as a macro_name, which characters can be used as macros, etc. – Jonathan Frech – 2018-09-19T14:19:06.633

Could a macro definition appear anywhere in the Brainstract source code? Or must it appear to the left of its first use? Or must it appear "before" its first use when taking into account how brainfuck moves around its source code? – ngm – 2018-09-19T14:23:14.240

2Is {cat ,.} {cat3 {cat}{cat}{cat}} {cat3} a valid input? – Arnauld – 2018-09-19T14:28:07.090

@ngm It can appear anywhere – FireCubez – 2018-09-19T14:46:30.013

@Arnauld yes it is, added to question. – FireCubez – 2018-09-19T14:48:55.690

(repeating Jonathan Frech) What characters can appear in the macro name? Also, what characters can appear in the input? – user202729 – 2018-09-19T14:51:02.137

@user202729 Any non-whitespace and non-Brainstract/brainfuck characters can appear in the macro name. Input is valid Brainstract characters – FireCubez – 2018-09-19T14:55:51.353

@JonathanFrech Done. – FireCubez – 2018-09-19T14:57:58.390

Is it guaranteed that the macro name and definition must be a space, or can it be any whitespace character?

– user202729 – 2018-09-19T15:07:34.503

May macro name be empty? – user202729 – 2018-09-19T15:08:22.863

@user202729 Any whitespace character – FireCubez – 2018-09-19T15:09:20.530

Let us continue this discussion in chat.

– FireCubez – 2018-09-19T15:09:32.547

Using -> to separate the input and output in the examples is kinda dumb... – Jo King – 2018-10-21T14:36:50.390

If you have a better idea you can edit the post or tell me – FireCubez – 2018-10-22T14:00:29.910

@FireCubez Does the "Any non-whitespace and non-Brainstract/brainfuck characters can appear in the macro name. Input is valid Brainstract characters" still apply? If so, please edit it into the question – ASCII-only – 2018-12-22T10:21:24.003

@FireCubez it's dumb because both - and > are valid brainfuck characters... – ASCII-only – 2018-12-22T10:21:56.323

@ASCII-only Well, what I meant by that was "valid Brainstract" i.e. the macro names don't need to be valid brainstract characters – FireCubez – 2018-12-22T11:04:40.140

@FireCubez No I mean, most of the answers assume only alphanumeric names, which appears to not fit the spec. Plus, this specific part of the spec isn't in the question yet... – ASCII-only – 2018-12-22T11:22:03.710

@ASCII-only My original intention for names is the one I specified in the post now, but I wasn't so good at expressing my original intention when I posted this so now all the answers are different from my requirements – FireCubez – 2018-12-22T11:31:23.460

Answers

1

JavaScript (Node.js), 113 108 102 bytes

-5 bytes because input will always be valid brainstract
-6 bytes thanks to Cows quack for regex function golfs

x=>{while(m=/({\w+)\s+(\W+)}/.exec(x))x=x.replace(eval(`/${m[1]}}/g`),m[2]).replace(m[0],'');return x}

Try it online!

TIO Link includes a test suite of all 4 "valid" test cases provided in the OP.
The invalid recursive macro test was ignored as, given it's undefined behavior and our program can whatever, there's no need to test it; the result is irrelevant.

Ungolfed Explanation:

fun = code => {
    // While there's a macro definition in the code
    while(match = /{(\w+)\s+(\W+)}/.exec(code)) {
        // Replace calls to the macro with the macro's code
        code = code.replace(eval(`/${m[1]}}/g`),m[2])
        // And remove the definition from the code
            .replace(m[0],'');
    }
    // Remove all spaces from the code and return
    return x;
}

The entire function relies on the regex: /{(\w+)\s+(\W+)}/
This should match any valid macro definition and include the macro name in Capture Group 1, and the macro's code in Capture Group 2.

Due to how JS's regex.exec() returns, m[0] is the whole matched string (the entire macro definition), m[1] is the string matched by Capture Group 1 (the macro name) and m[2] is the string matched by Capture Group 2 (the macro's code)

We can then replace all instances of {m[1]} with m[2], then remove the first instance of m[0] in order to remove the macro definition. This is required in part due to the way brainfuck would interpret the macro definition, and in part due to the way the while loop in this function works (it finds the first definition in the code, if we don't remove the definition after we've processed it, it'll keep finding the first definition over and over)

However, our first capture group m[1] also includes the opening brace { of the macro definition. This is so that when replacing {m[1]} with m[2], we actually only need to replace m[1]}, which saves a byte.

Unfortunately including a variable m[1] in a RegExp in JS is a bit costly, as we need to eval the regex, which adds 6 bytes.

There's probably quite a bit of golfing room in this answer, as there tends to me in my NodeJS answers.
If anybody has any suggestions for golfing, let me know in the comments.

However if your answer involves using an entirely different approach to mine, please post your own answer :)

Skidsdev

Posted 2018-09-19T13:36:02.473

Reputation: 9 656

The new appears to be redundant, but eval(\/${m[1]}}/g`)is still shorter. Alsoexecis shorter thanmatch` – user41805 – 2018-12-20T16:20:57.420

@Cowsquack oh nice, thanks! – Skidsdev – 2018-12-20T16:27:27.163

85 – ASCII-only – 2018-12-20T23:48:03.273

80 – ASCII-only – 2018-12-22T10:18:51.513

but according to the comment, 94

– ASCII-only – 2018-12-22T10:20:42.657

I believe you intended return code; in the ungolfed version – FireCubez – 2018-12-22T11:08:31.207

0

Ruby, 74+1 = 75 bytes

Add one byte for the p flag.

(k=$2;gsub(/#$1( .+?)?\}/){$1?p: k})while~/(\{\w+) (([^{}]|\{\g<2>\})*)\}/

Try it online!

Value Ink

Posted 2018-09-19T13:36:02.473

Reputation: 10 608

0

PHP, 197 196 186 bytes

<?php $p=$argv[$c=1];$m=[];while($c)$p=preg_replace_callback("/\{(\w+)(\s((?0)|[^{])+)?\}/",function($a)use(&$m){return count($a)<3?$m[$a[1]]:[$m[$a[1]]=$a[2],""][1];},$p,-1,$c);echo $p;

Try it online!

185 if only one kind of whitespace is accepted (by changing the regex to /\{(\w+)( ((?0)|[^{])+)?\}/) but I'm not sure whether this is valid.

I was tempted to use JavaScript but the RegExp class in JS doesn't support RE recursion...

Shieru Asakoto

Posted 2018-09-19T13:36:02.473

Reputation: 4 445

0

Python 2, 133 bytes

import re
s=input()
while'{'in s:x=re.findall('{.*? (?:[^{]|{.+})*?}',s)[0];d,c=x.split(' ',1);s=s.replace(x,'').replace(d,c)
print s

Try it online!

Erik the Outgolfer

Posted 2018-09-19T13:36:02.473

Reputation: 38 134

118, maybe. idk – ASCII-only – 2018-12-22T10:35:03.103

@ASCII-only Maybe you want this 127-byte version, but it doesn't work if you stick a letter in the code part of the macro.

– Erik the Outgolfer – 2018-12-22T10:54:30.560

"non-brainfuck characters are allowed in output" – ASCII-only – 2018-12-22T11:22:44.797

@ASCII-only No, I mean that, if you do {someMacro >>>--t-<<<} instead of {someMacro >>>---<<<}, your code doesn't work. – Erik the Outgolfer – 2018-12-22T11:25:31.790

0

Perl 5 -p, 47 bytes

-11 bytes with ideas from @ASCII-only

$s=$2,s/\Q$1}/$s/g while s/(\{\S+)\s+(\S+?)\}//

Try it online!

Xcali

Posted 2018-09-19T13:36:02.473

Reputation: 7 671

40? – ASCII-only – 2018-12-22T10:25:17.057

Replacing \s+ with works for all the current testcases as well. hmm – ASCII-only – 2018-12-22T10:28:17.427

@ASCII-only From a comment: "[The space between the macro name and definition must be] any whitespace character" – FireCubez – 2018-12-22T11:12:15.073

@ASCII-only I don't think yours works in all cases. Take a look at the last test case in my new link. – Xcali – 2018-12-22T18:35:49.200

This should work for 41 bytes. – DLosc – 2019-03-07T05:27:40.813

0

Pip, 33 bytes

Wa~`({\S+)(.+?)}`a:$`.$'R$1.'}$2a

Try it online!

Very similar approach to Xcali's Perl solution, though mostly independently derived.*

Wa~`({\S+)(.+?)}`a:$`.$'R$1.'}$2a
                                   a is 1st cmdline argument (implicit)
Wa~`            `                  While this regex matches in a:
    ({\S+)                         First group: { followed by non-whitespace characters
          (.+?)                    Second group: more characters, as few as possible...
               }                   ... followed by }
                                   This matches a macro definition, including all the
                                   whitespace as part of the replacement code
                                   (It doesn't work for macro definitions with macro calls
                                   inside them, but the leftmost definition at any time
                                   will not have a macro call inside it)
                                   So while possible, match the first occurrence, and:
                 a:                  Set a to:
                   $`                 The portion of the string left of the match
                     .$'              concatenated to the portion right of the match
                        R             In that string, replace
                         $1.'}        match group 1 plus a trailing } (i.e. a macro call)
                              $2      with match group 2 (the macro's code)
                                a  When the loop exits, output the final value of a

* That is, I saw the Perl solution's byte count, went "How?!...", and finally realized I could assume a few things that made the code a lot shorter.

DLosc

Posted 2018-09-19T13:36:02.473

Reputation: 21 213

0

Java 8, 251 bytes


226 bytes of code + 25 bytes for java.lang.regex.* import. I can't lambda this since you can't use recursion in a lambda function (self-reference). This just about ignores recursive macros (the space is removed).

import java.util.regex.*;
...
String f(String s){Pattern p=Pattern.compile("\\{(\\w+)\\s+(\\W+)\\}");Matcher m=p.matcher(s);while(m.find())s=s.replace("{"+m.group(1)+"}",m.group(2)).replace(m.group(0),"");return p.matcher(s).find()?f(s):s.replace(" ","");}

Try it online

Regex source

Benjamin Urquhart

Posted 2018-09-19T13:36:02.473

Reputation: 1 262