]> git.neil.brown.name Git - edlib.git/blob - lib-calc.c
TODO: clean out done items.
[edlib.git] / lib-calc.c
1 /*
2  * Copyright Neil Brown ©2020-2021 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * Perform numeric calculations.
6  *
7  * 'str' should hold an expression which is computed.
8  * It may can contain variables in which can comm2 should produce
9  * the contents as a string
10  * The result is passed to comm2 in decimal and hex
11  */
12
13 #include <gmp.h>
14 #include "core.h"
15
16 int do_calc(const char *expr, mpq_t result,
17             bool (*getvar)(const char *name, int len, mpq_t val, void *data),
18             void *data);
19
20 static bool getvar(const char *name, int len, mpq_t val, void *data)
21 {
22         struct cmd_info *ci = data;
23         char *vnm;
24         char *vval;
25
26         if (!ci)
27                 return False;
28         vnm = strnsave(ci->focus, name, len);
29         vval = comm_call_ret(strsave, ci->comm2, "get", ci->focus, 0, NULL, vnm);
30         if (!vval)
31                 return False;
32         if (do_calc(vval, val, NULL, NULL) != 0)
33                 return False;
34         return True;
35 }
36
37 DEF_CMD(calc)
38 {
39         int ret;
40         mpq_t result;
41         const char *formats = "xf";
42
43         if (!ci->str || !ci->comm2)
44                 return Enoarg;
45         if (ci->str2)
46                 formats = ci->str2;
47         mpq_init(result);
48         ret = do_calc(ci->str, result, getvar, (void*)ci);
49         if (ret == 0) {
50                 /* success */
51                 if (mpz_cmp_si(mpq_denref(result), 1) == 0) {
52                         char *buf = mpz_get_str(NULL, 10,
53                                                 mpq_numref(result));
54                         comm_call(ci->comm2, "result", ci->focus, 0, NULL, buf);
55                         free(buf);
56                         if (strchr(formats, 'x')) {
57                                 buf = mpz_get_str(NULL, 16, mpq_numref(result));
58                                 comm_call(ci->comm2, "hex-result", ci->focus, 0,
59                                           NULL, strconcat(ci->focus, "0x",buf));
60                                 free(buf);
61                         }
62                         if (strchr(formats, 'X')) {
63                                 buf = mpz_get_str(NULL, -16, mpq_numref(result));
64                                 comm_call(ci->comm2, "hex-result", ci->focus, 0,
65                                           NULL, strconcat(ci->focus, "0X",buf));
66                                 free(buf);
67                         }
68                         if (strchr(formats, 'o')) {
69                                 buf = mpz_get_str(NULL, 8, mpq_numref(result));
70                                 comm_call(ci->comm2, "oct-result", ci->focus, 0,
71                                           NULL, strconcat(ci->focus, "0o",buf));
72                                 free(buf);
73                         }
74                 } else {
75                         char *buf = NULL;
76                         mpf_t fl;
77                         buf = mpq_get_str(NULL, 10, result);
78                         comm_call(ci->comm2, "frac-result", ci->focus, 0, NULL,
79                                   buf);
80                         free(buf);
81                         if (strchr(formats, 'f')) {
82                                 mpf_init2(fl, 20);
83                                 mpf_set_q(fl, result);
84                                 gmp_asprintf(&buf, "%.10Fg", fl);
85                                 mpf_clear(fl);
86                                 comm_call(ci->comm2, "float-result", ci->focus,
87                                           0, NULL, buf);
88                                 free(buf);
89                         }
90                 }
91         } else {
92                 comm_call(ci->comm2, "err", ci->focus, ret-1);
93         }
94         mpq_clear(result);
95         return ret == 0 ? 1 : Efail;
96 }
97
98 DEF_CMD(calc_replace)
99 {
100         int ret;
101         mpq_t result;
102         const char *expr = ci->str;
103         struct mark *m2 = ci->mark2;
104         bool hex = False, oct = False;
105
106         if (ci->num != NO_NUMERIC) {
107                 /* e.g. from Alt-3 alt-#. Pop up a calc window */
108                 struct pane *doc = call_ret(pane, "docs:byname", ci->focus,
109                                             0, NULL, "*Calc*");
110                 struct pane *p;
111
112                 if (!doc)
113                         doc = call_ret(pane, "doc:from-text", ci->focus,
114                                        0, NULL, "*Calc*", 0, NULL, "? ");
115                 if (!doc) {
116                         call("Message", ci->focus, 0, NULL,
117                              "Cannot create *Calc* - sorry");
118                         return Efail;
119                 }
120                 attr_set_str(&doc->attrs, "view-default", "view-calc");
121                 p = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "MD3tsa");
122                 if (!p) {
123                         call("Message", ci->focus, 0, NULL,
124                              "Cannot popup *Calc* - sorry");
125                         return Efail;
126                 }
127                 p = home_call_ret(pane, doc, "doc:attach-view", p, 1);
128                 if (p)
129                         call("doc:file", p, 1);
130                 return 1;
131         }
132
133         if (!expr) {
134                 if (!ci->mark)
135                         return Enoarg;
136                 /* Try to find a WORD */
137                 call("doc:WORD", ci->focus, -1, ci->mark);
138                 m2 = mark_dup(ci->mark);
139                 call("doc:WORD", ci->focus, 1, m2);
140                 expr = call_ret(strsave, "doc:get-str", ci->focus, 0, ci->mark, NULL,
141                                 0, m2);
142                 if (!expr || !*expr)
143                         return Enoarg;
144         }
145         mpq_init(result);
146         if (expr[0] == '#') {
147                 hex = True;
148                 expr += 1;
149         } else if (expr[0] == '@') {
150                 oct = True;
151                 expr += 1;
152         }
153         ret = do_calc(expr, result, NULL, NULL);
154         if (ret == 0) {
155                 /* success */
156                 char buf[100];
157                 mpf_t fl;
158
159                 if (mpz_cmp_si(mpq_denref(result), 1) == 0) {
160                         gmp_snprintf(buf, sizeof(buf),
161                                      hex ? "%#Zx" : oct ? "0o%Zo" : "%Zd",
162                                      mpq_numref(result));
163                 } else {
164                         mpf_init(fl);
165                         mpf_set_q(fl, result);
166                         gmp_snprintf(buf, sizeof(buf), "%.10Fg", fl);
167                         mpf_clear(fl);
168                 }
169                 if (!ci->mark || !m2 ||
170                     call("doc:replace", ci->focus, 0, m2, buf, 0, ci->mark) <= 0)
171                         call("Message", ci->focus, 0, NULL,
172                              strconcat(ci->focus, expr, " -> ", buf));
173         } else {
174                 call("Message", ci->focus, 0, NULL,
175                      strconcat(ci->focus, expr, " -> error at ",
176                                expr + ret - 1));
177         }
178         mpq_clear(result);
179         return ret == 0 ? 1 : Efail;
180 }
181
182 void edlib_init(struct pane *ed safe)
183 {
184         call_comm("global-set-command", ed, &calc,
185                   0, NULL, "CalcExpr");
186         call_comm("global-set-command", ed, &calc_replace,
187                   0, NULL, "interactive-cmd-calc-replace");
188 }