Polyglot autogolfer for https://code.golf

Overview

PolyGolf

Ambitious polyglot autogolfer for https://code.golf

Design goals

  • a language (IR) that compiles to many languages (C, Lua, Java, etc.)
  • targeted languages are limited to those available on https://code.golf
    1. C-like mutable imperative (easy): Bash, BASIC, C, C#, C++, COBOL, Crystal, D, Fortran, Go, Java, JavaScript, Julia, Lua, Nim, Pascal, Perl, PHP, PowerShell, Python, Raku, Ruby, Rust, Swift, V, Zig
    2. mostly functional: Elixir, F#, Haskell, Lisp
    3. other: Assembly, ><>, brainfuck, GolfScript, Hexagony, J, K, Prolog, sed, SQL, VimL
  • can compile PolyGolf to any language without language-specific annotations
  • alternative options and domain annotations may help recognition of language-specific idioms
  • goal for each language target (vague): at most twice as long on average for all holes, aka score at least 500× number of holes

Processing pipeline

  1. Parse frontend to unrefined IR
  2. Detect all idioms required for the language, and replace in the IR
  • if an idiom may or may not save bytes depending on some details, mark it as one of several alternatives. For example, a procedure or function used twice may or may not be shorter when inlined. Try both alternatives and compare
  • this might lead to exponential complexity, so there should be flags to avoid excessive branching. Similarly to -O3 in gcc spending more time compiling for faster output, our -O3 would spend more time compiling for shorter output
  1. Emit to the desired language

Usage

Requires npm and node 17.0+ installed.

To get started, clone the repository, then to install, run

npm install
npm run build
npm install . --location=global

This will set up the polygolf command to point to the CLI script.

To uninstall, use npm uninstall polygolf --location=global

Development

To get started, clone the repository, then run npm install to install dependencies

After making a change, run npm run build before running the cli as node dist/cli.js.

The npm alias npm run cli is equivalent to npm run build; node dist/cli.js

Some concepts (visitor, Path, etc.) are similar to those used by the JavaScript transpiler Babel, so the Babel plugin handbook is worth skimming.

Example

Example naive Fibonacci (in a hypothetical lisp + imperative syntax):

(declare a integer)
(declare b integer)
(declare i integer)
a = 0
b = 1
i = 1
while (< i 31) {
  (println a)
  t = (add a b)
  b = a
  a = t
  i = (add i 1)
}

This could compile to the following in C

a;b=1;t;i=1;main(){for(;i++<31;t=a+b,b=a,a=t)printf("%d\n",a);}

Note the following C-specific features, besides the syntax:

  • declarations out front
  • default values (0) omitted
  • statements moved inside the for loop (!)

The same unrefined IR could compile to the following in Lua:

a=0
b=1
for i=1,30 do print(a)a,b=a+b,a end

Note the following Lua-specific features, besides the syntax:

  • foreach-range loop instead of a glorified while loop (!)
  • temporary variable replaced with simultaneous assignment (!)

Competitiveness

PolyGolf is designed to be decent at golfing, so there's concern about making it public with applications to the competitive site https://code.golf. However, I will keep this repository and all PolyGolf features public with a few naive reference solutions. To avoid spoilers, solutions should not be shared unless they are naive/obvious solution to simple holes.

Frontend

TODO. For now just create the unrefined IR directly. Worry about syntax later.

Unrefined IR

The goal is to have a small but expressive core subset of language features. Approximately a lowest common denominator of most of the languages targeted.

The IR is a tree. Assignments are by value, not reference (no aliasing)

Types:

  • boolean
  • integer (unbounded; domain annotations may help language-specific narrowing to 32-bit ints etc)
  • string
  • List<?> (0 indexed)
  • Table<?,?> (not implementing for now)

Constant literals for each type.

Control Flow:

  • procedures (no functions for now for simplicity)
  • if-else
  • while

Builtin constants

  • argv

Builtin functions:

  • arithmetic: add, subtract, multiply, (integer) divide, exponent, mod (mathematical)
  • integer comparison: less, greater, etc.
  • indexing (table, string)
  • conversions: (int <--> string, etc.)
  • string ops: string concatenation, string split, print, length
  • sort

Complete list of builtins.

Idiom recognition (backend)

Where the magic happens for golfing. Mixins shared across languages for idiom recognition, for example replacing (the IR for) i=1;while(i<5)do i=i+1 end with (the IR for) for i=1,4 do end when targeting Lua. The same IR code (loop over range) would represent for i in range(1,5) in Python, so the same mixin can target several languages.

Planned idioms:

  • automatic 1-indexing correction (necessary for 1-indexed langs)
  • constant collapse e.g. 2+1 → 3 to golf automatic 1-indexing correction
  • replace while loops with foreach-range loops (Lua, Python, etc)
  • replace exponent that has base 2 with bitshift (C, other)
  • inline procedures used exactly once (most langs)
  • if string concatenation's only purpose is to be printed, then split each appended part into its own print statement (C, other languages with long concat)
  • merge several prints into one print (pretty much every language)
  • replace while loops with for loops (C, Java, etc)
  • replace temp variable with simultaneous assignment (Lua, Python, etc)
  • variable shortening (all languages): convert all variables to a single letter, to allow for more-verbose PolyGolf code
  • ...much more. We'll see what's useful when starting

Implementation plan

Backend first. Only numbers, booleans, strings; add lists and tables later.

Comments
  • Traversal only visits 9 children.

    Traversal only visits 9 children.

    Attempting to build the following polyglot code into Lua gives the error Lua requires inclusive ForRange.

    $a <- 1;
    $a <- 1;
    $a <- 1;
    $a <- 1;
    $a <- 1;
    $a <- 1;
    $a <- 1;
    $a <- 1;
    $a <- 1;
    for $b 1 10 {
    };
    

    Removing one or more of the assignments fixes the error, but it seems having 9 or more assignments causes a bug.

    opened by MeWhenI 2
  • Python if-else spacing

    Python if-else spacing

    Compiling

    if (1==1) { println 1; } { println 2; };
    

    to Python produces

    if 1==1:print(1)else:print(2)
    

    This is a syntax error, it should produce

    if 1==1:print(1)
    else:print(2)
    
    bug 
    opened by MeWhenI 1
  • add function node and valueType

    add function node and valueType

    Usage: $increment <- (func $x:-oo..oo ($x+1)); Recurisve functions should work but need to be type annotated:

    $fac:(Func 0..oo 0..oo) <- (func $x:0..oo
        (conditional ($x == 0) 1 (($fac ($x-1):0..oo) * $x))
    )
    
    opened by MichalMarsalek 1
  • Add Polygolf DSL

    Add Polygolf DSL

    It's in a bit of a rough state, so marking this as draft for now. Pretty much everything is an s-expression.

    In particular, I want to avoid the need for parentheses around statements. Requiring a semicolon would work I guess.

    opened by jared-hughes 1
  • implement node type annotation

    implement node type annotation

    Added valueType key to nodes. Builder functions now set the valueType based on child nodes. There's no type checking. Identifiers don't know their types.

    Now that I thing about it it might be better to create all the nodes as typeless and then run a plugin that would annotate the nodes (knowing the varDeclarations information).

    opened by MichalMarsalek 1
  • Refactor IR

    Refactor IR

    This started as removing applications and evolved to refactoring the IR completely (split IR.ts into files, move unrelated files into common/).

    Some applications have not been converted yet, so I have marked this as draft. In particular:

    • print + println should be their own Print node with a flag for trailingNewline
    • str_length ?
    • cardinality ? maybe also rename into two nodes: one for List, one for Table
    • int_to_str should be unary op
    • str_to_int should be unary op
    • sorted should be an unary op?
    • str_get_char should be its own node by analogy with ArrayGet
    • contains_key, contains_value, and indexof should be their own nodes?
    opened by jared-hughes 1
  • Remove debugEmit

    Remove debugEmit

    Get rid of src/languages/debug as we now have Polygolf target for debugging.

    Replace debugEmit's usage in /src/plugins/loops.test.ts with markdown tests.

    enhancement good first issue 
    opened by MichalMarsalek 0
  • Add property

    Add property

    Adds a boolean switch to MethodExpr to treat a method call as a property. Useful for languages such as C# and Swift. For example, Swift needs text.count rather than text.count().

    enhancement 
    opened by MeWhenI 0
  • Markdown tests

    Markdown tests

    Adds support for markdown defined test suites. The markdown tests are automagically compiled to typescript Jest code.

    To create such suite, create a markdown file with a ".test.md" extension. Headings are used to group the tests (using Jest's describe). Code blocks are used to define the actual tests. All other markdown syntax is ignored.

    Each codeblock is required to have the language annotation and optionally other test parameters/options/commands (all space-separated). If the language is polygolf (with no other options) it denotes an input, otherwise an output. Multiple output blocks can relate to a single input block. If a codeblock title contains "skip" it's not used to generate tests.

    See loops.test.md for how it is used for plugin testing. See nim.test.md for how it is used for language testing.

    Implements #59 .

    enhancement 
    opened by MichalMarsalek 0
  • Readonly IR

    Readonly IR

    Despite the branch name, the primary contribution of this PR is a read-only IR state. This removes the need for structuredClones, which are pretty expensive compared to the alternative (just pass stuff around, knowing it will not get mutated).

    With the read-only IR state, I rewrote all of the plugins to use the new read-only APIs and restricted what plugins can do: each plugin is now a visitor that takes a node (and its parents) and returns undefined for no change or the new node if it suggests a change. Plugins are utilized in two ways:

    1. applyAll in applyLanguage.ts: apply the plugin over the whole tree, and replace every node it suggests to replace. This mode is used in emit plugins to ensure that all of their changes are applied, such as converting all exclusive for-ranges to inclusive for-ranges.
    2. one replacement at a time: this is used in the BFS search. The search creates a new program by replacing the node, then considers that new program as a valid state to push to the queue. So if a plugin suggests a replacement for each of 5 nodes, the search continues on those 5 new programs.

    The key to immutable actions is Spines as a replacement for the old Path. A spine is just a node with its parents, together with some helper functions. Spines make immutable actions quite natural, similar to how git branches make immutable commits natural as well. (I think I got the inspiration for Spines from some Anders Hejlsburg interview where he seemed to indicate it's what TypeScript uses (I am not sure about this claim)).

    The main reason immutable nodes are faster than structured clones is the ability to re-use nodes: In the tree (add A B), swapping the arguments to get (add B A) costs as little as just constructing a new "add" node and re-using the old pointers to the A and B subtrees. To get a new program with N changed to node M, it is enough to construct a new node like N.parent but with N changed to M, a new node like N.parent.parent but with N.parent changed to the new N.parent, etc. So it just needs to re-make the parent nodes to create an entirely new program tree, so replacing a node immutably is average-case O(log(N)) in the number of tree nodes.

    TODO:

    • [x] add a description of spines and stuff to architecture.md
    • [x] consider what happens if an alternative includes another, like if swapBinaryOps was used (for some reason) as an emitPlugin for a + (b + c)). This has realistic potential in nested exclusive for-loops converted to inclusive for-loops. I'm not sure if I got the visit order right here.
    • [x] comment the code a bit more
    enhancement 
    opened by jared-hughes 0
  • Add Newline between if-else in python

    Add Newline between if-else in python

    Fixes a syntax error in Python if-else generation, raised in #46. Correctly handles indentation at multiple levels as confirmed by compiling

    if(1==1) {
        if (2==1) { 
            println "a"; 
        } { 
            println "b"; 
        };
    } {
        if (3==1) { 
            if (4==1) { 
                if (5==1) { 
                    println "c"; 
                };
            } { 
                println "d"; 
            };
        } { 
            println "e"; 
        };
    };
    

    Sidenote: This would probably be a good program to include in a test suite.

    opened by MeWhenI 0
  • Allow compiling to all languages at once

    Allow compiling to all languages at once

    So do like polygolf -i src/programs/fibonacci.polygolf -l all -o fibonacci and it will output to fibonacci.lua, fibonacci.nim, etc.

    Another possible thing is that instead of having -o fibonacci, you can have an option like --cg that will automatically upload everything to code.golf (of course, you have to input your token or whatever).

    enhancement 
    opened by Steffan153 1
  • Nim method parenthesization

    Nim method parenthesization

    Compiling

    print (text_split "abc" "b");
    

    to Nim produces

    include re
    "abc".split "b".echo
    

    which throws a type error

    bug 
    opened by MeWhenI 1
  • Compiling invalid polygolf

    Compiling invalid polygolf

    Compiling

    print true;
    

    to polygolf produces

    print (true);
    

    which when fed into the parser produces a syntax error. Most likely this should not be a syntax error.

    bug good first issue 
    opened by MeWhenI 1
Owner
Jared Hughes
Jared Hughes
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
The Remix version of the fakebooks app demonstrated on https://remix.run. Check out the CRA version: https://github.com/kentcdodds/fakebooks-cra

Remix Fakebooks App This is a (very) simple implementation of the fakebooks mock app demonstrated on remix.run. There is no database, but there is an

Kent C. Dodds 61 Dec 22, 2022
nest연습용 (w. https://github.com/seuiggi, https://github.com/okysky1121)

A progressive Node.js framework for building efficient and scalable server-side applications. Description Nest framework TypeScript starter repository

이름 2 Oct 5, 2022
A VS Code extension to practice and improve your typing speed right inside your code editor. Practice with simple words or code snippets.

Warm Up ?? ??‍?? A VS Code extension to practice and improve your typing speed right inside your code editor. Practice with simple words or code snipp

Arhun Saday 34 Dec 12, 2022
Reference for How to Write an Open Source JavaScript Library - https://egghead.io/series/how-to-write-an-open-source-javascript-library

Reference for How to Write an Open Source JavaScript Library The purpose of this document is to serve as a reference for: How to Write an Open Source

Sarbbottam Bandyopadhyay 175 Dec 24, 2022
Zero dependencies, lightweight, and asynchronous https requests package.

This project is a Work in Progress and currently in development. The API is subject to change without warning. A small fetching package for super simp

Ray Arayilakath 11 Dec 8, 2022
A knowledge management garden for https://obsidian.md, in which to grow your ideas

?? ?? The Obsidian Garden Welcome to your Knowledge Garden The Obsidian Garden is both guide in helping you create your own knowledge system, and a kn

Tane Piper 145 Dec 27, 2022
Exemplo de como deve ser implementação de nirvana do teste de contrato com Pact seguindo todas as práticas descritas em https://docs.pact.io/pact_nirvana

Exemplo de 'Nirvana' do teste de contrato Esse repositório exemplifica as melhores implementações de teste de contrato, atingindo o nirvana e tendo co

Paulo Gonçalves 66 Nov 28, 2022
Contains the codes for https://SolAiNetwork.com

Solana Artificial Intelligence Network SINE is the token to get your work done using Artificial Intelligence and crowd-sourcing. We are creating a pla

null 26 Aug 1, 2022
NFT Game Starter Project: https://github.com/buildspace/buildspace-nft-game-starter

Running React on Repl.it React is a popular JavaScript library for building user interfaces. Vite is a blazing fast frontend build tool that includes

Zahuis 2 Feb 11, 2022
A set of APIs for handling HTTP and HTTPS requests with Deno 🐿️ 🦕

oak commons A set of APIs that are common to HTTP/HTTPS servers. HTTP Methods (/method.ts) A set of APIs for dealing with HTTP methods. Content Negoti

oak 7 May 23, 2022
A fresh look for the Hackage. Follow us: https://twitter.com/HackageUI

Hackage UI Fresh look for the https://hackage.haskell.org/. Work in progress. Search Search on Hoogle. Search on Hackage. Full-text search integration

visortelle 97 Dec 28, 2022
This project is made with the help of https://www.youtube.com/watch?v=LMagNcngvcU&t=897s

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

Md Sabbir Rahman 1 Apr 24, 2022
LunaSec - Open Source Security Software built by Security Engineers. Scan your dependencies for Log4Shell, or add Data Tokenization to prevent data leaks. Try our live Tokenizer demo: https://app.lunasec.dev

Our Software We're a team of Security Engineers on a mission to make awesome Open Source Application Security tooling. It all lives in this repo. Here

LunaSec 1.2k Jan 7, 2023
AFrame port of Lamina (https://github.com/pmndrs/lamina)

AFrame-Lamina Automated port of Lamina to AFrame <a-lamina geometry="" material="shader:lamina;color:white;lighting:phong;" position="-1 0.5 -3" rotat

Ada Rose Cannon 4 Apr 6, 2022
Desenvolvido durante um desafio do servidor Ballerini no Discord! https://discord.gg/ballerini

Desafio Autenticação Node - Tech da Semana ⏰ ?? ?? Informações sobre o projeto O projeto foi desenvolvido com base no desafio do Discord da Rafaela Ba

Gabriel Borges 5 Jun 1, 2022
GitHub starter project link: https://github.com/buildspace/waveportal-starter-project

Running React on Repl.it React is a popular JavaScript library for building user interfaces. Vite is a blazing fast frontend build tool that includes

MD Rafi Uddin 0 Jun 5, 2022
Software for the next generation of social media. https://gitlab.com/soapbox-pub/soapbox-fe

Soapbox FE Soapbox FE is a frontend for Mastodon and Pleroma with a focus on custom branding and ease of use. It's part of the Soapbox project. Try it

Soapbox 52 Dec 30, 2022