2 * Copyright (C) 2004-2008 Sami Pietila
3 * Copyright (C) 2008-2012 Robert Ancell.
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 2 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
12 public int sub_atoi (string data
)
14 const unichar digits
[] = {'₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'};
19 while (data
.get_next_char (ref index
, out c
))
21 var is_subdigit
= false;
22 for (var i
= 0; i
< digits
.length
; i
++)
26 value
= value
* 10 + i
;
38 public int super_atoi (string data
)
40 const unichar digits
[] = {'⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'};
44 data
.get_next_char (ref index
, out c
);
52 while (data
.get_next_char (ref index
, out c
))
54 var is_superdigit
= false;
55 for (var i
= 0; i
< digits
.length
; i
++)
59 value
= value
* 10 + i
;
71 public string mp_error_code_to_string (ErrorCode error_code
)
76 return "ErrorCode.NONE";
77 case ErrorCode
.INVALID
:
78 return "ErrorCode.INVALID";
79 case ErrorCode
.OVERFLOW
:
80 return "ErrorCode.OVERFLOW";
81 case ErrorCode
.UNKNOWN_VARIABLE
:
82 return "ErrorCode.UNKNOWN_VARIABLE";
83 case ErrorCode
.UNKNOWN_FUNCTION
:
84 return "ErrorCode.UNKNOWN_FUNCTION";
85 case ErrorCode
.UNKNOWN_CONVERSION
:
86 return "ErrorCode.UNKNOWN_CONVERSION";
88 return "ErrorCode.MP";
90 return "Unknown parser error";
105 public class Equation
109 public AngleUnit angle_units
;
110 private string expression
;
112 public Equation (string expression
)
114 this
.expression
= expression
;
117 public new Number?
parse (out ErrorCode error_code
= null, out string error_token
= null, out uint error_start
= null, out uint error_end
= null)
119 var parser
= new
EquationParser (this
, expression
);
122 var z
= parser
.parse (out error_code
, out error_token
, out error_start
, out error_end
);
124 /* Error during parsing */
125 if (error_code
!= ErrorCode
.NONE
)
128 if (mp_get_error () != null)
130 error_code
= ErrorCode
.MP
;
137 public virtual bool variable_is_defined (string name
)
142 public virtual Number?
get_variable (string name
)
147 public virtual void set_variable (string name
, Number x
)
151 public virtual bool function_is_defined (string name
)
156 public virtual Number?
get_function (string name
, Number x
)
161 public virtual Number?
convert (Number x
, string x_units
, string z_units
)
167 private class EquationParser
: Parser
169 private Equation equation
;
171 public EquationParser (Equation equation
, string expression
)
173 base (expression
, equation
.base, equation
.wordlen
);
174 this
.equation
= equation
;
177 protected override bool variable_is_defined (string name
)
179 /* FIXME: Make more generic */
180 if (name
== "e" || name
== "i" || name
== "π")
183 return equation
.variable_is_defined (name
);
186 protected override Number?
get_variable (string name
)
189 return new Number
.eulers ();
190 else if (name
== "i")
191 return new Number
.i ();
192 else if (name
== "π")
193 return new Number
.pi ();
195 return equation
.get_variable (name
);
198 protected override void set_variable (string name
, Number x
)
200 // Reserved words, e, π, mod, and, or, xor, not, abs, log, ln, sqrt, int, frac, sin, cos, ...
201 if (name
== "e" || name
== "i" || name
== "π")
204 equation
.set_variable (name
, x
);
207 // FIXME: Accept "2sin" not "2 sin", i.e. let the tokenizer collect the multiple
208 // Parser then distinguishes between "sin"="s*i*n" or "sin5" = "sin 5" = "sin (5)"
209 // i.e. numbers+letters = variable or function depending on following arg
210 // letters+numbers = numbers+letters+numbers = function
212 protected override bool function_is_defined (string name
)
214 var lower_name
= name
.down ();
216 /* FIXME: Make more generic */
217 if (lower_name
== "log" ||
218 (lower_name
.has_prefix ("log") && sub_atoi (lower_name
.substring (3)) >= 0) ||
219 lower_name
== "ln" ||
220 lower_name
== "sqrt" ||
221 lower_name
== "abs" ||
222 lower_name
== "sgn" ||
223 lower_name
== "arg" ||
224 lower_name
== "conj" ||
225 lower_name
== "int" ||
226 lower_name
== "frac" ||
227 lower_name
== "floor" ||
228 lower_name
== "ceil" ||
229 lower_name
== "round" ||
230 lower_name
== "re" ||
231 lower_name
== "im" ||
232 lower_name
== "sin" || lower_name
== "cos" || lower_name
== "tan" ||
233 lower_name
== "asin" || lower_name
== "acos" || lower_name
== "atan" ||
234 lower_name
== "sin⁻¹" || lower_name
== "cos⁻¹" || lower_name
== "tan⁻¹" ||
235 lower_name
== "sinh" || lower_name
== "cosh" || lower_name
== "tanh" ||
236 lower_name
== "sinh⁻¹" || lower_name
== "cosh⁻¹" || lower_name
== "tanh⁻¹" ||
237 lower_name
== "asinh" || lower_name
== "acosh" || lower_name
== "atanh" ||
238 lower_name
== "ones" ||
239 lower_name
== "twos")
242 return equation
.function_is_defined (name
);
245 protected override Number?
get_function (string name
, Number x
)
247 var lower_name
= name
.down ();
251 if (lower_name
== "log")
252 return x
.logarithm (10); // FIXME: Default to ln
253 else if (lower_name
.has_prefix ("log"))
255 var number_base
= sub_atoi (lower_name
.substring (3));
259 return x
.logarithm (number_base
);
261 else if (lower_name
== "ln")
263 else if (lower_name
== "sqrt") // √x
265 else if (lower_name
== "abs") // |x|
267 else if (lower_name
== "sgn")
269 else if (lower_name
== "arg")
270 return x
.arg (equation
.angle_units
);
271 else if (lower_name
== "conj")
272 return x
.conjugate ();
273 else if (lower_name
== "int")
274 return x
.integer_component ();
275 else if (lower_name
== "frac")
276 return x
.fractional_component ();
277 else if (lower_name
== "floor")
279 else if (lower_name
== "ceil")
281 else if (lower_name
== "round")
283 else if (lower_name
== "re")
284 return x
.real_component ();
285 else if (lower_name
== "im")
286 return x
.imaginary_component ();
287 else if (lower_name
== "sin")
288 return x
.sin (equation
.angle_units
);
289 else if (lower_name
== "cos")
290 return x
.cos (equation
.angle_units
);
291 else if (lower_name
== "tan")
292 return x
.tan (equation
.angle_units
);
293 else if (lower_name
== "sin⁻¹" || lower_name
== "asin")
294 return x
.asin (equation
.angle_units
);
295 else if (lower_name
== "cos⁻¹" || lower_name
== "acos")
296 return x
.acos (equation
.angle_units
);
297 else if (lower_name
== "tan⁻¹" || lower_name
== "atan")
298 return x
.atan (equation
.angle_units
);
299 else if (lower_name
== "sinh")
301 else if (lower_name
== "cosh")
303 else if (lower_name
== "tanh")
305 else if (lower_name
== "sinh⁻¹" || lower_name
== "asinh")
307 else if (lower_name
== "cosh⁻¹" || lower_name
== "acosh")
309 else if (lower_name
== "tanh⁻¹" || lower_name
== "atanh")
311 else if (lower_name
== "ones")
312 return x
.ones_complement (equation
.wordlen
);
313 else if (lower_name
== "twos")
314 return x
.twos_complement (equation
.wordlen
);
316 return equation
.get_function (name
, x
);
319 protected override Number?
convert (Number x
, string x_units
, string z_units
)
321 return equation
.convert (x
, x_units
, z_units
);