DEV Community

Shrijith Venkatramana
Shrijith Venkatramana

Posted on

DBChat: Getting a Toy REPL Going in Golang (Part 2)

Hi there! I'm Shrijith Venkatrama, the founder of Hexmos. Right now, I’m building LiveAPI, a super-convenient tool that simplifies engineering workflows by generating awesome API docs from your code in minutes.

In this tutorial series, I am on a journey to build for myself DBChat - a simple tool for using AI chat to explore and evolve databases.

See previous posts to get more context:

  1. Building DBChat - Explore and Evolve Your DB with Simple Chat (Part 1)

Kicking Off The DBChat Project

The first step is to start a GoLang project:

go mod init dbchat
mkdir -p cmd/dbchat pkg
touch cmd/dbchat/main.go
Enter fullscreen mode Exit fullscreen mode

Then let's create a skeleton cmd/dbchat/main.go like this:

package main

import (
    "fmt"
    "log"
)

func main() {
    log.Println("Starting dbchat application...")
    fmt.Println("Welcome to dbchat!")
}
Enter fullscreen mode Exit fullscreen mode

Also, let's create a convenience Makefile at the project root to build, run and clean go artifacts out of our source:

# Binary name
BINARY_NAME=dbchat

# Build directory
BUILD_DIR=build

# Go build flags
BUILD_FLAGS=-v

.PHONY: build run clean

# Build the application
build:
    @echo "Building ${BINARY_NAME}..."
    @mkdir -p ${BUILD_DIR}
    @go build ${BUILD_FLAGS} -o ${BUILD_DIR}/${BINARY_NAME} ./cmd/dbchat

# Run the application
run:
    @go run ./cmd/dbchat

# Clean build artifacts
clean:
    @echo "Cleaning..."
    @rm -rf ${BUILD_DIR} 
Enter fullscreen mode Exit fullscreen mode

Running dbchat for Testing Purposes

make run
Enter fullscreen mode Exit fullscreen mode

Building dbchat to Get a Binary Output

make build
cd build
./dbchat
Enter fullscreen mode Exit fullscreen mode

And we get this nice output:

2025/01/10 22:51:37 Starting dbchat...
Welcome to dbchat!
Enter fullscreen mode Exit fullscreen mode

Implementing a Basic REPL Loop

The next thing we want to do is - get a basic REPL going. In simpler words - you can call it an "interactive shell".

Usually, in C, et-cetera, the readline library within a loop is how it's achieved.

In Golang, I was shopping around for any helper libraries that can save our time from implementing our own

REPL from scratch.

go-repl

Something that looks sort of good for our purposes is go-repl

bc83f4cf-766b-46a8-a616-1dff76ba91bc

As you can see - it gives us many "goodies" which we are used to in a typical shell environment. Let's plug it in for now,

if necessary we will change later.

Getting Started with go-repl

Turns out the original repo is at: https://github.com/OpenEngineer/go-repl

And we find an example in the the README in that repo.

So I make a adaption, and turn my main.go to look something like this:

package main

import (
    "fmt"
    "log"
    "strings"

    repl "github.com/openengineer/go-repl"
)

const banner = `
██████╗ ██████╗  ██████╗██╗  ██╗ █████╗ ████████╗
██╔══██╗██╔══██╗██╔════╝██║  ██║██╔══██╗╚══██╔══╝
██║  ██║██████╔╝██║     ███████║███████║   ██║   
██║  ██║██╔══██╗██║     ██╔══██║██╔══██║   ██║   
██████╔╝██████╔╝╚██████╗██║  ██║██║  ██║   ██║   
╚═════╝ ╚═════╝  ╚═════╝╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝   
`

var helpMessage = `Available commands:
help     display this message
hello    get a friendly greeting
quit     quit DBChat`

// implements repl.Handler interface
type DBChatHandler struct {
    r *repl.Repl
}

func main() {
    log.Println("Starting dbchat...")
    fmt.Println(banner)
    fmt.Println("Welcome to DBChat! Type 'help' for available commands.")

    h := &DBChatHandler{}
    h.r = repl.NewRepl(h)

    // start the terminal loop
    if err := h.r.Loop(); err != nil {
        log.Fatal(err)
    }
}

func (h *DBChatHandler) Prompt() string {
    return "dbchat> "
}

func (h *DBChatHandler) Tab(buffer string) string {
    return "" // do nothing for now
}

func (h *DBChatHandler) Eval(line string) string {
    fields := strings.Fields(line)

    if len(fields) == 0 {
        return ""
    }

    cmd, _ := fields[0], fields[1:]
    switch cmd {
    case "help":
        return helpMessage
    case "hello":
        return "Hello! Welcome to DBChat! 👋"
    case "quit":
        h.r.Quit()
        return "Goodbye! 👋"
    default:
        return fmt.Sprintf("Unknown command: %s\nType 'help' for available commands.", cmd)
    }
}

Enter fullscreen mode Exit fullscreen mode

Toy REPL Demo

Toy REPL Demo

I'm Impressed With How Productive One Can Be With GoLang

So this whole thing took around 45 minutes - from nothing to a GoLang based toy REPL - with nice Makefile based automations. I chose GoLang for this exact reason - simple syntax, restrained feature-set, good standard libraries, and extensive open source libraries as well.

For example, the REPL library I am using doesn't seem like a major project - but it is so helpful to just get started with something rather than build everything ourselves to get something going.

Next Up

In the next post, I will try to tweak the REPL more towards the DBChat concept. Maybe I will focus on connecting to the database (postgresql first), and try to get an export of the entire schema.

Top comments (0)