X-Git-Url: https://git.josue.xyz/?a=blobdiff_plain;f=compiler.go;h=e928218affb8275dac9a52a98f29b6c603c29b4e;hb=refs%2Fremotes%2Forigin%2Fmaster;hp=84e3f5400ca3bca4e17f199724f270d5e5c31e5e;hpb=4d0fdf995ab098c19d5290b7c376eb5d143935e7;p=TinyThreePassCompiler%2F.git diff --git a/compiler.go b/compiler.go index 84e3f54..e928218 100644 --- a/compiler.go +++ b/compiler.go @@ -1,7 +1,235 @@ package main -import "fmt" +import ( + "fmt" + "strings" + + "golang.org/x/exp/slices" +) + +type op int + +const ( + imm op = iota + arg + plus + min + mul + div +) + +type AST struct { + Op op + Left *AST + Right *AST + Value int + Parent *AST +} func main() { - fmt.Println("Hello, world.") + //TODO(josuer08): Change this for a argv reader and all of the printing can + // be moved to writing to a file or just use standalone with redirection not + //quite sure about it. + input := "[ a b ] ((a*b) + (5*5))-3" + + variables, program := extractVariables(input) + //fmt.Println(variables, program) + Tree := AST{} + firstPass(variables, program, &Tree) + //fmt.Println(Tree) + secondPass(&Tree) + slices.Sort(variables) + thirdPass(&Tree, variables) + //printer(&Tree) + +} + +// printer si a function that prints in Reverse Pollish Notation the AST +func printer(tree *AST) { + switch { + case tree.Op == imm: + fmt.Print(tree.Value) + case tree.Op == arg: + fmt.Printf("%c", tree.Value) + default: + fmt.Print("(") + switch tree.Op { + case min: + fmt.Print("-") + case plus: + fmt.Print("+") + case div: + fmt.Print("/") + case mul: + fmt.Print("*") + } + fmt.Print(",") + printer(tree.Left) + fmt.Print(",") + printer(tree.Right) + fmt.Print(")") + + } +} + +// firstPass Is a function that makes the first pass of the compiler, +// it converts the variable and program into an AST +func firstPass(variables, program []rune, node *AST) { + pass := node + switch program[0] { + case '-': + node.Op = min + case '+': + node.Op = plus + case '*': + node.Op = mul + case '/': + node.Op = div + case '(': + if node.Left == nil { + node.Left = &AST{} + node.Left.Parent = node + pass = node.Left + } else { + node.Right = &AST{} + node.Right.Parent = node + pass = node.Right + } + case ')': + pass = node.Parent + + default: + if program[0] > 47 && program[0] < 58 { + var zeroOp op + if node.Op == zeroOp { + node.Left = &AST{Op: imm, Value: int(program[0]) - 48} + } else { + node.Right = &AST{Op: imm, Value: int(program[0]) - 48} + } + } else if slices.Contains(variables, program[0]) { + if node.Op != plus && node.Op != min && node.Op != mul && node.Op != div { + node.Left = &AST{Op: arg, Value: int(program[0])} + } else { + node.Right = &AST{Op: arg, Value: int(program[0])} + } + } + } + if len(program) > 1 { + firstPass(variables, program[1:], pass) + } +} + +// secondPass takes an AST and reduces the operations that only include imm +// values so the program results in a more compact one with precalculated imms +func secondPass(node *AST) { + if node.Op == arg { + return + } + if node.Op == imm { + return + } + if node.Right.Op == imm && node.Left.Op == imm { + switch node.Op { + case min: + node.Value = node.Left.Value - node.Right.Value + case plus: + node.Value = node.Left.Value + node.Right.Value + case mul: + node.Value = node.Left.Value * node.Right.Value + case div: + node.Value = node.Left.Value / node.Right.Value + } + node.Op = imm + node.Left = nil + node.Right = nil + return + } + if node.Left.Op != arg && node.Left.Op != imm { + secondPass(node.Left) + } + if node.Right.Op != arg && node.Right.Op != imm { + secondPass(node.Right) + } +} + +func thirdPass(node *AST, variables []rune) { + switch node.Op { + case arg: + number, found := slices.BinarySearch(variables, rune(node.Value)) + if found { + fmt.Printf("AR %d\n", number) + } + case imm: + fmt.Printf("IM %d\n", node.Value) + default: + switch node.Left.Op { + case arg: + number, valid := slices.BinarySearch(variables, rune(node.Left.Value)) + if valid { + fmt.Printf("AR %d\n", number) + } + case imm: + fmt.Printf("IM %d\n", node.Left.Value) + default: + thirdPass(node.Left, variables) + } + switch node.Right.Op { + case arg: + fmt.Println("SW") + number, valid := slices.BinarySearch(variables, rune(node.Right.Value)) + if valid { + fmt.Printf("AR %d\n", number) + } + fmt.Println("SW") + case imm: + fmt.Println("SW") + fmt.Printf("IM %d\n", node.Right.Value) + fmt.Println("SW") + default: + fmt.Println("PU") + thirdPass(node.Right, variables) + fmt.Println("SW") + fmt.Println("PO") + } + switch node.Op { + case mul: + fmt.Println("MU") + case div: + fmt.Println("DI") + case min: + fmt.Println("SU") + case plus: + fmt.Println("AD") + + } + + } + +} + +// extractVariables receives the original program string and converts it in +// two rune slices, the first containing the variables and a second containing +// the trimmed program +func extractVariables(input string) ([]rune, []rune) { + variables := strings.Split(input, "]") + // Cleaning out the variables that are gettting extracted + variables[0] = strings.Split(variables[0], "[")[1] + variables[0] = strings.Trim(variables[0], " ") + cleanVariables := []rune(variables[0]) + var resultVariables []rune + for _, v := range cleanVariables { + if v != ' ' { + resultVariables = append(resultVariables, v) + } + } + //Cleaning out the program that is getting extracted + variables[1] = strings.Trim(variables[1], " ") + cleanProgram := []rune(variables[1]) + var resultProgram []rune + for _, v := range cleanProgram { + if v != ' ' { + resultProgram = append(resultProgram, v) + } + } + return resultVariables, resultProgram }