@harmoniclabs/pebble 0.1.3-dev1 → 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 (34) hide show
  1. package/dist/IR/tree_utils/intToUtf8Bytes.d.ts +8 -0
  2. package/dist/IR/tree_utils/intToUtf8Bytes.js +49 -0
  3. package/dist/ast/nodes/statements/PebbleStmt.d.ts +2 -1
  4. package/dist/ast/nodes/statements/PebbleStmt.js +2 -0
  5. package/dist/ast/nodes/statements/TraceStmt.d.ts +11 -0
  6. package/dist/ast/nodes/statements/TraceStmt.js +10 -0
  7. package/dist/compiler/AstCompiler/AstCompiler.d.ts +11 -0
  8. package/dist/compiler/AstCompiler/AstCompiler.js +185 -0
  9. package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +8 -0
  10. package/dist/compiler/AstCompiler/internal/statements/_compileStatement.js +4 -0
  11. package/dist/compiler/AstCompiler/internal/statements/_compileTraceStmt.d.ts +4 -0
  12. package/dist/compiler/AstCompiler/internal/statements/_compileTraceStmt.js +15 -0
  13. package/dist/compiler/Compiler.d.ts +5 -0
  14. package/dist/compiler/Compiler.js +25 -1
  15. package/dist/compiler/TirCompiler/expressify/determineReassignedVariablesAndReturn.js +5 -3
  16. package/dist/compiler/TirCompiler/expressify/expressify.js +8 -0
  17. package/dist/compiler/TirCompiler/expressify/expressifyIfBranch.js +2 -0
  18. package/dist/compiler/TirCompiler/expressify/expressifyVars.js +10 -1
  19. package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
  20. package/dist/compiler/tir/expressions/TirExpr.js +2 -0
  21. package/dist/compiler/tir/expressions/TirTraceExpr.d.ts +22 -0
  22. package/dist/compiler/tir/expressions/TirTraceExpr.js +53 -0
  23. package/dist/compiler/tir/expressions/TirTypeConversionExpr.js +6 -0
  24. package/dist/compiler/tir/statements/TirStmt.d.ts +2 -1
  25. package/dist/compiler/tir/statements/TirStmt.js +2 -0
  26. package/dist/compiler/tir/statements/TirTraceStmt.d.ts +15 -0
  27. package/dist/compiler/tir/statements/TirTraceStmt.js +20 -0
  28. package/dist/parser/Parser.d.ts +2 -0
  29. package/dist/parser/Parser.js +15 -0
  30. package/dist/tokenizer/Token.d.ts +82 -81
  31. package/dist/tokenizer/Token.js +82 -81
  32. package/dist/tokenizer/utils/tokenFromKeyword.js +2 -0
  33. package/dist/utils/array/keepSortedArrInplace.js +15 -0
  34. package/package.json +1 -1
@@ -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
+ }
@@ -8,6 +8,11 @@ export declare class Compiler extends DiagnosticEmitter {
8
8
  constructor(io?: CompilerIoApi, cfg?: CompilerOptions, diagnostics?: DiagnosticMessage[]);
9
9
  compile(config?: Partial<CompilerOptions>): Promise<Uint8Array>;
10
10
  export(config: Partial<ExportOptions> & HasFuncitonName): Promise<Uint8Array>;
11
+ run(config?: Partial<CompilerOptions>): Promise<{
12
+ result: import("@harmoniclabs/plutus-machine").CEKValueObj;
13
+ budgetSpent: import("@harmoniclabs/plutus-machine").ExBudget;
14
+ logs: string[];
15
+ }>;
11
16
  private _compileBackend;
12
17
  }
13
18
  interface HasFuncitonName {
@@ -1,4 +1,5 @@
1
- import { compileUPLC, UPLCProgram } from "@harmoniclabs/uplc";
1
+ import { compileUPLC, parseUPLC, UPLCProgram } from "@harmoniclabs/uplc";
2
+ import { Machine } from "@harmoniclabs/plutus-machine";
2
3
  import { DiagnosticEmitter } from "../diagnostics/DiagnosticEmitter.js";
3
4
  import { defaultOptions } from "../IR/toUPLC/CompilerOptions.js";
4
5
  import { AstCompiler } from "./AstCompiler/AstCompiler.js";
@@ -60,6 +61,29 @@ export class Compiler extends DiagnosticEmitter {
60
61
  }
61
62
  return this._compileBackend(cfg, program);
62
63
  }
64
+ async run(config) {
65
+ const cfg = {
66
+ ...this.cfg,
67
+ ...config,
68
+ // NEVER generate markers when running
69
+ addMarker: false,
70
+ };
71
+ const astCompiler = new AstCompiler(cfg, this.io, this.diagnostics);
72
+ const program = await astCompiler.run();
73
+ if (this.diagnostics.length > 0) {
74
+ let msg;
75
+ globalThis.console && console.log(this.diagnostics);
76
+ const fstErrorMsg = this.diagnostics[0].toString();
77
+ const nDiags = this.diagnostics.length;
78
+ while (msg = this.diagnostics.shift()) {
79
+ this.io.stdout.write(msg.toString() + "\n");
80
+ }
81
+ throw new Error("compilation failed with " + nDiags + " diagnostic messages; first message: " + fstErrorMsg);
82
+ }
83
+ const serialized = this._compileBackend(cfg, program);
84
+ const uplcProgram = parseUPLC(serialized);
85
+ return Machine.eval(uplcProgram.body);
86
+ }
63
87
  _compileBackend(cfg, program) {
64
88
  // backend starts here
65
89
  const ir = compileTypedProgram(cfg, program);
@@ -9,6 +9,7 @@ import { TirBlockStmt } from "../../tir/statements/TirBlockStmt.js";
9
9
  import { TirBreakStmt } from "../../tir/statements/TirBreakStmt.js";
10
10
  import { TirContinueStmt } from "../../tir/statements/TirContinueStmt.js";
11
11
  import { TirFailStmt } from "../../tir/statements/TirFailStmt.js";
12
+ import { TirTraceStmt } from "../../tir/statements/TirTraceStmt.js";
12
13
  import { TirForOfStmt } from "../../tir/statements/TirForOfStmt.js";
13
14
  import { TirForStmt } from "../../tir/statements/TirForStmt.js";
14
15
  import { TirIfStmt } from "../../tir/statements/TirIfStmt.js";
@@ -25,6 +26,7 @@ export function determineReassignedVariablesAndReturn(stmt) {
25
26
  let returns = false;
26
27
  while (stmt = stack.pop()) {
27
28
  if (stmt instanceof TirFailStmt
29
+ || stmt instanceof TirTraceStmt
28
30
  || isTirVarDecl(stmt)
29
31
  || stmt instanceof TirBreakStmt
30
32
  || stmt instanceof TirContinueStmt
@@ -84,9 +86,8 @@ export function determineReassignedVariablesAndFlowInfos(stmt) {
84
86
  let canContinue = false;
85
87
  while (stmt = stack.pop()) {
86
88
  if (stmt instanceof TirFailStmt
89
+ || stmt instanceof TirTraceStmt
87
90
  || isTirVarDecl(stmt)
88
- || stmt instanceof TirBreakStmt
89
- || stmt instanceof TirContinueStmt
90
91
  || stmt instanceof TirAssertStmt)
91
92
  continue;
92
93
  if (stmt instanceof TirBreakStmt) {
@@ -193,7 +194,8 @@ export function definitelyFails(stmt) {
193
194
  || stmt instanceof TirBreakStmt
194
195
  || stmt instanceof TirContinueStmt
195
196
  || stmt instanceof TirAssertStmt
196
- || stmt instanceof TirAssignmentStmt)
197
+ || stmt instanceof TirAssignmentStmt
198
+ || stmt instanceof TirTraceStmt)
197
199
  continue;
198
200
  // const tsEnsureExsaustiveCheck: never = stmt;
199
201
  }
@@ -3,6 +3,7 @@ import { getUniqueInternalName } from "../../internalVar.js";
3
3
  import { TirLitVoidExpr } from "../../tir/expressions/litteral/TirLitVoidExpr.js";
4
4
  import { TirCaseExpr, TirCaseMatcher, TirWildcardCaseMatcher } from "../../tir/expressions/TirCaseExpr.js";
5
5
  import { TirFailExpr } from "../../tir/expressions/TirFailExpr.js";
6
+ import { TirTraceExpr } from "../../tir/expressions/TirTraceExpr.js";
6
7
  import { TirLettedExpr } from "../../tir/expressions/TirLettedExpr.js";
7
8
  import { TirVariableAccessExpr } from "../../tir/expressions/TirVariableAccessExpr.js";
8
9
  import { TirAssertStmt } from "../../tir/statements/TirAssertStmt.js";
@@ -11,6 +12,7 @@ import { TirBlockStmt } from "../../tir/statements/TirBlockStmt.js";
11
12
  import { TirBreakStmt } from "../../tir/statements/TirBreakStmt.js";
12
13
  import { TirContinueStmt } from "../../tir/statements/TirContinueStmt.js";
13
14
  import { TirFailStmt } from "../../tir/statements/TirFailStmt.js";
15
+ import { TirTraceStmt } from "../../tir/statements/TirTraceStmt.js";
14
16
  import { TirForOfStmt } from "../../tir/statements/TirForOfStmt.js";
15
17
  import { TirForStmt } from "../../tir/statements/TirForStmt.js";
16
18
  import { TirIfStmt } from "../../tir/statements/TirIfStmt.js";
@@ -217,6 +219,12 @@ loopReplacements, assertions = []) {
217
219
  return TirAssertAndContinueExpr.fromStmtsAndContinuation(assertions, new TirFailExpr(undefined, ctx.returnType, stmt.range));
218
220
  }
219
221
  }
222
+ else if (stmt instanceof TirTraceStmt) {
223
+ const modifiedExpr = expressifyVars(ctx, stmt.expr);
224
+ stmt.expr = modifiedExpr;
225
+ const continuation = expressifyFuncBody(ctx, bodyStmts, loopReplacements);
226
+ return TirAssertAndContinueExpr.fromStmtsAndContinuation(assertions, new TirTraceExpr(modifiedExpr, continuation, stmt.range));
227
+ }
220
228
  else if (stmt instanceof TirAssertStmt) {
221
229
  const condition = expressifyVars(ctx, stmt.condition);
222
230
  stmt.condition = condition;
@@ -8,6 +8,7 @@ import { TirBlockStmt } from "../../tir/statements/TirBlockStmt.js";
8
8
  import { TirBreakStmt } from "../../tir/statements/TirBreakStmt.js";
9
9
  import { TirContinueStmt } from "../../tir/statements/TirContinueStmt.js";
10
10
  import { TirFailStmt } from "../../tir/statements/TirFailStmt.js";
11
+ import { TirTraceStmt } from "../../tir/statements/TirTraceStmt.js";
11
12
  import { TirForOfStmt } from "../../tir/statements/TirForOfStmt.js";
12
13
  import { TirForStmt } from "../../tir/statements/TirForStmt.js";
13
14
  import { TirIfStmt } from "../../tir/statements/TirIfStmt.js";
@@ -60,6 +61,7 @@ function replaceReturnStatements(body, wrapReturnExpr, sopType) {
60
61
  || stmt instanceof TirBreakStmt
61
62
  || stmt instanceof TirContinueStmt
62
63
  || stmt instanceof TirFailStmt
64
+ || stmt instanceof TirTraceStmt
63
65
  || stmt instanceof TirAssertStmt
64
66
  || stmt instanceof TirAssignmentStmt)
65
67
  continue;
@@ -28,6 +28,7 @@ import { TirPropAccessExpr } from "../../tir/expressions/TirPropAccessExpr.js";
28
28
  import { TirTernaryExpr } from "../../tir/expressions/TirTernaryExpr.js";
29
29
  import { TirToDataExpr } from "../../tir/expressions/TirToDataExpr.js";
30
30
  import { TirTraceIfFalseExpr } from "../../tir/expressions/TirTraceIfFalseExpr.js";
31
+ import { TirTraceExpr } from "../../tir/expressions/TirTraceExpr.js";
31
32
  import { TirTypeConversionExpr } from "../../tir/expressions/TirTypeConversionExpr.js";
32
33
  import { TirVariableAccessExpr } from "../../tir/expressions/TirVariableAccessExpr.js";
33
34
  import { TirUnaryExclamation } from "../../tir/expressions/unary/TirUnaryExclamation.js";
@@ -212,6 +213,13 @@ export function expressifyVars(ctx, expr) {
212
213
  expr.traceStrExpr = modifiedTraceStrExpr;
213
214
  return expr;
214
215
  }
216
+ if (expr instanceof TirTraceExpr) {
217
+ const modifiedTraceExpr = expressifyVars(ctx, expr.traceExpr);
218
+ const modifiedContinuation = expressifyVars(ctx, expr.continuation);
219
+ expr.traceExpr = modifiedTraceExpr;
220
+ expr.continuation = modifiedContinuation;
221
+ return expr;
222
+ }
215
223
  if (expr instanceof TirInlineClosedIR)
216
224
  return expr;
217
225
  const tsEnsureExhautstiveCheck = expr;
@@ -243,9 +251,10 @@ function expressifyPropAccess(ctx, propAccessExpr) {
243
251
  || expr instanceof TirFuncExpr // functions have no properties
244
252
  || expr instanceof TirParentesizedExpr // typescript being stupid
245
253
  || isTirBinaryExpr(expr) // all of these return either int, bytes or boolean
246
- // TirAssertAndContinueExpr | TirTraceIfFalseExpr | TirInlineClosedIR
254
+ // TirAssertAndContinueExpr | TirTraceIfFalseExpr | TirTraceExpr | TirInlineClosedIR
247
255
  || expr instanceof TirAssertAndContinueExpr
248
256
  || expr instanceof TirTraceIfFalseExpr
257
+ || expr instanceof TirTraceExpr
249
258
  || expr instanceof TirInlineClosedIR)
250
259
  throw new Error("Invalid property access expression");
251
260
  if (expr instanceof TirLitThisExpr) {
@@ -17,7 +17,8 @@ import { TirHoistedExpr } from "./TirHoistedExpr.js";
17
17
  import { TirToDataExpr } from "./TirToDataExpr.js";
18
18
  import { TirAssertAndContinueExpr } from "./TirAssertAndContinueExpr.js";
19
19
  import { TirTraceIfFalseExpr } from "./TirTraceIfFalseExpr.js";
20
+ import { TirTraceExpr } from "./TirTraceExpr.js";
20
21
  import { TirNativeFunc } from "./TirNativeFunc.js";
21
22
  import { TirInlineClosedIR } from "./TirInlineClosedIR.js";
22
- export type TirExpr = (TirUnaryPrefixExpr | TirLitteralExpr | TirParentesizedExpr | TirFuncExpr | TirCallExpr | TirCaseExpr | TirTypeConversionExpr | TirElemAccessExpr | TirTernaryExpr | TirPropAccessExpr | TirBinaryExpr | TirVariableAccessExpr | TirLettedExpr | TirNativeFunc | TirFailExpr | TirHoistedExpr | TirFromDataExpr | TirToDataExpr | TirAssertAndContinueExpr | TirTraceIfFalseExpr | TirInlineClosedIR);
23
+ export type TirExpr = (TirUnaryPrefixExpr | TirLitteralExpr | TirParentesizedExpr | TirFuncExpr | TirCallExpr | TirCaseExpr | TirTypeConversionExpr | TirElemAccessExpr | TirTernaryExpr | TirPropAccessExpr | TirBinaryExpr | TirVariableAccessExpr | TirLettedExpr | TirNativeFunc | TirFailExpr | TirHoistedExpr | TirFromDataExpr | TirToDataExpr | TirAssertAndContinueExpr | TirTraceIfFalseExpr | TirTraceExpr | TirInlineClosedIR);
23
24
  export declare function isTirExpr(thing: any): thing is TirExpr;
@@ -18,6 +18,7 @@ import { TirHoistedExpr } from "./TirHoistedExpr.js";
18
18
  import { TirToDataExpr } from "./TirToDataExpr.js";
19
19
  import { TirAssertAndContinueExpr } from "./TirAssertAndContinueExpr.js";
20
20
  import { TirTraceIfFalseExpr } from "./TirTraceIfFalseExpr.js";
21
+ import { TirTraceExpr } from "./TirTraceExpr.js";
21
22
  import { TirNativeFunc } from "./TirNativeFunc.js";
22
23
  import { TirInlineClosedIR } from "./TirInlineClosedIR.js";
23
24
  export function isTirExpr(thing) {
@@ -42,5 +43,6 @@ export function isTirExpr(thing) {
42
43
  || thing instanceof TirToDataExpr
43
44
  || thing instanceof TirAssertAndContinueExpr
44
45
  || thing instanceof TirTraceIfFalseExpr
46
+ || thing instanceof TirTraceExpr
45
47
  || thing instanceof TirInlineClosedIR);
46
48
  }
@@ -0,0 +1,22 @@
1
+ import { SourceRange } from "../../../ast/Source/SourceRange.js";
2
+ import type { IRTerm } from "../../../IR/IRTerm.js";
3
+ import { TirType } from "../types/TirType.js";
4
+ import { ITirExpr } from "./ITirExpr.js";
5
+ import { TirExpr } from "./TirExpr.js";
6
+ import { ToIRTermCtx } from "./ToIRTermCtx.js";
7
+ export declare class TirTraceExpr implements ITirExpr {
8
+ /** must be bytes or int (converted to string via decodeUtf8 in toIR) */
9
+ traceExpr: TirExpr;
10
+ continuation: TirExpr;
11
+ readonly range: SourceRange;
12
+ get type(): TirType;
13
+ constructor(
14
+ /** must be bytes or int (converted to string via decodeUtf8 in toIR) */
15
+ traceExpr: TirExpr, continuation: TirExpr, range: SourceRange);
16
+ toString(): string;
17
+ pretty(indent: number): string;
18
+ clone(): TirExpr;
19
+ deps(): string[];
20
+ get isConstant(): boolean;
21
+ toIR(ctx: ToIRTermCtx): IRTerm;
22
+ }