Create a FizzBuzz compiler

17

5

Welcome to the world of compiler golf. Your task is to write a program that generates another program to play a variant of FizzBuzz on spec.

Your compiler

Write a compiler that generates variants of the FizzBuzz program to spec. The spec of this variant is expressed in the form of an array of integer/string pairs.

  • The input may be in any form that is convenient to your language. (My examples use n:xxxx, but this is just for illustrative purposes.)
  • Each integer input may only be used once per invocation of your compiler.
  • The integer of each pair will have a value of at least one.
  • The string of each pair will be made of only exactly four ASCII letters.
  • The output must be a single complete program that conforms to the rules below.
  • The output may be in any convenient form, as long as it is a textual program. (So no returning lambda expressions.)

Behavior is undefined for inputs not conforming to the above rules.

Your generated FizzBuzz program

The program generated by your compiler will take a single integer, n, as input. It will output a sequence of numbers starting from one up to and including n, replacing numbers with FizzBuzz strings when required.

  • The generated program must be in the same language as the compiler.
  • The input n may be in any form convenient to your language.
  • n will have a value of at least one.
  • A number that is a multiple of at least one of the integers input to the compiler must be replaced by all of the strings paired with those integers joined together.
  • A number that is not to be replaced by a FizzBuzz string must be output in decimal ASCII.

For example;

> GenFizzBuzz 3:Fizz 5:Buzz
> a.out 5
1
2
Fizz
4
Buzz

Scoring

Your entry will be scored by the length of the programs your compiler generates added to the length of your compiler. Run your compiler many times with each the following parameters and add the lengths of the generated programs together with the length of the compiler to find your score.

  1. Just Count. (No inputs - The generated program will count 1 to n without replacements.)
  2. Just Golf. (1:Golf - The generated program will output "Golf" n times.)
  3. Classic FizzBuzz. (3:Fizz, 5:Buzz)

(Note that your compiler is required to generate code for any valid input, not just these listed.)

billpg

Posted 2014-07-31T13:20:32.720

Reputation: 1 995

I assume that the generated programs must be independent and can't use the compiler in any way? – Hjulle – 2015-04-10T16:51:32.570

@Hjulle - I'm not sure what you mean. The text output of the first program must be a program, which may be compiled, especially if that language is normally compiled (like C et al). – billpg – 2015-04-11T12:13:12.540

I mean my own compiler program, the one that generates the fizzbuzz programs. My idea was to abuse that my compiler already existed in order to generate smaller programs (that doesn't work without access to my compiler). ;) – Hjulle – 2015-04-11T12:20:27.630

@Hjulle - You want your generated program to be able to call through to the compiler. Since I changed the rules to include the length of the compiler as well as the test cases, that should be okay. – billpg – 2015-04-13T08:19:53.917

Can we assume that input to the compiler is taken in order (lowest to highest or vice versa)? – brhfl – 2018-04-03T17:26:20.097

no scoring for the length of the compiler?? – Sparr – 2014-07-31T14:30:26.090

can we assume the integers are single digit? that there are no spaces in the strings? – Sparr – 2014-07-31T14:52:51.573

@Sparr Would that (two digit integers) make a difference? Remember, its only the code generated that makes your score. – billpg – 2014-07-31T15:04:13.123

well, fizzbuzz is an already extremely thoroughly golfed problem elsewhere on the internet. I don't know if I could forget reading the solution to it if I tried. – Sparr – 2014-07-31T15:09:12.647

@Sparr - You persuaded me. I've changed the rules to include the length of the compiler. – billpg – 2014-07-31T15:34:44.527

1Finally a golf challenge that actually makes sense to write in AWK. – shadowtalker – 2014-07-31T16:33:05.767

Should the compiler output to stdout or a.out? – nyuszika7h – 2014-08-06T16:57:18.947

@nyuszika7h - "The output may be in any convenient form, as long as it is a textual program." – billpg – 2014-08-07T08:07:59.300

Answers

8

Python 3 - 168 162 + 230 = 392

Oh, Python, you try so hard, but multiplying the import sys;sys.argv stuff by 4 really hurts!

import sys;a=eval(sys.argv[1])
print("import sys\nfor i in range(1,int(sys.argv[1])+1):print("+"+".join('"%s"*(i%%%d==0)'%t for t in a)+(a and"or str(i))"or"i)"))

Output programs:

import sys
for i in range(1,int(sys.argv[1])+1):print(i)
import sys
for i in range(1,int(sys.argv[1])+1):print("Golf"*(i%1==0)or str(i))
import sys
for i in range(1,int(sys.argv[1])+1):print("Fizz"*(i%3==0)+"Buzz"*(i%5==0)or str(i))
  • Expected input for the main program is an eval-able sequence of Python tuples or '()' for no input. (You did say "convenient".) Example input: '()', '("Golf",1),', '("Fizz",3),("Buzz",5)' Note quoting for shell and trailing comma for one input.

  • Fixed 1am mistake by changing from dict (undefined ordering!) to tuples.

  • Expected input for the other programs is just the number

Jason S

Posted 2014-07-31T13:20:32.720

Reputation: 371

@JasonS - Hi there. I'm interested in your experience of this challenge. http://meta.codegolf.stackexchange.com/questions/5050/thoughts-on-compiler-golf

– billpg – 2015-04-09T17:06:02.830

In your example command line argument, I had to wrap in double quotes and use single quotes for 'Fizz' and 'Buzz' - like so `"{3:'Fizz',5:'Buzz'}" however the program is still throwing an error for me. – James Williams – 2014-08-01T08:56:12.110

What is the error? – Jason S – 2014-08-01T14:13:56.130

6

perl6 376 340 84+115=199

UPDATE: switched from perl5 to perl6 to get say without use feature.

UPDATE: three test cases instead of five

There are hundreds of already-golfed solutions to FizzBuzz, and many contests end with the same result, so that's where I started. My compiler just produces a customized version of that solution. A few extra characters were inserted to account for the "just count" variation.

compiler, expects arguments like so: "Fizz 3" "Buzz 5"

print'say(('.(join'.',map{'('.(join')[$_%',split).']'}@ARGV).')||$_)for 1..$ARGV[0]'

compiled programs, expect argument like so: 100

say(()||$_)for 1..$ARGV[0]
say(((Golf)[$_%1])||$_)for 1..$ARGV[0]
say(((Fizz)[$_%3].(Buzz)[$_%5])||$_)for 1..$ARGV[0]

compiled programs for old test cases:

say(((Twoo)[$_%2].(Four)[$_%4].(Eiht)[$_%8])||$_)for 1..$ARGV[0]
say(((Twoo)[$_%2].(Thre)[$_%3].(Five)[$_%5].(Sevn)[$_%7])||$_)for 1..$ARGV[0]

Sparr

Posted 2014-07-31T13:20:32.720

Reputation: 5 758

Hi there. I'm interested in your experience of this challenge. http://meta.codegolf.stackexchange.com/questions/5050/thoughts-on-compiler-golf

– billpg – 2015-04-09T17:05:26.243

I've changed the rules as discussed in the question's comments. You'll want to recalculate your score. – billpg – 2014-07-31T15:37:15.770

@billpg done, and improved :) – Sparr – 2014-07-31T15:52:41.550

3

Pyth - 51 + (38 + 43 + 50) = 182 bytes

Can probably golf the compiler a few bytes. The links on all of them are permalinks to the online interpreter.

Compiler - 51 bytes

%"K[%s)=dc\"%s\"dFGr1hQJkFNKI!%%GN~J@dxKN))?JJG",zw

Just does string formatting with an input tuple. Takes input like:

3 5
Fizz Buzz

Nothing - 38 bytes

K[)=dc""dFGr1hQJkFNKI!%GN~J@dxKN))?JJG

Just Golf- 43 bytes

K[1)=dc"Golf"dFGr1hQJkFNKI!%GN~J@dxKN))?JJG

Classic Fizz Buzz - 50 bytes

K[3 5)=dc"Fizz Buzz"dFGr1hQJkFNKI!%GN~J@dxKN))?JJG

Maltysen

Posted 2014-07-31T13:20:32.720

Reputation: 25 023

2

Stax, 23+5+17+29=74

╥╟.└ç╘SJ∞CF╔v=▌╝Σ@∞ìé«g

Run and debug it

Shortest answer so far Not surprisingly beaten by Jelly. The string template provided in Stax is really neat and provides printf-like functions. The programs generated by the compiler are almost always as short as the best one can achieve by manually codegolfing, without using packing.

The compiler itself is 23 bytes long.

The ASCII equivalent is:

{H34|S_h"_`c%z`n?+"m"mz`cc_?

Provided input [], generates this one (5 bytes)

mzc_?

Run and debug it

Provided input [[1,"Golf"]], generates this one (17 bytes)

mz_1%z"Golf"?+c_?

Run and debug it

Provided input [[3,"Fizz"],[5,"Buzz"]], generates this one (29 bytes)

mz_3%z"Fizz"?+_5%z"Buzz"?+c_?

Run and debug it

Weijun Zhou

Posted 2014-07-31T13:20:32.720

Reputation: 3 396

2

C++11 ~ 486 + (234 + 244 + 255) = 1219

First participation here, this challenge is not among the most difficult ones so I thought I'd give it a try. Using C++ though, and even with C++11 additions it is still a pretty verbose language, but I'm sure there's room for improvement.

Compiler (486):

#include<sstream>
#include<iostream>
using namespace std;main(int c,char**v){stringstream t;int i;string s,o;o="#include <iostream>\n#include <map>\nusing namespace std;main(int c,char**v){int i,n=stoi(v[1]);map<int,string> f{";int z=2;for(int j=1;j<c;++j){t.str(v[j]);t.clear();t >> i; t >> s;o+="{"+to_string(i)+",\""+s+"\"}"+(z++==c?"":",");}o+= R"(};bool p;for(i=1;i<n;++i){p=true;for(auto e:f){if(i%e.first==0){cout<<e.second;p=false;}}cout<<(p?to_string(i):"")+"\n";}})";cout<<o;}

It assumes arguments in the form of 3Fizz 5Buzz etc.

Count (234):

#include <iostream>
#include <map>
using namespace std;main(int c,char**v){int i,n=stoi(v[1]);map<int,string> f{};bool p;for(i=1;i<n;++i){p=true;for(auto e:f){if(i%e.first==0){cout<<e.second;p=false;}}cout<<(p?to_string(i):"")+"\n";}}

Golf (244):

#include <iostream>
#include <map>
using namespace std;main(int c,char**v){int i,n=stoi(v[1]);map<int,string> f{{1,"Golf"}};bool p;for(i=1;i<n;++i){p=true;for(auto e:f){if(i%e.first==0){cout<<e.second;p=false;}}cout<<(p?to_string(i):"")+"\n";}}

FizzBuzz (255):

#include <iostream>
#include <map>
using namespace std;main(int c,char**v){int i,n=stoi(v[1]);map<int,string> f{{3,"Fizz"},{5,"Buzz"}};bool p;for(i=1;i<n;++i){p=true;for(auto e:f){if(i%e.first==0){cout<<e.second;p=false;}}cout<<(p?to_string(i):"")+"\n";}}

Additional information

Tested with GCC 4.8.1, no compiler cheats.

Here is a small makefile to automate the generation of the test cases and run them (use make run):

run:
    g++ main.cpp --std=c++11 -o fbc

    ./fbc > count.cpp
    g++ count.cpp --std=c++11
    echo "======= Count ========"
    ./a.out 15

    ./fbc 1Golf > golf.cpp
    g++ golf.cpp --std=c++11
    echo "======= Golf ========"
    ./a.out 15

    ./fbc 3Fizz 5Buzz > fizzbuzz.cpp
    g++ fizzbuzz.cpp --std=c++11
    echo "======= FizzBuzz ========"
    ./a.out 15

teh internets is made of catz

Posted 2014-07-31T13:20:32.720

Reputation: 1 881

Hi there. I'm interested in your experience of this challenge. http://meta.codegolf.stackexchange.com/questions/5050/thoughts-on-compiler-golf

– billpg – 2015-04-09T17:06:28.517

map<int,string> f could be map<int,string>f. You could initialize j=1 at the same time with z. – Yytsi – 2016-09-24T07:35:26.637

2

Ruby 99 + (86 + 94 + 103) = 382

puts"(1..ARGV[0].to_i).each{|i|x=[];#{ARGV[0]}.each{|k,v|x<<v if i%k==0};puts x.size>0?x.join():i}"

Usage:

wc -c main.rb # 99 chars
ruby main.rb "{}" | ruby - 100 # 1..2..3..
ruby main.rb "{}" | wc -c # 86 chars
ruby main.rb "{1=>:Golf}" | ruby - 100 # Golf..Golf..Golf..
ruby main.rb "{1=>:Golf}" | wc -c # 94 chars
ruby main.rb "{3=>:Fizz,5=>:Buzz}" | ruby - 100 # 1..2..Fizz..4..Buzz..
ruby main.rb "{3=>:Fizz,5=>:Buzz}" | wc -c # 103 chars

William Andrew Burnson

Posted 2014-07-31T13:20:32.720

Reputation: 21

1

Perl 5, 77 + 93, 170 bytes

say"say",(map{++$i%2?"'$_":"'x!(\$_%$_)".($i<$#F?'.':'||$_')}@F),' for 1..<>'

Try the compiler online!
Try just count online!
Try just golf online!
Try fizz buzz online!

Dom Hastings

Posted 2014-07-31T13:20:32.720

Reputation: 16 415

1

Jelly, 88 84 83 73 bytes

Shortest answer so far (beats the previous "shortest answer" by 1 byte)

Compiler:

Ṿ€“ḍ@€“ẋ"ЀF€ȯ"”jµFF?⁾RY

Try it online! (compiler)

Try it online! (verify the bytecount)


Statistics:

19 24 compiler
   20 golf
17  2 count
   27 fizzbuzz
83 73 total

user202729

Posted 2014-07-31T13:20:32.720

Reputation: 14 620

Nice to see being beaten. – Weijun Zhou – 2018-04-14T08:05:03.833

1

Common Lisp, 636 577

(ql:quickload'cl-ppcre)(lambda(z)(princ(subseq(ppcre:regex-replace-all" *([(')]) *"(with-output-to-string(@)(print`(lambda(n)(dotimes(i n)(loop for(m s)in ',z if(=(mod(1+ i)m)0)do(princ s))(do()((fresh-line))(princ (1+ i)))))@))"\\1")1)))

I took my other answer and wrapped it in quasiquotes while adding input parameters. I print the resulting form as a single-line and remove unnecessary whitespace characters. The compiler is a little longer than the previous version, but the resulting score is reduced.

Score

(let ((*standard-output* (make-broadcast-stream)))
  (loop
     for form in '(215                      ; Compiler
                   ()                       ; Count
                   ((1 "Golf"))             ; Golf
                   ((3 "Fizz")(5 "Buzz")))  ; FizzBuzz
     for length = (if (numberp form) form
                      (length (funcall *fun* form)))
     collect length into lengths
     sum length into sum
     finally (return (values sum lengths))))

Returned values:

574
(215 111 119 129)

Pretty

(defun fizz-buzz-compiler (z)
  (princ (subseq
          (cl-ppcre:regex-replace-all
           " *([(')]) *"
           (with-output-to-string (stream)
             (print
              `(lambda (n)
                 (dotimes(i n)
                   (loop for (m s) in ',z
                      if (=(mod(1+ i)m)0)
                      do (princ s))
                   (do () ((fresh-line))
                     (princ (1+ i))))) stream))
             "\\1") 1)))

The input format is a list of (number string) couples. For example:

(fizz-buzz-compiler '((3 "Fizz")(5 "Buzz")))

... prints to standard output:

(LAMBDA(N)(DOTIMES(I N)(LOOP FOR(M S)IN'((3 "Fizz")(5 "Buzz"))IF(=(MOD(1+ I)M)0)DO(PRINC S))(DO NIL((FRESH-LINE))(PRINC(1+ I)))))

... which, pretty-printed, is:

(lambda (n)
  (dotimes (i n)
    (loop for (m s) in '((3 "Fizz") (5 "Buzz"))
          if (= (mod (1+ i) m) 0)
          do (princ s))
    (do () ((fresh-line)) (princ (1+ i)))))

Testing the resulting function:

CL-USER> ((lambda (n)
  (dotimes (i n)
    (loop for (m s) in '((3 "Fizz") (5 "Buzz"))
          if (= (mod (1+ i) m) 0)
          do (princ s))
    (do () ((fresh-line)) (princ (1+ i))))) 20)
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz

coredump

Posted 2014-07-31T13:20:32.720

Reputation: 6 292

0

dc, 434 bytes

[:a]sa[91Pn93Pznlanps_znlanz0<R]sR[[[lj1-;aP1sb]sB0sj[dljd2+sj;a%0=Bljlz>F]sF[p2Q]sP]P]sI[[[]sF[pq]sP]nq]sN[z0=Nzn[sz]PlRxlIx]x[sn0dsb[1+0sjlFx[lb0=PAP]x0sbdln>M]dsMx]P

Try it online!

Input for the compiler (168 bytes) should be placed on the stack as integer, string, integer, string, and so on (3 [Fizz] 5 [Buzz]). It should be given in the order one wants their fizzes and buzzes to be printed, which may be a bit of a cheat (having implemented bubble sort in dc before, I believe it would cost me around 100 bytes) but it also allows the user to, say, still have 'Fizz' run on 3 and 'Buzz' run on 5, but have 15 yield 'BuzzFizz'.

I'm sure this can be golfed a bit more; the main macro in the final program (M) relies on two macros (F and P) that are rather unnecessary given no input. Right now the compiler checks for input and outputs different (much smaller) versions of these macros if there is none, but I'm not sure the whole setup is optimal.

The compiler itself is pretty straightforward, it just checks to see if there are 'rules' on the stack, and if so it prints code that stores the stack depth in z, stores the stack in a 0-indexed array a, and then prints the generalized FizzBuzz code. If there was nothing on the stack, it really just prints a modified version of the FizzBuzz code. Test cases:

No input (46 bytes):

[]sF[pq]sPsn0dsb[1+0sjlFx[lb0=PAP]x0sbdln>M]dsMx

3[Fizz]5[Buzz] (117 bytes):

4sz[Buzz]3:a5
2:a[Fizz]1:a3
0:a[lj1-;aP1sb]sB0sj[dljd2+sj;a%0=Bljlz>F]sF[p2Q]sPsn0dsb[1+0sjlFx[lb0=PAP]x0sbdln>M]dsMx

1[Golf] (103 bytes):

2sz[Golf]1:a1
0:a[lj1-;aP1sb]sB0sj[dljd2+sj;a%0=Bljlz>F]sF[p2Q]sPsn0dsb[1+0sjlFx[lb0=PAP]x0sbdln>M]dsMx

They all expect the n value on the stack, this gets stored in n. The ones that have 'rules' place them in the array a, with the strings at odd indices and the integers at evens. The main macro, M, increments whatever is on the stack, runs macro F which checks the value against array a, checks whether F set register b to truthy or not and prints the top of stack if so or a newline if not, resets b to falsy, and then keeps running itself if n hasn't been reached yet. Macro F, given rules, goes through the whole array looking for matches. It increments by two since our integers and strings are woven through the array, and on a match it calls macro B. Macro B just retrieves the string (current position in array less one), and prints it. It also sets b to truthy. Our compiler doesn't bother to print B for no input, and essentially makes F a nop.

brhfl

Posted 2014-07-31T13:20:32.720

Reputation: 1 291

0

vim, 122 (compiler) + 73 (empty) + 90 (golf) + 123 (fizzbuzz) = 392 bytes

Compiler

:%s/\v(.*):(.*)/qq\1jA\2<C-V><C-V><C-V><ESC>q=@qgg
VgggJAddGdd:%s/\v[0-9]*([^0-9])/\1
<C-V><ESC>:%@n
:w
:so! %
<ESC>ggii%s/=/<C-V><ESC><C-V><C-A>a/g<C-V><ESC>"ncc:%!seq 0 =
<ESC>

Input format

3:Fizz
5:Buzz

Generated code for FizzBuzz case

i%s/=/<ESC><C-A>a/g<ESC>"ncc:%!seq 0 =
qq3jAFizz<C-V><ESC>q=@qggqq5jABuzz<C-V><ESC>q=@qggddGdd:%s/\v[0-9]*([^0-9])/\1
<ESC>:%@n
:w
:so! %

Generated Code, Annotated

# replace the input number with a regex that replaces the placeholder (=) 
# with the real number + 1 (we'll need an extra line as a terminator later)
i%s/=/<ESC><C-A>a/g<ESC>

# pull the substitution command into register c and enter insert mode
"ncc

# create the numbers 0..N+1
:%!seq 0 =

# for each word, scan down k lines at a time and append the word to each
qq3jAFizz<C-V><ESC>q=@qgg
qq5jABuzz<C-V><ESC>q=@qgg

# delete the 0 and N+1 lines
ddGdd

# remove the numbers from any line with words
:%s/\v[0-9]*([^0-9])/\1
<ESC>

# Run the command we created at the beginning, replacing the placeholder 
# with the real number
:%@n

# The file now contains yet another program, with the constants defined.   
# Save and run.
:w
:so! %

# The file now contains a program that, when run on a buffer containing 
# a single line with a number, will produce the appropriate output

<C-V> is 0x16. <ESC> is 0x1b. <C-A> is 0x01.

Example session

$ cat code.txt
2:Foo
4:Bar
$ cat input.txt
8
$ { cat compile.vim; echo ':wq'; } | vim code.txt
# code.txt now contains the generated code
$ { cat code.txt; echo ':wq'; } | vim input.txt
$ cat input.txt
1
Foo
3
FooBar
5
Foo
7
FooBar

Ray

Posted 2014-07-31T13:20:32.720

Reputation: 1 488

There's something odd happening when I try to define and run a macro from within another macro. If I manage to sort that out, I might save a few bytes over the save and source approach. – Ray – 2018-04-04T21:23:21.747

0

C, 1080 bytes total

Compiler [369 bytes]

#include<stdlib.h>
r,t,f=3,b=5,n;char*F="FIzz",*B="buZZ";main(int c,char **v){if(f)for(c=atoi(v[1]),n=1;c>=n;)r=f?n%f:0,r?(t=b?n%b:0)?printf("%i\n",n):puts(B):r?printf("%s%s\n",F,B):puts(F),++n;else for(c=0;c<atoi(v[1]);)printf("%i\n",++c);}

Fizz Buzz [241]

#include<stdlib.h>
r,t,f=3,b=5,n;char*F="FIzz",*B="buZZ";main(int c,char **v){if(f)for(c=atoi(v[1]),n=1;c>=n;)r=f?n%f:0,r?(t=b?n%b:0)?printf("%i\n",n):puts(B):r?printf("%s%s\n",F,B):puts(F),++n;else for(c=0;c<atoi(v[1]);)printf("%i\n",++c);}

Golf [237]

#include<stdlib.h>
r,t,f=1,b=0,n;char*F="golf",*B="";main(int c,char **v){if(f)for(c=atoi(v[1]),n=1;c>=n;)r=f?n%f:0,r?(t=b?n%b:0)?printf("%i\n",n):puts(B):r?printf("%s%s\n",F,B):puts(F),++n;else for(c=0;c<atoi(v[1]);)printf("%i\n",++c);}

Count [233 bytes]

#include<stdlib.h>
r,t,f=0,b=1,n;char*F="",*B="";main(int c,char **v){if(f)for(c=atoi(v[1]),n=1;c>=n;)r=f?n%f:0,r?(t=b?n%b:0)?printf("%i\n",n):puts(B):r?printf("%s%s\n",F,B):puts(F),++n;else for(c=0;c<atoi(v[1]);)printf("%i\n",++c);}

NoSeatbelts

Posted 2014-07-31T13:20:32.720

Reputation: 89

-2

SlooSarksi .Lang, 179

%%--43^jjk"/][][0[#!#111# h SD G ergDFGdfg[]9--99+==

CodeBro

Posted 2014-07-31T13:20:32.720

Reputation: 1

10I'm not familiar with this language; could you link us to a page describing it? – lirtosiast – 2015-10-05T15:17:21.013