I am making my own Interpreted language in C++ - GoofyLang

GoofyLang is a interpreted language made in C++ that serves NO purpose except annoying you. Because it's EXACTLY what the name of the language is, goofy. GoofyLang is Turing complete (idk what it means) because the compiler (or interpreter) translate your GoofyLang code into C++ code, then compile it into binary. Which makes it having function of C++.

GoofyLang works by translate each file's line into an "encrypted" integer, decrypting it and cast it as a character, which you could write C++ code with.

The encryption method is pretty straight forward. It translate every integer's digit into another digit. The translation list is:

1->7
2->5
3->1
4->0
5->2
6->4
7->6
8->9
9->8
0->3

For Decryption (GoofyLang to C++) it works in reverse.

If I want to translate my C++ code into GoofyLang:

    int i;

I would have to translate them into integers first:

    105 (i)
    110 (n)
    116 (t)
    32  ( )
    105 (i)
    59  (;)

Encrypt/Translate it:

    732
    773
    774
    15
    732
    28

And this is exactly what you would write in your GoofyLang script!


ok now lets get codin'


if(argc != 3 && argc != 4){

    cout<<"Usage: "<<argv[0]<<" inputfile.goofy outputfilename (optional)--keepcpp"<<endl;

    return 1;

}


argc represent how many argument is typed in your command prompt. 

./goofylang script.goofy

count as 2 argument, since it counts "./goofylang" as the first argument. This function prevents use using my interpreter wrongly. Also returns 1 when there's invalid amount of arguments.


    

    std::string arg3="";

    if(argc==4) arg3=argv[3];

    bool keepcpp=false;

    if(arg3=="--keepcpp") keepcpp=true;


If I did "string arg3(argv[3])" directly, I would get a segmentation fault. Probably because string would be null if i didnt type "--keepcpp". Therefore I check if theres a 4th argument, then add the argument as string. Also I can't directly compare const char * as my goofy ahh IDE (xcode) give me a warning ðŸ˜­

    

    std::ifstream inputFile(inputFilepath.c_str());

    std::ofstream outputCode(outputFilename.c_str());

    if (inputFile.is_open() && outputCode.is_open()){

        while (inputFile>>line){

            char c=decrypt(line);

            if(c!=0) outputCode<<c;

        }

        inputFile.close();

        outputCode.close();

    }else{

        std::cout << "[ERROR] Unable to open file\n";

    }


This snipet of code ensure the file is correctly opened, and in the "while" loop, it gets every single line. In my interpreter, we process each line's integer and store it in a string.

Note that the variable "line" is a string since it would be easier to process in the "decrypt" function. Well let's see what does the decrypt function do!


    int decrypttable[10]{4, 3, 5, 0, 6, 2, 7, 1, 9, 8};

    int decrypt(std::string& s){//(int)'0' = 48, (int)'9' = 57

        int result=0;

        for(int c:s){

            if(48<=c&&c<=57){//ignore characters, act as comment

                result=result*10+decrypttable[(c-48)];

            }

        }

        return result;

    }


Noted that '0' as a character is 48 in UTF-8, therefore looping through string's character will need to -48 (or -'0') to convert to real digit ('0' => 0)

Also, This function IGNORES ANY CHARACTER OTHER THAN 0-9 in characters, that means you typing any other character, for example abcdefg, will act as a COMMENT. If you type a line without any 0-9, decrypt function will return 0, meaning null. Therefore in the loop above, the outputCode make sure it is not null. Although g++ makes your code run even with null characters, I don't want to risk it and g++ gives a warning if you put a null character.

    

    system(compileCommand.c_str());

    while(outputCode.is_open()){}

    if(!keepcpp) remove(outputFilename.c_str());

    outputFilename="./"+outputFilename;

    system(outputFilename.substr(0, outputFilename.length()-4).c_str());

    return 0;


I am using macOS, therefore my compileCommand is

std::string compileCommand="g++ "+outputFilename+" -o "+outputFilename.substr(0, outputFilename.length()-4);

(I included .cpp suffix into outputFilename, therefore substring the outputFilename (b.cpp => b))

I don't actually know if this is true, but I'm adding a loop checking if outputCode is open, if it is then loop until it isn't open. I figure it would work since g++ would have to read the cpp code somehow. Therefore I added that loop to make sure g++ is finished compiling, or finish reading the cpp file.

After cpp is closed, I check if --keepcpp flag is true, if not, then i will remove the .cpp file.

At last, I add "./" in front of .cpp file name. and console run the binary. If the outputFilename is b.cpp, it would become ./b.cpp then ./b, which execute the binary.

Interpreter Usage:

In this example, the interpreter name is "goofylang".

    ./goofylang a.goofy b

This will take "a.goofy" file as input file path (GoofyLang) and "b" as executable/binary name (If keepcpp is on, the .cpp file will be called "b.cpp" in this example)

The Full Compiler in c++ source code: (MacOS only, If windows or linux, use your own brain)

(Does not need external dependencies)

#include <iostream>

#include <fstream>

#include <stdio.h>

int decrypttable[10]{4, 3, 5, 0, 6, 2, 7, 1, 9, 8};

int decrypt(std::string& s){//(int)'0' = 48, (int)'9' = 57

    int result=0;

    for(int c:s){

        if(48<=c&&c<=57){//ignore characters, act as comment

            result=result*10+decrypttable[(c-48)];

        }

    }

    return result;

}

int main(int argc, const char * argv[]) {

    if(argc != 3 && argc != 4){

        std::cout<<"Usage: "<<argv[0]<<" inputfile.goofy outputfilename (optional)--keepcpp"<<std::endl;

        return 1;

    }

    std::string inputFilepath(argv[1]);

    std::string outputFilename(argv[2]);

    outputFilename+=".cpp";

    std::string arg3="";

    if(argc==4) arg3=argv[3];

    bool keepcpp=false;

    if(arg3=="--keepcpp") keepcpp=true;

    std::cout<<"[INFO] keepcpp = "<<std::boolalpha<<keepcpp<<std::endl;

    std::cout << "[INFO] Input Filename=\""<<inputFilepath<<"\"\n";

    if(keepcpp) std::cout << "[INFO] Output Filename=\""<<outputFilename<<"\"\n";

    std::cout<<"[INFO] Binary Filename=\""<<outputFilename.substr(0, outputFilename.length()-4)<<"\"\n";

    

    std::string line;

    std::string compileCommand="g++ "+outputFilename+" -o "+outputFilename.substr(0, outputFilename.length()-4);

    

    std::ifstream inputFile(inputFilepath.c_str());

    std::ofstream outputCode(outputFilename.c_str());

    if (inputFile.is_open() && outputCode.is_open()){

        while (inputFile>>line){

            char c=decrypt(line);

            if(c!=0) outputCode<<c;

        }

        inputFile.close();

        outputCode.close();

    }else{

        std::cout << "[ERROR] Unable to open file\n";

    }

    system(compileCommand.c_str());

    while(outputCode.is_open()){}

    if(!keepcpp) remove(outputFilename.c_str());

    outputFilename="./"+outputFilename;

    system(outputFilename.substr(0, outputFilename.length()-4).c_str());

    return 0;

}


Example GoofyLang script

This will output "Hello, World!" in console:

12
732
773
88
739
776
733
737
15
43
732
777
772
774
770
737
86
738
45
73
732
773
774
15
738
86
732
773
03
07
751
772
774
733
29
29
88
777
776
774
15
43
43
15
10
65
737
739
739
777
00
15
96
777
770
739
733
11
85
773
10
28
770
737
774
776
770
773
15
09
28
752

Since I WHOLY MADE THIS LANGUAGE, YOU HAVE TO FOLLOW THESE RULES:

  1. YOU CANNOT DIRECTLY TRANSLATE C++ CODE INTO GOOFYLANG!!!!!! YOU HAVE TO WRITE THEM BY HAND!!!! IF YOU DO, UR CHEATING AND THATS BREAKING THE RULES!!!
  2. YOU CAN ADMIRE MY ENCRYPT FUNCTION, BUT DO NOT USE IT!!!!!


map<int, int> m{{1, 7}, {2, 5}, {3, 1}, {4, 0}, {5, 2}, {6, 4}, {7, 6}, {8, 9}, {9, 8}, {0, 3}};

void encrypt(string s){

    for(int c:s){

        //cout<<c;

        string final;

        string lmfao=to_string(c);

        for(char huh:lmfao){

            final+=(char)(m[huh-'0']+'0');//m = my encrypt map

        }

        cout<<final<<endl;

    }

}


EDIT: I could use a normal int array since its just 0-9. Used int arr in interpreter.

Tips for you

  1. If you have code that's not working, type -keepcpp as the third argument of the interpreter and you might find some errors!


GoofyLang is heavily inspired by BrainFuck, a fellow ABSOLUTELY USELESS GODTIER INTERPRETED LANGUAGE. This whole project is just a proof of concept. If you are wondering what am I proofing here exactly, Don't ask me because even I don't know. Maybe because I am silly enough to write my own programming language.

btw i code in goofylang

#goofylang>python

Comments

  1. Nice Language!!!!!!!!! I will use GoofyLang to program every single thing!!!!!!!

    ReplyDelete

Post a Comment

Popular posts from this blog

GREAT NEWS!