package generator import "fmt" import "errors" import "git.tebibyte.media/sashakoshka/fspl/llvm" import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/analyzer" func (this *generator) generateLiteralInt (literal *entity.LiteralInt) (llvm.Value, error) { irType, err := this.generateType(analyzer.ReduceToBase(literal.Type())) if err != nil { return nil, err } base := analyzer.ReduceToBase(literal.Type()) switch base.(type) { case *entity.TypeInt, *entity.TypeWord: return llvm.NewConstInt(irType.(*llvm.TypeInt), int64(literal.Value)), nil case *entity.TypeFloat: return llvm.NewConstFloat(irType.(*llvm.TypeFloat), float64(literal.Value)), nil default: return nil, errors.New(fmt.Sprintln("int can't be used as", base)) } } func (this *generator) generateLiteralFloat (literal *entity.LiteralFloat) (llvm.Value, error) { irType, err := this.generateType(analyzer.ReduceToBase(literal.Type())) if err != nil { return nil, err } return llvm.NewConstFloat(irType.(*llvm.TypeFloat), literal.Value), nil } // generateLiteralArrayLoc generates an array literal. irDestLoc specifies the // location to assign the data to. If it is nil, this function will allocate a // destination (if necessary) and return its value as a register. func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray, irDestLoc llvm.Value) (llvm.Value, error) { destinationSpecified := irDestLoc != nil makeDataType := func (elementType entity.Type, nullTerminator bool) (llvm.Type, int, error) { length := len(literal.Elements) if nullTerminator { length += 1 } irDataType, err := this.generateType(&entity.TypeArray { Element: elementType, Length: length, }) return irDataType, length, err } populateData := func (elementType entity.Type, irDestLoc llvm.Value, length int, nullTerminator bool) error { irDataType, _, err := makeDataType(elementType, nullTerminator) if err != nil { return err } irElementType, err := this.generateType(elementType) if err != nil { return err } for index := 0; index < length; index ++ { elementPointer := this.blockManager.NewGetElementPtr ( irDataType, irDestLoc, llvm.NewConstInt(llvm.I32, 0), llvm.NewConstInt(llvm.I32, int64(index))) if index >= len(literal.Elements) { this.blockManager.NewStore ( llvm.NewConstZeroInitializer(irElementType), elementPointer) } else { _, err := this.generateAssignmentToDestination ( literal.Elements[index], elementType, elementPointer) if err != nil { return err } } } return nil } irDestType, err := this.generateType(literal.Type()) if err != nil { return nil, err } if !destinationSpecified { irDestLoc = this.blockManager.newAllocaFront(irDestType) } base := analyzer.ReduceToBase(literal.Type()) switch base := base.(type) { case *entity.TypeArray: populateData(base.Element, irDestLoc, base.Length, false) case *entity.TypeSlice: irDataType, length, err := makeDataType(base.Element, false) if err != nil { return nil, err } destDataLoc := this.blockManager.newAllocaFront(irDataType) err = populateData(base.Element, destDataLoc, length, false) if err != nil { return nil, err } this.sliceSetData(literal.Type(), irDestLoc, destDataLoc) this.sliceSetDefinedLength(literal.Type(), irDestLoc, int64(len(literal.Elements))) case *entity.TypePointer: irDataType, length, err := makeDataType(base.Referenced, true) if err != nil { return nil, err } destDataLoc := this.blockManager.newAllocaFront(irDataType) err = populateData(base.Referenced, destDataLoc, length, true) if err != nil { return nil, err } this.blockManager.NewStore(destDataLoc, irDestLoc) default: return nil, errors.New(fmt.Sprintln("array can't be used as ", base)) } if destinationSpecified { return nil, nil } else { return irDestLoc, nil } } func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString, irDestLoc llvm.Value) (llvm.Value, error) { destinationSpecified := irDestLoc != nil makeDataType := func (anyElementType entity.Type, nullTerminator bool) (llvm.Type, int, error) { elementType, ok := analyzer.ReduceToBase(anyElementType).(*entity.TypeInt) if !ok { return nil, 0, errors.New(fmt.Sprintln ( "string can't be used with element type", anyElementType)) } var length int; switch { case elementType.Width >= 32: length = len(literal.ValueUTF32) case elementType.Width >= 16: length = len(literal.ValueUTF16) default: length = len(literal.ValueUTF8) } if nullTerminator { length += 1 } irDataType, err := this.generateType(&entity.TypeArray { Element: anyElementType, Length: length, }) return irDataType, length, err } populateData := func (anyElementType entity.Type, irDestLoc llvm.Value, length int, nullTerminator bool) error { elementType, ok := analyzer.ReduceToBase(anyElementType).(*entity.TypeInt) if !ok { return errors.New(fmt.Sprintln ( "string can't be used with element type", anyElementType)) } irArrayType, _, err := makeDataType(anyElementType, nullTerminator) if err != nil { return err } for index := 0; index < length; index ++ { var element llvm.Value; switch { case elementType.Width >= 32: if index >= len(literal.ValueUTF32) { element = llvm.NewConstZeroInitializer(llvm.I32) } else { element = llvm.NewConstInt(llvm.I32, int64(literal.ValueUTF32[index])) } case elementType.Width >= 16: if index >= len(literal.ValueUTF16) { element = llvm.NewConstZeroInitializer(llvm.I16) } else { element = llvm.NewConstInt(llvm.I16, int64(literal.ValueUTF16[index])) } default: if index >= len(literal.ValueUTF8) { element = llvm.NewConstZeroInitializer(llvm.I8) } else { element = llvm.NewConstInt(llvm.I8, int64(literal.ValueUTF8[index])) } } elementPointer := this.blockManager.NewGetElementPtr ( irArrayType, irDestLoc, llvm.NewConstInt(llvm.I32, 0), llvm.NewConstInt(llvm.I32, int64(index))) this.blockManager.NewStore(element, elementPointer) } return nil } irDestType, err := this.generateType(literal.Type()) if err != nil { return nil, err } if !destinationSpecified { irDestLoc = this.blockManager.newAllocaFront(irDestType) } base := analyzer.ReduceToBase(literal.Type()) switch base := base.(type) { case *entity.TypeInt: var value llvm.Value; switch { case base.Width >= 32: value = llvm.NewConstInt(llvm.I32, int64(literal.ValueUTF32[0])) case base.Width >= 16: value = llvm.NewConstInt(llvm.I16, int64(literal.ValueUTF16[0])) default: value = llvm.NewConstInt(llvm.I8, int64(literal.ValueUTF8[0])) } this.blockManager.NewStore(value, irDestLoc) case *entity.TypeArray: err := populateData(base.Element, irDestLoc, base.Length, false) if err != nil { return nil, err } case *entity.TypeSlice: irDataType, length, err := makeDataType(base.Element, false) if err != nil { return nil, err } destDataLoc := this.blockManager.newAllocaFront(irDataType) err = populateData(base.Element, destDataLoc, length, false) if err != nil { return nil, err } this.sliceSetData(literal.Type(), irDestLoc, destDataLoc) this.sliceSetDefinedLength(literal.Type(), irDestLoc, int64(length)) case *entity.TypePointer: irDataType, length, err := makeDataType(base.Referenced, true) if err != nil { return nil, err } destDataLoc := this.blockManager.newAllocaFront(irDataType) err = populateData(base.Referenced, destDataLoc, length, true) if err != nil { return nil, err } this.blockManager.NewStore(destDataLoc, irDestLoc) default: return nil, errors.New(fmt.Sprintln("string can't be used as ", base)) } if destinationSpecified { return nil, nil } else { return irDestLoc, nil } } func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct, irDestLoc llvm.Value) (llvm.Value, error) { destinationSpecified := irDestLoc != nil base, ok := analyzer.ReduceToBase(literal.Type()).(*entity.TypeStruct) if !ok { return nil, errors.New(fmt.Sprintln("struct can't be used as ", literal.Type())) } irDestType, err := this.generateType(literal.Type()) if err != nil { return nil, err } if !destinationSpecified { irDestLoc = this.blockManager.newAllocaFront(irDestType) } for index, member := range base.Members { elementPointer := this.blockManager.NewGetElementPtr ( irDestType, irDestLoc, llvm.NewConstInt(llvm.I32, 0), llvm.NewConstInt(llvm.I32, int64(index))) if pair, ok := literal.MemberMap[member.Name]; ok { _, err = this.generateAssignmentToDestination ( pair.Value, member.Type(), elementPointer) if err != nil { return nil, err } } else { irMemberType, err := this.generateType(member.Type()) if err != nil { return nil, err } this.blockManager.NewStore ( llvm.NewConstZeroInitializer(irMemberType), elementPointer) } } if destinationSpecified { return nil, nil } else { return irDestLoc, nil } } func (this *generator) generateLiteralBoolean (literal *entity.LiteralBoolean) (llvm.Value, error) { return llvm.NewConstBool(bool(*literal.Value)), nil } func (this *generator) generateLiteralNil (literal *entity.LiteralNil) (llvm.Value, error) { ty, err := this.generateType(literal.Type()) if err != nil { return nil, err } return llvm.NewConstZeroInitializer(ty), nil }