Please, help a nube to write code "GoWay"

Hi there!
Sorry for my grammar, English is my second language.
Just start learning go a week ago.
Before that C were my choice, basically because it is so fundamental, and has no dependencies.
But it was before i’ve got my first real world “problem”.
Go is great, in any way, now its my language of choice, although i miss some bracketless C constructions.
Now I’m working with simple tool that should parse lookup table from .cube file and after that, apply color corrections from it to an image. I were have to produce some results fast, and have done some thing, it is kinda working on some simple tests, though now I have some time to learn how to write code that would be readable and may be in canonical style.
I know it is a lot of “s###” ) and I’m not going to cry )) please be cruel, it is only part one).
The “Parser” :
‘’’

type CubeLUT struct {
	title  string
	form   string
	size   int
	domain [6]float64
	data   []float64
}

func (cl *CubeLUT) Init(filename string) *CubeLUT {
    	var (
    		s   scanner.Scanner
    		f   *os.File
    		err error
    	)
    	if f, err = os.Open(filename); err != nil {
    		log.Fatal(err)
    	} else {
    		s.Init(f)
    		s.Whitespace ^= 1 << '\n' //don't skip new line
    	}
    	defer f.Close()
    	m := make(map[string]string)
    	d := make([]float64, 3)
    	for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
    		switch s.TokenText() {
    		case "TITLE", "LUT_1D_SIZE", "LUT_3D_SIZE":
    			key := s.TokenText()
    			tok = s.Scan()
    			m[key] = s.TokenText()
    		case "\n":
    		case "#":            \\skip comments
    			for s.TokenText() != "\n" {
    				tok = s.Scan()
    			}
    		case "DOMAIN_MIN", "DOMAIN_MAX":
    			key := s.TokenText()
    			for i := 0; i < 3; i++ {
    				keyi := key + [3]string{"_R", "_G", "_B"}[i]
    				tok = s.Scan()
    				m[keyi] = s.TokenText()
    			}
    		default:
    			p := [3]float64{}
    			for i := 0; i < 3; i++ {
    				p[i], err = strconv.ParseFloat(s.TokenText(), 64)
    				if err != nil {
    					log.Fatal(err)
    				}
    				tok = s.Scan()
    			}
    			d = append(d, p[:]...)
    		}
    	}
    	cl.data = d[3:]
    	cl.title = "undefined"
    	if v, ok := m["TITLE"]; ok == true {
    		cl.title = v
    	}
    	cl.form = "undefined"
    	m[cl.form] = "0"
    	if _, ok := m["LUT_1D_SIZE"]; ok == true {
    		cl.form = "LUT_1D_SIZE"
    	}
    	if _, ok := m["LUT_3D_SIZE"]; ok == true {
    		cl.form = "LUT_3D_SIZE"
    	}
    	if s, err := strconv.Atoi(m[cl.form]); err != nil {
    		log.Fatal(err)
    	} else {
    		cl.size = s
    	}
    	for j := 0; j < 2; j++ {
    		key := [2]string{"DOMAIN_MIN", "DOMAIN_MAX"}[j]
    		for i := 0; i < 3; i++ {
    			keyi := key + [3]string{"_R", "_G", "_B"}[i]
    			if v, ok := m[keyi]; ok == true {
    				d, err := strconv.ParseFloat(v, 64)
    				if err != nil {
    					log.Fatal(err)
    				} else {
    					cl.domain[j*3+i] = d
    				}

    			} else {
    			 cl.domain[j*3+i] = [6]float64{0, 0, 0, 1, 1, 1}[j*3+i]
    		}
    	}
   }

‘’’

At least should I use scanner for that kind of things?

You provide some code that solves an undefined problem. There is no specification. There are no examples. You are assuming that expert Go programmers are also experts in cube files and LUT. Really! You may not get many answers.

Write code in three passes. In the first pass, focus on writing correct, readable code, ignoring, as much as possible, errors and exceptions. This is the main or “happy” path. In the second pass, make sure all errors and exceptions are handled. In the third pass, provide good error and exception reporting.

Here’s a first draft of the first pass, based on the Cube LUT Specification, Version 1.0.

package main

import (
	"bufio"
	"io"
	"os"
	"strconv"
	"strings"
)

type RGB [3]float32

type CubeLUT struct {
	Title     string
	Form      string
	Size      int
	DomainMin RGB
	DomainMax RGB
	Data      []RGB
}

func LoadCubeLUTFile(filename string) (*CubeLUT, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	lut, err := readCubeLUT(f)
	if err != nil {
		return nil, err
	}
	return lut, nil
}

func readRGB(fields []string) (RGB, error) {
	var rgb RGB
	for i, field := range fields {
		f, err := strconv.ParseFloat(field, 32)
		if err != nil {
			return RGB{}, err
		}
		if i < len(rgb) {
			rgb[i] = float32(f)
		}
	}
	return rgb, nil
}

func readCubeLUT(r io.Reader) (*CubeLUT, error) {
	lut := CubeLUT{
		DomainMin: RGB{0, 0, 0},
		DomainMax: RGB{1, 1, 1},
	}

	s := bufio.NewScanner(r)
	for s.Scan() {
		line := s.Text()
		if strings.HasPrefix(line, "#") {
			continue
		}
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			continue
		}

		fields := strings.Fields(line)

		keyword := fields[0]
		switch keyword {
		case "TITLE":
			title := strings.TrimSpace(line[len(keyword):])
			title = strings.TrimPrefix(title, `"`)
			title = strings.TrimSuffix(title, `"`)
			lut.Title = title

		case "LUT_1D_SIZE":
			lut.Form = "1D"
			if len(fields) > 1 {
				n, err := strconv.ParseUint(fields[1], 10, 16)
				if err != nil {
					return nil, err
				}
				lut.Size = int(n)
				lut.Data = make([]RGB, 0, n)
			}

		case "LUT_3D_SIZE":
			lut.Form = "3D"
			if len(fields) > 1 {
				n, err := strconv.ParseUint(fields[1], 10, 8)
				if err != nil {
					return nil, err
				}
				lut.Size = int(n)
				lut.Data = make([]RGB, 0, n*n*n)
			}

		case "DOMAIN_MIN":
			rgb, err := readRGB(fields[1:])
			if err != nil {
				return nil, err
			}
			lut.DomainMin = rgb

		case "DOMAIN_MAX":
			rgb, err := readRGB(fields[1:])
			if err != nil {
				return nil, err
			}
			lut.DomainMax = rgb

		default:
			rgb, err := readRGB(fields)
			if err != nil {
				return nil, err
			}
			lut.Data = append(lut.Data, rgb)
		}

	}
	if err := s.Err(); err != nil {
		return nil, err
	}

	return &lut, nil
}

func main() {}
1 Like

This is great advice, definitely will use. Thank you!

Sorry for that :slightly_smiling_face:
It is actually my first post…
I didn’t expect any one would write another version of that. The very top expectations were that some one would say some thing like: “here is fine, there is no good, google scanners google stringers…”

@petrus one last thing, could you please write a word or two about scanners. Why did you use bufio in this example instead text scanner or fmt scanf ?