* SUBSTITUTE
* ACCRINTM fix
* tests
* MID
This commit is contained in:
Vyacheslav Zgordan 2020-01-08 11:32:38 +03:00 committed by Gunnsteinn Hall
parent fbcea8d7d6
commit 2cfb3539c7
4 changed files with 287 additions and 12 deletions

View File

@ -384,6 +384,9 @@ func Accrintm(args []Result) Result {
if errResult.Type == ResultTypeError {
return errResult
}
if issueDate >= settlementDate {
return MakeErrorResultType(ErrorTypeNum, "Issue date should be earlier than settlement date")
}
if args[2].Type != ResultTypeNumber {
return MakeErrorResult("ACCRINTM requires rate to be number argument")
}

View File

@ -1376,8 +1376,8 @@ const (
// Round is an implementation of the Excel ROUND function that rounds a number
// to a specified number of digits.
func round(args []Result, mode rmode) Result {
if len(args) == 0 {
return MakeErrorResult("ROUND() requires at least one numeric arguments")
if len(args) != 2 {
return MakeErrorResult("ROUND() requires two numeric arguments")
}
// number to round
number := args[0].AsNumber()
@ -1385,14 +1385,11 @@ func round(args []Result, mode rmode) Result {
return MakeErrorResult("first argument to ROUND() must be a number")
}
digits := float64(0)
if len(args) > 1 {
digitArg := args[1].AsNumber()
if digitArg.Type != ResultTypeNumber {
return MakeErrorResult("second argument to ROUND() must be a number")
}
digits = digitArg.ValueNumber
}
digits := digitArg.ValueNumber
v := number.ValueNumber

View File

@ -33,6 +33,7 @@ func init() {
RegisterFunction("LEN", Len)
RegisterFunction("LENB", Len)
RegisterFunction("LOWER", Lower)
RegisterFunction("MID", Mid)
RegisterFunction("PROPER", Proper)
RegisterFunction("REPLACE", Replace)
RegisterFunction("REPT", Rept)
@ -40,6 +41,7 @@ func init() {
RegisterFunction("RIGHTB", Right)
RegisterFunction("SEARCH", Search)
RegisterFunctionComplex("SEARCHB", Searchb)
RegisterFunction("SUBSTITUTE", Substitute)
RegisterFunction("T", T)
RegisterFunction("TEXT", Text)
RegisterFunction("TEXTJOIN", TextJoin)
@ -380,6 +382,43 @@ func lower(arg Result) Result {
}
}
// Mid is an implementation of the Excel MID function that returns a copy
// of the string with each word capitalized.
func Mid(args []Result) Result {
if len(args) != 3 {
return MakeErrorResult("MID requires three arguments")
}
if args[0].Type != ResultTypeString {
return MakeErrorResult("MID requires text to be a string")
}
text := args[0].ValueString
if args[1].Type != ResultTypeNumber {
return MakeErrorResult("MID requires start_num to be a number")
}
startNum := int(args[1].ValueNumber)
if startNum < 1 {
return MakeErrorResult("MID requires start_num to be more than 0")
}
if args[2].Type != ResultTypeNumber {
return MakeErrorResult("MID requires num_chars to be a number")
}
numChars := int(args[2].ValueNumber)
if numChars < 0 {
return MakeErrorResult("MID requires num_chars to be non negative")
}
l := len(text)
if startNum > l {
return MakeStringResult("")
}
startNum--
endNum := startNum + numChars
if endNum > l + 1 {
return MakeStringResult(text[startNum:])
} else {
return MakeStringResult(text[startNum:endNum])
}
}
// Proper is an implementation of the Excel PROPER function that returns a copy
// of the string with each word capitalized.
func Proper(args []Result) Result {
@ -534,6 +573,67 @@ func Searchb(ctx Context, ev Evaluator, args []Result) Result {
return MakeErrorResultType(ErrorTypeValue, "Not found")
}
// Substitute is an implementation of the Excel SUBSTITUTE function.
func Substitute(args []Result) Result {
argsNum := len(args)
if argsNum != 3 && argsNum != 4 {
return MakeErrorResult("SUBSTITUTE requires three or four arguments")
}
text, errResult := getString(args[0], "SUBSTITUTE", "text")
if errResult.Type == ResultTypeError {
return errResult
}
oldText, errResult := getString(args[1], "SUBSTITUTE", "old text")
if errResult.Type == ResultTypeError {
return errResult
}
newText, errResult := getString(args[2], "SUBSTITUTE", "new text")
if errResult.Type == ResultTypeError {
return errResult
}
instanceNum := 0
if argsNum == 3 {
return MakeStringResult(strings.Replace(text, oldText, newText, -1))
} else {
instanceNumF, errResult := getNumber(args[3], "SUBSTITUTE", "instance_num")
if errResult.Type == ResultTypeError {
return errResult
}
instanceNum = int(instanceNumF)
if instanceNum < 1 {
return MakeErrorResult("instance_num should be more than zero")
}
textCopy := text
countdown := instanceNum
pos := -1
l := len(oldText)
thrownTotal := 0
for {
countdown--
index := strings.Index(textCopy, oldText)
if index == -1 {
pos = -1
break
} else {
pos = index + thrownTotal
if countdown == 0 {
break
}
thrown := l + index
thrownTotal += thrown
textCopy = textCopy[thrown:]
}
}
if pos == -1 {
return MakeStringResult(text)
} else {
pre := text[:pos]
post := text[pos+l:]
return MakeStringResult(pre + newText + post)
}
}
}
// T is an implementation of the Excel T function that returns whether the
// argument is text.
func T(args []Result) Result {
@ -752,3 +852,29 @@ func Text(args []Result) Result {
return MakeErrorResult("Incorrect argument for TEXT")
}
}
func getString(arg Result, funcName, argName string) (string, Result) {
switch arg.Type {
case ResultTypeString, ResultTypeNumber, ResultTypeEmpty:
return arg.Value(), empty
default:
return "", MakeErrorResult(funcName + " requires " + argName + " to be a number or string")
}
}
func getNumber(arg Result, funcName, argName string) (float64, Result) {
switch arg.Type {
case ResultTypeEmpty:
return 0, empty
case ResultTypeNumber:
return arg.ValueNumber, empty
case ResultTypeString:
f, err := strconv.ParseFloat(arg.ValueString, 64)
if err != nil {
return 0, MakeErrorResult(argName + " should be a number for " + funcName)
}
return f, empty
default:
return 0, MakeErrorResult(funcName + " requires " + argName + " to be a number or empty")
}
}

View File

@ -564,8 +564,29 @@ func TestMatch(t *testing.T) {
runTests(t, ctx, td)
}
func TestMaxA(t *testing.T) {
func TestMax(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetNumber(0.1)
sheet.Cell("B1").SetNumber(0.2)
sheet.Cell("A2").SetNumber(0.4)
sheet.Cell("B2").SetNumber(0.8)
sheet.Cell("A3").SetBool(true)
sheet.Cell("B3").SetBool(false)
ctx := sheet.FormulaContext()
td := []testStruct{
{`MAX(A1:B3)`, `0.8 ResultTypeNumber`},
}
runTests(t, ctx, td)
}
func TestMaxA(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
@ -587,8 +608,29 @@ func TestMaxA(t *testing.T) {
runTests(t, ctx, td)
}
func TestMinA(t *testing.T) {
func TestMin(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetNumber(0.1)
sheet.Cell("B1").SetNumber(0.2)
sheet.Cell("A2").SetNumber(0.4)
sheet.Cell("B2").SetNumber(0.8)
sheet.Cell("A3").SetBool(true)
sheet.Cell("B3").SetBool(false)
ctx := sheet.FormulaContext()
td := []testStruct{
{`MIN(A1:B3)`, `0.1 ResultTypeNumber`},
}
runTests(t, ctx, td)
}
func TestMinA(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
@ -611,7 +653,6 @@ func TestMinA(t *testing.T) {
}
func TestIfs(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
@ -2742,3 +2783,111 @@ func TestYieldmat(t *testing.T) {
runTests(t, ctx, td)
}
func TestMid(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=MID("Fluid Flow",1,5)`, `Fluid ResultTypeString`},
{`=MID("Fluid Flow",7,20)`, `Flow ResultTypeString`},
{`=MID("Fluid Flow",20,5)`, ` ResultTypeString`},
{`=MID("Fluid Flow",1,0)`, ` ResultTypeString`},
{`=MID("Fluid Flow",0,5)`, `#VALUE! ResultTypeError`},
{`=MID("Fluid Flow",7,-20)`, `#VALUE! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestSubstitute(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetString("Hello Earth Earth Earth")
ctx := sheet.FormulaContext()
td := []testStruct{
{`=SUBSTITUTE(A1,"Earth","Krypton",1)`, `Hello Krypton Earth Earth ResultTypeString`},
{`=SUBSTITUTE(A1,"Earth","Krypton",2)`, `Hello Earth Krypton Earth ResultTypeString`},
{`=SUBSTITUTE(A1,"Earth","Krypton",3)`, `Hello Earth Earth Krypton ResultTypeString`},
{`=SUBSTITUTE(A1,"Earth","Krypton",4)`, `Hello Earth Earth Earth ResultTypeString`},
{`=SUBSTITUTE(A1,"Earth","Krypton")`, `Hello Krypton Krypton Krypton ResultTypeString`},
{`=SUBSTITUTE(A1,"World","Krypton")`, `Hello Earth Earth Earth ResultTypeString`},
{`=SUBSTITUTE(A1,"Earth","Krypton",0)`, `#VALUE! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestAnd(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=AND(FALSE,FALSE)`, `0 ResultTypeNumber`},
{`=AND(TRUE,FALSE)`, `0 ResultTypeNumber`},
{`=AND(FALSE,TRUE)`, `0 ResultTypeNumber`},
{`=AND(TRUE,TRUE)`, `1 ResultTypeNumber`},
}
runTests(t, ctx, td)
}
func TestIferror(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=IFERROR("No error","ERROR")`, `No error ResultTypeString`},
{`=IFERROR(1/0,"ERROR")`, `ERROR ResultTypeString`},
}
runTests(t, ctx, td)
}
func TestChar(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=CHAR(65)`, `A ResultTypeString`},
{`=CHAR(255)`, `ÿ ResultTypeString`},
{`=CHAR(1000)`, `#VALUE! ResultTypeError`},
{`=CHAR("invalid")`, `#VALUE! ResultTypeError`},
}
runTests(t, ctx, td)
}
func TestRound(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
ctx := sheet.FormulaContext()
td := []testStruct{
{`=ROUND(2.14,1)`, `2.1 ResultTypeNumber`},
{`=ROUND(2.16,1)`, `2.2 ResultTypeNumber`},
{`=ROUND(-2.14,1)`, `-2.1 ResultTypeNumber`},
{`=ROUND(-2.16,1)`, `-2.2 ResultTypeNumber`},
{`=ROUND(21.5,-1)`, `20 ResultTypeNumber`},
{`=ROUND(21.5,-2)`, `0 ResultTypeNumber`},
{`=ROUND(-55.5,-1)`, `-60 ResultTypeNumber`},
{`=ROUND(-55.5,-2)`, `-100 ResultTypeNumber`},
{`=ROUND(-55.5,0)`, `-56 ResultTypeNumber`},
{`=ROUND(-55.4,)`, `-55 ResultTypeNumber`},
{`=ROUND(-55.4)`, `#VALUE! ResultTypeError`},
}
runTests(t, ctx, td)
}