@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.
Files changed (42) hide show
  1. package/dist/IR/IRNodes/IRConst.js +4 -6
  2. package/dist/IR/toUPLC/compileIRToUPLC.js +2 -0
  3. package/dist/IR/toUPLC/subRoutines/performUplcOptimizationsAndReturnRoot/ensureProperlyForcedBuiltinsAndReturnRoot.d.ts +2 -0
  4. package/dist/IR/toUPLC/subRoutines/performUplcOptimizationsAndReturnRoot/ensureProperlyForcedBuiltinsAndReturnRoot.js +163 -0
  5. package/dist/IR/toUPLC/subRoutines/performUplcOptimizationsAndReturnRoot/performUplcOptimizationsAndReturnRoot.js +12 -2
  6. package/dist/IR/tree_utils/intToUtf8Bytes.d.ts +8 -0
  7. package/dist/IR/tree_utils/intToUtf8Bytes.js +49 -0
  8. package/dist/ast/nodes/statements/PebbleStmt.d.ts +2 -1
  9. package/dist/ast/nodes/statements/PebbleStmt.js +2 -0
  10. package/dist/ast/nodes/statements/TraceStmt.d.ts +11 -0
  11. package/dist/ast/nodes/statements/TraceStmt.js +10 -0
  12. package/dist/compiler/AstCompiler/AstCompiler.d.ts +11 -0
  13. package/dist/compiler/AstCompiler/AstCompiler.js +185 -0
  14. package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +8 -0
  15. package/dist/compiler/AstCompiler/internal/statements/_compileStatement.js +4 -0
  16. package/dist/compiler/AstCompiler/internal/statements/_compileTraceStmt.d.ts +4 -0
  17. package/dist/compiler/AstCompiler/internal/statements/_compileTraceStmt.js +15 -0
  18. package/dist/compiler/Compiler.d.ts +5 -0
  19. package/dist/compiler/Compiler.js +26 -2
  20. package/dist/compiler/TirCompiler/expressify/determineReassignedVariablesAndReturn.js +5 -3
  21. package/dist/compiler/TirCompiler/expressify/expressify.js +8 -0
  22. package/dist/compiler/TirCompiler/expressify/expressifyIfBranch.js +2 -0
  23. package/dist/compiler/TirCompiler/expressify/expressifyVars.js +10 -1
  24. package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
  25. package/dist/compiler/tir/expressions/TirExpr.js +2 -0
  26. package/dist/compiler/tir/expressions/TirNativeFunc.js +3 -3
  27. package/dist/compiler/tir/expressions/TirTraceExpr.d.ts +22 -0
  28. package/dist/compiler/tir/expressions/TirTraceExpr.js +53 -0
  29. package/dist/compiler/tir/expressions/TirTypeConversionExpr.js +6 -0
  30. package/dist/compiler/tir/statements/TirStmt.d.ts +2 -1
  31. package/dist/compiler/tir/statements/TirStmt.js +2 -0
  32. package/dist/compiler/tir/statements/TirTraceStmt.d.ts +15 -0
  33. package/dist/compiler/tir/statements/TirTraceStmt.js +20 -0
  34. package/dist/index.d.ts +0 -2
  35. package/dist/index.js +0 -2
  36. package/dist/parser/Parser.d.ts +2 -0
  37. package/dist/parser/Parser.js +15 -0
  38. package/dist/tokenizer/Token.d.ts +82 -81
  39. package/dist/tokenizer/Token.js +82 -81
  40. package/dist/tokenizer/utils/tokenFromKeyword.js +2 -0
  41. package/dist/utils/array/keepSortedArrInplace.js +15 -0
  42. 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 instanceof Pair &&
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), new ByteString(this.value));
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).toBuffer();
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,2 @@
1
+ import { IRTerm } from "../../../IRTerm.js";
2
+ export declare function ensureProperlyForcedBuiltinsAndReturnRoot(root: IRTerm): IRTerm;
@@ -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(...applicaitonTerms.args);
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
+ }
@@ -0,0 +1,10 @@
1
+ export class TraceStmt {
2
+ expr;
3
+ range;
4
+ constructor(
5
+ /** expression to trace (must be string) */
6
+ expr, range) {
7
+ this.expr = expr;
8
+ this.range = range;
9
+ }
10
+ }
@@ -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
+ }