package main

import (
    "fmt"
    "os"    
    "regexp"
    "strings"
    "strconv"
)

var register_map = map[string]int{
    "A": 0,
    "X": 1,
    "L": 2,
    "B": 3,
    "S": 4,
    "T": 5,
    "F": 6,
}

var ukazna_tabela = map[string]int{
    "ADD": 0x18, 
    "ADDF": 0x58,
    "ADDR": 0x90, 
    "AND": 0x40, 
    "CLEAR": 0xB4,
    "COMP": 0x28,
    "COMPF": 0x88,
    "COMPR": 0xA0,
    "DIV": 0x24, 
    "DIVF": 0x64,
    "DIVR": 0x9C, 
    "FIX": 0xC4, 
    "FLOAT": 0xC0,
    "HIO": 0xF4, 
    "J": 0x3C, 
    "JEQ": 0x30, 
    "JGT": 0x34, 
    "JLT": 0x38,
    "JSUB": 0x48,
    "LDA": 0x00, 
    "LDB": 0x68, 
    "LDCH": 0x50,
    "LDF": 0x70,
    "LDL": 0x08, 
    "LDS": 0x6C,
    "LDT": 0x74,
    "LDX": 0x04,
    "LPS": 0xD0,
    "MUL": 0x20,
    "MULF": 0x60,
    "MULR": 0x98,
    "NORM": 0xC8, 
    "OR": 0x44,
    "RD": 0xD4,
    "RMO": 0xAC,
    "RSUB": 0x4C,
    "SHIFTL": 0xA4,
    "SHIFTR": 0xA8,
    "SIO": 0xF0,
    "SSK": 0xEC, 
    "STA": 0x0C,
    "STB": 0x78, 
    "STCH": 0x54,
    "STF": 0x80,
    "STI": 0xD4,
    "STL": 0x14,
    "STS": 0x74, 
    "STSW": 0xE8, 
    "STT": 0x84, 
    "STX": 0x10, 
    "SUB": 0x1C, 
    "SUBF": 0x5C, 
    "SUBR": 0x94, 
    "SVC": 0xB0, 
    "TD": 0xE0, 
    "TIO": 0xF8, 
    "TIX": 0x2C, 
    "TIXR": 0xB8,
    "WD": 0xDC,
}

var simbolna_tabela = map[string]int{}

var ime_programa string

var lc_global int

var bazno_dovoljeno bool

var bazno_naslov int

var naslov_programa int

var zacetni_naslov int

type M_zapis struct {
    odmik int
    dolzina int
}

var M_tabela []M_zapis

type ukaz interface {
    Velikost() int
    GetAddr() int
}

func (f *format_F1) Velikost() int { return f.velikost }
func (f *format_F2_n) Velikost() int { return f.velikost }
func (f *format_F2_r) Velikost() int { return f.velikost } 
func (f *format_F2_rn) Velikost() int { return f.velikost }
func (f *format_F2_rr) Velikost() int { return f.velikost }
func (f *format_F3_m) Velikost() int { return f.velikost }
func (f *format_F4_m) Velikost() int { return f.velikost }
func (f *format_D) Velikost() int { return f.velikost }
func (f *format_D_n) Velikost() int { return f.velikost }
func (f *format_M_i) Velikost() int { return f.velikost }
func (f *format_M_r) Velikost() int { return f.velikost }

func (f *format_F1) GetAddr() int { return f.lc }
func (f *format_F2_n) GetAddr() int { return f.lc }
func (f *format_F2_r) GetAddr() int { return f.lc } 
func (f *format_F2_rn) GetAddr() int { return f.lc }
func (f *format_F2_rr) GetAddr() int { return f.lc }
func (f *format_F3_m) GetAddr() int { return f.lc }
func (f *format_F4_m) GetAddr() int { return f.lc }
func (f *format_D) GetAddr() int { return f.lc }
func (f *format_D_n) GetAddr() int { return f.lc }
func (f *format_M_i) GetAddr() int { return f.lc }
func (f *format_M_r) GetAddr() int { return f.lc }

var register string = `A|X|L|B|S|T|F`

type format_F1 struct {
    oznaka string
    opcode int 
    format int // F1 - 1, F2 - 2, F3 - 3, F4 - 4, SIC - 5
    velikost int //v bajtih
    lc int
}

type format_F2_n struct {
    oznaka string
    opcode int
    format int // 2
    velikost int
    n string
    lc int
}

type format_F2_r struct {
    oznaka string
    opcode int
    format int // 6
    velikost int
    r1 string
    lc int
}

type format_F2_rn struct {
    oznaka string
    opcode int
    format int // 2
    velikost int
    r1 string
    n string
    lc int
}

type format_F2_rr struct {
    oznaka string
    opcode int
    format int // 2
    velikost int
    r1 string
    r2 string
    lc int
}

type format_F3_m struct {
    oznaka string
    opcode int
    format int // 3
    velikost int
    naslov string
    odmik int
    x bool
    b bool
    p bool 
    e bool
    n bool
    i bool
    lc int
}

type format_F4_m struct {
    oznaka string
    opcode int 
    format int // 4
    velikost int
    naslov string
    odmik int
    x bool
    b bool
    p bool 
    e bool
    n bool
    i bool
    lc int
}

type format_D struct {
    oznaka string
    ime string //NOBASE
    format int // 6
    velikost int
    lc int
}

type format_D_n struct {
    oznaka string
    ime string
    format int // 7 
    velikost int
    operand string
    lc int
}

type format_M_r struct {
    oznaka string
    ime string
    format int // 8 
    velikost int
    operand string  
    rezervacija int
    lc int
}

type format_M_i struct {
    oznaka string
    ime string
    format int // 9
    velikost int
    operand string
    lc int
}
 
func remove_comments_and_empty_lines(el string) bool {
    match_pure_comment, err := regexp.MatchString(`(?m)^[\t ]*\..*$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_pure_comment {
        return true
    }
    match_empty, err := regexp.MatchString(`(?m)^[\t ]*$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_empty {
        return true
    }
    return false
}

func check_F1(el string, AST *[]ukaz) bool {
        match_F1, err := regexp.MatchString(`(?m)^[A-Za-z_]*[\t ]+[A-Za-z]+[\t ]*(\..*)?$\n`, el)
        if err != nil {
            fmt.Println("Error while matching string.")
        }
        if match_F1 {
            var nov_ukaz format_F1
            nov_ukaz.format = 1
            nov_ukaz.velikost = 1
            pattern := regexp.MustCompile(`[\t ]+[A-Za-z]+`)
            mnemonik := strings.Trim(pattern.FindString(el), "\t ")
            val, ok := ukazna_tabela[mnemonik];  
            if !ok {
                fmt.Println("Error: Opcode not found.")
                return false
            }
            nov_ukaz.opcode = val
            //fmt.Println("F1: ", mnemonik, nov_ukaz.opcode)
            *AST = append(*AST, &nov_ukaz)
            return true
        }  
        return false
}

func check_F2_n(el string, AST *[]ukaz) bool {
    match_F2_n, err := regexp.MatchString(`(?m)^[A-Za-z_]*[\t ]+SVC ([0-9]+)[\t ]*(\..*)?$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_F2_n {
        var nov_ukaz format_F2_n
        nov_ukaz.format = 2
        nov_ukaz.velikost = 2
        nov_ukaz.opcode = 10
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Z-a-z_]+`) 
            nov_ukaz.oznaka = re.FindString(el)   
        } else {
            nov_ukaz.oznaka = ""
        }
        re_par := regexp.MustCompile(`[\t ]+[0-9]+,?`)
        temp := strings.Trim(re_par.FindString(el), "\t ")
        if temp[len(temp) - 1] == ',' {
            nov_ukaz.n = strings.TrimRight(temp, ",")
        } else {
            nov_ukaz.n = temp
        }
        //fmt.Println("F2_n ", el[:len(el) - 1], nov_ukaz)
        *AST = append(*AST, &nov_ukaz)
        return true
    }
    return false
}

func check_F2_r(el string, AST *[]ukaz) bool {
    match_F2_r, err := regexp.MatchString(`(?m)^[A-Za-z_]*[\t ]+[A-Za-z]+ (`+register+`)[\t ]*(\..*)?$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_F2_r {
        var nov_ukaz format_F2_r
        nov_ukaz.format = 2
        nov_ukaz.velikost = 2
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Z-a-z_]+`) 
            nov_ukaz.oznaka = re.FindString(el)   
        } else {
            nov_ukaz.oznaka = ""
        }       
        re_op := regexp.MustCompile(`[\t ]+[A-Za-z]+`)
        val, ok := ukazna_tabela[strings.Trim(re_op.FindString(el), "\t ")]
        if !ok {
            fmt.Println("Error: Opcode not found.")
            return false
        }
        nov_ukaz.opcode = val
        re_par := regexp.MustCompile(`[\t ]+[A-Za-z]+ (`+register+`)`)
        nov_ukaz.r1 = strings.Split(strings.Trim(re_par.FindString(el), "\t \n"), " ")[1]
        //fmt.Println(nov_ukaz)
        *AST = append(*AST, &nov_ukaz)
        return true
    }
    return false
}

func check_F2_rn(el string, AST *[]ukaz) bool {
    match_F2_rn, err := regexp.MatchString(`(?m)^[A-Za-z_]*[\t ]+[A-Za-z]+ (`+register+`), ([0-9]+)[\t ]*(\..*)?$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_F2_rn {
        var nov_ukaz format_F2_rn
        nov_ukaz.format = 2
        nov_ukaz.velikost = 2
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Z-a-z_]+`) 
            nov_ukaz.oznaka = re.FindString(el)   
        } else {
            nov_ukaz.oznaka = ""
        }       
        re_op := regexp.MustCompile(`[\t ]+[A-Za-z]+`)
        val, ok := ukazna_tabela[strings.Trim(re_op.FindString(el), "\t ")]
        if !ok {
            fmt.Println("Error: Opcode not found.")
            return false
        }
        nov_ukaz.opcode = val
        re_par1 := regexp.MustCompile(`[\t ]+[A-Za-z]+ (`+register+`)`)
        nov_ukaz.r1 = strings.Split(strings.Trim(re_par1.FindString(el), "\t \n"), " ")[1]
        re_par2 := regexp.MustCompile(`(`+register+`), ([0-9]+),?`)
        temp := strings.Split(re_par2.FindString(el), " ")[1]
        if temp[len(temp) - 1] == ',' {
            nov_ukaz.n = strings.TrimRight(temp, ",")
        } else {
            nov_ukaz.n = temp
        }
        //fmt.Println("F2_rn: ", el[:len(el) - 1], nov_ukaz)
        *AST = append(*AST, &nov_ukaz)
        return true
    }
    return false
}

func check_F2_rr(el string, AST *[]ukaz) bool {
    match_F2_rr, err := regexp.MatchString(`(?m)^[A-Za-z_]*[\t ]+[A-Za-z]+ (`+register+`), (`+register+`)[\t ]*(\..*)?$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_F2_rr {
        var nov_ukaz format_F2_rr
        nov_ukaz.format = 2
        nov_ukaz.velikost = 2
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Z-a-z_]+`) 
            nov_ukaz.oznaka = re.FindString(el)   
        } else {
            nov_ukaz.oznaka = ""
        }       
        re_op := regexp.MustCompile(`[\t ]+[A-Za-z]+`)
        val, ok := ukazna_tabela[strings.Trim(re_op.FindString(el), "\t ")]
        if !ok {
            fmt.Println("Error: Opcode not found.")
            return false
        }
        nov_ukaz.opcode = val
        re_par1 := regexp.MustCompile(`[\t ]+[A-Za-z]+ (`+register+`)`)
        nov_ukaz.r1 = strings.Split(strings.Trim(re_par1.FindString(el), "\t \n"), " ")[1]
        re_par2 := regexp.MustCompile(`(`+register+`), (`+register+`)`)
        nov_ukaz.r2 = strings.Split(re_par2.FindString(el), " ")[1]
        //fmt.Println("F2_rr: ", nov_ukaz)
        *AST = append(*AST, &nov_ukaz)
        return true
    }
    return false
}

func check_F3_m(el string, AST *[]ukaz) bool {
    match_F3_m, err := regexp.MatchString(`(?m)^[A-Za-z_]*[\t ]+[A-Za-z]+ ((#?|@?)[A-Za-z]+|(#?|@?)[0-9+]+)(, X)?[\t ]*(\..*)?$\n`, el)    
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_F3_m {
        var nov_ukaz format_F3_m
        nov_ukaz.format = 3
        nov_ukaz.velikost = 3
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Za-z]+`)
            nov_ukaz.oznaka = re.FindString(el)
        } else {
            nov_ukaz.oznaka = ""
        }
        re_op := regexp.MustCompile(`[\t ]+[A-Za-z]+`)
        val, ok := ukazna_tabela[strings.Trim(re_op.FindString(el), "\t ")]
        if !ok {
            fmt.Println("Error: Opcode not found.")
            return false
        }
        nov_ukaz.opcode = val
        // naslov
        re_par1 := regexp.MustCompile(`[\t ]+[A-Za-z]+ ((#?|@?)[A-Za-z]+|(#?|@?)[0-9+]+)`)
        nov_ukaz.naslov = strings.Split(strings.Trim(re_par1.FindString(el), "\t \n"), " ")[1]
        if nov_ukaz.naslov[0] == '@' {
            nov_ukaz.n = true
            nov_ukaz.i = false    
            nov_ukaz.naslov = nov_ukaz.naslov[1:len(nov_ukaz.naslov)]
        } else if nov_ukaz.naslov[0] == '#' {
            nov_ukaz.n = false
            nov_ukaz.i = true
            nov_ukaz.naslov = nov_ukaz.naslov[1:len(nov_ukaz.naslov)]
        } else {
            //temp
            nov_ukaz.n = true
            nov_ukaz.i = true
        }
        // indeksno naslavljanje
        re_x := regexp.MustCompile(`(, X)[\t ]*(\..*)?\n`)
        if re_x.FindString(el) != "" {
            nov_ukaz.x = true
        } else {
            nov_ukaz.x = false
        }
        nov_ukaz.e = false
        //fmt.Println(el, nov_ukaz)
        *AST = append(*AST, &nov_ukaz)
        return true
    }
    return false
}

func check_F4_m(el string, AST *[]ukaz) bool {
    match_F4_m, err := regexp.MatchString(`(?m)^[A-Za-z_]*[\t ]+\+[A-Za-z]+ ((#?|@?)[A-Za-z]+|(#?|@?)[0-9+]+)(, X)?[\t ]*(\..*)?$\n`, el)    
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_F4_m {
        var nov_ukaz format_F4_m
        nov_ukaz.format = 4
        nov_ukaz.velikost = 4
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Za-z]+`)
            nov_ukaz.oznaka = re.FindString(el)
        } else {
            nov_ukaz.oznaka = ""
        }
        re_op := regexp.MustCompile(`[\t ]+\+[A-Za-z]+`)
        // skip "+"
        val, ok := ukazna_tabela[strings.Trim(re_op.FindString(el), "\t +")]
        if !ok {
            fmt.Println("Error: Opcode not found.")
            return false
        }
        nov_ukaz.opcode = val
        // naslov
        re_par1 := regexp.MustCompile(`[\t ]+\+[A-Za-z]+ ((#?|@?)[A-Za-z]+|(#?|@?)[0-9+]+)`)
        nov_ukaz.naslov = strings.Split(strings.Trim(re_par1.FindString(el), "\t \n"), " ")[1]
        if nov_ukaz.naslov[0] == '@' {
            nov_ukaz.n = true
            nov_ukaz.i = false    
            nov_ukaz.naslov = nov_ukaz.naslov[1:len(nov_ukaz.naslov)]
        } else if nov_ukaz.naslov[0] == '#' {
            nov_ukaz.n = false
            nov_ukaz.i = true
            nov_ukaz.naslov = nov_ukaz.naslov[1:len(nov_ukaz.naslov)]
        } else {
            nov_ukaz.n = true
            nov_ukaz.i = true
        }
        // indeksno naslavljanje
        re_x := regexp.MustCompile(`(, X)[\t ]*(\..*)?\n`)
        if re_x.FindString(el) != "" {
            nov_ukaz.x = true
        } else {
            nov_ukaz.x = false
        }
        nov_ukaz.e = true
        //fmt.Println(el, nov_ukaz)
        *AST = append(*AST, &nov_ukaz)
        return true
    }
    return false
}
 
func check_D(el string, AST *[]ukaz) bool {
    match_D, err := regexp.MatchString(`(?m)^[A-Za-z_]*[\t ]+NOBASE[\t ]*(\..*)?$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_D {
        var nova_direktiva format_D
        nova_direktiva.ime = "NOBASE"
        nova_direktiva.format = 6
        nova_direktiva.velikost = 0
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Za-z]+`)
            nova_direktiva.oznaka = re.FindString(el)
        } else {
            nova_direktiva.oznaka = ""
        }
        *AST = append(*AST, &nova_direktiva)
        //fmt.Println(el, nova_direktiva)
        return true
    }   
    return false
}

func check_D_n(el string, AST *[]ukaz) bool {
    match_D_n, err := regexp.MatchString(`(?m)^[A-Za-z]*[\t ]+(START|END|ORG|EQU|BASE) ([A-Za-z\+\-\*]+|[0-9\+\-\*]+)[\t ]*(\..*)?$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }
    if match_D_n {
        var nova_direktiva format_D_n
        re_ime := regexp.MustCompile(`START|END|ORG|EQU|BASE`)
        nova_direktiva.ime = re_ime.FindString(el)
        nova_direktiva.format = 7
        nova_direktiva.velikost = 0 
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Za-z]+`)
            nova_direktiva.oznaka = re.FindString(el)
        } else {
            nova_direktiva.oznaka = ""
        }
        re_ope := regexp.MustCompile(`(START|END|ORG|EQU|BASE) ([A-Za-z\+\-\*]+|[0-9\+\-\*]+)`)
        nova_direktiva.operand = strings.Split(re_ope.FindString(el), " ")[1] 
        *AST = append(*AST, &nova_direktiva)
        //fmt.Println(el, nova_direktiva)
        return true
    }
    return false
}

func check_M_r(el string, AST *[]ukaz) bool {
    match_M_r, err := regexp.MatchString(`(?m)^[A-Za-z]*[\t ]+(RESW|RESB) ([0-9]+)[\t ]*(\..*)?$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }   
    if match_M_r {
        var nova_direktiva format_M_r
        re_ime := regexp.MustCompile(`RESW|RESB`)
        nova_direktiva.ime = re_ime.FindString(el)
        nova_direktiva.format = 8
        nova_direktiva.velikost = 0
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Za-z]+`)
            nova_direktiva.oznaka = re.FindString(el)
        } else {
            nova_direktiva.oznaka = ""
        }
        re_ope := regexp.MustCompile(`[0-9]+`)
        nova_direktiva.operand = re_ope.FindString(el)
        operand_int, _ := strconv.Atoi(nova_direktiva.operand)
        if nova_direktiva.ime == "RESW" {
            nova_direktiva.rezervacija = 3 * operand_int
        } else {
            nova_direktiva.rezervacija = operand_int
        } 
        //fmt.Println(el, nova_direktiva) 
        *AST = append(*AST, &nova_direktiva)
        return true
    }    
    return false
}

func check_M_i(el string, AST *[]ukaz) bool {
    match_M_i, err := regexp.MatchString(`(?m)^[A-Za-z]*[\t ]+(WORD|BYTE) ([0-9]+)[\t ]*(\..*)?$\n`, el)
    if err != nil {
        fmt.Println("Error while matching string.")
    }   
    if match_M_i {
        var nova_direktiva format_M_i
        re_ime := regexp.MustCompile(`WORD|BYTE`)
        nova_direktiva.ime = re_ime.FindString(el)
        nova_direktiva.format = 9
        if !(strings.HasPrefix(el, " ") || strings.HasPrefix(el, "\t")) {
            re := regexp.MustCompile(`(?m)^[A-Za-z]+`)
            nova_direktiva.oznaka = re.FindString(el)
        } else {
            nova_direktiva.oznaka = ""
        }
        re_ope := regexp.MustCompile(`[0-9]+`)
        nova_direktiva.operand = re_ope.FindString(el)
        if nova_direktiva.ime == "WORD" {
            nova_direktiva.velikost = 3
        } else {
            nova_direktiva.velikost = 1
        } 
        //fmt.Println(el, nova_direktiva) 
        *AST = append(*AST, &nova_direktiva)
        return true
    }    
    return false
}

func prvi_prehod(AST *[]ukaz) {
    lc_global, _ = strconv.Atoi((*AST)[0].(*format_D_n).operand)
    naslov_programa = lc_global
    ime_programa = (*AST)[0].(*format_D_n).oznaka 
    for _, el := range *AST {
        //fmt.Printf("lc_global '%x'\n", lc_global)                
        switch val := el.(type) {
            case *format_F1: val.lc = lc_global
                             if val.oznaka != "" {
                                simbolna_tabela[val.oznaka] = lc_global
                             }
                             lc_global += val.velikost
            case *format_F2_n: val.lc = lc_global
                               if val.oznaka != "" {
                                    simbolna_tabela[val.oznaka] = lc_global
                               }
                               lc_global += val.velikost
            case *format_F2_r: val.lc = lc_global
                               if val.oznaka != "" {
                                    simbolna_tabela[val.oznaka] = lc_global
                               }
                               lc_global += val.velikost
            case *format_F2_rn: val.lc = lc_global
                                if val.oznaka != "" {
                                    simbolna_tabela[val.oznaka] = lc_global
                                }
                                lc_global += val.velikost
            case *format_F2_rr: val.lc = lc_global
                                if val.oznaka != "" {
                                    simbolna_tabela[val.oznaka] = lc_global
                                }
                                lc_global += val.velikost
            case *format_F3_m: val.lc = lc_global
                               if val.oznaka != "" {
                                    simbolna_tabela[val.oznaka] = lc_global
                               }
                               lc_global += val.velikost
                               if bazno_dovoljeno {
                                    val.b = true
                               }
            case *format_F4_m: val.lc = lc_global
                               if val.oznaka != "" {
                                    simbolna_tabela[val.oznaka] = lc_global
                               }
                               lc_global += val.velikost
                               if bazno_dovoljeno {
                                    val.b = true
                               }
            case *format_D: val.lc = lc_global
                            if val.oznaka != "" {
                                 simbolna_tabela[val.oznaka] = lc_global
                            }
                            bazno_dovoljeno = false
            case *format_D_n: val.lc = lc_global
                              if val.oznaka != "" {
                                   simbolna_tabela[val.oznaka] = lc_global
                              }
                              switch val.ime {
                                  case "ORG": inc, _ := strconv.Atoi(val.operand)
                                              lc_global += inc
                                  case "BASE": bazno_dovoljeno = true 
                              }
            case *format_M_r: val.lc = lc_global
                              if val.oznaka != "" {
                                   simbolna_tabela[val.oznaka] = lc_global
                              }
                              lc_global += val.rezervacija
            case *format_M_i: val.lc = lc_global
                              if val.oznaka != "" {
                                    simbolna_tabela[val.oznaka] = lc_global
                              }
                              lc_global += val.velikost
        }    
    }
}


func drugi_prehod(AST *[]ukaz) {
    for _, el := range *AST {
        switch val := el.(type) {
            case *format_F3_m: if st, err := strconv.Atoi(val.naslov); err == nil {
                                   if val.n && !val.i  {
                                        if st >= -2048 && st <= 2047 {
                                            val.p = true
                                            val.odmik = st - (val.lc + val.velikost) //naslov - PC  
                                        } else if bazno_dovoljeno && st >= 0 && st <= 4095 {
                                            val.odmik = st - bazno_naslov
                                            val.b = true
                                        } else {
                                            val.odmik = st
                                        }
                                   } else if !val.n && val.i {
                                        val.odmik = st
                                   } else if val.n && val.i {
                                        if st >= -2048 && st <= 2047 {
                                            val.p = true
                                            val.odmik = st - (val.lc + val.velikost) //naslov - PC  
                                        } else if bazno_dovoljeno && st >= 0 && st <= 4095 {
                                            val.b = true
                                            val.odmik = st - bazno_naslov
                                        } else {
                                            val.odmik = st
                                        }
                                   }    
                               } else {
                                   naslov := simbolna_tabela[val.naslov]
                                   if val.n && !val.i  { 
                                        if naslov >= -2048 && naslov <= 2047 {
                                            val.p = true
                                            val.odmik = naslov - (val.lc + val.velikost) //naslov - PC  
                                        } else if bazno_dovoljeno && naslov >= 0 && naslov <= 4095 {
                                            val.b = true
                                            val.odmik = naslov - bazno_naslov
                                        } else {
                                            val.odmik = naslov
                                        }
                                   } else if !val.n && val.i {
                                        val.odmik = naslov
                                   } else if val.n && val.i {   
                                        if naslov >= -2048 && naslov <= 2047 {
                                            val.p = true
                                            val.odmik = naslov - (val.lc + val.velikost) //naslov - PC  
                                            //fmt.Printf("pc %x\n", val.odmik)
                                        } else if bazno_dovoljeno && naslov >= 0 && naslov <= 4095 {
                                            val.b = true
                                            val.odmik = naslov - bazno_naslov
                                            //fmt.Printf("bazno %x\n", val.odmik)
                                        } else {
                                            val.odmik = naslov
                                            //fmt.Printf("neposredno %x\n", val.odmik)
                                        }
                                   }    
                               }
            case *format_F4_m: if st, err := strconv.Atoi(val.naslov); err == nil {
                                    val.odmik = st 
                               } else {
                                    val.odmik = simbolna_tabela[val.naslov]
                                    m := M_zapis{val.lc + 1, 5}
                                    M_tabela = append(M_tabela, m)
                               }
            case *format_D: bazno_dovoljeno = false
            case *format_D_n: switch val.ime {
                                    case "BASE": bazno_dovoljeno = true
                                                 if st, err := strconv.Atoi(val.operand); err == nil {
                                                        bazno_naslov = st
                                                 } else {
                                                        bazno_naslov = simbolna_tabela[val.operand]
                                                 }
                                    case "END": if st, err := strconv.Atoi(val.operand); err == nil {
                                                    zacetni_naslov = st  
                                                } else {
                                                    zacetni_naslov = simbolna_tabela[val.operand]
                                                }
                                    case "EQU": if st, err := strconv.Atoi(val.operand); err == nil {
                                                    simbolna_tabela[val.oznaka] = st
                                                } else {
                                                    simbolna_tabela[val.oznaka] = simbolna_tabela[val.operand]
                                                }
                              }
        }
    }        
}

func generacija(AST *[]ukaz) {
    obj, err := os.Create("out.obj")
    if err != nil {
        fmt.Println("Error creating a file: ", err)
        return
    }     
    defer obj.Close()
    obj.WriteString(fmt.Sprintf("H%-*s%06x%06x\n", 6, ime_programa, naslov_programa, lc_global + (*AST)[len(*AST) - 1].Velikost() - naslov_programa))
    line_len := 0
    line := ""
    line_addr := naslov_programa
    for _, el := range *AST {
        temp := ""
        switch val := el.(type) {
            case *format_F1: temp= fmt.Sprintf("%s%02x", temp, val.opcode)
            case *format_F2_n: temp = fmt.Sprintf("%s%02x%x%x", temp, val.opcode, val.n, 0)
            case *format_F2_r: temp = fmt.Sprintf("%s%02x%x%x", temp, val.opcode, register_map[val.r1], 0)
            case *format_F2_rn: temp = fmt.Sprintf("%s%02x%x%x", temp, val.opcode, val.r1, val.n)
            case *format_F2_rr: temp = fmt.Sprintf("%s%02x%x%x", temp, val.opcode, register_map[val.r1], register_map[val.r2])
            case *format_F3_m:  n := 0
                                i := 0
                                x := 0
                                b := 0
                                p := 0
                                e := 0      
                                if val.n {
                                    n = 1
                                }
                                if val.i {
                                    i = 1
                                }
                                if val.x {
                                    x = 1
                                }
                                if val.b {
                                    b = 1
                                }
                                if val.p {
                                    p = 1
                                }
                                if val.e {
                                    e = 1
                                }
                                temp = fmt.Sprintf("%s%02x%02x%02x", temp, val.opcode + (n << 1) + i, (x << 7) + (b << 6) + (p << 5) + (e << 4) +
                                                  ((val.odmik >> 8) & 0xF), val.odmik & 0xFF)
            case *format_F4_m:  n := 0
                                i := 0
                                x := 0
                                b := 0
                                p := 0
                                e := 0      
                                if val.n {
                                    n = 1
                                }
                                if val.i {
                                    i = 1
                                }
                                if val.x {
                                    x = 1
                                }
                                if val.b {
                                    b = 1
                                }
                                if val.p {
                                    p = 1
                                }
                                if val.e {
                                    e = 1
                                }
                                temp = fmt.Sprintf("%s%02x%02x%02x%02x", temp, val.opcode + (n << 1) + i, (x << 7) + (b << 6) + (p << 5) + (e << 4) +
                                                  ((val.odmik >> 16) & 0xF), (val.odmik >> 8) & 0xFF, val.odmik & 0xFF)
            case *format_D: continue
            case *format_D_n: if val.ime == "START" || val.ime == "END" {
                                continue
                              }
            case *format_M_r: continue
            case *format_M_i: vr, _ := strconv.Atoi(val.operand)
                              temp = fmt.Sprintf("%s%06x", temp, vr)
        }    
        if line_len + el.Velikost() <= 30 {
           line_len += el.Velikost()
           line = fmt.Sprintf("%s%s", line, temp)
        } else {
            obj.WriteString(fmt.Sprintf("T%06x%02x%s\n", line_addr, line_len, line)) 
            line_len = el.Velikost()
            line = temp
            line_addr = el.GetAddr()
        }
    }
    obj.WriteString(fmt.Sprintf("T%06x%02x%s\n", line_addr, line_len, line)) 
    for _, el := range M_tabela {
        obj.WriteString(fmt.Sprintf("M%06x%02x\n", el.odmik, el.dolzina))
    }
    obj.WriteString(fmt.Sprintf("E%06x", zacetni_naslov))
}

func izpis(AST *[]ukaz) {
    for _, el := range *AST {
        switch val := el.(type) {
            case *format_F1: fmt.Println("F1", val)
            case *format_F2_n: fmt.Println("F2_n", val)
            case *format_F2_r: fmt.Println("F2_r", val)
            case *format_F2_rn: fmt.Println("F2_rn", val)
            case *format_F2_rr: fmt.Println("F2_rr", val)
            case *format_F3_m: fmt.Println("F3_m", val)
            case *format_F4_m: fmt.Println("F4_m", val)
            case *format_D: fmt.Println("D", val)
            case *format_D_n: fmt.Println("D_n", val)
            case *format_M_r: fmt.Println("M_r", val)
            case *format_M_i: fmt.Println("M_i", val)
        }    
    }
}

// Pregledovalnik bere vhodne vrstice in jih razgradi na posamezne enote.
// Razpoznane elemente okvalificira (oznaka / mnemonik / operand / komentar) 
// //<zbirniski stavek> ::== [<oznaka stavka>] <ločilo> <mnemonik> <ločilo> {<operand> <ločilo>} [<komentar>]
func main() {
    inputbyte, err := os.ReadFile("input.asm")
    if err != nil {
        fmt.Println("Error reading input file: ", err)
        return
    }

    input := string(inputbyte)
    pattern := regexp.MustCompile(`.*\n`)    
    matches := pattern.FindAllString(input, -1)
    var AST []ukaz
    for _, el := range matches {
        if remove_comments_and_empty_lines(el) {
            continue
        }
        if check_D(el, &AST) {
            continue
        }
        if check_D_n(el, &AST) {
            continue
        }
        if check_M_r(el, &AST) {
            continue
        }
        if check_M_i(el, &AST) {
            continue
        }
        if check_F1(el, &AST) {
            continue
        }
        if check_F2_n(el, &AST) {
            continue
        }
        if check_F2_r(el, &AST) {
            continue 
        } 
        if check_F2_rn(el, &AST) {
            continue 
        }    
        if check_F2_rr(el, &AST) {
            continue
        }
        if check_F3_m(el, &AST) {
            continue
        }
        if check_F4_m(el, &AST) {
            continue
        }
    }
    prvi_prehod(&AST)
    //izpis(&AST)
    /*for k, v := range simbolna_tabela {
        fmt.Printf("k: %s v: %x\n", k, v)
    }*/
    drugi_prehod(&AST)
    //izpis(&AST)
    generacija(&AST)
}
