Implementing Functions (Pogo Pt:10)
Tags: python, go, compiler, transpiler
Published: 2024-01-26 01:30:00

## Intro
In this series I am creating a transpiler from Python to Golang called [Pogo](https://github.com/Chig-Beef/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.
```golang
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

}
```
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
```golang
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...)
```
My least favorite design decision of the `Structure` is having to explicitly make a slice of `Structure`s 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!
```python
from GoType import *

def blah():
    print("Hello")
blah()
```
This turns into the following code.
```golang
package main

func main() {
    
    blah()
}
func blah() { println("Hello") }
```
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.