@harmoniclabs/pebble 0.1.3-dev0 → 0.1.3-dev2
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 +4 -6
- package/dist/IR/toUPLC/compileIRToUPLC.js +2 -0
- package/dist/IR/toUPLC/subRoutines/performUplcOptimizationsAndReturnRoot/ensureProperlyForcedBuiltinsAndReturnRoot.d.ts +2 -0
- package/dist/IR/toUPLC/subRoutines/performUplcOptimizationsAndReturnRoot/ensureProperlyForcedBuiltinsAndReturnRoot.js +163 -0
- package/dist/IR/toUPLC/subRoutines/performUplcOptimizationsAndReturnRoot/performUplcOptimizationsAndReturnRoot.js +12 -2
- package/dist/IR/tree_utils/intToUtf8Bytes.d.ts +8 -0
- package/dist/IR/tree_utils/intToUtf8Bytes.js +49 -0
- package/dist/ast/nodes/statements/PebbleStmt.d.ts +2 -1
- package/dist/ast/nodes/statements/PebbleStmt.js +2 -0
- package/dist/ast/nodes/statements/TraceStmt.d.ts +11 -0
- package/dist/ast/nodes/statements/TraceStmt.js +10 -0
- package/dist/compiler/AstCompiler/AstCompiler.d.ts +11 -0
- package/dist/compiler/AstCompiler/AstCompiler.js +185 -0
- package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +8 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileStatement.js +4 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileTraceStmt.d.ts +4 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileTraceStmt.js +15 -0
- package/dist/compiler/Compiler.d.ts +5 -0
- package/dist/compiler/Compiler.js +26 -2
- package/dist/compiler/TirCompiler/expressify/determineReassignedVariablesAndReturn.js +5 -3
- package/dist/compiler/TirCompiler/expressify/expressify.js +8 -0
- package/dist/compiler/TirCompiler/expressify/expressifyIfBranch.js +2 -0
- package/dist/compiler/TirCompiler/expressify/expressifyVars.js +10 -1
- package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
- package/dist/compiler/tir/expressions/TirExpr.js +2 -0
- package/dist/compiler/tir/expressions/TirNativeFunc.js +3 -3
- package/dist/compiler/tir/expressions/TirTraceExpr.d.ts +22 -0
- package/dist/compiler/tir/expressions/TirTraceExpr.js +53 -0
- package/dist/compiler/tir/expressions/TirTypeConversionExpr.js +6 -0
- package/dist/compiler/tir/statements/TirStmt.d.ts +2 -1
- package/dist/compiler/tir/statements/TirStmt.js +2 -0
- package/dist/compiler/tir/statements/TirTraceStmt.d.ts +15 -0
- package/dist/compiler/tir/statements/TirTraceStmt.js +20 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/parser/Parser.d.ts +2 -0
- package/dist/parser/Parser.js +15 -0
- package/dist/tokenizer/Token.d.ts +82 -81
- package/dist/tokenizer/Token.js +82 -81
- package/dist/tokenizer/utils/tokenFromKeyword.js +2 -0
- package/dist/utils/array/keepSortedArrInplace.js +15 -0
- package/package.json +7 -10
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Pair } from "@harmoniclabs/pair";
|
|
2
1
|
import { isData, dataToCbor } from "@harmoniclabs/plutus-data";
|
|
3
2
|
import { fromUtf8, toHex } from "@harmoniclabs/uint8array-utils";
|
|
4
3
|
import { BasePlutsError } from "../../utils/BasePlutsError.js";
|
|
@@ -11,7 +10,7 @@ import { TirAliasType } from "../../compiler/tir/types/TirAliasType.js";
|
|
|
11
10
|
import { TirDataStructType, TirSoPStructType } from "../../compiler/tir/types/TirStructType.js";
|
|
12
11
|
import { getListTypeArg } from "../../compiler/tir/types/utils/getListTypeArg.js";
|
|
13
12
|
import { TirTypeParam } from "../../compiler/tir/types/TirTypeParam.js";
|
|
14
|
-
import { constT, UPLCConst } from "@harmoniclabs/uplc";
|
|
13
|
+
import { constT, isPair, UPLCConst } from "@harmoniclabs/uplc";
|
|
15
14
|
import { TirPairDataT, TirUnConstrDataResultT } from "../../compiler/tir/types/TirNativeType/index.js";
|
|
16
15
|
import { TirBoolT } from "../../compiler/tir/types/TirNativeType/native/bool.js";
|
|
17
16
|
import { TirBytesT } from "../../compiler/tir/types/TirNativeType/native/bytes.js";
|
|
@@ -25,10 +24,9 @@ import { TirSopOptT } from "../../compiler/tir/types/TirNativeType/native/Option
|
|
|
25
24
|
import { TirStringT } from "../../compiler/tir/types/TirNativeType/native/string.js";
|
|
26
25
|
import { TirVoidT } from "../../compiler/tir/types/TirNativeType/native/void.js";
|
|
27
26
|
import { hashIrData, isIRHash } from "../IRHash.js";
|
|
28
|
-
import { ByteString } from "@harmoniclabs/bytestring";
|
|
29
27
|
import { UPLCFlatUtils } from "../../utils/UPLCFlatUtils/index.js";
|
|
30
28
|
export function isIRConstPair(value) {
|
|
31
|
-
return (value
|
|
29
|
+
return (isPair(value) &&
|
|
32
30
|
isIRConstValue(value.fst) &&
|
|
33
31
|
isIRConstValue(value.snd));
|
|
34
32
|
}
|
|
@@ -65,7 +63,7 @@ export class IRConst {
|
|
|
65
63
|
const type = getUnaliased(this.type);
|
|
66
64
|
if (type instanceof TirBytesT && this.value instanceof Uint8Array) {
|
|
67
65
|
// make a copy to prevent external mutation
|
|
68
|
-
return new UPLCConst(tirTypeToUplcType(type),
|
|
66
|
+
return new UPLCConst(tirTypeToUplcType(type), this.value);
|
|
69
67
|
}
|
|
70
68
|
return new UPLCConst(tirTypeToUplcType(type), this.value);
|
|
71
69
|
}
|
|
@@ -215,7 +213,7 @@ function serializeIRConstValue(value, type) {
|
|
|
215
213
|
if (type instanceof TirDataT
|
|
216
214
|
|| type instanceof TirDataOptT
|
|
217
215
|
|| type instanceof TirDataStructType)
|
|
218
|
-
return dataToCbor(value)
|
|
216
|
+
return dataToCbor(value);
|
|
219
217
|
if (type instanceof TirFuncT
|
|
220
218
|
|| type instanceof TirSopOptT
|
|
221
219
|
|| type instanceof TirSoPStructType
|
|
@@ -20,6 +20,7 @@ import { _debug_assertClosedIR } from "../utils/index.js";
|
|
|
20
20
|
import { ToUplcCtx } from "./ctx/ToUplcCtx.js";
|
|
21
21
|
import { removeUnusedVarsAndReturnRoot } from "./subRoutines/removeUnusuedVarsAndReturnRoot/removeUnusuedVarsAndReturnRoot.js";
|
|
22
22
|
import { IRRecursive } from "../IRNodes/IRRecursive.js";
|
|
23
|
+
import { ensureProperlyForcedBuiltinsAndReturnRoot } from "./subRoutines/performUplcOptimizationsAndReturnRoot/ensureProperlyForcedBuiltinsAndReturnRoot.js";
|
|
23
24
|
export function compileIRToUPLC(term, paritalOptions = defaultOptions) {
|
|
24
25
|
// most of the time we are just compiling small
|
|
25
26
|
// pre-execuded terms (hence constants)
|
|
@@ -144,6 +145,7 @@ export function compileIRToUPLC(term, paritalOptions = defaultOptions) {
|
|
|
144
145
|
// }
|
|
145
146
|
term = removeUnusedVarsAndReturnRoot(term);
|
|
146
147
|
term = performUplcOptimizationsAndReturnRoot(term, options);
|
|
148
|
+
term = ensureProperlyForcedBuiltinsAndReturnRoot(term);
|
|
147
149
|
if (options.addMarker &&
|
|
148
150
|
options.targetUplcVersion.major >= 1 &&
|
|
149
151
|
options.targetUplcVersion.minor >= 1 &&
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { IRConstr, IRNative } from "../../../IRNodes/index.js";
|
|
2
|
+
import { IRApp } from "../../../IRNodes/IRApp.js";
|
|
3
|
+
import { IRCase } from "../../../IRNodes/IRCase.js";
|
|
4
|
+
import { IRForced } from "../../../IRNodes/IRForced.js";
|
|
5
|
+
import { IRFunc } from "../../../IRNodes/IRFunc.js";
|
|
6
|
+
import { IRHoisted } from "../../../IRNodes/IRHoisted.js";
|
|
7
|
+
import { IRLetted } from "../../../IRNodes/IRLetted.js";
|
|
8
|
+
import { IRRecursive } from "../../../IRNodes/IRRecursive.js";
|
|
9
|
+
import { IRSelfCall } from "../../../IRNodes/IRSelfCall.js";
|
|
10
|
+
import { IRVar } from "../../../IRNodes/IRVar.js";
|
|
11
|
+
import { _modifyChildFromTo } from "../../_internal/_modifyChildFromTo.js";
|
|
12
|
+
import { getApplicationTerms } from "../../utils/getApplicationTerms.js";
|
|
13
|
+
import { getUnboundedVars } from "../handleLetted/groupByScope.js";
|
|
14
|
+
import { getNRequiredForces } from "@harmoniclabs/uplc";
|
|
15
|
+
export function ensureProperlyForcedBuiltinsAndReturnRoot(root) {
|
|
16
|
+
// const opts = options.uplcOptimizations;
|
|
17
|
+
// if( isDebugUplcOptimizations( opts ) ) return root;
|
|
18
|
+
// const {
|
|
19
|
+
// groupApplications,
|
|
20
|
+
// inlineSingleUse: shouldInlineSingleUse,
|
|
21
|
+
// simplifyWrappedPartialFuncApps,
|
|
22
|
+
// removeForceDelay
|
|
23
|
+
// } = opts;
|
|
24
|
+
const stack = [root];
|
|
25
|
+
let t = root;
|
|
26
|
+
while (t = stack.pop()) {
|
|
27
|
+
if (t instanceof IRNative) {
|
|
28
|
+
const expectedForces = getNRequiredForces(t.tag);
|
|
29
|
+
if (expectedForces <= 0)
|
|
30
|
+
continue;
|
|
31
|
+
let newNode = t;
|
|
32
|
+
let nDirectForces = 0;
|
|
33
|
+
// save the original parent before walking up through existing forces
|
|
34
|
+
// and before creating new IRForced wrappers (whose constructors
|
|
35
|
+
// overwrite the child's parent pointer)
|
|
36
|
+
const originalParent = t.parent;
|
|
37
|
+
while (t.parent instanceof IRForced) {
|
|
38
|
+
nDirectForces++;
|
|
39
|
+
t = t.parent;
|
|
40
|
+
}
|
|
41
|
+
if (nDirectForces === expectedForces)
|
|
42
|
+
continue;
|
|
43
|
+
// when nDirectForces > 0, t has walked up to the topmost IRForced;
|
|
44
|
+
// save its parent before wrapping overwrites parent pointers
|
|
45
|
+
const replaceInParent = nDirectForces > 0 ? t.parent : originalParent;
|
|
46
|
+
for (let i = 0; i < expectedForces - nDirectForces; i++) {
|
|
47
|
+
newNode = new IRForced(newNode);
|
|
48
|
+
}
|
|
49
|
+
if (replaceInParent)
|
|
50
|
+
_modifyChildFromTo(replaceInParent, t, newNode);
|
|
51
|
+
else
|
|
52
|
+
root = newNode;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (t instanceof IRRecursive ||
|
|
56
|
+
t instanceof IRHoisted ||
|
|
57
|
+
t instanceof IRLetted ||
|
|
58
|
+
t instanceof IRSelfCall)
|
|
59
|
+
throw new Error("Unexpected term while performing uplc optimizations");
|
|
60
|
+
stack.push(...t.children());
|
|
61
|
+
}
|
|
62
|
+
return root;
|
|
63
|
+
}
|
|
64
|
+
function isIdLike(term) {
|
|
65
|
+
return (term instanceof IRFunc &&
|
|
66
|
+
term.params.length === 1 &&
|
|
67
|
+
term.body instanceof IRVar &&
|
|
68
|
+
term.body.name === term.params[0]);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @returns either an object containing the new root, sorted args and next body
|
|
73
|
+
* or undefined if grouping wasnt possible (root is the same)
|
|
74
|
+
*/
|
|
75
|
+
function groupIndependentApplications(root) {
|
|
76
|
+
const parent = root.parent;
|
|
77
|
+
let applicaitonTerms = getApplicationTerms(root);
|
|
78
|
+
if (!applicaitonTerms)
|
|
79
|
+
return undefined;
|
|
80
|
+
let { func, args } = applicaitonTerms;
|
|
81
|
+
if (!(func instanceof IRFunc))
|
|
82
|
+
return undefined;
|
|
83
|
+
const params = func.params.slice();
|
|
84
|
+
while (true) {
|
|
85
|
+
applicaitonTerms = getApplicationTerms(func.body);
|
|
86
|
+
if (!applicaitonTerms)
|
|
87
|
+
break;
|
|
88
|
+
if (!(applicaitonTerms.func instanceof IRFunc))
|
|
89
|
+
break;
|
|
90
|
+
func = applicaitonTerms.func;
|
|
91
|
+
params.push(...func.params);
|
|
92
|
+
args.push(...applicaitonTerms.args);
|
|
93
|
+
if (params.length !== args.length)
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
const len = Math.min(params.length, args.length);
|
|
97
|
+
const finalParams = params.slice(len);
|
|
98
|
+
const finalArgs = args.slice(len);
|
|
99
|
+
params.length = len;
|
|
100
|
+
args.length = len;
|
|
101
|
+
const paramToArg = {};
|
|
102
|
+
for (let i = 0; i < len; i++) {
|
|
103
|
+
const p = params[i];
|
|
104
|
+
paramToArg[p] = args[i];
|
|
105
|
+
}
|
|
106
|
+
const globalUnbound = getUnboundedVars(root);
|
|
107
|
+
const groups = [[]];
|
|
108
|
+
for (let i = 0; i < len; i++) {
|
|
109
|
+
const p = params[i];
|
|
110
|
+
const arg = args[i];
|
|
111
|
+
const unbound = getUnboundedVars(arg, globalUnbound);
|
|
112
|
+
let highestIdx = -1;
|
|
113
|
+
for (let j = groups.length - 1; j >= 0; j--) {
|
|
114
|
+
const group = groups[j];
|
|
115
|
+
if (group.some(sym => unbound.has(sym))) {
|
|
116
|
+
highestIdx = j;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (highestIdx === groups.length - 1) {
|
|
121
|
+
groups.push([p]);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
groups[highestIdx + 1].push(p);
|
|
125
|
+
}
|
|
126
|
+
groups[groups.length - 1].push(...finalParams);
|
|
127
|
+
const sortedArgs = new Array(len);
|
|
128
|
+
const soretedParams = groups.flat();
|
|
129
|
+
for (let i = 0; i < len; i++) {
|
|
130
|
+
const p = soretedParams[i];
|
|
131
|
+
sortedArgs[i] = paramToArg[p];
|
|
132
|
+
}
|
|
133
|
+
let nextBody = func.body;
|
|
134
|
+
if (finalArgs.length > 0) {
|
|
135
|
+
if (finalArgs.length === 1) {
|
|
136
|
+
nextBody = new IRApp(nextBody, finalArgs[0]);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
nextBody = new IRCase(new IRConstr(0, finalArgs), [nextBody]);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
let newTerm = nextBody;
|
|
143
|
+
for (let i = groups.length - 1; i >= 0; i--) {
|
|
144
|
+
const group = groups[i];
|
|
145
|
+
if (group.length === 1) {
|
|
146
|
+
newTerm = new IRApp(new IRFunc(group, newTerm), paramToArg[group[0]]);
|
|
147
|
+
}
|
|
148
|
+
else if (group.length <= 0)
|
|
149
|
+
continue;
|
|
150
|
+
else {
|
|
151
|
+
newTerm = new IRCase(new IRConstr(0, group.map(p => paramToArg[p])), [new IRFunc(group, newTerm)]);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (parent)
|
|
155
|
+
_modifyChildFromTo(parent, root, newTerm);
|
|
156
|
+
else
|
|
157
|
+
root = newTerm;
|
|
158
|
+
return {
|
|
159
|
+
newRoot: !parent ? root : undefined,
|
|
160
|
+
args: sortedArgs,
|
|
161
|
+
nextBody
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -89,6 +89,10 @@ function groupIndependentApplications(root) {
|
|
|
89
89
|
if (!(func instanceof IRFunc))
|
|
90
90
|
return undefined;
|
|
91
91
|
const params = func.params.slice();
|
|
92
|
+
// Separate excess outer args: getApplicationTerms may return more args
|
|
93
|
+
// than func.params (e.g. `(λp. body)(arg1)(arg2)` → 2 args, 1 param).
|
|
94
|
+
// Excess args are trailing applications applied after all param bindings.
|
|
95
|
+
const trailingArgs = args.splice(params.length);
|
|
92
96
|
while (true) {
|
|
93
97
|
applicaitonTerms = getApplicationTerms(func.body);
|
|
94
98
|
if (!applicaitonTerms)
|
|
@@ -96,14 +100,20 @@ function groupIndependentApplications(root) {
|
|
|
96
100
|
if (!(applicaitonTerms.func instanceof IRFunc))
|
|
97
101
|
break;
|
|
98
102
|
func = applicaitonTerms.func;
|
|
103
|
+
const nNewParams = func.params.length;
|
|
104
|
+
const innerArgs = applicaitonTerms.args;
|
|
99
105
|
params.push(...func.params);
|
|
100
|
-
args.push(...
|
|
106
|
+
args.push(...innerArgs.slice(0, nNewParams));
|
|
107
|
+
// Inner excess args are applied before outer excess args
|
|
108
|
+
if (innerArgs.length > nNewParams) {
|
|
109
|
+
trailingArgs.unshift(...innerArgs.slice(nNewParams));
|
|
110
|
+
}
|
|
101
111
|
if (params.length !== args.length)
|
|
102
112
|
break;
|
|
103
113
|
}
|
|
104
114
|
const len = Math.min(params.length, args.length);
|
|
105
115
|
const finalParams = params.slice(len);
|
|
106
|
-
const finalArgs = args.slice(len);
|
|
116
|
+
const finalArgs = [...args.slice(len), ...trailingArgs];
|
|
107
117
|
params.length = len;
|
|
108
118
|
args.length = len;
|
|
109
119
|
const paramToArg = {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IRHoisted } from "../IRNodes/IRHoisted.js";
|
|
2
|
+
/**
|
|
3
|
+
* Hoisted IR term: `int -> bytes`
|
|
4
|
+
*
|
|
5
|
+
* Converts an integer to its decimal UTF-8 byte representation.
|
|
6
|
+
* Handles negative numbers by prepending '-' (0x2d).
|
|
7
|
+
*/
|
|
8
|
+
export declare const hoisted_intToUtf8Bytes: IRHoisted;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { IRApp, _ir_apps } from "../IRNodes/IRApp.js";
|
|
2
|
+
import { IRConst } from "../IRNodes/IRConst.js";
|
|
3
|
+
import { IRFunc } from "../IRNodes/IRFunc.js";
|
|
4
|
+
import { IRHoisted } from "../IRNodes/IRHoisted.js";
|
|
5
|
+
import { IRNative } from "../IRNodes/IRNative/index.js";
|
|
6
|
+
import { IRRecursive } from "../IRNodes/IRRecursive.js";
|
|
7
|
+
import { IRSelfCall } from "../IRNodes/IRSelfCall.js";
|
|
8
|
+
import { IRVar } from "../IRNodes/IRVar.js";
|
|
9
|
+
import { _ir_lazyIfThenElse } from "./_ir_lazyIfThenElse.js";
|
|
10
|
+
// digitToBytes(n) = consByteString( addInteger(n, 48), #"" )
|
|
11
|
+
// positiveIntToBs = fix \self \n ->
|
|
12
|
+
// ifThenElse (lessThanInteger n 10)
|
|
13
|
+
// (consByteString (addInteger n 48) #"")
|
|
14
|
+
// (appendByteString (self (divideInteger n 10)) (consByteString (addInteger (modInteger n 10) 48) #""))
|
|
15
|
+
// intToUtf8Bytes(n) =
|
|
16
|
+
// ifThenElse (lessThanEqualInteger 0 n)
|
|
17
|
+
// (positiveIntToBs n)
|
|
18
|
+
// (appendByteString (consByteString 45 #"") (positiveIntToBs (subtractInteger 0 n)))
|
|
19
|
+
const self_sym = Symbol("intToUtf8Bytes_self");
|
|
20
|
+
const n_sym = Symbol("intToUtf8Bytes_n");
|
|
21
|
+
const outer_n_sym = Symbol("intToUtf8Bytes_outer_n");
|
|
22
|
+
const emptyBs = IRConst.bytes(new Uint8Array(0));
|
|
23
|
+
function digitToBytes(n) {
|
|
24
|
+
return _ir_apps(IRNative.consByteString, _ir_apps(IRNative.addInteger, n, IRConst.int(48)), emptyBs.clone());
|
|
25
|
+
}
|
|
26
|
+
const hoisted_positiveIntToBs = new IRHoisted(new IRRecursive(self_sym, new IRFunc([n_sym], _ir_lazyIfThenElse(
|
|
27
|
+
// condition: n < 10
|
|
28
|
+
_ir_apps(IRNative.lessThanInteger, new IRVar(n_sym), IRConst.int(10)),
|
|
29
|
+
// then: digitToBytes(n)
|
|
30
|
+
digitToBytes(new IRVar(n_sym)),
|
|
31
|
+
// else: appendByteString( self(n / 10), digitToBytes(n % 10) )
|
|
32
|
+
_ir_apps(IRNative.appendByteString, new IRApp(new IRSelfCall(self_sym), _ir_apps(IRNative.divideInteger, new IRVar(n_sym), IRConst.int(10))), digitToBytes(_ir_apps(IRNative.modInteger, new IRVar(n_sym), IRConst.int(10))))))));
|
|
33
|
+
hoisted_positiveIntToBs.hash;
|
|
34
|
+
/**
|
|
35
|
+
* Hoisted IR term: `int -> bytes`
|
|
36
|
+
*
|
|
37
|
+
* Converts an integer to its decimal UTF-8 byte representation.
|
|
38
|
+
* Handles negative numbers by prepending '-' (0x2d).
|
|
39
|
+
*/
|
|
40
|
+
export const hoisted_intToUtf8Bytes = new IRHoisted(new IRFunc([outer_n_sym], _ir_lazyIfThenElse(
|
|
41
|
+
// condition: 0 <= n
|
|
42
|
+
_ir_apps(IRNative.lessThanEqualInteger, IRConst.int(0), new IRVar(outer_n_sym)),
|
|
43
|
+
// then: positiveIntToBs(n)
|
|
44
|
+
_ir_apps(hoisted_positiveIntToBs.clone(), new IRVar(outer_n_sym)),
|
|
45
|
+
// else: appendByteString( consByteString(45, #""), positiveIntToBs(0 - n) )
|
|
46
|
+
_ir_apps(IRNative.appendByteString,
|
|
47
|
+
// "-" as bytes
|
|
48
|
+
_ir_apps(IRNative.consByteString, IRConst.int(45), emptyBs.clone()), _ir_apps(hoisted_positiveIntToBs.clone(), _ir_apps(IRNative.subtractInteger, IRConst.int(0), new IRVar(outer_n_sym)))))));
|
|
49
|
+
hoisted_intToUtf8Bytes.hash;
|
|
@@ -24,7 +24,8 @@ import { FuncDecl } from "./declarations/FuncDecl.js";
|
|
|
24
24
|
import { ExportStmt } from "./ExportStmt.js";
|
|
25
25
|
import { InterfaceDecl } from "./declarations/InterfaceDecl.js";
|
|
26
26
|
import { ContractDecl } from "./declarations/ContractDecl.js";
|
|
27
|
+
import { TraceStmt } from "./TraceStmt.js";
|
|
27
28
|
export type TopLevelStmt = EmptyStmt | VarStmt | PebbleTypeDecl | InterfaceDecl | FuncDecl | TestStmt | ExportStmt | ExportStarStmt | ExportImportStmt | ImportStmt | ImportStarStmt | TypeImplementsStmt | UsingStmt | ContractDecl;
|
|
28
29
|
export declare function isTopLevelStmt(stmt: any): stmt is TopLevelStmt;
|
|
29
|
-
export type BodyStmt = IfStmt | VarStmt | ForStmt | ForOfStmt | WhileStmt | ReturnStmt | BlockStmt | BreakStmt | ContinueStmt | EmptyStmt | FailStmt | AssertStmt | MatchStmt | AssignmentStmt | UsingStmt;
|
|
30
|
+
export type BodyStmt = IfStmt | VarStmt | ForStmt | ForOfStmt | WhileStmt | ReturnStmt | BlockStmt | BreakStmt | ContinueStmt | EmptyStmt | FailStmt | AssertStmt | TraceStmt | MatchStmt | AssignmentStmt | UsingStmt;
|
|
30
31
|
export declare function isBodyStmt(stmt: any): stmt is BodyStmt;
|
|
@@ -26,6 +26,7 @@ import { UsingStmt } from "./UsingStmt.js";
|
|
|
26
26
|
import { FuncDecl } from "./declarations/FuncDecl.js";
|
|
27
27
|
import { ExportStmt } from "./ExportStmt.js";
|
|
28
28
|
import { ContractDecl } from "./declarations/ContractDecl.js";
|
|
29
|
+
import { TraceStmt } from "./TraceStmt.js";
|
|
29
30
|
export function isTopLevelStmt(stmt) {
|
|
30
31
|
return (stmt instanceof EmptyStmt
|
|
31
32
|
|| stmt instanceof VarStmt
|
|
@@ -55,6 +56,7 @@ export function isBodyStmt(stmt) {
|
|
|
55
56
|
|| stmt instanceof EmptyStmt
|
|
56
57
|
|| stmt instanceof FailStmt
|
|
57
58
|
|| stmt instanceof AssertStmt
|
|
59
|
+
|| stmt instanceof TraceStmt
|
|
58
60
|
|| stmt instanceof MatchStmt
|
|
59
61
|
|| isAssignmentStmt(stmt)
|
|
60
62
|
// || stmt instanceof ExprStmt
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SourceRange } from "../../Source/SourceRange.js";
|
|
2
|
+
import { PebbleExpr } from "../expr/PebbleExpr.js";
|
|
3
|
+
import { HasSourceRange } from "../HasSourceRange.js";
|
|
4
|
+
export declare class TraceStmt implements HasSourceRange {
|
|
5
|
+
/** expression to trace (must be string) */
|
|
6
|
+
expr: PebbleExpr;
|
|
7
|
+
readonly range: SourceRange;
|
|
8
|
+
constructor(
|
|
9
|
+
/** expression to trace (must be string) */
|
|
10
|
+
expr: PebbleExpr, range: SourceRange);
|
|
11
|
+
}
|
|
@@ -43,6 +43,17 @@ export declare class AstCompiler extends DiagnosticEmitter {
|
|
|
43
43
|
constructor(cfg: CompilerOptions, io?: CompilerIoApi, diagnostics?: DiagnosticMessage[]);
|
|
44
44
|
private _isExporting;
|
|
45
45
|
export(funcName: string, modulePath?: string): Promise<TypedProgram>;
|
|
46
|
+
/**
|
|
47
|
+
* Parses the entry file, wraps all top-level statements
|
|
48
|
+
* (except functions, contracts, type declarations, imports, and exports)
|
|
49
|
+
* into a synthetic function, then compiles via export.
|
|
50
|
+
*
|
|
51
|
+
* The wrapping is done at the source text level (before parsing)
|
|
52
|
+
* so that statements which are not parseable at the top level
|
|
53
|
+
* (e.g. for loops, assignments, trace calls) become valid
|
|
54
|
+
* inside a function body.
|
|
55
|
+
*/
|
|
56
|
+
run(): Promise<TypedProgram>;
|
|
46
57
|
/**
|
|
47
58
|
* compiles the entry file specified in the config
|
|
48
59
|
*
|
|
@@ -104,6 +104,38 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
104
104
|
this.program.contractTirFuncName = funcName;
|
|
105
105
|
return await this.compile();
|
|
106
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Parses the entry file, wraps all top-level statements
|
|
109
|
+
* (except functions, contracts, type declarations, imports, and exports)
|
|
110
|
+
* into a synthetic function, then compiles via export.
|
|
111
|
+
*
|
|
112
|
+
* The wrapping is done at the source text level (before parsing)
|
|
113
|
+
* so that statements which are not parseable at the top level
|
|
114
|
+
* (e.g. for loops, assignments, trace calls) become valid
|
|
115
|
+
* inside a function body.
|
|
116
|
+
*/
|
|
117
|
+
async run() {
|
|
118
|
+
const RUN_FUNC_NAME = "__pebble_run__";
|
|
119
|
+
this._isExporting = true;
|
|
120
|
+
this.program.contractTirFuncName = RUN_FUNC_NAME;
|
|
121
|
+
const filePath = this.cfg.entry;
|
|
122
|
+
if (!filePath) {
|
|
123
|
+
this.error(DiagnosticCode.File_0_not_found, undefined, this.cfg.entry);
|
|
124
|
+
throw new Error("entry file not found");
|
|
125
|
+
}
|
|
126
|
+
if (!this.io.exsistSync(filePath))
|
|
127
|
+
throw new Error("AstCompiler.run: entry file does not exist: " + filePath);
|
|
128
|
+
const src = await this.getAbsoulteProjPathSource(filePath);
|
|
129
|
+
if (!src)
|
|
130
|
+
throw new Error("AstCompiler.run: could not read source: " + filePath);
|
|
131
|
+
// 1) Rewrite the source text: wrap non-declaration statements in a function
|
|
132
|
+
src.text = _wrapSourceTextForRun(src.text, RUN_FUNC_NAME);
|
|
133
|
+
// update range to match the new text length
|
|
134
|
+
src.range.end = src.text.length;
|
|
135
|
+
// 2) Use the normal compilation pipeline (parse + semantic analysis)
|
|
136
|
+
// which now sees the entry source as having a top-level function
|
|
137
|
+
return await this.compile();
|
|
138
|
+
}
|
|
107
139
|
/**
|
|
108
140
|
* compiles the entry file specified in the config
|
|
109
141
|
*
|
|
@@ -676,3 +708,156 @@ function isImportStmtLike(stmt) {
|
|
|
676
708
|
|| stmt instanceof ImportStarStmt
|
|
677
709
|
|| stmt instanceof ExportStarStmt);
|
|
678
710
|
}
|
|
711
|
+
/** Keywords that start a top-level declaration (kept outside the run wrapper). */
|
|
712
|
+
const _declKeywords = new Set([
|
|
713
|
+
"import",
|
|
714
|
+
"export",
|
|
715
|
+
"function",
|
|
716
|
+
"struct",
|
|
717
|
+
"enum",
|
|
718
|
+
"type",
|
|
719
|
+
"interface",
|
|
720
|
+
"contract",
|
|
721
|
+
"data",
|
|
722
|
+
"runtime",
|
|
723
|
+
]);
|
|
724
|
+
/**
|
|
725
|
+
* Splits source text into declaration blocks and body blocks
|
|
726
|
+
* using a simple character scanner, then wraps body blocks
|
|
727
|
+
* in a `function <funcName>(): void { ... }`.
|
|
728
|
+
*
|
|
729
|
+
* Declaration keywords (import, export, function, struct, enum, type, interface,
|
|
730
|
+
* contract, data, runtime) are recognized at the start of each top-level statement
|
|
731
|
+
* and kept outside the wrapper. Everything else goes inside the wrapper function.
|
|
732
|
+
*/
|
|
733
|
+
function _wrapSourceTextForRun(text, funcName) {
|
|
734
|
+
const len = text.length;
|
|
735
|
+
const ranges = [];
|
|
736
|
+
let pos = 0;
|
|
737
|
+
while (pos < len) {
|
|
738
|
+
// skip whitespace
|
|
739
|
+
while (pos < len && _isWhitespace(text.charCodeAt(pos)))
|
|
740
|
+
pos++;
|
|
741
|
+
if (pos >= len)
|
|
742
|
+
break;
|
|
743
|
+
const stmtStart = pos;
|
|
744
|
+
// read the first word to determine if this is a declaration
|
|
745
|
+
const word = _readWord(text, pos);
|
|
746
|
+
const isDecl = _declKeywords.has(word);
|
|
747
|
+
// find the end of this statement:
|
|
748
|
+
// track brace/paren depth, respect strings and comments
|
|
749
|
+
let braceDepth = 0;
|
|
750
|
+
let parenDepth = 0;
|
|
751
|
+
let ended = false;
|
|
752
|
+
while (pos < len && !ended) {
|
|
753
|
+
const ch = text.charCodeAt(pos);
|
|
754
|
+
// single-line comment
|
|
755
|
+
if (ch === 0x2F /* / */ && pos + 1 < len && text.charCodeAt(pos + 1) === 0x2F /* / */) {
|
|
756
|
+
pos += 2;
|
|
757
|
+
while (pos < len && text.charCodeAt(pos) !== 0x0A /* \n */)
|
|
758
|
+
pos++;
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
// multi-line comment
|
|
762
|
+
if (ch === 0x2F /* / */ && pos + 1 < len && text.charCodeAt(pos + 1) === 0x2A /* * */) {
|
|
763
|
+
pos += 2;
|
|
764
|
+
while (pos < len && !(text.charCodeAt(pos) === 0x2A && pos + 1 < len && text.charCodeAt(pos + 1) === 0x2F))
|
|
765
|
+
pos++;
|
|
766
|
+
pos += 2; // skip */
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
// string literals
|
|
770
|
+
if (ch === 0x22 /* " */ || ch === 0x27 /* ' */ || ch === 0x60 /* ` */) {
|
|
771
|
+
pos++;
|
|
772
|
+
while (pos < len) {
|
|
773
|
+
const sc = text.charCodeAt(pos);
|
|
774
|
+
if (sc === 0x5C /* \ */) {
|
|
775
|
+
pos += 2;
|
|
776
|
+
continue;
|
|
777
|
+
} // escaped char
|
|
778
|
+
if (sc === ch) {
|
|
779
|
+
pos++;
|
|
780
|
+
break;
|
|
781
|
+
}
|
|
782
|
+
pos++;
|
|
783
|
+
}
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
if (ch === 0x28 /* ( */) {
|
|
787
|
+
parenDepth++;
|
|
788
|
+
pos++;
|
|
789
|
+
}
|
|
790
|
+
else if (ch === 0x29 /* ) */) {
|
|
791
|
+
parenDepth--;
|
|
792
|
+
pos++;
|
|
793
|
+
}
|
|
794
|
+
else if (ch === 0x7B /* { */) {
|
|
795
|
+
braceDepth++;
|
|
796
|
+
pos++;
|
|
797
|
+
}
|
|
798
|
+
else if (ch === 0x7D /* } */) {
|
|
799
|
+
braceDepth--;
|
|
800
|
+
pos++;
|
|
801
|
+
if (braceDepth <= 0 && parenDepth <= 0) {
|
|
802
|
+
// consume optional trailing semicolon
|
|
803
|
+
let p2 = pos;
|
|
804
|
+
while (p2 < len && _isWhitespace(text.charCodeAt(p2)))
|
|
805
|
+
p2++;
|
|
806
|
+
if (p2 < len && text.charCodeAt(p2) === 0x3B /* ; */)
|
|
807
|
+
pos = p2 + 1;
|
|
808
|
+
ended = true;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
else if (ch === 0x3B /* ; */ && braceDepth === 0 && parenDepth === 0) {
|
|
812
|
+
pos++;
|
|
813
|
+
ended = true;
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
pos++;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
const stmtEnd = pos;
|
|
820
|
+
// skip empty ranges
|
|
821
|
+
if (stmtEnd > stmtStart) {
|
|
822
|
+
ranges.push({ start: stmtStart, end: stmtEnd, isDecl });
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
// If there are no body ranges, nothing to wrap
|
|
826
|
+
if (ranges.every(r => r.isDecl)) {
|
|
827
|
+
return text;
|
|
828
|
+
}
|
|
829
|
+
// Build the new source text
|
|
830
|
+
const declarations = [];
|
|
831
|
+
const bodyParts = [];
|
|
832
|
+
for (const r of ranges) {
|
|
833
|
+
const slice = text.substring(r.start, r.end);
|
|
834
|
+
if (r.isDecl)
|
|
835
|
+
declarations.push(slice);
|
|
836
|
+
else
|
|
837
|
+
bodyParts.push(slice);
|
|
838
|
+
}
|
|
839
|
+
return (declarations.join("\n") +
|
|
840
|
+
(declarations.length > 0 ? "\n" : "") +
|
|
841
|
+
"function " + funcName + "(): void {\n" +
|
|
842
|
+
bodyParts.join("\n") + "\n" +
|
|
843
|
+
"}\n");
|
|
844
|
+
}
|
|
845
|
+
function _isWhitespace(ch) {
|
|
846
|
+
return ch === 0x20 || ch === 0x09 || ch === 0x0A || ch === 0x0D;
|
|
847
|
+
}
|
|
848
|
+
function _readWord(text, pos) {
|
|
849
|
+
const start = pos;
|
|
850
|
+
const len = text.length;
|
|
851
|
+
while (pos < len) {
|
|
852
|
+
const ch = text.charCodeAt(pos);
|
|
853
|
+
if ((ch >= 0x61 && ch <= 0x7A) || // a-z
|
|
854
|
+
(ch >= 0x41 && ch <= 0x5A) || // A-Z
|
|
855
|
+
(ch >= 0x30 && ch <= 0x39) || // 0-9
|
|
856
|
+
ch === 0x5F // _
|
|
857
|
+
)
|
|
858
|
+
pos++;
|
|
859
|
+
else
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
return text.substring(start, pos);
|
|
863
|
+
}
|
|
@@ -40,6 +40,7 @@ import { ForStmt } from "../../../../ast/nodes/statements/ForStmt.js";
|
|
|
40
40
|
import { IfStmt } from "../../../../ast/nodes/statements/IfStmt.js";
|
|
41
41
|
import { MatchStmtCase, MatchStmt, MatchStmtElseCase } from "../../../../ast/nodes/statements/MatchStmt.js";
|
|
42
42
|
import { ReturnStmt } from "../../../../ast/nodes/statements/ReturnStmt.js";
|
|
43
|
+
import { TraceStmt } from "../../../../ast/nodes/statements/TraceStmt.js";
|
|
43
44
|
import { UsingStmt } from "../../../../ast/nodes/statements/UsingStmt.js";
|
|
44
45
|
import { VarStmt } from "../../../../ast/nodes/statements/VarStmt.js";
|
|
45
46
|
import { WhileStmt } from "../../../../ast/nodes/statements/WhileStmt.js";
|
|
@@ -589,6 +590,13 @@ function _getMatchedRedeemerBlockStatements(compiler, stmts, paramsInternalNames
|
|
|
589
590
|
stmt.body = new BlockStmt(nextBodyStmts, stmt.body.range);
|
|
590
591
|
continue;
|
|
591
592
|
}
|
|
593
|
+
if (stmt instanceof TraceStmt) {
|
|
594
|
+
const newExpr = _exprReplaceParamsAndAssertNoLitContext(compiler, stmt.expr, paramsInternalNamesMap, renamedVariables);
|
|
595
|
+
if (!newExpr)
|
|
596
|
+
return undefined;
|
|
597
|
+
stmt.expr = newExpr;
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
592
600
|
const tsEnsureExhaustiveCheck = stmt;
|
|
593
601
|
console.error(stmt);
|
|
594
602
|
throw new Error("unreachable::_getMatchedRedeemerBlockStatements::stmt::");
|
|
@@ -6,6 +6,7 @@ import { ContinueStmt } from "../../../../ast/nodes/statements/ContinueStmt.js";
|
|
|
6
6
|
import { isVarDecl } from "../../../../ast/nodes/statements/declarations/VarDecl/VarDecl.js";
|
|
7
7
|
import { EmptyStmt } from "../../../../ast/nodes/statements/EmptyStmt.js";
|
|
8
8
|
import { FailStmt } from "../../../../ast/nodes/statements/FailStmt.js";
|
|
9
|
+
import { TraceStmt } from "../../../../ast/nodes/statements/TraceStmt.js";
|
|
9
10
|
import { ForOfStmt } from "../../../../ast/nodes/statements/ForOfStmt.js";
|
|
10
11
|
import { ForStmt } from "../../../../ast/nodes/statements/ForStmt.js";
|
|
11
12
|
import { IfStmt } from "../../../../ast/nodes/statements/IfStmt.js";
|
|
@@ -20,6 +21,7 @@ import { _compileBlockStmt } from "./_compileBlockStmt.js";
|
|
|
20
21
|
import { _compileBreakStmt } from "./_compileBreakStmt.js";
|
|
21
22
|
import { _compileContinueStmt } from "./_compileContinueStmt.js";
|
|
22
23
|
import { _compileFailStmt } from "./_compileFailStmt.js";
|
|
24
|
+
import { _compileTraceStmt } from "./_compileTraceStmt.js";
|
|
23
25
|
import { _compileForOfStmt } from "./_compileForOfStmt.js";
|
|
24
26
|
import { _compileForStmt } from "./_compileForStmt.js";
|
|
25
27
|
import { _compileIfStmt } from "./_compileIfStmt.js";
|
|
@@ -62,6 +64,8 @@ export function _compileStatement(ctx, stmt) {
|
|
|
62
64
|
return _compileFailStmt(ctx, stmt);
|
|
63
65
|
if (stmt instanceof AssertStmt)
|
|
64
66
|
return _compileAssertStmt(ctx, stmt);
|
|
67
|
+
if (stmt instanceof TraceStmt)
|
|
68
|
+
return _compileTraceStmt(ctx, stmt);
|
|
65
69
|
// if( stmt instanceof TestStmt ) return _compileTestStmt( ctx, stmt );
|
|
66
70
|
if (stmt instanceof MatchStmt)
|
|
67
71
|
return _compileMatchStmt(ctx, stmt);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TraceStmt } from "../../../../ast/nodes/statements/TraceStmt.js";
|
|
2
|
+
import { TirTraceStmt } from "../../../tir/statements/TirTraceStmt.js";
|
|
3
|
+
import { AstCompilationCtx } from "../../AstCompilationCtx.js";
|
|
4
|
+
export declare function _compileTraceStmt(ctx: AstCompilationCtx, stmt: TraceStmt): [TirTraceStmt] | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DiagnosticCode } from "../../../../diagnostics/diagnosticMessages.generated.js";
|
|
2
|
+
import { TirTraceStmt } from "../../../tir/statements/TirTraceStmt.js";
|
|
3
|
+
import { canAssignTo } from "../../../tir/types/utils/canAssignTo.js";
|
|
4
|
+
import { _compileExpr } from "../exprs/_compileExpr.js";
|
|
5
|
+
export function _compileTraceStmt(ctx, stmt) {
|
|
6
|
+
const bytes_t = ctx.program.stdTypes.bytes;
|
|
7
|
+
const int_t = ctx.program.stdTypes.int;
|
|
8
|
+
let expr = _compileExpr(ctx, stmt.expr, undefined);
|
|
9
|
+
if (!expr)
|
|
10
|
+
return undefined;
|
|
11
|
+
if (!canAssignTo(expr.type, bytes_t) &&
|
|
12
|
+
!canAssignTo(expr.type, int_t))
|
|
13
|
+
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, stmt.expr.range, expr.type.toString(), "bytes | int");
|
|
14
|
+
return [new TirTraceStmt(expr, stmt.range)];
|
|
15
|
+
}
|