symbolic mathematics engine in OCaml with differentiation, integration, simplification, and numerical methods
1type position = { line : int; col : int; offset : int }
2
3type token =
4 | Num of float
5 | Var of string
6 | Ident of string
7 | Plus | Minus | Star | Slash | Caret
8 | LParen | RParen
9 | Comma
10 | EOF
11
12type token_with_pos = { token : token; start_pos : position; end_pos : position }
13
14let is_digit c = c >= '0' && c <= '9'
15let is_alpha c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
16let is_alphanum c = is_alpha c || is_digit c || c = '_'
17
18let needs_implicit_mult tok1 tok2 =
19 match (tok1, tok2) with
20 | (Num _ | Var _ | RParen), (Num _ | Var _ | LParen | Ident _) -> true
21 | _ -> false
22
23let tokenize str =
24 let len = String.length str in
25 let line = ref 1 in
26 let col = ref 1 in
27
28 let rec aux i acc =
29 if i >= len then
30 let pos = { line = !line; col = !col; offset = i } in
31 List.rev ({ token = EOF; start_pos = pos; end_pos = pos } :: acc)
32 else
33 let start_pos = { line = !line; col = !col; offset = i } in
34 match str.[i] with
35 | ' ' | '\t' ->
36 col := !col + 1;
37 aux (i + 1) acc
38 | '\n' ->
39 line := !line + 1;
40 col := 1;
41 aux (i + 1) acc
42 | '+' ->
43 col := !col + 1;
44 let end_pos = { line = !line; col = !col; offset = i + 1 } in
45 aux (i + 1) ({ token = Plus; start_pos; end_pos } :: acc)
46 | '-' ->
47 col := !col + 1;
48 let end_pos = { line = !line; col = !col; offset = i + 1 } in
49 aux (i + 1) ({ token = Minus; start_pos; end_pos } :: acc)
50 | '*' ->
51 col := !col + 1;
52 let end_pos = { line = !line; col = !col; offset = i + 1 } in
53 aux (i + 1) ({ token = Star; start_pos; end_pos } :: acc)
54 | '/' ->
55 col := !col + 1;
56 let end_pos = { line = !line; col = !col; offset = i + 1 } in
57 aux (i + 1) ({ token = Slash; start_pos; end_pos } :: acc)
58 | '^' ->
59 col := !col + 1;
60 let end_pos = { line = !line; col = !col; offset = i + 1 } in
61 aux (i + 1) ({ token = Caret; start_pos; end_pos } :: acc)
62 | '(' ->
63 col := !col + 1;
64 let end_pos = { line = !line; col = !col; offset = i + 1 } in
65 aux (i + 1) ({ token = LParen; start_pos; end_pos } :: acc)
66 | ')' ->
67 col := !col + 1;
68 let end_pos = { line = !line; col = !col; offset = i + 1 } in
69 aux (i + 1) ({ token = RParen; start_pos; end_pos } :: acc)
70 | ',' ->
71 col := !col + 1;
72 let end_pos = { line = !line; col = !col; offset = i + 1 } in
73 aux (i + 1) ({ token = Comma; start_pos; end_pos } :: acc)
74 | c when is_digit c || c = '.' ->
75 let j = ref (i + 1) in
76 while !j < len && (is_digit str.[!j] || str.[!j] = '.') do
77 incr j
78 done;
79 let num_str = String.sub str i (!j - i) in
80 col := !col + (!j - i);
81 let end_pos = { line = !line; col = !col; offset = !j } in
82 aux !j ({ token = Num (float_of_string num_str); start_pos; end_pos } :: acc)
83 | c when is_alpha c ->
84 let j = ref (i + 1) in
85 while !j < len && is_alphanum str.[!j] do
86 incr j
87 done;
88 let id = String.sub str i (!j - i) in
89 col := !col + (!j - i);
90 let end_pos = { line = !line; col = !col; offset = !j } in
91 let tok = if !j < len && str.[!j] = '(' then Ident id else Var id in
92 aux !j ({ token = tok; start_pos; end_pos } :: acc)
93 | c ->
94 failwith (Printf.sprintf "unexpected character: %c at line %d, column %d"
95 c !line !col)
96 in
97
98 let tokens = aux 0 [] in
99
100 let rec insert_implicit_mult = function
101 | [] -> []
102 | [t] -> [t]
103 | t1 :: t2 :: rest ->
104 if needs_implicit_mult t1.token t2.token then
105 let mult_pos = t2.start_pos in
106 t1 :: { token = Star; start_pos = mult_pos; end_pos = mult_pos } ::
107 insert_implicit_mult (t2 :: rest)
108 else
109 t1 :: insert_implicit_mult (t2 :: rest)
110 in
111
112 insert_implicit_mult tokens