future-lang 0.4.1 → 0.4.2
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.
- package/ARCHITECTURE.md +47 -13
- package/FUTURE_FOR_LLMS.md +21 -1
- package/MIGRATION.md +209 -1
- package/README.md +103 -11
- package/ROADMAP.md +58 -11
- package/package.json +1 -1
- package/runtime/ai.js +34 -0
- package/runtime/index.js +8 -2
- package/runtime/providers/anthropic.js +32 -1
- package/runtime/providers/openai-compat.js +31 -1
- package/src/cli.js +28 -7
package/ARCHITECTURE.md
CHANGED
|
@@ -15,22 +15,25 @@ future-lang/
|
|
|
15
15
|
│ ├── index.js # Public API — compile(source, options)
|
|
16
16
|
│ ├── lexer.js # Phase 1: source text → token list
|
|
17
17
|
│ ├── parser.js # Phase 2: token list → AST
|
|
18
|
-
│ ├── ast.js # AST node types and factory functions (
|
|
18
|
+
│ ├── ast.js # AST node types and factory functions (24 types)
|
|
19
19
|
│ ├── generator.js # Phase 3: AST → JavaScript source
|
|
20
20
|
│ ├── errors.js # FutureError with line/column tracking
|
|
21
|
-
│
|
|
21
|
+
│ ├── formatter.js # Line-based auto-formatter (future fmt)
|
|
22
|
+
│ ├── sourcemap.js # VLQ encoder + Source Map v3 builder
|
|
23
|
+
│ └── cli.js # CLI binary: 9 commands including future test
|
|
22
24
|
│
|
|
23
25
|
├── runtime/ # Capability modules (imported by generated JS)
|
|
24
26
|
│ ├── index.js # Aggregator: runtime object + manifest + introspection
|
|
25
27
|
│ │
|
|
26
|
-
│ ├── ai.js # Text generation —
|
|
27
|
-
│ ├── http.js # REST API
|
|
28
|
+
│ ├── ai.js # Text generation — ask/chat/stream accept opts {temperature, max_tokens, model}
|
|
29
|
+
│ ├── http.js # REST API — HttpError class, configure() for global headers/timeout
|
|
28
30
|
│ ├── mqtt.js # Pub/sub messaging (real broker or in-process loopback)
|
|
29
31
|
│ ├── tts.js # Text-to-speech (system engine)
|
|
30
32
|
│ ├── rag.js # RAG — chunk → embed → store → retrieve → answer
|
|
31
33
|
│ ├── vision.js # Vision AI — describe, detect, ocr, classify, compare
|
|
32
34
|
│ ├── home.js # Home automation (composes over MQTT)
|
|
33
35
|
│ │
|
|
36
|
+
│ ├── assert.js # Test assertions — ok/equal/notEqual/deepEqual/fail
|
|
34
37
|
│ ├── memory.js # In-process key-value store (set/get/search/delete/forget)
|
|
35
38
|
│ ├── schedule.js # Recurring / one-shot / cron scheduling
|
|
36
39
|
│ ├── system.js # OS utilities: exec, open, notify, read, write
|
|
@@ -41,7 +44,7 @@ future-lang/
|
|
|
41
44
|
│ │
|
|
42
45
|
│ ├── providers/ # AI provider implementations
|
|
43
46
|
│ │ ├── index.js # Provider factory + env-var resolution
|
|
44
|
-
│ │ ├── anthropic.js # Anthropic Messages API
|
|
47
|
+
│ │ ├── anthropic.js # Anthropic Messages API — AiError class, opts forwarding
|
|
45
48
|
│ │ ├── openai-compat.js # OpenAI-compatible (OpenAI, Ollama, Gemini, OpenRouter, …)
|
|
46
49
|
│ │ └── util.js # SSE parser, keyword vector, cosine similarity
|
|
47
50
|
│ │
|
|
@@ -69,6 +72,7 @@ future-lang/
|
|
|
69
72
|
│
|
|
70
73
|
├── future-browser.js # Browser entry point: window.Future + <script type="future">
|
|
71
74
|
├── future-playground.html # In-browser editor (11 examples, live compile)
|
|
75
|
+
├── FUTURE_FOR_LLMS.md # BNF grammar + all APIs — quick-reference for AI assistants
|
|
72
76
|
├── package.json
|
|
73
77
|
├── ARCHITECTURE.md # This file
|
|
74
78
|
├── ROADMAP.md # Feature roadmap
|
|
@@ -114,7 +118,7 @@ In **browser mode** (`browserMode: true` option), the `import` statement is omit
|
|
|
114
118
|
|
|
115
119
|
## Lexer
|
|
116
120
|
|
|
117
|
-
### Keywords (
|
|
121
|
+
### Keywords (26)
|
|
118
122
|
|
|
119
123
|
| Keyword | Token | Purpose |
|
|
120
124
|
|---------|-------|---------|
|
|
@@ -136,7 +140,8 @@ In **browser mode** (`browserMode: true` option), the `import` statement is omit
|
|
|
136
140
|
| `null` / `none` | NULL | Null literal (two spellings) |
|
|
137
141
|
| `stream` | STREAM | Streaming statement |
|
|
138
142
|
| `agent` | AGENT | Agent declaration |
|
|
139
|
-
| `use` | USE |
|
|
143
|
+
| `use` | USE | Import statement / capability declaration inside `agent` |
|
|
144
|
+
| `as` | AS | Alias in `use "..." as alias` |
|
|
140
145
|
|
|
141
146
|
### String escape
|
|
142
147
|
|
|
@@ -144,12 +149,13 @@ In **browser mode** (`browserMode: true` option), the `import` statement is omit
|
|
|
144
149
|
|
|
145
150
|
---
|
|
146
151
|
|
|
147
|
-
## AST Node Types (
|
|
152
|
+
## AST Node Types (24)
|
|
148
153
|
|
|
149
154
|
| Category | Nodes |
|
|
150
155
|
|----------|-------|
|
|
151
156
|
| Program | `Program` |
|
|
152
157
|
| Statements | `PrintStatement`, `Assignment`, `IfStatement`, `FunctionDeclaration`, `ReturnStatement`, `ExpressionStatement` |
|
|
158
|
+
| Import | `UseStatement` — `use "path"` / `use "path" as alias` |
|
|
153
159
|
| Control flow | `ForStatement`, `WhileStatement`, `TryStatement` |
|
|
154
160
|
| Event statements | `OnStatement`, `EveryStatement` |
|
|
155
161
|
| AI/IoT statements | `AgentDeclaration`, `StreamStatement` |
|
|
@@ -191,12 +197,37 @@ export const NAMESPACES = new Set([
|
|
|
191
197
|
'ai', 'http', 'mqtt', 'tts',
|
|
192
198
|
'rag', 'vision', 'home',
|
|
193
199
|
'memory', 'schedule', 'system', 'device',
|
|
194
|
-
'math',
|
|
200
|
+
'math', 'assert',
|
|
195
201
|
]);
|
|
196
202
|
```
|
|
197
203
|
|
|
198
204
|
Any identifier in this set, when used as the object of a `MemberExpression` or `CallExpression`, is routed through `__rt` in ASYNC mode. Adding a new capability is just a name in this set plus a matching runtime module — no grammar change.
|
|
199
205
|
|
|
206
|
+
`use ... as alias` imports are tracked in a `useAliases` Set and explicitly excluded from namespace routing — `m.add()` never becomes `__rt.m.add()`.
|
|
207
|
+
|
|
208
|
+
### Source maps (`sourceMaps` option)
|
|
209
|
+
|
|
210
|
+
When `compile(source, { sourceMaps: true })` is used, the generator prefixes each top-level statement line with `/*@FL:N*/` (where N is the original `.future` line number). `src/sourcemap.js` post-processes this output:
|
|
211
|
+
|
|
212
|
+
1. Scans the generated JS line by line.
|
|
213
|
+
2. Strips `/*@FL:N*/` markers.
|
|
214
|
+
3. Builds VLQ-encoded `mappings` in Source Map v3 format.
|
|
215
|
+
4. Returns `{ code: cleanJS, map: { version: 3, sources, sourcesContent, mappings } }`.
|
|
216
|
+
|
|
217
|
+
The CLI appends `//# sourceMappingURL=file.js.map` to the clean JS and writes the map as JSON.
|
|
218
|
+
|
|
219
|
+
### Import system (UseStatement)
|
|
220
|
+
|
|
221
|
+
`use "./file.future"` statements are emitted before all other code. At compile time, when `resolveSource` is provided, the compiler reads the imported file, parses it, and extracts top-level `FunctionDeclaration` names. This enables named imports:
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
import { formatName, greet } from "./utils.js"; // named import (default)
|
|
225
|
+
import * as m from "./math.js"; // namespace import (alias)
|
|
226
|
+
import * as df from "date-fns"; // npm package (alias)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The `pathMap` option (a `Map<futurePath, fileURL>`) allows `future run` to redirect imports to temp `.mjs` files compiled in `tmpdir`.
|
|
230
|
+
|
|
200
231
|
### String interpolation
|
|
201
232
|
|
|
202
233
|
String literals containing `{identifier}` or `{identifier.prop}` are emitted as JS template literals. Namespace references inside strings are correctly prefixed with `__rt.` in ASYNC mode:
|
|
@@ -227,7 +258,7 @@ In ASYNC mode, ALL call expressions where the callee is a `MemberExpression` are
|
|
|
227
258
|
```
|
|
228
259
|
runtime/index.js
|
|
229
260
|
│
|
|
230
|
-
├── Imports
|
|
261
|
+
├── Imports 13 capability modules (+ readline for input())
|
|
231
262
|
├── Exports `runtime` object → used as `__rt` in generated JS
|
|
232
263
|
├── Exports `manifest` → structured metadata for every function
|
|
233
264
|
└── Attaches introspection:
|
|
@@ -237,6 +268,8 @@ runtime/index.js
|
|
|
237
268
|
runtime.input(prompt) → Promise<string> (stdin)
|
|
238
269
|
```
|
|
239
270
|
|
|
271
|
+
When `FUTURE_DEBUG=1` (`future run --debug`), the runtime is wrapped by `wrapDebug()` which proxies every namespace method to log timing and arguments to stderr with ANSI colours.
|
|
272
|
+
|
|
240
273
|
### Manifest shape (per function)
|
|
241
274
|
|
|
242
275
|
```js
|
|
@@ -402,14 +435,15 @@ await __rt.ai.stream("Tell me a story", async (chunk) => {
|
|
|
402
435
|
|
|
403
436
|
---
|
|
404
437
|
|
|
405
|
-
## Adding a New Capability (
|
|
438
|
+
## Adding a New Capability (5 steps)
|
|
406
439
|
|
|
407
440
|
1. Create `runtime/mymodule.js` with named exports.
|
|
408
441
|
2. Import it in `runtime/index.js`, add to `runtime` object, `MODULE_NAMES`, and `manifest`.
|
|
409
442
|
3. Add the namespace name to `NAMESPACES` in `src/generator.js`.
|
|
410
|
-
4. Add the
|
|
443
|
+
4. Add the namespace name to `RESERVED_NAMESPACES` in `src/parser.js`.
|
|
444
|
+
5. Add the module to `browserRuntime` in `runtime/browser.js` if browser support is wanted.
|
|
411
445
|
|
|
412
|
-
No grammar, lexer,
|
|
446
|
+
No grammar, lexer, or AST changes needed.
|
|
413
447
|
|
|
414
448
|
---
|
|
415
449
|
|
package/FUTURE_FOR_LLMS.md
CHANGED
|
@@ -4,6 +4,19 @@ Future is a small language that compiles to JavaScript. It does NOT exist in you
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## Capability layers
|
|
8
|
+
|
|
9
|
+
| Layer | Namespaces | Notes |
|
|
10
|
+
|-------|-----------|-------|
|
|
11
|
+
| **Core language** | *(none)* | variables, if/end, for/end, while/end, try/catch/end, functions, lists, objects, strings |
|
|
12
|
+
| **Standard** | `math` `http` `memory` `system` `schedule` | General-purpose I/O; triggers async mode |
|
|
13
|
+
| **Extended** | `ai` `rag` `vision` `mqtt` `tts` `home` `device` `agent` | AI, IoT, automation; triggers async mode |
|
|
14
|
+
| **Testing** | `assert` | Use only in `*.test.future` files |
|
|
15
|
+
|
|
16
|
+
Any call from Standard or Extended triggers ASYNC mode automatically.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
7
20
|
## Reserved words
|
|
8
21
|
|
|
9
22
|
```
|
|
@@ -176,10 +189,17 @@ embed = ai.embed("text to embed")
|
|
|
176
189
|
ai.configure("openai", "sk-...")
|
|
177
190
|
ai.configure("ollama")
|
|
178
191
|
|
|
179
|
-
# With options
|
|
192
|
+
# With inference options
|
|
180
193
|
answer = ai.ask("Explain quantum physics", { temperature: 0.2 max_tokens: 200 })
|
|
181
194
|
reply = ai.chat(messages, { model: "gpt-4o" temperature: 0.7 })
|
|
182
195
|
|
|
196
|
+
# Structured response: text + token counts + model + provider
|
|
197
|
+
result = ai.complete("Summarise this in one line.")
|
|
198
|
+
print result.text
|
|
199
|
+
print result.tokens.total # total tokens used
|
|
200
|
+
print result.model # e.g. "claude-sonnet-4-6"
|
|
201
|
+
print result.provider # e.g. "anthropic"
|
|
202
|
+
|
|
183
203
|
stream ai.ask("Tell me a story")
|
|
184
204
|
print chunk
|
|
185
205
|
end
|
package/MIGRATION.md
CHANGED
|
@@ -4,7 +4,215 @@ All releases are **additive only**. No existing Future program has ever required
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## v0.
|
|
7
|
+
## v0.4.0 → v0.4.1 (current — Gemini improvements)
|
|
8
|
+
|
|
9
|
+
**No breaking changes.**
|
|
10
|
+
|
|
11
|
+
### AI inference options
|
|
12
|
+
|
|
13
|
+
`ai.ask`, `ai.chat`, and `ai.stream` now accept an optional second argument with inference parameters:
|
|
14
|
+
|
|
15
|
+
```future
|
|
16
|
+
# temperature controls creativity (0.0 = deterministic, 1.0 = creative)
|
|
17
|
+
precise = ai.ask("List the planets.", { temperature: 0.1 max_tokens: 100 })
|
|
18
|
+
creative = ai.chat(messages, { temperature: 0.9 model: "gpt-4o" })
|
|
19
|
+
ai.stream(prompt, { temperature: 0.7 })
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Supported fields: `temperature`, `max_tokens`, `model`, `system` (Anthropic only).
|
|
23
|
+
Both providers (Anthropic native, OpenAI-compat) forward all opts to the API.
|
|
24
|
+
|
|
25
|
+
### Structured errors
|
|
26
|
+
|
|
27
|
+
`HttpError` and `AiError` replace generic `Error` objects. Catchable with `try/catch err`:
|
|
28
|
+
|
|
29
|
+
```future
|
|
30
|
+
try
|
|
31
|
+
data = http.get("https://api.example.com/private")
|
|
32
|
+
catch err
|
|
33
|
+
print "{err.status}" # 401
|
|
34
|
+
print "{err.code}" # HTTP_401
|
|
35
|
+
print "{err.url}" # https://api.example.com/private
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
try
|
|
39
|
+
reply = ai.ask("hello")
|
|
40
|
+
catch err
|
|
41
|
+
print "{err.provider}" # anthropic
|
|
42
|
+
print "{err.status}" # 429
|
|
43
|
+
print "{err.code}" # AI_HTTP_429
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Error classes are exported from `runtime/providers/anthropic.js` (`AiError`) and `runtime/http.js` (`HttpError`) for use in JS integrations.
|
|
48
|
+
|
|
49
|
+
### `http.configure()` — global request defaults
|
|
50
|
+
|
|
51
|
+
```future
|
|
52
|
+
# Call once at the top of your program
|
|
53
|
+
http.configure({ headers: { Authorization: "Bearer {token}" } timeout: 5000 })
|
|
54
|
+
|
|
55
|
+
# All subsequent http.get / http.post calls include the header and timeout automatically
|
|
56
|
+
data = http.get("https://api.example.com/me")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Supported fields: `headers` (merged with per-call headers), `timeout` (ms, uses `AbortSignal.timeout`).
|
|
60
|
+
|
|
61
|
+
### Source maps (`future compile --sourcemap`)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
future compile --sourcemap program.future
|
|
65
|
+
# Produces: program.js + program.js.map
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The `.js.map` is a standard Source Map v3 file. Stack traces in Node.js, VS Code, and browser DevTools automatically map back to the original `.future` line numbers.
|
|
69
|
+
|
|
70
|
+
The generator embeds `/*@FL:N*/` markers at each statement and `src/sourcemap.js` post-processes them into VLQ-encoded mappings.
|
|
71
|
+
|
|
72
|
+
### `future test` command + `assert` namespace
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
future test # finds and runs all *.test.future / test/**/*.future files
|
|
76
|
+
future test myfeature # filter by filename substring
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Test files use the reserved `assert` namespace:
|
|
80
|
+
|
|
81
|
+
```future
|
|
82
|
+
# calculator.test.future
|
|
83
|
+
assert.equal(1 + 1, 2)
|
|
84
|
+
assert.ok(math.sqrt(16) == 4)
|
|
85
|
+
assert.notEqual("hello", "world")
|
|
86
|
+
assert.deepEqual([1, 2, 3], [1, 2, 3])
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Exit code 0 when all pass, 1 when any fail. `assert.*` calls throw `AssertionError` on failure — the test runner catches them and reports per-file results.
|
|
90
|
+
|
|
91
|
+
`assert` is a reserved namespace — it cannot be redefined by user code.
|
|
92
|
+
|
|
93
|
+
### Internal changes
|
|
94
|
+
|
|
95
|
+
- `runtime/assert.js` — new module, wraps `node:assert/strict`
|
|
96
|
+
- `src/sourcemap.js` — new module, VLQ encoder + Source Map v3 builder
|
|
97
|
+
- `runtime/providers/anthropic.js` — exports `AiError` class; `chat`/`stream` accept `opts`
|
|
98
|
+
- `runtime/providers/openai-compat.js` — imports `AiError`; `chat`/`stream` accept `opts`
|
|
99
|
+
- `runtime/http.js` — exports `HttpError` class; `get`/`post` throw it; adds `configure()`; uses `AbortSignal.timeout`
|
|
100
|
+
- `runtime/index.js` — `assert` added to module list, `_base`, and `manifest`
|
|
101
|
+
- `src/generator.js` — `assert` added to `NAMESPACES`; `sourceMaps` option emits `/*@FL:N*/` markers
|
|
102
|
+
- `src/parser.js` — `assert` added to `RESERVED_NAMESPACES`
|
|
103
|
+
- `src/cli.js` — `future test` command; `--sourcemap` flag in `future compile`; calls `buildSourceMap()` to strip markers and write `.js.map`
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## v0.3.2 → v0.4.0 (CLI + Import system + Language improvements)
|
|
108
|
+
|
|
109
|
+
**No breaking changes.**
|
|
110
|
+
|
|
111
|
+
### CLI commands
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
future run <file.future> # compile + run
|
|
115
|
+
future compile <file.future> # compile to .js next to source
|
|
116
|
+
future new <name> # scaffold a new project directory
|
|
117
|
+
future check <file.future> # syntax-check only, no output
|
|
118
|
+
future fmt <file.future> # auto-format source in-place
|
|
119
|
+
future playground # launch the interactive playground
|
|
120
|
+
future doctor # check Node.js version, runtime, AI env, MQTT, etc.
|
|
121
|
+
future --version # show version
|
|
122
|
+
future run --debug <file> # print per-call timing to stderr (FUTURE_DEBUG=1)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Import system (`use`)
|
|
126
|
+
|
|
127
|
+
```future
|
|
128
|
+
# Import all top-level functions by name
|
|
129
|
+
use "./utils.future"
|
|
130
|
+
print formatName("Alice")
|
|
131
|
+
|
|
132
|
+
# Import as a namespace
|
|
133
|
+
use "./math.future" as m
|
|
134
|
+
result = m.add(10, 20)
|
|
135
|
+
|
|
136
|
+
# Import an npm package as a namespace
|
|
137
|
+
use "date-fns" as df
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Compiles to standard ES module imports:
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
import { formatName } from "./utils.js";
|
|
144
|
+
import * as m from "./math.js";
|
|
145
|
+
import * as df from "date-fns";
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The compiler reads imported `.future` files at compile time, parses them, and extracts top-level function names to generate named imports instead of wildcard imports. Dependencies are compiled recursively.
|
|
149
|
+
|
|
150
|
+
`use ... as alias` aliases are excluded from `__rt` routing — `m.add()` does not become `__rt.m.add()`.
|
|
151
|
+
|
|
152
|
+
### `else if` chains
|
|
153
|
+
|
|
154
|
+
```future
|
|
155
|
+
if score >= 90
|
|
156
|
+
print "A"
|
|
157
|
+
else if score >= 80
|
|
158
|
+
print "B"
|
|
159
|
+
else if score >= 70
|
|
160
|
+
print "C"
|
|
161
|
+
else
|
|
162
|
+
print "F"
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
One `end` closes the entire chain. Previously required nesting.
|
|
167
|
+
|
|
168
|
+
### Reserved namespace protection
|
|
169
|
+
|
|
170
|
+
Trying to reassign a namespace name now raises a compile-time error:
|
|
171
|
+
|
|
172
|
+
```future
|
|
173
|
+
math = 42 # error[parse]: 'math' is a reserved namespace
|
|
174
|
+
http = {} # error[parse]: 'http' is a reserved namespace
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Async handler error safety (`__safe`)
|
|
178
|
+
|
|
179
|
+
Programs that use `on`, `every`, or `stream` now wrap their handlers in `__safe`. Errors inside handlers are logged to `stderr` with `[future:ns]` prefix instead of crashing the process silently:
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
const __safe = (ns, fn) => async (...a) => {
|
|
183
|
+
try { return await fn(...a); }
|
|
184
|
+
catch (e) { console.error(`[future:${ns}]`, e.message); }
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Better error messages
|
|
189
|
+
|
|
190
|
+
Parse errors now show the source line and a `^` pointer:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
error[parse]: Expected 'end' to close 'if' — did you forget 'end'?
|
|
194
|
+
--> hello.future:5:1
|
|
195
|
+
5 | x = 1
|
|
196
|
+
^
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### `FUTURE_FOR_LLMS.md`
|
|
200
|
+
|
|
201
|
+
A complete quick-reference for AI assistants generating Future code: BNF grammar, all keywords, all namespace APIs, every construct with an example, common mistakes, and what-compiles-to-what pairs.
|
|
202
|
+
|
|
203
|
+
### Internal changes
|
|
204
|
+
|
|
205
|
+
- `src/lexer.js` — `as` keyword added (`AS` token)
|
|
206
|
+
- `src/ast.js` — `UseStatement` node type and factory added
|
|
207
|
+
- `src/parser.js` — `parseUse()`, `parseIf(isChained)` updated; `RESERVED_NAMESPACES` set added
|
|
208
|
+
- `src/generator.js` — `genUseStatement()`, `useAliases` Set, `isModule` and `pathMap` options, `__safe` emission, `else if` chain detection
|
|
209
|
+
- `src/index.js` — `compile()` accepts `resolveSource`, `isModule`, `pathMap`, `importedNames` options
|
|
210
|
+
- `src/formatter.js` — new module: line-based indentation formatter
|
|
211
|
+
- `src/cli.js` — full rewrite; `compileDepsToTemp()` for `future run`; all CLI commands implemented
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## v0.3.1 → v0.3.2 (Phase 7: General-Purpose Programming)
|
|
8
216
|
|
|
9
217
|
**No breaking changes.**
|
|
10
218
|
|
package/README.md
CHANGED
|
@@ -164,6 +164,18 @@ In the browser this uses `window.prompt()`. In a Node.js CLI program it reads fr
|
|
|
164
164
|
|
|
165
165
|
Future programs talk to the outside world through **namespace calls**. The compiler detects them and automatically switches the program to async mode — you never write `async` or `await`.
|
|
166
166
|
|
|
167
|
+
### Capability layers
|
|
168
|
+
|
|
169
|
+
Future organises its built-ins into three layers. You only need to know the layer you're using.
|
|
170
|
+
|
|
171
|
+
| Layer | What it includes | Typical use |
|
|
172
|
+
|-------|-----------------|-------------|
|
|
173
|
+
| **Core language** | variables, functions, `if/else/end`, `for`, `while`, `try/catch`, lists, objects, string interpolation | Any program — no external calls |
|
|
174
|
+
| **Standard capabilities** | `math`, `http`, `memory`, `system`, `schedule` | General-purpose programs with I/O |
|
|
175
|
+
| **Extended modules** | `ai`, `rag`, `vision`, `mqtt`, `tts`, `home`, `device`, `agent`, `assert` | AI, IoT, and automation programs |
|
|
176
|
+
|
|
177
|
+
Any call from Standard or Extended triggers async mode — the compiler handles it automatically.
|
|
178
|
+
|
|
167
179
|
```future
|
|
168
180
|
# HTTP
|
|
169
181
|
todo = http.get("https://jsonplaceholder.typicode.com/todos/1")
|
|
@@ -182,8 +194,8 @@ mqtt.publish("home/livingroom/light", "on")
|
|
|
182
194
|
|
|
183
195
|
| Namespace | Functions | Notes |
|
|
184
196
|
|------------|-----------|-------|
|
|
185
|
-
| `http` | `get(url)`, `post(url, body)` | Parses JSON automatically |
|
|
186
|
-
| `ai` | `ask(prompt)`, `chat(messages)`, `embed(text)`, `stream(prompt, cb)`, `configure(provider, key)` |
|
|
197
|
+
| `http` | `get(url)`, `post(url, body)`, `configure(opts)` | Parses JSON automatically; throws `HttpError` with `.status`, `.code`, `.body` |
|
|
198
|
+
| `ai` | `ask(prompt, opts?)`, `chat(messages, opts?)`, `embed(text)`, `stream(prompt, cb, opts?)`, `configure(provider, key)` | opts: `{ temperature, max_tokens, model }`; throws `AiError` |
|
|
187
199
|
| `tts` | `speak(text)` | System engine (`say` / SAPI / `espeak-ng`) |
|
|
188
200
|
| `mqtt` | `publish(topic, msg)`, `subscribe(topic, handler)` | Real broker or in-process loopback |
|
|
189
201
|
| `memory` | `set(key, v)`, `get(key)`, `delete(key)`, `search(q)`, `forget(pattern?)` | In-process key-value store |
|
|
@@ -194,6 +206,7 @@ mqtt.publish("home/livingroom/light", "on")
|
|
|
194
206
|
| `home` | `turnOn(device)`, `turnOff(device)`, `set(device, value)` | Home automation via MQTT |
|
|
195
207
|
| `math` | `round`, `floor`, `ceil`, `abs`, `sqrt`, `pow`, `log`, `random`, `min`, `max`, `pi`, `e` | Full Math wrapper |
|
|
196
208
|
| `device` | `register(config)`, `get(name)`, `list()` | IoT device registry |
|
|
209
|
+
| `assert` | `ok(val)`, `equal(a, b)`, `notEqual(a, b)`, `deepEqual(a, b)`, `fail(msg?)` | Use in `*.test.future` files |
|
|
197
210
|
|
|
198
211
|
### Configuration (environment variables)
|
|
199
212
|
|
|
@@ -211,12 +224,56 @@ FUTURE_VECTOR_DB=memory # memory | file | qdrant
|
|
|
211
224
|
## AI configuration
|
|
212
225
|
|
|
213
226
|
```future
|
|
214
|
-
#
|
|
227
|
+
# Provider selection
|
|
215
228
|
ai.configure("openai", "sk-...")
|
|
216
229
|
ai.configure("ollama") # local, no key needed
|
|
217
230
|
|
|
231
|
+
# Basic usage
|
|
218
232
|
answer = ai.ask("What is 2 + 2?")
|
|
219
233
|
print answer
|
|
234
|
+
|
|
235
|
+
# With inference options
|
|
236
|
+
precise = ai.ask("List the planets.", { temperature: 0.1 max_tokens: 150 })
|
|
237
|
+
creative = ai.chat(messages, { temperature: 0.9 model: "gpt-4o" })
|
|
238
|
+
|
|
239
|
+
# Structured response — text + token counts + model + provider
|
|
240
|
+
result = ai.complete("Explain recursion in one sentence.")
|
|
241
|
+
print result.text
|
|
242
|
+
print "Tokens used: {result.tokens.total}"
|
|
243
|
+
print "Model: {result.model}"
|
|
244
|
+
print "Provider: {result.provider}"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`ai.ask()` returns a plain string. `ai.complete()` returns `{ text, model, provider, tokens: { input, output, total } }` — useful when you need to track costs or log which model answered.
|
|
248
|
+
|
|
249
|
+
## HTTP configuration
|
|
250
|
+
|
|
251
|
+
```future
|
|
252
|
+
# Set global headers and timeout once at the top of your program
|
|
253
|
+
http.configure({ headers: { Authorization: "Bearer {token}" } timeout: 5000 })
|
|
254
|
+
|
|
255
|
+
data = http.get("https://api.example.com/me") # Authorization header included automatically
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Structured errors
|
|
259
|
+
|
|
260
|
+
HTTP and AI errors now carry structured data you can inspect:
|
|
261
|
+
|
|
262
|
+
```future
|
|
263
|
+
try
|
|
264
|
+
data = http.get("https://api.example.com/private")
|
|
265
|
+
catch err
|
|
266
|
+
print "Status: {err.status}" # e.g. 401
|
|
267
|
+
print "Code: {err.code}" # e.g. HTTP_401
|
|
268
|
+
print "URL: {err.url}"
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
try
|
|
272
|
+
reply = ai.ask("hello")
|
|
273
|
+
catch err
|
|
274
|
+
print "Provider: {err.provider}" # e.g. anthropic
|
|
275
|
+
print "Status: {err.status}" # e.g. 429
|
|
276
|
+
end
|
|
220
277
|
```
|
|
221
278
|
|
|
222
279
|
---
|
|
@@ -333,14 +390,49 @@ Open `future-playground.html` in any browser for a live editor with 11 built-in
|
|
|
333
390
|
```bash
|
|
334
391
|
npm install -g future-lang
|
|
335
392
|
|
|
336
|
-
future --version
|
|
337
|
-
future new myapp
|
|
338
|
-
future run program.future
|
|
339
|
-
future
|
|
340
|
-
future
|
|
341
|
-
future
|
|
342
|
-
future
|
|
343
|
-
future
|
|
393
|
+
future --version # show version
|
|
394
|
+
future new myapp # create a new project
|
|
395
|
+
future run program.future # compile + run
|
|
396
|
+
future run --debug program.future # run with per-call timing logs
|
|
397
|
+
future compile program.future # compile to JavaScript
|
|
398
|
+
future compile --sourcemap program.future # compile + emit .js.map
|
|
399
|
+
future test # run all *.test.future files
|
|
400
|
+
future test myfeature # run matching test files
|
|
401
|
+
future ast program.future # print the AST as JSON
|
|
402
|
+
future ast --pretty program.future # indented JSON
|
|
403
|
+
future check program.future # syntax check only
|
|
404
|
+
future fmt program.future # format source in-place
|
|
405
|
+
future playground # launch the interactive playground
|
|
406
|
+
future doctor # check your environment
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## Testing
|
|
412
|
+
|
|
413
|
+
Name your test files `*.test.future` (or put them in a `test/` folder) and use the `assert` namespace:
|
|
414
|
+
|
|
415
|
+
```future
|
|
416
|
+
# math.test.future
|
|
417
|
+
|
|
418
|
+
assert.equal(math.round(3.7), 4)
|
|
419
|
+
assert.equal(math.sqrt(16), 4)
|
|
420
|
+
assert.ok(math.pi > 3.14)
|
|
421
|
+
assert.notEqual(math.random(), math.random())
|
|
422
|
+
print "math tests passed"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
future test # runs all *.test.future files
|
|
427
|
+
future test math # runs files matching "math"
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Output:
|
|
431
|
+
```
|
|
432
|
+
math tests passed
|
|
433
|
+
✓ math.test.future
|
|
434
|
+
|
|
435
|
+
1/1 tests passed
|
|
344
436
|
```
|
|
345
437
|
|
|
346
438
|
---
|
package/ROADMAP.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Future — Roadmap
|
|
2
2
|
|
|
3
|
-
**Version:** 0.
|
|
3
|
+
**Version:** 0.4.1 · **Last updated:** 2026-06-13
|
|
4
4
|
|
|
5
5
|
Status legend: ✅ Done · 🔄 In progress · 📋 Planned · 💡 Idea
|
|
6
6
|
|
|
@@ -18,7 +18,9 @@ Status legend: ✅ Done · 🔄 In progress · 📋 Planned · 💡 Idea
|
|
|
18
18
|
| Multi-line strings | 📋 | Strings must be single-line today |
|
|
19
19
|
| String `+` concatenation | ✅ | Works via binary `+` operator |
|
|
20
20
|
| Integer division / modulo | 📋 | `math.trunc(a / b)` workaround for now |
|
|
21
|
-
| `use "other.future"` imports |
|
|
21
|
+
| `use "other.future"` imports | ✅ | Named + namespace imports; npm packages; recursive deps |
|
|
22
|
+
| `else if` chains | ✅ | One `end` for the whole chain |
|
|
23
|
+
| Reserved namespace protection | ✅ | Compile-time error if reserved name is reassigned |
|
|
22
24
|
| REPL (`future repl`) | 💡 | Interactive shell with introspection |
|
|
23
25
|
|
|
24
26
|
---
|
|
@@ -28,11 +30,14 @@ Status legend: ✅ Done · 🔄 In progress · 📋 Planned · 💡 Idea
|
|
|
28
30
|
| Feature | Status | Notes |
|
|
29
31
|
|---------|--------|-------|
|
|
30
32
|
| `ai.ask(prompt)` | ✅ | Single-turn Q&A |
|
|
33
|
+
| `ai.ask(prompt, opts)` | ✅ | `opts`: `temperature`, `max_tokens`, `model`, `system` |
|
|
31
34
|
| `ai.chat(messages)` | ✅ | Multi-turn conversation |
|
|
35
|
+
| `ai.chat(messages, opts)` | ✅ | Inference options forwarded to provider |
|
|
32
36
|
| `ai.embed(text)` | ✅ | Real embeddings (OpenAI/Ollama) + keyword fallback |
|
|
33
|
-
| `ai.stream(prompt,
|
|
37
|
+
| `ai.stream(prompt, cb, opts?)` | ✅ | Streaming via SSE; opts forwarded |
|
|
34
38
|
| `stream ai.ask() ... end` syntax | ✅ | Language-level streaming with implicit `chunk` variable |
|
|
35
39
|
| `ai.configure(provider, key, model)` | ✅ | Pluggable provider from Future code |
|
|
40
|
+
| Structured `AiError` (status, code, provider) | ✅ | Catchable with rich properties |
|
|
36
41
|
| Provider: Anthropic | ✅ | Native Messages API |
|
|
37
42
|
| Provider: OpenAI | ✅ | Via OpenAI-compat layer |
|
|
38
43
|
| Provider: Ollama | ✅ | Local models, no key needed |
|
|
@@ -205,8 +210,11 @@ Status legend: ✅ Done · 🔄 In progress · 📋 Planned · 💡 Idea
|
|
|
205
210
|
| `len(x)` built-in | ✅ | Arrays, strings, objects — sync, no runtime needed |
|
|
206
211
|
| `math.*` module | ✅ | Full JS Math wrapper |
|
|
207
212
|
| `input(prompt)` built-in | ✅ | stdin (Node.js) / `window.prompt` (browser) |
|
|
213
|
+
| `else if` chains | ✅ | One `end` closes the whole chain |
|
|
214
|
+
| `use "./file.future"` imports | ✅ | Named or namespace imports, recursive deps |
|
|
215
|
+
| `use "npm-pkg" as alias` | ✅ | Import npm packages as namespaces |
|
|
216
|
+
| Reserved namespace protection | ✅ | Compile-time error on redefinition |
|
|
208
217
|
| Multi-line strings | 📋 | Strings must be single-line |
|
|
209
|
-
| `use "other.future"` — module imports | 💡 | No cross-file composition |
|
|
210
218
|
| REPL (`future repl`) | 💡 | Interactive shell with introspection |
|
|
211
219
|
|
|
212
220
|
---
|
|
@@ -230,21 +238,57 @@ Status legend: ✅ Done · 🔄 In progress · 📋 Planned · 💡 Idea
|
|
|
230
238
|
|
|
231
239
|
---
|
|
232
240
|
|
|
241
|
+
## HTTP
|
|
242
|
+
|
|
243
|
+
| Feature | Status | Notes |
|
|
244
|
+
|---------|--------|-------|
|
|
245
|
+
| `http.get(url, headers?)` | ✅ | Parses JSON or returns text |
|
|
246
|
+
| `http.post(url, body, headers?)` | ✅ | JSON body; parses response |
|
|
247
|
+
| `http.configure({ headers, timeout })` | ✅ | Global defaults for all requests |
|
|
248
|
+
| Structured `HttpError` (status, code, url, body) | ✅ | Catchable with rich properties |
|
|
249
|
+
| `http.put / patch / delete` | 📋 | Additional HTTP verbs |
|
|
250
|
+
| Response headers access | 📋 | `res.headers.get("content-type")` |
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Testing
|
|
255
|
+
|
|
256
|
+
| Feature | Status | Notes |
|
|
257
|
+
|---------|--------|-------|
|
|
258
|
+
| `future test` command | ✅ | Finds `*.test.future` / `test/**/*.future`, runs all |
|
|
259
|
+
| `future test <pattern>` | ✅ | Filter by filename substring |
|
|
260
|
+
| `assert` namespace | ✅ | `ok`, `equal`, `notEqual`, `deepEqual`, `fail` |
|
|
261
|
+
| Per-file pass/fail reporting | ✅ | `✓` / `✗` with error message |
|
|
262
|
+
| Exit code 1 on failure | ✅ | Integrates with CI |
|
|
263
|
+
| Test isolation (separate process) | 📋 | Currently runs in same Node process |
|
|
264
|
+
| `assert.throws(fn)` | 📋 | Assert that a function throws |
|
|
265
|
+
| Coverage reporting | 💡 | Line coverage for `.future` files |
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
233
269
|
## Tooling
|
|
234
270
|
|
|
235
271
|
| Feature | Status | Notes |
|
|
236
272
|
|---------|--------|-------|
|
|
237
273
|
| CLI: `future run` | ✅ | |
|
|
238
274
|
| CLI: `future compile` | ✅ | |
|
|
239
|
-
|
|
|
275
|
+
| CLI: `future compile --sourcemap` | ✅ | Emits Source Map v3 `.js.map` |
|
|
276
|
+
| CLI: `future run --debug` | ✅ | Per-call timing via `FUTURE_DEBUG=1` |
|
|
277
|
+
| CLI: `future test` | ✅ | Test runner for `*.test.future` files |
|
|
278
|
+
| CLI: `future new` | ✅ | Project scaffold |
|
|
279
|
+
| CLI: `future check` | ✅ | Syntax-check without running |
|
|
280
|
+
| CLI: `future fmt` | ✅ | Auto-formatter |
|
|
281
|
+
| CLI: `future doctor` | ✅ | Environment health check |
|
|
282
|
+
| CLI: `future playground` | ✅ | Launches browser playground server |
|
|
283
|
+
| Source maps (`.js.map`) | ✅ | VLQ-encoded Source Map v3 |
|
|
284
|
+
| Structured manifest | ✅ | All 13 modules, 50+ functions fully described |
|
|
240
285
|
| Runtime introspection API | ✅ | `runtime.describe()` / `listModules()` / `listFunctions()` |
|
|
241
286
|
| LSP metadata module | ✅ | Completions, hover, signatures |
|
|
242
287
|
| Browser playground | ✅ | `future-playground.html` — 11 examples |
|
|
288
|
+
| `FUTURE_FOR_LLMS.md` | ✅ | BNF grammar + all APIs for AI code assistants |
|
|
289
|
+
| npm publish (`future-lang`) | ✅ | Public — `npm install -g future-lang` |
|
|
243
290
|
| VSCode extension | 📋 | Syntax highlighting, completions, hover |
|
|
244
291
|
| Language Server (LSP) | 📋 | Full editor integration |
|
|
245
|
-
| `future fmt` | 📋 | Auto-formatter |
|
|
246
|
-
| `future check` | 📋 | Lint / type check without running |
|
|
247
|
-
| npm publish (`future-lang`) | 📋 | Public package registry |
|
|
248
292
|
|
|
249
293
|
---
|
|
250
294
|
|
|
@@ -252,12 +296,15 @@ Status legend: ✅ Done · 🔄 In progress · 📋 Planned · 💡 Idea
|
|
|
252
296
|
|
|
253
297
|
| Priority | Item | Why it matters |
|
|
254
298
|
|----------|------|----------------|
|
|
255
|
-
| 🔴 Critical | npm publish (`future-lang`) | Required to ship publicly |
|
|
256
299
|
| 🔴 Critical | VSCode extension (syntax highlighting) | First impression for new users |
|
|
257
|
-
|
|
|
258
|
-
| 🟠 High | `
|
|
300
|
+
| 🔴 Critical | Language Server (LSP) | Completions and hover for all IDEs |
|
|
301
|
+
| 🟠 High | `ai.extract(text, schema)` | Structured output is the #1 AI use case |
|
|
302
|
+
| 🟠 High | Test isolation (separate process) | Prevents test state leakage |
|
|
303
|
+
| 🟠 High | `assert.throws(fn)` | Needed for error-handling tests |
|
|
259
304
|
| 🟡 Medium | Home Assistant REST API | Most HA users don't run MQTT |
|
|
260
305
|
| 🟡 Medium | Persistent memory / device registry | Most programs are stateless today |
|
|
261
306
|
| 🟡 Medium | Agent tool-calling loop (ReAct) | True autonomous agents |
|
|
307
|
+
| 🟡 Medium | `http.put / patch / delete` | Needed for full REST APIs |
|
|
262
308
|
| 🟢 Low | `rag.delete(id)` | Selective document removal |
|
|
263
309
|
| 🟢 Low | REPL | Nice-to-have for exploration |
|
|
310
|
+
| 🟢 Low | Coverage reporting | `.future` line coverage |
|
package/package.json
CHANGED
package/runtime/ai.js
CHANGED
|
@@ -72,6 +72,40 @@ export async function stream(promptOrMessages, onChunk, opts = {}) {
|
|
|
72
72
|
return provider.stream(messages, onChunk, opts);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Like ask(), but returns a structured result object instead of a plain string.
|
|
77
|
+
* Includes the generated text, model used, provider name, and token counts.
|
|
78
|
+
*
|
|
79
|
+
* @param {string|Array} prompt String prompt or messages array
|
|
80
|
+
* @param {{ temperature?: number, max_tokens?: number, model?: string, system?: string }} [opts]
|
|
81
|
+
* @returns {Promise<{ text: string, model: string, provider: string, tokens: { input: number, output: number, total: number } }>}
|
|
82
|
+
*/
|
|
83
|
+
export async function complete(prompt, opts = {}) {
|
|
84
|
+
const messages = Array.isArray(prompt)
|
|
85
|
+
? prompt
|
|
86
|
+
: [{ role: 'user', content: String(prompt) }];
|
|
87
|
+
const provider = resolveProvider();
|
|
88
|
+
if (!provider) {
|
|
89
|
+
return {
|
|
90
|
+
text: offlineStub(messages),
|
|
91
|
+
model: 'none',
|
|
92
|
+
provider: 'offline',
|
|
93
|
+
tokens: { input: 0, output: 0, total: 0 },
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (typeof provider.complete === 'function') {
|
|
97
|
+
return provider.complete(messages, opts);
|
|
98
|
+
}
|
|
99
|
+
// Fallback for providers that don't implement complete() yet.
|
|
100
|
+
const text = await provider.chat(messages, opts);
|
|
101
|
+
return {
|
|
102
|
+
text,
|
|
103
|
+
model: opts.model ?? 'unknown',
|
|
104
|
+
provider: provider.name,
|
|
105
|
+
tokens: { input: 0, output: 0, total: 0 },
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
75
109
|
/**
|
|
76
110
|
* Generate a vector embedding for a piece of text.
|
|
77
111
|
* With OpenAI/Ollama providers, returns real semantic embeddings.
|
package/runtime/index.js
CHANGED
|
@@ -83,10 +83,16 @@ export const manifest = {
|
|
|
83
83
|
},
|
|
84
84
|
ask: {
|
|
85
85
|
description: 'Ask an AI model a question and get a text response',
|
|
86
|
-
params: [{ name: 'prompt', type: 'string' }],
|
|
86
|
+
params: [{ name: 'prompt', type: 'string' }, { name: 'opts', type: 'object', optional: true }],
|
|
87
87
|
returns: 'string',
|
|
88
88
|
async: true,
|
|
89
89
|
},
|
|
90
|
+
complete: {
|
|
91
|
+
description: 'Like ask(), but returns a structured object: { text, model, provider, tokens: { input, output, total } }',
|
|
92
|
+
params: [{ name: 'prompt', type: 'string|array' }, { name: 'opts', type: 'object', optional: true }],
|
|
93
|
+
returns: '{ text: string, model: string, provider: string, tokens: { input: number, output: number, total: number } }',
|
|
94
|
+
async: true,
|
|
95
|
+
},
|
|
90
96
|
chat: {
|
|
91
97
|
description: 'Send a multi-turn message list to an AI model',
|
|
92
98
|
params: [{ name: 'messages', type: 'array' }],
|
|
@@ -434,7 +440,7 @@ runtime.listFunctions = (mod) => {
|
|
|
434
440
|
* Suitable for AI agent discovery or documentation generation.
|
|
435
441
|
*/
|
|
436
442
|
runtime.describe = () => ({
|
|
437
|
-
version: '0.4.
|
|
443
|
+
version: '0.4.2',
|
|
438
444
|
modules: [...MODULE_NAMES],
|
|
439
445
|
manifest,
|
|
440
446
|
});
|
|
@@ -77,11 +77,42 @@ export function create(config) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
async function complete(messages, opts = {}) {
|
|
81
|
+
const model = opts.model ?? defaultModel;
|
|
82
|
+
const max_tokens = opts.max_tokens ?? 1024;
|
|
83
|
+
const body = { model, max_tokens, messages };
|
|
84
|
+
if (opts.temperature != null) body.temperature = opts.temperature;
|
|
85
|
+
if (opts.system) body.system = opts.system;
|
|
86
|
+
|
|
87
|
+
const res = await fetch(`${BASE}/messages`, {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers,
|
|
90
|
+
body: JSON.stringify(body),
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
let errBody;
|
|
94
|
+
try { errBody = await res.json(); } catch { errBody = await res.text(); }
|
|
95
|
+
throw new AiError(res.status, 'anthropic', errBody);
|
|
96
|
+
}
|
|
97
|
+
const data = await res.json();
|
|
98
|
+
const text = (data.content ?? []).map((b) => b.text ?? '').join('').trim();
|
|
99
|
+
return {
|
|
100
|
+
text,
|
|
101
|
+
model: data.model ?? model,
|
|
102
|
+
provider: 'anthropic',
|
|
103
|
+
tokens: {
|
|
104
|
+
input: data.usage?.input_tokens ?? 0,
|
|
105
|
+
output: data.usage?.output_tokens ?? 0,
|
|
106
|
+
total: (data.usage?.input_tokens ?? 0) + (data.usage?.output_tokens ?? 0),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
80
111
|
async function embed(text) {
|
|
81
112
|
// Anthropic has no public embeddings endpoint — use keyword vector fallback.
|
|
82
113
|
// For production semantic search, configure an OpenAI-compatible provider with an embed model.
|
|
83
114
|
return keywordVector(String(text));
|
|
84
115
|
}
|
|
85
116
|
|
|
86
|
-
return { name: 'anthropic', ask, chat, stream, embed };
|
|
117
|
+
return { name: 'anthropic', ask, chat, complete, stream, embed };
|
|
87
118
|
}
|
|
@@ -81,6 +81,36 @@ export function create(config) {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
async function complete(messages, opts = {}) {
|
|
85
|
+
const model = opts.model ?? defaultModel;
|
|
86
|
+
const max_tokens = opts.max_tokens ?? 1024;
|
|
87
|
+
const body = { model, messages, max_tokens };
|
|
88
|
+
if (opts.temperature != null) body.temperature = opts.temperature;
|
|
89
|
+
|
|
90
|
+
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers,
|
|
93
|
+
body: JSON.stringify(body),
|
|
94
|
+
});
|
|
95
|
+
if (!res.ok) {
|
|
96
|
+
let errBody;
|
|
97
|
+
try { errBody = await res.json(); } catch { errBody = await res.text(); }
|
|
98
|
+
throw new AiError(res.status, providerTag, errBody);
|
|
99
|
+
}
|
|
100
|
+
const data = await res.json();
|
|
101
|
+
const text = data.choices?.[0]?.message?.content?.trim() ?? '';
|
|
102
|
+
return {
|
|
103
|
+
text,
|
|
104
|
+
model: data.model ?? model,
|
|
105
|
+
provider: providerTag,
|
|
106
|
+
tokens: {
|
|
107
|
+
input: data.usage?.prompt_tokens ?? 0,
|
|
108
|
+
output: data.usage?.completion_tokens ?? 0,
|
|
109
|
+
total: data.usage?.total_tokens ?? 0,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
84
114
|
async function embed(text) {
|
|
85
115
|
if (!embedModel) return keywordVector(String(text));
|
|
86
116
|
try {
|
|
@@ -97,5 +127,5 @@ export function create(config) {
|
|
|
97
127
|
}
|
|
98
128
|
}
|
|
99
129
|
|
|
100
|
-
return { name: providerTag, ask, chat, stream, embed };
|
|
130
|
+
return { name: providerTag, ask, chat, complete, stream, embed };
|
|
101
131
|
}
|
package/src/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ import { format } from './formatter.js';
|
|
|
22
22
|
import { FutureError } from './errors.js';
|
|
23
23
|
import { buildSourceMap } from './sourcemap.js';
|
|
24
24
|
|
|
25
|
-
const VERSION = '0.4.
|
|
25
|
+
const VERSION = '0.4.2';
|
|
26
26
|
const PROJECT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
27
27
|
const RUNTIME_INDEX = join(PROJECT_ROOT, 'runtime', 'index.js');
|
|
28
28
|
|
|
@@ -32,13 +32,14 @@ Usage:
|
|
|
32
32
|
future run <file.future> Compile and run a program
|
|
33
33
|
future compile <file.future> Compile to JavaScript (<file>.js)
|
|
34
34
|
future test [pattern] Run *.test.future files
|
|
35
|
-
future
|
|
35
|
+
future ast <file.future> Print the AST as JSON
|
|
36
|
+
future new <name> Create a new project
|
|
36
37
|
future check <file.future> Check for syntax errors
|
|
37
38
|
future fmt <file.future> Format source code in-place
|
|
38
|
-
future playground
|
|
39
|
-
future doctor
|
|
40
|
-
future help
|
|
41
|
-
future --version
|
|
39
|
+
future playground Launch the interactive playground
|
|
40
|
+
future doctor Check your environment
|
|
41
|
+
future help Show this help
|
|
42
|
+
future --version Show the version
|
|
42
43
|
|
|
43
44
|
Import system:
|
|
44
45
|
use "./utils.future" Import all functions from a file
|
|
@@ -48,18 +49,21 @@ Import system:
|
|
|
48
49
|
Flags:
|
|
49
50
|
future run --debug <file> Show timing for every namespace call
|
|
50
51
|
future compile --sourcemap <file> Also emit a .js.map source map
|
|
52
|
+
future ast --pretty <file> Indented JSON output
|
|
51
53
|
`;
|
|
52
54
|
|
|
53
55
|
async function main(argv) {
|
|
54
56
|
const debug = argv.includes('--debug');
|
|
55
57
|
const sourcemap = argv.includes('--sourcemap');
|
|
58
|
+
const pretty = argv.includes('--pretty');
|
|
56
59
|
if (debug) process.env.FUTURE_DEBUG = '1';
|
|
57
|
-
const rest = argv.filter((a) => a !== '--debug' && a !== '--sourcemap');
|
|
60
|
+
const rest = argv.filter((a) => a !== '--debug' && a !== '--sourcemap' && a !== '--pretty');
|
|
58
61
|
const [command, arg] = rest;
|
|
59
62
|
switch (command) {
|
|
60
63
|
case 'run': return cmdRun(arg);
|
|
61
64
|
case 'compile': return cmdCompile(arg, { sourcemap });
|
|
62
65
|
case 'test': return cmdTest(arg);
|
|
66
|
+
case 'ast': return cmdAst(arg, { pretty });
|
|
63
67
|
case 'new': return cmdNew(arg);
|
|
64
68
|
case 'check': return cmdCheck(arg);
|
|
65
69
|
case 'fmt': return cmdFmt(arg);
|
|
@@ -345,6 +349,23 @@ function cmdFmt(file) {
|
|
|
345
349
|
return 0;
|
|
346
350
|
}
|
|
347
351
|
|
|
352
|
+
/** Output the AST of a .future file as JSON. */
|
|
353
|
+
function cmdAst(file, { pretty = false } = {}) {
|
|
354
|
+
let path, source;
|
|
355
|
+
try { ({ path, source } = readSource(file)); }
|
|
356
|
+
catch (err) { return fail(err, file); }
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
const tokens = tokenize(source);
|
|
360
|
+
const ast = parse(tokens);
|
|
361
|
+
process.stdout.write((pretty ? JSON.stringify(ast, null, 2) : JSON.stringify(ast)) + '\n');
|
|
362
|
+
return 0;
|
|
363
|
+
} catch (err) {
|
|
364
|
+
if (err instanceof FutureError) { reportFutureError(err, source, file); return 1; }
|
|
365
|
+
throw err;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
348
369
|
/** Recursively collect .future files matching a pattern or default test globs. */
|
|
349
370
|
function findTestFiles(pattern) {
|
|
350
371
|
const cwd = process.cwd();
|