package generator import "fmt" import "errors" import "git.tebibyte.media/sashakoshka/fspl/llvm" import "git.tebibyte.media/sashakoshka/fspl/entity" type loopEntry struct { value llvm.Value stub *llvm.Block wantLocation bool } type blockManager struct { *llvm.Block generator *generator function *llvm.Function declarations map[*entity.Declaration] llvm.Value loops []*loopEntry } func (this *generator) pushBlockManager (function *llvm.Function) *blockManager { manager := new(blockManager) this.managerStack = append(this.managerStack, manager) manager.generator = this manager.function = function manager.newBlock() manager.declarations = make(map[*entity.Declaration] llvm.Value) return manager } func (this *generator) popBlockManager () { this.managerStack = this.managerStack[:len(this.managerStack) - 1] if len(this.managerStack) > 0 { this.blockManager = this.managerStack[0] } else { this.blockManager = nil } } func (this *blockManager) pushLoop (wantLocation bool) *loopEntry { entry := &loopEntry { wantLocation: wantLocation } this.loops = append(this.loops, entry) return entry } func (this *blockManager) popLoop () { this.loops = this.loops[:len(this.loops) - 1] } func (this *blockManager) topLoop () *loopEntry { return this.loops[len(this.loops) - 1] } func (this *blockManager) newAllocaFront (element llvm.Type) llvm.Value { alloca := llvm.NewAlloca(element) firstBlock := this.function.Blocks[0] if firstBlock.Terminated() { lastIndex := len(firstBlock.Instructions) - 1 firstBlock.Instructions = append ( firstBlock.Instructions, firstBlock.Instructions[lastIndex]) firstBlock.Instructions[lastIndex] = alloca } else { firstBlock.AddInstruction(alloca) } return alloca } func (this *blockManager) newBlock () *llvm.Block { this.Block = this.function.NewBlock() return this.Block } func (this *blockManager) variable (declaration *entity.Declaration) (llvm.Value, error) { value, ok := this.declarations[declaration] if !ok { return nil, errors.New(fmt.Sprintf("declaration of %v does not exist", declaration.Name)) } return value, nil } func (this *blockManager) addDeclaration (declaration *entity.Declaration, initial llvm.Value) error { ty, err := this.generator.generateType(declaration.Type()) if err != nil { return err } location := this.newAllocaFront(ty) this.declarations[declaration] = location if initial != nil { this.NewStore(initial, location) } return nil } func (this *blockManager) addDeclarationsInFunction (scope entity.Scoped) error { var err error add := func (expression entity.Statement) bool { if expression, ok := expression.(entity.Scoped); ok { err = this.addDeclarationsDirectlyIn(expression) } return err == nil } switch scope.(type) { case *entity.Function: scope := scope.(*entity.Function) scope.OverVariables(func (declaration *entity.Declaration) bool { for index, argument := range scope.Signature.Arguments { if argument.Name == declaration.Name { this.addDeclaration ( declaration, this.function.Parameters[index]) return true } } this.addDeclaration(declaration, nil) return true }) if scope.Body == nil { break } overStatements(scope.Body, add) case *entity.Method: scope := scope.(*entity.Method) scope.OverVariables(func (declaration *entity.Declaration) bool { if declaration.Name == "this" { this.addDeclaration ( declaration, this.function.Parameters[0]) return true } for index, argument := range scope.Signature.Arguments { if argument.Name == declaration.Name { this.addDeclaration ( declaration, this.function.Parameters[index + 1]) return true } } this.addDeclaration(declaration, nil) return true }) if scope.Body == nil { break } overStatements(scope.Body, add) } return err } func (this *blockManager) addDeclarationsDirectlyIn (scope entity.Scoped) error { var err error scope.OverVariables(func (declaration *entity.Declaration) bool { err = this.addDeclaration(declaration, nil) return err == nil }) return err } // if ya nasty func overStatements ( expression entity.Statement, callback func(entity.Statement) bool, ) bool { if expression == nil { return true } if !callback(expression) { return false} switch expression := expression.(type) { case *entity.Call: for _, argument := range expression.Arguments { if !overStatements(argument, callback) { return false } } case *entity.MethodCall: for _, argument := range expression.Arguments { if !overStatements(argument, callback) { return false } } case *entity.Subscript: if !overStatements(expression.Slice, callback) { return false } if !overStatements(expression.Offset, callback) { return false } case *entity.Slice: if !overStatements(expression.Slice, callback) { return false } if !overStatements(expression.Start, callback) { return false } if !overStatements(expression.End, callback) { return false } case *entity.Dereference: if !overStatements(expression.Pointer, callback) { return false } case *entity.Reference: if !overStatements(expression.Value, callback) { return false } case *entity.Length: if !overStatements(expression.Slice, callback) { return false } case *entity.ValueCast: if !overStatements(expression.Value, callback) { return false } case *entity.BitCast: if !overStatements(expression.Value, callback) { return false } case *entity.Operation: for _, argument := range expression.Arguments { if !overStatements(argument, callback) { return false } } case *entity.Block: for _, step := range expression.Steps { if !overStatements(step, callback) { return false } } case *entity.IfElse: if !overStatements(expression.Condition, callback) { return false } if !overStatements(expression.True, callback) { return false } if !overStatements(expression.False, callback) { return false } case *entity.Loop: if !overStatements(expression.Body, callback) { return false } case *entity.Break: if !overStatements(expression.Value, callback) { return false } case *entity.Return: if !overStatements(expression.Value, callback) { return false } case *entity.LiteralArray: for _, element := range expression.Elements { if !overStatements(element, callback) { return false} } case *entity.LiteralStruct: for _, member := range expression.Members { if !overStatements(member.Value, callback) { return false} } } return true }