prometheus-lang 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prom/__init__.py +3 -0
- prom/ast.py +249 -0
- prom/builtins.py +437 -0
- prom/cli.py +279 -0
- prom/errors.py +81 -0
- prom/eval.py +495 -0
- prom/lexer.py +296 -0
- prom/parser.py +645 -0
- prom/pipeline.py +200 -0
- prom/stdlib/prelude.prom +193 -0
- prom/types.py +732 -0
- prometheus_lang-0.2.2.dist-info/METADATA +198 -0
- prometheus_lang-0.2.2.dist-info/RECORD +17 -0
- prometheus_lang-0.2.2.dist-info/WHEEL +5 -0
- prometheus_lang-0.2.2.dist-info/entry_points.txt +2 -0
- prometheus_lang-0.2.2.dist-info/licenses/LICENSE +202 -0
- prometheus_lang-0.2.2.dist-info/top_level.txt +1 -0
prom/__init__.py
ADDED
prom/ast.py
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Abstract syntax tree for Prometheus.
|
|
2
|
+
|
|
3
|
+
The AST is deliberately small: Prometheus is an expression language with a
|
|
4
|
+
thin declaration layer. Every node carries a Span for diagnostics.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
|
|
11
|
+
from .errors import Span
|
|
12
|
+
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
# Type annotations (surface syntax for types)
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class TAnn:
|
|
20
|
+
span: Span = field(default_factory=Span.unknown)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class TAName(TAnn):
|
|
25
|
+
name: str = ""
|
|
26
|
+
args: list["TAnn"] = field(default_factory=list)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class TAFun(TAnn):
|
|
31
|
+
params: list[TAnn] = field(default_factory=list)
|
|
32
|
+
ret: TAnn | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class TARecord(TAnn):
|
|
37
|
+
fields: list[tuple[str, TAnn]] = field(default_factory=list)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Expressions
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class Expr:
|
|
47
|
+
span: Span = field(default_factory=Span.unknown)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class Lit(Expr):
|
|
52
|
+
value: object = None
|
|
53
|
+
type_name: str = "" # Int | Float | Bool | Unit
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class Str(Expr):
|
|
58
|
+
# parts: list of ("text", str) or ("expr", Expr)
|
|
59
|
+
parts: list[tuple[str, object]] = field(default_factory=list)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class Var(Expr):
|
|
64
|
+
name: str = ""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class ListLit(Expr):
|
|
69
|
+
items: list[Expr] = field(default_factory=list)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class RecordLit(Expr):
|
|
74
|
+
fields: list[tuple[str, Expr]] = field(default_factory=list)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class Field(Expr):
|
|
79
|
+
obj: Expr | None = None
|
|
80
|
+
name: str = ""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class Call(Expr):
|
|
85
|
+
fn: Expr | None = None
|
|
86
|
+
args: list[Expr] = field(default_factory=list)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass
|
|
90
|
+
class Lambda(Expr):
|
|
91
|
+
params: list[str] = field(default_factory=list)
|
|
92
|
+
body: Expr | None = None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass
|
|
96
|
+
class BinOp(Expr):
|
|
97
|
+
op: str = ""
|
|
98
|
+
left: Expr | None = None
|
|
99
|
+
right: Expr | None = None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass
|
|
103
|
+
class UnOp(Expr):
|
|
104
|
+
op: str = ""
|
|
105
|
+
operand: Expr | None = None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass
|
|
109
|
+
class If(Expr):
|
|
110
|
+
cond: Expr | None = None
|
|
111
|
+
then: Expr | None = None
|
|
112
|
+
els: Expr | None = None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class MatchArm:
|
|
117
|
+
pattern: "Pattern"
|
|
118
|
+
body: Expr
|
|
119
|
+
span: Span
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass
|
|
123
|
+
class Match(Expr):
|
|
124
|
+
subject: Expr | None = None
|
|
125
|
+
arms: list[MatchArm] = field(default_factory=list)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass
|
|
129
|
+
class Bind:
|
|
130
|
+
name: str
|
|
131
|
+
ann: TAnn | None
|
|
132
|
+
expr: Expr
|
|
133
|
+
span: Span
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@dataclass
|
|
137
|
+
class ParBlock:
|
|
138
|
+
"""Bindings whose right-hand sides may evaluate in parallel.
|
|
139
|
+
|
|
140
|
+
Semantics are defined to be identical to sequential evaluation; the
|
|
141
|
+
expressions must be pure (the runtime enforces this dynamically in the
|
|
142
|
+
bootstrap, the native compiler will enforce it statically).
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
binds: list[Bind]
|
|
146
|
+
span: Span
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass
|
|
150
|
+
class Block(Expr):
|
|
151
|
+
# items: Bind | ParBlock | Expr ; the final item must be an Expr and is
|
|
152
|
+
# the block's value.
|
|
153
|
+
items: list[object] = field(default_factory=list)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# ---------------------------------------------------------------------------
|
|
157
|
+
# Patterns
|
|
158
|
+
# ---------------------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass
|
|
162
|
+
class Pattern:
|
|
163
|
+
span: Span = field(default_factory=Span.unknown)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class PWild(Pattern):
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@dataclass
|
|
172
|
+
class PVar(Pattern):
|
|
173
|
+
name: str = ""
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class PLit(Pattern):
|
|
178
|
+
value: object = None
|
|
179
|
+
type_name: str = ""
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@dataclass
|
|
183
|
+
class PCtor(Pattern):
|
|
184
|
+
name: str = ""
|
|
185
|
+
args: list[Pattern] = field(default_factory=list)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass
|
|
189
|
+
class PList(Pattern):
|
|
190
|
+
items: list[Pattern] = field(default_factory=list)
|
|
191
|
+
rest: str | None = None # name bound to the tail, or None
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# ---------------------------------------------------------------------------
|
|
195
|
+
# Declarations
|
|
196
|
+
# ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass
|
|
200
|
+
class Decl:
|
|
201
|
+
span: Span = field(default_factory=Span.unknown)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@dataclass
|
|
205
|
+
class DType(Decl):
|
|
206
|
+
name: str = ""
|
|
207
|
+
params: list[str] = field(default_factory=list)
|
|
208
|
+
ctors: list[tuple[str, list[TAnn]]] = field(default_factory=list)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@dataclass
|
|
212
|
+
class DFunc(Decl):
|
|
213
|
+
name: str = ""
|
|
214
|
+
params: list[tuple[str, TAnn | None]] = field(default_factory=list)
|
|
215
|
+
ret_ann: TAnn | None = None
|
|
216
|
+
body: Expr | None = None
|
|
217
|
+
intent: str | None = None
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@dataclass
|
|
221
|
+
class DVal(Decl):
|
|
222
|
+
name: str = ""
|
|
223
|
+
ann: TAnn | None = None
|
|
224
|
+
expr: Expr | None = None
|
|
225
|
+
intent: str | None = None
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@dataclass
|
|
229
|
+
class DUse(Decl):
|
|
230
|
+
"""use path.to.module — binds the module under its last path segment."""
|
|
231
|
+
|
|
232
|
+
parts: list[str] = field(default_factory=list)
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def alias(self) -> str:
|
|
236
|
+
return self.parts[-1]
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@dataclass
|
|
240
|
+
class DCheck(Decl):
|
|
241
|
+
label: str | None = None
|
|
242
|
+
expr: Expr | None = None
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@dataclass
|
|
246
|
+
class Program:
|
|
247
|
+
decls: list[Decl]
|
|
248
|
+
source: str
|
|
249
|
+
filename: str
|
prom/builtins.py
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"""Built-in functions: the primitive layer beneath the Prometheus prelude.
|
|
2
|
+
|
|
3
|
+
Kept deliberately small — everything that *can* be written in Prometheus
|
|
4
|
+
lives in stdlib/prelude.prom instead. Effectful builtins take a capability
|
|
5
|
+
as their first argument; without the capability value they are uncallable,
|
|
6
|
+
which is the whole security model in one sentence.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import math
|
|
12
|
+
import os
|
|
13
|
+
import random
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
from .errors import RuntimeError_, Span
|
|
17
|
+
from .eval import (
|
|
18
|
+
Builtin, Capability, Data, MapValue, list_iter, make_list, show_value,
|
|
19
|
+
)
|
|
20
|
+
from .types import (
|
|
21
|
+
BOOL, FLOAT, INT, TEXT, UNIT, Scheme, TCon, TFun, TRecord, TVar, tlist,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _ok(v):
|
|
26
|
+
return Data("Ok", (v,))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _err(msg: str):
|
|
30
|
+
return Data("Err", (msg,))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _some(v):
|
|
34
|
+
return Data("Some", (v,))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
NONE = Data("None")
|
|
38
|
+
|
|
39
|
+
SCALARS = (int, float, bool, str)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _require_scalar_key(k, span):
|
|
43
|
+
if not isinstance(k, SCALARS):
|
|
44
|
+
raise RuntimeError_(
|
|
45
|
+
f"map keys must be scalars (Int, Float, Bool, Text) in v0.2, got {show_value(k)}",
|
|
46
|
+
span,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _pure(name, arity, fn):
|
|
51
|
+
return Builtin(name, arity, lambda rt, args, span: fn(args, span), effectful=False)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _effect(name, arity, fn):
|
|
55
|
+
return Builtin(name, arity, fn, effectful=True)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _check_cap(args, kind: str, name: str, span: Span):
|
|
59
|
+
cap = args[0]
|
|
60
|
+
if not isinstance(cap, Capability) or cap.kind != kind:
|
|
61
|
+
raise RuntimeError_(f"{name} requires the {kind} capability", span)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# -- pure primitives ---------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _show(args, span):
|
|
68
|
+
return show_value(args[0])
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _length(args, span):
|
|
72
|
+
return sum(1 for _ in list_iter(args[0]))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _text_length(args, span):
|
|
76
|
+
return len(args[0])
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _range(args, span):
|
|
80
|
+
lo, hi = args
|
|
81
|
+
return make_list(range(lo, hi))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _split(args, span):
|
|
85
|
+
text, sep = args
|
|
86
|
+
if sep == "":
|
|
87
|
+
raise RuntimeError_("split separator must not be empty", span)
|
|
88
|
+
return make_list(text.split(sep))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _join(args, span):
|
|
92
|
+
parts, sep = args
|
|
93
|
+
return sep.join(list_iter(parts))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _to_float(args, span):
|
|
97
|
+
return float(args[0])
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _floor(args, span):
|
|
101
|
+
return math.floor(args[0])
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _sqrt(args, span):
|
|
105
|
+
if args[0] < 0.0:
|
|
106
|
+
raise RuntimeError_("sqrt of a negative number", span)
|
|
107
|
+
return math.sqrt(args[0])
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _contains(args, span):
|
|
111
|
+
return args[1] in args[0]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _starts_with(args, span):
|
|
115
|
+
return args[0].startswith(args[1])
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _ends_with(args, span):
|
|
119
|
+
return args[0].endswith(args[1])
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _replace(args, span):
|
|
123
|
+
text, old, new = args
|
|
124
|
+
if old == "":
|
|
125
|
+
raise RuntimeError_("replace: the text to replace must not be empty", span)
|
|
126
|
+
return text.replace(old, new)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _trim(args, span):
|
|
130
|
+
return args[0].strip()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _upper(args, span):
|
|
134
|
+
return args[0].upper()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _lower(args, span):
|
|
138
|
+
return args[0].lower()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _slice_text(args, span):
|
|
142
|
+
text, start, end = args
|
|
143
|
+
n = len(text)
|
|
144
|
+
start = max(0, min(start, n))
|
|
145
|
+
end = max(start, min(end, n))
|
|
146
|
+
return text[start:end]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _parse_int(args, span):
|
|
150
|
+
t = args[0].strip()
|
|
151
|
+
sign_ok = t and (t[0] in "+-" and t[1:].isdigit() or t.isdigit())
|
|
152
|
+
return _some(int(t)) if sign_ok else NONE
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _parse_float(args, span):
|
|
156
|
+
try:
|
|
157
|
+
return _some(float(args[0].strip()))
|
|
158
|
+
except ValueError:
|
|
159
|
+
return NONE
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _sort(args, span):
|
|
163
|
+
items = list(list_iter(args[0]))
|
|
164
|
+
kinds = {("text" if isinstance(x, str) else "num" if isinstance(x, (int, float))
|
|
165
|
+
and not isinstance(x, bool) else "other") for x in items}
|
|
166
|
+
if kinds - {"text"} and kinds - {"num"}:
|
|
167
|
+
raise RuntimeError_(
|
|
168
|
+
"sort works on lists of numbers or lists of Text (v0.2)", span
|
|
169
|
+
)
|
|
170
|
+
return make_list(sorted(items))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _abs(args, span):
|
|
174
|
+
return abs(args[0])
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _min(args, span):
|
|
178
|
+
return min(args[0], args[1])
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _max(args, span):
|
|
182
|
+
return max(args[0], args[1])
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# -- maps ---------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _put(args, span):
|
|
189
|
+
m, k, v = args
|
|
190
|
+
_require_scalar_key(k, span)
|
|
191
|
+
entries = dict(m.entries)
|
|
192
|
+
entries[k] = v
|
|
193
|
+
return MapValue(entries)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _get(args, span):
|
|
197
|
+
m, k = args
|
|
198
|
+
_require_scalar_key(k, span)
|
|
199
|
+
return _some(m.entries[k]) if k in m.entries else NONE
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _remove(args, span):
|
|
203
|
+
m, k = args
|
|
204
|
+
_require_scalar_key(k, span)
|
|
205
|
+
if k not in m.entries:
|
|
206
|
+
return m
|
|
207
|
+
entries = dict(m.entries)
|
|
208
|
+
del entries[k]
|
|
209
|
+
return MapValue(entries)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _has(args, span):
|
|
213
|
+
m, k = args
|
|
214
|
+
_require_scalar_key(k, span)
|
|
215
|
+
return k in m.entries
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _keys(args, span):
|
|
219
|
+
return make_list(sorted(args[0].entries.keys(), key=repr))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _size(args, span):
|
|
223
|
+
return len(args[0].entries)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# -- effectful primitives (capability-gated) ---------------------------------
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _print(rt, args, span):
|
|
230
|
+
_check_cap(args, "Io", "print", span)
|
|
231
|
+
rt.stdout.write(show_value(args[1]) + "\n")
|
|
232
|
+
from .eval import UNIT as U
|
|
233
|
+
|
|
234
|
+
return U
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _read_line(rt, args, span):
|
|
238
|
+
_check_cap(args, "Io", "read_line", span)
|
|
239
|
+
line = rt.stdin.readline()
|
|
240
|
+
return line[:-1] if line.endswith("\n") else line
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _read_file(rt, args, span):
|
|
244
|
+
_check_cap(args, "Fs", "read_file", span)
|
|
245
|
+
try:
|
|
246
|
+
with open(args[1], "r", encoding="utf-8") as f:
|
|
247
|
+
return _ok(f.read())
|
|
248
|
+
except OSError as err:
|
|
249
|
+
return _err(f"cannot read {args[1]}: {err.strerror or err}")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _write_file(rt, args, span):
|
|
253
|
+
_check_cap(args, "Fs", "write_file", span)
|
|
254
|
+
from .eval import UNIT as U
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
with open(args[1], "w", encoding="utf-8") as f:
|
|
258
|
+
f.write(args[2])
|
|
259
|
+
return _ok(U)
|
|
260
|
+
except OSError as err:
|
|
261
|
+
return _err(f"cannot write {args[1]}: {err.strerror or err}")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _list_dir(rt, args, span):
|
|
265
|
+
_check_cap(args, "Fs", "list_dir", span)
|
|
266
|
+
try:
|
|
267
|
+
return _ok(make_list(sorted(os.listdir(args[1]))))
|
|
268
|
+
except OSError as err:
|
|
269
|
+
return _err(f"cannot list {args[1]}: {err.strerror or err}")
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _exists(rt, args, span):
|
|
273
|
+
_check_cap(args, "Fs", "exists", span)
|
|
274
|
+
return os.path.exists(args[1])
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _get_env(rt, args, span):
|
|
278
|
+
_check_cap(args, "Env", "get_env", span)
|
|
279
|
+
v = os.environ.get(args[1])
|
|
280
|
+
return NONE if v is None else _some(v)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _http_get(rt, args, span):
|
|
284
|
+
_check_cap(args, "Net", "http_get", span)
|
|
285
|
+
import urllib.error
|
|
286
|
+
import urllib.request
|
|
287
|
+
|
|
288
|
+
url = args[1]
|
|
289
|
+
if not url.startswith(("http://", "https://")):
|
|
290
|
+
return _err(f"http_get: unsupported URL scheme in {url}")
|
|
291
|
+
try:
|
|
292
|
+
with urllib.request.urlopen(url, timeout=30) as resp:
|
|
293
|
+
return _ok(resp.read().decode("utf-8", errors="replace"))
|
|
294
|
+
except (urllib.error.URLError, OSError, ValueError) as err:
|
|
295
|
+
return _err(f"http_get {url}: {err}")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _rand_int(rt, args, span):
|
|
299
|
+
_check_cap(args, "Rand", "rand_int", span)
|
|
300
|
+
lo, hi = args[1], args[2]
|
|
301
|
+
if lo > hi:
|
|
302
|
+
raise RuntimeError_(f"rand_int: empty range {lo}..{hi}", span)
|
|
303
|
+
return random.randint(lo, hi)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _rand_float(rt, args, span):
|
|
307
|
+
_check_cap(args, "Rand", "rand_float", span)
|
|
308
|
+
return random.random()
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _now_ms(rt, args, span):
|
|
312
|
+
_check_cap(args, "Clock", "now_ms", span)
|
|
313
|
+
return int(time.time() * 1000)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
IO = TCon("Io")
|
|
317
|
+
FS = TCon("Fs")
|
|
318
|
+
CLOCK = TCon("Clock")
|
|
319
|
+
ENV = TCon("Env")
|
|
320
|
+
NET = TCon("Net")
|
|
321
|
+
RAND = TCon("Rand")
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _result(ok, err):
|
|
325
|
+
return TCon("Result", [ok, err])
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _option(a):
|
|
329
|
+
return TCon("Option", [a])
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _map_t(k, v):
|
|
333
|
+
return TCon("Map", [k, v])
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def make_builtins() -> tuple[dict[str, object], dict[str, Scheme]]:
|
|
337
|
+
"""Returns (value environment, type environment) for the primitives."""
|
|
338
|
+
a = TVar()
|
|
339
|
+
b = TVar()
|
|
340
|
+
values: dict[str, object] = {
|
|
341
|
+
"show": _pure("show", 1, _show),
|
|
342
|
+
"length": _pure("length", 1, _length),
|
|
343
|
+
"text_length": _pure("text_length", 1, _text_length),
|
|
344
|
+
"range": _pure("range", 2, _range),
|
|
345
|
+
"split": _pure("split", 2, _split),
|
|
346
|
+
"join": _pure("join", 2, _join),
|
|
347
|
+
"to_float": _pure("to_float", 1, _to_float),
|
|
348
|
+
"floor": _pure("floor", 1, _floor),
|
|
349
|
+
"sqrt": _pure("sqrt", 1, _sqrt),
|
|
350
|
+
"text_contains": _pure("text_contains", 2, _contains),
|
|
351
|
+
"starts_with": _pure("starts_with", 2, _starts_with),
|
|
352
|
+
"ends_with": _pure("ends_with", 2, _ends_with),
|
|
353
|
+
"replace": _pure("replace", 3, _replace),
|
|
354
|
+
"trim": _pure("trim", 1, _trim),
|
|
355
|
+
"upper": _pure("upper", 1, _upper),
|
|
356
|
+
"lower": _pure("lower", 1, _lower),
|
|
357
|
+
"slice": _pure("slice", 3, _slice_text),
|
|
358
|
+
"parse_int": _pure("parse_int", 1, _parse_int),
|
|
359
|
+
"parse_float": _pure("parse_float", 1, _parse_float),
|
|
360
|
+
"sort": _pure("sort", 1, _sort),
|
|
361
|
+
"abs": _pure("abs", 1, _abs),
|
|
362
|
+
"min": _pure("min", 2, _min),
|
|
363
|
+
"max": _pure("max", 2, _max),
|
|
364
|
+
"empty_map": MapValue({}),
|
|
365
|
+
"put": _pure("put", 3, _put),
|
|
366
|
+
"get": _pure("get", 2, _get),
|
|
367
|
+
"remove": _pure("remove", 2, _remove),
|
|
368
|
+
"has": _pure("has", 2, _has),
|
|
369
|
+
"keys": _pure("keys", 1, _keys),
|
|
370
|
+
"size": _pure("size", 1, _size),
|
|
371
|
+
"print": _effect("print", 2, _print),
|
|
372
|
+
"read_line": _effect("read_line", 1, _read_line),
|
|
373
|
+
"read_file": _effect("read_file", 2, _read_file),
|
|
374
|
+
"write_file": _effect("write_file", 3, _write_file),
|
|
375
|
+
"list_dir": _effect("list_dir", 2, _list_dir),
|
|
376
|
+
"exists": _effect("exists", 2, _exists),
|
|
377
|
+
"get_env": _effect("get_env", 2, _get_env),
|
|
378
|
+
"http_get": _effect("http_get", 2, _http_get),
|
|
379
|
+
"rand_int": _effect("rand_int", 3, _rand_int),
|
|
380
|
+
"rand_float": _effect("rand_float", 1, _rand_float),
|
|
381
|
+
"now_ms": _effect("now_ms", 1, _now_ms),
|
|
382
|
+
}
|
|
383
|
+
n1 = TVar(numeric=True)
|
|
384
|
+
n2 = TVar(numeric=True)
|
|
385
|
+
n3 = TVar(numeric=True)
|
|
386
|
+
s1 = TVar()
|
|
387
|
+
mk, mv = TVar(), TVar()
|
|
388
|
+
mk2, mv2 = TVar(), TVar()
|
|
389
|
+
mk3, mv3 = TVar(), TVar()
|
|
390
|
+
mk4, mv4 = TVar(), TVar()
|
|
391
|
+
mk5, mv5 = TVar(), TVar()
|
|
392
|
+
mk6, mv6 = TVar(), TVar()
|
|
393
|
+
mk7, mv7 = TVar(), TVar()
|
|
394
|
+
types: dict[str, Scheme] = {
|
|
395
|
+
"show": Scheme([a], TFun([a], TEXT)),
|
|
396
|
+
"length": Scheme([b], TFun([tlist(b)], INT)),
|
|
397
|
+
"text_length": Scheme([], TFun([TEXT], INT)),
|
|
398
|
+
"range": Scheme([], TFun([INT, INT], tlist(INT))),
|
|
399
|
+
"split": Scheme([], TFun([TEXT, TEXT], tlist(TEXT))),
|
|
400
|
+
"join": Scheme([], TFun([tlist(TEXT), TEXT], TEXT)),
|
|
401
|
+
"to_float": Scheme([], TFun([INT], FLOAT)),
|
|
402
|
+
"floor": Scheme([], TFun([FLOAT], INT)),
|
|
403
|
+
"sqrt": Scheme([], TFun([FLOAT], FLOAT)),
|
|
404
|
+
"text_contains": Scheme([], TFun([TEXT, TEXT], BOOL)),
|
|
405
|
+
"starts_with": Scheme([], TFun([TEXT, TEXT], BOOL)),
|
|
406
|
+
"ends_with": Scheme([], TFun([TEXT, TEXT], BOOL)),
|
|
407
|
+
"replace": Scheme([], TFun([TEXT, TEXT, TEXT], TEXT)),
|
|
408
|
+
"trim": Scheme([], TFun([TEXT], TEXT)),
|
|
409
|
+
"upper": Scheme([], TFun([TEXT], TEXT)),
|
|
410
|
+
"lower": Scheme([], TFun([TEXT], TEXT)),
|
|
411
|
+
"slice": Scheme([], TFun([TEXT, INT, INT], TEXT)),
|
|
412
|
+
"parse_int": Scheme([], TFun([TEXT], _option(INT))),
|
|
413
|
+
"parse_float": Scheme([], TFun([TEXT], _option(FLOAT))),
|
|
414
|
+
"sort": Scheme([s1], TFun([tlist(s1)], tlist(s1))),
|
|
415
|
+
"abs": Scheme([n1], TFun([n1], n1)),
|
|
416
|
+
"min": Scheme([n2], TFun([n2, n2], n2)),
|
|
417
|
+
"max": Scheme([n3], TFun([n3, n3], n3)),
|
|
418
|
+
"empty_map": Scheme([mk, mv], _map_t(mk, mv)),
|
|
419
|
+
"put": Scheme([mk2, mv2], TFun([_map_t(mk2, mv2), mk2, mv2], _map_t(mk2, mv2))),
|
|
420
|
+
"get": Scheme([mk3, mv3], TFun([_map_t(mk3, mv3), mk3], _option(mv3))),
|
|
421
|
+
"remove": Scheme([mk4, mv4], TFun([_map_t(mk4, mv4), mk4], _map_t(mk4, mv4))),
|
|
422
|
+
"has": Scheme([mk5, mv5], TFun([_map_t(mk5, mv5), mk5], BOOL)),
|
|
423
|
+
"keys": Scheme([mk6, mv6], TFun([_map_t(mk6, mv6)], tlist(mk6))),
|
|
424
|
+
"size": Scheme([mk7, mv7], TFun([_map_t(mk7, mv7)], INT)),
|
|
425
|
+
"print": Scheme([], TFun([IO, TEXT], UNIT)),
|
|
426
|
+
"read_line": Scheme([], TFun([IO], TEXT)),
|
|
427
|
+
"read_file": Scheme([], TFun([FS, TEXT], _result(TEXT, TEXT))),
|
|
428
|
+
"write_file": Scheme([], TFun([FS, TEXT, TEXT], _result(UNIT, TEXT))),
|
|
429
|
+
"list_dir": Scheme([], TFun([FS, TEXT], _result(tlist(TEXT), TEXT))),
|
|
430
|
+
"exists": Scheme([], TFun([FS, TEXT], BOOL)),
|
|
431
|
+
"get_env": Scheme([], TFun([ENV, TEXT], _option(TEXT))),
|
|
432
|
+
"http_get": Scheme([], TFun([NET, TEXT], _result(TEXT, TEXT))),
|
|
433
|
+
"rand_int": Scheme([], TFun([RAND, INT, INT], INT)),
|
|
434
|
+
"rand_float": Scheme([], TFun([RAND], FLOAT)),
|
|
435
|
+
"now_ms": Scheme([], TFun([CLOCK], INT)),
|
|
436
|
+
}
|
|
437
|
+
return values, types
|