DEV Community

Cover image for Implementing Functions (Pogo Pt:10)
Chig Beef
Chig Beef

Posted on • Updated on

Implementing Functions (Pogo Pt:10)

Intro

In this series I am creating a transpiler from Python to Golang called Pogo. In the last post we did some work to make sure that incorrect code didn't get translated into Go through the use of semantic analysis. This time we will be implementing functions.

Changing the Parser

I'm going to skip over the lexer since it's just adding the keyword def and that's about it. But with the parser we have a major change, which will be the property called functions, which will hold all the functions (other than main) in the program.

else if p.curToken.code == tokenCode["K_DEF"] {
    s = Structure{[]Structure{}, structureCode["ST_FUNCTION"], "ST_FUNCTION"}
    s.children = append(s.children, Structure{[]Structure{}, structureCode["K_DEF"], "def"})
    p.nextToken()

    temp, err := p.checkToken("IDENTIFIER")
    if err != nil {
        return s, err
    }
    temp.code = structureCode["FUNC_NAME"]
    s.children = append(s.children, temp)
    p.nextToken()

    temp, err = p.checkToken("L_PAREN")
    if err != nil {
        return s, err
    }
    s.children = append(s.children, temp)

    p.nextToken()

    for p.curToken.code == tokenCode["IDENTIFIER"] {
        temps, err := p.checkTokenRange([]string{
            "IDENTIFIER",
            "COLON",
            "IDENTIFIER",
        })
        if err != nil {
            return s, err
        }
        s.children = append(s.children, temps...)

        temp, err = p.checkToken("SEP")
        if err != nil {
            break
        }
        s.children = append(s.children, temp)
        p.nextToken()
    }

    temp, err = p.checkToken("R_PAREN")
    if err != nil {
        return s, err
    }
    s.children = append(s.children, temp)
    p.nextToken()

    temps, err := p.checkTokenRange([]string{
        "COLON",
        "NEWLINE",
    })
    if err != nil {
        return s, err
    }
    s.children = append(s.children, temps[0])

    temp, err = p.block()
    if err != nil {
        return s, err
    }
    s.children = append(s.children, temp)

    p.functions = append(p.functions, s)

    return Structure{[]Structure{}, structureCode["NEWLINE"], "\n"}, nil

}
Enter fullscreen mode Exit fullscreen mode

Most of this would look pretty standard except for the ending. Instead of adding this Structure to the program, we add it to functions and replace it with a newline (placeholder token). Now that we should have a bunch of functions in functions we need to apply them correctly. We also need to make the main function smarter, and make it into a function logically instead of just being text manipultion.

Creating the Main Function

main_func := Structure{
    []Structure{
        {[]Structure{}, structureCode["K_DEF"], "def"},
        {[]Structure{}, structureCode["FUNC_NAME"], "main"},
        {[]Structure{}, structureCode["L_PAREN"], "("},
        {[]Structure{}, structureCode["R_PAREN"], ")"},
        {[]Structure{}, structureCode["COLON"], ":"},
        {append(ast.children, Structure{[]Structure{}, structureCode["ANTI_COLON"], ":"}), structureCode["BLOCK"], ""},
    },
    structureCode["ST_FUNCTION"],
    "ST_FUNCTION",
}
ast.children = []Structure{main_func}
ast.children = append(ast.children, parser.functions...)
Enter fullscreen mode Exit fullscreen mode

My least favorite design decision of the Structure is having to explicitly make a slice of Structures every time I'm making one. At some point I'll probably cave in and make a function to make this easier, unless I find a better way.

The last line is all we need to add all the functions we found into the program.

Running our Compiler

Now there were a few extra steps such as making ST_CALL and so on but I didn't find them that important (especially since it's so similar to IB_PRINT. But now lets run some code!

from GoType import *

def blah():
    print("Hello")
blah()
Enter fullscreen mode Exit fullscreen mode

This turns into the following code.

package main

func main() {

    blah()
}
func blah() { println("Hello") }
Enter fullscreen mode Exit fullscreen mode

Honestly some weird formatting choices were made but other than that our compiler is functional, so I can't really complain too much.

Next

Currently, our errors are looking kind of bland, we should probably make them nicer by adding line numbers and which function threw the error [thanks Joshua (:]. In the end we want errors that are useful for the compiler designer, and the user.
You may have noticed that we didn't change the semantic analyzer, which is a huge oversight since any variables we have as function parameters won't be seen as declared, throwing an error. In a future post we will be working on fixing that issue.

Top comments (0)