blob: 6a86a2e1dc3fd1ad0fcb1e1af799c4f2cbdf31ad [file] [log] [blame]
Sketch🕴️cd59d222026-02-28 19:28:26 +04001package repl
2
3import (
4 "bufio"
5 "fmt"
6 "io"
7 "math"
8 "strings"
9
Sketch🕴️2f999112026-02-28 20:58:17 +040010 "matheval/ast"
Sketch🕴️cd59d222026-02-28 19:28:26 +040011 "matheval/evaluator"
12 "matheval/lexer"
13 "matheval/parser"
14)
15
16const prompt = ">> "
17
18// Run starts the read-eval-print loop, reading from r and writing to w.
19func Run(r io.Reader, w io.Writer) {
20 scanner := bufio.NewScanner(r)
Sketch🕴️2f999112026-02-28 20:58:17 +040021 ev := evaluator.New()
Sketch🕴️cd59d222026-02-28 19:28:26 +040022 fmt.Fprint(w, prompt)
23
24 for scanner.Scan() {
25 line := strings.TrimSpace(scanner.Text())
26 if line == "" {
27 fmt.Fprint(w, prompt)
28 continue
29 }
30
Sketch🕴️2f999112026-02-28 20:58:17 +040031 tokens, err := lexer.Tokenize(line)
Sketch🕴️cd59d222026-02-28 19:28:26 +040032 if err != nil {
33 fmt.Fprintf(w, "error: %s\n", err)
Sketch🕴️2f999112026-02-28 20:58:17 +040034 fmt.Fprint(w, prompt)
35 continue
36 }
37
38 stmt, err := parser.ParseLine(tokens)
39 if err != nil {
40 fmt.Fprintf(w, "error: %s\n", err)
41 fmt.Fprint(w, prompt)
42 continue
43 }
44
45 switch s := stmt.(type) {
46 case *ast.FuncDef:
47 if err := ev.Define(s); err != nil {
48 fmt.Fprintf(w, "error: %s\n", err)
49 } else {
50 fmt.Fprintf(w, "defined %s\n", s.Name)
51 }
52 case *ast.ExprStmt:
53 result, err := ev.Eval(s.Expr, nil)
54 if err != nil {
55 fmt.Fprintf(w, "error: %s\n", err)
56 } else {
57 fmt.Fprintln(w, formatResult(result))
58 }
Sketch🕴️cd59d222026-02-28 19:28:26 +040059 }
60
61 fmt.Fprint(w, prompt)
62 }
63}
64
Sketch🕴️cd59d222026-02-28 19:28:26 +040065// formatResult formats a float64 for display.
66// Whole numbers are printed without decimal points.
67func formatResult(val float64) string {
68 if val == math.Trunc(val) && !math.IsInf(val, 0) && !math.IsNaN(val) {
69 return fmt.Sprintf("%g", val)
70 }
71 return fmt.Sprintf("%g", val)
72}