Let's make a new programming language

clip art of a giraffe programming and using a computer

by Ryan Mullin


Do you know what the worlds needs right now? More programming languages. Never fear! I have concocted one from the deepest, darkest dephts of my strange sense of humor. I tried to keep it reasonable enough that it was usable, but also threw in some tricks to make it uniquely mine

It’s called GiraffeScript, or GScript for short. It’s logo is listed above

Basis

Let’s start of with a base language. I am most familiar with TypeScript, so we’ll build off of that. Plus, the current tooling in the JS ecosystem rigeht now is beyond compare, and has some of the best suppourt around. Plus, we can use existing tools like node, deno, and V8 to execute the code after it has been transpiled. (Yes, its a superset. Put your pitchforks and torches down please)

Basics

Let’s start off with the basics of the language, and demonstrate them with some example programs.

Hello, World

console.* is a bad abstraction. It’s meant strictly for debugging, and you have to access the console namespace to even display words on a screen. Not helpful nor fun for beginners. To print to the standard output, simply use write()

write('Hello, World!'); // Prints "Hello, World!" to the standard output.

Delclaring Variables

Why do we need three keywords to declare a variable? I still dont know the answer to that question. To declare a variable, use var.

var name = "GScript"

ALl variables shall be globally scoped and immutable by default. To change this behavior, change your declaration. To make it mutable, add mut."" To make it block scoped, add blockscope

mut var animal = 'Giraffe' // Globally scoped mutable variable
blockscope var short  = 'G' // Blockscoped immutable variable
blockscope mut var lessShort = 'GScript' //blockscoped mutable variable.

Types

As with typescript, all types are optional. If you do not specify a specific type, the type will be infered. If you want to be condecending about it, you can tell the compiler to explicity infer the type, which is its automatic behavior

var bestAnimal: infer = 'Giraffe'

In fact, you can use infer with a generic, defeating the whole purpose of it in the first place!!

var myFavoriteNumber: infer<number> = 6

To specify the return type of the function, use the squiggly arrow. This is due to how it looks kinda funny, and that pleases me.

function getBestAnimal() ~> infer<string> {
    return 'Giraffe'
}

Functions

There are multiple different ways to declare a function. The most simple of which is function

function add(a: infer<number>, b: infer<number>) ~> number {
    return a +b
}

However, if you want to be concise, a hipter, or a consice hipster, you can use an arrow function!

-> (a, b) ~> infer<number> => a+b <-
// Notice how the function has to be on one line, and surronded by arrows pointing at it, to let the rest of the program know how concise and hip it is.

As in typescript, you can assign these to variables

var addFn = -> (a, b) => a+b <-

Returning Values

To return a value, simply use return
To do the opposite, use retain
Retain keeps the value in memory, and will persist there, doing nothing, until the program is terminated.

function findersKeepers() {
    retain 'cookies'
}
// The string 'cookies' will persist in memory until the program is terminated.

Conditional Returns

You can return values if a certian parameter is met. For example:

function addButSpecial(a: number, b: number) ~> infer<number> {
    return a+b if (a+b > 5) else retain a+b
}

Funky Functions

To make your program even more fun, you can make your functions funky! funky functions run line by line, in a random order. How funky!

funky function getGroovy() ~> infer {
    blockscope var boogie = boogie()
    blockscope var chicken = chicken()
    blockscope var twist = twist()
    write(boogie);
    write(chicken);
    write(twist);
    return
}
// Expected output: ???

Arrow functions can also be funky!

var breakDance = -> funky () => breakdance(); askForTip(); <-
// Expected output: ???

Generator Functions

Generator functions are a dark pattern and have been removed from the language. To restore this archaic feature, you must jump through hoops:

// This function returns the function* keyword
funky function jumpThroughHoops(guess: number) ~> infer {
    return if (guess === random(1, Infinity))
}

If you guess correctly, the function* keyword becomes available However, as a punishment for using this dark pattern, all generator functions must be funky

funky function* goToHell() ~> infer {
    yield(void);
    yeild(process.platform);
    yeild(os.delete('C:/Windows/System32'));
}

Async Functions

Since async function always returns a promise, it makes it roughly 10x harder to work with, so, you must type out the word asynchronous to take the extra 1/2 second to realize what you are getting yourself into.
await also has a similar punishment, as you must use anticipate.

Example async function:

asynchronous function hitAPI() {
    blockscope var result = anticipate fetch('...')
    return if (anticpate result.json().isLongerThan(5000)) else retain anticipate result.json()
}

Of course, async functions can also be funky!

funky asynchronous function hitAPI() {
    blockscope var result = anticipate fetch('...')
    return if (anticpate result.json().isLongerThan(5000)) else retain anticipate result.json()
}
// This might return before even hitting the API

Arrays

Arrays work just like the do in TypeScript, however, there are some notable changes.

If the day of the week (local time) is Friday, arrays begin indexing at [1]. That way, the program always breaks on a Friday. This is to maximize job retention when using GScript.

var myArray: infer<string[]> = [
    '0',
    '1',
    '2'
]
if (myArray[1] === '0') {
    deleteApp()
}
// ^^ If this runs on a friday, the app will be deleted

Loops

Unlike other perfect languages named after animals, we believe in loops here. To perform a loop, there is no need to create a varible and increment it each time. Simply use do ... times

do 50 times {
    write('woohoo');
}

To get the index of where the loop is at, use with index ...

do 50 times with index i {
    write(`we are at iteration number ${i}`)
}

Loops Over Arrays

To loop over arrays, do not use do. Please use each ... in ...

each number in numberArray {
    write(number)
}

You can still use with index ... here as well

each word in dictionary with index i {
    write(`the ${i} word in the dictionary is ${word}`)
}

Example Programs:

FizzBuzz

do 100 times with index i {
    if ((i + 1) % 3 === 0 && (i + 1) % 5 === 0) {
        write('FizzBuzz');
    } else if ((i + 1) % 3 === 0) {
        write('Fizz');
    } else if ((i + 1) % 5 === 0) {
        write('Buzz');
    } else {
        write(i + 1);
    }
}

Fibonacci Sequence

blockscope var n = 10; // Specify the number of Fibonacci numbers to generate

blockscope var fibArray: infer<number[]> = [0, 1]; // Start with the first two numbers

do (n - 2) times {
    blockscope var len = fibArray.length;
    blockscope var nextNum = fibArray[len - 1] + fibArray[len - 2];
    fibArray.push(nextNum);
}

each fibNum in fibArray {
    write(fibNum);
}

Arrays

blockscope var fruits: infer<string[]> = ['Apple', 'Banana', 'Orange', 'Mango', 'Pineapple'];

write('Welcome to the fruit stand!');
write('Here are the available fruits:');

if (isFriday()) {
    each fruit in fruits with index i {
        write('[' + (i + 1) + '] ' + fruit);
    }
} else {
    each fruit in fruits with index i {
        write('[' + i + '] ' + fruit);
    }
}

blockscope var selectedFruitIndex = 3; // Let's pretend the user selected the third fruit

if (isFriday()) {
    selectedFruitIndex++; // Adjust the index by adding 1 on Fridays
}

if (selectedFruitIndex >= 1 && selectedFruitIndex <= fruits.length) {
    var selectedFruit = fruits[selectedFruitIndex - 1];
    write('Great choice! You selected: ' + selectedFruit);
} else {
    if (isFriday()) {
        write('Oops! Invalid selection. Today is Friday, the fruit stand is closed!');
        retain 'Backup plan initiated: Enjoy a backup fruit basket!';
    } else {
        write('Oops! Invalid selection. Please choose a valid fruit index.');
    }
}

Kitchen Sink

blockscope var name = 'GScript';
var age = 5;
mut var counter = 0;

function greetPerson(person: infer<string>, isFormal: infer<boolean>) ~> infer<string> {
    return if (isFormal) {
        `Hello, ${person}! Nice to meet you.`;
    } else {
        `Hey there, ${person}! How's it going?`;
    }
}

funky function funkyCounter() {
    do 5 times {
        counter++;
    }
    write('Funky counter: ' + counter);
}

function multiplyByTwo(num: infer<number>) ~> number {
    return num * 2;
}

asynchronous function fetchData() {
    blockscope var data = anticipate fetch('https://api.example.com/data');
    return if (anticipate data.status === 200) else retain anticipate data.json();
}

do 3 times with index i {
    write('Iteration: ' + i);
    retain 'Cookie'; // Retain the string 'Cookie' in memory
}

var result = multiplyByTwo(7);
write('Result: ' + result);

blockscope var fetchedData = anticipate fetchData();
write('Fetched data: ' + fetchedData);

write(greetPerson('John', true));
write(greetPerson('Jane', false));

funkyCounter();

ryan mullin
pattern