@bramas/simulant-lang 0.1.2 → 0.3.0
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/dist/builder.js +1 -1
- package/dist/check.js +2 -0
- package/dist/elab/elaborate.js +8 -0
- package/dist/parse/lexer.js +1 -1
- package/dist/parse/parser.js +5 -3
- package/package.json +2 -2
- package/src/builder.ts +2 -2
- package/src/check.ts +1 -0
- package/src/elab/elaborate.ts +7 -0
- package/src/ir.ts +4 -2
- package/src/parse/ast.ts +3 -3
- package/src/parse/lexer.ts +1 -1
- package/src/parse/parser.ts +5 -3
package/dist/builder.js
CHANGED
|
@@ -208,7 +208,7 @@ export function task(spec) {
|
|
|
208
208
|
return dropUndefined({ name: spec.name, goal: spec.goal, verdict: ir(spec.verdict(v('C'))), equivariance: spec.equivariance, defs: spec.defs });
|
|
209
209
|
}
|
|
210
210
|
export function experiment(spec) {
|
|
211
|
-
const params = (spec.params ?? []).map(p => dropUndefined({ name: p.name, type: p.type, domain: p.domain === undefined ? undefined : ir(p.domain) }));
|
|
211
|
+
const params = (spec.params ?? []).map(p => dropUndefined({ name: p.name, type: p.type, domain: p.domain === undefined ? undefined : ir(p.domain), accepts: p.accepts === undefined ? undefined : ir(p.accepts) }));
|
|
212
212
|
return dropUndefined({
|
|
213
213
|
irVersion: IR_VERSION, name: spec.name, model: spec.model, algorithm: spec.algorithm, task: spec.task, params,
|
|
214
214
|
agents: { count: ir(spec.agents.count), pos: ir(spec.agents.pos(v('i'))) },
|
package/dist/check.js
CHANGED
|
@@ -405,6 +405,8 @@ export class Checker {
|
|
|
405
405
|
this.report(`parameter ${p.name}: ${showType(p.type)} cannot be stored`);
|
|
406
406
|
if (p.domain)
|
|
407
407
|
this.root(p.domain, {}, unit('domain', `domain of ${p.name}`), T.list(p.type));
|
|
408
|
+
if (p.accepts)
|
|
409
|
+
this.root(p.accepts, {}, unit('domain', `acceptance of ${p.name}`), T.bool);
|
|
408
410
|
}
|
|
409
411
|
this.root(model.topology.size, {}, unit('domain', 'topology size'), T.nat);
|
|
410
412
|
this.root(exp.agents.count, {}, unit('init', 'agent count'), T.nat);
|
package/dist/elab/elaborate.js
CHANGED
|
@@ -197,8 +197,13 @@ export class Elaborator {
|
|
|
197
197
|
case 'ascribe':
|
|
198
198
|
return this.meval(e.e, env, e.type);
|
|
199
199
|
case 'name':
|
|
200
|
+
if (e.name === 'none' && expected?.t === 'name' && expected.name === 'Option')
|
|
201
|
+
return { m: 'option' };
|
|
200
202
|
return env.get(e.name) ?? this.metaCall(e.name, [], e.span, env);
|
|
201
203
|
case 'app': {
|
|
204
|
+
if (e.fn.t === 'name' && e.fn.name === 'some' && expected?.t === 'name' && expected.name === 'Option' && e.args.length === 1) {
|
|
205
|
+
return { m: 'option', value: this.meval(e.args[0].value, env, expected.args[0]) };
|
|
206
|
+
}
|
|
202
207
|
if (e.fn.t !== 'name')
|
|
203
208
|
fail('expected the name of a definition', e.fn.span);
|
|
204
209
|
return this.metaCall(e.fn.name, e.args, e.span, env);
|
|
@@ -280,6 +285,7 @@ export class Elaborator {
|
|
|
280
285
|
case 'str': return v.v;
|
|
281
286
|
case 'enum': return v.ctor;
|
|
282
287
|
case 'list': return v.items.map(i => this.json(i, span));
|
|
288
|
+
case 'option': return v.value ? this.json(v.value, span) : null;
|
|
283
289
|
default: return fail('expected a value', span);
|
|
284
290
|
}
|
|
285
291
|
}
|
|
@@ -611,6 +617,8 @@ export class Elaborator {
|
|
|
611
617
|
if (i.value.t !== 'nothing')
|
|
612
618
|
p.domain = this.tr(i.value, { scope: new Map(), params: new Set(paramNames), owner: decl.name });
|
|
613
619
|
paramNames.add(i.name);
|
|
620
|
+
if (i.param.accepts)
|
|
621
|
+
p.accepts = this.tr(i.param.accepts, { scope: new Map(), params: new Set(paramNames), owner: decl.name });
|
|
614
622
|
return p;
|
|
615
623
|
});
|
|
616
624
|
const ictx = (extra = []) => ({ scope: new Map(extra), params: paramNames, owner: decl.name });
|
package/dist/parse/lexer.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
export const KEYWORDS = new Set([
|
|
6
6
|
'import', 'def', 'structure', 'inductive', 'sensor', 'task', 'goal', 'algorithm', 'experiment', 'where', 'let',
|
|
7
7
|
'if', 'then', 'else', 'match', 'with', 'fun', 'choose', 'such', 'that', 'labels', 'param', 'flag', 'uses',
|
|
8
|
-
'true', 'false', 'raise', 'nothing', 'action',
|
|
8
|
+
'true', 'false', 'raise', 'nothing', 'action', 'in',
|
|
9
9
|
]);
|
|
10
10
|
/** Operators, longest first. Unicode and ASCII spellings are both accepted. */
|
|
11
11
|
const OPERATORS = [
|
package/dist/parse/parser.js
CHANGED
|
@@ -256,13 +256,15 @@ export class Parser {
|
|
|
256
256
|
if (output)
|
|
257
257
|
this.next();
|
|
258
258
|
if (this.is('param')) {
|
|
259
|
-
// `param N : Nat
|
|
259
|
+
// `param N : Nat in domain where predicate` (both clauses optional).
|
|
260
|
+
// Keep `:=` as an alias for the original finite-domain syntax.
|
|
260
261
|
this.next();
|
|
261
262
|
const name = this.ident();
|
|
262
263
|
this.expect(':');
|
|
263
264
|
const type = this.type();
|
|
264
|
-
const value = this.is(':=') ? (this.next(), this.expr()) : { t: 'nothing', span: this.span(start) };
|
|
265
|
-
|
|
265
|
+
const value = this.is('in') || this.is(':=') ? (this.next(), this.expr()) : { t: 'nothing', span: this.span(start) };
|
|
266
|
+
const accepts = this.is('where') ? (this.next(), this.expr()) : undefined;
|
|
267
|
+
items.push({ name, args: [], value, span: this.span(start), param: { type, ...(accepts ? { accepts } : {}) } });
|
|
266
268
|
return;
|
|
267
269
|
}
|
|
268
270
|
// Item names may be keywords: `algorithm := ...`, `task := ...`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bramas/simulant-lang",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "CECILL-B",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"LICENCE.txt"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@bramas/simulant-stdlib": "0.
|
|
23
|
+
"@bramas/simulant-stdlib": "0.3.0"
|
|
24
24
|
},
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
package/src/builder.ts
CHANGED
|
@@ -340,11 +340,11 @@ export function task(spec: { name: string; goal: Goal; verdict: (C: X) => Arg; e
|
|
|
340
340
|
|
|
341
341
|
export function experiment(spec: {
|
|
342
342
|
name: string; model: Model; algorithm: Algorithm; task: Task;
|
|
343
|
-
params?: { name: string; type: Type; domain?: Arg }[];
|
|
343
|
+
params?: { name: string; type: Type; domain?: Arg; accepts?: Arg }[];
|
|
344
344
|
agents: { count: Arg; pos: (i: X) => Arg };
|
|
345
345
|
limits?: Experiment['limits'];
|
|
346
346
|
}): Experiment {
|
|
347
|
-
const params: Param[] = (spec.params ?? []).map(p => dropUndefined({ name: p.name, type: p.type, domain: p.domain === undefined ? undefined : ir(p.domain) }));
|
|
347
|
+
const params: Param[] = (spec.params ?? []).map(p => dropUndefined({ name: p.name, type: p.type, domain: p.domain === undefined ? undefined : ir(p.domain), accepts: p.accepts === undefined ? undefined : ir(p.accepts) }));
|
|
348
348
|
return dropUndefined({
|
|
349
349
|
irVersion: IR_VERSION, name: spec.name, model: spec.model, algorithm: spec.algorithm, task: spec.task, params,
|
|
350
350
|
agents: { count: ir(spec.agents.count), pos: ir(spec.agents.pos(v('i'))) },
|
package/src/check.ts
CHANGED
|
@@ -388,6 +388,7 @@ export class Checker {
|
|
|
388
388
|
for (const p of exp.params) {
|
|
389
389
|
if (!isStorable(p.type)) this.report(`parameter ${p.name}: ${showType(p.type)} cannot be stored`);
|
|
390
390
|
if (p.domain) this.root(p.domain, {}, unit('domain', `domain of ${p.name}`), T.list(p.type));
|
|
391
|
+
if (p.accepts) this.root(p.accepts, {}, unit('domain', `acceptance of ${p.name}`), T.bool);
|
|
391
392
|
}
|
|
392
393
|
this.root(model.topology.size, {}, unit('domain', 'topology size'), T.nat);
|
|
393
394
|
this.root(exp.agents.count, {}, unit('init', 'agent count'), T.nat);
|
package/src/elab/elaborate.ts
CHANGED
|
@@ -46,6 +46,7 @@ type MV =
|
|
|
46
46
|
| { m: 'str'; v: string }
|
|
47
47
|
| { m: 'enum'; type: Type & { k: 'enum' }; ctor: string }
|
|
48
48
|
| { m: 'list'; items: MV[] }
|
|
49
|
+
| { m: 'option'; value?: MV }
|
|
49
50
|
| { m: 'component'; ir: Component }
|
|
50
51
|
| { m: 'model'; ir: Model }
|
|
51
52
|
| { m: 'sensor'; ir: Sensor }
|
|
@@ -236,8 +237,12 @@ export class Elaborator {
|
|
|
236
237
|
case 'ascribe':
|
|
237
238
|
return this.meval(e.e, env, e.type);
|
|
238
239
|
case 'name':
|
|
240
|
+
if (e.name === 'none' && expected?.t === 'name' && expected.name === 'Option') return { m: 'option' };
|
|
239
241
|
return env.get(e.name) ?? this.metaCall(e.name, [], e.span, env);
|
|
240
242
|
case 'app': {
|
|
243
|
+
if (e.fn.t === 'name' && e.fn.name === 'some' && expected?.t === 'name' && expected.name === 'Option' && e.args.length === 1) {
|
|
244
|
+
return { m: 'option', value: this.meval(e.args[0].value, env, expected.args[0]) };
|
|
245
|
+
}
|
|
241
246
|
if (e.fn.t !== 'name') fail('expected the name of a definition', e.fn.span);
|
|
242
247
|
return this.metaCall((e.fn as { name: string }).name, e.args, e.span, env);
|
|
243
248
|
}
|
|
@@ -307,6 +312,7 @@ export class Elaborator {
|
|
|
307
312
|
case 'nat': case 'bool': case 'str': return v.v;
|
|
308
313
|
case 'enum': return v.ctor;
|
|
309
314
|
case 'list': return v.items.map(i => this.json(i, span));
|
|
315
|
+
case 'option': return v.value ? this.json(v.value, span) : null;
|
|
310
316
|
default: return fail('expected a value', span);
|
|
311
317
|
}
|
|
312
318
|
}
|
|
@@ -618,6 +624,7 @@ export class Elaborator {
|
|
|
618
624
|
const p: Param = { name: i.name, type: this.type(i.param!.type) };
|
|
619
625
|
if (i.value.t !== 'nothing') p.domain = this.tr(i.value, { scope: new Map(), params: new Set(paramNames), owner: decl.name });
|
|
620
626
|
paramNames.add(i.name);
|
|
627
|
+
if (i.param!.accepts) p.accepts = this.tr(i.param!.accepts, { scope: new Map(), params: new Set(paramNames), owner: decl.name });
|
|
621
628
|
return p;
|
|
622
629
|
});
|
|
623
630
|
const ictx = (extra: [string, string][] = []): OCtx => ({ scope: new Map(extra), params: paramNames, owner: decl.name });
|
package/src/ir.ts
CHANGED
|
@@ -196,8 +196,10 @@ export type Goal =
|
|
|
196
196
|
/** A verdict on configurations (`C` in scope) and a goal over executions. */
|
|
197
197
|
export type Task = { name: string; verdict: Expr; goal: Goal; equivariance?: Equivariance; defs?: Def[] };
|
|
198
198
|
|
|
199
|
-
/** A parameter
|
|
200
|
-
|
|
199
|
+
/** A parameter's domain: an optional exact finite list and/or a Boolean refinement.
|
|
200
|
+
* Both constraints apply when present. Earlier parameters are in scope in the
|
|
201
|
+
* list; the refinement also sees the parameter itself. No simulator defaults. */
|
|
202
|
+
export type Param = { name: string; type: Type; domain?: Expr; accepts?: Expr };
|
|
201
203
|
|
|
202
204
|
/** Where a declaration is written (display only). */
|
|
203
205
|
export type DeclSource = {
|
package/src/parse/ast.ts
CHANGED
|
@@ -63,11 +63,11 @@ export type SStmt = { span: Span } & (
|
|
|
63
63
|
| { s: 'call'; name: string; args: SExpr[] }
|
|
64
64
|
);
|
|
65
65
|
|
|
66
|
-
/** An item of a `where` block: `name args := value`, or
|
|
66
|
+
/** An item of a `where` block: `name args := value`, or a parameter domain in experiments. */
|
|
67
67
|
export type SWhereItem = {
|
|
68
68
|
name: string; args: string[]; value: SExpr; span: Span;
|
|
69
|
-
/** `param N : T
|
|
70
|
-
param?: { type: SType };
|
|
69
|
+
/** `param N : T in domain where predicate`, in experiments (both clauses optional). */
|
|
70
|
+
param?: { type: SType; accepts?: SExpr };
|
|
71
71
|
/** `def p (x : T) := statements` and `main := statements`, in algorithms. */
|
|
72
72
|
proc?: { params: SBinder[]; body: SStmt[] };
|
|
73
73
|
};
|
package/src/parse/lexer.ts
CHANGED
|
@@ -30,7 +30,7 @@ export type Token = {
|
|
|
30
30
|
export const KEYWORDS = new Set([
|
|
31
31
|
'import', 'def', 'structure', 'inductive', 'sensor', 'task', 'goal', 'algorithm', 'experiment', 'where', 'let',
|
|
32
32
|
'if', 'then', 'else', 'match', 'with', 'fun', 'choose', 'such', 'that', 'labels', 'param', 'flag', 'uses',
|
|
33
|
-
'true', 'false', 'raise', 'nothing', 'action',
|
|
33
|
+
'true', 'false', 'raise', 'nothing', 'action', 'in',
|
|
34
34
|
]);
|
|
35
35
|
|
|
36
36
|
/** Operators, longest first. Unicode and ASCII spellings are both accepted. */
|
package/src/parse/parser.ts
CHANGED
|
@@ -252,13 +252,15 @@ export class Parser {
|
|
|
252
252
|
const output = algorithm && this.peek().text === 'output' && this.peek(1).kind === 'ident';
|
|
253
253
|
if (output) this.next();
|
|
254
254
|
if (this.is('param')) {
|
|
255
|
-
// `param N : Nat
|
|
255
|
+
// `param N : Nat in domain where predicate` (both clauses optional).
|
|
256
|
+
// Keep `:=` as an alias for the original finite-domain syntax.
|
|
256
257
|
this.next();
|
|
257
258
|
const name = this.ident();
|
|
258
259
|
this.expect(':');
|
|
259
260
|
const type = this.type();
|
|
260
|
-
const value: SExpr = this.is(':=') ? (this.next(), this.expr()) : { t: 'nothing', span: this.span(start) };
|
|
261
|
-
|
|
261
|
+
const value: SExpr = this.is('in') || this.is(':=') ? (this.next(), this.expr()) : { t: 'nothing', span: this.span(start) };
|
|
262
|
+
const accepts = this.is('where') ? (this.next(), this.expr()) : undefined;
|
|
263
|
+
items.push({ name, args: [], value, span: this.span(start), param: { type, ...(accepts ? { accepts } : {}) } });
|
|
262
264
|
return;
|
|
263
265
|
}
|
|
264
266
|
// Item names may be keywords: `algorithm := ...`, `task := ...`.
|