@harmoniclabs/pebble 0.3.1 → 0.3.3
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/IR/IRNodes/IRConst.js +7 -4
- package/dist/IR/toUPLC/_internal/_makeAllNegativeNativesHoisted.js +11 -1
- package/dist/IR/toUPLC/compileIRToUPLC.js +7 -0
- package/dist/IR/toUPLC/subRoutines/replaceNatives/nativeToIR.d.ts +1 -0
- package/dist/IR/toUPLC/subRoutines/replaceNatives/nativeToIR.js +10 -1
- package/dist/ast/Source/Source.js +8 -2
- package/dist/compiler/AstCompiler/AstCompiler.d.ts +4 -1
- package/dist/compiler/AstCompiler/AstCompiler.js +30 -5
- package/dist/compiler/AstCompiler/internal/exprs/_compileCallExpr.js +18 -3
- package/dist/compiler/AstCompiler/internal/exprs/_compileCaseExpr.js +8 -3
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanEqualExpr.js +4 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanExpr.js +4 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanEqualExpr.js +4 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanExpr.js +4 -1
- package/dist/compiler/AstCompiler/utils/getPropAccessReturnType.js +11 -0
- package/dist/compiler/TirCompiler/expressify/determineReassignedVariablesAndReturn.js +10 -2
- package/dist/compiler/TirCompiler/expressify/expressify.js +23 -1
- package/dist/compiler/TirCompiler/expressify/expressifyVars.js +25 -0
- package/dist/compiler/tir/expressions/TirNativeFunc.d.ts +1 -0
- package/dist/compiler/tir/expressions/TirNativeFunc.js +5 -0
- package/dist/compiler/tir/expressions/binary/TirBinaryExpr.js +37 -0
- package/dist/compiler/tir/program/stdScope/populateStdNamespace.js +3 -0
- package/dist/compiler/tir/program/stdScope/stdScope.js +2 -0
- package/dist/compiler/tir/types/utils/canAssignTo.d.ts +12 -0
- package/dist/compiler/tir/types/utils/canAssignTo.js +26 -0
- package/dist/parser/Parser.d.ts +27 -0
- package/dist/parser/Parser.js +85 -41
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/package.json +2 -2
|
@@ -292,6 +292,12 @@ export function tirTypeToUplcType(t) {
|
|
|
292
292
|
}
|
|
293
293
|
if (t instanceof TirValueT)
|
|
294
294
|
return constT.value;
|
|
295
|
+
if (t instanceof TirBlsG1T)
|
|
296
|
+
return constT.bls12_381_G1_element;
|
|
297
|
+
if (t instanceof TirBlsG2T)
|
|
298
|
+
return constT.bls12_381_G2_element;
|
|
299
|
+
if (t instanceof TirMlResultT)
|
|
300
|
+
return constT.bls12_381_MlResult;
|
|
295
301
|
if (t instanceof TirDataT
|
|
296
302
|
|| t instanceof TirDataOptT
|
|
297
303
|
|| t instanceof TirDataStructType)
|
|
@@ -308,10 +314,7 @@ export function tirTypeToUplcType(t) {
|
|
|
308
314
|
|| t instanceof TirFuncT
|
|
309
315
|
|| t instanceof TirSopOptT
|
|
310
316
|
|| t instanceof TirSoPStructType
|
|
311
|
-
|| t instanceof TirTypeParam
|
|
312
|
-
|| t instanceof TirBlsG1T
|
|
313
|
-
|| t instanceof TirBlsG2T
|
|
314
|
-
|| t instanceof TirMlResultT)
|
|
317
|
+
|| t instanceof TirTypeParam)
|
|
315
318
|
throw new Error("invalid uplc const type");
|
|
316
319
|
const tsEnsureExsaustiveCheck = t;
|
|
317
320
|
throw new Error("tirTypeToUplcType: unreachable");
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { IRHoisted } from "../../IRNodes/IRHoisted.js";
|
|
2
|
+
import { IRLetted } from "../../IRNodes/IRLetted.js";
|
|
2
3
|
import { IRNative } from "../../IRNodes/IRNative/index.js";
|
|
3
4
|
import { _modifyChildFromTo } from "./_modifyChildFromTo.js";
|
|
4
5
|
import { iterTree } from "./iterTree.js";
|
|
5
6
|
export function _makeAllNegativeNativesHoisted(term) {
|
|
6
7
|
iterTree(term, elem => {
|
|
7
|
-
if (elem instanceof IRNative
|
|
8
|
+
if (elem instanceof IRNative
|
|
9
|
+
// already wrapped in a sharing container
|
|
10
|
+
&& !(elem.parent instanceof IRHoisted)
|
|
11
|
+
// A native that is the direct value of an `IRLetted` is already
|
|
12
|
+
// shared via the letting mechanism, and crucially `IRLetted.value`
|
|
13
|
+
// UNWRAPS any `IRHoisted` assigned to it (see IRLetted.set value).
|
|
14
|
+
// Wrapping such a native therefore never "sticks" — the wrapper is
|
|
15
|
+
// immediately stripped back to the bare native and we'd loop
|
|
16
|
+
// forever re-wrapping it. Leave it letted.
|
|
17
|
+
&& !(elem.parent instanceof IRLetted)) {
|
|
8
18
|
_modifyChildFromTo(elem.parent, elem, new IRHoisted(elem));
|
|
9
19
|
return true;
|
|
10
20
|
}
|
|
@@ -61,6 +61,13 @@ export function compileIRToUPLC(term, paritalOptions = defaultOptions) {
|
|
|
61
61
|
term = replaceNativesAndReturnRoot(term);
|
|
62
62
|
// re-call rewrite to optimize introduced hoisted
|
|
63
63
|
term = rewriteNativesAppliedToConstantsAndReturnRoot(term);
|
|
64
|
+
// the rewrite above can itself introduce custom (negative-tag) natives
|
|
65
|
+
// (eg. `equalsInteger(x, 0)` -> `_isZero(x)`, `addInteger(x, 1)` ->
|
|
66
|
+
// `_increment(x)`); lower those too, otherwise they survive as bare
|
|
67
|
+
// IRNatives and crash the later forcing pass with
|
|
68
|
+
// "getNRequiredForces ... input was: -<tag>". This only surfaced in
|
|
69
|
+
// contracts complex enough for the rewrite to fire on shared/hoisted bodies.
|
|
70
|
+
term = replaceNativesAndReturnRoot(term);
|
|
64
71
|
// Lower `strictIfThenElse` triple-apps to `IRCase` BEFORE
|
|
65
72
|
// `replaceForcedNativesWithHoisted` would otherwise hoist
|
|
66
73
|
// `(force ifThenElse)` into a shared variable that's no longer
|
|
@@ -3,6 +3,7 @@ import { IRNative } from "../../../IRNodes/IRNative/index.js";
|
|
|
3
3
|
import { IRTerm } from "../../../IRTerm.js";
|
|
4
4
|
export declare const hoisted_id: IRHoisted;
|
|
5
5
|
export declare const hoisted_not: IRHoisted;
|
|
6
|
+
export declare const hoisted_equalBoolean: IRHoisted;
|
|
6
7
|
export declare const hoisted_incr: IRHoisted;
|
|
7
8
|
export declare const hoisted_decr: IRHoisted;
|
|
8
9
|
export declare const hoisted_isZero: IRHoisted;
|
|
@@ -28,6 +28,15 @@ export const hoisted_not = new IRHoisted((() => {
|
|
|
28
28
|
return new IRFunc([someBool], _ir_apps(IRNative.strictIfThenElse, new IRVar(someBool), IRConst.bool(false), IRConst.bool(true)));
|
|
29
29
|
})());
|
|
30
30
|
hoisted_not.hash;
|
|
31
|
+
// boolean equality: `a == b` ≡ `if a then b else (not b)`
|
|
32
|
+
export const hoisted_equalBoolean = new IRHoisted((() => {
|
|
33
|
+
const a = Symbol("a");
|
|
34
|
+
const b = Symbol("b");
|
|
35
|
+
return new IRFunc([a, b], _ir_apps(IRNative.strictIfThenElse, new IRVar(a), new IRVar(b),
|
|
36
|
+
// not b
|
|
37
|
+
_ir_apps(IRNative.strictIfThenElse, new IRVar(b), IRConst.bool(false), IRConst.bool(true))));
|
|
38
|
+
})());
|
|
39
|
+
hoisted_equalBoolean.hash;
|
|
31
40
|
export const hoisted_incr = new IRHoisted(new IRApp(IRNative.addInteger, IRConst.int(1)));
|
|
32
41
|
hoisted_incr.hash;
|
|
33
42
|
export const hoisted_decr = new IRHoisted(new IRApp(IRNative.addInteger, IRConst.int(-1)));
|
|
@@ -324,7 +333,7 @@ export function nativeToIR(native) {
|
|
|
324
333
|
case IRNativeTag._increment: return hoisted_addOne.clone();
|
|
325
334
|
case IRNativeTag._decrement: return hoisted_subOne.clone();
|
|
326
335
|
// case IRNativeTag._bytesToIntBE: ;
|
|
327
|
-
|
|
336
|
+
case IRNativeTag._equalBoolean: return hoisted_equalBoolean.clone();
|
|
328
337
|
// case IRNativeTag._equalPairData: ;
|
|
329
338
|
case IRNativeTag._mkEqualsList: return hoisted_mkEqualsList.clone();
|
|
330
339
|
case IRNativeTag._negateInt: return hoisted_negateInteger.clone();
|
|
@@ -52,8 +52,14 @@ export class Source {
|
|
|
52
52
|
lineColumn = 1;
|
|
53
53
|
/** Determines the line number at the specified position. Starts at `1`. */
|
|
54
54
|
lineAt(pos) {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
// Synthetic / internal nodes carry mock ranges (`SourceRange.mock` uses
|
|
56
|
+
// -1) and some positions can point past the end of the text. Clamp
|
|
57
|
+
// instead of throwing, otherwise printing a single diagnostic on such a
|
|
58
|
+
// node crashes the whole diagnostic pass and hides every later error.
|
|
59
|
+
if (pos < 0)
|
|
60
|
+
pos = 0;
|
|
61
|
+
if (pos >= 0x7fffffff)
|
|
62
|
+
pos = Math.max(0, this.text.length);
|
|
57
63
|
let lineCache = this.lineCache;
|
|
58
64
|
if (!lineCache) {
|
|
59
65
|
this.lineCache = lineCache = [0];
|
|
@@ -43,6 +43,9 @@ export declare class AstCompiler extends DiagnosticEmitter {
|
|
|
43
43
|
get rootPath(): string;
|
|
44
44
|
constructor(cfg: CompilerOptions, io?: CompilerIoApi, diagnostics?: DiagnosticMessage[]);
|
|
45
45
|
private _isExporting;
|
|
46
|
+
/** scope used to resolve field types of synthetic internal type decls while a contract body is being derived */
|
|
47
|
+
private _internalDeclScope;
|
|
48
|
+
private _internalDeclSrcUid;
|
|
46
49
|
export(funcName: string, modulePath?: string): Promise<TypedProgram>;
|
|
47
50
|
/**
|
|
48
51
|
* Parses the entry file, wraps all top-level statements
|
|
@@ -113,7 +116,7 @@ export declare class AstCompiler extends DiagnosticEmitter {
|
|
|
113
116
|
private _collectNamespaceDeclarations;
|
|
114
117
|
private _compileNamespaceDecl;
|
|
115
118
|
private _collectInterfaceDeclarations;
|
|
116
|
-
registerInternalTypeDecl(decl: StructDecl | TypeAliasDecl): void;
|
|
119
|
+
registerInternalTypeDecl(decl: StructDecl | TypeAliasDecl, scope?: AstScope, srcUid?: string): void;
|
|
117
120
|
private _collectTypeDeclarations;
|
|
118
121
|
private _compileStructDecl;
|
|
119
122
|
private _compileTypeAliasDecl;
|
|
@@ -101,6 +101,9 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
101
101
|
this._isExporting = false;
|
|
102
102
|
}
|
|
103
103
|
_isExporting;
|
|
104
|
+
/** scope used to resolve field types of synthetic internal type decls while a contract body is being derived */
|
|
105
|
+
_internalDeclScope = undefined;
|
|
106
|
+
_internalDeclSrcUid = "";
|
|
104
107
|
async export(funcName, modulePath) {
|
|
105
108
|
this._isExporting = true;
|
|
106
109
|
if (typeof funcName !== "string")
|
|
@@ -357,7 +360,7 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
357
360
|
i--;
|
|
358
361
|
continue;
|
|
359
362
|
}
|
|
360
|
-
const funcDeclContract = this._contractDeclToFuncDecl(stmt);
|
|
363
|
+
const funcDeclContract = this._contractDeclToFuncDecl(stmt, topLevelScope, srcUid);
|
|
361
364
|
if (!funcDeclContract) {
|
|
362
365
|
// remove from array so we don't process it again
|
|
363
366
|
void stmts.splice(i, 1);
|
|
@@ -665,8 +668,13 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
665
668
|
i--;
|
|
666
669
|
}
|
|
667
670
|
}
|
|
668
|
-
registerInternalTypeDecl(decl
|
|
669
|
-
|
|
671
|
+
registerInternalTypeDecl(decl,
|
|
672
|
+
// scope in which the decl's field types are resolved; defaults to the
|
|
673
|
+
// contract source scope while a contract body is being derived (so
|
|
674
|
+
// synthetic decls that reference user types — eg. a redeemer whose
|
|
675
|
+
// fields are the method params — resolve), otherwise the prelude.
|
|
676
|
+
scope = this._internalDeclScope ?? this.preludeScope, srcUid = this._internalDeclSrcUid) {
|
|
677
|
+
this._collectTypeDeclarations([decl], srcUid, scope, scope);
|
|
670
678
|
}
|
|
671
679
|
_collectTypeDeclarations(stmts, srcUid, topLevelScope, srcExports) {
|
|
672
680
|
for (let i = 0; i < stmts.length; i++) {
|
|
@@ -990,7 +998,10 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
990
998
|
}
|
|
991
999
|
;
|
|
992
1000
|
}
|
|
993
|
-
_contractDeclToFuncDecl(contractDecl
|
|
1001
|
+
_contractDeclToFuncDecl(contractDecl,
|
|
1002
|
+
// scope where user types referenced by the contract (eg. method param
|
|
1003
|
+
// types used to derive the redeemer) are resolved
|
|
1004
|
+
userScope = this.preludeScope, srcUid = "") {
|
|
994
1005
|
const funcName = getUniqueInternalName(contractDecl.name.text);
|
|
995
1006
|
const paramsInternalNamesMap = new Map();
|
|
996
1007
|
const funcParams = new Array(contractDecl.params.length + 1);
|
|
@@ -1015,7 +1026,21 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
1015
1026
|
funcParams[contractDecl.params.length] = new SimpleVarDecl(new Identifier(scriptContextName, contractDecl.name.range), new AstNamedTypeExpr(new Identifier("ScriptContext", contractDecl.name.range), [], contractDecl.name.range), undefined, // initExpr
|
|
1016
1027
|
CommonFlags.Const, contractDecl.name.range);
|
|
1017
1028
|
const funcSig = new AstFuncType(funcParams, new AstVoidType(contractDecl.name.range), contractDecl.name.range);
|
|
1018
|
-
|
|
1029
|
+
// make user types referenced by synthetic internal type decls (the
|
|
1030
|
+
// redeemer / datum / per-state structs derived below) resolvable in the
|
|
1031
|
+
// contract's source scope, instead of only the prelude.
|
|
1032
|
+
const prevInternalDeclScope = this._internalDeclScope;
|
|
1033
|
+
const prevInternalDeclSrcUid = this._internalDeclSrcUid;
|
|
1034
|
+
this._internalDeclScope = userScope;
|
|
1035
|
+
this._internalDeclSrcUid = srcUid;
|
|
1036
|
+
let contractBody;
|
|
1037
|
+
try {
|
|
1038
|
+
contractBody = _deriveContractBody(this, contractDecl, paramsInternalNamesMap, scriptContextName);
|
|
1039
|
+
}
|
|
1040
|
+
finally {
|
|
1041
|
+
this._internalDeclScope = prevInternalDeclScope;
|
|
1042
|
+
this._internalDeclSrcUid = prevInternalDeclSrcUid;
|
|
1043
|
+
}
|
|
1019
1044
|
if (!contractBody)
|
|
1020
1045
|
return undefined;
|
|
1021
1046
|
return new FuncDecl(new FuncExpr(new Identifier(funcName, contractDecl.name.range), CommonFlags.None, [], // typeParameters
|
|
@@ -2,7 +2,8 @@ import { FuncExpr } from "../../../../ast/nodes/expr/functions/FuncExpr.js";
|
|
|
2
2
|
import { DiagnosticCode } from "../../../../diagnostics/diagnosticMessages.generated.js";
|
|
3
3
|
import { TirCallExpr } from "../../../tir/expressions/TirCallExpr.js";
|
|
4
4
|
import { TirFuncT } from "../../../tir/types/TirNativeType/native/function.js";
|
|
5
|
-
import { canAssignTo } from "../../../tir/types/utils/canAssignTo.js";
|
|
5
|
+
import { canAssignTo, isOptionalEncodingBridge } from "../../../tir/types/utils/canAssignTo.js";
|
|
6
|
+
import { TirTypeConversionExpr } from "../../../tir/expressions/TirTypeConversionExpr.js";
|
|
6
7
|
import { inferTypeArgs } from "../../../tir/types/utils/inferTypeArgs.js";
|
|
7
8
|
import { substituteTypeParams } from "../../../tir/types/utils/substituteTypeParams.js";
|
|
8
9
|
import { TirVariableAccessExpr } from "../../../tir/expressions/TirVariableAccessExpr.js";
|
|
@@ -147,8 +148,15 @@ export function _compileCallExpr(ctx, expr, typeHint) {
|
|
|
147
148
|
? funcType.returnType
|
|
148
149
|
: new TirFuncT(funcType.argTypes.slice(tirArgs.length), funcType.returnType);
|
|
149
150
|
for (let i = 0; i < tirArgs.length && i < funcType.argTypes.length; i++) {
|
|
150
|
-
if (!canAssignTo(tirArgs[i].type, funcType.argTypes[i]))
|
|
151
|
+
if (!canAssignTo(tirArgs[i].type, funcType.argTypes[i])) {
|
|
152
|
+
// bridge the two incompatible `Optional` encodings with a
|
|
153
|
+
// real conversion instead of erroring
|
|
154
|
+
if (isOptionalEncodingBridge(tirArgs[i].type, funcType.argTypes[i])) {
|
|
155
|
+
tirArgs[i] = new TirTypeConversionExpr(tirArgs[i], funcType.argTypes[i], expr.args[i].range);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
151
158
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.args[i].range, tirArgs[i].type.toString(), funcType.argTypes[i].toString());
|
|
159
|
+
}
|
|
152
160
|
}
|
|
153
161
|
return new TirCallExpr(callee, tirArgs, finalCallExprType, expr.range);
|
|
154
162
|
}
|
|
@@ -167,8 +175,15 @@ export function _compileCallExpr(ctx, expr, typeHint) {
|
|
|
167
175
|
const arg = args[i];
|
|
168
176
|
if (!arg)
|
|
169
177
|
return undefined;
|
|
170
|
-
if (!canAssignTo(arg.type, funcType.argTypes[i]))
|
|
178
|
+
if (!canAssignTo(arg.type, funcType.argTypes[i])) {
|
|
179
|
+
// the two `Optional` encodings are incompatible but bridgeable;
|
|
180
|
+
// insert a real encoding conversion instead of erroring
|
|
181
|
+
if (isOptionalEncodingBridge(arg.type, funcType.argTypes[i])) {
|
|
182
|
+
args[i] = new TirTypeConversionExpr(arg, funcType.argTypes[i], expr.args[i].range);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
171
185
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.args[i].range, arg.type.toString(), funcType.argTypes[i].toString());
|
|
186
|
+
}
|
|
172
187
|
}
|
|
173
188
|
return new TirCallExpr(callee, args, finalCallExprType, expr.range);
|
|
174
189
|
}
|
|
@@ -60,21 +60,26 @@ export function _compileCaseExprMatcher(ctx, matcher, patternType, returnTypeHin
|
|
|
60
60
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, matcher.body.range, body.type.toString(), returnTypeHint.toString());
|
|
61
61
|
return new TirCaseMatcher(new TirNamedDeconstructVarDecl(memberName, new Map(), undefined, enumType, undefined, true, patternRange, ctorNameRange), body, matcher.range);
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
// each arm gets its own child scope so that the pattern binders are
|
|
64
|
+
// scoped to the arm and do NOT leak into the enclosing block (which would
|
|
65
|
+
// make two mutually-exclusive arms reusing a binder name collide as
|
|
66
|
+
// "duplicate identifier").
|
|
67
|
+
const armCtx = ctx.newBranchChildScope();
|
|
68
|
+
const pattern = _compileVarDecl(armCtx, matcher.pattern, patternType);
|
|
64
69
|
if (!pattern)
|
|
65
70
|
return undefined;
|
|
66
71
|
if (pattern instanceof TirSimpleVarDecl)
|
|
67
72
|
return ctx.error(DiagnosticCode._case_expression_must_decontructed_the_inspected_value, matcher.pattern.range);
|
|
68
73
|
if (!canAssignTo(pattern.type, patternType))
|
|
69
74
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, matcher.pattern.range, pattern.type.toString(), patternType.toString());
|
|
70
|
-
let bodyCtx =
|
|
75
|
+
let bodyCtx = armCtx;
|
|
71
76
|
if (matchedVarName && pattern instanceof TirNamedDeconstructVarDecl) {
|
|
72
77
|
const parentStruct = getStructType(patternType);
|
|
73
78
|
if (parentStruct) {
|
|
74
79
|
const localIdx = parentStruct.constructors.findIndex(c => c.name === pattern.constrName);
|
|
75
80
|
if (localIdx >= 0) {
|
|
76
81
|
const parentIdx = parentStruct.parentCtorIdx(localIdx);
|
|
77
|
-
bodyCtx =
|
|
82
|
+
bodyCtx = armCtx.newBranchChildScope();
|
|
78
83
|
if (parentStruct instanceof TirDataStructType
|
|
79
84
|
|| parentStruct instanceof TirSoPStructType) {
|
|
80
85
|
bodyCtx.scope.narrowVariable(matchedVarName, parentStruct.narrowTo([parentIdx]));
|
|
@@ -2,6 +2,8 @@ import { DiagnosticCode } from "../../../../../diagnostics/diagnosticMessages.ge
|
|
|
2
2
|
import { TirGreaterThanEqualExpr } from "../../../../tir/expressions/binary/TirBinaryExpr.js";
|
|
3
3
|
import { TirAliasType } from "../../../../tir/types/TirAliasType.js";
|
|
4
4
|
import { canAssignTo } from "../../../../tir/types/utils/canAssignTo.js";
|
|
5
|
+
import { getUnaliased } from "../../../../tir/types/utils/getUnaliased.js";
|
|
6
|
+
import { TirValueT } from "../../../../tir/types/TirNativeType/native/value.js";
|
|
5
7
|
import { normalizeEnumToInt } from "../../../../tir/types/utils/normalizeEnumToInt.js";
|
|
6
8
|
import { _compileExpr } from "../_compileExpr.js";
|
|
7
9
|
// only aviable for ints and bytes
|
|
@@ -11,7 +13,8 @@ export function _compileGreaterThanEqualExpr(ctx, expr, typeHint) {
|
|
|
11
13
|
const left = _compileExpr(ctx, expr.left, typeHint);
|
|
12
14
|
if (!left)
|
|
13
15
|
return undefined;
|
|
14
|
-
if (!
|
|
16
|
+
if (!(getUnaliased(left.type) instanceof TirValueT)
|
|
17
|
+
&& !canAssignTo(left.type, int_t)
|
|
15
18
|
&& !canAssignTo(left.type, bytes_t))
|
|
16
19
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.left.range, left.type.toString(), int_t.toString());
|
|
17
20
|
let leftType = normalizeEnumToInt(left.type, int_t);
|
|
@@ -2,6 +2,8 @@ import { DiagnosticCode } from "../../../../../diagnostics/diagnosticMessages.ge
|
|
|
2
2
|
import { TirGreaterThanExpr } from "../../../../tir/expressions/binary/TirBinaryExpr.js";
|
|
3
3
|
import { TirAliasType } from "../../../../tir/types/TirAliasType.js";
|
|
4
4
|
import { canAssignTo } from "../../../../tir/types/utils/canAssignTo.js";
|
|
5
|
+
import { getUnaliased } from "../../../../tir/types/utils/getUnaliased.js";
|
|
6
|
+
import { TirValueT } from "../../../../tir/types/TirNativeType/native/value.js";
|
|
5
7
|
import { normalizeEnumToInt } from "../../../../tir/types/utils/normalizeEnumToInt.js";
|
|
6
8
|
import { _compileExpr } from "../_compileExpr.js";
|
|
7
9
|
export function _compileGreaterThanExpr(ctx, expr, typeHint) {
|
|
@@ -10,7 +12,8 @@ export function _compileGreaterThanExpr(ctx, expr, typeHint) {
|
|
|
10
12
|
const left = _compileExpr(ctx, expr.left, typeHint);
|
|
11
13
|
if (!left)
|
|
12
14
|
return undefined;
|
|
13
|
-
if (!
|
|
15
|
+
if (!(getUnaliased(left.type) instanceof TirValueT)
|
|
16
|
+
&& !canAssignTo(left.type, int_t)
|
|
14
17
|
&& !canAssignTo(left.type, bytes_t))
|
|
15
18
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.left.range, left.type.toString(), int_t.toString());
|
|
16
19
|
let leftType = normalizeEnumToInt(left.type, int_t);
|
|
@@ -2,6 +2,8 @@ import { DiagnosticCode } from "../../../../../diagnostics/diagnosticMessages.ge
|
|
|
2
2
|
import { TirLessThanEqualExpr } from "../../../../tir/expressions/binary/TirBinaryExpr.js";
|
|
3
3
|
import { TirAliasType } from "../../../../tir/types/TirAliasType.js";
|
|
4
4
|
import { canAssignTo } from "../../../../tir/types/utils/canAssignTo.js";
|
|
5
|
+
import { getUnaliased } from "../../../../tir/types/utils/getUnaliased.js";
|
|
6
|
+
import { TirValueT } from "../../../../tir/types/TirNativeType/native/value.js";
|
|
5
7
|
import { normalizeEnumToInt } from "../../../../tir/types/utils/normalizeEnumToInt.js";
|
|
6
8
|
import { int_t, bytes_t } from "../../../../tir/program/stdScope/stdScope.js";
|
|
7
9
|
import { _compileExpr } from "../_compileExpr.js";
|
|
@@ -9,7 +11,8 @@ export function _compileLessThanEqualExpr(ctx, expr, typeHint) {
|
|
|
9
11
|
const left = _compileExpr(ctx, expr.left, typeHint);
|
|
10
12
|
if (!left)
|
|
11
13
|
return undefined;
|
|
12
|
-
if (!
|
|
14
|
+
if (!(getUnaliased(left.type) instanceof TirValueT)
|
|
15
|
+
&& !canAssignTo(left.type, int_t)
|
|
13
16
|
&& !canAssignTo(left.type, bytes_t))
|
|
14
17
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.left.range, left.type.toString(), int_t.toString());
|
|
15
18
|
let leftType = normalizeEnumToInt(left.type, int_t);
|
|
@@ -2,6 +2,8 @@ import { DiagnosticCode } from "../../../../../diagnostics/diagnosticMessages.ge
|
|
|
2
2
|
import { TirLessThanExpr } from "../../../../tir/expressions/binary/TirBinaryExpr.js";
|
|
3
3
|
import { TirAliasType } from "../../../../tir/types/TirAliasType.js";
|
|
4
4
|
import { canAssignTo } from "../../../../tir/types/utils/canAssignTo.js";
|
|
5
|
+
import { getUnaliased } from "../../../../tir/types/utils/getUnaliased.js";
|
|
6
|
+
import { TirValueT } from "../../../../tir/types/TirNativeType/native/value.js";
|
|
5
7
|
import { normalizeEnumToInt } from "../../../../tir/types/utils/normalizeEnumToInt.js";
|
|
6
8
|
import { _compileExpr } from "../_compileExpr.js";
|
|
7
9
|
export function _compileLessThanExpr(ctx, expr, typeHint) {
|
|
@@ -10,7 +12,8 @@ export function _compileLessThanExpr(ctx, expr, typeHint) {
|
|
|
10
12
|
const left = _compileExpr(ctx, expr.left, typeHint);
|
|
11
13
|
if (!left)
|
|
12
14
|
return undefined;
|
|
13
|
-
if (!
|
|
15
|
+
if (!(getUnaliased(left.type) instanceof TirValueT)
|
|
16
|
+
&& !canAssignTo(left.type, int_t)
|
|
14
17
|
&& !canAssignTo(left.type, bytes_t))
|
|
15
18
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.left.range, left.type.toString(), int_t.toString());
|
|
16
19
|
let leftType = normalizeEnumToInt(left.type, int_t);
|
|
@@ -148,6 +148,9 @@ function getLinearMapMethods(kT, vT) {
|
|
|
148
148
|
lookup: new TirFuncT([kT], new TirSopOptT(vT)),
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
|
+
// Method-form signatures: the receiver (the `bytes` value the method is
|
|
152
|
+
// called on) is implicit and dropped from the argument list. These mirror the
|
|
153
|
+
// `std.bytes.*` namespace functions registered in `populateStdNamespace`.
|
|
151
154
|
const bytesMethods = Object.freeze({
|
|
152
155
|
length: new TirFuncT([], int_t),
|
|
153
156
|
subByteString: new TirFuncT([int_t, int_t], bytes_t),
|
|
@@ -155,6 +158,14 @@ const bytesMethods = Object.freeze({
|
|
|
155
158
|
show: new TirFuncT([], bytes_t),
|
|
156
159
|
decodeUtf8: new TirFuncT([], string_t),
|
|
157
160
|
prepend: new TirFuncT([int_t], bytes_t),
|
|
161
|
+
concat: new TirFuncT([bytes_t], bytes_t),
|
|
162
|
+
indexAt: new TirFuncT([int_t], int_t),
|
|
163
|
+
equals: new TirFuncT([bytes_t], bool_t),
|
|
164
|
+
lessThan: new TirFuncT([bytes_t], bool_t),
|
|
165
|
+
lessThanEquals: new TirFuncT([bytes_t], bool_t),
|
|
166
|
+
greaterThan: new TirFuncT([bytes_t], bool_t),
|
|
167
|
+
greaterThanEquals: new TirFuncT([bytes_t], bool_t),
|
|
168
|
+
toInt: new TirFuncT([], int_t),
|
|
158
169
|
});
|
|
159
170
|
const stringMethods = Object.freeze({
|
|
160
171
|
encodeUtf8: new TirFuncT([], bytes_t),
|
|
@@ -20,7 +20,11 @@ import { TirWhileStmt } from "../../tir/statements/TirWhileStmt.js";
|
|
|
20
20
|
import { TirSoPStructType, TirStructConstr, TirStructField } from "../../tir/types/TirStructType.js";
|
|
21
21
|
import { isExpressifyFuncParam } from "./ExpressifyCtx.js";
|
|
22
22
|
export function determineReassignedVariablesAndReturn(stmt) {
|
|
23
|
-
|
|
23
|
+
// `keepSortedStrArrInplace` (used below) requires BOTH inputs sorted;
|
|
24
|
+
// `stmt.deps()` is NOT sorted, so sort a copy here. Without this, reassigned
|
|
25
|
+
// variables get spuriously dropped from the threaded loop/branch state —
|
|
26
|
+
// e.g. a `for` loop reassigning two accumulators would freeze all but one.
|
|
27
|
+
const originalStmtDeps = stmt.deps().slice().sort();
|
|
24
28
|
const stack = [stmt];
|
|
25
29
|
const reassignedSet = new Set();
|
|
26
30
|
let returns = false;
|
|
@@ -78,7 +82,11 @@ export function determineReassignedVariablesAndReturn(stmt) {
|
|
|
78
82
|
};
|
|
79
83
|
}
|
|
80
84
|
export function determineReassignedVariablesAndFlowInfos(stmt) {
|
|
81
|
-
|
|
85
|
+
// `keepSortedStrArrInplace` (used below) requires BOTH inputs sorted;
|
|
86
|
+
// `stmt.deps()` is NOT sorted, so sort a copy here. Without this, reassigned
|
|
87
|
+
// variables get spuriously dropped from the threaded loop/branch state —
|
|
88
|
+
// e.g. a `for` loop reassigning two accumulators would freeze all but one.
|
|
89
|
+
const originalStmtDeps = stmt.deps().slice().sort();
|
|
82
90
|
const stack = [stmt];
|
|
83
91
|
const reassignedSet = new Set();
|
|
84
92
|
let returns = false;
|
|
@@ -357,7 +357,29 @@ loopReplacements, assertions = []) {
|
|
|
357
357
|
//*/
|
|
358
358
|
);
|
|
359
359
|
}
|
|
360
|
-
|
|
360
|
+
// more than one case does not terminate:
|
|
361
|
+
// each non-terminating case must merge its reassigned variables
|
|
362
|
+
// (and optional early return) into a common SoP state, then the
|
|
363
|
+
// rest of the body continues from that merged state.
|
|
364
|
+
// (mirrors the non-terminating `if` statement handling above)
|
|
365
|
+
// build a SoP type to return from each branch
|
|
366
|
+
const { sop } = getBranchStmtReturnType(reassignsAndReturns, ctx, stmt.range);
|
|
367
|
+
const reassignedNames = reassignsAndReturns.reassigned;
|
|
368
|
+
const finalExpression = new TirCaseExpr(expressifyVars(ctx, stmt.matchExpr), stmt.cases.map(_case => {
|
|
369
|
+
if (_case.pattern instanceof TirArrayLikeDeconstr)
|
|
370
|
+
throw new Error("array-like deconstruction in match statement is not supported");
|
|
371
|
+
_case.pattern = toNamedDeconstructVarDecl(_case.pattern);
|
|
372
|
+
const caseCtx = ctx.newChild();
|
|
373
|
+
const nestedDeconstructs = flattenSopNamedDeconstructInplace_addTopDestructToCtx_getNestedDeconstruct(_case.pattern, caseCtx);
|
|
374
|
+
const branchStmts = _case.body instanceof TirBlockStmt
|
|
375
|
+
? _case.body.stmts
|
|
376
|
+
: [_case.body];
|
|
377
|
+
const branchBlock = new TirBlockStmt(nestedDeconstructs.concat(branchStmts), _case.body.range);
|
|
378
|
+
// expressify the branch so it returns the merged SoP state
|
|
379
|
+
const caseBody = expressifyIfBranch(caseCtx, branchBlock, reassignedNames, sop, loopReplacements);
|
|
380
|
+
return new TirCaseMatcher(_case.pattern, caseBody, _case.range);
|
|
381
|
+
}), stmt.wildcardCase ? new TirWildcardCaseMatcher(expressifyIfBranch(ctx.newChild(), stmt.wildcardCase.body, reassignedNames, sop, loopReplacements), stmt.wildcardCase.range) : undefined, sop, stmt.range);
|
|
382
|
+
return TirAssertAndContinueExpr.fromStmtsAndContinuation(assertions, wrapNonTerminatingFinalStmtAsCaseExpr(finalExpression, sop, ctx, stmt.range, reassignsAndReturns, bodyStmts, loopReplacements));
|
|
361
383
|
}
|
|
362
384
|
// else if( stmt instanceof TirForOfStmt ) {
|
|
363
385
|
//
|
|
@@ -519,6 +519,31 @@ function expressifyMethodCall(ctx, methodCall) {
|
|
|
519
519
|
if (methodName === "prepend") {
|
|
520
520
|
return new TirCallExpr(TirNativeFunc.consByteString, [methodCall.args[0], objectExpr], bytes_t, exprRange);
|
|
521
521
|
}
|
|
522
|
+
// receiver-first byte ops: `a.op( b )` -> native( a, b )
|
|
523
|
+
if (methodName === "concat") {
|
|
524
|
+
return new TirCallExpr(TirNativeFunc.appendByteString, [objectExpr, methodCall.args[0]], bytes_t, exprRange);
|
|
525
|
+
}
|
|
526
|
+
if (methodName === "indexAt") {
|
|
527
|
+
return new TirCallExpr(TirNativeFunc.indexByteString, [objectExpr, methodCall.args[0]], int_t, exprRange);
|
|
528
|
+
}
|
|
529
|
+
if (methodName === "equals") {
|
|
530
|
+
return new TirCallExpr(TirNativeFunc.equalsByteString, [objectExpr, methodCall.args[0]], bool_t, exprRange);
|
|
531
|
+
}
|
|
532
|
+
if (methodName === "lessThan") {
|
|
533
|
+
return new TirCallExpr(TirNativeFunc.lessThanByteString, [objectExpr, methodCall.args[0]], bool_t, exprRange);
|
|
534
|
+
}
|
|
535
|
+
if (methodName === "lessThanEquals") {
|
|
536
|
+
return new TirCallExpr(TirNativeFunc.lessThanEqualsByteString, [objectExpr, methodCall.args[0]], bool_t, exprRange);
|
|
537
|
+
}
|
|
538
|
+
if (methodName === "greaterThan") {
|
|
539
|
+
return new TirCallExpr(TirNativeFunc._gtBS, [objectExpr, methodCall.args[0]], bool_t, exprRange);
|
|
540
|
+
}
|
|
541
|
+
if (methodName === "greaterThanEquals") {
|
|
542
|
+
return new TirCallExpr(TirNativeFunc._gtEqBS, [objectExpr, methodCall.args[0]], bool_t, exprRange);
|
|
543
|
+
}
|
|
544
|
+
if (methodName === "toInt") {
|
|
545
|
+
return new TirCallExpr(TirNativeFunc._bytesToIntBE, [objectExpr], int_t, exprRange);
|
|
546
|
+
}
|
|
522
547
|
}
|
|
523
548
|
// Generic `.show()` fallback: any value whose type has a built-in
|
|
524
549
|
// `_showIR` impl can be shown by emitting a TirShowExpr. User types
|
|
@@ -113,6 +113,7 @@ export declare class TirNativeFunc implements ITirExpr {
|
|
|
113
113
|
static get _strictOr(): TirNativeFunc;
|
|
114
114
|
static get _gtBS(): TirNativeFunc;
|
|
115
115
|
static get _gtEqBS(): TirNativeFunc;
|
|
116
|
+
static get _bytesToIntBE(): TirNativeFunc;
|
|
116
117
|
static get _gtInt(): TirNativeFunc;
|
|
117
118
|
static get _gtEqInt(): TirNativeFunc;
|
|
118
119
|
static get fstPairData(): TirNativeFunc;
|
|
@@ -709,6 +709,11 @@ export class TirNativeFunc {
|
|
|
709
709
|
bytes_t
|
|
710
710
|
], bool_t));
|
|
711
711
|
}
|
|
712
|
+
static get _bytesToIntBE() {
|
|
713
|
+
return new TirNativeFunc(IRNativeTag._bytesToIntBE, new TirFuncT([
|
|
714
|
+
bytes_t
|
|
715
|
+
], int_t));
|
|
716
|
+
}
|
|
712
717
|
static get _gtInt() {
|
|
713
718
|
return new TirNativeFunc(IRNativeTag._gtInt, new TirFuncT([
|
|
714
719
|
int_t,
|
|
@@ -12,6 +12,31 @@ import { IRConst } from "../../../../IR/IRNodes/IRConst.js";
|
|
|
12
12
|
import { compileIRToUPLC } from "../../../../IR/toUPLC/compileIRToUPLC.js";
|
|
13
13
|
import { _ir_apps } from "../../../../IR/IRNodes/IRApp.js";
|
|
14
14
|
import { _ir_lazyIfThenElse } from "../../../../IR/tree_utils/_ir_lazyIfThenElse.js";
|
|
15
|
+
import { IRFunc } from "../../../../IR/IRNodes/IRFunc.js";
|
|
16
|
+
import { IRVar } from "../../../../IR/IRNodes/IRVar.js";
|
|
17
|
+
/**
|
|
18
|
+
* Lowering for Value relational operators, in terms of the `valueContains`
|
|
19
|
+
* builtin (`valueContains(x, y)` ⟺ `x ≥ y`, componentwise):
|
|
20
|
+
*
|
|
21
|
+
* a <= b → valueContains(b, a)
|
|
22
|
+
* a >= b → valueContains(a, b)
|
|
23
|
+
* a < b → valueContains(a, b) ? false : valueContains(b, a)
|
|
24
|
+
* a > b → valueContains(b, a) ? false : valueContains(a, b)
|
|
25
|
+
*
|
|
26
|
+
* `<` / `>` reference each operand twice, so they are bound once via a lambda
|
|
27
|
+
* to avoid re-evaluating the operand subterms.
|
|
28
|
+
*/
|
|
29
|
+
function _ir_valueContains(geIR, leIR) {
|
|
30
|
+
// valueContains(ge, le) ⟺ ge ≥ le
|
|
31
|
+
return _ir_apps(IRNative.valueContains, geIR, leIR);
|
|
32
|
+
}
|
|
33
|
+
/** strict Value `lo < hi` */
|
|
34
|
+
function _ir_valueStrictLess(loIR, hiIR) {
|
|
35
|
+
const lo = Symbol("vlt_lo");
|
|
36
|
+
const hi = Symbol("vlt_hi");
|
|
37
|
+
// (λ lo hi. valueContains(lo, hi) ? false : valueContains(hi, lo)) loIR hiIR
|
|
38
|
+
return _ir_apps(new IRFunc([lo, hi], _ir_lazyIfThenElse(_ir_valueContains(new IRVar(lo), new IRVar(hi)), IRConst.bool(false), _ir_valueContains(new IRVar(hi), new IRVar(lo)))), loIR, hiIR);
|
|
39
|
+
}
|
|
15
40
|
export function isTirBinaryExpr(thing) {
|
|
16
41
|
return isObject(thing) && (thing instanceof TirExponentiationExpr // int
|
|
17
42
|
|| thing instanceof TirLessThanExpr // bool
|
|
@@ -89,6 +114,9 @@ export class TirLessThanExpr {
|
|
|
89
114
|
get isConstant() { return this.left.isConstant && this.right.isConstant; }
|
|
90
115
|
toIR(ctx) {
|
|
91
116
|
const type = getUnaliased(this.left.type);
|
|
117
|
+
// Value: a < b → valueContains(a,b) ? false : valueContains(b,a)
|
|
118
|
+
if (type instanceof TirValueT)
|
|
119
|
+
return _ir_valueStrictLess(this.left.toIR(ctx), this.right.toIR(ctx));
|
|
92
120
|
const irFunc = ((type instanceof TirIntT || type instanceof TirEnumType) ? IRNative.lessThanInteger :
|
|
93
121
|
type instanceof TirBytesT ? IRNative.lessThanByteString :
|
|
94
122
|
undefined);
|
|
@@ -126,6 +154,9 @@ export class TirGreaterThanExpr {
|
|
|
126
154
|
get isConstant() { return this.left.isConstant && this.right.isConstant; }
|
|
127
155
|
toIR(ctx) {
|
|
128
156
|
const type = getUnaliased(this.left.type);
|
|
157
|
+
// Value: a > b ≡ b < a
|
|
158
|
+
if (type instanceof TirValueT)
|
|
159
|
+
return _ir_valueStrictLess(this.right.toIR(ctx), this.left.toIR(ctx));
|
|
129
160
|
const irFunc = ((type instanceof TirIntT || type instanceof TirEnumType) ? IRNative.lessThanInteger :
|
|
130
161
|
type instanceof TirBytesT ? IRNative.lessThanByteString :
|
|
131
162
|
undefined);
|
|
@@ -165,6 +196,9 @@ export class TirLessThanEqualExpr {
|
|
|
165
196
|
get isConstant() { return this.left.isConstant && this.right.isConstant; }
|
|
166
197
|
toIR(ctx) {
|
|
167
198
|
const type = getUnaliased(this.left.type);
|
|
199
|
+
// Value: a <= b → valueContains(b, a) (b ≥ a)
|
|
200
|
+
if (type instanceof TirValueT)
|
|
201
|
+
return _ir_valueContains(this.right.toIR(ctx), this.left.toIR(ctx));
|
|
168
202
|
const irFunc = ((type instanceof TirIntT || type instanceof TirEnumType) ? IRNative.lessThanEqualInteger :
|
|
169
203
|
type instanceof TirBytesT ? IRNative.lessThanEqualsByteString :
|
|
170
204
|
undefined);
|
|
@@ -202,6 +236,9 @@ export class TirGreaterThanEqualExpr {
|
|
|
202
236
|
get isConstant() { return this.left.isConstant && this.right.isConstant; }
|
|
203
237
|
toIR(ctx) {
|
|
204
238
|
const type = getUnaliased(this.left.type);
|
|
239
|
+
// Value: a >= b → valueContains(a, b) (a ≥ b)
|
|
240
|
+
if (type instanceof TirValueT)
|
|
241
|
+
return _ir_valueContains(this.left.toIR(ctx), this.right.toIR(ctx));
|
|
205
242
|
const irFunc = ((type instanceof TirIntT || type instanceof TirEnumType) ? IRNative.lessThanEqualInteger :
|
|
206
243
|
type instanceof TirBytesT ? IRNative.lessThanEqualsByteString :
|
|
207
244
|
undefined);
|
|
@@ -189,6 +189,8 @@ export function populateStdNamespace(program) {
|
|
|
189
189
|
defineBuiltin(blsNsScope, "g1HashToGroup", IRNativeTag.bls12_381_G1_hashToGroup, new TirFuncT([bytes_t, bytes_t], g1_t), blsNs);
|
|
190
190
|
defineBuiltin(blsNsScope, "g1Compress", IRNativeTag.bls12_381_G1_compress, new TirFuncT([g1_t], bytes_t), blsNs);
|
|
191
191
|
defineBuiltin(blsNsScope, "g1Uncompress", IRNativeTag.bls12_381_G1_uncompress, new TirFuncT([bytes_t], g1_t), blsNs);
|
|
192
|
+
// CIP-0381 multi-scalar multiplication: Σ scalars[i] · points[i]
|
|
193
|
+
defineBuiltin(blsNsScope, "g1MultiScalarMul", IRNativeTag.bls12_381_G1_multiScalarMul, new TirFuncT([new TirListT(int_t), new TirListT(g1_t)], g1_t), blsNs);
|
|
192
194
|
defineBuiltin(blsNsScope, "g2Add", IRNativeTag.bls12_381_G2_add, new TirFuncT([g2_t, g2_t], g2_t), blsNs);
|
|
193
195
|
defineBuiltin(blsNsScope, "g2Neg", IRNativeTag.bls12_381_G2_neg, new TirFuncT([g2_t], g2_t), blsNs);
|
|
194
196
|
defineBuiltin(blsNsScope, "g2ScalarMul", IRNativeTag.bls12_381_G2_scalarMul, new TirFuncT([int_t, g2_t], g2_t), blsNs);
|
|
@@ -196,6 +198,7 @@ export function populateStdNamespace(program) {
|
|
|
196
198
|
defineBuiltin(blsNsScope, "g2HashToGroup", IRNativeTag.bls12_381_G2_hashToGroup, new TirFuncT([bytes_t, bytes_t], g2_t), blsNs);
|
|
197
199
|
defineBuiltin(blsNsScope, "g2Compress", IRNativeTag.bls12_381_G2_compress, new TirFuncT([g2_t], bytes_t), blsNs);
|
|
198
200
|
defineBuiltin(blsNsScope, "g2Uncompress", IRNativeTag.bls12_381_G2_uncompress, new TirFuncT([bytes_t], g2_t), blsNs);
|
|
201
|
+
defineBuiltin(blsNsScope, "g2MultiScalarMul", IRNativeTag.bls12_381_G2_multiScalarMul, new TirFuncT([new TirListT(int_t), new TirListT(g2_t)], g2_t), blsNs);
|
|
199
202
|
defineBuiltin(blsNsScope, "millerLoop", IRNativeTag.bls12_381_millerLoop, new TirFuncT([g1_t, g2_t], ml_t), blsNs);
|
|
200
203
|
defineBuiltin(blsNsScope, "mulMlResult", IRNativeTag.bls12_381_mulMlResult, new TirFuncT([ml_t, ml_t], ml_t), blsNs);
|
|
201
204
|
defineBuiltin(blsNsScope, "finalVerify", IRNativeTag.bls12_381_finalVerify, new TirFuncT([ml_t, ml_t], bool_t), blsNs);
|
|
@@ -47,6 +47,8 @@ export function populateStdScope(program) {
|
|
|
47
47
|
}
|
|
48
48
|
_defineStdUnambigous(void_t);
|
|
49
49
|
_defineStdUnambigous(bool_t);
|
|
50
|
+
// `bool` is a common spelling; accept it as an alias for `boolean`.
|
|
51
|
+
stdScope.defineUnambigousType("bool", bool_t.toTirTypeKey(), true, new Map());
|
|
50
52
|
_defineStdUnambigous(int_t);
|
|
51
53
|
_defineStdUnambigous(bytes_t);
|
|
52
54
|
_defineStdUnambigous(string_t);
|
|
@@ -15,6 +15,18 @@ export declare function getStructType(type: TirType | undefined): TirStructType
|
|
|
15
15
|
export declare function getNamedDestructableType(type: TirType | undefined): TirNamedDestructableType | undefined;
|
|
16
16
|
export declare function canAssignToOptional(type: TirType): type is TirDataOptT | TirSopOptT | TirAliasType<TirDataOptT | TirSopOptT>;
|
|
17
17
|
export declare function canAssignToList(type: TirType): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The two `Optional` representations (data-encoded `TirDataOptT` and
|
|
20
|
+
* SoP-encoded `TirSopOptT`) are **incompatible at runtime** and therefore not
|
|
21
|
+
* directly assignable. They can however be bridged by a real encoding
|
|
22
|
+
* conversion (`TirTypeConversionExpr`, lowered via `_inlineFromData` /
|
|
23
|
+
* `_inlineToData`).
|
|
24
|
+
*
|
|
25
|
+
* @returns `true` if `from` and `to` are Optionals of *different* encodings
|
|
26
|
+
* whose type arguments are assignable — i.e. assigning `from` to `to`
|
|
27
|
+
* only requires inserting such an encoding conversion.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isOptionalEncodingBridge(from: TirType, to: TirType): boolean;
|
|
18
30
|
/**
|
|
19
31
|
* @returns `true` if `a` can be assigned to `b` **without** explicit cast
|
|
20
32
|
*
|
|
@@ -60,6 +60,32 @@ export function canAssignToList(type) {
|
|
|
60
60
|
// || type instanceof TirLinearMapT
|
|
61
61
|
);
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* The two `Optional` representations (data-encoded `TirDataOptT` and
|
|
65
|
+
* SoP-encoded `TirSopOptT`) are **incompatible at runtime** and therefore not
|
|
66
|
+
* directly assignable. They can however be bridged by a real encoding
|
|
67
|
+
* conversion (`TirTypeConversionExpr`, lowered via `_inlineFromData` /
|
|
68
|
+
* `_inlineToData`).
|
|
69
|
+
*
|
|
70
|
+
* @returns `true` if `from` and `to` are Optionals of *different* encodings
|
|
71
|
+
* whose type arguments are assignable — i.e. assigning `from` to `to`
|
|
72
|
+
* only requires inserting such an encoding conversion.
|
|
73
|
+
*/
|
|
74
|
+
export function isOptionalEncodingBridge(from, to) {
|
|
75
|
+
from = getUnaliased(from);
|
|
76
|
+
to = getUnaliased(to);
|
|
77
|
+
const fromIsData = from instanceof TirDataOptT;
|
|
78
|
+
const fromIsSop = from instanceof TirSopOptT;
|
|
79
|
+
const toIsData = to instanceof TirDataOptT;
|
|
80
|
+
const toIsSop = to instanceof TirSopOptT;
|
|
81
|
+
if (!(fromIsData || fromIsSop) || !(toIsData || toIsSop))
|
|
82
|
+
return false;
|
|
83
|
+
// only bridge *across* encodings; same-encoding cases are handled by the
|
|
84
|
+
// normal assignability rules
|
|
85
|
+
if (fromIsData === toIsData)
|
|
86
|
+
return false;
|
|
87
|
+
return canAssignTo(from.typeArg, to.typeArg);
|
|
88
|
+
}
|
|
63
89
|
/**
|
|
64
90
|
* @returns `true` if `a` can be assigned to `b` **without** explicit cast
|
|
65
91
|
*
|
package/dist/parser/Parser.d.ts
CHANGED
|
@@ -53,6 +53,28 @@ interface ParseStmtOpts {
|
|
|
53
53
|
}
|
|
54
54
|
export declare class Parser extends DiagnosticEmitter {
|
|
55
55
|
readonly tn: Tokenizer;
|
|
56
|
+
/**
|
|
57
|
+
* When set, the leading primary of the next parsed expression must NOT be
|
|
58
|
+
* interpreted as a named-object/struct literal (`Ident{ ... }`).
|
|
59
|
+
*
|
|
60
|
+
* This is needed for statements of the form `<keyword> <expr> { ... }`
|
|
61
|
+
* (eg. `match subject { ... }`) where the `{` introduces the statement
|
|
62
|
+
* block, not a struct literal body. The flag is consumed (and cleared) by
|
|
63
|
+
* the first `parseExprStart` call, so nested expressions still allow
|
|
64
|
+
* struct literals.
|
|
65
|
+
*/
|
|
66
|
+
private _noStructLiteral;
|
|
67
|
+
/**
|
|
68
|
+
* When set (while parsing the subject / arm bodies of a `case` expression),
|
|
69
|
+
* the `is` keyword is treated as a low-precedence arm separator rather than
|
|
70
|
+
* the binary `is` (type-narrowing) operator. This lets an arm body absorb
|
|
71
|
+
* higher-precedence operators (comparisons, arithmetic, etc.) while still
|
|
72
|
+
* stopping before the next arm's `is`.
|
|
73
|
+
*
|
|
74
|
+
* Inside parentheses the body is re-parsed at a low precedence, so a binary
|
|
75
|
+
* `is` there is still recognised normally (eg. `=> ( x is Foo )`).
|
|
76
|
+
*/
|
|
77
|
+
private _caseArmIsLowPrec;
|
|
56
78
|
constructor(tokenizer: Tokenizer, diagnostics?: DiagnosticMessage[] | undefined);
|
|
57
79
|
static parseFile(path: string, src: string, getUid?: () => string, isEntry?: boolean): [Source, DiagnosticMessage[]];
|
|
58
80
|
static parseSource(src: Source, diagnostics?: DiagnosticMessage[]): DiagnosticMessage[];
|
|
@@ -142,6 +164,11 @@ export declare class Parser extends DiagnosticEmitter {
|
|
|
142
164
|
private _parseTypeAndInitializer;
|
|
143
165
|
parseTypeExpr(suppressErrors?: boolean): AstTypeExpr | undefined;
|
|
144
166
|
parseParenthesizedExpr(startPos?: number | undefined): ParentesizedExpr | undefined;
|
|
167
|
+
/**
|
|
168
|
+
* Like {@link determinePrecedence} but context-sensitive: inside a `case`
|
|
169
|
+
* expression the `is` keyword acts as a low-precedence arm separator.
|
|
170
|
+
*/
|
|
171
|
+
private precedenceOf;
|
|
145
172
|
parseExpr(precedence?: Precedence): PebbleExpr | undefined;
|
|
146
173
|
parseExprStart(): PebbleExpr | undefined;
|
|
147
174
|
private joinPropertyCall;
|
package/dist/parser/Parser.js
CHANGED
|
@@ -81,6 +81,28 @@ import { LitContextExpr } from "../ast/nodes/expr/litteral/LitContextExpr.js";
|
|
|
81
81
|
import { tokenIsAlsoIdentifier } from "../tokenizer/utils/tokenIsAlsoIdentifier.js";
|
|
82
82
|
export class Parser extends DiagnosticEmitter {
|
|
83
83
|
tn;
|
|
84
|
+
/**
|
|
85
|
+
* When set, the leading primary of the next parsed expression must NOT be
|
|
86
|
+
* interpreted as a named-object/struct literal (`Ident{ ... }`).
|
|
87
|
+
*
|
|
88
|
+
* This is needed for statements of the form `<keyword> <expr> { ... }`
|
|
89
|
+
* (eg. `match subject { ... }`) where the `{` introduces the statement
|
|
90
|
+
* block, not a struct literal body. The flag is consumed (and cleared) by
|
|
91
|
+
* the first `parseExprStart` call, so nested expressions still allow
|
|
92
|
+
* struct literals.
|
|
93
|
+
*/
|
|
94
|
+
_noStructLiteral = false;
|
|
95
|
+
/**
|
|
96
|
+
* When set (while parsing the subject / arm bodies of a `case` expression),
|
|
97
|
+
* the `is` keyword is treated as a low-precedence arm separator rather than
|
|
98
|
+
* the binary `is` (type-narrowing) operator. This lets an arm body absorb
|
|
99
|
+
* higher-precedence operators (comparisons, arithmetic, etc.) while still
|
|
100
|
+
* stopping before the next arm's `is`.
|
|
101
|
+
*
|
|
102
|
+
* Inside parentheses the body is re-parsed at a low precedence, so a binary
|
|
103
|
+
* `is` there is still recognised normally (eg. `=> ( x is Foo )`).
|
|
104
|
+
*/
|
|
105
|
+
_caseArmIsLowPrec = false;
|
|
84
106
|
constructor(tokenizer, diagnostics = undefined) {
|
|
85
107
|
super(diagnostics);
|
|
86
108
|
this.tn = tokenizer;
|
|
@@ -1442,6 +1464,15 @@ export class Parser extends DiagnosticEmitter {
|
|
|
1442
1464
|
}
|
|
1443
1465
|
return new ParentesizedExpr(inner, tn.range(startPos, tn.pos));
|
|
1444
1466
|
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Like {@link determinePrecedence} but context-sensitive: inside a `case`
|
|
1469
|
+
* expression the `is` keyword acts as a low-precedence arm separator.
|
|
1470
|
+
*/
|
|
1471
|
+
precedenceOf(kind) {
|
|
1472
|
+
if (kind === Token.Is && this._caseArmIsLowPrec)
|
|
1473
|
+
return Precedence.CaseExpr;
|
|
1474
|
+
return determinePrecedence(kind);
|
|
1475
|
+
}
|
|
1445
1476
|
parseExpr(precedence = Precedence.Comma) {
|
|
1446
1477
|
const tn = this.tn;
|
|
1447
1478
|
let expr = this.parseExprStart();
|
|
@@ -1459,7 +1490,7 @@ export class Parser extends DiagnosticEmitter {
|
|
|
1459
1490
|
// see: http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm#climbing
|
|
1460
1491
|
let nextPrecedence;
|
|
1461
1492
|
let prevState;
|
|
1462
|
-
outer_while: while ((nextPrecedence =
|
|
1493
|
+
outer_while: while ((nextPrecedence = this.precedenceOf(tn.peek())) >= precedence) {
|
|
1463
1494
|
prevState = tn.mark();
|
|
1464
1495
|
const token = tn.next();
|
|
1465
1496
|
// DO NOT DIRECTLY RETURN FROM HERE
|
|
@@ -1645,6 +1676,10 @@ export class Parser extends DiagnosticEmitter {
|
|
|
1645
1676
|
}
|
|
1646
1677
|
parseExprStart() {
|
|
1647
1678
|
const tn = this.tn;
|
|
1679
|
+
// consume the flag: only the leading primary of this expression is
|
|
1680
|
+
// affected; any nested expression parsed below allows struct literals.
|
|
1681
|
+
const noStructLiteral = this._noStructLiteral;
|
|
1682
|
+
this._noStructLiteral = false;
|
|
1648
1683
|
const token = tn.next(IdentifierHandling.Prefer);
|
|
1649
1684
|
const startPos = tn.tokenPos;
|
|
1650
1685
|
switch (token) {
|
|
@@ -1781,7 +1816,7 @@ export class Parser extends DiagnosticEmitter {
|
|
|
1781
1816
|
const identifier = new Identifier(identifierText, tn.range(startPos, tn.pos));
|
|
1782
1817
|
// LitNamedObjExpr
|
|
1783
1818
|
// eg: `Identifier{ a: 1, b: 2 }`
|
|
1784
|
-
if (tn.peek() === Token.OpenBrace) {
|
|
1819
|
+
if (!noStructLiteral && tn.peek() === Token.OpenBrace) {
|
|
1785
1820
|
const endPos = tn.pos;
|
|
1786
1821
|
const litObjExpr = this.parseExprStart();
|
|
1787
1822
|
if (!(litObjExpr instanceof LitObjExpr)) {
|
|
@@ -1792,7 +1827,7 @@ export class Parser extends DiagnosticEmitter {
|
|
|
1792
1827
|
}
|
|
1793
1828
|
// LitNamedObjExpr with type qualifier
|
|
1794
1829
|
// eg: `Type.Constructor{ a: 1, b: 2 }`
|
|
1795
|
-
if (tn.peek() === Token.Dot) {
|
|
1830
|
+
if (!noStructLiteral && tn.peek() === Token.Dot) {
|
|
1796
1831
|
const savedState = tn.mark();
|
|
1797
1832
|
tn.next(); // consume '.'
|
|
1798
1833
|
if (tn.skipIdentifier(IdentifierHandling.Always)) {
|
|
@@ -1902,47 +1937,53 @@ export class Parser extends DiagnosticEmitter {
|
|
|
1902
1937
|
const tn = this.tn;
|
|
1903
1938
|
// at 'case': Expression ('is' VarDecl '=>' Expression)+ ('else' Expression)?
|
|
1904
1939
|
const startPos = tn.tokenPos;
|
|
1905
|
-
//
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
const matcher = this._parseVarDecl(CommonFlags.Const);
|
|
1914
|
-
if (!matcher)
|
|
1915
|
-
return undefined;
|
|
1916
|
-
// SimpleVarDecl patterns are validated by the AstCompiler, which
|
|
1917
|
-
// has type info (enum-member promotion vs invalid catch-all).
|
|
1918
|
-
if (matcher.initExpr || matcher.type)
|
|
1919
|
-
return this.error(DiagnosticCode.Patterns_may_not_have_initializers_or_explicit_types, matcher.initExpr ? matcher.initExpr.range : matcher.type.range);
|
|
1920
|
-
if (!tn.skip(Token.FatArrow))
|
|
1921
|
-
return this.error(DiagnosticCode._0_expected, tn.range(), "=>");
|
|
1922
|
-
// parse body at precedence higher than `is` so the next case-arm's
|
|
1923
|
-
// `is` is left unconsumed (otherwise `is` would be greedily parsed
|
|
1924
|
-
// as the binary `is` operator inside the body)
|
|
1925
|
-
const body = this.parseExpr(Precedence.Relational + 1);
|
|
1926
|
-
if (!body)
|
|
1940
|
+
// within the case expression, `is` is a low-precedence arm separator
|
|
1941
|
+
const prevCaseArmIsLowPrec = this._caseArmIsLowPrec;
|
|
1942
|
+
this._caseArmIsLowPrec = true;
|
|
1943
|
+
try {
|
|
1944
|
+
// parse the matched expression at a precedence higher than `is`
|
|
1945
|
+
// so the case-level `is` is left unconsumed
|
|
1946
|
+
const expr = this.parseExpr(Precedence.Relational + 1);
|
|
1947
|
+
if (!expr)
|
|
1927
1948
|
return undefined;
|
|
1928
|
-
cases
|
|
1949
|
+
const cases = new Array();
|
|
1950
|
+
while (tn.skip(Token.Is)) {
|
|
1951
|
+
const startPos = tn.tokenPos;
|
|
1952
|
+
const matcher = this._parseVarDecl(CommonFlags.Const);
|
|
1953
|
+
if (!matcher)
|
|
1954
|
+
return undefined;
|
|
1955
|
+
// SimpleVarDecl patterns are validated by the AstCompiler, which
|
|
1956
|
+
// has type info (enum-member promotion vs invalid catch-all).
|
|
1957
|
+
if (matcher.initExpr || matcher.type)
|
|
1958
|
+
return this.error(DiagnosticCode.Patterns_may_not_have_initializers_or_explicit_types, matcher.initExpr ? matcher.initExpr.range : matcher.type.range);
|
|
1959
|
+
if (!tn.skip(Token.FatArrow))
|
|
1960
|
+
return this.error(DiagnosticCode._0_expected, tn.range(), "=>");
|
|
1961
|
+
// parse the body absorbing all operators tighter than `is`; the
|
|
1962
|
+
// low-precedence `is` (see `_caseArmIsLowPrec`) stops the body
|
|
1963
|
+
// before the next case-arm's `is`
|
|
1964
|
+
const body = this.parseExpr(Precedence.CaseExpr + 1);
|
|
1965
|
+
if (!body)
|
|
1966
|
+
return undefined;
|
|
1967
|
+
cases.push(new CaseExprMatcher(matcher, body, tn.range(startPos, tn.pos)));
|
|
1968
|
+
}
|
|
1969
|
+
let wildcardCase = undefined;
|
|
1970
|
+
if (tn.skip(Token.Else)) {
|
|
1971
|
+
const wildcardStart = tn.tokenPos;
|
|
1972
|
+
// parse the body absorbing all operators tighter than `is`
|
|
1973
|
+
const body = this.parseExpr(Precedence.CaseExpr + 1);
|
|
1974
|
+
if (!body)
|
|
1975
|
+
return undefined;
|
|
1976
|
+
wildcardCase = new CaseWildcardMatcher(body, tn.range(wildcardStart));
|
|
1977
|
+
}
|
|
1978
|
+
tn.skip(Token.Semicolon); // if any
|
|
1979
|
+
const finalRange = tn.range(startPos, tn.pos);
|
|
1980
|
+
if (cases.length < 1)
|
|
1981
|
+
return this.error(DiagnosticCode.A_case_expression_must_have_at_least_one_clause, finalRange);
|
|
1982
|
+
return new CaseExpr(expr, cases, wildcardCase, finalRange);
|
|
1929
1983
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
const wildcardStart = tn.tokenPos;
|
|
1933
|
-
// parse body at precedence higher than `is` so the next case-arm's
|
|
1934
|
-
// `is` is left unconsumed (otherwise `is` would be greedily parsed
|
|
1935
|
-
// as the binary `is` operator inside the body)
|
|
1936
|
-
const body = this.parseExpr(Precedence.Relational + 1);
|
|
1937
|
-
if (!body)
|
|
1938
|
-
return undefined;
|
|
1939
|
-
wildcardCase = new CaseWildcardMatcher(body, tn.range(wildcardStart));
|
|
1984
|
+
finally {
|
|
1985
|
+
this._caseArmIsLowPrec = prevCaseArmIsLowPrec;
|
|
1940
1986
|
}
|
|
1941
|
-
tn.skip(Token.Semicolon); // if any
|
|
1942
|
-
const finalRange = tn.range(startPos, tn.pos);
|
|
1943
|
-
if (cases.length < 1)
|
|
1944
|
-
return this.error(DiagnosticCode.A_case_expression_must_have_at_least_one_clause, finalRange);
|
|
1945
|
-
return new CaseExpr(expr, cases, wildcardCase, finalRange);
|
|
1946
1987
|
}
|
|
1947
1988
|
parseFunctionExpr() {
|
|
1948
1989
|
const tn = this.tn;
|
|
@@ -2640,7 +2681,10 @@ export class Parser extends DiagnosticEmitter {
|
|
|
2640
2681
|
const tn = this.tn;
|
|
2641
2682
|
// at 'match': Expression '{' MatchStmtCase* '}' ';'
|
|
2642
2683
|
const startPos = tn.pos;
|
|
2684
|
+
// the `{` after the subject opens the match block, not a struct literal
|
|
2685
|
+
this._noStructLiteral = true;
|
|
2643
2686
|
const expr = this.parseExpr();
|
|
2687
|
+
this._noStructLiteral = false;
|
|
2644
2688
|
if (!expr)
|
|
2645
2689
|
return this.error(DiagnosticCode.Expression_expected, tn.range(startPos - 5, startPos));
|
|
2646
2690
|
if (!tn.skip(Token.OpenBrace))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const COMPILER_VERSION = "0.3.
|
|
1
|
+
export declare const COMPILER_VERSION = "0.3.3";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// This file is auto-generated by scripts/genVersion.js. Do not edit.
|
|
2
|
-
export const COMPILER_VERSION = "0.3.
|
|
2
|
+
export const COMPILER_VERSION = "0.3.3";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harmoniclabs/pebble",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "A simple, yet rock solid, functional language with an imperative bias, targeting UPLC",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@harmoniclabs/buildooor": "^0.2.6",
|
|
69
69
|
"@types/jest": "^28.1.4",
|
|
70
70
|
"@types/node": "^18.14.6",
|
|
71
|
-
"jest": "^
|
|
71
|
+
"jest": "^30.4.2",
|
|
72
72
|
"jest-environment-jsdom": "^30.3.0",
|
|
73
73
|
"tsc-alias": "^1.7.1",
|
|
74
74
|
"typescript": "^4.6.3"
|