Returning From Functions (Pogo Pt:14)
Tags: python, go, compiler, transpiler
Published: 2024-01-30 02: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 I thought we were mostly finished with functions, then I forgot that most of the time we want to return a value from a function. Not having this functionality seems kind of like cheating, so we're going to implement that right now.

## Function Typing
Lucky for us, return types are available in Python's type annotations, so we can add that in.
```python
def test(x: int, y: int) -> None:
```
And we also have to add `None` because we forgot to add that in (my bad). So now we have 2 tokens to add, `None`, and `->`. Furthermore, we have to add return statements (which I think is mostly done), and the ability to use calls as any literal would be able to be used.

## Lexing
Adding `None` won't be that hard in the lexer, neither will the arrow.
```golang
if word == "None" {
    token = Token{tokenCode["L_NULL"], word, l.line}
}
```
```golang
else if l.curChar == '-' {
    if l.peek() == '>' {
        l.nextChar()
        token = Token{tokenCode["ARROW"], "->", l.line}
    } else {
        token = Token{tokenCode["MO_SUB"], "-", l.line}
    }
}
```
Of course, we have to make sure that we are looking at an arrow, and not just the subtract sign.

## Parsing
In the parser, it should be mandatory to express what type a function returns (void functions return `None`). We currently only need to change the definition. The arrow and type appear before the colon and after the right bracket, so we need to add these lines in-between the checking for these tokens.
```golang
temps, err := p.checkTokenRange([]string{
    "R_PAREN",
    "ARROW",
})
if err != nil {
    return s, err
}
s.children = append(s.children, temps...)
p.nextToken()

temp, err = p.checkTokenChoices([]string{
    "IDENTIFIER",
    "L_NULL",
})
if err != nil {
    return s, err
}
s.children = append(s.children, temp)
p.nextToken()
```

## Modularizing Calls
Since we are going to be using calls in more places than one we should make the logic for calls another function, similar to `if` statements. This is simply a copy paste job.

## Using Calls
Now that we have modularized calls we can start using them in places such as declarations. We're going to have to work on the parser for this one.
```golang
p.setMarker()
temp, err := p.call()
if err != nil {
    p.gotoMarker()
    temp, err = p.checkTokenChoices([]string{
        "L_BOOL",
        "L_INT",
        "L_STRING",
    })
    if err != nil {
        return s, err
    }
}
s.children = append(s.children, temp)
```
We're making great use of `setMarker` here. Now we need to fix our semantic analyzer, as we're getting an error whenever we use a call here.

## Semantic Analysis
First I had to give `Function`s a `varType`, so we can keep our types in check (`print` can be given the `None` type). If you remember from last post we made a bit of a switch statement to deal with what could be in the call if the current `Structure` isn't an identifier. Calls start with a function name, which is different from an identifier, so we need to make a new case for that, and well, deal with it. I accidentally fixed it for declarations (without type-checking) so now we are going to put calls in calls.
```golang
case structureCode["ST_CALL"]:
    var insideCall Function

    name := s.children[i].children[1].text
    var valid bool
    for i := 0; i < len(funcs); i++ {
        valid = false
        if name == funcs[i].name {
            valid = true
            insideCall = funcs[i]
            break
        }
    }
    if !valid {
        return createError([]string{"analyze.go", "analyze:ST_CALL"}, "An attempt to call the non-existent function ""+s.children[0].text+"" was made", s.line)
    }

    if fn.params[pIndex] != insideCall.varType {
        return createError([]string{"analyze.go", "analyze:ST_CALL"}, """+s.children[0].text+"" is the wrong type, expected "+fn.params[pIndex]+" got "+insideCall.varType, s.line)
    }
```
We first have to check whether we are making a valid call, then we can type check, which turned out to be pretty simple.

## Back In The Parser
Of course, because of how I've done this we're back in the parser so we can implement calls in calls.
```golang
if p.peek().code == tokenCode["L_PAREN"] { // We're dealing with a call
    temp, err = p.call()
    if err != nil {
        return s, err
    }
}
```

## Test Case
Let's give our compiler some Python, and see what it manages to do.
```python
from GoType import *

def printNum(x: int) -> None:
    print(x)

def someNum() -> int:
    return 7

printNum(someNum())
```
Here we are just printing the number 7 but in a complicated way to test our compiler, and here is what it managed to get from it.
```golang
package main

func printNum(x int) { println(x) }
func someNum() int   { return 7 }
func main() {
    
    printNum(someNum())
}
```
That's pretty good, except for the formatting though, as always (did `gofmt` really just set the blocks of `printNum` and `someNum` to the same column??). Now we have to do a small error check of types, and we don't get the result we want. If we change the type `someNum` returns to `string` and change nothing else we don't get a compiler error, which leads to incorrect Go code, so we definitely need to be fixing this. The issue to this was honestly just a few bugs from my bad code, so nothing to hard to fix.

## Next
I know I said something about classes in the last post but then I realized that I still had quite a bit of work to do on functions, and with it being really close to the end of the month it definitely isn't feasible to add classes now. I honestly don't know what to add, I don't think an optimizer is a good idea because it doesn't create Go from Python as exactly, which is what we want. I do have to do type checking for calls in declarations still, and their use in expressions. Furthermore, comparisons should be able to be a single literal or identifier, which would allow code such as `if valid:`. I think small clean ups will be the theme of the next post, just to make the compiler as a whole a bit more usable, instead of adding a bunch of new features.