1// Copyright (c) 2021, Peter Ohler, All rights reserved.
2
3package asm
4
5import (
6	"fmt"
7)
8
9func init() {
10	Define(&Fn{
11		Name: "sum",
12		Eval: sum,
13		Desc: `Returns the sum of all arguments. All arguments must be numbers
14or strings. If any argument is a string then the result will be
15a string otherwise the result will be a number. If any of the
16arguments are not a number or a string an error is raised.`,
17	})
18	Define(&Fn{
19		Name: "+",
20		Eval: sum,
21		Desc: `Returns the sum of all arguments. All arguments must be numbers
22or strings. If any argument is a string then the result will be
23a string otherwise the result will be a number. If any of the
24arguments are not a number or a string an error is raised.`,
25	})
26}
27
28const (
29	intSum = iota
30	floatSum
31	strSum
32)
33
34func sum(root map[string]interface{}, at interface{}, args ...interface{}) interface{} {
35	kind := intSum
36	var ssum string
37	var isum int64
38	var fsum float64
39	for i, arg := range args {
40		switch v := evalArg(root, at, arg).(type) {
41		case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
42			ii, _ := asInt(v)
43			if i == 0 {
44				kind = intSum
45			}
46			switch kind {
47			case intSum:
48				isum += ii
49			case floatSum:
50				fsum += float64(ii)
51			case strSum:
52				ssum = fmt.Sprintf("%s%d", ssum, ii)
53			}
54		case float32, float64:
55			if i == 0 {
56				kind = floatSum
57			}
58			f, _ := asFloat(v)
59			switch kind {
60			case intSum:
61				kind = floatSum
62				fsum = float64(isum) + f
63			case floatSum:
64				fsum += f
65			case strSum:
66				ssum = fmt.Sprintf("%s%g", ssum, f)
67			}
68		case string:
69			if i == 0 {
70				kind = strSum
71			}
72			switch kind {
73			case intSum:
74				kind = strSum
75				ssum = fmt.Sprintf("%d%s", isum, v)
76			case floatSum:
77				kind = strSum
78				ssum = fmt.Sprintf("%g%s", fsum, v)
79			case strSum:
80				ssum += v
81			}
82		default:
83			panic(fmt.Errorf("a %T argument can not be summed", v))
84		}
85	}
86	switch kind {
87	case intSum:
88		return isum
89	case floatSum:
90		return fsum
91	}
92	return ssum
93}
94