all repos

scratch @ 6fd17573ac7a1d0ffb3ce5749e3e6db32ab8f3ff

⭐ me doing recreational ~~drugs~~ programming

scratch/blo/blox.go (view raw)

1
package main
2
3
import (
4
	"errors"
5
	"fmt"
6
	"io"
7
	"net/http"
8
	"os"
9
	"strings"
10
)
11
12
var escope = EvalScope{
13
	Vars: map[string]Expr{
14
		"my_name": {
15
			Type:  ExprStr,
16
			AsStr: "Olex",
17
		},
18
	},
19
	Funcs: map[string]Func{
20
		"let": func(ctx *EvalContext, args []Expr) (Expr, error) {
21
			if len(args) != 2 {
22
				return Expr{}, errors.New("let() expects two arguments")
23
			}
24
25
			if args[0].Type != ExprVar {
26
				return Expr{}, errors.New("First argument of let() has to be variable name")
27
			}
28
29
			name := args[0].AsVar
30
			value, err := ctx.EvalExpr(args[1])
31
			if err != nil {
32
				return Expr{}, err
33
			}
34
35
			ctx.TopScope().Vars[name] = value
36
			return Expr{}, nil
37
		},
38
39
		"define": func(ctx *EvalContext, args []Expr) (Expr, error) {
40
			if len(args) < 2 {
41
				return Expr{}, errors.New("define() expects at least 2 arguments")
42
			}
43
44
			if args[0].Type != ExprVar {
45
				return Expr{}, errors.New("define(): first argument must be the name of the function")
46
			}
47
48
			funName := args[0].AsVar
49
			if args[1].Type != ExprFuncall || args[1].AsFuncall.Name != "args" {
50
				return Expr{}, errors.New("define(): second argument must be the argument list")
51
			}
52
53
			funArgs := args[1].AsFuncall.Args
54
			for _, funArg := range funArgs {
55
				if funArg.Type != ExprVar {
56
					return Expr{}, errors.New("define(): argument list must consist of only variable names")
57
				}
58
			}
59
60
			ctx.TopScope().Funcs[funName] = func(context *EvalContext, callArgs []Expr) (Expr, error) {
61
				scope := EvalScope{
62
					Vars:  map[string]Expr{},
63
					Funcs: map[string]Func{},
64
				}
65
66
				if len(callArgs) != len(funArgs) {
67
					return Expr{}, errors.New(fmt.Sprintf("%s(): expected %d arguments but provided %d", funName, len(funArgs), len(args)))
68
				}
69
70
				for index := range callArgs {
71
					scope.Vars[funArgs[index].AsVar] = callArgs[index]
72
				}
73
74
				context.PushScope(scope)
75
				for _, stmt := range args[2:] {
76
					_, err := context.EvalExpr(stmt)
77
					if err != nil {
78
						return Expr{}, err
79
					}
80
				}
81
				context.PopScope()
82
83
				return Expr{}, nil
84
			}
85
86
			return Expr{}, nil
87
		},
88
89
		"say": func(context *EvalContext, args []Expr) (Expr, error) {
90
			for _, arg := range args {
91
				val, err := context.EvalExpr(arg)
92
				if err != nil {
93
					return Expr{}, err
94
				}
95
96
				switch val.Type {
97
				case ExprStr:
98
					fmt.Printf("%s", val.AsStr)
99
100
				case ExprInt:
101
					fmt.Printf("%d", val.AsInt)
102
				default:
103
					return Expr{}, errors.New("say() expects its arguments to be strings or numbers")
104
				}
105
			}
106
			fmt.Printf("\n")
107
			return Expr{}, nil
108
		},
109
110
		"http": func(context *EvalContext, args []Expr) (Expr, error) {
111
			var url strings.Builder
112
			for _, arg := range args {
113
				val, err := context.EvalExpr(arg)
114
				if err != nil {
115
					return Expr{}, err
116
				}
117
				if val.Type != ExprStr {
118
					return Expr{}, errors.New("http() expects its arguments to be strings")
119
				}
120
				fmt.Fprint(&url, val.AsStr)
121
			}
122
123
			resp, err := http.Get(url.String())
124
			if err != nil {
125
				return Expr{}, err
126
			}
127
			defer resp.Body.Close()
128
129
			body, err := io.ReadAll(resp.Body)
130
			if err != nil {
131
				return Expr{}, err
132
			}
133
134
			return Expr{
135
				Type:  ExprStr,
136
				AsStr: string(body),
137
			}, nil
138
		},
139
	},
140
}
141
142
func main() {
143
	ctx := EvalContext{}
144
	ctx.PushScope(escope)
145
146
	if len(os.Args) < 2 {
147
		fmt.Fprintf(os.Stderr, "ERROR: no input is provided\n")
148
		os.Exit(1)
149
	}
150
151
	fpath := os.Args[1]
152
	content, err := os.ReadFile(fpath)
153
	if err != nil {
154
		fmt.Fprintf(os.Stderr, "ERROR: could not read file %s: %s\n", fpath, err)
155
		os.Exit(1)
156
	}
157
158
	lexer := NewLexer([]rune(string(content)), fpath)
159
160
	exprs, err := ParseExprs(&lexer)
161
	if err != nil {
162
		fmt.Fprintln(os.Stderr, err)
163
		os.Exit(1)
164
	}
165
166
	for _, expr := range exprs {
167
		if _, err := ctx.EvalExpr(expr); err != nil {
168
			fmt.Fprintln(os.Stderr, err)
169
			os.Exit(1)
170
		}
171
	}
172
}